Use an iPad as a second display for Omarchy, the way macOS Sidecar does. It syncs over a USB-C cable (or, if you opt in, over Wi-Fi), and touch works as a mouse.
Made for Apple Silicon M1 and M2 MacBooks running Omarchy on Asahi Linux. Their USB-C ports can't drive an external display under Linux yet (no DisplayPort alt mode), so an iPad is the only second screen you can get. Nothing in the code is M1-specific, and it will probably run on other Omarchy machines, but it has only been tested on a 13" M1 MacBook Pro (2020) with an 11" iPad Pro. Reports from M2 owners are very welcome.
A bar widget creates a virtual monitor sized to your iPad and streams it to the free OpenDisplay app on the iPad. Taps, drags, Apple Pencil and two-finger scroll come back as pointer input. Drag windows onto it, or move a workspace there, like any other monitor.
- Omarchy (Hyprland with Lua config, Quickshell bar).
- On the iPad: OpenDisplay, free on TestFlight.
- USB-C (default): works out of the box. The first time you plug the iPad in, tap Trust on it.
- Wi-Fi (opt-in): turn on Allow Wi-Fi in the widget. The iPad and the computer must be on the same, trusted network (see Security). Phone hotspots may block device-to-device traffic.
- Packages: setup installs whatever is missing (it asks for your password once):
wf-recorder,avahi,wayland,gcc,make,pkgconf,python. DEPENDENCIES.md lists every package it uses, with the versions it was tested on.
omarchy plugin add https://github.com/LeoAba/omarchy-sidecar-leoaba --enableThen click the tablet icon in the bar once. A terminal opens and finishes the setup: it installs the packages Sidecar needs, builds the small touch-input helper (pointer/) and links the CLI to ~/.local/bin/omarchy-sidecar (only if that path is free). After that the widget is ready. You can also run ~/.config/omarchy/plugins/io.github.leoaba.sidecar/setup yourself.
- Open OpenDisplay on the iPad.
- Click the tablet icon in the bar, then pick the iPad.
- Right-click the icon to connect to the last iPad, or to disconnect.
- The iPad appears as a monitor to the right of your screen.
- The widget shows the connection (USB or Wi-Fi), the resolution, the frame rate, and the bitrate.
Quality switches live while connected. The desktop layout stays the same; only the number of pixels sent changes:
| Sent (11" iPad Pro) | ||
|---|---|---|
| Sharp | 2388×1668 | native pixels, crispest |
| Balanced | 1790×1250 | 56% of the pixels |
| Light | 1194×834 | 25% of the pixels, lightest on the laptop |
Connection: by default Sidecar only connects over the USB-C cable. With Allow Wi-Fi on, the cable is still preferred: plug in during a Wi-Fi session and it moves over; unplug and it falls back to Wi-Fi.
CLI:
omarchy-sidecar list # iPads on the network
omarchy-sidecar connect [NAME|IP] [--quality sharp|balanced|light] [--allow-wifi|--wifi] # default: USB-C only
omarchy-sidecar quality light # live
omarchy-sidecar status
omarchy-sidecar stopWith Wi-Fi allowed, if Bonjour can't see the iPad (another subnet, Tailscale), use By IP in the widget, or run omarchy-sidecar connect 192.168.x.y --allow-wifi.
- Display:
hyprctl output create headlesscreates the virtual monitor, sized from the iPad'shellomessage. - Video:
wf-recordercaptures the monitor. It's encoded in software (libx264 ultrafast/zerolatency, one slice, no VBV, level 5.1) and sent as H.264 following the OpenDisplay protocol. - Input: touches arrive as normalized coordinates, and
pointer/sidecar-pointer(azwlr_virtual_pointerclient) replays them on that monitor. - USB: a small built-in usbmuxd client opens the connection to the app over the cable. No
iproxyneeded.
State and a log live in ~/.local/state/omarchy/sidecar*. The log caps itself at 2 MB.
OpenDisplay is a simple protocol: plain TCP, no encryption and no pairing. The official OpenDisplay Mac sender works the same way. This plugin can't change that on its own, since it would need support in the iPad app and the protocol.
- Wi-Fi is off by default. Over Wi-Fi the stream is not encrypted and the iPad isn't authenticated: anyone on the same network could see it, or pretend to be your iPad. So it only happens if you turn on Allow Wi-Fi (or pass
--allow-wifi), and only on networks you trust. - USB-C keeps the stream off the network completely. It goes over the cable through usbmuxd, and it's the default.
- Want encrypted Wi-Fi? Run it over Tailscale.
- Trust on first use: an iPad found through Bonjour gets pinned to the address it had the first time you used it. Quick-connect and reconnect only dial that address. If an iPad with the same name or ID shows up somewhere else, quick-connect refuses, and you have to pick it from the list yourself (or use
--trust) to re-pin it. If you type an IP yourself, it connects to that IP directly. - Pointer input from the iPad only works on the virtual iPad display. The virtual pointer is bound to that output, so it can't reach your laptop's own screen.
- Long sessions slow Hyprland 0.56.2 down. This is the one open problem. Capturing a virtual monitor for a long time makes Hyprland use more and more CPU. The frame rate drops, and disconnecting can freeze the desktop for a few seconds. Hyprland recovers by itself afterwards.
- It looks like an upstream screencopy bug that's already fixed (hyprwm/Hyprland#16361, merged after 0.56.2). I'm waiting for the Hyprland release that includes it, to confirm.
- Any help here is very welcome: if you know this area of Hyprland, have tested a newer build, or have seen the same thing with other screencopy tools, please open an issue.
tools/stall-probe.pyrecords the stalls.
- The pointer is drawn into the video, so it moves at video frame rate rather than touch rate.
- Software encoding only: there's no hardware encoder on Asahi. Sharp at 2388×1668 costs about 1–1.5 CPU cores for capture and encoding.
~/.config/omarchy/plugins/io.github.leoaba.sidecar/setup --uninstall
omarchy plugin remove io.github.leoaba.sidecartools/fake-receiver.py stands in for the iPad app, tools/fake-usbmuxd.py for a USB-connected iPad, and tools/stall-probe.py watches for desktop stalls. SIDECAR_DUMP=file saves the exact stream sent; SIDECAR_X264=":key=value" appends x264 options.
Vibecoded with AI: designed, tested and debugged on real hardware together with AI coding assistants.
I'm so happy and grateful to be part of Omarchy, and I really appreciate all the work that's been put into it. Thank you to everyone behind Omarchy, and to Hyprland, Quickshell, Asahi Linux and OpenDisplay, which this plugin stands on. My M1 couldn't use an external monitor, and now it has a second screen again :)
MIT. OpenDisplay is a separate project with its own license; this plugin only talks to it over its published protocol.

0 comments
log in to comment.