4 * Copyright (c) Tuomo Valkonen 1999-2006.
6 * Ion is free software; you can redistribute it and/or modify it under
7 * the terms of the GNU Lesser General Public License as published by
8 * the Free Software Foundation; either version 2.1 of the License, or
9 * (at your option) any later version.
14 #include <libtu/objp.h>
15 #include <libextl/extl.h>
16 #include <libmainloop/defer.h>
28 #include "region-iter.h"
34 WHook *region_notify_hook=NULL;
36 static void region_notify_change_(WRegion *reg, const char *how,
40 /*{{{ Init & deinit */
43 void region_init(WRegion *reg, WWindow *par, const WFitParams *fp)
45 if(fp->g.w<0 || fp->g.h<0)
46 warn(TR("Creating region with negative width or height!"));
59 reg->active_prev=NULL;
60 reg->active_next=NULL;
70 reg->mgd_activity=FALSE;
73 reg->rootwin=((WRegion*)par)->rootwin;
74 region_set_parent(reg, par);
76 assert(OBJ_IS(reg, WRootWin));/* || OBJ_IS(reg, WScreen));*/
81 static void destroy_children(WRegion *reg)
83 WRegion *sub, *prev=NULL;
84 bool complained=FALSE;
86 /* destroy children */
91 assert(!OBJ_IS_BEING_DESTROYED(sub));
93 if(ioncore_g.opmode!=IONCORE_OPMODE_DEINIT && !complained && OBJ_IS(reg, WClientWin)){
94 warn(TR("Destroying object \"%s\" with client windows as "
95 "children."), region_name(reg));
99 destroy_obj((Obj*)sub);
104 void region_deinit(WRegion *reg)
106 destroy_children(reg);
108 if(ioncore_g.focus_next==reg){
109 D(warn("Region to be focused next destroyed[1]."));
110 ioncore_g.focus_next=NULL;
113 region_detach_manager(reg);
114 region_unset_parent(reg);
115 region_remove_bindings(reg);
117 region_unregister(reg);
119 region_focuslist_deinit(reg);
121 if(ioncore_g.focus_next==reg){
122 D(warn("Region to be focused next destroyed[2]."));
123 ioncore_g.focus_next=NULL;
134 bool region_fitrep(WRegion *reg, WWindow *par, const WFitParams *fp)
137 CALL_DYN_RET(ret, bool, region_fitrep, reg, (reg, par, fp));
142 void region_updategr(WRegion *reg)
144 CALL_DYN(region_updategr, reg, (reg));
148 void region_map(WRegion *reg)
150 CALL_DYN(region_map, reg, (reg));
154 void region_unmap(WRegion *reg)
156 CALL_DYN(region_unmap, reg, (reg));
160 void region_notify_rootpos(WRegion *reg, int x, int y)
162 CALL_DYN(region_notify_rootpos, reg, (reg, x, y));
166 Window region_xwindow(const WRegion *reg)
169 CALL_DYN_RET(ret, Window, region_xwindow, reg, (reg));
174 void region_activated(WRegion *reg)
176 CALL_DYN(region_activated, reg, (reg));
180 void region_inactivated(WRegion *reg)
182 CALL_DYN(region_inactivated, reg, (reg));
186 void region_do_set_focus(WRegion *reg, bool warp)
188 CALL_DYN(region_do_set_focus, reg, (reg, warp));
192 /*{{{ Manager region dynfuns */
195 void region_managed_activated(WRegion *mgr, WRegion *reg)
197 CALL_DYN(region_managed_activated, mgr, (mgr, reg));
201 void region_managed_inactivated(WRegion *mgr, WRegion *reg)
203 CALL_DYN(region_managed_inactivated, mgr, (mgr, reg));
207 static bool region_managed_prepare_focus_default(WRegion *mgr, WRegion *reg,
209 WPrepareFocusResult *res)
211 if(!region_prepare_focus(mgr, flags, res))
220 bool region_managed_prepare_focus(WRegion *mgr, WRegion *reg,
222 WPrepareFocusResult *res)
225 CALL_DYN_RET(ret, bool, region_managed_prepare_focus, mgr,
226 (mgr, reg, flags, res));
231 void region_managed_notify(WRegion *mgr, WRegion *reg, const char *how)
233 CALL_DYN(region_managed_notify, mgr, (mgr, reg, how));
237 void region_managed_remove(WRegion *mgr, WRegion *reg)
239 CALL_DYN(region_managed_remove, mgr, (mgr, reg));
244 * Return the object, if any, that is considered ''currently active''
245 * within the objects managed by \var{mplex}.
249 WRegion *region_current(WRegion *mgr)
252 CALL_DYN_RET(ret, WRegion*, region_current, mgr, (mgr));
257 void region_child_removed(WRegion *reg, WRegion *sub)
259 CALL_DYN(region_child_removed, reg, (reg, sub));
266 /*{{{ Dynfun defaults */
269 void region_updategr_default(WRegion *reg)
273 FOR_ALL_CHILDREN(reg, sub){
274 region_updategr(sub);
288 bool region_prepare_focus(WRegion *reg, int flags,
289 WPrepareFocusResult *res)
291 WRegion *mgr=REGION_MANAGER(reg);
292 WRegion *par=REGION_PARENT_REG(reg);
294 if(REGION_IS_MAPPED(reg) && region_may_control_focus(reg)){
300 return region_managed_prepare_focus(mgr, reg, flags, res);
302 if(!region_prepare_focus(par, flags, res))
304 /* Just focus reg, if it has no manager, and parent can be
315 bool region_goto_flags(WRegion *reg, int flags)
317 WPrepareFocusResult res;
320 ret=region_prepare_focus(reg, flags, &res);
323 if(res.flags®ION_GOTO_FOCUS)
324 region_maybewarp(res.reg, !(res.flags®ION_GOTO_NOWARP));
332 * Attempt to display \var{reg}, save region activity status and then
333 * warp to (or simply set focus to if warping is disabled) \var{reg}.
335 * Note that this function is asynchronous; the region will not
336 * actually have received the focus when this function returns.
339 bool region_goto(WRegion *reg)
341 return region_goto_flags(reg, REGION_GOTO_FOCUS);
348 /*{{{ Fit/reparent */
351 void region_fit(WRegion *reg, const WRectangle *geom, WRegionFitMode mode)
355 fp.mode=mode&~REGION_FIT_GRAVITY;
356 fp.gravity=ForgetGravity;
357 region_fitrep(reg, NULL, &fp);
361 bool region_reparent(WRegion *reg, WWindow *par,
362 const WRectangle *geom, WRegionFitMode mode)
367 return region_fitrep(reg, par, &fp);
377 static bool region_rqclose_default(WRegion *reg, bool relocate)
382 if((!relocate && !region_may_destroy(reg)) ||
383 !region_manager_allows_destroying(reg)){
387 ph=region_get_rescue_pholder(reg);
390 refuse=!region_rescue_clientwins(reg, ph);
391 destroy_obj((Obj*)ph);
395 warn(TR("Failed to rescue some client windows - not closing."));
399 mainloop_defer_destroy((Obj*)reg);
406 * Attempt to close/destroy \var{reg}. Whether this operation works
407 * depends on whether the particular type of region in question has
408 * implemented the feature and, in case of client windows, whether
409 * the client supports the \code{WM_DELETE} protocol (see also
410 * \fnref{WClientWin.kill}). If the operation is likely to succeed,
411 * \code{true} is returned, otherwise \code{false}. In most cases the
412 * region will not have been actually destroyed when this function returns.
413 * If \var{relocate} is not set, and \var{reg} manages other regions, it
414 * will not be closed. Otherwise the managed regions will be attempted
418 bool region_rqclose(WRegion *reg, bool relocate)
421 CALL_DYN_RET(ret, bool, region_rqclose, reg, (reg, relocate));
426 static WRegion *region_rqclose_propagate_default(WRegion *reg,
430 maybe_sub=region_current(reg);
432 return region_rqclose_propagate(maybe_sub, NULL);
433 return (region_rqclose(reg, FALSE) ? reg : NULL);
438 * Recursively attempt to close a region or one of the regions managed by
439 * it. If \var{sub} is set, it will be used as the managed region, otherwise
440 * \fnref{WRegion.current}\code{(reg)}. The object to be closed is
441 * returned or NULL if nothing can be closed. Also see notes for
442 * \fnref{WRegion.rqclose}.
445 WRegion *region_rqclose_propagate(WRegion *reg, WRegion *maybe_sub)
448 CALL_DYN_RET(ret, WRegion*, region_rqclose_propagate, reg,
454 bool region_may_destroy(WRegion *reg)
457 CALL_DYN_RET(ret, bool, region_may_destroy, reg, (reg));
462 bool region_managed_may_destroy(WRegion *mgr, WRegion *reg)
465 CALL_DYN_RET(ret, bool, region_managed_may_destroy, mgr, (mgr, reg));
470 bool region_manager_allows_destroying(WRegion *reg)
472 WRegion *mgr=REGION_MANAGER(reg);
477 return region_managed_may_destroy(mgr, reg);
484 /*{{{ Manager/parent stuff */
487 /* Routine to call to unmanage a region */
488 void region_detach_manager(WRegion *reg)
492 mgr=REGION_MANAGER(reg);
497 /* Restore activity state to non-parent manager */
498 if(region_may_control_focus(reg)){
499 WRegion *par=REGION_PARENT_REG(reg);
500 if(par!=NULL && mgr!=par && REGION_PARENT_REG(mgr)==par){
501 /* REGION_ACTIVE shouldn't be set for windowless regions
502 * but make the parent's active_sub point to it
503 * nevertheless so that region_may_control_focus can
507 /*if(region_xwindow(mgr)!=None){*/
508 region_do_set_focus(mgr, FALSE);
513 region_set_activity(reg, SETPARAM_UNSET);
515 region_managed_remove(mgr, reg);
517 assert(REGION_MANAGER(reg)==NULL);
521 /* This should only be called within region_managed_remove,
522 * _after_ any managed lists and other essential structures
523 * of mgr have been broken.
525 void region_unset_manager(WRegion *reg, WRegion *mgr)
527 if(reg->manager!=mgr)
532 if(region_is_activity_r(reg))
533 region_clear_mgd_activity(mgr);
535 region_notify_change_(reg, "unset_manager", (Obj*)mgr);
539 /* This should be called within region attach routines,
540 * _after_ any managed lists and other essential structures
541 * of mgr have been set up.
543 void region_set_manager(WRegion *reg, WRegion *mgr)
545 assert(reg->manager==NULL);
549 if(region_is_activity_r(reg))
550 region_mark_mgd_activity(mgr);
552 region_notify_change_(reg, "set_manager", (Obj*)mgr);
556 void region_set_parent(WRegion *reg, WWindow *parent)
558 assert(reg->parent==NULL && parent!=NULL);
559 LINK_ITEM(((WRegion*)parent)->children, reg, p_next, p_prev);
564 void region_unset_parent(WRegion *reg)
566 WRegion *p=REGION_PARENT_REG(reg);
568 if(p==NULL || p==reg)
571 UNLINK_ITEM(p->children, reg, p_next, p_prev);
574 if(p->active_sub==reg){
576 region_update_owned_grabs(p);
579 region_child_removed(p, reg);
584 * Returns the region that manages \var{reg}.
588 WRegion *region_manager(WRegion *reg)
595 * Returns the parent region of \var{reg}.
599 WWindow *region_parent(WRegion *reg)
605 WRegion *region_manager_or_parent(WRegion *reg)
607 if(reg->manager!=NULL)
610 return (WRegion*)(reg->parent);
614 WRegion *region_get_manager_chk(WRegion *p, const ClassDescr *descr)
619 mgr=REGION_MANAGER(p);
620 if(obj_is((Obj*)mgr, descr))
630 /*{{{ Stacking and ordering */
633 static void region_stacking_default(WRegion *reg,
634 Window *bottomret, Window *topret)
636 Window win=region_xwindow(reg);
642 void region_stacking(WRegion *reg, Window *bottomret, Window *topret)
644 CALL_DYN(region_stacking, reg, (reg, bottomret, topret));
648 void region_restack(WRegion *reg, Window other, int mode)
650 CALL_DYN(region_restack, reg, (reg, other, mode));
655 bool region_managed_rqorder(WRegion *reg, WRegion *sub, WRegionOrder order)
658 CALL_DYN_RET(ret, bool, region_managed_rqorder, reg, (reg, sub, order));
663 bool region_rqorder(WRegion *reg, WRegionOrder order)
665 WRegion *mgr=REGION_MANAGER(reg);
670 return region_managed_rqorder(mgr, reg, order);
675 * Request ordering. Currently supported values for \var{ord}
676 * are 'front' and 'back'.
678 EXTL_EXPORT_AS(WRegion, rqorder)
679 bool region_rqorder_extl(WRegion *reg, const char *ord)
683 if(strcmp(ord, "front")==0){
684 order=REGION_ORDER_FRONT;
685 }else if(strcmp(ord, "back")==0){
686 order=REGION_ORDER_BACK;
691 return region_rqorder(reg, order);
702 * Returns the root window \var{reg} is on.
706 WRootWin *region_rootwin_of(const WRegion *reg)
709 assert(reg!=NULL); /* Lua interface should not pass NULL reg. */
710 rw=(WRootWin*)(reg->rootwin);
717 * Returns the screen \var{reg} is on.
721 WScreen *region_screen_of(WRegion *reg)
724 if(OBJ_IS(reg, WScreen))
725 return (WScreen*)reg;
726 reg=REGION_PARENT_REG(reg);
732 Window region_root_of(const WRegion *reg)
734 return WROOTWIN_ROOT(region_rootwin_of(reg));
738 bool region_same_rootwin(const WRegion *reg1, const WRegion *reg2)
740 return (reg1->rootwin==reg2->rootwin);
745 * Is \var{reg} visible/is it and all it's ancestors mapped?
748 EXTL_EXPORT_AS(WRegion, is_mapped)
749 bool region_is_fully_mapped(WRegion *reg)
751 for(; reg!=NULL; reg=REGION_PARENT_REG(reg)){
752 if(!REGION_IS_MAPPED(reg))
760 void region_rootpos(WRegion *reg, int *xret, int *yret)
764 par=REGION_PARENT_REG(reg);
766 if(par==NULL || par==reg){
772 region_rootpos(par, xret, yret);
774 *xret+=REGION_GEOM(reg).x;
775 *yret+=REGION_GEOM(reg).y;
779 static bool mrsh_not(WHookDummy *fn, void *p)
781 WRegion *reg=(WRegion*)((void**)p)[0];
782 const char *how=(const char*)((void**)p)[1];
783 Obj *detail=(Obj*)((void**)p)[2];
785 fn(reg, how, detail);
791 static bool mrshe_not(ExtlFn fn, void *p)
793 WRegion *reg=(WRegion*)((void**)p)[0];
794 const char *how=(const char*)((void**)p)[1];
795 Obj *detail=(Obj*)((void**)p)[2];
797 extl_call(fn, "oso", NULL, reg, how, detail);
803 static void region_notify_change_(WRegion *reg, const char *how,
813 hook_call(region_notify_hook, p, mrsh_not, mrshe_not),
814 extl_unprotect(NULL);
819 void region_notify_change(WRegion *reg, const char *how)
821 WRegion *mgr=REGION_MANAGER(reg);
824 region_managed_notify(mgr, reg, how);
826 region_notify_change_(reg, how, NULL);
831 * Returns the geometry of \var{reg} within its parent; a table with fields
832 * \var{x}, \var{y}, \var{w} and \var{h}.
836 ExtlTab region_geom(WRegion *reg)
838 return extl_table_from_rectangle(®ION_GEOM(reg));
842 bool region_handle_drop(WRegion *reg, int x, int y, WRegion *dropped)
845 CALL_DYN_RET(ret, bool, region_handle_drop, reg, (reg, x, y, dropped));
850 WRegion *region_managed_within(WRegion *reg, WRegion *mgd)
853 (REGION_PARENT_REG(mgd)==reg ||
854 REGION_PARENT_REG(mgd)==REGION_PARENT_REG(reg))){
856 if(REGION_MANAGER(mgd)==reg)
858 mgd=REGION_MANAGER(mgd);
868 /*{{{ Dynamic function table and class implementation */
871 static DynFunTab region_dynfuntab[]={
872 {region_managed_rqgeom,
873 region_managed_rqgeom_allow},
875 {region_managed_rqgeom_absolute,
876 region_managed_rqgeom_absolute_default},
879 region_updategr_default},
881 {(DynFun*)region_rescue_clientwins,
882 (DynFun*)region_rescue_child_clientwins},
884 {(DynFun*)region_prepare_manage,
885 (DynFun*)region_prepare_manage_default},
887 {(DynFun*)region_prepare_manage_transient,
888 (DynFun*)region_prepare_manage_transient_default},
890 {(DynFun*)region_managed_prepare_focus,
891 (DynFun*)region_managed_prepare_focus_default},
893 {(DynFun*)region_rqclose_propagate,
894 (DynFun*)region_rqclose_propagate_default},
896 {(DynFun*)region_rqclose,
897 (DynFun*)region_rqclose_default},
899 {(DynFun*)region_displayname,
900 (DynFun*)region_name},
903 region_stacking_default},
910 IMPLCLASS(WRegion, Obj, region_deinit, region_dynfuntab);