Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1029 lines
32 KiB

/*++
Copyright (c) 2000 Microsoft Corporation. All rights reserved.
Module Name:
digidev.cpp
Abstract:
This module contains all the digitizer functions.
Author:
Michael Tsang (MikeTs) 01-Jun-2000
Environment:
User mode
Revision History:
--*/
#include "pch.h"
/*++
@doc INTERNAL
@func BOOL | GetMinMax | Get the logical min & max for a report values.
@parm IN USAGE | UsagePage | Specifies the UsagePage of the report.
@parm IN USAGE | Usage | Specifies the Usage of the report.
@parm OUT PULONG | pulMin | To hold the min value.
@parm OUT PULONG | pulMax | To hold the max value.
@rvalue SUCCESS | Returns TRUE.
@rvalue FAILURE | Returns FALSE.
--*/
BOOL
GetMinMax(
IN USAGE UsagePage,
IN USAGE Usage,
OUT PULONG pulMin,
OUT PULONG pulMax
)
{
TRACEPROC("GetMinMax", 3)
BOOL rc = FALSE;
NTSTATUS status;
HIDP_VALUE_CAPS ValueCaps;
USHORT cValueCaps = 1;
TRACEENTER(("(UsagePage=%x,Usage=%x,pulMin=%p,pulMax=%p)\n",
UsagePage, Usage, pulMin, pulMax));
status = HidP_GetSpecificValueCaps(HidP_Input,
UsagePage,
0,
Usage,
&ValueCaps,
&cValueCaps,
gdevDigitizer.pPreParsedData);
if ((status == HIDP_STATUS_SUCCESS) && (cValueCaps == 1))
{
if ((ValueCaps.LogicalMin >= 0) &&
(ValueCaps.LogicalMax > ValueCaps.LogicalMin))
{
*pulMin = ValueCaps.LogicalMin;
*pulMax = ValueCaps.LogicalMax;
rc = TRUE;
}
}
TRACEEXIT(("=%x\n", rc));
return rc;
} //GetMinMax
/*++
@doc INTERNAL
@func VOID | ProcessDigitizerReport | Process a digitizer input report.
@parm IN PCHAR | pBuff | Buffer containing report data.
@rvalue None.
--*/
VOID
ProcessDigitizerReport(
IN PCHAR pBuff
)
{
TRACEPROC("ProcessDigitizerReport", 5)
NTSTATUS StatusX, StatusY, StatusBtn;
DWORD dwCurrentTime;
ULONG x, y;
ULONG ulLength;
TRACEENTER(("(pBuff=%p)\n", pBuff));
dwCurrentTime = GetTickCount();
StatusX = HidP_GetUsageValue(HidP_Input,
HID_USAGE_PAGE_GENERIC,
0,
HID_USAGE_GENERIC_X,
&x,
gdevDigitizer.pPreParsedData,
pBuff,
gdevDigitizer.hidCaps.InputReportByteLength);
StatusY = HidP_GetUsageValue(HidP_Input,
HID_USAGE_PAGE_GENERIC,
0,
HID_USAGE_GENERIC_Y,
&y,
gdevDigitizer.pPreParsedData,
pBuff,
gdevDigitizer.hidCaps.InputReportByteLength);
ulLength = gdevDigitizer.dwcButtons;// ulLength == max # possible buttons
StatusBtn = HidP_GetButtons(HidP_Input,
HID_USAGE_PAGE_DIGITIZER,
0,
gdevDigitizer.pDownButtonUsages,
&ulLength,
gdevDigitizer.pPreParsedData,
pBuff,
gdevDigitizer.hidCaps.InputReportByteLength);
if ((StatusX == HIDP_STATUS_SUCCESS) &&
(StatusY == HIDP_STATUS_SUCCESS) &&
(StatusBtn == HIDP_STATUS_SUCCESS))
{
ULONG i;
WORD wCurrentButtons = 0;
for (i = 0; i < ulLength; i++)
{
if (gdevDigitizer.pDownButtonUsages[i] ==
HID_USAGE_DIGITIZER_TIP_SWITCH)
{
wCurrentButtons |= TIP_SWITCH;
}
else if (gdevDigitizer.pDownButtonUsages[i] ==
HID_USAGE_DIGITIZER_BARREL_SWITCH)
{
wCurrentButtons |= BARREL_SWITCH;
}
}
if ((gdwPenState == PENSTATE_PENDOWN) &&
(dwCurrentTime - gdwPenDownTime >
(DWORD)gConfig.GestureSettings.iPressHoldTime))
{
//
// PressHold timer has expired, simulate a right down.
//
PressHoldMode(TRUE);
}
else if ((gdwPenState == PENSTATE_PRESSHOLD) &&
(dwCurrentTime - gdwPenDownTime >
(DWORD)gConfig.GestureSettings.iPressHoldTime +
gConfig.GestureSettings.iCancelPressHoldTime))
{
TRACEINFO(3, ("Simulate a left-down on CancelPressHold timeout.\n"));
gdwPenState = PENSTATE_NORMAL;
SetPressHoldCursor(FALSE);
gInput.mi.dwFlags = MOUSEEVENTF_ABSOLUTE |
MOUSEEVENTF_MOVE |
SWAPBUTTONS(giButtonsSwapped,
MOUSEEVENTF_LEFTDOWN,
MOUSEEVENTF_RIGHTDOWN);
gInput.mi.dx = glPenDownX;
gInput.mi.dy = glPenDownY;
SendInput(1, &gInput, sizeof(INPUT));
}
else if ((gdwPenState == PENSTATE_LEFTUP_PENDING) &&
(dwCurrentTime - gdwPenUpTime > TIMEOUT_LEFTCLICK))
{
//
// Left up timer has expired, simulate a left up.
//
TRACEINFO(3, ("Simulate a left-up on timeout.\n"));
gdwPenState = PENSTATE_NORMAL;
gInput.mi.dwFlags = MOUSEEVENTF_ABSOLUTE |
MOUSEEVENTF_MOVE |
SWAPBUTTONS(giButtonsSwapped,
MOUSEEVENTF_LEFTUP,
MOUSEEVENTF_RIGHTUP);
gInput.mi.dx = glPenUpX;
gInput.mi.dy = glPenUpY;
SendInput(1, &gInput, sizeof(INPUT));
}
//
// If the report is the same as last, skip it.
//
if ((x != gLastRawDigiReport.wX) ||
(y != gLastRawDigiReport.wY) ||
(wCurrentButtons != gLastRawDigiReport.wButtonState))
{
//
// Generate mouse movement info.
//
if (gdwfTabSrv & TSF_HAS_LINEAR_MAP)
{
gInput.mi.dx = x;
gInput.mi.dy = y;
AdjustLinearity((PUSHORT)&gInput.mi.dx, (PUSHORT)&gInput.mi.dy);
}
else
{
ULONG t;
// Since we treat the digitizer as an 'absolute' pointing
// device, both the X/Y coordinates must be scaled to a range
// of 0 - 65535.
// (((t = x - gdwMinX) << 16) - t) == ((x - gdwMinX) * 65535)
// In non-raw mode, the pen driver should already be scaled to
// the range of 0-65535, but in raw mode, we still have to do
// this work.
gInput.mi.dx = (x >= gdwMaxX)?
MAX_NORMALIZED_X: // too high
((x <= gdwMinX)?
0: // too low
((((t = x - gdwMinX) << 16) - t) / gdwRngX));
gInput.mi.dy = (y >= gdwMaxY)?
MAX_NORMALIZED_Y : // too high
((y <= gdwMinY)?
0: // too low
((((t = y - gdwMinY) << 16) - t) / gdwRngY));
}
gInput.mi.dx = (((gInput.mi.dx > glLongOffset)?
gInput.mi.dx - glLongOffset: 0)*NUM_PIXELS_LONG)/
max(gcxPrimary, gcyPrimary);
gInput.mi.dy = (((gInput.mi.dy > glShortOffset)?
gInput.mi.dy - glShortOffset: 0)*NUM_PIXELS_SHORT)/
min(gcxPrimary, gcyPrimary);
if (gdwfTabSrv & TSF_PORTRAIT_MODE)
{
LONG temp;
if (gdwfTabSrv & TSF_PORTRAIT_MODE2)
{
temp = gInput.mi.dx;
gInput.mi.dx = MAX_NORMALIZED_Y - gInput.mi.dy;
}
else
{
temp = MAX_NORMALIZED_X - gInput.mi.dx;
gInput.mi.dx = gInput.mi.dy;
}
gInput.mi.dy = temp;
}
//
// Pen tilt compensation.
//
gInput.mi.dx += gConfig.PenTilt.dx;
gInput.mi.dy += gConfig.PenTilt.dy;
NotifyClient(RawPtEvent,
wCurrentButtons,
(y << 16) | (x & 0xffff));
ProcessMouseEvent(gInput.mi.dx,
gInput.mi.dy,
wCurrentButtons,
dwCurrentTime,
FALSE);
#ifdef DRAW_INK
if (ghwndDrawInk != NULL)
{
static BOOL fPenDown = FALSE;
static POINT ptPrev = {0, 0};
static HPEN hpen = NULL;
HDC hdc;
if (!(gwLastButtons & TIP_SWITCH) &&
(wCurrentButtons & TIP_SWITCH))
{
hpen = CreatePen(PS_SOLID, 0, RGB(255, 255, 255));
fPenDown = TRUE;
ptPrev.x = NORMAL_TO_SCREEN_X(gInput.mi.dx);
ptPrev.y = NORMAL_TO_SCREEN_Y(gInput.mi.dy);
}
else if ((gwLastButtons & TIP_SWITCH) &&
!(wCurrentButtons & TIP_SWITCH))
{
DeleteObject(hpen);
hpen = NULL;
fPenDown = FALSE;
}
else if (fPenDown)
{
HPEN hpenOld;
POINT pt;
pt.x = NORMAL_TO_SCREEN_X(gInput.mi.dx);
pt.y = NORMAL_TO_SCREEN_Y(gInput.mi.dy);
hdc = GetDC(ghwndDrawInk);
hpenOld = (HPEN)SelectObject(hdc, hpen);
MoveToEx(hdc, ptPrev.x, ptPrev.y, NULL);
LineTo(hdc, pt.x, pt.y);
SelectObject(hdc, hpenOld);
ReleaseDC(ghwndDrawInk, hdc);
ptPrev = pt;
}
}
#endif
gwLastButtons = wCurrentButtons;
gLastRawDigiReport.wX = (WORD)x;
gLastRawDigiReport.wY = (WORD)y;
gLastRawDigiReport.wButtonState = wCurrentButtons;
gLastRawDigiReport.dwTime = dwCurrentTime;
}
}
else
{
TABSRVERR(("failed getting data (StatusX=%d,StatusY=%d,StatusBtn=%d,Len=%d)\n",
StatusX, StatusY, StatusBtn, ulLength));
}
TRACEEXIT(("!\n"));
return;
} //ProcessDigitizerReport
/*++
@doc INTERNAL
@func VOID | AdjustLinearity | Adjust data according to the linearity map.
@parm IN OUT PUSHORT | pwX | Points to the X data.
@parm IN OUT PUSHORT | pwY | Points to the Y data.
@rvalue None.
--*/
VOID
AdjustLinearity(
IN OUT PUSHORT pwX,
IN OUT PUSHORT pwY
)
{
TRACEPROC("AdjustLinearity", 5)
int ix, iy, dix, diy, n;
DIGIRECT Rect;
LONG x, y;
TRACEENTER(("(x=%d,y=%d)\n", *pwX, *pwY));
for (n = 0, ix = gixIndex, iy = giyIndex; ; ix += dix, iy += diy, n++)
{
//
// Safe guard from never-ending loop.
//
TRACEASSERT(n <= NUM_LINEAR_XPTS + NUM_LINEAR_YPTS);
if (n > NUM_LINEAR_XPTS + NUM_LINEAR_YPTS)
{
TABSRVERR(("AdjustLinearity caught in a loop.\n"));
break;
}
if ((*pwX < gConfig.LinearityMap.Data[iy][ix].wDigiPtX) &&
(*pwX < gConfig.LinearityMap.Data[iy+1][ix].wDigiPtX))
{
dix = ix? -1: 0;
}
else if ((*pwX >= gConfig.LinearityMap.Data[iy][ix+1].wDigiPtX) &&
(*pwX >= gConfig.LinearityMap.Data[iy+1][ix+1].wDigiPtX))
{
dix = (ix + 2 < NUM_LINEAR_XPTS)? 1: 0;
}
else
{
dix = 0;
}
if ((*pwY < gConfig.LinearityMap.Data[iy][ix].wDigiPtY) &&
(*pwY < gConfig.LinearityMap.Data[iy][ix+1].wDigiPtY))
{
diy = iy? -1: 0;
}
else if ((*pwY >= gConfig.LinearityMap.Data[iy+1][ix].wDigiPtY) &&
(*pwY >= gConfig.LinearityMap.Data[iy+1][ix+1].wDigiPtY))
{
diy = (iy + 2 < NUM_LINEAR_YPTS)? 1: 0;
}
else
{
diy = 0;
}
//
// If delta-X and delta-Y are both zeros, we found the containing
// rectangle.
//
if ((dix == 0) && (diy == 0))
{
break;
}
}
TRACEASSERT(gConfig.LinearityMap.Data[iy+1][ix].wDigiPtY -
gConfig.LinearityMap.Data[iy][ix].wDigiPtY);
TRACEASSERT(gConfig.LinearityMap.Data[iy+1][ix+1].wDigiPtY -
gConfig.LinearityMap.Data[iy][ix+1].wDigiPtY);
Rect.wx0 = gConfig.LinearityMap.Data[iy][ix].wDigiPtX +
((*pwY - gConfig.LinearityMap.Data[iy][ix].wDigiPtY)*
(gConfig.LinearityMap.Data[iy+1][ix].wDigiPtX -
gConfig.LinearityMap.Data[iy][ix].wDigiPtX))/
(gConfig.LinearityMap.Data[iy+1][ix].wDigiPtY -
gConfig.LinearityMap.Data[iy][ix].wDigiPtY);
Rect.wx1 = gConfig.LinearityMap.Data[iy][ix+1].wDigiPtX +
((*pwY - gConfig.LinearityMap.Data[iy][ix+1].wDigiPtY)*
(gConfig.LinearityMap.Data[iy+1][ix+1].wDigiPtX -
gConfig.LinearityMap.Data[iy][ix+1].wDigiPtX))/
(gConfig.LinearityMap.Data[iy+1][ix+1].wDigiPtY -
gConfig.LinearityMap.Data[iy][ix+1].wDigiPtY);
TRACEASSERT(gConfig.LinearityMap.Data[iy][ix+1].wDigiPtX -
gConfig.LinearityMap.Data[iy][ix].wDigiPtX);
TRACEASSERT(gConfig.LinearityMap.Data[iy+1][ix+1].wDigiPtX -
gConfig.LinearityMap.Data[iy+1][ix].wDigiPtX);
Rect.wy0 = gConfig.LinearityMap.Data[iy][ix].wDigiPtY +
((*pwX - gConfig.LinearityMap.Data[iy][ix].wDigiPtX)*
(gConfig.LinearityMap.Data[iy][ix+1].wDigiPtY -
gConfig.LinearityMap.Data[iy][ix].wDigiPtY))/
(gConfig.LinearityMap.Data[iy][ix+1].wDigiPtX -
gConfig.LinearityMap.Data[iy][ix].wDigiPtX);
Rect.wy1 = gConfig.LinearityMap.Data[iy+1][ix].wDigiPtY +
((*pwX - gConfig.LinearityMap.Data[iy+1][ix].wDigiPtX)*
(gConfig.LinearityMap.Data[iy+1][ix+1].wDigiPtY -
gConfig.LinearityMap.Data[iy+1][ix].wDigiPtY))/
(gConfig.LinearityMap.Data[iy+1][ix+1].wDigiPtX -
gConfig.LinearityMap.Data[iy+1][ix].wDigiPtX);
//
// Remember the upper-left corner of the containing rectangle so that
// we will start with this rectangle the next time.
//
gixIndex = ix;
giyIndex = iy;
//
// Calculate the adjustment:
// x = x0Ref + (xDigi - x0Digi)*(x1Ref - x0Ref)/(x1Digi - x0Digi)
// y = y0Ref + (yDigi - y0Digi)*(y1Ref - y0Ref)/(y1Digi - y1Digi)
//
TRACEASSERT((Rect.wx1 - Rect.wx0) != 0);
TRACEASSERT((Rect.wy1 - Rect.wy0) != 0);
x = gConfig.LinearityMap.Data[iy][ix].wRefPtX +
((*pwX - Rect.wx0)*
(gConfig.LinearityMap.Data[iy][ix + 1].wRefPtX -
gConfig.LinearityMap.Data[iy][ix].wRefPtX))/
(Rect.wx1 - Rect.wx0);
*pwX = (USHORT)((x < 0)? 0:
(x > MAX_NORMALIZED_X)? MAX_NORMALIZED_X: x);
y = gConfig.LinearityMap.Data[iy][ix].wRefPtY +
((*pwY - Rect.wy0)*
(gConfig.LinearityMap.Data[iy + 1][ix].wRefPtY -
gConfig.LinearityMap.Data[iy][ix].wRefPtY))/
(Rect.wy1 - Rect.wy0);
*pwY = (USHORT)((y < 0)? 0:
(y > MAX_NORMALIZED_Y)? MAX_NORMALIZED_Y: y);
TRACEEXIT(("!(x=%d,y=%d)\n", *pwX, *pwY));
return;
} //AdjustLinearity
/*++
@doc INTERNAL
@func LRESULT | ProcessMouseEvent | Process the mouse event.
@parm IN LONG | x | Current X.
@parm IN LONG | y | Current Y.
@parm IN WORD | wButtons | Current buttons state.
@parm IN DWORD | dwTime | Current time.
@parm IN BOOL | fLowLevelMouse | TRUE if called by LowLevelMouseProc.
@rvalue SUCCESS | Returns non-zero to eat the mouse event.
@rvalue FAILURE | Returns 0 to pass along the event to next handler.
--*/
LRESULT
ProcessMouseEvent(
IN LONG x,
IN LONG y,
IN WORD wButtons,
IN DWORD dwTime,
IN BOOL fLowLevelMouse
)
{
TRACEPROC("ProcessMouseEvent", 5)
LRESULT rc = 0;
DWORD dwEvent;
TRACEENTER(("(x=%d,y=%d,Buttons=%x,Time=%d,fLLMouse=%x)\n",
x, y, wButtons, dwTime, fLowLevelMouse));
gInput.mi.dwFlags = MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE;
if (fLowLevelMouse)
{
gInput.mi.dwFlags |= MOUSEEVENTF_VIRTUALDESK;
}
// Has the tip switch changed state?
if (((gwLastButtons ^ wButtons) & TIP_SWITCH) != 0)
{
if (wButtons & TIP_SWITCH)
{
//
// Tip switch is down.
//
if (gdwPenState == PENSTATE_LEFTUP_PENDING)
{
if (fLowLevelMouse)
{
KillTimer(ghwndMouse, TIMERID_PRESSHOLD);
}
TRACEINFO(3, ("Simulate a left up on pen down.\n"));
gdwPenState = PENSTATE_NORMAL;
dwEvent = SWAPBUTTONS(giButtonsSwapped,
MOUSEEVENTF_LEFTUP,
MOUSEEVENTF_RIGHTUP);
gInput.mi.dwFlags |= dwEvent;
gInput.mi.dx = glPenUpX;
gInput.mi.dy = glPenUpY;
SendInput(1, &gInput, sizeof(INPUT));
gInput.mi.dwFlags &= ~dwEvent;
gInput.mi.dx = x;
gInput.mi.dy = y;
}
if (gConfig.GestureSettings.dwfFeatures &
GESTURE_FEATURE_PRESSHOLD_ENABLED)
{
if (CanDoPressHold(x, y))
{
TRACEINFO(3, ("Pendown.\n"));
//
// Press and Hold is enabled, so hold back the left down event.
//
gdwPenDownTime = dwTime;
glPenDownX = x;
glPenDownY = y;
gdwPenState = PENSTATE_PENDOWN;
if (fLowLevelMouse)
{
SetTimer(ghwndMouse,
TIMERID_PRESSHOLD,
gConfig.GestureSettings.iPressHoldTime,
NULL);
}
rc = 1;
}
else
{
gInput.mi.dwFlags |= SWAPBUTTONS(giButtonsSwapped,
MOUSEEVENTF_LEFTDOWN,
MOUSEEVENTF_RIGHTDOWN);
}
}
else
{
gInput.mi.dwFlags |= SWAPBUTTONS(giButtonsSwapped,
MOUSEEVENTF_LEFTDOWN,
MOUSEEVENTF_RIGHTDOWN);
}
}
else
{
//
// Tip switch is up.
//
if (gdwPenState == PENSTATE_PENDOWN)
{
//
// If we still have pendown pending, it means presshold timer
// has not expired, so we must cancel pendown pending and
// simulate a left click.
//
TRACEINFO(3, ("Simulate a left-down.\n"));
if (fLowLevelMouse)
{
KillTimer(ghwndMouse, TIMERID_PRESSHOLD);
}
gdwPenState = PENSTATE_LEFTUP_PENDING;
//
// Simulate a left down.
//
gInput.mi.dwFlags |= SWAPBUTTONS(giButtonsSwapped,
MOUSEEVENTF_LEFTDOWN,
MOUSEEVENTF_RIGHTDOWN);
gInput.mi.dx = glPenDownX;
gInput.mi.dy = glPenDownY;
SendInput(1, &gInput, sizeof(INPUT));
gdwPenUpTime = dwTime;
glPenUpX = x;
glPenUpY = y;
rc = 1;
if (fLowLevelMouse)
{
SetTimer(ghwndMouse,
TIMERID_PRESSHOLD,
TIMEOUT_LEFTCLICK,
NULL);
}
}
else if ((gdwPenState == PENSTATE_PRESSHOLD) ||
(gdwPenState == PENSTATE_RIGHTDRAG))
{
if (gdwPenState == PENSTATE_PRESSHOLD)
{
TRACEINFO(3, ("Simulate a right click.\n"));
SetPressHoldCursor(FALSE);
dwEvent = SWAPBUTTONS(giButtonsSwapped,
MOUSEEVENTF_RIGHTDOWN,
MOUSEEVENTF_LEFTDOWN);
gInput.mi.dwFlags |= dwEvent;
gInput.mi.dx = glPenDownX;
gInput.mi.dy = glPenDownY;
SendInput(1, &gInput, sizeof(INPUT));
gInput.mi.dwFlags &= ~dwEvent;
gInput.mi.dx = x;
gInput.mi.dy = y;
}
else
{
TRACEINFO(3, ("Simulate a right up.\n"));
}
gInput.mi.dwFlags |= SWAPBUTTONS(giButtonsSwapped,
MOUSEEVENTF_RIGHTUP,
MOUSEEVENTF_LEFTUP);
if (fLowLevelMouse)
{
SendInput(1, &gInput, sizeof(INPUT));
rc = 1;
}
PressHoldMode(FALSE);
}
else
{
TRACEINFO(3, ("Do a left up.\n"));
gInput.mi.dwFlags |= SWAPBUTTONS(giButtonsSwapped,
MOUSEEVENTF_LEFTUP,
MOUSEEVENTF_RIGHTUP);
}
}
}
else if (gdwPenState == PENSTATE_PENDOWN)
{
int cxScreen = fLowLevelMouse? gcxScreen: gcxPrimary,
cyScreen = fLowLevelMouse? gcyScreen: gcyPrimary;
if ((((abs(x - glPenDownX)*cxScreen) >> 16) >
gConfig.GestureSettings.iHoldTolerance) ||
(((abs(y - glPenDownY)*cyScreen) >> 16) >
gConfig.GestureSettings.iHoldTolerance))
{
TRACEINFO(3, ("Cancel pen down pending, simulate a left down.\n"));
if (fLowLevelMouse)
{
KillTimer(ghwndMouse, 1);
}
gdwPenState = PENSTATE_NORMAL;
dwEvent = SWAPBUTTONS(giButtonsSwapped,
MOUSEEVENTF_LEFTDOWN,
MOUSEEVENTF_RIGHTDOWN);
gInput.mi.dwFlags |= dwEvent;
gInput.mi.dx = glPenDownX;
gInput.mi.dy = glPenDownY;
SendInput(1, &gInput, sizeof(INPUT));
gInput.mi.dwFlags &= ~dwEvent;
gInput.mi.dx = x;
gInput.mi.dy = y;
}
}
else if (gdwPenState == PENSTATE_PRESSHOLD)
{
int cxScreen = fLowLevelMouse? gcxScreen: gcxPrimary,
cyScreen = fLowLevelMouse? gcyScreen: gcyPrimary;
if ((((abs(x - glPenDownX)*cxScreen) >> 16) >
gConfig.GestureSettings.iHoldTolerance) ||
(((abs(y - glPenDownY)*cyScreen) >> 16) >
gConfig.GestureSettings.iHoldTolerance))
{
KillTimer(ghwndMouse, TIMERID_PRESSHOLD);
TRACEINFO(3, ("Simulate a right drag.\n"));
SetPressHoldCursor(FALSE);
gdwPenState = PENSTATE_RIGHTDRAG;
dwEvent = SWAPBUTTONS(giButtonsSwapped,
MOUSEEVENTF_RIGHTDOWN,
MOUSEEVENTF_LEFTDOWN);
gInput.mi.dwFlags |= dwEvent;
gInput.mi.dx = glPenDownX;
gInput.mi.dy = glPenDownY;
SendInput(1, &gInput, sizeof(INPUT));
gInput.mi.dwFlags &= ~dwEvent;
gInput.mi.dx = x;
gInput.mi.dy = y;
}
}
else if (gdwPenState == PENSTATE_LEFTUP_PENDING)
{
//
// Eat any movement before left-up timer expires.
//
rc = 1;
}
if (gConfig.GestureSettings.dwfFeatures & GESTURE_FEATURE_RECOG_ENABLED)
{
RecognizeGesture(gInput.mi.dx,
gInput.mi.dy,
wButtons,
dwTime,
fLowLevelMouse);
}
if (gdwfTabSrv & TSF_SUPERTIP_SENDINK)
{
LONG x, y;
x = NORMAL_TO_SCREEN_X(gInput.mi.dx);
y = NORMAL_TO_SCREEN_Y(gInput.mi.dy);
PostMessage(ghwndSuperTIPInk,
guimsgSuperTIPInk,
gInput.mi.dwFlags,
(LPARAM)((y << 16) | (x & 0xffff)));
if (gInput.mi.dwFlags & MOUSEEVENTF_LEFTUP)
{
gdwfTabSrv &= ~TSF_SUPERTIP_SENDINK;
}
rc = 1;
}
else if (!fLowLevelMouse && (rc == 0))
{
// Convert digitizer input to mouse input
SendInput(1, &gInput, sizeof(INPUT));
}
TRACEEXIT(("=%x\n", rc));
return rc;
} //ProcessMouseEvent
/*++
@doc INTERNAL
@func VOID | PressHoldMode | Enable or disable Press and Hold mode.
@parm IN BOOL | fEnable | TRUE if entering Press and Hold mode.
@rvalue None.
--*/
VOID
PressHoldMode(
IN BOOL fEnable
)
{
TRACEPROC("PressHold", 3)
TRACEENTER(("(fEnable=%x)\n", fEnable));
if (fEnable)
{
TRACEINFO(3, ("Entering Press and Hold mode.\n"));
SetPressHoldCursor(TRUE);
gdwPenState = PENSTATE_PRESSHOLD;
}
else
{
TRACEINFO(3, ("Exiting Press and Hold mode.\n"));
gdwPenState = PENSTATE_NORMAL;
}
TRACEEXIT(("!\n"));
return;
} //PressHoldMode
/*++
@doc INTERNAL
@func VOID | SetPressHoldCursor | Set Press and Hold cursor.
@parm IN BOOL | fPressHold | TRUE to set press and hold cursor.
@rvalue None.
--*/
VOID
SetPressHoldCursor(
IN BOOL fPressHold
)
{
TRACEPROC("SetPressHoldCursor", 3)
// BOOL rc = FALSE;
// static HCURSOR hcurPrev = NULL;
static POINT pt = {0, 0};
HDC hDC;
HRGN hrgn;
TRACEENTER(("(fPressHold=%x)\n", fPressHold));
if (fPressHold)
{
GetCursorPos(&pt);
}
hDC = GetDC(NULL);
TRACEASSERT(hDC != NULL);
if (hDC != NULL)
{
hrgn = CreateEllipticRgn(pt.x - 10, pt.y - 10, pt.x + 10, pt.y + 10);
TRACEASSERT(hrgn != NULL);
if (hrgn != NULL)
{
InvertRgn(hDC, hrgn);
DeleteObject(hrgn);
}
ReleaseDC(NULL, hDC);
}
#if 0
if (fPressHold)
{
#if 0
hcurPrev = (HCURSOR)LoadImage(NULL,
MAKEINTRESOURCE(OCR_NORMAL),
IMAGE_CURSOR,
0,
0,
LR_SHARED | LR_DEFAULTSIZE);
TRACEASSERT(hcurPrev != NULL);
#endif
rc = SetSystemCursor(CopyCursor(ghcurPressHold), OCR_NORMAL);
TRACEASSERT(rc == TRUE);
}
else
{
#if 0
if (hcurPrev != NULL)
{
SetCursor(hcurPrev);
rc = SetSystemCursor(CopyCursor(hcurPrev), OCR_NORMAL);
TRACEASSERT(rc == TRUE);
hcurPrev = NULL;
}
else
{
TABSRVERR(("Failed to restore normal cursor (err=%d.\n",
GetLastError()));
}
#endif
//#if 0
LONG rcReg;
HKEY hkUser, hkey;
HCURSOR hcursor = NULL;
rcReg = RegOpenCurrentUser(KEY_READ, &hkUser);
if (rcReg == ERROR_SUCCESS)
{
rcReg = RegOpenKey(hkUser, REGSTR_PATH_CURSORS, &hkey);
if (rcReg == ERROR_SUCCESS)
{
TCHAR szFile[MAX_PATH];
DWORD dwcb = sizeof(szFile);
rcReg = RegQueryValueEx(hkey,
TEXT("Arrow"),
NULL,
NULL,
(LPBYTE)szFile,
&dwcb);
if (rcReg == ERROR_SUCCESS)
{
TRACEINFO(1, ("CursorArrow=%s\n", szFile));
hcursor = (HCURSOR)LoadImage(NULL,
MakeFileName(szFile),
IMAGE_CURSOR,
0,
0,
LR_LOADFROMFILE | LR_DEFAULTSIZE);
}
else
{
TRACEWARN(("Failed to read Arrow registry value (rcReg=%x)\n",
rcReg));
}
RegCloseKey(hkey);
}
else
{
TRACEWARN(("Failed to open cursor registry key (rcReg=%x).\n",
rcReg));
}
RegCloseKey(hkUser);
}
else
{
TRACEWARN(("Failed to open current user (rcReg=%x).\n", rcReg));
}
if (hcursor == NULL)
{
hcursor = CopyCursor(ghcurNormal);
}
TRACEASSERT(hcursor != NULL);
if (hcursor != NULL)
{
rc = SetSystemCursor(hcursor, OCR_NORMAL);
}
else
{
TABSRVERR(("Failed to restore system cursor.\n"));
}
//#endif
}
#endif
TRACEEXIT(("!\n"));
return;
} //SetPressHoldCursor
/*++
@doc INTERNAL
@func LPTSTR | MakeFileName | Make a valid path by doing environment
substitution.
@parm IN OUT LPTSTR | szFile | Points to the file name string.
@rvalue returns szFile.
--*/
LPTSTR
MakeFileName(
IN OUT LPTSTR pszFile
)
{
TRACEPROC("MakeFileName", 3)
TCHAR szTmp[MAX_PATH];
TRACEENTER(("(File=%s)\n", pszFile));
ExpandEnvironmentStrings(pszFile, szTmp, ARRAYSIZE(szTmp));
if ((szTmp[0] == TEXT('\\')) || (szTmp[1] == TEXT(':')))
{
lstrcpy(pszFile, szTmp);
}
else
{
GetSystemDirectory(pszFile, MAX_PATH);
lstrcat(pszFile, TEXT("\\"));
lstrcat(pszFile, szTmp);
}
TRACEEXIT(("! (File=%s)\n", pszFile));
return pszFile;
} //MakeFileName
/*++
@doc INTERNAL
@func BOOL | CanDoPressHold | Check if we can do press and hold.
@parm IN LONG | x | Cursor X.
@parm IN LONG | y | Cursor Y.
@rvalue SUCCESS | Returns TRUE.
@rvalue FAILURE | Returns FALSE.
--*/
BOOL
CanDoPressHold(
IN LONG x,
IN LONG y
)
{
TRACEPROC("CanDoPressHold", 3)
BOOL rc = TRUE;
TRACEENTER(("(x=%d,y=%d)\n", x, y));
if (gpISuperTip != NULL)
{
__try
{
TIP_HIT_TEST_TYPE HitType;
POINT pt;
pt.x = NORMAL_TO_SCREEN_X(x);
pt.y = NORMAL_TO_SCREEN_Y(y);
if (SUCCEEDED(gpISuperTip->HitTest(pt, &HitType)) &&
(HitType != TIP_HT_NONE) && (HitType != TIP_HT_STAGE))
{
TRACEINFO(3, ("Cursor is on HitType=%x\n", HitType));
rc = FALSE;
if ((HitType == TIP_HT_INK_AREA) && (ghwndSuperTIPInk != NULL))
{
gdwfTabSrv |= TSF_SUPERTIP_SENDINK;
}
}
}
__except(EXCEPTION_EXECUTE_HANDLER)
{
TABSRVERR(("Exception in SuperTIP HitTest (%x).\n",
_exception_code()));
}
}
TRACEEXIT(("=%x\n", rc));
return rc;
} //CanDoPressHold