]> git.decadent.org.uk Git - ion3.git/blob - ioncore/focus.c
[svn-inject] Installing original source of ion3
[ion3.git] / ioncore / focus.c
1 /*
2  * ion/ioncore/focus.c
3  *
4  * Copyright (c) Tuomo Valkonen 1999-2006. 
5  *
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.
10  */
11
12 #include <libmainloop/hooks.h>
13 #include "common.h"
14 #include "focus.h"
15 #include "global.h"
16 #include "window.h"
17 #include "region.h"
18 #include "colormap.h"
19 #include "activity.h"
20 #include "xwindow.h"
21 #include "regbind.h"
22
23
24 /*{{{ Hooks. */
25
26
27 WHook *region_do_warp_alt=NULL;
28 WHook *region_activated_hook=NULL;
29 WHook *region_inactivated_hook=NULL;
30
31
32 /*}}}*/
33
34
35 /*{{{ Focus list */
36
37
38 void region_focuslist_remove(WRegion *reg)
39 {
40     UNLINK_ITEM(ioncore_g.focus_current, reg, active_next, active_prev);
41 }
42
43
44 void region_focuslist_push(WRegion *reg)
45 {
46     region_focuslist_remove(reg);
47     LINK_ITEM_FIRST(ioncore_g.focus_current, reg, active_next, active_prev);
48 }
49
50
51 void region_focuslist_move_after(WRegion *reg, WRegion *after)
52 {
53     region_focuslist_remove(reg);
54     LINK_ITEM_AFTER(ioncore_g.focus_current, after, reg, 
55                     active_next, active_prev);
56 }
57
58
59 void region_focuslist_deinit(WRegion *reg)
60 {
61     WRegion *replace=region_manager_or_parent(reg);
62     
63     if(replace!=NULL)
64         region_focuslist_move_after(replace, reg);
65     
66     region_focuslist_remove(reg);
67 }
68
69
70 /*EXTL_DOC
71  * Go to and return to a previously active region (if any).
72  * 
73  * Note that this function is asynchronous; the region will not
74  * actually have received the focus when this function returns.
75  */
76 EXTL_EXPORT
77 WRegion *ioncore_goto_previous()
78 {
79     WRegion *next;
80     
81     if(ioncore_g.focus_current==NULL)
82         return NULL;
83     
84     /* Find the first region on focus history list that isn't currently
85      * active.
86      */
87     for(next=ioncore_g.focus_current->active_next;
88         next!=NULL; 
89         next=next->active_next){
90         
91         if(!REGION_IS_ACTIVE(next))
92             break;
93     }
94     
95     if(next!=NULL)
96         region_goto(next);
97     
98     return next;
99 }
100
101
102 /*}}}*/
103
104
105 /*{{{ Await focus */
106
107
108 static Watch await_watch=WATCH_INIT;
109
110
111 static void await_watch_handler(Watch *watch, WRegion *prev)
112 {
113     WRegion *r;
114     while(1){
115         r=REGION_PARENT_REG(prev);
116         if(r==NULL)
117             break;
118         
119         if(watch_setup(&await_watch, (Obj*)r, 
120                        (WatchHandler*)await_watch_handler))
121             break;
122         prev=r;
123     }
124 }
125
126
127 void region_set_await_focus(WRegion *reg)
128 {
129     if(reg==NULL){
130         watch_reset(&await_watch);
131     }else{
132         watch_setup(&await_watch, (Obj*)reg,
133                     (WatchHandler*)await_watch_handler);
134     }
135 }
136
137
138 static bool region_is_await(WRegion *reg)
139 {
140     WRegion *aw=(WRegion*)await_watch.obj;
141     
142     while(aw!=NULL){
143         if(aw==reg)
144             return TRUE;
145         aw=REGION_PARENT_REG(aw);
146     }
147     
148     return FALSE;
149 }
150
151
152 /* Only keep await status if focus event is to an ancestor of the await 
153  * region.
154  */
155 static void check_clear_await(WRegion *reg)
156 {
157     if(region_is_await(reg) && reg!=(WRegion*)await_watch.obj)
158         return;
159     
160     watch_reset(&await_watch);
161 }
162
163
164 bool ioncore_await_focus()
165 {
166     return (await_watch.obj!=NULL);
167 }
168
169
170 /*}}}*/
171
172
173 /*{{{ Events */
174
175
176 void region_got_focus(WRegion *reg)
177 {
178     WRegion *par, *mgr, *tmp;
179     
180     check_clear_await(reg);
181     
182     region_set_activity(reg, SETPARAM_UNSET);
183
184     if(reg->active_sub==NULL){
185         region_focuslist_push(reg);
186         /*ioncore_g.focus_current=reg;*/
187     }
188     
189     if(!REGION_IS_ACTIVE(reg)){
190         D(fprintf(stderr, "got focus (inact) %s [%p]\n", OBJ_TYPESTR(reg), reg);)
191         reg->flags|=REGION_ACTIVE;
192         
193         par=REGION_PARENT_REG(reg);
194         if(par!=NULL){
195             par->active_sub=reg;
196             region_update_owned_grabs(par);
197         }
198
199         region_activated(reg);
200         
201         mgr=REGION_MANAGER(reg);
202         tmp=reg;
203         while(mgr!=NULL){
204             /* We need to loop over managing non-windows (workspaces) here to
205              * signal their managers.
206              */
207             region_managed_activated(mgr, tmp);
208             
209             if(REGION_PARENT_REG(reg)==mgr)
210                 break;
211             
212             tmp=mgr;
213             mgr=REGION_MANAGER(mgr);
214         }
215     }else{
216         D(fprintf(stderr, "got focus (act) %s [%p]\n", OBJ_TYPESTR(reg), reg);)
217     }
218
219     /* Install default colour map only if there is no active subregion;
220      * their maps should come first. WClientWins will install their maps
221      * in region_activated. Other regions are supposed to use the same
222      * default map.
223      */
224     if(reg->active_sub==NULL && !OBJ_IS(reg, WClientWin))
225         rootwin_install_colormap(region_rootwin_of(reg), None); 
226     
227     extl_protect(NULL);
228     hook_call_o(region_activated_hook, (Obj*)reg);
229     extl_unprotect(NULL);
230 }
231
232
233 void region_lost_focus(WRegion *reg)
234 {
235     WRegion *r, *par;
236     
237     if(!REGION_IS_ACTIVE(reg)){
238         D(fprintf(stderr, "lost focus (inact) %s [%p:]\n", OBJ_TYPESTR(reg), reg);)
239         return;
240     }
241     
242     par=REGION_PARENT_REG(reg);
243     if(par!=NULL && par->active_sub==reg){
244         par->active_sub=NULL;
245         region_update_owned_grabs(par);
246     }
247
248
249 #if 0    
250     if(ioncore_g.focus_current==reg){
251         /* Find the closest active parent, or if none is found, stop at the
252          * screen and mark it "currently focused".
253          */
254         while(par!=NULL && !REGION_IS_ACTIVE(par) && !OBJ_IS(par, WScreen))
255             par=REGION_PARENT_REG(par);
256         ioncore_g.focus_current=par;
257     }
258 #endif
259
260     D(fprintf(stderr, "lost focus (act) %s [%p:]\n", OBJ_TYPESTR(reg), reg);)
261     
262     reg->flags&=~REGION_ACTIVE;
263     region_inactivated(reg);
264     r=REGION_MANAGER(reg);
265     if(r!=NULL)
266         region_managed_inactivated(r, reg);
267     
268     extl_protect(NULL);
269     hook_call_o(region_inactivated_hook, (Obj*)reg);
270     extl_unprotect(NULL);
271 }
272
273
274 /*}}}*/
275
276
277 /*{{{ Focus status requests */
278
279
280 /*EXTL_DOC
281  * Is \var{reg} active/does it or one of it's children of focus?
282  */
283 EXTL_SAFE
284 EXTL_EXPORT_MEMBER
285 bool region_is_active(WRegion *reg)
286 {
287     return REGION_IS_ACTIVE(reg);
288 }
289
290
291 bool region_may_control_focus(WRegion *reg)
292 {
293     WRegion *par, *r2;
294     
295     if(OBJ_IS_BEING_DESTROYED(reg))
296         return FALSE;
297
298     if(REGION_IS_ACTIVE(reg))
299         return TRUE;
300     
301     if(region_is_await(reg))
302         return TRUE;
303     
304     par=REGION_PARENT_REG(reg);
305     
306     if(par==NULL || !REGION_IS_ACTIVE(par))
307         return FALSE;
308     
309     r2=par->active_sub;
310     while(r2!=NULL && r2!=par){
311         if(r2==reg)
312             return TRUE;
313         r2=REGION_MANAGER(r2);
314     }
315
316     return FALSE;
317 }
318
319
320 /*}}}*/
321
322
323 /*{{{ set_focus, warp */
324
325
326 /*Time ioncore_focus_time=CurrentTime;*/
327
328
329 void region_finalise_focusing(WRegion* reg, Window win, bool warp)
330 {
331     if(warp)
332         region_do_warp(reg);
333     
334     region_set_await_focus(reg);
335     /*xwindow_do_set_focus(win);*/
336     XSetInputFocus(ioncore_g.dpy, win, RevertToParent, 
337                    CurrentTime/*ioncore_focus_time*/);
338     /*ioncore_focus_time=CurrentTime;*/
339 }
340
341
342
343 static WRegion *find_warp_to_reg(WRegion *reg)
344 {
345     if(reg==NULL)
346         return NULL;
347     if(reg->flags&REGION_PLEASE_WARP)
348         return reg;
349     return find_warp_to_reg(region_manager_or_parent(reg));
350 }
351
352
353 bool region_do_warp_default(WRegion *reg)
354 {
355     int x, y, w, h, px=0, py=0;
356     Window root;
357     
358     reg=find_warp_to_reg(reg);
359     
360     if(reg==NULL)
361         return FALSE;
362     
363     D(fprintf(stderr, "region_do_warp %p %s\n", reg, OBJ_TYPESTR(reg)));
364     
365     root=region_root_of(reg);
366     
367     region_rootpos(reg, &x, &y);
368     w=REGION_GEOM(reg).w;
369     h=REGION_GEOM(reg).h;
370
371     if(xwindow_pointer_pos(root, &px, &py)){
372         if(px>=x && py>=y && px<x+w && py<y+h)
373             return TRUE;
374     }
375     
376     XWarpPointer(ioncore_g.dpy, None, root, 0, 0, 0, 0,
377                  x+5, y+5);
378         
379     return TRUE;
380 }
381
382
383 void region_do_warp(WRegion *reg)
384 {
385     extl_protect(NULL);
386     hook_call_alt_o(region_do_warp_alt, (Obj*)reg);
387     extl_unprotect(NULL);
388 }
389
390
391 void region_maybewarp(WRegion *reg, bool warp)
392 {
393     ioncore_g.focus_next=reg;
394     ioncore_g.warp_next=(warp && ioncore_g.warp_enabled);
395 }
396
397
398 void region_set_focus(WRegion *reg)
399 {
400     region_maybewarp(reg, FALSE);
401 }
402
403
404 void region_warp(WRegion *reg)
405 {
406     region_maybewarp(reg, TRUE);
407 }
408
409
410 /*}}}*/
411
412
413 /*{{{ Misc. */
414
415
416 bool region_skip_focus(WRegion *reg)
417 {
418     while(reg!=NULL){
419         if(reg->flags&REGION_SKIP_FOCUS)
420             return TRUE;
421         reg=REGION_PARENT_REG(reg);
422     }
423     return FALSE;
424 }
425
426 /*EXTL_DOC
427  * Returns the currently focused region, if any.
428  */
429 EXTL_EXPORT
430 WRegion *ioncore_current()
431 {
432     return ioncore_g.focus_current;
433 }
434
435 /*}}}*/