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)
36 StrutPartial dock_strut;
38 static void dock_app_grab_button(ObDockApp *app, gboolean grab)
41 grab_button_full(config_dock_app_move_button,
42 config_dock_app_move_modifiers, app->icon_win,
43 ButtonPressMask | ButtonReleaseMask |
45 GrabModeAsync, OB_CURSOR_MOVE);
47 ungrab_button(config_dock_app_move_button,
48 config_dock_app_move_modifiers, app->icon_win);
52 void dock_startup(gboolean reconfig)
54 XSetWindowAttributes attrib;
59 XSetWindowBorder(ob_display, dock->frame,
60 RrColorPixel(ob_rr_theme->frame_b_color));
61 XSetWindowBorderWidth(ob_display, dock->frame, ob_rr_theme->fbwidth);
63 RrAppearanceFree(dock->a_frame);
64 dock->a_frame = RrAppearanceCopy(ob_rr_theme->a_focused_title);
66 stacking_add(DOCK_AS_WINDOW(dock));
71 for (it = dock->dock_apps; it; it = g_list_next(it))
72 dock_app_grab_button(it->data, TRUE);
76 STRUT_PARTIAL_SET(dock_strut, 0, 0, 0, 0,
77 0, 0, 0, 0, 0, 0, 0, 0);
79 dock = g_new0(ObDock, 1);
80 dock->obwin.type = Window_Dock;
84 attrib.event_mask = DOCK_EVENT_MASK;
85 attrib.override_redirect = True;
86 dock->frame = XCreateWindow(ob_display, RootWindow(ob_display, ob_screen),
88 RrDepth(ob_rr_inst), InputOutput,
90 CWOverrideRedirect | CWEventMask,
92 dock->a_frame = RrAppearanceCopy(ob_rr_theme->a_focused_title);
93 XSetWindowBorder(ob_display, dock->frame,
94 RrColorPixel(ob_rr_theme->frame_b_color));
95 XSetWindowBorderWidth(ob_display, dock->frame, ob_rr_theme->fbwidth);
97 g_hash_table_insert(window_map, &dock->frame, dock);
98 stacking_add(DOCK_AS_WINDOW(dock));
101 void dock_shutdown(gboolean reconfig)
106 stacking_remove(DOCK_AS_WINDOW(dock));
108 for (it = dock->dock_apps; it; it = g_list_next(it))
109 dock_app_grab_button(it->data, FALSE);
113 XDestroyWindow(ob_display, dock->frame);
114 RrAppearanceFree(dock->a_frame);
115 g_hash_table_remove(window_map, &dock->frame);
116 stacking_remove(dock);
119 void dock_add(Window win, XWMHints *wmhints)
122 XWindowAttributes attrib;
125 app = g_new0(ObDockApp, 1);
126 app->obwin.type = Window_DockApp;
128 app->icon_win = (wmhints->flags & IconWindowHint) ?
129 wmhints->icon_window : win;
131 if (PROP_GETSS(app->win, wm_class, locale, &data)) {
133 app->name = g_strdup(data[0]);
135 app->class = g_strdup(data[1]);
140 if (app->name == NULL) app->name = g_strdup("");
141 if (app->class == NULL) app->class = g_strdup("");
143 if (XGetWindowAttributes(ob_display, app->icon_win, &attrib)) {
144 app->w = attrib.width;
145 app->h = attrib.height;
147 app->w = app->h = 64;
150 dock->dock_apps = g_list_append(dock->dock_apps, app);
153 XReparentWindow(ob_display, app->icon_win, dock->frame, app->x, app->y);
155 This is the same case as in frame.c for client windows. When Openbox is
156 starting, the window is already mapped so we see unmap events occur for
157 it. There are 2 unmap events generated that we see, one with the 'event'
158 member set the root window, and one set to the client, but both get
159 handled and need to be ignored.
161 if (ob_state() == OB_STATE_STARTING)
162 app->ignore_unmaps += 2;
164 if (app->win != app->icon_win) {
165 /* have to map it so that it can be re-managed on a restart */
166 XMoveWindow(ob_display, app->win, -1000, -1000);
167 XMapWindow(ob_display, app->win);
169 XMapWindow(ob_display, app->icon_win);
170 XSync(ob_display, False);
172 /* specify that if we exit, the window should not be destroyed and should
173 be reparented back to root automatically */
174 XChangeSaveSet(ob_display, app->icon_win, SetModeInsert);
175 XSelectInput(ob_display, app->icon_win, DOCKAPP_EVENT_MASK);
177 dock_app_grab_button(app, TRUE);
179 g_hash_table_insert(window_map, &app->icon_win, app);
181 ob_debug("Managed Dock App: 0x%lx (%s)\n", app->icon_win, app->class);
184 void dock_remove_all()
186 while (dock->dock_apps)
187 dock_remove(dock->dock_apps->data, TRUE);
190 void dock_remove(ObDockApp *app, gboolean reparent)
192 dock_app_grab_button(app, FALSE);
193 XSelectInput(ob_display, app->icon_win, NoEventMask);
194 /* remove the window from our save set */
195 XChangeSaveSet(ob_display, app->icon_win, SetModeDelete);
196 XSync(ob_display, False);
198 g_hash_table_remove(window_map, &app->icon_win);
201 XReparentWindow(ob_display, app->icon_win,
202 RootWindow(ob_display, ob_screen), app->x, app->y);
204 dock->dock_apps = g_list_remove(dock->dock_apps, app);
207 ob_debug("Unmanaged Dock App: 0x%lx (%s)\n", app->icon_win, app->class);
214 void dock_configure()
223 RrMinsize(dock->a_frame, &minw, &minh);
225 dock->w = dock->h = 0;
228 for (it = dock->dock_apps; it; it = g_list_next(it)) {
229 ObDockApp *app = it->data;
230 switch (config_dock_orient) {
231 case OB_ORIENTATION_HORZ:
233 dock->h = MAX(dock->h, app->h);
235 case OB_ORIENTATION_VERT:
236 dock->w = MAX(dock->w, app->w);
242 spot = (config_dock_orient == OB_ORIENTATION_HORZ ? minw : minh) / 2;
244 /* position the apps */
245 for (it = dock->dock_apps; it; it = g_list_next(it)) {
246 ObDockApp *app = it->data;
247 switch (config_dock_orient) {
248 case OB_ORIENTATION_HORZ:
250 app->y = (dock->h - app->h) / 2;
253 case OB_ORIENTATION_VERT:
254 app->x = (dock->w - app->w) / 2;
260 XMoveWindow(ob_display, app->icon_win, app->x, app->y);
263 /* used for calculating offsets */
264 dock->w += ob_rr_theme->fbwidth * 2;
265 dock->h += ob_rr_theme->fbwidth * 2;
267 a = screen_physical_area();
269 /* calculate position */
270 if (config_dock_floating) {
271 dock->x = config_dock_x;
272 dock->y = config_dock_y;
273 gravity = NorthWestGravity;
275 switch (config_dock_pos) {
276 case OB_DIRECTION_NORTHWEST:
279 gravity = NorthWestGravity;
281 case OB_DIRECTION_NORTH:
282 dock->x = a->width / 2;
284 gravity = NorthGravity;
286 case OB_DIRECTION_NORTHEAST:
289 gravity = NorthEastGravity;
291 case OB_DIRECTION_WEST:
293 dock->y = a->height / 2;
294 gravity = WestGravity;
296 case OB_DIRECTION_EAST:
298 dock->y = a->height / 2;
299 gravity = EastGravity;
301 case OB_DIRECTION_SOUTHWEST:
304 gravity = SouthWestGravity;
306 case OB_DIRECTION_SOUTH:
307 dock->x = a->width / 2;
309 gravity = SouthGravity;
311 case OB_DIRECTION_SOUTHEAST:
314 gravity = SouthEastGravity;
317 g_assert_not_reached();
325 dock->x -= dock->w / 2;
327 case NorthEastGravity:
329 case SouthEastGravity:
337 dock->y -= dock->h / 2;
339 case SouthWestGravity:
341 case SouthEastGravity:
346 if (config_dock_hide && dock->hidden) {
347 if (!config_dock_floating) {
348 switch (config_dock_pos) {
349 case OB_DIRECTION_NORTHWEST:
350 switch (config_dock_orient) {
351 case OB_ORIENTATION_HORZ:
352 dock->y -= dock->h - ob_rr_theme->fbwidth;
354 case OB_ORIENTATION_VERT:
355 dock->x -= dock->w - ob_rr_theme->fbwidth;
359 case OB_DIRECTION_NORTH:
360 dock->y -= dock->h - ob_rr_theme->fbwidth;
362 case OB_DIRECTION_NORTHEAST:
363 switch (config_dock_orient) {
364 case OB_ORIENTATION_HORZ:
365 dock->y -= dock->h - ob_rr_theme->fbwidth;
367 case OB_ORIENTATION_VERT:
368 dock->x += dock->w - ob_rr_theme->fbwidth;
372 case OB_DIRECTION_WEST:
373 dock->x -= dock->w - ob_rr_theme->fbwidth;
375 case OB_DIRECTION_EAST:
376 dock->x += dock->w - ob_rr_theme->fbwidth;
378 case OB_DIRECTION_SOUTHWEST:
379 switch (config_dock_orient) {
380 case OB_ORIENTATION_HORZ:
381 dock->y += dock->h - ob_rr_theme->fbwidth;
383 case OB_ORIENTATION_VERT:
384 dock->x -= dock->w - ob_rr_theme->fbwidth;
387 case OB_DIRECTION_SOUTH:
388 dock->y += dock->h - ob_rr_theme->fbwidth;
390 case OB_DIRECTION_SOUTHEAST:
391 switch (config_dock_orient) {
392 case OB_ORIENTATION_HORZ:
393 dock->y += dock->h - ob_rr_theme->fbwidth;
395 case OB_ORIENTATION_VERT:
396 dock->x += dock->w - ob_rr_theme->fbwidth;
404 if (!config_dock_floating && config_dock_hide) {
405 strw = ob_rr_theme->fbwidth;
406 strh = ob_rr_theme->fbwidth;
413 if (!dock->dock_apps) {
414 STRUT_PARTIAL_SET(dock_strut, 0, 0, 0, 0,
415 0, 0, 0, 0, 0, 0, 0, 0);
416 } else if (config_dock_floating || config_dock_nostrut) {
417 STRUT_PARTIAL_SET(dock_strut, 0, 0, 0, 0,
418 0, 0, 0, 0, 0, 0, 0, 0);
420 switch (config_dock_pos) {
421 case OB_DIRECTION_NORTHWEST:
422 switch (config_dock_orient) {
423 case OB_ORIENTATION_HORZ:
424 STRUT_PARTIAL_SET(dock_strut, 0, strh, 0, 0,
425 0, 0, dock->x, dock->x + dock->w - 1,
428 case OB_ORIENTATION_VERT:
429 STRUT_PARTIAL_SET(dock_strut, strw, 0, 0, 0,
430 dock->y, dock->y + dock->h - 1,
435 case OB_DIRECTION_NORTH:
436 STRUT_PARTIAL_SET(dock_strut, 0, strh, 0, 0,
437 dock->x, dock->x + dock->w - 1,
440 case OB_DIRECTION_NORTHEAST:
441 switch (config_dock_orient) {
442 case OB_ORIENTATION_HORZ:
443 STRUT_PARTIAL_SET(dock_strut, 0, strh, 0, 0,
444 0, 0, dock->x, dock->x + dock->w -1,
447 case OB_ORIENTATION_VERT:
448 STRUT_PARTIAL_SET(dock_strut, 0, 0, strw, 0,
450 dock->y, dock->y + dock->h - 1, 0, 0);
454 case OB_DIRECTION_WEST:
455 STRUT_PARTIAL_SET(dock_strut, strw, 0, 0, 0,
456 dock->y, dock->y + dock->h - 1,
459 case OB_DIRECTION_EAST:
460 STRUT_PARTIAL_SET(dock_strut, 0, 0, strw, 0,
462 dock->y, dock->y + dock->h - 1, 0, 0);
464 case OB_DIRECTION_SOUTHWEST:
465 switch (config_dock_orient) {
466 case OB_ORIENTATION_HORZ:
467 STRUT_PARTIAL_SET(dock_strut, 0, 0, 0, strh,
469 dock->x, dock->x + dock->w - 1);
471 case OB_ORIENTATION_VERT:
472 STRUT_PARTIAL_SET(dock_strut, strw, 0, 0, 0,
473 dock->y, dock->y + dock->h - 1,
478 case OB_DIRECTION_SOUTH:
479 STRUT_PARTIAL_SET(dock_strut, 0, 0, 0, strh,
481 dock->x, dock->x + dock->w - 1);
483 case OB_DIRECTION_SOUTHEAST:
484 switch (config_dock_orient) {
485 case OB_ORIENTATION_HORZ:
486 STRUT_PARTIAL_SET(dock_strut, 0, 0, 0, strh,
488 dock->x, dock->x + dock->w - 1);
490 case OB_ORIENTATION_VERT:
491 STRUT_PARTIAL_SET(dock_strut, 0, 0, strw, 0,
493 dock->y, dock->y + dock->h - 1, 0, 0);
503 /* not used for actually sizing shit */
504 dock->w -= ob_rr_theme->fbwidth * 2;
505 dock->h -= ob_rr_theme->fbwidth * 2;
507 if (dock->dock_apps) {
508 g_assert(dock->w > 0);
509 g_assert(dock->h > 0);
511 XMoveResizeWindow(ob_display, dock->frame,
512 dock->x, dock->y, dock->w, dock->h);
514 RrPaint(dock->a_frame, dock->frame, dock->w, dock->h);
515 XMapWindow(ob_display, dock->frame);
517 XUnmapWindow(ob_display, dock->frame);
519 /* but they are useful outside of this function! */
520 dock->w += ob_rr_theme->fbwidth * 2;
521 dock->h += ob_rr_theme->fbwidth * 2;
523 screen_update_areas();
526 void dock_app_configure(ObDockApp *app, gint w, gint h)
533 void dock_app_drag(ObDockApp *app, XMotionEvent *e)
535 ObDockApp *over = NULL;
544 /* are we on top of the dock? */
545 if (!(x >= dock->x &&
547 x < dock->x + dock->w &&
548 y < dock->y + dock->h))
554 /* which dock app are we on top of? */
556 for (it = dock->dock_apps; it; it = g_list_next(it)) {
558 switch (config_dock_orient) {
559 case OB_ORIENTATION_HORZ:
560 if (x >= over->x && x < over->x + over->w)
563 case OB_ORIENTATION_VERT:
564 if (y >= over->y && y < over->y + over->h)
568 /* dont go to it->next! */
571 if (!it || app == over) return;
576 switch (config_dock_orient) {
577 case OB_ORIENTATION_HORZ:
578 after = (x > over->w / 2);
580 case OB_ORIENTATION_VERT:
581 after = (y > over->h / 2);
584 g_assert_not_reached();
587 /* remove before doing the it->next! */
588 dock->dock_apps = g_list_remove(dock->dock_apps, app);
590 if (after) it = it->next;
592 dock->dock_apps = g_list_insert_before(dock->dock_apps, it, app);
596 static gboolean hide_timeout(gpointer data)
602 return FALSE; /* don't repeat */
605 static gboolean show_timeout(gpointer data)
608 dock->hidden = FALSE;
611 return FALSE; /* don't repeat */
614 void dock_hide(gboolean hide)
617 if (dock->hidden && config_dock_hide) {
618 ob_main_loop_timeout_add(ob_main_loop, config_dock_show_delay,
619 show_timeout, NULL, g_direct_equal, NULL);
620 } else if (!dock->hidden && config_dock_hide) {
621 ob_main_loop_timeout_remove(ob_main_loop, hide_timeout);
624 if (!dock->hidden && config_dock_hide) {
625 ob_main_loop_timeout_add(ob_main_loop, config_dock_hide_delay,
626 hide_timeout, NULL, g_direct_equal, NULL);
627 } else if (dock->hidden && config_dock_hide) {
628 ob_main_loop_timeout_remove(ob_main_loop, show_timeout);