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"
32 #include "screen-notify.h"
37 WHook
*region_notify_hook
=NULL
;
40 static void region_notify_change_(WRegion
*reg
, WRegionNotify how
);
43 /*{{{ Init & deinit */
46 void region_init(WRegion
*reg
, WWindow
*par
, const WFitParams
*fp
)
48 if(fp
->g
.w
<0 || fp
->g
.h
<0)
49 warn(TR("Creating region with negative width or height!"));
62 reg
->active_prev
=NULL
;
63 reg
->active_next
=NULL
;
73 reg
->mgd_activity
=FALSE
;
76 reg
->rootwin
=((WRegion
*)par
)->rootwin
;
77 region_set_parent(reg
, par
);
79 assert(OBJ_IS(reg
, WRootWin
));
84 static void destroy_children(WRegion
*reg
)
86 WRegion
*sub
, *prev
=NULL
;
87 bool complained
=FALSE
;
89 /* destroy children */
94 assert(!OBJ_IS_BEING_DESTROYED(sub
));
96 if(ioncore_g
.opmode
!=IONCORE_OPMODE_DEINIT
&& !complained
&& OBJ_IS(reg
, WClientWin
)){
97 warn(TR("Destroying object \"%s\" with client windows as "
98 "children."), region_name(reg
));
102 destroy_obj((Obj
*)sub
);
107 void region_deinit(WRegion
*reg
)
109 region_notify_change(reg
, ioncore_g
.notifies
.deinit
);
111 destroy_children(reg
);
113 if(ioncore_g
.focus_next
==reg
){
114 D(warn("Region to be focused next destroyed[1]."));
115 ioncore_g
.focus_next
=NULL
;
118 assert(reg
->submapstat
==NULL
);
119 /*region_free_submapstat(reg);*/
120 region_detach_manager(reg
);
121 region_unset_return(reg
);
122 region_unset_parent(reg
);
123 region_remove_bindings(reg
);
125 region_unregister(reg
);
127 region_focus_deinit(reg
);
129 if(ioncore_g
.focus_next
==reg
){
130 D(warn("Region to be focused next destroyed[2]."));
131 ioncore_g
.focus_next
=NULL
;
142 bool region_fitrep(WRegion
*reg
, WWindow
*par
, const WFitParams
*fp
)
145 CALL_DYN_RET(ret
, bool, region_fitrep
, reg
, (reg
, par
, fp
));
150 void region_updategr(WRegion
*reg
)
152 CALL_DYN(region_updategr
, reg
, (reg
));
156 void region_map(WRegion
*reg
)
158 CALL_DYN(region_map
, reg
, (reg
));
159 region_notify_change_(reg
, ioncore_g
.notifies
.map
);
163 void region_unmap(WRegion
*reg
)
165 CALL_DYN(region_unmap
, reg
, (reg
));
166 region_notify_change_(reg
, ioncore_g
.notifies
.unmap
);
170 void region_notify_rootpos(WRegion
*reg
, int x
, int y
)
172 CALL_DYN(region_notify_rootpos
, reg
, (reg
, x
, y
));
176 Window
region_xwindow(const WRegion
*reg
)
179 CALL_DYN_RET(ret
, Window
, region_xwindow
, reg
, (reg
));
184 void region_activated(WRegion
*reg
)
186 CALL_DYN(region_activated
, reg
, (reg
));
190 void region_inactivated(WRegion
*reg
)
192 CALL_DYN(region_inactivated
, reg
, (reg
));
196 void region_do_set_focus(WRegion
*reg
, bool warp
)
198 CALL_DYN(region_do_set_focus
, reg
, (reg
, warp
));
202 /*{{{ Manager region dynfuns */
205 static bool region_managed_prepare_focus_default(WRegion
*mgr
, WRegion
*reg
,
207 WPrepareFocusResult
*res
)
209 if(!region_prepare_focus(mgr
, flags
, res
))
218 bool region_managed_prepare_focus(WRegion
*mgr
, WRegion
*reg
,
220 WPrepareFocusResult
*res
)
223 CALL_DYN_RET(ret
, bool, region_managed_prepare_focus
, mgr
,
224 (mgr
, reg
, flags
, res
));
229 void region_managed_notify(WRegion
*mgr
, WRegion
*reg
, WRegionNotify how
)
231 CALL_DYN(region_managed_notify
, mgr
, (mgr
, reg
, how
));
235 void region_managed_remove(WRegion
*mgr
, WRegion
*reg
)
237 CALL_DYN(region_managed_remove
, mgr
, (mgr
, reg
));
242 * Return the object, if any, that is considered ``currently active''
243 * within the objects managed by \var{mplex}.
247 WRegion
*region_current(WRegion
*mgr
)
250 CALL_DYN_RET(ret
, WRegion
*, region_current
, mgr
, (mgr
));
255 void region_child_removed(WRegion
*reg
, WRegion
*sub
)
257 CALL_DYN(region_child_removed
, reg
, (reg
, sub
));
264 /*{{{ Dynfun defaults */
267 void region_updategr_default(WRegion
*reg
)
271 FOR_ALL_CHILDREN(reg
, sub
){
272 region_updategr(sub
);
286 bool region_prepare_focus(WRegion
*reg
, int flags
,
287 WPrepareFocusResult
*res
)
290 if(TRUE
/* !REGION_IS_ACTIVE(reg) ||
291 !REGION_IS_MAPPED(reg) ||
292 ioncore_g.focus_next!=NULL*/){
293 WRegion
*mgr
=REGION_MANAGER(reg
);
294 WRegion
*par
=REGION_PARENT_REG(reg
);
297 return region_managed_prepare_focus(mgr
, reg
, flags
, res
);
299 if(!region_prepare_focus(par
, flags
, res
))
301 /* Just focus reg, if it has no manager, and parent can be
304 }else if(!REGION_IS_MAPPED(reg
)){
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_focus(WRegion
*reg
)
341 return region_goto_flags(reg
, REGION_GOTO_FOCUS
);
345 * Deprecated in favour of \fnref{WRegion.goto_focus} because 'goto' is a
346 * keyword since Lua 5.2.
349 bool region_goto(WRegion
*reg
)
351 return region_goto_focus(reg
);
355 * Kept for backwards compatibility
358 bool region_goto_(WRegion
*reg
)
360 return region_goto_focus(reg
);
364 * Kept for backwards compatibility
367 bool region_display(WRegion
*reg
)
369 return region_goto_focus(reg
);
376 /*{{{ Fit/reparent */
379 void region_fit(WRegion
*reg
, const WRectangle
*geom
, WRegionFitMode mode
)
383 fp
.mode
=mode
&~REGION_FIT_GRAVITY
;
384 fp
.gravity
=ForgetGravity
;
385 region_fitrep(reg
, NULL
, &fp
);
389 bool region_reparent(WRegion
*reg
, WWindow
*par
,
390 const WRectangle
*geom
, WRegionFitMode mode
)
395 return region_fitrep(reg
, par
, &fp
);
405 static void region_rqclose_default(WRegion
*reg
, bool relocate
)
407 if(relocate
|| region_may_dispose(reg
))
408 region_defer_rqdispose(reg
);
413 * Attempt to close/destroy \var{reg}. Whether this operation works
414 * depends on whether the particular type of region in question has
415 * implemented the feature and, in case of client windows, whether
416 * the client supports the \code{WM_DELETE} protocol (see also
417 * \fnref{WClientWin.kill}). The region will not be destroyed when
418 * this function returns. To find out if and when it is destroyed,
419 * use the \codestr{deinit} notification. If \var{relocate} is not set,
420 * and \var{reg} manages other regions, it will not be closed. Otherwise
421 * the managed regions will be attempted to be relocated.
424 void region_rqclose(WRegion
*reg
, bool relocate
)
426 CALL_DYN(region_rqclose
, reg
, (reg
, relocate
));
430 static WRegion
*region_rqclose_propagate_default(WRegion
*reg
,
434 maybe_sub
=region_current(reg
);
437 return region_rqclose_propagate(maybe_sub
, NULL
);
439 region_rqclose(reg
, FALSE
);
446 * Recursively attempt to close a region or one of the regions managed by
447 * it. If \var{sub} is set, it will be used as the managed region, otherwise
448 * \fnref{WRegion.current}\code{(reg)}. The object to be closed is
449 * returned, or NULL if nothing can be closed. For further details, see
450 * notes for \fnref{WRegion.rqclose}.
453 WRegion
*region_rqclose_propagate(WRegion
*reg
, WRegion
*maybe_sub
)
456 CALL_DYN_RET(ret
, WRegion
*, region_rqclose_propagate
, reg
,
462 bool region_may_dispose_default(WRegion
*reg
)
464 bool res
=region_rescue_needed(reg
);
467 const char *name
=region_name(reg
);
468 warn(TR("Can not destroy %s: contains client windows."),
469 (name
!=NULL
? name
: TR("(unknown)")));
476 bool region_may_dispose(WRegion
*reg
)
479 CALL_DYN_RET(ret
, bool, region_may_dispose
, reg
, (reg
));
484 static WRegion
*region_managed_disposeroot_default(WRegion
*UNUSED(mgr
), WRegion
*reg
)
490 WRegion
*region_managed_disposeroot(WRegion
*mgr
, WRegion
*reg
)
493 CALL_DYN_RET(ret
, WRegion
*, region_managed_disposeroot
, mgr
, (mgr
, reg
));
498 WRegion
*region_disposeroot(WRegion
*reg
)
500 WRegion
*mgr
=REGION_MANAGER(reg
);
503 ? region_managed_disposeroot(mgr
, reg
)
508 bool region_rqdispose(WRegion
*reg
)
512 if(!region_may_dispose(reg
))
515 root
=region_disposeroot(reg
);
520 return region_dispose(root
);
524 bool region_dispose_(WRegion
*reg
, bool not_simple
)
526 bool rescue
=not_simple
;
527 bool was_mcf
=(not_simple
&& region_may_control_focus(reg
));
531 if(!region_rescue(reg
, NULL
, 0)){
532 warn(TR("Failed to rescue some client windows - not closing."));
538 ph
=region_unset_get_return(reg
);
540 destroy_obj((Obj
*)reg
);
544 destroy_obj((Obj
*)ph
);
551 bool region_dispose(WRegion
*reg
)
553 return region_dispose_(reg
, TRUE
);
557 void region_defer_rqdispose(WRegion
*reg
)
559 mainloop_defer_action((Obj
*)reg
, (WDeferredAction
*)region_rqdispose
);
566 /*{{{ Manager/parent stuff */
569 /* Routine to call to unmanage a region */
570 void region_detach_manager(WRegion
*reg
)
572 WRegion
*mgr
=REGION_MANAGER(reg
);
577 region_managed_remove(mgr
, reg
);
579 assert(REGION_MANAGER(reg
)==NULL
);
583 void region_unset_manager_pseudoactivity(WRegion
*reg
)
585 WRegion
*mgr
=reg
->manager
, *par
=REGION_PARENT_REG(reg
);
587 if(mgr
==NULL
|| mgr
==par
|| !REGION_IS_PSEUDOACTIVE(mgr
))
590 mgr
->flags
&=~REGION_PSEUDOACTIVE
;
592 region_notify_change(mgr
, ioncore_g
.notifies
.pseudoinactivated
);
594 region_unset_manager_pseudoactivity(mgr
);
598 void region_set_manager_pseudoactivity(WRegion
*reg
)
600 WRegion
*mgr
=reg
->manager
, *par
=REGION_PARENT_REG(reg
);
602 if(!REGION_IS_ACTIVE(reg
) && !REGION_IS_PSEUDOACTIVE(reg
))
605 if(mgr
==NULL
|| mgr
==par
|| REGION_IS_PSEUDOACTIVE(mgr
))
608 mgr
->flags
|=REGION_PSEUDOACTIVE
;
610 region_notify_change(mgr
, ioncore_g
.notifies
.pseudoactivated
);
612 region_set_manager_pseudoactivity(mgr
);
616 /* This should only be called within region_managed_remove,
617 * _after_ any managed lists and other essential structures
618 * of mgr have been broken.
620 void region_unset_manager(WRegion
*reg
, WRegion
*mgr
)
622 if(reg
->manager
!=mgr
)
625 region_notify_change_(reg
, ioncore_g
.notifies
.unset_manager
);
627 region_unset_manager_pseudoactivity(reg
);
631 /* Reset status, as it is set by manager */
632 reg
->flags
&=~REGION_SKIP_FOCUS
;
634 if(region_is_activity_r(reg
))
635 region_clear_mgd_activity(mgr
);
637 region_unset_return(reg
);
641 /* This should be called within region attach routines,
642 * _after_ any managed lists and other essential structures
643 * of mgr have been set up.
645 void region_set_manager(WRegion
*reg
, WRegion
*mgr
)
647 assert(reg
->manager
==NULL
);
651 region_set_manager_pseudoactivity(reg
);
653 if(region_is_activity_r(reg
))
654 region_mark_mgd_activity(mgr
);
656 region_notify_change_(reg
, ioncore_g
.notifies
.set_manager
);
660 void region_set_parent(WRegion
*reg
, WWindow
*parent
)
662 assert(reg
->parent
==NULL
&& parent
!=NULL
);
663 LINK_ITEM(((WRegion
*)parent
)->children
, reg
, p_next
, p_prev
);
668 void region_unset_parent(WRegion
*reg
)
670 WRegion
*p
=REGION_PARENT_REG(reg
);
672 if(p
==NULL
|| p
==reg
)
675 UNLINK_ITEM(p
->children
, reg
, p_next
, p_prev
);
678 if(p
->active_sub
==reg
){
680 region_update_owned_grabs(p
);
683 region_child_removed(p
, reg
);
688 * Returns the region that manages \var{reg}.
692 WRegion
*region_manager(WRegion
*reg
)
699 * Returns the parent region of \var{reg}.
703 WWindow
*region_parent(WRegion
*reg
)
709 WRegion
*region_manager_or_parent(WRegion
*reg
)
711 if(reg
->manager
!=NULL
)
714 return (WRegion
*)(reg
->parent
);
718 WRegion
*region_get_manager_chk(WRegion
*p
, const ClassDescr
*descr
)
723 mgr
=REGION_MANAGER(p
);
724 if(obj_is((Obj
*)mgr
, descr
))
734 /*{{{ Stacking and ordering */
737 static void region_stacking_default(WRegion
*reg
,
738 Window
*bottomret
, Window
*topret
)
740 Window win
=region_xwindow(reg
);
746 void region_stacking(WRegion
*reg
, Window
*bottomret
, Window
*topret
)
748 CALL_DYN(region_stacking
, reg
, (reg
, bottomret
, topret
));
752 void region_restack(WRegion
*reg
, Window other
, int mode
)
754 CALL_DYN(region_restack
, reg
, (reg
, other
, mode
));
759 bool region_managed_rqorder(WRegion
*reg
, WRegion
*sub
, WRegionOrder order
)
762 CALL_DYN_RET(ret
, bool, region_managed_rqorder
, reg
, (reg
, sub
, order
));
767 bool region_rqorder(WRegion
*reg
, WRegionOrder order
)
769 WRegion
*mgr
=REGION_MANAGER(reg
);
774 return region_managed_rqorder(mgr
, reg
, order
);
779 * Request ordering. Currently supported values for \var{ord}
780 * are \codestr{front} and \codestr{back}.
782 EXTL_EXPORT_AS(WRegion
, rqorder
)
783 bool region_rqorder_extl(WRegion
*reg
, const char *ord
)
787 if(strcmp(ord
, "front")==0){
788 order
=REGION_ORDER_FRONT
;
789 }else if(strcmp(ord
, "back")==0){
790 order
=REGION_ORDER_BACK
;
795 return region_rqorder(reg
, order
);
806 * Returns the root window \var{reg} is on.
810 WRootWin
*region_rootwin_of(const WRegion
*reg
)
813 assert(reg
!=NULL
); /* Lua interface should not pass NULL reg. */
814 rw
=(WRootWin
*)(reg
->rootwin
);
821 * Returns the screen \var{reg} is on.
825 WScreen
*region_screen_of(WRegion
*reg
)
828 if(OBJ_IS(reg
, WScreen
))
829 return (WScreen
*)reg
;
830 reg
=REGION_PARENT_REG(reg
);
836 Window
region_root_of(const WRegion
*reg
)
838 return WROOTWIN_ROOT(region_rootwin_of(reg
));
842 bool region_same_rootwin(const WRegion
*reg1
, const WRegion
*reg2
)
844 return (reg1
->rootwin
==reg2
->rootwin
);
849 * Is \var{reg} visible/is it and all it's ancestors mapped?
852 EXTL_EXPORT_AS(WRegion
, is_mapped
)
853 bool region_is_fully_mapped(WRegion
*reg
)
855 for(; reg
!=NULL
; reg
=REGION_PARENT_REG(reg
)){
856 if(!REGION_IS_MAPPED(reg
))
864 void region_rootpos(WRegion
*reg
, int *xret
, int *yret
)
868 par
=REGION_PARENT_REG(reg
);
870 if(par
==NULL
|| par
==reg
){
876 region_rootpos(par
, xret
, yret
);
878 *xret
+=REGION_GEOM(reg
).x
;
879 *yret
+=REGION_GEOM(reg
).y
;
889 static bool mrsh_notify_change(WHookDummy
*fn
, void *p_
)
899 static bool mrshe_notify_change(ExtlFn fn
, void *p_
)
903 extl_call(fn
, "os", NULL
, p
->reg
, stringstore_get(p
->how
));
909 static void region_notify_change_(WRegion
*reg
, WRegionNotify how
)
917 hook_call(region_notify_hook
, &p
, mrsh_notify_change
, mrshe_notify_change
),
918 extl_unprotect(NULL
);
922 void region_notify_change(WRegion
*reg
, WRegionNotify how
)
924 WRegion
*mgr
=REGION_MANAGER(reg
);
927 region_managed_notify(mgr
, reg
, how
);
929 region_notify_change_(reg
, how
);
934 * Returns the geometry of \var{reg} within its parent; a table with fields
935 * \var{x}, \var{y}, \var{w} and \var{h}.
939 ExtlTab
region_geom(WRegion
*reg
)
941 return extl_table_from_rectangle(®ION_GEOM(reg
));
945 bool region_handle_drop(WRegion
*reg
, int x
, int y
, WRegion
*dropped
)
948 CALL_DYN_RET(ret
, bool, region_handle_drop
, reg
, (reg
, x
, y
, dropped
));
953 WRegion
*region_managed_within(WRegion
*reg
, WRegion
*mgd
)
956 (REGION_PARENT_REG(mgd
)==reg
||
957 REGION_PARENT_REG(mgd
)==REGION_PARENT_REG(reg
))){
959 if(REGION_MANAGER(mgd
)==reg
)
961 mgd
=REGION_MANAGER(mgd
);
967 void ioncore_region_notify(WRegion
*reg
, WRegionNotify how
)
971 if(how
==ioncore_g
.notifies
.name
&& obj_is((Obj
*)reg
, &CLASSDESCR(WWindow
))){
972 p
[0] = region_name(reg
);
973 xwindow_set_text_property(((WWindow
*)reg
)->win
, XA_WM_NAME
, p
, 1);
979 /*{{{ Debug printers */
981 #define REGION_DEBUGPRINT( what, next ) \
984 char line_indent[24]; \
985 line_indent[0] = ' '; \
986 line_indent[1] = '\0'; \
988 LOG(DEBUG, GENERAL, what " list start ========:"); \
989 while( reg != NULL ) \
991 LOG(DEBUG, GENERAL, "%s%p (%s)", line_indent, (void*)reg, reg->ni.name); \
993 if( indent == sizeof(line_indent )-1 ) \
995 LOG(DEBUG, GENERAL, "too long. cut off"); \
1000 line_indent[indent] = ' '; \
1002 line_indent[indent] = '\0'; \
1004 LOG(DEBUG, GENERAL, what " list end =========="); \
1007 void region_debugprint_parents ( const WRegion
* reg
)
1009 REGION_DEBUGPRINT( "parent", reg
->parent
&& reg
->parent
->region
.ni
.name
? ®
->parent
->region
: NULL
);
1012 void region_debugprint_managers( const WRegion
* reg
)
1014 REGION_DEBUGPRINT( "manager", reg
->manager
);
1017 #undef REGION_DEBUGPRINT
1022 /*{{{ Dynamic function table and class implementation */
1025 static DynFunTab region_dynfuntab
[]={
1026 {region_managed_rqgeom
,
1027 region_managed_rqgeom_allow
},
1029 {region_managed_rqgeom_absolute
,
1030 region_managed_rqgeom_absolute_default
},
1033 region_updategr_default
},
1035 {(DynFun
*)region_rescue_clientwins
,
1036 (DynFun
*)region_rescue_child_clientwins
},
1038 {(DynFun
*)region_may_dispose
,
1039 (DynFun
*)region_may_dispose_default
},
1041 {(DynFun
*)region_prepare_manage
,
1042 (DynFun
*)region_prepare_manage_default
},
1044 {(DynFun
*)region_prepare_manage_transient
,
1045 (DynFun
*)region_prepare_manage_transient_default
},
1047 {(DynFun
*)region_managed_prepare_focus
,
1048 (DynFun
*)region_managed_prepare_focus_default
},
1050 {(DynFun
*)region_managed_disposeroot
,
1051 (DynFun
*)region_managed_disposeroot_default
},
1053 {(DynFun
*)region_rqclose_propagate
,
1054 (DynFun
*)region_rqclose_propagate_default
},
1056 {(DynFun
*)region_rqclose
,
1057 (DynFun
*)region_rqclose_default
},
1059 {(DynFun
*)region_displayname
,
1060 (DynFun
*)region_name
},
1063 region_stacking_default
},
1070 IMPLCLASS(WRegion
, Obj
, region_deinit
, region_dynfuntab
);