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