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