4 * Copyright (c) Tuomo Valkonen 1999-2007.
6 * Ion is free software; you can redistribute it and/or modify it under
7 * the terms of the GNU Lesser General Public License as published by
8 * the Free Software Foundation; either version 2.1 of the License, or
9 * (at your option) any later version.
12 #include <libmainloop/hooks.h>
27 WHook *region_do_warp_alt=NULL;
36 void region_focuslist_remove_with_mgrs(WRegion *reg)
38 WRegion *mgrp=region_manager_or_parent(reg);
40 UNLINK_ITEM(ioncore_g.focus_current, reg, active_next, active_prev);
43 region_focuslist_remove_with_mgrs(mgrp);
47 void region_focuslist_push(WRegion *reg)
49 region_focuslist_remove_with_mgrs(reg);
50 LINK_ITEM_FIRST(ioncore_g.focus_current, reg, active_next, active_prev);
54 void region_focuslist_move_after(WRegion *reg, WRegion *after)
56 region_focuslist_remove_with_mgrs(reg);
57 LINK_ITEM_AFTER(ioncore_g.focus_current, after, reg,
58 active_next, active_prev);
62 void region_focuslist_deinit(WRegion *reg)
64 WRegion *replace=region_manager_or_parent(reg);
67 region_focuslist_move_after(replace, reg);
69 UNLINK_ITEM(ioncore_g.focus_current, reg, active_next, active_prev);
74 * Go to and return to a previously active region (if any).
76 * Note that this function is asynchronous; the region will not
77 * actually have received the focus when this function returns.
80 WRegion *ioncore_goto_previous()
84 if(ioncore_g.focus_current==NULL)
87 /* Find the first region on focus history list that isn't currently
90 for(next=ioncore_g.focus_current->active_next;
92 next=next->active_next){
94 if(!REGION_IS_ACTIVE(next))
106 * Iterate over focus history until \var{iterfn} returns \code{false}.
107 * The function itself returns \code{true} if it reaches the end of list
108 * without this happening.
111 bool ioncore_focushistory_i(ExtlFn iterfn)
115 if(ioncore_g.focus_current==NULL)
118 /* Find the first region on focus history list that isn't currently
121 for(next=ioncore_g.focus_current->active_next;
123 next=next->active_next){
125 if(!extl_iter_obj(iterfn, (Obj*)next))
139 static Watch await_watch=WATCH_INIT;
142 static void await_watch_handler(Watch *watch, WRegion *prev)
146 r=REGION_PARENT_REG(prev);
150 if(watch_setup(&await_watch, (Obj*)r,
151 (WatchHandler*)await_watch_handler))
158 void region_set_await_focus(WRegion *reg)
161 watch_reset(&await_watch);
163 watch_setup(&await_watch, (Obj*)reg,
164 (WatchHandler*)await_watch_handler);
169 static bool region_is_await(WRegion *reg)
171 WRegion *aw=(WRegion*)await_watch.obj;
176 aw=REGION_PARENT_REG(aw);
183 /* Only keep await status if focus event is to an ancestor of the await
186 static void check_clear_await(WRegion *reg)
188 if(region_is_await(reg) && reg!=(WRegion*)await_watch.obj)
191 watch_reset(&await_watch);
195 WRegion *ioncore_await_focus()
197 return (WRegion*)(await_watch.obj);
207 void region_got_focus(WRegion *reg)
211 check_clear_await(reg);
213 region_set_activity(reg, SETPARAM_UNSET);
215 if(reg->active_sub==NULL){
216 region_focuslist_push(reg);
217 /*ioncore_g.focus_current=reg;*/
220 if(!REGION_IS_ACTIVE(reg)){
221 D(fprintf(stderr, "got focus (inact) %s [%p]\n", OBJ_TYPESTR(reg), reg);)
223 reg->flags|=REGION_ACTIVE;
224 region_set_manager_pseudoactivity(reg);
226 par=REGION_PARENT_REG(reg);
229 region_update_owned_grabs(par);
232 region_activated(reg);
233 region_notify_change(reg, ioncore_g.notifies.activated);
235 D(fprintf(stderr, "got focus (act) %s [%p]\n", OBJ_TYPESTR(reg), reg);)
238 /* Install default colour map only if there is no active subregion;
239 * their maps should come first. WClientWins will install their maps
240 * in region_activated. Other regions are supposed to use the same
243 if(reg->active_sub==NULL && !OBJ_IS(reg, WClientWin))
244 rootwin_install_colormap(region_rootwin_of(reg), None);
248 void region_lost_focus(WRegion *reg)
252 if(!REGION_IS_ACTIVE(reg)){
253 D(fprintf(stderr, "lost focus (inact) %s [%p:]\n", OBJ_TYPESTR(reg), reg);)
257 par=REGION_PARENT_REG(reg);
258 if(par!=NULL && par->active_sub==reg){
259 par->active_sub=NULL;
260 region_update_owned_grabs(par);
265 if(ioncore_g.focus_current==reg){
266 /* Find the closest active parent, or if none is found, stop at the
267 * screen and mark it "currently focused".
269 while(par!=NULL && !REGION_IS_ACTIVE(par) && !OBJ_IS(par, WScreen))
270 par=REGION_PARENT_REG(par);
271 ioncore_g.focus_current=par;
275 D(fprintf(stderr, "lost focus (act) %s [%p:]\n", OBJ_TYPESTR(reg), reg);)
277 reg->flags&=~REGION_ACTIVE;
278 region_unset_manager_pseudoactivity(reg);
280 region_inactivated(reg);
281 region_notify_change(reg, ioncore_g.notifies.inactivated);
288 /*{{{ Focus status requests */
292 * Is \var{reg} active/does it or one of it's children of focus?
296 bool region_is_active(WRegion *reg)
298 return REGION_IS_ACTIVE(reg);
302 bool region_may_control_focus(WRegion *reg)
304 if(OBJ_IS_BEING_DESTROYED(reg))
307 if(REGION_IS_ACTIVE(reg) || REGION_IS_PSEUDOACTIVE(reg))
310 if(region_is_await(reg))
320 /*{{{ set_focus, warp */
323 /*Time ioncore_focus_time=CurrentTime;*/
326 void region_finalise_focusing(WRegion* reg, Window win, bool warp)
331 region_set_await_focus(reg);
332 /*xwindow_do_set_focus(win);*/
333 XSetInputFocus(ioncore_g.dpy, win, RevertToParent,
334 CurrentTime/*ioncore_focus_time*/);
335 /*ioncore_focus_time=CurrentTime;*/
340 static WRegion *find_warp_to_reg(WRegion *reg)
344 if(reg->flags®ION_PLEASE_WARP)
346 return find_warp_to_reg(region_manager_or_parent(reg));
350 bool region_do_warp_default(WRegion *reg)
352 int x, y, w, h, px=0, py=0;
355 reg=find_warp_to_reg(reg);
360 D(fprintf(stderr, "region_do_warp %p %s\n", reg, OBJ_TYPESTR(reg)));
362 root=region_root_of(reg);
364 region_rootpos(reg, &x, &y);
365 w=REGION_GEOM(reg).w;
366 h=REGION_GEOM(reg).h;
368 if(xwindow_pointer_pos(root, &px, &py)){
369 if(px>=x && py>=y && px<x+w && py<y+h)
373 XWarpPointer(ioncore_g.dpy, None, root, 0, 0, 0, 0,
380 void region_do_warp(WRegion *reg)
383 hook_call_alt_o(region_do_warp_alt, (Obj*)reg);
384 extl_unprotect(NULL);
388 void region_maybewarp(WRegion *reg, bool warp)
390 ioncore_g.focus_next=reg;
391 ioncore_g.warp_next=(warp && ioncore_g.warp_enabled);
395 void region_maybewarp_now(WRegion *reg, bool warp)
397 ioncore_g.focus_next=NULL;
398 /* TODO: what if focus isn't set? Should focus_next be reset then? */
399 region_do_set_focus(reg, warp);
403 void region_set_focus(WRegion *reg)
405 region_maybewarp(reg, FALSE);
409 void region_warp(WRegion *reg)
411 region_maybewarp(reg, TRUE);
421 bool region_skip_focus(WRegion *reg)
424 if(reg->flags®ION_SKIP_FOCUS)
426 reg=REGION_PARENT_REG(reg);
432 * Returns the currently focused region, if any.
435 WRegion *ioncore_current()
437 return ioncore_g.focus_current;
444 /*{{{ Pointer focus hack */
447 /* This ugly hack tries to prevent focus change, when the pointer is
448 * in a window to be unmapped (or destroyed), and that does not have
449 * the focus, or should not soon have it.
451 void region_pointer_focus_hack(WRegion *reg)
453 WRegion *act=ioncore_await_focus();
454 const WRectangle *g=®ION_GEOM(reg);
457 if(ioncore_g.opmode!=IONCORE_OPMODE_NORMAL)
460 if(!REGION_IS_ACTIVE(reg) && act==NULL)
461 act=ioncore_g.focus_current;
463 if(act==NULL || OBJ_IS_BEING_DESTROYED(act))
466 /* Ok, anything under us should not get focus as we're unmapped:
467 * Either we don't have the focus, or focus change somewhere else
470 * It might be possible to do the pointer check more efficiently
471 * by trying to maintain our internal pointer containment state
472 * by tracking Enter/Leave events...
475 xwindow_pointer_pos(region_xwindow(reg), &x, &y);
477 if(x>=0 && y>=0 && x<g->w && y<g->h){
478 D(fprintf(stderr, "Pointer here and shouldn't alter focus!\n"));
479 region_set_focus(act);