Files
2026-09-26 17:37:13 +02:00

11 KiB
Raw Permalink Blame History

Linux for the ASUS Zenbook Duo

This project adds better Linux support for the Zenbook Duo by running a small background service that reacts to the keyboard being attached/detached (USB or Bluetooth) and keeps the dual-screen experience usable.

Quick Start (Non-technical)

What you need

  • An ASUS Zenbook Duo
  • GNOME on Wayland, KDE Plasma on Wayland, or Niri
  • A Terminal and your sudo password (the installer needs to change system settings)
./install.sh

Notes:

  • install.sh auto-detects GNOME, KDE Plasma, or Niri and then runs the matching setup script plus the UI installer.
  • If you use multiple local accounts, run the installer from the account that should own the session agent and autostart entries; the Rust runtime itself is shared machine-wide, so other users can still open the UI after install.
  • If you prefer to run it with sudo, use sudo -E ./install.sh (so per-user setup targets your user session).
  • If you re-run the installer, restart the session agent: systemctl --user restart zenbook-duo-session-agent.service
  • Log out and back in (needed for permission changes).

Build (no install)

./build.sh

Use ./build.sh --runtime-only to build only the Rust runtime, or ./build.sh --ui-only to build only the Control Panel UI.

Manual fallback:

./setup-gnome.sh
# or
./setup-kde.sh
# or
./setup-niri.sh

Optional: install or update just the Control Panel app (UI)

If you only want to build/update the desktop app:

./install-ui.sh

Use ./install-ui.sh --build-only --dir . to compile without installing, or ./install-ui.sh --install-only --dir . to install already-built artifacts from the current checkout.

On desktop sessions, the installer now prefers a graphical admin-password prompt for the system steps and falls back to terminal sudo when needed.

Uninstall

To remove the background service and system changes:

./uninstall.sh

To remove the optional UI app:

  • Fedora / RHEL-based: sudo dnf remove zenbook-duo-control
  • Debian / Ubuntu-based: sudo apt remove zenbook-duo-control
  • Arch / CachyOS: sudo rm -f /usr/local/bin/zenbook-duo-control /usr/share/applications/zenbook-duo-control.desktop /usr/share/pixmaps/zenbook-duo-control.png

Advanced (Technical)

Screenshots

Zenbook Duo Control USB Zenbook Duo Control BLUETOOTH

Features

Feature USB Bluetooth
Toggle bottom screen on when keyboard removed ✅ ✅
Toggle bottom screen off when keyboard placed ✅ ✅
Toggle bluetooth on when keyboard removed ✅ ✅
Toggle bluetooth off when keyboard placed (if it was off before) ✅ ✅
Screen brightness sync ✅ ✅
Reset airplane mode on keyboard attach/detach ✅ N/A
Keyboard backlight set on boot/attach ✅ ✅
Keyboard backlight sync across attach/detach ✅ ✅
Keyboard backlight cycle (F4) ✅ ✅
Correct state on boot/resume (suspend & hibernate) ✅ ✅
Auto rotation ✅ ✅
Function keys (F1 mute, F2 volume down, F3 volume up, F10 bluetooth) ✅ ✅
Function keys (F5 brightness down, F6 brightness up) ✅ ✅
Function keys (F7 swap displays) ✅ ✅
Function keys (F9 mic mute) ✅ ❌
Function keys (F11 emojis) ✅ ✅ (Fn+F11)
Function keys (F8 airplane mode, F12 ASUS software) ❌ ❌
Correct state on lock/unlock ✅ ✅
Fn layer (top row) ✅ ✅

Notes:

  • USB top row defaults to media keys; hold Fn for F1-F12.
  • Do not install hwdb remaps for KEYBOARD_KEY_7003* on USB (it overrides the Fn layer).

Requirements

  • ASUS Zenbook Duo (USB vendor 0B05, product 1B2C or 1B2D on UX8406M-class models)
  • Linux with GNOME on Wayland, KDE Plasma on Wayland, or Niri (tested with Fedora)
  • systemd for service management
  • GNOME: gdctl (part of mutter) for display configuration
  • KDE: kscreen-doctor (part of kscreen) for display configuration
  • Niri: niri msg for display configuration

What ./setup-gnome.sh / ./setup-kde.sh / ./setup-niri.sh change

  • Installs dependencies:
    • Common: usbutils, iio-sensor-proxy, systemd
    • GNOME: mutter/gdctl (via setup-gnome.sh)
    • KDE: kscreen/kscreen-doctor (via setup-kde.sh)
    • Niri: niri (via setup-niri.sh)
  • Adds your user to the input group (logout/login required)
  • Installs a udev rule for the Zenbook Duo keyboard
  • Installs/enables Rust runtime units:
    • zenbook-duo-rust-daemon.service (system daemon)
    • zenbook-duo-rust-lifecycle.service (boot/shutdown + sleep hook)
    • zenbook-duo-session-agent.service (user session)
    • The session agent is enabled from the user manager's default.target, then syncs the current dock state when your graphical session comes up after reboot/login
  • Installs Rust runtime binaries to /usr/local/libexec/zenbook-duo
  • Adds a managed sudoers.d drop-in for the exact brightness writes used by the session agent

Contributor note: the desktop setup scripts are thin wrappers around setup-common.sh. When adding or changing supported systems, update the shared helper for common behavior and keep only package names/manual dependency hints in the per-desktop wrapper.

Contributor compatibility checks

Use the version bump helper when preparing a release:

./bump-version.sh patch    # or minor, major, or an explicit version like 0.3.4

Use the root check script before changing installer, runtime, or UI behavior:

./check.sh installers   # shell syntax + installer smoke tests
./check.sh rust         # Rust runtime unit tests
./check.sh frontend     # React/TypeScript production build
./check.sh all          # full compatibility pass

Supported matrix covered by the installer smoke tests:

Desktop backend Setup wrapper Display command
GNOME on Wayland setup-gnome.sh gdctl
KDE Plasma on Wayland setup-kde.sh kscreen-doctor
Niri setup-niri.sh niri msg
Distro family Package manager
Fedora / RHEL-based dnf
Debian / Ubuntu-based apt
Arch / CachyOS pacman

Compatibility checklist for maintainers:

  • Keep common setup behavior in setup-common.sh; keep desktop wrappers limited to backend-specific packages and manual dependency hints.
  • Keep the managed sudoers.d drop-in aligned with the exact backlight devices present on the target system; do not add wildcard command arguments to sudoers.
  • Keep settings defaults aligned across setup-common.sh, the Rust DuoSettings defaults, and the frontend default settings helper. The installer writes setupCompleted=true; a missing settings file should still show first-run setup.
  • Preserve GNOME, KDE, and Niri command arguments when refactoring display code unless a backend-specific behavior change is intentional and tested.
  • Keep desktop readiness probes centralized in the Rust session helpers so GNOME, KDE, and Niri fallback behavior stays consistent.
  • Update tests/install-stdin-test.sh whenever supported desktops, package managers, service units, defaults, or installer entrypoints change.
  • Run the narrow ./check.sh target for the area you touched; run ./check.sh all before handing off broad cross-area changes.

Troubleshooting

  • Nothing happens when docking/undocking:
    • Check the services are running: systemctl status zenbook-duo-rust-daemon.service and systemctl --user status zenbook-duo-session-agent.service
    • Watch daemon logs: journalctl -u zenbook-duo-rust-daemon.service -f
  • Reboot/login or resume comes up in the wrong layout:
    • After login or resume, the lifecycle handler and session agent re-sync the current attached/detached state without a manual restart
    • Check systemctl --user status zenbook-duo-session-agent.service; an early No supported session backend became ready before timeout; continuing to wait warning is OK if the service remains active
    • Confirm your user manager has the desktop-session environment: systemctl --user show-environment | grep -E 'DISPLAY|WAYLAND_DISPLAY|NIRI_SOCKET|XDG_CURRENT_DESKTOP|XDG_SESSION_DESKTOP|DESKTOP_SESSION|XDG_SESSION_TYPE'
    • If those variables are missing after reinstalling, rerun ./install.sh from an active desktop session, then log out and back in once
  • Keyboard media/Fn keys stop working after suspend or reattaching the keyboard:
    • The optional USB media remap helper is stopped before sleep and retried automatically after resume, so a manual service restart should not be needed.
    • If recovery still fails, check journalctl -u zenbook-duo-rust-daemon.service -f for USB media remap auto-start failed or repeated No such device messages.
    • You do not need a separate /etc/udev/rules.d/*uinput* rule for this project.
  • KBLIGHT - Device lost, re-scanning in a loop:
    • You likely need to log out and back in so your session gets the input group membership
  • If the Control Panel says Service/Rust runtime unavailable after switching to another local account:
    • Confirm the system daemon is running: systemctl status zenbook-duo-rust-daemon.service
    • If the daemon is stopped or stale, rerun ./install.sh from the account that should own the session agent and autostart entries, then log out and back in once

Upgrading from older versions

If you previously installed a hwdb key remap, remove it so Fn+F1-F12 works on USB:

sudo rm -f /etc/udev/hwdb.d/90-zenbook-duo-keyboard.hwdb
sudo systemd-hwdb update
sudo udevadm trigger

Supported distros

Distro Package Manager
Fedora / RHEL-based dnf
Debian / Ubuntu-based apt
Arch / CachyOS pacman

Other distros: install dependencies manually and run ./setup-gnome.sh, ./setup-kde.sh, or ./setup-niri.sh (it exits if it cannot detect your package manager).

Control Panel UI (Tauri + React)

  • Build & install: ./install-ui.sh
  • Build only: ./build.sh --ui-only
  • Arch / CachyOS note: install-ui.sh builds the UI locally, then installs zenbook-duo-control to /usr/local/bin and desktop assets under /usr/share
  • Dev mode:
cd ui-tauri-react
npm install
npm run dev

Fedora: “Nobara-like” setup helper

If you’re on Fedora and want a more “Nobara-like” out-of-box experience (RPM Fusion, codecs, common gaming tools), there’s an optional helper script:

./nobara-like.sh

It can also add the Nobara COPR repo definitions disabled by default, so you can cherry-pick packages without mixing repos during normal upgrades.