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