В этой главе мы расскажем о том, как приложения Presentation Manager работают с клавиатурой. Как и следовало ожидать, методика работы с клавиатурой в прилоениях Presentation Manager полностью отличается от той, что используется в программах MS-DOS .
Напомним, что когда пользователь нажимает клавишу, генерируется аппаратное прерывание. В операционной системе MS-DOS обработчик этого прерывания записывал скан-код нажатой клавиши в специальный клавиатурный буфер, расположенный в области данных BIOS . Когда программе MS-DOS нужно было ввести код нажатой клавиши, она (прямо или косвенно) обращалась к прерыванию INT 16h , которое возвращало расширенный ASCII-код нажатой клавиши. При необходимости программа переходила в режим ожидания и находилась в нем до тех пор, пока пользователь не нажимал какую-нибудь клавишу. Способы работы с клавиатурой в среде MS-DOS мы подробно рассмотрели во втором томе "Библиотеки системного программиста", который называется "Аппаратное обеспечение IBM PC".
В среде операционной системы IBM OS/2 одновременно может работать несколько приложений, которые при этом должны пользоваться только одной клавиатурой. Очевидно, что в такой ситуации никакое приложение не может блокировать работу всей системы, ожидая ввод с клавиатуры.
Для работы с клавиатурой в приложениях Presentation Manager используется механизм сообщений. Каждый раз когда пользователь нажимает или отпускает любую клавишу, система Presentation Manager генерирует сообщение с кодом WM_CHAR . Параметры этого сообщения несут всю необходимую информацию о нажатой (или отпущенной) клавише.
В какое окно попадают клавиатурные сообщения?
В то, которое имеет фокус ввода. Если пользователь делает одно из приложений активным, выдвигая его на передний план, функция окна этого приложения будет получать клавиатурные сообщения. С помощью функции WinSetFocus приложение может передать фокус ввода любому окну. При этом сообщения от клавиатуры будут поступать в функцию этого окна.
Заметим, что в среде операционной системы IBM OS/2 могут работать приложения двух типов - приложения Presentation Manager и приложения текстового режима, напоминающие программы MS-DOS . Последние работают с клавиатурой при помощи набора функций, имеющих префикс имени Kbd, например, KbdCharIn . По своим возможностям эти функции недалеко ушли от прерывания INT 16h. Такие функции нельзя использовать в приложениях Presentation Manager, поэтому в нашей книге мы не будем их рассматривать.
Вместе с параметрами сообщения WM_CHAR передается такая информация, как скан-код клавиши, код символа (для символьных клавиш), виртуальный код клавиши и слово флагов.
Аппаратный скан-код содержится в битах 24 - 31 параметра mp1 и имеет 8 разрядов. Приложения редко используют этот код, так как он аппаратно-зависимый. Больший интерес представляет виртуальный код клавиши и код символа.
Тем не менее, при необходимости вы можете извлечь скан-код из параметра mp1 при помощи макрокоманды CHAR4FROMMP, например, так:
nScan = CHAR4FROMMP(mp1);
Виртуальный код нажатой клавиши имеет 16
разрядов и передается в битах 16 - 31 параметра mp2.
Для обычных символьных клавиш виртуальных код
равен нулю. Функциональные клавиши и клавиши
серого цвета имеют виртуальные коды, приведенные
ниже:
Клавиша |
Виртуальный код |
Значение виртуального кода |
Левая клавиша мыши |
VK_BUTTON1 |
0x01 |
Правая клавиша мыши |
VK_BUTTON2 |
0x02 |
Средняя клавиша мыши |
VK_BUTTON3 |
0x03 |
<Break> |
VK_BREAK |
0x04 |
Забой |
VK_BACKSPACE |
0x05 |
Табуляция <Tab> |
VK_TAB |
0x06 |
Обратная табуляция |
VK_BACKTAB |
0x07 |
Переход на новую строку, клавиша <Enter>
основной клавиатуры |
VK_NEWLINE |
0x08 |
Левая или правая клавиша сдвига <Shift> |
VK_SHIFT |
0x09 |
Левая или правая клавиша <Control> |
VK_CTRL |
0x0A |
Левая клавиша <Alt> |
VK_ALT |
0x0B |
Правая клавиша <Alt> |
VK_ALTGRAF |
0x0C |
<Pause> |
VK_PAUSE |
0x0D |
<Caps Lock> |
VK_CAPSLOCK |
0x0E |
<Esc> |
VK_ESC |
0x0F |
Пробел |
VK_SPACE |
0x10 |
Переход на следующую страницу <PgUp> |
VK_PAGEUP |
0x11 |
Переход на предыдущую страницу <PgUp> |
VK_PAGEDOWN |
0x12 |
<End> |
VK_END |
0x13 |
<Home> |
VK_HOME |
0x14 |
Перемещение курсора влево <Left> |
VK_LEFT |
0x15 |
Перемещение курсора вверх <Up> |
VK_UP |
0x16 |
Перемещение курсора вправо <Right> |
VK_RIGHT |
0x17 |
Перемещение курсора вниз <Down> |
VK_DOWN |
0x18 |
Печать экрана |
VK_PRINTSCRN |
0x19 |
Вставка <Insert> |
VK_INSERT |
0x1A |
Удаление <Delete> |
VK_DELETE |
0x1B |
<Scroll Lock> |
VK_SCRLLOCK |
0x1C |
<Num Lock> |
VK_NUMLOCK |
0x1D |
<Enter> на дополнительной (цифровой)
клавиатуре |
VK_ENTER |
0x1E |
Клавиша запроса системы |
VK_SYSRQ |
0x1F |
Функциональная клавиша <F1> |
V K_F1 |
0x20 |
- // - <F2> |
VK_F2 |
0x21 |
- // - <F3> |
VK_F3 |
0x22 |
- // - <F4> |
VK_F4 |
0x23 |
- // - <F5> |
VK_F5 |
0x24 |
- // - <F6> |
VK_F6 |
0x25 |
- // - <F7> |
VK_F7 |
0x26 |
- // - <F8> |
VK_F8 |
0x27 |
- // - <F9> |
VK_F9 |
0x28 |
- // - <F10> |
VK_F10 |
0x29 |
- // - <F11> |
VK_F11 |
0x2A |
- // - <F12> |
VK_F12 |
0x2B |
- // - <F13> |
VK_F13 |
0x2C |
- // - <F14> |
VK_F14 |
0x2D |
- // - <F15> |
VK_F15 |
0x2E |
- // - <F16> |
VK_F16 |
0x2F |
- // - <F17> |
VK_F17 |
0x30 |
- // - <F18> |
VK_F18 |
0x31 |
- // - <F19> |
VK_F19 |
0x32 |
- // - <F20> |
VK_F20 |
0x33 |
- // - <F21> |
VK_F21 |
0x34 |
- // - <F22> |
VK_F22 |
0x35 |
- // - <F23> |
VK_F23 |
0x36 |
- // - <F24> |
VK_F24 |
0x37 |
Завершение перемещения |
VK_ENDDRAG |
0x38 |
Вызов меню |
VK_MENU |
VK_F10 |
Заметьте, что в приведенном выше списке есть коды клавиш, которые отсутствуют на стандартной клавиатуре компьютера, совместимого с IBM PC. В этом нет ничего удивительного, так как в механизм виртуальных кодов клавиш заложена возможность работы и на других платформах.
Виртуальный код клавиши можно получить из параметра mp2 при помощи макрокоманды SHORT2FROMMP :
nVirt = SHORT2FROMMP (mp2);
Код символа, соответствующий нажатой символьной клавише, располагается в битах 0 - 15 параметра mp2 и может быть получен при помощи макрокоманды SHORT1FROMMP :
cChar = SHORT1FROMMP (mp2);
Обычные символьные клавиши могут нажиматься одновременно с клавишей сдвига. Код символа, полученный вместе с сообщением WM_CHAR , учитывает состояние клавиши <Shift> в момент, когда была нажата символьная клавиша.
Заметьте, что клавиша <Shift> сама по себе генерирует сообщение WM_CHAR . Поэтому когда вы нажимаете, а затем отпускаете, например, комбинацию клавиш <Shift + a>, в функцию окна приходит четыре сообщения WM_CHAR. Первое соответствует нажатию клавиши <Shift>, второе - нажатию клавиши <a>. Третье и четвертое соответствуют отпусканию указанных клавиш и приходят в той последоваетльности, в которой отпускаются клавиши.
Одинаковые клавиатурные сообщения могут комбинироваться системой Presentation Manager в одно клавиатурное сообщение (например, когда влключается режим автоповтора). Количество скомбинированных таким образом сообщений записывается в поле счетчика повторений, который располагается в битах 16 - 23 параметра mp1. Его можно извлечь при помощи макрокоманды CHAR3FROMMP :
nRepeatCount = CHAR3FROMMP(mp1);
Младшие 16 бит параметра mp1 сообщения WM_CHAR содержат флаги, отражающие состояние клавиш в момент генерации сообщения.
Для извлечения флагов из параметра mp1 удобно использовать макрокоманду SHORT1FROMMP :
nFs = SHORT1FROMMP (mp1);
Список отдельных флагов вместе с маской для
проверки и кратким описанием мы привели ниже:
Флаг |
Маска |
Описание |
KC_CHAR |
0x0001 |
Поле кода символа содержит правильное
значение |
KC_VIRTUALKEY |
0x0002 |
Поле виртуального кода клавиши содержит
правильное значение |
KC_SCANCODE |
0x0004 |
Поле аппаратного скан-кода клавиши
содержит правильное значение |
KC_SHIFT |
0x0008 |
Была нажата клавиша <Shift> |
KC_CTRL |
0x0010 |
Была нажата клавиша <Control> |
KC_ALT |
0x0020 |
Была нажата клавиша <Alt> |
KC_KEYUP |
0x0040 |
Сообщение было создано, когда
пользователь отпустил нажатую ранее клавишу |
KC_PREVDOWN |
0x0080 |
Ранее эта клавиша находилась в нажатом
состоянии (устанавливается при выполнении
функции автоповтора для клавиши, которую держат
в нажатом состоянии достаточно долгое время) |
KC_LONEKEY |
0x0100 |
Во время ввода комбинации клавиш
отпущена та клавиша, которая была перед этим
нажата |
KC_DEADKEY |
0x0200 |
Нажата клавиша для создания
диактрических символов ("мертвая" клавиша) |
KC_COMPOSITE |
0x0400 |
Композитная клавиша, составленная с
использованием диактрических символов |
KC_INVALIDCOMP |
0x0800 |
Неправильная композитная клавиша |
KC_TOGGLE |
0x1000 |
С помощью этого флага можно использвать
любую клавишу как переключающую |
KC_INVALIDCHAR |
0x2000 |
Неправильный код клавиши |
KC_DBCSRSRVD1 |
0x4000 |
Зарезервировано для двухсимвольных
кодов клавиш |
KC_DBCSRSRVD2 |
0x8000 |
Аналогично предыдущему |
Для извлечения отдельных компонент сообщения WM_CHAR удобно использовать макрокоманду CHARMSG , определенную следующим образом:
#define CHARMSG(pmsg) \ ((PCHRMSG)((PBYTE)pmsg + sizeof(MPARAM) ))
Эта макрокоманда пользуется структурой CHRMSG , показанной ниже:
typedef struct _CHARMSG
{
USHORT fs; // поле флагов
UCHAR cRepeat; // счетчик повторений
UCHAR scancode; // скан-код
USHORT chr; // код символа
USHORT vkey; // виртуальный код клавиши
} CHRMSG;
typedef CHRMSG *PCHRMSG;
Ниже мы показали пример использования этой макрокоманды для сохранения всех параметров сообщения WM_CHAR в структуре cm типа CHRMSG:
CHRMSG 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;
Изучение параметров сообщения 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 не имеет никаких особенностей. Она создает главное окно приложения и очередь сообщений, а затем запускает цикл обработки сообщений.
Эта функция выполняет всю полезную работу в нашем приложении. Рассмотрим обработчики отдельных сообщений.
Задачей обработчика сообщения 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_SIZE ).
Получив пространство отображения, обработчик сообщения WM_PAINT закрашивает область, требующую обновления, выбирает шрифт с фиксированной шириной букв и затем рисует заголовок таблицы в нижней части главного окна приложения. После этого он восстанавливает шрифт и освобождает пространство отображения.
Это сообщение обрабатывается как обычно. В ответ на него функция окна возвращает значение 1L, позволяя при необхоимости окну Frame Window стереть содержимое окна Client Window .
При обработке сообщения WM_SIZE функция окна определяет размеры окна, сохраняя их в переменных cxClient и cyClient, а затем инициирует перерисовку окна. С этой целью вызывается функция WinInvalidateRect , объявляющая все окно требующим перерисовки. В результате функция окна получит сообщение WM_PAINT .
Обработчик сообщения 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 подробно. Скажем только, что для выбора шрифта его описание в виде набора параметров записывается в поля структуры fat типа FATTRS . Адрес этой структуры затем передается функции GpiCreateLogFont, создающей логический шрифт с идентификатором 1L.
Далее созданный шрифт выбирается в пространство отображения при помощи функции GpiSetCharSet .
Функция ResetFont восстанавливает шрифт после функции SetCourierFont. Для этого она с помощью функции GpiSetCharSet выбирает в пространство отображения шрифт по умолчанию и затем удаляет логический шрифт, созданный в функции SetCourierFont.
Файл 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 с именем kbdmsg.rc представлен в листинге 5.3.
Листинг 5.3. Файл kbdmsg\kbdmsg.rc
#include <os2.h> #include "kbdmsg.h" ICON ID_APP_FRAMEWND KBDMSG.ICO
Файл определения модуля приложения 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