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