240 lines
11 KiB
Markdown
240 lines
11 KiB
Markdown
# 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 (recommended)
|
||
|
||
```bash
|
||
./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)
|
||
|
||
```bash
|
||
./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:
|
||
|
||
```bash
|
||
./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:
|
||
|
||
```bash
|
||
./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:
|
||
|
||
```bash
|
||
./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
|
||
|
||

|
||

|
||
|
||
### 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:
|
||
|
||
```bash
|
||
./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:
|
||
|
||
```bash
|
||
./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:
|
||
|
||
```bash
|
||
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:
|
||
|
||
```bash
|
||
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:
|
||
|
||
```bash
|
||
./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.
|