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 "obrender/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_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_slice_new0(ObDock
);
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_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
);
132 g_slice_free(ObDock
, dock
);
136 void dock_manage(Window icon_win
, Window name_win
)
139 XWindowAttributes attrib
;
142 app
= g_slice_new0(ObDockApp
);
143 app
->name_win
= name_win
;
144 app
->icon_win
= icon_win
;
146 if (OBT_PROP_GETSS(app
->name_win
, WM_CLASS
, locale
, &data
)) {
148 app
->name
= g_strdup(data
[0]);
150 app
->class = g_strdup(data
[1]);
155 if (app
->name
== NULL
) app
->name
= g_strdup("");
156 if (app
->class == NULL
) app
->class = g_strdup("");
158 if (XGetWindowAttributes(obt_display
, app
->icon_win
, &attrib
)) {
159 app
->w
= attrib
.width
;
160 app
->h
= attrib
.height
;
162 app
->w
= app
->h
= 64;
165 dock
->dock_apps
= g_list_append(dock
->dock_apps
, app
);
166 g_hash_table_insert(dock
->dock_map
, &app
->icon_win
, app
);
169 XReparentWindow(obt_display
, app
->icon_win
, dock
->frame
, app
->x
, app
->y
);
171 This is the same case as in frame.c for client windows. When Openbox is
172 starting, the window is already mapped so we see unmap events occur for
173 it. There are 2 unmap events generated that we see, one with the 'event'
174 member set the root window, and one set to the client, but both get
175 handled and need to be ignored.
177 if (ob_state() == OB_STATE_STARTING
)
178 app
->ignore_unmaps
+= 2;
179 XChangeSaveSet(obt_display
, app
->icon_win
, SetModeInsert
);
180 XMapWindow(obt_display
, app
->icon_win
);
182 if (app
->name_win
!= app
->icon_win
) {
183 XReparentWindow(obt_display
, app
->name_win
, dock
->frame
, -1000, -1000);
184 XChangeSaveSet(obt_display
, app
->name_win
, SetModeInsert
);
185 XMapWindow(obt_display
, app
->name_win
);
188 XSync(obt_display
, False
);
190 XSelectInput(obt_display
, app
->icon_win
, DOCKAPP_EVENT_MASK
);
192 dock_app_grab_button(app
, TRUE
);
194 ob_debug("Managed Dock App: 0x%lx 0x%lx (%s)",
195 app
->icon_win
, app
->name_win
, app
->class);
200 void dock_unmanage_all(void)
202 while (dock
->dock_apps
)
203 dock_unmanage(dock
->dock_apps
->data
, TRUE
);
206 void dock_unmanage(ObDockApp
*app
, gboolean reparent
)
208 dock_app_grab_button(app
, FALSE
);
209 XSelectInput(obt_display
, app
->icon_win
, NoEventMask
);
210 /* remove the window from our save set */
211 XChangeSaveSet(obt_display
, app
->icon_win
, SetModeDelete
);
212 XSync(obt_display
, False
);
215 XReparentWindow(obt_display
, app
->icon_win
, obt_root(ob_screen
), 0, 0);
216 if (app
->name_win
!= app
->icon_win
)
217 XReparentWindow(obt_display
, app
->name_win
,
218 obt_root(ob_screen
), 0, 0);
221 dock
->dock_apps
= g_list_remove(dock
->dock_apps
, app
);
222 g_hash_table_remove(dock
->dock_map
, &app
->icon_win
);
225 ob_debug("Unmanaged Dock App: 0x%lx (%s)", app
->icon_win
, app
->class);
229 g_slice_free(ObDockApp
, app
);
232 void dock_configure(void)
242 RrMargins(dock
->a_frame
, &l
, &t
, &r
, &b
);
243 hidesize
= MAX(1, ob_rr_theme
->obwidth
);
245 dock
->area
.width
= dock
->area
.height
= 0;
248 for (it
= dock
->dock_apps
; it
; it
= g_list_next(it
)) {
249 ObDockApp
*app
= it
->data
;
250 switch (config_dock_orient
) {
251 case OB_ORIENTATION_HORZ
:
252 dock
->area
.width
+= app
->w
;
253 dock
->area
.height
= MAX(dock
->area
.height
, app
->h
);
255 case OB_ORIENTATION_VERT
:
256 dock
->area
.width
= MAX(dock
->area
.width
, app
->w
);
257 dock
->area
.height
+= app
->h
;
262 if (dock
->dock_apps
) {
263 dock
->area
.width
+= l
+ r
;
264 dock
->area
.height
+= t
+ b
;
270 /* position the apps */
271 for (it
= dock
->dock_apps
; it
; it
= g_list_next(it
)) {
272 ObDockApp
*app
= it
->data
;
273 switch (config_dock_orient
) {
274 case OB_ORIENTATION_HORZ
:
276 app
->y
= (dock
->area
.height
- app
->h
) / 2;
279 case OB_ORIENTATION_VERT
:
280 app
->x
= (dock
->area
.width
- app
->w
) / 2;
286 XMoveWindow(obt_display
, app
->icon_win
, app
->x
, app
->y
);
289 /* used for calculating offsets */
290 dock
->area
.width
+= ob_rr_theme
->obwidth
* 2;
291 dock
->area
.height
+= ob_rr_theme
->obwidth
* 2;
293 a
= screen_physical_area_all_monitors();
295 /* calculate position */
296 if (config_dock_floating
) {
297 dock
->area
.x
= config_dock_x
;
298 dock
->area
.y
= config_dock_y
;
299 gravity
= NorthWestGravity
;
301 switch (config_dock_pos
) {
302 case OB_DIRECTION_NORTHWEST
:
305 gravity
= NorthWestGravity
;
307 case OB_DIRECTION_NORTH
:
308 dock
->area
.x
= a
->width
/ 2;
310 gravity
= NorthGravity
;
312 case OB_DIRECTION_NORTHEAST
:
313 dock
->area
.x
= a
->width
;
315 gravity
= NorthEastGravity
;
317 case OB_DIRECTION_WEST
:
319 dock
->area
.y
= a
->height
/ 2;
320 gravity
= WestGravity
;
322 case OB_DIRECTION_EAST
:
323 dock
->area
.x
= a
->width
;
324 dock
->area
.y
= a
->height
/ 2;
325 gravity
= EastGravity
;
327 case OB_DIRECTION_SOUTHWEST
:
329 dock
->area
.y
= a
->height
;
330 gravity
= SouthWestGravity
;
332 case OB_DIRECTION_SOUTH
:
333 dock
->area
.x
= a
->width
/ 2;
334 dock
->area
.y
= a
->height
;
335 gravity
= SouthGravity
;
337 case OB_DIRECTION_SOUTHEAST
:
338 dock
->area
.x
= a
->width
;
339 dock
->area
.y
= a
->height
;
340 gravity
= SouthEastGravity
;
343 g_assert_not_reached();
351 dock
->area
.x
-= dock
->area
.width
/ 2;
353 case NorthEastGravity
:
355 case SouthEastGravity
:
356 dock
->area
.x
-= dock
->area
.width
;
363 dock
->area
.y
-= dock
->area
.height
/ 2;
365 case SouthWestGravity
:
367 case SouthEastGravity
:
368 dock
->area
.y
-= dock
->area
.height
;
372 if (config_dock_hide
&& dock
->hidden
) {
373 if (!config_dock_floating
) {
374 switch (config_dock_pos
) {
375 case OB_DIRECTION_NORTHWEST
:
376 switch (config_dock_orient
) {
377 case OB_ORIENTATION_HORZ
:
378 dock
->area
.y
-= dock
->area
.height
- hidesize
;
380 case OB_ORIENTATION_VERT
:
381 dock
->area
.x
-= dock
->area
.width
- hidesize
;
385 case OB_DIRECTION_NORTH
:
386 dock
->area
.y
-= dock
->area
.height
- hidesize
;
388 case OB_DIRECTION_NORTHEAST
:
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
;
398 case OB_DIRECTION_WEST
:
399 dock
->area
.x
-= dock
->area
.width
- hidesize
;
401 case OB_DIRECTION_EAST
:
402 dock
->area
.x
+= dock
->area
.width
- hidesize
;
404 case OB_DIRECTION_SOUTHWEST
:
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
;
413 case OB_DIRECTION_SOUTH
:
414 dock
->area
.y
+= dock
->area
.height
- hidesize
;
416 case OB_DIRECTION_SOUTHEAST
:
417 switch (config_dock_orient
) {
418 case OB_ORIENTATION_HORZ
:
419 dock
->area
.y
+= dock
->area
.height
- hidesize
;
421 case OB_ORIENTATION_VERT
:
422 dock
->area
.x
+= dock
->area
.width
- hidesize
;
430 if (!config_dock_floating
&& config_dock_hide
) {
434 strw
= dock
->area
.width
;
435 strh
= dock
->area
.height
;
439 if (!dock
->dock_apps
) {
440 STRUT_PARTIAL_SET(dock_strut
, 0, 0, 0, 0,
441 0, 0, 0, 0, 0, 0, 0, 0);
443 else if (config_dock_floating
|| config_dock_nostrut
) {
444 STRUT_PARTIAL_SET(dock_strut
, 0, 0, 0, 0,
445 0, 0, 0, 0, 0, 0, 0, 0);
448 switch (config_dock_pos
) {
449 case OB_DIRECTION_NORTHWEST
:
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
454 + dock
->area
.width
- 1, 0, 0, 0, 0);
456 case OB_ORIENTATION_VERT
:
457 STRUT_PARTIAL_SET(dock_strut
, strw
, 0, 0, 0,
458 dock
->area
.y
, dock
->area
.y
459 + dock
->area
.height
- 1, 0, 0, 0, 0, 0, 0);
463 case OB_DIRECTION_NORTH
:
464 STRUT_PARTIAL_SET(dock_strut
, 0, strh
, 0, 0,
465 0, 0, dock
->area
.x
, dock
->area
.x
466 + dock
->area
.width
- 1, 0, 0, 0, 0);
468 case OB_DIRECTION_NORTHEAST
:
469 switch (config_dock_orient
) {
470 case OB_ORIENTATION_HORZ
:
471 STRUT_PARTIAL_SET(dock_strut
, 0, strh
, 0, 0,
472 0, 0, dock
->area
.x
, dock
->area
.x
473 + dock
->area
.width
-1, 0, 0, 0, 0);
475 case OB_ORIENTATION_VERT
:
476 STRUT_PARTIAL_SET(dock_strut
, 0, 0, strw
, 0,
477 0, 0, 0, 0, dock
->area
.y
, dock
->area
.y
478 + dock
->area
.height
- 1, 0, 0);
482 case OB_DIRECTION_WEST
:
483 STRUT_PARTIAL_SET(dock_strut
, strw
, 0, 0, 0,
484 dock
->area
.y
, dock
->area
.y
485 + dock
->area
.height
- 1, 0, 0, 0, 0, 0, 0);
487 case OB_DIRECTION_EAST
:
488 STRUT_PARTIAL_SET(dock_strut
, 0, 0, strw
, 0,
489 0, 0, 0, 0, dock
->area
.y
, dock
->area
.y
490 + dock
->area
.height
- 1, 0, 0);
492 case OB_DIRECTION_SOUTHWEST
:
493 switch (config_dock_orient
) {
494 case OB_ORIENTATION_HORZ
:
495 STRUT_PARTIAL_SET(dock_strut
, 0, 0, 0, strh
,
496 0, 0, 0, 0, 0, 0, dock
->area
.x
, dock
->area
.x
497 + dock
->area
.width
- 1);
499 case OB_ORIENTATION_VERT
:
500 STRUT_PARTIAL_SET(dock_strut
, strw
, 0, 0, 0,
501 dock
->area
.y
, dock
->area
.y
502 + dock
->area
.height
- 1, 0, 0, 0, 0, 0, 0);
506 case OB_DIRECTION_SOUTH
:
507 STRUT_PARTIAL_SET(dock_strut
, 0, 0, 0, strh
,
508 0, 0, 0, 0, 0, 0, dock
->area
.x
, dock
->area
.x
509 + dock
->area
.width
- 1);
511 case OB_DIRECTION_SOUTHEAST
:
512 switch (config_dock_orient
) {
513 case OB_ORIENTATION_HORZ
:
514 STRUT_PARTIAL_SET(dock_strut
, 0, 0, 0, strh
,
515 0, 0, 0, 0, 0, 0, dock
->area
.x
,
516 dock
->area
.x
+ dock
->area
.width
- 1);
518 case OB_ORIENTATION_VERT
:
519 STRUT_PARTIAL_SET(dock_strut
, 0, 0, strw
, 0,
520 0, 0, 0, 0, dock
->area
.y
, dock
->area
.y
521 + dock
->area
.height
- 1, 0, 0);
528 /* not used for actually sizing shit */
529 dock
->area
.width
-= ob_rr_theme
->obwidth
* 2;
530 dock
->area
.height
-= ob_rr_theme
->obwidth
* 2;
532 if (dock
->dock_apps
) {
533 g_assert(dock
->area
.width
> 0);
534 g_assert(dock
->area
.height
> 0);
536 XMoveResizeWindow(obt_display
, dock
->frame
, dock
->area
.x
, dock
->area
.y
,
537 dock
->area
.width
, dock
->area
.height
);
539 RrPaint(dock
->a_frame
, dock
->frame
, dock
->area
.width
,
541 XMapWindow(obt_display
, dock
->frame
);
543 XUnmapWindow(obt_display
, dock
->frame
);
545 /* but they are useful outside of this function! but don't add it if the
546 dock is actually not visible */
547 if (dock
->dock_apps
) {
548 dock
->area
.width
+= ob_rr_theme
->obwidth
* 2;
549 dock
->area
.height
+= ob_rr_theme
->obwidth
* 2;
552 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
);