[WIN32SS:NTUSER] Implement SharedUserData->LastSystemRITEventTickCount (#8537)

This field was introduced in NT5.1, records the tick count of the last user input
(system-wide), updated at most once per minute (or once per second since NT6.0).

Unlike `GetLastInputInfo` which is designed for providing session-specific
user input information, `SharedUserData->LastSystemRITEventTickCount` provides
system-wide time, used by:
- [Task Schedule (TASK_EVENT_TRIGGER_ON_IDLE)](https://learn.microsoft.com/en-us/windows/win32/taskschd/i)
- [Inactivity Monitoring](https://learn.microsoft.com/en-us/windows/win32/devnotes/inactivity-monitoring)
- Maybe some places I don't know...

See also:
- [KUSER_SHARED_DATA - Geoff Chappell](https://www.geoffchappell.com/studies/windows/km/ntoskrnl/inc/api/ntexapi_x/kuser_shared_data/index.htm)
- [TASK_TRIGGER_TYPE enumeration - Microsoft Learn](https://learn.microsoft.com/en-us/windows/win32/api/mstask/ne-mstask-task_trigger_type)
- [Task Scheduler: idle conditions](https://learn.microsoft.com/en-us/windows/win32/taskschd/i)
- [GetLastInputInfo function - Microsoft Learn](https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getlastinputinfo)
- [Inactivity Monitoring](https://learn.microsoft.com/en-us/windows/win32/devnotes/inactivity-monitoring)

## Proposed changes

Before this PR, `SharedUserData->LastSystemRITEventTickCount` is never used
and always 0, `gpsi->dwLastSystemRITEventTickCountUpdate` is never used too.
After this PR, `SharedUserData->LastSystemRITEventTickCount` updates correctly
by using `gpsi->dwLastSystemRITEventTickCountUpdate` to record previous update
time, the behavior is the same as on Windows.
This commit is contained in:
Ratin Gao
2026-03-09 01:58:37 +08:00
committed by GitHub
parent d61f7e5cd0
commit 36ffee8ea3
4 changed files with 70 additions and 1 deletions

View File

@@ -3,4 +3,5 @@ add_subdirectory(messagebox)
add_subdirectory(paintdesktop)
add_subdirectory(psmtest)
add_subdirectory(sysicon)
add_subdirectory(useridletime)
add_subdirectory(winstation)

View File

@@ -0,0 +1,5 @@
add_executable(useridletime UserIdleTime.c)
set_module_type(useridletime win32cui UNICODE)
add_importlibs(useridletime user32 msvcrt kernel32)
add_rostests_file(TARGET useridletime SUBDIR suppl)

View File

@@ -0,0 +1,47 @@
/*
* PROJECT: ReactOS Tests
* LICENSE: MIT (https://spdx.org/licenses/MIT)
* PURPOSE: Calculate and print user idle time once per second by using
* user32!GetLastInputInfo() API and SharedUserData->LastSystemRITEventTickCount
* field separately. The former updates in real-time, while the latter updates
* periodically (60s in NT5, 1s since NT6.0). Press Ctrl+C to exit this program.
* COPYRIGHT: Copyright 2026 Ratin Gao <ratin@knsoft.org>
*/
#include <stdio.h>
#define WIN32_NO_STATUS
#include <windef.h>
#include <winbase.h>
#include <winuser.h>
#define NTOS_MODE_USER
#include <ndk/psfuncs.h>
int
_cdecl
wmain(
_In_ int argc,
_In_reads_(argc) _Pre_z_ wchar_t** argv)
{
DWORD dwTickCount, dwError;
LASTINPUTINFO lii = { sizeof(lii) };
puts("User idle time in second (LastSystemRITEventTickCount, GetLastInputInfo):");
for (;;)
{
dwTickCount = GetTickCount();
if (!GetLastInputInfo(&lii))
{
dwError = GetLastError();
printf("GetLastInputInfo failed with: 0x%08lX\n", dwError);
return dwError;
}
printf("\t%lu, %lu\n",
(dwTickCount - SharedUserData->LastSystemRITEventTickCount) / 1000UL,
(dwTickCount - lii.dwTime) / 1000UL);
Sleep(1000);
}
return 0;
}

View File

@@ -10,6 +10,13 @@
#include <win32k.h>
DBG_DEFAULT_CHANNEL(UserInput);
/* LastRITEventTickCount update cycle has been adjusted from 60s to 1s since NT6 */
#if (NTDDI_VERSION < NTDDI_VISTA)
#define LAST_RIT_EVENT_UPDATE_INTERVAL 60000UL
#else
#define LAST_RIT_EVENT_UPDATE_INTERVAL 1000UL
#endif
/* GLOBALS *******************************************************************/
PTHREADINFO ptiRawInput;
@@ -34,7 +41,16 @@ IntLastInputTick(BOOL bUpdate)
if (bUpdate)
{
LastInputTick = EngGetTickCount32();
if (gpsi) gpsi->dwLastRITEventTickCount = LastInputTick;
if (gpsi)
{
gpsi->dwLastRITEventTickCount = LastInputTick;
if (gpsi->dwLastRITEventTickCount - gpsi->dwLastSystemRITEventTickCountUpdate >
LAST_RIT_EVENT_UPDATE_INTERVAL)
{
SharedUserData->LastSystemRITEventTickCount = LastInputTick;
gpsi->dwLastSystemRITEventTickCountUpdate = LastInputTick;
}
}
}
return LastInputTick;
}