]> icculus.org git repositories - mikachu/openbox.git/blob - openbox/dock.c
let you specify the padding y and x independently
[mikachu/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 "mainloop.h"
23 #include "screen.h"
24 #include "prop.h"
25 #include "config.h"
26 #include "grab.h"
27 #include "openbox.h"
28 #include "render/theme.h"
29
30 #define DOCK_EVENT_MASK (ButtonPressMask | ButtonReleaseMask | \
31                          EnterWindowMask | LeaveWindowMask)
32 #define DOCKAPP_EVENT_MASK (StructureNotifyMask)
33 #define DOCK_NOPROPAGATEMASK (ButtonPressMask | ButtonReleaseMask | \
34                               ButtonMotionMask)
35
36 static ObDock *dock;
37
38 StrutPartial dock_strut;
39
40 static void dock_app_grab_button(ObDockApp *app, gboolean grab)
41 {
42     if (grab) {
43         grab_button_full(config_dock_app_move_button,
44                          config_dock_app_move_modifiers, app->icon_win,
45                          ButtonPressMask | ButtonReleaseMask |
46                          ButtonMotionMask,
47                          GrabModeAsync, OB_CURSOR_MOVE);
48     } else {
49         ungrab_button(config_dock_app_move_button,
50                       config_dock_app_move_modifiers, app->icon_win);
51     }
52 }
53
54 void dock_startup(gboolean reconfig)
55 {
56     XSetWindowAttributes attrib;
57
58     if (reconfig) {
59         GList *it;
60
61         XSetWindowBorder(ob_display, dock->frame,
62                          RrColorPixel(ob_rr_theme->osd_border_color));
63         XSetWindowBorderWidth(ob_display, dock->frame, ob_rr_theme->obwidth);
64
65         RrAppearanceFree(dock->a_frame);
66         dock->a_frame = RrAppearanceCopy(ob_rr_theme->osd_hilite_bg);
67
68         stacking_add(DOCK_AS_WINDOW(dock));
69
70         dock_configure();
71         dock_hide(TRUE);
72
73         for (it = dock->dock_apps; it; it = g_list_next(it))
74             dock_app_grab_button(it->data, TRUE);
75         return;
76     }
77
78     STRUT_PARTIAL_SET(dock_strut, 0, 0, 0, 0,
79                       0, 0, 0, 0, 0, 0, 0, 0);
80
81     dock = g_new0(ObDock, 1);
82     dock->obwin.type = Window_Dock;
83
84     dock->hidden = TRUE;
85
86     attrib.event_mask = DOCK_EVENT_MASK;
87     attrib.override_redirect = True;
88     attrib.do_not_propagate_mask = DOCK_NOPROPAGATEMASK;
89     dock->frame = XCreateWindow(ob_display, RootWindow(ob_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(ob_display, dock->frame,
98                      RrColorPixel(ob_rr_theme->osd_border_color));
99     XSetWindowBorderWidth(ob_display, dock->frame, ob_rr_theme->obwidth);
100
101     /* Setting the window type so xcompmgr can tell what it is */
102     PROP_SET32(dock->frame, net_wm_window_type, atom,
103                prop_atoms.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(ob_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 (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(ob_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(ob_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(ob_display, app->win, -1000, -1000);
175         XMapWindow(ob_display, app->win);
176     }
177     XMapWindow(ob_display, app->icon_win);
178     XSync(ob_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(ob_display, app->icon_win, SetModeInsert);
183     XSelectInput(ob_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()
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(ob_display, app->icon_win, NoEventMask);
202     /* remove the window from our save set */
203     XChangeSaveSet(ob_display, app->icon_win, SetModeDelete);
204     XSync(ob_display, False);
205
206     g_hash_table_remove(window_map, &app->icon_win);
207
208     if (reparent)
209         XReparentWindow(ob_display, app->icon_win,
210                         RootWindow(ob_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()
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
231     RrMargins(dock->a_frame, &l, &t, &r, &b);
232
233     dock->area.width = dock->area.height = 0;
234
235     /* get the size */
236     for (it = dock->dock_apps; it; it = g_list_next(it)) {
237         ObDockApp *app = it->data;
238         switch (config_dock_orient) {
239         case OB_ORIENTATION_HORZ:
240             dock->area.width += app->w;
241             dock->area.height = MAX(dock->area.height, app->h);
242             break;
243         case OB_ORIENTATION_VERT:
244             dock->area.width = MAX(dock->area.width, app->w);
245             dock->area.height += app->h;
246             break;
247         }
248     }
249
250     dock->area.width += l + r;
251     dock->area.height += t + b;
252
253     hspot = l;
254     vspot = t;
255
256     /* position the apps */
257     for (it = dock->dock_apps; it; it = g_list_next(it)) {
258         ObDockApp *app = it->data;
259         switch (config_dock_orient) {
260         case OB_ORIENTATION_HORZ:
261             app->x = hspot;
262             app->y = (dock->area.height - app->h) / 2;
263             hspot += app->w;
264             break;
265         case OB_ORIENTATION_VERT:
266             app->x = (dock->area.width - app->w) / 2;
267             app->y = vspot;
268             vspot += app->h;
269             break;
270         }
271
272         XMoveWindow(ob_display, app->icon_win, app->x, app->y);
273     }
274
275     /* used for calculating offsets */
276     dock->area.width += ob_rr_theme->obwidth * 2;
277     dock->area.height += ob_rr_theme->obwidth * 2;
278
279     a = screen_physical_area_all_monitors();
280
281     /* calculate position */
282     if (config_dock_floating) {
283         dock->area.x = config_dock_x;
284         dock->area.y = config_dock_y;
285         gravity = NorthWestGravity;
286     } else {
287         switch (config_dock_pos) {
288         case OB_DIRECTION_NORTHWEST:
289             dock->area.x = 0;
290             dock->area.y = 0;
291             gravity = NorthWestGravity;
292             break;
293         case OB_DIRECTION_NORTH:
294             dock->area.x = a->width / 2;
295             dock->area.y = 0;
296             gravity = NorthGravity;
297             break;
298         case OB_DIRECTION_NORTHEAST:
299             dock->area.x = a->width;
300             dock->area.y = 0;
301             gravity = NorthEastGravity;
302             break;
303         case OB_DIRECTION_WEST:
304             dock->area.x = 0;
305             dock->area.y = a->height / 2;
306             gravity = WestGravity;
307             break;
308         case OB_DIRECTION_EAST:
309             dock->area.x = a->width;
310             dock->area.y = a->height / 2;
311             gravity = EastGravity;
312             break;
313         case OB_DIRECTION_SOUTHWEST:
314             dock->area.x = 0;
315             dock->area.y = a->height;
316             gravity = SouthWestGravity;
317             break;
318         case OB_DIRECTION_SOUTH:
319             dock->area.x = a->width / 2;
320             dock->area.y = a->height;
321             gravity = SouthGravity;
322             break;
323         case OB_DIRECTION_SOUTHEAST:
324             dock->area.x = a->width;
325             dock->area.y = a->height;
326             gravity = SouthEastGravity;
327             break;
328         default:
329             g_assert_not_reached();
330         }
331     }
332
333     switch(gravity) {
334     case NorthGravity:
335     case CenterGravity:
336     case SouthGravity:
337         dock->area.x -= dock->area.width / 2;
338         break;
339     case NorthEastGravity:
340     case EastGravity:
341     case SouthEastGravity:
342         dock->area.x -= dock->area.width;
343         break;
344     }
345     switch(gravity) {
346     case WestGravity:
347     case CenterGravity:
348     case EastGravity:
349         dock->area.y -= dock->area.height / 2;
350         break;
351     case SouthWestGravity:
352     case SouthGravity:
353     case SouthEastGravity:
354         dock->area.y -= dock->area.height;
355         break;
356     }
357
358     if (config_dock_hide && dock->hidden) {
359         if (!config_dock_floating) {
360             switch (config_dock_pos) {
361             case OB_DIRECTION_NORTHWEST:
362                 switch (config_dock_orient) {
363                 case OB_ORIENTATION_HORZ:
364                     dock->area.y -= dock->area.height - ob_rr_theme->obwidth;
365                     break;
366                 case OB_ORIENTATION_VERT:
367                     dock->area.x -= dock->area.width - ob_rr_theme->obwidth;
368                     break;
369                 }
370                 break;
371             case OB_DIRECTION_NORTH:
372                 dock->area.y -= dock->area.height - ob_rr_theme->obwidth;
373                 break;
374             case OB_DIRECTION_NORTHEAST:
375                 switch (config_dock_orient) {
376                 case OB_ORIENTATION_HORZ:
377                     dock->area.y -= dock->area.height - ob_rr_theme->obwidth;
378                     break;
379                 case OB_ORIENTATION_VERT:
380                     dock->area.x += dock->area.width - ob_rr_theme->obwidth;
381                     break;
382                 }
383                 break;
384             case OB_DIRECTION_WEST:
385                 dock->area.x -= dock->area.width - ob_rr_theme->obwidth;
386                 break;
387             case OB_DIRECTION_EAST:
388                 dock->area.x += dock->area.width - ob_rr_theme->obwidth;
389                 break;
390             case OB_DIRECTION_SOUTHWEST:
391                 switch (config_dock_orient) {
392                 case OB_ORIENTATION_HORZ:
393                     dock->area.y += dock->area.height - ob_rr_theme->obwidth;
394                     break;
395                 case OB_ORIENTATION_VERT:
396                     dock->area.x -= dock->area.width - ob_rr_theme->obwidth;
397                     break;
398                 } break;
399             case OB_DIRECTION_SOUTH:
400                 dock->area.y += dock->area.height - ob_rr_theme->obwidth;
401                 break;
402             case OB_DIRECTION_SOUTHEAST:
403                 switch (config_dock_orient) {
404                 case OB_ORIENTATION_HORZ:
405                     dock->area.y += dock->area.height - ob_rr_theme->obwidth;
406                     break;
407                 case OB_ORIENTATION_VERT:
408                     dock->area.x += dock->area.width - ob_rr_theme->obwidth;
409                     break;
410                 }
411                 break;
412             }
413         }
414     }
415
416     if (!config_dock_floating && config_dock_hide) {
417         strw = ob_rr_theme->obwidth;
418         strh = ob_rr_theme->obwidth;
419     } else {
420         strw = dock->area.width;
421         strh = dock->area.height;
422     }
423
424     /* set the strut */
425     if (!dock->dock_apps) {
426         STRUT_PARTIAL_SET(dock_strut, 0, 0, 0, 0,
427                           0, 0, 0, 0, 0, 0, 0, 0);
428     } else if (config_dock_floating || config_dock_nostrut)
429     {
430         STRUT_PARTIAL_SET(dock_strut, 0, 0, 0, 0,
431                           0, 0, 0, 0, 0, 0, 0, 0);
432     } else {
433         switch (config_dock_pos) {
434         case OB_DIRECTION_NORTHWEST:
435             switch (config_dock_orient) {
436             case OB_ORIENTATION_HORZ:
437                 STRUT_PARTIAL_SET(dock_strut, 0, strh, 0, 0,
438                                   0, 0, dock->area.x, dock->area.x
439                                   + dock->area.width - 1, 0, 0, 0, 0);
440                 break;
441             case OB_ORIENTATION_VERT:
442                 STRUT_PARTIAL_SET(dock_strut, strw, 0, 0, 0,
443                                   dock->area.y, dock->area.y
444                                   + dock->area.height - 1, 0, 0, 0, 0, 0, 0);
445                 break;
446             }
447             break;
448         case OB_DIRECTION_NORTH:
449             STRUT_PARTIAL_SET(dock_strut, 0, strh, 0, 0,
450                               0, 0, dock->area.x, dock->area.x
451                               + dock->area.width - 1, 0, 0, 0, 0);
452             break;
453         case OB_DIRECTION_NORTHEAST:
454             switch (config_dock_orient) {
455             case OB_ORIENTATION_HORZ:
456                 STRUT_PARTIAL_SET(dock_strut, 0, strh, 0, 0,
457                                   0, 0, dock->area.x, dock->area.x
458                                   + dock->area.width -1, 0, 0, 0, 0);
459                 break;
460             case OB_ORIENTATION_VERT:
461                 STRUT_PARTIAL_SET(dock_strut, 0, 0, strw, 0,
462                                   0, 0, 0, 0, dock->area.y, dock->area.y
463                                   + dock->area.height - 1, 0, 0);
464                 break;
465             }
466             break;
467         case OB_DIRECTION_WEST:
468             STRUT_PARTIAL_SET(dock_strut, strw, 0, 0, 0,
469                               dock->area.y, dock->area.y
470                               + dock->area.height - 1, 0, 0, 0, 0, 0, 0);
471             break;
472         case OB_DIRECTION_EAST:
473             STRUT_PARTIAL_SET(dock_strut, 0, 0, strw, 0,
474                               0, 0, 0, 0, dock->area.y, dock->area.y
475                               + dock->area.height - 1, 0, 0);
476             break;
477         case OB_DIRECTION_SOUTHWEST:
478             switch (config_dock_orient) {
479             case OB_ORIENTATION_HORZ:
480                 STRUT_PARTIAL_SET(dock_strut, 0, 0, 0, strh,
481                                   0, 0, 0, 0, 0, 0, dock->area.x, dock->area.x
482                                   + dock->area.width - 1);
483                 break;
484             case OB_ORIENTATION_VERT:
485                 STRUT_PARTIAL_SET(dock_strut, strw, 0, 0, 0,
486                                   dock->area.y, dock->area.y
487                                   + dock->area.height - 1, 0, 0, 0, 0, 0, 0);
488                 break;
489             }
490             break;
491         case OB_DIRECTION_SOUTH:
492             STRUT_PARTIAL_SET(dock_strut, 0, 0, 0, strh,
493                               0, 0, 0, 0, 0, 0, dock->area.x, dock->area.x
494                               + dock->area.width - 1);
495             break;
496         case OB_DIRECTION_SOUTHEAST:
497             switch (config_dock_orient) {
498             case OB_ORIENTATION_HORZ:
499                 STRUT_PARTIAL_SET(dock_strut, 0, 0, 0, strh,
500                                   0, 0, 0, 0, 0, 0, dock->area.x,
501                                   dock->area.x + dock->area.width - 1);
502                 break;
503             case OB_ORIENTATION_VERT:
504                 STRUT_PARTIAL_SET(dock_strut, 0, 0, strw, 0,
505                                   0, 0, 0, 0, dock->area.y, dock->area.y
506                                   + dock->area.height - 1, 0, 0);
507                 break;
508             }
509             break;
510         }
511     }
512
513     /* not used for actually sizing shit */
514     dock->area.width -= ob_rr_theme->obwidth * 2;
515     dock->area.height -= ob_rr_theme->obwidth * 2;
516
517     if (dock->dock_apps) {
518         g_assert(dock->area.width > 0);
519         g_assert(dock->area.height > 0);
520
521         XMoveResizeWindow(ob_display, dock->frame, dock->area.x, dock->area.y,
522                           dock->area.width, dock->area.height);
523
524         RrPaint(dock->a_frame, dock->frame, dock->area.width,
525                 dock->area.height);
526         XMapWindow(ob_display, dock->frame);
527     } else
528         XUnmapWindow(ob_display, dock->frame);
529
530     /* but they are useful outside of this function! */
531     dock->area.width += ob_rr_theme->obwidth * 2;
532     dock->area.height += ob_rr_theme->obwidth * 2;
533
534     screen_update_areas();
535
536     g_free(a);
537 }
538
539 void dock_app_configure(ObDockApp *app, gint w, gint h)
540 {
541     app->w = w;
542     app->h = h;
543     dock_configure();
544 }
545
546 void dock_app_drag(ObDockApp *app, XMotionEvent *e)
547 {
548     ObDockApp *over = NULL;
549     GList *it;
550     gint x, y;
551     gboolean after;
552     gboolean stop;
553
554     x = e->x_root;
555     y = e->y_root;
556
557     /* are we on top of the dock? */
558     if (!(x >= dock->area.x &&
559           y >= dock->area.y &&
560           x < dock->area.x + dock->area.width &&
561           y < dock->area.y + dock->area.height))
562         return;
563
564     x -= dock->area.x;
565     y -= dock->area.y;
566
567     /* which dock app are we on top of? */
568     stop = FALSE;
569     for (it = dock->dock_apps; it; it = g_list_next(it)) {
570         over = it->data;
571         switch (config_dock_orient) {
572         case OB_ORIENTATION_HORZ:
573             if (x >= over->x && x < over->x + over->w)
574                 stop = TRUE;
575             break;
576         case OB_ORIENTATION_VERT:
577             if (y >= over->y && y < over->y + over->h)
578                 stop = TRUE;
579             break;
580         }
581         /* dont go to it->next! */
582         if (stop) break;
583     }
584     if (!it || app == over) return;
585
586     x -= over->x;
587     y -= over->y;
588
589     switch (config_dock_orient) {
590     case OB_ORIENTATION_HORZ:
591         after = (x > over->w / 2);
592         break;
593     case OB_ORIENTATION_VERT:
594         after = (y > over->h / 2);
595         break;
596     default:
597         g_assert_not_reached();
598     }
599
600     /* remove before doing the it->next! */
601     dock->dock_apps = g_list_remove(dock->dock_apps, app);
602
603     if (after) it = it->next;
604
605     dock->dock_apps = g_list_insert_before(dock->dock_apps, it, app);
606     dock_configure();
607 }
608
609 static gboolean hide_timeout(gpointer data)
610 {
611     /* hide */
612     dock->hidden = TRUE;
613     dock_configure();
614
615     return FALSE; /* don't repeat */
616 }
617
618 static gboolean show_timeout(gpointer data)
619 {
620     /* hide */
621     dock->hidden = FALSE;
622     dock_configure();
623
624     return FALSE; /* don't repeat */
625 }
626
627 void dock_hide(gboolean hide)
628 {
629     if (!hide) {
630         if (dock->hidden && config_dock_hide) {
631             ob_main_loop_timeout_add(ob_main_loop,
632                                      config_dock_show_delay * 1000,
633                                      show_timeout, NULL, g_direct_equal, NULL);
634         } else if (!dock->hidden && config_dock_hide) {
635             ob_main_loop_timeout_remove(ob_main_loop, hide_timeout);
636         }
637     } else {
638         if (!dock->hidden && config_dock_hide) {
639             ob_main_loop_timeout_add(ob_main_loop,
640                                      config_dock_hide_delay * 1000,
641                                      hide_timeout, NULL, g_direct_equal, NULL);
642         } else if (dock->hidden && config_dock_hide) {
643             ob_main_loop_timeout_remove(ob_main_loop, show_timeout);
644         }
645     }
646 }
647
648 void dock_get_area(Rect *a)
649 {
650     RECT_SET(*a, dock->area.x, dock->area.y,
651              dock->area.width, dock->area.height);
652 }