]> git.decadent.org.uk Git - ion3.git/blob - ioncore/event.c
Merge commit '20070927' into HEAD
[ion3.git] / ioncore / event.c
1 /*
2  * ion/ioncore/event.c
3  *
4  * Copyright (c) Tuomo Valkonen 1999-2007. 
5  *
6  * See the included file LICENSE for details.
7  */
8
9 #include <X11/Xmd.h>
10 #include <stdlib.h>
11 #include <unistd.h>
12 #include <sys/time.h>
13 #include <sys/signal.h>
14
15 #include <libmainloop/select.h>
16 #include <libmainloop/signal.h>
17 #include <libmainloop/defer.h>
18
19 #include "common.h"
20 #include "global.h"
21 #include "event.h"
22 #include "eventh.h"
23 #include "focus.h"
24 #include "exec.h"
25 #include "ioncore.h"
26
27
28
29 /*{{{ Hooks */
30
31
32 WHook *ioncore_handle_event_alt=NULL;
33
34
35 /*}}}*/
36
37
38 /*{{{ Signal check */
39     
40
41 static void check_signals()
42 {
43     int kill_sig=mainloop_check_signals();
44     
45     if(kill_sig!=0){
46         if(kill_sig==SIGUSR1){
47             ioncore_restart();
48             assert(0);
49         } 
50         if(kill_sig==SIGTERM){
51             /* Save state if not running under a session manager. */
52             ioncore_emergency_snapshot();
53             ioncore_resign();
54             /* We may still return here if running under a session manager. */
55         }else{
56             ioncore_emergency_snapshot();
57             ioncore_deinit();
58             kill(getpid(), kill_sig);
59         }
60     }
61 }
62
63
64 /*}}}*/
65
66
67 /*{{{ Timestamp stuff */
68
69 #define CHKEV(E, T) case E: tm=((T*)ev)->time; break;
70 #define CLOCK_SKEW_MS 30000
71
72 static Time last_timestamp=CurrentTime;
73
74 void ioncore_update_timestamp(XEvent *ev)
75 {
76     Time tm;
77     
78     switch(ev->type){
79     CHKEV(ButtonPress, XButtonPressedEvent);
80     CHKEV(ButtonRelease, XButtonReleasedEvent);
81     CHKEV(EnterNotify, XEnterWindowEvent);
82     CHKEV(KeyPress, XKeyPressedEvent);
83     CHKEV(KeyRelease, XKeyReleasedEvent);
84     CHKEV(LeaveNotify, XLeaveWindowEvent);
85     CHKEV(MotionNotify, XPointerMovedEvent);
86     CHKEV(PropertyNotify, XPropertyEvent);
87     CHKEV(SelectionClear, XSelectionClearEvent);
88     CHKEV(SelectionNotify, XSelectionEvent);
89     CHKEV(SelectionRequest, XSelectionRequestEvent);
90     default:
91         return;
92     }
93
94     if(tm>last_timestamp || last_timestamp - tm > CLOCK_SKEW_MS)
95         last_timestamp=tm;
96 }
97
98
99 Time ioncore_get_timestamp()
100 {
101     if(last_timestamp==CurrentTime){
102         /* Idea blatantly copied from wmx */
103         XEvent ev;
104         Atom dummy;
105         
106         D(fprintf(stderr, "Attempting to get time from X server."));
107         
108         dummy=XInternAtom(ioncore_g.dpy, "_ION_TIMEREQUEST", False);
109         if(dummy==None){
110             warn(TR("Time request from X server failed."));
111             return 0;
112         }
113         /* TODO: use some other window that should also function as a
114          * NET_WM support check window.
115          */
116         XChangeProperty(ioncore_g.dpy, ioncore_g.rootwins->dummy_win,
117                         dummy, dummy, 8, PropModeAppend,
118                         (unsigned char*)"", 0);
119         ioncore_get_event(&ev, PropertyChangeMask);
120         XPutBackEvent(ioncore_g.dpy, &ev);
121     }
122     
123     return last_timestamp;
124 }
125
126
127 /*}}}*/
128
129
130 /*{{{ Event reading */
131
132
133 void ioncore_get_event(XEvent *ev, long mask)
134 {
135     fd_set rfds;
136     
137     while(1){
138         check_signals();
139         
140         if(XCheckMaskEvent(ioncore_g.dpy, mask, ev)){
141             ioncore_update_timestamp(ev);
142             return;
143         }
144         
145         FD_ZERO(&rfds);
146         FD_SET(ioncore_g.conn, &rfds);
147
148         /* Other FD:s are _not_ to be handled! */
149         select(ioncore_g.conn+1, &rfds, NULL, NULL, NULL);
150     }
151 }
152
153
154 /*}}}*/
155
156
157 /*{{{ Flush */
158
159
160 static void skip_enterwindow()
161 {
162     XEvent ev;
163     
164     XSync(ioncore_g.dpy, False);
165     
166     while(XCheckMaskEvent(ioncore_g.dpy, EnterWindowMask, &ev)){
167         ioncore_update_timestamp(&ev);
168     }
169 }
170
171
172 void ioncore_flushfocus()
173 {
174     WRegion *next;
175     bool warp;
176     
177     if(ioncore_g.input_mode!=IONCORE_INPUTMODE_NORMAL)
178         return;
179         
180     next=ioncore_g.focus_next;
181     warp=ioncore_g.warp_next;
182
183     if(next==NULL)
184         return;
185         
186     ioncore_g.focus_next=NULL;
187         
188     region_do_set_focus(next, warp);
189         
190     /* Just greedily eating it all away that X has to offer
191      * seems to be the best we can do with Xlib.
192      */
193     if(warp)
194         skip_enterwindow();
195 }
196
197
198 /*}}}*/
199
200
201 /*{{{ X connection FD handler */
202
203
204 void ioncore_x_connection_handler(int conn, void *unused)
205 {
206     XEvent ev;
207
208     XNextEvent(ioncore_g.dpy, &ev);
209     ioncore_update_timestamp(&ev);
210
211     hook_call_alt_p(ioncore_handle_event_alt, &ev, NULL);
212 }
213
214
215 /*}}}*/
216
217
218 /*{{{ Mainloop */
219
220
221 void ioncore_mainloop()
222 {
223     mainloop_trap_signals(NULL);
224     
225     ioncore_g.opmode=IONCORE_OPMODE_NORMAL;
226
227     while(1){
228         check_signals();
229         mainloop_execute_deferred();
230         
231         if(QLength(ioncore_g.dpy)==0){
232             XSync(ioncore_g.dpy, False);
233             
234             if(QLength(ioncore_g.dpy)==0){
235                 ioncore_flushfocus();
236                 XSync(ioncore_g.dpy, False);
237                 
238                 if(QLength(ioncore_g.dpy)==0){
239                     mainloop_select();
240                     continue;
241                 }
242             }
243         }
244         
245         ioncore_x_connection_handler(ioncore_g.conn, NULL);
246     }
247 }
248
249
250 /*}}}*/
251