4 * Copyright (c) Tuomo Valkonen 1999-2007.
6 * See the included file LICENSE for details.
11 #include <libtu/objp.h>
12 #include <libextl/extl.h>
13 #include <libmainloop/defer.h>
25 #include "region-iter.h"
33 WHook *region_notify_hook=NULL;
36 static void region_notify_change_(WRegion *reg, WRegionNotify how);
39 /*{{{ Init & deinit */
42 void region_init(WRegion *reg, WWindow *par, const WFitParams *fp)
44 if(fp->g.w<0 || fp->g.h<0)
45 warn(TR("Creating region with negative width or height!"));
58 reg->active_prev=NULL;
59 reg->active_next=NULL;
69 reg->mgd_activity=FALSE;
72 reg->rootwin=((WRegion*)par)->rootwin;
73 region_set_parent(reg, par);
75 assert(OBJ_IS(reg, WRootWin));
80 static void destroy_children(WRegion *reg)
82 WRegion *sub, *prev=NULL;
83 bool complained=FALSE;
85 /* destroy children */
90 assert(!OBJ_IS_BEING_DESTROYED(sub));
92 if(ioncore_g.opmode!=IONCORE_OPMODE_DEINIT && !complained && OBJ_IS(reg, WClientWin)){
93 warn(TR("Destroying object \"%s\" with client windows as "
94 "children."), region_name(reg));
98 destroy_obj((Obj*)sub);
103 void region_deinit(WRegion *reg)
105 region_notify_change(reg, ioncore_g.notifies.deinit);
107 destroy_children(reg);
109 if(ioncore_g.focus_next==reg){
110 D(warn("Region to be focused next destroyed[1]."));
111 ioncore_g.focus_next=NULL;
114 assert(reg->submapstat==NULL);
115 /*region_free_submapstat(reg);*/
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));
155 region_notify_change_(reg, ioncore_g.notifies.map);
159 void region_unmap(WRegion *reg)
161 CALL_DYN(region_unmap, reg, (reg));
162 region_notify_change_(reg, ioncore_g.notifies.unmap);
166 void region_notify_rootpos(WRegion *reg, int x, int y)
168 CALL_DYN(region_notify_rootpos, reg, (reg, x, y));
172 Window region_xwindow(const WRegion *reg)
175 CALL_DYN_RET(ret, Window, region_xwindow, reg, (reg));
180 void region_activated(WRegion *reg)
182 CALL_DYN(region_activated, reg, (reg));
186 void region_inactivated(WRegion *reg)
188 CALL_DYN(region_inactivated, reg, (reg));
192 void region_do_set_focus(WRegion *reg, bool warp)
194 CALL_DYN(region_do_set_focus, reg, (reg, warp));
198 /*{{{ Manager region dynfuns */
201 static bool region_managed_prepare_focus_default(WRegion *mgr, WRegion *reg,
203 WPrepareFocusResult *res)
205 if(!region_prepare_focus(mgr, flags, res))
214 bool region_managed_prepare_focus(WRegion *mgr, WRegion *reg,
216 WPrepareFocusResult *res)
219 CALL_DYN_RET(ret, bool, region_managed_prepare_focus, mgr,
220 (mgr, reg, flags, res));
225 void region_managed_notify(WRegion *mgr, WRegion *reg, WRegionNotify how)
227 CALL_DYN(region_managed_notify, mgr, (mgr, reg, how));
231 void region_managed_remove(WRegion *mgr, WRegion *reg)
233 CALL_DYN(region_managed_remove, mgr, (mgr, reg));
238 * Return the object, if any, that is considered ``currently active''
239 * within the objects managed by \var{mplex}.
243 WRegion *region_current(WRegion *mgr)
246 CALL_DYN_RET(ret, WRegion*, region_current, mgr, (mgr));
251 void region_child_removed(WRegion *reg, WRegion *sub)
253 CALL_DYN(region_child_removed, reg, (reg, sub));
260 /*{{{ Dynfun defaults */
263 void region_updategr_default(WRegion *reg)
267 FOR_ALL_CHILDREN(reg, sub){
268 region_updategr(sub);
282 bool region_prepare_focus(WRegion *reg, int flags,
283 WPrepareFocusResult *res)
286 if(!REGION_IS_ACTIVE(reg) || !REGION_IS_MAPPED(reg)){
287 WRegion *mgr=REGION_MANAGER(reg);
288 WRegion *par=REGION_PARENT_REG(reg);
291 return region_managed_prepare_focus(mgr, reg, flags, res);
293 if(!region_prepare_focus(par, flags, res))
295 /* Just focus reg, if it has no manager, and parent can be
298 }else if(!REGION_IS_MAPPED(reg)){
309 bool region_goto_flags(WRegion *reg, int flags)
311 WPrepareFocusResult res;
314 ret=region_prepare_focus(reg, flags, &res);
317 if(res.flags®ION_GOTO_FOCUS)
318 region_maybewarp(res.reg, !(res.flags®ION_GOTO_NOWARP));
326 * Attempt to display \var{reg}, save region activity status and then
327 * warp to (or simply set focus to if warping is disabled) \var{reg}.
329 * Note that this function is asynchronous; the region will not
330 * actually have received the focus when this function returns.
333 bool region_goto(WRegion *reg)
335 return region_goto_flags(reg, REGION_GOTO_FOCUS);
342 /*{{{ Fit/reparent */
345 void region_fit(WRegion *reg, const WRectangle *geom, WRegionFitMode mode)
349 fp.mode=mode&~REGION_FIT_GRAVITY;
350 fp.gravity=ForgetGravity;
351 region_fitrep(reg, NULL, &fp);
355 bool region_reparent(WRegion *reg, WWindow *par,
356 const WRectangle *geom, WRegionFitMode mode)
361 return region_fitrep(reg, par, &fp);
371 static void region_rqclose_default(WRegion *reg, bool relocate)
373 if(relocate || region_may_dispose(reg))
374 region_defer_rqdispose(reg);
379 * Attempt to close/destroy \var{reg}. Whether this operation works
380 * depends on whether the particular type of region in question has
381 * implemented the feature and, in case of client windows, whether
382 * the client supports the \code{WM_DELETE} protocol (see also
383 * \fnref{WClientWin.kill}). The region will not be destroyed when
384 * this function returns. To find out if and when it is destroyed,
385 * use the \codestr{deinit} notification. If \var{relocate} is not set,
386 * and \var{reg} manages other regions, it will not be closed. Otherwise
387 * the managed regions will be attempted to be relocated.
390 void region_rqclose(WRegion *reg, bool relocate)
392 CALL_DYN(region_rqclose, reg, (reg, relocate));
396 static WRegion *region_rqclose_propagate_default(WRegion *reg,
400 maybe_sub=region_current(reg);
403 return region_rqclose_propagate(maybe_sub, NULL);
405 region_rqclose(reg, FALSE);
412 * Recursively attempt to close a region or one of the regions managed by
413 * it. If \var{sub} is set, it will be used as the managed region, otherwise
414 * \fnref{WRegion.current}\code{(reg)}. The object to be closed is
415 * returned, or NULL if nothing can be closed. For further details, see
416 * notes for \fnref{WRegion.rqclose}.
419 WRegion *region_rqclose_propagate(WRegion *reg, WRegion *maybe_sub)
422 CALL_DYN_RET(ret, WRegion*, region_rqclose_propagate, reg,
428 bool region_may_dispose_default(WRegion *reg)
430 bool res=region_rescue_needed(reg);
433 const char *name=region_name(reg);
434 warn(TR("Can not destroy %s: contains client windows."),
435 (name!=NULL ? name : TR("(unknown)")));
442 bool region_may_dispose(WRegion *reg)
445 CALL_DYN_RET(ret, bool, region_may_dispose, reg, (reg));
450 static WRegion *region_managed_disposeroot_default(WRegion *mgr, WRegion *reg)
456 WRegion *region_managed_disposeroot(WRegion *mgr, WRegion *reg)
459 CALL_DYN_RET(ret, WRegion*, region_managed_disposeroot, mgr, (mgr, reg));
464 WRegion *region_disposeroot(WRegion *reg)
466 WRegion *mgr=REGION_MANAGER(reg);
469 ? region_managed_disposeroot(mgr, reg)
474 bool region_rqdispose(WRegion *reg)
478 if(!region_may_dispose(reg))
481 root=region_disposeroot(reg);
486 return region_dispose(root);
490 bool region_dispose_(WRegion *reg, bool not_simple)
492 bool rescue=not_simple;
493 bool was_mcf=(not_simple && region_may_control_focus(reg));
497 if(!region_rescue(reg, NULL)){
498 warn(TR("Failed to rescue some client windows - not closing."));
504 ph=region_unset_get_return(reg);
506 destroy_obj((Obj*)reg);
510 destroy_obj((Obj*)ph);
517 bool region_dispose(WRegion *reg)
519 return region_dispose_(reg, TRUE);
523 void region_defer_rqdispose(WRegion *reg)
525 mainloop_defer_action((Obj*)reg, (WDeferredAction*)region_rqdispose);
532 /*{{{ Manager/parent stuff */
535 /* Routine to call to unmanage a region */
536 void region_detach_manager(WRegion *reg)
538 WRegion *mgr=REGION_MANAGER(reg);
543 region_set_activity(reg, SETPARAM_UNSET);
545 region_managed_remove(mgr, reg);
547 assert(REGION_MANAGER(reg)==NULL);
551 void region_unset_manager_pseudoactivity(WRegion *reg)
553 WRegion *mgr=reg->manager, *par=REGION_PARENT_REG(reg);
555 if(mgr==NULL || mgr==par || !REGION_IS_PSEUDOACTIVE(mgr))
558 mgr->flags&=~REGION_PSEUDOACTIVE;
560 region_notify_change(mgr, ioncore_g.notifies.pseudoinactivated);
562 region_unset_manager_pseudoactivity(mgr);
566 void region_set_manager_pseudoactivity(WRegion *reg)
568 WRegion *mgr=reg->manager, *par=REGION_PARENT_REG(reg);
570 if(!REGION_IS_ACTIVE(reg) && !REGION_IS_PSEUDOACTIVE(reg))
573 if(mgr==NULL || mgr==par || REGION_IS_PSEUDOACTIVE(mgr))
576 mgr->flags|=REGION_PSEUDOACTIVE;
578 region_notify_change(mgr, ioncore_g.notifies.pseudoactivated);
580 region_set_manager_pseudoactivity(mgr);
584 /* This should only be called within region_managed_remove,
585 * _after_ any managed lists and other essential structures
586 * of mgr have been broken.
588 void region_unset_manager(WRegion *reg, WRegion *mgr)
590 if(reg->manager!=mgr)
593 region_notify_change_(reg, ioncore_g.notifies.unset_manager);
595 region_unset_manager_pseudoactivity(reg);
599 if(region_is_activity_r(reg))
600 region_clear_mgd_activity(mgr);
602 region_unset_return(reg);
606 /* This should be called within region attach routines,
607 * _after_ any managed lists and other essential structures
608 * of mgr have been set up.
610 void region_set_manager(WRegion *reg, WRegion *mgr)
612 assert(reg->manager==NULL);
616 region_set_manager_pseudoactivity(reg);
618 if(region_is_activity_r(reg))
619 region_mark_mgd_activity(mgr);
621 region_notify_change_(reg, ioncore_g.notifies.set_manager);
625 void region_set_parent(WRegion *reg, WWindow *parent)
627 assert(reg->parent==NULL && parent!=NULL);
628 LINK_ITEM(((WRegion*)parent)->children, reg, p_next, p_prev);
633 void region_unset_parent(WRegion *reg)
635 WRegion *p=REGION_PARENT_REG(reg);
637 if(p==NULL || p==reg)
640 UNLINK_ITEM(p->children, reg, p_next, p_prev);
643 if(p->active_sub==reg){
645 region_update_owned_grabs(p);
648 region_child_removed(p, reg);
653 * Returns the region that manages \var{reg}.
657 WRegion *region_manager(WRegion *reg)
664 * Returns the parent region of \var{reg}.
668 WWindow *region_parent(WRegion *reg)
674 WRegion *region_manager_or_parent(WRegion *reg)
676 if(reg->manager!=NULL)
679 return (WRegion*)(reg->parent);
683 WRegion *region_get_manager_chk(WRegion *p, const ClassDescr *descr)
688 mgr=REGION_MANAGER(p);
689 if(obj_is((Obj*)mgr, descr))
699 /*{{{ Stacking and ordering */
702 static void region_stacking_default(WRegion *reg,
703 Window *bottomret, Window *topret)
705 Window win=region_xwindow(reg);
711 void region_stacking(WRegion *reg, Window *bottomret, Window *topret)
713 CALL_DYN(region_stacking, reg, (reg, bottomret, topret));
717 void region_restack(WRegion *reg, Window other, int mode)
719 CALL_DYN(region_restack, reg, (reg, other, mode));
724 bool region_managed_rqorder(WRegion *reg, WRegion *sub, WRegionOrder order)
727 CALL_DYN_RET(ret, bool, region_managed_rqorder, reg, (reg, sub, order));
732 bool region_rqorder(WRegion *reg, WRegionOrder order)
734 WRegion *mgr=REGION_MANAGER(reg);
739 return region_managed_rqorder(mgr, reg, order);
744 * Request ordering. Currently supported values for \var{ord}
745 * are \codestr{front} and \codestr{back}.
747 EXTL_EXPORT_AS(WRegion, rqorder)
748 bool region_rqorder_extl(WRegion *reg, const char *ord)
752 if(strcmp(ord, "front")==0){
753 order=REGION_ORDER_FRONT;
754 }else if(strcmp(ord, "back")==0){
755 order=REGION_ORDER_BACK;
760 return region_rqorder(reg, order);
771 * Returns the root window \var{reg} is on.
775 WRootWin *region_rootwin_of(const WRegion *reg)
778 assert(reg!=NULL); /* Lua interface should not pass NULL reg. */
779 rw=(WRootWin*)(reg->rootwin);
786 * Returns the screen \var{reg} is on.
790 WScreen *region_screen_of(WRegion *reg)
793 if(OBJ_IS(reg, WScreen))
794 return (WScreen*)reg;
795 reg=REGION_PARENT_REG(reg);
801 Window region_root_of(const WRegion *reg)
803 return WROOTWIN_ROOT(region_rootwin_of(reg));
807 bool region_same_rootwin(const WRegion *reg1, const WRegion *reg2)
809 return (reg1->rootwin==reg2->rootwin);
814 * Is \var{reg} visible/is it and all it's ancestors mapped?
817 EXTL_EXPORT_AS(WRegion, is_mapped)
818 bool region_is_fully_mapped(WRegion *reg)
820 for(; reg!=NULL; reg=REGION_PARENT_REG(reg)){
821 if(!REGION_IS_MAPPED(reg))
829 void region_rootpos(WRegion *reg, int *xret, int *yret)
833 par=REGION_PARENT_REG(reg);
835 if(par==NULL || par==reg){
841 region_rootpos(par, xret, yret);
843 *xret+=REGION_GEOM(reg).x;
844 *yret+=REGION_GEOM(reg).y;
854 static bool mrsh_notify_change(WHookDummy *fn, void *p_)
864 static bool mrshe_notify_change(ExtlFn fn, void *p_)
868 extl_call(fn, "os", NULL, p->reg, stringstore_get(p->how));
874 static void region_notify_change_(WRegion *reg, WRegionNotify how)
882 hook_call(region_notify_hook, &p, mrsh_notify_change, mrshe_notify_change),
883 extl_unprotect(NULL);
887 void region_notify_change(WRegion *reg, WRegionNotify how)
889 WRegion *mgr=REGION_MANAGER(reg);
892 region_managed_notify(mgr, reg, how);
894 region_notify_change_(reg, how);
899 * Returns the geometry of \var{reg} within its parent; a table with fields
900 * \var{x}, \var{y}, \var{w} and \var{h}.
904 ExtlTab region_geom(WRegion *reg)
906 return extl_table_from_rectangle(®ION_GEOM(reg));
910 bool region_handle_drop(WRegion *reg, int x, int y, WRegion *dropped)
913 CALL_DYN_RET(ret, bool, region_handle_drop, reg, (reg, x, y, dropped));
918 WRegion *region_managed_within(WRegion *reg, WRegion *mgd)
921 (REGION_PARENT_REG(mgd)==reg ||
922 REGION_PARENT_REG(mgd)==REGION_PARENT_REG(reg))){
924 if(REGION_MANAGER(mgd)==reg)
926 mgd=REGION_MANAGER(mgd);
936 /*{{{ Dynamic function table and class implementation */
939 static DynFunTab region_dynfuntab[]={
940 {region_managed_rqgeom,
941 region_managed_rqgeom_allow},
943 {region_managed_rqgeom_absolute,
944 region_managed_rqgeom_absolute_default},
947 region_updategr_default},
949 {(DynFun*)region_rescue_clientwins,
950 (DynFun*)region_rescue_child_clientwins},
952 {(DynFun*)region_may_dispose,
953 (DynFun*)region_may_dispose_default},
955 {(DynFun*)region_prepare_manage,
956 (DynFun*)region_prepare_manage_default},
958 {(DynFun*)region_prepare_manage_transient,
959 (DynFun*)region_prepare_manage_transient_default},
961 {(DynFun*)region_managed_prepare_focus,
962 (DynFun*)region_managed_prepare_focus_default},
964 {(DynFun*)region_managed_disposeroot,
965 (DynFun*)region_managed_disposeroot_default},
967 {(DynFun*)region_rqclose_propagate,
968 (DynFun*)region_rqclose_propagate_default},
970 {(DynFun*)region_rqclose,
971 (DynFun*)region_rqclose_default},
973 {(DynFun*)region_displayname,
974 (DynFun*)region_name},
977 region_stacking_default},
984 IMPLCLASS(WRegion, Obj, region_deinit, region_dynfuntab);