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