]> git.saurik.com Git - wxWidgets.git/blob - src/os2/evtloop.cpp
fixed double deletion (patch #945491)
[wxWidgets.git] / src / os2 / evtloop.cpp
1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: os2/evtloop.cpp
3 // Purpose: implements wxEventLoop for PM
4 // Author: Vadim Zeitlin
5 // Modified by:
6 // Created: 01.06.01
7 // RCS-ID: $Id$
8 // Copyright: (c) 2001 Vadim Zeitlin <zeitlin@dptmaths.ens-cachan.fr>
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 // For compilers that support precompilation, includes "wx.h".
25 #include "wx/wxprec.h"
26
27 #ifdef __BORLANDC__
28 #pragma hdrstop
29 #endif
30
31 #ifndef WX_PRECOMP
32 #include "wx/window.h"
33 #include "wx/app.h"
34 #include "wx/timer.h"
35 #endif //WX_PRECOMP
36
37 #include "wx/evtloop.h"
38 #include "wx/log.h"
39 #include "wx/tooltip.h"
40 #include "wx/ptr_scpd.h"
41
42 #include "wx/os2/private.h"
43
44 #if wxUSE_THREADS
45 // define the array of QMSG strutures
46 WX_DECLARE_OBJARRAY(QMSG, wxMsgArray);
47
48 #include "wx/arrimpl.cpp"
49
50 WX_DEFINE_OBJARRAY(wxMsgArray);
51 #endif
52
53 extern HAB vHabmain;
54
55 // ----------------------------------------------------------------------------
56 // wxEventLoopImpl
57 // ----------------------------------------------------------------------------
58
59 class WXDLLEXPORT wxEventLoopImpl
60 {
61 public:
62 // ctor
63 wxEventLoopImpl() { SetExitCode(0); }
64
65 // process a message
66 void ProcessMessage(QMSG *msg);
67
68 // generate an idle message, return TRUE if more idle time requested
69 bool SendIdleMessage();
70
71 // set/get the exit code
72 void SetExitCode(int exitcode) { m_exitcode = exitcode; }
73 int GetExitCode() const { return m_exitcode; }
74
75 private:
76 // preprocess a message, return TRUE if processed (i.e. no further
77 // dispatching required)
78 bool PreProcessMessage(QMSG *msg);
79
80 // the exit code of the event loop
81 int m_exitcode;
82 };
83
84 // ----------------------------------------------------------------------------
85 // helper class
86 // ----------------------------------------------------------------------------
87
88 wxDEFINE_TIED_SCOPED_PTR_TYPE(wxEventLoopImpl);
89
90 // this object sets the wxEventLoop given to the ctor as the currently active
91 // one and unsets it in its dtor
92 class wxEventLoopActivator
93 {
94 public:
95 wxEventLoopActivator(wxEventLoop **pActive,
96 wxEventLoop *evtLoop)
97 {
98 m_pActive = pActive;
99 m_evtLoopOld = *pActive;
100 *pActive = evtLoop;
101 }
102
103 ~wxEventLoopActivator()
104 {
105 // restore the previously active event loop
106 *m_pActive = m_evtLoopOld;
107 }
108
109 private:
110 wxEventLoop *m_evtLoopOld;
111 wxEventLoop **m_pActive;
112 };
113
114 // ============================================================================
115 // wxEventLoopImpl implementation
116 // ============================================================================
117
118 // ----------------------------------------------------------------------------
119 // wxEventLoopImpl message processing
120 // ----------------------------------------------------------------------------
121
122 void wxEventLoopImpl::ProcessMessage(QMSG *msg)
123 {
124 // give us the chance to preprocess the message first
125 if ( !PreProcessMessage(msg) )
126 {
127 // if it wasn't done, dispatch it to the corresponding window
128 ::WinDispatchMsg(vHabmain, msg);
129 }
130 }
131
132 bool wxEventLoopImpl::PreProcessMessage(QMSG *pMsg)
133 {
134 HWND hWnd = pMsg->hwnd;
135 wxWindow *pWndThis = wxFindWinFromHandle((WXHWND)hWnd);
136 wxWindow *pWnd;
137
138 //
139 // Pass non-system timer messages to the wxTimerProc
140 //
141 if (pMsg->msg == WM_TIMER &&
142 (SHORT1FROMMP(pMsg->mp1) != TID_CURSOR &&
143 SHORT1FROMMP(pMsg->mp1) != TID_FLASHWINDOW &&
144 SHORT1FROMMP(pMsg->mp1) != TID_SCROLL &&
145 SHORT1FROMMP(pMsg->mp1) != 0x0000
146 ))
147 wxTimerProc(NULL, 0, (int)pMsg->mp1, 0);
148
149 // Allow the window to prevent certain messages from being
150 // translated/processed (this is currently used by wxTextCtrl to always
151 // grab Ctrl-C/V/X, even if they are also accelerators in some parent)
152 //
153 if (pWndThis && !pWndThis->OS2ShouldPreProcessMessage((WXMSG*)pMsg))
154 {
155 return FALSE;
156 }
157
158 //
159 // For some composite controls (like a combobox), wndThis might be NULL
160 // because the subcontrol is not a wxWindow, but only the control itself
161 // is - try to catch this case
162 //
163 while (hWnd && !pWndThis)
164 {
165 hWnd = ::WinQueryWindow(hWnd, QW_PARENT);
166 pWndThis = wxFindWinFromHandle((WXHWND)hWnd);
167 }
168
169
170 //
171 // Try translations first; find the youngest window with
172 // a translation table. OS/2 has case sensiive accels, so
173 // this block, coded by BK, removes that and helps make them
174 // case insensitive.
175 //
176 if(pMsg->msg == WM_CHAR)
177 {
178 PBYTE pChmsg = (PBYTE)&(pMsg->msg);
179 USHORT uSch = CHARMSG(pChmsg)->chr;
180 bool bRc = FALSE;
181
182 //
183 // Do not process keyup events
184 //
185 if(!(CHARMSG(pChmsg)->fs & KC_KEYUP))
186 {
187 if((CHARMSG(pChmsg)->fs & (KC_ALT | KC_CTRL)) && CHARMSG(pChmsg)->chr != 0)
188 CHARMSG(pChmsg)->chr = (USHORT)wxToupper((UCHAR)uSch);
189
190
191 for(pWnd = pWndThis; pWnd; pWnd = pWnd->GetParent() )
192 {
193 if((bRc = pWnd->OS2TranslateMessage((WXMSG*)pMsg)) == TRUE)
194 break;
195 // stop at first top level window, i.e. don't try to process the
196 // key strokes originating in a dialog using the accelerators of
197 // the parent frame - this doesn't make much sense
198 if ( pWnd->IsTopLevel() )
199 break;
200 }
201
202 if(!bRc) // untranslated, should restore original value
203 CHARMSG(pChmsg)->chr = uSch;
204 }
205 }
206 //
207 // Anyone for a non-translation message? Try youngest descendants first.
208 //
209 // for (pWnd = pWndThis->GetParent(); pWnd; pWnd = pWnd->GetParent())
210 // {
211 // if (pWnd->OS2ProcessMessage(pWxmsg))
212 // return TRUE;
213 // }
214 return FALSE;
215 }
216
217 // ----------------------------------------------------------------------------
218 // wxEventLoopImpl idle event processing
219 // ----------------------------------------------------------------------------
220
221 bool wxEventLoopImpl::SendIdleMessage()
222 {
223 return wxTheApp->ProcessIdle() ;
224 }
225
226 // ============================================================================
227 // wxEventLoop implementation
228 // ============================================================================
229
230 wxEventLoop *wxEventLoop::ms_activeLoop = NULL;
231
232 // ----------------------------------------------------------------------------
233 // wxEventLoop running and exiting
234 // ----------------------------------------------------------------------------
235
236 wxEventLoop::~wxEventLoop()
237 {
238 wxASSERT_MSG( !m_impl, _T("should have been deleted in Run()") );
239 }
240
241 bool wxEventLoop::IsRunning() const
242 {
243 return m_impl != NULL;
244 }
245
246 //////////////////////////////////////////////////////////////////////////////
247 //
248 // Keep trying to process messages until WM_QUIT
249 // received.
250 //
251 // If there are messages to be processed, they will all be
252 // processed and OnIdle will not be called.
253 // When there are no more messages, OnIdle is called.
254 // If OnIdle requests more time,
255 // it will be repeatedly called so long as there are no pending messages.
256 // A 'feature' of this is that once OnIdle has decided that no more processing
257 // is required, then it won't get processing time until further messages
258 // are processed (it'll sit in Dispatch).
259 //
260 //////////////////////////////////////////////////////////////////////////////
261 class CallEventLoopMethod
262 {
263 public:
264 typedef void (wxEventLoop::*FuncType)();
265
266 CallEventLoopMethod(wxEventLoop *evtLoop, FuncType fn)
267 : m_evtLoop(evtLoop), m_fn(fn) { }
268 ~CallEventLoopMethod() { (m_evtLoop->*m_fn)(); }
269
270 private:
271 wxEventLoop *m_evtLoop;
272 FuncType m_fn;
273 };
274
275 int wxEventLoop::Run()
276 {
277 // event loops are not recursive, you need to create another loop!
278 wxCHECK_MSG( !IsRunning(), -1, _T("can't reenter a message loop") );
279
280 // SendIdleMessage() and Dispatch() below may throw so the code here should
281 // be exception-safe, hence we must use local objects for all actions we
282 // should undo
283 wxEventLoopActivator activate(&ms_activeLoop, this);
284 wxEventLoopImplTiedPtr impl(&m_impl, new wxEventLoopImpl);
285
286 CallEventLoopMethod callOnExit(this, &wxEventLoop::OnExit);
287
288 for ( ;; )
289 {
290 #if wxUSE_THREADS
291 wxMutexGuiLeaveOrEnter();
292 #endif // wxUSE_THREADS
293
294 // generate and process idle events for as long as we don't have
295 // anything else to do
296 while ( !Pending() && m_impl->SendIdleMessage() )
297 {
298 wxTheApp->HandleSockets();
299 wxUsleep(10);
300 }
301
302 wxTheApp->HandleSockets();
303 if (Pending())
304 {
305 if ( !Dispatch() )
306 {
307 // we got WM_QUIT
308 break;
309 }
310 }
311 else
312 wxUsleep(10);
313 }
314
315 return m_impl->GetExitCode();
316 }
317
318 void wxEventLoop::Exit(int rc)
319 {
320 wxCHECK_RET( IsRunning(), _T("can't call Exit() if not running") );
321
322 m_impl->SetExitCode(rc);
323
324 ::WinPostMsg(NULL, WM_QUIT, 0, 0);
325 }
326
327 // ----------------------------------------------------------------------------
328 // wxEventLoop message processing dispatching
329 // ----------------------------------------------------------------------------
330
331 bool wxEventLoop::Pending() const
332 {
333 QMSG msg;
334 return ::WinPeekMsg(vHabmain, &msg, 0, 0, 0, PM_NOREMOVE) != 0;
335 }
336
337 bool wxEventLoop::Dispatch()
338 {
339 wxCHECK_MSG( IsRunning(), FALSE, _T("can't call Dispatch() if not running") );
340
341 QMSG msg;
342 BOOL bRc = ::WinGetMsg(vHabmain, &msg, (HWND) NULL, 0, 0);
343
344 if ( bRc == 0 )
345 {
346 // got WM_QUIT
347 return FALSE;
348 }
349
350 #if wxUSE_THREADS
351 wxASSERT_MSG( wxThread::IsMain(),
352 wxT("only the main thread can process Windows messages") );
353
354 static bool s_hadGuiLock = TRUE;
355 static wxMsgArray s_aSavedMessages;
356
357 // if a secondary thread owning the mutex is doing GUI calls, save all
358 // messages for later processing - we can't process them right now because
359 // it will lead to recursive library calls (and we're not reentrant)
360 if ( !wxGuiOwnedByMainThread() )
361 {
362 s_hadGuiLock = FALSE;
363
364 // leave out WM_COMMAND messages: too dangerous, sometimes
365 // the message will be processed twice
366 if ( !wxIsWaitingForThread() || msg.msg != WM_COMMAND )
367 {
368 s_aSavedMessages.Add(msg);
369 }
370
371 return TRUE;
372 }
373 else
374 {
375 // have we just regained the GUI lock? if so, post all of the saved
376 // messages
377 //
378 // FIXME of course, it's not _exactly_ the same as processing the
379 // messages normally - expect some things to break...
380 if ( !s_hadGuiLock )
381 {
382 s_hadGuiLock = TRUE;
383
384 size_t count = s_aSavedMessages.Count();
385 for ( size_t n = 0; n < count; n++ )
386 {
387 QMSG& msg = s_aSavedMessages[n];
388 m_impl->ProcessMessage(&msg);
389 }
390
391 s_aSavedMessages.Empty();
392 }
393 }
394 #endif // wxUSE_THREADS
395
396 m_impl->ProcessMessage(&msg);
397
398 return TRUE;
399 }
400