Win32鼠标事件
Win32鼠标事件
一.鼠标事件
鼠标事件种类分为:
1.左键按下抬起
2.右键按下抬起
3.鼠标移动(坐标)
4.滑轮上下滑动
5.鼠标进入移出客户区
对应的事件代码如下:
class Event
{
public:
enum class Type
{
LPress,
LRelease,
RPress,
RRelease,
WheelUp,
WheelDown,
Move,
Enter,
Leave,
Invalid
};
private:
Type type;
bool leftIsPressed;
bool rightIsPressed;
int x;
int y;
public:
Event() noexcept
:
type(Type::Invalid),
leftIsPressed(false),
rightIsPressed(false),
x(0),
y(0)
{}
Event( Type type,const Mouse& parent ) noexcept
:
type( type ),
leftIsPressed( parent.leftIsPressed ),
rightIsPressed( parent.rightIsPressed ),
x( parent.x ),
y( parent.y )
{}
Type GetType() const noexcept
{
return type;
}
std::pair<int,int> GetPos() const noexcept
{
return{ x,y };
}
int GetPosX() const noexcept
{
return x;
}
int GetPosY() const noexcept
{
return y;
}
bool LeftIsPressed() const noexcept
{
return leftIsPressed;
}
bool RightIsPressed() const noexcept
{
return rightIsPressed;
}
};
二.状态管理
由于鼠标键位没有键盘那么多,只需要一个Queue存储所有事件;
其他状态使用bool值记录即可;
int x;
int y;
bool leftIsPressed = false;
bool rightIsPressed = false;
bool isInWindow = false;
int wheelDeltaCarry = 0;
std::queue<Event> buffer;
滑轮移动:
Windows的滑轮移动事件,wParam代表滑动间隔,标准是120,可以按倍数增加;
因此滑轮检测记录数值,每到120的倍数触发一次事件;
超出屏幕:
超出屏幕有两种情况,鼠标有键位按下和没有键位按下;
按下超出,鼠标被屏幕捕获(capture,Win32Api),无法超出屏幕,松开时解除;
未按下时正常;
以下是WindowProc函数中鼠标消息的拦截处理:
/*****************鼠标消息******************/
case WM_MOUSEMOVE:
{
const POINTS pt = MAKEPOINTS(lParam);
//是否在客户区内
if (pt.x >= 0 && pt.x < width && pt.y >= 0 && pt.y < height)
{
mouse.OnMouseMove(pt.x, pt.y);
if (!mouse.IsInWindow())
{
SetCapture(hWnd);
mouse.OnMouseEnter();
}
}
else
{
//左右键是否按下,也可以使用LeftIsPressd(),不允许鼠标按下时离开客户区
if (wParam & (MK_LBUTTON | MK_RBUTTON))
{
mouse.OnMouseMove(pt.x, pt.y);
}
else
{
ReleaseCapture();
mouse.OnMouseLeave();
}
}
break;
}
case WM_LBUTTONDOWN:
{
const POINTS pt = MAKEPOINTS(lParam);
mouse.OnLeftPressed(pt.x, pt.y);
break;
}
case WM_RBUTTONDOWN:
{
const POINTS pt = MAKEPOINTS(lParam);
mouse.OnRightPressed(pt.x, pt.y);
break;
}
case WM_LBUTTONUP:
{
const POINTS pt = MAKEPOINTS(lParam);
mouse.OnLeftReleased(pt.x, pt.y);
break;
}
case WM_RBUTTONUP:
{
const POINTS pt = MAKEPOINTS(lParam);
mouse.OnRightReleased(pt.x, pt.y);
break;
}
case WM_MOUSEWHEEL:
{
const POINTS pt = MAKEPOINTS(lParam);
const int delta = GET_WHEEL_DELTA_WPARAM(wParam);
mouse.OnWheelDetla(pt.x, pt.y, delta);
}
/*****************鼠标消息******************/
完整代码:
class Mouse
{
friend class Window;
public:
class Event{}//上面展示过,省略;
public:
Mouse() = default;
Mouse( const Mouse& ) = delete;
Mouse& operator=( const Mouse& ) = delete;
std::pair<int,int> GetPos() const noexcept;
int GetPosX() const noexcept;
int GetPosY() const noexcept;
bool IsInWindow() const noexcept;
bool LeftIsPressed() const noexcept;
bool RightIsPressed() const noexcept;
std::optional<Mouse::Event> Read() noexcept;
bool IsEmpty() const noexcept
{
return buffer.empty();
}
void Flush() noexcept;
private:
void OnMouseMove( int x,int y ) noexcept;
void OnMouseLeave() noexcept;
void OnMouseEnter() noexcept;
void OnLeftPressed( int x,int y ) noexcept;
void OnLeftReleased( int x,int y ) noexcept;
void OnRightPressed( int x,int y ) noexcept;
void OnRightReleased( int x,int y ) noexcept;
void OnWheelUp( int x,int y ) noexcept;
void OnWheelDown( int x,int y ) noexcept;
void TrimBuffer() noexcept;
void OnWheelDetla(int x, int y,int delta) noexcept;
private:
static constexpr unsigned int bufferSize = 16u;
int x;
int y;
bool leftIsPressed = false;
bool rightIsPressed = false;
bool isInWindow = false;
int wheelDeltaCarry = 0;
std::queue<Event> buffer;
};
实现中都是简单代码,只列一下滑轮事件的实现;
void Mouse::OnWheelDetla(int x, int y, int delta) noexcept
{
wheelDeltaCarry += delta;
//鼠标滑轮的dpi是120每格子,超过这个值就触发一次事件
while (wheelDeltaCarry >= WHEEL_DELTA)
{
wheelDeltaCarry -= WHEEL_DELTA;
OnWheelUp(x, y);
}
while (wheelDeltaCarry <= -WHEEL_DELTA)
{
wheelDeltaCarry += WHEEL_DELTA;
OnWheelDown(x, y);
}
}
Life is too short for so much sorrow.
本博客所有文章除特别声明外,均采用
CC BY-NC-SA 4.0 许可协议。转载请注明来自 小紫苏!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 【杭电多校比赛记录】2025“钉耙编程”中国大学生算法设计春季联赛(1)