Build the Panama Hyprland desktop
This commit is contained in:
@@ -0,0 +1,250 @@
|
||||
# Panama :: Hyprland
|
||||
|
||||
A Hyprland desktop built to reproduce the GNOME + Forge setup it replaces, so
|
||||
that muscle memory transfers unchanged.
|
||||
|
||||
## The one thing to know first
|
||||
|
||||
**Hyprland 0.55 replaced hyprlang with Lua.** `hyprland.conf` still loads on
|
||||
0.56 as a legacy fallback and is removed in 0.57. Everything here is Lua.
|
||||
|
||||
The authoritative option reference for the installed build is on disk:
|
||||
|
||||
| File | What it is |
|
||||
|---|---|
|
||||
| `/usr/share/hypr/stubs/hl.meta.lua` | Generated type stub — the exact option list for this build |
|
||||
| `/usr/share/hypr/hyprland.lua` | Upstream example config |
|
||||
|
||||
Wire the stub into your editor with `.luarc.json` in this directory for
|
||||
autocomplete.
|
||||
|
||||
**The Hypr ecosystem tools did NOT move to Lua.** `hyprlock.conf`,
|
||||
`hypridle.conf`, `hyprpaper.conf` and `hyprtoolkit.conf` are still hyprlang.
|
||||
Don't "fix" them.
|
||||
|
||||
## Layout
|
||||
|
||||
| File | Contents |
|
||||
|---|---|
|
||||
| `hyprland.lua` | Entry point. Each `require()` is its own error scope |
|
||||
| `env.lua` | Environment. Note the uwsm caveat below |
|
||||
| `monitors.lua` | DP-2 geometry, scaling, and the HDR decision |
|
||||
| `looks.lua` | Colours, blur, glow, shadows, animations, VRR, scanout |
|
||||
| `input.lua` | Keyboard/mouse. Click-to-focus, like GNOME |
|
||||
| `rules.lua` | Window rules, gaming rules, layer rules for the shell |
|
||||
| `keybinds.lua` | The full keymap |
|
||||
| `autostart.lua` | Session startup |
|
||||
| `overrides.lua` | Per-machine escape hatch, loaded last |
|
||||
| `hyprlock.conf` / `hypridle.conf` / `hyprpaper.conf` / `hyprtoolkit.conf` | Ecosystem tools (hyprlang) |
|
||||
|
||||
Validate any change without leaving your session:
|
||||
|
||||
```sh
|
||||
Hyprland --verify-config
|
||||
```
|
||||
|
||||
## The look
|
||||
|
||||
Tokyo Night Moon, with two accents that come from the tmux theme:
|
||||
|
||||
| Token | Value | Role |
|
||||
|---|---|---|
|
||||
| `accent` | `#82aaff` | Carries every state meaning — focused, active, on |
|
||||
| `accentSecondary` | `#b172b0` | Never used alone; only the far end of a gradient |
|
||||
|
||||
The signature is **the prism**: the two accents meeting. It appears in exactly
|
||||
four places, and nowhere else —
|
||||
|
||||
1. A one-pixel hairline along the top edge of every glass surface (the bar,
|
||||
dock, popovers), running blue on the left to orchid on the right and fading
|
||||
out before the corners. See `quickshell/widgets/PrismEdge.qml`.
|
||||
2. The focused window's border — `general.col.active_border`, blue→orchid at
|
||||
115°. Unfocused windows get no colour at all, because the gradient only
|
||||
means something if one window on screen is wearing it.
|
||||
3. The active workspace pill in the bar.
|
||||
4. Slider fills.
|
||||
|
||||
The restraint is the point. The pink stops being special the moment it's used
|
||||
as a flat fill, so it never is.
|
||||
|
||||
### Typography
|
||||
|
||||
**Adwaita Sans for everything the user reads as text — no monospace in the UI.**
|
||||
A monospaced clock or percentage reads as terminal output pasted into a panel,
|
||||
which is the opposite of the intent.
|
||||
|
||||
The Nerd Font is still used, but only to draw **icon glyphs** — it is the
|
||||
pragmatic alternative to freedesktop symbolic icons, which ship with a
|
||||
hardcoded `#2e3436` fill that Qt (unlike GTK) will not recolour. Where a
|
||||
themed freedesktop icon is wanted instead, `quickshell/widgets/ThemedIcon.qml`
|
||||
paints a palette colour through the icon's alpha.
|
||||
|
||||
Anything whose digits change in place — the clock, the vitals percentages, the
|
||||
recording timer, the selection readout — sets `font.features:
|
||||
Theme.tabularFigures`. Tabular figures share one advance width, so the text
|
||||
stops twitching as numbers tick without reaching for a monospaced face.
|
||||
|
||||
Window rounding is 18, matching the shell's popover radius, so a window and a
|
||||
panel next to each other read as the same family of object.
|
||||
|
||||
**On animated gradient borders:** Hyprland can rotate the border gradient
|
||||
continuously with `borderangle` + `style = "loop"`. Don't. The wiki is explicit
|
||||
that it forces a full-refresh-rate repaint forever, even when no border is
|
||||
visible — at 4500×3000 that is pure idle GPU burn. This config uses
|
||||
`style = "once"`, so the gradient sweeps into place when a window takes focus
|
||||
and then costs nothing.
|
||||
|
||||
## Session: use the uwsm one
|
||||
|
||||
Log in as **"Hyprland (uwsm-managed)"**, not plain "Hyprland".
|
||||
|
||||
`xdg-desktop-portal-hyprland`'s systemd unit requires `graphical-session.target`,
|
||||
and Fedora ships no `hyprland-session.target`. Without uwsm that target never
|
||||
activates and **screen sharing in OBS, Sunshine and Zoom silently fails.**
|
||||
|
||||
### The uwsm environment gotcha
|
||||
|
||||
`hl.env()` in `env.lua` exports into the *compositor's* environment, **not** the
|
||||
systemd user manager. Anything started as a user unit — `vicinae.service`,
|
||||
`hyprpaper.service`, `hyprpolkitagent.service`, `hypridle.service` — and every
|
||||
app those launch will not see it.
|
||||
|
||||
That is why `~/.config/uwsm/env` and `~/.config/uwsm/env-hyprland` exist and
|
||||
duplicate the important variables. Keep them in sync with `env.lua`; `env.lua`
|
||||
remains the fallback for the plain session.
|
||||
|
||||
## HDR — read this before turning it on
|
||||
|
||||
GNOME ran this panel in bt2100/HDR. This config deliberately does not.
|
||||
|
||||
On Hyprland 0.56.x, `cm = "hdr"` currently breaks **screencopy**: `grim`
|
||||
screenshots come back empty, and OBS/Sunshine capture and hyprlock's blurred
|
||||
background go with them. Root cause is still open upstream.
|
||||
|
||||
So the desktop runs SDR at 10-bit (`cm = "auto"`) and HDR is handed to
|
||||
fullscreen games only, via `render.cm_auto_hdr = 1` in `looks.lua`. Games get
|
||||
HDR; screenshots keep working.
|
||||
|
||||
To try full-time HDR anyway, `overrides.lua` has the block and the ordered list
|
||||
of workarounds to reach for when things break.
|
||||
|
||||
## Keymap
|
||||
|
||||
The mental model is unchanged from Forge:
|
||||
|
||||
- **SUPER** acts on windows
|
||||
- **ALT** acts on workspaces
|
||||
- **SUPER + CTRL** changes layout structure
|
||||
|
||||
### Windows
|
||||
| Key | Action |
|
||||
|---|---|
|
||||
| `SUPER + H/J/K/L` (or arrows) | Focus |
|
||||
| `SUPER + SHIFT + H/J/K/L` | Move window |
|
||||
| `SUPER + CTRL + H/J/K/L` | Swap window |
|
||||
| `SUPER + SHIFT + Y/O` · `B/M` | Wider · narrower |
|
||||
| `SUPER + SHIFT + I/U` · `P/N` | Taller · shorter |
|
||||
| `SUPER + [` / `]` / `=` | Shrink / expand / reset split |
|
||||
| `SUPER + Q` | Close |
|
||||
| `SUPER + U` | Fullscreen |
|
||||
| `SUPER + CTRL + C` | Toggle float |
|
||||
| `SUPER + CTRL + SHIFT + C` | Pin (nearest thing to Forge's "always float") |
|
||||
| `SUPER + CTRL + G` / `Z` / `V` | Toggle split / preselect right / preselect down |
|
||||
| `SUPER + Tab` / `SHIFT + Tab` | Cycle windows |
|
||||
| `SUPER + SHIFT + grave` | Last window |
|
||||
| `SUPER + X` / `SUPER + SHIFT + X` | Restore scratchpad / minimise to scratchpad |
|
||||
|
||||
### Workspaces (dynamic, like GNOME)
|
||||
| Key | Action |
|
||||
|---|---|
|
||||
| `ALT + H` / `ALT + L` | Previous / next workspace |
|
||||
| `ALT + SHIFT + H/L` | Move window to previous / next |
|
||||
| `ALT + 1..9`, `ALT + 0` | Jump to workspace |
|
||||
| `ALT + SHIFT + 1..9` | Send window to workspace |
|
||||
| `SUPER + scroll` | Change workspace |
|
||||
|
||||
### Launcher and shell
|
||||
| Key | Action |
|
||||
|---|---|
|
||||
| `SUPER + A` / `R` / `Space` | Launcher (vicinae) — all three, pick your favourite |
|
||||
| `SUPER + SHIFT + R` | Fallback launcher (wofi) if the shell is broken |
|
||||
| `SUPER + V` | Clipboard history |
|
||||
| `SUPER + .` | Emoji picker |
|
||||
| `SUPER + S` | Quick settings |
|
||||
| `SUPER + I` | Panama Settings |
|
||||
| `SUPER + SHIFT + F` | Start or reveal focus session |
|
||||
| `SUPER + B` | Notification centre |
|
||||
| `SUPER + grave` | Workspace overview |
|
||||
| `Print` | Screenshot / record picker |
|
||||
| `SHIFT` / `ALT + Print` | Screenshot screen / window immediately |
|
||||
| `SUPER + SHIFT + S` | Screen Intelligence — read text and codes from a selection |
|
||||
| `SUPER + SHIFT + P` | Colour picker |
|
||||
| `CTRL + ALT + L` | Lock (SUPER+L is "focus right") |
|
||||
| `CTRL + ALT + Delete` | Power menu |
|
||||
|
||||
### Apps
|
||||
`SUPER + T` terminal · `N` neovim · `W` browser · `F` files · `C` calculator ·
|
||||
`E` mail · `I` Panama Settings · `CTRL + SHIFT + Esc` system monitor. GNOME
|
||||
Settings remains searchable in Vicinae for hardware and account panels.
|
||||
|
||||
## Screen Intelligence
|
||||
|
||||
`SUPER + SHIFT + S` opens the capture picker directly in Selection + Read
|
||||
mode. Read is also available beside Screenshot and Record under `Print`, and
|
||||
works on a whole display, a window, or a region. Tesseract recognizes English
|
||||
text locally; ZBar recognizes QR codes and barcodes. The result sheet can copy
|
||||
text, search it, translate it, or open a detected web address. No pixels leave
|
||||
the workstation unless one of those explicit network actions is selected.
|
||||
|
||||
## Deliberate deviations from GNOME
|
||||
|
||||
These are the places a 1:1 port was impossible, and what was done instead:
|
||||
|
||||
- **Per-edge resize.** Forge resized one named edge; Hyprland resizes along an
|
||||
axis and lets the layout pick the edge. The eight Forge keys collapse to four
|
||||
behaviours, keeping the horizontal/vertical and grow/shrink pairing.
|
||||
- **Overview on `SUPER + grave`, not a bare SUPER tap.** Tap-detection on a
|
||||
modifier misfires when you're quick with SUPER combos.
|
||||
- **Lock on `CTRL + ALT + L`.** `SUPER + L` is "focus right" in this keymap.
|
||||
- **`SUPER + grave` was Forge's "cycle windows of same app"**, which Hyprland
|
||||
has no equivalent for. "Last window" moved to `SUPER + SHIFT + grave`.
|
||||
- **`gnome-control-center` is launched with `XDG_CURRENT_DESKTOP=GNOME`**,
|
||||
because it hard-refuses to start otherwise. Panels backed by system services
|
||||
work; panels backed by GNOME Shell (Displays, Keyboard Shortcuts,
|
||||
Multitasking, Appearance) are inert — Hyprland owns those now.
|
||||
|
||||
## Gaming
|
||||
|
||||
`content = "game"` on the window rules in `rules.lua` is the keystone — it is
|
||||
what `misc.vrr = 3`, `render.direct_scanout = 2` and `cursor.no_break_fs_vrr = 2`
|
||||
key off. Games additionally get blur, animation, shadow and dim disabled, plus
|
||||
`immediate` for tearing.
|
||||
|
||||
Tearing only takes effect when the game is fullscreen and the only thing on
|
||||
screen — no bar, no notifications.
|
||||
|
||||
## Do not install a notification daemon
|
||||
|
||||
`mako`, `dunst` and `SwayNotificationCenter` all register
|
||||
`Name=org.freedesktop.Notifications` for D-Bus activation. Installing any of
|
||||
them creates a startup race against Quickshell's notification server, and
|
||||
whoever wins keeps the name. If one gets installed as a dependency,
|
||||
`systemctl --user mask <name>.service`.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
**Nothing autostarts** — check `systemctl --user status hyprland-session.target`
|
||||
and `systemctl --user show-environment | grep WAYLAND_DISPLAY`. An empty
|
||||
environment means uwsm didn't export it and units with
|
||||
`ConditionEnvironment=WAYLAND_DISPLAY` silently skip.
|
||||
|
||||
**Screen sharing missing** — `busctl --user list | grep impl.portal` should show
|
||||
`xdg-desktop-portal-hyprland`.
|
||||
|
||||
**Qt apps look wrong** — `QT_QPA_PLATFORMTHEME` must be `gtk3`. It is a single
|
||||
value, not a list: Qt splits on `:` and uses only the first token.
|
||||
|
||||
**Wrong GPU / won't start** — `AQ_DRM_DEVICES` points at
|
||||
`/dev/dri/amd-dgpu`, created by `config/copy/etc/udev/rules.d/99-panama-gpu.rules`.
|
||||
Both `env.lua` and `uwsm/env-hyprland` guard on the symlink existing, so a
|
||||
missing rule degrades to "let aquamarine choose" rather than failing to start.
|
||||
Reference in New Issue
Block a user