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