]> icculus.org git repositories - dana/openbox.git/blob - openbox/dock.c
fix brain damage in lineto
[dana/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
8 /* XXX temp */
9 static int theme_bwidth = 3;
10
11 #define DOCK_EVENT_MASK (ButtonPressMask | ButtonReleaseMask | \
12                          EnterWindowMask | LeaveWindowMask)
13 #define DOCKAPP_EVENT_MASK (StructureNotifyMask)
14
15 static Dock *dock;
16
17 Strut dock_strut;
18
19 void dock_startup()
20 {
21     XSetWindowAttributes attrib;
22
23     STRUT_SET(dock_strut, 0, 0, 0, 0);
24
25     dock = g_new0(struct Dock, 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, ob_root, 0, 0, 1, 1, 0,
33                                 RrInstanceDepth(ob_render_inst), InputOutput,
34                                 RrInstanceVisual(ob_render_inst),
35                                 CWOverrideRedirect | CWEventMask,
36                                 &attrib);
37     dock->s_frame = RrSurfaceNew(ob_render_inst, RR_SURFACE_PLANAR,
38                                  dock->frame, 0);
39     /* XXX COPY THE APPEARANCE FROM THE THEME !#&*(# LIKE THIS SORTA...
40     dock->a_frame = appearance_copy(theme_a_unfocused_title);
41     */
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     RrSurfaceFree(dock->s_frame);
51     XDestroyWindow(ob_display, dock->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     DockApp *app;
59     XWindowAttributes attrib;
60     char **data;
61
62     app = g_new0(DockApp, 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 == 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_cursors.move);
117
118     g_hash_table_insert(window_map, &app->icon_win, app);
119
120     g_message("Managed Dock App: 0x%lx (%s)", 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(DockApp *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, ob_root, app->x, app->y);
141
142     dock->dock_apps = g_list_remove(dock->dock_apps, app);
143     dock_configure();
144
145     g_message("Unmanaged Dock App: 0x%lx (%s)", 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     int spot;
156     int gravity;
157
158     dock->w = dock->h = spot = 0;
159
160     /* get the size */
161     for (it = dock->dock_apps; it; it = it->next) {
162         struct DockApp *app = it->data;
163         if (config_dock_horz) {
164             dock->w += app->w;
165             dock->h = MAX(dock->h, app->h);
166         } else {
167             dock->w = MAX(dock->w, app->w);
168             dock->h += app->h;
169         }
170     }
171
172     dock->w += theme_bwidth * 2;
173     dock->h += theme_bwidth * 2;
174
175     /* position the apps */
176     for (it = dock->dock_apps; it; it = it->next) {
177         struct DockApp *app = it->data;
178         if (config_dock_horz) {
179             app->x = spot;
180             app->y = (dock->h - app->h) / 2;
181             spot += app->w;
182         } else {
183             app->x = (dock->w - app->w) / 2;
184             app->y = spot;
185             spot += app->h;
186         }
187
188         XMoveWindow(ob_display, app->icon_win, app->x, app->y);
189     }
190
191     /* calculate position */
192     switch (config_dock_pos) {
193     case DockPos_Floating:
194         dock->x = config_dock_x;
195         dock->y = config_dock_y;
196         gravity = NorthWestGravity;
197         break;
198     case DockPos_TopLeft:
199         dock->x = 0;
200         dock->y = 0;
201         gravity = NorthWestGravity;
202         break;
203     case DockPos_Top:
204         dock->x = screen_physical_size.width / 2;
205         dock->y = 0;
206         gravity = NorthGravity;
207         break;
208     case DockPos_TopRight:
209         dock->x = screen_physical_size.width;
210         dock->y = 0;
211         gravity = NorthEastGravity;
212         break;
213     case DockPos_Left:
214         dock->x = 0;
215         dock->y = screen_physical_size.height / 2;
216         gravity = WestGravity;
217         break;
218     case DockPos_Right:
219         dock->x = screen_physical_size.width;
220         dock->y = screen_physical_size.height / 2;
221         gravity = EastGravity;
222         break;
223     case DockPos_BottomLeft:
224         dock->x = 0;
225         dock->y = screen_physical_size.height;
226         gravity = SouthWestGravity;
227         break;
228     case DockPos_Bottom:
229         dock->x = screen_physical_size.width / 2;
230         dock->y = screen_physical_size.height;
231         gravity = SouthGravity;
232         break;
233     case DockPos_BottomRight:
234         dock->x = screen_physical_size.width;
235         dock->y = screen_physical_size.height;
236         gravity = SouthEastGravity;
237         break;
238     }
239
240     switch(gravity) {
241     case NorthGravity:
242     case CenterGravity:
243     case SouthGravity:
244         dock->x -= dock->w / 2;
245         break;
246     case NorthEastGravity:
247     case EastGravity:
248     case SouthEastGravity:
249         dock->x -= dock->w;
250         break;
251     }
252     switch(gravity) {
253     case WestGravity:
254     case CenterGravity:
255     case EastGravity:
256         dock->y -= dock->h / 2;
257         break;
258     case SouthWestGravity:
259     case SouthGravity:
260     case SouthEastGravity:
261         dock->y -= dock->h;
262         break;
263     }
264
265     if (config_dock_hide && dock->hidden) {
266         switch (config_dock_pos) {
267         case DockPos_Floating:
268             break;
269         case DockPos_TopLeft:
270             if (config_dock_horz)
271                 dock->y -= dock->h - theme_bwidth;
272             else
273                 dock->x -= dock->w - theme_bwidth;
274             break;
275         case DockPos_Top:
276             dock->y -= dock->h - theme_bwidth;
277             break;
278         case DockPos_TopRight:
279             if (config_dock_horz)
280                 dock->y -= dock->h - theme_bwidth;
281             else
282                 dock->x += dock->w - theme_bwidth;
283             break;
284         case DockPos_Left:
285             dock->x -= dock->w - theme_bwidth;
286             break;
287         case DockPos_Right:
288             dock->x += dock->w - theme_bwidth;
289             break;
290         case DockPos_BottomLeft:
291             if (config_dock_horz)
292                 dock->y += dock->h - theme_bwidth;
293             else
294                 dock->x -= dock->w - theme_bwidth;
295             break;
296         case DockPos_Bottom:
297             dock->y += dock->h - theme_bwidth;
298             break;
299         case DockPos_BottomRight:
300             if (config_dock_horz)
301                 dock->y += dock->h - theme_bwidth;
302             else
303                 dock->x += dock->w - theme_bwidth;
304             break;
305         }    
306     }
307
308     /* set the strut */
309     switch (config_dock_pos) {
310     case DockPos_Floating:
311         STRUT_SET(dock_strut, 0, 0, 0, 0);
312         break;
313     case DockPos_TopLeft:
314         if (config_dock_horz)
315             STRUT_SET(dock_strut, 0, dock->h, 0, 0);
316         else
317             STRUT_SET(dock_strut, dock->w, 0, 0, 0);
318         break;
319     case DockPos_Top:
320         STRUT_SET(dock_strut, 0, dock->h, 0, 0);
321         break;
322     case DockPos_TopRight:
323         if (config_dock_horz)
324             STRUT_SET(dock_strut, 0, dock->h, 0, 0);
325         else
326             STRUT_SET(dock_strut, 0, 0, dock->w, 0);
327         break;
328     case DockPos_Left:
329         STRUT_SET(dock_strut, dock->w, 0, 0, 0);
330         break;
331     case DockPos_Right:
332         STRUT_SET(dock_strut, 0, 0, dock->w, 0);
333         break;
334     case DockPos_BottomLeft:
335         if (config_dock_horz)
336             STRUT_SET(dock_strut, 0, 0, 0, dock->h);
337         else
338             STRUT_SET(dock_strut, dock->w, 0, 0, 0);
339         break;
340     case DockPos_Bottom:
341         STRUT_SET(dock_strut, 0, 0, 0, dock->h);
342         break;
343     case DockPos_BottomRight:
344         if (config_dock_horz)
345             STRUT_SET(dock_strut, 0, 0, 0, dock->h);
346         else
347             STRUT_SET(dock_strut, 0, 0, dock->w, 0);
348         break;
349     }
350
351     if (dock->w > 0 && dock->h > 0) {
352         RrSurfaceSetArea(dock->s_frame, dock->x, dock->y, dock->w, dock->h);
353         RrSurfaceShow(dock->s_frame);
354     } else
355         RrSurfaceHide(dock->s_frame);
356
357     screen_update_struts();
358 }
359
360 void dock_app_configure(DockApp *app, int w, int h)
361 {
362     app->w = w;
363     app->h = h;
364     dock_configure();
365 }
366
367 void dock_app_drag(DockApp *app, XMotionEvent *e)
368 {
369     DockApp *over = NULL;
370     GList *it;
371     int x, y;
372     gboolean after;
373
374     x = e->x_root;
375     y = e->y_root;
376
377     /* are we on top of the dock? */
378     if (!(x >= dock->x &&
379           y >= dock->y &&
380           x < dock->x + dock->w &&
381           y < dock->y + dock->h))
382         return;
383
384     x -= dock->x;
385     y -= dock->y;
386
387     /* which dock app are we on top of? */
388     for (it = dock->dock_apps; it; it = it->next) {
389         over = it->data;
390         if (config_dock_horz) {
391             if (x >= over->x && x < over->x + over->w)
392                 break;
393         } else {
394             if (y >= over->y && y < over->y + over->h)
395                 break;
396         }
397     }
398     if (!it || app == over) return;
399
400     x -= over->x;
401     y -= over->y;
402
403     if (config_dock_horz)
404         after = (x > over->w / 2);
405     else
406         after = (y > over->h / 2);
407
408     /* remove before doing the it->next! */
409     dock->dock_apps = g_list_remove(dock->dock_apps, app);
410
411     if (after) it = it->next;
412
413     dock->dock_apps = g_list_insert_before(dock->dock_apps, it, app);
414     dock_configure();
415 }
416
417 static void hide_timeout(void *n)
418 {
419     /* dont repeat */
420     timer_stop(dock->hide_timer);
421     dock->hide_timer = NULL;
422
423     /* hide */
424     dock->hidden = TRUE;
425     dock_configure();
426 }
427
428 void dock_hide(gboolean hide)
429 {
430     if (dock->hidden == hide || !config_dock_hide)
431         return;
432     if (!hide) {
433         /* show */
434         dock->hidden = FALSE;
435         dock_configure();
436
437         /* if was hiding, stop it */
438         if (dock->hide_timer) {
439             timer_stop(dock->hide_timer);
440             dock->hide_timer = NULL;
441         }
442     } else {
443         g_assert(!dock->hide_timer);
444         dock->hide_timer = timer_start(config_dock_hide_timeout * 1000,
445                                        (TimeoutHandler)hide_timeout,
446                                        NULL);
447     }
448 }