// Copyright 2005-2020 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 . // This file is included by overlay.c for // Mac-specific overlay initialization. @implementation NSOpenGLContext (MumbleOverlay) - (void) overlayFlushBuffer { ods("[NSOpenGLContext flushBuffer] %p %p", self, [self CGLContextObj]); Context *c = contexts; while (c) { if (c->nsctx == self && c->cglctx == [self CGLContextObj]) break; c = c->next; } if (!c) { ods("Current context is: %p", self); c = malloc(sizeof(Context)); if (!c) { ods("malloc failure"); return; } c->next = contexts; c->nsctx = (NSOpenGLContext *)self; c->cglctx = (CGLContextObj)[self CGLContextObj]; contexts = c; newContext(c); } NSView *v = [c->nsctx view]; int width = 0, height = 0; if (v) { NSRect r = [v bounds]; width = (int)r.size.width; height = (int)r.size.height; } else { GLint viewport[4]; glGetIntegerv(GL_VIEWPORT, viewport); width = viewport[2]; height = viewport[3]; } drawContext(c, width, height); [self overlayFlushBuffer]; } @end void CGLFlushDrawableOverride(CGLContextObj ctx) { ods("CGLFlushDrawableOverride %p", ctx); Context *c = contexts; /* Sometimes, we can get a FlushDrawable where the current context is NULL. * Also, check that the context CGLFlushDrawable was called on is the active context. * If it isn't, who knows what context we're drawing on? * * Maybe we should even use CGLSetCurrentContext() to switch to the context that's being * flushed? */ CGLContextObj current = CGLGetCurrentContext(); if (current == NULL || ctx != current) goto skip; while (c) { if (c->cglctx == ctx) { /* There is no NSOpenGLContext for this CGLContext, so we should draw. */ if (c->nsctx == NULL) break; /* This context is coupled with a NSOpenGLContext, so skip. */ else goto skip; } c = c->next; } if (!c) { ods("Current context is: %p", ctx); c = malloc(sizeof(Context)); if (!c) { ods("malloc failure"); return; } memset(c, 0, sizeof(Context)); c->next = contexts; c->cglctx = ctx; c->nsctx = NULL; contexts = c; newContext(c); } GLint viewport[4]; glGetIntegerv(GL_VIEWPORT, viewport); int width = viewport[2]; int height = viewport[3]; /* Are the viewport values crazy? Skip them in that case. */ if (height < 0 || width < 0 || height > 5000 || width > 5000) { goto skip; } drawContext(c, width, height); skip: oCGLFlushDrawable(ctx); } CGError CGDisplayShowCursorOverride(CGDirectDisplayID display) { ods("CGDisplayShowCursor()"); return oCGDisplayShowCursor(display); } CGError CGDisplayHideCursorOverride(CGDirectDisplayID display) { ods("CGDisplayHideCursor()"); return oCGDisplayHideCursor(display); } __attribute__((constructor)) static void initializeLibrary() { struct stat buf; bDebug = (stat("/Library/Application Support/.mumble_overlay_debug", &buf) == 0); ods("!"); void *nsgl = NULL, *cgl = NULL; nsgl = dlsym(RTLD_DEFAULT, "NSClassFromString"); cgl = dlsym(RTLD_DEFAULT, "CGLFlushDrawable"); /* Check for GL symbol availability */ if (!(AVAIL_ALL_GLSYM)) { ods("Missing GL symbols. Disabling overlay."); return; } /* NSOpenGL */ if (nsgl) { ods("Attempting to hook NSOpenGL"); Class c = NSClassFromString(@"NSOpenGLContext"); if (c) { Method orig = class_getInstanceMethod(c, @selector(flushBuffer)); Method new = class_getInstanceMethod(c, @selector(overlayFlushBuffer)); if (class_addMethod(c, @selector(flushBuffer), method_getImplementation(new), method_getTypeEncoding(new))) class_replaceMethod(c, @selector(overlayFlushBuffer), method_getImplementation(orig), method_getTypeEncoding(orig)); else method_exchangeImplementations(orig, new); } } /* CGL */ if (AVAIL(CGLFlushDrawable)) { ods("Attempting to hook CGL"); if (mach_override_ptr(dlsym(RTLD_DEFAULT, "CGLFlushDrawable"), CGLFlushDrawableOverride, (void **) &oCGLFlushDrawable) != 0) { ods("CGLFlushDrawable override failed."); } else ods("Up running."); } else { ods("Required entry points not available in process. Not hooking up overlay."); } if (AVAIL(CGDisplayHideCursor) && AVAIL(CGDisplayShowCursor)) { if (mach_override_ptr(dlsym(RTLD_DEFAULT, "CGDisplayHideCursor"), CGDisplayHideCursorOverride, (void **) &oCGDisplayHideCursor) != 0) { ods("CGDisplayHideCursor override failed"); } if (mach_override_ptr(dlsym(RTLD_DEFAULT, "CGDisplayShowCursor"), CGDisplayShowCursorOverride, (void **) &oCGDisplayShowCursor) != 0) { ods("CGDisplayShowCursor override failed"); } ods("Hooked CGDisplayShowCursor and CGDisplayHideCursor"); bCursorAvail = true; } }