A GPU terminal emulator for Wayland and Windows on libghostty-vt, drawing with Vulkan, running a program on a pty or a serial port.
harrier CONTRIBUTING.md
7.2 kB · 172 lines
Markdown
at main

Contributing to harrier #

Thank you for wanting to help. This file covers what you need to know to get a change into harrier. The README explains what harrier is and how it is put together, and its "Design" section is worth reading before you change anything structural.

Issues and pull requests #

Report bugs and open pull requests on the Tangled mirror: https://tangled.org/jcollie.dev/harrier.

For a bug, say:

  • which compositor and GPU (or llvmpipe) you ran it on;
  • what you did, and what you expected instead;
  • the log on standard error. HARRIER_STARTUP_TIMES=1 adds timings for startup problems, --validation turns on Vulkan's validation layer for rendering problems, and AERIE_TRACE_POINTER=1 logs every pointer enter, leave and button, and every change in the seat's devices, for a drag or selection that does not end when the button is let go.

Setting up #

Everything comes from the Nix flake:

$ nix develop
$ zig build run
$ zig build test --summary all

The development shell has Zig 0.17.0, the Vulkan loader and its validation layer, REUSE, and zon2nix.

Before you send a change #

All of these must pass, and CI runs each of them:

$ nix develop -c zig fmt --exclude zig-pkg --check .
$ nix develop -c reuse lint
$ nix develop -c zig build test --summary all
$ nix develop -c zig build -Dtarget=x86_64-windows-gnu
$ nix build .#msi
$ nix flake check

CI runs the two zig build commands through tools/zig-build.sh, for example nix develop -c tools/zig-build.sh test --summary all. The script gives Zig the dependencies Nix fetched, the same ones the package build uses, so nothing is downloaded from the dependencies' own hosts. Run them that way if a dependency's host is failing.

On Windows itself, zig build test --summary all runs the tests that do not need a POSIX shell, the pseudoconsole's among them.

nix flake check builds the package and runs every NixOS VM test. A single VM test can be run on its own, for example nix build .#checks.x86_64-linux.sway. The VM tests need KVM. The shortcuts test boots KDE Plasma and needs a guest with 4 GB of memory.

Tests #

  • Logic goes under unit tests. Keep code that can be tested without a window apart from the Wayland and Vulkan code: layout, hit-testing, parsing, the placement of things on the grid. Test it in the same file.
  • What only shows on screen goes in a VM test. Add or extend a test in tests/nixos/ and register it in flake.nix's checks. If CI should run it, add a step to .forgejo/workflows/test.yaml.
    • Assert on pixels: count a color that nothing else on the screen has.
    • Assert what should not happen as well, such as nothing drawn with the feature turned off.
  • Look at what you drew. Changes to drawing are worth checking by hand in a headless sway, taking screenshots with grim. The README's "Tests" section says what has been checked that way.

Style #

  • Zig 0.17. Match the code around you: its naming, its comment density, and its doc comments, which explain why rather than restating the code.
  • American English in code, comments and documentation.
  • The README describes harrier as it is now, for a reader who has never seen it. Update it in the same change as the code: the feature list, the configuration, the design, and the tests. Leave history to the commit messages.
  • Commit messages say what changed and why, in the imperative ("Draw images sent with kitty's graphics protocol"), with a body for anything not obvious.

Dependencies #

  • Zig dependencies go in build.zig.zon. After adding, removing or updating one, regenerate the Nix expression and commit it with the change:

    $ nix develop -c tools/zon2nix.sh
    

    The script leaves out what only Ghostty's own application uses. If nix build then stops with "lazy dependency package not found", the build does ask for that package: take it out of the script's list.

    The package builds with no network access, so a dependency missing from build.zig.zon.nix fails nix build while zig build still works.

  • libghostty-vt comes from the zig-0.17-jeff branch of https://git.jcollie.dev/jeff/ghostty. Changes to the terminal itself belong there, not in harrier.

  • zig-aerie, the window system, the Vulkan renderer and its shaders, the font stack, the title bar and the desktop's settings, comes from https://git.jcollie.dev/jeff/zig-aerie, pinned in build.zig.zon. A change to any of those belongs there, not in harrier: other programs draw with it too. To try one before it is pushed, build harrier against your checkout with zig build --fork=../zig-aerie; once it is pushed, pin the new commit with zig fetch --save=aerie git+https://git.jcollie.dev/jeff/zig-aerie.git#<commit> and regenerate build.zig.zon.nix. zig-aerie and harrier must pin the same commits of the packages they share — mdbus, zig-dbus-service, zig-xkb, the font libraries — and build them with the same options, or the build stops with "file exists in modules".

  • Everything else (Vulkan, the fonts) comes from the flake.

Translations #

harrier's text is in locales/, in Fluent syntax. locales/en-US.ftl is the source: every message is there, with a comment saying what its arguments are. To add a language:

  1. Copy en-US.ftl to locales/<tag>.ftl, named by its BCP 47 tag: de, pt-BR, zh-Hant.
  2. Translate the values, never the message names or the $arguments. The variants of a select expression ([failed], *[working]) are the values the program passes; translate what follows each, and add or drop plural forms as your language needs with NUMBER() and CLDR plural categories. The Fluent Syntax Guide explains all of it.
  3. Add the tag to the list in localesModule in build.zig, after "en-US".
  4. zig build test checks that every file parses. Try it with .{ .language = "<tag>" } in a configuration, or LANGUAGE=<tag>.

A message you leave out falls back to English, so a partial translation is better than none.

When code adds text users read, it goes into en-US.ftl with a comment, and the code formats it through src/l10n.zig: l10n.text for a message with no arguments, l10n.format or l10n.write for one with them. A new message is a new name; change a message's name when its meaning changes, so that translations of the old meaning stop matching.

Licensing #

harrier is MIT licensed and follows the REUSE specification:

  • Every new file needs an SPDX header. Files that cannot carry one need an annotation in REUSE.toml.
  • Code ported from elsewhere keeps its original copyright line, above harrier's, and is listed in the README's "License" section.
  • Only bring in code under a license compatible with MIT.

References #

If your change follows a specification, a protocol or someone else's documentation, add it to the README's "References cited" section.