mirror of
https://github.com/mumble-voip/mumble.git
synced 2025-03-20 07:13:09 +00:00

Keeping these up-to-date is just super tedious and they don't really fulfill any purpose these days.
672 lines
18 KiB
C
672 lines
18 KiB
C
// Copyright The Mumble Developers. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style license
|
|
// that can be found in the LICENSE file at the root of the
|
|
// Mumble source tree or at <https://www.mumble.info/LICENSE>.
|
|
|
|
#define GLX_GLXEXT_LEGACY
|
|
#define GL_GLEXT_PROTOTYPES
|
|
#define _GNU_SOURCE
|
|
#include <dlfcn.h>
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <limits.h>
|
|
#include <math.h>
|
|
#include <pwd.h>
|
|
#include <semaphore.h>
|
|
#include <stdarg.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <sys/ipc.h>
|
|
#include <sys/mman.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/time.h>
|
|
#include <sys/types.h>
|
|
#include <sys/un.h>
|
|
#include <time.h>
|
|
#include <unistd.h>
|
|
|
|
#if defined(TARGET_UNIX)
|
|
# define GLX_GLXEXT_LEGACY
|
|
# define GL_GLEXT_PROTOTYPES
|
|
# define _GNU_SOURCE
|
|
# include <GL/gl.h>
|
|
# include <GL/glext.h>
|
|
# include <GL/glx.h>
|
|
|
|
# include <link.h>
|
|
|
|
typedef unsigned char bool;
|
|
# define true 1
|
|
# define false 0
|
|
#elif defined(TARGET_MAC)
|
|
# include <AGL/agl.h>
|
|
# include <Carbon/Carbon.h>
|
|
# include <Cocoa/Cocoa.h>
|
|
# include <OpenGL/OpenGL.h>
|
|
#
|
|
# include <objc/objc-runtime.h>
|
|
#
|
|
# include "mach_override.h"
|
|
#
|
|
# include "avail_mac.h"
|
|
#endif
|
|
|
|
#include "../overlay/overlay.h"
|
|
|
|
static bool bDebug = false;
|
|
static bool bCursorAvail = false;
|
|
|
|
typedef struct _Context {
|
|
struct _Context *next;
|
|
|
|
#if defined(TARGET_UNIX)
|
|
Display *dpy;
|
|
GLXDrawable draw;
|
|
#elif defined(TARGET_MAC)
|
|
CGLContextObj cglctx;
|
|
NSOpenGLContext *nsctx;
|
|
#endif
|
|
|
|
unsigned int uiWidth, uiHeight;
|
|
unsigned int uiLeft, uiRight, uiTop, uiBottom;
|
|
|
|
struct sockaddr_un saName;
|
|
int iSocket;
|
|
// overlay message, temporary variable for processing from socket
|
|
struct OverlayMsg omMsg;
|
|
// opengl overlay texture
|
|
GLuint texture;
|
|
|
|
// overlay texture in shared memory
|
|
unsigned char *a_ucTexture;
|
|
unsigned int uiMappedLength;
|
|
|
|
bool bValid;
|
|
bool bMesa;
|
|
|
|
GLuint uiProgram;
|
|
|
|
clock_t timeT;
|
|
unsigned int frameCount;
|
|
|
|
GLint maxVertexAttribs;
|
|
GLboolean *vertexAttribStates;
|
|
} Context;
|
|
|
|
static const char vshader[] = ""
|
|
"void main() {"
|
|
"gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;"
|
|
"gl_TexCoord[0] = gl_MultiTexCoord0;"
|
|
"}";
|
|
|
|
static const char fshader[] = ""
|
|
"uniform sampler2D tex;"
|
|
"void main() {"
|
|
"gl_FragColor = texture2D(tex, gl_TexCoord[0].st);"
|
|
"}";
|
|
|
|
const GLfloat fBorder[] = { 0.125f, 0.250f, 0.5f, 0.75f };
|
|
|
|
static Context *contexts = NULL;
|
|
|
|
#define AVAIL(name) dlsym(RTLD_DEFAULT, #name)
|
|
#define FDEF(name) static __typeof__(&name) o##name = NULL
|
|
|
|
#if defined(TARGET_UNIX)
|
|
FDEF(dlsym);
|
|
FDEF(glXSwapBuffers);
|
|
FDEF(glXGetProcAddressARB);
|
|
FDEF(glXGetProcAddress);
|
|
#elif defined(TARGET_MAC)
|
|
FDEF(CGLFlushDrawable);
|
|
FDEF(CGDisplayHideCursor);
|
|
FDEF(CGDisplayShowCursor);
|
|
#endif
|
|
|
|
__attribute__((format(printf, 1, 2))) static void ods(const char *format, ...) {
|
|
if (!bDebug) {
|
|
return;
|
|
}
|
|
|
|
fprintf(stderr, "MumbleOverlay: ");
|
|
|
|
va_list args;
|
|
va_start(args, format);
|
|
vfprintf(stderr, format, args);
|
|
va_end(args);
|
|
fprintf(stderr, "\n");
|
|
fflush(stderr);
|
|
}
|
|
|
|
static void newContext(Context *ctx) {
|
|
ctx->iSocket = -1;
|
|
ctx->omMsg.omh.iLength = -1;
|
|
ctx->texture = ~0U;
|
|
ctx->timeT = clock();
|
|
ctx->frameCount = 0;
|
|
|
|
char *home = getenv("HOME");
|
|
if (home == NULL) {
|
|
struct passwd *pwent = getpwuid(getuid());
|
|
if (pwent && pwent->pw_dir && pwent->pw_dir[0]) {
|
|
home = pwent->pw_dir;
|
|
}
|
|
}
|
|
|
|
char *xdgRuntimeDir = getenv("XDG_RUNTIME_DIR");
|
|
|
|
if (xdgRuntimeDir != NULL) {
|
|
ctx->saName.sun_family = PF_UNIX;
|
|
strcpy(ctx->saName.sun_path, xdgRuntimeDir);
|
|
strcat(ctx->saName.sun_path, "/MumbleOverlayPipe");
|
|
} else if (home) {
|
|
ctx->saName.sun_family = PF_UNIX;
|
|
strcpy(ctx->saName.sun_path, home);
|
|
strcat(ctx->saName.sun_path, "/.MumbleOverlayPipe");
|
|
}
|
|
|
|
ods("OpenGL Version %s, Vendor %s, Renderer %s, Shader %s", glGetString(GL_VERSION), glGetString(GL_VENDOR),
|
|
glGetString(GL_RENDERER), glGetString(GL_SHADING_LANGUAGE_VERSION));
|
|
|
|
const char *vsource = vshader;
|
|
const char *fsource = fshader;
|
|
char buffer[8192];
|
|
GLint l;
|
|
GLuint vs = glCreateShader(GL_VERTEX_SHADER);
|
|
GLuint fs = glCreateShader(GL_FRAGMENT_SHADER);
|
|
glShaderSource(vs, 1, &vsource, NULL);
|
|
glShaderSource(fs, 1, &fsource, NULL);
|
|
glCompileShader(vs);
|
|
glCompileShader(fs);
|
|
glGetShaderInfoLog(vs, 8192, &l, buffer);
|
|
ods("VERTEX: %s", buffer);
|
|
glGetShaderInfoLog(fs, 8192, &l, buffer);
|
|
ods("FRAGMENT: %s", buffer);
|
|
ctx->uiProgram = glCreateProgram();
|
|
glAttachShader(ctx->uiProgram, vs);
|
|
glAttachShader(ctx->uiProgram, fs);
|
|
glLinkProgram(ctx->uiProgram);
|
|
|
|
glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &ctx->maxVertexAttribs);
|
|
ctx->vertexAttribStates = calloc((size_t) ctx->maxVertexAttribs, sizeof(GLboolean));
|
|
}
|
|
|
|
static void releaseMem(Context *ctx) {
|
|
if (ctx->a_ucTexture) {
|
|
munmap(ctx->a_ucTexture, ctx->uiMappedLength);
|
|
ctx->a_ucTexture = NULL;
|
|
ctx->uiMappedLength = 0;
|
|
}
|
|
if (ctx->texture != ~0U) {
|
|
glDeleteTextures(1, &ctx->texture);
|
|
ctx->texture = ~0U;
|
|
}
|
|
ctx->uiLeft = ctx->uiTop = ctx->uiRight = ctx->uiBottom = 0;
|
|
}
|
|
|
|
static void disconnect(Context *ctx) {
|
|
releaseMem(ctx);
|
|
ctx->uiWidth = ctx->uiHeight = 0;
|
|
if (ctx->iSocket != -1) {
|
|
close(ctx->iSocket);
|
|
ctx->iSocket = -1;
|
|
}
|
|
ods("Disconnected");
|
|
}
|
|
|
|
static bool sendMessage(Context *ctx, struct OverlayMsg *om) {
|
|
if (ctx->iSocket != -1) {
|
|
size_t wantsend = sizeof(struct OverlayMsgHeader) + (size_t) om->omh.iLength;
|
|
ssize_t sent = send(ctx->iSocket, om, wantsend, MSG_DONTWAIT);
|
|
if (sent != -1 && wantsend == (size_t) sent) {
|
|
return true;
|
|
}
|
|
ods("Short write. Disconnecting pipe.");
|
|
}
|
|
disconnect(ctx);
|
|
return false;
|
|
}
|
|
|
|
static void regenTexture(Context *ctx) {
|
|
if (ctx->texture != ~0U) {
|
|
glDeleteTextures(1, &ctx->texture);
|
|
}
|
|
glGenTextures(1, &ctx->texture);
|
|
|
|
glBindTexture(GL_TEXTURE_2D, ctx->texture);
|
|
glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, fBorder);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (GLsizei) ctx->uiWidth, (GLsizei) ctx->uiHeight, 0, GL_BGRA,
|
|
GL_UNSIGNED_BYTE, ctx->a_ucTexture);
|
|
}
|
|
|
|
static void drawOverlay(Context *ctx, unsigned int width, unsigned int height) {
|
|
// if no socket is active, initialize and connect to socket
|
|
if (ctx->iSocket == -1) {
|
|
releaseMem(ctx);
|
|
if (!ctx->saName.sun_path[0])
|
|
return;
|
|
ctx->iSocket = socket(AF_UNIX, SOCK_STREAM, 0);
|
|
if (ctx->iSocket == -1) {
|
|
ods("socket() failure");
|
|
return;
|
|
}
|
|
fcntl(ctx->iSocket, F_SETFL, O_NONBLOCK, 1);
|
|
if (connect(ctx->iSocket, (struct sockaddr *) (&ctx->saName), sizeof(ctx->saName)) != 0) {
|
|
close(ctx->iSocket);
|
|
ctx->iSocket = -1;
|
|
ods("connect() failure %s", ctx->saName.sun_path);
|
|
return;
|
|
}
|
|
ods("Socket connected");
|
|
|
|
struct OverlayMsg om;
|
|
om.omh.uiMagic = OVERLAY_MAGIC_NUMBER;
|
|
om.omh.uiType = OVERLAY_MSGTYPE_PID;
|
|
om.omh.iLength = sizeof(struct OverlayMsgPid);
|
|
om.omp.pid = (unsigned int) getpid(); // getpid can't fail
|
|
|
|
if (!sendMessage(ctx, &om))
|
|
return;
|
|
|
|
ods("SentPid");
|
|
}
|
|
|
|
// if overlay size (width or height) is not up-to-date create and send an overlay initialization message
|
|
if ((ctx->uiWidth != width) || (ctx->uiHeight != height)) {
|
|
ods("Sending init overlay msg with w h %i %i", width, height);
|
|
releaseMem(ctx);
|
|
|
|
ctx->uiWidth = width;
|
|
ctx->uiHeight = height;
|
|
|
|
struct OverlayMsg om;
|
|
om.omh.uiMagic = OVERLAY_MAGIC_NUMBER;
|
|
om.omh.uiType = OVERLAY_MSGTYPE_INIT;
|
|
om.omh.iLength = sizeof(struct OverlayMsgInit);
|
|
om.omi.uiWidth = ctx->uiWidth;
|
|
om.omi.uiHeight = ctx->uiHeight;
|
|
|
|
if (!sendMessage(ctx, &om))
|
|
return;
|
|
}
|
|
|
|
// receive and process overlay messages
|
|
while (1) {
|
|
if (ctx->omMsg.omh.iLength < 0) {
|
|
// receive the overlay message header
|
|
ssize_t length = recv(ctx->iSocket, ctx->omMsg.headerbuffer, sizeof(struct OverlayMsgHeader), 0);
|
|
if (length < 0) {
|
|
if ((errno == EAGAIN) || (errno == EWOULDBLOCK))
|
|
break;
|
|
disconnect(ctx);
|
|
return;
|
|
} else if (length != sizeof(struct OverlayMsgHeader)) {
|
|
ods("Short header read on overlay message");
|
|
disconnect(ctx);
|
|
return;
|
|
}
|
|
} else {
|
|
// receive the overlay message body
|
|
ssize_t length = recv(ctx->iSocket, ctx->omMsg.msgbuffer, (size_t) ctx->omMsg.omh.iLength, 0);
|
|
if (length < 0) {
|
|
if ((errno == EAGAIN) || (errno == EWOULDBLOCK))
|
|
break;
|
|
disconnect(ctx);
|
|
return;
|
|
} else if (length != ctx->omMsg.omh.iLength) {
|
|
ods("Short overlay message read %x %zd/%d", ctx->omMsg.omh.uiType, length, ctx->omMsg.omh.iLength);
|
|
disconnect(ctx);
|
|
return;
|
|
}
|
|
// set len to -1 again for a clean state on next receive
|
|
ctx->omMsg.omh.iLength = -1;
|
|
|
|
switch (ctx->omMsg.omh.uiType) {
|
|
// shared memory overlay message:
|
|
case OVERLAY_MSGTYPE_SHMEM: {
|
|
struct OverlayMsgShmem *oms = (struct OverlayMsgShmem *) &ctx->omMsg.omi;
|
|
ods("SHMEM %s", oms->a_cName);
|
|
releaseMem(ctx);
|
|
int fd = shm_open(oms->a_cName, O_RDONLY, 0600);
|
|
if (fd != -1) {
|
|
struct stat buf;
|
|
|
|
if (fstat(fd, &buf) != -1) {
|
|
unsigned int buflen = buf.st_size;
|
|
if (buflen >= ctx->uiWidth * ctx->uiHeight * 4 && buflen < 512 * 1024 * 1024) {
|
|
ctx->uiMappedLength = buflen;
|
|
ctx->a_ucTexture = mmap(NULL, (size_t) buflen, PROT_READ, MAP_SHARED, fd, 0);
|
|
if (ctx->a_ucTexture != MAP_FAILED) {
|
|
// mmap successfull; send a new bodyless sharedmemory overlay message and regenerate
|
|
// the overlay texture
|
|
struct OverlayMsg om;
|
|
om.omh.uiMagic = OVERLAY_MAGIC_NUMBER;
|
|
om.omh.uiType = OVERLAY_MSGTYPE_SHMEM;
|
|
om.omh.iLength = 0;
|
|
|
|
if (!sendMessage(ctx, &om))
|
|
return;
|
|
|
|
regenTexture(ctx);
|
|
continue;
|
|
}
|
|
ctx->a_ucTexture = NULL;
|
|
}
|
|
ctx->uiMappedLength = 0;
|
|
} else {
|
|
ods("Failed to fstat memory map");
|
|
}
|
|
close(fd);
|
|
}
|
|
ods("Failed to map memory");
|
|
} break;
|
|
// blit overlay message: blit overlay texture from shared memory to gl-texture var
|
|
case OVERLAY_MSGTYPE_BLIT: {
|
|
struct OverlayMsgBlit *omb = &ctx->omMsg.omb;
|
|
ods("BLIT %d %d %d %d", omb->x, omb->y, omb->w, omb->h);
|
|
if ((ctx->a_ucTexture != NULL) && (ctx->texture != ~0U)) {
|
|
glBindTexture(GL_TEXTURE_2D, ctx->texture);
|
|
|
|
if ((omb->x == 0) && (omb->y == 0) && (omb->w == ctx->uiWidth) && (omb->h == ctx->uiHeight)) {
|
|
ods("Optimzied fullscreen blit");
|
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (GLsizei) ctx->uiWidth, (GLsizei) ctx->uiHeight, 0,
|
|
GL_BGRA, GL_UNSIGNED_BYTE, ctx->a_ucTexture);
|
|
} else {
|
|
// allocate temporary memory
|
|
unsigned int x = omb->x;
|
|
unsigned int y = omb->y;
|
|
unsigned int w = omb->w;
|
|
unsigned int h = omb->h;
|
|
unsigned char *ptr = (unsigned char *) malloc(w * h * 4);
|
|
unsigned int row;
|
|
memset(ptr, 0, w * h * 4);
|
|
|
|
// copy overlay texture to temporary memory to adapt to full opengl ui size (overlay at
|
|
// correct place)
|
|
for (row = 0; row < h; ++row) {
|
|
const unsigned char *sptr = ctx->a_ucTexture + 4 * ((y + row) * ctx->uiWidth + x);
|
|
unsigned char *dptr = ptr + 4 * w * row;
|
|
memcpy(dptr, sptr, w * 4);
|
|
}
|
|
|
|
// copy temporary texture to opengl
|
|
glTexSubImage2D(GL_TEXTURE_2D, 0, (GLint) x, (GLint) y, (GLint) w, (GLint) h, GL_BGRA,
|
|
GL_UNSIGNED_BYTE, ptr);
|
|
free(ptr);
|
|
}
|
|
}
|
|
} break;
|
|
case OVERLAY_MSGTYPE_ACTIVE: {
|
|
struct OverlayMsgActive *oma = &ctx->omMsg.oma;
|
|
ods("ACTIVE %d %d %d %d", oma->x, oma->y, oma->w, oma->h);
|
|
ctx->uiLeft = oma->x;
|
|
ctx->uiTop = oma->y;
|
|
ctx->uiRight = oma->x + oma->w;
|
|
ctx->uiBottom = oma->y + oma->h;
|
|
} break;
|
|
case OVERLAY_MSGTYPE_INTERACTIVE: {
|
|
#if defined(TARGET_MAC)
|
|
struct OverlayMsgInteractive *omin = &ctx->omMsg.omin;
|
|
ods("Interactive %d", omin->state);
|
|
if (bCursorAvail) {
|
|
if (omin->state) {
|
|
oCGDisplayHideCursor(kCGNullDirectDisplay);
|
|
} else {
|
|
oCGDisplayShowCursor(kCGNullDirectDisplay);
|
|
}
|
|
}
|
|
#endif
|
|
} break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ((ctx->a_ucTexture == NULL) || (ctx->texture == ~0U))
|
|
return;
|
|
|
|
// texture checks, that our gltexture is still valid and sane
|
|
if (!glIsTexture(ctx->texture)) {
|
|
ctx->texture = ~0U;
|
|
ods("Lost texture");
|
|
regenTexture(ctx);
|
|
} else {
|
|
glBindTexture(GL_TEXTURE_2D, ctx->texture);
|
|
GLfloat bordercolor[4];
|
|
glGetTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, bordercolor);
|
|
if (bordercolor[0] != fBorder[0] || bordercolor[1] != fBorder[1] || bordercolor[2] != fBorder[2]
|
|
|| bordercolor[3] != fBorder[3]) {
|
|
ods("Texture was hijacked! Texture will be regenerated.");
|
|
regenTexture(ctx);
|
|
}
|
|
}
|
|
|
|
glBindTexture(GL_TEXTURE_2D, ctx->texture);
|
|
glPushMatrix();
|
|
|
|
float w = (float) (ctx->uiWidth);
|
|
float h = (float) (ctx->uiHeight);
|
|
|
|
float left = (float) (ctx->uiLeft);
|
|
float top = (float) (ctx->uiTop);
|
|
float right = (float) (ctx->uiRight);
|
|
float bottom = (float) (ctx->uiBottom);
|
|
|
|
float xm = left / w;
|
|
float ym = top / h;
|
|
float xmx = right / w;
|
|
float ymx = bottom / h;
|
|
|
|
GLfloat vertex[] = { left, bottom, left, top, right, top,
|
|
|
|
left, bottom, right, top, right, bottom };
|
|
glVertexPointer(2, GL_FLOAT, 0, vertex);
|
|
|
|
GLfloat tex[] = { xm, ymx, xm, ym, xmx, ym,
|
|
|
|
xm, ymx, xmx, ym, xmx, ymx };
|
|
glTexCoordPointer(2, GL_FLOAT, 0, tex);
|
|
|
|
glDrawArrays(GL_TRIANGLES, 0, 6);
|
|
|
|
glPopMatrix();
|
|
}
|
|
|
|
static void drawContext(Context *ctx, int width, int height) {
|
|
// calculate FPS and send it as an overlay message
|
|
clock_t t = clock();
|
|
float elapsed = (float) (t - ctx->timeT) / CLOCKS_PER_SEC;
|
|
++(ctx->frameCount);
|
|
if (elapsed > OVERLAY_FPS_INTERVAL) {
|
|
struct OverlayMsg om;
|
|
om.omh.uiMagic = OVERLAY_MAGIC_NUMBER;
|
|
om.omh.uiType = OVERLAY_MSGTYPE_FPS;
|
|
om.omh.iLength = sizeof(struct OverlayMsgFps);
|
|
om.omf.fps = (float) ctx->frameCount / elapsed;
|
|
|
|
sendMessage(ctx, &om);
|
|
|
|
ctx->frameCount = 0;
|
|
ctx->timeT = t;
|
|
}
|
|
|
|
GLuint program;
|
|
GLint viewport[4];
|
|
int i;
|
|
|
|
glPushAttrib(GL_ALL_ATTRIB_BITS);
|
|
glPushClientAttrib(GL_ALL_ATTRIB_BITS);
|
|
glGetIntegerv(GL_VIEWPORT, viewport);
|
|
glGetIntegerv(GL_CURRENT_PROGRAM, (GLint *) &program);
|
|
|
|
glViewport(0, 0, width, height);
|
|
|
|
glMatrixMode(GL_PROJECTION);
|
|
glPushMatrix();
|
|
glLoadIdentity();
|
|
glOrtho(0, width, height, 0, -100.0, 100.0);
|
|
|
|
glMatrixMode(GL_MODELVIEW);
|
|
glPushMatrix();
|
|
glLoadIdentity();
|
|
|
|
glMatrixMode(GL_TEXTURE);
|
|
glPushMatrix();
|
|
glLoadIdentity();
|
|
|
|
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
|
|
|
|
glDisable(GL_ALPHA_TEST);
|
|
glDisable(GL_AUTO_NORMAL);
|
|
// Skip clip planes, there are thousands of them.
|
|
glDisable(GL_COLOR_LOGIC_OP);
|
|
glDisable(GL_COLOR_TABLE);
|
|
glDisable(GL_CONVOLUTION_1D);
|
|
glDisable(GL_CONVOLUTION_2D);
|
|
glDisable(GL_CULL_FACE);
|
|
glDisable(GL_DEPTH_TEST);
|
|
glDisable(GL_DITHER);
|
|
glDisable(GL_FOG);
|
|
glDisable(GL_HISTOGRAM);
|
|
glDisable(GL_INDEX_LOGIC_OP);
|
|
glDisable(GL_LIGHTING);
|
|
glDisable(GL_NORMALIZE);
|
|
// Skip line smmooth
|
|
// Skip map
|
|
glDisable(GL_MINMAX);
|
|
// Skip polygon offset
|
|
glDisable(GL_SEPARABLE_2D);
|
|
glDisable(GL_SCISSOR_TEST);
|
|
glDisable(GL_STENCIL_TEST);
|
|
|
|
GLboolean b = 0;
|
|
glGetBooleanv(GL_TEXTURE_GEN_Q, &b);
|
|
if (b)
|
|
glDisable(GL_TEXTURE_GEN_Q);
|
|
glGetBooleanv(GL_TEXTURE_GEN_R, &b);
|
|
if (b)
|
|
glDisable(GL_TEXTURE_GEN_R);
|
|
glGetBooleanv(GL_TEXTURE_GEN_S, &b);
|
|
if (b)
|
|
glDisable(GL_TEXTURE_GEN_S);
|
|
glGetBooleanv(GL_TEXTURE_GEN_T, &b);
|
|
if (b)
|
|
glDisable(GL_TEXTURE_GEN_T);
|
|
|
|
glRenderMode(GL_RENDER);
|
|
|
|
glDisableClientState(GL_VERTEX_ARRAY);
|
|
glDisableClientState(GL_NORMAL_ARRAY);
|
|
glDisableClientState(GL_COLOR_ARRAY);
|
|
glDisableClientState(GL_INDEX_ARRAY);
|
|
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
|
|
glDisableClientState(GL_EDGE_FLAG_ARRAY);
|
|
|
|
glPixelStorei(GL_UNPACK_SWAP_BYTES, 0);
|
|
glPixelStorei(GL_UNPACK_LSB_FIRST, 0);
|
|
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
|
|
glPixelStorei(GL_UNPACK_SKIP_ROWS, 0);
|
|
glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0);
|
|
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
|
|
|
|
GLint texunits = 1;
|
|
|
|
glGetIntegerv(GL_MAX_TEXTURE_UNITS, &texunits);
|
|
|
|
for (i = texunits - 1; i >= 0; --i) {
|
|
glActiveTexture(GL_TEXTURE0 + (GLenum) i);
|
|
glDisable(GL_TEXTURE_1D);
|
|
glDisable(GL_TEXTURE_2D);
|
|
glDisable(GL_TEXTURE_3D);
|
|
}
|
|
|
|
glDisable(GL_TEXTURE_CUBE_MAP);
|
|
glDisable(GL_VERTEX_PROGRAM_ARB);
|
|
glDisable(GL_FRAGMENT_PROGRAM_ARB);
|
|
|
|
GLint enabled;
|
|
for (i = 0; i < ctx->maxVertexAttribs; ++i) {
|
|
enabled = GL_FALSE;
|
|
glGetVertexAttribiv((GLuint) i, GL_VERTEX_ATTRIB_ARRAY_ENABLED, &enabled);
|
|
if (enabled == GL_TRUE) {
|
|
glDisableVertexAttribArray((GLuint) i);
|
|
ctx->vertexAttribStates[i] = GL_TRUE;
|
|
}
|
|
}
|
|
|
|
glUseProgram(ctx->uiProgram);
|
|
|
|
glEnable(GL_COLOR_MATERIAL);
|
|
glEnable(GL_TEXTURE_2D);
|
|
glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE);
|
|
glEnable(GL_BLEND);
|
|
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
|
|
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
|
|
|
|
glMatrixMode(GL_MODELVIEW);
|
|
|
|
GLint uni = glGetUniformLocation(ctx->uiProgram, "tex");
|
|
glUniform1i(uni, 0);
|
|
|
|
glEnableClientState(GL_VERTEX_ARRAY);
|
|
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
|
|
|
|
GLuint bound = 0, vbobound = 0;
|
|
glGetIntegerv(GL_PIXEL_UNPACK_BUFFER_BINDING, (GLint *) &bound);
|
|
glGetIntegerv(GL_ARRAY_BUFFER_BINDING, (GLint *) &vbobound);
|
|
|
|
if (bound != 0) {
|
|
glBindBuffer(GL_PIXEL_UNPACK_BUFFER_ARB, 0);
|
|
}
|
|
if (vbobound != 0) {
|
|
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
|
}
|
|
|
|
drawOverlay(ctx, (unsigned int) width, (unsigned int) height);
|
|
|
|
if (bound != 0) {
|
|
glBindBuffer(GL_PIXEL_UNPACK_BUFFER_ARB, bound);
|
|
}
|
|
if (vbobound != 0) {
|
|
glBindBuffer(GL_ARRAY_BUFFER, vbobound);
|
|
}
|
|
|
|
for (i = 0; i < ctx->maxVertexAttribs; ++i) {
|
|
if (ctx->vertexAttribStates[i] == GL_TRUE) {
|
|
glEnableVertexAttribArray((GLuint) i);
|
|
ctx->vertexAttribStates[i] = GL_FALSE;
|
|
}
|
|
}
|
|
|
|
glMatrixMode(GL_TEXTURE);
|
|
glPopMatrix();
|
|
|
|
glMatrixMode(GL_MODELVIEW);
|
|
glPopMatrix();
|
|
|
|
glMatrixMode(GL_PROJECTION);
|
|
glPopMatrix();
|
|
|
|
glPopClientAttrib();
|
|
glPopAttrib();
|
|
glViewport(viewport[0], viewport[1], viewport[2], viewport[3]);
|
|
glUseProgram(program);
|
|
|
|
// drain opengl error queue
|
|
while (glGetError() != GL_NO_ERROR)
|
|
;
|
|
}
|
|
|
|
#if defined(TARGET_UNIX)
|
|
# include "init_unix.c"
|
|
#elif defined(TARGET_MAC)
|
|
# include "init_mac.c"
|
|
#endif
|