// =================================================
// Определения
// =================================================

#define INCL_WIN
#define INCL_GPI
#define INCL_WINDIALOGS
#include <os2.h>
#include <stdio.h>
#include <stdlib.h>
#include "pointer.h"

// Прототип функции окна приложения
MRESULT EXPENTRY WndProc(HWND, ULONG, MPARAM, MPARAM);

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

HAB  hab;
HWND hWndFrame;
HWND hWndClient;

CHAR szAppTitle[] = "Mouse Poiter";

// Размеры пиктограммы курсора мыши
LONG cxPointer, cyPointer;

// Размеры окна Client Window
LONG cxClient, cyClient;

// =================================================
// Главная функция приложения main
// =================================================

int main()
{
  HMQ   hmq;
  QMSG  qmsg;
  BOOL  fRc;

  // Флаги для создания окна Frame Window
  ULONG flFrameFlags =
    FCF_SYSMENU    | FCF_TITLEBAR      | FCF_MINMAX   |
    FCF_SIZEBORDER | FCF_SHELLPOSITION | FCF_TASKLIST |
    FCF_ICON;

  // Имя класса главного окна
  CHAR  szWndClass[] = "MOUSEPOINTER";

  hab = WinInitialize(0);
  if(hab == NULLHANDLE)
  {
    WinMessageBox(HWND_DESKTOP, HWND_DESKTOP,
      "Ошибка инициализации",
      "Ошибка", 0, MB_ICONHAND | MB_OK);
    return(-1);
  }

  // Создаем очередь сообщений
  hmq = WinCreateMsgQueue(hab, 0);
  if(hmq == NULLHANDLE)
  {
    WinMessageBox(HWND_DESKTOP, HWND_DESKTOP,
      "Ошибка при создании очереди сообщений",
      "Ошибка", 0, MB_ICONHAND | MB_OK);
    WinTerminate(hab);
    return(-1);
  }

  // Регистрация главного окна приложения
  fRc = WinRegisterClass(hab, szWndClass,
    (PFNWP)WndProc, 0, 0);
  if(fRc == FALSE)
  {
    WinMessageBox(HWND_DESKTOP, HWND_DESKTOP,
      "Ошибка при регистрации класса главного окна",
      "Ошибка", 0, MB_ICONHAND | MB_OK);
    WinDestroyMsgQueue(hmq);
    WinTerminate(hab);

    return(-1);
  }

  // Создаем главное окно приложения
  hWndFrame = WinCreateStdWindow (HWND_DESKTOP,
    WS_VISIBLE,
    &flFrameFlags, szWndClass, szAppTitle,
    0, 0, ID_APP_FRAMEWND, &hWndClient);
  if(hWndFrame == NULLHANDLE)
  {
    WinMessageBox(HWND_DESKTOP, HWND_DESKTOP,
      "Ошибка при создании главного окна",
      "Ошибка", 0, MB_ICONHAND | MB_OK);
    WinDestroyMsgQueue(hmq);
    WinTerminate(hab);

    return(-1);
  }

  // Устанавливаем новую пиктограмму
  // для главного окна
  WinSendMsg(hWndFrame, WM_SETICON,
    (MPARAM)WinQuerySysPointer(HWND_DESKTOP,
          SPTR_ICONINFORMATION, FALSE), NULL);

  // Запускаем цикл обработки сообщений
  while(WinGetMsg(hab, &qmsg, 0, 0, 0))
    WinDispatchMsg(hab, &qmsg);

  WinDestroyWindow(hWndFrame);
  WinDestroyMsgQueue(hmq);
  WinTerminate(hab);
  return(0);
}

// =================================================
// Функция главного окна приложения
// =================================================

MRESULT EXPENTRY
WndProc(HWND hWnd, ULONG msg, MPARAM mp1, MPARAM mp2)
{
  HPS hps;
  RECTL rec;
  POINTL ptl;
  LONG x, y;
  HPOINTER hptr;
  LONG xMouse, yMouse;

  switch (msg)
  {
    case WM_CREATE:
    {
      // Определяем и сохраняем размеры пиктограмм
      cxPointer = WinQuerySysValue(HWND_DESKTOP,
        SV_CXPOINTER);

      cyPointer = WinQuerySysValue(HWND_DESKTOP,
        SV_CYPOINTER);

      // Расстояние между пиктограммами
      cxPointer += 5;
      return FALSE;
    }

    case WM_PAINT:
    {
      // Получаем пространство отображения
      hps = WinBeginPaint(hWnd, NULLHANDLE, &rec);

      // Закрашиваем область, требующую обновление
      WinFillRect(hps, &rec, CLR_WHITE);

      // Рисуем стандартные изображения курсора мыши
      x = 0;
      y = cyClient - cyPointer;
      DrawMousePtr(hps, x, y, SPTR_ARROW);

      x += cxPointer;
      DrawMousePtr(hps, x, y, SPTR_TEXT);
      x += cxPointer;
      DrawMousePtr(hps, x, y, SPTR_WAIT);
      x += cxPointer;
      DrawMousePtr(hps, x, y, SPTR_MOVE);
      x += cxPointer;
      DrawMousePtr(hps, x, y, SPTR_SIZENWSE);
      x += cxPointer;
      DrawMousePtr(hps, x, y, SPTR_SIZENESW);
      x += cxPointer;
      DrawMousePtr(hps, x, y, SPTR_SIZEWE);
      x += cxPointer;
      DrawMousePtr(hps, x, y, SPTR_SIZENS);

      // Рисуем стандартные пиктограммы
      x = 0;
      y = cyClient - 3*cyPointer;
      DrawMousePtr(hps, x, y, SPTR_APPICON);
      x += cxPointer;
      DrawMousePtr(hps, x, y, SPTR_ICONINFORMATION);
      x += cxPointer;
      DrawMousePtr(hps, x, y, SPTR_ICONQUESTION);
      x += cxPointer;
      DrawMousePtr(hps, x, y, SPTR_ICONERROR);
      x += cxPointer;
      DrawMousePtr(hps, x, y, SPTR_ICONWARNING);
      x += cxPointer;
      DrawMousePtr(hps, x, y, SPTR_ILLEGAL);
      x += cxPointer;
      DrawMousePtr(hps, x, y, SPTR_FILE);
      x += cxPointer;
      DrawMousePtr(hps, x, y, SPTR_MULTFILE);
      x += cxPointer;
      DrawMousePtr(hps, x, y, SPTR_FOLDER);
      x += cxPointer;
      DrawMousePtr(hps, x, y, SPTR_PROGRAM);

      // Освобождаем пространство отображения
      WinEndPaint(hps);
      return 0;
    }

    case WM_MOUSEMOVE:
    {
       // Получаем идентификатор стандартной
       // пиктограммы SPTR_ICONINFORMATION
       hptr = WinQuerySysPointer(HWND_DESKTOP,
          SPTR_ICONINFORMATION, FALSE);

       // Устанавливаем курсор мыши так: чтобы он
       // имел форму пиктограммы SPTR_ICONINFORMATION
       WinSetPointer(HWND_DESKTOP, hptr);
       return (MRESULT)TRUE;
    }

    case WM_ERASEBACKGROUND:
      return(MRFROMLONG(1L));

    // При изменении размеров окна приложения
    // перерисовываем его содержимое
    case WM_SIZE:
    {
      // Сохраняем размеры окна Client Window
      cxClient = SHORT1FROMMP(mp2);
      cyClient = SHORT2FROMMP(mp2);

      // Обновляем окно Client Window
      WinInvalidateRect(hWnd, NULL, TRUE);
      return 0;
    }

    // Выполняем эмуляцию мыши с помощью клавиатуры
    case WM_CHAR:
    {
      // Фильтруем сообщения, соответствующие отжатию
      // клавиш, а также сообщения от обычных клавиш
      if((CHARMSG(&msg) ->fs & KC_KEYUP) |
         (!(CHARMSG(&msg) ->fs & KC_VIRTUALKEY)))
         return 0;

      // Определяем текущую позицию курсора мыши
      // в экранных координатах
      WinQueryPointerPos(HWND_DESKTOP, &ptl);

      // Преобразуем экранные координаты в оконные
      WinMapWindowPoints(HWND_DESKTOP, hWnd, &ptl, 1);

      // Сохраняем текущие оконные координаты курсора
      xMouse = ptl.x;
      yMouse = ptl.y;

      // Изменяем координаты в соответствии с
      // виртуальным кодом нажатой клавиши
      switch(CHARMSG(&msg) ->vkey)
      {
        case VK_LEFT:
        {
          xMouse -= cxPointer;
          break;
        }
        case VK_RIGHT:
        {
          xMouse += cxPointer;
          break;
        }
        case VK_DOWN:
        {
          yMouse -= cyPointer;
          break;
        }
        case VK_UP:
        {
          yMouse += cyPointer;
          break;
        }
        case VK_HOME:
        {
          xMouse = cxPointer;
          yMouse = cyClient - cyPointer;
          break;
        }
        case VK_END:
        {
          xMouse = cxClient - cxPointer;
          yMouse = cyPointer;
          break;
        }
        default:
          break;
      }

      // Не допускаем выхода курсора мыши за пределы
      // окна приложения
      ptl.x = max(min(xMouse, cxClient - 1), 0);
      ptl.y = max(min(yMouse, cyClient - 1), 0);

      // Преобразуем оконные координаты в экранные
      WinMapWindowPoints(hWnd, HWND_DESKTOP, &ptl, 1);

      // Устанавливаем новую позицию курсора мыши
      WinSetPointerPos(HWND_DESKTOP,
        (SHORT) ptl.x, (SHORT) ptl.y);

      return 0;
    }

    default:
      return(WinDefWindowProc(hWnd, msg, mp1, mp2));
  }
}

// =================================================
// Функция рисования курсора мыши
// =================================================

void DrawMousePtr(HPS hps, LONG x, LONG y, LONG lPtrId)
{
  HPOINTER hptr;

  // Получаем идентификатор стандартного курсора мыши
  hptr = WinQuerySysPointer(HWND_DESKTOP,
    lPtrId, FALSE);

  // Рисуем изображение курсора мыши
  WinDrawPointer(hps, x, y, hptr, DP_NORMAL);
}
