/* =========================================================================== Return to Castle Wolfenstein multiplayer GPL Source Code Copyright (C) 1999-2010 id Software LLC, a ZeniMax Media company. This file is part of the Return to Castle Wolfenstein multiplayer GPL Source Code (“RTCW MP Source Code”). RTCW MP Source Code is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. RTCW MP Source Code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with RTCW MP Source Code. If not, see . In addition, the RTCW MP Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the RTCW MP Source Code. If not, please request a copy in writing from id Software at the address below. If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. =========================================================================== */ #import "CGMouseDeltaFix.h" #import "CGPrivateAPI.h" #import #import // We will try to automatically fall back to using the original CGGetLastMouseDelta when we are on a system new enough to have the fix. Any version of CoreGraphics past 1.93.0 will have the fixed version. static BOOL originalVersionShouldWork = YES; static CGMouseDelta CGFix_Mouse_DeltaX, CGFix_Mouse_DeltaY; static void CGFix_NotificationCallback( CGSNotificationType note, CGSNotificationData data, CGSByteCount dataLength, CGSNotificationArg arg ); static CGSRegisterNotifyProcType registerNotifyProc = NULL; void CGFix_Initialize() { NSAutoreleasePool *pool; NSBundle *cgBundle; NSString *version; NSArray *components; if ( registerNotifyProc ) { // We've already been called once and have registered our callbacks. If the original version works, this will be NULL, but we'll end up doing nothing (again, possibly). return; } //NSLog(@"CGFix_Initialize\n"); pool = [[NSAutoreleasePool alloc] init]; cgBundle = [NSBundle bundleWithPath: @ "/System/Library/Frameworks/ApplicationServices.framework/Frameworks/CoreGraphics.framework"]; if ( !cgBundle ) { // If it's moved, it must be newer than what we know about and should work //NSLog(@"No /System/Library/Frameworks/ApplicationServices.framework/Frameworks/CoreGraphics.framework\n"); goto done; } version = [[cgBundle infoDictionary] objectForKey: @ "CFBundleShortVersionString"]; components = [version componentsSeparatedByString: @ "."]; //NSLog(@"version = %@\n", version); //NSLog(@"components = %@\n", components); if ([components count] < 2 ) { // We don't understand this versioning scheme. Must have changed. goto done; } if ( ![[components objectAtIndex : 0] isEqualToString : @ "1"] || [[components objectAtIndex : 1] intValue] > 93 ) { // This version should be new enough to work goto done; } // Look up the function pointer we need to register our callback. if ( !NSIsSymbolNameDefined( "_CGSRegisterNotifyProc" ) ) { //NSLog(@"No _CGSRegisterNotifyProc\n"); goto done; } registerNotifyProc = NSAddressOfSymbol( NSLookupAndBindSymbol( "_CGSRegisterNotifyProc" ) ); //NSLog(@"registerNotifyProc = 0x%08x", registerNotifyProc); // Must not work if we got here originalVersionShouldWork = NO; // We want to catch all the events that could possible indicate mouse movement and sum them up registerNotifyProc( CGFix_NotificationCallback, kCGSEventNotificationMouseMoved, NULL ); registerNotifyProc( CGFix_NotificationCallback, kCGSEventNotificationLeftMouseDragged, NULL ); registerNotifyProc( CGFix_NotificationCallback, kCGSEventNotificationRightMouseDragged, NULL ); registerNotifyProc( CGFix_NotificationCallback, kCGSEventNotificationNotificationOtherMouseDragged, NULL ); done: [pool release]; } void CGFix_GetLastMouseDelta( CGMouseDelta *dx, CGMouseDelta *dy ) { if ( originalVersionShouldWork ) { CGGetLastMouseDelta( dx, dy ); return; } *dx = CGFix_Mouse_DeltaX; *dy = CGFix_Mouse_DeltaY; CGFix_Mouse_DeltaX = CGFix_Mouse_DeltaY = 0; } static void CGFix_NotificationCallback( CGSNotificationType note, CGSNotificationData data, CGSByteCount dataLength, CGSNotificationArg arg ) { CGSEventRecordPtr event; //fprintf(stderr, "CGFix_NotificationCallback(note=%d, date=0x%08x, dataLength=%d, arg=0x%08x)\n", note, data, dataLength, arg); #ifdef DEBUG if ( ( note != kCGSEventNotificationMouseMoved && note != kCGSEventNotificationLeftMouseDragged && note != kCGSEventNotificationRightMouseDragged && note != kCGSEventNotificationNotificationOtherMouseDragged ) || dataLength != sizeof( CGSEventRecord ) ) { fprintf( stderr, "Unexpected arguments to callback function CGFix_NotificationCallback(note=%d, date=0x%08x, dataLength=%d, arg=0x%08x)\n", note, data, dataLength, arg ); } abort(); } #endif event = (CGSEventRecordPtr)data; CGFix_Mouse_DeltaX += event->data.move.deltaX; CGFix_Mouse_DeltaY += event->data.move.deltaY; //fprintf(stderr, " dx += %d, dy += %d\n", event->data.move.deltaX, event->data.move.deltaY); }