// ================================================
// Приложение WINHOOK
// Простейший руссификатор клавиатуры
// для Microsoft Windows
// Работает совместно с DLL-библиотекой kbhook.dll
// ================================================

#define STRICT
#include <windows.h>
#include <windowsx.h>
#include <dos.h>
#include <mem.h>

#include "kbhook.hpp"

// ----------------------------------------------------
// Прототипы функций
// ----------------------------------------------------

extern "C"
void WINAPI _export SetKbHook(HWND hwnd);

extern "C"
void WINAPI _export RemoveKbHook(void);

BOOL InitApp(HINSTANCE);
LRESULT CALLBACK _export WndProc(HWND, UINT, WPARAM, LPARAM);

// ----------------------------------------------------
// Глобальные переменные
// ----------------------------------------------------

char const szClassName[]   = "WINHOOKAppClass";
char const szWindowTitle[] = "WINHOOK Application";

TEXTMETRIC tm;
int cxChar, cyChar;
RECT rc;
static BOOL bCyrillic = FALSE;

// =====================================
// Функция WinMain
// =====================================
#pragma argsused

int PASCAL
WinMain(HINSTANCE hInstance,
	HINSTANCE hPrevInstance,
	LPSTR     lpszCmdLine,
	int       nCmdShow)
{
  MSG  msg;   // структура для работы с сообщениями
  HWND hwnd;  // идентификатор главного окна приложения

  // Можно запускать только кодну копию приложения
  if(hPrevInstance)
    return 0;

  // Инициализация копии приложения
  if(!InitApp(hInstance))
      return FALSE;

  // Получаем координаты окна Desktop.
  // Это окно занимает всю поверхность экрана
  // и на нем расположены все остальные окна
  GetWindowRect(GetDesktopWindow(), &rc);

  // Создаем временное окно с толстой
  // рамкой для изменения размера, но без
  // заголовка и системного меню.
  // При создании окна указываем произвольные
  // размеры окна и произвольное расположение
  hwnd = CreateWindow(
    szClassName, szWindowTitle, 
    WS_POPUPWINDOW | WS_THICKFRAME,
    100, 100,
    100, 100,
    0, 0, hInstance, NULL); 

  if(!hwnd)
    return FALSE;

  // Передвигаем окно в правый нижний
  // угол экрана и делаем его самым
  // верхним, т. е. это окно будет
  // всегда находиться над другими окнами
  SetWindowPos(hwnd, HWND_TOPMOST,
    rc.right  - cxChar * 15,
    rc.bottom - cyChar * 3,
    cxChar * 10, cyChar * 2, 0);

  // Отображаем окно в новом месте
  ShowWindow(hwnd, nCmdShow);
  UpdateWindow(hwnd);

  // Запускаем цикл обработки сообщений
  while(GetMessage(&msg, 0, 0, 0))
  {
    DispatchMessage(&msg);
  }
  return msg.wParam;
}

// =====================================
// Функция InitApp
// =====================================

BOOL
InitApp(HINSTANCE hInstance)
{
  ATOM aWndClass; // атом для кода возврата
  WNDCLASS wc;    // структура для регистрации
                  // класса окна

  memset(&wc, 0, sizeof(wc));
  wc.style         = CS_HREDRAW | CS_VREDRAW;
  wc.lpfnWndProc   = (WNDPROC) WndProc;
  wc.cbClsExtra    = 0;
  wc.cbWndExtra    = 0;
  wc.hInstance     = hInstance;
  wc.hIcon   = LoadIcon(NULL, IDI_APPLICATION);
  wc.hCursor = LoadCursor(NULL, IDC_ARROW);
  wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
  wc.lpszMenuName  = (LPSTR)NULL;
  wc.lpszClassName = (LPSTR)szClassName;

  aWndClass = RegisterClass(&wc);
  return (aWndClass != 0);
}

// =====================================
// Функция WndProc
// =====================================

LRESULT CALLBACK _export
WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
  HDC        hdc;
  PAINTSTRUCT ps;

  switch (msg)
  {
    case WM_CREATE:
    {
      // Устанавливаем перехватчики
      SetKbHook(hwnd);

      // Получаем контекст отображения
      hdc = GetDC(hwnd);

      // Выбираем шрифт с фиксированной шириной букв
      SelectObject(hdc, GetStockObject(SYSTEM_FIXED_FONT));

      // Заполняем структуру информацией
      // о метрике шрифта, выбранного в
      // контекст отображения
      GetTextMetrics(hdc, &tm);

      // Запоминаем значение ширины для
      // самого широкого символа
      cxChar = tm.tmMaxCharWidth;

      // Запоминаем значение высоты букв с
      // учетом межстрочного интервала
      cyChar = tm.tmHeight + tm.tmExternalLeading;

      ReleaseDC(hwnd, hdc);
      return 0;
    }

    // Для обеспечения возможности перемещения
    // окна, не имеющего заголовка, встраиваем
    // свой обработчик сообщения WM_NCHITTEST
    case WM_NCHITTEST:
    {
      long lRetVal;

      // Вызываем функцию DefWindowProc и проверяем
      // возвращаемое ей значение
      lRetVal = DefWindowProc(hwnd, msg, wParam, lParam);

      // Если курсор мыши находится на одном из
      // элементов толстой рамки, предназначенной
      // для изменения размера окна, возвращаем
      // неизмененное значение, полученное от
      // функции DefWindowProc
      if(lRetVal == HTLEFT        ||
	 lRetVal == HTRIGHT       ||
	 lRetVal == HTTOP         ||
	 lRetVal == HTBOTTOM      ||
	 lRetVal == HTBOTTOMRIGHT ||
	 lRetVal == HTTOPRIGHT    ||
	 lRetVal == HTTOPLEFT     ||
	 lRetVal == HTBOTTOMLEFT)
      {
	return lRetVal;
      }

      // В противном случае возвращаем значение
      // HTCAPTION, которое соответствует
      // заголовку окна.
      else
      {
	return HTCAPTION;
      }
    }

    case WM_DESTROY:
    {
      // Перед завершением работы приложения
      // удаляем перехватчики
      RemoveKbHook();

      PostQuitMessage(0);
      return 0;
    }

    // Это сообщение приходит от DLL-библиотеки
    // при переключении раскладки клавиатуры
    case WM_KBHOOK:
    {
      // Получаем флаг раскладки
      bCyrillic = (BOOL)wParam;

      // Выдаем звуковой сигнал
      MessageBeep(0);

      // Перерисовываем окно приложения
      InvalidateRect(hwnd, NULL, FALSE);
      return 0;
    }

    case WM_PAINT:
    {
      BYTE   szBuf[10];
      RECT    rc;

      // Получаем контекст отображения
      hdc = BeginPaint(hwnd, &ps);

      // Выбираем шрифт с фиксированной шириной букв
      SelectObject(hdc, GetStockObject(SYSTEM_FIXED_FONT));

      // Получаем координаты и размер окна
      GetClientRect(hwnd, &rc);

      // В зависимости от состояния флага раскладки
      // клавиатуры выбираем надпись для
      // отображения в окне
      if(bCyrillic)
        lstrcpy(szBuf, (LPCSTR)"CYRILLIC");
      else
        lstrcpy(szBuf, (LPCSTR)"DEFAULT ");

      // Выводим надпись в центре окна
      DrawText(hdc, (LPSTR)szBuf, 8, &rc,
	       DT_CENTER | DT_VCENTER | DT_NOCLIP | DT_SINGLELINE);

      EndPaint(hwnd, &ps);
    }
  }
  return DefWindowProc(hwnd, msg, wParam, lParam);
}

