1///////////////////////////////////////////////////////////////////////////////
2// Name:        src/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: evtloop.cpp 43840 2006-12-06 23:28:44Z VZ $
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// For compilers that support precompilation, includes "wx.h".
21#include "wx/wxprec.h"
22
23#ifdef __BORLANDC__
24    #pragma hdrstop
25#endif
26
27#ifndef WX_PRECOMP
28    #include "wx/window.h"
29    #include "wx/app.h"
30    #include "wx/timer.h"
31    #include "wx/log.h"
32#endif //WX_PRECOMP
33
34#include "wx/evtloop.h"
35#include "wx/tooltip.h"
36#include "wx/ptr_scpd.h"
37
38#include "wx/os2/private.h"
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
49extern HAB vHabmain;
50
51// ----------------------------------------------------------------------------
52// wxEventLoopImpl
53// ----------------------------------------------------------------------------
54
55class WXDLLEXPORT wxEventLoopImpl
56{
57public:
58    // ctor
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
68    void SetExitCode(int exitcode) { m_exitcode = exitcode; }
69    int GetExitCode() const { return m_exitcode; }
70
71private:
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
84wxDEFINE_TIED_SCOPED_PTR_TYPE(wxEventLoopImpl);
85
86// ============================================================================
87// wxEventLoopImpl implementation
88// ============================================================================
89
90// ----------------------------------------------------------------------------
91// wxEventLoopImpl message processing
92// ----------------------------------------------------------------------------
93
94void 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
104bool 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
193bool wxEventLoopImpl::SendIdleMessage()
194{
195    return wxTheApp->ProcessIdle() ;
196}
197
198// ============================================================================
199// wxEventLoop implementation
200// ============================================================================
201
202// ----------------------------------------------------------------------------
203// wxEventLoop running and exiting
204// ----------------------------------------------------------------------------
205
206wxEventLoop::~wxEventLoop()
207{
208    wxASSERT_MSG( !m_impl, _T("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//////////////////////////////////////////////////////////////////////////////
226class CallEventLoopMethod
227{
228public:
229    typedef void (wxEventLoop::*FuncType)();
230
231    CallEventLoopMethod(wxEventLoop *evtLoop, FuncType fn)
232        : m_evtLoop(evtLoop), m_fn(fn) { }
233    ~CallEventLoopMethod() { (m_evtLoop->*m_fn)(); }
234
235private:
236    wxEventLoop *m_evtLoop;
237    FuncType m_fn;
238};
239
240int wxEventLoop::Run()
241{
242    // event loops are not recursive, you need to create another loop!
243    wxCHECK_MSG( !IsRunning(), -1, _T("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, &wxEventLoop::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
285void wxEventLoop::Exit(int rc)
286{
287    wxCHECK_RET( IsRunning(), _T("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// wxEventLoop message processing dispatching
296// ----------------------------------------------------------------------------
297
298bool wxEventLoop::Pending() const
299{
300    QMSG msg;
301    return ::WinPeekMsg(vHabmain, &msg, 0, 0, 0, PM_NOREMOVE) != 0;
302}
303
304bool wxEventLoop::Dispatch()
305{
306    wxCHECK_MSG( IsRunning(), false, _T("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