Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions bin/omarchy-cmd-electron-gl-args
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#!/bin/bash

# omarchy:summary=Print Chromium/Electron flags for software GL when no render GPU is present
# omarchy:hidden=true

if omarchy-hw-render-gpu; then
exit 0
fi

printf '%s\n' --ozone-platform=wayland --disable-gpu
62 changes: 62 additions & 0 deletions bin/omarchy-cmd-electron-gl-wrap
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
#!/bin/bash

# omarchy:summary=Install a PATH wrapper that adds software GL flags when no render GPU is present
# omarchy:args=<command-name> <real-binary>
# omarchy:examples=omarchy-cmd-electron-gl-wrap 1password /opt/1Password/1password | omarchy-cmd-electron-gl-wrap chromium /usr/bin/chromium
# omarchy:hidden=true
# omarchy:requires-sudo=true

set -euo pipefail

if (($# != 2)); then
echo "Usage: omarchy-cmd-electron-gl-wrap <command-name> <real-binary>" >&2
exit 2
fi

name=$1
real=$2
bind_dir=${OMARCHY_ELECTRON_GL_BIND_DIR:-/usr/local/bin}
dest=$bind_dir/$name

if [[ ! -x $real ]]; then
echo "omarchy-cmd-electron-gl-wrap: not executable: $real" >&2
exit 1
fi

if [[ $dest -ef $real ]]; then
echo "omarchy-cmd-electron-gl-wrap: refusing to wrap a binary over itself: $dest" >&2
exit 1
fi

as_root() {
if ((EUID == 0)) || [[ -d $bind_dir && -w $bind_dir ]]; then
"$@"
else
sudo "$@"
fi
}

quoted_real=$(printf '%q' "$real")
wrapper=$(cat <<EOF
#!/bin/bash
# omarchy-electron-gl-wrapper
real=$quoted_real
extra=()
if omarchy-cmd-present omarchy-cmd-electron-gl-args; then
while IFS= read -r flag; do
[[ -n \$flag ]] && extra+=("\$flag")
done < <(omarchy-cmd-electron-gl-args)
fi
exec "\$real" "\${extra[@]}" "\$@"
EOF
)

if [[ -f $dest ]] && grep -q '^# omarchy-electron-gl-wrapper$' "$dest" &&
grep -Fq "real=$quoted_real" "$dest"; then
as_root chmod 755 "$dest"
exit 0
fi

as_root mkdir -p "$bind_dir"
as_root tee "$dest" >/dev/null <<<"$wrapper"
as_root chmod 755 "$dest"
15 changes: 15 additions & 0 deletions bin/omarchy-hw-render-gpu
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#!/bin/bash

# omarchy:summary=Detect whether a DRM render GPU is present
# omarchy:hidden=true

dri=${OMARCHY_DRI_PATH:-/dev/dri}

shopt -s nullglob
for node in "$dri"/renderD*; do
if [[ -e $node ]]; then
exit 0
fi
done

exit 1
29 changes: 23 additions & 6 deletions bin/omarchy-install-1password
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,36 @@ set -euo pipefail

VERSION="8.12.0"
ARCH_SUFFIX="arm64"
INSTALL_DIR="/opt/1Password"
BIN_LINK="/usr/local/bin/1password"
INSTALL_DIR="${OMARCHY_1PASSWORD_INSTALL_DIR:-/opt/1Password}"
BIN_LINK="${OMARCHY_1PASSWORD_BIN_LINK:-/usr/local/bin/1password}"

# Only run on aarch64
if [ "$(uname -m)" != "aarch64" ]; then
echo "Skipping: 1Password aarch64 installer only runs on aarch64 architecture"
exit 0
fi

link_1password() {
if [[ -x ${INSTALL_DIR}/1password ]]; then
if omarchy-cmd-present omarchy-cmd-electron-gl-wrap; then
OMARCHY_ELECTRON_GL_BIND_DIR=$(dirname "$BIN_LINK") \
omarchy-cmd-electron-gl-wrap "$(basename "$BIN_LINK")" "${INSTALL_DIR}/1password"
else
sudo ln -sf "${INSTALL_DIR}/1password" "${BIN_LINK}"
fi
fi

desktop=/usr/share/applications/1password.desktop
if [[ -f $desktop ]] && ! grep -q "^Exec=${BIN_LINK}" "$desktop"; then
sudo sed -i "s|^Exec=.*|Exec=${BIN_LINK} %U|" "$desktop"
fi
}

# Check if already installed
if [ -f "${BIN_LINK}" ] && [ -d "${INSTALL_DIR}" ]; then
INSTALLED_VERSION=$("${BIN_LINK}" --version 2>/dev/null || echo "unknown")
echo "1Password already installed (version: ${INSTALLED_VERSION})"
link_1password
exit 0
fi

Expand Down Expand Up @@ -48,15 +65,15 @@ sudo mv "1password-${VERSION}.${ARCH_SUFFIX}" "${INSTALL_DIR}"
sudo chown -R root:root "${INSTALL_DIR}"
sudo chmod 4755 "${INSTALL_DIR}/chrome-sandbox"

# Create symlink
echo "Creating symlink..."
sudo ln -sf "${INSTALL_DIR}/1password" "${BIN_LINK}"

# Install desktop file
echo "Installing desktop file..."
sudo mkdir -p /usr/share/applications
sudo cp "${INSTALL_DIR}/resources/1password.desktop" /usr/share/applications/

# PATH wrapper (not a raw symlink) so missing AGX gets --disable-gpu
echo "Creating launcher..."
link_1password

# Install icons
echo "Installing icons..."
for size in 32x32 64x64 256x256 512x512; do
Expand Down
1 change: 1 addition & 0 deletions install/user/all.sh
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ run_logged "$OMARCHY_INSTALL/user/hardware/dell/xps13-text-scaling.sh"
run_logged "$OMARCHY_INSTALL/user/hardware/fix-nouveau-cursor.sh"
run_logged "$OMARCHY_INSTALL/user/hardware/apple/share-picker.sh"
run_logged "$OMARCHY_INSTALL/user/hardware/apple/obsidian.sh"
run_logged "$OMARCHY_INSTALL/user/hardware/apple/electron-gl.sh"

run_logged "$OMARCHY_INSTALL/user/default-keyring.sh"
run_logged "$OMARCHY_INSTALL/user/mise.sh"
43 changes: 43 additions & 0 deletions install/user/hardware/apple/electron-gl.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# Chromium/Electron need a DRM render node. Apple Silicon without AGX only
# exposes the DCP scanout device, so GPU process init fails and the app can
# stay running with no window. Wrappers live on /usr/local/bin (ahead of
# /usr/bin in PATH) so upstream omarchy-launch-* scripts stay untouched.
# Flags are chosen at launch: when a render node appears, the same wrapper
# stops passing --disable-gpu.
compatible=${OMARCHY_DEVICE_TREE_COMPATIBLE:-/proc/device-tree/compatible}

if [[ -f $compatible ]] && grep -qi apple "$compatible"; then
if omarchy-cmd-present omarchy-cmd-electron-gl-wrap; then
chromium_bin=${OMARCHY_CHROMIUM_BIN:-/usr/bin/chromium}
if [[ -x $chromium_bin ]]; then
omarchy-cmd-electron-gl-wrap chromium "$chromium_bin"
chromium_desktop=${OMARCHY_CHROMIUM_DESKTOP:-/usr/share/applications/chromium.desktop}
user_chromium=$HOME/.local/share/applications/chromium.desktop
if [[ -f $chromium_desktop ]]; then
mkdir -p "$HOME/.local/share/applications"
sed 's|^Exec=/usr/bin/chromium|Exec=/usr/local/bin/chromium|' \
"$chromium_desktop" >"$user_chromium"
fi
fi

onepassword_bin=${OMARCHY_1PASSWORD_BIN:-/opt/1Password/1password}
if [[ -x $onepassword_bin ]]; then
omarchy-cmd-electron-gl-wrap 1password "$onepassword_bin"
fi
fi

looknfeel=$HOME/.config/hypr/looknfeel.lua
if ! omarchy-hw-render-gpu && [[ -f $looknfeel ]] &&
! grep -q 'no_hardware_cursors' "$looknfeel"; then
cat >>"$looknfeel" <<'EOF'

-- Apple Silicon without AGX has no DRM render node. Hardware cursors never
-- appear; draw the pointer in software like the nouveau workaround.
hl.config({
cursor = {
no_hardware_cursors = true,
},
})
EOF
fi
fi
21 changes: 21 additions & 0 deletions migrations/1788639443.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
echo "Wrap Electron apps when Apple Silicon has no render GPU"


# New installs get this from install/user/hardware/apple/electron-gl.sh.
# Existing 1Password installs used a raw /usr/local/bin symlink, so Hyprland
# launched Electron without --disable-gpu and the window never mapped.

[[ -f /proc/device-tree/compatible ]] || exit 0
grep -qi apple /proc/device-tree/compatible || exit 0

if omarchy-cmd-present omarchy-cmd-electron-gl-wrap; then
if [[ -x /usr/bin/chromium ]]; then
omarchy-cmd-electron-gl-wrap chromium /usr/bin/chromium
fi
if [[ -x /opt/1Password/1password ]]; then
omarchy-cmd-electron-gl-wrap 1password /opt/1Password/1password
fi
fi

# shellcheck disable=SC1091
source "$OMARCHY_PATH/install/user/hardware/apple/electron-gl.sh"
126 changes: 126 additions & 0 deletions test/shell.d/electron-gl-test.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
#!/bin/bash

set -euo pipefail

source "$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)/base-test.sh"

test_tmp=$(mktemp -d)
trap 'rm -rf "$test_tmp"' EXIT

dri="$test_tmp/dri"
mkdir -p "$dri"

if OMARCHY_DRI_PATH="$dri" "$ROOT/bin/omarchy-hw-render-gpu"; then
fail "render GPU is absent when dri is empty"
fi
pass "render GPU is absent when dri is empty"

touch "$dri/card1"
if OMARCHY_DRI_PATH="$dri" "$ROOT/bin/omarchy-hw-render-gpu"; then
fail "a scanout node is not a render GPU"
fi
pass "a scanout node is not a render GPU"

touch "$dri/renderD128"
OMARCHY_DRI_PATH="$dri" "$ROOT/bin/omarchy-hw-render-gpu" ||
fail "renderD128 is a render GPU"
pass "renderD128 is a render GPU"

args=$(PATH="$ROOT/bin:$PATH" OMARCHY_DRI_PATH="$dri" \
"$ROOT/bin/omarchy-cmd-electron-gl-args")
[[ -z $args ]] || fail "no Electron GL flags when a render GPU exists" "$args"
pass "no Electron GL flags when a render GPU exists"

rm -f "$dri/renderD128"
args=$(PATH="$ROOT/bin:$PATH" OMARCHY_DRI_PATH="$dri" \
"$ROOT/bin/omarchy-cmd-electron-gl-args")
[[ $args == $'--ozone-platform=wayland\n--disable-gpu' ]] ||
fail "software GL flags when no render GPU" "$args"
pass "software GL flags when no render GPU"

real="$test_tmp/real-bin"
bind="$test_tmp/bind"
mkdir -p "$bind"
printf '#!/bin/bash\nprintf "real %%s\\n" "$*"\n' >"$real"
chmod +x "$real"

PATH="$ROOT/bin:$PATH" \
OMARCHY_ELECTRON_GL_BIND_DIR="$bind" \
"$ROOT/bin/omarchy-cmd-electron-gl-wrap" demo "$real"

grep -q '^# omarchy-electron-gl-wrapper$' "$bind/demo" ||
fail "wrapper is marked as an Omarchy Electron GL wrapper"
pass "wrapper is marked as an Omarchy Electron GL wrapper"

out=$(PATH="$ROOT/bin:$PATH" OMARCHY_DRI_PATH="$dri" "$bind/demo" hello)
[[ $out == "real --ozone-platform=wayland --disable-gpu hello" ]] ||
fail "wrapper injects software GL flags" "$out"
pass "wrapper injects software GL flags"

mkdir -p "$dri"
touch "$dri/renderD128"
out=$(PATH="$ROOT/bin:$PATH" OMARCHY_DRI_PATH="$dri" "$bind/demo" hello)
[[ $out == "real hello" ]] ||
fail "wrapper is a no-op when a render GPU exists" "$out"
pass "wrapper is a no-op when a render GPU exists"

grep -Fq 'apple/electron-gl.sh' "$ROOT/install/user/all.sh" ||
fail "Apple Electron GL setup runs during user hardware setup"
pass "Apple Electron GL setup runs during user hardware setup"

grep -Fq 'omarchy-cmd-electron-gl-wrap' "$ROOT/bin/omarchy-install-1password" ||
fail "1Password aarch64 installer installs the Electron GL wrapper"
pass "1Password aarch64 installer installs the Electron GL wrapper"

grep -Fq 'exec setsid uwsm-app -- 1password' "$ROOT/bin/omarchy-launch-1password" ||
fail "1Password launcher is still the upstream uwsm-app invocation"
pass "1Password launcher is still the upstream uwsm-app invocation"

compatible="$test_tmp/compatible"
printf 'apple,j613\0apple,t8122\n' >"$compatible"
looknfeel="$test_tmp/home/.config/hypr/looknfeel.lua"
mkdir -p "$(dirname "$looknfeel")"
printf '%s\n' '-- User look and feel' >"$looknfeel"
rm -f "$dri/renderD128"

run_apple_gl() {
HOME="$test_tmp/home" \
PATH="$ROOT/bin:$PATH" \
OMARCHY_DEVICE_TREE_COMPATIBLE="$compatible" \
OMARCHY_DRI_PATH="$dri" \
OMARCHY_CHROMIUM_BIN=/dev/null/missing \
OMARCHY_1PASSWORD_BIN=/dev/null/missing \
bash -euo pipefail -c 'source "$ROOT/install/user/hardware/apple/electron-gl.sh"'
}

run_apple_gl

grep -F 'no_hardware_cursors = true' "$looknfeel" >/dev/null ||
fail "Apple Electron GL setup enables software cursors without a render GPU"
pass "Apple Electron GL setup enables software cursors without a render GPU"

run_apple_gl
(( $(grep -c 'no_hardware_cursors = true' "$looknfeel") == 1 )) ||
fail "Apple software cursor setup is idempotent"
pass "Apple software cursor setup is idempotent"

printf '%s\n' '-- User look and feel' >"$looknfeel"
touch "$dri/renderD128"
run_apple_gl
if grep -q 'no_hardware_cursors' "$looknfeel"; then
fail "Apple software cursors are skipped when a render GPU exists"
fi
pass "Apple software cursors are skipped when a render GPU exists"

printf 'intel,something\n' >"$compatible"
printf '%s\n' '-- User look and feel' >"$looknfeel"
rm -f "$dri/renderD128"
run_apple_gl
if grep -q 'no_hardware_cursors' "$looknfeel"; then
fail "Apple Electron GL setup ignores non-Apple machines"
fi
pass "Apple Electron GL setup ignores non-Apple machines"

migration=$(grep -rl 'Wrap Electron apps when Apple Silicon has no render GPU' "$ROOT/migrations" | head -n 1 || true)
[[ -n $migration ]] || fail "existing installs get the Electron GL wrapper migration"
pass "existing installs get the Electron GL wrapper migration"
Loading