1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: src/x11/evtloop.cpp
3 // Purpose: implements wxEventLoop for X11
4 // Author: Julian Smart
5 // Modified by:
6 // Created: 01.06.01
7 // Copyright: (c) 2002 Julian Smart
8 // Licence: wxWindows licence
9 ///////////////////////////////////////////////////////////////////////////////
10
11 // ============================================================================
12 // declarations
13 // ============================================================================
14
15 // ----------------------------------------------------------------------------
16 // headers
17 // ----------------------------------------------------------------------------
18
19 // for compilers that support precompilation, includes "wx.h".
20 #include "wx/wxprec.h"
21
22 #include "wx/evtloop.h"
23
24 #ifndef WX_PRECOMP
25 #include "wx/hash.h"
26 #include "wx/app.h"
27 #include "wx/window.h"
28 #include "wx/module.h"
29 #endif
30
31 #include "wx/private/fdiodispatcher.h"
32 #include "wx/unix/private.h"
33 #include "wx/x11/private.h"
34 #include "wx/generic/private/timer.h"
35
36 #if wxUSE_THREADS
37 #include "wx/thread.h"
38 #endif
39
40 #include <X11/Xlib.h>
41 #include <sys/time.h>
42 #include <unistd.h>
43
44 #ifdef HAVE_SYS_SELECT_H
45 # include <sys/select.h>
46 #endif
47
48 // ----------------------------------------------------------------------------
49 // wxEventLoopImpl
50 // ----------------------------------------------------------------------------
51
52 class WXDLLEXPORT wxEventLoopImpl
53 {
54 public:
55 // ctor
wxEventLoopImpl()56 wxEventLoopImpl() { SetExitCode(0); m_keepGoing = false; }
57
58 // process an XEvent, return true if it was processed
59 bool ProcessEvent(XEvent* event);
60
61 // generate an idle message, return true if more idle time requested
62 bool SendIdleEvent();
63
64 // set/get the exit code
SetExitCode(int exitcode)65 void SetExitCode(int exitcode) { m_exitcode = exitcode; }
GetExitCode() const66 int GetExitCode() const { return m_exitcode; }
67
68 public:
69 // preprocess an event, return true if processed (i.e. no further
70 // dispatching required)
71 bool PreProcessEvent(XEvent* event);
72
73 // the exit code of the event loop
74 int m_exitcode;
75
76 bool m_keepGoing;
77 };
78
79 // ============================================================================
80 // wxEventLoopImpl implementation
81 // ============================================================================
82
83 // ----------------------------------------------------------------------------
84 // wxEventLoopImpl message processing
85 // ----------------------------------------------------------------------------
86
ProcessEvent(XEvent * event)87 bool wxEventLoopImpl::ProcessEvent(XEvent *event)
88 {
89 // give us the chance to preprocess the message first
90 if ( PreProcessEvent(event) )
91 return true;
92
93 // if it wasn't done, dispatch it to the corresponding window
94 if (wxTheApp)
95 return wxTheApp->ProcessXEvent((WXEvent*) event);
96
97 return false;
98 }
99
PreProcessEvent(XEvent * WXUNUSED (event))100 bool wxEventLoopImpl::PreProcessEvent(XEvent *WXUNUSED(event))
101 {
102 return false;
103 }
104
105 // ----------------------------------------------------------------------------
106 // wxEventLoopImpl idle event processing
107 // ----------------------------------------------------------------------------
108
SendIdleEvent()109 bool wxEventLoopImpl::SendIdleEvent()
110 {
111 return wxTheApp->ProcessIdle();
112 }
113
114 // ============================================================================
115 // wxEventLoop implementation
116 // ============================================================================
117
118 // ----------------------------------------------------------------------------
119 // wxEventLoop running and exiting
120 // ----------------------------------------------------------------------------
121
~wxGUIEventLoop()122 wxGUIEventLoop::~wxGUIEventLoop()
123 {
124 wxASSERT_MSG( !m_impl, wxT("should have been deleted in Run()") );
125 }
126
DoRun()127 int wxGUIEventLoop::DoRun()
128 {
129 m_impl = new wxEventLoopImpl;
130
131 m_impl->m_keepGoing = true;
132 while ( m_impl->m_keepGoing )
133 {
134 // generate and process idle events for as long as we don't have
135 // anything else to do
136 while ( ! Pending() )
137 {
138 #if wxUSE_TIMER
139 wxGenericTimerImpl::NotifyTimers(); // TODO: is this the correct place for it?
140 #endif
141 if (!m_impl->SendIdleEvent())
142 {
143 // Break out of while loop
144 break;
145 }
146 }
147
148 // a message came or no more idle processing to do, sit in Dispatch()
149 // waiting for the next message
150 if ( !Dispatch() )
151 {
152 break;
153 }
154 }
155
156 OnExit();
157
158 int exitcode = m_impl->GetExitCode();
159 wxDELETE(m_impl);
160
161 return exitcode;
162 }
163
ScheduleExit(int rc)164 void wxGUIEventLoop::ScheduleExit(int rc)
165 {
166 if ( m_impl )
167 {
168 m_impl->SetExitCode(rc);
169 m_impl->m_keepGoing = false;
170 }
171 }
172
173 // ----------------------------------------------------------------------------
174 // wxEventLoop message processing dispatching
175 // ----------------------------------------------------------------------------
176
Pending() const177 bool wxGUIEventLoop::Pending() const
178 {
179 XFlush( wxGlobalDisplay() );
180 return (XPending( wxGlobalDisplay() ) > 0);
181 }
182
Dispatch()183 bool wxGUIEventLoop::Dispatch()
184 {
185 // see comment in wxEventLoopManual::ProcessEvents()
186 if ( wxTheApp )
187 wxTheApp->ProcessPendingEvents();
188
189 XEvent event;
190
191 // TODO allowing for threads, as per e.g. wxMSW
192
193 // This now waits until either an X event is received,
194 // or the select times out. So we should now process
195 // wxTimers in a reasonably timely fashion. However it
196 // does also mean that idle processing will happen more
197 // often, so we should probably limit idle processing to
198 // not be repeated more than every N milliseconds.
199
200 if (XPending( wxGlobalDisplay() ) == 0)
201 {
202 #if wxUSE_NANOX
203 GR_TIMEOUT timeout = 10; // Milliseconds
204 // Wait for next event, or timeout
205 GrGetNextEventTimeout(& event, timeout);
206
207 // Fall through to ProcessEvent.
208 // we'll assume that ProcessEvent will just ignore
209 // the event if there was a timeout and no event.
210
211 #else
212 struct timeval tv;
213 tv.tv_sec=0;
214 tv.tv_usec=10000; // TODO make this configurable
215 int fd = ConnectionNumber( wxGlobalDisplay() );
216
217 fd_set readset;
218 fd_set writeset;
219 wxFD_ZERO(&readset);
220 wxFD_ZERO(&writeset);
221 wxFD_SET(fd, &readset);
222
223 if (select( fd+1, &readset, &writeset, NULL, &tv ) != 0)
224 {
225 // An X11 event was pending, get it
226 if (wxFD_ISSET( fd, &readset ))
227 XNextEvent( wxGlobalDisplay(), &event );
228 }
229 #endif
230 }
231 else
232 {
233 XNextEvent( wxGlobalDisplay(), &event );
234 }
235
236 #if wxUSE_SOCKETS
237 // handle any pending socket events:
238 wxFDIODispatcher::DispatchPending();
239 #endif
240
241 (void) m_impl->ProcessEvent( &event );
242 return true;
243 }
244
YieldFor(long eventsToProcess)245 bool wxGUIEventLoop::YieldFor(long eventsToProcess)
246 {
247 // Sometimes only 2 yields seem
248 // to do the trick, e.g. in the
249 // progress dialog
250 int i;
251 for (i = 0; i < 2; i++)
252 {
253 m_isInsideYield = true;
254 m_eventsToProcessInsideYield = eventsToProcess;
255
256 // Call dispatch at least once so that sockets
257 // can be tested
258 wxTheApp->Dispatch();
259
260 // TODO: implement event filtering using the eventsToProcess mask
261 while (wxTheApp && wxTheApp->Pending())
262 wxTheApp->Dispatch();
263
264 #if wxUSE_TIMER
265 wxGenericTimerImpl::NotifyTimers();
266 #endif
267 ProcessIdle();
268
269 m_isInsideYield = false;
270 }
271
272 return true;
273 }
274