brightnessctl drives the kernel backlight class, which laptop panels have and this desktop does not -- it reports only keyboard and NIC LEDs. So BrightnessControl removed itself and there was no way to dim the screen from Panama at all. DDC/CI is the channel the buttons on a monitor's bezel drive, and it is the only brightness an external display has. Both sources now render a row each, so a machine gets whichever it actually has, or none. Displays are enumerated from sysfs rather than `ddcutil detect`. The kernel publishes the connector-to-bus mapping as /sys/class/drm/<card>-<connector>/ddc along with whether anything is plugged in, which beats parsing detect's undocumented brief output, yields the connector name spelled exactly as Hyprland spells it, and probes only connectors with a monitor attached -- one bus on this machine rather than fourteen, where each empty bus costs a timeout. No model name is read: Hyprland already knows what every output is called, so the UI joins on the connector instead of keeping a second source of truth that could disagree with the Displays page. Writes are debounced, serial, and read back. Serial because DDC/CI has no arbitration and two ddcutil processes on one bus interleave their exchanges and both return garbage. Read back because a write is not a promise: panels clamp to their own range, ignore values while waking from standby, and drop writes that arrive too fast. Without the read the slider would show what Panama asked for rather than what the monitor did, which is the same class of lie as trusting `hyprctl keyword`. Brightness is deliberately not a stored preference. The monitor remembers it and the bezel buttons change it behind Panama's back, so persisting it would mean restoring a value the panel had moved past. The contract runs against fixtures with ddcutil stubbed and both sysfs roots redirected, so it never touches a real monitor. Its fixture reports a maximum of 200 rather than 100 on purpose -- at 100 the scaling arithmetic is the identity and a helper that ignored the reported maximum would pass everything. Verified it catches that, plus a dropped connection-status filter and an unstripped connector prefix. Not yet confirmed against hardware: this machine cannot open any I2C bus yet. ddcutil's udev rule grants that through uaccess but only to devices created after it was installed, so it needs one udevadm trigger. The helper detects exactly that case and returns the command as its error rather than reporting "no displays". Claude-Session: https://claude.ai/code/session_01BRvzt4H8XXLPVH5MyYdk9L
Panama
Formerly Sunhat. A personal config for Fedora, with the intention of helping a user set up their Fedora system with one command.
git clone https://git.gbrown.org/gib/Panama.git ~/.local/share/Panama
~/.local/share/Panama/install
install runs everything in setup/scripts/ in order:
| Script | Does |
|---|---|
install-packages |
Repos (RPM Fusion, Terra, Hyprland COPR), then the four package lists in setup/packages/ |
link-dotfiles |
Symlinks config/dot/<name> → ~/.config/<name> |
change-settings |
Copies config/copy/ over /, applies gsettings, enables user services |
Existing configs are moved to config/old/ rather than overwritten.
Desktops
Panama configures two desktops that coexist. Both sessions stay available in GDM, so you can switch back and forth while you settle in.
GNOME
The original setup: Forge for tiling, Dash-to-Dock, Openbar, Vitals,
AppIndicator support. Configured through config/dot/forge/ and gsettings.
Hyprland
A from-scratch replacement built to reproduce the GNOME setup closely enough that muscle memory transfers — same keybinds, same panel contents, same dock, same Tokyo Night Moon palette.
| Piece | What it is |
|---|---|
config/dot/hypr/ |
Compositor config. Lua, not hyprlang — see its README |
config/dot/quickshell/ |
The shell: bar, dock, Continuum overview, Panama Settings, Screen Intelligence, focus sessions, quick settings, notifications, screenshot UI |
config/dot/vicinae/ |
Raycast-style launcher, themed |
config/dot/uwsm/ |
Session environment (see the uwsm caveat in the hypr README) |
config/dot/wofi/ |
Fallback launcher, in case the shell fails to start |
config/dot/xdg-desktop-portal/ |
Portal backend routing |
Start here: config/dot/hypr/README.md — it
covers the Lua migration, the uwsm environment gotcha, the HDR decision, the
full keymap, and troubleshooting.
Log in as "Hyprland (uwsm-managed)", not plain "Hyprland".
Layout
bin/ Small user-facing commands on PATH
config/
bash/ .bashrc, aliases, env (env is gitignored)
copy/ Files copied verbatim over / (needs sudo)
dot/ Symlinked into ~/.config
old/ Backups of whatever was replaced (gitignored)
setup/
packages/ One package per line
scripts/ Run in order by ./install