Reputation: 423
I have written some code that makes use of a timer checking when the last user input was and prints it out to Visual Studio's debug console.
#include <Windows.h>
#include <stdio.h>
DWORD InactiveThreshold; // The allowable amount of inactive time milliseconds
BOOL Inactive;
SYSTEMTIME CurrentTime(char *ddmmyyyy, char *hhmmss)
{
SYSTEMTIME lt;
GetLocalTime(<);
snprintf(ddmmyyyy, 11, "%d/%d/%d", lt.wDay, lt.wMonth, lt.wYear);
snprintf(hhmmss, 9, "%d:%d:%d", lt.wHour, lt.wMinute, lt.wSecond);
return lt;
}
void DebugOutput(char *ddmmyyyy, char *hhmmss)
{
OutputDebugString(TEXT("\n\n"));
OutputDebugString(ddmmyyyy);
OutputDebugString(TEXT(" -- "));
OutputDebugString(hhmmss);
OutputDebugString(TEXT(" : "));
OutputDebugString(TEXT("Inactive"));
}
void CALLBACK TimerProc(HWND hwnd, UINT unt, UINT_PTR id, DWORD current_time)
{
LASTINPUTINFO li;
SYSTEMTIME lt;
DWORD TimeSinceInput, RemainingTime;
char ddmmyyyy[11], hhmmss[9];
// Timers continue to expire, so we need to stop it first.
KillTimer(NULL, id);
li.cbSize = sizeof(LASTINPUTINFO);
GetLastInputInfo(&li);
TimeSinceInput = current_time - li.dwTime;
if (TimeSinceInput > InactiveThreshold)
{
// [Also find a way to check if audio or video was playing during the user inactive period]
if (!(Inactive))
{
// Set flag so it only outputs that it is inactive once during an inactive period
Inactive = TRUE;
lt = CurrentTime(ddmmyyyy, hhmmss);
// [Find way to print the start of the inactive period as timestamp,
// subtracting InactiveThreshold from current time]
DebugOutput(ddmmyyyy, hhmmss);
}
// [Change below so once Inactive, stop timer and instead set event loop
// to check for user input(keyboard / mouse) to end the inactive period
// and output timestamp of the end of the inactive period]
SetTimer(NULL, 0, InactiveThreshold, &TimerProc);
}
else
{
Inactive = FALSE;
RemainingTime = InactiveThreshold - TimeSinceInput;
SetTimer(NULL, 0, RemainingTime, &TimerProc);
}
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
MSG msg;
Inactive = FALSE;
InactiveThreshold = 30000; // 30 seconds
SetTimer(NULL, 0, InactiveThreshold, &TimerProc);
while (GetMessage(&msg, NULL, 0, 0) > 0)
{
DispatchMessage(&msg);
}
return 0;
}
Ideally it should print when the timestamp of the start of the inactive period was, then when the user is active again to print the timestamp of when the inactive period finished. However there's three main issues (also indicated in the code with square bracket comments //[] ):
Upvotes: 0
Views: 239
Reputation: 13699
GetLastInputInfo() doesn't take into account if the user is playing media (ex: watching a video or listening to audio in Google Chrome)
As I understand, system itself track activity only as whatever is available from GetLastInputInfo()
.
Cases like "user is watching video" are handled by sending WM_SYSCOMMAND
with SC_SCREENSAVE
or SC_MONITORPOWER
, so that screen does not go blank if application swallows such message instead of passing to DefWindowProc
. Or alternatively by SetThreadExecutionState
with ES_DISPLAY_REQUIRED
Cases like background audio are handled by SetThreadExecutionState
with ES_SYSTEM_REQUIRED
.
This all means that user inactivity does happen, but some other conditions prevent from taking actions on this inactivity.
I'm afraid an application cannot reliably detect all such conditions.
Upvotes: 1