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 "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(obt_display
, dock
->frame
,
61 RrColorPixel(ob_rr_theme
->osd_border_color
));
62 XSetWindowBorderWidth(obt_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
= OB_WINDOW_CLASS_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(obt_display
,
89 RootWindow(obt_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(obt_display
, dock
->frame
,
98 RrColorPixel(ob_rr_theme
->osd_border_color
));
99 XSetWindowBorderWidth(obt_display
, dock
->frame
, ob_rr_theme
->obwidth
);
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
));
105 window_add(&dock
->frame
, DOCK_AS_WINDOW(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(obt_display
, dock
->frame
);
122 RrAppearanceFree(dock
->a_frame
);
123 window_remove(dock
->frame
);
124 stacking_remove(dock
);
127 void dock_add(Window win
, XWMHints
*wmhints
)
130 XWindowAttributes attrib
;
133 app
= g_new0(ObDockApp
, 1);
135 app
->icon_win
= (wmhints
->flags
& IconWindowHint
) ?
136 wmhints
->icon_window
: win
;
138 if (OBT_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(obt_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(obt_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(obt_display
, app
->win
, -1000, -1000);
174 XMapWindow(obt_display
, app
->win
);
176 XMapWindow(obt_display
, app
->icon_win
);
177 XSync(obt_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(obt_display
, app
->icon_win
, SetModeInsert
);
182 XSelectInput(obt_display
, app
->icon_win
, DOCKAPP_EVENT_MASK
);
184 dock_app_grab_button(app
, TRUE
);
186 ob_debug("Managed Dock App: 0x%lx (%s)\n", app
->icon_win
, app
->class);
189 void dock_remove_all(void)
191 while (dock
->dock_apps
)
192 dock_remove(dock
->dock_apps
->data
, TRUE
);
195 void dock_remove(ObDockApp
*app
, gboolean reparent
)
197 dock_app_grab_button(app
, FALSE
);
198 XSelectInput(obt_display
, app
->icon_win
, NoEventMask
);
199 /* remove the window from our save set */
200 XChangeSaveSet(obt_display
, app
->icon_win
, SetModeDelete
);
201 XSync(obt_display
, False
);
204 XReparentWindow(obt_display
, app
->icon_win
,
205 RootWindow(obt_display
, ob_screen
), app
->x
, app
->y
);
207 dock
->dock_apps
= g_list_remove(dock
->dock_apps
, app
);
210 ob_debug("Unmanaged Dock App: 0x%lx (%s)\n", app
->icon_win
, app
->class);
217 void dock_configure(void)
227 RrMargins(dock
->a_frame
, &l
, &t
, &r
, &b
);
228 hidesize
= MAX(1, ob_rr_theme
->obwidth
);
230 dock
->area
.width
= dock
->area
.height
= 0;
233 for (it
= dock
->dock_apps
; it
; it
= g_list_next(it
)) {
234 ObDockApp
*app
= it
->data
;
235 switch (config_dock_orient
) {
236 case OB_ORIENTATION_HORZ
:
237 dock
->area
.width
+= app
->w
;
238 dock
->area
.height
= MAX(dock
->area
.height
, app
->h
);
240 case OB_ORIENTATION_VERT
:
241 dock
->area
.width
= MAX(dock
->area
.width
, app
->w
);
242 dock
->area
.height
+= app
->h
;
247 dock
->area
.width
+= l
+ r
;
248 dock
->area
.height
+= t
+ b
;
253 /* position the apps */
254 for (it
= dock
->dock_apps
; it
; it
= g_list_next(it
)) {
255 ObDockApp
*app
= it
->data
;
256 switch (config_dock_orient
) {
257 case OB_ORIENTATION_HORZ
:
259 app
->y
= (dock
->area
.height
- app
->h
) / 2;
262 case OB_ORIENTATION_VERT
:
263 app
->x
= (dock
->area
.width
- app
->w
) / 2;
269 XMoveWindow(obt_display
, app
->icon_win
, app
->x
, app
->y
);
272 /* used for calculating offsets */
273 dock
->area
.width
+= ob_rr_theme
->obwidth
* 2;
274 dock
->area
.height
+= ob_rr_theme
->obwidth
* 2;
276 a
= screen_physical_area_all_monitors();
278 /* calculate position */
279 if (config_dock_floating
) {
280 dock
->area
.x
= config_dock_x
;
281 dock
->area
.y
= config_dock_y
;
282 gravity
= NorthWestGravity
;
284 switch (config_dock_pos
) {
285 case OB_DIRECTION_NORTHWEST
:
288 gravity
= NorthWestGravity
;
290 case OB_DIRECTION_NORTH
:
291 dock
->area
.x
= a
->width
/ 2;
293 gravity
= NorthGravity
;
295 case OB_DIRECTION_NORTHEAST
:
296 dock
->area
.x
= a
->width
;
298 gravity
= NorthEastGravity
;
300 case OB_DIRECTION_WEST
:
302 dock
->area
.y
= a
->height
/ 2;
303 gravity
= WestGravity
;
305 case OB_DIRECTION_EAST
:
306 dock
->area
.x
= a
->width
;
307 dock
->area
.y
= a
->height
/ 2;
308 gravity
= EastGravity
;
310 case OB_DIRECTION_SOUTHWEST
:
312 dock
->area
.y
= a
->height
;
313 gravity
= SouthWestGravity
;
315 case OB_DIRECTION_SOUTH
:
316 dock
->area
.x
= a
->width
/ 2;
317 dock
->area
.y
= a
->height
;
318 gravity
= SouthGravity
;
320 case OB_DIRECTION_SOUTHEAST
:
321 dock
->area
.x
= a
->width
;
322 dock
->area
.y
= a
->height
;
323 gravity
= SouthEastGravity
;
326 g_assert_not_reached();
334 dock
->area
.x
-= dock
->area
.width
/ 2;
336 case NorthEastGravity
:
338 case SouthEastGravity
:
339 dock
->area
.x
-= dock
->area
.width
;
346 dock
->area
.y
-= dock
->area
.height
/ 2;
348 case SouthWestGravity
:
350 case SouthEastGravity
:
351 dock
->area
.y
-= dock
->area
.height
;
355 if (config_dock_hide
&& dock
->hidden
) {
356 if (!config_dock_floating
) {
357 switch (config_dock_pos
) {
358 case OB_DIRECTION_NORTHWEST
:
359 switch (config_dock_orient
) {
360 case OB_ORIENTATION_HORZ
:
361 dock
->area
.y
-= dock
->area
.height
- hidesize
;
363 case OB_ORIENTATION_VERT
:
364 dock
->area
.x
-= dock
->area
.width
- hidesize
;
368 case OB_DIRECTION_NORTH
:
369 dock
->area
.y
-= dock
->area
.height
- hidesize
;
371 case OB_DIRECTION_NORTHEAST
:
372 switch (config_dock_orient
) {
373 case OB_ORIENTATION_HORZ
:
374 dock
->area
.y
-= dock
->area
.height
- hidesize
;
376 case OB_ORIENTATION_VERT
:
377 dock
->area
.x
+= dock
->area
.width
- hidesize
;
381 case OB_DIRECTION_WEST
:
382 dock
->area
.x
-= dock
->area
.width
- hidesize
;
384 case OB_DIRECTION_EAST
:
385 dock
->area
.x
+= dock
->area
.width
- hidesize
;
387 case OB_DIRECTION_SOUTHWEST
:
388 switch (config_dock_orient
) {
389 case OB_ORIENTATION_HORZ
:
390 dock
->area
.y
+= dock
->area
.height
- hidesize
;
392 case OB_ORIENTATION_VERT
:
393 dock
->area
.x
-= dock
->area
.width
- hidesize
;
396 case OB_DIRECTION_SOUTH
:
397 dock
->area
.y
+= dock
->area
.height
- hidesize
;
399 case OB_DIRECTION_SOUTHEAST
:
400 switch (config_dock_orient
) {
401 case OB_ORIENTATION_HORZ
:
402 dock
->area
.y
+= dock
->area
.height
- hidesize
;
404 case OB_ORIENTATION_VERT
:
405 dock
->area
.x
+= dock
->area
.width
- hidesize
;
413 if (!config_dock_floating
&& config_dock_hide
) {
417 strw
= dock
->area
.width
;
418 strh
= dock
->area
.height
;
422 if (!dock
->dock_apps
) {
423 STRUT_PARTIAL_SET(dock_strut
, 0, 0, 0, 0,
424 0, 0, 0, 0, 0, 0, 0, 0);
425 } else if (config_dock_floating
|| config_dock_nostrut
)
427 STRUT_PARTIAL_SET(dock_strut
, 0, 0, 0, 0,
428 0, 0, 0, 0, 0, 0, 0, 0);
430 switch (config_dock_pos
) {
431 case OB_DIRECTION_NORTHWEST
:
432 switch (config_dock_orient
) {
433 case OB_ORIENTATION_HORZ
:
434 STRUT_PARTIAL_SET(dock_strut
, 0, strh
, 0, 0,
435 0, 0, dock
->area
.x
, dock
->area
.x
436 + dock
->area
.width
- 1, 0, 0, 0, 0);
438 case OB_ORIENTATION_VERT
:
439 STRUT_PARTIAL_SET(dock_strut
, strw
, 0, 0, 0,
440 dock
->area
.y
, dock
->area
.y
441 + dock
->area
.height
- 1, 0, 0, 0, 0, 0, 0);
445 case OB_DIRECTION_NORTH
:
446 STRUT_PARTIAL_SET(dock_strut
, 0, strh
, 0, 0,
447 0, 0, dock
->area
.x
, dock
->area
.x
448 + dock
->area
.width
- 1, 0, 0, 0, 0);
450 case OB_DIRECTION_NORTHEAST
:
451 switch (config_dock_orient
) {
452 case OB_ORIENTATION_HORZ
:
453 STRUT_PARTIAL_SET(dock_strut
, 0, strh
, 0, 0,
454 0, 0, dock
->area
.x
, dock
->area
.x
455 + dock
->area
.width
-1, 0, 0, 0, 0);
457 case OB_ORIENTATION_VERT
:
458 STRUT_PARTIAL_SET(dock_strut
, 0, 0, strw
, 0,
459 0, 0, 0, 0, dock
->area
.y
, dock
->area
.y
460 + dock
->area
.height
- 1, 0, 0);
464 case OB_DIRECTION_WEST
:
465 STRUT_PARTIAL_SET(dock_strut
, strw
, 0, 0, 0,
466 dock
->area
.y
, dock
->area
.y
467 + dock
->area
.height
- 1, 0, 0, 0, 0, 0, 0);
469 case OB_DIRECTION_EAST
:
470 STRUT_PARTIAL_SET(dock_strut
, 0, 0, strw
, 0,
471 0, 0, 0, 0, dock
->area
.y
, dock
->area
.y
472 + dock
->area
.height
- 1, 0, 0);
474 case OB_DIRECTION_SOUTHWEST
:
475 switch (config_dock_orient
) {
476 case OB_ORIENTATION_HORZ
:
477 STRUT_PARTIAL_SET(dock_strut
, 0, 0, 0, strh
,
478 0, 0, 0, 0, 0, 0, dock
->area
.x
, dock
->area
.x
479 + dock
->area
.width
- 1);
481 case OB_ORIENTATION_VERT
:
482 STRUT_PARTIAL_SET(dock_strut
, strw
, 0, 0, 0,
483 dock
->area
.y
, dock
->area
.y
484 + dock
->area
.height
- 1, 0, 0, 0, 0, 0, 0);
488 case OB_DIRECTION_SOUTH
:
489 STRUT_PARTIAL_SET(dock_strut
, 0, 0, 0, strh
,
490 0, 0, 0, 0, 0, 0, dock
->area
.x
, dock
->area
.x
491 + dock
->area
.width
- 1);
493 case OB_DIRECTION_SOUTHEAST
:
494 switch (config_dock_orient
) {
495 case OB_ORIENTATION_HORZ
:
496 STRUT_PARTIAL_SET(dock_strut
, 0, 0, 0, strh
,
497 0, 0, 0, 0, 0, 0, dock
->area
.x
,
498 dock
->area
.x
+ dock
->area
.width
- 1);
500 case OB_ORIENTATION_VERT
:
501 STRUT_PARTIAL_SET(dock_strut
, 0, 0, strw
, 0,
502 0, 0, 0, 0, dock
->area
.y
, dock
->area
.y
503 + dock
->area
.height
- 1, 0, 0);
510 /* not used for actually sizing shit */
511 dock
->area
.width
-= ob_rr_theme
->obwidth
* 2;
512 dock
->area
.height
-= ob_rr_theme
->obwidth
* 2;
514 if (dock
->dock_apps
) {
515 g_assert(dock
->area
.width
> 0);
516 g_assert(dock
->area
.height
> 0);
518 XMoveResizeWindow(obt_display
, dock
->frame
, dock
->area
.x
, dock
->area
.y
,
519 dock
->area
.width
, dock
->area
.height
);
521 RrPaint(dock
->a_frame
, dock
->frame
, dock
->area
.width
,
523 XMapWindow(obt_display
, dock
->frame
);
525 XUnmapWindow(obt_display
, dock
->frame
);
527 /* but they are useful outside of this function! */
528 dock
->area
.width
+= ob_rr_theme
->obwidth
* 2;
529 dock
->area
.height
+= ob_rr_theme
->obwidth
* 2;
531 screen_update_areas();
536 void dock_app_configure(ObDockApp
*app
, gint w
, gint h
)
543 void dock_app_drag(ObDockApp
*app
, XMotionEvent
*e
)
545 ObDockApp
*over
= NULL
;
554 /* are we on top of the dock? */
555 if (!(x
>= dock
->area
.x
&&
557 x
< dock
->area
.x
+ dock
->area
.width
&&
558 y
< dock
->area
.y
+ dock
->area
.height
))
564 /* which dock app are we on top of? */
566 for (it
= dock
->dock_apps
; it
; it
= g_list_next(it
)) {
568 switch (config_dock_orient
) {
569 case OB_ORIENTATION_HORZ
:
570 if (x
>= over
->x
&& x
< over
->x
+ over
->w
)
573 case OB_ORIENTATION_VERT
:
574 if (y
>= over
->y
&& y
< over
->y
+ over
->h
)
578 /* dont go to it->next! */
581 if (!it
|| app
== over
) return;
586 switch (config_dock_orient
) {
587 case OB_ORIENTATION_HORZ
:
588 after
= (x
> over
->w
/ 2);
590 case OB_ORIENTATION_VERT
:
591 after
= (y
> over
->h
/ 2);
594 g_assert_not_reached();
597 /* remove before doing the it->next! */
598 dock
->dock_apps
= g_list_remove(dock
->dock_apps
, app
);
600 if (after
) it
= it
->next
;
602 dock
->dock_apps
= g_list_insert_before(dock
->dock_apps
, it
, app
);
606 static gboolean
hide_timeout(gpointer data
)
612 return FALSE
; /* don't repeat */
615 static gboolean
show_timeout(gpointer data
)
618 dock
->hidden
= FALSE
;
621 return FALSE
; /* don't repeat */
624 void dock_hide(gboolean hide
)
627 if (dock
->hidden
&& config_dock_hide
) {
628 obt_main_loop_timeout_add(ob_main_loop
,
629 config_dock_show_delay
* 1000,
631 g_direct_equal
, NULL
);
632 } else if (!dock
->hidden
&& config_dock_hide
) {
633 obt_main_loop_timeout_remove(ob_main_loop
, hide_timeout
);
636 if (!dock
->hidden
&& config_dock_hide
) {
637 obt_main_loop_timeout_add(ob_main_loop
,
638 config_dock_hide_delay
* 1000,
640 g_direct_equal
, NULL
);
641 } else if (dock
->hidden
&& config_dock_hide
) {
642 obt_main_loop_timeout_remove(ob_main_loop
, show_timeout
);
647 void dock_get_area(Rect
*a
)
649 RECT_SET(*a
, dock
->area
.x
, dock
->area
.y
,
650 dock
->area
.width
, dock
->area
.height
);
653 ObDockApp
* dock_find_dockapp(Window xwin
)
655 g_assert(xwin
!= None
);
657 /* there are never that many dock apps, so we can use a list here instead
659 for (it
= dock
->dock_apps
; it
; it
= g_list_next(it
)) {
660 ObDockApp
*app
= it
->data
;
661 if (app
->icon_win
== xwin
)