【问题标题】:How to track mouse movements without limiting it to screen size?如何在不限制屏幕大小的情况下跟踪鼠标移动?
【发布时间】:2013-12-28 01:14:48
【问题描述】:

我正在使用 WM_MOUSEMOVE 来更改鼠标位置。例如,在模拟“旋钮”时,希望让用户无限制地用鼠标上下移动。在这种情况下,我隐藏光标并使用 SetCursorPos 在用户每次移动时更改其位置,并仅检测与原始位置的差异。

不幸的是,它似乎不起作用 - 如果我设置鼠标位置,它有时会起作用,但有时会相距一个或多个像素,这是错误的。更大的麻烦是,在调用之后似乎传递了另一个 WM_MOUSEMOVE,不幸的是,它做了同样的事情,因为它想再次将光标移回原始位置。所以它最终会陷入无限循环或设置鼠标位置并接收消息,直到用户释放鼠标按钮。

什么是正确的方法或有什么问题?

【问题讨论】:

  • 你能用鼠标上的滚轮代替 x/y 吗?
  • 我认为您可以使用 DirectX 来获取原始鼠标输入消息,这些消息只是“鼠标移动了这么远”,而不是被转换为(并限制为)屏幕坐标。尝试寻找 DirectInput 示例代码。
  • 鼠标滚轮怕是太笨拙了,还是用吧。 DirectInput 可能会工作,但我真的不想进入这些深水区,该软件必须在非常旧的机器上工作,而 DirectInput 甚至可能不适合。更不用说为了让 DX 为这种微不足道(但显然不是)的事情工作而付出的所有努力。
  • 另外,请考虑如果用户将您的“旋钮”移动很长一段距离,他们将不得不拿起鼠标并再次将其放下,可能会多次。您可以考虑让他们将鼠标绕圈移动(转动旋钮),而不是直接向上或向下移动;这对用户来说可能更容易,并且可以避免您的屏幕边界问题。
  • 我知道,但我认为不会那么糟糕。我个人认为使用“按角度旋转”非常混乱且难以使用。

标签: winapi mousemove


【解决方案1】:

原始输入系统可以做到这一点 - 它允许您注册未剪辑或限制在屏幕边界的原始鼠标输入。

一般来说,您使用RegisterRawInputDevices() 注册原始输入。然后,您的窗口将收到 WM_INPUT 消息,您使用 GetRawInputData() 函数对其进行处理。

有关示例,请参阅 Using Raw Input

【讨论】:

  • 原始输入对于这个来说是多余的。 Capture 就足够了,并且与标准 UI 一致。
  • @RaymondChen 即使鼠标被捕获,它仍然会被裁剪到屏幕边界,不是吗?
  • 是的。我错过了那个细节。
  • 没错!屏幕是问题。我会检查原始输入。
【解决方案2】:

我隐藏光标并使用 SetCursorPos 在每次用户随其移动时更改其位置,并仅检测与原始位置的差异。

这完全是错误的。相反,使用SetCapture() 来捕获鼠标。所有移动都将报告为 WM_MOUSEMOVE 消息,其坐标相对于指定窗口,即使鼠标位于该窗口之外,直到您释放捕获。

【讨论】:

  • 这不是问题。事实上,我使用鼠标捕获。问题不是窗口的限制,而是屏幕的限制!想象一下,您的旋钮在屏幕坐标中大约位于 (50,50) 处。那么你只有50个垂直像素!然后鼠标消息停止出现,因为坐标是负数。
  • 如果您在捕获时保存起始屏幕坐标,您可以使用消息坐标(即使它们为负数)轻松计算鼠标从起始坐标移动的距离。但是,如果您说当您到达屏幕顶部时消息完全停止出现,那就这样吧。正如 Jonathan 建议的那样,您可能需要 Raw Input 才能克服这一点。
【解决方案3】:

要求用户连续移动鼠标,即使在光标达到屏幕限制后,就用户界面而言,恕我直言,这是一个非常糟糕的主意。

有些游戏有另一种方法:当鼠标达到“极限”时,游戏进入一种特殊模式:即使用户没有移动,事物的功能似乎也完全就好像鼠标在移动不要移动它。当用户想要退出该模式时,他只需移动鼠标限制

这样做需要一个计时器,当鼠标达到某个限制时,它会定期执行代码,就像鼠标在移动一样。当真正的鼠标移动超出限制时,计时器将停止。

【讨论】:

  • 嗯,在我工作的领域中,这是一种非常普遍的做法。我想在游戏中它会有意义。
  • @VojtěchMeldaMeluzín 当鼠标在 50cm 外时,用户让它飞回原来的位置?
  • 嗯,这有点夸张,你不觉得吗? ;) 除了你可能很不走运而且“旋钮”将在屏幕顶部下方 10 像素处使其几乎无法使用之外,我所在领域的许多人正在使用其他设备,例如平板电脑等......跨度>
【解决方案4】:

好的,所以我找到了一个非常简单的解决方案:

主要问题是 SetCursorPos 可能无法准确设置坐标,我猜是因为一些高分辨率处理,但它可能是一个错误。无论如何,如果 SetCursorPos 没有正确设置坐标(但在 x 和/或 y 中为 +-1),它也会将 WM_MOUSEMOVE 发送到目标窗口。结果,窗口执行与以前完全相同的操作,并且一直如此。

所以解决方案是在 SetCursorPos 之后立即删除所有 WM_MOUSEMOVE 消息:

MSG msg;
while (::PeekMessage(&msg, NULL, WM_MOUSEMOVE, WM_MOUSEMOVE, PM_REMOVE)) { };

然后使用 ::GetCursorPos 检索当前鼠标光标位置。 这很难看,但似乎可以解决问题。基本上似乎在鼠标的某个位置,系统总是在任一坐标中加或减 1,所以这样你就可以让系统做一些奇怪的事情并使用新的坐标,而不是试图说服系统你的坐标是正确的: )。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-02-06
    • 2017-02-28
    • 1970-01-01
    • 1970-01-01
    • 2021-12-10
    • 1970-01-01
    • 2023-04-01
    相关资源
    最近更新 更多