Программирование на Паскале. Выпуск 11
В предыдущих выпусках, которые можно найти на сайте, я начал рассказывать, как на Паскале писать программы, работающие с мышью.
ОБРАТИТЕ ВНИМАНИЕ, что описанные здесь эффекты будут работать только в полноэкранном режиме. Переключаться между режимами лучше всего с помощью комбинации Alt + ENTER.
Текст программы сильно менять не будем. Просто добавим новую возможность: изменение вида указателя мыши с помощью функции $0A.
При вызове | AX = $0A |
BX - тип указателя. 0 = программный указатель (software). 1 - системный (hardware) | |
CX - маска И (AND) при BX =0, или начальная линия курсора при BX =1 | |
DX - маска исключающее ИЛИ (XOR) при BX =0, или конечная линия курсора при BX =1 | |
Возвращает | ничего |
По умолчанию указатель отображается как "software". Он имеет вид того символа, над которым находится, но с "инвертированными" атрибутами текста.
Ниже приводится листинг программы, демонстрирующий изменение вида курсора. Вид изменяется при нажатии на левую кнопку мыши: сначала он "стандартный программный" software, потом такой же, но отображается двойным белым крестом на синем фоне, а потом системный (hardware). Попробуйте сами изменять значения r.cx и r.dx для получения различных эффектов.
program mouse_02b; uses DOS, CRT; procedure FillScreen; var f: File of char; c: Char; begin Assign(f, 'mou_02a.pas'); {$I-} Reset(f); {$I+} if IOResult <> 0 then Exit; ClrScr; While NOT EOF(f) and (WhereY < 25) do begin Read(f, c); Write(c) end; Close(f); end; var r: Registers; v: Integer; procedure ChangeView(ViewNo: Integer); begin r.ax:=$0a; case ViewNo of 1: with r do begin bx:=0; cx:=$00ff; dx:=$4e00 end; 2: with r do begin bx:=0; cx:=$0; dh:=$1f; dl:=206 end; 3: with r do begin bx:=1; cx:=7; dx:=8 end; else Exit; end; intr($33, r); delay(100); end; BEGIN r.ax:=0; intr($33, r); if r.ax <> $FFFF then begin WriteLn('Мышь не обнаружена'); Halt(1) end; FillScreen; ChangeView(1); Write('Мышь обнаружена с ',r.bx,' кнопками'); v:=1; r.ax:=1; intr($33, r); repeat r.ax:=3; intr($33, r); if r.bx and 1 <> 0 then begin case v of 1: v:=2; 2: v:=3; else v:=1 end; ChangeView(v); end; until r.bx and 2 <> 0; END.Эта программа "неуклюжая". Часто даже вид курсора проскакиваешь. Из-за этого пришлось ввести задержку в процедуре изменения вида курсора.
Да и драйвер мыши не ориентирован на такой стиль работы: программа опрашивает сама - не случилось ли что?
По-настоящему не так надо работать. Ведь можно определить в программе процедуру, которая будет вызываться драйвером мыши тогда и только тогда, когда что-либо произойдет. Причем, за какими событиями от мыши Вы хотите следить, сами определяете. Представляете, как удобно! Программа работает себе и работает, как надо. И только если нужно, то прерывается на какие-то действия.
Только очень важно учесть, что эта подпрограмма - обработчик событий от мыши будет вызываться очень часто. Так что ей нельзя поручать ничего медленного. Обычно такая подпрограмма заполняет какие-то глобальные поля и устанавливает флаг "ПРОИЗОШЛО!". Главная программа выполняя свою работу, должна следить за этим флагом. Если он установлен, то в удобное для нее время выполняет нужные действия и снимает флаг. Подпрограмма же, если флаг установлен, ничего не должна делать - еще прежнее сообщение не обработано!
Пример такой программы показан в следующем листинге. Здесь тоже ничего нового не делаем. Просто показываем, что это работает. Работать будем на графическом экране
ОЧЕНЬ ВАЖНО!!! Подпрограмма, которую будет вызывать драйвер мыши, не простая процедура. Это процедура обслуживает прерывание! ОБЯЗАТЕЛЬНО нужно "присобачить" к ее заголовку ключевое слово FAR;. Для обработчиков других прерываний нужно использовать interrupt, но мышиное прерывание - исключение. Без этого ничего не выйдет!
Правила вызова функции, устанавливающей обработчик прерываний от мыши, описаны в таблице
При вызове | AX = $0C | |||||||||||||||||||||||||||||
СX - маска событий (событий, которые Вы хотите получать от мыши)
| ||||||||||||||||||||||||||||||
ES - сегмент адреса Вашей подпрограммы, обработчика событий. Устанавливается: reg.ES:=Seg(MyMouHandler); | ||||||||||||||||||||||||||||||
DX - смещение адреса Вашей подпрограммы, обработчика событий. Устанавливается: reg.DX:=Ofs(MyMouHandler); | ||||||||||||||||||||||||||||||
Возвращает | ничего |
В регистре CX задается список событий, по которым Ваша подпрограмма будет вызываться, а полный адрес подпрограммы задается в паре регистровES:DX
Когда возникает одно из определенных битами регистра CX событий, вызывается Ваша подпрограмма и ей передается в регистрах процессора следующая информация:
program mouse_02b; uses DOS, CRT; procedure FillScreen; var f: File of char; c: Char; begin Assign(f, 'mou_02c.pas'); {$I-} Reset(f); {$I+} if IOResult <> 0 then Exit; ClrScr; While NOT EOF(f) and (WhereY < 25) do begin Read(f, c); Write(c) end; Close(f); end; var r: Registers; v: Integer; L_Pressed, R_Pressed: Boolean; Flag: Boolean; procedure MouInt; far; begin if Flag then Exit; Flag:=True; L_Pressed:=False; R_Pressed:=False; {Извините, но без ассемблерной вставки сейчас не обойтись: Нужно именно сейчас опросить состояние регистров процессора.} asm {Начинается ассемлерная вставка. Далее пойдут команды на языке ассемблера} test bx, 1 {проверяем (тестируем) первый бит регистра bx} JZ @1 {если не установлен, равен 0, то переходим к метке с номером @1 JZ это обозначение команды "Jump if Zerou" Это означает "прыгнуть" (перейти), если ноль} mov L_Pressed, 1 {Переместить (move по-английски) 1 (единицу) в переменную L_Pressed Паскаль поймет 1, как значение "ИСТИНА" (TRUE). Если перескочим эту команду, то в переменной останется значение "0" = "ЛОЖЬ" = "FASLE"} @1: {Теперь аналогично проверим второй бит регистра процессора BX} test bx, 2 jz @2 mov R_Pressed, 1 @2: end; {Ассемблерную вставку, начатую директивой ASM следует завершить END} end; function ChangeView(ViewNo: Integer): Integer; const Current: Integer = -1; {И здесь была ошибка в прежней программе} begin ChangeView:=Current; if ViewNo = Current then Exit; r.ax:=$0a; case ViewNo of 1: with r do begin bx:=0; cx:=$00ff; dx:=$4e00 end; 2: with r do begin bx:=0; cx:=$0; dh:=$1f; dl:=ord('+') end; 3: with r do begin bx:=1; cx:=7; dx:=8 end; else Exit; end; intr($33, r); end; BEGIN r.ax:=0; intr($33, r); if r.ax <> $FFFF then begin WriteLn('Мышь не обнаружена'); Halt(1) end; FillScreen; r.ax:=1; intr($33, r); {Нужно показать мышь на экране. Этого не делалось в неверной версии программы} {Установка обработчика} r.ax:=$0c; r.cx:=2 + 4 + 8; {Список событий: - нажата и отпущена левая кнопка; - нажата правая} r.es:=Seg(MouInt); r.dx:=Ofs(MouInt); intr($33, r); Write('Мышь обнаружена с ',r.bx,' кнопками'); v:=1; repeat if Flag then begin Flag:=False; if L_Pressed then case v of 1: v := 1; 2: v := 3; ELSE v := 1 end; {Этого END тоже не хватало ... Увы ... :(( } ChangeView(v); end; until R_Pressed; {Удаление обработчика. Так положено} r.ax:=$0c; r.cx:=0; r.es:=Seg(MouInt); r.dx:=Ofs(MouInt); intr($33, r); END.
Свои вопросы и предложения присылайте Борису
Мы приглашаем Вас и Ваших друзей к сотрудничеству. Напишите, какая проблема Вас лично интересует - и мы постараемся помочь Вам. Поделитесь со всеми, если Вам удастся найти красивое решение. Присылайте свои программы, и если они хороши, то опубликуем их с обязательным указанием Вашего авторства.
По всем вопросам можно писать либо в Гостевую книгу нашего сайта на www.turbopascal.tk, либо
мне,
Постараюсь ответить на все вопросы и учесть все разумные предложения
Рассылка поддерживается сайтом www.turbopascal.tk. При перепечатке ссылка на сайт обязательна