|
|
/*++
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
|