CGMouseDeltaFix.m 5.2 KB

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