Tutorial 7: Entrada del Ratón

Aprenderemos a recibir y a responder a la entrada del ratón en nuestro procedimiento de ventana. El programa esperará por los clicks del botón izquierdo del ratón y desplegará una cadena de texto en exactamente el punto indicado por el ratón sobre el área cliente.

Bajar el ejemplo de aquí.

Teoría:

Como con la entrada del teclado, Windows detecta y envía notificaciones sobre las actividades del ratón que son relevantes para las ventanas. Esas actividades incluyen los clicks de los botones izquierdo y derecho del ratón, el movimiento del cursor del ratón sobre la ventana, doble clicks. A diferencia de la entrada del teclado, que es dirigida a la ventana que tiene el foco de entrada, los mensajes del ratón son enviados a cualquier ventana sobre la cual esté el cursor del ratón, activo o no. Además, también hay mensajes del ratón sobre el área no cliente. Pero la mayoría de las veces, afortunademente podemos ignorarlas. Podemos concentrarnos en los mensajes relacionados con el área cliente.

Hay dos mensajes para cada botón el ratón: los mensajes WM_LBUTTONDOWN, WM_RBUTTONDOWN y WM_LBUTTONUP, WM_RBUTTONUP. Para un ratón con tres botones, están también WM_MBUTTONDOWN and WM_MBUTTONUP. Cuando el cursor del ratón se mueve sobre el área cliente, Windows envía mensajes WM_MOUSEMOVE a la ventana debajo del cursor. Una ventana puede recibir mensajes de doble clicks, WM_LBUTTONDBCLK o WM_RBUTTONDBCLK, si y sólo si la clase de su ventana tiene activada la bandera correspondiente al estilo CS_DBLCLKS, sino la ventana recibirá sólo una serie de mensajes del topo botón del ratón arriba o abajo.

Para todos estos mensajes, el valor de lParam contiene la posición del ratón. La palabra [word] baja es la coordenada 'x', y la palabra alta es la coordenada 'y' relativa a la esquina izquierda superior del área cliente de la ventana. wParam indica el estado de los botones del ratón y de las teclas Shift y Ctrl.
 

Ejemplo:

.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
    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
 

Analysis:

    .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

El procedimiento de ventana espera a que el botón izquierdo del ratón haga un click. Cuando recibe el mensaje WM_LBUTTONDOWN, lParam contiene la coordenada del botón del ratón en el área cliente. Salva la coordenada en la variable de tipo POINT definida así:

POINT STRUCT
    x   dd ?
    y   dd ?
POINT ENDS

y establece la bandera, MouseClick, a TRUE, lo que siginifica que al menos hay un click del botón izquierdo del ratón sobre el área cliente.

        mov eax,lParam
        and eax,0FFFFh
        mov hitpoint.x,eax

Como la coordenada 'x' es la palabra baja de lParam y los miembros de la estructura POINT tiene un tamaño de 32-bits, debemos poner en cero la palabara alta de eax antes de almacenarla en hitpoint.x.

        shr eax,16
        mov hitpoint.y,eax

Como la coordenada 'y' es la palabra alta de lParam, debemos ponerla en la palabra baja de eax antes de almacenarla en hitpoint.y. Hacemos esto desplazando [shifting] el contenido de eax 16 bits a la derecha.

Después de almacenar la posición del ratón, establecemos la bandera, MouseClick, a TRUE con el fin de dejar que el código de pintura en la sección WM_PAINT sepa que hay al menos un click en el área cliente y puede dibujar la cadena en la posición del ratón. Luego llamamos a la función InvalidateRect para que Windows vuelva a pintar toda el área cliente.

        .IF MouseClick
            invoke lstrlen,ADDR AppName
            invoke TextOut,hdc,hitpoint.x,hitpoint.y,ADDR AppName,eax
        .ENDIF

El código de pintura en la sección WM_PAINT debe chequear si MouseClick es uno ( TRUE ), ya que cuando la ventana fue creada, recibió un mensaje WM_PAINT en ese momento, ningún click del ratón había ocurrido aún, así que no dibujará la cadena en el área cliente. Inicializamos MouseClick a FALSE y cambiamos su valor a TRUE cuando ocurre un click del ratón.

Si ha ocurrido al menos un click de ratón, se dibuja la cadena en el área cliente en la posición del ratón. Nota que se llama a lstrlen para obtener el tamaño de la cadena a desplegar y envía este tamaño como último parámetro de la función TextOut.


Índice

Siguiente

[Iczelion's Win32 Assembly HomePage]

n u M I T_o r's   Programming Page

Este tutorial, original de Iczelion, ha sido traducido por:   n u M I T_o r

www.000webhost.com