627 lines
21 KiB
C++
627 lines
21 KiB
C++
/*++
|
|
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
|