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
- Hlavní automatizační skript – co dělá a proč existuje
- Napojení skriptu na udev
- Jak zjistit, že události fungují
- Doporučení pro praxi
- 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í.
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:
xrandrpro X11kscreen-doctorpro KDE/Waylandwlr-randrpro 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-logpro 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.
