2017-09-13 408 views
2

我试图拖动GLFW未修饰的窗口,但我遇到了一些事件的滞后,即使我使用glfwWaitEvents()GLFW鼠标事件的滞后与窗口拖动

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

// 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步)。

回答

3

我怀疑你的问题是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); 
    } 
} 
+0

谢谢你的详细解答!这绝对是解决这个问题的核心。然而,它只是部分解决了问题,抖动仍然存在,但至少它更好地遵循了光标。 –

3

标志着我其他的答案,作为接受的解决方案,因为它是真实的,你需要记录最初开始的光标位置(相对于窗口),并用它来计算三角洲。

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

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

为了摆脱完全任何滞后,保证窗口+光标正在同步增长,你必须glfwGetWindowPos()glfwSetWindowPos()同时渲染循环里面!

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

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