/* Clutter.
 * An OpenGL based 'interactive canvas' library.
 * Authored By Matthew Allum  <mallum@openedhand.com>
 * Copyright (C) 2006-2007 OpenedHand
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include "clutter-backend-glx.h"
#include "clutter-stage-glx.h"
#include "clutter-glx.h"

#include "../clutter-main.h"
#include "../clutter-feature.h"
#include "../clutter-color.h"
#include "../clutter-util.h"
#include "../clutter-event.h"
#include "../clutter-enum-types.h"
#include "../clutter-private.h"
#include "../clutter-debug.h"
#include "../clutter-units.h"

#include "cogl.h"

#ifdef HAVE_XFIXES
#include <X11/extensions/Xfixes.h>
#endif

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

#include <gdk-pixbuf-xlib/gdk-pixbuf-xlib.h>

G_DEFINE_TYPE (ClutterStageGLX, clutter_stage_glx, CLUTTER_TYPE_STAGE);

#define _NET_WM_STATE_REMOVE        0    /* remove/unset property */
#define _NET_WM_STATE_ADD           1    /* add/set property */
#define _NET_WM_STATE_TOGGLE        2    /* toggle property  */

static void
send_wmspec_change_state (ClutterBackendGLX *backend_glx,
			  Window             window,
			  Atom               state,
			  gboolean           add)
{
  XClientMessageEvent xclient;

  memset (&xclient, 0, sizeof (xclient));

  xclient.type         = ClientMessage;
  xclient.window       = window;
  xclient.message_type = backend_glx->atom_WM_STATE;
  xclient.format       = 32;

  xclient.data.l[0] = add ? _NET_WM_STATE_ADD : _NET_WM_STATE_REMOVE;
  xclient.data.l[1] = state;
  xclient.data.l[2] = 0;
  xclient.data.l[3] = 0;
  xclient.data.l[4] = 0;

  XSendEvent (backend_glx->xdpy, 
	      DefaultRootWindow(backend_glx->xdpy), 
	      False,
              SubstructureRedirectMask|SubstructureNotifyMask,
              (XEvent *)&xclient);
}

static void
fix_window_size (ClutterStageGLX *stage_glx)
{
  gboolean resize;

  resize = clutter_stage_get_user_resizable (CLUTTER_STAGE (stage_glx));

  if (stage_glx->xwin != None && stage_glx->is_foreign_xwin == FALSE)
    {
      XSizeHints *size_hints;

      size_hints = XAllocSizeHints();

      if (!resize)
	{
	  size_hints->max_width  
	    = size_hints->min_width = stage_glx->xwin_width;
	  size_hints->max_height 
	    = size_hints->min_height = stage_glx->xwin_height;
	  size_hints->flags = PMinSize|PMaxSize;
	}

      XSetWMNormalHints (stage_glx->xdpy, stage_glx->xwin, size_hints);

      XFree(size_hints);
    }
}

static void
clutter_stage_glx_show (ClutterActor *actor)
{
  ClutterStageGLX *stage_glx = CLUTTER_STAGE_GLX (actor);

  /* Chain up to set mapped flags */
  CLUTTER_ACTOR_CLASS (clutter_stage_glx_parent_class)->show(actor);

  if (stage_glx->xwin)
    {
      /* Fire off a redraw to avoid flicker on first map.
       * Appears not to work perfectly on intel drivers at least.
      */
      clutter_redraw();
      XSync (stage_glx->xdpy, FALSE);
      XMapWindow (stage_glx->xdpy, stage_glx->xwin);
    }
}

static void
clutter_stage_glx_hide (ClutterActor *actor)
{
  ClutterStageGLX *stage_glx = CLUTTER_STAGE_GLX (actor);

  if (stage_glx->xwin)
    XUnmapWindow (stage_glx->xdpy, stage_glx->xwin);
}

static void
clutter_stage_glx_unrealize (ClutterActor *actor)
{
  ClutterStageGLX *stage_glx = CLUTTER_STAGE_GLX (actor);
  gboolean was_offscreen;

  CLUTTER_MARK();

  g_object_get (actor, "offscreen", &was_offscreen, NULL);

  clutter_glx_trap_x_errors ();

  if (G_UNLIKELY (was_offscreen))
    {
      if (stage_glx->glxpixmap)
	{
	  glXDestroyGLXPixmap (stage_glx->xdpy, stage_glx->glxpixmap);
	  stage_glx->glxpixmap = None;
	}

      if (stage_glx->xpixmap)
	{
	  XFreePixmap (stage_glx->xdpy, stage_glx->xpixmap);
	  stage_glx->xpixmap = None;
	}
    }
  else
    {
      if (!stage_glx->is_foreign_xwin && stage_glx->xwin != None)
	{
	  XDestroyWindow (stage_glx->xdpy, stage_glx->xwin);
	  stage_glx->xwin = None;
	}
      else
	stage_glx->xwin = None;
    }

  glXMakeCurrent (stage_glx->xdpy, None, NULL);
  if (stage_glx->gl_context != None)
    {
      glXDestroyContext (stage_glx->xdpy, stage_glx->gl_context);
      stage_glx->gl_context = None;
    }

  XSync (stage_glx->xdpy, False);

  clutter_glx_untrap_x_errors ();

  CLUTTER_MARK ();
}

static void
set_wm_protocols (Display *xdisplay,
                  Window   xwindow)
{
  Atom protocols[2];
  int n = 0;
  
  protocols[n++] = XInternAtom (xdisplay, "WM_DELETE_WINDOW", False);
  protocols[n++] = XInternAtom (xdisplay, "_NET_WM_PING", False);

  XSetWMProtocols (xdisplay, xwindow, protocols, n);
}

static void
clutter_stage_glx_realize (ClutterActor *actor)
{
  ClutterStageGLX *stage_glx = CLUTTER_STAGE_GLX (actor);
  gboolean is_offscreen;

  CLUTTER_NOTE (MISC, "Realizing main stage");

  g_object_get (actor, "offscreen", &is_offscreen, NULL);

  if (G_LIKELY (!is_offscreen))
    {
      int gl_attributes[] = 
	{
	  GLX_RGBA, 
	  GLX_DOUBLEBUFFER,
	  GLX_RED_SIZE, 1,
	  GLX_GREEN_SIZE, 1,
	  GLX_BLUE_SIZE, 1,
	  GLX_STENCIL_SIZE, 1,
	  0
	};

      if (stage_glx->xvisinfo)
	XFree (stage_glx->xvisinfo);

      if (stage_glx->xvisinfo == None)
	stage_glx->xvisinfo = glXChooseVisual (stage_glx->xdpy,
                                               stage_glx->xscreen,
					       gl_attributes);
      if (!stage_glx->xvisinfo)
	{
	  g_critical ("Unable to find suitable GL visual.");
	  CLUTTER_ACTOR_UNSET_FLAGS (actor, CLUTTER_ACTOR_REALIZED);
	  return;
	}

      if (stage_glx->xwin == None)
        {
 	XSetWindowAttributes xattr;
	unsigned long mask;
	
        CLUTTER_NOTE (MISC, "Creating stage X window");

	/* window attributes */  
	xattr.background_pixel = WhitePixel (stage_glx->xdpy,
					     stage_glx->xscreen);
	xattr.border_pixel = 0;
	xattr.colormap = XCreateColormap (stage_glx->xdpy, 
					  stage_glx->xwin_root,
					  stage_glx->xvisinfo->visual,
					  AllocNone);
	mask = CWBackPixel | CWBorderPixel | CWColormap;
	stage_glx->xwin = XCreateWindow (stage_glx->xdpy,
                                            stage_glx->xwin_root,
                                            0, 0,
                                            stage_glx->xwin_width,
                                            stage_glx->xwin_height,
                                            0,
                                            stage_glx->xvisinfo->depth,
                                            InputOutput,
                                            stage_glx->xvisinfo->visual,
                                            mask, &xattr);
        }
      
      CLUTTER_NOTE (MISC, "XSelectInput");
      XSelectInput (stage_glx->xdpy, stage_glx->xwin,
                    StructureNotifyMask |
		    FocusChangeMask |
                    ExposureMask |
		   /* FIXME: we may want to eplicity enable MotionMask */
		    PointerMotionMask |
		    KeyPressMask | KeyReleaseMask |
		    ButtonPressMask | ButtonReleaseMask |
		    PropertyChangeMask);

      /* no user resize.. */
      fix_window_size (stage_glx);

      set_wm_protocols (stage_glx->xdpy, stage_glx->xwin);

      if (stage_glx->gl_context)
	glXDestroyContext (stage_glx->xdpy, stage_glx->gl_context);

      CLUTTER_NOTE (GL, "Creating GL Context");
      stage_glx->gl_context = glXCreateContext (stage_glx->xdpy, 
					        stage_glx->xvisinfo, 
					        0, 
					        True);
      
      if (stage_glx->gl_context == None)
	{
	  g_critical ("Unable to create suitable GL context.");

	  CLUTTER_ACTOR_UNSET_FLAGS (actor, CLUTTER_ACTOR_REALIZED);
	  
          return;
	}

      CLUTTER_NOTE (GL, "glXMakeCurrent");
      glXMakeCurrent (stage_glx->xdpy, stage_glx->xwin, stage_glx->gl_context);
    }
  else
    {
      int gl_attributes[] = {
	GLX_DEPTH_SIZE,    0,
	GLX_ALPHA_SIZE,    0,
	GLX_RED_SIZE, 1,
	GLX_GREEN_SIZE, 1,
	GLX_BLUE_SIZE, 1,
	GLX_USE_GL,
	GLX_RGBA,
	0
      };

      if (stage_glx->xvisinfo)
	XFree (stage_glx->xvisinfo);

      CLUTTER_NOTE (GL, "glXChooseVisual");
      stage_glx->xvisinfo = glXChooseVisual (stage_glx->xdpy,
					     stage_glx->xscreen,
					     gl_attributes);
      if (!stage_glx->xvisinfo)
	{
	  g_critical ("Unable to find suitable GL visual.");
	  goto fail;
	}

      if (stage_glx->gl_context)
	glXDestroyContext (stage_glx->xdpy, stage_glx->gl_context);

      stage_glx->xpixmap = XCreatePixmap (stage_glx->xdpy,
				          stage_glx->xwin_root,
				          stage_glx->xwin_width, 
				          stage_glx->xwin_height,
					  DefaultDepth (stage_glx->xdpy,
							stage_glx->xscreen));

      stage_glx->glxpixmap = glXCreateGLXPixmap (stage_glx->xdpy,
					         stage_glx->xvisinfo,
					         stage_glx->xpixmap);
      
      /* indirect */
      stage_glx->gl_context = glXCreateContext (stage_glx->xdpy, 
					        stage_glx->xvisinfo, 
					        0, 
					        False);

      clutter_glx_trap_x_errors ();

      glXMakeCurrent (stage_glx->xdpy,
                      stage_glx->glxpixmap,
                      stage_glx->gl_context);

      if (clutter_glx_untrap_x_errors ())
	{
	  g_critical ("Unable to set up offscreen context.");
	  goto fail;
	}

#if 0
      /* Debug code for monitoring a off screen pixmap via window */
      {
	Colormap cmap;
	XSetWindowAttributes swa;

	cmap = XCreateColormap(clutter_glx_display(),
			       clutter_glx_root_window(), 
			       backend->xvisinfo->visual, AllocNone);

	/* create a window */
	swa.colormap = cmap; 

	foo_win = XCreateWindow(clutter_glx_display(),
				clutter_glx_root_window(), 
				0, 0, 
				backend->xwin_width, backend->xwin_height,
				0, 
				backend->xvisinfo->depth, 
				InputOutput, 
				backend->xvisinfo->visual,
				CWColormap, &swa);

	XMapWindow(clutter_glx_display(), foo_win);
      }
#endif

    }

  /* Make sure the viewport gets set up correctly */
  CLUTTER_SET_PRIVATE_FLAGS(actor, CLUTTER_ACTOR_SYNC_MATRICES);
  return;
  
 fail:

  /* For one reason or another we cant realize the stage.. */
  CLUTTER_ACTOR_UNSET_FLAGS (actor, CLUTTER_ACTOR_REALIZED);
  return;
}

static void
clutter_stage_glx_query_coords (ClutterActor        *self,
				ClutterActorBox     *box)
{
  ClutterStageGLX *stage_glx = CLUTTER_STAGE_GLX (self);

  box->x1 = box->y1 = 0;
  box->x2 = box->x1 + CLUTTER_UNITS_FROM_INT (stage_glx->xwin_width);
  box->y2 = box->y1 + CLUTTER_UNITS_FROM_INT (stage_glx->xwin_height);
}

static void
clutter_stage_glx_request_coords (ClutterActor        *self,
				  ClutterActorBox     *box)
{
  ClutterStageGLX *stage_glx = CLUTTER_STAGE_GLX (self);
  gint new_width, new_height;

  new_width  = ABS (CLUTTER_UNITS_TO_INT (box->x2 - box->x1));
  new_height = ABS (CLUTTER_UNITS_TO_INT (box->y2 - box->y1)); 

  if (new_width != stage_glx->xwin_width ||
      new_height != stage_glx->xwin_height)
    {
      stage_glx->xwin_width  = new_width;
      stage_glx->xwin_height = new_height;

      if (stage_glx->xwin != None)
	{
	  XResizeWindow (stage_glx->xdpy, 
			 stage_glx->xwin,
			 stage_glx->xwin_width,
			 stage_glx->xwin_height);

	  fix_window_size (stage_glx);
	}
      
      if (stage_glx->xpixmap != None)
	{
	  /* Need to recreate to resize */
	  clutter_actor_unrealize (self);
	  clutter_actor_realize (self);
	}

      CLUTTER_SET_PRIVATE_FLAGS(self, CLUTTER_ACTOR_SYNC_MATRICES);
    }

  if (stage_glx->xwin != None) /* Do we want to bother ? */
    XMoveWindow (stage_glx->xdpy,
		 stage_glx->xwin,
		 CLUTTER_UNITS_TO_INT (box->x1),
		 CLUTTER_UNITS_TO_INT (box->y1));

 
}

static void
clutter_stage_glx_set_fullscreen (ClutterStage *stage,
                                  gboolean      fullscreen)
{
  ClutterStageGLX *stage_glx = CLUTTER_STAGE_GLX (stage);
  ClutterBackendGLX *backend_glx = stage_glx->backend;

  static gboolean was_resizeable = FALSE;

  if (fullscreen)
    {
      if (stage_glx->xwin != None)
	{
	  if (!CLUTTER_ACTOR_IS_MAPPED(CLUTTER_ACTOR (stage_glx)))
	    {
	      gint width, height;

	      width  = DisplayWidth (stage_glx->xdpy, stage_glx->xscreen);
	      height = DisplayHeight (stage_glx->xdpy, stage_glx->xscreen);

	      clutter_actor_set_size (CLUTTER_ACTOR (stage_glx), 
				      width, height);
	      /* FIXME: This wont work if we support more states */
	      XChangeProperty 
		(stage_glx->xdpy,
		 stage_glx->xwin,
		 backend_glx->atom_WM_STATE, XA_ATOM, 32,
		 PropModeReplace,
		 (unsigned char *)&backend_glx->atom_WM_STATE_FULLSCREEN,
		 1);
	    }
	  else
	    {
	      /* We need to set window user resize-able for metacity at 
	       * at least to allow the window to fullscreen *sigh* 	 
	      */
	      if (clutter_stage_get_user_resizable (stage) == TRUE)
		was_resizeable = TRUE;
	      else
		clutter_stage_set_user_resizable (stage, TRUE);

	      send_wmspec_change_state(backend_glx,
				       stage_glx->xwin,
				       backend_glx->atom_WM_STATE_FULLSCREEN,
				       TRUE);
	    }
	}
    }
  else
    {
      if (stage_glx->xwin != None)
	{
	  if (!CLUTTER_ACTOR_IS_MAPPED(CLUTTER_ACTOR (stage_glx)))
	    {
	      /* FIXME: This wont work if we support more states */
	      XDeleteProperty (stage_glx->xdpy, 
			       stage_glx->xwin, 
			       backend_glx->atom_WM_STATE);
	    }
	  else
	    {
	      clutter_stage_set_user_resizable (stage, TRUE);

	      send_wmspec_change_state(backend_glx,
				       stage_glx->xwin,
				       backend_glx->atom_WM_STATE_FULLSCREEN,
				       FALSE);

	      /* reset the windows state - this isn't fun - see above */
	      if (!was_resizeable)
		clutter_stage_set_user_resizable (stage, FALSE);

	      was_resizeable = FALSE;
	    }
	}
    }

  CLUTTER_SET_PRIVATE_FLAGS(stage, CLUTTER_ACTOR_SYNC_MATRICES);
}

static void
clutter_stage_glx_set_cursor_visible (ClutterStage *stage,
                                      gboolean      show_cursor)
{
  ClutterStageGLX *stage_glx = CLUTTER_STAGE_GLX (stage);

  if (stage_glx->xwin == None)
    return;

  CLUTTER_NOTE (BACKEND, "setting cursor state ('%s') over stage window (%u)",
                show_cursor ? "visible" : "invisible",
                (unsigned int) stage_glx->xwin);

  if (show_cursor)
    {
#if 0 /* HAVE_XFIXES - borked on fiesty at least so disabled until further 
       *               investigation.	 
       */
      XFixesShowCursor (stage_glx->xdpy, stage_glx->xwin);
#else
      XUndefineCursor (stage_glx->xdpy, stage_glx->xwin);
#endif /* HAVE_XFIXES */
    }
  else
    {
#if 0 /* HAVE_XFIXES - borked */
      XFixesHideCursor (stage_glx->xdpy, stage_glx->xwin);
#else
      XColor col;
      Pixmap pix;
      Cursor curs;

      pix = XCreatePixmap (stage_glx->xdpy, stage_glx->xwin, 1, 1, 1);
      memset (&col, 0, sizeof (col));
      curs = XCreatePixmapCursor (stage_glx->xdpy, 
                                  pix, pix,
                                  &col, &col,
                                  1, 1);
      XFreePixmap (stage_glx->xdpy, pix);
      XDefineCursor (stage_glx->xdpy, stage_glx->xwin, curs);
#endif /* HAVE_XFIXES */
    }
}

static void
clutter_stage_glx_set_title (ClutterStage *stage,
			     const gchar  *title)
{
  ClutterStageGLX *stage_glx = CLUTTER_STAGE_GLX (stage);
  Atom             atom_NET_WM_NAME, atom_UTF8_STRING;

  if (stage_glx->xwin == None)
    return;

  /* FIXME: pre create these to avoid too many round trips */
  atom_NET_WM_NAME  = XInternAtom (stage_glx->xdpy, "_NET_WM_NAME", False);
  atom_UTF8_STRING  = XInternAtom (stage_glx->xdpy, "UTF8_STRING", False);

  if (title == NULL)
    {
      XDeleteProperty (stage_glx->xdpy, 
		       stage_glx->xwin, 
		       atom_NET_WM_NAME);
    }
  else
    {
      XChangeProperty (stage_glx->xdpy, 
		       stage_glx->xwin, 
		       atom_NET_WM_NAME, 
		       atom_UTF8_STRING, 
		       8, 
		       PropModeReplace, 
		       (unsigned char*)title, 
		       (int)strlen(title));
    }
}

static void
clutter_stage_glx_set_user_resize (ClutterStage *stage,
				   gboolean      value)
{
  ClutterStageGLX *stage_glx = CLUTTER_STAGE_GLX (stage);

  fix_window_size (stage_glx);
}

static void
clutter_stage_glx_set_offscreen (ClutterStage *stage,
                                 gboolean      offscreen)
{
  /* Do nothing ? */
}

static void
snapshot_pixbuf_free (guchar   *pixels,
		      gpointer  data)
{
  g_free (pixels);
}

static GdkPixbuf*
clutter_stage_glx_draw_to_pixbuf (ClutterStage *stage,
                                  gint          x,
                                  gint          y,
                                  gint          width,
                                  gint          height)
{
  guchar *data;
  GdkPixbuf *pixb;
  ClutterActor *actor;
  ClutterStageGLX *stage_glx;
  gboolean is_offscreen = FALSE;

  stage_glx = CLUTTER_STAGE_GLX (stage);
  actor = CLUTTER_ACTOR (stage);

  if (width < 0)
    width = clutter_actor_get_width (actor);

  if (height < 0)
    height = clutter_actor_get_height (actor);

  g_object_get (stage, "offscreen", &is_offscreen, NULL);

  if (G_UNLIKELY (is_offscreen))
    {
      gdk_pixbuf_xlib_init (stage_glx->xdpy, stage_glx->xscreen);

      pixb = gdk_pixbuf_xlib_get_from_drawable (NULL,
                                                (Drawable) stage_glx->xpixmap,
                                                DefaultColormap (stage_glx->xdpy,
                                                                 stage_glx->xscreen),
                                                stage_glx->xvisinfo->visual,
                                                x, y,
                                                0, 0,
                                                width, height);
    }
  else
    {
      GdkPixbuf *tmp = NULL, *tmp2 = NULL;
      gint stride;

      stride = ((width * 4 + 3) &~ 3);

      data = g_malloc0 (sizeof (guchar) * stride * height);

      glReadPixels (x, 
		    clutter_actor_get_height (actor) - y - height,
		    width, 
		    height, GL_RGBA, GL_UNSIGNED_BYTE, data);
      
      tmp = gdk_pixbuf_new_from_data (data,
				      GDK_COLORSPACE_RGB, 
				      TRUE, 
				      8, 
				      width, height,
				      stride,
				      snapshot_pixbuf_free,
				      NULL);
      
      tmp2 = gdk_pixbuf_flip (tmp, TRUE); 
      g_object_unref (tmp);

      pixb = gdk_pixbuf_rotate_simple (tmp2, GDK_PIXBUF_ROTATE_UPSIDEDOWN);
      g_object_unref (tmp2);
   }

  return pixb;
}

static void
clutter_stage_glx_dispose (GObject *gobject)
{
  ClutterStageGLX *stage_glx = CLUTTER_STAGE_GLX (gobject);

  if (stage_glx->xwin)
    clutter_actor_unrealize (CLUTTER_ACTOR (stage_glx));

  G_OBJECT_CLASS (clutter_stage_glx_parent_class)->dispose (gobject);
}

static void
clutter_stage_glx_class_init (ClutterStageGLXClass *klass)
{
  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
  ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
  ClutterStageClass *stage_class = CLUTTER_STAGE_CLASS (klass);

  gobject_class->dispose = clutter_stage_glx_dispose;
  
  actor_class->show = clutter_stage_glx_show;
  actor_class->hide = clutter_stage_glx_hide;
  actor_class->realize = clutter_stage_glx_realize;
  actor_class->unrealize = clutter_stage_glx_unrealize;
  actor_class->request_coords = clutter_stage_glx_request_coords;
  actor_class->query_coords = clutter_stage_glx_query_coords;
  
  stage_class->set_fullscreen = clutter_stage_glx_set_fullscreen;
  stage_class->set_cursor_visible = clutter_stage_glx_set_cursor_visible;
  stage_class->set_offscreen = clutter_stage_glx_set_offscreen;
  stage_class->draw_to_pixbuf = clutter_stage_glx_draw_to_pixbuf;
  stage_class->set_title = clutter_stage_glx_set_title;
  stage_class->set_user_resize = clutter_stage_glx_set_user_resize;
}

static void
clutter_stage_glx_init (ClutterStageGLX *stage)
{
  stage->xdpy = NULL;
  stage->xwin_root = None;
  stage->xscreen = 0;

  stage->xwin = None;
  stage->xwin_width = 640;
  stage->xwin_height = 480;
  stage->xvisinfo = None;

  stage->is_foreign_xwin = FALSE;

  CLUTTER_SET_PRIVATE_FLAGS(stage, CLUTTER_ACTOR_SYNC_MATRICES);
}

/**
 * clutter_glx_get_stage_window:
 * @stage: a #ClutterStage
 *
 * Gets the stages X Window. 
 *
 * Return value: An XID for the stage window.
 *
 * Since: 0.4
 */
Window
clutter_glx_get_stage_window (ClutterStage *stage)
{
  g_return_val_if_fail (CLUTTER_IS_STAGE_GLX (stage), None);

  return CLUTTER_STAGE_GLX (stage)->xwin;
}

/**
 * clutter_glx_get_stage_visual:
 * @stage: a #ClutterStage
 *
 * Returns the stage XVisualInfo
 *
 * Return value: The XVisualInfo for the stage.
 *
 * Since: 0.4
 */
XVisualInfo *
clutter_glx_get_stage_visual (ClutterStage *stage)
{
  g_return_val_if_fail (CLUTTER_IS_STAGE_GLX (stage), NULL);

  return CLUTTER_STAGE_GLX (stage)->xvisinfo;
}

/**
 * clutter_glx_set_stage_foreign:
 * @stage: a #ClutterStage
 * @xwindow: an existing X Window id
 *
 * Target the #ClutterStage to use an existing external X Window
 *
 * Return value: %TRUE if foreign window is valid
 *
 * Since: 0.4
 */
gboolean
clutter_glx_set_stage_foreign (ClutterStage *stage,
                               Window        xwindow)
{
  ClutterStageGLX *stage_glx;
  ClutterActor *actor;
  gint x, y;
  guint width, height, border, depth;
  Window root_return;
  Status status;
  ClutterGeometry geom;

  g_return_val_if_fail (CLUTTER_IS_STAGE_GLX (stage), FALSE);
  g_return_val_if_fail (xwindow != None, FALSE);

  stage_glx = CLUTTER_STAGE_GLX (stage);
  actor = CLUTTER_ACTOR (stage);

  clutter_glx_trap_x_errors ();

  status = XGetGeometry (stage_glx->xdpy,
                         xwindow,
                         &root_return,
                         &x, &y,
                         &width, &height,
                         &border,
                         &depth);
  
  if (clutter_glx_untrap_x_errors () ||
      !status ||
      width == 0 || height == 0 ||
      depth != stage_glx->xvisinfo->depth)
    {
      return FALSE;
    }

  clutter_actor_unrealize (actor);

  stage_glx->xwin = xwindow;
  stage_glx->is_foreign_xwin = TRUE;

  geom.x = x;
  geom.y = y;
  geom.width = stage_glx->xwin_width = width;
  geom.height = stage_glx->xwin_height = height;

  clutter_actor_set_geometry (actor, &geom);
  clutter_actor_realize (actor);

  return TRUE;
}