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