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.
28 #include "render/theme.h"
30 #define DOCK_EVENT_MASK (ButtonPressMask | ButtonReleaseMask | \
31 EnterWindowMask | LeaveWindowMask)
32 #define DOCKAPP_EVENT_MASK (StructureNotifyMask)
33 #define DOCK_NOPROPAGATEMASK (ButtonPressMask | ButtonReleaseMask | \
38 StrutPartial dock_strut;
40 static void dock_app_grab_button(ObDockApp *app, gboolean grab)
43 grab_button_full(config_dock_app_move_button,
44 config_dock_app_move_modifiers, app->icon_win,
45 ButtonPressMask | ButtonReleaseMask |
47 GrabModeAsync, OB_CURSOR_MOVE);
49 ungrab_button(config_dock_app_move_button,
50 config_dock_app_move_modifiers, app->icon_win);
54 void dock_startup(gboolean reconfig)
56 XSetWindowAttributes attrib;
61 XSetWindowBorder(ob_display, dock->frame,
62 RrColorPixel(ob_rr_theme->osd_border_color));
63 XSetWindowBorderWidth(ob_display, dock->frame, ob_rr_theme->obwidth);
65 RrAppearanceFree(dock->a_frame);
66 dock->a_frame = RrAppearanceCopy(ob_rr_theme->osd_hilite_bg);
68 stacking_add(DOCK_AS_WINDOW(dock));
73 for (it = dock->dock_apps; it; it = g_list_next(it))
74 dock_app_grab_button(it->data, TRUE);
78 STRUT_PARTIAL_SET(dock_strut, 0, 0, 0, 0,
79 0, 0, 0, 0, 0, 0, 0, 0);
81 dock = g_new0(ObDock, 1);
82 dock->obwin.type = Window_Dock;
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),
91 RrDepth(ob_rr_inst), InputOutput,
93 CWOverrideRedirect | CWEventMask |
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);
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);
105 g_hash_table_insert(window_map, &dock->frame, dock);
106 stacking_add(DOCK_AS_WINDOW(dock));
109 void dock_shutdown(gboolean reconfig)
114 stacking_remove(DOCK_AS_WINDOW(dock));
116 for (it = dock->dock_apps; it; it = g_list_next(it))
117 dock_app_grab_button(it->data, FALSE);
121 XDestroyWindow(ob_display, dock->frame);
122 RrAppearanceFree(dock->a_frame);
123 g_hash_table_remove(window_map, &dock->frame);
124 stacking_remove(dock);
127 void dock_add(Window win, XWMHints *wmhints)
130 XWindowAttributes attrib;
133 app = g_new0(ObDockApp, 1);
134 app->obwin.type = Window_DockApp;
136 app->icon_win = (wmhints->flags & IconWindowHint) ?
137 wmhints->icon_window : win;
139 if (PROP_GETSS(app->win, wm_class, locale, &data)) {
141 app->name = g_strdup(data[0]);
143 app->class = g_strdup(data[1]);
148 if (app->name == NULL) app->name = g_strdup("");
149 if (app->class == NULL) app->class = g_strdup("");
151 if (XGetWindowAttributes(ob_display, app->icon_win, &attrib)) {
152 app->w = attrib.width;
153 app->h = attrib.height;
155 app->w = app->h = 64;
158 dock->dock_apps = g_list_append(dock->dock_apps, app);
161 XReparentWindow(ob_display, app->icon_win, dock->frame, app->x, app->y);
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.
169 if (ob_state() == OB_STATE_STARTING)
170 app->ignore_unmaps += 2;
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);
177 XMapWindow(ob_display, app->icon_win);
178 XSync(ob_display, False);
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);
185 dock_app_grab_button(app, TRUE);
187 g_hash_table_insert(window_map, &app->icon_win, app);
189 ob_debug("Managed Dock App: 0x%lx (%s)\n", app->icon_win, app->class);
192 void dock_remove_all(void)
194 while (dock->dock_apps)
195 dock_remove(dock->dock_apps->data, TRUE);
198 void dock_remove(ObDockApp *app, gboolean reparent)
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);
206 g_hash_table_remove(window_map, &app->icon_win);
209 XReparentWindow(ob_display, app->icon_win,
210 RootWindow(ob_display, ob_screen), app->x, app->y);
212 dock->dock_apps = g_list_remove(dock->dock_apps, app);
215 ob_debug("Unmanaged Dock App: 0x%lx (%s)\n", app->icon_win, app->class);
222 void dock_configure(void)
232 RrMargins(dock->a_frame, &l, &t, &r, &b);
233 hidesize = MAX(1, ob_rr_theme->obwidth);
235 dock->area.width = dock->area.height = 0;
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);
245 case OB_ORIENTATION_VERT:
246 dock->area.width = MAX(dock->area.width, app->w);
247 dock->area.height += app->h;
252 if (dock->dock_apps) {
253 dock->area.width += l + r;
254 dock->area.height += t + b;
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:
266 app->y = (dock->area.height - app->h) / 2;
269 case OB_ORIENTATION_VERT:
270 app->x = (dock->area.width - app->w) / 2;
276 XMoveWindow(ob_display, app->icon_win, app->x, app->y);
279 /* used for calculating offsets */
280 dock->area.width += ob_rr_theme->obwidth * 2;
281 dock->area.height += ob_rr_theme->obwidth * 2;
283 a = screen_physical_area_all_monitors();
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;
291 switch (config_dock_pos) {
292 case OB_DIRECTION_NORTHWEST:
295 gravity = NorthWestGravity;
297 case OB_DIRECTION_NORTH:
298 dock->area.x = a->width / 2;
300 gravity = NorthGravity;
302 case OB_DIRECTION_NORTHEAST:
303 dock->area.x = a->width;
305 gravity = NorthEastGravity;
307 case OB_DIRECTION_WEST:
309 dock->area.y = a->height / 2;
310 gravity = WestGravity;
312 case OB_DIRECTION_EAST:
313 dock->area.x = a->width;
314 dock->area.y = a->height / 2;
315 gravity = EastGravity;
317 case OB_DIRECTION_SOUTHWEST:
319 dock->area.y = a->height;
320 gravity = SouthWestGravity;
322 case OB_DIRECTION_SOUTH:
323 dock->area.x = a->width / 2;
324 dock->area.y = a->height;
325 gravity = SouthGravity;
327 case OB_DIRECTION_SOUTHEAST:
328 dock->area.x = a->width;
329 dock->area.y = a->height;
330 gravity = SouthEastGravity;
333 g_assert_not_reached();
341 dock->area.x -= dock->area.width / 2;
343 case NorthEastGravity:
345 case SouthEastGravity:
346 dock->area.x -= dock->area.width;
353 dock->area.y -= dock->area.height / 2;
355 case SouthWestGravity:
357 case SouthEastGravity:
358 dock->area.y -= dock->area.height;
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;
370 case OB_ORIENTATION_VERT:
371 dock->area.x -= dock->area.width - hidesize;
375 case OB_DIRECTION_NORTH:
376 dock->area.y -= dock->area.height - hidesize;
378 case OB_DIRECTION_NORTHEAST:
379 switch (config_dock_orient) {
380 case OB_ORIENTATION_HORZ:
381 dock->area.y -= dock->area.height - hidesize;
383 case OB_ORIENTATION_VERT:
384 dock->area.x += dock->area.width - hidesize;
388 case OB_DIRECTION_WEST:
389 dock->area.x -= dock->area.width - hidesize;
391 case OB_DIRECTION_EAST:
392 dock->area.x += dock->area.width - hidesize;
394 case OB_DIRECTION_SOUTHWEST:
395 switch (config_dock_orient) {
396 case OB_ORIENTATION_HORZ:
397 dock->area.y += dock->area.height - hidesize;
399 case OB_ORIENTATION_VERT:
400 dock->area.x -= dock->area.width - hidesize;
403 case OB_DIRECTION_SOUTH:
404 dock->area.y += dock->area.height - hidesize;
406 case OB_DIRECTION_SOUTHEAST:
407 switch (config_dock_orient) {
408 case OB_ORIENTATION_HORZ:
409 dock->area.y += dock->area.height - hidesize;
411 case OB_ORIENTATION_VERT:
412 dock->area.x += dock->area.width - hidesize;
420 if (!config_dock_floating && config_dock_hide) {
424 strw = dock->area.width;
425 strh = dock->area.height;
429 if (!dock->dock_apps) {
430 STRUT_PARTIAL_SET(dock_strut, 0, 0, 0, 0,
431 0, 0, 0, 0, 0, 0, 0, 0);
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);
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);
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);
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);
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);
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);
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);
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);
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);
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);
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);
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);
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);
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;
522 if (dock->dock_apps) {
523 g_assert(dock->area.width > 0);
524 g_assert(dock->area.height > 0);
526 XMoveResizeWindow(ob_display, dock->frame, dock->area.x, dock->area.y,
527 dock->area.width, dock->area.height);
529 RrPaint(dock->a_frame, dock->frame, dock->area.width,
531 XMapWindow(ob_display, dock->frame);
533 XUnmapWindow(ob_display, dock->frame);
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;
542 screen_update_areas();
547 void dock_app_configure(ObDockApp *app, gint w, gint h)
554 void dock_app_drag(ObDockApp *app, XMotionEvent *e)
556 ObDockApp *over = NULL;
565 /* are we on top of the dock? */
566 if (!(x >= dock->area.x &&
568 x < dock->area.x + dock->area.width &&
569 y < dock->area.y + dock->area.height))
575 /* which dock app are we on top of? */
577 for (it = dock->dock_apps; it; it = g_list_next(it)) {
579 switch (config_dock_orient) {
580 case OB_ORIENTATION_HORZ:
581 if (x >= over->x && x < over->x + over->w)
584 case OB_ORIENTATION_VERT:
585 if (y >= over->y && y < over->y + over->h)
589 /* dont go to it->next! */
592 if (!it || app == over) return;
597 switch (config_dock_orient) {
598 case OB_ORIENTATION_HORZ:
599 after = (x > over->w / 2);
601 case OB_ORIENTATION_VERT:
602 after = (y > over->h / 2);
605 g_assert_not_reached();
608 /* remove before doing the it->next! */
609 dock->dock_apps = g_list_remove(dock->dock_apps, app);
611 if (after) it = it->next;
613 dock->dock_apps = g_list_insert_before(dock->dock_apps, it, app);
617 static gboolean hide_timeout(gpointer data)
623 return FALSE; /* don't repeat */
626 static gboolean show_timeout(gpointer data)
629 dock->hidden = FALSE;
632 return FALSE; /* don't repeat */
635 void dock_hide(gboolean 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);
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);
656 void dock_get_area(Rect *a)
658 RECT_SET(*a, dock->area.x, dock->area.y,
659 dock->area.width, dock->area.height);