]> git.decadent.org.uk Git - ion3.git/blob - ioncore/eventh.c
[svn-upgrade] Integrating new upstream version, ion3 (20070506)
[ion3.git] / ioncore / eventh.c
1 /*
2  * ion/ioncore/eventh.c
3  *
4  * Copyright (c) Tuomo Valkonen 1999-2007. 
5  *
6  * See the included file LICENSE for details.
7  */
8
9 #include <stdlib.h>
10 #include <unistd.h>
11 #include <sys/time.h>
12
13 #include <libtu/objp.h>
14 #include "common.h"
15 #include "global.h"
16 #include "rootwin.h"
17 #include "property.h"
18 #include "pointer.h"
19 #include "key.h"
20 #include "focus.h"
21 #include "selection.h"
22 #include "event.h"
23 #include "eventh.h"
24 #include "clientwin.h"
25 #include "colormap.h"
26 #include "grab.h"
27 #include "bindmaps.h"
28 #include "activity.h"
29 #include "netwm.h"
30 #include "xwindow.h"
31
32
33 /*{{{ ioncore_handle_event */
34
35
36 #define CASE_EVENT(EV) case EV:  /*\
37     fprintf(stderr, "[%#lx] %s\n", ev->xany.window, #EV);*/
38
39
40 bool ioncore_handle_event(XEvent *ev)
41 {
42     
43     switch(ev->type){
44     CASE_EVENT(MapRequest)
45         ioncore_handle_map_request(&(ev->xmaprequest));
46         break;
47     CASE_EVENT(ConfigureRequest)
48         ioncore_handle_configure_request(&(ev->xconfigurerequest));
49         break;
50     CASE_EVENT(UnmapNotify)
51         ioncore_handle_unmap_notify(&(ev->xunmap));
52         break;
53     CASE_EVENT(DestroyNotify)
54         ioncore_handle_destroy_notify(&(ev->xdestroywindow));
55         break;
56     CASE_EVENT(ClientMessage)
57         ioncore_handle_client_message(&(ev->xclient));
58         break;
59     CASE_EVENT(PropertyNotify)
60         ioncore_handle_property(&(ev->xproperty));
61         break;
62     CASE_EVENT(FocusIn)
63         ioncore_handle_focus_in(&(ev->xfocus));
64         break;
65     CASE_EVENT(FocusOut)
66         ioncore_handle_focus_out(&(ev->xfocus));
67         break;
68     CASE_EVENT(EnterNotify)
69         ioncore_handle_enter_window(ev);
70         break;
71     CASE_EVENT(Expose)        
72         ioncore_handle_expose(&(ev->xexpose));
73         break;
74     CASE_EVENT(KeyPress)
75         ioncore_handle_keyboard(ev);
76         break;
77     CASE_EVENT(KeyRelease)
78         ioncore_handle_keyboard(ev);
79         break;
80     CASE_EVENT(ButtonPress)
81         ioncore_handle_buttonpress(ev);
82         break;
83     CASE_EVENT(ColormapNotify)
84         ioncore_handle_colormap_notify(&(ev->xcolormap));
85         break;
86     CASE_EVENT(MappingNotify)
87         ioncore_handle_mapping_notify(ev);
88         break;
89     CASE_EVENT(SelectionClear)
90         ioncore_clear_selection();
91         break;
92     CASE_EVENT(SelectionNotify)
93         ioncore_handle_selection(&(ev->xselection));
94         break;
95     CASE_EVENT(SelectionRequest)
96         ioncore_handle_selection_request(&(ev->xselectionrequest));
97         break;
98     }
99     
100     return TRUE;
101 }
102
103
104 /*}}}*/
105
106
107 /*{{{ Map, unmap, destroy */
108
109
110 void ioncore_handle_map_request(const XMapRequestEvent *ev)
111 {
112     WRegion *reg;
113     
114     reg=XWINDOW_REGION_OF(ev->window);
115     
116     if(reg!=NULL)
117         return;
118     
119     ioncore_manage_clientwin(ev->window, TRUE);
120 }
121
122
123 void ioncore_handle_unmap_notify(const XUnmapEvent *ev)
124 {
125     WClientWin *cwin;
126
127     /* We are not interested in SubstructureNotify -unmaps. */
128     if(ev->event!=ev->window && ev->send_event!=True)
129         return;
130
131     cwin=XWINDOW_REGION_OF_T(ev->window, WClientWin);
132     
133     if(cwin!=NULL)
134         clientwin_unmapped(cwin);
135 }
136
137
138 void ioncore_handle_destroy_notify(const XDestroyWindowEvent *ev)
139 {
140     WClientWin *cwin;
141
142     cwin=XWINDOW_REGION_OF_T(ev->window, WClientWin);
143     
144     if(cwin!=NULL)
145         clientwin_destroyed(cwin);
146 }
147
148
149 /*}}}*/
150
151
152 /*{{{ Client configure/property/message */
153
154
155 void ioncore_handle_configure_request(XConfigureRequestEvent *ev)
156 {
157     WClientWin *cwin;
158     XWindowChanges wc;
159
160     cwin=XWINDOW_REGION_OF_T(ev->window, WClientWin);
161     
162     if(cwin==NULL){
163         wc.border_width=ev->border_width;
164         wc.sibling=ev->above;
165         wc.stack_mode=ev->detail;
166         wc.x=ev->x;
167         wc.y=ev->y;
168         wc.width=ev->width;
169         wc.height=ev->height;
170         XConfigureWindow(ioncore_g.dpy, ev->window, ev->value_mask, &wc);
171         return;
172     }
173
174     clientwin_handle_configure_request(cwin, ev);
175 }
176
177
178 void ioncore_handle_client_message(const XClientMessageEvent *ev)
179 {
180     netwm_handle_client_message(ev);
181
182 #if 0
183     WClientWin *cwin;
184
185     if(ev->message_type!=ioncore_g.atom_wm_change_state)
186         return;
187     
188     cwin=XWINDOW_REGION_OF_T(ev->window, WClientWin);
189
190     if(cwin==NULL)
191         return;
192     
193     if(ev->format==32 && ev->data.l[0]==IconicState){
194         if(cwin->state==NormalState)
195             iconify_clientwin(cwin);
196     }
197 #endif
198 }
199
200
201 static bool pchg_mrsh_extl(ExtlFn fn, void **p)
202 {
203     extl_call(fn, "oi", NULL, p[0], ((XPropertyEvent*)p[1])->atom);
204     return TRUE;
205 }
206
207 static bool pchg_mrsh(void (*fn)(void *p1, void *p2), void **p)
208 {
209     fn(p[0], p[1]);
210     return TRUE;
211 }
212                              
213
214 void ioncore_handle_property(const XPropertyEvent *ev)
215 {
216     WClientWin *cwin;
217     
218     cwin=XWINDOW_REGION_OF_T(ev->window, WClientWin);
219     
220     if(cwin==NULL)
221         return;
222     
223     if(ev->atom==XA_WM_HINTS){
224         XWMHints *hints;
225         hints=XGetWMHints(ioncore_g.dpy, ev->window);
226         /* region_notify/clear_activity take care of checking current state */
227         if(hints!=NULL){
228             if(hints->flags&XUrgencyHint){
229                 if(!region_skip_focus((WRegion*)cwin))
230                     region_set_activity((WRegion*)cwin, SETPARAM_SET);
231             }else{
232                 region_set_activity((WRegion*)cwin, SETPARAM_UNSET);
233             }
234         }
235         XFree(hints);
236     }else if(ev->atom==XA_WM_NORMAL_HINTS){
237         clientwin_get_size_hints(cwin);
238     }else if(ev->atom==XA_WM_NAME){
239         if(!(cwin->flags&CLIENTWIN_USE_NET_WM_NAME))
240             clientwin_get_set_name(cwin);
241     }else if(ev->atom==XA_WM_TRANSIENT_FOR){
242         clientwin_tfor_changed(cwin);
243     }else if(ev->atom==ioncore_g.atom_wm_protocols){
244         clientwin_get_protocols(cwin);
245     }else{
246         netwm_handle_property(cwin, ev);
247     }
248     
249     /* Call property hook */
250     {
251         void *p[2];
252         p[0]=(void*)cwin;
253         p[1]=(void*)ev;
254         hook_call(clientwin_property_change_hook, p,
255                   (WHookMarshall*)pchg_mrsh,
256                   (WHookMarshallExtl*)pchg_mrsh_extl);
257     }
258 }
259
260
261 /*}}}*/
262
263
264 /*{{{ Misc. notifies */
265
266
267 void ioncore_handle_mapping_notify(XEvent *ev)
268 {
269     do{
270         XRefreshKeyboardMapping(&(ev->xmapping));
271     }while(XCheckTypedEvent(ioncore_g.dpy, MappingNotify, ev));
272     
273     ioncore_refresh_bindmaps();
274 }
275
276
277 /*}}}*/
278
279
280 /*{{{ Expose */
281
282
283 void ioncore_handle_expose(const XExposeEvent *ev)
284 {
285     WWindow *wwin;
286     WRootWin *rootwin;
287     XEvent tmp;
288     
289     while(XCheckWindowEvent(ioncore_g.dpy, ev->window, ExposureMask, &tmp))
290         /* nothing */;
291
292     wwin=XWINDOW_REGION_OF_T(ev->window, WWindow);
293
294     if(wwin!=NULL)
295         window_draw(wwin, FALSE);
296 }
297
298
299 /*}}}*/
300
301
302 /*{{{ Enter window, focus */
303
304
305 void ioncore_handle_enter_window(XEvent *ev)
306 {
307     XEnterWindowEvent *eev=&(ev->xcrossing);
308     WRegion *reg=NULL;
309     
310     if(ioncore_g.input_mode!=IONCORE_INPUTMODE_NORMAL)
311         return;
312         
313     if(eev->mode!=NotifyNormal && !ioncore_g.warp_enabled)
314         return;
315                 
316     reg=XWINDOW_REGION_OF_T(eev->window, WRegion);
317     
318     if(reg==NULL)
319         return;
320         
321     if(REGION_IS_ACTIVE(reg))
322         return;
323         
324     if(region_skip_focus(reg))
325         return;
326     
327     if(ioncore_g.focus_next!=NULL &&
328        ioncore_g.focus_next_source<IONCORE_FOCUSNEXT_ENTERWINDOW){
329         return;
330     }
331     
332     /* If a child of 'reg' is to be focused, do not process this
333      * event. (ioncore_g.focus_next should only be set here by
334      * another call to use from ioncore_handle_enter_window below.)
335      */
336     if(ioncore_g.focus_next!=NULL){
337         WRegion *r2=ioncore_g.focus_next;
338         while(r2!=NULL){
339             if(r2==reg)
340                 return;
341             r2=REGION_PARENT_REG(r2);
342         }
343     }
344     
345     if(region_goto_flags(reg, (REGION_GOTO_FOCUS|
346                                REGION_GOTO_NOWARP|
347                                REGION_GOTO_ENTERWINDOW))){
348         ioncore_g.focus_next_source=IONCORE_FOCUSNEXT_ENTERWINDOW;
349     }
350 }
351
352
353 static bool pointer_in_root(Window root1)
354 {
355     Window root2=None, win;
356     int x, y, wx, wy;
357     uint mask;
358     
359     XQueryPointer(ioncore_g.dpy, root1, &root2, &win,
360                   &x, &y, &wx, &wy, &mask);
361     
362     return (root1==root2);
363 }
364
365
366 void ioncore_handle_focus_in(const XFocusChangeEvent *ev)
367 {
368     WRegion *reg;
369     WWindow *wwin;
370     
371     reg=XWINDOW_REGION_OF_T(ev->window, WRegion);
372     
373     if(reg==NULL)
374         return;
375
376     D(fprintf(stderr, "FI: %s %p %d %d\n", OBJ_TYPESTR(reg), reg, ev->mode, ev->detail);)
377
378     if(ev->mode==NotifyGrab)
379         return;
380
381     if(ev->detail==NotifyPointer)
382         return;
383     
384     /* Input contexts */
385     if(OBJ_IS(reg, WWindow)){
386         wwin=(WWindow*)reg;
387         if(wwin->xic!=NULL)
388             XSetICFocus(wwin->xic);
389     }
390
391     if(ev->detail!=NotifyInferior)
392         netwm_set_active(reg);
393     
394     region_got_focus(reg);
395     
396     if(ioncore_g.focus_next!=NULL && 
397        ioncore_g.focus_next_source<IONCORE_FOCUSNEXT_FALLBACK){
398         return;
399     }
400     
401     if((ev->detail==NotifyPointerRoot || ev->detail==NotifyDetailNone) 
402        && ev->window==region_root_of(reg) /* OBJ_IS(reg, WRootWin) */){
403         /* Restore focus if it was returned to a root window and we don't
404          * know of a pending focus change.
405          */
406         if(pointer_in_root(ev->window)){
407             region_set_focus(reg);
408             ioncore_g.focus_next_source=IONCORE_FOCUSNEXT_FALLBACK;
409         }
410     }else{
411         /* Something got the focus, don't use fallback. */
412         ioncore_g.focus_next=NULL;
413     }
414 }
415
416
417 void ioncore_handle_focus_out(const XFocusChangeEvent *ev)
418 {
419     WRegion *reg;
420     WWindow *wwin;
421     
422     reg=XWINDOW_REGION_OF_T(ev->window, WRegion);
423     
424     if(reg==NULL)
425         return;
426
427     D(fprintf(stderr, "FO: %s %p %d %d\n", OBJ_TYPESTR(reg), reg, ev->mode, ev->detail);)
428
429     if(ev->mode==NotifyGrab)
430         return;
431
432     if(ev->detail==NotifyPointer)
433         return;
434
435     D(if(OBJ_IS(reg, WRootWin))
436       fprintf(stderr, "scr-out %d %d %d\n", ((WRootWin*)reg)->xscr, ev->mode, ev->detail));
437
438     if(OBJ_IS(reg, WWindow)){
439         wwin=(WWindow*)reg;
440         if(wwin->xic!=NULL)
441             XUnsetICFocus(wwin->xic);
442     }
443     
444     if(ev->detail!=NotifyInferior)
445         region_lost_focus(reg);
446     else
447         region_got_focus(reg);
448 }
449
450
451 /*}}}*/
452
453
454 /*{{{ Pointer, keyboard */
455
456
457 void ioncore_handle_buttonpress(XEvent *ev)
458 {
459     XEvent tmp;
460     Window win_pressed;
461     bool finished=FALSE;
462
463     if(ioncore_grab_held())
464         return;
465
466     win_pressed=ev->xbutton.window;
467     
468     if(!ioncore_do_handle_buttonpress(&(ev->xbutton)))
469         return;
470
471     while(!finished && ioncore_grab_held()){
472         XFlush(ioncore_g.dpy);
473         ioncore_get_event(ev, IONCORE_EVENTMASK_PTRLOOP);
474         
475         if(ev->type==MotionNotify){
476             /* Handle sequences of MotionNotify (possibly followed by button
477              * release) as one.
478              */
479             if(XPeekEvent(ioncore_g.dpy, &tmp)){
480                 if(tmp.type==MotionNotify || tmp.type==ButtonRelease)
481                     XNextEvent(ioncore_g.dpy, ev);
482             }
483         }
484         
485         switch(ev->type){
486         CASE_EVENT(ButtonRelease)
487             if(ioncore_do_handle_buttonrelease(&ev->xbutton))
488                 finished=TRUE;
489             break;
490         CASE_EVENT(MotionNotify)
491             ioncore_do_handle_motionnotify(&ev->xmotion);
492             break;
493         CASE_EVENT(Expose)
494             ioncore_handle_expose(&(ev->xexpose));
495             break;
496         CASE_EVENT(KeyPress)
497         CASE_EVENT(KeyRelease)
498             ioncore_handle_grabs(ev);
499             break;
500         CASE_EVENT(FocusIn)
501             ioncore_handle_focus_in(&(ev->xfocus));
502             break;
503         CASE_EVENT(FocusOut)
504             ioncore_handle_focus_out(&(ev->xfocus));
505             break;
506         }
507     }
508 }
509
510
511 void ioncore_handle_keyboard(XEvent *ev)
512 {
513     if(ioncore_handle_grabs(ev))
514         return;
515     
516     if(ev->type==KeyPress)
517         ioncore_do_handle_keypress(&(ev->xkey));
518 }
519
520
521 /*}}}*/
522
523