КАК добавить мышь?

КАК мне поможет мышь в моих программках?
КАК сделать так, чтобы мышка вышла из норки?
КАК прогнать мышь с экрана?
КАК отследить какая нажалась кнопка?
КАК отследить где моя мышь?

КАК загнать мышь в узкий ящик?
КАК вывести стрелочку, как в преславутой Windows?
КАК самому нарисовать себе курсор, не прибегая к стандартным средствам?

Вопрос: КАК мне поможет мышь в моих программках?

В двух словах о том, что может мышь, если ею грамотно управлять. Первым делом, используя мышь вы сделаете более дружественный интерфейс. Пользователь, привыкший к мыши в среде Windows, будет клясть вашу программу на чём свет стоит, если вы не позаботитесь об управлении с мыши. Хлопотно это. В Windows мышь по определению есть - без неё там делать нечего. Нам же придётся идти на множество ухищрений. Начнём с того, что в среде ДОС, под которую мы сейчас и пишем, про мышь и слыхом не слыхивали. Если мы хотим, чтобы она там появилась, надо хорошенько попросить об этом... микропроцессор. Делается это с помощью прерываний. То есть, на какое-то время мы остановим процессор и попросим его обратить внимание именно на то устройство, которое нам надо - мышь. У каждого устройства свой номер прерывания или вектор прерывания: у клавиатуры 60-й, у видеокарты 10-й, у мыши 33-й. Для того, чтобы компьютер сделал именно то, что нам нужно перед вызовом прерывания, надо записать в соответствующие регистры процессора соответствующие числа. В справочнике по сервисным функциям DOS подробно записано в какой регистр что нужно записать, чтобы получить то-то.
Регистров у микропроцессора прибавляется с каждой новой маркой. Однако, незыблемыми остаются некоторые регистры, которые существовали у слабеньких и жалких процессоров времен DOS.
Регистры AX, BX, CX, DX и др. Поскольку регистры эти двухбайтовые, для удобства они разбиты на два. Например, регистр AX разбит на AH (high - старший), AL (low - младший). Так они образуют регистровую пару. Записав какие-то числа в регистры, вызвав прерывание 33, мы получим мышь. Механизм регистров - слабое место в Windows. Он используется в компьютерах с незапамятных ассемблерных времён, когда для того, чтобы что-то сделать приходилось работать напрямую с регистрами и прерываниями.
Добрые дядюшки Деннис Ритчи и Бьярн Страуструп подарили нам Си и Си++, где с помощью макросов типа #define огромные залежи ассемблерных кодов приведены к более читабельному виду в виде функций: printf(), scanf() и др, за которыми в действительности кроются регистры и прерывания. По сути, вы можете не учить никакой язык и пользуясь справочником всё делать через регистры. Но это очень муторно, код совершенно не читабельный, да и... овичнка выделки не стоит! Разработчики Windows всеми силами старались уйти от этого механизма. Сама мысль о том, что Человек получает власть над Компьютером для них кажется жуткой. Вот почему, создавая свои слащавые и примитивные программы, они уводили его всё дальше и дальше от власти, которая была ему дана. С появлением файловой системы NTFS и новых версий Windows, о DOS и вовсе пришлось забыть. По сути, вы можете использовать только готовое программное обеспечение, API функции в крайнем случае. То есть, им, этим разработчикам лучше знать что вы хотите сделать на компьютере, в то время, как прерывания власть давали колоссальную. Вы можете выводить свои сигналы на параллельные и последовательные порты компьютера, распределять память, в том числе и видеопамять, записывать файлы в те кластеры винчестера, в которые вы хотите, а не в те, которые хочет ОС-ка, увеличивать или уменьшать скорость повтора клавиатуры, работать с мышью, дисковым пространством и многое, многое другое. Вы можете деже программно зажигать светодиоды "Caps Lock" и "Num Lock" на клавиатуре!
Вобщем, иначе с мышью работать мы не сможем. Однако, какие недостатки таят в себе прерывания? Допустим, мы хотим нарисовать на экране кнопку, нажимая которую пользователь получит результат. Опустим детали рисования. Пусть мы её уже нарисовали. Дальше, нам надо:
*вывести мышь на экран (инициализировать её)
*задать режим, при котором отслеживаются координаты и номер нажатой кнопки
*в теле цикла проверки проследить - если нажата левая кнопка мыши, и при этом координаты мыши по х и по у были в пределах кнопки, (коордианты кнопки нам известны), то запустить соответствующую процедуру. Представляете, какой громоздкий if получится?
Кроме того, прерывания очень ненадёжная вещь. Сотни раз в секунду меняется содержимое этих регистров. Каждый тянет одеяло на себя. Поэтому, чтобы не было мучительно больно, лучше объявить переменные, которые будут хранить значения регистров, чем работать с регистрами напрямую. Кстати, для того, чтобы использовать сервисные функции ДОС, надо использовать библиотеку dos.h. Прошу любить и жаловать.

Вопрос: КАК сделать так, чтобы мышка вышла из норки?

Чтобы не быть голословным я перво-наперво покажу вам мышь на экране вашего компьютера. Для этого нам необходимо сгенерировать прерывание №33 - прерывание мыши. Это делают сразу несколько функций библиотеки dos.h. Выберем самую простую: geninterrupt(№ прер.). Просто 33 в скобках этой функции мы написать не можем, так как это число в 16-ричной системе счисления. Шестнадцатиречиные числа на языке СИ записываются так: 0x33, 0x16, 0x60, 0x21... Имена регистров пишутся с большой буквы и начинаются со знака подчёркивания: _AX, _DX, _BL, _CX...

//Вывод на экран мыши
#include <dos.h>
#include<conio.h>
main()
{
_AX=1; //заносим в регистр AX 1
geninterrupt(0x33); //генерируем 33-е прерывание
getch();
}

Мышь появится на экране в виде белого прямоугольничка, так как мы работаем в текстовом режиме. Причём, после выхода из программы, если вы работаете в ДОС без мыши, она не уберётся, так как вы не приказали компьютеру мышь погасить. Так как на дворе 2000-ые, врядли вы работаете в чистом ДОСе. Но для того, чтобы скомпилировать программу, работающую с библиотекой DOS.H, вам не подойдут компиляторы, работающие под Windows (Visual C++, Borland C++5.0, C++ Builder...). Лучше всего пользоваться Borland C++ 3.0 или 3.1 или Turbo C 2.0, на худой конец. Они не капризны и искренне уверенны, что работают под DOS, так как написаны в конце 80-х. Windows же закрывает глаза на их работу, так как они работают в эмуляции MS-DOS.

Вопрос: КАК прогнать мышь с экрана?

Иногда нужно убирать мышь - после выхода из программы или же, чтобы мышь не мельтешилась при заставках. Это сделать так же просто, как и вызвать её.

//Убираем мышь с экрана
#include <dos.h>
#include<conio.h>
#include<stdio.h>

void main()
{
clrscr();
_AX=1; //заносим в регистр AX 1
geninterrupt(0x33); //генерируем 33-е прерывание
printf("Мышь, пожалуйста!");
getch();

printf("\n\nМыши больше нет...\n");
_AX=2;
geninterrupt(0x33);
getch();
}

Меняя содержиоме регистров и вызывая то же самое прерывание, мы получаем разные результаты.

Вопрос: КАК отследить какая нажалась кнопка?
Вопрос: КАК отследить где моя мышь?


Это очень просто. Я напишу вам программу, которая будет постоянно показывать координаты мыши, а если вы нажмёте кнопку, то и номер кнопки. Естественно, что ни колёсико, ни среднюю кнопку задействовать вы не сможете. Но для духовных людей и этого достаточно.

#include<dos.h>
#include<conio.h>
#include<stdio.h>

main()
{
int x,y, button;
_AX=1;
geninterrupt(0x33);
getch();
do {
_AX=3;
geninterrupt(0x33);
x=_CX;
y=_DX;
button=_BL;
gotoxy(1,1);
printf("Кооpд х=%d у=%d кнопка=%d",x,y, button);
delay(100);
clrscr();
}while(!kbhit());
}


Разбор полётов. Обратите внимание, что я присвоил значения регистров переменным, а уж после вывел их на экран. Что касается кнопки, вы можете использовать в своих программах нажатие одновременно обеих кнопок - левой и правой. Тогда значение регистра _BL будет 3.
Вот конструкция для написания своих кнопок. Пусть мы хотим отследить нажатие левой кнопки в пределах (10,10, 100, 40), тогда:

if((x>10)&&(x<100)&&(y>10)&&(y<40)&&(button==1))
{

...//Здесь вы уж сами что-то вставите...
}

Грамотно используя графические функции Turbo C, вы сможете сделать что-то вроде своего Windows в миниатюре, предусмотрев как выглядит кнопка и как выглядит нажатая кнопка. В момент нажатия на кнопку мышью, вы подменяете имевшийся рисунок кнопки на рисунок нажатой кнопки, задаёте какую-то задержку функцией delay(), а потом перерисовываете кнопку. Лет 15 назад все так делали.

Вопрос: КАК загнать мышь в узкий ящик?

В студенческие годы я как-то должен был написать игру "Морской бой". Корабли пользователя были в виде клеточек на игровом поле, напоминающем шахматную доску. Расставлялись они мышью. Можно было каждый раз отслеживать находится ли мышь в пределах игрового поля при расстановке кораблей, но я сделал проще. Мышь не выходит за пределы поля, и хочешь-не хочешь, а корабли расставлять надо. Как только последний корабль расставлен, пользователь получает в распоряжение всё поле, но уже поздно. Идёт война!
Делается это за два раза. В первый раз вы передаёте процессору координаты левого верхнего угла рамки по х и по у, а за второй заход - правого нижнего. Прерывание придётся генерить два раза. Если вы хотите задавать несколько раз рамки, целесообразно задать рамку функцией, как в программе ниже:

//Зададим рамки для нашей мыши:
#include<dos.h>
#include<conio.h>

//Задание рамок для мыши
void box (int x1,int y1,int x2,int y2)
{
_CX=x1;
_DX=x2;
_AX=7;
geninterrupt (0x33);
_CX=y1;
_DX=y2;
_AX=8;
geninterrupt (0x33);
}

//Инициализация мыши
void mouse()
{
_AX=1;
geninterrupt(0x33);
}


void main()
{
int a=0, d=0;
int x1=10, y1=10, x2=100, y2=100; //Зададим границы
initgraph(&a, &d, "c:\\tc\\bgi"); //Инициализируем графику
setfillstyle(1,1); //Зададим стиль заполнения фигуры
bar(x1,y1,x2,y2); //Рисуем рамку
mouse(); //Показываем мышь
box(x1, y1, x2, y2); //Вербуем в рамки
getch(); //Любуемся...
}



Вопрос: КАК вывести стрелочку, как в преславутой Windows?

Стрелочка (ARROW), почти как в Windows у вас появится только тогда, когда вы перейдёте в графический режим, перед тем, как вызвать мышь.

#include<graphics.h> //Инициализируем графическую библиотеку
#include<conio.h>
#include<dos.h>
void main()
{
int a=0, b=0; //задаём графический режим и разрешение
initgraph(&a, &b, "c:\\tc\\bgi\\"); //инициализируем графику и указываем путь к папке с файлом egavga.bgi
outtext("Graphic mouse! You're welcome to Windows!"); //Выводим текст в графическом режиме
_AX=1; //Вызываем мышку
geninterrupt(0x33);
getch();
}

Вопрос: КАК самому нарисовать себе курсор, не прибегая к стандартным средствам?

В компиляторах нового типа есть встроенные средства для рисования своих собственных курсоров. В Visual C++ курсор относится к ресурсам, его можно хранить курсор в виде файла **.cur в папке с программой. Так многие и делают. Но пользуясь теми же самыми прерываниями, можно сделать совершенно "страшную" вещь: нарисовать свой кусор в ДОС! Уж здесь свобода творчества не ограничивается ничем!
Я дам вам только две маски. Одна из них фигурная стрелочка - не стандартная стрелка Windows, вторая - кошечка. Кому что нравится. Можете нарисовать, например, песочные часы или знак вопроса. В начале 90-х, например, возможности рисования импровизированного курсора использовались в играх довольно широко. Курсор то превращался в скачущего рыцаря, то в бегущего человека, то в руль, если речь шла об автогонках, то в яблоко, если вы играли в какой-то квест. Сегодня Windows позволяет нам использовать анимированные и даже 3-х мерные курсоры, но этот пример был, есть и остаётся основой основ!

#include <graphics.h>
#include<conio.h>
#include<stdio.h>
#include<dos.h>

//Зададим несколько масоk глобально.
//Всё это беззнаковые массивы 16-ричных чисел
//Стрелочка

static unsigned mask[] = {

//Половина массива - фон
0xffff, 0xffff,0xffff, 0xffff,
0xffff, 0xffff,0xffff, 0xffff,
0xffff, 0xffff,0xffff, 0xffff,
0xffff, 0xffff,0xffff, 0xffff,

//Половина массива - рисунок
0x8000, 0xC000, 0xA000, 0x9000,
0xA800, 0xB400, 0xBA00, 0xBD00,
0xBE80, 0xBF40, 0xBFA0, 0xBFd0,
0xBFE0, 0xAE00, 0xA300, 0xE300
};

//Кошечка
static unsigned kitty[] = {

/*AND*/
0xffff, 0xffff,0xffff, 0xffff,
0xffff, 0xffff,0xffff, 0xffff,
0xffff, 0xffff,0xffff, 0xffff,
0xffff, 0xffff,0xffff, 0xffff,

/*XOR*/
0x0, 0x0, 0x2202, 0x2202,
0x2202, 0x3602, 0x2A06, 0x36F6,
0x1FFE, 0x1FFE, 0x1636, 0x1636,
0x1636, 0x1636, 0x1636, 0x0
};

//Палец, как в Internet Explorer
static unsigned finger[] = {

//and
0xffff, 0xffff,0xffff, 0xffff,
0xffff, 0xffff,0xffff, 0xffff,
0xffff, 0xffff,0xffff, 0xffff,
0xffff, 0xffff,0xffff, 0xffff,

//xor
0x700, 0xD80, 0x1180, 0x8E0,
0xE888, 0xB8AC, 0x982A, 0xC00A,
0x2002, 0x2002, 0x1004, 0x1008,
0x810, 0x420, 0x420, 0x7E0
};

//Функция вывода графической пользовательской мыши
//row, col - какая координата курсора будет считаться указателем.
//По умолчанию 1, 1, как в стрелке кусора Windows.

void Gr_Mouse(int row, int col, unsigned int far * mask)
{
struct REGPACK r; //Создаём экземпляр структуры REGPACK - стандартной структуры dos.h
r.r_ax=9; //Заносим в регистр ax 9
r.r_bx=col; //Это ещё один способ занесения данных в регистры
r.r_cx=row; //И работы с прерываниями - через структуру REGPACK и функцию intr, в которую
//передаётся эта структура и номер прерывания.
r.r_es=FP_SEG(mask); //Задаём адрес в памяти нашей маски
r.r_dx=FP_OFF(mask);
intr(0x33, &r); //Вызовём 33-у прерывание с заполненной структурой
}

void main()
{
int a=0, d=0;
initgraph(&a, &d, "c:\\tc\\bgi");

_AX=1;
geninterrupt(0x33); //инициализация мыши

Gr_Mouse(1,1, kitty); //Можете вместо kitty подставить mask или finger, картинка поменяется!
getch();
closegraph();

}


Маленькое лирическое отступление. Вы наверное уже задумались над тем, где я беру эти 16-ричные числа для рисунка? Ответ очевиден. Представим рисунок кусора в виде координатной сетки 16х16. Тогда в двоичной системе счисления, верхний ряд будет как 1111111100000000 : 8 единиц и 8 нулей. С помощью стандартного калькулятора Windows переводим это число в 16-ричную систему. Получаем: 0xFF00. Переводя строку за строкой, мы получим 16 значений, которые мы перечисляем через запятую в массве. Если я у вас ещё не отбил охоту к творчеству, дерзайте!

                                 
                                 
                                 
                                 
                                 
                                 
                                 
                                 
                                 
                                 
                                 
                                 
                                 
                                 
                                 
                                 

Назад Содержание Вперёд