1. Change magnifier zoom stepping from additive to multiplicative,
because zoom is a ratio, not a difference, and human perception is
logarithmic (browser zoom steps, map zoom levels, microscope powers).
2. Add `pub const ZoomConfig::ZOOM_INCREMENT_PRESETS` with two
additional presets: 10% (macOS, Cinnamon) and 75% (for users at
70-90x zoom).
3. Bound the zoom level to a generous maximum, beyond what anyone is expected
to need, to prevent runaway / unrecoverable levels (#1230).
Full background and rationale at #2615.
I used AI to assist with research and issue writeup. Code is 100% mine.
- Closes#2615.
- Fixes#1239.
- Partially addresses #1230 and #1237.
Revient sur 7187efd5, qui elisait le rafraichissement le plus eleve a la
resolution native au lieu du mode marque PREFERRED.
Sans effet au quotidien : ~/.local/state/cosmic-comp/outputs.ron memorise
le mode choisi par sortie et pin deja DP-1 en 3840x2160 a 119,88 Hz.
populate_modes ne decide qu'en l'absence d'etat memorise, c'est-a-dire sur
un profil neuf ou un ecran jamais vu.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Revient sur 3d01a219. La feature "tokio" de libcosmic-yoda activait
zbus?/tokio, ce qui imposait un Handle::current() ambiant alors que la
boucle de cosmic-comp n'est pas async ; d'ou le garde de runtime dans
main(). La feature est retiree de la dependance libcosmic, zbus n'a donc
plus besoin de reacteur Tokio et le garde n'a plus lieu d'etre.
Cargo.lock perd tokio et toute la chaine qui venait avec la feature :
ashpd, rfd, atspi, accesskit_unix, zbus_xml, tokio-stream.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
SessionLockHandler::unlock clears the lock and schedules a render, but
never tells IdleNotifierState that the user is there. Unlock paths that
produce no input event -- fingerprint and smartcard readers -- therefore
leave the idle clock running across the entire lock period, and it is
already past its timeouts by the time the session is unlocked.
The visible result is that the screen stays off after a successful
unlock until the user moves the mouse or presses a key, or that the
session locks itself again seconds after being unlocked.
Notifying activity for every seat in unlock() resets the timers the same
way an input event would.
Drafted with AI assistance (Claude Code); the change was reviewed,
built and tested by me on real hardware as described.
Signed-off-by: chris-010 <10660568+chris-010@users.noreply.github.com>
32 commits amont, aucun conflit de code : seul le lockfile divergeait,
il est regenere. Les 9 commits yoda sont preserves, dont le patch des
onglets de piles suivant WindowControlsPosition.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The window stack tabs build their own Tab widget rather than using
libcosmic segmented_button, so they kept the close button on the right
even with macOS-style controls enabled.
Order the tab items from the configured position: close first when
Start, last when End, with the container alignment following.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
- Use the configured keyboard layout, track ei seats to reconfigure their keyboards
- DBus interface
- Disambiguate input devices per backend instance
- Add support for `ei_keysym` and `ei_text`
- Act as input method for ei_text UTF-8 injection
- Create a region per output for abosolute pointer position
- Map ei region for touchscreen emulation
- Release pointer buttons and keys on removal
- Send modifiers to EI connections
GetCornerRadiusLayer looked up the existing hook via ToplevelHookId while
inserting into LayerHookId a few lines later. Both are
Mutex<Option<(HookId, Weak<..>)>> so it compiles, but they are distinct
data_map slots: the "does this surface already have a corner-radius object?"
check for a layer surface read the toplevel slot, so it could not see a layer
object that was already there, and it created a toplevel entry on a layer
surface as a side effect.
Every other read of the layer hook already uses LayerHookId.
Disclosure: found and written with AI assistance (Claude). The change is the
one line quoted above; I understand it and verified the tree builds with it.
When the DRM device is lost transiently, for example when the amdgpu driver
performs a GPU reset and recovers ("device wedged, but recovered through
reset"), acquiring a renderer in `SurfaceThreadState::redraw` fails. The three
`renderer`/`single_renderer` calls used `.unwrap()`, so the failure panicked
the surface's render thread. Because that panic unwinds across the EGL/GBM FFI
boundary it aborts the whole process (SIGABRT), dropping the session back to
the greeter and closing every running application.
`redraw` is already fallible and its caller logs the error and reschedules a
redraw, and the frame-submission path (`render_frame`/`queue_frame`) already
propagates errors the same way. Propagate the renderer-acquisition errors too,
so a transient device loss is retried on the next frame, once the device is
back, instead of taking down the compositor.
Relates to #649.
Assisted by an AI coding tool; I have reviewed and fully understand the change
and am able to maintain it and respond to review.
populate_modes() now selects the mode with the highest refresh rate at
the preferred (native) resolution instead of blindly using the EDID
PREFERRED flag. This ensures high-refresh panels (120 Hz, 144 Hz, …)
are used out-of-the-box, including in the greeter session.
Also fix libcosmic → libcosmic-yoda package reference.
A11yKeyboardMonitorState::key_event calls self.has_key_grab() inside
self.clients guard loop.
A "well-behaved" a11y client calls `WatchKeyboard` before `SetKeyGrabs`.
If WatchKeyboard is not called first, the `!client.watched` guard does
not short-circuit, so `has_key_grab()` runs and locks `self.clients` again.
This causes a deadlock and the compositor freezes, requiring a hard reboot.
This commit moves the `self.has_key_grab()` before the guard to
prevent a double lock.
display_configuration() issued an ALLOW_MODESET atomic commit on every udev
event, even when no planes needed detaching. The empty commit is a no-op
modeset that interferes with in-flight commits on the render thread, and it
fails with EPERM whenever we don't hold DRM master (e.g. a render-only
secondary GPU), aborting device enumeration.
Track whether any property was added and skip the commit when there is nothing
to do. A commit that does have changes still propagates its error as before, so
a genuine failure to apply cleanup is not silently swallowed.
https://github.com/pop-os/cosmic-comp/issues/2375
Authored by Claude Opus 4.8
Reviewed by Timo Strunk <Timo.Strunk@gmail.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
When the kiosk child exits, calling process::exit() directly from the calloop
callback runs libc's atexit handlers (Mesa's util_queue_kill_threads) while a
KMS surface thread may still be mid-frame in eglCreateSync(). Concurrent Mesa
teardown and live EGL use corrupt the heap and glibc aborts the process
("free(): corrupted unsorted chunks", SIGABRT).
Stop the event loop cleanly via should_stop instead, stashing the child's exit
code in Common::kiosk_exit_code. After the loop returns, pause() each DRM device
(releasing master so the surface drop path skips its blocking clear_state(), and
the incoming compositor gets master immediately), then drop_and_join() every
surface so no thread is left in Mesa. Only then is process::exit() called, at
which point atexit is safe to run.
The join is done here rather than in Surface::Drop because an unconditional join
in Drop deadlocks against apply_config_for_outputs during normal operation; once
the event loop has stopped, the surface thread can process ThreadCommand::End
and exit without the main thread servicing any pending message.
https://github.com/pop-os/cosmic-comp/issues/2375
Authored by Claude Opus 4.8
Reviewed by Timo Strunk <Timo.Strunk@gmail.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>