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