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