Die WinAPI Plattform

Tutorials

Tutorial Nummer 09

(09.) Die Maus

In diesem Tutorial zeige ich dir, wie man Maus Ereignisse abfragt. Wir werden zusammen ein kleines Zeichenprogramm schreiben. Wenn man die linke Maustaste drückt, dann zeichnet das Programm einen Punkt in den Anwendungsbereich. Wenn man nun die Taste gedrückt hält und an einer anderen Position wieder los läßt (auch ziehen genannt), dann zeichnet das Programm eine Linie zwischen diesen beiden Punkten. Mit der rechten Maustaste kann man den Bildschirm (also den Anwendungsbereich) wieder löschen. Um das Beispiel simple zu halten, zeichnen wir direkt in den Maus Nachrichten (umgehen also WM_PAINT), das hat den Vorteil, das wir die Zeichenoperationen nicht speichern müssen, aber genau das ist auch der Nachteil, denn wenn man Windows dazu bringt uns eine WM_PAINT Nachricht zu schicken (also z.B. Fenster vergrößern), dann wird unsere Zeichnung gelöscht, obwohl die rechte Maustaste gar nicht gedrückt wurde.

#include <windows.h>

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
                   PSTR szCmdLine, int iCmdShow)
{
   HWND         hWnd;
   MSG          msg;
   WNDCLASS     wc;
   
   char         szAppName[] = "Die Maus";
   
   wc.cbClsExtra          = 0;
   wc.cbWndExtra          = 0;
   wc.hbrBackground       = (HBRUSH)GetStockObject(WHITE_BRUSH);
   wc.hCursor             = LoadCursor(NULL, IDC_ARROW);
   wc.hIcon               = LoadIcon(NULL, IDI_APPLICATION);
   wc.hInstance           = hInstance;
   wc.lpfnWndProc         = WndProc;
   wc.lpszClassName       = szAppName;
   wc.lpszMenuName        = NULL;
   wc.style               = CS_HREDRAW | CS_VREDRAW;
   
   RegisterClass(&wc);
   
   hWnd = CreateWindow(  szAppName,
                         szAppName,
                         WS_OVERLAPPEDWINDOW,
                         CW_USEDEFAULT,
                         CW_USEDEFAULT,
                         CW_USEDEFAULT,
                         CW_USEDEFAULT,
                         NULL,
                         NULL,
                         hInstance,
                         NULL);
                         
   ShowWindow(hWnd, iCmdShow);
   UpdateWindow(hWnd);
   
   while (GetMessage(&msg, NULL, 0, 0))
   {
      TranslateMessage(&msg);
      DispatchMessage(&msg);
   }
   
   return msg.wParam;
}

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{

In der POINT Struktur speichern wir immer die Cursorposition, bei der die linke Maustaste gedrückt wurde. Denn von diesem Punkt wollen wir ja eine Linie zu der Cursorposition beim Lösen der linken Maustaste zeichnen. In der WM_CREATE Nachricht belegen wir die Struktur mit dem Wert -1 vor. Diesen Wert benutzen wir um zu kennzeichnen, dass wir im Moment keine gültige Cursorpoition haben, also die linke Maustaste nicht gedrückt ist.

   static POINT    point;
   
   switch (message)
   {
   case WM_CREATE:
      {
         point.x  = -1;
         point.y  = -1;
         return 0;
      }
   case WM_DESTROY:
      {
         PostQuitMessage(0);
         return 0;
      }

Die WM_RBUTTONDOWN Nachricht wird immer dann zu einem Programm geschickt, wenn die rechte Maustaste im Anwendungsbereich des Programmes gedrückt wird. Um den Bildschirm zu löschen, rufen wir einfach InvalidateRect auf.

   case WM_RBUTTONDOWN:
      {
         InvalidateRect(hWnd, NULL, TRUE);
         return 0;
      }

Für WM_LBUTTONDOWN gilt das selbe, wie fü WM_RBUTTONDOWN, nur ist hier natürlich die linke Maustaste gemeint.

   case WM_LBUTTONDOWN:
      {

In diesem Tutorial benutzen wir eine andere Möglichkeit, um an den Handle zum Device Context heranzukommen (Wie auch schon im 4. Tutorial 'Textausgabe zentrieren'). Wir lassen uns den Handle einfach durch GetDC mit dem Handle des Fensters als Parameter zurückgeben. Dieses Verfahren benutzt man, wenn man außerhalb der WM_PAINT Nachricht etwas zeichnen möchte. Jedoch muss man dann die WM_PAINT Nachricht so gestallten, dass das außerhalb gezeichnete wieder mit gezeichnet wird (hier machen wir das nicht. Das Problem wurde oben schon angesprochen). Doch gilt bei diesem Verfahren das gleiche, wie bei BeginPaint: Nach Beendigung des Zeichenvorganges muss der Gerätekontext auch wieder freigegeben werden. Dieses Problem wird hier durch die ReleaseDC Funktion gelöst. Dann ist noch die SetPixel Funktion neu. Wie der Name schon sagt, zeichnet diese Funktion einen einfachen Punkt an der angegebenen Position. Der erste Parameter ist wie immer der Handle zum Gerätekontext. Der zweite und dritte Parameter ist die Position des zu zeichnenden Punktes. Diese ist im lParam der WM_LBUTTONDOWN Nachricht gespeichert. Im niederen Wort die X- und im höheren Wort die Y-Koordinate. Der letzte Parameter bestimmt die Farbe, die wir wieder über das RGB Makro erzeugen.

         HDC  hDC = GetDC(hWnd);
            SetPixel(hDC, LOWORD(lParam), HIWORD(lParam), RGB(0, 0, 255));
         ReleaseDC(hWnd, hDC);

Nun speichern wir die Koordinaten noch in der POINT Struktur.

         point.x  = LOWORD(lParam);
         point.y  = HIWORD(lParam);
         return 0;
      }

Die WM_LBUTTONUP Nachricht wird immer dann geschickt, wenn die linke Maustaste über dem Anwendungsbereich gelöst wird.

   case WM_LBUTTONUP:
      {

Dann prüfen wir, ob die Maustaste zuvor in unserem Anwendungsbereich gedrückt wurde, also die X-Koordinate ungleich -1 ist. Wir könnten auch noch den Y-Wert testen, aber der wird, wenn der X-Wert -1 ist, auch -1 sein. In dem if Zweig besorgen wir uns wieder einen Gerätekontext und setzen erst die Startposition und zeichen schließlich zum Endpunkt, also zur aktuellen Cursorposition. Dieser Punkt ist wieder in lParam gespeichert. Zum Schluß setzen wir die Koordinaten wieder auf den Ausgangspunkt zurück, da die linke Maustaste im Moment nicht gedrückt ist.

         if (point.x != -1)
         {
            HDC   hDC = GetDC(hWnd);
               MoveToEx(hDC, point.x, point.y, NULL);
               LineTo(hDC, LOWORD(lParam), HIWORD(lParam));
            ReleaseDC(hWnd, hDC);
            
            point.x   = -1;
            point.y   = -1;
         }
         return 0;
      }
   }
   
   return DefWindowProc(hWnd, message, wParam, lParam);
}

In diesem Tutorial hast du gelernt, wie du Maus Nachrichten abfängst und so die Maus in deinem Programm mehr Bedeutung gewinnt. Wenn du meinst, dass irgend eine Stelle in diesem Tutorial ungenau ist, oder etwas nicht erklärt wird, dann schreibe mir einfach eine E-Mail.

webmaster@win-api.de