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