desk-test.c

desk-test.c

#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;
}

© All Right Reserved 2019-2020 futuredesktop.org