我想实现在OpenGL / GLUT窗口我自己的光标。通常的方式做,这是冻结光标(以便它不能打在屏幕的边缘),并跟踪其自己的位置。我可以使屏幕上的光标不可见使用

glutSetCursor(GLUT_CURSOR_NONE);

和然后我glutPassiveMotionFunc回调的内部将指针移动到该窗口的使用中间

int centerX = (float)kWindowWidth / 2.0;
int centerY = (float)kWindowHeight / 2.0;

int deltaX = (x - centerX);
int deltaY = (y - centerY);

mouseX += deltaX / (float)kWindowWidth;
mouseY -= deltaY / (float)kWindowHeight;

glutWarpPointer( centerX, centerY );

这个作品在其保持指针粘在窗口的中间。的问题是,当我绘制“的OpenGL”小鼠(内部的glutDisplayFunc()回调的)是非常干。

我在网上看了一下,发现可以有一个问题,即glutWarpPointer()导致glutPassiveMotionFunc回调再次调用,导致一个循环,但是这似乎并没有发生在这里。

我在Mac OS X中,我发现一个帖子说,CGDisplayMoveCursorToPoint是这个更适合。调用CGDisplayMoveCursorToPoint工作,但运动还是很干(我似乎得到了很多的事件,其中x和y都为0)。在任何情况下,我想这是在Linux上工作,以及因此仅适用于Mac的解决方案是不理想的(但我没事不必在不同的系统做不同的事情)。

我已经减少到这样的测试用例。

#include <stdio.h>
#include <OpenGL/OpenGL.h>
#include <GLUT/GLUT.h>

int curX = 0;
int curY = 0;

void display() {
    glClearColor( 0.0, 0.0, 0.0, 1.0 );
    glClear( GL_COLOR_BUFFER_BIT );

    float vx = (float)curX / 300.0 + 0.5;
    float vy = (float)curY / 300.0 + 0.5;

    glColor3f( 1.0, 0.0, 0.0 );
    glBegin( GL_POINTS );
        glVertex3f( vx, vy, 0.0 );
    glEnd();

    glutSwapBuffers();

}

void passivemotion( int x, int y ) {
    int centerX = 150;
    int centerY = 150;

    int deltaX = x - centerX;
    int deltaY = y - centerY;
    curX += deltaX;
    curY -= deltaY;

    glutWarpPointer( centerX, centerY );
}

void timer( int val ) {
    glutTimerFunc( 16, &timer,  0);
    glutPostRedisplay();
}

int main (int argc, char * argv[]) {
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_RGB);
    glutInitWindowSize(300,300);
    glutCreateWindow("FPS Mouse Sample");
    glutDisplayFunc(&display);
    glutPassiveMotionFunc(&passivemotion);
    glutSetCursor( GLUT_CURSOR_NONE );
    glutTimerFunc( 16, &timer, 0 );
    glutMainLoop();
    return 0;
}
有帮助吗?

解决方案

感谢AIB的提示。你让我寻找到glutWarpPointer的拆卸,并成为明显发生了什么事。调用glutWarpPointer CGPostMouseEvent导致了一堆废话事件(并没有什么办法可以跳过它们,因为你只有鼠标事件每帧一次,新的“真实”的事件会迟到)。我已经找到了解决方案是唯一经当指针在屏幕(点,毕竟是假装像点可以永远不会到达屏幕边缘)的边缘。在任何情况下,这里是代码。

int lastX = 150;
int lastY = 150;
void passivemotion( int x, int y ) {    
    int deltaX = x - lastX;
    int deltaY = y - lastY;

    lastX = x;
    lastY = y;

    if( deltaX == 0 && deltaY == 0 ) return;

    int windowX     = glutGet( GLUT_WINDOW_X );
    int windowY     = glutGet( GLUT_WINDOW_Y );
    int screenWidth     = glutGet( GLUT_SCREEN_WIDTH );
    int screenHeight    = glutGet( GLUT_SCREEN_HEIGHT );

    int screenLeft = -windowX;
    int screenTop = -windowY;
    int screenRight = screenWidth - windowX;
    int screenBottom = screenHeight - windowY;

    if( x <= screenLeft+10 || (y) <= screenTop+10 || x >= screenRight-10 || y >= screenBottom - 10) {
        lastX = 150;
        lastY = 150;
        glutWarpPointer( lastX, lastY );
        //  If on Mac OS X, the following will also work (and CGwarpMouseCursorPosition seems faster than glutWarpPointer).
        //  CGPoint centerPos = CGPointMake( windowX + lastX, windowY + lastY );
        //  CGWarpMouseCursorPosition( centerPos );
        // Have to re-hide if the user touched any UI element with the invisible pointer, like the Dock.
        //  CGDisplayHideCursor(kCGDirectMainDisplay);
    }

    curX += deltaX;
    curY -= deltaY;
}

其他提示

我发现一个较好的方法。这是怎么回事的是,OS抑制事件约0.25秒钟,然后弯曲后鼠标。所以,相反只要致电:

#ifdef __APPLE__
CGSetLocalEventsSuppressionInterval(0.0);
#endif

然后一切都会smoothy没有任何口吃。

您可能需要包括:

#include <ApplicationServices/ApplicationServices.h>

和这个框架添加到您的项目或你的编译器选项。

请注意,你可能会得到鼠标移动到屏幕的中心事件,所以我只是忽视了一个事件,如果它是在屏幕的中央。

我没有与过剩太多的经验,除红书的例子,但它是干,因为你正在绘制的光标什么,或者你多久绘制呢?如果你只是画在光标应该使用OpenGL调用一个点,它仍然生涩?难道你的时间码是一个问题?

什么代码,你叫更新指针每打勾?我认为它是没有被列为你会每次计算的中心点,而不是在一个调整大小事件的代码。

我的道歉一味这里回答(即具有有限过剩体验)。

我猜在这里,但我怀疑是运动生涩,因为光标在应用程序绘制函数绘制(显示()),而不是由操作系统来处理。

一个正常的鼠标指针通过与帧缓冲器的内容进行XOR光标图像在驱动级进行处理 - 从而闪电快,并且具有非常高的优先级由OS在中断服务例程来处理(保持响应的错觉)。

当你自己绘制它,你以你的操作系统和经过普通透明和重绘整个窗口rigamarole的定期调度机制。在这种情况下,它的快速但不一样快,我们已经习惯了用鼠标指针由于上述

在短,我不知道你永远得到它是一样快,您希望它是(特别是你的显示功能和应用程序的逻辑变得更加复杂)。

祝你好运!

您平均超过几帧鼠标的运动? 我无法找到我的代码为我以前的项目,因为我在工作。 但我想我平均超过几帧的鼠标移动,前 我这样做的运动是非常生涩。

难道是因为你是一个非双缓冲窗口交换缓存?

您如不能在我的Win32系统上工作,除非我添加GLUT_DOUBLE到glutInitDisplayMode()。

编辑:

您是正确的。调用glutWarpPointer()从运动功能内似乎导致我的[win32的]系统上的环。计时器不会甚至有机会开火,除非我点击一个按钮什么的。我打赌消息队列被充斥着运动的事件。

调用向右从运动功能显示()似乎不工作,无论是 - 这一次它无法注册任何种类的运动的

我能得到你的例子来工作的唯一途径是通过从功能改变的被动运动回调至一个直接的活性运动回调和调用显示()。我知道这远不是你原本打算,但至少我得到了一些平滑的运动这种方式。

您是否尝试过使用glutIdleFunc()来触发您的显示更新?它可能仍然无法与被水淹没的消息队列的工作,但它可能是值得一试。你也可以考虑使用的API调用手动捕获鼠标代替缠绕光标移动到窗口的中心在每个运动。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top