1 /////////////////////////////////////////////////////////////////////////////// 
   2 // Name:        msw/evtloop.cpp 
   3 // Purpose:     implements wxEventLoop for MSW 
   4 // Author:      Vadim Zeitlin 
   8 // Copyright:   (c) 2001 Vadim Zeitlin <zeitlin@dptmaths.ens-cachan.fr> 
   9 // License:     wxWindows licence 
  10 /////////////////////////////////////////////////////////////////////////////// 
  12 // ============================================================================ 
  14 // ============================================================================ 
  16 // ---------------------------------------------------------------------------- 
  18 // ---------------------------------------------------------------------------- 
  20 #if defined(__GNUG__) && !defined(NO_GCC_PRAGMA) 
  21     #pragma implementation "evtloop.h" 
  24 // For compilers that support precompilation, includes "wx.h". 
  25 #include "wx/wxprec.h" 
  32     #include "wx/window.h" 
  36 #include "wx/evtloop.h" 
  38 #include "wx/tooltip.h" 
  39 #include "wx/except.h" 
  40 #include "wx/ptr_scpd.h" 
  42 #include "wx/msw/private.h" 
  46 #include "wx/msw/wince/missing.h" 
  50     #include "wx/thread.h" 
  52     // define the array of MSG strutures 
  53     WX_DECLARE_OBJARRAY(MSG
, wxMsgArray
); 
  55     #include "wx/arrimpl.cpp" 
  57     WX_DEFINE_OBJARRAY(wxMsgArray
); 
  58 #endif // wxUSE_THREADS 
  60 // ---------------------------------------------------------------------------- 
  62 // ---------------------------------------------------------------------------- 
  64 class WXDLLEXPORT wxEventLoopImpl
 
  68     wxEventLoopImpl() { m_exitcode 
= 0; m_shouldExit 
= false; } 
  71     void ProcessMessage(MSG 
*msg
); 
  73     // generate an idle message, return TRUE if more idle time requested 
  74     bool SendIdleMessage(); 
  76     // set/get the exit code 
  77     void Exit(int exitcode
) { m_exitcode 
= exitcode
; m_shouldExit 
= true; } 
  78     int GetExitCode() const { return m_exitcode
; } 
  79     bool ShouldExit() const { return m_shouldExit
; } 
  81     enum wxCatchAllResponse 
{ 
  86     wxCatchAllResponse 
OnCatchAll(); 
  89     // preprocess a message, return TRUE if processed (i.e. no further 
  90     // dispatching required) 
  91     bool PreProcessMessage(MSG 
*msg
); 
  93     // the exit code of the event loop 
  96     // true if we were asked to terminate 
 100 // ---------------------------------------------------------------------------- 
 102 // ---------------------------------------------------------------------------- 
 104 wxDEFINE_TIED_SCOPED_PTR_TYPE(wxEventLoopImpl
); 
 106 // this object sets the wxEventLoop given to the ctor as the currently active 
 107 // one and unsets it in its dtor 
 108 class wxEventLoopActivator
 
 111     wxEventLoopActivator(wxEventLoop 
**pActive
, 
 112                          wxEventLoop 
*evtLoop
) 
 115         m_evtLoopOld 
= *pActive
; 
 119     ~wxEventLoopActivator() 
 121         // restore the previously active event loop 
 122         *m_pActive 
= m_evtLoopOld
; 
 126     wxEventLoop 
*m_evtLoopOld
; 
 127     wxEventLoop 
**m_pActive
; 
 130 // ============================================================================ 
 131 // wxEventLoopImpl implementation 
 132 // ============================================================================ 
 134 // ---------------------------------------------------------------------------- 
 135 // wxEventLoopImpl message processing 
 136 // ---------------------------------------------------------------------------- 
 138 void wxEventLoopImpl::ProcessMessage(MSG 
*msg
) 
 140     // give us the chance to preprocess the message first 
 141     if ( !PreProcessMessage(msg
) ) 
 143         // if it wasn't done, dispatch it to the corresponding window 
 144         ::TranslateMessage(msg
); 
 145         ::DispatchMessage(msg
); 
 149 bool wxEventLoopImpl::PreProcessMessage(MSG 
*msg
) 
 151     HWND hwnd 
= msg
->hwnd
; 
 152     wxWindow 
*wndThis 
= wxGetWindowFromHWND((WXHWND
)hwnd
); 
 154     // this may happen if the event occured in a standard modeless dialog (the 
 155     // only example of which I know of is the find/replace dialog) - then call 
 156     // IsDialogMessage() to make TAB navigation in it work 
 159         // we need to find the dialog containing this control as 
 160         // IsDialogMessage() just eats all the messages (i.e. returns TRUE for 
 161         // them) if we call it for the control itself 
 162         while ( hwnd 
&& ::GetWindowLong(hwnd
, GWL_STYLE
) & WS_CHILD 
) 
 164             hwnd 
= ::GetParent(hwnd
); 
 167         return hwnd 
&& ::IsDialogMessage(hwnd
, msg
) != 0; 
 171     // we must relay WM_MOUSEMOVE events to the tooltip ctrl if we want it to 
 172     // popup the tooltip bubbles 
 173     if ( msg
->message 
== WM_MOUSEMOVE 
) 
 175         wxToolTip 
*tt 
= wndThis
->GetToolTip(); 
 178             tt
->RelayEvent((WXMSG 
*)msg
); 
 181 #endif // wxUSE_TOOLTIPS 
 183     // allow the window to prevent certain messages from being 
 184     // translated/processed (this is currently used by wxTextCtrl to always 
 185     // grab Ctrl-C/V/X, even if they are also accelerators in some parent) 
 186     if ( !wndThis
->MSWShouldPreProcessMessage((WXMSG 
*)msg
) ) 
 191     // try translations first: the accelerators override everything 
 194     for ( wnd 
= wndThis
; wnd
; wnd 
= wnd
->GetParent() ) 
 196         if ( wnd
->MSWTranslateMessage((WXMSG 
*)msg
)) 
 199         // stop at first top level window, i.e. don't try to process the key 
 200         // strokes originating in a dialog using the accelerators of the parent 
 201         // frame - this doesn't make much sense 
 202         if ( wnd
->IsTopLevel() ) 
 206     // now try the other hooks (kbd navigation is handled here): we start from 
 207     // wndThis->GetParent() because wndThis->MSWProcessMessage() was already 
 209     for ( wnd 
= wndThis
->GetParent(); wnd
; wnd 
= wnd
->GetParent() ) 
 211         if ( wnd
->MSWProcessMessage((WXMSG 
*)msg
) ) 
 215     // no special preprocessing for this message, dispatch it normally 
 219 // ---------------------------------------------------------------------------- 
 220 // wxEventLoopImpl idle event processing 
 221 // ---------------------------------------------------------------------------- 
 223 bool wxEventLoopImpl::SendIdleMessage() 
 225     return wxTheApp
->ProcessIdle(); 
 228 // ---------------------------------------------------------------------------- 
 229 // wxEventLoopImpl exception handling 
 230 // ---------------------------------------------------------------------------- 
 232 wxEventLoopImpl::wxCatchAllResponse 
wxEventLoopImpl::OnCatchAll() 
 234     switch (::MessageBox(NULL
,  
 235             _T("An unhandled exception occurred. 'Abort' will terminate the program,\r\n\ 
 236 'Retry' will close the current dialog, 'Ignore' will try to continue."), 
 237             _T("Unhandled exception"),  
 238             MB_ABORTRETRYIGNORE
|MB_ICONERROR
|MB_TASKMODAL
)) 
 240         case IDABORT
: return catch_rethrow
; 
 241         case IDRETRY
: return catch_exit
; 
 242         case IDIGNORE
: return catch_continue
; 
 244     return catch_rethrow
; 
 247 // ============================================================================ 
 248 // wxEventLoop implementation 
 249 // ============================================================================ 
 251 wxEventLoop 
*wxEventLoop::ms_activeLoop 
= NULL
; 
 253 // ---------------------------------------------------------------------------- 
 254 // wxEventLoop running and exiting 
 255 // ---------------------------------------------------------------------------- 
 257 wxEventLoop::~wxEventLoop() 
 259     wxASSERT_MSG( !m_impl
, _T("should have been deleted in Run()") ); 
 262 bool wxEventLoop::IsRunning() const 
 264     return m_impl 
!= NULL
; 
 267 int wxEventLoop::Run() 
 269     // event loops are not recursive, you need to create another loop! 
 270     wxCHECK_MSG( !IsRunning(), -1, _T("can't reenter a message loop") ); 
 272     // SendIdleMessage() and Dispatch() below may throw so the code here should 
 273     // be exception-safe, hence we must use local objects for all actions we 
 275     wxEventLoopActivator 
activate(&ms_activeLoop
, this); 
 276     wxEventLoopImplTiedPtr 
impl(&m_impl
, new wxEventLoopImpl
); 
 278     // we must ensure that OnExit() is called even if an exception is thrown 
 279     // from inside Dispatch() but we must call it from Exit() in normal 
 280     // situations because it is supposed to be called synchronously, 
 281     // wxModalEventLoop depends on this (so we can't just use ON_BLOCK_EXIT or 
 282     // something similar here) 
 284     bool retryAfterException 
= false; 
 286         retryAfterException
=false; 
 293             wxMutexGuiLeaveOrEnter(); 
 294     #endif // wxUSE_THREADS 
 296             // generate and process idle events for as long as we don't have 
 297             // anything else to do 
 298             while ( !Pending() && m_impl
->SendIdleMessage() ) 
 301             // if the "should exit" flag is set, the loop should terminate but 
 302             // not before processing any remaining messages so while Pending() 
 303             // returns true, do process them 
 304             if ( m_impl
->ShouldExit() ) 
 312             // a message came or no more idle processing to do, sit in 
 313             // Dispatch() waiting for the next message 
 322             switch (m_impl
->OnCatchAll()) { 
 323                 case wxEventLoopImpl::catch_continue
: 
 324                     retryAfterException
=true; 
 326                 case wxEventLoopImpl::catch_exit
: 
 329                 case wxEventLoopImpl::catch_rethrow
: 
 331                     // should be replaced with wx macro, but 
 332                     // there is none yet. OTOH, wxCATCH_ALL isn't 
 333                     // expanded unless wxUSE_EXCEPTIONS, so its 
 334                     // safe to use throw here. 
 341     } while (retryAfterException
); 
 344     return m_impl
->GetExitCode(); 
 347 void wxEventLoop::Exit(int rc
) 
 349     wxCHECK_RET( IsRunning(), _T("can't call Exit() if not running") ); 
 355     // all we have to do to exit from the loop is to (maybe) wake it up so that 
 356     // it can notice that Exit() had been called 
 358     // in particular, we do *not* use PostQuitMessage() here because we're not 
 359     // sure that WM_QUIT is going to be processed by the correct event loop: it 
 360     // is possible that another one is started before this one has a chance to 
 362     ::PostMessage(NULL
, WM_NULL
, 0, 0); 
 365 // ---------------------------------------------------------------------------- 
 366 // wxEventLoop message processing dispatching 
 367 // ---------------------------------------------------------------------------- 
 369 bool wxEventLoop::Pending() const 
 372     return ::PeekMessage(&msg
, 0, 0, 0, PM_NOREMOVE
) != 0; 
 375 bool wxEventLoop::Dispatch() 
 377     wxCHECK_MSG( IsRunning(), FALSE
, _T("can't call Dispatch() if not running") ); 
 380     BOOL rc 
= ::GetMessage(&msg
, (HWND
) NULL
, 0, 0); 
 390         // should never happen, but let's test for it nevertheless 
 391         wxLogLastError(wxT("GetMessage")); 
 393         // still break from the loop 
 398     wxASSERT_MSG( wxThread::IsMain(), 
 399                   wxT("only the main thread can process Windows messages") ); 
 401     static bool s_hadGuiLock 
= TRUE
; 
 402     static wxMsgArray s_aSavedMessages
; 
 404     // if a secondary thread owning the mutex is doing GUI calls, save all 
 405     // messages for later processing - we can't process them right now because 
 406     // it will lead to recursive library calls (and we're not reentrant) 
 407     if ( !wxGuiOwnedByMainThread() ) 
 409         s_hadGuiLock 
= FALSE
; 
 411         // leave out WM_COMMAND messages: too dangerous, sometimes 
 412         // the message will be processed twice 
 413         if ( !wxIsWaitingForThread() || msg
.message 
!= WM_COMMAND 
) 
 415             s_aSavedMessages
.Add(msg
); 
 422         // have we just regained the GUI lock? if so, post all of the saved 
 425         // FIXME of course, it's not _exactly_ the same as processing the 
 426         //       messages normally - expect some things to break... 
 431             size_t count 
= s_aSavedMessages
.Count(); 
 432             for ( size_t n 
= 0; n 
< count
; n
++ ) 
 434                 MSG
& msg 
= s_aSavedMessages
[n
]; 
 435                 m_impl
->ProcessMessage(&msg
); 
 438             s_aSavedMessages
.Empty(); 
 441 #endif // wxUSE_THREADS 
 443     m_impl
->ProcessMessage(&msg
);