]> git.decadent.org.uk Git - ion3.git/blob - ioncore/eventh.c
Imported Upstream version 20090110
[ion3.git] / ioncore / eventh.c
1 /*
2  * ion/ioncore/eventh.c
3  *
4  * Copyright (c) Tuomo Valkonen 1999-2009. 
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        ioncore_g.no_mousefocus){
312         return;
313     }
314         
315     if(eev->mode!=NotifyNormal && !ioncore_g.warp_enabled)
316         return;
317                 
318     reg=XWINDOW_REGION_OF_T(eev->window, WRegion);
319     
320     if(reg==NULL)
321         return;
322         
323     if(REGION_IS_ACTIVE(reg))
324         return;
325         
326     if(region_skip_focus(reg))
327         return;
328     
329     if(ioncore_g.focus_next!=NULL &&
330        ioncore_g.focus_next_source<IONCORE_FOCUSNEXT_ENTERWINDOW){
331         return;
332     }
333     
334     /* If a child of 'reg' is to be focused, do not process this
335      * event. (ioncore_g.focus_next should only be set here by
336      * another call to use from ioncore_handle_enter_window below.)
337      */
338     if(ioncore_g.focus_next!=NULL){
339         WRegion *r2=ioncore_g.focus_next;
340         while(r2!=NULL){
341             if(r2==reg)
342                 return;
343             r2=REGION_PARENT_REG(r2);
344         }
345     }
346     
347     if(region_goto_flags(reg, (REGION_GOTO_FOCUS|
348                                REGION_GOTO_NOWARP|
349                                REGION_GOTO_ENTERWINDOW))){
350         ioncore_g.focus_next_source=IONCORE_FOCUSNEXT_ENTERWINDOW;
351     }
352 }
353
354
355 static bool pointer_in_root(Window root1)
356 {
357     Window root2=None, win;
358     int x, y, wx, wy;
359     uint mask;
360     
361     XQueryPointer(ioncore_g.dpy, root1, &root2, &win,
362                   &x, &y, &wx, &wy, &mask);
363     
364     return (root1==root2);
365 }
366
367
368 void ioncore_handle_focus_in(const XFocusChangeEvent *ev)
369 {
370     WRegion *reg;
371     WWindow *wwin;
372     
373     reg=XWINDOW_REGION_OF_T(ev->window, WRegion);
374     
375     if(reg==NULL)
376         return;
377
378     D(fprintf(stderr, "FI: %s %p %d %d\n", OBJ_TYPESTR(reg), reg, ev->mode, ev->detail);)
379
380     if(ev->mode==NotifyGrab)
381         return;
382
383     if(ev->detail==NotifyPointer)
384         return;
385     
386     /* Input contexts */
387     if(OBJ_IS(reg, WWindow)){
388         wwin=(WWindow*)reg;
389         if(wwin->xic!=NULL)
390             XSetICFocus(wwin->xic);
391     }
392
393     if(ev->detail!=NotifyInferior)
394         netwm_set_active(reg);
395     
396     region_got_focus(reg);
397     
398     if(ioncore_g.focus_next!=NULL && 
399        ioncore_g.focus_next_source<IONCORE_FOCUSNEXT_FALLBACK){
400         return;
401     }
402     
403     if((ev->detail==NotifyPointerRoot || ev->detail==NotifyDetailNone) 
404        && ev->window==region_root_of(reg) /* OBJ_IS(reg, WRootWin) */){
405         /* Restore focus if it was returned to a root window and we don't
406          * know of a pending focus change.
407          */
408         if(pointer_in_root(ev->window)){
409             region_set_focus(reg);
410             ioncore_g.focus_next_source=IONCORE_FOCUSNEXT_FALLBACK;
411         }
412     }else{
413         /* Something got the focus, don't use fallback. */
414         ioncore_g.focus_next=NULL;
415     }
416 }
417
418
419 void ioncore_handle_focus_out(const XFocusChangeEvent *ev)
420 {
421     WRegion *reg;
422     WWindow *wwin;
423     
424     reg=XWINDOW_REGION_OF_T(ev->window, WRegion);
425     
426     if(reg==NULL)
427         return;
428
429     D(fprintf(stderr, "FO: %s %p %d %d\n", OBJ_TYPESTR(reg), reg, ev->mode, ev->detail);)
430
431     if(ev->mode==NotifyGrab)
432         return;
433
434     if(ev->detail==NotifyPointer)
435         return;
436
437     D(if(OBJ_IS(reg, WRootWin))
438       fprintf(stderr, "scr-out %d %d %d\n", ((WRootWin*)reg)->xscr, ev->mode, ev->detail));
439
440     if(OBJ_IS(reg, WWindow)){
441         wwin=(WWindow*)reg;
442         if(wwin->xic!=NULL)
443             XUnsetICFocus(wwin->xic);
444     }
445     
446     if(ev->detail!=NotifyInferior)
447         region_lost_focus(reg);
448     else
449         region_got_focus(reg);
450 }
451
452
453 /*}}}*/
454
455
456 /*{{{ Pointer, keyboard */
457
458
459 void ioncore_handle_buttonpress(XEvent *ev)
460 {
461     XEvent tmp;
462     Window win_pressed;
463     bool finished=FALSE;
464
465     if(ioncore_grab_held())
466         return;
467
468     win_pressed=ev->xbutton.window;
469     
470     if(!ioncore_do_handle_buttonpress(&(ev->xbutton)))
471         return;
472
473     while(!finished && ioncore_grab_held()){
474         XFlush(ioncore_g.dpy);
475         ioncore_get_event(ev, IONCORE_EVENTMASK_PTRLOOP);
476         
477         if(ev->type==MotionNotify){
478             /* Handle sequences of MotionNotify (possibly followed by button
479              * release) as one.
480              */
481             if(XPeekEvent(ioncore_g.dpy, &tmp)){
482                 if(tmp.type==MotionNotify || tmp.type==ButtonRelease)
483                     XNextEvent(ioncore_g.dpy, ev);
484             }
485         }
486         
487         switch(ev->type){
488         CASE_EVENT(ButtonRelease)
489             if(ioncore_do_handle_buttonrelease(&ev->xbutton))
490                 finished=TRUE;
491             break;
492         CASE_EVENT(MotionNotify)
493             ioncore_do_handle_motionnotify(&ev->xmotion);
494             break;
495         CASE_EVENT(Expose)
496             ioncore_handle_expose(&(ev->xexpose));
497             break;
498         CASE_EVENT(KeyPress)
499         CASE_EVENT(KeyRelease)
500             ioncore_handle_grabs(ev);
501             break;
502         CASE_EVENT(FocusIn)
503             ioncore_handle_focus_in(&(ev->xfocus));
504             break;
505         CASE_EVENT(FocusOut)
506             ioncore_handle_focus_out(&(ev->xfocus));
507             break;
508         }
509     }
510 }
511
512
513 void ioncore_handle_keyboard(XEvent *ev)
514 {
515     if(ioncore_handle_grabs(ev))
516         return;
517     
518     if(ev->type==KeyPress)
519         ioncore_do_handle_keypress(&(ev->xkey));
520 }
521
522
523 /*}}}*/
524
525