macos glutPassiveMotionFunc 和 glutWarpMousePointer

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/728049/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-21 06:06:12  来源:igfitidea点击:

glutPassiveMotionFunc and glutWarpMousePointer

macosopenglmouseglut

提问by Steven Canfield

I want to implement my own cursor in an OpenGL / GLUT window. The usual way to do this is to freeze the cursor (so it can't hit the edges of the screen) and keep track of its position yourself. I can make the onscreen cursor invisible using

我想在 OpenGL / GLUT 窗口中实现我自己的光标。通常的做法是冻结光标(这样它就不会碰到屏幕的边缘)并自己跟踪它的位置。我可以使用使屏幕光标不可见

glutSetCursor(GLUT_CURSOR_NONE);

and then inside of my glutPassiveMotionFunc callback move the pointer to the middle of the window using

然后在我的 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 );

This works in that it keeps the pointer stuck to the middle of the window. The problem is that when I am drawing the 'OpenGL' mouse (inside of the glutDisplayFunc() callback) it is extremely jerky.

这是因为它将指针保持在窗口的中间。问题是,当我绘制“OpenGL”鼠标(在 glutDisplayFunc() 回调内部)时,它非常生涩。

I have looked online and found that there can be an issue where glutWarpPointer() causes the glutPassiveMotionFunc callback to be called again, resulting in a loop, but this doesn't seem to happen here.

我在网上查了一下,发现可能存在一个问题,即 glutWarpPointer() 导致 glutPassiveMotionFunc 回调被再次调用,从而导致循环,但这里似乎不会发生这种情况。

I'm on Mac OS X and I found a post saying that CGDisplayMoveCursorToPoint was a better fit for this. Calling CGDisplayMoveCursorToPoint works but the movement is still very jerky (and I seem to get a lot of events where x and y are both 0). In any case, I'd like this to work on Linux as well so a Mac only solution is not ideal (but I'm okay having to do different things on the different systems).

我在 Mac OS X 上,我发现一个帖子说 CGDisplayMoveCursorToPoint 更适合这个。调用 CGDisplayMoveCursorToPoint 有效,但运动仍然非常生涩(我似乎得到了很多 x 和 y 都为 0 的事件)。无论如何,我希望它也能在 Linux 上运行,因此仅 Mac 的解决方案并不理想(但我可以在不同的系统上做不同的事情)。

I've reduced this to a testcase.

我已将其简化为测试用例。

#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;
}

采纳答案by Steven Canfield

Thanks aib for the tips. You got me looking into the disassembly of glutWarpPointer and it became obvious what was going on. Calling glutWarpPointer CGPostMouseEvent which results in a bunch of nonsense events (and there isn't any way to skip them since you only get Mouse Events once per frame, the new "real" events will be late). The solution I've found is to only warp when the pointer is at the edge of the screen ( the point, after all, is to pretend like the point can never reach the edge of the screen ). In any case, here is the code.

感谢 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;
}

回答by Nathan S.

I found a better approach. What is going on is that the OS suppresses events for about 0.25 seconds after you warp the mouse. So, instead just call:

我找到了一个更好的方法。正在发生的事情是,在您扭曲鼠标后,操作系统会抑制事件约 0.25 秒。因此,只需调用:

#ifdef __APPLE__
CGSetLocalEventsSuppressionInterval(0.0);
#endif

Then everything will go smoothy without any stuttering.

然后一切都会顺利进行,没有任何口吃。

You may need to include:

您可能需要包括:

#include <ApplicationServices/ApplicationServices.h>

and add this framework to your project or to your compiler options.

并将此框架添加到您的项目或编译器选项中。

Note that you may get an event for the mouse moving to the center of the screen, so I just ignore an event if it is to the middle of the screen.

请注意,您可能会收到鼠标移动到屏幕中心的事件,因此如果它移动到屏幕中间,我将忽略该事件。

回答by Bernard

I don't have too much experience with glut, save for red-book examples, but is it jerky because of what you're drawing for the cursor, or how often you're drawing it? If you just draw a point where the cursor is supposed to be using OpenGL calls, is it still jerky? Could your timing code be an issue?

除了红皮书示例外,我对 glut 没有太多经验,但是它是否因为您为光标绘制的内容而生涩,或者您绘制它的频率如何?如果你只是在光标应该使用 OpenGL 调用的地方画一个点,它还是生涩的吗?你的时间码会不会有问题?

What code are you calling to update the pointer every tick? I assume it isn't the code listed as you would be calculating the centre point every time, instead of on a resize event.

你调用什么代码来更新指针每个滴答声?我假设它不是列出的代码,因为您每次都会计算中心点,而不是在调整大小事件上。

My apologies for blindly answering here (i.e. with limited glut experience).

我很抱歉在这里盲目回答(即有限的过剩经验)。

回答by Drew Hall

I'm guessing here, but I suspect that the motion is jerky because the cursor is drawn in your application's draw function (display()), rather than handled by the OS.

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

A normal mouse pointer is handled at the driver level by XORing the cursor image with the frame buffer contents--thus lightning quick, and handled with very high priority by the OS in an interrupt service routine (to maintain the illusion of responsiveness).

普通鼠标指针在驱动程序级别通过将光标图像与帧缓冲区内容进行异或来处理 - 因此闪电般快速,并由操作系统在中断服务例程中以非常高的优先级进行处理(以保持响应的错觉)。

When you draw it yourself, you're subject to the regular scheduling mechanism of your OS & go through the regular clear & redraw the whole window rigamarole. In this case, it's fast but not as fast as we're used to with a mouse pointer due to the above.

当你自己绘制它时,你会受到操作系统的常规调度机制的影响,并通过常规清除和重绘整个窗口rigamarole。在这种情况下,由于上述原因,它很快,但不如我们习惯使用鼠标指针快。

In short, I'm not sure you'll ever get it to be as fast as you expect it to be (particularly as your display function & app logic gets more complex).

简而言之,我不确定您是否会像预期的那样快(尤其是当您的显示功能和应用程序逻辑变得更加复杂时)。

Good luck!

祝你好运!

回答by Craig

Are you averaging the movement of the mouse over a few frames? I can't find my code for my previous project because i am at work. But i think i averaged the mouse movements over a few frames, before i did that the movement was very jerky.

您是否在几帧内平均鼠标的移动?我找不到我以前项目的代码,因为我在工作。但我认为我在几帧内平均鼠标移动,在我这样做之前,移动非常生涩。

回答by aib

Could it be because you're swapping buffers on a non-double buffered window?

可能是因为您在非双缓冲窗口上交换缓冲区?

Your example doesn't work on my Win32 system, unless I add GLUT_DOUBLE to glutInitDisplayMode().

您的示例在我的 Win32 系统上不起作用,除非我将 GLUT_DOUBLE 添加到 glutInitDisplayMode()。

Edit:

编辑:

You are correct. Calling glutWarpPointer() from within the motion function seems to cause a loop on my [win32] system. The timer doesn't even get a chance to fire unless I click a button or something. I'm betting the message queue is being flooded with motion events.

你是对的。从运动函数中调用 glutWarpPointer() 似乎会导致我的 [win32] 系统出现循环。除非我单击按钮或其他东西,否则计时器甚至没有机会触发。我敢打赌消息队列中充斥着运动事件。

Calling display() right from the motion function doesn't seem to work, either - this time it fails to register any kind of motion.

直接从运动功能调用 display() 似乎也不起作用 - 这次它无法注册任何类型的运动。

The only way I could get your example to work was by changing the passivemotion callback to an activemotion callback and calling display() directly from that function. I know this is far from what you've originally intended, but at least I got some smooth motion this way.

我可以让您的示例工作的唯一方法是将被动运动回调更改为主动运动回调并直接从该函数调用 display() 。我知道这与您最初的意图相去甚远,但至少我通过这种方式获得了一些平滑的运动。

Have you tried using a glutIdleFunc() to trigger your display updates for you? It may still not work with a flooded message queue but it may be worth a try. You could also look into capturing the mouse using an API call instead of manually wrapping the cursor to the center of the window at every motion.

您是否尝试过使用 glutIdleFunc() 来触发您的显示更新?它可能仍然不适用于泛洪的消息队列,但可能值得一试。您还可以考虑使用 API 调用来捕获鼠标,而不是在每次移动时手动将光标包裹到窗口的中心。