From 1e996609a03c1f5198524ceaaa4029cdec0d1ee1 Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Fri, 3 Jul 2026 10:13:19 -0700 Subject: [PATCH] Capture the entire screen with Return during a region pick MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A previous submap attempt was brittle because the script owned the keyboard state: if it died between enter and exit, the submap stuck. This version lets the compositor own both edges. The Lua config binds plain Return when a layer surface with slurp's "selection" namespace opens and unbinds it when the last one closes, so the bind exists exactly while a pick is on screen. slurp dying for any reason (cancel, crash, pkill, SIGKILL of the picker) fires layer.closed and the bind evaporates — there is no code path where it leaks. Return invokes omarchy-capture-region --take-fullscreen, which flags a marker in XDG_RUNTIME_DIR and dismisses slurp; the picker treats an empty slurp result with a fresh marker as the focused monitor, reusing the existing fullscreen path. Since monitor focus follows the cursor, that is the monitor under the slurp crosshair. The recording flow gets monitor:NAME from the existing match, so Return during a recording pick starts a native full-monitor capture. The signal mode no-ops unless slurp is actually running, and both the signal protocol and the layer-scoped bind lifecycle are verified live: bind present only while slurp is open, real picks resolved by the signal on both the screenshot and recording paths, no leftover binds. Co-Authored-By: Claude Fable 5 --- bin/omarchy-capture-region | 39 ++++++++++++++++++++++++++--- default/hypr/bindings/utilities.lua | 23 +++++++++++++++++ 2 files changed, 58 insertions(+), 4 deletions(-) diff --git a/bin/omarchy-capture-region b/bin/omarchy-capture-region index bf9c5526..b9b75244 100755 --- a/bin/omarchy-capture-region +++ b/bin/omarchy-capture-region @@ -20,6 +20,18 @@ # --match-monitor print "monitor:NAME" instead when the picked geometry # exactly matches a monitor +FULLSCREEN_MARKER="${XDG_RUNTIME_DIR:-/tmp}/omarchy-capture-region-fullscreen" + +# Pressing Return while slurp is open captures the entire focused monitor: +# a bind scoped to slurp's layer surface (default/hypr/bindings/utilities.lua) +# invokes this mode, which flags the intent and dismisses slurp. +if [[ ${1:-} == "--take-fullscreen" ]]; then + pgrep -x slurp >/dev/null || exit 0 + touch "$FULLSCREEN_MARKER" + pkill -x slurp + exit 0 +fi + MODE=smart KEEP_FREEZE=false MATCH_MONITOR=false @@ -52,6 +64,25 @@ get_rectangles() { hyprctl clients -j | jq -r --arg ws "$active_workspace" '.[] | select(.workspace.id == ($ws | tonumber)) | "\(.at[0]),\(.at[1]) \(.size[0])x\(.size[1])"' } +focused_monitor_geo() { + hyprctl monitors -j | jq -r "${JQ_MONITOR_GEO} .[] | select(.focused == true) | format_geo" +} + +# Runs slurp; an empty result with the fullscreen marker present means Return +# was pressed, so the focused monitor is the selection. +pick() { + local selection + rm -f "$FULLSCREEN_MARKER" + selection=$(slurp "$@" 2>/dev/null) + + if [[ -z $selection && -e $FULLSCREEN_MARKER ]]; then + rm -f "$FULLSCREEN_MARKER" + selection=$(focused_monitor_geo) + fi + + printf '%s' "$selection" +} + FREEZE_PID="" freeze_screen() { hyprpicker -r -z >/dev/null 2>&1 & @@ -68,19 +99,19 @@ trap cleanup_freeze EXIT case "$MODE" in region) freeze_screen - SELECTION=$(slurp 2>/dev/null) + SELECTION=$(pick) ;; windows) freeze_screen - SELECTION=$(get_rectangles | slurp -r 2>/dev/null) + SELECTION=$(get_rectangles | pick -r) ;; fullscreen) - SELECTION=$(hyprctl monitors -j | jq -r "${JQ_MONITOR_GEO} .[] | select(.focused == true) | format_geo") + SELECTION=$(focused_monitor_geo) ;; smart | *) RECTS=$(get_rectangles) freeze_screen - SELECTION=$(echo "$RECTS" | slurp 2>/dev/null) + SELECTION=$(echo "$RECTS" | pick) # A bare click (area < 20px^2) snaps to whichever rectangle it landed in, # so users don't end up with accidental 2px captures. X and Y can be diff --git a/default/hypr/bindings/utilities.lua b/default/hypr/bindings/utilities.lua index bb7f934e..4f83752b 100644 --- a/default/hypr/bindings/utilities.lua +++ b/default/hypr/bindings/utilities.lua @@ -41,6 +41,29 @@ o.bind("ALT + PRINT", "Screenrecording", "omarchy-capture-screenrecording --stop o.bind("SUPER + PRINT", "Color picker", "pkill hyprpicker || hyprpicker -a") o.bind("SUPER + CTRL + PRINT", "Extract text (OCR) from screenshot", "omarchy-capture-text-extraction") +-- While the slurp region picker is open, Return captures the entire focused +-- monitor. The bind lives exactly as long as a selection layer is on screen +-- (slurp opens one per monitor), so it cannot leak or get stuck. +local selection_layers = 0 + +hl.on("layer.opened", function(layer) + if layer.namespace == "selection" then + selection_layers = selection_layers + 1 + if selection_layers == 1 then + hl.bind("RETURN", hl.dsp.exec_cmd("omarchy-capture-region --take-fullscreen"), { description = "Capture entire screen" }) + end + end +end) + +hl.on("layer.closed", function(layer) + if layer.namespace == "selection" and selection_layers > 0 then + selection_layers = selection_layers - 1 + if selection_layers == 0 then + hl.unbind("RETURN") + end + end +end) + o.bind("SUPER + CTRL + S", "Share", "omarchy-menu toggle share") o.bind("SUPER + CTRL + PERIOD", "Transcode", "omarchy-transcode")