Blame libxklavier/xklavier_toplevel.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/Xmd.h>
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
Window xkl_toplevel_window_prev;
Packit 7e555f
Packit 7e555f
void
Packit 7e555f
xkl_engine_set_toplevel_window_transparent(XklEngine * engine,
Packit 7e555f
					   Window toplevel_win,
Packit 7e555f
					   gboolean transparent)
Packit 7e555f
{
Packit 7e555f
	gboolean oldval;
Packit 7e555f
Packit 7e555f
	oldval =
Packit 7e555f
	    xkl_engine_is_toplevel_window_transparent(engine,
Packit 7e555f
						      toplevel_win);
Packit 7e555f
	xkl_debug(150, "toplevel_win " WINID_FORMAT " was %stransparent\n",
Packit 7e555f
		  toplevel_win, oldval ? "" : "not ");
Packit 7e555f
	if (transparent && !oldval) {
Packit 7e555f
		CARD32 prop = 1;
Packit 7e555f
		XChangeProperty(xkl_engine_get_display(engine),
Packit 7e555f
				toplevel_win,
Packit 7e555f
				xkl_engine_priv(engine, atoms)
Packit 7e555f
				[XKLAVIER_TRANSPARENT], XA_INTEGER, 32,
Packit 7e555f
				PropModeReplace,
Packit 7e555f
				(const unsigned char *) &prop, 1);
Packit 7e555f
	} else if (!transparent && oldval) {
Packit 7e555f
		XDeleteProperty(xkl_engine_get_display(engine),
Packit 7e555f
				toplevel_win,
Packit 7e555f
				xkl_engine_priv(engine, atoms)
Packit 7e555f
				[XKLAVIER_TRANSPARENT]);
Packit 7e555f
	}
Packit 7e555f
}
Packit 7e555f
Packit 7e555f
/*
Packit 7e555f
 * "Adds" app window to the set of managed windows.
Packit 7e555f
 * Actually, no data structures involved. The only thing we do is save app state
Packit 7e555f
 * and register ourselves us listeners.
Packit 7e555f
 * Note: User's callback is called
Packit 7e555f
 */
Packit 7e555f
void
Packit 7e555f
xkl_engine_add_toplevel_window(XklEngine * engine, Window toplevel_win,
Packit 7e555f
			       Window parent,
Packit 7e555f
			       gboolean ignore_existing_state,
Packit 7e555f
			       XklState * init_state)
Packit 7e555f
{
Packit 7e555f
	XklState state = *init_state;
Packit 7e555f
	gint default_group_to_use = -1;
Packit 7e555f
	GValue params[3];
Packit 7e555f
	GValue rv;
Packit 7e555f
	guint signal_id;
Packit 7e555f
Packit 7e555f
	if (toplevel_win == xkl_engine_priv(engine, root_window))
Packit 7e555f
		xkl_debug(150, "??? root app win ???\n");
Packit 7e555f
Packit 7e555f
	xkl_debug(150,
Packit 7e555f
		  "Trying to add window " WINID_FORMAT
Packit 7e555f
		  "/%s with group %d\n", toplevel_win,
Packit 7e555f
		  xkl_get_debug_window_title(engine, toplevel_win),
Packit 7e555f
		  init_state->group);
Packit 7e555f
Packit 7e555f
	if (!ignore_existing_state) {
Packit 7e555f
		gboolean have_state =
Packit 7e555f
		    xkl_engine_get_toplevel_window_state(engine,
Packit 7e555f
							 toplevel_win,
Packit 7e555f
							 &state);
Packit 7e555f
Packit 7e555f
		if (have_state) {
Packit 7e555f
			xkl_debug(150,
Packit 7e555f
				  "The window " WINID_FORMAT
Packit 7e555f
				  " does not require to be added, it already has the xklavier state \n",
Packit 7e555f
				  toplevel_win);
Packit 7e555f
			return;
Packit 7e555f
		}
Packit 7e555f
	}
Packit 7e555f
	memset(params, 0, sizeof(params));
Packit 7e555f
	g_value_init(params, XKL_TYPE_ENGINE);
Packit 7e555f
	g_value_set_object(params, engine);
Packit 7e555f
	g_value_init(params + 1, G_TYPE_LONG);
Packit 7e555f
	g_value_set_long(params + 1, toplevel_win);
Packit 7e555f
	g_value_init(params + 2, G_TYPE_LONG);
Packit 7e555f
	g_value_set_long(params + 2, parent);
Packit 7e555f
Packit 7e555f
	memset(&rv, 0, sizeof(rv));
Packit 7e555f
	g_value_init(&rv, G_TYPE_INT);
Packit 7e555f
	g_value_set_int(&rv, default_group_to_use);
Packit 7e555f
Packit 7e555f
	signal_id =
Packit 7e555f
	    g_signal_lookup("new-toplevel-window", xkl_engine_get_type());
Packit 7e555f
	g_signal_emitv(params, signal_id, 0, &rv;;
Packit 7e555f
	default_group_to_use = g_value_get_int(&rv;;
Packit 7e555f
        
Packit 7e555f
	if (default_group_to_use == -1) {
Packit 7e555f
		Window transient_for = 0;
Packit 7e555f
		if (XGetTransientForHint(xkl_engine_get_display(engine), toplevel_win, &transient_for)) {
Packit 7e555f
			if (transient_for) {
Packit 7e555f
				XklState trans_state;
Packit 7e555f
				gboolean have_state =
Packit 7e555f
					xkl_engine_get_toplevel_window_state(engine,
Packit 7e555f
							 transient_for,
Packit 7e555f
							 &trans_state);
Packit 7e555f
				if (have_state) {
Packit 7e555f
					default_group_to_use = trans_state.group;
Packit 7e555f
				}
Packit 7e555f
			}
Packit 7e555f
		}
Packit 7e555f
	}
Packit 7e555f
Packit 7e555f
	if (default_group_to_use == -1)
Packit 7e555f
		default_group_to_use =
Packit 7e555f
		    xkl_engine_priv(engine, default_group);
Packit 7e555f
Packit 7e555f
	if (default_group_to_use != -1)
Packit 7e555f
		state.group = default_group_to_use;
Packit 7e555f
Packit 7e555f
	xkl_engine_save_toplevel_window_state(engine, toplevel_win,
Packit 7e555f
					      &state);
Packit 7e555f
	xkl_engine_select_input_merging(engine, toplevel_win,
Packit 7e555f
					FocusChangeMask |
Packit 7e555f
					PropertyChangeMask);
Packit 7e555f
Packit 7e555f
	if (default_group_to_use != -1) {
Packit 7e555f
		if (xkl_engine_priv(engine, curr_toplvl_win) ==
Packit 7e555f
		    toplevel_win) {
Packit 7e555f
			if ((xkl_engine_priv(engine, secondary_groups_mask)
Packit 7e555f
			     & (1 << default_group_to_use)) != 0)
Packit 7e555f
				xkl_engine_allow_one_switch_to_secondary_group
Packit 7e555f
				    (engine);
Packit 7e555f
			xkl_engine_lock_group(engine,
Packit 7e555f
					      default_group_to_use);
Packit 7e555f
		}
Packit 7e555f
	}
Packit 7e555f
Packit 7e555f
	if (parent == (Window) NULL)
Packit 7e555f
		parent =
Packit 7e555f
		    xkl_engine_get_registered_parent(engine, toplevel_win);
Packit 7e555f
Packit 7e555f
	xkl_debug(150, "done\n");
Packit 7e555f
}
Packit 7e555f
Packit 7e555f
/*
Packit 7e555f
 * Checks the window and goes up
Packit 7e555f
 */
Packit 7e555f
gboolean
Packit 7e555f
xkl_engine_find_toplevel_window_bottom_to_top(XklEngine * engine,
Packit 7e555f
					      Window win,
Packit 7e555f
					      Window * toplevel_win_out)
Packit 7e555f
{
Packit 7e555f
	Window parent = (Window) NULL, rwin = (Window) NULL, *children =
Packit 7e555f
	    NULL;
Packit 7e555f
	guint num = 0;
Packit 7e555f
Packit 7e555f
	if (win == (Window) NULL
Packit 7e555f
	    || win == xkl_engine_priv(engine, root_window)) {
Packit 7e555f
		*toplevel_win_out = win;
Packit 7e555f
		xkl_last_error_message = "The window is either 0 or root";
Packit 7e555f
		return FALSE;
Packit 7e555f
	}
Packit 7e555f
Packit 7e555f
	if (xkl_engine_if_window_has_wm_state(engine, win)) {
Packit 7e555f
		*toplevel_win_out = win;
Packit 7e555f
		return TRUE;
Packit 7e555f
	}
Packit 7e555f
Packit 7e555f
	xkl_engine_priv(engine, last_error_code) =
Packit 7e555f
	    xkl_engine_query_tree(engine, win, &rwin, &parent, &children,
Packit 7e555f
				  &num);
Packit 7e555f
Packit 7e555f
	if (xkl_engine_priv(engine, last_error_code) != Success) {
Packit 7e555f
		*toplevel_win_out = (Window) NULL;
Packit 7e555f
		return FALSE;
Packit 7e555f
	}
Packit 7e555f
Packit 7e555f
	if (children != NULL)
Packit 7e555f
		XFree(children);
Packit 7e555f
Packit 7e555f
	return xkl_engine_find_toplevel_window_bottom_to_top(engine,
Packit 7e555f
							     parent,
Packit 7e555f
							     toplevel_win_out);
Packit 7e555f
}
Packit 7e555f
Packit 7e555f
/*
Packit 7e555f
 * Recursively finds "App window" (window with WM_STATE) for given window.
Packit 7e555f
 * First, checks the window itself
Packit 7e555f
 * Then, for first level of recursion, checks childen,
Packit 7e555f
 * Then, goes to parent.
Packit 7e555f
 * NOTE: root window cannot be "App window" under normal circumstances
Packit 7e555f
 */
Packit 7e555f
gboolean
Packit 7e555f
xkl_engine_find_toplevel_window(XklEngine * engine, Window win,
Packit 7e555f
				Window * toplevel_win_out)
Packit 7e555f
{
Packit 7e555f
	Window parent = (Window) NULL,
Packit 7e555f
	    rwin = (Window) NULL, *children = NULL, *child;
Packit 7e555f
	guint num = 0;
Packit 7e555f
	gboolean rv;
Packit 7e555f
Packit 7e555f
	if (win == (Window) NULL
Packit 7e555f
	    || win == PointerRoot
Packit 7e555f
	    || win == xkl_engine_priv(engine, root_window)) {
Packit 7e555f
		*toplevel_win_out = (Window) NULL;
Packit 7e555f
		xkl_last_error_message = "The window is either 0 or root";
Packit 7e555f
		xkl_debug(150,
Packit 7e555f
			  "Window " WINID_FORMAT
Packit 7e555f
			  " is either 0 or root so could not get the app window for it\n",
Packit 7e555f
			  win);
Packit 7e555f
		return FALSE;
Packit 7e555f
	}
Packit 7e555f
Packit 7e555f
	if (xkl_engine_if_window_has_wm_state(engine, win)) {
Packit 7e555f
		*toplevel_win_out = win;
Packit 7e555f
		return TRUE;
Packit 7e555f
	}
Packit 7e555f
Packit 7e555f
	xkl_engine_priv(engine, last_error_code) =
Packit 7e555f
	    xkl_engine_query_tree(engine, win, &rwin, &parent, &children,
Packit 7e555f
				  &num);
Packit 7e555f
Packit 7e555f
	if (xkl_engine_priv(engine, last_error_code) != Success) {
Packit 7e555f
		*toplevel_win_out = (Window) NULL;
Packit 7e555f
		xkl_debug(150,
Packit 7e555f
			  "Could not get tree for window " WINID_FORMAT
Packit 7e555f
			  " so could not get the app window for it\n",
Packit 7e555f
			  win);
Packit 7e555f
		return FALSE;
Packit 7e555f
	}
Packit 7e555f
Packit 7e555f
	/*
Packit 7e555f
	 * Here we first check the children (in case win is just above some "App Window")
Packit 7e555f
	 * and then go upstairs
Packit 7e555f
	 */
Packit 7e555f
	child = children;
Packit 7e555f
	while (num) {
Packit 7e555f
		if (xkl_engine_if_window_has_wm_state(engine, *child)) {
Packit 7e555f
			*toplevel_win_out = *child;
Packit 7e555f
			if (children != NULL)
Packit 7e555f
				XFree(children);
Packit 7e555f
			return TRUE;
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
	rv = xkl_engine_find_toplevel_window_bottom_to_top(engine, parent,
Packit 7e555f
							   toplevel_win_out);
Packit 7e555f
Packit 7e555f
	if (!rv)
Packit 7e555f
		xkl_debug(200,
Packit 7e555f
			  "Could not get the app window for " WINID_FORMAT
Packit 7e555f
			  "/%s\n", win, xkl_get_debug_window_title(engine,
Packit 7e555f
								   win));
Packit 7e555f
Packit 7e555f
	return rv;
Packit 7e555f
}
Packit 7e555f
Packit 7e555f
/*
Packit 7e555f
 * Gets the state from the window property
Packit 7e555f
 */
Packit 7e555f
gboolean
Packit 7e555f
xkl_engine_get_toplevel_window_state(XklEngine * engine,
Packit 7e555f
				     Window toplevel_win,
Packit 7e555f
				     XklState * state_out)
Packit 7e555f
{
Packit 7e555f
	Atom type_ret;
Packit 7e555f
	int format_ret;
Packit 7e555f
	unsigned long nitems, rest;
Packit 7e555f
	CARD32 *prop = NULL;
Packit 7e555f
	gboolean ret = FALSE;
Packit 7e555f
Packit 7e555f
	gint grp = -1;
Packit 7e555f
	guint inds = 0;
Packit 7e555f
Packit 7e555f
	if ((XGetWindowProperty
Packit 7e555f
	     (xkl_engine_get_display(engine), toplevel_win,
Packit 7e555f
	      xkl_engine_priv(engine, atoms)[XKLAVIER_STATE], 0L,
Packit 7e555f
	      XKLAVIER_STATE_PROP_LENGTH, False, XA_INTEGER, &type_ret,
Packit 7e555f
	      &format_ret, &nitems, &rest,
Packit 7e555f
	      (unsigned char **) (void *) &prop) == Success)
Packit 7e555f
	    && (type_ret == XA_INTEGER) && (format_ret == 32)) {
Packit 7e555f
		grp = prop[0];
Packit 7e555f
		if (grp >= xkl_engine_get_num_groups(engine) || grp < 0)
Packit 7e555f
			grp = 0;
Packit 7e555f
Packit 7e555f
		inds = prop[1];
Packit 7e555f
Packit 7e555f
		if (state_out != NULL) {
Packit 7e555f
			state_out->group = grp;
Packit 7e555f
			state_out->indicators = inds;
Packit 7e555f
		}
Packit 7e555f
		if (prop != NULL)
Packit 7e555f
			XFree(prop);
Packit 7e555f
Packit 7e555f
		ret = TRUE;
Packit 7e555f
	}
Packit 7e555f
Packit 7e555f
	if (ret)
Packit 7e555f
		xkl_debug(150,
Packit 7e555f
			  "Appwin " WINID_FORMAT
Packit 7e555f
			  ", '%s' has the group %d, indicators %X\n",
Packit 7e555f
			  toplevel_win,
Packit 7e555f
			  xkl_get_debug_window_title(engine, toplevel_win),
Packit 7e555f
			  grp, inds);
Packit 7e555f
	else
Packit 7e555f
		xkl_debug(150,
Packit 7e555f
			  "Appwin " WINID_FORMAT
Packit 7e555f
			  ", '%s' does not have state\n", toplevel_win,
Packit 7e555f
			  xkl_get_debug_window_title(engine,
Packit 7e555f
						     toplevel_win));
Packit 7e555f
Packit 7e555f
	return ret;
Packit 7e555f
}
Packit 7e555f
Packit 7e555f
/*
Packit 7e555f
 * Deletes the state from the window properties
Packit 7e555f
 */
Packit 7e555f
void
Packit 7e555f
xkl_engine_remove_toplevel_window_state(XklEngine * engine,
Packit 7e555f
					Window toplevel_win)
Packit 7e555f
{
Packit 7e555f
	XDeleteProperty(xkl_engine_get_display(engine), toplevel_win,
Packit 7e555f
			xkl_engine_priv(engine, atoms)[XKLAVIER_STATE]);
Packit 7e555f
}
Packit 7e555f
Packit 7e555f
/*
Packit 7e555f
 * Saves the state into the window properties
Packit 7e555f
 */
Packit 7e555f
void
Packit 7e555f
xkl_engine_save_toplevel_window_state(XklEngine * engine,
Packit 7e555f
				      Window toplevel_win,
Packit 7e555f
				      XklState * state)
Packit 7e555f
{
Packit 7e555f
	CARD32 prop[XKLAVIER_STATE_PROP_LENGTH];
Packit 7e555f
Packit 7e555f
	prop[0] = state->group;
Packit 7e555f
	prop[1] = state->indicators;
Packit 7e555f
Packit 7e555f
	XChangeProperty(xkl_engine_get_display(engine), toplevel_win,
Packit 7e555f
			xkl_engine_priv(engine, atoms)[XKLAVIER_STATE],
Packit 7e555f
			XA_INTEGER, 32, PropModeReplace,
Packit 7e555f
			(const unsigned char *) prop,
Packit 7e555f
			XKLAVIER_STATE_PROP_LENGTH);
Packit 7e555f
Packit 7e555f
	xkl_debug(160,
Packit 7e555f
		  "Saved the group %d, indicators %X for appwin "
Packit 7e555f
		  WINID_FORMAT "\n", state->group, state->indicators,
Packit 7e555f
		  toplevel_win);
Packit 7e555f
}
Packit 7e555f
Packit 7e555f
gboolean
Packit 7e555f
xkl_engine_is_toplevel_window_transparent(XklEngine * engine,
Packit 7e555f
					  Window toplevel_win)
Packit 7e555f
{
Packit 7e555f
	Atom type_ret;
Packit 7e555f
	int format_ret;
Packit 7e555f
	unsigned long nitems, rest;
Packit 7e555f
	CARD32 *prop = NULL;
Packit 7e555f
	if ((XGetWindowProperty
Packit 7e555f
	     (xkl_engine_get_display(engine), toplevel_win,
Packit 7e555f
	      xkl_engine_priv(engine, atoms)[XKLAVIER_TRANSPARENT], 0L, 1,
Packit 7e555f
	      False, XA_INTEGER, &type_ret, &format_ret, &nitems, &rest,
Packit 7e555f
	      (unsigned char **) (void *) &prop) == Success)
Packit 7e555f
	    && (type_ret == XA_INTEGER) && (format_ret == 32)) {
Packit 7e555f
		if (prop != NULL)
Packit 7e555f
			XFree(prop);
Packit 7e555f
		return TRUE;
Packit 7e555f
	}
Packit 7e555f
	return FALSE;
Packit 7e555f
}