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