]> icculus.org git repositories - dana/openbox.git/blob - openbox/dock.c
add some more font metrics api
[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 #include "render2/render.h"
8 #include "render2/theme.h"
9
10 #define DOCK_EVENT_MASK (ButtonPressMask | ButtonReleaseMask | \
11                          EnterWindowMask | LeaveWindowMask)
12 #define DOCKAPP_EVENT_MASK (StructureNotifyMask)
13
14 static Dock *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(struct Dock, 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, ob_root, 0, 0, 1, 1, 0,
32                                 RrInstanceDepth(ob_render_inst),
33                                 InputOutput,
34                                 RrInstanceVisual(ob_render_inst),
35                                 CWOverrideRedirect | CWEventMask,
36                                 &attrib);
37     dock->s_frame = RrSurfaceNew(ob_render_inst, 0, dock->frame, 0);
38     RrSurfaceCopy(dock->s_frame, ob_theme->title);
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     RrSurfaceFree(dock->s_frame);
48     XDestroyWindow(ob_display, dock->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     DockApp *app;
56     XWindowAttributes attrib;
57     char **data;
58
59     app = g_new0(DockApp, 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 == 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_cursors.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(DockApp *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     int spot;
153     int gravity;
154
155     dock->w = dock->h = spot = 0;
156
157     /* get the size */
158     for (it = dock->dock_apps; it; it = it->next) {
159         struct DockApp *app = it->data;
160         if (config_dock_horz) {
161             dock->w += app->w;
162             dock->h = MAX(dock->h, app->h);
163         } else {
164             dock->w = MAX(dock->w, app->w);
165             dock->h += app->h;
166         }
167     }
168
169     /* position the apps */
170     for (it = dock->dock_apps; it; it = it->next) {
171         struct DockApp *app = it->data;
172         if (config_dock_horz) {
173             app->x = spot;
174             app->y = (dock->h - app->h) / 2;
175             spot += app->w;
176         } else {
177             app->x = (dock->w - app->w) / 2;
178             app->y = spot;
179             spot += app->h;
180         }
181
182         XMoveWindow(ob_display, app->icon_win, app->x, app->y);
183     }
184
185     dock->w += ob_theme->bwidth * 2;
186     dock->h += ob_theme->bwidth * 2;
187
188     /* calculate position */
189     switch (config_dock_pos) {
190     case DockPos_Floating:
191         dock->x = config_dock_x;
192         dock->y = config_dock_y;
193         gravity = NorthWestGravity;
194         break;
195     case DockPos_TopLeft:
196         dock->x = 0;
197         dock->y = 0;
198         gravity = NorthWestGravity;
199         break;
200     case DockPos_Top:
201         dock->x = screen_physical_size.width / 2;
202         dock->y = 0;
203         gravity = NorthGravity;
204         break;
205     case DockPos_TopRight:
206         dock->x = screen_physical_size.width;
207         dock->y = 0;
208         gravity = NorthEastGravity;
209         break;
210     case DockPos_Left:
211         dock->x = 0;
212         dock->y = screen_physical_size.height / 2;
213         gravity = WestGravity;
214         break;
215     case DockPos_Right:
216         dock->x = screen_physical_size.width;
217         dock->y = screen_physical_size.height / 2;
218         gravity = EastGravity;
219         break;
220     case DockPos_BottomLeft:
221         dock->x = 0;
222         dock->y = screen_physical_size.height;
223         gravity = SouthWestGravity;
224         break;
225     case DockPos_Bottom:
226         dock->x = screen_physical_size.width / 2;
227         dock->y = screen_physical_size.height;
228         gravity = SouthGravity;
229         break;
230     case DockPos_BottomRight:
231         dock->x = screen_physical_size.width;
232         dock->y = screen_physical_size.height;
233         gravity = SouthEastGravity;
234         break;
235     }
236
237     switch(gravity) {
238     case NorthGravity:
239     case CenterGravity:
240     case SouthGravity:
241         dock->x -= dock->w / 2;
242         break;
243     case NorthEastGravity:
244     case EastGravity:
245     case SouthEastGravity:
246         dock->x -= dock->w;
247         break;
248     }
249     switch(gravity) {
250     case WestGravity:
251     case CenterGravity:
252     case EastGravity:
253         dock->y -= dock->h / 2;
254         break;
255     case SouthWestGravity:
256     case SouthGravity:
257     case SouthEastGravity:
258         dock->y -= dock->h;
259         break;
260     }
261
262     if (config_dock_hide && dock->hidden) {
263         switch (config_dock_pos) {
264         case DockPos_Floating:
265             break;
266         case DockPos_TopLeft:
267             if (config_dock_horz)
268                 dock->y -= dock->h - ob_theme->bwidth;
269             else
270                 dock->x -= dock->w - ob_theme->bwidth;
271             break;
272         case DockPos_Top:
273             dock->y -= dock->h - ob_theme->bwidth;
274             break;
275         case DockPos_TopRight:
276             if (config_dock_horz)
277                 dock->y -= dock->h - ob_theme->bwidth;
278             else
279                 dock->x += dock->w - ob_theme->bwidth;
280             break;
281         case DockPos_Left:
282             dock->x -= dock->w - ob_theme->bwidth;
283             break;
284         case DockPos_Right:
285             dock->x += dock->w - ob_theme->bwidth;
286             break;
287         case DockPos_BottomLeft:
288             if (config_dock_horz)
289                 dock->y += dock->h - ob_theme->bwidth;
290             else
291                 dock->x -= dock->w - ob_theme->bwidth;
292             break;
293         case DockPos_Bottom:
294             dock->y += dock->h - ob_theme->bwidth;
295             break;
296         case DockPos_BottomRight:
297             if (config_dock_horz)
298                 dock->y += dock->h - ob_theme->bwidth;
299             else
300                 dock->x += dock->w - ob_theme->bwidth;
301             break;
302         }    
303     }
304
305     /* set the strut */
306     switch (config_dock_pos) {
307     case DockPos_Floating:
308         STRUT_SET(dock_strut, 0, 0, 0, 0);
309         break;
310     case DockPos_TopLeft:
311         if (config_dock_horz)
312             STRUT_SET(dock_strut, 0, dock->h, 0, 0);
313         else
314             STRUT_SET(dock_strut, dock->w, 0, 0, 0);
315         break;
316     case DockPos_Top:
317         STRUT_SET(dock_strut, 0, dock->h, 0, 0);
318         break;
319     case DockPos_TopRight:
320         if (config_dock_horz)
321             STRUT_SET(dock_strut, 0, dock->h, 0, 0);
322         else
323             STRUT_SET(dock_strut, 0, 0, dock->w, 0);
324         break;
325     case DockPos_Left:
326         STRUT_SET(dock_strut, dock->w, 0, 0, 0);
327         break;
328     case DockPos_Right:
329         STRUT_SET(dock_strut, 0, 0, dock->w, 0);
330         break;
331     case DockPos_BottomLeft:
332         if (config_dock_horz)
333             STRUT_SET(dock_strut, 0, 0, 0, dock->h);
334         else
335             STRUT_SET(dock_strut, dock->w, 0, 0, 0);
336         break;
337     case DockPos_Bottom:
338         STRUT_SET(dock_strut, 0, 0, 0, dock->h);
339         break;
340     case DockPos_BottomRight:
341         if (config_dock_horz)
342             STRUT_SET(dock_strut, 0, 0, 0, dock->h);
343         else
344             STRUT_SET(dock_strut, 0, 0, dock->w, 0);
345         break;
346     }
347
348     if (dock->w > 0 && dock->h > 0) {
349         RrSurfaceSetArea(dock->s_frame, dock->x, dock->y, dock->w, dock->h);
350         RrSurfaceShow(dock->s_frame);
351     } else
352         RrSurfaceHide(dock->s_frame);
353
354     dock->w += ob_theme->bwidth * 2;
355     dock->h += ob_theme->bwidth * 2;
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 }