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)
33 #define DOCK_NOPROPAGATEMASK (ButtonPressMask | ButtonReleaseMask | \
38 StrutPartial dock_strut
;
40 static void dock_app_grab_button(ObDockApp
*app
, gboolean grab
)
43 grab_button_full(config_dock_app_move_button
,
44 config_dock_app_move_modifiers
, app
->icon_win
,
45 ButtonPressMask
| ButtonReleaseMask
|
47 GrabModeAsync
, OB_CURSOR_MOVE
);
49 ungrab_button(config_dock_app_move_button
,
50 config_dock_app_move_modifiers
, app
->icon_win
);
54 void dock_startup(gboolean reconfig
)
56 XSetWindowAttributes attrib
;
61 XSetWindowBorder(ob_display
, dock
->frame
,
62 RrColorPixel(ob_rr_theme
->osd_border_color
));
63 XSetWindowBorderWidth(ob_display
, dock
->frame
, ob_rr_theme
->obwidth
);
65 RrAppearanceFree(dock
->a_frame
);
66 dock
->a_frame
= RrAppearanceCopy(ob_rr_theme
->osd_hilite_bg
);
68 stacking_add(DOCK_AS_WINDOW(dock
));
73 for (it
= dock
->dock_apps
; it
; it
= g_list_next(it
))
74 dock_app_grab_button(it
->data
, TRUE
);
78 STRUT_PARTIAL_SET(dock_strut
, 0, 0, 0, 0,
79 0, 0, 0, 0, 0, 0, 0, 0);
81 dock
= g_new0(ObDock
, 1);
82 dock
->obwin
.type
= Window_Dock
;
86 attrib
.event_mask
= DOCK_EVENT_MASK
;
87 attrib
.override_redirect
= True
;
88 attrib
.do_not_propagate_mask
= DOCK_NOPROPAGATEMASK
;
89 dock
->frame
= XCreateWindow(ob_display
, RootWindow(ob_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(ob_display
, dock
->frame
,
98 RrColorPixel(ob_rr_theme
->osd_border_color
));
99 XSetWindowBorderWidth(ob_display
, dock
->frame
, ob_rr_theme
->obwidth
);
101 /* Setting the window type so xcompmgr can tell what it is */
102 PROP_SET32(dock
->frame
, net_wm_window_type
, atom
,
103 prop_atoms
.net_wm_window_type_dock
);
105 g_hash_table_insert(window_map
, &dock
->frame
, 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(ob_display
, dock
->frame
);
122 RrAppearanceFree(dock
->a_frame
);
123 g_hash_table_remove(window_map
, &dock
->frame
);
124 stacking_remove(dock
);
127 void dock_add(Window win
, XWMHints
*wmhints
)
130 XWindowAttributes attrib
;
133 app
= g_new0(ObDockApp
, 1);
134 app
->obwin
.type
= Window_DockApp
;
136 app
->icon_win
= (wmhints
->flags
& IconWindowHint
) ?
137 wmhints
->icon_window
: win
;
139 if (PROP_GETSS(app
->win
, wm_class
, locale
, &data
)) {
141 app
->name
= g_strdup(data
[0]);
143 app
->class = g_strdup(data
[1]);
148 if (app
->name
== NULL
) app
->name
= g_strdup("");
149 if (app
->class == NULL
) app
->class = g_strdup("");
151 if (XGetWindowAttributes(ob_display
, app
->icon_win
, &attrib
)) {
152 app
->w
= attrib
.width
;
153 app
->h
= attrib
.height
;
155 app
->w
= app
->h
= 64;
158 dock
->dock_apps
= g_list_append(dock
->dock_apps
, app
);
161 XReparentWindow(ob_display
, app
->icon_win
, dock
->frame
, app
->x
, app
->y
);
163 This is the same case as in frame.c for client windows. When Openbox is
164 starting, the window is already mapped so we see unmap events occur for
165 it. There are 2 unmap events generated that we see, one with the 'event'
166 member set the root window, and one set to the client, but both get
167 handled and need to be ignored.
169 if (ob_state() == OB_STATE_STARTING
)
170 app
->ignore_unmaps
+= 2;
172 if (app
->win
!= app
->icon_win
) {
173 /* have to map it so that it can be re-managed on a restart */
174 XMoveWindow(ob_display
, app
->win
, -1000, -1000);
175 XMapWindow(ob_display
, app
->win
);
177 XMapWindow(ob_display
, app
->icon_win
);
178 XSync(ob_display
, False
);
180 /* specify that if we exit, the window should not be destroyed and should
181 be reparented back to root automatically */
182 XChangeSaveSet(ob_display
, app
->icon_win
, SetModeInsert
);
183 XSelectInput(ob_display
, app
->icon_win
, DOCKAPP_EVENT_MASK
);
185 dock_app_grab_button(app
, TRUE
);
187 g_hash_table_insert(window_map
, &app
->icon_win
, app
);
189 ob_debug("Managed Dock App: 0x%lx (%s)\n", app
->icon_win
, app
->class);
192 void dock_remove_all(void)
194 while (dock
->dock_apps
)
195 dock_remove(dock
->dock_apps
->data
, TRUE
);
198 void dock_remove(ObDockApp
*app
, gboolean reparent
)
200 dock_app_grab_button(app
, FALSE
);
201 XSelectInput(ob_display
, app
->icon_win
, NoEventMask
);
202 /* remove the window from our save set */
203 XChangeSaveSet(ob_display
, app
->icon_win
, SetModeDelete
);
204 XSync(ob_display
, False
);
206 g_hash_table_remove(window_map
, &app
->icon_win
);
209 XReparentWindow(ob_display
, app
->icon_win
,
210 RootWindow(ob_display
, ob_screen
), app
->x
, app
->y
);
212 dock
->dock_apps
= g_list_remove(dock
->dock_apps
, app
);
215 ob_debug("Unmanaged Dock App: 0x%lx (%s)\n", app
->icon_win
, app
->class);
222 void dock_configure(void)
231 RrMargins(dock
->a_frame
, &l
, &t
, &r
, &b
);
233 dock
->area
.width
= dock
->area
.height
= 0;
236 for (it
= dock
->dock_apps
; it
; it
= g_list_next(it
)) {
237 ObDockApp
*app
= it
->data
;
238 switch (config_dock_orient
) {
239 case OB_ORIENTATION_HORZ
:
240 dock
->area
.width
+= app
->w
;
241 dock
->area
.height
= MAX(dock
->area
.height
, app
->h
);
243 case OB_ORIENTATION_VERT
:
244 dock
->area
.width
= MAX(dock
->area
.width
, app
->w
);
245 dock
->area
.height
+= app
->h
;
250 dock
->area
.width
+= l
+ r
;
251 dock
->area
.height
+= t
+ b
;
256 /* position the apps */
257 for (it
= dock
->dock_apps
; it
; it
= g_list_next(it
)) {
258 ObDockApp
*app
= it
->data
;
259 switch (config_dock_orient
) {
260 case OB_ORIENTATION_HORZ
:
262 app
->y
= (dock
->area
.height
- app
->h
) / 2;
265 case OB_ORIENTATION_VERT
:
266 app
->x
= (dock
->area
.width
- app
->w
) / 2;
272 XMoveWindow(ob_display
, app
->icon_win
, app
->x
, app
->y
);
275 /* used for calculating offsets */
276 dock
->area
.width
+= ob_rr_theme
->obwidth
* 2;
277 dock
->area
.height
+= ob_rr_theme
->obwidth
* 2;
279 a
= screen_physical_area_all_monitors();
281 /* calculate position */
282 if (config_dock_floating
) {
283 dock
->area
.x
= config_dock_x
;
284 dock
->area
.y
= config_dock_y
;
285 gravity
= NorthWestGravity
;
287 switch (config_dock_pos
) {
288 case OB_DIRECTION_NORTHWEST
:
291 gravity
= NorthWestGravity
;
293 case OB_DIRECTION_NORTH
:
294 dock
->area
.x
= a
->width
/ 2;
296 gravity
= NorthGravity
;
298 case OB_DIRECTION_NORTHEAST
:
299 dock
->area
.x
= a
->width
;
301 gravity
= NorthEastGravity
;
303 case OB_DIRECTION_WEST
:
305 dock
->area
.y
= a
->height
/ 2;
306 gravity
= WestGravity
;
308 case OB_DIRECTION_EAST
:
309 dock
->area
.x
= a
->width
;
310 dock
->area
.y
= a
->height
/ 2;
311 gravity
= EastGravity
;
313 case OB_DIRECTION_SOUTHWEST
:
315 dock
->area
.y
= a
->height
;
316 gravity
= SouthWestGravity
;
318 case OB_DIRECTION_SOUTH
:
319 dock
->area
.x
= a
->width
/ 2;
320 dock
->area
.y
= a
->height
;
321 gravity
= SouthGravity
;
323 case OB_DIRECTION_SOUTHEAST
:
324 dock
->area
.x
= a
->width
;
325 dock
->area
.y
= a
->height
;
326 gravity
= SouthEastGravity
;
329 g_assert_not_reached();
337 dock
->area
.x
-= dock
->area
.width
/ 2;
339 case NorthEastGravity
:
341 case SouthEastGravity
:
342 dock
->area
.x
-= dock
->area
.width
;
349 dock
->area
.y
-= dock
->area
.height
/ 2;
351 case SouthWestGravity
:
353 case SouthEastGravity
:
354 dock
->area
.y
-= dock
->area
.height
;
358 if (config_dock_hide
&& dock
->hidden
) {
359 if (!config_dock_floating
) {
360 switch (config_dock_pos
) {
361 case OB_DIRECTION_NORTHWEST
:
362 switch (config_dock_orient
) {
363 case OB_ORIENTATION_HORZ
:
364 dock
->area
.y
-= dock
->area
.height
- ob_rr_theme
->obwidth
;
366 case OB_ORIENTATION_VERT
:
367 dock
->area
.x
-= dock
->area
.width
- ob_rr_theme
->obwidth
;
371 case OB_DIRECTION_NORTH
:
372 dock
->area
.y
-= dock
->area
.height
- ob_rr_theme
->obwidth
;
374 case OB_DIRECTION_NORTHEAST
:
375 switch (config_dock_orient
) {
376 case OB_ORIENTATION_HORZ
:
377 dock
->area
.y
-= dock
->area
.height
- ob_rr_theme
->obwidth
;
379 case OB_ORIENTATION_VERT
:
380 dock
->area
.x
+= dock
->area
.width
- ob_rr_theme
->obwidth
;
384 case OB_DIRECTION_WEST
:
385 dock
->area
.x
-= dock
->area
.width
- ob_rr_theme
->obwidth
;
387 case OB_DIRECTION_EAST
:
388 dock
->area
.x
+= dock
->area
.width
- ob_rr_theme
->obwidth
;
390 case OB_DIRECTION_SOUTHWEST
:
391 switch (config_dock_orient
) {
392 case OB_ORIENTATION_HORZ
:
393 dock
->area
.y
+= dock
->area
.height
- ob_rr_theme
->obwidth
;
395 case OB_ORIENTATION_VERT
:
396 dock
->area
.x
-= dock
->area
.width
- ob_rr_theme
->obwidth
;
399 case OB_DIRECTION_SOUTH
:
400 dock
->area
.y
+= dock
->area
.height
- ob_rr_theme
->obwidth
;
402 case OB_DIRECTION_SOUTHEAST
:
403 switch (config_dock_orient
) {
404 case OB_ORIENTATION_HORZ
:
405 dock
->area
.y
+= dock
->area
.height
- ob_rr_theme
->obwidth
;
407 case OB_ORIENTATION_VERT
:
408 dock
->area
.x
+= dock
->area
.width
- ob_rr_theme
->obwidth
;
416 if (!config_dock_floating
&& config_dock_hide
) {
417 strw
= ob_rr_theme
->obwidth
;
418 strh
= ob_rr_theme
->obwidth
;
420 strw
= dock
->area
.width
;
421 strh
= dock
->area
.height
;
425 if (!dock
->dock_apps
) {
426 STRUT_PARTIAL_SET(dock_strut
, 0, 0, 0, 0,
427 0, 0, 0, 0, 0, 0, 0, 0);
428 } else if (config_dock_floating
|| config_dock_nostrut
)
430 STRUT_PARTIAL_SET(dock_strut
, 0, 0, 0, 0,
431 0, 0, 0, 0, 0, 0, 0, 0);
433 switch (config_dock_pos
) {
434 case OB_DIRECTION_NORTHWEST
:
435 switch (config_dock_orient
) {
436 case OB_ORIENTATION_HORZ
:
437 STRUT_PARTIAL_SET(dock_strut
, 0, strh
, 0, 0,
438 0, 0, dock
->area
.x
, dock
->area
.x
439 + dock
->area
.width
- 1, 0, 0, 0, 0);
441 case OB_ORIENTATION_VERT
:
442 STRUT_PARTIAL_SET(dock_strut
, strw
, 0, 0, 0,
443 dock
->area
.y
, dock
->area
.y
444 + dock
->area
.height
- 1, 0, 0, 0, 0, 0, 0);
448 case OB_DIRECTION_NORTH
:
449 STRUT_PARTIAL_SET(dock_strut
, 0, strh
, 0, 0,
450 0, 0, dock
->area
.x
, dock
->area
.x
451 + dock
->area
.width
- 1, 0, 0, 0, 0);
453 case OB_DIRECTION_NORTHEAST
:
454 switch (config_dock_orient
) {
455 case OB_ORIENTATION_HORZ
:
456 STRUT_PARTIAL_SET(dock_strut
, 0, strh
, 0, 0,
457 0, 0, dock
->area
.x
, dock
->area
.x
458 + dock
->area
.width
-1, 0, 0, 0, 0);
460 case OB_ORIENTATION_VERT
:
461 STRUT_PARTIAL_SET(dock_strut
, 0, 0, strw
, 0,
462 0, 0, 0, 0, dock
->area
.y
, dock
->area
.y
463 + dock
->area
.height
- 1, 0, 0);
467 case OB_DIRECTION_WEST
:
468 STRUT_PARTIAL_SET(dock_strut
, strw
, 0, 0, 0,
469 dock
->area
.y
, dock
->area
.y
470 + dock
->area
.height
- 1, 0, 0, 0, 0, 0, 0);
472 case OB_DIRECTION_EAST
:
473 STRUT_PARTIAL_SET(dock_strut
, 0, 0, strw
, 0,
474 0, 0, 0, 0, dock
->area
.y
, dock
->area
.y
475 + dock
->area
.height
- 1, 0, 0);
477 case OB_DIRECTION_SOUTHWEST
:
478 switch (config_dock_orient
) {
479 case OB_ORIENTATION_HORZ
:
480 STRUT_PARTIAL_SET(dock_strut
, 0, 0, 0, strh
,
481 0, 0, 0, 0, 0, 0, dock
->area
.x
, dock
->area
.x
482 + dock
->area
.width
- 1);
484 case OB_ORIENTATION_VERT
:
485 STRUT_PARTIAL_SET(dock_strut
, strw
, 0, 0, 0,
486 dock
->area
.y
, dock
->area
.y
487 + dock
->area
.height
- 1, 0, 0, 0, 0, 0, 0);
491 case OB_DIRECTION_SOUTH
:
492 STRUT_PARTIAL_SET(dock_strut
, 0, 0, 0, strh
,
493 0, 0, 0, 0, 0, 0, dock
->area
.x
, dock
->area
.x
494 + dock
->area
.width
- 1);
496 case OB_DIRECTION_SOUTHEAST
:
497 switch (config_dock_orient
) {
498 case OB_ORIENTATION_HORZ
:
499 STRUT_PARTIAL_SET(dock_strut
, 0, 0, 0, strh
,
500 0, 0, 0, 0, 0, 0, dock
->area
.x
,
501 dock
->area
.x
+ dock
->area
.width
- 1);
503 case OB_ORIENTATION_VERT
:
504 STRUT_PARTIAL_SET(dock_strut
, 0, 0, strw
, 0,
505 0, 0, 0, 0, dock
->area
.y
, dock
->area
.y
506 + dock
->area
.height
- 1, 0, 0);
513 /* not used for actually sizing shit */
514 dock
->area
.width
-= ob_rr_theme
->obwidth
* 2;
515 dock
->area
.height
-= ob_rr_theme
->obwidth
* 2;
517 if (dock
->dock_apps
) {
518 g_assert(dock
->area
.width
> 0);
519 g_assert(dock
->area
.height
> 0);
521 XMoveResizeWindow(ob_display
, dock
->frame
, dock
->area
.x
, dock
->area
.y
,
522 dock
->area
.width
, dock
->area
.height
);
524 RrPaint(dock
->a_frame
, dock
->frame
, dock
->area
.width
,
526 XMapWindow(ob_display
, dock
->frame
);
528 XUnmapWindow(ob_display
, dock
->frame
);
530 /* but they are useful outside of this function! */
531 dock
->area
.width
+= ob_rr_theme
->obwidth
* 2;
532 dock
->area
.height
+= ob_rr_theme
->obwidth
* 2;
534 screen_update_areas();
539 void dock_app_configure(ObDockApp
*app
, gint w
, gint h
)
546 void dock_app_drag(ObDockApp
*app
, XMotionEvent
*e
)
548 ObDockApp
*over
= NULL
;
557 /* are we on top of the dock? */
558 if (!(x
>= dock
->area
.x
&&
560 x
< dock
->area
.x
+ dock
->area
.width
&&
561 y
< dock
->area
.y
+ dock
->area
.height
))
567 /* which dock app are we on top of? */
569 for (it
= dock
->dock_apps
; it
; it
= g_list_next(it
)) {
571 switch (config_dock_orient
) {
572 case OB_ORIENTATION_HORZ
:
573 if (x
>= over
->x
&& x
< over
->x
+ over
->w
)
576 case OB_ORIENTATION_VERT
:
577 if (y
>= over
->y
&& y
< over
->y
+ over
->h
)
581 /* dont go to it->next! */
584 if (!it
|| app
== over
) return;
589 switch (config_dock_orient
) {
590 case OB_ORIENTATION_HORZ
:
591 after
= (x
> over
->w
/ 2);
593 case OB_ORIENTATION_VERT
:
594 after
= (y
> over
->h
/ 2);
597 g_assert_not_reached();
600 /* remove before doing the it->next! */
601 dock
->dock_apps
= g_list_remove(dock
->dock_apps
, app
);
603 if (after
) it
= it
->next
;
605 dock
->dock_apps
= g_list_insert_before(dock
->dock_apps
, it
, app
);
609 static gboolean
hide_timeout(gpointer data
)
615 return FALSE
; /* don't repeat */
618 static gboolean
show_timeout(gpointer data
)
621 dock
->hidden
= FALSE
;
624 return FALSE
; /* don't repeat */
627 void dock_hide(gboolean hide
)
630 if (dock
->hidden
&& config_dock_hide
) {
631 ob_main_loop_timeout_add(ob_main_loop
,
632 config_dock_show_delay
* 1000,
633 show_timeout
, NULL
, g_direct_equal
, NULL
);
634 } else if (!dock
->hidden
&& config_dock_hide
) {
635 ob_main_loop_timeout_remove(ob_main_loop
, hide_timeout
);
638 if (!dock
->hidden
&& config_dock_hide
) {
639 ob_main_loop_timeout_add(ob_main_loop
,
640 config_dock_hide_delay
* 1000,
641 hide_timeout
, NULL
, g_direct_equal
, NULL
);
642 } else if (dock
->hidden
&& config_dock_hide
) {
643 ob_main_loop_timeout_remove(ob_main_loop
, show_timeout
);
648 void dock_get_area(Rect
*a
)
650 RECT_SET(*a
, dock
->area
.x
, dock
->area
.y
,
651 dock
->area
.width
, dock
->area
.height
);