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