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