add a reconfigure action, also reconfigure on SIGUSR2.
[dana/openbox.git] / openbox / dock.c
1 #include "debug.h"
2 #include "dock.h"
3 #include "mainloop.h"
4 #include "screen.h"
5 #include "prop.h"
6 #include "config.h"
7 #include "grab.h"
8 #include "openbox.h"
9 #include "render/theme.h"
10
11 #define DOCK_EVENT_MASK (ButtonPressMask | ButtonReleaseMask | \
12                          EnterWindowMask | LeaveWindowMask)
13 #define DOCKAPP_EVENT_MASK (StructureNotifyMask)
14
15 static ObDock *dock;
16
17 StrutPartial dock_strut;
18
19 void dock_startup(gboolean reconfig)
20 {
21     XSetWindowAttributes attrib;
22
23     if (reconfig) {
24         dock_configure();
25         return;
26     }
27
28     STRUT_PARTIAL_SET(dock_strut, 0, 0, 0, 0,
29                       0, 0, 0, 0, 0, 0, 0, 0);
30
31     dock = g_new0(ObDock, 1);
32     dock->obwin.type = Window_Dock;
33
34     dock->hidden = TRUE;
35
36     attrib.event_mask = DOCK_EVENT_MASK;
37     attrib.override_redirect = True;
38     dock->frame = XCreateWindow(ob_display, RootWindow(ob_display, ob_screen),
39                                 0, 0, 1, 1, 0,
40                                 RrDepth(ob_rr_inst), InputOutput,
41                                 RrVisual(ob_rr_inst),
42                                 CWOverrideRedirect | CWEventMask,
43                                 &attrib);
44     dock->a_frame = RrAppearanceCopy(ob_rr_theme->a_unfocused_title);
45     XSetWindowBorder(ob_display, dock->frame,
46                      RrColorPixel(ob_rr_theme->b_color));
47     XSetWindowBorderWidth(ob_display, dock->frame, ob_rr_theme->bwidth);
48
49     g_hash_table_insert(window_map, &dock->frame, dock);
50     stacking_add(DOCK_AS_WINDOW(dock));
51     stacking_raise(DOCK_AS_WINDOW(dock));
52 }
53
54 void dock_shutdown(gboolean reconfig)
55 {
56     if (reconfig) return;
57
58     XDestroyWindow(ob_display, dock->frame);
59     RrAppearanceFree(dock->a_frame);
60     g_hash_table_remove(window_map, &dock->frame);
61     stacking_remove(dock);
62 }
63
64 void dock_add(Window win, XWMHints *wmhints)
65 {
66     ObDockApp *app;
67     XWindowAttributes attrib;
68     gchar **data;
69
70     app = g_new0(ObDockApp, 1);
71     app->obwin.type = Window_DockApp;
72     app->win = win;
73     app->icon_win = (wmhints->flags & IconWindowHint) ?
74         wmhints->icon_window : win;
75
76     if (PROP_GETSS(app->win, wm_class, locale, &data)) {
77         if (data[0]) {
78             app->name = g_strdup(data[0]);
79             if (data[1])
80                 app->class = g_strdup(data[1]);
81         }
82         g_strfreev(data);     
83     }
84
85     if (app->name == NULL) app->name = g_strdup("");
86     if (app->class == NULL) app->class = g_strdup("");
87     
88     if (XGetWindowAttributes(ob_display, app->icon_win, &attrib)) {
89         app->w = attrib.width;
90         app->h = attrib.height;
91     } else {
92         app->w = app->h = 64;
93     }
94
95     dock->dock_apps = g_list_append(dock->dock_apps, app);
96     dock_configure();
97
98     XReparentWindow(ob_display, app->icon_win, dock->frame, app->x, app->y);
99     /*
100       This is the same case as in frame.c for client windows. When Openbox is
101       starting, the window is already mapped so we see unmap events occur for
102       it. There are 2 unmap events generated that we see, one with the 'event'
103       member set the root window, and one set to the client, but both get
104       handled and need to be ignored.
105     */
106     if (ob_state() == OB_STATE_STARTING)
107         app->ignore_unmaps += 2;
108
109     if (app->win != app->icon_win) {
110         /* have to map it so that it can be re-managed on a restart */
111         XMoveWindow(ob_display, app->win, -1000, -1000);
112         XMapWindow(ob_display, app->win);
113     }
114     XMapWindow(ob_display, app->icon_win);
115     XSync(ob_display, False);
116
117     /* specify that if we exit, the window should not be destroyed and should
118        be reparented back to root automatically */
119     XChangeSaveSet(ob_display, app->icon_win, SetModeInsert);
120     XSelectInput(ob_display, app->icon_win, DOCKAPP_EVENT_MASK);
121
122     grab_button_full(2, 0, app->icon_win,
123                      ButtonPressMask | ButtonReleaseMask | ButtonMotionMask,
124                      GrabModeAsync, OB_CURSOR_MOVE);
125
126     g_hash_table_insert(window_map, &app->icon_win, app);
127
128     ob_debug("Managed Dock App: 0x%lx (%s)\n", app->icon_win, app->class);
129 }
130
131 void dock_remove_all()
132 {
133     while (dock->dock_apps)
134         dock_remove(dock->dock_apps->data, TRUE);
135 }
136
137 void dock_remove(ObDockApp *app, gboolean reparent)
138 {
139     ungrab_button(2, 0, app->icon_win);
140     XSelectInput(ob_display, app->icon_win, NoEventMask);
141     /* remove the window from our save set */
142     XChangeSaveSet(ob_display, app->icon_win, SetModeDelete);
143     XSync(ob_display, False);
144
145     g_hash_table_remove(window_map, &app->icon_win);
146
147     if (reparent)
148         XReparentWindow(ob_display, app->icon_win,
149                         RootWindow(ob_display, ob_screen), app->x, app->y);
150
151     dock->dock_apps = g_list_remove(dock->dock_apps, app);
152     dock_configure();
153
154     ob_debug("Unmanaged Dock App: 0x%lx (%s)\n", app->icon_win, app->class);
155
156     g_free(app->name);
157     g_free(app->class);
158     g_free(app);
159 }
160
161 void dock_configure()
162 {
163     GList *it;
164     gint spot;
165     gint gravity;
166     gint minw, minh;
167     gint strw, strh;
168     Rect *a;
169
170     RrMinsize(dock->a_frame, &minw, &minh);
171
172     dock->w = dock->h = 0;
173
174     /* get the size */
175     for (it = dock->dock_apps; it; it = it->next) {
176         ObDockApp *app = it->data;
177         switch (config_dock_orient) {
178         case OB_ORIENTATION_HORZ:
179             dock->w += app->w;
180             dock->h = MAX(dock->h, app->h);
181             break;
182         case OB_ORIENTATION_VERT:
183             dock->w = MAX(dock->w, app->w);
184             dock->h += app->h;
185             break;
186         }
187     }
188
189     spot = (config_dock_orient == OB_ORIENTATION_HORZ ? minw : minh) / 2;
190
191     /* position the apps */
192     for (it = dock->dock_apps; it; it = it->next) {
193         ObDockApp *app = it->data;
194         switch (config_dock_orient) {
195         case OB_ORIENTATION_HORZ:
196             app->x = spot;
197             app->y = (dock->h - app->h) / 2;
198             spot += app->w;
199             break;
200         case OB_ORIENTATION_VERT:
201             app->x = (dock->w - app->w) / 2;
202             app->y = spot;
203             spot += app->h;
204             break;
205         }
206
207         XMoveWindow(ob_display, app->icon_win, app->x, app->y);
208     }
209
210     /* used for calculating offsets */
211     dock->w += ob_rr_theme->bwidth * 2;
212     dock->h += ob_rr_theme->bwidth * 2;
213
214     a = screen_physical_area();
215
216     /* calculate position */
217     if (config_dock_floating) {
218         dock->x = config_dock_x;
219         dock->y = config_dock_y;
220         gravity = NorthWestGravity;
221     } else {
222         switch (config_dock_pos) {
223         case OB_DIRECTION_NORTHWEST:
224             dock->x = 0;
225             dock->y = 0;
226             gravity = NorthWestGravity;
227             break;
228         case OB_DIRECTION_NORTH:
229             dock->x = a->width / 2;
230             dock->y = 0;
231             gravity = NorthGravity;
232             break;
233         case OB_DIRECTION_NORTHEAST:
234             dock->x = a->width;
235             dock->y = 0;
236             gravity = NorthEastGravity;
237             break;
238         case OB_DIRECTION_WEST:
239             dock->x = 0;
240             dock->y = a->height / 2;
241             gravity = WestGravity;
242             break;
243         case OB_DIRECTION_EAST:
244             dock->x = a->width;
245             dock->y = a->height / 2;
246             gravity = EastGravity;
247             break;
248         case OB_DIRECTION_SOUTHWEST:
249             dock->x = 0;
250             dock->y = a->height;
251             gravity = SouthWestGravity;
252             break;
253         case OB_DIRECTION_SOUTH:
254             dock->x = a->width / 2;
255             dock->y = a->height;
256             gravity = SouthGravity;
257             break;
258         case OB_DIRECTION_SOUTHEAST:
259             dock->x = a->width;
260             dock->y = a->height;
261             gravity = SouthEastGravity;
262             break;
263         }
264     }
265
266     switch(gravity) {
267     case NorthGravity:
268     case CenterGravity:
269     case SouthGravity:
270         dock->x -= dock->w / 2;
271         break;
272     case NorthEastGravity:
273     case EastGravity:
274     case SouthEastGravity:
275         dock->x -= dock->w;
276         break;
277     }
278     switch(gravity) {
279     case WestGravity:
280     case CenterGravity:
281     case EastGravity:
282         dock->y -= dock->h / 2;
283         break;
284     case SouthWestGravity:
285     case SouthGravity:
286     case SouthEastGravity:
287         dock->y -= dock->h;
288         break;
289     }
290
291     if (config_dock_hide && dock->hidden) {
292         if (!config_dock_floating) {
293             switch (config_dock_pos) {
294             case OB_DIRECTION_NORTHWEST:
295                 switch (config_dock_orient) {
296                 case OB_ORIENTATION_HORZ:
297                     dock->y -= dock->h - ob_rr_theme->bwidth;
298                     break;
299                 case OB_ORIENTATION_VERT:
300                     dock->x -= dock->w - ob_rr_theme->bwidth;
301                     break;
302                 }
303                 break;
304             case OB_DIRECTION_NORTH:
305                 dock->y -= dock->h - ob_rr_theme->bwidth;
306                 break;
307             case OB_DIRECTION_NORTHEAST:
308                 switch (config_dock_orient) {
309                 case OB_ORIENTATION_HORZ:
310                     dock->y -= dock->h - ob_rr_theme->bwidth;
311                     break;
312                 case OB_ORIENTATION_VERT:
313                     dock->x += dock->w - ob_rr_theme->bwidth;
314                     break;
315                 }
316                 break;
317             case OB_DIRECTION_WEST:
318                 dock->x -= dock->w - ob_rr_theme->bwidth;
319                 break;
320             case OB_DIRECTION_EAST:
321                 dock->x += dock->w - ob_rr_theme->bwidth;
322                 break;
323             case OB_DIRECTION_SOUTHWEST:
324                 switch (config_dock_orient) {
325                 case OB_ORIENTATION_HORZ:
326                     dock->y += dock->h - ob_rr_theme->bwidth;
327                     break;
328                 case OB_ORIENTATION_VERT:
329                     dock->x -= dock->w - ob_rr_theme->bwidth;
330                     break;
331                 } break;
332             case OB_DIRECTION_SOUTH:
333                 dock->y += dock->h - ob_rr_theme->bwidth;
334                 break;
335             case OB_DIRECTION_SOUTHEAST:
336                 switch (config_dock_orient) {
337                 case OB_ORIENTATION_HORZ:
338                     dock->y += dock->h - ob_rr_theme->bwidth;
339                     break;
340                 case OB_ORIENTATION_VERT:
341                     dock->x += dock->w - ob_rr_theme->bwidth;
342                     break;
343                 }
344                 break;
345             }    
346         }
347     }
348
349     if (!config_dock_floating && config_dock_hide) {
350         strw = ob_rr_theme->bwidth;
351         strh = ob_rr_theme->bwidth;
352     } else {
353         strw = dock->w;
354         strh =  dock->h;
355     }
356
357     /* set the strut */
358     if (config_dock_floating) {
359         STRUT_PARTIAL_SET(dock_strut, 0, 0, 0, 0,
360                           0, 0, 0, 0, 0, 0, 0, 0);
361     } else {
362         switch (config_dock_pos) {
363         case OB_DIRECTION_NORTHWEST:
364             switch (config_dock_orient) {
365             case OB_ORIENTATION_HORZ:
366                 STRUT_PARTIAL_SET(dock_strut, 0, strh, 0, 0,
367                                   0, 0, dock->x, dock->x + dock->w - 1,
368                                   0, 0, 0, 0);
369                 break;
370             case OB_ORIENTATION_VERT:
371                 STRUT_PARTIAL_SET(dock_strut, strw, 0, 0, 0,
372                                   dock->y, dock->y + dock->h - 1,
373                                   0, 0, 0, 0, 0, 0);
374                 break;
375             }
376             break;
377         case OB_DIRECTION_NORTH:
378             STRUT_PARTIAL_SET(dock_strut, 0, strh, 0, 0,
379                               dock->x, dock->x + dock->w - 1,
380                               0, 0, 0, 0, 0, 0);
381             break;
382         case OB_DIRECTION_NORTHEAST:
383             switch (config_dock_orient) {
384             case OB_ORIENTATION_HORZ:
385                 STRUT_PARTIAL_SET(dock_strut, 0, strh, 0, 0,
386                                   0, 0, dock->x, dock->x + dock->w -1,
387                                   0, 0, 0, 0);
388                 break;
389             case OB_ORIENTATION_VERT:
390                 STRUT_PARTIAL_SET(dock_strut, 0, 0, strw, 0,
391                                   0, 0, 0, 0,
392                                   dock->y, dock->y + dock->h - 1, 0, 0);
393                 break;
394             }
395             break;
396         case OB_DIRECTION_WEST:
397             STRUT_PARTIAL_SET(dock_strut, strw, 0, 0, 0,
398                               dock->y, dock->y + dock->h - 1,
399                               0, 0, 0, 0, 0, 0);
400             break;
401         case OB_DIRECTION_EAST:
402             STRUT_PARTIAL_SET(dock_strut, 0, 0, strw, 0,
403                               0, 0, 0, 0,
404                               dock->y, dock->y + dock->h - 1, 0, 0);
405             break;
406         case OB_DIRECTION_SOUTHWEST:
407             switch (config_dock_orient) {
408             case OB_ORIENTATION_HORZ:
409                 STRUT_PARTIAL_SET(dock_strut, 0, 0, 0, strh,
410                                   0, 0, 0, 0, 0, 0,
411                                   dock->x, dock->x + dock->w - 1);
412                 break;
413             case OB_ORIENTATION_VERT:
414                 STRUT_PARTIAL_SET(dock_strut, strw, 0, 0, 0,
415                                   dock->y, dock->y + dock->h - 1,
416                                   0, 0, 0, 0, 0, 0);
417                 break;
418             }
419             break;
420         case OB_DIRECTION_SOUTH:
421             STRUT_PARTIAL_SET(dock_strut, 0, 0, 0, strh,
422                               0, 0, 0, 0, 0, 0,
423                               dock->x, dock->x + dock->w - 1);
424             break;
425         case OB_DIRECTION_SOUTHEAST:
426             switch (config_dock_orient) {
427             case OB_ORIENTATION_HORZ:
428                 STRUT_PARTIAL_SET(dock_strut, 0, 0, 0, strh,
429                                   0, 0, 0, 0, 0, 0,
430                                   dock->x, dock->x + dock->w - 1);
431                 break;
432             case OB_ORIENTATION_VERT:
433                 STRUT_PARTIAL_SET(dock_strut, 0, 0, strw, 0,
434                                   0, 0, 0, 0,
435                                   dock->y, dock->y + dock->h - 1, 0, 0);
436                 break;
437             }
438             break;
439         }
440     }
441
442     dock->w += minw;
443     dock->h += minh;
444
445     /* not used for actually sizing shit */
446     dock->w -= ob_rr_theme->bwidth * 2;
447     dock->h -= ob_rr_theme->bwidth * 2;
448
449     if (dock->w > 0 && dock->h > 0) {
450         XMoveResizeWindow(ob_display, dock->frame,
451                           dock->x, dock->y, dock->w, dock->h);
452
453         RrPaint(dock->a_frame, dock->frame, dock->w, dock->h);
454         XMapWindow(ob_display, dock->frame);
455     } else
456         XUnmapWindow(ob_display, dock->frame);
457
458     /* but they are useful outside of this function! */
459     dock->w += ob_rr_theme->bwidth * 2;
460     dock->h += ob_rr_theme->bwidth * 2;
461
462     screen_update_areas();
463 }
464
465 void dock_app_configure(ObDockApp *app, gint w, gint h)
466 {
467     app->w = w;
468     app->h = h;
469     dock_configure();
470 }
471
472 void dock_app_drag(ObDockApp *app, XMotionEvent *e)
473 {
474     ObDockApp *over = NULL;
475     GList *it;
476     gint x, y;
477     gboolean after;
478     gboolean stop;
479
480     x = e->x_root;
481     y = e->y_root;
482
483     /* are we on top of the dock? */
484     if (!(x >= dock->x &&
485           y >= dock->y &&
486           x < dock->x + dock->w &&
487           y < dock->y + dock->h))
488         return;
489
490     x -= dock->x;
491     y -= dock->y;
492
493     /* which dock app are we on top of? */
494     stop = FALSE;
495     for (it = dock->dock_apps; it; it = it->next) {
496         over = it->data;
497         switch (config_dock_orient) {
498         case OB_ORIENTATION_HORZ:
499             if (x >= over->x && x < over->x + over->w)
500                 stop = TRUE;
501             break;
502         case OB_ORIENTATION_VERT:
503             if (y >= over->y && y < over->y + over->h)
504                 stop = TRUE;
505             break;
506         }
507         /* dont go to it->next! */
508         if (stop) break;
509     }
510     if (!it || app == over) return;
511
512     x -= over->x;
513     y -= over->y;
514
515     switch (config_dock_orient) {
516     case OB_ORIENTATION_HORZ:
517         after = (x > over->w / 2);
518         break;
519     case OB_ORIENTATION_VERT:
520         after = (y > over->h / 2);
521         break;
522     }
523
524     /* remove before doing the it->next! */
525     dock->dock_apps = g_list_remove(dock->dock_apps, app);
526
527     if (after) it = it->next;
528
529     dock->dock_apps = g_list_insert_before(dock->dock_apps, it, app);
530     dock_configure();
531 }
532
533 static gboolean hide_timeout(gpointer data)
534 {
535     /* hide */
536     dock->hidden = TRUE;
537     dock_configure();
538
539     return FALSE; /* don't repeat */
540 }
541
542 void dock_hide(gboolean hide)
543 {
544     if (dock->hidden == hide || !config_dock_hide)
545         return;
546     if (!hide) {
547         /* show */
548         dock->hidden = FALSE;
549         dock_configure();
550
551         /* if was hiding, stop it */
552         ob_main_loop_timeout_remove(ob_main_loop, hide_timeout);
553     } else {
554         ob_main_loop_timeout_add(ob_main_loop, config_dock_hide_timeout * 1000,
555                                  hide_timeout, NULL, NULL);
556     }
557 }