/*   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 <cairo.h>
#include <gtk/gtk.h>

#include <GL/gl.h>
#include <GL/glu.h>

#include "visu_basic.h"
#include "gtk_openGLWidget.h"
#include "OSOpenGL/visu_openGL.h"
#include "visu_tools.h"
#include "visu_extension.h"
#include "visu_gtk.h"
#include "opengl.h"
#include "openGLFunctions/text.h"

/* OpenGL implementation dependencies */
#ifdef HAVE_GTKGLEXT
  #include <gtk/gtkgl.h>
  #include <gdk/gdkx.h>
  #define IMPL_GTKGLEXT
#else
  #if SYSTEM_X11 == 1
  #include <GL/glx.h>
  #include <gdk/gdkx.h>
  #define IMPL_BUILTIN_X11
  #endif
  #if SYSTEM_WIN32 == 1
  #include <windows.h>
  #include <gdk/gdkwin32.h>
  #define IMPL_BUILTIN_WIN32
  #endif
  #if SYSTEM_QUARTZ == 1
  #include <agl.h>
  #include <gdk/gdkquatrz.h>
  #define IMPL_BUILTIN_QUARTZ
  #endif
#endif

/**
 * SECTION:gtk_openGLWidget
 * @short_description: defines an OpenGL capable GtkWidget.
 *
 * <para>This is a simple implementation of GtkGlExt to create an
 * OpenGL surface that is a full GtkWidget. When creating such a
 * widget, one should give then a VisuUiGlWidgetRedrawMethod() to tell the widget
 * how to redraw itself when needed.</para>
 *
 * <para>The current implementation is working on X11 (built-in or
 * with GtkGlExt) and Win32.</para>
 */
struct _VisuUiGlWidget
{
  GtkWidget parent;

  gboolean sizeAllocation_has_run;
  gboolean dispose_has_run;

  /* Redraw method and user data. */
  VisuGlExtSet *model;

  /* Default background pixbuf. */
  cairo_surface_t *backLogo;

  /* OpenGL part, OS dependent. */
  gboolean isContextDirect;
  GdkVisual *visual;
#ifdef IMPL_GTKGLEXT
  GdkGLConfig *glconfig;
  GdkGLWindow *glwindow;
  GdkGLContext *context;
#endif
#ifdef IMPL_BUILTIN_X11
  Display *dpy;
  XVisualInfo *vinfo;
  GLXContext context;
#endif
#ifdef IMPL_BUILTIN_WIN32
  HDC hdc;
  HGLRC context;
  HWND windowId;
#endif
#ifdef IMPL_BUILTIN_QUARTZ
  NSWindow windowId;
  AGLContext context;
  AGLPixelFmtID pxlfmt;
#endif
};

struct _VisuUiGlWidgetClass
{
  GtkWidgetClass parent_class;

  VisuUiGlWidget *contextCurrent;
};

G_DEFINE_TYPE(VisuUiGlWidget, visu_ui_gl_widget, GTK_TYPE_WIDGET)

/* Local variables. */
static VisuUiGlWidgetClass *myClass = (VisuUiGlWidgetClass*)0;

/* Local callbacks. */
#if GTK_MAJOR_VERSION < 3 && GTK_MINOR_VERSION < 26
static gboolean visu_ui_gl_widgetEvent_expose(GtkWidget *widget, GdkEventExpose *event);
static void visu_ui_gl_widgetEvent_sizeRequest(GtkWidget *widget, GtkRequisition *requisition);
#else
static gboolean visu_ui_gl_widgetEvent_draw(GtkWidget *widget, cairo_t *cr);
static void visu_ui_gl_widgetEvent_getWidth(GtkWidget *widget, gint *minWidth, gint *width);
static void visu_ui_gl_widgetEvent_getHeight(GtkWidget *widget, gint *minHeight, gint *height);
#endif
static gboolean visu_ui_gl_widgetEvent_visibility(GtkWidget *widget, GdkEventVisibility *event);
static void visu_ui_gl_widgetEvent_sizeAllocate(GtkWidget *widget, GtkAllocation *allocation);
static void visu_ui_gl_widgetEvent_realise(GtkWidget *widget);
static void drawToGl(VisuUiGlWidget *render);
static void drawToEmpty(VisuUiGlWidget *render, cairo_t *cr);
static void swapGl(VisuUiGlWidget *render);

/* Initialisation methods. */
static void visu_ui_gl_widgetInit_context(VisuUiGlWidget *render, gboolean contextIsDirect);

/* Freeing methods. */
static void visu_ui_gl_widgetEvent_dispose(GObject *obj);
static void visu_ui_gl_widgetFree_openGL(VisuUiGlWidget *render);

/* Miscellaneous methods. */
static void setViewport(VisuUiGlWidget *render, guint width,
				     guint height, gboolean redraw);
static void visu_ui_gl_widgetSet_pixelFormat(VisuUiGlWidget *render);
#if GTK_MAJOR_VERSION < 3 && GTK_MINOR_VERSION < 26
static GdkColormap* visu_ui_gl_widgetGet_openGLColormap(VisuUiGlWidget *render);
#endif

static void visu_ui_gl_widget_class_init(VisuUiGlWidgetClass *class)
{
  GtkWidgetClass *widget_class;
  GObjectClass *gobject_class;

  /* Dealing with GObject events. */
  gobject_class = G_OBJECT_CLASS(class);
  gobject_class->dispose  = visu_ui_gl_widgetEvent_dispose;

  /* Dealing with widget events. */
  widget_class = GTK_WIDGET_CLASS(class);
#if GTK_MAJOR_VERSION < 3 && GTK_MINOR_VERSION < 26
  widget_class->expose_event  = visu_ui_gl_widgetEvent_expose;
  widget_class->size_request  = visu_ui_gl_widgetEvent_sizeRequest;
#else
  widget_class->draw          = visu_ui_gl_widgetEvent_draw;
  widget_class->get_preferred_width  = visu_ui_gl_widgetEvent_getWidth;
  widget_class->get_preferred_height = visu_ui_gl_widgetEvent_getHeight;
#endif
  widget_class->visibility_notify_event = visu_ui_gl_widgetEvent_visibility;
  widget_class->realize       = visu_ui_gl_widgetEvent_realise;
  widget_class->size_allocate = visu_ui_gl_widgetEvent_sizeAllocate;

  class->contextCurrent = (VisuUiGlWidget*)0;

  myClass = class;
}

static void visu_ui_gl_widget_init(VisuUiGlWidget *render)
{
  DBG_fprintf(stderr, "Gtk OpenGL (init) : create object %p.\n", (gpointer)render);
  render->sizeAllocation_has_run = FALSE;
  render->dispose_has_run = FALSE;

  render->model = (VisuGlExtSet*)0;

  render->backLogo = (cairo_surface_t*)0;

  render->isContextDirect = FALSE;
  render->visual   = (GdkVisual*)0;
#ifdef IMPL_GTKGLEXT
  render->glconfig = (GdkGLConfig*)0;
  render->glwindow = (GdkGLWindow*)0;
  render->context  = (GdkGLContext*)0;
#endif
#ifdef IMPL_BUILTIN_X11
  render->dpy      = (Display*)0;
  render->vinfo    = (XVisualInfo*)0;
  render->context  = (GLXContext)0;
#endif
#ifdef IMPL_BUILTIN_WIN32
  render->hdc      = (HDC)0;
  render->context  = (HGLRC)0;
  render->windowId = (HWND)0;
#endif

  /* Cancel the GTK double buffering since it is taken into
     account by OpenGL itself. */
#if GTK_MAJOR_VERSION < 3 || (GTK_MAJOR_VERSION == 3 && GTK_MINOR_VERSION < 14)
  gtk_widget_set_double_buffered(GTK_WIDGET(render), FALSE);
#endif
  gtk_widget_set_has_window(GTK_WIDGET(render), TRUE);
}

GtkWidget* visu_ui_gl_widget_new(gboolean contextIsDirect)
{
  VisuUiGlWidget *render;

  render = VISU_UI_GL_WIDGET(g_object_new(VISU_TYPE_UI_GL_WIDGET, NULL));

  render->isContextDirect = contextIsDirect;

  return GTK_WIDGET(render);
}

/* Methods to deals with the events. */
static void visu_ui_gl_widgetEvent_dispose(GObject *obj)
{
  VisuUiGlWidget *self;

  DBG_fprintf(stderr, "Gtk OpenGL (events) : dispose event for object %p.\n", (gpointer)obj);

  self = VISU_UI_GL_WIDGET(obj);

  if (self->dispose_has_run)
    return;
  self->dispose_has_run = TRUE;

  visu_ui_gl_widgetFree_openGL(self);

  if (self->backLogo)
    cairo_surface_destroy(self->backLogo);
  if (self->model)
    g_object_unref(self->model);

  /* Chain up to the parent class */
  G_OBJECT_CLASS(visu_ui_gl_widget_parent_class)->dispose(obj);
}
#if GTK_MAJOR_VERSION < 3 && GTK_MINOR_VERSION < 26
static gboolean visu_ui_gl_widgetEvent_expose(GtkWidget *widget, GdkEventExpose *event _U_)
{
  DBG_fprintf(stderr, "Gtk OpenGL (events): expose event for object %p.\n", (gpointer)widget);
  if (VISU_UI_GL_WIDGET(widget)->model)
    drawToGl(VISU_UI_GL_WIDGET(widget));

  return FALSE;
}
#else
static gboolean visu_ui_gl_widgetEvent_draw(GtkWidget *widget, cairo_t *cr)
{
  DBG_fprintf(stderr, "Gtk OpenGL (events): draw event for object %p.\n", (gpointer)widget);
  if (VISU_UI_GL_WIDGET(widget)->model)
    drawToGl(VISU_UI_GL_WIDGET(widget));
  else
    drawToEmpty(VISU_UI_GL_WIDGET(widget), cr);

  return FALSE;
}
#endif
static gboolean visu_ui_gl_widgetEvent_visibility(GtkWidget *widget, GdkEventVisibility *event)
{
  if (event->state != GDK_VISIBILITY_UNOBSCURED)
    return FALSE;

  DBG_fprintf(stderr, "Gtk OpenGL (events): visibility event for object %p.\n", (gpointer)widget);
  gtk_widget_queue_draw(widget);
  DBG_fprintf(stderr, "Gtk OpenGL (events): visibility OK.\n");

  return FALSE;
}
#if GTK_MAJOR_VERSION < 3 && GTK_MINOR_VERSION < 26
static void visu_ui_gl_widgetEvent_sizeRequest(GtkWidget *widget,
					  GtkRequisition *requisition)
{
  VisuUiGlWidget *render;
  GtkAllocation allocation;

  DBG_fprintf(stderr, "Gtk OpenGL (events): size request event.\n");

  render = VISU_UI_GL_WIDGET(widget);
  if (render->sizeAllocation_has_run)
    {
      gtk_widget_get_allocation(widget, &allocation);
      requisition->width  = allocation.width;
      requisition->height = allocation.height;
    }
  else
    {
      requisition->width  = 200;
      requisition->height = 200;
    }

  /* Chain up to default that simply reads current requisition */
/*   GTK_WIDGET_CLASS(visu_ui_gl_widget_parent_class)->size_request(widget, requisition); */

  DBG_fprintf(stderr, "Gtk OpenGL (events): size request event (%dx%d).\n",
	      requisition->width, requisition->height);
}
#else
static void visu_ui_gl_widgetEvent_getSize(GtkWidget *widget _U_, GtkOrientation orientation,
                                      gint *minSize, gint *size)
{
  DBG_fprintf(stderr, "Gtk OpenGL (events): size request event.\n");

  if (orientation == GTK_ORIENTATION_HORIZONTAL)
    {
      *minSize = 200;
      *size = 450;
    }
  else
    {
      *minSize = 200;
      *size = 450;
    }
}
static void visu_ui_gl_widgetEvent_getWidth(GtkWidget *widget, gint *minWidth, gint *width)
{
  visu_ui_gl_widgetEvent_getSize(widget, GTK_ORIENTATION_HORIZONTAL, minWidth, width);
}
static void visu_ui_gl_widgetEvent_getHeight(GtkWidget *widget, gint *minHeight, gint *height)
{
  visu_ui_gl_widgetEvent_getSize(widget, GTK_ORIENTATION_HORIZONTAL, minHeight, height);
}
#endif
static void visu_ui_gl_widgetEvent_sizeAllocate(GtkWidget *widget, GtkAllocation *allocation)
{
  VisuUiGlWidget *render;
  GtkAllocation wdAllocation;

  render = VISU_UI_GL_WIDGET(widget);
  gtk_widget_get_allocation(widget, &wdAllocation);
  if ((wdAllocation.width  == allocation->width) &&
      (wdAllocation.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(visu_ui_gl_widget_parent_class)->size_allocate(widget, allocation);

#ifdef IMPL_BUILTIN_X11
  glXWaitX();
#endif

  setViewport(render, allocation->width, allocation->height, FALSE);
}
static void visu_ui_gl_widgetEvent_realise(GtkWidget *widget)
{
  VisuUiGlWidget *render;
  GdkWindowAttr attributes;
  gint attributes_mask;
#if GTK_MAJOR_VERSION < 3 && GTK_MINOR_VERSION < 26
  GdkColormap *colormap;
#else
#define GDK_WA_COLORMAP 0
#endif
  GdkWindow *window;
  GtkAllocation allocation;

  DBG_fprintf(stderr, "Gtk OpenGL (events): realise event for %p.\n",
	      (gpointer)widget);

  gtk_widget_set_realized(widget, TRUE);

  render = VISU_UI_GL_WIDGET(widget);

#ifndef IMPL_BUILTIN_WIN32
  visu_ui_gl_widgetSet_pixelFormat(render);
#else
  render->visual = gdk_screen_get_system_visual(gdk_screen_get_default());
#endif

  gtk_widget_get_allocation(widget, &allocation);
  attributes.window_type = GDK_WINDOW_CHILD;
  attributes.x      = allocation.x;
  attributes.y      = allocation.y;
  attributes.width  = allocation.width;
  attributes.height = allocation.height;
  attributes.wclass = GDK_INPUT_OUTPUT;
#if GTK_MAJOR_VERSION < 3 && GTK_MINOR_VERSION < 26
  colormap = visu_ui_gl_widgetGet_openGLColormap(render);
  DBG_fprintf(stderr, "Gtk OpenGL (debug): colormap is %p.\n",
	      (gpointer)colormap);
  attributes.visual = gdk_colormap_get_visual(colormap);
  attributes.colormap = colormap;
#else
  attributes.visual = render->visual;
#endif
  attributes.event_mask = GDK_EXPOSURE_MASK |
    GDK_VISIBILITY_NOTIFY_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_ENTER_NOTIFY_MASK;

#ifdef IMPL_GTKGLEXT
  attributes_mask = GDK_WA_X | GDK_WA_Y;
#else
  attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
#endif

  window = gdk_window_new(gtk_widget_get_parent_window(widget), 
			  &attributes, attributes_mask);
  gtk_widget_set_window(widget, window);
#if GTK_MAJOR_VERSION < 3 && GTK_MINOR_VERSION < 26
  gdk_window_set_back_pixmap(window, NULL, FALSE);
#elseif GTK_MAJOR_VERSION == 3 && GTK_MINOR_VERSION < 22
  gdk_window_set_background_pattern(window, NULL);
#endif
#if GTK_MAJOR_VERSION < 3 || (GTK_MAJOR_VERSION == 3 && GTK_MINOR_VERSION < 8)
  gdk_window_set_user_data(window, widget);
#else
  gtk_widget_register_window(widget, window);
#endif
  G_GNUC_BEGIN_IGNORE_DEPRECATIONS
  gtk_widget_set_double_buffered(widget, FALSE);
  G_GNUC_END_IGNORE_DEPRECATIONS
  DBG_fprintf(stderr, "Gtk OpenGL (debug): GdkWindow is %p.\n",
	      (gpointer)window);

#if GTK_MAJOR_VERSION < 3
  gtk_widget_style_attach(widget);
#endif
#if GTK_MAJOR_VERSION < 3 || (GTK_MAJOR_VERSION == 3 && GTK_MINOR_VERSION < 4)
  gdk_window_set_background(window,
                            &gtk_widget_get_style(widget)->base[GTK_STATE_NORMAL]);
#else
  {
    GtkCssProvider *css = gtk_css_provider_new();
    gtk_css_provider_load_from_data(css, "VisuUiGlWidget {background-color: @theme_bg_color;}", -1, (GError**)0);
    
    gtk_style_context_add_provider(gtk_widget_get_style_context(widget),
                                   GTK_STYLE_PROVIDER(css),
                                   GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);

    g_object_unref(css);
  }
#endif

  gtk_widget_set_can_focus(widget, TRUE);
  gtk_widget_set_can_default(widget, TRUE);

  gdk_display_sync(gtk_widget_get_display(widget));
#ifdef IMPL_BUILTIN_X11
  glXWaitX();
#endif

  /* Initialize context for OpenGL, OS dependent. */
#ifdef IMPL_BUILTIN_WIN32
  visu_ui_gl_widgetSet_pixelFormat(render);
#endif
  visu_ui_gl_widgetInit_context(render, render->isContextDirect);
  visu_ui_gl_widget_setCurrent(render, FALSE);
}

/**
 * visu_ui_gl_widget_class_getCurrentContext:
 *
 * Class routine that returns the OpenGL widget which has the current context.
 *
 * Returns: (transfer none): the #VisuUiGlWidget with the current OpenGL context.
 */
VisuUiGlWidget* visu_ui_gl_widget_class_getCurrentContext()
{
  g_return_val_if_fail(myClass, (VisuUiGlWidget*)0);

  return myClass->contextCurrent;
}

static void setViewport(VisuUiGlWidget *render, guint width,
			guint height, gboolean redraw)
{
  g_return_if_fail(VISU_IS_UI_GL_WIDGET(render));

  if (VISU_UI_GL_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);
      swapGl(render);
    }
}
static void drawToEmpty(VisuUiGlWidget *render, cairo_t *cr)
{
  GtkAllocation allocation;
  gchar *path;

  DBG_fprintf(stderr, "Gtk OpenGL (action): Rebuild default background.\n");

  gtk_widget_get_allocation(GTK_WIDGET(render), &allocation);
  gtk_render_background(gtk_widget_get_style_context(GTK_WIDGET(render)),
                        cr, allocation.x, allocation.y, allocation.width, allocation.height);
  
  if (!render->backLogo)
    {
      path = g_build_filename(visu_basic_getPixmapsDir(), "logo_grey.png", NULL);
      render->backLogo = cairo_image_surface_create_from_png(path);
      g_free(path);
    }
  cairo_set_source_surface(cr, render->backLogo, allocation.x + allocation.width / 2 - cairo_image_surface_get_width(render->backLogo) / 2,
                           allocation.y + allocation.height / 2 - cairo_image_surface_get_height(render->backLogo) / 2);
  cairo_paint(cr);
}
/**
 * visu_ui_gl_widget_setModel:
 * @render: a #VisuUiGlWidget object ;
 * @model: a #VisuGlExtSet object.
 *
 * This method is used to defined a redraw method for the OpenGL area. By doing this
 * the area will automatically redraw itself when necessary. Before doing it
 * it calls visu_ui_gl_widget_setCurrent(), and after it calls
 * swapGl().
 */
void visu_ui_gl_widget_setModel(VisuUiGlWidget *render, VisuGlExtSet *model)
{
  VisuGlExtSet *current;

  g_return_if_fail(VISU_IS_UI_GL_WIDGET(render));
  
  DBG_fprintf(stderr, "Gtk OpenGL (action) : set redraw method for OpenGL area %p.\n", (gpointer)render);

  if (render->model == model)
    return;

  current = render->model;
  render->model = model;
  
  if (current)
    {
      g_object_unref(current);
    }
  if (model)
    {
      g_object_ref(model);
    }

  gtk_widget_queue_draw(GTK_WIDGET(render));
}
static void drawToGl(VisuUiGlWidget *render)
{
#if DEBUG == 1
  GTimer *timer;
  gulong fractionTimer;
#endif

  g_return_if_fail(VISU_IS_UI_GL_WIDGET(render));

#if DEBUG == 1
  timer = g_timer_new();
  g_timer_start(timer);
#endif

  DBG_fprintf(stderr, "Gtk OpenGL (action) : redraw OpenGL area for %p.\n", (gpointer)render);
  DBG_fprintf(stderr, " | set current.\n");
  visu_ui_gl_widget_setCurrent(render, FALSE);
  DBG_fprintf(stderr, " | redraw inside.\n");
  visu_gl_ext_set_draw(render->model);
  DBG_fprintf(stderr, " | swap buffers.\n");
  swapGl(render);
#if DEBUG == 1
  g_timer_stop(timer);
  fprintf(stderr, "Gtk OpenGL: total redraw in %g micro-s.\n",
          g_timer_elapsed(timer, &fractionTimer)*1e6);
  g_timer_destroy(timer);
#endif
}

/* OpenGL functions, OS dependent. */
#ifdef IMPL_GTKGLEXT
static void visu_ui_gl_widgetSet_pixelFormat(VisuUiGlWidget *render)
{
  GdkScreen *screen;

  DBG_fprintf(stderr, "Gtk OpenGL (glext): choose visual for %p.\n", render);
  /* Get the screen. */
  if (gtk_widget_get_visible(GTK_WIDGET(render)))
    screen = gtk_widget_get_screen(GTK_WIDGET(render));
  else
    screen = gdk_screen_get_default();
  DBG_fprintf(stderr, " | get a screen number %d.\n",
	      gdk_x11_screen_get_screen_number(screen));

  render->glconfig = visu_gl_getGLConfig(screen);
  DBG_fprintf(stderr, " | get a visual.\n");
}
static void visu_ui_gl_widgetInit_context(VisuUiGlWidget *render, gboolean contextIsDirect)
{
  GtkWidget *wd;

  DBG_fprintf(stderr, "Gtk OpenGL (glext): create an OpenGL context (%d).\n",
	      contextIsDirect);

  wd = GTK_WIDGET(render);

  /* Create a GL area on the widget window. */
  render->glwindow = gdk_gl_window_new(render->glconfig, wd->window, NULL);

  /* Finaly, create the context. */
  if (contextIsDirect)
    {
      render->context = gdk_gl_context_new(GDK_GL_DRAWABLE(render->glwindow),
					   (GdkGLContext*)0, TRUE, GDK_GL_RGBA_TYPE);
      if (!render->context)
	{
	  g_warning("Can't create a direct rendering context, try an inderect one.\n");
	  render->context = gdk_gl_context_new(GDK_GL_DRAWABLE(render->glwindow),
					       (GdkGLContext*)0, FALSE,
					       GDK_GL_RGBA_TYPE);
	  render->isContextDirect = FALSE;
	}
    }
  else
    render->context = gdk_gl_context_new(GDK_GL_DRAWABLE(render->glwindow),
					 (GdkGLContext*)0, FALSE, GDK_GL_RGBA_TYPE);
  DBG_fprintf(stderr, " | create the context %p (%d).\n", (gpointer)render->context,
	      (int)render->isContextDirect);

  if (!render->context)
    {
      g_error("Cannot create a GtkGLExt context.\n");
    }
}
static void visu_ui_gl_widgetFree_openGL(VisuUiGlWidget *render)
{
  g_return_if_fail(VISU_IS_UI_GL_WIDGET(render));

  DBG_fprintf(stderr, "Gtk OpenGL (glext): freeing context & window.\n");
  if (render->glwindow)
    gdk_gl_window_destroy(render->glwindow);
  if (render->context)
    gdk_gl_context_destroy(render->context);
}
gboolean visu_ui_gl_widget_setCurrent(VisuUiGlWidget *render, gboolean force)
{
  gboolean res;
  GtkWidget *wd;
  
  g_return_val_if_fail(VISU_IS_UI_GL_WIDGET(render), FALSE);

  if (!force && VISU_UI_GL_WIDGET_GET_CLASS(render)->contextCurrent == render)
    return TRUE;

  DBG_fprintf(stderr, "Gtk OpenGL (glext): %p is set current.\n", (gpointer)render);
  res = gdk_gl_drawable_make_current(GDK_GL_DRAWABLE(render->glwindow),
				     render->context);
  visu_gl_text_onNewContext();

  if (!res)
    {
      g_warning("Cannot make the visu_ui_gl_widget object %p current.\n",
		(gpointer)render);
      return FALSE;
    }
  DBG_fprintf(stderr, "Gtk OpenGL (glext): OK context changed.\n");

  VISU_UI_GL_WIDGET_GET_CLASS(render)->contextCurrent = render;

  wd = GTK_WIDGET(render);
  setViewport(render, wd->allocation.width, wd->allocation.height, FALSE);

  return TRUE;
}
static void swapGl(VisuUiGlWidget *render)
{
  g_return_if_fail(VISU_UI_GL_WIDGET_GET_CLASS(render)->contextCurrent == render);

  DBG_fprintf(stderr, "Gtk OpenGL (glext): swap buffers of area %p.\n",
	      (gpointer)render);
  gdk_gl_drawable_swap_buffers(GDK_GL_DRAWABLE(render->glwindow));
}
static GdkColormap* visu_ui_gl_widgetGet_openGLColormap(VisuUiGlWidget *render)
{
  g_return_val_if_fail(render->glconfig, (GdkColormap*)0);

  return gdk_gl_config_get_colormap(render->glconfig);
}
#endif
#ifdef IMPL_BUILTIN_X11
static void visu_ui_gl_widgetSet_pixelFormat(VisuUiGlWidget *render)
{
  gint screenId;
  gboolean built;
  GdkScreen *screen;

  built = gtk_widget_get_visible(GTK_WIDGET(render));
  DBG_fprintf(stderr, "Gtk OpenGL (X11): choose visual for %p.\n", (gpointer)render);
  if (built)
    render->dpy = GDK_DISPLAY_XDISPLAY(gtk_widget_get_display(GTK_WIDGET(render)));
  else
    render->dpy = gdk_x11_get_default_xdisplay();
  DBG_fprintf(stderr, " | get the display %p.\n", (gpointer)render->dpy);
  /* XSynchronize(render->dpy, True); */

  /* Get the screen id. */
  if (built)
    screen = gtk_widget_get_screen(GTK_WIDGET(render));
  else
    screen = gdk_screen_get_default();
  screenId = gdk_x11_screen_get_screen_number(screen);
  DBG_fprintf(stderr, " | get a screen number %d.\n", screenId);

  render->vinfo = visu_gl_getVisualInfo(render->dpy, screenId);
  DBG_fprintf(stderr, " | get a visual %d.\n", (int)render->vinfo->visualid);
  render->visual = gdk_x11_screen_lookup_visual(screen, render->vinfo->visualid);
}
static void visu_ui_gl_widgetInit_context(VisuUiGlWidget *render, gboolean contextIsDirect)
{
  DBG_fprintf(stderr, "Gtk OpenGL (X11): create an OpenGL context (%d).\n",
	      contextIsDirect);

  /* 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");
    }

  /* Finaly, create the context. */
  if (contextIsDirect)
    {
      render->context = glXCreateContext(render->dpy, render->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, render->vinfo, 0, GL_FALSE);
	  render->isContextDirect = FALSE;
	}
    }
  else
    render->context = glXCreateContext(render->dpy, render->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 visu_ui_gl_widgetFree_openGL(VisuUiGlWidget *render)
{
  g_return_if_fail(VISU_IS_UI_GL_WIDGET(render));

  if (render->dpy)
    {
      DBG_fprintf(stderr, "Free (X11): freeing context (%p).\n",
                  (gpointer)render->context);
      if (render->context)
        {
          glXMakeCurrent(render->dpy, None, NULL);
          glXWaitX();
          glXDestroyContext(render->dpy, render->context);
        }
      if (render->vinfo)
        XFree(render->vinfo);
      /* 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 visu_ui_gl_widget_setCurrent(VisuUiGlWidget *render, gboolean force)
{
  int res;
  GtkWidget *wd;
  XID windowId;
  GtkAllocation alloc;
  
  g_return_val_if_fail(VISU_IS_UI_GL_WIDGET(render), FALSE);

  if (!force && VISU_UI_GL_WIDGET_GET_CLASS(render)->contextCurrent == render)
    return TRUE;

  DBG_fprintf(stderr, "Gtk OpenGL (X11): unset the context.\n");
  g_return_val_if_fail(render->dpy, FALSE);
  glXMakeCurrent(render->dpy, None, NULL);
  glXWaitX();
  
  DBG_fprintf(stderr, "Gtk OpenGL (X11): widget visualID %d.\n",
	      (int)gdk_x11_visual_get_xvisual
	      (gtk_widget_get_visual(GTK_WIDGET(render)))->visualid);

  DBG_fprintf(stderr, "Gtk OpenGL (X11): %p is set current.\n", (gpointer)render);

  windowId = GDK_WINDOW_XID(GDK_WINDOW(gtk_widget_get_window(GTK_WIDGET(render))));
  DBG_fprintf(stderr, "Gtk OpenGL (X11): widget windowID 0x%x.\n", (int)windowId);

  res = glXMakeCurrent(render->dpy, (GLXDrawable)windowId,
		       render->context);
  if (!res)
    {
      g_warning("Cannot make the visu_ui_gl_widget object %p current.\n",
		(gpointer)render);
      return FALSE;
    }
  glXWaitX();
  DBG_fprintf(stderr, "Gtk OpenGL (X11): OK context changed.\n");
  visu_gl_text_onNewContext();

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

  VISU_UI_GL_WIDGET_GET_CLASS(render)->contextCurrent = render;

  wd = GTK_WIDGET(render);
  gtk_widget_get_allocation(wd, &alloc);
  setViewport(render, alloc.width, alloc.height, FALSE);

  return TRUE;
}
static void swapGl(VisuUiGlWidget *render)
{
  g_return_if_fail(VISU_UI_GL_WIDGET_GET_CLASS(render)->contextCurrent == render);

  DBG_fprintf(stderr, "Gtk OpenGL (X11): swap buffers of area %p.\n", (gpointer)render);
  glXSwapBuffers(render->dpy,
                 (GLXDrawable)GDK_WINDOW_XID(GDK_WINDOW(gtk_widget_get_window(GTK_WIDGET(render)))));
  DBG_fprintf(stderr, "Gtk OpenGL (X11): swap OK.\n");
}
#if GTK_MAJOR_VERSION < 3 && GTK_MINOR_VERSION < 26
static GdkColormap* visu_ui_gl_widgetGet_openGLColormap(VisuUiGlWidget *render)
{
  g_return_val_if_fail(VISU_IS_UI_GL_WIDGET(render), (GdkColormap*)0);
  g_return_val_if_fail(render->vinfo, (GdkColormap*)0);

  return gdk_colormap_new(gdkx_visual_get(render->vinfo->visualid), FALSE);
}
#endif
#endif
#ifdef IMPL_BUILTIN_WIN32
static void visu_ui_gl_widgetSet_pixelFormat(VisuUiGlWidget *render)
{
  DBG_fprintf(stderr, "Gtk OpenGL (Win32): choose visual for %p.\n", render);

  render->windowId = (HWND)gdk_win32_window_get_handle(GDK_WINDOW(gtk_widget_get_window(GTK_WIDGET(render))));
  render->hdc = GetDC(render->windowId);
  DBG_fprintf(stderr, " | get the hdc %d.\n", (int)render->hdc);

  visu_gl_setupPixelFormat(render->hdc);
}
static void visu_ui_gl_widgetInit_context(VisuUiGlWidget *render, gboolean contextIsDirect)
{
  DBG_fprintf(stderr, "Gtk OpenGL (Win32): create an OpenGL context (%d).\n", contextIsDirect);

  /* Finaly, create the context. */
  render->context = wglCreateContext(render->hdc);
}
static void visu_ui_gl_widgetFree_openGL(VisuUiGlWidget *render)
{
  g_return_if_fail(VISU_IS_UI_GL_WIDGET(render));

  if (render->context)
    wglDeleteContext(render->context);
/*   if (render->hdc) */
/*     DeleteDC(render->hdc); */
}
gboolean visu_ui_gl_widget_setCurrent(VisuUiGlWidget *render, gboolean force _U_)
{
  GtkWidget *wd;
  GtkAllocation alloc;

  g_return_val_if_fail(VISU_IS_UI_GL_WIDGET(render), FALSE);

  DBG_fprintf(stderr, "Gtk OpenGL (action) : %p is set current.\n", (gpointer)render);
  wglMakeCurrent(NULL, NULL);
  wglMakeCurrent(render->hdc, render->context);
  visu_gl_text_onNewContext();

  VISU_UI_GL_WIDGET_GET_CLASS(render)->contextCurrent = render;

  wd = GTK_WIDGET(render);
  gtk_widget_get_allocation(wd, &alloc);
  setViewport(render, alloc.width, alloc.height, FALSE);

  return TRUE;
}
static void swapGl(VisuUiGlWidget *render)
{
  g_return_if_fail(VISU_UI_GL_WIDGET_GET_CLASS(render)->contextCurrent == render);

  DBG_fprintf(stderr, "Gtk OpenGL (action) : swap buffers of area %p.\n", (gpointer)render);
  SwapBuffers(render->hdc);
}
#if GTK_MAJOR_VERSION < 3 && GTK_MINOR_VERSION < 26
static GdkColormap* visu_ui_gl_widgetGet_openGLColormap(VisuUiGlWidget *render)
{
  g_return_val_if_fail(VISU_IS_UI_GL_WIDGET(render), (GdkColormap*)0);
  
  return gdk_screen_get_system_colormap(gdk_screen_get_default());
}
#endif
#endif
#ifdef IMPL_BUILTIN_QUARTZ
static AGLPixelFmtID visu_gl_setupPixelFormat()
{
  AGLPixelFmtID id;
  int list[] = {
    AGL_RGBA,
    AGL_RED_SIZE, 1, AGL_GREEN_SIZE, 1, AGL_BLUE_SIZE, 1,
    AGL_DEPTH_SIZE, 1,
    AGL_DOUBLEBUFFER,
    AGL_STEREO,
    AGL_NONE
  };

  if ( (id = aglChoosePixelFmt(NULL, 0, list)) == NULL )
    {
      list[10] = AGL_NONE; 
      if ( (id = aglChoosePixelFmt(NULL, 0, list)) == NULL )
	{
	  g_error("Cannot find a pixel format.");
	}
      DBG_fprintf(stderr, " | not a stereo buffer.\n");
    }
  else
    DBG_fprintf(stderr, " | stereo buffer.\n");

  return id;
}
static void visu_ui_gl_widgetSet_pixelFormat(VisuUiGlWidget *render)
{
  DBG_fprintf(stderr, "Gtk OpenGL (Quartz): choose visual for %p.\n", render);

  render->windowId = (NSWindow)gdk_quartz_window_get_nswindow(GDK_WINDOW(GTK_WIDGET(render)->window));

  render->pxlfmt = visu_gl_setupPixelFormat();
  
  render->visual = gdk_screen_get_system_visual(gdk_screen_get_default());
}
static void visu_ui_gl_widgetInit_context(VisuUiGlWidget *render, gboolean contextIsDirect)
{
  DBG_fprintf(stderr, "Gtk OpenGL (Quartz): create an OpenGL context (%d).\n", contextIsDirect);

  /* Finaly, create the context. */
  render->context = aglCreateContext(render->pxlfmt, 0);
}
static void visu_ui_gl_widgetFree_openGL(VisuUiGlWidget *render)
{
  g_return_if_fail(VISU_IS_UI_GL_WIDGET(render));

  if (render->context)
    aglDestroyContext(render->context);
/*   if (render->hdc) */
/*     DeleteDC(render->hdc); */
}
gboolean visu_ui_gl_widget_setCurrent(VisuUiGlWidget *render, gboolean force _U_)
{
  GtkWidget *wd;

  g_return_val_if_fail(VISU_IS_UI_GL_WIDGET(render), FALSE);

  DBG_fprintf(stderr, "Gtk OpenGL (Quartz): %p is set current.\n", (gpointer)render);
  aglMakeCurrent((AGLDrawable)render->windowId, render->context);
  /* visu_gl_text_onNewContext(); */

  VISU_UI_GL_WIDGET_GET_CLASS(render)->contextCurrent = render;

  wd = GTK_WIDGET(render);
  setViewport(render, wd->allocation.width, wd->allocation.height, FALSE);

  return TRUE;
}
static void swapGl(VisuUiGlWidget *render)
{
  g_return_if_fail(VISU_UI_GL_WIDGET_GET_CLASS(render)->contextCurrent == render);

  DBG_fprintf(stderr, "Gtk OpenGL (Quartz): swap buffers of area %p.\n", (gpointer)render);
  aglSwapBuffers((AGLDrawable)render->windowId);
}
#if GTK_MAJOR_VERSION < 3 && GTK_MINOR_VERSION < 26
static GdkColormap* visu_ui_gl_widgetGet_openGLColormap(VisuUiGlWidget *render)
{
  g_return_val_if_fail(VISU_IS_UI_GL_WIDGET(render), (GdkColormap*)0);
  
  return gdk_screen_get_system_colormap(gdk_screen_get_default());
}
#endif
#endif
