Skip to content

Dockerfile update plasma desktop - #394

Open
PierreBeucher wants to merge 41 commits into
masterfrom
dockerfile-update-plasma-desktop
Open

PierreBeucher wants to merge 41 commits into
masterfrom
dockerfile-update-plasma-desktop

Conversation

@PierreBeucher

Copy link
Copy Markdown
Owner

No description provided.

- plasma-desktop + plasma-workspace + breeze theming per Selkies egl-desktop
  recipe, stock kwin_x11 first (patch later only if it misbehaves on
  Xorg-dummy); noble ships Plasma 5.27 so session comes from
  plasma-workspace (startplasma-x11), no plasma-session-x11 package
- start-desktop.sh: startplasma-x11, drop xfce4 wiring
- drop xfce4-default config overlay + setup-xfce4-* scripts, xsettings
  theme application (Plasma applies Breeze itself)
- role compose: persist plasma workspace config instead of xfce4
- dev inventory: drop xfce4-default debug mount
- cloudypad-*.desktop launcher files in /usr/share/applications mirroring
  the former XFCE dock: Steam, Epic, GOG, Heroic, Lutris, Firefox, Konsole,
  Files (dolphin /cloudy/data), Keyboard/Mouse settings (systemsettings5),
  Help, Exit - plus systemsettings package
- cloudypad-wallpaper.svg installed to /usr/share/wallpapers
- setup-plasma-default-desktop.sh: waits for plasmashell then applies
  wallpaper + task manager pins via org.kde.PlasmaShell.evaluateScript
  (additive live change, mirrors old setup-xfce4-default-wallpaper pattern)
Cloudy Pad is a headless streamed desktop: Plasma screen locker would
lock the user out of their own instance after idle timeout (default ~5min).
Disable autolock and lock-on-resume via kscreenlockerrc in the image.
Suspend is not possible in-container (no logind) so no powerdevil config
needed.
…ttings"

This reverts commit 6a18b38dde95f6e346ff8cf5748f65566fa4b69d.
- cloudypad-*.desktop launchers (Steam, Epic, GOG, Heroic, Lutris, Files,
  Keyboard/Mouse settings, Help, Exit) shipped in overlay conf/cloudypad/
  applications/, discovered via XDG_DATA_DIRS (Dockerfile ENV + compose env)
- conf/plasma-default/plasma-org.kde.plasma.desktop-appletsrc: default
  Plasma config with the launchers pinned in the task manager
  [Configuration][General] group - NOT via the plasmashell scripting API
  which writes keys to the wrong [Configuration] level (renders one blank
  broken icon, see script header note)
- setup-plasma-default-desktop.sh: seed default appletsrc from overlay when
  missing (user customizations preserved on restart, XFCE
  setup-xfce4-default-config pattern)
- start-desktop.sh: seed config + rebuild KSycoca services cache BEFORE
  starting the session (plasmashell caches service lookups at startup;
  stale cache also renders pins blank)
- mount conf/plasma-default/ into the container on dev VM
  (sunshine_extra_volumes) so Plasma config changes in the overlay land
  with a simple container restart, no image rebuild
- CLOUDYPAD_PLASMA_DEFAULT_FORCE env (sunshine_plasma_default_force var,
  default false): re-seed the default appletsrc over the persistent config
  at each container start in dev; prod keeps seed-once semantics so user
  customizations survive restarts
- dev inventory enables the force flag with rationale
- base image ubuntu:26.04 (pinned SHA), Sunshine ubuntu26.04 asset
- Plasma 6.6.6: plasma-session-x11 + kwin-x11 packages (Plasma 6 naming),
  start-desktop.sh kbuildsycoca version-agnostic (6 then 5), launchers
  Exec systemsettings (Plasma 6 binary name)
- Steam: --allow-downgrades on steam-libs (resolute ships newer i386 libs
  than Steam pins; Steam runtime containers bundle their own libs)
- validated on dev VM: Plasma 5-format appletsrc seeds fine on Plasma 6,
  launchers render, stock kwin_x11 needs no patches, proton_stable mapping
…ance restarts

XDG_CONFIG_HOME pointed to the ephemeral Cloudy Pad conf dir: Plasma user
settings (panel layout, mouse, keyboard layout) were lost on container
recreation and instance stop/start as the container rw layer lives on the
root disk which reverts to the base image snapshot.

- point XDG_CONFIG_HOME to the persisted home ($HOME/.config, bind-mounted
  to the data disk): session/user config persists, defaults stay ephemeral
- symlink $HOME/.config/sunshine to CLOUDYPAD_CONF_DIR/sunshine so Sunshine
  appdata (apps.json, assets, covers) keeps resolving to image-managed conf
- seed Plasma defaults (appletsrc, kscreenlockerrc) from CLOUDYPAD_CONF_DIR
  into persisted config, only when missing (user customizations preserved)
- move Heroic config mount to the new $HOME/.config/heroic path (same host
  source, existing state carries over), drop dead plasma-workspace mount
- resolve sudo lecture files from conf dir (cloudy-managed, not user state)
…isorctl socket

- capture backend is now templated from CLOUDYPAD_SUNSHINE_CAPTURE compose env:
  nvfbc on datacenter (Tesla) GPUs - atomic GPU capture avoids stream tearing
  on fullscreen unredirected games and skips the CPU framebuffer copy - and
  x11 everywhere else (NvFBC is driver-blocked on GeForce). start-sunshine.sh
  defaults to x11 so a stale compose without the var never renders an empty
  capture setting
- disable Baloo file indexer via seeded baloofilerc: it has no use on a
  headless streamed desktop and can cause IO/CPU stutters walking large game
  libraries on the data disk
- restore supervisord control socket (unix_http_server + supervisorctl +
  rpcinterface) dropped in the Plasma rework, so supervisorctl works
  in-container again for debugging
…airer tests

- remove dev-sunshine-pin.sh + dev:pin task (duplicates cloudypad pair CLI),
  fix stale supervisorctl and dev:pin references in reset script
- remove nvidia-drm-hold service/tasks (stale labwc/Wayland rationale, X11
  stack does not need it)
- dedupe sunshine_csrf_allowed_origins in dev-vagrant inventory
- remove Wayland-justified tmpfs /cloudy/runtime from compose
- rename xfce4-dbus-session-bus-address to dbus-session-bus-address
- cleanup contribution guide: xfce mount refs, .cursor/rules refs
- trim unusable systemtray applets from default Plasma appletsrc
- restore autostop timeout default selection (900) in prompter
- add Sunshine pairer unit tests (pairing_id flow)
… 60px panel)

- ship default look in overlay conf/plasma-default/: BreezeDark theme
  (kdedefaults/kdeglobals, package, ksplashrc) and 60px panel
  (plasmashellrc [PlasmaViews][Panel 2][Defaults])
- seed defaults on NEW instances only: freshness gate detects a config dir
  already used by a Plasma session (session-written files) and keeps
  whatever existing instances have; protective seeds (kscreenlockerrc,
  baloofilerc) still apply when missing on any instance
- apply default look AFTER session start (apply-plasma-default-lookandfeel.sh):
  Plasma first-session startup overrides pre-seeded theme files with the
  stock light theme, so the look is applied via plasma-apply-lookandfeel
  once plasmashell is up, gated by an ephemeral flag file
- move kscreenlockerrc/baloofilerc under conf/plasma-default/ (all seeds
  in one place)
- dev inventory: force re-seed feedback loop applies to all look & feel
  defaults
- pair CLI command: --pin sends a given PIN (non-interactive), --auto
  generates a PIN, prints the moonlight pair command and sends it
- pairAuto() on pairer/manager/runner: auto branch extracted from the
  interactive flow, no prompts
- Sunshine pairer: persistent SSH connection across polls with 1s interval
  (was: reconnect per attempt, 2s interval) - Moonlight only waits a few
  seconds for the PIN confirmation, confirmation must land fast
- default retry budget raised to ~2 min (120 x 1s) to cover Moonlight's
  handshake + confirmation window
- hack/dev-pair.sh + task dev:pair: fully non-interactive pairing,
  removes stale same-hostname Moonlight entries (destroyed/recreated
  instances keep old server identity, blocking pairing), 10s pairer
  confirmation health bar with fail-fast diagnostics, end-to-end
  verification via moonlight list
- unit tests for pairing_id flow, multi-pending skip, retry and pairAuto
…g pairing

supervisord tracked the start script shell instead of Sunshine itself, so
restarts signaled only the shell: Sunshine survived as an orphan keeping
47984/48010 bound while the new process could only serve the web UI
(Fatal: Couldn't bind RTSP server to port 48010) - Moonlight pairing then
failed with 'PC offline' loops and no pending pairing session on the API.

- start-sunshine.sh: exec sunshine so the supervised process IS sunshine
  (supervisorctl tracks its real PID)
- supervisord.conf: stopasgroup/killasgroup on all long-running programs,
  restarts stop the whole process group (also covers Sunshine's internal
  privilege-drop fork)

Verified on a Scaleway instance: supervisorctl restart sunshine leaves
exactly one process with all ports bound, pairing unaffected.
Comment thread Taskfile.yml Resolved
Comment thread ansible/inventories/dev-vagrant.yml Resolved
Comment thread src/core/moonlight/pairer/sunshine.ts Resolved
Comment on lines +1 to +55
#!/usr/bin/env bash

#
# Apply the Cloudy Pad default look & feel (Breeze Dark) after the Plasma
# session is up, on fresh instances only.
#
# Why after session start: on a fresh user config, Plasma session startup
# applies the default look & feel (Breeze, light) and overwrites the theme
# files seeded before the session (kdedefaults/*, kdeglobals) - the pre-session
# seed cannot win. Applying via plasma-apply-lookandfeel AFTER the session is
# up is the same code path as System Settings and the result persists across
# container restarts and instance stop/start.
#
# Gated by a flag file written by setup-plasma-default-desktop.sh when the user
# config is fresh (new instance, or CLOUDYPAD_PLASMA_DEFAULT_FORCE in dev).
# The flag lives in XDG_RUNTIME_DIR: wiped on container restart, so this runs
# at most once per fresh instance boot.
#

DEFAULT_LOOKANDFEEL_PACKAGE="org.kde.breezedark.desktop"

FLAG=$XDG_RUNTIME_DIR/cloudypad-apply-plasma-default-lookandfeel
if [ ! -f "$FLAG" ]; then
exit 0
fi

echo "Fresh Plasma config detected: waiting for session to apply default look & feel..."

# Wait for plasmashell (up to 120s)
PLASMASHELL_UP=false
for i in $(seq 1 60); do
if pgrep -x plasmashell > /dev/null 2>&1; then
PLASMASHELL_UP=true
break
fi
sleep 2
done

if [ "$PLASMASHELL_UP" != "true" ]; then
echo "Warning: plasmashell did not start within timeout, cannot apply default look & feel" >&2
exit 1
fi

# Let the session settle: the default (light) look & feel is applied during
# session startup, give it time to complete before overriding
sleep 10

source export-dbus-address.sh

if plasma-apply-lookandfeel --apply "$DEFAULT_LOOKANDFEEL_PACKAGE"; then
echo "Default look & feel applied: $DEFAULT_LOOKANDFEEL_PACKAGE"
rm -f "$FLAG"
else
echo "Warning: failed to apply default look & feel $DEFAULT_LOOKANDFEEL_PACKAGE" >&2
fi

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this still needed since we have default config in Overlay? I don't care anymore about "forcing default" with env var. I just need the overlay data in the base image to be used as default, and runtime overrides to be kept between restart.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 Answer: still needed, the overlay alone can't cover it. The sequence problem: on a fresh user config, Plasma session startup applies its own default look & feel (Breeze, light) AFTER the session starts and overwrites the theme files seeded before the session (kdedefaults/*, kdeglobals) — the pre-session seed cannot win. So the flow is: (1) overlay seed pre-session via setup-plasma-default-desktop.sh, (2) re-apply the Cloudy Pad default look via plasma-apply-lookandfeel once the session is up — the same code path System Settings uses, so the result persists across restarts.

Runtime overrides are kept between restarts as you asked: the applier is gated by a flag file in XDG_RUNTIME_DIR (wiped on container restart), so it runs at most once per fresh instance — on existing instances the flag is absent and the script exits immediately. No env var involved anymore since the force-reseed removal.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 Update (5faf068): right — and the analysis showed the L&F overlay files were worse than redundant, they were provably ineffective: on a fresh config, Plasma session startup applies its own look & feel after the seed (that's the very behavior that forced the applier into existence). Removing them changes nothing visible.

Removed from the overlay: kdedefaults/kdeglobals, kdedefaults/ksplashrc, kdedefaults/package (all L&F-related). The overlay now holds only what the applier structurally cannot do:

  • plasma-org.kde.plasma.desktop-appletsrc: panel layout + task manager launcher pins (scripting API proven broken for pins)
  • plasmashellrc: 60px panel thickness
  • kscreenlockerrc, baloofilerc: protective seeds (never auto-lock, disable indexer)

Clean separation now: overlay = shell layout + protective seeds, applier = look & feel. Failure mode unchanged (applier failure → stock light theme, same as today since the seed was overwritten anyway). One item to eyeball in dev VM: kdedefaults/package removal — I'm confident it's inert (BreezeDark ships no shell layout template and our full appletsrc seed preempts template use), but worth a once-over on first boot.

Comment thread containers/sunshine/Dockerfile Resolved
Comment thread containers/sunshine/Dockerfile Resolved
Comment thread containers/sunshine/Dockerfile Resolved
Comment thread hack/dev-pair.sh Resolved
Comment thread hack/dev-pair.sh Resolved
Comment thread hack/dev-pair.sh Resolved
Comment thread hack/dev-reset-sunshine-state.sh Resolved
PierreBeucher added a commit that referenced this pull request Oct 31, 2026
- Remove CLOUDYPAD_PLASMA_DEFAULT_FORCE / sunshine_plasma_default_force:
  force-reseed was only a dev helper; keep seed-once semantics and test
  default overlay config on fresh containers instead
- Taskfile dev-docker-sunshine-to-vm: extract multi-line cmds into
  hack/dev-docker-sunshine-to-vm.sh
- start-sunshine.sh: render csrf_allowed_origins via the sunshine.conf
  template (envsubst) instead of appending to the rendered file; drop the
  trailing-newline workaround now that nothing is appended
- start-sunshine.sh: fail at start when template variables are unset
  (envsubst renders unset vars as empty strings, silently producing an
  invalid config) instead of defaulting capture to x11 in-script: the
  x11/nvfbc default now lives in compose templates only
- sunshine.conf.template: restore original state-files comment
@PierreBeucher

Copy link
Copy Markdown
Owner Author

🤖 Addressed the review comments in commit 5779d5c (single commit for review):

Code changes

  • Removed CLOUDYPAD_PLASMA_DEFAULT_FORCE / sunshine_plasma_default_force entirely: compose template env var, Ansible default, dev inventory value, and all usages in setup-plasma-default-desktop.sh / apply-plasma-default-lookandfeel.sh. Default overlay config changes are now tested on a fresh container only (updated dev-vagrant.yml comment accordingly).
  • Extracted task dev-docker-sunshine-to-vm multi-line cmds into hack/dev-docker-sunshine-to-vm.sh (disk-full guards before scp/deploy preserved).
  • start-sunshine.sh: csrf_allowed_origins is now rendered via the sunshine.conf.template (single envsubst pass) instead of being appended to the rendered file; the trailing-newline workaround is gone since nothing is appended anymore.
  • start-sunshine.sh: new template-validity check — before envsubst, every $VAR referenced by the template must be SET in the environment or the start fails listing the missing ones. The script-level CLOUDYPAD_SUNSHINE_CAPTURE:-x11 fallback was removed; the x11/nvfbc default lives in the Compose templates only.
  • sunshine.conf.template: restored the original state-files comment.

Comments answered without code (details in each thread)

  • XDG_DATA_DIRS in compose: explained; it's redundant with the Dockerfile value — happy to drop it and improve the Dockerfile comment if you agree.
  • CLOUDYPAD_SUNSHINE_CAPTURE: "x11" (compose) and the docker-compose.nvidia.yml nvfbc override: explained why both are needed (and now required by the template check).
  • setup-dirs.sh symlink, setup-sudo.sh CLOUDYPAD_CONF_DIR usage, start-desktop.sh kbuildsycoca rebuild: explained.
  • sunshine.ts multiple-pending-pairings handling: listed stronger mechanisms (match by client address / reset Sunshine state before pairing / purge stale sessions) for discussion.
  • typed-select-helpers.ts cast: explained (inquirer generic signature not structurally satisfiable without it).

⚠️ Also spotted while re-reading setup-sudo.sh: latent bug — SUDO_LECTURE_DIR is used (line 48) but never defined. Flagged in the thread; awaiting your call on whether to fix it in this PR.

- Remove CLOUDYPAD_PLASMA_DEFAULT_FORCE / sunshine_plasma_default_force:
  force-reseed was only a dev helper; keep seed-once semantics and test
  default overlay config on fresh containers instead
- Taskfile dev-docker-sunshine-to-vm: extract multi-line cmds into
  hack/dev-docker-sunshine-to-vm.sh
- start-sunshine.sh: render csrf_allowed_origins via the sunshine.conf
  template (envsubst) instead of appending to the rendered file; drop the
  trailing-newline workaround now that nothing is appended
- start-sunshine.sh: explicit template variables check before envsubst,
  failing hard when a variable is UNSET (empty-but-set allowed); keep
  x11 capture default in-script as a safety net for stale compose files
- docker-compose.nvidia.yml: move capture override into the datacenter
  GPU block, drop redundant x11 rewrite for GeForce
- dev-vagrant.yml: drop duplicate capture=x11 additional config (compose
  x11 default suffices), update Plasma debug mounts comment
- sunshine.conf.template: restore original state-files comment
@PierreBeucher
PierreBeucher force-pushed the dockerfile-update-plasma-desktop branch from 63891ad to 8b83b62 Compare October 31, 2026 10:52
- Dockerfile: drop 2025.x asset naming note, trim Selkies NOTE block
- dev-pair.sh: never touch Moonlight config (detect stale host entry,
  ask user to remove), log pairer confirmation, stop waiting on Moonlight
  (grace window then kill, moonlight list stays the verdict)
- Sunshine state reset: rewrite as ansible/dev-reset-sunshine-state.yml
  reusing the dev-vagrant inventory, wrapped as task dev:reset-sunshine-state
- Pairer: reset Sunshine pairing state before pairing (stop, wipe state
  files, restart) then purge stale pending sessions via DELETE /api/pin,
  so any pending pairing seen during pairing is ours
- Pairer unit tests updated for the new deterministic pairing flow
Sunshine appdata (XDG_CONFIG_HOME/sunshine) was a symlink to the
ephemeral image-managed conf dir, so user state was lost on container
recreation:
- apps.json (user-added apps via Web UI)
- covers/ (user-uploaded game covers, hardcoded to appdata/covers)

Instead, start-sunshine.sh now materializes a real appdata dir in the
persisted user home ($XDG_CONFIG_HOME/sunshine):
- seed apps.json from the image when missing (new instances, or user
  deleted it): Cloudy Pad app list, never overwrites user edits
- refresh cover images from the image on every start (static, tiny:
  image updates land without recreation)
- symlink covers/ to the persisted data dir (dir is hardcoded by
  Sunshine, symlink is the only way to persist it)
- render sunshine.conf into the appdata dir and pass it explicitly

setup-dirs.sh drops the symlink line: back to purely generic XDG
hygiene.
Resetting Sunshine state (supervisorctl stop, wipe state files, restart)
would restart the Sunshine server in the middle of the pairing flow and
clobber an in-flight Moonlight pairing handshake - rollback of option 2.

Keep option 3: purge stale pending pairing sessions via DELETE /api/pin
before the pairing flow, so any pending session seen during the pairing
window is ours (no restart involved).
Desktop launchers (cloudypad-*.desktop) and icons lived under the
Cloudy Pad conf dir (conf/cloudypad) exposed via a custom
XDG_DATA_DIRS entry - both in the Dockerfile and the compose template
(setting the identical value, redundant).

Config vs data separation: launchers and icons are XDG *data* (read-only
resources discovered by Plasma), not config. Move them to the standard
system path /usr/local/share/cloudypad (overlay/usr/local/share), which
is part of the default XDG_DATA_DIRS chain: no custom env entry needed
anywhere.

- overlay/usr/local/share/cloudypad/{applications,icons}: launchers and
  icons moved from conf/cloudypad
- conf/cloudypad keeps only sudo/ (password-secured config, not XDG data)
- epic/gog .desktop icon paths updated to /usr/local/share/cloudypad/icons
- Dockerfile: drop custom XDG_DATA_DIRS entry
- compose template: drop the redundant XDG_DATA_DIRS entry
- dev-vagrant.yml: debug bind mount follows the new path
The kdedefaults/ theme seeds (kdeglobals, ksplashrc, package) were
provably ineffective: on a fresh user config, Plasma session startup
applies its own default look & feel (Breeze, light) AFTER the pre-session
seed and overwrites the theme files. The post-session applier
(apply-plasma-default-lookandfeel.sh, plasma-apply-lookandfeel) is the
only mechanism that wins - the overlay theme files just duplicated it.

Overlay now holds only what the applier cannot do:
- plasma-org.kde.plasma.desktop-appletsrc: panel layout + task manager
  launcher pins (scripting API proven broken for pins)
- plasmashellrc: 60px panel thickness
- kscreenlockerrc, baloofilerc: protective seeds (never auto-lock,
  disable file indexer)
Desktop launchers (cloudypad-*.desktop) now ship in the image at
/usr/local/share/applications and icons at /usr/local/share/pixmaps:
both are standard XDG_DATA_DIRS system paths scanned by Plasma by
default, so launchers and icons resolve without any custom env var.
(/usr/local/share/cloudypad was not on the XDG chain, which broke
icon/launcher discovery.)

- epic/gog icons referenced via /usr/local/share/pixmaps absolute paths
- dev VM bind mounts follow the new paths
- Dockerfile XDG_DATA_DIRS stays on the default chain (no conf-dir hack)
- cloudypad-mouse launcher: input-mouse icon instead of input-tablet
- cloudypad-help launcher: help-about icon instead of help-contents
- Dockerfile: set SHELL=/bin/bash globally (supervisord children, session
  processes and docker exec shells inherit it): Konsole warned 'could not
  find ""' on every open, falling back to /bin/bash, as no process had
  $SHELL set
- setup-sudo.sh: fix latent undefined SUDO_LECTURE_DIR variable in mkdir
  (use dirname of lecture file); add set -euo pipefail so this class of
  bug cannot pass silently
Game launchers (Steam, Heroic, Epic, GOG, Lutris) set StartupNotify=false,
inherited from upstream steam.desktop, so Plasma showed no loading
indicator on click (KIO marks non-notifying apps silent, see
kio kprocessrunner.cpp checkStartupNotify). Set StartupNotify=true with
StartupWMClass per launcher: spinner clears when a window matching the
class maps, no dependency on apps implementing the startup notification
protocol (Heroic for Epic/GOG which run inside it).

Steam startup is slow and sometimes shows no window for tens of seconds
(steamwebhelper, see ValveSoftware/steam-for-linux#10775), which feels
like a crash. Desktop launcher now opens a minimal xterm log viewer:
- steam-start.sh always detaches Steam with setsid (terminal close no
  longer kills Steam, SIGHUP goes to the console session only) and logs
  to $CLOUDYPAD_LOG_DIR/steam-user.log while tail --pid follows it, so
  the console closes automatically when Steam exits and logs persist
- xterm over konsole: konsole toolbars (New Tab, Split View, Copy...)
  cannot be hidden on Konsole 25.12 (no --hide-toolbars) while xterm is
  bare by design
Simplify sudo setup: cloudy user gets passwordless sudo, no lecture file
and no sudo password. Container changes are not persistent by nature,
which is already stated in documentation and on-site help launcher.

Rationale: sudo on Ubuntu 24.10+/26.04 is sudo-rs which has no lecture
feature (trifectatechfoundation/sudo-rs#400) - unknown Defaults abort
every sudo call, and PAM-based echoes are suppressed by sudo-rs' silent
mode for direct invocations. Generating a sudo password and warning file
cannot work reliably on all supported bases; passwordless access removes
the whole class of problem.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant