Prevent overflow while calculating frame size hints
[notion/jeffpc.git] / ioncore / focus.c
blob90b3ab9baca69c5884ecb36ad3477ac8f8d5cef5
1 /*
2 * ion/ioncore/focus.c
4 * Copyright (c) Tuomo Valkonen 1999-2009.
6 * See the included file LICENSE for details.
7 */
9 #include <libmainloop/hooks.h>
10 #include "common.h"
11 #include "focus.h"
12 #include "global.h"
13 #include "window.h"
14 #include "region.h"
15 #include "colormap.h"
16 #include "activity.h"
17 #include "xwindow.h"
18 #include "regbind.h"
21 /*{{{ Hooks. */
24 WHook *region_do_warp_alt=NULL;
27 /*}}}*/
30 /*{{{ Focus list */
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);
39 if(mgrp!=NULL)
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);
63 if(replace!=NULL)
64 region_focuslist_move_after(replace, reg);
66 UNLINK_ITEM(ioncore_g.focus_current, reg, active_next, active_prev);
70 /*EXTL_DOC
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.
76 EXTL_EXPORT
77 WRegion *ioncore_goto_previous()
79 WRegion *next;
81 if(ioncore_g.focus_current==NULL)
82 return NULL;
84 /* Find the first region on focus history list that isn't currently
85 * active.
87 for(next=ioncore_g.focus_current->active_next;
88 next!=NULL;
89 next=next->active_next){
91 if(!REGION_IS_ACTIVE(next))
92 break;
95 if(next!=NULL)
96 region_goto(next);
98 return next;
102 /*EXTL_DOC
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.
108 EXTL_EXPORT
109 bool ioncore_focushistory_i(ExtlFn iterfn)
111 WRegion *next;
113 if(ioncore_g.focus_current==NULL)
114 return FALSE;
116 /* Find the first region on focus history list that isn't currently
117 * active.
119 for(next=ioncore_g.focus_current->active_next;
120 next!=NULL;
121 next=next->active_next){
123 if(!extl_iter_obj(iterfn, (Obj*)next))
124 return FALSE;
127 return TRUE;
131 /*}}}*/
134 /*{{{ Await focus */
137 static Watch await_watch=WATCH_INIT;
140 static void await_watch_handler(Watch *watch, WRegion *prev)
142 WRegion *r;
143 while(1){
144 r=REGION_PARENT_REG(prev);
145 if(r==NULL)
146 break;
148 if(watch_setup(&await_watch, (Obj*)r,
149 (WatchHandler*)await_watch_handler))
150 break;
151 prev=r;
156 void region_set_await_focus(WRegion *reg)
158 if(reg==NULL){
159 watch_reset(&await_watch);
160 }else{
161 watch_setup(&await_watch, (Obj*)reg,
162 (WatchHandler*)await_watch_handler);
167 static bool region_is_parent(WRegion *reg, WRegion *aw)
169 while(aw!=NULL){
170 if(aw==reg)
171 return TRUE;
172 aw=REGION_PARENT_REG(aw);
175 return FALSE;
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
192 * region.
194 static void check_clear_await(WRegion *reg)
196 if(region_is_await(reg) && reg!=(WRegion*)await_watch.obj)
197 return;
199 watch_reset(&await_watch);
203 WRegion *ioncore_await_focus()
205 return (WRegion*)(await_watch.obj);
209 /*}}}*/
212 /*{{{ Events */
215 void region_got_focus(WRegion *reg)
217 WRegion *par;
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);
235 if(par!=NULL){
236 par->active_sub=reg;
237 region_update_owned_grabs(par);
240 region_activated(reg);
241 region_notify_change(reg, ioncore_g.notifies.activated);
242 }else{
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
249 * default map.
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)
258 WRegion *par;
260 if(!REGION_IS_ACTIVE(reg)){
261 D(fprintf(stderr, "lost focus (inact) %s [%p:]\n", OBJ_TYPESTR(reg), reg);)
262 return;
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);
272 #if 0
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;
281 #endif
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);
293 /*}}}*/
296 /*{{{ Focus status requests */
299 /*EXTL_DOC
300 * Is \var{reg} active/does it or one of it's children of focus?
302 EXTL_SAFE
303 EXTL_EXPORT_MEMBER
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)
314 return FALSE;
316 if(reg==ioncore_g.focus_next)
317 return TRUE;
319 return region_manager_is_focusnext(REGION_MANAGER(reg));
323 bool region_may_control_focus(WRegion *reg)
325 if(OBJ_IS_BEING_DESTROYED(reg))
326 return FALSE;
328 if(REGION_IS_ACTIVE(reg) || REGION_IS_PSEUDOACTIVE(reg))
329 return TRUE;
331 if(region_is_await(reg) || region_is_focusnext(reg))
332 return TRUE;
334 if(region_manager_is_focusnext(reg))
335 return TRUE;
337 return FALSE;
341 /*}}}*/
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)
352 if(warp)
353 region_do_warp(reg);
355 if(REGION_IS_ACTIVE(reg) && ioncore_await_focus()==NULL)
356 return;
358 region_set_await_focus(reg);
359 if(set_input)
360 XSetInputFocus(ioncore_g.dpy, win, RevertToParent, time);
365 static WRegion *find_warp_to_reg(WRegion *reg)
367 if(reg==NULL)
368 return NULL;
369 if(reg->flags&REGION_PLEASE_WARP)
370 return reg;
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;
378 Window root;
380 reg=find_warp_to_reg(reg);
382 if(reg==NULL)
383 return FALSE;
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)
395 return TRUE;
398 XWarpPointer(ioncore_g.dpy, None, root, 0, 0, 0, 0,
399 x+5, y+5);
401 return TRUE;
405 void region_do_warp(WRegion *reg)
407 extl_protect(NULL);
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);
441 /*}}}*/
444 /*{{{ Misc. */
447 bool region_skip_focus(WRegion *reg)
449 while(reg!=NULL){
450 if(reg->flags&REGION_SKIP_FOCUS)
451 return TRUE;
452 reg=REGION_PARENT_REG(reg);
454 return FALSE;
457 /*EXTL_DOC
458 * Returns the currently focused region, if any.
460 EXTL_EXPORT
461 WRegion *ioncore_current()
463 return ioncore_g.focus_current;
467 /*}}}*/
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)
479 WRegion *act;
481 if(ioncore_g.opmode!=IONCORE_OPMODE_NORMAL)
482 return;
484 if(ioncore_g.focus_next!=NULL &&
485 ioncore_g.focus_next_source<=IONCORE_FOCUSNEXT_POINTERHACK){
486 return;
489 act=ioncore_await_focus();
491 if((REGION_IS_ACTIVE(reg) && act==NULL) || !region_is_fully_mapped(reg))
492 return;
494 if(act==NULL)
495 act=ioncore_g.focus_current;
497 if(act==NULL ||
498 OBJ_IS_BEING_DESTROYED(act) ||
499 !region_is_fully_mapped(act) ||
500 region_skip_focus(act)){
501 return;
504 region_set_focus(act);
505 ioncore_g.focus_next_source=IONCORE_FOCUSNEXT_POINTERHACK;
509 /*}}}*/