4 * Copyright (c) Tuomo Valkonen 1999-2009.
6 * See the included file LICENSE for details.
9 #include <libmainloop/hooks.h>
10 #include <libmainloop/signal.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
.focuslist
, 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
.focuslist
, 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
.focuslist
, after
, reg
,
58 active_next
, active_prev
);
62 static 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
.focuslist
, reg
, active_next
, active_prev
);
72 void region_focus_deinit(WRegion
*reg
)
74 region_focuslist_deinit(reg
);
76 if(ioncore_g
.focus_current
==reg
)
77 ioncore_g
.focus_current
=ioncore_g
.focuslist
;
82 * Go to and return to a previously active region (if any).
84 * Note that this function is asynchronous; the region will not
85 * actually have received the focus when this function returns.
88 WRegion
*ioncore_goto_previous()
92 if(ioncore_g
.focuslist
==NULL
)
95 // We're trying to access the focus list from lua (likely from the UI). I
96 // thus force any pending focuslist updates to complete now
97 region_focuslist_awaiting_insertion_trigger();
99 /* Find the first region on focus history list that isn't currently
102 for(next
=ioncore_g
.focuslist
->active_next
;
104 next
=next
->active_next
){
106 if(!REGION_IS_ACTIVE(next
))
118 * Iterate over focus history until \var{iterfn} returns \code{false}.
119 * The function is called in protected mode.
120 * This routine returns \code{true} if it reaches the end of list
121 * without this happening.
124 bool ioncore_focushistory_i(ExtlFn iterfn
)
128 if(ioncore_g
.focuslist
==NULL
)
131 // We're trying to access the focus list from lua (likely from the UI). I
132 // thus force any pending focuslist updates to complete now
133 region_focuslist_awaiting_insertion_trigger();
135 /* Find the first region on focus history list that isn't currently
138 for(next
=ioncore_g
.focuslist
->active_next
;
140 next
=next
->active_next
){
142 if(!extl_iter_obj(iterfn
, (Obj
*)next
))
156 static Watch await_watch
=WATCH_INIT
;
159 static void await_watch_handler(Watch
*watch
, WRegion
*prev
)
163 r
=REGION_PARENT_REG(prev
);
167 if(watch_setup(&await_watch
, (Obj
*)r
,
168 (WatchHandler
*)await_watch_handler
))
175 void region_set_await_focus(WRegion
*reg
)
178 watch_reset(&await_watch
);
180 watch_setup(&await_watch
, (Obj
*)reg
,
181 (WatchHandler
*)await_watch_handler
);
186 static bool region_is_parent(WRegion
*reg
, WRegion
*aw
)
191 aw
=REGION_PARENT_REG(aw
);
198 static bool region_is_await(WRegion
*reg
)
200 return region_is_parent(reg
, (WRegion
*)await_watch
.obj
);
204 static bool region_is_focusnext(WRegion
*reg
)
206 return region_is_parent(reg
, ioncore_g
.focus_next
);
210 /* Only keep await status if focus event is to an ancestor of the await
213 static void check_clear_await(WRegion
*reg
)
215 if(region_is_await(reg
) && reg
!=(WRegion
*)await_watch
.obj
)
218 watch_reset(&await_watch
);
222 WRegion
*ioncore_await_focus()
224 return (WRegion
*)(await_watch
.obj
);
231 /*{{{ focuslist delayed insertion logic */
233 static WTimer
* focuslist_insert_timer
=NULL
;
234 static WRegion
* region_awaiting_insertion
;
236 static void timer_expired__focuslist_insert(WTimer
* dummy1
, Obj
* dummy2
)
238 region_focuslist_push(region_awaiting_insertion
);
239 region_awaiting_insertion
= NULL
;
242 static void schedule_focuslist_insert_timer(WRegion
*reg
)
244 // if the delay is disabled, add to the list NOW
245 if( ioncore_g
.focuslist_insert_delay
<= 0 )
247 region_focuslist_push(reg
);
251 if( focuslist_insert_timer
== NULL
)
253 focuslist_insert_timer
=create_timer();
254 if( focuslist_insert_timer
== NULL
)
258 region_awaiting_insertion
= reg
;
259 timer_set(focuslist_insert_timer
, ioncore_g
.focuslist_insert_delay
,
260 (WTimerHandler
*)timer_expired__focuslist_insert
, NULL
);
264 void region_focuslist_awaiting_insertion_cancel(void)
266 if( focuslist_insert_timer
== NULL
)
269 timer_reset(focuslist_insert_timer
);
272 void region_focuslist_awaiting_insertion_trigger(void)
274 if( focuslist_insert_timer
!= NULL
&&
275 region_awaiting_insertion
!= NULL
)
277 timer_expired__focuslist_insert(NULL
,NULL
);
278 timer_reset(focuslist_insert_timer
);
282 const WRegion
* region_focuslist_region_awaiting_insertion(void)
284 return region_awaiting_insertion
;
294 void region_got_focus(WRegion
*reg
)
298 check_clear_await(reg
);
300 region_set_activity(reg
, SETPARAM_UNSET
);
302 if(reg
->active_sub
==NULL
)
304 // I update the current focus indicator right now. The focuslist is
305 // updated on a timer to keep the list ordered by importance (to keep
306 // windows that the user quickly cycles through at the bottom of the list)
307 ioncore_g
.focus_current
= reg
;
308 schedule_focuslist_insert_timer(reg
);
311 if(!REGION_IS_ACTIVE(reg
)){
312 D(fprintf(stderr
, "got focus (inact) %s [%p]\n", OBJ_TYPESTR(reg
), reg
);)
314 reg
->flags
|=REGION_ACTIVE
;
315 region_set_manager_pseudoactivity(reg
);
317 par
=REGION_PARENT_REG(reg
);
320 region_update_owned_grabs(par
);
323 region_activated(reg
);
324 region_notify_change(reg
, ioncore_g
.notifies
.activated
);
326 D(fprintf(stderr
, "got focus (act) %s [%p]\n", OBJ_TYPESTR(reg
), reg
);)
329 /* Install default colour map only if there is no active subregion;
330 * their maps should come first. WClientWins will install their maps
331 * in region_activated. Other regions are supposed to use the same
334 if(reg
->active_sub
==NULL
&& !OBJ_IS(reg
, WClientWin
))
335 rootwin_install_colormap(region_rootwin_of(reg
), None
);
339 void region_lost_focus(WRegion
*reg
)
343 if(!REGION_IS_ACTIVE(reg
)){
344 D(fprintf(stderr
, "lost focus (inact) %s [%p:]\n", OBJ_TYPESTR(reg
), reg
);)
348 par
=REGION_PARENT_REG(reg
);
349 if(par
!=NULL
&& par
->active_sub
==reg
){
350 par
->active_sub
=NULL
;
351 region_update_owned_grabs(par
);
354 D(fprintf(stderr
, "lost focus (act) %s [%p:]\n", OBJ_TYPESTR(reg
), reg
);)
356 reg
->flags
&=~REGION_ACTIVE
;
357 region_unset_manager_pseudoactivity(reg
);
359 region_inactivated(reg
);
360 region_notify_change(reg
, ioncore_g
.notifies
.inactivated
);
367 /*{{{ Focus status requests */
371 * Is \var{reg} active/does it or one of it's children of focus?
375 bool region_is_active(WRegion
*reg
, bool pseudoact_ok
)
377 return (REGION_IS_ACTIVE(reg
) ||
378 (pseudoact_ok
&& REGION_IS_PSEUDOACTIVE(reg
)));
382 bool region_manager_is_focusnext(WRegion
*reg
)
384 if(reg
==NULL
|| ioncore_g
.focus_next
==NULL
)
387 if(reg
==ioncore_g
.focus_next
)
390 return region_manager_is_focusnext(REGION_MANAGER(reg
));
394 bool region_may_control_focus(WRegion
*reg
)
396 if(OBJ_IS_BEING_DESTROYED(reg
))
399 if(REGION_IS_ACTIVE(reg
) || REGION_IS_PSEUDOACTIVE(reg
))
402 if(region_is_await(reg
) || region_is_focusnext(reg
))
405 if(region_manager_is_focusnext(reg
))
415 /*{{{ set_focus, warp */
418 /*Time ioncore_focus_time=CurrentTime;*/
420 bool ioncore_should_focus_parent_when_refusing_focus(WRegion
* reg
){
421 WWindow
* parent
= reg
->parent
;
423 LOG(FOCUS
, DEBUG
, "Region %s refusing focus", reg
->ni
.name
);
429 * If the region is refusing focus, this might be because the mouse
430 * entered it but it doesn't accept any focus (like in the case of
431 * oclock). In that case we want to focus its parent WTiling, if any.
433 * However, when for example IntelliJ IDEA's main window is refusing
434 * the focus because some transient completion popup is open, we want
435 * to leave the focus alone.
437 LOG(FOCUS
, DEBUG
, "Parent is %s", parent
->region
.ni
.name
);
439 if (obj_is((Obj
*)parent
, &CLASSDESCR(WFrame
))
440 && ((WFrame
*)parent
)->mode
== FRAME_MODE_TILED
) {
441 LOG(FOCUS
, DEBUG
, "Parent %s is a tiled WFrame", parent
->region
.ni
.name
);
448 void region_finalise_focusing(WRegion
* reg
, Window win
, bool warp
, Time time
, int set_input
) {
452 if(REGION_IS_ACTIVE(reg
) && ioncore_await_focus()==NULL
)
455 region_set_await_focus(reg
);
458 XSetInputFocus(ioncore_g
.dpy
, win
, RevertToParent
, time
);
459 else if(ioncore_should_focus_parent_when_refusing_focus(reg
)) {
460 XSetInputFocus(ioncore_g
.dpy
, reg
->parent
->win
, RevertToParent
, time
);
466 static WRegion
*find_warp_to_reg(WRegion
*reg
)
470 if(reg
->flags
®ION_PLEASE_WARP
)
472 return find_warp_to_reg(region_manager_or_parent(reg
));
476 bool region_do_warp_default(WRegion
*reg
)
478 int x
, y
, w
, h
, px
=0, py
=0;
481 reg
=find_warp_to_reg(reg
);
486 D(fprintf(stderr
, "region_do_warp %p %s\n", reg
, OBJ_TYPESTR(reg
)));
488 root
=region_rootwin_of(reg
);
490 region_rootpos(reg
, &x
, &y
);
491 w
=REGION_GEOM(reg
).w
;
492 h
=REGION_GEOM(reg
).h
;
494 if(xwindow_pointer_pos(WROOTWIN_ROOT(root
), &px
, &py
)){
495 if(px
>=x
&& py
>=y
&& px
<x
+w
&& py
<y
+h
)
499 rootwin_warp_pointer(root
, x
+5, y
+5);
505 void region_do_warp(WRegion
*reg
)
508 hook_call_alt_o(region_do_warp_alt
, (Obj
*)reg
);
509 extl_unprotect(NULL
);
513 void region_maybewarp(WRegion
*reg
, bool warp
)
515 ioncore_g
.focus_next
=reg
;
516 ioncore_g
.focus_next_source
=IONCORE_FOCUSNEXT_OTHER
;
517 ioncore_g
.warp_next
=(warp
&& ioncore_g
.warp_enabled
);
521 void region_maybewarp_now(WRegion
*reg
, bool warp
)
523 ioncore_g
.focus_next
=NULL
;
524 /* TODO: what if focus isn't set? Should focus_next be reset then? */
525 region_do_set_focus(reg
, warp
&& ioncore_g
.warp_enabled
);
529 void region_set_focus(WRegion
*reg
)
531 region_maybewarp(reg
, FALSE
);
535 void region_warp(WRegion
*reg
)
537 region_maybewarp(reg
, TRUE
);
547 bool region_skip_focus(WRegion
*reg
)
550 if(reg
->flags
®ION_SKIP_FOCUS
)
552 reg
=REGION_PARENT_REG(reg
);
558 * Returns the currently focused region, if any.
561 WRegion
*ioncore_current()
563 return ioncore_g
.focus_current
;
570 /*{{{ Pointer focus hack */
573 /* This ugly hack tries to prevent focus change, when the pointer is
574 * in a window to be unmapped (or destroyed), and that does not have
575 * the focus, or should not soon have it.
577 void region_pointer_focus_hack(WRegion
*reg
)
581 if(ioncore_g
.opmode
!=IONCORE_OPMODE_NORMAL
)
584 if(ioncore_g
.focus_next
!=NULL
&&
585 ioncore_g
.focus_next_source
<=IONCORE_FOCUSNEXT_POINTERHACK
){
589 act
=ioncore_await_focus();
591 if((REGION_IS_ACTIVE(reg
) && act
==NULL
) || !region_is_fully_mapped(reg
))
595 act
=ioncore_g
.focus_current
;
598 OBJ_IS_BEING_DESTROYED(act
) ||
599 !region_is_fully_mapped(act
) ||
600 region_skip_focus(act
)){
604 region_set_focus(act
);
605 ioncore_g
.focus_next_source
=IONCORE_FOCUSNEXT_POINTERHACK
;