Sensing for inactivity... part 2. Topic is solved

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
jmason1182
Earned some good credits
Earned some good credits
Posts: 149
Joined: Fri Dec 14, 2007 3:40 pm
Location: Midland, TX
Contact:

Sensing for inactivity... part 2.

Post by jmason1182 » Wed Sep 24, 2008 2:40 pm

OK. So I have a program that uses many different dialogs and frames. BUT this program requires a login to access the database. The requirements are that if a user walks away for longer than 60 seconds, it should lock the program down.

SO, I have the following:

Code: Select all


	//Part of the LockOut mechanism:
	EVT_MOUSE_EVENTS(MyClass::LockTimerResetM)
	EVT_KEY_DOWN(MyClass::LockTimerResetK)
	EVT_CHAR(MyClass::LockTimerResetK)
	EVT_KEY_UP(MyClass::LockTimerResetK)
and then the following in the definition of MyClass (header file)

Code: Select all

			static wxTimer *lockTimer;
			wxDateTime lockTracker;

			void StartLockTimer();
			void StopLockTimer();
			void LockTimerResetK(wxKeyEvent& event);
			void LockTimerResetM(wxMouseEvent& event);
			void LockTimerReset();
And then, here's the actual code for these functions:

Code: Select all


void MyClass::LockTimerResetM(wxMouseEvent& event) { LockTimerReset(); }
void MyClass::LockTimerResetK(wxKeyEvent& event) { LockTimerReset(); }
void MyClass::LockTimerReset() {
	if(!MyClass::lockTimer || !MyClass::lockTimer->IsRunning()) StartLockTimer();
	else lockTracker = wxDateTime::Now();
}

void MyClass::StartLockTimer() {
	if(!MyClass::lockTimer) lockTimer = new wxTimer(this, ID_LOCKTIMER);
	if(lockTimer->Start(LOCK_TIMER_CHECK_CYCLE, wxTIMER_CONTINUOUS)) {
		lockTracker = wxDateTime::Now();
	} else {
		delete MyClass::lockTimer;
		MyClass::lockTimer = NULL;
	}
}

void MyClass::StopLockTimer() {
	if(MyClass::lockTimer) MyClass::lockTimer->Stop();
	if(MyClass::lockTimer) delete MyClass::lockTimer;
	MyClass::lockTimer = NULL;
}

void MyClass::LockTimerFire(wxTimerEvent &event) {
	if(wxDateTime::Now().Subtract(lockTracker).IsLongerThan(wxTimeSpan(0,0,0,LOCK_TIMER_INACTIVITY))) {
		StopLockTimer();
		wxMessageBox("LockTimerFire");
		StartLockTimer();
	} else {
		mainStatus->SetStatusText(wxString::Format(wxT(""),lockTracker.),2);
	}
}
I call the StartLockTimer function when the user logs in successfully. I call the StopLockTimer function when the user logs out. My plan was for any error messages, or anything at all to be covered by a really large (fullscreen) modal dialog requesting the password. (I don't like this idea, but that was a requirement) So I'll replace the wxMessageBox with a custom dialog later.

Down to the questions:

1) First off, do I need the timer to be static? And does the date tracker variable also need to be static? (See Question 2 below for why I am playing with it)

2) So the timer part itself works.... FOR THE MAIN WINDOW ONLY. I need for all child-windows to allow this window to see their events so that the LockTimerReset is called. If I go to a child-window, then mouse movements, etc. won't trigger the reset. Thus, it fires and I can't get it to stop.

SO, any ideas?
John A. Mason
Midland, TX

upCASE
Site Admin
Site Admin
Posts: 3176
Joined: Mon Aug 30, 2004 6:55 am
Location: Germany, Cologne

Post by upCASE » Wed Sep 24, 2008 2:49 pm

Hi!
Just an idea: Instead of Connect() for all childs, try intercepting the events before any other event handler is called.
This can be done with wxApp::FilterEvent

You'd need to check what event exactly happened. You can use wxWidgets RTTI for that, see here http://docs.wxwidgets.org/stable/wx_wxo ... ctiskindof. Maybe just checking the event type is enough -> http://docs.wxwidgets.org/stable/wx_wxe ... teventtype
OS: OpenSuSE, Ubuntu, Win XP Pro
wx: svn
Compiler: gcc 4.5.1, VC 2008, eVC 4

"If it was hard to write it should be hard to read..." - the unknown coder
"Try not! Do. Or do not. There is no try." - Yoda

jmason1182
Earned some good credits
Earned some good credits
Posts: 149
Joined: Fri Dec 14, 2007 3:40 pm
Location: Midland, TX
Contact:

Post by jmason1182 » Wed Sep 24, 2008 2:55 pm

OK, so at the APP level, I can filter all events and just do some checks on each event to see if I need to call the reset... is that the basic gist of it?


So I need to override the wxAPP::FilterEvent function correct?
John A. Mason
Midland, TX

upCASE
Site Admin
Site Admin
Posts: 3176
Joined: Mon Aug 30, 2004 6:55 am
Location: Germany, Cologne

Post by upCASE » Wed Sep 24, 2008 3:10 pm

Hi!
Yes, that's the basic idea.

I had a situation once where I needed to catch a key event indicated that the user hit return. Like in your case the app had multiple panels and tabs, but the event needed to be catched globally. Registering a hotkey for that wasn't an appropriate solution, as this interferes with all other apps. So I used FilterEvent(), checked for wxEVT_KEY_DOWN and wether return was pressed, then calling some code to process the keypress if needed.
OS: OpenSuSE, Ubuntu, Win XP Pro
wx: svn
Compiler: gcc 4.5.1, VC 2008, eVC 4

"If it was hard to write it should be hard to read..." - the unknown coder
"Try not! Do. Or do not. There is no try." - Yoda

jmason1182
Earned some good credits
Earned some good credits
Posts: 149
Joined: Fri Dec 14, 2007 3:40 pm
Location: Midland, TX
Contact:

Post by jmason1182 » Wed Sep 24, 2008 3:21 pm

OK, so how did you do the check... I'm trying to keep it simple and just check for the F1 key for a proof-of-concept.


Here's what I got... and it aint working. (mainframe is an instance of MyClass earlier... I'm catching F1 and F2 to open up the main help and main error log respectively. proof of concept...

Code: Select all

int MainAPP::FilterEvent(wxEvent &event) {

	switch(event.GetEventType()) {
		case wxEVT_KEY_DOWN:
			if(((wxKeyEvent)event).GetKeyCode()==VK_F1) {
				mainFrame->HelpKeyClick(event);
			} else if(((wxKeyEvent)event).GetKeyCode()==VK_F2) {
				mainFrame->LogKeyClick(event);
			}
			//we handled this event.
			return true;
			break;
	}

	//We have handled this event.
	//return true;

	//We won't even handle this event at all.
	//return false;

	//Allow the event to continue untouched.
	return -1;
}
Here's my errors:
error: `wxEVT_KEY_DOWN' cannot appear in a constant-expression
error: no matching function for call to `wxKeyEvent::wxKeyEvent(wxEvent&)'
note: candidates are: wxKeyEvent::wxKeyEvent(const wxKeyEvent&)
note: wxKeyEvent::wxKeyEvent(wxEventType)
So how did you do your test for the event type?
John A. Mason
Midland, TX

Auria
Site Admin
Site Admin
Posts: 6695
Joined: Thu Sep 28, 2006 12:23 am
Contact:

Post by Auria » Wed Sep 24, 2008 3:25 pm


upCASE
Site Admin
Site Admin
Posts: 3176
Joined: Mon Aug 30, 2004 6:55 am
Location: Germany, Cologne

Post by upCASE » Wed Sep 24, 2008 3:27 pm

Hi!
I did something like

Code: Select all

if ( event.GetEventType()==wxEVT_KEY_DOWN)
{
	if( ((wxKeyEvent&)event).GetKeyCode() == WXK_RETURN )
	{	
		//Process my stuff here
		return true;
	}
}
The problem with the switch is that wxEVT_KEY_DOWN is not a constant, but an enum value.
OS: OpenSuSE, Ubuntu, Win XP Pro
wx: svn
Compiler: gcc 4.5.1, VC 2008, eVC 4

"If it was hard to write it should be hard to read..." - the unknown coder
"Try not! Do. Or do not. There is no try." - Yoda

jmason1182
Earned some good credits
Earned some good credits
Posts: 149
Joined: Fri Dec 14, 2007 3:40 pm
Location: Midland, TX
Contact:

Post by jmason1182 » Wed Sep 24, 2008 3:29 pm

I can't tell you how many times I've looked at that exact wiki page and just skipped over the first part....


That is just awesome. Here's my FilterEvent function that discerns what events are what.

Code: Select all

int Main::FilterEvent(wxEvent &event) {

	//capture all key events - Key presses
	if ( event.GetEventType()==wxEVT_KEY_DOWN ) {

		//Regardless of what key was pressed, we reset the
		// locking timer (keypress = NOT inactivity)
		mainFrame->LockTimerResetK((wxKeyEvent&)event);

		if(((wxKeyEvent&)event).GetKeyCode()==WXK_F1) {
			//F1 pressed - open help
			mainFrame->HelpKeyClick( (wxKeyEvent&)event );
			return true;
		}
		if(((wxKeyEvent&)event).GetKeyCode()==WXK_F2) {
			//F2 pressed - open main error log
			mainFrame->LogKeyClick( (wxKeyEvent&)event );
			return true;
		}

		//all other key presses we handle as normal.
		return -1;

	}

	//now we check for the mouse movement, clicks, etc. ALL mouse events (this is a range)
	if( event.GetEventType()>=wxEVT_LEFT_DOWN && event.GetEventType()<= wxEVT_MOUSEWHEEL ) {
		//User did SOMETHING, so we are not inactive, reset the lock timer.
		mainFrame->LockTimerResetM((wxMouseEvent&)event);
		//But we will allow the mouse "action" to be handled as normal.
		return -1;
	}


	//And lastly we check for any and all System messages that we want to exit from
	// SUCH AS logging out of the computer and shutting the computer down.
	if( event.GetEventType()==wxEVT_QUERY_END_SESSION ) {
		wxCloseEvent closeEvent(wxEVT_QUERY_END_SESSION, wxID_ANY);
		closeEvent.SetEventObject(this);
		closeEvent.SetCanVeto(false);

		this->ProcessEvent(closeEvent);
		return true;
	}

	//Allow the event to continue untouched.
	return -1;
}

Thanks for all your help.
John A. Mason
Midland, TX

Post Reply