const Tk_ClassProcs C3dCanvasProcs = {
    sizeof(Tk_ClassProcs),	/* size */
    C3d_Canvas_WorldChanged,	/* worldChangedProc */
    C3d_Native_CreateCanvas3d,  /* createProc */
    NULL		        /* modalProc */
};

static void
C3d_Native_ResizeContext(C3dWidget *pCanvas,int w,int h)
{
    Window xwin = Tk_WindowId(pCanvas->tkwin);
    XResizeWindow(Tk_Display(pCanvas->tkwin), xwin, w, h);
    pCanvas->options.width = w;
    pCanvas->options.height = h;
}

/*
 * cd3SDLMain.c --
 *
 *     This file contains the implementation of the Tcl interface of the
 *     canvas3d widget in SDL environments.
 *
 *     This file is called by an include directive in c3dmain.c
 *-------------------------------------------------------------------------
 */
static void
C3d_Native_DeleteContext(C3dWidget *pCanvas)
{
    if ((pCanvas->tkwin != NULL) && (pCanvas->context != NULL)) {
	SdlTkGLXDestroyContext(Tk_Display(pCanvas->tkwin),
			       Tk_WindowId(pCanvas->tkwin),
			       pCanvas->context);
	pCanvas->context = NULL;
    }
}

/*
 *---------------------------------------------------------------------------
 *
 * C3d_Native_FreePixMaps --
 *
 *     This function frees all currently allocated pixmaps used by the
 *     -saveunder option. This function is called in the following three
 *     circumstances:
 *
 *         * When the widget is being deleted,
 *         * When the -saveunder mode changes,
 *         * When the window size changes.
 *
 *     The pixmaps are allocated within C3d_Native_Select3dContext().
 *
 * Results:
 *     None.
 *
 * Side effects:
 *     None.
 *
 *---------------------------------------------------------------------------
 */
static void
C3d_Native_FreePixMaps(C3dWidget *pCanvas)
{
    pCanvas->pixmapheight = 0;
    pCanvas->pixmapwidth = 0;
    if (pCanvas->pixmap && pCanvas->tkwin) {
        C3dFreePixmap(Tk_Display(pCanvas->tkwin), pCanvas->pixmap);
        pCanvas->pixmap = 0;
    }
}

/*
 *---------------------------------------------------------------------------
 *
 * C3d_Native_CreateCanvas3d --
 *
 *     This function is invoked when Tk_MakeWindowExist() is called to
 *     create the 3d-canvas window (it is registered using
 *     Tk_SetClassProcs() in function C3d_CanvasObjCmd()). This is where we do
 *     platform specific OpenGL initialisation.
 *
 * Results:
 *     X-window Window identifier to be wrapped into a Tk_Window by Tk.
 *
 * Side effects:
 *     None.
 *
 *---------------------------------------------------------------------------
 */
static Window
C3d_Native_CreateCanvas3d(
    Tk_Window tkwin,
    Window parent,                /* X-windows wrapper around parent HWND */
    ClientData clientData
) {
    /* The rest of this function is the X11 version. */
    C3dWidget *pCanvas = (C3dWidget *)clientData;
    Display *pDisplay = Tk_Display(tkwin); /* Application display */
    int screen = Tk_ScreenNumber(tkwin);   /* Application screen number */
    Window xwindow;                      /* X-window created (return value) */
    int width;                 /* Width of created window in pixels */
    int height;                /* Height of created window in pixels */
    XSetWindowAttributes swa;

    swa.border_pixel = 0;
    swa.event_mask = ALL_EVENTS_MASK;

    /* Create the X-window. Use the width and height specified as widget
     * options to size the window.
     */
    width = pCanvas->options.width;
    height = pCanvas->options.height;
    xwindow = XCreateWindow(
        pDisplay,              /* Display to create new window for */
        parent,                /* Parent X-window */
        0, 0,                  /* Initial X and Y coords of new window */
        width, height,         /* Initial width and height of new window */
        0,                     /* Border width */
        DefaultDepth(pDisplay, screen),            /* Depth of window */
        InputOutput,           /* Type of window */
        DefaultVisual(pDisplay, screen),           /* Visual class */
        CWBorderPixel | CWColormap | CWEventMask,
        &swa                                       /* Window attributes */
    );

    /* Create an Open-GL rendering context to use. */
    pCanvas->context = SdlTkGLXCreateContext(pDisplay, xwindow, tkwin);
    return xwindow;
}

/*
 *---------------------------------------------------------------------------
 *
 * C3d_Native_Select3dContext --
 *
 *     This function selects the correct OpenGL context to draw the 3d scene
 *     using either glXMakeCurrent() or wglMakeCurrent(), depending on the
 *     platform (X11 or win32).
 *
 *     The context selected draws either to the back-buffer of the
 *     double-buffered window, or to a platform specific bitmap (a Pixmap for
 *     X11, a DIB for win32).
 *
 *     If required, this function may allocate and/or deallocate bitmaps.
 *
 * Results:
 *     None.
 *
 * Side effects:
 *     None.
 *
 *---------------------------------------------------------------------------
 */
static void
C3d_Native_Select3dContext(C3dWidget *pCanvas)
{
    Tk_Window win = pCanvas->tkwin;
    int saveunder = pCanvas->options.saveunder;
    int w = Tk_Width(win);
    int h = Tk_Height(win);

    /* If the -saveunder mode is not "none", then we need at least
     * C3dWidget.pixmap.
     */
    if (saveunder != SAVEUNDER_NONE && !pCanvas->pixmap) {
        Pixmap pixmap;
        pixmap = C3dGetPixmap(Tk_Display(win), Tk_WindowId(win),
			      w, h, Tk_Depth(win));
        assert(pixmap);
        pCanvas->pixmap = pixmap;
        pCanvas->pixmapwidth = w;
        pCanvas->pixmapheight = h;
    }
    assert(saveunder == SAVEUNDER_NONE || w == pCanvas->pixmapwidth);
    assert(saveunder == SAVEUNDER_NONE || h == pCanvas->pixmapheight);

    SdlTkGLXMakeCurrent(Tk_Display(win), Tk_WindowId(win), pCanvas->context);
    pCanvas->options.width = w;
    pCanvas->options.height = h;
    glViewport(0, 0, w, h);
}

/*
 *---------------------------------------------------------------------------
 *
 * C3d_Native_Release3dContext --
 *
 *     This function releases the current OpenGL context.
 *
 * Results:
 *     None.
 *
 * Side effects:
 *     None.
 *
 *---------------------------------------------------------------------------
 */
static void
C3d_Native_Release3dContext(C3dWidget *pCanvas)
{
    Tk_Window win = pCanvas->tkwin;

    SdlTkGLXReleaseCurrent(Tk_Display(win), Tk_WindowId(win), pCanvas->context);
}

/*
 *---------------------------------------------------------------------------
 *
 * C3d_Native_Pixmap3dToPixmap --
 *
 *     Copy from C3dPixmap.pixmap to C3dPixmap.pixmap3d.
 *
 * Results:
 *     None.
 *
 * Side effects:
 *     None.
 *
 *---------------------------------------------------------------------------
 */
static void
C3d_Native_Pixmap3dToPixmap(C3dWidget *pCanvas)
{
}

/*
 *---------------------------------------------------------------------------
 *
 * C3d_Native_PixmapToPixmap3d --
 *
 *     Copy from C3dPixmap.pixmap3d to C3dPixmap.pixmap.
 *
 * Results:
 *     None.
 *
 * Side effects:
 *     None.
 *
 *---------------------------------------------------------------------------
 */
static void
C3d_Native_PixmapToPixmap3d(C3dWidget *pCanvas)
{
}

/*
 *---------------------------------------------------------------------------
 *
 * C3d_Native_PixmapToWindow --
 *
 *     If the -saveunder mode is not "none", copy from the pixmap
 *     C3dWidget.pixmap to the main window. If it is "none", then swap the
 *     OpenGL windows buffers.
 *
 * Results:
 *     None.
 *
 * Side effects:
 *     None.
 *
 *---------------------------------------------------------------------------
 */
static void
C3d_Native_PixmapToWindow(C3dWidget *pCanvas)
{
    glFlush();
    SdlTkGLXSwapBuffers(Tk_Display(pCanvas->tkwin),
			Tk_WindowId(pCanvas->tkwin));
}

/*
 *---------------------------------------------------------------------------
 *
 * C3d_Native_EventProc --
 *
 *     Widget callback for <Expose> events. In this case we need to redraw
 *     the scene. We don't do the drawing immediately, instead
 *     C3dDrawWhenIdle() is invoked.
 *
 * Results:
 *     None.
 *
 * Side effects:
 *     None.
 *
 *---------------------------------------------------------------------------
 */
static void
C3d_Native_EventProc(ClientData clientData, XEvent *eventPtr)
{
    C3dWidget *pCanvas = (C3dWidget *)clientData;
    if (eventPtr->type == MapNotify) {
        C3dDrawWhenIdle(pCanvas, STATE_REDRAW_ALL);
    }
}

static int
C3d_Native_Init(Tcl_Interp *interp)
{
    Tk_Window tkwin = Tk_MainWindow(interp);

    if (0) {
	/* Squelch compiler warning */
	C3d_Native_ResizeContext(NULL, 0, 0);
    }
    if (0) {
	/* Squelch compiler warning */
	C3d_Native_Pixmap3dToPixmap(NULL);
    }
    if ((tkwin == NULL) || !SdlTkGLXAvailable(Tk_Display(tkwin))) {
	Tcl_SetResult(interp, "no OpenGL available", TCL_STATIC);
	return TCL_ERROR;
    }
    return TCL_OK;
}
