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