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.
27 #include "render/theme.h"
29 #define DOCK_EVENT_MASK (ButtonPressMask | ButtonReleaseMask | \
30 EnterWindowMask | LeaveWindowMask)
31 #define DOCKAPP_EVENT_MASK (StructureNotifyMask)
32 #define DOCK_NOPROPAGATEMASK (ButtonPressMask | ButtonReleaseMask | \
37 StrutPartial dock_strut;
39 static void dock_app_grab_button(ObDockApp *app, gboolean grab)
42 grab_button_full(config_dock_app_move_button,
43 config_dock_app_move_modifiers, app->icon_win,
44 ButtonPressMask | ButtonReleaseMask |
46 GrabModeAsync, OB_CURSOR_MOVE);
48 ungrab_button(config_dock_app_move_button,
49 config_dock_app_move_modifiers, app->icon_win);
53 void dock_startup(gboolean reconfig)
55 XSetWindowAttributes attrib;
60 XSetWindowBorder(ob_display, dock->frame,
61 RrColorPixel(ob_rr_theme->osd_border_color));
62 XSetWindowBorderWidth(ob_display, dock->frame, ob_rr_theme->obwidth);
64 RrAppearanceFree(dock->a_frame);
65 dock->a_frame = RrAppearanceCopy(ob_rr_theme->osd_hilite_bg);
67 stacking_add(DOCK_AS_WINDOW(dock));
72 for (it = dock->dock_apps; it; it = g_list_next(it))
73 dock_app_grab_button(it->data, TRUE);
77 STRUT_PARTIAL_SET(dock_strut, 0, 0, 0, 0,
78 0, 0, 0, 0, 0, 0, 0, 0);
80 dock = g_new0(ObDock, 1);
81 dock->obwin.type = Window_Dock;
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),
90 RrDepth(ob_rr_inst), InputOutput,
92 CWOverrideRedirect | CWEventMask |
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);
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);
104 g_hash_table_insert(window_map, &dock->frame, dock);
105 stacking_add(DOCK_AS_WINDOW(dock));
108 void dock_shutdown(gboolean reconfig)
113 stacking_remove(DOCK_AS_WINDOW(dock));
115 for (it = dock->dock_apps; it; it = g_list_next(it))
116 dock_app_grab_button(it->data, FALSE);
120 XDestroyWindow(ob_display, dock->frame);
121 RrAppearanceFree(dock->a_frame);
122 g_hash_table_remove(window_map, &dock->frame);
123 stacking_remove(dock);
126 void dock_add(Window win, XWMHints *wmhints)
129 XWindowAttributes attrib;
132 app = g_new0(ObDockApp, 1);
133 app->obwin.type = Window_DockApp;
135 app->icon_win = (wmhints->flags & IconWindowHint) ?
136 wmhints->icon_window : win;
138 if (PROP_GETSS(app->win, wm_class, locale, &data)) {
140 app->name = g_strdup(data[0]);
142 app->class = g_strdup(data[1]);
147 if (app->name == NULL) app->name = g_strdup("");
148 if (app->class == NULL) app->class = g_strdup("");
150 if (XGetWindowAttributes(ob_display, app->icon_win, &attrib)) {
151 app->w = attrib.width;
152 app->h = attrib.height;
154 app->w = app->h = 64;
157 dock->dock_apps = g_list_append(dock->dock_apps, app);
160 XReparentWindow(ob_display, app->icon_win, dock->frame, app->x, app->y);
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.
168 if (ob_state() == OB_STATE_STARTING)
169 app->ignore_unmaps += 2;
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);
176 XMapWindow(ob_display, app->icon_win);
177 XSync(ob_display, False);
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);
184 dock_app_grab_button(app, TRUE);
186 g_hash_table_insert(window_map, &app->icon_win, app);
188 ob_debug("Managed Dock App: 0x%lx (%s)\n", app->icon_win, app->class);
191 void dock_remove_all(void)
193 while (dock->dock_apps)
194 dock_remove(dock->dock_apps->data, TRUE);
197 void dock_remove(ObDockApp *app, gboolean reparent)
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);
205 g_hash_table_remove(window_map, &app->icon_win);
208 XReparentWindow(ob_display, app->icon_win,
209 RootWindow(ob_display, ob_screen), app->x, app->y);
211 dock->dock_apps = g_list_remove(dock->dock_apps, app);
214 ob_debug("Unmanaged Dock App: 0x%lx (%s)\n", app->icon_win, app->class);
221 void dock_configure(void)
231 RrMargins(dock->a_frame, &l, &t, &r, &b);
232 hidesize = MAX(1, ob_rr_theme->obwidth);
234 dock->area.width = dock->area.height = 0;
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);
244 case OB_ORIENTATION_VERT:
245 dock->area.width = MAX(dock->area.width, app->w);
246 dock->area.height += app->h;
251 dock->area.width += l + r;
252 dock->area.height += t + b;
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:
263 app->y = (dock->area.height - app->h) / 2;
266 case OB_ORIENTATION_VERT:
267 app->x = (dock->area.width - app->w) / 2;
273 XMoveWindow(ob_display, app->icon_win, app->x, app->y);
276 /* used for calculating offsets */
277 dock->area.width += ob_rr_theme->obwidth * 2;
278 dock->area.height += ob_rr_theme->obwidth * 2;
280 a = screen_physical_area_all_monitors();
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;
288 switch (config_dock_pos) {
289 case OB_DIRECTION_NORTHWEST:
292 gravity = NorthWestGravity;
294 case OB_DIRECTION_NORTH:
295 dock->area.x = a->width / 2;
297 gravity = NorthGravity;
299 case OB_DIRECTION_NORTHEAST:
300 dock->area.x = a->width;
302 gravity = NorthEastGravity;
304 case OB_DIRECTION_WEST:
306 dock->area.y = a->height / 2;
307 gravity = WestGravity;
309 case OB_DIRECTION_EAST:
310 dock->area.x = a->width;
311 dock->area.y = a->height / 2;
312 gravity = EastGravity;
314 case OB_DIRECTION_SOUTHWEST:
316 dock->area.y = a->height;
317 gravity = SouthWestGravity;
319 case OB_DIRECTION_SOUTH:
320 dock->area.x = a->width / 2;
321 dock->area.y = a->height;
322 gravity = SouthGravity;
324 case OB_DIRECTION_SOUTHEAST:
325 dock->area.x = a->width;
326 dock->area.y = a->height;
327 gravity = SouthEastGravity;
330 g_assert_not_reached();
338 dock->area.x -= dock->area.width / 2;
340 case NorthEastGravity:
342 case SouthEastGravity:
343 dock->area.x -= dock->area.width;
350 dock->area.y -= dock->area.height / 2;
352 case SouthWestGravity:
354 case SouthEastGravity:
355 dock->area.y -= dock->area.height;
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;
367 case OB_ORIENTATION_VERT:
368 dock->area.x -= dock->area.width - hidesize;
372 case OB_DIRECTION_NORTH:
373 dock->area.y -= dock->area.height - hidesize;
375 case OB_DIRECTION_NORTHEAST:
376 switch (config_dock_orient) {
377 case OB_ORIENTATION_HORZ:
378 dock->area.y -= dock->area.height - hidesize;
380 case OB_ORIENTATION_VERT:
381 dock->area.x += dock->area.width - hidesize;
385 case OB_DIRECTION_WEST:
386 dock->area.x -= dock->area.width - hidesize;
388 case OB_DIRECTION_EAST:
389 dock->area.x += dock->area.width - hidesize;
391 case OB_DIRECTION_SOUTHWEST:
392 switch (config_dock_orient) {
393 case OB_ORIENTATION_HORZ:
394 dock->area.y += dock->area.height - hidesize;
396 case OB_ORIENTATION_VERT:
397 dock->area.x -= dock->area.width - hidesize;
400 case OB_DIRECTION_SOUTH:
401 dock->area.y += dock->area.height - hidesize;
403 case OB_DIRECTION_SOUTHEAST:
404 switch (config_dock_orient) {
405 case OB_ORIENTATION_HORZ:
406 dock->area.y += dock->area.height - hidesize;
408 case OB_ORIENTATION_VERT:
409 dock->area.x += dock->area.width - hidesize;
417 if (!config_dock_floating && config_dock_hide) {
421 strw = dock->area.width;
422 strh = dock->area.height;
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)
431 STRUT_PARTIAL_SET(dock_strut, 0, 0, 0, 0,
432 0, 0, 0, 0, 0, 0, 0, 0);
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);
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);
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);
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);
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);
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);
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);
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);
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);
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);
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);
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);
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;
518 if (dock->dock_apps) {
519 g_assert(dock->area.width > 0);
520 g_assert(dock->area.height > 0);
522 XMoveResizeWindow(ob_display, dock->frame, dock->area.x, dock->area.y,
523 dock->area.width, dock->area.height);
525 RrPaint(dock->a_frame, dock->frame, dock->area.width,
527 XMapWindow(ob_display, dock->frame);
529 XUnmapWindow(ob_display, dock->frame);
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;
535 screen_update_areas();
540 void dock_app_configure(ObDockApp *app, gint w, gint h)
547 void dock_app_drag(ObDockApp *app, XMotionEvent *e)
549 ObDockApp *over = NULL;
558 /* are we on top of the dock? */
559 if (!(x >= dock->area.x &&
561 x < dock->area.x + dock->area.width &&
562 y < dock->area.y + dock->area.height))
568 /* which dock app are we on top of? */
570 for (it = dock->dock_apps; it; it = g_list_next(it)) {
572 switch (config_dock_orient) {
573 case OB_ORIENTATION_HORZ:
574 if (x >= over->x && x < over->x + over->w)
577 case OB_ORIENTATION_VERT:
578 if (y >= over->y && y < over->y + over->h)
582 /* dont go to it->next! */
585 if (!it || app == over) return;
590 switch (config_dock_orient) {
591 case OB_ORIENTATION_HORZ:
592 after = (x > over->w / 2);
594 case OB_ORIENTATION_VERT:
595 after = (y > over->h / 2);
598 g_assert_not_reached();
601 /* remove before doing the it->next! */
602 dock->dock_apps = g_list_remove(dock->dock_apps, app);
604 if (after) it = it->next;
606 dock->dock_apps = g_list_insert_before(dock->dock_apps, it, app);
610 static gboolean hide_timeout(gpointer data)
616 return FALSE; /* don't repeat */
619 static gboolean show_timeout(gpointer data)
622 dock->hidden = FALSE;
625 return FALSE; /* don't repeat */
628 void dock_hide(gboolean hide)
631 if (dock->hidden && config_dock_hide) {
632 obt_main_loop_timeout_add(ob_main_loop,
633 config_dock_show_delay * 1000,
635 g_direct_equal, NULL);
636 } else if (!dock->hidden && config_dock_hide) {
637 obt_main_loop_timeout_remove(ob_main_loop, hide_timeout);
640 if (!dock->hidden && config_dock_hide) {
641 obt_main_loop_timeout_add(ob_main_loop,
642 config_dock_hide_delay * 1000,
644 g_direct_equal, NULL);
645 } else if (dock->hidden && config_dock_hide) {
646 obt_main_loop_timeout_remove(ob_main_loop, show_timeout);
651 void dock_get_area(Rect *a)
653 RECT_SET(*a, dock->area.x, dock->area.y,
654 dock->area.width, dock->area.height);