// Done by TheTutor -- 5/27/02
// Перевод © 2004 Евгений Казеко
// www.gamecoder.kazeko.com
// evgeniy@kazeko.com
/* Эта обучалка о получении ввода с помощью мыши в приложениях Win32.
В частности, мы узнаем, как получать текущие координаты мыши. Мы получим
эту информацию в WinProc(), проверяя сообщение WM_MOUSEMOVE.
Это приложение достаточно простое. Каждый раз, когда мышь передвигается, мы
будем отображать текущие координаты курсора (x,y) в центре экрана.
*/
#include <windows.h>
#include <stdio.h>
#define WIN_WIDTH 320
#define WIN_HEIGHT 240
#define class_name "GT_MouseInput"
// Эта функция будет печатать передаваемые координаты мыши в центре окна
void PrintMouseCoords(HWND hwnd, int xPos, int yPos);
// Стандартная callback функция
LRESULT CALLBACK WinProc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam);
int WINAPI WinMain(HINSTANCE hinstance, HINSTANCE hprev, PSTR cmdline, int ishow)
{
HWND hwnd; // Ссылка на наше окно
MSG msg; // В этой переменной будут сохраняться сообщения (такие, как
// щелчок мышью), которые может получить наше окно.
WNDCLASSEX wndclassex = {0}; // Это наш "класс окна". "EX" означает
// "extended style" (расширенный стиль),
// который дает нам больше опций, когда
// мы создаем наше окно (хотя мы и проигнорируем их).
// Заполняем нужные нам поля
wndclassex.cbSize = sizeof(WNDCLASSEX); // Всегда должно быть установлено
wndclassex.style = CS_HREDRAW | CS_VREDRAW; // Стиль класса окна
wndclassex.lpfnWndProc = WinProc; // Указатель туда, где определена WinProc()
wndclassex.hInstance = hinstance; // Ссылка на экземпляр нашего окна
wndclassex.lpszClassName = class_name; // Имя нашего класса окна
wndclassex.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); // Устанавливаем фоновый
// цвет окна - белый
RegisterClassEx(&wndclassex); // Регистрируем класс окна, чтобы вызовы CreateWindow()
// и CreateWindowEx() знали, какой класс окна использовать
// при создании окна
hwnd = CreateWindowEx(NULL, // Дополнительные атрибуты окна, NULL - их нет
class_name, // Имя, которое мы дали WNDCLASSEX
"Mouse", // Текст заголовка окна
WS_OVERLAPPEDWINDOW, // Стиль окна
CW_USEDEFAULT, // x левого верхнего угла окна (по умолчанию)
CW_USEDEFAULT, // y левого верхнего угла окна (по умолчанию)
WIN_WIDTH, // Ширина окна в пикселях
WIN_HEIGHT, // Высота окна в пикселях
NULL, // Ссылка на "родительское окно" (у нас его нет)
NULL, // Ссылка на меню (у нас его нет)
hinstance, // Ссылка на экземпляр окна (передается WinMain())
NULL); // "Дополнительная информация" для передачи
// в WinProc (у нас ее нет)
// Проверка ошибок
if(!hwnd)
return EXIT_FAILURE; // Случилось что-то плохое!
ShowWindow(hwnd, ishow); // Показать окно (сделать его видимым)
UpdateWindow(hwnd); // Обновить окно (нарисовать его)
while(1)
{
// Получаем сообщения, если они есть
if(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
if(msg.message == WM_QUIT)
break;
TranslateMessage(&msg);
DispatchMessage(&msg);
}
else
{
// Здесь проводим все сложные вычисления :)
}
}
// Освободить память, выделенную WNDCLASSEX
UnregisterClass(class_name,hinstance);
return msg.wParam; // Выход из программы
}
// Оконная процедура
LRESULT CALLBACK WinProc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam)
{
PAINTSTRUCT ps;
// В зависимости от сообщения, выполняем различные действия
switch(message)
{
// Это сообщение, которое отправляется окну, когда двигается мышь.
case WM_MOUSEMOVE:
{
// Здесь мы обрабатываем получаемую из сообщения информацию
// "flags" содержит данные, говорящие, нажаты ли определенные
// клавиши, такие как CTRL. Подробнее об этом в конце файла.
int flags = wparam;
int xPos = LOWORD(lparam); // Дает нам положение X курсора
int yPos = HIWORD(lparam); // Дает нам положение Y курсора
PrintMouseCoords(hwnd,xPos,yPos); // Печатаем это на экран
return 0;
// **Замечение**
//
// Вы можете также создать структуру POINTS - структуру Win32, которая
// содержит 'x' и 'y' определенные как SHORT (16-битные целые)
// вызвав этот макрос:
//
// POINTS point = MAKEPOINTS(lparam); // "point" будет равно положению (x,y) мыши
}
case WM_PAINT:
BeginPaint(hwnd,&ps);
EndPaint(hwnd,&ps);
return 0;
case WM_DESTROY:
case WM_CLOSE:
PostQuitMessage(0);
return 0;
} // конец switch(message)
return DefWindowProc(hwnd, message, wparam, lparam);
}
// Печатает координаты мыши по центру окна
void PrintMouseCoords(HWND hwnd, int xPos, int yPos)
{
HDC hdc = GetDC(hwnd); // Получаем контекст устройства
// Проверка ошибок
if(!hdc)
return; // Невозможно рисовать в окне без HDC, выходим из функции
RECT rect;
GetClientRect(hwnd,&rect); // Получаем клиент-область окна RECT
// Закрашиваем окно (клиент-область) в белый цвет
FillRect(hdc,&rect,(HBRUSH)GetStockObject(WHITE_BRUSH));
// **Замечание**
//
// Что именно является клиент областью окна? Когда вы думаете об окне,
// вам нужно представить его как два RECT, описывающих окно. Первый это
// WINDOW RECT. Как и говорит это название, он включает в себя все окно.
// Второй это CLIENT RECT. Это та область окна, где все и рисуется.
// Клиент область не включает в себя заголовок и рамку окна. Ее верхний
// левый угол всегда начинается с (0,0) и считается относительно окна.
// В этом приложении, клиент-область это прямоугольная область, которая
// закрашена белым цветом и где отображаются координаты мыши.
char buffX[128] = {0}; // char буфер для положения X
char buffY[128] = {0}; // char буфер для положения Y
// Заполняем оба буфера соответствующим текстом
sprintf(buffX,"X Pos = %d",xPos);
sprintf(buffY,"Y Pos = %d",yPos);
/* А здесь начинается математика. Чтобы отобразить текст с горизонтальным
центрированием в окне, мы должны прибегнуть к помощи основ алгебры.
Итак, мы хотим половину текста слева от центра окна, и половину текста
справа от центра окна, и тогда он будет центрирован. Это дает нам
такое уравнение:
xStart = centerX - (длина текста / 2)
yStart = все, что мы выберем, мы беспокоимся только о горизонтальном центрировании
Но мы забыли одну вещь, ширину каждой буквы текста. Поэтому мы добавляем ее
в уравнение:
xStart = centerX - ((lengthOfText / 2) * widthOfACharacter)
yStart = все, что мы выберем, мы беспокоимся только о горизонтальном центрировании
Теперь, когда у нас есть уравнение, мы можем записывать переменные
Сперва, получим центр окна. Обратите внимание на то, как мы используем
CLIENT rect, чтобы получить центр (x,y) окна.
*/
int centerX = rect.right / 2;
int centerY = rect.bottom / 2;
// Теперь получим "ширину текста". Чтобы сделать это, используем структуру
// TEXTMETRIC. TEXTMETRIC это структура Win32, в которой хранится вся
// информация о шрифте.
TEXTMETRIC textmetric;
GetTextMetrics(hdc,&textmetric);
// Так мы заполняем "textmetric" информацией о текущем шрифте. Так как мы
// не установили никакого определенного шрифта, структура заполнится
// информацией о шрифте по умолчанию.
// Сперва выведем координату х мыши, поэтому давайте используем наше
// уровнение для вычисления координаты х начала рисования текста.
// Вы увидите, что мы используем переменную "tmAveCharWidth" структуры
// TEXTMETRIC. Она дает нам среднюю ширину символа, что как раз является
// тем, что нам нужно.
int start = centerX - ((strlen(buffX) / 2) * textmetric.tmAveCharWidth);
// А теперь фактически выведем текст на экран.
// Мы нарисуем его чуть выше вертикального центра экрана, т.е.
// (centerY - 25)
TextOut(hdc, start, centerY - 25, buffX, strlen(buffX));
// Теперь выведем координату y мыши, поэтому пересчитаем начальное положение
// текста
start = centerX - ((strlen(buffY) / 2) * textmetric.tmAveCharWidth);
// Рисуем текст на экране немного ниже вертикального центра окна
TextOut(hdc, start, centerY + 25, buffY, strlen(buffY));
ReleaseDC(hwnd,hdc); // Всегда освобождаем HDC
}
// Больше о мыши --------------
/* Как было обещано, вот информация о переменных, которые заполняются
каждый раз, когда WinProc() получает сообщение WM_MOUSEMOVE.
Важно отметить, что переменные (значение WPARAM) могут содержать
любые комбинации нижеперечисленного:
MK_CONTROL Установлено, если нажата клавиша ctrl.
MK_LBUTTON Установлено, если нажата левая кнопка мыши.
MK_MBUTTON Установлено, если нажата средняя кнопка мыши.
MK_RBUTTON Установлено, если нажата правая кнопка мыши.
MK_SHIFT Установлено, если нажата клавиша shift.
Чтобы определить если, скажем, нажата клавиша ctrl, мы просто
используем наших старых друзей - бинарные операторы, и сделаем
что-то вроде этого:
if(flags & MK_CONTROL)
// Делать это, потому что нажата клавиша ctrl.
*/
/*
| TheTutor
| thetutor@gametutorials.com
| © 2000-2002 GameTutorials
*/
// Перевод © 2004 Евгений Казеко
// www.gamecoder.kazeko.com
// evgeniy@kazeko.com
|