From 35789cc10224c27223e79097dc981d0a2a28f3e0 Mon Sep 17 00:00:00 2001 From: timbelmon Date: Wed, 29 Apr 2026 20:19:32 +0200 Subject: [PATCH] Add laptop display mirror toggle + hotkey (#5462) * Add check for active external monitors before disabling internal display Co-authored-by: Copilot * Revert "Add check for active external monitors before disabling internal display" This reverts commit e20b3da87e3df0f084c0bca232a61ba58fab4cdc. * feat(hyprland): add toggle for internal monitor mirroring - Implement `omarchy-hyprland-monitor-internal-mirror` to dynamically mirror the internal eDP display to external monitor. - Update `omarchy-hyprland-monitor-internal` to prevent disabling the laptop screen if mirroring is currently active. - Add "Mirror Display" toggle to omarchy hardware Menu * Ensure there is also a laptop monitor available to mirror before we do it * Fix toggle use * Recover from mirror mode gracefully * Add hotkey to mirroring --------- Co-authored-by: Copilot Co-authored-by: David Heinemeier Hansson --- bin/omarchy-hyprland-monitor-internal | 3 +- bin/omarchy-hyprland-monitor-internal-mirror | 55 ++++++++++++++++++++ bin/omarchy-hyprland-monitor-watch | 5 +- bin/omarchy-menu | 3 +- default/hypr/bindings/utilities.conf | 1 + 5 files changed, 63 insertions(+), 4 deletions(-) create mode 100755 bin/omarchy-hyprland-monitor-internal-mirror diff --git a/bin/omarchy-hyprland-monitor-internal b/bin/omarchy-hyprland-monitor-internal index 6936d2cf..bc5ee0b9 100755 --- a/bin/omarchy-hyprland-monitor-internal +++ b/bin/omarchy-hyprland-monitor-internal @@ -1,6 +1,7 @@ #!/bin/bash TOGGLE="internal-monitor-disable" +MIRROR_TOGGLE="internal-monitor-mirror" enable() { if omarchy-hyprland-toggle-enabled "$TOGGLE"; then @@ -10,7 +11,7 @@ enable() { disable() { if omarchy-hw-external-monitors; then - if omarchy-hyprland-toggle-disabled "$TOGGLE"; then + if omarchy-hyprland-toggle-disabled "$TOGGLE" && omarchy-hyprland-toggle-disabled "$MIRROR_TOGGLE"; then omarchy-hyprland-toggle --enabled-notification "󰍹 Laptop display disabled" "$TOGGLE" fi else diff --git a/bin/omarchy-hyprland-monitor-internal-mirror b/bin/omarchy-hyprland-monitor-internal-mirror new file mode 100755 index 00000000..f4457b2a --- /dev/null +++ b/bin/omarchy-hyprland-monitor-internal-mirror @@ -0,0 +1,55 @@ +#!/bin/bash + +TOGGLE="internal-monitor-mirror" +TOGGLE_FLAG="$HOME/.local/state/omarchy/toggles/hypr/$TOGGLE.conf" +DISABLE_TOGGLE="internal-monitor-disable" + +# Get names dynamically +INTERNAL=$(hyprctl monitors -j | jq -r '.[] | select(.name | contains("eDP")).name' | head -n 1) +# Get the first available external monitor +EXTERNAL=$(hyprctl monitors -j | jq -r '.[] | select(.name | contains("eDP") | not).name' | head -n 1) + +enable() { + if [[ -z "$EXTERNAL" ]]; then + notify-send -u low "󰍹 No external monitors found for mirror" + exit 1 + fi + + if [[ -z "$INTERNAL" ]]; then + notify-send -u low "󰍹 No laptop monitor found to mirror" + exit 1 + fi + + if omarchy-hyprland-toggle-enabled "$DISABLE_TOGGLE"; then + omarchy-hyprland-toggle "$DISABLE_TOGGLE" + fi + + if omarchy-hyprland-toggle-disabled "$TOGGLE"; then + echo "monitor=$EXTERNAL, preferred, auto, 1, mirror, $INTERNAL" > "$TOGGLE_FLAG" + notify-send -u low "󰍹 Mirroring enabled ($EXTERNAL)" + hyprctl reload + fi +} + +disable() { + if omarchy-hyprland-toggle-enabled "$TOGGLE"; then + omarchy-hyprland-toggle --disabled-notification "󰍹 Extended mode restored" "$TOGGLE" + fi +} + +recover() { + if ! omarchy-hw-external-monitors && omarchy-hyprland-toggle-enabled "$TOGGLE"; then + omarchy-hyprland-toggle "$TOGGLE" + fi +} + +case "$1" in + on) enable ;; + off) disable ;; + toggle) if omarchy-hyprland-toggle-enabled "$TOGGLE"; then disable; else enable; fi ;; + recover) recover ;; + *) + echo "Usage: $(basename "$0") {on|off|toggle|recover}" >&2 + exit 1 + ;; +esac diff --git a/bin/omarchy-hyprland-monitor-watch b/bin/omarchy-hyprland-monitor-watch index 470bf3de..ee096f25 100755 --- a/bin/omarchy-hyprland-monitor-watch +++ b/bin/omarchy-hyprland-monitor-watch @@ -1,7 +1,7 @@ #!/bin/bash -# Listen on Hyprland's event socket and recover the internal display whenever -# a monitor is removed. +# Listen on Hyprland's event socket and recover monitor toggles whenever a +# monitor is removed. SOCKET="$XDG_RUNTIME_DIR/hypr/$HYPRLAND_INSTANCE_SIGNATURE/.socket2.sock" @@ -9,6 +9,7 @@ socat -U - "UNIX-CONNECT:$SOCKET" | while read -r event; do case "$event" in monitorremoved\>\>*|monitorremovedv2\>\>*) omarchy-hyprland-monitor-internal recover + omarchy-hyprland-monitor-internal-mirror recover ;; esac done diff --git a/bin/omarchy-menu b/bin/omarchy-menu index da191b7b..260a851e 100755 --- a/bin/omarchy-menu +++ b/bin/omarchy-menu @@ -186,7 +186,7 @@ show_toggle_menu() { } show_hardware_menu() { - local options="󰛧 Laptop Display" + local options="󰛧 Laptop Display\n 󰍹 Mirror Display" if omarchy-hw-hybrid-gpu; then options="$options\n Hybrid GPU" @@ -198,6 +198,7 @@ show_hardware_menu() { case $(menu "Toggle" "$options") in *Laptop*) omarchy-hyprland-monitor-internal toggle ;; + *Mirror*) omarchy-hyprland-monitor-internal-mirror toggle;; *Touchpad*) omarchy-toggle-touchpad ;; *"Hybrid GPU"*) present_terminal omarchy-toggle-hybrid-gpu ;; *) back_to show_trigger_menu ;; diff --git a/default/hypr/bindings/utilities.conf b/default/hypr/bindings/utilities.conf index 144c6b47..09e2e4dd 100644 --- a/default/hypr/bindings/utilities.conf +++ b/default/hypr/bindings/utilities.conf @@ -30,6 +30,7 @@ bindd = SUPER SHIFT ALT, COMMA, Restore last notification, exec, makoctl restore bindd = SUPER CTRL, I, Toggle locking on idle, exec, omarchy-toggle-idle bindd = SUPER CTRL, N, Toggle nightlight, exec, omarchy-toggle-nightlight bindd = SUPER CTRL, Delete, Toggle laptop display, exec, omarchy-hyprland-monitor-internal toggle +bindd = SUPER CTRL ALT, Delete, Toggle laptop display mirroring, exec, omarchy-hyprland-monitor-internal-mirror toggle bindl = , switch:on:Lid Switch, exec, omarchy-hyprland-monitor-internal off bindl = , switch:off:Lid Switch, exec, omarchy-hyprland-monitor-internal on