From 61f8cdec21ab083b29c22aa11fda54d6005666ca Mon Sep 17 00:00:00 2001 From: Frank Huang Date: Mon, 30 Jul 2012 11:10:23 -0700 Subject: [PATCH] Scrolling, mouse scope, icon --- Credits.rtf | 8 +- English.lproj/MainMenu.xib | 648 ++++++++++++++++++++++++++++++-- Enjoy.xcodeproj/project.pbxproj | 12 + Enjoy_Prefix.pch | 4 +- JSActionAnalog.h | 3 + JSActionAnalog.m | 9 +- JoystickController.h | 7 + JoystickController.m | 60 ++- README.md | 14 +- Target.h | 11 +- Target.m | 25 +- TargetController.h | 4 + TargetController.m | 58 ++- TargetKeyboard.m | 4 +- TargetMouseBtn.m | 4 +- TargetMouseMove.m | 60 ++- TargetMouseScroll.h | 19 + TargetMouseScroll.m | 35 ++ TargetToggleMouseScope.h | 15 + TargetToggleMouseScope.m | 28 ++ icon.icns | Bin 0 -> 58086 bytes 21 files changed, 945 insertions(+), 83 deletions(-) create mode 100644 TargetMouseScroll.h create mode 100644 TargetMouseScroll.m create mode 100644 TargetToggleMouseScope.h create mode 100644 TargetToggleMouseScope.m create mode 100644 icon.icns diff --git a/Credits.rtf b/Credits.rtf index d05d232..60f8d64 100644 --- a/Credits.rtf +++ b/Credits.rtf @@ -1,8 +1,8 @@ -{\rtf1\ansi\ansicpg1252\cocoartf949\cocoasubrtf430 +{\rtf1\ansi\ansicpg1252\cocoartf1138\cocoasubrtf470 {\fonttbl\f0\fswiss\fcharset0 Helvetica;} {\colortbl;\red255\green255\blue255;} \paperw11900\paperh16840\margl1440\margr1440\vieww9800\viewh11760\viewkind0 -\pard\tx566\tx1133\tx1700\tx2267\tx2834\tx3401\tx3968\tx4535\tx5102\tx5669\tx6236\tx6803\ql\qnatural\pardirnatural +\pard\tx566\tx1133\tx1700\tx2267\tx2834\tx3401\tx3968\tx4535\tx5102\tx5669\tx6236\tx6803\pardirnatural \f0\fs24 \cf0 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\ \ @@ -10,4 +10,6 @@ The above copyright notice and this permission notice shall be included in\ all copies or substantial portions of the Software.\ \ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\ -} \ No newline at end of file +\ +\ +The joystick icon is from the Tango icon set and is public domain.} \ No newline at end of file diff --git a/English.lproj/MainMenu.xib b/English.lproj/MainMenu.xib index 60d948f..486e9db 100644 --- a/English.lproj/MainMenu.xib +++ b/English.lproj/MainMenu.xib @@ -860,6 +860,7 @@ 256 {242, 485} + YES @@ -956,6 +957,7 @@ {{1, 1}, {242, 485}} + @@ -966,6 +968,7 @@ -2147483392 {{1, 1}, {8, 298}} + _doScroller: @@ -976,6 +979,7 @@ -2147483392 {{-100, -100}, {473, 15}} + 1 @@ -985,6 +989,7 @@ {244, 487} + 133650 @@ -995,6 +1000,7 @@ {244, 487} + NSView @@ -1003,23 +1009,89 @@ 256 YES - + 268 - {{227, 57}, {180, 24}} + {{227, 241}, {180, 24}} - + + _NS:9 YES - + 67239424 0 - + LucidaGrande 13 16 _NS:9 + + + YES + + 87 + Horizontal + YES + 0 + + + 86 + Vertical + 1 + 0 + + + 1 + + + + + 268 + {{227, 137}, {180, 24}} + + + _NS:9 + YES + + 67239424 + 0 + + _NS:9 + + + YES + + 87 + Up + YES + 0 + + + 86 + Down + 1 + 0 + + + 1 + + + + + 268 + {{227, 189}, {180, 24}} + + + + _NS:9 + YES + + 67239424 + 0 + + _NS:9 YES @@ -1042,10 +1114,11 @@ 268 - {{20, 39}, {201, 388}} + {{20, 70}, {201, 369}} + - 6 + 7 1 YES @@ -1298,7 +1371,7 @@ ZSwgSW5jLiwgMjAwOQA 604110336 0 - Mouse X + Mouse movement 1211912703 @@ -1310,7 +1383,7 @@ ZSwgSW5jLiwgMjAwOQA 604110336 0 - Mouse Y + Mouse button 1211912703 @@ -1322,7 +1395,19 @@ ZSwgSW5jLiwgMjAwOQA 604110336 0 - Mouse button + Mouse scroll + + + 1211912703 + 128 + + 400 + 75 + + + 604110336 + 0 + Toggle mouse scope 1211912703 @@ -1332,7 +1417,7 @@ ZSwgSW5jLiwgMjAwOQA 75 - {201, 63} + {201, 51} {4, 2} 1151868928 NSActionCell @@ -1413,6 +1498,7 @@ AAMAAAABAAEAAAFTAAMAAAAEAAAFwgAAAAAACAAIAAgACAABAAEAAQABA 2322 {172, 14} + @@ -1490,11 +1576,12 @@ AAMAAAABAAEAAAFTAAMAAAAEAAAFwgAAAAAACAAIAAgACAABAAEAAQABA {{2, 2}, {172, 20}} + - {1, -1} + {5, 5} 0 4 @@ -1504,6 +1591,7 @@ AAMAAAABAAEAAAFTAAMAAAAEAAAFwgAAAAAACAAIAAgACAABAAEAAQABA -2147483392 {{-100, -100}, {15, 133}} + _doScroller: @@ -1514,6 +1602,7 @@ AAMAAAABAAEAAAFTAAMAAAAEAAAFwgAAAAAACAAIAAgACAABAAEAAQABA -2147483392 {{-100, -100}, {87, 18}} + 1 @@ -1522,8 +1611,9 @@ AAMAAAABAAEAAAFTAAMAAAAEAAAFwgAAAAAACAAIAAgACAABAAEAAQABA 0.94565220000000005 - {{229, 318}, {176, 24}} + {{229, 350}, {176, 24}} + 133123 @@ -1533,9 +1623,10 @@ AAMAAAABAAEAAAFTAAMAAAAEAAAFwgAAAAAACAAIAAgACAABAAEAAQABA 268 - {{226, 251}, {182, 26}} + {{226, 293}, {182, 26}} - + + YES -1539178944 @@ -1568,6 +1659,7 @@ AAMAAAABAAEAAAFTAAMAAAAEAAAFwgAAAAAACAAIAAgACAABAAEAAQABA 268 {{5, 456}, {507, 17}} + YES @@ -1590,6 +1682,7 @@ AAMAAAABAAEAAAFTAAMAAAAEAAAFwgAAAAAACAAIAAgACAABAAEAAQABA 12 {{12, 445}, {493, 5}} + {0, 0} @@ -1616,26 +1709,29 @@ AAMAAAABAAEAAAFTAAMAAAAEAAAFwgAAAAAACAAIAAgACAABAAEAAQABA {{253, 0}, {517, 487}} + NSView {770, 487} + YES {770, 487} + - {{0, 0}, {1920, 1178}} + {{0, 0}, {1600, 1178}} {10000000000000, 10000000000000} YES - + 256 YES @@ -1777,7 +1873,6 @@ AAMAAAABAAEAAAFTAAMAAAAEAAAFwgAAAAAACAAIAAgACAABAAEAAQABA 260 {{57, 4}, {39, 28}} - YES 604110336 @@ -1799,7 +1894,6 @@ AAMAAAABAAEAAAFTAAMAAAAEAAAFwgAAAAAACAAIAAgACAABAAEAAQABA {322, 454} - NSView @@ -2398,6 +2492,38 @@ AAMAAAABAAEAAAFTAAMAAAAEAAAFwgAAAAAACAAIAAgACAABAAEAAQABA 747 + + + scrollDirSelect + + + + 751 + + + + sdirChanged: + + + + 752 + + + + mouseDirSelect + + + + 756 + + + + mdirChanged: + + + + 757 + @@ -3124,6 +3250,8 @@ AAMAAAABAAEAAAFTAAMAAAAEAAAFwgAAAAAACAAIAAgACAABAAEAAQABA + + @@ -3170,6 +3298,7 @@ AAMAAAABAAEAAAFTAAMAAAAEAAAFwgAAAAAACAAIAAgACAABAAEAAQABA + @@ -3272,6 +3401,39 @@ AAMAAAABAAEAAAFTAAMAAAAEAAAFwgAAAAAACAAIAAgACAABAAEAAQABA + + 748 + + + + + 749 + + + YES + + + + + + 750 + + + + + 754 + + + YES + + + + + + 755 + + + @@ -3401,6 +3563,13 @@ AAMAAAABAAEAAAFTAAMAAAAEAAAFwgAAAAAACAAIAAgACAABAAEAAQABA 744.IBPluginDependency 745.IBNSSegmentedControlInspectorSelectedSegmentMetadataKey 745.IBPluginDependency + 748.IBPluginDependency + 749.IBPluginDependency + 750.IBNSSegmentedControlInspectorSelectedSegmentMetadataKey + 750.IBPluginDependency + 754.IBPluginDependency + 755.IBNSSegmentedControlInspectorSelectedSegmentMetadataKey + 755.IBPluginDependency 81.IBPluginDependency 83.IBPluginDependency 92.IBPluginDependency @@ -3532,6 +3701,13 @@ AAMAAAABAAEAAAFTAAMAAAAEAAAFwgAAAAAACAAIAAgACAABAAEAAQABA com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin @@ -3547,15 +3723,445 @@ AAMAAAABAAEAAAFTAAMAAAAEAAAFwgAAAAAACAAIAAgACAABAAEAAQABA - 747 + 757 + + + + YES + + ApplicationController + NSObject + + toggleActivity: + id + + + toggleActivity: + + toggleActivity: + id + + + + YES + + YES + activeButton + activeMenuItem + configsController + dockMenuBase + drawer + jsController + mainWindow + targetController + + + YES + NSToolbarItem + NSMenuItem + ConfigsController + NSMenu + NSDrawer + JoystickController + NSWindow + TargetController + + + + YES + + YES + activeButton + activeMenuItem + configsController + dockMenuBase + drawer + jsController + mainWindow + targetController + + + YES + + activeButton + NSToolbarItem + + + activeMenuItem + NSMenuItem + + + configsController + ConfigsController + + + dockMenuBase + NSMenu + + + drawer + NSDrawer + + + jsController + JoystickController + + + mainWindow + NSWindow + + + targetController + TargetController + + + + + IBProjectSource + ./Classes/ApplicationController.h + + + + ConfigsController + NSObject + + YES + + YES + addPressed: + removePressed: + + + YES + id + id + + + + YES + + YES + addPressed: + removePressed: + + + YES + + addPressed: + id + + + removePressed: + id + + + + + YES + + YES + removeButton + tableView + targetController + + + YES + NSButton + NSTableView + TargetController + + + + YES + + YES + removeButton + tableView + targetController + + + YES + + removeButton + NSButton + + + tableView + NSTableView + + + targetController + TargetController + + + + + IBProjectSource + ./Classes/ConfigsController.h + + + + JoystickController + NSObject + + YES + + YES + configsController + outlineView + targetController + + + YES + ConfigsController + NSOutlineView + TargetController + + + + YES + + YES + configsController + outlineView + targetController + + + YES + + configsController + ConfigsController + + + outlineView + NSOutlineView + + + targetController + TargetController + + + + + IBProjectSource + ./Classes/JoystickController.h + + + + KeyInputTextView + NSTextView + + YES + + YES + targetController + window + + + YES + TargetController + NSWindow + + + + YES + + YES + targetController + window + + + YES + + targetController + TargetController + + + window + NSWindow + + + + + IBProjectSource + ./Classes/KeyInputTextView.h + + + + TargetController + NSObject + + YES + + YES + configChosen: + mbtnChanged: + mdirChanged: + radioChanged: + sdirChanged: + + + YES + id + id + id + id + id + + + + YES + + YES + configChosen: + mbtnChanged: + mdirChanged: + radioChanged: + sdirChanged: + + + YES + + configChosen: + id + + + mbtnChanged: + id + + + mdirChanged: + id + + + radioChanged: + id + + + sdirChanged: + id + + + + + YES + + YES + configPopup + configsController + joystickController + keyInput + mouseBtnSelect + mouseDirSelect + radioButtons + radioConfig + radioKey + radioNoAction + scrollDirSelect + title + + + YES + NSPopUpButton + ConfigsController + JoystickController + KeyInputTextView + NSSegmentedControl + NSSegmentedControl + NSMatrix + NSButtonCell + NSButtonCell + NSButtonCell + NSSegmentedControl + NSTextField + + + + YES + + YES + configPopup + configsController + joystickController + keyInput + mouseBtnSelect + mouseDirSelect + radioButtons + radioConfig + radioKey + radioNoAction + scrollDirSelect + title + + + YES + + configPopup + NSPopUpButton + + + configsController + ConfigsController + + + joystickController + JoystickController + + + keyInput + KeyInputTextView + + + mouseBtnSelect + NSSegmentedControl + + + mouseDirSelect + NSSegmentedControl + + + radioButtons + NSMatrix + + + radioConfig + NSButtonCell + + + radioKey + NSButtonCell + + + radioNoAction + NSButtonCell + + + scrollDirSelect + NSSegmentedControl + + + title + NSTextField + + + + + IBProjectSource + ./Classes/TargetController.h + + + - 0 IBCocoaFramework com.apple.InterfaceBuilder.CocoaPlugin.macosx + + com.apple.InterfaceBuilder.CocoaPlugin.macosx + + com.apple.InterfaceBuilder.CocoaPlugin.InterfaceBuilder3 diff --git a/Enjoy.xcodeproj/project.pbxproj b/Enjoy.xcodeproj/project.pbxproj index 44084e7..8892e86 100644 --- a/Enjoy.xcodeproj/project.pbxproj +++ b/Enjoy.xcodeproj/project.pbxproj @@ -10,6 +10,8 @@ 1DDD58160DA1D0A300B32029 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 1DDD58140DA1D0A300B32029 /* MainMenu.xib */; }; 8B7E476C15C314A200C588FA /* TargetMouseBtn.m in Sources */ = {isa = PBXBuildFile; fileRef = 8B7E476B15C314A200C588FA /* TargetMouseBtn.m */; }; 8BD9B54315C230FF00929C5D /* TargetMouseMove.m in Sources */ = {isa = PBXBuildFile; fileRef = 8BD9B54215C230FE00929C5D /* TargetMouseMove.m */; }; + 8BEFAD9C15C46BFF00823AEC /* TargetMouseScroll.m in Sources */ = {isa = PBXBuildFile; fileRef = 8BEFAD9B15C46BFF00823AEC /* TargetMouseScroll.m */; }; + 8BEFADA015C476DC00823AEC /* TargetToggleMouseScope.m in Sources */ = {isa = PBXBuildFile; fileRef = 8BEFAD9F15C476DC00823AEC /* TargetToggleMouseScope.m */; }; 8D11072B0486CEB800E47090 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 089C165CFE840E0CC02AAC07 /* InfoPlist.strings */; }; 8D11072D0486CEB800E47090 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 29B97316FDCFA39411CA2CEA /* main.m */; settings = {ATTRIBUTES = (); }; }; 8D11072F0486CEB800E47090 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1058C7A1FEA54F0111CA2CBB /* Cocoa.framework */; }; @@ -47,6 +49,10 @@ 8B7E476B15C314A200C588FA /* TargetMouseBtn.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TargetMouseBtn.m; sourceTree = ""; }; 8BD9B54115C230FE00929C5D /* TargetMouseMove.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TargetMouseMove.h; sourceTree = ""; }; 8BD9B54215C230FE00929C5D /* TargetMouseMove.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TargetMouseMove.m; sourceTree = ""; }; + 8BEFAD9A15C46BFF00823AEC /* TargetMouseScroll.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TargetMouseScroll.h; sourceTree = ""; }; + 8BEFAD9B15C46BFF00823AEC /* TargetMouseScroll.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TargetMouseScroll.m; sourceTree = ""; }; + 8BEFAD9E15C476DC00823AEC /* TargetToggleMouseScope.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TargetToggleMouseScope.h; sourceTree = ""; }; + 8BEFAD9F15C476DC00823AEC /* TargetToggleMouseScope.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TargetToggleMouseScope.m; sourceTree = ""; }; 8D1107310486CEB800E47090 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 8D1107320486CEB800E47090 /* Enjoy.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Enjoy.app; sourceTree = BUILT_PRODUCTS_DIR; }; D549CA4B0FBB441B00BC8203 /* Credits.rtf */ = {isa = PBXFileReference; lastKnownFileType = text.rtf; path = Credits.rtf; sourceTree = ""; }; @@ -136,6 +142,10 @@ 8BD9B54215C230FE00929C5D /* TargetMouseMove.m */, 8B7E476A15C314A200C588FA /* TargetMouseBtn.h */, 8B7E476B15C314A200C588FA /* TargetMouseBtn.m */, + 8BEFAD9A15C46BFF00823AEC /* TargetMouseScroll.h */, + 8BEFAD9B15C46BFF00823AEC /* TargetMouseScroll.m */, + 8BEFAD9E15C476DC00823AEC /* TargetToggleMouseScope.h */, + 8BEFAD9F15C476DC00823AEC /* TargetToggleMouseScope.m */, ); name = Classes; sourceTree = ""; @@ -292,6 +302,8 @@ D5F809710FB093400006A4DE /* TargetConfig.m in Sources */, 8BD9B54315C230FF00929C5D /* TargetMouseMove.m in Sources */, 8B7E476C15C314A200C588FA /* TargetMouseBtn.m in Sources */, + 8BEFAD9C15C46BFF00823AEC /* TargetMouseScroll.m in Sources */, + 8BEFADA015C476DC00823AEC /* TargetToggleMouseScope.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/Enjoy_Prefix.pch b/Enjoy_Prefix.pch index b915d20..0fab501 100644 --- a/Enjoy_Prefix.pch +++ b/Enjoy_Prefix.pch @@ -24,4 +24,6 @@ #import "TargetController.h" #import "TargetKeyboard.h" #import "TargetMouseMove.h" -#import "TargetMouseBtn.h" \ No newline at end of file +#import "TargetMouseBtn.h" +#import "TargetMouseScroll.h" +#import "TargetToggleMouseScope.h" \ No newline at end of file diff --git a/JSActionAnalog.h b/JSActionAnalog.h index 5087582..f1276d5 100644 --- a/JSActionAnalog.h +++ b/JSActionAnalog.h @@ -16,4 +16,7 @@ @property(readwrite) double offset; @property(readwrite) double scale; +- (id) initWithIndex: (int)newIndex; +-(double) getRealValue: (int) value; + @end diff --git a/JSActionAnalog.m b/JSActionAnalog.m index a1c57e8..e351996 100644 --- a/JSActionAnalog.m +++ b/JSActionAnalog.m @@ -30,7 +30,7 @@ //Target* target = [[base->configsController currentConfig] getTargetForAction: [subActions objectAtIndex: 0]]; int raw = IOHIDValueGetIntegerValue(value); - double parsed = offset + scale * raw; + double parsed = [self getRealValue: raw]; if(parsed < -0.3) // fixed?! return [subActions objectAtIndex: 0]; @@ -44,12 +44,17 @@ [[subActions objectAtIndex: 2] setActive: true]; int raw = IOHIDValueGetIntegerValue(value); - double parsed = offset + scale * raw; + double parsed = [self getRealValue: raw]; [[subActions objectAtIndex: 0] setActive: (parsed < -0.3)]; [[subActions objectAtIndex: 1] setActive: (parsed > 0.3)]; } +-(double) getRealValue: (int)value { + double parsed = offset + scale * value; + return parsed; +} + @synthesize offset, scale; diff --git a/JoystickController.h b/JoystickController.h index 4c878f2..aa95815 100644 --- a/JoystickController.h +++ b/JoystickController.h @@ -15,12 +15,17 @@ @interface JoystickController : NSObject { NSMutableArray *joysticks; + NSMutableArray *runningTargets; IOHIDManagerRef hidManager; IBOutlet NSOutlineView* outlineView; IBOutlet TargetController* targetController; IBOutlet ConfigsController* configsController; id selectedAction; BOOL programmaticallySelecting; + BOOL frontWindowOnly; + + @public + NSPoint mouseLoc; } -(void) setup; @@ -28,5 +33,7 @@ @property(readonly) id selectedAction; @property(readonly) NSMutableArray *joysticks; +@property(readonly) NSMutableArray *runningTargets; +@property(readwrite) BOOL frontWindowOnly; @end diff --git a/JoystickController.m b/JoystickController.m index b510848..976b531 100644 --- a/JoystickController.m +++ b/JoystickController.m @@ -5,14 +5,18 @@ // Created by Sam McCall on 4/05/09. // +#import "CoreFoundation/CoreFoundation.h" + @implementation JoystickController -@synthesize joysticks, selectedAction; +@synthesize joysticks, runningTargets, selectedAction, frontWindowOnly; -(id) init { if(self=[super init]) { joysticks = [[NSMutableArray alloc]init]; + runningTargets = [[NSMutableArray alloc]init]; programmaticallySelecting = NO; + mouseLoc.x = mouseLoc.y = 0; } return self; } @@ -40,6 +44,22 @@ static NSMutableDictionary* create_criterion( UInt32 inUsagePage, UInt32 inUsage [outlineView expandItem: handler]; } +BOOL objInArray(NSMutableArray *array, id object) { + for (id o in array) { + if (o == object) + return true; + } + return false; +} + +void timer_callback(CFRunLoopTimerRef timer, void *ctx) { + JoystickController *jc = (JoystickController *)ctx; + jc->mouseLoc = [NSEvent mouseLocation]; + for (Target *target in [jc runningTargets]) { + [target update: jc]; + } +} + void input_callback(void* inContext, IOReturn inResult, void* inSender, IOHIDValueRef value) { JoystickController* self = (JoystickController*)inContext; IOHIDDeviceRef device = IOHIDQueueGetDevice((IOHIDQueueRef) inSender); @@ -61,8 +81,27 @@ void input_callback(void* inContext, IOReturn inResult, void* inSender, IOHIDVal continue; /* target application? doesn't seem to be any need since we are only active when it's in front */ /* might be required for some strange actions */ - [target setRunning: [subaction active]]; - [target setInputValue: IOHIDValueGetIntegerValue(value)]; + if ([target running] != [subaction active]) { + if ([subaction active]) { + [target trigger: self]; + } + else { + [target untrigger: self]; + } + [target setRunning: [subaction active]]; + } + + if ([mainAction isKindOfClass: [JSActionAnalog class]]) { + double realValue = [(JSActionAnalog*)mainAction getRealValue: IOHIDValueGetIntegerValue(value)]; + [target setInputValue: realValue]; + + // Add to list of running targets + if ([target isContinuous] && [target running]) { + if (!objInArray([self runningTargets], target)) { + [[self runningTargets] addObject: target]; + } + } + } } } else if([[NSApplication sharedApplication] isActive] && [[[NSApplication sharedApplication]mainWindow]isVisible]) { // joysticks not active, use it to select stuff @@ -134,11 +173,11 @@ void remove_callback(void* inContext, IOReturn inResult, void* inSender, IOHIDDe create_criterion(kHIDPage_GenericDesktop, kHIDUsage_GD_Joystick), create_criterion(kHIDPage_GenericDesktop, kHIDUsage_GD_GamePad), create_criterion(kHIDPage_GenericDesktop, kHIDUsage_GD_MultiAxisController), - create_criterion(kHIDPage_GenericDesktop, kHIDUsage_GD_Keyboard), + //create_criterion(kHIDPage_GenericDesktop, kHIDUsage_GD_Keyboard), nil]; IOHIDManagerSetDeviceMatchingMultiple(hidManager, (CFArrayRef)criteria); - + IOHIDManagerScheduleWithRunLoop( hidManager, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode ); IOReturn tIOReturn = IOHIDManagerOpen( hidManager, kIOHIDOptionsTypeNone ); (void)tIOReturn; @@ -147,6 +186,17 @@ void remove_callback(void* inContext, IOReturn inResult, void* inSender, IOHIDDe IOHIDManagerRegisterDeviceRemovalCallback(hidManager, remove_callback, (void*) self); // IOHIDManagerRegisterInputValueCallback(hidManager, input_callback, (void*)self); // register individually so we can find the device more easily + + + + // Setup timer for continuous targets + CFRunLoopTimerContext ctx = { + 0, (void*)self, NULL, NULL, NULL + }; + CFRunLoopTimerRef timer = CFRunLoopTimerCreate(kCFAllocatorDefault, + CFAbsoluteTimeGetCurrent(), 1.0/80.0, + 0, 0, timer_callback, &ctx); + CFRunLoopAddTimer(CFRunLoopGetCurrent(), timer, kCFRunLoopDefaultMode); } -(id) determineSelectedAction { diff --git a/README.md b/README.md index daf4ed6..2a7c478 100644 --- a/README.md +++ b/README.md @@ -8,11 +8,21 @@ If you've ever played a video game which only supports mouse and keyboard input, * Key events * Mouse clicks * Mouse movement (for analog sticks) +* Scrolling Enjoy2 supports multiple configurations (for different games or programs) and you can even map joystick buttons to change configurations on-the-fly. Enjoy2 is written by [Yifeng Huang](htty://nongraphical.com) and is based on [Enjoy by Sam McCall](http://abstractable.net/enjoy/). Enjoy is MIT-licensed. +## How to use + +At startup, and when Enjoy2 is paused, press any button or move any analog stick to jump to the configuration for that button or stick. From there, select one of the mapping options from the choices on the right. + +To use an analog axis to move the mouse, select the "Analog" sub-item on the left. + +### Mapping modes + +Enjoy2 offers two mouse mapping modes: global and single-window. Enjoy2 starts in global mode, but you can set any joystick button to the "toggle mouse scope" action, which will change the mode. If you are using Enjoy2 to play a video game, you may find that one or the other mode offers better compatibility with your game's specific requirements. ## Requirements @@ -25,4 +35,6 @@ Version 1.1 * Forked from Enjoy * Mouse movement support -* Mouse button support \ No newline at end of file +* Mouse button support +* Scrollwheel support +* Support for two mouse movement modes diff --git a/Target.h b/Target.h index 215245f..43b7d0c 100644 --- a/Target.h +++ b/Target.h @@ -10,13 +10,16 @@ @interface Target : NSObject { BOOL running; - int inputValue; + BOOL isContinuous; + double inputValue; } @property(readwrite) BOOL running; -@property(readwrite) int inputValue; --(void) trigger; --(void) untrigger; +@property(readonly) BOOL isContinuous; +@property(readwrite) double inputValue; +-(void) trigger: (JoystickController *)jc; +-(void) untrigger: (JoystickController *)jc; +-(void) update: (JoystickController *)jc; -(NSString*) stringify; +(Target*) unstringify: (NSString*) str withConfigList: (NSArray*) configs; diff --git a/Target.m b/Target.m index 35ebfc2..ec2d6a1 100644 --- a/Target.m +++ b/Target.m @@ -19,6 +19,10 @@ return [TargetMouseMove unstringifyImpl:components]; if([typeTag isEqualToString:@"mbtn"]) return [TargetMouseBtn unstringifyImpl:components]; + if([typeTag isEqualToString:@"mscroll"]) + return [TargetMouseScroll unstringifyImpl:components]; + if([typeTag isEqualToString:@"mtoggle"]) + return [TargetToggleMouseScope unstringifyImpl:components]; NSParameterAssert(NO); return NULL; @@ -29,27 +33,22 @@ return NULL; } --(void) trigger { +-(void) trigger: (JoystickController *)jc { [self doesNotRecognizeSelector:_cmd]; } --(void) untrigger { +-(void) untrigger: (JoystickController *)jc { // no-op by default } --(BOOL) running { - return running; +-(void) update: (JoystickController *) jc { + [self doesNotRecognizeSelector:_cmd]; } --(void) setRunning: (BOOL) newRunning { - if(newRunning == running) - return; - if(newRunning) - [self trigger]; - else - [self untrigger]; - running = newRunning; + +-(BOOL) isContinuous { + return false; } -@synthesize inputValue; +@synthesize inputValue, running; @end diff --git a/TargetController.h b/TargetController.h index f68a6da..2d499b7 100644 --- a/TargetController.h +++ b/TargetController.h @@ -18,7 +18,9 @@ IBOutlet KeyInputTextView* keyInput; IBOutlet NSButtonCell *radioNoAction, *radioKey, *radioConfig; IBOutlet NSMatrix* radioButtons; + IBOutlet NSSegmentedControl* mouseDirSelect; IBOutlet NSSegmentedControl* mouseBtnSelect; + IBOutlet NSSegmentedControl* scrollDirSelect; IBOutlet NSTextField* title; IBOutlet NSPopUpButton* configPopup; IBOutlet ConfigsController* configsController; @@ -34,7 +36,9 @@ -(void) refreshConfigsPreservingSelection: (BOOL) preserve; -(IBAction)configChosen:(id)sender; -(IBAction)radioChanged:(id)sender; +-(IBAction)mdirChanged:(id)sender; -(IBAction)mbtnChanged:(id)sender; +-(IBAction)sdirChanged:(id)sender; -(void) focusKey; @property(readwrite) BOOL enabled; diff --git a/TargetController.m b/TargetController.m index 9b075a1..3442a61 100644 --- a/TargetController.m +++ b/TargetController.m @@ -15,7 +15,17 @@ [[[NSApplication sharedApplication] mainWindow] makeFirstResponder: sender]; [self commit]; } +-(IBAction)mdirChanged:(id)sender { + [radioButtons setState: 1 atRow: 3 column: 0]; + [[[NSApplication sharedApplication] mainWindow] makeFirstResponder: sender]; + [self commit]; +} -(IBAction)mbtnChanged:(id)sender { + [radioButtons setState: 1 atRow: 4 column: 0]; + [[[NSApplication sharedApplication] mainWindow] makeFirstResponder: sender]; + [self commit]; +} +-(IBAction)sdirChanged:(id)sender { [radioButtons setState: 1 atRow: 5 column: 0]; [[[NSApplication sharedApplication] mainWindow] makeFirstResponder: sender]; [self commit]; @@ -41,18 +51,12 @@ return c; } case 3: { - // mouse X + // mouse X/Y TargetMouseMove *mm = [[TargetMouseMove alloc] init]; - [mm setDir: 0]; + [mm setDir: [mouseDirSelect selectedSegment]]; return mm; } case 4: { - // mouse Y - TargetMouseMove *mm = [[TargetMouseMove alloc] init]; - [mm setDir: 1]; - return mm; - } - case 5: { // mouse button TargetMouseBtn *mb = [[TargetMouseBtn alloc] init]; if ([mouseBtnSelect selectedSegment] == 0) { @@ -63,6 +67,22 @@ } return mb; } + case 5: { + // scroll + TargetMouseScroll *ms = [[TargetMouseScroll alloc] init]; + if ([scrollDirSelect selectedSegment] == 0) { + [ms setHowMuch: -1]; + } + else { + [ms setHowMuch: 1]; + } + return ms; + } + case 6: { + // toggle mouse scope + TargetToggleMouseScope *tms = [[TargetToggleMouseScope alloc] init]; + return tms; + } } return NULL; } @@ -83,7 +103,9 @@ -(void) reset { [keyInput clear]; [radioButtons setState: 1 atRow: 0 column: 0]; + [mouseDirSelect setSelectedSegment: 0]; [mouseBtnSelect setSelectedSegment: 0]; + [scrollDirSelect setSelectedSegment: 0]; [self refreshConfigsPreservingSelection: NO]; } @@ -91,7 +113,9 @@ [radioButtons setEnabled: enabled]; [keyInput setEnabled: enabled]; [configPopup setEnabled: enabled]; + [mouseDirSelect setEnabled: enabled]; [mouseBtnSelect setEnabled: enabled]; + [scrollDirSelect setEnabled: enabled]; } -(BOOL) enabled { return [radioButtons isEnabled]; @@ -127,17 +151,25 @@ [configPopup selectItemAtIndex: [[configsController configs] indexOfObject: [(TargetConfig*)target config]]]; } else if ([target isKindOfClass: [TargetMouseMove class]]) { - if ([(TargetMouseMove *)target dir] == 0) - [radioButtons setState:1 atRow: 3 column: 0]; - else - [radioButtons setState:1 atRow: 4 column: 0]; + [radioButtons setState:1 atRow: 3 column: 0]; + [mouseDirSelect setSelectedSegment: [(TargetMouseMove *)target dir]]; } else if ([target isKindOfClass: [TargetMouseBtn class]]) { - [radioButtons setState: 1 atRow: 5 column: 0]; + [radioButtons setState: 1 atRow: 4 column: 0]; if ([(TargetMouseBtn *)target which] == kCGMouseButtonLeft) [mouseBtnSelect setSelectedSegment: 0]; else [mouseBtnSelect setSelectedSegment: 1]; + } + else if ([target isKindOfClass: [TargetMouseScroll class]]) { + [radioButtons setState: 1 atRow: 5 column: 0]; + if ([(TargetMouseScroll *)target howMuch] < 0) + [scrollDirSelect setSelectedSegment: 0]; + else + [scrollDirSelect setSelectedSegment: 1]; + } + else if ([target isKindOfClass: [TargetToggleMouseScope class]]) { + [radioButtons setState: 1 atRow: 6 column: 0]; } else { [NSException raise:@"Unknown target subclass" format:@"Unknown target subclass"]; } diff --git a/TargetKeyboard.m b/TargetKeyboard.m index 7bf9f83..40661b8 100644 --- a/TargetKeyboard.m +++ b/TargetKeyboard.m @@ -21,13 +21,13 @@ return target; } --(void) trigger { +-(void) trigger: (JoystickController *)jc { CGEventRef keyDown = CGEventCreateKeyboardEvent(NULL, vk, true); CGEventPost(kCGHIDEventTap, keyDown); CFRelease(keyDown); } --(void) untrigger { +-(void) untrigger: (JoystickController *)jc { CGEventRef keyUp = CGEventCreateKeyboardEvent(NULL, vk, false); CGEventPost(kCGHIDEventTap, keyUp); CFRelease(keyUp); diff --git a/TargetMouseBtn.m b/TargetMouseBtn.m index 857de7b..7429874 100644 --- a/TargetMouseBtn.m +++ b/TargetMouseBtn.m @@ -23,7 +23,7 @@ return target; } --(void) trigger { +-(void) trigger: (JoystickController *)jc { NSRect screenRect = [[NSScreen mainScreen] frame]; NSInteger height = screenRect.size.height; NSPoint mouseLoc = [NSEvent mouseLocation]; @@ -36,7 +36,7 @@ CFRelease(click); } --(void) untrigger { +-(void) untrigger: (JoystickController *)jc { NSRect screenRect = [[NSScreen mainScreen] frame]; NSInteger height = screenRect.size.height; NSPoint mouseLoc = [NSEvent mouseLocation]; diff --git a/TargetMouseMove.m b/TargetMouseMove.m index 2bcbce3..8cf09d2 100644 --- a/TargetMouseMove.m +++ b/TargetMouseMove.m @@ -10,20 +10,8 @@ @implementation TargetMouseMove --(void) setInputValue: (int) newIV { - NSRect screenRect = [[NSScreen mainScreen] frame]; - NSInteger height = screenRect.size.height; - NSPoint mouseLoc = [NSEvent mouseLocation]; - if (dir == 0) - mouseLoc.x += newIV; - else - mouseLoc.y += newIV; - - CGEventRef move = CGEventCreateMouseEvent(NULL, kCGEventMouseMoved, - CGPointMake(mouseLoc.x, height - mouseLoc.y), - kCGMouseButtonLeft); - CGEventPost(kCGHIDEventTap, move); - CFRelease(move); +-(BOOL) isContinuous { + return true; } @synthesize dir; @@ -39,12 +27,52 @@ return target; } --(void) trigger { +-(void) trigger: (JoystickController *)jc { return; } --(void) untrigger { +-(void) untrigger: (JoystickController *)jc { return; } +-(void) update: (JoystickController *)jc { + //printf("Dir %d inputValue %f\n", [self dir], [self inputValue]); + if (fabs([self inputValue]) < 0.01) + return; // dead zone + + NSRect screenRect = [[NSScreen mainScreen] frame]; + NSInteger height = screenRect.size.height; + + // TODO + double speed = 4.0; + if ([jc frontWindowOnly]) + speed = 12.0; + double dx = 0.0, dy = 0.0; + if ([self dir] == 0) + dx = [self inputValue] * speed; + else + dy = [self inputValue] * speed; + NSPoint *mouseLoc = &jc->mouseLoc; + mouseLoc->x += dx; + mouseLoc->y -= dy; + + CGEventRef move = CGEventCreateMouseEvent(NULL, kCGEventMouseMoved, + CGPointMake(mouseLoc->x, height - mouseLoc->y), + 0); + CGEventSetType(move, kCGEventMouseMoved); + CGEventSetIntegerValueField(move, kCGMouseEventDeltaX, dx); + CGEventSetIntegerValueField(move, kCGMouseEventDeltaY, dy); + + if ([jc frontWindowOnly]) { + ProcessSerialNumber psn; + GetFrontProcess(&psn); + CGEventPostToPSN(&psn, move); + } + else { + CGEventPost(kCGHIDEventTap, move); + } + + CFRelease(move); +} + @end diff --git a/TargetMouseScroll.h b/TargetMouseScroll.h new file mode 100644 index 0000000..ddafef4 --- /dev/null +++ b/TargetMouseScroll.h @@ -0,0 +1,19 @@ +// +// TargetMouseScroll.h +// Enjoy +// +// Created by Yifeng Huang on 7/28/12. +// Copyright (c) 2012 __MyCompanyName__. All rights reserved. +// + +#import + +@interface TargetMouseScroll : Target { + int howMuch; +} + +@property(readwrite) int howMuch; + ++(TargetMouseScroll*) unstringifyImpl: (NSArray*) comps; + +@end diff --git a/TargetMouseScroll.m b/TargetMouseScroll.m new file mode 100644 index 0000000..a1cca9c --- /dev/null +++ b/TargetMouseScroll.m @@ -0,0 +1,35 @@ +// +// TargetMouseScroll.m +// Enjoy +// +// Created by Yifeng Huang on 7/28/12. +// Copyright (c) 2012 __MyCompanyName__. All rights reserved. +// + +#import "TargetMouseScroll.h" + +@implementation TargetMouseScroll + +@synthesize howMuch; + +-(NSString*) stringify { + return [[NSString alloc] initWithFormat: @"mscroll~%d", howMuch]; +} + ++(TargetMouseScroll*) unstringifyImpl: (NSArray*) comps { + NSParameterAssert([comps count] == 2); + TargetMouseScroll* target = [[TargetMouseScroll alloc] init]; + [target setHowMuch: [[comps objectAtIndex:1] integerValue]]; + return target; +} + +-(void) trigger: (JoystickController *)jc { + CGEventRef scroll = CGEventCreateScrollWheelEvent(NULL, + kCGScrollEventUnitLine, + 1, + [self howMuch]); + CGEventPost(kCGHIDEventTap, scroll); + CFRelease(scroll); +} + +@end diff --git a/TargetToggleMouseScope.h b/TargetToggleMouseScope.h new file mode 100644 index 0000000..f167b5e --- /dev/null +++ b/TargetToggleMouseScope.h @@ -0,0 +1,15 @@ +// +// TargetToggleMouseScope.h +// Enjoy +// +// Created by Yifeng Huang on 7/28/12. +// Copyright (c) 2012 __MyCompanyName__. All rights reserved. +// + +#import "Target.h" + +@interface TargetToggleMouseScope : Target + ++(TargetToggleMouseScope*) unstringifyImpl: (NSArray*) comps; + +@end diff --git a/TargetToggleMouseScope.m b/TargetToggleMouseScope.m new file mode 100644 index 0000000..55357e5 --- /dev/null +++ b/TargetToggleMouseScope.m @@ -0,0 +1,28 @@ +// +// TargetToggleMouseScope.m +// Enjoy +// +// Created by Yifeng Huang on 7/28/12. +// Copyright (c) 2012 __MyCompanyName__. All rights reserved. +// + +#import "TargetToggleMouseScope.h" + +@implementation TargetToggleMouseScope + +-(NSString*) stringify { + return [[NSString alloc] initWithFormat: @"mtoggle"]; +} + ++(TargetToggleMouseScope*) unstringifyImpl: (NSArray*) comps { + NSParameterAssert([comps count] == 1); + TargetToggleMouseScope* target = [[TargetToggleMouseScope alloc] init]; + return target; +} + +-(void) trigger: (JoystickController *)jc { + [jc setFrontWindowOnly: ![jc frontWindowOnly]]; + printf("Front window only: %d\n", [jc frontWindowOnly]); +} + +@end diff --git a/icon.icns b/icon.icns new file mode 100644 index 0000000000000000000000000000000000000000..d9a900d07a04e029f9f90f07206e77eea061c7c5 GIT binary patch literal 58086 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