Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mumble-voip/mumble.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/macx
diff options
context:
space:
mode:
authorMikkel Krautz <mikkel@krautz.dk>2010-02-16 00:17:51 +0300
committerMikkel Krautz <mikkel@krautz.dk>2010-02-16 00:24:05 +0300
commit2dbba848bae6a051947701ed8a3261a9538dce63 (patch)
treee45e6df29a14d754346e49f2c6cd00312b5e650a /macx
parentffe8fe2872b7a9cca734ffd3037ee42a68383f41 (diff)
Move OSX overlay and overlay launcher into macx subdir.
Diffstat (limited to 'macx')
-rw-r--r--macx/common.pri6
-rw-r--r--macx/launcher/launcher.m170
-rw-r--r--macx/launcher/launcher.plist20
-rw-r--r--macx/launcher/launcher.pro23
-rw-r--r--macx/macx.pro4
-rw-r--r--macx/overlay/mach_override.c711
-rw-r--r--macx/overlay/mach_override.h157
-rw-r--r--macx/overlay/overlay.m587
-rw-r--r--macx/overlay/overlay.plist13
-rw-r--r--macx/overlay/overlay.pro28
10 files changed, 1719 insertions, 0 deletions
diff --git a/macx/common.pri b/macx/common.pri
new file mode 100644
index 000000000..f8add5c73
--- /dev/null
+++ b/macx/common.pri
@@ -0,0 +1,6 @@
+# Common OSX overlay settings.
+
+VERSION = 1.2.3
+
+DEFINES *= VERSION=\\\"$$VERSION\\\"
+
diff --git a/macx/launcher/launcher.m b/macx/launcher/launcher.m
new file mode 100644
index 000000000..7475a064f
--- /dev/null
+++ b/macx/launcher/launcher.m
@@ -0,0 +1,170 @@
+/*
+ Copyright (C) 2009, Mikkel Krautz <mikkel@krautz.dk>
+
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ - Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+ - Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+ - Neither the name of the Mumble Developers nor the names of its
+ contributors may be used to endorse or promote products derived from this
+ software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR
+ CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+/*
+ * Mumble overlay app-launcher for OS X.
+ */
+
+#include <unistd.h>
+#include <ApplicationServices/ApplicationServices.h>
+
+#import <Cocoa/Cocoa.h>
+
+static void usage(void) {
+ printf("Usage: mumble-overlay [opts] <app> [app-args ...]\n");
+ printf("\n");
+ printf(" Launch an application (given by the `app' argument) with\n");
+ printf(" the Mumble overlay enabled.\n");
+ printf("\n");
+ printf(" -h -? Show usage information.\n");
+ printf(" -d Run overlay in debug mode.\n");
+ printf(" -v Show version information.\n");
+ printf("\n");
+ exit(0);
+}
+
+static void version(void) {
+ printf("%s\n", VERSION);
+ exit(0);
+}
+
+int main(int argc, char *argv[]) {
+ NSAutoreleasePool *pool;
+ char *appPath = NULL;
+ int c, i, debug = 0;
+
+ pool = [[NSAutoreleasePool alloc] init];
+
+ while ((c = getopt(argc, argv, "h?vd")) != -1) {
+ switch (c) {
+ case 'h':
+ case '?':
+ usage();
+ break;
+ case 'v':
+ version();
+ break;
+ case 'd':
+ debug = 1;
+ break;
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc) {
+ appPath = argv[0];
+ --argc;
+ ++argv;
+ } else
+ usage();
+
+ FSRef ref;
+ OSStatus err;
+
+ /* Get FSRef of our app path. */
+ err = FSPathMakeRef((const UInt8 *) appPath, &ref, NULL);
+ if (err != noErr) {
+ fprintf(stderr, "mumble-overlay: Unable to get reference to application path. Make sure you specify a valid path.\n");
+ return -1;
+ }
+
+ /* Get a hold of our Mumble process, to find our overlay library. */
+ NSArray *apps = [[NSWorkspace sharedWorkspace] launchedApplications];
+ NSString *mumblePath = nil;
+
+ for (NSDictionary *dict in apps) {
+ if ([[dict objectForKey:@"NSApplicationBundleIdentifier"] isEqualToString:@"net.sourceforge.mumble.Mumble"]) {
+ mumblePath = [dict objectForKey:@"NSApplicationPath"];
+ break;
+ }
+ if ([[dict objectForKey:@"NSApplicationBundleIdentifier"] isEqualToString:@"net.sourceforge.mumble.Mumble11x"]) {
+ mumblePath = [dict objectForKey:@"NSApplicationPath"];
+ break;
+ }
+ }
+
+ if (! mumblePath) {
+ fprintf(stderr, "mumble-overlay: Cannot find open Mumble instance. Bailing.\n");
+ return -1;
+ }
+
+ NSBundle *appBundle = [NSBundle bundleWithPath: mumblePath];
+ if (! appBundle) {
+ fprintf(stderr, "mumble-overlay: Cannot open Mumble app bundle.\n");
+ return -1;
+ }
+
+ NSString *overlayInBundle = [appBundle objectForInfoDictionaryKey:@"MumbleOverlayLibrary"];
+ if (! overlayInBundle) {
+ fprintf(stderr, "mumble-overlay: No key 'MumbleOverlayLibrary' specified in Mumble's property list. Cannot find overlay library.\n");
+ return -1;
+ }
+
+ NSString *overlayPath = [NSString stringWithFormat:@"%@/%@", mumblePath, overlayInBundle];
+
+ if (! [[NSFileManager defaultManager] fileExistsAtPath:overlayPath]) {
+ fprintf(stderr, "mumble-overlay: Overlay library non-existant at MumbleOverlayLibrary-specified path.\n");
+ return -1;
+ }
+
+ /* Create environment dict. */
+ NSMutableDictionary *environment = [NSMutableDictionary dictionaryWithCapacity: 2];
+ [environment setObject:overlayPath forKey:@"DYLD_INSERT_LIBRARIES"];
+ if (debug) {
+ [environment setObject:@"1" forKey:@"MUMBLE_OVERLAY_DEBUG"];
+ }
+
+ /* Create argv array. */
+ NSMutableArray *arguments = [NSMutableArray arrayWithCapacity:(NSUInteger)argc];
+ for (i = 0; i < argc; i++) {
+ [arguments insertObject: [NSString stringWithCString:argv[i] encoding:NSUTF8StringEncoding] atIndex:i];
+ }
+
+ LSApplicationParameters parm = {
+ .version = 0,
+ .flags = kLSLaunchDefaults,
+ .application = &ref,
+ .asyncLaunchRefCon = NULL,
+ .environment = environment,
+ .argv = arguments,
+ .initialEvent = NULL
+ };
+
+ err = LSOpenApplication(&parm, NULL);
+
+ if (err != noErr) {
+ fprintf(stderr, "mumble-overlay: LSOpenApplication() failed: %i\n", (int)err);
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/macx/launcher/launcher.plist b/macx/launcher/launcher.plist
new file mode 100644
index 000000000..9b62681ea
--- /dev/null
+++ b/macx/launcher/launcher.plist
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>CFBundleExecutable</key>
+ <string>mumble-overlay</string>
+ <key>CFBundleGetInfoString</key>
+ <string>Mumble helper utility for launching games with the in-game overlay enabled.</string>
+ <key>CFBundleIdentifier</key>
+ <string>net.sourceforge.mumble.OverlayLauncher</string>
+ <key>CFBundlePackageType</key>
+ <string>APPL</string>
+ <key>CFBundleSignature</key>
+ <string>LAUN</string>
+ <key>CFBundleVersion</key>
+ <string>1.2.3</string>
+ <key>NSHumanReadableCopyright</key>
+ <string>Copyright (c) 2009 Mikkel Krautz &lt;mikkel@krautz.dk&gt;</string>
+</dict>
+</plist>
diff --git a/macx/launcher/launcher.pro b/macx/launcher/launcher.pro
new file mode 100644
index 000000000..43d9fa927
--- /dev/null
+++ b/macx/launcher/launcher.pro
@@ -0,0 +1,23 @@
+# Overlay app-launcher for Mac OS X
+
+include(../../compiler.pri)
+include(../common.pri)
+
+TEMPLATE = app
+CONFIG -= qt app_bundle
+CONFIG *= debug_and_release
+
+TARGET = mumble-overlay
+SOURCES = launcher.m
+QMAKE_LFLAGS += -framework ApplicationServices -framework Cocoa -sectcreate __TEXT __info_plist launcher.plist
+DIST = launcher.plist
+
+CONFIG(debug, debug|release) {
+ DESTDIR = ../../debug
+}
+
+CONFIG(release, debug|release) {
+ DESTDIR = ../../release
+}
+
+include(../../symbols.pri)
diff --git a/macx/macx.pro b/macx/macx.pro
new file mode 100644
index 000000000..138a9fadb
--- /dev/null
+++ b/macx/macx.pro
@@ -0,0 +1,4 @@
+TEMPLATE = subdirs
+
+CONFIG += debug_and_release
+SUBDIRS = overlay launcher
diff --git a/macx/overlay/mach_override.c b/macx/overlay/mach_override.c
new file mode 100644
index 000000000..3a1b3e22d
--- /dev/null
+++ b/macx/overlay/mach_override.c
@@ -0,0 +1,711 @@
+/*******************************************************************************
+ mach_override.c
+ Copyright (c) 2003-2009 Jonathan 'Wolf' Rentzsch: <http://rentzsch.com>
+ Some rights reserved: <http://opensource.org/licenses/mit-license.php>
+
+ ***************************************************************************/
+
+#include "mach_override.h"
+
+#include <mach-o/dyld.h>
+#include <mach/mach_host.h>
+#include <mach/mach_init.h>
+#include <mach/vm_map.h>
+#include <sys/mman.h>
+
+#include <CoreServices/CoreServices.h>
+
+/**************************
+*
+* Constants
+*
+**************************/
+#pragma mark -
+#pragma mark (Constants)
+
+#if defined(__ppc__) || defined(__POWERPC__)
+
+long kIslandTemplate[] = {
+ 0x9001FFFC, // stw r0,-4(SP)
+ 0x3C00DEAD, // lis r0,0xDEAD
+ 0x6000BEEF, // ori r0,r0,0xBEEF
+ 0x7C0903A6, // mtctr r0
+ 0x8001FFFC, // lwz r0,-4(SP)
+ 0x60000000, // nop ; optionally replaced
+ 0x4E800420 // bctr
+};
+
+#define kAddressHi 3
+#define kAddressLo 5
+#define kInstructionHi 10
+#define kInstructionLo 11
+
+#elif defined(__i386__)
+
+#define kOriginalInstructionsSize 16
+
+char kIslandTemplate[] = {
+ // kOriginalInstructionsSize nop instructions so that we
+ // should have enough space to host original instructions
+ 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+ 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+ // Now the real jump instruction
+ 0xE9, 0xEF, 0xBE, 0xAD, 0xDE
+};
+
+#define kInstructions 0
+#define kJumpAddress kInstructions + kOriginalInstructionsSize + 1
+#elif defined(__x86_64__)
+
+#define kOriginalInstructionsSize 32
+
+#define kJumpAddress kOriginalInstructionsSize + 6
+
+char kIslandTemplate[] = {
+ // kOriginalInstructionsSize nop instructions so that we
+ // should have enough space to host original instructions
+ 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+ 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+ 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+ 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+ // Now the real jump instruction
+ 0xFF, 0x25, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00
+};
+
+#endif
+
+#define kAllocateHigh 1
+#define kAllocateNormal 0
+
+/**************************
+*
+* Data Types
+*
+**************************/
+#pragma mark -
+#pragma mark (Data Types)
+
+typedef struct {
+ char instructions[sizeof(kIslandTemplate)];
+ int allocatedHigh;
+} BranchIsland;
+
+/**************************
+*
+* Funky Protos
+*
+**************************/
+#pragma mark -
+#pragma mark (Funky Protos)
+
+ mach_error_t
+allocateBranchIsland(
+ BranchIsland **island,
+ int allocateHigh,
+ void *originalFunctionAddress);
+
+ mach_error_t
+freeBranchIsland(
+ BranchIsland *island );
+
+#if defined(__ppc__) || defined(__POWERPC__)
+ mach_error_t
+setBranchIslandTarget(
+ BranchIsland *island,
+ const void *branchTo,
+ long instruction );
+#endif
+
+#if defined(__i386__) || defined(__x86_64__)
+mach_error_t
+setBranchIslandTarget_i386(
+ BranchIsland *island,
+ const void *branchTo,
+ char* instructions );
+void
+atomic_mov64(
+ uint64_t *targetAddress,
+ uint64_t value );
+
+ static Boolean
+eatKnownInstructions(
+ unsigned char *code,
+ uint64_t *newInstruction,
+ int *howManyEaten,
+ char *originalInstructions );
+#endif
+
+/*******************************************************************************
+*
+* Interface
+*
+*******************************************************************************/
+#pragma mark -
+#pragma mark (Interface)
+
+ mach_error_t
+mach_override(
+ char *originalFunctionSymbolName,
+ const char *originalFunctionLibraryNameHint,
+ const void *overrideFunctionAddress,
+ void **originalFunctionReentryIsland )
+{
+ assert( originalFunctionSymbolName );
+ assert( strlen( originalFunctionSymbolName ) );
+ assert( overrideFunctionAddress );
+
+ // Lookup the original function's code pointer.
+ long *originalFunctionPtr;
+ if( originalFunctionLibraryNameHint )
+ _dyld_lookup_and_bind_with_hint(
+ originalFunctionSymbolName,
+ originalFunctionLibraryNameHint,
+ (void*) &originalFunctionPtr,
+ NULL );
+ else
+ _dyld_lookup_and_bind(
+ originalFunctionSymbolName,
+ (void*) &originalFunctionPtr,
+ NULL );
+
+ //printf ("In mach_override\n");
+ return mach_override_ptr( originalFunctionPtr, overrideFunctionAddress,
+ originalFunctionReentryIsland );
+}
+
+#if defined(__x86_64__)
+mach_error_t makeIslandExecutable(void *address) {
+ mach_error_t err = err_none;
+ vm_size_t pageSize;
+ host_page_size( mach_host_self(), &pageSize );
+ uint64_t page = (uint64_t)address & ~(uint64_t)(pageSize-1);
+ int e = err_none;
+ e |= mprotect((void *)page, pageSize, PROT_EXEC | PROT_READ | PROT_WRITE);
+ e |= msync((void *)page, pageSize, MS_INVALIDATE );
+ if (e) {
+ err = err_cannot_override;
+ }
+ return err;
+}
+#endif
+
+ mach_error_t
+mach_override_ptr(
+ void *originalFunctionAddress,
+ const void *overrideFunctionAddress,
+ void **originalFunctionReentryIsland )
+{
+ assert( originalFunctionAddress );
+ assert( overrideFunctionAddress );
+
+ long *originalFunctionPtr = (long*) originalFunctionAddress;
+ mach_error_t err = err_none;
+
+#if defined(__ppc__) || defined(__POWERPC__)
+ // Ensure first instruction isn't 'mfctr'.
+ #define kMFCTRMask 0xfc1fffff
+ #define kMFCTRInstruction 0x7c0903a6
+
+ long originalInstruction = *originalFunctionPtr;
+ if( !err && ((originalInstruction & kMFCTRMask) == kMFCTRInstruction) )
+ err = err_cannot_override;
+#elif defined(__i386__) || defined(__x86_64__)
+ int eatenCount = 0;
+ char originalInstructions[kOriginalInstructionsSize];
+ uint64_t jumpRelativeInstruction = 0; // JMP
+
+ Boolean overridePossible = eatKnownInstructions ((unsigned char *)originalFunctionPtr,
+ &jumpRelativeInstruction, &eatenCount, originalInstructions);
+ if (eatenCount > kOriginalInstructionsSize) {
+ //printf ("Too many instructions eaten\n");
+ overridePossible = false;
+ }
+ if (!overridePossible) err = err_cannot_override;
+ if (err) printf("err = %x %d\n", err, __LINE__);
+#endif
+
+ // Make the original function implementation writable.
+ if( !err ) {
+ err = vm_protect( mach_task_self(),
+ (vm_address_t) originalFunctionPtr,
+ sizeof(long), false, (VM_PROT_ALL | VM_PROT_COPY) );
+ if( err )
+ err = vm_protect( mach_task_self(),
+ (vm_address_t) originalFunctionPtr, sizeof(long), false,
+ (VM_PROT_DEFAULT | VM_PROT_COPY) );
+ }
+ if (err) printf("err = %x %d\n", err, __LINE__);
+
+ // Allocate and target the escape island to the overriding function.
+ BranchIsland *escapeIsland = NULL;
+ if( !err )
+ err = allocateBranchIsland( &escapeIsland, kAllocateHigh, originalFunctionAddress );
+ if (err) printf("err = %x %d\n", err, __LINE__);
+
+
+#if defined(__ppc__) || defined(__POWERPC__)
+ if( !err )
+ err = setBranchIslandTarget( escapeIsland, overrideFunctionAddress, 0 );
+
+ // Build the branch absolute instruction to the escape island.
+ long branchAbsoluteInstruction = 0; // Set to 0 just to silence warning.
+ if( !err ) {
+ long escapeIslandAddress = ((long) escapeIsland) & 0x3FFFFFF;
+ branchAbsoluteInstruction = 0x48000002 | escapeIslandAddress;
+ }
+#elif defined(__i386__) || defined(__x86_64__)
+ if (err) printf("err = %x %d\n", err, __LINE__);
+
+ if( !err )
+ err = setBranchIslandTarget_i386( escapeIsland, overrideFunctionAddress, 0 );
+
+ if (err) printf("err = %x %d\n", err, __LINE__);
+ // Build the jump relative instruction to the escape island
+#endif
+
+
+#if defined(__i386__) || defined(__x86_64__)
+ if (!err) {
+ uint32_t addressOffset = ((void*)escapeIsland - (void*)originalFunctionPtr - 5);
+ addressOffset = OSSwapInt32(addressOffset);
+
+ jumpRelativeInstruction |= 0xE900000000000000LL;
+ jumpRelativeInstruction |= ((uint64_t)addressOffset & 0xffffffff) << 24;
+ jumpRelativeInstruction = OSSwapInt64(jumpRelativeInstruction);
+ }
+#endif
+
+ // Optionally allocate & return the reentry island.
+ BranchIsland *reentryIsland = NULL;
+ if( !err && originalFunctionReentryIsland ) {
+ err = allocateBranchIsland( &reentryIsland, kAllocateNormal, NULL);
+ if( !err )
+ *originalFunctionReentryIsland = reentryIsland;
+ }
+
+#if defined(__ppc__) || defined(__POWERPC__)
+ // Atomically:
+ // o If the reentry island was allocated:
+ // o Insert the original instruction into the reentry island.
+ // o Target the reentry island at the 2nd instruction of the
+ // original function.
+ // o Replace the original instruction with the branch absolute.
+ if( !err ) {
+ int escapeIslandEngaged = false;
+ do {
+ if( reentryIsland )
+ err = setBranchIslandTarget( reentryIsland,
+ (void*) (originalFunctionPtr+1), originalInstruction );
+ if( !err ) {
+ escapeIslandEngaged = CompareAndSwap( originalInstruction,
+ branchAbsoluteInstruction,
+ (UInt32*)originalFunctionPtr );
+ if( !escapeIslandEngaged ) {
+ // Someone replaced the instruction out from under us,
+ // re-read the instruction, make sure it's still not
+ // 'mfctr' and try again.
+ originalInstruction = *originalFunctionPtr;
+ if( (originalInstruction & kMFCTRMask) == kMFCTRInstruction)
+ err = err_cannot_override;
+ }
+ }
+ } while( !err && !escapeIslandEngaged );
+ }
+#elif defined(__i386__) || defined(__x86_64__)
+ // Atomically:
+ // o If the reentry island was allocated:
+ // o Insert the original instructions into the reentry island.
+ // o Target the reentry island at the first non-replaced
+ // instruction of the original function.
+ // o Replace the original first instructions with the jump relative.
+ //
+ // Note that on i386, we do not support someone else changing the code under our feet
+ if ( !err ) {
+ if( reentryIsland )
+ err = setBranchIslandTarget_i386( reentryIsland,
+ (void*) ((char *)originalFunctionPtr+eatenCount), originalInstructions );
+ if ( !err )
+ atomic_mov64((uint64_t *)originalFunctionPtr, jumpRelativeInstruction);
+ }
+#endif
+
+ // Clean up on error.
+ if( err ) {
+ if( reentryIsland )
+ freeBranchIsland( reentryIsland );
+ if( escapeIsland )
+ freeBranchIsland( escapeIsland );
+ }
+
+#if defined(__x86_64__)
+ err = makeIslandExecutable(escapeIsland);
+ err = makeIslandExecutable(reentryIsland);
+#endif
+
+ return err;
+}
+
+/*******************************************************************************
+*
+* Implementation
+*
+*******************************************************************************/
+#pragma mark -
+#pragma mark (Implementation)
+
+/***************************************************************************//**
+ Implementation: Allocates memory for a branch island.
+
+ @param island <- The allocated island.
+ @param allocateHigh -> Whether to allocate the island at the end of the
+ address space (for use with the branch absolute
+ instruction).
+ @result <- mach_error_t
+
+ ***************************************************************************/
+
+ mach_error_t
+allocateBranchIsland(
+ BranchIsland **island,
+ int allocateHigh,
+ void *originalFunctionAddress)
+{
+ assert( island );
+
+ mach_error_t err = err_none;
+
+ if( allocateHigh ) {
+ vm_size_t pageSize;
+ err = host_page_size( mach_host_self(), &pageSize );
+ if( !err ) {
+ assert( sizeof( BranchIsland ) <= pageSize );
+#if defined(__x86_64__)
+ vm_address_t first = (uint64_t)originalFunctionAddress & ~(uint64_t)(((uint64_t)1 << 31) - 1) | ((uint64_t)1 << 31); // start in the middle of the page?
+ vm_address_t last = 0x0;
+#else
+ vm_address_t first = 0xfeffffff;
+ vm_address_t last = 0xfe000000 + pageSize;
+#endif
+
+ vm_address_t page = first;
+ int allocated = 0;
+ vm_map_t task_self = mach_task_self();
+
+ while( !err && !allocated && page != last ) {
+
+ err = vm_allocate( task_self, &page, pageSize, 0 );
+ if( err == err_none )
+ allocated = 1;
+ else if( err == KERN_NO_SPACE ) {
+#if defined(__x86_64__)
+ page -= pageSize;
+#else
+ page += pageSize;
+#endif
+ err = err_none;
+ }
+ }
+ if( allocated )
+ *island = (void*) page;
+ else if( !allocated && !err )
+ err = KERN_NO_SPACE;
+ }
+ } else {
+ void *block = malloc( sizeof( BranchIsland ) );
+ if( block )
+ *island = block;
+ else
+ err = KERN_NO_SPACE;
+ }
+ if( !err )
+ (**island).allocatedHigh = allocateHigh;
+
+ return err;
+}
+
+/***************************************************************************//**
+ Implementation: Deallocates memory for a branch island.
+
+ @param island -> The island to deallocate.
+ @result <- mach_error_t
+
+ ***************************************************************************/
+
+ mach_error_t
+freeBranchIsland(
+ BranchIsland *island )
+{
+ assert( island );
+ assert( (*(long*)&island->instructions[0]) == kIslandTemplate[0] );
+ assert( island->allocatedHigh );
+
+ mach_error_t err = err_none;
+
+ if( island->allocatedHigh ) {
+ vm_size_t pageSize;
+ err = host_page_size( mach_host_self(), &pageSize );
+ if( !err ) {
+ assert( sizeof( BranchIsland ) <= pageSize );
+ err = vm_deallocate(
+ mach_task_self(),
+ (vm_address_t) island, pageSize );
+ }
+ } else {
+ free( island );
+ }
+
+ return err;
+}
+
+/***************************************************************************//**
+ Implementation: Sets the branch island's target, with an optional
+ instruction.
+
+ @param island -> The branch island to insert target into.
+ @param branchTo -> The address of the target.
+ @param instruction -> Optional instruction to execute prior to branch. Set
+ to zero for nop.
+ @result <- mach_error_t
+
+ ***************************************************************************/
+#if defined(__ppc__) || defined(__POWERPC__)
+ mach_error_t
+setBranchIslandTarget(
+ BranchIsland *island,
+ const void *branchTo,
+ long instruction )
+{
+ // Copy over the template code.
+ bcopy( kIslandTemplate, island->instructions, sizeof( kIslandTemplate ) );
+
+ // Fill in the address.
+ ((short*)island->instructions)[kAddressLo] = ((long) branchTo) & 0x0000FFFF;
+ ((short*)island->instructions)[kAddressHi]
+ = (((long) branchTo) >> 16) & 0x0000FFFF;
+
+ // Fill in the (optional) instuction.
+ if( instruction != 0 ) {
+ ((short*)island->instructions)[kInstructionLo]
+ = instruction & 0x0000FFFF;
+ ((short*)island->instructions)[kInstructionHi]
+ = (instruction >> 16) & 0x0000FFFF;
+ }
+
+ //MakeDataExecutable( island->instructions, sizeof( kIslandTemplate ) );
+ msync( island->instructions, sizeof( kIslandTemplate ), MS_INVALIDATE );
+
+ return err_none;
+}
+#endif
+
+#if defined(__i386__)
+ mach_error_t
+setBranchIslandTarget_i386(
+ BranchIsland *island,
+ const void *branchTo,
+ char* instructions )
+{
+
+ // Copy over the template code.
+ bcopy( kIslandTemplate, island->instructions, sizeof( kIslandTemplate ) );
+
+ // copy original instructions
+ if (instructions) {
+ bcopy (instructions, island->instructions + kInstructions, kOriginalInstructionsSize);
+ }
+
+ // Fill in the address.
+ int32_t addressOffset = (char *)branchTo - (island->instructions + kJumpAddress + 4);
+ *((int32_t *)(island->instructions + kJumpAddress)) = addressOffset;
+
+ msync( island->instructions, sizeof( kIslandTemplate ), MS_INVALIDATE );
+ return err_none;
+}
+
+#elif defined(__x86_64__)
+mach_error_t
+setBranchIslandTarget_i386(
+ BranchIsland *island,
+ const void *branchTo,
+ char* instructions )
+{
+ // Copy over the template code.
+ bcopy( kIslandTemplate, island->instructions, sizeof( kIslandTemplate ) );
+
+ // Copy original instructions.
+ if (instructions) {
+ bcopy (instructions, island->instructions, kOriginalInstructionsSize);
+ }
+
+ // Fill in the address.
+ *((uint64_t *)(island->instructions + kJumpAddress)) = (uint64_t)branchTo;
+ msync( island->instructions, sizeof( kIslandTemplate ), MS_INVALIDATE );
+
+ return err_none;
+}
+#endif
+
+
+#if defined(__i386__) || defined(__x86_64__)
+// simplistic instruction matching
+typedef struct {
+ unsigned int length; // max 15
+ unsigned char mask[15]; // sequence of bytes in memory order
+ unsigned char constraint[15]; // sequence of bytes in memory order
+} AsmInstructionMatch;
+
+#if defined(__i386__)
+static AsmInstructionMatch possibleInstructions[] = {
+ { 0x1, {0xFF}, {0x90} }, // nop
+ { 0x1, {0xFF}, {0x55} }, // push %esp
+ { 0x2, {0xFF, 0xFF}, {0x89, 0xE5} }, // mov %esp,%ebp
+ { 0x1, {0xFF}, {0x53} }, // push %ebx
+ { 0x3, {0xFF, 0xFF, 0x00}, {0x83, 0xEC, 0x00} }, // sub 0x??, %esp
+ { 0x1, {0xFF}, {0x57} }, // push %edi
+ { 0x1, {0xFF}, {0x56} }, // push %esi
+ { 0x0 }
+};
+#elif defined(__x86_64__)
+static AsmInstructionMatch possibleInstructions[] = {
+ { 0x1, {0xFF}, {0x90} }, // nop
+ { 0x1, {0xF8}, {0x50} }, // push %rX
+ { 0x3, {0xFF, 0xFF, 0xFF}, {0x48, 0x89, 0xE5} }, // mov %rsp,%rbp
+ { 0x4, {0xFF, 0xFF, 0xFF, 0x00}, {0x48, 0x83, 0xEC, 0x00} }, // sub 0x??, %rsp
+ { 0x4, {0xFB, 0xFF, 0x00, 0x00}, {0x48, 0x89, 0x00, 0x00} }, // move onto rbp
+ { 0x2, {0xFF, 0x00}, {0x41, 0x00} }, // push %rXX
+ { 0x2, {0xFF, 0x00}, {0x85, 0x00} }, // test %rX,%rX
+ { 0x0 }
+};
+#endif
+
+static Boolean codeMatchesInstruction(unsigned char *code, AsmInstructionMatch* instruction)
+{
+ Boolean match = true;
+
+ int i;
+ for (i=0; i<instruction->length; i++) {
+ unsigned char mask = instruction->mask[i];
+ unsigned char constraint = instruction->constraint[i];
+ unsigned char codeValue = code[i];
+
+ match = ((codeValue & mask) == constraint);
+ if (!match) break;
+ }
+
+ return match;
+}
+
+#if defined(__i386__) || defined(__x86_64__)
+ static Boolean
+eatKnownInstructions(
+ unsigned char *code,
+ uint64_t* newInstruction,
+ int* howManyEaten,
+ char* originalInstructions )
+{
+ Boolean allInstructionsKnown = true;
+ int totalEaten = 0;
+ unsigned char* ptr = code;
+ int remainsToEat = 5; // a JMP instruction takes 5 bytes
+
+ if (howManyEaten) *howManyEaten = 0;
+ while (remainsToEat > 0) {
+ Boolean curInstructionKnown = false;
+
+ // See if instruction matches one we know
+ AsmInstructionMatch* curInstr = possibleInstructions;
+ do {
+ if (curInstructionKnown = codeMatchesInstruction(ptr, curInstr)) break;
+ curInstr++;
+ } while (curInstr->length > 0);
+
+ // if all instruction matches failed, we don't know current instruction then, stop here
+ if (!curInstructionKnown) {
+ allInstructionsKnown = false;
+ break;
+ }
+
+ // At this point, we've matched curInstr
+ int eaten = curInstr->length;
+ ptr += eaten;
+ remainsToEat -= eaten;
+ totalEaten += eaten;
+ }
+
+
+ if (howManyEaten) *howManyEaten = totalEaten;
+
+ if (originalInstructions) {
+ Boolean enoughSpaceForOriginalInstructions = (totalEaten < kOriginalInstructionsSize);
+
+ if (enoughSpaceForOriginalInstructions) {
+ memset(originalInstructions, 0x90 /* NOP */, kOriginalInstructionsSize); // fill instructions with NOP
+ bcopy(code, originalInstructions, totalEaten);
+ } else {
+ // printf ("Not enough space in island to store original instructions. Adapt the island definition and kOriginalInstructionsSize\n");
+ return false;
+ }
+ }
+
+ if (allInstructionsKnown) {
+ // save last 3 bytes of first 64bits of codre we'll replace
+ uint64_t currentFirst64BitsOfCode = *((uint64_t *)code);
+ currentFirst64BitsOfCode = OSSwapInt64(currentFirst64BitsOfCode); // back to memory representation
+ currentFirst64BitsOfCode &= 0x0000000000FFFFFFLL;
+
+ // keep only last 3 instructions bytes, first 5 will be replaced by JMP instr
+ *newInstruction &= 0xFFFFFFFFFF000000LL; // clear last 3 bytes
+ *newInstruction |= (currentFirst64BitsOfCode & 0x0000000000FFFFFFLL); // set last 3 bytes
+ }
+
+ return allInstructionsKnown;
+}
+#endif
+
+#if defined(__i386__)
+asm(
+ ".text;"
+ ".align 2, 0x90;"
+ ".globl _atomic_mov64;"
+ "_atomic_mov64:;"
+ " pushl %ebp;"
+ " movl %esp, %ebp;"
+ " pushl %esi;"
+ " pushl %ebx;"
+ " pushl %ecx;"
+ " pushl %eax;"
+ " pushl %edx;"
+
+ // atomic push of value to an address
+ // we use cmpxchg8b, which compares content of an address with
+ // edx:eax. If they are equal, it atomically puts 64bit value
+ // ecx:ebx in address.
+ // We thus put contents of address in edx:eax to force ecx:ebx
+ // in address
+ " mov 8(%ebp), %esi;" // esi contains target address
+ " mov 12(%ebp), %ebx;"
+ " mov 16(%ebp), %ecx;" // ecx:ebx now contains value to put in target address
+ " mov (%esi), %eax;"
+ " mov 4(%esi), %edx;" // edx:eax now contains value currently contained in target address
+ " lock; cmpxchg8b (%esi);" // atomic move.
+
+ // restore registers
+ " popl %edx;"
+ " popl %eax;"
+ " popl %ecx;"
+ " popl %ebx;"
+ " popl %esi;"
+ " popl %ebp;"
+ " ret"
+);
+#elif defined(__x86_64__)
+void atomic_mov64(
+ uint64_t *targetAddress,
+ uint64_t value )
+{
+ *targetAddress = value;
+}
+#endif
+#endif \ No newline at end of file
diff --git a/macx/overlay/mach_override.h b/macx/overlay/mach_override.h
new file mode 100644
index 000000000..685f1deb1
--- /dev/null
+++ b/macx/overlay/mach_override.h
@@ -0,0 +1,157 @@
+/*******************************************************************************
+ mach_override.h
+ Copyright (c) 2003-2009 Jonathan 'Wolf' Rentzsch: <http://rentzsch.com>
+ Some rights reserved: <http://opensource.org/licenses/mit-license.php>
+
+ ***************************************************************************/
+
+/***************************************************************************//**
+ @mainpage mach_override
+ @author Jonathan 'Wolf' Rentzsch: <http://rentzsch.com>
+
+ This package, coded in C to the Mach API, allows you to override ("patch")
+ program- and system-supplied functions at runtime. You can fully replace
+ functions with your implementations, or merely head- or tail-patch the
+ original implementations.
+
+ Use it by #include'ing mach_override.h from your .c, .m or .mm file(s).
+
+ @todo Discontinue use of Carbon's MakeDataExecutable() and
+ CompareAndSwap() calls and start using the Mach equivalents, if they
+ exist. If they don't, write them and roll them in. That way, this
+ code will be pure Mach, which will make it easier to use everywhere.
+ Update: MakeDataExecutable() has been replaced by
+ msync(MS_INVALIDATE). There is an OSCompareAndSwap in libkern, but
+ I'm currently unsure if I can link against it. May have to roll in
+ my own version...
+ @todo Stop using an entire 4K high-allocated VM page per 28-byte escape
+ branch island. Done right, this will dramatically speed up escape
+ island allocations when they number over 250. Then again, if you're
+ overriding more than 250 functions, maybe speed isn't your main
+ concern...
+ @todo Add detection of: b, bl, bla, bc, bcl, bcla, bcctrl, bclrl
+ first-instructions. Initially, we should refuse to override
+ functions beginning with these instructions. Eventually, we should
+ dynamically rewrite them to make them position-independent.
+ @todo Write mach_unoverride(), which would remove an override placed on a
+ function. Must be multiple-override aware, which means an almost
+ complete rewrite under the covers, because the target address can't
+ be spread across two load instructions like it is now since it will
+ need to be atomically updatable.
+ @todo Add non-rentry variants of overrides to test_mach_override.
+
+ ***************************************************************************/
+
+#ifndef _mach_override_
+#define _mach_override_
+
+#include <sys/types.h>
+#include <mach/error.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+ /**
+ Returned if the function to be overrided begins with a 'mfctr' instruction.
+ */
+#define err_cannot_override (err_local|1)
+
+ /***************************************************************************//**
+ Dynamically overrides the function implementation referenced by
+ originalFunctionSymbolName with the implentation pointed to by
+ overrideFunctionAddress. Optionally returns a pointer to a "reentry island"
+ which, if jumped to, will resume the original implementation.
+
+ @param originalFunctionSymbolName -> Required symbol name of the
+ function to override (with
+ overrideFunctionAddress).
+ Remember, C function name
+ symbols are prepended with an
+ underscore.
+ @param originalFunctionLibraryNameHint -> Optional name of the library
+ which contains
+ originalFunctionSymbolName. Can
+ be NULL, but this may result in
+ the wrong function being
+ overridden and/or a crash.
+ @param overrideFunctionAddress -> Required address to the
+ overriding function.
+ @param originalFunctionReentryIsland <- Optional pointer to pointer to
+ the reentry island. Can be NULL.
+ @result <- err_cannot_override if the
+ original function's
+ implementation begins with the
+ 'mfctr' instruction.
+
+ ***************************************************************************/
+
+ mach_error_t
+ mach_override(
+ char *originalFunctionSymbolName,
+ const char *originalFunctionLibraryNameHint,
+ const void *overrideFunctionAddress,
+ void **originalFunctionReentryIsland);
+
+ /************************************************************************************//**
+ Dynamically overrides the function implementation referenced by
+ originalFunctionAddress with the implentation pointed to by overrideFunctionAddress.
+ Optionally returns a pointer to a "reentry island" which, if jumped to, will resume
+ the original implementation.
+
+ @param originalFunctionAddress -> Required address of the function to
+ override (with overrideFunctionAddress).
+ @param overrideFunctionAddress -> Required address to the overriding
+ function.
+ @param originalFunctionReentryIsland <- Optional pointer to pointer to the
+ reentry island. Can be NULL.
+ @result <- err_cannot_override if the original
+ function's implementation begins with
+ the 'mfctr' instruction.
+
+ ************************************************************************************/
+
+ mach_error_t
+ mach_override_ptr(
+ void *originalFunctionAddress,
+ const void *overrideFunctionAddress,
+ void **originalFunctionReentryIsland);
+
+ /************************************************************************************//**
+
+
+ ************************************************************************************/
+
+#ifdef __cplusplus
+
+#define MACH_OVERRIDE( ORIGINAL_FUNCTION_RETURN_TYPE, ORIGINAL_FUNCTION_NAME, ORIGINAL_FUNCTION_ARGS, ERR ) \
+ { \
+ static ORIGINAL_FUNCTION_RETURN_TYPE (*ORIGINAL_FUNCTION_NAME##_reenter)ORIGINAL_FUNCTION_ARGS; \
+ static bool ORIGINAL_FUNCTION_NAME##_overriden = false; \
+ class mach_override_class__##ORIGINAL_FUNCTION_NAME { \
+ public: \
+ static kern_return_t override(void *originalFunctionPtr) { \
+ kern_return_t result = err_none; \
+ if (!ORIGINAL_FUNCTION_NAME##_overriden) { \
+ ORIGINAL_FUNCTION_NAME##_overriden = true; \
+ result = mach_override_ptr( (void*)originalFunctionPtr, \
+ (void*)mach_override_class__##ORIGINAL_FUNCTION_NAME::replacement, \
+ (void**)&ORIGINAL_FUNCTION_NAME##_reenter ); \
+ } \
+ return result; \
+ } \
+ static ORIGINAL_FUNCTION_RETURN_TYPE replacement ORIGINAL_FUNCTION_ARGS {
+
+#define END_MACH_OVERRIDE( ORIGINAL_FUNCTION_NAME ) \
+ } \
+ }; \
+ \
+ err = mach_override_class__##ORIGINAL_FUNCTION_NAME::override((void*)ORIGINAL_FUNCTION_NAME); \
+ }
+
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+#endif // _mach_override_ \ No newline at end of file
diff --git a/macx/overlay/overlay.m b/macx/overlay/overlay.m
new file mode 100644
index 000000000..216979def
--- /dev/null
+++ b/macx/overlay/overlay.m
@@ -0,0 +1,587 @@
+/* Copyright (C) 2005-2010, Thorvald Natvig <thorvald@natvig.com>
+ Copyright (C) 2008-2009, Mikkel Krautz <mikkel@krautz.dk>
+
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ - Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+ - Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+ - Neither the name of the Mumble Developers nor the names of its
+ contributors may be used to endorse or promote products derived from this
+ software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR
+ CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include <wchar.h>
+#include <sys/types.h>
+#include <sys/mman.h>
+#include <sys/ipc.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <sys/stat.h>
+#include <time.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <string.h>
+#include <mach/mach_init.h>
+#include <mach/task.h>
+#include <dlfcn.h>
+#include <pwd.h>
+
+#include <OpenGL/OpenGL.h>
+#include <Carbon/Carbon.h>
+#include <Cocoa/Cocoa.h>
+#include <AGL/AGL.h>
+
+#include "mach_override.h"
+#include <objc/objc-runtime.h>
+
+#include "../../overlay/overlay.h"
+
+static bool bDebug = false;
+static bool vendorIntel = false;
+
+typedef struct _Context {
+ struct _Context *next;
+ CGLContextObj cglctx;
+ GLuint uiProgram;
+
+ unsigned int uiWidth, uiHeight;
+ unsigned int uiLeft, uiRight, uiTop, uiBottom;
+
+ struct sockaddr_un saName;
+ int iSocket;
+ struct OverlayMsg omMsg;
+ GLuint texture;
+
+ unsigned char *a_ucTexture;
+ unsigned int uiMappedLength;
+} 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 FDEF(name) static __typeof__(&name) o##name = NULL
+FDEF(CGLFlushDrawable);
+
+__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 = ~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;
+ }
+
+ 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));
+
+ if (!strcmp(glGetString(GL_VENDOR), "Intel Inc.")) {
+ ods("Enabling Intel specific hacks");
+ vendorIntel = true;
+ }
+
+ 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);
+}
+
+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 != ~0) {
+ glDeleteTextures(1, &ctx->texture);
+ ctx->texture = ~0;
+ }
+ 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) {
+ ssize_t wantsend = sizeof(struct OverlayMsgHeader) + om->omh.iLength;
+ ssize_t sent = send(ctx->iSocket, om, wantsend, MSG_DONTWAIT);
+ if (wantsend == sent)
+ return true;
+ ods("Short write");
+ }
+ disconnect(ctx);
+ return false;
+}
+
+static void regenTexture(Context *ctx) {
+ if (ctx->texture != ~0)
+ 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, ctx->uiWidth, ctx->uiHeight, 0, GL_BGRA, GL_UNSIGNED_BYTE, ctx->a_ucTexture);
+}
+
+static void drawOverlay(Context *ctx, unsigned int width, unsigned int height) {
+ 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("Connected");
+ }
+
+ if ((ctx->uiWidth != width) || (ctx->uiHeight != height)) {
+ ods("Sent init");
+ 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;
+ }
+
+ while (1) {
+ if (ctx->omMsg.omh.iLength == -1) {
+ 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");
+ disconnect(ctx);
+ return;
+ }
+ } else {
+ ssize_t length = recv(ctx->iSocket, ctx->omMsg.msgbuffer, 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 message read %x %d/%d", ctx->omMsg.omh.uiType, length, ctx->omMsg.omh.iLength);
+ disconnect(ctx);
+ return;
+ }
+ ctx->omMsg.omh.iLength = -1;
+
+ switch (ctx->omMsg.omh.uiType) {
+ case OVERLAY_MSGTYPE_SHMEM: {
+ struct OverlayMsgShmem *oms = & 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;
+ fstat(fd, &buf);
+ if (buf.st_size >= ctx->uiWidth * ctx->uiHeight * 4) {
+ ctx->uiMappedLength = buf.st_size;
+ ctx->a_ucTexture = mmap(NULL, buf.st_size, PROT_READ, MAP_SHARED, fd, 0);
+ if (ctx->a_ucTexture != (unsigned char *) -1) {
+ regenTexture(ctx);
+ continue;
+ }
+ ctx->a_ucTexture = NULL;
+ }
+ ctx->uiMappedLength = 0;
+ close(fd);
+ }
+ ods("Failed to map memory");
+ }
+ break;
+ 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 != ~0)) {
+ 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, ctx->uiWidth, ctx->uiHeight, 0, GL_BGRA, GL_UNSIGNED_BYTE, ctx->a_ucTexture);
+ } else {
+ 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);
+ int r;
+ memset(ptr, 0, w * h * 4);
+
+ for (r = 0; r < h; ++r) {
+ const unsigned char *sptr = ctx->a_ucTexture + 4 * ((y+r) * ctx->uiWidth + x);
+ unsigned char *dptr = ptr + 4 * w * r;
+ memcpy(dptr, sptr, w * 4);
+ }
+
+ glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, w, 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;
+ default:
+ break;
+ }
+ }
+ }
+
+ if ((ctx->a_ucTexture == NULL) || (ctx->texture == ~0))
+ return;
+
+ if (! glIsTexture(ctx->texture)) {
+ ctx->texture = ~0;
+ 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 hijacked");
+ 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,
+ right, bottom};
+ GLfloat tex[] = {xm, ymx, xm, ym, xmx, ym, xmx, ymx};
+
+ if (! vendorIntel) {
+ glVertexPointer(2, GL_FLOAT, 0, vertex);
+ glTexCoordPointer(2, GL_FLOAT, 0, tex);
+ glDrawArrays(GL_QUADS, 0, 4);
+ } else {
+ glBegin(GL_QUADS);
+ glTexCoord2f(tex[0], tex[1]);
+ glVertex2f(vertex[0], vertex[1]);
+ glTexCoord2f(tex[2], tex[3]);
+ glVertex2f(vertex[2], vertex[3]);
+ glTexCoord2f(tex[4], tex[5]);
+ glVertex2f(vertex[4], vertex[5]);
+ glTexCoord2f(tex[6], tex[7]);
+ glVertex2f(vertex[6], vertex[7]);
+ glEnd();
+ }
+
+ glPopMatrix();
+}
+
+static void drawContext(Context * ctx, int width, int height) {
+ GLint program;
+ GLint viewport[4];
+ int i;
+
+ glPushAttrib(GL_ALL_ATTRIB_BITS);
+ glPushClientAttrib(GL_ALL_ATTRIB_BITS);
+ glGetIntegerv(GL_VIEWPORT, viewport);
+ glGetIntegerv(GL_CURRENT_PROGRAM, &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();
+
+
+ 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);
+ glDisable(GL_TEXTURE_GEN_Q);
+ glDisable(GL_TEXTURE_GEN_R);
+ glDisable(GL_TEXTURE_GEN_S);
+ 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 + 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);
+
+ 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_SRC_ALPHA, 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);
+
+ int bound = 0;
+ glGetIntegerv(GL_PIXEL_UNPACK_BUFFER_BINDING, &bound);
+
+ if (bound != 0)
+ glBindBuffer(GL_PIXEL_UNPACK_BUFFER_ARB, 0);
+
+ drawOverlay(ctx, width, height);
+
+ if (bound != 0)
+ glBindBuffer(GL_PIXEL_UNPACK_BUFFER_ARB, bound);
+
+ 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);
+
+ while (glGetError() != GL_NO_ERROR);
+}
+
+void CGLFlushDrawableOverride(CGLContextObj ctx) {
+ ods("CGLFlushDrawable()");
+
+ Context *c = contexts;
+
+ while (c) {
+ if (c->cglctx == ctx)
+ break;
+ 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;
+ contexts = c;
+ newContext(c);
+ }
+
+ GLint viewport[4];
+ glGetIntegerv(GL_VIEWPORT, viewport);
+ ods("%i, %i", viewport[0], viewport[1]);
+ int width = viewport[2];
+ int height = viewport[3];
+ drawContext(c, width, height);
+
+ oCGLFlushDrawable(ctx);
+}
+
+
+__attribute__((constructor))
+static void entryPoint() {
+ if (getenv("MUMBLE_OVERLAY_DEBUG"))
+ bDebug = true;
+ else
+ bDebug = false;
+
+ void *cglAvailable = dlsym(RTLD_DEFAULT, "CGLFlushDrawable");
+ if (cglAvailable) {
+ ods("Attempting to hook CGL");
+ if (mach_override("_CGLFlushDrawable", NULL, CGLFlushDrawableOverride, (void **) &oCGLFlushDrawable) != 0) {
+ ods("CGLFlushDrawable override failed.");
+ }
+ } else {
+ ods("Unable to hook CGL");
+ return;
+ }
+
+ ods("Up running.");
+}
diff --git a/macx/overlay/overlay.plist b/macx/overlay/overlay.plist
new file mode 100644
index 000000000..fd75e9336
--- /dev/null
+++ b/macx/overlay/overlay.plist
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist SYSTEM "file://localhost/System/Library/DTDs/PropertyList.dtd">
+<plist version="0.9">
+<dict>
+ <key>CFBundlePackageType</key>
+ <string>FMWK</string>
+ <key>CFBundleSignature</key>
+ <string>OLAY</string>
+ <key>CFBundleExecutable</key>
+ <string>MumbleOverlay</string>
+</dict>
+</plist>
+
diff --git a/macx/overlay/overlay.pro b/macx/overlay/overlay.pro
new file mode 100644
index 000000000..594813692
--- /dev/null
+++ b/macx/overlay/overlay.pro
@@ -0,0 +1,28 @@
+# Overlay payload for Mac OS X.
+
+include(../../compiler.pri)
+
+CONFIG += x86_64 i386 ppc
+
+TEMPLATE = lib
+CONFIG += plugin
+CONFIG -= gui qt
+
+TARGET = mumbleoverlay
+
+QMAKE_LFLAGS_PLUGIN += -undefined dynamic_lookup -dynamic
+QMAKE_LFLAGS = -framework CoreFoundation
+
+SOURCES = mach_override.c overlay.m
+HEADERS = mach_override.h
+DIST = overlay.plist
+
+CONFIG(debug, debug|release) {
+ DESTDIR = ../../debug
+}
+
+CONFIG(release, debug|release) {
+ DESTDIR = ../../release
+}
+
+include(../../symbols.pri)