#include
#include
#include /* sleep() */
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xatom.h>
/* EDIT: GTK is not needed.
#include <gtk/gtk.h>
*/
#include <gdk/gdk.h>
#include <gdk/gdkx.h>
#define _(x) x
/*
Created by Osmo Antero (moma) Maatta.
Test various desktop operations in Metacity and Compiz window managers.
The tests are:
TEST1: Get number of desktops.
TEST2: Get desktop names.
TEST3: Get the current desktop number.
TEST4: Move to the last desktop, sleep(2) and come back.
TEST5: Get the desktop number for a spesific Window ID.
(Requires you to modify the code (TEST5) and chnage the window_id= to a correct value.
Use xwininfo command to find out window-id for any window you like)
---------------------
Compile:
EDIT: I removed gtk+-2.0. Only gdk-2.0 is needed.
$ gcc `pkg-config --cflags --libs gdk-2.0` -lX11 -Wall desk-test.c -o desk-test
Run:
$ ./desk-test
You should run the tests in both Metacity (or in equivalent WM) and Compiz.
---------------------
Additional information.
xwininfo and xprop tools are very helpful when finding information about window manager attributes (atoms) and windows.
Some examples:
$ xwininfo # point at a window an press left-mouse-button.
$ xprop -root # will report all atoms (values) for the root-window (in the window manager).
$ xprop -root | grep "_NET_"
$ xprop -id 0x2a029d5 # report values of spesific X window (use xwininfo first ;-)
Fusion-icon is a handy tool to switch between Compiz and other window managers. Install it first.
$ fusion-icon
*/
void free_string_list(GList **list)
{
GList *item = *list;
while (item)
{
g_free((gchar*) item->data);
item = item->next;
}
g_list_free(*list);
*list = NULL;
}
gboolean get_property_data_long(gchar *atom_name, glong **ret_data, gint *ret_length)
{
GdkAtom actual_type;
gint actual_format;
GdkAtom gatom = gdk_atom_intern(atom_name, FALSE);
if (gdk_property_get(gdk_get_default_root_window(), gatom, gdk_atom_intern("CARDINAL", FALSE), 0L, (0xFF), FALSE,
&actual_type, &actual_format, ret_length, (guchar**)ret_data))
{
return TRUE;
}
*ret_data = NULL;
*ret_length = 0;
return FALSE;
}
guint get_number_of_desktops_metacity()
{
GdkAtom actual_type;
gint actual_format;
gint num_items;
guchar *ret_data_ptr;
GdkAtom atom_net_number_desktops = gdk_atom_intern("_NET_NUMBER_OF_DESKTOPS", FALSE);
guint num = 0;
if (gdk_property_get(gdk_get_default_root_window(), atom_net_number_desktops, gdk_atom_intern("CARDINAL", FALSE), 0L, 1L, FALSE,
&actual_type, &actual_format, &num_items, (guchar**)&ret_data_ptr))
{
num = (int)*ret_data_ptr;
g_free(ret_data_ptr);
}
return num;
}
guint get_number_of_desktops()
{
/* Ask window manager its "_NET_NUMBER_OF_DESKTOPS" */
guint num_desktops = get_number_of_desktops_metacity();
/* But Compiz window manager sets "_NET_NUMBER_OF_DESKTOPS" always to 1.
So I have to calculate num_desktops via wm's "_NET_WORKAREA" and "_NET_DESKTOP_GEOMETRY" values.
This is propably not right because VIEWPORT is not the same as DESKTOP.
What do you say?
*/
if (num_desktops <= 1) { guint desktop_width = 1; guint desktop_total_width = 0; glong *data; gint data_len; /* An example: _NET_WORKAREA(CARDINAL) = 0, 25, 1680, 1000 (run "xprop -root" for more details ) */ if (get_property_data_long("_NET_WORKAREA", &data, &data_len)) { desktop_width = (int)data[2]; g_free(data); } /* An example: _NET_DESKTOP_GEOMETRY(CARDINAL) = 8400, 1050 (run "xprop -root" for more details ) */ if (get_property_data_long("_NET_DESKTOP_GEOMETRY", &data, &data_len)) { desktop_total_width = data[0]; g_free(data); } num_desktops = MAX(desktop_total_width / desktop_width, 1); } return num_desktops; } GList *get_desktop_names() { /* Return GList of desktop names */ GdkAtom actual_type; gint actual_format; gint num_items; guchar *ret_data_ptr = NULL; /* Number of desktops */ guint num_desktops = get_number_of_desktops(); GList *list = NULL; /* Ask the names */ GdkAtom atom_net_desktop_names = gdk_atom_intern("_NET_DESKTOP_NAMES", FALSE); if (gdk_property_get(gdk_get_default_root_window(),atom_net_desktop_names, gdk_atom_intern("UTF8_STRING", FALSE), 0, 0xFFFFFF, FALSE, &actual_type, &actual_format, &num_items, &ret_data_ptr)) { if (num_items > 0)
{
guchar *p = (guchar *)ret_data_ptr;
list = g_list_append(list, g_strdup((gchar*)p));
guint i;
for (i = 0; i < MIN(num_items-1, num_desktops-1); i++)
{
if (*(ret_data_ptr + i) == '\0')
{
p = ret_data_ptr + i + 1;
list = g_list_append(list, g_strdup((gchar*)p));
}
}
}
g_free(ret_data_ptr);
}
/* Again, Compiz window manager will not give the workspaces (viewports) any names,
so we'll set the names to string "Compiz desktop #".
*/
if (g_list_length(list) < num_desktops)
{
guint i, len;
len = g_list_length(list);
for (i=len; i< num_desktops; i++) { list = g_list_append(list, g_strdup_printf(_("Compiz desktop %d"), i+1)); } } /* Notice: You should free the list (and list->data values) after usage */
return list;
}
gint get_active_desktop()
{
/* Get the current, active desktop.
The returned value is zero-based: 0, 1, 2...
*/
GdkAtom actual_type;
gint actual_format;
gint num_items;
guchar *ret_data_ptr = NULL;
/* This works in Metacity */
GdkAtom atom_net_current_desktop = gdk_atom_intern("_NET_CURRENT_DESKTOP", FALSE);
gint num = -1;
if (gdk_property_get(gdk_get_default_root_window(), atom_net_current_desktop, gdk_atom_intern("CARDINAL", FALSE), 0L, 1L, FALSE,
&actual_type, &actual_format, &num_items, (guchar**)&ret_data_ptr))
{
num = *((int*)ret_data_ptr);
g_free(ret_data_ptr);
}
/* This is for Compiz */
if (num == 0)
{
/* Compiz window manager wil always set the _NET_CURRENT_DESKTOP to 0. So we have to calculate this by reading
the _NET_DESKTOP_VIEWPORT and _NET_WORKAREA values.
I may take wrong here because workarea or viewport is not the same as desktop??
*/
guint desktop_width = 0;
guint desktop_viewport = 0;
glong *data;
gint data_len;
/* An example: _NET_WORKAREA(CARDINAL) = 0, 25, 1680, 1000 (run "xprop -root" for more details ) */
if (get_property_data_long("_NET_WORKAREA", &data, &data_len))
{
desktop_width = (int)data[2];
g_free(data);
}
/* An example: _NET_DESKTOP_VIEWPORT(CARDINAL) = 5040, 0 (run "xprop -root" for more details ) */
if (get_property_data_long("_NET_DESKTOP_VIEWPORT", &data, &data_len))
{
desktop_viewport = (int)data[0];
g_free(data);
}
if (desktop_width > 0)
num = desktop_viewport / desktop_width;
}
return num;
}
void set_active_desktop(guint desktop_num)
{
/* Set active desktop.
The desktop_num is zero-based: 0, 1, 2...
Sorry for the Xlib stuff, I'll try convert this to pure GDK later...
*/
if (desktop_num+1 > get_number_of_desktops()) return;
Display *display = GDK_DISPLAY();
Window root_win = GDK_WINDOW_XWINDOW(gdk_get_default_root_window());
Atom atom_net_current_desktop = XInternAtom(display, "_NET_CURRENT_DESKTOP", False);
XEvent xevent;
xevent.type = ClientMessage;
xevent.xclient.type = ClientMessage;
xevent.xclient.display = display;
xevent.xclient.window = root_win;
xevent.xclient.message_type = atom_net_current_desktop;
xevent.xclient.format = 32;
xevent.xclient.data.l[0] = desktop_num;
xevent.xclient.data.l[1] = CurrentTime;
xevent.xclient.data.l[2] = 0;
xevent.xclient.data.l[3] = 0;
xevent.xclient.data.l[4] = 0;
XSendEvent(display, root_win, False, SubstructureNotifyMask | SubstructureRedirectMask, &xevent);
XFlush(display);
}
gint get_desktop_for_window(GdkWindow *window)
{
/* Find desktop number for given window.
*/
GdkAtom actual_type;
gint actual_format;
gint num_items;
guchar *ret_data_ptr;
/* Works in Metacity but not in Compiz */
GdkAtom atom_net_wm_desktop = gdk_atom_intern("_NET_WM_DESKTOP", FALSE);
gint num = -1;
if (gdk_property_get(window, atom_net_wm_desktop, gdk_atom_intern("CARDINAL", FALSE), 0L, 1L,FALSE,
&actual_type, &actual_format, &num_items, (guchar**)&ret_data_ptr))
{
num = (int)*ret_data_ptr;
g_free(ret_data_ptr);
}
return num;
}
int main(int argc, char *argv[])
{
gdk_init(&argc, &argv);
printf("TEST1: Get number of desktops.\n" );
guint num_desktops = get_number_of_desktops();
printf("Number of desktops is: %d\n", num_desktops);
printf("------------------------------------------\n");
printf("TEST2: Get desktop names.\n");
GList *list = get_desktop_names();
printf("\tLength of the name list is: %d\n", g_list_length(list));
GList *item = list;
while (item)
{
printf("\t%s\n", (gchar*)item->data);
item = item->next;
}
free_string_list(&list);
printf("------------------------------------------\n");
printf("TEST3: Get the current desktop number.\n");
gint active_desktop = get_active_desktop();
printf("\tCurrent desktop is: %d\n", active_desktop);
printf("------------------------------------------\n");
printf("TEST4: Move to the last desktop, sleep(2) and come back.\n");
printf("You should see the desktop move if this is right.\n");
printf("My test shows that this works in Metacity but not in Compiz WM.\n");
num_desktops = get_number_of_desktops();
active_desktop = get_active_desktop();
set_active_desktop(num_desktops - 1);
sleep(2);
set_active_desktop(active_desktop);
printf("------------------------------------------\n");
printf("TEST5: Get the desktop number for a spesific Window ID.\n");
printf("Run xwininfo command to find out a window-id (for any window you like) then change the variable window_id= in the code (in TEST5).\n");
printf("Move your test window from workspace to workspace, rerun this program and check the reported desktop number.\n");
printf("My test shows that this works in Metacity but not in Compiz WM.\n");
Window window_id;
/* YOU NEED TO CHANGE THIS VALUE. */
window_id = 0;
if (window_id == 0L)
printf("Modify the code in TEST5 and change the window_id= to something sensible\n");
else
{
gint desktop_num = get_desktop_for_window(gdk_window_foreign_new(window_id));
printf("Desktop number for X-window 0x%lx is: %d\n", window_id, desktop_num);
}
printf("------------------------------------------\n");
return 0;
}