3 * Copyright (C) 2003 Tom Payne
4 * Copyright (C) 2003 Per Olofsson
5 * Copyright (C) 2004-2009 Tuomo Valkonen
7 * by Tom Payne <ion@tompayne.org>
8 * based on code by Per Olofsson <pelle@dsv.su.se>
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Lesser General Public
12 * License as published by the Free Software Foundation; either
13 * version 2.1 of the License, or (at your option) any later version.
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Lesser General Public License for more details.
20 * You should have received a copy of the GNU Lesser General Public
21 * License along with this library; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 * $Header: /home/twp/cvsroot/twp/ion/ion-devel-dock/dock.c,v 1.17 2003/12/21 11:59:48 twp Exp $
34 #include <X11/Xatom.h>
35 #include <X11/Xutil.h>
36 #include <X11/extensions/shape.h>
38 #include <libtu/objp.h>
39 #include <libtu/map.h>
40 #include <libtu/minmax.h>
41 #include <libextl/extl.h>
42 #include <libextl/readconfig.h>
43 #include <libmainloop/defer.h>
45 #include <ioncore/common.h>
46 #include <ioncore/clientwin.h>
47 #include <ioncore/eventh.h>
48 #include <ioncore/global.h>
49 #include <ioncore/manage.h>
50 #include <ioncore/names.h>
51 #include <ioncore/property.h>
52 #include <ioncore/resize.h>
53 #include <ioncore/window.h>
54 #include <ioncore/mplex.h>
55 #include <ioncore/saveload.h>
56 #include <ioncore/bindmaps.h>
57 #include <ioncore/regbind.h>
58 #include <ioncore/extlconv.h>
59 #include <ioncore/event.h>
60 #include <ioncore/resize.h>
61 #include <ioncore/sizehint.h>
62 #include <ioncore/basicpholder.h>
72 #define UNUSED __attribute__ ((unused))
82 #include "../version.h"
84 static const char *modname
="dock";
85 const char mod_dock_ion_api_version
[]=NOTION_API_VERSION
;
87 static WBindmap
*dock_bindmap
=NULL
;
94 INTRSTRUCT(WDockParam
);
98 DECLSTRUCT(WDockParam
){
101 const StringIntMap
*map
;
105 DECLSTRUCT(WDockApp
){
106 WDockApp
*next
, *prev
;
112 WRectangle tile_geom
;
113 WRectangle border_geom
;
118 WDock
*dock_next
, *dock_prev
;
131 static WDock
*docks
=NULL
;
136 /*{{{ Parameter conversion */
138 static void dock_param_extl_table_get(const WDockParam
*param
,
139 ExtlTab conftab
, int value
)
143 s
=stringintmap_key(param
->map
, value
, NULL
);
145 extl_table_sets_s(conftab
, param
->key
, s
);
151 static bool dock_param_do_set(const WDockParam
*param
, char *s
,
155 int i
=stringintmap_value(param
->map
, s
, -1);
157 warn_obj(modname
, "Invalid %s \"%s\"", param
->desc
, s
);
171 static bool dock_param_extl_table_set(const WDockParam
*param
, ExtlTab conftab
,
176 if(extl_table_gets_s(conftab
, param
->key
, &s
))
177 return dock_param_do_set(param
, s
, ret
);
184 static bool dock_param_brush_set(const WDockParam
*param
, GrBrush
*brush
,
189 if(grbrush_get_extra(brush
, param
->key
, 's', &s
))
190 return dock_param_do_set(param
, s
, ret
);
199 /*{{{ Parameter descriptions */
201 static const WDockParam dock_param_name
={
209 #define DOCK_HPOS_MASK 0x000f
210 #define DOCK_HPOS_LEFT 0x0000
211 #define DOCK_HPOS_CENTER 0x0001
212 #define DOCK_HPOS_RIGHT 0x0002
213 #define DOCK_VPOS_MASK 0x00f0
214 #define DOCK_VPOS_TOP 0x0000
215 #define DOCK_VPOS_MIDDLE 0x0010
216 #define DOCK_VPOS_BOTTOM 0x0020
219 static StringIntMap dock_pos_map
[]={
220 {"tl", DOCK_VPOS_TOP
|DOCK_HPOS_LEFT
},
221 {"tc", DOCK_VPOS_TOP
|DOCK_HPOS_CENTER
},
222 {"tr", DOCK_VPOS_TOP
|DOCK_HPOS_RIGHT
},
223 {"ml", DOCK_VPOS_MIDDLE
|DOCK_HPOS_LEFT
},
224 {"mc", DOCK_VPOS_MIDDLE
|DOCK_HPOS_CENTER
},
225 {"mr", DOCK_VPOS_MIDDLE
|DOCK_HPOS_RIGHT
},
226 {"bl", DOCK_VPOS_BOTTOM
|DOCK_HPOS_LEFT
},
227 {"bc", DOCK_VPOS_BOTTOM
|DOCK_HPOS_CENTER
},
228 {"br", DOCK_VPOS_BOTTOM
|DOCK_HPOS_RIGHT
},
232 static WDockParam dock_param_pos
={
236 DOCK_HPOS_LEFT
|DOCK_VPOS_BOTTOM
247 static StringIntMap dock_grow_map
[]={
248 {"up", DOCK_GROW_UP
},
249 {"down", DOCK_GROW_DOWN
},
250 {"left", DOCK_GROW_LEFT
},
251 {"right", DOCK_GROW_RIGHT
},
255 WDockParam dock_param_grow
={
263 static const WDockParam dock_param_is_auto
={
271 enum WDockOutlineStyle
{
272 DOCK_OUTLINE_STYLE_NONE
,
273 DOCK_OUTLINE_STYLE_ALL
,
274 DOCK_OUTLINE_STYLE_EACH
277 static StringIntMap dock_outline_style_map
[]={
278 {"none", DOCK_OUTLINE_STYLE_NONE
},
279 {"all", DOCK_OUTLINE_STYLE_ALL
},
280 {"each", DOCK_OUTLINE_STYLE_EACH
},
284 WDockParam dock_param_outline_style
={
287 dock_outline_style_map
,
288 DOCK_OUTLINE_STYLE_ALL
292 static const WDockParam dock_param_tile_width
={
299 static const WDockParam dock_param_tile_height
={
312 #define CLIENTWIN_WINPROP_POSITION "dockposition"
313 #define CLIENTWIN_WINPROP_BORDER "dockborder"
315 static WDockApp
*dock_find_dockapp(WDock
*dock
, WRegion
*reg
)
319 for(dockapp
=dock
->dockapps
; dockapp
!=NULL
; dockapp
=dockapp
->next
){
320 if(dockapp
->reg
==reg
){
330 static void dock_get_outline_style(WDock
*dock
, int *ret
)
333 *ret
=dock_param_outline_style
.dflt
;
334 if(dock
->brush
!=NULL
)
335 dock_param_brush_set(&dock_param_outline_style
, dock
->brush
, ret
);
342 /*{{{ Size calculation */
345 static void dock_get_tile_size(WDock
*dock
, WRectangle
*ret
)
347 ExtlTab tile_size_table
;
351 ret
->w
=dock_param_tile_width
.dflt
;
352 ret
->h
=dock_param_tile_height
.dflt
;
353 if(dock
->brush
==NULL
)
355 if(grbrush_get_extra(dock
->brush
, "tile_size", 't', &tile_size_table
)){
356 extl_table_gets_i(tile_size_table
, dock_param_tile_width
.key
, &ret
->w
);
357 extl_table_gets_i(tile_size_table
, dock_param_tile_height
.key
, &ret
->h
);
358 extl_unref_table(tile_size_table
);
364 static void dock_get_pos_grow(WDock
*dock
, int *pos
, int *grow
)
366 WMPlex
*mplex
=OBJ_CAST(REGION_PARENT(dock
), WMPlex
);
367 WRegion
*mplex_stdisp
;
368 WMPlexSTDispInfo din
;
371 mplex_get_stdisp(mplex
, &mplex_stdisp
, &din
);
372 if(mplex_stdisp
==(WRegion
*)dock
){
373 /* Ok, we're assigned as a status display for mplex, so
374 * get parameters from there.
376 *pos
=((din
.pos
==MPLEX_STDISP_TL
|| din
.pos
==MPLEX_STDISP_BL
)
379 | ((din
.pos
==MPLEX_STDISP_TL
|| din
.pos
==MPLEX_STDISP_TR
)
393 static void dock_reshape(WDock
*dock
)
397 if(!ioncore_g
.shape_extension
)
400 dock_get_outline_style(dock
, &outline_style
);
402 switch(outline_style
){
403 case DOCK_OUTLINE_STYLE_NONE
:
404 case DOCK_OUTLINE_STYLE_EACH
:
408 /* Start with an empty set */
409 XShapeCombineRectangles(ioncore_g
.dpy
, ((WWindow
*)dock
)->win
,
410 ShapeBounding
, 0, 0, NULL
, 0, ShapeSet
, 0);
412 /* Union with dockapp shapes */
413 for(dockapp
=dock
->dockapps
; dockapp
!=NULL
; dockapp
=dockapp
->next
){
414 WClientWin
*cwin
=OBJ_CAST(dockapp
->reg
, WClientWin
);
415 if(outline_style
==DOCK_OUTLINE_STYLE_EACH
416 && dockapp
->draw_border
){
417 /* Union with border shape */
418 XRectangle tile_rect
;
420 tile_rect
.x
=dockapp
->border_geom
.x
;
421 tile_rect
.y
=dockapp
->border_geom
.y
;
422 tile_rect
.width
=dockapp
->border_geom
.w
;
423 tile_rect
.height
=dockapp
->border_geom
.h
;
424 XShapeCombineRectangles(ioncore_g
.dpy
, ((WWindow
*)dock
)->win
,
425 ShapeBounding
, 0, 0, &tile_rect
, 1,
427 }else if(cwin
!=NULL
){
428 /* Union with dockapp shape */
432 XRectangle
*rects
=XShapeGetRectangles(ioncore_g
.dpy
, cwin
->win
,
433 ShapeBounding
, &count
,
436 WRectangle dockapp_geom
=REGION_GEOM(cwin
);
437 XShapeCombineRectangles(ioncore_g
.dpy
, ((WWindow
*)dock
)->win
,
439 dockapp_geom
.x
, dockapp_geom
.y
,
440 rects
, count
, ShapeUnion
, ordering
);
448 case DOCK_OUTLINE_STYLE_ALL
:
453 geom
=REGION_GEOM(dock
);
458 XShapeCombineRectangles(ioncore_g
.dpy
, ((WWindow
*)dock
)->win
,
459 ShapeBounding
, 0, 0, &rect
, 1, ShapeSet
, 0);
467 static void dock_arrange_dockapps(WDock
*dock
, const WRectangle
*bd_dockg
,
468 const WDockApp
*replace_this
,
471 GrBorderWidths dock_bdw
, dockapp_bdw
;
472 WDockApp dummy_copy
, *dockapp
;
473 int pos
, grow
, cur_coord
=0;
474 WRectangle dock_geom
;
476 dock
->arrange_called
=TRUE
;
478 dock_get_pos_grow(dock
, &pos
, &grow
);
480 /* Determine dock and dockapp border widths */
481 memset(&dock_bdw
, 0, sizeof(GrBorderWidths
));
482 memset(&dockapp_bdw
, 0, sizeof(GrBorderWidths
));
487 dock_get_outline_style(dock
, &outline_style
);
488 switch(outline_style
){
489 case DOCK_OUTLINE_STYLE_NONE
:
491 case DOCK_OUTLINE_STYLE_ALL
:
492 grbrush_get_border_widths(dock
->brush
, &dock_bdw
);
493 dockapp_bdw
.spacing
=dock_bdw
.spacing
;
495 case DOCK_OUTLINE_STYLE_EACH
:
496 grbrush_get_border_widths(dock
->brush
, &dockapp_bdw
);
501 dock_geom
.w
=bd_dockg
->w
-dock_bdw
.left
-dock_bdw
.right
;
502 dock_geom
.h
=bd_dockg
->h
-dock_bdw
.top
-dock_bdw
.bottom
;
504 /* Calculate initial co-ordinate for layout algorithm */
507 cur_coord
=dock_bdw
.top
+dock_geom
.h
;
510 cur_coord
=dock_bdw
.top
;
513 cur_coord
=dock_bdw
.left
+dock_geom
.w
;
515 case DOCK_GROW_RIGHT
:
516 cur_coord
=dock_bdw
.left
;
520 /* Arrange dockapps */
521 for(dockapp
=dock
->dockapps
; dockapp
!=NULL
; dockapp
=dockapp
->next
){
522 WDockApp
*da
=dockapp
;
524 if(replace_this
!=NULL
){
525 if(replace_this
==dockapp
){
533 /* Calculate first co-ordinate */
537 switch(pos
&DOCK_HPOS_MASK
){
541 case DOCK_HPOS_CENTER
:
542 da
->border_geom
.x
=(dock_geom
.w
-da
->border_geom
.w
)/2;
544 case DOCK_HPOS_RIGHT
:
545 da
->border_geom
.x
=dock_geom
.w
-da
->border_geom
.w
;
548 da
->border_geom
.x
+=dock_bdw
.left
;
551 case DOCK_GROW_RIGHT
:
552 switch(pos
&DOCK_VPOS_MASK
){
556 case DOCK_VPOS_MIDDLE
:
557 da
->border_geom
.y
=(dock_geom
.h
-da
->border_geom
.h
)/2;
559 case DOCK_VPOS_BOTTOM
:
560 da
->border_geom
.y
=dock_geom
.h
-da
->border_geom
.h
;
563 da
->border_geom
.y
+=dock_bdw
.top
;
567 /* Calculate second co-ordinate */
570 cur_coord
-=da
->border_geom
.h
;
571 da
->border_geom
.y
=cur_coord
;
572 cur_coord
-=dockapp_bdw
.spacing
;
575 da
->border_geom
.y
=cur_coord
;
576 cur_coord
+=da
->border_geom
.h
+dockapp_bdw
.spacing
;
579 cur_coord
-=da
->border_geom
.w
;
580 da
->border_geom
.x
=cur_coord
;
581 cur_coord
-=dockapp_bdw
.spacing
;
583 case DOCK_GROW_RIGHT
:
584 da
->border_geom
.x
=cur_coord
;
585 cur_coord
+=da
->border_geom
.w
+dockapp_bdw
.spacing
;
589 /* Calculate tile geom */
590 da
->tile_geom
.x
=da
->border_geom
.x
+dockapp_bdw
.left
;
591 da
->tile_geom
.y
=da
->border_geom
.y
+dockapp_bdw
.top
;
593 /* Calculate dockapp geom */
595 da
->geom
.x
=da
->tile_geom
.x
+(da
->tile_geom
.w
-da
->geom
.w
)/2;
596 da
->geom
.y
=da
->tile_geom
.y
+(da
->tile_geom
.h
-da
->geom
.h
)/2;
598 da
->geom
.x
=da
->tile_geom
.x
;
599 da
->geom
.y
=da
->tile_geom
.y
;
602 if(replace_this
==NULL
)
603 region_fit(da
->reg
, &(da
->geom
), REGION_FIT_BOUNDS
);
608 static void dock_set_minmax(WDock
*dock
, int grow
, const WRectangle
*g
)
612 if(grow
==DOCK_GROW_UP
|| grow
==DOCK_GROW_DOWN
){
622 static void dockapp_calc_preferred_size(WDock
*dock
, int grow
,
623 const WRectangle
*tile_size
,
626 int w
=da
->geom
.w
, h
=da
->geom
.h
;
628 if(grow
==DOCK_GROW_UP
|| grow
==DOCK_GROW_DOWN
){
629 da
->geom
.w
=minof(w
, tile_size
->w
);
633 da
->geom
.h
=minof(h
, tile_size
->h
);
636 region_size_hints_correct(da
->reg
, &(da
->geom
.w
), &(da
->geom
.h
), TRUE
);
641 static void dock_managed_rqgeom_(WDock
*dock
, WRegion
*reg
, int flags
,
642 const WRectangle
*geom
, WRectangle
*geomret
,
643 bool just_update_minmax
)
645 WDockApp
*dockapp
=NULL
, *thisdockapp
=NULL
, thisdockapp_copy
;
646 WRectangle dock_geom
, border_dock_geom
;
647 GrBorderWidths dock_bdw
, dockapp_bdw
;
648 int n_dockapps
=0, max_w
=1, max_h
=1, total_w
=0, total_h
=0;
650 WRectangle tile_size
;
652 /* dock_resize calls with NULL parameters. */
653 assert(reg
!=NULL
|| (geomret
==NULL
&& !(flags
®ION_RQGEOM_TRYONLY
)));
655 dock_get_pos_grow(dock
, &pos
, &grow
);
656 dock_get_tile_size(dock
, &tile_size
);
658 /* Determine dock and dockapp border widths */
659 memset(&dock_bdw
, 0, sizeof(GrBorderWidths
));
660 memset(&dockapp_bdw
, 0, sizeof(GrBorderWidths
));
665 dock_get_outline_style(dock
, &outline_style
);
666 switch(outline_style
){
667 case DOCK_OUTLINE_STYLE_NONE
:
669 case DOCK_OUTLINE_STYLE_ALL
:
670 grbrush_get_border_widths(dock
->brush
, &dock_bdw
);
671 dockapp_bdw
.spacing
=dock_bdw
.spacing
;
673 case DOCK_OUTLINE_STYLE_EACH
:
674 grbrush_get_border_widths(dock
->brush
, &dockapp_bdw
);
679 /* Calculate widths and heights */
680 for(dockapp
=dock
->dockapps
; dockapp
!=NULL
; dockapp
=dockapp
->next
){
681 WDockApp
*da
=dockapp
;
682 bool update
=!(flags
®ION_RQGEOM_TRYONLY
);
683 if(dockapp
->reg
==reg
){
685 if(flags
®ION_RQGEOM_TRYONLY
){
686 thisdockapp_copy
=*dockapp
;
687 thisdockapp_copy
.geom
=*geom
;
688 da
=&thisdockapp_copy
;
695 /* Calculcate preferred size */
696 dockapp_calc_preferred_size(dock
, grow
, &tile_size
, da
);
698 /* Determine whether dockapp should be placed on a tile */
699 da
->tile
=da
->geom
.w
<=tile_size
.w
&& da
->geom
.h
<=tile_size
.h
;
701 /* Calculate width and height */
703 da
->tile_geom
.w
=tile_size
.w
;
704 da
->tile_geom
.h
=tile_size
.h
;
706 da
->tile_geom
.w
=da
->geom
.w
;
707 da
->tile_geom
.h
=da
->geom
.h
;
710 /* Calculate border width and height */
711 da
->border_geom
.w
=dockapp_bdw
.left
+da
->tile_geom
.w
+dockapp_bdw
.right
;
712 da
->border_geom
.h
=dockapp_bdw
.top
+da
->tile_geom
.h
+dockapp_bdw
.right
;
715 /* Calculate maximum and accumulated widths and heights */
716 if(da
->border_geom
.w
>max_w
)
717 max_w
=da
->border_geom
.w
;
718 total_w
+=da
->border_geom
.w
+(n_dockapps
? dockapp_bdw
.spacing
: 0);
720 if(da
->border_geom
.h
>max_h
)
721 max_h
=da
->border_geom
.h
;
722 total_h
+=da
->border_geom
.h
+(n_dockapps
? dockapp_bdw
.spacing
: 0);
728 if(thisdockapp
==NULL
&& reg
!=NULL
){
729 warn("Requesting dockapp not found.");
731 *geomret
=REGION_GEOM(reg
);
735 /* Calculate width and height of dock */
739 case DOCK_GROW_RIGHT
:
751 dock_geom
.w
=tile_size
.w
;
752 dock_geom
.h
=tile_size
.h
;
755 border_dock_geom
.x
=REGION_GEOM(dock
).x
;
756 border_dock_geom
.y
=REGION_GEOM(dock
).y
;
757 border_dock_geom
.w
=dock_bdw
.left
+dock_geom
.w
+dock_bdw
.right
;
758 border_dock_geom
.h
=dock_bdw
.top
+dock_geom
.h
+dock_bdw
.bottom
;
760 /* Fit dock to new geom if required */
761 if(!(flags
®ION_RQGEOM_TRYONLY
)){
762 WRQGeomParams rq
=RQGEOMPARAMS_INIT
;
764 dock_set_minmax(dock
, grow
, &border_dock_geom
);
766 if(just_update_minmax
)
769 rq
.flags
=REGION_RQGEOM_WEAK_X
|REGION_RQGEOM_WEAK_Y
;
770 rq
.geom
=border_dock_geom
;
772 dock
->arrange_called
=FALSE
;
774 region_rqgeom((WRegion
*)dock
, &rq
, NULL
);
776 if(!dock
->arrange_called
)
777 dock_arrange_dockapps(dock
, ®ION_GEOM(dock
), NULL
, NULL
);
779 if(thisdockapp
!=NULL
&& geomret
!=NULL
)
780 *geomret
=thisdockapp
->geom
;
782 if(thisdockapp
!=NULL
&& geomret
!=NULL
){
783 dock_arrange_dockapps(dock
, ®ION_GEOM(dock
),
784 thisdockapp
, &thisdockapp_copy
);
785 *geomret
=thisdockapp_copy
.geom
;
790 static void dock_managed_rqgeom(WDock
*dock
, WRegion
*reg
,
791 const WRQGeomParams
*rq
,
794 dock_managed_rqgeom_(dock
, reg
, rq
->flags
, &rq
->geom
, geomret
, FALSE
);
798 void dock_size_hints(WDock
*dock
, WSizeHints
*hints
)
801 hints
->min_width
=dock
->min_w
;
802 hints
->min_height
=dock
->min_h
;
805 hints
->max_width
=dock
->max_w
;
806 hints
->max_height
=dock
->max_h
;
810 static bool dock_fitrep(WDock
*dock
, WWindow
*parent
, const WFitParams
*fp
)
812 if(!window_fitrep(&(dock
->win
), parent
, fp
))
815 dock_arrange_dockapps(dock
, &(fp
->g
), NULL
, NULL
);
817 if(ioncore_g
.shape_extension
)
824 static int dock_orientation(WDock
*dock
)
826 return ((dock
->grow
==DOCK_GROW_LEFT
|| dock
->grow
==DOCK_GROW_RIGHT
)
827 ? REGION_ORIENTATION_HORIZONTAL
828 : REGION_ORIENTATION_VERTICAL
);
838 static void dock_draw(WDock
*dock
, bool complete
)
843 if(dock
->brush
==NULL
)
848 g
.w
=REGION_GEOM(dock
).w
;
849 g
.h
=REGION_GEOM(dock
).h
;
851 grbrush_begin(dock
->brush
, &g
, (complete
? 0 : GRBRUSH_NO_CLEAR_OK
));
853 dock_get_outline_style(dock
, &outline_style
);
854 switch(outline_style
){
855 case DOCK_OUTLINE_STYLE_NONE
:
857 case DOCK_OUTLINE_STYLE_ALL
:
859 WRectangle geom
=REGION_GEOM(dock
);
861 grbrush_draw_border(dock
->brush
, &geom
);
864 case DOCK_OUTLINE_STYLE_EACH
:
867 for(dockapp
=dock
->dockapps
; dockapp
!=NULL
;
868 dockapp
=dockapp
->next
){
869 grbrush_draw_border(dock
->brush
, &dockapp
->tile_geom
);
875 grbrush_end(dock
->brush
);
880 * Resizes and refreshes \var{dock}.
883 void dock_resize(WDock
*dock
)
885 dock_managed_rqgeom_(dock
, NULL
, 0, NULL
, NULL
, FALSE
);
886 dock_draw(dock
, TRUE
);
890 static void dock_brush_release(WDock
*dock
)
894 grbrush_release(dock
->brush
);
901 static void dock_brush_get(WDock
*dock
)
904 dock_brush_release(dock
);
905 dock
->brush
=gr_get_brush(((WWindow
*)dock
)->win
,
906 region_rootwin_of((WRegion
*)dock
),
911 static void dock_updategr(WDock
*dock
)
913 dock_brush_get(dock
);
923 static void mplexpos(int pos
, int *mpos
)
925 int hp
=pos
&DOCK_HPOS_MASK
, vp
=pos
&DOCK_VPOS_MASK
;
928 p
=(vp
!=DOCK_VPOS_MIDDLE
930 ? (hp
!=DOCK_HPOS_CENTER
931 ? (hp
==DOCK_HPOS_RIGHT
935 : (hp
!=DOCK_HPOS_CENTER
936 ? (hp
==DOCK_HPOS_RIGHT
943 warn("Invalid dock position while as stdisp.");
949 static void mplexszplcy(int pos
, WSizePolicy
*szplcy
)
951 int hp
=pos
&DOCK_HPOS_MASK
, vp
=pos
&DOCK_VPOS_MASK
;
954 p
=(vp
!=DOCK_VPOS_MIDDLE
956 ? (hp
!=DOCK_HPOS_CENTER
957 ? (hp
==DOCK_HPOS_RIGHT
958 ? SIZEPOLICY_GRAVITY_NORTHEAST
959 : SIZEPOLICY_GRAVITY_NORTHWEST
)
960 : SIZEPOLICY_GRAVITY_NORTH
)
961 : (hp
!=DOCK_HPOS_CENTER
962 ? (hp
==DOCK_HPOS_RIGHT
963 ? SIZEPOLICY_GRAVITY_SOUTHEAST
964 : SIZEPOLICY_GRAVITY_SOUTHWEST
)
965 : SIZEPOLICY_GRAVITY_SOUTH
))
966 : (hp
!=DOCK_HPOS_CENTER
967 ? (hp
==DOCK_HPOS_RIGHT
968 ? SIZEPOLICY_GRAVITY_EAST
969 : SIZEPOLICY_GRAVITY_WEST
)
970 : SIZEPOLICY_GRAVITY_CENTER
));
976 static void dock_do_set(WDock
*dock
, ExtlTab conftab
, bool resize
)
984 if(extl_table_gets_s(conftab
, dock_param_name
.key
, &s
)){
985 if(!region_set_name((WRegion
*)dock
, s
)){
986 warn_obj(modname
, "Can't set name to \"%s\"", s
);
991 if(extl_table_gets_b(conftab
, "save", &save
))
994 if(dock_param_extl_table_set(&dock_param_pos
, conftab
, &dock
->pos
))
997 if(dock_param_extl_table_set(&dock_param_grow
, conftab
, &dock
->grow
))
1000 if(extl_table_gets_b(conftab
, dock_param_is_auto
.key
, &b
))
1003 if(resize
&& (growset
|| posset
)){
1004 WMPlex
*par
=OBJ_CAST(REGION_PARENT(dock
), WMPlex
);
1005 WRegion
*stdisp
=NULL
;
1006 WMPlexSTDispInfo din
;
1009 mplex_get_stdisp(par
, &stdisp
, &din
);
1010 din
.fullsize
=FALSE
; /* not supported. */
1011 if(stdisp
==(WRegion
*)dock
){
1013 mplexpos(dock
->pos
, &din
.pos
);
1015 /* Update min/max first */
1016 dock_managed_rqgeom_(dock
, NULL
, 0, NULL
, NULL
, TRUE
);
1018 mplex_set_stdisp(par
, (WRegion
*)dock
, &din
);
1019 }else if((WRegion
*)par
==REGION_MANAGER(dock
)){
1021 mplexszplcy(dock
->pos
, &szplcy
);
1022 mplex_set_szplcy(par
, (WRegion
*)dock
, szplcy
);
1032 * Configure \var{dock}. \var{conftab} is a table of key/value pairs:
1034 * \begin{tabularx}{\linewidth}{llX}
1035 * \tabhead{Key & Values & Description}
1036 * \var{name} & string & Name of dock \\
1037 * \var{pos} & string in $\{t,m,b\}\times\{t,c,b\}$ & Dock position.
1038 * Can only be used in floating mode. \\
1039 * \var{grow} & up/down/left/right &
1040 * Growth direction where new dockapps are added. Also
1041 * sets orientation for dock when working as WMPlex status
1042 * display (see \fnref{WMPlex.set_stdisp}). \\
1043 * \var{is_auto} & bool &
1044 * Should \var{dock} automatically manage new dockapps? \\
1047 * Any parameters not explicitly set in \var{conftab} will be left unchanged.
1050 void dock_set(WDock
*dock
, ExtlTab conftab
)
1052 dock_do_set(dock
, conftab
, TRUE
);
1056 static void dock_do_get(WDock
*dock
, ExtlTab conftab
)
1058 extl_table_sets_s(conftab
, dock_param_name
.key
,
1059 region_name((WRegion
*)dock
));
1060 dock_param_extl_table_get(&dock_param_pos
, conftab
, dock
->pos
);
1061 dock_param_extl_table_get(&dock_param_grow
, conftab
, dock
->grow
);
1062 extl_table_sets_b(conftab
, dock_param_is_auto
.key
, dock
->is_auto
);
1063 extl_table_sets_b(conftab
, "save", dock
->save
);
1068 * Get \var{dock}'s configuration table. See \fnref{WDock.set} for a
1069 * description of the table.
1073 ExtlTab
dock_get(WDock
*dock
)
1077 conftab
=extl_create_table();
1078 dock_do_get(dock
, conftab
);
1086 /*{{{ Init/deinit */
1089 static bool dock_init(WDock
*dock
, WWindow
*parent
, const WFitParams
*fp
)
1093 dock
->pos
=dock_param_pos
.dflt
;
1094 dock
->grow
=dock_param_grow
.dflt
;
1095 dock
->is_auto
=dock_param_is_auto
.dflt
;
1097 dock
->dockapps
=NULL
;
1102 dock
->arrange_called
=FALSE
;
1106 if(!window_init((WWindow
*)dock
, parent
, &fp2
, "WDock"))
1109 region_add_bindmap((WRegion
*)dock
, dock_bindmap
);
1111 window_select_input(&(dock
->win
), IONCORE_EVENTMASK_CWINMGR
);
1113 dock_brush_get(dock
);
1115 LINK_ITEM(docks
, dock
, dock_next
, dock_prev
);
1121 static WDock
*create_dock(WWindow
*parent
, const WFitParams
*fp
)
1123 CREATEOBJ_IMPL(WDock
, dock
, (p
, parent
, fp
));
1127 static void dock_deinit(WDock
*dock
)
1129 while(dock
->dockapps
!=NULL
)
1130 destroy_obj((Obj
*)dock
->dockapps
->reg
);
1132 UNLINK_ITEM(docks
, dock
, dock_next
, dock_prev
);
1134 dock_brush_release(dock
);
1136 window_deinit((WWindow
*) dock
);
1141 WDock
*mod_dock_create(ExtlTab tab
)
1144 bool floating
=FALSE
;
1146 WScreen
*screen
=NULL
;
1148 WRegion
*stdisp
=NULL
;
1149 WMPlexSTDispInfo din
;
1152 if(extl_table_gets_s(tab
, "mode", &mode
)){
1153 if(strcmp(mode
, "floating")==0){
1155 }else if(strcmp(mode
, "embedded")!=0){
1156 warn("Invalid dock mode.");
1163 extl_table_gets_i(tab
, "screen", &screenid
);
1164 screen
=ioncore_find_screen_id(screenid
);
1166 warn("Screen %d does not exist.", screenid
);
1170 for(dock
=docks
; dock
; dock
=dock
->dock_next
){
1171 if(region_screen_of((WRegion
*)dock
)==screen
){
1172 warn("Screen %d already has a dock. Refusing to create another.",
1179 mplex_get_stdisp((WMPlex
*)screen
, &stdisp
, &din
);
1180 if(stdisp
!=NULL
&& !extl_table_is_bool_set(tab
, "force")){
1181 warn("Screen %d already has an stdisp. Refusing to add embedded "
1187 /* Create the dock */
1188 fp
.mode
=REGION_FIT_BOUNDS
|REGION_FIT_WHATEVER
;
1194 dock
=create_dock((WWindow
*)screen
, &fp
);
1197 warn("Failed to create dock.");
1202 /* Get parameters */
1204 dock_do_set(dock
, tab
, FALSE
);
1206 /* Calculate min/max size */
1207 dock_managed_rqgeom_(dock
, NULL
, 0, NULL
, NULL
, TRUE
);
1211 WMPlexAttachParams par
=MPLEXATTACHPARAMS_INIT
;
1212 WRegionAttachData data
;
1214 par
.flags
=(MPLEX_ATTACH_UNNUMBERED
1215 |MPLEX_ATTACH_SIZEPOLICY
1217 |MPLEX_ATTACH_PASSIVE
);
1219 par
.geom
.w
=dock
->min_w
;
1220 par
.geom
.h
=dock
->min_h
;
1224 mplexszplcy(dock
->pos
, &par
.szplcy
);
1226 if(extl_table_is_bool_set(tab
, "floating_hidden"))
1227 par
.flags
|=MPLEX_ATTACH_HIDDEN
;
1229 data
.type
=REGION_ATTACH_REPARENT
;
1230 data
.u
.reg
=(WRegion
*)dock
;
1232 if(mplex_do_attach((WMPlex
*)screen
, &par
, &data
))
1235 mplexpos(dock
->pos
, &din
.pos
);
1236 din
.fullsize
=FALSE
; /* not supported */
1237 if(mplex_set_stdisp((WMPlex
*)screen
, (WRegion
*)dock
, &din
))
1241 /* Failed to attach. */
1242 warn("Failed to attach dock to screen.");
1243 destroy_obj((Obj
*)dock
);
1255 * Toggle floating docks on \var{mplex}.
1258 void mod_dock_set_floating_shown_on(WMPlex
*mplex
, const char *how
)
1260 int setpar
=libtu_setparam_invert(libtu_string_to_setparam(how
));
1263 for(dock
=docks
; dock
; dock
=dock
->dock_next
){
1264 if(REGION_MANAGER(dock
)==(WRegion
*)mplex
)
1265 mplex_set_hidden(mplex
, (WRegion
*)dock
, setpar
);
1276 ExtlTab
dock_get_configuration(WDock
*dock
)
1280 if(dock
->save
==FALSE
)
1281 return extl_table_none();
1283 tab
=region_get_base_configuration((WRegion
*)dock
);
1284 dock_do_get(dock
, tab
);
1290 WRegion
*dock_load(WWindow
*par
, const WFitParams
*fp
, ExtlTab tab
)
1292 WDock
*dock
=create_dock(par
, fp
);
1294 dock_set(dock
, tab
);
1295 dock_fitrep(dock
, NULL
, fp
);
1298 return (WRegion
*)dock
;
1305 /*{{{ Client window management setup */
1308 static bool dock_do_attach_final(WDock
*dock
, WRegion
*reg
, void *unused
)
1310 WDockApp
*dockapp
, *before_dockapp
;
1312 bool draw_border
=TRUE
;
1315 /* Create and initialise a new WDockApp struct */
1316 dockapp
=ALLOC(WDockApp
);
1321 if(OBJ_IS(reg
, WClientWin
)){
1322 ExtlTab proptab
=((WClientWin
*)reg
)->proptab
;
1323 extl_table_gets_b(proptab
, CLIENTWIN_WINPROP_BORDER
, &draw_border
);
1324 extl_table_gets_i(proptab
, CLIENTWIN_WINPROP_POSITION
, &pos
);
1328 dockapp
->draw_border
=draw_border
;
1330 dockapp
->tile
=FALSE
;
1332 /* Insert the dockapp at the correct relative position */
1333 before_dockapp
=dock
->dockapps
;
1334 for(before_dockapp
=dock
->dockapps
;
1335 before_dockapp
!=NULL
&& dockapp
->pos
>=before_dockapp
->pos
;
1336 before_dockapp
=before_dockapp
->next
){
1339 if(before_dockapp
!=NULL
){
1340 LINK_ITEM_BEFORE(dock
->dockapps
, before_dockapp
, dockapp
, next
, prev
);
1342 LINK_ITEM(dock
->dockapps
, dockapp
, next
, prev
);
1345 region_set_manager(reg
, (WRegion
*)dock
);
1347 geom
=REGION_GEOM(reg
);
1348 dock_managed_rqgeom_(dock
, reg
,
1349 REGION_RQGEOM_WEAK_X
|REGION_RQGEOM_WEAK_Y
,
1350 &geom
, NULL
, FALSE
);
1359 static WRegion
*dock_do_attach(WDock
*dock
, WRegionAttachData
*data
)
1362 dock_get_tile_size(dock
, &(fp
.g
));
1365 fp
.mode
=REGION_FIT_WHATEVER
|REGION_FIT_BOUNDS
;
1367 return region_attach_helper((WRegion
*)dock
, (WWindow
*)dock
, &fp
,
1368 (WRegionDoAttachFn
*)dock_do_attach_final
,
1374 * Attach \var{reg} to \var{dock}.
1377 bool dock_attach(WDock
*dock
, WRegion
*reg
)
1379 WRegionAttachData data
;
1381 data
.type
=REGION_ATTACH_REPARENT
;
1384 return (dock_do_attach(dock
, &data
)!=NULL
);
1388 static bool dock_handle_drop(WDock
*dock
, int x
, int y
,
1391 return dock_attach(dock
, dropped
);
1395 static WRegion
*dock_ph_handler(WDock
*dock
, int flags
, WRegionAttachData
*data
)
1397 return dock_do_attach(dock
, data
);
1401 static WPHolder
*dock_managed_get_pholder(WDock
*dock
, WRegion
*mgd
)
1403 return (WPHolder
*)create_basicpholder((WRegion
*)dock
,
1404 ((WBasicPHolderHandler
*)
1409 static WPHolder
*dock_prepare_manage(WDock
*dock
, const WClientWin
*cwin
,
1410 const WManageParams
*param UNUSED
,
1413 if(!MANAGE_PRIORITY_OK(priority
, MANAGE_PRIORITY_LOW
))
1416 return (WPHolder
*)create_basicpholder((WRegion
*)dock
,
1417 ((WBasicPHolderHandler
*)
1422 static void dock_managed_remove(WDock
*dock
, WRegion
*reg
)
1425 WDockApp
*dockapp
=dock_find_dockapp(dock
, reg
);
1430 UNLINK_ITEM(dock
->dockapps
, dockapp
, next
, prev
);
1433 region_unset_manager(reg
, (WRegion
*)dock
);
1439 static bool dock_clientwin_is_dockapp(WClientWin
*cwin
,
1440 const WManageParams
*param
)
1442 bool is_dockapp
=FALSE
;
1444 /* First, inspect the WManageParams.dockapp parameter */
1449 /* Second, inspect the _NET_WM_WINDOW_TYPE property */
1451 static Atom atom__net_wm_window_type
=None
;
1452 static Atom atom__net_wm_window_type_dock
=None
;
1453 Atom actual_type
=None
;
1455 unsigned long nitems
;
1456 unsigned long bytes_after
;
1457 unsigned char *prop
;
1459 if(atom__net_wm_window_type
==None
){
1460 atom__net_wm_window_type
=XInternAtom(ioncore_g
.dpy
,
1461 "_NET_WM_WINDOW_TYPE",
1464 if(atom__net_wm_window_type_dock
==None
){
1465 atom__net_wm_window_type_dock
=XInternAtom(ioncore_g
.dpy
,
1466 "_NET_WM_WINDOW_TYPE_DOCK",
1469 if(XGetWindowProperty(ioncore_g
.dpy
, cwin
->win
, atom__net_wm_window_type
,
1470 0, sizeof(Atom
), False
, XA_ATOM
, &actual_type
,
1471 &actual_format
, &nitems
, &bytes_after
, &prop
)
1473 if(actual_type
==XA_ATOM
&& nitems
>=1
1474 && *(Atom
*)prop
==atom__net_wm_window_type_dock
){
1481 /* Third, inspect the WM_CLASS property */
1486 p
=xwindow_get_text_property(cwin
->win
, XA_WM_CLASS
, &n
);
1488 if(n
>=2 && strcmp(p
[1], "DockApp")==0){
1495 /* Fourth, inspect the _KDE_NET_WM_SYSTEM_TRAY_WINDOW_FOR property */
1497 static Atom atom__kde_net_wm_system_tray_window_for
=None
;
1498 Atom actual_type
=None
;
1500 unsigned long nitems
;
1501 unsigned long bytes_after
;
1502 unsigned char *prop
;
1504 if(atom__kde_net_wm_system_tray_window_for
==None
){
1505 atom__kde_net_wm_system_tray_window_for
=XInternAtom(ioncore_g
.dpy
,
1506 "_KDE_NET_WM_SYSTEM_TRAY_WINDOW_FOR",
1509 if(XGetWindowProperty(ioncore_g
.dpy
, cwin
->win
,
1510 atom__kde_net_wm_system_tray_window_for
, 0,
1511 sizeof(Atom
), False
, AnyPropertyType
,
1512 &actual_type
, &actual_format
, &nitems
,
1513 &bytes_after
, &prop
)==Success
){
1514 if(actual_type
!=None
){
1526 static WDock
*dock_find_suitable_dock(WClientWin
*cwin
,
1527 const WManageParams
*param
)
1531 for(dock
=docks
; dock
; dock
=dock
->dock_next
){
1534 if(!region_same_rootwin((WRegion
*)dock
, (WRegion
*)cwin
))
1543 static bool clientwin_do_manage_hook(WClientWin
*cwin
, const WManageParams
*param
)
1547 if(!dock_clientwin_is_dockapp(cwin
, param
)){
1551 dock
=dock_find_suitable_dock(cwin
, param
);
1556 return region_manage_clientwin((WRegion
*)dock
, cwin
, param
,
1557 MANAGE_PRIORITY_NONE
);
1564 /*{{{ Module init/deinit */
1567 bool mod_dock_init()
1570 if(!ioncore_register_regclass(&CLASSDESCR(WDock
),
1571 (WRegionLoadCreateFn
*)dock_load
)){
1575 if(!mod_dock_register_exports()){
1576 ioncore_unregister_regclass(&CLASSDESCR(WDock
));
1580 dock_bindmap
=ioncore_alloc_bindmap("WDock", NULL
);
1581 if(dock_bindmap
==NULL
){
1582 warn("Unable to allocate dock bindmap.");
1583 mod_dock_unregister_exports();
1584 ioncore_unregister_regclass(&CLASSDESCR(WDock
));
1587 extl_read_config("cfg_dock", NULL
, TRUE
);
1589 hook_add(clientwin_do_manage_alt
,
1590 (WHookDummy
*)clientwin_do_manage_hook
);
1597 void mod_dock_deinit()
1601 ioncore_unregister_regclass(&CLASSDESCR(WDock
));
1603 hook_remove(clientwin_do_manage_alt
,
1604 (WHookDummy
*)clientwin_do_manage_hook
);
1608 WDock
*next
=dock
->dock_next
;
1609 destroy_obj((Obj
*)dock
);
1613 mod_dock_unregister_exports();
1615 if(dock_bindmap
!=NULL
){
1616 ioncore_free_bindmap("WDock", dock_bindmap
);
1625 /*{{{ WDock class description and dynfun list */
1628 static DynFunTab dock_dynfuntab
[]={
1629 {window_draw
, dock_draw
},
1630 {region_updategr
, dock_updategr
},
1631 {region_managed_rqgeom
, dock_managed_rqgeom
},
1632 {(DynFun
*)region_prepare_manage
, (DynFun
*)dock_prepare_manage
},
1633 {region_managed_remove
, dock_managed_remove
},
1634 {(DynFun
*)region_get_configuration
, (DynFun
*)dock_get_configuration
},
1635 {region_size_hints
, dock_size_hints
},
1636 {(DynFun
*)region_fitrep
, (DynFun
*)dock_fitrep
},
1637 {(DynFun
*)region_orientation
, (DynFun
*)dock_orientation
},
1638 {(DynFun
*)region_handle_drop
, (DynFun
*)dock_handle_drop
},
1640 {(DynFun
*)region_managed_get_pholder
,
1641 (DynFun
*)dock_managed_get_pholder
},
1647 IMPLCLASS(WDock
, WWindow
, dock_deinit
, dock_dynfuntab
);