c语言 - GLFW 鼠标事件滞后与窗口拖动



我正在尝试在 GLFW 中拖动一个未修饰的窗口,但遇到了一些事件延迟,即使我正在使用glfwWaitEvents()

我有一个光标位置回调和一个简单的循环:

// register a cursor position callback
glfwSetCursorPosCallback(win, cursor_pos_callback);
// then loop..
while(!glfwWindowShouldClose(win)) {
glfwWaitEvents();
... some rendering...
glfwSwapBuffers(win);
}

我的光标回调对增量进行一些简单的跟踪并更新窗口位置。

cursor_pos_callback(GLFWwindow *win, double xpos, double ypos) {
// figure out delta_x and delta_y based on cursor previous position
int delta_x, delta_y;
// update window position
if (window_drag_active) {
int x,y;
glfwGetWindowPos(window, &x, &y);
glfwSetWindowPos(window, x + delta_x, y + delta_y);
}
}

这是我在直线上拖动时增量的样子

delta_x:  10    delta_y:   0    |    xpos:   649    ypos: 55
delta_x:   5    delta_y:  -1    |    xpos:   654    ypos: 54
delta_x:   5    delta_y:   3    |    xpos:   659    ypos: 57
delta_x:   5    delta_y:   2    |    xpos:   664    ypos: 59
delta_x:  -5    delta_y:  -2    |    xpos:   659    ypos: 57
delta_x:   4    delta_y:   0    |    xpos:   663    ypos: 57
delta_x:   2    delta_y:   0    |    xpos:   665    ypos: 57
delta_x:  -3    delta_y:  -3    |    xpos:   662    ypos: 54
delta_x:   2    delta_y:   1    |    xpos:   664    ypos: 55
delta_x:   2    delta_y:   0    |    xpos:   666    ypos: 55
delta_x:   3    delta_y:   2    |    xpos:   669    ypos: 57
delta_x:   1    delta_y:  -1    |    xpos:   670    ypos: 56
delta_x:   2    delta_y:  -1    |    xpos:   672    ypos: 55
delta_x:   7    delta_y:   3    |    xpos:   679    ypos: 58
delta_x:   2    delta_y:  -1    |    xpos:   681    ypos: 57
delta_x:  -2    delta_y:  -3    |    xpos:   679    ypos: 54
delta_x:   0    delta_y:  -2    |    xpos:   679    ypos: 52
delta_x:   3    delta_y:   3    |    xpos:   682    ypos: 55
delta_x:  -5    delta_y:  -3    |    xpos:   677    ypos: 52

xpos按应有的方式递增,然后每隔一段时间就会倒退(陈旧事件?

也许我的窗口移动没有与光标同步?

结果是当我拖动窗口时,它剧烈摇晃,我几乎无法在任何地方移动它......


更新:我也尝试将glfwSetWindowPos逻辑移动到主循环中,但没有成功——我仍然得到同样的摇晃和口吃。


更新:当我注释掉glfwSetWindowPos()时,窗口不再移动(当然),但事件流现在是一致的。

这让我认为,当快速完成时,窗口的移动会导致抽搐运动(即向前 2 步,向后退 1 步)。

我怀疑您的问题是cursor_pos_callback接收相对于窗口的光标位置,并且移动窗口对该位置有直接影响。

假设您以恒定速率对角线移动光标。 如果在一个刻度以上,光标从相对位置 (100,100) 移动到 (105,105),则计算delta_x=5delta_y=5。 然后移动窗口。 然后,移动窗口的过程会立即将相对坐标从 (105,105) 更改回 (100,100),并且在下一次刻度时,即使您已移动到相对于原始窗口位置的位置 (110,110),相对于新窗口位置,您也仅处于相对位置 (105,105),因此即使您实际上移动了 5 个单位,您也会从之前的位置计算delta_x=0delta_y=0(加上一些随机抖动噪音)沿每个轴。

相反,请修改算法以保持恒定的相对光标位置。 在拖动开始时,存储相对光标位置(例如 (100,100))。 现在,在每个刻度处,计算您需要放置窗口的位置,以使光标移回该固定的相对位置。 因此,如果光标已移动到 (112,108),请将窗口移动 (+12,+8) 以将光标放回 (100,100)。 在稍后的刻度中,如果光标已移动到 (108,106),请不要尝试从 (112,108) 计算增量;相反,将其与原始起点 (100,100) 进行比较,并将窗口移动 (+8,+6)。 它将如下所示:

cursor_pos_callback(GLFWwindow *win, double xpos, double ypos) {
// update window position
if (window_drag_active) {
int delta_x = xpos - window_drag_starting_xpos;
int delta_y = ypos - window_drag_starting_ypos;
int x,y;
glfwGetWindowPos(window, &x, &y);
glfwSetWindowPos(window, x + delta_x, y + delta_y);
}
}

我将另一个答案标记为可接受的解决方案,因为您确实需要记录初始起始光标位置(相对于窗口)并使用它来计算增量。

但是,这样做只能提供一半的解决方案。 拖拽时,我仍然经历了一些重大的跳跃和在屏幕上射击!

最后,我发现此同步问题来自我设置新窗口位置的位置。

要完全消除任何延迟,并确保窗口 + 光标同步您必须渲染循环中同时执行glfwGetWindowPos()glfwSetWindowPos()

在回调内部这样做将导致抖动,仓位变得不同步。所以我相信解决方案的一部分也是确保尽可能保持窗口+光标同步。

这是我来的一个最小的例子(非常感谢 K.A. 布尔!

#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <GLFW/glfw3.h>
static double cursor_pos_x = 0;
static double cursor_pos_y = 0;
static double delta_x = 0;
static double delta_y = 0;
static int window_drag_active = 0;
static void mouse_button_callback(GLFWwindow *window, int button, int action,
int mods) {
if (button == GLFW_MOUSE_BUTTON_LEFT && action == GLFW_PRESS) {
window_drag_active = 1;
double x, y;
glfwGetCursorPos(window, &x, &y);
cursor_pos_x = floor(x);
cursor_pos_y = floor(y);
}
if (button == GLFW_MOUSE_BUTTON_LEFT && action == GLFW_RELEASE) {
window_drag_active = 0;
}
}
int main() {
glfwInit();
GLFWwindow *win = glfwCreateWindow(400, 500, "Drag Example", NULL, NULL);
glfwSetMouseButtonCallback(win, mouse_button_callback);
glfwMakeContextCurrent(win);
while (!glfwWindowShouldClose(win)) {
glfwWaitEvents();
if (window_drag_active) {
double xpos, ypos;
glfwGetCursorPos(win, &xpos, &ypos);
delta_x = xpos - cursor_pos_x;
delta_y = ypos - cursor_pos_y;
int x, y;
glfwGetWindowPos(win, &x, &y);
glfwSetWindowPos(win, x + delta_x, y + delta_y);
}
glfwSwapBuffers(win);
}
return 0;
}

最新更新