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