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