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