#include "ecore_xcb_private.h" typedef struct _Shadow Shadow; struct _Shadow { Shadow *parent, **children; Ecore_X_Window win; int children_num; short x, y; unsigned short w, h; }; static Eina_Bool _inside_rects(Shadow *s, int x, int y, int bx, int by, Ecore_X_Rectangle *rects, int num); //static int shadow_count = 0; static Shadow **shadow_base = NULL; static int shadow_num = 0; /* FIXME: round trips */ static Shadow * _ecore_x_window_tree_walk(Ecore_X_Window window) { Shadow *s, **sl; xcb_get_window_attributes_reply_t *reply_attr; xcb_get_geometry_reply_t *reply_geom; xcb_query_tree_reply_t *reply_tree; xcb_get_window_attributes_cookie_t cookie_attr; xcb_get_geometry_cookie_t cookie_geom; xcb_query_tree_cookie_t cookie_tree; int i, j; CHECK_XCB_CONN; cookie_attr = xcb_get_window_attributes_unchecked(_ecore_xcb_conn, window); reply_attr = xcb_get_window_attributes_reply(_ecore_xcb_conn, cookie_attr, NULL); if (!reply_attr) return NULL; if (reply_attr->map_state != XCB_MAP_STATE_VIEWABLE) { free(reply_attr); return NULL; } free(reply_attr); cookie_geom = xcb_get_geometry_unchecked(_ecore_xcb_conn, window); reply_geom = xcb_get_geometry_reply(_ecore_xcb_conn, cookie_geom, NULL); if (!reply_geom) return NULL; if (!(s = calloc(1, sizeof(Shadow)))) { free(reply_geom); return NULL; } s->win = window; s->x = reply_geom->x; s->y = reply_geom->y; s->w = reply_geom->width; s->h = reply_geom->height; free(reply_geom); cookie_tree = xcb_query_tree_unchecked(_ecore_xcb_conn, window); reply_tree = xcb_query_tree_reply(_ecore_xcb_conn, cookie_tree, NULL); if (reply_tree) { xcb_window_t *list; int num; num = xcb_query_tree_children_length(reply_tree); list = xcb_query_tree_children(reply_tree); s->children = calloc(1, sizeof(Shadow *) * num); if (s->children) { s->children_num = num; for (i = 0; i < num; i++) { s->children[i] = _ecore_x_window_tree_walk(list[i]); if (s->children[i]) s->children[i]->parent = s; } /* compress list down */ j = 0; for (i = 0; i < num; i++) { if (s->children[i]) { s->children[j] = s->children[i]; j++; } } if (j == 0) { free(s->children); s->children = NULL; s->children_num = 0; } else { s->children_num = j; sl = realloc(s->children, sizeof(Shadow *) * j); if (sl) s->children = sl; } } free(reply_tree); } return s; } static void _ecore_x_window_tree_shadow_free1(Shadow *s) { int i = 0; if (!s) return; if (s->children) { for (i = 0; i < s->children_num; i++) { if (s->children[i]) _ecore_x_window_tree_shadow_free1(s->children[i]); } free(s->children); } free(s); } static void _ecore_x_window_tree_shadow_free(void) { int i = 0; if (!shadow_base) return; for (i = 0; i < shadow_num; i++) { if (!shadow_base[i]) continue; _ecore_x_window_tree_shadow_free1(shadow_base[i]); } free(shadow_base); shadow_base = NULL; shadow_num = 0; } static void _ecore_x_window_tree_shadow_populate(void) { Ecore_X_Window *roots = NULL; int i = 0, num = 0; if ((roots = ecore_x_window_root_list(&num))) { shadow_base = calloc(1, sizeof(Shadow *) * num); if (shadow_base) { shadow_num = num; for (i = 0; i < num; i++) shadow_base[i] = _ecore_x_window_tree_walk(roots[i]); } free(roots); } } /* static void _ecore_x_window_tree_shadow_start(void) { shadow_count++; if (shadow_count > 1) return; _ecore_x_window_tree_shadow_populate(); } static void _ecore_x_window_tree_shadow_stop(void) { shadow_count--; if (shadow_count != 0) return; _ecore_x_window_tree_shadow_free(); } */ Shadow * _ecore_x_window_shadow_tree_find_shadow(Shadow *s, Ecore_X_Window win) { Shadow *ss; int i = 0; if (s->win == win) return s; if (s->children) { for (i = 0; i < s->children_num; i++) { if (!s->children[i]) continue; if ((ss = _ecore_x_window_shadow_tree_find_shadow(s->children[i], win))) return ss; } } return NULL; } Shadow * _ecore_x_window_shadow_tree_find(Ecore_X_Window base) { Shadow *s; int i = 0; for (i = 0; i < shadow_num; i++) { if (!shadow_base[i]) continue; if ((s = _ecore_x_window_shadow_tree_find_shadow(shadow_base[i], base))) return s; } return NULL; } static Ecore_X_Window _ecore_x_window_shadow_tree_at_xy_get_shadow(Shadow *s, int bx, int by, int x, int y, Ecore_X_Window *skip, int skip_num) { Ecore_X_Window child; Ecore_X_Rectangle *rects; int i = 0, j = 0, wx = 0, wy = 0, num = 0; wx = s->x + bx; wy = s->y + by; if (!((x >= wx) && (y >= wy) && (x < (wx + s->w)) && (y < (wy + s->h)))) return 0; rects = ecore_x_window_shape_rectangles_get(s->win, &num); if (!_inside_rects(s, x, y, bx, by, rects, num)) return 0; num = 0; rects = ecore_x_window_shape_input_rectangles_get(s->win, &num); if (!_inside_rects(s, x, y, bx, by, rects, num)) return 0; if (s->children) { int skipit = 0; for (i = s->children_num - 1; i >= 0; --i) { if (!s->children[i]) continue; skipit = 0; if (skip) { for (j = 0; j < skip_num; j++) { if (s->children[i]->win == skip[j]) { skipit = 1; goto onward; } } } onward: if (!skipit) { if ((child = _ecore_x_window_shadow_tree_at_xy_get_shadow(s->children[i], wx, wy, x, y, skip, skip_num))) return child; } } } return s->win; } static Ecore_X_Window _ecore_x_window_shadow_tree_at_xy_get(Ecore_X_Window base, int bx, int by, int x, int y, Ecore_X_Window *skip, int skip_num) { Shadow *s; if (!shadow_base) { _ecore_x_window_tree_shadow_populate(); if (!shadow_base) return 0; } s = _ecore_x_window_shadow_tree_find(base); if (!s) return 0; return _ecore_x_window_shadow_tree_at_xy_get_shadow(s, bx, by, x, y, skip, skip_num); } static Eina_Bool _inside_rects(Shadow *s, int x, int y, int bx, int by, Ecore_X_Rectangle *rects, int num) { Eina_Bool inside = EINA_FALSE; int i = 0; if (!rects) return EINA_FALSE; for (i = 0; i < num; i++) { if ((x >= s->x + bx + rects[i].x) && (y >= s->y + by + rects[i].y) && (x < (int)(s->x + bx + rects[i].x + rects[i].width)) && (y < (int)(s->y + by + rects[i].y + rects[i].height))) { inside = EINA_TRUE; break; } } free(rects); return inside; } /** * Retrieves the top, visible window at the given location, * but skips the windows in the list. This uses a shadow tree built from the * window tree that is only updated the first time * ecore_x_window_shadow_tree_at_xy_with_skip_get() is called, or the next time * it is called after a ecore_x_window_shadow_tree_flush() * @param base The base window to start searching from (normally root). * @param x The given X position. * @param y The given Y position. * @return The window at that position. * @ingroup Ecore_X_Window_Geometry_Group */ EAPI Ecore_X_Window ecore_x_window_shadow_tree_at_xy_with_skip_get(Ecore_X_Window base, int x, int y, Ecore_X_Window *skip, int skip_num) { return _ecore_x_window_shadow_tree_at_xy_get(base, 0, 0, x, y, skip, skip_num); } /** * Retrieves the parent window a given window has. This uses the shadow window * tree. * @param root The root window of @p win - if 0, this will be automatically determined with extra processing overhead * @param win The window to get the parent window of * @return The parent window of @p win * @ingroup Ecore_X_Window_Geometry_Group */ EAPI Ecore_X_Window ecore_x_window_shadow_parent_get(Ecore_X_Window root __UNUSED__, Ecore_X_Window win) { Shadow *s; int i = 0; if (!shadow_base) { _ecore_x_window_tree_shadow_populate(); if (!shadow_base) return 0; } for (i = 0; i < shadow_num; i++) { if (!shadow_base[i]) continue; s = _ecore_x_window_shadow_tree_find_shadow(shadow_base[i], win); if (s) { if (!s->parent) return 0; return s->parent->win; } } return 0; } /** * Flushes the window shadow tree so nothing is stored. * @ingroup Ecore_X_Window_Geometry_Group */ EAPI void ecore_x_window_shadow_tree_flush(void) { _ecore_x_window_tree_shadow_free(); }