How to move all windows to another workspace

Is is possible to move all windows (or all not minimized windows) form one workspace to another?

I know I can move one window to other workspace with Shift+Ctrl+Alt+arrow, but it will move only that one focused window.


Unity : What are Viewports?

Ubuntu Unity uses viewports - basically a coordinate system (with coordinate 0,0 being top left corner) , where one giant desktop subdivided into chunks that fit your screen resolution. Coordinates grow in value as you move to the right and downwards.

enter image description here

The coordinate system is relative. If my current viewport is top left , everything relative to that viewport will be positive values in increments of width and height. For instance, if my current viewport is top leftmost, the firefox window in top middle workspace you see above is positioned in at x value 1366 and y value 0 relative to the top leftmost viewport. If my active viewport is top middle one, the terminal window in top leftmost viewport is positioned at x value -1327 60. This is the key issue for xdotool , because xdotool does not deal with negative numbers.

Note also that, the top left corner of your current viewport will always be assumed by xdotool as coordinates 0 0 . That means we can only move stuff right and down.

Making xdotool work for Unity

Now we know that xdotool can move windows only relative to our top-left corner ( i.e., you can always move window down and right , but never up and left ). How do we make that work for unity. Well, the basic idea would be to

  1. Figure out all the windows on current viewport
  2. Move to a requested viewport momentarily to make the top left corner assume coordinates 0 0 at that viewport
  3. Move all windows to user-defined viewport coordinates
  4. Return to the old viewport ( optional , could follow the windows as well )

Scripting Solution

The script below performs exactly the procedure described above. It can be called with either -v flag to manually specify coordinates or you can use -g flag to bring up GUI dialog. -f flag will tell the script to switch viewport too ; if that flag is not used - you'll remain on the current viewport and only windows will be moved around

Obtaining the script

One can copy the source code from this post directly or through github using the following steps:

  1. sudo apt-get install git
  2. cd /opt ; sudo git clone https://github.com/SergKolo/sergrep.git
  3. sudo chmod -R +x sergrep

The script file will be /opt/sergrep/move_viewport_windows.sh

To bind script to shortcut , refer to How do I bind .sh files to keyboard combination?

Note that wmctrl and xdotool are required for this script to work properly. You can install them via sudo apt-get install xdotool and wmctrl

Source Code

#!/usr/bin/env bash
#
###########################################################
# Author: Serg Kolo , contact: [email protected] 
# Date: April 17 2016
# Purpose: Move all windows on the current viewport
#          to a user-defined one
# Written for:
# Tested on: Ubuntu 14.04 LTS , Unity 7.2.6
###########################################################
# Copyright: Serg Kolo , 2016
#    
#     Permission to use, copy, modify, and distribute this software is hereby granted
#     without fee, provided that  the copyright notice above and this permission statement
#     appear in all copies.
#
#     THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
#     IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
#     FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
#     THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
#     LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
#     FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
#     DEALINGS IN THE SOFTWARE.

get_active_viewport()
{
  xprop -root -notype _NET_DESKTOP_VIEWPORT
}

get_screen_geometry()
{
 xwininfo -root | awk '/-geometry/{gsub(/+|x/," ");print $2,$3}'  
}

current_wins()
{  
   HEX="$(wmctrl -lG | \
   awk -v xlim="$XMAX" -v ylim="$YMAX" \
      'BEGIN{printf "ibase=16;"} $3>0 && $3<xlim  && $4>0 && $4<ylim \
      { gsub(/0x/,""); printf "%s;",toupper($1)  } ')"
   echo $HEX | bc | tr '\n' ' '
}

gui_selection()
{
  SCHEMA="org.compiz.core:/org/compiz/profiles/unity/plugins/core/"
  read swidth sdepth  <<< "$(get_screen_geometry)"
  vwidth=$(gsettings get $SCHEMA hsize)
  vheight=$(gsettings get $SCHEMA vsize)

 width=0
 for horizontal in $(seq 1 $vwidth); do
    height=0 
    for vertical in $(seq 1 $vheight);  do

      array+=( FALSE  )
      array+=( $(echo "$width"x"$height") )

    height=$(($height+$sdepth))
    done
 width=$(($width+$swidth))
 done

 zenity --list --radiolist --column="" --column "CHOICE" ${array[@]} --width 350 --height 350 2> /dev/null
}

print_usage()
{
cat << EOF
move_viewport_windows.sh [-v 'XPOS YPOS' ] [-g] [-f ] [-h]

Copyright Serg Kolo , 2016

The script gets list of all windows on the current Unity 
viewport and moves them to user-specified viewport. If
ran without flags specified, script prints this text

-g flag brings up GUI dialog with list of viewports

-v allows manually specifying viewoport. Argument must be
   quoted, X and Y position space separated

-f if set, the viewport will switch to the same one where
   windows were sent

-h prints this text

** NOTE ** 
wmctrl and xdotool are required for this script to work
properly. You can install them via sudo apt-get install
xdotool and wmctrl

EOF
}

parse_args()
{
  if [ $# -eq 0  ];then
    print_usage
    exit
  fi
  while getopts "v:ghf" opt
 do
   case ${opt} in
     v) NEWVP=${OPTARG}
        ;;
     g) NEWVP="$(gui_selection | tr 'x' ' ' )"
        [ -z "$NEWVP" ] && exit 1
        ;;
     f) FOLLOW=true
        ;; 
     h) print_usage
        exit 0
        ;;
     \?)
      echo "Invalid option: -$OPTARG" >&2
      ;;
    esac
  done
  shift $((OPTIND-1))
}

main()
{
 # Basic idea:
 #-------------------
 # 1. get current viewport and list of windows
 # 2. go to viewport 0 0 and move all windows from list
 #    to desired viewport
 # 3. go back to original viewport or follow the windows,
 #    depending on user choice
 # 4. Tell the user where they are currently

 local FOLLOW
 local NEWVP # coordinates of desired viewport
 local XMAX YMAX # must be two vals for awk to work
 local OLDVP=$(get_active_viewport | awk -F '=' '{sub(/,/," ");print $2}' )

 parse_args "$@"

 read XMAX YMAX  <<< "$(get_screen_geometry)" # move to getopts

 windows=( $(current_wins) )

 xdotool set_desktop_viewport 0 0 
 for win in ${windows[@]} ; do
    echo "$win"
    xdotool windowmove $win $NEWVP
 done
 # sleep 0.25 # uncomment if necessary

 if [ $FOLLOW  ]; then
     xdotool set_desktop_viewport $NEWVP
 else
     xdotool set_desktop_viewport $OLDVP
 fi

 sleep 0.25 # delay to allow catching active viewport
 notify-send "current viewport is $(get_active_viewport | awk -F '=' '{sub(/,/," ");print $2}' )"
 exit 0
}
main "$@"

Demo

Webm recording of the script in action:

https://www.youtube.com/watch?v=cJMlC41CWWo

Issues

Due to Unity's grid plugin that is responsible for window snapping, the script cannot move the maximized , or right/left snapped windows. It will be attempted to add the momentary unset and reset of that plugin to make the script work with all windows, but because unsetting and resetting has a time delay, it might be abandoned as an idea. If you want the script to make working with all windows, install unity-tweak-tool and unset window snapping under Window Manager options.


Non-Compiz based desktop environments (XFCE, LXDE, GNOME, KDE...)

You can use a combination of wmctrl and xdotool for this. First make sure these two utilities are installed:

sudo apt-get install xdotool wmctrl

With the dependencies satisfied you should be able to use the following one-liner to move all windows on the current desktop to another one:

while read i; do wmctrl -i -t 2 -r "$i"  ; done  < <(wmctrl -l | awk -v var=$(xdotool get_desktop) '{if ($2 == var) print $0;}' | cut -d' '  -f1)

A quick breakdown of the commands used:

  • wmctrl -l | awk -v var=$(xdotool get_desktop) '{if ($2 == var) print $0;}' | cut -d' ' -f1

    List all windows, filter out those that aren't on the current workspace, and extract their window ID

  • wmctrl -i -t 2 -r "$i"

    Move window with window ID $i to workspace 2.

  • all of this is packed in a simple while read ... do; done loop that iterates over all windows on the current desktop

Compiz-based desktop environments (e.g. Unity)

Finding a solution for desktop environments like Unity is made difficult by the fact that Compiz (Unity's window manager) does not use desktops in a traditional sense.