/* * The Helium Toolkit * * Copyright (C) 1996-2000 Edouard Thiel * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License; see http://www.lif-sud.univ-mrs.fr/~thiel/helium/LICENSE */ /* * glx/glarea.c - 16/09/1999 * * Widget glarea, qui ouvre un subwindow dans lequel on peut faire du OpenGL */ #include /* Type du package */ #define OBJ_TYPE Obj_glarea static He_method he_meth_glarea; /*------------------- I N T E R F A C E - P U B L I Q U E --------------------*/ /* * Création d'un glarea. * Renvoie le noeud ou NULL. * * Les différentes valeurs de gl_attr_list sont décrites dans * la documentation de glXChooseVisual(). * * Le paramètre share_context sert à partager un contexte avec un autre * GLArea déjà créé, par exemple pour afficher plusieurs vues d'un même objet. * (Pour récupérer le contexte d'un GLArea hn utiliser HeGetGLAreaContext(hn)). */ He_node *HeCreateGLArea (He_node *owner, int *gl_attr_list, GLXContext share_context) { He_node *hn; OBJ_TYPE *ob; XSetWindowAttributes wa; /* Init unique du module et test si OpenGL est supporte' */ if (HeGLAreaAutoInit() < 0) return NULL; /* On cree le noeud */ hn = HeBaseCreate (owner, &he_meth_glarea); if (hn == NULL) return NULL; /* On cree l'objet */ ob = hn->obj.any = malloc (sizeof (OBJ_TYPE)); if (ob == NULL) { HeError ("HeCreateGLArea: malloc error\n"); return NULL; } /* Init des champs du noeud */ hn->win_border = 0; /* sinon le contenu du bord est indetermine */ /* init des champs de l'objet */ ob->init_proc = NULL; ob->resize_proc = HeGLAreaDefaultResizeProc; ob->repaint_proc = NULL; ob->event_proc = NULL; HeTipInitInfo (&ob->tip_info); ob->old_xm = -1; /* pour detecter les resize ; ici on */ ob->old_ym = -1; /* force un resize à la creation. */ /* Creation d'un contexte GL (avec direct_draw == True) */ HeNewGLXContext (gl_attr_list, share_context, True, &ob->context, &ob->visual, &ob->depth, &ob->doublebuffer); if (ob->context == NULL) { HeError ("HeCreateGLArea: can't create GLXContext\n"); return NULL; } /* Il faut une colormap compatible avec le visual, * Pour le moment on cree une colormap ; par la suite * il faudrait pouvoir reutiliser des colormap, * et gerer le cas PseudoColor. * cf glut-src/glut_win.c, __glutSetupColormap(). */ ob->colormap = XCreateColormap (he_display, he_root_window, ob->visual, AllocNone); /* Creation du Window avec Xlib. * Attention, il faut employer XCreateWindow() pour donner * le visual, depth et colormap. */ wa.background_pixel = he_black; wa.border_pixel = he_black; /* sinon BadMatch */ wa.colormap = ob->colormap; /* sinon BadMatch */ hn->win = XCreateWindow (he_display, hn->owner->win, hn->xb, hn->yb, hn->xm, hn->ym, hn->win_border, ob->depth, InputOutput, ob->visual, CWBackPixel | CWBorderPixel | CWColormap, &wa); /* Propriété du window */ XSetStandardProperties (he_display, hn->win, "", "", 0, he_argv, he_argc, NULL); HeEventMask (hn->win); HeWinListInsert (hn); /* Update différé */ HeUpdateMark (hn, HE_UPD_DRAW); /* Fin succes */ return hn; } /* * Set */ void HeSetGLAreaInitProc (He_node *hn, He_glinit_proc proc) { if (HeNotGLArea(hn)) return; ((OBJ_TYPE*)hn->obj.any)->init_proc = proc; } void HeSetGLAreaResizeProc (He_node *hn, He_glresize_proc proc) { if (HeNotGLArea(hn)) return; ((OBJ_TYPE*)hn->obj.any)->resize_proc = proc; } void HeSetGLAreaRepaintProc (He_node *hn, He_glrepaint_proc proc) { if (HeNotGLArea(hn)) return; ((OBJ_TYPE*)hn->obj.any)->repaint_proc = proc; } void HeSetGLAreaEventProc (He_node *hn, He_glevent_proc proc) { if (HeNotGLArea(hn)) return; ((OBJ_TYPE*)hn->obj.any)->event_proc = proc; } /* * Get */ /* Il n'y a pas de HeGetGLAreaInitProc car le champ init_proc est mis à NULL dès que l'appel unique de l'init_proc a eu lieu. */ He_glresize_proc HeGetGLAreaResizeProc (He_node *hn) { if (HeNotGLArea(hn)) return NULL; return ((OBJ_TYPE*)hn->obj.any)->resize_proc; } He_glrepaint_proc HeGetGLAreaRepaintProc (He_node *hn) { if (HeNotGLArea(hn)) return NULL; return ((OBJ_TYPE*)hn->obj.any)->repaint_proc; } He_glevent_proc HeGetGLAreaEventProc (He_node *hn) { if (HeNotGLArea(hn)) return NULL; return ((OBJ_TYPE*)hn->obj.any)->event_proc; } int HeGetGLAreaDepth (He_node *hn) { if (HeNotGLArea(hn)) return 0; return ((OBJ_TYPE*)hn->obj.any)->depth; } GLXContext HeGetGLAreaContext (He_node *hn) { if (HeNotGLArea(hn)) return NULL; return ((OBJ_TYPE*)hn->obj.any)->context; } Visual *HeGetGLAreaVisual (He_node *hn) { if (HeNotGLArea(hn)) return NULL; return ((OBJ_TYPE*)hn->obj.any)->visual; } Colormap HeGetGLAreaColormap (He_node *hn) { if (HeNotGLArea(hn)) return 0; return ((OBJ_TYPE*)hn->obj.any)->colormap; } /* * Rend courant le contexte GL du GLArea. * N'a aucun effet si le contexte est déjà courant. * Appele' automatiquement avant les callbacks du GLArea. * * Renvoie 0 succes, -1 erreur. */ int HeGLAreaMakeCurrent (He_node *hn) { if (HeNotGLArea(hn)) return -1; return glXMakeCurrent (he_display, hn->win, ((OBJ_TYPE*)hn->obj.any)->context) == True ? 0 : -1; } /* * Swap les doubles buffers ou flush l'affichage. * Appele' automatiquement apres une repaint_proc */ void HeGLAreaSwapBuffers (He_node *hn) { if (HeNotGLArea(hn)) return; /* Swap uniquement si double buffer, sinon flush */ if (((OBJ_TYPE*)hn->obj.any)->doublebuffer) glXSwapBuffers(he_display, hn->win); else glFlush(); } /* * Resize_proc par defaut : * la zone d'affichage GL occupe tout le window. */ void HeGLAreaDefaultResizeProc (He_node *hn, int width, int height) { glViewport(0,0, width, height); } /*-------------------- M E T H O D E S - P R I V E E S ------------------------ * * On ne teste pas si hn == NULL. */ /* * Initialisation du module ; appelé par HeCreateGLArea(). * Renvoie 0 succes, -1 erreur (OpenGL non supporté). */ int HeGLAreaAutoInit () { static int did_init = FALSE; if (did_init) return 0; /* Test si OpenGL est supporte */ if (HeQueryGL() < 0) { HeError ("HeCreateGLArea: GLX extension is missing\n"); return -1; } /* Méthodes de la classe */ HeMethodDefault (&he_meth_glarea, HE_NAT_UNKNOWN); he_meth_glarea.props = HE_PM_WIN_OWNER; he_meth_glarea.destroy = HeGLAreaDestroy; he_meth_glarea.update = HeGLAreaUpdate; he_meth_glarea.event = HeGLAreaEvent; he_meth_glarea.tip = HeGLAreaStoreTip; did_init = TRUE; return 0; } /* * Destruction d'un glarea ; appele' par HeDestroyRec(). */ void HeGLAreaDestroy (He_node *hn) { OBJ_TYPE *ob = (OBJ_TYPE*) hn->obj.any; glXDestroyContext (he_display, ob->context); /* En premier */ XDestroyWindow (he_display, hn->win); XFreeColormap (he_display, ob->colormap); HeWinListRemove (hn); HeTipDestroyInfo (&ob->tip_info); free (ob); } /* * M-A-J geometrie et affichage ; appele' par HeUpdateFlush(). */ void HeGLAreaUpdate (He_node *hn, He_upd_info *ui) { HeUpdateWinGeom (hn, ui); if (HE_BIT_ISSET (ui->mask, HE_UPD_BORDER)) { XSetWindowBorderWidth (he_display, hn->win, hn->win_border); } if (HE_BIT_ISSET (ui->mask, HE_UPD_DRAW)) { if (HE_BIT_ISSET (hn->status, HE_STA_SHOW)) XMapWindow (he_display, hn->win); else XUnmapWindow (he_display, hn->win); } if (HE_BIT_ISSET (ui->mask, HE_UPD_EXPOSE)) { HeSendExpose (hn->win); } } /* * Gestion evenements X ; appele' par HeEventDispatch(). * Renvoie -1 si erreur. */ int HeGLAreaEvent (He_node *hn, He_event *hev) { OBJ_TYPE *ob = (OBJ_TYPE*) hn->obj.any; HeTipEvent (hn, hev); /* if (hev->type != MotionNotify) printf ("GLArea: %s\n", HeGetXEventName(hev->type)); */ switch (hev->type) { case EnterNotify : case LeaveNotify : { /* Emprunte ou restitue le focus clavier */ if (hev->sb == 0) HeFocusBorrow (hn, hev->type == EnterNotify); /* On rend courant le contexte GL du GLArea */ if (HeGLAreaMakeCurrent (hn) < 0) break; /* Appel de l'EventProc */ if (ob->event_proc != NULL) (ob->event_proc)(hn, hev); } break; case KeyPress : case KeyRelease : { if (HE_BIT_ISSET (hn->status, HE_STA_FOCUS_OWN)) { /* On rend courant le contexte GL du GLArea */ if (HeGLAreaMakeCurrent (hn) < 0) break; /* Appel de l'EventProc */ if (ob->event_proc != NULL) (ob->event_proc)(hn, hev); } else { HeSendEventToFocus (hn, hev); } } break; case ButtonPress : case ButtonRelease : case MotionNotify : { /* On rend courant le contexte GL du GLArea */ if (HeGLAreaMakeCurrent (hn) < 0) break; /* Appel de l'EventProc */ if (ob->event_proc != NULL) (ob->event_proc)(hn, hev); } break; /* Changement de la geometrie d'un Window ** On met a jour les dimensions. */ case ConfigureNotify : { XConfigureEvent *ev = &hev->xev->xconfigure; if (hn->xm != ev->width || hn->ym != ev->height) { hn->xm = ev->width; hn->ym = ev->height; } } break; /* Expose d'un Window (demande de refresh) ** ** On appelle la resize_proc si necessaire, puis la repaint_proc. ** Ceci garantit a l'utilisateur que la resize_proc precede toujours ** la repaint_proc lors d'un changement de dimensions. ** ** Juste avant le premier resize (ou a defaut le premier repaint) ** on appelle la init_proc. */ case Expose : { /* On rend courant le contexte GL du GLArea */ int current_ok = (HeGLAreaMakeCurrent (hn) == 0); /* On teste si les dimensions ont change */ if (ob->old_xm != hn->xm || ob->old_ym != hn->ym) { /* M-a-j pour prochain test */ ob->old_xm = hn->xm; ob->old_ym = hn->ym; /* glXWaitX (); ?? */ /* Avant le premier resize on appelle init_proc * puis on met init_proc a NULL */ if (ob->init_proc != NULL && current_ok) { (ob->init_proc)(hn); ob->init_proc = NULL; } if (ob->resize_proc != NULL && current_ok) (ob->resize_proc)(hn, hn->xm, hn->ym); } /* repaint_proc appele APRES resize_proc */ if (ob->repaint_proc != NULL && current_ok) { /* glXWaitX (); ?? */ (ob->repaint_proc)(hn); HeGLAreaSwapBuffers (hn); /* glXWaitGL (); ?? */ } } break; } /* switch */ return 0; } /* * Méthode renvoyant &tip. */ He_tip_info *HeGLAreaStoreTip (He_node *hn) { return &((OBJ_TYPE*)hn->obj.any)->tip_info; } /*-------------------- F O N C T I O N S - P R I V E E S ---------------------*/ /* * Teste nature du noeud ; renvoie TRUE si erreur. */ int HeNotGLArea (He_node *hn) { if (hn == NULL || hn->meth != &he_meth_glarea) { HeError ("GLArea expected\n"); return TRUE; } return FALSE; }