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