diff options
Diffstat (limited to 'src/glut/fbdev/glut_fbdev.c')
-rw-r--r-- | src/glut/fbdev/glut_fbdev.c | 2199 |
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 0000000..3ed8fe9 --- /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); +} |