|
|
/*++
Copyright (c) 2000 Microsoft Corporation. All rights reserved.
Module Name: tabdev.cpp
Abstract: This module contains tablet device functions.
Author: Michael Tsang (MikeTs) 01-Jun-2000
Environment: User mode
Revision History: --*/
#include "pch.h"
/*++
@doc INTERNAL
@func unsigned | DeviceThread | Device thread.
@parm IN OUT PDEVICE_DATA | pdevdata | Points to device data.
@rvalue Always returns 0. --*/
unsigned __stdcall DeviceThread( IN PVOID param ) { TRACEPROC("DeviceThread", 2) PDEVICE_DATA pdevdata = (PDEVICE_DATA)param; BOOL fDigitizer = (pdevdata == &gdevDigitizer); BOOL fSuccess = TRUE;
TRACEENTER(("(pdevdata=%p,Thread=%s)\n", pdevdata, fDigitizer? "Digitizer": "Buttons"));
// Bump our priority so we can service device data ASAP.
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_HIGHEST);
if (OpenTabletDevice(pdevdata)) { OVERLAPPED overLapped; HANDLE ahWait[2]; PCHAR apBuf[2];
// Initialize the OVERLAPPED structure for ReadFile
overLapped.Offset = overLapped.OffsetHigh = 0; overLapped.hEvent = CreateEvent(NULL, // default security
FALSE, // auto-reset event
FALSE, // initially unset
NULL); // no name
TRACEASSERT(overLapped.hEvent != NULL);
// Create the array of event handles to wait upon (below)
ahWait[0] = pdevdata->hStopDeviceEvent; ahWait[1] = overLapped.hEvent;
// Allocate and zero-init a couple of buffers for device reports
apBuf[0] = (PCHAR)calloc(pdevdata->hidCaps.InputReportByteLength, sizeof(CHAR)); apBuf[1] = (PCHAR)calloc(pdevdata->hidCaps.InputReportByteLength, sizeof(CHAR)); TRACEASSERT(apBuf[0] && apBuf[1]);
// Allocate a buffer for HID report button states
pdevdata->dwcButtons = HidP_MaxUsageListLength(HidP_Input, 0, pdevdata->pPreParsedData); if (pdevdata->dwcButtons > 0) { pdevdata->pDownButtonUsages = (PUSAGE)malloc(pdevdata->dwcButtons* sizeof(USAGE)); TRACEASSERT(pdevdata->pDownButtonUsages); } else { pdevdata->pDownButtonUsages = NULL; }
if (fDigitizer) { // Determine the digitizer's (default) max X & Y values for scaling
// the reports later
GetMinMax(HID_USAGE_PAGE_GENERIC, HID_USAGE_GENERIC_X, &gdwMinX, &gdwMaxX); gdwRngX = gdwMaxX - gdwMinX;
GetMinMax(HID_USAGE_PAGE_GENERIC, HID_USAGE_GENERIC_Y, &gdwMinY, &gdwMaxY); gdwRngY = gdwMaxY - gdwMinY; #ifdef DRAW_INK
ghwndDrawInk = GetDesktopWindow(); #endif
}
PTSTHREAD pThread = FindThread(fDigitizer? TSF_DIGITHREAD: TSF_BUTTONTHREAD); while (fSuccess && !(gdwfTabSrv & TSF_TERMINATE)) { if (SwitchThreadToInputDesktop(pThread)) { BOOL fImpersonate = FALSE; int iBufIdx = 0; DWORD dwBytesRead;
fImpersonate = ImpersonateCurrentUser();
// Issue an overlapped read to get a device (input) report
if (ReadReportOverlapped(pdevdata, apBuf[iBufIdx], &dwBytesRead, &overLapped)) { DWORD rcWait; MSG Msg;
for (;;) { // Wait for the read to complete, or for the termination
// event to be set
if (fDigitizer) { rcWait = WaitForMultipleObjectsEx(2, ahWait, FALSE, INFINITE, FALSE); } else { rcWait = MsgWaitForMultipleObjects(2, ahWait, FALSE, INFINITE, QS_TIMER); }
if (rcWait == WAIT_OBJECT_0) { // The terminate device event was set.
break; } else if (rcWait == WAIT_OBJECT_0 + 1) { DWORD dwcb; // Previous read has completed, swap the buffer
// pointer and issue another overlapped read
// before processing this report.
PCHAR pBufLast = apBuf[iBufIdx]; iBufIdx ^= 1;
if (!GetOverlappedResult(pdevdata->hDevice, &overLapped, &dwcb, FALSE)) { TRACEWARN(("GetOverlappedResult failed (err=%d).\n", GetLastError())); fSuccess = FALSE; } else { if (!ReadReportOverlapped(pdevdata, apBuf[iBufIdx], &dwBytesRead, &overLapped)) { TABSRVERR(("Read error getting device report (err=%d)", GetLastError())); fSuccess = FALSE; break; }
if (fDigitizer) { ProcessDigitizerReport(pBufLast); } else { ProcessButtonsReport(pBufLast); } } } else if (!fDigitizer && PeekMessage(&Msg, NULL, WM_TIMER, WM_TIMER, PM_REMOVE | PM_NOYIELD)) { ((TIMERPROC)Msg.lParam)(Msg.hwnd, Msg.message, Msg.wParam, Msg.time); } else { TRACEWARN(("WaitForMultipleObject failed (rcWait=%d,err=%d)\n", rcWait, GetLastError())); } } CancelIo(pdevdata->hDevice); } else { TABSRVERR(("Read error getting device report (err=%d)", GetLastError())); fSuccess = FALSE; }
if (fImpersonate) { RevertToSelf(); } } else { TABSRVERR(("Failed to set current desktop.\n")); fSuccess = FALSE; } }
if (pdevdata->pDownButtonUsages) { free(pdevdata->pDownButtonUsages); pdevdata->pDownButtonUsages = NULL; } free(apBuf[0]); free(apBuf[1]); CloseHandle(overLapped.hEvent); CloseTabletDevice(pdevdata); } else { TABSRVERR(("Failed to open %s device.\n", fDigitizer? "Digitizer": "Buttons")); }
TRACEEXIT(("=0\n")); return 0; } //DeviceThread
/*++
@doc INTERNAL
@func BOOL | OpenTabletDevice | Open and initialize the tablet device.
@parm IN OUT PDEVICE_DATA | pDevData | Points to the tablet device data.
@rvalue SUCCESS | Returns TRUE. @rvalue FAILURE | Returns FALSE. --*/
BOOL OpenTabletDevice( IN OUT PDEVICE_DATA pDevData ) { TRACEPROC("OpenTabletDevice", 2) BOOL rc = FALSE; GUID hidGuid; HDEVINFO hDevInfo;
TRACEENTER(("(pDevData=%p,UsagePage=%x,Usage=%x)\n", pDevData, pDevData->UsagePage, pDevData->Usage));
pDevData->hDevice = INVALID_HANDLE_VALUE;
// Get the GUID for HID devices
HidD_GetHidGuid(&hidGuid);
// Get handle to present HID devices
hDevInfo = SetupDiGetClassDevs(&hidGuid, NULL, NULL, (DIGCF_PRESENT | DIGCF_DEVICEINTERFACE));
if (hDevInfo != INVALID_HANDLE_VALUE) { ULONG i;
// Enumerate the HID devices, looking for the digitizer and button devices
for (i = 0; ; ++i) { SP_DEVICE_INTERFACE_DATA devInterface; devInterface.cbSize = sizeof(devInterface);
// First we have to enumerate the device interfaces
if (SetupDiEnumDeviceInterfaces(hDevInfo, 0, &hidGuid, i, &devInterface)) { // Then we get the interface detail information (including device pathname)
PSP_DEVICE_INTERFACE_DETAIL_DATA pIfDetail;
pIfDetail = GetDeviceInterfaceDetail(hDevInfo, &devInterface); TRACEASSERT(pIfDetail != 0);
if (pIfDetail != 0) { DEVICE_DATA thisDev;
TRACEINFO(2, ("HID DEVICE: %s\n", pIfDetail->DevicePath));
// Finally we get specific device info to determine
// if it's one of ours
thisDev = *pDevData; if (GetDeviceData(pIfDetail->DevicePath, &thisDev)) { TRACEINFO(2, ("Usage Page: %d, Usage: %d\n", thisDev.hidCaps.UsagePage, thisDev.hidCaps.Usage));
// Is this is a device we want?
if ((pDevData->UsagePage == thisDev.hidCaps.UsagePage) && (pDevData->Usage == thisDev.hidCaps.Usage)) { *pDevData = thisDev; break; } else { // Not one of our devices, clean-up and try again
CloseTabletDevice(&thisDev); } }
free(pIfDetail); } } else { DWORD dwLastError = GetLastError();
// SetupDiEnumDeviceInterfaces() failed, hopefull with
// ERROR_NO_MORE_ITEMS
if (dwLastError != ERROR_NO_MORE_ITEMS) { TABSRVERR(("SetupDiEnumDeviceInterfaces failed (err=%d)\n", dwLastError)); } break; } }
// Clean-up from SetupDiGetClassDevs()
SetupDiDestroyDeviceInfoList(hDevInfo); } else { TABSRVERR(("SetupDiGetClassDevs failed (err=%d)\n", GetLastError())); }
rc = pDevData->hDevice != INVALID_HANDLE_VALUE;
TRACEEXIT(("=%x\n", rc)); return rc; } //OpenTabletDevice
/*++
@doc INTERNAL
@func VOID | CloseTabletDevice | Close and clean up the tablet device.
@parm IN PDEVICE_DATA | pDevData | Points to the tablet device data.
@rvalue None. --*/
VOID CloseTabletDevice( IN PDEVICE_DATA pDevData ) { TRACEPROC("CloseTabletDevice", 2) TRACEENTER(("(pDevData=%p)\n", pDevData));
if (pDevData->hDevice != INVALID_HANDLE_VALUE) { CloseHandle(pDevData->hDevice); HidD_FreePreparsedData(pDevData->pPreParsedData);
pDevData->hDevice = INVALID_HANDLE_VALUE; }
TRACEEXIT(("!\n")); } //CloseTabletDevice
/*++
@doc INTERNAL
@func PSP_DEVICE_INTERFACE_DETAIL_DATA | GetDeviceInterfaceDetail | Gets interface details for a specific device.
@parm IN HDEVINFO | hDevInfo | Handle to HID device info. @parm IN PSP_DEVICE_INTERFACE_DATA | pDevInterface | Points to device interface data.
@rvalue SUCCESS | Returns pointer to SP_DEVICE_INTERFACE_DETAIL_DATA. @rvalue FAILURE | Returns NULL.
@note Caller is responsible for freeing the returned memory. --*/
PSP_DEVICE_INTERFACE_DETAIL_DATA GetDeviceInterfaceDetail( IN HDEVINFO hDevInfo, IN PSP_DEVICE_INTERFACE_DATA pDevInterface ) { TRACEPROC("GetDeviceInterfaceDetail", 3) PSP_DEVICE_INTERFACE_DETAIL_DATA pDetails; ULONG ulLen = 0;
TRACEENTER(("(hDevInfo=%x,pDevInterface=%p)\n", hDevInfo, pDevInterface));
// Make a call to get the required buffer length
SetupDiGetDeviceInterfaceDetail(hDevInfo, pDevInterface, NULL, 0, &ulLen, NULL); TRACEASSERT(ulLen > 0);
// Allocate a sufficiently large buffer
pDetails = (PSP_DEVICE_INTERFACE_DETAIL_DATA)malloc(ulLen); TRACEASSERT(pDetails != 0);
if (pDetails != NULL) { // Now that we have a buffer, get the details
pDetails->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
if (!SetupDiGetDeviceInterfaceDetail(hDevInfo, pDevInterface, pDetails, ulLen, &ulLen, NULL)) { TABSRVERR(("SetupDiGetDeviceInterfaceDetail failed (err=%d)\n", GetLastError())); free(pDetails); pDetails = NULL; } }
TRACEEXIT(("=%p\n", pDetails)); return pDetails; } //GetDeviceInterfaceDetail
/*++
@doc INTERNAL
@func BOOL | GetDeviceData | Opens a HID device and retrieves the 'preparsed' data and HID capabilities.
@parm IN LPCTSTR | pszDevPath | Points to the device path name. @parm OUT PDEVICE_DATA | pDevData | Points to the DEVICE_DATA structure.
@rvalue SUCCESS | Returns TRUE. @rvalue FAILURE | Returns FALSE. --*/
BOOL GetDeviceData( IN LPCTSTR pszDevPath, OUT PDEVICE_DATA pDevData ) { TRACEPROC("GetDeviceData", 3) BOOL rc = FALSE;
TRACEENTER(("(pszDevPath=%s,pDevData=%p)\n", pszDevPath, pDevData));
// First, open the device for read/write, exclusive access
pDevData->hDevice = CreateFile(pszDevPath, GENERIC_READ | GENERIC_WRITE,// access mode
FILE_SHARE_READ | FILE_SHARE_WRITE,// sharing flags
NULL, // security attributes
OPEN_EXISTING, FILE_FLAG_OVERLAPPED, // flags & attributes
NULL); // template file
if (pDevData->hDevice != INVALID_HANDLE_VALUE) { // Then get the preparsed data and capabilities
if (HidD_GetPreparsedData(pDevData->hDevice, &pDevData->pPreParsedData) && (HidP_GetCaps(pDevData->pPreParsedData, &pDevData->hidCaps) == HIDP_STATUS_SUCCESS)) { #ifdef DEBUG
#if !defined(_UNICODE)
char szAnsi[512]; #endif
TCHAR szBuff[256]; if (HidD_GetManufacturerString(pDevData->hDevice, szBuff, sizeof(szBuff))) { #if !defined(_UNICODE)
WideCharToMultiByte(CP_ACP, 0, (LPWSTR)szBuff, -1, szAnsi, sizeof(szAnsi), NULL, NULL); strcpy(szBuff, szAnsi); #endif
TRACEINFO(2, ("ManufacturerStr: %s\n", szBuff)); }
if (HidD_GetProductString(pDevData->hDevice, szBuff, sizeof(szBuff))) { #if !defined(_UNICODE)
WideCharToMultiByte(CP_ACP, 0, (LPWSTR)szBuff, -1, szAnsi, sizeof(szAnsi), NULL, NULL); strcpy(szBuff, szAnsi); #endif
TRACEINFO(2, ("ProductStr: %s\n", szBuff)); } #endif
rc = TRUE; } else { TABSRVERR(("HidD_GetPreparsedData/HidP_GetCaps failed (err=%d)\n", GetLastError())); CloseHandle(pDevData->hDevice); pDevData->hDevice = INVALID_HANDLE_VALUE; } } else { TRACEWARN(("failed to open %s (err=%d)\n", pszDevPath, GetLastError())); }
TRACEEXIT(("=%x\n", rc)); return rc; } //GetDeviceData
/*++
@doc INTERNAL
@func BOOL | ReadReportOverlapped | Get a device input report using overlapped ReadFile.
@parm IN PDEVICE_DATA | pDevData | Device to read. @parm OUT LPVOID | lpvBuffer | Data buffer. @parm OUT LPDWORD | lpdwcBytesRead | To hold number of bytes read. @parm IN LPOVERLAPPED | lpOverlapped | Overlapped buffer
@rvalue SUCCESS | Returns TRUE. @rvalue FAILURE | Returns FALSE. --*/
BOOL ReadReportOverlapped( IN PDEVICE_DATA pDevData, OUT LPVOID lpvBuffer, OUT LPDWORD lpdwcBytesRead, IN LPOVERLAPPED lpOverlapped ) { TRACEPROC("ReadReportOverlapped", 5) BOOL rc = TRUE;
TRACEENTER(("(pDevData=%p,lpvBuffer=%p,lpdwcBytesRead=%p,lpOverlapped=%p)\n", pDevData, lpvBuffer, lpdwcBytesRead, lpOverlapped));
rc = ReadFile(pDevData->hDevice, lpvBuffer, pDevData->hidCaps.InputReportByteLength, lpdwcBytesRead, lpOverlapped);
if (rc == TRUE) { // ReadFile completed synchronously. Set our completion event
// so this case can be handled by without special checking by
// our caller.
SetEvent(lpOverlapped->hEvent); } else { DWORD dwLastError = GetLastError();
if (dwLastError == ERROR_IO_PENDING) { rc = TRUE; } else { TABSRVERR(("Error reading device report (err=%d)\n", dwLastError)); } }
TRACEEXIT(("=%x\n", rc)); return rc; } //ReadReportOverlapped
|