4 * Copyright (c) Tuomo Valkonen 1999-2009.
6 * See the included file LICENSE for details.
9 #include <libmainloop/hooks.h>
24 WHook
*region_do_warp_alt
=NULL
;
33 void region_focuslist_remove_with_mgrs(WRegion
*reg
)
35 WRegion
*mgrp
=region_manager_or_parent(reg
);
37 UNLINK_ITEM(ioncore_g
.focus_current
, reg
, active_next
, active_prev
);
40 region_focuslist_remove_with_mgrs(mgrp
);
44 void region_focuslist_push(WRegion
*reg
)
46 region_focuslist_remove_with_mgrs(reg
);
47 LINK_ITEM_FIRST(ioncore_g
.focus_current
, reg
, active_next
, active_prev
);
51 void region_focuslist_move_after(WRegion
*reg
, WRegion
*after
)
53 region_focuslist_remove_with_mgrs(reg
);
54 LINK_ITEM_AFTER(ioncore_g
.focus_current
, after
, reg
,
55 active_next
, active_prev
);
59 void region_focuslist_deinit(WRegion
*reg
)
61 WRegion
*replace
=region_manager_or_parent(reg
);
64 region_focuslist_move_after(replace
, reg
);
66 UNLINK_ITEM(ioncore_g
.focus_current
, reg
, active_next
, active_prev
);
71 * Go to and return to a previously active region (if any).
73 * Note that this function is asynchronous; the region will not
74 * actually have received the focus when this function returns.
77 WRegion
*ioncore_goto_previous()
81 if(ioncore_g
.focus_current
==NULL
)
84 /* Find the first region on focus history list that isn't currently
87 for(next
=ioncore_g
.focus_current
->active_next
;
89 next
=next
->active_next
){
91 if(!REGION_IS_ACTIVE(next
))
103 * Iterate over focus history until \var{iterfn} returns \code{false}.
104 * The function is called in protected mode.
105 * This routine returns \code{true} if it reaches the end of list
106 * without this happening.
109 bool ioncore_focushistory_i(ExtlFn iterfn
)
113 if(ioncore_g
.focus_current
==NULL
)
116 /* Find the first region on focus history list that isn't currently
119 for(next
=ioncore_g
.focus_current
->active_next
;
121 next
=next
->active_next
){
123 if(!extl_iter_obj(iterfn
, (Obj
*)next
))
137 static Watch await_watch
=WATCH_INIT
;
140 static void await_watch_handler(Watch
*watch
, WRegion
*prev
)
144 r
=REGION_PARENT_REG(prev
);
148 if(watch_setup(&await_watch
, (Obj
*)r
,
149 (WatchHandler
*)await_watch_handler
))
156 void region_set_await_focus(WRegion
*reg
)
159 watch_reset(&await_watch
);
161 watch_setup(&await_watch
, (Obj
*)reg
,
162 (WatchHandler
*)await_watch_handler
);
167 static bool region_is_parent(WRegion
*reg
, WRegion
*aw
)
172 aw
=REGION_PARENT_REG(aw
);
179 static bool region_is_await(WRegion
*reg
)
181 return region_is_parent(reg
, (WRegion
*)await_watch
.obj
);
185 static bool region_is_focusnext(WRegion
*reg
)
187 return region_is_parent(reg
, ioncore_g
.focus_next
);
191 /* Only keep await status if focus event is to an ancestor of the await
194 static void check_clear_await(WRegion
*reg
)
196 if(region_is_await(reg
) && reg
!=(WRegion
*)await_watch
.obj
)
199 watch_reset(&await_watch
);
203 WRegion
*ioncore_await_focus()
205 return (WRegion
*)(await_watch
.obj
);
215 void region_got_focus(WRegion
*reg
)
219 check_clear_await(reg
);
221 region_set_activity(reg
, SETPARAM_UNSET
);
223 if(reg
->active_sub
==NULL
){
224 region_focuslist_push(reg
);
225 /*ioncore_g.focus_current=reg;*/
228 if(!REGION_IS_ACTIVE(reg
)){
229 D(fprintf(stderr
, "got focus (inact) %s [%p]\n", OBJ_TYPESTR(reg
), reg
);)
231 reg
->flags
|=REGION_ACTIVE
;
232 region_set_manager_pseudoactivity(reg
);
234 par
=REGION_PARENT_REG(reg
);
237 region_update_owned_grabs(par
);
240 region_activated(reg
);
241 region_notify_change(reg
, ioncore_g
.notifies
.activated
);
243 D(fprintf(stderr
, "got focus (act) %s [%p]\n", OBJ_TYPESTR(reg
), reg
);)
246 /* Install default colour map only if there is no active subregion;
247 * their maps should come first. WClientWins will install their maps
248 * in region_activated. Other regions are supposed to use the same
251 if(reg
->active_sub
==NULL
&& !OBJ_IS(reg
, WClientWin
))
252 rootwin_install_colormap(region_rootwin_of(reg
), None
);
256 void region_lost_focus(WRegion
*reg
)
260 if(!REGION_IS_ACTIVE(reg
)){
261 D(fprintf(stderr
, "lost focus (inact) %s [%p:]\n", OBJ_TYPESTR(reg
), reg
);)
265 par
=REGION_PARENT_REG(reg
);
266 if(par
!=NULL
&& par
->active_sub
==reg
){
267 par
->active_sub
=NULL
;
268 region_update_owned_grabs(par
);
273 if(ioncore_g
.focus_current
==reg
){
274 /* Find the closest active parent, or if none is found, stop at the
275 * screen and mark it "currently focused".
277 while(par
!=NULL
&& !REGION_IS_ACTIVE(par
) && !OBJ_IS(par
, WScreen
))
278 par
=REGION_PARENT_REG(par
);
279 ioncore_g
.focus_current
=par
;
283 D(fprintf(stderr
, "lost focus (act) %s [%p:]\n", OBJ_TYPESTR(reg
), reg
);)
285 reg
->flags
&=~REGION_ACTIVE
;
286 region_unset_manager_pseudoactivity(reg
);
288 region_inactivated(reg
);
289 region_notify_change(reg
, ioncore_g
.notifies
.inactivated
);
296 /*{{{ Focus status requests */
300 * Is \var{reg} active/does it or one of it's children of focus?
304 bool region_is_active(WRegion
*reg
, bool pseudoact_ok
)
306 return (REGION_IS_ACTIVE(reg
) ||
307 (pseudoact_ok
&& REGION_IS_PSEUDOACTIVE(reg
)));
311 bool region_manager_is_focusnext(WRegion
*reg
)
313 if(reg
==NULL
|| ioncore_g
.focus_next
==NULL
)
316 if(reg
==ioncore_g
.focus_next
)
319 return region_manager_is_focusnext(REGION_MANAGER(reg
));
323 bool region_may_control_focus(WRegion
*reg
)
325 if(OBJ_IS_BEING_DESTROYED(reg
))
328 if(REGION_IS_ACTIVE(reg
) || REGION_IS_PSEUDOACTIVE(reg
))
331 if(region_is_await(reg
) || region_is_focusnext(reg
))
334 if(region_manager_is_focusnext(reg
))
344 /*{{{ set_focus, warp */
347 /*Time ioncore_focus_time=CurrentTime;*/
350 void region_finalise_focusing(WRegion
* reg
, Window win
, bool warp
, Time time
, int set_input
)
355 if(REGION_IS_ACTIVE(reg
) && ioncore_await_focus()==NULL
)
358 region_set_await_focus(reg
);
360 XSetInputFocus(ioncore_g
.dpy
, win
, RevertToParent
, time
);
365 static WRegion
*find_warp_to_reg(WRegion
*reg
)
369 if(reg
->flags
®ION_PLEASE_WARP
)
371 return find_warp_to_reg(region_manager_or_parent(reg
));
375 bool region_do_warp_default(WRegion
*reg
)
377 int x
, y
, w
, h
, px
=0, py
=0;
380 reg
=find_warp_to_reg(reg
);
385 D(fprintf(stderr
, "region_do_warp %p %s\n", reg
, OBJ_TYPESTR(reg
)));
387 root
=region_root_of(reg
);
389 region_rootpos(reg
, &x
, &y
);
390 w
=REGION_GEOM(reg
).w
;
391 h
=REGION_GEOM(reg
).h
;
393 if(xwindow_pointer_pos(root
, &px
, &py
)){
394 if(px
>=x
&& py
>=y
&& px
<x
+w
&& py
<y
+h
)
398 XWarpPointer(ioncore_g
.dpy
, None
, root
, 0, 0, 0, 0,
405 void region_do_warp(WRegion
*reg
)
408 hook_call_alt_o(region_do_warp_alt
, (Obj
*)reg
);
409 extl_unprotect(NULL
);
413 void region_maybewarp(WRegion
*reg
, bool warp
)
415 ioncore_g
.focus_next
=reg
;
416 ioncore_g
.focus_next_source
=IONCORE_FOCUSNEXT_OTHER
;
417 ioncore_g
.warp_next
=(warp
&& ioncore_g
.warp_enabled
);
421 void region_maybewarp_now(WRegion
*reg
, bool warp
)
423 ioncore_g
.focus_next
=NULL
;
424 /* TODO: what if focus isn't set? Should focus_next be reset then? */
425 region_do_set_focus(reg
, warp
&& ioncore_g
.warp_enabled
);
429 void region_set_focus(WRegion
*reg
)
431 region_maybewarp(reg
, FALSE
);
435 void region_warp(WRegion
*reg
)
437 region_maybewarp(reg
, TRUE
);
447 bool region_skip_focus(WRegion
*reg
)
450 if(reg
->flags
®ION_SKIP_FOCUS
)
452 reg
=REGION_PARENT_REG(reg
);
458 * Returns the currently focused region, if any.
461 WRegion
*ioncore_current()
463 return ioncore_g
.focus_current
;
470 /*{{{ Pointer focus hack */
473 /* This ugly hack tries to prevent focus change, when the pointer is
474 * in a window to be unmapped (or destroyed), and that does not have
475 * the focus, or should not soon have it.
477 void region_pointer_focus_hack(WRegion
*reg
)
481 if(ioncore_g
.opmode
!=IONCORE_OPMODE_NORMAL
)
484 if(ioncore_g
.focus_next
!=NULL
&&
485 ioncore_g
.focus_next_source
<=IONCORE_FOCUSNEXT_POINTERHACK
){
489 act
=ioncore_await_focus();
491 if((REGION_IS_ACTIVE(reg
) && act
==NULL
) || !region_is_fully_mapped(reg
))
495 act
=ioncore_g
.focus_current
;
498 OBJ_IS_BEING_DESTROYED(act
) ||
499 !region_is_fully_mapped(act
) ||
500 region_skip_focus(act
)){
504 region_set_focus(act
);
505 ioncore_g
.focus_next_source
=IONCORE_FOCUSNEXT_POINTERHACK
;