]> git.saurik.com Git - wxWidgets.git/blob - src/motif/evtloop.cpp
fixing PPI bugs in printer dcs and print preview
[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 #include "wx/event.h"
30 #include "wx/app.h"
31 #include "wx/window.h"
32 #endif //WX_PRECOMP
33
34 #include "wx/evtloop.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::~wxEventLoop()
100 {
101 wxASSERT_MSG( !m_impl, _T("should have been deleted in Run()") );
102 }
103
104 int wxEventLoop::Run()
105 {
106 // event loops are not recursive, you need to create another loop!
107 wxCHECK_MSG( !IsRunning(), -1, _T("can't reenter a message loop") );
108
109 wxEventLoopActivator activate(this);
110
111 m_impl = new wxEventLoopImpl;
112 m_impl->SetKeepGoing( true );
113
114 for( ;; )
115 {
116 if( !wxDoEventLoopIteration( *this ) )
117 break;
118 }
119
120 int exitcode = m_impl->GetExitCode();
121 delete m_impl;
122 m_impl = NULL;
123
124 return exitcode;
125 }
126
127 void wxEventLoop::Exit(int rc)
128 {
129 wxCHECK_RET( IsRunning(), _T("can't call Exit() if not running") );
130
131 m_impl->SetExitCode(rc);
132 m_impl->SetKeepGoing( false );
133
134 ::wxBreakDispatch();
135 }
136
137 // ----------------------------------------------------------------------------
138 // wxEventLoop message processing dispatching
139 // ----------------------------------------------------------------------------
140
141 bool wxEventLoop::Pending() const
142 {
143 return XtAppPending( (XtAppContext)wxTheApp->GetAppContext() ) != 0;
144 }
145
146 bool wxEventLoop::Dispatch()
147 {
148 XEvent event;
149 XtAppContext context = (XtAppContext)wxTheApp->GetAppContext();
150
151 if( XtAppPeekEvent( context, &event ) != 0 )
152 {
153 XtAppNextEvent( context, &event );
154 ProcessXEvent( &event );
155 }
156 else
157 {
158 XtAppProcessEvent( context, XtIMTimer | XtIMAlternateInput
159 #ifdef XtIMSignal
160 | XtIMSignal
161 #endif
162 );
163 }
164
165 return m_impl ? m_impl->GetKeepGoing() : true;
166 }
167
168 // ----------------------------------------------------------------------------
169 // Static functions (originally in app.cpp)
170 // ----------------------------------------------------------------------------
171
172 void ProcessXEvent(XEvent* event)
173 {
174 if (event->type == KeyPress)
175 {
176 if (CheckForAccelerator(event))
177 {
178 // Do nothing! We intercepted and processed the event as an
179 // accelerator.
180 return;
181 }
182 // It seemed before that this hack was redundant and
183 // key down events were being generated by wxCanvasInputEvent.
184 // But no longer - why ???
185 //
186 else if (CheckForKeyDown(event))
187 {
188 // We intercepted and processed the key down event
189 return;
190 }
191 else
192 {
193 XtDispatchEvent(event);
194 return;
195 }
196 }
197 else if (event->type == KeyRelease)
198 {
199 // TODO: work out why we still need this ! -michael
200 //
201 if (::CheckForKeyUp(event))
202 {
203 // We intercepted and processed the key up event
204 return;
205 }
206 else
207 {
208 XtDispatchEvent(event);
209 return;
210 }
211 }
212 else if (event->type == PropertyNotify)
213 {
214 wxTheApp->HandlePropertyChange(event);
215 return;
216 }
217 else if (event->type == ResizeRequest)
218 {
219 /* Terry Gitnick <terryg@scientech.com> - 1/21/98
220 * If resize event, don't resize until the last resize event for this
221 * window is recieved. Prevents flicker as windows are resized.
222 */
223
224 Display *disp = event->xany.display;
225 Window win = event->xany.window;
226 XEvent report;
227
228 // to avoid flicker
229 report = * event;
230 while( XCheckTypedWindowEvent (disp, win, ResizeRequest, &report));
231
232 // TODO: when implementing refresh optimization, we can use
233 // XtAddExposureToRegion to expand the window's paint region.
234
235 XtDispatchEvent(event);
236 }
237 else
238 {
239 XtDispatchEvent(event);
240 }
241 }
242
243 // Returns true if an accelerator has been processed
244 bool CheckForAccelerator(XEvent* event)
245 {
246 if (event->xany.type == KeyPress)
247 {
248 // Find a wxWindow for this window
249 // TODO: should get display for the window, not the current display
250 Widget widget = XtWindowToWidget(event->xany.display,
251 event->xany.window);
252 wxWindow* win = NULL;
253
254 // Find the first wxWindow that corresponds to this event window
255 while (widget && ((win = wxGetWindowFromTable(widget))!=NULL))
256 widget = XtParent(widget);
257
258 if (!widget || !win)
259 return false;
260
261 wxKeyEvent keyEvent(wxEVT_CHAR);
262 wxTranslateKeyEvent(keyEvent, win, (Widget) 0, event);
263
264 // Now we have a wxKeyEvent and we have a wxWindow.
265 // Go up the hierarchy until we find a matching accelerator,
266 // or we get to the top.
267 while (win)
268 {
269 if (win->ProcessAccelerator(keyEvent))
270 return true;
271 win = win->GetParent();
272 }
273 return false;
274 }
275 return false;
276 }
277
278 // bool wxApp::CheckForKeyDown(WXEvent* event) { wxFAIL; return false; }
279
280 bool CheckForKeyDown(XEvent* event)
281 {
282 if (event->xany.type == KeyPress)
283 {
284 Widget widget = XtWindowToWidget(event->xany.display,
285 event->xany.window);
286 wxWindow* win = NULL;
287
288 // Find the first wxWindow that corresponds to this event window
289 while (widget && ((win = wxGetWindowFromTable(widget))!=NULL))
290 widget = XtParent(widget);
291
292 if (!widget || !win)
293 return false;
294
295 wxKeyEvent keyEvent(wxEVT_KEY_DOWN);
296 wxTranslateKeyEvent(keyEvent, win, (Widget) 0, event);
297
298 return win->GetEventHandler()->ProcessEvent( keyEvent );
299 }
300
301 return false;
302 }
303
304 // bool wxApp::CheckForKeyUp(WXEvent* event) { wxFAIL; return false; }
305
306 bool CheckForKeyUp(XEvent* event)
307 {
308 if (event->xany.type == KeyRelease)
309 {
310 Widget widget = XtWindowToWidget(event->xany.display,
311 event->xany.window);
312 wxWindow* win = NULL;
313
314 // Find the first wxWindow that corresponds to this event window
315 while (widget && ((win = wxGetWindowFromTable(widget))!=NULL))
316 widget = XtParent(widget);
317
318 if (!widget || !win)
319 return false;
320
321 wxKeyEvent keyEvent(wxEVT_KEY_UP);
322 wxTranslateKeyEvent(keyEvent, win, (Widget) 0, event);
323
324 return win->GetEventHandler()->ProcessEvent( keyEvent );
325 }
326
327 return false;
328 }
329
330 // ----------------------------------------------------------------------------
331 // executes one main loop iteration (declared in include/wx/motif/private.h)
332 // ----------------------------------------------------------------------------
333
334 bool wxDoEventLoopIteration( wxEventLoop& evtLoop )
335 {
336 bool moreRequested, pendingEvents;
337
338 for(;;)
339 {
340 pendingEvents = evtLoop.Pending();
341 if( pendingEvents ) break;
342 moreRequested = ::SendIdleMessage();
343 if( !moreRequested ) break;
344 }
345
346 #if wxUSE_THREADS
347 if( !pendingEvents && !moreRequested )
348 {
349 // leave the main loop to give other threads a chance to
350 // perform their GUI work
351 wxMutexGuiLeave();
352 wxMilliSleep(20);
353 wxMutexGuiEnter();
354 }
355 #endif
356
357 if( !evtLoop.Dispatch() )
358 return false;
359
360 return true;
361 }
362
363 // ----------------------------------------------------------------------------
364 // ::wxWakeUpIdle implementation
365 // ----------------------------------------------------------------------------
366
367 // As per Vadim's suggestion, we open a pipe, and XtAppAddInputSource it;
368 // writing a single byte to the pipe will cause wxEventLoop::Pending
369 // to return true, and wxEventLoop::Dispatch to dispatch an input handler
370 // that simply removes the byte(s), and to return, which will cause
371 // an idle event to be sent
372
373 // also wxEventLoop::Exit is implemented that way, so that exiting an
374 // event loop won't require an event being in the queue
375
376 #include "wx/module.h"
377
378 #include <sys/types.h>
379 #include <sys/time.h>
380 #include <unistd.h>
381
382 static int idleFds[2] = { -1, -1 };
383
384 class wxIdlePipeModule : public wxModule
385 {
386 public:
387 wxIdlePipeModule() {};
388
389 virtual bool OnInit()
390 {
391 // Must be done before modules are initialized
392 #if 0
393 if( pipe(idleFds) != 0 )
394 return false;
395 #endif
396 return true;
397 }
398
399 virtual void OnExit()
400 {
401 close( idleFds[0] );
402 close( idleFds[1] );
403 }
404 private:
405 DECLARE_DYNAMIC_CLASS(wxIdlePipeModule)
406 };
407
408 IMPLEMENT_DYNAMIC_CLASS(wxIdlePipeModule, wxModule)
409
410 static void wxInputCallback( XtPointer, int* fd, XtInputId* )
411 {
412 char buffer[128];
413
414 // wxWakeUpIdle may have been called more than once
415 for(;;)
416 {
417 fd_set in;
418 struct timeval timeout;
419
420 timeout.tv_sec = 0;
421 timeout.tv_usec = 0;
422
423 wxFD_ZERO( &in );
424 wxFD_SET( *fd, &in );
425
426 if( select( *fd + 1, &in, NULL, NULL, &timeout ) <= 0 )
427 break;
428 if( read( *fd, buffer, sizeof(buffer) - 1 ) != sizeof(buffer) - 1 )
429 break;
430 }
431 }
432
433 static void wxBreakDispatch()
434 {
435 char dummy = 0; // for valgrind
436
437 // check if wxWakeUpIdle has already been called
438 fd_set in;
439 struct timeval timeout;
440
441 timeout.tv_sec = 0;
442 timeout.tv_usec = 0;
443
444 wxFD_ZERO( &in );
445 wxFD_SET( idleFds[0], &in );
446
447 if( select( idleFds[0] + 1, &in, NULL, NULL, &timeout ) > 0 )
448 return;
449
450 // write a single byte
451 write( idleFds[1], &dummy, 1 );
452 }
453
454 void wxApp::WakeUpIdle()
455 {
456 ::wxBreakDispatch();
457 }
458
459 bool wxInitIdleFds()
460 {
461 if( pipe(idleFds) != 0 )
462 return false;
463 return true;
464 }
465
466 bool wxAddIdleCallback()
467 {
468 if (!wxInitIdleFds())
469 return false;
470
471 // install input handler for wxWakeUpIdle
472 XtAppAddInput((XtAppContext) wxTheApp->GetAppContext(),
473 idleFds[0],
474 (XtPointer)XtInputReadMask,
475 wxInputCallback,
476 NULL);
477
478 return true;
479 }