/* cache.c: the window cache */ /* Copyright (C) 1999 by the Massachusetts Institute of Technology. * * Permission to use, copy, modify, and distribute this * software and its documentation for any purpose and without * fee is hereby granted, provided that the above copyright * notice appear in all copies and that both that copyright * notice and this permission notice appear in supporting * documentation, and that the name of M.I.T. not be used in * advertising or publicity pertaining to distribution of the * software without specific, written prior permission. * M.I.T. makes no representations about the suitability of * this software for any purpose. It is provided "as is" * without express or implied warranty. */ #include "nawm.h" #include "cache.h" #include #include #include #include extern Display *dpy; extern Window root; extern int debug, window_manager; cacheinfo *cachehead; int cachedebug; static Atom XA_WM_STATE; int is_client_window(Window win); Window client_window(Window win); Window client_window_internal(Window win); void cachewin(Window win); void uncachewin(Window win); void destroycache(void); void initcache(void) { Window rootret, parent, *children; unsigned int numchildren, n; cachehead = NULL; XA_WM_STATE = XInternAtom(dpy, "WM_STATE", False); XQueryTree(dpy, root, &rootret, &parent, &children, &numchildren); for (n = 0; n < numchildren; n++) { cachewin(client_window(children[n])); cachewin(children[n]); } XFree(children); } void destroycache(void) { cacheinfo *ci, *cin; for (ci = cachehead; ci; ci = cin) { XFree(ci->names[0]); if (ci->numnames > 1) XFree(ci->names[1]); cin = ci->next; free(ci); } } void cachewin(Window win) { cacheinfo *new; unsigned long items, left; unsigned char *name, *prop; Atom type; int format, n; Window cwin; XWindowAttributes attr; XGetWindowAttributes(dpy, win, &attr); cwin = client_window(win); if (win != cwin) { cacheinfo *ci; for (ci = cachehead; ci; ci = ci->next) { if (ci->win == cwin) { ci->parent = win; ci->x = attr.x - attr.border_width; ci->y = attr.y - attr.border_width; ci->w = attr.width + 2 * attr.border_width; ci->h = attr.height + 2 * attr.border_width; } } return; } /* Select Enter/Leave events on this window */ XSelectInput(dpy, win, attr.your_event_mask | EnterWindowMask | LeaveWindowMask); if (XGetWindowProperty(dpy, win, XA_WM_NAME, 0, 1000, False, AnyPropertyType, &type, &format, &items, &left, &name) != Success) return; if (!items) { XFree(name); return; } XGetWindowProperty(dpy, win, XA_WM_CLASS, 0, 1000, False, AnyPropertyType, &type, &format, &items, &left, &prop); new = xmalloc(sizeof(cacheinfo) + (items * sizeof(char *))); new->next = cachehead; new->win = win; new->parent = (Window)0; new->x = attr.x - attr.border_width; new->y = attr.y - attr.border_width; new->w = attr.width + 2 * attr.border_width; new->h = attr.height + 2 * attr.border_width; new->mapped = attr.map_state == IsViewable; new->names[0] = (char *)name; if (items) { for (n = 1; *prop; n++) { new->names[n] = (char *)prop; prop = (unsigned char *)strchr((char *)prop, '\0') + 1; } new->numnames = n; } else { XFree(prop); new->numnames = 1; } cachehead = new; } void uncachewin(Window win) { cacheinfo *ci, *prev; for (ci = cachehead, prev = NULL; ci; prev = ci, ci = ci->next) { if (ci->win == win || ci->parent == win) break; } if (!ci) return; if (!prev) cachehead = ci->next; else prev->next = ci->next; XFree(ci->names[0]); if (ci->numnames>1) XFree(ci->names[1]); } void update_cache(XEvent *ev) { Window win = ev->xconfigure.window; cacheinfo *ci; if (ev->type == CreateNotify) { cachewin(win); return; } else if (ev->type == DestroyNotify) { uncachewin(win); return; } for (ci = cachehead; ci; ci = ci->next) { if (ci->win == win || ci->parent == win) break; } if (!ci) { cachewin(win); return; } if (ev->type == ConfigureNotify) { XConfigureEvent *xcev = (XConfigureEvent *)ev; ci->x = xcev->x - xcev->border_width; ci->y = xcev->y - xcev->border_width; ci->w = xcev->width + 2 * xcev->border_width; ci->h = xcev->height + 2 * xcev->border_width; } else if (ev->type == MapNotify) ci->mapped = 1; else if (ev->type == UnmapNotify) ci->mapped = 0; } Window client_window(Window win) { if (window_manager != NAWM_WM_NAWM) { Window cw; cacheinfo ci; /* First try the cache. */ ci.win = win; if (find_window(&ci, NAWM_CACHE_WINDOW)) return ci.win; /* Now actually look for it. */ cw = client_window_internal(win); if (cw) return cw; } return win; } Window client_window_internal(Window win) { Window root, parent; Window *children; unsigned int nchildren; unsigned int i; Window inf = 0; if (is_client_window(win)) return win; if (!XQueryTree(dpy, win, &root, &parent, &children, &nchildren)) return 0; for (i = 0; !inf && (i < nchildren); i++) inf = client_window_internal(children[i]); if (children) XFree((char *)children); return inf ? inf : 0; } int is_client_window(Window win) { Atom type = None; int format; unsigned long nitems, after; unsigned char *data = NULL; XGetWindowProperty(dpy, win, XA_WM_STATE, 0, 0, False, AnyPropertyType, &type, &format, &nitems, &after, &data); if (data) XFree((char*)data); if (type) return 1; return 0; } Window manager_window(Window win) { if (window_manager == NAWM_WM_NAWM) return win; else { Window root, parent, *children; unsigned int nchildren; cacheinfo ci; /* First try the cache */ ci.win = win; if (find_window(&ci, NAWM_CACHE_WINDOW)) return ci.parent ? ci.parent : ci.win; if (!is_client_window(win)) return win; if (!XQueryTree(dpy, win, &root, &parent, &children, &nchildren)) return win; if (children) XFree((char *)children); if (!parent || (parent == root)) return win; return parent; } } int hasname(Window win, char *name) { cacheinfo ci; int n; ci.win = win; ci.names[0] = name; return find_window(&ci, NAWM_CACHE_WINDOW | NAWM_CACHE_NAME); } int find_window(cacheinfo *match, long mask) { cacheinfo *ci; int try = 1; retry: for (ci = cachehead; ci; ci = ci->next) { if (mask & NAWM_CACHE_WINDOW) { if ((match->win != ci->win) && (match->win != ci->parent)) continue; } else { if ((mask & NAWM_CACHE_CLIENT) && (match->win != ci->win)) continue; if ((mask & NAWM_CACHE_PARENT) && (match->win != ci->parent)) continue; } if (((mask & NAWM_CACHE_MAPPED) || (mask & NAWM_CACHE_LOCATION)) && !ci->mapped) continue; if (mask & NAWM_CACHE_LOCATION) { if (match->x < ci->x || match->x > ci->x + ci->w) continue; if (match->y < ci->y || match->y > ci->y + ci->w) continue; } if (mask & NAWM_CACHE_NAME) { int n; for (n = 0; n < ci->numnames; n++) { if (!strcmp(match->names[0], ci->names[n])) break; } if (n == ci->numnames) continue; } break; } if (!ci) { if ((mask & NAWM_CACHE_RETRY) && (try == 1)) { if (cachedebug) fprintf(stderr, "nawm: rebuilding cache\n"); destroycache(); initcache(); try++; goto retry; } return 0; } memcpy(match, ci, sizeof(*ci)); if (match->numnames) match->numnames = 1; return 1; } void dumpcache(void) { cacheinfo *ci; int i; for (ci = cachehead; ci; ci = ci->next) { printf("Window 0x%lx (%ld) is %dx%d%s%d%s%d, %smapped, has parent " "0x%lx (%ld) and names ", (long)ci->win, (long)ci->win, ci->w, ci->h, ci->x >= 0 ? "+" : "", ci->x, ci->y >= 0 ? "+" : "", ci->y, ci->mapped ? "" : "un", (long)ci->parent, (long)ci->parent); for (i = 0; i < ci->numnames; i++) { printf("\"%s\"%s", ci->names[i], i == ci->numnames - 1 ? "\n" : ", "); } } }