]> icculus.org git repositories - dana/openbox.git/blob - openbox/dock.c
plug memory leaks from the new strut code
[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     g_free(a);
527 }
528
529 void dock_app_configure(ObDockApp *app, gint w, gint h)
530 {
531     app->w = w;
532     app->h = h;
533     dock_configure();
534 }
535
536 void dock_app_drag(ObDockApp *app, XMotionEvent *e)
537 {
538     ObDockApp *over = NULL;
539     GList *it;
540     gint x, y;
541     gboolean after;
542     gboolean stop;
543
544     x = e->x_root;
545     y = e->y_root;
546
547     /* are we on top of the dock? */
548     if (!(x >= dock->x &&
549           y >= dock->y &&
550           x < dock->x + dock->w &&
551           y < dock->y + dock->h))
552         return;
553
554     x -= dock->x;
555     y -= dock->y;
556
557     /* which dock app are we on top of? */
558     stop = FALSE;
559     for (it = dock->dock_apps; it; it = g_list_next(it)) {
560         over = it->data;
561         switch (config_dock_orient) {
562         case OB_ORIENTATION_HORZ:
563             if (x >= over->x && x < over->x + over->w)
564                 stop = TRUE;
565             break;
566         case OB_ORIENTATION_VERT:
567             if (y >= over->y && y < over->y + over->h)
568                 stop = TRUE;
569             break;
570         }
571         /* dont go to it->next! */
572         if (stop) break;
573     }
574     if (!it || app == over) return;
575
576     x -= over->x;
577     y -= over->y;
578
579     switch (config_dock_orient) {
580     case OB_ORIENTATION_HORZ:
581         after = (x > over->w / 2);
582         break;
583     case OB_ORIENTATION_VERT:
584         after = (y > over->h / 2);
585         break;
586     default:
587         g_assert_not_reached();
588     }
589
590     /* remove before doing the it->next! */
591     dock->dock_apps = g_list_remove(dock->dock_apps, app);
592
593     if (after) it = it->next;
594
595     dock->dock_apps = g_list_insert_before(dock->dock_apps, it, app);
596     dock_configure();
597 }
598
599 static gboolean hide_timeout(gpointer data)
600 {
601     /* hide */
602     dock->hidden = TRUE;
603     dock_configure();
604
605     return FALSE; /* don't repeat */
606 }
607
608 static gboolean show_timeout(gpointer data)
609 {
610     /* hide */
611     dock->hidden = FALSE;
612     dock_configure();
613
614     return FALSE; /* don't repeat */
615 }
616
617 void dock_hide(gboolean hide)
618 {
619     if (!hide) {
620         if (dock->hidden && config_dock_hide) {
621             ob_main_loop_timeout_add(ob_main_loop, config_dock_show_delay,
622                                  show_timeout, NULL, g_direct_equal, NULL);
623         } else if (!dock->hidden && config_dock_hide) {
624             ob_main_loop_timeout_remove(ob_main_loop, hide_timeout);
625         }
626     } else {
627         if (!dock->hidden && config_dock_hide) {
628             ob_main_loop_timeout_add(ob_main_loop, config_dock_hide_delay,
629                                  hide_timeout, NULL, g_direct_equal, NULL);
630         } else if (dock->hidden && config_dock_hide) {
631             ob_main_loop_timeout_remove(ob_main_loop, show_timeout);
632         }
633     }
634 }