kick/README.md

167 lines
7.3 KiB
Markdown
Raw Normal View History

# tabbed-wlroots
A nested Wayland compositor (built on wlroots) that presents several
client apps as tabs in a single window on the host Wayland compositor.
Each command-line argument launches one tab.
## Building
The project builds with [Meson](https://mesonbuild.com/) + Ninja. On most
distributions the non-wlroots dependencies are available in the standard
repositories; `wlroots` is used from the system where one is installed, and
otherwise is fetched and built automatically from a pinned source tarball
(`subprojects/wlroots.wrap`).
```sh
meson setup build
meson compile -C build
```
The resulting binary is `build/tabbed-wlroots`.
### Debian / Ubuntu
```sh
sudo apt install build-essential meson ninja-build pkg-config \
libwayland-dev libwayland-bin wayland-protocols \
libxkbcommon-dev libpixman-1-dev \
libegl1-mesa-dev libgles2-mesa-dev
```
No wlroots package is needed: this project targets wlroots 0.19, while
Debian/Ubuntu ship another version as a versioned package (e.g.
`libwlroots-0.20-dev` on Debian >= trixie, pkg-config name `wlroots-0.20`),
so Meson builds the bundled 0.19.1 instead (see below).
### Arch
```sh
sudo pacman -S base-devel meson ninja pkgconf wayland wayland-protocols \
wlroots xkbcommon mesa pixman
```
### Fedora
```sh
sudo dnf install gcc meson ninja-build pkgconf-pkg-config \
wayland-devel wayland-protocols-devel wlroots-devel \
libxkbcommon-devel mesa-libEGL-devel mesa-libGLES-devel pixman-devel
```
### When the system wlroots dep is missing / the fallback can't find one
The Meson setup searches for `wlroots-0.19` then `wlroots`; if neither is
installed it builds the pinned 0.19.1 from `subprojects/wlroots.wrap`, which
only needs the wayland, wayland-protocols, xkbcommon and EGL/GLES2 dev
packages listed above. On newer Debian/Ubuntu the packaged wlroots is a
versioned library (e.g. `libwlroots-0.20-dev`, pkg-config `wlroots-0.20`)
whose version falls outside this project's `>=0.19,<0.20` pin, so the
fallback build is used automatically and no wlroots package needs to be
installed. Optional wlroots features (Vulkan, X11, libinput, etc.) are
auto-detected and skipped when their build deps aren't present, so a minimal
install works too.
## Usage
```
tabbed-wlroots [prog...]
```
Each `<prog>` is one shell command; quote it when it takes arguments, e.g.
`tabbed-wlroots "foot -e htop" "firefox -no-remote"`. With no arguments, a
single terminal tab is opened (see `$TERMINAL` below). `tabbed-wlroots -h`
prints this usage.
Every argument starts a new tab. There is no tab-count limit. Client apps run
as children of tabbed-wlroots and connect to its private Wayland socket, so
`DISPLAY` is unset for them and their `WAYLAND_DISPLAY` points at the tabbed
compositor (clients are launched with the GDK and Qt Wayland backends forced).
`Ctrl+Shift+T` opens the terminal named by the `$TERMINAL` environment variable
when it is set (see Key bindings); otherwise it falls back to `alacritty`. The
value is used verbatim, so it may carry arguments, e.g.
`TERMINAL="foot -e htop" tabbed-wlroots ...`.
Notes:
- An argument written bare (starting with `-`) is taken as a tab launcher too
(unless quoted); a warning is printed when it looks like a stray option.
- Each command (an argument or the default `$TERMINAL`) is resolved on `PATH`
before the session starts: if its program isn't found, tabbed-wlroots prints
an error and exits non-zero without opening any window.
- The client applications run inside the session, so closing their windows
only closes their tab; closing the host tabbed-wlroots window, or sending
SIGINT/SIGTERM, shuts the whole session down and kills all client children.
## Key bindings
| Key | Action |
|----------------------------|---------------------------------------------------|
| `Alt` (hold) | Show the tab-title overlay (centered) |
| `Alt+Up` | Previous tab (hold to keep cycling) |
| `Alt+Down` | Next tab (hold to keep cycling) |
| `Ctrl+Shift+T` | Open a new terminal tab next to the current one |
Holding `Alt` shows a passive overlay listing every tab's title with the
active tab highlighted; the list updates live as you cycle with `Up`/`Down`
and disappears on `Alt` release. It is drawn by tabbed itself, so it works
on hosts with or without a title bar (the overlay text uses a system font
via FreeType; set `TABBED_FONT` to pick one). Each row also shows the
tab's app icon, resolved from its Wayland `app_id` via the desktop
files and icon themes in `XDG_DATA_DIRS` (PNG via libpng, SVG via
librsvg); tabs without a resolvable icon get a generic placeholder.
`Ctrl+Shift+T` launches a fresh terminal (`$TERMINAL`, default
`alacritty`) as a new tab placed immediately to the right of the current tab,
and it becomes the active tab once it maps — exactly as if its command had been
given on the command line.
The tab-cycling hotkey only emits a physical press for the first tab switch;
repeats are synthesized, so holding `Alt+Up/Down` keeps stepping through
tabs while any other key stops it.
Function keys are **not** grabbed: `F9`/`F10`/`F11` (and anything else) are
forwarded to the focused client, so apps keep their own bindings (e.g. a
browser's `F11`).
## Fullscreen
Fullscreen mirrors between the host window and the clients:
- Fullscreen the tabbed window (use your host compositor's fullscreen
keybinding, or an app's own `F11`) and every tab is told it is fullscreen,
so clients drop their decorations and fill the screen.
- A client that requests fullscreen itself (e.g. a video player) also
fullscreens the tabbed host window.
- Leaving fullscreen returns every tab to maximized.
## Logging (`TABBED_LOG`)
Quiet by default: a plain launch prints nothing to the launching terminal
except fatal errors. Set `TABBED_LOG` before starting:
| Value | Meaning |
|-------|------------------------------------------------------------------|
| unset/`0` | Quiet. Only fatal errors are shown. |
| `1` | Info. Status lines, dump summaries, host notes (e.g. socket). |
| `2` | Debug. Adds a per-frame `[diag]` trace and EGL/render/import messages, and client stdout/stderr flows through. |
At debug level (`2`) client programs also inherit the launching terminal's
stdout/stderr instead of being silenced, which makes client errors visible.
## Frame dumps (SIGUSR1 / TABBED_AUTODUMP)
The `SIGUSR1` signal and `TABBED_AUTODUMP` write raw `P6` portable-pixmap
files to `/tmp` regardless of the log level:
- `SIGUSR1` writes `/tmp/scene_N.ppm` — the composed scene (whatever
tabbed-wlroots rendered for the host).
- `SIGUSR1` also writes `/tmp/host_N.ppm` — the host compositor's back
buffer, so you can see what the host actually displayed.
- `N` is an incrementing per-run counter; a single `SIGUSR1` produces a
matching scene/host pair.
- With `TABBED_AUTODUMP` (any value, e.g. `TABBED_AUTODUMP=1`), a scene and
host dump are taken automatically once the first frame in which a client
committed a CPU-readable (shared-memory) buffer — handy for capturing
exactly what a newly launched client drew.
- At `TABBED_LOG=2` the diagnostic that detects the client's own shm buffer
additionally writes that buffer to `/tmp/client_N.ppm`.