1
2 #ifndef B3_USE_GLFW
3
4 #include "X11OpenGLWindow.h"
5 #include "OpenGLInclude.h"
6
7 #include <stdio.h>
8 #include <stdlib.h>
9 #include "glad/gl.h"
10
11 #ifdef GLEW_DYNAMIC_LOAD_ALL_GLX_FUNCTIONS
12 #include "glad/glx.h"
13 #else
14 #include <GL/glx.h>
15 #endif // GLEW_DYNAMIC_LOAD_ALL_GLX_FUNCTIONS
16 #include <assert.h>
17
18 //#define DYNAMIC_LOAD_X11_FUNCTIONS
19 #ifdef DYNAMIC_LOAD_X11_FUNCTIONS
20 #include <dlfcn.h>
21 #endif //DYNAMIC_LOAD_X11_FUNCTIONS
22
23 //#include<X11/X.h>
24 //#include<X11/Xlib.h>
25 //#include<GL/gl.h>
26
27 //defined in GL/glxew.h
28 //#include<GL/glu.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <unistd.h>
33
34 #include <pthread.h>
35
36 GLint att[] = {GLX_RGBA,
37 GLX_DEPTH_SIZE, 24,
38 GLX_RED_SIZE, 8,
39 GLX_GREEN_SIZE, 8,
40 GLX_BLUE_SIZE, 8,
41 GLX_ALPHA_SIZE, 8,
42 GLX_STENCIL_SIZE, 8,
43 GLX_DOUBLEBUFFER,
44 None};
45 /*
46 static int att[] =
47 {
48 GLX_RGBA, GLX_DEPTH_SIZE, 24, GLX_DOUBLEBUFFER, None
49
50 GLX_X_RENDERABLE , True,
51 GLX_DRAWABLE_TYPE , GLX_WINDOW_BIT,
52 GLX_RENDER_TYPE , GLX_RGBA_BIT,
53 GLX_X_VISUAL_TYPE , GLX_TRUE_COLOR,
54 GLX_RED_SIZE , 8,
55 GLX_GREEN_SIZE , 8,
56 GLX_BLUE_SIZE , 8,
57 GLX_ALPHA_SIZE , 8,
58 GLX_DEPTH_SIZE , 24,
59 GLX_STENCIL_SIZE , 8,
60 GLX_DOUBLEBUFFER , True,
61 None
62 };
63 */
64 static bool forceOpenGL3 = true;
65
66 #ifdef DYNAMIC_LOAD_X11_FUNCTIONS
67
68 ///our X11 function typedefs
69
70 typedef int (*PFNXFREE)(void*);
71 typedef XErrorHandler (*PFNXSETERRORHANDLER)(XErrorHandler);
72 typedef int (*PFNXSYNC)(Display* a, Bool b);
73 typedef Display* (*PFNXOPENDISPLAY)(_Xconst char* a);
74 typedef Colormap (*PFNXCREATECOLORMAP)(Display* a, Window b, Visual* c, int d);
75 typedef Window (*PFNXCREATEWINDOW)(Display* a, Window b, int c, int d, unsigned int e, unsigned int f, unsigned int g, int h, unsigned int i, Visual* j, unsigned long k, XSetWindowAttributes* l);
76 typedef int (*PFNXMAPWINDOW)(Display*, Window);
77 typedef int (*PFNXSTORENAME)(Display* a, Window b, _Xconst char* c);
78 typedef int (*PFNXCLOSEDISPLAY)(Display* a);
79 typedef int (*PFNXDESTROYWINDOW)(Display* a, Window b);
80 typedef int (*PFNXRAISEWINDOW)(Display* a, Window b);
81
82 #if NeedWidePrototypes
83 typedef KeySym* (*PFNXGETKEYBOARDMAPPING)(Display*, unsigned int, int, int*);
84 typedef KeySym (*PFNXKEYCODETOKEYSYM)(Display* a, unsigned int b, int c);
85 #else
86 typedef KeySym* (*PFNXGETKEYBOARDMAPPING)(Display*, KeyCode, int, int*);
87 typedef KeySym (*PFNXKEYCODETOKEYSYM)(Display* a, KeyCode b, int c);
88 #endif
89 typedef void (*PFNXCONVERTCASE)(KeySym /* sym */, KeySym* /* lower */, KeySym* /* upper */);
90 typedef int (*PFNXPENDING)(Display* a);
91 typedef int (*PFNXNEXTEVENT)(Display* a, XEvent* b);
92 typedef int (*PFNXEVENTSQUEUED)(Display* a, int b);
93 typedef int (*PFNXPEEKEVENT)(Display* a, XEvent* b);
94 typedef KeySym (*PFNXLOOKUPKEYSYM)(XKeyEvent* a, int b);
95 typedef Status (*PFNXGETWINDOWATTRIBUTES)(Display* a, Window b, XWindowAttributes* c);
96
97 #define X11_LIBRARY "libX11.so.6"
98
99 #define MyXSync m_data->m_x11_XSync
100 #define MyXGetKeyboardMapping m_data->m_x11_XGetKeyboardMapping
101 #define MyXSetErrorHandler m_data->m_x11_XSetErrorHandler
102 #define MyXOpenDisplay m_data->m_x11_XOpenDisplay
103 #define MyXCreateColormap m_data->m_x11_XCreateColormap
104 #define MyXCreateWindow m_data->m_x11_XCreateWindow
105 #define MyXMapWindow m_data->m_x11_XMapWindow
106 #define MyXStoreName m_data->m_x11_XStoreName
107 #define MyXDestroyWindow m_data->m_x11_XDestroyWindow
108 #define MyXRaiseWindow m_data->m_x11_XRaiseWindow
109 #define MyXCloseDisplay m_data->m_x11_XCloseDisplay
110 #define MyXKeycodeToKeysym m_data->m_x11_XKeycodeToKeysym
111 #define MyXConvertCase m_data->m_x11_XConvertCase
112 #define MyXPending m_data->m_x11_XPending
113 #define MyXNextEvent m_data->m_x11_XNextEvent
114 #define MyXEventsQueued m_data->m_x11_XEventsQueued
115 #define MyXPeekEvent m_data->m_x11_XPeekEvent
116 #define MyXNextEvent m_data->m_x11_XNextEvent
117 #define MyXGetWindowAttributes m_data->m_x11_XGetWindowAttributes
118 #define MyXStoreName m_data->m_x11_XStoreName
119 #define MyXFree m_data->m_x11_XFree
120 #define MyXMapWindow m_data->m_x11_XMapWindow
121 #define MyXStoreName m_data->m_x11_XStoreName
122 #define MyXLookupKeysym m_data->m_x11_XLookupKeysym
123
124 #else
125 #define MyXSync XSync
126 #define MyXGetKeyboardMapping XGetKeyboardMapping
127 #define MyXSetErrorHandler XSetErrorHandler
128 #define MyXOpenDisplay XOpenDisplay
129 #define MyXCreateColormap XCreateColormap
130 #define MyXCreateWindow XCreateWindow
131 #define MyXMapWindow XMapWindow
132 #define MyXStoreName XStoreName
133 #define MyXDestroyWindow XDestroyWindow
134 #define MyXRaiseWindow XRaiseWindow
135 #define MyXCloseDisplay XCloseDisplay
136 #define MyXKeycodeToKeysym XKeycodeToKeysym
137 #define MyXConvertCase XConvertCase
138 #define MyXPending XPending
139 #define MyXNextEvent XNextEvent
140 #define MyXEventsQueued XEventsQueued
141 #define MyXPeekEvent XPeekEvent
142 #define MyXNextEvent XNextEvent
143 #define MyXGetWindowAttributes XGetWindowAttributes
144 #define MyXStoreName XStoreName
145 #define MyXFree XFree
146 #define MyXMapWindow XMapWindow
147 #define MyXStoreName XStoreName
148 #define MyXLookupKeysym XLookupKeysym
149
150 #endif //DYNAMIC_LOAD_X11_FUNCTIONS
151
152 enum
153 {
154 MY_X11_ALT_KEY = 1,
155 MY_X11_SHIFT_KEY = 2,
156 MY_X11_CONTROL_KEY = 4
157 };
158
159 struct InternalData2
160 {
161 Display* m_dpy;
162 Window m_root;
163 XVisualInfo* m_vi;
164 Colormap m_cmap;
165 XSetWindowAttributes m_swa;
166 Window m_win;
167 GLXContext m_glc;
168 XWindowAttributes m_gwa;
169 XEvent m_xev;
170 GLXFBConfig m_bestFbc;
171 int m_modifierFlags;
172 int m_glWidth;
173 int m_glHeight;
174
175 #ifdef DYNAMIC_LOAD_X11_FUNCTIONS
176 //dynamically load stuff
177 void* m_x11_library;
178 PFNXFREE m_x11_XFree;
179 PFNXSETERRORHANDLER m_x11_XSetErrorHandler;
180 PFNXSYNC m_x11_XSync;
181 PFNXOPENDISPLAY m_x11_XOpenDisplay;
182 PFNXCREATECOLORMAP m_x11_XCreateColormap;
183 PFNXCREATEWINDOW m_x11_XCreateWindow;
184 PFNXMAPWINDOW m_x11_XMapWindow;
185 PFNXSTORENAME m_x11_XStoreName;
186 PFNXCLOSEDISPLAY m_x11_XCloseDisplay;
187 PFNXDESTROYWINDOW m_x11_XDestroyWindow;
188 PFNXRAISEWINDOW m_x11_XRaiseWindow;
189 PFNXKEYCODETOKEYSYM m_x11_XKeycodeToKeysym;
190 PFNXGETKEYBOARDMAPPING m_x11_XGetKeyboardMapping;
191 PFNXCONVERTCASE m_x11_XConvertCase;
192 PFNXPENDING m_x11_XPending;
193 PFNXNEXTEVENT m_x11_XNextEvent;
194 PFNXEVENTSQUEUED m_x11_XEventsQueued;
195 PFNXPEEKEVENT m_x11_XPeekEvent;
196 PFNXLOOKUPKEYSYM m_x11_XLookupKeysym;
197 PFNXGETWINDOWATTRIBUTES m_x11_XGetWindowAttributes;
198 #endif //DYNAMIC_LOAD_X11_FUNCTIONS
199
200 b3WheelCallback m_wheelCallback;
201 b3MouseMoveCallback m_mouseMoveCallback;
202 b3MouseButtonCallback m_mouseButtonCallback;
203 b3ResizeCallback m_resizeCallback;
204 b3KeyboardCallback m_keyboardCallback;
205
InternalData2InternalData2206 InternalData2()
207 : m_dpy(0),
208 m_vi(0),
209 m_modifierFlags(0),
210 m_glWidth(-1),
211 m_glHeight(-1),
212 m_wheelCallback(0),
213 m_mouseMoveCallback(0),
214 m_mouseButtonCallback(0),
215 m_resizeCallback(0),
216 m_keyboardCallback(0)
217 {
218 #ifdef DYNAMIC_LOAD_X11_FUNCTIONS
219 m_x11_library = dlopen(X11_LIBRARY, RTLD_LOCAL | RTLD_NOW);
220 if (!m_x11_library)
221 {
222 // TODO: Properly handle this error.
223 fprintf(stderr, "Error opening X11 library %s: %s\n", X11_LIBRARY, dlerror());
224 exit(EXIT_FAILURE);
225 }
226
227 bool missingFunc = false;
228
229 missingFunc = ((m_x11_XFree = (PFNXFREE)dlsym(m_x11_library, "XFree")) == NULL) | missingFunc;
230 assert(!missingFunc);
231 if (missingFunc)
232 {
233 fprintf(stderr, "Error: missing func XFree in %s, exiting!\n", X11_LIBRARY);
234 exit(EXIT_FAILURE);
235 }
236 missingFunc = ((m_x11_XSetErrorHandler = (PFNXSETERRORHANDLER)dlsym(m_x11_library, "XSetErrorHandler")) == NULL) | missingFunc;
237 if (missingFunc)
238 {
239 fprintf(stderr, "Error: missing func XSetErrorHandler in %s, exiting!\n", X11_LIBRARY);
240 exit(EXIT_FAILURE);
241 }
242 missingFunc = ((m_x11_XSetErrorHandler = (PFNXSETERRORHANDLER)dlsym(m_x11_library, "XSetErrorHandler")) == NULL) | missingFunc;
243 if (missingFunc)
244 {
245 fprintf(stderr, "Error: missing func XSetErrorHandler in %s, exiting!\n", X11_LIBRARY);
246 exit(EXIT_FAILURE);
247 }
248 missingFunc = ((m_x11_XSync = (PFNXSYNC)dlsym(m_x11_library, "XSync")) == NULL) | missingFunc;
249 if (missingFunc)
250 {
251 fprintf(stderr, "Error: missing func XSync in %s, exiting!\n", X11_LIBRARY);
252 exit(EXIT_FAILURE);
253 }
254 missingFunc = ((m_x11_XOpenDisplay = (PFNXOPENDISPLAY)dlsym(m_x11_library, "XOpenDisplay")) == NULL) | missingFunc;
255 if (missingFunc)
256 {
257 fprintf(stderr, "Error: missing func XOpenDisplay in %s, exiting!\n", X11_LIBRARY);
258 exit(EXIT_FAILURE);
259 }
260 missingFunc = ((m_x11_XCreateColormap = (PFNXCREATECOLORMAP)dlsym(m_x11_library, "XCreateColormap")) == NULL) | missingFunc;
261 if (missingFunc)
262 {
263 fprintf(stderr, "Error: missing func XCreateColormap in %s, exiting!\n", X11_LIBRARY);
264 exit(EXIT_FAILURE);
265 }
266 missingFunc = ((m_x11_XCreateWindow = (PFNXCREATEWINDOW)dlsym(m_x11_library, "XCreateWindow")) == NULL) | missingFunc;
267 if (missingFunc)
268 {
269 fprintf(stderr, "Error: missing func XCreateWindow in %s, exiting!\n", X11_LIBRARY);
270 exit(EXIT_FAILURE);
271 }
272 missingFunc = ((m_x11_XMapWindow = (PFNXMAPWINDOW)dlsym(m_x11_library, "XMapWindow")) == NULL) | missingFunc;
273 if (missingFunc)
274 {
275 fprintf(stderr, "Error: missing func XMapWindow in %s, exiting!\n", X11_LIBRARY);
276 exit(EXIT_FAILURE);
277 }
278 missingFunc = ((m_x11_XStoreName = (PFNXSTORENAME)dlsym(m_x11_library, "XStoreName")) == NULL) | missingFunc;
279 if (missingFunc)
280 {
281 fprintf(stderr, "Error: missing func XStoreName in %s, exiting!\n", X11_LIBRARY);
282 exit(EXIT_FAILURE);
283 }
284 missingFunc = ((m_x11_XCloseDisplay = (PFNXCLOSEDISPLAY)dlsym(m_x11_library, "XCloseDisplay")) == NULL) | missingFunc;
285 if (missingFunc)
286 {
287 fprintf(stderr, "Error: missing func XCloseDisplay in %s, exiting!\n", X11_LIBRARY);
288 exit(EXIT_FAILURE);
289 }
290 missingFunc = ((m_x11_XDestroyWindow = (PFNXDESTROYWINDOW)dlsym(m_x11_library, "XDestroyWindow")) == NULL) | missingFunc;
291 if (missingFunc)
292 {
293 fprintf(stderr, "Error: missing func XDestroyWindow in %s, exiting!\n", X11_LIBRARY);
294 exit(EXIT_FAILURE);
295 }
296 missingFunc = ((m_x11_XRaiseWindow = (PFNXRAISEWINDOW)dlsym(m_x11_library, "XRaiseWindow")) == NULL) | missingFunc;
297 if (missingFunc)
298 {
299 fprintf(stderr, "Error: missing func XRaiseWindow in %s, exiting!\n", X11_LIBRARY);
300 exit(EXIT_FAILURE);
301 }
302
303 missingFunc = ((m_x11_XGetKeyboardMapping = (PFNXGETKEYBOARDMAPPING)dlsym(m_x11_library, "XGetKeyboardMapping")) == NULL) | missingFunc;
304 if (missingFunc)
305 {
306 fprintf(stderr, "Error: missing func XGetKeyboardMapping in %s, exiting!\n", X11_LIBRARY);
307 exit(EXIT_FAILURE);
308 }
309 missingFunc = ((m_x11_XKeycodeToKeysym = (PFNXKEYCODETOKEYSYM)dlsym(m_x11_library, "XKeycodeToKeysym")) == NULL) | missingFunc;
310 if (missingFunc)
311 {
312 fprintf(stderr, "Error: missing func XKeycodeToKeysym in %s, exiting!\n", X11_LIBRARY);
313 exit(EXIT_FAILURE);
314 }
315 missingFunc = ((m_x11_XConvertCase = (PFNXCONVERTCASE)dlsym(m_x11_library, "XConvertCase")) == NULL) | missingFunc;
316 if (missingFunc)
317 {
318 fprintf(stderr, "Error: missing func XConvertCase in %s, exiting!\n", X11_LIBRARY);
319 exit(EXIT_FAILURE);
320 }
321 missingFunc = ((m_x11_XPending = (PFNXPENDING)dlsym(m_x11_library, "XPending")) == NULL) | missingFunc;
322 if (missingFunc)
323 {
324 fprintf(stderr, "Error: missing func XPending in %s, exiting!\n", X11_LIBRARY);
325 exit(EXIT_FAILURE);
326 }
327 missingFunc = ((m_x11_XNextEvent = (PFNXNEXTEVENT)dlsym(m_x11_library, "XNextEvent")) == NULL) | missingFunc;
328 if (missingFunc)
329 {
330 fprintf(stderr, "Error: missing func XNextEvent in %s, exiting!\n", X11_LIBRARY);
331 exit(EXIT_FAILURE);
332 }
333 missingFunc = ((m_x11_XEventsQueued = (PFNXEVENTSQUEUED)dlsym(m_x11_library, "XEventsQueued")) == NULL) | missingFunc;
334 if (missingFunc)
335 {
336 fprintf(stderr, "Error: missing func XEventsQueued in %s, exiting!\n", X11_LIBRARY);
337 exit(EXIT_FAILURE);
338 }
339 missingFunc = ((m_x11_XPeekEvent = (PFNXPEEKEVENT)dlsym(m_x11_library, "XPeekEvent")) == NULL) | missingFunc;
340 if (missingFunc)
341 {
342 fprintf(stderr, "Error: missing func XPeekEvent in %s, exiting!\n", X11_LIBRARY);
343 exit(EXIT_FAILURE);
344 }
345 missingFunc = ((m_x11_XLookupKeysym = (PFNXLOOKUPKEYSYM)dlsym(m_x11_library, "XLookupKeysym")) == NULL) | missingFunc;
346 if (missingFunc)
347 {
348 fprintf(stderr, "Error: missing func XLookupKeysym in %s, exiting!\n", X11_LIBRARY);
349 exit(EXIT_FAILURE);
350 }
351 missingFunc = ((m_x11_XGetWindowAttributes = (PFNXGETWINDOWATTRIBUTES)dlsym(m_x11_library, "XGetWindowAttributes")) == NULL) | missingFunc;
352 if (missingFunc)
353 {
354 fprintf(stderr, "Error: missing func XGetWindowAttributes in %s, exiting!\n", X11_LIBRARY);
355 exit(EXIT_FAILURE);
356 }
357
358 if (missingFunc)
359 {
360 fprintf(stderr, "Error: a missing func in %s, exiting!\n", X11_LIBRARY);
361 exit(EXIT_FAILURE);
362 }
363 else
364 {
365 printf("X11 functions dynamically loaded using dlopen/dlsym OK!\n");
366 }
367 #endif //DYNAMIC_LOAD_X11_FUNCTIONS
368 }
369 };
370
371 #define GLX_CONTEXT_MAJOR_VERSION_ARB 0x2091
372 #define GLX_CONTEXT_MINOR_VERSION_ARB 0x2092
373 typedef GLXContext (*glXCreateContextAttribsARBProc)(Display*, GLXFBConfig, GLXContext, Bool, const int*);
374
375 // Helper to check for extension string presence. Adapted from:
376 // http://www.opengl.org/resources/features/OGLextensions/
isExtensionSupported(const char * extList,const char * extension)377 static bool isExtensionSupported(const char* extList, const char* extension)
378 {
379 const char* start;
380 const char *where, *terminator;
381
382 /* Extension names should not have spaces. */
383 where = strchr(extension, ' ');
384 if (where || *extension == '\0')
385 return false;
386
387 /* It takes a bit of care to be fool-proof about parsing the
388 OpenGL extensions string. Don't be fooled by sub-strings,
389 etc. */
390 for (start = extList;;)
391 {
392 where = strstr(start, extension);
393
394 if (!where)
395 break;
396
397 terminator = where + strlen(extension);
398
399 if (where == start || *(where - 1) == ' ')
400 if (*terminator == ' ' || *terminator == '\0')
401 return true;
402
403 start = terminator;
404 }
405
406 return false;
407 }
408
409 static bool ctxErrorOccurred = false;
ctxErrorHandler(Display * dpy,XErrorEvent * ev)410 static int ctxErrorHandler(Display* dpy, XErrorEvent* ev)
411 {
412 ctxErrorOccurred = true;
413 return 0;
414 }
415
X11OpenGLWindow()416 X11OpenGLWindow::X11OpenGLWindow()
417 : m_OpenGLInitialized(false),
418 m_requestedExit(false)
419 {
420 m_data = new InternalData2;
421 }
422
~X11OpenGLWindow()423 X11OpenGLWindow::~X11OpenGLWindow()
424 {
425 if (m_OpenGLInitialized)
426 {
427 disableOpenGL();
428 }
429
430 delete m_data;
431 }
432
enableOpenGL()433 void X11OpenGLWindow::enableOpenGL()
434 {
435 if (forceOpenGL3)
436 {
437 // Get the default screen's GLX extension list
438 const char* glxExts = glXQueryExtensionsString(m_data->m_dpy,
439 DefaultScreen(m_data->m_dpy));
440
441 // NOTE: It is not necessary to create or make current to a context before
442 // calling glXGetProcAddressARB, unless we dynamically load OpenGL/GLX/X11
443
444 glXCreateContextAttribsARBProc glXCreateContextAttribsARB = 0;
445 glXCreateContextAttribsARB = (glXCreateContextAttribsARBProc)
446 glXGetProcAddressARB((const GLubyte*)"glXCreateContextAttribsARB");
447
448 GLXContext ctx = 0;
449
450 // Install an X error handler so the application won't exit if GL 3.3
451 // context allocation fails.
452 //
453 // Note this error handler is global. All display connections in all threads
454 // of a process use the same error handler, so be sure to guard against other
455 // threads issuing X commands while this code is running.
456 ctxErrorOccurred = false;
457 int (*oldHandler)(Display*, XErrorEvent*) =
458 MyXSetErrorHandler(&ctxErrorHandler);
459
460 // Check for the GLX_ARB_create_context extension string and the function.
461 // If either is not present, use GLX 1.3 context creation method.
462 if (!isExtensionSupported(glxExts, "GLX_ARB_create_context") ||
463 !glXCreateContextAttribsARB)
464 {
465 printf(
466 "glXCreateContextAttribsARB() not found"
467 " ... using old-style GLX context\n");
468 ctx = glXCreateNewContext(m_data->m_dpy, m_data->m_bestFbc, GLX_RGBA_TYPE, 0, True);
469 }
470
471 // If it does, try to get a GL 3.3 context!
472 else
473 {
474 int context_attribs[] = {
475 GLX_CONTEXT_MAJOR_VERSION_ARB, 3,
476 GLX_CONTEXT_MINOR_VERSION_ARB, 3,
477 GLX_CONTEXT_FLAGS_ARB, GLX_CONTEXT_DEBUG_BIT_ARB,
478 GLX_CONTEXT_PROFILE_MASK_ARB, GLX_CONTEXT_CORE_PROFILE_BIT_ARB, None};
479 /*
480 int context_attribs[] =
481 {
482 GLX_CONTEXT_MAJOR_VERSION_ARB, 3,
483 GLX_CONTEXT_MINOR_VERSION_ARB, 2,
484
485 //GLX_CONTEXT_FLAGS_ARB , GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB,
486 None
487 };
488 */
489 printf("Creating context\n");
490 ctx = glXCreateContextAttribsARB(m_data->m_dpy, m_data->m_bestFbc, 0,
491 True, context_attribs);
492
493 // Sync to ensure any errors generated are processed.
494 MyXSync(m_data->m_dpy, False);
495 if (!ctxErrorOccurred && ctx)
496 printf("Created GL 3.3 context\n");
497 else
498 {
499 // Couldn't create GL 3.3 context. Fall back to old-style 2.x context.
500 // When a context version below 3.0 is requested, implementations will
501 // return the newest context version compatible with OpenGL versions less
502 // than version 3.0.
503 // GLX_CONTEXT_MAJOR_VERSION_ARB = 1
504 context_attribs[1] = 1;
505 // GLX_CONTEXT_MINOR_VERSION_ARB = 0
506 context_attribs[3] = 0;
507
508 ctxErrorOccurred = false;
509
510 printf(
511 "Failed to create GL 3.3 context"
512 " ... using old-style GLX context\n");
513 ctx = glXCreateContextAttribsARB(m_data->m_dpy, m_data->m_bestFbc, 0,
514 True, context_attribs);
515 }
516 }
517
518 // Sync to ensure any errors generated are processed.
519 MyXSync(m_data->m_dpy, False);
520
521 // Restore the original error handler
522 MyXSetErrorHandler(oldHandler);
523
524 if (ctxErrorOccurred || !ctx)
525 {
526 fprintf(stderr, "Failed to create an OpenGL context\n");
527 exit(1);
528 }
529
530 // Verifying that context is a direct context
531 if (!glXIsDirect(m_data->m_dpy, ctx))
532 {
533 printf("Indirect GLX rendering context obtained\n");
534 }
535 else
536 {
537 printf("Direct GLX rendering context obtained\n");
538 }
539
540 printf("Making context current\n");
541 glXMakeCurrent(m_data->m_dpy, m_data->m_win, ctx);
542 m_data->m_glc = ctx;
543 }
544 else
545 {
546 m_data->m_glc = glXCreateContext(m_data->m_dpy, m_data->m_vi, NULL, GL_TRUE);
547 glXMakeCurrent(m_data->m_dpy, m_data->m_win, m_data->m_glc);
548 }
549
550 if (!gladLoaderLoadGL())
551 {
552 printf("gladLoadGL failed!\n");
553 exit(-1);
554 }
555
556 const GLubyte* ven = glGetString(GL_VENDOR);
557 printf("GL_VENDOR=%s\n", ven);
558
559 const GLubyte* ren = glGetString(GL_RENDERER);
560 printf("GL_RENDERER=%s\n", ren);
561 const GLubyte* ver = glGetString(GL_VERSION);
562 printf("GL_VERSION=%s\n", ver);
563 const GLubyte* sl = glGetString(GL_SHADING_LANGUAGE_VERSION);
564 printf("GL_SHADING_LANGUAGE_VERSION=%s\n", sl);
565
566 //Access pthreads as a workaround for a bug in Linux/Ubuntu
567 //See https://bugs.launchpad.net/ubuntu/+source/nvidia-graphics-drivers-319/+bug/1248642
568
569 #if !defined(__NetBSD__)
570 int i = pthread_getconcurrency();
571 printf("pthread_getconcurrency()=%d\n", i);
572 #endif
573
574 // const GLubyte* ext = glGetString(GL_EXTENSIONS);
575 // printf("GL_EXTENSIONS=%s\n", ext);
576 }
577
disableOpenGL()578 void X11OpenGLWindow::disableOpenGL()
579 {
580 glXMakeCurrent(m_data->m_dpy, None, NULL);
581 glXDestroyContext(m_data->m_dpy, m_data->m_glc);
582 }
583
createWindow(const b3gWindowConstructionInfo & ci)584 void X11OpenGLWindow::createWindow(const b3gWindowConstructionInfo& ci)
585 {
586 m_data->m_dpy = MyXOpenDisplay(NULL);
587
588 m_data->m_glWidth = ci.m_width;
589 m_data->m_glHeight = ci.m_height;
590
591 if (m_data->m_dpy == NULL)
592 {
593 fprintf(stderr, "\n\tcannot connect to X server\n\n");
594 exit(EXIT_FAILURE);
595 }
596
597 m_data->m_root = DefaultRootWindow(m_data->m_dpy);
598
599 #ifdef GLEW_DYNAMIC_LOAD_ALL_GLX_FUNCTIONS
600
601 int res = gladLoaderLoadGLX(m_data->m_dpy, DefaultScreen(m_data->m_dpy));
602 if (!res)
603 {
604 printf("Error in gladLoadGLX\n");
605 exit(0);
606 }
607
608 #endif
609
610 if (ci.m_openglVersion < 3)
611 {
612 forceOpenGL3 = false;
613 }
614
615 if (forceOpenGL3)
616 {
617 int glxMinor, glxMajor;
618 if (!glXQueryVersion(m_data->m_dpy, &glxMajor, &glxMinor) || (((glxMajor == 1) && (glxMinor < 3)) || (glxMajor < 1)))
619 {
620 fprintf(stderr, "Invalid GLX version: major %d, minor %d\n", glxMajor, glxMinor);
621 exit(EXIT_FAILURE);
622 }
623
624 static int visual_attribs[] =
625 {
626 GLX_X_RENDERABLE, True,
627 GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT,
628 GLX_RENDER_TYPE, GLX_RGBA_BIT,
629 GLX_X_VISUAL_TYPE, GLX_TRUE_COLOR,
630 GLX_RED_SIZE, 8,
631 GLX_GREEN_SIZE, 8,
632 GLX_BLUE_SIZE, 8,
633 GLX_ALPHA_SIZE, 8,
634 GLX_DEPTH_SIZE, 24,
635 GLX_STENCIL_SIZE, 8,
636 GLX_DOUBLEBUFFER, True,
637 None};
638 int fbcount;
639 GLXFBConfig* fbc = glXChooseFBConfig(m_data->m_dpy, DefaultScreen(m_data->m_dpy), visual_attribs, &fbcount);
640 if (!fbc)
641 {
642 fprintf(stderr, "Failed to retrieve a framebuffer config\n");
643 exit(1);
644 }
645 ///don't use highest samples, it is really slow on some NVIDIA Quadro cards
646 #ifdef USE_HIGHEST_SAMPLES
647 int best_fbc = -1, worst_fbc = -1, best_num_samp = -1, worst_num_samp = 999;
648
649 int i;
650 for (i = 0; i < fbcount; ++i)
651 {
652 XVisualInfo* vi = glXGetVisualFromFBConfig(m_data->m_dpy, fbc[i]);
653 if (vi)
654 {
655 int samp_buf, samples;
656 glXGetFBConfigAttrib(m_data->m_dpy, fbc[i], GLX_SAMPLE_BUFFERS, &samp_buf);
657 glXGetFBConfigAttrib(m_data->m_dpy, fbc[i], GLX_SAMPLES, &samples);
658
659 //printf( " Matching fbconfig %d, visual ID 0x%2x: SAMPLE_BUFFERS = %d,"
660 // " SAMPLES = %d\n",
661 // i, vi -> visualid, samp_buf, samples );
662
663 if (best_fbc < 0 || (samp_buf && (samples > best_num_samp)))
664 best_fbc = i, best_num_samp = samples;
665 if (worst_fbc < 0 || (!samp_buf || (samples < worst_num_samp)))
666 worst_fbc = i, worst_num_samp = samples;
667 }
668 MyXFree(vi);
669 }
670
671 m_data->m_bestFbc = fbc[best_fbc];
672 #else
673 m_data->m_bestFbc = *fbc;
674 #endif
675 // Be sure to free the FBConfig list allocated by glXChooseFBConfig()
676 MyXFree(fbc);
677
678 m_data->m_vi = glXGetVisualFromFBConfig(m_data->m_dpy, m_data->m_bestFbc);
679
680 m_data->m_swa.colormap = m_data->m_cmap = MyXCreateColormap(m_data->m_dpy,
681 RootWindow(m_data->m_dpy, m_data->m_vi->screen),
682 m_data->m_vi->visual, AllocNone);
683 m_data->m_swa.background_pixmap = None;
684 m_data->m_swa.border_pixel = 0;
685 m_data->m_swa.event_mask = ExposureMask | KeyReleaseMask | KeyPressMask | ButtonPressMask | ButtonReleaseMask | PointerMotionMask | StructureNotifyMask;
686 ;
687 m_data->m_root = RootWindow(m_data->m_dpy, m_data->m_vi->screen);
688
689 m_data->m_win = MyXCreateWindow(m_data->m_dpy, m_data->m_root,
690 0, 0, ci.m_width, ci.m_height, 0, m_data->m_vi->depth, InputOutput,
691 m_data->m_vi->visual,
692 CWBorderPixel | CWColormap | CWEventMask, &m_data->m_swa);
693
694 //m_data->m_win = m_data->m_x11_XCreateWindow(m_data->m_dpy, m_data->m_root, 0, 0, ci.m_width, ci.m_height, 0, m_data->m_vi->depth, InputOutput, m_data->m_vi->visual, CWColormap | CWEventMask, &m_data->m_swa);
695
696 if (!m_data->m_win)
697 {
698 fprintf(stderr, "Cannot create window\n");
699 exit(EXIT_FAILURE);
700 }
701
702 MyXMapWindow(m_data->m_dpy, m_data->m_win);
703 MyXStoreName(m_data->m_dpy, m_data->m_win, "OpenGL3 Window");
704 }
705 else
706 {
707 m_data->m_vi = glXChooseVisual(m_data->m_dpy, 0, att);
708
709 printf("4\n");
710
711 if (m_data->m_vi == NULL)
712 {
713 fprintf(stderr, "\n\tno appropriate visual found\n\n");
714 exit(EXIT_FAILURE);
715 }
716 else
717 {
718 printf("\n\tvisual %p selected\n", (void*)m_data->m_vi->visualid); /* %p creates hexadecimal output like in glxinfo */
719 }
720
721 m_data->m_cmap = MyXCreateColormap(m_data->m_dpy, m_data->m_root, m_data->m_vi->visual, AllocNone);
722 m_data->m_swa.colormap = m_data->m_cmap;
723 m_data->m_swa.event_mask = ExposureMask | KeyReleaseMask | KeyPressMask | ButtonPressMask | ButtonReleaseMask | PointerMotionMask | StructureNotifyMask;
724 m_data->m_win = MyXCreateWindow(m_data->m_dpy, m_data->m_root, 0, 0, ci.m_width, ci.m_height, 0, m_data->m_vi->depth, InputOutput, m_data->m_vi->visual, CWColormap | CWEventMask, &m_data->m_swa);
725
726 MyXMapWindow(m_data->m_dpy, m_data->m_win);
727
728 MyXStoreName(m_data->m_dpy, m_data->m_win, "OpenGL2 Window");
729 }
730
731 enableOpenGL();
732 }
733
closeWindow()734 void X11OpenGLWindow::closeWindow()
735 {
736 disableOpenGL();
737
738 MyXDestroyWindow(m_data->m_dpy, m_data->m_win);
739 MyXCloseDisplay(m_data->m_dpy);
740 }
741
getAsciiCodeFromVirtualKeycode(int keycode)742 int X11OpenGLWindow::getAsciiCodeFromVirtualKeycode(int keycode)
743 {
744 int result = 0;
745
746 KeySym key, key_lc, key_uc;
747
748 int keysyms_per_keycode_return;
749 KeySym* keysym = MyXGetKeyboardMapping(m_data->m_dpy,
750 keycode,
751 1,
752 &keysyms_per_keycode_return);
753
754 key = keysym[0];
755
756 //key = MyXKeycodeToKeysym( m_data->m_dpy, keycode, 0 );
757
758 switch (key)
759 {
760 case XK_Escape:
761 return B3G_ESCAPE;
762 case XK_Return:
763 return B3G_RETURN;
764
765 case XK_Control_L:
766 case XK_Control_R:
767 {
768 return B3G_CONTROL;
769 }
770 case XK_Left:
771 return B3G_LEFT_ARROW;
772 case XK_Right:
773 return B3G_RIGHT_ARROW;
774 case XK_Up:
775 return B3G_UP_ARROW;
776 case XK_Down:
777 return B3G_DOWN_ARROW;
778
779 case XK_Alt_L:
780 case XK_Alt_R:
781 {
782 return B3G_ALT;
783 }
784 case XK_Shift_L:
785 case XK_Shift_R:
786 return B3G_SHIFT;
787 case XK_F1:
788 return B3G_F1;
789 case XK_F2:
790 return B3G_F2;
791 case XK_F3:
792 return B3G_F3;
793 case XK_F4:
794 return B3G_F4;
795 case XK_F5:
796 return B3G_F5;
797 case XK_F6:
798 return B3G_F6;
799 case XK_F7:
800 return B3G_F7;
801 case XK_F8:
802 return B3G_F8;
803 case XK_F9:
804 return B3G_F9;
805 case XK_F10:
806 return B3G_F10;
807 case XK_F11:
808 return B3G_F11;
809 case XK_F12:
810 return B3G_F12;
811 case XK_F13:
812 return B3G_F13;
813 case XK_F14:
814 return B3G_F14;
815 case XK_F15:
816 return B3G_F15;
817 default:
818 // Make lowercase
819 MyXConvertCase(key, &key_lc, &key_uc);
820 key = key_lc;
821 // Valid ISO 8859-1 character?
822 if ((key >= 32 && key <= 126) || (key >= 160 && key <= 255))
823 {
824 return (int)key;
825 }
826 result = -1;
827 }
828
829 MyXFree(keysym);
830
831 return result;
832 }
833
isModifierKeyPressed(int key)834 bool X11OpenGLWindow::isModifierKeyPressed(int key)
835 {
836 bool isPressed = false;
837
838 switch (key)
839 {
840 case B3G_ALT:
841 {
842 isPressed = ((m_data->m_modifierFlags & MY_X11_ALT_KEY) != 0);
843 break;
844 };
845 case B3G_SHIFT:
846 {
847 isPressed = ((m_data->m_modifierFlags & MY_X11_SHIFT_KEY) != 0);
848 break;
849 };
850 case B3G_CONTROL:
851 {
852 isPressed = ((m_data->m_modifierFlags & MY_X11_CONTROL_KEY) != 0);
853 break;
854 };
855
856 default:
857 {
858 }
859 };
860 return isPressed;
861 }
862
pumpMessage()863 void X11OpenGLWindow::pumpMessage()
864 {
865 int buttonState = 1;
866
867 // Process all pending events
868 while (MyXPending(m_data->m_dpy))
869 {
870 MyXNextEvent(m_data->m_dpy, &m_data->m_xev);
871 // printf("#");
872 // fflush(stdout);
873 switch (m_data->m_xev.type)
874 {
875 case KeyPress:
876 {
877 int keycode = getAsciiCodeFromVirtualKeycode(m_data->m_xev.xkey.keycode);
878 switch (keycode)
879 {
880 case B3G_ALT:
881 m_data->m_modifierFlags |= MY_X11_ALT_KEY;
882 break;
883 case B3G_SHIFT:
884 m_data->m_modifierFlags |= MY_X11_SHIFT_KEY;
885 break;
886 case B3G_CONTROL:
887 m_data->m_modifierFlags |= MY_X11_CONTROL_KEY;
888 break;
889 default:
890 {
891 }
892 };
893 if (m_data->m_keyboardCallback)
894 {
895 int state = 1;
896 (*m_data->m_keyboardCallback)(keycode, state);
897 // printf("keycode %d",keycode);
898 // fflush(stdout);
899 }
900 break;
901 }
902
903 case KeyRelease:
904 {
905 // fflush(stdout);
906 int keycode = getAsciiCodeFromVirtualKeycode(m_data->m_xev.xkey.keycode);
907 switch (keycode)
908 {
909 case B3G_ALT:
910 m_data->m_modifierFlags &= ~MY_X11_ALT_KEY;
911 break;
912 case B3G_SHIFT:
913 m_data->m_modifierFlags &= ~MY_X11_SHIFT_KEY;
914 break;
915 case B3G_CONTROL:
916 m_data->m_modifierFlags &= ~MY_X11_CONTROL_KEY;
917 break;
918 default:
919 {
920 }
921 };
922
923 if (m_data->m_keyboardCallback)
924 {
925 #if 1
926 unsigned short is_retriggered = 0;
927 ///filter out keyboard repeat
928 //see http://stackoverflow.com/questions/2100654/ignore-auto-repeat-in-x11-applications
929 if (MyXEventsQueued(m_data->m_dpy, QueuedAfterReading))
930 {
931 XEvent nev;
932 MyXPeekEvent(m_data->m_dpy, &nev);
933
934 if (nev.type == KeyPress && nev.xkey.time == m_data->m_xev.xkey.time &&
935 nev.xkey.keycode == m_data->m_xev.xkey.keycode)
936 {
937 //fprintf (stdout, "key #%ld was retriggered.\n",
938 // (long) MyXLookupKeysym(&nev.xkey, 0));
939
940 // delete retriggered KeyPress event
941 MyXNextEvent(m_data->m_dpy, &m_data->m_xev);
942 is_retriggered = 1;
943 }
944 }
945 #endif
946 int state = 0;
947 if (!is_retriggered)
948 (*m_data->m_keyboardCallback)(keycode, state);
949 }
950
951 break;
952 }
953
954 case ButtonRelease:
955 buttonState = 0;
956 //continue with ButtonPress code
957 case ButtonPress:
958 {
959 // printf("!");
960 // fflush(stdout);
961
962 int button = -1;
963
964 switch (m_data->m_xev.xbutton.button)
965 {
966 case Button1:
967 {
968 button = 0;
969 break;
970 }
971 case Button2:
972 {
973 button = 1;
974 break;
975 }
976 case Button3:
977 {
978 button = 2;
979 break;
980 }
981 case Button4:
982 {
983 if (m_data->m_wheelCallback)
984 {
985 (*m_data->m_wheelCallback)(0, 10);
986 }
987 break;
988 }
989 case Button5:
990 {
991 if (m_data->m_wheelCallback)
992 {
993 (*m_data->m_wheelCallback)(0, -10);
994 }
995 break;
996 }
997 }
998 int xpos = m_data->m_xev.xmotion.x;
999 int ypos = m_data->m_xev.xmotion.y;
1000
1001 if (button >= 0 && m_data->m_mouseButtonCallback)
1002 {
1003 // printf("xpos = %d, ypos = %d\n",xpos,ypos);
1004
1005 (*m_data->m_mouseButtonCallback)(button, buttonState, xpos, ypos);
1006 }
1007 break;
1008 }
1009 case MotionNotify:
1010 {
1011 // printf("!");
1012 // fflush(0);
1013 if (m_data->m_mouseMoveCallback)
1014 {
1015 int xpos = m_data->m_xev.xmotion.x;
1016 int ypos = m_data->m_xev.xmotion.y;
1017 (*m_data->m_mouseMoveCallback)(xpos, ypos);
1018 }
1019 break;
1020 }
1021 case ConfigureNotify:
1022 {
1023 // printf("@");
1024 // fflush(0);
1025 m_data->m_glWidth = m_data->m_xev.xconfigure.width;
1026 m_data->m_glHeight = m_data->m_xev.xconfigure.height;
1027
1028 if (m_data->m_resizeCallback)
1029 {
1030 (*m_data->m_resizeCallback)(m_data->m_xev.xconfigure.width, m_data->m_xev.xconfigure.height);
1031 }
1032 break;
1033 }
1034 case ClientMessage:
1035 {
1036 // printf("?");
1037 // fflush(stdout);
1038 break;
1039 }
1040 case Expose:
1041 {
1042 break;
1043 }
1044 case DestroyNotify:
1045 {
1046 break;
1047 }
1048 default:
1049 {
1050 //XRRUpdateConfiguration( &event );
1051 }
1052 };
1053 }
1054 }
1055
startRendering()1056 void X11OpenGLWindow::startRendering()
1057 {
1058 pumpMessage();
1059
1060 MyXGetWindowAttributes(m_data->m_dpy, m_data->m_win, &m_data->m_gwa);
1061 glViewport(0, 0, m_data->m_gwa.width, m_data->m_gwa.height);
1062
1063 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); //clear buffers
1064
1065 //glCullFace(GL_BACK);
1066 //glFrontFace(GL_CCW);
1067 glEnable(GL_DEPTH_TEST);
1068 }
1069
renderAllObjects()1070 void X11OpenGLWindow::renderAllObjects()
1071 {
1072 }
1073
endRendering()1074 void X11OpenGLWindow::endRendering()
1075 {
1076 glXSwapBuffers(m_data->m_dpy, m_data->m_win);
1077 }
1078
runMainLoop()1079 void X11OpenGLWindow::runMainLoop()
1080 {
1081 }
1082
getTimeInSeconds()1083 float X11OpenGLWindow::getTimeInSeconds()
1084 {
1085 return 0.f;
1086 }
1087
requestedExit() const1088 bool X11OpenGLWindow::requestedExit() const
1089 {
1090 return m_requestedExit;
1091 }
1092
setRequestExit()1093 void X11OpenGLWindow::setRequestExit()
1094 {
1095 m_requestedExit = true;
1096 }
1097
setRenderCallback(b3RenderCallback renderCallback)1098 void X11OpenGLWindow::setRenderCallback(b3RenderCallback renderCallback)
1099 {
1100 }
1101
setWindowTitle(const char * title)1102 void X11OpenGLWindow::setWindowTitle(const char* title)
1103 {
1104 MyXStoreName(m_data->m_dpy, m_data->m_win, title);
1105 }
1106
setWheelCallback(b3WheelCallback wheelCallback)1107 void X11OpenGLWindow::setWheelCallback(b3WheelCallback wheelCallback)
1108 {
1109 m_data->m_wheelCallback = wheelCallback;
1110 }
1111
setMouseMoveCallback(b3MouseMoveCallback mouseCallback)1112 void X11OpenGLWindow::setMouseMoveCallback(b3MouseMoveCallback mouseCallback)
1113 {
1114 m_data->m_mouseMoveCallback = mouseCallback;
1115 }
1116
setMouseButtonCallback(b3MouseButtonCallback mouseCallback)1117 void X11OpenGLWindow::setMouseButtonCallback(b3MouseButtonCallback mouseCallback)
1118 {
1119 m_data->m_mouseButtonCallback = mouseCallback;
1120 }
1121
setResizeCallback(b3ResizeCallback resizeCallback)1122 void X11OpenGLWindow::setResizeCallback(b3ResizeCallback resizeCallback)
1123 {
1124 if (resizeCallback && m_data->m_glWidth > 0 && m_data->m_glHeight > 0)
1125 {
1126 resizeCallback(m_data->m_glWidth, m_data->m_glHeight);
1127 }
1128 m_data->m_resizeCallback = resizeCallback;
1129 }
1130
setKeyboardCallback(b3KeyboardCallback keyboardCallback)1131 void X11OpenGLWindow::setKeyboardCallback(b3KeyboardCallback keyboardCallback)
1132 {
1133 m_data->m_keyboardCallback = keyboardCallback;
1134 }
1135
getMouseMoveCallback()1136 b3MouseMoveCallback X11OpenGLWindow::getMouseMoveCallback()
1137 {
1138 return m_data->m_mouseMoveCallback;
1139 }
getMouseButtonCallback()1140 b3MouseButtonCallback X11OpenGLWindow::getMouseButtonCallback()
1141 {
1142 return m_data->m_mouseButtonCallback;
1143 }
getResizeCallback()1144 b3ResizeCallback X11OpenGLWindow::getResizeCallback()
1145 {
1146 return m_data->m_resizeCallback;
1147 }
getWheelCallback()1148 b3WheelCallback X11OpenGLWindow::getWheelCallback()
1149 {
1150 return m_data->m_wheelCallback;
1151 }
1152
getKeyboardCallback()1153 b3KeyboardCallback X11OpenGLWindow::getKeyboardCallback()
1154 {
1155 return m_data->m_keyboardCallback;
1156 }
1157
getWidth() const1158 int X11OpenGLWindow::getWidth() const
1159 {
1160 if (m_data)
1161 return m_data->m_glWidth;
1162 return 0;
1163 }
getHeight() const1164 int X11OpenGLWindow::getHeight() const
1165 {
1166 if (m_data)
1167 return m_data->m_glHeight;
1168 return 0;
1169 }
1170
1171 #include <stdio.h>
1172
fileOpenDialog(char * filename,int maxNameLength)1173 int X11OpenGLWindow::fileOpenDialog(char* filename, int maxNameLength)
1174 {
1175 int len = 0;
1176 FILE* output = popen("zenity --file-selection --file-filter=\"*.urdf\" --file-filter=\"*.sdf\" --file-filter=\"*.obj\" --file-filter=\"*.*\"", "r");
1177 if (output)
1178 {
1179 while (fgets(filename, maxNameLength - 1, output) != NULL)
1180 {
1181 len = strlen(filename);
1182 if (len > 0)
1183 {
1184 filename[len - 1] = 0;
1185 printf("file open (length=%d) = %s\n", len, filename);
1186 }
1187 }
1188 pclose(output);
1189 }
1190 else
1191 {
1192 printf("Error: fileOpenDialog no popen output, perhaps install zenity?\n");
1193 }
1194 MyXRaiseWindow(m_data->m_dpy, m_data->m_win);
1195 return len;
1196 }
1197 #endif
1198