【问题标题】:Draw mouse cursor绘制鼠标光标
【发布时间】:2012-08-20 00:39:24
【问题描述】:

我正在尝试在 Win32 表单中模拟鼠标光标。在每个WM_MOUSEMOVE 我都有

hCursor = LoadCursor(NULL, IDC_ARROW);
////Get device context
hDeviceContext = GetDC(hwnd);
hDCMem = CreateCompatibleDC(hDeviceContext);
hBitmap = CreateCompatibleBitmap(hDCMem, 50, 50);
hbmOld = SelectObject(hDCMem, hBitmap);
DrawIcon(hDCMem, x, y, hCursor);
SelectObject(hDCMem, hbmOld);

但我没有看到任何被绘制的东西。但是,如果我直接在 DC 上画:

DrawIcon(hDeviceContext, x, y, hCursor);

我确实看到了光标,但是当我移动光标时它并没有擦除图像,留下一条长尾巴。

【问题讨论】:

  • 很好奇,你为什么要模拟光标而不是让 Windows 为你绘制?
  • 我正在开发一个使用高清鼠标的实验性应用程序。我想将鼠标设置为较高的报告 dpi,比如 5000,但在屏幕上的响应与 400 dpi 的普通鼠标相同。
  • 有趣,我添加了一个可能值得考虑的替代方法的答案。

标签: winapi gdi mouse-cursor


【解决方案1】:

不要在WM_MOUSEMOVE 中绘画,这就是WM_PAINT 的用途。基本上,您需要处理三个消息:

    case WM_CREATE:
        hCursor = LoadCursor(NULL, IDC_ARROW);
        cWidth  = GetSystemMetrics(SM_CXCURSOR); // saving the cursor dimensions
        cHeight = GetSystemMetrics(SM_CYCURSOR);
    break;

    case WM_MOUSEMOVE:
        rcOld = rcNew;
        rcNew.left   = GET_X_LPARAM(lParam);     // saving the mouse coordinates
        rcNew.top    = GET_Y_LPARAM(lParam);
        rcNew.right  = rcNew.left + cWidth;
        rcNew.bottom = rcNew.top + cHeight;
        InvalidateRect(hwnd, &rcOld, TRUE);      // asking to redraw the rectangles
        InvalidateRect(hwnd, &rcNew, TRUE);
        UpdateWindow(hwnd);
    break;

    case WM_PAINT:
        hDC = BeginPaint(hwnd, &ps);
        DrawIcon(hDC, rcNew.left, rcNew.top, hCursor);
        EndPaint(hwnd, &ps);
    break;

注意:我不确定“模拟鼠标光标”是什么意思,但可能有更好的方法来做你可能想要的事情。请检查函数SetCursor() 和SetWindowLongPtr() with GCL_HCURSOR。

【讨论】:

  • 重绘整个客户区可能效率低下;每次绘制时最好记住光标矩形,并且在鼠标移动时使新旧矩形无效。也有可能在生成 WM_PAINT 之前获取多个 WM_MOUSEMOVE 消息,因此为了响应,您可能还希望在其中放置一个 UpdateWindow()。
  • @JonathanPotter,谢谢,您的观点都是正确的。我已经编辑了我的答案。
  • DrawIcon 和将光标移动到设备上下文缓冲区并使用 BitBlt 绘制到屏幕有什么区别?
  • @dave,我不能说微软那个,但是DrawIconEx的Wine实现在内部使用StretchBlt:source.winehq.org/source/dlls/user32/cursoricon.c#L2212
【解决方案2】:

物有所值:

另一种可能值得研究的方法是使用 WS_EX_TOPMOST|WS_EX_LAYERED 窗口,您可以根据需要四处移动,并让 Windows 为您处理绘图/透明度。这样做的好处是它完全不会干扰绘图主窗口,并且即使主窗口有子控件或类似的东西也可以工作。

它还可以让您的“鼠标指针”“悬停在窗口的边缘”——例如。当鼠标定位到最右下角像素时,普通鼠标指针的尾部和箭头将在框架和它后面的其他窗口上方,而您不能仅通过绘制自己的窗口客户区来做到这一点.

--

话虽如此,但我不确定“假装”鼠标位置是否正确,尽管它可能会让您启动并运行。另一种方法是让鼠标正常运行,但对于需要更高分辨率的应用程序,有另一个 API 可以公开更精确的值。除了通常的鼠标/键盘消息和 API 之外,Windows 还具有各种技术和 API(例如 Raw Input)用于获取输入信息、定义附加输入设备,并且我认为还将附加信息与消息相关联,其中一种技术可能在这里更合适。重新提出您的问题可能是值得的,但要关注更高级别的问题,而不是光标绘图。

【讨论】:

  • RAW INPUT 正是我目前使用的。 RAW INPUT 报告的鼠标移动与设备驱动程序看到的完全相同。但问题在于,使用高分辨率鼠标时,屏幕上的光标会飞得如此之快,以至于几乎不可能指向边缘。所以基本上我想“下采样”屏幕光标移动(到普通 400dpi 鼠标的移动),同时保持 5000dpi 的物理空间分辨率。另一种选择是编写鼠标驱动程序来进行下采样。更简单的事情似乎是以较低的分辨率模拟应用程序中的光标。
  • 关于快速指针问题 - 那么在鼠标控制面板中更改指针速度怎么样,它会变得足够慢吗?这样你就有了可用的指针速度(Windows 为你下采样),但仍然通过你的应用程序的原始输入获得高分辨率样本。
  • 那行得通。我实际上之前尝试过该选项,但上次没有让它工作 - 问题是在鼠标特定驱动程序中设置速度选项,即罗技。事实证明,您需要使用 Windows 本机驱动程序设置鼠标速度,以便 RAW INPUT 接收未过滤的输入。
猜你喜欢
  • 2011-03-14
  • 2019-11-03
  • 1970-01-01
  • 1970-01-01
  • 2018-08-12
  • 2016-05-06
  • 1970-01-01
  • 1970-01-01
  • 2011-05-04
相关资源
最近更新 更多