diff --git a/pkgbuilds/cua-hyprland-plugin/.omarchy/package.json b/pkgbuilds/cua-hyprland-plugin/.omarchy/package.json new file mode 100644 index 00000000..50588b93 --- /dev/null +++ b/pkgbuilds/cua-hyprland-plugin/.omarchy/package.json @@ -0,0 +1,6 @@ +{ + "source": "local", + "release_ring": "fast", + "channels": ["stable"], + "skip_build": true +} diff --git a/pkgbuilds/cua-hyprland-plugin/PKGBUILD b/pkgbuilds/cua-hyprland-plugin/PKGBUILD new file mode 100644 index 00000000..6f9ac12a --- /dev/null +++ b/pkgbuilds/cua-hyprland-plugin/PKGBUILD @@ -0,0 +1,146 @@ +# Separately reviewed download wrapper; original kit and source identity are unchanged. +# Normal reruns need a fresh build directory; makepkg -e reuses verified extracted trees. +# Profile kit: original source bytes and separately committed packaging tooling. +# shellcheck shell=bash disable=SC2034,SC2154 +pkgname=cua-hyprland-plugin +pkgver=0.26.1 +pkgrel=2 +pkgdesc='Cua input candidate for reviewed profile omarchy-stable-20260910' +arch=('x86_64') +url='https://github.com/trycua/cua' +license=('MIT') +depends=('hyprland=0.56.2-2' 'aquamarine=0.15.0-2' 'glibc=2.44+r24+g16be1518495f-1' 'hyprcursor=0.1.13-7' 'hyprgraphics=0.5.1-4' 'hyprlang=0.6.8-5' 'hyprutils=0.14.2-1' 'libgcc=16.2.1+r23+gd564253eb6c8-1' 'libstdc++=16.2.1+r23+gd564253eb6c8-1' 'libxkbcommon=1.13.2-1' 'wayland=1.26.0-1' 'python>=3.11' 'binutils') +makedepends=('cmake>=3.30' 'ninja' 'pkgconf' 'gcc') +options=('!strip' '!debug' '!lto') +_stem='cua-hyprland-plugin-0.26.1-cc54254464c0c9aebfd6547fe7e4a0ceaf0456d7' +_archive_sha256='47bca9e018f32f4fcfe683f91c7475c60368f3b65d318cc35c1f2de88a4ee9ab' +_kit_sha256='7beb736adfd334eed52e84070177634269e3a09f8bb25971b38606933ff4c997' +_profile_sha256='5dacea79a6a5927e59fca7b51e6c04e94fa45133ac1b8fb7f4e1f442d113eb9a' +_verifier_sha256='480f649cbd35a0ddc7f49dc0a3a44785402c9e0ec9653b0f8ea4965d52d7f900' +_cxx="${CUA_RELEASE_CXX:-/usr/bin/g++}" +_download_name='cua-hyprland-plugin-0.26.1-cc54254464c0c9aebfd6547fe7e4a0ceaf0456d7-profile-omarchy-stable-20260910-kit-1.1.0-5dacea79a6a5927e59fca7b51e6c04e94fa45133ac1b8fb7f4e1f442d113eb9a-67f89ceb47edd973aa748820eadd02ddbfb3d3e4.tar.gz' +_download_sha256='a89bfa7f6490f598719dfabdd2a5badc137e27b0764fd25e6a05e1f969433520' +source=('https://github.com/trycua/cua/releases/download/cua-hyprland-kit-v1.1.0-omarchy-stable-20260910/cua-hyprland-plugin-0.26.1-cc54254464c0c9aebfd6547fe7e4a0ceaf0456d7-profile-omarchy-stable-20260910-kit-1.1.0-5dacea79a6a5927e59fca7b51e6c04e94fa45133ac1b8fb7f4e1f442d113eb9a-67f89ceb47edd973aa748820eadd02ddbfb3d3e4.tar.gz') +noextract=("$_download_name") +sha256sums=('a89bfa7f6490f598719dfabdd2a5badc137e27b0764fd25e6a05e1f969433520') + +_verify_download() { + python3 -I - "$SRCDEST/$_download_name" "$_download_sha256" "$srcdir" "$1" <<'CUA_DOWNLOAD_PY' +import hashlib +import io +from pathlib import Path, PurePosixPath +import sys +import tarfile + +expected = {'KIT-PROVENANCE.json': '7beb736adfd334eed52e84070177634269e3a09f8bb25971b38606933ff4c997', 'PKGBUILD': 'b945a6a6eda13d0e382770edd5419485e3196041956663eb38f5183b05d30db3', 'PROFILE-PKGBUILD.in': 'c350d1b2375946cb0166893d67a1fc01344ee5e3215bbe801b4d54bb361d6bce', 'PROFILE-USAGE.md': 'c14d8e8103fccab59e558758f4249f86bce700a8723cd4ba1b97b1102e02bbd2', 'PROFILE.json': '5dacea79a6a5927e59fca7b51e6c04e94fa45133ac1b8fb7f4e1f442d113eb9a', 'SHA256SUMS': '34a2126bcfab983f171382aafac3ef218475b652f4583c58f4a04088044cb935', 'SOURCE-PROVENANCE.json': '54f514664c84e1358a435f29cd6befd5661b0b133d76997191c000b10f021a75', 'cua-hyprland-plugin-0.26.1-cc54254464c0c9aebfd6547fe7e4a0ceaf0456d7.tar.gz': '47bca9e018f32f4fcfe683f91c7475c60368f3b65d318cc35c1f2de88a4ee9ab', 'lifecycle.py': 'b18dceb8b8e05b93586ddd3a2f1d90d70ae1c36088e54fc05c89e08585290990', 'profile_bundle.py': 'ac883883814787da477c037f017939ee92c9fb5f46f373af7de1331b24f1f6da', 'profile_verify.py': '480f649cbd35a0ddc7f49dc0a3a44785402c9e0ec9653b0f8ea4965d52d7f900'} +stem = 'cua-hyprland-plugin-0.26.1-cc54254464c0c9aebfd6547fe7e4a0ceaf0456d7' + +def require(condition, message): + if not condition: + raise SystemExit(message) + +def digest(data): + return hashlib.sha256(data).hexdigest() + +archive, checksum, srcdir, mode = sys.argv[1:] +archive, srcdir = Path(archive), Path(srcdir) +require(mode in {'check', 'extract'}, 'invalid kit verification mode') +require(archive.is_file() and not archive.is_symlink(), 'outer archive must be a regular file') +data = archive.read_bytes() +require(digest(data) == checksum, 'outer archive checksum mismatch') +payload = {} +with tarfile.open(fileobj=io.BytesIO(data), mode='r:gz') as contents: + for member in contents: + require(member.isfile() and not member.issparse() and not member.pax_headers, + 'nonregular outer kit member') + require(member.name in expected and member.name not in payload, 'outer kit inventory mismatch') + content = contents.extractfile(member).read() + require(digest(content) == expected[member.name], 'outer kit member checksum mismatch') + payload[member.name] = content +require(payload.keys() == expected.keys(), 'outer kit inventory mismatch') +require(srcdir.is_dir() and not srcdir.is_symlink(), 'srcdir must be a real directory') +kit, source = srcdir / 'cua-profile-kit', srcdir / stem +if mode == 'extract': + require(not kit.exists() and not kit.is_symlink() and not source.exists() and not source.is_symlink(), + 'prepare requires fresh kit and source destinations; use a clean srcdir') + source_payload = {} + with tarfile.open(fileobj=io.BytesIO(payload[stem + '.tar.gz']), mode='r:gz') as contents: + for member in contents: + require(member.isfile() and not member.issparse() and not member.pax_headers and + member.name.startswith(stem + '/'), 'invalid source member') + name = member.name[len(stem) + 1:] + path = PurePosixPath(name) + require(name and path.as_posix() == name and not path.is_absolute() and + '..' not in path.parts and '\\' not in name and name not in source_payload, + 'unsafe or duplicate source path') + source_payload[name] = contents.extractfile(member).read() + kit.mkdir() + source.mkdir() + for name, content in payload.items(): + (kit / name).write_bytes(content) + for name, content in source_payload.items(): + destination = source / name + destination.parent.mkdir(parents=True, exist_ok=True) + destination.write_bytes(content) +require(kit.is_dir() and not kit.is_symlink(), 'kit must be a real directory') +require({path.name for path in kit.iterdir()} == expected.keys(), 'extracted kit inventory mismatch') +for name, checksum in expected.items(): + path = kit / name + require(path.is_file() and not path.is_symlink() and digest(path.read_bytes()) == checksum, + 'extracted kit checksum mismatch: ' + name) +CUA_DOWNLOAD_PY +} + +_verify() { + printf '%s %s\n' "$_download_sha256" "$SRCDEST/$_download_name" | sha256sum -c - || return 1 + _verify_download check || return 1 + # Explicit checks still apply to --skipinteg, --noextract and --repackage. + printf '%s %s\n' "$_archive_sha256" "$srcdir/cua-profile-kit/${_stem}.tar.gz" | sha256sum -c - || return 1 + printf '%s %s\n' "$_kit_sha256" "$srcdir/cua-profile-kit/KIT-PROVENANCE.json" | sha256sum -c - || return 1 + printf '%s %s\n' "$_profile_sha256" "$srcdir/cua-profile-kit/PROFILE.json" | sha256sum -c - || return 1 + printf '%s %s\n' "$_verifier_sha256" "$srcdir/cua-profile-kit/profile_verify.py" | sha256sum -c - || return 1 + python3 "$srcdir/cua-profile-kit/profile_verify.py" --kit "$srcdir/cua-profile-kit" --kit-sha256 "$_kit_sha256" \ + --archive "$srcdir/cua-profile-kit/${_stem}.tar.gz" --source "$srcdir/$_stem" --cxx "$_cxx" "$@" +} + +prepare() { + _verify_download extract || return 1 + _verify +} + +build() { + _verify || return 1 + cmake -S "$srcdir/$_stem" -B "$srcdir/build" -G Ninja \ + -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_COMPILER="$_cxx" \ + -DPKG_CONFIG_EXECUTABLE=/usr/bin/pkgconf -DPKG_CONFIG_ARGN= \ + -DPKG_CONFIG_USE_CMAKE_PREFIX_PATH=OFF -DCMAKE_PREFIX_PATH= \ + -DBUILD_TESTING=ON -DCUA_HYPRLAND_BUILD_PLUGIN=ON \ + -DCUA_HYPRLAND_EXPECTED_VERSION=0.56.2 \ + -DCUA_HYPRLAND_INPUT=ON -DCUA_HYPRLAND_TEST_INPUT=OFF \ + -DCUA_HYPRLAND_INPUT_TRACE=OFF -DCUA_HYPRLAND_TEST_OPERATOR_KEY= || return 1 + cmake --build "$srcdir/build" +} + +check() { + _verify || return 1 + ( + unset LD_PRELOAD FAKEROOTKEY FAKED_MODE + ctest --test-dir "$srcdir/build" --output-on-failure --no-tests=error + ) +} + +package() { + check || return 1 + _verify --build "$srcdir/build" --output "$srcdir/BUILD-PROVENANCE.json" || return 1 + install -Dm755 "$srcdir/build/cua-hyprland-plugin.so" \ + "$pkgdir/usr/lib/cua/hyprland/cua-hyprland-plugin.so" || return 1 + install -Dm644 "$srcdir/$_stem/LICENSE.md" \ + "$pkgdir/usr/share/licenses/$pkgname/LICENSE" || return 1 + install -Dm644 "$srcdir/$_stem/SOURCE-PROVENANCE.json" \ + "$pkgdir/usr/share/$pkgname/SOURCE-PROVENANCE.json" || return 1 + local name + install -Dm644 "$srcdir/BUILD-PROVENANCE.json" "$pkgdir/usr/share/$pkgname/BUILD-PROVENANCE.json" || return 1 + for name in KIT-PROVENANCE.json PROFILE.json profile_verify.py; do + install -Dm644 "$srcdir/cua-profile-kit/$name" "$pkgdir/usr/share/$pkgname/$name" || return 1 + done +} diff --git a/pkgbuilds/cua-hyprland-plugin/README.md b/pkgbuilds/cua-hyprland-plugin/README.md new file mode 100644 index 00000000..561dcde0 --- /dev/null +++ b/pkgbuilds/cua-hyprland-plugin/README.md @@ -0,0 +1,274 @@ +# Optional Cua Hyprland plugin + +This package targets **Omarchy stable x86_64**, with Inkscape `1.4.4-6` and +two independent background-input lanes. Cua's native qualification is recorded +in [the kit's qualification record](https://github.com/trycua/cua/releases/download/cua-hyprland-kit-v1.1.0-omarchy-stable-20260910/QUALIFICATION.md) +and [Cua #3698](https://github.com/trycua/cua/pull/3698). Omabot replay and +Omarchy's explicit merge, signing, and publication decisions remain required. +Keep `skip_build: true` until those gates pass. + +The plugin is optional. Cua Driver works independently, and installation does +not load the plugin or enable input. Metadata restricts the initial destination +to `channels: ["stable"]`; `release_ring: fast` permits a native package build. +Edge, RC, and ARM publication are outside this initial scope. + +## Source and build profile + +The package uses the [Driver 0.26.1 plugin source](https://github.com/trycua/cua/releases/tag/cua-driver-rs-v0.26.1), +including the [desktop-fault cleanup repair](https://github.com/trycua/cua/pull/3702). +It is not a repackaging of the unmodified 0.24.0 plugin. The Driver client +intended for Omarchy publication is `cua-driver-bin 0.27.0-1`, with input +protocol v3. Driver 0.27.0 contains the bounded stale-geometry retry validated +with this pinned module; its production plugin source is unchanged from the +source used here. Discovery protocol v2 is separate. Omarchy must still build, +replay, sign, and publish that exact package pairing before rollout. + +Profile `omarchy-stable-20260910`, kit `1.1.0`, and package release `2` pin: + +- Hyprland `0.56.2-2`, headers `0.56.2`, and measured executable/header hashes. +- GCC `16.2.1 20260810`, including compiler bytes and emitted ELF identity. +- Shared runtime `libstdc++.so.6.0.36`, its bytes, and exact ABI package versions. + +The generated `PKGBUILD` identifies the immutable kit download, outer checksum, +and member checksums. The kit records the full source and tooling revisions, +profile digest, and source archive/manifest digests. Do not infer compatibility +from a matching version label or substitute an unreviewed profile. + +The download wrapper verifies its complete inventory before executing downloaded +tooling. It preserves the source archive and its historical embedded verifier, +but explicitly uses the new kit's `profile_verify.py`. Source integrity, +package-owned headers, pkg-config selection, compiler probes, runtime equality, +and production flags remain mandatory. Packaging runs all bundled CTests even +with `--nocheck` or `--repackage`; `--skipinteg` does not bypass recipe checks. +Production input is built in; experimental signed input and tracing are off. + +## What is qualified + +The initial app scope is native Wayland Inkscape `1.4.4-6` with the canonical +US keymap. Two lanes require independent Driver processes and distinct native +application clients, not merely two windows. This is concurrency inside one +desktop account, not multi-user or mutually untrusted-agent isolation. + +Cua's retained evidence covers background application effects, two-lane overlap, +third-owner refusal, primary-input preservation, conflicts, stale targets and +geometry, cancellation, desktop faults, recovery, and cold package transitions. +Production-package app checks and independent primary observers are separate +from trace-enabled diagnostics. Cua's retained canonical run +`433ce968ee164d5e8e3226e800db93a6` recorded all 128 required cells: 87 +deliveries and 41 expected refusals, with no failures or skips; its completion +report has SHA-256 +`1eda4cc008ea6b27d96c21d58f8041834398a43409642376bafe84ad38f0e112`. +That historical run used source-built released Driver 0.24.0; earlier real-app +checks separately used the then-published Omarchy `cua-driver-bin 0.24.0-1` +executable. A separate later Omabot replay reported 124 passes and four +failures, plus seven incomplete native cases. Cua subsequently passed those +four cells with the repair candidate shipped in Driver 0.27.0 and passed all +seven lifecycle cases. A final exact-release Fleet replay then passed all four +cells with Driver and harness source `082de4344b731ae4738ddc6a6f13f21bb3c49a85`, +released Driver binary SHA-256 +`bb1b65394e912246220f9f758c9efbbf6260cec16e3562e62a361fa95329377f`, +and evidence SHA-256 +`b6c47278a3db398ecbb4d7aed2bce44a467e2af37e6d4ccfc236d1e07aa70af7`. +Omarchy replay of the intended 0.27.0 package pairing remains an explicit +merge gate. See the linked qualification record and PR description for exact +artifacts and observation limits. + +Duplicate motion notifications are retained and counted. They are acceptable +only when pointer identity, coordinates, focus, held input, and foreground +interaction remain unchanged. Actual motion—including moving away and back— +fails isolation. After cancellation, an inert agent pointer may remain parked +if held input is released and authority is revoked. + +Current LibreOffice Calc `26.8`, Chromium/Electron raw background input, +XWayland, Unicode/IME, non-US layouts, and modified pointer gestures are outside +this profile. The plugin does not widen Driver's application admission. +Foreground input, capture, and accessibility have separate contracts; a +background refusal never authorizes a hidden foreground fallback or unlock. + +## Omabot replay before merge + +Use the unsigned, explicitly scoped build command: + +```sh +bin/repo build --package cua-hyprland-plugin --arch x86_64 --mirror stable +``` + +In a fresh worker matching the reviewed profile: + +1. Verify the downloaded kit and source identities against the reviewed recipe. + Record the actual channel snapshot, Driver, compiler, compositor, runtime, + applications, keymap, and resulting package/module hashes. +2. Require all bundled tests and native compatibility checks. Do not weaken + exact dependencies or replace the compositor to make the build pass. +3. Install through pacman and activate in a fresh session. Replay the declared + app, two-lane, refusal, primary-input, cancellation, and fault/recovery checks + against the actual packaged Driver and module. A Cua Fleet result is not an + Omabot result; matching source alone does not certify different binaries. +4. Verify restart-based upgrade, rollback, removal, and reinstallation. Retain + evidence that binds each result to the package and mapped module bytes. + +Portable tests, screenshots, health reports, and a successful build do not +replace native qualification. Recheck the published Driver package before +rollout and qualify any changed pairing explicitly. + +## Activation, updates, and removal + +The package installs the module at +`/usr/lib/cua/hyprland/cua-hyprland-plugin.so` and provenance plus the consumer +verifier under `/usr/share/cua-hyprland-plugin/`. There are no hooks, autoloading, +configuration edits, or hot replacement. + +Save your work and exit Hyprland before installing, replacing, or removing the +package. Install the exact reviewed package from a text console, then start a +fresh session. Before loading, run the consumer check with the independently +reviewed kit-provenance digest from the qualification record: + +```sh +python3 /usr/share/cua-hyprland-plugin/profile_verify.py \ + --kit /usr/share/cua-hyprland-plugin \ + --kit-sha256 7beb736adfd334eed52e84070177634269e3a09f8bb25971b38606933ff4c997 \ + --consumer /usr/lib/cua/hyprland/cua-hyprland-plugin.so +``` + +This check requires Python 3.11+, binutils `readelf`, and system `ldd`/`pacman`, +not a compiler or headers. If it fails, leave the plugin unloaded. It verifies +installed compatibility, not runtime mapping or input effects. + +After that check passes in the fresh session, load the module explicitly: + +```sh +hyprctl plugin load /usr/lib/cua/hyprland/cua-hyprland-plugin.so +hyprctl -j cua:status +``` + +Loading alone does not enable input. Before opting in, save open work and save +the exact current personal input configuration. The backup command refuses a +symlinked input file and refuses to replace an earlier backup: + +```sh +test -f "$HOME/.config/hypr/input.lua" && \ + test ! -L "$HOME/.config/hypr/input.lua" && \ + test ! -e "$HOME/.config/hypr/input.lua.cua-before" && \ + cp --archive -- "$HOME/.config/hypr/input.lua" \ + "$HOME/.config/hypr/input.lua.cua-before" +``` + +If `input.lua` is a symlink, stop here: back up and later restore its resolved +target explicitly instead of using the commands below. + +The background-input admission guard requires the exact XKB keymap +`rules=evdev`, `model=pc105`, `layout=us`, empty variant and options, and no +custom keymap file. Stock Omarchy 4.0.3 English (US) is not that literal +configuration: it leaves rules and model empty and sets +`compose:caps,shift:both_capslock_cancel`. Those options make Caps Lock the +Compose key and both Shift keys the Caps Lock/cancel chord. The required empty +options restore ordinary Caps Lock behavior and remove both stock shortcuts +while Cua input is enabled. Any other effective value is intentionally refused +as `unsupported_layout`; do not weaken or bypass that admission guard. + +Append this override to `~/.config/hypr/input.lua` so it follows any existing +input settings. It both selects the exact admitted keymap and enables the +trusted local transport: + +```lua +hl.config({ + input = { + kb_rules = "evdev", + kb_model = "pc105", + kb_layout = "us", + kb_variant = "", + kb_options = "", + kb_file = "", + }, + plugin = { cua = { enabled = true } }, +}) +``` + +Reload, then read back every keymap value rather than relying on the source +file alone: + +```sh +hyprctl reload +for name in kb_rules kb_model kb_layout kb_variant kb_options kb_file; do + value=$(hyprctl -j getoption "input:$name" | jq -r '.str') + printf '%s=%s\n' "$name" "$value" +done +hyprctl -j cua:status +``` + +The keymap readback must be exactly: + +```text +kb_rules=evdev +kb_model=pc105 +kb_layout=us +kb_variant= +kb_options= +kb_file= +``` + +A runtime keyword or Lua evaluation without `hyprctl reload` does not reconcile +the input sockets. Continue only when status also reports input protocol v3, +input capability, socket paths, and the expected compositor identity. Do not +disable NumLock; that was required only by a strict qualification observer, not +by the demonstrated background-input contract. + +Start Driver with `CUA_DRIVER_RS_ENABLE_WAYLAND=1`. For the activation check, +use background input in a new disposable Inkscape document to create a text +object containing `CUA activation check`. Save it under a new temporary +filename, then verify the text in both a fresh Driver snapshot and the reopened +saved SVG. Never test against an existing document, and do not automatically +replay an action with a partial or unknown outcome. + +After the check, restore the exact saved configuration and reload it: + +```sh +command mv --force -- "$HOME/.config/hypr/input.lua.cua-before" \ + "$HOME/.config/hypr/input.lua" +hyprctl reload +hyprctl -j cua:status +``` + +Confirm that the prior keymap values are back and status reports input disabled. +Retained inert agent pointers can remain until the compositor exits; disabling +input does not unload the mapped module. If you intentionally keep activation, +retain the backup until you are ready to perform this exact restoration. + +Before an incompatible desktop update, remove operator-added plugin activation +settings, save work, and exit the graphical session. From a text console, run +`sudo pacman -R cua-hyprland-plugin`, then apply the normal desktop update and +verify a fresh session without the plugin. Declining removal preserves the +dependency refusal. Disabling input alone leaves exact dependencies installed; +do not force an upgrade past them. + +Retain the previous package with its matching compositor, runtime, Driver, and +provenance as a rollback set. Restore a consistent set outside the graphical +session, then repeat the fresh-session consumer and app checks. Do not hot +unload/reload or replace a mapped module. + +## Ownership and publication + +The [agreed ownership split](https://github.com/omacom/omarchy-pkgs/pull/346#issuecomment-5612834061) +assigns profiles, build kits, plugin fixes, and native input evidence to Cua. +Francesco (@f-trycua) is the Cua contact through this PR. Omarchy owns package +integration, dependency-change detection, Omabot validation, and signing and +publication decisions. Spencer (@spencerbull) and Emir (@emirb) jointly own +that Omarchy package and release path. Maintenance is best effort, with no +turnaround commitment. + +Edge detects upcoming incompatibilities; RC validates the intended stable +environment. Mirror/channel changes and changes to ABI dependencies, Driver, +or admitted apps request a new candidate and affected qualification. They do +not establish compatibility or authorize additional publication channels. + +`skip_build` controls selection, not publication authority. This package has no +upstream polling, AUR synchronization, or automatic rebuild bump. After replay +and explicit merge approval, the named Omarchy owner must deliberately sign +and publish the validated bytes. `bin/repo release` rebuilds before publication; +`push` and `upload-prebuilt` also publish. None supplies native qualification. +Do not silently substitute newly rebuilt bytes during signing/publication. + +Finally, install the signed published package on a fresh consumer, verify its +signature and package/module digests, and perform a short activation, +background-action, and cleanup smoke. Broader channels or unattended publishing +require an enforced artifact-to-evidence gate, including prebuilt uploads.