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;
}

/*
 * cd3CarbonMain.c --
 *
 *     This file contains the implementation of the Tcl interface of the
 *     canvas3d widget on Macos X running Carbon.
 *
 *     This file is called by an include directive in c3dmain.c
 *-------------------------------------------------------------------------
 */

static void C3d_Native_DeleteContext(C3dWidget *pCanvas) {
  aglDestroyContext(pCanvas->context);
}


/*
 *---------------------------------------------------------------------------
 *
 * 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(pCanvas)
    C3dWidget *pCanvas;
{
    pCanvas->pixmapheight = 0;
    pCanvas->pixmapwidth = 0;
    if (pCanvas->pixmap) {
        C3dFreePixmap(Tk_Display(pCanvas->tkwin), pCanvas->pixmap);
        pCanvas->pixmap = 0;
    }
    if (pCanvas->context) {
        aglDestroyContext(pCanvas->context);
        pCanvas->context = 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(tkwin, parent, clientData)
    Tk_Window tkwin; 
    Window parent;                /* X-windows wrapper around parent HWND */
    ClientData clientData;
{
    /*
    ** Here we initialize for Apple AGL
    */
    GLint   _attribs[32], attribs[] = {
	AGL_RGBA, AGL_DOUBLEBUFFER, AGL_DEPTH_SIZE, 24, AGL_NONE
    };
    GLint swapInterval = 1;
    int     na, width, height;
    AGLPixelFormat fmt;
    Window window;
    Display *pDisplay = Tk_Display(pCanvas->tkwin); /* Application display */
    TkWindow *winPtr = (TkWindow *) pCanvas->tkwin;
    Colormap cmap;

    //cmap = DefaultColormap(pDisplay, DefaultScreen(pDisplay));
    cmap = Tk_Colormap(pCanvas->tkwin);

    /* 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;
    window = TkpMakeWindow(winPtr, parent);

    (void) XMapWindow(pDisplay, window);

    /* Need to do this after mapping window, so MacDrawable structure
     * is more completely filled in */
    na = 0;
    attribs[na++] = AGL_MINIMUM_POLICY;
    //attribs[na++] = AGL_ROBUST;
#if 1
    attribs[na++] = AGL_RGBA;
#if 0
    attribs[na++] = AGL_RED_SIZE;
    attribs[na++] = 1;
    attribs[na++] = AGL_GREEN_SIZE;
    attribs[na++] = 1;
    attribs[na++] = AGL_BLUE_SIZE;
    attribs[na++] = 1;
//    attribs[na++] = AGL_ALPHA_SIZE;
//    attribs[na++] = 1;
#endif
#else
    attribs[na++] = AGL_BUFFER_SIZE;
    attribs[na++] = 8;
#endif
    //attribs[na++] = AGL_DOUBLEBUFFER;
    attribs[na++] = AGL_DEPTH_SIZE;
    attribs[na++] = 24;
    attribs[na++] = AGL_NONE;

    if ((fmt = aglChoosePixelFormat(NULL, 0, attribs)) == NULL) {
        Tcl_SetResult(interp, "canvas3d: couldn't choose pixel format",
                TCL_STATIC);
        return 0;
    }

    /* Create an Open-GL rendering context to use. */
    pCanvas->context = aglCreateContext(fmt, NULL);
    if (pCanvas->context == NULL) {
	Tcl_SetResult(interp, "couldn't create context", TCL_STATIC);
	return 0;
    }
    aglDestroyPixelFormat(fmt);
    if (!aglSetDrawable(pCanvas->context,
		    TkMacOSXGetDrawablePort(window)
		    //((MacDrawable *) (window))->toplevel->grafPtr
		)) {
	aglDestroyContext(pCanvas->context);
	Tcl_SetResult(interp, "couldn't set drawable", TCL_STATIC);
	return 0;
    }
    aglSetCurrentContext(pCanvas->context);
    aglSetInteger(pCanvas->context, AGL_SWAP_INTERVAL, &swapInterval);

    Tk_SetWindowVisual(pCanvas->tkwin,
	    DefaultVisual(pDisplay, DefaultScreen(pDisplay)), 32,
	    cmap);

    return window;
}


/*
 *---------------------------------------------------------------------------
 *
 * 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(pCanvas)
    C3dWidget *pCanvas;
{
    Tk_Window win = pCanvas->tkwin;
    Display *dpy = Tk_Display(win);
    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(dpy, Tk_WindowId(win), w, h, Tk_Depth(win));
        assert(pixmap);
        pCanvas->pixmap = pixmap;
        pCanvas->pixmapwidth = w;
        pCanvas->pixmapheight = h;
    }
    glViewport(0, 0, w, h);

    assert(saveunder == SAVEUNDER_NONE || w == pCanvas->pixmapwidth);
    assert(saveunder == SAVEUNDER_NONE || h == pCanvas->pixmapheight);

    /* If the -saveunder mode is "3d", then we also need C3dWidget.pixmap3d */
    if (saveunder == SAVEUNDER_3D && !pCanvas->pixmap3d) {
        /* Noop.  saveunder is not yet supported on AGL */
    }

    /* Finally, select the appropriate context. */
    aglSetCurrentContext(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(pCanvas)
    C3dWidget *pCanvas;
{
}


/*
 *---------------------------------------------------------------------------
 *
 * C3d_Native_Pixmap3dToPixmap --
 *
 *     Copy from C3dPixmap.pixmap to C3dPixmap.pixmap3d.
 *
 * Results:
 *     None.
 *
 * Side effects:
 *     None.
 *
 *---------------------------------------------------------------------------
 */
static void C3d_Native_Pixmap3dToPixmap(pCanvas)
    C3dWidget *pCanvas;
{
    int w = pCanvas->pixmapwidth;
    int h = pCanvas->pixmapheight;

    w = h; /* lint */
    assert(!"cannot happen");
}


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

/* tell OpenGL which part of the Mac window to render to */
static void
SetMacBufRect(C3dWidget *pCanvas)
{
    Tk_Window tkwin = pCanvas->tkwin;
    Rect    r;
#if 0
    GLint   wrect[4];

    /* set wrect[0,1] to lower left corner of widget */
    wrect[2] = Tk_Width(tkwin);
    wrect[3] = Tk_Height(tkwin);

    wrect[0] = winPtr->privatePtr->xOff;
    wrect[1] = r.bottom - wrect[3] - winPtr->privatePtr->yOff;
    aglSetInteger(pCanvas->context, AGL_BUFFER_RECT, wrect);
    aglEnable(pCanvas->context, AGL_BUFFER_RECT);
#endif
    aglSetCurrentContext(pCanvas->context);
    aglUpdateContext(pCanvas->context);
    GetPortBounds(TkMacOSXGetDrawablePort(Tk_WindowId(tkwin)), &r);
    glViewport(0, 0, r.right - r.left, r.bottom - r.top);
}

/*
 *---------------------------------------------------------------------------
 *
 * 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(pCanvas)
    C3dWidget *pCanvas;
{
    if (pCanvas->options.saveunder == SAVEUNDER_NONE) {
	SetMacBufRect(pCanvas);
        aglSwapBuffers(pCanvas->context);
    } else {
        int w = pCanvas->pixmapwidth;
        int h = pCanvas->pixmapheight;
        Tk_Window win = pCanvas->tkwin;
        Window xwin = Tk_WindowId(win);
        GC gc = C3dGetGC(win, 0, 0);
	assert(pCanvas->pixmap);
        XCopyArea(Tk_Display(win), pCanvas->pixmap, xwin, gc, 0, 0, w, h, 0, 0);
        C3dFreeGC(Tk_Display(pCanvas->tkwin), gc);
    }
}

/*
 *---------------------------------------------------------------------------
 *
 * 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;
    switch (eventPtr->type) {
      case MapNotify: {
          AGLDrawable d = TkMacOSXGetDrawablePort(Tk_WindowId(pCanvas->tkwin));
          aglSetDrawable(pCanvas->context, d);
          break;
      }
      case UnmapNotify: {
          /*
           * For Mac OS X Aqua, Tk subwindows are not implemented as
           * separate Aqua windows.  They are just different regions of a
           * single Aqua window.  To unmap them they are just not drawn.
           * Have to disconnect the AGL context otherwise they will continue
           * to be displayed directly by Aqua.
           */
          aglSetDrawable(pCanvas->context, NULL);
          break;
      }
  }
}

static int C3d_Native_Init(Tcl_Interp *interp) {
  tkMacOSXGCEnabled = ([NSGarbageCollector defaultCollector] != nil);
  return TCL_OK;
}
