Skip to content
Open
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
21 changes: 21 additions & 0 deletions default/thunderbolt/boot/thunderbolt_autoauth.hook
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#!/bin/sh
# Conservative Thunderbolt authorization to avoid killing DP link at KMS time.

run_hook() {
# Give amdgpu+kms a moment to light the panel before we touch TB
sleep 2

for dev in /sys/bus/thunderbolt/devices/*; do
base="$(basename "$dev")"
# Skip domain/root (0-0) – writing there errors and can flap the bus
[ "$base" = "0-0" ] && continue

auth="$dev/authorized"
[ -f "$auth" ] || continue

cur="$(cat "$auth" 2>/dev/null || echo "?")"
if [ "$cur" = "0" ]; then
echo 1 > "$auth" 2>/dev/null
fi
done
}
7 changes: 7 additions & 0 deletions default/thunderbolt/boot/thunderbolt_autoauth.install
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#!/bin/sh
build() { add_runscript; }
help() {
cat <<'HELPEOF'
Conservative TB auth (delay, skip 0-0, auth once) to keep DP link stable for splash/LUKS.
HELPEOF
}
3 changes: 3 additions & 0 deletions default/thunderbolt/ghost-fix/99-thunderbolt-ghost-fix.rules
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Run ghost display fix when Thunderbolt devices are added
# Use systemd-run to completely detach from udev
ACTION=="add", SUBSYSTEM=="thunderbolt", RUN+="/usr/bin/systemd-run --uid=0 --gid=0 --on-active=8s /usr/local/bin/thunderbolt-ghost-fix"
49 changes: 49 additions & 0 deletions default/thunderbolt/ghost-fix/fix-ghost-displays.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
#!/bin/bash
# fix-ghost-displays.sh
# Disable phantom MST clones (e.g., Studio Display "ghost" head).
# Keep the monitor entry with MORE available modes; disable the rest.

set -euo pipefail

# Need hyprctl + jq
command -v hyprctl >/dev/null || exit 0
command -v jq >/dev/null || { echo "[ghost-fix] jq not found"; exit 0; }

# Retry a bit in case monitors aren't ready yet
# Wait for monitors to stabilize (especially for hot-plug scenarios)
MON_JSON=""
for i in {1..10}; do
MON_JSON="$(hyprctl monitors -j 2>/dev/null || true)"
# Check if we have monitors and if they've stabilized
if [ -n "$MON_JSON" ] && [ "$MON_JSON" != "[]" ]; then
# Wait a bit more to ensure all monitors are detected
sleep 2
MON_JSON="$(hyprctl monitors -j 2>/dev/null || true)"
break
fi
sleep 1
done
[ -z "$MON_JSON" ] || [ "$MON_JSON" = "[]" ] && exit 0

# Handle escaped JSON from hyprctl (when run via sudo)
MON_JSON=$(echo -e "$MON_JSON")

# Some entries might have null serial; group by serial string key
SERIALS=$(echo "$MON_JSON" | jq -r '[.[].serial // "NULL"] | unique[]')

for SERIAL in $SERIALS; do
MATCHING=$(echo "$MON_JSON" | jq -c --arg S "$SERIAL" '.[] | select((.serial // "NULL") == $S)')
COUNT=$(echo "$MATCHING" | wc -l)
if [ "$COUNT" -gt 1 ]; then
KEEP=$(echo "$MATCHING" | jq -s 'max_by(.availableModes|length)')
KEEPID=$(echo "$KEEP" | jq -r '.id')
echo "[ghost-fix] keeping id=$KEEPID serial=$SERIAL"
# Disable the rest
echo "$MATCHING" | jq -r '.name + ":" + (.id|tostring)' | while IFS=: read -r NAME ID; do
if [ "$ID" != "$KEEPID" ]; then
echo "[ghost-fix] disabling phantom $NAME (id=$ID) serial=$SERIAL"
hyprctl keyword monitor "$NAME,disable,add" >/dev/null
fi
done
fi
done
27 changes: 27 additions & 0 deletions default/thunderbolt/ghost-fix/thunderbolt-ghost-fix
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
#!/bin/bash
# Run ghost display fix for Hyprland when Thunderbolt connects

# Get Hyprland process (exit if none)
pid=$(pgrep -x Hyprland | head -n1) || exit 0

# Get user who owns the process
user=$(stat -c %U /proc/$pid) || exit 0
uid=$(id -u $user)

# Find the Hyprland socket and derive the instance signature
sock=$(find /run/user/$uid -maxdepth 4 -type s -path "*/hypr/*/.socket.sock" 2>/dev/null | head -n1)
if [ -n "$sock" ]; then
inst_dir=$(dirname "$sock")
XDG_RUNTIME_DIR="/run/user/$uid"
HYPRLAND_INSTANCE_SIGNATURE=$(basename "$inst_dir")

# Run the fix as that user with proper environment
sudo -u $user \
XDG_RUNTIME_DIR="$XDG_RUNTIME_DIR" \
HYPRLAND_INSTANCE_SIGNATURE="$HYPRLAND_INSTANCE_SIGNATURE" \
/home/$user/.config/hypr/fix-ghost-displays.sh
else
# Fallback: just try to run it anyway
[ -f /home/$user/.config/hypr/fix-ghost-displays.sh ] && \
sudo -u $user /home/$user/.config/hypr/fix-ghost-displays.sh
fi
2 changes: 2 additions & 0 deletions install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ source $OMARCHY_INSTALL/config/branding.sh
source $OMARCHY_INSTALL/config/git.sh
source $OMARCHY_INSTALL/config/gpg.sh
source $OMARCHY_INSTALL/config/timezones.sh
source $OMARCHY_INSTALL/config/thunderbolt.sh
source $OMARCHY_INSTALL/config/fix-ghost-display.sh
source $OMARCHY_INSTALL/config/increase-sudo-tries.sh
source $OMARCHY_INSTALL/config/increase-lockout-limit.sh
source $OMARCHY_INSTALL/config/ssh-flakiness.sh
Expand Down
24 changes: 24 additions & 0 deletions install/config/fix-ghost-display.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#!/bin/bash
set -e

echo "Setting up ghost display fix for MST devices..."

SCRIPT="$HOME/.config/hypr/fix-ghost-displays.sh"
AUTOSTART="$HOME/.config/hypr/autostart.conf"

# Copy the user Hyprland script
mkdir -p "$(dirname "$SCRIPT")"
cp "$OMARCHY_PATH/default/thunderbolt/ghost-fix/fix-ghost-displays.sh" "$SCRIPT"
chmod +x "$SCRIPT"

# Add to Hyprland autostart
mkdir -p "$(dirname "$AUTOSTART")"
touch "$AUTOSTART"
grep -q "fix-ghost-displays.sh" "$AUTOSTART" || echo "exec-once = ~/.config/hypr/fix-ghost-displays.sh" >> "$AUTOSTART"

# Install system components for hot-plug support
sudo cp "$OMARCHY_PATH/default/thunderbolt/ghost-fix/thunderbolt-ghost-fix" /usr/local/bin/
sudo chmod +x /usr/local/bin/thunderbolt-ghost-fix

sudo cp "$OMARCHY_PATH/default/thunderbolt/ghost-fix/99-thunderbolt-ghost-fix.rules" /etc/udev/rules.d/
sudo udevadm control --reload
53 changes: 53 additions & 0 deletions install/config/thunderbolt.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
#!/bin/bash

# Setup early boot Thunderbolt authorization for LUKS password entry
# Devices connected at boot are authorized for LUKS entry and will be
# automatically enrolled by bolt when it starts in userspace
INSTALL_DIR="/etc/initcpio/install"
HOOKS_DIR="/etc/initcpio/hooks"
CONF="/etc/mkinitcpio.conf"

sudo mkdir -p "$INSTALL_DIR" "$HOOKS_DIR"

# Add required modules to mkinitcpio.conf (insert after 'btrfs' if present)
# Only add amdgpu if AMD GPU is detected (not just AMD USB controllers, etc.)
if lspci | grep -Ei 'VGA|Display' | grep -qi 'AMD'; then
NEEDED_MODS="amdgpu xhci_hcd xhci_pci thunderbolt typec ucsi_acpi typec_displayport usbhid hid_generic"
else
NEEDED_MODS="xhci_hcd xhci_pci thunderbolt typec ucsi_acpi typec_displayport usbhid hid_generic"
fi

# Only modify if at least one needed module is not already present
NEED_PATCH=false
for m in $NEEDED_MODS; do
if ! grep -qE "^[[:space:]]*MODULES=\([^)]*\b$m\b" "$CONF"; then
NEED_PATCH=true
break
fi
done

if $NEED_PATCH; then
# Insert all needed modules immediately after 'btrfs' in the MODULES line
sudo sed -Ei \
"s/^(MODULES=\([^)]*\bbtrfs\b)([^)]*\))$/\1 $(echo $NEEDED_MODS)\2/" \
"$CONF"
fi

# Add thunderbolt_autoauth hook if not present (insert before 'encrypt')
if ! grep -q "thunderbolt_autoauth" "$CONF"; then
sudo sed -Ei \
"/^[[:space:]]*HOOKS=\(/ s/(block[[:space:]]+)(encrypt)/\1thunderbolt_autoauth \2/" \
"$CONF"
fi

# Copy initcpio install script
sudo cp "$OMARCHY_PATH/default/thunderbolt/boot/thunderbolt_autoauth.install" "$INSTALL_DIR/thunderbolt_autoauth"
sudo chmod +x "$INSTALL_DIR/thunderbolt_autoauth"

# Copy runtime hook script
sudo cp "$OMARCHY_PATH/default/thunderbolt/boot/thunderbolt_autoauth.hook" "$HOOKS_DIR/thunderbolt_autoauth"
sudo chmod +x "$HOOKS_DIR/thunderbolt_autoauth"

# TODO: Consider optimizing to run mkinitcpio -P once at end of install.sh instead of multiple times
# Currently runs in: nvidia.sh, login.sh, and here (thunderbolt.sh)
echo "Y" | sudo mkinitcpio -P
1 change: 1 addition & 0 deletions install/packages.sh
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ sudo pacman -S --noconfirm --needed \
bash-completion \
bat \
blueberry \
bolt \
brightnessctl \
btop \
cargo \
Expand Down