Программирование для IBM OS/2© Александр Фролов, Григорий ФроловТом 25, М.: Диалог-МИФИ, 1993, 286 стр. 5.2. Приложение KBDMSGИзучение параметров сообщения WM_CHAR удобно выполнять с помощью приложения KBDMSG, которое отображает эти параметры в своем окне (рис. 5.1).
Рис. 5.1. Просмотр клавиатурных сообщений в окне приложения KBDMSG В столбце VKEY отображается виртуальный код нажатой клавиши, в столбце SCAN - аппаратный скан-код, в столбце FS - флаги. Далее в столбце REPT отображается счетчик повторений и в столбце CHAR - символ (только для символьных клавиш). Когда вы нажимаете очередную клавишу, текущее содержимое главного окна приложения сдвигается вверх и на экране появляется строка, соответствующая сообщению от нажатой клавиши. После отжимания клавиши появляется еще одна строка. Исходные тексты приложения приведены в листинге 5.1. Листинг 5.1. Файл kbdmsg\kbdmsg.c // ================================================= // Определения // ================================================= #define INCL_WIN #define INCL_GPI #define INCL_WINDIALOGS #include <os2.h> #include <stdio.h> #include <string.h> #include "kbdmsg.h" // Прототип функции окна приложения MRESULT EXPENTRY WndProc(HWND, ULONG, MPARAM, MPARAM); // ================================================= // Глобальные переменные // ================================================= HAB hab; HWND hWndFrame; HWND hWndClient; CHAR szAppTitle[] = "Keyboard Messages"; // Размеры окна Client Window SHORT cxClient; SHORT cyClient; // Размеры символов выбранного шрифта SHORT cxChar, cyChar, cyDesc; // Заголовок столбцов CHAR szTitle[] = "VKEY SCAN FS REPT CHAR"; // ================================================= // Главная функция приложения 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[] = "KBDMSG"; 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) { CHAR szMsg[100]; int nMsgSize; HPS hps; POINTL ptl; RECTL rec; CHRMSG cm; FONTMETRICS fm; switch (msg) { // При создании главного окна приложения // определяем и сохраняем метрики шрифта // с фиксированной шириной символов case WM_CREATE : { // Получаем пространство отображения hps = WinGetPS (hWnd); // Выбираем в пространство отображения шрифт // с фиксированной шириной символов SetCourierFont(hps); // Определяем метрики шрифта GpiQueryFontMetrics(hps, (LONG)sizeof(fm), &fm); cxChar = fm.lAveCharWidth; cyChar = fm.lMaxBaselineExt; cyDesc = fm.lMaxDescender; // Устанавливаем шрифт, выбранный в пространство // отображения по умолчанию ResetFont(hps); // Возвращаем пространство отображения WinReleasePS (hps); return FALSE; } // Во время перерисовки стираем содержимое // окна и выводим строку заголовка таблицы case WM_PAINT : { // Получаем пространство отображения hps = WinBeginPaint (hWnd, NULLHANDLE, &rec); // Закрашиваем область, требующую обновление WinFillRect (hps, &rec, CLR_WHITE); // Выбираем в пространство отображения шрифт // с фиксированной шириной символов SetCourierFont(hps); // Рисуем заголовок таблицы в нижней части окна ptl.x = cxChar; ptl.y = cyDesc + cyChar; GpiCharString At (hps, &ptl, strlen(szTitle), szTitle); // Устанавливаем шрифт, выбранный в пространство // отображения по умолчанию ResetFont(hps); // Возвращаем пространство отображения WinEndPaint (hps); return 0; } case WM_ERASEBACKGROUND : return(MRFROMLONG(1L)); // При изменении размеров окна сохраняем новые // размеры и перерисовываем окно case WM_SIZE : { cxClient = SHORT1FROMMP (mp2); cyClient = SHORT2FROMMP (mp2); // Все окно требует перерисовки WinInvalidateRect (hWnd, NULL, TRUE); return 0; } // Это сообщение появляется, когда пользователь // нажимает или отжимает клавишу case WM_CHAR : { // Получаем пространство отображения hps = WinGetPS (hWnd); // Выбираем в пространство отображения шрифт // с фиксированной шириной символов SetCourierFont(hps); // Выделяем параметры клавиатурного сообщения // и сохраняем их в структуре cm cm.chr = CHARMSG(&msg) ->chr; cm.vkey = CHARMSG(&msg) ->vkey; cm.scancode = CHARMSG(&msg) ->scancode; cm.cRepeat = CHARMSG(&msg) ->cRepeat; cm.fs = CHARMSG(&msg) ->fs; // Готовим строку для отображения параметров // клавиатурного сообщения sprintf (szMsg, "%04x %04x %04x %d %c", cm.vkey, cm.scancode, cm.fs, cm.cRepeat, cm.fs & KC_CHAR ? cm.chr : ' '); // Отображаем строку над заголовком таблицы // в нижней части окна ptl.x = cxChar; ptl.y = 2 * cyChar + cyDesc; nMsgSize = strlen(szMsg); GpiCharString At (hps, &ptl, nMsgSize, szMsg); // Устанавливаем шрифт, выбранный в пространство // отображения по умолчанию ResetFont(hps); // Возвращаем пространство отображения WinReleasePS (hps); // Устанавливаем границы сдвигаемой области окна WinSetRect (hab, &rec, 0, 2 * cyChar, cxClient, cyClient); // Выполняем сдвиг верхней части окна WinScrollWindow (hWnd, 0, cyChar + cyDesc, &rec, NULL, NULLHANDLE, NULL, SW_INVALIDATERGN ); return 0; } default: return(WinDefWindowProc (hWnd, msg, mp1, mp2)); } } // ================================================= // Выбор шрифта с фиксированной шириной символов // ================================================= void SetCourierFont(HPS hps) { FATTRS fat; // Заполняем структуру описанием нужного // нам шрифта // Размер структуры fat.usRecordLength = sizeof(FATTRS); // Название шрифта strcpy(fat.szFacename ,"Courier"); // Используем нормальный шрифт без выделений // наклоном, подчеркиванием и т. п. fat.fsSelection = 0; // Указываем, что система Presentation Manager должна // подобрать шрифт, подходящий к нашему описанию fat.lMatch = 0L; // Регистрационный номер, должен быть равен 0 fat.idRegistry = 0; // Кодовая страница fat.usCodePage = 850; // Высота шрифта fat.lMaxBaselineExt = 12L; // Ширина шрифта fat.lAveCharWidth = 12L; // Тип шрифта - обычный без использования кернинга, // двухбайтовых символов и т. д. fat.fsType = 0; // Использование шрифта - шрифт, который отображается // без смешивания с графикой fat.fsFontUse = FATTR_FONTUSE_NOMIX; // Создаем логический шрифт, имеющий идентификатор 1L GpiCreateLogFont(hps, NULL, 1L, &fat); // Выбираем созданный шрифт в пространство // отображения GpiSetCharSet (hps, 1L); } // ================================================= // Установка шрифта, выбранного в пространство // отображения по умолчанию // ================================================= void ResetFont(HPS hps) { // Выбираем шрифт по умолчанию GpiSetCharSet (hps, LCID_DEFAULT); // Удаляем созданный ранее шрифт // с идентификатором 1L GpiDeleteSetId(hps, 1L); } Глобальные переменныеВ глобальных переменных cxClient и cyClient хранятся размеры окна Client Window (соответственно, ширина и высота). Эти размеры определяются в момент обработки сообщения WM_SIZE и используются при сдвиге (свертке) содержимого окна. В переменные cxChar, cyChar и cyDesc записываются размеры символов для выбранного нами шрифта с фиксированной шириной символов. Подробно о метриках шрифта вы узнаете позже в одной из следующих книг "Библиотеки системного программиста", а пока можете считать, что в переменных cxChar и cyChar хранятся, соответственно, ширина и высота символов, а в переменной cyDesc - размер выступающей части символов (например, размер хвостика у буквы 'у'). В массиве szTitle записана текстовая строка, которая используется для заголовка таблицы и отображается в нижней части главного окна приложения. Функция mainФункция main не имеет никаких особенностей. Она создает главное окно приложения и очередь сообщений, а затем запускает цикл обработки сообщений. Функция WndProcЭта функция выполняет всю полезную работу в нашем приложении. Рассмотрим обработчики отдельных сообщений. Сообщение WM_CREATEЗадачей обработчика сообщения WM_CREATE является определение метрик шрифта с фиксированной шириной букв, удобного для отображения таблицы с параметрами сообщения WM_CHAR . Так как перед определением метрик шрифта последний необходимо выбрать в пространство отображения, первое, что делает обработчик сообщения WM_CREATE - это получает идентификатор пространства отображения при помощи функции WinGetPS . Напомним, что обработчики любых сообщений, кроме сообщения WM_PAINT , должны получать этот идентификатор с помощью функции WinGetPS. Далее обработчик сообщения выбирает в полученное пространство отображения шрифт с фиксированной шириной символов. Для этого он вызывает функцию SetCourierFont, определенную в нашем приложении. После этого с помощью функции GpiQueryFontMetrics обработчик сообщения WM_CREATE записывает метрики выбранного шрифта в структуру fm типа FONTMETRICS. Детальное описание этой структуры мы отложим до главы, посвященной шрифтам в Presentation Manager. Скажем только, что поля lAveCharWidth, lMaxBaselineExt и lMaxDescender структуры FONTMETRICS после возвращения из функции GpiQueryFontMetrics будут содержать, соответственно, ширину, высоту и размер выступающей части символов. После определения и сохранения необходимых нам метрик шрифта обработчик выбирает в контекст отобаржения тот шрифт, который используется по умолчанию. Для этого он вызывает функцию ResetFont, определенную в нашем приложении. Перед возвращением управления обработчик сообщения WM_CREATE освобождает пространство отображения, вызывая функцию WinReleasePS . Сообщение WM_PAINTСообщение WM_PAINT поступает в функцию окна после его создания, а также тогда, когда пользователь изменяет размеры окна (в последнем случае посылку этого сообщения инициирует обработчик сообщения WM_SIZE ). Получив пространство отображения, обработчик сообщения WM_PAINT закрашивает область, требующую обновления, выбирает шрифт с фиксированной шириной букв и затем рисует заголовок таблицы в нижней части главного окна приложения. После этого он восстанавливает шрифт и освобождает пространство отображения. Сообщение WM_ERASEBACKGROUNDЭто сообщение обрабатывается как обычно. В ответ на него функция окна возвращает значение 1L, позволяя при необхоимости окну Frame Window стереть содержимое окна Client Window . Сообщение WM_SIZEПри обработке сообщения WM_SIZE функция окна определяет размеры окна, сохраняя их в переменных cxClient и cyClient, а затем инициирует перерисовку окна. С этой целью вызывается функция WinInvalidateRect , объявляющая все окно требующим перерисовки. В результате функция окна получит сообщение WM_PAINT . Сообщение WM_CHARОбработчик сообщения WM_CHAR получает пространство отображения и выбирает в него шрифт с фиксированной шириной символов, вызывая функцию SetCourierFont. Далее при помощи макрокоманды CHARMSG обработчик разбирает параметры сообщения WM_CHAR и записывает их в соответствующие поля структуры cm типа CHRMSG. Из параметров сообщения WM_CHAR формируется текстовая строка szMsg, которая затем отображается в нижней части окна приложения над заголовком таблицы. При формировании текстовой строки проверяется флаг KC_CHAR. Если сообщение соответствует символьной клавише, в строку записывается код соответствующего символа, в противном случае - код символа пробела. После отображения отформированной текстовой строки обработчик сообщения WM_CHAR восстанавливает шрифт и возвращает пространство отображения. Затем содержимое всего окна за исключением строки заголовка сдвигается вверх при помощи функции WinScrollWindow . Прототип этой функции приведен ниже: LONG WinScrollWindow ( HWND hwnd, // идентификатор окна LONG lDx, // величина сдвига вправо LONG lDy, // величина сдвига вверх PRECTL prclScroll, // область сдвига PRECTL prclClip, // область ограничения HRGN hrgnUpdateRgn, // область обновления PRECTL prclUpdate, // прямоугольная область обновления ULONG flOptions); // параметры сдвига В нашем приложении эта функция используется сделующим образом: WinScrollWindow (hWnd, 0, cyChar + cyDesc, &rec, NULL, NULLHANDLE, NULL, SW_INVALIDATERGN ); Окно hWnd сдвигается вверх на величину высоты символов (с учетом размера выступающей части символов). Параметр SW_INVALIDATERGN указывает, что сдвинутая область должна быть обновлена, для чего функции окна будет передано сообщение WM_PAINT . Область свертки (т. е. область, в которой будет выполняться сдвиг), определяется содержимым полей структуры rec, для заполнения которой мы использовали функцию WinSetRect : WinSetRect (hab, &rec, 0, 2 * cyChar, cxClient, cyClient); Прототип функции WinSetRect приведен ниже: BOOL WinSetRect ( HAB hab, // идентификатор блока Anchor-block PRECTL prclrect, // адрес структуры RECTL LONG lLeft, // левый край LONG lBottom, // нижний край LONG lRight, // правый край LONG lTop); // верхний край Как видно, сдвигается все окно кроме полосы, имеющей двойную высоту символов и расположенной в нижней части окна. В этой полосе отображается заголовок таблицы. Функция SetCourierFontЭта функция выбирает в пространство отображения шрифт с фиксированной шириной символов. Пока мы не будем рассматривать функцию SetCourierFont подробно. Скажем только, что для выбора шрифта его описание в виде набора параметров записывается в поля структуры fat типа FATTRS . Адрес этой структуры затем передается функции GpiCreateLogFont, создающей логический шрифт с идентификатором 1L. Далее созданный шрифт выбирается в пространство отображения при помощи функции GpiSetCharSet . Функция ResetFontФункция ResetFont восстанавливает шрифт после функции SetCourierFont. Для этого она с помощью функции GpiSetCharSet выбирает в пространство отображения шрифт по умолчанию и затем удаляет логический шрифт, созданный в функции SetCourierFont. Файл kbdmsg.hФайл kbdmsg.h (листинг 5.2) содержит определение константы ID_APP_FRAMEWND, а также прототипы функций SetCourierFont и ResetFont. Листинг 5.2. Файл kbdmsg\kbdmsg.h #define ID_APP_FRAMEWND 1 void SetCourierFont(HPS hps); void ResetFont(HPS hps); Файл kbdmsg.rcФайл описания ресурсов приложения KBDMSG с именем kbdmsg.rc представлен в листинге 5.3. Листинг 5.3. Файл kbdmsg\kbdmsg.rc #include <os2.h> #include "kbdmsg.h" ICON ID_APP_FRAMEWND KBDMSG.ICO Файл kbdmsg.defФайл определения модуля приложения kbdmsg.def представлен в листинге 5.4. Листинг 5.4. Файл kbdmsg\kbdmsg.def NAME KBDMSG WINDOWAPI DESCRIPTION 'KbdMsg Application (C) Frolov A., 1996' HEAPSIZE 4096 STACKSIZE 32768 EXPORTS WndProc |