Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package cosmic-comp for openSUSE:Factory 
checked in at 2026-08-28 19:57:53
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/cosmic-comp (Old)
 and      /work/SRC/openSUSE:Factory/.cosmic-comp.new.1265 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "cosmic-comp"

Fri Aug 28 19:57:53 2026 rev:15 rq:1374427 version:1.7.0

Changes:
--------
--- /work/SRC/openSUSE:Factory/cosmic-comp/cosmic-comp.changes  2026-08-21 
16:55:41.670994550 +0200
+++ /work/SRC/openSUSE:Factory/.cosmic-comp.new.1265/cosmic-comp.changes        
2026-08-28 20:00:58.134473447 +0200
@@ -1,0 +2,12 @@
+Thu Aug 27 04:25:44 UTC 2026 - Richard Rahl <[email protected]>
+
+- Update to version 1.7.0:
+  * improv: clear image_cache on refresh
+  * improv: round up and down sample cursor sizes to limit cache
+  * feat: scalable cursor support
+  * Clamp zoom level to 1.0 if < 1.01
+  * a11y: magnifier: change zoom stepping to multiplicative, bound max, add 
presets
+  * feat: shake cursor to find by magnifying the cursor
+  * fix: treat unlocking the session as user activity
+
+-------------------------------------------------------------------

Old:
----
  cosmic-comp-1.6.0.obscpio

New:
----
  cosmic-comp-1.7.0.obscpio

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ cosmic-comp.spec ++++++
--- /var/tmp/diff_new_pack.3QF0Lf/_old  2026-08-28 20:01:00.467555015 +0200
+++ /var/tmp/diff_new_pack.3QF0Lf/_new  2026-08-28 20:01:00.474555260 +0200
@@ -17,7 +17,7 @@
 
 
 Name:           cosmic-comp
-Version:        1.6.0
+Version:        1.7.0
 Release:        0
 Summary:        Compositor for the COSMIC DE
 License:        GPL-3.0-only

++++++ _service ++++++
--- /var/tmp/diff_new_pack.3QF0Lf/_old  2026-08-28 20:01:00.561558302 +0200
+++ /var/tmp/diff_new_pack.3QF0Lf/_new  2026-08-28 20:01:00.578558896 +0200
@@ -3,7 +3,7 @@
   <service name="obs_scm" mode="manual">
     <param name="url">https://github.com/pop-os/cosmic-comp.git</param>
     <param name="scm">git</param>
-    <param name="revision">refs/tags/epoch-1.6.0</param>
+    <param name="revision">refs/tags/epoch-1.7.0</param>
     <param name="versionrewrite-pattern">epoch-(.*)</param>
     <param name="versionformat">@PARENT_TAG@</param>
     <param name="versionrewrite-replacement">\1</param>

++++++ _servicedata ++++++
--- /var/tmp/diff_new_pack.3QF0Lf/_old  2026-08-28 20:01:00.624560505 +0200
+++ /var/tmp/diff_new_pack.3QF0Lf/_new  2026-08-28 20:01:00.633560819 +0200
@@ -1,6 +1,6 @@
 <servicedata>
 <service name="tar_scm">
                 <param 
name="url">https://github.com/pop-os/cosmic-comp.git</param>
-              <param 
name="changesrevision">314fc670a83f1e570b069cd709df809ba99fa334</param></service></servicedata>
+              <param 
name="changesrevision">31827ed2409f92d3bc224d3ce601c6f2136183fb</param></service></servicedata>
 (No newline at EOF)
 

++++++ cosmic-comp-1.6.0.obscpio -> cosmic-comp-1.7.0.obscpio ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/cosmic-comp-1.6.0/.git/FETCH_HEAD 
new/cosmic-comp-1.7.0/.git/FETCH_HEAD
--- old/cosmic-comp-1.6.0/.git/FETCH_HEAD       1970-01-01 01:00:00.000000000 
+0100
+++ new/cosmic-comp-1.7.0/.git/FETCH_HEAD       2026-08-25 17:33:56.000000000 
+0200
@@ -0,0 +1,53 @@
+df7819b2b24bb6b66ca97a2456572410f3e05521       not-for-merge   branch 
'action-on-typing' of https://github.com/pop-os/cosmic-comp
+669d497b8ccdc9bce2663c2e87ac2114ceca3c57       not-for-merge   branch 
'checklist_nobuild' of https://github.com/pop-os/cosmic-comp
+84b99f934688a80fffc6fa53d46c1aaad2d39ebc       not-for-merge   branch 
'cleanup-corner-redraw' of https://github.com/pop-os/cosmic-comp
+d47754d41e915bb5e5a698970beb93a9a04eaa88       not-for-merge   branch 
'comp-output-redirect_noble' of https://github.com/pop-os/cosmic-comp
+8eaa3a8b6831bec6eb7ffdfe7e91711edc18c656       not-for-merge   branch 
'cursor_noble' of https://github.com/pop-os/cosmic-comp
+020c3361e6b02ac320f67cfded3a29b9c5b285a3       not-for-merge   branch 
'delay-capture_noble' of https://github.com/pop-os/cosmic-comp
+38bcf97553aaf35d708708905e7566ec309486ce       not-for-merge   branch 
'dispatch2_noble' of https://github.com/pop-os/cosmic-comp
+3c8c9edfded44b72738a6c26c4551fab8dbf2ff7       not-for-merge   branch 
'dpms_noble' of https://github.com/pop-os/cosmic-comp
+bbb5e4a9c65c621ae307e781b4f9aaa24ef6b54d       not-for-merge   branch 
'drm-order-test' of https://github.com/pop-os/cosmic-comp
+2892e9408c4e043b92ea938cc782706bfc3b1aa9       not-for-merge   branch 
'edid-match' of https://github.com/pop-os/cosmic-comp
+aa698b337cdffd7b0c8bde9f3a468a1aae90f783       not-for-merge   branch 
'ext-screencopy_nobuild' of https://github.com/pop-os/cosmic-comp
+eb7f079ae4ec36963f3cc079f12fe58b45303b5c       not-for-merge   branch 
'ext-workspace-capture_noble' of https://github.com/pop-os/cosmic-comp
+5894ff2eea4fdbde43e05d8e27a2b05588254ceb       not-for-merge   branch 
'fifo_noble' of https://github.com/pop-os/cosmic-comp
+4d7aa5b8c6779ed3d6401ddc8d5a247ba15554ef       not-for-merge   branch 
'fix-2275_noble' of https://github.com/pop-os/cosmic-comp
+ac443217178bb46075b1a98f5921f12a8f0b5580       not-for-merge   branch 
'fix-3330_noble' of https://github.com/pop-os/cosmic-comp
+12f18758154535e1f8d4ad98d5bb755dc01830e8       not-for-merge   branch 
'fix-check-all-connectors_noble' of https://github.com/pop-os/cosmic-comp
+8d3cb6d5159b5ba8f423cbebcc9c9f2cbadf274d       not-for-merge   branch 
'fix-late-cloexec-session_noble' of https://github.com/pop-os/cosmic-comp
+822b68cab19963cf12a252d6b43984e45d7e3586       not-for-merge   branch 
'fix-name-owners' of https://github.com/pop-os/cosmic-comp
+324a8c6eb52f4099d02f06c2c65e11b33438d994       not-for-merge   branch 
'fix-positioning' of https://github.com/pop-os/cosmic-comp
+ba20ddd2076d02cb8141d23a684c6da075864b47       not-for-merge   branch 
'fix-positioning-2' of https://github.com/pop-os/cosmic-comp
+9c72388ac8d5fee2008ac9f290bf4d4c3fcf9f9c       not-for-merge   branch 
'fix-use-10-bpc_noble' of https://github.com/pop-os/cosmic-comp
+beac0214f4f5aad54cb1432fd08d6f2890ace582       not-for-merge   branch 
'fix-wipe-dpms-on-resume' of https://github.com/pop-os/cosmic-comp
+d3016f142c182edf7dd529fe9be7314f6cabd87a       not-for-merge   branch 
'fix_dnd_events' of https://github.com/pop-os/cosmic-comp
+b942a9a64754aaca9c8ab0b5dd2fc59e5ef8823b       not-for-merge   branch 
'focus-follow-border_noble' of https://github.com/pop-os/cosmic-comp
+e7843332b34b430ae2f1e6c0570bfd82bea767d5       not-for-merge   branch 
'iced-rebase' of https://github.com/pop-os/cosmic-comp
+dde9279c51dcdd4e3aca09d67af12dc5b0f9718f       not-for-merge   branch 
'image-copy-session-stop_noble' of https://github.com/pop-os/cosmic-comp
+47470fe63f3b0481f58b85a23a41453502e11eb0       not-for-merge   branch 
'image-copy_noble' of https://github.com/pop-os/cosmic-comp
+1ae9d1e68fcef34b202b87913812dbfdeec33dd6       not-for-merge   branch 
'inhibitor-lock_nobuild' of https://github.com/pop-os/cosmic-comp
+5c93094574b31800caaa27255caac4a7253f24ad       not-for-merge   branch 'master' 
of https://github.com/pop-os/cosmic-comp
+1d00a51ca9ec3ccee83b1aa2a776f054a729d1d0       not-for-merge   branch 
'master_jammy' of https://github.com/pop-os/cosmic-comp
+ebc268e6ae954ea96aaf8c9c671822c6aa8a6a2a       not-for-merge   branch 
'migration-enter_noble' of https://github.com/pop-os/cosmic-comp
+f72781f3acb695b4ae574181a4569dc60fa79176       not-for-merge   branch 
'mst-path_noble' of https://github.com/pop-os/cosmic-comp
+05b99e8e3439e441ff97dc460dd1bc6aeb4afb1b       not-for-merge   branch 
'pointer-capture-wip_nobuild' of https://github.com/pop-os/cosmic-comp
+745a65e33b4c1335b97096e744bb5d345124fa64       not-for-merge   branch 
'position-hint_noble' of https://github.com/pop-os/cosmic-comp
+198ac1eeb4d6dfcf450e41da5f31bf2598f4d258       not-for-merge   branch 'reis' 
of https://github.com/pop-os/cosmic-comp
+d9ba816a43c365fbece7e55c1d278794a0f5fe08       not-for-merge   branch 
'remaining-clippy_noble' of https://github.com/pop-os/cosmic-comp
+0cdb2b7169cc602d706505b2e6367d62db29fed9       not-for-merge   branch 
'screencopy-damage_noble' of https://github.com/pop-os/cosmic-comp
+b265c9217a381be67b61f7308cb9b71cd4aebc07       not-for-merge   branch 
'screencopy-no-invalid_noble' of https://github.com/pop-os/cosmic-comp
+1670b4c682998bcbd9bb17e5855d1f2c718a2dc2       not-for-merge   branch 
'session-lock-frosted-subsurface' of https://github.com/pop-os/cosmic-comp
+c135f76cc5494fff676987435ec548fba8c0c775       not-for-merge   branch 
'ssd-preference_noble' of https://github.com/pop-os/cosmic-comp
+620c1a730d270d206d017cd5aee16ebff946fbda       not-for-merge   branch 'stack' 
of https://github.com/pop-os/cosmic-comp
+7ef4c56f6629fe3feadb3f0bbf8df73d1a6223ff       not-for-merge   branch 
'stack-previous-keyboard_noble' of https://github.com/pop-os/cosmic-comp
+55cef24f4d5348a20cd4fe0b173fee84c0fc8923       not-for-merge   branch 
'strict-activation_jammy' of https://github.com/pop-os/cosmic-comp
+93baf72971b37039f929c608f5d85dff2de2e681       not-for-merge   branch 
'suspend-key' of https://github.com/pop-os/cosmic-comp
+374b690b8d7c22c20f250c6e95da23b50d5f937b       not-for-merge   branch 
'tablet-update_noble' of https://github.com/pop-os/cosmic-comp
+5505b25c81e38331902cca620d78ac2cac34badb       not-for-merge   branch 
'testing-cosmic-comp-pr2656_noble' of https://github.com/pop-os/cosmic-comp
+d760be5201a0e46242e6319d2e4e142b7cceba45       not-for-merge   branch 
'tgl-flicker' of https://github.com/pop-os/cosmic-comp
+a39f380291fb01462853b198efb6cd14e3f1d6ef       not-for-merge   branch 
'toplevel-info_noble' of https://github.com/pop-os/cosmic-comp
+0d7da43cbc75766611d8f692affd52fb0e830201       not-for-merge   branch 
'touch_noble' of https://github.com/pop-os/cosmic-comp
+b66849a309e49ebc04ee3ab78a7cca3e47a43661       not-for-merge   branch 
'udmabuf-sharing' of https://github.com/pop-os/cosmic-comp
+08b82773c077a31e62cac127e7e6c3319ce78df5       not-for-merge   branch 
'update-smithay_noble' of https://github.com/pop-os/cosmic-comp
+a80578d191469a2ba8bed58589c2dabaa67a65ae       not-for-merge   branch 
'wayland-max-buffer-size_noble' of https://github.com/pop-os/cosmic-comp
+3c8a52067a41142ff9e83d630dd584e1e02887e3       not-for-merge   branch 
'xdg-system-bell_noble' of https://github.com/pop-os/cosmic-comp
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/cosmic-comp-1.6.0/.git/HEAD 
new/cosmic-comp-1.7.0/.git/HEAD
--- old/cosmic-comp-1.6.0/.git/HEAD     2026-08-18 13:26:18.000000000 +0200
+++ new/cosmic-comp-1.7.0/.git/HEAD     2026-08-25 17:33:56.000000000 +0200
@@ -1 +1 @@
-314fc670a83f1e570b069cd709df809ba99fa334
+31827ed2409f92d3bc224d3ce601c6f2136183fb
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/cosmic-comp-1.6.0/.git/ORIG_HEAD 
new/cosmic-comp-1.7.0/.git/ORIG_HEAD
--- old/cosmic-comp-1.6.0/.git/ORIG_HEAD        2026-08-18 13:26:18.000000000 
+0200
+++ new/cosmic-comp-1.7.0/.git/ORIG_HEAD        2026-08-25 17:33:56.000000000 
+0200
@@ -1 +1 @@
-314fc670a83f1e570b069cd709df809ba99fa334
+31827ed2409f92d3bc224d3ce601c6f2136183fb
Binary files old/cosmic-comp-1.6.0/.git/index and 
new/cosmic-comp-1.7.0/.git/index differ
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/cosmic-comp-1.6.0/.git/logs/HEAD 
new/cosmic-comp-1.7.0/.git/logs/HEAD
--- old/cosmic-comp-1.6.0/.git/logs/HEAD        2026-08-18 13:26:18.000000000 
+0200
+++ new/cosmic-comp-1.7.0/.git/logs/HEAD        2026-08-25 17:33:56.000000000 
+0200
@@ -1,2 +1,3 @@
 0000000000000000000000000000000000000000 
314fc670a83f1e570b069cd709df809ba99fa334 Richard Rahl <[email protected]> 
1787139434 +0200  clone: from https://github.com/pop-os/cosmic-comp.git
 314fc670a83f1e570b069cd709df809ba99fa334 
314fc670a83f1e570b069cd709df809ba99fa334 Richard Rahl <[email protected]> 
1787139436 +0200  checkout: moving from master to refs/tags/epoch-1.6.0
+314fc670a83f1e570b069cd709df809ba99fa334 
31827ed2409f92d3bc224d3ce601c6f2136183fb Richard Rahl <user@t14.(none)> 
1787804744 +0200      merge refs/tags/epoch-1.7.0: Fast-forward
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/cosmic-comp-1.6.0/.git/logs/refs/remotes/origin/fix-check-all-connectors_noble
 
new/cosmic-comp-1.7.0/.git/logs/refs/remotes/origin/fix-check-all-connectors_noble
--- 
old/cosmic-comp-1.6.0/.git/logs/refs/remotes/origin/fix-check-all-connectors_noble
  1970-01-01 01:00:00.000000000 +0100
+++ 
new/cosmic-comp-1.7.0/.git/logs/refs/remotes/origin/fix-check-all-connectors_noble
  2026-08-25 17:33:56.000000000 +0200
@@ -0,0 +1 @@
+9c95ebfcf4c4cb678e1dd359cf1e685c759d5739 
12f18758154535e1f8d4ad98d5bb755dc01830e8 Richard Rahl <user@t14.(none)> 
1787804742 +0200      fetch --tags: forced-update
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/cosmic-comp-1.6.0/.git/logs/refs/remotes/origin/focus-follow-border_noble 
new/cosmic-comp-1.7.0/.git/logs/refs/remotes/origin/focus-follow-border_noble
--- 
old/cosmic-comp-1.6.0/.git/logs/refs/remotes/origin/focus-follow-border_noble   
    1970-01-01 01:00:00.000000000 +0100
+++ 
new/cosmic-comp-1.7.0/.git/logs/refs/remotes/origin/focus-follow-border_noble   
    2026-08-25 17:33:56.000000000 +0200
@@ -0,0 +1 @@
+d915d08f80aa07bb71494a693edbdb24049aa8a3 
b942a9a64754aaca9c8ab0b5dd2fc59e5ef8823b Richard Rahl <user@t14.(none)> 
1787804742 +0200      fetch --tags: forced-update
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/cosmic-comp-1.6.0/.git/logs/refs/remotes/origin/master 
new/cosmic-comp-1.7.0/.git/logs/refs/remotes/origin/master
--- old/cosmic-comp-1.6.0/.git/logs/refs/remotes/origin/master  1970-01-01 
01:00:00.000000000 +0100
+++ new/cosmic-comp-1.7.0/.git/logs/refs/remotes/origin/master  2026-08-25 
17:33:56.000000000 +0200
@@ -0,0 +1 @@
+314fc670a83f1e570b069cd709df809ba99fa334 
5c93094574b31800caaa27255caac4a7253f24ad Richard Rahl <user@t14.(none)> 
1787804742 +0200      fetch --tags: fast-forward
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/cosmic-comp-1.6.0/.git/logs/refs/remotes/origin/testing-cosmic-comp-pr2656_noble
 
new/cosmic-comp-1.7.0/.git/logs/refs/remotes/origin/testing-cosmic-comp-pr2656_noble
--- 
old/cosmic-comp-1.6.0/.git/logs/refs/remotes/origin/testing-cosmic-comp-pr2656_noble
        1970-01-01 01:00:00.000000000 +0100
+++ 
new/cosmic-comp-1.7.0/.git/logs/refs/remotes/origin/testing-cosmic-comp-pr2656_noble
        2026-08-25 17:33:56.000000000 +0200
@@ -0,0 +1 @@
+0000000000000000000000000000000000000000 
5505b25c81e38331902cca620d78ac2cac34badb Richard Rahl <user@t14.(none)> 
1787804742 +0200      fetch --tags: storing head
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/cosmic-comp-1.6.0/.git/logs/refs/remotes/origin/update-smithay_noble 
new/cosmic-comp-1.7.0/.git/logs/refs/remotes/origin/update-smithay_noble
--- old/cosmic-comp-1.6.0/.git/logs/refs/remotes/origin/update-smithay_noble    
1970-01-01 01:00:00.000000000 +0100
+++ new/cosmic-comp-1.7.0/.git/logs/refs/remotes/origin/update-smithay_noble    
2026-08-25 17:33:56.000000000 +0200
@@ -0,0 +1 @@
+d96d9e52a8c39b7c4195c47f4766dd6d24851a91 
08b82773c077a31e62cac127e7e6c3319ce78df5 Richard Rahl <user@t14.(none)> 
1787804742 +0200      fetch --tags: forced-update
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/cosmic-comp-1.6.0/.git/objects/info/commit-graphs/commit-graph-chain 
new/cosmic-comp-1.7.0/.git/objects/info/commit-graphs/commit-graph-chain
--- old/cosmic-comp-1.6.0/.git/objects/info/commit-graphs/commit-graph-chain    
1970-01-01 01:00:00.000000000 +0100
+++ new/cosmic-comp-1.7.0/.git/objects/info/commit-graphs/commit-graph-chain    
2026-08-25 17:33:56.000000000 +0200
@@ -0,0 +1 @@
+d457831da5339573fa6aacea0b57247e6bc1ea73
Binary files 
old/cosmic-comp-1.6.0/.git/objects/info/commit-graphs/graph-d457831da5339573fa6aacea0b57247e6bc1ea73.graph
 and 
new/cosmic-comp-1.7.0/.git/objects/info/commit-graphs/graph-d457831da5339573fa6aacea0b57247e6bc1ea73.graph
 differ
Binary files 
old/cosmic-comp-1.6.0/.git/objects/pack/pack-2aa37258aca0791aacefc8999491c85af3885fd6.idx
 and 
new/cosmic-comp-1.7.0/.git/objects/pack/pack-2aa37258aca0791aacefc8999491c85af3885fd6.idx
 differ
Binary files 
old/cosmic-comp-1.6.0/.git/objects/pack/pack-2aa37258aca0791aacefc8999491c85af3885fd6.pack
 and 
new/cosmic-comp-1.7.0/.git/objects/pack/pack-2aa37258aca0791aacefc8999491c85af3885fd6.pack
 differ
Binary files 
old/cosmic-comp-1.6.0/.git/objects/pack/pack-2aa37258aca0791aacefc8999491c85af3885fd6.rev
 and 
new/cosmic-comp-1.7.0/.git/objects/pack/pack-2aa37258aca0791aacefc8999491c85af3885fd6.rev
 differ
Binary files 
old/cosmic-comp-1.6.0/.git/objects/pack/pack-53ae127e8f33c4a339f28b1b3ad39f18e67470df.idx
 and 
new/cosmic-comp-1.7.0/.git/objects/pack/pack-53ae127e8f33c4a339f28b1b3ad39f18e67470df.idx
 differ
Binary files 
old/cosmic-comp-1.6.0/.git/objects/pack/pack-53ae127e8f33c4a339f28b1b3ad39f18e67470df.pack
 and 
new/cosmic-comp-1.7.0/.git/objects/pack/pack-53ae127e8f33c4a339f28b1b3ad39f18e67470df.pack
 differ
Binary files 
old/cosmic-comp-1.6.0/.git/objects/pack/pack-53ae127e8f33c4a339f28b1b3ad39f18e67470df.rev
 and 
new/cosmic-comp-1.7.0/.git/objects/pack/pack-53ae127e8f33c4a339f28b1b3ad39f18e67470df.rev
 differ
Binary files 
old/cosmic-comp-1.6.0/.git/objects/pack/pack-c06bebfd96f9e6439e02edceb7e625010161296e.idx
 and 
new/cosmic-comp-1.7.0/.git/objects/pack/pack-c06bebfd96f9e6439e02edceb7e625010161296e.idx
 differ
Binary files 
old/cosmic-comp-1.6.0/.git/objects/pack/pack-c06bebfd96f9e6439e02edceb7e625010161296e.pack
 and 
new/cosmic-comp-1.7.0/.git/objects/pack/pack-c06bebfd96f9e6439e02edceb7e625010161296e.pack
 differ
Binary files 
old/cosmic-comp-1.6.0/.git/objects/pack/pack-c06bebfd96f9e6439e02edceb7e625010161296e.rev
 and 
new/cosmic-comp-1.7.0/.git/objects/pack/pack-c06bebfd96f9e6439e02edceb7e625010161296e.rev
 differ
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/cosmic-comp-1.6.0/.git/refs/remotes/origin/fix-check-all-connectors_noble 
new/cosmic-comp-1.7.0/.git/refs/remotes/origin/fix-check-all-connectors_noble
--- 
old/cosmic-comp-1.6.0/.git/refs/remotes/origin/fix-check-all-connectors_noble   
    1970-01-01 01:00:00.000000000 +0100
+++ 
new/cosmic-comp-1.7.0/.git/refs/remotes/origin/fix-check-all-connectors_noble   
    2026-08-25 17:33:56.000000000 +0200
@@ -0,0 +1 @@
+12f18758154535e1f8d4ad98d5bb755dc01830e8
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/cosmic-comp-1.6.0/.git/refs/remotes/origin/focus-follow-border_noble 
new/cosmic-comp-1.7.0/.git/refs/remotes/origin/focus-follow-border_noble
--- old/cosmic-comp-1.6.0/.git/refs/remotes/origin/focus-follow-border_noble    
1970-01-01 01:00:00.000000000 +0100
+++ new/cosmic-comp-1.7.0/.git/refs/remotes/origin/focus-follow-border_noble    
2026-08-25 17:33:56.000000000 +0200
@@ -0,0 +1 @@
+b942a9a64754aaca9c8ab0b5dd2fc59e5ef8823b
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/cosmic-comp-1.6.0/.git/refs/remotes/origin/master 
new/cosmic-comp-1.7.0/.git/refs/remotes/origin/master
--- old/cosmic-comp-1.6.0/.git/refs/remotes/origin/master       1970-01-01 
01:00:00.000000000 +0100
+++ new/cosmic-comp-1.7.0/.git/refs/remotes/origin/master       2026-08-25 
17:33:56.000000000 +0200
@@ -0,0 +1 @@
+5c93094574b31800caaa27255caac4a7253f24ad
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/cosmic-comp-1.6.0/.git/refs/remotes/origin/testing-cosmic-comp-pr2656_noble 
new/cosmic-comp-1.7.0/.git/refs/remotes/origin/testing-cosmic-comp-pr2656_noble
--- 
old/cosmic-comp-1.6.0/.git/refs/remotes/origin/testing-cosmic-comp-pr2656_noble 
    1970-01-01 01:00:00.000000000 +0100
+++ 
new/cosmic-comp-1.7.0/.git/refs/remotes/origin/testing-cosmic-comp-pr2656_noble 
    2026-08-25 17:33:56.000000000 +0200
@@ -0,0 +1 @@
+5505b25c81e38331902cca620d78ac2cac34badb
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/cosmic-comp-1.6.0/.git/refs/remotes/origin/update-smithay_noble 
new/cosmic-comp-1.7.0/.git/refs/remotes/origin/update-smithay_noble
--- old/cosmic-comp-1.6.0/.git/refs/remotes/origin/update-smithay_noble 
1970-01-01 01:00:00.000000000 +0100
+++ new/cosmic-comp-1.7.0/.git/refs/remotes/origin/update-smithay_noble 
2026-08-25 17:33:56.000000000 +0200
@@ -0,0 +1 @@
+08b82773c077a31e62cac127e7e6c3319ce78df5
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/cosmic-comp-1.6.0/.git/refs/tags/epoch-1.7.0 
new/cosmic-comp-1.7.0/.git/refs/tags/epoch-1.7.0
--- old/cosmic-comp-1.6.0/.git/refs/tags/epoch-1.7.0    1970-01-01 
01:00:00.000000000 +0100
+++ new/cosmic-comp-1.7.0/.git/refs/tags/epoch-1.7.0    2026-08-25 
17:33:56.000000000 +0200
@@ -0,0 +1 @@
+c0b3192fd9cc979b84d58ce7b444145e32aa65f9
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/cosmic-comp-1.6.0/Cargo.lock 
new/cosmic-comp-1.7.0/Cargo.lock
--- old/cosmic-comp-1.6.0/Cargo.lock    2026-08-18 13:26:18.000000000 +0200
+++ new/cosmic-comp-1.7.0/Cargo.lock    2026-08-25 17:33:56.000000000 +0200
@@ -817,6 +817,7 @@
  "profiling",
  "rand 0.10.0",
  "regex",
+ "resvg 0.45.1",
  "ron 0.12.0",
  "rust-embed",
  "rustix 1.1.4",
@@ -6893,9 +6894,9 @@
 
 [[package]]
 name = "xcursor"
-version = "0.3.10"
+version = "0.3.11"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "bec9e4a500ca8864c5b47b8b482a73d62e4237670e5b5f1d6b9e3cae50f28f2b"
+checksum = "163b33ed8786455e2fa5d72f554057ce3f3182425434f756cd39c99839d88e23"
 
 [[package]]
 name = "xdg"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/cosmic-comp-1.6.0/Cargo.toml 
new/cosmic-comp-1.7.0/Cargo.toml
--- old/cosmic-comp-1.6.0/Cargo.toml    2026-08-18 13:26:18.000000000 +0200
+++ new/cosmic-comp-1.7.0/Cargo.toml    2026-08-25 17:33:56.000000000 +0200
@@ -49,6 +49,7 @@
 ordered-float = "5.1"
 png = "0.18"
 regex = "1"
+resvg = "0.45"
 ron = "0.12"
 rust-embed = { version = "8.11", features = ["debug-embed"] }
 sanitize-filename = "0.6.0"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/cosmic-comp-1.6.0/cosmic-comp-config/src/lib.rs 
new/cosmic-comp-1.7.0/cosmic-comp-config/src/lib.rs
--- old/cosmic-comp-1.6.0/cosmic-comp-config/src/lib.rs 2026-08-18 
13:26:18.000000000 +0200
+++ new/cosmic-comp-1.7.0/cosmic-comp-config/src/lib.rs 2026-08-25 
17:33:56.000000000 +0200
@@ -101,6 +101,8 @@
     pub appearance_settings: AppearanceConfig,
     /// Hide the cursor after this many seconds of pointer inactivity (None 
disables)
     pub cursor_hide_timeout: Option<u32>,
+    /// Briefly magnify the cursor when the pointer is shaken, to help locate 
it
+    pub cursor_shake_to_find: bool,
     pub activation_policy: ActivationPolicy,
 }
 
@@ -139,6 +141,7 @@
             accessibility_zoom: ZoomConfig::default(),
             appearance_settings: AppearanceConfig::default(),
             cursor_hide_timeout: None,
+            cursor_shake_to_find: true,
             activation_policy: ActivationPolicy::default(),
         }
     }
@@ -195,6 +198,10 @@
     pub enable_mouse_zoom_shortcuts: bool,
 }
 
+impl ZoomConfig {
+    pub const ZOOM_INCREMENT_PRESETS: &[u32] = &[10, 25, 50, 75, 100, 150, 
200];
+}
+
 impl Default for ZoomConfig {
     fn default() -> Self {
         ZoomConfig {
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/cosmic-comp-1.6.0/src/backend/render/cursor.rs 
new/cosmic-comp-1.7.0/src/backend/render/cursor.rs
--- old/cosmic-comp-1.6.0/src/backend/render/cursor.rs  2026-08-18 
13:26:18.000000000 +0200
+++ new/cosmic-comp-1.7.0/src/backend/render/cursor.rs  2026-08-25 
17:33:56.000000000 +0200
@@ -8,6 +8,9 @@
     utils::prelude::*,
     wayland::handlers::compositor::FRAME_TIME_FILTER,
 };
+use keyframe::{ease, functions::EaseInOutCubic};
+use resvg::{tiny_skia, usvg};
+use serde::Deserialize;
 use smithay::{
     backend::{
         allocator::Fourcc,
@@ -38,7 +41,7 @@
     wayland::compositor::{get_role, with_states},
 };
 use std::{
-    collections::HashMap,
+    collections::{HashMap, VecDeque},
     io::Read,
     sync::Mutex,
     time::{Duration, Instant},
@@ -51,14 +54,43 @@
 
 static FALLBACK_CURSOR_DATA: &[u8] = 
include_bytes!("../../../resources/cursor.rgba");
 
+/// A single frame of a scalable SVG cursor: the parsed SVG plus its metadata.
+#[derive(Debug, Clone)]
+struct SvgFrame {
+    tree: usvg::Tree,
+    /// The nominal (logical) size the SVG is authored for.
+    nominal_size: f32,
+    /// Hotspot coordinates, in the SVG's own (nominal) coordinate space.
+    hotspot_x: f32,
+    hotspot_y: f32,
+    /// Delay to the next frame in milliseconds (0 for static cursors).
+    delay: u32,
+}
+
+#[derive(Debug, Clone)]
+enum CursorKind {
+    /// Legacy raster XCursor frames.
+    Xcursor(Vec<Image>),
+    /// Scalable SVG frames, rasterized on demand.
+    Svg(Vec<SvgFrame>),
+}
+
 #[derive(Debug, Clone)]
 pub struct Cursor {
-    icons: Vec<Image>,
+    kind: CursorKind,
     size: u32,
 }
 
 impl Cursor {
     pub fn load(theme: &CursorTheme, shape: CursorIcon, size: u32) -> Cursor {
+        // Prefer a scalable SVG cursor when the theme provides one.
+        if let Some(frames) = load_svg_icon(theme, shape) {
+            return Cursor {
+                kind: CursorKind::Svg(frames),
+                size,
+            };
+        }
+
         let icons = load_icon(theme, shape)
             .map_err(|err| warn!(?err, "Unable to load xcursor, using fallback 
cursor"))
             .or_else(|_| load_icon(theme, CursorIcon::Default))
@@ -75,44 +107,134 @@
                 }]
             });
 
-        Cursor { icons, size }
+        Cursor {
+            kind: CursorKind::Xcursor(icons),
+            size,
+        }
     }
 
     pub fn get_image(&self, scale: u32, millis: u32) -> Image {
         let size = self.size * scale;
-        frame(millis, size, &self.icons)
+        let idx = self.frame_index(size, millis);
+        self.render_frame(size, idx)
+    }
+
+    /// Selects the index of the frame to display at nominal size `size` (in 
px)
+    /// and elapsed `millis`.
+    fn frame_index(&self, size: u32, millis: u32) -> usize {
+        match &self.kind {
+            CursorKind::Xcursor(images) => xcursor_frame_index(millis, size, 
images),
+            CursorKind::Svg(frames) => svg_frame_index(millis, frames),
+        }
+    }
+
+    /// Produces the RGBA image for frame `idx`, rasterizing SVG cursors at the
+    /// requested nominal size `size` (in px).
+    fn render_frame(&self, size: u32, idx: usize) -> Image {
+        match &self.kind {
+            CursorKind::Xcursor(images) => images[idx].clone(),
+            CursorKind::Svg(frames) => rasterize_svg_frame(&frames[idx], size),
+        }
     }
 }
 
-fn nearest_images(size: u32, images: &[Image]) -> impl Iterator<Item = &Image> 
{
-    // Follow the nominal size of the cursor to choose the nearest
+/// Rasterize a scalable cursor frame at nominal pixel size `size`. Per the KDE
+/// SVG cursor format, the SVG canvas and hotspot are scaled by
+/// `size / nominal_size`. The returned [`Image`] uses the same premultiplied
+/// byte order (BGRA / little-endian ARGB) as xcursor images.
+fn rasterize_svg_frame(frame: &SvgFrame, size: u32) -> Image {
+    let factor = size as f32 / frame.nominal_size;
+    let svg_size = frame.tree.size();
+    let width = ((svg_size.width() * factor).floor() as u32).max(1);
+    let height = ((svg_size.height() * factor).floor() as u32).max(1);
+
+    let mut image = Image {
+        size,
+        width,
+        height,
+        xhot: (frame.hotspot_x * factor).floor() as u32,
+        yhot: (frame.hotspot_y * factor).floor() as u32,
+        delay: frame.delay,
+        pixels_rgba: Vec::new(),
+        pixels_argb: Vec::new(), // unused
+    };
+
+    match tiny_skia::Pixmap::new(width, height) {
+        Some(mut pixmap) => {
+            resvg::render(
+                &frame.tree,
+                tiny_skia::Transform::from_scale(factor, factor),
+                &mut pixmap.as_mut(),
+            );
+            // tiny-skia produces premultiplied RGBA; xcursor images consume 
the
+            // raw little-endian ARGB byte order (premultiplied BGRA). Swap 
R<->B.
+            let mut pixels = pixmap.take();
+            for px in pixels.as_chunks_mut::<4>().0 {
+                px.swap(0, 2);
+            }
+            image.pixels_rgba = pixels;
+        }
+        None => {
+            warn!(width, height, "Failed to allocate cursor pixmap");
+            image.pixels_rgba = vec![0; (width as usize) * (height as usize) * 
4];
+        }
+    }
+
+    image
+}
+
+/// Indices (into `images`) of all frames sharing the resolution nearest to 
`size`.
+fn nearest_image_indices(size: u32, images: &[Image]) -> Vec<usize> {
+    // Follow the nominal size of the cursor to choose the nearest.
     let nearest_image = images
         .iter()
         .min_by_key(|image| u32::abs_diff(size, image.size))
         .unwrap();
+    let (width, height) = (nearest_image.width, nearest_image.height);
 
-    images.iter().filter(move |image| {
-        image.width == nearest_image.width && image.height == 
nearest_image.height
-    })
+    images
+        .iter()
+        .enumerate()
+        .filter(|(_, image)| image.width == width && image.height == height)
+        .map(|(i, _)| i)
+        .collect()
 }
 
-fn frame(mut millis: u32, size: u32, images: &[Image]) -> Image {
-    let total = nearest_images(size, images).fold(0, |acc, image| acc + 
image.delay);
+fn xcursor_frame_index(mut millis: u32, size: u32, images: &[Image]) -> usize {
+    let indices = nearest_image_indices(size, images);
+    let total: u32 = indices.iter().map(|&i| images[i].delay).sum();
 
     if total == 0 {
-        millis = 0;
-    } else {
-        millis %= total;
+        return indices[0];
     }
+    millis %= total;
 
-    for img in nearest_images(size, images) {
-        if millis <= img.delay {
-            return img.clone();
+    for &i in &indices {
+        if millis <= images[i].delay {
+            return i;
         }
-        millis -= img.delay;
+        millis -= images[i].delay;
     }
 
-    unreachable!()
+    *indices.last().unwrap()
+}
+
+fn svg_frame_index(mut millis: u32, frames: &[SvgFrame]) -> usize {
+    let total: u32 = frames.iter().map(|frame| frame.delay).sum();
+
+    if total == 0 {
+        return 0;
+    }
+    millis %= total;
+
+    for (i, frame) in frames.iter().enumerate() {
+        if millis <= frame.delay {
+            return i;
+        }
+        millis -= frame.delay;
+    }
+
+    frames.len() - 1
 }
 
 #[derive(thiserror::Error, Debug)]
@@ -174,6 +296,79 @@
     Err(Error::NoDefaultCursor)
 }
 
+/// A frame entry in a `cursors_scalable/<shape>/metadata.json` file, per the
+/// KDE SVG cursor format specification.
+#[derive(Debug, Clone, Deserialize)]
+struct SvgCursorMeta {
+    filename: String,
+    nominal_size: f32,
+    hotspot_x: f32,
+    hotspot_y: f32,
+    /// Only present for animated cursors; defaults to 0 for static ones.
+    #[serde(default)]
+    delay: u32,
+}
+
+/// Resolves a scalable (SVG) cursor for `shape`
+fn load_svg_icon(theme: &CursorTheme, shape: CursorIcon) -> 
Option<Vec<SvgFrame>> {
+    let shape_name = shape.to_string();
+    let options = usvg::Options::default();
+    for name in cursor_aliases(&shape_name)
+        .iter()
+        .copied()
+        .chain(std::iter::once(shape_name.as_str()))
+    {
+        if let Some(dir) = theme.load_scalable(name)
+            && let Some(frames) = parse_svg_dir(&dir, &options)
+        {
+            return Some(frames);
+        }
+    }
+
+    None
+}
+
+/// Reads a `cursors_scalable/<shape>` directory (its `metadata.json` and the
+/// referenced SVG files) into a list of parsed frames.
+fn parse_svg_dir(dir: &std::path::Path, options: &usvg::Options) -> 
Option<Vec<SvgFrame>> {
+    let metadata = std::fs::read(dir.join("metadata.json")).ok()?;
+    let metas: Vec<SvgCursorMeta> = match serde_json::from_slice(&metadata) {
+        Ok(metas) => metas,
+        Err(err) => {
+            warn!(?dir, ?err, "Malformed SVG cursor metadata");
+            return None;
+        }
+    };
+
+    let mut frames = Vec::with_capacity(metas.len());
+    for meta in metas {
+        let svg_path = dir.join(&meta.filename);
+        let svg_data = match std::fs::read(&svg_path) {
+            Ok(data) => data,
+            Err(err) => {
+                warn!(?svg_path, ?err, "Unable to read SVG cursor");
+                break;
+            }
+        };
+        let tree = match usvg::Tree::from_data(&svg_data, options) {
+            Ok(tree) => tree,
+            Err(err) => {
+                warn!(?svg_path, ?err, "Unable to parse SVG cursor");
+                break;
+            }
+        };
+        frames.push(SvgFrame {
+            tree,
+            nominal_size: meta.nominal_size,
+            hotspot_x: meta.hotspot_x,
+            hotspot_y: meta.hotspot_y,
+            delay: meta.delay,
+        });
+    }
+
+    (!frames.is_empty()).then_some(frames)
+}
+
 render_elements! {
     pub CursorRenderElement<R> where R: ImportAll + ImportMem + 
AsGlowRenderer, R::TextureId: Send;
     Static=MemoryRenderBufferRenderElement<R>,
@@ -265,11 +460,62 @@
 
     cursors: HashMap<CursorIcon, Cursor>,
     current_image: Option<Image>,
-    image_cache: Vec<(Image, MemoryRenderBuffer)>,
+    image_cache: Vec<CachedFrame>,
 
     hidden: bool,
     idle_timer: Option<RegistrationToken>,
     last_armed: Option<Instant>,
+
+    // shake-to-find
+    shake_path: VecDeque<PathSample>,
+    shake_path_position: Point<f64, Logical>,
+    magnify_until: Option<Instant>,
+    magnify_target: f32,
+    magnification: f32,
+    anim_from: f32,
+    anim_start: Option<Instant>,
+    rest_started: Option<Instant>,
+}
+
+/// A rasterized cursor frame, keyed by `(shape, pixel size, frame index)`.
+struct CachedFrame {
+    key: (CursorIcon, u32, usize),
+    image: Image,
+    buffer: MemoryRenderBuffer,
+    unmagnified: bool,
+}
+
+/// One sampled pointer position on the recent motion path.
+#[derive(Clone, Copy)]
+struct PathSample {
+    position: Point<f64, Logical>,
+    time: Instant,
+}
+
+/// How long everything must stay unmagnified before the enlarged frames go.
+const MAGNIFIED_FRAME_GRACE: Duration = Duration::from_secs(10);
+
+/// How far back the motion path is considered when looking for a shake.
+const SHAKE_INTERVAL: Duration = Duration::from_millis(1000);
+/// Path-length / bounding-box-diagonal ratio required to count as a shake.
+const SHAKE_SENSITIVITY: f64 = 4.0;
+/// Minimum bounding-box diagonal (logical px) before a shake is considered.
+const SHAKE_DIAGONAL_MIN: f64 = 100.0;
+/// Two deltas count as "the same direction" if both lie within this tolerance.
+const SHAKE_SAME_SIGN_TOLERANCE: f64 = 1.0;
+/// Keep the cursor enlarged for this long after the last detected shake.
+const SHAKE_HOLD: Duration = Duration::from_millis(2000);
+/// Extra magnification added by each shake, growing from the normal cursor 
size.
+const OVER_MAGNIFICATION: f32 = 1.0;
+/// Upper bound on the nominal size (in px) a cursor frame is rasterized at.
+const MAX_RASTER_SIZE: u32 = 512;
+/// Duration of the grow/shrink animation.
+const MAGNIFICATION_ANIM: Duration = Duration::from_millis(200);
+
+/// small movement is ignored and direction stays the same
+fn same_direction(a: f64, b: f64) -> bool {
+    (a >= -SHAKE_SAME_SIGN_TOLERANCE && b >= -SHAKE_SAME_SIGN_TOLERANCE)
+        || (a <= SHAKE_SAME_SIGN_TOLERANCE && b <= SHAKE_SAME_SIGN_TOLERANCE)
 }
 
 impl CursorStateInner {
@@ -282,14 +528,141 @@
     }
 
     pub fn get_named_cursor(&mut self, shape: CursorIcon) -> &Cursor {
+        let cursor_theme = &self.cursor_theme;
+        let cursor_size = self.cursor_size;
         self.cursors
             .entry(shape)
-            .or_insert_with(|| Cursor::load(&self.cursor_theme, shape, 
self.cursor_size))
+            .or_insert_with(|| Cursor::load(cursor_theme, shape, cursor_size))
     }
 
     pub fn size(&self) -> u32 {
         self.cursor_size
     }
+
+    /// Drop the rasterizations only a magnified cursor needed, once nothing 
has
+    /// magnified it for [`MAGNIFIED_FRAME_GRACE`].
+    pub fn drop_magnified_frames(&mut self, now: Instant, zoomed: bool) {
+        if zoomed || self.is_magnifying() {
+            self.rest_started = None;
+            return;
+        }
+        let rest_started = *self.rest_started.get_or_insert(now);
+        if now.duration_since(rest_started) < MAGNIFIED_FRAME_GRACE {
+            return;
+        }
+
+        self.image_cache.retain(|frame| frame.unmagnified);
+    }
+
+    /// Feed one relative-motion event into the shake detector.
+    pub fn detect_shake(&mut self, delta: Point<f64, Logical>, now: Instant) {
+        // Drop samples that have aged out of the time window.
+        while let Some(oldest) = self.shake_path.front() {
+            if now.duration_since(oldest.time) >= SHAKE_INTERVAL {
+                self.shake_path.pop_front();
+            } else {
+                break;
+            }
+        }
+
+        if delta.x != 0.0 || delta.y != 0.0 {
+            self.shake_path_position += delta;
+            let sample = PathSample {
+                position: self.shake_path_position,
+                time: now,
+            };
+
+            if self.shake_path.len() >= 2 {
+                let last = self.shake_path[self.shake_path.len() - 1].position;
+                let prev = self.shake_path[self.shake_path.len() - 2].position;
+                let last_delta = last - prev;
+                if same_direction(last_delta.x, delta.x) && 
same_direction(last_delta.y, delta.y) {
+                    *self.shake_path.back_mut().unwrap() = sample;
+                } else {
+                    self.shake_path.push_back(sample);
+                }
+            } else {
+                self.shake_path.push_back(sample);
+            }
+        }
+
+        if self.shake_path.len() < 2 {
+            return;
+        }
+
+        let first = self.shake_path[0].position;
+        let (mut left, mut top, mut right, mut bottom) = (first.x, first.y, 
first.x, first.y);
+        let mut path_length = 0.0;
+        for i in 1..self.shake_path.len() {
+            let p = self.shake_path[i].position;
+            left = left.min(p.x);
+            top = top.min(p.y);
+            right = right.max(p.x);
+            bottom = bottom.max(p.y);
+
+            let step = p - self.shake_path[i - 1].position;
+            path_length += step.x.hypot(step.y);
+        }
+
+        let diagonal = (right - left).hypot(bottom - top);
+        if diagonal < SHAKE_DIAGONAL_MIN {
+            return;
+        }
+
+        // Path noticeably longer than the diagonal => a shake gesture.
+        if path_length / diagonal > SHAKE_SENSITIVITY {
+            self.grow(now);
+            self.shake_path.clear();
+        }
+    }
+
+    /// grow the cursor by one more increment (unbounded)
+    fn grow(&mut self, now: Instant) {
+        self.animate_to(self.magnify_target + OVER_MAGNIFICATION, now);
+        self.magnify_until = Some(now + SHAKE_HOLD);
+    }
+
+    /// Start a 200ms `InOutCubic` tween from the current size to `target`.
+    fn animate_to(&mut self, target: f32, now: Instant) {
+        if (target - self.magnify_target).abs() < f32::EPSILON {
+            return;
+        }
+        self.anim_from = self.magnification;
+        self.anim_start = Some(now);
+        self.magnify_target = target;
+    }
+
+    /// Advance the magnification animation and return the current factor.
+    pub fn animated_magnification(&mut self, now: Instant) -> f32 {
+        // Begin shrinking back once the hold window elapses.
+        if let Some(until) = self.magnify_until
+            && now >= until
+        {
+            self.magnify_until = None;
+            self.animate_to(1.0, now);
+        }
+
+        self.magnification = match self.anim_start {
+            Some(start) => {
+                // `ease` clamps the time to `0.0..=1.0` for us.
+                let t = now.duration_since(start).as_secs_f32() / 
MAGNIFICATION_ANIM.as_secs_f32();
+                if t >= 1.0 {
+                    self.anim_start = None;
+                }
+                ease(EaseInOutCubic, self.anim_from, self.magnify_target, t)
+            }
+            None => self.magnify_target,
+        };
+        self.magnification
+    }
+
+    /// Whether the cursor is currently magnified or pending; drives continued 
redraws.
+    pub fn is_magnifying(&self) -> bool {
+        self.magnify_until.is_some()
+            || self.anim_start.is_some()
+            || self.magnification > 1.001
+            || self.magnify_target > 1.001
+    }
 }
 
 pub fn load_cursor_env() -> (String, u32) {
@@ -324,10 +697,34 @@
             hidden: false,
             idle_timer: None,
             last_armed: None,
+
+            shake_path: VecDeque::new(),
+            shake_path_position: Point::from((0.0, 0.0)),
+            magnify_until: None,
+            magnify_target: 1.0,
+            magnification: 1.0,
+            anim_from: 1.0,
+            anim_start: None,
+            rest_started: None,
         }
     }
 }
 
+/// Pick the size a cursor frame is rasterized at, given the size the output 
wants
+/// (`needed`) and the size it would want unmagnified (`base`).
+///
+/// Rasterizations are restricted to `base * 2^n`, rounded up, and clamped to
+/// [`MAX_RASTER_SIZE`].
+fn raster_size(needed: u32, base: u32) -> u32 {
+    let base = base.max(1);
+    let cap = MAX_RASTER_SIZE.max(base);
+    let mut rung = base;
+    while rung < needed && rung.saturating_mul(2) <= cap {
+        rung *= 2;
+    }
+    rung
+}
+
 #[profiling::function]
 pub fn draw_cursor<R>(
     renderer: &mut R,
@@ -363,32 +760,53 @@
             return;
         }
 
-        let integer_scale = (scale.x.max(scale.y) * buffer_scale).ceil() as 
u32;
-        let frame = state
-            .get_named_cursor(current_cursor)
-            .get_image(integer_scale, time.as_millis());
-        let actual_scale = (frame.size / state.size()).max(1);
+        let output_scale = scale.x.max(scale.y);
+        let integer_scale = (output_scale * buffer_scale).ceil() as u32;
+        let unmagnified_px = state.size() * (output_scale.ceil() as u32);
+        let size_px = raster_size(state.size() * integer_scale, 
unmagnified_px);
+
+        // Pick the frame to display without rasterizing, so a cache hit avoids
+        // any SVG rendering. The `&Cursor` borrow is scoped to this block.
+        let frame_idx = {
+            let cursor = state.get_named_cursor(current_cursor);
+            cursor.frame_index(size_px, time.as_millis())
+        };
+        let key = (current_cursor, size_px, frame_idx);
 
-        let pointer_images = &mut state.image_cache;
-        let maybe_image = pointer_images
-            .iter()
-            .find_map(|(image, texture)| if image == &frame { Some(texture) } 
else { None });
-        let pointer_image = match maybe_image {
-            Some(image) => image,
+        // Rasterize and upload this (shape, size, frame) only if not cached.
+        let index = match state.image_cache.iter().position(|frame| frame.key 
== key) {
+            Some(index) => index,
             None => {
+                let image = {
+                    let cursor = state.get_named_cursor(current_cursor);
+                    cursor.render_frame(size_px, frame_idx)
+                };
+                let actual_scale = (image.size / state.size()).max(1);
                 let buffer = MemoryRenderBuffer::from_slice(
-                    &frame.pixels_rgba,
+                    &image.pixels_rgba,
                     Fourcc::Argb8888,
-                    (frame.width as i32, frame.height as i32),
+                    (image.width as i32, image.height as i32),
                     actual_scale as i32,
                     Transform::Normal,
                     None,
                 );
-                pointer_images.push((frame.clone(), buffer));
-                pointer_images.last().map(|(_, i)| i).unwrap()
+                state.image_cache.push(CachedFrame {
+                    key,
+                    image,
+                    buffer,
+                    unmagnified: size_px == unmagnified_px,
+                });
+                state.image_cache.len() - 1
             }
         };
 
+        let (frame, pointer_image) = {
+            let entry = &mut state.image_cache[index];
+            entry.unmagnified |= size_px == unmagnified_px;
+            (entry.image.clone(), entry.buffer.clone())
+        };
+        let actual_scale = (frame.size / state.size()).max(1);
+
         let hotspot = Point::<i32, BufferCoords>::from((frame.xhot as i32, 
frame.yhot as i32))
             .to_logical(
                 actual_scale as i32,
@@ -402,7 +820,7 @@
                 MemoryRenderBufferRenderElement::from_buffer(
                     renderer,
                     location.to_physical(scale),
-                    pointer_image,
+                    &pointer_image,
                     None,
                     None,
                     None,
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/cosmic-comp-1.6.0/src/backend/render/element.rs 
new/cosmic-comp-1.7.0/src/backend/render/element.rs
--- old/cosmic-comp-1.6.0/src/backend/render/element.rs 2026-08-18 
13:26:18.000000000 +0200
+++ new/cosmic-comp-1.7.0/src/backend/render/element.rs 2026-08-25 
17:33:56.000000000 +0200
@@ -41,7 +41,9 @@
     Workspace(
         
RelocateRenderElement<CropRenderElement<RescaleRenderElement<WorkspaceRenderElement<R>>>>,
     ),
-    
Cursor(RescaleRenderElement<RelocateRenderElement<CursorRenderElement<R>>>),
+    Cursor(
+        
RescaleRenderElement<RescaleRenderElement<RelocateRenderElement<CursorRenderElement<R>>>>,
+    ),
     Dnd(SurfaceRenderElement<R>),
     MoveGrab(RescaleRenderElement<CosmicMappedRenderElement<R>>),
     Postprocess(
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/cosmic-comp-1.6.0/src/backend/render/mod.rs 
new/cosmic-comp-1.7.0/src/backend/render/mod.rs
--- old/cosmic-comp-1.6.0/src/backend/render/mod.rs     2026-08-18 
13:26:18.000000000 +0200
+++ new/cosmic-comp-1.7.0/src/backend/render/mod.rs     2026-08-25 
17:33:56.000000000 +0200
@@ -510,22 +510,35 @@
         };
         let location = pointer.current_location() - 
output.current_location().to_f64();
 
+        // Shake-to-find magnification, applied around the pointer tip.
+        let cursor_magnification = seat
+            .user_data()
+            .get::<cursor::CursorState>()
+            .map_or(1.0, |s| {
+                s.lock().unwrap().animated_magnification(Instant::now())
+            });
+        let cursor_center = location.to_physical(scale).to_i32_round();
+
         if mode != CursorMode::None {
             cursor::draw_cursor(
                 renderer,
                 seat,
                 location,
                 scale.into(),
-                zoom_scale,
+                zoom_scale * cursor_magnification as f64,
                 now,
                 blur_strength,
                 mode != CursorMode::NotDefault,
                 &mut |elem, hotspot| {
                     
push(CosmicElement::Cursor(RescaleRenderElement::from_element(
-                        RelocateRenderElement::from_element(
-                            elem,
-                            Point::from((-hotspot.x, -hotspot.y)),
-                            Relocate::Relative,
+                        RescaleRenderElement::from_element(
+                            RelocateRenderElement::from_element(
+                                elem,
+                                Point::from((-hotspot.x, -hotspot.y)),
+                                Relocate::Relative,
+                            ),
+                            cursor_center,
+                            cursor_magnification as f64,
                         ),
                         focal_point
                             .as_logical()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/cosmic-comp-1.6.0/src/config/mod.rs 
new/cosmic-comp-1.7.0/src/config/mod.rs
--- old/cosmic-comp-1.6.0/src/config/mod.rs     2026-08-18 13:26:18.000000000 
+0200
+++ new/cosmic-comp-1.7.0/src/config/mod.rs     2026-08-25 17:33:56.000000000 
+0200
@@ -969,6 +969,10 @@
                     }
                 }
             }
+            "cursor_shake_to_find" => {
+                let new = get_config::<bool>(&config, "cursor_shake_to_find");
+                state.common.config.cosmic_conf.cursor_shake_to_find = new;
+            }
             "cursor_hide_timeout" => {
                 let new = get_config::<Option<u32>>(&config, 
"cursor_hide_timeout");
                 if new != state.common.config.cosmic_conf.cursor_hide_timeout {
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/cosmic-comp-1.6.0/src/input/actions.rs 
new/cosmic-comp-1.7.0/src/input/actions.rs
--- old/cosmic-comp-1.6.0/src/input/actions.rs  2026-08-18 13:26:18.000000000 
+0200
+++ new/cosmic-comp-1.7.0/src/input/actions.rs  2026-08-25 17:33:56.000000000 
+0200
@@ -29,6 +29,8 @@
 
 use super::gestures;
 
+const MAX_ZOOM: f64 = 256.0;
+
 fn propagate_by_default(action: &shortcuts::Action) -> bool {
     matches!(
         action,
@@ -1116,7 +1118,14 @@
         }
 
         if zoom_seat == *seat {
-            let new_level = (current_level + change).max(1.0);
+            let factor = 1.0 + change.abs();
+            let new_level = if change < 0. {
+                current_level / factor
+            } else {
+                current_level * factor
+            }
+            .clamp(1.0, MAX_ZOOM);
+            let new_level = if new_level < 1.01 { 1.0 } else { new_level };
             shell.trigger_zoom(
                 seat,
                 Some(&output),
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/cosmic-comp-1.6.0/src/input/mod.rs 
new/cosmic-comp-1.7.0/src/input/mod.rs
--- old/cosmic-comp-1.6.0/src/input/mod.rs      2026-08-18 13:26:18.000000000 
+0200
+++ new/cosmic-comp-1.7.0/src/input/mod.rs      2026-08-25 17:33:56.000000000 
+0200
@@ -337,6 +337,21 @@
                     notify_cursor_activity(self, &seat);
                     let current_output = seat.active_output();
 
+                    if self.common.config.cosmic_conf.cursor_shake_to_find
+                        && let Some(cursor_state) =
+                            seat.user_data()
+                                
.get::<crate::backend::render::cursor::CursorState>()
+                    {
+                        let active = {
+                            let mut cursor = cursor_state.lock().unwrap();
+                            cursor.detect_shake(event.delta(), 
std::time::Instant::now());
+                            cursor.is_magnifying()
+                        };
+                        if active {
+                            self.backend.schedule_render(&current_output);
+                        }
+                    }
+
                     let mut position = 
seat.get_pointer().unwrap().current_location().as_global();
 
                     let under = State::surface_under(position, 
&current_output, &shell)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/cosmic-comp-1.6.0/src/shell/mod.rs 
new/cosmic-comp-1.7.0/src/shell/mod.rs
--- old/cosmic-comp-1.6.0/src/shell/mod.rs      2026-08-18 13:26:18.000000000 
+0200
+++ new/cosmic-comp-1.7.0/src/shell/mod.rs      2026-08-25 17:33:56.000000000 
+0200
@@ -1614,6 +1614,29 @@
             a11y_keyboard_monitor.refresh();
         }
         self.image_copy_capture_state.cleanup();
+        self.cleanup_cursor_images();
+    }
+
+    /// Release the enlarged cursor frames a finished shake or zoom left 
behind.
+    fn cleanup_cursor_images(&mut self) {
+        let shell = self.shell.read();
+        let zoomed = shell.zoom_state.as_ref().is_some_and(|zoom_state| {
+            shell
+                .outputs()
+                .any(|output| zoom_state.animating_level(output) > 1.0)
+        });
+        let now = Instant::now();
+        for seat in shell.seats.iter() {
+            if let Some(cursor_state) = seat
+                .user_data()
+                .get::<crate::backend::render::cursor::CursorState>()
+            {
+                cursor_state
+                    .lock()
+                    .unwrap()
+                    .drop_magnified_frames(now, zoomed);
+            }
+        }
     }
 
     pub fn refresh_idle_inhibit(&mut self) {
@@ -2286,6 +2309,11 @@
                         .is_some_and(|state| 
state.lock().unwrap().is_animating())
                 })
             })
+            || self.seats.iter().any(|seat| {
+                seat.user_data()
+                    .get::<crate::backend::render::cursor::CursorState>()
+                    .is_some_and(|state| state.lock().unwrap().is_magnifying())
+            })
     }
 
     pub fn update_animations(&mut self) -> HashMap<ClientId, Client> {
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/cosmic-comp-1.6.0/src/shell/zoom.rs 
new/cosmic-comp-1.7.0/src/shell/zoom.rs
--- old/cosmic-comp-1.6.0/src/shell/zoom.rs     2026-08-18 13:26:18.000000000 
+0200
+++ new/cosmic-comp-1.7.0/src/shell/zoom.rs     2026-08-25 17:33:56.000000000 
+0200
@@ -7,7 +7,7 @@
     theme,
     widget::{self, icon::Named},
 };
-use cosmic_comp_config::ZoomMovement;
+use cosmic_comp_config::{ZoomConfig, ZoomMovement};
 use cosmic_config::ConfigSet;
 use keyframe::{ease, functions::Linear};
 use smithay::{
@@ -447,7 +447,7 @@
 
 impl ZoomProgram {
     pub fn new(level: f64, movement: ZoomMovement, increment: u32) -> Self {
-        let mut increments = vec![25, 50, 100, 150, 200];
+        let mut increments = ZoomConfig::ZOOM_INCREMENT_PRESETS.to_vec();
         if !increments.contains(&increment) {
             increments.push(increment);
         }
@@ -828,7 +828,7 @@
                 if let Some(pos) = self.increments.iter().position(|val| *val 
== increment) {
                     self.increment_idx = pos;
                 } else {
-                    let mut increments = vec![25, 50, 100, 150, 200];
+                    let mut increments = 
ZoomConfig::ZOOM_INCREMENT_PRESETS.to_vec();
                     if !increments.contains(&increment) {
                         increments.push(increment);
                     }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/cosmic-comp-1.6.0/src/wayland/handlers/session_lock.rs 
new/cosmic-comp-1.7.0/src/wayland/handlers/session_lock.rs
--- old/cosmic-comp-1.6.0/src/wayland/handlers/session_lock.rs  2026-08-18 
13:26:18.000000000 +0200
+++ new/cosmic-comp-1.7.0/src/wayland/handlers/session_lock.rs  2026-08-25 
17:33:56.000000000 +0200
@@ -46,6 +46,11 @@
         let mut shell = self.common.shell.write();
         shell.session_lock = None;
 
+        let seats = shell.seats.iter().cloned().collect::<Vec<_>>();
+        for seat in &seats {
+            self.common.idle_notifier_state.notify_activity(seat);
+        }
+
         for output in shell.outputs() {
             self.backend.schedule_render(output);
         }

++++++ cosmic-comp.obsinfo ++++++
--- /var/tmp/diff_new_pack.3QF0Lf/_old  2026-08-28 20:01:01.327585083 +0200
+++ /var/tmp/diff_new_pack.3QF0Lf/_new  2026-08-28 20:01:01.336585398 +0200
@@ -1,5 +1,5 @@
 name: cosmic-comp
-version: 1.6.0
-mtime: 1787052378
-commit: 314fc670a83f1e570b069cd709df809ba99fa334
+version: 1.7.0
+mtime: 1787672036
+commit: 31827ed2409f92d3bc224d3ce601c6f2136183fb
 

++++++ vendor.tar.zst ++++++
/work/SRC/openSUSE:Factory/cosmic-comp/vendor.tar.zst 
/work/SRC/openSUSE:Factory/.cosmic-comp.new.1265/vendor.tar.zst differ: char 7, 
line 1

Reply via email to