Saturday, February 02, 2008

Rotate the Cube in Compiz with wmctrl

The Cube plugin in Compiz or Compiz-Fusion could change the desktop into a virtual cube, and each face of the cube is one of the virtual desktop. Normally, the "rotation" of the cube -- rotate the cube to swith to other virtual desktop on the face -- could be done by keyboard or mouse. However, what if I wanted to rotate it with some shell script?

There are many ways to control the rotation of the cube, such as doing so with the DBus objects provided by Compiz. Besides that, is it possible to rotate the cube with wmctrl, a useful command line tool that could interact with EWMH/NetWM compatible Window Manager? Although wmctrl does not directly support cube rotation in Compiz, the answer is still "YES!"

To control the cube rotation with wmctrl, first of all, we should find out how Cube plugin actually does when managing the virtual desktops. Take my laptop for example. I use the Compiz-Fusion came with Ubuntu 7.10, and the display resolution was set to 1024x768 due to the low hardware specification of my laptop. Let's see what we get with "wmctrl -d" when we are on the first face of the cube:

$ wmctrl -d
0 * DG: 4096x768 VP: 0,0 WA: 0,25 1024x718 N/A

The "DG" in the output is Desktop Geometry, "VP" is the coordinates of the Viewport Position, and "WA" is the coordinates and the geometry of the WorkArea. The last "N/A" is the desktop name, and, since I did not give specific name to the virtual desktop, it is "Not Available." From the output of wmctrl, we can assume that, the Cube is actually a very wide virtual desktop that wrap around the cube. In order to verify our assumption, let's rotate to the right hand side, and run "wmctrl -d" in the second face of the cube:

$ wmctrl -d
0 * DG: 4096x768 VP: 1024,0 WA: 0,25 1024x718 N/A

We can find that, the only difference between these two tests is that, the X coordinate of the VP is changed! With this result, we can conlude that, the Cube is actually a very wide desktop, and the virtual desktop we see on each face is actually a viewport to that desktop. This also explains that why the application windows could be shown across the boundary of two faces. Known that each face of the cube is actually a viewport, now we can achieve the rotation with wmctrl by simply changing the current viewport position!

I wrote a simple BASH script for this:

# compiz-rotate-wmctrl - Rotate the cube using wmctrl
# Author: Shang-Feng Yang
# Released under GPLv3


function rotate() {
  # The target face number (begins with 0)
  TVPN=$(( $1 % ${NF} ))

  # The X coordinate of the target viewport
  TVPX=$(( ${TVPN} * ${WW} ))

  # Change to the target viewport
  wmctrl -o ${TVPX},0

function usage() {
  echo -e "$(basename $0) v${VER}\n"
  echo -e "Usage:\n"
  echo -e "\t$(basename $0) {left|right|#}\n"
  echo -e "\tWhere:\n"
  echo -e "\t\tleft - rotate the cube to the left"
  echo -e "\t\tright - rotate the cube to the right"
  echo -e "\t\t# - rotate to #th face (begins with 0)\n\n"
  echo -e "Author: Shang-Feng Yang <storm dot sfyang at gmail dot com>"
  echo -e "Released under GPLv3"

# The action to be performed. $ACT could be 'left' or 'right' to rotate
# left or right, accordingly. $ACT could also be the number of the face
# to rotate into.
ACT=$(echo $1 |tr '[A-Z]' '[a-z]')

[ "x$ACT" == "x" ] && { usage; exit 1; } || {
  case $ACT in
      exit 1

# The informations about the desktop
INFO=$(wmctrl -d)
# The width of the desktop
DW=$(echo "${INFO}"| awk '{sub(/x[0-9]+/, "", $4); print $4}')
# The width of the workarea
WW=$(echo "${INFO}"| awk '{sub(/x[0-9]+/, "", $9); print $9}')
# The number of faces on the cube
# The X coordinate of the viewport
CVPX=$(echo "${INFO}" |awk '{sub(/,[0-9]+/, "", $6); print $6}')
# Current number of the face in all faces (begins with 0)
CVPN=$(( ${CVPX} / ${WW} ))

[ "$ACT" == "right" ] && {
  ACT=$(( ${CVPN} + 1 ))
} || {
  [ "$ACT" == "left" ] && {
    ACT=$(( ${CVPN} - 1 ))

rotate ${ACT}

To use the script,

  • if you didn't specify the parameters, or gave wrong parameters, a short usage information would be shown:

    $ compiz-rotate-wmctrl
    compiz-rotate-wmctrl v1.0


    compiz-rotate-wmctrl {left|right|#}


    left - rotate the cube to the left
    right - rotate the cube to the right
    # - rotate to #th face (begins with 0)

    Author: Shang-Feng Yang <storm dot sfyang at gmail dot com>
    Released under GPLv3

  • or you specify "left" to achive a left hand rotation:

    $ compiz-rotate-wmctrl left

  • or "right" for a right hand rotation:

    $ compiz-rotate-wmctrl right

  • or given a face number, begins with 0, to rotate to the specified face:

    $ compiz-rotate-wmctrl 3

Where could this script be used? Well, it would be much easier to use such a kind of script to control the rotation when you use touchscreen to control the computer, or when you remotely control the desktop through network or bluetooth PAN.


Anonymous said...

Awesome, thanks for posting this!


Parq said...

very good tutorial!


Shang-Feng Yang said...

Thank you guys!

It's nice to know people do browse my blog.

Chris in Canada said...

Worked like a charm. Thanks Shang-Feng!

Shang-Feng Yang said...

I'm glad to hear that! Happy hacking!

Anonymous said...

You're a star! A much more sensible and consistent-over-releases way of doing things than messing around with dbus :)

Anonymous said...

This tool is wonderful! Now I can automatically startup my windows box and switch to a clean workspace when I login

Chencho said...

Currently dont work (ubuntu 9.04 + Compiz)

sbellver@rosita:~$ wmctrl -d
0 * DG: N/A VP: N/A WA: N/A Escritorio 1


Chencho said...

Sorry... i have the (almost one) solution:

wmctrl -o 0,0
wmctrl -o 1280,0
wmctrl -o 2560,0
wmctrl -o 3840,0

Shang-Feng Yang said...


That's weird. I am using Ubuntu 9.04 with Compiz now, and wmctrl is working on my system:

$ ps x |grep -i compiz
4594 ? S 0:00 /bin/sh /usr/bin/compiz
4657 ? S 0:29 /usr/bin/compiz.real --ignore-desktop-hints --replace --indirect-rendering --sm-client-id 10a81e66d53ddb764a125245125947470400000044320028 core ccp
$ wmctrl -d
0 * DG: 4096x600 VP: 0,0 WA: 0,24 1024x575 N/A

If you were able to rotate the cube with "wmctrl -o x,y" then the WM you are using could be controlled by wmctrl, and it should be working. If for some unknown reason it do not work, maybe you can try the DBus way to rotate it.

JaseP said...

Awesome script, but if you accidentally run it 1st on a screen other than the first desktop, the variables need to be reset.

I had to hack-edit to work for me. Once done, Exactly what I needed. Thanks.

Shang-Feng Yang said...


Thanks. This is a primitive version of this script, so it is not a big surprise for me that it's buggy. But I'm glad to hear that you like it.

By the way, I saw some people asked that, is it possible to use this script to rotate the cube into cap and bottom? The "rotation" of the cube plugin is actually achieved by changing the viewport of the current screen, which is what this script does using wmctrl. Unfortunately, the cap and the bottom of the cube are not "covered" by the viewport. The cube plugin, as from what I understood to the cube plugin source, renders the cap and the bottom in a different way, so it is not possible yet to switch to cap and bottom by simply changing the viewport. Thus, it, at least for now, can't be done with this script.

Сыр Российский said...

It's not correct to take workarea size as viewport size, because it's smaller (during to panels). It's better to use $(xwininfo -root | grep geometry) instead.

Shang-Feng Yang said...

@Сыр Российский:

Thanks for the feedback. You are right about the working area problem. However, the script will still be working, even it uses only the working area geometry instead of the correct viewport geometry, as long as the panel without auto-hide setting is not on the either sides of the working area.

Anonymous said...

Awesome, thanks for posting this!
With love from Russia :)

Shang-Feng Yang said...

I'm flattered! I'm glad that you like it.