/* * Copyright (C) 2002-2006 Sergey V. Udaltsov * * 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 #include #include #include #include #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; }