]> icculus.org git repositories - dana/openbox.git/blob - openbox/dock.c
be explicit than automake 1.9 is needed
[dana/openbox.git] / openbox / dock.c
1 /* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
2
3    dock.c for the Openbox window manager
4    Copyright (c) 2006        Mikael Magnusson
5    Copyright (c) 2003-2007   Dana Jansens
6
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 2 of the License, or
10    (at your option) any later version.
11
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16
17    See the COPYING file for a copy of the GNU General Public License.
18 */
19
20 #include "debug.h"
21 #include "dock.h"
22 #include "screen.h"
23 #include "config.h"
24 #include "grab.h"
25 #include "openbox.h"
26 #include "render/theme.h"
27 #include "obt/prop.h"
28
29 #define DOCK_EVENT_MASK (ButtonPressMask | ButtonReleaseMask | \
30                          EnterWindowMask | LeaveWindowMask)
31 #define DOCKAPP_EVENT_MASK (StructureNotifyMask)
32 #define DOCK_NOPROPAGATEMASK (ButtonPressMask | ButtonReleaseMask | \
33                               ButtonMotionMask)
34
35 static ObDock *dock;
36
37 StrutPartial dock_strut;
38
39 static void dock_app_grab_button(ObDockApp *app, gboolean grab)
40 {
41     if (grab) {
42         grab_button_full(config_dock_app_move_button,
43                          config_dock_app_move_modifiers, app->icon_win,
44                          ButtonPressMask | ButtonReleaseMask |
45                          ButtonMotionMask,
46                          GrabModeAsync, OB_CURSOR_MOVE);
47     } else {
48         ungrab_button(config_dock_app_move_button,
49                       config_dock_app_move_modifiers, app->icon_win);
50     }
51 }
52
53 void dock_startup(gboolean reconfig)
54 {
55     XSetWindowAttributes attrib;
56
57     if (reconfig) {
58         GList *it;
59
60         XSetWindowBorder(obt_display, dock->frame,
61                          RrColorPixel(ob_rr_theme->osd_border_color));
62         XSetWindowBorderWidth(obt_display, dock->frame, ob_rr_theme->obwidth);
63
64         RrAppearanceFree(dock->a_frame);
65         dock->a_frame = RrAppearanceCopy(ob_rr_theme->osd_hilite_bg);
66
67         stacking_add(DOCK_AS_WINDOW(dock));
68
69         dock_configure();
70         dock_hide(TRUE);
71
72         for (it = dock->dock_apps; it; it = g_list_next(it))
73             dock_app_grab_button(it->data, TRUE);
74         return;
75     }
76
77     STRUT_PARTIAL_SET(dock_strut, 0, 0, 0, 0,
78                       0, 0, 0, 0, 0, 0, 0, 0);
79
80     dock = g_new0(ObDock, 1);
81     dock->obwin.type = OB_WINDOW_CLASS_DOCK;
82
83     dock->hidden = TRUE;
84
85     attrib.event_mask = DOCK_EVENT_MASK;
86     attrib.override_redirect = True;
87     attrib.do_not_propagate_mask = DOCK_NOPROPAGATEMASK;
88     dock->frame = XCreateWindow(obt_display, obt_root(ob_screen),
89                                 0, 0, 1, 1, 0,
90                                 RrDepth(ob_rr_inst), InputOutput,
91                                 RrVisual(ob_rr_inst),
92                                 CWOverrideRedirect | CWEventMask |
93                                 CWDontPropagate,
94                                 &attrib);
95     dock->a_frame = RrAppearanceCopy(ob_rr_theme->osd_hilite_bg);
96     XSetWindowBorder(obt_display, dock->frame,
97                      RrColorPixel(ob_rr_theme->osd_border_color));
98     XSetWindowBorderWidth(obt_display, dock->frame, ob_rr_theme->obwidth);
99
100     /* Setting the window type so xcompmgr can tell what it is */
101     OBT_PROP_SET32(dock->frame, NET_WM_WINDOW_TYPE, ATOM,
102                    OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_DOCK));
103
104     window_add(&dock->frame, DOCK_AS_WINDOW(dock));
105     stacking_add(DOCK_AS_WINDOW(dock));
106 }
107
108 void dock_shutdown(gboolean reconfig)
109 {
110     if (reconfig) {
111         GList *it;
112
113         stacking_remove(DOCK_AS_WINDOW(dock));
114
115         for (it = dock->dock_apps; it; it = g_list_next(it))
116             dock_app_grab_button(it->data, FALSE);
117         return;
118     }
119
120     XDestroyWindow(obt_display, dock->frame);
121     RrAppearanceFree(dock->a_frame);
122     window_remove(dock->frame);
123     stacking_remove(dock);
124 }
125
126 void dock_add(Window win, XWMHints *wmhints)
127 {
128     ObDockApp *app;
129     XWindowAttributes attrib;
130     gchar **data;
131
132     app = g_new0(ObDockApp, 1);
133     app->win = win;
134     app->icon_win = (wmhints->flags & IconWindowHint) ?
135         wmhints->icon_window : win;
136
137     if (OBT_PROP_GETSS(app->win, WM_CLASS, locale, &data)) {
138         if (data[0]) {
139             app->name = g_strdup(data[0]);
140             if (data[1])
141                 app->class = g_strdup(data[1]);
142         }
143         g_strfreev(data);
144     }
145
146     if (app->name == NULL) app->name = g_strdup("");
147     if (app->class == NULL) app->class = g_strdup("");
148
149     if (XGetWindowAttributes(obt_display, app->icon_win, &attrib)) {
150         app->w = attrib.width;
151         app->h = attrib.height;
152     } else {
153         app->w = app->h = 64;
154     }
155
156     dock->dock_apps = g_list_append(dock->dock_apps, app);
157     dock_configure();
158
159     XReparentWindow(obt_display, app->icon_win, dock->frame, app->x, app->y);
160     /*
161       This is the same case as in frame.c for client windows. When Openbox is
162       starting, the window is already mapped so we see unmap events occur for
163       it. There are 2 unmap events generated that we see, one with the 'event'
164       member set the root window, and one set to the client, but both get
165       handled and need to be ignored.
166     */
167     if (ob_state() == OB_STATE_STARTING)
168         app->ignore_unmaps += 2;
169
170     if (app->win != app->icon_win) {
171         /* have to map it so that it can be re-managed on a restart */
172         XMoveWindow(obt_display, app->win, -1000, -1000);
173         XMapWindow(obt_display, app->win);
174     }
175     XMapWindow(obt_display, app->icon_win);
176     XSync(obt_display, False);
177
178     /* specify that if we exit, the window should not be destroyed and should
179        be reparented back to root automatically */
180     XChangeSaveSet(obt_display, app->icon_win, SetModeInsert);
181     XSelectInput(obt_display, app->icon_win, DOCKAPP_EVENT_MASK);
182
183     dock_app_grab_button(app, TRUE);
184
185     ob_debug("Managed Dock App: 0x%lx (%s)\n", app->icon_win, app->class);
186 }
187
188 void dock_remove_all(void)
189 {
190     while (dock->dock_apps)
191         dock_remove(dock->dock_apps->data, TRUE);
192 }
193
194 void dock_remove(ObDockApp *app, gboolean reparent)
195 {
196     dock_app_grab_button(app, FALSE);
197     XSelectInput(obt_display, app->icon_win, NoEventMask);
198     /* remove the window from our save set */
199     XChangeSaveSet(obt_display, app->icon_win, SetModeDelete);
200     XSync(obt_display, False);
201
202     if (reparent)
203         XReparentWindow(obt_display, app->icon_win,
204                         obt_root(ob_screen), app->x, app->y);
205
206     dock->dock_apps = g_list_remove(dock->dock_apps, app);
207     dock_configure();
208
209     ob_debug("Unmanaged Dock App: 0x%lx (%s)\n", app->icon_win, app->class);
210
211     g_free(app->name);
212     g_free(app->class);
213     g_free(app);
214 }
215
216 void dock_configure(void)
217 {
218     GList *it;
219     gint hspot, vspot;
220     gint gravity;
221     gint l, r, t, b;
222     gint strw, strh;
223     Rect *a;
224     gint hidesize;
225
226     RrMargins(dock->a_frame, &l, &t, &r, &b);
227     hidesize = MAX(1, ob_rr_theme->obwidth);
228
229     dock->area.width = dock->area.height = 0;
230
231     /* get the size */
232     for (it = dock->dock_apps; it; it = g_list_next(it)) {
233         ObDockApp *app = it->data;
234         switch (config_dock_orient) {
235         case OB_ORIENTATION_HORZ:
236             dock->area.width += app->w;
237             dock->area.height = MAX(dock->area.height, app->h);
238             break;
239         case OB_ORIENTATION_VERT:
240             dock->area.width = MAX(dock->area.width, app->w);
241             dock->area.height += app->h;
242             break;
243         }
244     }
245
246     dock->area.width += l + r;
247     dock->area.height += t + b;
248
249     hspot = l;
250     vspot = t;
251
252     /* position the apps */
253     for (it = dock->dock_apps; it; it = g_list_next(it)) {
254         ObDockApp *app = it->data;
255         switch (config_dock_orient) {
256         case OB_ORIENTATION_HORZ:
257             app->x = hspot;
258             app->y = (dock->area.height - app->h) / 2;
259             hspot += app->w;
260             break;
261         case OB_ORIENTATION_VERT:
262             app->x = (dock->area.width - app->w) / 2;
263             app->y = vspot;
264             vspot += app->h;
265             break;
266         }
267
268         XMoveWindow(obt_display, app->icon_win, app->x, app->y);
269     }
270
271     /* used for calculating offsets */
272     dock->area.width += ob_rr_theme->obwidth * 2;
273     dock->area.height += ob_rr_theme->obwidth * 2;
274
275     a = screen_physical_area_all_monitors();
276
277     /* calculate position */
278     if (config_dock_floating) {
279         dock->area.x = config_dock_x;
280         dock->area.y = config_dock_y;
281         gravity = NorthWestGravity;
282     } else {
283         switch (config_dock_pos) {
284         case OB_DIRECTION_NORTHWEST:
285             dock->area.x = 0;
286             dock->area.y = 0;
287             gravity = NorthWestGravity;
288             break;
289         case OB_DIRECTION_NORTH:
290             dock->area.x = a->width / 2;
291             dock->area.y = 0;
292             gravity = NorthGravity;
293             break;
294         case OB_DIRECTION_NORTHEAST:
295             dock->area.x = a->width;
296             dock->area.y = 0;
297             gravity = NorthEastGravity;
298             break;
299         case OB_DIRECTION_WEST:
300             dock->area.x = 0;
301             dock->area.y = a->height / 2;
302             gravity = WestGravity;
303             break;
304         case OB_DIRECTION_EAST:
305             dock->area.x = a->width;
306             dock->area.y = a->height / 2;
307             gravity = EastGravity;
308             break;
309         case OB_DIRECTION_SOUTHWEST:
310             dock->area.x = 0;
311             dock->area.y = a->height;
312             gravity = SouthWestGravity;
313             break;
314         case OB_DIRECTION_SOUTH:
315             dock->area.x = a->width / 2;
316             dock->area.y = a->height;
317             gravity = SouthGravity;
318             break;
319         case OB_DIRECTION_SOUTHEAST:
320             dock->area.x = a->width;
321             dock->area.y = a->height;
322             gravity = SouthEastGravity;
323             break;
324         default:
325             g_assert_not_reached();
326         }
327     }
328
329     switch(gravity) {
330     case NorthGravity:
331     case CenterGravity:
332     case SouthGravity:
333         dock->area.x -= dock->area.width / 2;
334         break;
335     case NorthEastGravity:
336     case EastGravity:
337     case SouthEastGravity:
338         dock->area.x -= dock->area.width;
339         break;
340     }
341     switch(gravity) {
342     case WestGravity:
343     case CenterGravity:
344     case EastGravity:
345         dock->area.y -= dock->area.height / 2;
346         break;
347     case SouthWestGravity:
348     case SouthGravity:
349     case SouthEastGravity:
350         dock->area.y -= dock->area.height;
351         break;
352     }
353
354     if (config_dock_hide && dock->hidden) {
355         if (!config_dock_floating) {
356             switch (config_dock_pos) {
357             case OB_DIRECTION_NORTHWEST:
358                 switch (config_dock_orient) {
359                 case OB_ORIENTATION_HORZ:
360                     dock->area.y -= dock->area.height - hidesize;
361                     break;
362                 case OB_ORIENTATION_VERT:
363                     dock->area.x -= dock->area.width - hidesize;
364                     break;
365                 }
366                 break;
367             case OB_DIRECTION_NORTH:
368                 dock->area.y -= dock->area.height - hidesize;
369                 break;
370             case OB_DIRECTION_NORTHEAST:
371                 switch (config_dock_orient) {
372                 case OB_ORIENTATION_HORZ:
373                     dock->area.y -= dock->area.height - hidesize;
374                     break;
375                 case OB_ORIENTATION_VERT:
376                     dock->area.x += dock->area.width - hidesize;
377                     break;
378                 }
379                 break;
380             case OB_DIRECTION_WEST:
381                 dock->area.x -= dock->area.width - hidesize;
382                 break;
383             case OB_DIRECTION_EAST:
384                 dock->area.x += dock->area.width - hidesize;
385                 break;
386             case OB_DIRECTION_SOUTHWEST:
387                 switch (config_dock_orient) {
388                 case OB_ORIENTATION_HORZ:
389                     dock->area.y += dock->area.height - hidesize;
390                     break;
391                 case OB_ORIENTATION_VERT:
392                     dock->area.x -= dock->area.width - hidesize;
393                     break;
394                 } break;
395             case OB_DIRECTION_SOUTH:
396                 dock->area.y += dock->area.height - hidesize;
397                 break;
398             case OB_DIRECTION_SOUTHEAST:
399                 switch (config_dock_orient) {
400                 case OB_ORIENTATION_HORZ:
401                     dock->area.y += dock->area.height - hidesize;
402                     break;
403                 case OB_ORIENTATION_VERT:
404                     dock->area.x += dock->area.width - hidesize;
405                     break;
406                 }
407                 break;
408             }
409         }
410     }
411
412     if (!config_dock_floating && config_dock_hide) {
413         strw = hidesize;
414         strh = hidesize;
415     } else {
416         strw = dock->area.width;
417         strh = dock->area.height;
418     }
419
420     /* set the strut */
421     if (!dock->dock_apps) {
422         STRUT_PARTIAL_SET(dock_strut, 0, 0, 0, 0,
423                           0, 0, 0, 0, 0, 0, 0, 0);
424     } else if (config_dock_floating || config_dock_nostrut)
425     {
426         STRUT_PARTIAL_SET(dock_strut, 0, 0, 0, 0,
427                           0, 0, 0, 0, 0, 0, 0, 0);
428     } else {
429         switch (config_dock_pos) {
430         case OB_DIRECTION_NORTHWEST:
431             switch (config_dock_orient) {
432             case OB_ORIENTATION_HORZ:
433                 STRUT_PARTIAL_SET(dock_strut, 0, strh, 0, 0,
434                                   0, 0, dock->area.x, dock->area.x
435                                   + dock->area.width - 1, 0, 0, 0, 0);
436                 break;
437             case OB_ORIENTATION_VERT:
438                 STRUT_PARTIAL_SET(dock_strut, strw, 0, 0, 0,
439                                   dock->area.y, dock->area.y
440                                   + dock->area.height - 1, 0, 0, 0, 0, 0, 0);
441                 break;
442             }
443             break;
444         case OB_DIRECTION_NORTH:
445             STRUT_PARTIAL_SET(dock_strut, 0, strh, 0, 0,
446                               0, 0, dock->area.x, dock->area.x
447                               + dock->area.width - 1, 0, 0, 0, 0);
448             break;
449         case OB_DIRECTION_NORTHEAST:
450             switch (config_dock_orient) {
451             case OB_ORIENTATION_HORZ:
452                 STRUT_PARTIAL_SET(dock_strut, 0, strh, 0, 0,
453                                   0, 0, dock->area.x, dock->area.x
454                                   + dock->area.width -1, 0, 0, 0, 0);
455                 break;
456             case OB_ORIENTATION_VERT:
457                 STRUT_PARTIAL_SET(dock_strut, 0, 0, strw, 0,
458                                   0, 0, 0, 0, dock->area.y, dock->area.y
459                                   + dock->area.height - 1, 0, 0);
460                 break;
461             }
462             break;
463         case OB_DIRECTION_WEST:
464             STRUT_PARTIAL_SET(dock_strut, strw, 0, 0, 0,
465                               dock->area.y, dock->area.y
466                               + dock->area.height - 1, 0, 0, 0, 0, 0, 0);
467             break;
468         case OB_DIRECTION_EAST:
469             STRUT_PARTIAL_SET(dock_strut, 0, 0, strw, 0,
470                               0, 0, 0, 0, dock->area.y, dock->area.y
471                               + dock->area.height - 1, 0, 0);
472             break;
473         case OB_DIRECTION_SOUTHWEST:
474             switch (config_dock_orient) {
475             case OB_ORIENTATION_HORZ:
476                 STRUT_PARTIAL_SET(dock_strut, 0, 0, 0, strh,
477                                   0, 0, 0, 0, 0, 0, dock->area.x, dock->area.x
478                                   + dock->area.width - 1);
479                 break;
480             case OB_ORIENTATION_VERT:
481                 STRUT_PARTIAL_SET(dock_strut, strw, 0, 0, 0,
482                                   dock->area.y, dock->area.y
483                                   + dock->area.height - 1, 0, 0, 0, 0, 0, 0);
484                 break;
485             }
486             break;
487         case OB_DIRECTION_SOUTH:
488             STRUT_PARTIAL_SET(dock_strut, 0, 0, 0, strh,
489                               0, 0, 0, 0, 0, 0, dock->area.x, dock->area.x
490                               + dock->area.width - 1);
491             break;
492         case OB_DIRECTION_SOUTHEAST:
493             switch (config_dock_orient) {
494             case OB_ORIENTATION_HORZ:
495                 STRUT_PARTIAL_SET(dock_strut, 0, 0, 0, strh,
496                                   0, 0, 0, 0, 0, 0, dock->area.x,
497                                   dock->area.x + dock->area.width - 1);
498                 break;
499             case OB_ORIENTATION_VERT:
500                 STRUT_PARTIAL_SET(dock_strut, 0, 0, strw, 0,
501                                   0, 0, 0, 0, dock->area.y, dock->area.y
502                                   + dock->area.height - 1, 0, 0);
503                 break;
504             }
505             break;
506         }
507     }
508
509     /* not used for actually sizing shit */
510     dock->area.width -= ob_rr_theme->obwidth * 2;
511     dock->area.height -= ob_rr_theme->obwidth * 2;
512
513     if (dock->dock_apps) {
514         g_assert(dock->area.width > 0);
515         g_assert(dock->area.height > 0);
516
517         XMoveResizeWindow(obt_display, dock->frame, dock->area.x, dock->area.y,
518                           dock->area.width, dock->area.height);
519
520         RrPaint(dock->a_frame, dock->frame, dock->area.width,
521                 dock->area.height);
522         XMapWindow(obt_display, dock->frame);
523     } else
524         XUnmapWindow(obt_display, dock->frame);
525
526     /* but they are useful outside of this function! */
527     dock->area.width += ob_rr_theme->obwidth * 2;
528     dock->area.height += ob_rr_theme->obwidth * 2;
529
530     screen_update_areas();
531
532     g_free(a);
533 }
534
535 void dock_app_configure(ObDockApp *app, gint w, gint h)
536 {
537     app->w = w;
538     app->h = h;
539     dock_configure();
540 }
541
542 void dock_app_drag(ObDockApp *app, XMotionEvent *e)
543 {
544     ObDockApp *over = NULL;
545     GList *it;
546     gint x, y;
547     gboolean after;
548     gboolean stop;
549
550     x = e->x_root;
551     y = e->y_root;
552
553     /* are we on top of the dock? */
554     if (!(x >= dock->area.x &&
555           y >= dock->area.y &&
556           x < dock->area.x + dock->area.width &&
557           y < dock->area.y + dock->area.height))
558         return;
559
560     x -= dock->area.x;
561     y -= dock->area.y;
562
563     /* which dock app are we on top of? */
564     stop = FALSE;
565     for (it = dock->dock_apps; it; it = g_list_next(it)) {
566         over = it->data;
567         switch (config_dock_orient) {
568         case OB_ORIENTATION_HORZ:
569             if (x >= over->x && x < over->x + over->w)
570                 stop = TRUE;
571             break;
572         case OB_ORIENTATION_VERT:
573             if (y >= over->y && y < over->y + over->h)
574                 stop = TRUE;
575             break;
576         }
577         /* dont go to it->next! */
578         if (stop) break;
579     }
580     if (!it || app == over) return;
581
582     x -= over->x;
583     y -= over->y;
584
585     switch (config_dock_orient) {
586     case OB_ORIENTATION_HORZ:
587         after = (x > over->w / 2);
588         break;
589     case OB_ORIENTATION_VERT:
590         after = (y > over->h / 2);
591         break;
592     default:
593         g_assert_not_reached();
594     }
595
596     /* remove before doing the it->next! */
597     dock->dock_apps = g_list_remove(dock->dock_apps, app);
598
599     if (after) it = it->next;
600
601     dock->dock_apps = g_list_insert_before(dock->dock_apps, it, app);
602     dock_configure();
603 }
604
605 static gboolean hide_timeout(gpointer data)
606 {
607     /* hide */
608     dock->hidden = TRUE;
609     dock_configure();
610
611     return FALSE; /* don't repeat */
612 }
613
614 static gboolean show_timeout(gpointer data)
615 {
616     /* hide */
617     dock->hidden = FALSE;
618     dock_configure();
619
620     return FALSE; /* don't repeat */
621 }
622
623 void dock_hide(gboolean hide)
624 {
625     if (!hide) {
626         if (dock->hidden && config_dock_hide) {
627             obt_main_loop_timeout_add(ob_main_loop,
628                                       config_dock_show_delay * 1000,
629                                       show_timeout, NULL,
630                                       g_direct_equal, NULL);
631         } else if (!dock->hidden && config_dock_hide) {
632             obt_main_loop_timeout_remove(ob_main_loop, hide_timeout);
633         }
634     } else {
635         if (!dock->hidden && config_dock_hide) {
636             obt_main_loop_timeout_add(ob_main_loop,
637                                       config_dock_hide_delay * 1000,
638                                       hide_timeout, NULL,
639                                       g_direct_equal, NULL);
640         } else if (dock->hidden && config_dock_hide) {
641             obt_main_loop_timeout_remove(ob_main_loop, show_timeout);
642         }
643     }
644 }
645
646 void dock_get_area(Rect *a)
647 {
648     RECT_SET(*a, dock->area.x, dock->area.y,
649              dock->area.width, dock->area.height);
650 }
651
652 ObDockApp* dock_find_dockapp(Window xwin)
653 {
654     GList *it;
655     /* there are never that many dock apps, so we can use a list here instead
656        of a hash table */
657     for (it = dock->dock_apps; it; it = g_list_next(it)) {
658         ObDockApp *app = it->data;
659         if (app->icon_win == xwin)
660             return app;
661     }
662     return NULL;
663 }