# Sway kiosk session for the touch panel (docs/project-plan.md Phase 16). # # Installed to /home//.config/sway/config by build-touch-panel-iso.sh. # Every `exec` below inherits the environment set by /usr/local/bin/kiosk-session, # which sources /etc/touchpanel-agent/config.env — that is how $HA_URL gets here # without this file being templated. # # This is a DIFFERENT device from hosts/thin-client: that one is a couch-distance # media station whose primary control surface is HA/MQTT and wayvnc, deliberately with # "no bars" (see its config's own comment). This one is touched directly, so the whole # point is an always-visible on-screen dock a finger can hit — see the eww bar below. set $mod Mod4 set $ws_spotify 1:spotify set $ws_home 2:home set $ws_web 3:web # Workspace names are a contract with touchpanel_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@ } # Real touchscreen hardware, not the thin client's couch-distance camera-gesture # trick: libinput/Wayland surfaces (GTK, Chromium, Firefox) all speak wl_touch # natively, so taps/scrolls/pinches just work without a pointer-emulation layer — # when the kernel/udev actually classifies the device as a touchscreen. See # configs/udev/99-touchscreen-override.rules for the case where it doesn't. input type:touch { map_to_output "*" } input type:tablet_tool { map_to_output "*" } # --- Fallback: some cheap touch controllers report themselves as an absolute- # position HID mouse instead of a proper touch digitizer (ID_INPUT_MOUSE rather than # ID_INPUT_TOUCHSCREEN in udev, so libinput hands sway a `type:pointer` device, not a # `type:touch` one, and every tap becomes a mouse click at that position instead of a # touch event). Rather than assume this never happens, this block makes that # degraded mode usable on its own: # - accel_profile flat + pointer_accel 0: a touch-emulated pointer's coordinates # are already absolute; libinput's normal mouse acceleration curve is tuned for # relative motion and would make taps land somewhere other than where the finger # is, or make the cursor visibly drift/overshoot before settling. # - map_to_output: same coordinate-space fix `type:touch` already gets above, # needed here too since this device isn't going through that block. # See configs/udev/99-touchscreen-override.rules for the real fix (reclassifying the # device as a touchscreen instead of merely working around it being a pointer), and # the "Touch input: two tiers" section in README.md for the full picture, including # why single-touch click-drag (not true multi-touch/pinch) is the ceiling here. input type:pointer { accel_profile flat pointer_accel 0 map_to_output "*" } # Hides the cursor after ~1ms of no input instead of leaving a static arrow sitting # on screen. Irrelevant for real wl_touch input (Wayland never draws a cursor for # touch events at all) but is what keeps the type:pointer fallback above from looking # broken — a touch-emulated tap moves the cursor, clicks, and it's gone again before # anyone would register it as a cursor rather than a tap. seat seat0 hide_cursor 1 # --------------------------------------------------------------------------- # Look # --------------------------------------------------------------------------- default_border none default_floating_border none hide_edge_borders both gaps inner 0 gaps outer 0 # Reserve a strip at the bottom for the touch dock (bar/app-switcher), so it is # never covered by, and never steals area from behind, whatever app is on screen. # :exclusive is the eww window's own setting (configs/eww/eww.yuck) — sway just # needs to know nothing else should tile into that space, which layer-shell # exclusivity already guarantees without a `bar {}` block here. # --------------------------------------------------------------------------- # Session-scoped helpers # --------------------------------------------------------------------------- # touchpanel-agent is NOT started here — see thin-client's identical note. systemd # owns it so it is reachable over MQTT whether or not a graphical session is up, and # survives a sway restart. Starting it from sway too would give two competing MQTT # clients. # The touch dock (eww bar) and the on-screen keyboard toggle both live for the # session, not the system, for the same reason the thin client's now-playing widget # does: they are compositor UI, not device control, and should die with sway rather # than need their own restart-independent lifecycle. exec_always eww daemon exec_always eww open touch-dock # --------------------------------------------------------------------------- # Applications # --------------------------------------------------------------------------- # No `assign` rules, same reasoning as the thin client: every launch path is explicit # about its own workspace via touchpanel_agent.sway_control.launch_app(), so a # separately-matched `assign` rule can't race it. for_window [app_id="chromium.*"] fullscreen enable for_window [app_id="spotify"] fullscreen enable # HA kiosk window and Spotify both auto-launch at session start — this is a fixed # 3-app panel, not an on-demand surface like the thin client's digest/admin # workspaces, so there is no "nothing to show yet" state to guard against beyond an # unset HA_URL. exec sh -c '[ -n "$HA_URL" ] && { swaymsg workspace $ws_home; /usr/local/bin/ha-kiosk; }' exec sh -c 'swaymsg workspace $ws_spotify; /usr/local/bin/spotify-launch' exec swaymsg workspace $ws_home # --------------------------------------------------------------------------- # Idle # --------------------------------------------------------------------------- # Blank after 10 minutes to save the panel; any touch (or local input) resumes it. # No lock screen, same reasoning as the thin client: a lock screen would make a wall # panel unusable to anyone in the room who isn't holding a passcode. exec swayidle -w \ timeout 600 'swaymsg "output * power off"' \ resume 'swaymsg "output * power on"' for_window [app_id="chromium.*"] inhibit_idle fullscreen for_window [app_id="spotify"] inhibit_idle fullscreen # --------------------------------------------------------------------------- # Local override keys — a fallback for standing in front of the machine (or a # connected keyboard), not the primary control surface, which is touch + HA/MQTT. # --------------------------------------------------------------------------- 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_spotify bindsym $mod+2 workspace $ws_home bindsym $mod+3 workspace $ws_web bindsym XF86AudioPlay exec playerctl -p spotify play-pause bindsym XF86AudioNext exec playerctl -p spotify next bindsym XF86AudioPrev exec playerctl -p spotify previous 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 # Deliberately no exit binding — see the thin client's identical note: `swaymsg exit` # would just drop to a black VT and greetd would autologin straight back in. workspace $ws_home