Jump to content
Tommi Prami

Is there a way to check is the "user Idle" (no interaction)

Recommended Posts

Hello,

Similarly as Screen Saver starts after some time no input and so, I would like to detect/get notification  for that. Or as the Teams will change status to the Away.,.. 

Tried to google around but nothing good came up...

 

-Tee-

Share this post


Link to post

@Tommi Prami - So, do you want to have your app do something, when the PC signals that there has been no keyboard or mouse events for a while?

Do you want to track that yourself, or are you looking for a system event?


https://stackoverflow.com/questions/2212823/how-to-detect-inactive-user

https://stackoverflow.com/questions/2177513/receive-screensaver-notification

Share this post


Link to post
12 minutes ago, Lars Fosdal said:

@Tommi Prami - So, do you want to have your app do something, when the PC signals that there has been no keyboard or mouse events for a while?

Do you want to track that yourself, or are you looking for a system event?


https://stackoverflow.com/questions/2212823/how-to-detect-inactive-user

https://stackoverflow.com/questions/2177513/receive-screensaver-notification

 


No need to track especially, but to know how long user has been idle...  

I GetLastInputInfo() API for now, and seems OK for now.

Added todo-items for those links, seemed to have many good things to research later.

Thanks.

 

-Tee-

Share this post


Link to post
12 hours ago, aehimself said:

I have a component which detects and attempts to "break" the idle status.

 

https://github.com/aehimself/AEFramework/blob/master/AE.Comp.KeepMeAwake.pas

Browsing the code i have thoughts

1) from SendInput https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-sendinput 

Quote

This function fails when it is blocked by UIPI. Note that neither GetLastError nor the return value will indicate the failure was caused by UIPI blocking.

2) This class TAEKeepMeAwake is somehow asynchronous, in other words it has its own timer and doesn't depend on application logic to be triggered or executed, it depends on OS timer, so when things go wrong that exceptions raising RaiseLastOSError, might cause havoc like endless messages every 1 second.

3) I hate raising exception specially if they are not needed, and in this case its like ok, let some other part of the application handle this, here take an exception,...

Exceptions are not signaling mechanism, notice here with (the mentioned above function) the exception will be generic and useless, and if the application error reporting failed to capture the stack, then application might end up showing funny messages like "exception was caught.. last error (or last operation) was success".

 

I would suggest to remove exception raising and replace them with OnError event, in my opinion this is less intrusive method and more useful (less ticking bombs), also this class and before calling such event it should disable its timer, leaving the decision to reenable the developer. 

 

Sorry if Off-topic.

  • Like 1

Share this post


Link to post

Nice insights! I'll make some updates once I managed to set my dev VM up (I'm in the middle of switching to Linux on my daily driver atm...)

  • Like 1

Share this post


Link to post
On 6/7/2024 at 9:45 AM, Tommi Prami said:

Similarly as Screen Saver starts after some time no input and so, I would like to detect/get notification  for that.

Its unclear to me, if you are looking after an application-wide or global detection of user-idle?
For the local app, I think its easy, but for the global that can be tricky.
How will external, long-running tasks in threads be handled?

For example, if you do a longer running FTP-synchronisation, running updates or something like that:
Does that mean the user is idle after some time, or not?

What often disturbs me is, if the system is heavily running, like updating, giving a powerpoint presentation,
but the OS switches unexpectedly to "idle", only because the user is waiting for finishing such a process.

This forces me to hit the mouse from time to time, to keep the whole system awake.

Maybe to improve to detect such situations above, it would be possible by checking the CPU load additionally and block "idle" mode 🙂

 

 

 

 

Share this post


Link to post
42 minutes ago, Rollo62 said:

Its unclear to me, if you are looking after an application-wide or global detection of user-idle?
For the local app, I think its easy, but for the global that can be tricky.

Donät know what you mean by local/global.

Mainly I need to know is the user being using mouse or keyboard etc. At least for now that seems to be enough.

GetLastInputInfo() is working well for now. 

I'll try to get this thingy more than less ready this week, so I can show it to you,... Not much to do but...

 

-Tee-

Share this post


Link to post
Posted (edited)
3 minutes ago, Tommi Prami said:

Donät know what you mean by local/global.

Local, I mean within the app itself.
There its quite easy to detect missing keystrokes or mouse moves by slow running timer.

So the app could manage an Idle state, similar like banking apps in the browser (log out after 5 min.).

Global, I mean, when your app is running, but in background.
While another, long running task is in foreground, for example you wat 1h Youtube video in the browser.
This means Global is "active" (even if the user is idle), while your local App is Idle.
What do you expect to happen in such situation?
 

Edited by Rollo62

Share this post


Link to post
Just now, Rollo62 said:

While another, long running task is in foreground, for example you wat 1h Youtube video in the browser.
This means Global is "active" (even if the user is idle), while your local App is Idle.
What do you expect to happen in such situation?
 

That is Good point, most likely that does not work currently, maybe... 

Thanks for the bug, that needs to be fixed, somehow...

 

-Tee-

Share this post


Link to post
42 minutes ago, Rollo62 said:

For example, if you do a longer running FTP-synchronisation, running updates or something like that:

There is even better example, IIS !

 

I think you are overthinking the whole saving battery, sleep, hibernate... see, in Windows Server OS the Sleep and Hibernate are disabled by default by policy group, these might cause lot of problem if they kicked in, while on personal PC the thing is completely different due the different tasks priority or lets say different default tasks required from the PC to do, one have zero dependency on user input, the other in general is to handle and process direct user interaction, input, output or even playing music...etc

 

Well don't know what to say more about that, but i can suggest a neat solution for such  

 

Use Awake from Microsoft PowerToys, 

https://learn.microsoft.com/en-gb/windows/powertoys/awake

https://learn.microsoft.com/en-gb/windows/powertoys/?WT.mc_id=twitter-0000-docsmsft

https://github.com/microsoft/PowerToys

Share this post


Link to post
6 minutes ago, Kas Ob. said:

Use Awake from Microsoft PowerToys,

Thanks, that "Awake" is a good tip.
Usually I use my machines more or less clean and unchanged, like their were intended from their Maker.
Mainly because finetuning, optimizing GroupPolicies or the like is a pain in the a** and some permanent gameplay for IT nerds or admins.
I usually try to avoid that on all cost, because that is an evergrowing overhead with little benefit, IMHO.
 

Share this post


Link to post
19 minutes ago, Rollo62 said:

like their were intended from their Maker.

Well, their makers lost their compass and bloating it with useless tools, so i strip them to the skeleton first then re-add my needs.

here how my Windows 10 looks when after booting

image.thumb.png.6fbc967db932e319c147fff812dde196.png

few months ago had to reinstall windows store and many similar components for a project, it was even cleaner than now 😡

Share this post


Link to post

Yes, you name it.
Thats why I use portable, non-service tools wherever I can, looks like Awake plays in that category too, have not tested yet.
I cannot understand this permanent services and processes bloat also, which only brings performance and reliability to the ground level.

Share this post


Link to post
Posted (edited)


I played around with the routine from James Campbell's (Feb 6, 2020) answer from @Lars Fosdal's posted link, and posted again, below. 

 

https://stackoverflow.com/questions/2212823/how-to-detect-inactive-user

 

Note: To make testing/debugging easier, I made this change: FormStyle=fsStayOnTop in the Object Inspector area so that I could better observe the app's behavior. 

And, while the app does work as expected, when I minimize the app and go into another running app or use keyboard/mouse activity outside the said app, it would continue to work--that is, in the timer section it would continue. 

 

I modified the code to: detect the app's active status and check if it is minimized.

 

procedure TForm1.Timer1Timer(Sender: TObject);
begin
  if (application.Active=true) and (WindowState = wsMinimized) then else
    if application.Active=true then begin
      Caption := Format('System IDLE last %d seconds', [SecondsIdle]) ;
    end;
end;

And now it seems to work as expected when it is minimized the counting in the timer event is ignored, and if the app is active in the foreground, the counting continues. 

 

Edited by JohnLM
  • Like 1

Share this post


Link to post
Posted (edited)

Just to add some some thoughts on an alternative way, with certain pros and cons.
I like to use a simple TDateTime helper in such uncritical situations, instead of GetTickCount, to determine timeouts:

function  TDateTime_Helper.Timeout_MSec_IsElapsed( const AValue : Integer ) : Boolean;
begin
    if Self.IsNull then
        Result := False                                       // is stopped => trigger never
    else
        Result := Now.MillisecondsBetween( Self ) >= AValue;  // is elapsed => trigger conditional
end;

Cons:
- Yes, it has much lower performance, since this has to run through many conversions and calculations.
   Roughly speaking this is in the microseconds range, while the GetTickCounter may lay in the nanoseconds range ( approx. factor 1000 ).
   In these cases, where I just need to test this in quite large periods, like > 10 sec., I think this is not a too big deal to loose some microseconds.

 

Pros:<
- It adresses timing questions directly, by easier readable millisecond notation and avoids any tick count mis-calculation or logical errors by default.
- It avoids possible overflows, which may happen at 32-Bit in about 49.7 days (using GetTickCount64 practically may solve this, by overflow in about after million years).
   (A consideration to use QueryPerformanceCounter also might solve this overflow, but in 99.99% of the use-case any high precision is not needed.)

- It avoids possible counter blocks or counter resets, by always using the real timestamp
   (of course a user might change the system time, but this is not very likely to be done without a clear user invention).
- It avoids issues by non-incremental counting, which is noted in the GetLastInputInfo description already

Quote

The tick count when the last input event was received (see LASTINPUTINFO) is not guaranteed to be incremental. In some cases, the value might be less than the tick count of a prior event.

For example, this can be caused by a timing gap between the raw input thread and the desktop thread or an event raised by SendInput, which supplies its own tick count.

- It elegantly avoids the need to build any additional safety measure around the overflow or incremental issue from above, just to ensure a proper incremental timeline.
- It should immediately work in the same way under all platform, because a timeline is a universal feature.
  (GetTickCount is originally a Windows function, other platforms may handle this completely different) .

 

 

 

Edited by Rollo62

Share this post


Link to post

Made first test "Release"

Little of usage info in the readme.md

Note that it's start as minimized, sould block the sleep and screensaver (which is my point, might be optional later), and just show black screen.

Why Im made this, is that I have LG OLED TV as monitor, and don't want to go to screensaver at home office, and would like to go to the back screen. 

https://github.com/TommiPrami/OLEDBlackScreen

Prebuild exe if you dare to use: https://github.com/TommiPrami/OLEDBlackScreen/releases/tag/Alpha_0.1

 

-Tee-

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×