]> icculus.org git repositories - dana/openbox.git/blob - openbox/dock.c
scary commit..but here goes.
[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        Ben 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->frame_b_color));
61         XSetWindowBorderWidth(ob_display, dock->frame, ob_rr_theme->fbwidth);
62
63         RrAppearanceFree(dock->a_frame);
64         dock->a_frame = RrAppearanceCopy(ob_rr_theme->a_focused_title);
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->a_focused_title);
93     XSetWindowBorder(ob_display, dock->frame,
94                      RrColorPixel(ob_rr_theme->frame_b_color));
95     XSetWindowBorderWidth(ob_display, dock->frame, ob_rr_theme->fbwidth);
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->fbwidth * 2;
265     dock->h += ob_rr_theme->fbwidth * 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->fbwidth;
353                     break;
354                 case OB_ORIENTATION_VERT:
355                     dock->x -= dock->w - ob_rr_theme->fbwidth;
356                     break;
357                 }
358                 break;
359             case OB_DIRECTION_NORTH:
360                 dock->y -= dock->h - ob_rr_theme->fbwidth;
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->fbwidth;
366                     break;
367                 case OB_ORIENTATION_VERT:
368                     dock->x += dock->w - ob_rr_theme->fbwidth;
369                     break;
370                 }
371                 break;
372             case OB_DIRECTION_WEST:
373                 dock->x -= dock->w - ob_rr_theme->fbwidth;
374                 break;
375             case OB_DIRECTION_EAST:
376                 dock->x += dock->w - ob_rr_theme->fbwidth;
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->fbwidth;
382                     break;
383                 case OB_ORIENTATION_VERT:
384                     dock->x -= dock->w - ob_rr_theme->fbwidth;
385                     break;
386                 } break;
387             case OB_DIRECTION_SOUTH:
388                 dock->y += dock->h - ob_rr_theme->fbwidth;
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->fbwidth;
394                     break;
395                 case OB_ORIENTATION_VERT:
396                     dock->x += dock->w - ob_rr_theme->fbwidth;
397                     break;
398                 }
399                 break;
400             }    
401         }
402     }
403
404     if (!config_dock_floating && config_dock_hide) {
405         strw = ob_rr_theme->fbwidth;
406         strh = ob_rr_theme->fbwidth;
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         STRUT_PARTIAL_SET(dock_strut, 0, 0, 0, 0,
418                           0, 0, 0, 0, 0, 0, 0, 0);
419     } else {
420         switch (config_dock_pos) {
421         case OB_DIRECTION_NORTHWEST:
422             switch (config_dock_orient) {
423             case OB_ORIENTATION_HORZ:
424                 STRUT_PARTIAL_SET(dock_strut, 0, strh, 0, 0,
425                                   0, 0, dock->x, dock->x + dock->w - 1,
426                                   0, 0, 0, 0);
427                 break;
428             case OB_ORIENTATION_VERT:
429                 STRUT_PARTIAL_SET(dock_strut, strw, 0, 0, 0,
430                                   dock->y, dock->y + dock->h - 1,
431                                   0, 0, 0, 0, 0, 0);
432                 break;
433             }
434             break;
435         case OB_DIRECTION_NORTH:
436             STRUT_PARTIAL_SET(dock_strut, 0, strh, 0, 0,
437                               dock->x, dock->x + dock->w - 1,
438                               0, 0, 0, 0, 0, 0);
439             break;
440         case OB_DIRECTION_NORTHEAST:
441             switch (config_dock_orient) {
442             case OB_ORIENTATION_HORZ:
443                 STRUT_PARTIAL_SET(dock_strut, 0, strh, 0, 0,
444                                   0, 0, dock->x, dock->x + dock->w -1,
445                                   0, 0, 0, 0);
446                 break;
447             case OB_ORIENTATION_VERT:
448                 STRUT_PARTIAL_SET(dock_strut, 0, 0, strw, 0,
449                                   0, 0, 0, 0,
450                                   dock->y, dock->y + dock->h - 1, 0, 0);
451                 break;
452             }
453             break;
454         case OB_DIRECTION_WEST:
455             STRUT_PARTIAL_SET(dock_strut, strw, 0, 0, 0,
456                               dock->y, dock->y + dock->h - 1,
457                               0, 0, 0, 0, 0, 0);
458             break;
459         case OB_DIRECTION_EAST:
460             STRUT_PARTIAL_SET(dock_strut, 0, 0, strw, 0,
461                               0, 0, 0, 0,
462                               dock->y, dock->y + dock->h - 1, 0, 0);
463             break;
464         case OB_DIRECTION_SOUTHWEST:
465             switch (config_dock_orient) {
466             case OB_ORIENTATION_HORZ:
467                 STRUT_PARTIAL_SET(dock_strut, 0, 0, 0, strh,
468                                   0, 0, 0, 0, 0, 0,
469                                   dock->x, dock->x + dock->w - 1);
470                 break;
471             case OB_ORIENTATION_VERT:
472                 STRUT_PARTIAL_SET(dock_strut, strw, 0, 0, 0,
473                                   dock->y, dock->y + dock->h - 1,
474                                   0, 0, 0, 0, 0, 0);
475                 break;
476             }
477             break;
478         case OB_DIRECTION_SOUTH:
479             STRUT_PARTIAL_SET(dock_strut, 0, 0, 0, strh,
480                               0, 0, 0, 0, 0, 0,
481                               dock->x, dock->x + dock->w - 1);
482             break;
483         case OB_DIRECTION_SOUTHEAST:
484             switch (config_dock_orient) {
485             case OB_ORIENTATION_HORZ:
486                 STRUT_PARTIAL_SET(dock_strut, 0, 0, 0, strh,
487                                   0, 0, 0, 0, 0, 0,
488                                   dock->x, dock->x + dock->w - 1);
489                 break;
490             case OB_ORIENTATION_VERT:
491                 STRUT_PARTIAL_SET(dock_strut, 0, 0, strw, 0,
492                                   0, 0, 0, 0,
493                                   dock->y, dock->y + dock->h - 1, 0, 0);
494                 break;
495             }
496             break;
497         }
498     }
499
500     dock->w += minw;
501     dock->h += minh;
502
503     /* not used for actually sizing shit */
504     dock->w -= ob_rr_theme->fbwidth * 2;
505     dock->h -= ob_rr_theme->fbwidth * 2;
506
507     if (dock->dock_apps) {
508         g_assert(dock->w > 0);
509         g_assert(dock->h > 0);
510
511         XMoveResizeWindow(ob_display, dock->frame,
512                           dock->x, dock->y, dock->w, dock->h);
513
514         RrPaint(dock->a_frame, dock->frame, dock->w, dock->h);
515         XMapWindow(ob_display, dock->frame);
516     } else
517         XUnmapWindow(ob_display, dock->frame);
518
519     /* but they are useful outside of this function! */
520     dock->w += ob_rr_theme->fbwidth * 2;
521     dock->h += ob_rr_theme->fbwidth * 2;
522
523     screen_update_areas();
524 }
525
526 void dock_app_configure(ObDockApp *app, gint w, gint h)
527 {
528     app->w = w;
529     app->h = h;
530     dock_configure();
531 }
532
533 void dock_app_drag(ObDockApp *app, XMotionEvent *e)
534 {
535     ObDockApp *over = NULL;
536     GList *it;
537     gint x, y;
538     gboolean after;
539     gboolean stop;
540
541     x = e->x_root;
542     y = e->y_root;
543
544     /* are we on top of the dock? */
545     if (!(x >= dock->x &&
546           y >= dock->y &&
547           x < dock->x + dock->w &&
548           y < dock->y + dock->h))
549         return;
550
551     x -= dock->x;
552     y -= dock->y;
553
554     /* which dock app are we on top of? */
555     stop = FALSE;
556     for (it = dock->dock_apps; it; it = g_list_next(it)) {
557         over = it->data;
558         switch (config_dock_orient) {
559         case OB_ORIENTATION_HORZ:
560             if (x >= over->x && x < over->x + over->w)
561                 stop = TRUE;
562             break;
563         case OB_ORIENTATION_VERT:
564             if (y >= over->y && y < over->y + over->h)
565                 stop = TRUE;
566             break;
567         }
568         /* dont go to it->next! */
569         if (stop) break;
570     }
571     if (!it || app == over) return;
572
573     x -= over->x;
574     y -= over->y;
575
576     switch (config_dock_orient) {
577     case OB_ORIENTATION_HORZ:
578         after = (x > over->w / 2);
579         break;
580     case OB_ORIENTATION_VERT:
581         after = (y > over->h / 2);
582         break;
583     default:
584         g_assert_not_reached();
585     }
586
587     /* remove before doing the it->next! */
588     dock->dock_apps = g_list_remove(dock->dock_apps, app);
589
590     if (after) it = it->next;
591
592     dock->dock_apps = g_list_insert_before(dock->dock_apps, it, app);
593     dock_configure();
594 }
595
596 static gboolean hide_timeout(gpointer data)
597 {
598     /* hide */
599     dock->hidden = TRUE;
600     dock_configure();
601
602     return FALSE; /* don't repeat */
603 }
604
605 static gboolean show_timeout(gpointer data)
606 {
607     /* hide */
608     dock->hidden = FALSE;
609     dock_configure();
610
611     return FALSE; /* don't repeat */
612 }
613
614 void dock_hide(gboolean hide)
615 {
616     if (!hide) {
617         if (dock->hidden && config_dock_hide) {
618             ob_main_loop_timeout_add(ob_main_loop, config_dock_show_delay,
619                                  show_timeout, NULL, NULL);
620         } else if (!dock->hidden && config_dock_hide) {
621             ob_main_loop_timeout_remove(ob_main_loop, hide_timeout);
622         }
623     } else {
624         if (!dock->hidden && config_dock_hide) {
625             ob_main_loop_timeout_add(ob_main_loop, config_dock_hide_delay,
626                                  hide_timeout, NULL, NULL);
627         } else if (dock->hidden && config_dock_hide) {
628             ob_main_loop_timeout_remove(ob_main_loop, show_timeout);
629         }
630     }
631 }