]> icculus.org git repositories - mikachu/openbox.git/blob - openbox/dock.c
set the resize cursors only when the window can be resized
[mikachu/openbox.git] / openbox / dock.c
1 /* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
2
3    dock.c for the Openbox window manager
4    Copyright (c) 2006        Mikael Magnusson
5    Copyright (c) 2003-2007   Dana Jansens
6
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 2 of the License, or
10    (at your option) any later version.
11
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16
17    See the COPYING file for a copy of the GNU General Public License.
18 */
19
20 #include "debug.h"
21 #include "dock.h"
22 #include "mainloop.h"
23 #include "screen.h"
24 #include "prop.h"
25 #include "config.h"
26 #include "grab.h"
27 #include "openbox.h"
28 #include "render/theme.h"
29
30 #define DOCK_EVENT_MASK (ButtonPressMask | ButtonReleaseMask | \
31                          EnterWindowMask | LeaveWindowMask)
32 #define DOCKAPP_EVENT_MASK (StructureNotifyMask)
33
34 static ObDock *dock;
35
36 StrutPartial dock_strut;
37
38 static void dock_app_grab_button(ObDockApp *app, gboolean grab)
39 {
40     if (grab) {
41         grab_button_full(config_dock_app_move_button,
42                          config_dock_app_move_modifiers, app->icon_win,
43                          ButtonPressMask | ButtonReleaseMask |
44                          ButtonMotionMask,
45                          GrabModeAsync, OB_CURSOR_MOVE);
46     } else {
47         ungrab_button(config_dock_app_move_button,
48                       config_dock_app_move_modifiers, app->icon_win);
49     }
50 }
51
52 void dock_startup(gboolean reconfig)
53 {
54     XSetWindowAttributes attrib;
55
56     if (reconfig) {
57         GList *it;
58
59         XSetWindowBorder(ob_display, dock->frame,
60                          RrColorPixel(ob_rr_theme->
61                                       frame_focused_border_color));
62         XSetWindowBorderWidth(ob_display, dock->frame, ob_rr_theme->fbwidth);
63
64         RrAppearanceFree(dock->a_frame);
65         dock->a_frame = RrAppearanceCopy(ob_rr_theme->a_focused_title);
66
67         stacking_add(DOCK_AS_WINDOW(dock));
68
69         dock_configure();
70         dock_hide(TRUE);
71
72         for (it = dock->dock_apps; it; it = g_list_next(it))
73             dock_app_grab_button(it->data, TRUE);
74         return;
75     }
76
77     STRUT_PARTIAL_SET(dock_strut, 0, 0, 0, 0,
78                       0, 0, 0, 0, 0, 0, 0, 0);
79
80     dock = g_new0(ObDock, 1);
81     dock->obwin.type = Window_Dock;
82
83     dock->hidden = TRUE;
84
85     attrib.event_mask = DOCK_EVENT_MASK;
86     attrib.override_redirect = True;
87     dock->frame = XCreateWindow(ob_display, RootWindow(ob_display, ob_screen),
88                                 0, 0, 1, 1, 0,
89                                 RrDepth(ob_rr_inst), InputOutput,
90                                 RrVisual(ob_rr_inst),
91                                 CWOverrideRedirect | CWEventMask,
92                                 &attrib);
93     dock->a_frame = RrAppearanceCopy(ob_rr_theme->a_focused_title);
94     XSetWindowBorder(ob_display, dock->frame,
95                      RrColorPixel(ob_rr_theme->frame_focused_border_color));
96     XSetWindowBorderWidth(ob_display, dock->frame, ob_rr_theme->fbwidth);
97
98     g_hash_table_insert(window_map, &dock->frame, dock);
99     stacking_add(DOCK_AS_WINDOW(dock));
100 }
101
102 void dock_shutdown(gboolean reconfig)
103 {
104     if (reconfig) {
105         GList *it;
106
107         stacking_remove(DOCK_AS_WINDOW(dock));
108
109         for (it = dock->dock_apps; it; it = g_list_next(it))
110             dock_app_grab_button(it->data, FALSE);
111         return;
112     }
113
114     XDestroyWindow(ob_display, dock->frame);
115     RrAppearanceFree(dock->a_frame);
116     g_hash_table_remove(window_map, &dock->frame);
117     stacking_remove(dock);
118 }
119
120 void dock_add(Window win, XWMHints *wmhints)
121 {
122     ObDockApp *app;
123     XWindowAttributes attrib;
124     gchar **data;
125
126     app = g_new0(ObDockApp, 1);
127     app->obwin.type = Window_DockApp;
128     app->win = win;
129     app->icon_win = (wmhints->flags & IconWindowHint) ?
130         wmhints->icon_window : win;
131
132     if (PROP_GETSS(app->win, wm_class, locale, &data)) {
133         if (data[0]) {
134             app->name = g_strdup(data[0]);
135             if (data[1])
136                 app->class = g_strdup(data[1]);
137         }
138         g_strfreev(data);     
139     }
140
141     if (app->name == NULL) app->name = g_strdup("");
142     if (app->class == NULL) app->class = g_strdup("");
143     
144     if (XGetWindowAttributes(ob_display, app->icon_win, &attrib)) {
145         app->w = attrib.width;
146         app->h = attrib.height;
147     } else {
148         app->w = app->h = 64;
149     }
150
151     dock->dock_apps = g_list_append(dock->dock_apps, app);
152     dock_configure();
153
154     XReparentWindow(ob_display, app->icon_win, dock->frame, app->x, app->y);
155     /*
156       This is the same case as in frame.c for client windows. When Openbox is
157       starting, the window is already mapped so we see unmap events occur for
158       it. There are 2 unmap events generated that we see, one with the 'event'
159       member set the root window, and one set to the client, but both get
160       handled and need to be ignored.
161     */
162     if (ob_state() == OB_STATE_STARTING)
163         app->ignore_unmaps += 2;
164
165     if (app->win != app->icon_win) {
166         /* have to map it so that it can be re-managed on a restart */
167         XMoveWindow(ob_display, app->win, -1000, -1000);
168         XMapWindow(ob_display, app->win);
169     }
170     XMapWindow(ob_display, app->icon_win);
171     XSync(ob_display, False);
172
173     /* specify that if we exit, the window should not be destroyed and should
174        be reparented back to root automatically */
175     XChangeSaveSet(ob_display, app->icon_win, SetModeInsert);
176     XSelectInput(ob_display, app->icon_win, DOCKAPP_EVENT_MASK);
177
178     dock_app_grab_button(app, TRUE);
179
180     g_hash_table_insert(window_map, &app->icon_win, app);
181
182     ob_debug("Managed Dock App: 0x%lx (%s)\n", app->icon_win, app->class);
183 }
184
185 void dock_remove_all()
186 {
187     while (dock->dock_apps)
188         dock_remove(dock->dock_apps->data, TRUE);
189 }
190
191 void dock_remove(ObDockApp *app, gboolean reparent)
192 {
193     dock_app_grab_button(app, FALSE);
194     XSelectInput(ob_display, app->icon_win, NoEventMask);
195     /* remove the window from our save set */
196     XChangeSaveSet(ob_display, app->icon_win, SetModeDelete);
197     XSync(ob_display, False);
198
199     g_hash_table_remove(window_map, &app->icon_win);
200
201     if (reparent)
202         XReparentWindow(ob_display, app->icon_win,
203                         RootWindow(ob_display, ob_screen), app->x, app->y);
204
205     dock->dock_apps = g_list_remove(dock->dock_apps, app);
206     dock_configure();
207
208     ob_debug("Unmanaged Dock App: 0x%lx (%s)\n", app->icon_win, app->class);
209
210     g_free(app->name);
211     g_free(app->class);
212     g_free(app);
213 }
214
215 void dock_configure()
216 {
217     GList *it;
218     gint spot;
219     gint gravity;
220     gint minw, minh;
221     gint strw, strh;
222     Rect *a;
223
224     RrMinSize(dock->a_frame, &minw, &minh);
225
226     dock->w = dock->h = 0;
227
228     /* get the size */
229     for (it = dock->dock_apps; it; it = g_list_next(it)) {
230         ObDockApp *app = it->data;
231         switch (config_dock_orient) {
232         case OB_ORIENTATION_HORZ:
233             dock->w += app->w;
234             dock->h = MAX(dock->h, app->h);
235             break;
236         case OB_ORIENTATION_VERT:
237             dock->w = MAX(dock->w, app->w);
238             dock->h += app->h;
239             break;
240         }
241     }
242
243     spot = (config_dock_orient == OB_ORIENTATION_HORZ ? minw : minh) / 2;
244
245     /* position the apps */
246     for (it = dock->dock_apps; it; it = g_list_next(it)) {
247         ObDockApp *app = it->data;
248         switch (config_dock_orient) {
249         case OB_ORIENTATION_HORZ:
250             app->x = spot;
251             app->y = (dock->h - app->h) / 2;
252             spot += app->w;
253             break;
254         case OB_ORIENTATION_VERT:
255             app->x = (dock->w - app->w) / 2;
256             app->y = spot;
257             spot += app->h;
258             break;
259         }
260
261         XMoveWindow(ob_display, app->icon_win, app->x, app->y);
262     }
263
264     /* used for calculating offsets */
265     dock->w += ob_rr_theme->fbwidth * 2;
266     dock->h += ob_rr_theme->fbwidth * 2;
267
268     a = screen_physical_area();
269
270     /* calculate position */
271     if (config_dock_floating) {
272         dock->x = config_dock_x;
273         dock->y = config_dock_y;
274         gravity = NorthWestGravity;
275     } else {
276         switch (config_dock_pos) {
277         case OB_DIRECTION_NORTHWEST:
278             dock->x = 0;
279             dock->y = 0;
280             gravity = NorthWestGravity;
281             break;
282         case OB_DIRECTION_NORTH:
283             dock->x = a->width / 2;
284             dock->y = 0;
285             gravity = NorthGravity;
286             break;
287         case OB_DIRECTION_NORTHEAST:
288             dock->x = a->width;
289             dock->y = 0;
290             gravity = NorthEastGravity;
291             break;
292         case OB_DIRECTION_WEST:
293             dock->x = 0;
294             dock->y = a->height / 2;
295             gravity = WestGravity;
296             break;
297         case OB_DIRECTION_EAST:
298             dock->x = a->width;
299             dock->y = a->height / 2;
300             gravity = EastGravity;
301             break;
302         case OB_DIRECTION_SOUTHWEST:
303             dock->x = 0;
304             dock->y = a->height;
305             gravity = SouthWestGravity;
306             break;
307         case OB_DIRECTION_SOUTH:
308             dock->x = a->width / 2;
309             dock->y = a->height;
310             gravity = SouthGravity;
311             break;
312         case OB_DIRECTION_SOUTHEAST:
313             dock->x = a->width;
314             dock->y = a->height;
315             gravity = SouthEastGravity;
316             break;
317         default:
318             g_assert_not_reached();
319         }
320     }
321
322     switch(gravity) {
323     case NorthGravity:
324     case CenterGravity:
325     case SouthGravity:
326         dock->x -= dock->w / 2;
327         break;
328     case NorthEastGravity:
329     case EastGravity:
330     case SouthEastGravity:
331         dock->x -= dock->w;
332         break;
333     }
334     switch(gravity) {
335     case WestGravity:
336     case CenterGravity:
337     case EastGravity:
338         dock->y -= dock->h / 2;
339         break;
340     case SouthWestGravity:
341     case SouthGravity:
342     case SouthEastGravity:
343         dock->y -= dock->h;
344         break;
345     }
346
347     if (config_dock_hide && dock->hidden) {
348         if (!config_dock_floating) {
349             switch (config_dock_pos) {
350             case OB_DIRECTION_NORTHWEST:
351                 switch (config_dock_orient) {
352                 case OB_ORIENTATION_HORZ:
353                     dock->y -= dock->h - ob_rr_theme->fbwidth;
354                     break;
355                 case OB_ORIENTATION_VERT:
356                     dock->x -= dock->w - ob_rr_theme->fbwidth;
357                     break;
358                 }
359                 break;
360             case OB_DIRECTION_NORTH:
361                 dock->y -= dock->h - ob_rr_theme->fbwidth;
362                 break;
363             case OB_DIRECTION_NORTHEAST:
364                 switch (config_dock_orient) {
365                 case OB_ORIENTATION_HORZ:
366                     dock->y -= dock->h - ob_rr_theme->fbwidth;
367                     break;
368                 case OB_ORIENTATION_VERT:
369                     dock->x += dock->w - ob_rr_theme->fbwidth;
370                     break;
371                 }
372                 break;
373             case OB_DIRECTION_WEST:
374                 dock->x -= dock->w - ob_rr_theme->fbwidth;
375                 break;
376             case OB_DIRECTION_EAST:
377                 dock->x += dock->w - ob_rr_theme->fbwidth;
378                 break;
379             case OB_DIRECTION_SOUTHWEST:
380                 switch (config_dock_orient) {
381                 case OB_ORIENTATION_HORZ:
382                     dock->y += dock->h - ob_rr_theme->fbwidth;
383                     break;
384                 case OB_ORIENTATION_VERT:
385                     dock->x -= dock->w - ob_rr_theme->fbwidth;
386                     break;
387                 } break;
388             case OB_DIRECTION_SOUTH:
389                 dock->y += dock->h - ob_rr_theme->fbwidth;
390                 break;
391             case OB_DIRECTION_SOUTHEAST:
392                 switch (config_dock_orient) {
393                 case OB_ORIENTATION_HORZ:
394                     dock->y += dock->h - ob_rr_theme->fbwidth;
395                     break;
396                 case OB_ORIENTATION_VERT:
397                     dock->x += dock->w - ob_rr_theme->fbwidth;
398                     break;
399                 }
400                 break;
401             }    
402         }
403     }
404
405     if (!config_dock_floating && config_dock_hide) {
406         strw = ob_rr_theme->fbwidth;
407         strh = ob_rr_theme->fbwidth;
408     } else {
409         strw = dock->w;
410         strh = dock->h;
411     }
412
413     /* set the strut */
414     if (!dock->dock_apps) {
415         STRUT_PARTIAL_SET(dock_strut, 0, 0, 0, 0,
416                           0, 0, 0, 0, 0, 0, 0, 0);
417     } else if (config_dock_floating || config_dock_nostrut)
418     {
419         STRUT_PARTIAL_SET(dock_strut, 0, 0, 0, 0,
420                           0, 0, 0, 0, 0, 0, 0, 0);
421     } else {
422         switch (config_dock_pos) {
423         case OB_DIRECTION_NORTHWEST:
424             switch (config_dock_orient) {
425             case OB_ORIENTATION_HORZ:
426                 STRUT_PARTIAL_SET(dock_strut, 0, strh, 0, 0,
427                                   0, 0, dock->x, dock->x + dock->w - 1,
428                                   0, 0, 0, 0);
429                 break;
430             case OB_ORIENTATION_VERT:
431                 STRUT_PARTIAL_SET(dock_strut, strw, 0, 0, 0,
432                                   dock->y, dock->y + dock->h - 1,
433                                   0, 0, 0, 0, 0, 0);
434                 break;
435             }
436             break;
437         case OB_DIRECTION_NORTH:
438             STRUT_PARTIAL_SET(dock_strut, 0, strh, 0, 0,
439                               dock->x, dock->x + dock->w - 1,
440                               0, 0, 0, 0, 0, 0);
441             break;
442         case OB_DIRECTION_NORTHEAST:
443             switch (config_dock_orient) {
444             case OB_ORIENTATION_HORZ:
445                 STRUT_PARTIAL_SET(dock_strut, 0, strh, 0, 0,
446                                   0, 0, dock->x, dock->x + dock->w -1,
447                                   0, 0, 0, 0);
448                 break;
449             case OB_ORIENTATION_VERT:
450                 STRUT_PARTIAL_SET(dock_strut, 0, 0, strw, 0,
451                                   0, 0, 0, 0,
452                                   dock->y, dock->y + dock->h - 1, 0, 0);
453                 break;
454             }
455             break;
456         case OB_DIRECTION_WEST:
457             STRUT_PARTIAL_SET(dock_strut, strw, 0, 0, 0,
458                               dock->y, dock->y + dock->h - 1,
459                               0, 0, 0, 0, 0, 0);
460             break;
461         case OB_DIRECTION_EAST:
462             STRUT_PARTIAL_SET(dock_strut, 0, 0, strw, 0,
463                               0, 0, 0, 0,
464                               dock->y, dock->y + dock->h - 1, 0, 0);
465             break;
466         case OB_DIRECTION_SOUTHWEST:
467             switch (config_dock_orient) {
468             case OB_ORIENTATION_HORZ:
469                 STRUT_PARTIAL_SET(dock_strut, 0, 0, 0, strh,
470                                   0, 0, 0, 0, 0, 0,
471                                   dock->x, dock->x + dock->w - 1);
472                 break;
473             case OB_ORIENTATION_VERT:
474                 STRUT_PARTIAL_SET(dock_strut, strw, 0, 0, 0,
475                                   dock->y, dock->y + dock->h - 1,
476                                   0, 0, 0, 0, 0, 0);
477                 break;
478             }
479             break;
480         case OB_DIRECTION_SOUTH:
481             STRUT_PARTIAL_SET(dock_strut, 0, 0, 0, strh,
482                               0, 0, 0, 0, 0, 0,
483                               dock->x, dock->x + dock->w - 1);
484             break;
485         case OB_DIRECTION_SOUTHEAST:
486             switch (config_dock_orient) {
487             case OB_ORIENTATION_HORZ:
488                 STRUT_PARTIAL_SET(dock_strut, 0, 0, 0, strh,
489                                   0, 0, 0, 0, 0, 0,
490                                   dock->x, dock->x + dock->w - 1);
491                 break;
492             case OB_ORIENTATION_VERT:
493                 STRUT_PARTIAL_SET(dock_strut, 0, 0, strw, 0,
494                                   0, 0, 0, 0,
495                                   dock->y, dock->y + dock->h - 1, 0, 0);
496                 break;
497             }
498             break;
499         }
500     }
501
502     dock->w += minw;
503     dock->h += minh;
504
505     /* not used for actually sizing shit */
506     dock->w -= ob_rr_theme->fbwidth * 2;
507     dock->h -= ob_rr_theme->fbwidth * 2;
508
509     if (dock->dock_apps) {
510         g_assert(dock->w > 0);
511         g_assert(dock->h > 0);
512
513         XMoveResizeWindow(ob_display, dock->frame,
514                           dock->x, dock->y, dock->w, dock->h);
515
516         RrPaint(dock->a_frame, dock->frame, dock->w, dock->h);
517         XMapWindow(ob_display, dock->frame);
518     } else
519         XUnmapWindow(ob_display, dock->frame);
520
521     /* but they are useful outside of this function! */
522     dock->w += ob_rr_theme->fbwidth * 2;
523     dock->h += ob_rr_theme->fbwidth * 2;
524
525     screen_update_areas();
526 }
527
528 void dock_app_configure(ObDockApp *app, gint w, gint h)
529 {
530     app->w = w;
531     app->h = h;
532     dock_configure();
533 }
534
535 void dock_app_drag(ObDockApp *app, XMotionEvent *e)
536 {
537     ObDockApp *over = NULL;
538     GList *it;
539     gint x, y;
540     gboolean after;
541     gboolean stop;
542
543     x = e->x_root;
544     y = e->y_root;
545
546     /* are we on top of the dock? */
547     if (!(x >= dock->x &&
548           y >= dock->y &&
549           x < dock->x + dock->w &&
550           y < dock->y + dock->h))
551         return;
552
553     x -= dock->x;
554     y -= dock->y;
555
556     /* which dock app are we on top of? */
557     stop = FALSE;
558     for (it = dock->dock_apps; it; it = g_list_next(it)) {
559         over = it->data;
560         switch (config_dock_orient) {
561         case OB_ORIENTATION_HORZ:
562             if (x >= over->x && x < over->x + over->w)
563                 stop = TRUE;
564             break;
565         case OB_ORIENTATION_VERT:
566             if (y >= over->y && y < over->y + over->h)
567                 stop = TRUE;
568             break;
569         }
570         /* dont go to it->next! */
571         if (stop) break;
572     }
573     if (!it || app == over) return;
574
575     x -= over->x;
576     y -= over->y;
577
578     switch (config_dock_orient) {
579     case OB_ORIENTATION_HORZ:
580         after = (x > over->w / 2);
581         break;
582     case OB_ORIENTATION_VERT:
583         after = (y > over->h / 2);
584         break;
585     default:
586         g_assert_not_reached();
587     }
588
589     /* remove before doing the it->next! */
590     dock->dock_apps = g_list_remove(dock->dock_apps, app);
591
592     if (after) it = it->next;
593
594     dock->dock_apps = g_list_insert_before(dock->dock_apps, it, app);
595     dock_configure();
596 }
597
598 static gboolean hide_timeout(gpointer data)
599 {
600     /* hide */
601     dock->hidden = TRUE;
602     dock_configure();
603
604     return FALSE; /* don't repeat */
605 }
606
607 static gboolean show_timeout(gpointer data)
608 {
609     /* hide */
610     dock->hidden = FALSE;
611     dock_configure();
612
613     return FALSE; /* don't repeat */
614 }
615
616 void dock_hide(gboolean hide)
617 {
618     if (!hide) {
619         if (dock->hidden && config_dock_hide) {
620             ob_main_loop_timeout_add(ob_main_loop, config_dock_show_delay,
621                                  show_timeout, NULL, g_direct_equal, NULL);
622         } else if (!dock->hidden && config_dock_hide) {
623             ob_main_loop_timeout_remove(ob_main_loop, hide_timeout);
624         }
625     } else {
626         if (!dock->hidden && config_dock_hide) {
627             ob_main_loop_timeout_add(ob_main_loop, config_dock_hide_delay,
628                                  hide_timeout, NULL, g_direct_equal, NULL);
629         } else if (dock->hidden && config_dock_hide) {
630             ob_main_loop_timeout_remove(ob_main_loop, show_timeout);
631         }
632     }
633 }