Приложение 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); }