Win VM modifications
Rob Gayvert
rtg at rochester.rr.com
Thu Mar 3 17:47:46 UTC 2005
Andreas Raab wrote:
>>> We just tried it, and no, it wouldn't harm regular Squeak at all.
>>> But it does seem to harm Areithfa Ffenestri support (see
>>> http://minnow.cc.gatech.edu/squeak/3862) quite severely.
>>
>> That certainly makes sense -- everything but your main window will be
>> ignored.
>
> Well, it makes no sense whatsoever to me ;-) If wxWindows runs a
> mainloop I would expect it to drain the app event queue and dispatch
> all the events (just like the loop in ioProcessEvent does). If it
> would do so, then clearly Ffenestri should work (since the events
> would eventually get into the WndProc and passed up to Squeak) so the
> behavior of wxWindows is truly extraordinary.
Why doesn't this make sense? If you use
PeekMessage(&msg,stWindow,0,0,PM_NOREMOVE))
you're only getting messages for the main Squeak window. And in my wx
check, I'm only peeking for messages to windows other than the main
Squeak window.
> It is really important to understand what a Windows app mainloop
> really does: If we have, e.g., the standard loop from sqWin32Window.c
>
> while(PeekMessage(&msg,NULL,0,0,PM_NOREMOVE))
> {
> GetMessage(&msg,NULL,0,0);
> DispatchMessage(&msg);
> }
>
> then the following happens:
> * PeekMessage: Will see if there are any events for *any* window in
> the app's event queue without removing it
> * GetMessage: Will fetch the next event from the queue which may be
> for *any* window
> * DispatchMessage: Will deliver the event to the *appropriate*
> window's WndProc (which is registered with the window when the window
> is created)
>
> In other words: A window created by wxWindow *will* receive the event
> through the above mechanism in its registered WndProc. A mainloop like
> the above run by wxWindow *should* deliver events to *either* the main
> Squeak window or any other (e.g., Ffenestri-created) windows. The
> behavior of wxWindows apparently not adhering to either of the above
> two is extremely strange!
Well, I'm certainly no Win32 expert, so I can't say whether wxWidgets is
strange, but its MainLoop does more than Peek/Get/Dispatch. If you have
the stomach for it, take a look at the attachment, evtloop.cpp.
If I revert the original ioProcessEvents and turn off my event process,
events do get delivered to wx windows, but it behaves very badly and
generally crashes in short order. And if events destined for wx windows
are passed on to wxApp::ProcessMessage from ioProcessEvents, it works a
bit better, but still crashes. The only scheme I've found that works
consistently is to poll for wx events in a separate Squeak process. I
don't like it, but it works.
> I would expect that the first version works (wxWindows does get the
> events synchronously in its WndProc!) *and* that the second version
> works - wxWindow should deliver the events to the appropriate WndProc,
> so you should be perfectly able to simply not to run ioProcessEvents
> *at all* (this would be a good test - if Squeak doesn't work when
> wxWindows runs the mainloop something is horribly broken).
That would be a good test, but I wouldn't be surprised if the funky
stuff that goes on in ProcessMessage will screw it up for Squeak.
>> Well, this was the fundamental problem in getting two GUI systems to
>> work together, when each one wants to have the MainLoop. I'm not
>> crazy about the current scheme, but here's how it works. As we've
>> discussed, the Squeak loop is modified to ignore events to non-Squeak
>> windows. At startup, a process is run that checks every few
>> milliseconds for an event to a wx-window (ignoring events to the
>> Squeak window). When it gets an event, it passes it through the
>> normal wxWidgets processing. Not pretty, but it works.
>
> How does it check for the events? (can I see the source code?)
You can get all the wxSqueak source from my website, and all of the
wxWidgets source from wxwidgets.org.
The key part of the check is this:
if (PeekMessage(&msg,0,0,0,PM_NOREMOVE)) {
if (msg.hwnd != stWindow){
GetMessage(&msg,0,0,0);
if (!((wxSqueakApp*)wxTheApp)->ProcessMessage(&msg)){
TranslateMessage(&msg);
DispatchMessage(&msg);
}
If it weren't for the ProcessMessage() call, this would be a "normal" loop.
>> Initially, I had hoped that I could tap into events after they
>> arrived on the Smalltalk side, assuming they had been augmented with
>> window information like your're doing with Areithfa Ffenestri. But
>> Squeak events are handled asychronously, and so much of the wxWidgets
>> event processing depends on them being handled synchronously that I
>> just couldn't get it to work.
>
> I think you have a major bug in your thought process - as far as I can
> tell (and as far as I know Windows) your windows should get the events
> just fine (see above explanation). Unless there is some very strange
> magic in the main loop of wxWindows (do you have the source for the
> mainloop?) this ought to work with no further modifications.
I hope you're right. It would be much cleaner to handle all events in a
unified way, and eliminate the horribly inefficient event process in Squeak.
-------------- next part --------------
///////////////////////////////////////////////////////////////////////////////
// Name: msw/evtloop.cpp
// Purpose: implements wxEventLoop for MSW
// Author: Vadim Zeitlin
// Modified by:
// Created: 01.06.01
// RCS-ID: $Id: evtloop.cpp,v 1.25 2004/07/30 22:54:25 VZ Exp $
// Copyright: (c) 2001 Vadim Zeitlin <zeitlin at dptmaths.ens-cachan.fr>
// License: wxWindows licence
///////////////////////////////////////////////////////////////////////////////
// ============================================================================
// declarations
// ============================================================================
// ----------------------------------------------------------------------------
// headers
// ----------------------------------------------------------------------------
#if defined(__GNUG__) && !defined(NO_GCC_PRAGMA)
#pragma implementation "evtloop.h"
#endif
// For compilers that support precompilation, includes "wx.h".
#include "wx/wxprec.h"
#ifdef __BORLANDC__
#pragma hdrstop
#endif
#ifndef WX_PRECOMP
#include "wx/window.h"
#include "wx/app.h"
#endif //WX_PRECOMP
#include "wx/evtloop.h"
#include "wx/tooltip.h"
#include "wx/except.h"
#include "wx/ptr_scpd.h"
#include "wx/msw/private.h"
#if wxUSE_THREADS
#include "wx/thread.h"
// define the array of MSG strutures
WX_DECLARE_OBJARRAY(MSG, wxMsgArray);
#include "wx/arrimpl.cpp"
WX_DEFINE_OBJARRAY(wxMsgArray);
#endif // wxUSE_THREADS
// ----------------------------------------------------------------------------
// helper class
// ----------------------------------------------------------------------------
// this object sets the wxEventLoop given to the ctor as the currently active
// one and unsets it in its dtor
class wxEventLoopActivator
{
public:
wxEventLoopActivator(wxEventLoop **pActive,
wxEventLoop *evtLoop)
{
m_pActive = pActive;
m_evtLoopOld = *pActive;
*pActive = evtLoop;
}
~wxEventLoopActivator()
{
// restore the previously active event loop
*m_pActive = m_evtLoopOld;
}
private:
wxEventLoop *m_evtLoopOld;
wxEventLoop **m_pActive;
};
// ============================================================================
// wxEventLoop implementation
// ============================================================================
wxEventLoop *wxEventLoopBase::ms_activeLoop = NULL;
// ----------------------------------------------------------------------------
// ctor/dtor
// ----------------------------------------------------------------------------
wxEventLoop::wxEventLoop()
{
m_shouldExit = false;
m_exitcode = 0;
}
// ----------------------------------------------------------------------------
// wxEventLoop message processing
// ----------------------------------------------------------------------------
void wxEventLoop::ProcessMessage(WXMSG *msg)
{
// give us the chance to preprocess the message first
if ( !PreProcessMessage(msg) )
{
// if it wasn't done, dispatch it to the corresponding window
::TranslateMessage(msg);
::DispatchMessage(msg);
}
}
bool wxEventLoop::PreProcessMessage(WXMSG *msg)
{
HWND hwnd = msg->hwnd;
wxWindow *wndThis = wxGetWindowFromHWND((WXHWND)hwnd);
// this may happen if the event occured in a standard modeless dialog (the
// only example of which I know of is the find/replace dialog) - then call
// IsDialogMessage() to make TAB navigation in it work
if ( !wndThis )
{
// we need to find the dialog containing this control as
// IsDialogMessage() just eats all the messages (i.e. returns true for
// them) if we call it for the control itself
while ( hwnd && ::GetWindowLong(hwnd, GWL_STYLE) & WS_CHILD )
{
hwnd = ::GetParent(hwnd);
}
return hwnd && ::IsDialogMessage(hwnd, msg) != 0;
}
#if wxUSE_TOOLTIPS
// we must relay WM_MOUSEMOVE events to the tooltip ctrl if we want it to
// popup the tooltip bubbles
if ( msg->message == WM_MOUSEMOVE )
{
wxToolTip *tt = wndThis->GetToolTip();
if ( tt )
{
tt->RelayEvent((WXMSG *)msg);
}
}
#endif // wxUSE_TOOLTIPS
// allow the window to prevent certain messages from being
// translated/processed (this is currently used by wxTextCtrl to always
// grab Ctrl-C/V/X, even if they are also accelerators in some parent)
if ( !wndThis->MSWShouldPreProcessMessage((WXMSG *)msg) )
{
return false;
}
// try translations first: the accelerators override everything
wxWindow *wnd;
for ( wnd = wndThis; wnd; wnd = wnd->GetParent() )
{
if ( wnd->MSWTranslateMessage((WXMSG *)msg))
return true;
// stop at first top level window, i.e. don't try to process the key
// strokes originating in a dialog using the accelerators of the parent
// frame - this doesn't make much sense
if ( wnd->IsTopLevel() )
break;
}
// now try the other hooks (kbd navigation is handled here): we start from
// wndThis->GetParent() because wndThis->MSWProcessMessage() was already
// called above
for ( wnd = wndThis->GetParent(); wnd; wnd = wnd->GetParent() )
{
if ( wnd->MSWProcessMessage((WXMSG *)msg) )
return true;
}
// no special preprocessing for this message, dispatch it normally
return false;
}
// ----------------------------------------------------------------------------
// wxEventLoop running and exiting
// ----------------------------------------------------------------------------
bool wxEventLoop::IsRunning() const
{
return ms_activeLoop == this;
}
int wxEventLoop::Run()
{
// event loops are not recursive, you need to create another loop!
wxCHECK_MSG( !IsRunning(), -1, _T("can't reenter a message loop") );
// ProcessIdle() and Dispatch() below may throw so the code here should
// be exception-safe, hence we must use local objects for all actions we
// should undo
wxEventLoopActivator activate(&ms_activeLoop, this);
// we must ensure that OnExit() is called even if an exception is thrown
// from inside Dispatch() but we must call it from Exit() in normal
// situations because it is supposed to be called synchronously,
// wxModalEventLoop depends on this (so we can't just use ON_BLOCK_EXIT or
// something similar here)
#if wxUSE_EXCEPTIONS
for ( ;; )
{
try
{
#endif // wxUSE_EXCEPTIONS
// this is the event loop itself
for ( ;; )
{
#if wxUSE_THREADS
wxMutexGuiLeaveOrEnter();
#endif // wxUSE_THREADS
// generate and process idle events for as long as we don't
// have anything else to do
while ( !Pending() && (wxTheApp && wxTheApp->ProcessIdle()) )
;
// if the "should exit" flag is set, the loop should terminate
// but not before processing any remaining messages so while
// Pending() returns true, do process them
if ( m_shouldExit )
{
while ( Pending() )
Dispatch();
break;
}
// a message came or no more idle processing to do, sit in
// Dispatch() waiting for the next message
if ( !Dispatch() )
{
// we got WM_QUIT
break;
}
}
#if wxUSE_EXCEPTIONS
// exit the outer loop as well
break;
}
catch ( ... )
{
try
{
if ( !wxTheApp || !wxTheApp->OnExceptionInMainLoop() )
{
OnExit();
break;
}
//else: continue running the event loop
}
catch ( ... )
{
// OnException() throwed, possibly rethrowing the same
// exception again: very good, but we still need OnExit() to
// be called
OnExit();
throw;
}
}
}
#endif // wxUSE_EXCEPTIONS
return m_exitcode;
}
void wxEventLoop::Exit(int rc)
{
wxCHECK_RET( IsRunning(), _T("can't call Exit() if not running") );
m_exitcode = rc;
m_shouldExit = true;
OnExit();
// all we have to do to exit from the loop is to (maybe) wake it up so that
// it can notice that Exit() had been called
//
// in particular, we do *not* use PostQuitMessage() here because we're not
// sure that WM_QUIT is going to be processed by the correct event loop: it
// is possible that another one is started before this one has a chance to
// process WM_QUIT
::PostMessage(NULL, WM_NULL, 0, 0);
}
// ----------------------------------------------------------------------------
// wxEventLoop message processing dispatching
// ----------------------------------------------------------------------------
bool wxEventLoop::Pending() const
{
MSG msg;
return ::PeekMessage(&msg, 0, 0, 0, PM_NOREMOVE) != 0;
}
bool wxEventLoop::Dispatch()
{
wxCHECK_MSG( IsRunning(), false, _T("can't call Dispatch() if not running") );
MSG msg;
BOOL rc = ::GetMessage(&msg, (HWND) NULL, 0, 0);
if ( rc == 0 )
{
// got WM_QUIT
return false;
}
if ( rc == -1 )
{
// should never happen, but let's test for it nevertheless
wxLogLastError(wxT("GetMessage"));
// still break from the loop
return false;
}
#if wxUSE_THREADS
wxASSERT_MSG( wxThread::IsMain(),
wxT("only the main thread can process Windows messages") );
static bool s_hadGuiLock = true;
static wxMsgArray s_aSavedMessages;
// if a secondary thread owning the mutex is doing GUI calls, save all
// messages for later processing - we can't process them right now because
// it will lead to recursive library calls (and we're not reentrant)
if ( !wxGuiOwnedByMainThread() )
{
s_hadGuiLock = false;
// leave out WM_COMMAND messages: too dangerous, sometimes
// the message will be processed twice
if ( !wxIsWaitingForThread() || msg.message != WM_COMMAND )
{
s_aSavedMessages.Add(msg);
}
return true;
}
else
{
// have we just regained the GUI lock? if so, post all of the saved
// messages
//
// FIXME of course, it's not _exactly_ the same as processing the
// messages normally - expect some things to break...
if ( !s_hadGuiLock )
{
s_hadGuiLock = true;
size_t count = s_aSavedMessages.Count();
for ( size_t n = 0; n < count; n++ )
{
MSG& msg = s_aSavedMessages[n];
ProcessMessage(&msg);
}
s_aSavedMessages.Empty();
}
}
#endif // wxUSE_THREADS
ProcessMessage(&msg);
return true;
}
More information about the Wxsqueak
mailing list