# 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
