mirror of https://github.com/tongzx/nt5src
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
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
|