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)
232 RrMargins(dock
->a_frame
, &l
, &t
, &r
, &b
);
233 hidesize
= MAX(1, ob_rr_theme
->obwidth
);
235 dock
->area
.width
= dock
->area
.height
= 0;
238 for (it
= dock
->dock_apps
; it
; it
= g_list_next(it
)) {
239 ObDockApp
*app
= it
->data
;
240 switch (config_dock_orient
) {
241 case OB_ORIENTATION_HORZ
:
242 dock
->area
.width
+= app
->w
;
243 dock
->area
.height
= MAX(dock
->area
.height
, app
->h
);
245 case OB_ORIENTATION_VERT
:
246 dock
->area
.width
= MAX(dock
->area
.width
, app
->w
);
247 dock
->area
.height
+= app
->h
;
252 dock
->area
.width
+= l
+ r
;
253 dock
->area
.height
+= t
+ b
;
258 /* position the apps */
259 for (it
= dock
->dock_apps
; it
; it
= g_list_next(it
)) {
260 ObDockApp
*app
= it
->data
;
261 switch (config_dock_orient
) {
262 case OB_ORIENTATION_HORZ
:
264 app
->y
= (dock
->area
.height
- app
->h
) / 2;
267 case OB_ORIENTATION_VERT
:
268 app
->x
= (dock
->area
.width
- app
->w
) / 2;
274 XMoveWindow(ob_display
, app
->icon_win
, app
->x
, app
->y
);
277 /* used for calculating offsets */
278 dock
->area
.width
+= ob_rr_theme
->obwidth
* 2;
279 dock
->area
.height
+= ob_rr_theme
->obwidth
* 2;
281 a
= screen_physical_area_all_monitors();
283 /* calculate position */
284 if (config_dock_floating
) {
285 dock
->area
.x
= config_dock_x
;
286 dock
->area
.y
= config_dock_y
;
287 gravity
= NorthWestGravity
;
289 switch (config_dock_pos
) {
290 case OB_DIRECTION_NORTHWEST
:
293 gravity
= NorthWestGravity
;
295 case OB_DIRECTION_NORTH
:
296 dock
->area
.x
= a
->width
/ 2;
298 gravity
= NorthGravity
;
300 case OB_DIRECTION_NORTHEAST
:
301 dock
->area
.x
= a
->width
;
303 gravity
= NorthEastGravity
;
305 case OB_DIRECTION_WEST
:
307 dock
->area
.y
= a
->height
/ 2;
308 gravity
= WestGravity
;
310 case OB_DIRECTION_EAST
:
311 dock
->area
.x
= a
->width
;
312 dock
->area
.y
= a
->height
/ 2;
313 gravity
= EastGravity
;
315 case OB_DIRECTION_SOUTHWEST
:
317 dock
->area
.y
= a
->height
;
318 gravity
= SouthWestGravity
;
320 case OB_DIRECTION_SOUTH
:
321 dock
->area
.x
= a
->width
/ 2;
322 dock
->area
.y
= a
->height
;
323 gravity
= SouthGravity
;
325 case OB_DIRECTION_SOUTHEAST
:
326 dock
->area
.x
= a
->width
;
327 dock
->area
.y
= a
->height
;
328 gravity
= SouthEastGravity
;
331 g_assert_not_reached();
339 dock
->area
.x
-= dock
->area
.width
/ 2;
341 case NorthEastGravity
:
343 case SouthEastGravity
:
344 dock
->area
.x
-= dock
->area
.width
;
351 dock
->area
.y
-= dock
->area
.height
/ 2;
353 case SouthWestGravity
:
355 case SouthEastGravity
:
356 dock
->area
.y
-= dock
->area
.height
;
360 if (config_dock_hide
&& dock
->hidden
) {
361 if (!config_dock_floating
) {
362 switch (config_dock_pos
) {
363 case OB_DIRECTION_NORTHWEST
:
364 switch (config_dock_orient
) {
365 case OB_ORIENTATION_HORZ
:
366 dock
->area
.y
-= dock
->area
.height
- hidesize
;
368 case OB_ORIENTATION_VERT
:
369 dock
->area
.x
-= dock
->area
.width
- hidesize
;
373 case OB_DIRECTION_NORTH
:
374 dock
->area
.y
-= dock
->area
.height
- hidesize
;
376 case OB_DIRECTION_NORTHEAST
:
377 switch (config_dock_orient
) {
378 case OB_ORIENTATION_HORZ
:
379 dock
->area
.y
-= dock
->area
.height
- hidesize
;
381 case OB_ORIENTATION_VERT
:
382 dock
->area
.x
+= dock
->area
.width
- hidesize
;
386 case OB_DIRECTION_WEST
:
387 dock
->area
.x
-= dock
->area
.width
- hidesize
;
389 case OB_DIRECTION_EAST
:
390 dock
->area
.x
+= dock
->area
.width
- hidesize
;
392 case OB_DIRECTION_SOUTHWEST
:
393 switch (config_dock_orient
) {
394 case OB_ORIENTATION_HORZ
:
395 dock
->area
.y
+= dock
->area
.height
- hidesize
;
397 case OB_ORIENTATION_VERT
:
398 dock
->area
.x
-= dock
->area
.width
- hidesize
;
401 case OB_DIRECTION_SOUTH
:
402 dock
->area
.y
+= dock
->area
.height
- hidesize
;
404 case OB_DIRECTION_SOUTHEAST
:
405 switch (config_dock_orient
) {
406 case OB_ORIENTATION_HORZ
:
407 dock
->area
.y
+= dock
->area
.height
- hidesize
;
409 case OB_ORIENTATION_VERT
:
410 dock
->area
.x
+= dock
->area
.width
- hidesize
;
418 if (!config_dock_floating
&& config_dock_hide
) {
422 strw
= dock
->area
.width
;
423 strh
= dock
->area
.height
;
427 if (!dock
->dock_apps
) {
428 STRUT_PARTIAL_SET(dock_strut
, 0, 0, 0, 0,
429 0, 0, 0, 0, 0, 0, 0, 0);
430 } else if (config_dock_floating
|| config_dock_nostrut
)
432 STRUT_PARTIAL_SET(dock_strut
, 0, 0, 0, 0,
433 0, 0, 0, 0, 0, 0, 0, 0);
435 switch (config_dock_pos
) {
436 case OB_DIRECTION_NORTHWEST
:
437 switch (config_dock_orient
) {
438 case OB_ORIENTATION_HORZ
:
439 STRUT_PARTIAL_SET(dock_strut
, 0, strh
, 0, 0,
440 0, 0, dock
->area
.x
, dock
->area
.x
441 + dock
->area
.width
- 1, 0, 0, 0, 0);
443 case OB_ORIENTATION_VERT
:
444 STRUT_PARTIAL_SET(dock_strut
, strw
, 0, 0, 0,
445 dock
->area
.y
, dock
->area
.y
446 + dock
->area
.height
- 1, 0, 0, 0, 0, 0, 0);
450 case OB_DIRECTION_NORTH
:
451 STRUT_PARTIAL_SET(dock_strut
, 0, strh
, 0, 0,
452 0, 0, dock
->area
.x
, dock
->area
.x
453 + dock
->area
.width
- 1, 0, 0, 0, 0);
455 case OB_DIRECTION_NORTHEAST
:
456 switch (config_dock_orient
) {
457 case OB_ORIENTATION_HORZ
:
458 STRUT_PARTIAL_SET(dock_strut
, 0, strh
, 0, 0,
459 0, 0, dock
->area
.x
, dock
->area
.x
460 + dock
->area
.width
-1, 0, 0, 0, 0);
462 case OB_ORIENTATION_VERT
:
463 STRUT_PARTIAL_SET(dock_strut
, 0, 0, strw
, 0,
464 0, 0, 0, 0, dock
->area
.y
, dock
->area
.y
465 + dock
->area
.height
- 1, 0, 0);
469 case OB_DIRECTION_WEST
:
470 STRUT_PARTIAL_SET(dock_strut
, strw
, 0, 0, 0,
471 dock
->area
.y
, dock
->area
.y
472 + dock
->area
.height
- 1, 0, 0, 0, 0, 0, 0);
474 case OB_DIRECTION_EAST
:
475 STRUT_PARTIAL_SET(dock_strut
, 0, 0, strw
, 0,
476 0, 0, 0, 0, dock
->area
.y
, dock
->area
.y
477 + dock
->area
.height
- 1, 0, 0);
479 case OB_DIRECTION_SOUTHWEST
:
480 switch (config_dock_orient
) {
481 case OB_ORIENTATION_HORZ
:
482 STRUT_PARTIAL_SET(dock_strut
, 0, 0, 0, strh
,
483 0, 0, 0, 0, 0, 0, dock
->area
.x
, dock
->area
.x
484 + dock
->area
.width
- 1);
486 case OB_ORIENTATION_VERT
:
487 STRUT_PARTIAL_SET(dock_strut
, strw
, 0, 0, 0,
488 dock
->area
.y
, dock
->area
.y
489 + dock
->area
.height
- 1, 0, 0, 0, 0, 0, 0);
493 case OB_DIRECTION_SOUTH
:
494 STRUT_PARTIAL_SET(dock_strut
, 0, 0, 0, strh
,
495 0, 0, 0, 0, 0, 0, dock
->area
.x
, dock
->area
.x
496 + dock
->area
.width
- 1);
498 case OB_DIRECTION_SOUTHEAST
:
499 switch (config_dock_orient
) {
500 case OB_ORIENTATION_HORZ
:
501 STRUT_PARTIAL_SET(dock_strut
, 0, 0, 0, strh
,
502 0, 0, 0, 0, 0, 0, dock
->area
.x
,
503 dock
->area
.x
+ dock
->area
.width
- 1);
505 case OB_ORIENTATION_VERT
:
506 STRUT_PARTIAL_SET(dock_strut
, 0, 0, strw
, 0,
507 0, 0, 0, 0, dock
->area
.y
, dock
->area
.y
508 + dock
->area
.height
- 1, 0, 0);
515 /* not used for actually sizing shit */
516 dock
->area
.width
-= ob_rr_theme
->obwidth
* 2;
517 dock
->area
.height
-= ob_rr_theme
->obwidth
* 2;
519 if (dock
->dock_apps
) {
520 g_assert(dock
->area
.width
> 0);
521 g_assert(dock
->area
.height
> 0);
523 XMoveResizeWindow(ob_display
, dock
->frame
, dock
->area
.x
, dock
->area
.y
,
524 dock
->area
.width
, dock
->area
.height
);
526 RrPaint(dock
->a_frame
, dock
->frame
, dock
->area
.width
,
528 XMapWindow(ob_display
, dock
->frame
);
530 XUnmapWindow(ob_display
, dock
->frame
);
532 /* but they are useful outside of this function! */
533 dock
->area
.width
+= ob_rr_theme
->obwidth
* 2;
534 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 ob_main_loop_timeout_add(ob_main_loop
,
634 config_dock_show_delay
* 1000,
635 show_timeout
, NULL
, g_direct_equal
, NULL
);
636 } else if (!dock
->hidden
&& config_dock_hide
) {
637 ob_main_loop_timeout_remove(ob_main_loop
, hide_timeout
);
640 if (!dock
->hidden
&& config_dock_hide
) {
641 ob_main_loop_timeout_add(ob_main_loop
,
642 config_dock_hide_delay
* 1000,
643 hide_timeout
, NULL
, g_direct_equal
, NULL
);
644 } else if (dock
->hidden
&& config_dock_hide
) {
645 ob_main_loop_timeout_remove(ob_main_loop
, show_timeout
);
650 void dock_get_area(Rect
*a
)
652 RECT_SET(*a
, dock
->area
.x
, dock
->area
.y
,
653 dock
->area
.width
, dock
->area
.height
);