1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: x11/evtloop.cpp
3 // Purpose: implements wxEventLoop for X11
4 // Author: Julian Smart
8 // Copyright: (c) 2002 Julian Smart
9 // License: wxWindows licence
10 ///////////////////////////////////////////////////////////////////////////////
12 // ============================================================================
14 // ============================================================================
16 // ----------------------------------------------------------------------------
18 // ----------------------------------------------------------------------------
20 #include "wx/window.h"
22 #include "wx/evtloop.h"
23 #include "wx/tooltip.h"
25 #include "wx/thread.h"
29 #include "wx/module.h"
30 #include "wx/x11/private.h"
37 // ----------------------------------------------------------------------------
39 // ----------------------------------------------------------------------------
41 typedef void (*wxSocketCallback
) (int fd
, void* data
);
43 class wxSocketTableEntry
: public wxObject
48 m_fdInput
= -1; m_fdOutput
= -1;
49 m_callbackInput
= NULL
; m_callbackOutput
= NULL
;
50 m_dataInput
= NULL
; m_dataOutput
= NULL
;
55 wxSocketCallback m_callbackInput
;
56 wxSocketCallback m_callbackOutput
;
62 { wxSocketTableInput
, wxSocketTableOutput
} wxSocketTableType
;
64 class wxSocketTable
: public wxHashTable
67 wxSocketTable(): wxHashTable(wxKEY_INTEGER
)
72 WX_CLEAR_HASH_TABLE(*this)
75 wxSocketTableEntry
* FindEntry(int fd
);
77 void RegisterCallback(int fd
, wxSocketTableType socketType
, wxSocketCallback callback
, void* data
);
79 void UnregisterCallback(int fd
, wxSocketTableType socketType
);
81 bool CallCallback(int fd
, wxSocketTableType socketType
);
83 void FillSets(fd_set
* readset
, fd_set
* writeset
, int* highest
);
85 void ProcessEvents(fd_set
* readset
, fd_set
* writeset
);
88 wxSocketTableEntry
* wxSocketTable::FindEntry(int fd
)
90 wxSocketTableEntry
* entry
= (wxSocketTableEntry
*) Get(fd
);
94 void wxSocketTable::RegisterCallback(int fd
, wxSocketTableType socketType
, wxSocketCallback callback
, void* data
)
96 wxSocketTableEntry
* entry
= FindEntry(fd
);
99 entry
= new wxSocketTableEntry();
103 if (socketType
== wxSocketTableInput
)
105 entry
->m_fdInput
= fd
;
106 entry
->m_dataInput
= data
;
107 entry
->m_callbackInput
= callback
;
111 entry
->m_fdOutput
= fd
;
112 entry
->m_dataOutput
= data
;
113 entry
->m_callbackOutput
= callback
;
117 void wxSocketTable::UnregisterCallback(int fd
, wxSocketTableType socketType
)
119 wxSocketTableEntry
* entry
= FindEntry(fd
);
122 if (socketType
== wxSocketTableInput
)
124 entry
->m_fdInput
= -1;
125 entry
->m_dataInput
= NULL
;
126 entry
->m_callbackInput
= NULL
;
130 entry
->m_fdOutput
= -1;
131 entry
->m_dataOutput
= NULL
;
132 entry
->m_callbackOutput
= NULL
;
134 if (entry
->m_fdInput
== -1 && entry
->m_fdOutput
== -1)
142 bool wxSocketTable::CallCallback(int fd
, wxSocketTableType socketType
)
144 wxSocketTableEntry
* entry
= FindEntry(fd
);
147 if (socketType
== wxSocketTableInput
)
149 if (entry
->m_fdInput
!= -1 && entry
->m_callbackInput
)
151 (entry
->m_callbackInput
) (entry
->m_fdInput
, entry
->m_dataInput
);
156 if (entry
->m_fdOutput
!= -1 && entry
->m_callbackOutput
)
158 (entry
->m_callbackOutput
) (entry
->m_fdOutput
, entry
->m_dataOutput
);
167 void wxSocketTable::FillSets(fd_set
* readset
, fd_set
* writeset
, int* highest
)
170 wxHashTable::compatibility_iterator node
= Next();
173 wxSocketTableEntry
* entry
= (wxSocketTableEntry
*) node
->GetData();
175 if (entry
->m_fdInput
!= -1)
177 FD_SET(entry
->m_fdInput
, readset
);
178 if (entry
->m_fdInput
> *highest
)
179 * highest
= entry
->m_fdInput
;
182 if (entry
->m_fdOutput
!= -1)
184 FD_SET(entry
->m_fdOutput
, writeset
);
185 if (entry
->m_fdOutput
> *highest
)
186 * highest
= entry
->m_fdOutput
;
193 void wxSocketTable::ProcessEvents(fd_set
* readset
, fd_set
* writeset
)
196 wxHashTable::compatibility_iterator node
= Next();
199 wxSocketTableEntry
* entry
= (wxSocketTableEntry
*) node
->GetData();
201 if (entry
->m_fdInput
!= -1 && FD_ISSET(entry
->m_fdInput
, readset
))
203 (entry
->m_callbackInput
) (entry
->m_fdInput
, entry
->m_dataInput
);
206 if (entry
->m_fdOutput
!= -1 && FD_ISSET(entry
->m_fdOutput
, writeset
))
208 (entry
->m_callbackOutput
) (entry
->m_fdOutput
, entry
->m_dataOutput
);
215 wxSocketTable
* wxTheSocketTable
= NULL
;
217 class wxSocketTableModule
: public wxModule
219 DECLARE_DYNAMIC_CLASS(wxSocketTableModule
)
221 wxSocketTableModule() {}
222 bool OnInit() { wxTheSocketTable
= new wxSocketTable
; return TRUE
; };
223 void OnExit() { delete wxTheSocketTable
; wxTheSocketTable
= NULL
; };
226 IMPLEMENT_DYNAMIC_CLASS(wxSocketTableModule
, wxModule
)
228 // Implement registration functions as C functions so they
229 // can be called from gsock11.c
231 extern "C" void wxRegisterSocketCallback(int fd
, wxSocketTableType socketType
, wxSocketCallback callback
, void* data
)
233 if (wxTheSocketTable
)
235 wxTheSocketTable
->RegisterCallback(fd
, socketType
, callback
, data
);
239 extern "C" void wxUnregisterSocketCallback(int fd
, wxSocketTableType socketType
)
241 if (wxTheSocketTable
)
243 wxTheSocketTable
->UnregisterCallback(fd
, socketType
);
248 // ----------------------------------------------------------------------------
250 // ----------------------------------------------------------------------------
252 class WXDLLEXPORT wxEventLoopImpl
256 wxEventLoopImpl() { SetExitCode(0); m_keepGoing
= FALSE
; }
258 // process an XEvent, return TRUE if it was processed
259 bool ProcessEvent(XEvent
* event
);
261 // generate an idle message, return TRUE if more idle time requested
262 bool SendIdleEvent();
264 // set/get the exit code
265 void SetExitCode(int exitcode
) { m_exitcode
= exitcode
; }
266 int GetExitCode() const { return m_exitcode
; }
269 // preprocess an event, return TRUE if processed (i.e. no further
270 // dispatching required)
271 bool PreProcessEvent(XEvent
* event
);
273 // the exit code of the event loop
279 // ============================================================================
280 // wxEventLoopImpl implementation
281 // ============================================================================
283 // ----------------------------------------------------------------------------
284 // wxEventLoopImpl message processing
285 // ----------------------------------------------------------------------------
287 bool wxEventLoopImpl::ProcessEvent(XEvent
*event
)
289 // give us the chance to preprocess the message first
290 if ( PreProcessEvent(event
) )
293 // if it wasn't done, dispatch it to the corresponding window
295 return wxTheApp
->ProcessXEvent((WXEvent
*) event
);
300 bool wxEventLoopImpl::PreProcessEvent(XEvent
*event
)
304 HWND hWnd
= msg
->hwnd
;
305 wxWindow
*wndThis
= wxGetWindowFromHWND((WXHWND
)hWnd
);
308 // try translations first; find the youngest window with a translation
311 for ( wnd
= wndThis
; wnd
; wnd
= wnd
->GetParent() )
313 if ( wnd
->MSWTranslateMessage((WXMSG
*)msg
) )
317 // Anyone for a non-translation message? Try youngest descendants first.
318 for ( wnd
= wndThis
; wnd
; wnd
= wnd
->GetParent() )
320 if ( wnd
->MSWProcessMessage((WXMSG
*)msg
) )
328 // ----------------------------------------------------------------------------
329 // wxEventLoopImpl idle event processing
330 // ----------------------------------------------------------------------------
332 bool wxEventLoopImpl::SendIdleEvent()
334 return wxTheApp
->ProcessIdle();
337 // ============================================================================
338 // wxEventLoop implementation
339 // ============================================================================
341 wxEventLoop
*wxEventLoopBase::ms_activeLoop
= NULL
;
343 // ----------------------------------------------------------------------------
344 // wxEventLoop running and exiting
345 // ----------------------------------------------------------------------------
347 wxEventLoop::~wxEventLoop()
349 wxASSERT_MSG( !m_impl
, _T("should have been deleted in Run()") );
352 int wxEventLoop::Run()
354 // event loops are not recursive, you need to create another loop!
355 wxCHECK_MSG( !IsRunning(), -1, _T("can't reenter a message loop") );
357 m_impl
= new wxEventLoopImpl
;
359 wxEventLoop
*oldLoop
= ms_activeLoop
;
360 ms_activeLoop
= this;
362 m_impl
->m_keepGoing
= TRUE
;
363 while ( m_impl
->m_keepGoing
)
365 #if 0 // wxUSE_THREADS
366 wxMutexGuiLeaveOrEnter();
367 #endif // wxUSE_THREADS
369 // generate and process idle events for as long as we don't have
370 // anything else to do
371 while ( ! Pending() )
374 wxTimer::NotifyTimers(); // TODO: is this the correct place for it?
376 if (!m_impl
->SendIdleEvent())
378 #if 0 // wxUSE_THREADS
379 // leave the main loop to give other threads a chance to
380 // perform their GUI work
385 // Break out of while loop
390 // a message came or no more idle processing to do, sit in Dispatch()
391 // waiting for the next message
398 int exitcode
= m_impl
->GetExitCode();
402 ms_activeLoop
= oldLoop
;
407 void wxEventLoop::Exit(int rc
)
409 wxCHECK_RET( IsRunning(), _T("can't call Exit() if not running") );
411 m_impl
->SetExitCode(rc
);
412 m_impl
->m_keepGoing
= FALSE
;
415 // ----------------------------------------------------------------------------
416 // wxEventLoop message processing dispatching
417 // ----------------------------------------------------------------------------
419 bool wxEventLoop::Pending() const
421 XFlush( wxGlobalDisplay() );
422 return (XPending( wxGlobalDisplay() ) > 0);
425 bool wxEventLoop::Dispatch()
429 // TODO allowing for threads, as per e.g. wxMSW
431 // This now waits until either an X event is received,
432 // or the select times out. So we should now process
433 // wxTimers in a reasonably timely fashion. However it
434 // does also mean that idle processing will happen more
435 // often, so we should probably limit idle processing to
436 // not be repeated more than every N milliseconds.
438 if (XPending( wxGlobalDisplay() ) == 0)
441 GR_TIMEOUT timeout
= 10; // Milliseconds
442 // Wait for next event, or timeout
443 GrGetNextEventTimeout(& event
, timeout
);
445 // Fall through to ProcessEvent.
446 // we'll assume that ProcessEvent will just ignore
447 // the event if there was a timeout and no event.
452 tv
.tv_usec
=10000; // TODO make this configurable
453 int fd
= ConnectionNumber( wxGlobalDisplay() );
461 FD_SET(fd
, &readset
);
464 if (wxTheSocketTable
)
465 wxTheSocketTable
->FillSets( &readset
, &writeset
, &highest
);
468 if (select( highest
+1, &readset
, &writeset
, NULL
, &tv
) == 0)
470 // Timed out, so no event to process
475 // An X11 event was pending, so get it
476 if (FD_ISSET( fd
, &readset
))
477 XNextEvent( wxGlobalDisplay(), &event
);
480 // Check if any socket events were pending,
481 // and if so, call their callbacks
482 if (wxTheSocketTable
)
483 wxTheSocketTable
->ProcessEvents( &readset
, &writeset
);
490 XNextEvent( wxGlobalDisplay(), &event
);
494 (void) m_impl
->ProcessEvent( &event
);