天天看點

Windows 程式設計--- 滑鼠(1)1.滑鼠的基礎知識2.客戶區滑鼠消息3.非客戶區滑鼠消息

最近學習了一下Windows 程式設計這本書中的滑鼠章節,現在總結一下學習的内容。大家可以一起參考。我将分為兩個部分進行介紹。有關滑鼠更具體的學習可以參考微軟官方文檔滑鼠輸入

滑鼠

  • 1.滑鼠的基礎知識
  • 2.客戶區滑鼠消息
  • 3.非客戶區滑鼠消息

1.滑鼠的基礎知識

1.0 定義:滑鼠帶有一個或者多個按鈕的定位裝置。差別于鍵盤:輸入和管理文本功能。滑鼠主要用來繪制和處理圖形對象。我們常用的滑鼠是兩個按鈕+一個滾輪,其實滾輪中間也有一個按鈕。

1.1 作業系統中與滑鼠有關的API函數:

①擷取電腦是否連接配接滑鼠:

fMouse = GetSystemMetrics(SM_MOUSEPRESENT)
// fMouse:連接配接了滑鼠時為TRUE,否則為0。
           

②确定滑鼠按鈕數

cButtons = GetSystemMetrics(SM_CMOUSEBUTTONS)
// 如果沒有滑鼠則cButtons 為0。
           

③設定滑鼠的其他資訊

BOOL
WINAPI
SystemParametersInfoW(
    _In_ UINT uiAction,
    _In_ UINT uiParam,
    _Pre_maybenull_ _Post_valid_ PVOID pvParam,
    _In_ UINT fWinIni);
           

例如擷取滑鼠滾輪資訊:

1.2 一些和滑鼠有關的術語:

①滑鼠指針:位圖格式的小圖示。具有一個單像素精度的"熱點"(hot spot),它在顯示裝置上訓示了一個精确位置。我們平時知道的滑鼠位置,說的就是熱點的位置了。

Windows 在Winuser.h中為我們提供了幾種常用的指針。最常用的是斜向箭頭,稱為IDC_ARROW。然後還有IDC_CROSS(➕ 狀)、IDC_WAIT(⌛狀,表示等待)等,具體檢視Winuser.h頭檔案。我們的建立視窗類時,預設指定為IDC_ARROW。

②單擊:按下滑鼠按鈕,然後松開。

③輕按兩下:連續兩次快速按下按鈕并且松開。

④拖動:保持按下按鈕,并且移動滑鼠。

2.客戶區滑鼠消息

2.0 介紹:與鍵盤不同的是鍵盤隻會把消息發送到具有指定焦點的視窗上。但是滑鼠不同,當滑鼠被單擊或者經過視窗時,即使該視窗是非活動視窗或不帶輸入焦點,視窗過程依然會處理滑鼠消息。

Windows 定義了21種滑鼠消息。但是其中11種消息與客戶區無關,稱為"非客戶區消息"。Windows應用程式經常忽略這類消息。

2.1 客戶區滑鼠消息:

按鈕 按下 釋放 第二次按下按鈕
左鍵 WM_LBUTTONDOWN WM_LBUTTONUP WM_LBUTTONDBLCLK
中鍵 WM_MBUTTONDOWN WM_MBUTTONUP WM_MBUTTONDBLCLK
右鍵 WM_RBUTTONDOWN WM_RBUTTONUP WM_RBUTTONDBLCLK

注意:隻有當視窗定義成了接受滑鼠消息時,視窗過程才能處理DBLICK。

對于所有的客戶區消息來說:

參數LParam:包含了滑鼠的位置資訊。LOWORD(LParam):低位字表示x坐标,HIWORD(LParam)表示y坐标。

參數wParam:表示滑鼠按鈕、SHIFT鍵和CTRL鍵的狀态。在WinUser.h頭檔案中定義了位掩碼來測試參數wParam。字首MK_代表"滑鼠鍵"(Mouse Key).

/*
 * Key State Masks for Mouse Messages
 */
#define MK_LBUTTON          0x0001
#define MK_RBUTTON          0x0002
#define MK_SHIFT            0x0004
#define MK_CONTROL          0x0008
#define MK_MBUTTON          0x0010
#if(_WIN32_WINNT >= 0x0500)
#define MK_XBUTTON1         0x0020
#define MK_XBUTTON2         0x0040
#endif /* _WIN32_WINNT >= 0x0500 */
           

例如可以這樣使用判斷當出現WM_LBUTTONDOWN消息時,是否同時按下SHIFT鍵:

case WM_LBUTTONDOWN:
	{
	if(wParam & MK_SHIFT)
			// Do Something...
		    MessageBox(hWnd, TEXT("滑鼠左鍵 + SHIFT鍵 "), TEXT("提示"), MB_OK);
		break;
	}
           

Windows規定:如果在非活動視窗内按下滑鼠左鍵,Windows會将這個視窗變成活動視窗。并且向該視窗發送WM_LBUTTONDOWN消息。視窗過程再收到WM_LBUTTONDOWN消息時,就能保證安全的保證該視窗是活動視窗。另外即使視窗沒有WM_LBUTTONDOWN消息時,也能夠處理WM_LBUTTONUP消息。

下面是一個簡單例子:

#include <windows.h>

#define MAXPOINTS 1000

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

int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
                    PSTR szCmdLine, int iCmdShow)
{
     static TCHAR szAppName[] = TEXT ("Connect") ;
     HWND         hwnd ;
     MSG          msg ;
     WNDCLASS     wndclass ;
     
     wndclass.style         = CS_HREDRAW | CS_VREDRAW ;
     wndclass.lpfnWndProc   = WndProc ;
     wndclass.cbClsExtra    = 0 ;
     wndclass.cbWndExtra    = 0 ;
     wndclass.hInstance     = hInstance ;
     wndclass.hIcon         = LoadIcon (NULL, IDI_APPLICATION) ;
     wndclass.hCursor       = LoadCursor (NULL, IDC_ARROW) ;
     wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ;
     wndclass.lpszMenuName  = NULL ;
     wndclass.lpszClassName = szAppName ;
     
     if (!RegisterClass (&wndclass))
     {
          MessageBox (NULL, TEXT ("Program requires Windows NT!"), 
                      szAppName, MB_ICONERROR) ;
          return 0 ;
     }
     
     hwnd = CreateWindow (szAppName, TEXT ("Connect-the-Points Mouse Demo"),
                          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)
{
     static POINT pt[MAXPOINTS] ;
     static int   iCount ;
     HDC          hdc ;
     int          i, j ;
     PAINTSTRUCT  ps ;

     switch (message)
     {
     case WM_LBUTTONDOWN:
          iCount = 0 ;
          InvalidateRect (hwnd, NULL, TRUE) ;
          return 0 ;
          
     case WM_MOUSEMOVE:
          if (wParam & MK_LBUTTON && iCount < 1000)
          {
               pt[iCount  ].x = LOWORD (lParam) ;
               pt[iCount++].y = HIWORD (lParam) ;
               
               hdc = GetDC (hwnd) ;
               SetPixel (hdc, LOWORD (lParam), HIWORD (lParam), 0) ;
               ReleaseDC (hwnd, hdc) ;
          }
          return 0 ;
          
     case WM_LBUTTONUP:
          InvalidateRect (hwnd, NULL, FALSE) ;
          return 0 ;
          
     case WM_PAINT:
          hdc = BeginPaint (hwnd, &ps) ;
          
          SetCursor (LoadCursor (NULL, IDC_WAIT)) ;
          ShowCursor (TRUE) ;
          
          for (i = 0 ; i < iCount - 1 ; i++)
               for (j = i + 1 ; j < iCount ; j++)
               {
                    MoveToEx (hdc, pt[i].x, pt[i].y, NULL) ;
                    LineTo   (hdc, pt[j].x, pt[j].y) ;
               }
               
          ShowCursor (FALSE) ;
          SetCursor (LoadCursor (NULL, IDC_ARROW)) ;
               
          EndPaint (hwnd, &ps) ;
          return 0 ;
               
     case WM_DESTROY:
          PostQuitMessage (0) ;
          return 0 ;
     }
     return DefWindowProc (hwnd, message, wParam, lParam) ;
}
           

2.2 處理Shift鍵:

在上面的程式中在處理WM_MOUSEMOVE消息時有如下處理: if (wParam & MK_LBUTTON && iCount < 1000)判斷是否按下滑鼠左鍵。如果在處理滑鼠消息時,需要用判斷Shift鍵和Ctrl鍵的狀态時,可以這樣處理:

if(wParam & MK_SHIFT)
	{
		if (wParam & MK_CONTROL)
		{
			 // 按下Shift + Ctrl 鍵
			MessageBeep(0);
			MessageBox(hWnd,TEXT("Ctrl+Shift 鍵被按下"),TEXT("提示"),MB_OK);
		}
		else if (wParam & MK_SHIFT)
		{
			 // 按下Shift鍵
			MessageBeep(0);
			MessageBox(hWnd, TEXT("Shift 鍵被按下"), TEXT("提示"), MB_OK);
		}
	}
	else 
	{
		if (wParam & MK_CONTROL)
		{
			MessageBox(hWnd, TEXT("Ctrl 鍵被按下"), TEXT("提示"), MB_OK);
		}
		else
		{
			MessageBox(hWnd, TEXT("Ctrl 和 shift鍵都沒有被按下"), TEXT("提示"), MB_OK);
	    }
	}
           

利用虛拟鍵VK_LBUTTON、VK_RBUTTON、VK_MBUTTON、VK_SHIFT、VK_CONTROL和GetKeyState()函數也能夠傳回滑鼠按鈕的狀态。

2.3 滑鼠輕按兩下:

要做到滑鼠輕按兩下需要達到兩個條件:

①實體位置上十分靠近。

②必須發生在特定的時間間隔内。(輕按兩下速度)

前面提到視窗過程要處理滑鼠輕按兩下消息時,視窗類需要包含CS_DBLCLKS風格:

如果視窗過程沒有包含CS_DBLCLKS風格,則視窗過程處理滑鼠輕按兩下消息順序如下:

WM_LBUTTONDOWN

WM_LBUTTONUP

WM_LBUTTONDOWN

WM_LBUTTONUP

可以使用GetMessageTime函數來擷取WM_LBUTTONDOWN消息之間的時間間隔。

包含CS_DBLCLKS風格時,處理順序如下:

WM_LBUTTONDOWN

WM_LBUTTONUP

WM_LBUTTONDBLCLK

WM_LBUTTONUP

3.非客戶區滑鼠消息

到目前為止我們說的10種滑鼠消息都是發生在視窗客戶區内的移動或者單擊。如果滑鼠位于視窗客戶區以外的區域,滑鼠就會産生非客戶區消息。視窗的非封閉區域包括其邊框、菜單欄、标題欄、滾動條、視窗菜單、最小化按鈕和最大化按鈕。具體可以參考微軟官方文檔滑鼠輸入

系統一般不需要使用者處理非客戶區消息。隻需要将消息發送給DefWindowProc函數,由系統來處理。這與鍵盤中的WM_SYSKEYDOWN、WM_SYSKEYUP、WM_CHAR等消息相似。非客戶區消息與客戶區消息基本上是對應的。表識符包含了"NC"字樣,表示"非客戶"(NonClient).

非客戶區消息如下:

按鈕 按下 釋放 第二次按下按鈕
左鍵 WM_NCLBUTTONDOWN WM_NCLBUTTONUP WM_NCLBUTTONDBLCLK
中鍵 WM_NCMBUTTONDOWN WM_NCMBUTTONUP WM_NCMBUTTONDBLCLK
右鍵 WM_NCRBUTTONDOWN WM_NCRBUTTONUP WM_NCRBUTTONDBLCLK

非客戶區消息的wParam參數與客戶區的有一些不同。wParam參數表示非客戶區滑鼠移動或者單擊的位置。它的值被設定成表示一個HT為首的辨別符,其中HT表示"擊中測試"。

LParam則相同都是表示坐标。

3.1 擊中測試消息

我們來學習最後一個滑鼠消息叫做WM_NCHITTEST。這個消息的優先級高于其他所有的客戶區和非客戶區消息。Windows 應用程式通常會把這個消息發送給DefWindowProc函數,然後系統利用WM_NCHITTEST産生其他所有和滑鼠位置有關的消息。DefWindowProc函數傳回一個可用于滑鼠參數的wParam值。這個值可以是任何非客戶區消息的wParam值。也可以是如下的值。

HTBORDER

18

在沒有大小調整邊框的視窗的邊框中。

HTBOTTOM

15

在可調整大小的視窗的下水準邊框 (使用者可以單擊滑鼠以垂直調整視窗大小) 。

HTBOTTOMLEFT

16

在可調整大小的視窗邊框的左下角, (使用者可以單擊滑鼠以) 對角線調整視窗大小。

HTBOTTOMRIGHT

17

在可調整大小的視窗邊框的右下角 (使用者可以單擊滑鼠以) 對角線調整視窗大小。

HTCAPTION

2

在标題欄中。

HTCLIENT

1

在工作區中。

HTCLOSE

20

在 “關閉 ”按鈕中。

HTERROR

-2

在螢幕背景或視窗之間的分隔線上, (與 HTNOWHERE 相同,隻不過 DefWindowProc 函數會發出系統蜂鳴聲以訓示) 錯誤。

HTGROWBOX

4

在大小框中, (與 HTSIZE) 相同。

HTHELP

21

在 “幫助 ”按鈕中。

HTHSCROLL

6

在水準滾動條中。

HTLEFT

10

在可調整大小的視窗的左邊框 (使用者可以單擊滑鼠以水準調整視窗大小) 。

HTMENU

5

在菜單中。

HTMAXBUTTON

9

在 “最大化 ”按鈕中。

HTMINBUTTON

8

在 “最小化 ”按鈕中。

HTNOWHERE

在螢幕背景上或視窗之間的分隔線上。

HTREDUCE

8

在 “最小化 ”按鈕中。

HTRIGHT

11

在可調整大小的視窗的右邊框 (使用者可以單擊滑鼠以水準調整視窗大小) 。

HTSIZE

4

在大小框中 (與 HTGROWBOX) 相同。

HTSYSMENU

3

在視窗菜單或子視窗的 “關閉 ”按鈕中。

HTTOP

12

在視窗的上水準邊框中。

HTTOPLEFT

13

在視窗邊框的左上角。

HTTOPRIGHT

14

在視窗邊框的右上角。

HTTRANSPARENT

-1

在同一線程 (目前由另一個視窗覆寫的視窗中,消息将發送到同一線程中的基礎視窗,直到其中一個視窗傳回不是 HTTRANSPARENT) 的代碼。

HTVSCROLL

7

在垂直滾動條中。

HTZOOM

9

在 “最大化 ”按鈕中。

歡迎大家一起學習和交流。

繼續閱讀