boinc/clientlib/win/IdleTracker.cpp

287 lines
7.1 KiB
C++

/**
* IdleTracker - a DLL that tracks the user's idle input time
* system-wide.
*
* Usage
* =====
* - call IdleTrackerInit() when you want to start monitoring.
* - call IdleTrackerTerm() when you want to stop monitoring.
* - to get the time past since last user input, do the following:
* GetTickCount() - IdleTrackerGetLastTickCount()
*
* Author: Sidney Chong
* Date: 25/5/2000
* Version: 1.0
**/
#include "stdafx.h"
#include "boinc_dll.h"
#include "win_util.h"
/**
* The following global data is only shared in this instance of the DLL
**/
HMODULE g_hUser32 = NULL;
HANDLE g_hMemoryMappedData = NULL;
BOOL g_bIsWindows2000Compatible = FALSE;
BOOL g_bIsTerminalServicesEnabled = FALSE;
/**
* The following global data is SHARED among all instances of the DLL
* (processes) within a terminal services session.
**/
#pragma data_seg(".IdleTrac") // you must define as SHARED in .def
HHOOK g_hHkKeyboard = NULL; // handle to the keyboard hook
HHOOK g_hHkMouse = NULL; // handle to the mouse hook
LONG g_mouseLocX = -1; // x-location of mouse position
LONG g_mouseLocY = -1; // y-location of mouse position
DWORD g_dwLastTick = 0; // tick time of last input event
#pragma data_seg()
#pragma comment(linker, "/section:.IdleTrac,rws")
/**
* The following global data is SHARED among all instances of the DLL
* (processes); i.e., these are system-wide globals.
**/
struct SystemWideIdleData
{
DWORD dwLastTick; // tick time of last input event
};
struct SystemWideIdleData* g_pSystemWideIdleData = NULL;
/**
* Define stuff that only exists on Windows 2000 compatible machines
**/
typedef struct tagLASTINPUTINFO {
UINT cbSize;
DWORD dwTime;
} LASTINPUTINFO, *PLASTINPUTINFO;
typedef BOOL (WINAPI *GETLASTINPUTINFO)(PLASTINPUTINFO);
GETLASTINPUTINFO g_fnGetLastInputInfo = NULL;
/**
* Keyboard hook: record tick count
**/
LRESULT CALLBACK KeyboardTracker(int code, WPARAM wParam, LPARAM lParam)
{
if (code==HC_ACTION)
{
g_dwLastTick = GetTickCount();
}
return ::CallNextHookEx(g_hHkKeyboard, code, wParam, lParam);
}
/**
* Mouse hook: record tick count
**/
LRESULT CALLBACK MouseTracker(int code, WPARAM wParam, LPARAM lParam)
{
if (code==HC_ACTION)
{
MOUSEHOOKSTRUCT* pStruct = (MOUSEHOOKSTRUCT*)lParam;
//we will assume that any mouse msg with the same locations as spurious
if (pStruct->pt.x != g_mouseLocX || pStruct->pt.y != g_mouseLocY)
{
g_mouseLocX = pStruct->pt.x;
g_mouseLocY = pStruct->pt.y;
g_dwLastTick = GetTickCount();
}
}
return ::CallNextHookEx(g_hHkMouse, code, wParam, lParam);
}
/**
* Get tick count of last keyboard or mouse event
**/
EXTERN_C __declspec(dllexport) DWORD BOINCGetIdleTickCount()
{
DWORD dwCurrentTickCount = GetTickCount();
DWORD dwLastTickCount = 0;
if ( g_bIsWindows2000Compatible )
{
if ( g_pSystemWideIdleData )
{
LASTINPUTINFO lii;
ZeroMemory( &lii, sizeof(lii) );
lii.cbSize = sizeof(lii);
g_fnGetLastInputInfo( &lii );
/**
* If both values are greater than the system tick count then
* the system must have looped back to the beginning.
**/
if ( ( dwCurrentTickCount < lii.dwTime ) &&
( dwCurrentTickCount < g_pSystemWideIdleData->dwLastTick ) )
{
lii.dwTime = dwCurrentTickCount;
g_pSystemWideIdleData->dwLastTick = dwCurrentTickCount;
}
if ( lii.dwTime > g_pSystemWideIdleData->dwLastTick )
g_pSystemWideIdleData->dwLastTick = lii.dwTime;
dwLastTickCount = g_pSystemWideIdleData->dwLastTick;
}
}
else
{
dwLastTickCount = g_dwLastTick;
}
return (dwCurrentTickCount - dwLastTickCount);
}
/**
* Initialize DLL: install kbd/mouse hooks.
**/
BOOL IdleTrackerStartup()
{
BOOL bExists = FALSE;
BOOL bResult = FALSE;
SECURITY_ATTRIBUTES sec_attr;
SECURITY_DESCRIPTOR sd;
g_bIsWindows2000Compatible = IsWindows2000Compatible();
g_bIsTerminalServicesEnabled = IsTerminalServicesEnabled();
if ( !g_bIsWindows2000Compatible )
{
if ( NULL == g_hHkKeyboard )
{
g_hHkKeyboard = SetWindowsHookEx(
WH_KEYBOARD,
KeyboardTracker,
g_hModule,
0
);
}
if ( NULL == g_hHkMouse )
{
g_hHkMouse = SetWindowsHookEx(
WH_MOUSE,
MouseTracker,
g_hModule,
0
);
}
_ASSERT( g_hHkKeyboard );
_ASSERT( g_hHkMouse );
}
else
{
g_hUser32 = LoadLibrary("user32.dll");
if (g_hUser32)
g_fnGetLastInputInfo = (GETLASTINPUTINFO)GetProcAddress(g_hUser32, "GetLastInputInfo");
/*
* Create a security descriptor that will allow
* everyone full access.
*/
InitializeSecurityDescriptor( &sd, SECURITY_DESCRIPTOR_REVISION );
SetSecurityDescriptorDacl( &sd, TRUE, NULL, FALSE );
sec_attr.nLength = sizeof(sec_attr);
sec_attr.bInheritHandle = TRUE;
sec_attr.lpSecurityDescriptor = &sd;
/*
* Create a filemap object that is global for everyone,
* including users logged in via terminal services.
*/
g_hMemoryMappedData =
CreateFileMapping(
INVALID_HANDLE_VALUE,
&sec_attr,
PAGE_READWRITE,
0,
4096,
"Global\\BoincIdleTracker"
);
if( NULL != g_hMemoryMappedData )
{
if( ERROR_ALREADY_EXISTS == GetLastError() )
bExists = TRUE;
g_pSystemWideIdleData = (struct SystemWideIdleData*)
MapViewOfFile(
g_hMemoryMappedData,
FILE_MAP_ALL_ACCESS,
0,
0,
0
);
_ASSERT( g_pSystemWideIdleData );
}
if( !bExists && g_pSystemWideIdleData )
{
g_pSystemWideIdleData->dwLastTick = GetTickCount();
}
}
if ( !g_bIsWindows2000Compatible )
{
if ( !g_hHkKeyboard || !g_hHkMouse )
bResult = FALSE;
else
bResult = TRUE;
}
else
{
if ( !g_hUser32 || !g_fnGetLastInputInfo || !g_hMemoryMappedData || !g_pSystemWideIdleData )
bResult = FALSE;
else
bResult = TRUE;
}
return bResult;
}
/**
* Terminate DLL: remove hooks.
**/
void IdleTrackerShutdown()
{
if ( !g_bIsWindows2000Compatible )
{
BOOL bResult;
if ( g_hHkKeyboard )
{
bResult = UnhookWindowsHookEx( g_hHkKeyboard );
_ASSERT( bResult );
g_hHkKeyboard = NULL;
}
if ( g_hHkMouse )
{
bResult = UnhookWindowsHookEx(g_hHkMouse);
_ASSERT( bResult );
g_hHkMouse = NULL;
}
}
else
{
if( NULL != g_pSystemWideIdleData )
{
UnmapViewOfFile(g_pSystemWideIdleData);
CloseHandle(g_hMemoryMappedData);
}
if ( NULL != g_hUser32 )
FreeLibrary(g_hUser32);
}
}
const char *BOINC_RCSID_14d432d5b3 = "$Id$";