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