Помощь - Поиск - Пользователи - Календарь
Полная версия: МЫШЬ. Все о программировании мыши.
Форум «Всё о Паскале» > Pascal, Object Pascal > Задачи > FAQ
Altair
Все операции, связанные с мышью, в программах выполняются посредством функций
прерывания int $33. Общий формат их вызова таков:
ASM
MOV AX, Number_Function
{...}
INT $33
END;

В отличии от остальных прерываний, функции которых определяются значением в регистре AH, прерывание INT $33 используют весь регистр AX.

Первой используемой в программе функцией этого прерывания должна быть функция $00 (инициализация мыши). Обычно эта команда используется только один раз - в самом начале программы. Для того, чтобы программы могли использовать мышь, предварительно необходимо загрузить драйвер. Этот драйвер обычно запускается командой в файле CONFIG.SYS или AUTOEXEC.BAT (В среде Windows драйвер мыши загружается согласно записи в реестре).

Ниже приведены основные термины, касающиеся мыши:
  • пиксель - наименьший адресуемый элемент экрана.
  • указатель мыши - в текстовом режиме это мерцающий прямоугольник в инверсном видеорежиме, в графическом режиме его вид может быть произвольным, но обычно это стрелка.
  • микки(шаг) - наименьшее расстояние, перемещение на которое мышь может зарегистрировать (т.е. расстояние регистрируемое датчиками мыши). Обычно 0.125 мм.
  • счетчик мыши - отсчитывает число микки (шагов), на которое мышь сместилась горизонтально или вертикально. Драйвер мыши отсчитывает эти перемещения и соответственно перемещает указатель мыши на экране.
  • пороговая скорость - определенная скорость перемещения
    (микки/с), при превышении которой скорость перемещения курсора по экрану удваивается. По умолчанию - 64 микки в секунду.
Рассмотрим основные функции прерывания INT $33.

Инициализация мыши
Для инициализации мыши необходимо вызвать функцию $00. Эта функция должна вызываться первой из всех команд обработчика. Для ее вызова необходимо поместить в регистр AX значение $00 и вызвать прерывание INT $33.
Function InitMouse:boolean;
var
ResultRegAX:word;
begin
asm
mov ax,$00
int $33
mov ResultRegAX,ax
end;
If ResultRegAX=$FFFF then InitMouse:=true else InitMouse:=false
End;

Приведенная функция пробует инициализировать мышь, и если мышь инициализирована, возвращает true.
При инициализации, в регистр BX помещается количество кнопок у обнаруженной мыши. Поскольку не всегда требуется эта информация, можно вынести определение количества кнопок у мыши в отдельную функцию:
function ButtonMouse:byte;
var
ResultRegBX:word;
Begin
Asm
mov ax,$00
int $33
mov ResultRegBX,bx
End;
ButtonMouse:=ResultRegBX
end;

Функция возвращает количество кнопок у мыши. Эту функцию можно использовать в любом месте программы. (Это и есть тот случай, когда функцию $00 прерывания $33 вызывают второй раз).
При инициализации мыши, указатель не появляется на экране.

Отображение указателя мыши
Для отображения указателя мыши на экране, необходимо использовать функцию $01.
Драйвер мыши содержит флаг указателя, определяющий, должен ли указатель отображаться на экране. Указатель отображается, если флаг установлен в 0, и скрывается, если флаг установлен в любое другое значение. Изначально его значение -1. Функция $01 увеличивает его до 0, и курсор становится видим.
Procedure ShowMouse; assembler;
Asm
Mov AX,$01
Int $33
end;

Зарезервированное слово Assembler, которое было использовано в приведенной процедуре, указывает компилятору, что процедура целиком написана на ассемблере.

Cокрытие указателя мыши
Часто в процессе работы программы, требуется на время скрыть указатель мыши, например, при выводе графики. Для этого используют функцию $02.
Следующая процедура, при вызове, скрывает указатель мыши:
Procedure HideMouse; assembler;
Asm
Mov AX,$02
INT $33
End;

Перечисленные процедуры и функции можно так использовать в программе:

{...}
var
kolichestvoKnopok:byte;
{...}
begin
{...}
If InitMouse then
begin
{мышь инициализирована}
kolichestvoKnopok:=ButtonMouse;
ShowMouse;{отображение указателя}
{...}
HideMouse; {сокрытие указателя}
end else {мыши нет или не поддерживается}
{...}



Получение состояния мыши
Функция $03 позволяет получить данные о текущем состоянии мыши.
После вызова этой функции, в регистр BX помещается информация о состоянии кнопок, в регистр CX координата по горизонтали (х), а в регистр DX координата по вертикали (у)*(см. примечание!).
Следующая процедура возвращает координаты указателя.
Procedure GetMouseXY(VAR KoordX,KoordY:Word );
var
ResultCX,ResultDX:word;
begin
Asm
mov ax,$03
int $33
mov ResultCX,CX
mov ResultDX,DX
end;
KoordX:=ResultCX;
KoordY:=ResultDX
end;

При написании программ, очень часто нужно проверять не находится ли указатель в заданной прямоугольной части экрана. (например на кнопке).
Следующая процедура, возвращает true, если указатель находится в зоне x1,y1,x2,y2, где x1-левая граница зоны, x2 - правая граница, y1 - верхняя граница, y2 - нижняя граница.
Function MouseIn(X1,Y1,X2,Y2:word):boolean;
var
rdx,rcx:word;
begin
asm
mov ax,$03
int $33
mov rdx,dx
mov rcx,cx
end;
MouseIn := (rcx>=X1) and (rcx<=X2) and (rdx>=Y1) and (rdx<=Y2)
end;

Функция $03, как уже было сказано, возвращает состояние кнопок мыши.
Function ButtonPressed:byte;
var
rbl:byte;
begin
asm
mov ax,$03
int $33
mov rbl,bl
end;
ButtonPressed:=rbl
end;

Функция возвращает следующие значения:
  • 1 - если нажата левая кнопка.
  • 2 - если нажата правая кнопка.
  • 3 - если нажаты левая и правая кнопки
  • 4 - если нажата средняя кнопка (колесико)
  • 5 - если нажаты левая кнопка и колесико
  • 6 - если нажаты правая кнопка и колесико.
  • 7 - если нажаты все кнопки (2 кнопки и колесико).
------------------------------------------
* все координаты исчисляются в пикселях!!! Даже в текстовом режиме!!! Поэтому для перемещения указателя мыши, скажем на 3 стоку, надо указать координату 8*3 = 24 (на одну строку в текстовом режиме идет 8 пикселей.

Установка указателя мыши в нужную позицию
Для установки указателя мыши в нужную позицию (X,Y) используйте следующую процедуру:
Procedure GotoMouseXY(x,y:word); assembler;
asm
mov ax,$04
mov cx,x
mov dx,y
end;

Не забудте умножить координаты на 8, если вы используете текстовый режим!

Регистрация события нажатия клавиши.
В модуль CRT включена очень полезная функция, возвращающая true, если была нажата клавиша, вот ее аналог для мыши. Функция возвращает true, если была нажата любая клавиша.
Function MousePressed: Boolean;
var
resultbx:word;
begin
asm
Mov ax, $03
Int $33
mov resultbx,bx
end;
MousePressed := resultbx<>0
end;


Определение количества нажатий клавиш
Для определения количества нажатия кнопок мыши используют функцию $05.
Следующая функция возвращает количество нажатий левой(при num=0), средней(при num=2),правой(при num=1) кнопкой мыши с момента последнего вызова данной процедуры. И еще они возвращают координаты мыши при последнем нажатии данной клавиши.
Function getnum(num:word; var x,y:word):word;
var
kn,ResultCX,ResultDX:word;
begin
asm
mov ax,$05
mov bx,num
int $33
mov ResultCX,cx
mov ResultDX,dx
mov kn,bx
end;
getnum:=kn;
x:=ResultCX;
y:=ResultDX
end;

Например, если надо получить координаты мыши при последнем нажатии левой кнопки мыши, достаточно вызвать процедуру так:
getnum(0,x,y);
Для такого вызова функции, включите расширенный синтаксис директивой
компилятору {$X+} или в настройках компилятора.

Пределы перемещения указателя Если в программе требуется ограничить область перемещения указателя, то для этого необходимо использовать функции $07 и $08.
Ограничение перемещения указателя по горизонтали.
Procedure LimHor(max,min:word); assembler;
asm
mov ax,$07
mov cx,min
mov dx,max
int $33
end;

Ограничение перемещения указателя по вертикали.
Procedure LimVer(max,min:word); assembler;
asm
mov ax,$08
mov cx,min
mov dx,max
int $33
end;

Чтение содержимого счетчиков перемещения мыши
эта операция возвращает величины смещения мыши по вертикали и горизонтали (в микки) со времени последнего вызова процедуры.
Procedure GetMikki(var x,y:integer);
var
RCX,RDX:word;
begin
asm
mov ax,$0B
int $33
mov RCX,cx
mov RDX,dx
end;
x:=RCX; y:=RDX
end;
Altair
Область исключения указателя
Если в какой-то области экрана указатель мыши не должен отображаться, можно запретить это делать используя функцию $10
координаты x1,y1,x2,y2 - соответственно - левая граница(горизонт), гарница сверху, правая граница, нижняя.
Procedure SetArea(x1,y1,x2,y2:word); assembler;
asm
mov ax,$10
mov cx,x1
mov dx,y1
mov si,x2
mov di,y2
int $33
end;


Установка пороговой скорости
Как уже отмечалось, пороговая скорость - скорость(микки/с) при превышении которой, указатель начинает перемещаться по экрану в 2 раза быстрее.
Значение по умолчания - 64 микки/с
Procedure SetSpeedMouse(mikkiS:word); assembler;
asm
mov ax,$13
mov dx,mikkiS
end;


Установка чувствительности мыши
Чувствительность мыши - это минимальное расстояние в микки, ан которое нужно переместить мышь, чтобы указатель мыши на экране начал движение.
Эта функция позволяет изменять чувствительность мыши отдельно по вертикали и горизонтали.
Значения по умолчанию:
Горизонтальная чувствительность 8
вертикальная чувствительность 16
пороговая скорость перемещения 64 (см. "установку пороговой скорости")
Procedure SetMouseOptions(hor,ver,porog:word); assembler;
asm
mov ax,$1A
mov bx,hor
mov cx,ver
mov dx,porog
int $33
end;

Все перечисленные функции, удобно поместить в модуль.


-----------------------------------------------------------------

Некоторые замечания.
  • При использовании некоторых видеодрайверов (например vesa256), в видеорежимах, с большим разрешением экрана (например 1024*768) указатель мыши не отображается.
  • В графическом режиме указатель мыши может иметь произвольную форму (о том, как его менять, см. ниже).
  • Горизонтальные и вертикальные координаты указателя мыши измеряются в пикселах.
  • В текстовом режиме, одна строка - 8 пикселей, один столбец - 4 пиксела.
Форма указателя в графическом режиме

uses dos, {...}
{...}
Procedure MouseGraphCursor(var ScrCurMask;X,Y: Byte);
var
reg:registers;
begin
with Reg do
begin
ax := $9;
bx := X;
cx := Y;
es := seg(ScrCurMask);
dx := ofs(ScrCurMask);
Intr($33,Reg)
end
end;

Для использования этой процедуры, пропишите в разделе подключаемых модулей, модуль DOS. Эта процедура специально написанна без использования ассемблера, чтобы у Вас, читающих это, не возникло ощущения отсупления в этой теме, от Паскаля. Все предыдущие процедуры и функции тоже могут быть написанны на Паскале, без использования ассемблера (о переводе Паскаль <--> Ассемблер скоро появиться тема...)
Здесь ScrCurMasc - это 64 байтный массив, определяющий маску экрана и маску указателя.
x,y - смещение координатной точки относительно левого верзенго угла указателя.
Попробуем создать свой указатель.
Сразу скажу, что максимальный размер указателя - 16*16 пикселей.
Для начала нам надо создать 64 байтный массив.
Это делаеться следующим образом:
первые 32 байта заполним единицами:
const
a:array[1..64] of byte = (1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
{...}


Теперь составляем вторую часть массива (тоже 32 байта )
Тут правило такое: (для удобства)
распологаем по 2 столбика:

a:array[1..64] of byte = (1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,

255,255,
255,255,
255,255,
255,255,
255,255,
255,255,
255,255,
255,255,
255,255,
255,255,
255,255,
255,255,
255,255,
255,255,
255,255,
255,255);


Теперь, если мы попробуем создать такой указатель:
Var grDriver : Integer;
grMode : Integer;

Procedure MouseGraphCursor(var ScrCurMask;X,Y: Byte);
var reg:registers; begin with Reg do begin ax := $9;
bx := X; cx := Y; es := seg(ScrCurMask); dx := ofs(ScrCurMask);
Intr($33,Reg) end end;

Begin
grDriver:=Detect;
InitGraph(grDriver, grMode, '');
initmouse;
showmouse;
testmous.mousegraphcursor(a,0,0);
readkey;
closegraph;
end.

(представленно в трудночитаемой форме, т.к. я это вырезал из окна TP.)
Предварительно не забудте прописать uses dos,graph,crt;
И описать константу a, как мы это уже сделали.
На экране вы увидите белый квадрат 16*16 пикселей. Это получаеться следующим образом:
первый столбец (из двух) в константе-массиве, это вторая часть указателя мыши!!!
т.е.
указатель:

********00000000
********00000000
********00000000
********00000000
********00000000
********00000000
********00000000
********00000000
********00000000
********00000000
********00000000
********00000000
********00000000
********00000000
********00000000
********00000000


нули здесь показывают вторую часть указателя мыши (описываеться первым столбцом в массиве!!!!).
И наоборот, первая часть описывается вторым столбцом массива.
Каждой число типа byte (эл-т массива) - это как раз 8 бит (как раз половина указателя).
Т.е. для создания указателя придется пользоваться двоичной арифметикой.
Проще всего запустить стандартный калькулятор в Windows, переключить его в инженерный вид, и в двоичную форму, записать нужное число и конвертировать его в десятичную или шестадцатиричную.
Ну и в заключении, вот пример массива - маски, позволяющий создать указатель в виде восклицательного знака:

{
00000001 10000000
00000111 11100000
00000111 11100000
00001111 11110000
00001111 11110000
00001111 11110000
00001111 11110000
00001111 11110000
00000111 11110000
00000011 11000000
00000001 10000000
00000001 10000000
00000000 00000000
00000111 11100000
00000111 11100000
00000011 11100000
}
a:array[1..64] of byte = (1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
128,1,
224,7,
224,7,
240,15,
240,15,
240,15,
240,15,
240,15,
224,7,
192,3,
128,1,
128,1,
0,0,
224,7,
224,7,
192,3);



Если у Вас есть какие-либо вопросы, дополнения, или предложения, напишите сюда, или мне лично, на E-MAIL ( zharkih@list.ru ).
Altair
На основе вышеизложенной иформации, составлен модуль для работы с мышью.
скачать с pascal.dax.ru
перейти на сайт.

<div align="center">Краткое описание модуля:</div>

Инициализация мыши
Function InitMouse:boolean;

Функция возвращает количество кнопок у мыши
function ButtonMouse:byte;

Отображение указателя мыши
Procedure ShowMouse;

Скрыть указатель
Procedure HideMouse;

Возвращаются координаты указателя
Procedure GetMouseXY(VAR KoordX,KoordY:Word );

Возвращает true, если указатель находится в
заданной области X1,Y1,X2,Y2

Function MouseIn(X1,Y1,X2,Y2:word):boolean;

Следующая Функция возвращает следующие значения:
1 - если нажата левая кнопка.
2 - если нажата правая кнопка.
3 - если нажаты левая и правая кнопки
4 - если нажата средняя кнопка (колесико)
5 - если нажаты левая кнопка и колесико
6 - если нажаты правая кнопка и колесико.
7 - если нажаты все кнопки (2 кнопки и колесико).

Function ButtonPressed:byte;

Установка указателя мыши в позицию X, Y
Procedure GotoMouseXY(x,y:word);

Возвращает true, если была нажата любая клавиша.
Function MousePressed: Boolean;

Следующая функция возвращает количество нажатий
левой(при num=0), средней(при num=2),
правой(при num=1) кнопкой мыши с момента
последнего вызова данной процедуры.

Function getnum(num:word; var x,y:word):word;

Ограничение перемещения указателя по горизонтали.
Procedure LimHor(max,min:word);

Ограничение перемещения указателя по вертикали.
Procedure LimVer(max,min:word);

возвращает величины смещения мыши по вертикали и
горизонтали (в микки) со времени последнего
вызова процедуры.

Procedure GetMikki(var x,y:integer);


Область исключения указателя
координаты x1,y1,x2,y2 - соответственно -
левая граница(горизонт),
гарница сверху, правая граница, нижняя.

Procedure SetArea(x1,y1,x2,y2:word);

Установка пороговой скорости
Значение по умолчания - 64 микки/с

Procedure SetSpeedMouse(mikkiS:word);

Установка чувствительности мыши
procedure SetMouseOptions(hor,ver,porog:word);
Altair
FAQ ...
...
Изменение вида курсора в графическом режиме.
Альтернативный вариант - пишем свой драйвер на паскале.

Одна из функций прерывания $33 -$0C служит для определения пользовательской процедуры, которая будет вызываться при возникновении прерывания $33 если буду выполнять условия, заданные в маске.
Маска -это двоичное число, каждый бит которого представляет логическую функцию условия.
маски:

Цитата
бит 0 вызов при перемещении мышки
бит 1 вызов при нажатии левой кнопки
бит 2 вызов при отпускании левой кнопки
бит 3 вызов при нажатии правой кнопки
бит 4 вызов при отпускании правой кнопки
бит 5 вызов при нажатии средней кнопки
бит 6 вызов при отпускании средней кнопки
бит 7 зарезервирован (оставить 0)
бит 8 зарезервирован (оставить 0)

При выходе из прикладной программы все вызовы должны быть запрещены ( т.е. все биты должны равняться 0 ), иначе следующее прерывание мышки ( перемещения, нажатия на клавиши ) приведет к обращению к освободившейся памяти, что, как вы уже догадались, вызовет непредсказуемые последствия.

------------------------------------------------------------

Реализация.
Я взял для демонстрации приема замены обработчика на свой - мой модуль, выложенный на сайте (ссылка выше). Немного изменим его. В секцию INTERFACE модуля, добавим вот такой код:
const
n=40;
Type MC=Array[1..50,1..50] of byte;
Var
_Cursor:MC;

Это описание вида курсора. (примитивно...)

и добавим процедуру
Procedure SetMouseEventManager;


Теперь в секцию IMPLEMENTATION добавим в самое начало:
Var
x,y:word;
pr:boolean;

и внизу опишем процедуры (2 - одна из них открыта для использования в основной программе, другая - сам обработчик).
Procedure SetMouseEventManager;
var
saddr,oaddr:word;
a,i,j:byte;
begin
hidemouse; {скроем стандартный курсор, что бы они не наплывали друг на друга}
pr:=true; {это логическую переменную мы ввели что бы нельзя было включить стандартный курсор. }
for i:=1 to n do for j:=1 to n do _cursor[i,j]:=15;
{здесь по идее должна создаваться форма указателя. элементами матрицы явл. значения цвета.}

a:=0; {маска - мы обрабатываем только перемещение мыши}
saddr:=seg(GrMouseVGA); {узнаем сегмент нашего обработчика}
oaddr:=ofs(GrMouseVGA); {узнаем смещение процедуры обработчика}
asm
pusha {сохраняем регистры в стеке. }
mov ax,0Ch {вызываем функцию}
lea cx,a {помещаем маску}
mov es,saddr {... ES:DX - адрес процедуры обработчика}
mov dx,oaddr
int 33h {вызываем мышиный сервис}
popa {восстанавливаем регистры из стека}
end;
end;

Я привел код процедуры из модуля но с комментариями.
------------------------------------------------------------
ВАЖНО!
Включите в настройках компилятора :
Цитата
286 instructions

------------------------------------------------------------

Все остальное можете посмотреть в коде, который я присоединил к сообщению.
Я не старался улучшить этот модуль, доведя этот способ замены обработчика до совершенства, так как вряд-ли это кому-то нужно...
Все это лишь эксперименты... Как Вы наверно заметили, этот способ довольно сложен, т.к. требует написания обработчика почти исключительно на ассемблере. так например стоит нам попробовать вызвать процедуру writeln из обработчика, произойдет ошибка Run-Time Error 1795 - вообще недокументированная :)

Одним словом, результат работы чуть неправильно составленного обработчика может быть непредсказуем. Тот код что я предоставил - стабилен, я его протестировал, но пока тестировал - Windows 2000 раз 6 останавливала работу эмулятора ДОС из-за разных ошибок. Это недостатки метода.

Теперь преимущества метода.
Теоретически, используя этот метод, можно выводить 16 цветные курсоры любой формы.
Это текстовая версия — только основной контент. Для просмотра полной версии этой страницы, пожалуйста, нажмите сюда.