darmanandClaude Opus 5 d69fe10b46
Version Bump / check-bump-label (pull_request) Skipped
Version Bump / apply-version-bump (pull_request) Successful in 20s
Add a drop shadow cast by the frame
New plugin:hyprchrome:shadow_size (px, 0 = off), shadow_color and
shadow_offset (vec2). Off by default, so existing setups are unchanged.

The silhouette is the frame's own outer boundary - extracted out of
GetBorderTexture into AppendFrameOuterPath so the shadow and the shape
casting it can't drift apart - filled into an A8 mask, blurred with three
box passes, then tinted. The frame's cutouts (side notches, the strip
beside the title bar) are part of that outline, so they cast their shape
for free.

Three things worth not undoing later:

- The window's interior is cleared after the blur, not before. Punching
  it first leaves the blur smearing shadow inward across the window's own
  content. Clearing afterwards puts the cut exactly on the ring's inner
  boundary, where the frame's opaque pixels hide it.
- The shadow is not part of the decoration's reserved extents, since
  reserving it would push neighbouring windows away by the shadow's
  width. It's drawn past the decoration's box instead, which is why
  damageEntire() and boundingBox() expand by ShadowMarginLogical().
- It renders at no more than kShadowMaxDim px on the long edge and lets
  the GPU upscale. A blurred blob loses nothing to that, and a
  full-resolution blur would otherwise be re-run per frame for the whole
  length of a resize animation.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-31 01:50:02 +02:00
2026-07-31 01:50:02 +02:00
2026-07-29 22:15:29 +00:00
2026-07-28 21:22:13 +02:00
2026-07-31 01:50:02 +02:00
2026-07-28 21:22:13 +02:00
2026-07-28 21:22:13 +02:00

hypr-chrome

A Hyprland plugin that draws a chamfered HUD-style border frame behind each window, expanded past the window's own edges, with a floating title bar hanging off the top-left corner. The frame's corners are chamfered by an amount inferred from the window's own border rounding, so the border peeking out around a rounded window's corners reads as a matching diagonal cut rather than a hard rectangular corner. Border color tracks Hyprland's own active/inactive border color live, gradients included.

Config

plugin {
    hyprchrome {
        enabled = true                        # bool
        extent = 12                           # int, px the border extends past the window edge
        follow_hyprland_border_color = true   # bool, use Hyprland's own active/inactive border color instead of active_color/inactive_color below
        active_color = 0xFFD063FF             # focused-window border color, used when follow_hyprland_border_color is false
        inactive_color = 0xFF6C6C6C           # unfocused-window border color, used when follow_hyprland_border_color is false
        titlebar_height = 8                   # int, px height of the floating title bar
        titlebar_text_size = 12               # int, px font size of the title bar's text
        titlebar_font = sans-serif            # string, passed to cairo_select_font_face
    }
}

active_color/inactive_color take the same syntax as Hyprland's own general:col.active_border — a single color, or several plus an optional angle for a gradient:

active_color = rgba(ff0000ff) rgba(00ff00ff) 45deg

Installing

hyprpm add https://git.mgaction.town/darman/hypr-chrome.git
hyprpm enable hypr-chrome

hyprpm builds the plugin from source against your own installed Hyprland, using the build commands declared in hyprpm.toml (the same cmake invocations as below).

NixOS

flake.nix exposes packages.x86_64-linux.default, built against pkgs.hyprland from this flake's own nixpkgs input (with Hyprland's own compiler/stdenv, for ABI correctness). Add this repo as a flake input and set inputs.hypr-chrome.inputs.nixpkgs.follows = "nixpkgs" (your own system's nixpkgs input) so it's built against the exact Hyprland your system runs - then add inputs.hypr-chrome.packages.${system}.default directly to your wayland.windowManager.hyprland.plugins (NixOS/home-manager) list. The package installs as lib/libhypr-chrome.so (the lib<pname>.so convention that module derives its path from automatically) even though the plain hyprctl/hyprpm path above uses the unprefixed hypr-chrome.so.

Building

Requires the Hyprland headers/libs (hyprland, libdrm, libinput, libudev, wayland-server, xkbcommon, pixman-1, cairo, etc.), ABI-locked to the exact Hyprland build you'll load the plugin into. A pinned Nix flake dev shell provides all of it:

nix develop
cmake -B build -S .
cmake --build build

This produces hypr-chrome.so at the repo root (not inside build/, so the hyprctl/buildAndLoad.sh commands below can find it directly).

Developing

Load/reload the plugin into a running Hyprland session:

hyprctl plugin load "$(pwd)/hypr-chrome.so"
hyprctl plugin unload "$(pwd)/hypr-chrome.so"

buildAndLoad.sh chains build + reload for iteration. Hyprland's plugin loader never fully unmaps a .so once dlopen'd, so reloading the same path just re-serves the old (possibly stale) mapping — the script works around this by copying each build to a uniquely-suffixed filename before loading it and cleaning up prior copies. It also unloads any copy your own Hyprland config loads (found by grepping $XDG_CONFIG_HOME/hypr for a hypr-chrome .so path), since that one is a separate dlopen that would draw a second frame on every window. It hardcodes an absolute checkout path at the top; check it matches your checkout before running it.

S
Description
No description provided
Readme
142 KiB
Languages
C++ 89.5%
Nix 5.5%
CMake 3%
Shell 2%