// Done by TheTutor -- 6/13/02
// Перевод © 2004 Евгений Казеко
// www.gamecoder.kazeko.com
// evgeniy@kazeko.com
/* Вновь обратимся к теме получения ввода с помощью мыши в приложении Win32.
На этот раз мы узнаем, как проверять левую, правую кнопки мыши и колесо.
Вот что будет делать наша программа:
1) Пока удерживается левая кнопка мыши, в центре экрана
появится текст "Left MB"
2) Пока удерживается правая кнопка мыши, в центре экрана
появится текст "Right MB"
3) Вращение колеса мыши изменит цвет текста для обоих сообщений.
*/
// Сперва нужно заткнуть дыру MS. Если почитаете MSDN, то прочтете, что
// программировать колесо мыши можно если у вас Windows 98 и выше.
// Это неправда. В зависимости от вашей системы, вам может понадобиться
// определить колесо самим.
#ifndef WM_MOUSEWHEEL
#define WM_MOUSEWHEEL 522 // Значение из Winuser.h
#endif
#include <windows.h>
#define WIN_WIDTH 320
#define WIN_HEIGHT 240
// Положение Y для отображения текста для левой и правой кнопок мыши
#define LMB_YPOS 80
#define RMB_YPOS 120
#define class_name "GT_MouseInput2"
// Эта функция печатает текст на экране начиная с "yPos",
// горизонтально центруя его в окне и используя передаваемый цвет.
void TextOutCentered(HWND hwnd, char *text, int yPos, COLORREF color);
// Стандарнтая 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;
static unsigned char red, green, blue; // Цвета нашего текста
// В зависимости от сообщения, выполняем различные действия
switch(message)
{
case WM_CREATE:
// Устанавливаем цвет текста черный
red = green = blue = 0;
return 0;
/* Это сообщение, которое посылается, когда нажата левая кнопка мыши,
в то время, как курсор находится в клиент области окна (области окна,
которая не включает в себя заголовок и границы окна).
И когда мы получаем это сообщение, мы выведем на экран текст "Left MB".
*/
case WM_LBUTTONDOWN:
TextOutCentered(hwnd,"Left MB",LMB_YPOS,RGB(red,green,blue));
return 0;
/* Это сообщение, которое посылается, когда левая кнопка мыши освобождается,
в то время, как курсор находится в клиент области окна. И когда мы получаем
это сообщение, мы отображаем тот же самый текст, но с использованием
цвета (255,255,255), что создает видимость стирания текста.
*/
case WM_LBUTTONUP:
TextOutCentered(hwnd,"Left MB",LMB_YPOS,RGB(255,255,255));
return 0;
/* Это сообщение, которое посылается, когда нажата правая кнопка мыши,
в то время, как курсор находится в клиент области окна (области окна,
которая не включает в себя заголовок и границы окна).
И когда мы получаем это сообщение, мы выведем на экран текст "Right MB".
*/
case WM_RBUTTONDOWN:
TextOutCentered(hwnd,"Right MB",RMB_YPOS,RGB(red,green,blue));
return 0;
/* Это сообщение, которое посылается, когда правая кнопка мыши освобождается,
в то время, как курсор находится в клиент области окна. И когда мы получаем
это сообщение, мы отображаем тот же самый текст, но с использованием
цвета (255,255,255), что создает видимость стирания текста.
*/
case WM_RBUTTONUP:
TextOutCentered(hwnd,"Right MB",RMB_YPOS,RGB(255,255,255));
return 0;
/* Это сообщение посылается, когда вращается колесо мыши. Это сообщение
будет посылаться текущему активному окну. Получим из сообщения информацию,
которая нам необходима.
*/
case WM_MOUSEWHEEL:
{
int flags = LOWORD(wparam); // LOWORD параметра WPARAM содержит информацию о том,
// нажаты ли такие клавиши, как CTRL.
// Подробнее о "flags" внизу файла.
/* HIWORD параметра WPARAM содержит информацию о расстоянии, на которое
вращается колесо, выражаемом числом WHEEL_DELTA, равном 120.
Положительное значение означает, что колесо вращается вперед, от
пользователя, отрицательное значение означает, что колесо вращается
назад, к пользователю. Больше информации об использовании WHEEL_DELTA
внизу этого файла.
*/
short delta = (short)HIWORD(wparam);
// Мы собираемся отслеживать, вращается ли колесо мыши вперед или назад.
// Если оно вращается вперед, мы прибавим 5 к значанию цвета.
// Если оно вращается назад, мы отнимем 5 от значения цвета.
int amount = (delta >= 0) ? 5 : -5;
// Если нажата клавиша CTRL
if(flags & MK_CONTROL)
red += amount; // Изменим красную составляющую цвета
else if(flags & MK_SHIFT) // А если нажата клавиша SHIFT
green += amount; // Изменим зеленую составляющую цвета
else // В противном случае изменим синюю составляющую цвета
blue += amount;
// Если нажата левая кнопка мыши
if(flags & MK_LBUTTON)
TextOutCentered(hwnd,"Left MB",LMB_YPOS,RGB(red,green,blue));
// Если нажата правая кнопка мыши
if(flags & MK_RBUTTON)
TextOutCentered(hwnd,"Right MB",RMB_YPOS,RGB(red,green,blue));
return 0;
// **Замечание**
//
// Также из сообщения может быть получена следующая дополнительная информация:
//
// int xPos = LOWORD(lparam); // горизонтальное положение курсора
//
// int yPos = HIWORD(lparam); // вертикальное положение курсора
}
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);
}
// Эта функция делает большинство "грязной работы". Она печатает текст
// на экран горизонтально центрируя его, начиная с "yPos".
// Текст печатается цветом, передаваемым COLORREF.
void TextOutCentered(HWND hwnd, char *text, int yPos, COLORREF color)
{
HDC hdc = GetDC(hwnd); // Получаем контекст устройства окна
// Проверка на ошибку
if(!hdc)
return;
RECT rect;
GetClientRect(hwnd,&rect); // Получаем RECT клиент области окна
TEXTMETRIC textmetric; // Структура, содержащая информацию о шрифте
GetTextMetrics(hdc,&textmetric); // Заполняем TEXTMETRIC информацией о шрифте нашего окна
// Устатавливаем цвет текста согласно с передаваемым в функцию
SetTextColor(hdc,color);
// Вычисляем начальную координату Х отображения текста.
// (rect.right / 2) равняется центру X окна.
int xPos = (rect.right / 2) - ((strlen(text) / 2) * textmetric.tmAveCharWidth);
// Выводим текст на экран
TextOut(hdc, xPos, yPos, text, strlen(text));
ReleaseDC(hwnd,hdc); // Всегда освобождайте HDC
}
// Колесо неудачи...
/*
К сожалению, вам "потенциально" приходится делать кое-что особенное, чтобы
заставить работать колесо мыши (да, можете винить в этом MS).
Посмотрим на код, который мы используем для WM_MOUSEWHEEL, и вот что он делает:
#ifndef WM_MOUSEWHEEL // Это говорит "Хорошо, если то, что, как утверждает MS,
// определено, на самом деле не определено, то надо что-то
// с этим сделать"
#define WM_MOUSEWHEEL 522 // Мы определяем значение согласно с тем, чему оно
// должно равняться.
// Мы берем его из Winuser.h
#endif
Если вы захотите использовать значение WHEEL_DELTA, вам понадобится написать
код, подобный тому, что приведен выше. WHEEL_DELTA указыает расстояние, на которое
вращается колесо, выраженное в количестве "раз" WHEEL_DELTA, равного 120. Поэтому,
если хотите использовать WHEEL_DELTA, установите его значение 120.
Если вам интересно узнать, почему без этого ничего не работает, откройте winuser.h.
Вы увидите код, который не позволяет ничему работать. Я настоятельно рекомендую НЕ
изменять winuser.h. Просто делайте так, как в этой обучалке.
Кстати, без этих изменений я не смог скомпилировать обучалку в Win98 и Win2000,
используя VC++ 6.0
Когда обрабатываете любые сообщения WinProc(), важно знать точно, что они делают.
В качестве упражнения попробуйте удерживать кнопку мыши и при этом убирать курсор
с окна, после чего отпускать ее. Сможете ли вы сделать так, чтобы это приложение
показывало/стирало текст, вне зависимости от того, где находится мышь.
*Подсказка* посмотрите WM_NCLBUTTONDOWN (и подобные) в MSDN
*/
/*
| TheTutor
| thetutor@gametutorials.com
| © 2000-2002 GameTutorials
*/
// Перевод © 2004 Евгений Казеко
// www.gamecoder.kazeko.com
// evgeniy@kazeko.com
|