Skip to content

feat: add hyprcursor theme build - #5

Open
MihkelMK wants to merge 3 commits into
ful1e5:mainfrom
MihkelMK:hyprcursor
Open

feat: add hyprcursor theme build#5
MihkelMK wants to merge 3 commits into
ful1e5:mainfrom
MihkelMK:hyprcursor

Conversation

@MihkelMK

@MihkelMK MihkelMK commented Aug 1, 2026

Copy link
Copy Markdown

Adds Hyprcursor as a third output format alongside XCursor and Windows cursors.

Hyprcursor supports vector cursors.
The build packages SVGs from svg/ as-is with resize_algorithm = none, so one
file per shape covers every size instead of 14 pre-rendered bitmaps. No bitmap
step, so no cbmp or ctgen dependency.

Approach

The script reads shape data from the existing configs/x.build.toml instead of
duplicating it into a second config, keeping both formats in sync from one source:

XCursor Hyprcursor
x_hotspot = 37 (px on the 256 canvas) hotspot_x = 0.144531 (fraction)
x11_symlinks = [...] define_override = ...
watch-*.png, 14 baked sizes define_size = 0, watch-01.svg, 20 x 60

Colors come from render.json, the same file cbmp uses, so the documented
color customization applies to Hyprcursors too.

Changes

  • scripts/gen-hyprcursor.py: generates the working state (recolored SVGs,
    meta.hl, manifest.hl) for all three variants
  • build-hyprcursor.sh: compiles with hyprcursor-util --create and packs
    bin/Notwaita-*-Hyprcursor.tar.xz
  • yarn build:hyprcursor, plus hyprcursors/ in .gitignore and clean
  • CI job building the themes and verifying the shape count
  • README install and build sections, CHANGELOG entry

Notes

  • CI runs in an archlinux:latest container. Ubuntu does not package
    hyprcursor-util, and building it from source pulls the hyprutils/hyprlang
    chain, Arch ships it in extra. The job needs neither node nor clickgen.
  • svg/crosshair.svg:19 uses #FE0000 for a 4px dot. render.json does not
    list that value, so it stays red through recoloring. The generator maps
    #FE0000 to #FF0000 (COLOR_FIXUPS). I left the SVG untouched so XCursor
    output does not change. Fixing the source is a separate task.
  • x.build.toml omits svg/person.svg and svg/pin.svg, so the XCursor theme
    skips them. The Hyprcursor build skips them too, for parity.

Testing

Built all three variants, then loaded Notwaita-Black through the
libhyprcursor C API at size 24. All 58 shapes resolve, including overrides
(default, pointer) and the legacy hash names
(00008160000006810000408080010102). Animated shapes report 60 frames at 20ms,
matching the XCursor timing. 173K per archive.

Ran the CI job end-to-end in a clean archlinux:latest container: 58 shapes
per theme, all three archives produced.

Installed and tested locally, seems to work as intended.

Hyprcursor ships SVGs directly, so the themes are generated from the
existing sources without cbmp or clickgen. Shape data (hotspots,
aliases, animation frames) is read from configs/x.build.toml so both
formats stay in sync from one config.

- hotspots converted from px on the 256 canvas to a 0.0-1.0 fraction
- x11_symlinks map to define_override, incl. legacy hash names
- crosshair's stray #FE0000 is treated as #FF0000; render.json does
  not list it, leaving the dot unrecolored
hyprcursor-util is not packaged for Ubuntu and building it from
source pulls the hyprutils/hyprlang chain, so the job runs on Arch
where pacman has it. Needs neither node nor clickgen, since the
SVG sources are packaged as-is.

Expected shape count is derived from configs/x.build.toml so adding
a cursor cannot silently pass the check.
Covers both theme layouts: the standalone Notwaita-*-Hyprcursor
directory, and merging the files into an existing XCursor theme for
setups that drive HYPRCURSOR_THEME and XCURSOR_THEME from one name.

XCURSOR_THEME stays in the example config because XWayland and apps
without hyprcursor support fall back to XCursor.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant