aboutsummaryrefslogtreecommitdiffstats
path: root/src/glut/fbdev/glut_fbdev.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/glut/fbdev/glut_fbdev.c')
-rw-r--r--src/glut/fbdev/glut_fbdev.c2199
1 files changed, 2199 insertions, 0 deletions
diff --git a/src/glut/fbdev/glut_fbdev.c b/src/glut/fbdev/glut_fbdev.c
new file mode 100644
index 00000000000..3ed8fe90e33
--- /dev/null
+++ b/src/glut/fbdev/glut_fbdev.c
@@ -0,0 +1,2199 @@
+/*
+ * Mesa 3-D graphics library
+ * Version: 6.5
+ * Copyright (C) 1995-2006 Brian Paul
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*
+ * Library for glut using mesa fbdev driver
+ *
+ * Written by Sean D'Epagnier (c) 2006
+ */
+
+#include <assert.h>
+#include <errno.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <termios.h>
+#include <inttypes.h>
+
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/poll.h>
+#include <sys/types.h>
+#include <sys/kd.h>
+
+#include <linux/fb.h>
+#include <linux/keyboard.h>
+#include <linux/vt.h>
+
+#include <GL/gl.h>
+#include <GL/glfbdev.h>
+#include <GL/glut.h>
+
+#include <math.h>
+
+#include "../../mesa/main/config.h"
+
+#define MULTIHEAD /* enable multihead hacks,
+ it allows the program to continue drawing
+ without reading input when a second fbdev
+ has keyboard focus it can cause
+ screen corruption that requires C-l to fix */
+
+#define FBMODES "/etc/fb.modes"
+
+#define HAVE_GPM
+
+#ifdef HAVE_GPM
+#include <gpm.h>
+static int GpmMouse;
+#endif
+
+#define MOUSEDEV "/dev/gpmdata"
+
+static int CurrentVT;
+static int ConsoleFD = - 1;
+
+/* save settings to restore on exit */
+static int OldKDMode = -1;
+static int OldMode;
+struct vt_mode OldVTMode;
+struct termios OldTermios;
+
+static struct fb_fix_screeninfo FixedInfo;
+static struct fb_var_screeninfo VarInfo, OrigVarInfo;
+struct fb_cmap ColorMap;
+
+static int DesiredDepth = 0;
+
+static int FrameBufferFD = -1;
+static caddr_t FrameBuffer = (caddr_t) -1;
+static caddr_t BackBuffer = NULL;
+static int DisplayMode;
+
+static int AccumSize = 16; /* per channel size of accumulation buffer */
+static int DepthSize = DEFAULT_SOFTWARE_DEPTH_BITS;
+static int StencilSize = STENCIL_BITS;
+
+#define MENU_FONT_WIDTH 9
+#define MENU_FONT_HEIGHT 15
+#define MENU_FONT GLUT_BITMAP_9_BY_15
+#define SUBMENU_OFFSET 20
+
+static int AttachedMenus[3];
+static int ActiveMenu;
+static int SelectedMenu;
+static int CurrentMenu;
+static int NumMenus = 1;
+
+static struct {
+ int NumItems;
+ int x, y;
+ int width;
+ int selected;
+ struct {
+ int value;
+ int submenu;
+ char *name;
+ } *Items;
+ void (*func)(int);
+} *Menus = NULL;
+
+struct GlutTimer {
+ int time;
+ void (*func)(int);
+ int value;
+ struct GlutTimer *next;
+};
+
+struct GlutTimer *GlutTimers = NULL;
+
+static struct timeval StartTime;
+
+static int KeyboardModifiers;
+static int KeyboardLedState;
+
+static int MouseFD;
+static int NumMouseButtons;
+static int MouseX;
+static int MouseY;
+static double MouseSpeed = 0;
+static int CurrentCursor = GLUT_CURSOR_LEFT_ARROW;
+/* only display the mouse if there is a registered callback for it */
+static int MouseEnabled = 0;
+
+/* per window data */
+static GLFBDevContextPtr Context;
+static GLFBDevBufferPtr Buffer;
+static GLFBDevVisualPtr Visual;
+static void (*DisplayFunc)(void) = NULL;
+static void (*ReshapeFunc)(int width, int height) = NULL;
+static void (*KeyboardFunc)(unsigned char key, int x, int y) = NULL;
+static void (*MouseFunc)(int key, int state, int x, int y) = NULL;
+static void (*MotionFunc)(int x, int y) = NULL;
+static void (*PassiveMotionFunc)(int x, int y) = NULL;
+static void (*VisibilityFunc)(int state) = NULL;
+static void (*SpecialFunc)(int key, int x, int y) = NULL;
+static void (*IdleFunc)(void) = NULL;
+static void (*MenuStatusFunc)(int state, int x, int y) = NULL;
+static void (*MenuStateFunc)(int state) = NULL;
+
+static int Redisplay;
+static int Visible;
+static int VisibleSwitch;
+static int Active;
+/* we have to poll to see if we are visible
+ on a framebuffer that is not active */
+static int VisiblePoll;
+static int FramebufferIndex;
+
+static int RequiredWidth;
+static int RequiredHeight;
+static int InitialWidthHint;
+static int InitialHeightHint;
+
+static char exiterror[256];
+
+/* --------- Initialization ------------*/
+/* test if the active console is attached to the same framebuffer */
+static void TestVisible(void) {
+ struct fb_con2fbmap confb;
+ struct vt_stat st;
+ int ret;
+ ioctl(ConsoleFD, VT_GETSTATE, &st);
+ confb.console = st.v_active;
+
+ ret = ioctl(FrameBufferFD, FBIOGET_CON2FBMAP, &confb);
+
+ if(ret == -1 || confb.framebuffer == FramebufferIndex) {
+ VisibleSwitch = 1;
+ Visible = 0;
+ VisiblePoll = 0;
+ }
+}
+
+static void VTSwitchHandler(int sig)
+{
+ struct vt_stat st;
+ switch(sig) {
+ case SIGUSR1:
+ ioctl(ConsoleFD, VT_RELDISP, 1);
+ Active = 0;
+#ifdef MULTIHEAD
+ VisiblePoll = 1;
+ TestVisible();
+#else
+ VisibleSwitch = 1;
+ Visible = 0;
+#endif
+ break;
+ case SIGUSR2:
+ ioctl(ConsoleFD, VT_GETSTATE, &st);
+ if(st.v_active)
+ ioctl(ConsoleFD, VT_RELDISP, VT_ACKACQ);
+
+ /* this is a hack to turn the cursor off */
+ ioctl(FrameBufferFD, FBIOPUT_VSCREENINFO, &VarInfo);
+
+ /* restore color map */
+ if(DisplayMode & GLUT_INDEX) {
+ ColorMap.start = 0;
+ ColorMap.len = 256;
+
+ if (ioctl(FrameBufferFD, FBIOPUTCMAP, (void *) &ColorMap) < 0)
+ fprintf(stderr, "ioctl(FBIOPUTCMAP) failed!\n");
+ }
+
+ Active = 1;
+ Visible = 1;
+ VisibleSwitch = 1;
+
+ Redisplay = 1;
+
+ break;
+ }
+}
+
+static void Cleanup(void)
+{
+ if(ConsoleFD >= 0)
+ if (tcsetattr(0, TCSANOW, &OldTermios) < 0)
+ fprintf(stderr, "tcsetattr failed\n");
+
+ if(ConsoleFD > 0) {
+ /* restore keyboard state */
+ if (ioctl(ConsoleFD, VT_SETMODE, &OldVTMode) < 0)
+ fprintf(stderr, "Failed to set vtmode\n");
+
+ if (ioctl(ConsoleFD, KDSKBMODE, OldKDMode) < 0)
+ fprintf(stderr, "ioctl KDSKBMODE failed!\n");
+
+ if(ioctl(ConsoleFD, KDSETMODE, OldMode) < 0)
+ fprintf(stderr, "ioctl KDSETMODE failed!\n");
+
+ close(ConsoleFD);
+ }
+
+ /* close mouse */
+#ifdef HAVE_GPM
+ if(GpmMouse) {
+ if(NumMouseButtons)
+ Gpm_Close();
+ } else
+#endif
+ if(MouseFD >= 0)
+ close(MouseFD);
+
+ glFBDevMakeCurrent( NULL, NULL, NULL);
+
+ glFBDevDestroyContext(Context);
+ glFBDevDestroyBuffer(Buffer);
+ glFBDevDestroyVisual(Visual);
+
+ struct vt_mode VT;
+
+ /* restore original variable screen info */
+ if(FrameBufferFD != -1) {
+ if (ioctl(FrameBufferFD, FBIOPUT_VSCREENINFO, &OrigVarInfo))
+ fprintf(stderr, "ioctl(FBIOPUT_VSCREENINFO failed): %s\n",
+ strerror(errno));
+
+ munmap(FrameBuffer, FixedInfo.smem_len);
+ close(FrameBufferFD);
+ }
+
+ /* free allocated back buffer */
+ if(DisplayMode & GLUT_DOUBLE)
+ free(BackBuffer);
+
+ /* free menu items */
+ int i, j;
+
+ for(i = 1; i<NumMenus; i++) {
+ for(i = 1; i<Menus[i].NumItems; i++)
+ free(Menus[i].Items[j].name);
+ free(Menus[i].Items);
+ }
+ free(Menus);
+
+ if(exiterror[0])
+ fprintf(stderr, "[glfbdev glut] %s", exiterror);
+}
+
+static void CrashHandler(int sig)
+{
+ sprintf(exiterror, "Caught signal %d, cleaning up\n", sig);
+ exit(0);
+}
+
+static void InitializeVT(int usestdin)
+{
+ /* terminos settings for straight-through mode */
+ if (tcgetattr(0, &OldTermios) < 0) {
+ sprintf(exiterror, "tcgetattr failed\n");
+ exit(0);
+ }
+
+ struct termios tio = OldTermios;
+
+ tio.c_lflag &= ~(ICANON | ECHO | ISIG);
+ tio.c_iflag &= ~(ISTRIP | IGNCR | ICRNL | INLCR | IXOFF | IXON);
+ tio.c_iflag |= IGNBRK;
+ tio.c_cc[VMIN] = 0;
+ tio.c_cc[VTIME] = 0;
+
+ if (tcsetattr(0, TCSANOW, &tio) < 0) {
+ sprintf(exiterror, "tcsetattr failed\n");
+ exit(0);
+ }
+
+ if(fcntl(0, F_SETFL, O_NONBLOCK) < 0) {
+ sprintf(exiterror, "Failed to set keyboard to non-blocking\n");
+ exit(0);
+ }
+
+ Active = 1;
+
+ if(usestdin) {
+ ConsoleFD = 0;
+ return;
+ }
+
+ /* detect the current vt if it was not specified */
+ if(CurrentVT == 0) {
+ int fd = open("/dev/tty", O_RDWR | O_NDELAY, 0);
+ struct vt_stat st;
+ if(fd == -1) {
+ sprintf(exiterror, "Failed to open /dev/tty\n");
+ exit(0);
+ }
+ if(ioctl(fd, VT_GETSTATE, &st) == -1) {
+ fprintf(stderr, "Could not detect current vt, specify with -vt\n");
+ fprintf(stderr, "Defaulting to stdin input\n");
+ ConsoleFD = 0;
+ close(fd);
+ return;
+ } else
+ CurrentVT = st.v_active;
+
+ close(fd);
+ }
+
+ /* open the console tty */
+ char console[128];
+ sprintf(console, "/dev/tty%d", CurrentVT);
+ ConsoleFD = open(console, O_RDWR | O_NDELAY, 0);
+ if (ConsoleFD < 0) {
+ sprintf(exiterror, "error couldn't open %s,"
+ " defaulting to stdin \n", console);
+ ConsoleFD = 0;
+ return;
+ }
+
+ signal(SIGUSR1, VTSwitchHandler);
+ signal(SIGUSR2, VTSwitchHandler);
+
+ struct vt_mode vt;
+
+ if (ioctl(ConsoleFD, VT_GETMODE, &OldVTMode) < 0) {
+ sprintf(exiterror,"Failed to grab %s, defaulting to stdin\n", console);
+ close(ConsoleFD);
+ ConsoleFD = 0;
+ return;
+ }
+
+ vt = OldVTMode;
+
+ vt.mode = VT_PROCESS;
+ vt.waitv = 0;
+ vt.relsig = SIGUSR1;
+ vt.acqsig = SIGUSR2;
+ if (ioctl(ConsoleFD, VT_SETMODE, &vt) < 0) {
+ sprintf(exiterror, "error: ioctl(VT_SETMODE) failed: %s\n",
+ strerror(errno));
+ close(ConsoleFD);
+ ConsoleFD = 0;
+ exit(1);
+ }
+
+ if (ioctl(ConsoleFD, KDGKBMODE, &OldKDMode) < 0) {
+ fprintf(stderr, "warning: ioctl KDGKBMODE failed!\n");
+ OldKDMode = K_XLATE;
+ }
+
+ if(ioctl(ConsoleFD, KDGETMODE, &OldMode) < 0)
+ sprintf(exiterror, "Warning: Failed to get terminal mode\n");
+
+#ifdef HAVE_GPM
+ if(!GpmMouse)
+#endif
+ if(ioctl(ConsoleFD, KDSETMODE, KD_GRAPHICS) < 0)
+ sprintf(exiterror,"Warning: Failed to set terminal to graphics\n");
+
+
+ if (ioctl(ConsoleFD, KDSKBMODE, K_MEDIUMRAW) < 0) {
+ sprintf(exiterror, "ioctl KDSKBMODE failed!\n");
+ tcsetattr(0, TCSANOW, &OldTermios);
+ exit(0);
+ }
+
+ if( ioctl(ConsoleFD, KDGKBLED, &KeyboardLedState) < 0) {
+ sprintf(exiterror, "ioctl KDGKBLED failed!\n");
+ exit(0);
+ }
+}
+
+static void InitializeMouse(void)
+{
+#ifdef HAVE_GPM
+ if(GpmMouse) {
+ Gpm_Connect conn;
+ int c;
+ conn.eventMask = ~0; /* Want to know about all the events */
+ conn.defaultMask = 0; /* don't handle anything by default */
+ conn.minMod = 0; /* want everything */
+ conn.maxMod = ~0; /* all modifiers included */
+ if(Gpm_Open(&conn, 0) == -1) {
+ fprintf(stderr, "Cannot open gpmctl. Continuing without Mouse\n");
+ return;
+ }
+
+ if(!MouseSpeed)
+ MouseSpeed = 5;
+ } else
+#endif
+ {
+ const char *mousedev = getenv("MOUSE");
+ if(!mousedev)
+ mousedev = MOUSEDEV;
+ if((MouseFD = open(mousedev, O_RDONLY)) < 0) {
+ fprintf(stderr,"Cannot open %s.\n"
+ "Continuing without Mouse\n", MOUSEDEV);
+ return;
+ }
+
+ if(!MouseSpeed)
+ MouseSpeed = 1;
+ }
+
+ NumMouseButtons = 3;
+}
+
+static void removeArgs(int *argcp, char **argv, int num)
+{
+ int i;
+ for (i = 0; argv[i+num]; i++)
+ argv[i] = argv[i+num];
+
+ argv[i] = NULL;
+ *argcp -= num;
+}
+
+#define REQPARAM(PARAM) \
+ if (i >= *argcp - 1) { \
+ fprintf(stderr, PARAM" requires a parameter\n"); \
+ exit(0); \
+ }
+
+void glutInit (int *argcp, char **argv)
+{
+ int i;
+ int nomouse = 0;
+ int nokeyboard = 0;
+ int usestdin = 0;
+
+ /* parse out args */
+ for (i = 1; i < *argcp;) {
+ if (!strcmp(argv[i], "-geometry")) {
+ REQPARAM("geometry");
+ if(sscanf(argv[i+1], "%dx%d", &RequiredWidth,
+ &RequiredHeight) != 2) {
+ fprintf(stderr,"Please specify geometry as widthxheight\n");
+ exit(0);
+ }
+ removeArgs(argcp, &argv[i], 2);
+ } else
+ if (!strcmp(argv[i], "-bpp")) {
+ REQPARAM("bpp");
+ if(sscanf(argv[i+1], "%d", &DesiredDepth) != 1) {
+ fprintf(stderr, "Please specify a parameter for bpp\n");
+ exit(0);
+ }
+
+ removeArgs(argcp, &argv[i], 2);
+ } else
+ if (!strcmp(argv[i], "-vt")) {
+ REQPARAM("vt");
+ if(sscanf(argv[i+1], "%d", &CurrentVT) != 1) {
+ fprintf(stderr, "Please specify a parameter for vt\n");
+ exit(0);
+ }
+ removeArgs(argcp, &argv[i], 2);
+ } else
+ if (!strcmp(argv[i], "-mousespeed")) {
+ REQPARAM("mousespeed");
+ if(sscanf(argv[i+1], "%lf", &MouseSpeed) != 1) {
+ fprintf(stderr, "Please specify a mouse speed, eg: 2.5\n");
+ exit(0);
+ }
+ removeArgs(argcp, &argv[i], 2);
+ } else
+ if (!strcmp(argv[i], "-nomouse")) {
+ nomouse = 1;
+ removeArgs(argcp, &argv[i], 1);
+ } else
+ if (!strcmp(argv[i], "-nokeyboard")) {
+ nokeyboard = 1;
+ removeArgs(argcp, &argv[i], 1);
+ } else
+ if (!strcmp(argv[i], "-stdin")) {
+ usestdin = 1;
+ removeArgs(argcp, &argv[i], 1);
+ } else
+ if (!strcmp(argv[i], "-gpmmouse")) {
+#ifdef HAVE_GPM
+ GpmMouse = 1;
+#else
+ fprintf(stderr, "gpm support was not compiled\n");
+ exit(0);
+#endif
+ removeArgs(argcp, &argv[i], 1);
+ } else
+ if (!strcmp(argv[i], "--")) {
+ removeArgs(argcp, &argv[i], 1);
+ break;
+ } else
+ i++;
+ }
+
+ gettimeofday(&StartTime, 0);
+ atexit(Cleanup);
+
+ signal(SIGSEGV, CrashHandler);
+ signal(SIGINT, CrashHandler);
+ signal(SIGTERM, CrashHandler);
+
+ if(nomouse == 0)
+ InitializeMouse();
+ if(nokeyboard == 0)
+ InitializeVT(usestdin);
+}
+
+void glutInitDisplayMode (unsigned int mode)
+{
+ DisplayMode = mode;
+}
+
+void glutInitWindowPosition (int x, int y)
+{
+}
+
+void glutInitWindowSize (int width, int height)
+{
+ InitialWidthHint = width;
+ InitialHeightHint = height;
+}
+
+/* --------- Mouse Rendering ------------*/
+#include "cursors.h"
+static int LastMouseX;
+static int LastMouseY;
+static unsigned char *MouseBuffer;
+
+static void EraseCursor(void)
+{
+ int off = LastMouseY * FixedInfo.line_length
+ + LastMouseX * VarInfo.bits_per_pixel / 8;
+ int stride = CURSOR_WIDTH * VarInfo.bits_per_pixel / 8;
+ int i;
+
+ unsigned char *src = MouseBuffer;
+
+ for(i = 0; i<CURSOR_HEIGHT; i++) {
+ memcpy(BackBuffer + off, src, stride);
+ src += stride;
+ off += FixedInfo.line_length;
+ }
+}
+
+static void SaveCursor(int x, int y)
+{
+ if(x < 0)
+ LastMouseX = 0;
+ else
+ if(x > (int)VarInfo.xres - CURSOR_WIDTH)
+ LastMouseX = VarInfo.xres - CURSOR_WIDTH;
+ else
+ LastMouseX = x;
+
+ if(y < 0)
+ LastMouseY = 0;
+ else
+ if(y > (int)VarInfo.yres - CURSOR_HEIGHT)
+ LastMouseY = VarInfo.yres - CURSOR_HEIGHT;
+ else
+ LastMouseY = y;
+
+ int off = LastMouseY * FixedInfo.line_length
+ + LastMouseX * VarInfo.bits_per_pixel / 8;
+ int stride = CURSOR_WIDTH * VarInfo.bits_per_pixel / 8;
+ int i;
+ unsigned char *src = MouseBuffer;
+ for(i = 0; i<CURSOR_HEIGHT; i++) {
+ memcpy(src, BackBuffer + off, stride);
+ src += stride;
+ off += FixedInfo.line_length;
+ }
+}
+
+static void DrawCursor(void)
+{
+ if(CurrentCursor < 0 || CurrentCursor >= NUM_CURSORS)
+ return;
+
+ int px = MouseX - CursorsXOffset[CurrentCursor];
+ int py = MouseY - CursorsYOffset[CurrentCursor];
+
+ SaveCursor(px, py);
+
+ int xoff = 0;
+ if(px < 0)
+ xoff = -px;
+
+ int xlen = CURSOR_WIDTH;
+ if(px + CURSOR_WIDTH > VarInfo.xres)
+ xlen = VarInfo.xres - px;
+
+ int yoff = 0;
+ if(py < 0)
+ yoff = -py;
+
+ int ylen = CURSOR_HEIGHT;
+ if(py + CURSOR_HEIGHT > VarInfo.yres)
+ ylen = VarInfo.yres - py;
+
+ int bypp = VarInfo.bits_per_pixel / 8;
+
+ unsigned char *c = BackBuffer + FixedInfo.line_length * (py + yoff)
+ + (px + xoff) * bypp;
+
+ unsigned char *d = Cursors[CurrentCursor] + (CURSOR_WIDTH * yoff + xoff)*4;
+ int i, j;
+
+ int dstride = (CURSOR_WIDTH - xlen + xoff) * 4;
+ int cstride = FixedInfo.line_length - bypp * (xlen - xoff);
+
+ switch(bypp) {
+ case 1: /* no support for 8bpp mouse yet */
+ break;
+ case 2:
+ {
+ uint16_t *e = (void*)c;
+ cstride /= 2;
+ for(i = yoff; i < ylen; i++) {
+ for(j = xoff; j < xlen; j++) {
+ e[0] = ((((d[0] + (((int)(((e[0] >> 8) & 0xf8)
+ | ((c[0] >> 11) & 0x7)) * d[3]) >> 8)) & 0xf8) << 8)
+ | (((d[1] + (((int)(((e[0] >> 3) & 0xfc)
+ | ((e[0] >> 5) & 0x3)) * d[3]) >> 8)) & 0xfc) << 3)
+ | ((d[2] + (((int)(((e[0] << 3) & 0xf8)
+ | (e[0] & 0x7)) * d[3]) >> 8)) >> 3));
+
+ e++;
+ d+=4;
+ }
+ d += dstride;
+ e += cstride;
+ }
+ }
+ break;
+ case 3:
+ case 4:
+ for(i = yoff; i < ylen; i++) {
+ for(j = xoff; j < xlen; j++) {
+ c[0] = d[0] + (((int)c[0] * d[3]) >> 8);
+ c[1] = d[1] + (((int)c[1] * d[3]) >> 8);
+ c[2] = d[2] + (((int)c[2] * d[3]) >> 8);
+
+ c+=bypp;
+ d+=4;
+ }
+ d += dstride;
+ c += cstride;
+ } break;
+ }
+}
+
+#define MIN(x, y) x < y ? x : y
+static void SwapCursor(void)
+{
+ int px = MouseX - CursorsXOffset[CurrentCursor];
+ int py = MouseY - CursorsYOffset[CurrentCursor];
+
+ int minx = MIN(px, LastMouseX);
+ int sizex = abs(px - LastMouseX);
+
+ int miny = MIN(py, LastMouseY);
+ int sizey = abs(py - LastMouseY);
+
+ DrawCursor();
+ /* now update the portion of the screen that has changed */
+
+ if(DisplayMode & GLUT_DOUBLE && (sizex || sizey)) {
+ if(minx < 0)
+ minx = 0;
+ if(miny < 0)
+ miny = 0;
+
+ if(minx + sizex > VarInfo.xres)
+ sizex = VarInfo.xres - minx;
+ if(miny + sizey > VarInfo.yres)
+ sizey = VarInfo.yres - miny;
+ int off = FixedInfo.line_length * miny
+ + minx * VarInfo.bits_per_pixel / 8;
+ int stride = (sizex + CURSOR_WIDTH) * VarInfo.bits_per_pixel / 8;
+ int i;
+ for(i = 0; i< sizey + CURSOR_HEIGHT; i++) {
+ memcpy(FrameBuffer+off, BackBuffer+off, stride);
+ off += FixedInfo.line_length;
+ }
+ }
+}
+
+/* --------- Menu Rendering ------------*/
+static double MenuProjection[16];
+static double MenuModelview[16];
+
+static void InitMenuMatrices(void)
+{
+ glMatrixMode(GL_PROJECTION);
+ glLoadIdentity();
+ gluOrtho2D(0.0,VarInfo.xres,VarInfo.yres,0.0);
+ glMatrixMode(GL_MODELVIEW);
+ glLoadIdentity();
+ glViewport(0,0,VarInfo.xres,VarInfo.yres);
+ glGetDoublev(GL_PROJECTION_MATRIX, MenuProjection);
+ glGetDoublev(GL_MODELVIEW_MATRIX, MenuModelview);
+}
+
+static int DrawMenu(int menu, int x, int *y)
+{
+ int i;
+ int ret = 1;
+ for(i=0; i < Menus[menu].NumItems; i++) {
+ char *s = Menus[menu].Items[i].name;
+ int a =0;
+ if(MouseY >= *y && MouseY < *y + MENU_FONT_HEIGHT &&
+ MouseX >= x && MouseX < x + Menus[menu].width) {
+ a = 1;
+ SelectedMenu = menu;
+ ret = 0;
+ Menus[menu].selected = i;
+ glColor3f(1,0,0);
+ } else
+ glColor3f(0,0,1);
+
+ *y += MENU_FONT_HEIGHT;
+ glRasterPos2i(x, *y);
+ for(; *s; s++)
+ glutBitmapCharacter(MENU_FONT, *s);
+
+ if(Menus[menu].selected == i)
+ if(Menus[menu].Items[i].submenu)
+ if(DrawMenu(Menus[menu].Items[i].submenu, x
+ + SUBMENU_OFFSET, y)) {
+ if(!a)
+ Menus[menu].selected = -1;
+ } else
+ ret = 0;
+ }
+ return ret;
+}
+
+static void DrawMenus(void)
+{
+ /* save old settings */
+ glPushAttrib(-1);
+
+ glMatrixMode(GL_MODELVIEW);
+ glPushMatrix();
+ glLoadMatrixd(MenuModelview);
+ glMatrixMode(GL_PROJECTION);
+ glPushMatrix();
+ glLoadMatrixd(MenuProjection);
+
+ glDisable(GL_DEPTH_TEST);
+ glDisable(GL_ALPHA_TEST);
+ glDisable(GL_LIGHTING);
+ glDisable(GL_FOG);
+ glDisable(GL_TEXTURE_2D);
+ // glEnable(GL_LOGIC_OP);
+ //glEnable(GL_COLOR_LOGIC_OP);
+ // glLogicOp(GL_XOR);
+
+ int x = Menus[ActiveMenu].x;
+ int y = Menus[ActiveMenu].y;
+
+ if(DrawMenu(ActiveMenu, x, &y))
+ Menus[ActiveMenu].selected = -1;
+
+ /* restore settings */
+
+ glPopMatrix();
+ glMatrixMode(GL_MODELVIEW);
+ glPopMatrix();
+
+ glPopAttrib();
+}
+
+/* --------- Event Processing ------------*/
+#define MODIFIER(mod) \
+ KeyboardModifiers = release ? KeyboardModifiers & ~mod \
+ : KeyboardModifiers | mod;
+
+#define READKEY read(ConsoleFD, &code, 1)
+
+static void LedModifier(int led, int release)
+{
+ static int releaseflag = K_CAPS | K_NUM;
+ if(release)
+ releaseflag |= led;
+ else
+ if(releaseflag & led) {
+ KeyboardLedState ^= led;
+ releaseflag &= ~led;
+ }
+ ioctl(ConsoleFD, KDSKBLED, KeyboardLedState);
+ ioctl(ConsoleFD, KDSETLED, 0x80);
+}
+
+static int ReadKey(void)
+{
+ int x;
+ unsigned char code;
+ int specialkey = 0;
+ if(READKEY == 0)
+ return 0;
+
+ if(code == 0)
+ return 0;
+
+ /* stdin input escape code based */
+ if(ConsoleFD == 0) {
+ KeyboardModifiers = 0;
+ altset:
+ if(code == 27 && READKEY == 1) {
+ switch(code) {
+ case 79: /* function key */
+ READKEY;
+ if(code == 50) {
+ READKEY;
+ shiftfunc:
+ KeyboardModifiers |= GLUT_ACTIVE_SHIFT;
+ specialkey = GLUT_KEY_F1 + code - 53;
+ READKEY;
+ } else {
+ READKEY;
+ specialkey = GLUT_KEY_F1 + code - 80;
+ }
+ break;
+ case 91:
+ READKEY;
+ switch(code) {
+ case 68:
+ specialkey = GLUT_KEY_LEFT; break;
+ case 65:
+ specialkey = GLUT_KEY_UP; break;
+ case 67:
+ specialkey = GLUT_KEY_RIGHT; break;
+ case 66:
+ specialkey = GLUT_KEY_DOWN; break;
+ case 53:
+ specialkey = GLUT_KEY_PAGE_UP; READKEY; break;
+ case 54:
+ specialkey = GLUT_KEY_PAGE_DOWN; READKEY; break;
+ case 49:
+ specialkey = GLUT_KEY_HOME; READKEY; break;
+ case 52:
+ specialkey = GLUT_KEY_END; READKEY; break;
+ case 50:
+ READKEY;
+ if(code != 126)
+ goto shiftfunc;
+ specialkey = GLUT_KEY_INSERT;
+ break;
+ case 51:
+ code = '\b'; goto stdkey;
+ case 91:
+ READKEY;
+ specialkey = GLUT_KEY_F1 + code - 65;
+ break;
+ default:
+ return 0;
+ }
+ break;
+ default:
+ KeyboardModifiers |= GLUT_ACTIVE_ALT;
+ goto altset;
+ }
+ }
+ stdkey:
+ if(specialkey) {
+ if(SpecialFunc)
+ SpecialFunc(specialkey, MouseX, MouseY);
+ } else {
+ if(code >= 1 && code <= 26) {
+ KeyboardModifiers |= GLUT_ACTIVE_CTRL;
+ code += 'a' - 1;
+ }
+ if((code >= 43 && code <= 34) || (code == 60)
+ || (code >= 62 && code <= 90) || (code == 94)
+ || (code == 95) || (code >= 123 && code <= 126))
+ KeyboardModifiers |= GLUT_ACTIVE_SHIFT;
+
+ if(KeyboardFunc)
+ KeyboardFunc(code, MouseX, MouseY);
+ }
+ return 1;
+ }
+
+ /* linux kbd reading */
+ struct kbentry entry;
+ entry.kb_table = 0;
+ if(KeyboardModifiers & GLUT_ACTIVE_SHIFT)
+ entry.kb_table |= K_SHIFTTAB;
+
+ int release = code & 0x80;
+ code &= 0x7F;
+
+ entry.kb_index = code;
+
+ if (ioctl(ConsoleFD, KDGKBENT, &entry) < 0) {
+ sprintf(exiterror, "ioctl(KDGKBENT) failed.\n");
+ exit(0);
+ }
+
+ int labelval = entry.kb_value;
+
+ switch(labelval) {
+ case K_SHIFT:
+ case K_SHIFTL:
+ MODIFIER(GLUT_ACTIVE_SHIFT);
+ return 0;
+ case K_CTRL:
+ MODIFIER(GLUT_ACTIVE_CTRL);
+ return 0;
+ case K_ALT:
+ case K_ALTGR:
+ MODIFIER(GLUT_ACTIVE_ALT);
+ return 0;
+ }
+
+ if(!release && labelval >= K_F1 && labelval <= K_F12)
+ if(KeyboardModifiers & GLUT_ACTIVE_ALT) {
+ /* VT switch, we must do it */
+ if(ioctl(ConsoleFD, VT_ACTIVATE, labelval - K_F1 + 1) < 0)
+ sprintf(exiterror, "Error switching console\n");
+ return 0;
+ }
+
+ switch(labelval) {
+ case K_CAPS:
+ LedModifier(LED_CAP, release);
+ return 0;
+ case K_NUM:
+ LedModifier(LED_NUM, release);
+ return 0;
+ case K_HOLD: /* scroll lock suspends glut */
+ LedModifier(LED_SCR, release);
+ while(KeyboardLedState & LED_SCR) {
+ usleep(10000);
+ ReadKey();
+ }
+ return 0;
+ }
+
+ /* we could queue keypresses here */
+ if(KeyboardLedState & LED_SCR)
+ return 0;
+
+ if(release)
+ return 0;
+
+ if(labelval >= K_F1 && labelval <= K_F12)
+ specialkey = GLUT_KEY_F1 + labelval - K_F1;
+ else
+ switch(labelval) {
+ case K_LEFT:
+ specialkey = GLUT_KEY_LEFT; break;
+ case K_UP:
+ specialkey = GLUT_KEY_UP; break;
+ case K_RIGHT:
+ specialkey = GLUT_KEY_RIGHT; break;
+ case K_DOWN:
+ specialkey = GLUT_KEY_DOWN; break;
+ case K_PGUP:
+ specialkey = GLUT_KEY_PAGE_UP; break;
+ case K_PGDN:
+ specialkey = GLUT_KEY_PAGE_DOWN; break;
+ case K_FIND:
+ specialkey = GLUT_KEY_HOME; break;
+ case K_SELECT:
+ specialkey = GLUT_KEY_END; break;
+ case K_INSERT:
+ specialkey = GLUT_KEY_INSERT; break;
+ case K_REMOVE:
+ labelval = '\b'; break;
+ case K_ENTER:
+ labelval = '\n'; break;
+ }
+
+ if(specialkey) {
+ if(SpecialFunc)
+ SpecialFunc(specialkey, MouseX, MouseY);
+ } else
+ if(KeyboardFunc) {
+ char c = labelval;
+ if(KeyboardLedState & LED_CAP) {
+ if(c >= 'A' && c <= 'Z')
+ c += 'a' - 'A';
+ else
+ if(c >= 'a' && c <= 'z')
+ c += 'A' - 'a';
+ }
+ KeyboardFunc(c, MouseX, MouseY);
+ }
+ return 1;
+}
+
+static void HandleMousePress(int button, int pressed)
+{
+ if(ActiveMenu && !pressed) {
+ if(MenuStatusFunc)
+ MenuStatusFunc(GLUT_MENU_NOT_IN_USE, MouseX, MouseY);
+ if(MenuStateFunc)
+ MenuStateFunc(GLUT_MENU_NOT_IN_USE);
+ if(SelectedMenu > 0) {
+ int selected = Menus[SelectedMenu].selected;
+ if(selected >= 0)
+ if(Menus[SelectedMenu].Items[selected].submenu == 0)
+ Menus[SelectedMenu].func(Menus[SelectedMenu].Items
+ [selected].value);
+ }
+ ActiveMenu = 0;
+ Redisplay = 1;
+ return;
+ }
+
+ if(AttachedMenus[button] && pressed) {
+ ActiveMenu = AttachedMenus[button];
+ if(MenuStatusFunc)
+ MenuStatusFunc(GLUT_MENU_IN_USE, MouseX, MouseY);
+ if(MenuStateFunc)
+ MenuStateFunc(GLUT_MENU_IN_USE);
+ Menus[ActiveMenu].x = MouseX - Menus[ActiveMenu].width/2;
+ Menus[ActiveMenu].y = MouseY - Menus[ActiveMenu].NumItems*MENU_FONT_HEIGHT/2;
+ Menus[ActiveMenu].selected = -1;
+ Redisplay = 1;
+ return;
+ }
+
+ if(MouseFunc)
+ MouseFunc(button, pressed ? GLUT_DOWN : GLUT_UP, MouseX, MouseY);
+}
+
+static int ReadMouse(void)
+{
+ int l, r, m;
+ static int ll, lm, lr;
+ signed char dx, dy;
+
+#ifdef HAVE_GPM
+ if(GpmMouse) {
+ Gpm_Event event;
+ struct pollfd pfd;
+ pfd.fd = gpm_fd;
+ pfd.events = POLLIN;
+ if(poll(&pfd, 1, 1) != 1)
+ return 0;
+
+ if(Gpm_GetEvent(&event) != 1)
+ return 0;
+
+ l = event.buttons & GPM_B_LEFT;
+ m = event.buttons & GPM_B_MIDDLE;
+ r = event.buttons & GPM_B_RIGHT;
+
+ /* gpm is weird in that it gives a button number when the button
+ is released, with type set to GPM_UP, this is only a problem
+ if it is the last button released */
+
+ if(event.type & GPM_UP)
+ if(event.buttons == GPM_B_LEFT || event.buttons == GPM_B_MIDDLE ||
+ event.buttons == GPM_B_RIGHT || event.buttons == GPM_B_FOURTH)
+ l = m = r = 0;
+
+ dx = event.dx;
+ dy = event.dy;
+ } else
+#endif
+ {
+ if(MouseFD == -1)
+ return 0;
+
+ if(fcntl(MouseFD, F_SETFL, O_NONBLOCK) == -1) {
+ close(MouseFD);
+ MouseFD = -1;
+ return 0;
+ }
+
+ char data[4];
+ if(read(MouseFD, data, 4) != 4)
+ return 0;
+
+ l = ((data[0] & 0x20) >> 3);
+ m = ((data[3] & 0x10) >> 3);
+ r = ((data[0] & 0x10) >> 4);
+
+ dx = (((data[0] & 0x03) << 6) | (data[1] & 0x3F));
+ dy = (((data[0] & 0x0C) << 4) | (data[2] & 0x3F));
+ }
+
+ MouseX += dx * MouseSpeed;
+ if(MouseX < 0)
+ MouseX = 0;
+ else
+ if(MouseX >= VarInfo.xres)
+ MouseX = VarInfo.xres - 1;
+
+ MouseY += dy * MouseSpeed;
+ if(MouseY < 0)
+ MouseY = 0;
+ else
+ if(MouseY >= VarInfo.yres)
+ MouseY = VarInfo.yres - 1;
+
+ if(l != ll)
+ HandleMousePress(GLUT_LEFT_BUTTON, l);
+ if(m != lm)
+ HandleMousePress(GLUT_MIDDLE_BUTTON, m);
+ if(r != lr)
+ HandleMousePress(GLUT_RIGHT_BUTTON, r);
+
+ ll = l, lm = m, lr = r;
+
+ if(dx || dy) {
+ if(l || m || r) {
+ if(MotionFunc)
+ MotionFunc(MouseX, MouseY);
+ } else
+ if(PassiveMotionFunc)
+ PassiveMotionFunc(MouseX, MouseY);
+
+ EraseCursor();
+ if(ActiveMenu)
+ Redisplay = 1;
+ else
+ SwapCursor();
+ }
+
+ return 1;
+}
+
+static void RecieveEvents(void)
+{
+ while(ReadKey());
+
+ if(MouseEnabled)
+ while(ReadMouse());
+}
+
+static void ProcessTimers(void)
+{
+ if(GlutTimers && GlutTimers->time < glutGet(GLUT_ELAPSED_TIME)) {
+ struct GlutTimer *timer = GlutTimers;
+ timer->func(timer->value);
+ GlutTimers = timer->next;
+ free(timer);
+ }
+}
+
+void glutMainLoop(void)
+{
+ if(ReshapeFunc)
+ ReshapeFunc(VarInfo.xres, VarInfo.yres);
+
+ if(!DisplayFunc) {
+ sprintf(exiterror, "Fatal Error: No Display Function registered\n");
+ exit(0);
+ }
+
+ for(;;) {
+ ProcessTimers();
+
+ if(Active)
+ RecieveEvents();
+ else
+ if(VisiblePoll)
+ TestVisible();
+
+ if(IdleFunc)
+ IdleFunc();
+
+ if(VisibleSwitch) {
+ VisibleSwitch = 0;
+ if(VisibilityFunc)
+ VisibilityFunc(Visible ? GLUT_VISIBLE : GLUT_NOT_VISIBLE);
+ }
+
+ if(Visible && Redisplay) {
+ Redisplay = 0;
+ if(MouseEnabled)
+ EraseCursor();
+ DisplayFunc();
+ if(!(DisplayMode & GLUT_DOUBLE)) {
+ if(ActiveMenu)
+ DrawMenus();
+ if(MouseEnabled)
+ DrawCursor();
+ }
+ }
+ }
+}
+
+/* ---------- Window Management ----------*/
+static void ParseFBModes(void)
+{
+ char buf[1024];
+ struct fb_var_screeninfo vi = VarInfo;
+
+ FILE *fbmodes = fopen(FBMODES, "r");
+
+ if(!fbmodes) {
+ sprintf(exiterror, "Warning: could not open "
+ FBMODES" using current mode\n");
+ return;
+ }
+
+ if(InitialWidthHint == 0 && InitialHeightHint == 0
+ && RequiredWidth == 0)
+ return; /* use current mode */
+
+ while(fgets(buf, sizeof buf, fbmodes)) {
+ char *c;
+ int v;
+
+ if(!(c = strstr(buf, "geometry")))
+ continue;
+ v = sscanf(c, "geometry %d %d %d %d %d", &vi.xres, &vi.yres,
+ &vi.xres_virtual, &vi.yres_virtual, &vi.bits_per_pixel);
+ if(v != 5)
+ continue;
+
+ /* now we have to decide what is best */
+ if(RequiredWidth) {
+ if(RequiredWidth != vi.xres || RequiredHeight != vi.yres)
+ continue;
+ } else {
+ if(VarInfo.xres < vi.xres && VarInfo.xres < InitialWidthHint)
+ v++;
+ if(VarInfo.xres > vi.xres && vi.xres > InitialWidthHint)
+ v++;
+
+ if(VarInfo.yres < vi.yres && VarInfo.yres < InitialHeightHint)
+ v++;
+ if(VarInfo.yres > vi.yres && vi.yres > InitialHeightHint)
+ v++;
+
+ if(v < 7)
+ continue;
+ }
+
+ fgets(buf, sizeof buf, fbmodes);
+ if(!(c = strstr(buf, "timings")))
+ continue;
+
+ v = sscanf(c, "timings %d %d %d %d %d %d %d", &vi.pixclock,
+ &vi.left_margin, &vi.right_margin, &vi.upper_margin,
+ &vi.lower_margin, &vi.hsync_len, &vi.vsync_len);
+ if(v != 7)
+ continue;
+
+ VarInfo = vi; /* finally found a better mode */
+ if(RequiredWidth) {
+ fclose(fbmodes);
+ return;
+ }
+ }
+
+ fclose(fbmodes);
+
+ if(RequiredWidth) {
+ sprintf(exiterror, "No mode (%dx%d) found in "FBMODES"\n",
+ RequiredWidth, RequiredHeight);
+ exit(0);
+ }
+}
+
+int glutCreateWindow (const char *title)
+{
+ if(ConsoleFD == -1) {
+ int argc = 0;
+ char *argv[] = {NULL};
+ glutInit(&argc, argv);
+ }
+
+ if(Context)
+ return 0;
+
+ char *fbdev = getenv("FRAMEBUFFER");
+ if(fbdev) {
+#ifdef MULTIHEAD
+ if(!sscanf(fbdev, "/dev/fb%d", &FramebufferIndex))
+ if(!sscanf(fbdev, "/dev/fb/%d", &FramebufferIndex))
+ sprintf(exiterror, "Could not determine Framebuffer index!\n");
+#endif
+ } else {
+ static char fb[128];
+ FramebufferIndex = 0;
+ struct fb_con2fbmap confb;
+ int fd = open("/dev/fb0", O_RDWR);
+ confb.console = CurrentVT;
+ if(ioctl(fd, FBIOGET_CON2FBMAP, &confb) != -1)
+ FramebufferIndex = confb.framebuffer;
+ sprintf(fb, "/dev/fb%d", FramebufferIndex);
+ fbdev = fb;
+ close(fd);
+ }
+
+ /* open the framebuffer device */
+ FrameBufferFD = open(fbdev, O_RDWR);
+ if (FrameBufferFD < 0) {
+ sprintf(exiterror, "Error opening %s: %s\n", fbdev, strerror(errno));
+ exit(0);
+ }
+
+ /* Get the fixed screen info */
+ if (ioctl(FrameBufferFD, FBIOGET_FSCREENINFO, &FixedInfo)) {
+ sprintf(exiterror, "error: ioctl(FBIOGET_FSCREENINFO) failed: %s\n",
+ strerror(errno));
+ exit(0);
+ }
+
+ /* get the variable screen info */
+ if (ioctl(FrameBufferFD, FBIOGET_VSCREENINFO, &OrigVarInfo)) {
+ sprintf(exiterror, "error: ioctl(FBIOGET_VSCREENINFO) failed: %s\n",
+ strerror(errno));
+ exit(0);
+ }
+
+ /* operate on a copy */
+ VarInfo = OrigVarInfo;
+
+ /* set the depth, resolution, etc */
+ ParseFBModes();
+
+ if(DisplayMode & GLUT_INDEX)
+ VarInfo.bits_per_pixel = 8;
+ else
+ if(VarInfo.bits_per_pixel == 8)
+ VarInfo.bits_per_pixel = 32;
+
+ if (DesiredDepth)
+ VarInfo.bits_per_pixel = DesiredDepth;
+
+ VarInfo.xoffset = 0;
+ VarInfo.yoffset = 0;
+ VarInfo.nonstd = 0;
+ VarInfo.vmode &= ~FB_VMODE_YWRAP; /* turn off scrolling */
+
+ /* set new variable screen info */
+ if (ioctl(FrameBufferFD, FBIOPUT_VSCREENINFO, &VarInfo)) {
+ sprintf(exiterror, "ioctl(FBIOPUT_VSCREENINFO failed): %s\n",
+ strerror(errno));
+ exit(0);
+ }
+
+ /* reload the screen info to update offsets */
+ if (ioctl(FrameBufferFD, FBIOGET_VSCREENINFO, &VarInfo)) {
+ sprintf(exiterror, "error: ioctl(FBIOGET_VSCREENINFO) failed: %s\n",
+ strerror(errno));
+ exit(0);
+ }
+
+ /* reload the fixed info to update color mode */
+ if (ioctl(FrameBufferFD, FBIOGET_FSCREENINFO, &FixedInfo)) {
+ sprintf(exiterror, "error: ioctl(FBIOGET_FSCREENINFO) failed: %s\n",
+ strerror(errno));
+ exit(0);
+ }
+
+ if(DisplayMode & GLUT_INDEX) {
+ /* initialize colormap */
+ if (FixedInfo.visual != FB_VISUAL_DIRECTCOLOR) {
+ static unsigned short red[256], green[256], blue[256];
+ /* we're assuming 256 entries here */
+
+ ColorMap.start = 0;
+ ColorMap.len = 256;
+ ColorMap.red = red;
+ ColorMap.green = green;
+ ColorMap.blue = blue;
+ ColorMap.transp = NULL;
+
+ if (ioctl(FrameBufferFD, FBIOGETCMAP, (void *) &ColorMap) < 0)
+ sprintf(exiterror, "ioctl(FBIOGETCMAP) failed!\n");
+
+ } else {
+ sprintf(exiterror, "error: Could not set 8 bit color mode\n");
+ exit(0);
+ }
+ }
+
+ /* mmap the framebuffer into our address space */
+ FrameBuffer = mmap(0, FixedInfo.smem_len, PROT_READ | PROT_WRITE,
+ MAP_SHARED, FrameBufferFD, 0);
+ if (FrameBuffer == MAP_FAILED) {
+ sprintf(exiterror, "error: unable to mmap framebuffer: %s\n",
+ strerror(errno));
+ exit(0);
+ }
+
+ int attribs[9];
+ int i;
+
+ int mask = DisplayMode;
+ for(i=0; i<8 && mask; i++) {
+ if(mask & GLUT_DOUBLE) {
+ attribs[i] = GLFBDEV_DOUBLE_BUFFER;
+ mask &= ~GLUT_DOUBLE;
+ continue;
+ }
+
+ if(mask & GLUT_INDEX) {
+ attribs[i] = GLFBDEV_COLOR_INDEX;
+ mask &= ~GLUT_INDEX;
+ continue;
+ }
+
+ if(mask & GLUT_DEPTH) {
+ attribs[i] = GLFBDEV_DEPTH_SIZE;
+ attribs[++i] = DepthSize;
+ mask &= ~GLUT_DEPTH;
+ continue;
+ }
+
+ if(mask & GLUT_STENCIL) {
+ attribs[i] = GLFBDEV_STENCIL_SIZE;
+ attribs[++i] = StencilSize;
+ mask &= ~GLUT_STENCIL;
+ continue;
+ }
+
+ if(mask & GLUT_ACCUM) {
+ attribs[i] = GLFBDEV_ACCUM_SIZE;
+ attribs[++i] = AccumSize;
+ mask &= ~GLUT_ACCUM;
+ continue;
+ }
+
+ if(mask & GLUT_ALPHA)
+ if(!(DisplayMode & GLUT_INDEX)) {
+ mask &= ~GLUT_ALPHA;
+ i--;
+ continue;
+ }
+
+ sprintf(exiterror, "Invalid mode from glutInitDisplayMode\n");
+ exit(0);
+ }
+
+ attribs[i] = GLFBDEV_NONE;
+
+ if(!(Visual = glFBDevCreateVisual( &FixedInfo, &VarInfo, attribs ))) {
+ sprintf(exiterror, "Failure to create Visual\n");
+ exit(0);
+ }
+
+ int size = VarInfo.xres_virtual * VarInfo.yres_virtual
+ * VarInfo.bits_per_pixel / 8;
+ if(DisplayMode & GLUT_DOUBLE) {
+ if(!(BackBuffer = malloc(size))) {
+ sprintf(exiterror, "Failed to allocate double buffer\n");
+ exit(0);
+ }
+ } else
+ BackBuffer = FrameBuffer;
+
+ if(!(Buffer = glFBDevCreateBuffer( &FixedInfo, &VarInfo, Visual,
+ FrameBuffer, BackBuffer, size))) {
+ sprintf(exiterror, "Failure to create Buffer\n");
+ exit(0);
+ }
+
+ if(!(Context = glFBDevCreateContext(Visual, NULL))) {
+ sprintf(exiterror, "Failure to create Context\n");
+ exit(0);
+ }
+
+ if(!glFBDevMakeCurrent( Context, Buffer, Buffer )) {
+ sprintf(exiterror, "Failure to Make Current\n");
+ exit(0);
+ }
+
+ Visible = 1;
+ VisibleSwitch = 1;
+ Redisplay = 1;
+
+ /* set up mouse */
+ if((MouseBuffer = malloc(CURSOR_WIDTH * CURSOR_HEIGHT
+ * VarInfo.bits_per_pixel / 8)) == NULL) {
+ sprintf(exiterror, "malloc failure\n");
+ exit(0);
+ }
+
+ MouseX = VarInfo.xres / 2;
+ MouseY = VarInfo.yres / 2;
+
+ /* set up menus */
+ InitMenuMatrices();
+ return 1;
+}
+
+int glutCreateSubWindow(int win, int x, int y, int width, int height)
+{
+ return 0;
+}
+
+void glutSetWindow(int win)
+{
+}
+
+int glutGetWindow(void)
+{
+ return 1;
+}
+
+void glutDestroyWindow(int win)
+{
+}
+
+void glutPostRedisplay(void)
+{
+ Redisplay = 1;
+}
+
+void glutSwapBuffers(void)
+{
+ glFlush();
+
+ if(DisplayMode & GLUT_DOUBLE) {
+ if(ActiveMenu)
+ DrawMenus();
+ if(MouseEnabled)
+ DrawCursor();
+ glFBDevSwapBuffers(Buffer);
+ }
+}
+
+void glutPositionWindow(int x, int y)
+{
+}
+
+void glutReshapeWindow(int width, int height)
+{
+}
+
+void glutFullScreen(void)
+{
+}
+
+void glutPopWindow(void)
+{
+}
+
+void glutPushWindow(void)
+{
+}
+
+void glutShowWindow(void)
+{
+}
+
+void glutHideWindow(void)
+{
+}
+
+void glutIconifyWindow(void)
+{
+}
+
+void glutSetWindowTitle(const char *name)
+{
+}
+
+void glutSetIconTitle(const char *name)
+{
+}
+
+void glutSetCursor(int cursor)
+{
+ if(cursor == GLUT_CURSOR_FULL_CROSSHAIR)
+ cursor = GLUT_CURSOR_CROSSHAIR;
+ CurrentCursor = cursor;
+ MouseEnabled = 1;
+ EraseCursor();
+ SwapCursor();
+}
+
+/* --------- Overlays ------------*/
+void glutEstablishOverlay(void)
+{
+ exit(0);
+}
+
+void glutUseLayer(GLenum layer)
+{
+}
+
+void glutRemoveOverlay(void)
+{
+}
+
+void glutPostOverlayRedisplay(void)
+{
+}
+
+void glutShowOverlay(void)
+{
+}
+
+void glutHideOverlay(void)
+{
+}
+
+/* --------- Menus ------------*/
+int glutCreateMenu(void (*func)(int value))
+{
+ MouseEnabled = 1;
+ CurrentMenu = NumMenus;
+ NumMenus++;
+ Menus = realloc(Menus, sizeof(*Menus) * NumMenus);
+ Menus[CurrentMenu].NumItems = 0;
+ Menus[CurrentMenu].Items = NULL;
+ Menus[CurrentMenu].func = func;
+ Menus[CurrentMenu].width = 0;
+ return CurrentMenu;
+}
+
+void glutSetMenu(int menu)
+{
+ CurrentMenu = menu;
+}
+
+int glutGetMenu(void)
+{
+ return CurrentMenu;
+}
+
+void glutDestroyMenu(int menu)
+{
+ if(menu == CurrentMenu)
+ CurrentMenu = 0;
+}
+
+static void NameMenuEntry(int entry, const char *name)
+{
+ int cm = CurrentMenu;
+ if(!(Menus[cm].Items[entry-1].name = realloc(Menus[cm].Items[entry-1].name,
+ strlen(name) + 1))) {
+ sprintf(exiterror, "realloc failed in NameMenuEntry\n");
+ exit(0);
+ }
+ strcpy(Menus[cm].Items[entry-1].name, name);
+ if(strlen(name) * MENU_FONT_WIDTH > Menus[cm].width)
+ Menus[cm].width = strlen(name) * MENU_FONT_WIDTH;
+}
+
+static int AddMenuItem(const char *name)
+{
+ int cm = CurrentMenu;
+ int item = Menus[cm].NumItems++;
+ if(!(Menus[cm].Items = realloc(Menus[cm].Items,
+ Menus[cm].NumItems * sizeof(*Menus[0].Items)))) {
+ sprintf(exiterror, "realloc failed in AddMenuItem\n");
+ exit(0);
+ }
+ Menus[cm].Items[item].name = NULL;
+ NameMenuEntry(item+1, name);
+ return item;
+}
+
+void glutAddMenuEntry(const char *name, int value)
+{
+ int item = AddMenuItem(name);
+ Menus[CurrentMenu].Items[item].value = value;
+ Menus[CurrentMenu].Items[item].submenu = 0;
+}
+
+void glutAddSubMenu(const char *name, int menu)
+{
+ int item = AddMenuItem(name);
+ if(menu == CurrentMenu) {
+ sprintf(exiterror, "Recursive menus not supported\n");
+ exit(0);
+ }
+ Menus[CurrentMenu].Items[item].submenu = menu;
+}
+
+void glutChangeToMenuEntry(int entry, const char *name, int value)
+{
+ NameMenuEntry(entry, name);
+ Menus[CurrentMenu].Items[entry-1].value = value;
+ Menus[CurrentMenu].Items[entry-1].submenu = 0;
+}
+
+void glutChangeToSubMenu(int entry, const char *name, int menu)
+{
+ NameMenuEntry(entry, name);
+ Menus[CurrentMenu].Items[entry-1].submenu = menu;
+}
+
+void glutRemoveMenuItem(int entry)
+{
+ memmove(Menus[CurrentMenu].Items + entry - 1,
+ Menus[CurrentMenu].Items + entry,
+ sizeof(*Menus[0].Items) * (Menus[CurrentMenu].NumItems - entry));
+ Menus[CurrentMenu].NumItems--;
+}
+
+void glutAttachMenu(int button)
+{
+ AttachedMenus[button] = CurrentMenu;
+}
+
+void glutDetachMenu(int button)
+{
+ AttachedMenus[button] = 0;
+}
+
+/* --------- Callbacks ------------ */
+void glutDisplayFunc(void (*func)(void))
+{
+ DisplayFunc = func;
+}
+
+void glutOverlayDisplayFunc(void (*func)(void))
+{
+}
+
+void glutReshapeFunc(void (*func)(int width, int height))
+{
+ ReshapeFunc = func;
+}
+
+void glutKeyboardFunc(void (*func)(unsigned char key, int x, int y))
+{
+ KeyboardFunc = func;
+}
+
+void glutMouseFunc(void (*func)(int button, int state, int x, int y))
+{
+ MouseEnabled = 1;
+ MouseFunc = func;
+}
+
+void glutMotionFunc(void (*func)(int x, int y))
+{
+ MouseEnabled = 1;
+ MotionFunc = func;
+}
+
+void glutPassiveMotionFunc(void (*func)(int x, int y))
+{
+ MouseEnabled = 1;
+ PassiveMotionFunc = func;
+}
+
+void glutVisibilityFunc(void (*func)(int state))
+{
+ VisibilityFunc = func;
+}
+
+void glutEntryFunc(void (*func)(int state))
+{
+}
+
+void glutSpecialFunc(void (*func)(int key, int x, int y))
+{
+ SpecialFunc = func;
+}
+
+void glutSpaceballMotionFunc(void (*func)(int x, int y, int z))
+{
+}
+
+void glutSpaceballRotateFunc(void (*func)(int x, int y, int z))
+{
+}
+
+void glutButtonBoxFunc(void (*func)(int button, int state))
+{
+}
+
+void glutDialsFunc(void (*func)(int dial, int value))
+{
+}
+
+void glutTabletMotionFunc(void (*func)(int x, int y))
+{
+}
+
+void glutTabletButtonFunc(void (*func)(int button, int state,
+ int x, int y))
+{
+}
+
+void glutMenuStatusFunc(void (*func)(int status, int x, int y))
+{
+ MenuStatusFunc = func;
+}
+
+void glutMenuStateFunc(void (*func)(int status))
+{
+ MenuStateFunc = func;
+}
+
+void glutIdleFunc(void (*func)(void))
+{
+ IdleFunc = func;
+}
+
+void glutTimerFunc(unsigned int msecs,
+ void (*func)(int value), int value)
+{
+ struct GlutTimer *timer = malloc(sizeof *timer);
+ timer->time = glutGet(GLUT_ELAPSED_TIME) + msecs;
+ timer->func = func;
+ timer->value = value;
+
+ struct GlutTimer **head = &GlutTimers;
+ while(*head && (*head)->time < timer->time)
+ head = &(*head)->next;
+
+ timer->next = *head;
+ *head = timer;
+}
+
+/* --------- Color Map ------------*/
+#define TOCMAP(x) (unsigned short)((x<0?0:x>1?1:x) * (GLfloat) (2<<16))
+#define FROMCMAP(x) (GLfloat)x / (GLfloat)(2<<16)
+
+void glutSetColor(int cell, GLfloat red, GLfloat green, GLfloat blue)
+{
+ if(cell >=0 && cell < 256) {
+
+ ColorMap.red[cell] = TOCMAP(red);
+ ColorMap.green[cell] = TOCMAP(green);
+ ColorMap.blue[cell] = TOCMAP(blue);
+
+ ColorMap.start = cell;
+ ColorMap.len = 1;
+
+ if (ioctl(FrameBufferFD, FBIOPUTCMAP, (void *) &ColorMap) < 0)
+ fprintf(stderr, "ioctl(FBIOPUTCMAP) failed [%d]\n", cell);
+ }
+}
+
+GLfloat glutGetColor(int cell, int component)
+{
+ if(!(DisplayMode & GLUT_INDEX))
+ return -1.0;
+
+ if(cell < 0 || cell > 256)
+ return -1.0;
+
+ switch(component) {
+ case GLUT_RED:
+ return FROMCMAP(ColorMap.red[cell]);
+ case GLUT_GREEN:
+ return FROMCMAP(ColorMap.green[cell]);
+ case GLUT_BLUE:
+ return FROMCMAP(ColorMap.blue[cell]);
+ }
+ return -1.0;
+}
+
+void glutCopyColormap(int win)
+{
+}
+
+/* --------- State ------------*/
+void glutWarpPointer(int x, int y)
+{
+ if(x < 0)
+ x = 0;
+ if(x >= VarInfo.xres)
+ x = VarInfo.xres - 1;
+ MouseX = x;
+
+ if(y < 0)
+ y = 0;
+ if(y >= VarInfo.yres)
+ y = VarInfo.yres - 1;
+ MouseY = y;
+
+ EraseCursor();
+ SwapCursor();
+}
+
+int glutGet(GLenum state)
+{
+ switch(state) {
+ case GLUT_WINDOW_X:
+ return 0;
+ case GLUT_WINDOW_Y:
+ return 0;
+ case GLUT_INIT_WINDOW_WIDTH:
+ case GLUT_WINDOW_WIDTH:
+ case GLUT_SCREEN_WIDTH:
+ return VarInfo.xres;
+ case GLUT_INIT_WINDOW_HEIGHT:
+ case GLUT_WINDOW_HEIGHT:
+ case GLUT_SCREEN_HEIGHT:
+ return VarInfo.yres;
+ case GLUT_WINDOW_BUFFER_SIZE:
+ return VarInfo.bits_per_pixel;
+ case GLUT_WINDOW_STENCIL_SIZE:
+ return StencilSize;
+ case GLUT_WINDOW_DEPTH_SIZE:
+ return DepthSize;
+ case GLUT_WINDOW_RED_SIZE:
+ return VarInfo.red.length;
+ case GLUT_WINDOW_GREEN_SIZE:
+ return VarInfo.green.length;
+ case GLUT_WINDOW_BLUE_SIZE:
+ return VarInfo.green.length;
+ case GLUT_WINDOW_ALPHA_SIZE:
+ return VarInfo.transp.length;
+ case GLUT_WINDOW_ACCUM_RED_SIZE:
+ case GLUT_WINDOW_ACCUM_GREEN_SIZE:
+ case GLUT_WINDOW_ACCUM_BLUE_SIZE:
+ case GLUT_WINDOW_ACCUM_ALPHA_SIZE:
+ return AccumSize;
+ case GLUT_WINDOW_DOUBLEBUFFER:
+ if(DisplayMode & GLUT_DOUBLE)
+ return 1;
+ return 0;
+ case GLUT_WINDOW_RGBA:
+ if(DisplayMode & GLUT_INDEX)
+ return 0;
+ return 1;
+ case GLUT_WINDOW_PARENT:
+ return 0;
+ case GLUT_WINDOW_NUM_CHILDREN:
+ return 0;
+ case GLUT_WINDOW_COLORMAP_SIZE:
+ if(DisplayMode & GLUT_INDEX)
+ return 256;
+ return 0;
+ case GLUT_WINDOW_NUM_SAMPLES:
+ return 0;
+ case GLUT_WINDOW_STEREO:
+ return 0;
+ case GLUT_WINDOW_CURSOR:
+ return CurrentCursor;
+ case GLUT_SCREEN_WIDTH_MM:
+ return VarInfo.width;
+ case GLUT_SCREEN_HEIGHT_MM:
+ return VarInfo.height;
+ case GLUT_MENU_NUM_ITEMS:
+ if(CurrentMenu)
+ return Menus[CurrentMenu].NumItems;
+ return 0;
+ case GLUT_DISPLAY_MODE_POSSIBLE:
+ if((DisplayMode & GLUT_MULTISAMPLE)
+ || (DisplayMode & GLUT_STEREO)
+ || (DisplayMode & GLUT_LUMINANCE)
+ || (DisplayMode & GLUT_ALPHA) && (DisplayMode & GLUT_INDEX))
+ return 0;
+ return 1;
+ case GLUT_INIT_DISPLAY_MODE:
+ return DisplayMode;
+ case GLUT_INIT_WINDOW_X:
+ case GLUT_INIT_WINDOW_Y:
+ return 0;
+ case GLUT_ELAPSED_TIME:
+ {
+ static struct timeval tv;
+ gettimeofday(&tv, 0);
+ return 1000 * (tv.tv_sec - StartTime.tv_sec)
+ + (tv.tv_usec - StartTime.tv_usec) / 1000;
+ }
+ }
+}
+
+int glutLayerGet(GLenum info)
+{
+ switch(info) {
+ case GLUT_OVERLAY_POSSIBLE:
+ return 0;
+ case GLUT_LAYER_IN_USE:
+ return GLUT_NORMAL;
+ case GLUT_HAS_OVERLAY:
+ return 0;
+ case GLUT_TRANSPARENT_INDEX:
+ return -1;
+ case GLUT_NORMAL_DAMAGED:
+ return Redisplay;
+ case GLUT_OVERLAY_DAMAGED:
+ return -1;
+ }
+ return -1;
+}
+
+int glutDeviceGet(GLenum info)
+{
+ switch(info) {
+ case GLUT_HAS_KEYBOARD:
+ return 1;
+ case GLUT_HAS_MOUSE:
+ case GLUT_NUM_MOUSE_BUTTONS:
+ return NumMouseButtons;
+ case GLUT_HAS_SPACEBALL:
+ case GLUT_HAS_DIAL_AND_BUTTON_BOX:
+ case GLUT_HAS_TABLET:
+ return 0;
+ case GLUT_NUM_SPACEBALL_BUTTONS:
+ case GLUT_NUM_BUTTON_BOX_BUTTONS:
+ case GLUT_NUM_DIALS:
+ case GLUT_NUM_TABLET_BUTTONS:
+ return 0;
+ }
+ return -1;
+}
+
+int glutGetModifiers(void){
+ return KeyboardModifiers;
+}
+
+/* ------------- extensions ------------ */
+int glutExtensionSupported(const char *extension)
+{
+ const char *exts = (const char *) glGetString(GL_EXTENSIONS);
+ const char *start = exts;
+ int len = strlen(extension);
+
+ for(;;) {
+ const char *p = strstr(exts, extension);
+ if(!p)
+ break;
+ if((p == start || p[-1] == ' ') && (p[len] == ' ' || p[len] == 0))
+ return 1;
+ exts = p + len;
+ }
+ return 0;
+}
+
+void glutReportErrors(void)
+{
+ GLenum error;
+
+ while ((error = glGetError()) != GL_NO_ERROR)
+ fprintf(stderr, "GL error: %s", gluErrorString(error));
+}
+
+static struct {
+ const char *name;
+ const GLUTproc address;
+} glut_functions[] = {
+ { "glutInit", (const GLUTproc) glutInit },
+ { "glutInitDisplayMode", (const GLUTproc) glutInitDisplayMode },
+ { "glutInitWindowPosition", (const GLUTproc) glutInitWindowPosition },
+ { "glutInitWindowSize", (const GLUTproc) glutInitWindowSize },
+ { "glutMainLoop", (const GLUTproc) glutMainLoop },
+ { "glutCreateWindow", (const GLUTproc) glutCreateWindow },
+ { "glutCreateSubWindow", (const GLUTproc) glutCreateSubWindow },
+ { "glutDestroyWindow", (const GLUTproc) glutDestroyWindow },
+ { "glutPostRedisplay", (const GLUTproc) glutPostRedisplay },
+ { "glutSwapBuffers", (const GLUTproc) glutSwapBuffers },
+ { "glutGetWindow", (const GLUTproc) glutGetWindow },
+ { "glutSetWindow", (const GLUTproc) glutSetWindow },
+ { "glutSetWindowTitle", (const GLUTproc) glutSetWindowTitle },
+ { "glutSetIconTitle", (const GLUTproc) glutSetIconTitle },
+ { "glutPositionWindow", (const GLUTproc) glutPositionWindow },
+ { "glutReshapeWindow", (const GLUTproc) glutReshapeWindow },
+ { "glutPopWindow", (const GLUTproc) glutPopWindow },
+ { "glutPushWindow", (const GLUTproc) glutPushWindow },
+ { "glutIconifyWindow", (const GLUTproc) glutIconifyWindow },
+ { "glutShowWindow", (const GLUTproc) glutShowWindow },
+ { "glutHideWindow", (const GLUTproc) glutHideWindow },
+ { "glutFullScreen", (const GLUTproc) glutFullScreen },
+ { "glutSetCursor", (const GLUTproc) glutSetCursor },
+ { "glutWarpPointer", (const GLUTproc) glutWarpPointer },
+ { "glutEstablishOverlay", (const GLUTproc) glutEstablishOverlay },
+ { "glutRemoveOverlay", (const GLUTproc) glutRemoveOverlay },
+ { "glutUseLayer", (const GLUTproc) glutUseLayer },
+ { "glutPostOverlayRedisplay", (const GLUTproc) glutPostOverlayRedisplay },
+ { "glutShowOverlay", (const GLUTproc) glutShowOverlay },
+ { "glutHideOverlay", (const GLUTproc) glutHideOverlay },
+ { "glutCreateMenu", (const GLUTproc) glutCreateMenu },
+ { "glutDestroyMenu", (const GLUTproc) glutDestroyMenu },
+ { "glutGetMenu", (const GLUTproc) glutGetMenu },
+ { "glutSetMenu", (const GLUTproc) glutSetMenu },
+ { "glutAddMenuEntry", (const GLUTproc) glutAddMenuEntry },
+ { "glutAddSubMenu", (const GLUTproc) glutAddSubMenu },
+ { "glutChangeToMenuEntry", (const GLUTproc) glutChangeToMenuEntry },
+ { "glutChangeToSubMenu", (const GLUTproc) glutChangeToSubMenu },
+ { "glutRemoveMenuItem", (const GLUTproc) glutRemoveMenuItem },
+ { "glutAttachMenu", (const GLUTproc) glutAttachMenu },
+ { "glutDetachMenu", (const GLUTproc) glutDetachMenu },
+ { "glutDisplayFunc", (const GLUTproc) glutDisplayFunc },
+ { "glutReshapeFunc", (const GLUTproc) glutReshapeFunc },
+ { "glutKeyboardFunc", (const GLUTproc) glutKeyboardFunc },
+ { "glutMouseFunc", (const GLUTproc) glutMouseFunc },
+ { "glutMotionFunc", (const GLUTproc) glutMotionFunc },
+ { "glutPassiveMotionFunc", (const GLUTproc) glutPassiveMotionFunc },
+ { "glutEntryFunc", (const GLUTproc) glutEntryFunc },
+ { "glutVisibilityFunc", (const GLUTproc) glutVisibilityFunc },
+ { "glutIdleFunc", (const GLUTproc) glutIdleFunc },
+ { "glutTimerFunc", (const GLUTproc) glutTimerFunc },
+ { "glutMenuStateFunc", (const GLUTproc) glutMenuStateFunc },
+ { "glutSpecialFunc", (const GLUTproc) glutSpecialFunc },
+ { "glutSpaceballRotateFunc", (const GLUTproc) glutSpaceballRotateFunc },
+ { "glutButtonBoxFunc", (const GLUTproc) glutButtonBoxFunc },
+ { "glutDialsFunc", (const GLUTproc) glutDialsFunc },
+ { "glutTabletMotionFunc", (const GLUTproc) glutTabletMotionFunc },
+ { "glutTabletButtonFunc", (const GLUTproc) glutTabletButtonFunc },
+ { "glutMenuStatusFunc", (const GLUTproc) glutMenuStatusFunc },
+ { "glutOverlayDisplayFunc", (const GLUTproc) glutOverlayDisplayFunc },
+ { "glutSetColor", (const GLUTproc) glutSetColor },
+ { "glutGetColor", (const GLUTproc) glutGetColor },
+ { "glutCopyColormap", (const GLUTproc) glutCopyColormap },
+ { "glutGet", (const GLUTproc) glutGet },
+ { "glutDeviceGet", (const GLUTproc) glutDeviceGet },
+ { "glutExtensionSupported", (const GLUTproc) glutExtensionSupported },
+ { "glutGetModifiers", (const GLUTproc) glutGetModifiers },
+ { "glutLayerGet", (const GLUTproc) glutLayerGet },
+ { "glutGetProcAddress", (const GLUTproc) glutGetProcAddress },
+ { "glutBitmapCharacter", (const GLUTproc) glutBitmapCharacter },
+ { "glutBitmapWidth", (const GLUTproc) glutBitmapWidth },
+ { "glutStrokeCharacter", (const GLUTproc) glutStrokeCharacter },
+ { "glutStrokeWidth", (const GLUTproc) glutStrokeWidth },
+ { "glutBitmapLength", (const GLUTproc) glutBitmapLength },
+ { "glutStrokeLength", (const GLUTproc) glutStrokeLength },
+ { "glutWireSphere", (const GLUTproc) glutWireSphere },
+ { "glutSolidSphere", (const GLUTproc) glutSolidSphere },
+ { "glutWireCone", (const GLUTproc) glutWireCone },
+ { "glutSolidCone", (const GLUTproc) glutSolidCone },
+ { "glutWireCube", (const GLUTproc) glutWireCube },
+ { "glutSolidCube", (const GLUTproc) glutSolidCube },
+ { "glutWireTorus", (const GLUTproc) glutWireTorus },
+ { "glutSolidTorus", (const GLUTproc) glutSolidTorus },
+ { "glutWireDodecahedron", (const GLUTproc) glutWireDodecahedron },
+ { "glutSolidDodecahedron", (const GLUTproc) glutSolidDodecahedron },
+ { "glutWireTeapot", (const GLUTproc) glutWireTeapot },
+ { "glutSolidTeapot", (const GLUTproc) glutSolidTeapot },
+ { "glutWireOctahedron", (const GLUTproc) glutWireOctahedron },
+ { "glutSolidOctahedron", (const GLUTproc) glutSolidOctahedron },
+ { "glutWireTetrahedron", (const GLUTproc) glutWireTetrahedron },
+ { "glutSolidTetrahedron", (const GLUTproc) glutSolidTetrahedron },
+ { "glutWireIcosahedron", (const GLUTproc) glutWireIcosahedron },
+ { "glutSolidIcosahedron", (const GLUTproc) glutSolidIcosahedron },
+ { "glutReportErrors", (const GLUTproc) glutReportErrors },
+ { NULL, NULL }
+};
+
+GLUTproc glutGetProcAddress(const char *procName)
+{
+ /* Try GLUT functions first */
+ int i;
+ for (i = 0; glut_functions[i].name; i++) {
+ if (strcmp(glut_functions[i].name, procName) == 0)
+ return glut_functions[i].address;
+ }
+
+ /* Try core GL functions */
+ return (GLUTproc) glFBDevGetProcAddress(procName);
+}