199 lines
10 KiB
Plaintext
199 lines
10 KiB
Plaintext
# Sway kiosk session for the thin client.
|
|
# Installed to /home/<KIOSK_USERNAME>/.config/sway/config by build-thin-client-iso.sh.
|
|
#
|
|
# Every `exec` below inherits the environment set by /usr/local/bin/kiosk-session,
|
|
# which sources /etc/thinclient-agent/config.env — that is how $DIGEST_WEB_URL gets
|
|
# here without this file being templated.
|
|
|
|
set $mod Mod4
|
|
set $ws_web 1:web
|
|
set $ws_digest 2:digest
|
|
set $ws_media 3:media
|
|
set $ws_admin 4:admin
|
|
set $ws_capture 5:capture
|
|
|
|
# Workspace names are a contract with thinclient_agent/digest_canvas.py,
|
|
# thinclient_agent/admin_canvas.py, thinclient_agent/capture_control.py, and
|
|
# thinclient_agent/sway_control.py — changing one side means changing the other.
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Output / input
|
|
# ---------------------------------------------------------------------------
|
|
output * bg #101014 solid_color
|
|
|
|
input type:keyboard {
|
|
xkb_layout @KEYBOARD_LAYOUT@
|
|
}
|
|
|
|
input type:touchpad {
|
|
tap enabled
|
|
natural_scroll enabled
|
|
}
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Look — no bars, no borders. Primary control is HA/MQTT and wayvnc; the local
|
|
# display is a media surface, not a desktop.
|
|
# ---------------------------------------------------------------------------
|
|
default_border none
|
|
default_floating_border none
|
|
hide_edge_borders both
|
|
gaps inner 0
|
|
gaps outer 0
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Remote control
|
|
# ---------------------------------------------------------------------------
|
|
# exec_always so a `swaymsg reload` re-establishes it. start-wayvnc refuses to run
|
|
# until /etc/wayvnc/wayvnc-password has been set on this machine.
|
|
exec_always /usr/local/bin/start-wayvnc
|
|
|
|
# Now-playing widget. Session-scoped (dies with sway, unlike thinclient-agent) — see
|
|
# fullscreen-watcher.sh's own comment for why it isn't part of the system service.
|
|
# Its own PID-file guard makes exec_always safe across `swaymsg reload`.
|
|
exec_always /usr/local/bin/fullscreen-watcher
|
|
|
|
# Camera gesture control. Session-scoped for the same blast-radius reason as the widget
|
|
# above, plus a privacy one: the camera must not be open while there is no session whose
|
|
# pointer it could move. It no-ops both when the image was built without it and when
|
|
# gesture-config.json still has the default "enabled": false — see the wrapper.
|
|
exec_always /usr/local/bin/gesture-control
|
|
|
|
# thinclient-agent is NOT started here. systemd owns it (thinclient-agent.service,
|
|
# enabled by 0700-thinclient-agent.hook.chroot) so that it is up and connected to
|
|
# Mosquitto whether or not a graphical session ever comes up, and so it survives a
|
|
# sway restart. Starting it from sway too would give two competing MQTT clients.
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Applications
|
|
# ---------------------------------------------------------------------------
|
|
# No `assign [app_id=...]` rules here on purpose: both digest-browser and web-browser
|
|
# (added for the minimal-chrome general-browsing app) launch the same firefox-esr
|
|
# app_id on different profiles, and `assign` cannot tell those two windows apart —
|
|
# it would fight thinclient_agent.sway_control.launch_app()'s switch-workspace-then-
|
|
# launch approach for whichever one it matched. Every launch path, including the one
|
|
# below, is therefore explicit about its own workspace instead.
|
|
# Steam Link runs under Xwayland (native Wayland black-screens on wlroots), so it
|
|
# appears as an X11 class, not a Wayland app_id.
|
|
assign [class="steamlink"] $ws_media
|
|
for_window [class="steamlink"] fullscreen enable
|
|
for_window [app_id="mpv"] fullscreen enable
|
|
|
|
# Guarded so a powered-off container host (or an unset DIGEST_WEB_URL) leaves an
|
|
# empty workspace instead of hanging the session — Phase 11.10. The explicit
|
|
# `swaymsg workspace` mirrors launch_app()'s own switch-then-launch order, since this
|
|
# runs before thinclient-agent's MQTT-driven launches ever fire.
|
|
exec sh -c '[ -n "$DIGEST_WEB_URL" ] && { swaymsg workspace $ws_digest; /usr/local/bin/digest-browser "$DIGEST_WEB_URL/full.html?detail_level=full"; }'
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Idle
|
|
# ---------------------------------------------------------------------------
|
|
# Never lock: this is an always-on shared media station, and a lock screen would make
|
|
# the room's display unusable to anyone who isn't holding a keyboard. Any wayvnc or
|
|
# local input resumes it.
|
|
#
|
|
# After 15 minutes, idle-gallery replaces the old unconditional "blank the panel"
|
|
# behaviour with a photo slideshow from the gallery SMB share (Phase 11 follow-up) —
|
|
# but it degrades all the way back to that exact blank-the-panel behaviour itself if
|
|
# no share is configured, unreachable, or empty, so a freshly-built or offline thin
|
|
# client behaves exactly as before. See configs/idle-gallery/idle-gallery.sh.
|
|
exec swayidle -w \
|
|
timeout 900 '/usr/local/bin/idle-gallery' \
|
|
resume 'swaymsg "output * power on"; /usr/local/bin/idle-gallery stop'
|
|
|
|
# Media playback keeps the screen alive.
|
|
for_window [class="steamlink"] inhibit_idle focus
|
|
for_window [app_id="mpv"] inhibit_idle focus
|
|
for_window [app_id="firefox-esr"] inhibit_idle fullscreen
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Local override keys — a fallback for standing in front of the machine, not the
|
|
# primary control surface.
|
|
# ---------------------------------------------------------------------------
|
|
|
|
# Maintenance shell. A deliberately obscure chord (not $mod+Return, which is the
|
|
# ordinary local-terminal key above it) so it is not something a visitor bumps into,
|
|
# floating so it overlays the kiosk content instead of tiling against it. It only opens
|
|
# a shell — it does not pause thinclient-agent or anything else, since standing at the
|
|
# machine already means treating it as a maintenance session; if that turns out to be
|
|
# too little, tightening it is a config change here, not a new subsystem.
|
|
bindsym $mod+Shift+Ctrl+m exec foot --title maintenance-shell
|
|
for_window [title="maintenance-shell"] floating enable, resize set width 800 height 500, move position center
|
|
|
|
bindsym $mod+Return exec foot
|
|
bindsym $mod+q kill
|
|
bindsym $mod+f fullscreen toggle
|
|
bindsym $mod+Shift+c reload
|
|
bindsym $mod+1 workspace $ws_web
|
|
bindsym $mod+2 workspace $ws_digest
|
|
bindsym $mod+3 workspace $ws_media
|
|
bindsym $mod+4 workspace $ws_admin
|
|
bindsym $mod+5 workspace $ws_capture
|
|
bindsym $mod+Left focus left
|
|
bindsym $mod+Right focus right
|
|
bindsym $mod+Up focus up
|
|
bindsym $mod+Down focus down
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Remote control — the full standardised media-key set.
|
|
#
|
|
# THE DEVICE THIS IS FOR: a wireless USB remote with TV controls on the front and a
|
|
# small keyboard on the back. To Linux that is just two HID keyboards on one dongle
|
|
# (a normal keyboard plus a "consumer control" device), so there is nothing to
|
|
# configure per-device — the front buttons arrive as the XF86* keysyms below and the
|
|
# back keyboard arrives as ordinary keys. Any remote in that class works; the bindings
|
|
# are what make it useful.
|
|
#
|
|
# WHAT IS DELIBERATELY *NOT* BOUND: plain Up/Down/Left/Right and Return. A remote's
|
|
# D-pad and OK button send exactly those, unmodified — and Chromium, mpv and every
|
|
# kiosk page need them. Stealing them at the compositor would break scrolling a web
|
|
# page with the remote, which is most of what the remote is for. Window focus stays on
|
|
# $mod+arrows above; the bare arrows belong to whatever is on screen.
|
|
#
|
|
# CHECKING WHAT YOUR REMOTE ACTUALLY SENDS: remotes vary more than their marketing
|
|
# does. `wev` (or `sudo libinput debug-events`) prints the keysym for each button —
|
|
# run it once from the maintenance shell and add any button that comes back unbound.
|
|
# A button that reports no keysym at all is one the kernel has no mapping for, which
|
|
# is a udev hwdb entry, not a Sway binding.
|
|
# ---------------------------------------------------------------------------
|
|
|
|
# Transport. `playerctl -p mpv,spotifyd` matches the two players this image runs;
|
|
# the order is the priority when both are alive.
|
|
bindsym XF86AudioPlay exec playerctl -p mpv,spotifyd play-pause
|
|
bindsym XF86AudioPause exec playerctl -p mpv,spotifyd pause
|
|
bindsym XF86AudioStop exec playerctl -p mpv,spotifyd stop
|
|
bindsym XF86AudioNext exec playerctl -p mpv,spotifyd next
|
|
bindsym XF86AudioPrev exec playerctl -p mpv,spotifyd previous
|
|
# Skip rather than scan: a remote's ⏪/⏩ are pressed repeatedly, not held, and
|
|
# playerctl has no scan mode. 30s forward / 10s back is the podcast convention and is
|
|
# the one people already have in their fingers.
|
|
bindsym XF86AudioForward exec playerctl -p mpv,spotifyd position 30+
|
|
bindsym XF86AudioRewind exec playerctl -p mpv,spotifyd position 10-
|
|
|
|
# Volume. Sinks, not players — the remote's volume rocker should move the room's
|
|
# volume regardless of what is making the noise.
|
|
bindsym XF86AudioRaiseVolume exec wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%+
|
|
bindsym XF86AudioLowerVolume exec wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%-
|
|
bindsym XF86AudioMute exec wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle
|
|
bindsym XF86AudioMicMute exec wpctl set-mute @DEFAULT_AUDIO_SOURCE@ toggle
|
|
|
|
# Navigation. A media station's "channels" are its workspaces, which is the closest
|
|
# honest analogy and means the channel rocker does something useful instead of nothing.
|
|
bindsym XF86ChannelUp workspace next
|
|
bindsym XF86ChannelDown workspace prev
|
|
bindsym XF86HomePage workspace $ws_media
|
|
bindsym XF86Back workspace back_and_forth
|
|
bindsym XF86Forward workspace back_and_forth
|
|
|
|
# Power. **The display, not the machine.** A remote's power button on a TV turns the
|
|
# picture off, and that is what people expect it to do here — while `poweroff` on a
|
|
# thin client that autologins is a button that takes the room's screen away until
|
|
# somebody walks over to it. See the display-toggle script for the wake side.
|
|
bindsym XF86PowerOff exec display-toggle
|
|
bindsym XF86Sleep exec display-toggle
|
|
bindsym XF86ScreenSaver exec display-toggle
|
|
|
|
# Deliberately no exit binding: `swaymsg exit` would drop to a black VT, and greetd
|
|
# would just autologin straight back in. Use SSH or the local terminal to administer.
|
|
|
|
workspace $ws_digest
|