4 * Copyright (c) Tuomo Valkonen 1999-2008.
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(TRUE /* !REGION_IS_ACTIVE(reg) ||
287 !REGION_IS_MAPPED(reg) ||
288 ioncore_g.focus_next!=NULL*/){
289 WRegion *mgr=REGION_MANAGER(reg);
290 WRegion *par=REGION_PARENT_REG(reg);
293 return region_managed_prepare_focus(mgr, reg, flags, res);
295 if(!region_prepare_focus(par, flags, res))
297 /* Just focus reg, if it has no manager, and parent can be
300 }else if(!REGION_IS_MAPPED(reg)){
311 bool region_goto_flags(WRegion *reg, int flags)
313 WPrepareFocusResult res;
316 ret=region_prepare_focus(reg, flags, &res);
319 if(res.flags®ION_GOTO_FOCUS)
320 region_maybewarp(res.reg, !(res.flags®ION_GOTO_NOWARP));
328 * Attempt to display \var{reg}, save region activity status and then
329 * warp to (or simply set focus to if warping is disabled) \var{reg}.
331 * Note that this function is asynchronous; the region will not
332 * actually have received the focus when this function returns.
335 bool region_goto(WRegion *reg)
337 return region_goto_flags(reg, REGION_GOTO_FOCUS);
344 /*{{{ Fit/reparent */
347 void region_fit(WRegion *reg, const WRectangle *geom, WRegionFitMode mode)
351 fp.mode=mode&~REGION_FIT_GRAVITY;
352 fp.gravity=ForgetGravity;
353 region_fitrep(reg, NULL, &fp);
357 bool region_reparent(WRegion *reg, WWindow *par,
358 const WRectangle *geom, WRegionFitMode mode)
363 return region_fitrep(reg, par, &fp);
373 static void region_rqclose_default(WRegion *reg, bool relocate)
375 if(relocate || region_may_dispose(reg))
376 region_defer_rqdispose(reg);
381 * Attempt to close/destroy \var{reg}. Whether this operation works
382 * depends on whether the particular type of region in question has
383 * implemented the feature and, in case of client windows, whether
384 * the client supports the \code{WM_DELETE} protocol (see also
385 * \fnref{WClientWin.kill}). The region will not be destroyed when
386 * this function returns. To find out if and when it is destroyed,
387 * use the \codestr{deinit} notification. If \var{relocate} is not set,
388 * and \var{reg} manages other regions, it will not be closed. Otherwise
389 * the managed regions will be attempted to be relocated.
392 void region_rqclose(WRegion *reg, bool relocate)
394 CALL_DYN(region_rqclose, reg, (reg, relocate));
398 static WRegion *region_rqclose_propagate_default(WRegion *reg,
402 maybe_sub=region_current(reg);
405 return region_rqclose_propagate(maybe_sub, NULL);
407 region_rqclose(reg, FALSE);
414 * Recursively attempt to close a region or one of the regions managed by
415 * it. If \var{sub} is set, it will be used as the managed region, otherwise
416 * \fnref{WRegion.current}\code{(reg)}. The object to be closed is
417 * returned, or NULL if nothing can be closed. For further details, see
418 * notes for \fnref{WRegion.rqclose}.
421 WRegion *region_rqclose_propagate(WRegion *reg, WRegion *maybe_sub)
424 CALL_DYN_RET(ret, WRegion*, region_rqclose_propagate, reg,
430 bool region_may_dispose_default(WRegion *reg)
432 bool res=region_rescue_needed(reg);
435 const char *name=region_name(reg);
436 warn(TR("Can not destroy %s: contains client windows."),
437 (name!=NULL ? name : TR("(unknown)")));
444 bool region_may_dispose(WRegion *reg)
447 CALL_DYN_RET(ret, bool, region_may_dispose, reg, (reg));
452 static WRegion *region_managed_disposeroot_default(WRegion *mgr, WRegion *reg)
458 WRegion *region_managed_disposeroot(WRegion *mgr, WRegion *reg)
461 CALL_DYN_RET(ret, WRegion*, region_managed_disposeroot, mgr, (mgr, reg));
466 WRegion *region_disposeroot(WRegion *reg)
468 WRegion *mgr=REGION_MANAGER(reg);
471 ? region_managed_disposeroot(mgr, reg)
476 bool region_rqdispose(WRegion *reg)
480 if(!region_may_dispose(reg))
483 root=region_disposeroot(reg);
488 return region_dispose(root);
492 bool region_dispose_(WRegion *reg, bool not_simple)
494 bool rescue=not_simple;
495 bool was_mcf=(not_simple && region_may_control_focus(reg));
499 if(!region_rescue(reg, NULL, 0)){
500 warn(TR("Failed to rescue some client windows - not closing."));
506 ph=region_unset_get_return(reg);
508 destroy_obj((Obj*)reg);
512 destroy_obj((Obj*)ph);
519 bool region_dispose(WRegion *reg)
521 return region_dispose_(reg, TRUE);
525 void region_defer_rqdispose(WRegion *reg)
527 mainloop_defer_action((Obj*)reg, (WDeferredAction*)region_rqdispose);
534 /*{{{ Manager/parent stuff */
537 /* Routine to call to unmanage a region */
538 void region_detach_manager(WRegion *reg)
540 WRegion *mgr=REGION_MANAGER(reg);
545 region_set_activity(reg, SETPARAM_UNSET);
547 region_managed_remove(mgr, reg);
549 assert(REGION_MANAGER(reg)==NULL);
553 void region_unset_manager_pseudoactivity(WRegion *reg)
555 WRegion *mgr=reg->manager, *par=REGION_PARENT_REG(reg);
557 if(mgr==NULL || mgr==par || !REGION_IS_PSEUDOACTIVE(mgr))
560 mgr->flags&=~REGION_PSEUDOACTIVE;
562 region_notify_change(mgr, ioncore_g.notifies.pseudoinactivated);
564 region_unset_manager_pseudoactivity(mgr);
568 void region_set_manager_pseudoactivity(WRegion *reg)
570 WRegion *mgr=reg->manager, *par=REGION_PARENT_REG(reg);
572 if(!REGION_IS_ACTIVE(reg) && !REGION_IS_PSEUDOACTIVE(reg))
575 if(mgr==NULL || mgr==par || REGION_IS_PSEUDOACTIVE(mgr))
578 mgr->flags|=REGION_PSEUDOACTIVE;
580 region_notify_change(mgr, ioncore_g.notifies.pseudoactivated);
582 region_set_manager_pseudoactivity(mgr);
586 /* This should only be called within region_managed_remove,
587 * _after_ any managed lists and other essential structures
588 * of mgr have been broken.
590 void region_unset_manager(WRegion *reg, WRegion *mgr)
592 if(reg->manager!=mgr)
595 region_notify_change_(reg, ioncore_g.notifies.unset_manager);
597 region_unset_manager_pseudoactivity(reg);
601 /* Reset status, as it is set by manager */
602 reg->flags&=~REGION_SKIP_FOCUS;
604 if(region_is_activity_r(reg))
605 region_clear_mgd_activity(mgr);
607 region_unset_return(reg);
611 /* This should be called within region attach routines,
612 * _after_ any managed lists and other essential structures
613 * of mgr have been set up.
615 void region_set_manager(WRegion *reg, WRegion *mgr)
617 assert(reg->manager==NULL);
621 region_set_manager_pseudoactivity(reg);
623 if(region_is_activity_r(reg))
624 region_mark_mgd_activity(mgr);
626 region_notify_change_(reg, ioncore_g.notifies.set_manager);
630 void region_set_parent(WRegion *reg, WWindow *parent)
632 assert(reg->parent==NULL && parent!=NULL);
633 LINK_ITEM(((WRegion*)parent)->children, reg, p_next, p_prev);
638 void region_unset_parent(WRegion *reg)
640 WRegion *p=REGION_PARENT_REG(reg);
642 if(p==NULL || p==reg)
645 UNLINK_ITEM(p->children, reg, p_next, p_prev);
648 if(p->active_sub==reg){
650 region_update_owned_grabs(p);
653 region_child_removed(p, reg);
658 * Returns the region that manages \var{reg}.
662 WRegion *region_manager(WRegion *reg)
669 * Returns the parent region of \var{reg}.
673 WWindow *region_parent(WRegion *reg)
679 WRegion *region_manager_or_parent(WRegion *reg)
681 if(reg->manager!=NULL)
684 return (WRegion*)(reg->parent);
688 WRegion *region_get_manager_chk(WRegion *p, const ClassDescr *descr)
693 mgr=REGION_MANAGER(p);
694 if(obj_is((Obj*)mgr, descr))
704 /*{{{ Stacking and ordering */
707 static void region_stacking_default(WRegion *reg,
708 Window *bottomret, Window *topret)
710 Window win=region_xwindow(reg);
716 void region_stacking(WRegion *reg, Window *bottomret, Window *topret)
718 CALL_DYN(region_stacking, reg, (reg, bottomret, topret));
722 void region_restack(WRegion *reg, Window other, int mode)
724 CALL_DYN(region_restack, reg, (reg, other, mode));
729 bool region_managed_rqorder(WRegion *reg, WRegion *sub, WRegionOrder order)
732 CALL_DYN_RET(ret, bool, region_managed_rqorder, reg, (reg, sub, order));
737 bool region_rqorder(WRegion *reg, WRegionOrder order)
739 WRegion *mgr=REGION_MANAGER(reg);
744 return region_managed_rqorder(mgr, reg, order);
749 * Request ordering. Currently supported values for \var{ord}
750 * are \codestr{front} and \codestr{back}.
752 EXTL_EXPORT_AS(WRegion, rqorder)
753 bool region_rqorder_extl(WRegion *reg, const char *ord)
757 if(strcmp(ord, "front")==0){
758 order=REGION_ORDER_FRONT;
759 }else if(strcmp(ord, "back")==0){
760 order=REGION_ORDER_BACK;
765 return region_rqorder(reg, order);
776 * Returns the root window \var{reg} is on.
780 WRootWin *region_rootwin_of(const WRegion *reg)
783 assert(reg!=NULL); /* Lua interface should not pass NULL reg. */
784 rw=(WRootWin*)(reg->rootwin);
791 * Returns the screen \var{reg} is on.
795 WScreen *region_screen_of(WRegion *reg)
798 if(OBJ_IS(reg, WScreen))
799 return (WScreen*)reg;
800 reg=REGION_PARENT_REG(reg);
806 Window region_root_of(const WRegion *reg)
808 return WROOTWIN_ROOT(region_rootwin_of(reg));
812 bool region_same_rootwin(const WRegion *reg1, const WRegion *reg2)
814 return (reg1->rootwin==reg2->rootwin);
819 * Is \var{reg} visible/is it and all it's ancestors mapped?
822 EXTL_EXPORT_AS(WRegion, is_mapped)
823 bool region_is_fully_mapped(WRegion *reg)
825 for(; reg!=NULL; reg=REGION_PARENT_REG(reg)){
826 if(!REGION_IS_MAPPED(reg))
834 void region_rootpos(WRegion *reg, int *xret, int *yret)
838 par=REGION_PARENT_REG(reg);
840 if(par==NULL || par==reg){
846 region_rootpos(par, xret, yret);
848 *xret+=REGION_GEOM(reg).x;
849 *yret+=REGION_GEOM(reg).y;
859 static bool mrsh_notify_change(WHookDummy *fn, void *p_)
869 static bool mrshe_notify_change(ExtlFn fn, void *p_)
873 extl_call(fn, "os", NULL, p->reg, stringstore_get(p->how));
879 static void region_notify_change_(WRegion *reg, WRegionNotify how)
887 hook_call(region_notify_hook, &p, mrsh_notify_change, mrshe_notify_change),
888 extl_unprotect(NULL);
892 void region_notify_change(WRegion *reg, WRegionNotify how)
894 WRegion *mgr=REGION_MANAGER(reg);
897 region_managed_notify(mgr, reg, how);
899 region_notify_change_(reg, how);
904 * Returns the geometry of \var{reg} within its parent; a table with fields
905 * \var{x}, \var{y}, \var{w} and \var{h}.
909 ExtlTab region_geom(WRegion *reg)
911 return extl_table_from_rectangle(®ION_GEOM(reg));
915 bool region_handle_drop(WRegion *reg, int x, int y, WRegion *dropped)
918 CALL_DYN_RET(ret, bool, region_handle_drop, reg, (reg, x, y, dropped));
923 WRegion *region_managed_within(WRegion *reg, WRegion *mgd)
926 (REGION_PARENT_REG(mgd)==reg ||
927 REGION_PARENT_REG(mgd)==REGION_PARENT_REG(reg))){
929 if(REGION_MANAGER(mgd)==reg)
931 mgd=REGION_MANAGER(mgd);
941 /*{{{ Dynamic function table and class implementation */
944 static DynFunTab region_dynfuntab[]={
945 {region_managed_rqgeom,
946 region_managed_rqgeom_allow},
948 {region_managed_rqgeom_absolute,
949 region_managed_rqgeom_absolute_default},
952 region_updategr_default},
954 {(DynFun*)region_rescue_clientwins,
955 (DynFun*)region_rescue_child_clientwins},
957 {(DynFun*)region_may_dispose,
958 (DynFun*)region_may_dispose_default},
960 {(DynFun*)region_prepare_manage,
961 (DynFun*)region_prepare_manage_default},
963 {(DynFun*)region_prepare_manage_transient,
964 (DynFun*)region_prepare_manage_transient_default},
966 {(DynFun*)region_managed_prepare_focus,
967 (DynFun*)region_managed_prepare_focus_default},
969 {(DynFun*)region_managed_disposeroot,
970 (DynFun*)region_managed_disposeroot_default},
972 {(DynFun*)region_rqclose_propagate,
973 (DynFun*)region_rqclose_propagate_default},
975 {(DynFun*)region_rqclose,
976 (DynFun*)region_rqclose_default},
978 {(DynFun*)region_displayname,
979 (DynFun*)region_name},
982 region_stacking_default},
989 IMPLCLASS(WRegion, Obj, region_deinit, region_dynfuntab);