В приложениях Presentation Manager мышь используется также часто, как и клавиатура, а в некоторых случаях даже чаще. И хотя многие приложения могут обходиться без мыши, существуют такие приложения, для которых мышь является необходимым устройством ввода. Например, большинство функций графических редакторов выполняется только с помощью мыши.
Когда пользователь перемещает мышь в окне приложения, функция окна получает сообщение с кодом WM_MOUSEMOVE . Если установить курсор мыши в область окна и нажимать на мыши клавиши, функция окна также будет получать различные сообщения, которые мы скоро подробно рассмотрим. Вместе с параметрами этих сообщений передаются текущие координаты курсора мыши.
В настоящий момент выпускаются мыши с одной, двумя или тремя кнопками. И хотя больше всего распространены двухкнопочные мыши, ваше приложение должно уметь определять количество кнопок и соответствующим образом корректировать свои функции.
В программном интерфейсе Presentation Manager имеется функция с именем WinQuerySysValue , с помощью которой можно получить значения различных параметров системы Presentation Manager. Прототип этой функции представлен ниже:
LONG WinQuerySysValue ( HWND hwndDeskTop, // идентификатор окна рабочего стола LONG iSysValue); // код системного параметра
Для параметра hwndDeskTop вы можете указать значение HWND_DESKTOP. Параметр iSysValue определяет, значение какого системного параметра необходимо определить.
Для того чтобы узнать количество кнопок мыши, подключенной к компьютеру, приложение может использовать функцию WinQuerySysValue следующим образом:
LONG lButtons; // количество кнопок lButtons = WinQuerySysValue (HWND_DESKTOP, SV_CMOUSEBUTTONS );
Если функция WinQuerySysValue вернула нулевое значение, мышь к компьютеру не подключена. Если мышь необходима для выполнения основных функций, приложение может вывести сообщение об ошибке и завершить свою работу. Однако в большинстве случаев функции мыши могут быть продублированы клавиатурой, поэтому не каждому приложению требуется проверять наличие мыши.
До сих пор для того чтобы различать кнопки мыши, мы называли их в зависимости от расположения - левая, правая или средняя кнопка. Однако в документации к программному интерфейсу Program Manager принято нумеровать кнопки.
Для однокнопочной мыши все просто - единственная кнопка имеет первый номер. Левая кнопка двухкнопочной мыши имеет первый номер, а правая - второй. Кнопки трехкнопочной мыши нумеруются следующим образом: левая имеет первый номер, правая - третий, средняя - второй.
Однако с помощью приложения Mouse, пиктограмма которого находится в папке System Setup, пользователь-левша может изменить назначение кнопок. При этом левая кнопка двухкнопочной мыши будет иметь второй номер, а правая - первый. В трехкнопочной мыши номера левой и правой кнопок меняются местами, а номер средней остается прежним.
В отличие от клавиатурных сообщений, поступающих в то окно, которое имеет фокус ввода, сообщения от мыши попадают в окно, в котором расположен курсор мыши. Если на экране находится несколько перекрывающих друг друга окон, сообщения мыши передаются в окно, расположенное наверху. В том случае, когда в родительском окне создано дочернее окно и курсор мыши располагается в дочернем окне, то сообщения мыши посупают в функцию дочернего окна.
Когда пользователь нажимает клавишу мыши, функция окна, расположенного под курсором мыши, получает одно из следующих сообщений: WM_BUTTON1DOWN , WM_BUTTON2DOWN , WM_BUTTON3DOWN (соответственно, для первой, второй и третьей кнопки). При отпускании клавиши мыши функция окна получает сообщения WM_BUTTON1UP , WM_BUTTON2UP и WM_BUTTON3UP (в зависимости от номера кнопки). Пример обработки сообщения WM_BUTTON1DOWN есть в приложении MYWINDOW, исходные тексты которого мы привели в первой главе этой книги.
Таким образом, приложение может выполнять раздельную обработку операций нажатия и отпускания кнопок мыши.
Если пользователь сделал одиночный щелчок в окне приложения (т. е. достаточно бытро нажал и отпустил клавишу мыши, не свигая курсор), в функцию окна после отпускания кнопки мыши поступит одно из следующих сообщений: WM_BUTTON1CLICK , WM_BUTTON2CLICK или WM_BUTTON3CLICK .
В ответ на двойной щелчок в функцию окна поступают сообщения WM_BUTTON1DBLCLK , WM_BUTTON2DBLCLK и WM_BUTTON3DBLCLK . Заметим, что в ответ на первый щелчок при этом в функцию окна потсупят сообщения о нажатии кнопки и об отпускани кнопки. В ответ на второй щелчок придет сообщение о двойном щелчке и затем сообщение об отпускании кнопки.
Через параметр mp1 вместе с перечисленными выше сообщениями передаются координаты курсора мыши. Вы можете извлечь их с помощью макрокоманд SHORT1FROMMP или SHORT2FROMMP (соответственно, координату X и Y). Через параметр mp2 передаются флаги и результаты теста Hit Test (о котором мы расскажем позже).
Для извлечения параметров собщений мыши удобно использовать макрокоманду MOUSEMSG , например:
case WM_BUTTON1DOWN : { cxPoint = MOUSEMSG(&msg) -> x; cyPoint = MOUSEMSG(&msg) -> y; break; }
Эта макрокоманда, а также соответствующая структура MSEMSG и указатель на нее определены следующим образом:
typedef struct _MOUSEMSG { SHORT x; // коодрината X SHORT y; // координата Y USHORT codeHitTest; // результаты тестирования USHORT fsInp; // флаги } MSEMSG; typedef MSEMSG *PMSEMSG; #define MOUSEMSG(pmsg) \ ((PMSEMSG)((PBYTE)pmsg + sizeof(MPARAM)))
В поле fsInp могут расплагаться флаги,
аналогичные флагам, передаваемым вместе с
сообщением WM_CHAR . Используя эти флаги, можно
определить, была ли нажата на клавиатуре
какая-либо клавиша в момент, когда пользователь
нажал клавишу мыши. Список возможных значений
для поля fsInp был приведен при описании сообщения
WM_CHAR. Теперь к этому списку добавится только одно
значение:
Флаг |
Маска |
Описание |
KC_NONE |
0x0000 |
На клавиатуре не была нажата ни одна
клавиша |
Поле codeHitTest мы опишем позже.
Сообщение WM_MOUSEMOVE передается в функцию окна при перемещении курсора мыши внутри окна. Параметры, которые передаются с этим сообщением, такие же, что и параметры сообщений от клавиш мыши, рассмотренные выше.
По умолчанию это сообщение обрабатывается функцией WinDefWindowProc , причем соответствующий обработчик устанавливает стандартную форму курсора мыши (в виде стрелки). Приложение может изменить форму курсора мыши, выполняя обработку сообщения WM_MOUSEMOVE . В приложении MOUSEMOV, исходные тексты которого приведены в этой главе, мы покажем, как это делается.
С помощью сообщения WM_HITTEST система Presentation Manager может определить способ обработки сообщений мыши для заданного окна. Это сообщение передается фукнции окна только в том случае, если при регистрации класса окна был указан стиль CS_HITTEST .
Определив собственный обработчик для этого сообщения, приложение может заблокировать сообщения мыши для выбранного окна, при этом окно будет вести себя как заблокированное. Попытка сделать щелчок мышью в этом окне вызовет звуковой сигнал.
Обработчик сообщения WM_HITTEST получает через
парметр mp1 координаты курсора мыши. Он может
вернуть одно из перечисленных ниже значений (так
называемый индикатор теста Hit Test):
Значение |
Описание |
HT_NORMAL |
Сообщения мыши обрабатываются обычным
образом |
HT_TRANSPARENT |
Часть окна, расположенная под курсором
мыши, считается прозрачной. Тест должен
выполняться для окна, расположенного ниже под
данным окном, как будто прозрачное окно не
существует. В качестве прозрачного вы можете
объявить, например, дочернее окно |
HT_DISCARD |
Сообщения мыши не должны передаваться
функции окна |
HT_ERROR |
Аналогично предыдущему, однако
раздается звуковой сигнал и окно отодвигается на
задний план |
По умолчанию обработчик активного окна возвращает значение HT_NORMAL, а обработчик заблокированного окна - значение HT_ERROR.
Одна из часто выполняемых с помощью мыши операций - перемещение объектов (drag and drop). Для выполнения этой операции вы можете использовать сообщения WM_BUTTON1MOTIONSTART и WM_BUTTON1MOTIONEND и аналогичные для кнопок с другими номерами. Первое из этих сообщений передается функции окна когда пользователь начинает операцию перемещения, нажав кнопку с соответствующим номером.
Через параметр mp1 этих сообщений передаются координаты курсора мыши, а через младшее слово параметра mp2 - результаты теста Hit Test.
Кроме этого, для обработки операций перемещения можно использовать сообщения WM_BEGINDRAG и WM_ENDDRAG . Через параметр mp1 сообщений WM_BEGINDRAG и WM_ENDDRAG передаются координаты курсора мыши, а через младшее слово параметра mp2 - флаг, определяющий устройство ввода, которое было использовано для выполнения операции перемещения. Если это была мышь, флаг равен TRUE, если клавиатура - FALSE.
Есть еще одно сообщение, имеющее отношение к мыши. Это сообщение с кодом WM_MOUSEMAP , позволяющее определить отображение клавиш мыши в сообщениях, проходящих через очередь. Однако это сообщение не рекомендуется к использованию обычными приложениями.
В программном интерфейсе Presentation Manager есть несколько функций, предназначенных для работы с мышью. В этом разделе мы опишем некоторые из них, наиболее полезные на наш взгляд.
Как мы уже говорили, сообщения мыши поступают в функцию того окна, в котором находится курсор мыши. Однако при необходимости приложение может захватить мышь в монопольное владение с помощью функции WinSetCapture . При этом функция окна приложения будет получать сообщения мыши вне зависимости от того, где находится курсор мыши. Если курсор будет расположен вне окна приложения, координаты курсора могут принимать отрицательные значения.
Для чего приложению может потребоваться захват мыши?
Например, для того чтобы выделить какую-либо область рабочего стола Workplace Shell или нарисовать что-либо вне области, занятой окном приложения. Типичный пример - приложение, сохраняющее выделенный фрагмент изображения рабочего стола в графическом файле.
Для захвата мыши приложение может вызвать функцию WinSetCapture следующим образом:
WinSetCapture (HWND_DESKTOP, hWnd);
В качестве первого параметра функции передается идентификатор окна Desktop Window , в качестве второго - идентификатор окна, захватывающего мышь.
Освободить мышь несложно. Для этого достаточно вызвать функцию WinSetCapture еще раз, указав в качестве второго параметра идентификатор NULLHANDLE:
WinSetCapture (HWND_DESKTOP, NULLHANDLE);
Один из способов определения координат курсора мыши вы уже знаете - эти координаты передаются вместе с сообщениями мыши. Существует и и другой способ, основанный на вызове функций программного интерфейса Presentation Manager.
С помощью функции WinQueryPointerPos вы можете узнать положение курсора на момент выборки последнего сообщения из очереди приложения. Прототип функции приведен ниже:
BOOL WinQueryPointerPos ( HWND hwndDeskTop, // идентификатор окна Desktop Window PPOINTL pptlPoint); // адрес структуры типа POINTL
Положение курсора записывается в структуру типа POINTL, адрес которой передается функции через параметр pptlPoint. В случае успешного завершения функция возвращает значение TRUE, при ошибке - FALSE.
Заметим, что при определении координат курсора мыши используется система координат, связанная с окном рабочего стола. Таким образом, с помощью этой функции вы можете определить не окнонные, а экранные координаты курсора мыши.
Так же как и функция WinQueryPointerPos , функция WinQueryMsgPos возвращает экранные координаты курсора мыши. Однако в отличие от функции WinQueryPointerPos, координаты курсора определяются не в момент извлечения сообщения из очереди, а в момент записи последнего сообщения в очередь приложения.
Прототип функции WinQueryMsgPos приведен ниже:
BOOL WinQueryMsgPos ( HAB hab, // идентификатор блока Anchor-block PPOINTL pptl); // адрес структуры типа POINTL
С помощью функции WinGetKeyState приложение может определить состояние любой клавиши мыши или любой клавиши, расположенной на клавиатуре, на момент выборки последнего сообщения из очереди приложения.
Прототип функции WinGetKeyState представлен ниже:
LONG WinGetKeyState ( HWND hwndDeskTop, // идентификатор окна Desktop Window LONG vkey); // код виртуальной клавиши
Для того чтобы определить состояние кнопок мыши, необходимо указать в параметре vkey значения констант VK_BUTTON1 , VK_BUTTON2 или VK_BUTTON3 . Информацию о кнопках, расположенных на клавиатуре, вы можете получить, указав в этом параметре соответствующий код виртуальной клавиши. Эти коды приведены в главе нашей книги, посвященной клавиатуре.
Возвращаемое функцией значение может быть
комбинацией следующих констант:
Константа |
Описание |
0x0001 |
С момента запуска операционной системы
клавиша была нажато нечетное количество раз |
0x8000 |
Клавиша нажата |
Ваше приложение может не только определить текущую позицию курсора мыши, но и установить курсор в новую позицию. Для этого следует использовать функцию WinSetPointer Pos , прототип которой приведен ниже:
BOOL WinSetPointer Pos ( HWND hwndDeskTop, // идентификатор окна Desktop Window LONG lx, // новая экранная координата X LONG ly); // новая экранная координата Y
Заметим, что для установки курсора мыши используются экранные координаты.
С помощью функции WinShowPointer вы можете убрать курсор мыши с экрана или высветить его вновь. Прототип этой функции представлен ниже:
BOOL WinShowPointer( HWND hwndDeskTop, // идентификатор окна Desktop Window BOOL fShow); // флаг
Вызывая эту функцию, вы можете увеличивать или уменьшать внутренний индикатор отображения курсора, передавая через параметр fShow, соответственно, значения FALSE и TRUE. Если этот индикатор равен нулю, курсор мыши отображается, а если больше нуля - нет.
Функция WinShowPointer возвращает новое значение индикатора отображения курсора мыши.
С помощью сообщения WM_MOUSEMOVE мы попробуем решить задачу перемещения окна приложения, не имеющего заголовка. Приложение MOUSEMOV создает окно без заголовка, системного меню и кнопок минимизации/максимизации, отображая в его центре текстовую строку (рис. 6.1).
Рис. 6.1. Окно приложения MOUSEMOV не имеет заголовка, но его можно перемещать при помощи мыши
Если в окне нажать левую клавишу мыши, то обрабатывая сообщение WM_MOUSEMOVE , приложение отслеживает текущие координаты мыши, передвигая соответствующим образом главное окно приложения Frame Window .
Когда курсор мыши оказывается над окном приложения MOUSEMOV, его форма изменяется - курсор принимает вид открытой ладони. Если нажать левую клавишу мыши, курсор будет изображаться в виде закрытой ладони. Внешне это выглядит так, как будто в процессе перемещения окна вы берете его рукой, переносите и затем отпускаете руку в нужном месте.
Рамка окна позволяет изменять размеры окна, причем текст отображается всегда в центре окна незвисимо от этих размеров.
Исходные тексты приложения MOUSEMOV приведены в листинге 6.1.
Листинг 6.1. Файл mousemov\mousemov.c
// ================================================= // Определения // ================================================= #define INCL_WIN #define INCL_GPI #define INCL_WINDIALOGS #include <os2.h> #include <stdio.h> #include "mousemov.h" // Прототип функции окна приложения MRESULT EXPENTRY WndProc(HWND, ULONG, MPARAM, MPARAM); // ================================================= // Глобальные переменные // ================================================= HAB hab; HWND hWndFrame; HWND hWndClient; CHAR szAppTitle[] = "Mouse Mover"; // Координаты курсора мыши в момент нажатия // левой клавиши мыши SHORT cxPoint; SHORT cyPoint; // Координаты курсора мыши в момент отпускания // левой клавиши мыши SHORT cxNewPoint; SHORT cyNewPoint; // Признак начала процесса перемещения окна BOOL fDrag = FALSE; // Текст, который будет отображаться в окне CHAR pszText[] = "Перемещайте окно при помощи левой клавиши мыши"; // Идентификаторы указателей мыши HPOINTER hptr, hptr1; // ================================================= // Главная функция приложения main // ================================================= int main () { HMQ hmq; QMSG qmsg; BOOL fRc; // Флаги для создания окна Frame Window // Мы не создаем окна заголовка, системного меню, // а также окна кнопок минимизации и максимизации ULONG flFrameFlags = FCF_SIZEBORDER | FCF_SHELLPOSITION | FCF_TASKLIST | FCF_ICON; // Имя класса главного окна CHAR szWndClass[] = "MOUSEMOV"; 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); } 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; POINTL ptl; RECTL rec; SWP swp; switch (msg) { // При создании окна загружаем указатели мыши case WM_CREATE : { hptr = WinLoadPointer(HWND_DESKTOP, NULLHANDLE, ID_APP_POINTER); hptr1 = WinLoadPointer(HWND_DESKTOP, NULLHANDLE, ID_APP_POINTER1); return FALSE; } // При уничтожении окна удаляем указатели мыши case WM_DESTROY : { WinDestroyPointer (hptr); WinDestroyPointer (hptr1); return 0; } // Когда пользователь нажимает левую клавишу // мыши, запоминаем координаты курсора и // выдвигаем окно приложения на передний план case WM_BUTTON1DOWN : { cxPoint = MOUSEMSG(&msg) -> x; cyPoint = MOUSEMSG(&msg) -> y; // Изменяем расположение окна по оси Z WinSetWindowPos (hWndFrame, HWND_TOP , 0, 0, 0, 0, SWP _ZORDER ); // Устанавливаем признак перемещения // главного окна приложения fDrag = TRUE; return 0; } // При отпускании левой клавиши мыши сбрасываем // признак перемещения окна case WM_BUTTON1UP : { // Сбрасываем признак перемещения // главного окна приложения fDrag = FALSE; return 0; } // Это сообщение приходит при перемещении курсора // мыши case WM_MOUSEMOVE : { // Если включен признак перемещения, определяем // новые координаты курсора и передвигаем окно if(fDrag) { // Выбираем указатель в виде закрытой руки WinSetPointer (HWND_DESKTOP, hptr1); cxNewPoint = MOUSEMSG(&msg) -> x; cyNewPoint = MOUSEMSG(&msg) -> y; // Определяем текущие координаты окна WinQueryWindow Pos(hWndFrame, &swp); // Передвигаем окно WinSetWindowPos (hWndFrame, HWND_TOP , swp.x + (cxNewPoint - cxPoint), swp.y + (cyNewPoint - cyPoint), 0, 0, SWP _MOVE ); } else // Выбираем указатель в виде открытой руки WinSetPointer (HWND_DESKTOP, hptr); return (MRESULT)TRUE; } // В ответ на сообщение WM_PAINT выводим // в центре окна строку текста case WM_PAINT : { // Получаем пространство отображения hps = WinBeginPaint (hWnd, NULLHANDLE, &rec); // Определяем размеры главного окна WinQueryWindow Rect(hWnd, &rec); // Выводим текст в центре окна WinDrawText (hps, -1, pszText, &rec, 0L, 0L, DT_WORDBREAK | DT_CENTER | DT_VCENTER | DT_TEXTATTRS | DT_ERASERECT); // Освобождаем пространство отображения WinEndPaint (hps); return 0; } case WM_ERASEBACKGROUND : return(MRFROMLONG(1L)); // При изменении размеров окна сохраняем новые // размеры и перерисовываем окно case WM_SIZE : { // При изменении размеров окна выполняем его // перерисовку WinInvalidateRect (hWnd, NULL, TRUE); return 0; } // Если пользователь сделал двойной щелчок левой // клавише мыши, завершаем работу приложения case WM_BUTTON1DBLCLK : { WinPostMsg (hWnd, WM_QUIT , 0L, 0L); return 0; } default: return(WinDefWindowProc (hWnd, msg, mp1, mp2)); } }
В переменные cxPoint и cyPoint записываются координаты курсора мыши в момент, когда пользователь начинает перемещение окна приложения, нажав левую кнопку мыши. Эти координаты будут затем сравниваться с координатами курсора мыши после завершения процесса перемещения, которые хранятся в переменных cxNewPoint и cyNewPoint.
Переменная fDrag используется в качестве признака перемещения окна и проверяется при обработке сообщения WM_MOUSEMOVE . Вначале в нее записывается значение FALSE. Когда пользователь начинает перемещать окно, это значение изменяется на TRUE. После завершения процесса перемещения в переменную снова записывается значение FALSE.
В области глобальных переменных мы также определили переменные hptr и hptr1 типа HPOINTER , в которых хранятся идентификаторы курсоров мыши, загруженных из ресурсов прложения. В первой из этих переменных хранится идентификатор курсора в виде открытой ладони, во второй - в виде закрытой ладони.
В функции main нет ничего особенного за исключением того, что при создании главного окна приложения используется сокращенный набор флагов:
ULONG flFrameFlags = FCF_SIZEBORDER | FCF_SHELLPOSITION | FCF_TASKLIST | FCF_ICON;
В результате окно Frame Window не создает окна заголовка, системное меню и кнопки минимизации/максимизации. Без дополнительной обработки сообщений мыши пользователь не сможет перемещать такое окно. Размер окна можно изменять, так как указан флаг FCF_SIZEBORDER.
Функция окна обрабатывает сообщения WM_CREATE , WM_DESTROY , WM_BUTTON1DOWN , WM_BUTTON1UP , WM_MOUSEMOVE , WM_PAINT , WM_ERASEBACKGROUND , WM_SIZE и WM_BUTTON1DBLCLK .
При создании окна обработчик сообщения WM_CREATE загружает из ресурсов приложения изображения курсоров мыши, пользуясь для этого функцией WinLoadPointer:
hptr = WinLoadPointer(HWND_DESKTOP, NULLHANDLE, ID_APP_POINTER); hptr1 = WinLoadPointer(HWND_DESKTOP, NULLHANDLE, ID_APP_POINTER1);
В качестве первого параметра функции передается идентификатор окна Desktop Window . Через второй параметр необходимо передать идентификатор модуля, содержащего соответствующий ресурс, или значение NULLHANDLE, если ресурс располагается в загрузочном файле приложения. Последний параметр предназначен для передачи идентификатора ресурса, с которым последний определен в файле описания ресурсов приложения.
В ответ на сообщение WM_DESTROY , которое передается в функцию окна при завершении работы приложения, выполняется уничтожение двух загруженных курсоров мыши при помощи функции WinDestroyPointer :
WinDestroyPointer (hptr); WinDestroyPointer (hptr1);
В качестве единственного параметра этой функции передается идентификатор уничтожаемого курсора мыши.
Обработчик сообщения WM_SIZE выполняет перерисовку окна приложения, когда пользователь изменяет его размеры, вызывая функцию WinInvalidateRect . В результате функция окна приложения получит сообщение WM_PAINT . Это необходимо для того чтобы строка символов всегда отображалась в центре окна.
Обработчик сообщения WM_PAINT получает пространство отображения и затем определяет размеры окна, вызывая для этого функцию WinQueryWindow Rect. Далее с помощью функции WinDrawText в центре окна выводится текстовая строка, после чего пространство отображения освобождается.
Когда пользователь начинает перемещение окна приложения MOUSEMOVE, он передвигает курсор мыши во внутреннюю область окна и нажимает левую клавишу мыши. При этом окно приложения получает сообщение WM_BUTTON1DOWN .
Обработчик этого сообщения запоминает координаты курсора мыши, соответствующие точке начала перемещения, в глобальных переменных cxPoint и cyPoint.
Далее окно выдвигается на передний план функцией WinSetWindowPos и устанавливается признак перемещения окна (в переменную fDrag записывается значение TRUE).
После выполнения перемещения пользователь отпускает левую клавишу мыши. При этом в функцию окна приложения передается сообщение WM_BUTTON1UP . Обработчик этого сообщения просто сбрасывает признак перемещения окна, записывая в переменную fDrag значение FALSE.
Окно приложения получает сообщение WM_MOUSEMOVE всегда, когда над ним перемещается курсор мыши. Если пользователь нажал левую клавишу мыши и начал перемещение окна, обработчиком сообщения WM_BUTTON1UP устанавливается признак fDrag. В результате обработчик сообщения WM_MOUSEMOVE начинает процедуру перемещения окна.
Прежде всего он изменяет форму курсора мыши, вызывая функцию WinSetPointer :
WinSetPointer (HWND_DESKTOP, hptr1);
В качестве первого параметра этой функции передается идентификатор окна Desktop Window , а в качестве второго - идентификатор курсора мыши, который будет отображаться внутри окна.
На следующем шаге обработчик сообщения WM_MOUSEMOVE записывает новые координаты курсора в переменные cxNewPoint и cyNewPoint, а также определяет текущие координаты окна Frame Window , вызывая для этого функцию WinQueryWindow Pos.
Затем выполняется перемещение окна при помощи функции WinSetWindowPos :
WinSetWindowPos (hWndFrame, HWND_TOP , swp.x + (cxNewPoint - cxPoint), swp.y + (cyNewPoint - cyPoint), 0, 0, SWP _MOVE );
Новые координаты окна выбираются исходя из текущих (записанных в структуре swp) и относительной величины смещения мыши по вертикали и горизонтали. Заметим, что функция WinQueryWindow Pos записывает в структуру swp координаты окна в системе координат, связанной с окном Desktop Window . Начало этой системы координат находится в левом нижнем углу экрана.
Если пользователь перемещает курсор мыши над окном приложения не нажимая левой клавиши мыши, обработчик сообщения WM_MOUSEMOVE отображает курсор мыши в виде открытой ладони:
WinSetPointer (HWND_DESKTOP, hptr);
Так как окно приложения не имеет ни системного меню, ни меню верхнего уровня, для завершения его работы мы используем двойной щелчок левой клавишей мыши в окне приложения. Обработчик соответствующего сообщения WM_BUTTON1DBLCLK записывает в очередь приложения сообщение WM_QUIT , что приводит к завершению цикла обработки сообщений и прекращению работы приложения.
В файле mousemov.h определены символичесике константы для ресурсов приложения (листинг 6.2).
Листинг 6.2. Файл mousemov\mousemov.h
#define ID_APP_FRAMEWND 1 #define ID_APP_POINTER 2 #define ID_APP_POINTER1 3
В файле описания ресурсов приложения mousemov.rc (листинг 6.3) помимо пиктограммы определены два указателя мыши, для чего использован оператор POINTER .
Листинг 6.3. Файл mousemov\mousemov.rc
#include <os2.h> #include "mousemov.h" ICON ID_APP_FRAMEWND MOUSEMOV.ICO POINTER ID_APP_POINTER MOUSEMOV.PTR POINTER ID_APP_POINTER1 MOUSE1.PTR
Файл определения модуля приложения представлен в листинге 6.4.
Листинг 6.4. Файл mousemov\mousemov.def
NAME MOUSEMOV WINDOWAPI DESCRIPTION 'MouseMov Application (C) Frolov A., 1996' HEAPSIZE 4096 STACKSIZE 32768 EXPORTS WndProc
В предыдущем приложении мы изменяли пиктограмму курсора мыши, когда он находился над поверхностью окна Client Window . Процедура изменения пиктограммы очень проста - в момент обработки сообщения WM_MOUSEMOVE нужно получить идентификатор нужной пиктограммы и передать его функции WinSetPointer .
Вы можете загрузить идентификатор пиктограммы из ресурсов приложения с помощью функции WinLoadPointer , передав ей в качестве последнего параметра идентификатор курсора мыши, с которым этот курсор определен в файле описания ресурсов приложения, например:
HPOINTER hptr; hptr = WinLoadPointer(HWND_DESKTOP, NULLHANDLE, ID_APP_POINTER);
Здесь подразумевается, что файл описания ресурсов приложения содержит строку вида:
POINTER ID_APP_POINTER MOUSEMOV.PTR
В файле MOUSEMOV.PTR находится изображение курсора мыши, подготовленное приложением Icon Editor . С помощью этого приложения вы можете создавать не только пиктограммы, но и курсоры мыши.
В системе Presentation Manager имеется также набор встроенных изображений курсора мыши, который также доступен для вашего приложения. Получить идентификатор одного из таких курсоров можно с помощью функции WinQuerySysPointer :
hptr = WinQuerySysPointer (HWND_DESKTOP, SPTR_ICONINFORMATION, FALSE);
Идентификатор встроенного курсора передается этой функции через второй параметр. Третий параметр определяет, надо ли копировать курсор для приложения (значение FALSE) либо достаточно просто вернуть идентификатор встроенного курсора (значение TRUE). В первом случае перед завершением приложение должно удалить копию встроенного курсора, передав соответствующий идентификатор фукнции WinDestroyPointer через ее единственный параметр.
Получив идентификатор курсора мыши, вы можете изменить форму курсора при помощи функции WinSetPointer , вызвав ее при обработке сообщения WM_MOUSEMOVE :
WinSetPointer (HWND_DESKTOP, hptr);
Ниже мы приведем список всроенных
идентификаторов курсора мыши вместе с кратким
описанием и соответствующим изображением:
Идентификатор |
Описание |
Изображение курсора мыши |
SPTR_ARROW |
Стандартный курсор |
|
SPTR_TEXT |
Текстовый курсор, используется при
редакторовании текста |
|
SPTR_WAIT |
Курсор в виде часов, используется для
режима, в котором пользователь должен ждать
завершение какой-либо длительной операции |
|
SPTR_MOVE |
Курсор для перемещения объекта |
|
SPTR_SIZENWSE |
Курсор для одновременного изменения
размеров окна по вертикали и горизонтали |
|
SPTR_SIZENESW |
Аналогично предыдущему, но с другим
направлением стрелки |
|
SPTR_SIZEWE |
Курсор для изменения ширины окна |
|
SPTR_SIZENS |
Курсор для изменения высоты окна |
|
С помощью функции WinQuerySysPointer вы также можете
получить идентификаторы встроенных пиктограмм,
которые также можно использовать для установки
курсора мыши:
Идентификатор |
Описание |
Изображение пиктограммы |
SPTR_APPICON |
Пиктограмма приложения |
|
SPTR_ICONINFORMATION |
Информационное сообщение |
|
SPTR_ICONQUESTION |
Запрос информации от пользователя |
|
SPTR_ICONERROR |
Сообщение об ошибке |
|
SPTR_ICONWARNING |
Предупреждающее сообщение |
|
SPTR_ILLEGAL |
Запрещенные действия |
|
SPTR_FILE |
Файл |
|
SPTR_MULTFILE |
Группа файлов |
|
SPTR_FOLDER |
Папка |
|
SPTR_PROGRAM |
Программа |
|
Для того чтобы можно было работать с приложением даже в том случае, если к компьютеру не подключена мышь (или подключена, но неисправна), имеет смысл предусмотреть дублирование функций мыши при помощи клавиатуры. Это можно сделать, обрабатывая соответствующим образом сообщение WM_CHAR , поступающее в фукнцию окна от клавиатуры. Параметры этого сообщения мы рассмотрели раньше, в главе, посвященной клавиатуре.
Идея управления положением курсора мыши при помощи клавиатуры заключается в том, чтобы в ответ на сообщение WM_CHAR от клавиш перемещения курсора определять, а затем изменять положение курсора мыши.
Определить текущие экранные координаты курсора мыши можно при помощи функции WinQueryPointerPos , например, так:
POINTL ptl; WinQueryPointerPos (HWND_DESKTOP, &ptl);
Эта функция записывает в структуру ptl координаты курсора мыши в системе координат, связанной с окном рабочего стола Desktop Window . Если нужно сделать так, чтобы при управлении с помощью клавиатуры курсор мыши не выходил за рамки окна приложения, экранные координаты курсора мыши следует преобразовать в оконные. Нужные преобразования можно сделать при помощи функции WinMapWindowPoints , передав ей в качестве первого параметра идентификатор окна рабочего стола, а в качестве второго - идентификатор окна приложения:
WinMapWindowPoints (HWND_DESKTOP, hWnd, &ptl, 1);
Подробнее мы рассмотрим эту функцию в одной из следующих книг "Библиотеки системного программиста" в главе, посвященной графическому интерфейсу Presentation Manager.
После преобразования координат обработчик сообщения WM_CHAR должен изменить поля x и y структуры ptl, увеличив их или уменьшив, в зависимости от того, какая клавиша была нажата. Затем нужно выполнить обратное преобразование оконных координат в экранные и установить курсор мыши в новую позицию.
Первая задача решается все той же функцией WinMapWindowPoints , однако теперь через первый параметр этой функции передается идентификатор окна приложения, а через второй - идентификатор окна рабочего стола:
WinMapWindowPoints (hWnd, HWND_DESKTOP, &ptl, 1);
Для решения второй задачи (установки курсора мыши в новую позицию) следует вызвать функцию WinSetPointer Pos , передав ей через первый параметр идентификатор окна рабочего стола, а через второй и третий - новые экранные координаты курсора мыши по оси X и Y, соответственно:
WinSetPointer Pos (HWND_DESKTOP, (SHORT) ptl.x, (SHORT) ptl.y);
Приложение POINTER отображет в своем окне изображения всех стандартных курсоров мыши и стандартных пиктограмм. Оно демонстрирует способ изменения формы курсора мыши, способ изменения пиктограммы приложения, отображаемой в левом верхнем углу последнего, а также способ управления курсором мыши при помощи клавиатуры.
Внешний вид главного окна приложения показан на рис. 6.2.
Рис. 6.2. Главное окно приложения POINTER
Исходные тексты приложения представлены в листинге 6.5.
Листинг 6.5. Файл pointer\pointer.c
// ================================================= // Определения // ================================================= #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); // Устанавливаем новую позицию курсора мыши WinSetPointer Pos (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); }
В области глобальных переменных определены переменные cxPointer и cyPointer, в которых хранятся размеры пиктограммы курсора мыши, полученные на этапе инициализации приложения.
Размеры окна приложения записываются в переменные cxClient и cyClient обработчиком сообщения WM_SIZE при создании окна и при изменении пользователем его размеров.
Функция main имеет одну особенность - после создания главного окна приложения и перед запуском цикла обработки сообщений главному окну посылается сообщение WM_SETICON :
WinSendMsg (hWndFrame, WM_SETICON , (MPARAM)WinQuerySysPointer (HWND_DESKTOP, SPTR_ICONINFORMATION, FALSE), NULL);
Через первый параметр вместе с этим сообщением передается идентификатор встроенной пиктограммы SPTR_ICONINFORMATION , полученный от функции WinQuerySysPointer . Второй параметр сообщения WM_SETICON равен нулю.
После получения этого сообщения пиктограмма приложения, которая отображается в окне системного меню и которая была загружена из ресурсов приложения, заменяется на новую.
Рассмотрим обработчики сообщений, расположенные в функции главного окна приложения WndProc.
Обработчик сообщения WM_CREATE определяет размеры курсора мыши. Эти размеры являются системными значениями, для получения которых необходимо использовать функцию WinQuerySysValue , передав ей через второй параметр константы SV_CXPOINTER (ширина курсора мыши) и SV_CYPOINTER (высота курсора мыши):
cxPointer = WinQuerySysValue (HWND_DESKTOP, SV_CXPOINTER); cyPointer = WinQuerySysValue (HWND_DESKTOP, SV_CYPOINTER);
Эти размеры используются приложением для размещения изображений пиктограмм в своем окне. Чтобы обеспечить интервал между пиктограммами, значение переменной cxPointer в нашем приложении увеличивается на 5 пикселов.
Обработчик этого сообщения получает пространство отображения и закрашивает его белым цветом. Затем он последовательно рисует изображения всех страндартных курсоров мыши и стандартных пиктограмм при помощи функции DrawMousePtr, определенной в нашем приложении:
x = 0; y = cyClient - cyPointer; DrawMousePtr(hps, x, y, SPTR_ARROW);
Через первый параметр этой функции необходимо передать идентификатор пространства отображения, черз второй и третий, соответственно, координаты левого нижнего угла пиктограммы в оконной системе координат, и, наконец, через последний параметр - идентификатор курсора мыши или пиктограммы.
Перед возвращением управления обработчик сообщения WM_PAINT освобождает пространство отображения.
Обработчик сообщения WM_MOUSEMOVE с помощью функции WinQuerySysPointer получает идентификатор встроенной пиктограммы SPTR_ICONINFORMATION и использует его для изменения курсора мыши. Последняя операция выполняется с помощью фукнции WinSetPointer . Таким образом, для курсора мыши вы можете использовать не только встроенные изображения курсоров, но и встроенные изображения пиктограмм.
Обратите внимание, что обработка сообщения WM_MOUSEMOVE завершается возвращением значения TRUE. Это необходимо для того, чтобы отменить вызов стандартного обработчика данного сообщения, который устанавливает стандартный курсор мыши. Если после обработки сообщения WM_MOUSEMOVE передать управление функции WinDefWindowProc , форму курсора изменить не удастся, так как данная функция будет всегда восстанавливать стандартный курсор.
Обработчик сообщения WM_SIZE получает и сохраняет размеры окна Client Window , а затем обновляет это окно, вызывая для этого функцию WinInvalidateRect . В результате главное окно приложения получит сообщение WM_PAINT .
Приложение выполняет обработку этого сообщения для дублирования функций мыши при помощи клавиатуры.
Так как сообщение WM_CHAR приходит и при нажатии, и при отжатии клавиш, наш обработчик фильтрует сообщения, пропуская только те, что соответствуют нажатиям. Кроме того, мы фильтруем сообщения от обычных символьных клавиш, которые не имеют виртуального кода клавиши:
if((CHARMSG (&msg) ->fs & KC_KEYUP) | (!(CHARMSG(&msg) ->fs & KC_VIRTUALKEY))) return 0;
После фильтрации обработчик сообщения WM_CHAR определяет текущие экранные координаты курсора мыши и преобразует их в оконные координаты, вызывая для этого функции WinQueryPointerPos и WinMapWindowPoints . Текущие оконные координаты курсора мыши сохраняютя в глобальных переменных xMouse и yMouse.
Далее наш обработчик сообщения WM_CHAR анализирует полученный код виртуальной клавиши, изменяя соответствующим образом значения глобальных переменных xMouse и yMouse.
Если были нажаты клавиши перемещения курсора влево или вправо, содержимое переменной xMouse, соответственно, уменьшается или увеличивается на ширину курсора мыши (увеличенную на 5 пикселов для обеспечения зазора при отображении). Аналогично, если пользователь нажимает клавиши перемещения курсора вверх или вниз, содержимое переменной yMouse, соовтетственно, увеличивается или уменьшается на величину высоты курсора мыши. Вы, разумеется, можете выбрать для своего приложения иной шаг изменения позиции курсора мыши.
Когда пользователь нажимает клавиши <Home> или <End>, курсор мыши устанавливается, соответственно, в верхний левый или правый нижний угол окна. Все остальные коды виртуальных клавиш фильтруются.
После изменения переменных xMouse и yMouse обработчик сообщения изменяет содержимое полей x и y структуры ptl таким образом, чтобы новые координаты курсора мыши не выходили за границы окна Client Window , размеры которого были определены при обработке сообщения WM_SIZE :
ptl.x = max(min(xMouse, cxClient - 1), 0); ptl.y = max(min(yMouse, cyClient - 1), 0);
Затем полученные таким образом оконные координаты преобразуются в экранные и используются для установки новой позиции курсора мыши.
Функция DrawMousePtr, определенная в нашем приложении, получает с помощью функции WinQuerySysPointer идентификатор встроенного курсора мыши или встроенной пиктограммы, а затем рисует соответствующее изображение при помощи функции WinDrawPointer. Подробнее функции рисования мы рассмотрим позже в одной из следующих книг "Библиотеки системного программиста" в главе, посвященной графическому интерфейсу Presentation Manager.
Файл pointer.h (листинг 6.6) содержит определение константы ID_APP_FRAMEWND, а также прототип функции DrawMousePtr.
Листинг 6.6. Файл pointer\pointer.h
#define ID_APP_FRAMEWND 1 void DrawMousePtr(HPS hps, LONG x, LONG y, LONG lPtrId);
В файле описания ресуросв приложения pointer.rc (листинг 6.7) определена пиктограмма с идентификатором ID_APP_FRAMEWND.
Листинг 6.7. Файл pointer\pointer.rc
#include <os2.h> #include "pointer.h" ICON ID_APP_FRAMEWND POINTER.ICO
Файл определения модуля приложения pointer.def представлен в листинге 6.8.
Листинг 6.8. Файл pointer\pointer.def
NAME POINTER WINDOWAPI DESCRIPTION 'Pointer Application (C) Frolov A., 1996' HEAPSIZE 4096 STACKSIZE 32768 EXPORTS WndProc