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

#define STRICT
#include <windows.h>
#include <stdio.h>
#include <string.h>

void PrintMsg(HWND, WPARAM, LPARAM, char *);

static int cxChar, cyChar;
RECT rect;

LRESULT CALLBACK _export
WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
  HDC hdc;              // индекс контекста устройства
  PAINTSTRUCT ps;       // структура для рисования
  static TEXTMETRIC tm; // структура для записи метрик
                        // шрифта

  switch (msg)
  {
    case WM_CREATE:
    {
      // Получаем контекст отображения,
      // необходимый для определения метрик шрифта
      hdc = GetDC(hwnd);

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

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

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

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

      // Освобождаем контекст
      ReleaseDC(hwnd, hdc);

      // Задаем верхнюю границу несвертываемой
      // области окна. Эта область будет использована
      // для двух строк заголовка
      rect.top = 3 * cyChar;

      return 0;
    }

    case WM_SIZE:
    {
      // Сохраняем координаты нижнего правого
      // угла окна
      rect.right  = LOWORD(lParam);
      rect.bottom = HIWORD(lParam);

      // Перерисовываем все окно
      InvalidateRect(hwnd, NULL, TRUE);
      UpdateWindow(hwnd);

      return 0;
    }

    case WM_PAINT:
    {
      // Две строки заголовка сообщений
      char szHead1[] =
	"Message              Char  wParam lParam   KeyName";
      char szHead2[] =
	"-------              ----  ------ ------   -------";

      hdc = BeginPaint(hwnd, &ps);

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

      // Выводим строки заголовка
      TextOut(hdc, cxChar, cyChar/2,
        szHead1, sizeof(szHead1) - 1);
      TextOut(hdc, cxChar, cyChar/2 + cyChar,
        szHead2, sizeof(szHead2) - 1);

      EndPaint(hwnd, &ps);
      return 0;
    }

    case WM_KEYDOWN:
    {
      PrintMsg(hwnd, wParam, lParam, "WM_KEYDOWN");
      break;
    }

    case WM_KEYUP:
    {
      PrintMsg(hwnd, wParam, lParam, "WM_KEYUP");
      break;
    }

    case WM_SYSKEYDOWN:
    {
      PrintMsg(hwnd, wParam, lParam, "WM_SYSKEYDOWN");
      break;
    }

    case WM_SYSKEYUP:
    {
      PrintMsg(hwnd, wParam, lParam, "WM_SYSKEYUP");
      break;
    }

    case WM_CHAR:
    {
      PrintMsg(hwnd, wParam, lParam, "WM_CHAR");
      break;
    }

    case WM_SYSCHAR:
    {
      PrintMsg(hwnd, wParam, lParam, "WM_SYSCHAR");
      break;
    }

    case WM_DEADCHAR:
    {
      PrintMsg(hwnd, wParam, lParam, "WM_DEADCHAR");
      break;
    }

    case WM_SYSDEADCHAR:
    {
      PrintMsg(hwnd, wParam, lParam, "WM_SYSDEADCHAR");
      break;
    }

    case WM_DESTROY:
    {
      PostQuitMessage(0);
      return 0;
    }
  }
  return DefWindowProc(hwnd, msg, wParam, lParam);
}

// ==========================================
// Функция для вывода параметров сообщений
// от клавиатуры в окно
// ==========================================

void PrintMsg(HWND hwnd, WPARAM wParam, LPARAM lParam,
	char *szMsg)
{
  HDC  hdc;
  char szBuf[256];
  char szKeyName[20];
  int  nBufSize;
  int  rc;

  // Сворачиваем часть окна, не занятую заголовком
  ScrollWindow(hwnd, 0, -cyChar, &rect, &rect);

  hdc = GetDC(hwnd);

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

  // Получаем имя клавиши, определенное в
  // драйвере клавиатуры
  rc = GetKeyNameText(lParam, szKeyName, 20);
  if(!rc) MessageBeep(0);

  // Подготавливаем строку, описывающую сообщение
  nBufSize = wsprintf(szBuf,
    "%-14s       %c     %02x     %08lX %-20s",
    (LPSTR)szMsg, (BYTE)wParam, (BYTE)wParam,
    lParam, (LPSTR)szKeyName);

  // Выводим строку в нижнюю часть окна
  TextOut(hdc, cxChar, rect.bottom - cyChar, szBuf, nBufSize);

  ReleaseDC(hwnd, hdc);

  // Удаляем всю внутреннюю часть окна из
  // списка областей, требующих обновления
  ValidateRect(hwnd, NULL);
}
