//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
//
// Use of this source code is subject to the terms of the Microsoft shared
// source or premium shared source license agreement under which you licensed
// this source code. If you did not accept the terms of the license agreement,
// you are not authorized to use this source code. For the terms of the license,
// please see the license agreement between you and Microsoft or, if applicable,
// see the SOURCE.RTF on your install media or the root of your tools installation.
// THE SOURCE CODE IS PROVIDED "AS IS", WITH NO WARRANTIES OR INDEMNITIES.
//
/*++
THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
PARTICULAR PURPOSE.

Copyright  1998-2000  Microsoft Corporation.  All Rights Reserved.

Module Name:

     global.cpp  

Abstract:

   Code used by the entire GDI test suite
***************************************************************************/
#include "global.h"
#include <keybd.h>

#define TIMERID 777

HINSTANCE globalInst;
HWND      globalHwnd;
HDC       globalHDC;
HFONT     hfont;        // For fixed pitch draw text
LPCTSTR   localAtom;
int       goKey = 32;   // Space
float     adjustRatio;

static TCHAR     szThisFile[] = TEXT("global.cpp");

/***********************************************************************************
***
***   Size Calc
***
************************************************************************************/
void InitAdjust(void) {   
    RECT rectWorking;
    SystemParametersInfo( SPI_GETWORKAREA, 0, &rectWorking, FALSE );
    adjustRatio = (((float)rectWorking.bottom/(float)rectWorking.right) +
        ((float)rectWorking.right/(float)640))/2;
}



/***********************************************************************************
***
***   Window Support
***
************************************************************************************/
WNDPROC func[LASTPROC] = {
   GenericWndProc,
   InstructWindProc,
   KeyPressWindProc,
   KeySequenceWindProc,
   KeyChordingWinProc,
   EditTextWndProc,
   RepeatRateKeyDelayWndProc,
   AsyncWndProc,
};


//***********************************************************************************
LRESULT CALLBACK GenericWndProc(HWND hWnd, UINT message,WPARAM wParam, LPARAM lParam) {
   static int currentProc; 

   if (message == WM_SWITCHPROC) {
      currentProc = (int)wParam;
      return 0;
   }

   switch (message) {
   case WM_SETFOCUS:
      doUpdate(NULL);
      return 0;
   }

   if (currentProc != GENERIC) {
      if (currentProc > -1 && currentProc < LASTPROC)
         return (LRESULT)func[currentProc](hWnd,message,wParam,lParam);
   } 
   return DefWindowProc(hWnd, message, wParam, lParam);
}


//***********************************************************************************
LPCTSTR MakeMeAWndClass(void) {
   WNDCLASS wc;
   memset(&wc, 0, sizeof(wc));
   wc.lpfnWndProc   = (WNDPROC)GenericWndProc;   // Window Procedure                       
   wc.hInstance     = globalInst;                // Owner of this class
   wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); 
   wc.lpszClassName = WNDNAME;
   wc.style         = CS_DBLCLKS;
   return(LPCTSTR)RegisterClass(&wc);
}

HFONT GetNormalFont(INT size)
{
    LOGFONT logfont;
    HDC     hdc;

   ASSERT(size>1);

    memset(&logfont, 0, sizeof (logfont));

    hdc = GetDC(NULL);
    logfont.lfHeight = -( size* GetDeviceCaps(hdc, LOGPIXELSY) / 72);
    ReleaseDC(NULL, hdc);

    logfont.lfWeight         = FW_NORMAL;
    logfont.lfCharSet        = DEFAULT_CHARSET;
    logfont.lfOutPrecision   = OUT_DEFAULT_PRECIS;
    logfont.lfClipPrecision  = CLIP_DEFAULT_PRECIS;
    logfont.lfQuality        = DEFAULT_QUALITY;
    logfont.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;

   
    return CreateFontIndirect(&logfont);
}

//***********************************************************************************
void initAll(void) {
    //Get the Unfiltered WM_*KEY* messages from Keyboard.
    //WM_*KEY* won't get routed through Shell
    AllKeys(TRUE);
    ImmDisableIME(0);

    //Programmatically set the Keyboard Mode to SHIME_MODE_SPELL.
    //Because the current PS2 WM Test Keyboard driver ends up in SHIME_MODE_NUMBERS
    //when running this test.
    HANDLE hKeyboard = {NULL};
    DWORD dwKeyboardMode = KBDI_SHIME_MODE_SPELL;
    BOOL fReturn = FALSE;

    hKeyboard = CreateFile(
        _T ("KBD1:"),
        GENERIC_READ | GENERIC_WRITE,
        FILE_SHARE_READ,
        NULL,
        OPEN_EXISTING,
        FILE_ATTRIBUTE_NORMAL,
        NULL);

    if ((fReturn = DeviceIoControl(
                       hKeyboard,
                       IOCTL_KBD_SET_SHIME_MODE_ID,
                       (LPVOID)&dwKeyboardMode,
                       sizeof(DWORD),
                       NULL,
                       0,
                       NULL,
                       NULL)) != TRUE)
    {
        g_pKato->Log(
            LOG_FAIL,
            TEXT("FAIL: Failed to set Keyboard driver mode to KBDI_SHIME_MODE_SPELL\n"));
    }

    if (fReturn == TRUE)
    {
        dwKeyboardMode = KBDI_SHIME_MODE_NONE;
        if ((fReturn = DeviceIoControl(
                           hKeyboard,
                           IOCTL_KBD_GET_SHIME_MODE_ID,
                           NULL,
                           0,
                           (LPVOID)&dwKeyboardMode,
                           sizeof(DWORD),
                           NULL,
                           NULL)) != TRUE)
        {
            g_pKato->Log(
                LOG_FAIL,
                TEXT("FAIL: Failed to get the Keyboard driver mode\n"));
        }
    }

    if (fReturn == TRUE && dwKeyboardMode == 1)
    {
        g_pKato->Log(
            LOG_DETAIL,
            TEXT("Keyboard Driver has been set to KBDI_SHIME_MODE_SPELL\n"));
    }
    else
    {
        g_pKato->Log(
            LOG_FAIL,
            TEXT("FAIL: Keyboard Driver may NOT be set to KBDI_SHIME_MODE_SPELL\n"));
    }

    CloseHandle(hKeyboard);

    RECT rectWorking;
    SystemParametersInfo( SPI_GETWORKAREA, 0, &rectWorking, FALSE );
    
   InitAdjust();
    localAtom = MakeMeAWndClass();
   if (!localAtom)
   {
    g_pKato->Log( LOG_FAIL,TEXT("FAIL in %s @ line %d: MakeMeAClass failed: %d"), 
                  szThisFile, __LINE__, GetLastError());
    return;
   }
   
   globalHwnd = CreateWindowEx(0, localAtom, WNDNAME, WS_POPUP | WS_VISIBLE|WS_HSCROLL,
      0,0,rectWorking.right,rectWorking.bottom,NULL,NULL,globalInst,NULL);
   if (!globalHwnd)
       g_pKato->Log( LOG_FAIL, 
                     TEXT("FAIL in %s @ line %d: CreateWindow failed"),
                     szThisFile, __LINE__ );

   ShowWindow(globalHwnd, SW_SHOW);
   SetFocus(globalHwnd);
   SetForegroundWindow(globalHwnd);

   // create a fixed pitch font, used by scrollBody and drawKeyEventHeader
   
   hfont = GetNormalFont(9);
   Sleep(0);
}


//***********************************************************************************
// free up all the UI resources
void closeAll(void) {
   static BOOL bClosed = FALSE;  // only need to do this once
   if (!bClosed) {
        ReleaseDC(globalHwnd,globalHDC);
        DestroyWindow(globalHwnd);
        UnregisterClass(localAtom,globalInst);
        globalHwnd  = NULL;
        localAtom   = NULL;
        bClosed = TRUE;
   }
}


//***********************************************************************************
void clean(void) {
   MSG   msg;
   while (PeekMessage(&msg, globalHwnd, WM_KEYFIRST, WM_KEYLAST, PM_REMOVE))
   {
      ;  // clean out queue
   }
}


//***********************************************************************************
// pump: message loop, until WM_GOAL message returns true or user input times out
int pump(void) {
   return pump(globalHwnd);
}

// same functionality as pump(void), which makes sure a specific window get the focus
//  if after a timeout message box.  Created for edit text.
int pump(HWND hwndTestWindow) {
    MSG   msg;
    do {
      SetTimer(globalHwnd,TIMERID,20000,NULL);
      GetMessage(&msg,NULL,0,0);
      if (msg.message == WM_TIMER && (UINT)msg.wParam == TIMERID)
      {
         if (!askMessage(hwndTestWindow, TEXT("Time out. Do you want to try again?"),1))
            return 2;
       }
      KillTimer(globalHwnd,777);
      TranslateMessage(&msg);
      DispatchMessage(&msg);
      if (SendMessage(globalHwnd,WM_GOAL,0,0)) 
         return 1;
   } while (msg.message != WM_QUIT);
   return 0;
}
/***********************************************************************************
***
***   UI
***
************************************************************************************/

//***********************************************************************************
BOOL askMessage(const TCHAR *str, int pass) {
   return askMessage(globalHwnd, str, pass);
}

BOOL askMessage(HWND hwndTestWindow, const TCHAR *str, int pass) {
   clean();
   BOOL result = (IDYES == MessageBox(hwndTestWindow, str, TEXT("Input Required"),MB_YESNO|MB_DEFBUTTON1));
   g_pKato->Log( (pass || result) ? LOG_DETAIL : LOG_FAIL,
                 TEXT("%s %s @ line %d: User replied <%s> to <%s>"), 
                 (pass || result) ? TEXT("In") : TEXT("FAIL in"), szThisFile, __LINE__,
                 result ? TEXT("YES") : TEXT("No"), str );
   SetFocus(hwndTestWindow);
   return result;
}


//***********************************************************************************
void initObjects(HDC hdc) {


   SelectObject(hdc, GetNormalFont(9));
   SelectObject(hdc,(HBRUSH)GetStockObject(WHITE_BRUSH));
   SelectObject(hdc,(HPEN)GetStockObject(BLACK_PEN));
}
 

//***********************************************************************************
void initWindow(HDC hdc, const TCHAR *top, const TCHAR *middle) {

   RECT rectWorking;

   SystemParametersInfo( SPI_GETWORKAREA, 0, &rectWorking, FALSE );

   // right and bottom edges are the width and height of our screen area
   DWORD x = rectWorking.right;
   DWORD y = rectWorking.bottom;

   RECT  r[4]     = {{0,0,x,y}, {0,0,x,40}, {0,40,x,90}, {0,90,x,y}},
         textr[2] = {{0+7,0+7,x-7,40-7}, {0+7,40+7,x-7,90-7}};
   HRGN  hRgn     = CreateRectRgnIndirect(&r[0]);;

   initObjects(hdc);
   
   SelectClipRgn(hdc,hRgn);
   for(int i = 1; i < 4; i++)
      ExcludeClipRect(hdc,r[i].left+5,r[i].top+5,r[i].right-5,r[i].bottom-5);
   FillRect(hdc,&r[0],(HBRUSH)GetStockObject(LTGRAY_BRUSH));
   SelectClipRgn(hdc,NULL);
   
   for(i = 1; i < 4; i++)
      Rectangle(hdc,r[i].left+5,r[i].top+5,r[i].right-5,r[i].bottom-5);
   DrawText(hdc, top,_tcslen(top), &textr[0],DT_CENTER|DT_WORDBREAK);
   DrawText(hdc, middle,_tcslen(middle),&textr[1],DT_CENTER|DT_WORDBREAK);
   DeleteObject(hRgn);
}


//***********************************************************************************
void bodyText(HDC hdc, int x0, int x1, int line, const TCHAR *str) {
   RECT rectWorking;
   SystemParametersInfo( SPI_GETWORKAREA, 0, &rectWorking, FALSE );
   
   RECT  textr[1] = {x0+7,90+7+line*20,x1-7,rectWorking.bottom-7};

   initObjects(hdc);
   DrawText(hdc, str,_tcslen(str), &textr[0],DT_LEFT|DT_WORDBREAK);
}
//************************************************************************************  

//Scroll the body and display new text, print a keyboard message header
// used by keyboard event viewers
void scrollBody(HDC hdc, const TCHAR *szText) {
	HFONT hfontPrev,hfontCur;

    RECT rectWorking;

    SystemParametersInfo( SPI_GETWORKAREA, 0, &rectWorking, FALSE );

    // right and bottom edges are the width and height of our screen area
    DWORD x = rectWorking.right;
    DWORD y = rectWorking.bottom;
    DWORD fheight=0;
    TEXTMETRIC tm;

    hfontCur=GetNormalFont(7 );
    hfontPrev = (HFONT) SelectObject(hdc, hfontCur);
    GetTextMetrics(hdc,&tm);
    fheight=tm.tmHeight;
    
    
    RECT rectScroll = {7, 97 + 25, x - 7, y - 7};
    RECT rectDraw = {7, y - 7 - 25 , x - 7, y - 7};

    ScrollDC(hdc, 0, -20, &rectScroll, &rectScroll, NULL, NULL);
       DrawText(hdc, szText, _tcslen(szText), &rectDraw, DT_LEFT);
    SelectObject(hdc, hfontPrev);  // put back old font
    ValidateRect(globalHwnd, NULL);

}
//***********************************************************************************
void drawKeyEventHeader(HDC hdc) {
    HFONT hfontPrev,hfontCur;

    RECT rectWorking;
    SystemParametersInfo( SPI_GETWORKAREA, 0, &rectWorking, FALSE );

    RECT rectHeader = {7, 97, rectWorking.right - 7, 97 + 20};
    
    TCHAR szHeader[] =    TEXT("Message        Key Char Repeat Scan Ext ALT Prev Tran");

        hfontCur=GetNormalFont(9);
        
    hfontPrev = (HFONT) SelectObject(hdc, hfontCur);
    
    DrawText(hdc, szHeader, _countof(szHeader) - 1, &rectHeader, DT_LEFT);

    SelectObject(hdc, hfontPrev);  // put back old font
}
//***********************************************************************************
void doUpdate(CONST RECT *aRect) {
   InvalidateRect(globalHwnd,aRect,0);
   UpdateWindow(globalHwnd);
}


/***********************************************************************************
***
***   Instructions
***
************************************************************************************/
TCHAR *str[] = {
   TEXT("Top box: describes the current test."),
   TEXT("Second box: instructions for the current test."),
   TEXT("This box: shows the output (if any) from the current test."),
   TEXT(""),
   TEXT("Note: WHEN THE TEST PROMPTS FOR USER INPUT,"),
   TEXT("PLEASE USE MOUSE OR TOUCH."),
   TEXT("F1 (SoftKey1) and F2 (SoftKey2) on the keyboard won't work,"),
   TEXT("because all the keyboard inputs will be directly posted to the"),
   TEXT("test application; instead of being routed through Shell."),
   TEXT(""),
   TEXT("Test Hung? - wait 20 seconds - a dialog will let you move on."),
    TEXT(""),
   TEXT("Output goes to DEBUG out (view using: terminal, windbg, etc.)."),
};


//***********************************************************************************
LRESULT CALLBACK InstructWindProc(HWND hwnd, UINT message,WPARAM wParam, LPARAM lParam) {
   static int  keyPress, goal;
   int         strSize, i;
   PAINTSTRUCT ps;

   RECT rectWorking;

   switch(message) {
   case WM_PAINT:
      SystemParametersInfo( SPI_GETWORKAREA, 0, &rectWorking, FALSE );
      BeginPaint(hwnd, &ps);
      strSize = sizeof(str)/sizeof(TCHAR *);
      initWindow(ps.hdc,TEXT("Keyboard Tests: Instructions"),TEXT("Press <SPACE> to continue."));
      g_pKato->Log(LOG_DETAIL,TEXT("From the WinCE device."));
      for(i = 0; i < strSize; i++) {
         g_pKato->Log(LOG_DETAIL,str[i]);
         bodyText(ps.hdc,0, rectWorking.right,i,str[i]);
      }
      EndPaint(hwnd, &ps);
      return 0;
   case WM_INIT:
      keyPress = 0;
      goal = (int)wParam;
      return 0;
   case WM_KEYDOWN:
      keyPress = (int)wParam;
      return 0;
   case WM_GOAL:
      return keyPress == goal;     
   }
   return DefWindowProc(hwnd, message, wParam, lParam);
}


//***********************************************************************************
TESTPROCAPI Instructions_T(UINT uMsg, TPPARAM /*tpParam*/, LPFUNCTION_TABLE_ENTRY /*lpFTE*/) {
   NO_MESSAGES;

   SendMessage(globalHwnd,WM_SWITCHPROC,INSTRUCT,0);
   SendMessage(globalHwnd,WM_INIT,goKey,0);
   doUpdate(NULL);
   pump();
   return getCode();
}
