/*
 * Copyright (C) 1997-2009, R3vis Corporation.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library 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
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library 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, or visit http://www.gnu.org/copyleft/lgpl.html.
 *
 * Original Contributor:
 *   Wes Bethel, R3vis Corporation, Marin County, California
 *   http://www.r3vis.com/
 * Additional Contributor(s):
 *
 * The OpenRM project is located at http://openrm.sourceforge.net/.
 */
/*
 * $Id: rmcreatw.c,v 1.11 2008/09/22 01:48:58 wes Exp $
 * Version: $Name: v180-alpha-02 $
 * $Revision: 1.11 $
 * $Log: rmcreatw.c,v $
 * Revision 1.11  2008/09/22 01:48:58  wes
 * Improve error msg when no GLX extension found during window creation
 *
 * Revision 1.10  2005/06/08 18:33:02  wes
 * Code cleanups to eliminate compiler warnings.
 *
 * Revision 1.9  2005/05/16 01:05:46  wes
 * Streamlining through code refactoring.
 *
 * Revision 1.8  2005/02/19 16:08:45  wes
 * Distro sync and consolidation.
 *
 * Revision 1.7  2005/01/23 17:11:49  wes
 * Copyright update to 2005.
 *
 * Revision 1.6  2004/01/17 04:07:53  wes
 * Updated copyright line for 2004.
 *
 * Revision 1.5  2003/10/03 19:17:47  wes
 * Use rmPipeSet/GetContext interface to interact with the RMpipe's
 * OpenGL context rather than using platform-specific interfaces.
 *
 * Revision 1.4  2003/07/23 13:32:32  wes
 * Win32: problems with offscreen rendering appeared with new context
 * initialization code sequence (1.5.0). Minor repairs needed to fix the bug.
 *
 * Revision 1.3  2003/02/02 02:07:18  wes
 * Updated copyright to 2003.
 *
 * Revision 1.2  2003/02/01 17:56:16  wes
 * Win32 code work to reflect new RMpipe initialization sequence.
 *
 * Revision 1.1.1.1  2003/01/28 02:15:23  wes
 * Manual rebuild of rm150 repository.
 *
 * Revision 1.8  2003/01/27 05:04:42  wes
 * Changes to RMpipe API and initialization sequence to unify GLX, WGL and CR
 * platforms w/o too much disruption to existing apps.
 *
 * Revision 1.7  2003/01/16 22:21:18  wes
 * Updated all source files to reflect new organization of header files:
 * all header files formerly located in include/rmaux, include/rmi, include/rmv
 * are now located in include/rm.
 *
 * Revision 1.6  2002/04/30 19:38:26  wes
 * Updated copyright dates.
 *
 * Revision 1.5  2001/06/03 20:53:22  wes
 * Added new routine to create windows under X11 to replace some older
 * code that seemed to have problems on some systems.
 *
 * Revision 1.4  2001/03/31 18:46:50  wes
 * Added inline doc for rmauxCreateOffscreenDrawable.
 *
 * Revision 1.3  2001/03/31 17:09:31  wes
 * v1.4.0-alpha2 checkin.
 *
 * Revision 1.2  2000/04/17 00:06:51  wes
 * Numerous documentation updates and code reorganization courtesy of jdb.
 *
 * Revision 1.1.1.1  2000/02/28 21:29:40  wes
 * OpenRM 1.2 Checkin
 *
 * Revision 1.1.1.1  2000/02/28 17:18:48  wes
 * Initial entry - pre-RM120 release, source base for OpenRM 1.2.
 *
 */

#include <rm/rm.h>
#include <rm/rmaux.h>
#include "../rm/rmprivat.h"

#ifdef RM_WIN
/*
 * ----------------------------------------------------
 * @Name rmauxCreateW32Window
 @pstart
 HWND rmauxCreateW32Window (const RMpipe *useMe,	
		            HWND parent,
			    int xpos,
			    int ypos,
			    int width,
			    int height,
			    char *wtitle,
			    HINSTANCE hInstance,
			    void *event_loop_fptr)
 @pend

 @astart
 const RMpipe *useMe - a handle to an initialized RMpipe object.

 HWND parent - the Win32 window handle to the parent window. NULL is
    OK.

 int xpos, ypos - integer values specifying the (x,y) pixel coordinate
    of the new window.

 int width, height - integer values specifying the pixel width and
    height of the new window.

 char *wtitle - a character string used as the title of the
    window. This string will appear in the title bar painted around
    your window by Wxx.
     

 HINSTANCE hInstance - an HINSTANCE handle. this handle is provided to
    your WinMain routine by Windows.

 void *event_loop_fptr - A function pointer, cast to a (void *), of
    your WndProc (event loop). 
 @aend

 @dstart

 Creates a window suitable for rendering by OpenRM on the Win32
 platform.  Returns a valid window handle upon success, or NULL (zero)
 upon failure.  Strictly speaking, the window is not yet ready for
 drawing upon conclusion of this routine due to how Win32 works.

 All Win32 developers must use an "init function" that is called once
 after the WM_CREATE message has been posted and processed. The Win32
 version of rmauxEventLoop() should be used as a guide.
 
 @dend
 * ----------------------------------------------------
 */
HWND
rmauxCreateW32Window (RMpipe *useMe,
		      HWND parent,
		      int xpos,
		      int ypos,
		      int width,
		      int height,
		      char *wtitle,
		      HINSTANCE hInstance,
		      void *event_loop_fptr)
{
    static char szAppName[] = "RM-OpenGL";
    WNDCLASS    wc;
    HWND        hWnd;
    HDC         hDC;
    HWND        parentWindow;

    memset((void *)&wc, 0, sizeof(WNDCLASS));

    wc.style = (CS_HREDRAW | CS_VREDRAW); 		/* class styles */
    wc.lpfnWndProc = (WNDPROC)(event_loop_fptr);	/* event loop function */

    /* no per-class or per-window data */
    wc.cbClsExtra = 0;
    wc.cbWndExtra = 0;

    wc.hInstance = hInstance;				/* owner of this class */
    wc.hIcon = NULL;					/* icon name  */
    wc.hCursor = LoadCursor(NULL, IDC_ARROW); 		/* arrow cursor */
    wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);	/* efault color */
    wc.lpszMenuName  = NULL;  				/* menu from RC */
    wc.lpszClassName = szAppName;            		/* regististry name */

    /* register the window class */
    RegisterClass(&wc);

    if (parent == NULL)
	parentWindow = NULL;
    else
	parentWindow = parent;
  
    /* 
     * Create a main window for this application instance
     * give app name, window title, various flags & geometry, 
     * specify no class menu, bind window instance and pass 
     * in a handle to the RMpipe in use 
     */
    hWnd = CreateWindow(szAppName, wtitle, (WS_OVERLAPPEDWINDOW  | WS_CLIPCHILDREN | WS_CLIPSIBLINGS), xpos, ypos, width, height, parentWindow, NULL, hInstance, (void *)useMe);

    /*  if window could not be created, return zero */
    if (!hWnd)
       return(0);

    /*
    ** Set up for OpenGL rendering.  Bind the rendering context to
    ** the same device context that the palette will be selected into.
    */

    rmPipeSetWindow(useMe, hWnd, width, height);

    if (rmPipeCreateContext(useMe) == RM_WHACKED)
	rmWarning("Unable to create a suitable WGL context. ");

    return(hWnd);
}


/*
 * ----------------------------------------------------
 * @Name rmauxCreateOffscreenDrawable
 @pstart
 HWND rmauxCreateOffscreenDrawable (const RMpipe *pipe,
		                    int width,
			            int height,
			            int depth,
				    HINSTANCE hCurrentInst,
				    void *eventLoopFuncPtr)

 @pend

 @astart
 const RMpipe *pipe - a handle to an RMpipe object (input).
 
 int width, height - integer values that specify the pixel dimensions
    of the new offscreen drawable.
    
 int depth - integer value that specifies the pixel depth of the new
    drawable. This value is used for both color planes and depth buffer.

 HINSTANCE hInstance - an HINSTANCE handle. this handle is provided to
    your WinMain routine by Windows.

 void *event_loop_fptr - A function pointer, cast to a (void *), of
    your WndProc (event loop). 
 @aend

 @dstart
 Use this routine to create an offscreen drawable object in OpenRM.
 You can use this drawable just like a normal on-screen window, but
 the window is never mapped. On most systems, it is permissible to have
 the offscreen drawable be much larger than the actual dimensions of
 the physical display device.

 Upon success, this routine returns a non-zero drawable handle.

 Win32 notes (v1.4.0-alpha-2): it may be permissible to pass in a value
 of NULL for the eventLoopFuncPtr - we haven't tried that. Refer to the
 OpenRM demonstration program offscreen.c - we pass in rmauxWndProc as
 the "WndProc." However, because the window is never mapped, the WndProc
 is never invoked.

 @dend
 * ----------------------------------------------------
 */
HWND
rmauxCreateOffscreenDrawable (RMpipe *pipe,
			      int width,
			      int height,
			      int depth,
			      HINSTANCE hCurrentInst,
			      void *eventLoopFuncPtr)
{
    WNDCLASS wndClass;
    HWND hWnd;
    HDC hDC;
    HDC hDCFrontBuffer;
    char className[] = "OpenGL";

    /* Define and register a window class */
    memset((void *)&wndClass,0,sizeof(WNDCLASS));
    wndClass.style = CS_HREDRAW | CS_VREDRAW;
    wndClass.lpfnWndProc = (WNDPROC)(eventLoopFuncPtr); /* event loop function */
/*    wndClass.lpfnWndProc = WndProc; */
    wndClass.cbClsExtra = 0;
    wndClass.cbWndExtra = 0;
    wndClass.hInstance = hCurrentInst;
    wndClass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
    wndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
    wndClass.hbrBackground = GetStockObject(WHITE_BRUSH);
    wndClass.lpszMenuName = NULL;
    wndClass.lpszClassName = className,
    RegisterClass(&wndClass);
    
    hWnd = CreateWindow(
			className,		/* Window class's name */
	"OpenRM Offscreen",		/* Title bar text */
	WS_OVERLAPPEDWINDOW |	/* The window's style */
	WS_CLIPCHILDREN |
	WS_CLIPSIBLINGS,
	0, 0,		/* Position */
	width, height,	/* Size */
	NULL,			/* Parent window's handle */
	NULL,			/* Menu handle */
	hCurrentInst,		/* Instance handle */
	NULL);			/* No additional data */

    if (hWnd != NULL)
    {
	rmPipeSetWindow(pipe, hWnd, width, height);
	if (pipe->createContextFunc(pipe) == RM_WHACKED)
	    rmWarning("Unable to create a suitable WGL context. ");
    }
    
    return(hWnd);
}

#endif /* RM_WIN */


#ifdef RM_X

#if 1
Window
private_rmauxCreateXWindow(Display *dpy, 
			   Window parent, 
			   int x, int y, 
			   int width, int height,
			   XVisualInfo *vinfo,
			   RMenum managed,
			   const char *winTitle,
			   const char *iconTitle)
{
    XSetWindowAttributes watt;
    XWindowAttributes att;
    Window window;
    unsigned long mask = CWBackPixel | CWBorderPixel | CWColormap | CWOverrideRedirect;
    int screen = DefaultScreen( dpy );

    /* Query for GLX extension */
    int erb, evb;
    if( !glXQueryExtension( dpy, &erb, &evb) )
    {
        fprintf( stderr, "private_rmauxCreateXWindow() : Display \"%s\" has no GLX extension.\n", 
                                        XDisplayName( "" ) );
        XCloseDisplay( dpy );
        exit(1);
    }

    XGetWindowAttributes( dpy, parent, &att );

    /* Create a window, including Colormap */
    watt.colormap = XCreateColormap( dpy, RootWindow( dpy, screen ), 
					    vinfo->visual, AllocNone);

/*    watt.background_pixel = 0; */
    watt.border_pixel = 0;
    watt.override_redirect = False;
    watt.override_redirect = (managed == RM_TRUE) ? False : True;
    watt.event_mask = RM_AUX_XWINDOW_EVENT_MASK;
    
/*    watt.event_mask = ExposureMask | ButtonPressMask | PointerMotionMask | StructureNotifyMask; */
    window = XCreateWindow( dpy, parent, x, y, width, height, 0, 
                            vinfo->depth, InputOutput, vinfo->visual, mask, &watt );

    XSetStandardProperties(dpy, window, winTitle, iconTitle, None, NULL, 0, NULL);
    XMapWindow(dpy, window);
    XMoveWindow(dpy, window, x, y);

    XSetWMColormapWindows( dpy, window, &window, 1 ); 

    XSync(dpy, False);
    XFlush( dpy );

    return window;
}
#endif

/*
 * ----------------------------------------------------
 * @Name rmauxCreateXWindow
 @pstart
 Window rmauxCreateXWindow (RMpipe *pipe,
		            Window parent,
			    int xPosition,
			    int yPosition,
			    int width,
			    int height,
			    const char *winTitle,
			    const char *iconTitle,
			    RMenum managed)

 @pend

 @astart

 RMpipe *pipe - a handle to an RMpipe (input, possibly modified).

 Window parent - an X11 Window handle to the parent window (NULL is OK).

 int xPosition, yPosition - integer values specifying the pixel
    location of the upper left corner of the new window.

 int width, height - integer values specifying the pixel width and
    height of the new window.

 const char *winTitle, *iconTitle - character strings containing the
    window title and icon title, respectively. The ultimate use of
    these strings is implementation and window-system specific.

 RMenum managed - an RMenum value indicating whether or not this
    window will be managed by the window manager. When not managed,
    the window will be drawn with no decorations, title bars, etc. and
    all window events will bypass the window manager and go directly
    to the window.  RM_TRUE or RM_FALSE. Unmanaged windows are useful
    in VR applications. 
 @aend

 @dstart

 This routine creates an X11 window suitable for use by OpenRM for
 drawing, and returns a handle to the caller. Prior to calling this
 routine, the RMpipe must be initialized (see rmInitPipe).

 Returns zero upon failure, or a Window handle upon success.

 During processing, this routine will check the input RMpipe for the
 presence of an OpenGL context and an XVisual. If both are not present,
 a suitable OpenGL context will be created, along with an appropriate
 XVisual structure. The XVisual structure is needed to ensure the new
 window that is created will be suitable for OpenGL rendering use.

 See also XCreateWindow (X11R6).
 
 @dend
 * ----------------------------------------------------
 */
Window
rmauxCreateXWindow (RMpipe *pipe,
		    Window parent,
		    int x,
		    int y,
		    int width,
		    int height,
		    const char *win_title,
		    const char *icon_title,
		    RMenum managed)
{
    Window  win;

    if (rmxPipeGetDisplay(pipe) == NULL)
    {
	rmError("rmauxCreateXWindow error: before calling this routine, you must first assign an XDisplay to the RMpipe using rmxPipeSetDisplay.");
	return 0;
    }

    if (parent == 0)
	parent = RootWindow(rmxPipeGetDisplay(pipe),
			    DefaultScreen(rmxPipeGetDisplay(pipe)));

    /*
     * if either the context or the visual are null, create a new
     * openGL context.
     */
    if ((rmPipeGetContext(pipe) == (GLXContext)0) ||
	(rmxPipeGetVisual(pipe) == NULL))
    {
	if (rmPipeCreateContext(pipe) == RM_WHACKED)
	{
	    rmError("rmauxCreateXWindow fails.");
	    return RM_WHACKED;
	}
    }

    /*
     * else, use the existing OpenGL context & visual. If the app has
     * made a mistake and assigned an invalid XVisual, the create window
     * call will fail, causing confusion.
     */
    
    win = private_rmauxCreateXWindow(rmxPipeGetDisplay(pipe),
				     parent, x, y, width, height,
				     rmxPipeGetVisual(pipe),
				     managed,
				     win_title,
				     icon_title);
    return(win);
}

/*
 * ----------------------------------------------------
 * @Name rmauxCreateOffscreenDrawable
 @pstart
 GLXPixmap rmauxCreateOffscreenDrawable (const RMpipe *pipe,
			                 int width,
			                 int height,
			                 int depth)

 @pend

 @astart
 const RMpipe *pipe - a handle to an RMpipe object (input).
 int width, height - integer values that specify the pixel dimensions
    of the new offscreen drawable.
 int depth - integer value that specifies the pixel depth of the new
    drawable. This value is used for both color planes and depth buffer.
 @aend

 @dstart
 Use this routine to create an offscreen drawable object in OpenRM.
 You can use this drawable just like a normal on-screen window, but
 the window is never mapped. On most systems, it is permissible to have
 the offscreen drawable be much larger than the actual dimensions of
 the physical display device.

 Upon success, this routine returns a non-zero drawable handle.

 @dend
 * ----------------------------------------------------
 */
GLXPixmap
rmauxCreateOffscreenDrawable (RMpipe *pipe,
			      int width,
			      int height,
			      int depth)
{
    Pixmap    p;
    GLXPixmap glxp;
    Display *d = rmxPipeGetDisplay(pipe);
    XVisualInfo         *visual;

    /*
     * if either the context or the visual are null, create a new
     * openGL context.
     */
    if ((rmPipeGetContext(pipe) == (GLXContext)0) ||
	(rmxPipeGetVisual(pipe) == NULL))
    {
	if (rmPipeCreateContext(pipe) == RM_WHACKED)
	{
	    rmError("rmauxCreateXWindow fails.");
	    return RM_WHACKED;
	}
    }

    visual = rmxPipeGetVisual(pipe);
    
    p = XCreatePixmap(d, RootWindow(d, visual->screen),
		      width, height, visual->depth);

    glxp = glXCreateGLXPixmap(d, visual, p);

    /* foil compiler warning */
    depth = 0;
    
    return(glxp);
}

#endif /* RM_X */

/* EOF */
