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 // For compilers that support precompilation, includes "wx.h". 
  21 #include "wx/wxprec.h" 
  27 #include "wx/evtloop.h" 
  31         #include "wx/window.h" 
  37 #include "wx/thread.h" 
  38 #include "wx/except.h" 
  39 #include "wx/msw/private.h" 
  40 #include "wx/scopeguard.h" 
  43     #include "wx/tooltip.h" 
  45         // define the list of MSG strutures 
  46         WX_DECLARE_LIST(MSG
, wxMsgList
); 
  48         #include "wx/listimpl.cpp" 
  50         WX_DEFINE_LIST(wxMsgList
) 
  51     #endif // wxUSE_THREADS 
  56 // ============================================================================ 
  57 // wxMSWEventLoopBase implementation 
  58 // ============================================================================ 
  60 // ---------------------------------------------------------------------------- 
  62 // ---------------------------------------------------------------------------- 
  64 wxMSWEventLoopBase::wxMSWEventLoopBase() 
  70 // ---------------------------------------------------------------------------- 
  71 // wxEventLoop message processing dispatching 
  72 // ---------------------------------------------------------------------------- 
  74 bool wxMSWEventLoopBase::Pending() const 
  77     return ::PeekMessage(&msg
, 0, 0, 0, PM_NOREMOVE
) != 0; 
  80 bool wxMSWEventLoopBase::GetNextMessage(WXMSG
* msg
) 
  82     const BOOL rc 
= ::GetMessage(msg
, NULL
, 0, 0); 
  92         // should never happen, but let's test for it nevertheless 
  93         wxLogLastError(wxT("GetMessage")); 
  95         // still break from the loop 
 102 int wxMSWEventLoopBase::GetNextMessageTimeout(WXMSG 
*msg
, unsigned long timeout
) 
 104     // MsgWaitForMultipleObjects() won't notice any input which was already 
 105     // examined (e.g. using PeekMessage()) but not yet removed from the queue 
 106     // so we need to remove any immediately messages manually 
 108     // NB: using MsgWaitForMultipleObjectsEx() could simplify the code here but 
 109     //     it is not available in very old Windows versions 
 110     if ( !::PeekMessage(msg
, 0, 0, 0, PM_REMOVE
) ) 
 112         // we use this function just in order to not block longer than the 
 113         // given timeout, so we don't pass any handles to it at all 
 114         DWORD rc 
= ::MsgWaitForMultipleObjects
 
 125                 wxLogDebug("unexpected MsgWaitForMultipleObjects() return " 
 133                 if ( !::PeekMessage(msg
, 0, 0, 0, PM_REMOVE
) ) 
 135                     // somehow it may happen that MsgWaitForMultipleObjects() 
 136                     // returns true but there are no messages -- just treat it 
 137                     // the same as timeout then 
 144     return msg
->message 
!= WM_QUIT
; 
 152 // ============================================================================ 
 153 // GUI wxEventLoop implementation 
 154 // ============================================================================ 
 156 wxWindowMSW 
*wxGUIEventLoop::ms_winCritical 
= NULL
; 
 158 bool wxGUIEventLoop::IsChildOfCriticalWindow(wxWindowMSW 
*win
) 
 162         if ( win 
== ms_winCritical 
) 
 165         win 
= win
->GetParent(); 
 171 bool wxGUIEventLoop::PreProcessMessage(WXMSG 
*msg
) 
 173     HWND hwnd 
= msg
->hwnd
; 
 174     wxWindow 
*wndThis 
= wxGetWindowFromHWND((WXHWND
)hwnd
); 
 177     // this might happen if we're in a modeless dialog, or if a wx control has 
 178     // children which themselves were not created by wx (i.e. wxActiveX control children) 
 181         while ( hwnd 
&& (::GetWindowLong(hwnd
, GWL_STYLE
) & WS_CHILD 
)) 
 183             hwnd 
= ::GetParent(hwnd
); 
 185             // If the control has a wx parent, break and give the parent a chance 
 186             // to process the window message 
 187             wndThis 
= wxGetWindowFromHWND((WXHWND
)hwnd
); 
 194             // this may happen if the event occurred in a standard modeless dialog (the 
 195             // only example of which I know of is the find/replace dialog) - then call 
 196             // IsDialogMessage() to make TAB navigation in it work 
 198             // NOTE: IsDialogMessage() just eats all the messages (i.e. returns true for 
 199             // them) if we call it for the control itself 
 200             return hwnd 
&& ::IsDialogMessage(hwnd
, msg
) != 0; 
 204     if ( !AllowProcessing(wndThis
) ) 
 206         // not a child of critical window, so we eat the event but take care to 
 207         // stop an endless stream of WM_PAINTs which would have resulted if we 
 208         // didn't validate the invalidated part of the window 
 209         if ( msg
->message 
== WM_PAINT 
) 
 210             ::ValidateRect(hwnd
, NULL
); 
 216     // we must relay WM_MOUSEMOVE events to the tooltip ctrl if we want it to 
 217     // popup the tooltip bubbles 
 218     if ( msg
->message 
== WM_MOUSEMOVE 
) 
 220         // we should do it if one of window children has an associated tooltip 
 221         // (and not just if the window has a tooltip itself) 
 222         if ( wndThis
->HasToolTips() ) 
 223             wxToolTip::RelayEvent((WXMSG 
*)msg
); 
 225 #endif // wxUSE_TOOLTIPS 
 227     // allow the window to prevent certain messages from being 
 228     // translated/processed (this is currently used by wxTextCtrl to always 
 229     // grab Ctrl-C/V/X, even if they are also accelerators in some parent) 
 230     if ( !wndThis
->MSWShouldPreProcessMessage((WXMSG 
*)msg
) ) 
 235     // try translations first: the accelerators override everything 
 236     for ( wnd 
= wndThis
; wnd
; wnd 
= wnd
->GetParent() ) 
 238         if ( wnd
->MSWTranslateMessage((WXMSG 
*)msg
)) 
 241         // stop at first top level window, i.e. don't try to process the key 
 242         // strokes originating in a dialog using the accelerators of the parent 
 243         // frame - this doesn't make much sense 
 244         if ( wnd
->IsTopLevel() ) 
 248     // now try the other hooks (kbd navigation is handled here) 
 249     for ( wnd 
= wndThis
; wnd
; wnd 
= wnd
->GetParent() ) 
 251         if ( wnd
->MSWProcessMessage((WXMSG 
*)msg
) ) 
 254         // also stop at first top level window here, just as above because 
 255         // if we don't do this, pressing ESC on a modal dialog shown as child 
 256         // of a modal dialog with wxID_CANCEL will cause the parent dialog to 
 257         // be closed, for example 
 258         if ( wnd
->IsTopLevel() ) 
 262     // no special preprocessing for this message, dispatch it normally 
 266 void wxGUIEventLoop::ProcessMessage(WXMSG 
*msg
) 
 268     // give us the chance to preprocess the message first 
 269     if ( !PreProcessMessage(msg
) ) 
 271         // if it wasn't done, dispatch it to the corresponding window 
 272         ::TranslateMessage(msg
); 
 273         ::DispatchMessage(msg
); 
 277 bool wxGUIEventLoop::Dispatch() 
 280     if ( !GetNextMessage(&msg
) ) 
 284     wxASSERT_MSG( wxThread::IsMain(), 
 285                   wxT("only the main thread can process Windows messages") ); 
 287     static bool s_hadGuiLock 
= true; 
 288     static wxMsgList s_aSavedMessages
; 
 290     // if a secondary thread owning the mutex is doing GUI calls, save all 
 291     // messages for later processing - we can't process them right now because 
 292     // it will lead to recursive library calls (and we're not reentrant) 
 293     if ( !wxGuiOwnedByMainThread() ) 
 295         s_hadGuiLock 
= false; 
 297         // leave out WM_COMMAND messages: too dangerous, sometimes 
 298         // the message will be processed twice 
 299         if ( !wxIsWaitingForThread() || msg
.message 
!= WM_COMMAND 
) 
 301             MSG
* pMsg 
= new MSG(msg
); 
 302             s_aSavedMessages
.Append(pMsg
); 
 309         // have we just regained the GUI lock? if so, post all of the saved 
 312         // FIXME of course, it's not _exactly_ the same as processing the 
 313         //       messages normally - expect some things to break... 
 318             wxMsgList::compatibility_iterator node 
= s_aSavedMessages
.GetFirst(); 
 321                 MSG
* pMsg 
= node
->GetData(); 
 322                 s_aSavedMessages
.Erase(node
); 
 324                 ProcessMessage(pMsg
); 
 327                 node 
= s_aSavedMessages
.GetFirst(); 
 331 #endif // wxUSE_THREADS 
 333     ProcessMessage(&msg
); 
 338 int wxGUIEventLoop::DispatchTimeout(unsigned long timeout
) 
 341     int rc 
= GetNextMessageTimeout(&msg
, timeout
); 
 345     ProcessMessage(&msg
); 
 350 void wxGUIEventLoop::OnNextIteration() 
 353     wxMutexGuiLeaveOrEnter(); 
 354 #endif // wxUSE_THREADS 
 357 void wxGUIEventLoop::WakeUp() 
 359     ::PostMessage(NULL
, WM_NULL
, 0, 0); 
 363 // ---------------------------------------------------------------------------- 
 364 // Yield to incoming messages 
 365 // ---------------------------------------------------------------------------- 
 367 #include <wx/arrimpl.cpp> 
 368 WX_DEFINE_OBJARRAY(wxMSGArray
); 
 370 bool wxGUIEventLoop::YieldFor(long eventsToProcess
) 
 372     // set the flag and don't forget to reset it before returning 
 373     m_isInsideYield 
= true; 
 374     m_eventsToProcessInsideYield 
= eventsToProcess
; 
 376     wxON_BLOCK_EXIT_SET(m_isInsideYield
, false); 
 379     // disable log flushing from here because a call to wxYield() shouldn't 
 380     // normally result in message boxes popping up &c 
 383     // ensure the logs will be flashed again when we exit 
 384     wxON_BLOCK_EXIT0(wxLog::Resume
); 
 387     // we don't want to process WM_QUIT from here - it should be processed in 
 388     // the main event loop in order to stop it 
 390     int nPaintsReceived 
= 0; 
 391     while ( PeekMessage(&msg
, (HWND
)0, 0, 0, PM_NOREMOVE
) && 
 392             msg
.message 
!= WM_QUIT 
) 
 395         wxMutexGuiLeaveOrEnter(); 
 396 #endif // wxUSE_THREADS 
 398         if (msg
.message 
== WM_PAINT
) 
 400             // NOTE: WM_PAINTs are categorized as wxEVT_CATEGORY_UI 
 401             if ((eventsToProcess 
& wxEVT_CATEGORY_UI
) == 0) 
 403                 // this msg is not going to be dispatched... 
 404                 // however WM_PAINT is special: until there are damaged  
 405                 // windows, Windows will keep sending it forever! 
 406                 if (nPaintsReceived 
> 10) 
 408                     // we got 10 WM_PAINT consecutive messages... 
 409                     // we must have reached the tail of the message queue: 
 410                     // we're now getting _only_ WM_PAINT events and this will 
 411                     // continue forever (since we don't dispatch them 
 412                     // because of the user-specified eventsToProcess mask)... 
 413                     // break out of this loop! 
 419             //else: we're going to dispatch it below, 
 420             //      so we don't need to take any special action 
 424             // reset the counter of consecutive WM_PAINT messages received: 
 428         // choose a wxEventCategory for this Windows message 
 433             case WM_NCLBUTTONDOWN
: 
 435             case WM_NCLBUTTONDBLCLK
: 
 436             case WM_NCRBUTTONDOWN
: 
 438             case WM_NCRBUTTONDBLCLK
: 
 439             case WM_NCMBUTTONDOWN
: 
 441             case WM_NCMBUTTONDBLCLK
: 
 455             case WM_IME_STARTCOMPOSITION
: 
 456             case WM_IME_ENDCOMPOSITION
: 
 457             case WM_IME_COMPOSITION
: 
 461             case WM_IME_SETCONTEXT
: 
 464             case WM_IME_COMPOSITIONFULL
: 
 471 #ifdef WM_NCMOUSELEAVE 
 472             case WM_NCMOUSELEAVE
: 
 485             case WM_LBUTTONDBLCLK
: 
 488             case WM_RBUTTONDBLCLK
: 
 491             case WM_MBUTTONDBLCLK
: 
 493                 cat 
= wxEVT_CATEGORY_USER_INPUT
; 
 497                 cat 
= wxEVT_CATEGORY_TIMER
; 
 501                 if (msg
.message 
< WM_USER
) 
 503                     // 0;WM_USER-1 is the range of message IDs reserved for use 
 505                     // there are too many of these types of messages to handle 
 506                     // them in this switch 
 507                     cat 
= wxEVT_CATEGORY_UI
; 
 510                     cat 
= wxEVT_CATEGORY_UNKNOWN
; 
 513         // should we process this event now? 
 514         if (cat 
& eventsToProcess
) 
 516             if ( !wxTheApp
->Dispatch() ) 
 521             // remove the message and store it 
 522             ::GetMessage(&msg
, NULL
, 0, 0); 
 527     // if there are pending events, we must process them. 
 529         wxTheApp
->ProcessPendingEvents(); 
 531     // put back unprocessed events in the queue 
 532     DWORD id 
= GetCurrentThreadId(); 
 533     for (size_t i
=0; i
<m_arrMSG
.GetCount(); i
++) 
 535         PostThreadMessage(id
, m_arrMSG
[i
].message
, 
 536                           m_arrMSG
[i
].wParam
, m_arrMSG
[i
].lParam
); 
 548 // ============================================================================ 
 549 // wxConsoleEventLoop implementation 
 550 // ============================================================================ 
 552 #if wxUSE_CONSOLE_EVENTLOOP 
 554 void wxConsoleEventLoop::WakeUp() 
 557     wxWakeUpMainThread(); 
 561 void wxConsoleEventLoop::ProcessMessage(WXMSG 
*msg
) 
 563     if ( msg
->message 
== WM_TIMER 
) 
 565         TIMERPROC proc 
= (TIMERPROC
)msg
->lParam
; 
 567             (*proc
)(NULL
, 0, msg
->wParam
, 0); 
 571         ::DispatchMessage(msg
); 
 575 bool wxConsoleEventLoop::Dispatch() 
 578     if ( !GetNextMessage(&msg
) ) 
 581     ProcessMessage(&msg
); 
 583     return !m_shouldExit
; 
 586 int wxConsoleEventLoop::DispatchTimeout(unsigned long timeout
) 
 589     int rc 
= GetNextMessageTimeout(&msg
, timeout
); 
 593     ProcessMessage(&msg
); 
 595     return !m_shouldExit
; 
 598 #endif // wxUSE_CONSOLE_EVENTLOOP