第三章、鍵盤和滑鼠
精華濃縮:
鍵盤和滑鼠恐怕是用電腦的人摸的最多的兩樣東西(當然,DIYer們除外),也正因為有很直觀的印象,要理解它們並不難。
在windows中,鍵盤和滑鼠輸入是以訊息的形式出現的。首先,裝置驅動程式接收並處理滑鼠和鍵盤中斷,通過中斷處理(ISR)把最初的電脈衝訊號轉換為系統識別的事件通知(就是訊息)放入系統訊息佇列中。作業系統核心有一個專門的線程(應該就是user32.dll)來監控系統訊息佇列,通過它把訊息分發到各應用程式線程訊息佇列中。以後,就是應用程式對訊息的檢索處理,在MFC開發中,就是第一章講到的訊息映射機制了。
學習如何響應鍵盤和滑鼠的輸入關鍵是要瞭解該處理哪個訊息以及和處理相關的一些MFC和API函數。
滑鼠訊息首先分為兩大類,即客戶區訊息和非客戶區訊息。再按不同輸入動作,又細分為左(右、中)鍵單擊、雙擊和按下、彈起以及滑鼠移動訊息等。例如WM_LBUTTONDOWN, 表示這是客戶區滑鼠左鍵按下訊息,而WM_NCMBUTTONUP, 則表示這是非客戶區(Not Client)滑鼠中鍵彈起訊息。滑鼠事件和對應訊息的詳情可參考MSDN 。
在MFC中,客戶區滑鼠訊息處理函數的原型為:
afx_msg void OnMsgName(UINT nFlags,CPoint point)
其中,point指示當前游標位置,這個位置座標是以相對於視窗客戶區左上方的裝置座標而言的。如有必要,可用CDC::DPtoLP()將其轉換為邏輯座標。nFlags參數指出訊息產生時滑鼠鍵以及Shift和Ctrl的狀態。通常用於判斷滑鼠或鍵盤狀態,如 if(nFlags & MK_LBUTTON) ...。
非客戶區訊息的處理函數原型為:
afx_msg void OnMsgName(UINT nHitTest,CPoint point)
其中,point指示事件在視窗中發生的位置,但這個位置是螢幕座標。可用CWnd::ScreenToClient()把螢幕座標轉換為客戶區座標。nHitTest參數包含標識視窗非客戶區事件發生地方的點擊測試碼。如HTCAPTION、HTCLOSE、HTSYSMENU等。可在WM_NCHITTEST或CWnd::OnNcHitTest()的協助文檔中找到完整的點擊測試碼列表。實際應用與nFlags類似。
滑鼠游標是滑鼠與使用者之間的介面。我們知道,每個視窗都有相應的WNDCLASS, 其特性在WNDCLASS結構中定義。其中的hCursor欄位儲存了視窗類別型游標的控制代碼,該游標就是在視窗客戶區上的圖象。在滑鼠移動時,windows通過重畫游標的背景把游標從舊位置上清除。然後,它給游標下的視窗發送包含點擊測試碼的WM_SETCURSOR訊息。對此訊息系統的預設響應是調用::SetCursor()API函數。如果點擊測試碼是HTCLIENT(即在客戶區中),則顯示設定的類型游標,否則顯示預設箭頭游標。游標將根據滑鼠移動時周期的捕獲點擊測試碼自動更新。這個技巧也可以應用到其他圖象處理過程中。
設定游標的方法總結:
1.註冊WNDCLASS(如何註冊,在後面知識點部分有總結),並指定希望的游標類型為視窗類別游標;
2.或在OnSetCursor訊息處理函數中調用API函數::SetCursor()響應WM_SETCURSOR訊息。
windows應用程式瞭解鍵盤事件的方式與瞭解滑鼠事件相同:都是通過訊息。鍵盤訊息只有三種,即
WM_KEYDOWN 鍵盤按下
WM_KEYUP 鍵盤抬起
WM_CHAR 可列印字元鍵按下或抬起(這是為了簡化字元處理過程)
windows總是把鍵盤訊息送到擁有輸入焦點的視窗。它用WM_SETFOCUS和WM_KILLFOCUS訊息通知即將通知接收或失去輸入焦點的視窗。CWnd::SetFocus()把輸入焦點轉移到另一個視窗;CWnd::GetFocus()用於找到當前用於輸入焦點的視窗,它的傳回型別為CWnd。
一個鍵盤處理函數會接收很多有關擊鍵的訊息,其中包括一個代碼用來標識被按下或釋放的索引值。原型如下:
afx_msg void OnMsgName(UINT nChar,UINT nRepCnt,UINT nFlags)
其中,nChar是被按下或釋放的鍵的虛擬索引值,nRepCnt是重複次數,通常為1。nFlags參數包含鍵的掃描碼以及一些位標誌(詳情可參考MSDN)。
和滑鼠與游標關聯一樣,與鍵盤有關的系統資源是插入符。但游標是全域共用資源,而插入符是單線程共用資源,它被運行在同一線程上的所有視窗共用。
插入符的使用有一些簡單規則:
1.插入符應在接收到輸入焦點時建立,在失去輸入焦點時“銷毀”。相關函數有CreateCaret、CreateGrayCaret、::DestroyCaret等;
2.直到調用ShowCaret才能使建立的插入符可見,而調用HideCaret可將使其隱藏;
3.調用SetCaretPos來移動插入符,永遠記住,控制插入符的移動是你自己的工作。對插入符位置的檢索可用GetCaretPos。
一些知識點:
1.自訂視窗類別型:在MFC中,可以用全域函數AfxRigisterWndClass註冊視窗類別型。這需要初始化WNDCLASS結構中的欄位。AfxRigisterWndClass已為你自動填滿了大多數欄位,你一般只需要關心4個值。AfxRigisterWndClass的函數原型為:
LPCTSTR AfxRigisterWndClass(UINT nClassStyle,HCURSOR hCursor=0,HBRUSH hbrBackground=0,HICON hIcon=0)
其中,nClassStyle指定視窗類別型樣式(不是顯示類型),而是視窗允許的某種操作特性。如CS_OWNDC,表示由WNDCLASS建立的視窗均有自己的裝置描述表(DC);hCursor為由WNDCLASS建立的視窗標識“類型游標”。可通過AfxGetApp()->LoadStandardCursor(IDC_XXX)獲得;hbrBackground參數定義了視窗的預設背景顏色;最後一個參數hIcon指定windows用來在案頭上、工作列等地方代表應用程式的大、小表徵圖。
在MFC中,調用此函數通常通過覆蓋PreCreateWindow虛函數完成。
2.一些非主流的滑鼠訊息:
1)WM_NCHITTEST訊息:視窗在接受一個客戶區或非客戶區滑鼠訊息之前,首先接收到游標的螢幕座標和WM_NCHITTEST訊息。windows一般預設處理它。一個使用OnNcHitTest處理函數的技巧是用HTCAPTION點擊測試碼代替HTCLIENT,以建立一個可在客戶區拖動的視窗:
UINT CMainWindow::OnNcHitTest(CPoint point)
{
UINT nHitTest = CFrameWnd::OnNcHitTest(point);
if(nHitTest == HTCLIENT)
nHitTest=HICAPTION;
return nHitTest;
}
2)WM_MOUSELEAVE和WM_MOUSEHOVER訊息:這兩個訊息可以使你知道滑鼠何時進入視窗或在視窗中移動了。與此相關的API函數是::TrackMouseEvent(),通過它,一個程式可以註冊,當游標離開視窗時接收WM_MOUSELEAVE訊息,而游標在視窗中停滯時接收WM_MOUSEHOVER訊息。::TrackMouseEvent()函數只有一個參數,是一個指向TRACKMOUSEEVENT結構的指標。
3)WM_MOUSEWHEEL訊息: windows的ON_WM_MOUSEWHEEL宏將WM_MOUSEWHEEL訊息映射到訊息處理函數OnMouseWheel,其原型為:
BOOL OnMouseWheel(UINT nFlags,short zDelta,CPoint point)
其中,nFlags和point與其他滑鼠處理函數意義相同,zDelta的值等於WHEEL_DELTA(向前滾動一個增量)或-WHEEL_DELTA(向後滾動一個增量)。
3.滑鼠的捕獲:這通常用於“橡皮筋”畫圖操作中。用CWnd::SetCapture捕獲滑鼠,相應的用::ReleaseCapture釋放它。