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