Уроки Iczelion'а

       

Мышь


Мы научимся как получать и отвечать на ввод с мыши в нашей процедуре окна. Программа-пример будет ждать нажатия на левую кнопку мыши и отображать текстовую строку в точности в том месте клиентской области, где кликнули на мышь.

Скачайте пример здесь.

Теория:

Так же, как и при вводе с клавиатуры, Windows определяет и посылает уведомления об активности мыши относительно какого-либо окна. Эта активность включает в себя нажатия на правую и левую клавишу, передвижения курсора через окно, двойные нажатия. В отличие от клавиатуры, сообщения от которой направляются окну, имеющему в данный момент фокус ввода, сведения о котором передаются окну, над которым находится мышь, независимо от того, активно оно или нет. Вдобавок, есть сообщения от мыши, связанные с не-клиентской части окна, но, к счастью, мы можем их, как правило, игнорировать. Мы можем сфокусироваться на связанных с клиентской областью.

Есть два сообщения для каждой из кнопок мыши: WM_LBUTTONDOWN, WM_RBUTTONDOWN и WM_LBUTTONUр, WM_RBUTTONUр. Если мышь трехкнопочная, то есть еще WM_MBUTTONDOWN и WM_MBUTTONUр. Когда курсор мыши двигается над клиентской областью, Windows шлет WM_MOUSEMOVE окну, над которым он находится. Окно может получать сообщения о двойных нажатиях, WM_LBUTTONDBCLK или WM_RBUTTONDBCLK, тогда и только тогда, когда окно имеет стиль CS_DBLCLKS, или же оно будет получать только серию сообщений об одинарных нажатиях.

Во всех этих сообщениях значение lParam содержит позицию мыши. Hижнее слово - это x-координата, верхнее слово - y-координата верхнего левого угла клиентской области окна. wParam содержит информацию о состоянии кнопок мыши, Shift'а и Ctrl'а.

Пpимеp:

.386 .model flat,stdcall

option casemap:none

WinMain proto :DWORD,:DWORD,:DWORD,:DWORD

include \masm32\include\windows.inc include \masm32\include\user32.inc

include \masm32\include\kernel32.inc include \masm32\include\gdi32.inc includelib \masm32\lib\user32.lib includelib \masm32\lib\kernel32.lib

includelib \masm32\lib\gdi32.lib




.data
ClassName db "SimpleWinClass",0 AppName db "Our First Window",0 MouseClick db 0 ; 0=no click yet
.data? hInstance HINSTANCE ? CommandLine LPSTR ?
hitpoint POINT <>
.code
start: invoke GetModuleHandle, NULL mov hInstance,eax invoke GetCommandLine
mov CommandLine,eax invoke WinMain, hInstance,NULL,CommandLine, SW_SHOWDEFAULT invoke ExitProcess,eax
WinMain proc hInst:HINSTANCE,hPrevInst:HINSTANCE,CmdLine:LPSTR,CmdShow:DWORD LOCAL wc:WNDCLASSEX
LOCAL msg:MSG LOCAL hwnd:HWND mov wc.cbSize,SIZEOF WNDCLASSEX mov wc.style, CS_HREDRAW or CS_VREDRAW
mov wc.lpfnWndProc, OFFSET WndProc mov wc.cbClsExtra,NULL mov wc.cbWndExtra,NULL push hInst
pop wc.hInstance mov wc.hbrBackground,COLOR_WINDOW+1 mov wc.lpszMenuName,NULL mov wc.lpszClassName,OFFSET ClassName
invoke LoadIcon,NULL,IDI_APPLICATION mov wc.hIcon,eax mov wc.hIconSm,eax invoke LoadCursor,NULL,IDC_ARROW
mov wc.hCursor, eax invoke RegisterClassEx, addr wc invoke CreateWindowEx,NULL,ADDR ClassName,ADDR AppName,\ WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,\ CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,NULL,NULL,\ hInst,NULL mov hwnd,eax invoke ShowWindow, hwnd,SW_SHOWNORMAL
invoke UpdateWindow, hwnd .WHILE TRUE invoke GetMessage, ADDR msg,NULL,0,0 .BREAK .IF (!eax)
invoke DispatchMessage, ADDR msg .ENDW mov eax,msg.wParam ret
WinMain endp
WndProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
LOCAL hdc:HDC
LOCAL ps:PAINTSTRUCT
.IF uMsg==WM_DESTROY invoke PostQuitMessage,NULL .ELSEIF uMsg==WM_LBUTTONDOWN mov eax,lParam
and eax,0FFFFh mov hitpoint.x,eax mov eax,lParam shr eax,16
mov hitpoint.y,eax mov MouseClick,TRUE invoke InvalidateRect,hWnd,NULL,TRUE .ELSEIF uMsg==WM_PAINT
invoke BeginPaint,hWnd, ADDR ps mov hdc,eax .IF MouseClick invoke lstrlen,ADDR AppName
invoke TextOut,hdc,hitpoint.x,hitpoint.y,ADDR AppName,eax .ENDIF invoke EndPaint,hWnd, ADDR ps .ELSE
invoke DefWindowProc,hWnd,uMsg,wParam,lParam ret .ENDIF xor eax,eax
ret WndProc endp end start
Анализ:
.ELSEIF uMsg==WM_LBUTTONDOWN


mov eax,lParam and eax,0FFFFh mov hitpoint.x,eax mov eax,lParam
shr eax,16 mov hitpoint.y,eax mov MouseClick,TRUE invoke InvalidateRect,hWnd,NULL,TRUE
Процедура окна ждет нажатия на левую клавишу мыши. Когда она получает WM_LBUTTONDOWN, lParam содержит координаты курсора мыши в клиентской области. Процедура сохраняет их в переменной типа POINT, определенной следующим образом:
POINT STRUCT x dd ?
y dd ?
POINT ENDS
Затем устанавливает флаг, MouseClick, в TRUE, что значит в клиентской области была нажата левая клавиша мыши.
mov eax,lParam and eax,0FFFFh mov hitpoint.x,eax
Так как x-координата - это нижнее слово lParam и члены структуры POINT размером в 32 бита, мы должны обнулить верхнее слово eax, прежде чем сохранить значение в hitpoint.x.
shr eax,16
mov hitpoint.y,eax
Так как y-координата - это верхнее слово lParam, мы должны ее в нижнее слово, прежде чем сохранять в hitрoint.y. Мы делаем это сдвигая eax на 16 битов вправо. После сохранения позиции мыши, мы устанавливаем флаг, MouseClick, в TRUE для того, чтобы отрисовывающий код в секции WM_PAINT, знал, что было нажатие в клиентской области, и значит поэтому он может нарисовать строку в позиции, где была мышь при нажатии. Затем мы вызываем функцию InvalidateRect, чтобы заставить окно полностью перерисовать ее клиентскую область.
.IF MouseClick
invoke lstrlen,ADDR AppName invoke TextOut,hdc,hitpoint.x,hitpoint.y,ADDR AppName,eax
.ENDIF
Отрисовывающий код в секции WM_PAINT должен проверять, установлен ли флаг MouseClick в TRUE, потому что когда окно создается, процедура окна получает сообщение WM_PAINT в то время, когда не было сделано еще ни одного нажатия, то есть строку отрисовывать нельзя. Мы инициализируем MouseClick в FALSE и меняем ее значение в TRUE, когда происходит нажатие на мышь. Если по крайней мере одно нажатие на мышь произошло, она вырисовывает строку в клиентской области в позиции, где была мышь при нажатии. Заметьте, что она вызывает lstrlen для того, чтобы определить длину строки и шлет полученное значение в качестве последнего параметра функции TextOut.
[C] Iczelion, пер. Aquila.

Содержание раздела