Way to Check for user Activity?

This is for desktop Mac and Win (and I am using API 1 if that matters)
I have a situation where I want want an app to lock up and require the user re-login if there is no keyboard, or mouse activity on the machine for a set period of time…

If I had a way for checking last activity I could use a timer to do that… But how would one do that? Failing a system wide way of doing that, what would the best way to at least do it within my app?

Within my App th obvious put code in all then relevant windows and control events (I suppose I can subclass them all) that reset an app global timer…But that gets messy and makes it hard to reuse code written for other apps.

Thanks,

  • Karen

Older MS Windows API send Mouse and Key Board Events.
I think Users should use Start/Windows L to Lock the PC if they go away or at least a screensaver that would lock the desktop.
if users must do log in quite often they use a easy password or put sticky note at monitor.

have you looked at System.MouseX or Keyboard.AsyncKeyDown?
maybe you could poll via Timer and compare.

You could check GlobalIdleTimeMBS in a timer and see if it goes up over a threshold.

2 Likes

I think I usually just use the mouse click, enter and exit events for the window or common controls that flip a variable for activity. Then a timer to flip that variable off.
It isn’t 100% accurate of course but it works good enough.

I did this for a Web1 app and got it working well. However, I simply set a timer for inactivity and scattered reset calls throughout the controls (GotFocus, LostFocus, Keydown, Keyup etc). There are likely better solutions.

For Web it was a session level timer that would be started when a dialog was presented for user action. Any control on the dialog would reset the timer.

Any critical code, such as uploading a file or executing an SQL statement, would pause the timer and restart it once that action was complete. Pressing the OK button on a dialog would halt the timer so it would always complete the instruction to save.

If the timer fired it would tidy up with whatever was happening at the time and return to the login screen.

Thanks all…

I had originally went the route of putting code in the mouse move and keydown events that reset a timer … But when I wanted to incorporate some UI code in a container control that as not specifically written for that functionality that I did not want to have the to modify for this specific use.

I wondered if there was a better way , maybe with platform specific declares.

I guess i will need just modify what was meant be drop in reusable functionality - which means 2 sets of code. Oh well!

Thanks

  • Karen

You could perhaps create a subclass of all the controls and implement the resets in those classes. Creating an event definition and re-raise the events to allow individual controls to still implement the events. That still leaves you having to change the super on all your controls, but not otherwise modify them.

Class EditFieldWithReset

Event Definition KeyDown

Method KeyDown
   Reset timer
   Raise KeyDown
End Method
// etc etc.

That is what I had done for the other code in the project for controls used on multiple windows in it… but in this case that would mean having 2 versions of that container control which I was trying to avoid.

While you can subclass container controls, unfortunately you can only do it for an abstract container with no controls.

I did not want to have fix any bugs I may find in the reusable container control twice.Oh Well.

  • Karen

Alternatively have two modules that specify the subclasses. Always use them for everything and swap the module to have one that performs the reset and one that doesn’t?

The container would always use MySubClassEditField. Include the MySubClassEditFieldWithReset or the standard MySubClassEditField module.

The Without reset module would only contain:

Class MySubClassEditField Super EditField
Class MySubClassRadioButton Super RadioButton
// etc

The Withreset module would use the technique above.

Thinking about it the WithReset module could probably include extends to the App class to implement the timer methods required. Potentially making it a drop in package to apply a timeout system to any application that uses the control subclasses.

Karen needs Mac and Windows, but the documentation for GlobalIdleTimeMBS is confusing: the function field reads “Queries the current global idle time on a Mac.” while, in the header, “MacOS” and “Windows” are both checked (:heavy_check_mark:). :man_shrugging:

I think it was first macOS only and later got windows support.

1 Like

Thanks. Would be nice for this information to be reported in the documentation.

Sure, I’ll update it with the next batch run.

1 Like

On windows:

Dim lastTimeTicks As Integer

Soft Declare Sub GetLastInputInfo Lib "User32" (pli As Ptr)
Soft Declare Function GetTickCount Lib "Kernel32.dll" () As UInt32

If System.IsFunctionAvailable("GetLastInputInfo", "User32") Then
  Dim mb As New MemoryBlock(8)
  mb.Long(0) = mb.Size
  GetLastInputInfo(mb)
    lastTimeTicks = mb.UInt32Value(4)
End If

Dim TickCount As UInt32 = GetTickCount
Dim Seconds As Integer = (GetTickCount - lastTimeTicks) / 1000

Return Seconds
2 Likes

Thanks Ivan,

Now I only need the Mac equivalent!

-Karen

Christian’s answer doesn’t work for you?

I don’t have the plugin.

-Karen

Googling for the Mac equivalent of the windows call I got:

This might work if I understand it…

It returns a count of the events so the logic needs to be different , but if I can figure out the declare to call it, using that function should be doable to achieve what I want on the Mac.

  • Karen

There are apparently two ways to achieve your goal.
The first one is easy to implement but involve parsing from a shell result. I tested what I found on Internet and it works:

var s As new Shell

s.Execute "/usr/sbin/ioreg -c IOHIDSystem | /usr/bin/awk '/HIDIdleTime/ {print int($NF/1000000000); exit}'"
Var t As String=s.ReadAll.Trim 'a non-mentioned trailing chr(10) is in the result

The other way I found, using API calls, is more complex to implement in Xojo. Take a look there: Get Mac OS X system idle time in Swift or Objective-C · GitHub

HTH

1 Like

Thanks Arnaud.

I would rather avoid using the shell but I could if I had to… As I don’t know Swift or objective C and am not familiar with OSX declares, I may have to.

That said, is there a reason I should not use I use the Api I posted which MIGHT be simpler? (If I understand it and can figure out how to call it!!!)

The the Xojo timer would save the event count at startup. When it fires it checks if the count has increased… If it has changed it stores the new count and resets itself and if not it requires the user to log in again.

-Karen