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