Zenbook Duo a Debian: automatické přepínání režimů při změně konfigurace

Dnes
Doba čtení: 8 minut

Sdílet

Zaměříme se na praktickou stránku provozu zařízení: ukážeme si skript, který zajišťuje automatické přepínání režimů při připojení nebo odpojení klávesnice, ovládá zapínání a vypínání spodního displeje a řeší rotaci obrazovky.

Cílem je vytvořit spolehlivý mechanismus, díky kterému se notebook chová konzistentně a předvídatelně napříč různými prostředími (KDE/Wayland, wlroots, případně X11). Díky této automatizaci už nebude nutné vše nastavovat ručně.

Co se dozvíte v článku
  1. Hlavní automatizační skript – co dělá a proč existuje
  2. Napojení skriptu na udev
  3. Jak zjistit, že události fungují
  4. Doporučení pro praxi
  5. Přirozené automatické chování

Hlavní automatizační skript – co dělá a proč existuje

Aby bylo možné Zenbook Duo pohodlně používat pod Linuxem, je potřeba napsat vlastní logiku, která reaguje na změny hardwaru – hlavně na připojení a odpojení klávesnice, případně na změnu orientace zařízení. Tyto události určují, zda se má spodní displej zapnout, vypnout, rotovat nebo přesunout do jiného uspořádání.

logika automatizačního skriptuAutor: Antonín Mička

K tomu slouží hlavní skript s pracovním názvem asus-check-keyboard , na kterém stojí celá automatizace. Skript je volán prostřednictvím pravidel udev, takže se spustí během okamžiku pokaždé, když systém zaznamená změnu stavu USB zařízení, případně další událost – například změnu orientace detekovanou pomocnou službou.

Níže popisujeme hlavní automatizační skript, jako by všechny potřebné hodnoty (VID/PID klávesnice, názvy displejů nebo název senzoru víka) byly deklarované přímo v něm.

1. Inicializace statických hodnot

Na začátku skript nastaví několik základních proměnných. V plné verzi jsou načítané z konfigurace, ale pro přehlednost je zde ukazujeme přímo:

# Identifikace klávesnice
VENDOR_ID="ffff"
PRODUCT_ID="ffff"

# Názvy displejů
PRIMARY_DISPLAY_NAME="eDP-1"
SECONDARY_DISPLAY_NAME="eDP-2"

# ACPI identifikátor víka
LID="LID0"

2. Kontrola stavu víka

Pokud je víko zavřené, nemá smysl měnit režim displejů.

LID_STATE=$(grep -i "state" /proc/acpi/button/lid/${LID}/state | awk '{print $2}')
if [[ "$LID_STATE" == "closed" ]]; then
    echo "Víko je zavřené"
    exit 0
fi

3. Zjištění orientace zařízení

Zenbook Duo má akcelerometr, který hlásí orientaci jako normal, bottom-up, left-up nebo right-up. Skript tuto hodnotu namapuje na rotaci displeje:

DIR=$(timeout 2 monitor-sensor --accel | grep orientation)
case "$DIR" in
  *normal*)     DISPLAY_ROTATION="normal" ;;
  *bottom-up*)  DISPLAY_ROTATION="inverted" ;;
  *left-up*)    DISPLAY_ROTATION="left" ;;
  *right-up*)   DISPLAY_ROTATION="right" ;;
esac

4. Rozpoznání, zda je připojena klávesnice

Hlavní logika: pokud skript detekuje USB zařízení klávesnice, přepíná notebook do „režimu laptop“.

if lsusb | grep -iq "${VENDOR_ID}:${PRODUCT_ID}"; then
    echo "Klávesnice detekována – vypínám spodní displej"
else
    echo "Klávesnice není připojena – zapínám spodní displej"
fi

5. Rozlišení prostředí (X11, KDE Wayland, wlroots)

Skript automaticky zjišťuje, zda běží pod X11, KDE/Wayland nebo pod wlroots. Podle toho použije nástroj:

  • xrandr pro X11
  • kscreen-doctor pro KDE/Wayland
  • wlr-randr pro wlroots (Hyprland / Sway)
if [[ "$XDG_SESSION_TYPE" == "x11" ]]; then
    # X11 – používá xrandr
elif [[ "$XDG_SESSION_TYPE" == "wayland" && "$XDG_CURRENT_DESKTOP" == "KDE" ]]; then
    # KDE Wayland – používá kscreen-doctor
else
    # wlroots – používá wlr-randr
fi

6. Zapnutí/vypnutí a zarovnání displejů

📌 A) Při připojené klávesnici – spodní displej se vypne

# X11
xrandr --output ${SECONDARY_DISPLAY_NAME} --off

# KDE Wayland
kscreen-doctor output.${SECONDARY_DISPLAY_NAME}.disable

# wlroots
wlr-randr --output ${SECONDARY_DISPLAY_NAME} --off

📌 B) Při odpojené klávesnici – spodní displej se zapne a zarovná

case "$DISPLAY_ROTATION" in
  normal)   # pod primární
            xrandr --output $SECONDARY_DISPLAY_NAME --auto --below $PRIMARY_DISPLAY_NAME ;;
  left)     xrandr --output $SECONDARY_DISPLAY_NAME --auto --left-of $PRIMARY_DISPLAY_NAME ;;
  right)    xrandr --output $SECONDARY_DISPLAY_NAME --auto --right-of $PRIMARY_DISPLAY_NAME ;;
  inverted) xrandr --output $SECONDARY_DISPLAY_NAME --auto --above $PRIMARY_DISPLAY_NAME ;;
esac

KDE Wayland část navíc vypočítává přesnou geometrii pomocí kscreen-doctor -o a nastavuje souřadnice explicitně — to je nutné, protože KDE nepodporuje jednoduché above/right-of makra.

Plné znění skriptu

Zde je kompletní pracovní verze skriptu tak, jak byl popsán výše:

APP_NAME="asus-check-keyboard"
STATE_DIR="$HOME/.local/state/$APP_NAME"
STATE_FILE="$STATE_DIR/state"

# Výchozí režim, pokud není stavový soubor
if [[ -n "$1" ]]; then
    mode="$1"
elif [[ -f "$STATE_FILE" ]]; then
    mode=$(<"$STATE_FILE")
else
    mode="automatic-enabled"
fi

# Konfigurace (pro jednoduchost přímo zde)
VENDOR_ID="ffff"
PRODUCT_ID="ffff"
PRIMARY_DISPLAY_NAME="eDP-1"
SECONDARY_DISPLAY_NAME="eDP-2"
LID="LID0"

# Rotace (pokud existuje pomocný soubor)
if [[ -f /tmp/asus-rotation ]]; then
    source /tmp/asus-rotation
fi

# Stav víka
LID_STATE=$(grep -i "state" /proc/acpi/button/lid/${LID}/state | awk '{print $2}')
if [[ "$LID_STATE" == "closed" ]]; then
    exit 0
fi

DISPLAY_ROTATION="normal"

# Automatika – zjištění orientace
if [[ "$mode" == "automatic-enabled" ]]; then
    if [ -z "$DIR" ]; then
        DIR=$(timeout 2 monitor-sensor --accel | grep orientation)
    fi

    case "$DIR" in
      *normal*)     DISPLAY_ROTATION="normal" ;;
      *bottom-up*)  DISPLAY_ROTATION="inverted" ;;
      *left-up*)    DISPLAY_ROTATION="left" ;;
      *right-up*)   DISPLAY_ROTATION="right" ;;
    esac
fi

user=$USER
type=$XDG_SESSION_TYPE
desktop_env=$XDG_CURRENT_DESKTOP

# Ignorovat greeter
if [[ "$user" == "sddm" ]]; then
    exit 0
fi

# X11 větev
if [[ "$type" == "x11" ]]; then
    if lsusb | grep -iq "${VENDOR_ID}:${PRODUCT_ID}"; then
        xrandr --output ${SECONDARY_DISPLAY_NAME} --off
    else
        case "$DISPLAY_ROTATION" in
          left)     xrandr --output ${PRIMARY_DISPLAY_NAME} --rotate left \
                            --right-of ${SECONDARY_DISPLAY_NAME} \
                            --output ${SECONDARY_DISPLAY_NAME} --auto ;;
          right)    xrandr --output ${PRIMARY_DISPLAY_NAME} --rotate right \
                            --left-of ${SECONDARY_DISPLAY_NAME} \
                            --output ${SECONDARY_DISPLAY_NAME} --auto ;;
          inverted) xrandr --output ${PRIMARY_DISPLAY_NAME} --rotate inverted \
                            --below ${SECONDARY_DISPLAY_NAME} \
                            --output ${SECONDARY_DISPLAY_NAME} --auto ;;
          normal)   xrandr --output ${PRIMARY_DISPLAY_NAME} --rotate normal \
                            --above ${SECONDARY_DISPLAY_NAME} \
                            --output ${SECONDARY_DISPLAY_NAME} --auto ;;
        esac
    fi
    exit 0
fi

# KDE Wayland větev (zkrácená)
if [[ "$type" == "wayland" && "$desktop_env" == "KDE" ]]; then
    if lsusb | grep -iq "${VENDOR_ID}:${PRODUCT_ID}"; then
        kscreen-doctor output.${PRIMARY_DISPLAY_NAME}.rotation.normal
        kscreen-doctor output.${SECONDARY_DISPLAY_NAME}.disable
    else
        kscreen-doctor output.${PRIMARY_DISPLAY_NAME}.rotation.${DISPLAY_ROTATION}
        kscreen-doctor output.${SECONDARY_DISPLAY_NAME}.enable \
                       output.${SECONDARY_DISPLAY_NAME}.rotation.${DISPLAY_ROTATION}
        # Geometrie a rearrange

        # --- Získání geometrie primárního výstupu ---
        read PX PY PW PH <<< $(kscreen-doctor -o | awk -v out="$PRIMARY_DISPLAY_NAME" '$0 ~ "Output: " && $0 ~ out { in_block=1; next } in_block && $0 ~ "Geometry:" { split($3, pos, ","); split($4, res, "x"); print pos[1], pos[2], res[1], res[2]; exit }')

        # --- Získání velikosti sekundárního výstupu ---
        read SX SY SW SH <<< $(kscreen-doctor -o | awk -v out="$SECONDARY_DISPLAY_NAME" '$0 ~ "Output: " && $0 ~ out { in_block=1; next } in_block && $0 ~ "Geometry:" { split($3, pos, ","); split($4, res, "x"); print pos[1], pos[2], res[1], res[2]; exit }')

        PX=0
        PY=0

        # --- Výpočet nové pozice ---
        case "$DISPLAY_ROTATION" in
            *left*)
                PX=$(echo "$PX" | tr -dc '0-9')
                SW=$(echo "$SW" | tr -dc '0-9')
                SX=$((PX - SW))
                SY=$PY
                ;;
            *right*)
                PX=$(echo "$PX" | tr -dc '0-9')
                PW=$(echo "$PW" | tr -dc '0-9')
                SX=$((PX + PW))
                SY=$PY
                ;;
            *inverted*)
                SX=$PX
                SY=$((PY - SH))
                ;;
            *normal*)
                SX=$PX
                SY=$((PY + PH))
                ;;
            *)
                echo "Neplatná orientace: $DISPLAY_ROTATION"
                exit 1
                ;;
        esac

        # --- Výstup a nastavení ---
        kscreen-doctor output.$PRIMARY_DISPLAY_NAME.position.$PX,$PY output.${PRIMARY_DISPLAY_NAME}.rotation.${DISPLAY_ROTATION}
        kscreen-doctor output.$SECONDARY_DISPLAY_NAME.position.$SX,$SY output.${SECONDARY_DISPLAY_NAME}.rotation.${DISPLAY_ROTATION}
    fi
    exit 0
fi

# wlroots větev
if [[ "$type" == "wayland" ]]; then
    if lsusb | grep -iq "${VENDOR_ID}:${PRODUCT_ID}"; then
        wlr-randr --output ${PRIMARY_DISPLAY_NAME} --rotate normal
        wlr-randr --output ${SECONDARY_DISPLAY_NAME} --off
    else
        wlr-randr --output ${SECONDARY_DISPLAY_NAME} --auto --rotate ${DISPLAY_ROTATION}

        # --- Výpočet nové pozice ---
        case "$DISPLAY_ROTATION" in
            *left*)
                wlr-randr --output ${PRIMARY_DISPLAY_NAME} --rotate ${DISPLAY_ROTATION} --right-of ${SECONDARY_DISPLAY_NAME}
                ;;
            *right*)
                wlr-randr --output ${PRIMARY_DISPLAY_NAME} --rotate ${DISPLAY_ROTATION} --left-of ${SECONDARY_DISPLAY_NAME}
                ;;
            *inverted*)
                wlr-randr --output ${PRIMARY_DISPLAY_NAME} --rotate ${DISPLAY_ROTATION} --below ${SECONDARY_DISPLAY_NAME}
                ;;
            *normal*)
                wlr-randr --output ${PRIMARY_DISPLAY_NAME} --rotate ${DISPLAY_ROTATION} --above ${SECONDARY_DISPLAY_NAME}
                ;;
            *)
                echo "Neplatná orientace: $DISPLAY_ROTATION"
                exit 1
                ;;
        esac
    fi
    exit 0
fi
exit 0

Napojení skriptu na udev

Aby se automatizační skript mohl spouštět přesně ve chvíli, kdy se změní stav klávesnice (připojení / odpojení), je potřeba jej propojit se subsystémem udev. Ten je součástí systemd a zajišťuje reakce na hardwarové události jádra – tedy i na připojení USB zařízení.

Proč nestačí spouštět skript ručně

Ruční spouštění by neodráželo skutečné chování notebooku. Zenbook Duo totiž často připojuje a odpojuje klávesnici i během běžného používání – například při zaklapnutí, změně režimu nebo přechodu mezi „tabletovým“ a „notebookovým“ rozložením. Pomocí udev tedy zajistíme okamžitou reakci.

Udev pravidlo pro detekci klávesnice

Na základě VID/PID klávesnice vytvoříme jednoduché udev pravidlo, které se spustí při každé změně jejího stavu.

# /etc/udev/rules.d/99-asus-check-keyboard.rules

ACTION=="add|remove", \
  SUBSYSTEM=="usb", \
  ATTR{idVendor}=="ffff", ATTR{idProduct}=="ffff", \
  RUN+="/usr/local/bin/asus-check-keyboard udev-event"

Toto pravidlo říká:

  • ACTION==add|remove — spustit při připojení i odpojení USB zařízení
  • SUBSYSTEM==usb — reagovat pouze na USB události
  • idVendor / idProduct — identifikace originální klávesnice Asus
  • RUN+= — spustit náš skript

Po úpravě pravidla je nutné jej načíst:

sudo udevadm control --reload-rules
sudo udevadm trigger

Wrapper pro běh ve správném uživatelském kontextu

Udev běží jako root a mimo grafickou seanci — nemá přístup k proměnným jako DISPLAY, XDG_RUNTIME_DIR nebo socketům Waylandu. To znamená, že skript nemůže přímo spustit xrandr, kscreen-doctor nebo wlr-randr.

Proto je potřeba maličký wrapper, který zajistí spuštění skriptu v kontextu přihlášeného uživatele.

# /usr/local/bin/asus-check-keyboard-wrapper

#!/bin/bash
USER_NAME="tvoje-uživatelské-jméno"

sudo -u "$USER_NAME" \
  XDG_RUNTIME_DIR="/run/user/$(id -u $USER_NAME)" \
  DISPLAY=:0 \
  /usr/local/bin/asus-check-keyboard "$@"

V udev pravidle pak budeme volat:

RUN+="/usr/local/bin/asus-check-keyboard-wrapper"

Jak zjistit, že události fungují

Můžete sledovat živé události:

udevadm monitor --environment --udev

Nebo otestovat pravidlo:

udevadm test $(udevadm info -q path -n /dev/bus/usb/001/002)

Doporučení pro praxi

  • Ujistěte se, že skript je spustitelný ( chmod +x).
  • Výstup skriptu logujte do /tmp/asus-log pro ladění.
  • Pokud používáte více uživatelů, wrapper musí umět najít aktivního.
  • V systemd prostředích lze wrapper nahradit user service — elegantnější řešení.

Přirozené automatické chování

Pomocí udev se automatizační skript spouští přesně ve chvíli, kdy se změní stav klávesnice. O zbytek se stará samotný skript, který umí podle prostředí (X11/KDE/wlroots) správně nastavit displeje. Tím získáme přirozené chování, které je velmi blízké tomu, co poskytuje operační systém Windows.

V příštím závěrečném díle se podíváme na vylepšený wrapper a zabalení do balíčku deb.

Seriál: Asus Zenbook Duo
Neutrální ikona do widgetu na odběr článků ze seriálů

Zajímá vás toto téma? Chcete se o něm dozvědět víc?

Objednejte si upozornění na nově vydané články do vašeho mailu. Žádný článek vám tak neuteče.


Autor článku

Vývojový pracovník realtime aplikací, se specializací na Qt, C++ a Linux.