Committing in .
[wxWidgets.git] / src / motif / evtloop.cpp
1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: motif/evtloop.cpp
3 // Purpose: implements wxEventLoop for Motif
4 // Author: Mattia Barbon
5 // Modified by:
6 // Created: 01.11.02
7 // RCS-ID: $Id$
8 // Copyright: (c) 2002 Mattia Barbon
9 // License: wxWindows license
10 ///////////////////////////////////////////////////////////////////////////////
11
12 // ============================================================================
13 // declarations
14 // ============================================================================
15
16 // ----------------------------------------------------------------------------
17 // headers
18 // ----------------------------------------------------------------------------
19
20 #ifdef __GNUG__
21 #pragma implementation "evtloop.h"
22 #endif
23
24 #ifdef __VMS
25 #define XtParent XTPARENT
26 #define XtDisplay XTDISPLAY
27 #endif
28
29 // For compilers that support precompilation, includes "wx.h".
30 #include "wx/wxprec.h"
31
32 #ifndef WX_PRECOMP
33 #endif //WX_PRECOMP
34
35 #include "wx/evtloop.h"
36 #include "wx/event.h"
37 #include "wx/app.h"
38
39 #ifdef __VMS__
40 #pragma message disable nosimpint
41 #endif
42 #include <Xm/Xm.h>
43 #include <X11/Xlib.h>
44 #ifdef __VMS__
45 #pragma message enable nosimpint
46 #endif
47
48 #include "wx/motif/private.h"
49
50 static bool CheckForKeyUp(XEvent* event);
51 static bool CheckForKeyDown(XEvent* event);
52 static bool CheckForAccelerator(XEvent* event);
53 static void ProcessXEvent(XEvent* event);
54 static void wxBreakDispatch();
55
56 // ----------------------------------------------------------------------------
57 // wxEventLoopImpl
58 // ----------------------------------------------------------------------------
59
60 class WXDLLEXPORT wxEventLoopImpl
61 {
62 public:
63 // ctor
64 wxEventLoopImpl() { SetExitCode(0); }
65
66 // set/get the exit code
67 void SetExitCode(int exitcode) { m_exitcode = exitcode; }
68 int GetExitCode() const { return m_exitcode; }
69
70 bool SendIdleMessage();
71 bool GetKeepGoing() const { return m_keepGoing; }
72 void SetKeepGoing(bool keepGoing) { m_keepGoing = keepGoing; }
73 private:
74 // the exit code of the event loop
75 int m_exitcode;
76 bool m_keepGoing;
77 };
78
79 // ----------------------------------------------------------------------------
80 // wxEventLoopImpl idle event processing
81 // ----------------------------------------------------------------------------
82
83 static bool SendIdleMessage()
84 {
85 wxIdleEvent event;
86
87 return wxTheApp->ProcessEvent(event) && event.MoreRequested();
88 }
89
90 bool wxEventLoopImpl::SendIdleMessage()
91 {
92 return ::SendIdleMessage();
93 }
94
95 // ============================================================================
96 // wxEventLoop implementation
97 // ============================================================================
98
99 // ----------------------------------------------------------------------------
100 // wxEventLoop running and exiting
101 // ----------------------------------------------------------------------------
102
103 wxEventLoop *wxEventLoop::ms_activeLoop = NULL;
104
105 wxEventLoop::~wxEventLoop()
106 {
107 wxASSERT_MSG( !m_impl, _T("should have been deleted in Run()") );
108 }
109
110 bool wxEventLoop::IsRunning() const
111 {
112 return m_impl != NULL;
113 }
114
115 int wxEventLoop::Run()
116 {
117 // event loops are not recursive, you need to create another loop!
118 wxCHECK_MSG( !IsRunning(), -1, _T("can't reenter a message loop") );
119
120 wxEventLoop *oldLoop = ms_activeLoop;
121 ms_activeLoop = this;
122
123 m_impl = new wxEventLoopImpl;
124 m_impl->SetKeepGoing( true );
125
126 for( ;; )
127 {
128 if( !wxDoEventLoopIteration( *this ) )
129 break;
130 }
131
132 int exitcode = m_impl->GetExitCode();
133 delete m_impl;
134 m_impl = NULL;
135
136 ms_activeLoop = oldLoop;
137
138 return exitcode;
139 }
140
141 void wxEventLoop::Exit(int rc)
142 {
143 wxCHECK_RET( IsRunning(), _T("can't call Exit() if not running") );
144
145 m_impl->SetExitCode(rc);
146 m_impl->SetKeepGoing( false );
147
148 ::wxBreakDispatch();
149 }
150
151 // ----------------------------------------------------------------------------
152 // wxEventLoop message processing dispatching
153 // ----------------------------------------------------------------------------
154
155 bool wxEventLoop::Pending() const
156 {
157 return XtAppPending( (XtAppContext)wxTheApp->GetAppContext() ) != 0;
158 }
159
160 bool wxEventLoop::Dispatch()
161 {
162 XEvent event;
163 XtAppContext context = (XtAppContext)wxTheApp->GetAppContext();
164
165 if( XtAppPeekEvent( context, &event ) != 0 )
166 {
167 XtAppNextEvent( context, &event );
168 ProcessXEvent( &event );
169 }
170 else
171 #ifdef __VMS
172 XtAppProcessEvent( context, XtIMTimer|XtIMAlternateInput );
173 #else
174 XtAppProcessEvent( context, XtIMTimer|XtIMAlternateInput|XtIMSignal );
175 #endif
176
177 return m_impl ? m_impl->GetKeepGoing() : true;
178 }
179
180 // ----------------------------------------------------------------------------
181 // Static functions (originally in app.cpp)
182 // ----------------------------------------------------------------------------
183
184 void ProcessXEvent(XEvent* event)
185 {
186 if (event->type == KeyPress)
187 {
188 if (CheckForAccelerator(event))
189 {
190 // Do nothing! We intercepted and processed the event as an
191 // accelerator.
192 return;
193 }
194 // It seemed before that this hack was redundant and
195 // key down events were being generated by wxCanvasInputEvent.
196 // But no longer - why ???
197 //
198 else if (CheckForKeyDown(event))
199 {
200 // We intercepted and processed the key down event
201 return;
202 }
203 else
204 {
205 XtDispatchEvent(event);
206 return;
207 }
208 }
209 else if (event->type == KeyRelease)
210 {
211 // TODO: work out why we still need this ! -michael
212 //
213 if (::CheckForKeyUp(event))
214 {
215 // We intercepted and processed the key up event
216 return;
217 }
218 else
219 {
220 XtDispatchEvent(event);
221 return;
222 }
223 }
224 else if (event->type == PropertyNotify)
225 {
226 wxTheApp->HandlePropertyChange(event);
227 return;
228 }
229 else if (event->type == ResizeRequest)
230 {
231 /* Terry Gitnick <terryg@scientech.com> - 1/21/98
232 * If resize event, don't resize until the last resize event for this
233 * window is recieved. Prevents flicker as windows are resized.
234 */
235
236 Display *disp = XtDisplay((Widget) wxTheApp->GetTopLevelWidget());
237 Window win = event->xany.window;
238 XEvent report;
239
240 // to avoid flicker
241 report = * event;
242 while( XCheckTypedWindowEvent (disp, win, ResizeRequest, &report));
243
244 // TODO: when implementing refresh optimization, we can use
245 // XtAddExposureToRegion to expand the window's paint region.
246
247 XtDispatchEvent(event);
248 }
249 else
250 {
251 XtDispatchEvent(event);
252 }
253 }
254
255 // Returns true if an accelerator has been processed
256 bool CheckForAccelerator(XEvent* event)
257 {
258 if (event->xany.type == KeyPress)
259 {
260 // Find a wxWindow for this window
261 // TODO: should get display for the window, not the current display
262 Widget widget = XtWindowToWidget((Display*) wxGetDisplay(),
263 event->xany.window);
264 wxWindow* win = NULL;
265
266 // Find the first wxWindow that corresponds to this event window
267 while (widget && !(win = wxGetWindowFromTable(widget)))
268 widget = XtParent(widget);
269
270 if (!widget || !win)
271 return false;
272
273 wxKeyEvent keyEvent(wxEVT_CHAR);
274 wxTranslateKeyEvent(keyEvent, win, (Widget) 0, event);
275
276 // Now we have a wxKeyEvent and we have a wxWindow.
277 // Go up the hierarchy until we find a matching accelerator,
278 // or we get to the top.
279 while (win)
280 {
281 if (win->ProcessAccelerator(keyEvent))
282 return true;
283 win = win->GetParent();
284 }
285 return false;
286 }
287 return false;
288 }
289
290 // bool wxApp::CheckForKeyDown(WXEvent* event) { wxFAIL; return false; }
291
292 bool CheckForKeyDown(XEvent* event)
293 {
294 if (event->xany.type == KeyPress)
295 {
296 Widget widget = XtWindowToWidget((Display*) wxGetDisplay(),
297 event->xany.window);
298 wxWindow* win = NULL;
299
300 // Find the first wxWindow that corresponds to this event window
301 while (widget && !(win = wxGetWindowFromTable(widget)))
302 widget = XtParent(widget);
303
304 if (!widget || !win)
305 return false;
306
307 wxKeyEvent keyEvent(wxEVT_KEY_DOWN);
308 wxTranslateKeyEvent(keyEvent, win, (Widget) 0, event);
309
310 return win->ProcessEvent( keyEvent );
311 }
312
313 return false;
314 }
315
316 // bool wxApp::CheckForKeyUp(WXEvent* event) { wxFAIL; return false; }
317
318 bool CheckForKeyUp(XEvent* event)
319 {
320 if (event->xany.type == KeyRelease)
321 {
322 Widget widget = XtWindowToWidget((Display*) wxGetDisplay(),
323 event->xany.window);
324 wxWindow* win = NULL;
325
326 // Find the first wxWindow that corresponds to this event window
327 while (widget && !(win = wxGetWindowFromTable(widget)))
328 widget = XtParent(widget);
329
330 if (!widget || !win)
331 return false;
332
333 wxKeyEvent keyEvent(wxEVT_KEY_UP);
334 wxTranslateKeyEvent(keyEvent, win, (Widget) 0, event);
335
336 return win->ProcessEvent( keyEvent );
337 }
338
339 return false;
340 }
341
342 // ----------------------------------------------------------------------------
343 // executes one main loop iteration (declared in include/wx/motif/private.h)
344 // ----------------------------------------------------------------------------
345
346 bool wxDoEventLoopIteration( wxEventLoop& evtLoop )
347 {
348 #if wxUSE_THREADS
349 // leave the main loop to give other threads a chance to
350 // perform their GUI work
351 wxMutexGuiLeave();
352 wxUsleep(20);
353 wxMutexGuiEnter();
354 #endif
355
356 while ( !evtLoop.Pending() && ::SendIdleMessage() )
357 ;
358
359 if( !evtLoop.Dispatch() )
360 return false;
361
362 return true;
363 }
364
365 // ----------------------------------------------------------------------------
366 // ::wxWakeUpIdle implementation
367 // ----------------------------------------------------------------------------
368
369 // As per Vadim's suggestion, we open a pipe, and XtAppAddInputSource it;
370 // writing a single byte to the pipe will cause wxEventLoop::Pending
371 // to return true, and wxEventLoop::Dispatch to dispatch an input handler
372 // that simply removes the byte(s), and to return, which will cause
373 // an idle event to be sent
374
375 // also wxEventLoop::Exit is implemented that way, so that exiting an
376 // event loop won't require an event being in the queue
377
378 #include "wx/module.h"
379
380 #include <sys/types.h>
381 #include <sys/time.h>
382 #include <unistd.h>
383
384 static XtInputId inputId;
385 static int idleFds[2] = { -1, -1 };
386
387 class wxIdlePipeModule : public wxModule
388 {
389 public:
390 wxIdlePipeModule() {};
391
392 virtual bool OnInit()
393 {
394 if( pipe(idleFds) != 0 )
395 return false;
396
397 return true;
398 }
399
400 virtual void OnExit()
401 {
402 close( idleFds[0] );
403 close( idleFds[1] );
404 }
405 private:
406 DECLARE_DYNAMIC_CLASS(wxIdlePipeModule);
407 };
408
409 IMPLEMENT_DYNAMIC_CLASS(wxIdlePipeModule, wxModule);
410
411 static void wxInputCallback( XtPointer, int* fd, XtInputId* )
412 {
413 char buffer[128];
414
415 // wxWakeUpIdle may have been called more than once
416 for(;;)
417 {
418 fd_set in;
419 struct timeval timeout;
420
421 timeout.tv_sec = 0;
422 timeout.tv_usec = 0;
423
424 FD_ZERO( &in );
425 FD_SET( *fd, &in );
426
427 if( select( *fd + 1, &in, NULL, NULL, &timeout ) <= 0 )
428 break;
429 if( read( *fd, buffer, sizeof(buffer) - 1 ) != sizeof(buffer) - 1 )
430 break;
431 }
432 }
433
434 static void wxBreakDispatch()
435 {
436 char dummy;
437
438 // check if wxWakeUpIdle has already been called
439 fd_set in;
440 struct timeval timeout;
441
442 timeout.tv_sec = 0;
443 timeout.tv_usec = 0;
444
445 FD_ZERO( &in );
446 FD_SET( idleFds[0], &in );
447
448 if( select( idleFds[0] + 1, &in, NULL, NULL, &timeout ) > 0 )
449 return;
450
451 // write a single byte
452 write( idleFds[1], &dummy, 1 );
453 }
454
455 void wxWakeUpIdle()
456 {
457 ::wxBreakDispatch();
458 }
459
460 bool wxAddIdleCallback()
461 {
462 // install input handler for wxWakeUpIdle
463 inputId = XtAppAddInput( (XtAppContext) wxTheApp->GetAppContext(),
464 idleFds[0],
465 (XtPointer)XtInputReadMask,
466 wxInputCallback,
467 NULL );
468
469 return true;
470 }
471