Avoid message queue flooding

If you are using the main C++ distribution of wxWidgets, Feel free to ask any question related to wxWidgets development here. This means questions regarding to C++ and wxWidgets, not compile problems.
Post Reply
heinermueller
Experienced Solver
Experienced Solver
Posts: 99
Joined: Sat Oct 26, 2013 11:54 am

Avoid message queue flooding

Post by heinermueller »

Hello everybody,

i want to avoid event queue flooding.

Imagine a timed repaint, that works nicely but sometimes cannot be handled immediately, while e.g. a dialog is opened, windows works on something unknown, a network drive is accessed etc... Then redraws (or other commands) pile up and many problems occur. Is is possible to access the message queue (wxPendingEvents in former times) and check the last issued but not handled - aka 'pending' event? Actually all that has to be avoided is issuing a multitude of consecutive paint events (not two consecutive paint events, this is perfectly fine, this means timed animation without user interaction)

In previous versions 2.8 there was wxPendingEvents, what is the wx 3.0 equivalent?

Best regards
Heiner
User avatar
doublemax
Moderator
Moderator
Posts: 19114
Joined: Fri Apr 21, 2006 8:03 pm
Location: $FCE2

Re: Avoid message queue flooding

Post by doublemax »

I don't know any way to access the event queue, but that definitely sounds like a bad idea. You should try to avoid flooding the queue in the first place.

How exactly do you trigger the repaints? Calling Refresh() should just mark the window as 'dirty' and calling it multiple times should not generate additional events.

Another option could be to use a one-shot timer that you only (re-)start at the end of the paint event handler.
Use the source, Luke!
heinermueller
Experienced Solver
Experienced Solver
Posts: 99
Joined: Sat Oct 26, 2013 11:54 am

Re: Avoid message queue flooding

Post by heinermueller »

Hi doublemax,

thank you for your answer.
You should try to avoid flooding the queue in the first place.
True, but only a threaded timer gives me exact jitter-free pulses. I tried a lot other stuff, it was all not reliable for several reasons. Then again inside the timer, it is impossible to check the application state, so i have to fire the pulses there and sort out elsewhere.

Actually i had good results so far with the following approach, please comment if you see problems:

1. override Filterevent, keep deque of two last issued events

Code: Select all

std::deque<bool> queued_events_were_draw_events;

int App::FilterEvent(wxEvent& event)
{
	// keep record of events
	queued_events_were_draw_events.push_back(event.GetEventType() == wxEVT_UPDATEFRAME);
	if (queued_events_were_draw_events.size() > 2)
	{
		queued_events_were_draw_events.pop_front();
	}

	// process the events normally by default
	return -1;
}
2. in actual paint handler, check pending events *and* for successive paint events

Code: Select all

canvas::paint()
{
	if (wxGetApp().Pending() && wxGetApp().updateframes_events_recorded_only())
	{
		return;
	}
//else paint ..
}

Code: Select all

bool App::updateframes_events_recorded_only()
{
	return std::all_of(queued_events_were_draw_events.begin(), queued_events_were_draw_events.end(), [](auto e) { return e; });
}
It is working fine with stable frame rates and no lags when opening large dialogs, opening network drives etc, also across multiple OS and wx versions. I do not miss any mouse moves or whatever. To my surprise it was sufficient to keep record only for the last two events.

Best regards,
Heiner
User avatar
doublemax
Moderator
Moderator
Posts: 19114
Joined: Fri Apr 21, 2006 8:03 pm
Location: $FCE2

Re: Avoid message queue flooding

Post by doublemax »

Looks ok. But wouldn't a single flag have been enough? Just don't send a new event if the last paint event hasn't been handled yet?
Use the source, Luke!
heinermueller
Experienced Solver
Experienced Solver
Posts: 99
Joined: Sat Oct 26, 2013 11:54 am

Re: Avoid message queue flooding

Post by heinermueller »

But wouldn't a single flag have been enough?
I think about that a lot, but it is a little bit difficult to grasp. All i can do is skip an excessive paint inside the paint handler, based on previous events. But maybe it is a valid paint event, i am about to skip? Maybe it is exactly the event that has this single flag in ::FilterEvent? So i thought, if there are two successive paint events scheduled, i can be sure at least one f them is excessive, so i can skip the one i am inside.

Sorry for my weird explanation, i still have to learn about this event handling.
User avatar
doublemax
Moderator
Moderator
Posts: 19114
Joined: Fri Apr 21, 2006 8:03 pm
Location: $FCE2

Re: Avoid message queue flooding

Post by doublemax »

My idea was to have a flag, e.g. "bool redraw_pending".
Default would be false.
Inside the thread, if the flag is false, you set it to true and send to event to trigger a redraw
At the end of the paint event handler, you set it to false
Use the source, Luke!
JOHI
Knows some wx things
Knows some wx things
Posts: 34
Joined: Fri Jul 20, 2018 8:25 pm

Re: Avoid message queue flooding

Post by JOHI »

I remember that Petzold wrote may years ago that windows combines the pending WM_PAINT for a single window.
As I was not sure, i looked it up and found a post illustrating exactly that : "https://stackoverflow.com/questions/257 ... d-messages" So checking if there is a WM_PAINT on the que to prevent additional ones I would not do. (I possibly I did not understand your question correctly).
Then if you want to make sure that you do not overload your application, consider using on idle.

Code: Select all

void WawiLib002Frame::OnEvtIdle(wxIdleEvent& event)
{
	UpdateGui(); => Invalidate your parts to be redrawn if something needs to be redrawn
}
and

Code: Select all

void WawiLib002Frame::OnTimer(wxTimerEvent& e)
{
	wxWakeUpIdle();
}
I had a similar problem with updating a list control that needed to process tons of updates, and the above approach worked very well: updates are fast and if things overload, non GUI gets priority.
heinermueller
Experienced Solver
Experienced Solver
Posts: 99
Joined: Sat Oct 26, 2013 11:54 am

Re: Avoid message queue flooding

Post by heinermueller »

JOHI wrote:Then if you want to make sure that you do not overload your application, consider using on idle.
Using OnIdle() itself gave very jittery timing (see my posts in the beginning) but combining the scheduled timer with wxIdleEvent looks good:
- set up a bool 'can_i_draw' flag
- before posting a paint message (from the timer thread) check the flag
- if false, do not send
- if true, send and set the flag to false
- in the OnIdle() handler, set the flag to true, because
wxIdleEvent doc wrote:"This class is used for idle events, which are generated when the system becomes idle."
OnIdle() gets called when every event has been handled. When this is fulfilled, switch back to timed repainting.
doublemax wrote:You should try to avoid flooding the queue in the first place
This. Now i know how. :D
Post Reply