Blame libxklavier/xklavier_util.c

Packit 7e555f
/*
Packit 7e555f
 * Copyright (C) 2002-2006 Sergey V. Udaltsov <svu@gnome.org>
Packit 7e555f
 *
Packit 7e555f
 * This library is free software; you can redistribute it and/or
Packit 7e555f
 * modify it under the terms of the GNU Lesser General Public
Packit 7e555f
 * License as published by the Free Software Foundation; either
Packit 7e555f
 * version 2 of the License, or (at your option) any later version.
Packit 7e555f
 *
Packit 7e555f
 * This library is distributed in the hope that it will be useful,
Packit 7e555f
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 7e555f
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit 7e555f
 * Lesser General Public License for more details.
Packit 7e555f
 *
Packit 7e555f
 * You should have received a copy of the GNU Lesser General Public
Packit 7e555f
 * License along with this library; if not, write to the
Packit 7e555f
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
Packit 7e555f
 * Boston, MA 02111-1307, USA.
Packit 7e555f
 */
Packit 7e555f
Packit 7e555f
#include <time.h>
Packit 7e555f
#include <string.h>
Packit 7e555f
Packit 7e555f
#include <X11/Xatom.h>
Packit 7e555f
#include <X11/Xlib.h>
Packit 7e555f
#include <X11/Xutil.h>
Packit 7e555f
Packit 7e555f
#include "xklavier_private.h"
Packit 7e555f
Packit 7e555f
XklState *
Packit 7e555f
_xkl_state_copy(XklState * state)
Packit 7e555f
{
Packit 7e555f
	XklState * copy;
Packit 7e555f
Packit 7e555f
	copy = g_new(XklState, 1);
Packit 7e555f
	copy->group = state->group;
Packit 7e555f
	copy->indicators = state->indicators;
Packit 7e555f
Packit 7e555f
	return copy;
Packit 7e555f
}
Packit 7e555f
Packit 7e555f
G_DEFINE_BOXED_TYPE (XklState, xkl_state, _xkl_state_copy, g_free);
Packit 7e555f
Packit 7e555f
XklState *
Packit 7e555f
xkl_engine_get_current_state(XklEngine * engine)
Packit 7e555f
{
Packit 7e555f
	return &xkl_engine_priv(engine, curr_state);
Packit 7e555f
}
Packit 7e555f
Packit 7e555f
const gchar *
Packit 7e555f
xkl_get_last_error()
Packit 7e555f
{
Packit 7e555f
	return xkl_last_error_message;
Packit 7e555f
}
Packit 7e555f
Packit 7e555f
gchar *
Packit 7e555f
xkl_engine_get_window_title(XklEngine * engine, Window w)
Packit 7e555f
{
Packit 7e555f
	Atom type_ret;
Packit 7e555f
	int format_ret;
Packit 7e555f
	unsigned long nitems, rest;
Packit 7e555f
	unsigned char *prop;
Packit 7e555f
Packit 7e555f
	if ((w == xkl_engine_priv(engine, root_window))
Packit 7e555f
	    || (w == PointerRoot))
Packit 7e555f
		return g_strdup("ROOT");
Packit 7e555f
Packit 7e555f
	if (Success ==
Packit 7e555f
	    XGetWindowProperty(xkl_engine_get_display(engine), w,
Packit 7e555f
			       xkl_engine_priv(engine, atoms)[WM_NAME], 0L,
Packit 7e555f
			       -1L, False, XA_STRING, &type_ret,
Packit 7e555f
			       &format_ret, &nitems, &rest, &prop))
Packit 7e555f
		return (gchar *) prop;
Packit 7e555f
	else
Packit 7e555f
		return NULL;
Packit 7e555f
}
Packit 7e555f
Packit 7e555f
gboolean
Packit 7e555f
xkl_engine_is_window_from_same_toplevel_window(XklEngine * engine,
Packit 7e555f
					       Window win1, Window win2)
Packit 7e555f
{
Packit 7e555f
	Window app1, app2;
Packit 7e555f
	return xkl_engine_find_toplevel_window(engine, win1, &app1) &&
Packit 7e555f
	    xkl_engine_find_toplevel_window(engine, win2, &app2)
Packit 7e555f
	    && app1 == app2;
Packit 7e555f
}
Packit 7e555f
Packit 7e555f
gboolean
Packit 7e555f
xkl_engine_get_state(XklEngine * engine, Window win, XklState * state_out)
Packit 7e555f
{
Packit 7e555f
	Window app_win;
Packit 7e555f
Packit 7e555f
	if (!xkl_engine_find_toplevel_window(engine, win, &app_win)) {
Packit 7e555f
		if (state_out != NULL)
Packit 7e555f
			state_out->group = -1;
Packit 7e555f
		return FALSE;
Packit 7e555f
	}
Packit 7e555f
Packit 7e555f
	return xkl_engine_get_toplevel_window_state(engine, app_win,
Packit 7e555f
						    state_out);
Packit 7e555f
}
Packit 7e555f
Packit 7e555f
void
Packit 7e555f
xkl_engine_delete_state(XklEngine * engine, Window win)
Packit 7e555f
{
Packit 7e555f
	Window app_win;
Packit 7e555f
Packit 7e555f
	if (xkl_engine_find_toplevel_window(engine, win, &app_win))
Packit 7e555f
		xkl_engine_remove_toplevel_window_state(engine, app_win);
Packit 7e555f
}
Packit 7e555f
Packit 7e555f
void
Packit 7e555f
xkl_engine_save_state(XklEngine * engine, Window win, XklState * state)
Packit 7e555f
{
Packit 7e555f
	Window app_win;
Packit 7e555f
Packit 7e555f
	if (!
Packit 7e555f
	    (xkl_engine_is_listening_for
Packit 7e555f
	     (engine, XKLL_MANAGE_WINDOW_STATES)))
Packit 7e555f
		return;
Packit 7e555f
Packit 7e555f
	if (xkl_engine_find_toplevel_window(engine, win, &app_win))
Packit 7e555f
		xkl_engine_save_toplevel_window_state(engine, app_win,
Packit 7e555f
						      state);
Packit 7e555f
}
Packit 7e555f
Packit 7e555f
/*
Packit 7e555f
 *  Prepares the name of window suitable for debugging (32characters long).
Packit 7e555f
 */
Packit 7e555f
gchar *
Packit 7e555f
xkl_get_debug_window_title(XklEngine * engine, Window win)
Packit 7e555f
{
Packit 7e555f
	static gchar sname[33];
Packit 7e555f
	gchar *name;
Packit 7e555f
	strcpy(sname, "NULL");
Packit 7e555f
	if (win != (Window) NULL) {
Packit 7e555f
		name = xkl_engine_get_window_title(engine, win);
Packit 7e555f
		if (name != NULL) {
Packit 7e555f
			g_snprintf(sname, sizeof(sname), "%.32s", name);
Packit 7e555f
			g_free(name);
Packit 7e555f
		}
Packit 7e555f
	}
Packit 7e555f
	return sname;
Packit 7e555f
}
Packit 7e555f
Packit 7e555f
Window
Packit 7e555f
xkl_engine_get_current_window(XklEngine * engine)
Packit 7e555f
{
Packit 7e555f
	return xkl_engine_priv(engine, curr_toplvl_win);
Packit 7e555f
}
Packit 7e555f
Packit 7e555f
/*
Packit 7e555f
 * Loads subtree. 
Packit 7e555f
 * All the windows with WM_STATE are added.
Packit 7e555f
 * All the windows within level 0 are listened for focus and property
Packit 7e555f
 */
Packit 7e555f
gboolean
Packit 7e555f
xkl_engine_load_subtree(XklEngine * engine, Window window, gint level,
Packit 7e555f
			XklState * init_state)
Packit 7e555f
{
Packit 7e555f
	Window rwin = (Window) NULL,
Packit 7e555f
	    parent = (Window) NULL, *children = NULL, *child;
Packit 7e555f
	guint num = 0;
Packit 7e555f
	gboolean retval = True;
Packit 7e555f
Packit 7e555f
	xkl_engine_priv(engine, last_error_code) =
Packit 7e555f
	    xkl_engine_query_tree(engine, window, &rwin, &parent,
Packit 7e555f
				  &children, &num);
Packit 7e555f
Packit 7e555f
	if (xkl_engine_priv(engine, last_error_code) != Success) {
Packit 7e555f
		return FALSE;
Packit 7e555f
	}
Packit 7e555f
Packit 7e555f
	child = children;
Packit 7e555f
	while (num) {
Packit 7e555f
		if (xkl_engine_if_window_has_wm_state(engine, *child)) {
Packit 7e555f
			xkl_debug(160,
Packit 7e555f
				  "Window " WINID_FORMAT
Packit 7e555f
				  " '%s' has WM_STATE so we'll add it\n",
Packit 7e555f
				  *child,
Packit 7e555f
				  xkl_get_debug_window_title(engine,
Packit 7e555f
							     *child));
Packit 7e555f
			xkl_engine_add_toplevel_window(engine, *child,
Packit 7e555f
						       window, TRUE,
Packit 7e555f
						       init_state);
Packit 7e555f
		} else {
Packit 7e555f
			xkl_debug(200,
Packit 7e555f
				  "Window " WINID_FORMAT
Packit 7e555f
				  " '%s' does not have have WM_STATE so we'll not add it\n",
Packit 7e555f
				  *child,
Packit 7e555f
				  xkl_get_debug_window_title(engine,
Packit 7e555f
							     *child));
Packit 7e555f
Packit 7e555f
			if (level == 0) {
Packit 7e555f
				xkl_debug(200,
Packit 7e555f
					  "But we are at level 0 so we'll spy on it\n");
Packit 7e555f
				xkl_engine_select_input_merging(engine,
Packit 7e555f
								*child,
Packit 7e555f
								FocusChangeMask
Packit 7e555f
								|
Packit 7e555f
								PropertyChangeMask);
Packit 7e555f
			} else
Packit 7e555f
				xkl_debug(200,
Packit 7e555f
					  "And we are at level %d so we'll not spy on it\n",
Packit 7e555f
					  level);
Packit 7e555f
Packit 7e555f
			retval =
Packit 7e555f
			    xkl_engine_load_subtree(engine, *child,
Packit 7e555f
						    level + 1, init_state);
Packit 7e555f
		}
Packit 7e555f
Packit 7e555f
		child++;
Packit 7e555f
		num--;
Packit 7e555f
	}
Packit 7e555f
Packit 7e555f
	if (children != NULL)
Packit 7e555f
		XFree(children);
Packit 7e555f
Packit 7e555f
	return retval;
Packit 7e555f
}
Packit 7e555f
Packit 7e555f
/*
Packit 7e555f
 * Checks whether given window has WM_STATE property (i.e. "App window").
Packit 7e555f
 */
Packit 7e555f
gboolean
Packit 7e555f
xkl_engine_if_window_has_wm_state(XklEngine * engine, Window win)
Packit 7e555f
{				/* ICCCM 4.1.3.1 */
Packit 7e555f
	Atom type = None;
Packit 7e555f
	int format;
Packit 7e555f
	unsigned long nitems;
Packit 7e555f
	unsigned long after;
Packit 7e555f
	unsigned char *data = NULL;	/* Helps in the case of BadWindow error */
Packit 7e555f
Packit 7e555f
	XGetWindowProperty(xkl_engine_get_display(engine), win,
Packit 7e555f
			   xkl_engine_priv(engine, atoms)[WM_STATE], 0, 0,
Packit 7e555f
			   False, xkl_engine_priv(engine, atoms)[WM_STATE],
Packit 7e555f
			   &type, &format, &nitems, &after, &data);
Packit 7e555f
	if (data != NULL)
Packit 7e555f
		XFree(data);	/* To avoid an one-byte memory leak because after successfull return
Packit 7e555f
				 * data array always contains at least one nul byte (NULL-equivalent) */
Packit 7e555f
	return type != None;
Packit 7e555f
}
Packit 7e555f
Packit 7e555f
/*
Packit 7e555f
 * Finds out the official parent window (accortind to XQueryTree)
Packit 7e555f
 */
Packit 7e555f
Window
Packit 7e555f
xkl_engine_get_registered_parent(XklEngine * engine, Window win)
Packit 7e555f
{
Packit 7e555f
	Window parent = (Window) NULL, rw = (Window) NULL, *children =
Packit 7e555f
	    NULL;
Packit 7e555f
	guint nchildren = 0;
Packit 7e555f
Packit 7e555f
	xkl_engine_priv(engine, last_error_code) =
Packit 7e555f
	    xkl_engine_query_tree(engine, win, &rw, &parent, &children,
Packit 7e555f
				  &nchildren);
Packit 7e555f
Packit 7e555f
	if (children != NULL)
Packit 7e555f
		XFree(children);
Packit 7e555f
Packit 7e555f
	return xkl_engine_priv(engine, last_error_code) ==
Packit 7e555f
	    Success ? parent : (Window) NULL;
Packit 7e555f
}
Packit 7e555f
Packit 7e555f
/*
Packit 7e555f
 * Make sure about the result. Origial XQueryTree is pretty stupid beast:)
Packit 7e555f
 */
Packit 7e555f
Status
Packit 7e555f
xkl_engine_query_tree(XklEngine * engine, Window w,
Packit 7e555f
		      Window * root_out,
Packit 7e555f
		      Window * parent_out,
Packit 7e555f
		      Window ** children_out, guint * nchildren_out)
Packit 7e555f
{
Packit 7e555f
	gboolean result;
Packit 7e555f
	unsigned int nc;
Packit 7e555f
Packit 7e555f
	result = (gboolean) XQueryTree(xkl_engine_get_display(engine),
Packit 7e555f
				       w,
Packit 7e555f
				       root_out,
Packit 7e555f
				       parent_out, children_out, &nc);
Packit 7e555f
	*nchildren_out = nc;
Packit 7e555f
Packit 7e555f
	if (!result) {
Packit 7e555f
		xkl_debug(160,
Packit 7e555f
			  "Could not get tree info for window "
Packit 7e555f
			  WINID_FORMAT ": %d\n", w, result);
Packit 7e555f
		xkl_last_error_message = "Could not get the tree info";
Packit 7e555f
	}
Packit 7e555f
Packit 7e555f
	return result ? Success : FirstExtensionError;
Packit 7e555f
}
Packit 7e555f
Packit 7e555f
const gchar *
Packit 7e555f
xkl_event_get_name(gint type)
Packit 7e555f
{
Packit 7e555f
	/* Not really good to use the fact of consecutivity
Packit 7e555f
	   but X protocol is already standartized so... */
Packit 7e555f
	static const gchar *evt_names[] = {
Packit 7e555f
		"KeyPress",
Packit 7e555f
		"KeyRelease",
Packit 7e555f
		"ButtonPress",
Packit 7e555f
		"ButtonRelease",
Packit 7e555f
		"MotionNotify",
Packit 7e555f
		"EnterNotify",
Packit 7e555f
		"LeaveNotify",
Packit 7e555f
		"FocusIn",
Packit 7e555f
		"FocusOut",
Packit 7e555f
		"KeymapNotify",
Packit 7e555f
		"Expose",
Packit 7e555f
		"GraphicsExpose",
Packit 7e555f
		"NoExpose",
Packit 7e555f
		"VisibilityNotify",
Packit 7e555f
		"CreateNotify",
Packit 7e555f
		"DestroyNotify",
Packit 7e555f
		"UnmapNotify",
Packit 7e555f
		"MapNotify",
Packit 7e555f
		"MapRequest",
Packit 7e555f
		"ReparentNotify",
Packit 7e555f
		"ConfigureNotify",
Packit 7e555f
		"ConfigureRequest",
Packit 7e555f
		"GravityNotify",
Packit 7e555f
		"ResizeRequest",
Packit 7e555f
		"CirculateNotify",
Packit 7e555f
		"CirculateRequest",
Packit 7e555f
		"PropertyNotify",
Packit 7e555f
		"SelectionClear",
Packit 7e555f
		"SelectionRequest",
Packit 7e555f
		"SelectionNotify",
Packit 7e555f
		"ColormapNotify", "ClientMessage", "MappingNotify",
Packit 7e555f
		"LASTEvent"
Packit 7e555f
	};
Packit 7e555f
	type -= KeyPress;
Packit 7e555f
	if (type < 0 || type >= (sizeof(evt_names) / sizeof(evt_names[0])))
Packit 7e555f
		return "UNKNOWN";
Packit 7e555f
	return evt_names[type];
Packit 7e555f
}
Packit 7e555f
Packit 7e555f
void
Packit 7e555f
xkl_engine_update_current_state(XklEngine * engine, int group,
Packit 7e555f
				unsigned indicators, const char reason[])
Packit 7e555f
{
Packit 7e555f
	xkl_debug(150,
Packit 7e555f
		  "Updating the current state with [g:%d/i:%u], reason: %s\n",
Packit 7e555f
		  group, indicators, reason);
Packit 7e555f
	xkl_engine_priv(engine, curr_state).group = group;
Packit 7e555f
	xkl_engine_priv(engine, curr_state).indicators = indicators;
Packit 7e555f
}