save the dock's class/name for future evil purposes!
[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     for (it = dock->dock_apps; it; it = it->next) {
156         struct DockApp *app = it->data;
157         if (config_dock_horz) {
158             app->x = spot;
159             app->y = 0;
160             dock->w += app->w;
161             dock->h = MAX(dock->h, app->h);
162             spot += app->w;
163         } else {
164             app->x = 0;
165             app->y = spot;
166             dock->w = MAX(dock->w, app->w);
167             dock->h += app->h;
168             spot += app->h;
169         }
170
171         XMoveWindow(ob_display, app->icon_win, app->x, app->y);
172     }
173
174     /* used for calculating offsets */
175     dock->w += theme_bwidth * 2;
176     dock->h += theme_bwidth * 2;
177
178     /* calculate position */
179     switch (config_dock_pos) {
180     case DockPos_Floating:
181         dock->x = config_dock_x;
182         dock->y = config_dock_y;
183         gravity = NorthWestGravity;
184         break;
185     case DockPos_TopLeft:
186         dock->x = 0;
187         dock->y = 0;
188         gravity = NorthWestGravity;
189         break;
190     case DockPos_Top:
191         dock->x = screen_physical_size.width / 2;
192         dock->y = 0;
193         gravity = NorthGravity;
194         break;
195     case DockPos_TopRight:
196         dock->x = screen_physical_size.width;
197         dock->y = 0;
198         gravity = NorthEastGravity;
199         break;
200     case DockPos_Left:
201         dock->x = 0;
202         dock->y = screen_physical_size.height / 2;
203         gravity = WestGravity;
204         break;
205     case DockPos_Right:
206         dock->x = screen_physical_size.width;
207         dock->y = screen_physical_size.height / 2;
208         gravity = EastGravity;
209         break;
210     case DockPos_BottomLeft:
211         dock->x = 0;
212         dock->y = screen_physical_size.height;
213         gravity = SouthWestGravity;
214         break;
215     case DockPos_Bottom:
216         dock->x = screen_physical_size.width / 2;
217         dock->y = screen_physical_size.height;
218         gravity = SouthGravity;
219         break;
220     case DockPos_BottomRight:
221         dock->x = screen_physical_size.width;
222         dock->y = screen_physical_size.height;
223         gravity = SouthEastGravity;
224         break;
225     }
226
227     switch(gravity) {
228     case NorthGravity:
229     case CenterGravity:
230     case SouthGravity:
231         dock->x -= dock->w / 2;
232         break;
233     case NorthEastGravity:
234     case EastGravity:
235     case SouthEastGravity:
236         dock->x -= dock->w;
237         break;
238     }
239     switch(gravity) {
240     case WestGravity:
241     case CenterGravity:
242     case EastGravity:
243         dock->y -= dock->h / 2;
244         break;
245     case SouthWestGravity:
246     case SouthGravity:
247     case SouthEastGravity:
248         dock->y -= dock->h;
249         break;
250     }
251
252     if (config_dock_hide && dock->hidden) {
253         switch (config_dock_pos) {
254         case DockPos_Floating:
255             break;
256         case DockPos_TopLeft:
257             if (config_dock_horz)
258                 dock->y -= dock->h - theme_bwidth;
259             else
260                 dock->x -= dock->w - theme_bwidth;
261             break;
262         case DockPos_Top:
263             dock->y -= dock->h - theme_bwidth;
264             break;
265         case DockPos_TopRight:
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_Left:
272             dock->x -= dock->w - theme_bwidth;
273             break;
274         case DockPos_Right:
275             dock->x += dock->w - theme_bwidth;
276             break;
277         case DockPos_BottomLeft:
278             if (config_dock_horz)
279                 dock->y += dock->h - theme_bwidth;
280             else
281                 dock->x -= dock->w - theme_bwidth;
282             break;
283         case DockPos_Bottom:
284             dock->y += dock->h - theme_bwidth;
285             break;
286         case DockPos_BottomRight:
287             if (config_dock_horz)
288                 dock->y += dock->h - theme_bwidth;
289             else
290                 dock->x += dock->w - theme_bwidth;
291             break;
292         }    
293     }
294
295     /* set the strut */
296     switch (config_dock_pos) {
297     case DockPos_Floating:
298         STRUT_SET(dock_strut, 0, 0, 0, 0);
299         break;
300     case DockPos_TopLeft:
301         if (config_dock_horz)
302             STRUT_SET(dock_strut, 0, dock->h, 0, 0);
303         else
304             STRUT_SET(dock_strut, dock->w, 0, 0, 0);
305         break;
306     case DockPos_Top:
307         STRUT_SET(dock_strut, 0, dock->h, 0, 0);
308         break;
309     case DockPos_TopRight:
310         if (config_dock_horz)
311             STRUT_SET(dock_strut, 0, dock->h, 0, 0);
312         else
313             STRUT_SET(dock_strut, 0, 0, dock->w, 0);
314         break;
315     case DockPos_Left:
316         STRUT_SET(dock_strut, dock->w, 0, 0, 0);
317         break;
318     case DockPos_Right:
319         STRUT_SET(dock_strut, 0, 0, dock->w, 0);
320         break;
321     case DockPos_BottomLeft:
322         if (config_dock_horz)
323             STRUT_SET(dock_strut, 0, 0, 0, dock->h);
324         else
325             STRUT_SET(dock_strut, dock->w, 0, 0, 0);
326         break;
327     case DockPos_Bottom:
328         STRUT_SET(dock_strut, 0, 0, 0, dock->h);
329         break;
330     case DockPos_BottomRight:
331         if (config_dock_horz)
332             STRUT_SET(dock_strut, 0, 0, 0, dock->h);
333         else
334             STRUT_SET(dock_strut, 0, 0, dock->w, 0);
335         break;
336     }
337
338     /* not used for actually sizing shit */
339     dock->w -= theme_bwidth * 2;
340     dock->h -= theme_bwidth * 2;
341
342     if (dock->w > 0 && dock->h > 0) {
343         RECT_SET(dock->a_frame->area, 0, 0, dock->w, dock->h);
344         XMoveResizeWindow(ob_display, dock->frame,
345                           dock->x, dock->y, dock->w, dock->h);
346
347         paint(dock->frame, dock->a_frame);
348         XMapWindow(ob_display, dock->frame);
349     } else
350         XUnmapWindow(ob_display, dock->frame);
351
352     /* but they are useful outside of this function! */
353     dock->w += theme_bwidth * 2;
354     dock->h += theme_bwidth * 2;
355
356     screen_update_struts();
357 }
358
359 void dock_app_configure(DockApp *app, int w, int h)
360 {
361     app->w = w;
362     app->h = h;
363     dock_configure();
364 }
365
366 void dock_app_drag(DockApp *app, XMotionEvent *e)
367 {
368     DockApp *over = NULL;
369     GList *it;
370     int x, y;
371     gboolean after;
372
373     x = e->x_root;
374     y = e->y_root;
375
376     /* are we on top of the dock? */
377     if (!(x >= dock->x &&
378           y >= dock->y &&
379           x < dock->x + dock->w &&
380           y < dock->y + dock->h))
381         return;
382
383     x -= dock->x;
384     y -= dock->y;
385
386     /* which dock app are we on top of? */
387     for (it = dock->dock_apps; it; it = it->next) {
388         over = it->data;
389         if (config_dock_horz) {
390             if (x >= over->x && x < over->x + over->w)
391                 break;
392         } else {
393             if (y >= over->y && y < over->y + over->h)
394                 break;
395         }
396     }
397     if (!it || app == over) return;
398
399     x -= over->x;
400     y -= over->y;
401
402     if (config_dock_horz)
403         after = (x > over->w / 2);
404     else
405         after = (y > over->h / 2);
406
407     /* remove before doing the it->next! */
408     dock->dock_apps = g_list_remove(dock->dock_apps, app);
409
410     if (after) it = it->next;
411
412     dock->dock_apps = g_list_insert_before(dock->dock_apps, it, app);
413     dock_configure();
414 }
415
416 static void hide_timeout(void *n)
417 {
418     /* dont repeat */
419     timer_stop(dock->hide_timer);
420     dock->hide_timer = NULL;
421
422     /* hide */
423     dock->hidden = TRUE;
424     dock_configure();
425 }
426
427 void dock_hide(gboolean hide)
428 {
429     if (dock->hidden == hide || !config_dock_hide)
430         return;
431     if (!hide) {
432         /* show */
433         dock->hidden = FALSE;
434         dock_configure();
435
436         /* if was hiding, stop it */
437         if (dock->hide_timer) {
438             timer_stop(dock->hide_timer);
439             dock->hide_timer = NULL;
440         }
441     } else {
442         g_assert(!dock->hide_timer);
443         dock->hide_timer = timer_start(config_dock_hide_timeout * 1000,
444                                        (TimeoutHandler)hide_timeout,
445                                        NULL);
446     }
447 }