We use glXGetProcAddress to load glXCreateContextAttribsARB ourselves,
so GLX_GLXEXT_PROTOTYPES is not needed.
| ... | ... |
@@ -42,9 +42,8 @@ |
| 42 | 42 |
#include <stdlib.h> |
| 43 | 43 |
#include <stdio.h> |
| 44 | 44 |
#include <string.h> |
| 45 |
+#define GL_GLEXT_PROTOTYPES |
|
| 45 | 46 |
#include <GL/gl.h> |
| 46 |
-#define GLX_GLXEXT_PROTOTYPES |
|
| 47 |
-#include <GL/glext.h> |
|
| 48 | 47 |
#include <X11/Xlib.h> |
| 49 | 48 |
#include <X11/keysym.h> |
| 50 | 49 |
#include <GL/glx.h> |
| ... | ... |
@@ -83,6 +83,8 @@ static char gRunning = 1; |
| 83 | 83 |
|
| 84 | 84 |
void glutInit(int *argc, char *argv[]) |
| 85 | 85 |
{
|
| 86 |
+ (void)argc; |
|
| 87 |
+ (void)argv; |
|
| 86 | 88 |
gettimeofday(&timeStart, NULL); |
| 87 | 89 |
memset(gKeymap, 0, sizeof(gKeymap)); |
| 88 | 90 |
} |
| ... | ... |
@@ -110,16 +112,13 @@ static void checktimers(); |
| 110 | 112 |
*/ |
| 111 | 113 |
|
| 112 | 114 |
static void |
| 113 |
-make_window( Display *dpy, const char *name, |
|
| 114 |
- int x, int y, int width, int height, |
|
| 115 |
- Window *winRet, GLXContext *ctxRet) |
|
| 115 |
+make_window( const char *name, |
|
| 116 |
+ int x, int y, int width, int height) |
|
| 116 | 117 |
{
|
| 117 | 118 |
int scrnum; |
| 118 | 119 |
XSetWindowAttributes attr; |
| 119 | 120 |
unsigned long mask; |
| 120 | 121 |
Window root; |
| 121 |
- Window win; |
|
| 122 |
- GLXContext ctx; |
|
| 123 | 122 |
XVisualInfo *visinfo; |
| 124 | 123 |
|
| 125 | 124 |
scrnum = DefaultScreen( dpy ); |
| ... | ... |
@@ -305,9 +304,6 @@ make_window( Display *dpy, const char *name, |
| 305 | 304 |
} |
| 306 | 305 |
|
| 307 | 306 |
XFree(visinfo); |
| 308 |
- |
|
| 309 |
- *winRet = win; |
|
| 310 |
- *ctxRet = ctx; |
|
| 311 | 307 |
} |
| 312 | 308 |
|
| 313 | 309 |
void glutCreateWindow(const char *windowTitle) |
| ... | ... |
@@ -319,7 +315,7 @@ void glutCreateWindow(const char *windowTitle) |
| 319 | 315 |
windowTitle ? windowTitle : getenv("DISPLAY"));
|
| 320 | 316 |
} |
| 321 | 317 |
|
| 322 |
- make_window(dpy, windowTitle, winPosX, winPosY, winWidth, winHeight, &win, &ctx); |
|
| 318 |
+ make_window(windowTitle, winPosX, winPosY, winWidth, winHeight); |
|
| 323 | 319 |
|
| 324 | 320 |
XMapWindow(dpy, win); |
| 325 | 321 |
glXMakeCurrent(dpy, win, ctx); |
| ... | ... |
@@ -455,6 +451,7 @@ void doKeyboardEvent(XEvent event, void (*keyProc)(unsigned char key, int x, int |
| 455 | 451 |
|
| 456 | 452 |
void internaltimer(int x) |
| 457 | 453 |
{
|
| 454 |
+ (void)x; |
|
| 458 | 455 |
glutPostRedisplay(); |
| 459 | 456 |
} |
| 460 | 457 |
|
| ... | ... |
@@ -479,7 +479,7 @@ void glutMainLoop() |
| 479 | 479 |
switch (event.type) |
| 480 | 480 |
{
|
| 481 | 481 |
case ClientMessage: |
| 482 |
- if (event.xclient.data.l[0] == wmDeleteMessage) // quit! |
|
| 482 |
+ if ((Atom)event.xclient.data.l[0] == wmDeleteMessage) // quit! |
|
| 483 | 483 |
gRunning = 0; |
| 484 | 484 |
break; |
| 485 | 485 |
case Expose: |
find . -type f -exec chmod -x {} +
| 1 | 1 |
new file mode 100755 |
| ... | ... |
@@ -0,0 +1,831 @@ |
| 1 |
+// MicroGlut is a stripped-down reimplementation of the classic GLUT/FreeGLUT library. By Ingemar Ragnemalm 2012-2015. |
|
| 2 |
+// This is the Linux version. There is also a Mac version (and drafts for a Windows version). |
|
| 3 |
+ |
|
| 4 |
+// Why do we use MicroGlut? |
|
| 5 |
+// 1) Transparency! Including it as a single source file makes it easy to see what it does. |
|
| 6 |
+// 2) Editability. Missing something? Want something to work a bit different? Just hack it in. |
|
| 7 |
+// 3) No obsolete stuff! The old GLUT has a lot of functions that are inherently obsolete. Avoid! |
|
| 8 |
+ |
|
| 9 |
+// Please note that this is still in an early stage. A lot of functionality is still missing. |
|
| 10 |
+// If you add/change something of interest, especially if it follows the GLUT API, please sumbit |
|
| 11 |
+// to me so I can consider adding that to the official version. |
|
| 12 |
+ |
|
| 13 |
+// 2012: Made a first draft by extracting context creation from other code. |
|
| 14 |
+// 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. |
|
| 15 |
+// 130206: Timers now tested and working. CPU load kept decent when not intentionally high. |
|
| 16 |
+// 130907: Bug fixes, test for non-existing gReshape, glutKeyboardUpFunc corrected. |
|
| 17 |
+// 130916: Fixed the event bug. Cleaned up most comments left from debugging. |
|
| 18 |
+// 130926: Cleaned up warnings, added missing #includes. |
|
| 19 |
+// 140130: A bit more cleanup for avoiding warnings (_BSD_SOURCE below). |
|
| 20 |
+// 140401: glutKeyIsDown and glutWarpPointer added and got an extra round of testing. |
|
| 21 |
+// 150205: glutRepeatingTimer new, better name for glutRepeatingTimerFunc |
|
| 22 |
+// Added a default glViewport call when the window is resized. |
|
| 23 |
+// Made a bug fix in the event processing so that mouse drag events won't stack up. |
|
| 24 |
+// Somewhere here I added a kind of full-screen support (but without removing window borders). |
|
| 25 |
+// 150216: Added proper OpenGL3 initalization. (Based on a patch by Sebastian Parborg.) |
|
| 26 |
+// 150223: Finally, decent handling on the GLUT configuration! |
|
| 27 |
+// 150227: Resize triggers an update! |
|
| 28 |
+// 150302: Window position, multisample, even better config |
|
| 29 |
+// 150618: Added glutMouseIsDown() (not in the old GLUT API but a nice extension!). |
|
| 30 |
+// Added #ifdefs to produce errors if compiled on the wrong platform! |
|
| 31 |
+// 150909: Added glutExit. |
|
| 32 |
+// 150924: Added support for special keys. |
|
| 33 |
+// 160302: Added glutShowCursor and glutHideCursor. |
|
| 34 |
+// 170405: Made some globals static. |
|
| 35 |
+// 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). |
|
| 36 |
+// 170410: Modified glutWarpPointer to make it more robust. Commended out some unused variables to avoid warnings. |
|
| 37 |
+// 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. |
|
| 38 |
+// 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. |
|
| 39 |
+ |
|
| 40 |
+#define _DEFAULT_SOURCE |
|
| 41 |
+#include <math.h> |
|
| 42 |
+#include <stdlib.h> |
|
| 43 |
+#include <stdio.h> |
|
| 44 |
+#include <string.h> |
|
| 45 |
+#include <GL/gl.h> |
|
| 46 |
+#define GLX_GLXEXT_PROTOTYPES |
|
| 47 |
+#include <GL/glext.h> |
|
| 48 |
+#include <X11/Xlib.h> |
|
| 49 |
+#include <X11/keysym.h> |
|
| 50 |
+#include <GL/glx.h> |
|
| 51 |
+#include "MicroGlut.h" |
|
| 52 |
+#include <sys/time.h> |
|
| 53 |
+#include <unistd.h> |
|
| 54 |
+ |
|
| 55 |
+#ifndef M_PI |
|
| 56 |
+#define M_PI 3.14159265 |
|
| 57 |
+#endif |
|
| 58 |
+ |
|
| 59 |
+// If this is compiled on the Mac or Windows, tell me! |
|
| 60 |
+#ifdef __APPLE__ |
|
| 61 |
+ ERROR! This is NOT the Mac version of MicroGlut and will not work on the Mac! |
|
| 62 |
+#endif |
|
| 63 |
+#ifdef _WIN32 |
|
| 64 |
+ ERROR! This is NOT the Windows version of MicroGlut and will not work on Windows! |
|
| 65 |
+#endif |
|
| 66 |
+ |
|
| 67 |
+static unsigned int winWidth = 300, winHeight = 300; |
|
| 68 |
+static unsigned int winPosX = 40, winPosY = 40; |
|
| 69 |
+//static int mode = GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH; |
|
| 70 |
+static int gContextVersionMajor = 0; |
|
| 71 |
+static int gContextVersionMinor = 0; |
|
| 72 |
+ |
|
| 73 |
+static Display *dpy; |
|
| 74 |
+static Window win; |
|
| 75 |
+static GLXContext ctx; |
|
| 76 |
+//static char *dpyName = NULL; |
|
| 77 |
+int gMode; // NOT YET USED |
|
| 78 |
+static char animate = 1; // Use for glutNeedsRedisplay? |
|
| 79 |
+struct timeval timeStart; |
|
| 80 |
+static Atom wmDeleteMessage; // To handle delete msg |
|
| 81 |
+static char gKeymap[256]; |
|
| 82 |
+static char gRunning = 1; |
|
| 83 |
+ |
|
| 84 |
+void glutInit(int *argc, char *argv[]) |
|
| 85 |
+{
|
|
| 86 |
+ gettimeofday(&timeStart, NULL); |
|
| 87 |
+ memset(gKeymap, 0, sizeof(gKeymap)); |
|
| 88 |
+} |
|
| 89 |
+void glutInitDisplayMode(unsigned int mode) |
|
| 90 |
+{
|
|
| 91 |
+ gMode = mode; // NOT YET USED |
|
| 92 |
+} |
|
| 93 |
+void glutInitWindowSize(int w, int h) |
|
| 94 |
+{
|
|
| 95 |
+ winWidth = w; |
|
| 96 |
+ winHeight = h; |
|
| 97 |
+} |
|
| 98 |
+ |
|
| 99 |
+void glutInitWindowPosition (int x, int y) |
|
| 100 |
+{
|
|
| 101 |
+ winPosX = x; |
|
| 102 |
+ winPosY = y; |
|
| 103 |
+} |
|
| 104 |
+ |
|
| 105 |
+static void checktimers(); |
|
| 106 |
+ |
|
| 107 |
+/* |
|
| 108 |
+ * Create an RGB, double-buffered window. |
|
| 109 |
+ * Return the window and context handles. |
|
| 110 |
+ */ |
|
| 111 |
+ |
|
| 112 |
+static void |
|
| 113 |
+make_window( Display *dpy, const char *name, |
|
| 114 |
+ int x, int y, int width, int height, |
|
| 115 |
+ Window *winRet, GLXContext *ctxRet) |
|
| 116 |
+{
|
|
| 117 |
+ int scrnum; |
|
| 118 |
+ XSetWindowAttributes attr; |
|
| 119 |
+ unsigned long mask; |
|
| 120 |
+ Window root; |
|
| 121 |
+ Window win; |
|
| 122 |
+ GLXContext ctx; |
|
| 123 |
+ XVisualInfo *visinfo; |
|
| 124 |
+ |
|
| 125 |
+ scrnum = DefaultScreen( dpy ); |
|
| 126 |
+ root = RootWindow( dpy, scrnum ); |
|
| 127 |
+ |
|
| 128 |
+// 3.2 support |
|
| 129 |
+//#ifdef glXCreateContextAttribsARB |
|
| 130 |
+ if (gContextVersionMajor > 2) |
|
| 131 |
+ {
|
|
| 132 |
+// We asked for OpenGL3+, but can we do it? |
|
| 133 |
+ |
|
| 134 |
+ typedef GLXContext (*glXCreateContextAttribsARBProc)(Display*, GLXFBConfig, GLXContext, Bool, const int*); |
|
| 135 |
+ |
|
| 136 |
+ // Verify GL driver supports glXCreateContextAttribsARB() |
|
| 137 |
+ glXCreateContextAttribsARBProc glXCreateContextAttribsARB = 0; |
|
| 138 |
+ |
|
| 139 |
+ // Verify that GLX implementation supports the new context create call |
|
| 140 |
+ if ( strstr( glXQueryExtensionsString( dpy, scrnum ), |
|
| 141 |
+ "GLX_ARB_create_context" ) != 0 ) |
|
| 142 |
+ glXCreateContextAttribsARB = (glXCreateContextAttribsARBProc) |
|
| 143 |
+ glXGetProcAddress( (const GLubyte *) "glXCreateContextAttribsARB" ); |
|
| 144 |
+ |
|
| 145 |
+ if ( !glXCreateContextAttribsARB ) |
|
| 146 |
+ {
|
|
| 147 |
+ printf( "Can't create new-style GL context\n" ); |
|
| 148 |
+ } |
|
| 149 |
+ |
|
| 150 |
+// We need this for OpenGL3 |
|
| 151 |
+ int elemc; |
|
| 152 |
+ GLXFBConfig *fbcfg; |
|
| 153 |
+ |
|
| 154 |
+ int attribs[] = { GLX_RENDER_TYPE, GLX_RGBA_BIT,
|
|
| 155 |
+ GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT, // ? |
|
| 156 |
+ GLX_RED_SIZE, 1, // 1 = prefer high precision |
|
| 157 |
+ GLX_GREEN_SIZE, 1, |
|
| 158 |
+ GLX_BLUE_SIZE, 1, |
|
| 159 |
+ GLX_ALPHA_SIZE, 1, |
|
| 160 |
+ None, |
|
| 161 |
+ None, |
|
| 162 |
+ None, |
|
| 163 |
+ None, |
|
| 164 |
+ None, |
|
| 165 |
+ None, |
|
| 166 |
+ None, |
|
| 167 |
+ None, |
|
| 168 |
+ None, |
|
| 169 |
+ None, |
|
| 170 |
+ None, |
|
| 171 |
+ None, |
|
| 172 |
+ None, |
|
| 173 |
+ None, |
|
| 174 |
+ None, |
|
| 175 |
+ None }; |
|
| 176 |
+ |
|
| 177 |
+ int i = 12; |
|
| 178 |
+ if (gMode & GLUT_DOUBLE) |
|
| 179 |
+ {
|
|
| 180 |
+ attribs[i++] = GLX_DOUBLEBUFFER; |
|
| 181 |
+ attribs[i++] = 1; |
|
| 182 |
+ } |
|
| 183 |
+ if (gMode & GLUT_DEPTH) |
|
| 184 |
+ {
|
|
| 185 |
+ attribs[i++] = GLX_DEPTH_SIZE; |
|
| 186 |
+ attribs[i++] = 1; |
|
| 187 |
+ } |
|
| 188 |
+ if (gMode & GLUT_STENCIL) |
|
| 189 |
+ {
|
|
| 190 |
+ attribs[i++] = GLX_STENCIL_SIZE; |
|
| 191 |
+ attribs[i++] = 8; // Smallest available, at least 8. Configurable setting needed! |
|
| 192 |
+ } |
|
| 193 |
+ if (gMode & GLUT_MULTISAMPLE) |
|
| 194 |
+ {
|
|
| 195 |
+ attribs[i++] = GLX_SAMPLE_BUFFERS; |
|
| 196 |
+ attribs[i++] = 1; |
|
| 197 |
+ attribs[i++] = GLX_SAMPLES; |
|
| 198 |
+ attribs[i++] = 4; |
|
| 199 |
+ } |
|
| 200 |
+ |
|
| 201 |
+ fbcfg = glXChooseFBConfig(dpy, scrnum, attribs, &elemc); |
|
| 202 |
+ if (!fbcfg) |
|
| 203 |
+ {
|
|
| 204 |
+ fbcfg = glXChooseFBConfig(dpy, scrnum, NULL, &elemc); |
|
| 205 |
+ } |
|
| 206 |
+ if (!fbcfg) |
|
| 207 |
+ printf("Couldn't get FB configs\n");
|
|
| 208 |
+ |
|
| 209 |
+ int gl3attr[] = |
|
| 210 |
+ {
|
|
| 211 |
+ GLX_CONTEXT_MAJOR_VERSION_ARB, gContextVersionMajor, |
|
| 212 |
+ GLX_CONTEXT_MINOR_VERSION_ARB, gContextVersionMinor, |
|
| 213 |
+// GLX_CONTEXT_FLAGS_ARB, GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB, |
|
| 214 |
+ GLX_CONTEXT_FLAGS_ARB, GLX_CONTEXT_DEBUG_BIT_ARB, |
|
| 215 |
+ GLX_CONTEXT_PROFILE_MASK_ARB, GLX_CONTEXT_CORE_PROFILE_BIT_ARB, |
|
| 216 |
+ None |
|
| 217 |
+ }; |
|
| 218 |
+ ctx = glXCreateContextAttribsARB(dpy, fbcfg[0], NULL, 1, gl3attr); |
|
| 219 |
+ if (ctx == NULL) printf("No ctx!\n");
|
|
| 220 |
+ |
|
| 221 |
+ visinfo = glXGetVisualFromFBConfig(dpy, fbcfg[0]); |
|
| 222 |
+ if (!visinfo) |
|
| 223 |
+ printf("Error: couldn't create OpenGL window with this pixel format.\n");
|
|
| 224 |
+ |
|
| 225 |
+ } |
|
| 226 |
+ else // old style |
|
| 227 |
+//#endif |
|
| 228 |
+ {
|
|
| 229 |
+ int attribs[] = { GLX_RGBA,
|
|
| 230 |
+ GLX_RED_SIZE, 1, // 1 = prefer high precision |
|
| 231 |
+ GLX_GREEN_SIZE, 1, |
|
| 232 |
+ GLX_BLUE_SIZE, 1, |
|
| 233 |
+ None, |
|
| 234 |
+ None, |
|
| 235 |
+ None, |
|
| 236 |
+ None, |
|
| 237 |
+ None, |
|
| 238 |
+ None, |
|
| 239 |
+ None, |
|
| 240 |
+ None, |
|
| 241 |
+ None, |
|
| 242 |
+ None, |
|
| 243 |
+ None, |
|
| 244 |
+ None, |
|
| 245 |
+ None, |
|
| 246 |
+ None, |
|
| 247 |
+ None, |
|
| 248 |
+ None }; |
|
| 249 |
+ |
|
| 250 |
+ int i = 7; |
|
| 251 |
+ if (gMode & GLUT_DOUBLE) |
|
| 252 |
+ attribs[i++] = GLX_DOUBLEBUFFER; |
|
| 253 |
+ if (gMode & GLUT_DEPTH) |
|
| 254 |
+ {
|
|
| 255 |
+ attribs[i++] = GLX_DEPTH_SIZE; |
|
| 256 |
+ attribs[i++] = 1; |
|
| 257 |
+ } |
|
| 258 |
+ if (gMode & GLUT_STENCIL) |
|
| 259 |
+ {
|
|
| 260 |
+ attribs[i++] = GLX_STENCIL_SIZE; |
|
| 261 |
+ attribs[i++] = 8; // Smallest available, at least 8. Configurable setting needed! |
|
| 262 |
+ } |
|
| 263 |
+ visinfo = glXChooseVisual( dpy, scrnum, attribs ); |
|
| 264 |
+ if (!visinfo) |
|
| 265 |
+ {
|
|
| 266 |
+ printf("Error: couldn't get a visual according to settings\n");
|
|
| 267 |
+ exit(1); |
|
| 268 |
+ } |
|
| 269 |
+ |
|
| 270 |
+ ctx = glXCreateContext( dpy, visinfo, 0, True ); |
|
| 271 |
+ if (ctx == NULL) printf("No ctx!\n");
|
|
| 272 |
+ } |
|
| 273 |
+ |
|
| 274 |
+ /* window attributes */ |
|
| 275 |
+ attr.background_pixel = 0; |
|
| 276 |
+ attr.border_pixel = 0; |
|
| 277 |
+ attr.colormap = XCreateColormap( dpy, root, visinfo->visual, AllocNone); |
|
| 278 |
+ attr.event_mask = StructureNotifyMask | ExposureMask | KeyPressMask | KeyReleaseMask | ButtonPress | ButtonReleaseMask | Button1MotionMask | PointerMotionMask; |
|
| 279 |
+ attr.override_redirect = 0; |
|
| 280 |
+ mask = CWBackPixel | CWBorderPixel | CWColormap | CWEventMask | CWOverrideRedirect; |
|
| 281 |
+ |
|
| 282 |
+ win = XCreateWindow( dpy, root, x, y, width, height, |
|
| 283 |
+ 0, visinfo->depth, InputOutput, |
|
| 284 |
+ visinfo->visual, mask, &attr ); |
|
| 285 |
+ |
|
| 286 |
+// Register delete! |
|
| 287 |
+ wmDeleteMessage = XInternAtom(dpy, "WM_DELETE_WINDOW", False); |
|
| 288 |
+ XSetWMProtocols(dpy, win, &wmDeleteMessage, 1); // Register |
|
| 289 |
+ |
|
| 290 |
+ /* set hints and properties */ |
|
| 291 |
+ XSizeHints sizehints; |
|
| 292 |
+ sizehints.x = x; |
|
| 293 |
+ sizehints.y = y; |
|
| 294 |
+ sizehints.width = width; |
|
| 295 |
+ sizehints.height = height; |
|
| 296 |
+ sizehints.flags = USSize | USPosition; |
|
| 297 |
+ XSetNormalHints(dpy, win, &sizehints); |
|
| 298 |
+ XSetStandardProperties(dpy, win, name, name, |
|
| 299 |
+ None, (char **)NULL, 0, &sizehints); |
|
| 300 |
+ |
|
| 301 |
+ if (!ctx) |
|
| 302 |
+ {
|
|
| 303 |
+ printf("Error: glXCreateContext failed\n");
|
|
| 304 |
+ exit(1); |
|
| 305 |
+ } |
|
| 306 |
+ |
|
| 307 |
+ XFree(visinfo); |
|
| 308 |
+ |
|
| 309 |
+ *winRet = win; |
|
| 310 |
+ *ctxRet = ctx; |
|
| 311 |
+} |
|
| 312 |
+ |
|
| 313 |
+void glutCreateWindow(const char *windowTitle) |
|
| 314 |
+{
|
|
| 315 |
+ dpy = XOpenDisplay(NULL); |
|
| 316 |
+ if (!dpy) |
|
| 317 |
+ {
|
|
| 318 |
+ printf("Error: couldn't open display %s\n",
|
|
| 319 |
+ windowTitle ? windowTitle : getenv("DISPLAY"));
|
|
| 320 |
+ } |
|
| 321 |
+ |
|
| 322 |
+ make_window(dpy, windowTitle, winPosX, winPosY, winWidth, winHeight, &win, &ctx); |
|
| 323 |
+ |
|
| 324 |
+ XMapWindow(dpy, win); |
|
| 325 |
+ glXMakeCurrent(dpy, win, ctx); |
|
| 326 |
+} |
|
| 327 |
+ |
|
| 328 |
+void (*gDisplay)(void); |
|
| 329 |
+void (*gReshape)(int width, int height); |
|
| 330 |
+void (*gIdle)(void); |
|
| 331 |
+void (*gKey)(unsigned char key, int x, int y); |
|
| 332 |
+void (*gKeyUp)(unsigned char key, int x, int y); |
|
| 333 |
+void (*gSpecialKey)(unsigned char key, int x, int y); |
|
| 334 |
+void (*gSpecialKeyUp)(unsigned char key, int x, int y); |
|
| 335 |
+void (*gMouseMoved)(int x, int y); |
|
| 336 |
+void (*gMouseDragged)(int x, int y); |
|
| 337 |
+void (*gMouseFunc)(int button, int state, int x, int y); |
|
| 338 |
+int gLastMousePositionX, gLastMousePositionY; // Avoids a problem with glutWarpPointer |
|
| 339 |
+ |
|
| 340 |
+// Maybe I should just drop these for simplicity |
|
| 341 |
+//void (*gSpecialKey)(unsigned char key, int x, int y) = NULL; |
|
| 342 |
+//void (*gSpecialKeyUp)(unsigned char key, int x, int y) = NULL; |
|
| 343 |
+ |
|
| 344 |
+ |
|
| 345 |
+void glutReshapeFunc(void (*func)(int width, int height)) |
|
| 346 |
+{
|
|
| 347 |
+ gReshape = func; |
|
| 348 |
+} |
|
| 349 |
+void glutDisplayFunc(void (*func)(void)) |
|
| 350 |
+{
|
|
| 351 |
+ gDisplay = func; |
|
| 352 |
+} |
|
| 353 |
+void glutIdleFunc(void (*func)(void)) |
|
| 354 |
+{gIdle = func;}
|
|
| 355 |
+ |
|
| 356 |
+void glutKeyboardFunc(void (*func)(unsigned char key, int x, int y)) |
|
| 357 |
+{
|
|
| 358 |
+ gKey = func; |
|
| 359 |
+} |
|
| 360 |
+void glutKeyboardUpFunc(void (*func)(unsigned char key, int x, int y)) |
|
| 361 |
+{
|
|
| 362 |
+ gKeyUp = func; |
|
| 363 |
+} |
|
| 364 |
+void glutSpecialFunc(void (*func)(unsigned char key, int x, int y)) |
|
| 365 |
+{
|
|
| 366 |
+ gSpecialKey = func; |
|
| 367 |
+} |
|
| 368 |
+ |
|
| 369 |
+void glutSpecialUpFunc(void (*func)(unsigned char key, int x, int y)) |
|
| 370 |
+{
|
|
| 371 |
+ gSpecialKeyUp = func; |
|
| 372 |
+} |
|
| 373 |
+ |
|
| 374 |
+void glutMouseFunc(void (*func)(int button, int state, int x, int y)) |
|
| 375 |
+{gMouseFunc = func;}
|
|
| 376 |
+void glutMotionFunc(void (*func)(int x, int y)) |
|
| 377 |
+{gMouseDragged = func;}
|
|
| 378 |
+void glutPassiveMotionFunc(void (*func)(int x, int y)) |
|
| 379 |
+{gMouseMoved = func;}
|
|
| 380 |
+ |
|
| 381 |
+char gButtonPressed[10] = {0,0,0,0,0,0,0,0,0,0};
|
|
| 382 |
+ |
|
| 383 |
+void doKeyboardEvent(XEvent event, void (*keyProc)(unsigned char key, int x, int y), void (*specialKeyProc)(unsigned char key, int x, int y), int keyMapValue) |
|
| 384 |
+{
|
|
| 385 |
+ char buffer[10]; |
|
| 386 |
+// int r; // code; |
|
| 387 |
+ |
|
| 388 |
+ int code = ((XKeyEvent *)&event)->keycode; |
|
| 389 |
+ |
|
| 390 |
+// r = |
|
| 391 |
+ XLookupString(&event.xkey, buffer, sizeof(buffer), NULL, NULL); |
|
| 392 |
+ char raw = buffer[0]; // Before remapping |
|
| 393 |
+ switch(code) |
|
| 394 |
+ {
|
|
| 395 |
+ case 111: buffer[0] = GLUT_KEY_UP; break; |
|
| 396 |
+ case 114: buffer[0] = GLUT_KEY_RIGHT; break; |
|
| 397 |
+ case 116: buffer[0] = GLUT_KEY_DOWN; break; |
|
| 398 |
+ case 113: buffer[0] = GLUT_KEY_LEFT; break; |
|
| 399 |
+ case 67: buffer[0] = GLUT_KEY_F1; break; |
|
| 400 |
+ case 68: buffer[0] = GLUT_KEY_F2; break; |
|
| 401 |
+ case 69: buffer[0] = GLUT_KEY_F3; break; |
|
| 402 |
+ case 70: buffer[0] = GLUT_KEY_F4; break; |
|
| 403 |
+ case 71: buffer[0] = GLUT_KEY_F5; break; |
|
| 404 |
+ case 72: buffer[0] = GLUT_KEY_F6; break; |
|
| 405 |
+ case 73: buffer[0] = GLUT_KEY_F7; break; |
|
| 406 |
+ case 112: buffer[0] = GLUT_KEY_PAGE_UP; break; |
|
| 407 |
+ case 117: buffer[0] = GLUT_KEY_PAGE_DOWN; break; |
|
| 408 |
+ case 110: buffer[0] = GLUT_KEY_HOME; break; |
|
| 409 |
+ case 115: buffer[0] = GLUT_KEY_END; break; |
|
| 410 |
+ case 118: buffer[0] = GLUT_KEY_INSERT; break; |
|
| 411 |
+ |
|
| 412 |
+ case 50: buffer[0] = GLUT_KEY_LEFT_SHIFT; break; |
|
| 413 |
+ case 62: buffer[0] = GLUT_KEY_RIGHT_SHIFT; break; |
|
| 414 |
+ case 37:case 105: buffer[0] = GLUT_KEY_CONTROL; break; |
|
| 415 |
+ case 64:case 108: buffer[0] = GLUT_KEY_ALT; break; |
|
| 416 |
+ case 133:case 134: buffer[0] = GLUT_KEY_COMMAND; break; |
|
| 417 |
+ |
|
| 418 |
+// Keypad |
|
| 419 |
+ case 90: buffer[0] = GLUT_KEY_INSERT; break; |
|
| 420 |
+ case 87: buffer[0] = GLUT_KEY_END; break; |
|
| 421 |
+ case 88: buffer[0] = GLUT_KEY_DOWN; break; |
|
| 422 |
+ case 89: buffer[0] = GLUT_KEY_PAGE_DOWN; break; |
|
| 423 |
+ case 83: buffer[0] = GLUT_KEY_LEFT; break; |
|
| 424 |
+// case 84: buffer[0] = GLUT_KEY_KEYPAD_5; break; |
|
| 425 |
+ case 85: buffer[0] = GLUT_KEY_RIGHT; break; |
|
| 426 |
+ case 79: buffer[0] = GLUT_KEY_HOME; break; |
|
| 427 |
+ case 80: buffer[0] = GLUT_KEY_UP; break; |
|
| 428 |
+ case 81: buffer[0] = GLUT_KEY_PAGE_UP; break; |
|
| 429 |
+ case 82: buffer[0] = 127; break; |
|
| 430 |
+// case 77: buffer[0] = GLUT_KEY_KEYPAD_NUMLOCK; break; |
|
| 431 |
+ } |
|
| 432 |
+ |
|
| 433 |
+ // If we asked for a separate callback for special ketys, call it. Otherwise call the standard one. |
|
| 434 |
+ // I am considering removing the special callback for simplicity! |
|
| 435 |
+ if (raw == 0) |
|
| 436 |
+ {
|
|
| 437 |
+ if (specialKeyProc) |
|
| 438 |
+ specialKeyProc(buffer[0], 0, 0); |
|
| 439 |
+ else |
|
| 440 |
+ if (keyProc) |
|
| 441 |
+ keyProc(buffer[0], 0, 0); |
|
| 442 |
+ } |
|
| 443 |
+ else |
|
| 444 |
+ if (keyProc) |
|
| 445 |
+ keyProc(buffer[0], 0, 0); |
|
| 446 |
+ gKeymap[(int)buffer[0]] = keyMapValue; |
|
| 447 |
+ |
|
| 448 |
+// printf("%c %d %d %d\n", buffer[0], buffer[0], r, code);
|
|
| 449 |
+ |
|
| 450 |
+// if (event.type == KeyPress) |
|
| 451 |
+// { if (gKey) gKey(buffer[0], 0, 0); gKeymap[(int)buffer[0]] = 1;}
|
|
| 452 |
+// else |
|
| 453 |
+// { if (gKeyUp) gKeyUp(buffer[0], 0, 0); gKeymap[(int)buffer[0]] = 0;}
|
|
| 454 |
+} |
|
| 455 |
+ |
|
| 456 |
+void internaltimer(int x) |
|
| 457 |
+{
|
|
| 458 |
+ glutPostRedisplay(); |
|
| 459 |
+} |
|
| 460 |
+ |
|
| 461 |
+void glutMainLoop() |
|
| 462 |
+{
|
|
| 463 |
+ char pressed = 0; |
|
| 464 |
+ int i; |
|
| 465 |
+ |
|
| 466 |
+ XAllowEvents(dpy, AsyncBoth, CurrentTime); |
|
| 467 |
+ |
|
| 468 |
+// 2018-01-24: An attempt to patch over the problem that recent MESA tends to fail the first update. |
|
| 469 |
+ glutTimerFunc(100, internaltimer, 0); |
|
| 470 |
+ |
|
| 471 |
+ while (gRunning) |
|
| 472 |
+ {
|
|
| 473 |
+// int op = 0; |
|
| 474 |
+ while (XPending(dpy) > 0) |
|
| 475 |
+ {
|
|
| 476 |
+ XEvent event; |
|
| 477 |
+ XNextEvent(dpy, &event); |
|
| 478 |
+ |
|
| 479 |
+ switch (event.type) |
|
| 480 |
+ {
|
|
| 481 |
+ case ClientMessage: |
|
| 482 |
+ if (event.xclient.data.l[0] == wmDeleteMessage) // quit! |
|
| 483 |
+ gRunning = 0; |
|
| 484 |
+ break; |
|
| 485 |
+ case Expose: |
|
| 486 |
+// op = 1; |
|
| 487 |
+ break; // Update event! Should do draw here. |
|
| 488 |
+ case ConfigureNotify: |
|
| 489 |
+ if (gReshape) |
|
| 490 |
+ gReshape(event.xconfigure.width, event.xconfigure.height); |
|
| 491 |
+ else |
|
| 492 |
+ {
|
|
| 493 |
+ glViewport(0, 0, event.xconfigure.width, event.xconfigure.height); |
|
| 494 |
+ } |
|
| 495 |
+ animate = 1; |
|
| 496 |
+ winWidth = event.xconfigure.width; |
|
| 497 |
+ winHeight = event.xconfigure.height; |
|
| 498 |
+ break; |
|
| 499 |
+ case KeyPress: |
|
| 500 |
+ doKeyboardEvent(event, gKey, gSpecialKey, 1);break; |
|
| 501 |
+ case KeyRelease: |
|
| 502 |
+ doKeyboardEvent(event, gKeyUp, gSpecialKeyUp, 0);break; |
|
| 503 |
+ case ButtonPress: |
|
| 504 |
+ gButtonPressed[event.xbutton.button] = 1; |
|
| 505 |
+ if (gMouseFunc != NULL) |
|
| 506 |
+ switch (event.xbutton.button) |
|
| 507 |
+ {
|
|
| 508 |
+ case Button1: |
|
| 509 |
+ gMouseFunc(GLUT_LEFT_BUTTON, GLUT_DOWN, event.xbutton.x, event.xbutton.y);break; |
|
| 510 |
+ case Button2: |
|
| 511 |
+ gMouseFunc(GLUT_MIDDLE_BUTTON, GLUT_DOWN, event.xbutton.x, event.xbutton.y);break; |
|
| 512 |
+ case Button3: |
|
| 513 |
+ gMouseFunc(GLUT_RIGHT_BUTTON, GLUT_DOWN, event.xbutton.x, event.xbutton.y);break; |
|
| 514 |
+ } |
|
| 515 |
+ break; |
|
| 516 |
+ case ButtonRelease: |
|
| 517 |
+ gButtonPressed[event.xbutton.button] = 0; |
|
| 518 |
+ if (gMouseFunc != NULL) |
|
| 519 |
+ switch (event.xbutton.button) |
|
| 520 |
+ {
|
|
| 521 |
+ case Button1: |
|
| 522 |
+ gMouseFunc(GLUT_LEFT_BUTTON, GLUT_UP, event.xbutton.x, event.xbutton.y);break; |
|
| 523 |
+ case Button2: |
|
| 524 |
+ gMouseFunc(GLUT_MIDDLE_BUTTON, GLUT_UP, event.xbutton.x, event.xbutton.y);break; |
|
| 525 |
+ case Button3: |
|
| 526 |
+ gMouseFunc(GLUT_RIGHT_BUTTON, GLUT_UP, event.xbutton.x, event.xbutton.y);break; |
|
| 527 |
+ } |
|
| 528 |
+ break; |
|
| 529 |
+ case MotionNotify: |
|
| 530 |
+ pressed = 0; |
|
| 531 |
+ for (i = 0; i < 5; i++) |
|
| 532 |
+ if (gButtonPressed[i]) pressed = 1; |
|
| 533 |
+ |
|
| 534 |
+ // Saving the last known position in order to avoid problems for glutWarpPointer |
|
| 535 |
+ // If we try warping to this position, don't! |
|
| 536 |
+ gLastMousePositionX = event.xbutton.x; |
|
| 537 |
+ gLastMousePositionY = event.xbutton.y; |
|
| 538 |
+ |
|
| 539 |
+ if (pressed && gMouseDragged) |
|
| 540 |
+ gMouseDragged(event.xbutton.x, event.xbutton.y); |
|
| 541 |
+ else |
|
| 542 |
+ if (gMouseMoved) |
|
| 543 |
+ gMouseMoved(event.xbutton.x, event.xbutton.y); |
|
| 544 |
+ break; |
|
| 545 |
+ |
|
| 546 |
+ default: |
|
| 547 |
+ break; |
|
| 548 |
+ } |
|
| 549 |
+ } |
|
| 550 |
+ |
|
| 551 |
+ if (animate) |
|
| 552 |
+ {
|
|
| 553 |
+ animate = 0; |
|
| 554 |
+ if (gDisplay) |
|
| 555 |
+ gDisplay(); |
|
| 556 |
+ else |
|
| 557 |
+ printf("No display function!\n");
|
|
| 558 |
+// op = 0; |
|
| 559 |
+ } |
|
| 560 |
+ else |
|
| 561 |
+ if (gIdle) gIdle(); |
|
| 562 |
+ checktimers(); |
|
| 563 |
+ } |
|
| 564 |
+ |
|
| 565 |
+ glXMakeCurrent(dpy, None, NULL); |
|
| 566 |
+ glXDestroyContext(dpy, ctx); |
|
| 567 |
+ XDestroyWindow(dpy, win); |
|
| 568 |
+ XCloseDisplay(dpy); |
|
| 569 |
+} |
|
| 570 |
+ |
|
| 571 |
+void glutSwapBuffers() |
|
| 572 |
+{
|
|
| 573 |
+ glFlush(); // Added 2018-01-24, part of a fix for new MESA |
|
| 574 |
+ glXSwapBuffers(dpy, win); |
|
| 575 |
+} |
|
| 576 |
+ |
|
| 577 |
+void glutPostRedisplay() |
|
| 578 |
+{
|
|
| 579 |
+ animate = 1; |
|
| 580 |
+} |
|
| 581 |
+ |
|
| 582 |
+int glutGet(int type) |
|
| 583 |
+{
|
|
| 584 |
+ struct timeval tv; |
|
| 585 |
+ |
|
| 586 |
+ switch (type) |
|
| 587 |
+ {
|
|
| 588 |
+ case GLUT_ELAPSED_TIME: |
|
| 589 |
+ gettimeofday(&tv, NULL); |
|
| 590 |
+ return (tv.tv_usec - timeStart.tv_usec) / 1000 + (tv.tv_sec - timeStart.tv_sec)*1000; |
|
| 591 |
+ case GLUT_WINDOW_WIDTH: |
|
| 592 |
+ return winWidth; |
|
| 593 |
+ case GLUT_WINDOW_HEIGHT: |
|
| 594 |
+ return winHeight; |
|
| 595 |
+ case GLUT_MOUSE_POSITION_X: |
|
| 596 |
+ return gLastMousePositionX; |
|
| 597 |
+ case GLUT_MOUSE_POSITION_Y: |
|
| 598 |
+ return gLastMousePositionY; |
|
| 599 |
+ } |
|
| 600 |
+ return 0; |
|
| 601 |
+} |
|
| 602 |
+ |
|
| 603 |
+// NOTE: The timer is not designed with any multithreading in mind! |
|
| 604 |
+typedef struct TimerRec |
|
| 605 |
+{
|
|
| 606 |
+ int arg; |
|
| 607 |
+ int time; |
|
| 608 |
+ int repeatTime; |
|
| 609 |
+ void (*func)(int arg); |
|
| 610 |
+ char repeating; |
|
| 611 |
+ struct TimerRec *next; |
|
| 612 |
+ struct TimerRec *prev; |
|
| 613 |
+} TimerRec; |
|
| 614 |
+ |
|
| 615 |
+TimerRec *gTimers = NULL; |
|
| 616 |
+ |
|
| 617 |
+void glutTimerFunc(int millis, void (*func)(int arg), int arg) |
|
| 618 |
+{
|
|
| 619 |
+ TimerRec *t = (TimerRec *)malloc(sizeof(TimerRec)); |
|
| 620 |
+ t->arg = arg; |
|
| 621 |
+ t->time = millis + glutGet(GLUT_ELAPSED_TIME); |
|
| 622 |
+ t->repeatTime = 0; |
|
| 623 |
+ t->repeating = 0; |
|
| 624 |
+ t->func = func; |
|
| 625 |
+ t->next = gTimers; |
|
| 626 |
+ t->prev = NULL; |
|
| 627 |
+ if (gTimers != NULL) |
|
| 628 |
+ gTimers->prev = t; |
|
| 629 |
+ gTimers = t; |
|
| 630 |
+} |
|
| 631 |
+ |
|
| 632 |
+// Added by Ingemar |
|
| 633 |
+// void glutRepeatingTimerFunc(int millis) |
|
| 634 |
+void glutRepeatingTimer(int millis) |
|
| 635 |
+{
|
|
| 636 |
+ TimerRec *t = (TimerRec *)malloc(sizeof(TimerRec)); |
|
| 637 |
+ t->arg = 0; |
|
| 638 |
+ t->time = millis + glutGet(GLUT_ELAPSED_TIME); |
|
| 639 |
+ t->repeatTime = millis; |
|
| 640 |
+ t->repeating = 1; |
|
| 641 |
+ t->func = NULL; |
|
| 642 |
+ t->next = gTimers; |
|
| 643 |
+ t->prev = NULL; |
|
| 644 |
+ if (gTimers != NULL) |
|
| 645 |
+ gTimers->prev = t; |
|
| 646 |
+ gTimers = t; |
|
| 647 |
+} |
|
| 648 |
+ |
|
| 649 |
+static void checktimers() |
|
| 650 |
+{
|
|
| 651 |
+ if (gTimers != NULL) |
|
| 652 |
+ {
|
|
| 653 |
+ TimerRec *t, *firethis = NULL; |
|
| 654 |
+ int now = glutGet(GLUT_ELAPSED_TIME); |
|
| 655 |
+ int nextTime = now + 1000; // Distant future, 1 second |
|
| 656 |
+ t = gTimers; |
|
| 657 |
+ for (t = gTimers; t != NULL; t = t->next) |
|
| 658 |
+ {
|
|
| 659 |
+ if (t->time < nextTime) nextTime = t->time; // Time for the next one |
|
| 660 |
+ if (t->time < now) // See if this is due to fire |
|
| 661 |
+ {
|
|
| 662 |
+ firethis = t; |
|
| 663 |
+ } |
|
| 664 |
+ } |
|
| 665 |
+ if (firethis != NULL) |
|
| 666 |
+ {
|
|
| 667 |
+ // Fire the timer |
|
| 668 |
+ if (firethis->func != NULL) |
|
| 669 |
+ firethis->func(firethis->arg); |
|
| 670 |
+ else |
|
| 671 |
+ glutPostRedisplay(); |
|
| 672 |
+ // Remove the timer if it was one-shot, otherwise update the time |
|
| 673 |
+ if (firethis->repeating) |
|
| 674 |
+ {
|
|
| 675 |
+ firethis->time = now + firethis->repeatTime; |
|
| 676 |
+ } |
|
| 677 |
+ else |
|
| 678 |
+ {
|
|
| 679 |
+ if (firethis->prev != NULL) |
|
| 680 |
+ firethis->prev->next = firethis->next; |
|
| 681 |
+ else |
|
| 682 |
+ gTimers = firethis->next; |
|
| 683 |
+ if (firethis->next != NULL) |
|
| 684 |
+ firethis->next->prev = firethis->prev; |
|
| 685 |
+ free(firethis); |
|
| 686 |
+ } |
|
| 687 |
+ } |
|
| 688 |
+ // Otherwise, sleep until any timer should fire |
|
| 689 |
+ if (!animate) |
|
| 690 |
+ if (nextTime > now) |
|
| 691 |
+ {
|
|
| 692 |
+ usleep((nextTime - now)*1000); |
|
| 693 |
+ } |
|
| 694 |
+ } |
|
| 695 |
+ else |
|
| 696 |
+// If no timer and no update, sleep a little to keep CPU load low |
|
| 697 |
+ if (!animate) |
|
| 698 |
+ usleep(10); |
|
| 699 |
+} |
|
| 700 |
+ |
|
| 701 |
+void glutInitContextVersion(int major, int minor) |
|
| 702 |
+{
|
|
| 703 |
+ gContextVersionMajor = major; |
|
| 704 |
+ gContextVersionMinor = minor; |
|
| 705 |
+} |
|
| 706 |
+ |
|
| 707 |
+// Based on FreeGlut glutWarpPointer, but with a significant improvement! |
|
| 708 |
+/* |
|
| 709 |
+ * Moves the mouse pointer to given window coordinates |
|
| 710 |
+ */ |
|
| 711 |
+void glutWarpPointer( int x, int y ) |
|
| 712 |
+{
|
|
| 713 |
+ if (dpy == NULL) |
|
| 714 |
+ {
|
|
| 715 |
+ fprintf(stderr, "glutWarpPointer failed: MicroGlut not initialized!\n"); |
|
| 716 |
+ return; |
|
| 717 |
+ } |
|
| 718 |
+ |
|
| 719 |
+ if (x == gLastMousePositionX && y == gLastMousePositionY) |
|
| 720 |
+ return; // Don't warp to where we already are - this causes event flooding! |
|
| 721 |
+ |
|
| 722 |
+ XWarpPointer( |
|
| 723 |
+ dpy, // fgDisplay.Display, |
|
| 724 |
+ None, |
|
| 725 |
+ win, // fgStructure.CurrentWindow->Window.Handle, |
|
| 726 |
+ 0, 0, 0, 0, |
|
| 727 |
+ x, y |
|
| 728 |
+ ); |
|
| 729 |
+ /* Make the warp visible immediately. */ |
|
| 730 |
+ XFlush( dpy ); |
|
| 731 |
+// XFlush( fgDisplay.Display ); |
|
| 732 |
+} |
|
| 733 |
+ |
|
| 734 |
+// Replaces glutSetMousePointer. This limits us to the two most common cases: None and arrow! |
|
| 735 |
+void glutShowCursor() |
|
| 736 |
+{
|
|
| 737 |
+ XUndefineCursor(dpy, win); |
|
| 738 |
+} |
|
| 739 |
+ |
|
| 740 |
+void glutHideCursor() |
|
| 741 |
+{
|
|
| 742 |
+ if (dpy == NULL) |
|
| 743 |
+ {
|
|
| 744 |
+ printf("glutHideCursor failed: MicroGlut not initialized!\n");
|
|
| 745 |
+ return; |
|
| 746 |
+ } |
|
| 747 |
+ |
|
| 748 |
+ Cursor invisibleCursor; |
|
| 749 |
+ Pixmap bitmapNoData; |
|
| 750 |
+ static char noll[] = { 0,0,0};
|
|
| 751 |
+ bitmapNoData = XCreateBitmapFromData(dpy, win, noll, 1, 1); |
|
| 752 |
+ invisibleCursor = XCreatePixmapCursor(dpy,bitmapNoData, bitmapNoData, |
|
| 753 |
+ (XColor *)noll, (XColor *)noll, 0, 0); |
|
| 754 |
+ XDefineCursor(dpy,win, invisibleCursor); |
|
| 755 |
+ XFreeCursor(dpy, invisibleCursor); |
|
| 756 |
+ XFreePixmap(dpy, bitmapNoData); |
|
| 757 |
+} |
|
| 758 |
+ |
|
| 759 |
+ |
|
| 760 |
+ |
|
| 761 |
+char glutKeyIsDown(unsigned char c) |
|
| 762 |
+{
|
|
| 763 |
+ return gKeymap[(unsigned int)c]; |
|
| 764 |
+} |
|
| 765 |
+ |
|
| 766 |
+// Added by the Risinger/R\8Cberg/Wikstr\9Am project! But... gButtonPressed |
|
| 767 |
+// was already here! Did I miss something? |
|
| 768 |
+char glutMouseIsDown(unsigned char c) |
|
| 769 |
+{
|
|
| 770 |
+ return gButtonPressed[(unsigned int)c]; |
|
| 771 |
+} |
|
| 772 |
+ |
|
| 773 |
+ |
|
| 774 |
+ |
|
| 775 |
+// These were missing up to 150205 |
|
| 776 |
+ |
|
| 777 |
+void glutReshapeWindow(int width, int height) |
|
| 778 |
+{
|
|
| 779 |
+ XResizeWindow(dpy, win, width, height); |
|
| 780 |
+} |
|
| 781 |
+void glutPositionWindow(int x, int y) |
|
| 782 |
+{
|
|
| 783 |
+ XMoveWindow(dpy, win, x, y); |
|
| 784 |
+} |
|
| 785 |
+void glutSetWindowTitle(char *title) |
|
| 786 |
+{
|
|
| 787 |
+ XStoreName(dpy, win, title); |
|
| 788 |
+} |
|
| 789 |
+ |
|
| 790 |
+// Not complete full screen mode yet since the window frame and menu are not hidden yet |
|
| 791 |
+ |
|
| 792 |
+char gFullScreen = 0; |
|
| 793 |
+unsigned int savedHeight, savedWidth; |
|
| 794 |
+int savedX, savedY; |
|
| 795 |
+ |
|
| 796 |
+void glutFullScreen() |
|
| 797 |
+{
|
|
| 798 |
+ gFullScreen = 1; |
|
| 799 |
+ |
|
| 800 |
+ |
|
| 801 |
+ Drawable d; |
|
| 802 |
+ unsigned int a, b; |
|
| 803 |
+ |
|
| 804 |
+ XGetGeometry(dpy, win, &d, &savedX, &savedY, &savedWidth, &savedHeight, &a, &b); |
|
| 805 |
+ |
|
| 806 |
+ int scrnum = DefaultScreen(dpy); |
|
| 807 |
+ int width = DisplayWidth( dpy, scrnum ); |
|
| 808 |
+ int height = DisplayHeight( dpy, scrnum ); |
|
| 809 |
+ |
|
| 810 |
+ XMoveResizeWindow(dpy, win, 0, 0, width, height); |
|
| 811 |
+} |
|
| 812 |
+ |
|
| 813 |
+void glutExitFullScreen() |
|
| 814 |
+{
|
|
| 815 |
+ gFullScreen = 0; |
|
| 816 |
+ XMoveResizeWindow(dpy, win, savedX, savedY, savedWidth, savedHeight); |
|
| 817 |
+} |
|
| 818 |
+ |
|
| 819 |
+void glutToggleFullScreen() |
|
| 820 |
+{
|
|
| 821 |
+ if (gFullScreen) |
|
| 822 |
+ glutExitFullScreen(); |
|
| 823 |
+ else |
|
| 824 |
+ glutFullScreen(); |
|
| 825 |
+} |
|
| 826 |
+ |
|
| 827 |
+void glutExit() |
|
| 828 |
+{
|
|
| 829 |
+ gRunning = 0; |
|
| 830 |
+} |
|
| 831 |
+ |