Tutorial 7: Les entrées Souris

Nous allons voir comment recevoir et répondre aux entrées souris dans notre procédure de fenêtre. Le programme d'exemple contrôlera les cliques gauche de la souris et affichera un texte sur le secteur client aux coordonnées même de la souris.

Downloadez le fichier en example ici.

Théorie:

Comme pour les entrées clavier, Windows détecte les activités de la souris dépendantes de chaque fenêtre. Ici notre activité portera uniquement sur le clique gauche et sur le mouvement du curseur de la souris dans la fenêtre. À la différence des entrées clavier qui sont adressés uniquement à la fenêtre qui est activée, les messages de la souris sont envoyés à n'importe quelle fenêtre (active ou non) du moment que la souris soit placée dessus. En plus, il existe aussi des 'messages souris' adressés au secteur non-client. Mais la plupart du temps, nous pouvons les ignorer sans s'en soucier. On peut se concentrer pleinement sur les messages touchant au secteur client.
Il y a deux messages pour chaque bouton de la souris : WM_LBUTTONDOWN, WM_RBUTTONDOWN et WM_LBUTTONUP, WM_RBUTTONUP. Pour une souris avec trois boutons, il y a aussi WM_MBUTTONDOWN et WM_MBUTTONUP. Pour le déplacement du curseur de souris sur le secteur client, Windows envoie le message WM_MOUSEMOVE à la fenêtre qui se trouve sous le curseur.
Une fenêtre peut recevoir des messages de double-clic, WM_LBUTTONDBCLK ou WM_RBUTTONDBCLK, si et seulement si sa classe de fenêtre obtient le drapeau (flag) de style CS_DBLCLKS, sinon la fenêtre ne recevra seulement qu'une série de messages disant que les boutons de souris on étés pressés (Button Up, Button Down).
Pour tous ces messages, la valeur de lParam contient (sera) la position de la souris. On travaille en 32 Bit donc sur des doubles mots. Le mot de poids faible sera la coordonnée X et le mot de poids fort sera la coordonnée Y à partir du coin supérieur gauche du secteur client de la fenêtre. WParam indique l'état des boutons de la souris, et des touches Shift et Ctrl.
 

Example:

.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=pas encore de click

.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
 

Analyse:

    .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

La procédure de fenêtre attend qu'on clique sur le bouton gauche de la souris. Quand il reçoit WM_LBUTTONDOWN, lParam prend la valeur de la coordonnée du curseur de la souris dans le secteur client. Il sauvegarde la coordonnée dans une variable de type POINT qui est défini comme ceci:

POINT STRUCT
    x   dd ?
    y   dd ?
POINT ENDS

et le 'flag' qui contrôle l'état de MouseClick, est mis à TRUE (VRAI), signifiant ainsi qu'il y a au moins le bouton gauche de la souris qu'on peut cliquer sur le secteur client.

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

Puisque la coordonnée X est le mot de poids faible du double mot lParam et que les 2 membres qui forment la structure du POINT sont d'une taille de 32 bits, nous devons mettre la partie haute de 'eax' à 0 avant le stockage de celle-ci.

        shr eax,16
        mov hitpoint.y,eax

Puisque la coordonnée Y est le mot de poids fort du double mot lParam, nous devons mettre la partie basse de 'eax' à 0 avant le stockage de celle-ci. Nous pouvons faire ça en déplaçant 16bits de eax vers la droit.
Après le stockage de la position de la souris, nous mettons le flag contrôlant l'état de MouseClick à TRUE (VRAI) pour que le code de la section WM_PAINT sache qu'il y a au moins un clique effectué sur le secteur client. Donc il peut dessiner la chaîne de caractère à la position de la souris. Ensuite nous appelons la fonction InvalidateRect pour forcer la fenêtre à repeindre la totalité de son secteur client.

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

Le code de la section WM_PAINT (celui qui sert à repeindre le secteur client de votre fenêtre) doit vérifier si MouseClick est vrai, dès que la fenêtre a été créée. Au tout début il a reçu un message WM_PAINT, pour lequel il n'y avait aucun clique de souris, ainsi il ne doit pas dessiner la chaîne de caractère dans le secteur client. Nous initialisons donc MouseClick à FALSE (FAUX), mais changeons sa valeur à TRUE (VRAI) uniquement quand advient un réel clique de souris.
Si au moins un clique de souris arrive, il dessine la chaîne de caractère sur le secteur client à la position de la souris. Notez qu'il appelle 'lstrlen' pour obtenir la longueur de la chaîne à afficher et renvoie la longueur comme dernier paramètre de la fonction TextOut.


[Iczelion's Win32 Assembly HomePage]


Traduit par Morgatte