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
->osd_border_color
));
61 XSetWindowBorderWidth(ob_display
, dock
->frame
, ob_rr_theme
->obwidth
);
63 RrAppearanceFree(dock
->a_frame
);
64 dock
->a_frame
= RrAppearanceCopy(ob_rr_theme
->osd_hilite_bg
);
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
->osd_hilite_bg
);
93 XSetWindowBorder(ob_display
, dock
->frame
,
94 RrColorPixel(ob_rr_theme
->osd_border_color
));
95 XSetWindowBorderWidth(ob_display
, dock
->frame
, ob_rr_theme
->obwidth
);
97 /* Setting the window type so xcompmgr can tell what it is */
98 PROP_SET32(dock
->frame
, net_wm_window_type
, atom
,
99 prop_atoms
.net_wm_window_type_dock
);
101 g_hash_table_insert(window_map
, &dock
->frame
, dock
);
102 stacking_add(DOCK_AS_WINDOW(dock
));
105 void dock_shutdown(gboolean reconfig
)
110 stacking_remove(DOCK_AS_WINDOW(dock
));
112 for (it
= dock
->dock_apps
; it
; it
= g_list_next(it
))
113 dock_app_grab_button(it
->data
, FALSE
);
117 XDestroyWindow(ob_display
, dock
->frame
);
118 RrAppearanceFree(dock
->a_frame
);
119 g_hash_table_remove(window_map
, &dock
->frame
);
120 stacking_remove(dock
);
123 void dock_add(Window win
, XWMHints
*wmhints
)
126 XWindowAttributes attrib
;
129 app
= g_new0(ObDockApp
, 1);
130 app
->obwin
.type
= Window_DockApp
;
132 app
->icon_win
= (wmhints
->flags
& IconWindowHint
) ?
133 wmhints
->icon_window
: win
;
135 if (PROP_GETSS(app
->win
, wm_class
, locale
, &data
)) {
137 app
->name
= g_strdup(data
[0]);
139 app
->class = g_strdup(data
[1]);
144 if (app
->name
== NULL
) app
->name
= g_strdup("");
145 if (app
->class == NULL
) app
->class = g_strdup("");
147 if (XGetWindowAttributes(ob_display
, app
->icon_win
, &attrib
)) {
148 app
->w
= attrib
.width
;
149 app
->h
= attrib
.height
;
151 app
->w
= app
->h
= 64;
154 dock
->dock_apps
= g_list_append(dock
->dock_apps
, app
);
157 XReparentWindow(ob_display
, app
->icon_win
, dock
->frame
, app
->x
, app
->y
);
159 This is the same case as in frame.c for client windows. When Openbox is
160 starting, the window is already mapped so we see unmap events occur for
161 it. There are 2 unmap events generated that we see, one with the 'event'
162 member set the root window, and one set to the client, but both get
163 handled and need to be ignored.
165 if (ob_state() == OB_STATE_STARTING
)
166 app
->ignore_unmaps
+= 2;
168 if (app
->win
!= app
->icon_win
) {
169 /* have to map it so that it can be re-managed on a restart */
170 XMoveWindow(ob_display
, app
->win
, -1000, -1000);
171 XMapWindow(ob_display
, app
->win
);
173 XMapWindow(ob_display
, app
->icon_win
);
174 XSync(ob_display
, False
);
176 /* specify that if we exit, the window should not be destroyed and should
177 be reparented back to root automatically */
178 XChangeSaveSet(ob_display
, app
->icon_win
, SetModeInsert
);
179 XSelectInput(ob_display
, app
->icon_win
, DOCKAPP_EVENT_MASK
);
181 dock_app_grab_button(app
, TRUE
);
183 g_hash_table_insert(window_map
, &app
->icon_win
, app
);
185 ob_debug("Managed Dock App: 0x%lx (%s)\n", app
->icon_win
, app
->class);
188 void dock_remove_all()
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(ob_display
, app
->icon_win
, NoEventMask
);
198 /* remove the window from our save set */
199 XChangeSaveSet(ob_display
, app
->icon_win
, SetModeDelete
);
200 XSync(ob_display
, False
);
202 g_hash_table_remove(window_map
, &app
->icon_win
);
205 XReparentWindow(ob_display
, app
->icon_win
,
206 RootWindow(ob_display
, ob_screen
), app
->x
, app
->y
);
208 dock
->dock_apps
= g_list_remove(dock
->dock_apps
, app
);
211 ob_debug("Unmanaged Dock App: 0x%lx (%s)\n", app
->icon_win
, app
->class);
218 void dock_configure()
227 RrMargins(dock
->a_frame
, &l
, &t
, &r
, &b
);
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 dock
->area
.width
+= l
+ r
;
247 dock
->area
.height
+= t
+ b
;
252 /* position the apps */
253 for (it
= dock
->dock_apps
; it
; it
= g_list_next(it
)) {
254 ObDockApp
*app
= it
->data
;
255 switch (config_dock_orient
) {
256 case OB_ORIENTATION_HORZ
:
258 app
->y
= (dock
->area
.height
- app
->h
) / 2;
261 case OB_ORIENTATION_VERT
:
262 app
->x
= (dock
->area
.width
- app
->w
) / 2;
268 XMoveWindow(ob_display
, app
->icon_win
, app
->x
, app
->y
);
271 /* used for calculating offsets */
272 dock
->area
.width
+= ob_rr_theme
->obwidth
* 2;
273 dock
->area
.height
+= ob_rr_theme
->obwidth
* 2;
275 a
= screen_physical_area_all_monitors();
277 /* calculate position */
278 if (config_dock_floating
) {
279 dock
->area
.x
= config_dock_x
;
280 dock
->area
.y
= config_dock_y
;
281 gravity
= NorthWestGravity
;
283 switch (config_dock_pos
) {
284 case OB_DIRECTION_NORTHWEST
:
287 gravity
= NorthWestGravity
;
289 case OB_DIRECTION_NORTH
:
290 dock
->area
.x
= a
->width
/ 2;
292 gravity
= NorthGravity
;
294 case OB_DIRECTION_NORTHEAST
:
295 dock
->area
.x
= a
->width
;
297 gravity
= NorthEastGravity
;
299 case OB_DIRECTION_WEST
:
301 dock
->area
.y
= a
->height
/ 2;
302 gravity
= WestGravity
;
304 case OB_DIRECTION_EAST
:
305 dock
->area
.x
= a
->width
;
306 dock
->area
.y
= a
->height
/ 2;
307 gravity
= EastGravity
;
309 case OB_DIRECTION_SOUTHWEST
:
311 dock
->area
.y
= a
->height
;
312 gravity
= SouthWestGravity
;
314 case OB_DIRECTION_SOUTH
:
315 dock
->area
.x
= a
->width
/ 2;
316 dock
->area
.y
= a
->height
;
317 gravity
= SouthGravity
;
319 case OB_DIRECTION_SOUTHEAST
:
320 dock
->area
.x
= a
->width
;
321 dock
->area
.y
= a
->height
;
322 gravity
= SouthEastGravity
;
325 g_assert_not_reached();
333 dock
->area
.x
-= dock
->area
.width
/ 2;
335 case NorthEastGravity
:
337 case SouthEastGravity
:
338 dock
->area
.x
-= dock
->area
.width
;
345 dock
->area
.y
-= dock
->area
.height
/ 2;
347 case SouthWestGravity
:
349 case SouthEastGravity
:
350 dock
->area
.y
-= dock
->area
.height
;
354 if (config_dock_hide
&& dock
->hidden
) {
355 if (!config_dock_floating
) {
356 switch (config_dock_pos
) {
357 case OB_DIRECTION_NORTHWEST
:
358 switch (config_dock_orient
) {
359 case OB_ORIENTATION_HORZ
:
360 dock
->area
.y
-= dock
->area
.height
- ob_rr_theme
->obwidth
;
362 case OB_ORIENTATION_VERT
:
363 dock
->area
.x
-= dock
->area
.width
- ob_rr_theme
->obwidth
;
367 case OB_DIRECTION_NORTH
:
368 dock
->area
.y
-= dock
->area
.height
- ob_rr_theme
->obwidth
;
370 case OB_DIRECTION_NORTHEAST
:
371 switch (config_dock_orient
) {
372 case OB_ORIENTATION_HORZ
:
373 dock
->area
.y
-= dock
->area
.height
- ob_rr_theme
->obwidth
;
375 case OB_ORIENTATION_VERT
:
376 dock
->area
.x
+= dock
->area
.width
- ob_rr_theme
->obwidth
;
380 case OB_DIRECTION_WEST
:
381 dock
->area
.x
-= dock
->area
.width
- ob_rr_theme
->obwidth
;
383 case OB_DIRECTION_EAST
:
384 dock
->area
.x
+= dock
->area
.width
- ob_rr_theme
->obwidth
;
386 case OB_DIRECTION_SOUTHWEST
:
387 switch (config_dock_orient
) {
388 case OB_ORIENTATION_HORZ
:
389 dock
->area
.y
+= dock
->area
.height
- ob_rr_theme
->obwidth
;
391 case OB_ORIENTATION_VERT
:
392 dock
->area
.x
-= dock
->area
.width
- ob_rr_theme
->obwidth
;
395 case OB_DIRECTION_SOUTH
:
396 dock
->area
.y
+= dock
->area
.height
- ob_rr_theme
->obwidth
;
398 case OB_DIRECTION_SOUTHEAST
:
399 switch (config_dock_orient
) {
400 case OB_ORIENTATION_HORZ
:
401 dock
->area
.y
+= dock
->area
.height
- ob_rr_theme
->obwidth
;
403 case OB_ORIENTATION_VERT
:
404 dock
->area
.x
+= dock
->area
.width
- ob_rr_theme
->obwidth
;
412 if (!config_dock_floating
&& config_dock_hide
) {
413 strw
= ob_rr_theme
->obwidth
;
414 strh
= ob_rr_theme
->obwidth
;
416 strw
= dock
->area
.width
;
417 strh
= dock
->area
.height
;
421 if (!dock
->dock_apps
) {
422 STRUT_PARTIAL_SET(dock_strut
, 0, 0, 0, 0,
423 0, 0, 0, 0, 0, 0, 0, 0);
424 } else if (config_dock_floating
|| config_dock_nostrut
)
426 STRUT_PARTIAL_SET(dock_strut
, 0, 0, 0, 0,
427 0, 0, 0, 0, 0, 0, 0, 0);
429 switch (config_dock_pos
) {
430 case OB_DIRECTION_NORTHWEST
:
431 switch (config_dock_orient
) {
432 case OB_ORIENTATION_HORZ
:
433 STRUT_PARTIAL_SET(dock_strut
, 0, strh
, 0, 0,
434 0, 0, dock
->area
.x
, dock
->area
.x
+ dock
->area
.width
- 1,
437 case OB_ORIENTATION_VERT
:
438 STRUT_PARTIAL_SET(dock_strut
, strw
, 0, 0, 0,
439 dock
->area
.y
, dock
->area
.y
+ dock
->area
.height
- 1,
444 case OB_DIRECTION_NORTH
:
445 STRUT_PARTIAL_SET(dock_strut
, 0, strh
, 0, 0,
446 0, 0, dock
->area
.x
, dock
->area
.x
+ dock
->area
.width
- 1,
449 case OB_DIRECTION_NORTHEAST
:
450 switch (config_dock_orient
) {
451 case OB_ORIENTATION_HORZ
:
452 STRUT_PARTIAL_SET(dock_strut
, 0, strh
, 0, 0,
453 0, 0, dock
->area
.x
, dock
->area
.x
+ dock
->area
.width
-1,
456 case OB_ORIENTATION_VERT
:
457 STRUT_PARTIAL_SET(dock_strut
, 0, 0, strw
, 0,
459 dock
->area
.y
, dock
->area
.y
+ dock
->area
.height
- 1, 0, 0);
463 case OB_DIRECTION_WEST
:
464 STRUT_PARTIAL_SET(dock_strut
, strw
, 0, 0, 0,
465 dock
->area
.y
, dock
->area
.y
+ dock
->area
.height
- 1,
468 case OB_DIRECTION_EAST
:
469 STRUT_PARTIAL_SET(dock_strut
, 0, 0, strw
, 0,
471 dock
->area
.y
, dock
->area
.y
+ dock
->area
.height
- 1, 0, 0);
473 case OB_DIRECTION_SOUTHWEST
:
474 switch (config_dock_orient
) {
475 case OB_ORIENTATION_HORZ
:
476 STRUT_PARTIAL_SET(dock_strut
, 0, 0, 0, strh
,
478 dock
->area
.x
, dock
->area
.x
+ dock
->area
.width
- 1);
480 case OB_ORIENTATION_VERT
:
481 STRUT_PARTIAL_SET(dock_strut
, strw
, 0, 0, 0,
482 dock
->area
.y
, dock
->area
.y
+ dock
->area
.height
- 1,
487 case OB_DIRECTION_SOUTH
:
488 STRUT_PARTIAL_SET(dock_strut
, 0, 0, 0, strh
,
490 dock
->area
.x
, dock
->area
.x
+ dock
->area
.width
- 1);
492 case OB_DIRECTION_SOUTHEAST
:
493 switch (config_dock_orient
) {
494 case OB_ORIENTATION_HORZ
:
495 STRUT_PARTIAL_SET(dock_strut
, 0, 0, 0, strh
,
497 dock
->area
.x
, dock
->area
.x
+ dock
->area
.width
- 1);
499 case OB_ORIENTATION_VERT
:
500 STRUT_PARTIAL_SET(dock_strut
, 0, 0, strw
, 0,
502 dock
->area
.y
, dock
->area
.y
+ dock
->area
.height
- 1, 0, 0);
509 /* not used for actually sizing shit */
510 dock
->area
.width
-= ob_rr_theme
->obwidth
* 2;
511 dock
->area
.height
-= ob_rr_theme
->obwidth
* 2;
513 if (dock
->dock_apps
) {
514 g_assert(dock
->area
.width
> 0);
515 g_assert(dock
->area
.height
> 0);
517 XMoveResizeWindow(ob_display
, dock
->frame
,
518 dock
->area
.x
, dock
->area
.y
, dock
->area
.width
, dock
->area
.height
);
520 RrPaint(dock
->a_frame
, dock
->frame
, dock
->area
.width
, dock
->area
.height
);
521 XMapWindow(ob_display
, dock
->frame
);
523 XUnmapWindow(ob_display
, dock
->frame
);
525 /* but they are useful outside of this function! */
526 dock
->area
.width
+= ob_rr_theme
->obwidth
* 2;
527 dock
->area
.height
+= ob_rr_theme
->obwidth
* 2;
529 screen_update_areas();
534 void dock_app_configure(ObDockApp
*app
, gint w
, gint h
)
541 void dock_app_drag(ObDockApp
*app
, XMotionEvent
*e
)
543 ObDockApp
*over
= NULL
;
552 /* are we on top of the dock? */
553 if (!(x
>= dock
->area
.x
&&
555 x
< dock
->area
.x
+ dock
->area
.width
&&
556 y
< dock
->area
.y
+ dock
->area
.height
))
562 /* which dock app are we on top of? */
564 for (it
= dock
->dock_apps
; it
; it
= g_list_next(it
)) {
566 switch (config_dock_orient
) {
567 case OB_ORIENTATION_HORZ
:
568 if (x
>= over
->x
&& x
< over
->x
+ over
->w
)
571 case OB_ORIENTATION_VERT
:
572 if (y
>= over
->y
&& y
< over
->y
+ over
->h
)
576 /* dont go to it->next! */
579 if (!it
|| app
== over
) return;
584 switch (config_dock_orient
) {
585 case OB_ORIENTATION_HORZ
:
586 after
= (x
> over
->w
/ 2);
588 case OB_ORIENTATION_VERT
:
589 after
= (y
> over
->h
/ 2);
592 g_assert_not_reached();
595 /* remove before doing the it->next! */
596 dock
->dock_apps
= g_list_remove(dock
->dock_apps
, app
);
598 if (after
) it
= it
->next
;
600 dock
->dock_apps
= g_list_insert_before(dock
->dock_apps
, it
, app
);
604 static gboolean
hide_timeout(gpointer data
)
610 return FALSE
; /* don't repeat */
613 static gboolean
show_timeout(gpointer data
)
616 dock
->hidden
= FALSE
;
619 return FALSE
; /* don't repeat */
622 void dock_hide(gboolean hide
)
625 if (dock
->hidden
&& config_dock_hide
) {
626 ob_main_loop_timeout_add(ob_main_loop
, config_dock_show_delay
,
627 show_timeout
, NULL
, g_direct_equal
, NULL
);
628 } else if (!dock
->hidden
&& config_dock_hide
) {
629 ob_main_loop_timeout_remove(ob_main_loop
, hide_timeout
);
632 if (!dock
->hidden
&& config_dock_hide
) {
633 ob_main_loop_timeout_add(ob_main_loop
, config_dock_hide_delay
,
634 hide_timeout
, NULL
, g_direct_equal
, NULL
);
635 } else if (dock
->hidden
&& config_dock_hide
) {
636 ob_main_loop_timeout_remove(ob_main_loop
, show_timeout
);
641 void dock_get_area(Rect
*a
)
643 RECT_SET(*a
, dock
->area
.x
, dock
->area
.y
, dock
->area
.width
, dock
->area
.height
);