dont set a strut when there are no apps in the dock
[mikachu/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 (!dock->dock_apps) {
359         STRUT_PARTIAL_SET(dock_strut, 0, 0, 0, 0,
360                           0, 0, 0, 0, 0, 0, 0, 0);
361     } else if (config_dock_floating) {
362         STRUT_PARTIAL_SET(dock_strut, 0, 0, 0, 0,
363                           0, 0, 0, 0, 0, 0, 0, 0);
364     } else {
365         switch (config_dock_pos) {
366         case OB_DIRECTION_NORTHWEST:
367             switch (config_dock_orient) {
368             case OB_ORIENTATION_HORZ:
369                 STRUT_PARTIAL_SET(dock_strut, 0, strh, 0, 0,
370                                   0, 0, dock->x, dock->x + dock->w - 1,
371                                   0, 0, 0, 0);
372                 break;
373             case OB_ORIENTATION_VERT:
374                 STRUT_PARTIAL_SET(dock_strut, strw, 0, 0, 0,
375                                   dock->y, dock->y + dock->h - 1,
376                                   0, 0, 0, 0, 0, 0);
377                 break;
378             }
379             break;
380         case OB_DIRECTION_NORTH:
381             STRUT_PARTIAL_SET(dock_strut, 0, strh, 0, 0,
382                               dock->x, dock->x + dock->w - 1,
383                               0, 0, 0, 0, 0, 0);
384             break;
385         case OB_DIRECTION_NORTHEAST:
386             switch (config_dock_orient) {
387             case OB_ORIENTATION_HORZ:
388                 STRUT_PARTIAL_SET(dock_strut, 0, strh, 0, 0,
389                                   0, 0, dock->x, dock->x + dock->w -1,
390                                   0, 0, 0, 0);
391                 break;
392             case OB_ORIENTATION_VERT:
393                 STRUT_PARTIAL_SET(dock_strut, 0, 0, strw, 0,
394                                   0, 0, 0, 0,
395                                   dock->y, dock->y + dock->h - 1, 0, 0);
396                 break;
397             }
398             break;
399         case OB_DIRECTION_WEST:
400             STRUT_PARTIAL_SET(dock_strut, strw, 0, 0, 0,
401                               dock->y, dock->y + dock->h - 1,
402                               0, 0, 0, 0, 0, 0);
403             break;
404         case OB_DIRECTION_EAST:
405             STRUT_PARTIAL_SET(dock_strut, 0, 0, strw, 0,
406                               0, 0, 0, 0,
407                               dock->y, dock->y + dock->h - 1, 0, 0);
408             break;
409         case OB_DIRECTION_SOUTHWEST:
410             switch (config_dock_orient) {
411             case OB_ORIENTATION_HORZ:
412                 STRUT_PARTIAL_SET(dock_strut, 0, 0, 0, strh,
413                                   0, 0, 0, 0, 0, 0,
414                                   dock->x, dock->x + dock->w - 1);
415                 break;
416             case OB_ORIENTATION_VERT:
417                 STRUT_PARTIAL_SET(dock_strut, strw, 0, 0, 0,
418                                   dock->y, dock->y + dock->h - 1,
419                                   0, 0, 0, 0, 0, 0);
420                 break;
421             }
422             break;
423         case OB_DIRECTION_SOUTH:
424             STRUT_PARTIAL_SET(dock_strut, 0, 0, 0, strh,
425                               0, 0, 0, 0, 0, 0,
426                               dock->x, dock->x + dock->w - 1);
427             break;
428         case OB_DIRECTION_SOUTHEAST:
429             switch (config_dock_orient) {
430             case OB_ORIENTATION_HORZ:
431                 STRUT_PARTIAL_SET(dock_strut, 0, 0, 0, strh,
432                                   0, 0, 0, 0, 0, 0,
433                                   dock->x, dock->x + dock->w - 1);
434                 break;
435             case OB_ORIENTATION_VERT:
436                 STRUT_PARTIAL_SET(dock_strut, 0, 0, strw, 0,
437                                   0, 0, 0, 0,
438                                   dock->y, dock->y + dock->h - 1, 0, 0);
439                 break;
440             }
441             break;
442         }
443     }
444
445     dock->w += minw;
446     dock->h += minh;
447
448     /* not used for actually sizing shit */
449     dock->w -= ob_rr_theme->bwidth * 2;
450     dock->h -= ob_rr_theme->bwidth * 2;
451
452     if (dock->dock_apps) {
453         g_assert(dock->w > 0);
454         g_assert(dock->h > 0);
455
456         XMoveResizeWindow(ob_display, dock->frame,
457                           dock->x, dock->y, dock->w, dock->h);
458
459         RrPaint(dock->a_frame, dock->frame, dock->w, dock->h);
460         XMapWindow(ob_display, dock->frame);
461     } else
462         XUnmapWindow(ob_display, dock->frame);
463
464     /* but they are useful outside of this function! */
465     dock->w += ob_rr_theme->bwidth * 2;
466     dock->h += ob_rr_theme->bwidth * 2;
467
468     screen_update_areas();
469 }
470
471 void dock_app_configure(ObDockApp *app, gint w, gint h)
472 {
473     app->w = w;
474     app->h = h;
475     dock_configure();
476 }
477
478 void dock_app_drag(ObDockApp *app, XMotionEvent *e)
479 {
480     ObDockApp *over = NULL;
481     GList *it;
482     gint x, y;
483     gboolean after;
484     gboolean stop;
485
486     x = e->x_root;
487     y = e->y_root;
488
489     /* are we on top of the dock? */
490     if (!(x >= dock->x &&
491           y >= dock->y &&
492           x < dock->x + dock->w &&
493           y < dock->y + dock->h))
494         return;
495
496     x -= dock->x;
497     y -= dock->y;
498
499     /* which dock app are we on top of? */
500     stop = FALSE;
501     for (it = dock->dock_apps; it; it = it->next) {
502         over = it->data;
503         switch (config_dock_orient) {
504         case OB_ORIENTATION_HORZ:
505             if (x >= over->x && x < over->x + over->w)
506                 stop = TRUE;
507             break;
508         case OB_ORIENTATION_VERT:
509             if (y >= over->y && y < over->y + over->h)
510                 stop = TRUE;
511             break;
512         }
513         /* dont go to it->next! */
514         if (stop) break;
515     }
516     if (!it || app == over) return;
517
518     x -= over->x;
519     y -= over->y;
520
521     switch (config_dock_orient) {
522     case OB_ORIENTATION_HORZ:
523         after = (x > over->w / 2);
524         break;
525     case OB_ORIENTATION_VERT:
526         after = (y > over->h / 2);
527         break;
528     }
529
530     /* remove before doing the it->next! */
531     dock->dock_apps = g_list_remove(dock->dock_apps, app);
532
533     if (after) it = it->next;
534
535     dock->dock_apps = g_list_insert_before(dock->dock_apps, it, app);
536     dock_configure();
537 }
538
539 static gboolean hide_timeout(gpointer data)
540 {
541     /* hide */
542     dock->hidden = TRUE;
543     dock_configure();
544
545     return FALSE; /* don't repeat */
546 }
547
548 void dock_hide(gboolean hide)
549 {
550     if (dock->hidden == hide || !config_dock_hide)
551         return;
552     if (!hide) {
553         /* show */
554         dock->hidden = FALSE;
555         dock_configure();
556
557         /* if was hiding, stop it */
558         ob_main_loop_timeout_remove(ob_main_loop, hide_timeout);
559     } else {
560         ob_main_loop_timeout_add(ob_main_loop, config_dock_hide_timeout * 1000,
561                                  hide_timeout, NULL, NULL);
562     }
563 }