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 static guint
window_hash(Window
*w
) { return *w
; }
54 static gboolean
window_comp(Window
*w1
, Window
*w2
) { return *w1
== *w2
; }
56 void dock_startup(gboolean reconfig
)
58 XSetWindowAttributes attrib
;
63 XSetWindowBorder(obt_display
, dock
->frame
,
64 RrColorPixel(ob_rr_theme
->osd_border_color
));
65 XSetWindowBorderWidth(obt_display
, dock
->frame
, ob_rr_theme
->obwidth
);
67 RrAppearanceFree(dock
->a_frame
);
68 dock
->a_frame
= RrAppearanceCopy(ob_rr_theme
->osd_hilite_bg
);
70 stacking_add(DOCK_AS_WINDOW(dock
));
75 for (it
= dock
->dock_apps
; it
; it
= g_list_next(it
))
76 dock_app_grab_button(it
->data
, TRUE
);
80 STRUT_PARTIAL_SET(dock_strut
, 0, 0, 0, 0,
81 0, 0, 0, 0, 0, 0, 0, 0);
83 dock
= g_new0(ObDock
, 1);
84 dock
->obwin
.type
= OB_WINDOW_CLASS_DOCK
;
88 dock
->dock_map
= g_hash_table_new((GHashFunc
)window_hash
,
89 (GEqualFunc
)window_comp
);
91 attrib
.event_mask
= DOCK_EVENT_MASK
;
92 attrib
.override_redirect
= True
;
93 attrib
.do_not_propagate_mask
= DOCK_NOPROPAGATEMASK
;
94 dock
->frame
= XCreateWindow(obt_display
, obt_root(ob_screen
),
96 RrDepth(ob_rr_inst
), InputOutput
,
98 CWOverrideRedirect
| CWEventMask
|
101 dock
->a_frame
= RrAppearanceCopy(ob_rr_theme
->osd_hilite_bg
);
102 XSetWindowBorder(obt_display
, dock
->frame
,
103 RrColorPixel(ob_rr_theme
->osd_border_color
));
104 XSetWindowBorderWidth(obt_display
, dock
->frame
, ob_rr_theme
->obwidth
);
106 /* Setting the window type so xcompmgr can tell what it is */
107 OBT_PROP_SET32(dock
->frame
, NET_WM_WINDOW_TYPE
, ATOM
,
108 OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_DOCK
));
110 window_add(&dock
->frame
, DOCK_AS_WINDOW(dock
));
111 stacking_add(DOCK_AS_WINDOW(dock
));
114 void dock_shutdown(gboolean reconfig
)
119 stacking_remove(DOCK_AS_WINDOW(dock
));
121 for (it
= dock
->dock_apps
; it
; it
= g_list_next(it
))
122 dock_app_grab_button(it
->data
, FALSE
);
126 g_hash_table_destroy(dock
->dock_map
);
128 XDestroyWindow(obt_display
, dock
->frame
);
129 RrAppearanceFree(dock
->a_frame
);
130 window_remove(dock
->frame
);
131 stacking_remove(dock
);
134 void dock_manage(Window icon_win
, Window name_win
)
137 XWindowAttributes attrib
;
140 app
= g_new0(ObDockApp
, 1);
141 app
->name_win
= name_win
;
142 app
->icon_win
= icon_win
;
144 if (OBT_PROP_GETSS(app
->name_win
, WM_CLASS
, locale
, &data
)) {
146 app
->name
= g_strdup(data
[0]);
148 app
->class = g_strdup(data
[1]);
153 if (app
->name
== NULL
) app
->name
= g_strdup("");
154 if (app
->class == NULL
) app
->class = g_strdup("");
156 if (XGetWindowAttributes(obt_display
, app
->icon_win
, &attrib
)) {
157 app
->w
= attrib
.width
;
158 app
->h
= attrib
.height
;
160 app
->w
= app
->h
= 64;
163 dock
->dock_apps
= g_list_append(dock
->dock_apps
, app
);
164 g_hash_table_insert(dock
->dock_map
, &app
->icon_win
, app
);
167 XReparentWindow(obt_display
, app
->icon_win
, dock
->frame
, app
->x
, app
->y
);
169 This is the same case as in frame.c for client windows. When Openbox is
170 starting, the window is already mapped so we see unmap events occur for
171 it. There are 2 unmap events generated that we see, one with the 'event'
172 member set the root window, and one set to the client, but both get
173 handled and need to be ignored.
175 if (ob_state() == OB_STATE_STARTING
)
176 app
->ignore_unmaps
+= 2;
177 XChangeSaveSet(obt_display
, app
->icon_win
, SetModeInsert
);
178 XMapWindow(obt_display
, app
->icon_win
);
180 if (app
->name_win
!= app
->icon_win
) {
181 XReparentWindow(obt_display
, app
->name_win
, dock
->frame
, -1000, -1000);
182 XChangeSaveSet(obt_display
, app
->name_win
, SetModeInsert
);
183 XMapWindow(obt_display
, app
->name_win
);
186 XSync(obt_display
, False
);
188 XSelectInput(obt_display
, app
->icon_win
, DOCKAPP_EVENT_MASK
);
190 dock_app_grab_button(app
, TRUE
);
192 ob_debug("Managed Dock App: 0x%lx 0x%lx (%s)",
193 app
->icon_win
, app
->name_win
, app
->class);
198 void dock_unmanage_all(void)
200 while (dock
->dock_apps
)
201 dock_unmanage(dock
->dock_apps
->data
, TRUE
);
204 void dock_unmanage(ObDockApp
*app
, gboolean reparent
)
206 dock_app_grab_button(app
, FALSE
);
207 XSelectInput(obt_display
, app
->icon_win
, NoEventMask
);
208 /* remove the window from our save set */
209 XChangeSaveSet(obt_display
, app
->icon_win
, SetModeDelete
);
210 XSync(obt_display
, False
);
213 XReparentWindow(obt_display
, app
->icon_win
, obt_root(ob_screen
), 0, 0);
214 if (app
->name_win
!= app
->icon_win
)
215 XReparentWindow(obt_display
, app
->name_win
,
216 obt_root(ob_screen
), 0, 0);
219 dock
->dock_apps
= g_list_remove(dock
->dock_apps
, app
);
220 g_hash_table_remove(dock
->dock_map
, &app
->icon_win
);
223 ob_debug("Unmanaged Dock App: 0x%lx (%s)", app
->icon_win
, app
->class);
230 void dock_configure(void)
240 RrMargins(dock
->a_frame
, &l
, &t
, &r
, &b
);
241 hidesize
= MAX(1, ob_rr_theme
->obwidth
);
243 dock
->area
.width
= dock
->area
.height
= 0;
246 for (it
= dock
->dock_apps
; it
; it
= g_list_next(it
)) {
247 ObDockApp
*app
= it
->data
;
248 switch (config_dock_orient
) {
249 case OB_ORIENTATION_HORZ
:
250 dock
->area
.width
+= app
->w
;
251 dock
->area
.height
= MAX(dock
->area
.height
, app
->h
);
253 case OB_ORIENTATION_VERT
:
254 dock
->area
.width
= MAX(dock
->area
.width
, app
->w
);
255 dock
->area
.height
+= app
->h
;
260 if (dock
->dock_apps
) {
261 dock
->area
.width
+= l
+ r
;
262 dock
->area
.height
+= t
+ b
;
268 /* position the apps */
269 for (it
= dock
->dock_apps
; it
; it
= g_list_next(it
)) {
270 ObDockApp
*app
= it
->data
;
271 switch (config_dock_orient
) {
272 case OB_ORIENTATION_HORZ
:
274 app
->y
= (dock
->area
.height
- app
->h
) / 2;
277 case OB_ORIENTATION_VERT
:
278 app
->x
= (dock
->area
.width
- app
->w
) / 2;
284 XMoveWindow(obt_display
, app
->icon_win
, app
->x
, app
->y
);
287 /* used for calculating offsets */
288 dock
->area
.width
+= ob_rr_theme
->obwidth
* 2;
289 dock
->area
.height
+= ob_rr_theme
->obwidth
* 2;
291 a
= screen_physical_area_all_monitors();
293 /* calculate position */
294 if (config_dock_floating
) {
295 dock
->area
.x
= config_dock_x
;
296 dock
->area
.y
= config_dock_y
;
297 gravity
= NorthWestGravity
;
299 switch (config_dock_pos
) {
300 case OB_DIRECTION_NORTHWEST
:
303 gravity
= NorthWestGravity
;
305 case OB_DIRECTION_NORTH
:
306 dock
->area
.x
= a
->width
/ 2;
308 gravity
= NorthGravity
;
310 case OB_DIRECTION_NORTHEAST
:
311 dock
->area
.x
= a
->width
;
313 gravity
= NorthEastGravity
;
315 case OB_DIRECTION_WEST
:
317 dock
->area
.y
= a
->height
/ 2;
318 gravity
= WestGravity
;
320 case OB_DIRECTION_EAST
:
321 dock
->area
.x
= a
->width
;
322 dock
->area
.y
= a
->height
/ 2;
323 gravity
= EastGravity
;
325 case OB_DIRECTION_SOUTHWEST
:
327 dock
->area
.y
= a
->height
;
328 gravity
= SouthWestGravity
;
330 case OB_DIRECTION_SOUTH
:
331 dock
->area
.x
= a
->width
/ 2;
332 dock
->area
.y
= a
->height
;
333 gravity
= SouthGravity
;
335 case OB_DIRECTION_SOUTHEAST
:
336 dock
->area
.x
= a
->width
;
337 dock
->area
.y
= a
->height
;
338 gravity
= SouthEastGravity
;
341 g_assert_not_reached();
349 dock
->area
.x
-= dock
->area
.width
/ 2;
351 case NorthEastGravity
:
353 case SouthEastGravity
:
354 dock
->area
.x
-= dock
->area
.width
;
361 dock
->area
.y
-= dock
->area
.height
/ 2;
363 case SouthWestGravity
:
365 case SouthEastGravity
:
366 dock
->area
.y
-= dock
->area
.height
;
370 if (config_dock_hide
&& dock
->hidden
) {
371 if (!config_dock_floating
) {
372 switch (config_dock_pos
) {
373 case OB_DIRECTION_NORTHWEST
:
374 switch (config_dock_orient
) {
375 case OB_ORIENTATION_HORZ
:
376 dock
->area
.y
-= dock
->area
.height
- hidesize
;
378 case OB_ORIENTATION_VERT
:
379 dock
->area
.x
-= dock
->area
.width
- hidesize
;
383 case OB_DIRECTION_NORTH
:
384 dock
->area
.y
-= dock
->area
.height
- hidesize
;
386 case OB_DIRECTION_NORTHEAST
:
387 switch (config_dock_orient
) {
388 case OB_ORIENTATION_HORZ
:
389 dock
->area
.y
-= dock
->area
.height
- hidesize
;
391 case OB_ORIENTATION_VERT
:
392 dock
->area
.x
+= dock
->area
.width
- hidesize
;
396 case OB_DIRECTION_WEST
:
397 dock
->area
.x
-= dock
->area
.width
- hidesize
;
399 case OB_DIRECTION_EAST
:
400 dock
->area
.x
+= dock
->area
.width
- hidesize
;
402 case OB_DIRECTION_SOUTHWEST
:
403 switch (config_dock_orient
) {
404 case OB_ORIENTATION_HORZ
:
405 dock
->area
.y
+= dock
->area
.height
- hidesize
;
407 case OB_ORIENTATION_VERT
:
408 dock
->area
.x
-= dock
->area
.width
- hidesize
;
411 case OB_DIRECTION_SOUTH
:
412 dock
->area
.y
+= dock
->area
.height
- hidesize
;
414 case OB_DIRECTION_SOUTHEAST
:
415 switch (config_dock_orient
) {
416 case OB_ORIENTATION_HORZ
:
417 dock
->area
.y
+= dock
->area
.height
- hidesize
;
419 case OB_ORIENTATION_VERT
:
420 dock
->area
.x
+= dock
->area
.width
- hidesize
;
428 if (!config_dock_floating
&& config_dock_hide
) {
432 strw
= dock
->area
.width
;
433 strh
= dock
->area
.height
;
437 if (!dock
->dock_apps
) {
438 STRUT_PARTIAL_SET(dock_strut
, 0, 0, 0, 0,
439 0, 0, 0, 0, 0, 0, 0, 0);
441 else if (config_dock_floating
|| config_dock_nostrut
) {
442 STRUT_PARTIAL_SET(dock_strut
, 0, 0, 0, 0,
443 0, 0, 0, 0, 0, 0, 0, 0);
446 switch (config_dock_pos
) {
447 case OB_DIRECTION_NORTHWEST
:
448 switch (config_dock_orient
) {
449 case OB_ORIENTATION_HORZ
:
450 STRUT_PARTIAL_SET(dock_strut
, 0, strh
, 0, 0,
451 0, 0, dock
->area
.x
, dock
->area
.x
452 + dock
->area
.width
- 1, 0, 0, 0, 0);
454 case OB_ORIENTATION_VERT
:
455 STRUT_PARTIAL_SET(dock_strut
, strw
, 0, 0, 0,
456 dock
->area
.y
, dock
->area
.y
457 + dock
->area
.height
- 1, 0, 0, 0, 0, 0, 0);
461 case OB_DIRECTION_NORTH
:
462 STRUT_PARTIAL_SET(dock_strut
, 0, strh
, 0, 0,
463 0, 0, dock
->area
.x
, dock
->area
.x
464 + dock
->area
.width
- 1, 0, 0, 0, 0);
466 case OB_DIRECTION_NORTHEAST
:
467 switch (config_dock_orient
) {
468 case OB_ORIENTATION_HORZ
:
469 STRUT_PARTIAL_SET(dock_strut
, 0, strh
, 0, 0,
470 0, 0, dock
->area
.x
, dock
->area
.x
471 + dock
->area
.width
-1, 0, 0, 0, 0);
473 case OB_ORIENTATION_VERT
:
474 STRUT_PARTIAL_SET(dock_strut
, 0, 0, strw
, 0,
475 0, 0, 0, 0, dock
->area
.y
, dock
->area
.y
476 + dock
->area
.height
- 1, 0, 0);
480 case OB_DIRECTION_WEST
:
481 STRUT_PARTIAL_SET(dock_strut
, strw
, 0, 0, 0,
482 dock
->area
.y
, dock
->area
.y
483 + dock
->area
.height
- 1, 0, 0, 0, 0, 0, 0);
485 case OB_DIRECTION_EAST
:
486 STRUT_PARTIAL_SET(dock_strut
, 0, 0, strw
, 0,
487 0, 0, 0, 0, dock
->area
.y
, dock
->area
.y
488 + dock
->area
.height
- 1, 0, 0);
490 case OB_DIRECTION_SOUTHWEST
:
491 switch (config_dock_orient
) {
492 case OB_ORIENTATION_HORZ
:
493 STRUT_PARTIAL_SET(dock_strut
, 0, 0, 0, strh
,
494 0, 0, 0, 0, 0, 0, dock
->area
.x
, dock
->area
.x
495 + dock
->area
.width
- 1);
497 case OB_ORIENTATION_VERT
:
498 STRUT_PARTIAL_SET(dock_strut
, strw
, 0, 0, 0,
499 dock
->area
.y
, dock
->area
.y
500 + dock
->area
.height
- 1, 0, 0, 0, 0, 0, 0);
504 case OB_DIRECTION_SOUTH
:
505 STRUT_PARTIAL_SET(dock_strut
, 0, 0, 0, strh
,
506 0, 0, 0, 0, 0, 0, dock
->area
.x
, dock
->area
.x
507 + dock
->area
.width
- 1);
509 case OB_DIRECTION_SOUTHEAST
:
510 switch (config_dock_orient
) {
511 case OB_ORIENTATION_HORZ
:
512 STRUT_PARTIAL_SET(dock_strut
, 0, 0, 0, strh
,
513 0, 0, 0, 0, 0, 0, dock
->area
.x
,
514 dock
->area
.x
+ dock
->area
.width
- 1);
516 case OB_ORIENTATION_VERT
:
517 STRUT_PARTIAL_SET(dock_strut
, 0, 0, strw
, 0,
518 0, 0, 0, 0, dock
->area
.y
, dock
->area
.y
519 + dock
->area
.height
- 1, 0, 0);
526 /* not used for actually sizing shit */
527 dock
->area
.width
-= ob_rr_theme
->obwidth
* 2;
528 dock
->area
.height
-= ob_rr_theme
->obwidth
* 2;
530 if (dock
->dock_apps
) {
531 g_assert(dock
->area
.width
> 0);
532 g_assert(dock
->area
.height
> 0);
534 XMoveResizeWindow(obt_display
, dock
->frame
, dock
->area
.x
, dock
->area
.y
,
535 dock
->area
.width
, dock
->area
.height
);
537 RrPaint(dock
->a_frame
, dock
->frame
, dock
->area
.width
,
539 XMapWindow(obt_display
, dock
->frame
);
541 XUnmapWindow(obt_display
, dock
->frame
);
543 /* but they are useful outside of this function! but don't add it if the
544 dock is actually not visible */
545 if (dock
->dock_apps
) {
546 dock
->area
.width
+= ob_rr_theme
->obwidth
* 2;
547 dock
->area
.height
+= ob_rr_theme
->obwidth
* 2;
550 screen_update_areas();
555 void dock_app_configure(ObDockApp
*app
, gint w
, gint h
)
562 void dock_app_drag(ObDockApp
*app
, XMotionEvent
*e
)
564 ObDockApp
*over
= NULL
;
573 /* are we on top of the dock? */
574 if (!(x
>= dock
->area
.x
&&
576 x
< dock
->area
.x
+ dock
->area
.width
&&
577 y
< dock
->area
.y
+ dock
->area
.height
))
583 /* which dock app are we on top of? */
585 for (it
= dock
->dock_apps
; it
; it
= g_list_next(it
)) {
587 switch (config_dock_orient
) {
588 case OB_ORIENTATION_HORZ
:
589 if (x
>= over
->x
&& x
< over
->x
+ over
->w
)
592 case OB_ORIENTATION_VERT
:
593 if (y
>= over
->y
&& y
< over
->y
+ over
->h
)
597 /* dont go to it->next! */
600 if (!it
|| app
== over
) return;
605 switch (config_dock_orient
) {
606 case OB_ORIENTATION_HORZ
:
607 after
= (x
> over
->w
/ 2);
609 case OB_ORIENTATION_VERT
:
610 after
= (y
> over
->h
/ 2);
613 g_assert_not_reached();
616 /* remove before doing the it->next! */
617 dock
->dock_apps
= g_list_remove(dock
->dock_apps
, app
);
619 if (after
) it
= it
->next
;
621 dock
->dock_apps
= g_list_insert_before(dock
->dock_apps
, it
, app
);
625 static gboolean
hide_timeout(gpointer data
)
631 return FALSE
; /* don't repeat */
634 static gboolean
show_timeout(gpointer data
)
637 dock
->hidden
= FALSE
;
640 return FALSE
; /* don't repeat */
643 void dock_hide(gboolean hide
)
646 if (dock
->hidden
&& config_dock_hide
) {
647 obt_main_loop_timeout_add(ob_main_loop
,
648 config_dock_show_delay
* 1000,
650 g_direct_equal
, NULL
);
651 } else if (!dock
->hidden
&& config_dock_hide
) {
652 obt_main_loop_timeout_remove(ob_main_loop
, hide_timeout
);
655 if (!dock
->hidden
&& config_dock_hide
) {
656 obt_main_loop_timeout_add(ob_main_loop
,
657 config_dock_hide_delay
* 1000,
659 g_direct_equal
, NULL
);
660 } else if (dock
->hidden
&& config_dock_hide
) {
661 obt_main_loop_timeout_remove(ob_main_loop
, show_timeout
);
666 void dock_get_area(Rect
*a
)
668 RECT_SET(*a
, dock
->area
.x
, dock
->area
.y
,
669 dock
->area
.width
, dock
->area
.height
);
672 ObDockApp
* dock_find_dockapp(Window xwin
)
674 return g_hash_table_lookup(dock
->dock_map
, &xwin
);