]> Dogcows Code - chaz/openbox/blob - openbox/dock.c
dba74f684923d53ffb28ea04844f7bb18c6bcebc
[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 = 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(obt_display,
89 RootWindow(obt_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(obt_display, dock->frame,
98 RrColorPixel(ob_rr_theme->osd_border_color));
99 XSetWindowBorderWidth(obt_display, dock->frame, ob_rr_theme->obwidth);
100
101 /* Setting the window type so xcompmgr can tell what it is */
102 OBT_PROP_SET32(dock->frame, NET_WM_WINDOW_TYPE, ATOM,
103 OBT_PROP_ATOM(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(obt_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 (OBT_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(obt_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(obt_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(obt_display, app->win, -1000, -1000);
175 XMapWindow(obt_display, app->win);
176 }
177 XMapWindow(obt_display, app->icon_win);
178 XSync(obt_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(obt_display, app->icon_win, SetModeInsert);
183 XSelectInput(obt_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(obt_display, app->icon_win, NoEventMask);
202 /* remove the window from our save set */
203 XChangeSaveSet(obt_display, app->icon_win, SetModeDelete);
204 XSync(obt_display, False);
205
206 g_hash_table_remove(window_map, &app->icon_win);
207
208 if (reparent)
209 XReparentWindow(obt_display, app->icon_win,
210 RootWindow(obt_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 dock->area.width += l + r;
253 dock->area.height += t + b;
254
255 hspot = l;
256 vspot = t;
257
258 /* position the apps */
259 for (it = dock->dock_apps; it; it = g_list_next(it)) {
260 ObDockApp *app = it->data;
261 switch (config_dock_orient) {
262 case OB_ORIENTATION_HORZ:
263 app->x = hspot;
264 app->y = (dock->area.height - app->h) / 2;
265 hspot += app->w;
266 break;
267 case OB_ORIENTATION_VERT:
268 app->x = (dock->area.width - app->w) / 2;
269 app->y = vspot;
270 vspot += app->h;
271 break;
272 }
273
274 XMoveWindow(obt_display, app->icon_win, app->x, app->y);
275 }
276
277 /* used for calculating offsets */
278 dock->area.width += ob_rr_theme->obwidth * 2;
279 dock->area.height += ob_rr_theme->obwidth * 2;
280
281 a = screen_physical_area_all_monitors();
282
283 /* calculate position */
284 if (config_dock_floating) {
285 dock->area.x = config_dock_x;
286 dock->area.y = config_dock_y;
287 gravity = NorthWestGravity;
288 } else {
289 switch (config_dock_pos) {
290 case OB_DIRECTION_NORTHWEST:
291 dock->area.x = 0;
292 dock->area.y = 0;
293 gravity = NorthWestGravity;
294 break;
295 case OB_DIRECTION_NORTH:
296 dock->area.x = a->width / 2;
297 dock->area.y = 0;
298 gravity = NorthGravity;
299 break;
300 case OB_DIRECTION_NORTHEAST:
301 dock->area.x = a->width;
302 dock->area.y = 0;
303 gravity = NorthEastGravity;
304 break;
305 case OB_DIRECTION_WEST:
306 dock->area.x = 0;
307 dock->area.y = a->height / 2;
308 gravity = WestGravity;
309 break;
310 case OB_DIRECTION_EAST:
311 dock->area.x = a->width;
312 dock->area.y = a->height / 2;
313 gravity = EastGravity;
314 break;
315 case OB_DIRECTION_SOUTHWEST:
316 dock->area.x = 0;
317 dock->area.y = a->height;
318 gravity = SouthWestGravity;
319 break;
320 case OB_DIRECTION_SOUTH:
321 dock->area.x = a->width / 2;
322 dock->area.y = a->height;
323 gravity = SouthGravity;
324 break;
325 case OB_DIRECTION_SOUTHEAST:
326 dock->area.x = a->width;
327 dock->area.y = a->height;
328 gravity = SouthEastGravity;
329 break;
330 default:
331 g_assert_not_reached();
332 }
333 }
334
335 switch(gravity) {
336 case NorthGravity:
337 case CenterGravity:
338 case SouthGravity:
339 dock->area.x -= dock->area.width / 2;
340 break;
341 case NorthEastGravity:
342 case EastGravity:
343 case SouthEastGravity:
344 dock->area.x -= dock->area.width;
345 break;
346 }
347 switch(gravity) {
348 case WestGravity:
349 case CenterGravity:
350 case EastGravity:
351 dock->area.y -= dock->area.height / 2;
352 break;
353 case SouthWestGravity:
354 case SouthGravity:
355 case SouthEastGravity:
356 dock->area.y -= dock->area.height;
357 break;
358 }
359
360 if (config_dock_hide && dock->hidden) {
361 if (!config_dock_floating) {
362 switch (config_dock_pos) {
363 case OB_DIRECTION_NORTHWEST:
364 switch (config_dock_orient) {
365 case OB_ORIENTATION_HORZ:
366 dock->area.y -= dock->area.height - hidesize;
367 break;
368 case OB_ORIENTATION_VERT:
369 dock->area.x -= dock->area.width - hidesize;
370 break;
371 }
372 break;
373 case OB_DIRECTION_NORTH:
374 dock->area.y -= dock->area.height - hidesize;
375 break;
376 case OB_DIRECTION_NORTHEAST:
377 switch (config_dock_orient) {
378 case OB_ORIENTATION_HORZ:
379 dock->area.y -= dock->area.height - hidesize;
380 break;
381 case OB_ORIENTATION_VERT:
382 dock->area.x += dock->area.width - hidesize;
383 break;
384 }
385 break;
386 case OB_DIRECTION_WEST:
387 dock->area.x -= dock->area.width - hidesize;
388 break;
389 case OB_DIRECTION_EAST:
390 dock->area.x += dock->area.width - hidesize;
391 break;
392 case OB_DIRECTION_SOUTHWEST:
393 switch (config_dock_orient) {
394 case OB_ORIENTATION_HORZ:
395 dock->area.y += dock->area.height - hidesize;
396 break;
397 case OB_ORIENTATION_VERT:
398 dock->area.x -= dock->area.width - hidesize;
399 break;
400 } break;
401 case OB_DIRECTION_SOUTH:
402 dock->area.y += dock->area.height - hidesize;
403 break;
404 case OB_DIRECTION_SOUTHEAST:
405 switch (config_dock_orient) {
406 case OB_ORIENTATION_HORZ:
407 dock->area.y += dock->area.height - hidesize;
408 break;
409 case OB_ORIENTATION_VERT:
410 dock->area.x += dock->area.width - hidesize;
411 break;
412 }
413 break;
414 }
415 }
416 }
417
418 if (!config_dock_floating && config_dock_hide) {
419 strw = hidesize;
420 strh = hidesize;
421 } else {
422 strw = dock->area.width;
423 strh = dock->area.height;
424 }
425
426 /* set the strut */
427 if (!dock->dock_apps) {
428 STRUT_PARTIAL_SET(dock_strut, 0, 0, 0, 0,
429 0, 0, 0, 0, 0, 0, 0, 0);
430 } else if (config_dock_floating || config_dock_nostrut)
431 {
432 STRUT_PARTIAL_SET(dock_strut, 0, 0, 0, 0,
433 0, 0, 0, 0, 0, 0, 0, 0);
434 } else {
435 switch (config_dock_pos) {
436 case OB_DIRECTION_NORTHWEST:
437 switch (config_dock_orient) {
438 case OB_ORIENTATION_HORZ:
439 STRUT_PARTIAL_SET(dock_strut, 0, strh, 0, 0,
440 0, 0, dock->area.x, dock->area.x
441 + dock->area.width - 1, 0, 0, 0, 0);
442 break;
443 case OB_ORIENTATION_VERT:
444 STRUT_PARTIAL_SET(dock_strut, strw, 0, 0, 0,
445 dock->area.y, dock->area.y
446 + dock->area.height - 1, 0, 0, 0, 0, 0, 0);
447 break;
448 }
449 break;
450 case OB_DIRECTION_NORTH:
451 STRUT_PARTIAL_SET(dock_strut, 0, strh, 0, 0,
452 0, 0, dock->area.x, dock->area.x
453 + dock->area.width - 1, 0, 0, 0, 0);
454 break;
455 case OB_DIRECTION_NORTHEAST:
456 switch (config_dock_orient) {
457 case OB_ORIENTATION_HORZ:
458 STRUT_PARTIAL_SET(dock_strut, 0, strh, 0, 0,
459 0, 0, dock->area.x, dock->area.x
460 + dock->area.width -1, 0, 0, 0, 0);
461 break;
462 case OB_ORIENTATION_VERT:
463 STRUT_PARTIAL_SET(dock_strut, 0, 0, strw, 0,
464 0, 0, 0, 0, dock->area.y, dock->area.y
465 + dock->area.height - 1, 0, 0);
466 break;
467 }
468 break;
469 case OB_DIRECTION_WEST:
470 STRUT_PARTIAL_SET(dock_strut, strw, 0, 0, 0,
471 dock->area.y, dock->area.y
472 + dock->area.height - 1, 0, 0, 0, 0, 0, 0);
473 break;
474 case OB_DIRECTION_EAST:
475 STRUT_PARTIAL_SET(dock_strut, 0, 0, strw, 0,
476 0, 0, 0, 0, dock->area.y, dock->area.y
477 + dock->area.height - 1, 0, 0);
478 break;
479 case OB_DIRECTION_SOUTHWEST:
480 switch (config_dock_orient) {
481 case OB_ORIENTATION_HORZ:
482 STRUT_PARTIAL_SET(dock_strut, 0, 0, 0, strh,
483 0, 0, 0, 0, 0, 0, dock->area.x, dock->area.x
484 + dock->area.width - 1);
485 break;
486 case OB_ORIENTATION_VERT:
487 STRUT_PARTIAL_SET(dock_strut, strw, 0, 0, 0,
488 dock->area.y, dock->area.y
489 + dock->area.height - 1, 0, 0, 0, 0, 0, 0);
490 break;
491 }
492 break;
493 case OB_DIRECTION_SOUTH:
494 STRUT_PARTIAL_SET(dock_strut, 0, 0, 0, strh,
495 0, 0, 0, 0, 0, 0, dock->area.x, dock->area.x
496 + dock->area.width - 1);
497 break;
498 case OB_DIRECTION_SOUTHEAST:
499 switch (config_dock_orient) {
500 case OB_ORIENTATION_HORZ:
501 STRUT_PARTIAL_SET(dock_strut, 0, 0, 0, strh,
502 0, 0, 0, 0, 0, 0, dock->area.x,
503 dock->area.x + dock->area.width - 1);
504 break;
505 case OB_ORIENTATION_VERT:
506 STRUT_PARTIAL_SET(dock_strut, 0, 0, strw, 0,
507 0, 0, 0, 0, dock->area.y, dock->area.y
508 + dock->area.height - 1, 0, 0);
509 break;
510 }
511 break;
512 }
513 }
514
515 /* not used for actually sizing shit */
516 dock->area.width -= ob_rr_theme->obwidth * 2;
517 dock->area.height -= ob_rr_theme->obwidth * 2;
518
519 if (dock->dock_apps) {
520 g_assert(dock->area.width > 0);
521 g_assert(dock->area.height > 0);
522
523 XMoveResizeWindow(obt_display, dock->frame, dock->area.x, dock->area.y,
524 dock->area.width, dock->area.height);
525
526 RrPaint(dock->a_frame, dock->frame, dock->area.width,
527 dock->area.height);
528 XMapWindow(obt_display, dock->frame);
529 } else
530 XUnmapWindow(obt_display, dock->frame);
531
532 /* but they are useful outside of this function! */
533 dock->area.width += ob_rr_theme->obwidth * 2;
534 dock->area.height += ob_rr_theme->obwidth * 2;
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 }
This page took 0.060947 seconds and 4 git commands to generate.