перенасыщениеPassiveMotionFunc и перенасыщениеWarpMousePointer

StackOverflow https://stackoverflow.com/questions/728049

  •  05-09-2019
  •  | 
  •  

Вопрос

Я хочу реализовать свой собственный курсор в окне 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()), она работает очень дергано.

Я поискал в Интернете и обнаружил, что может возникнуть проблема, когда GluWarpPointer() вызывает повторный вызов обратного вызова 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;
}
Это было полезно?

Решение

Спасибо, айб, за советы.Вы заставили меня изучить дизассемблирование 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;
}

Другие советы

Я нашел лучший подход.Происходит то, что ОС подавляет события примерно на 0,25 секунды после деформации мыши.Итак, вместо этого просто позвоните:

#ifdef __APPLE__
CGSetLocalEventsSuppressionInterval(0.0);
#endif

Тогда все пойдет гладко, без каких-либо заиканий.

Возможно, вам придется включить:

#include <ApplicationServices/ApplicationServices.h>

и добавьте эту структуру в свой проект или в параметры компилятора.

Обратите внимание, что вы можете получить событие для перемещения мыши в центр экрана, поэтому я просто игнорирую событие, если оно находится в середине экрана.

У меня не так уж много опыта работы с перенасыщением, за исключением примеров из красной книги, но это дергается из-за того, что вы рисуете курсором, или как часто вы это рисуете?Если вы просто нарисуете точку, где курсор должен использовать вызовы OpenGL, это все равно будет дергаться?Может ли ваш временной код быть проблемой?

Какой код вы вызываете, чтобы обновлять указатель каждый тик?Я предполагаю, что это не тот код, который указан, поскольку вы будете вычислять центральную точку каждый раз, а не при событии изменения размера.

Приношу извинения за слепой ответ здесь (т.с ограниченным опытом перенасыщения).

Я предполагаю, но подозреваю, что движение прерывистое, потому что курсор рисуется функцией рисования вашего приложения (display()), а не обрабатывается ОС.

Обычный указатель мыши обрабатывается на уровне драйвера путем выполнения XOR изображения курсора с содержимым кадрового буфера — таким образом, молниеносно, и обрабатывается с очень высоким приоритетом операционной системой в процедуре обслуживания прерываний (чтобы сохранить иллюзию реагирования).

Когда вы рисуете его самостоятельно, вы подчиняетесь обычному механизму планирования вашей ОС и выполняете регулярную очистку и перерисовку всего окна.В данном случае это происходит быстро, но не так быстро, как мы привыкли при использовании указателя мыши из-за вышеизложенного.

Короче говоря, я не уверен, что вы когда-нибудь добьетесь того, чтобы он работал так быстро, как вы ожидаете (особенно по мере того, как ваша функция отображения и логика приложения становятся более сложными).

Удачи!

Вы усредняете движение мыши по нескольким кадрам?Я не могу найти код для своего предыдущего проекта, потому что я на работе.Но я думаю, что я усреднял движения мыши по нескольким кадрам, прежде чем я сделал это, движение было очень резким.

Может ли это быть потому, что вы меняете буферы в окне без двойной буферизации?

Ваш пример не работает в моей системе Win32, если я не добавлю GLUT_DOUBLE в GlutInitDisplayMode().

Редактировать:

Ты прав.Вызов GluWarpPointer() из функции движения, похоже, вызывает цикл в моей системе [win32].Таймер даже не сможет сработать, если я не нажму кнопку или что-то в этом роде.Могу поспорить, что очередь сообщений переполнена событиями движения.

Вызов display() прямо из функции движения также не работает — на этот раз он не может зарегистрировать какое-либо движение.

Единственный способ заставить ваш пример работать - это изменить пассивный обратный вызов движения к активный обратный вызов движения и вызов display() непосредственно из этой функции.Я знаю, что это далеко не то, что вы изначально задумали, но, по крайней мере, таким образом я получил плавное движение.

Пробовали ли вы использовать GluIdleFunc() для запуска обновлений дисплея?Возможно, он по-прежнему не работает с переполненной очередью сообщений, но, возможно, стоит попробовать.Вы также можете рассмотреть возможность захвата мыши с помощью вызова API вместо того, чтобы вручную перемещать курсор в центр окна при каждом движении.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top