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
, obt_root(ob_screen
),
90 RrDepth(ob_rr_inst
), InputOutput
,
92 CWOverrideRedirect
| CWEventMask
|
95 dock
->a_frame
= RrAppearanceCopy(ob_rr_theme
->osd_hilite_bg
);
96 XSetWindowBorder(obt_display
, dock
->frame
,
97 RrColorPixel(ob_rr_theme
->osd_border_color
));
98 XSetWindowBorderWidth(obt_display
, dock
->frame
, ob_rr_theme
->obwidth
);
100 /* Setting the window type so xcompmgr can tell what it is */
101 OBT_PROP_SET32(dock
->frame
, NET_WM_WINDOW_TYPE
, ATOM
,
102 OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_DOCK
));
104 window_add(&dock
->frame
, DOCK_AS_WINDOW(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(obt_display
, dock
->frame
);
121 RrAppearanceFree(dock
->a_frame
);
122 window_remove(dock
->frame
);
123 stacking_remove(dock
);
126 void dock_add(Window win
, XWMHints
*wmhints
)
129 XWindowAttributes attrib
;
132 app
= g_new0(ObDockApp
, 1);
134 app
->icon_win
= (wmhints
->flags
& IconWindowHint
) ?
135 wmhints
->icon_window
: win
;
137 if (OBT_PROP_GETSS(app
->win
, WM_CLASS
, locale
, &data
)) {
139 app
->name
= g_strdup(data
[0]);
141 app
->class = g_strdup(data
[1]);
146 if (app
->name
== NULL
) app
->name
= g_strdup("");
147 if (app
->class == NULL
) app
->class = g_strdup("");
149 if (XGetWindowAttributes(obt_display
, app
->icon_win
, &attrib
)) {
150 app
->w
= attrib
.width
;
151 app
->h
= attrib
.height
;
153 app
->w
= app
->h
= 64;
156 dock
->dock_apps
= g_list_append(dock
->dock_apps
, app
);
159 XReparentWindow(obt_display
, app
->icon_win
, dock
->frame
, app
->x
, app
->y
);
161 This is the same case as in frame.c for client windows. When Openbox is
162 starting, the window is already mapped so we see unmap events occur for
163 it. There are 2 unmap events generated that we see, one with the 'event'
164 member set the root window, and one set to the client, but both get
165 handled and need to be ignored.
167 if (ob_state() == OB_STATE_STARTING
)
168 app
->ignore_unmaps
+= 2;
170 if (app
->win
!= app
->icon_win
) {
171 /* have to map it so that it can be re-managed on a restart */
172 XMoveWindow(obt_display
, app
->win
, -1000, -1000);
173 XMapWindow(obt_display
, app
->win
);
175 XMapWindow(obt_display
, app
->icon_win
);
176 XSync(obt_display
, False
);
178 /* specify that if we exit, the window should not be destroyed and should
179 be reparented back to root automatically */
180 XChangeSaveSet(obt_display
, app
->icon_win
, SetModeInsert
);
181 XSelectInput(obt_display
, app
->icon_win
, DOCKAPP_EVENT_MASK
);
183 dock_app_grab_button(app
, TRUE
);
185 ob_debug("Managed Dock App: 0x%lx (%s)", app
->icon_win
, app
->class);
188 void dock_remove_all(void)
190 while (dock
->dock_apps
)
191 dock_remove(dock
->dock_apps
->data
, TRUE
);
194 void dock_remove(ObDockApp
*app
, gboolean reparent
)
196 dock_app_grab_button(app
, FALSE
);
197 XSelectInput(obt_display
, app
->icon_win
, NoEventMask
);
198 /* remove the window from our save set */
199 XChangeSaveSet(obt_display
, app
->icon_win
, SetModeDelete
);
200 XSync(obt_display
, False
);
203 XReparentWindow(obt_display
, app
->icon_win
,
204 obt_root(ob_screen
), app
->x
, app
->y
);
206 dock
->dock_apps
= g_list_remove(dock
->dock_apps
, app
);
209 ob_debug("Unmanaged Dock App: 0x%lx (%s)", app
->icon_win
, app
->class);
216 void dock_configure(void)
226 RrMargins(dock
->a_frame
, &l
, &t
, &r
, &b
);
227 hidesize
= MAX(1, ob_rr_theme
->obwidth
);
229 dock
->area
.width
= dock
->area
.height
= 0;
232 for (it
= dock
->dock_apps
; it
; it
= g_list_next(it
)) {
233 ObDockApp
*app
= it
->data
;
234 switch (config_dock_orient
) {
235 case OB_ORIENTATION_HORZ
:
236 dock
->area
.width
+= app
->w
;
237 dock
->area
.height
= MAX(dock
->area
.height
, app
->h
);
239 case OB_ORIENTATION_VERT
:
240 dock
->area
.width
= MAX(dock
->area
.width
, app
->w
);
241 dock
->area
.height
+= app
->h
;
246 if (dock
->dock_apps
) {
247 dock
->area
.width
+= l
+ r
;
248 dock
->area
.height
+= t
+ b
;
254 /* position the apps */
255 for (it
= dock
->dock_apps
; it
; it
= g_list_next(it
)) {
256 ObDockApp
*app
= it
->data
;
257 switch (config_dock_orient
) {
258 case OB_ORIENTATION_HORZ
:
260 app
->y
= (dock
->area
.height
- app
->h
) / 2;
263 case OB_ORIENTATION_VERT
:
264 app
->x
= (dock
->area
.width
- app
->w
) / 2;
270 XMoveWindow(obt_display
, app
->icon_win
, app
->x
, app
->y
);
273 /* used for calculating offsets */
274 dock
->area
.width
+= ob_rr_theme
->obwidth
* 2;
275 dock
->area
.height
+= ob_rr_theme
->obwidth
* 2;
277 a
= screen_physical_area_all_monitors();
279 /* calculate position */
280 if (config_dock_floating
) {
281 dock
->area
.x
= config_dock_x
;
282 dock
->area
.y
= config_dock_y
;
283 gravity
= NorthWestGravity
;
285 switch (config_dock_pos
) {
286 case OB_DIRECTION_NORTHWEST
:
289 gravity
= NorthWestGravity
;
291 case OB_DIRECTION_NORTH
:
292 dock
->area
.x
= a
->width
/ 2;
294 gravity
= NorthGravity
;
296 case OB_DIRECTION_NORTHEAST
:
297 dock
->area
.x
= a
->width
;
299 gravity
= NorthEastGravity
;
301 case OB_DIRECTION_WEST
:
303 dock
->area
.y
= a
->height
/ 2;
304 gravity
= WestGravity
;
306 case OB_DIRECTION_EAST
:
307 dock
->area
.x
= a
->width
;
308 dock
->area
.y
= a
->height
/ 2;
309 gravity
= EastGravity
;
311 case OB_DIRECTION_SOUTHWEST
:
313 dock
->area
.y
= a
->height
;
314 gravity
= SouthWestGravity
;
316 case OB_DIRECTION_SOUTH
:
317 dock
->area
.x
= a
->width
/ 2;
318 dock
->area
.y
= a
->height
;
319 gravity
= SouthGravity
;
321 case OB_DIRECTION_SOUTHEAST
:
322 dock
->area
.x
= a
->width
;
323 dock
->area
.y
= a
->height
;
324 gravity
= SouthEastGravity
;
327 g_assert_not_reached();
335 dock
->area
.x
-= dock
->area
.width
/ 2;
337 case NorthEastGravity
:
339 case SouthEastGravity
:
340 dock
->area
.x
-= dock
->area
.width
;
347 dock
->area
.y
-= dock
->area
.height
/ 2;
349 case SouthWestGravity
:
351 case SouthEastGravity
:
352 dock
->area
.y
-= dock
->area
.height
;
356 if (config_dock_hide
&& dock
->hidden
) {
357 if (!config_dock_floating
) {
358 switch (config_dock_pos
) {
359 case OB_DIRECTION_NORTHWEST
:
360 switch (config_dock_orient
) {
361 case OB_ORIENTATION_HORZ
:
362 dock
->area
.y
-= dock
->area
.height
- hidesize
;
364 case OB_ORIENTATION_VERT
:
365 dock
->area
.x
-= dock
->area
.width
- hidesize
;
369 case OB_DIRECTION_NORTH
:
370 dock
->area
.y
-= dock
->area
.height
- hidesize
;
372 case OB_DIRECTION_NORTHEAST
:
373 switch (config_dock_orient
) {
374 case OB_ORIENTATION_HORZ
:
375 dock
->area
.y
-= dock
->area
.height
- hidesize
;
377 case OB_ORIENTATION_VERT
:
378 dock
->area
.x
+= dock
->area
.width
- hidesize
;
382 case OB_DIRECTION_WEST
:
383 dock
->area
.x
-= dock
->area
.width
- hidesize
;
385 case OB_DIRECTION_EAST
:
386 dock
->area
.x
+= dock
->area
.width
- hidesize
;
388 case OB_DIRECTION_SOUTHWEST
:
389 switch (config_dock_orient
) {
390 case OB_ORIENTATION_HORZ
:
391 dock
->area
.y
+= dock
->area
.height
- hidesize
;
393 case OB_ORIENTATION_VERT
:
394 dock
->area
.x
-= dock
->area
.width
- hidesize
;
397 case OB_DIRECTION_SOUTH
:
398 dock
->area
.y
+= dock
->area
.height
- hidesize
;
400 case OB_DIRECTION_SOUTHEAST
:
401 switch (config_dock_orient
) {
402 case OB_ORIENTATION_HORZ
:
403 dock
->area
.y
+= dock
->area
.height
- hidesize
;
405 case OB_ORIENTATION_VERT
:
406 dock
->area
.x
+= dock
->area
.width
- hidesize
;
414 if (!config_dock_floating
&& config_dock_hide
) {
418 strw
= dock
->area
.width
;
419 strh
= dock
->area
.height
;
423 if (!dock
->dock_apps
) {
424 STRUT_PARTIAL_SET(dock_strut
, 0, 0, 0, 0,
425 0, 0, 0, 0, 0, 0, 0, 0);
427 else if (config_dock_floating
|| config_dock_nostrut
) {
428 STRUT_PARTIAL_SET(dock_strut
, 0, 0, 0, 0,
429 0, 0, 0, 0, 0, 0, 0, 0);
432 switch (config_dock_pos
) {
433 case OB_DIRECTION_NORTHWEST
:
434 switch (config_dock_orient
) {
435 case OB_ORIENTATION_HORZ
:
436 STRUT_PARTIAL_SET(dock_strut
, 0, strh
, 0, 0,
437 0, 0, dock
->area
.x
, dock
->area
.x
438 + dock
->area
.width
- 1, 0, 0, 0, 0);
440 case OB_ORIENTATION_VERT
:
441 STRUT_PARTIAL_SET(dock_strut
, strw
, 0, 0, 0,
442 dock
->area
.y
, dock
->area
.y
443 + dock
->area
.height
- 1, 0, 0, 0, 0, 0, 0);
447 case OB_DIRECTION_NORTH
:
448 STRUT_PARTIAL_SET(dock_strut
, 0, strh
, 0, 0,
449 0, 0, dock
->area
.x
, dock
->area
.x
450 + dock
->area
.width
- 1, 0, 0, 0, 0);
452 case OB_DIRECTION_NORTHEAST
:
453 switch (config_dock_orient
) {
454 case OB_ORIENTATION_HORZ
:
455 STRUT_PARTIAL_SET(dock_strut
, 0, strh
, 0, 0,
456 0, 0, dock
->area
.x
, dock
->area
.x
457 + dock
->area
.width
-1, 0, 0, 0, 0);
459 case OB_ORIENTATION_VERT
:
460 STRUT_PARTIAL_SET(dock_strut
, 0, 0, strw
, 0,
461 0, 0, 0, 0, dock
->area
.y
, dock
->area
.y
462 + dock
->area
.height
- 1, 0, 0);
466 case OB_DIRECTION_WEST
:
467 STRUT_PARTIAL_SET(dock_strut
, strw
, 0, 0, 0,
468 dock
->area
.y
, dock
->area
.y
469 + dock
->area
.height
- 1, 0, 0, 0, 0, 0, 0);
471 case OB_DIRECTION_EAST
:
472 STRUT_PARTIAL_SET(dock_strut
, 0, 0, strw
, 0,
473 0, 0, 0, 0, dock
->area
.y
, dock
->area
.y
474 + dock
->area
.height
- 1, 0, 0);
476 case OB_DIRECTION_SOUTHWEST
:
477 switch (config_dock_orient
) {
478 case OB_ORIENTATION_HORZ
:
479 STRUT_PARTIAL_SET(dock_strut
, 0, 0, 0, strh
,
480 0, 0, 0, 0, 0, 0, dock
->area
.x
, dock
->area
.x
481 + dock
->area
.width
- 1);
483 case OB_ORIENTATION_VERT
:
484 STRUT_PARTIAL_SET(dock_strut
, strw
, 0, 0, 0,
485 dock
->area
.y
, dock
->area
.y
486 + dock
->area
.height
- 1, 0, 0, 0, 0, 0, 0);
490 case OB_DIRECTION_SOUTH
:
491 STRUT_PARTIAL_SET(dock_strut
, 0, 0, 0, strh
,
492 0, 0, 0, 0, 0, 0, dock
->area
.x
, dock
->area
.x
493 + dock
->area
.width
- 1);
495 case OB_DIRECTION_SOUTHEAST
:
496 switch (config_dock_orient
) {
497 case OB_ORIENTATION_HORZ
:
498 STRUT_PARTIAL_SET(dock_strut
, 0, 0, 0, strh
,
499 0, 0, 0, 0, 0, 0, dock
->area
.x
,
500 dock
->area
.x
+ dock
->area
.width
- 1);
502 case OB_ORIENTATION_VERT
:
503 STRUT_PARTIAL_SET(dock_strut
, 0, 0, strw
, 0,
504 0, 0, 0, 0, dock
->area
.y
, dock
->area
.y
505 + dock
->area
.height
- 1, 0, 0);
512 /* not used for actually sizing shit */
513 dock
->area
.width
-= ob_rr_theme
->obwidth
* 2;
514 dock
->area
.height
-= ob_rr_theme
->obwidth
* 2;
516 if (dock
->dock_apps
) {
517 g_assert(dock
->area
.width
> 0);
518 g_assert(dock
->area
.height
> 0);
520 XMoveResizeWindow(obt_display
, dock
->frame
, dock
->area
.x
, dock
->area
.y
,
521 dock
->area
.width
, dock
->area
.height
);
523 RrPaint(dock
->a_frame
, dock
->frame
, dock
->area
.width
,
525 XMapWindow(obt_display
, dock
->frame
);
527 XUnmapWindow(obt_display
, dock
->frame
);
529 /* but they are useful outside of this function! but don't add it if the
530 dock is actually not visible */
531 if (dock
->dock_apps
) {
532 dock
->area
.width
+= ob_rr_theme
->obwidth
* 2;
533 dock
->area
.height
+= ob_rr_theme
->obwidth
* 2;
536 screen_update_areas();
541 void dock_app_configure(ObDockApp
*app
, gint w
, gint h
)
548 void dock_app_drag(ObDockApp
*app
, XMotionEvent
*e
)
550 ObDockApp
*over
= NULL
;
559 /* are we on top of the dock? */
560 if (!(x
>= dock
->area
.x
&&
562 x
< dock
->area
.x
+ dock
->area
.width
&&
563 y
< dock
->area
.y
+ dock
->area
.height
))
569 /* which dock app are we on top of? */
571 for (it
= dock
->dock_apps
; it
; it
= g_list_next(it
)) {
573 switch (config_dock_orient
) {
574 case OB_ORIENTATION_HORZ
:
575 if (x
>= over
->x
&& x
< over
->x
+ over
->w
)
578 case OB_ORIENTATION_VERT
:
579 if (y
>= over
->y
&& y
< over
->y
+ over
->h
)
583 /* dont go to it->next! */
586 if (!it
|| app
== over
) return;
591 switch (config_dock_orient
) {
592 case OB_ORIENTATION_HORZ
:
593 after
= (x
> over
->w
/ 2);
595 case OB_ORIENTATION_VERT
:
596 after
= (y
> over
->h
/ 2);
599 g_assert_not_reached();
602 /* remove before doing the it->next! */
603 dock
->dock_apps
= g_list_remove(dock
->dock_apps
, app
);
605 if (after
) it
= it
->next
;
607 dock
->dock_apps
= g_list_insert_before(dock
->dock_apps
, it
, app
);
611 static gboolean
hide_timeout(gpointer data
)
617 return FALSE
; /* don't repeat */
620 static gboolean
show_timeout(gpointer data
)
623 dock
->hidden
= FALSE
;
626 return FALSE
; /* don't repeat */
629 void dock_hide(gboolean hide
)
632 if (dock
->hidden
&& config_dock_hide
) {
633 obt_main_loop_timeout_add(ob_main_loop
,
634 config_dock_show_delay
* 1000,
636 g_direct_equal
, NULL
);
637 } else if (!dock
->hidden
&& config_dock_hide
) {
638 obt_main_loop_timeout_remove(ob_main_loop
, hide_timeout
);
641 if (!dock
->hidden
&& config_dock_hide
) {
642 obt_main_loop_timeout_add(ob_main_loop
,
643 config_dock_hide_delay
* 1000,
645 g_direct_equal
, NULL
);
646 } else if (dock
->hidden
&& config_dock_hide
) {
647 obt_main_loop_timeout_remove(ob_main_loop
, show_timeout
);
652 void dock_get_area(Rect
*a
)
654 RECT_SET(*a
, dock
->area
.x
, dock
->area
.y
,
655 dock
->area
.width
, dock
->area
.height
);
658 ObDockApp
* dock_find_dockapp(Window xwin
)
661 /* there are never that many dock apps, so we can use a list here instead
663 for (it
= dock
->dock_apps
; it
; it
= g_list_next(it
)) {
664 ObDockApp
*app
= it
->data
;
665 if (app
->icon_win
== xwin
)