1 /* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
3 dock.c for the Openbox window manager
4 Copyright (c) 2006 Mikael Magnusson
5 Copyright (c) 2003-2007 Dana Jansens
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.
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.
17 See the COPYING file for a copy of the GNU General Public License.
26 #include "obrender/theme.h"
29 #define DOCK_EVENT_MASK (ButtonPressMask | ButtonReleaseMask | \
30 EnterWindowMask | LeaveWindowMask)
31 #define DOCKAPP_EVENT_MASK (StructureNotifyMask)
32 #define DOCK_NOPROPAGATEMASK (ButtonPressMask | ButtonReleaseMask | \
36 static guint show_timeout_id;
37 static guint hide_timeout_id;
39 StrutPartial dock_strut;
41 static void dock_app_grab_button(ObDockApp *app, gboolean grab)
44 grab_button_full(config_dock_app_move_button,
45 config_dock_app_move_modifiers, app->icon_win,
46 ButtonPressMask | ButtonReleaseMask |
48 GrabModeAsync, OB_CURSOR_MOVE);
50 ungrab_button(config_dock_app_move_button,
51 config_dock_app_move_modifiers, app->icon_win);
55 static guint window_hash(Window *w) { return *w; }
56 static gboolean window_comp(Window *w1, Window *w2) { return *w1 == *w2; }
58 void dock_startup(gboolean reconfig)
60 XSetWindowAttributes attrib;
61 const Rect r = {0, 0, 1, 1};
67 XSetWindowBorder(obt_display, dock->frame,
68 RrColorPixel(ob_rr_theme->osd_border_color));
69 XSetWindowBorderWidth(obt_display, dock->frame, ob_rr_theme->obwidth);
71 RrAppearanceFree(dock->a_frame);
72 dock->a_frame = RrAppearanceCopy(ob_rr_theme->osd_bg);
74 stacking_add(DOCK_AS_WINDOW(dock));
79 for (it = dock->dock_apps; it; it = g_list_next(it))
80 dock_app_grab_button(it->data, TRUE);
84 STRUT_PARTIAL_SET(dock_strut, 0, 0, 0, 0,
85 0, 0, 0, 0, 0, 0, 0, 0);
87 dock = window_new(OB_WINDOW_CLASS_DOCK, ObDock);
91 dock->dock_map = g_hash_table_new((GHashFunc)window_hash,
92 (GEqualFunc)window_comp);
94 attrib.event_mask = DOCK_EVENT_MASK;
95 attrib.override_redirect = True;
96 attrib.do_not_propagate_mask = DOCK_NOPROPAGATEMASK;
97 dock->depth = RrDepth(ob_rr_inst);
98 dock->frame = XCreateWindow(obt_display, obt_root(ob_screen),
99 r.x, r.y, r.width, r.height, b,
100 dock->depth, InputOutput,
101 RrVisual(ob_rr_inst),
102 CWOverrideRedirect | CWEventMask |
105 dock->a_frame = RrAppearanceCopy(ob_rr_theme->osd_bg);
106 XSetWindowBorder(obt_display, dock->frame,
107 RrColorPixel(ob_rr_theme->osd_border_color));
108 XSetWindowBorderWidth(obt_display, dock->frame, ob_rr_theme->obwidth);
110 /* Setting the window type so xcompmgr can tell what it is */
111 OBT_PROP_SET32(dock->frame, NET_WM_WINDOW_TYPE, ATOM,
112 OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_DOCK));
114 window_set_top_area(DOCK_AS_WINDOW(dock), &r, b);
115 window_set_abstract(DOCK_AS_WINDOW(dock),
116 &dock->frame, /* top level window */
117 &dock->frame, /* composite redir window */
118 &config_dock_layer, /* stacking layer */
119 &dock->depth, /* window depth */
122 window_add(&dock->frame, DOCK_AS_WINDOW(dock));
123 stacking_add(DOCK_AS_WINDOW(dock));
126 void dock_shutdown(gboolean reconfig)
131 stacking_remove(DOCK_AS_WINDOW(dock));
133 for (it = dock->dock_apps; it; it = g_list_next(it))
134 dock_app_grab_button(it->data, FALSE);
138 window_cleanup(DOCK_AS_WINDOW(dock));
140 g_hash_table_destroy(dock->dock_map);
142 XDestroyWindow(obt_display, dock->frame);
143 RrAppearanceFree(dock->a_frame);
144 window_remove(dock->frame);
145 stacking_remove(dock);
146 window_free(DOCK_AS_WINDOW(dock));
150 void dock_manage(Window icon_win, Window name_win)
153 XWindowAttributes attrib;
156 app = g_slice_new0(ObDockApp);
157 app->name_win = name_win;
158 app->icon_win = icon_win;
160 if (OBT_PROP_GETSS(app->name_win, WM_CLASS, locale, &data)) {
162 app->name = g_strdup(data[0]);
164 app->class = g_strdup(data[1]);
169 if (app->name == NULL) app->name = g_strdup("");
170 if (app->class == NULL) app->class = g_strdup("");
172 if (XGetWindowAttributes(obt_display, app->icon_win, &attrib)) {
173 app->w = attrib.width;
174 app->h = attrib.height;
176 app->w = app->h = 64;
179 dock->dock_apps = g_list_append(dock->dock_apps, app);
180 g_hash_table_insert(dock->dock_map, &app->icon_win, app);
183 XReparentWindow(obt_display, app->icon_win, dock->frame, app->x, app->y);
185 This is the same case as in frame.c for client windows. When Openbox is
186 starting, the window is already mapped so we see unmap events occur for
187 it. There are 2 unmap events generated that we see, one with the 'event'
188 member set the root window, and one set to the client, but both get
189 handled and need to be ignored.
191 if (ob_state() == OB_STATE_STARTING)
192 app->ignore_unmaps += 2;
193 XChangeSaveSet(obt_display, app->icon_win, SetModeInsert);
194 XMapWindow(obt_display, app->icon_win);
196 if (app->name_win != app->icon_win) {
197 XReparentWindow(obt_display, app->name_win, dock->frame, -1000, -1000);
198 XChangeSaveSet(obt_display, app->name_win, SetModeInsert);
199 XMapWindow(obt_display, app->name_win);
202 XSync(obt_display, False);
204 XSelectInput(obt_display, app->icon_win, DOCKAPP_EVENT_MASK);
206 dock_app_grab_button(app, TRUE);
208 ob_debug("Managed Dock App: 0x%lx 0x%lx (%s)",
209 app->icon_win, app->name_win, app->class);
214 void dock_unmanage_all(void)
216 while (dock->dock_apps)
217 dock_unmanage(dock->dock_apps->data, TRUE);
220 void dock_unmanage(ObDockApp *app, gboolean reparent)
222 dock_app_grab_button(app, FALSE);
223 XSelectInput(obt_display, app->icon_win, NoEventMask);
224 /* remove the window from our save set */
225 XChangeSaveSet(obt_display, app->icon_win, SetModeDelete);
226 XSync(obt_display, False);
229 XReparentWindow(obt_display, app->icon_win, obt_root(ob_screen), 0, 0);
230 if (app->name_win != app->icon_win)
231 XReparentWindow(obt_display, app->name_win,
232 obt_root(ob_screen), 0, 0);
235 dock->dock_apps = g_list_remove(dock->dock_apps, app);
236 g_hash_table_remove(dock->dock_map, &app->icon_win);
239 ob_debug("Unmanaged Dock App: 0x%lx (%s)", app->icon_win, app->class);
243 g_slice_free(ObDockApp, app);
246 void dock_configure(void)
256 RrMargins(dock->a_frame, &l, &t, &r, &b);
257 hidesize = MAX(1, ob_rr_theme->obwidth);
259 dock->area.width = dock->area.height = 0;
262 for (it = dock->dock_apps; it; it = g_list_next(it)) {
263 ObDockApp *app = it->data;
264 switch (config_dock_orient) {
265 case OB_ORIENTATION_HORZ:
266 dock->area.width += app->w;
267 dock->area.height = MAX(dock->area.height, app->h);
269 case OB_ORIENTATION_VERT:
270 dock->area.width = MAX(dock->area.width, app->w);
271 dock->area.height += app->h;
276 if (dock->dock_apps) {
277 dock->area.width += l + r;
278 dock->area.height += t + b;
284 /* position the apps */
285 for (it = dock->dock_apps; it; it = g_list_next(it)) {
286 ObDockApp *app = it->data;
287 switch (config_dock_orient) {
288 case OB_ORIENTATION_HORZ:
290 app->y = (dock->area.height - app->h) / 2;
293 case OB_ORIENTATION_VERT:
294 app->x = (dock->area.width - app->w) / 2;
300 XMoveWindow(obt_display, app->icon_win, app->x, app->y);
303 /* used for calculating offsets */
304 dock->area.width += ob_rr_theme->obwidth * 2;
305 dock->area.height += ob_rr_theme->obwidth * 2;
307 a = screen_physical_area_all_monitors();
309 /* calculate position */
310 if (config_dock_floating) {
311 dock->area.x = config_dock_x;
312 dock->area.y = config_dock_y;
313 gravity = NorthWestGravity;
315 switch (config_dock_pos) {
316 case OB_DIRECTION_NORTHWEST:
319 gravity = NorthWestGravity;
321 case OB_DIRECTION_NORTH:
322 dock->area.x = a->width / 2;
324 gravity = NorthGravity;
326 case OB_DIRECTION_NORTHEAST:
327 dock->area.x = a->width;
329 gravity = NorthEastGravity;
331 case OB_DIRECTION_WEST:
333 dock->area.y = a->height / 2;
334 gravity = WestGravity;
336 case OB_DIRECTION_EAST:
337 dock->area.x = a->width;
338 dock->area.y = a->height / 2;
339 gravity = EastGravity;
341 case OB_DIRECTION_SOUTHWEST:
343 dock->area.y = a->height;
344 gravity = SouthWestGravity;
346 case OB_DIRECTION_SOUTH:
347 dock->area.x = a->width / 2;
348 dock->area.y = a->height;
349 gravity = SouthGravity;
351 case OB_DIRECTION_SOUTHEAST:
352 dock->area.x = a->width;
353 dock->area.y = a->height;
354 gravity = SouthEastGravity;
357 g_assert_not_reached();
365 dock->area.x -= dock->area.width / 2;
367 case NorthEastGravity:
369 case SouthEastGravity:
370 dock->area.x -= dock->area.width;
377 dock->area.y -= dock->area.height / 2;
379 case SouthWestGravity:
381 case SouthEastGravity:
382 dock->area.y -= dock->area.height;
386 if (config_dock_hide && dock->hidden) {
387 if (!config_dock_floating) {
388 switch (config_dock_pos) {
389 case OB_DIRECTION_NORTHWEST:
390 switch (config_dock_orient) {
391 case OB_ORIENTATION_HORZ:
392 dock->area.y -= dock->area.height - hidesize;
394 case OB_ORIENTATION_VERT:
395 dock->area.x -= dock->area.width - hidesize;
399 case OB_DIRECTION_NORTH:
400 dock->area.y -= dock->area.height - hidesize;
402 case OB_DIRECTION_NORTHEAST:
403 switch (config_dock_orient) {
404 case OB_ORIENTATION_HORZ:
405 dock->area.y -= dock->area.height - hidesize;
407 case OB_ORIENTATION_VERT:
408 dock->area.x += dock->area.width - hidesize;
412 case OB_DIRECTION_WEST:
413 dock->area.x -= dock->area.width - hidesize;
415 case OB_DIRECTION_EAST:
416 dock->area.x += dock->area.width - hidesize;
418 case OB_DIRECTION_SOUTHWEST:
419 switch (config_dock_orient) {
420 case OB_ORIENTATION_HORZ:
421 dock->area.y += dock->area.height - hidesize;
423 case OB_ORIENTATION_VERT:
424 dock->area.x -= dock->area.width - hidesize;
427 case OB_DIRECTION_SOUTH:
428 dock->area.y += dock->area.height - hidesize;
430 case OB_DIRECTION_SOUTHEAST:
431 switch (config_dock_orient) {
432 case OB_ORIENTATION_HORZ:
433 dock->area.y += dock->area.height - hidesize;
435 case OB_ORIENTATION_VERT:
436 dock->area.x += dock->area.width - hidesize;
444 if (!config_dock_floating && config_dock_hide) {
448 strw = dock->area.width;
449 strh = dock->area.height;
453 if (!dock->dock_apps) {
454 STRUT_PARTIAL_SET(dock_strut, 0, 0, 0, 0,
455 0, 0, 0, 0, 0, 0, 0, 0);
457 else if (config_dock_floating || config_dock_nostrut) {
458 STRUT_PARTIAL_SET(dock_strut, 0, 0, 0, 0,
459 0, 0, 0, 0, 0, 0, 0, 0);
462 switch (config_dock_pos) {
463 case OB_DIRECTION_NORTHWEST:
464 switch (config_dock_orient) {
465 case OB_ORIENTATION_HORZ:
466 STRUT_PARTIAL_SET(dock_strut, 0, strh, 0, 0,
467 0, 0, dock->area.x, dock->area.x
468 + dock->area.width - 1, 0, 0, 0, 0);
470 case OB_ORIENTATION_VERT:
471 STRUT_PARTIAL_SET(dock_strut, strw, 0, 0, 0,
472 dock->area.y, dock->area.y
473 + dock->area.height - 1, 0, 0, 0, 0, 0, 0);
477 case OB_DIRECTION_NORTH:
478 STRUT_PARTIAL_SET(dock_strut, 0, strh, 0, 0,
479 0, 0, dock->area.x, dock->area.x
480 + dock->area.width - 1, 0, 0, 0, 0);
482 case OB_DIRECTION_NORTHEAST:
483 switch (config_dock_orient) {
484 case OB_ORIENTATION_HORZ:
485 STRUT_PARTIAL_SET(dock_strut, 0, strh, 0, 0,
486 0, 0, dock->area.x, dock->area.x
487 + dock->area.width -1, 0, 0, 0, 0);
489 case OB_ORIENTATION_VERT:
490 STRUT_PARTIAL_SET(dock_strut, 0, 0, strw, 0,
491 0, 0, 0, 0, dock->area.y, dock->area.y
492 + dock->area.height - 1, 0, 0);
496 case OB_DIRECTION_WEST:
497 STRUT_PARTIAL_SET(dock_strut, strw, 0, 0, 0,
498 dock->area.y, dock->area.y
499 + dock->area.height - 1, 0, 0, 0, 0, 0, 0);
501 case OB_DIRECTION_EAST:
502 STRUT_PARTIAL_SET(dock_strut, 0, 0, strw, 0,
503 0, 0, 0, 0, dock->area.y, dock->area.y
504 + dock->area.height - 1, 0, 0);
506 case OB_DIRECTION_SOUTHWEST:
507 switch (config_dock_orient) {
508 case OB_ORIENTATION_HORZ:
509 STRUT_PARTIAL_SET(dock_strut, 0, 0, 0, strh,
510 0, 0, 0, 0, 0, 0, dock->area.x, dock->area.x
511 + dock->area.width - 1);
513 case OB_ORIENTATION_VERT:
514 STRUT_PARTIAL_SET(dock_strut, strw, 0, 0, 0,
515 dock->area.y, dock->area.y
516 + dock->area.height - 1, 0, 0, 0, 0, 0, 0);
520 case OB_DIRECTION_SOUTH:
521 STRUT_PARTIAL_SET(dock_strut, 0, 0, 0, strh,
522 0, 0, 0, 0, 0, 0, dock->area.x, dock->area.x
523 + dock->area.width - 1);
525 case OB_DIRECTION_SOUTHEAST:
526 switch (config_dock_orient) {
527 case OB_ORIENTATION_HORZ:
528 STRUT_PARTIAL_SET(dock_strut, 0, 0, 0, strh,
529 0, 0, 0, 0, 0, 0, dock->area.x,
530 dock->area.x + dock->area.width - 1);
532 case OB_ORIENTATION_VERT:
533 STRUT_PARTIAL_SET(dock_strut, 0, 0, strw, 0,
534 0, 0, 0, 0, dock->area.y, dock->area.y
535 + dock->area.height - 1, 0, 0);
542 /* not used for actually sizing shit */
543 dock->area.width -= ob_rr_theme->obwidth * 2;
544 dock->area.height -= ob_rr_theme->obwidth * 2;
546 if (dock->dock_apps) {
547 g_assert(dock->area.width > 0);
548 g_assert(dock->area.height > 0);
550 XMoveResizeWindow(obt_display, dock->frame, dock->area.x, dock->area.y,
551 dock->area.width, dock->area.height);
553 RrPaint(dock->a_frame, dock->frame, dock->area.width,
555 XMapWindow(obt_display, dock->frame);
557 XUnmapWindow(obt_display, dock->frame);
559 /* but they are useful outside of this function! but don't add it if the
560 dock is actually not visible */
561 if (dock->dock_apps) {
562 dock->area.width += ob_rr_theme->obwidth * 2;
563 dock->area.height += ob_rr_theme->obwidth * 2;
566 screen_update_areas();
569 void dock_app_configure(ObDockApp *app, gint w, gint h)
576 void dock_app_drag(ObDockApp *app, XMotionEvent *e)
578 ObDockApp *over = NULL;
587 /* are we on top of the dock? */
588 if (!(x >= dock->area.x &&
590 x < dock->area.x + dock->area.width &&
591 y < dock->area.y + dock->area.height))
597 /* which dock app are we on top of? */
599 for (it = dock->dock_apps; it; it = g_list_next(it)) {
601 switch (config_dock_orient) {
602 case OB_ORIENTATION_HORZ:
603 if (x >= over->x && x < over->x + over->w)
606 case OB_ORIENTATION_VERT:
607 if (y >= over->y && y < over->y + over->h)
611 /* dont go to it->next! */
614 if (!it || app == over) return;
619 switch (config_dock_orient) {
620 case OB_ORIENTATION_HORZ:
621 after = (x > over->w / 2);
623 case OB_ORIENTATION_VERT:
624 after = (y > over->h / 2);
627 g_assert_not_reached();
630 /* remove before doing the it->next! */
631 dock->dock_apps = g_list_remove(dock->dock_apps, app);
633 if (after) it = it->next;
635 dock->dock_apps = g_list_insert_before(dock->dock_apps, it, app);
639 static gboolean hide_timeout(gpointer data)
648 return FALSE; /* don't repeat */
651 static gboolean show_timeout(gpointer data)
654 dock->hidden = FALSE;
660 return FALSE; /* don't repeat */
663 void dock_hide(gboolean hide)
666 if (dock->hidden && config_dock_hide) {
667 show_timeout_id = g_timeout_add_full(G_PRIORITY_DEFAULT,
668 config_dock_show_delay,
669 show_timeout, NULL, NULL);
670 } else if (!dock->hidden && config_dock_hide && hide_timeout_id) {
671 if (hide_timeout_id) g_source_remove(hide_timeout_id);
675 if (!dock->hidden && config_dock_hide) {
676 hide_timeout_id = g_timeout_add_full(G_PRIORITY_DEFAULT,
677 config_dock_show_delay,
678 hide_timeout, NULL, NULL);
679 } else if (dock->hidden && config_dock_hide && show_timeout_id) {
680 if (show_timeout_id) g_source_remove(show_timeout_id);
686 void dock_get_area(Rect *a)
688 RECT_SET(*a, dock->area.x, dock->area.y,
689 dock->area.width, dock->area.height);
692 void dock_raise_dock(void)
694 stacking_raise(DOCK_AS_WINDOW(dock));
697 void dock_lower_dock(void)
699 stacking_lower(DOCK_AS_WINDOW(dock));
702 ObDockApp* dock_find_dockapp(Window xwin)
704 return g_hash_table_lookup(dock->dock_map, &xwin);