2009-04-08 12 views
8

Je veux implémenter mon propre curseur dans une fenêtre OpenGL/GLUT. La manière habituelle de faire ceci est de geler le curseur (de sorte qu'il ne peut pas frapper les bords de l'écran) et de garder une trace de sa position vous-même. Je peux faire le curseur à l'écran invisible à l'aideglutPassiveMotionFunc et glutWarpMousePointer

glutSetCursor(GLUT_CURSOR_NONE); 

puis à l'intérieur de mon rappel glutPassiveMotionFunc déplacer le pointeur vers le milieu de la fenêtre à l'aide

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

Cela fonctionne en ce qu'il garde le pointeur collé à la milieu de la fenêtre. Le problème est que lorsque je dessine la souris 'OpenGL' (à l'intérieur du callback glutDisplayFunc()), elle est extrêmement saccadée. J'ai regardé en ligne et j'ai constaté qu'il peut y avoir un problème où glutWarpPointer() provoque le rappel de glutPassiveMotionFunc à nouveau, ce qui entraîne une boucle, mais cela ne semble pas se produire ici. Je suis sur Mac OS X et j'ai trouvé un post disant que CGDisplayMoveCursorToPoint convenait mieux à cela. L'appel de CGDisplayMoveCursorToPoint fonctionne mais le mouvement est toujours très saccadé (et il semble que j'obtiens beaucoup d'événements où x et y sont tous deux à 0). Dans tous les cas, je voudrais que cela fonctionne également sur Linux, donc une solution uniquement pour Mac n'est pas idéale (mais je suis d'accord pour faire différentes choses sur les différents systèmes). J'ai réduit cela à un test de cas.

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

Répondre

6

Merci BAI pour les conseils. Vous m'avez regardé dans le démontage de glutWarpPointer et il est devenu évident ce qui se passait. L'appel de glutWarpPointer CGPostMouseEvent qui se traduit par un tas d'événements non sens (et il n'y a aucun moyen de les ignorer puisque vous n'obtenez que des événements de souris une fois par trame, les nouveaux événements "réels" seront en retard). La solution que j'ai trouvée est de ne se déformer que lorsque le pointeur est sur le bord de l'écran (le point, après tout, est de prétendre que le point ne peut jamais atteindre le bord de l'écran). En tout cas, voici le code.

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

Je suis content que vous ayez réussi. +1 pour poster votre solution et une bonne explication. On dirait que je n'étais même pas proche! :) –

+1

J'ai corrigé cela en retournant un peu avant d'appeler glutWarpPointer et ignorer les événements de souris jusqu'à la ligne suivante. –

0

Je n'ai pas trop d'expérience avec surabondance, sauf exemples rouge-livre, mais est-ce saccadée à cause de ce que vous dessinez pour le curseur, ou comment vous êtes souvent le dessiner? Si vous dessinez juste un point où le curseur est supposé utiliser les appels OpenGL, est-ce que c'est encore saccadé? Votre code temporel pourrait-il être un problème?

Quel code appelez-vous pour mettre à jour le pointeur à chaque tick? Je suppose que ce n'est pas le code indiqué, car vous calculeriez le point central à chaque fois, plutôt que sur un événement de redimensionnement. Mes excuses pour avoir répondu aveuglément ici (c'est-à-dire avec une expérience de surabondance limitée).

+0

J'ai modifié pour fournir la source d'un cas réduit. –

0

Je devine ici, mais je soupçonne que le mouvement est saccadé parce que le curseur est dessiné dans la fonction draw de votre application (display()), plutôt que géré par le système d'exploitation. Un pointeur de souris normale est géré au niveau du pilote par XOR l'image du curseur avec le contenu du tampon d'image - ainsi rapide et manipulé avec une très haute priorité par l'OS dans une routine de service d'interruption (pour maintenir l'illusion de réactivité).

Lorsque vous dessinez vous-même, vous êtes soumis au mécanisme de planification régulière de votre système d'exploitation. & passez par la normale & effacer toute la fenêtre rigamarole. Dans ce cas, c'est rapide mais pas aussi rapide que ce à quoi nous sommes habitués avec un pointeur de souris. En bref, je ne suis pas sûr que vous l'obtiendrez jamais aussi vite que vous l'attendez (d'autant plus que la logique d'application de votre fonction d'affichage & devient plus complexe).

Bonne chance!

+0

C'est une bonne idée, mais la suppression de l'appel glutWarpPointer() entraîne la finesse attendue (mais évidemment, nous perdons l'action du pointeur Warp). –

0

Faites-vous la moyenne du mouvement de la souris sur quelques images? Je ne trouve pas mon code pour mon projet précédent parce que je suis au travail. Mais je pense que j'ai fait la moyenne des mouvements de la souris sur quelques images, avant je l'ai fait que le mouvement était très saccadé.

0

Serait-ce parce que vous êtes la permutation des tampons sur une fenêtre tampon non double?

Votre exemple ne fonctionne pas sur mon système Win32, à moins que j'ajouter à GLUT_DOUBLE glutInitDisplayMode().

Editer:

Vous avez raison. Appeler glutWarpPointer() depuis la fonction de mouvement semble provoquer une boucle sur mon système [win32]. La minuterie n'a même pas une chance de tirer à moins que je clique sur un bouton ou quelque chose. Je parie que la file d'attente de messages est inondée d'événements de mouvement.

affichage d'appel() à droite de la fonction de mouvement ne semble pas fonctionner, que ce soit - cette fois-ci n'enregistrer tout type de mouvement.

La seule façon que je pouvais obtenir votre exemple au travail était en changeant le rappel de mouvement passif à un rappel de mouvement active et l'affichage d'appel() directement à partir de cette fonction. Je sais que c'est loin de ce que vous aviez prévu à l'origine, mais au moins j'ai eu un mouvement fluide de cette façon.

Avez-vous essayé d'utiliser un glutIdleFunc() pour déclencher vos mises à jour d'affichage pour vous? Il peut toujours ne pas fonctionner avec une file d'attente de messages inondés, mais cela peut valoir la peine d'essayer. Vous pouvez également vous intéresser à la capture de la souris à l'aide d'un appel API au lieu de placer manuellement le curseur au centre de la fenêtre à chaque mouvement.

5

J'ai trouvé une meilleure approche. Qu'est-ce qui se passe est que le système d'exploitation supprime les événements pendant environ 0,25 secondes après avoir déformé la souris. Donc, au lieu de cela, il suffit d'appeler:

#ifdef __APPLE__ 
CGSetLocalEventsSuppressionInterval(0.0); 
#endif 

Ensuite, tout ira smoothy sans bégaiement.

Vous devrez peut-être inclure:

#include <ApplicationServices/ApplicationServices.h> 

et ajouter ce cadre à votre projet ou à vos options de compilateur.

Notez que vous pouvez obtenir un événement pour la souris en mouvement au centre de l'écran, donc j'ignorer un événement si elle est au milieu de l'écran.

+0

Cheeez !! Merci beaucoup pour cette réponse !!!Je pensais que quelque chose comme ça se passait, et cela fait disparaître mon agitation! Seul inconvénient est que la fonction est obsolète depuis 10.6 :( – v01pe

+0

il y a un bon hack (non déprécié) pour cela: http://stackoverflow.com/a/17547015/1050264 – v01pe

+1

Bon à savoir Merci de le signaler. –