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