Logo Search packages:      
Sourcecode: v-sim version File versions  Download package

gtk_openGLWidget.c

/*   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 <config.h>
#endif

#include "gtk/gtkwidget.h"

#include <GL/gl.h>

#include "gtk_openGLWidget.h"
#include "OSOpenGL/visu_openGL.h"
#include "visu_tools.h"

/* OS dependent includes */
#if SYSTEM_X11 == 1
#include <GL/glx.h>
#include <gdk/gdkx.h>
#endif
#if SYSTEM_WIN32 == 1
#include <windows.h>
#include <gdk/gdkwin32.h>
#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

Generated by  Doxygen 1.6.0   Back to index