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