388 lines
11 KiB
C++
388 lines
11 KiB
C++
// Copyright 2007-2023 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>.
|
|
|
|
// Needs to be included before GL/gl.h
|
|
#include "lib.h"
|
|
// Needs to be included before glext.h
|
|
#include <GL/gl.h>
|
|
|
|
#include <ctime>
|
|
|
|
#include "../3rdparty/GL/glext.h"
|
|
|
|
#define TDEF(ret, name, arg) typedef ret(__stdcall *t##name) arg
|
|
#define GLDEF(ret, name, arg) \
|
|
TDEF(ret, name, arg); \
|
|
t##name o##name = nullptr
|
|
|
|
GLDEF(HGLRC, wglCreateContext, (HDC));
|
|
GLDEF(void, glGenTextures, (GLsizei, GLuint *) );
|
|
GLDEF(void, glDeleteTextures, (GLsizei, GLuint *) );
|
|
GLDEF(void, glEnable, (GLenum));
|
|
GLDEF(void, glDisable, (GLenum));
|
|
GLDEF(void, glBlendFunc, (GLenum, GLenum));
|
|
GLDEF(void, glViewport, (GLint, GLint, GLsizei, GLsizei));
|
|
GLDEF(void, glMatrixMode, (GLenum));
|
|
GLDEF(void, glLoadIdentity, (void) );
|
|
GLDEF(void, glOrtho, (GLdouble, GLdouble, GLdouble, GLdouble, GLdouble, GLdouble));
|
|
GLDEF(void, glBindTexture, (GLenum, GLuint));
|
|
GLDEF(void, glPushMatrix, (void) );
|
|
GLDEF(void, glBegin, (GLenum));
|
|
GLDEF(void, glEnd, (void) );
|
|
GLDEF(void, glTexCoord2f, (GLfloat, GLfloat));
|
|
GLDEF(void, glVertex2f, (GLfloat, GLfloat));
|
|
GLDEF(void, glPopMatrix, (void) );
|
|
GLDEF(void, glTexParameteri, (GLenum, GLenum, GLint));
|
|
GLDEF(void, glTexEnvi, (GLenum, GLenum, GLint));
|
|
GLDEF(void, glTexImage2D, (GLenum, GLint, GLint, GLsizei, GLsizei, GLint, GLenum, GLenum, const GLvoid *) );
|
|
GLDEF(void, glTexSubImage2D, (GLenum, GLint, GLint, GLint, GLsizei, GLsizei, GLenum, GLenum, const GLvoid *) );
|
|
GLDEF(void, glPixelStorei, (GLenum, GLint));
|
|
GLDEF(void, wglMakeCurrent, (HDC, HGLRC));
|
|
GLDEF(HGLRC, wglGetCurrentContext, (void) );
|
|
GLDEF(HDC, wglGetCurrentDC, (void) );
|
|
GLDEF(int, GetDeviceCaps, (HDC, int) );
|
|
|
|
#define INJDEF(ret, name, arg) \
|
|
GLDEF(ret, name, arg); \
|
|
static HardHook hh##name
|
|
|
|
INJDEF(BOOL, wglSwapBuffers, (HDC));
|
|
|
|
static bool bHooked = false;
|
|
|
|
class Context : protected Pipe {
|
|
public:
|
|
Context(HDC hdc);
|
|
void draw(HDC hdc);
|
|
|
|
protected:
|
|
virtual void blit(unsigned int x, unsigned int y, unsigned int w, unsigned int h);
|
|
virtual void setRect();
|
|
virtual void newTexture(unsigned int width, unsigned int height);
|
|
|
|
private:
|
|
HGLRC ctx;
|
|
GLuint texture;
|
|
|
|
clock_t timeT;
|
|
unsigned int frameCount;
|
|
|
|
void initContext();
|
|
void doDraw(HDC hdc);
|
|
};
|
|
|
|
Context::Context(HDC hdc) {
|
|
timeT = clock();
|
|
frameCount = 0;
|
|
|
|
texture = ~0;
|
|
ctx = owglCreateContext(hdc);
|
|
|
|
HGLRC oldctx = owglGetCurrentContext();
|
|
HDC oldhdc = owglGetCurrentDC();
|
|
owglMakeCurrent(hdc, ctx);
|
|
|
|
initContext();
|
|
|
|
owglMakeCurrent(oldhdc, oldctx);
|
|
}
|
|
|
|
void Context::initContext() {
|
|
// Here we go. From the top. Where is glResetState?
|
|
oglDisable(GL_ALPHA_TEST);
|
|
oglDisable(GL_AUTO_NORMAL);
|
|
oglEnable(GL_BLEND);
|
|
// Skip clip planes, there are thousands of them.
|
|
oglDisable(GL_COLOR_LOGIC_OP);
|
|
oglDisable(GL_COLOR_MATERIAL);
|
|
oglDisable(GL_COLOR_TABLE);
|
|
oglDisable(GL_CONVOLUTION_1D);
|
|
oglDisable(GL_CONVOLUTION_2D);
|
|
oglDisable(GL_CULL_FACE);
|
|
oglDisable(GL_DEPTH_TEST);
|
|
oglDisable(GL_DITHER);
|
|
oglDisable(GL_FOG);
|
|
oglDisable(GL_HISTOGRAM);
|
|
oglDisable(GL_INDEX_LOGIC_OP);
|
|
oglDisable(GL_LIGHTING);
|
|
// Skip line smmooth
|
|
// Skip map
|
|
oglDisable(GL_MINMAX);
|
|
// Skip polygon offset
|
|
oglDisable(GL_SEPARABLE_2D);
|
|
oglDisable(GL_SCISSOR_TEST);
|
|
oglDisable(GL_STENCIL_TEST);
|
|
oglEnable(GL_TEXTURE_2D);
|
|
oglDisable(GL_TEXTURE_GEN_Q);
|
|
oglDisable(GL_TEXTURE_GEN_R);
|
|
oglDisable(GL_TEXTURE_GEN_S);
|
|
oglDisable(GL_TEXTURE_GEN_T);
|
|
|
|
oglBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
|
|
}
|
|
|
|
void Context::blit(unsigned int x, unsigned int y, unsigned int w, unsigned int h) {
|
|
ods("OpenGL: Blit %d %d %d %d -- %d %d : %d", x, y, w, h, uiWidth, uiHeight, texture);
|
|
|
|
if (texture == ~0)
|
|
return;
|
|
|
|
oglBindTexture(GL_TEXTURE_2D, texture);
|
|
|
|
if ((x == 0) && (y == 0) && (w == uiWidth) && (h == uiHeight)) {
|
|
oglTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, uiWidth, uiHeight, 0, GL_BGRA, GL_UNSIGNED_BYTE, a_ucTexture);
|
|
} else {
|
|
if (w != uiWidth)
|
|
oglPixelStorei(GL_UNPACK_ROW_LENGTH, uiWidth);
|
|
oglTexSubImage2D(GL_TEXTURE_2D, 0, x, y, w, h, GL_BGRA, GL_UNSIGNED_BYTE, a_ucTexture + 4 * (y * uiWidth + x));
|
|
if (w != uiWidth)
|
|
oglPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
|
|
}
|
|
}
|
|
|
|
void Context::setRect() {
|
|
ods("OpenGL: setRect");
|
|
}
|
|
|
|
void Context::newTexture(unsigned int width, unsigned int height) {
|
|
ods("OpenGL: newTex");
|
|
|
|
if (texture == ~0) {
|
|
oglBindTexture(GL_TEXTURE_2D, 0);
|
|
oglDeleteTextures(1, &texture);
|
|
texture = ~0;
|
|
}
|
|
oglGenTextures(1, &texture);
|
|
|
|
oglBindTexture(GL_TEXTURE_2D, texture);
|
|
oglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
|
oglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
|
oglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
|
oglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
|
|
|
unsigned char *ptr = new unsigned char[width * height * 4];
|
|
memset(ptr, 0, width * height * 4);
|
|
oglTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_BGRA, GL_UNSIGNED_BYTE, ptr);
|
|
delete[] ptr;
|
|
}
|
|
|
|
void Context::draw(HDC hdc) {
|
|
HGLRC oldctx = owglGetCurrentContext();
|
|
HDC oldhdc = owglGetCurrentDC();
|
|
owglMakeCurrent(hdc, ctx);
|
|
|
|
doDraw(hdc);
|
|
|
|
owglMakeCurrent(oldhdc, oldctx);
|
|
}
|
|
|
|
void Context::doDraw(HDC hdc) {
|
|
// DEBUG
|
|
// sm->bDebug = true;
|
|
|
|
clock_t t = clock();
|
|
float elapsed = static_cast< float >(t - timeT) / CLOCKS_PER_SEC;
|
|
++frameCount;
|
|
if (elapsed > OVERLAY_FPS_INTERVAL) {
|
|
OverlayMsg om;
|
|
om.omh.uiMagic = OVERLAY_MAGIC_NUMBER;
|
|
om.omh.uiType = OVERLAY_MSGTYPE_FPS;
|
|
om.omh.iLength = sizeof(OverlayMsgFps);
|
|
om.omf.fps = frameCount / elapsed;
|
|
|
|
sendMessage(om);
|
|
|
|
frameCount = 0;
|
|
timeT = t;
|
|
}
|
|
|
|
unsigned int width, height;
|
|
|
|
width = oGetDeviceCaps(hdc, HORZRES);
|
|
height = oGetDeviceCaps(hdc, VERTRES);
|
|
|
|
HWND hwnd = WindowFromDC(hdc);
|
|
if (hwnd) {
|
|
RECT r;
|
|
if (GetClientRect(hwnd, &r)) {
|
|
width = r.right - r.left;
|
|
height = r.bottom - r.top;
|
|
}
|
|
}
|
|
|
|
ods("OpenGL: DrawStart: Screen is %d x %d", width, height);
|
|
|
|
checkMessage(width, height);
|
|
|
|
oglViewport(0, 0, width, height);
|
|
|
|
oglMatrixMode(GL_PROJECTION);
|
|
oglLoadIdentity();
|
|
oglOrtho(0, width, height, 0, -100.0, 100.0);
|
|
|
|
oglMatrixMode(GL_MODELVIEW);
|
|
|
|
oglBindTexture(GL_TEXTURE_2D, texture);
|
|
oglPushMatrix();
|
|
oglLoadIdentity();
|
|
|
|
float w = static_cast< float >(uiWidth);
|
|
float h = static_cast< float >(uiHeight);
|
|
|
|
float left = static_cast< float >(uiLeft);
|
|
float top = static_cast< float >(uiTop);
|
|
float right = static_cast< float >(uiRight);
|
|
float bottom = static_cast< float >(uiBottom);
|
|
|
|
float xm = (left) / w;
|
|
float ym = (top) / h;
|
|
float xmx = (right) / w;
|
|
float ymx = (bottom) / h;
|
|
|
|
oglTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
|
|
oglBegin(GL_QUADS);
|
|
|
|
oglTexCoord2f(xm, ymx);
|
|
oglVertex2f(left, bottom);
|
|
|
|
oglTexCoord2f(xm, ym);
|
|
oglVertex2f(left, top);
|
|
|
|
oglTexCoord2f(xmx, ym);
|
|
oglVertex2f(right, top);
|
|
|
|
oglTexCoord2f(xmx, ymx);
|
|
oglVertex2f(right, bottom);
|
|
oglEnd();
|
|
|
|
oglPopMatrix();
|
|
}
|
|
|
|
static map< HDC, Context * > contexts;
|
|
|
|
static void doSwap(HDC hdc) {
|
|
Context *c = contexts[hdc];
|
|
|
|
if (!c) {
|
|
ods("OpenGL: New context for device %p", hdc);
|
|
c = new Context(hdc);
|
|
contexts[hdc] = c;
|
|
} else {
|
|
ods("OpenGL: Reusing old context");
|
|
}
|
|
c->draw(hdc);
|
|
}
|
|
|
|
static BOOL __stdcall mywglSwapBuffers(HDC hdc) {
|
|
ods("OpenGL: wglSwapBuffers");
|
|
doSwap(hdc);
|
|
|
|
hhwglSwapBuffers.restore();
|
|
BOOL ret = owglSwapBuffers(hdc);
|
|
hhwglSwapBuffers.inject();
|
|
|
|
return ret;
|
|
}
|
|
|
|
/// Ensure that all the symbols that the OpenGL overlay requires have been
|
|
/// looked up.
|
|
/// @return true if all symbols have been looked up and are available.
|
|
/// Otherwise false.
|
|
static bool lookupSymbols(HMODULE hGL) {
|
|
#define FNFIND(handle, name) \
|
|
{ \
|
|
if (!o##name) { \
|
|
o##name = reinterpret_cast< t##name >(GetProcAddress(handle, #name)); \
|
|
if (!o##name) { \
|
|
ods("OpenGL: Could not resolve symbol %s in %s", #name, #handle); \
|
|
return false; \
|
|
} \
|
|
} \
|
|
}
|
|
|
|
if (!hGL) {
|
|
return false;
|
|
}
|
|
|
|
HMODULE hGDI = GetModuleHandle("GDI32.DLL");
|
|
if (!hGDI) {
|
|
ods("OpenGL: Failed to identify GDI32");
|
|
return false;
|
|
}
|
|
|
|
// Lookup OpenGL32.DLL symbols
|
|
FNFIND(hGL, wglCreateContext);
|
|
FNFIND(hGL, glGenTextures);
|
|
FNFIND(hGL, glDeleteTextures);
|
|
FNFIND(hGL, glEnable);
|
|
FNFIND(hGL, glDisable);
|
|
FNFIND(hGL, glBlendFunc);
|
|
FNFIND(hGL, glViewport);
|
|
FNFIND(hGL, glMatrixMode);
|
|
FNFIND(hGL, glLoadIdentity);
|
|
FNFIND(hGL, glOrtho);
|
|
FNFIND(hGL, glBindTexture);
|
|
FNFIND(hGL, glPushMatrix);
|
|
FNFIND(hGL, glBegin);
|
|
FNFIND(hGL, glEnd);
|
|
FNFIND(hGL, glTexCoord2f);
|
|
FNFIND(hGL, glVertex2f);
|
|
FNFIND(hGL, glPopMatrix);
|
|
FNFIND(hGL, glTexParameteri);
|
|
FNFIND(hGL, glTexEnvi);
|
|
FNFIND(hGL, glTexImage2D);
|
|
FNFIND(hGL, glTexSubImage2D);
|
|
FNFIND(hGL, glPixelStorei);
|
|
FNFIND(hGL, wglMakeCurrent);
|
|
FNFIND(hGL, wglGetCurrentContext);
|
|
FNFIND(hGL, wglGetCurrentDC);
|
|
|
|
// Lookup GDI32.DLL symbols
|
|
FNFIND(hGDI, GetDeviceCaps);
|
|
|
|
return true;
|
|
}
|
|
|
|
void checkOpenGLHook() {
|
|
static bool bCheckHookActive = false;
|
|
if (bCheckHookActive) {
|
|
ods("OpenGL: Recursion in checkOpenGLHook");
|
|
return;
|
|
}
|
|
|
|
bCheckHookActive = true;
|
|
|
|
if (!bHooked) {
|
|
HMODULE hGL = GetModuleHandle("OpenGL32.DLL");
|
|
|
|
if (lookupSymbols(hGL)) {
|
|
char procname[1024];
|
|
GetModuleFileName(nullptr, procname, 1024);
|
|
ods("OpenGL: Hooking into OpenGL App %s", procname);
|
|
|
|
// Add a ref to ourselves; we do NOT want to get unloaded directly from this process.
|
|
HMODULE hTempSelf = nullptr;
|
|
GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, reinterpret_cast< LPCTSTR >(&checkOpenGLHook),
|
|
&hTempSelf);
|
|
|
|
#define INJECT(handle, name) \
|
|
{ \
|
|
o##name = reinterpret_cast< t##name >(GetProcAddress(handle, #name)); \
|
|
if (o##name) { \
|
|
hh##name.setup(reinterpret_cast< voidFunc >(o##name), reinterpret_cast< voidFunc >(my##name)); \
|
|
o##name = (t##name) hh##name.call; \
|
|
} else { \
|
|
ods("OpenGL: Could not resolve symbol %s in %s", #name, #handle); \
|
|
} \
|
|
}
|
|
INJECT(hGL, wglSwapBuffers);
|
|
}
|
|
} else {
|
|
hhwglSwapBuffers.check();
|
|
}
|
|
|
|
bCheckHookActive = false;
|
|
}
|