From 5d294a562054867e1772f0ab79fe96f9cf55428b Mon Sep 17 00:00:00 2001 From: Sebastian Date: Fri, 29 May 2026 14:58:23 +0200 Subject: [PATCH] first commint --- README.md | 505 +++++++++++++++++++++++++++++++++++++++ kiosk-manager.sh | 155 ++++++++++++ kiosk-schedule | 15 ++ kiosk.service | 26 ++ logo.png | Bin 0 -> 49362 bytes mnt-presentation.mount | 14 ++ presentation-manager.sh | 222 +++++++++++++++++ smb-credentials.template | 5 + sway-config | 65 +++++ waiting.html | 105 ++++++++ wol-enable.service | 17 ++ 11 files changed, 1129 insertions(+) create mode 100644 README.md create mode 100644 kiosk-manager.sh create mode 100644 kiosk-schedule create mode 100644 kiosk.service create mode 100644 logo.png create mode 100644 mnt-presentation.mount create mode 100644 presentation-manager.sh create mode 100644 smb-credentials.template create mode 100644 sway-config create mode 100644 waiting.html create mode 100644 wol-enable.service diff --git a/README.md b/README.md new file mode 100644 index 0000000..87c53ac --- /dev/null +++ b/README.md @@ -0,0 +1,505 @@ +# Kiosk Dwuekranowy — Chromium + Prezentacja PowerPoint — Debian 13 + +Kiosk na 1 monitor.w Chromium tylko strona oczekiwania na sieć, prawy ekran wyświetla w pętli prezentację PowerPoint z zamontowanego udziału SMB. Używa **X11** (xinit + openbox) i **LibreOffice Impress** w trybie pokazu (`--show`) — bez konwersji, z zachowanymi animacjami i przejściami. + +--- + +## Architektura + +``` +┌──────────────────────────────────────────────────────────────────┐ +│ kiosk.service │ +│ (xinit + openbox — tty7) │ +│ │ +│ Wirtualny desktop 3840×1080 (xrandr --right-of) │ +│ ┌──────────────────────────────┐ │ +│ │ LibreOffice Impress --show │ │ +│ │ HDMI-1 (1920,0–3840×1080) │ │ +│ │ terminal_live.pptx │ │ +│ └──────────────────────────────┘ │ +│ ▲ above (wmctrl) ▲ │ +│ kiosk-manager.sh presentation-manager.sh │ +└──────────────────────────────────────────────────────────────────┘ + ▲ + /mnt/presentation (CIFS/SMB) + montowany przez skrypt (fstab user) + ▲ + //192.168.40.201/DatyFirmowe/ALS +``` + +**Przepływ — lewy ekran (kiosk-manager.sh):** +1. Konfiguruje X11: xrandr dual-screen, wyłącza DPMS, uruchamia openbox +2. Zapisuje `registrymodifications.xcu` — wyłącza presenter console LO +3. Uruchamia watchdog sesji (co 0.4 s chowa okna LO z lewego ekranu) +4. Uruchamia `presentation-manager.sh` w tle +5. Otwiera `waiting.html` fullscreen + always-on-top +6. Czeka na: sieć (`ping diluals31`) **oraz** sygnał od `presentation-manager.sh` (plik `/tmp/kiosk-smb-ready`) +7. Po otrzymaniu obu sygnałów: `sleep 10` — daje LO czas na otwarcie i pozycjonowanie okna na prawym ekranie +8. Przełącza Chromium na docelowy URL + +> `sleep 10` jest kluczowe: zapewnia, że LibreOffice zdąży otworzyć i ustawić okno pokazu zanim Chromium wejdzie w tryb fullscreen+above na lewym ekranie. Bez tego opóźnienia konsola prezentacji była widoczna po reboot. + +**Przepływ — prawy ekran (presentation-manager.sh):** +1. Montuje udział SMB (`mount /mnt/presentation`) z retry co 5 s +2. Synchronizuje czcionki z `$SMB_MOUNT/fonts/` do `~/.local/share/fonts/smb/` +3. Tworzy plik `/tmp/kiosk-smb-ready` — sygnał dla `kiosk-manager.sh` +4. Czeka na plik `terminal_live.pptx` +5. Uruchamia persistentny watchdog konsoli (co 0.3 s) +6. Uruchamia `soffice --show terminal_live.pptx` +7. Przesuwa okno pokazu na prawy ekran i stosuje fullscreen +8. Co 5 s: sprawdza mtime pliku i restartuje po zmianie + +--- + +## Wymagania + +- Debian 13 netinst — instalacja minimalna bez środowiska graficznego +- Monitor `HDMI-1` +- Dostęp do udziału SMB/CIFS z plikiem prezentacji + +--- + +## Instalacja krok po kroku + +### 1. Utwórz użytkownika kiosk + +```bash +useradd -m -s /usr/sbin/nologin kiosk +usermod -aG tty,input,video,render,dialout kiosk +``` + +### 2. Zainstaluj pakiety + +```bash +apt update && apt install -y \ + xorg xinit openbox \ + chromium \ + libreoffice-impress \ + cifs-utils \ + xdotool wmctrl \ + x11-xserver-utils \ + iputils-ping \ + ethtool \ + wakeonlan +``` + +> Nie są już potrzebne: + +### 2a. Zainstaluj czcionki + +LibreOffice podstawia czcionkę zastępczą gdy oryginalna nie jest zainstalowana w systemie — może to zmienić układ tekstu na slajdach. + +#### Automatyczna instalacja przez udział SMB (zalecane) + +`presentation-manager.sh` automatycznie synchronizuje czcionki z folderu `fonts/` na udziale SMB do `/home/kiosk/.local/share/fonts/smb/`. Wystarczy wrzucić pliki `.ttf`/`.otf` na udział — zostaną zainstalowane przy najbliższym restarcie kiosku. + +**Na maszynie Windows** — skopiuj pliki czcionek do `\\192.168.40.201\DatyFirmowe\ALS\fonts\`: + +```powershell +# Calibri (C:\Windows\Fonts\ lub %LOCALAPPDATA%\Microsoft\Windows\Fonts\) +Copy-Item C:\Windows\Fonts\calibri*.ttf \\192.168.40.201\DatyFirmowe\ALS\fonts\ + +# Aptos — zazwyczaj w cache czcionek chmurowych Microsoft 365: +$src = "$env:LOCALAPPDATA\Microsoft\FontCache\4\CloudFonts\Aptos" +Copy-Item "$src\*" \\192.168.40.201\DatyFirmowe\ALS\fonts\ +``` + +> Nazwy plików Aptos w cache mogą być numeryczne — to normalne, fontconfig czyta metadane wewnętrzne, nie nazwę pliku. + +**Restart kiosku** — czcionki zostaną zainstalowane automatycznie: + +```bash +systemctl restart kiosk.service +# Sprawdź w logach: +journalctl -u kiosk.service -b | grep "czcionka\|Cache czcionek" +``` + +#### Carlito — open-source'owy zamiennik Calibri (opcjonalnie) + +Jeśli nie masz dostępu do oryginalnych plików Calibri, `Carlito` ma identyczne metryki (ten sam układ tekstu i łamania linii): + +```bash +apt install fonts-crosextra-carlito fonts-crosextra-caladea +``` + +`fonts-crosextra-caladea` to odpowiednik Cambrii. Nie wymaga wrzucania plików na SMB — instalowany systemowo. + +#### Inne czcionki Microsoft (Arial, Times New Roman, Verdana) + +```bash +apt install ttf-mscorefonts-installer +``` + +> Nie są już potrzebne: `sway`, `swayidle`, `wlopm`, `wlr-randr`, `seatd`, `mpv`, `poppler-utils`. + +### 3. Skonfiguruj montowanie udziału SMB + +#### 3a. Utwórz plik poświadczeń + +```bash +mkdir -p /etc/samba +cat > /etc/samba/kiosk-credentials << 'EOF' +username=NAZWA_UZYTKOWNIKA +password=HASLO +domain=WORKGROUP +EOF +chmod 600 /etc/samba/kiosk-credentials +chown root:kiosk /etc/samba/kiosk-credentials +``` + +#### 3b. Utwórz punkt montowania + +```bash +mkdir -p /mnt/presentation +``` + +#### 3c. Dodaj wpis w /etc/fstab (montowanie przez użytkownika kiosk) + +```bash +echo '//192.168.40.201/DatyFirmowe/ALS /mnt/presentation cifs credentials=/etc/samba/kiosk-credentials,uid=kiosk,gid=kiosk,vers=3.0,user,noauto,_netdev 0 0' >> /etc/fstab +``` + +Opcja `user` pozwala użytkownikowi `kiosk` wykonać `mount /mnt/presentation` bez uprawnień roota. Opcja `noauto` wyłącza automatyczne montowanie przy starcie — skrypt sam zarządza montem z retry. + +> **Uwaga:** Systemd unit `mnt-presentation.mount` jest **wyłączony** — skrypt `presentation-manager.sh` przejął odpowiedzialność za montowanie. + +```bash +systemctl disable mnt-presentation.mount +systemctl stop mnt-presentation.mount +``` + +### 4. Wykryj nazwy wyjść wideo + +```bash +for p in /sys/class/drm/card*-*; do + echo "$(basename $p | sed 's/card[0-9]*-//'): $(cat $p/status)" +done +``` + +Przykładowe wyjście: +``` +DP-1: connected +HDMI-1: connected +``` + +Ustaw właściwe nazwy w `kiosk-manager.sh`: +```bash +OUTPUT_LEFT="DP-1" # lewy ekran — primary +OUTPUT_RIGHT="HDMI-1" # prawy ekran +``` + +### 5. Skopiuj pliki konfiguracyjne + +```bash +cp kiosk-manager.sh /home/kiosk/kiosk-manager.sh +cp presentation-manager.sh /home/kiosk/presentation-manager.sh +cp waiting.html /home/kiosk/waiting.html +chmod +x /home/kiosk/kiosk-manager.sh /home/kiosk/presentation-manager.sh +chown -R kiosk:kiosk /home/kiosk/ + +cp kiosk.service /etc/systemd/system/kiosk.service +``` + +### 6. Rozwiązanie problemu z domenami .local + +Jeśli `diluals31` jest rozwiązywany przez DNS firmowy (nie mDNS): + +- Otwórz `/etc/nsswitch.conf` +- Zmień linię `hosts:` na: `hosts: files dns mdns4_minimal [NOTFOUND=return]` + +### 7. Włącz i uruchom serwis + +```bash +systemctl daemon-reload +systemctl enable kiosk.service +systemctl start kiosk.service +``` + +### 8. Skonfiguruj GRUB — natychmiastowy start + +```bash +# /etc/default/grub +GRUB_DEFAULT=0 +GRUB_TIMEOUT=0 +``` + +```bash +/usr/sbin/grub-mkconfig -o /boot/grub/grub.cfg +``` + +--- + +## Harmonogram — Automatyczne wyłączanie i Wake on LAN + +Kiosk wyłącza się automatycznie w **sobotę o 7:00** i jest budzony przez Magic Packet (Wake on LAN) co 5 minut przez całe okno aktywności (**niedziela 21:00 – sobota 7:00**). Pakiet WoL jest ignorowany gdy kiosk jest już włączony — cykliczne wysyłanie nie powoduje restartów. + +> **Utrata prądu:** WoL nie zadziała gdy karta sieciowa nie ma zasilania. Aby kiosk sam wstał po powrocie prądu, wejdź w ustawienia BIOS/UEFI i ustaw opcję **Restore on AC Power Loss** (lub **After Power Failure**) na **Power On**. W połączeniu z cyklicznym WoL zapewnia to pełne pokrycie: brak prądu → BIOS bootuje automatycznie; awaria software → WoL obudzi po max. 5 min. +> +> Jeśli prąd wróci w oknie wyłączenia (sobota 7:00 – niedziela 20:59), kiosk uruchomi się przez BIOS, ale `kiosk-schedule` wyłączy go ponownie w ciągu max. 5 minut. + +### Na kiosku + +#### 9a. Cron — automatyczne wyłączanie + +```bash +cp kiosk-schedule /etc/cron.d/kiosk-schedule +chmod 644 /etc/cron.d/kiosk-schedule +``` + +#### 9b. Wake on LAN — włączanie i utrwalanie + +```bash +cp wol-enable.service /etc/systemd/system/ +systemctl daemon-reload +systemctl enable --now wol-enable.service +``` + +Sprawdź, czy WoL jest aktywne: + +```bash +ip link show | grep -A1 "state UP" | grep ether # MAC adres kiosku +ethtool enp2s0 | grep -i wake # zamień enp2s0 na właściwy interfejs +``` + +> **Wymagane ręcznie: BIOS kiosku** — wejdź w ustawienia firmware i włącz opcję **Wake on LAN** lub **Power on by PCI-E/PCI**. Bez tego ustawienia Magic Packet jest ignorowany przez sprzęt. + +#### 9c. Pobierz MAC adres kiosku + +```bash +ip link show | grep -A1 "state UP" | grep ether | awk '{print $2}' +``` + +Zanotuj ten adres — potrzebny do konfiguracji serwera (krok poniżej). + +### Na serwerze (192.168.40.126) + +Zainstaluj narzędzie do wysyłania Magic Packet: + +```bash +apt install wakeonlan +``` + +Utwórz plik crona wysyłającego WoL co 5 minut przez całe okno aktywności kiosku (wstaw MAC kiosku): + +```bash +cat > /etc/cron.d/kiosk-wol << 'EOF' +SHELL=/bin/sh +PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin +# Budź kiosk WS-VK016 co 5 min w oknie aktywności: nd 21:00 – sb 7:00 +# Jeśli kiosk jest już włączony, pakiet WoL jest ignorowany (bezpieczne). +*/5 21-23 * * 0 root wakeonlan XX:XX:XX:XX:XX:XX +*/5 * * * 1-5 root wakeonlan XX:XX:XX:XX:XX:XX +*/5 0-6 * * 6 root wakeonlan XX:XX:XX:XX:XX:XX +EOF +chmod 644 /etc/cron.d/kiosk-wol +``` + +Zastąp `XX:XX:XX:XX:XX:XX` adresem MAC pobranym w kroku 9c. + +> WoL działa w obrębie jednej podsieci (broadcast). Kiosk i serwer są w sieci `192.168.40.x` — brak potrzeby konfiguracji routera. + +--- + +### `kiosk.service` → `/etc/systemd/system/kiosk.service` + +Jednostka systemd uruchamiająca sesję X11 jako użytkownik `kiosk`. + +| Opcja | Wartość | Opis | +|---|---|---| +| `ExecStart` | `xinit kiosk-manager.sh -- :0 vt7` | Uruchamia X11 na tty7 | +| `ExecStartPre` | `chvt 7` | Przełącza konsolę na tty7 | +| `PAMName` | `login` | Wymagane do bezrootowego X11 (xinit jako user) | +| `TTYPath` | `/dev/tty7` | Przypisuje serwis do tty7 | +| `SLIDE_DURATION` | `10` | Czas slajdu (s) — aktualnie nieużywany, LO używa timingów z PPTX | +| `Restart=always` | — | Automatyczny restart po awarii | + +> Serwis startuje **bez** `network-online.target` — celowo, aby `waiting.html` było widoczne od razu po uruchomieniu systemu. Montowanie SMB obsługuje skrypt z retry. + +--- + +### `kiosk-manager.sh` → `/home/kiosk/kiosk-manager.sh` + +Uruchamiany przez xinit. Konfiguruje X11 i obsługuje lewy ekran. + +**Co robi:** +1. `xrandr` — tryb rozszerzony: `DP-1` primary (0,0), `HDMI-1` po prawej (1920,0) +2. `xsetroot` + `xset` — czarne tło, wyłączony wygaszacz i DPMS +3. `openbox --sm-disable` — lekki WM wymagany przez `wmctrl fullscreen` +4. Zapisuje `registrymodifications.xcu` — wyłącza presenter console LO (`StartWithPresenterScreen=false`) +5. Uruchamia watchdog sesji w tle — co 0.4 s chowa okna soffice z lewego ekranu (x < 1920) +6. Uruchamia `presentation-manager.sh` w tle +7. Chromium otwiera `waiting.html` z `fullscreen` + `above` +8. Czeka na sieć (`ping diluals31`) **i** na plik `/tmp/kiosk-smb-ready` +9. `sleep 10` — LO ma czas na pełny start i pozycjonowanie okna przed Chromium +10. Kill waiting Chromium → exec Chromium z docelowym URL + +**Flagi Chromium (`CHROMIUM_OPTS`):** + +| Flaga | Opis | +|---|---| +| `--app=URL` | Tryb aplikacji — brak paska adresu, brak kart | +| `--window-position=0,0 --window-size=1920,1080` | Pozycja na lewym ekranie (X11: bez `--kiosk`, który rozciąga na cały wirtualny desktop) | +| `--no-first-run` | Pomija ekran powitalny | +| `--noerrdialogs` | Ukrywa okna dialogowe błędów | +| `--disable-gpu-suspend` | Zapobiega zawieszaniu GPU przy wygaszaniu | +| `--force-device-scale-factor=1` | Skalowanie 1:1 | +| `--disable-features=Translate,MediaRouter` | Wyłącza tłumaczenie i Chromecast | +| `--disable-sync` | Wyłącza synchronizację Google | +| `--disable-extensions` | Wyłącza rozszerzenia | +| `--password-store=basic` | Nie używa systemowego portfela haseł | +| `--disable-pinch` | Wyłącza pinch-to-zoom | + +> **Uwaga:** `--kiosk` na X11 rozciąga okno na cały wirtualny desktop 3840×1080 (oba ekrany). Używamy `--app` + `--window-position/size` + `wmctrl fullscreen`. + +**Zmiana hosta / URL kiosku:** +```bash +# kiosk-manager.sh +KIOSK_HOST="diluals31" +KIOSK_URL="http://diluals31/terminal/ift/5/DE/index" +``` + +--- + +### `presentation-manager.sh` → `/home/kiosk/presentation-manager.sh` + +Obsługuje prawy ekran — montowanie SMB i wyświetlanie prezentacji. + +**Montowanie SMB:** +```bash +until mountpoint -q "$SMB_MOUNT"; do + mount "$SMB_MOUNT" # korzysta z wpisu fstab z opcją 'user' + sleep 5 # retry co 5 s +done +``` +Przy zakończeniu skryptu (trap EXIT/INT/TERM): `umount /mnt/presentation`. + +**Ustawianie okien LibreOffice (`start_lo`):** + +Iteruje wszystkie widoczne okna `soffice` wg pozycji X: +- `x ≥ 1920` → okno pokazu slajdów → `wmctrl fullscreen` +- `x < 1920` → konsola prezentacji → przesunięcie na `x=3840` (poza desktop) + `wmctrl below` +- Fallback po 5 s: jeśli nadal brak okna na prawym ekranie → przeniesienie pierwszego znalezionego + +Po ustawieniu LO: Chromium podniesiony jako `above` (always-on-top na lewym ekranie). + +**Watchdog (co 5 s w głównej pętli):** +- Sprawdza wszystkie widoczne okna `soffice` — jeśli któreś wróciło na `x < 1920`, wysyła je z powrotem na `x=3840` +- Sprawdza `mtime` pliku PPTX — po zmianie restartuje pokaz +- Sprawdza czy `soffice` żyje — jeśli zakończył (koniec pokazu), restartuje + +**Konfiguracja:** +```bash +SMB_MOUNT="/mnt/presentation" +SMB_SOURCE="//192.168.40.201/DatyFirmowe/ALS" +PRESENTATION_FILE="$SMB_MOUNT/terminal_live.pptx" +``` + +--- + +### `kiosk.service` (wyłączone) `mnt-presentation.mount` + +Systemd unit do montowania SMB jest **wyłączony**. Skrypt `presentation-manager.sh` przejął montowanie przez `mount /mnt/presentation` (wpis fstab z opcją `user,noauto`). + +--- + +### `sway-config` + +Nieużywany — zachowany jako archiwum poprzedniej architektury (Wayland/sway). + +--- + +### `kiosk-schedule` → `/etc/cron.d/kiosk-schedule` + +Plik crona systemu wyłączający kiosk w sobotę o 7:00 (`shutdown -h now` jako root). + +--- + +### `wol-enable.service` → `/etc/systemd/system/wol-enable.service` + +Jednostka systemd uruchamiana przy każdym starcie systemu. Wywołuje `ethtool -s IFACE wol g` na wszystkich interfejsach sieciowych — utrzymuje Wake on LAN włączone po rebootach (niektóre sterowniki resetują ustawienie WoL przy wyłączaniu). + +--- + +### `waiting.html` → `/home/kiosk/waiting.html` + +Lokalna strona HTML wyświetlana przez Chromium podczas oczekiwania na sieć. Ciemne tło, animowany spinner. Widoczna od startu systemu do momentu, gdy `diluals31` odpowie na ping. + +--- + +## Zarządzanie i diagnostyka + +### Logi + +```bash +# Logi kiosku na żywo +journalctl -u kiosk.service -f + +# Ostatnie 100 linii +journalctl -u kiosk.service -b -n 100 + +# Tylko logi prezentacji +journalctl -u kiosk.service -b | grep presentation-manager + +# Logi montowania SMB +journalctl -u kiosk.service -b | grep "mount:" +``` + +### Restart / zatrzymanie + +```bash +systemctl restart kiosk.service +systemctl stop kiosk.service +``` + +### Ręczne montowanie / odmontowanie SMB (jako root) + +```bash +mount /mnt/presentation +umount /mnt/presentation +mountpoint /mnt/presentation && echo "zamontowany" +``` + +### Wykrycie nazw wyjść wideo (bez uruchomionego kiosku) + +```bash +for p in /sys/class/drm/card*-*; do + echo "$(basename $p | sed 's/card[0-9]*-//'): $(cat $p/status)" +done +``` + +### Wymuszenie restartu pokazu (zmiana pliku) + +```bash +touch /mnt/presentation/terminal_live.pptx +# skrypt wykryje zmianę mtime w ciągu 5 s i zrestartuje LO +``` + +### Czyszczenie cache Chromium + +```bash +systemctl stop kiosk.service +rm -rf /home/kiosk/.cache/chromium +systemctl start kiosk.service +``` + +--- + +## Rozwiązywanie problemów + +| Objaw | Przyczyna | Rozwiązanie | +|---|---|---| +| `waiting.html` nie pojawia się | Sieć dostępna natychmiast, ping loop kończy się w <2 s | Normalne zachowanie — `sleep 2` zapewnia min. 2 s widoczności | +| Prezentacja się nie wyświetla | SMB nie zamontowany | `journalctl -u kiosk.service -b \| grep mount` — sprawdź błędy | +| `mount: Permission denied` | Opcja `user` brak w fstab lub kiosk nie jest właścicielem | Sprawdź wpis fstab: `grep presentation /etc/fstab` | +| `mount: not found in /etc/fstab` | Brak wpisu fstab | Dodaj wpis zgodnie z sekcją 3c | +| Konsola LibreOffice widoczna na lewym ekranie po reboot | LibreOffice startuje przed Chromium (kiosk URL) i konsola zdąży się pokazać | Zwiększ `sleep 10` w `kiosk-manager.sh` (linia po pętli `kiosk-smb-ready`) | +| Chromium rozciągnięty na oba ekrany | Użyto `--kiosk` zamiast `--app` | Sprawdź `kiosk-manager.sh` — nie używamy `--kiosk` | +| Lewy ekran czarny, tylko prawy działa | Chromium nie znalazł okna w 30 s | `journalctl \| grep "kiosk-manager"` — sprawdź błędy Chromium | +| `openbox: command not found` | Brak pakietu | `apt install openbox` | +| `wmctrl: command not found` | Brak pakietu | `apt install wmctrl` | +| `xdotool: command not found` | Brak pakietu | `apt install xdotool` | +| Animacje nie działają | LO konwertuje do PNG/PDF (stare podejście) | Aktualna wersja używa `soffice --show` bezpośrednio — animacje powinny działać | +| Tekst źle wyświetlany (zła czcionka, zły układ) | Brak czcionki | Skopiuj pliki `.ttf`/`.otf` do `\\serwer\DatyFirmowe\ALS\fonts\` i zrestartuj kiosk — skrypt zainstaluje je automatycznie | +| Udział SMB z protokołem SMB 1.0 | `vers=3.0` nieobsługiwany | Zmień na `vers=2.1` lub `vers=1.0` w `/etc/fstab` | +| Czarny ekran po restarcie | Chromium trzyma zablokowany profil | `rm -rf /home/kiosk/.cache/chromium` | +| X11 nie startuje (`cannot open display`) | Brak pakietu xorg | `apt install xorg` | diff --git a/kiosk-manager.sh b/kiosk-manager.sh new file mode 100644 index 0000000..328663d --- /dev/null +++ b/kiosk-manager.sh @@ -0,0 +1,155 @@ +#!/bin/bash +# kiosk-manager.sh — konfiguracja X11 + lewy ekran: Chromium +# Plik docelowy: /home/kiosk/kiosk-manager.sh + +# Jawnie ustawiamy HOME — serwis może startować jako root (xinit bez User=kiosk), +# a LibreOffice szuka profilu w $HOME/.config/libreoffice/ (nie w /home/kiosk/). +# Wszystkie procesy potomne (soffice, chromium) dziedziczą tę wartość. +export HOME=/home/kiosk + +KIOSK_HOST="diluals31" +KIOSK_URL="http://diluals31/terminal/ift/5/DE/index" +WAITING_PAGE="file:///home/kiosk/waiting.html" + +OUTPUT_LEFT="DP-1" +OUTPUT_RIGHT="HDMI-1" +RES_W=1920 +RES_H=1080 + +# ─── Niewidoczny kursor ────────────────────────────────────────────────────── +# Tworzymy motyw XCursor (1x1 px, ARGB 0x00000000) i eksportujemy XCURSOR_THEME +# zanim uruchomią się Chromium i LibreOffice — dziedziczą środowisko procesu. +# Brak myszy w kiosku nie usuwa kursora X11; bez tego wskaźnik jest widoczny +# w środku między ekranami (domyślna pozycja X11 = centrum wirtualnego desktopa). +_BLANK_DIR="/home/kiosk/.icons/blank/cursors" +mkdir -p "$_BLANK_DIR" +# printf pisze binarnie bezpośrednio do pliku — nie przez zmienną (bash $() ucina null-byte'y) +for _n in left_ptr default pointer crosshair text watch wait move size_all fleur; do + printf '\x58\x63\x75\x72\x10\x00\x00\x00\x00\x00\x01\x00\x01\x00\x00\x00\x02\x00\xfd\xff\x01\x00\x00\x00\x1c\x00\x00\x00\x24\x00\x00\x00\x02\x00\xfd\xff\x01\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x32\x00\x00\x00\x00\x00\x00\x00' > "$_BLANK_DIR/$_n" +done +printf '[Icon Theme]\nName=blank\n' > /home/kiosk/.icons/blank/index.theme +export XCURSOR_THEME=blank +export XCURSOR_SIZE=1 + + + +# Dual-screen rozszerzony: lewy ekran primary, prawy po prawej +# --auto wykrywa tryb automatycznie (bezpieczniejsze niż --mode 1920x1080) +xrandr --output "$OUTPUT_LEFT" --auto --primary --pos 0x0 +xrandr --output "$OUTPUT_RIGHT" --auto --right-of "$OUTPUT_LEFT" +# Rozszerz wirtualny framebuffer 200 px poniżej ekranów — kursor trafi w ten obszar +# i nie będzie widoczny na żadnym fizycznym monitorze (bez potrzeby dodatkowych pakietów) +xrandr --fb "$((RES_W * 2))x$((RES_H + 200))" +echo "[kiosk-manager] Konfiguracja xrandr:" +xrandr | grep -E "connected|[0-9]+x[0-9]+\+" + +# Czarne tło na obu ekranach +xsetroot -solid black + +# Wyłącz wygaszacz ekranu i DPMS (kiosk działa non-stop) +xset s off +xset -dpms +xset s noblank + +# Przesuń kursor poniżej fizycznych ekranów — w obszar framebuffera bez outputu +# Kursor istnieje w X11, ale nie jest wyświetlany na żadnym monitorze +xdotool mousemove "$RES_W" "$((RES_H + 100))" + +# Uruchom lekki WM — wymagany do per-screen fullscreen przez wmctrl +openbox --sm-disable & +sleep 0.5 + +# Konfiguracja LibreOffice Impress: wyłącz presenter console, pokaz na ekranie 1 (HDMI-1) +# Nadpisujemy przy każdym starcie — profil kiosku nie wymaga ochrony +LO_PROFILE="$HOME/.config/libreoffice/4/user" +mkdir -p "$LO_PROFILE" +cat > "$LO_PROFILE/registrymodifications.xcu" << 'XCUEOF' + + + + + false + + + +XCUEOF + +# ─── Start aplikacji ───────────────────────────────────────────────────────── + +# Uruchom menedżera prezentacji w tle (prawy ekran) +/home/kiosk/presentation-manager.sh & + +# Watchdog sesji: ukrywa okna konsoli LibreOffice przez cały czas trwania sesji X. +# Uruchomiony tu — nie czeka na SMB, działa od startu X niezależnie od timing'u LO. +( + while sleep 0.4; do + for _wid in $(xdotool search --class soffice --onlyvisible 2>/dev/null); do + _wx=$(xdotool getwindowgeometry "$_wid" 2>/dev/null \ + | awk '/Position:/{split($2,a,","); print a[1]+0}') + if [ "${_wx:-0}" -lt 1920 ]; then + wmctrl -i -r "$_wid" -b remove,above 2>/dev/null + wmctrl -i -r "$_wid" -b add,below 2>/dev/null + xdotool windowmove --sync "$_wid" 4000 0 2>/dev/null + echo "[kiosk-manager/watchdog] Konsola LO ukryta (WID=$_wid, x=${_wx:-?})" + fi + done + done +) & + +# ─── START PREZENTACJI ─────────────────────────────────────────────────────── +/home/kiosk/presentation-manager.sh & + +# ─── WAITING SCREEN ────────────────────────────────────────────────────────── +chromium --app="$WAITING_PAGE" \ + --window-position=0,0 \ + --window-size=${RES_W},${RES_H} \ + --no-first-run \ + --disable-infobars \ + --disable-session-crashed-bubble & + +CHROMIUM_PID=$! + +# fullscreen waiting +for _i in $(seq 1 40); do + sleep 0.5 + WID=$(xdotool search --pid "$CHROMIUM_PID" --onlyvisible 2>/dev/null | head -1) + if [ -n "$WID" ]; then + wmctrl -i -r "$WID" -b add,fullscreen + wmctrl -i -r "$WID" -b add,above + break + fi +done + +echo "[kiosk-manager] Czekam na SMB..." + +while [ ! -f /tmp/kiosk-smb-ready ]; do + sleep 1 +done + +echo "[kiosk-manager] SMB gotowe — zamykam waiting" + +sleep 3 + +kill "$CHROMIUM_PID" +wait "$CHROMIUM_PID" 2>/dev/null + +# watchdog LibreOffice +( + while sleep 0.4; do + for _wid in $(xdotool search --class soffice --onlyvisible 2>/dev/null); do + _wx=$(xdotool getwindowgeometry "$_wid" 2>/dev/null \ + | awk '/Position:/{split($2,a,","); print a[1]+0}') + if [ "${_wx:-0}" -lt 1920 ]; then + wmctrl -i -r "$_wid" -b remove,above 2>/dev/null + wmctrl -i -r "$_wid" -b add,below 2>/dev/null + xdotool windowmove --sync "$_wid" 4000 0 2>/dev/null + fi + done + done +) & + +echo "[kiosk-manager] Tryb: prezentacja aktywna" + +wait diff --git a/kiosk-schedule b/kiosk-schedule new file mode 100644 index 0000000..3271c39 --- /dev/null +++ b/kiosk-schedule @@ -0,0 +1,15 @@ + GNU nano 8.4 /etc/cron.d/kiosk-schedule +# /etc/cron.d/kiosk-schedule +# Harmonogram kiosku WS-FE035 +SHELL=/bin/sh +PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin + +# Wyłącz kiosk w piątek o 22:00 (planowe wyłączenie) +0 22 * * * root /sbin/shutdown -h now + +# Jeśli kiosk uruchomi się po utracie prądu w oknie wyłączenia (sob 06:00 - pod 6:00 ), +# wyłącz go w ciągu max. 5 minut. Gdy kiosk jest normalnie wyłączony, wpisy nie działają. + +*/5 * * * 0,6 root /sbin/shutdown -h now +*/5 0-5 * * 1-5 root /sbin/shutdown -h now +*/5 22-23 * * 1-5 root /sbin/shutdown -h now \ No newline at end of file diff --git a/kiosk.service b/kiosk.service new file mode 100644 index 0000000..fc7ab22 --- /dev/null +++ b/kiosk.service @@ -0,0 +1,26 @@ +[Unit] +Description=Kiosk Dwuekranowy — Instrukcje + Prezentacja +After=systemd-user-sessions.service + +[Service] +User=kiosk +Group=kiosk +Type=simple +PAMName=login + +TTYPath=/dev/tty7 +StandardInput=tty +StandardOutput=journal +StandardError=journal + +# Czas wyświetlania każdego slajdu prezentacji (sekundy) +Environment=SLIDE_DURATION=10 + +ExecStartPre=/usr/bin/chvt 7 +ExecStart=/usr/bin/xinit /home/kiosk/kiosk-manager.sh -- :0 vt7 -nolisten tcp + +Restart=always +RestartSec=3 + +[Install] +WantedBy=multi-user.target diff --git a/logo.png b/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..a20bd2acb8d4a9560a043256f1e472313d458fc7 GIT binary patch literal 49362 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%0D;3zMNovk%vc|z?}7p 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/dev/null; umount "$SMB_MOUNT" 2>/dev/null' EXIT INT TERM + +stop_lo() { + if [ -n "${LO_PID:-}" ] && kill -0 "$LO_PID" 2>/dev/null; then + kill "$LO_PID" + wait "$LO_PID" 2>/dev/null + LO_PID="" + fi +} + +# Persistentny watchdog co 0.3s — przenosi konsolę LO poza ekran zanim stanie się widoczna. +# Działa przez cały czas życia skryptu. Fullscreen i Chromium above obsługuje start_lo(). +console_watchdog() { + while true; do + sleep 0.3 + for _wid in $(xdotool search --class soffice --onlyvisible 2>/dev/null); do + _wx=$(xdotool getwindowgeometry "$_wid" 2>/dev/null \ + | awk '/Position:/{split($2,a,","); print a[1]+0}') + if [ "${_wx:-0}" -lt 1920 ]; then + wmctrl -i -r "$_wid" -b remove,above 2>/dev/null + wmctrl -i -r "$_wid" -b add,below 2>/dev/null + xdotool windowmove --sync "$_wid" 4000 0 2>/dev/null + echo "[watchdog] Konsola ukryta (WID=$_wid, x=${_wx:-?})" + fi + done + done +} + +start_lo() { + stop_lo + # Usuń plik blokady LO — pozostaje po kill i blokuje kolejne uruchomienie + rm -f /tmp/.~lock.kiosk-live.pptx\# + + # Wyłącz konsolę prezentera — zapisujemy tuż przed uruchomieniem LO, + # żeby mieć pewność że dotyczy bieżącego profilu ($HOME może być /root) + _LO_PROFILE="$HOME/.config/libreoffice/4/user" + mkdir -p "$_LO_PROFILE" + cat > "$_LO_PROFILE/registrymodifications.xcu" << 'XCUEOF' + + + + + false + + + +XCUEOF + + echo "[presentation-manager] Kopiowanie pliku z SMB do /tmp..." + cp "$PRESENTATION_FILE" "$LOCAL_PRESENTATION" || { + echo "[presentation-manager] Błąd kopiowania pliku — retry za 5s" + sleep 5 + return 1 + } + echo "[presentation-manager] Uruchamianie pokazu: $LOCAL_PRESENTATION" + # --show otwiera bezpośrednio pokaz (bez Start Center) + soffice \ + --norestore \ + --nofirststartwizard \ + --show "$LOCAL_PRESENTATION" & + LO_PID=$! + echo "[presentation-manager] LibreOffice Impress uruchomiony (PID=$LO_PID)" + + # Obsłuż wszystkie okna LO wg pozycji: + # x >= 1920 (prawy ekran) → okno pokazu slajdów → fullscreen + # x < 1920 (lewy ekran) → konsola prezentacji → zminimalizuj + # Fallback po 5s: przenieś pierwsze znalezione okno na prawy ekran + ( + for _i in $(seq 1 30); do + sleep 1 + WIDS=$(xdotool search --pid "$LO_PID" --onlyvisible 2>/dev/null) + [ -z "$WIDS" ] && WIDS=$(xdotool search --class soffice --onlyvisible 2>/dev/null) + [ -z "$WIDS" ] && continue + + PRES_WID="" + for WID in $WIDS; do + WIN_X=$(xdotool getwindowgeometry "$WID" 2>/dev/null \ + | awk '/Position:/{split($2,a,","); print a[1]+0}') + if [ "${WIN_X:-0}" -ge 1920 ]; then + PRES_WID=$WID + fi + # Konsola (x<1920) obsługiwana przez console_watchdog() + done + + if [ -n "$PRES_WID" ]; then + wmctrl -i -r "$PRES_WID" -b add,fullscreen 2>/dev/null + echo "[presentation-manager] Pokaz fullscreen na prawym ekranie (próba $_i)" + break + fi + + # Fallback: po 5s nadal brak okna na prawym ekranie → przenieś pierwsze + if [ "$_i" -ge 5 ]; then + FIRST_WID=$(echo "$WIDS" | head -1) + if [ -n "$FIRST_WID" ]; then + wmctrl -i -r "$FIRST_WID" -b remove,fullscreen 2>/dev/null + sleep 0.3 + xdotool windowmove --sync "$FIRST_WID" 1920 0 + xdotool windowsize --sync "$FIRST_WID" 1920 1080 + wmctrl -i -r "$FIRST_WID" -b add,fullscreen 2>/dev/null + echo "[presentation-manager] Fallback: przeniesiono na prawy ekran (próba $_i)" + break + fi + fi + done + + # Podnieś Chromium na wierzch lewego ekranu (przykrywa konsolę jeśli się pojawiła) + sleep 1 + CHROMIUM_WID=$(xdotool search --class chromium --onlyvisible 2>/dev/null | head -1) + if [ -n "$CHROMIUM_WID" ]; then + xdotool windowraise "$CHROMIUM_WID" + wmctrl -i -r "$CHROMIUM_WID" -b add,above + echo "[presentation-manager] Chromium always-on-top (above)" + fi + ) & +} + +# ─── GŁÓWNA LOGIKA ─────────────────────────────────────────────────────────── + +echo "[presentation-manager] Montowanie udziału SMB: $SMB_SOURCE" +until mountpoint -q "$SMB_MOUNT" 2>/dev/null; do + mount "$SMB_MOUNT" 2>&1 \ + | sed 's/^/[presentation-manager] mount: /' + mountpoint -q "$SMB_MOUNT" 2>/dev/null || sleep 5 +done +echo "[presentation-manager] SMB zamontowany." + +# Synchronizuj czcionki z udziału SMB do katalogu użytkownika kiosk. +# Fontconfig i LibreOffice odczytują ~/.local/share/fonts/ bez uprawnień roota. +# Wystarczy wrzucić nowe pliki .ttf/.otf do $SMB_MOUNT/fonts/ — zostaną +# automatycznie zainstalowane przy następnym starcie kiosku. +FONTS_SRC="$SMB_MOUNT/fonts" +FONTS_DST="/home/kiosk/.local/share/fonts/smb" +if [ -d "$FONTS_SRC" ]; then + mkdir -p "$FONTS_DST" + FONTS_NEW=0 + for _f in "$FONTS_SRC"/*.ttf "$FONTS_SRC"/*.otf "$FONTS_SRC"/*.TTF "$FONTS_SRC"/*.OTF; do + [ -f "$_f" ] || continue + _dest="$FONTS_DST/$(basename "$_f")" + if [ ! -f "$_dest" ]; then + cp "$_f" "$_dest" 2>/dev/null && FONTS_NEW=1 + echo "[presentation-manager] Nowa czcionka: $(basename "$_f")" + fi + done + if [ "$FONTS_NEW" -eq 1 ]; then + fc-cache "$FONTS_DST" + echo "[presentation-manager] Cache czcionek zaktualizowany" + fi +fi + +# Sygnał dla kiosk-manager.sh: SMB zamontowany, można przełączyć Chromium +touch /tmp/kiosk-smb-ready +echo "[presentation-manager] SMB gotowy — sygnał wysłany" + +until [ -f "$PRESENTATION_FILE" ]; do + echo "[presentation-manager] Oczekiwanie na plik: $PRESENTATION_FILE" >&2 + sleep 5 +done + +# Uruchom persistentny watchdog przed pierwszym startem LO +console_watchdog & +WATCHDOG_PID=$! +echo "[presentation-manager] Watchdog uruchomiony (PID=$WATCHDOG_PID)" + +CURRENT_MTIME=$(stat -c %Y "$PRESENTATION_FILE" 2>/dev/null || echo "0") +start_lo + +# Pętla: restartuje pokaz po zakończeniu lub gdy wykryje zmianę pliku +while true; do + sleep 5 + + NEW_MTIME=$(stat -c %Y "$PRESENTATION_FILE" 2>/dev/null || echo "0") + + # Plik zmieniony — odśwież pokaz + if [ "$NEW_MTIME" != "$CURRENT_MTIME" ]; then + echo "[presentation-manager] Wykryto zmianę pliku — restart pokazu" + CURRENT_MTIME="$NEW_MTIME" + start_lo + # Po 5 s wyślij sygnał do kiosk-manager.sh — restartuje Chromium po tym jak LO + # zakończy ładowanie i ustawi okno pokazu na prawym ekranie + ( sleep 5 + touch /tmp/kiosk-chromium-restart + echo "[presentation-manager] Sygnał restart Chromium wysłany" + ) & + continue + fi + + # LibreOffice zakończył (koniec pokazu) — uruchom ponownie + if ! kill -0 "${LO_PID:-0}" 2>/dev/null; then + echo "[presentation-manager] Pokaz zakończony — restart" + start_lo + fi +done diff --git a/smb-credentials.template b/smb-credentials.template new file mode 100644 index 0000000..2023a46 --- /dev/null +++ b/smb-credentials.template @@ -0,0 +1,5 @@ +# Plik poświadczeń SMB dla kiosku + +username=NAZWA_UZYTKOWNIKA +password=HASLO +domain=WORKGROUP diff --git a/sway-config b/sway-config new file mode 100644 index 0000000..8760b59 --- /dev/null +++ b/sway-config @@ -0,0 +1,65 @@ +# Konfiguracja sway dla kiosku dwuekranowego +# Plik docelowy: /home/kiosk/.config/sway/config +# +# Nazwy wyjść wideo wykryj BEZ uruchomionego sway (działa zawsze jako root): +# for p in /sys/class/drm/card*-*; do echo "$(basename $p | sed 's/card[0-9]*-//'): $(cat $p/status)"; done +# +# lub gdy sway/kiosk JUŻ działa: +# WAYLAND_DISPLAY=wayland-0 XDG_RUNTIME_DIR=/run/kiosk wlr-randr +# +# Wpisz właściwe nazwy poniżej: + +set $OUTPUT_LEFT DP-1 +set $OUTPUT_RIGHT HDMI-1 + +# --- Wyjścia wideo --- +output * bg #000000 solid_color + +# Opcjonalnie: rotacja per-wyjście (odkomentuj jeśli wymagana) +# output $OUTPUT_LEFT transform 270 +# output $OUTPUT_RIGHT transform 270 + +# Przypisanie workspace do ekranów +workspace 1 output $OUTPUT_LEFT +workspace 2 output $OUTPUT_RIGHT + +# --- Przypisanie aplikacji do workspaces --- +# Chromium (prawy ekran — workspace 2) +# --app=URL generuje app_id: "chrome--Default" — używamy regex +assign [app_id="~^chrome"] workspace 2 +for_window [app_id="~^chrome"] move to workspace 2, fullscreen enable + +# Prezentacja LibreOffice Impress (lewy ekran — workspace 1) +# LO zawsze otwiera się na workspace 1 — assign to potwierdza +assign [app_id="soffice"] workspace 1 +assign [class="libreoffice-impress"] workspace 1 +for_window [app_id="soffice"] fullscreen enable +for_window [class="libreoffice-impress"] fullscreen enable +# Jeśli powyższe nie działają, sprawdź rzeczywistą klasę: +# WAYLAND_DISPLAY=wayland-0 XDG_RUNTIME_DIR=/run/kiosk swaymsg -t get_tree | grep -E 'app_id|class' + +# --- Wygląd --- +default_border none +default_floating_border none +hide_edge_borders both + +# Ukryj kursor — niewidoczny motyw XCursor (1x1 px, całkowicie przezroczysty) +# Działa niezależnie od pozycji kursora i bez dodatkowych pakietów. +# Plik binarny XCursor tworzony przez printf przy każdym starcie sway. +# Jeśli motyw nie zdąży załadować się przed pierwszym rysowaniem → hide_cursor 1 jako fallback. +seat * hide_cursor 1 +seat * xcursor_theme blank 1 +exec_always bash -c ' +D=/home/kiosk/.icons/blank/cursors +mkdir -p "$D" +# 68-bajtowy XCursor: 1x1 px, hotspot 0,0, ARGB 0x00000000 +for n in left_ptr default pointer crosshair text watch wait move size_all fleur; do + printf "\x58\x63\x75\x72\x10\x00\x00\x00\x00\x00\x01\x00\x01\x00\x00\x00\x02\x00\xfd\xff\x01\x00\x00\x00\x1c\x00\x00\x00\x24\x00\x00\x00\x02\x00\xfd\xff\x01\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x32\x00\x00\x00\x00\x00\x00\x00" > "$D/$n" +done +printf "[Icon Theme]\nName=blank\n" > /home/kiosk/.icons/blank/index.theme' + +# Wygaszanie ekranów przy uśpieniu (bez -w: ignoruje inhibitory Chromium) +exec swayidle before-sleep "wlopm --off '*'" + +# Uruchom skrypt kiosku +exec /home/kiosk/kiosk-manager.sh diff --git a/waiting.html b/waiting.html new file mode 100644 index 0000000..6a3a4f5 --- /dev/null +++ b/waiting.html @@ -0,0 +1,105 @@ + + + + + + Oczekiwanie na sieć + + + +
+ +
+
Oczekiwanie na sieć
+
+ + diff --git a/wol-enable.service b/wol-enable.service new file mode 100644 index 0000000..df79186 --- /dev/null +++ b/wol-enable.service @@ -0,0 +1,17 @@ +[Unit] +Description=Enable Wake on LAN on all ethernet interfaces +After=network.target + +[Service] +Type=oneshot +# Włącza WoL (g = magic packet) na każdym interfejsie sieciowym +ExecStart=/bin/bash -c '\ + for iface in $(ls /sys/class/net | grep -v lo); do \ + ethtool -s "$iface" wol g 2>/dev/null \ + && echo "WoL enabled: $iface" \ + || true; \ + done' +RemainAfterExit=yes + +[Install] +WantedBy=multi-user.target