#include "systraybar.h"
#include "server.h"
-#include "window.h"
#include "panel.h"
+GSList *icons;
+
+/* defined in the systray spec */
+#define SYSTEM_TRAY_REQUEST_DOCK 0
+#define SYSTEM_TRAY_BEGIN_MESSAGE 1
+#define SYSTEM_TRAY_CANCEL_MESSAGE 2
+
+// selection window
+Window net_sel_win = None;
+
+
+void init_systray()
+{
+ Panel *panel;
+ Systraybar *sysbar;
+ int i, run_systray;
+
+ cleanup_systray();
+
+ run_systray = 0;
+ for (i=0 ; i < nb_panel ; i++) {
+ if (panel1[i].systray.area.visible) {
+ run_systray = 1;
+ break;
+ }
+ }
+ if (run_systray) {
+ if (XGetSelectionOwner(server.dsp, server.atom._NET_SYSTEM_TRAY_SCREEN) != None) {
+ fprintf(stderr, "tint2 : another systray is running\n");
+ run_systray = 0;
+ }
+ }
+
+ if (run_systray)
+ run_systray = net_init();
+
+ // configure sysbar on all panels
+ for (i=0 ; i < nb_panel ; i++) {
+ panel = &panel1[i];
+ sysbar = &panel->systray;
+
+ if (!run_systray) {
+ sysbar->area.visible = 0;
+ continue;
+ }
+ if (!sysbar->area.visible)
+ continue;
+
+ sysbar->area.parent = panel;
+ sysbar->area.panel = panel;
+
+ sysbar->area.posy = panel->area.pix.border.width + panel->area.paddingy;
+ sysbar->area.height = panel->area.height - (2 * sysbar->area.posy);
+ sysbar->area.width = 100;
+
+ sysbar->area.posx = panel->area.width - panel->area.paddingxlr - panel->area.pix.border.width - sysbar->area.width;
+ if (panel->clock.area.visible)
+ sysbar->area.posx -= (panel->clock.area.width + panel->area.paddingx);
+
+ sysbar->area.redraw = 1;
+ }
+}
+
+
+void cleanup_systray()
+{
+ Panel *panel;
+ int i;
+
+ for (i=0 ; i < nb_panel ; i++) {
+ panel = &panel1[i];
+ if (!panel->systray.area.visible) continue;
+
+ free_area(&panel->systray.area);
+ }
+
+ if (net_sel_win != None) {
+ XDestroyWindow(server.dsp, net_sel_win);
+ net_sel_win = None;
+ }
+}
+
+
+int net_init()
+{
+ // init systray protocol
+ net_sel_win = XCreateSimpleWindow(server.dsp, server.root_win, -1, -1, 1, 1, 0, 0, 0);
+
+ // v0.2 trayer specification. tint2 always orizontal.
+ int orient = 0;
+ XChangeProperty(server.dsp, net_sel_win, server.atom._NET_SYSTEM_TRAY_ORIENTATION, XA_CARDINAL, 32, PropModeReplace, (unsigned char *) &orient, 1);
+
+ XSetSelectionOwner(server.dsp, server.atom._NET_SYSTEM_TRAY_SCREEN, net_sel_win, CurrentTime);
+ if (XGetSelectionOwner(server.dsp, server.atom._NET_SYSTEM_TRAY_SCREEN) != net_sel_win) {
+ fprintf(stderr, "tint2 : can't get systray manager\n");
+ return 0;
+ }
+
+ XClientMessageEvent ev;
+ ev.type = ClientMessage;
+ ev.window = server.root_win;
+ ev.message_type = server.atom.MANAGER;
+ ev.format = 32;
+ ev.data.l[0] = CurrentTime;
+ ev.data.l[1] = server.atom._NET_SYSTEM_TRAY_SCREEN;
+ ev.data.l[2] = net_sel_win;
+ ev.data.l[3] = 0;
+ ev.data.l[4] = 0;
+ XSendEvent(server.dsp, server.root_win, False, StructureNotifyMask, &ev);
+
+ return 1;
+}
int resize_systray (Systraybar *sysbar)
return 0;
}
+
+
+Window win, root;
+int width, height;
+int border;
+int icon_size;
+
+
+void fix_geometry()
+{
+ GSList *it;
+
+ // find the proper width and height
+ width = 0;
+ height = icon_size;
+ for (it = icons; it != NULL; it = g_slist_next(it)) {
+ width += icon_size;
+ }
+
+ XResizeWindow(server.dsp, win, width + border * 2, height + border * 2);
+}
+
+
+gboolean error;
+int window_error_handler(Display *d, XErrorEvent *e)
+{
+ d=d;e=e;
+ if (e->error_code == BadWindow) {
+ error = TRUE;
+ } else {
+ //g_printerr("X ERROR NOT BAD WINDOW!\n");
+ abort();
+ }
+ return 0;
+}
+
+
+gboolean icon_swallow(TrayWindow *traywin)
+{
+ XErrorHandler old;
+
+ error = FALSE;
+ old = XSetErrorHandler(window_error_handler);
+ XReparentWindow(server.dsp, traywin->id, win, 0, 0);
+ XSync(server.dsp, False);
+ XSetErrorHandler(old);
+
+ return !error;
+}
+
+
+// The traywin must have its id and type set.
+gboolean icon_add(Window id)
+{
+ TrayWindow *traywin;
+
+ traywin = g_new0(TrayWindow, 1);
+ traywin->id = id;
+
+ if (!icon_swallow(traywin)) {
+ printf("not icon_swallow\n");
+ g_free(traywin);
+ return FALSE;
+ }
+
+ // find the positon for the systray app window
+ int count = g_slist_length(icons);
+ traywin->x = border + ((width % icon_size) / 2) +
+ (count % (width / icon_size)) * icon_size;
+ traywin->y = border + ((height % icon_size) / 2) +
+ (count / (height / icon_size)) * icon_size;
+
+ // add the new icon to the list
+ icons = g_slist_append(icons, traywin);
+
+ // watch for the icon trying to resize itself!
+ XSelectInput(server.dsp, traywin->id, StructureNotifyMask);
+
+ // position and size the icon window
+ XMoveResizeWindow(server.dsp, traywin->id, traywin->x, traywin->y, icon_size, icon_size);
+
+ // resize our window so that the new window can fit in it
+ fix_geometry();
+
+ // flush before clearing, otherwise the clear isn't effective.
+ XFlush(server.dsp);
+ // make sure the new child will get the right stuff in its background
+ // for ParentRelative.
+ XClearWindow(server.dsp, win);
+
+ // show the window
+ XMapRaised(server.dsp, traywin->id);
+
+ return TRUE;
+}
+
+
+void net_message(XClientMessageEvent *e)
+{
+ unsigned long opcode;
+ Window id;
+
+ opcode = e->data.l[1];
+
+ switch (opcode) {
+ case SYSTEM_TRAY_REQUEST_DOCK:
+ panel_refresh = 1;
+ id = e->data.l[2];
+ printf("add dockapp\n");
+ if (id && icon_add(id)) {
+ XSelectInput(server.dsp, id, StructureNotifyMask);
+ }
+ break;
+
+ case SYSTEM_TRAY_BEGIN_MESSAGE:
+ //g_printerr("Message From Dockapp\n");
+ id = e->window;
+ break;
+
+ case SYSTEM_TRAY_CANCEL_MESSAGE:
+ //g_printerr("Message Cancelled\n");
+ id = e->window;
+ break;
+
+ default:
+ if (opcode == server.atom._NET_SYSTEM_TRAY_MESSAGE_DATA) {
+ printf("message from dockapp:\n %s\n", e->data.b);
+ id = e->window;
+ }
+ // unknown message type. not in the spec
+ break;
+ }
+}
+
+
/*
-// initialise taskbar posx and width
-void resize_taskbar()
+void event_loop()
{
- int taskbar_width, modulo_width, taskbar_on_screen;
-
- if (panel.mode == MULTI_DESKTOP) taskbar_on_screen = panel.nb_desktop;
- else taskbar_on_screen = panel.nb_monitor;
-
- taskbar_width = panel.area.width - (2 * panel.area.paddingx) - (2 * panel.area.pix.border.width);
- if (panel.clock.time1_format)
- taskbar_width -= (panel.clock.area.width + panel.area.paddingx);
- taskbar_width = (taskbar_width - ((taskbar_on_screen-1) * panel.area.paddingx)) / taskbar_on_screen;
-
- if (taskbar_on_screen > 1)
- modulo_width = (taskbar_width - ((taskbar_on_screen-1) * panel.area.paddingx)) % taskbar_on_screen;
- else
- modulo_width = 0;
-
- int i, nb, modulo=0, posx=0;
- nb = panel.nb_desktop * panel.nb_monitor;
- for (i=0 ; i < nb ; i++) {
- if ((i % taskbar_on_screen) == 0) {
- posx = panel.area.pix.border.width + panel.area.paddingx;
- modulo = modulo_width;
- }
- else posx += taskbar_width + panel.area.paddingx;
+ XEvent e;
+ Window cover;
+ GSList *it;
+
+ while (!exit_app) {
+ while (XPending(server.dsp)) {
+ XNextEvent(display, &e);
+
+ switch (e.type)
+ {
+ case PropertyNotify:
+ // systray window list has changed?
+ if (e.xproperty.atom == kde_systray_prop) {
+ XSelectInput(display, win, NoEventMask);
+ kde_update_icons();
+ XSelectInput(display, win, StructureNotifyMask);
- panel.taskbar[i].area.posx = posx;
- panel.taskbar[i].area.width = taskbar_width;
- if (modulo) {
- panel.taskbar[i].area.width++;
- modulo--;
+ while (XCheckTypedEvent(display, PropertyNotify, &e));
+ }
+
+ break;
+
+ case ConfigureNotify:
+ if (e.xany.window != win) {
+ // find the icon it pertains to and beat it into submission
+ GSList *it;
+
+ for (it = icons; it != NULL; it = g_slist_next(it)) {
+ TrayWindow *traywin = it->data;
+ if (traywin->id == e.xany.window) {
+ XMoveResizeWindow(display, traywin->id, traywin->x, traywin->y,
+ icon_size, icon_size);
+ break;
+ }
+ }
+ break;
+ }
+
+ // briefly cover the entire containing window, which causes it and
+ // all of the icons to refresh their windows. finally, they update
+ // themselves when the background of the main window's parent changes.
+
+ cover = XCreateSimpleWindow(display, win, 0, 0,
+ border * 2 + width, border * 2 + height,
+ 0, 0, 0);
+ XMapWindow(display, cover);
+ XDestroyWindow(display, cover);
+
+ break;
+
+ case ReparentNotify:
+ if (e.xany.window == win) // reparented to us
+ break;
+ case UnmapNotify:
+ case DestroyNotify:
+ for (it = icons; it; it = g_slist_next(it)) {
+ if (((TrayWindow*)it->data)->id == e.xany.window) {
+ icon_remove(it);
+ break;
+ }
+ }
+ break;
+
+ case ClientMessage:
+ if (e.xclient.message_type == net_opcode_atom &&
+ e.xclient.format == 32 &&
+ e.xclient.window == net_sel_win)
+ net_message(&e.xclient);
+
+ default:
+ break;
}
+ }
+ usleep(500000);
+ }
- resize_tasks(&panel.taskbar[i]);
- }
+ // remove/unparent all the icons
+ while (icons) {
+ // do the remove here explicitly, cuz the event handler isn't going to
+ // happen anymore.
+ icon_remove(icons);
+ }
}
*/
-