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