An unattended run takes twenty minutes and said nothing at all while it worked.
The notification area now carries a live entry for the duration - which step is
running, which package is being unpacked, item count, percentage. It is a job
in the sense of org.kde.JobViewServer, the same mechanism Dolphin uses while
copying files, rather than a notification, which is what makes it a real bar
instead of a line of text.
That mechanism needs a process of its own. The desktop ties a job to the D-Bus
connection that requested it and withdraws it the moment that connection
closes, so gdbus, busctl and dbus-send cannot drive one at all - every
invocation is a fresh connection that closes again immediately. A small helper
therefore runs inside each graphical session for the length of the update,
holding the connection open and taking instructions on stdin. It needs
python-gobject; without it there is no bar and nothing else changes. Position
within the repository step is read from pacman's own "(120/260) upgrading foo"
lines. Its other (n/m) sequences are ignored on purpose: checking keys, package
integrity and loading files each count up to the same total, and following them
would run the bar to the end three times before the first package was unpacked.
The "system updated" notification never arrived, which is what prompted looking
at any of this. Tagged notifications were posted with --replace-id naming the
start message, and Plasma silently drops a Notify() whose replaces_id points at
an expired notification: no bubble, no error, and the id it hands back is the
dead one it just ignored. The start bubble times out in seconds and a run lasts
minutes, so the result fell into exactly that hole every single time. The
previous message is now withdrawn and a fresh one posted.
How long a message stays is set per message rather than left to the daemon.
Anything reporting that the machine still needs a person - a failed update, a
locked package database, packages that had to be held back - waits until it is
dismissed. Everything else times out on its own, a successful update included;
nothing should have to be clicked away for having gone right. Daemons do keep
critical-urgency messages up and the specification asks them to, but that is a
should, it says nothing about the normal-urgency messages here that still need
somebody to act, and urgency separately governs sound and do-not-disturb.
"Restart recommended" is gone, and NotifyReboot with it. The running kernel
loses its module tree the moment pacman unpacks the new one, so the notice
fired while the run was still building AUR packages and pulling Flatpaks, where
it reads as an invitation to restart in the middle of an update. The state is
still recorded and cachy-auto-update status still reports it. The option went
rather than only its default, because an installed system keeps its own
configuration file and would have carried on notifying regardless.
The prompt offering to silence cachy-update's own update notification now
defaults to yes: once updates install themselves that notification is nothing
but noise, so the common answer should be the one you get by pressing Enter.
It also accepts "j" now. The prompt is translated, so a German user reads
"[J/n]" and types the German letter - which the old check for "y" alone
silently read as a refusal.
Arrow-key navigation took 435 ms per keypress, which reads as the whole console
reloading on every press - because it effectively was. The cost was forks: each
frame ran a command substitution per label for the value, the translation and
the rendered state, 54 subshells for eighteen rows.
The frame is now assembled in memory and written once. Translations, the split
specs and the terminfo clear string are resolved before the loop; values are
re-read only after something actually changes, not on cursor movement. Helpers
on that path assign to a variable instead of printing, since printing is what
forced the substitution.
Measured on the same machine: 435 ms -> 7.5 ms per keypress.
All eighteen options are now reachable from the menu as a cursor list: arrows
select, Space or Right cycles a value, q goes back, changes are written
immediately. A numbered menu would have run out of digits.
Three real bugs surfaced while building it, each found by testing against an
actual pty rather than a pipe:
- Labels went through printf as format strings, so the percent sign in
"Minimum battery level (%)" was an invalid conversion. cau_msg_in now only
treats a message as a format string when arguments were actually passed -
otherwise any literal % a translator writes is a trap.
- Mixing bash's line-mode read into a single-key interface left the following
read -sn1 receiving nothing at all, reproducibly, so the screen froze after
editing the package list. Replaced with a small line editor built on the same
single-character reader.
- Backspace was being swallowed: in canonical mode DEL is the ERASE character
and the line discipline consumes it, and bash returns to canonical mode
between each read -sn1. The interface now holds non-canonical mode for its
whole lifetime and hands the terminal back only around actions that print or
prompt, with a trap restoring it on Ctrl-C.
Translations and config reads are memoized; the screen redraws every label on
every keypress and a fork per lookup was the reason the redraw was slow enough
to matter.
Asked what happens if someone shuts the laptop down mid-update, the honest
answer turned out to be poor. The inhibitor does stop them - logind refuses and
falls back to org.freedesktop.login1.power-off-ignore-inhibit, which is
auth_admin_keep - so the desktop answers with an administrator password prompt
reading "Power off the system while an application is inhibiting this". systemd
ships that string untranslated, it never mentions updates, and no KDE catalog
contains any shutdown-blocked text at all. Only systemctl names the reason.
So a notification now goes out before the transaction, asking for the machine to
be left on. Notifications gained a tag: the result replaces the start message in
place rather than stacking a second, contradictory bubble beside it.
Tagged notifications also carry a queue flag. "An update is starting" is only
meaningful while somebody is looking, so unlike the result it is not spooled for
delivery at next login.
CleanCache was off, so nothing ever pruned /var/cache/pacman/pkg - 23 GB on the
machine this was found on, with three 3.2 GB copies of one package. Trimming
only drops older versions of installed packages and cached versions of
uninstalled ones, so the cost is downgrade depth, not working software.
flatpak uninstall --unused moves from CleanCache to RemoveOrphans, where it
belongs: unused runtimes are the Flatpak equivalent of orphaned packages, and
removing installed software should not hide behind a flag named for cache
trimming. RemoveOrphans stays off - 'orphaned' only means nothing depends on
it, which is also true of something installed deliberately.
The log now reports what a trim reclaimed, since a real paccache run says
almost nothing and there was otherwise no way to tell it was working.
A root systemd service applies pacman, AUR, Flatpak and AppImage updates on
its own, gated on battery state, gaming activity and whether anybody else is
using the package system. The CLI is deliberately two switches plus status.
No user password is stored anywhere: pacman runs as root directly, and the AUR
step - which makepkg forbids running as root - drops to a locked system account
that sudoers permits to call pacman without a password.