4 * Copyright (c) Tuomo Valkonen 1999-2008.
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 static 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)
355 if(REGION_IS_ACTIVE(reg) && ioncore_await_focus()==NULL)
358 region_set_await_focus(reg);
359 /*xwindow_do_set_focus(win);*/
360 XSetInputFocus(ioncore_g.dpy, win, RevertToParent,
361 CurrentTime/*ioncore_focus_time*/);
362 /*ioncore_focus_time=CurrentTime;*/
367 static WRegion *find_warp_to_reg(WRegion *reg)
371 if(reg->flags®ION_PLEASE_WARP)
373 return find_warp_to_reg(region_manager_or_parent(reg));
377 bool region_do_warp_default(WRegion *reg)
379 int x, y, w, h, px=0, py=0;
382 reg=find_warp_to_reg(reg);
387 D(fprintf(stderr, "region_do_warp %p %s\n", reg, OBJ_TYPESTR(reg)));
389 root=region_root_of(reg);
391 region_rootpos(reg, &x, &y);
392 w=REGION_GEOM(reg).w;
393 h=REGION_GEOM(reg).h;
395 if(xwindow_pointer_pos(root, &px, &py)){
396 if(px>=x && py>=y && px<x+w && py<y+h)
400 XWarpPointer(ioncore_g.dpy, None, root, 0, 0, 0, 0,
407 void region_do_warp(WRegion *reg)
410 hook_call_alt_o(region_do_warp_alt, (Obj*)reg);
411 extl_unprotect(NULL);
415 void region_maybewarp(WRegion *reg, bool warp)
417 ioncore_g.focus_next=reg;
418 ioncore_g.focus_next_source=IONCORE_FOCUSNEXT_OTHER;
419 ioncore_g.warp_next=(warp && ioncore_g.warp_enabled);
423 void region_maybewarp_now(WRegion *reg, bool warp)
425 ioncore_g.focus_next=NULL;
426 /* TODO: what if focus isn't set? Should focus_next be reset then? */
427 region_do_set_focus(reg, warp && ioncore_g.warp_enabled);
431 void region_set_focus(WRegion *reg)
433 region_maybewarp(reg, FALSE);
437 void region_warp(WRegion *reg)
439 region_maybewarp(reg, TRUE);
449 bool region_skip_focus(WRegion *reg)
452 if(reg->flags®ION_SKIP_FOCUS)
454 reg=REGION_PARENT_REG(reg);
460 * Returns the currently focused region, if any.
463 WRegion *ioncore_current()
465 return ioncore_g.focus_current;
472 /*{{{ Pointer focus hack */
475 /* This ugly hack tries to prevent focus change, when the pointer is
476 * in a window to be unmapped (or destroyed), and that does not have
477 * the focus, or should not soon have it.
479 void region_pointer_focus_hack(WRegion *reg)
483 if(ioncore_g.opmode!=IONCORE_OPMODE_NORMAL)
486 if(ioncore_g.focus_next!=NULL &&
487 ioncore_g.focus_next_source<=IONCORE_FOCUSNEXT_POINTERHACK){
491 act=ioncore_await_focus();
493 if((REGION_IS_ACTIVE(reg) && act==NULL) || !region_is_fully_mapped(reg))
497 act=ioncore_g.focus_current;
500 OBJ_IS_BEING_DESTROYED(act) ||
501 !region_is_fully_mapped(act) ||
502 region_skip_focus(act)){
506 region_set_focus(act);
507 ioncore_g.focus_next_source=IONCORE_FOCUSNEXT_POINTERHACK;