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