diff --git a/Makefile.am b/Makefile.am index f84dc6fe..6ab3dc2d 100644 --- a/Makefile.am +++ b/Makefile.am @@ -22,7 +22,8 @@ lib_LTLIBRARIES = \ parser/libobparser.la bin_PROGRAMS = \ - kernel/openbox + kernel/openbox \ + tools/kdetrayproxy/kdetrayproxy plugin_LTLIBRARIES = \ plugins/resistance/resistance.la \ @@ -354,6 +355,16 @@ tools_obconf_obconf_SOURCES = \ tools/obconf/main.c +## kdetrayproxy ## + +tools_kdetrayproxy_kdetrayproxy_CPPFLAGS = \ + $(X_CFLAGS) +tools_kdetrayproxy_kdetrayproxy_LDADD = \ + $(X_LIBS) +tools_kdetrayproxy_kdetrayproxy_SOURCES = \ + tools/kdetrayproxy/kdetrayproxy.c + + ## themes ## dist_theme_DATA = \ diff --git a/tools/kdetrayproxy/.cvsignore b/tools/kdetrayproxy/.cvsignore new file mode 100644 index 00000000..3e53acde --- /dev/null +++ b/tools/kdetrayproxy/.cvsignore @@ -0,0 +1,4 @@ +.deps +.dirstamp +.libs +kdetrayproxy diff --git a/tools/kdetrayproxy/Makefile b/tools/kdetrayproxy/Makefile new file mode 100644 index 00000000..cfc46539 --- /dev/null +++ b/tools/kdetrayproxy/Makefile @@ -0,0 +1,4 @@ +all clean install: + $(MAKE) -C ../.. -$(MAKEFLAGS) $@ + +.PHONY: all clean install diff --git a/tools/kdetrayproxy/kdetrayproxy.c b/tools/kdetrayproxy/kdetrayproxy.c new file mode 100644 index 00000000..31a4d974 --- /dev/null +++ b/tools/kdetrayproxy/kdetrayproxy.c @@ -0,0 +1,220 @@ +#include +#include +#include +#include +#include +#include +#include + +typedef struct IList { + Window win; + int ignore_unmaps; + + struct IList *next; +} IList; + +Display *display; +Window root; +Atom winhint; +Atom roothint; +int xfd; +IList *list; + +void init(); +void eventloop(); +void handleevent(XEvent *e); +void addicon(Window win); +void removeicon(Window win, int unmap); +int issystray(Atom *a, int n); +void updatehint(); +Window findclient(Window win); +int ignore_errors(Display *d, XErrorEvent *e); +void wait_time(unsigned int t); + +int main() +{ + init(); + updatehint(); + eventloop(); + return 0; +} + +void init() +{ + display = XOpenDisplay(NULL); + if (!display) { + fprintf(stderr, "Could not open display\n"); + exit(EXIT_FAILURE); + } + + xfd = ConnectionNumber(display); + + root = RootWindowOfScreen(DefaultScreenOfDisplay(display)); + + winhint = XInternAtom(display, "_KDE_NET_WM_SYSTEM_TRAY_WINDOW_FOR", 0); + roothint = XInternAtom(display, "_KDE_NET_SYSTEM_TRAY_WINDOWS", 0); + + XSelectInput(display, root, SubstructureNotifyMask); +} + +void eventloop() +{ + XEvent e; + fd_set set; + + while (1) { + int event = False; + while (XPending(display)) { + event = True; + XNextEvent(display, &e); + handleevent(&e); + } + if (!event) { + FD_ZERO(&set); + FD_SET(xfd, &set); + select(xfd + 1, &set, NULL, NULL, NULL); + } + } +} + +void handleevent(XEvent *e) +{ + switch (e->type) { + case MapNotify: + { + Atom *a; + int n; + Window w; + + w = findclient(e->xmap.window); + if (w) { + a = XListProperties(display, w, &n); + if (issystray(a, n)) + addicon(w); + XFree(a); + } + break; + } + case UnmapNotify: + removeicon(e->xunmap.window, True); + break; + case DestroyNotify: + removeicon(e->xdestroywindow.window, False); + break; + } +} + +int ignore_errors(Display *d, XErrorEvent *e) +{ + (void)d; (void)e; + return 1; +} + +void addicon(Window win) +{ + IList *it; + + for (it = list; it; it = it->next) + if (it->win == win) return; /* duplicate */ + + it = list; + list = malloc(sizeof(IList)); + list->win = win; + list->ignore_unmaps = 2; + list->next = it; + + XSelectInput(display, win, StructureNotifyMask); + /* if i set the root hint too fast the dock app can fuck itself up */ + wait_time(1000000 / 8); + updatehint(); +} + +void removeicon(Window win, int unmap) +{ + IList *it, *last = NULL; + void *old; + + for (it = list; it; last = it, it = it->next) + if (it->win == win) { + if (it->ignore_unmaps && unmap) { + it->ignore_unmaps--; + return; + } + + if (!last) + list = it->next; + else + last->next = it->next; + + XSync(display, False); + old = XSetErrorHandler(ignore_errors); + XSelectInput(display, win, NoEventMask); + XSync(display, False); + XSetErrorHandler(old); + free(it); + + updatehint(); + } +} + +int issystray(Atom *a, int n) +{ + int i, r = False; + + for (i = 0; i < n; ++i) { + if (a[i] == winhint) { + r = True; + break; + } + } + return r; +} + +void updatehint() +{ + IList *it; + int *wins, n, i; + + for (it = list, n = 0; it; it = it->next, ++n) ; + if (n) { + wins = malloc(sizeof(int) * n); + for (it = list, i = 0; it; it = it->next, ++i) + wins[i] = it->win; + } + XChangeProperty(display, root, roothint, XA_WINDOW, 32, PropModeReplace, + (unsigned char*) wins, n); +} + +Window findclient(Window win) +{ + Window r, *children; + unsigned int n, i; + Atom state = XInternAtom(display, "WM_STATE", True); + Atom ret_type; + int ret_format; + unsigned long ret_items, ret_bytesleft; + unsigned long *prop_return; + + XQueryTree(display, win, &r, &r, &children, &n); + for (i = 0; i < n; ++i) { + Window w = findclient(children[i]); + if (w) return w; + } + + /* try me */ + XGetWindowProperty(display, win, state, 0, 1, + False, state, &ret_type, &ret_format, + &ret_items, &ret_bytesleft, + (unsigned char**) &prop_return); + if (ret_type == None || ret_items < 1) + return None; + return win; /* found it! */ +} + +void wait_time(unsigned int t) +{ + struct timeval time; + time.tv_sec = 0; + time.tv_usec = t; + select(1, NULL, NULL, NULL, &time); +}