1 #include "version.h"
2 #include "kde.h"
3 #include "icons.h"
4 #include "docker.h"
5 #include "net.h"
6 
7 #include <assert.h>
8 #include <signal.h>
9 #include <stdlib.h>
10 #include <string.h>
11 #include <unistd.h>
12 #include <X11/Xutil.h>
13 
14 int argc;
15 char **argv;
16 
17 Window win = None, hint_win = None, root = None;
18 gboolean wmaker = FALSE; /* WindowMakerMode!!! wheeee */
19 Display *display = NULL;
20 GSList *icons = NULL;
21 int width = 0, height = 0;
22 int border = 1; /* blank area around icons. must be > 0 */
23 gboolean horizontal = TRUE; /* layout direction */
24 int icon_size = 24; /* width and height of systray icons */
25 
26 static char *display_string = NULL;
27 /* excluding the border. sum of all child apps */
28 static gboolean exit_app = FALSE;
29 
signal_handler(int signal)30 void signal_handler(int signal)
31 {
32   switch (signal)
33   {
34   case SIGSEGV:
35   case SIGFPE:
36     g_printerr("Segmentation Fault or Critical Error encountered. "
37                "Dumping core and aborting.\n");
38     abort();
39 
40   case SIGPIPE:
41   case SIGTERM:
42   case SIGINT:
43   case SIGHUP:
44     exit_app = TRUE;
45   }
46 }
47 
48 
parse_cmd_line()49 void parse_cmd_line()
50 {
51   int i;
52   gboolean help = FALSE;
53 
54   for (i = 1; i < argc; i++) {
55     if (0 == strcasecmp(argv[i], "-display")) {
56       ++i;
57       if (i < argc) {
58         display_string = argv[i];
59       } else { /* argument doesn't exist */
60         g_printerr("-display requires a parameter\n");
61         help = TRUE;
62       }
63     } else if (0 == strcasecmp(argv[i], "-wmaker")) {
64       wmaker = TRUE;
65     } else if (0 == strcasecmp(argv[i], "-vertical")) {
66       horizontal = FALSE;
67     } else if (0 == strcasecmp(argv[i], "-border")) {
68       ++i;
69 
70       if (i < argc) {
71         int b = atoi(argv[i]);
72         if (b > 0) {
73           border = b;
74         } else {
75           g_printerr("-border must be a value greater than 0\n");
76           help = TRUE;
77         }
78       } else { /* argument doesn't exist */
79         g_printerr("-border requires a parameter\n");
80         help = TRUE;
81       }
82       } else if (0 == strcasecmp(argv[i], "-iconsize")) {
83       ++i;
84       if (i < argc) {
85         int s = atoi(argv[i]);
86         if (s > 0) {
87           icon_size = s;
88         } else {
89           g_printerr("-iconsize must be a value greater than 0\n");
90           help = TRUE;
91         }
92       } else { /* argument doesn't exist */
93         g_printerr("-iconsize requires a parameter\n");
94         help = TRUE;
95       }
96     } else {
97       if (argv[i][0] == '-')
98         help = TRUE;
99     }
100 
101 
102     if (help) {
103       /* show usage help */
104       g_print("%s - version %s\n", argv[0], VERSION);
105       g_print("Copyright 2003, Ben Jansens <ben@orodu.net>\n\n");
106       g_print("Usage: %s [OPTIONS]\n\n", argv[0]);
107       g_print("Options:\n");
108       g_print("  -help             Show this help.\n");
109       g_print("  -display DISLPAY  The X display to connect to.\n");
110       g_print("  -border           The width of the border to put around the\n"
111               "                    system tray icons. Defaults to 1.\n");
112       g_print("  -vertical         Line up the icons vertically. Defaults to\n"
113               "                    horizontally.\n");
114       g_print("  -wmaker           WindowMaker mode. This makes docker a\n"
115               "                    fixed size (64x64) to appear nicely in\n"
116               "                    in WindowMaker.\n"
117               "                    Note: In this mode, you have a fixed\n"
118               "                    number of icons that docker can hold.\n");
119       g_print("  -iconsize SIZE    The size (width and height) to display\n"
120               "                    icons as in the system tray. Defaults to\n"
121               "                    24.\n");
122       exit(1);
123     }
124   }
125 }
126 
127 
create_hint_win()128 void create_hint_win()
129 {
130   XWMHints hints;
131   XClassHint classhints;
132 
133   hint_win = XCreateSimpleWindow(display, root, 0, 0, 1, 1, 0, 0, 0);
134   assert(hint_win);
135 
136   hints.flags = StateHint | WindowGroupHint | IconWindowHint;
137   hints.initial_state = WithdrawnState;
138   hints.window_group = hint_win;
139   hints.icon_window = win;
140 
141   classhints.res_name = "docker";
142   classhints.res_class = "Docker";
143 
144   XSetWMProperties(display, hint_win, NULL, NULL, argv, argc,
145                    NULL, &hints, &classhints);
146 
147   XMapWindow(display, hint_win);
148 }
149 
150 
create_main_window()151 void create_main_window()
152 {
153   XWMHints hints;
154   XTextProperty text;
155   char *name = "Docker";
156 
157   /* the border must be > 0 if not in wmaker mode */
158   assert(wmaker || border > 0);
159 
160   if (!wmaker)
161     win = XCreateSimpleWindow(display, root, 0, 0,
162                               border * 2, border * 2, 0, 0, 0);
163   else
164     win = XCreateSimpleWindow(display, root, 0, 0,
165                               64, 64, 0, 0, 0);
166 
167   assert(win);
168 
169   XStringListToTextProperty(&name, 1, &text);
170   XSetWMName(display, win, &text);
171 
172   hints.flags = StateHint;
173   hints.initial_state = WithdrawnState;
174   XSetWMHints(display, win, &hints);
175 
176   create_hint_win();
177 
178   XSync(display, False);
179   XSetWindowBackgroundPixmap(display, win, ParentRelative);
180   XClearWindow(display, win);
181 }
182 
183 
reposition_icons()184 void reposition_icons()
185 {
186   int x = border + ((width % icon_size) / 2),
187       y = border + ((height % icon_size) / 2);
188   GSList *it;
189 
190   for (it = icons; it != NULL; it = g_slist_next(it)) {
191     TrayWindow *traywin = it->data;
192     traywin->x = x;
193     traywin->y = y;
194     XMoveWindow(display, traywin->id, x, y);
195     XSync(display, False);
196     if (wmaker) {
197       x += icon_size;
198       if (x + icon_size > width) {
199         x = border;
200         y += icon_size;
201       }
202     } else if (horizontal)
203       x += icon_size;
204     else
205       y += icon_size;
206   }
207 }
208 
209 
fix_geometry()210 void fix_geometry()
211 {
212   GSList *it;
213 
214   /* in wmaker mode we're a fixed size */
215   if (wmaker) return;
216 
217   /* find the proper width and height */
218   width = horizontal ? 0 : icon_size;
219   height = horizontal ? icon_size : 0;
220   for (it = icons; it != NULL; it = g_slist_next(it)) {
221     if (horizontal)
222       width += icon_size;
223     else
224       height += icon_size;
225   }
226 
227   XResizeWindow(display, win, width + border * 2, height + border * 2);
228 }
229 
230 
event_loop()231 void event_loop()
232 {
233   XEvent e;
234   Window cover;
235   GSList *it;
236 
237   while (!exit_app) {
238     while (XPending(display)) {
239       XNextEvent(display, &e);
240 
241       switch (e.type)
242       {
243       case PropertyNotify:
244         /* systray window list has changed? */
245         if (e.xproperty.atom == kde_systray_prop) {
246           XSelectInput(display, win, NoEventMask);
247           kde_update_icons();
248           XSelectInput(display, win, StructureNotifyMask);
249 
250           while (XCheckTypedEvent(display, PropertyNotify, &e));
251         }
252 
253         break;
254 
255       case ConfigureNotify:
256         if (e.xany.window != win) {
257           /* find the icon it pertains to and beat it into submission */
258           GSList *it;
259 
260           for (it = icons; it != NULL; it = g_slist_next(it)) {
261             TrayWindow *traywin = it->data;
262             if (traywin->id == e.xany.window) {
263               XMoveResizeWindow(display, traywin->id, traywin->x, traywin->y,
264                                 icon_size, icon_size);
265               break;
266             }
267           }
268           break;
269         }
270 
271         /* briefly cover the entire containing window, which causes it and
272            all of the icons to refresh their windows. finally, they update
273            themselves when the background of the main window's parent changes.
274         */
275         cover = XCreateSimpleWindow(display, win, 0, 0,
276                                     border * 2 + width, border * 2 + height,
277                                     0, 0, 0);
278         XMapWindow(display, cover);
279         XDestroyWindow(display, cover);
280 
281         break;
282 
283       case ReparentNotify:
284         if (e.xany.window == win) /* reparented to us */
285           break;
286       case UnmapNotify:
287       case DestroyNotify:
288         for (it = icons; it; it = g_slist_next(it)) {
289           if (((TrayWindow*)it->data)->id == e.xany.window) {
290             icon_remove(it);
291             break;
292           }
293         }
294         break;
295 
296       case ClientMessage:
297         if (e.xclient.message_type == net_opcode_atom &&
298             e.xclient.format == 32 &&
299             e.xclient.window == net_sel_win)
300           net_message(&e.xclient);
301 
302       default:
303         break;
304       }
305     }
306     usleep(500000);
307   }
308 
309   /* remove/unparent all the icons */
310   while (icons) {
311     /* do the remove here explicitly, cuz the event handler isn't going to
312        happen anymore. */
313     icon_remove(icons);
314   }
315 }
316 
main(int c,char ** v)317 int main(int c, char **v)
318 {
319   struct sigaction act;
320 
321   argc = c; argv = v;
322 
323   act.sa_handler = signal_handler;
324   act.sa_flags = 0;
325   sigaction(SIGSEGV, &act, NULL);
326   sigaction(SIGPIPE, &act, NULL);
327   sigaction(SIGFPE, &act, NULL);
328   sigaction(SIGTERM, &act, NULL);
329   sigaction(SIGINT, &act, NULL);
330   sigaction(SIGHUP, &act, NULL);
331 
332   parse_cmd_line(argc, argv);
333 
334   display = XOpenDisplay(display_string);
335   if (!display) {
336     g_printerr("Unable to open Display %s. Exiting.\n",
337                DisplayString(display_string));
338   }
339 
340   root = RootWindow(display, DefaultScreen(display));
341   assert(root);
342 
343   if (wmaker)
344     width = height = 64 - border * 2;
345 
346   create_main_window();
347 
348   /* set up to find KDE systray icons, and get any that already exist */
349   kde_init();
350 
351   net_init();
352 
353   /* we want to get ConfigureNotify events, and assume our parent's background
354      has changed when we do, so we need to refresh ourself to match */
355   XSelectInput(display, win, StructureNotifyMask);
356 
357   event_loop();
358 
359   XCloseDisplay(display);
360 
361   return 0;
362 }
363