Opened 9 years ago

Closed 8 years ago

#12703 closed defect (fixed)

wxApp::OnInit and event handling

Reported by: ryazanov Owned by:
Priority: high Milestone: 2.9.3
Component: GUI-all Version: 2.9.0
Keywords: regression Cc: f18m_cpp217828@…
Blocked By: Blocking:
Patch: no

Description

There was an important change in version 2.9:

- Processing of pending events now requires a running event loop.
  Thus initialization code (e.g. showing a dialog) previously done in wxApp::OnRun()
  or equivalent function should now be done into wxApp::OnEventLoopEnter().
  See wxApp::OnEventLoopEnter() and wxApp::OnEventLoopExit() docs for more info.

however it is not reflected in OnInit() method documentation. There are some hints in wxAppConsole::GetMainLoop() and wxAppConsole::OnEventLoopEnter() descriptions, but there is not clue to look there.

Attachments (1)

test.cpp download (1.5 KB) - added by ryazanov 9 years ago.
End process detection in OnInit()

Download all attachments as: .zip

Change History (8)

comment:1 Changed 9 years ago by vadz

  • Cc f18m_cpp217828@… added
  • Status changed from new to infoneeded_new
  • Summary changed from wxApp...::OnInit and event handling to wxApp::OnInit and event handling

Is this note in docs/changes.txt really correct? I thought we (well, Francesco) restored the old behaviour to keep compatibility.

What exactly doesn't work when done in OnRun() instead of in OnEventLoopEnter() now?

comment:2 Changed 9 years ago by ryazanov

  • Status changed from infoneeded_new to new

End process detection during OnInit() does not work in wxGTK (but seems to work in wxMSW). I noticed that while debugging #12636. Namely, at some point I checked and saw that wxAppConsole::GetMainLoop() returns NULL. However, if logging with frequent calling of wxLog::FlushActive() was introduced, the end process event was processed from the event loop of the log dialog (according to debugger's backtrace).

What was the point to call OnInit() without a running event loop? If there is no real advantage, and the "old behaviour" was restored at least somewhere, please check whether it was restored everywhere.

And in any case the documentation needs to be brought in correspondence with the reality.

comment:3 Changed 9 years ago by vadz

  • Milestone set to 3.0

I think OnInit() was always called before starting the main event loop (which was done by OnRun()). I don't really know what would happen if we started it before OnInit() but I suspect this wouldn't be without some undesirable consequences...

How exactly does the end process detection fail to work BTW? For me it would be acceptable if the process termination were not detected before the main loop is started but not if it were lost entirely. What really happens?

Thanks!

P.S. Francesco, could you please confirm or deny that this note in docs/changes.txt is not needed any more? I'm afraid I didn't follow all changes here in details...

comment:4 Changed 9 years ago by ryazanov

  • Status changed from new to confirmed

I'm attaching a minimalistic example (test.cpp) to illustrate the end process detection problem. In wxW 2.8.11 the log looks like

22:03:15: Event loop = 00000000
22:03:15: Process started.
22:03:15: Waiting for 1324.
22:03:15: Waiting for 1324.
[cut]
22:03:15: Waiting for 1324.
22:03:15: Waiting for 1324.
22:03:15: Process 1324 terminated.
22:03:15: Process stopped.

In the current wxW version the program hangs in the loop

      while (pid) {
         wxLogMessage("Waiting for %ld.", pid);
         //wxLog::FlushActive();
         wxSafeYield();
      }

unless wxLog::FlushActive(); is enabled.

Actually, now I understand, that the problem is really not in the missing event loop (it was missing before, according to the log above), but in wxSafeYield(), which did call ProcessPendingEvents() in 2.8 and now does nothing, if there is no active event loop.

I don't think, that the process termination event is entirely lost, but for my purposes (running external programs to obtain some configuration data) I need it to be processed within the initialization code.

Anyway, please improve the documentation, making clear, that event processing does not work in OnInit() — this is not obvious!

Another question: "How to do it right?" I mean, initialization code, that requires events. The wxAppConsole::OnEventLoopEnter() documentation says

"this function is called whenever an event loop is activated; you may want to use wxEventLoopBase::IsMain() to perform initialization specific for the app's main event loop."

Does this "whenever activated" mean, that OnEventLoopEnter() might be called many times for the main loop (when some other loops are created and destroyed), so that the "initialization" code must maintain a flag in order to run only once?

Changed 9 years ago by ryazanov

End process detection in OnInit()

comment:5 Changed 9 years ago by Andrew.Smart

This defect might be related to #12930 "wxAppConsole abnormal termination when using event loop on user initiated app close".

comment:6 Changed 9 years ago by vadz

  • Component changed from documentation to GUI-all
  • Keywords regression added
  • Milestone changed from 3.0 to 2.9.3
  • Priority changed from normal to high

More I think about this, more I become certain that we need to actually dispatch the events when wxYield() is called instead of just silently doing nothing when there is no active event loop. IOW I think we need to create the event loop if necessary as I just don't see how can the user code deal with its absence in a sane manner otherwise.

And, indeed, doing something like this:

  • src/common/appbase.cpp

    a b bool wxAppConsoleBase::Dispatch() 
    345345bool wxAppConsoleBase::Yield(bool onlyIfNeeded)
    346346{
    347347    wxEventLoopBase * const loop = wxEventLoopBase::GetActive();
     348    if ( loop )
     349       return loop->Yield(onlyIfNeeded);
    348350
    349     return loop && loop->Yield(onlyIfNeeded);
     351    wxScopedPtr<wxEventLoopBase> tmpLoop(CreateMainLoop());
     352    return tmpLoop->Yield(onlyIfNeeded);
    350353}
    351354
    352355void wxAppConsoleBase::WakeUpIdle()

does solve the problem but it doesn't look like a good idea because we probably don't want to recreate the event loop on each wxYield() call. So we probably should create m_mainLoop on demand instead.

But this is a detail, the main question is whether there is any reason we can't create (without running it!) the main event loop before OnRun() is called. If nobody sees any reason not to do this, I'm going to change this as proposed.

comment:7 Changed 8 years ago by vadz

  • Resolution set to fixed
  • Status changed from confirmed to closed

Actually the patch from comment:6 was committed (possibly accidentally?) 7 months ago in r67619 so this should be already fixed.

The problem with (re)creating the event loop in Yield() remains but it's probably not that bad because event loop objects are relatively lightweight.

Note: See TracTickets for help on using tickets.