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