/* EXTRAITS DE LA LICENCE Copyright CEA, contributeurs : Luc BILLARD et Damien CALISTE, laboratoire L_Sim, (2001-2006) Adresse mèl : BILLARD, non joignable par mèl ; CALISTE, damien P caliste AT cea P fr. Ce logiciel est un programme informatique servant à visualiser des structures atomiques dans un rendu pseudo-3D. Ce logiciel est régi par la licence CeCILL soumise au droit français et respectant les principes de diffusion des logiciels libres. Vous pouvez utiliser, modifier et/ou redistribuer ce programme sous les conditions de la licence CeCILL telle que diffusée par le CEA, le CNRS et l'INRIA sur le site "http://www.cecill.info". Le fait que vous puissiez accéder à cet en-tête signifie que vous avez pris connaissance de la licence CeCILL, et que vous en avez accepté les termes (cf. le fichier Documentation/licence.fr.txt fourni avec ce logiciel). */ /* LICENCE SUM UP Copyright CEA, contributors : Luc BILLARD et Damien CALISTE, laboratoire L_Sim, (2001-2006) E-mail address: BILLARD, not reachable any more ; CALISTE, damien P caliste AT cea P fr. This software is a computer program whose purpose is to visualize atomic configurations in 3D. This software is governed by the CeCILL license under French law and abiding by the rules of distribution of free software. You can use, modify and/ or redistribute the software under the terms of the CeCILL license as circulated by CEA, CNRS and INRIA at the following URL "http://www.cecill.info". The fact that you are presently reading this means that you have had knowledge of the CeCILL license and that you accept its terms. You can find a copy of this licence shipped with this software at Documentation/licence.en.txt. */ #ifdef HAVE_CONFIG_H #include #endif #include "gtk/gtkwidget.h" #include #include "gtk_openGLWidget.h" #include "OSOpenGL/visu_openGL.h" #include "visu_tools.h" /* OS dependent includes */ #if SYSTEM_X11 == 1 #include #include #endif #if SYSTEM_WIN32 == 1 #include #include #endif struct OpenGLWidget_struct { GtkWidget parent; gboolean sizeAllocation_has_run; gboolean dispose_has_run; /* Redraw method and user data. */ RedrawMethod redraw; gpointer redrawData; /* OpenGL part, OS dependent. */ #if SYSTEM_X11 == 1 Display *dpy; gboolean isContextDirect; GLXContext context; #endif #if SYSTEM_WIN32 == 1 HDC hdc; gboolean isContextDirect; HGLRC context; HWND windowId; #endif }; struct OpenGLWidgetClass_struct { GtkWidgetClass parent_class; OpenGLWidget *contextCurrent; }; /* Local variables. */ static gpointer parent_class; static OpenGLWidgetClass *myClass = (OpenGLWidgetClass*)0; /* Local callbacks. */ static gboolean openGLWidgetEvent_expose(GtkWidget *widget, GdkEventExpose *event); static void openGLWidgetEvent_sizeRequest(GtkWidget *widget, GtkRequisition *requisition); static void openGLWidgetEvent_sizeAllocate(GtkWidget *widget, GtkAllocation *allocation); static void openGLWidgetEvent_realise(GtkWidget *widget); static void openGLWidgetEvent_styleSet(GtkWidget *widget, GtkStyle *previous_style); /* Initialisation methods. */ static void openGLWidgetInit_class(OpenGLWidgetClass *class); static void openGLWidgetInit_object(OpenGLWidget *render); static void openGLWidgetInit_context(OpenGLWidget *render, gboolean contextIsDirect); /* Freeing methods. */ static void openGLWidgetEvent_dispose(GObject *obj); static void openGLWidgetEvent_finalize(GObject *obj); static void openGLWidgetFree_openGL(OpenGLWidget *render); /* Miscellaneous methods. */ static void openGLWidgetSet_viewport(OpenGLWidget *render, guint width, guint height, gboolean redraw); static GdkColormap* openGLWidgetGet_openGLColormap(OpenGLWidget *render); /* Methods to deals with the handling of the object. */ GType openGLWidgetGet_type(void) { static GType openGLWidget_type = 0; if (!openGLWidget_type) { static const GTypeInfo openGLWidget_info = { sizeof (OpenGLWidgetClass), NULL, /* base_init */ NULL, /* base_finalize */ (GClassInitFunc) openGLWidgetInit_class, NULL, /* class_finalize */ NULL, /* class_data */ sizeof (OpenGLWidget), 0, (GInstanceInitFunc) openGLWidgetInit_object, NULL }; openGLWidget_type = g_type_register_static(GTK_TYPE_WIDGET, "OpenGLWidget", &openGLWidget_info, 0); } return openGLWidget_type; } static void openGLWidgetInit_class(OpenGLWidgetClass *class) { GtkWidgetClass *widget_class; GObjectClass *gobject_class; /* Put a static pointer on the parent class for chaining actions. */ parent_class = g_type_class_peek_parent(class); /* Dealing with GObject events. */ gobject_class = G_OBJECT_CLASS(class); gobject_class->dispose = openGLWidgetEvent_dispose; gobject_class->finalize = openGLWidgetEvent_finalize; /* Dealing with widget events. */ widget_class = GTK_WIDGET_CLASS(class); widget_class->expose_event = openGLWidgetEvent_expose; widget_class->realize = openGLWidgetEvent_realise; widget_class->size_request = openGLWidgetEvent_sizeRequest; widget_class->size_allocate = openGLWidgetEvent_sizeAllocate; class->contextCurrent = (OpenGLWidget*)0; myClass = class; } static void openGLWidgetInit_object(OpenGLWidget *render) { DBG_fprintf(stderr, "Gtk OpenGL (init) : create object %p.\n", (gpointer)render); render->sizeAllocation_has_run = FALSE; render->dispose_has_run = FALSE; render->redraw = (RedrawMethod)0; render->redrawData = (gpointer)0; render->isContextDirect = FALSE; #if SYSTEM_X11 == 1 render->dpy = (Display*)0; render->context = (GLXContext)0; #endif #if SYSTEM_WIN32 == 1 render->hdc = (HDC)0; render->context = (HGLRC)0; render->windowId = (HWND)0; /* Cancel the GTK double buffering since it is taken into account by OpenGL itself. */ gtk_widget_set_double_buffered(GTK_WIDGET(render), FALSE); #endif } GtkWidget* openGLWidgetNew(gboolean contextIsDirect) { OpenGLWidget *render; render = OPENGL_WIDGET(g_object_new(TYPE_OPENGL_WIDGET, NULL)); render->isContextDirect = contextIsDirect; return GTK_WIDGET(render); } /* Methods to deals with the events. */ static void openGLWidgetEvent_dispose(GObject *obj) { DBG_fprintf(stderr, "Gtk OpenGL (events) : dispose event for object %p.\n", (gpointer)obj); if (OPENGL_WIDGET(obj)->dispose_has_run) return; OPENGL_WIDGET(obj)->dispose_has_run = TRUE; /* Chain up to the parent class */ G_OBJECT_CLASS(parent_class)->dispose(obj); } static void openGLWidgetEvent_finalize(GObject *obj) { DBG_fprintf(stderr, "Gtk OpenGL (events) : finalize event for object %p.\n", (gpointer)obj); openGLWidgetFree_openGL(OPENGL_WIDGET(obj)); /* Chain up to the parent class */ G_OBJECT_CLASS(parent_class)->finalize(obj); } static gboolean openGLWidgetEvent_expose(GtkWidget *widget, GdkEventExpose *event) { OpenGLWidget *render; DBG_fprintf(stderr, "Gtk OpenGL (events) : expose event for object %p.\n", (gpointer)widget); render = OPENGL_WIDGET(widget); if (render->redraw) openGLWidgetRedraw(render); else { DBG_fprintf(stderr, " | clear window.\n"); gdk_window_clear_area(widget->window, event->area.x, event->area.y, event->area.width, event->area.height); } return FALSE; } static void openGLWidgetEvent_sizeRequest(GtkWidget *widget, GtkRequisition *requisition) { OpenGLWidget *render; int width, height; DBG_fprintf(stderr, "Gtk OpenGL (events) : size request event (%dx%d).\n", requisition->width, requisition->height); render = OPENGL_WIDGET(widget); if (render->sizeAllocation_has_run) { width = widget->allocation.width; height = widget->allocation.height; } else { width = 200; height = 200; } widget->requisition.width = width; widget->requisition.height = height; /* Chain up to default that simply reads current requisition */ GTK_WIDGET_CLASS(parent_class)->size_request(widget, requisition); } static void openGLWidgetEvent_sizeAllocate(GtkWidget *widget, GtkAllocation *allocation) { OpenGLWidget *render; render = OPENGL_WIDGET(widget); if ((widget->allocation.width == allocation->width) && (widget->allocation.height == allocation->height)) return; DBG_fprintf(stderr, "Gtk OpenGL (events) : size allocation event (%dx%d).\n", allocation->width, allocation->height); render->sizeAllocation_has_run = TRUE; /* Chain up to default that simply reads current requisition */ GTK_WIDGET_CLASS(parent_class)->size_allocate(widget, allocation); #if SYSTEM_X11 == 1 glXWaitX(); #endif openGLWidgetSet_viewport(render, widget->allocation.width, widget->allocation.height, TRUE); } static void openGLWidgetEvent_realise(GtkWidget *widget) { OpenGLWidget *render; GdkWindowAttr attributes; gint attributes_mask; GdkColormap *colormap; DBG_fprintf(stderr, "Gtk OpenGL (events) : realise event for %p.\n", (gpointer)widget); render = OPENGL_WIDGET(widget); colormap = openGLWidgetGet_openGLColormap(render); attributes.window_type = GDK_WINDOW_CHILD; attributes.x = widget->allocation.x; attributes.y = widget->allocation.y; attributes.width = widget->allocation.width; attributes.height = widget->allocation.height; attributes.wclass = GDK_INPUT_OUTPUT; attributes.visual = gdk_colormap_get_visual(colormap); attributes.colormap = colormap; attributes.event_mask = GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_SCROLL_MASK | GDK_KEY_PRESS_MASK | GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_VISIBILITY_NOTIFY_MASK; attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP; widget->window = gdk_window_new(gtk_widget_get_parent_window(widget), &attributes, attributes_mask); gdk_window_set_user_data(widget->window, render); widget->style = gtk_style_attach(widget->style, widget->window); gtk_style_set_background(widget->style, widget->window, GTK_STATE_NORMAL); GTK_WIDGET_SET_FLAGS(widget, GTK_REALIZED); gdk_display_sync(gtk_widget_get_display(widget)); #if SYSTEM_X11 == 1 glXWaitX(); #endif /* Initialize context for OpenGL, OS dependent. */ openGLWidgetInit_context(render, render->isContextDirect); openGLWidgetSet_current(render); } OpenGLWidget* openGLWidgetClassGet_currentContext() { g_return_val_if_fail(myClass, (OpenGLWidget*)0); return myClass->contextCurrent; } static void openGLWidgetSet_viewport(OpenGLWidget *render, guint width, guint height, gboolean redraw) { g_return_if_fail(IS_OPENGL_WIDGET(render)); if (OPENGL_WIDGET_GET_CLASS(render)->contextCurrent != render) return; DBG_fprintf(stderr, " | adjusting viewport to %dx%d.\n", width, height); glViewport(0, 0, width, height); if (redraw) { /* We synchronize the rendering area. */ gdk_display_sync(gtk_widget_get_display(GTK_WIDGET(render))); /* We clear the back buffer and swap because this buffer has still the wrong size. */ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); openGLWidgetSwap_buffers(render); } } void openGLWidgetSet_redraw(OpenGLWidget *render, RedrawMethod method, gpointer data) { g_return_if_fail(IS_OPENGL_WIDGET(render)); DBG_fprintf(stderr, "Gtk OpenGL (action) : set redraw method for OpenGL area %p.\n", (gpointer)render); render->redraw = method; render->redrawData = data; } void openGLWidgetRedraw(OpenGLWidget *render) { g_return_if_fail(IS_OPENGL_WIDGET(render)); if (!render->redraw) return; DBG_fprintf(stderr, "Gtk OpenGL (action) : redraw OpenGL area for %p.\n", (gpointer)render); if (render->redraw) { DBG_fprintf(stderr, " | set current.\n"); openGLWidgetSet_current(render); DBG_fprintf(stderr, " | redraw inside.\n"); render->redraw(GTK_WIDGET(render)->allocation.width, GTK_WIDGET(render)->allocation.height, render->redrawData); DBG_fprintf(stderr, " | swap buffers.\n"); openGLWidgetSwap_buffers(render); } } guchar* openGLWidgetGet_pixmapData(OpenGLWidget *render, int *width, int *height, gboolean offScreen) { GtkWidget *wd; guchar *image; DumpImage *dumpData; g_return_val_if_fail(IS_OPENGL_WIDGET(render), (guchar*)0); g_return_val_if_fail(OPENGL_WIDGET_GET_CLASS(render)->contextCurrent == render, (guchar*)0); g_return_val_if_fail(render->redraw, (guchar*)0); g_return_val_if_fail(width && height, (guchar*)0); wd = GTK_WIDGET(render); if (!offScreen) { *width = wd->allocation.width; *height = wd->allocation.height; return visuOpenGLGet_pixmapData(*width, *height); } /* If a method is given, then we draw in memory to a pixmap. */ *width = (*width > 0)?*width:wd->allocation.width; *height = (*height > 0)?*height:wd->allocation.height; /* We create a pixmap context and make this context current. */ OPENGL_WIDGET_GET_CLASS(render)->contextCurrent = (OpenGLWidget*)0; dumpData = visuOpenGLNew_pixmapContext((guint)*width, (guint)*height); /* We set the glViewport of this new context. */ glViewport(0, 0, *width, *height); /* We call the given draw method. */ render->redraw(*width, *height, render->redrawData); /* We copy the pixmap into generic data. */ image = visuOpenGLGet_pixmapData((guint)*width, (guint)*height); /* We free the pixmap context. */ visuOpenGLFree_pixmapContext(dumpData); /* We change back the context to the current rendering area. */ openGLWidgetSet_current(render); return image; } /* OpenGL functions, OS dependent. */ #if SYSTEM_X11 == 1 static void openGLWidgetInit_context(OpenGLWidget *render, gboolean contextIsDirect) { gint screenId; XVisualInfo *vinfo; GtkWidget *wd; DBG_fprintf(stderr, "Gtk OpenGL (init) : create an OpenGL context (%d).\n", contextIsDirect); wd = GTK_WIDGET(render); if (!render->dpy) render->dpy = gdk_x11_drawable_get_xdisplay(GDK_DRAWABLE(wd->window)); DBG_fprintf(stderr, " | get the display %p.\n", (gpointer)render->dpy); /* Check for GLX. */ if (!glXQueryExtension(render->dpy, 0, 0)) g_error("No GLX extension.\nYour X server" " does not support OpenGL extension. Please contact your" " system administrator to ask him to add the 'glx'" " extension to your X server.\n"); /* Get the screen id. */ screenId = gdk_x11_screen_get_screen_number (gdk_drawable_get_screen(GDK_DRAWABLE(wd->window))); DBG_fprintf(stderr, " | get a screen number %d.\n", screenId); /* We don't cancel the double buffering since the backing store need it. */ /* if (DoesBackingStore(ScreenOfDisplay(render->dpy, screenId)) == NotUseful) */ gtk_widget_set_double_buffered(GTK_WIDGET(render), FALSE); /* Try to create a visual. */ vinfo = visuOpenGLGet_visualInfo(render->dpy, screenId); /* Finaly, create the context. */ if (contextIsDirect) { render->context = glXCreateContext(render->dpy, vinfo, 0, GL_TRUE); if (!render->context) { g_warning("Can't create a direct rendering context, try an inderect one.\n"); render->context = glXCreateContext(render->dpy, vinfo, 0, GL_FALSE); render->isContextDirect = FALSE; } } else render->context = glXCreateContext(render->dpy, vinfo, 0, GL_FALSE); DBG_fprintf(stderr, " | create the context %p (%d).\n", (gpointer)render->context, (int)render->isContextDirect); if (!render->context) g_error("Cannot create a GLX context.\n"); } static void openGLWidgetFree_openGL(OpenGLWidget *render) { g_return_if_fail(IS_OPENGL_WIDGET(render)); if (render->dpy) { DBG_fprintf(stderr, "Free : freeing context.\n"); if (render->context) glXDestroyContext(render->dpy, render->context); /* We do NOT close the display since it is shared by all the application and thus dpy structure is unique and will be closed by GTK when quiting. */ /* XCloseDisplay(render->dpy); */ } } gboolean openGLWidgetSet_current(OpenGLWidget *render) { int res; GtkWidget *wd; XID windowId; g_return_val_if_fail(IS_OPENGL_WIDGET(render), FALSE); if (OPENGL_WIDGET_GET_CLASS(render)->contextCurrent == render) return TRUE; DBG_fprintf(stderr, "Gtk OpenGL (debug) : widget visualID %d.\n", (int)gdk_x11_visual_get_xvisual (gtk_widget_get_visual(GTK_WIDGET(render)))->visualid); DBG_fprintf(stderr, "Gtk OpenGL (action) : %p is set current.\n", (gpointer)render); windowId = gdk_x11_drawable_get_xid(GDK_DRAWABLE(GTK_WIDGET(render)->window)); res = glXMakeCurrent(render->dpy, (GLXDrawable)windowId, render->context); if (!res) { g_warning("Cannot make the openGLWidget object %p current.\n", (gpointer)render); return FALSE; } /* Now that the glx tunnel has been added, we need to specify again that we want a backing store because until now the backing store is only for the X window (and thus is black) but not for the glx screen. */ /* wattrs.backing_store = Always; */ /* XChangeWindowAttributes(render->dpy, windowId, */ /* CWBackingStore, &wattrs); */ OPENGL_WIDGET_GET_CLASS(render)->contextCurrent = render; wd = GTK_WIDGET(render); openGLWidgetSet_viewport(render, wd->allocation.width, wd->allocation.height, FALSE); return TRUE; } void openGLWidgetSwap_buffers(OpenGLWidget *render) { g_return_if_fail(OPENGL_WIDGET_GET_CLASS(render)->contextCurrent == render); DBG_fprintf(stderr, "Gtk OpenGL (action) : swap buffers of area %p.\n", (gpointer)render); glXSwapBuffers(render->dpy, (GLXDrawable)gdk_x11_drawable_get_xid (GDK_DRAWABLE(GTK_WIDGET(render)->window))); } static GdkColormap* openGLWidgetGet_openGLColormap(OpenGLWidget *render) { XVisualInfo *vinfo; Display *dpy; int screenId; GdkVisual *visual; GdkColormap *colormap; g_return_val_if_fail(IS_OPENGL_WIDGET(render), (GdkColormap*)0); dpy = gdk_x11_get_default_xdisplay(); screenId = gdk_x11_get_default_screen(); vinfo = visuOpenGLGet_visualInfo(dpy, screenId); visual = gdkx_visual_get(vinfo->visualid); colormap = gdk_colormap_new(visual, FALSE); return colormap; } #endif #if SYSTEM_WIN32 == 1 static void openGLWidgetInit_context(OpenGLWidget *render, gboolean contextIsDirect) { GtkWidget *wd; DBG_fprintf(stderr, "Gtk OpenGL (init) : create an OpenGL context (%d).\n", contextIsDirect); wd = GTK_WIDGET(render); render->windowId = (HWND)gdk_win32_drawable_get_handle(GDK_DRAWABLE(wd->window)); render->hdc = GetDC(render->windowId); DBG_fprintf(stderr, " | get the hdc %d.\n", (int)render->hdc); /* Choose best matching format*/ visuOpenGLSetup_pixelFormat(render->hdc); /* Finaly, create the context. */ render->context = wglCreateContext(render->hdc); } static void openGLWidgetFree_openGL(OpenGLWidget *render) { g_return_if_fail(IS_OPENGL_WIDGET(render)); if (render->context) wglDeleteContext(render->context); /* if (render->hdc) */ /* DeleteDC(render->hdc); */ } gboolean openGLWidgetSet_current(OpenGLWidget *render) { int res; GtkWidget *wd; g_return_val_if_fail(IS_OPENGL_WIDGET(render), FALSE); DBG_fprintf(stderr, "Gtk OpenGL (action) : %p is set current.\n", (gpointer)render); wglMakeCurrent(NULL, NULL); wglMakeCurrent(render->hdc, render->context); OPENGL_WIDGET_GET_CLASS(render)->contextCurrent = render; wd = GTK_WIDGET(render); openGLWidgetSet_viewport(render, wd->allocation.width, wd->allocation.height, FALSE); return TRUE; } void openGLWidgetSwap_buffers(OpenGLWidget *render) { g_return_if_fail(OPENGL_WIDGET_GET_CLASS(render)->contextCurrent == render); DBG_fprintf(stderr, "Gtk OpenGL (action) : swap buffers of area %p.\n", (gpointer)render); SwapBuffers(render->hdc); } static GdkColormap* openGLWidgetGet_openGLColormap(OpenGLWidget *render) { g_return_val_if_fail(IS_OPENGL_WIDGET(render), (GdkColormap*)0); return gdk_screen_get_system_colormap(gdk_screen_get_default()); } #endif