b23636c9 |
// MicroGlut is a stripped-down reimplementation of the classic GLUT/FreeGLUT library. By Ingemar Ragnemalm 2012-2015.
// This is the Linux version. There is also a Mac version (and drafts for a Windows version).
// Why do we use MicroGlut?
// 1) Transparency! Including it as a single source file makes it easy to see what it does.
// 2) Editability. Missing something? Want something to work a bit different? Just hack it in.
// 3) No obsolete stuff! The old GLUT has a lot of functions that are inherently obsolete. Avoid!
// Please note that this is still in an early stage. A lot of functionality is still missing.
// If you add/change something of interest, especially if it follows the GLUT API, please sumbit
// to me so I can consider adding that to the official version.
// 2012: Made a first draft by extracting context creation from other code.
// 130131: Fixed bugs in keyboard and update events. Seems to work pretty well! Things are missing, GL3 untested, but otherwise it runs stable with glutgears.
// 130206: Timers now tested and working. CPU load kept decent when not intentionally high.
// 130907: Bug fixes, test for non-existing gReshape, glutKeyboardUpFunc corrected.
// 130916: Fixed the event bug. Cleaned up most comments left from debugging.
// 130926: Cleaned up warnings, added missing #includes.
// 140130: A bit more cleanup for avoiding warnings (_BSD_SOURCE below).
// 140401: glutKeyIsDown and glutWarpPointer added and got an extra round of testing.
// 150205: glutRepeatingTimer new, better name for glutRepeatingTimerFunc
// Added a default glViewport call when the window is resized.
// Made a bug fix in the event processing so that mouse drag events won't stack up.
// Somewhere here I added a kind of full-screen support (but without removing window borders).
// 150216: Added proper OpenGL3 initalization. (Based on a patch by Sebastian Parborg.)
// 150223: Finally, decent handling on the GLUT configuration!
// 150227: Resize triggers an update!
// 150302: Window position, multisample, even better config
// 150618: Added glutMouseIsDown() (not in the old GLUT API but a nice extension!).
// Added #ifdefs to produce errors if compiled on the wrong platform!
// 150909: Added glutExit.
// 150924: Added support for special keys.
// 160302: Added glutShowCursor and glutHideCursor.
// 170405: Made some globals static.
// 170406: Added "const" to string arguments to make C++ happier. glutSpecialFunc and glutSpecialUpFunc are now officially supported - despite being deprecated. (I recommend that you use the same keyboard func for everything.) Added support for multiple mouse buttons (right and left).
// 170410: Modified glutWarpPointer to make it more robust. Commended out some unused variables to avoid warnings.
// 180124: Modifications to make it work better on recent MESA, which seems to have introduced some changes. Adds glFlush() in glutSwapBuffers and a timer when starting glutMain to invoke an update after 100 ms.
// 180208: Added GLUT_WINDOW_WIDTH, GLUT_WINDOW_HEIGHT, GLUT_MOUSE_POSITION_X and GLUT_MOUSE_POSITION_Y to GlutGet. They were already in the Mac version, so let's converge the versions a bit.
#define _DEFAULT_SOURCE
#include <math.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <GL/gl.h>
#define GLX_GLXEXT_PROTOTYPES
#include <GL/glext.h>
#include <X11/Xlib.h>
#include <X11/keysym.h>
#include <GL/glx.h>
#include "MicroGlut.h"
#include <sys/time.h>
#include <unistd.h>
#ifndef M_PI
#define M_PI 3.14159265
#endif
// If this is compiled on the Mac or Windows, tell me!
#ifdef __APPLE__
ERROR! This is NOT the Mac version of MicroGlut and will not work on the Mac!
#endif
#ifdef _WIN32
ERROR! This is NOT the Windows version of MicroGlut and will not work on Windows!
#endif
static unsigned int winWidth = 300, winHeight = 300;
static unsigned int winPosX = 40, winPosY = 40;
//static int mode = GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH;
static int gContextVersionMajor = 0;
static int gContextVersionMinor = 0;
static Display *dpy;
static Window win;
static GLXContext ctx;
//static char *dpyName = NULL;
int gMode; // NOT YET USED
static char animate = 1; // Use for glutNeedsRedisplay?
struct timeval timeStart;
static Atom wmDeleteMessage; // To handle delete msg
static char gKeymap[256];
static char gRunning = 1;
void glutInit(int *argc, char *argv[])
{
gettimeofday(&timeStart, NULL);
memset(gKeymap, 0, sizeof(gKeymap));
}
void glutInitDisplayMode(unsigned int mode)
{
gMode = mode; // NOT YET USED
}
void glutInitWindowSize(int w, int h)
{
winWidth = w;
winHeight = h;
}
void glutInitWindowPosition (int x, int y)
{
winPosX = x;
winPosY = y;
}
static void checktimers();
/*
* Create an RGB, double-buffered window.
* Return the window and context handles.
*/
static void
make_window( Display *dpy, const char *name,
int x, int y, int width, int height,
Window *winRet, GLXContext *ctxRet)
{
int scrnum;
XSetWindowAttributes attr;
unsigned long mask;
Window root;
Window win;
GLXContext ctx;
XVisualInfo *visinfo;
scrnum = DefaultScreen( dpy );
root = RootWindow( dpy, scrnum );
// 3.2 support
//#ifdef glXCreateContextAttribsARB
if (gContextVersionMajor > 2)
{
// We asked for OpenGL3+, but can we do it?
typedef GLXContext (*glXCreateContextAttribsARBProc)(Display*, GLXFBConfig, GLXContext, Bool, const int*);
// Verify GL driver supports glXCreateContextAttribsARB()
glXCreateContextAttribsARBProc glXCreateContextAttribsARB = 0;
// Verify that GLX implementation supports the new context create call
if ( strstr( glXQueryExtensionsString( dpy, scrnum ),
"GLX_ARB_create_context" ) != 0 )
glXCreateContextAttribsARB = (glXCreateContextAttribsARBProc)
glXGetProcAddress( (const GLubyte *) "glXCreateContextAttribsARB" );
if ( !glXCreateContextAttribsARB )
{
printf( "Can't create new-style GL context\n" );
}
// We need this for OpenGL3
int elemc;
GLXFBConfig *fbcfg;
int attribs[] = { GLX_RENDER_TYPE, GLX_RGBA_BIT,
GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT, // ?
GLX_RED_SIZE, 1, // 1 = prefer high precision
GLX_GREEN_SIZE, 1,
GLX_BLUE_SIZE, 1,
GLX_ALPHA_SIZE, 1,
None,
None,
None,
None,
None,
None,
None,
None,
None,
None,
None,
None,
None,
None,
None,
None };
int i = 12;
if (gMode & GLUT_DOUBLE)
{
attribs[i++] = GLX_DOUBLEBUFFER;
attribs[i++] = 1;
}
if (gMode & GLUT_DEPTH)
{
attribs[i++] = GLX_DEPTH_SIZE;
attribs[i++] = 1;
}
if (gMode & GLUT_STENCIL)
{
attribs[i++] = GLX_STENCIL_SIZE;
attribs[i++] = 8; // Smallest available, at least 8. Configurable setting needed!
}
if (gMode & GLUT_MULTISAMPLE)
{
attribs[i++] = GLX_SAMPLE_BUFFERS;
attribs[i++] = 1;
attribs[i++] = GLX_SAMPLES;
attribs[i++] = 4;
}
fbcfg = glXChooseFBConfig(dpy, scrnum, attribs, &elemc);
if (!fbcfg)
{
fbcfg = glXChooseFBConfig(dpy, scrnum, NULL, &elemc);
}
if (!fbcfg)
printf("Couldn't get FB configs\n");
int gl3attr[] =
{
GLX_CONTEXT_MAJOR_VERSION_ARB, gContextVersionMajor,
GLX_CONTEXT_MINOR_VERSION_ARB, gContextVersionMinor,
// GLX_CONTEXT_FLAGS_ARB, GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB,
GLX_CONTEXT_FLAGS_ARB, GLX_CONTEXT_DEBUG_BIT_ARB,
GLX_CONTEXT_PROFILE_MASK_ARB, GLX_CONTEXT_CORE_PROFILE_BIT_ARB,
None
};
ctx = glXCreateContextAttribsARB(dpy, fbcfg[0], NULL, 1, gl3attr);
if (ctx == NULL) printf("No ctx!\n");
visinfo = glXGetVisualFromFBConfig(dpy, fbcfg[0]);
if (!visinfo)
printf("Error: couldn't create OpenGL window with this pixel format.\n");
}
else // old style
//#endif
{
int attribs[] = { GLX_RGBA,
GLX_RED_SIZE, 1, // 1 = prefer high precision
GLX_GREEN_SIZE, 1,
GLX_BLUE_SIZE, 1,
None,
None,
None,
None,
None,
None,
None,
None,
None,
None,
None,
None,
None,
None,
None,
None };
int i = 7;
if (gMode & GLUT_DOUBLE)
attribs[i++] = GLX_DOUBLEBUFFER;
if (gMode & GLUT_DEPTH)
{
attribs[i++] = GLX_DEPTH_SIZE;
attribs[i++] = 1;
}
if (gMode & GLUT_STENCIL)
{
attribs[i++] = GLX_STENCIL_SIZE;
attribs[i++] = 8; // Smallest available, at least 8. Configurable setting needed!
}
visinfo = glXChooseVisual( dpy, scrnum, attribs );
if (!visinfo)
{
printf("Error: couldn't get a visual according to settings\n");
exit(1);
}
ctx = glXCreateContext( dpy, visinfo, 0, True );
if (ctx == NULL) printf("No ctx!\n");
}
/* window attributes */
attr.background_pixel = 0;
attr.border_pixel = 0;
attr.colormap = XCreateColormap( dpy, root, visinfo->visual, AllocNone);
attr.event_mask = StructureNotifyMask | ExposureMask | KeyPressMask | KeyReleaseMask | ButtonPress | ButtonReleaseMask | Button1MotionMask | PointerMotionMask;
attr.override_redirect = 0;
mask = CWBackPixel | CWBorderPixel | CWColormap | CWEventMask | CWOverrideRedirect;
win = XCreateWindow( dpy, root, x, y, width, height,
0, visinfo->depth, InputOutput,
visinfo->visual, mask, &attr );
// Register delete!
wmDeleteMessage = XInternAtom(dpy, "WM_DELETE_WINDOW", False);
XSetWMProtocols(dpy, win, &wmDeleteMessage, 1); // Register
/* set hints and properties */
XSizeHints sizehints;
sizehints.x = x;
sizehints.y = y;
sizehints.width = width;
sizehints.height = height;
sizehints.flags = USSize | USPosition;
XSetNormalHints(dpy, win, &sizehints);
XSetStandardProperties(dpy, win, name, name,
None, (char **)NULL, 0, &sizehints);
if (!ctx)
{
printf("Error: glXCreateContext failed\n");
exit(1);
}
XFree(visinfo);
*winRet = win;
*ctxRet = ctx;
}
void glutCreateWindow(const char *windowTitle)
{
dpy = XOpenDisplay(NULL);
if (!dpy)
{
printf("Error: couldn't open display %s\n",
windowTitle ? windowTitle : getenv("DISPLAY"));
}
make_window(dpy, windowTitle, winPosX, winPosY, winWidth, winHeight, &win, &ctx);
XMapWindow(dpy, win);
glXMakeCurrent(dpy, win, ctx);
}
void (*gDisplay)(void);
void (*gReshape)(int width, int height);
void (*gIdle)(void);
void (*gKey)(unsigned char key, int x, int y);
void (*gKeyUp)(unsigned char key, int x, int y);
void (*gSpecialKey)(unsigned char key, int x, int y);
void (*gSpecialKeyUp)(unsigned char key, int x, int y);
void (*gMouseMoved)(int x, int y);
void (*gMouseDragged)(int x, int y);
void (*gMouseFunc)(int button, int state, int x, int y);
int gLastMousePositionX, gLastMousePositionY; // Avoids a problem with glutWarpPointer
// Maybe I should just drop these for simplicity
//void (*gSpecialKey)(unsigned char key, int x, int y) = NULL;
//void (*gSpecialKeyUp)(unsigned char key, int x, int y) = NULL;
void glutReshapeFunc(void (*func)(int width, int height))
{
gReshape = func;
}
void glutDisplayFunc(void (*func)(void))
{
gDisplay = func;
}
void glutIdleFunc(void (*func)(void))
{gIdle = func;}
void glutKeyboardFunc(void (*func)(unsigned char key, int x, int y))
{
gKey = func;
}
void glutKeyboardUpFunc(void (*func)(unsigned char key, int x, int y))
{
gKeyUp = func;
}
void glutSpecialFunc(void (*func)(unsigned char key, int x, int y))
{
gSpecialKey = func;
}
void glutSpecialUpFunc(void (*func)(unsigned char key, int x, int y))
{
gSpecialKeyUp = func;
}
void glutMouseFunc(void (*func)(int button, int state, int x, int y))
{gMouseFunc = func;}
void glutMotionFunc(void (*func)(int x, int y))
{gMouseDragged = func;}
void glutPassiveMotionFunc(void (*func)(int x, int y))
{gMouseMoved = func;}
char gButtonPressed[10] = {0,0,0,0,0,0,0,0,0,0};
void doKeyboardEvent(XEvent event, void (*keyProc)(unsigned char key, int x, int y), void (*specialKeyProc)(unsigned char key, int x, int y), int keyMapValue)
{
char buffer[10];
// int r; // code;
int code = ((XKeyEvent *)&event)->keycode;
// r =
XLookupString(&event.xkey, buffer, sizeof(buffer), NULL, NULL);
char raw = buffer[0]; // Before remapping
switch(code)
{
case 111: buffer[0] = GLUT_KEY_UP; break;
case 114: buffer[0] = GLUT_KEY_RIGHT; break;
case 116: buffer[0] = GLUT_KEY_DOWN; break;
case 113: buffer[0] = GLUT_KEY_LEFT; break;
case 67: buffer[0] = GLUT_KEY_F1; break;
case 68: buffer[0] = GLUT_KEY_F2; break;
case 69: buffer[0] = GLUT_KEY_F3; break;
case 70: buffer[0] = GLUT_KEY_F4; break;
case 71: buffer[0] = GLUT_KEY_F5; break;
case 72: buffer[0] = GLUT_KEY_F6; break;
case 73: buffer[0] = GLUT_KEY_F7; break;
case 112: buffer[0] = GLUT_KEY_PAGE_UP; break;
case 117: buffer[0] = GLUT_KEY_PAGE_DOWN; break;
case 110: buffer[0] = GLUT_KEY_HOME; break;
case 115: buffer[0] = GLUT_KEY_END; break;
case 118: buffer[0] = GLUT_KEY_INSERT; break;
case 50: buffer[0] = GLUT_KEY_LEFT_SHIFT; break;
case 62: buffer[0] = GLUT_KEY_RIGHT_SHIFT; break;
case 37:case 105: buffer[0] = GLUT_KEY_CONTROL; break;
case 64:case 108: buffer[0] = GLUT_KEY_ALT; break;
case 133:case 134: buffer[0] = GLUT_KEY_COMMAND; break;
// Keypad
case 90: buffer[0] = GLUT_KEY_INSERT; break;
case 87: buffer[0] = GLUT_KEY_END; break;
case 88: buffer[0] = GLUT_KEY_DOWN; break;
case 89: buffer[0] = GLUT_KEY_PAGE_DOWN; break;
case 83: buffer[0] = GLUT_KEY_LEFT; break;
// case 84: buffer[0] = GLUT_KEY_KEYPAD_5; break;
case 85: buffer[0] = GLUT_KEY_RIGHT; break;
case 79: buffer[0] = GLUT_KEY_HOME; break;
case 80: buffer[0] = GLUT_KEY_UP; break;
case 81: buffer[0] = GLUT_KEY_PAGE_UP; break;
case 82: buffer[0] = 127; break;
// case 77: buffer[0] = GLUT_KEY_KEYPAD_NUMLOCK; break;
}
// If we asked for a separate callback for special ketys, call it. Otherwise call the standard one.
// I am considering removing the special callback for simplicity!
if (raw == 0)
{
if (specialKeyProc)
specialKeyProc(buffer[0], 0, 0);
else
if (keyProc)
keyProc(buffer[0], 0, 0);
}
else
if (keyProc)
keyProc(buffer[0], 0, 0);
gKeymap[(int)buffer[0]] = keyMapValue;
// printf("%c %d %d %d\n", buffer[0], buffer[0], r, code);
// if (event.type == KeyPress)
// { if (gKey) gKey(buffer[0], 0, 0); gKeymap[(int)buffer[0]] = 1;}
// else
// { if (gKeyUp) gKeyUp(buffer[0], 0, 0); gKeymap[(int)buffer[0]] = 0;}
}
void internaltimer(int x)
{
glutPostRedisplay();
}
void glutMainLoop()
{
char pressed = 0;
int i;
XAllowEvents(dpy, AsyncBoth, CurrentTime);
// 2018-01-24: An attempt to patch over the problem that recent MESA tends to fail the first update.
glutTimerFunc(100, internaltimer, 0);
while (gRunning)
{
// int op = 0;
while (XPending(dpy) > 0)
{
XEvent event;
XNextEvent(dpy, &event);
switch (event.type)
{
case ClientMessage:
if (event.xclient.data.l[0] == wmDeleteMessage) // quit!
gRunning = 0;
break;
case Expose:
// op = 1;
break; // Update event! Should do draw here.
case ConfigureNotify:
if (gReshape)
gReshape(event.xconfigure.width, event.xconfigure.height);
else
{
glViewport(0, 0, event.xconfigure.width, event.xconfigure.height);
}
animate = 1;
winWidth = event.xconfigure.width;
winHeight = event.xconfigure.height;
break;
case KeyPress:
doKeyboardEvent(event, gKey, gSpecialKey, 1);break;
case KeyRelease:
doKeyboardEvent(event, gKeyUp, gSpecialKeyUp, 0);break;
case ButtonPress:
gButtonPressed[event.xbutton.button] = 1;
if (gMouseFunc != NULL)
switch (event.xbutton.button)
{
case Button1:
gMouseFunc(GLUT_LEFT_BUTTON, GLUT_DOWN, event.xbutton.x, event.xbutton.y);break;
case Button2:
gMouseFunc(GLUT_MIDDLE_BUTTON, GLUT_DOWN, event.xbutton.x, event.xbutton.y);break;
case Button3:
gMouseFunc(GLUT_RIGHT_BUTTON, GLUT_DOWN, event.xbutton.x, event.xbutton.y);break;
}
break;
case ButtonRelease:
gButtonPressed[event.xbutton.button] = 0;
if (gMouseFunc != NULL)
switch (event.xbutton.button)
{
case Button1:
gMouseFunc(GLUT_LEFT_BUTTON, GLUT_UP, event.xbutton.x, event.xbutton.y);break;
case Button2:
gMouseFunc(GLUT_MIDDLE_BUTTON, GLUT_UP, event.xbutton.x, event.xbutton.y);break;
case Button3:
gMouseFunc(GLUT_RIGHT_BUTTON, GLUT_UP, event.xbutton.x, event.xbutton.y);break;
}
break;
case MotionNotify:
pressed = 0;
for (i = 0; i < 5; i++)
if (gButtonPressed[i]) pressed = 1;
// Saving the last known position in order to avoid problems for glutWarpPointer
// If we try warping to this position, don't!
gLastMousePositionX = event.xbutton.x;
gLastMousePositionY = event.xbutton.y;
if (pressed && gMouseDragged)
gMouseDragged(event.xbutton.x, event.xbutton.y);
else
if (gMouseMoved)
gMouseMoved(event.xbutton.x, event.xbutton.y);
break;
default:
break;
}
}
if (animate)
{
animate = 0;
if (gDisplay)
gDisplay();
else
printf("No display function!\n");
// op = 0;
}
else
if (gIdle) gIdle();
checktimers();
}
glXMakeCurrent(dpy, None, NULL);
glXDestroyContext(dpy, ctx);
XDestroyWindow(dpy, win);
XCloseDisplay(dpy);
}
void glutSwapBuffers()
{
glFlush(); // Added 2018-01-24, part of a fix for new MESA
glXSwapBuffers(dpy, win);
}
void glutPostRedisplay()
{
animate = 1;
}
int glutGet(int type)
{
struct timeval tv;
switch (type)
{
case GLUT_ELAPSED_TIME:
gettimeofday(&tv, NULL);
return (tv.tv_usec - timeStart.tv_usec) / 1000 + (tv.tv_sec - timeStart.tv_sec)*1000;
case GLUT_WINDOW_WIDTH:
return winWidth;
case GLUT_WINDOW_HEIGHT:
return winHeight;
case GLUT_MOUSE_POSITION_X:
return gLastMousePositionX;
case GLUT_MOUSE_POSITION_Y:
return gLastMousePositionY;
}
return 0;
}
// NOTE: The timer is not designed with any multithreading in mind!
typedef struct TimerRec
{
int arg;
int time;
int repeatTime;
void (*func)(int arg);
char repeating;
struct TimerRec *next;
struct TimerRec *prev;
} TimerRec;
TimerRec *gTimers = NULL;
void glutTimerFunc(int millis, void (*func)(int arg), int arg)
{
TimerRec *t = (TimerRec *)malloc(sizeof(TimerRec));
t->arg = arg;
t->time = millis + glutGet(GLUT_ELAPSED_TIME);
t->repeatTime = 0;
t->repeating = 0;
t->func = func;
t->next = gTimers;
t->prev = NULL;
if (gTimers != NULL)
gTimers->prev = t;
gTimers = t;
}
// Added by Ingemar
// void glutRepeatingTimerFunc(int millis)
void glutRepeatingTimer(int millis)
{
TimerRec *t = (TimerRec *)malloc(sizeof(TimerRec));
t->arg = 0;
t->time = millis + glutGet(GLUT_ELAPSED_TIME);
t->repeatTime = millis;
t->repeating = 1;
t->func = NULL;
t->next = gTimers;
t->prev = NULL;
if (gTimers != NULL)
gTimers->prev = t;
gTimers = t;
}
static void checktimers()
{
if (gTimers != NULL)
{
TimerRec *t, *firethis = NULL;
int now = glutGet(GLUT_ELAPSED_TIME);
int nextTime = now + 1000; // Distant future, 1 second
t = gTimers;
for (t = gTimers; t != NULL; t = t->next)
{
if (t->time < nextTime) nextTime = t->time; // Time for the next one
if (t->time < now) // See if this is due to fire
{
firethis = t;
}
}
if (firethis != NULL)
{
// Fire the timer
if (firethis->func != NULL)
firethis->func(firethis->arg);
else
glutPostRedisplay();
// Remove the timer if it was one-shot, otherwise update the time
if (firethis->repeating)
{
firethis->time = now + firethis->repeatTime;
}
else
{
if (firethis->prev != NULL)
firethis->prev->next = firethis->next;
else
gTimers = firethis->next;
if (firethis->next != NULL)
firethis->next->prev = firethis->prev;
free(firethis);
}
}
// Otherwise, sleep until any timer should fire
if (!animate)
if (nextTime > now)
{
usleep((nextTime - now)*1000);
}
}
else
// If no timer and no update, sleep a little to keep CPU load low
if (!animate)
usleep(10);
}
void glutInitContextVersion(int major, int minor)
{
gContextVersionMajor = major;
gContextVersionMinor = minor;
}
// Based on FreeGlut glutWarpPointer, but with a significant improvement!
/*
* Moves the mouse pointer to given window coordinates
*/
void glutWarpPointer( int x, int y )
{
if (dpy == NULL)
{
fprintf(stderr, "glutWarpPointer failed: MicroGlut not initialized!\n");
return;
}
if (x == gLastMousePositionX && y == gLastMousePositionY)
return; // Don't warp to where we already are - this causes event flooding!
XWarpPointer(
dpy, // fgDisplay.Display,
None,
win, // fgStructure.CurrentWindow->Window.Handle,
0, 0, 0, 0,
x, y
);
/* Make the warp visible immediately. */
XFlush( dpy );
// XFlush( fgDisplay.Display );
}
// Replaces glutSetMousePointer. This limits us to the two most common cases: None and arrow!
void glutShowCursor()
{
XUndefineCursor(dpy, win);
}
void glutHideCursor()
{
if (dpy == NULL)
{
printf("glutHideCursor failed: MicroGlut not initialized!\n");
return;
}
Cursor invisibleCursor;
Pixmap bitmapNoData;
static char noll[] = { 0,0,0};
bitmapNoData = XCreateBitmapFromData(dpy, win, noll, 1, 1);
invisibleCursor = XCreatePixmapCursor(dpy,bitmapNoData, bitmapNoData,
(XColor *)noll, (XColor *)noll, 0, 0);
XDefineCursor(dpy,win, invisibleCursor);
XFreeCursor(dpy, invisibleCursor);
XFreePixmap(dpy, bitmapNoData);
}
char glutKeyIsDown(unsigned char c)
{
return gKeymap[(unsigned int)c];
}
// Added by the Risinger/R\8Cberg/Wikstr\9Am project! But... gButtonPressed
// was already here! Did I miss something?
char glutMouseIsDown(unsigned char c)
{
return gButtonPressed[(unsigned int)c];
}
// These were missing up to 150205
void glutReshapeWindow(int width, int height)
{
XResizeWindow(dpy, win, width, height);
}
void glutPositionWindow(int x, int y)
{
XMoveWindow(dpy, win, x, y);
}
void glutSetWindowTitle(char *title)
{
XStoreName(dpy, win, title);
}
// Not complete full screen mode yet since the window frame and menu are not hidden yet
char gFullScreen = 0;
unsigned int savedHeight, savedWidth;
int savedX, savedY;
void glutFullScreen()
{
gFullScreen = 1;
Drawable d;
unsigned int a, b;
XGetGeometry(dpy, win, &d, &savedX, &savedY, &savedWidth, &savedHeight, &a, &b);
int scrnum = DefaultScreen(dpy);
int width = DisplayWidth( dpy, scrnum );
int height = DisplayHeight( dpy, scrnum );
XMoveResizeWindow(dpy, win, 0, 0, width, height);
}
void glutExitFullScreen()
{
gFullScreen = 0;
XMoveResizeWindow(dpy, win, savedX, savedY, savedWidth, savedHeight);
}
void glutToggleFullScreen()
{
if (gFullScreen)
glutExitFullScreen();
else
glutFullScreen();
}
void glutExit()
{
gRunning = 0;
}
|