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 parent_geom
, 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
;
651 WWindow
*par
=REGION_PARENT(dock
);
653 /* dock_resize calls with NULL parameters. */
654 assert(reg
!=NULL
|| (geomret
==NULL
&& !(flags
®ION_RQGEOM_TRYONLY
)));
656 dock_get_pos_grow(dock
, &pos
, &grow
);
658 /* Determine parent and tile geoms */
662 parent_geom
.w
=REGION_GEOM(par
).w
;
663 parent_geom
.h
=REGION_GEOM(par
).h
;
665 /* Should not happen in normal operation. */
670 dock_get_tile_size(dock
, &tile_size
);
672 /* Determine dock and dockapp border widths */
673 memset(&dock_bdw
, 0, sizeof(GrBorderWidths
));
674 memset(&dockapp_bdw
, 0, sizeof(GrBorderWidths
));
679 dock_get_outline_style(dock
, &outline_style
);
680 switch(outline_style
){
681 case DOCK_OUTLINE_STYLE_NONE
:
683 case DOCK_OUTLINE_STYLE_ALL
:
684 grbrush_get_border_widths(dock
->brush
, &dock_bdw
);
685 dockapp_bdw
.spacing
=dock_bdw
.spacing
;
687 case DOCK_OUTLINE_STYLE_EACH
:
688 grbrush_get_border_widths(dock
->brush
, &dockapp_bdw
);
693 /* Calculate widths and heights */
694 for(dockapp
=dock
->dockapps
; dockapp
!=NULL
; dockapp
=dockapp
->next
){
695 WDockApp
*da
=dockapp
;
696 bool update
=!(flags
®ION_RQGEOM_TRYONLY
);
697 if(dockapp
->reg
==reg
){
699 if(flags
®ION_RQGEOM_TRYONLY
){
700 thisdockapp_copy
=*dockapp
;
701 thisdockapp_copy
.geom
=*geom
;
702 da
=&thisdockapp_copy
;
709 /* Calculcate preferred size */
710 dockapp_calc_preferred_size(dock
, grow
, &tile_size
, da
);
712 /* Determine whether dockapp should be placed on a tile */
713 da
->tile
=da
->geom
.w
<=tile_size
.w
&& da
->geom
.h
<=tile_size
.h
;
715 /* Calculate width and height */
717 da
->tile_geom
.w
=tile_size
.w
;
718 da
->tile_geom
.h
=tile_size
.h
;
720 da
->tile_geom
.w
=da
->geom
.w
;
721 da
->tile_geom
.h
=da
->geom
.h
;
724 /* Calculate border width and height */
725 da
->border_geom
.w
=dockapp_bdw
.left
+da
->tile_geom
.w
+dockapp_bdw
.right
;
726 da
->border_geom
.h
=dockapp_bdw
.top
+da
->tile_geom
.h
+dockapp_bdw
.right
;
729 /* Calculate maximum and accumulated widths and heights */
730 if(da
->border_geom
.w
>max_w
)
731 max_w
=da
->border_geom
.w
;
732 total_w
+=da
->border_geom
.w
+(n_dockapps
? dockapp_bdw
.spacing
: 0);
734 if(da
->border_geom
.h
>max_h
)
735 max_h
=da
->border_geom
.h
;
736 total_h
+=da
->border_geom
.h
+(n_dockapps
? dockapp_bdw
.spacing
: 0);
742 if(thisdockapp
==NULL
&& reg
!=NULL
){
743 warn("Requesting dockapp not found.");
745 *geomret
=REGION_GEOM(reg
);
749 /* Calculate width and height of dock */
753 case DOCK_GROW_RIGHT
:
765 dock_geom
.w
=tile_size
.w
;
766 dock_geom
.h
=tile_size
.h
;
769 border_dock_geom
.x
=REGION_GEOM(dock
).x
;
770 border_dock_geom
.y
=REGION_GEOM(dock
).y
;
771 border_dock_geom
.w
=dock_bdw
.left
+dock_geom
.w
+dock_bdw
.right
;
772 border_dock_geom
.h
=dock_bdw
.top
+dock_geom
.h
+dock_bdw
.bottom
;
774 /* Fit dock to new geom if required */
775 if(!(flags
®ION_RQGEOM_TRYONLY
)){
776 WRQGeomParams rq
=RQGEOMPARAMS_INIT
;
778 dock_set_minmax(dock
, grow
, &border_dock_geom
);
780 if(just_update_minmax
)
783 rq
.flags
=REGION_RQGEOM_WEAK_X
|REGION_RQGEOM_WEAK_Y
;
784 rq
.geom
=border_dock_geom
;
786 dock
->arrange_called
=FALSE
;
788 region_rqgeom((WRegion
*)dock
, &rq
, NULL
);
790 if(!dock
->arrange_called
)
791 dock_arrange_dockapps(dock
, ®ION_GEOM(dock
), NULL
, NULL
);
793 if(thisdockapp
!=NULL
&& geomret
!=NULL
)
794 *geomret
=thisdockapp
->geom
;
796 if(thisdockapp
!=NULL
&& geomret
!=NULL
){
797 dock_arrange_dockapps(dock
, ®ION_GEOM(dock
),
798 thisdockapp
, &thisdockapp_copy
);
799 *geomret
=thisdockapp_copy
.geom
;
804 static void dock_managed_rqgeom(WDock
*dock
, WRegion
*reg
,
805 const WRQGeomParams
*rq
,
808 dock_managed_rqgeom_(dock
, reg
, rq
->flags
, &rq
->geom
, geomret
, FALSE
);
812 void dock_size_hints(WDock
*dock
, WSizeHints
*hints
)
815 hints
->min_width
=dock
->min_w
;
816 hints
->min_height
=dock
->min_h
;
819 hints
->max_width
=dock
->max_w
;
820 hints
->max_height
=dock
->max_h
;
824 static bool dock_fitrep(WDock
*dock
, WWindow
*parent
, const WFitParams
*fp
)
826 if(!window_fitrep(&(dock
->win
), parent
, fp
))
829 dock_arrange_dockapps(dock
, &(fp
->g
), NULL
, NULL
);
831 if(ioncore_g
.shape_extension
)
838 static int dock_orientation(WDock
*dock
)
840 return ((dock
->grow
==DOCK_GROW_LEFT
|| dock
->grow
==DOCK_GROW_RIGHT
)
841 ? REGION_ORIENTATION_HORIZONTAL
842 : REGION_ORIENTATION_VERTICAL
);
852 static void dock_draw(WDock
*dock
, bool complete
)
857 if(dock
->brush
==NULL
)
862 g
.w
=REGION_GEOM(dock
).w
;
863 g
.h
=REGION_GEOM(dock
).h
;
865 grbrush_begin(dock
->brush
, &g
, (complete
? 0 : GRBRUSH_NO_CLEAR_OK
));
867 dock_get_outline_style(dock
, &outline_style
);
868 switch(outline_style
){
869 case DOCK_OUTLINE_STYLE_NONE
:
871 case DOCK_OUTLINE_STYLE_ALL
:
873 WRectangle geom
=REGION_GEOM(dock
);
875 grbrush_draw_border(dock
->brush
, &geom
);
878 case DOCK_OUTLINE_STYLE_EACH
:
881 for(dockapp
=dock
->dockapps
; dockapp
!=NULL
;
882 dockapp
=dockapp
->next
){
883 grbrush_draw_border(dock
->brush
, &dockapp
->tile_geom
);
889 grbrush_end(dock
->brush
);
894 * Resizes and refreshes \var{dock}.
897 void dock_resize(WDock
*dock
)
899 dock_managed_rqgeom_(dock
, NULL
, 0, NULL
, NULL
, FALSE
);
900 dock_draw(dock
, TRUE
);
904 static void dock_brush_release(WDock
*dock
)
908 grbrush_release(dock
->brush
);
915 static void dock_brush_get(WDock
*dock
)
918 dock_brush_release(dock
);
919 dock
->brush
=gr_get_brush(((WWindow
*)dock
)->win
,
920 region_rootwin_of((WRegion
*)dock
),
925 static void dock_updategr(WDock
*dock
)
927 dock_brush_get(dock
);
937 static void mplexpos(int pos
, int *mpos
)
939 int hp
=pos
&DOCK_HPOS_MASK
, vp
=pos
&DOCK_VPOS_MASK
;
942 p
=(vp
!=DOCK_VPOS_MIDDLE
944 ? (hp
!=DOCK_HPOS_CENTER
945 ? (hp
==DOCK_HPOS_RIGHT
949 : (hp
!=DOCK_HPOS_CENTER
950 ? (hp
==DOCK_HPOS_RIGHT
957 warn("Invalid dock position while as stdisp.");
963 static void mplexszplcy(int pos
, WSizePolicy
*szplcy
)
965 int hp
=pos
&DOCK_HPOS_MASK
, vp
=pos
&DOCK_VPOS_MASK
;
968 p
=(vp
!=DOCK_VPOS_MIDDLE
970 ? (hp
!=DOCK_HPOS_CENTER
971 ? (hp
==DOCK_HPOS_RIGHT
972 ? SIZEPOLICY_GRAVITY_NORTHEAST
973 : SIZEPOLICY_GRAVITY_NORTHWEST
)
974 : SIZEPOLICY_GRAVITY_NORTH
)
975 : (hp
!=DOCK_HPOS_CENTER
976 ? (hp
==DOCK_HPOS_RIGHT
977 ? SIZEPOLICY_GRAVITY_SOUTHEAST
978 : SIZEPOLICY_GRAVITY_SOUTHWEST
)
979 : SIZEPOLICY_GRAVITY_SOUTH
))
980 : (hp
!=DOCK_HPOS_CENTER
981 ? (hp
==DOCK_HPOS_RIGHT
982 ? SIZEPOLICY_GRAVITY_EAST
983 : SIZEPOLICY_GRAVITY_WEST
)
984 : SIZEPOLICY_GRAVITY_CENTER
));
990 static void dock_do_set(WDock
*dock
, ExtlTab conftab
, bool resize
)
998 if(extl_table_gets_s(conftab
, dock_param_name
.key
, &s
)){
999 if(!region_set_name((WRegion
*)dock
, s
)){
1000 warn_obj(modname
, "Can't set name to \"%s\"", s
);
1005 if(extl_table_gets_b(conftab
, "save", &save
))
1008 if(dock_param_extl_table_set(&dock_param_pos
, conftab
, &dock
->pos
))
1011 if(dock_param_extl_table_set(&dock_param_grow
, conftab
, &dock
->grow
))
1014 if(extl_table_gets_b(conftab
, dock_param_is_auto
.key
, &b
))
1017 if(resize
&& (growset
|| posset
)){
1018 WMPlex
*par
=OBJ_CAST(REGION_PARENT(dock
), WMPlex
);
1019 WRegion
*stdisp
=NULL
;
1020 WMPlexSTDispInfo din
;
1023 mplex_get_stdisp(par
, &stdisp
, &din
);
1024 din
.fullsize
=FALSE
; /* not supported. */
1025 if(stdisp
==(WRegion
*)dock
){
1027 mplexpos(dock
->pos
, &din
.pos
);
1029 /* Update min/max first */
1030 dock_managed_rqgeom_(dock
, NULL
, 0, NULL
, NULL
, TRUE
);
1032 mplex_set_stdisp(par
, (WRegion
*)dock
, &din
);
1033 }else if((WRegion
*)par
==REGION_MANAGER(dock
)){
1035 mplexszplcy(dock
->pos
, &szplcy
);
1036 mplex_set_szplcy(par
, (WRegion
*)dock
, szplcy
);
1046 * Configure \var{dock}. \var{conftab} is a table of key/value pairs:
1048 * \begin{tabularx}{\linewidth}{llX}
1049 * \tabhead{Key & Values & Description}
1050 * \var{name} & string & Name of dock \\
1051 * \var{pos} & string in $\{t,m,b\}\times\{t,c,b\}$ & Dock position.
1052 * Can only be used in floating mode. \\
1053 * \var{grow} & up/down/left/right &
1054 * Growth direction where new dockapps are added. Also
1055 * sets orientation for dock when working as WMPlex status
1056 * display (see \fnref{WMPlex.set_stdisp}). \\
1057 * \var{is_auto} & bool &
1058 * Should \var{dock} automatically manage new dockapps? \\
1061 * Any parameters not explicitly set in \var{conftab} will be left unchanged.
1064 void dock_set(WDock
*dock
, ExtlTab conftab
)
1066 dock_do_set(dock
, conftab
, TRUE
);
1070 static void dock_do_get(WDock
*dock
, ExtlTab conftab
)
1072 extl_table_sets_s(conftab
, dock_param_name
.key
,
1073 region_name((WRegion
*)dock
));
1074 dock_param_extl_table_get(&dock_param_pos
, conftab
, dock
->pos
);
1075 dock_param_extl_table_get(&dock_param_grow
, conftab
, dock
->grow
);
1076 extl_table_sets_b(conftab
, dock_param_is_auto
.key
, dock
->is_auto
);
1077 extl_table_sets_b(conftab
, "save", dock
->save
);
1082 * Get \var{dock}'s configuration table. See \fnref{WDock.set} for a
1083 * description of the table.
1087 ExtlTab
dock_get(WDock
*dock
)
1091 conftab
=extl_create_table();
1092 dock_do_get(dock
, conftab
);
1100 /*{{{ Init/deinit */
1103 static bool dock_init(WDock
*dock
, WWindow
*parent
, const WFitParams
*fp
)
1107 dock
->pos
=dock_param_pos
.dflt
;
1108 dock
->grow
=dock_param_grow
.dflt
;
1109 dock
->is_auto
=dock_param_is_auto
.dflt
;
1111 dock
->dockapps
=NULL
;
1116 dock
->arrange_called
=FALSE
;
1120 if(!window_init((WWindow
*)dock
, parent
, &fp2
, "WDock"))
1123 region_add_bindmap((WRegion
*)dock
, dock_bindmap
);
1125 window_select_input(&(dock
->win
), IONCORE_EVENTMASK_CWINMGR
);
1127 dock_brush_get(dock
);
1129 LINK_ITEM(docks
, dock
, dock_next
, dock_prev
);
1135 static WDock
*create_dock(WWindow
*parent
, const WFitParams
*fp
)
1137 CREATEOBJ_IMPL(WDock
, dock
, (p
, parent
, fp
));
1141 static void dock_deinit(WDock
*dock
)
1143 while(dock
->dockapps
!=NULL
)
1144 destroy_obj((Obj
*)dock
->dockapps
->reg
);
1146 UNLINK_ITEM(docks
, dock
, dock_next
, dock_prev
);
1148 dock_brush_release(dock
);
1150 window_deinit((WWindow
*) dock
);
1155 WDock
*mod_dock_create(ExtlTab tab
)
1158 bool floating
=FALSE
;
1160 WScreen
*screen
=NULL
;
1162 WRegion
*stdisp
=NULL
;
1163 WMPlexSTDispInfo din
;
1166 if(extl_table_gets_s(tab
, "mode", &mode
)){
1167 if(strcmp(mode
, "floating")==0){
1169 }else if(strcmp(mode
, "embedded")!=0){
1170 warn("Invalid dock mode.");
1177 extl_table_gets_i(tab
, "screen", &screenid
);
1178 screen
=ioncore_find_screen_id(screenid
);
1180 warn("Screen %d does not exist.", screenid
);
1184 for(dock
=docks
; dock
; dock
=dock
->dock_next
){
1185 if(region_screen_of((WRegion
*)dock
)==screen
){
1186 warn("Screen %d already has a dock. Refusing to create another.",
1193 mplex_get_stdisp((WMPlex
*)screen
, &stdisp
, &din
);
1194 if(stdisp
!=NULL
&& !extl_table_is_bool_set(tab
, "force")){
1195 warn("Screen %d already has an stdisp. Refusing to add embedded "
1201 /* Create the dock */
1202 fp
.mode
=REGION_FIT_BOUNDS
|REGION_FIT_WHATEVER
;
1208 dock
=create_dock((WWindow
*)screen
, &fp
);
1211 warn("Failed to create dock.");
1216 /* Get parameters */
1218 dock_do_set(dock
, tab
, FALSE
);
1220 /* Calculate min/max size */
1221 dock_managed_rqgeom_(dock
, NULL
, 0, NULL
, NULL
, TRUE
);
1225 const WRectangle
*pg
=®ION_GEOM(screen
);
1226 WMPlexAttachParams par
=MPLEXATTACHPARAMS_INIT
;
1227 WRegionAttachData data
;
1229 par
.flags
=(MPLEX_ATTACH_UNNUMBERED
1230 |MPLEX_ATTACH_SIZEPOLICY
1232 |MPLEX_ATTACH_PASSIVE
);
1234 par
.geom
.w
=dock
->min_w
;
1235 par
.geom
.h
=dock
->min_h
;
1239 mplexszplcy(dock
->pos
, &par
.szplcy
);
1241 if(extl_table_is_bool_set(tab
, "floating_hidden"))
1242 par
.flags
|=MPLEX_ATTACH_HIDDEN
;
1244 data
.type
=REGION_ATTACH_REPARENT
;
1245 data
.u
.reg
=(WRegion
*)dock
;
1247 if(mplex_do_attach((WMPlex
*)screen
, &par
, &data
))
1250 mplexpos(dock
->pos
, &din
.pos
);
1251 din
.fullsize
=FALSE
; /* not supported */
1252 if(mplex_set_stdisp((WMPlex
*)screen
, (WRegion
*)dock
, &din
))
1256 /* Failed to attach. */
1257 warn("Failed to attach dock to screen.");
1258 destroy_obj((Obj
*)dock
);
1270 * Toggle floating docks on \var{mplex}.
1273 void mod_dock_set_floating_shown_on(WMPlex
*mplex
, const char *how
)
1275 int setpar
=libtu_setparam_invert(libtu_string_to_setparam(how
));
1278 for(dock
=docks
; dock
; dock
=dock
->dock_next
){
1279 if(REGION_MANAGER(dock
)==(WRegion
*)mplex
)
1280 mplex_set_hidden(mplex
, (WRegion
*)dock
, setpar
);
1291 ExtlTab
dock_get_configuration(WDock
*dock
)
1295 if(dock
->save
==FALSE
)
1296 return extl_table_none();
1298 tab
=region_get_base_configuration((WRegion
*)dock
);
1299 dock_do_get(dock
, tab
);
1305 WRegion
*dock_load(WWindow
*par
, const WFitParams
*fp
, ExtlTab tab
)
1307 WDock
*dock
=create_dock(par
, fp
);
1309 dock_set(dock
, tab
);
1310 dock_fitrep(dock
, NULL
, fp
);
1313 return (WRegion
*)dock
;
1320 /*{{{ Client window management setup */
1323 static bool dock_do_attach_final(WDock
*dock
, WRegion
*reg
, void *unused
)
1325 WDockApp
*dockapp
, *before_dockapp
;
1328 bool draw_border
=TRUE
;
1331 /* Create and initialise a new WDockApp struct */
1332 dockapp
=ALLOC(WDockApp
);
1337 if(OBJ_IS(reg
, WClientWin
)){
1338 ExtlTab proptab
=((WClientWin
*)reg
)->proptab
;
1339 extl_table_gets_b(proptab
, CLIENTWIN_WINPROP_BORDER
, &draw_border
);
1340 extl_table_gets_i(proptab
, CLIENTWIN_WINPROP_POSITION
, &pos
);
1344 dockapp
->draw_border
=draw_border
;
1346 dockapp
->tile
=FALSE
;
1348 /* Insert the dockapp at the correct relative position */
1349 before_dockapp
=dock
->dockapps
;
1350 for(before_dockapp
=dock
->dockapps
;
1351 before_dockapp
!=NULL
&& dockapp
->pos
>=before_dockapp
->pos
;
1352 before_dockapp
=before_dockapp
->next
){
1355 if(before_dockapp
!=NULL
){
1356 LINK_ITEM_BEFORE(dock
->dockapps
, before_dockapp
, dockapp
, next
, prev
);
1358 LINK_ITEM(dock
->dockapps
, dockapp
, next
, prev
);
1361 region_set_manager(reg
, (WRegion
*)dock
);
1363 geom
=REGION_GEOM(reg
);
1364 dock_managed_rqgeom_(dock
, reg
,
1365 REGION_RQGEOM_WEAK_X
|REGION_RQGEOM_WEAK_Y
,
1366 &geom
, NULL
, FALSE
);
1375 static WRegion
*dock_do_attach(WDock
*dock
, WRegionAttachData
*data
)
1378 dock_get_tile_size(dock
, &(fp
.g
));
1381 fp
.mode
=REGION_FIT_WHATEVER
|REGION_FIT_BOUNDS
;
1383 return region_attach_helper((WRegion
*)dock
, (WWindow
*)dock
, &fp
,
1384 (WRegionDoAttachFn
*)dock_do_attach_final
,
1390 * Attach \var{reg} to \var{dock}.
1393 bool dock_attach(WDock
*dock
, WRegion
*reg
)
1395 WRegionAttachData data
;
1398 data
.type
=REGION_ATTACH_REPARENT
;
1401 return (dock_do_attach(dock
, &data
)!=NULL
);
1405 static bool dock_handle_drop(WDock
*dock
, int x
, int y
,
1408 return dock_attach(dock
, dropped
);
1412 static WRegion
*dock_ph_handler(WDock
*dock
, int flags
, WRegionAttachData
*data
)
1414 return dock_do_attach(dock
, data
);
1418 static WPHolder
*dock_managed_get_pholder(WDock
*dock
, WRegion
*mgd
)
1420 return (WPHolder
*)create_basicpholder((WRegion
*)dock
,
1421 ((WBasicPHolderHandler
*)
1426 static WPHolder
*dock_prepare_manage(WDock
*dock
, const WClientWin
*cwin
,
1427 const WManageParams
*param UNUSED
,
1430 if(!MANAGE_PRIORITY_OK(priority
, MANAGE_PRIORITY_LOW
))
1433 return (WPHolder
*)create_basicpholder((WRegion
*)dock
,
1434 ((WBasicPHolderHandler
*)
1439 static void dock_managed_remove(WDock
*dock
, WRegion
*reg
)
1442 WDockApp
*dockapp
=dock_find_dockapp(dock
, reg
);
1447 UNLINK_ITEM(dock
->dockapps
, dockapp
, next
, prev
);
1450 region_unset_manager(reg
, (WRegion
*)dock
);
1456 static bool dock_clientwin_is_dockapp(WClientWin
*cwin
,
1457 const WManageParams
*param
)
1459 bool is_dockapp
=FALSE
;
1461 /* First, inspect the WManageParams.dockapp parameter */
1466 /* Second, inspect the _NET_WM_WINDOW_TYPE property */
1468 static Atom atom__net_wm_window_type
=None
;
1469 static Atom atom__net_wm_window_type_dock
=None
;
1470 Atom actual_type
=None
;
1472 unsigned long nitems
;
1473 unsigned long bytes_after
;
1474 unsigned char *prop
;
1476 if(atom__net_wm_window_type
==None
){
1477 atom__net_wm_window_type
=XInternAtom(ioncore_g
.dpy
,
1478 "_NET_WM_WINDOW_TYPE",
1481 if(atom__net_wm_window_type_dock
==None
){
1482 atom__net_wm_window_type_dock
=XInternAtom(ioncore_g
.dpy
,
1483 "_NET_WM_WINDOW_TYPE_DOCK",
1486 if(XGetWindowProperty(ioncore_g
.dpy
, cwin
->win
, atom__net_wm_window_type
,
1487 0, sizeof(Atom
), False
, XA_ATOM
, &actual_type
,
1488 &actual_format
, &nitems
, &bytes_after
, &prop
)
1490 if(actual_type
==XA_ATOM
&& nitems
>=1
1491 && *(Atom
*)prop
==atom__net_wm_window_type_dock
){
1498 /* Third, inspect the WM_CLASS property */
1503 p
=xwindow_get_text_property(cwin
->win
, XA_WM_CLASS
, &n
);
1505 if(n
>=2 && strcmp(p
[1], "DockApp")==0){
1512 /* Fourth, inspect the _KDE_NET_WM_SYSTEM_TRAY_WINDOW_FOR property */
1514 static Atom atom__kde_net_wm_system_tray_window_for
=None
;
1515 Atom actual_type
=None
;
1517 unsigned long nitems
;
1518 unsigned long bytes_after
;
1519 unsigned char *prop
;
1521 if(atom__kde_net_wm_system_tray_window_for
==None
){
1522 atom__kde_net_wm_system_tray_window_for
=XInternAtom(ioncore_g
.dpy
,
1523 "_KDE_NET_WM_SYSTEM_TRAY_WINDOW_FOR",
1526 if(XGetWindowProperty(ioncore_g
.dpy
, cwin
->win
,
1527 atom__kde_net_wm_system_tray_window_for
, 0,
1528 sizeof(Atom
), False
, AnyPropertyType
,
1529 &actual_type
, &actual_format
, &nitems
,
1530 &bytes_after
, &prop
)==Success
){
1531 if(actual_type
!=None
){
1543 static WDock
*dock_find_suitable_dock(WClientWin
*cwin
,
1544 const WManageParams
*param
)
1548 for(dock
=docks
; dock
; dock
=dock
->dock_next
){
1551 if(!region_same_rootwin((WRegion
*)dock
, (WRegion
*)cwin
))
1560 static bool clientwin_do_manage_hook(WClientWin
*cwin
, const WManageParams
*param
)
1564 if(!dock_clientwin_is_dockapp(cwin
, param
)){
1568 dock
=dock_find_suitable_dock(cwin
, param
);
1573 return region_manage_clientwin((WRegion
*)dock
, cwin
, param
,
1574 MANAGE_PRIORITY_NONE
);
1581 /*{{{ Module init/deinit */
1584 bool mod_dock_init()
1587 if(!ioncore_register_regclass(&CLASSDESCR(WDock
),
1588 (WRegionLoadCreateFn
*)dock_load
)){
1592 if(!mod_dock_register_exports()){
1593 ioncore_unregister_regclass(&CLASSDESCR(WDock
));
1597 dock_bindmap
=ioncore_alloc_bindmap("WDock", NULL
);
1598 if(dock_bindmap
==NULL
){
1599 warn("Unable to allocate dock bindmap.");
1600 mod_dock_unregister_exports();
1601 ioncore_unregister_regclass(&CLASSDESCR(WDock
));
1604 extl_read_config("cfg_dock", NULL
, TRUE
);
1606 hook_add(clientwin_do_manage_alt
,
1607 (WHookDummy
*)clientwin_do_manage_hook
);
1614 void mod_dock_deinit()
1618 ioncore_unregister_regclass(&CLASSDESCR(WDock
));
1620 hook_remove(clientwin_do_manage_alt
,
1621 (WHookDummy
*)clientwin_do_manage_hook
);
1625 WDock
*next
=dock
->dock_next
;
1626 destroy_obj((Obj
*)dock
);
1630 mod_dock_unregister_exports();
1632 if(dock_bindmap
!=NULL
){
1633 ioncore_free_bindmap("WDock", dock_bindmap
);
1642 /*{{{ WDock class description and dynfun list */
1645 static DynFunTab dock_dynfuntab
[]={
1646 {window_draw
, dock_draw
},
1647 {region_updategr
, dock_updategr
},
1648 {region_managed_rqgeom
, dock_managed_rqgeom
},
1649 {(DynFun
*)region_prepare_manage
, (DynFun
*)dock_prepare_manage
},
1650 {region_managed_remove
, dock_managed_remove
},
1651 {(DynFun
*)region_get_configuration
, (DynFun
*)dock_get_configuration
},
1652 {region_size_hints
, dock_size_hints
},
1653 {(DynFun
*)region_fitrep
, (DynFun
*)dock_fitrep
},
1654 {(DynFun
*)region_orientation
, (DynFun
*)dock_orientation
},
1655 {(DynFun
*)region_handle_drop
, (DynFun
*)dock_handle_drop
},
1657 {(DynFun
*)region_managed_get_pholder
,
1658 (DynFun
*)dock_managed_get_pholder
},
1664 IMPLCLASS(WDock
, WWindow
, dock_deinit
, dock_dynfuntab
);