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.
 
 
 
 
 
 

619 lines
20 KiB

/*++
Copyright (c) 2000 Microsoft Corporation. All rights reserved.
Module Name:
buttons.cpp
Abstract:
This module contains all the tablet pc button functions.
Author:
Michael Tsang (MikeTs) 29-Sep-2000
Environment:
User mode
Revision History:
--*/
#include "pch.h"
#define HOTKEY_TIMEOUT 200 //200msec
#define REPEAT_DELAY 500 //500msec
#define PAGEUPDOWN_TIMEOUT 200 //200msec
#define ALTTAB_TIMEOUT 500 //500msec
typedef struct _HOTKEYTIMER
{
UINT_PTR TimerID;
BUTTON_ACTION Action;
ULONG dwButtonTag;
ULONG dwButtonState;
} HOTKEYTIMER, *PHOTKEYTIMER;
typedef struct _REPEATKEYTIMER
{
UINT_PTR TimerID;
UINT uiRepeatInterval;
INPUT KbdInput;
} REPEATKEYTIMER, *PREPEATKEYTIMER;
HOTKEYTIMER gHotKeyTimer = {0};
REPEATKEYTIMER gPageUpDownTimer = {0};
REPEATKEYTIMER gAltTabTimer = {0};
/*++
@doc INTERNAL
@func VOID | ProcessButtonsReport | Process a button input report.
@parm IN PCHAR | pBuff | Buffer containing report data.
@rvalue None.
--*/
VOID
ProcessButtonsReport(
IN PCHAR pBuff
)
{
TRACEPROC("ProcessButtonsReport", 3)
static ULONG dwLastButtons = 0;
static ULONG dwPendingUpButtons = 0;
NTSTATUS status;
ULONG ulLength;
TRACEENTER(("(pBuff=%p)\n", pBuff));
ulLength = gdevButtons.dwcButtons;
status = HidP_GetButtons(HidP_Input,
HID_USAGE_PAGE_BUTTON,
0,
gdevButtons.pDownButtonUsages,
&ulLength,
gdevButtons.pPreParsedData,
pBuff,
gdevButtons.hidCaps.InputReportByteLength);
if (status == HIDP_STATUS_SUCCESS)
{
ULONG i, dwMask;
ULONG dwCurrentButtons = 0;
ULONG dwChangedButtons, dwButtons;
for (i = 0; i < ulLength; i++)
{
TRACEINFO(3, ("%d: %d\n", i, gdevButtons.pDownButtonUsages[i]));
dwCurrentButtons |= 1 << (gdevButtons.pDownButtonUsages[i] - 1);
}
dwChangedButtons = dwCurrentButtons^dwLastButtons;
TRACEINFO(1, ("LastButtons=%x,CurrentButtons=%x,ChangedButtons=%x,PendingUpButtons=%x\n",
dwLastButtons, dwCurrentButtons, dwChangedButtons,
dwPendingUpButtons));
//
// If there are any released buttons that are in the PendingUpButtons
// list, eat them.
//
dwButtons = dwChangedButtons & dwPendingUpButtons & ~dwCurrentButtons;
dwChangedButtons &= ~dwButtons;
dwPendingUpButtons &= ~dwButtons;
if (dwButtons)
{
TRACEINFO(1, ("Released PendingUpButtons=%x\n", dwButtons));
}
if ((gHotKeyTimer.TimerID != 0) &&
(dwChangedButtons & gConfig.ButtonSettings.dwHotKeyButtons) &&
(dwCurrentButtons == gConfig.ButtonSettings.dwHotKeyButtons))
{
//
// One of the hotkey buttons changes state and both hotkey
// buttons are down, so send the hotkey event.
//
TRACEINFO(1, ("HotKey buttons pressed, kill HotKey timer.\n"));
KillTimer(NULL, gHotKeyTimer.TimerID);
gHotKeyTimer.TimerID = 0;
SetEvent(ghHotkeyEvent);
dwPendingUpButtons |= dwCurrentButtons;
TRACEINFO(1, ("Detected HotKey (PendingUpButtons=%x)\n",
dwPendingUpButtons));
}
else
{
if ((dwCurrentButtons ^ -((long)dwCurrentButtons)) &
dwCurrentButtons)
{
//
// More than 1 buttons pressed, ignored the new buttons
// pressed.
//
TRACEINFO(1, ("More than 1 buttons pressed (NewPressedButtons=%x)\n",
dwChangedButtons & dwCurrentButtons));
dwPendingUpButtons |= dwChangedButtons & dwCurrentButtons;
dwChangedButtons &= ~(dwChangedButtons & dwCurrentButtons);
}
//
// Determine which remaining buttons have changed states and do
// corresponding actions.
//
for (i = 0, dwMask = 1; i < NUM_BUTTONS; i++, dwMask <<= 1)
{
dwButtons = dwChangedButtons & dwMask;
if (dwButtons != 0)
{
if (dwButtons & gConfig.ButtonSettings.dwHotKeyButtons)
{
//
// One of the hotkey buttons has changed state.
//
if (dwButtons & dwCurrentButtons)
{
//
// It's a hotkey button down, we delay the
// action for 200msec. If the second hotkey
// button is pressed within the period, it is
// a hotkey, otherwise the action will be
// performed when the timer expires.
//
TRACEINFO(1, ("HotKey button is pressed (Button=%x)\n",
dwButtons));
gHotKeyTimer.Action =
gConfig.ButtonSettings.ButtonMap[i];
gHotKeyTimer.dwButtonTag = dwButtons;
gHotKeyTimer.dwButtonState = dwCurrentButtons &
dwButtons;
gHotKeyTimer.TimerID = SetTimer(NULL,
0,
HOTKEY_TIMEOUT,
ButtonTimerProc);
TRACEASSERT(gHotKeyTimer.TimerID);
if (gHotKeyTimer.TimerID == 0)
{
TABSRVERR(("Failed to set hotkey timer (err=%d).\n",
GetLastError()));
}
}
else
{
//
// The hotkey button is released.
//
if (gHotKeyTimer.TimerID != 0)
{
//
// It is released before timeout, so kill
// the timer and perform the delayed down
// action.
//
KillTimer(NULL, gHotKeyTimer.TimerID);
gHotKeyTimer.TimerID = 0;
TRACEINFO(1, ("HotKey button released before timeout (Button=%x)\n",
dwButtons));
DoButtonAction(gHotKeyTimer.Action,
gHotKeyTimer.dwButtonTag,
gHotKeyTimer.dwButtonState != 0);
}
//
// Do the HotKey button release.
//
TRACEINFO(1, ("HotKey button released (Button=%x)\n",
dwButtons));
DoButtonAction(gConfig.ButtonSettings.ButtonMap[i],
dwButtons,
FALSE);
}
}
else
{
//
// Not a hotkey button, do the normal press/release
// action.
//
TRACEINFO(1, ("Non HotKey buttons (Button=%x,Current=%x)\n",
dwButtons, dwCurrentButtons));
DoButtonAction(gConfig.ButtonSettings.ButtonMap[i],
dwButtons,
(dwCurrentButtons & dwButtons) != 0);
}
}
}
}
dwLastButtons = dwCurrentButtons;
}
else
{
TABSRVERR(("failed getting data (status=%d)\n", status));
}
TRACEEXIT(("!\n"));
return;
} //ProcessButtonsReport
/*++
@doc INTERNAL
@func VOID | ButtonTimerProc | Button timer proc.
@parm IN HWND | hwnd | Not used.
@parm IN UINT | uMsg | WM_TIMER.
@parm IN UINT_PTR | idEvent | Not used.
@parm IN DWORD | dwTime | Not used.
@rvalue None.
--*/
VOID
CALLBACK
ButtonTimerProc(
IN HWND hwnd,
IN UINT uMsg,
IN UINT_PTR idEvent,
IN DWORD dwTime
)
{
TRACEPROC("ButtonTimerProc", 3)
TRACEENTER(("hwnd=%x,Msg=%s,idEvent=%x,Time=%x)\n",
hwnd, LookupName(uMsg, WMMsgNames), idEvent, dwTime));
TRACEASSERT(idEvent != 0);
KillTimer(NULL, idEvent);
if (idEvent == gHotKeyTimer.TimerID)
{
//
// HotKey timer times out. So we will do the pending action.
//
gHotKeyTimer.TimerID = 0;
TRACEINFO(3, ("HotKey timer expired, do pending action (Action=%d,Button=%x,fDown=%x).\n",
gHotKeyTimer.Action, gHotKeyTimer.dwButtonTag,
gHotKeyTimer.dwButtonState != 0));
DoButtonAction(gHotKeyTimer.Action,
gHotKeyTimer.dwButtonTag,
gHotKeyTimer.dwButtonState != 0);
}
else if (idEvent == gPageUpDownTimer.TimerID)
{
TRACEINFO(3, ("Page Up/Down timer expired, send another Page Up/Down.\n"));
SendInput(1, &gPageUpDownTimer.KbdInput, sizeof(INPUT));
gPageUpDownTimer.TimerID = SetTimer(NULL,
0,
gPageUpDownTimer.uiRepeatInterval,
ButtonTimerProc);
}
else if (idEvent == gAltTabTimer.TimerID)
{
TRACEINFO(3, ("Alt-tab timer expired, send another tab.\n"));
SendInput(1, &gAltTabTimer.KbdInput, sizeof(INPUT));
gAltTabTimer.TimerID = SetTimer(NULL,
0,
gAltTabTimer.uiRepeatInterval,
ButtonTimerProc);
}
else
{
TABSRVERR(("Unknown timer (TimerID=%x).\n", idEvent));
}
TRACEEXIT(("!\n"));
return;
} //ButtonTimerProc
/*++
@doc INTERNAL
@func BOOL | DoButtonAction | Do the button action.
@parm IN BUTTON_ACTION | Action | Button action to be performed.
@parm IN DWORD | dwButtonTag | Specifies which button.
@parm IN BOOL | fButtonDown | TRUE if the button is in down state.
@rvalue SUCCESS | Return TRUE.
@rvalue FAILURE | Return FALSE.
--*/
BOOL
DoButtonAction(
IN BUTTON_ACTION Action,
IN DWORD dwButtonTag,
IN BOOL fButtonDown
)
{
TRACEPROC("DoButtonAction", 2)
BOOL rc = TRUE;
INPUT KbdInput[2];
WORD wVk;
TRACEENTER(("(Action=%d,ButtonTag=%x,fDown=%x)\n",
Action, dwButtonTag, fButtonDown));
memset(KbdInput, 0, sizeof(KbdInput));
KbdInput[0].type = INPUT_KEYBOARD;
KbdInput[1].type = INPUT_KEYBOARD;
switch (Action)
{
case InvokeNoteBook:
if (!fButtonDown)
{
TRACEINFO(1, ("Invoke NoteBook on button down (Button=%x)\n",
dwButtonTag));
rc = DoInvokeNoteBook();
}
break;
case PageUp:
wVk = VK_PRIOR;
TRACEINFO(1, ("PageUp (Button=%x,fDown=%x)\n",
dwButtonTag, fButtonDown));
goto PageUpDownCommon;
case PageDown:
wVk = VK_NEXT;
TRACEINFO(1, ("PageDown (Button=%x,fDown=%x)\n",
dwButtonTag, fButtonDown));
PageUpDownCommon:
if (fButtonDown)
{
if (gPageUpDownTimer.TimerID != 0)
{
//
// There is an existing Page Up/Down timer, cancel it.
//
KillTimer(NULL, gPageUpDownTimer.TimerID);
gPageUpDownTimer.TimerID = 0;
}
//
// Send PageUpDown-down.
//
memset(&gPageUpDownTimer, 0, sizeof(gPageUpDownTimer));
gPageUpDownTimer.uiRepeatInterval = PAGEUPDOWN_TIMEOUT;
gPageUpDownTimer.KbdInput.type = INPUT_KEYBOARD;
gPageUpDownTimer.KbdInput.ki.wVk = wVk;
SendInput(1, &gPageUpDownTimer.KbdInput, sizeof(INPUT));
gPageUpDownTimer.TimerID = SetTimer(NULL,
0,
REPEAT_DELAY,
ButtonTimerProc);
TRACEASSERT(gPageUpDownTimer.TimerID);
if (gPageUpDownTimer.TimerID == 0)
{
TABSRVERR(("Failed to set Page Up/Down timer (err=%d).\n",
GetLastError()));
rc = FALSE;
}
}
else
{
TRACEASSERT(gPageUpDownTimer.TimerID != 0);
KillTimer(NULL, gPageUpDownTimer.TimerID);
gPageUpDownTimer.TimerID = 0;
//
// Send PageUpDown-up.
//
KbdInput[0].ki.wVk = wVk;
KbdInput[0].ki.dwFlags = KEYEVENTF_KEYUP;
SendInput(1, KbdInput, sizeof(INPUT));
}
break;
case AltTab:
if (fButtonDown)
{
TRACEINFO(1, ("AltTab down (Button=%x)\n", dwButtonTag));
TRACEASSERT(gAltTabTimer.TimerID == 0);
//
// Send Alt-down, Tab-down.
//
KbdInput[0].ki.wVk = VK_MENU;
KbdInput[1].ki.wVk = VK_TAB;
SendInput(2, KbdInput, sizeof(INPUT));
memset(&gAltTabTimer, 0, sizeof(gAltTabTimer));
gAltTabTimer.uiRepeatInterval = ALTTAB_TIMEOUT;
gAltTabTimer.KbdInput.type = INPUT_KEYBOARD;
gAltTabTimer.KbdInput.ki.wVk = VK_TAB;
gAltTabTimer.TimerID = SetTimer(NULL,
0,
REPEAT_DELAY,
ButtonTimerProc);
TRACEASSERT(gAltTabTimer.TimerID);
if (gAltTabTimer.TimerID == 0)
{
TABSRVERR(("Failed to set Alt-Tab timer (err=%d).\n",
GetLastError()));
rc = FALSE;
}
}
else
{
TRACEINFO(1, ("AltTab up (Button=%x)\n", dwButtonTag));
TRACEASSERT(gAltTabTimer.TimerID != 0);
KillTimer(NULL, gAltTabTimer.TimerID);
gAltTabTimer.TimerID = 0;
//
// Send Tab-up, Alt-up.
//
KbdInput[0].ki.wVk = VK_TAB;
KbdInput[0].ki.dwFlags = KEYEVENTF_KEYUP;
KbdInput[1].ki.wVk = VK_MENU;
KbdInput[1].ki.dwFlags = KEYEVENTF_KEYUP;
SendInput(2, KbdInput, sizeof(INPUT));
}
break;
case AltEsc:
if (fButtonDown)
{
//
// Send Alt-down, Esc-down.
//
TRACEINFO(1, ("AltEsc down (Button=%x)\n", dwButtonTag));
KbdInput[0].ki.wVk = VK_MENU;
KbdInput[1].ki.wVk = VK_ESCAPE;
SendInput(2, KbdInput, sizeof(INPUT));
}
else
{
//
// Send Esc-up, Alt-up.
//
TRACEINFO(1, ("AltEsc up (Button=%x)\n", dwButtonTag));
KbdInput[0].ki.wVk = VK_ESCAPE;
KbdInput[0].ki.dwFlags = KEYEVENTF_KEYUP;
KbdInput[1].ki.wVk = VK_MENU;
KbdInput[1].ki.dwFlags = KEYEVENTF_KEYUP;
SendInput(2, KbdInput, sizeof(INPUT));
}
break;
case Enter:
if (fButtonDown)
{
//
// Send Enter-down.
//
TRACEINFO(1, ("Return down (Button=%x)\n", dwButtonTag));
KbdInput[0].ki.wVk = VK_RETURN;
SendInput(1, KbdInput, sizeof(INPUT));
}
else
{
//
// Send Enter-up.
//
TRACEINFO(1, ("Return up (Button=%x)\n", dwButtonTag));
KbdInput[0].ki.wVk = VK_RETURN;
KbdInput[0].ki.dwFlags = KEYEVENTF_KEYUP;
SendInput(1, KbdInput, sizeof(INPUT));
}
break;
case Esc:
if (fButtonDown)
{
//
// Send Esc-down.
//
TRACEINFO(1, ("Escape down (Button=%x)\n", dwButtonTag));
KbdInput[0].ki.wVk = VK_ESCAPE;
SendInput(1, KbdInput, sizeof(INPUT));
}
else
{
//
// Send Esc-up.
//
TRACEINFO(1, ("Escape up (Button=%x)\n", dwButtonTag));
KbdInput[0].ki.wVk = VK_ESCAPE;
KbdInput[0].ki.dwFlags = KEYEVENTF_KEYUP;
SendInput(1, KbdInput, sizeof(INPUT));
}
break;
}
TRACEEXIT(("=%x\n", rc));
return rc;
} //DoButtonAction
/*++
@doc INTERNAL
@func BOOL | DoInvokeNoteBook | Invoke the Notebook app.
@parm None.
@rvalue SUCCESS | Returns TRUE.
@rvalue FAILURE | Returns FALSE.
--*/
BOOL
DoInvokeNoteBook(
VOID
)
{
TRACEPROC("DoInvokeNoteBook", 3)
BOOL rc = FALSE;
PTSTHREAD pThread = FindThread(TSF_BUTTONTHREAD);
TRACEENTER(("()\n"));
if (!(pThread->dwfThread & THREADF_DESKTOP_WINLOGON))
{
TCHAR szNoteBook[MAX_PATH];
DWORD dwcb = sizeof(szNoteBook);
HRESULT hr;
hr = GetRegValueString(HKEY_LOCAL_MACHINE,
TEXT("SOFTWARE\\Microsoft\\MSNotebook"),
TEXT("InstallDir"),
szNoteBook,
&dwcb);
if (SUCCEEDED(hr))
{
lstrcat(szNoteBook, "NoteBook.exe");
rc = RunProcessAsUser(szNoteBook);
}
else
{
TABSRVERR(("Failed to read MSNoteBook install path from the registry (hr=%d).\n",
hr));
}
}
else
{
TRACEWARN(("Cannot run NoteBook app. in WinLogon desktop.\n"));
}
TRACEEXIT(("=%x\n", rc));
return rc;
} //DoInvokeNoteBook
/*++
@doc INTERNAL
@func VOID | UpdateButtonRepeatRate | Update button repeat rate.
@parm None.
@rvalue None.
--*/
VOID
UpdateButtonRepeatRate(
VOID
)
{
TRACEPROC("UpdateButtonRepeatRate", 3)
static DWORD KbdDelayTime[] = {1000, 750, 500, 250};
int iKbdDelay;
int iKbdSpeed;
TRACEENTER(("()\n"));
#if 0
if (!SystemParametersInfo(SPI_GETKEYBOARDDELAY, 0, &iKbdDelay, 0))
{
TRACEWARN(("Get keyboard delay failed (err=%d).\n", GetLastError()));
}
else if (!SystemParametersInfo(SPI_GETKEYBOARDSPEED, 0, &iKbdSpeed, 0))
{
TRACEWARN(("Get keyboard speed failed (err=%d).\n", GetLastError()));
}
else
{
TRACEASSERT((iKbdDelay >= 0) && (iKbdDelay <= 3));
gdwKbdDelayTime = KbdDelayTime[iKbdDelay];
gdwKbdRepeatTime =
}
#endif
TRACEEXIT(("!\n"));
return;
} //UpdateButtonRepeatRate