1 // Copyright 2005-2019 The Mumble Developers. All rights reserved.
2 // Use of this source code is governed by a BSD-style license
3 // that can be found in the LICENSE file at the root of the
4 // Mumble source tree or at <https://www.mumble.info/LICENSE>.
5
6 #define GLX_GLXEXT_LEGACY
7 #define GL_GLEXT_PROTOTYPES
8 #define _GNU_SOURCE
9 #include <dlfcn.h>
10 #include <stdio.h>
11 #include <unistd.h>
12 #include <stdlib.h>
13 #include <string.h>
14 #include <sys/types.h>
15 #include <sys/mman.h>
16 #include <sys/ipc.h>
17 #include <sys/time.h>
18 #include <sys/socket.h>
19 #include <sys/stat.h>
20 #include <sys/un.h>
21 #include <sys/stat.h>
22 #include <time.h>
23 #include <semaphore.h>
24 #include <fcntl.h>
25 #include <stdarg.h>
26 #include <pwd.h>
27 #include <math.h>
28 #include <errno.h>
29 #include <time.h>
30 #include <limits.h>
31
32 #if defined(TARGET_UNIX)
33 # define GLX_GLXEXT_LEGACY
34 # define GL_GLEXT_PROTOTYPES
35 # define _GNU_SOURCE
36 # include <GL/glx.h>
37 # include <GL/gl.h>
38 # include <GL/glext.h>
39
40 #include <link.h>
41
42 typedef unsigned char bool;
43 # define true 1
44 # define false 0
45 #elif defined(TARGET_MAC)
46 # include <OpenGL/OpenGL.h>
47 # include <Carbon/Carbon.h>
48 # include <Cocoa/Cocoa.h>
49 # include <AGL/agl.h>
50 #
51 # include <objc/objc-runtime.h>
52 #
53 # include "mach_override.h"
54 #
55 # include "avail_mac.h"
56 #endif
57
58 #include "../overlay/overlay.h"
59
60 static bool bDebug = false;
61 static bool bCursorAvail = false;
62
63 typedef struct _Context {
64 struct _Context *next;
65
66 #if defined(TARGET_UNIX)
67 Display *dpy;
68 GLXDrawable draw;
69 #elif defined(TARGET_MAC)
70 CGLContextObj cglctx;
71 NSOpenGLContext *nsctx;
72 #endif
73
74 unsigned int uiWidth, uiHeight;
75 unsigned int uiLeft, uiRight, uiTop, uiBottom;
76
77 struct sockaddr_un saName;
78 int iSocket;
79 // overlay message, temporary variable for processing from socket
80 struct OverlayMsg omMsg;
81 // opengl overlay texture
82 GLuint texture;
83
84 // overlay texture in shared memory
85 unsigned char *a_ucTexture;
86 unsigned int uiMappedLength;
87
88 bool bValid;
89 bool bMesa;
90
91 GLuint uiProgram;
92
93 clock_t timeT;
94 unsigned int frameCount;
95
96 GLint maxVertexAttribs;
97 GLboolean* vertexAttribStates;
98 } Context;
99
100 static const char vshader[] = ""
101 "void main() {"
102 "gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;"
103 "gl_TexCoord[0] = gl_MultiTexCoord0;"
104 "}";
105
106 static const char fshader[] = ""
107 "uniform sampler2D tex;"
108 "void main() {"
109 "gl_FragColor = texture2D(tex, gl_TexCoord[0].st);"
110 "}";
111
112 const GLfloat fBorder[] = {0.125f, 0.250f, 0.5f, 0.75f};
113
114 static Context *contexts = NULL;
115
116 #define AVAIL(name) dlsym(RTLD_DEFAULT,#name)
117 #define FDEF(name) static __typeof__(&name) o##name = NULL
118
119 #if defined(TARGET_UNIX)
120 FDEF(dlsym);
121 FDEF(glXSwapBuffers);
122 FDEF(glXGetProcAddressARB);
123 FDEF(glXGetProcAddress);
124 #elif defined(TARGET_MAC)
125 FDEF(CGLFlushDrawable);
126 FDEF(CGDisplayHideCursor);
127 FDEF(CGDisplayShowCursor);
128 #endif
129
130 __attribute__((format(printf, 1, 2)))
ods(const char * format,...)131 static void ods(const char *format, ...) {
132 if (! bDebug) {
133 return;
134 }
135
136 fprintf(stderr, "MumbleOverlay: ");
137
138 va_list args;
139 va_start(args, format);
140 vfprintf(stderr, format, args);
141 va_end(args);
142 fprintf(stderr, "\n");
143 fflush(stderr);
144 }
145
newContext(Context * ctx)146 static void newContext(Context * ctx) {
147 ctx->iSocket = -1;
148 ctx->omMsg.omh.iLength = -1;
149 ctx->texture = ~0U;
150 ctx->timeT = clock();
151 ctx->frameCount = 0;
152
153 char *home = getenv("HOME");
154 if (home == NULL) {
155 struct passwd *pwent= getpwuid(getuid());
156 if (pwent && pwent->pw_dir && pwent->pw_dir[0]) {
157 home = pwent->pw_dir;
158 }
159 }
160
161 char *xdgRuntimeDir = getenv("XDG_RUNTIME_DIR");
162
163 if (xdgRuntimeDir != NULL) {
164 ctx->saName.sun_family = PF_UNIX;
165 strcpy(ctx->saName.sun_path, xdgRuntimeDir);
166 strcat(ctx->saName.sun_path, "/MumbleOverlayPipe");
167 } else if (home) {
168 ctx->saName.sun_family = PF_UNIX;
169 strcpy(ctx->saName.sun_path, home);
170 strcat(ctx->saName.sun_path, "/.MumbleOverlayPipe");
171 }
172
173 ods("OpenGL Version %s, Vendor %s, Renderer %s, Shader %s", glGetString(GL_VERSION), glGetString(GL_VENDOR), glGetString(GL_RENDERER), glGetString(GL_SHADING_LANGUAGE_VERSION));
174
175 const char *vsource = vshader;
176 const char *fsource = fshader;
177 char buffer[8192];
178 GLint l;
179 GLuint vs = glCreateShader(GL_VERTEX_SHADER);
180 GLuint fs = glCreateShader(GL_FRAGMENT_SHADER);
181 glShaderSource(vs, 1, &vsource, NULL);
182 glShaderSource(fs, 1, &fsource, NULL);
183 glCompileShader(vs);
184 glCompileShader(fs);
185 glGetShaderInfoLog(vs, 8192, &l, buffer);
186 ods("VERTEX: %s", buffer);
187 glGetShaderInfoLog(fs, 8192, &l, buffer);
188 ods("FRAGMENT: %s", buffer);
189 ctx->uiProgram = glCreateProgram();
190 glAttachShader(ctx->uiProgram, vs);
191 glAttachShader(ctx->uiProgram, fs);
192 glLinkProgram(ctx->uiProgram);
193
194 glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &ctx->maxVertexAttribs);
195 ctx->vertexAttribStates = calloc((size_t) ctx->maxVertexAttribs, sizeof(GLboolean));
196 }
197
releaseMem(Context * ctx)198 static void releaseMem(Context *ctx) {
199 if (ctx->a_ucTexture) {
200 munmap(ctx->a_ucTexture, ctx->uiMappedLength);
201 ctx->a_ucTexture = NULL;
202 ctx->uiMappedLength = 0;
203 }
204 if (ctx->texture != ~0U) {
205 glDeleteTextures(1, &ctx->texture);
206 ctx->texture = ~0U;
207 }
208 ctx->uiLeft = ctx->uiTop = ctx->uiRight = ctx->uiBottom = 0;
209 }
210
disconnect(Context * ctx)211 static void disconnect(Context *ctx) {
212 releaseMem(ctx);
213 ctx->uiWidth = ctx->uiHeight = 0;
214 if (ctx->iSocket != -1) {
215 close(ctx->iSocket);
216 ctx->iSocket = -1;
217 }
218 ods("Disconnected");
219 }
220
sendMessage(Context * ctx,struct OverlayMsg * om)221 static bool sendMessage(Context *ctx, struct OverlayMsg *om) {
222 if (ctx->iSocket != -1) {
223 size_t wantsend = sizeof(struct OverlayMsgHeader) + (size_t)om->omh.iLength;
224 ssize_t sent = send(ctx->iSocket, om, wantsend, MSG_DONTWAIT);
225 if (sent != -1 && wantsend == (size_t)sent) {
226 return true;
227 }
228 ods("Short write. Disconnecting pipe.");
229 }
230 disconnect(ctx);
231 return false;
232 }
233
regenTexture(Context * ctx)234 static void regenTexture(Context *ctx) {
235 if (ctx->texture != ~0U) {
236 glDeleteTextures(1, & ctx->texture);
237 }
238 glGenTextures(1, &ctx->texture);
239
240 glBindTexture(GL_TEXTURE_2D, ctx->texture);
241 glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, fBorder);
242 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);
243 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
244 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
245 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
246 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
247 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (GLsizei)ctx->uiWidth, (GLsizei)ctx->uiHeight, 0, GL_BGRA, GL_UNSIGNED_BYTE, ctx->a_ucTexture);
248 }
249
drawOverlay(Context * ctx,unsigned int width,unsigned int height)250 static void drawOverlay(Context *ctx, unsigned int width, unsigned int height) {
251 // if no socket is active, initialize and connect to socket
252 if (ctx->iSocket == -1) {
253 releaseMem(ctx);
254 if (! ctx->saName.sun_path[0])
255 return;
256 ctx->iSocket = socket(AF_UNIX, SOCK_STREAM, 0);
257 if (ctx->iSocket == -1) {
258 ods("socket() failure");
259 return;
260 }
261 fcntl(ctx->iSocket, F_SETFL, O_NONBLOCK, 1);
262 if (connect(ctx->iSocket, (struct sockaddr *)(& ctx->saName), sizeof(ctx->saName)) != 0) {
263 close(ctx->iSocket);
264 ctx->iSocket = -1;
265 ods("connect() failure %s", ctx->saName.sun_path);
266 return;
267 }
268 ods("Socket connected");
269
270 struct OverlayMsg om;
271 om.omh.uiMagic = OVERLAY_MAGIC_NUMBER;
272 om.omh.uiType = OVERLAY_MSGTYPE_PID;
273 om.omh.iLength = sizeof(struct OverlayMsgPid);
274 om.omp.pid = (unsigned int)getpid(); // getpid can't fail
275
276 if (!sendMessage(ctx, &om))
277 return;
278
279 ods("SentPid");
280 }
281
282 // if overlay size (width or height) is not up-to-date create and send an overlay initialization message
283 if ((ctx->uiWidth != width) || (ctx->uiHeight != height)) {
284 ods("Sending init overlay msg with w h %i %i", width, height);
285 releaseMem(ctx);
286
287 ctx->uiWidth = width;
288 ctx->uiHeight = height;
289
290 struct OverlayMsg om;
291 om.omh.uiMagic = OVERLAY_MAGIC_NUMBER;
292 om.omh.uiType = OVERLAY_MSGTYPE_INIT;
293 om.omh.iLength = sizeof(struct OverlayMsgInit);
294 om.omi.uiWidth = ctx->uiWidth;
295 om.omi.uiHeight = ctx->uiHeight;
296
297 if (! sendMessage(ctx, &om))
298 return;
299 }
300
301 // receive and process overlay messages
302 while (1) {
303 if (ctx->omMsg.omh.iLength < 0) {
304 // receive the overlay message header
305 ssize_t length = recv(ctx->iSocket, ctx->omMsg.headerbuffer, sizeof(struct OverlayMsgHeader), 0);
306 if (length < 0) {
307 if ((errno == EAGAIN) || (errno == EWOULDBLOCK))
308 break;
309 disconnect(ctx);
310 return;
311 } else if (length != sizeof(struct OverlayMsgHeader)) {
312 ods("Short header read on overlay message");
313 disconnect(ctx);
314 return;
315 }
316 } else {
317 // receive the overlay message body
318 ssize_t length = recv(ctx->iSocket, ctx->omMsg.msgbuffer, (size_t)ctx->omMsg.omh.iLength, 0);
319 if (length < 0) {
320 if ((errno == EAGAIN) || (errno == EWOULDBLOCK))
321 break;
322 disconnect(ctx);
323 return;
324 } else if (length != ctx->omMsg.omh.iLength) {
325 ods("Short overlay message read %x %zd/%d", ctx->omMsg.omh.uiType, length, ctx->omMsg.omh.iLength);
326 disconnect(ctx);
327 return;
328 }
329 // set len to -1 again for a clean state on next receive
330 ctx->omMsg.omh.iLength = -1;
331
332 switch (ctx->omMsg.omh.uiType) {
333 // shared memory overlay message:
334 case OVERLAY_MSGTYPE_SHMEM: {
335 struct OverlayMsgShmem *oms = (struct OverlayMsgShmem *) & ctx->omMsg.omi;
336 ods("SHMEM %s", oms->a_cName);
337 releaseMem(ctx);
338 int fd = shm_open(oms->a_cName, O_RDONLY, 0600);
339 if (fd != -1) {
340 struct stat buf;
341
342 if (fstat(fd, &buf) != -1) {
343 unsigned int buflen = buf.st_size;
344 if (buflen >= ctx->uiWidth * ctx->uiHeight * 4
345 && buflen < 512 * 1024 * 1024) {
346 ctx->uiMappedLength = buflen;
347 ctx->a_ucTexture = mmap(NULL, (size_t)buflen, PROT_READ, MAP_SHARED, fd, 0);
348 if (ctx->a_ucTexture != MAP_FAILED) {
349 // mmap successfull; send a new bodyless sharedmemory overlay message and regenerate the overlay texture
350 struct OverlayMsg om;
351 om.omh.uiMagic = OVERLAY_MAGIC_NUMBER;
352 om.omh.uiType = OVERLAY_MSGTYPE_SHMEM;
353 om.omh.iLength = 0;
354
355 if (! sendMessage(ctx, &om))
356 return;
357
358 regenTexture(ctx);
359 continue;
360 }
361 ctx->a_ucTexture = NULL;
362 }
363 ctx->uiMappedLength = 0;
364 } else {
365 ods("Failed to fstat memory map");
366 }
367 close(fd);
368 }
369 ods("Failed to map memory");
370 }
371 break;
372 // blit overlay message: blit overlay texture from shared memory to gl-texture var
373 case OVERLAY_MSGTYPE_BLIT: {
374 struct OverlayMsgBlit *omb = & ctx->omMsg.omb;
375 ods("BLIT %d %d %d %d", omb->x, omb->y, omb->w, omb->h);
376 if ((ctx->a_ucTexture != NULL) && (ctx->texture != ~0U)) {
377 glBindTexture(GL_TEXTURE_2D, ctx->texture);
378
379 if ((omb->x == 0) && (omb->y == 0) && (omb->w == ctx->uiWidth) && (omb->h == ctx->uiHeight)) {
380 ods("Optimzied fullscreen blit");
381 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (GLsizei)ctx->uiWidth, (GLsizei)ctx->uiHeight, 0, GL_BGRA, GL_UNSIGNED_BYTE, ctx->a_ucTexture);
382 } else {
383 // allocate temporary memory
384 unsigned int x = omb->x;
385 unsigned int y = omb->y;
386 unsigned int w = omb->w;
387 unsigned int h = omb->h;
388 unsigned char *ptr = (unsigned char *) malloc(w*h*4);
389 unsigned int row;
390 memset(ptr, 0, w * h * 4);
391
392 // copy overlay texture to temporary memory to adapt to full opengl ui size (overlay at correct place)
393 for (row = 0; row < h; ++row) {
394 const unsigned char *sptr = ctx->a_ucTexture + 4 * ((y+row) * ctx->uiWidth + x);
395 unsigned char *dptr = ptr + 4 * w * row;
396 memcpy(dptr, sptr, w * 4);
397 }
398
399 // copy temporary texture to opengl
400 glTexSubImage2D(GL_TEXTURE_2D, 0, (GLint)x, (GLint)y, (GLint)w, (GLint)h, GL_BGRA, GL_UNSIGNED_BYTE, ptr);
401 free(ptr);
402 }
403 }
404 }
405 break;
406 case OVERLAY_MSGTYPE_ACTIVE: {
407 struct OverlayMsgActive *oma = & ctx->omMsg.oma;
408 ods("ACTIVE %d %d %d %d", oma->x, oma->y, oma->w, oma->h);
409 ctx->uiLeft = oma->x;
410 ctx->uiTop = oma->y;
411 ctx->uiRight = oma->x + oma->w;
412 ctx->uiBottom = oma->y + oma->h;
413 }
414 break;
415 case OVERLAY_MSGTYPE_INTERACTIVE: {
416 #if defined(TARGET_MAC)
417 struct OverlayMsgInteractive *omin = & ctx->omMsg.omin;
418 ods("Interactive %d", omin->state);
419 if (bCursorAvail) {
420 if (omin->state) {
421 oCGDisplayHideCursor(kCGNullDirectDisplay);
422 } else {
423 oCGDisplayShowCursor(kCGNullDirectDisplay);
424 }
425 }
426 #endif
427 }
428 break;
429 default:
430 break;
431 }
432 }
433 }
434
435 if ((ctx->a_ucTexture == NULL) || (ctx->texture == ~0U))
436 return;
437
438 // texture checks, that our gltexture is still valid and sane
439 if (! glIsTexture(ctx->texture)) {
440 ctx->texture = ~0U;
441 ods("Lost texture");
442 regenTexture(ctx);
443 } else {
444 glBindTexture(GL_TEXTURE_2D, ctx->texture);
445 GLfloat bordercolor[4];
446 glGetTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, bordercolor);
447 if (bordercolor[0] != fBorder[0]
448 || bordercolor[1] != fBorder[1]
449 || bordercolor[2] != fBorder[2]
450 || bordercolor[3] != fBorder[3]) {
451 ods("Texture was hijacked! Texture will be regenerated.");
452 regenTexture(ctx);
453 }
454 }
455
456 glBindTexture(GL_TEXTURE_2D, ctx->texture);
457 glPushMatrix();
458
459 float w = (float)(ctx->uiWidth);
460 float h = (float)(ctx->uiHeight);
461
462 float left = (float)(ctx->uiLeft);
463 float top = (float)(ctx->uiTop);
464 float right = (float)(ctx->uiRight);
465 float bottom = (float)(ctx->uiBottom);
466
467 float xm = left / w;
468 float ym = top / h;
469 float xmx = right / w;
470 float ymx = bottom / h;
471
472 GLfloat vertex[] = {
473 left, bottom,
474 left, top,
475 right, top,
476
477 left, bottom,
478 right, top,
479 right, bottom
480 };
481 glVertexPointer(2, GL_FLOAT, 0, vertex);
482
483 GLfloat tex[] = {
484 xm, ymx,
485 xm, ym,
486 xmx, ym,
487
488 xm, ymx,
489 xmx, ym,
490 xmx, ymx
491 };
492 glTexCoordPointer(2, GL_FLOAT, 0, tex);
493
494 glDrawArrays(GL_TRIANGLES, 0, 6);
495
496 glPopMatrix();
497 }
498
drawContext(Context * ctx,int width,int height)499 static void drawContext(Context * ctx, int width, int height) {
500 // calculate FPS and send it as an overlay message
501 clock_t t = clock();
502 float elapsed = (float)(t - ctx->timeT) / CLOCKS_PER_SEC;
503 ++(ctx->frameCount);
504 if (elapsed > OVERLAY_FPS_INTERVAL) {
505 struct OverlayMsg om;
506 om.omh.uiMagic = OVERLAY_MAGIC_NUMBER;
507 om.omh.uiType = OVERLAY_MSGTYPE_FPS;
508 om.omh.iLength = sizeof(struct OverlayMsgFps);
509 om.omf.fps = (float)ctx->frameCount / elapsed;
510
511 sendMessage(ctx, &om);
512
513 ctx->frameCount = 0;
514 ctx->timeT = t;
515 }
516
517 GLuint program;
518 GLint viewport[4];
519 int i;
520
521 glPushAttrib(GL_ALL_ATTRIB_BITS);
522 glPushClientAttrib(GL_ALL_ATTRIB_BITS);
523 glGetIntegerv(GL_VIEWPORT, viewport);
524 glGetIntegerv(GL_CURRENT_PROGRAM, (GLint *)&program);
525
526 glViewport(0, 0, width, height);
527
528 glMatrixMode(GL_PROJECTION);
529 glPushMatrix();
530 glLoadIdentity();
531 glOrtho(0, width, height, 0, -100.0, 100.0);
532
533 glMatrixMode(GL_MODELVIEW);
534 glPushMatrix();
535 glLoadIdentity();
536
537 glMatrixMode(GL_TEXTURE);
538 glPushMatrix();
539 glLoadIdentity();
540
541 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
542
543 glDisable(GL_ALPHA_TEST);
544 glDisable(GL_AUTO_NORMAL);
545 // Skip clip planes, there are thousands of them.
546 glDisable(GL_COLOR_LOGIC_OP);
547 glDisable(GL_COLOR_TABLE);
548 glDisable(GL_CONVOLUTION_1D);
549 glDisable(GL_CONVOLUTION_2D);
550 glDisable(GL_CULL_FACE);
551 glDisable(GL_DEPTH_TEST);
552 glDisable(GL_DITHER);
553 glDisable(GL_FOG);
554 glDisable(GL_HISTOGRAM);
555 glDisable(GL_INDEX_LOGIC_OP);
556 glDisable(GL_LIGHTING);
557 glDisable(GL_NORMALIZE);
558 // Skip line smmooth
559 // Skip map
560 glDisable(GL_MINMAX);
561 // Skip polygon offset
562 glDisable(GL_SEPARABLE_2D);
563 glDisable(GL_SCISSOR_TEST);
564 glDisable(GL_STENCIL_TEST);
565
566 GLboolean b = 0;
567 glGetBooleanv(GL_TEXTURE_GEN_Q, &b);
568 if (b)
569 glDisable(GL_TEXTURE_GEN_Q);
570 glGetBooleanv(GL_TEXTURE_GEN_R, &b);
571 if (b)
572 glDisable(GL_TEXTURE_GEN_R);
573 glGetBooleanv(GL_TEXTURE_GEN_S, &b);
574 if (b)
575 glDisable(GL_TEXTURE_GEN_S);
576 glGetBooleanv(GL_TEXTURE_GEN_T, &b);
577 if (b)
578 glDisable(GL_TEXTURE_GEN_T);
579
580 glRenderMode(GL_RENDER);
581
582 glDisableClientState(GL_VERTEX_ARRAY);
583 glDisableClientState(GL_NORMAL_ARRAY);
584 glDisableClientState(GL_COLOR_ARRAY);
585 glDisableClientState(GL_INDEX_ARRAY);
586 glDisableClientState(GL_TEXTURE_COORD_ARRAY);
587 glDisableClientState(GL_EDGE_FLAG_ARRAY);
588
589 glPixelStorei(GL_UNPACK_SWAP_BYTES, 0);
590 glPixelStorei(GL_UNPACK_LSB_FIRST, 0);
591 glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
592 glPixelStorei(GL_UNPACK_SKIP_ROWS, 0);
593 glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0);
594 glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
595
596 GLint texunits = 1;
597
598 glGetIntegerv(GL_MAX_TEXTURE_UNITS, &texunits);
599
600 for (i=texunits-1;i>=0;--i) {
601 glActiveTexture(GL_TEXTURE0 + (GLenum)i);
602 glDisable(GL_TEXTURE_1D);
603 glDisable(GL_TEXTURE_2D);
604 glDisable(GL_TEXTURE_3D);
605 }
606
607 glDisable(GL_TEXTURE_CUBE_MAP);
608 glDisable(GL_VERTEX_PROGRAM_ARB);
609 glDisable(GL_FRAGMENT_PROGRAM_ARB);
610
611 GLint enabled;
612 for (i=0;i<ctx->maxVertexAttribs;++i) {
613 enabled = GL_FALSE;
614 glGetVertexAttribiv((GLuint)i, GL_VERTEX_ATTRIB_ARRAY_ENABLED, &enabled);
615 if (enabled == GL_TRUE) {
616 glDisableVertexAttribArray((GLuint)i);
617 ctx->vertexAttribStates[i] = GL_TRUE;
618 }
619 }
620
621 glUseProgram(ctx->uiProgram);
622
623 glEnable(GL_COLOR_MATERIAL);
624 glEnable(GL_TEXTURE_2D);
625 glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE);
626 glEnable(GL_BLEND);
627 glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
628 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
629
630 glMatrixMode(GL_MODELVIEW);
631
632 GLint uni = glGetUniformLocation(ctx->uiProgram, "tex");
633 glUniform1i(uni, 0);
634
635 glEnableClientState(GL_VERTEX_ARRAY);
636 glEnableClientState(GL_TEXTURE_COORD_ARRAY);
637
638 GLuint bound = 0, vbobound = 0;
639 glGetIntegerv(GL_PIXEL_UNPACK_BUFFER_BINDING, (GLint *)&bound);
640 glGetIntegerv(GL_ARRAY_BUFFER_BINDING, (GLint *)&vbobound);
641
642 if (bound != 0) {
643 glBindBuffer(GL_PIXEL_UNPACK_BUFFER_ARB, 0);
644 }
645 if (vbobound != 0) {
646 glBindBuffer(GL_ARRAY_BUFFER, 0);
647 }
648
649 drawOverlay(ctx, (unsigned int)width, (unsigned int)height);
650
651 if (bound != 0) {
652 glBindBuffer(GL_PIXEL_UNPACK_BUFFER_ARB, bound);
653 }
654 if (vbobound != 0) {
655 glBindBuffer(GL_ARRAY_BUFFER, vbobound);
656 }
657
658 for (i=0;i<ctx->maxVertexAttribs;++i) {
659 if (ctx->vertexAttribStates[i] == GL_TRUE) {
660 glEnableVertexAttribArray((GLuint)i);
661 ctx->vertexAttribStates[i] = GL_FALSE;
662 }
663 }
664
665 glMatrixMode(GL_TEXTURE);
666 glPopMatrix();
667
668 glMatrixMode(GL_MODELVIEW);
669 glPopMatrix();
670
671 glMatrixMode(GL_PROJECTION);
672 glPopMatrix();
673
674 glPopClientAttrib();
675 glPopAttrib();
676 glViewport(viewport[0], viewport[1], viewport[2], viewport[3]);
677 glUseProgram(program);
678
679 // drain opengl error queue
680 while (glGetError() != GL_NO_ERROR);
681 }
682
683 #if defined(TARGET_UNIX)
684 # include "init_unix.c"
685 #elif defined(TARGET_MAC)
686 # include "init_mac.c"
687 #endif
688