95 в двух томах Том I



Pdf просмотр
страница20/41
Дата27.11.2016
Размер4.32 Mb.
Просмотров7752
Скачиваний0
ТипРеферат
1   ...   16   17   18   19   20   21   22   23   ...   41
и заставляют каретку переместиться в начало или конец строки, клавиши и
— к верхней или нижней границе окна. Клавиши стрелок работают так, как вы и ожидаете. При нажатии клавиши программа TYPER должна переместить все, что находится в буфере, начиная от следующей позиции каретки и до конца строки, а затем вывести на экран в конец строки символ пробела.
Обработчик сообщения WM_CHAR включает в себя обработку клавиш , ,
(+), , и символьных клавиш. Отметьте, что поле Repeat Count параметра lParam использовано при обработке сообщения WM_CHAR (здесь предполагается, что важен каждый вводимый пользователем символ), а не при обработке сообщения WM_KEYDOWN (чтобы предотвратить нечаянное двойное нажатие). Обработка нажатий и отчасти упрощена путем использования функции SendMessage.
Логика обработки клавиши заменяется логикой обработки , а клавише ставится в соответствие несколько пробелов.
Как уже упоминалось, во время рисования в окне при обработке отличного от WM_PAINT сообщения, вы должны сделать каретку невидимой. В программе это делается при обработке сообщения WM_KEYDOWN для клавиши
и сообщения WM_CHAR для символьных клавиш. В обоих этих случаях, в программе TYPER меняется содержимое буфера, а затем в окне рисуется новый символ или символы.
Программу TYPER можно использовать при работе над выступлениями, что и показано на рис. 5.6.
Рис. 5.6 Вид экрана программы TYPER
Наборы символов Windows
Уже упоминалось, что буквенные клавиши, если их нажатию предшествует нажатие клавиши немого символа, вырабатывают сообщения WM_CHAR, где параметр wParam является кодом ASCII для символа с диакритическим знаком. Это может вызвать легкое замешательство, поскольку в наборе кодов ASCII отсутствуют какие бы то ни было символы с диакритическими знаками. Так что же на самом деле представляет собой параметр wParam? Ответ

197 на этот вопрос требует, чтобы мы разобрались с наборами символов, что, как вначале может показаться, больше относится к шрифтам. Однако, эта тема также жизненно важна и для обработки клавиатуры.
Стандартный 7-битный набор символов ASCII определяет коды от 0 до 31 (0x1F) и 127 (0x7F) как управляющие символы, а также коды от 32 (0x20) до 126 (0x7E) как символы, которые могут быть выведены на экран. Здесь нет ни одного символа с диакритическим знаком. Поскольку в персональных компьютерах используются байты, состоящие из 8 битов, то производители компьютеров часто определяют наборы символов, использующие 256 кодов вместо 128 кодов ASCII. Дополнительные коды могут назначаться символам с диакритическими знаками. В итоге получается "расширенный набор символов" (extended character set), который включает в себя набор символов
ASCII и до 128 других символов.
Если бы в Windows поддерживался такой расширенный набор символов, выводить на экран символы с диакритическими знаками было бы просто. Но в Windows обычный расширенный набор символов не поддерживается. В Windows поддерживается два расширенных набора символов. К несчастью, наличие двух наборов символов не делает их использование вдвое проще.
Набор символов OEM
Для начала давайте обратимся к аппаратуре, на которой работает Windows — к персональным компьютерам IBM и совместимым с ними. В начале 80-х годов производители IBM PC решили расширить набор символов ASCII так, как показано на рис. 5.7. Коды от 0x20 до 0x7E — это выводимые на дисплей символы из набора символов ASCII.
Оставшиеся являются нестандартными или, по крайней мере, тогда являлись нестандартными.
Этот набор символов не может игнорироваться. Он закодирован в миллионах микросхем ПЗУ в видеоадаптерах, принтерах и микросхемах BIOS. Он был растиражирован в аппаратуре многочисленных производителей IBM- совместимых компьютеров и периферии. Этот набор символов стал частью того, что обозначается фразой "стандарт
IBM". Для множества программ, работающих в текстовом режиме и написанных не для Windows, требуется этот расширенный набор символов, поскольку в них для вывода информации на экран используются символы псевдографики — символы блоков и линий (коды от B0H до DFH).
Здесь есть только одна проблема: расширенный набор символов IBM не предназначен для Windows. Во-первых, символы псевдографики, которые обычно используются в программах персональных компьютеров для приложений, работающих в текстовом режиме, в Windows не нужны, поскольку Windows работает с настоящей графикой. Если вы хотите нарисовать в Windows горизонтальную линию, то гораздо легче нарисовать эту линию, а не выводить на экран строку символов с кодом 0xC4. Во-вторых, греческий алфавит и математические символы менее важны для Windows, чем буквы с символами ударения, которые используются в большинстве европейских языков. Программы, для которых нужен вывод на экран математических символов, гораздо лучше их рисуют с помощью графических функций.
Короче говоря, Windows поддерживает набор символов IBM, но им придается второстепенное значение — в основном он используется в старых приложениях, работающих в окне. Приложения Windows обычно не используют набор символов IBM. В документации по Windows набор символов IBM упоминается как "набор символов OEM" (OEM character set). Набор символов OEM более точно определяется как набор символов национального алфавита для машины, работающей под Windows.
Поддержка языков различных стран в DOS
Имеется несколько вариантов набора символов IBM, которые называются "кодовые страницы" (code pages).
Вариант, используемый в Соединенных Штатах и большинстве европейских стран, называется Code Page 437. В системах, продаваемых в Норвегии, Дании, Португалии и некоторых других странах Европы, используются другие специальные кодовые страницы, в которых содержится больше специальных символов, необходимых для языков этих стран. Недавно некоторые из этих стран начали использовать Code Page 850, в которой содержится меньше графических символов, а больше букв со значками типа знаков ударения и других специальных символов.
Windows поддерживает кодовые страницы, устанавливая шрифты OEM (которые используются работающими в окне приложениями DOS и в программе просмотра буфера обмена), которые соответствуют кодовой странице системы, и устанавливая соответствующие таблицы преобразования для функций CharToOem и OemToChar (о которых будет рассказано позднее).
Программа установки Windows Setup выберет нужные кодовые страницы на основе региональных установок текущей конфигурации системы.

198
0 1 2 3 4 5 6 7

8 9 A B

C D

E

F
00:
10:
20:
30:
40:
50:
60:
70:
80:
90:
A0:
B 0:
C 0:
D 0:
E0:
F0:
Рис. 5.7 Расширенный набор символов IBM, упорядоченный по возрастанию значений кода символов
Набор символов ANSI
Расширенный набор символов, который Windows и программы для Windows в большинстве случаев используют, называется "набор символов ANSI" (ANSI character set), но фактически он является стандартом ISO.
0

1 2

3

4 5

6

7

8

9

A

B

C

D

E

F
00:
10
:
20:
30:
40:
50:
60:
70:
80:
90:
A0:
B 0:
C 0:
D 0:
E0:
F0:
Рис. 5.8 Набор символов ANSI, упорядоченный по возрастанию значений кода символов
Когда ваша программа получает сообщение WM_CHAR, параметр wParam содержит символьный код ANSI. Набор символов ANSI показан на рис. 5.8. Как вы можете видеть, коды от 0x20 до 0x7E представляют собой те же самые символы, которые имеются в наборе символов OEM и наборе символов ASCII. Символы, показанные в виде закрашенных прямоугольников, не определены. Они могут оказаться иными на другом устройстве вывода информации (например, на принтере). Шрифты TrueType определяют для кодов ANSI несколько дополнительных символов в диапазоне от 0x80 до 0x9F.
Наборы символов OEM, ANSI и шрифты
В Windows имеются различные шрифты для вывода на экран символов из набора ANSI и OEM. Когда вы первый раз получаете описатель контекста устройства, то одним из атрибутов контекста устройства является шрифт. По

199 умолчанию им является SYSTEM_FONT или "системный шрифт" (system font), в котором используется набор символов ANSI. Если вы хотите выводить на экран символы из набора OEM, то вы можете выбрать
OEM_FIXED_FONT (также называемый "терминальный шрифт" (terminal font) в контекст устройства, используя следующий оператор:
SelectObject(hdc, GetStockObject(OEM_FIXED_FONT));
Международные интересы
Здесь рассказывается о том, почему в середине главы, посвященной клавиатуре, нам приходится говорить о шрифтах. Мы установили, что когда пользователь Windows набирает на неамериканской клавиатуре символ с диакритическим знаком, то параметром wParam сообщения WM_CHAR является код этого символа из набора символов ANSI.
Поэтому, если вам необходимо получить на экране отображение этого символа, то вам было бы лучше пользоваться шрифтом из набора символов ANSI (таким как SYSTEM_FONT или SYSTEM_FIXED_FONT). Если вы вместо этого используете OEM_FIXED_FONT, то символ, который вы выводите на экран, окажется неправильным, и пользователь будет неприятно удивлен. Несколько других простых правил позволят сохранить логику работы с клавиатурой в вашей программе для Windows при адаптации к рынкам Европы.
Работа с набором символов
Если вы получаете сообщение WM_CHAR, то запомните, что значение параметра wParam вполне реально может оказаться больше, чем 128. И это не ошибка. Не думайте, что все, что больше 127 — это неправильные символы.
Вам может понадобиться преобразовать регистр символа, т. е. сделать из строчной буквы — прописную. Не используйте ваш собственный алгоритм: if(ch >= 'a' && ch <= 'z') ch -= 32;
// ОШИБКА!!!
Это плохой стиль даже для программ, написанных не для Windows. Но однако нельзя пользоваться и стандартной функцией С: ch = toupper(ch);
//
ОШИБКА
!!!
Обе эти функции работают только с нижней половиной набора символов ANSI. Они не преобразуют 0xE0 в 0xC0.
Вместо них вам следует пользоваться функциями CharUpper и CharLower Windows. Если pString — это оканчивающаяся нулевым символом строка, то вы можете преобразовать ее в верхний регистр с помощью функции CharUpper

:
CharUpper(pString);
Для строки, которая не оканчивается нулевым символом, нужно использовать функцию CharUpperBuff

:
CharUpperBuff(pString, nLength);
Для преобразования одного символа также можно пользоваться функцией CharUpper, но требуется некоторая поправка, поскольку старшее слово параметра должно быть равно 0: ch = CharUpper((PSTR)(LONG)(BYTE)ch);
Если ch определяется как беззнаковый символ, то преобразование типа BYTE не требуется. Кроме вышеперечисленных, в Windows используются функции CharLower и CharLowerBuff для преобразования прописных букв в строчные.
Если вы действительно серьезно намерены писать программы для Windows, которые можно было бы приспосабливать к иностранным языкам, вы должны также изучить функции CharNext и CharPrev. Эти функции облегчают работу с многобайтными наборами символов, часто используемых в странах Дальнего Востока. Для этих наборов символов требуется больше 256 символов, некоторые из которых задаются двумя байтами. Если вы используете обычную арифметику указателей С для просмотра строки (например, при поиске символа обратной косой черты в строке пути, содержащем каталоги), то можете решить, что нашли нужный символ, хотя фактически вы нашли второй байт двухбайтного символьного кода. Функциям CharNext и CharPrev передается дальний указатель на символьную строку и они возвращают дальний указатель, который необходимым образом увеличен или уменьшен с учетом последних двухбайтных символьных кодов.
Связь с MS-DOS
Если бы Windows была только одной операционной оболочкой, работающей на машине, то вы могли бы забыть о наборе символов OEM и работать только с набором символов ANSI. Однако пользователи могут создавать файлы в

200 среде MS-DOS, а использовать их в Windows; они также могут создавать файлы в Windows, а использовать в MS-
DOS. К сожалению, в MS-DOS используется набор символов OEM.
Вот пример одной из проблем, которые могут встретиться. Предположим, что говорящий на немецком языке пользователь персонального компьютера создает в MS-DOS файл ÜBUNGEN.TXT "практические упражнения" в программе EDLIN. Для IBM PC буква Ü — это часть набора символов IBM (т. е. OEM) и ее код 154 или 0х9А. (При использовании MS-DOS с американской клавиатурой на IBM PC, вы можете набрать эту букву, напечатав
+154 на числовой клавиатуре.) MS-DOS использует этот код символа в записи каталога, соответствующей этому файлу.
Если программа для Windows использует вызовы функций MS-DOS для получения каталога файлов и вывода их имен затем прямо на экран с использованием шрифта, содержащего символы из набора символов ANSI, то первая буква ÜBUNGEN.TXT будет изображена в виде закрашенного прямоугольника, поскольку код 154 — это один из неопределенных символов набора символов ANSI. Программе для Windows необходимо преобразовать код 154
(или 0х9А) из расширенного набора символов IBM в код символа 220 (или 0хDС) из набора символов ANSI, который представляет из себя букву Ü. Эти задачи для вас решает функция Windows OemToChar. Она получает в качестве параметров два дальних указателя на строки. Символы OEM в первой строке преобразуются в символы
ANSI и сохраняются во второй строке:
OemToChar(lpszOemStr, lpszAnsiStr);
Теперь рассмотрим противоположный пример. Пользователь, говорящий по-немецки, хочет воспользоваться вашей программой, написанной для Windows, для создания файла ÜBUNGEN.TXT. В имени файла, введенном пользователем, первая буква имеет код 220 (или 0хDС). Если вы используете для открытия этого файла вызов функции MS-DOS, то MS-DOS использует этот символ в имени файла. Если потом пользователь, находясь в MS-
DOS, посмотрит на этот файл, то первый символ будет выглядеть как прямоугольник. Перед тем как использовать вызов функции MS-DOS, вы должны преобразовать имя файла в набор символов OEM:
CharToOem(lpszAnsiStr, lpszOemStr);
Этот вызов преобразует код 220 (или 0хDС) в код 154 (или 0х9А). Windows также содержит две функции
CharToOemBuff и OemToCharBuff, для которых символ ноль в конце строки не требуется.
Кроме этих функций для подобных преобразований в Windows имеется функция OpenFile. Если вы используете эту функцию, то вам не нужно преобразование с помощью функции CharToOem. Если вы используете вызовы функций MS-DOS для получения списка имен файлов (как это делает в Windows программа File Manager), то эти имена файлов, перед их выводом на экран, следует передать в функцию OemToChar.
Преобразование содержимого файлов является еще одной проблемой, возникающей, когда файлы используются и в Windows и в MS-DOS. Если в вашей программе для Windows используются файлы, которые, как вы уверены, были созданы в программе для MS-DOS, тогда вам может понадобиться обработать текстовое содержимое этих файлов с помощью функции OemToChar. Аналогично, если в программе для Windows подготовлен файл для использования в программе MS-DOS, то для преобразования текста вам может понадобиться функция CharToOem.
Функции OemToChar и CharToOem реализованы в драйвере клавиатуры. В них включены очень простые таблицы.
Программа функции OemToChar преобразует код OEM от 0х80 до 0xFF в код символа из набора ANSI, который больше всего похож на соответствующий символ OEM. В некоторых случаях, это преобразование является лишь очень грубым приближением. Например, большинство символов псевдографики в наборе символов IBM преобразуется в знаки плюсов, тире и вертикальных линий. Большинство кодов OEM от 0х00 до 0x1F не преобразуются в коды ANSI.
Функция CharToOem преобразует коды ANSI от 0хА0 до 0xFF в коды из набора символов OEM. Символы со знаками типа ударения в наборе символов ANSI, которых нет в наборе символов OEM, преобразуются в коды обычных, не имеющих диакритических знаков, символов ASCII.
Использование цифровой клавиатуры
Как вы, вероятно, знаете, клавиатура IBM PC и BIOS позволяет вам вводить коды расширенного набора символов
IBM посредством нажатия клавиши и набора десятичного кода из трех цифр, представляющего собой код символа OEM, на цифровой клавиатуре. Эта возможность воспроизводится в Windows двумя способами.
Во-первых, когда вы вводите -[код OEM] на цифровой клавиатуре, то Windows выдает вам код того символа
ANSI (в параметре wParam сообщения WM_CHAR), который имеет наибольшее сходство с соответствующим символом OEM, представленным кодом OEM. Вернее, Windows перед тем как выработать сообщение WM_CHAR, обрабатывает код с помощью функции OemToChar. Эта возможность очень удобна для пользователя: если у вас нет иноязычной клавиатуры, и вы привыкли печатать Ü с помощью +154, то вы можете делать то же самое и в программе для Windows. Вам не нужно переучиваться на коды символов ANSI.

201
Во-вторых, если вам нужно генерировать коды расширенного набора символов ANSI с помощью американской клавиатуры, наберите -0[код OEM] на числовой клавиатуре. Параметр wParam сообщения WM_CHAR получит этот код OEM. Таким образом, -0220 тоже соответствует Ü. Вы можете попытаться проделать это в программах KEYLOOK или TYPER.
Решение проблемы с использованием системы UNICODE в Windows NT
Производители программ, создающие приложения для международного рынка, вынуждены были иметь дело с нестандартными решениями проблемы 7-разрядного кода ASCII, такими как кодовые страницы и наборы двухбайтных символов. Лучшее решение необходимо, и им может стать Unicode.
Unicode — это кодирование символа, которое использует единообразный 16-разрядный код для каждого символа.
Это позволяет получать коды любого символа, написанного на любом языке мира, из тех, которые вероятнее всего будут использоваться в сфере компьютерных коммуникаций, включая иероглифы Китая, Японии и Кореи. Unicode разрабатывался консорциумом компьютерных компаний (включая самые крупные), и документирован в книге
Unicode Standart, опубликованной издательством Addison-Wesley.
К сожалению, в Windows 95 имеются только некоторые элементы поддержки системы Unicode, и Windows 95 не обеспечивает работы с символами Unicode с помощью драйвера клавиатуры, в отличие от Windows NT, в которой изначально была заложена поддержка Unicode.
Очевидно, что адаптация программ (и умов программистов) к идее 16-разрядных символов — это непростая работа, но она окупится сторицей, если у нас появится возможность выводить на экраны и принтеры персональных компьютеров информацию на всех языках мира. Если вы интересуетесь концепцией и механикой системы кодирования Unicode, реализованной в Windows NT, то вы можете открыть рубрику "Enviroments" в PC Magazine за 1993 год, статьи за 26 октября, 9 ноября, 23 ноября и 7 декабря (где они были случайно не указаны в содержании, но тем не менее напечатаны, начиная со страницы 426).

6
6


Глава 6
Мышь
Мышь — это графическое устройство ввода информации с одной или более кнопками. Несмотря на многочисленные эксперименты с устройствами для ввода информации, такими как сенсорные экраны или световые перья, мышь (и ее варианты типа трекбола, широко используемого в портативных компьютерах) осталась единственным устройством, которое наиболее широко проникло на рынок персональных компьютеров.
Но так было не всегда. В самом деле, первые разработчики Windows чувствовали, что им нельзя заставлять пользователей покупать мышь для работы со своим программным продуктом. Поэтому они сделали мышь необязательным аксессуаром, и обеспечили доступ ко всем операциям в Windows и ее приложениям через интерфейс клавиатуры. Другим производителям также рекомендовалось дублировать функции мыши с помощью интерфейса клавиатуры.
Хотя мышь стала почти повсеместным атрибутом компьютеров с Windows, эта философия по-прежнему актуальна.
Особенно машинистки предпочитают оставлять свои руки на клавиатуре, и предполагается, что каждый из вас когда-нибудь "терял" мышь на заваленном бумагами собственном столе. По этой причине по-прежнему рекомендуется, чтобы везде, где это возможно, вы добавляли интерфейс клавиатуры для дублирования функций мыши.
Базовые знания о мыши
Windows 95 поддерживает однокнопочную, двухкнопочную или трехкнопочную мышь, а также позволяет использовать джойстик или световое перо для имитации однокнопочной мыши. Поскольку однокнопочная мышь является простейшей, то многие программисты, работающие под Windows, традиционно не работают со второй и третьей кнопками. Однако, двухкнопочная мышь стала стандартом де-факто, поэтому традиционная сдержанность в использовании второй кнопки неоправданна. Действительно, вторая кнопка мыши нажимается или для появления "контекстного меню", т. е. меню, которое появляется в окне помимо обычной строки меню, или для специальных операций перетаскивания. (Перетаскивание будет рассмотрено ниже.)
Вы можете определить наличие мыши с помощью функции GetSystemMetrics: fMouse = GetSystemMetrics(SM_MOUSEPRESENT);
Значение fMouse будет равным TRUE (ненулевым), если мышь установлена. Для определения количества кнопок установленной мыши используйте следующий вызов: cButtons = GetSystemMetrics(SM_CMOUSEBUTTONS);
Если мышь не инсталлирована, то возвращаемым значением этой функции будет 0.
Пользователи-левши могут поменять назначение кнопок мыши с помощью программы Control Panel. Хотя приложение может определить, было ли такое переключение, передав в функцию GetSystemMetrics параметр
SM_SWAPBUTTON, но обычно это не нужно. Кнопка, нажимаемая указательным пальцем, считается левой кнопкой, даже если физически она находится на правой стороне мыши. Однако в обучающих программах вы можете нарисовать мышь на экране, и в этом случае вам надо будет узнать, менялось ли назначение кнопок мыши.
Несколько кратких определений
Когда пользователь Windows перемещает мышь, Windows перемещает по экрану маленькую растровую картинку, которая называется "курсор мыши" (mouse cursor). Курсор мыши имеет "вершину" (hot spot) размером в один пиксель, точно указывающее положение мыши на экране.

204
В драйвере дисплея содержатся несколько ранее определенных курсоров мыши, которые могут использоваться в программах. Наиболее типичным курсором является наклонная стрелка, которая называется IDC_ARROW и определяется в заголовочных файлах Windows. Вершина — это конец стрелки. Курсор IDC_CROSS (используемый в приведенных в этой главе программах BLOKOUT) имеет вершину в центре крестообразного шаблона. Курсор
IDC_WAIT в виде песочных часов обычно используется программами для индикации того, что они чем-то заняты.
Программисты также могут спроектировать свои собственные курсоры (как это делается в главе 9). Курсор, устанавливаемый по умолчанию, для конкретного окна задается при определении структуры класса окна.
Например: wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
Ниже приведены определения терминов, соответствующих вашим действиям над кнопками мыши:

Щелчок — нажатие и отпускание кнопки мыши

Двойной щелчок — двойное быстрое одно за другим нажатие и отпускание кнопки мыши

Перетаскивание — перемещение мыши при нажатой кнопке
На трехкнопочной мыши кнопки называются левой кнопкой, средней кнопкой и правой кнопкой. В связанных с мышью идентификаторах, определенных в заголовочных файлах Windows, используются аббревиатуры
LBUTTON, MBUTTON и RBUTTON. Двухкнопочная мышь имеет только левую и правую кнопки. Единственная кнопка однокнопочной мыши является левой.
Сообщения мыши, связанные с рабочей областью окна
В предыдущей главе вы видели, как Windows посылает сообщения клавиатуры только тому окну, которое имеет фокус ввода. Сообщения мыши отличаются: оконная процедура получает сообщения мыши и когда мышь проходит через окно и при щелчке внутри окна, даже если окно неактивно или не имеет фокуса ввода. В Windows для мыши определен набор из 21 сообщения. Однако, 11 из этих сообщений не относятся к рабочей области, и программы для Windows обычно игнорируют их.
Если мышь перемещается по рабочей области окна, оконная процедура получает сообщение WM_MOUSEMOVE.
Если кнопка мыши нажимается или отпускается внутри рабочей области окна, оконная процедура получает следующие сообщения:
Кнопка
Нажатие
Отпускание
Нажатие
(Второй щелчок)
Левая
WM_LBUTTONDOWN
WM_LBUTTONUP
WM_LBUTTONDBLCLK
Средняя
WM_MBUTTONDOWN WM_MBUTTONUP
WM_MBUTTONDBLCLK
Правая WM_RBUTTONDOWN
WM_RBUTTONUP
WM_RBUTTONDBLCLK
Ваша оконная процедура получает сообщения "MBUTTON" только при наличии трехкнопочной мыши и сообщения "RBUTTON" только при наличии двух- или трехкнопочной мыши. Оконная процедура получает сообщения "DBLCLK" (двойной щелчок) только в том случае, если класс окна был определен так, чтобы их можно было получать (как описано ниже).
Для всех этих сообщений значение параметра lParam содержит положение мыши. Младшее слово — это координата х, а старшее слово — координата y относительно верхнего левого угла рабочей области окна. Вы можете извлечь координаты х и y из параметра lParam с помощью макросов LOWORD и HIWORD, определенных в заголовочных файлах Windows. Значение параметра wParam показывает состояние кнопок мыши и клавиш
и . Вы можете проверить параметр wParam с помощью битовых масок, определенных в заголовочных файлах. Префикс MK означает "клавиша мыши" (mouse key).
MK_LBUTTON
Левая кнопка нажата
MK_MBUTTON
Средняя кнопка нажата
MK_RBUTTON
Правая кнопка нажата
MK_SHIFT
Клавиша нажата
MK_CONTROL
Клавиша нажата
При движении мыши по рабочей области окна, Windows не вырабатывает сообщение WM_MOUSEMOVE для всех возможных положений мыши. Количество сообщений WM_MOUSEMOVE, которые получает ваша программа, зависит от устройства мыши и от скорости, с которой ваша оконная процедура может обрабатывать сообщения о движении мыши. Вы получите хорошее представление о темпе получения сообщений WM_MOUSEMOVE, когда поэкспериментируете с представленной ниже программой CONNECT.
Если вы щелкните левой кнопкой мыши в рабочей области неактивного окна, Windows сделает активным окно, в котором вы произвели щелчок, и затем передаст оконной процедуре сообщение WM_LBUTTONDOWN. Если ваша

205 оконная процедура получает сообщение WM_LBUTTONDOWN, то ваша программа может уверенно считать, что ее окно активно. Однако, ваша оконная процедура может получить сообщение WM_LBUTTONUP, не получив вначале сообщения WM_LBUTTONDOWN. Это может случиться, если кнопка мыши нажимается в одном окне, мышь перемещается в ваше окно, и кнопка отпускается. Аналогично, оконная процедура может получить сообщение WM_LBUTTONDOWN без соответствующего ему сообщения WM_LBUTTONUP, если кнопка мыши отпускается во время нахождения в другом окне.
Из этих правил есть два исключения:

Оконная процедура может "захватить мышь" (capture the mouse) и продолжать получать сообщения мыши, даже если она находится вне рабочей области окна. Позднее в этой главе вы узнаете, как захватить мышь.

Если системное модальное окно сообщений или системное модальное окно диалога находится на экране, никакая другая программа не может получать сообщения мыши. Системные модальные окна сообщений и диалога запрещают переключение на другое окно программы, пока оно активно. (Примером системного модального окна сообщений является окно, которое появляется, когда вы завершаете работу с Windows.)
Простой пример обработки сообщений мыши
Программа CONNECT, приведенная на рис. 6.1, выполняет достаточно простую обработку сообщений мыши, что позволяет вам получить хорошее представление о том, как Windows посылает сообщения мыши вашей программе.
CONNECT.MAK
#-----------------------
# CONNECT.MAK make file
#----------------------- connect.exe : connect.obj
$(LINKER) $(GUIFLAGS) -OUT:connect.exe connect.obj $(GUILIBS) connect.obj : connect.c
$(CC) $(CFLAGS) connect.c
CONNECT.C
/*--------------------------------------------------
CONNECT.C -- Connect-the-Dots Mouse Demo Program
(c) Charles Petzold, 1996
--------------------------------------------------*/
#include
#define MAXPOINTS 1000
#define MoveTo(hdc, x, y) MoveToEx(hdc, x, y, NULL)
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
PSTR szCmdLine, int iCmdShow)
{ static char szAppName[] = "Connect";
HWND hwnd;
MSG msg;
WNDCLASSEX wndclass; wndclass.cbSize = sizeof(wndclass); wndclass.style = CS_HREDRAW | CS_VREDRAW; wndclass.lpfnWndProc = WndProc; wndclass.cbClsExtra = 0; wndclass.cbWndExtra = 0; wndclass.hInstance = hInstance; wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION); wndclass.hCursor = LoadCursor(NULL, IDC_ARROW); wndclass.hbrBackground =(HBRUSH) GetStockObject(WHITE_BRUSH); wndclass.lpszMenuName = NULL; wndclass.lpszClassName = szAppName; wndclass.hIconSm = LoadIcon(NULL, IDI_APPLICATION);

206
RegisterClassEx(&wndclass); hwnd = CreateWindow(szAppName, "Connect-the-Points Mouse Demo",
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT,
NULL, NULL, hInstance, NULL);
ShowWindow(hwnd, iCmdShow);
UpdateWindow(hwnd); while(GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
} return msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam)
{ static POINT points[MAXPOINTS]; static int iCount;
HDC hdc;
PAINTSTRUCT ps; int i, j; switch(iMsg)
{ case WM_LBUTTONDOWN : iCount = 0;
InvalidateRect(hwnd, NULL, TRUE); return 0; case WM_MOUSEMOVE : if(wParam & MK_LBUTTON && iCount < 1000)
{ points[iCount ].x = LOWORD(lParam); points[iCount++].y = HIWORD(lParam); hdc = GetDC(hwnd);
SetPixel(hdc, LOWORD(lParam), HIWORD(lParam), 0L);
ReleaseDC(hwnd, hdc);
} return 0; case WM_LBUTTONUP :
InvalidateRect(hwnd, NULL, FALSE); return 0; case WM_PAINT : hdc = BeginPaint(hwnd, &ps);
SetCursor(LoadCursor(NULL, IDC_WAIT));
ShowCursor(TRUE); for(i = 0; i < iCount - 1; i++) for(j = i + 1; j < iCount; j++)
{
MoveTo(hdc, points[i].x, points[i].y);
LineTo(hdc, points[j].x, points[j].y);
}
ShowCursor(FALSE);

207
SetCursor(LoadCursor(NULL, IDC_ARROW));
EndPaint(hwnd, &ps); return 0; case WM_DESTROY :
PostQuitMessage(0); return 0;
} return DefWindowProc(hwnd, iMsg, wParam, lParam);
}
Рис. 6.1 Программа CONNECT
Программа CONNECT обрабатывает три сообщения мыши:

WM_LBUTTONDOWN — Программа очищает рабочую область.

WM_MOUSEMOVE — Если левая кнопка мыши нажата, то программа рисует черную точку в рабочей области в текущем положении мыши.

WM_LBUTTONUP — Программа соединяет все точки, нарисованные в рабочей области, друг с другом.
Иногда в результате этого получается симпатичный рисунок, а иногда — плотно заполненный клубок. (См. рис. 6.2.)
Чтобы воспользоваться программой CONNECT, поместите курсор мыши в рабочую область, нажмите левую кнопку, немного подвигайте мышь, и отпустите левую кнопку. Программа CONNECT лучше работает с кривым шаблоном из нескольких точек, который вы можете нарисовать, быстро двигая мышь при нажатой левой кнопке. В программе используется несколько простых функций интерфейса графического устройства (GDI). Функция
SetPixel рисует точку размером с один пиксель определенного цвета — в нашем случае черного. (На дисплее с высокой разрешающей способностью, пиксель может быть почти невидим.) Для рисования линий нужны две функции: MoveTo отмечает координаты х и y начала линии, а LineTo рисует линию. (Обратите внимание, что
MoveTo определяется как макрос, использующий функцию MoveToEx.)
Если вы перемещаете курсор мыши за пределы рабочей области до того, как отпускаете кнопку, программа
CONNECT не соединяет точки между собой, поскольку она не получает сообщения WM_LBUTTONUP. Если вы возвращаете курсор мыши обратно в рабочую область и снова нажимаете левую кнопку, то CONNECT очищает рабочую область. (Если вы хотите продолжить рисование после того, как отпустили кнопку вне рабочей области, то снова нажмите левую кнопку, пока мышь находится вне рабочей области, а затем переместите мышь обратно внутрь.)
Рис. 6.2 Вывод на экран программы CONNECT
CONNECT хранит информацию о максимум 1000 точках. Число линий, которые рисует программа равно:
((P)
×
(P — 1))/2
где P — это количество точек. При наличии всех 1000 точек, а это почти 500000 линий, на рисование может уйти несколько минут. Поскольку Windows 95 является вытесняющей многозадачной средой, вы можете в это время переключиться на другие программы. Однако, вы ничего не сможете сделать с программой CONNECT (например,

208 сдвинуть ее окно или изменить его размер), пока продолжается ее работа. В главе 14 мы изучим методы решения этой проблемы.
Поскольку программе CONNECT для рисования линий может потребоваться некоторое время, при обработке сообщения WM_PAINT она изменяет вид курсора на песочные часы, а после окончания рисования, возвращает курсор в предыдущее состояние. Для этого требуется два вызова функции SetCursor, в которых используются два стандартных курсора. В программе также дважды вызывается функция ShowCursor, первый раз с параметром
TRUE и второй — с параметром FALSE. Позднее в этой главе в разделе "Эмуляция мыши с помощью клавиатуры" об этих вызовах будет рассказано более подробно.
Если программа CONNECT занята рисованием линий, вы можете нажать кнопку мыши, подвигать мышью и отпустить кнопку мыши, но ничего не произойдет. CONNECT не получает эти сообщения, поскольку она занята и не может сделать ни одного вызова GetMessage. После того, как программа закончит рисование линий, она опять не получает этих сообщений, поскольку кнопка мыши к этому времени отпущена. В этом отношении мышь не похожа на клавиатуру. Windows обращается с каждой нажатой на клавиатуре клавишей как с чем-то важным.
Однако, если кнопка мыши нажата и отпущена в рабочей области, пока программа занята, щелчки мыши просто сбрасываются.
Теперь попытайтесь сделать следующее: пока программа CONNECT долго занимается рисованием, нажмите кнопку мыши и подвигайте курсором. После того, как программа CONNECT закончит рисование, она извлечет сообщение WM_LBUTTONDOWN из очереди сообщений (и обновит рабочую область), поскольку в этот момент кнопка нажата. Однако, она получает только сообщения WM_MOUSEMOVE, возникшие после получения сообщения WM_LBUTTONDOWN.
Иногда слово "слежение" (tracking) используется для ссылки на способ, которым программа обрабатывает движение мыши. Слежение, однако, не значит, что ваша программа остается в цикле своей оконной процедуры, пытаясь отслеживать движение мыши по экрану. Вместо этого оконная процедура обрабатывает каждое приходящее сообщение мыши и тут же быстро заканчивается.
Обработка клавиш
Когда программа CONNECT получает сообщение WM_MOUSEMOVE, она выполняет поразрядную операцию
AND со значениями wParam и MK_LBUTTON для определения того, нажата ли левая кнопка. Вы также можете использовать wParam для определения состояния клавиш . Например, если обработка должна зависеть от состояния клавиш и , то вы могли бы воспользоваться следующей логикой: if(MK_SHIFT & wParam) if(MK_CONTROL & wParam)
{
[нажаты клавиши и ]

} else
{
[нажата клавиша ]
} else if(MK_CONTROL & wParam)
{
[нажата клавиша ]

} else
{
[клавиши и не нажаты]

}
Если вы хотите в вашей программе использовать и левую и правую кнопки мыши, и если вы также хотите обеспечить возможность работы пользователям однокнопочной мыши, вы можете так написать вашу программу, чтобы действие клавиши в сочетании с левой кнопкой мыши было тождественно действию правой кнопки.
В этом случае ваша обработка щелчков кнопки могла бы выглядеть так: case WM_LBUTTONDOWN: if(!MK_SHIFT & wParam)
{
[логика обработки левой кнопки]

return
0;
}
// идем дальше вниз case WM_RBUTTONDOWN:
[логика обработки правой кнопки]


209 return 0;
Функция GetKeyState (описанная в главе 4) также может возвращать состояние кнопок мыши или клавиш , используя виртуальные коды клавиш VK_LBUTTON, VK_RBUTTON, VK_MBUTTON, VK_SHIFT и
VK_CONTROL. При нажатой кнопке или клавише возвращаемое значение функции GetKeyState отрицательно.
Функция GetKeyState возвращает состояние мыши или клавиши в связи с обрабатываемым в данный момент сообщением, т. е. информация о состоянии должным образом синхронизируется с сообщениями. Но поскольку вы не можете использовать функцию GetKeyState для клавиши, которая еще только должна быть нажата, ее нельзя использовать и для кнопки мыши, которая еще только должна быть нажата. Не делайте так: while(GetKeyState(VK_LBUTTON) >= 0);
//
ОШИБКА
!!!
Функция GetKeyState сообщит о том, что левая кнопка нажата только в том случае, если левая кнопка уже нажата, когда вы обрабатываете сообщение и вызываете GetKeyState.
Двойные щелчки клавиш мыши
Двойным щелчком мыши называются два, следующих один за другим в быстром темпе, щелчка мыши. Для того, чтобы два последовательных щелчка мыши считались двойным щелчком, они должны произойти в течение очень короткого промежутка времени, который называется "временем двойного щелчка" (double-click time). Если вы хотите, чтобы ваша оконная процедура получала сообщения двойного щелчка мыши, то вы должны включить идентификатор CS_DBLCLKS при задании стиля окна в классе окна перед вызовом функции RegisterClassEx

: wndclass.style = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS;
Если вы не включите CS_DBLCLKS в стиль окна, и пользователь дважды в быстром темпе щелкнет левой кнопкой мыши, то ваша оконная процедура получит следующие сообщения: WM_LBUTTONDOWN, WM_LBUTTONUP,
WM_LBUTTONDOWN и WM_LBUTTONUP. (Оконная процедура вполне может между этими сообщениями от кнопок мыши получать и другие сообщения.) Если вы хотите реализовать собственную логику обработки двойного щелчка мыши, то для получения относительного времени сообщений WM_LBUTTONDOWN, вы можете использовать функцию Windows GetMessageTime. Более подробно об этой функции рассказывается в главе 7.
Если вы включаете в свой класс окна идентификатор CS_DBLCLKS, то оконная процедура при двойном щелчке мыши получает следующие сообщения: WM_LBUTTONDOWN, WM_LBUTTONUP, WM_LBUTTONDBLCLK и
WM_LBUTTONUP.
Сообщение WM_LBUTTONDBLCLK просто заменяет второе сообщение
WM_LBUTTONDOWN.
Двойной щелчок мыши гораздо легче обрабатывать, если первый щелчок выполняет в оконной процедуре те же самые действия, которые выполняет простой щелчок. Затем второй щелчок (сообщение WM_LBUTTONBLCLK) выполняет какие-то дополнительные, относительно первого щелчка, действия. Например, посмотрите, как работает мышь со списком файлов в программе Windows Explorer. С помощью одного щелчка выбирается файл.
Программа Windows Explorer выделяет выбранный файл, инвертируя цвет строки. Двойной щелчок выполняет два действия: при первом щелчке выбирается файл, точно также, как и при единственном щелчке; а второй щелчок побуждает программу Windows Explorer запустить файл. Логика здесь элементарна. Если бы первый щелчок двойного щелчка мыши не выполнял те же действия, которые выполняет единственный щелчок, то логика управления мышью могла бы быть гораздо сложнее.
Сообщения мыши нерабочей области
Те десять сообщений мыши, которые мы только что обсудили, относятся к ситуации, когда действия с мышью, т. е. перемещения мыши и щелчки ее кнопками, происходят в рабочей области окна. Если мышь оказывается вне рабочей области окна, но все еще внутри окна, Windows посылает оконной процедуре сообщения мыши "нерабочей области". Нерабочая область включает в себя панель заголовка, меню и полосы прокрутки окна.
Обычно вам нет необходимости обрабатывать сообщения мыши нерабочей области. Вместо этого вы просто передаете их в DefWindowProc, чтобы Windows могла выполнить системные функции. В этом смысле сообщения мыши нерабочей области похожи на системные сообщения клавиатуры WM_SYSKEYDOWN, WM_SYSKEYUP и
WM_SYSCHAR.
Сообщения мыши нерабочей области почти полностью такие же как и сообщения мыши рабочей области. В названия сообщений входят буквы "NC", что означает "нерабочая" (nonclient). Если мышь перемещается внутри нерабочей области окна, то оконная процедура получает сообщение WM_NCMOUSEMOVE. Кнопки мыши вырабатывают следующие сообщения:
Кнопка
Нажатие
Отпускание
Нажатие (Второй щелчок)
Левая
WM_NCLBUTTONDOWN
WM_NCLBUTTONUP
WM_NCLBUTTONDBLCLK
Средняя
WM_NCMBUTTONDOWN WM_NCMBUTTONUP
WM_NCMBUTTONDBLCLK

210
Кнопка
Нажатие
Отпускание
Нажатие (Второй щелчок)
Правая
WM_NCRBUTTONDOWN WM_NCRBUTTONUP
WM_NCRBUTTONDBLCLK
Однако, параметры wParam и lParam для сообщений мыши нерабочей области отличаются от соответствующих параметров для сообщений мыши рабочей области. Параметр wParam показывает зону нерабочей области, в которой произошло перемещение или щелчок мыши. Его значение приравнивается одному из идентификаторов, начинающихся с HT (что означает "тест попадания" (hit-test), которые определяются в заголовочных файлах
Windows.
0
x
y
0
x
y
Client-area coordinate
Screen coordinate
Рис. 6.3 Координаты экрана и координаты рабочей области
Переменная lParam содержит в младшем слове значение координаты х, а в старшем — y. Однако, эти координаты являются координатами экрана, а не координатами рабочей области, как это было у сообщений мыши рабочей области. Значения координат x и y верхнего левого угла экрана равны 0. Если вы движетесь вправо, то увеличивается значение координаты х, если вниз, то значение координаты у. (См. рис. 6.3.)
Вы можете преобразовать экранные координаты в координаты рабочей области окна и наоборот с помощью двух функций Windows:
ScreenToClient(hwnd, pPoint);
ClientToScreen(hwnd, pPoint);
Параметр pPoint — это указатель на структуру тира POINT. Две эти функции преобразуют хранящиеся в структуре данные без сохранения их прежних значений. Отметьте, что если точка с экранными координатами находится выше рабочей области окна, то преобразованное значение координаты у рабочей области будет отрицательным. И аналогично, значение х экранной координаты левее рабочей области после преобразования в координату рабочей области станет отрицательным.
Сообщение теста попадания
Если вы вели подсчет, то знаете, что мы рассмотрели 20 из 21 сообщения мыши. Последним сообщением является
WM_NCHITTEST, означающее "тест попадания в нерабочую область" (nonclient hit-test). Это сообщение предшествует всем остальным сообщениям мыши рабочей и нерабочей области. Параметр lParam содержит значения х и у экранных координат положения мыши. Параметр wParam не используется.
В приложениях для Windows это сообщение обычно передается в DefWindowProc. В этом случае Windows использует сообщение WM_NCHITTEST для выработки всех остальных сообщений мыши на основе положения мыши. Для сообщений мыши нерабочей области возвращаемое значение функции DefWindowProc при обработке сообщения
WM_NCHITTEST передается как параметр wParam в сообщении мыши. Это значение может быть любым из множества значений wParam, которое бывает у этого параметра для сообщений мыши нерабочей области, плюс следующие:
HTCLIENT
Рабочая область
HTNOWHERE
Нет ни на одном из окон
HTTRANSPARENT
Окно перекрыто другим окном

211
HTERROR
Заставляет DefWindowProc генерировать гудок
Если функция DefWindowProc после обработки сообщения WM_NCHITTEST возвращает значение HTCLIENT, то
Windows преобразует экранные координаты в координаты рабочей области вырабатывает сообщение мыши рабочей области.
Если вы вспомните, как мы запретили все системные функции клавиатуры при обработке сообщения
WM_SYSKEYDOWN, то вы, наверное, удивитесь, если сможете сделать что-нибудь подобное, используя сообщения мыши. Действительно, если вы вставите строки: case WM_NCHITTEST: return(LRESULT) HTNOWHERE; в вашу оконную процедуру, то вы полностью запретите все сообщения мыши рабочей и нерабочей области вашего окна. Кнопки мыши просто не будут работать до тех пор, пока мышь будет находится где-либо внутри вашего окна, включая значок системного меню, кнопки минимизации, максимизации и закрытия окна.
Сообщения порождают сообщения
Windows использует сообщение WM_NCHITTEST для выработки всех остальных сообщений мыши. Идея сообщений, порождающих другие сообщения, характерна для Windows. Давайте рассмотрим пример. Если вы дважды щелкните мышью на значке системного меню Windows-программы, то программа завершится. Двойной щелчок генерирует серию сообщений WM_NCHITTEST. Поскольку мышь установлена над значком системного меню, то возвращаемым значением функции DefWindowProc является HTSYSMENU, и Windows ставит в очередь сообщение WM_NCLBUTTONDBLCLK с параметром wParam, равным HTSYSMENU.
Оконная процедура обычно передает это сообщение DefWindowProc. Когда функция DefWindowProc получает сообщение WM_NCLBUTTONDBLCLK с параметром wParam, равным HTSYSMENU, то она ставит в очередь сообщение WM_SYSCOMMAND с параметром
wParam, равным SC_CLOSE. (Это сообщение
WM_SYSCOMMAND также генерируется, когда пользователь выбирает в системном меню пункт Close.) Оконная процедура вновь передает это сообщение в DefWindowProc. DefWindowProc обрабатывает сообщение, отправляя оконной процедуре синхронное сообщение WM_CLOSE.
Если программе для своего завершения требуется получить от пользователя подтверждение, оконная процедура может обработать сообщение WM_CLOSE. В противном случае оконная процедура обрабатывает сообщение
WM_CLOSE, вызывая функцию DestroyWindow. Помимо других действий, функция DestroyWindow посылает оконной процедуре синхронное сообщение WM_DESTROY. Обычно оконная процедура обрабатывает сообщение
WM_DESTROY следующим образом: case WM_DESTROY:
PostQuitMessage(0); return 0;
Функция PostQuitMessage заставляет Windows поместить в очередь сообщений сообщение WM_QUIT. До оконной процедуры это сообщение никогда не доходит, поскольку оно является причиной того, что функция GetMessage возвращает 0, что вызывает завершение цикла обработки сообщений и программы в целом.
Тестирование попадания в ваших программах
Ранее говорилось о том, как программа Windows Explorer реагирует на одиночные и двойные щелчки мыши.
Очевидно, что программа должна определить, на какой именно файл пользователь указывает с помощью мыши.
Это называется "тестом попадания". Так же как функция DefWindowProc должна осуществлять тестирование попадания при обработке сообщений WM_NCHITTEST, также очень часто и оконная процедура должна выполнять тестирование попадания внутри рабочей области. Как правило тест попадания включает в себя расчеты с использованием координат х и у, переданных вашей оконной процедуре в параметре lParam сообщения мыши.
Гипотетический пример
Рассмотрим пример. Ваша программа выводит на экран несколько столбцов файлов, отсортированных в алфавитном порядке. Список файлов начинается вверху рабочей области, имеющей ширину cxClient пикселей и высоту cyClient пикселей; высота каждого символа равна cyChar пикселей. Имена файлов хранятся в отсортированном массиве указателей на символьные строки, который называется szFileNames.
Давайте предположим, что ширина столбцов равна cxColWidth пикселей. Число файлов, которые вы сможете разместить в каждом столбце равно: iNumInCol = cyClient / cyChar;

212
Вы получаете сообщение о щелчке мыши с координатами cxMouse и cyMouse, извлеченными из параметра lParam.
На какой столбец имен файлов пользователь указывает с помощью мыши, можно определить по формуле: iColumn = cxMouse / cxColWidth;
Положение имени файла относительно вершины столбца равно: iFromTop = cyMouse / cyChar;
Теперь можно рассчитать индекс массива szFileNames: iIndex = iColumn * iNumInCol + iFromTop;
Очевидно, что если значение iIndex превосходит число файлов, содержащихся в массиве, пользователь производит щелчок на пустой области экрана.
Во многих случаях тест попадания более сложен, чем предложенный в этом примере. Он может быть очень запутанным, например, в программе текстового редактора WORDPAD, в котором используются шрифты различных размеров. Когда вы что-нибудь выводите в рабочую область окна, то должны определить координаты каждого выводимого на экран объекта. В расчетах при тестировании попадания вы должны двигаться в обратном направлении: от координат к объекту. Однако, если объектом, который вы выводите на экран, является строка, такое движение в обратном направлении включает в себя и определение положения символов внутри строки.
Пример программы
Программа CHECKER1, приведенная на рис. 6.4, демонстрирует несколько простых тестов попадания. Программа делит рабочую область на 25 прямоугольников, получая таким образом массив размером 5 на 5. Если вы щелкаете мышью на одном из прямоугольников, то в прямоугольнике рисуется символ X. При повторном щелчке символ Х удаляется.
CHECKER1.MAK
#------------------------
# CHECKER1.MAK make file
#------------------------ checker1.exe : checker1.obj
$(LINKER) $(GUIFLAGS) -OUT:checker1.exe checker1.obj $(GUILIBS) checker1.obj : checker1.c
$(CC) $(CFLAGS) checker1.c
CHECKER1.C
/*-------------------------------------------------
CHECKER1.C -- Mouse Hit-Test Demo Program No. 1
(c) Charles Petzold, 1996
-------------------------------------------------*/
#include
#define DIVISIONS 5
#define MoveTo(hdc, x, y) MoveToEx(hdc, x, y, NULL)
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
PSTR szCmdLine, int iCmdShow)
{ static char szAppName[] = "Checker1";
HWND hwnd;
MSG msg;
WNDCLASSEX wndclass; wndclass.cbSize = sizeof(wndclass); wndclass.style = CS_HREDRAW | CS_VREDRAW; wndclass.lpfnWndProc = WndProc; wndclass.cbClsExtra = 0; wndclass.cbWndExtra = 0;

213 wndclass.hInstance = hInstance; wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION); wndclass.hCursor = LoadCursor(NULL, IDC_ARROW); wndclass.hbrBackground =(HBRUSH) GetStockObject(WHITE_BRUSH); wndclass.lpszMenuName = NULL; wndclass.lpszClassName = szAppName; wndclass.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
RegisterClassEx(&wndclass); hwnd = CreateWindow(szAppName, "Checker1 Mouse Hit-Test Demo",
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT,
NULL, NULL, hInstance, NULL);
ShowWindow(hwnd, iCmdShow);
UpdateWindow(hwnd); while(GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
} return msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam)
{ static BOOL fState[DIVISIONS][DIVISIONS]; static int cxBlock, cyBlock;
HDC hdc;
PAINTSTRUCT ps;
RECT rect; int x, y; switch(iMsg)
{ case WM_SIZE : cxBlock = LOWORD(lParam) / DIVISIONS; cyBlock = HIWORD(lParam) / DIVISIONS; return 0; case WM_LBUTTONDOWN : x = LOWORD(lParam) / cxBlock; y = HIWORD(lParam) / cyBlock; if(x < DIVISIONS && y < DIVISIONS)
{ fState [x][y] ^= 1; rect.left = x * cxBlock; rect.top = y * cyBlock; rect.right =(x + 1) * cxBlock; rect.bottom =(y + 1) * cyBlock;
InvalidateRect(hwnd, &rect, FALSE);
} else
MessageBeep(0); return 0; case WM_PAINT : hdc = BeginPaint(hwnd, &ps);

214 for(x = 0; x < DIVISIONS; x++) for(y = 0; y < DIVISIONS; y++)
{
Rectangle(hdc, x * cxBlock, y * cyBlock,
(x + 1) * cxBlock,(y + 1) * cyBlock); if(fState [x][y])
{
MoveTo(hdc, x * cxBlock, y * cyBlock);
LineTo(hdc,(x+1) * cxBlock,(y+1) * cyBlock);
MoveTo(hdc, x * cxBlock,(y+1) * cyBlock);
LineTo(hdc,(x+1) * cxBlock, y * cyBlock);
}
}
EndPaint(hwnd, &ps); return 0; case WM_DESTROY :
PostQuitMessage(0); return 0;
} return DefWindowProc(hwnd, iMsg, wParam, lParam);
}
Рис. 6.4 Программа CHECKER1
На рис. 6.5 показан вывод программы CHECKER1. У всех 25 прямоугольников одинаковая высота и ширина.
Значения ширины и высоты хранятся в cxBlock и cyBlock, и пересчитываются при изменении размеров рабочей области. В логике обработки сообщений WM_LBUTTONDOWN для определения прямоугольника, на котором был произведен щелчок, используются координаты мыши. Этот обработчик устанавливает текущее состояние прямоугольника в массиве fState, и делает соответствующий прямоугольник недействительным для выработки сообщения WM_PAINT. Если ширина или высота рабочей области не делится без остатка на пять, узкая полоса справа или внизу рабочей области не будет зарисована прямоугольниками. В ответ на щелчок мыши в этой области, программа CHECKER1, вызывая функцию MessageBeep, сообщит об ошибке.
Когда программа CHECKER1 получает сообщение WM_PAINT, она перерисовывает всю рабочую область, рисуя прямоугольники с помощью функции GDI Rectangle. Если установлено значение fState, то программа CHECKER1 с помощью функций MoveTo и LineTo рисует две линии. Перед перерисовкой, при обработке сообщения
WM_PAINT, программа не проверяет, действительна ли каждая прямоугольная область, хотя могла бы это делать.
Первый метод такой проверки заключается в создании внутри цикла структуры RECT для каждого прямоугольного блока (с использованием тех же формул, что и для сообщения WM_LBUTTONDOWN) и проверки, с помощью функции IntersectRect, пересекается ли он с недействительным прямоугольником
(ps.rcPaint). Другим методом может быть использование функции PtInRect для определения того, находится ли любой из четырех углов прямоугольного блока внутри недействительного прямоугольника.
Рис. 6.5 Вывод на экран программы CHECKER1

215
Эмуляция мыши с помощью клавиатуры
Программа CHECKER1 работает только при наличии у вас мыши. Добавим к программе интерфейс клавиатуры примерно также, как мы это делали для программы SYSMETS в главе 5. Однако, добавление интерфейса клавиатуры к программе, использующей курсор мыши для выбора объекта, требует, чтобы мы также побеспокоились об отображении и перемещении курсора на экране.
Даже если мышь не установлена, Windows все же может вывести курсор мыши на экран. Windows поддерживает для курсора "счетчик отображения" (display count). Если мышь инсталлирована, то начальное значение счетчика отображения равно 0; если нет, то его начальное значение равно —1. Курсор мыши выводится на экран только в том случае, если значение счетчика отображения неотрицательно. Вы можете увеличить значение счетчика отображения на 1, вызывая функцию:
ShowCursor(TRUE); и уменьшить его на 1 с помощью вызова:
ShowCursor(FALSE);
Перед использованием функции ShowCursor нет нужды определять, инсталлирована мышь или нет. Если вы хотите вывести на экран курсор мыши независимо от наличия собственно мыши, просто увеличьте на 1 счетчик отображения. После однократного увеличения счетчика отображения, его уменьшение скроет курсор только в том случае, если мышь не была инсталлирована, но при наличии мыши курсор останется на экране. Вывод на экран курсора мыши относится ко всей операционной системе Windows, поэтому вам следует быть уверенным, что вы увеличивали и уменьшали счетчик отображения равное число раз.
В вашей оконной процедуре можете использовать следующую простую логику: case WM_SETFOCUS:
ShowCursor(TRUE); return 0; case WM_KILLFOCUS:
ShowCursor(FALSE); return 0;
Оконная процедура получает сообщение WM_SETFOCUS, когда окно приобретает фокус ввода клавиатуры, и сообщение WM_KILLFOCUS, когда теряет фокус ввода. Эти события являются самыми подходящими для того, чтобы вывести на экран или убрать с экрана курсор мыши. Во-первых, сообщения WM_SETFOCUS и
WM_KILLFOCUS являются взаимодополняющими — следовательно, оконная процедура равное число раз увеличит и уменьшит счетчик отображения курсора. Во-вторых, для версий Windows, в которых мышь не инсталлирована, использование сообщений WM_SETFOCUS и WM_KILLFOCUS вызовет появление курсора только в том окне, которое имеет фокус ввода. И только в этом случае, используя интерфейс клавиатуры, который вы создадите, пользователь сможет перемещать курсор.
Windows отслеживает текущее положение курсора мыши, даже если сама мышь не инсталлирована. Если мышь не установлена, и вы выводите курсор мыши на экран, то он может оказаться в любой части экрана и будет оставаться там до тех пор, пока вы не переместите его. Получить положение курсора можно с помощью функции:
GetCursorPos(pPoint); где pPoint — это указатель на структуру POINT. Функция заполняет поля структуры POINT значениями координат
х и у мыши. Установить положение курсора можно с помощью функции:
SetCursorPos(х, у);
В обоих этих случаях значения координат х и у являются координатами экрана, а не рабочей области. (Это очевидно, поскольку этим функциям не нужен параметр hwnd.) Как уже отмечалось, вы можете преобразовать экранные координаты в координаты рабочей области и наоборот с помощью функций ScreenToClient и
ClientToScreen.
Если вы при обработке сообщения мыши вызываете функцию GetCursorPos и преобразуете экранные координаты в координаты рабочей области, то они могут слегка отличаться от координат, содержащихся в параметре lParam сообщения мыши. Координаты, которые возвращаются из функции GetCursorPos, показывают текущее положение мыши. Координаты в параметре lParam сообщения мыши — это координаты мыши в момент генерации сообщения.
Вы, вероятно, захотите так написать логику работы клавиатуры, чтобы двигать курсор мыши с помощью клавиш управления курсором клавиатуры и воспроизводить кнопки мыши с помощью клавиши пробела или
. При этом не следует делать так, чтобы курсор мыши сдвигался на один пиксель за одно нажатие клавиши.
В этом случае вам для того, чтобы переместить указатель мыши от одной стороны экрана до другой, надо было бы держать клавишу со стрелкой нажатой более минуты.

216
Если нужно реализовать интерфейс клавиатуры так, чтобы точность позиционирования курсора мыши по- прежнему оставалась равной одному пикселю, тогда обработка сообщения клавиатуры должна идти следующим образом: при нажатии клавиши со стрелкой курсор мыши начинает двигаться медленно, а затем, если клавиша остается нажатой, его скорость возрастает. Вспомните, что параметр lParam в сообщениях WM_KEYDOWN показывает, являются ли сообщения о нажатии клавиш результатом автоповтора. Превосходное применение этой информации!
Добавление интерфейса клавиатуры к программе CHECKER
Программа CHECKER2, представленная на рис. 6.6, является той же программой CHECKER1, за исключением того, что здесь добавлен интерфейс клавиатуры. Вы можете использовать клавиши со стрелками для перемещения курсора между 25 прямоугольниками. Клавиша


Поделитесь с Вашими друзьями:
1   ...   16   17   18   19   20   21   22   23   ...   41


База данных защищена авторским правом ©nethash.ru 2019
обратиться к администрации

войти | регистрация
    Главная страница


загрузить материал