Продолжение, начало см. в МК №46, 51-52, 4, 6-7, 10, 12-13, 16-18, 22, 24, 29, 34, 41, 46, 4, 6, 17, 21, 23 (165, 170-171, 175, 177-178, 181, 183-184, 187-189, 193, 195, 200, 205, 212, 217, 227, 229, 240, 244, 246). Те читатели, которые внимательно следят за моими статьями, имели возможность убедиться, насколько легко можно расширять возможности языка Turbo Pascal, если не ограничиваться стандартными средствами и модулями.
Спрашивали? Отвечаю…
Дружба с Микки Маусом
Ну вот, выводить текст и получать информацию посредством ее ввода с клавиатуры мы уже научились. Все это хорошо, НО для создания программы с настоящим серьезным пользовательским интерфейсом необходима одна маленькая, но немаловажная деталь работа с манипулятором «Мышь» (Mouse), а вернее, получение управляющих команд пользователя посредством интерфейса мыши и их обработка. Так вот, задача, которую мы сегодня перед собой поставим это составление универсального модуля, который бы состоял из процедур и функций, позволяющих управлять манипулятором «мышь» и получать информацию о действиях пользователя (кликах и перемещении манипулятора). Своеобразный джентльменский набор самых необходимых процедур и функций.
Ну что же, начнем, как обычно, с блока Interface, описав в нем перечислимый тип TMouseButton для идентификации нажатой клавиши и множественный тип TMouseState, который послужит индикатором того, какая клавиша мыши нажата в данный момент. А так как одновременно могут быть нажаты несколько клавиш, рациональность применения множества в данном случае неоспорима.
Перейдя к блоку Implementation, опишем типизированную константу Ms_ButtonFlag, начальное значение которой свидетельствует о том, что ни одна клавиша мыши не нажата.
Теперь опишем короткую, но очень важную функцию Ms_Init, с выполнения которой нужно начинать работу программы, чтобы проверить наличие драйвера мыши в памяти. Будет возвращен результат true, если драйвер загружен, и false, если драйвер отсутствует или несовместим со стандартным интерфейсом, принятым Microsoft, но такое на моем веку не встречалось. Так что можно смело загружать драйверы mouse.com, mmouse.com, mouse.sys и другие, вручную или через autoexec.bat. Думаю, вас не нужно этому учить.
Функция состоит из двух машинных команд: mov ax,0 загружает номер 0 функции инициализации драйвера мыши, а int 33h вызывает обработчик интерфейса мыши (драйвер), который, как правило, позиционируется на программном прерывании $33. При этом драйвер не только откликается и возвращает результат в регистре AX (AX = 0 драйвера нет, или AX = $0FFFF драйвер есть), но в регистре BX он может вернуть значение $0FFFF стандартная мышь Microsoft с двумя клавишами, любое другое значение мышь нестандартная. Но на это нам уже начхать :-).
Кроме того, драйвер осуществляет сброс аппаратного и программного обеспечения мыши. Т.е. все установки чувствительности и диапазона координат устанавливаются по умолчанию, курсор в центре экрана и невидим, видеостраница 0, работа пользовательского обработчика сообщений мыши заблокирована, чувствительность по горизонтали 8:8 микки/pixel, по вертикали 16:8 микки/pixel, порог удвоения скорости равен 64 микки/сек.
Пример:
С этого момента можно обращаться к драйверу мыши. Наверное, первое, что может нас заинтересовать, это «Гюльчатай, покажи личико!» то бишь курсор, ради которого мы все это затеяли. Для исполнения нашего желания следует вызвать процедуру Ms_CurShow, которую сейчас опишем:
Наверно, уже нет необходимости объяснять, что опять устанавливаем номер вызываемой функции и вызываем ее программным прерыванием $33. Скажу лишь, что функция под номером 1 отобразит (включит) курсор мыши, если при этом будет установлен стандартный текстовый или графический режим, коими являются текстовый режим 8025 и графические режимы CGA, EGA, VGA (разрешение последнего 320200). Иначе, хотя и будет считаться, что курсор включен, он будет отображаться неправильно или не будет виден вовсе, так как, например, при VESA-режимах драйвер мыши не знает, как правильно рисовать курсор. Мы ему поможем, создав еще один модуль-надстройку, но это будет немного позднее.
Для осуществления собственной прорисовки курсора, например, программисту может понадобиться скрыть (выключить) курсор процедурой Ms_CurHide, которая вызывает функцию драйвера с номером 2.
При этом следует помнить, что если курсор мыши был выключен подряд определенное количество раз, чтобы его включить, следует столько же раз выполнить процедуру включения. Я так и вижу, как у некоторых читателей рука тянется к какому-нибудь тяжелому предмету, чтобы запустить в меня. Для тех, кто готов метнуть в меня камнем, скажу, что это придумал не я, а пресловутый Micro… Все камни прошу завернуть в яркую подарочную бумагу и послать посылкой в главный офис Microsoft, а лучше прямо на виллу резиденции гражданина Билла, как это делал кот Матроскин, посылая Шарику кочергу :-).
Особо любопытных могут заинтересовать функции Ms_X и Ms_Y, которые возвращают координаты курсора мыши в пикселях соответственно по горизонтали и вертикали. Даже если курсор мыши будет невидим, он все равно способен перемещаться, иначе зачем нам была бы нужна ента мышь белая :-)?
При этом вызывается функция 3 драйвера мыши, которая дает исчерпывающую информацию о положении курсора в регистрах CX X, DX Y и состоянии клавиш в регистре BX. Биты 0, 1 и 2 отвечают за левую (Left), правую (Right) и среднюю (Middle) клавиши соответственно, нулевое значение бита означает, что клавиша отпущена, а 1 клавиша нажата. Но в данном случае, на мой взгляд, удобней читать координаты отдельно, а состояние клавиш отдельно. Хотя это и не рационально в смысле беспокойства для драйвера, но он не будет за это на нас в обиде.
Если возникнет непреодолимое желание установить курсор где-то в другом месте экрана, то в этом сможет помочь процедура Ms_SetPosition, которая позиционирует курсор мыши с новыми координатами X и Y, даже если он невидим. В итоге регистр CX будет загружен значением параметра X, а регистр DX значением параметра Y, и будет вызвана функция 4 драйвера.
Тем, кто, читая мой талмуд, успел себе задать (риторический) вопрос «А где же у него кнопка?!…», внятный ответ сможет дать функция Ms_State, которая возвращает результат типа byte; последний можно легко привести к типу TMouseState путем нехитрой комбинации byte(MouseState):=Ms_State, где MouseState переменная, заранее описанная с типом TMouseState.
Как вы могли заметить, в данном случае вызывается все та же функция драйвера 3, рассмотренная выше. Пример
иллюстрирует использование функции Ms_State. При нажатии левой клавиши (mbLeft) выполняется некоторое действие, а при нажатии правой (mbRight) цикл прекращается.
Основываясь на функции Ms_State, можно расширить функциональность модуля, описав функцию Ms_Down, которая позволяет определить, нажата ли затребованная клавиша Button в данный момент. Если да, то вернет true, если нет false.
Тогда уже известный вам пример будет выглядеть гораздо нагляднее и эффективнее с точки зрения экономии памяти сегмента данных:
Вы со мной согласны? Не слышу? Ладно, молчание знак согласия :-).
Следующая функция может помочь в отслеживании полного нажатия-отпускания клавиши (клик Click). Достаточно лишь указать нужную клавишу, и функция вернет true, если клавиша была нажата и теперь отпущена, или false если нет. Возможно, у кого-то возникнет вопрос: «А почему нельзя просто отследить отпускание клавиши? Ведь раз произошло отпускание, значит, было и нажатие». Дело в том, что если просто проверять состояние клавиши, то очень часто может фиксироваться факт, что клавиша отпущена, и тогда получится, что будет многократно опознано отпускание клавиши, даже когда это отпускание уже было обработано ранее. Именно для того чтобы устранить такое многократное срабатывание, и нужна функция Ms_Click. Правда, ее следует постоянно вызывать в теле некоторого цикла, чтобы не проморгать момент нажатия и отпускания клавиши. И тогда она зарегистрирует нажатие нужной клавиши в переменной Ms_ButtonFlag, а затем, при отпускании, сообщит нам.
Все тот же пример теперь будет выглядеть немного иначе:
Обычно чувствительность мыши по умолчанию вполне приемлема, но все же может возникнуть необходимость регулировать чувствительность мыши к перемещению. Для этого пригодится процедура Ms_SetMickey. Надо лишь задать величину, на сколько микки нужно передвинуть корпус мышки, чтобы переместиться на 8 точек по экрану по горизонтали и вертикали.
Таким образом, чем меньше величины параметров Horiz, Vertic, тем чувствительнее (быстрее) будет двигаться курсор, и наоборот, если установить эти величины очень большими, то, возможно, придется проехать мышкой полстола, чтобы добраться ее курсором до границы экрана :-).
В данном случае параметр Horiz загружается в регистр CX, а Vertic в регистр DX, и вызывается функция $0f драйвера.