update copyright step 2
[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        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->b_color));
61         XSetWindowBorderWidth(ob_display, dock->frame, ob_rr_theme->bwidth);
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->b_color));
95     XSetWindowBorderWidth(ob_display, dock->frame, ob_rr_theme->bwidth);
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->bwidth * 2;
265     dock->h += ob_rr_theme->bwidth * 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         }
317     }
318
319     switch(gravity) {
320     case NorthGravity:
321     case CenterGravity:
322     case SouthGravity:
323         dock->x -= dock->w / 2;
324         break;
325     case NorthEastGravity:
326     case EastGravity:
327     case SouthEastGravity:
328         dock->x -= dock->w;
329         break;
330     }
331     switch(gravity) {
332     case WestGravity:
333     case CenterGravity:
334     case EastGravity:
335         dock->y -= dock->h / 2;
336         break;
337     case SouthWestGravity:
338     case SouthGravity:
339     case SouthEastGravity:
340         dock->y -= dock->h;
341         break;
342     }
343
344     if (config_dock_hide && dock->hidden) {
345         if (!config_dock_floating) {
346             switch (config_dock_pos) {
347             case OB_DIRECTION_NORTHWEST:
348                 switch (config_dock_orient) {
349                 case OB_ORIENTATION_HORZ:
350                     dock->y -= dock->h - ob_rr_theme->bwidth;
351                     break;
352                 case OB_ORIENTATION_VERT:
353                     dock->x -= dock->w - ob_rr_theme->bwidth;
354                     break;
355                 }
356                 break;
357             case OB_DIRECTION_NORTH:
358                 dock->y -= dock->h - ob_rr_theme->bwidth;
359                 break;
360             case OB_DIRECTION_NORTHEAST:
361                 switch (config_dock_orient) {
362                 case OB_ORIENTATION_HORZ:
363                     dock->y -= dock->h - ob_rr_theme->bwidth;
364                     break;
365                 case OB_ORIENTATION_VERT:
366                     dock->x += dock->w - ob_rr_theme->bwidth;
367                     break;
368                 }
369                 break;
370             case OB_DIRECTION_WEST:
371                 dock->x -= dock->w - ob_rr_theme->bwidth;
372                 break;
373             case OB_DIRECTION_EAST:
374                 dock->x += dock->w - ob_rr_theme->bwidth;
375                 break;
376             case OB_DIRECTION_SOUTHWEST:
377                 switch (config_dock_orient) {
378                 case OB_ORIENTATION_HORZ:
379                     dock->y += dock->h - ob_rr_theme->bwidth;
380                     break;
381                 case OB_ORIENTATION_VERT:
382                     dock->x -= dock->w - ob_rr_theme->bwidth;
383                     break;
384                 } break;
385             case OB_DIRECTION_SOUTH:
386                 dock->y += dock->h - ob_rr_theme->bwidth;
387                 break;
388             case OB_DIRECTION_SOUTHEAST:
389                 switch (config_dock_orient) {
390                 case OB_ORIENTATION_HORZ:
391                     dock->y += dock->h - ob_rr_theme->bwidth;
392                     break;
393                 case OB_ORIENTATION_VERT:
394                     dock->x += dock->w - ob_rr_theme->bwidth;
395                     break;
396                 }
397                 break;
398             }    
399         }
400     }
401
402     if (!config_dock_floating && config_dock_hide) {
403         strw = ob_rr_theme->bwidth;
404         strh = ob_rr_theme->bwidth;
405     } else {
406         strw = dock->w;
407         strh = dock->h;
408     }
409
410     /* set the strut */
411     if (!dock->dock_apps) {
412         STRUT_PARTIAL_SET(dock_strut, 0, 0, 0, 0,
413                           0, 0, 0, 0, 0, 0, 0, 0);
414     } else if (config_dock_floating || config_dock_nostrut) {
415         STRUT_PARTIAL_SET(dock_strut, 0, 0, 0, 0,
416                           0, 0, 0, 0, 0, 0, 0, 0);
417     } else {
418         switch (config_dock_pos) {
419         case OB_DIRECTION_NORTHWEST:
420             switch (config_dock_orient) {
421             case OB_ORIENTATION_HORZ:
422                 STRUT_PARTIAL_SET(dock_strut, 0, strh, 0, 0,
423                                   0, 0, dock->x, dock->x + dock->w - 1,
424                                   0, 0, 0, 0);
425                 break;
426             case OB_ORIENTATION_VERT:
427                 STRUT_PARTIAL_SET(dock_strut, strw, 0, 0, 0,
428                                   dock->y, dock->y + dock->h - 1,
429                                   0, 0, 0, 0, 0, 0);
430                 break;
431             }
432             break;
433         case OB_DIRECTION_NORTH:
434             STRUT_PARTIAL_SET(dock_strut, 0, strh, 0, 0,
435                               dock->x, dock->x + dock->w - 1,
436                               0, 0, 0, 0, 0, 0);
437             break;
438         case OB_DIRECTION_NORTHEAST:
439             switch (config_dock_orient) {
440             case OB_ORIENTATION_HORZ:
441                 STRUT_PARTIAL_SET(dock_strut, 0, strh, 0, 0,
442                                   0, 0, dock->x, dock->x + dock->w -1,
443                                   0, 0, 0, 0);
444                 break;
445             case OB_ORIENTATION_VERT:
446                 STRUT_PARTIAL_SET(dock_strut, 0, 0, strw, 0,
447                                   0, 0, 0, 0,
448                                   dock->y, dock->y + dock->h - 1, 0, 0);
449                 break;
450             }
451             break;
452         case OB_DIRECTION_WEST:
453             STRUT_PARTIAL_SET(dock_strut, strw, 0, 0, 0,
454                               dock->y, dock->y + dock->h - 1,
455                               0, 0, 0, 0, 0, 0);
456             break;
457         case OB_DIRECTION_EAST:
458             STRUT_PARTIAL_SET(dock_strut, 0, 0, strw, 0,
459                               0, 0, 0, 0,
460                               dock->y, dock->y + dock->h - 1, 0, 0);
461             break;
462         case OB_DIRECTION_SOUTHWEST:
463             switch (config_dock_orient) {
464             case OB_ORIENTATION_HORZ:
465                 STRUT_PARTIAL_SET(dock_strut, 0, 0, 0, strh,
466                                   0, 0, 0, 0, 0, 0,
467                                   dock->x, dock->x + dock->w - 1);
468                 break;
469             case OB_ORIENTATION_VERT:
470                 STRUT_PARTIAL_SET(dock_strut, strw, 0, 0, 0,
471                                   dock->y, dock->y + dock->h - 1,
472                                   0, 0, 0, 0, 0, 0);
473                 break;
474             }
475             break;
476         case OB_DIRECTION_SOUTH:
477             STRUT_PARTIAL_SET(dock_strut, 0, 0, 0, strh,
478                               0, 0, 0, 0, 0, 0,
479                               dock->x, dock->x + dock->w - 1);
480             break;
481         case OB_DIRECTION_SOUTHEAST:
482             switch (config_dock_orient) {
483             case OB_ORIENTATION_HORZ:
484                 STRUT_PARTIAL_SET(dock_strut, 0, 0, 0, strh,
485                                   0, 0, 0, 0, 0, 0,
486                                   dock->x, dock->x + dock->w - 1);
487                 break;
488             case OB_ORIENTATION_VERT:
489                 STRUT_PARTIAL_SET(dock_strut, 0, 0, strw, 0,
490                                   0, 0, 0, 0,
491                                   dock->y, dock->y + dock->h - 1, 0, 0);
492                 break;
493             }
494             break;
495         }
496     }
497
498     dock->w += minw;
499     dock->h += minh;
500
501     /* not used for actually sizing shit */
502     dock->w -= ob_rr_theme->bwidth * 2;
503     dock->h -= ob_rr_theme->bwidth * 2;
504
505     if (dock->dock_apps) {
506         g_assert(dock->w > 0);
507         g_assert(dock->h > 0);
508
509         XMoveResizeWindow(ob_display, dock->frame,
510                           dock->x, dock->y, dock->w, dock->h);
511
512         RrPaint(dock->a_frame, dock->frame, dock->w, dock->h);
513         XMapWindow(ob_display, dock->frame);
514     } else
515         XUnmapWindow(ob_display, dock->frame);
516
517     /* but they are useful outside of this function! */
518     dock->w += ob_rr_theme->bwidth * 2;
519     dock->h += ob_rr_theme->bwidth * 2;
520
521     screen_update_areas();
522 }
523
524 void dock_app_configure(ObDockApp *app, gint w, gint h)
525 {
526     app->w = w;
527     app->h = h;
528     dock_configure();
529 }
530
531 void dock_app_drag(ObDockApp *app, XMotionEvent *e)
532 {
533     ObDockApp *over = NULL;
534     GList *it;
535     gint x, y;
536     gboolean after;
537     gboolean stop;
538
539     x = e->x_root;
540     y = e->y_root;
541
542     /* are we on top of the dock? */
543     if (!(x >= dock->x &&
544           y >= dock->y &&
545           x < dock->x + dock->w &&
546           y < dock->y + dock->h))
547         return;
548
549     x -= dock->x;
550     y -= dock->y;
551
552     /* which dock app are we on top of? */
553     stop = FALSE;
554     for (it = dock->dock_apps; it; it = g_list_next(it)) {
555         over = it->data;
556         switch (config_dock_orient) {
557         case OB_ORIENTATION_HORZ:
558             if (x >= over->x && x < over->x + over->w)
559                 stop = TRUE;
560             break;
561         case OB_ORIENTATION_VERT:
562             if (y >= over->y && y < over->y + over->h)
563                 stop = TRUE;
564             break;
565         }
566         /* dont go to it->next! */
567         if (stop) break;
568     }
569     if (!it || app == over) return;
570
571     x -= over->x;
572     y -= over->y;
573
574     switch (config_dock_orient) {
575     case OB_ORIENTATION_HORZ:
576         after = (x > over->w / 2);
577         break;
578     case OB_ORIENTATION_VERT:
579         after = (y > over->h / 2);
580         break;
581     }
582
583     /* remove before doing the it->next! */
584     dock->dock_apps = g_list_remove(dock->dock_apps, app);
585
586     if (after) it = it->next;
587
588     dock->dock_apps = g_list_insert_before(dock->dock_apps, it, app);
589     dock_configure();
590 }
591
592 static gboolean hide_timeout(gpointer data)
593 {
594     /* hide */
595     dock->hidden = TRUE;
596     dock_configure();
597
598     return FALSE; /* don't repeat */
599 }
600
601 static gboolean show_timeout(gpointer data)
602 {
603     /* hide */
604     dock->hidden = FALSE;
605     dock_configure();
606
607     return FALSE; /* don't repeat */
608 }
609
610 void dock_hide(gboolean hide)
611 {
612     if (!hide) {
613         if (dock->hidden && config_dock_hide) {
614             ob_main_loop_timeout_add(ob_main_loop, config_dock_show_delay,
615                                  show_timeout, NULL, NULL);
616         } else if (!dock->hidden && config_dock_hide) {
617             ob_main_loop_timeout_remove(ob_main_loop, hide_timeout);
618         }
619     } else {
620         if (!dock->hidden && config_dock_hide) {
621             ob_main_loop_timeout_add(ob_main_loop, config_dock_hide_delay,
622                                  hide_timeout, NULL, NULL);
623         } else if (dock->hidden && config_dock_hide) {
624             ob_main_loop_timeout_remove(ob_main_loop, show_timeout);
625         }
626     }
627 }