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 | \
36 static guint show_timeout_id
;
37 static guint hide_timeout_id
;
39 StrutPartial dock_strut
;
41 static void dock_app_grab_button(ObDockApp
*app
, gboolean grab
)
44 grab_button_full(config_dock_app_move_button
,
45 config_dock_app_move_modifiers
, app
->icon_win
,
46 ButtonPressMask
| ButtonReleaseMask
|
48 GrabModeAsync
, OB_CURSOR_MOVE
);
50 ungrab_button(config_dock_app_move_button
,
51 config_dock_app_move_modifiers
, app
->icon_win
);
55 static guint
window_hash(Window
*w
) { return *w
; }
56 static gboolean
window_comp(Window
*w1
, Window
*w2
) { return *w1
== *w2
; }
58 void dock_startup(gboolean reconfig
)
60 XSetWindowAttributes attrib
;
65 XSetWindowBorder(obt_display
, dock
->frame
,
66 RrColorPixel(ob_rr_theme
->osd_border_color
));
67 XSetWindowBorderWidth(obt_display
, dock
->frame
, ob_rr_theme
->obwidth
);
69 RrAppearanceFree(dock
->a_frame
);
70 dock
->a_frame
= RrAppearanceCopy(ob_rr_theme
->osd_bg
);
72 stacking_add(DOCK_AS_WINDOW(dock
));
77 for (it
= dock
->dock_apps
; it
; it
= g_list_next(it
))
78 dock_app_grab_button(it
->data
, TRUE
);
82 STRUT_PARTIAL_SET(dock_strut
, 0, 0, 0, 0,
83 0, 0, 0, 0, 0, 0, 0, 0);
85 dock
= g_slice_new0(ObDock
);
86 dock
->obwin
.type
= OB_WINDOW_CLASS_DOCK
;
90 dock
->dock_map
= g_hash_table_new((GHashFunc
)window_hash
,
91 (GEqualFunc
)window_comp
);
93 attrib
.event_mask
= DOCK_EVENT_MASK
;
94 attrib
.override_redirect
= True
;
95 attrib
.do_not_propagate_mask
= DOCK_NOPROPAGATEMASK
;
96 dock
->frame
= XCreateWindow(obt_display
, obt_root(ob_screen
),
98 RrDepth(ob_rr_inst
), InputOutput
,
100 CWOverrideRedirect
| CWEventMask
|
103 dock
->a_frame
= RrAppearanceCopy(ob_rr_theme
->osd_bg
);
104 XSetWindowBorder(obt_display
, dock
->frame
,
105 RrColorPixel(ob_rr_theme
->osd_border_color
));
106 XSetWindowBorderWidth(obt_display
, dock
->frame
, ob_rr_theme
->obwidth
);
108 /* Setting the window type so xcompmgr can tell what it is */
109 OBT_PROP_SET32(dock
->frame
, NET_WM_WINDOW_TYPE
, ATOM
,
110 OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_DOCK
));
112 window_add(&dock
->frame
, DOCK_AS_WINDOW(dock
));
113 stacking_add(DOCK_AS_WINDOW(dock
));
116 void dock_shutdown(gboolean reconfig
)
121 stacking_remove(DOCK_AS_WINDOW(dock
));
123 for (it
= dock
->dock_apps
; it
; it
= g_list_next(it
))
124 dock_app_grab_button(it
->data
, FALSE
);
128 g_hash_table_destroy(dock
->dock_map
);
130 XDestroyWindow(obt_display
, dock
->frame
);
131 RrAppearanceFree(dock
->a_frame
);
132 window_remove(dock
->frame
);
133 stacking_remove(dock
);
134 g_slice_free(ObDock
, dock
);
138 void dock_manage(Window icon_win
, Window name_win
)
141 XWindowAttributes attrib
;
144 app
= g_slice_new0(ObDockApp
);
145 app
->name_win
= name_win
;
146 app
->icon_win
= icon_win
;
148 if (OBT_PROP_GETSS_TYPE(app
->name_win
, WM_CLASS
, STRING_NO_CC
, &data
)) {
150 app
->name
= g_strdup(data
[0]);
152 app
->class = g_strdup(data
[1]);
157 if (app
->name
== NULL
) app
->name
= g_strdup("");
158 if (app
->class == NULL
) app
->class = g_strdup("");
160 if (XGetWindowAttributes(obt_display
, app
->icon_win
, &attrib
)) {
161 app
->w
= attrib
.width
;
162 app
->h
= attrib
.height
;
164 app
->w
= app
->h
= 64;
167 dock
->dock_apps
= g_list_append(dock
->dock_apps
, app
);
168 g_hash_table_insert(dock
->dock_map
, &app
->icon_win
, app
);
171 XReparentWindow(obt_display
, app
->icon_win
, dock
->frame
, app
->x
, app
->y
);
173 This is the same case as in frame.c for client windows. When Openbox is
174 starting, the window is already mapped so we see unmap events occur for
175 it. There are 2 unmap events generated that we see, one with the 'event'
176 member set the root window, and one set to the client, but both get
177 handled and need to be ignored.
179 if (ob_state() == OB_STATE_STARTING
)
180 app
->ignore_unmaps
+= 2;
181 XChangeSaveSet(obt_display
, app
->icon_win
, SetModeInsert
);
182 XMapWindow(obt_display
, app
->icon_win
);
184 if (app
->name_win
!= app
->icon_win
) {
185 XReparentWindow(obt_display
, app
->name_win
, dock
->frame
, -1000, -1000);
186 XChangeSaveSet(obt_display
, app
->name_win
, SetModeInsert
);
187 XMapWindow(obt_display
, app
->name_win
);
190 XSync(obt_display
, False
);
192 XSelectInput(obt_display
, app
->icon_win
, DOCKAPP_EVENT_MASK
);
194 dock_app_grab_button(app
, TRUE
);
196 ob_debug("Managed Dock App: 0x%lx 0x%lx (%s)",
197 app
->icon_win
, app
->name_win
, app
->class);
202 void dock_unmanage_all(void)
204 while (dock
->dock_apps
)
205 dock_unmanage(dock
->dock_apps
->data
, TRUE
);
208 void dock_unmanage(ObDockApp
*app
, gboolean reparent
)
210 dock_app_grab_button(app
, FALSE
);
211 XSelectInput(obt_display
, app
->icon_win
, NoEventMask
);
212 /* remove the window from our save set */
213 XChangeSaveSet(obt_display
, app
->icon_win
, SetModeDelete
);
214 XSync(obt_display
, False
);
217 XReparentWindow(obt_display
, app
->icon_win
, obt_root(ob_screen
), 0, 0);
218 if (app
->name_win
!= app
->icon_win
)
219 XReparentWindow(obt_display
, app
->name_win
,
220 obt_root(ob_screen
), 0, 0);
223 dock
->dock_apps
= g_list_remove(dock
->dock_apps
, app
);
224 g_hash_table_remove(dock
->dock_map
, &app
->icon_win
);
227 ob_debug("Unmanaged Dock App: 0x%lx (%s)", app
->icon_win
, app
->class);
231 g_slice_free(ObDockApp
, app
);
234 void dock_configure(void)
244 RrMargins(dock
->a_frame
, &l
, &t
, &r
, &b
);
245 hidesize
= MAX(1, ob_rr_theme
->obwidth
);
247 dock
->area
.width
= dock
->area
.height
= 0;
250 for (it
= dock
->dock_apps
; it
; it
= g_list_next(it
)) {
251 ObDockApp
*app
= it
->data
;
252 switch (config_dock_orient
) {
253 case OB_ORIENTATION_HORZ
:
254 dock
->area
.width
+= app
->w
;
255 dock
->area
.height
= MAX(dock
->area
.height
, app
->h
);
257 case OB_ORIENTATION_VERT
:
258 dock
->area
.width
= MAX(dock
->area
.width
, app
->w
);
259 dock
->area
.height
+= app
->h
;
264 if (dock
->dock_apps
) {
265 dock
->area
.width
+= l
+ r
;
266 dock
->area
.height
+= t
+ b
;
272 /* position the apps */
273 for (it
= dock
->dock_apps
; it
; it
= g_list_next(it
)) {
274 ObDockApp
*app
= it
->data
;
275 switch (config_dock_orient
) {
276 case OB_ORIENTATION_HORZ
:
278 app
->y
= (dock
->area
.height
- app
->h
) / 2;
281 case OB_ORIENTATION_VERT
:
282 app
->x
= (dock
->area
.width
- app
->w
) / 2;
288 XMoveWindow(obt_display
, app
->icon_win
, app
->x
, app
->y
);
291 /* used for calculating offsets */
292 dock
->area
.width
+= ob_rr_theme
->obwidth
* 2;
293 dock
->area
.height
+= ob_rr_theme
->obwidth
* 2;
295 a
= screen_physical_area_all_monitors();
297 /* calculate position */
298 if (config_dock_floating
) {
299 dock
->area
.x
= config_dock_x
;
300 dock
->area
.y
= config_dock_y
;
301 gravity
= NorthWestGravity
;
303 switch (config_dock_pos
) {
304 case OB_DIRECTION_NORTHWEST
:
307 gravity
= NorthWestGravity
;
309 case OB_DIRECTION_NORTH
:
310 dock
->area
.x
= a
->width
/ 2;
312 gravity
= NorthGravity
;
314 case OB_DIRECTION_NORTHEAST
:
315 dock
->area
.x
= a
->width
;
317 gravity
= NorthEastGravity
;
319 case OB_DIRECTION_WEST
:
321 dock
->area
.y
= a
->height
/ 2;
322 gravity
= WestGravity
;
324 case OB_DIRECTION_EAST
:
325 dock
->area
.x
= a
->width
;
326 dock
->area
.y
= a
->height
/ 2;
327 gravity
= EastGravity
;
329 case OB_DIRECTION_SOUTHWEST
:
331 dock
->area
.y
= a
->height
;
332 gravity
= SouthWestGravity
;
334 case OB_DIRECTION_SOUTH
:
335 dock
->area
.x
= a
->width
/ 2;
336 dock
->area
.y
= a
->height
;
337 gravity
= SouthGravity
;
339 case OB_DIRECTION_SOUTHEAST
:
340 dock
->area
.x
= a
->width
;
341 dock
->area
.y
= a
->height
;
342 gravity
= SouthEastGravity
;
345 g_assert_not_reached();
353 dock
->area
.x
-= dock
->area
.width
/ 2;
355 case NorthEastGravity
:
357 case SouthEastGravity
:
358 dock
->area
.x
-= dock
->area
.width
;
365 dock
->area
.y
-= dock
->area
.height
/ 2;
367 case SouthWestGravity
:
369 case SouthEastGravity
:
370 dock
->area
.y
-= dock
->area
.height
;
374 if (config_dock_hide
&& dock
->hidden
) {
375 if (!config_dock_floating
) {
376 switch (config_dock_pos
) {
377 case OB_DIRECTION_NORTHWEST
:
378 switch (config_dock_orient
) {
379 case OB_ORIENTATION_HORZ
:
380 dock
->area
.y
-= dock
->area
.height
- hidesize
;
382 case OB_ORIENTATION_VERT
:
383 dock
->area
.x
-= dock
->area
.width
- hidesize
;
387 case OB_DIRECTION_NORTH
:
388 dock
->area
.y
-= dock
->area
.height
- hidesize
;
390 case OB_DIRECTION_NORTHEAST
:
391 switch (config_dock_orient
) {
392 case OB_ORIENTATION_HORZ
:
393 dock
->area
.y
-= dock
->area
.height
- hidesize
;
395 case OB_ORIENTATION_VERT
:
396 dock
->area
.x
+= dock
->area
.width
- hidesize
;
400 case OB_DIRECTION_WEST
:
401 dock
->area
.x
-= dock
->area
.width
- hidesize
;
403 case OB_DIRECTION_EAST
:
404 dock
->area
.x
+= dock
->area
.width
- hidesize
;
406 case OB_DIRECTION_SOUTHWEST
:
407 switch (config_dock_orient
) {
408 case OB_ORIENTATION_HORZ
:
409 dock
->area
.y
+= dock
->area
.height
- hidesize
;
411 case OB_ORIENTATION_VERT
:
412 dock
->area
.x
-= dock
->area
.width
- hidesize
;
415 case OB_DIRECTION_SOUTH
:
416 dock
->area
.y
+= dock
->area
.height
- hidesize
;
418 case OB_DIRECTION_SOUTHEAST
:
419 switch (config_dock_orient
) {
420 case OB_ORIENTATION_HORZ
:
421 dock
->area
.y
+= dock
->area
.height
- hidesize
;
423 case OB_ORIENTATION_VERT
:
424 dock
->area
.x
+= dock
->area
.width
- hidesize
;
432 if (!config_dock_floating
&& config_dock_hide
) {
436 strw
= dock
->area
.width
;
437 strh
= dock
->area
.height
;
441 if (!dock
->dock_apps
) {
442 STRUT_PARTIAL_SET(dock_strut
, 0, 0, 0, 0,
443 0, 0, 0, 0, 0, 0, 0, 0);
445 else if (config_dock_floating
|| config_dock_nostrut
) {
446 STRUT_PARTIAL_SET(dock_strut
, 0, 0, 0, 0,
447 0, 0, 0, 0, 0, 0, 0, 0);
450 switch (config_dock_pos
) {
451 case OB_DIRECTION_NORTHWEST
:
452 switch (config_dock_orient
) {
453 case OB_ORIENTATION_HORZ
:
454 STRUT_PARTIAL_SET(dock_strut
, 0, strh
, 0, 0,
455 0, 0, dock
->area
.x
, dock
->area
.x
456 + dock
->area
.width
- 1, 0, 0, 0, 0);
458 case OB_ORIENTATION_VERT
:
459 STRUT_PARTIAL_SET(dock_strut
, strw
, 0, 0, 0,
460 dock
->area
.y
, dock
->area
.y
461 + dock
->area
.height
- 1, 0, 0, 0, 0, 0, 0);
465 case OB_DIRECTION_NORTH
:
466 STRUT_PARTIAL_SET(dock_strut
, 0, strh
, 0, 0,
467 0, 0, dock
->area
.x
, dock
->area
.x
468 + dock
->area
.width
- 1, 0, 0, 0, 0);
470 case OB_DIRECTION_NORTHEAST
:
471 switch (config_dock_orient
) {
472 case OB_ORIENTATION_HORZ
:
473 STRUT_PARTIAL_SET(dock_strut
, 0, strh
, 0, 0,
474 0, 0, dock
->area
.x
, dock
->area
.x
475 + dock
->area
.width
-1, 0, 0, 0, 0);
477 case OB_ORIENTATION_VERT
:
478 STRUT_PARTIAL_SET(dock_strut
, 0, 0, strw
, 0,
479 0, 0, 0, 0, dock
->area
.y
, dock
->area
.y
480 + dock
->area
.height
- 1, 0, 0);
484 case OB_DIRECTION_WEST
:
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);
489 case OB_DIRECTION_EAST
:
490 STRUT_PARTIAL_SET(dock_strut
, 0, 0, strw
, 0,
491 0, 0, 0, 0, dock
->area
.y
, dock
->area
.y
492 + dock
->area
.height
- 1, 0, 0);
494 case OB_DIRECTION_SOUTHWEST
:
495 switch (config_dock_orient
) {
496 case OB_ORIENTATION_HORZ
:
497 STRUT_PARTIAL_SET(dock_strut
, 0, 0, 0, strh
,
498 0, 0, 0, 0, 0, 0, dock
->area
.x
, dock
->area
.x
499 + dock
->area
.width
- 1);
501 case OB_ORIENTATION_VERT
:
502 STRUT_PARTIAL_SET(dock_strut
, strw
, 0, 0, 0,
503 dock
->area
.y
, dock
->area
.y
504 + dock
->area
.height
- 1, 0, 0, 0, 0, 0, 0);
508 case OB_DIRECTION_SOUTH
:
509 STRUT_PARTIAL_SET(dock_strut
, 0, 0, 0, strh
,
510 0, 0, 0, 0, 0, 0, dock
->area
.x
, dock
->area
.x
511 + dock
->area
.width
- 1);
513 case OB_DIRECTION_SOUTHEAST
:
514 switch (config_dock_orient
) {
515 case OB_ORIENTATION_HORZ
:
516 STRUT_PARTIAL_SET(dock_strut
, 0, 0, 0, strh
,
517 0, 0, 0, 0, 0, 0, dock
->area
.x
,
518 dock
->area
.x
+ dock
->area
.width
- 1);
520 case OB_ORIENTATION_VERT
:
521 STRUT_PARTIAL_SET(dock_strut
, 0, 0, strw
, 0,
522 0, 0, 0, 0, dock
->area
.y
, dock
->area
.y
523 + dock
->area
.height
- 1, 0, 0);
530 /* not used for actually sizing shit */
531 dock
->area
.width
-= ob_rr_theme
->obwidth
* 2;
532 dock
->area
.height
-= ob_rr_theme
->obwidth
* 2;
534 if (dock
->dock_apps
) {
535 g_assert(dock
->area
.width
> 0);
536 g_assert(dock
->area
.height
> 0);
538 XMoveResizeWindow(obt_display
, dock
->frame
, dock
->area
.x
, dock
->area
.y
,
539 dock
->area
.width
, dock
->area
.height
);
541 RrPaint(dock
->a_frame
, dock
->frame
, dock
->area
.width
,
543 XMapWindow(obt_display
, dock
->frame
);
545 XUnmapWindow(obt_display
, dock
->frame
);
547 /* but they are useful outside of this function! but don't add it if the
548 dock is actually not visible */
549 if (dock
->dock_apps
) {
550 dock
->area
.width
+= ob_rr_theme
->obwidth
* 2;
551 dock
->area
.height
+= ob_rr_theme
->obwidth
* 2;
554 /* screen_resize() depends on this function to call screen_update_areas(),
555 so if this changes, also update screen_resize(). */
556 screen_update_areas();
559 void dock_app_configure(ObDockApp
*app
, gint w
, gint h
)
566 void dock_app_drag(ObDockApp
*app
, XMotionEvent
*e
)
568 ObDockApp
*over
= NULL
;
577 /* are we on top of the dock? */
578 if (!(x
>= dock
->area
.x
&&
580 x
< dock
->area
.x
+ dock
->area
.width
&&
581 y
< dock
->area
.y
+ dock
->area
.height
))
587 /* which dock app are we on top of? */
589 for (it
= dock
->dock_apps
; it
; it
= g_list_next(it
)) {
591 switch (config_dock_orient
) {
592 case OB_ORIENTATION_HORZ
:
593 if (x
>= over
->x
&& x
< over
->x
+ over
->w
)
596 case OB_ORIENTATION_VERT
:
597 if (y
>= over
->y
&& y
< over
->y
+ over
->h
)
601 /* dont go to it->next! */
604 if (!it
|| app
== over
) return;
609 switch (config_dock_orient
) {
610 case OB_ORIENTATION_HORZ
:
611 after
= (x
> over
->w
/ 2);
613 case OB_ORIENTATION_VERT
:
614 after
= (y
> over
->h
/ 2);
617 g_assert_not_reached();
620 /* remove before doing the it->next! */
621 dock
->dock_apps
= g_list_remove(dock
->dock_apps
, app
);
623 if (after
) it
= it
->next
;
625 dock
->dock_apps
= g_list_insert_before(dock
->dock_apps
, it
, app
);
629 static gboolean
hide_timeout(gpointer data
)
635 return FALSE
; /* don't repeat */
638 static gboolean
show_timeout(gpointer data
)
641 dock
->hidden
= FALSE
;
644 return FALSE
; /* don't repeat */
647 static void destroy_timeout(gpointer data
)
653 void dock_hide(gboolean hide
)
656 if (dock
->hidden
&& config_dock_hide
) {
657 show_timeout_id
= g_timeout_add_full(G_PRIORITY_DEFAULT
,
658 config_dock_show_delay
,
659 show_timeout
, &show_timeout_id
, destroy_timeout
);
660 } else if (!dock
->hidden
&& config_dock_hide
&& hide_timeout_id
) {
661 if (hide_timeout_id
) g_source_remove(hide_timeout_id
);
664 if (!dock
->hidden
&& config_dock_hide
) {
665 hide_timeout_id
= g_timeout_add_full(G_PRIORITY_DEFAULT
,
666 config_dock_hide_delay
,
667 hide_timeout
, &hide_timeout_id
, destroy_timeout
);
668 } else if (dock
->hidden
&& config_dock_hide
&& show_timeout_id
) {
669 if (show_timeout_id
) g_source_remove(show_timeout_id
);
674 void dock_get_area(Rect
*a
)
676 RECT_SET(*a
, dock
->area
.x
, dock
->area
.y
,
677 dock
->area
.width
, dock
->area
.height
);
680 void dock_raise_dock(void)
682 stacking_raise(DOCK_AS_WINDOW(dock
));
685 void dock_lower_dock(void)
687 stacking_lower(DOCK_AS_WINDOW(dock
));
690 ObDockApp
* dock_find_dockapp(Window xwin
)
692 return g_hash_table_lookup(dock
->dock_map
, &xwin
);