3 * Copyright (C) 2003 Tom Payne
4 * Copyright (C) 2003 Per Olofsson
5 * Copyright (C) 2004-2006 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>
37 #include <X11/extensions/Xext.h>
39 #include <libtu/objp.h>
40 #include <libtu/map.h>
41 #include <libtu/minmax.h>
42 #include <libextl/extl.h>
43 #include <libextl/readconfig.h>
44 #include <libmainloop/defer.h>
46 #include <ioncore/common.h>
47 #include <ioncore/clientwin.h>
48 #include <ioncore/eventh.h>
49 #include <ioncore/global.h>
50 #include <ioncore/manage.h>
51 #include <ioncore/names.h>
52 #include <ioncore/property.h>
53 #include <ioncore/resize.h>
54 #include <ioncore/window.h>
55 #include <ioncore/mplex.h>
56 #include <ioncore/saveload.h>
57 #include <ioncore/bindmaps.h>
58 #include <ioncore/regbind.h>
59 #include <ioncore/extlconv.h>
60 #include <ioncore/event.h>
61 #include <ioncore/resize.h>
62 #include <ioncore/sizehint.h>
63 #include <ioncore/basicpholder.h>
73 #define UNUSED __attribute__ ((unused))
83 #include "../version.h"
85 static const char *modname="dock";
86 const char mod_dock_ion_api_version[]=ION_API_VERSION;
88 static bool shape_extension=FALSE;
89 static int shape_event_basep=0;
90 static int shape_error_basep=0;
92 static WBindmap *dock_bindmap=NULL;
99 INTRSTRUCT(WDockParam);
100 INTRSTRUCT(WDockApp);
103 DECLSTRUCT(WDockParam){
106 const StringIntMap *map;
110 DECLSTRUCT(WDockApp){
111 WDockApp *next, *prev;
117 WRectangle tile_geom;
118 WRectangle border_geom;
123 WDock *dock_next, *dock_prev;
136 static WDock *docks=NULL;
141 /*{{{ Parameter conversion */
143 static void dock_param_extl_table_get(const WDockParam *param,
144 ExtlTab conftab, int value)
148 s=stringintmap_key(param->map, value, NULL);
150 extl_table_sets_s(conftab, param->key, s);
156 static bool dock_param_do_set(const WDockParam *param, char *s,
160 int i=stringintmap_value(param->map, s, -1);
162 warn_obj(modname, "Invalid %s \"%s\"", param->desc, s);
176 static bool dock_param_extl_table_set(const WDockParam *param, ExtlTab conftab,
181 if(extl_table_gets_s(conftab, param->key, &s))
182 return dock_param_do_set(param, s, ret);
189 static bool dock_param_brush_set(const WDockParam *param, GrBrush *brush,
194 if(grbrush_get_extra(brush, param->key, 's', &s))
195 return dock_param_do_set(param, s, ret);
204 /*{{{ Parameter descriptions */
206 static const WDockParam dock_param_name={
214 #define DOCK_HPOS_MASK 0x000f
215 #define DOCK_HPOS_LEFT 0x0000
216 #define DOCK_HPOS_CENTER 0x0001
217 #define DOCK_HPOS_RIGHT 0x0002
218 #define DOCK_VPOS_MASK 0x00f0
219 #define DOCK_VPOS_TOP 0x0000
220 #define DOCK_VPOS_MIDDLE 0x0010
221 #define DOCK_VPOS_BOTTOM 0x0020
224 static StringIntMap dock_pos_map[]={
225 {"tl", DOCK_VPOS_TOP|DOCK_HPOS_LEFT},
226 {"tc", DOCK_VPOS_TOP|DOCK_HPOS_CENTER},
227 {"tr", DOCK_VPOS_TOP|DOCK_HPOS_RIGHT},
228 {"ml", DOCK_VPOS_MIDDLE|DOCK_HPOS_LEFT},
229 {"mc", DOCK_VPOS_MIDDLE|DOCK_HPOS_CENTER},
230 {"mr", DOCK_VPOS_MIDDLE|DOCK_HPOS_RIGHT},
231 {"bl", DOCK_VPOS_BOTTOM|DOCK_HPOS_LEFT},
232 {"bc", DOCK_VPOS_BOTTOM|DOCK_HPOS_CENTER},
233 {"br", DOCK_VPOS_BOTTOM|DOCK_HPOS_RIGHT},
237 static WDockParam dock_param_pos={
241 DOCK_HPOS_LEFT|DOCK_VPOS_BOTTOM
252 static StringIntMap dock_grow_map[]={
253 {"up", DOCK_GROW_UP},
254 {"down", DOCK_GROW_DOWN},
255 {"left", DOCK_GROW_LEFT},
256 {"right", DOCK_GROW_RIGHT},
260 WDockParam dock_param_grow={
268 static const WDockParam dock_param_is_auto={
276 enum WDockOutlineStyle{
277 DOCK_OUTLINE_STYLE_NONE,
278 DOCK_OUTLINE_STYLE_ALL,
279 DOCK_OUTLINE_STYLE_EACH
282 static StringIntMap dock_outline_style_map[]={
283 {"none", DOCK_OUTLINE_STYLE_NONE},
284 {"all", DOCK_OUTLINE_STYLE_ALL},
285 {"each", DOCK_OUTLINE_STYLE_EACH},
289 WDockParam dock_param_outline_style={
292 dock_outline_style_map,
293 DOCK_OUTLINE_STYLE_ALL
297 static const WDockParam dock_param_tile_width={
304 static const WDockParam dock_param_tile_height={
317 #define CLIENTWIN_WINPROP_POSITION "dockposition"
318 #define CLIENTWIN_WINPROP_BORDER "dockborder"
320 static WDockApp *dock_find_dockapp(WDock *dock, WRegion *reg)
324 for(dockapp=dock->dockapps; dockapp!=NULL; dockapp=dockapp->next){
325 if(dockapp->reg==reg){
335 static void dock_get_outline_style(WDock *dock, int *ret)
338 *ret=dock_param_outline_style.dflt;
339 if(dock->brush!=NULL)
340 dock_param_brush_set(&dock_param_outline_style, dock->brush, ret);
347 /*{{{ Size calculation */
350 static void dock_get_tile_size(WDock *dock, WRectangle *ret)
352 ExtlTab tile_size_table;
356 ret->w=dock_param_tile_width.dflt;
357 ret->h=dock_param_tile_height.dflt;
358 if(dock->brush==NULL)
360 if(grbrush_get_extra(dock->brush, "tile_size", 't', &tile_size_table)){
361 extl_table_gets_i(tile_size_table, dock_param_tile_width.key, &ret->w);
362 extl_table_gets_i(tile_size_table, dock_param_tile_height.key, &ret->h);
363 extl_unref_table(tile_size_table);
369 static void dock_get_pos_grow(WDock *dock, int *pos, int *grow)
371 WMPlex *mplex=OBJ_CAST(REGION_PARENT(dock), WMPlex);
372 WRegion *mplex_stdisp;
373 WMPlexSTDispInfo din;
376 mplex_get_stdisp(mplex, &mplex_stdisp, &din);
377 if(mplex_stdisp==(WRegion*)dock){
378 /* Ok, we're assigned as a status display for mplex, so
379 * get parameters from there.
381 *pos=((din.pos==MPLEX_STDISP_TL || din.pos==MPLEX_STDISP_BL)
384 | ((din.pos==MPLEX_STDISP_TL || din.pos==MPLEX_STDISP_TR)
398 static void dock_reshape(WDock *dock)
402 if(!shape_extension){
406 dock_get_outline_style(dock, &outline_style);
408 switch(outline_style){
409 case DOCK_OUTLINE_STYLE_NONE:
410 case DOCK_OUTLINE_STYLE_EACH:
414 /* Start with an empty set */
415 XShapeCombineRectangles(ioncore_g.dpy, ((WWindow*)dock)->win,
416 ShapeBounding, 0, 0, NULL, 0, ShapeSet, 0);
418 /* Union with dockapp shapes */
419 for(dockapp=dock->dockapps; dockapp!=NULL; dockapp=dockapp->next){
420 WClientWin *cwin=OBJ_CAST(dockapp->reg, WClientWin);
421 if(outline_style==DOCK_OUTLINE_STYLE_EACH
422 && dockapp->draw_border){
423 /* Union with border shape */
424 XRectangle tile_rect;
426 tile_rect.x=dockapp->border_geom.x;
427 tile_rect.y=dockapp->border_geom.y;
428 tile_rect.width=dockapp->border_geom.w;
429 tile_rect.height=dockapp->border_geom.h;
430 XShapeCombineRectangles(ioncore_g.dpy, ((WWindow*)dock)->win,
431 ShapeBounding, 0, 0, &tile_rect, 1,
433 }else if(cwin!=NULL){
434 /* Union with dockapp shape */
438 XRectangle *rects=XShapeGetRectangles(ioncore_g.dpy, cwin->win,
439 ShapeBounding, &count,
442 WRectangle dockapp_geom=REGION_GEOM(cwin);
443 XShapeCombineRectangles(ioncore_g.dpy, ((WWindow*)dock)->win,
445 dockapp_geom.x, dockapp_geom.y,
446 rects, count, ShapeUnion, ordering);
454 case DOCK_OUTLINE_STYLE_ALL:
459 geom=REGION_GEOM(dock);
464 XShapeCombineRectangles(ioncore_g.dpy, ((WWindow*)dock)->win,
465 ShapeBounding, 0, 0, &rect, 1, ShapeSet, 0);
473 static void dock_arrange_dockapps(WDock *dock, const WRectangle *bd_dockg,
474 const WDockApp *replace_this,
477 GrBorderWidths dock_bdw, dockapp_bdw;
478 WDockApp dummy_copy, *dockapp;
479 int pos, grow, cur_coord=0;
480 WRectangle dock_geom;
482 dock->arrange_called=TRUE;
484 dock_get_pos_grow(dock, &pos, &grow);
486 /* Determine dock and dockapp border widths */
487 memset(&dock_bdw, 0, sizeof(GrBorderWidths));
488 memset(&dockapp_bdw, 0, sizeof(GrBorderWidths));
493 dock_get_outline_style(dock, &outline_style);
494 switch(outline_style){
495 case DOCK_OUTLINE_STYLE_NONE:
497 case DOCK_OUTLINE_STYLE_ALL:
498 grbrush_get_border_widths(dock->brush, &dock_bdw);
499 dockapp_bdw.spacing=dock_bdw.spacing;
501 case DOCK_OUTLINE_STYLE_EACH:
502 grbrush_get_border_widths(dock->brush, &dockapp_bdw);
507 dock_geom.w=bd_dockg->w-dock_bdw.left-dock_bdw.right;
508 dock_geom.h=bd_dockg->h-dock_bdw.top-dock_bdw.bottom;
510 /* Calculate initial co-ordinate for layout algorithm */
513 cur_coord=dock_bdw.top+dock_geom.h;
516 cur_coord=dock_bdw.top;
519 cur_coord=dock_bdw.left+dock_geom.w;
521 case DOCK_GROW_RIGHT:
522 cur_coord=dock_bdw.left;
526 /* Arrange dockapps */
527 for(dockapp=dock->dockapps; dockapp!=NULL; dockapp=dockapp->next){
528 WDockApp *da=dockapp;
530 if(replace_this!=NULL){
531 if(replace_this==dockapp){
539 /* Calculate first co-ordinate */
543 switch(pos&DOCK_HPOS_MASK){
547 case DOCK_HPOS_CENTER:
548 da->border_geom.x=(dock_geom.w-da->border_geom.w)/2;
550 case DOCK_HPOS_RIGHT:
551 da->border_geom.x=dock_geom.w-da->border_geom.w;
554 da->border_geom.x+=dock_bdw.left;
557 case DOCK_GROW_RIGHT:
558 switch(pos&DOCK_VPOS_MASK){
562 case DOCK_VPOS_MIDDLE:
563 da->border_geom.y=(dock_geom.h-da->border_geom.h)/2;
565 case DOCK_VPOS_BOTTOM:
566 da->border_geom.y=dock_geom.h-da->border_geom.h;
569 da->border_geom.y+=dock_bdw.top;
573 /* Calculate second co-ordinate */
576 cur_coord-=da->border_geom.h;
577 da->border_geom.y=cur_coord;
578 cur_coord-=dockapp_bdw.spacing;
581 da->border_geom.y=cur_coord;
582 cur_coord+=da->border_geom.h+dockapp_bdw.spacing;
585 cur_coord-=da->border_geom.w;
586 da->border_geom.x=cur_coord;
587 cur_coord-=dockapp_bdw.spacing;
589 case DOCK_GROW_RIGHT:
590 da->border_geom.x=cur_coord;
591 cur_coord+=da->border_geom.w+dockapp_bdw.spacing;
595 /* Calculate tile geom */
596 da->tile_geom.x=da->border_geom.x+dockapp_bdw.left;
597 da->tile_geom.y=da->border_geom.y+dockapp_bdw.top;
599 /* Calculate dockapp geom */
601 da->geom.x=da->tile_geom.x+(da->tile_geom.w-da->geom.w)/2;
602 da->geom.y=da->tile_geom.y+(da->tile_geom.h-da->geom.h)/2;
604 da->geom.x=da->tile_geom.x;
605 da->geom.y=da->tile_geom.y;
608 if(replace_this==NULL)
609 region_fit(da->reg, &(da->geom), REGION_FIT_BOUNDS);
614 static void calc_dock_pos(WRectangle *dg, const WRectangle *pg, int pos)
616 switch(pos&DOCK_HPOS_MASK){
620 case DOCK_HPOS_CENTER:
621 dg->x=pg->x+(pg->w-dg->w)/2;
623 case DOCK_HPOS_RIGHT:
624 dg->x=pg->x+(pg->w-dg->w);
628 switch(pos&DOCK_VPOS_MASK){
632 case DOCK_VPOS_MIDDLE:
633 dg->y=pg->y+(pg->h-dg->h)/2;
635 case DOCK_VPOS_BOTTOM:
636 dg->y=pg->y+(pg->h-dg->h);
642 static void dock_set_minmax(WDock *dock, int grow, const WRectangle *g)
646 if(grow==DOCK_GROW_UP || grow==DOCK_GROW_DOWN){
656 static void dockapp_calc_preferred_size(WDock *dock, int grow,
657 const WRectangle *tile_size,
660 int w=da->geom.w, h=da->geom.h;
662 if(grow==DOCK_GROW_UP || grow==DOCK_GROW_DOWN){
663 da->geom.w=minof(w, tile_size->w);
667 da->geom.h=minof(h, tile_size->h);
670 region_size_hints_correct(da->reg, &(da->geom.w), &(da->geom.h), TRUE);
675 static void dock_managed_rqgeom_(WDock *dock, WRegion *reg, int flags,
676 const WRectangle *geom, WRectangle *geomret,
677 bool just_update_minmax)
679 WDockApp *dockapp=NULL, *thisdockapp=NULL, thisdockapp_copy;
680 WRectangle parent_geom, dock_geom, border_dock_geom;
681 GrBorderWidths dock_bdw, dockapp_bdw;
682 int n_dockapps=0, max_w=1, max_h=1, total_w=0, total_h=0;
684 WRectangle tile_size;
685 WWindow *par=REGION_PARENT(dock);
687 /* dock_resize calls with NULL parameters. */
688 assert(reg!=NULL || (geomret==NULL && !(flags®ION_RQGEOM_TRYONLY)));
690 dock_get_pos_grow(dock, &pos, &grow);
692 /* Determine parent and tile geoms */
696 parent_geom.w=REGION_GEOM(par).w;
697 parent_geom.h=REGION_GEOM(par).h;
699 /* Should not happen in normal operation. */
704 dock_get_tile_size(dock, &tile_size);
706 /* Determine dock and dockapp border widths */
707 memset(&dock_bdw, 0, sizeof(GrBorderWidths));
708 memset(&dockapp_bdw, 0, sizeof(GrBorderWidths));
713 dock_get_outline_style(dock, &outline_style);
714 switch(outline_style){
715 case DOCK_OUTLINE_STYLE_NONE:
717 case DOCK_OUTLINE_STYLE_ALL:
718 grbrush_get_border_widths(dock->brush, &dock_bdw);
719 dockapp_bdw.spacing=dock_bdw.spacing;
721 case DOCK_OUTLINE_STYLE_EACH:
722 grbrush_get_border_widths(dock->brush, &dockapp_bdw);
727 /* Calculate widths and heights */
728 for(dockapp=dock->dockapps; dockapp!=NULL; dockapp=dockapp->next){
729 WDockApp *da=dockapp;
730 bool update=!(flags®ION_RQGEOM_TRYONLY);
731 if(dockapp->reg==reg){
733 if(flags®ION_RQGEOM_TRYONLY){
734 thisdockapp_copy=*dockapp;
735 thisdockapp_copy.geom=*geom;
736 da=&thisdockapp_copy;
743 /* Calculcate preferred size */
744 dockapp_calc_preferred_size(dock, grow, &tile_size, da);
746 /* Determine whether dockapp should be placed on a tile */
747 da->tile=da->geom.w<=tile_size.w && da->geom.h<=tile_size.h;
749 /* Calculate width and height */
751 da->tile_geom.w=tile_size.w;
752 da->tile_geom.h=tile_size.h;
754 da->tile_geom.w=da->geom.w;
755 da->tile_geom.h=da->geom.h;
758 /* Calculate border width and height */
759 da->border_geom.w=dockapp_bdw.left+da->tile_geom.w+dockapp_bdw.right;
760 da->border_geom.h=dockapp_bdw.top+da->tile_geom.h+dockapp_bdw.right;
763 /* Calculate maximum and accumulated widths and heights */
764 if(da->border_geom.w>max_w)
765 max_w=da->border_geom.w;
766 total_w+=da->border_geom.w+(n_dockapps ? dockapp_bdw.spacing : 0);
768 if(da->border_geom.h>max_h)
769 max_h=da->border_geom.h;
770 total_h+=da->border_geom.h+(n_dockapps ? dockapp_bdw.spacing : 0);
776 if(thisdockapp==NULL && reg!=NULL){
777 warn("Requesting dockapp not found.");
779 *geomret=REGION_GEOM(reg);
783 /* Calculate width and height of dock */
787 case DOCK_GROW_RIGHT:
799 dock_geom.w=tile_size.w;
800 dock_geom.h=tile_size.h;
802 border_dock_geom.w=dock_bdw.left+dock_geom.w+dock_bdw.right;
803 border_dock_geom.h=dock_bdw.top+dock_geom.h+dock_bdw.bottom;
805 calc_dock_pos(&border_dock_geom, &parent_geom, pos);
807 /* Fit dock to new geom if required */
808 if(!(flags®ION_RQGEOM_TRYONLY)){
809 WRQGeomParams rq=RQGEOMPARAMS_INIT;
811 dock_set_minmax(dock, grow, &border_dock_geom);
813 if(just_update_minmax)
816 rq.flags=REGION_RQGEOM_WEAK_X|REGION_RQGEOM_WEAK_Y;
817 rq.geom=border_dock_geom;
819 dock->arrange_called=FALSE;
821 region_rqgeom((WRegion*)dock, &rq, NULL);
823 if(!dock->arrange_called)
824 dock_arrange_dockapps(dock, ®ION_GEOM(dock), NULL, NULL);
826 if(thisdockapp!=NULL && geomret!=NULL)
827 *geomret=thisdockapp->geom;
829 if(thisdockapp!=NULL && geomret!=NULL){
830 dock_arrange_dockapps(dock, ®ION_GEOM(dock),
831 thisdockapp, &thisdockapp_copy);
832 *geomret=thisdockapp_copy.geom;
837 static void dock_managed_rqgeom(WDock *dock, WRegion *reg,
838 const WRQGeomParams *rq,
841 dock_managed_rqgeom_(dock, reg, rq->flags, &rq->geom, geomret, FALSE);
845 void dock_size_hints(WDock *dock, WSizeHints *hints)
848 hints->min_width=dock->min_w;
849 hints->min_height=dock->min_h;
852 hints->max_width=dock->max_w;
853 hints->max_height=dock->max_h;
857 static bool dock_fitrep(WDock *dock, WWindow *parent, const WFitParams *fp)
859 if(!window_fitrep(&(dock->win), parent, fp))
862 dock_arrange_dockapps(dock, &(fp->g), NULL, NULL);
871 static int dock_orientation(WDock *dock)
873 return ((dock->grow==DOCK_GROW_LEFT || dock->grow==DOCK_GROW_RIGHT)
874 ? REGION_ORIENTATION_HORIZONTAL
875 : REGION_ORIENTATION_VERTICAL);
885 static void dock_draw(WDock *dock, bool complete)
890 if(dock->brush==NULL)
895 g.w=REGION_GEOM(dock).w;
896 g.h=REGION_GEOM(dock).h;
898 grbrush_begin(dock->brush, &g, (complete ? 0 : GRBRUSH_NO_CLEAR_OK));
900 dock_get_outline_style(dock, &outline_style);
901 switch(outline_style){
902 case DOCK_OUTLINE_STYLE_NONE:
904 case DOCK_OUTLINE_STYLE_ALL:
906 WRectangle geom=REGION_GEOM(dock);
908 grbrush_draw_border(dock->brush, &geom, "dock");
911 case DOCK_OUTLINE_STYLE_EACH:
914 for(dockapp=dock->dockapps; dockapp!=NULL;
915 dockapp=dockapp->next){
916 grbrush_draw_border(dock->brush, &dockapp->tile_geom,
923 grbrush_end(dock->brush);
928 * Resizes and refreshes \var{dock}.
931 void dock_resize(WDock *dock)
933 dock_managed_rqgeom_(dock, NULL, 0, NULL, NULL, FALSE);
934 dock_draw(dock, TRUE);
938 static void dock_brush_release(WDock *dock)
942 grbrush_release(dock->brush);
949 static void dock_brush_get(WDock *dock)
952 dock_brush_release(dock);
953 dock->brush=gr_get_brush(((WWindow*)dock)->win,
954 region_rootwin_of((WRegion*)dock),
959 static void dock_updategr(WDock *dock)
961 dock_brush_get(dock);
971 static void mplexpos(int pos, int *mpos)
973 int hp=pos&DOCK_HPOS_MASK, vp=pos&DOCK_VPOS_MASK;
976 p=(vp!=DOCK_VPOS_MIDDLE
978 ? (hp!=DOCK_HPOS_CENTER
979 ? (hp==DOCK_HPOS_RIGHT
983 : (hp!=DOCK_HPOS_CENTER
984 ? (hp==DOCK_HPOS_RIGHT
991 warn("Invalid dock position while as stdisp.");
997 static void dock_do_set(WDock *dock, ExtlTab conftab, bool resize)
1005 if(extl_table_gets_s(conftab, dock_param_name.key, &s)){
1006 if(!region_set_name((WRegion*)dock, s)){
1007 warn_obj(modname, "Can't set name to \"%s\"", s);
1012 if(extl_table_gets_b(conftab, "save", &save))
1015 if(dock_param_extl_table_set(&dock_param_pos, conftab, &dock->pos))
1018 if(dock_param_extl_table_set(&dock_param_grow, conftab, &dock->grow))
1021 if(extl_table_gets_b(conftab, dock_param_is_auto.key, &b))
1024 if(resize && (growset || posset)){
1025 WMPlex *par=OBJ_CAST(REGION_PARENT(dock), WMPlex);
1026 WRegion *stdisp=NULL;
1027 WMPlexSTDispInfo din;
1030 mplex_get_stdisp(par, &stdisp, &din);
1031 din.fullsize=FALSE; /* not supported. */
1032 if(stdisp==(WRegion*)dock){
1034 mplexpos(dock->pos, &din.pos);
1036 /* Update min/max first */
1037 dock_managed_rqgeom_(dock, NULL, 0, NULL, NULL, TRUE);
1039 mplex_set_stdisp(par, (WRegion*)dock, &din);
1049 * Configure \var{dock}. \var{conftab} is a table of key/value pairs:
1051 * \begin{tabularx}{\linewidth}{llX}
1052 * \tabhead{Key & Values & Description}
1053 * \var{name} & string & Name of dock \\
1054 * \var{pos} & string in $\{t,m,b\}\times\{t,c,b\}$ & Dock position.
1055 * Can only be used in floating mode. \\
1056 * \var{grow} & up/down/left/right &
1057 * Growth direction where new dockapps are added. Also
1058 * sets orientation for dock when working as WMPlex status
1059 * display (see \fnref{WMPlex.set_stdisp}). \\
1060 * \var{is_auto} & bool &
1061 * Should \var{dock} automatically manage new dockapps? \\
1064 * Any parameters not explicitly set in \var{conftab} will be left unchanged.
1067 void dock_set(WDock *dock, ExtlTab conftab)
1069 dock_do_set(dock, conftab, TRUE);
1073 static void dock_do_get(WDock *dock, ExtlTab conftab)
1075 extl_table_sets_s(conftab, dock_param_name.key,
1076 region_name((WRegion*)dock));
1077 dock_param_extl_table_get(&dock_param_pos, conftab, dock->pos);
1078 dock_param_extl_table_get(&dock_param_grow, conftab, dock->grow);
1079 extl_table_sets_b(conftab, dock_param_is_auto.key, dock->is_auto);
1080 extl_table_sets_b(conftab, "save", dock->save);
1085 * Get \var{dock}'s configuration table. See \fnref{WDock.set} for a
1086 * description of the table.
1090 ExtlTab dock_get(WDock *dock)
1094 conftab=extl_create_table();
1095 dock_do_get(dock, conftab);
1103 /*{{{ Init/deinit */
1106 static bool dock_init(WDock *dock, WWindow *parent, const WFitParams *fp)
1110 dock->pos=dock_param_pos.dflt;
1111 dock->grow=dock_param_grow.dflt;
1112 dock->is_auto=dock_param_is_auto.dflt;
1114 dock->dockapps=NULL;
1119 dock->arrange_called=FALSE;
1123 if(!window_init((WWindow*)dock, parent, &fp2))
1126 region_add_bindmap((WRegion*)dock, dock_bindmap);
1128 ((WRegion*)dock)->flags|=REGION_SKIP_FOCUS;
1130 window_select_input(&(dock->win), IONCORE_EVENTMASK_CWINMGR);
1132 dock_brush_get(dock);
1134 LINK_ITEM(docks, dock, dock_next, dock_prev);
1140 static WDock *create_dock(WWindow *parent, const WFitParams *fp)
1142 CREATEOBJ_IMPL(WDock, dock, (p, parent, fp));
1146 static void dock_deinit(WDock *dock)
1148 while(dock->dockapps!=NULL)
1149 destroy_obj((Obj*)dock->dockapps->reg);
1151 UNLINK_ITEM(docks, dock, dock_next, dock_prev);
1153 dock_brush_release(dock);
1155 window_deinit((WWindow*) dock);
1159 bool dock_may_destroy(WDock *dock)
1161 if(dock->dockapps!=NULL){
1162 warn_obj(modname, "Dock \"%s\" is still managing other objects "
1163 " -- refusing to close.", region_name((WRegion*)dock));
1172 WDock *mod_dock_create(ExtlTab tab)
1175 bool floating=FALSE;
1177 WScreen *screen=NULL;
1179 WRegion *stdisp=NULL;
1180 WMPlexSTDispInfo din;
1182 if(extl_table_gets_s(tab, "mode", &mode)){
1183 if(strcmp(mode, "floating")==0){
1185 }else if(strcmp(mode, "embedded")!=0){
1186 warn("Invalid dock mode.");
1193 extl_table_gets_i(tab, "screen", &screenid);
1194 screen=ioncore_find_screen_id(screenid);
1196 warn("Screen %d does not exist.", screenid);
1200 for(dock=docks; dock; dock=dock->dock_next){
1201 if(region_screen_of((WRegion*)dock)==screen){
1202 warn("Screen %d already has a dock. Refusing to create another.",
1209 mplex_get_stdisp((WMPlex*)screen, &stdisp, &din);
1210 if(stdisp!=NULL && !extl_table_is_bool_set(tab, "force")){
1211 warn("Screen %d already has an stdisp. Refusing to add embedded "
1217 /* Create the dock */
1220 WMPlexAttachParams par;
1222 par.flags=(MPLEX_ATTACH_UNNUMBERED
1223 |MPLEX_ATTACH_SIZEPOLICY
1224 |MPLEX_ATTACH_GEOM);
1226 par.szplcy=SIZEPOLICY_FREE;
1232 if(extl_table_is_bool_set(tab, "floating_hidden"))
1233 par.flags|=MPLEX_ATTACH_HIDDEN;
1235 dock=(WDock*)mplex_do_attach_new((WMPlex*)screen, &par,
1236 (WRegionCreateFn*)create_dock,
1241 fp.mode=REGION_FIT_BOUNDS|REGION_FIT_WHATEVER;
1247 dock=create_dock((WWindow*)screen, &fp);
1251 warn("Failed to create dock.");
1255 /* Get parameters */
1257 dock_do_set(dock, tab, FALSE);
1261 WRQGeomParams rq=RQGEOMPARAMS_INIT;
1262 const WRectangle *pg=®ION_GEOM(screen);
1264 /* Just calculate real min/max size */
1265 dock_managed_rqgeom_(dock, NULL, 0, NULL, NULL, TRUE);
1267 rq.geom.w=minof(dock->min_w, pg->w);
1268 rq.geom.h=minof(dock->min_h, pg->h);
1269 calc_dock_pos(&rq.geom, pg, dock->pos);
1271 region_rqgeom((WRegion*)dock, &rq, NULL);
1275 mplexpos(dock->pos, &din.pos);
1276 din.fullsize=FALSE; /* not supported */
1277 if(mplex_set_stdisp((WMPlex*)screen, (WRegion*)dock, &din))
1281 /* Failed to attach. */
1282 warn("Failed to attach dock to screen.");
1283 destroy_obj((Obj*)dock);
1295 * Toggle floating docks on \var{mplex}.
1298 void mod_dock_set_floating_shown_on(WMPlex *mplex, const char *how)
1300 int setpar=libtu_setparam_invert(libtu_string_to_setparam(how));
1303 for(dock=docks; dock; dock=dock->dock_next){
1304 if(REGION_MANAGER(dock)==(WRegion*)mplex)
1305 mplex_set_hidden(mplex, (WRegion*)dock, setpar);
1316 ExtlTab dock_get_configuration(WDock *dock)
1320 if(dock->save==FALSE)
1321 return extl_table_none();
1323 tab=region_get_base_configuration((WRegion*)dock);
1324 dock_do_get(dock, tab);
1330 WRegion *dock_load(WWindow *par, const WFitParams *fp, ExtlTab tab)
1332 WDock *dock=create_dock(par, fp);
1334 dock_set(dock, tab);
1335 dock_fitrep(dock, NULL, fp);
1338 return (WRegion*)dock;
1345 /*{{{ Client window management setup */
1348 static bool dock_do_attach_final(WDock *dock, WRegion *reg, void *unused)
1350 WDockApp *dockapp, *before_dockapp;
1353 bool draw_border=TRUE;
1356 /* Create and initialise a new WDockApp struct */
1357 dockapp=ALLOC(WDockApp);
1362 if(OBJ_IS(reg, WClientWin)){
1363 ExtlTab proptab=((WClientWin*)reg)->proptab;
1364 extl_table_gets_b(proptab, CLIENTWIN_WINPROP_BORDER, &draw_border);
1365 extl_table_gets_i(proptab, CLIENTWIN_WINPROP_POSITION, &pos);
1369 dockapp->draw_border=draw_border;
1371 dockapp->tile=FALSE;
1373 /* Insert the dockapp at the correct relative position */
1374 before_dockapp=dock->dockapps;
1375 for(before_dockapp=dock->dockapps;
1376 before_dockapp!=NULL && dockapp->pos>=before_dockapp->pos;
1377 before_dockapp=before_dockapp->next){
1380 if(before_dockapp!=NULL){
1381 LINK_ITEM_BEFORE(dock->dockapps, before_dockapp, dockapp, next, prev);
1383 LINK_ITEM(dock->dockapps, dockapp, next, prev);
1386 region_set_manager(reg, (WRegion*)dock);
1388 geom=REGION_GEOM(reg);
1389 dock_managed_rqgeom_(dock, reg,
1390 REGION_RQGEOM_WEAK_X|REGION_RQGEOM_WEAK_Y,
1391 &geom, NULL, FALSE);
1400 static WRegion *dock_do_attach(WDock *dock, WRegionAttachData *data)
1403 dock_get_tile_size(dock, &(fp.g));
1406 fp.mode=REGION_FIT_WHATEVER|REGION_FIT_BOUNDS;
1408 return region_attach_helper((WRegion*)dock, (WWindow*)dock, &fp,
1409 (WRegionDoAttachFn*)dock_do_attach_final,
1415 * Attach \var{reg} to \var{dock}.
1418 bool dock_attach(WDock *dock, WRegion *reg)
1420 WRegionAttachData data;
1423 data.type=REGION_ATTACH_REPARENT;
1426 return (dock_do_attach(dock, &data)!=NULL);
1430 static bool dock_handle_drop(WDock *dock, int x, int y,
1433 return dock_attach(dock, dropped);
1437 static WRegion *dock_ph_handler(WDock *dock, int flags, WRegionAttachData *data)
1439 return dock_do_attach(dock, data);
1443 static WPHolder *dock_managed_get_pholder(WDock *dock, WRegion *mgd)
1445 return (WPHolder*)create_basicpholder((WRegion*)dock,
1446 ((WBasicPHolderHandler*)
1451 static WPHolder *dock_prepare_manage(WDock *dock, const WClientWin *cwin,
1452 const WManageParams *param UNUSED,
1455 if(redir==MANAGE_REDIR_STRICT_YES)
1458 return (WPHolder*)create_basicpholder((WRegion*)dock,
1459 ((WBasicPHolderHandler*)
1464 static void dock_managed_remove(WDock *dock, WRegion *reg)
1467 WDockApp *dockapp=dock_find_dockapp(dock, reg);
1472 UNLINK_ITEM(dock->dockapps, dockapp, next, prev);
1475 region_unset_manager(reg, (WRegion*)dock);
1481 static bool dock_clientwin_is_dockapp(WClientWin *cwin,
1482 const WManageParams *param)
1484 bool is_dockapp=FALSE;
1486 /* First, inspect the WManageParams.dockapp parameter */
1491 /* Second, inspect the _NET_WM_WINDOW_TYPE property */
1493 static Atom atom__net_wm_window_type=None;
1494 static Atom atom__net_wm_window_type_dock=None;
1495 Atom actual_type=None;
1497 unsigned long nitems;
1498 unsigned long bytes_after;
1499 unsigned char *prop;
1501 if(atom__net_wm_window_type==None){
1502 atom__net_wm_window_type=XInternAtom(ioncore_g.dpy,
1503 "_NET_WM_WINDOW_TYPE",
1506 if(atom__net_wm_window_type_dock==None){
1507 atom__net_wm_window_type_dock=XInternAtom(ioncore_g.dpy,
1508 "_NET_WM_WINDOW_TYPE_DOCK",
1511 if(XGetWindowProperty(ioncore_g.dpy, cwin->win, atom__net_wm_window_type,
1512 0, sizeof(Atom), False, XA_ATOM, &actual_type,
1513 &actual_format, &nitems, &bytes_after, &prop)
1515 if(actual_type==XA_ATOM && nitems>=1
1516 && *(Atom*)prop==atom__net_wm_window_type_dock){
1523 /* Third, inspect the WM_CLASS property */
1528 p=xwindow_get_text_property(cwin->win, XA_WM_CLASS, &n);
1530 if(n>=2 && strcmp(p[1], "DockApp")==0){
1537 /* Fourth, inspect the _KDE_NET_WM_SYSTEM_TRAY_WINDOW_FOR property */
1539 static Atom atom__kde_net_wm_system_tray_window_for=None;
1540 Atom actual_type=None;
1542 unsigned long nitems;
1543 unsigned long bytes_after;
1544 unsigned char *prop;
1546 if(atom__kde_net_wm_system_tray_window_for==None){
1547 atom__kde_net_wm_system_tray_window_for=XInternAtom(ioncore_g.dpy,
1548 "_KDE_NET_WM_SYSTEM_TRAY_WINDOW_FOR",
1551 if(XGetWindowProperty(ioncore_g.dpy, cwin->win,
1552 atom__kde_net_wm_system_tray_window_for, 0,
1553 sizeof(Atom), False, AnyPropertyType,
1554 &actual_type, &actual_format, &nitems,
1555 &bytes_after, &prop)==Success){
1556 if(actual_type!=None){
1568 static WDock *dock_find_suitable_dock(WClientWin *cwin,
1569 const WManageParams *param)
1573 for(dock=docks; dock; dock=dock->dock_next){
1576 if(!region_same_rootwin((WRegion*)dock, (WRegion*)cwin))
1585 static bool clientwin_do_manage_hook(WClientWin *cwin, const WManageParams *param)
1589 if(!dock_clientwin_is_dockapp(cwin, param)){
1593 dock=dock_find_suitable_dock(cwin, param);
1598 return region_manage_clientwin((WRegion*)dock, cwin, param,
1599 MANAGE_REDIR_PREFER_NO);
1606 /*{{{ Module init/deinit */
1609 bool mod_dock_init()
1612 if(XShapeQueryExtension(ioncore_g.dpy, &shape_event_basep,
1613 &shape_error_basep)){
1614 shape_extension=TRUE;
1616 XMissingExtension(ioncore_g.dpy, "SHAPE");
1619 if(!ioncore_register_regclass(&CLASSDESCR(WDock),
1620 (WRegionLoadCreateFn*)dock_load)){
1624 if(!mod_dock_register_exports()){
1625 ioncore_unregister_regclass(&CLASSDESCR(WDock));
1629 dock_bindmap=ioncore_alloc_bindmap("WDock", NULL);
1630 if(dock_bindmap==NULL){
1631 warn("Unable to allocate dock bindmap.");
1632 mod_dock_unregister_exports();
1633 ioncore_unregister_regclass(&CLASSDESCR(WDock));
1636 extl_read_config("cfg_dock", NULL, TRUE);
1638 hook_add(clientwin_do_manage_alt,
1639 (WHookDummy*)clientwin_do_manage_hook);
1646 void mod_dock_deinit()
1650 ioncore_unregister_regclass(&CLASSDESCR(WDock));
1652 hook_remove(clientwin_do_manage_alt,
1653 (WHookDummy*)clientwin_do_manage_hook);
1657 WDock *next=dock->dock_next;
1658 destroy_obj((Obj*)dock);
1662 mod_dock_unregister_exports();
1664 if(dock_bindmap!=NULL){
1665 ioncore_free_bindmap("WDock", dock_bindmap);
1674 /*{{{ WDock class description and dynfun list */
1677 static DynFunTab dock_dynfuntab[]={
1678 {window_draw, dock_draw},
1679 {region_updategr, dock_updategr},
1680 {region_managed_rqgeom, dock_managed_rqgeom},
1681 {(DynFun*)region_prepare_manage, (DynFun*)dock_prepare_manage},
1682 {region_managed_remove, dock_managed_remove},
1683 {(DynFun*)region_get_configuration, (DynFun*)dock_get_configuration},
1684 {region_size_hints, dock_size_hints},
1685 {(DynFun*)region_fitrep, (DynFun*)dock_fitrep},
1686 {(DynFun*)region_orientation, (DynFun*)dock_orientation},
1687 {(DynFun*)region_may_destroy, (DynFun*)dock_may_destroy},
1688 {(DynFun*)region_handle_drop, (DynFun*)dock_handle_drop},
1690 {(DynFun*)region_managed_get_pholder,
1691 (DynFun*)dock_managed_get_pholder},
1697 IMPLCLASS(WDock, WWindow, dock_deinit, dock_dynfuntab);