GLFW 鼠标事件延迟 window 拖动
GLFW Mouse event lag with window drag
我试图在 GLFW 中拖动未修饰的 window,但遇到了一些事件延迟,即使我使用的是 glfwWaitEvents()
我有一个光标位置回调和一个简单的循环:
// register a cursor position callback
glfwSetCursorPosCallback(win, cursor_pos_callback);
// then loop..
while(!glfwWindowShouldClose(win)) {
glfwWaitEvents();
... some rendering...
glfwSwapBuffers(win);
}
我的光标回调对增量进行一些简单的跟踪并更新 window 位置。
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
应该递增,然后每隔一段时间就会倒退(陈旧的事件?)
也许我的window移动与光标不同步?
结果是window拖的时候摇的很厉害,几乎不能动...
更新:我也尝试过将 glfwSetWindowPos
逻辑移动到主循环中,但没有成功——我仍然遇到同样的抖动和口吃。
更新:当我注释掉 glfwSetWindowPos()
时,window 不再移动 (当然) 但事件流现在是一致的。
这让我想到 window 的移动如果快速完成会导致急动(即向前 2 步,向后 1 步)。
我怀疑你的问题是 cursor_pos_callback
收到相对于 window 的光标位置 并且移动 window 会立即对该位置的影响。
假设您正以恒定速率沿对角线移动光标。如果在一个刻度内,光标从相对位置 (100,100) 移动到 (105,105),则计算 delta_x=5
和 delta_y=5
。然后你移动 window。移动 window 的过程会立即将相对坐标从 (105,105) 更改回 (100,100),并在下一次报价时,即使您已经移动到相对于原始 [=26 的位置 (110,110) =] 位置,相对于新的 window 位置,您仅处于 relative 位置 (105,105),因此您计算 delta_x=0
和 delta_y=0
你以前的位置(加上一些随机的抖动噪音)即使你实际上已经沿着每个轴移动了 5 个单位。
相反,修改您的算法以保持恒定的相对光标位置。在拖动开始时,存储相对光标位置(例如(100,100))。现在,在每个刻度处,计算您需要将 window 定位的位置,以将光标移回该固定的相对位置。因此,如果光标移动到 (112,108),则将 window 移动 (+12,+8) 以将光标放回 (100,100)。在稍后的滴答中,如果光标移动到 (108,106),请不要尝试从 (112,108) 计算增量;相反,将其与原始 (100,100) 起点进行比较,并将 window 移动 (+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);
}
}
我将另一个答案标记为可接受的解决方案,因为您确实需要记录初始起始光标位置(相对于 window)并使用它来计算增量。
然而这样做只提供了一半的解决方案。
拖动时我仍然在屏幕上经历了一些重大的跳跃和射击!
最后我发现这个同步问题出在我设置新 window 位置的地方。
要完全消除任何延迟,并确保 window + 光标同步 ,您 必须 glfwGetWindowPos()
和 glfwSetWindowPos()
同时 在渲染循环内!
在回调内部这样做会导致位置不同步的抖动。所以我相信部分解决方案还确保尽可能保持 window + 光标同步。
这是我得出的一个最小示例(非常感谢 K.A。Buhr!)
#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;
}
我试图在 GLFW 中拖动未修饰的 window,但遇到了一些事件延迟,即使我使用的是 glfwWaitEvents()
我有一个光标位置回调和一个简单的循环:
// register a cursor position callback
glfwSetCursorPosCallback(win, cursor_pos_callback);
// then loop..
while(!glfwWindowShouldClose(win)) {
glfwWaitEvents();
... some rendering...
glfwSwapBuffers(win);
}
我的光标回调对增量进行一些简单的跟踪并更新 window 位置。
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
应该递增,然后每隔一段时间就会倒退(陈旧的事件?)
也许我的window移动与光标不同步?
结果是window拖的时候摇的很厉害,几乎不能动...
更新:我也尝试过将 glfwSetWindowPos
逻辑移动到主循环中,但没有成功——我仍然遇到同样的抖动和口吃。
更新:当我注释掉 glfwSetWindowPos()
时,window 不再移动 (当然) 但事件流现在是一致的。
这让我想到 window 的移动如果快速完成会导致急动(即向前 2 步,向后 1 步)。
我怀疑你的问题是 cursor_pos_callback
收到相对于 window 的光标位置 并且移动 window 会立即对该位置的影响。
假设您正以恒定速率沿对角线移动光标。如果在一个刻度内,光标从相对位置 (100,100) 移动到 (105,105),则计算 delta_x=5
和 delta_y=5
。然后你移动 window。移动 window 的过程会立即将相对坐标从 (105,105) 更改回 (100,100),并在下一次报价时,即使您已经移动到相对于原始 [=26 的位置 (110,110) =] 位置,相对于新的 window 位置,您仅处于 relative 位置 (105,105),因此您计算 delta_x=0
和 delta_y=0
你以前的位置(加上一些随机的抖动噪音)即使你实际上已经沿着每个轴移动了 5 个单位。
相反,修改您的算法以保持恒定的相对光标位置。在拖动开始时,存储相对光标位置(例如(100,100))。现在,在每个刻度处,计算您需要将 window 定位的位置,以将光标移回该固定的相对位置。因此,如果光标移动到 (112,108),则将 window 移动 (+12,+8) 以将光标放回 (100,100)。在稍后的滴答中,如果光标移动到 (108,106),请不要尝试从 (112,108) 计算增量;相反,将其与原始 (100,100) 起点进行比较,并将 window 移动 (+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);
}
}
我将另一个答案标记为可接受的解决方案,因为您确实需要记录初始起始光标位置(相对于 window)并使用它来计算增量。
然而这样做只提供了一半的解决方案。 拖动时我仍然在屏幕上经历了一些重大的跳跃和射击!
最后我发现这个同步问题出在我设置新 window 位置的地方。
要完全消除任何延迟,并确保 window + 光标同步 ,您 必须 glfwGetWindowPos()
和 glfwSetWindowPos()
同时 在渲染循环内!
在回调内部这样做会导致位置不同步的抖动。所以我相信部分解决方案还确保尽可能保持 window + 光标同步。
这是我得出的一个最小示例(非常感谢 K.A。Buhr!)
#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;
}