Add some more conditions to the If action
[dana/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 "screen.h"
23 #include "config.h"
24 #include "grab.h"
25 #include "openbox.h"
26 #include "obrender/theme.h"
27 #include "obt/prop.h"
28
29 #define DOCK_EVENT_MASK (ButtonPressMask | ButtonReleaseMask | \
30                          EnterWindowMask | LeaveWindowMask)
31 #define DOCKAPP_EVENT_MASK (StructureNotifyMask)
32 #define DOCK_NOPROPAGATEMASK (ButtonPressMask | ButtonReleaseMask | \
33                               ButtonMotionMask)
34
35 static ObDock *dock;
36
37 StrutPartial dock_strut;
38
39 static void dock_app_grab_button(ObDockApp *app, gboolean grab)
40 {
41     if (grab) {
42         grab_button_full(config_dock_app_move_button,
43                          config_dock_app_move_modifiers, app->icon_win,
44                          ButtonPressMask | ButtonReleaseMask |
45                          ButtonMotionMask,
46                          GrabModeAsync, OB_CURSOR_MOVE);
47     } else {
48         ungrab_button(config_dock_app_move_button,
49                       config_dock_app_move_modifiers, app->icon_win);
50     }
51 }
52
53 static guint window_hash(Window *w) { return *w; }
54 static gboolean window_comp(Window *w1, Window *w2) { return *w1 == *w2; }
55
56 void dock_startup(gboolean reconfig)
57 {
58     XSetWindowAttributes attrib;
59
60     if (reconfig) {
61         GList *it;
62
63         XSetWindowBorder(obt_display, dock->frame,
64                          RrColorPixel(ob_rr_theme->osd_border_color));
65         XSetWindowBorderWidth(obt_display, dock->frame, ob_rr_theme->obwidth);
66
67         RrAppearanceFree(dock->a_frame);
68         dock->a_frame = RrAppearanceCopy(ob_rr_theme->osd_bg);
69
70         stacking_add(DOCK_AS_WINDOW(dock));
71
72         dock_configure();
73         dock_hide(TRUE);
74
75         for (it = dock->dock_apps; it; it = g_list_next(it))
76             dock_app_grab_button(it->data, TRUE);
77         return;
78     }
79
80     STRUT_PARTIAL_SET(dock_strut, 0, 0, 0, 0,
81                       0, 0, 0, 0, 0, 0, 0, 0);
82
83     dock = g_slice_new0(ObDock);
84     dock->obwin.type = OB_WINDOW_CLASS_DOCK;
85
86     dock->hidden = TRUE;
87
88     dock->dock_map = g_hash_table_new((GHashFunc)window_hash,
89                                       (GEqualFunc)window_comp);
90
91     attrib.event_mask = DOCK_EVENT_MASK;
92     attrib.override_redirect = True;
93     attrib.do_not_propagate_mask = DOCK_NOPROPAGATEMASK;
94     dock->frame = XCreateWindow(obt_display, obt_root(ob_screen),
95                                 0, 0, 1, 1, 0,
96                                 RrDepth(ob_rr_inst), InputOutput,
97                                 RrVisual(ob_rr_inst),
98                                 CWOverrideRedirect | CWEventMask |
99                                 CWDontPropagate,
100                                 &attrib);
101     dock->a_frame = RrAppearanceCopy(ob_rr_theme->osd_bg);
102     XSetWindowBorder(obt_display, dock->frame,
103                      RrColorPixel(ob_rr_theme->osd_border_color));
104     XSetWindowBorderWidth(obt_display, dock->frame, ob_rr_theme->obwidth);
105
106     /* Setting the window type so xcompmgr can tell what it is */
107     OBT_PROP_SET32(dock->frame, NET_WM_WINDOW_TYPE, ATOM,
108                    OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_DOCK));
109
110     window_add(&dock->frame, DOCK_AS_WINDOW(dock));
111     stacking_add(DOCK_AS_WINDOW(dock));
112 }
113
114 void dock_shutdown(gboolean reconfig)
115 {
116     if (reconfig) {
117         GList *it;
118
119         stacking_remove(DOCK_AS_WINDOW(dock));
120
121         for (it = dock->dock_apps; it; it = g_list_next(it))
122             dock_app_grab_button(it->data, FALSE);
123         return;
124     }
125
126     g_hash_table_destroy(dock->dock_map);
127
128     XDestroyWindow(obt_display, dock->frame);
129     RrAppearanceFree(dock->a_frame);
130     window_remove(dock->frame);
131     stacking_remove(dock);
132     g_slice_free(ObDock, dock);
133     dock = NULL;
134 }
135
136 void dock_manage(Window icon_win, Window name_win)
137 {
138     ObDockApp *app;
139     XWindowAttributes attrib;
140     gchar **data;
141
142     app = g_slice_new0(ObDockApp);
143     app->name_win = name_win;
144     app->icon_win = icon_win;
145
146     if (OBT_PROP_GETSS(app->name_win, WM_CLASS, locale, &data)) {
147         if (data[0]) {
148             app->name = g_strdup(data[0]);
149             if (data[1])
150                 app->class = g_strdup(data[1]);
151         }
152         g_strfreev(data);
153     }
154
155     if (app->name == NULL) app->name = g_strdup("");
156     if (app->class == NULL) app->class = g_strdup("");
157
158     if (XGetWindowAttributes(obt_display, app->icon_win, &attrib)) {
159         app->w = attrib.width;
160         app->h = attrib.height;
161     } else {
162         app->w = app->h = 64;
163     }
164
165     dock->dock_apps = g_list_append(dock->dock_apps, app);
166     g_hash_table_insert(dock->dock_map, &app->icon_win, app);
167     dock_configure();
168
169     XReparentWindow(obt_display, app->icon_win, dock->frame, app->x, app->y);
170     /*
171       This is the same case as in frame.c for client windows. When Openbox is
172       starting, the window is already mapped so we see unmap events occur for
173       it. There are 2 unmap events generated that we see, one with the 'event'
174       member set the root window, and one set to the client, but both get
175       handled and need to be ignored.
176     */
177     if (ob_state() == OB_STATE_STARTING)
178         app->ignore_unmaps += 2;
179     XChangeSaveSet(obt_display, app->icon_win, SetModeInsert);
180     XMapWindow(obt_display, app->icon_win);
181
182     if (app->name_win != app->icon_win) {
183         XReparentWindow(obt_display, app->name_win, dock->frame, -1000, -1000);
184         XChangeSaveSet(obt_display, app->name_win, SetModeInsert);
185         XMapWindow(obt_display, app->name_win);
186     }
187
188     XSync(obt_display, False);
189
190     XSelectInput(obt_display, app->icon_win, DOCKAPP_EVENT_MASK);
191
192     dock_app_grab_button(app, TRUE);
193
194     ob_debug("Managed Dock App: 0x%lx 0x%lx (%s)",
195              app->icon_win, app->name_win, app->class);
196
197     grab_server(FALSE);
198 }
199
200 void dock_unmanage_all(void)
201 {
202     while (dock->dock_apps)
203         dock_unmanage(dock->dock_apps->data, TRUE);
204 }
205
206 void dock_unmanage(ObDockApp *app, gboolean reparent)
207 {
208     dock_app_grab_button(app, FALSE);
209     XSelectInput(obt_display, app->icon_win, NoEventMask);
210     /* remove the window from our save set */
211     XChangeSaveSet(obt_display, app->icon_win, SetModeDelete);
212     XSync(obt_display, False);
213
214     if (reparent) {
215         XReparentWindow(obt_display, app->icon_win, obt_root(ob_screen), 0, 0);
216         if (app->name_win != app->icon_win)
217             XReparentWindow(obt_display, app->name_win,
218                             obt_root(ob_screen), 0, 0);
219     }
220
221     dock->dock_apps = g_list_remove(dock->dock_apps, app);
222     g_hash_table_remove(dock->dock_map, &app->icon_win);
223     dock_configure();
224
225     ob_debug("Unmanaged Dock App: 0x%lx (%s)", app->icon_win, app->class);
226
227     g_free(app->name);
228     g_free(app->class);
229     g_slice_free(ObDockApp, app);
230 }
231
232 void dock_configure(void)
233 {
234     GList *it;
235     gint hspot, vspot;
236     gint gravity;
237     gint l, r, t, b;
238     gint strw, strh;
239     const Rect *a;
240     gint hidesize;
241
242     RrMargins(dock->a_frame, &l, &t, &r, &b);
243     hidesize = MAX(1, ob_rr_theme->obwidth);
244
245     dock->area.width = dock->area.height = 0;
246
247     /* get the size */
248     for (it = dock->dock_apps; it; it = g_list_next(it)) {
249         ObDockApp *app = it->data;
250         switch (config_dock_orient) {
251         case OB_ORIENTATION_HORZ:
252             dock->area.width += app->w;
253             dock->area.height = MAX(dock->area.height, app->h);
254             break;
255         case OB_ORIENTATION_VERT:
256             dock->area.width = MAX(dock->area.width, app->w);
257             dock->area.height += app->h;
258             break;
259         }
260     }
261
262     if (dock->dock_apps) {
263         dock->area.width += l + r;
264         dock->area.height += t + b;
265     }
266
267     hspot = l;
268     vspot = t;
269
270     /* position the apps */
271     for (it = dock->dock_apps; it; it = g_list_next(it)) {
272         ObDockApp *app = it->data;
273         switch (config_dock_orient) {
274         case OB_ORIENTATION_HORZ:
275             app->x = hspot;
276             app->y = (dock->area.height - app->h) / 2;
277             hspot += app->w;
278             break;
279         case OB_ORIENTATION_VERT:
280             app->x = (dock->area.width - app->w) / 2;
281             app->y = vspot;
282             vspot += app->h;
283             break;
284         }
285
286         XMoveWindow(obt_display, app->icon_win, app->x, app->y);
287     }
288
289     /* used for calculating offsets */
290     dock->area.width += ob_rr_theme->obwidth * 2;
291     dock->area.height += ob_rr_theme->obwidth * 2;
292
293     a = screen_physical_area_all_monitors();
294
295     /* calculate position */
296     if (config_dock_floating) {
297         dock->area.x = config_dock_x;
298         dock->area.y = config_dock_y;
299         gravity = NorthWestGravity;
300     } else {
301         switch (config_dock_pos) {
302         case OB_DIRECTION_NORTHWEST:
303             dock->area.x = 0;
304             dock->area.y = 0;
305             gravity = NorthWestGravity;
306             break;
307         case OB_DIRECTION_NORTH:
308             dock->area.x = a->width / 2;
309             dock->area.y = 0;
310             gravity = NorthGravity;
311             break;
312         case OB_DIRECTION_NORTHEAST:
313             dock->area.x = a->width;
314             dock->area.y = 0;
315             gravity = NorthEastGravity;
316             break;
317         case OB_DIRECTION_WEST:
318             dock->area.x = 0;
319             dock->area.y = a->height / 2;
320             gravity = WestGravity;
321             break;
322         case OB_DIRECTION_EAST:
323             dock->area.x = a->width;
324             dock->area.y = a->height / 2;
325             gravity = EastGravity;
326             break;
327         case OB_DIRECTION_SOUTHWEST:
328             dock->area.x = 0;
329             dock->area.y = a->height;
330             gravity = SouthWestGravity;
331             break;
332         case OB_DIRECTION_SOUTH:
333             dock->area.x = a->width / 2;
334             dock->area.y = a->height;
335             gravity = SouthGravity;
336             break;
337         case OB_DIRECTION_SOUTHEAST:
338             dock->area.x = a->width;
339             dock->area.y = a->height;
340             gravity = SouthEastGravity;
341             break;
342         default:
343             g_assert_not_reached();
344         }
345     }
346
347     switch(gravity) {
348     case NorthGravity:
349     case CenterGravity:
350     case SouthGravity:
351         dock->area.x -= dock->area.width / 2;
352         break;
353     case NorthEastGravity:
354     case EastGravity:
355     case SouthEastGravity:
356         dock->area.x -= dock->area.width;
357         break;
358     }
359     switch(gravity) {
360     case WestGravity:
361     case CenterGravity:
362     case EastGravity:
363         dock->area.y -= dock->area.height / 2;
364         break;
365     case SouthWestGravity:
366     case SouthGravity:
367     case SouthEastGravity:
368         dock->area.y -= dock->area.height;
369         break;
370     }
371
372     if (config_dock_hide && dock->hidden) {
373         if (!config_dock_floating) {
374             switch (config_dock_pos) {
375             case OB_DIRECTION_NORTHWEST:
376                 switch (config_dock_orient) {
377                 case OB_ORIENTATION_HORZ:
378                     dock->area.y -= dock->area.height - hidesize;
379                     break;
380                 case OB_ORIENTATION_VERT:
381                     dock->area.x -= dock->area.width - hidesize;
382                     break;
383                 }
384                 break;
385             case OB_DIRECTION_NORTH:
386                 dock->area.y -= dock->area.height - hidesize;
387                 break;
388             case OB_DIRECTION_NORTHEAST:
389                 switch (config_dock_orient) {
390                 case OB_ORIENTATION_HORZ:
391                     dock->area.y -= dock->area.height - hidesize;
392                     break;
393                 case OB_ORIENTATION_VERT:
394                     dock->area.x += dock->area.width - hidesize;
395                     break;
396                 }
397                 break;
398             case OB_DIRECTION_WEST:
399                 dock->area.x -= dock->area.width - hidesize;
400                 break;
401             case OB_DIRECTION_EAST:
402                 dock->area.x += dock->area.width - hidesize;
403                 break;
404             case OB_DIRECTION_SOUTHWEST:
405                 switch (config_dock_orient) {
406                 case OB_ORIENTATION_HORZ:
407                     dock->area.y += dock->area.height - hidesize;
408                     break;
409                 case OB_ORIENTATION_VERT:
410                     dock->area.x -= dock->area.width - hidesize;
411                     break;
412                 } break;
413             case OB_DIRECTION_SOUTH:
414                 dock->area.y += dock->area.height - hidesize;
415                 break;
416             case OB_DIRECTION_SOUTHEAST:
417                 switch (config_dock_orient) {
418                 case OB_ORIENTATION_HORZ:
419                     dock->area.y += dock->area.height - hidesize;
420                     break;
421                 case OB_ORIENTATION_VERT:
422                     dock->area.x += dock->area.width - hidesize;
423                     break;
424                 }
425                 break;
426             }
427         }
428     }
429
430     if (!config_dock_floating && config_dock_hide) {
431         strw = hidesize;
432         strh = hidesize;
433     } else {
434         strw = dock->area.width;
435         strh = dock->area.height;
436     }
437
438     /* set the strut */
439     if (!dock->dock_apps) {
440         STRUT_PARTIAL_SET(dock_strut, 0, 0, 0, 0,
441                           0, 0, 0, 0, 0, 0, 0, 0);
442     }
443     else if (config_dock_floating || config_dock_nostrut) {
444         STRUT_PARTIAL_SET(dock_strut, 0, 0, 0, 0,
445                           0, 0, 0, 0, 0, 0, 0, 0);
446     }
447     else {
448         switch (config_dock_pos) {
449         case OB_DIRECTION_NORTHWEST:
450             switch (config_dock_orient) {
451             case OB_ORIENTATION_HORZ:
452                 STRUT_PARTIAL_SET(dock_strut, 0, strh, 0, 0,
453                                   0, 0, dock->area.x, dock->area.x
454                                   + dock->area.width - 1, 0, 0, 0, 0);
455                 break;
456             case OB_ORIENTATION_VERT:
457                 STRUT_PARTIAL_SET(dock_strut, strw, 0, 0, 0,
458                                   dock->area.y, dock->area.y
459                                   + dock->area.height - 1, 0, 0, 0, 0, 0, 0);
460                 break;
461             }
462             break;
463         case OB_DIRECTION_NORTH:
464             STRUT_PARTIAL_SET(dock_strut, 0, strh, 0, 0,
465                               0, 0, dock->area.x, dock->area.x
466                               + dock->area.width - 1, 0, 0, 0, 0);
467             break;
468         case OB_DIRECTION_NORTHEAST:
469             switch (config_dock_orient) {
470             case OB_ORIENTATION_HORZ:
471                 STRUT_PARTIAL_SET(dock_strut, 0, strh, 0, 0,
472                                   0, 0, dock->area.x, dock->area.x
473                                   + dock->area.width -1, 0, 0, 0, 0);
474                 break;
475             case OB_ORIENTATION_VERT:
476                 STRUT_PARTIAL_SET(dock_strut, 0, 0, strw, 0,
477                                   0, 0, 0, 0, dock->area.y, dock->area.y
478                                   + dock->area.height - 1, 0, 0);
479                 break;
480             }
481             break;
482         case OB_DIRECTION_WEST:
483             STRUT_PARTIAL_SET(dock_strut, strw, 0, 0, 0,
484                               dock->area.y, dock->area.y
485                               + dock->area.height - 1, 0, 0, 0, 0, 0, 0);
486             break;
487         case OB_DIRECTION_EAST:
488             STRUT_PARTIAL_SET(dock_strut, 0, 0, strw, 0,
489                               0, 0, 0, 0, dock->area.y, dock->area.y
490                               + dock->area.height - 1, 0, 0);
491             break;
492         case OB_DIRECTION_SOUTHWEST:
493             switch (config_dock_orient) {
494             case OB_ORIENTATION_HORZ:
495                 STRUT_PARTIAL_SET(dock_strut, 0, 0, 0, strh,
496                                   0, 0, 0, 0, 0, 0, dock->area.x, dock->area.x
497                                   + dock->area.width - 1);
498                 break;
499             case OB_ORIENTATION_VERT:
500                 STRUT_PARTIAL_SET(dock_strut, strw, 0, 0, 0,
501                                   dock->area.y, dock->area.y
502                                   + dock->area.height - 1, 0, 0, 0, 0, 0, 0);
503                 break;
504             }
505             break;
506         case OB_DIRECTION_SOUTH:
507             STRUT_PARTIAL_SET(dock_strut, 0, 0, 0, strh,
508                               0, 0, 0, 0, 0, 0, dock->area.x, dock->area.x
509                               + dock->area.width - 1);
510             break;
511         case OB_DIRECTION_SOUTHEAST:
512             switch (config_dock_orient) {
513             case OB_ORIENTATION_HORZ:
514                 STRUT_PARTIAL_SET(dock_strut, 0, 0, 0, strh,
515                                   0, 0, 0, 0, 0, 0, dock->area.x,
516                                   dock->area.x + dock->area.width - 1);
517                 break;
518             case OB_ORIENTATION_VERT:
519                 STRUT_PARTIAL_SET(dock_strut, 0, 0, strw, 0,
520                                   0, 0, 0, 0, dock->area.y, dock->area.y
521                                   + dock->area.height - 1, 0, 0);
522                 break;
523             }
524             break;
525         }
526     }
527
528     /* not used for actually sizing shit */
529     dock->area.width -= ob_rr_theme->obwidth * 2;
530     dock->area.height -= ob_rr_theme->obwidth * 2;
531
532     if (dock->dock_apps) {
533         g_assert(dock->area.width > 0);
534         g_assert(dock->area.height > 0);
535
536         XMoveResizeWindow(obt_display, dock->frame, dock->area.x, dock->area.y,
537                           dock->area.width, dock->area.height);
538
539         RrPaint(dock->a_frame, dock->frame, dock->area.width,
540                 dock->area.height);
541         XMapWindow(obt_display, dock->frame);
542     } else
543         XUnmapWindow(obt_display, dock->frame);
544
545     /* but they are useful outside of this function! but don't add it if the
546        dock is actually not visible */
547     if (dock->dock_apps) {
548         dock->area.width += ob_rr_theme->obwidth * 2;
549         dock->area.height += ob_rr_theme->obwidth * 2;
550     }
551
552     screen_update_areas();
553 }
554
555 void dock_app_configure(ObDockApp *app, gint w, gint h)
556 {
557     app->w = w;
558     app->h = h;
559     dock_configure();
560 }
561
562 void dock_app_drag(ObDockApp *app, XMotionEvent *e)
563 {
564     ObDockApp *over = NULL;
565     GList *it;
566     gint x, y;
567     gboolean after;
568     gboolean stop;
569
570     x = e->x_root;
571     y = e->y_root;
572
573     /* are we on top of the dock? */
574     if (!(x >= dock->area.x &&
575           y >= dock->area.y &&
576           x < dock->area.x + dock->area.width &&
577           y < dock->area.y + dock->area.height))
578         return;
579
580     x -= dock->area.x;
581     y -= dock->area.y;
582
583     /* which dock app are we on top of? */
584     stop = FALSE;
585     for (it = dock->dock_apps; it; it = g_list_next(it)) {
586         over = it->data;
587         switch (config_dock_orient) {
588         case OB_ORIENTATION_HORZ:
589             if (x >= over->x && x < over->x + over->w)
590                 stop = TRUE;
591             break;
592         case OB_ORIENTATION_VERT:
593             if (y >= over->y && y < over->y + over->h)
594                 stop = TRUE;
595             break;
596         }
597         /* dont go to it->next! */
598         if (stop) break;
599     }
600     if (!it || app == over) return;
601
602     x -= over->x;
603     y -= over->y;
604
605     switch (config_dock_orient) {
606     case OB_ORIENTATION_HORZ:
607         after = (x > over->w / 2);
608         break;
609     case OB_ORIENTATION_VERT:
610         after = (y > over->h / 2);
611         break;
612     default:
613         g_assert_not_reached();
614     }
615
616     /* remove before doing the it->next! */
617     dock->dock_apps = g_list_remove(dock->dock_apps, app);
618
619     if (after) it = it->next;
620
621     dock->dock_apps = g_list_insert_before(dock->dock_apps, it, app);
622     dock_configure();
623 }
624
625 static gboolean hide_timeout(gpointer data)
626 {
627     /* hide */
628     dock->hidden = TRUE;
629     dock_configure();
630
631     return FALSE; /* don't repeat */
632 }
633
634 static gboolean show_timeout(gpointer data)
635 {
636     /* hide */
637     dock->hidden = FALSE;
638     dock_configure();
639
640     return FALSE; /* don't repeat */
641 }
642
643 void dock_hide(gboolean hide)
644 {
645     if (!hide) {
646         if (dock->hidden && config_dock_hide) {
647             obt_main_loop_timeout_add(ob_main_loop,
648                                       config_dock_show_delay * 1000,
649                                       show_timeout, NULL,
650                                       g_direct_equal, NULL);
651         } else if (!dock->hidden && config_dock_hide) {
652             obt_main_loop_timeout_remove(ob_main_loop, hide_timeout);
653         }
654     } else {
655         if (!dock->hidden && config_dock_hide) {
656             obt_main_loop_timeout_add(ob_main_loop,
657                                       config_dock_hide_delay * 1000,
658                                       hide_timeout, NULL,
659                                       g_direct_equal, NULL);
660         } else if (dock->hidden && config_dock_hide) {
661             obt_main_loop_timeout_remove(ob_main_loop, show_timeout);
662         }
663     }
664 }
665
666 void dock_get_area(Rect *a)
667 {
668     RECT_SET(*a, dock->area.x, dock->area.y,
669              dock->area.width, dock->area.height);
670 }
671
672 ObDockApp* dock_find_dockapp(Window xwin)
673 {
674     return g_hash_table_lookup(dock->dock_map, &xwin);
675 }