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