]> git.saurik.com Git - wxWidgets.git/blob - src/x11/evtloop.cpp
Fixed #1338966.
[wxWidgets.git] / src / x11 / evtloop.cpp
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
9 // License: wxWindows licence
10 ///////////////////////////////////////////////////////////////////////////////
11
12 // ============================================================================
13 // declarations
14 // ============================================================================
15
16 // ----------------------------------------------------------------------------
17 // headers
18 // ----------------------------------------------------------------------------
19
20 #include "wx/window.h"
21 #include "wx/app.h"
22 #include "wx/evtloop.h"
23 #include "wx/tooltip.h"
24 #if wxUSE_THREADS
25 #include "wx/thread.h"
26 #endif
27 #include "wx/timer.h"
28 #include "wx/hash.h"
29 #include "wx/module.h"
30 #include "wx/unix/private.h"
31 #include "wx/x11/private.h"
32 #include "X11/Xlib.h"
33
34 #include <sys/time.h>
35 #include <unistd.h>
36
37 #if wxUSE_SOCKETS
38 // ----------------------------------------------------------------------------
39 // wxSocketTable
40 // ----------------------------------------------------------------------------
41
42 typedef void (*wxSocketCallback) (int fd, void* data);
43
44 class 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 }
53
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
62 typedef enum
63 { wxSocketTableInput, wxSocketTableOutput } wxSocketTableType ;
64
65 class wxSocketTable: public wxHashTable
66 {
67 public:
68 wxSocketTable(): wxHashTable(wxKEY_INTEGER)
69 {
70 }
71 ~wxSocketTable()
72 {
73 WX_CLEAR_HASH_TABLE(*this)
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
89 wxSocketTableEntry* wxSocketTable::FindEntry(int fd)
90 {
91 wxSocketTableEntry* entry = (wxSocketTableEntry*) Get(fd);
92 return entry;
93 }
94
95 void 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
118 void 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
143 bool 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
168 void wxSocketTable::FillSets(fd_set* readset, fd_set* writeset, int* highest)
169 {
170 BeginFind();
171 wxHashTable::compatibility_iterator node = Next();
172 while (node)
173 {
174 wxSocketTableEntry* entry = (wxSocketTableEntry*) node->GetData();
175
176 if (entry->m_fdInput != -1)
177 {
178 wxFD_SET(entry->m_fdInput, readset);
179 if (entry->m_fdInput > *highest)
180 * highest = entry->m_fdInput;
181 }
182
183 if (entry->m_fdOutput != -1)
184 {
185 wxFD_SET(entry->m_fdOutput, writeset);
186 if (entry->m_fdOutput > *highest)
187 * highest = entry->m_fdOutput;
188 }
189
190 node = Next();
191 }
192 }
193
194 void wxSocketTable::ProcessEvents(fd_set* readset, fd_set* writeset)
195 {
196 BeginFind();
197 wxHashTable::compatibility_iterator node = Next();
198 while (node)
199 {
200 wxSocketTableEntry* entry = (wxSocketTableEntry*) node->GetData();
201
202 if (entry->m_fdInput != -1 && wxFD_ISSET(entry->m_fdInput, readset))
203 {
204 (entry->m_callbackInput) (entry->m_fdInput, entry->m_dataInput);
205 }
206
207 if (entry->m_fdOutput != -1 && wxFD_ISSET(entry->m_fdOutput, writeset))
208 {
209 (entry->m_callbackOutput) (entry->m_fdOutput, entry->m_dataOutput);
210 }
211
212 node = Next();
213 }
214 }
215
216 wxSocketTable* wxTheSocketTable = NULL;
217
218 class wxSocketTableModule: public wxModule
219 {
220 DECLARE_DYNAMIC_CLASS(wxSocketTableModule)
221 public:
222 wxSocketTableModule() {}
223 bool OnInit() { wxTheSocketTable = new wxSocketTable; return TRUE; };
224 void OnExit() { delete wxTheSocketTable; wxTheSocketTable = NULL; };
225 };
226
227 IMPLEMENT_DYNAMIC_CLASS(wxSocketTableModule, wxModule)
228
229 // Implement registration functions as C functions so they
230 // can be called from gsock11.c
231
232 extern "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
240 extern "C" void wxUnregisterSocketCallback(int fd, wxSocketTableType socketType)
241 {
242 if (wxTheSocketTable)
243 {
244 wxTheSocketTable->UnregisterCallback(fd, socketType);
245 }
246 }
247 #endif
248
249 // ----------------------------------------------------------------------------
250 // wxEventLoopImpl
251 // ----------------------------------------------------------------------------
252
253 class WXDLLEXPORT wxEventLoopImpl
254 {
255 public:
256 // ctor
257 wxEventLoopImpl() { SetExitCode(0); m_keepGoing = FALSE; }
258
259 // process an XEvent, return TRUE if it was processed
260 bool ProcessEvent(XEvent* event);
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
269 public:
270 // preprocess an event, return TRUE if processed (i.e. no further
271 // dispatching required)
272 bool PreProcessEvent(XEvent* event);
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
288 bool wxEventLoopImpl::ProcessEvent(XEvent *event)
289 {
290 // give us the chance to preprocess the message first
291 if ( PreProcessEvent(event) )
292 return TRUE;
293
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;
299 }
300
301 bool 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
333 bool wxEventLoopImpl::SendIdleEvent()
334 {
335 return wxTheApp->ProcessIdle();
336 }
337
338 // ============================================================================
339 // wxEventLoop implementation
340 // ============================================================================
341
342 wxEventLoop *wxEventLoopBase::ms_activeLoop = NULL;
343
344 // ----------------------------------------------------------------------------
345 // wxEventLoop running and exiting
346 // ----------------------------------------------------------------------------
347
348 wxEventLoop::~wxEventLoop()
349 {
350 wxASSERT_MSG( !m_impl, _T("should have been deleted in Run()") );
351 }
352
353 int wxEventLoop::Run()
354 {
355 // event loops are not recursive, you need to create another loop!
356 wxCHECK_MSG( !IsRunning(), -1, _T("can't reenter a message loop") );
357
358 m_impl = new wxEventLoopImpl;
359
360 wxEventLoop *oldLoop = ms_activeLoop;
361 ms_activeLoop = this;
362
363 m_impl->m_keepGoing = TRUE;
364 while ( m_impl->m_keepGoing )
365 {
366 #if 0 // wxUSE_THREADS
367 wxMutexGuiLeaveOrEnter();
368 #endif // wxUSE_THREADS
369
370 // generate and process idle events for as long as we don't have
371 // anything else to do
372 while ( ! Pending() )
373 {
374 #if wxUSE_TIMER
375 wxTimer::NotifyTimers(); // TODO: is this the correct place for it?
376 #endif
377 if (!m_impl->SendIdleEvent())
378 {
379 #if 0 // wxUSE_THREADS
380 // leave the main loop to give other threads a chance to
381 // perform their GUI work
382 wxMutexGuiLeave();
383 wxUsleep(20);
384 wxMutexGuiEnter();
385 #endif
386 // Break out of while loop
387 break;
388 }
389 }
390
391 // a message came or no more idle processing to do, sit in Dispatch()
392 // waiting for the next message
393 if ( !Dispatch() )
394 {
395 break;
396 }
397 }
398
399 int exitcode = m_impl->GetExitCode();
400 delete m_impl;
401 m_impl = NULL;
402
403 ms_activeLoop = oldLoop;
404
405 return exitcode;
406 }
407
408 void wxEventLoop::Exit(int rc)
409 {
410 wxCHECK_RET( IsRunning(), _T("can't call Exit() if not running") );
411
412 m_impl->SetExitCode(rc);
413 m_impl->m_keepGoing = FALSE;
414 }
415
416 // ----------------------------------------------------------------------------
417 // wxEventLoop message processing dispatching
418 // ----------------------------------------------------------------------------
419
420 bool wxEventLoop::Pending() const
421 {
422 XFlush( wxGlobalDisplay() );
423 return (XPending( wxGlobalDisplay() ) > 0);
424 }
425
426 bool wxEventLoop::Dispatch()
427 {
428 XEvent event;
429
430 // TODO allowing for threads, as per e.g. wxMSW
431
432 // This now waits until either an X event is received,
433 // or the select times out. So we should now process
434 // wxTimers in a reasonably timely fashion. However it
435 // does also mean that idle processing will happen more
436 // often, so we should probably limit idle processing to
437 // not be repeated more than every N milliseconds.
438
439 if (XPending( wxGlobalDisplay() ) == 0)
440 {
441 #if wxUSE_NANOX
442 GR_TIMEOUT timeout = 10; // Milliseconds
443 // Wait for next event, or timeout
444 GrGetNextEventTimeout(& event, timeout);
445
446 // Fall through to ProcessEvent.
447 // we'll assume that ProcessEvent will just ignore
448 // the event if there was a timeout and no event.
449
450 #else
451 struct timeval tv;
452 tv.tv_sec=0;
453 tv.tv_usec=10000; // TODO make this configurable
454 int fd = ConnectionNumber( wxGlobalDisplay() );
455
456 fd_set readset;
457 fd_set writeset;
458 int highest = fd;
459 wxFD_ZERO(&readset);
460 wxFD_ZERO(&writeset);
461
462 wxFD_SET(fd, &readset);
463
464 #if wxUSE_SOCKETS
465 if (wxTheSocketTable)
466 wxTheSocketTable->FillSets( &readset, &writeset, &highest );
467 #endif
468
469 if (select( highest+1, &readset, &writeset, NULL, &tv ) == 0)
470 {
471 // Timed out, so no event to process
472 return TRUE;
473 }
474 else
475 {
476 // An X11 event was pending, so get it
477 if (wxFD_ISSET( fd, &readset ))
478 XNextEvent( wxGlobalDisplay(), &event );
479
480 #if wxUSE_SOCKETS
481 // Check if any socket events were pending,
482 // and if so, call their callbacks
483 if (wxTheSocketTable)
484 wxTheSocketTable->ProcessEvents( &readset, &writeset );
485 #endif
486 }
487 #endif
488 }
489 else
490 {
491 XNextEvent( wxGlobalDisplay(), &event );
492 }
493
494
495 (void) m_impl->ProcessEvent( &event );
496 return TRUE;
497 }
498