xinerama support
[mikachu/openbox.git] / openbox / dock.c
1 #include "dock.h"
2 #include "screen.h"
3 #include "prop.h"
4 #include "config.h"
5 #include "grab.h"
6 #include "openbox.h"
7 #include "render/theme.h"
8
9 #define DOCK_EVENT_MASK (ButtonPressMask | ButtonReleaseMask | \
10                          EnterWindowMask | LeaveWindowMask)
11 #define DOCKAPP_EVENT_MASK (StructureNotifyMask)
12
13 static Dock *dock;
14
15 Strut dock_strut;
16
17 void dock_startup()
18 {
19     XSetWindowAttributes attrib;
20
21     STRUT_SET(dock_strut, 0, 0, 0, 0);
22
23     dock = g_new0(struct Dock, 1);
24     dock->obwin.type = Window_Dock;
25
26     dock->hidden = TRUE;
27
28     attrib.event_mask = DOCK_EVENT_MASK;
29     attrib.override_redirect = True;
30     dock->frame = XCreateWindow(ob_display, ob_root, 0, 0, 1, 1, 0,
31                                 RrDepth(ob_rr_inst), InputOutput,
32                                 RrVisual(ob_rr_inst),
33                                 CWOverrideRedirect | CWEventMask,
34                                 &attrib);
35     dock->a_frame = RrAppearanceCopy(ob_rr_theme->a_unfocused_title);
36     XSetWindowBorder(ob_display, dock->frame, ob_rr_theme->b_color->pixel);
37     XSetWindowBorderWidth(ob_display, dock->frame, ob_rr_theme->bwidth);
38
39     g_hash_table_insert(window_map, &dock->frame, dock);
40     stacking_add(DOCK_AS_WINDOW(dock));
41     stacking_raise(DOCK_AS_WINDOW(dock));
42 }
43
44 void dock_shutdown()
45 {
46     XDestroyWindow(ob_display, dock->frame);
47     RrAppearanceFree(dock->a_frame);
48     g_hash_table_remove(window_map, &dock->frame);
49     stacking_remove(dock);
50 }
51
52 void dock_add(Window win, XWMHints *wmhints)
53 {
54     DockApp *app;
55     XWindowAttributes attrib;
56     char **data;
57
58     app = g_new0(DockApp, 1);
59     app->obwin.type = Window_DockApp;
60     app->win = win;
61     app->icon_win = (wmhints->flags & IconWindowHint) ?
62         wmhints->icon_window : win;
63
64     if (PROP_GETSS(app->win, wm_class, locale, &data)) {
65         if (data[0]) {
66             app->name = g_strdup(data[0]);
67             if (data[1])
68                 app->class = g_strdup(data[1]);
69         }
70         g_strfreev(data);     
71     }
72
73     if (app->name == NULL) app->name = g_strdup("");
74     if (app->class == NULL) app->class = g_strdup("");
75     
76     if (XGetWindowAttributes(ob_display, app->icon_win, &attrib)) {
77         app->w = attrib.width;
78         app->h = attrib.height;
79     } else {
80         app->w = app->h = 64;
81     }
82
83     dock->dock_apps = g_list_append(dock->dock_apps, app);
84     dock_configure();
85
86     XReparentWindow(ob_display, app->icon_win, dock->frame, app->x, app->y);
87     /*
88       This is the same case as in frame.c for client windows. When Openbox is
89       starting, the window is already mapped so we see unmap events occur for
90       it. There are 2 unmap events generated that we see, one with the 'event'
91       member set the root window, and one set to the client, but both get
92       handled and need to be ignored.
93     */
94     if (ob_state == State_Starting)
95         app->ignore_unmaps += 2;
96
97     if (app->win != app->icon_win) {
98         /* have to map it so that it can be re-managed on a restart */
99         XMoveWindow(ob_display, app->win, -1000, -1000);
100         XMapWindow(ob_display, app->win);
101     }
102     XMapWindow(ob_display, app->icon_win);
103     XSync(ob_display, False);
104
105     /* specify that if we exit, the window should not be destroyed and should
106        be reparented back to root automatically */
107     XChangeSaveSet(ob_display, app->icon_win, SetModeInsert);
108     XSelectInput(ob_display, app->icon_win, DOCKAPP_EVENT_MASK);
109
110     grab_button_full(2, 0, app->icon_win,
111                      ButtonPressMask | ButtonReleaseMask | ButtonMotionMask,
112                      GrabModeAsync, ob_cursors.move);
113
114     g_hash_table_insert(window_map, &app->icon_win, app);
115
116     g_message("Managed Dock App: 0x%lx (%s)", app->icon_win, app->class);
117 }
118
119 void dock_remove_all()
120 {
121     while (dock->dock_apps)
122         dock_remove(dock->dock_apps->data, TRUE);
123 }
124
125 void dock_remove(DockApp *app, gboolean reparent)
126 {
127     ungrab_button(2, 0, app->icon_win);
128     XSelectInput(ob_display, app->icon_win, NoEventMask);
129     /* remove the window from our save set */
130     XChangeSaveSet(ob_display, app->icon_win, SetModeDelete);
131     XSync(ob_display, False);
132
133     g_hash_table_remove(window_map, &app->icon_win);
134
135     if (reparent)
136         XReparentWindow(ob_display, app->icon_win, ob_root, app->x, app->y);
137
138     dock->dock_apps = g_list_remove(dock->dock_apps, app);
139     dock_configure();
140
141     g_message("Unmanaged Dock App: 0x%lx (%s)", app->icon_win, app->class);
142
143     g_free(app->name);
144     g_free(app->class);
145     g_free(app);
146 }
147
148 void dock_configure()
149 {
150     GList *it;
151     int spot;
152     int gravity;
153     int minw, minh;
154     Rect *a;
155
156     RrMinsize(dock->a_frame, &minw, &minh);
157
158     dock->w = dock->h = 0;
159
160     /* get the size */
161     for (it = dock->dock_apps; it; it = it->next) {
162         struct DockApp *app = it->data;
163         if (config_dock_horz) {
164             dock->w += app->w;
165             dock->h = MAX(dock->h, app->h);
166         } else {
167             dock->w = MAX(dock->w, app->w);
168             dock->h += app->h;
169         }
170     }
171
172     spot = (config_dock_horz ? minw : minh) / 2;
173
174     /* position the apps */
175     for (it = dock->dock_apps; it; it = it->next) {
176         struct DockApp *app = it->data;
177         if (config_dock_horz) {
178             app->x = spot;
179             app->y = (dock->h - app->h) / 2;
180             spot += app->w;
181         } else {
182             app->x = (dock->w - app->w) / 2;
183             app->y = spot;
184             spot += app->h;
185         }
186
187         XMoveWindow(ob_display, app->icon_win, app->x, app->y);
188     }
189
190     /* used for calculating offsets */
191     dock->w += ob_rr_theme->bwidth * 2;
192     dock->h += ob_rr_theme->bwidth * 2;
193
194     a = screen_physical_area();
195
196     /* calculate position */
197     switch (config_dock_pos) {
198     case DockPos_Floating:
199         dock->x = config_dock_x;
200         dock->y = config_dock_y;
201         gravity = NorthWestGravity;
202         break;
203     case DockPos_TopLeft:
204         dock->x = 0;
205         dock->y = 0;
206         gravity = NorthWestGravity;
207         break;
208     case DockPos_Top:
209         dock->x = a->width / 2;
210         dock->y = 0;
211         gravity = NorthGravity;
212         break;
213     case DockPos_TopRight:
214         dock->x = a->width;
215         dock->y = 0;
216         gravity = NorthEastGravity;
217         break;
218     case DockPos_Left:
219         dock->x = 0;
220         dock->y = a->height / 2;
221         gravity = WestGravity;
222         break;
223     case DockPos_Right:
224         dock->x = a->width;
225         dock->y = a->height / 2;
226         gravity = EastGravity;
227         break;
228     case DockPos_BottomLeft:
229         dock->x = 0;
230         dock->y = a->height;
231         gravity = SouthWestGravity;
232         break;
233     case DockPos_Bottom:
234         dock->x = a->width / 2;
235         dock->y = a->height;
236         gravity = SouthGravity;
237         break;
238     case DockPos_BottomRight:
239         dock->x = a->width;
240         dock->y = a->height;
241         gravity = SouthEastGravity;
242         break;
243     }
244
245     switch(gravity) {
246     case NorthGravity:
247     case CenterGravity:
248     case SouthGravity:
249         dock->x -= dock->w / 2;
250         break;
251     case NorthEastGravity:
252     case EastGravity:
253     case SouthEastGravity:
254         dock->x -= dock->w;
255         break;
256     }
257     switch(gravity) {
258     case WestGravity:
259     case CenterGravity:
260     case EastGravity:
261         dock->y -= dock->h / 2;
262         break;
263     case SouthWestGravity:
264     case SouthGravity:
265     case SouthEastGravity:
266         dock->y -= dock->h;
267         break;
268     }
269
270     if (config_dock_hide && dock->hidden) {
271         switch (config_dock_pos) {
272         case DockPos_Floating:
273             break;
274         case DockPos_TopLeft:
275             if (config_dock_horz)
276                 dock->y -= dock->h - ob_rr_theme->bwidth;
277             else
278                 dock->x -= dock->w - ob_rr_theme->bwidth;
279             break;
280         case DockPos_Top:
281             dock->y -= dock->h - ob_rr_theme->bwidth;
282             break;
283         case DockPos_TopRight:
284             if (config_dock_horz)
285                 dock->y -= dock->h - ob_rr_theme->bwidth;
286             else
287                 dock->x += dock->w - ob_rr_theme->bwidth;
288             break;
289         case DockPos_Left:
290             dock->x -= dock->w - ob_rr_theme->bwidth;
291             break;
292         case DockPos_Right:
293             dock->x += dock->w - ob_rr_theme->bwidth;
294             break;
295         case DockPos_BottomLeft:
296             if (config_dock_horz)
297                 dock->y += dock->h - ob_rr_theme->bwidth;
298             else
299                 dock->x -= dock->w - ob_rr_theme->bwidth;
300             break;
301         case DockPos_Bottom:
302             dock->y += dock->h - ob_rr_theme->bwidth;
303             break;
304         case DockPos_BottomRight:
305             if (config_dock_horz)
306                 dock->y += dock->h - ob_rr_theme->bwidth;
307             else
308                 dock->x += dock->w - ob_rr_theme->bwidth;
309             break;
310         }    
311     }
312
313     /* set the strut */
314     switch (config_dock_pos) {
315     case DockPos_Floating:
316         STRUT_SET(dock_strut, 0, 0, 0, 0);
317         break;
318     case DockPos_TopLeft:
319         if (config_dock_horz)
320             STRUT_SET(dock_strut, 0, dock->h, 0, 0);
321         else
322             STRUT_SET(dock_strut, dock->w, 0, 0, 0);
323         break;
324     case DockPos_Top:
325         STRUT_SET(dock_strut, 0, dock->h, 0, 0);
326         break;
327     case DockPos_TopRight:
328         if (config_dock_horz)
329             STRUT_SET(dock_strut, 0, dock->h, 0, 0);
330         else
331             STRUT_SET(dock_strut, 0, 0, dock->w, 0);
332         break;
333     case DockPos_Left:
334         STRUT_SET(dock_strut, dock->w, 0, 0, 0);
335         break;
336     case DockPos_Right:
337         STRUT_SET(dock_strut, 0, 0, dock->w, 0);
338         break;
339     case DockPos_BottomLeft:
340         if (config_dock_horz)
341             STRUT_SET(dock_strut, 0, 0, 0, dock->h);
342         else
343             STRUT_SET(dock_strut, dock->w, 0, 0, 0);
344         break;
345     case DockPos_Bottom:
346         STRUT_SET(dock_strut, 0, 0, 0, dock->h);
347         break;
348     case DockPos_BottomRight:
349         if (config_dock_horz)
350             STRUT_SET(dock_strut, 0, 0, 0, dock->h);
351         else
352             STRUT_SET(dock_strut, 0, 0, dock->w, 0);
353         break;
354     }
355
356     dock->w += minw;
357     dock->h += minh;
358
359     /* not used for actually sizing shit */
360     dock->w -= ob_rr_theme->bwidth * 2;
361     dock->h -= ob_rr_theme->bwidth * 2;
362
363     if (dock->w > 0 && dock->h > 0) {
364         XMoveResizeWindow(ob_display, dock->frame,
365                           dock->x, dock->y, dock->w, dock->h);
366
367         RrPaint(dock->a_frame, dock->frame, dock->w, dock->h);
368         XMapWindow(ob_display, dock->frame);
369     } else
370         XUnmapWindow(ob_display, dock->frame);
371
372     /* but they are useful outside of this function! */
373     dock->w += ob_rr_theme->bwidth * 2;
374     dock->h += ob_rr_theme->bwidth * 2;
375
376     screen_update_areas();
377 }
378
379 void dock_app_configure(DockApp *app, int w, int h)
380 {
381     app->w = w;
382     app->h = h;
383     dock_configure();
384 }
385
386 void dock_app_drag(DockApp *app, XMotionEvent *e)
387 {
388     DockApp *over = NULL;
389     GList *it;
390     int x, y;
391     gboolean after;
392
393     x = e->x_root;
394     y = e->y_root;
395
396     /* are we on top of the dock? */
397     if (!(x >= dock->x &&
398           y >= dock->y &&
399           x < dock->x + dock->w &&
400           y < dock->y + dock->h))
401         return;
402
403     x -= dock->x;
404     y -= dock->y;
405
406     /* which dock app are we on top of? */
407     for (it = dock->dock_apps; it; it = it->next) {
408         over = it->data;
409         if (config_dock_horz) {
410             if (x >= over->x && x < over->x + over->w)
411                 break;
412         } else {
413             if (y >= over->y && y < over->y + over->h)
414                 break;
415         }
416     }
417     if (!it || app == over) return;
418
419     x -= over->x;
420     y -= over->y;
421
422     if (config_dock_horz)
423         after = (x > over->w / 2);
424     else
425         after = (y > over->h / 2);
426
427     /* remove before doing the it->next! */
428     dock->dock_apps = g_list_remove(dock->dock_apps, app);
429
430     if (after) it = it->next;
431
432     dock->dock_apps = g_list_insert_before(dock->dock_apps, it, app);
433     dock_configure();
434 }
435
436 static void hide_timeout(void *n)
437 {
438     /* dont repeat */
439     timer_stop(dock->hide_timer);
440     dock->hide_timer = NULL;
441
442     /* hide */
443     dock->hidden = TRUE;
444     dock_configure();
445 }
446
447 void dock_hide(gboolean hide)
448 {
449     if (dock->hidden == hide || !config_dock_hide)
450         return;
451     if (!hide) {
452         /* show */
453         dock->hidden = FALSE;
454         dock_configure();
455
456         /* if was hiding, stop it */
457         if (dock->hide_timer) {
458             timer_stop(dock->hide_timer);
459             dock->hide_timer = NULL;
460         }
461     } else {
462         g_assert(!dock->hide_timer);
463         dock->hide_timer = timer_start(config_dock_hide_timeout * 1000,
464                                        (TimeoutHandler)hide_timeout,
465                                        NULL);
466     }
467 }