]> Dogcows Code - chaz/openbox/blob - openbox/dock.c
Move the main loop out into the libobt
[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 "prop.h"
24 #include "config.h"
25 #include "grab.h"
26 #include "openbox.h"
27 #include "render/theme.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(ob_display, dock->frame,
61 RrColorPixel(ob_rr_theme->osd_border_color));
62 XSetWindowBorderWidth(ob_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 = Window_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(ob_display, RootWindow(ob_display, 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(ob_display, dock->frame,
97 RrColorPixel(ob_rr_theme->osd_border_color));
98 XSetWindowBorderWidth(ob_display, dock->frame, ob_rr_theme->obwidth);
99
100 /* Setting the window type so xcompmgr can tell what it is */
101 PROP_SET32(dock->frame, net_wm_window_type, atom,
102 prop_atoms.net_wm_window_type_dock);
103
104 g_hash_table_insert(window_map, &dock->frame, 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(ob_display, dock->frame);
121 RrAppearanceFree(dock->a_frame);
122 g_hash_table_remove(window_map, &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->obwin.type = Window_DockApp;
134 app->win = win;
135 app->icon_win = (wmhints->flags & IconWindowHint) ?
136 wmhints->icon_window : win;
137
138 if (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(ob_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(ob_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(ob_display, app->win, -1000, -1000);
174 XMapWindow(ob_display, app->win);
175 }
176 XMapWindow(ob_display, app->icon_win);
177 XSync(ob_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(ob_display, app->icon_win, SetModeInsert);
182 XSelectInput(ob_display, app->icon_win, DOCKAPP_EVENT_MASK);
183
184 dock_app_grab_button(app, TRUE);
185
186 g_hash_table_insert(window_map, &app->icon_win, app);
187
188 ob_debug("Managed Dock App: 0x%lx (%s)\n", app->icon_win, app->class);
189 }
190
191 void dock_remove_all(void)
192 {
193 while (dock->dock_apps)
194 dock_remove(dock->dock_apps->data, TRUE);
195 }
196
197 void dock_remove(ObDockApp *app, gboolean reparent)
198 {
199 dock_app_grab_button(app, FALSE);
200 XSelectInput(ob_display, app->icon_win, NoEventMask);
201 /* remove the window from our save set */
202 XChangeSaveSet(ob_display, app->icon_win, SetModeDelete);
203 XSync(ob_display, False);
204
205 g_hash_table_remove(window_map, &app->icon_win);
206
207 if (reparent)
208 XReparentWindow(ob_display, app->icon_win,
209 RootWindow(ob_display, ob_screen), app->x, app->y);
210
211 dock->dock_apps = g_list_remove(dock->dock_apps, app);
212 dock_configure();
213
214 ob_debug("Unmanaged Dock App: 0x%lx (%s)\n", app->icon_win, app->class);
215
216 g_free(app->name);
217 g_free(app->class);
218 g_free(app);
219 }
220
221 void dock_configure(void)
222 {
223 GList *it;
224 gint hspot, vspot;
225 gint gravity;
226 gint l, r, t, b;
227 gint strw, strh;
228 Rect *a;
229 gint hidesize;
230
231 RrMargins(dock->a_frame, &l, &t, &r, &b);
232 hidesize = MAX(1, ob_rr_theme->obwidth);
233
234 dock->area.width = dock->area.height = 0;
235
236 /* get the size */
237 for (it = dock->dock_apps; it; it = g_list_next(it)) {
238 ObDockApp *app = it->data;
239 switch (config_dock_orient) {
240 case OB_ORIENTATION_HORZ:
241 dock->area.width += app->w;
242 dock->area.height = MAX(dock->area.height, app->h);
243 break;
244 case OB_ORIENTATION_VERT:
245 dock->area.width = MAX(dock->area.width, app->w);
246 dock->area.height += app->h;
247 break;
248 }
249 }
250
251 dock->area.width += l + r;
252 dock->area.height += t + b;
253
254 hspot = l;
255 vspot = t;
256
257 /* position the apps */
258 for (it = dock->dock_apps; it; it = g_list_next(it)) {
259 ObDockApp *app = it->data;
260 switch (config_dock_orient) {
261 case OB_ORIENTATION_HORZ:
262 app->x = hspot;
263 app->y = (dock->area.height - app->h) / 2;
264 hspot += app->w;
265 break;
266 case OB_ORIENTATION_VERT:
267 app->x = (dock->area.width - app->w) / 2;
268 app->y = vspot;
269 vspot += app->h;
270 break;
271 }
272
273 XMoveWindow(ob_display, app->icon_win, app->x, app->y);
274 }
275
276 /* used for calculating offsets */
277 dock->area.width += ob_rr_theme->obwidth * 2;
278 dock->area.height += ob_rr_theme->obwidth * 2;
279
280 a = screen_physical_area_all_monitors();
281
282 /* calculate position */
283 if (config_dock_floating) {
284 dock->area.x = config_dock_x;
285 dock->area.y = config_dock_y;
286 gravity = NorthWestGravity;
287 } else {
288 switch (config_dock_pos) {
289 case OB_DIRECTION_NORTHWEST:
290 dock->area.x = 0;
291 dock->area.y = 0;
292 gravity = NorthWestGravity;
293 break;
294 case OB_DIRECTION_NORTH:
295 dock->area.x = a->width / 2;
296 dock->area.y = 0;
297 gravity = NorthGravity;
298 break;
299 case OB_DIRECTION_NORTHEAST:
300 dock->area.x = a->width;
301 dock->area.y = 0;
302 gravity = NorthEastGravity;
303 break;
304 case OB_DIRECTION_WEST:
305 dock->area.x = 0;
306 dock->area.y = a->height / 2;
307 gravity = WestGravity;
308 break;
309 case OB_DIRECTION_EAST:
310 dock->area.x = a->width;
311 dock->area.y = a->height / 2;
312 gravity = EastGravity;
313 break;
314 case OB_DIRECTION_SOUTHWEST:
315 dock->area.x = 0;
316 dock->area.y = a->height;
317 gravity = SouthWestGravity;
318 break;
319 case OB_DIRECTION_SOUTH:
320 dock->area.x = a->width / 2;
321 dock->area.y = a->height;
322 gravity = SouthGravity;
323 break;
324 case OB_DIRECTION_SOUTHEAST:
325 dock->area.x = a->width;
326 dock->area.y = a->height;
327 gravity = SouthEastGravity;
328 break;
329 default:
330 g_assert_not_reached();
331 }
332 }
333
334 switch(gravity) {
335 case NorthGravity:
336 case CenterGravity:
337 case SouthGravity:
338 dock->area.x -= dock->area.width / 2;
339 break;
340 case NorthEastGravity:
341 case EastGravity:
342 case SouthEastGravity:
343 dock->area.x -= dock->area.width;
344 break;
345 }
346 switch(gravity) {
347 case WestGravity:
348 case CenterGravity:
349 case EastGravity:
350 dock->area.y -= dock->area.height / 2;
351 break;
352 case SouthWestGravity:
353 case SouthGravity:
354 case SouthEastGravity:
355 dock->area.y -= dock->area.height;
356 break;
357 }
358
359 if (config_dock_hide && dock->hidden) {
360 if (!config_dock_floating) {
361 switch (config_dock_pos) {
362 case OB_DIRECTION_NORTHWEST:
363 switch (config_dock_orient) {
364 case OB_ORIENTATION_HORZ:
365 dock->area.y -= dock->area.height - hidesize;
366 break;
367 case OB_ORIENTATION_VERT:
368 dock->area.x -= dock->area.width - hidesize;
369 break;
370 }
371 break;
372 case OB_DIRECTION_NORTH:
373 dock->area.y -= dock->area.height - hidesize;
374 break;
375 case OB_DIRECTION_NORTHEAST:
376 switch (config_dock_orient) {
377 case OB_ORIENTATION_HORZ:
378 dock->area.y -= dock->area.height - hidesize;
379 break;
380 case OB_ORIENTATION_VERT:
381 dock->area.x += dock->area.width - hidesize;
382 break;
383 }
384 break;
385 case OB_DIRECTION_WEST:
386 dock->area.x -= dock->area.width - hidesize;
387 break;
388 case OB_DIRECTION_EAST:
389 dock->area.x += dock->area.width - hidesize;
390 break;
391 case OB_DIRECTION_SOUTHWEST:
392 switch (config_dock_orient) {
393 case OB_ORIENTATION_HORZ:
394 dock->area.y += dock->area.height - hidesize;
395 break;
396 case OB_ORIENTATION_VERT:
397 dock->area.x -= dock->area.width - hidesize;
398 break;
399 } break;
400 case OB_DIRECTION_SOUTH:
401 dock->area.y += dock->area.height - hidesize;
402 break;
403 case OB_DIRECTION_SOUTHEAST:
404 switch (config_dock_orient) {
405 case OB_ORIENTATION_HORZ:
406 dock->area.y += dock->area.height - hidesize;
407 break;
408 case OB_ORIENTATION_VERT:
409 dock->area.x += dock->area.width - hidesize;
410 break;
411 }
412 break;
413 }
414 }
415 }
416
417 if (!config_dock_floating && config_dock_hide) {
418 strw = hidesize;
419 strh = hidesize;
420 } else {
421 strw = dock->area.width;
422 strh = dock->area.height;
423 }
424
425 /* set the strut */
426 if (!dock->dock_apps) {
427 STRUT_PARTIAL_SET(dock_strut, 0, 0, 0, 0,
428 0, 0, 0, 0, 0, 0, 0, 0);
429 } else if (config_dock_floating || config_dock_nostrut)
430 {
431 STRUT_PARTIAL_SET(dock_strut, 0, 0, 0, 0,
432 0, 0, 0, 0, 0, 0, 0, 0);
433 } else {
434 switch (config_dock_pos) {
435 case OB_DIRECTION_NORTHWEST:
436 switch (config_dock_orient) {
437 case OB_ORIENTATION_HORZ:
438 STRUT_PARTIAL_SET(dock_strut, 0, strh, 0, 0,
439 0, 0, dock->area.x, dock->area.x
440 + dock->area.width - 1, 0, 0, 0, 0);
441 break;
442 case OB_ORIENTATION_VERT:
443 STRUT_PARTIAL_SET(dock_strut, strw, 0, 0, 0,
444 dock->area.y, dock->area.y
445 + dock->area.height - 1, 0, 0, 0, 0, 0, 0);
446 break;
447 }
448 break;
449 case OB_DIRECTION_NORTH:
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_DIRECTION_NORTHEAST:
455 switch (config_dock_orient) {
456 case OB_ORIENTATION_HORZ:
457 STRUT_PARTIAL_SET(dock_strut, 0, strh, 0, 0,
458 0, 0, dock->area.x, dock->area.x
459 + dock->area.width -1, 0, 0, 0, 0);
460 break;
461 case OB_ORIENTATION_VERT:
462 STRUT_PARTIAL_SET(dock_strut, 0, 0, strw, 0,
463 0, 0, 0, 0, dock->area.y, dock->area.y
464 + dock->area.height - 1, 0, 0);
465 break;
466 }
467 break;
468 case OB_DIRECTION_WEST:
469 STRUT_PARTIAL_SET(dock_strut, strw, 0, 0, 0,
470 dock->area.y, dock->area.y
471 + dock->area.height - 1, 0, 0, 0, 0, 0, 0);
472 break;
473 case OB_DIRECTION_EAST:
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 case OB_DIRECTION_SOUTHWEST:
479 switch (config_dock_orient) {
480 case OB_ORIENTATION_HORZ:
481 STRUT_PARTIAL_SET(dock_strut, 0, 0, 0, strh,
482 0, 0, 0, 0, 0, 0, dock->area.x, dock->area.x
483 + dock->area.width - 1);
484 break;
485 case OB_ORIENTATION_VERT:
486 STRUT_PARTIAL_SET(dock_strut, strw, 0, 0, 0,
487 dock->area.y, dock->area.y
488 + dock->area.height - 1, 0, 0, 0, 0, 0, 0);
489 break;
490 }
491 break;
492 case OB_DIRECTION_SOUTH:
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_DIRECTION_SOUTHEAST:
498 switch (config_dock_orient) {
499 case OB_ORIENTATION_HORZ:
500 STRUT_PARTIAL_SET(dock_strut, 0, 0, 0, strh,
501 0, 0, 0, 0, 0, 0, dock->area.x,
502 dock->area.x + dock->area.width - 1);
503 break;
504 case OB_ORIENTATION_VERT:
505 STRUT_PARTIAL_SET(dock_strut, 0, 0, strw, 0,
506 0, 0, 0, 0, dock->area.y, dock->area.y
507 + dock->area.height - 1, 0, 0);
508 break;
509 }
510 break;
511 }
512 }
513
514 /* not used for actually sizing shit */
515 dock->area.width -= ob_rr_theme->obwidth * 2;
516 dock->area.height -= ob_rr_theme->obwidth * 2;
517
518 if (dock->dock_apps) {
519 g_assert(dock->area.width > 0);
520 g_assert(dock->area.height > 0);
521
522 XMoveResizeWindow(ob_display, dock->frame, dock->area.x, dock->area.y,
523 dock->area.width, dock->area.height);
524
525 RrPaint(dock->a_frame, dock->frame, dock->area.width,
526 dock->area.height);
527 XMapWindow(ob_display, dock->frame);
528 } else
529 XUnmapWindow(ob_display, dock->frame);
530
531 /* but they are useful outside of this function! */
532 dock->area.width += ob_rr_theme->obwidth * 2;
533 dock->area.height += ob_rr_theme->obwidth * 2;
534
535 screen_update_areas();
536
537 g_free(a);
538 }
539
540 void dock_app_configure(ObDockApp *app, gint w, gint h)
541 {
542 app->w = w;
543 app->h = h;
544 dock_configure();
545 }
546
547 void dock_app_drag(ObDockApp *app, XMotionEvent *e)
548 {
549 ObDockApp *over = NULL;
550 GList *it;
551 gint x, y;
552 gboolean after;
553 gboolean stop;
554
555 x = e->x_root;
556 y = e->y_root;
557
558 /* are we on top of the dock? */
559 if (!(x >= dock->area.x &&
560 y >= dock->area.y &&
561 x < dock->area.x + dock->area.width &&
562 y < dock->area.y + dock->area.height))
563 return;
564
565 x -= dock->area.x;
566 y -= dock->area.y;
567
568 /* which dock app are we on top of? */
569 stop = FALSE;
570 for (it = dock->dock_apps; it; it = g_list_next(it)) {
571 over = it->data;
572 switch (config_dock_orient) {
573 case OB_ORIENTATION_HORZ:
574 if (x >= over->x && x < over->x + over->w)
575 stop = TRUE;
576 break;
577 case OB_ORIENTATION_VERT:
578 if (y >= over->y && y < over->y + over->h)
579 stop = TRUE;
580 break;
581 }
582 /* dont go to it->next! */
583 if (stop) break;
584 }
585 if (!it || app == over) return;
586
587 x -= over->x;
588 y -= over->y;
589
590 switch (config_dock_orient) {
591 case OB_ORIENTATION_HORZ:
592 after = (x > over->w / 2);
593 break;
594 case OB_ORIENTATION_VERT:
595 after = (y > over->h / 2);
596 break;
597 default:
598 g_assert_not_reached();
599 }
600
601 /* remove before doing the it->next! */
602 dock->dock_apps = g_list_remove(dock->dock_apps, app);
603
604 if (after) it = it->next;
605
606 dock->dock_apps = g_list_insert_before(dock->dock_apps, it, app);
607 dock_configure();
608 }
609
610 static gboolean hide_timeout(gpointer data)
611 {
612 /* hide */
613 dock->hidden = TRUE;
614 dock_configure();
615
616 return FALSE; /* don't repeat */
617 }
618
619 static gboolean show_timeout(gpointer data)
620 {
621 /* hide */
622 dock->hidden = FALSE;
623 dock_configure();
624
625 return FALSE; /* don't repeat */
626 }
627
628 void dock_hide(gboolean hide)
629 {
630 if (!hide) {
631 if (dock->hidden && config_dock_hide) {
632 obt_main_loop_timeout_add(ob_main_loop,
633 config_dock_show_delay * 1000,
634 show_timeout, NULL,
635 g_direct_equal, NULL);
636 } else if (!dock->hidden && config_dock_hide) {
637 obt_main_loop_timeout_remove(ob_main_loop, hide_timeout);
638 }
639 } else {
640 if (!dock->hidden && config_dock_hide) {
641 obt_main_loop_timeout_add(ob_main_loop,
642 config_dock_hide_delay * 1000,
643 hide_timeout, NULL,
644 g_direct_equal, NULL);
645 } else if (dock->hidden && config_dock_hide) {
646 obt_main_loop_timeout_remove(ob_main_loop, show_timeout);
647 }
648 }
649 }
650
651 void dock_get_area(Rect *a)
652 {
653 RECT_SET(*a, dock->area.x, dock->area.y,
654 dock->area.width, dock->area.height);
655 }
This page took 0.068024 seconds and 5 git commands to generate.