From ef022a8bdd4063f83f8d5b5b53bf89b90aef7443 Mon Sep 17 00:00:00 2001 From: Raul Benencia Date: Mon, 8 Jun 2026 12:50:33 -0300 Subject: gnome-move-windows updates --- bin/gnome-move-windows | 163 +++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 146 insertions(+), 17 deletions(-) diff --git a/bin/gnome-move-windows b/bin/gnome-move-windows index f3a5e34..4611759 100755 --- a/bin/gnome-move-windows +++ b/bin/gnome-move-windows @@ -1,46 +1,175 @@ #!/bin/sh # Move windows according to my workflow. Check bin/gnome-set-config to -# see its key-binding. Needs wmctrl. +# see its key-binding. +# +# GNOME on Wayland does not expose native windows to wmctrl. Prefer the +# Workspace Router GNOME Shell extension, which runs inside Shell and can +# see both Wayland and Xwayland windows. Fall back to wmctrl for sessions +# where the extension is unavailable. + +workspace_router_call() { + method="$1" + + command -v gdbus >/dev/null 2>&1 || return 1 + + gdbus call \ + --session \ + --dest name.rbenencia.WorkspaceRouter \ + --object-path /name/rbenencia/WorkspaceRouter \ + --method "name.rbenencia.WorkspaceRouter.$method" +} + +is_x11_session() { + [ "${XDG_SESSION_TYPE:-}" = "x11" ] || { + [ -n "${DISPLAY:-}" ] && [ -z "${WAYLAND_DISPLAY:-}" ] + } +} + +is_wayland_session() { + [ "${XDG_SESSION_TYPE:-}" = "wayland" ] || [ -n "${WAYLAND_DISPLAY:-}" ] +} + +print_diagnostics() { + if workspace_router_call ListWindows >/tmp/gnome-move-windows-router.out 2>/tmp/gnome-move-windows-router.err; then + router_status="available" + router_preview=$(sed -n '1p' /tmp/gnome-move-windows-router.out) + else + router_status="unavailable" + router_preview=$(sed -n '1p' /tmp/gnome-move-windows-router.err) + fi + + if command -v wmctrl >/dev/null 2>&1; then + wmctrl_count=$(wmctrl -l 2>/dev/null | wc -l | awk '{print $1}') + else + wmctrl_count="not-installed" + fi + + echo "session_type=${XDG_SESSION_TYPE:-unknown}" + echo "display=${DISPLAY:-unset}" + echo "wayland_display=${WAYLAND_DISPLAY:-unset}" + echo "workspace_router=${router_status}" + echo "workspace_router_preview=${router_preview:-none}" + echo "wmctrl_window_count=${wmctrl_count}" + + if is_wayland_session && [ "$router_status" != "available" ]; then + echo "diagnosis=Wayland session without Workspace Router; fallback can only manage Xwayland windows" + fi +} + +warn_wayland_fallback() { + echo "gnome-move-windows: Workspace Router is unavailable on Wayland; falling back to wmctrl, which only sees some windows" >&2 + echo "gnome-move-windows: Run 'gnome-move-windows --diagnose' and ensure the workspace-router-cli GNOME extension is enabled" >&2 +} + +case "$1" in + --list) + workspace_router_call ListWindows + exit $? + ;; + --diagnose) + print_diagnostics + exit $? + ;; +esac + +if [ -z "$GNOME_MOVE_WINDOWS_FORCE_WMCTRL" ] && ! is_x11_session; then + if workspace_router_call RouteWindows >/dev/null 2>&1; then + exit 0 + fi + + if is_wayland_session; then + warn_wayland_fallback + fi +fi + +command -v wmctrl >/dev/null 2>&1 || { + echo "gnome-move-windows: Workspace Router is unavailable and wmctrl is not installed" >&2 + exit 1 +} # Move all windows to the primary display. If they're on the secondary # display, and we try to move them to a workspace, it won't work. -for window_id in $(wmctrl -l | awk '{print $1}'); do - wmctrl -i -r $window_id -e 0,0,0,-1,-1 -done +move_windows_to_primary() { + window_ids=$(wmctrl -l | awk '{print $1}') + maximized_windows="" + fullscreen_windows="" + has_xprop=0 + + command -v xprop >/dev/null 2>&1 && has_xprop=1 + + for window_id in $window_ids; do + if [ "$has_xprop" -eq 1 ]; then + state=$(xprop -id "$window_id" _NET_WM_STATE 2>/dev/null || true) + + case "$state" in + *"_NET_WM_STATE_MAXIMIZED_VERT"*"_NET_WM_STATE_MAXIMIZED_HORZ"*|*"_NET_WM_STATE_MAXIMIZED_HORZ"*"_NET_WM_STATE_MAXIMIZED_VERT"*) + maximized_windows="$maximized_windows $window_id" + ;; + esac + + case "$state" in + *"_NET_WM_STATE_FULLSCREEN"*) + fullscreen_windows="$fullscreen_windows $window_id" + ;; + esac + fi + + wmctrl -i -r "$window_id" -b remove,fullscreen + wmctrl -i -r "$window_id" -b remove,maximized_vert,maximized_horz + done + + sleep 0.2 + + for window_id in $window_ids; do + wmctrl -i -r "$window_id" -e 0,0,0,-1,-1 + done + + sleep 0.2 + + for window_id in $maximized_windows; do + wmctrl -i -r "$window_id" -b add,maximized_vert,maximized_horz + done + + for window_id in $fullscreen_windows; do + wmctrl -i -r "$window_id" -b add,fullscreen + done +} + +move_windows_to_primary # Assign windows to predetermined workplaces -misc=$(wmctrl -l | awk '/isco|eepa/ {print $1}') -main="$(wmctrl -xl | awk '/ emacs/ {print $1}')" -communications="$(wmctrl -xl | awk 'tolower($0) ~ /(ebex|lack|communications|notmuch|outlook|elfeed|thunderbird)/ {print $1}')" -media="$(wmctrl -l | awk '/YouTube|Spotify/ {print $1}')" -terminals="$(wmctrl -l | awk '/Alacritty|kitty|terminal/ {print $1}')" +misc=$(wmctrl -xl | awk 'tolower($0) ~ /(com\\.cisco\\.secureclient|secure client|anyconnect|vpnui|keepass)/ {print $1}') +main="$(wmctrl -xl | awk 'tolower($0) ~ / emacs/ {print $1}')" +communications="$(wmctrl -xl | awk 'tolower($0) ~ /(webex|slack|communications|notmuch|outlook|elfeed|thunderbird)/ {print $1}')" +media="$(wmctrl -xl | awk 'tolower($0) ~ /(youtube|spotify)/ {print $1}')" +terminals="$(wmctrl -xl | awk 'tolower($0) ~ /(alacritty|kitty|terminal)/ {print $1}')" teleport="$(wmctrl -xl | awk 'tolower($0) ~ /teleport/ {print $1}')" -browsers="$(wmctrl -xl | awk '/irefox|hrom/ {print $1}')" +browsers="$(wmctrl -xl | awk 'tolower($0) ~ /(firefox|chrom)/ {print $1}')" for window_id in $misc; do - wmctrl -i -r $window_id -t 4 + wmctrl -i -r "$window_id" -t 4 done for window_id in $main; do - wmctrl -i -r $window_id -t 0 + wmctrl -i -r "$window_id" -t 0 done for window_id in $browsers; do - wmctrl -i -r $window_id -t 1 + wmctrl -i -r "$window_id" -t 1 done for window_id in $communications; do - wmctrl -i -r $window_id -t 2 + wmctrl -i -r "$window_id" -t 2 done for window_id in $terminals; do - wmctrl -i -r $window_id -t 3 + wmctrl -i -r "$window_id" -t 3 done for window_id in $teleport; do - wmctrl -i -r $window_id -t 5 + wmctrl -i -r "$window_id" -t 5 done for window_id in $media; do - wmctrl -i -r $window_id -t 8 + wmctrl -i -r "$window_id" -t 8 done -- cgit v1.2.3