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 {} +
Robert Cranston authored on 27/02/2024 02:27:271 | 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 |
+ |