5. Клавиатура

В этой главе мы расскажем о том, как приложения 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, поэтому в нашей книге мы не будем их рассматривать.

5.1. Сообщение WM_CHAR

Вместе с параметрами сообщения 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 Аналогично предыдущему

Макрокоманда CHARMSG

Для извлечения отдельных компонент сообщения 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;

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