#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; }
Leave a Reply