diff --git a/docs/changes.txt b/docs/changes.txt index 4a4e8018fb..2cfac6cc8f 100644 --- a/docs/changes.txt +++ b/docs/changes.txt @@ -584,6 +584,7 @@ All: - Add new wxFSW_EVENT_ATTRIB and wxFSW_EVENT_UNMOUNT flags (David Hart). - Add separate read/written bytes counters and per-direction NOWAIT and WAITALL flags to wxSocket (Rob Bresalier). +- Add wxEventLoop::ScheduleExit() (Rob Bresalier). - Add wxProcess::SetPriority() (Marian Meravy). - Add wxDir::Close() method (Silverstorm82). - Fix wxDateTime::GetWeekOfYear() for the last week of year (aimo). diff --git a/include/wx/cocoa/evtloop.h b/include/wx/cocoa/evtloop.h index 0790014c9a..f957dd8967 100644 --- a/include/wx/cocoa/evtloop.h +++ b/include/wx/cocoa/evtloop.h @@ -20,7 +20,7 @@ class WXDLLIMPEXP_CORE wxGUIEventLoop : public wxEventLoopBase public: wxGUIEventLoop() { m_exitcode = 0; } - virtual void Exit(int rc = 0); + virtual void ScheduleExit(int rc = 0); virtual bool Pending() const; virtual bool Dispatch(); virtual int DispatchTimeout(unsigned long timeout); diff --git a/include/wx/evtloop.h b/include/wx/evtloop.h index 52084d997d..2abbd9cef4 100644 --- a/include/wx/evtloop.h +++ b/include/wx/evtloop.h @@ -100,7 +100,15 @@ public: bool IsRunning() const; // exit from the loop with the given exit code - virtual void Exit(int rc = 0) = 0; + // + // this can be only used to exit the currently running loop, use + // ScheduleExit() if this might not be the case + virtual void Exit(int rc = 0); + + // ask the event loop to exit with the given exit code, can be used even if + // this loop is not running right now but the loop must have been started, + // i.e. Run() should have been already called + virtual void ScheduleExit(int rc = 0) = 0; // return true if any events are available virtual bool Pending() const = 0; @@ -180,6 +188,12 @@ protected: // an exception thrown from inside the loop) virtual void OnExit(); + // Return true if we're currently inside our Run(), even if another nested + // event loop is currently running, unlike IsRunning() (which should have + // been really called IsActive() but it's too late to change this now). + bool IsInsideRun() const { return m_isInsideRun; } + + // the pointer to currently active loop static wxEventLoopBase *ms_activeLoop; @@ -190,6 +204,10 @@ protected: bool m_isInsideYield; long m_eventsToProcessInsideYield; +private: + // this flag is set on entry into Run() and reset before leaving it + bool m_isInsideRun; + wxDECLARE_NO_COPY_CLASS(wxEventLoopBase); }; @@ -206,7 +224,7 @@ public: // sets the "should exit" flag and wakes up the loop so that it terminates // soon - virtual void Exit(int rc = 0); + virtual void ScheduleExit(int rc = 0); protected: // enters a loop calling OnNextIteration(), Pending() and Dispatch() and @@ -291,7 +309,7 @@ public: } #endif // wxUSE_EVENTLOOP_SOURCE - virtual void Exit(int rc = 0); + virtual void ScheduleExit(int rc = 0); virtual bool Pending() const; virtual bool Dispatch(); virtual int DispatchTimeout(unsigned long timeout) diff --git a/include/wx/gtk/evtloop.h b/include/wx/gtk/evtloop.h index d7f5205f37..af4d00348d 100644 --- a/include/wx/gtk/evtloop.h +++ b/include/wx/gtk/evtloop.h @@ -22,7 +22,7 @@ class WXDLLIMPEXP_CORE wxGUIEventLoop : public wxEventLoopBase public: wxGUIEventLoop(); - virtual void Exit(int rc = 0); + virtual void ScheduleExit(int rc = 0); virtual bool Pending() const; virtual bool Dispatch(); virtual int DispatchTimeout(unsigned long timeout); diff --git a/include/wx/osx/core/evtloop.h b/include/wx/osx/core/evtloop.h index 02830f8c40..862a4d9d54 100644 --- a/include/wx/osx/core/evtloop.h +++ b/include/wx/osx/core/evtloop.h @@ -26,7 +26,7 @@ public: // sets the "should exit" flag and wakes up the loop so that it terminates // soon - virtual void Exit(int rc = 0); + virtual void ScheduleExit(int rc = 0); // return true if any events are available virtual bool Pending() const; diff --git a/interface/wx/evtloop.h b/interface/wx/evtloop.h index 4e42be0dc4..c9c6e6cb42 100644 --- a/interface/wx/evtloop.h +++ b/interface/wx/evtloop.h @@ -23,6 +23,19 @@ You can create your own event loop if you need, provided that you restore the main event loop once yours is destroyed (see wxEventLoopActivator). + Notice that there can be more than one event loop at any given moment, e.g. + an event handler called from the main loop can show a modal dialog, which + starts its own loop resulting in two nested loops, with the modal dialog + being the active one (its IsRunning() returns @true). And a handler for a + button inside the modal dialog can, of course, create another modal dialog + with its own event loop and so on. So in general event loops form a stack + and only the event loop at the top of the stack is considered to be active. + It is also the only loop that can be directly asked to terminate by calling + Exit() (which is done by wxDialog::EndModal()), an outer event loop can't + be stopped while an inner one is still running. It is however possible to + ask an outer event loop to terminate as soon as all its nested loops exit + and the control returns back to it by using ScheduleExit(). + @library{wxbase} @category{appmanagement} @@ -90,9 +103,32 @@ public: virtual bool IsOk() const; /** - Exit from the loop with the given exit code. + Exit the currently running loop with the given exit code. + + The loop will exit, i.e. its Run() method will return, during the next + event loop iteration. + + Notice that this method can only be used if this event loop is the + currently running one, i.e. its IsRunning() returns @true. If this is + not the case, an assert failure is triggered and nothing is done as + outer event loops can't be exited from immediately. Use ScheduleExit() + if you'd like to exit this loop even if it doesn't run currently. */ - virtual void Exit(int rc = 0) = 0; + virtual void Exit(int rc = 0); + + /** + Schedule an exit from the loop with the given exit code. + + This method is similar to Exit() but can be called even if this event + loop is not the currently running one -- and if it is the active loop, + then it works in exactly the same way as Exit(). + + The loop will exit as soon as the control flow returns to it, i.e. + after any nested loops terminate. + + @since 2.9.5 + */ + virtual void ScheduleExit(int rc = 0) = 0; /** Return true if any events are available. diff --git a/src/cocoa/evtloop.mm b/src/cocoa/evtloop.mm index c73a2307d1..4ec9e52ace 100644 --- a/src/cocoa/evtloop.mm +++ b/src/cocoa/evtloop.mm @@ -39,9 +39,9 @@ int wxGUIEventLoop::DoRun() return m_exitcode; } -void wxGUIEventLoop::Exit(int rc) +void wxGUIEventLoop::ScheduleExit(int rc) { - wxCHECK_RET( IsRunning(), wxT("can't call Exit() if not running") ); + wxCHECK_RET( IsInsideRun(), wxT("can't call ScheduleExit() if not started") ); m_exitcode = rc; diff --git a/src/common/evtloopcmn.cpp b/src/common/evtloopcmn.cpp index 2f922c248d..da0b79fba4 100644 --- a/src/common/evtloopcmn.cpp +++ b/src/common/evtloopcmn.cpp @@ -22,6 +22,8 @@ #include "wx/app.h" #endif //WX_PRECOMP +#include "wx/scopeguard.h" + // ---------------------------------------------------------------------------- // wxEventLoopBase // ---------------------------------------------------------------------------- @@ -30,6 +32,7 @@ wxEventLoopBase *wxEventLoopBase::ms_activeLoop = NULL; wxEventLoopBase::wxEventLoopBase() { + m_isInsideRun = false; m_shouldExit = false; m_isInsideYield = false; @@ -55,7 +58,7 @@ void wxEventLoopBase::SetActive(wxEventLoopBase* loop) int wxEventLoopBase::Run() { // event loops are not recursive, you need to create another loop! - wxCHECK_MSG( !IsRunning(), -1, wxT("can't reenter a message loop") ); + wxCHECK_MSG( !IsInsideRun(), -1, wxT("can't reenter a message loop") ); // ProcessIdle() and ProcessEvents() below may throw so the code here should // be exception-safe, hence we must use local objects for all actions we @@ -66,10 +69,21 @@ int wxEventLoopBase::Run() // reset this flag. m_shouldExit = false; + // Set this variable to true for the duration of this method. + m_isInsideRun = true; + wxON_BLOCK_EXIT_SET(m_isInsideRun, false); + // Finally really run the loop. return DoRun(); } +void wxEventLoopBase::Exit(int rc) +{ + wxCHECK_RET( IsRunning(), wxS("Use ScheduleExit() on not running loop") ); + + ScheduleExit(rc); +} + void wxEventLoopBase::OnExit() { if (wxTheApp) @@ -231,9 +245,9 @@ int wxEventLoopManual::DoRun() return m_exitcode; } -void wxEventLoopManual::Exit(int rc) +void wxEventLoopManual::ScheduleExit(int rc) { - wxCHECK_RET( IsRunning(), wxT("can't call Exit() if not running") ); + wxCHECK_RET( IsInsideRun(), wxT("can't call ScheduleExit() if not running") ); m_exitcode = rc; m_shouldExit = true; diff --git a/src/gtk/evtloop.cpp b/src/gtk/evtloop.cpp index 68f65545e4..d104d82f0c 100644 --- a/src/gtk/evtloop.cpp +++ b/src/gtk/evtloop.cpp @@ -52,19 +52,41 @@ wxGUIEventLoop::wxGUIEventLoop() int wxGUIEventLoop::DoRun() { - gtk_main(); + guint loopLevel = gtk_main_level(); + + // This is placed inside of a loop to take into account nested + // event loops. For example, inside this event loop, we may receive + // Exit() for a different event loop (which we are currently inside of) + // That Exit() will cause this gtk_main() to exit so we need to re-enter it. + while ( !m_shouldExit ) + { + gtk_main(); + } + + // Force the enclosing event loop to also exit to see if it is done in case + // that event loop had Exit() called inside of the just ended loop. If it + // is not time yet for that event loop to exit, it will be executed again + // due to the while() loop on m_shouldExit(). + // + // This is unnecessary if we are the top level loop, i.e. loop of level 0. + if ( loopLevel ) + { + gtk_main_quit(); + } OnExit(); return m_exitcode; } -void wxGUIEventLoop::Exit(int rc) +void wxGUIEventLoop::ScheduleExit(int rc) { - wxCHECK_RET( IsRunning(), "can't call Exit() if not running" ); + wxCHECK_RET( IsInsideRun(), wxT("can't call ScheduleExit() if not started") ); m_exitcode = rc; + m_shouldExit = true; + gtk_main_quit(); } diff --git a/src/gtk1/evtloop.cpp b/src/gtk1/evtloop.cpp index 5f4a91f995..b66c7015cb 100644 --- a/src/gtk1/evtloop.cpp +++ b/src/gtk1/evtloop.cpp @@ -69,7 +69,27 @@ int wxGUIEventLoop::DoRun() { m_impl = new wxEventLoopImpl; - gtk_main(); + guint loopLevel = gtk_main_level(); + + // This is placed inside of a loop to take into account nested + // event loops. For example, inside this event loop, we may recieve + // Exit() for a different event loop (which we are currently inside of) + // That Exit() will cause this gtk_main() to exit so we need to re-enter it. + while ( !m_shouldExit ) + { + gtk_main(); + } + + // Force the enclosing event loop to also exit to see if it is done + // in case that event loop ended inside of this one. If it is not time + // yet for that event loop to exit, it will be executed again due to + // the while() loop on m_shouldExit(). + // + // This is unnecessary if we are the top level loop, i.e. loop of level 0. + if ( loopLevel ) + { + gtk_main_quit(); + } OnExit(); @@ -79,12 +99,14 @@ int wxGUIEventLoop::DoRun() return exitcode; } -void wxGUIEventLoop::Exit(int rc) +void wxGUIEventLoop::ScheduleExit(int rc) { - wxCHECK_RET( IsRunning(), wxT("can't call Exit() if not running") ); + wxCHECK_RET( IsInsideRun(), wxT("can't call ScheduleExit() if not started") ); m_impl->SetExitCode(rc); + m_shouldExit = true; + gtk_main_quit(); } diff --git a/src/motif/evtloop.cpp b/src/motif/evtloop.cpp index 7ec41cd123..c2b0f367e4 100644 --- a/src/motif/evtloop.cpp +++ b/src/motif/evtloop.cpp @@ -121,9 +121,9 @@ int wxGUIEventLoop::DoRun() return exitcode; } -void wxGUIEventLoop::Exit(int rc) +void wxGUIEventLoop::SchduleExit(int rc) { - wxCHECK_RET( IsRunning(), wxT("can't call Exit() if not running") ); + wxCHECK_RET( IsInsideRun(), wxT("can't call ScheduleExit() if not started") ); m_impl->SetExitCode(rc); m_impl->SetKeepGoing( false ); diff --git a/src/osx/carbon/evtloop.cpp b/src/osx/carbon/evtloop.cpp index d064a8dbd1..288210641b 100644 --- a/src/osx/carbon/evtloop.cpp +++ b/src/osx/carbon/evtloop.cpp @@ -96,7 +96,15 @@ void wxGUIEventLoop::WakeUp() void wxGUIEventLoop::OSXDoRun() { wxMacAutoreleasePool autoreleasepool; - RunApplicationEventLoop(); + + while (!m_shouldExit) + { + RunApplicationEventLoop(); + } + + // Force enclosing event loop to temporarily exit and check + // if it should be stopped. + QuitApplicationEventLoop(); } void wxGUIEventLoop::OSXDoStop() diff --git a/src/osx/cocoa/evtloop.mm b/src/osx/cocoa/evtloop.mm index 00a3008dbc..a4d6cc1239 100644 --- a/src/osx/cocoa/evtloop.mm +++ b/src/osx/cocoa/evtloop.mm @@ -32,6 +32,7 @@ #endif // WX_PRECOMP #include "wx/log.h" +#include "wx/scopeguard.h" #include "wx/osx/private.h" @@ -240,17 +241,113 @@ int wxGUIEventLoop::DoDispatchTimeout(unsigned long timeout) } } +static int gs_loopNestingLevel = 0; + void wxGUIEventLoop::OSXDoRun() { - wxMacAutoreleasePool autoreleasepool; - [NSApp run]; + /* + In order to properly nest GUI event loops in Cocoa, it is important to + have [NSApp run] only as the main/outermost event loop. There are many + problems if [NSApp run] is used as an inner event loop. The main issue + is that a call to [NSApp stop] is needed to exit an [NSApp run] event + loop. But the [NSApp stop] has some side effects that we do not want - + such as if there was a modal dialog box with a modal event loop running, + that event loop would also get exited, and the dialog would be closed. + The call to [NSApp stop] would also cause the enclosing event loop to + exit as well. + + webkit's webcore library uses CFRunLoopRun() for nested event loops. See + the log of the commit log about the change in webkit's webcore module: + http://www.mail-archive.com/webkit-changes@lists.webkit.org/msg07397.html + + See here for the latest run loop that is used in webcore: + https://github.com/WebKit/webkit/blob/master/Source/WebCore/platform/mac/RunLoopMac.mm + + CFRunLoopRun() was tried for the nested event loop here but it causes a + problem in that all user input is disabled - and there is no way to + re-enable it. The caller of this event loop may not want user input + disabled (such as synchronous wxExecute with wxEXEC_NODISABLE flag). + + In order to have an inner event loop where user input can be enabled, + the old wxCocoa code that used the [NSApp nextEventMatchingMask] was + borrowed but changed to use blocking instead of polling. By specifying + 'distantFuture' in 'untildate', we can have it block until the next + event. Then we can keep looping on each new event until m_shouldExit is + raised to exit the event loop. + */ + gs_loopNestingLevel++; + wxON_BLOCK_EXIT_SET(gs_loopNestingLevel, gs_loopNestingLevel - 1); + + while ( !m_shouldExit ) + { + // By putting this inside the loop, we can drain it in each + // loop iteration. + wxMacAutoreleasePool autoreleasepool; + + if ( gs_loopNestingLevel == 1 ) + { + // Use -[NSApplication run] for the main run loop. + [NSApp run]; + } + else + { + // We use this blocking call to [NSApp nextEventMatchingMask:...] + // because the other methods (such as CFRunLoopRun() and [runLoop + // runMode:beforeDate] were always disabling input to the windows + // (even if we wanted it enabled). + // + // Here are the other run loops which were tried, but always left + // user input disabled: + // + // [runLoop runMode:NSDefaultRunLoopMode beforeDate:date]; + // CFRunLoopRun(); + // CFRunLoopRunInMode(kCFRunLoopDefaultMode, 10 , true); + // + // Using [NSApp nextEventMatchingMask:...] would leave windows + // enabled if we wanted them to be, so that is why it is used. + NSEvent *event = [NSApp + nextEventMatchingMask:NSAnyEventMask + untilDate:[NSDate distantFuture] + inMode:NSDefaultRunLoopMode + dequeue: YES]; + + [NSApp sendEvent: event]; + + /** + The NSApplication documentation states that: + + " + This method is invoked automatically in the main event loop + after each event when running in NSDefaultRunLoopMode or + NSModalRunLoopMode. This method is not invoked automatically + when running in NSEventTrackingRunLoopMode. + " + + So to be safe, we also invoke it here in this event loop. + + See: https://developer.apple.com/library/mac/#documentation/Cocoa/Reference/ApplicationKit/Classes/NSApplication_Class/Reference/Reference.html + */ + [NSApp updateWindows]; + } + } + + // Wake up the enclosing loop so that it can check if it also needs + // to exit. + WakeUp(); } void wxGUIEventLoop::OSXDoStop() { - // only calling stop: is not enough when called from a runloop-observer, - // therefore add a dummy event, to make sure the runloop gets another round - [NSApp stop:0]; + // We should only stop the top level event loop. + if ( gs_loopNestingLevel <= 1 ) + { + [NSApp stop:0]; + } + + // For the top level loop only calling stop: is not enough when called from + // a runloop-observer, therefore add a dummy event, to make sure the + // runloop gets another round. And for the nested loops we need to wake it + // up to notice that it should exit, so do this unconditionally. WakeUp(); } diff --git a/src/osx/core/evtloop_cf.cpp b/src/osx/core/evtloop_cf.cpp index 5ef10ae432..12ecb2c872 100644 --- a/src/osx/core/evtloop_cf.cpp +++ b/src/osx/core/evtloop_cf.cpp @@ -440,7 +440,7 @@ int wxCFEventLoop::DoRun() // sets the "should exit" flag and wakes up the loop so that it terminates // soon -void wxCFEventLoop::Exit(int rc) +void wxCFEventLoop::ScheduleExit(int rc) { m_exitcode = rc; m_shouldExit = true; diff --git a/src/x11/evtloop.cpp b/src/x11/evtloop.cpp index 1f988f9762..e351092267 100644 --- a/src/x11/evtloop.cpp +++ b/src/x11/evtloop.cpp @@ -162,7 +162,7 @@ int wxGUIEventLoop::DoRun() return exitcode; } -void wxGUIEventLoop::Exit(int rc) +void wxGUIEventLoop::ScheduleExit(int rc) { if ( m_impl ) { diff --git a/tests/Makefile.in b/tests/Makefile.in index 0c786be59e..14da03c03c 100644 --- a/tests/Makefile.in +++ b/tests/Makefile.in @@ -65,6 +65,7 @@ TEST_OBJECTS = \ test_regconf.o \ test_datetimetest.o \ test_evthandler.o \ + test_evtlooptest.o \ test_evtsource.o \ test_stopwatch.o \ test_timertest.o \ @@ -208,6 +209,7 @@ TEST_GUI_OBJECTS = \ test_gui_windowtest.o \ test_gui_dialogtest.o \ test_gui_clone.o \ + test_gui_evtlooptest.o \ test_gui_propagation.o \ test_gui_keyboard.o \ test_gui_fonttest.o \ @@ -476,6 +478,9 @@ test_datetimetest.o: $(srcdir)/datetime/datetimetest.cpp $(TEST_ODEP) test_evthandler.o: $(srcdir)/events/evthandler.cpp $(TEST_ODEP) $(CXXC) -c -o $@ $(TEST_CXXFLAGS) $(srcdir)/events/evthandler.cpp +test_evtlooptest.o: $(srcdir)/events/evtlooptest.cpp $(TEST_ODEP) + $(CXXC) -c -o $@ $(TEST_CXXFLAGS) $(srcdir)/events/evtlooptest.cpp + test_evtsource.o: $(srcdir)/events/evtsource.cpp $(TEST_ODEP) $(CXXC) -c -o $@ $(TEST_CXXFLAGS) $(srcdir)/events/evtsource.cpp @@ -881,6 +886,9 @@ test_gui_dialogtest.o: $(srcdir)/controls/dialogtest.cpp $(TEST_GUI_ODEP) test_gui_clone.o: $(srcdir)/events/clone.cpp $(TEST_GUI_ODEP) $(CXXC) -c -o $@ $(TEST_GUI_CXXFLAGS) $(srcdir)/events/clone.cpp +test_gui_evtlooptest.o: $(srcdir)/events/evtlooptest.cpp $(TEST_GUI_ODEP) + $(CXXC) -c -o $@ $(TEST_GUI_CXXFLAGS) $(srcdir)/events/evtlooptest.cpp + test_gui_propagation.o: $(srcdir)/events/propagation.cpp $(TEST_GUI_ODEP) $(CXXC) -c -o $@ $(TEST_GUI_CXXFLAGS) $(srcdir)/events/propagation.cpp diff --git a/tests/events/evtlooptest.cpp b/tests/events/evtlooptest.cpp new file mode 100644 index 0000000000..a75014d1ce --- /dev/null +++ b/tests/events/evtlooptest.cpp @@ -0,0 +1,128 @@ +/////////////////////////////////////////////////////////////////////////////// +// Name: tests/events/evtloop.cpp +// Purpose: Tests for the event loop classes +// Author: Rob Bresalier +// Created: 2013-05-02 +// RCS-ID: $Id$ +// Copyright: (c) 2013 Rob Bresalier +/////////////////////////////////////////////////////////////////////////////// + +// ---------------------------------------------------------------------------- +// headers +// ---------------------------------------------------------------------------- + +#include "testprec.h" + +#ifdef __BORLANDC__ + #pragma hdrstop +#endif + +#include "wx/timer.h" + +// ---------------------------------------------------------------------------- +// constants +// ---------------------------------------------------------------------------- + +// Use two arbitrary but different return codes for the two loops. +const int EXIT_CODE_OUTER_LOOP = 99; +const int EXIT_CODE_INNER_LOOP = 55; + +// ---------------------------------------------------------------------------- +// test class +// ---------------------------------------------------------------------------- + +class EvtloopTestCase : public CppUnit::TestCase +{ +public: + EvtloopTestCase() { } + +private: + CPPUNIT_TEST_SUITE( EvtloopTestCase ); + CPPUNIT_TEST( TestExit ); + CPPUNIT_TEST_SUITE_END(); + + void TestExit(); + + DECLARE_NO_COPY_CLASS(EvtloopTestCase) +}; + +// register in the unnamed registry so that these tests are run by default +CPPUNIT_TEST_SUITE_REGISTRATION( EvtloopTestCase ); + +// also include in its own registry so that these tests can be run alone +CPPUNIT_TEST_SUITE_NAMED_REGISTRATION( EvtloopTestCase, "EvtloopTestCase" ); + + +// Helper class to schedule exit of the given event loop after the specified +// delay. +class ScheduleLoopExitTimer : public wxTimer +{ +public: + ScheduleLoopExitTimer(wxEventLoop& loop, int rc) + : m_loop(loop), + m_rc(rc) + { + } + + virtual void Notify() + { + m_loop.ScheduleExit(m_rc); + } + +private: + wxEventLoop& m_loop; + const int m_rc; +}; + +// Another helper which runs a nested loop and schedules exiting both the outer +// and the inner loop after the specified delays. +class RunNestedAndExitBothLoopsTimer : public wxTimer +{ +public: + RunNestedAndExitBothLoopsTimer(wxTimer& timerOuter, + int loopOuterDuration, + int loopInnerDuration) + : m_timerOuter(timerOuter), + m_loopOuterDuration(loopOuterDuration), + m_loopInnerDuration(loopInnerDuration) + { + } + + virtual void Notify() + { + wxEventLoop loopInner; + ScheduleLoopExitTimer timerInner(loopInner, EXIT_CODE_INNER_LOOP); + + m_timerOuter.StartOnce(m_loopOuterDuration); + timerInner.StartOnce(m_loopInnerDuration); + + CPPUNIT_ASSERT_EQUAL( EXIT_CODE_INNER_LOOP, loopInner.Run() ); + } + +private: + wxTimer& m_timerOuter; + const int m_loopOuterDuration; + const int m_loopInnerDuration; +}; + +void EvtloopTestCase::TestExit() +{ + // Test that simply exiting the loop works. + wxEventLoop loopOuter; + ScheduleLoopExitTimer timerExit(loopOuter, EXIT_CODE_OUTER_LOOP); + timerExit.StartOnce(1); + CPPUNIT_ASSERT_EQUAL( EXIT_CODE_OUTER_LOOP, loopOuter.Run() ); + + // Test that exiting the outer loop before the inner loop (outer duration + // parameter less than inner duration in the timer ctor below) works. + ScheduleLoopExitTimer timerExitOuter(loopOuter, EXIT_CODE_OUTER_LOOP); + RunNestedAndExitBothLoopsTimer timerRun(timerExitOuter, 5, 10); + timerRun.StartOnce(1); + CPPUNIT_ASSERT_EQUAL( EXIT_CODE_OUTER_LOOP, loopOuter.Run() ); + + // Test that exiting the inner loop before the outer one works too. + ScheduleLoopExitTimer timerExitOuter2(loopOuter, EXIT_CODE_OUTER_LOOP); + RunNestedAndExitBothLoopsTimer timerRun2(timerExitOuter, 10, 5); + timerRun2.StartOnce(1); + CPPUNIT_ASSERT_EQUAL( EXIT_CODE_OUTER_LOOP, loopOuter.Run() ); +} diff --git a/tests/makefile.bcc b/tests/makefile.bcc index 331b2563cb..4013df583e 100644 --- a/tests/makefile.bcc +++ b/tests/makefile.bcc @@ -50,6 +50,7 @@ TEST_OBJECTS = \ $(OBJS)\test_regconf.obj \ $(OBJS)\test_datetimetest.obj \ $(OBJS)\test_evthandler.obj \ + $(OBJS)\test_evtlooptest.obj \ $(OBJS)\test_evtsource.obj \ $(OBJS)\test_stopwatch.obj \ $(OBJS)\test_timertest.obj \ @@ -194,6 +195,7 @@ TEST_GUI_OBJECTS = \ $(OBJS)\test_gui_windowtest.obj \ $(OBJS)\test_gui_dialogtest.obj \ $(OBJS)\test_gui_clone.obj \ + $(OBJS)\test_gui_evtlooptest.obj \ $(OBJS)\test_gui_propagation.obj \ $(OBJS)\test_gui_keyboard.obj \ $(OBJS)\test_gui_fonttest.obj \ @@ -531,6 +533,9 @@ $(OBJS)\test_datetimetest.obj: .\datetime\datetimetest.cpp $(OBJS)\test_evthandler.obj: .\events\evthandler.cpp $(CXX) -q -c -P -o$@ $(TEST_CXXFLAGS) .\events\evthandler.cpp +$(OBJS)\test_evtlooptest.obj: .\events\evtlooptest.cpp + $(CXX) -q -c -P -o$@ $(TEST_CXXFLAGS) .\events\evtlooptest.cpp + $(OBJS)\test_evtsource.obj: .\events\evtsource.cpp $(CXX) -q -c -P -o$@ $(TEST_CXXFLAGS) .\events\evtsource.cpp @@ -939,6 +944,9 @@ $(OBJS)\test_gui_dialogtest.obj: .\controls\dialogtest.cpp $(OBJS)\test_gui_clone.obj: .\events\clone.cpp $(CXX) -q -c -P -o$@ $(TEST_GUI_CXXFLAGS) .\events\clone.cpp +$(OBJS)\test_gui_evtlooptest.obj: .\events\evtlooptest.cpp + $(CXX) -q -c -P -o$@ $(TEST_GUI_CXXFLAGS) .\events\evtlooptest.cpp + $(OBJS)\test_gui_propagation.obj: .\events\propagation.cpp $(CXX) -q -c -P -o$@ $(TEST_GUI_CXXFLAGS) .\events\propagation.cpp diff --git a/tests/makefile.gcc b/tests/makefile.gcc index b64a38ad07..875dd6c0c9 100644 --- a/tests/makefile.gcc +++ b/tests/makefile.gcc @@ -43,6 +43,7 @@ TEST_OBJECTS = \ $(OBJS)\test_regconf.o \ $(OBJS)\test_datetimetest.o \ $(OBJS)\test_evthandler.o \ + $(OBJS)\test_evtlooptest.o \ $(OBJS)\test_evtsource.o \ $(OBJS)\test_stopwatch.o \ $(OBJS)\test_timertest.o \ @@ -188,6 +189,7 @@ TEST_GUI_OBJECTS = \ $(OBJS)\test_gui_windowtest.o \ $(OBJS)\test_gui_dialogtest.o \ $(OBJS)\test_gui_clone.o \ + $(OBJS)\test_gui_evtlooptest.o \ $(OBJS)\test_gui_propagation.o \ $(OBJS)\test_gui_keyboard.o \ $(OBJS)\test_gui_fonttest.o \ @@ -520,6 +522,9 @@ $(OBJS)\test_datetimetest.o: ./datetime/datetimetest.cpp $(OBJS)\test_evthandler.o: ./events/evthandler.cpp $(CXX) -c -o $@ $(TEST_CXXFLAGS) $(CPPDEPS) $< +$(OBJS)\test_evtlooptest.o: ./events/evtlooptest.cpp + $(CXX) -c -o $@ $(TEST_CXXFLAGS) $(CPPDEPS) $< + $(OBJS)\test_evtsource.o: ./events/evtsource.cpp $(CXX) -c -o $@ $(TEST_CXXFLAGS) $(CPPDEPS) $< @@ -928,6 +933,9 @@ $(OBJS)\test_gui_dialogtest.o: ./controls/dialogtest.cpp $(OBJS)\test_gui_clone.o: ./events/clone.cpp $(CXX) -c -o $@ $(TEST_GUI_CXXFLAGS) $(CPPDEPS) $< +$(OBJS)\test_gui_evtlooptest.o: ./events/evtlooptest.cpp + $(CXX) -c -o $@ $(TEST_GUI_CXXFLAGS) $(CPPDEPS) $< + $(OBJS)\test_gui_propagation.o: ./events/propagation.cpp $(CXX) -c -o $@ $(TEST_GUI_CXXFLAGS) $(CPPDEPS) $< diff --git a/tests/makefile.vc b/tests/makefile.vc index 4054a2f575..09066b2462 100644 --- a/tests/makefile.vc +++ b/tests/makefile.vc @@ -44,6 +44,7 @@ TEST_OBJECTS = \ $(OBJS)\test_regconf.obj \ $(OBJS)\test_datetimetest.obj \ $(OBJS)\test_evthandler.obj \ + $(OBJS)\test_evtlooptest.obj \ $(OBJS)\test_evtsource.obj \ $(OBJS)\test_stopwatch.obj \ $(OBJS)\test_timertest.obj \ @@ -191,6 +192,7 @@ TEST_GUI_OBJECTS = \ $(OBJS)\test_gui_windowtest.obj \ $(OBJS)\test_gui_dialogtest.obj \ $(OBJS)\test_gui_clone.obj \ + $(OBJS)\test_gui_evtlooptest.obj \ $(OBJS)\test_gui_propagation.obj \ $(OBJS)\test_gui_keyboard.obj \ $(OBJS)\test_gui_fonttest.obj \ @@ -671,6 +673,9 @@ $(OBJS)\test_datetimetest.obj: .\datetime\datetimetest.cpp $(OBJS)\test_evthandler.obj: .\events\evthandler.cpp $(CXX) /c /nologo /TP /Fo$@ $(TEST_CXXFLAGS) .\events\evthandler.cpp +$(OBJS)\test_evtlooptest.obj: .\events\evtlooptest.cpp + $(CXX) /c /nologo /TP /Fo$@ $(TEST_CXXFLAGS) .\events\evtlooptest.cpp + $(OBJS)\test_evtsource.obj: .\events\evtsource.cpp $(CXX) /c /nologo /TP /Fo$@ $(TEST_CXXFLAGS) .\events\evtsource.cpp @@ -1079,6 +1084,9 @@ $(OBJS)\test_gui_dialogtest.obj: .\controls\dialogtest.cpp $(OBJS)\test_gui_clone.obj: .\events\clone.cpp $(CXX) /c /nologo /TP /Fo$@ $(TEST_GUI_CXXFLAGS) .\events\clone.cpp +$(OBJS)\test_gui_evtlooptest.obj: .\events\evtlooptest.cpp + $(CXX) /c /nologo /TP /Fo$@ $(TEST_GUI_CXXFLAGS) .\events\evtlooptest.cpp + $(OBJS)\test_gui_propagation.obj: .\events\propagation.cpp $(CXX) /c /nologo /TP /Fo$@ $(TEST_GUI_CXXFLAGS) .\events\propagation.cpp diff --git a/tests/makefile.wat b/tests/makefile.wat index 88a3f23823..505bfa3232 100644 --- a/tests/makefile.wat +++ b/tests/makefile.wat @@ -319,6 +319,7 @@ TEST_OBJECTS = & $(OBJS)\test_regconf.obj & $(OBJS)\test_datetimetest.obj & $(OBJS)\test_evthandler.obj & + $(OBJS)\test_evtlooptest.obj & $(OBJS)\test_evtsource.obj & $(OBJS)\test_stopwatch.obj & $(OBJS)\test_timertest.obj & @@ -463,6 +464,7 @@ TEST_GUI_OBJECTS = & $(OBJS)\test_gui_windowtest.obj & $(OBJS)\test_gui_dialogtest.obj & $(OBJS)\test_gui_clone.obj & + $(OBJS)\test_gui_evtlooptest.obj & $(OBJS)\test_gui_propagation.obj & $(OBJS)\test_gui_keyboard.obj & $(OBJS)\test_gui_fonttest.obj & @@ -580,6 +582,9 @@ $(OBJS)\test_datetimetest.obj : .AUTODEPEND .\datetime\datetimetest.cpp $(OBJS)\test_evthandler.obj : .AUTODEPEND .\events\evthandler.cpp $(CXX) -bt=nt -zq -fo=$^@ $(TEST_CXXFLAGS) $< +$(OBJS)\test_evtlooptest.obj : .AUTODEPEND .\events\evtlooptest.cpp + $(CXX) -bt=nt -zq -fo=$^@ $(TEST_CXXFLAGS) $< + $(OBJS)\test_evtsource.obj : .AUTODEPEND .\events\evtsource.cpp $(CXX) -bt=nt -zq -fo=$^@ $(TEST_CXXFLAGS) $< @@ -988,6 +993,9 @@ $(OBJS)\test_gui_dialogtest.obj : .AUTODEPEND .\controls\dialogtest.cpp $(OBJS)\test_gui_clone.obj : .AUTODEPEND .\events\clone.cpp $(CXX) -bt=nt -zq -fo=$^@ $(TEST_GUI_CXXFLAGS) $< +$(OBJS)\test_gui_evtlooptest.obj : .AUTODEPEND .\events\evtlooptest.cpp + $(CXX) -bt=nt -zq -fo=$^@ $(TEST_GUI_CXXFLAGS) $< + $(OBJS)\test_gui_propagation.obj : .AUTODEPEND .\events\propagation.cpp $(CXX) -bt=nt -zq -fo=$^@ $(TEST_GUI_CXXFLAGS) $< diff --git a/tests/test.bkl b/tests/test.bkl index 4fc52db5a8..afe4637ef0 100644 --- a/tests/test.bkl +++ b/tests/test.bkl @@ -40,6 +40,7 @@ config/regconf.cpp datetime/datetimetest.cpp events/evthandler.cpp + events/evtlooptest.cpp events/evtsource.cpp events/stopwatch.cpp events/timertest.cpp @@ -189,6 +190,10 @@ controls/windowtest.cpp controls/dialogtest.cpp events/clone.cpp + + events/evtlooptest.cpp events/propagation.cpp events/keyboard.cpp font/fonttest.cpp diff --git a/tests/test_test.dsp b/tests/test_test.dsp index f1e68bec39..287a816edc 100644 --- a/tests/test_test.dsp +++ b/tests/test_test.dsp @@ -205,6 +205,10 @@ SOURCE=.\events\evthandler.cpp # End Source File # Begin Source File +SOURCE=.\events\evtlooptest.cpp +# End Source File +# Begin Source File + SOURCE=.\events\evtsource.cpp # End Source File # Begin Source File diff --git a/tests/test_test_gui.dsp b/tests/test_test_gui.dsp index 2569507311..14a6c42d77 100644 --- a/tests/test_test_gui.dsp +++ b/tests/test_test_gui.dsp @@ -229,6 +229,10 @@ SOURCE=.\graphics\ellipsization.cpp # End Source File # Begin Source File +SOURCE=.\events\evtlooptest.cpp +# End Source File +# Begin Source File + SOURCE=.\font\fonttest.cpp # End Source File # Begin Source File diff --git a/tests/test_vc7_test.vcproj b/tests/test_vc7_test.vcproj index 5a0bd705a8..06d87811a1 100644 --- a/tests/test_vc7_test.vcproj +++ b/tests/test_vc7_test.vcproj @@ -370,6 +370,9 @@ + + diff --git a/tests/test_vc7_test_gui.vcproj b/tests/test_vc7_test_gui.vcproj index f3345afef8..012dcdaf5a 100644 --- a/tests/test_vc7_test_gui.vcproj +++ b/tests/test_vc7_test_gui.vcproj @@ -388,6 +388,9 @@ + + diff --git a/tests/test_vc8_test.vcproj b/tests/test_vc8_test.vcproj index 9fb69ed0ae..c5e36fc801 100644 --- a/tests/test_vc8_test.vcproj +++ b/tests/test_vc8_test.vcproj @@ -529,6 +529,10 @@ RelativePath=".\events\evthandler.cpp" > + + diff --git a/tests/test_vc8_test_gui.vcproj b/tests/test_vc8_test_gui.vcproj index a4bd5961b8..a9abb0833f 100644 --- a/tests/test_vc8_test_gui.vcproj +++ b/tests/test_vc8_test_gui.vcproj @@ -553,6 +553,10 @@ RelativePath=".\graphics\ellipsization.cpp" > + + diff --git a/tests/test_vc9_test.vcproj b/tests/test_vc9_test.vcproj index 3324157e51..f1ca266e4f 100644 --- a/tests/test_vc9_test.vcproj +++ b/tests/test_vc9_test.vcproj @@ -515,6 +515,10 @@ RelativePath=".\events\evthandler.cpp" > + + diff --git a/tests/test_vc9_test_gui.vcproj b/tests/test_vc9_test_gui.vcproj index 746fd5d0a2..640de7a67d 100644 --- a/tests/test_vc9_test_gui.vcproj +++ b/tests/test_vc9_test_gui.vcproj @@ -539,6 +539,10 @@ RelativePath=".\graphics\ellipsization.cpp" > + +