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>
22 #include "screen-notify.h"
25 static void region_focuslist_awaiting_insertion_trigger(void);
30 WHook
*region_do_warp_alt
=NULL
;
39 void region_focuslist_remove_with_mgrs(WRegion
*reg
)
41 WRegion
*mgrp
=region_manager_or_parent(reg
);
43 UNLINK_ITEM(ioncore_g
.focuslist
, reg
, active_next
, active_prev
);
46 region_focuslist_remove_with_mgrs(mgrp
);
50 void region_focuslist_push(WRegion
*reg
)
52 region_focuslist_remove_with_mgrs(reg
);
53 LINK_ITEM_FIRST(ioncore_g
.focuslist
, reg
, active_next
, active_prev
);
57 void region_focuslist_move_after(WRegion
*reg
, WRegion
*after
)
59 region_focuslist_remove_with_mgrs(reg
);
60 LINK_ITEM_AFTER(ioncore_g
.focuslist
, after
, reg
,
61 active_next
, active_prev
);
65 static void region_focuslist_deinit(WRegion
*reg
)
67 WRegion
*replace
=region_manager_or_parent(reg
);
70 region_focuslist_move_after(replace
, reg
);
72 UNLINK_ITEM(ioncore_g
.focuslist
, reg
, active_next
, active_prev
);
77 * Go to and return to a previously active region (if any).
79 * Note that this function is asynchronous; the region will not
80 * actually have received the focus when this function returns.
83 WRegion
*ioncore_goto_previous()
87 if(ioncore_g
.focuslist
==NULL
)
90 /* We're trying to access the focus list from lua (likely from the UI). I
91 * thus force any pending focuslist updates to complete now */
92 region_focuslist_awaiting_insertion_trigger();
94 /* Find the first region on focus history list that isn't currently
97 for(next
=ioncore_g
.focuslist
->active_next
;
99 next
=next
->active_next
){
101 if(!REGION_IS_ACTIVE(next
))
113 * Iterate over focus history until \var{iterfn} returns \code{false}.
114 * The function is called in protected mode.
115 * This routine returns \code{true} if it reaches the end of list
116 * without this happening.
119 bool ioncore_focushistory_i(ExtlFn iterfn
)
123 if(ioncore_g
.focuslist
==NULL
)
126 /* We're trying to access the focus list from lua (likely from the UI). I
127 * thus force any pending focuslist updates to complete now */
128 region_focuslist_awaiting_insertion_trigger();
130 /* Find the first region on focus history list that isn't currently
133 for(next
=ioncore_g
.focuslist
->active_next
;
135 next
=next
->active_next
){
137 if(!extl_iter_obj(iterfn
, (Obj
*)next
))
151 static Watch await_watch
=WATCH_INIT
;
154 static void await_watch_handler(Watch
*UNUSED(watch
), WRegion
*prev
)
158 r
=REGION_PARENT_REG(prev
);
162 if(watch_setup(&await_watch
, (Obj
*)r
,
163 (WatchHandler
*)await_watch_handler
))
170 void region_set_await_focus(WRegion
*reg
)
173 watch_reset(&await_watch
);
175 watch_setup(&await_watch
, (Obj
*)reg
,
176 (WatchHandler
*)await_watch_handler
);
181 static bool region_is_parent(WRegion
*reg
, WRegion
*aw
)
186 aw
=REGION_PARENT_REG(aw
);
193 static bool region_is_await(WRegion
*reg
)
195 return region_is_parent(reg
, (WRegion
*)await_watch
.obj
);
199 static bool region_is_focusnext(WRegion
*reg
)
201 return region_is_parent(reg
, ioncore_g
.focus_next
);
205 /* Only keep await status if focus event is to an ancestor of the await
208 static void check_clear_await(WRegion
*reg
)
210 if(region_is_await(reg
) && reg
!=(WRegion
*)await_watch
.obj
)
213 watch_reset(&await_watch
);
217 WRegion
*ioncore_await_focus()
219 return (WRegion
*)(await_watch
.obj
);
226 /*{{{ focuslist delayed insertion logic */
228 static WTimer
* focuslist_insert_timer
=NULL
;
229 static WRegion
* region_awaiting_insertion
;
231 static void timer_expired__focuslist_insert(WTimer
* UNUSED(dummy1
), Obj
* UNUSED(dummy2
))
233 region_focuslist_push(region_awaiting_insertion
);
234 region_awaiting_insertion
= NULL
;
237 static void schedule_focuslist_insert_timer(WRegion
*reg
)
239 /* if the delay is disabled, add to the list NOW */
240 if( ioncore_g
.focuslist_insert_delay
<= 0 )
242 region_focuslist_push(reg
);
246 if( focuslist_insert_timer
== NULL
)
248 focuslist_insert_timer
=create_timer();
249 if( focuslist_insert_timer
== NULL
)
253 region_awaiting_insertion
= reg
;
254 timer_set(focuslist_insert_timer
, ioncore_g
.focuslist_insert_delay
,
255 (WTimerHandler
*)timer_expired__focuslist_insert
, NULL
);
258 static void region_focuslist_awaiting_insertion_cancel_if_is( WRegion
* reg
)
260 if( region_awaiting_insertion
== reg
&&
261 focuslist_insert_timer
!= NULL
)
263 timer_reset(focuslist_insert_timer
);
267 static void region_focuslist_awaiting_insertion_trigger(void)
269 if( focuslist_insert_timer
!= NULL
&&
270 region_awaiting_insertion
!= NULL
)
272 timer_expired__focuslist_insert(NULL
,NULL
);
273 timer_reset(focuslist_insert_timer
);
284 void region_got_focus(WRegion
*reg
)
288 check_clear_await(reg
);
290 region_set_activity(reg
, SETPARAM_UNSET
);
292 if(reg
->active_sub
==NULL
)
294 /* I update the current focus indicator right now. The focuslist is
295 * updated on a timer to keep the list ordered by importance (to keep
296 * windows that the user quickly cycles through at the bottom of the list) */
297 ioncore_g
.focus_current
= reg
;
298 schedule_focuslist_insert_timer(reg
);
301 if(!REGION_IS_ACTIVE(reg
)){
302 D(fprintf(stderr
, "got focus (inact) %s [%p]\n", OBJ_TYPESTR(reg
), reg
);)
304 reg
->flags
|=REGION_ACTIVE
;
305 region_set_manager_pseudoactivity(reg
);
307 par
=REGION_PARENT_REG(reg
);
310 region_update_owned_grabs(par
);
313 region_activated(reg
);
314 region_notify_change(reg
, ioncore_g
.notifies
.activated
);
316 D(fprintf(stderr
, "got focus (act) %s [%p]\n", OBJ_TYPESTR(reg
), reg
);)
319 /* Install default colour map only if there is no active subregion;
320 * their maps should come first. WClientWins will install their maps
321 * in region_activated. Other regions are supposed to use the same
324 if(reg
->active_sub
==NULL
&& !OBJ_IS(reg
, WClientWin
))
325 rootwin_install_colormap(region_rootwin_of(reg
), None
);
327 screen_update_workspace_indicatorwin( reg
);
331 void region_lost_focus(WRegion
*reg
)
335 if(!REGION_IS_ACTIVE(reg
)){
336 D(fprintf(stderr
, "lost focus (inact) %s [%p:]\n", OBJ_TYPESTR(reg
), reg
);)
340 par
=REGION_PARENT_REG(reg
);
341 if(par
!=NULL
&& par
->active_sub
==reg
){
342 par
->active_sub
=NULL
;
343 region_update_owned_grabs(par
);
346 D(fprintf(stderr
, "lost focus (act) %s [%p:]\n", OBJ_TYPESTR(reg
), reg
);)
348 reg
->flags
&=~REGION_ACTIVE
;
349 region_unset_manager_pseudoactivity(reg
);
351 region_inactivated(reg
);
352 region_notify_change(reg
, ioncore_g
.notifies
.inactivated
);
356 void region_focus_deinit(WRegion
*reg
)
358 /* if the region that's waiting to be added to the focuslist is being
359 * deleted, cancel the insertion */
360 if(region_awaiting_insertion
)
361 region_focuslist_awaiting_insertion_cancel_if_is(reg
);
363 region_focuslist_deinit(reg
);
365 if(ioncore_g
.focus_current
==reg
)
366 ioncore_g
.focus_current
=ioncore_g
.focuslist
;
373 /*{{{ Focus status requests */
377 * Is \var{reg} active/does it or one of it's children of focus?
381 bool region_is_active(WRegion
*reg
, bool pseudoact_ok
)
383 return (REGION_IS_ACTIVE(reg
) ||
384 (pseudoact_ok
&& REGION_IS_PSEUDOACTIVE(reg
)));
388 bool region_manager_is_focusnext(WRegion
*reg
)
390 if(reg
==NULL
|| ioncore_g
.focus_next
==NULL
)
393 if(reg
==ioncore_g
.focus_next
)
396 return region_manager_is_focusnext(REGION_MANAGER(reg
));
400 bool region_may_control_focus(WRegion
*reg
)
402 if(OBJ_IS_BEING_DESTROYED(reg
))
405 if(REGION_IS_ACTIVE(reg
) || REGION_IS_PSEUDOACTIVE(reg
))
408 if(region_is_await(reg
) || region_is_focusnext(reg
))
411 if(region_manager_is_focusnext(reg
))
421 /*{{{ set_focus, warp */
424 /*Time ioncore_focus_time=CurrentTime;*/
426 bool ioncore_should_focus_parent_when_refusing_focus(WRegion
* reg
){
427 WWindow
* parent
= reg
->parent
;
429 LOG(DEBUG
, FOCUS
, "Region %s refusing focus", reg
->ni
.name
);
435 * If the region is refusing focus, this might be because the mouse
436 * entered it but it doesn't accept any focus (like in the case of
437 * oclock). In that case we want to focus its parent WTiling, if any.
439 * However, when for example IntelliJ IDEA's main window is refusing
440 * the focus because some transient completion popup is open, we want
441 * to leave the focus alone.
443 LOG(DEBUG
, FOCUS
, "Parent is %s", parent
->region
.ni
.name
);
445 if (obj_is((Obj
*)parent
, &CLASSDESCR(WFrame
))
446 && ((WFrame
*)parent
)->mode
== FRAME_MODE_TILED
) {
447 LOG(DEBUG
, FOCUS
, "Parent %s is a tiled WFrame", parent
->region
.ni
.name
);
454 void region_finalise_focusing(WRegion
* reg
, Window win
, bool warp
, Time time
, int set_input
) {
458 if(REGION_IS_ACTIVE(reg
) && ioncore_await_focus()==NULL
)
461 region_set_await_focus(reg
);
464 XSetInputFocus(ioncore_g
.dpy
, win
, RevertToParent
, time
);
465 else if(ioncore_should_focus_parent_when_refusing_focus(reg
)) {
466 XSetInputFocus(ioncore_g
.dpy
, reg
->parent
->win
, RevertToParent
, time
);
472 static WRegion
*find_warp_to_reg(WRegion
*reg
)
476 if(reg
->flags
®ION_PLEASE_WARP
)
478 return find_warp_to_reg(region_manager_or_parent(reg
));
482 bool region_do_warp_default(WRegion
*reg
)
484 int x
, y
, w
, h
, px
=0, py
=0;
487 reg
=find_warp_to_reg(reg
);
492 D(fprintf(stderr
, "region_do_warp %p %s\n", reg
, OBJ_TYPESTR(reg
)));
494 root
=region_rootwin_of(reg
);
496 region_rootpos(reg
, &x
, &y
);
497 w
=REGION_GEOM(reg
).w
;
498 h
=REGION_GEOM(reg
).h
;
500 if(xwindow_pointer_pos(WROOTWIN_ROOT(root
), &px
, &py
)){
501 if(px
>=x
&& py
>=y
&& px
<x
+w
&& py
<y
+h
)
505 rootwin_warp_pointer(root
, x
+5, y
+5);
511 void region_do_warp(WRegion
*reg
)
514 hook_call_alt_o(region_do_warp_alt
, (Obj
*)reg
);
515 extl_unprotect(NULL
);
519 void region_maybewarp(WRegion
*reg
, bool warp
)
521 ioncore_g
.focus_next
=reg
;
522 ioncore_g
.focus_next_source
=IONCORE_FOCUSNEXT_OTHER
;
523 ioncore_g
.warp_next
=(warp
&& ioncore_g
.warp_enabled
);
527 void region_maybewarp_now(WRegion
*reg
, bool warp
)
529 ioncore_g
.focus_next
=NULL
;
530 /* TODO: what if focus isn't set? Should focus_next be reset then? */
531 region_do_set_focus(reg
, warp
&& ioncore_g
.warp_enabled
);
535 void region_set_focus(WRegion
*reg
)
537 region_maybewarp(reg
, FALSE
);
541 void region_warp(WRegion
*reg
)
543 region_maybewarp(reg
, TRUE
);
553 bool region_skip_focus(WRegion
*reg
)
556 if(reg
->flags
®ION_SKIP_FOCUS
)
558 reg
=REGION_PARENT_REG(reg
);
564 * Returns the currently focused region, if any.
567 WRegion
*ioncore_current()
569 return ioncore_g
.focus_current
;
576 /*{{{ Pointer focus hack */
579 /* This ugly hack tries to prevent focus change, when the pointer is
580 * in a window to be unmapped (or destroyed), and that does not have
581 * the focus, or should not soon have it.
583 void region_pointer_focus_hack(WRegion
*reg
)
587 if(ioncore_g
.opmode
!=IONCORE_OPMODE_NORMAL
)
590 if(ioncore_g
.focus_next
!=NULL
&&
591 ioncore_g
.focus_next_source
<=IONCORE_FOCUSNEXT_POINTERHACK
){
595 act
=ioncore_await_focus();
597 if((REGION_IS_ACTIVE(reg
) && act
==NULL
) || !region_is_fully_mapped(reg
))
601 act
=ioncore_g
.focus_current
;
604 OBJ_IS_BEING_DESTROYED(act
) ||
605 !region_is_fully_mapped(act
) ||
606 region_skip_focus(act
)){
610 region_set_focus(act
);
611 ioncore_g
.focus_next_source
=IONCORE_FOCUSNEXT_POINTERHACK
;
617 /*{{{ Debug printers */
619 void region_focuslist_debugprint(void)
623 LOG(DEBUG
, GENERAL
, "focus list start =========================");
624 LOG(DEBUG
, GENERAL
, "currently-focused region ");
625 region_debugprint_parents( ioncore_g
.focus_current
);
626 LOG(DEBUG
, GENERAL
, "");
628 LOG(DEBUG
, GENERAL
, "top-of-focuslist ");
629 region_debugprint_parents( ioncore_g
.focuslist
);
630 LOG(DEBUG
, GENERAL
, "");
632 reg
= ioncore_g
.focuslist
;
635 LOG(DEBUG
, GENERAL
, "%p (%s)", (void*)reg
, reg
!= NULL
? reg
->ni
.name
: "NULL");
638 reg
->active_next
== NULL
||
639 reg
== reg
->active_next
)
644 reg
= reg
->active_next
;
647 LOG(DEBUG
, GENERAL
, "focus list end =========================");