#!/bin/sh # Move windows according to my workflow. Check bin/gnome-set-config to # 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. 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 -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 'tolower($0) ~ /(firefox|chrom)/ {print $1}')" for window_id in $misc; do wmctrl -i -r "$window_id" -t 4 done for window_id in $main; do wmctrl -i -r "$window_id" -t 0 done for window_id in $browsers; do wmctrl -i -r "$window_id" -t 1 done for window_id in $communications; do wmctrl -i -r "$window_id" -t 2 done for window_id in $terminals; do wmctrl -i -r "$window_id" -t 3 done for window_id in $teleport; do wmctrl -i -r "$window_id" -t 5 done for window_id in $media; do wmctrl -i -r "$window_id" -t 8 done