FUCK dock is not an array anymore!
[mikachu/openbox.git] / openbox / dock.c
1 #include "dock.h"
2 #include "screen.h"
3 #include "config.h"
4 #include "grab.h"
5 #include "openbox.h"
6 #include "render/theme.h"
7
8 #define DOCK_EVENT_MASK (ButtonPressMask | ButtonReleaseMask | \
9                          EnterWindowMask | LeaveWindowMask)
10 #define DOCKAPP_EVENT_MASK (StructureNotifyMask)
11
12 static Dock *dock;
13
14 Strut dock_strut;
15
16 void dock_startup()
17 {
18     XSetWindowAttributes attrib;
19
20     STRUT_SET(dock_strut, 0, 0, 0, 0);
21
22     dock = g_new0(struct Dock, 1);
23     dock->obwin.type = Window_Dock;
24
25     dock->hidden = TRUE;
26
27     attrib.event_mask = DOCK_EVENT_MASK;
28     attrib.override_redirect = True;
29     dock->frame = XCreateWindow(ob_display, ob_root, 0, 0, 1, 1, 0,
30                                 render_depth, InputOutput, render_visual,
31                                 CWOverrideRedirect | CWEventMask,
32                                 &attrib);
33     dock->a_frame = appearance_copy(theme_a_unfocused_title);
34     XSetWindowBorder(ob_display, dock->frame, theme_b_color->pixel);
35     XSetWindowBorderWidth(ob_display, dock->frame, theme_bwidth);
36
37     g_hash_table_insert(window_map, &dock->frame, dock);
38     stacking_add(DOCK_AS_WINDOW(dock));
39     stacking_raise(DOCK_AS_WINDOW(dock));
40 }
41
42 void dock_shutdown()
43 {
44     XDestroyWindow(ob_display, dock->frame);
45     appearance_free(dock->a_frame);
46     g_hash_table_remove(window_map, &dock->frame);
47     stacking_remove(dock);
48 }
49
50 void dock_add(Window win, XWMHints *wmhints)
51 {
52     DockApp *app;
53     XWindowAttributes attrib;
54
55     app = g_new0(DockApp, 1);
56     app->obwin.type = Window_DockApp;
57     app->win = win;
58     app->icon_win = (wmhints->flags & IconWindowHint) ?
59         wmhints->icon_window : win;
60     
61     if (XGetWindowAttributes(ob_display, app->icon_win, &attrib)) {
62         app->w = attrib.width;
63         app->h = attrib.height;
64     } else {
65         app->w = app->h = 64;
66     }
67
68     dock->dock_apps = g_list_append(dock->dock_apps, app);
69     dock_configure();
70
71     XReparentWindow(ob_display, app->icon_win, dock->frame, app->x, app->y);
72     /*
73       This is the same case as in frame.c for client windows. When Openbox is
74       starting, the window is already mapped so we see unmap events occur for
75       it. There are 2 unmap events generated that we see, one with the 'event'
76       member set the root window, and one set to the client, but both get
77       handled and need to be ignored.
78     */
79     if (ob_state == State_Starting)
80         app->ignore_unmaps += 2;
81
82     if (app->win != app->icon_win) {
83         /* have to map it so that it can be re-managed on a restart */
84         XMoveWindow(ob_display, app->win, -1000, -1000);
85         XMapWindow(ob_display, app->win);
86     }
87     XMapWindow(ob_display, app->icon_win);
88     XSync(ob_display, False);
89
90     /* specify that if we exit, the window should not be destroyed and should
91        be reparented back to root automatically */
92     XChangeSaveSet(ob_display, app->icon_win, SetModeInsert);
93     XSelectInput(ob_display, app->icon_win, DOCKAPP_EVENT_MASK);
94
95     grab_button_full(2, 0, app->icon_win,
96                      ButtonPressMask | ButtonReleaseMask | ButtonMotionMask,
97                      GrabModeAsync, ob_cursors.move);
98
99     g_hash_table_insert(window_map, &app->icon_win, app);
100
101     g_message("Managed Dock App: 0x%lx", app->icon_win);
102 }
103
104 void dock_remove_all()
105 {
106     while (dock->dock_apps)
107         dock_remove(dock->dock_apps->data, TRUE);
108 }
109
110 void dock_remove(DockApp *app, gboolean reparent)
111 {
112     ungrab_button(2, 0, app->icon_win);
113     XSelectInput(ob_display, app->icon_win, NoEventMask);
114     /* remove the window from our save set */
115     XChangeSaveSet(ob_display, app->icon_win, SetModeDelete);
116     XSync(ob_display, False);
117
118     g_hash_table_remove(window_map, &app->icon_win);
119
120     if (reparent)
121         XReparentWindow(ob_display, app->icon_win, ob_root, app->x, app->y);
122
123     dock->dock_apps = g_list_remove(dock->dock_apps, app);
124     dock_configure();
125
126     g_message("Unmanaged Dock App: 0x%lx", app->icon_win);
127
128     g_free(app);
129 }
130
131 void dock_configure()
132 {
133     GList *it;
134     int spot;
135     int gravity;
136
137     dock->w = dock->h = spot = 0;
138
139     for (it = dock->dock_apps; it; it = it->next) {
140         struct DockApp *app = it->data;
141         if (config_dock_horz) {
142             app->x = spot;
143             app->y = 0;
144             dock->w += app->w;
145             dock->h = MAX(dock->h, app->h);
146             spot += app->w;
147         } else {
148             app->x = 0;
149             app->y = spot;
150             dock->w = MAX(dock->w, app->w);
151             dock->h += app->h;
152             spot += app->h;
153         }
154
155         XMoveWindow(ob_display, app->icon_win, app->x, app->y);
156     }
157
158     /* used for calculating offsets */
159     dock->w += theme_bwidth * 2;
160     dock->h += theme_bwidth * 2;
161
162     /* calculate position */
163     switch (config_dock_pos) {
164     case DockPos_Floating:
165         dock->x = config_dock_x;
166         dock->y = config_dock_y;
167         gravity = NorthWestGravity;
168         break;
169     case DockPos_TopLeft:
170         dock->x = 0;
171         dock->y = 0;
172         gravity = NorthWestGravity;
173         break;
174     case DockPos_Top:
175         dock->x = screen_physical_size.width / 2;
176         dock->y = 0;
177         gravity = NorthGravity;
178         break;
179     case DockPos_TopRight:
180         dock->x = screen_physical_size.width;
181         dock->y = 0;
182         gravity = NorthEastGravity;
183         break;
184     case DockPos_Left:
185         dock->x = 0;
186         dock->y = screen_physical_size.height / 2;
187         gravity = WestGravity;
188         break;
189     case DockPos_Right:
190         dock->x = screen_physical_size.width;
191         dock->y = screen_physical_size.height / 2;
192         gravity = EastGravity;
193         break;
194     case DockPos_BottomLeft:
195         dock->x = 0;
196         dock->y = screen_physical_size.height;
197         gravity = SouthWestGravity;
198         break;
199     case DockPos_Bottom:
200         dock->x = screen_physical_size.width / 2;
201         dock->y = screen_physical_size.height;
202         gravity = SouthGravity;
203         break;
204     case DockPos_BottomRight:
205         dock->x = screen_physical_size.width;
206         dock->y = screen_physical_size.height;
207         gravity = SouthEastGravity;
208         break;
209     }
210
211     switch(gravity) {
212     case NorthGravity:
213     case CenterGravity:
214     case SouthGravity:
215         dock->x -= dock->w / 2;
216         break;
217     case NorthEastGravity:
218     case EastGravity:
219     case SouthEastGravity:
220         dock->x -= dock->w;
221         break;
222     }
223     switch(gravity) {
224     case WestGravity:
225     case CenterGravity:
226     case EastGravity:
227         dock->y -= dock->h / 2;
228         break;
229     case SouthWestGravity:
230     case SouthGravity:
231     case SouthEastGravity:
232         dock->y -= dock->h;
233         break;
234     }
235
236     if (config_dock_hide && dock->hidden) {
237         switch (config_dock_pos) {
238         case DockPos_Floating:
239             break;
240         case DockPos_TopLeft:
241             if (config_dock_horz)
242                 dock->y -= dock->h - theme_bwidth;
243             else
244                 dock->x -= dock->w - theme_bwidth;
245             break;
246         case DockPos_Top:
247             dock->y -= dock->h - theme_bwidth;
248             break;
249         case DockPos_TopRight:
250             if (config_dock_horz)
251                 dock->y -= dock->h - theme_bwidth;
252             else
253                 dock->x += dock->w - theme_bwidth;
254             break;
255         case DockPos_Left:
256             dock->x -= dock->w - theme_bwidth;
257             break;
258         case DockPos_Right:
259             dock->x += dock->w - theme_bwidth;
260             break;
261         case DockPos_BottomLeft:
262             if (config_dock_horz)
263                 dock->y += dock->h - theme_bwidth;
264             else
265                 dock->x -= dock->w - theme_bwidth;
266             break;
267         case DockPos_Bottom:
268             dock->y += dock->h - theme_bwidth;
269             break;
270         case DockPos_BottomRight:
271             if (config_dock_horz)
272                 dock->y += dock->h - theme_bwidth;
273             else
274                 dock->x += dock->w - theme_bwidth;
275             break;
276         }    
277     }
278
279     /* set the strut */
280     switch (config_dock_pos) {
281     case DockPos_Floating:
282         STRUT_SET(dock_strut, 0, 0, 0, 0);
283         break;
284     case DockPos_TopLeft:
285         if (config_dock_horz)
286             STRUT_SET(dock_strut, 0, dock->h, 0, 0);
287         else
288             STRUT_SET(dock_strut, dock->w, 0, 0, 0);
289         break;
290     case DockPos_Top:
291         STRUT_SET(dock_strut, 0, dock->h, 0, 0);
292         break;
293     case DockPos_TopRight:
294         if (config_dock_horz)
295             STRUT_SET(dock_strut, 0, dock->h, 0, 0);
296         else
297             STRUT_SET(dock_strut, 0, 0, dock->w, 0);
298         break;
299     case DockPos_Left:
300         STRUT_SET(dock_strut, dock->w, 0, 0, 0);
301         break;
302     case DockPos_Right:
303         STRUT_SET(dock_strut, 0, 0, dock->w, 0);
304         break;
305     case DockPos_BottomLeft:
306         if (config_dock_horz)
307             STRUT_SET(dock_strut, 0, 0, 0, dock->h);
308         else
309             STRUT_SET(dock_strut, dock->w, 0, 0, 0);
310         break;
311     case DockPos_Bottom:
312         STRUT_SET(dock_strut, 0, 0, 0, dock->h);
313         break;
314     case DockPos_BottomRight:
315         if (config_dock_horz)
316             STRUT_SET(dock_strut, 0, 0, 0, dock->h);
317         else
318             STRUT_SET(dock_strut, 0, 0, dock->w, 0);
319         break;
320     }
321
322     /* not used for actually sizing shit */
323     dock->w -= theme_bwidth * 2;
324     dock->h -= theme_bwidth * 2;
325
326     if (dock->w > 0 && dock->h > 0) {
327         RECT_SET(dock->a_frame->area, 0, 0, dock->w, dock->h);
328         XMoveResizeWindow(ob_display, dock->frame,
329                           dock->x, dock->y, dock->w, dock->h);
330
331         paint(dock->frame, dock->a_frame);
332         XMapWindow(ob_display, dock->frame);
333     } else
334         XUnmapWindow(ob_display, dock->frame);
335
336     /* but they are useful outside of this function! */
337     dock->w += theme_bwidth * 2;
338     dock->h += theme_bwidth * 2;
339
340     screen_update_struts();
341 }
342
343 void dock_app_configure(DockApp *app, int w, int h)
344 {
345     app->w = w;
346     app->h = h;
347     dock_configure();
348 }
349
350 void dock_app_drag(DockApp *app, XMotionEvent *e)
351 {
352     DockApp *over = NULL;
353     GList *it;
354     int x, y;
355     gboolean after;
356
357     x = e->x_root;
358     y = e->y_root;
359
360     /* are we on top of the dock? */
361     if (!(x >= dock->x &&
362           y >= dock->y &&
363           x < dock->x + dock->w &&
364           y < dock->y + dock->h))
365         return;
366
367     x -= dock->x;
368     y -= dock->y;
369
370     /* which dock app are we on top of? */
371     for (it = dock->dock_apps; it; it = it->next) {
372         over = it->data;
373         if (config_dock_horz) {
374             if (x >= over->x && x < over->x + over->w)
375                 break;
376         } else {
377             if (y >= over->y && y < over->y + over->h)
378                 break;
379         }
380     }
381     if (!it || app == over) return;
382
383     x -= over->x;
384     y -= over->y;
385
386     if (config_dock_horz)
387         after = (x > over->w / 2);
388     else
389         after = (y > over->h / 2);
390
391     /* remove before doing the it->next! */
392     dock->dock_apps = g_list_remove(dock->dock_apps, app);
393
394     if (after) it = it->next;
395
396     dock->dock_apps = g_list_insert_before(dock->dock_apps, it, app);
397     dock_configure();
398 }
399
400 static void hide_timeout(void *n)
401 {
402     /* dont repeat */
403     timer_stop(dock->hide_timer);
404     dock->hide_timer = NULL;
405
406     /* hide */
407     dock->hidden = TRUE;
408     dock_configure();
409 }
410
411 void dock_hide(gboolean hide)
412 {
413     if (dock->hidden == hide || !config_dock_hide)
414         return;
415     if (!hide) {
416         /* show */
417         dock->hidden = FALSE;
418         dock_configure();
419
420         /* if was hiding, stop it */
421         if (dock->hide_timer) {
422             timer_stop(dock->hide_timer);
423             dock->hide_timer = NULL;
424         }
425     } else {
426         g_assert(!dock->hide_timer);
427         dock->hide_timer = timer_start(config_dock_hide_timeout * 1000,
428                                        (TimeoutHandler)hide_timeout,
429                                        NULL);
430     }
431 }