]> git.saurik.com Git - wxWidgets.git/blame - src/x11/evtloop.cpp
deal with the situation when a (dynamic) event handler disconnects itself during...
[wxWidgets.git] / src / x11 / evtloop.cpp
CommitLineData
1b0fb34b
JS
1///////////////////////////////////////////////////////////////////////////////
2// Name: 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$
8// Copyright: (c) 2002 Julian Smart
65571936 9// License: wxWindows licence
1b0fb34b
JS
10///////////////////////////////////////////////////////////////////////////////
11
12// ============================================================================
13// declarations
14// ============================================================================
15
16// ----------------------------------------------------------------------------
17// headers
18// ----------------------------------------------------------------------------
19
1b0fb34b
JS
20#include "wx/window.h"
21#include "wx/app.h"
22#include "wx/evtloop.h"
23#include "wx/tooltip.h"
7266b672
JS
24#if wxUSE_THREADS
25#include "wx/thread.h"
26#endif
b513212d 27#include "wx/timer.h"
52127426
JS
28#include "wx/hash.h"
29#include "wx/module.h"
17a1ebd1 30#include "wx/unix/private.h"
1b0fb34b
JS
31#include "wx/x11/private.h"
32#include "X11/Xlib.h"
33
1016f0de
JS
34#include <sys/time.h>
35#include <unistd.h>
36
2d1084ea 37#if wxUSE_SOCKETS
52127426
JS
38// ----------------------------------------------------------------------------
39// wxSocketTable
40// ----------------------------------------------------------------------------
41
42typedef void (*wxSocketCallback) (int fd, void* data);
43
44class wxSocketTableEntry: public wxObject
45{
46 public:
47 wxSocketTableEntry()
48 {
49 m_fdInput = -1; m_fdOutput = -1;
50 m_callbackInput = NULL; m_callbackOutput = NULL;
51 m_dataInput = NULL; m_dataOutput = NULL;
52 }
3754265e 53
52127426
JS
54 int m_fdInput;
55 int m_fdOutput;
56 wxSocketCallback m_callbackInput;
57 wxSocketCallback m_callbackOutput;
58 void* m_dataInput;
59 void* m_dataOutput;
60};
61
62typedef enum
63{ wxSocketTableInput, wxSocketTableOutput } wxSocketTableType ;
64
65class wxSocketTable: public wxHashTable
66{
67 public:
68 wxSocketTable(): wxHashTable(wxKEY_INTEGER)
69 {
70 }
71 ~wxSocketTable()
72 {
ac32ba44 73 WX_CLEAR_HASH_TABLE(*this)
52127426
JS
74 }
75
76 wxSocketTableEntry* FindEntry(int fd);
77
78 void RegisterCallback(int fd, wxSocketTableType socketType, wxSocketCallback callback, void* data);
79
80 void UnregisterCallback(int fd, wxSocketTableType socketType);
81
82 bool CallCallback(int fd, wxSocketTableType socketType);
83
84 void FillSets(fd_set* readset, fd_set* writeset, int* highest);
85
86 void ProcessEvents(fd_set* readset, fd_set* writeset);
87};
88
89wxSocketTableEntry* wxSocketTable::FindEntry(int fd)
90{
91 wxSocketTableEntry* entry = (wxSocketTableEntry*) Get(fd);
92 return entry;
93}
94
95void wxSocketTable::RegisterCallback(int fd, wxSocketTableType socketType, wxSocketCallback callback, void* data)
96{
97 wxSocketTableEntry* entry = FindEntry(fd);
98 if (!entry)
99 {
100 entry = new wxSocketTableEntry();
101 Put(fd, entry);
102 }
103
104 if (socketType == wxSocketTableInput)
105 {
106 entry->m_fdInput = fd;
107 entry->m_dataInput = data;
108 entry->m_callbackInput = callback;
109 }
110 else
111 {
112 entry->m_fdOutput = fd;
113 entry->m_dataOutput = data;
114 entry->m_callbackOutput = callback;
115 }
116}
117
118void wxSocketTable::UnregisterCallback(int fd, wxSocketTableType socketType)
119{
120 wxSocketTableEntry* entry = FindEntry(fd);
121 if (entry)
122 {
123 if (socketType == wxSocketTableInput)
124 {
125 entry->m_fdInput = -1;
126 entry->m_dataInput = NULL;
127 entry->m_callbackInput = NULL;
128 }
129 else
130 {
131 entry->m_fdOutput = -1;
132 entry->m_dataOutput = NULL;
133 entry->m_callbackOutput = NULL;
134 }
135 if (entry->m_fdInput == -1 && entry->m_fdOutput == -1)
136 {
137 Delete(fd);
138 delete entry;
139 }
140 }
141}
142
143bool wxSocketTable::CallCallback(int fd, wxSocketTableType socketType)
144{
145 wxSocketTableEntry* entry = FindEntry(fd);
146 if (entry)
147 {
148 if (socketType == wxSocketTableInput)
149 {
150 if (entry->m_fdInput != -1 && entry->m_callbackInput)
151 {
152 (entry->m_callbackInput) (entry->m_fdInput, entry->m_dataInput);
153 }
154 }
155 else
156 {
157 if (entry->m_fdOutput != -1 && entry->m_callbackOutput)
158 {
159 (entry->m_callbackOutput) (entry->m_fdOutput, entry->m_dataOutput);
160 }
161 }
162 return TRUE;
163 }
164 else
165 return FALSE;
166}
167
168void wxSocketTable::FillSets(fd_set* readset, fd_set* writeset, int* highest)
169{
170 BeginFind();
ac32ba44 171 wxHashTable::compatibility_iterator node = Next();
52127426
JS
172 while (node)
173 {
09a1dffa 174 wxSocketTableEntry* entry = (wxSocketTableEntry*) node->GetData();
3754265e 175
52127426
JS
176 if (entry->m_fdInput != -1)
177 {
17a1ebd1 178 wxFD_SET(entry->m_fdInput, readset);
52127426
JS
179 if (entry->m_fdInput > *highest)
180 * highest = entry->m_fdInput;
181 }
182
183 if (entry->m_fdOutput != -1)
184 {
17a1ebd1 185 wxFD_SET(entry->m_fdOutput, writeset);
52127426
JS
186 if (entry->m_fdOutput > *highest)
187 * highest = entry->m_fdOutput;
188 }
189
190 node = Next();
191 }
192}
193
194void wxSocketTable::ProcessEvents(fd_set* readset, fd_set* writeset)
195{
196 BeginFind();
ac32ba44 197 wxHashTable::compatibility_iterator node = Next();
52127426
JS
198 while (node)
199 {
09a1dffa 200 wxSocketTableEntry* entry = (wxSocketTableEntry*) node->GetData();
3754265e 201
17a1ebd1 202 if (entry->m_fdInput != -1 && wxFD_ISSET(entry->m_fdInput, readset))
52127426
JS
203 {
204 (entry->m_callbackInput) (entry->m_fdInput, entry->m_dataInput);
205 }
206
17a1ebd1 207 if (entry->m_fdOutput != -1 && wxFD_ISSET(entry->m_fdOutput, writeset))
52127426
JS
208 {
209 (entry->m_callbackOutput) (entry->m_fdOutput, entry->m_dataOutput);
210 }
211
212 node = Next();
213 }
214}
215
216wxSocketTable* wxTheSocketTable = NULL;
217
218class wxSocketTableModule: public wxModule
219{
220DECLARE_DYNAMIC_CLASS(wxSocketTableModule)
221public:
222 wxSocketTableModule() {}
223 bool OnInit() { wxTheSocketTable = new wxSocketTable; return TRUE; };
224 void OnExit() { delete wxTheSocketTable; wxTheSocketTable = NULL; };
225};
226
227IMPLEMENT_DYNAMIC_CLASS(wxSocketTableModule, wxModule)
228
229// Implement registration functions as C functions so they
230// can be called from gsock11.c
231
232extern "C" void wxRegisterSocketCallback(int fd, wxSocketTableType socketType, wxSocketCallback callback, void* data)
233{
234 if (wxTheSocketTable)
235 {
236 wxTheSocketTable->RegisterCallback(fd, socketType, callback, data);
237 }
238}
239
240extern "C" void wxUnregisterSocketCallback(int fd, wxSocketTableType socketType)
241{
242 if (wxTheSocketTable)
243 {
244 wxTheSocketTable->UnregisterCallback(fd, socketType);
245 }
246}
2d1084ea 247#endif
52127426 248
1b0fb34b
JS
249// ----------------------------------------------------------------------------
250// wxEventLoopImpl
251// ----------------------------------------------------------------------------
252
253class WXDLLEXPORT wxEventLoopImpl
254{
255public:
256 // ctor
257 wxEventLoopImpl() { SetExitCode(0); m_keepGoing = FALSE; }
258
086fd560
JS
259 // process an XEvent, return TRUE if it was processed
260 bool ProcessEvent(XEvent* event);
1b0fb34b
JS
261
262 // generate an idle message, return TRUE if more idle time requested
263 bool SendIdleEvent();
264
265 // set/get the exit code
266 void SetExitCode(int exitcode) { m_exitcode = exitcode; }
267 int GetExitCode() const { return m_exitcode; }
268
269public:
270 // preprocess an event, return TRUE if processed (i.e. no further
271 // dispatching required)
7266b672 272 bool PreProcessEvent(XEvent* event);
1b0fb34b
JS
273
274 // the exit code of the event loop
275 int m_exitcode;
276
277 bool m_keepGoing;
278};
279
280// ============================================================================
281// wxEventLoopImpl implementation
282// ============================================================================
283
284// ----------------------------------------------------------------------------
285// wxEventLoopImpl message processing
286// ----------------------------------------------------------------------------
287
086fd560 288bool wxEventLoopImpl::ProcessEvent(XEvent *event)
1b0fb34b
JS
289{
290 // give us the chance to preprocess the message first
086fd560
JS
291 if ( PreProcessEvent(event) )
292 return TRUE;
3754265e 293
086fd560
JS
294 // if it wasn't done, dispatch it to the corresponding window
295 if (wxTheApp)
296 return wxTheApp->ProcessXEvent((WXEvent*) event);
297
298 return FALSE;
1b0fb34b
JS
299}
300
301bool wxEventLoopImpl::PreProcessEvent(XEvent *event)
302{
303 // TODO
304#if 0
305 HWND hWnd = msg->hwnd;
306 wxWindow *wndThis = wxGetWindowFromHWND((WXHWND)hWnd);
307
308
309 // try translations first; find the youngest window with a translation
310 // table.
311 wxWindow *wnd;
312 for ( wnd = wndThis; wnd; wnd = wnd->GetParent() )
313 {
314 if ( wnd->MSWTranslateMessage((WXMSG *)msg) )
315 return TRUE;
316 }
317
318 // Anyone for a non-translation message? Try youngest descendants first.
319 for ( wnd = wndThis; wnd; wnd = wnd->GetParent() )
320 {
321 if ( wnd->MSWProcessMessage((WXMSG *)msg) )
322 return TRUE;
323 }
324#endif
325
326 return FALSE;
327}
328
329// ----------------------------------------------------------------------------
330// wxEventLoopImpl idle event processing
331// ----------------------------------------------------------------------------
332
333bool wxEventLoopImpl::SendIdleEvent()
334{
e39af974 335 return wxTheApp->ProcessIdle();
1b0fb34b
JS
336}
337
338// ============================================================================
339// wxEventLoop implementation
340// ============================================================================
341
1b0fb34b
JS
342// ----------------------------------------------------------------------------
343// wxEventLoop running and exiting
344// ----------------------------------------------------------------------------
345
346wxEventLoop::~wxEventLoop()
347{
348 wxASSERT_MSG( !m_impl, _T("should have been deleted in Run()") );
349}
350
1b0fb34b
JS
351int wxEventLoop::Run()
352{
353 // event loops are not recursive, you need to create another loop!
354 wxCHECK_MSG( !IsRunning(), -1, _T("can't reenter a message loop") );
355
356 m_impl = new wxEventLoopImpl;
3754265e 357
77fb1a02 358 wxEventLoopActivator activate(this);
1b0fb34b
JS
359
360 m_impl->m_keepGoing = TRUE;
361 while ( m_impl->m_keepGoing )
362 {
7266b672 363#if 0 // wxUSE_THREADS
2b5f62a0 364 wxMutexGuiLeaveOrEnter();
1b0fb34b
JS
365#endif // wxUSE_THREADS
366
367 // generate and process idle events for as long as we don't have
368 // anything else to do
369 while ( ! Pending() )
370 {
b555c37c 371#if wxUSE_TIMER
b513212d 372 wxTimer::NotifyTimers(); // TODO: is this the correct place for it?
b555c37c 373#endif
1b0fb34b
JS
374 if (!m_impl->SendIdleEvent())
375 {
2b5f62a0 376#if 0 // wxUSE_THREADS
1b0fb34b
JS
377 // leave the main loop to give other threads a chance to
378 // perform their GUI work
379 wxMutexGuiLeave();
380 wxUsleep(20);
381 wxMutexGuiEnter();
382#endif
383 // Break out of while loop
384 break;
385 }
386 }
387
388 // a message came or no more idle processing to do, sit in Dispatch()
389 // waiting for the next message
390 if ( !Dispatch() )
391 {
392 break;
393 }
394 }
395
396 int exitcode = m_impl->GetExitCode();
397 delete m_impl;
398 m_impl = NULL;
399
1b0fb34b
JS
400 return exitcode;
401}
402
403void wxEventLoop::Exit(int rc)
404{
405 wxCHECK_RET( IsRunning(), _T("can't call Exit() if not running") );
406
407 m_impl->SetExitCode(rc);
408 m_impl->m_keepGoing = FALSE;
409}
410
411// ----------------------------------------------------------------------------
412// wxEventLoop message processing dispatching
413// ----------------------------------------------------------------------------
414
415bool wxEventLoop::Pending() const
416{
2b5f62a0
VZ
417 XFlush( wxGlobalDisplay() );
418 return (XPending( wxGlobalDisplay() ) > 0);
1b0fb34b
JS
419}
420
421bool wxEventLoop::Dispatch()
422{
423 XEvent event;
424
425 // TODO allowing for threads, as per e.g. wxMSW
426
1016f0de
JS
427 // This now waits until either an X event is received,
428 // or the select times out. So we should now process
429 // wxTimers in a reasonably timely fashion. However it
430 // does also mean that idle processing will happen more
431 // often, so we should probably limit idle processing to
432 // not be repeated more than every N milliseconds.
3754265e 433
2b5f62a0 434 if (XPending( wxGlobalDisplay() ) == 0)
1016f0de 435 {
868741e9
JS
436#if wxUSE_NANOX
437 GR_TIMEOUT timeout = 10; // Milliseconds
438 // Wait for next event, or timeout
439 GrGetNextEventTimeout(& event, timeout);
440
441 // Fall through to ProcessEvent.
442 // we'll assume that ProcessEvent will just ignore
443 // the event if there was a timeout and no event.
3754265e 444
868741e9 445#else
1016f0de
JS
446 struct timeval tv;
447 tv.tv_sec=0;
448 tv.tv_usec=10000; // TODO make this configurable
2b5f62a0 449 int fd = ConnectionNumber( wxGlobalDisplay() );
3754265e 450
1016f0de 451 fd_set readset;
52127426
JS
452 fd_set writeset;
453 int highest = fd;
17a1ebd1
VZ
454 wxFD_ZERO(&readset);
455 wxFD_ZERO(&writeset);
3754265e 456
17a1ebd1 457 wxFD_SET(fd, &readset);
52127426 458
2d1084ea 459#if wxUSE_SOCKETS
52127426 460 if (wxTheSocketTable)
2b5f62a0 461 wxTheSocketTable->FillSets( &readset, &writeset, &highest );
2d1084ea 462#endif
3754265e 463
2b5f62a0 464 if (select( highest+1, &readset, &writeset, NULL, &tv ) == 0)
1016f0de
JS
465 {
466 // Timed out, so no event to process
467 return TRUE;
468 }
469 else
470 {
52127426 471 // An X11 event was pending, so get it
17a1ebd1 472 if (wxFD_ISSET( fd, &readset ))
2b5f62a0 473 XNextEvent( wxGlobalDisplay(), &event );
52127426 474
2d1084ea 475#if wxUSE_SOCKETS
52127426
JS
476 // Check if any socket events were pending,
477 // and if so, call their callbacks
478 if (wxTheSocketTable)
2b5f62a0 479 wxTheSocketTable->ProcessEvents( &readset, &writeset );
2d1084ea 480#endif
1016f0de 481 }
868741e9 482#endif
3754265e 483 }
2b5f62a0 484 else
1016f0de 485 {
2b5f62a0 486 XNextEvent( wxGlobalDisplay(), &event );
1016f0de 487 }
3754265e
VZ
488
489
2b5f62a0 490 (void) m_impl->ProcessEvent( &event );
1b0fb34b
JS
491 return TRUE;
492}
493