/*************************************************************************
	Project:    Active Accessibility
    Module:     INPUT.C

    Author:     LauraBu
    Date:       6/96
    
    Notes:      Tests new SendInput and BlockInput APIs

    Copyright (C) 1996 by Microsoft Corporation.  All rights reserved.
    See bottom of file for disclaimer
    
    History:    

*************************************************************************/
#define STRICT
#include <windows.h>
#include <windowsx.h>
#include <winable.h>
#include "resource.h"
#include "input.h"

//
// Constants
//
#define CMS_TIME_TO_UNBLOCK     5000
#define IDTIMER_BLOCKING        100
#define HARDWARE_PARAMH         0x1234
#define HARDWARE_PARAML         0x9876

// Sequences
#define IDK_CTRLESC             1
#define IDK_ALTTAB              2
#define IDK_SHIFTALTTABTAB      3
#define IDK_RIGHTCLICK          4
#define IDK_LEFTCLICKDRAG       5
#define IDK_HARDWAREPEN         6


typedef struct tagMOUSEINFO
{
    int MouseThresh1;
    int MouseThresh2;
    int MouseSpeed;
}
MOUSEINFO, FAR* LPMOUSEINFO;

//
// Variables
//
BOOL    fBlocked = FALSE;
UINT    idButtonCur = 0;
ATOM    atomMyMessage = 0;

static char szUnBlocked[] = "Input is not blocked.";
static char szBlocked[] = "Input is blocked.";
static char szHardwareWorked[] = "Hardware event worked.";

//
// Functions
//
BOOL CALLBACK   MainDlgProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
void            PlayBack(HWND hwnd);


/*************************************************************************
    Function:   WinMain
    Purpose:    Start of program
    Inputs:     
    Returns:    
    History:    
*************************************************************************/
int PASCAL WinMain(HINSTANCE hinst, HINSTANCE hinstPrev, LPSTR szCmdLine, int nCmdShow)
{
    atomMyMessage = RegisterWindowMessage("InputTestHardwareMsg");

    // Kick off the dialog and exit
    return(DialogBoxParam(hinst, MAKEINTRESOURCE(IDD_MAIN), NULL,
        MainDlgProc, 0L));
}


/*************************************************************************
    Function:   MainDlgProc
    Purpose:    Controls user interaction with the dialog
    Inputs:     hwnd - Handle to the dialog window
                uMsg - Message being sent
                wParam - 16 bit message parameter
                lParam - 32 bit message parameter
    Returns:    BOOL - TRUE if message handled
    History:    
*************************************************************************/
BOOL CALLBACK
MainDlgProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    switch (uMsg)
    {
        // First time through, initialize our stuff
        case WM_INITDIALOG:
            SetDlgItemText(hwnd, IDSTATUS, szUnBlocked);
            CheckRadioButton(hwnd, IDKEYBOARD, IDJUSTBLOCK, IDKEYBOARD);
            idButtonCur = IDKEYBOARD;
            break;

        case WM_COMMAND:
            switch (GET_WM_COMMAND_ID(wParam, lParam))
            {
                case IDCANCEL:
                    if (fBlocked)
                    {
                        fBlocked = FALSE;
                        if (!BlockInput(FALSE))
                            MessageBeep(0);
                    }

                    EndDialog(hwnd, TRUE);
                    break;

                case IDOK:
                    if (!BlockInput(TRUE))
                        MessageBeep(0);
                    else
                    {
                        fBlocked = TRUE;
                        SetDlgItemText(hwnd, IDSTATUS, szBlocked);
                        SetTimer(hwnd, IDTIMER_BLOCKING, CMS_TIME_TO_UNBLOCK, NULL);
                    }
                    break;

                case IDKEYBOARD:
                case IDMOUSE:
                case IDTERMINATE:
                case IDHARDWARE:
                case IDJUSTBLOCK:
                    if (GET_WM_COMMAND_CMD(wParam, lParam) == BN_CLICKED)
                        idButtonCur = GET_WM_COMMAND_ID(wParam, lParam);
                    // FALL THROUGH

                default:
                    return(FALSE);
            }
            break;

        case WM_CLOSE:
            PostMessage(hwnd, WM_COMMAND, MAKELONG(IDCANCEL, BN_CLICKED), 0);
            break;

        case WM_TIMER:
            if (wParam != IDTIMER_BLOCKING)
                return(FALSE);

            KillTimer(hwnd, IDTIMER_BLOCKING);
            if (fBlocked)
            {
                PlayBack(hwnd);

                fBlocked = FALSE;
                if (!BlockInput(FALSE))
                    MessageBeep(0);

                SetDlgItemText(hwnd, IDSTATUS, szUnBlocked);
            }
            break;

        default:
            if (uMsg == atomMyMessage)
            {
                if ((LOWORD(lParam) == HARDWARE_PARAML) &&
                    (HIWORD(lParam) == HARDWARE_PARAMH))
                    SetDlgItemText(hwnd, IDSTATUS, szHardwareWorked);
                else
                    MessageBeep(0);
            }
            else
                return(FALSE);
            break;

    }

    return(TRUE);
}



/*************************************************************************
    Function:   PlayBack
    Purpose:    This plays back Ctrl+Esc keys, mouse click on the Start 
                button, or terminates us based on the count.
    Inputs:     HWND - window handle of dialog
    Returns:    void - nada, zip, zilch
    History:    
*************************************************************************/
void PlayBack(HWND hwndMain)
{
    INPUT   rgInput[4];
    RECT    rcStart;
    HWND    hwndStart;
    int     nButtons;
    POINT   ptCursor;
    MOUSEINFO   miSave;
    MOUSEINFO   miNew;

    switch (idButtonCur)
    {
        case IDKEYBOARD:
            //
            // Set up Ctrl+Esc sequence by Ctrl down, Esc down, Ctrl up, Esc up
            //

            // Control down
            rgInput[0].type = INPUT_KEYBOARD;
            rgInput[0].ki.dwFlags = 0;
            rgInput[0].ki.dwExtraInfo = MAKELONG(0x9F, 0x14FE);
            rgInput[0].ki.wVk = VK_CONTROL;
            rgInput[0].ki.wScan = 0x1D;

            // Escape down
            rgInput[1].type = INPUT_KEYBOARD;
            rgInput[1].ki.dwFlags = 0;
            rgInput[1].ki.dwExtraInfo = MAKELONG(0x9F, 0x14FE);
            rgInput[1].ki.wVk = VK_ESCAPE;
            rgInput[1].ki.wScan = 0x01;

            // Control up
            rgInput[2].type = INPUT_KEYBOARD;
            rgInput[2].ki.dwFlags = KEYEVENTF_KEYUP;
            rgInput[2].ki.dwExtraInfo = MAKELONG(0x9F, 0x14FE);
            rgInput[2].ki.wVk = VK_CONTROL;
            rgInput[2].ki.wScan = 0x1D;

            // Escape up
            rgInput[3].type = INPUT_KEYBOARD;
            rgInput[3].ki.dwFlags = KEYEVENTF_KEYUP;
            rgInput[3].ki.dwExtraInfo = MAKELONG(0x97, 0x14FE);
            rgInput[3].ki.wVk = VK_ESCAPE;
            rgInput[3].ki.wScan = 0x01;

            if (!SendInput(4, rgInput,sizeof(INPUT)))
                MessageBeep(0);
            break;

        case IDMOUSE:
            //
            // Set up mouse move + click on start button
            //

            // Get the tray
            hwndStart = FindWindowEx(NULL, NULL, "Shell_TrayWnd", NULL);
            if (!hwndStart)
                break;

            // Get the start button
            hwndStart = FindWindowEx(hwndStart, NULL, "button", NULL);
            if (!hwndStart)
                break;

            //
            // Get screen coords of start button
            //
            GetWindowRect(hwndStart, &rcStart);

            //
            // Get current cursor pos.
            //
            GetCursorPos(&ptCursor);

            //
            // Get delta to move to center of start button from current
            // cursor location.
            //
            ptCursor.x = ((rcStart.left + rcStart.right)/2) - ptCursor.x;
            ptCursor.y = ((rcStart.top + rcStart.bottom)/2) - ptCursor.y;

            //
            // Save mouse acceleration info
            //
            if (!SystemParametersInfo(SPI_GETMOUSE, 0, &miSave, 0))
                break;

            //
            // NOTE:  For relative moves, USER actually multiplies the
            // coords by any acceleration.  But accounting for it is too
            // hard and wrap around stuff is weird.  So, temporarily turn
            // acceleration off; then turn it back on after playback.
            //
            if (miSave.MouseSpeed)
            {
                miNew.MouseThresh1 = 0;
                miNew.MouseThresh2 = 0;
                miNew.MouseSpeed = 0;

                if (!SystemParametersInfo(SPI_SETMOUSE, 0, &miNew, 0))
                    break;
            }


            //
            // Get # of buttons
            // 
            nButtons = GetSystemMetrics(SM_CMOUSEBUTTONS);

            // Absolute mouse move to center of start button
            rgInput[0].type = INPUT_MOUSE;
            rgInput[0].mi.dwFlags = MOUSEEVENTF_MOVE;
            rgInput[0].mi.dwExtraInfo = 0;
            rgInput[0].mi.dx = ptCursor.x;
            rgInput[0].mi.dy = ptCursor.y;
            rgInput[0].mi.mouseData = nButtons;

            // Mouse click down, left button
            rgInput[1].type = INPUT_MOUSE;
            rgInput[1].mi.dwFlags = MOUSEEVENTF_LEFTDOWN;
            rgInput[1].mi.dwExtraInfo = 0;
            rgInput[1].mi.dx = 0;
            rgInput[1].mi.dy = 0;
            rgInput[1].mi.mouseData = nButtons;

            // Mouse click up, left button
            rgInput[2].type = INPUT_MOUSE;
            rgInput[2].mi.dwFlags = MOUSEEVENTF_LEFTUP;
            rgInput[2].mi.dwExtraInfo = 0;
            rgInput[2].mi.dx = 0;
            rgInput[2].mi.dy = 0;
            rgInput[2].mi.mouseData = nButtons;

            if (!SendInput(3, rgInput,sizeof(INPUT)))
                MessageBeep(0);

            //
            // Restore Mouse Acceleration
            //
            if (miSave.MouseSpeed)
                SystemParametersInfo(SPI_SETMOUSE, 0, &miSave, 0);
            break;

        case IDTERMINATE:
            ExitProcess(0);
            break;

        case IDHARDWARE:
            rgInput[0].type = INPUT_HARDWARE;
            rgInput[0].hi.uMsg = atomMyMessage;
            rgInput[0].hi.wParamL = HARDWARE_PARAML;
            rgInput[0].hi.wParamH = HARDWARE_PARAMH;

            if (!SendInput(1, rgInput,sizeof(INPUT)))
                MessageBeep(0);
            break;

        case IDJUSTBLOCK:
            break;
    }
}

/*
    THE INFORMATION AND CODE PROVIDED HEREUNDER (COLLECTIVELY REFERRED TO
    AS "SOFTWARE") IS PROVIDED AS IS WITHOUT WARRANTY OF ANY KIND, EITHER
    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED
    WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN
    NO EVENT SHALL MICROSOFT CORPORATION OR ITS SUPPLIERS BE LIABLE FOR
    ANY DAMAGES WHATSOEVER INCLUDING DIRECT, INDIRECT, INCIDENTAL,
    CONSEQUENTIAL, LOSS OF BUSINESS PROFITS OR SPECIAL DAMAGES, EVEN IF
    MICROSOFT CORPORATION OR ITS SUPPLIERS HAVE BEEN ADVISED OF THE
    POSSIBILITY OF SUCH DAMAGES. SOME STATES DO NOT ALLOW THE EXCLUSION OR
    LIMITATION OF LIABILITY FOR CONSEQUENTIAL OR INCIDENTAL DAMAGES SO THE
    FOREGOING LIMITATION MAY NOT APPLY.
*/
