From a808a97c50d84219856d9212aa9ae51b6e0586cc Mon Sep 17 00:00:00 2001 From: yixy <34703796+yixy-only@users.noreply.github.com> Date: Sat, 23 Nov 2024 22:41:29 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=A2=9E=E5=8A=A0=E9=BC=A0=E6=A0=87?= =?UTF-8?q?=E6=89=A9=E5=B1=95=E9=94=AE=E7=9A=84=E6=A3=80=E6=B5=8B=20(#237)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: 增加鼠标扩展键的检测 * refactor: 优化鼠标消息处理部分的代码 * fix: 修复 WM_MOUSEWHEEL 提供的屏幕坐标经由 ScreenToClient 计算后与其它消息提供的客户区坐标不一致的问题 --- include/ege.h | 22 +++++-- src/ege_head.h | 9 ++- src/egegapi.cpp | 4 +- src/graphics.cpp | 168 +++++++++++++++++++++-------------------------- src/mouse.cpp | 162 +++++++++++++++++++++++++++------------------ src/mouse.h | 2 + 6 files changed, 198 insertions(+), 169 deletions(-) diff --git a/include/ege.h b/include/ege.h index 133adaf6..18e4c807 100644 --- a/include/ege.h +++ b/include/ege.h @@ -567,6 +567,8 @@ enum key_code_e key_mouse_l = 0x01, key_mouse_r = 0x02, key_mouse_m = 0x04, + key_mouse_x1 = 0x05, + key_mouse_x2 = 0x06, key_back = 0x08, key_tab = 0x09, key_enter = 0x0d, @@ -722,9 +724,11 @@ enum mouse_msg_e enum mouse_flag_e { - mouse_flag_left = 1, - mouse_flag_right = 2, - mouse_flag_mid = 4, + mouse_flag_left = 0x001, + mouse_flag_right = 0x002, + mouse_flag_mid = 0x004, + mouse_flag_x1 = 0x008, + mouse_flag_x2 = 0x010, mouse_flag_shift = 0x100, mouse_flag_ctrl = 0x200 }; @@ -736,12 +740,16 @@ struct mouse_msg mouse_msg_e msg; unsigned int flags; int wheel; + bool is_left() const {return (flags & mouse_flag_left) != 0;} bool is_right() const {return (flags & mouse_flag_right) != 0;} bool is_mid() const {return (flags & mouse_flag_mid) != 0;} - bool is_down() const {return msg == mouse_msg_down;} - bool is_up() const {return msg == mouse_msg_up;} - bool is_move() const {return msg == mouse_msg_move;} + bool is_x1() const {return (flags & mouse_flag_x1) != 0;} + bool is_x2() const {return (flags & mouse_flag_x2) != 0;} + + bool is_down() const {return msg == mouse_msg_down; } + bool is_up() const {return msg == mouse_msg_up; } + bool is_move() const {return msg == mouse_msg_move; } bool is_wheel() const {return msg == mouse_msg_wheel;} }; @@ -753,6 +761,8 @@ struct MOUSEMSG bool mkLButton; bool mkMButton; bool mkRButton; + bool mkXButton1; + bool mkXButton2; short x; short y; short wheel; diff --git a/src/ege_head.h b/src/ege_head.h index 44c07b9d..7a686f2f 100644 --- a/src/ege_head.h +++ b/src/ege_head.h @@ -124,6 +124,8 @@ #define DEFAULT_CHARSET ANSI_CHARSET #endif +#include "types.h" + namespace ege { @@ -200,10 +202,7 @@ struct _graph_setting HANDLE threadui_handle; /* 鼠标状态记录 */ - int mouse_state_l, mouse_state_m, mouse_state_r; - int mouse_last_x, mouse_last_y; - int mouse_lastclick_x, mouse_lastclick_y; - int mouse_lastup_x, mouse_lastup_y; + Point mouse_pos; int mouse_show; LPMSG_KEY_PROC callback_key; @@ -212,7 +211,7 @@ struct _graph_setting void* callback_mouse_param; LPCALLBACK_PROC callback_close; - /* 键盘状态记录 */ + /* 按键状态记录 */ int keystatemap[MAX_KEY_VCODE]; /* egeControlBase */ diff --git a/src/egegapi.cpp b/src/egegapi.cpp index 6275547f..bf009090 100644 --- a/src/egegapi.cpp +++ b/src/egegapi.cpp @@ -55,8 +55,8 @@ int showmouse(int bShow) int mousepos(int* x, int* y) { struct _graph_setting* pg = &graph_setting; - *x = pg->mouse_last_x; - *y = pg->mouse_last_y; + *x = pg->mouse_pos.x; + *y = pg->mouse_pos.y; return 0; } diff --git a/src/graphics.cpp b/src/graphics.cpp index 65e437f7..18aca338 100644 --- a/src/graphics.cpp +++ b/src/graphics.cpp @@ -41,6 +41,8 @@ #include #include +#include + #include "ege_head.h" #include "ege_common.h" #include "ege_extension.h" @@ -435,18 +437,79 @@ static void on_key(struct _graph_setting* pg, UINT message, unsigned long keycod } /*private function*/ -static void push_mouse_msg(struct _graph_setting* pg, UINT message, WPARAM wparam, LPARAM lparam) +static void push_mouse_msg(struct _graph_setting* pg, UINT message, WPARAM wparam, LPARAM lparam, int time) { EGEMSG msg = {0}; msg.hwnd = pg->hwnd; msg.message = message; msg.wParam = wparam; msg.lParam = lparam; - msg.mousekey = (pg->mouse_state_m << 2) | (pg->mouse_state_r << 1) | (pg->mouse_state_l << 0); - msg.time = ::GetTickCount(); + + msg.mousekey |= pg->keystatemap[VK_LBUTTON] ? mouse_flag_left : 0; + msg.mousekey |= pg->keystatemap[VK_RBUTTON] ? mouse_flag_right : 0; + msg.mousekey |= pg->keystatemap[VK_MBUTTON] ? mouse_flag_mid : 0; + msg.mousekey |= pg->keystatemap[VK_XBUTTON1] ? mouse_flag_x1 : 0; + msg.mousekey |= pg->keystatemap[VK_XBUTTON2] ? mouse_flag_x2 : 0; + + msg.time = time; pg->msgmouse_queue->push(msg); } +static void mouseProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + /* up 消息会后紧跟一条 move 消息,标记并将其忽略 */ + static bool skipNextMoveMessage = false; + if ((message < WM_MOUSEFIRST) || (message > WM_MOUSELAST)) + return; + + _graph_setting* pg = &graph_setting; + + bool curMsgIsNeedToPush = true; + + int key = 0; + + /* WINAPI bug: WM_MOUSEWHEEL 提供的是屏幕坐标,DPI 不等于 100% 时 ScreenToClient 的计算 + * 结果与其它鼠标消息提供的坐标不一致,故忽略 lParam 提供的值,直接使用之前记录的客户区坐标 + */ + if (message == WM_MOUSEWHEEL) { + lParam = MAKELPARAM(pg->mouse_pos.x, pg->mouse_pos.y); + } + + mouse_msg msg = mouseMessageConvert(message, wParam, lParam, &key); + Point curPos(msg.x, msg.y); + + if (msg.is_up()) { + skipNextMoveMessage = true; + } else if (msg.is_move()) { + /* 忽略 up 消息后伴随的同位置 move 消息 */ + if (skipNextMoveMessage && (curPos == pg->mouse_pos)) { + curMsgIsNeedToPush = false; + skipNextMoveMessage = false; + } + } + + /* 鼠标按键动作 */ + if (key != 0) { + pg->keystatemap[key] = msg.is_down(); + + /* 设置鼠标消息捕获 */ + if (msg.is_down()) { + SetCapture(hWnd); + } else { + const int keyStateMask = MK_LBUTTON | MK_RBUTTON | MK_MBUTTON | MK_XBUTTON1 | MK_XBUTTON2; + if ((wParam & keyStateMask) == 0) { + ReleaseCapture(); + } + } + } + + if (curMsgIsNeedToPush && (hWnd == pg->hwnd)) { + push_mouse_msg(pg, message, wParam, lParam, GetMessageTime()); + } + + pg->mouse_pos = curPos; +} + /*private function*/ static LRESULT CALLBACK wndproc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { @@ -526,95 +589,15 @@ static LRESULT CALLBACK wndproc(HWND hWnd, UINT message, WPARAM wParam, LPARAM l } } break; - case WM_LBUTTONDOWN: - case WM_LBUTTONDBLCLK: - pg->mouse_lastclick_x = (short int)((UINT)lParam & 0xFFFF); - pg->mouse_lastclick_y = (short int)((UINT)lParam >> 16); - pg->keystatemap[VK_LBUTTON] = 1; - SetCapture(hWnd); - pg->mouse_state_l = 1; - if (hWnd == pg->hwnd) { - push_mouse_msg(pg, message, wParam, lParam); - } - break; - case WM_MBUTTONDOWN: - case WM_MBUTTONDBLCLK: - pg->mouse_lastclick_x = (short int)((UINT)lParam & 0xFFFF); - pg->mouse_lastclick_y = (short int)((UINT)lParam >> 16); - pg->keystatemap[VK_MBUTTON] = 1; - SetCapture(hWnd); - pg->mouse_state_m = 1; - if (hWnd == pg->hwnd) { - push_mouse_msg(pg, message, wParam, lParam); - } - break; - case WM_RBUTTONDOWN: - case WM_RBUTTONDBLCLK: - pg->mouse_lastclick_x = (short int)((UINT)lParam & 0xFFFF); - pg->mouse_lastclick_y = (short int)((UINT)lParam >> 16); - pg->keystatemap[VK_RBUTTON] = 1; - SetCapture(hWnd); - pg->mouse_state_r = 1; - if (hWnd == pg->hwnd) { - push_mouse_msg(pg, message, wParam, lParam); - } - break; - case WM_LBUTTONUP: - pg->mouse_lastup_x = (short int)((UINT)lParam & 0xFFFF); - pg->mouse_lastup_y = (short int)((UINT)lParam >> 16); - pg->mouse_state_l = 0; - pg->keystatemap[VK_LBUTTON] = 0; - if (pg->mouse_state_l == 0 && pg->mouse_state_m == 0 && pg->mouse_state_r == 0) { - ReleaseCapture(); - } - if (hWnd == pg->hwnd) { - push_mouse_msg(pg, message, wParam, lParam); - } - break; - case WM_MBUTTONUP: - pg->mouse_lastup_x = (short int)((UINT)lParam & 0xFFFF); - pg->mouse_lastup_y = (short int)((UINT)lParam >> 16); - pg->mouse_state_m = 0; - pg->keystatemap[VK_MBUTTON] = 0; - if (pg->mouse_state_l == 0 && pg->mouse_state_m == 0 && pg->mouse_state_r == 0) { - ReleaseCapture(); - } - if (hWnd == pg->hwnd) { - push_mouse_msg(pg, message, wParam, lParam); - } - break; - case WM_RBUTTONUP: - pg->mouse_lastup_x = (short int)((UINT)lParam & 0xFFFF); - pg->mouse_lastup_y = (short int)((UINT)lParam >> 16); - pg->mouse_state_r = 0; - pg->keystatemap[VK_RBUTTON] = 0; - if (pg->mouse_state_l == 0 && pg->mouse_state_m == 0 && pg->mouse_state_r == 0) { - ReleaseCapture(); - } - if (hWnd == pg->hwnd) { - push_mouse_msg(pg, message, wParam, lParam); - } - break; - case WM_MOUSEMOVE: - pg->mouse_last_x = (short int)((UINT)lParam & 0xFFFF); - pg->mouse_last_y = (short int)((UINT)lParam >> 16); - if (hWnd == pg->hwnd && (pg->mouse_lastup_x != pg->mouse_last_x || pg->mouse_lastup_y != pg->mouse_last_y)) { - push_mouse_msg(pg, message, wParam, lParam); - } - break; - case WM_MOUSEWHEEL: { - POINT pt; - pt.x = (short int)((UINT)lParam & 0xFFFF); - pt.y = (short int)((UINT)lParam >> 16); - ScreenToClient(pg->hwnd, &pt); - pg->mouse_last_x = pt.x; - pg->mouse_last_y = pt.y; - lParam = ((unsigned short)(short int)pg->mouse_last_y << 16) | (unsigned short)(short int)pg->mouse_last_x; - } - if (hWnd == pg->hwnd) { - push_mouse_msg(pg, message, wParam, lParam); - } + + case WM_LBUTTONDOWN: case WM_LBUTTONUP: case WM_LBUTTONDBLCLK: + case WM_MBUTTONDOWN: case WM_MBUTTONUP: case WM_MBUTTONDBLCLK: + case WM_RBUTTONDOWN: case WM_RBUTTONUP: case WM_RBUTTONDBLCLK: + case WM_XBUTTONDOWN: case WM_XBUTTONUP: case WM_XBUTTONDBLCLK: + case WM_MOUSEMOVE: case WM_MOUSEWHEEL: + mouseProc(hWnd, message, wParam, lParam); break; + case WM_SETCURSOR: if (pg == pg_w) { on_setcursor(pg, hWnd); @@ -850,8 +833,7 @@ void initgraph(int* gdriver, int* gmode, const char* path) POINT pt; GetCursorPos(&pt); ScreenToClient(pg->hwnd, &pt); - pg->mouse_last_x = pt.x; - pg->mouse_last_y = pt.y; + pg->mouse_pos = Point(pt.x, pt.y); static egeControlBase _egeControlBase; diff --git a/src/mouse.cpp b/src/mouse.cpp index 1d29004b..5e436974 100644 --- a/src/mouse.cpp +++ b/src/mouse.cpp @@ -3,6 +3,8 @@ #include "mouse.h" +#include + namespace ege { @@ -32,6 +34,65 @@ static EGEMSG peekmouse(_graph_setting* pg) return msg; } +mouse_msg mouseMessageConvert(UINT message, WPARAM wParam, LPARAM lParam, int* key) +{ + mouse_msg msg = {0}; + + /* WINAPI bug: WM_MOUSEWHEEL 提供的是屏幕坐标,DPI 不等于 100% 时 ScreenToClient 的计算 + * 结果与其它鼠标消息提供的坐标不一致。因此 lParam 参数已经处理过,直接使用之前记录的客户区坐标, + * 这里不需要再进行转换。 + */ + msg.x = GET_X_LPARAM(lParam); + msg.y = GET_Y_LPARAM(lParam); + + int vkCode = 0; + + if (message == WM_MOUSEMOVE) { + msg.msg = mouse_msg_move; + } else if (message == WM_MOUSEWHEEL) { + msg.msg = mouse_msg_wheel; + msg.wheel = GET_WHEEL_DELTA_WPARAM(wParam); + } else if ((message >= WM_XBUTTONDOWN) && (message <= WM_XBUTTONDBLCLK)) { + switch(GET_XBUTTON_WPARAM(wParam)) { + case XBUTTON1: msg.flags |= mouse_flag_x1; vkCode = key_mouse_x1; break; + case XBUTTON2: msg.flags |= mouse_flag_x2; vkCode = key_mouse_x2; break; + default: break; + } + + switch (message) { + case WM_XBUTTONDOWN: msg.msg = mouse_msg_down; break; + case WM_XBUTTONUP: msg.msg = mouse_msg_up; break; + //case WM_XBUTTONDBLCLK: msg.msg = mouse_msg_down; break; + default:break; + } + } else { + switch(message) { + case WM_LBUTTONDOWN: msg.flags |= mouse_flag_left; msg.msg = mouse_msg_down; vkCode = key_mouse_l; break; + case WM_LBUTTONUP: msg.flags |= mouse_flag_left; msg.msg = mouse_msg_up; vkCode = key_mouse_l; break; + //case WM_LBUTTONDBLCLK: msg.flags |= mouse_flag_left; break; + + case WM_RBUTTONDOWN: msg.flags |= mouse_flag_right; msg.msg = mouse_msg_down; vkCode = key_mouse_r; break; + case WM_RBUTTONUP: msg.flags |= mouse_flag_right; msg.msg = mouse_msg_up; vkCode = key_mouse_r; break; + //case WM_RBUTTONDBLCLK: msg.flags |= mouse_flag_right; break; + + case WM_MBUTTONDOWN: msg.flags |= mouse_flag_mid; msg.msg = mouse_msg_down; vkCode = key_mouse_m; break; + case WM_MBUTTONUP: msg.flags |= mouse_flag_mid; msg.msg = mouse_msg_up; vkCode = key_mouse_m; break; + //case WM_MBUTTONDBLCLK: msg.flags |= mouse_flag_mid; break; + default: break; + } + } + + /* 读取辅助键状态 */ + msg.flags |= (wParam & MK_CONTROL) ? mouse_flag_ctrl : 0; + msg.flags |= (wParam & MK_SHIFT) ? mouse_flag_shift : 0; + + if (key != NULL) { + *key = vkCode; + } + + return msg; +} + void flushmouse() { struct _graph_setting* pg = &graph_setting; @@ -63,48 +124,20 @@ int mousemsg() mouse_msg getmouse() { - struct _graph_setting* pg = &graph_setting; - mouse_msg mmsg = {0}; + struct _graph_setting* pg = &graph_setting; + mouse_msg mmsg = {0}; + if (pg->exit_window) { return mmsg; } - { - EGEMSG msg; - do { - msg = _getmouse(pg); - if (msg.hwnd) { - mmsg.flags |= ((msg.wParam & MK_CONTROL) != 0 ? mouse_flag_ctrl : 0); - mmsg.flags |= ((msg.wParam & MK_SHIFT) != 0 ? mouse_flag_shift : 0); - mmsg.x = (short)((int)msg.lParam & 0xFFFF); - mmsg.y = (short)((unsigned)msg.lParam >> 16); - mmsg.msg = mouse_msg_move; - if (msg.message == WM_LBUTTONDOWN) { - mmsg.msg = mouse_msg_down; - mmsg.flags |= mouse_flag_left; - } else if (msg.message == WM_RBUTTONDOWN) { - mmsg.msg = mouse_msg_down; - mmsg.flags |= mouse_flag_right; - } else if (msg.message == WM_MBUTTONDOWN) { - mmsg.msg = mouse_msg_down; - mmsg.flags |= mouse_flag_mid; - } else if (msg.message == WM_LBUTTONUP) { - mmsg.msg = mouse_msg_up; - mmsg.flags |= mouse_flag_left; - } else if (msg.message == WM_RBUTTONUP) { - mmsg.msg = mouse_msg_up; - mmsg.flags |= mouse_flag_right; - } else if (msg.message == WM_MBUTTONUP) { - mmsg.msg = mouse_msg_up; - mmsg.flags |= mouse_flag_mid; - } else if (msg.message == WM_MOUSEWHEEL) { - mmsg.msg = mouse_msg_wheel; - mmsg.wheel = (short)((unsigned)msg.wParam >> 16); - } - return mmsg; - } - } while (!pg->exit_window && !pg->exit_flag && waitdealmessage(pg)); - } + do { + EGEMSG msg = _getmouse(pg); + if (msg.hwnd) { + return mouseMessageConvert(msg.message, msg.wParam, msg.lParam); + } + } while (!pg->exit_window && !pg->exit_flag && waitdealmessage(pg)); + return mmsg; } @@ -116,32 +149,35 @@ MOUSEMSG GetMouseMsg() return mmsg; } - { - EGEMSG msg; - do { - msg = _getmouse(pg); - if (msg.hwnd) { - mmsg.uMsg = msg.message; - mmsg.mkCtrl = ((msg.wParam & MK_CONTROL) != 0); - mmsg.mkShift = ((msg.wParam & MK_SHIFT) != 0); - mmsg.x = (short)((int)msg.lParam & 0xFFFF); - mmsg.y = (short)((unsigned)msg.lParam >> 16); - if (msg.mousekey & 1) { - mmsg.mkLButton = 1; - } - if (msg.mousekey & 4) { - mmsg.mkMButton = 1; - } - if (msg.mousekey & 2) { - mmsg.mkRButton = 1; - } - if (msg.message == WM_MOUSEWHEEL) { - mmsg.wheel = (short)((unsigned)msg.wParam >> 16); - } - return mmsg; + EGEMSG msg = {0}; + do { + msg = _getmouse(pg); + if (msg.hwnd) { + mmsg.uMsg = msg.message; + + /* WINAPI bug: WM_MOUSEWHEEL 提供的是屏幕坐标,DPI 不等于 100% 时 ScreenToClient 的计算 + * 结果与其它鼠标消息提供的坐标不一致。因此 lParam 参数已经处理过,直接使用之前记录的客户区坐标, + * 这里不需要再进行转换。 + */ + mmsg.x = GET_X_LPARAM(msg.lParam); + mmsg.y = GET_Y_LPARAM(msg.lParam); + + /* mouse key states */ + mmsg.mkCtrl = ((msg.wParam & MK_CONTROL) != 0); + mmsg.mkShift = ((msg.wParam & MK_SHIFT) != 0); + mmsg.mkLButton = ((msg.wParam & MK_LBUTTON) != 0); + mmsg.mkRButton = ((msg.wParam & MK_RBUTTON) != 0); + mmsg.mkMButton = ((msg.wParam & MK_MBUTTON) != 0); + mmsg.mkXButton1 = ((msg.wParam & MK_XBUTTON1) != 0); + mmsg.mkXButton2 = ((msg.wParam & MK_XBUTTON2) != 0); + + if (msg.message == WM_MOUSEWHEEL) { + mmsg.wheel = GET_WHEEL_DELTA_WPARAM(msg.wParam); } - } while (!pg->exit_window && waitdealmessage(pg)); - } + return mmsg; + } + } while (!pg->exit_window && waitdealmessage(pg)); + return mmsg; } diff --git a/src/mouse.h b/src/mouse.h index 604dfa7f..35bcb1ae 100644 --- a/src/mouse.h +++ b/src/mouse.h @@ -3,4 +3,6 @@ namespace ege { +mouse_msg mouseMessageConvert(UINT message, WPARAM wParam, LPARAM lParam, int* keyCode = NULL); + }