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