Sign in Join

Live chat for Free Pascal, Lazarus, Delphi and every Pascal programmer.

Mailing lists / fpc-pascal / 2021-12

Getting Last User Input reliably

11 messages · 6 people · –

Reply by email Subscribe to this list Your subscription

Read-only archive. Use j and k to move between messages.

James Richters

I'm trying to get the time lapsed since the last user input with keyboard or mouse on a Windows PC. For this I am doing:

GetLastInputInfo(Last_Input_Info);
IdleTime:= (GetTickCount - Last_Input_Info.dwTime) DIV 1000;

This works most of the time, but every 25 days or so GetTickCount rolls over, and so if there was activity before the roll over and then no activity until after the roll over, then this whole scheme just produces incorrect results.

Is there a better way of doing this that doesn't have this issue? Most of the PCs I run this on have not been rebooted or shutdown for years so this happens quite often.
Is there some function in FPC that would give me the time elapsed since last user input?

If there isn't a better way to detect the amount of time since last user input, then is there a way I can reliably detect the roll over and correct for it?

I thought I might be able to fix this by using GetTickCount64 and comparing it to GetTickCount to see if GetTickCount rolled over, but that doesn't actually help a lot.. because knowing it rolled over would only help some of the time and eventually GetTickCount64 will roll over as well, and then I have the same problem.. just not as often.. and eventually GetTickCount64 will be so much larger than GetTickCount, I can never know how many times GetTickCount rolled over. On top of all this, I need to allow for the possibility that there was no user input for more than 25 days.. which is quite likely. What would be MUCH better would be a TDateTime variable that gets updated during user input and stops getting updated when there is no user input... but I don't think there is such a thing... or is there?

Is there a way to reset the tick count to 0? If I reset it to 0 when I knew there was user input,
If there is, is it a bad idea to reset it to 0? Maybe some system processes are also using it?

Anyone have any ideas? I don't need this accurate to 1ms, accurate to a second or ten would be fine.

James

Luca Olivetti
to James Richters

El 3/12/21 a les 14:52, James Richters via fpc-pascal ha escrit:

Show quoted text 5 lines
I'm trying to get the time lapsed since the last user input with keyboard or mouse on a Windows PC. For this I am doing: GetLastInputInfo(Last_Input_Info); IdleTime:= (GetTickCount - Last_Input_Info.dwTime) DIV 1000;

I was going to suggest

IdleTime:= DWORD(GetTickCount - Last_Input_Info.dwTime) DIV 1000;

but then I saw

On top of all this, I need to allow for the possibility that there was no user input for more than 25 days.. which is quite likely.

so that's not going to work, sorry. I'm using the above for short intervals and it works in spite of the rollover (unless you enabled overflow checking, but that's another issue).

Bye

Luca
Travis Siegel
to Luca Olivetti

If there's some sort of a configuration file, just write out the time/date info at the time of leaving the input, then when that routine gets called again, grab the current time/date, and perform a comparison.  That should allow you to bypass any roll overs of any kind (unless it goes past the operating system's time keeping abilities), which isn't likely.

Luca Olivetti wrote 25 lines
El 3/12/21 a les 14:52, James Richters via fpc-pascal ha escrit: > I'm trying to get the time lapsed since the last user input with > keyboard or > mouse on a Windows PC.  For this I am doing: > > GetLastInputInfo(Last_Input_Info); > IdleTime:= (GetTickCount - Last_Input_Info.dwTime) DIV 1000; I was going to suggest IdleTime:= DWORD(GetTickCount - Last_Input_Info.dwTime) DIV 1000; but then I saw > On top of all this, I need to allow for the > possibility that there was no user input for more than 25 days.. > which is > quite likely. so that's not going to work, sorry. I'm using the above for short intervals and it works in spite of the rollover (unless you enabled overflow checking, but that's another issue). Bye
Luca Olivetti
to Travis Siegel

El 3/12/21 a les 16:41, Travis Siegel via fpc-pascal ha escrit:

Show quoted text 6 lines
If there's some sort of a configuration file, just write out the time/date info at the time of leaving the input, then when that routine gets called again, grab the current time/date, and perform a comparison.  That should allow you to bypass any roll overs of any kind (unless it goes past the operating system's time keeping abilities), which isn't likely.

I suppose he wants to check for any activity, not just for input directed at his application.
One possibility is to have a thread calling GetLastInputInfo cyclically (say, every second or every half second), if the dwTime changed just register the current GetTickCount64 as the time of the input. That should work unless the new input happens exactly after 49.7 days (that's the rollover time, not 25 days) from the last one, at the same, exact, millisecond. Quite improbable but not impossible. Oh, and keep in mind that in older versions of windows there's no GetTickCount64, the rtl simulates it by using.....GetTickCount. Windows Vista and above should be fine though.

Bye

Luca
Luca Olivetti

El 3/12/21 a les 18:37, Dennis Lee Bieber via fpc-pascal ha escrit:

Detection is normally done by: if current tick is less than saved tick, roll-over has occurred. Make adjustments to the delta.

There is no need to do that, just make sure you're using DWORDs (i.e. unsigned arithmetic) everywhere and disable overflow checking.

Bye

Luca
James Richters
to Luca Olivetti

Indeed keeping it a DWord solves all the problems for any amount of time less than the 49.7 day rollover time and I don't even have to check for the rollover because it takes care of itself by doing unsigned arithmetic.

Is there even such a thing as overflow checking for DWord variables?

I did some tests and went to go turn on Overflow checking just to see what would happen... and noticed it was on already... but I noticed it's called "Integer Overflow Checking" which seems it would not apply to things specifically set as DWord. I did some searches and didn't come across any overflow checking that wasn't for integers.

Am I missing something? Or is this not even an issue because there is no overflow checking for DWord variables anyway?

So the only thing I need to consider is times of longer than 49.7 days without user input... because if it was exactly 50 days, it would look like 0.3 days as the first rollover would be forgotten. To take care of this I can just check it in my main program loop and keep updating my own datetime variable of last user input. This way I'm checking it often enough that I can't ever have more than one rollover between checks.. and the math all fixes itself... after being sure to keep it all Dword My program will be running anyway... and even if it's not it won't matter because I will cause user input when I start it.

James

Jonas Maebe
to James Richters
James Richters wrote 5 lines
I did some tests and went to go turn on Overflow checking just to see what would happen... and noticed it was on already... but I noticed it's called "Integer Overflow Checking" which seems it would not apply to things specifically set as DWord. I did some searches and didn't come across any overflow checking that wasn't for integers. Am I missing something? Or is this not even an issue because there is no overflow checking for DWord variables anyway?

See https://gitlab.com/freepascal.org/fpc/source/-/issues/25201 (first comment)

Jonas

James Richters
to Jonas Maebe

Oh I see.. it does work on DWord.. I was testing it with constants: Writeln($FFFFFFFE+$00000006);
But I think the compiler resolved this so it would never create a overflow error.. When I do it with variables now I get the overflow and I can control it with {$Q-} and {$Q+}

Is there some way I can do {$Q-} before my check then restore it to whatever it was before after it? (for example if it was already set to {$Q-} in the compiler, I would not want to turn it back on) Is there a way to do a temporary override for the one line?

I'm trying to figure out how to make a generic function for this that will work regardless of what is set in the compiler without changing the rest of the program.

I Thought I could put the result into a Qword then just AND it with $FFFFFFFF and put that into my DWord... that seems like it would make it work even if overflow checking was on... But when I tried this with a little sample program, I still got the runtime error 215... I don't know why, because I was putting the answer into a Qword.

var
num1,num2:dword;
QWresult:Qword;
DWresult:Dword;
begin
num1:=8;
num2:=$FFFFFFFE;
QWresult:=QWord(num1+num2);    //Generates runtime 215
DWresult:=(QWresult AND $00000000FFFFFFFF);
writeln(DWresult);
end.

But if I do
QWresult:=(QWord(num1)+QWord(num2));
It does not

So I'm wondering why
QWresult:=QWord(num1+num2);
Did not work as I expected it to.

James

James Richters

Thanks for the explanation.
This works the way I want it to... working with the rollover without causing an overflow error even if I leave overflow checking turned on.

DWord((QWord(GetTickCount) - QWord(Last_Input_Info.dwTime)) AND $00000000FFFFFFFF)

James

Sven Barth
to James Richters: Oh I see.. it does work on DWord.. I was testing it with constants:…

James Richters via fpc-pascal schrieb am
So., 5. Dez. 2021, 19:06:

Show quoted text 5 lines
Is there some way I can do {$Q-} before my check then restore it to whatever it was before after it? (for example if it was already set to {$Q-} in the compiler, I would not want to turn it back on) Is there a way to do a temporary override for the one line?
{$push}
{$Q-}
// whatever code you need
{$pop}

Regards,
Sven

Unknown author
to James Richters: Indeed keeping it a DWord solves all the problems for any amount of time less than the…
James Richters wrote 5 lines
So the only thing I need to consider is times of longer than 49.7 days without user input... because if it was exactly 50 days, it would look like 0.3 days as the first rollover would be forgotten. To take care of this I can just check it in my main program loop and keep updating my own datetime variable of last user input.

i don't understand why you are keeping up with user input times by counting ticks... you said that one second accuracy is just fine so Keep It Simple, man, and don't over engineer it...

grab the unix timestamp NOW each time they input something and store that... when you want to know how long it has been since their last input, just subtract lastUserInputTime from currentNow and you have your elapsed time...

if you use a 32bit signed variable, you still have 17 years before you have to even think about a rollover... in this day in age, i'd use a 64bit signed variable along with the 64bit unix time routines and not worry about it for the next 292 billion years ;)

NOTE: No off-list assistance is given without prior approval. *Please keep mailing list traffic on the list where it belongs!*