GLFW鼠标事件滞后与窗口拖动

时间:2017-09-13 17:43:05

标签: c opengl glfw cursor-position setwindowpos

我试图在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步)。

2 个答案:

答案 0 :(得分:4)

我怀疑你的问题是cursor_pos_callback相对于窗口接收光标位置,移动窗口会对该位置产生直接影响。

假设您以恒定速率对角移动光标。如果超过一个刻度,光标从相对位置(100,100)移动到(105,105),则计算delta_x=5delta_y=5。然后移动窗口。移动窗口的过程然后瞬间将相对坐标从(105,105)改回(100,100),并在下一个刻度上,即使你已经移动到相对于原始窗口位置的位置(110,110),你是仅在相对位置(105,105)相对于新窗口位置,因此您可以从之前的位置计算delta_x=0delta_y=0(加上一些随机的抖动噪音),即使您和#39; ve实际上沿每个轴移动了5个单位。

相反,修改算法以保持恒定的相对光标位置。在拖动开始时,存储相对光标位置(例如(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);
  }
}

答案 1 :(得分:3)

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

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

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

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

在回调中执行此操作将导致位置变得不同步的抖动。所以我相信解决方案的一部分也是确保尽可能保持窗口+光标同步。

这是一个最小的例子,我来了(非常感谢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;
}