Xubuntu – moving windows between monitors

I just installed Xubuntu 12.04. I like it so far and am getting used to differences (was on Ubuntu 11.10 before). One thing that I had with Ubuntu by default was Compiz. It has a lot of plugins, one of which I used a lot was Compiz Put plugin. I could not find a similar thing in Xfce – there are shortcuts to move between workspaces, but not between the monitors that belong to one workspace.

Now I searched over the net and found some useful things – one of them is xdotool. It’s a nice utility that allows you to perform different operations on available X windows, such as move, hide, maximize, etc. Xfce has keyboard shortcuts readily available – Main menu / Settings / Settings Manager, Keyboard, Application Shortcuts tab. These allow you to bind a keyboard shortcut and run any application, including bash scripts, with it. Exactly what I need here – make a xdotool script that moves the window left or right, depending on the current position.

Searching for a window

To do something to a window, you first search for it. You can do this using the following command:

wid=`xdotool search --name Calculator|head -1`

The above will yield the window ID of the first found window that has “Calculator” in the default set of search categories (window class, window class name or window name / title).

Displaying the names of all windows

In case you don’t know the name of the window – for example, if you turned off the window decorations – you can use this script to show them:

wids=`xdotool search --name ""`
for wid in $wids; do
  wname=`xdotool getwindowname $wid`
  if [ -n "$wname" ]; then
    echo $wid --- $wname
  fi
done

which gives the output such as:

16777220 --- xfce4-panel
50331652 --- xfce4-verve-plugin
54525956 --- xfce4-cpugraph-plugin
37748740 --- xfce4-netload-plugin
52428804 --- xfce4-systemload-plugin
58720260 --- xfce4-xkb-plugin
56623108 --- wrapper
...

One thing you’ll notice is that it’s quite slow – that’s because it needs to spawn a shell and a xdotool instance for each of the window IDs it found in the first line.

Moving windows

Let’s say we wanted to move our calculator instance 10px to the left, just for fun. What we need to do is:

  • Find the window ID of the calculator
  • Find it’s geometry (position and size)
  • Calculate the new value for position
  • Move the window there

Here’s a script that does these steps:

wid=`xdotool search --name Calculator|head -1`
eval `xdotool getwindowgeometry --shell $wid`
X=$(($X - 10))
xdotool windowmove $wid $X $Y

You can test this easily – just run this in a terminal while you have a calculator instance running.

Maximized windows

Now, there’s a slight problem with the above – it doesn’t work with maximized windows. Although that’s not usually a problem (i.e. maximized windows are considered stationary for regular purposes), for the purpose of moving to the other monitor this will be a problem. There’s another utility – wmctrl – that can be used for this job:

wid=`xdotool search --name Calculator|head -1`
wmctrl -ir $wid -b remove,maximized_vert,maximized_horz

Similarly, to maximize:

wid=`xdotool search --name Calculator|head -1`
wmctrl -ir $wid -b add,maximized_vert,maximized_horz

You can even toggle:

wid=`xdotool search --name Calculator|head -1`
wmctrl -ir $wid -b toggle,maximized_vert,maximized_horz

Now, we can use the third tool – xprop – to actually get the state:

wid=`xdotool search --name Calculator|head -1`
xprop -id $wid _NET_WM_STATE

This should print something like this:

$ xprop -id $wid _NET_WM_STATE
_NET_WM_STATE(ATOM) = 
$ xprop -id $wid _NET_WM_STATE
_NET_WM_STATE(ATOM) = _NET_WM_STATE_MAXIMIZED_HORZ, _NET_WM_STATE_MAXIMIZED_VERT

In the first case, the window is not maximized, in the second case it is (both vertically and horizontally).

Hiding windows

Another issue with this is – if we are to move the windows, it’s going to actually be very flickery. This is especially true if the difference between maximized and minimized versions of the window are great. So, we need to hide / show them before we do all the things. xdotool can do that:

wid=`xdotool search --name Calculator|head -1`
xdotool windowunmap $wid

hides the window. To show it:

wid=`xdotool search --name Calculator|head -1`
xdotool windowmap $wid

Unfortunately, this is not going to help due to the issue – when the window is shown (mapped), all the changes to its position / size seem to be lost.

Getting the current window

As this is going to be a keyboard shortcut, it needs to work on the current window – xdotool allows getting the current window ID like this:

wid=`xdotool getactivewindow`

Raising the window

When moved to the other monitor, the window may actually be below the other windows, depending on how their creation / switching happened, so we need to raise it – this can be done like this:

wid=`xdotool getactivewindow`
xdotool windowraise $wid

Moving the active window to the other monitor (finally!)

Here’s what we need to do:

  • Find the active window
  • Get its maximized state and remember it
  • Remove maximization
  • Get its geometry
  • Calculate the new position
  • Move it
  • Maximize based on the previous state
  • Raise it

Here’s a script that does that:

wid=`xdotool getactivewindow`
max_state=`xprop -id $wid _NET_WM_STATE`

wmctrl -ir $wid -b remove,maximized_vert,maximized_horz
eval `xdotool getwindowgeometry --shell $wid`

new_x=1600
if [[ "$X" -ge "$new_x" ]]; then
  new_x=0
fi

xdotool windowmove $wid $new_x $Y
if [ -z "${max_state/*_NET_WM_STATE_MAXIMIZED_*/}" ]; then
  wmctrl -ir $wid -b add,maximized_vert,maximized_horz
fi

xdotool windowraise $wid

Some problems

There are some problems with the above that I’ve noticed:

  • Obviously, as made works only with two horizontally located monitors side by side
  • It’s a bit slow as it spawns a lot of shells / runs a lot of commands
  • Although it’s not hard to get one, hard-coding the new_x to the half of your monitor setup speeds this up – a trade-off here
  • When you have a panel on one monitor only, moving the windows slides below the panel if they are in the same position on the other monitor