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