2009-04-08 7 views
8

Quiero implementar mi propio cursor en una ventana de OpenGL/GLUT. La forma habitual de hacerlo es congelar el cursor (para que no pueda golpear los bordes de la pantalla) y hacer un seguimiento de su posición usted mismo. Puedo hacer que el cursor en pantalla invisible utilizandoglutPassiveMotionFunc y glutWarpMousePointer

glutSetCursor(GLUT_CURSOR_NONE); 

y luego dentro de mi devolución de llamada glutPassiveMotionFunc mover el puntero a la mitad de la ventana usando

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); 

Esto funciona en el que se mantiene el puntero pegado a la medio de la ventana. El problema es que cuando dibujo el mouse 'OpenGL' (dentro de la devolución de llamada glutDisplayFunc()) es extremadamente espasmódico.

He buscado en línea y he descubierto que puede haber un problema donde glutWarpPointer() hace que la devolución de llamada glutPassiveMotionFunc sea llamada nuevamente, lo que da como resultado un bucle, pero parece que esto no ocurre aquí.

Estoy en Mac OS X y encontré una publicación que decía que CGDisplayMoveCursorToPoint era una mejor opción para esto. Llamar a CGDisplayMoveCursorToPoint funciona pero el movimiento sigue siendo muy desigual (y parece que tengo muchos eventos en los que xey son ambos 0). En cualquier caso, me gustaría que esto también funcione en Linux, por lo que una solución Mac única no es ideal (pero estoy bien tener que hacer cosas diferentes en los diferentes sistemas).

He reducido esto a un caso de prueba.

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

Respuesta

6

Gracias aib por los consejos. Me hiciste ver el desensamblaje de glutWarpPointer y se hizo obvio lo que estaba pasando. Llamando a glutWarpPointer CGPostMouseEvent que da como resultado un grupo de eventos sin sentido (y no hay forma de omitirlos, ya que solo obtiene Mouse Events una vez por cuadro, los nuevos eventos "reales" llegarán tarde). La solución que he encontrado es solo deformar cuando el puntero está en el borde de la pantalla (el punto, después de todo, es pretender que el punto nunca puede alcanzar el borde de la pantalla). En cualquier caso, aquí está el código.

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; 
} 
+1

Me alegro de que lo haya hecho funcionar. +1 para publicar su solución y una buena explicación. Parece que ni siquiera estaba cerca! :) –

+1

Lo arreglé volteando un poco antes de llamar a glutWarpPointer e ignorar los eventos del mouse hasta la próxima línea. –

0

no tengo demasiada experiencia con exceso, con excepción de ejemplos de color rojo-libro, pero ¿es desigual debido a lo que está dibujando para el cursor, o con qué frecuencia va a dibujar ella? Si dibuja un punto donde se supone que el cursor está utilizando llamadas OpenGL, ¿sigue siendo desigual? ¿Podría su código de tiempo ser un problema?

¿Qué código está llamando para actualizar el puntero cada tic? Supongo que no es el código que figura en la lista, ya que se estaría calculando el punto central cada vez, en lugar de en un evento de cambio de tamaño.

Mis disculpas por responder ciegamente aquí (es decir, con una experiencia limitada de superación).

+0

He editado para proporcionar el origen de un caso reducido. –

0

Estoy adivinando aquí, pero sospecho que el movimiento es desigual porque el cursor se dibuja en la función de dibujar de la aplicación (pantalla()), en lugar de manejado por el sistema operativo.

Un puntero de mouse normal se maneja en el nivel del controlador XORing la imagen del cursor con el contenido del búfer de cuadros, por lo que es extremadamente rápido y maneja con alta prioridad por el SO en una rutina de servicio de interrupción (para mantener la ilusión de sensibilidad).

Cuando se dibuja usted mismo, usted está sujeto al mecanismo de la programación regular de su sistema operativo & pasan por la regularidad clara & redibujar todo el galimatías ventana. En este caso, es rápido pero no tan rápido como estamos acostumbrados con un puntero de mouse debido a lo anterior.

En resumen, no estoy seguro de que alguna vez llegue a ser tan rápido como usted espera (particularmente cuando su función de pantalla & la lógica de la aplicación se vuelve más compleja).

¡Buena suerte!

+0

Esta es una buena idea, pero eliminar la llamada glutWarpPointer() da como resultado la suavidad esperada (pero obviamente perdemos la acción de puntero de warp). –

0

¿Estás haciendo un promedio del movimiento del mouse sobre unos pocos cuadros? No puedo encontrar mi código para mi proyecto anterior porque estoy en el trabajo. Pero creo que hice un promedio de los movimientos del mouse sobre unos pocos cuadros, antes de hice que el movimiento fuera muy desigual.

0

¿Podría ser porque está intercambiando almacenamientos intermedios en una ventana con doble memoria no duplicada?

Su ejemplo no funciona en mi sistema Win32, a menos que agregue GLUT_DOUBLE a glutInitDisplayMode().

Editar:

Estás en lo correcto. Llamar a glutWarpPointer() desde dentro de la función de movimiento parece causar un bucle en mi sistema [win32]. El temporizador ni siquiera tiene la oportunidad de disparar a menos que haga clic en un botón o algo así. Apuesto a que la cola de mensajes está siendo inundada con eventos de movimiento.

La visualización de llamada() directamente desde la función de movimiento tampoco parece funcionar, esta vez no registra ningún tipo de movimiento.

La única manera de que pudiera obtener su ejemplo de trabajo fue cambiando la devolución de llamada movimiento pasiva a una devolución de llamada de movimiento activo y llamando a la pantalla() directamente de esa función. Sé que esto está lejos de lo que originalmente pretendías, pero al menos obtuve un movimiento suave de esta manera.

¿Ha intentado utilizar un glutIdleFunc() para activar las actualizaciones de pantalla para usted? Es posible que todavía no funcione con una cola de mensajes inundada, pero puede valer la pena intentarlo. También podría considerar capturar el mouse con una llamada API en lugar de colocar manualmente el cursor en el centro de la ventana con cada movimiento.

5

Encontré un mejor enfoque. Lo que está sucediendo es que el sistema operativo suprime los eventos durante aproximadamente 0,25 segundos después de deformar el mouse. Así, en lugar a llamarlo:

#ifdef __APPLE__ 
CGSetLocalEventsSuppressionInterval(0.0); 
#endif 

Entonces todo irá colado sin ningún tartamudeo.

Es posible que deba incluir:

#include <ApplicationServices/ApplicationServices.h> 

y añadir este marco para su proyecto o de sus opciones de compilación.

Tenga en cuenta que puede obtener un evento para que el mouse se mueva al centro de la pantalla, por lo que simplemente ignoro un evento si está en el medio de la pantalla.

+0

Cheeez !! Muchas gracias por esta respuesta!¡Pensé que algo así estaba pasando, y esto hace que mi nerviosismo desaparezca! El único inconveniente es que la función está obsoleta desde 10.6 :( – v01pe

+0

hay un truco agradable (no en desuso) para esto: http://stackoverflow.com/a/17547015/1050264 – v01pe

+1

Es bueno saberlo. Gracias por señalar esto. –