/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena 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 2 of the License, or (at your option) any later version. Quake III Arena 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 Foobar; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 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); }