]> git.saurik.com Git - wxWidgets.git/blob - src/x11/evtloop.cpp
implementation streamlined
[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 // ----------------------------------------------------------------------------
343 // wxEventLoop running and exiting
344 // ----------------------------------------------------------------------------
345
346 wxEventLoop::~wxEventLoop()
347 {
348 wxASSERT_MSG( !m_impl, _T("should have been deleted in Run()") );
349 }
350
351 int 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;
357
358 wxEventLoopActivator activate(this);
359
360 m_impl->m_keepGoing = TRUE;
361 while ( m_impl->m_keepGoing )
362 {
363 #if 0 // wxUSE_THREADS
364 wxMutexGuiLeaveOrEnter();
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 {
371 #if wxUSE_TIMER
372 wxTimer::NotifyTimers(); // TODO: is this the correct place for it?
373 #endif
374 if (!m_impl->SendIdleEvent())
375 {
376 #if 0 // wxUSE_THREADS
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
400 return exitcode;
401 }
402
403 void 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
415 bool wxEventLoop::Pending() const
416 {
417 XFlush( wxGlobalDisplay() );
418 return (XPending( wxGlobalDisplay() ) > 0);
419 }
420
421 bool wxEventLoop::Dispatch()
422 {
423 XEvent event;
424
425 // TODO allowing for threads, as per e.g. wxMSW
426
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.
433
434 if (XPending( wxGlobalDisplay() ) == 0)
435 {
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.
444
445 #else
446 struct timeval tv;
447 tv.tv_sec=0;
448 tv.tv_usec=10000; // TODO make this configurable
449 int fd = ConnectionNumber( wxGlobalDisplay() );
450
451 fd_set readset;
452 fd_set writeset;
453 int highest = fd;
454 wxFD_ZERO(&readset);
455 wxFD_ZERO(&writeset);
456
457 wxFD_SET(fd, &readset);
458
459 #if wxUSE_SOCKETS
460 if (wxTheSocketTable)
461 wxTheSocketTable->FillSets( &readset, &writeset, &highest );
462 #endif
463
464 if (select( highest+1, &readset, &writeset, NULL, &tv ) == 0)
465 {
466 // Timed out, so no event to process
467 return TRUE;
468 }
469 else
470 {
471 // An X11 event was pending, so get it
472 if (wxFD_ISSET( fd, &readset ))
473 XNextEvent( wxGlobalDisplay(), &event );
474
475 #if wxUSE_SOCKETS
476 // Check if any socket events were pending,
477 // and if so, call their callbacks
478 if (wxTheSocketTable)
479 wxTheSocketTable->ProcessEvents( &readset, &writeset );
480 #endif
481 }
482 #endif
483 }
484 else
485 {
486 XNextEvent( wxGlobalDisplay(), &event );
487 }
488
489
490 (void) m_impl->ProcessEvent( &event );
491 return TRUE;
492 }
493