4 * Copyright (c) Tuomo Valkonen 1999-2009.
6 * See the included file LICENSE for details.
11 #include <X11/Xatom.h>
13 #include <libtu/objp.h>
14 #include <libextl/extl.h>
15 #include <libmainloop/defer.h>
28 #include "region-iter.h"
36 WHook
*region_notify_hook
=NULL
;
39 static void region_notify_change_(WRegion
*reg
, WRegionNotify how
);
42 /*{{{ Init & deinit */
45 void region_init(WRegion
*reg
, WWindow
*par
, const WFitParams
*fp
)
47 if(fp
->g
.w
<0 || fp
->g
.h
<0)
48 warn(TR("Creating region with negative width or height!"));
61 reg
->active_prev
=NULL
;
62 reg
->active_next
=NULL
;
72 reg
->mgd_activity
=FALSE
;
75 reg
->rootwin
=((WRegion
*)par
)->rootwin
;
76 region_set_parent(reg
, par
);
78 assert(OBJ_IS(reg
, WRootWin
));
83 static void destroy_children(WRegion
*reg
)
85 WRegion
*sub
, *prev
=NULL
;
86 bool complained
=FALSE
;
88 /* destroy children */
93 assert(!OBJ_IS_BEING_DESTROYED(sub
));
95 if(ioncore_g
.opmode
!=IONCORE_OPMODE_DEINIT
&& !complained
&& OBJ_IS(reg
, WClientWin
)){
96 warn(TR("Destroying object \"%s\" with client windows as "
97 "children."), region_name(reg
));
101 destroy_obj((Obj
*)sub
);
106 void region_deinit(WRegion
*reg
)
108 region_notify_change(reg
, ioncore_g
.notifies
.deinit
);
110 destroy_children(reg
);
112 if(ioncore_g
.focus_next
==reg
){
113 D(warn("Region to be focused next destroyed[1]."));
114 ioncore_g
.focus_next
=NULL
;
117 assert(reg
->submapstat
==NULL
);
118 /*region_free_submapstat(reg);*/
119 region_detach_manager(reg
);
120 region_unset_return(reg
);
121 region_unset_parent(reg
);
122 region_remove_bindings(reg
);
124 region_unregister(reg
);
126 region_focuslist_deinit(reg
);
128 if(ioncore_g
.focus_next
==reg
){
129 D(warn("Region to be focused next destroyed[2]."));
130 ioncore_g
.focus_next
=NULL
;
141 bool region_fitrep(WRegion
*reg
, WWindow
*par
, const WFitParams
*fp
)
144 CALL_DYN_RET(ret
, bool, region_fitrep
, reg
, (reg
, par
, fp
));
149 void region_updategr(WRegion
*reg
)
151 CALL_DYN(region_updategr
, reg
, (reg
));
155 void region_map(WRegion
*reg
)
157 CALL_DYN(region_map
, reg
, (reg
));
158 region_notify_change_(reg
, ioncore_g
.notifies
.map
);
162 void region_unmap(WRegion
*reg
)
164 CALL_DYN(region_unmap
, reg
, (reg
));
165 region_notify_change_(reg
, ioncore_g
.notifies
.unmap
);
169 void region_notify_rootpos(WRegion
*reg
, int x
, int y
)
171 CALL_DYN(region_notify_rootpos
, reg
, (reg
, x
, y
));
175 Window
region_xwindow(const WRegion
*reg
)
178 CALL_DYN_RET(ret
, Window
, region_xwindow
, reg
, (reg
));
183 void region_activated(WRegion
*reg
)
185 CALL_DYN(region_activated
, reg
, (reg
));
189 void region_inactivated(WRegion
*reg
)
191 CALL_DYN(region_inactivated
, reg
, (reg
));
195 void region_do_set_focus(WRegion
*reg
, bool warp
)
197 CALL_DYN(region_do_set_focus
, reg
, (reg
, warp
));
201 /*{{{ Manager region dynfuns */
204 static bool region_managed_prepare_focus_default(WRegion
*mgr
, WRegion
*reg
,
206 WPrepareFocusResult
*res
)
208 if(!region_prepare_focus(mgr
, flags
, res
))
217 bool region_managed_prepare_focus(WRegion
*mgr
, WRegion
*reg
,
219 WPrepareFocusResult
*res
)
222 CALL_DYN_RET(ret
, bool, region_managed_prepare_focus
, mgr
,
223 (mgr
, reg
, flags
, res
));
228 void region_managed_notify(WRegion
*mgr
, WRegion
*reg
, WRegionNotify how
)
230 CALL_DYN(region_managed_notify
, mgr
, (mgr
, reg
, how
));
234 void region_managed_remove(WRegion
*mgr
, WRegion
*reg
)
236 CALL_DYN(region_managed_remove
, mgr
, (mgr
, reg
));
241 * Return the object, if any, that is considered ``currently active''
242 * within the objects managed by \var{mplex}.
246 WRegion
*region_current(WRegion
*mgr
)
249 CALL_DYN_RET(ret
, WRegion
*, region_current
, mgr
, (mgr
));
254 void region_child_removed(WRegion
*reg
, WRegion
*sub
)
256 CALL_DYN(region_child_removed
, reg
, (reg
, sub
));
263 /*{{{ Dynfun defaults */
266 void region_updategr_default(WRegion
*reg
)
270 FOR_ALL_CHILDREN(reg
, sub
){
271 region_updategr(sub
);
285 bool region_prepare_focus(WRegion
*reg
, int flags
,
286 WPrepareFocusResult
*res
)
289 if(TRUE
/* !REGION_IS_ACTIVE(reg) ||
290 !REGION_IS_MAPPED(reg) ||
291 ioncore_g.focus_next!=NULL*/){
292 WRegion
*mgr
=REGION_MANAGER(reg
);
293 WRegion
*par
=REGION_PARENT_REG(reg
);
296 return region_managed_prepare_focus(mgr
, reg
, flags
, res
);
298 if(!region_prepare_focus(par
, flags
, res
))
300 /* Just focus reg, if it has no manager, and parent can be
303 }else if(!REGION_IS_MAPPED(reg
)){
314 bool region_goto_flags(WRegion
*reg
, int flags
)
316 WPrepareFocusResult res
;
319 ret
=region_prepare_focus(reg
, flags
, &res
);
322 if(res
.flags
®ION_GOTO_FOCUS
)
323 region_maybewarp(res
.reg
, !(res
.flags
®ION_GOTO_NOWARP
));
331 * Attempt to display \var{reg}, save region activity status and then
332 * warp to (or simply set focus to if warping is disabled) \var{reg}.
334 * Note that this function is asynchronous; the region will not
335 * actually have received the focus when this function returns.
338 bool region_goto_focus(WRegion
*reg
)
340 return region_goto_flags(reg
, REGION_GOTO_FOCUS
);
344 * Deprecated in favour of \fnref{WRegion.goto_focus} because 'goto' is a
345 * keyword since Lua 5.2.
348 bool region_goto(WRegion
*reg
)
350 return region_goto_focus(reg
);
354 * Kept for backwards compatibility
357 bool region_goto_(WRegion
*reg
)
359 return region_goto_focus(reg
);
363 * Kept for backwards compatibility
366 bool region_display(WRegion
*reg
)
368 return region_goto_focus(reg
);
375 /*{{{ Fit/reparent */
378 void region_fit(WRegion
*reg
, const WRectangle
*geom
, WRegionFitMode mode
)
382 fp
.mode
=mode
&~REGION_FIT_GRAVITY
;
383 fp
.gravity
=ForgetGravity
;
384 region_fitrep(reg
, NULL
, &fp
);
388 bool region_reparent(WRegion
*reg
, WWindow
*par
,
389 const WRectangle
*geom
, WRegionFitMode mode
)
394 return region_fitrep(reg
, par
, &fp
);
404 static void region_rqclose_default(WRegion
*reg
, bool relocate
)
406 if(relocate
|| region_may_dispose(reg
))
407 region_defer_rqdispose(reg
);
412 * Attempt to close/destroy \var{reg}. Whether this operation works
413 * depends on whether the particular type of region in question has
414 * implemented the feature and, in case of client windows, whether
415 * the client supports the \code{WM_DELETE} protocol (see also
416 * \fnref{WClientWin.kill}). The region will not be destroyed when
417 * this function returns. To find out if and when it is destroyed,
418 * use the \codestr{deinit} notification. If \var{relocate} is not set,
419 * and \var{reg} manages other regions, it will not be closed. Otherwise
420 * the managed regions will be attempted to be relocated.
423 void region_rqclose(WRegion
*reg
, bool relocate
)
425 CALL_DYN(region_rqclose
, reg
, (reg
, relocate
));
429 static WRegion
*region_rqclose_propagate_default(WRegion
*reg
,
433 maybe_sub
=region_current(reg
);
436 return region_rqclose_propagate(maybe_sub
, NULL
);
438 region_rqclose(reg
, FALSE
);
445 * Recursively attempt to close a region or one of the regions managed by
446 * it. If \var{sub} is set, it will be used as the managed region, otherwise
447 * \fnref{WRegion.current}\code{(reg)}. The object to be closed is
448 * returned, or NULL if nothing can be closed. For further details, see
449 * notes for \fnref{WRegion.rqclose}.
452 WRegion
*region_rqclose_propagate(WRegion
*reg
, WRegion
*maybe_sub
)
455 CALL_DYN_RET(ret
, WRegion
*, region_rqclose_propagate
, reg
,
461 bool region_may_dispose_default(WRegion
*reg
)
463 bool res
=region_rescue_needed(reg
);
466 const char *name
=region_name(reg
);
467 warn(TR("Can not destroy %s: contains client windows."),
468 (name
!=NULL
? name
: TR("(unknown)")));
475 bool region_may_dispose(WRegion
*reg
)
478 CALL_DYN_RET(ret
, bool, region_may_dispose
, reg
, (reg
));
483 static WRegion
*region_managed_disposeroot_default(WRegion
*mgr
, WRegion
*reg
)
489 WRegion
*region_managed_disposeroot(WRegion
*mgr
, WRegion
*reg
)
492 CALL_DYN_RET(ret
, WRegion
*, region_managed_disposeroot
, mgr
, (mgr
, reg
));
497 WRegion
*region_disposeroot(WRegion
*reg
)
499 WRegion
*mgr
=REGION_MANAGER(reg
);
502 ? region_managed_disposeroot(mgr
, reg
)
507 bool region_rqdispose(WRegion
*reg
)
511 if(!region_may_dispose(reg
))
514 root
=region_disposeroot(reg
);
519 return region_dispose(root
);
523 bool region_dispose_(WRegion
*reg
, bool not_simple
)
525 bool rescue
=not_simple
;
526 bool was_mcf
=(not_simple
&& region_may_control_focus(reg
));
530 if(!region_rescue(reg
, NULL
, 0)){
531 warn(TR("Failed to rescue some client windows - not closing."));
537 ph
=region_unset_get_return(reg
);
539 destroy_obj((Obj
*)reg
);
543 destroy_obj((Obj
*)ph
);
550 bool region_dispose(WRegion
*reg
)
552 return region_dispose_(reg
, TRUE
);
556 void region_defer_rqdispose(WRegion
*reg
)
558 mainloop_defer_action((Obj
*)reg
, (WDeferredAction
*)region_rqdispose
);
565 /*{{{ Manager/parent stuff */
568 /* Routine to call to unmanage a region */
569 void region_detach_manager(WRegion
*reg
)
571 WRegion
*mgr
=REGION_MANAGER(reg
);
576 region_managed_remove(mgr
, reg
);
578 assert(REGION_MANAGER(reg
)==NULL
);
582 void region_unset_manager_pseudoactivity(WRegion
*reg
)
584 WRegion
*mgr
=reg
->manager
, *par
=REGION_PARENT_REG(reg
);
586 if(mgr
==NULL
|| mgr
==par
|| !REGION_IS_PSEUDOACTIVE(mgr
))
589 mgr
->flags
&=~REGION_PSEUDOACTIVE
;
591 region_notify_change(mgr
, ioncore_g
.notifies
.pseudoinactivated
);
593 region_unset_manager_pseudoactivity(mgr
);
597 void region_set_manager_pseudoactivity(WRegion
*reg
)
599 WRegion
*mgr
=reg
->manager
, *par
=REGION_PARENT_REG(reg
);
601 if(!REGION_IS_ACTIVE(reg
) && !REGION_IS_PSEUDOACTIVE(reg
))
604 if(mgr
==NULL
|| mgr
==par
|| REGION_IS_PSEUDOACTIVE(mgr
))
607 mgr
->flags
|=REGION_PSEUDOACTIVE
;
609 region_notify_change(mgr
, ioncore_g
.notifies
.pseudoactivated
);
611 region_set_manager_pseudoactivity(mgr
);
615 /* This should only be called within region_managed_remove,
616 * _after_ any managed lists and other essential structures
617 * of mgr have been broken.
619 void region_unset_manager(WRegion
*reg
, WRegion
*mgr
)
621 if(reg
->manager
!=mgr
)
624 region_notify_change_(reg
, ioncore_g
.notifies
.unset_manager
);
626 region_unset_manager_pseudoactivity(reg
);
630 /* Reset status, as it is set by manager */
631 reg
->flags
&=~REGION_SKIP_FOCUS
;
633 if(region_is_activity_r(reg
))
634 region_clear_mgd_activity(mgr
);
636 region_unset_return(reg
);
640 /* This should be called within region attach routines,
641 * _after_ any managed lists and other essential structures
642 * of mgr have been set up.
644 void region_set_manager(WRegion
*reg
, WRegion
*mgr
)
646 assert(reg
->manager
==NULL
);
650 region_set_manager_pseudoactivity(reg
);
652 if(region_is_activity_r(reg
))
653 region_mark_mgd_activity(mgr
);
655 region_notify_change_(reg
, ioncore_g
.notifies
.set_manager
);
659 void region_set_parent(WRegion
*reg
, WWindow
*parent
)
661 assert(reg
->parent
==NULL
&& parent
!=NULL
);
662 LINK_ITEM(((WRegion
*)parent
)->children
, reg
, p_next
, p_prev
);
667 void region_unset_parent(WRegion
*reg
)
669 WRegion
*p
=REGION_PARENT_REG(reg
);
671 if(p
==NULL
|| p
==reg
)
674 UNLINK_ITEM(p
->children
, reg
, p_next
, p_prev
);
677 if(p
->active_sub
==reg
){
679 region_update_owned_grabs(p
);
682 region_child_removed(p
, reg
);
687 * Returns the region that manages \var{reg}.
691 WRegion
*region_manager(WRegion
*reg
)
698 * Returns the parent region of \var{reg}.
702 WWindow
*region_parent(WRegion
*reg
)
708 WRegion
*region_manager_or_parent(WRegion
*reg
)
710 if(reg
->manager
!=NULL
)
713 return (WRegion
*)(reg
->parent
);
717 WRegion
*region_get_manager_chk(WRegion
*p
, const ClassDescr
*descr
)
722 mgr
=REGION_MANAGER(p
);
723 if(obj_is((Obj
*)mgr
, descr
))
733 /*{{{ Stacking and ordering */
736 static void region_stacking_default(WRegion
*reg
,
737 Window
*bottomret
, Window
*topret
)
739 Window win
=region_xwindow(reg
);
745 void region_stacking(WRegion
*reg
, Window
*bottomret
, Window
*topret
)
747 CALL_DYN(region_stacking
, reg
, (reg
, bottomret
, topret
));
751 void region_restack(WRegion
*reg
, Window other
, int mode
)
753 CALL_DYN(region_restack
, reg
, (reg
, other
, mode
));
758 bool region_managed_rqorder(WRegion
*reg
, WRegion
*sub
, WRegionOrder order
)
761 CALL_DYN_RET(ret
, bool, region_managed_rqorder
, reg
, (reg
, sub
, order
));
766 bool region_rqorder(WRegion
*reg
, WRegionOrder order
)
768 WRegion
*mgr
=REGION_MANAGER(reg
);
773 return region_managed_rqorder(mgr
, reg
, order
);
778 * Request ordering. Currently supported values for \var{ord}
779 * are \codestr{front} and \codestr{back}.
781 EXTL_EXPORT_AS(WRegion
, rqorder
)
782 bool region_rqorder_extl(WRegion
*reg
, const char *ord
)
786 if(strcmp(ord
, "front")==0){
787 order
=REGION_ORDER_FRONT
;
788 }else if(strcmp(ord
, "back")==0){
789 order
=REGION_ORDER_BACK
;
794 return region_rqorder(reg
, order
);
805 * Returns the root window \var{reg} is on.
809 WRootWin
*region_rootwin_of(const WRegion
*reg
)
812 assert(reg
!=NULL
); /* Lua interface should not pass NULL reg. */
813 rw
=(WRootWin
*)(reg
->rootwin
);
820 * Returns the screen \var{reg} is on.
824 WScreen
*region_screen_of(WRegion
*reg
)
827 if(OBJ_IS(reg
, WScreen
))
828 return (WScreen
*)reg
;
829 reg
=REGION_PARENT_REG(reg
);
835 Window
region_root_of(const WRegion
*reg
)
837 return WROOTWIN_ROOT(region_rootwin_of(reg
));
841 bool region_same_rootwin(const WRegion
*reg1
, const WRegion
*reg2
)
843 return (reg1
->rootwin
==reg2
->rootwin
);
848 * Is \var{reg} visible/is it and all it's ancestors mapped?
851 EXTL_EXPORT_AS(WRegion
, is_mapped
)
852 bool region_is_fully_mapped(WRegion
*reg
)
854 for(; reg
!=NULL
; reg
=REGION_PARENT_REG(reg
)){
855 if(!REGION_IS_MAPPED(reg
))
863 void region_rootpos(WRegion
*reg
, int *xret
, int *yret
)
867 par
=REGION_PARENT_REG(reg
);
869 if(par
==NULL
|| par
==reg
){
875 region_rootpos(par
, xret
, yret
);
877 *xret
+=REGION_GEOM(reg
).x
;
878 *yret
+=REGION_GEOM(reg
).y
;
888 static bool mrsh_notify_change(WHookDummy
*fn
, void *p_
)
898 static bool mrshe_notify_change(ExtlFn fn
, void *p_
)
902 extl_call(fn
, "os", NULL
, p
->reg
, stringstore_get(p
->how
));
908 static void region_notify_change_(WRegion
*reg
, WRegionNotify how
)
916 hook_call(region_notify_hook
, &p
, mrsh_notify_change
, mrshe_notify_change
),
917 extl_unprotect(NULL
);
921 void region_notify_change(WRegion
*reg
, WRegionNotify how
)
923 WRegion
*mgr
=REGION_MANAGER(reg
);
926 region_managed_notify(mgr
, reg
, how
);
928 region_notify_change_(reg
, how
);
933 * Returns the geometry of \var{reg} within its parent; a table with fields
934 * \var{x}, \var{y}, \var{w} and \var{h}.
938 ExtlTab
region_geom(WRegion
*reg
)
940 return extl_table_from_rectangle(®ION_GEOM(reg
));
944 bool region_handle_drop(WRegion
*reg
, int x
, int y
, WRegion
*dropped
)
947 CALL_DYN_RET(ret
, bool, region_handle_drop
, reg
, (reg
, x
, y
, dropped
));
952 WRegion
*region_managed_within(WRegion
*reg
, WRegion
*mgd
)
955 (REGION_PARENT_REG(mgd
)==reg
||
956 REGION_PARENT_REG(mgd
)==REGION_PARENT_REG(reg
))){
958 if(REGION_MANAGER(mgd
)==reg
)
960 mgd
=REGION_MANAGER(mgd
);
966 void ioncore_region_notify(WRegion
*reg
, WRegionNotify how
)
970 if(how
==ioncore_g
.notifies
.name
&& obj_is((Obj
*)reg
, &CLASSDESCR(WWindow
))){
971 p
[0] = region_name(reg
);
972 xwindow_set_text_property(((WWindow
*)reg
)->win
, XA_WM_NAME
, p
, 1);
979 /*{{{ Dynamic function table and class implementation */
982 static DynFunTab region_dynfuntab
[]={
983 {region_managed_rqgeom
,
984 region_managed_rqgeom_allow
},
986 {region_managed_rqgeom_absolute
,
987 region_managed_rqgeom_absolute_default
},
990 region_updategr_default
},
992 {(DynFun
*)region_rescue_clientwins
,
993 (DynFun
*)region_rescue_child_clientwins
},
995 {(DynFun
*)region_may_dispose
,
996 (DynFun
*)region_may_dispose_default
},
998 {(DynFun
*)region_prepare_manage
,
999 (DynFun
*)region_prepare_manage_default
},
1001 {(DynFun
*)region_prepare_manage_transient
,
1002 (DynFun
*)region_prepare_manage_transient_default
},
1004 {(DynFun
*)region_managed_prepare_focus
,
1005 (DynFun
*)region_managed_prepare_focus_default
},
1007 {(DynFun
*)region_managed_disposeroot
,
1008 (DynFun
*)region_managed_disposeroot_default
},
1010 {(DynFun
*)region_rqclose_propagate
,
1011 (DynFun
*)region_rqclose_propagate_default
},
1013 {(DynFun
*)region_rqclose
,
1014 (DynFun
*)region_rqclose_default
},
1016 {(DynFun
*)region_displayname
,
1017 (DynFun
*)region_name
},
1020 region_stacking_default
},
1027 IMPLCLASS(WRegion
, Obj
, region_deinit
, region_dynfuntab
);