4 * Copyright (c) Tuomo Valkonen 1999-2007.
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"
35 WHook *region_notify_hook=NULL;
38 static void region_notify_change_(WRegion *reg, WRegionNotify how);
41 /*{{{ Init & deinit */
44 void region_init(WRegion *reg, WWindow *par, const WFitParams *fp)
46 if(fp->g.w<0 || fp->g.h<0)
47 warn(TR("Creating region with negative width or height!"));
60 reg->active_prev=NULL;
61 reg->active_next=NULL;
71 reg->mgd_activity=FALSE;
74 reg->rootwin=((WRegion*)par)->rootwin;
75 region_set_parent(reg, par);
77 assert(OBJ_IS(reg, WRootWin));
82 static void destroy_children(WRegion *reg)
84 WRegion *sub, *prev=NULL;
85 bool complained=FALSE;
87 /* destroy children */
92 assert(!OBJ_IS_BEING_DESTROYED(sub));
94 if(ioncore_g.opmode!=IONCORE_OPMODE_DEINIT && !complained && OBJ_IS(reg, WClientWin)){
95 warn(TR("Destroying object \"%s\" with client windows as "
96 "children."), region_name(reg));
100 destroy_obj((Obj*)sub);
105 void region_deinit(WRegion *reg)
107 region_notify_change(reg, ioncore_g.notifies.deinit);
109 destroy_children(reg);
111 if(ioncore_g.focus_next==reg){
112 D(warn("Region to be focused next destroyed[1]."));
113 ioncore_g.focus_next=NULL;
116 region_detach_manager(reg);
117 region_unset_return(reg);
118 region_unset_parent(reg);
119 region_remove_bindings(reg);
121 region_unregister(reg);
123 region_focuslist_deinit(reg);
125 if(ioncore_g.focus_next==reg){
126 D(warn("Region to be focused next destroyed[2]."));
127 ioncore_g.focus_next=NULL;
138 bool region_fitrep(WRegion *reg, WWindow *par, const WFitParams *fp)
141 CALL_DYN_RET(ret, bool, region_fitrep, reg, (reg, par, fp));
146 void region_updategr(WRegion *reg)
148 CALL_DYN(region_updategr, reg, (reg));
152 void region_map(WRegion *reg)
154 CALL_DYN(region_map, reg, (reg));
158 void region_unmap(WRegion *reg)
160 CALL_DYN(region_unmap, reg, (reg));
164 void region_notify_rootpos(WRegion *reg, int x, int y)
166 CALL_DYN(region_notify_rootpos, reg, (reg, x, y));
170 Window region_xwindow(const WRegion *reg)
173 CALL_DYN_RET(ret, Window, region_xwindow, reg, (reg));
178 void region_activated(WRegion *reg)
180 CALL_DYN(region_activated, reg, (reg));
184 void region_inactivated(WRegion *reg)
186 CALL_DYN(region_inactivated, reg, (reg));
190 void region_do_set_focus(WRegion *reg, bool warp)
192 CALL_DYN(region_do_set_focus, reg, (reg, warp));
196 /*{{{ Manager region dynfuns */
199 static bool region_managed_prepare_focus_default(WRegion *mgr, WRegion *reg,
201 WPrepareFocusResult *res)
203 if(!region_prepare_focus(mgr, flags, res))
212 bool region_managed_prepare_focus(WRegion *mgr, WRegion *reg,
214 WPrepareFocusResult *res)
217 CALL_DYN_RET(ret, bool, region_managed_prepare_focus, mgr,
218 (mgr, reg, flags, res));
223 void region_managed_notify(WRegion *mgr, WRegion *reg, WRegionNotify how)
225 CALL_DYN(region_managed_notify, mgr, (mgr, reg, how));
229 void region_managed_remove(WRegion *mgr, WRegion *reg)
231 CALL_DYN(region_managed_remove, mgr, (mgr, reg));
236 * Return the object, if any, that is considered ''currently active''
237 * within the objects managed by \var{mplex}.
241 WRegion *region_current(WRegion *mgr)
244 CALL_DYN_RET(ret, WRegion*, region_current, mgr, (mgr));
249 void region_child_removed(WRegion *reg, WRegion *sub)
251 CALL_DYN(region_child_removed, reg, (reg, sub));
258 /*{{{ Dynfun defaults */
261 void region_updategr_default(WRegion *reg)
265 FOR_ALL_CHILDREN(reg, sub){
266 region_updategr(sub);
280 bool region_prepare_focus(WRegion *reg, int flags,
281 WPrepareFocusResult *res)
283 WRegion *mgr=REGION_MANAGER(reg);
284 WRegion *par=REGION_PARENT_REG(reg);
286 if(REGION_IS_MAPPED(reg) && region_may_control_focus(reg)){
292 return region_managed_prepare_focus(mgr, reg, flags, res);
294 if(!region_prepare_focus(par, flags, res))
296 /* Just focus reg, if it has no manager, and parent can be
307 bool region_goto_flags(WRegion *reg, int flags)
309 WPrepareFocusResult res;
312 ret=region_prepare_focus(reg, flags, &res);
315 if(res.flags®ION_GOTO_FOCUS)
316 region_maybewarp(res.reg, !(res.flags®ION_GOTO_NOWARP));
324 * Attempt to display \var{reg}, save region activity status and then
325 * warp to (or simply set focus to if warping is disabled) \var{reg}.
327 * Note that this function is asynchronous; the region will not
328 * actually have received the focus when this function returns.
331 bool region_goto(WRegion *reg)
333 return region_goto_flags(reg, REGION_GOTO_FOCUS);
340 /*{{{ Fit/reparent */
343 void region_fit(WRegion *reg, const WRectangle *geom, WRegionFitMode mode)
347 fp.mode=mode&~REGION_FIT_GRAVITY;
348 fp.gravity=ForgetGravity;
349 region_fitrep(reg, NULL, &fp);
353 bool region_reparent(WRegion *reg, WWindow *par,
354 const WRectangle *geom, WRegionFitMode mode)
359 return region_fitrep(reg, par, &fp);
369 static bool region_rqclose_default(WRegion *reg, bool relocate)
372 bool refuse=TRUE, mcf;
374 if((!relocate && !region_may_destroy(reg)) ||
375 !region_manager_allows_destroying(reg)){
379 ph=region_get_rescue_pholder(reg);
380 mcf=region_may_control_focus(reg);
383 refuse=!region_rescue_clientwins(reg, ph);
384 destroy_obj((Obj*)ph);
388 warn(TR("Failed to rescue some client windows - not closing."));
392 region_dispose(reg, mcf);
399 * Attempt to close/destroy \var{reg}. Whether this operation works
400 * depends on whether the particular type of region in question has
401 * implemented the feature and, in case of client windows, whether
402 * the client supports the \code{WM_DELETE} protocol (see also
403 * \fnref{WClientWin.kill}). If the operation is likely to succeed,
404 * \code{true} is returned, otherwise \code{false}. In most cases the
405 * region will not have been actually destroyed when this function returns.
406 * If \var{relocate} is not set, and \var{reg} manages other regions, it
407 * will not be closed. Otherwise the managed regions will be attempted
411 bool region_rqclose(WRegion *reg, bool relocate)
414 CALL_DYN_RET(ret, bool, region_rqclose, reg, (reg, relocate));
419 static WRegion *region_rqclose_propagate_default(WRegion *reg,
423 maybe_sub=region_current(reg);
425 return region_rqclose_propagate(maybe_sub, NULL);
426 return (region_rqclose(reg, FALSE) ? reg : NULL);
431 * Recursively attempt to close a region or one of the regions managed by
432 * it. If \var{sub} is set, it will be used as the managed region, otherwise
433 * \fnref{WRegion.current}\code{(reg)}. The object to be closed is
434 * returned or NULL if nothing can be closed. Also see notes for
435 * \fnref{WRegion.rqclose}.
438 WRegion *region_rqclose_propagate(WRegion *reg, WRegion *maybe_sub)
441 CALL_DYN_RET(ret, WRegion*, region_rqclose_propagate, reg,
447 bool region_may_destroy(WRegion *reg)
450 CALL_DYN_RET(ret, bool, region_may_destroy, reg, (reg));
455 bool region_managed_may_destroy(WRegion *mgr, WRegion *reg)
458 CALL_DYN_RET(ret, bool, region_managed_may_destroy, mgr, (mgr, reg));
463 bool region_manager_allows_destroying(WRegion *reg)
465 WRegion *mgr=REGION_MANAGER(reg);
470 return region_managed_may_destroy(mgr, reg);
474 void region_dispose(WRegion *reg, bool was_mcf)
477 WPHolder *ph=region_get_return(reg);
482 mainloop_defer_destroy((Obj*)reg);
486 void region_dispose_(WRegion *reg)
488 region_dispose(reg, region_may_control_focus(reg));
495 /*{{{ Manager/parent stuff */
498 /* Routine to call to unmanage a region */
499 void region_detach_manager(WRegion *reg)
503 mgr=REGION_MANAGER(reg);
509 /* Restore activity state to non-parent manager */
510 if(region_may_control_focus(reg)){
511 WRegion *par=REGION_PARENT_REG(reg);
512 if(par!=NULL && mgr!=par && REGION_PARENT_REG(mgr)==par){
513 /* REGION_ACTIVE shouldn't be set for windowless regions
514 * but make the parent's active_sub point to it
515 * nevertheless so that region_may_control_focus can
519 region_maybewarp_now(mgr, FALSE);
524 region_set_activity(reg, SETPARAM_UNSET);
526 region_managed_remove(mgr, reg);
528 assert(REGION_MANAGER(reg)==NULL);
532 void region_unset_manager_pseudoactivity(WRegion *reg)
534 WRegion *mgr=reg->manager, *par=REGION_PARENT_REG(reg);
536 if(mgr==NULL || mgr==par || !REGION_IS_PSEUDOACTIVE(mgr))
539 mgr->flags&=~REGION_PSEUDOACTIVE;
541 region_notify_change(mgr, ioncore_g.notifies.pseudoinactivated);
543 region_unset_manager_pseudoactivity(mgr);
547 void region_set_manager_pseudoactivity(WRegion *reg)
549 WRegion *mgr=reg->manager, *par=REGION_PARENT_REG(reg);
551 if(!REGION_IS_ACTIVE(reg) && !REGION_IS_PSEUDOACTIVE(reg))
554 if(mgr==NULL || mgr==par || REGION_IS_PSEUDOACTIVE(mgr))
557 mgr->flags|=REGION_PSEUDOACTIVE;
559 region_notify_change(mgr, ioncore_g.notifies.pseudoactivated);
561 region_set_manager_pseudoactivity(mgr);
565 /* This should only be called within region_managed_remove,
566 * _after_ any managed lists and other essential structures
567 * of mgr have been broken.
569 void region_unset_manager(WRegion *reg, WRegion *mgr)
571 if(reg->manager!=mgr)
574 region_notify_change_(reg, ioncore_g.notifies.unset_manager);
576 region_unset_manager_pseudoactivity(reg);
580 if(region_is_activity_r(reg))
581 region_clear_mgd_activity(mgr);
583 region_unset_return(reg);
587 /* This should be called within region attach routines,
588 * _after_ any managed lists and other essential structures
589 * of mgr have been set up.
591 void region_set_manager(WRegion *reg, WRegion *mgr)
593 assert(reg->manager==NULL);
597 region_set_manager_pseudoactivity(reg);
599 if(region_is_activity_r(reg))
600 region_mark_mgd_activity(mgr);
602 region_notify_change_(reg, ioncore_g.notifies.set_manager);
606 void region_set_parent(WRegion *reg, WWindow *parent)
608 assert(reg->parent==NULL && parent!=NULL);
609 LINK_ITEM(((WRegion*)parent)->children, reg, p_next, p_prev);
614 void region_unset_parent(WRegion *reg)
616 WRegion *p=REGION_PARENT_REG(reg);
618 if(p==NULL || p==reg)
621 UNLINK_ITEM(p->children, reg, p_next, p_prev);
624 if(p->active_sub==reg){
626 region_update_owned_grabs(p);
629 region_child_removed(p, reg);
634 * Returns the region that manages \var{reg}.
638 WRegion *region_manager(WRegion *reg)
645 * Returns the parent region of \var{reg}.
649 WWindow *region_parent(WRegion *reg)
655 WRegion *region_manager_or_parent(WRegion *reg)
657 if(reg->manager!=NULL)
660 return (WRegion*)(reg->parent);
664 WRegion *region_get_manager_chk(WRegion *p, const ClassDescr *descr)
669 mgr=REGION_MANAGER(p);
670 if(obj_is((Obj*)mgr, descr))
680 /*{{{ Stacking and ordering */
683 static void region_stacking_default(WRegion *reg,
684 Window *bottomret, Window *topret)
686 Window win=region_xwindow(reg);
692 void region_stacking(WRegion *reg, Window *bottomret, Window *topret)
694 CALL_DYN(region_stacking, reg, (reg, bottomret, topret));
698 void region_restack(WRegion *reg, Window other, int mode)
700 CALL_DYN(region_restack, reg, (reg, other, mode));
705 bool region_managed_rqorder(WRegion *reg, WRegion *sub, WRegionOrder order)
708 CALL_DYN_RET(ret, bool, region_managed_rqorder, reg, (reg, sub, order));
713 bool region_rqorder(WRegion *reg, WRegionOrder order)
715 WRegion *mgr=REGION_MANAGER(reg);
720 return region_managed_rqorder(mgr, reg, order);
725 * Request ordering. Currently supported values for \var{ord}
726 * are 'front' and 'back'.
728 EXTL_EXPORT_AS(WRegion, rqorder)
729 bool region_rqorder_extl(WRegion *reg, const char *ord)
733 if(strcmp(ord, "front")==0){
734 order=REGION_ORDER_FRONT;
735 }else if(strcmp(ord, "back")==0){
736 order=REGION_ORDER_BACK;
741 return region_rqorder(reg, order);
752 * Returns the root window \var{reg} is on.
756 WRootWin *region_rootwin_of(const WRegion *reg)
759 assert(reg!=NULL); /* Lua interface should not pass NULL reg. */
760 rw=(WRootWin*)(reg->rootwin);
767 * Returns the screen \var{reg} is on.
771 WScreen *region_screen_of(WRegion *reg)
774 if(OBJ_IS(reg, WScreen))
775 return (WScreen*)reg;
776 reg=REGION_PARENT_REG(reg);
782 Window region_root_of(const WRegion *reg)
784 return WROOTWIN_ROOT(region_rootwin_of(reg));
788 bool region_same_rootwin(const WRegion *reg1, const WRegion *reg2)
790 return (reg1->rootwin==reg2->rootwin);
795 * Is \var{reg} visible/is it and all it's ancestors mapped?
798 EXTL_EXPORT_AS(WRegion, is_mapped)
799 bool region_is_fully_mapped(WRegion *reg)
801 for(; reg!=NULL; reg=REGION_PARENT_REG(reg)){
802 if(!REGION_IS_MAPPED(reg))
810 void region_rootpos(WRegion *reg, int *xret, int *yret)
814 par=REGION_PARENT_REG(reg);
816 if(par==NULL || par==reg){
822 region_rootpos(par, xret, yret);
824 *xret+=REGION_GEOM(reg).x;
825 *yret+=REGION_GEOM(reg).y;
835 static bool mrsh_notify_change(WHookDummy *fn, void *p_)
845 static bool mrshe_notify_change(ExtlFn fn, void *p_)
849 extl_call(fn, "os", NULL, p->reg, stringstore_get(p->how));
855 static void region_notify_change_(WRegion *reg, WRegionNotify how)
863 hook_call(region_notify_hook, &p, mrsh_notify_change, mrshe_notify_change),
864 extl_unprotect(NULL);
868 void region_notify_change(WRegion *reg, WRegionNotify how)
870 WRegion *mgr=REGION_MANAGER(reg);
873 region_managed_notify(mgr, reg, how);
875 region_notify_change_(reg, how);
880 * Returns the geometry of \var{reg} within its parent; a table with fields
881 * \var{x}, \var{y}, \var{w} and \var{h}.
885 ExtlTab region_geom(WRegion *reg)
887 return extl_table_from_rectangle(®ION_GEOM(reg));
891 bool region_handle_drop(WRegion *reg, int x, int y, WRegion *dropped)
894 CALL_DYN_RET(ret, bool, region_handle_drop, reg, (reg, x, y, dropped));
899 WRegion *region_managed_within(WRegion *reg, WRegion *mgd)
902 (REGION_PARENT_REG(mgd)==reg ||
903 REGION_PARENT_REG(mgd)==REGION_PARENT_REG(reg))){
905 if(REGION_MANAGER(mgd)==reg)
907 mgd=REGION_MANAGER(mgd);
917 /*{{{ Dynamic function table and class implementation */
920 static DynFunTab region_dynfuntab[]={
921 {region_managed_rqgeom,
922 region_managed_rqgeom_allow},
924 {region_managed_rqgeom_absolute,
925 region_managed_rqgeom_absolute_default},
928 region_updategr_default},
930 {(DynFun*)region_rescue_clientwins,
931 (DynFun*)region_rescue_child_clientwins},
933 {(DynFun*)region_prepare_manage,
934 (DynFun*)region_prepare_manage_default},
936 {(DynFun*)region_prepare_manage_transient,
937 (DynFun*)region_prepare_manage_transient_default},
939 {(DynFun*)region_managed_prepare_focus,
940 (DynFun*)region_managed_prepare_focus_default},
942 {(DynFun*)region_rqclose_propagate,
943 (DynFun*)region_rqclose_propagate_default},
945 {(DynFun*)region_rqclose,
946 (DynFun*)region_rqclose_default},
948 {(DynFun*)region_displayname,
949 (DynFun*)region_name},
952 region_stacking_default},
959 IMPLCLASS(WRegion, Obj, region_deinit, region_dynfuntab);