CGMouseDeltaFix.m (5304B)
1 /* 2 =========================================================================== 3 Copyright (C) 1999-2005 Id Software, Inc. 4 5 This file is part of Quake III Arena source code. 6 7 Quake III Arena source code is free software; you can redistribute it 8 and/or modify it under the terms of the GNU General Public License as 9 published by the Free Software Foundation; either version 2 of the License, 10 or (at your option) any later version. 11 12 Quake III Arena source code is distributed in the hope that it will be 13 useful, but WITHOUT ANY WARRANTY; without even the implied warranty of 14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 GNU General Public License for more details. 16 17 You should have received a copy of the GNU General Public License 18 along with Foobar; if not, write to the Free Software 19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 20 =========================================================================== 21 */ 22 #import "CGMouseDeltaFix.h" 23 #import "CGPrivateAPI.h" 24 25 #import <Foundation/Foundation.h> 26 #import <mach-o/dyld.h> 27 28 29 // 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. 30 31 32 static BOOL originalVersionShouldWork = YES; 33 static CGMouseDelta CGFix_Mouse_DeltaX, CGFix_Mouse_DeltaY; 34 35 static void CGFix_NotificationCallback(CGSNotificationType note, CGSNotificationData data, CGSByteCount dataLength, CGSNotificationArg arg); 36 37 static CGSRegisterNotifyProcType registerNotifyProc = NULL; 38 39 void CGFix_Initialize() 40 { 41 NSAutoreleasePool *pool; 42 NSBundle *cgBundle; 43 NSString *version; 44 NSArray *components; 45 46 if (registerNotifyProc) 47 // 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). 48 return; 49 50 //NSLog(@"CGFix_Initialize\n"); 51 52 pool = [[NSAutoreleasePool alloc] init]; 53 cgBundle = [NSBundle bundleWithPath: @"/System/Library/Frameworks/ApplicationServices.framework/Frameworks/CoreGraphics.framework"]; 54 if (!cgBundle) { 55 // If it's moved, it must be newer than what we know about and should work 56 //NSLog(@"No /System/Library/Frameworks/ApplicationServices.framework/Frameworks/CoreGraphics.framework\n"); 57 goto done; 58 } 59 60 version = [[cgBundle infoDictionary] objectForKey: @"CFBundleShortVersionString"]; 61 components = [version componentsSeparatedByString: @"."]; 62 //NSLog(@"version = %@\n", version); 63 //NSLog(@"components = %@\n", components); 64 65 66 if ([components count] < 2) 67 // We don't understand this versioning scheme. Must have changed. 68 goto done; 69 70 if (![[components objectAtIndex: 0] isEqualToString: @"1"] || [[components objectAtIndex: 1] intValue] > 93) 71 // This version should be new enough to work 72 goto done; 73 74 // Look up the function pointer we need to register our callback. 75 if (!NSIsSymbolNameDefined("_CGSRegisterNotifyProc")) { 76 //NSLog(@"No _CGSRegisterNotifyProc\n"); 77 goto done; 78 } 79 80 registerNotifyProc = NSAddressOfSymbol(NSLookupAndBindSymbol("_CGSRegisterNotifyProc")); 81 //NSLog(@"registerNotifyProc = 0x%08x", registerNotifyProc); 82 83 // Must not work if we got here 84 originalVersionShouldWork = NO; 85 86 // We want to catch all the events that could possible indicate mouse movement and sum them up 87 registerNotifyProc( CGFix_NotificationCallback, kCGSEventNotificationMouseMoved, NULL); 88 registerNotifyProc( CGFix_NotificationCallback, kCGSEventNotificationLeftMouseDragged, NULL); 89 registerNotifyProc( CGFix_NotificationCallback, kCGSEventNotificationRightMouseDragged, NULL); 90 registerNotifyProc( CGFix_NotificationCallback, kCGSEventNotificationNotificationOtherMouseDragged, NULL); 91 92 done: 93 [pool release]; 94 } 95 96 void CGFix_GetLastMouseDelta(CGMouseDelta *dx, CGMouseDelta *dy) 97 { 98 if (originalVersionShouldWork) { 99 CGGetLastMouseDelta(dx, dy); 100 return; 101 } 102 103 *dx = CGFix_Mouse_DeltaX; 104 *dy = CGFix_Mouse_DeltaY; 105 106 CGFix_Mouse_DeltaX = CGFix_Mouse_DeltaY = 0; 107 } 108 109 static void CGFix_NotificationCallback(CGSNotificationType note, CGSNotificationData data, CGSByteCount dataLength, CGSNotificationArg arg) 110 { 111 CGSEventRecordPtr event; 112 113 //fprintf(stderr, "CGFix_NotificationCallback(note=%d, date=0x%08x, dataLength=%d, arg=0x%08x)\n", note, data, dataLength, arg); 114 115 #ifdef DEBUG 116 if ((note != kCGSEventNotificationMouseMoved && 117 note != kCGSEventNotificationLeftMouseDragged && 118 note != kCGSEventNotificationRightMouseDragged && 119 note != kCGSEventNotificationNotificationOtherMouseDragged) || 120 dataLength != sizeof (CGSEventRecord)) 121 fprintf(stderr, "Unexpected arguments to callback function CGFix_NotificationCallback(note=%d, date=0x%08x, dataLength=%d, arg=0x%08x)\n", note, data, dataLength, arg); 122 abort(); 123 } 124 #endif 125 126 event = (CGSEventRecordPtr)data; 127 128 CGFix_Mouse_DeltaX += event->data.move.deltaX; 129 CGFix_Mouse_DeltaY += event->data.move.deltaY; 130 //fprintf(stderr, " dx += %d, dy += %d\n", event->data.move.deltaX, event->data.move.deltaY); 131 }