]> Dogcows Code - chaz/openbox/blob - openbox/dock.c
Add a focus debug message for the frame's visible focus state
[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(void)
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(void)
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 gint hidesize;
231
232 RrMargins(dock->a_frame, &l, &t, &r, &b);
233 hidesize = MAX(1, ob_rr_theme->obwidth);
234
235 dock->area.width = dock->area.height = 0;
236
237 /* get the size */
238 for (it = dock->dock_apps; it; it = g_list_next(it)) {
239 ObDockApp *app = it->data;
240 switch (config_dock_orient) {
241 case OB_ORIENTATION_HORZ:
242 dock->area.width += app->w;
243 dock->area.height = MAX(dock->area.height, app->h);
244 break;
245 case OB_ORIENTATION_VERT:
246 dock->area.width = MAX(dock->area.width, app->w);
247 dock->area.height += app->h;
248 break;
249 }
250 }
251
252 if (dock->dock_apps) {
253 dock->area.width += l + r;
254 dock->area.height += t + b;
255 }
256
257 hspot = l;
258 vspot = t;
259
260 /* position the apps */
261 for (it = dock->dock_apps; it; it = g_list_next(it)) {
262 ObDockApp *app = it->data;
263 switch (config_dock_orient) {
264 case OB_ORIENTATION_HORZ:
265 app->x = hspot;
266 app->y = (dock->area.height - app->h) / 2;
267 hspot += app->w;
268 break;
269 case OB_ORIENTATION_VERT:
270 app->x = (dock->area.width - app->w) / 2;
271 app->y = vspot;
272 vspot += app->h;
273 break;
274 }
275
276 XMoveWindow(ob_display, app->icon_win, app->x, app->y);
277 }
278
279 /* used for calculating offsets */
280 dock->area.width += ob_rr_theme->obwidth * 2;
281 dock->area.height += ob_rr_theme->obwidth * 2;
282
283 a = screen_physical_area_all_monitors();
284
285 /* calculate position */
286 if (config_dock_floating) {
287 dock->area.x = config_dock_x;
288 dock->area.y = config_dock_y;
289 gravity = NorthWestGravity;
290 } else {
291 switch (config_dock_pos) {
292 case OB_DIRECTION_NORTHWEST:
293 dock->area.x = 0;
294 dock->area.y = 0;
295 gravity = NorthWestGravity;
296 break;
297 case OB_DIRECTION_NORTH:
298 dock->area.x = a->width / 2;
299 dock->area.y = 0;
300 gravity = NorthGravity;
301 break;
302 case OB_DIRECTION_NORTHEAST:
303 dock->area.x = a->width;
304 dock->area.y = 0;
305 gravity = NorthEastGravity;
306 break;
307 case OB_DIRECTION_WEST:
308 dock->area.x = 0;
309 dock->area.y = a->height / 2;
310 gravity = WestGravity;
311 break;
312 case OB_DIRECTION_EAST:
313 dock->area.x = a->width;
314 dock->area.y = a->height / 2;
315 gravity = EastGravity;
316 break;
317 case OB_DIRECTION_SOUTHWEST:
318 dock->area.x = 0;
319 dock->area.y = a->height;
320 gravity = SouthWestGravity;
321 break;
322 case OB_DIRECTION_SOUTH:
323 dock->area.x = a->width / 2;
324 dock->area.y = a->height;
325 gravity = SouthGravity;
326 break;
327 case OB_DIRECTION_SOUTHEAST:
328 dock->area.x = a->width;
329 dock->area.y = a->height;
330 gravity = SouthEastGravity;
331 break;
332 default:
333 g_assert_not_reached();
334 }
335 }
336
337 switch(gravity) {
338 case NorthGravity:
339 case CenterGravity:
340 case SouthGravity:
341 dock->area.x -= dock->area.width / 2;
342 break;
343 case NorthEastGravity:
344 case EastGravity:
345 case SouthEastGravity:
346 dock->area.x -= dock->area.width;
347 break;
348 }
349 switch(gravity) {
350 case WestGravity:
351 case CenterGravity:
352 case EastGravity:
353 dock->area.y -= dock->area.height / 2;
354 break;
355 case SouthWestGravity:
356 case SouthGravity:
357 case SouthEastGravity:
358 dock->area.y -= dock->area.height;
359 break;
360 }
361
362 if (config_dock_hide && dock->hidden) {
363 if (!config_dock_floating) {
364 switch (config_dock_pos) {
365 case OB_DIRECTION_NORTHWEST:
366 switch (config_dock_orient) {
367 case OB_ORIENTATION_HORZ:
368 dock->area.y -= dock->area.height - hidesize;
369 break;
370 case OB_ORIENTATION_VERT:
371 dock->area.x -= dock->area.width - hidesize;
372 break;
373 }
374 break;
375 case OB_DIRECTION_NORTH:
376 dock->area.y -= dock->area.height - hidesize;
377 break;
378 case OB_DIRECTION_NORTHEAST:
379 switch (config_dock_orient) {
380 case OB_ORIENTATION_HORZ:
381 dock->area.y -= dock->area.height - hidesize;
382 break;
383 case OB_ORIENTATION_VERT:
384 dock->area.x += dock->area.width - hidesize;
385 break;
386 }
387 break;
388 case OB_DIRECTION_WEST:
389 dock->area.x -= dock->area.width - hidesize;
390 break;
391 case OB_DIRECTION_EAST:
392 dock->area.x += dock->area.width - hidesize;
393 break;
394 case OB_DIRECTION_SOUTHWEST:
395 switch (config_dock_orient) {
396 case OB_ORIENTATION_HORZ:
397 dock->area.y += dock->area.height - hidesize;
398 break;
399 case OB_ORIENTATION_VERT:
400 dock->area.x -= dock->area.width - hidesize;
401 break;
402 } break;
403 case OB_DIRECTION_SOUTH:
404 dock->area.y += dock->area.height - hidesize;
405 break;
406 case OB_DIRECTION_SOUTHEAST:
407 switch (config_dock_orient) {
408 case OB_ORIENTATION_HORZ:
409 dock->area.y += dock->area.height - hidesize;
410 break;
411 case OB_ORIENTATION_VERT:
412 dock->area.x += dock->area.width - hidesize;
413 break;
414 }
415 break;
416 }
417 }
418 }
419
420 if (!config_dock_floating && config_dock_hide) {
421 strw = hidesize;
422 strh = hidesize;
423 } else {
424 strw = dock->area.width;
425 strh = dock->area.height;
426 }
427
428 /* set the strut */
429 if (!dock->dock_apps) {
430 STRUT_PARTIAL_SET(dock_strut, 0, 0, 0, 0,
431 0, 0, 0, 0, 0, 0, 0, 0);
432 }
433 else if (config_dock_floating || config_dock_nostrut) {
434 STRUT_PARTIAL_SET(dock_strut, 0, 0, 0, 0,
435 0, 0, 0, 0, 0, 0, 0, 0);
436 }
437 else {
438 switch (config_dock_pos) {
439 case OB_DIRECTION_NORTHWEST:
440 switch (config_dock_orient) {
441 case OB_ORIENTATION_HORZ:
442 STRUT_PARTIAL_SET(dock_strut, 0, strh, 0, 0,
443 0, 0, dock->area.x, dock->area.x
444 + dock->area.width - 1, 0, 0, 0, 0);
445 break;
446 case OB_ORIENTATION_VERT:
447 STRUT_PARTIAL_SET(dock_strut, strw, 0, 0, 0,
448 dock->area.y, dock->area.y
449 + dock->area.height - 1, 0, 0, 0, 0, 0, 0);
450 break;
451 }
452 break;
453 case OB_DIRECTION_NORTH:
454 STRUT_PARTIAL_SET(dock_strut, 0, strh, 0, 0,
455 0, 0, dock->area.x, dock->area.x
456 + dock->area.width - 1, 0, 0, 0, 0);
457 break;
458 case OB_DIRECTION_NORTHEAST:
459 switch (config_dock_orient) {
460 case OB_ORIENTATION_HORZ:
461 STRUT_PARTIAL_SET(dock_strut, 0, strh, 0, 0,
462 0, 0, dock->area.x, dock->area.x
463 + dock->area.width -1, 0, 0, 0, 0);
464 break;
465 case OB_ORIENTATION_VERT:
466 STRUT_PARTIAL_SET(dock_strut, 0, 0, strw, 0,
467 0, 0, 0, 0, dock->area.y, dock->area.y
468 + dock->area.height - 1, 0, 0);
469 break;
470 }
471 break;
472 case OB_DIRECTION_WEST:
473 STRUT_PARTIAL_SET(dock_strut, strw, 0, 0, 0,
474 dock->area.y, dock->area.y
475 + dock->area.height - 1, 0, 0, 0, 0, 0, 0);
476 break;
477 case OB_DIRECTION_EAST:
478 STRUT_PARTIAL_SET(dock_strut, 0, 0, strw, 0,
479 0, 0, 0, 0, dock->area.y, dock->area.y
480 + dock->area.height - 1, 0, 0);
481 break;
482 case OB_DIRECTION_SOUTHWEST:
483 switch (config_dock_orient) {
484 case OB_ORIENTATION_HORZ:
485 STRUT_PARTIAL_SET(dock_strut, 0, 0, 0, strh,
486 0, 0, 0, 0, 0, 0, dock->area.x, dock->area.x
487 + dock->area.width - 1);
488 break;
489 case OB_ORIENTATION_VERT:
490 STRUT_PARTIAL_SET(dock_strut, strw, 0, 0, 0,
491 dock->area.y, dock->area.y
492 + dock->area.height - 1, 0, 0, 0, 0, 0, 0);
493 break;
494 }
495 break;
496 case OB_DIRECTION_SOUTH:
497 STRUT_PARTIAL_SET(dock_strut, 0, 0, 0, strh,
498 0, 0, 0, 0, 0, 0, dock->area.x, dock->area.x
499 + dock->area.width - 1);
500 break;
501 case OB_DIRECTION_SOUTHEAST:
502 switch (config_dock_orient) {
503 case OB_ORIENTATION_HORZ:
504 STRUT_PARTIAL_SET(dock_strut, 0, 0, 0, strh,
505 0, 0, 0, 0, 0, 0, dock->area.x,
506 dock->area.x + dock->area.width - 1);
507 break;
508 case OB_ORIENTATION_VERT:
509 STRUT_PARTIAL_SET(dock_strut, 0, 0, strw, 0,
510 0, 0, 0, 0, dock->area.y, dock->area.y
511 + dock->area.height - 1, 0, 0);
512 break;
513 }
514 break;
515 }
516 }
517
518 /* not used for actually sizing shit */
519 dock->area.width -= ob_rr_theme->obwidth * 2;
520 dock->area.height -= ob_rr_theme->obwidth * 2;
521
522 if (dock->dock_apps) {
523 g_assert(dock->area.width > 0);
524 g_assert(dock->area.height > 0);
525
526 XMoveResizeWindow(ob_display, dock->frame, dock->area.x, dock->area.y,
527 dock->area.width, dock->area.height);
528
529 RrPaint(dock->a_frame, dock->frame, dock->area.width,
530 dock->area.height);
531 XMapWindow(ob_display, dock->frame);
532 } else
533 XUnmapWindow(ob_display, dock->frame);
534
535 /* but they are useful outside of this function! but don't add it if the
536 dock is actually not visible */
537 if (dock->dock_apps) {
538 dock->area.width += ob_rr_theme->obwidth * 2;
539 dock->area.height += ob_rr_theme->obwidth * 2;
540 }
541
542 screen_update_areas();
543
544 g_free(a);
545 }
546
547 void dock_app_configure(ObDockApp *app, gint w, gint h)
548 {
549 app->w = w;
550 app->h = h;
551 dock_configure();
552 }
553
554 void dock_app_drag(ObDockApp *app, XMotionEvent *e)
555 {
556 ObDockApp *over = NULL;
557 GList *it;
558 gint x, y;
559 gboolean after;
560 gboolean stop;
561
562 x = e->x_root;
563 y = e->y_root;
564
565 /* are we on top of the dock? */
566 if (!(x >= dock->area.x &&
567 y >= dock->area.y &&
568 x < dock->area.x + dock->area.width &&
569 y < dock->area.y + dock->area.height))
570 return;
571
572 x -= dock->area.x;
573 y -= dock->area.y;
574
575 /* which dock app are we on top of? */
576 stop = FALSE;
577 for (it = dock->dock_apps; it; it = g_list_next(it)) {
578 over = it->data;
579 switch (config_dock_orient) {
580 case OB_ORIENTATION_HORZ:
581 if (x >= over->x && x < over->x + over->w)
582 stop = TRUE;
583 break;
584 case OB_ORIENTATION_VERT:
585 if (y >= over->y && y < over->y + over->h)
586 stop = TRUE;
587 break;
588 }
589 /* dont go to it->next! */
590 if (stop) break;
591 }
592 if (!it || app == over) return;
593
594 x -= over->x;
595 y -= over->y;
596
597 switch (config_dock_orient) {
598 case OB_ORIENTATION_HORZ:
599 after = (x > over->w / 2);
600 break;
601 case OB_ORIENTATION_VERT:
602 after = (y > over->h / 2);
603 break;
604 default:
605 g_assert_not_reached();
606 }
607
608 /* remove before doing the it->next! */
609 dock->dock_apps = g_list_remove(dock->dock_apps, app);
610
611 if (after) it = it->next;
612
613 dock->dock_apps = g_list_insert_before(dock->dock_apps, it, app);
614 dock_configure();
615 }
616
617 static gboolean hide_timeout(gpointer data)
618 {
619 /* hide */
620 dock->hidden = TRUE;
621 dock_configure();
622
623 return FALSE; /* don't repeat */
624 }
625
626 static gboolean show_timeout(gpointer data)
627 {
628 /* hide */
629 dock->hidden = FALSE;
630 dock_configure();
631
632 return FALSE; /* don't repeat */
633 }
634
635 void dock_hide(gboolean hide)
636 {
637 if (!hide) {
638 if (dock->hidden && config_dock_hide) {
639 ob_main_loop_timeout_add(ob_main_loop,
640 config_dock_show_delay * 1000,
641 show_timeout, NULL, g_direct_equal, NULL);
642 } else if (!dock->hidden && config_dock_hide) {
643 ob_main_loop_timeout_remove(ob_main_loop, hide_timeout);
644 }
645 } else {
646 if (!dock->hidden && config_dock_hide) {
647 ob_main_loop_timeout_add(ob_main_loop,
648 config_dock_hide_delay * 1000,
649 hide_timeout, NULL, g_direct_equal, NULL);
650 } else if (dock->hidden && config_dock_hide) {
651 ob_main_loop_timeout_remove(ob_main_loop, show_timeout);
652 }
653 }
654 }
655
656 void dock_get_area(Rect *a)
657 {
658 RECT_SET(*a, dock->area.x, dock->area.y,
659 dock->area.width, dock->area.height);
660 }
This page took 0.067539 seconds and 4 git commands to generate.