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