├── README.md ├── vibrate.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ └── contents.xcworkspacedata └── xcuserdata │ └── felix.xcuserdatad │ └── xcschemes │ ├── vibrate.xcscheme │ └── xcschememanagement.plist └── vibrate └── main.m /README.md: -------------------------------------------------------------------------------- 1 | # ForceTouchVibrationCLI 2 | A very bad CLI interface to make the MacBook's Force Touch Trackpad vibrate 3 | 4 | It probably shouldn't be used for anything serious, as explained very clearly by Apple [here](https://developer.apple.com/library/content/samplecode/ForceTouchCatalog/Listings/ForceTouchCatalog_MasterViewController_swift.html#//apple_ref/doc/uid/TP40016148-ForceTouchCatalog_MasterViewController_swift-DontLinkElementID_11): 5 | ``` 6 | /* 7 | Use the `NSHapticFeedbackManager` class to perform alignment haptic 8 | feedback on the Force Touch trackpad. 9 | 10 | Note: You can call this even if this Macintosh doesn't have a Force 11 | Touch Trackpad Haptic feedback should be used sparingly. Here we are 12 | performing it when the user aligns the photo to 0 degrees. A more 13 | real world example would be when the user aligns the photo to when 14 | the horizon is level. 15 | 16 | Ideally, the velocity of slider value changes would be considered such 17 | that haptic feedback is only performed when the user is trying to find 18 | the alignment point (aka moving slowly). This is left as an exercise 19 | for the reader. 20 | */ 21 | ``` 22 | 23 | ## Usage 24 | ``` 25 | Vibrate for 0.5 seconds: vibrate -t 0.5 26 | Vibrate for 30 seconds: vibrate --time 30 27 | Vibrate forever: vibrate 28 | Show help: vibrate --help 29 | ``` 30 | -------------------------------------------------------------------------------- /vibrate.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | DC0B525A1DDABC1E0005E47D /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = DC0B52591DDABC1E0005E47D /* main.m */; }; 11 | /* End PBXBuildFile section */ 12 | 13 | /* Begin PBXCopyFilesBuildPhase section */ 14 | DC0B52541DDABC1E0005E47D /* CopyFiles */ = { 15 | isa = PBXCopyFilesBuildPhase; 16 | buildActionMask = 2147483647; 17 | dstPath = /usr/share/man/man1/; 18 | dstSubfolderSpec = 0; 19 | files = ( 20 | ); 21 | runOnlyForDeploymentPostprocessing = 1; 22 | }; 23 | /* End PBXCopyFilesBuildPhase section */ 24 | 25 | /* Begin PBXFileReference section */ 26 | DC0B52561DDABC1E0005E47D /* vibrate */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = vibrate; sourceTree = BUILT_PRODUCTS_DIR; }; 27 | DC0B52591DDABC1E0005E47D /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 28 | /* End PBXFileReference section */ 29 | 30 | /* Begin PBXFrameworksBuildPhase section */ 31 | DC0B52531DDABC1E0005E47D /* Frameworks */ = { 32 | isa = PBXFrameworksBuildPhase; 33 | buildActionMask = 2147483647; 34 | files = ( 35 | ); 36 | runOnlyForDeploymentPostprocessing = 0; 37 | }; 38 | /* End PBXFrameworksBuildPhase section */ 39 | 40 | /* Begin PBXGroup section */ 41 | DC0B524D1DDABC1E0005E47D = { 42 | isa = PBXGroup; 43 | children = ( 44 | DC0B52581DDABC1E0005E47D /* vibrate */, 45 | DC0B52571DDABC1E0005E47D /* Products */, 46 | ); 47 | sourceTree = ""; 48 | }; 49 | DC0B52571DDABC1E0005E47D /* Products */ = { 50 | isa = PBXGroup; 51 | children = ( 52 | DC0B52561DDABC1E0005E47D /* vibrate */, 53 | ); 54 | name = Products; 55 | sourceTree = ""; 56 | }; 57 | DC0B52581DDABC1E0005E47D /* vibrate */ = { 58 | isa = PBXGroup; 59 | children = ( 60 | DC0B52591DDABC1E0005E47D /* main.m */, 61 | ); 62 | path = vibrate; 63 | sourceTree = ""; 64 | }; 65 | /* End PBXGroup section */ 66 | 67 | /* Begin PBXNativeTarget section */ 68 | DC0B52551DDABC1E0005E47D /* vibrate */ = { 69 | isa = PBXNativeTarget; 70 | buildConfigurationList = DC0B525D1DDABC1E0005E47D /* Build configuration list for PBXNativeTarget "vibrate" */; 71 | buildPhases = ( 72 | DC0B52521DDABC1E0005E47D /* Sources */, 73 | DC0B52531DDABC1E0005E47D /* Frameworks */, 74 | DC0B52541DDABC1E0005E47D /* CopyFiles */, 75 | ); 76 | buildRules = ( 77 | ); 78 | dependencies = ( 79 | ); 80 | name = vibrate; 81 | productName = vibrate; 82 | productReference = DC0B52561DDABC1E0005E47D /* vibrate */; 83 | productType = "com.apple.product-type.tool"; 84 | }; 85 | /* End PBXNativeTarget section */ 86 | 87 | /* Begin PBXProject section */ 88 | DC0B524E1DDABC1E0005E47D /* Project object */ = { 89 | isa = PBXProject; 90 | attributes = { 91 | LastUpgradeCheck = 0810; 92 | ORGANIZATIONNAME = "Felix Lapalme"; 93 | TargetAttributes = { 94 | DC0B52551DDABC1E0005E47D = { 95 | CreatedOnToolsVersion = 8.1; 96 | DevelopmentTeam = DVKB9Z8TQ2; 97 | ProvisioningStyle = Automatic; 98 | }; 99 | }; 100 | }; 101 | buildConfigurationList = DC0B52511DDABC1E0005E47D /* Build configuration list for PBXProject "vibrate" */; 102 | compatibilityVersion = "Xcode 3.2"; 103 | developmentRegion = English; 104 | hasScannedForEncodings = 0; 105 | knownRegions = ( 106 | en, 107 | ); 108 | mainGroup = DC0B524D1DDABC1E0005E47D; 109 | productRefGroup = DC0B52571DDABC1E0005E47D /* Products */; 110 | projectDirPath = ""; 111 | projectRoot = ""; 112 | targets = ( 113 | DC0B52551DDABC1E0005E47D /* vibrate */, 114 | ); 115 | }; 116 | /* End PBXProject section */ 117 | 118 | /* Begin PBXSourcesBuildPhase section */ 119 | DC0B52521DDABC1E0005E47D /* Sources */ = { 120 | isa = PBXSourcesBuildPhase; 121 | buildActionMask = 2147483647; 122 | files = ( 123 | DC0B525A1DDABC1E0005E47D /* main.m in Sources */, 124 | ); 125 | runOnlyForDeploymentPostprocessing = 0; 126 | }; 127 | /* End PBXSourcesBuildPhase section */ 128 | 129 | /* Begin XCBuildConfiguration section */ 130 | DC0B525B1DDABC1E0005E47D /* Debug */ = { 131 | isa = XCBuildConfiguration; 132 | buildSettings = { 133 | ALWAYS_SEARCH_USER_PATHS = NO; 134 | CLANG_ANALYZER_NONNULL = YES; 135 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 136 | CLANG_CXX_LIBRARY = "libc++"; 137 | CLANG_ENABLE_MODULES = YES; 138 | CLANG_ENABLE_OBJC_ARC = YES; 139 | CLANG_WARN_BOOL_CONVERSION = YES; 140 | CLANG_WARN_CONSTANT_CONVERSION = YES; 141 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 142 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 143 | CLANG_WARN_EMPTY_BODY = YES; 144 | CLANG_WARN_ENUM_CONVERSION = YES; 145 | CLANG_WARN_INFINITE_RECURSION = YES; 146 | CLANG_WARN_INT_CONVERSION = YES; 147 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 148 | CLANG_WARN_SUSPICIOUS_MOVES = YES; 149 | CLANG_WARN_UNREACHABLE_CODE = YES; 150 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 151 | CODE_SIGN_IDENTITY = "-"; 152 | COPY_PHASE_STRIP = NO; 153 | DEBUG_INFORMATION_FORMAT = dwarf; 154 | ENABLE_STRICT_OBJC_MSGSEND = YES; 155 | ENABLE_TESTABILITY = YES; 156 | GCC_C_LANGUAGE_STANDARD = gnu99; 157 | GCC_DYNAMIC_NO_PIC = NO; 158 | GCC_NO_COMMON_BLOCKS = YES; 159 | GCC_OPTIMIZATION_LEVEL = 0; 160 | GCC_PREPROCESSOR_DEFINITIONS = ( 161 | "DEBUG=1", 162 | "$(inherited)", 163 | ); 164 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 165 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 166 | GCC_WARN_UNDECLARED_SELECTOR = YES; 167 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 168 | GCC_WARN_UNUSED_FUNCTION = YES; 169 | GCC_WARN_UNUSED_VARIABLE = YES; 170 | MACOSX_DEPLOYMENT_TARGET = 10.12; 171 | MTL_ENABLE_DEBUG_INFO = YES; 172 | ONLY_ACTIVE_ARCH = YES; 173 | SDKROOT = macosx; 174 | }; 175 | name = Debug; 176 | }; 177 | DC0B525C1DDABC1E0005E47D /* Release */ = { 178 | isa = XCBuildConfiguration; 179 | buildSettings = { 180 | ALWAYS_SEARCH_USER_PATHS = NO; 181 | CLANG_ANALYZER_NONNULL = YES; 182 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 183 | CLANG_CXX_LIBRARY = "libc++"; 184 | CLANG_ENABLE_MODULES = YES; 185 | CLANG_ENABLE_OBJC_ARC = YES; 186 | CLANG_WARN_BOOL_CONVERSION = YES; 187 | CLANG_WARN_CONSTANT_CONVERSION = YES; 188 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 189 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 190 | CLANG_WARN_EMPTY_BODY = YES; 191 | CLANG_WARN_ENUM_CONVERSION = YES; 192 | CLANG_WARN_INFINITE_RECURSION = YES; 193 | CLANG_WARN_INT_CONVERSION = YES; 194 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 195 | CLANG_WARN_SUSPICIOUS_MOVES = YES; 196 | CLANG_WARN_UNREACHABLE_CODE = YES; 197 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 198 | CODE_SIGN_IDENTITY = "-"; 199 | COPY_PHASE_STRIP = NO; 200 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 201 | ENABLE_NS_ASSERTIONS = NO; 202 | ENABLE_STRICT_OBJC_MSGSEND = YES; 203 | GCC_C_LANGUAGE_STANDARD = gnu99; 204 | GCC_NO_COMMON_BLOCKS = YES; 205 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 206 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 207 | GCC_WARN_UNDECLARED_SELECTOR = YES; 208 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 209 | GCC_WARN_UNUSED_FUNCTION = YES; 210 | GCC_WARN_UNUSED_VARIABLE = YES; 211 | MACOSX_DEPLOYMENT_TARGET = 10.12; 212 | MTL_ENABLE_DEBUG_INFO = NO; 213 | SDKROOT = macosx; 214 | }; 215 | name = Release; 216 | }; 217 | DC0B525E1DDABC1E0005E47D /* Debug */ = { 218 | isa = XCBuildConfiguration; 219 | buildSettings = { 220 | CODE_SIGN_IDENTITY = "Mac Developer"; 221 | COPY_PHASE_STRIP = YES; 222 | DEAD_CODE_STRIPPING = YES; 223 | DEVELOPMENT_TEAM = DVKB9Z8TQ2; 224 | MACOSX_DEPLOYMENT_TARGET = 10.11; 225 | PRODUCT_NAME = "$(TARGET_NAME)"; 226 | }; 227 | name = Debug; 228 | }; 229 | DC0B525F1DDABC1E0005E47D /* Release */ = { 230 | isa = XCBuildConfiguration; 231 | buildSettings = { 232 | CODE_SIGN_IDENTITY = "Mac Developer"; 233 | COPY_PHASE_STRIP = YES; 234 | DEAD_CODE_STRIPPING = YES; 235 | DEVELOPMENT_TEAM = DVKB9Z8TQ2; 236 | MACOSX_DEPLOYMENT_TARGET = 10.11; 237 | PRODUCT_NAME = "$(TARGET_NAME)"; 238 | }; 239 | name = Release; 240 | }; 241 | /* End XCBuildConfiguration section */ 242 | 243 | /* Begin XCConfigurationList section */ 244 | DC0B52511DDABC1E0005E47D /* Build configuration list for PBXProject "vibrate" */ = { 245 | isa = XCConfigurationList; 246 | buildConfigurations = ( 247 | DC0B525B1DDABC1E0005E47D /* Debug */, 248 | DC0B525C1DDABC1E0005E47D /* Release */, 249 | ); 250 | defaultConfigurationIsVisible = 0; 251 | defaultConfigurationName = Release; 252 | }; 253 | DC0B525D1DDABC1E0005E47D /* Build configuration list for PBXNativeTarget "vibrate" */ = { 254 | isa = XCConfigurationList; 255 | buildConfigurations = ( 256 | DC0B525E1DDABC1E0005E47D /* Debug */, 257 | DC0B525F1DDABC1E0005E47D /* Release */, 258 | ); 259 | defaultConfigurationIsVisible = 0; 260 | defaultConfigurationName = Release; 261 | }; 262 | /* End XCConfigurationList section */ 263 | }; 264 | rootObject = DC0B524E1DDABC1E0005E47D /* Project object */; 265 | } 266 | -------------------------------------------------------------------------------- /vibrate.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /vibrate.xcodeproj/xcuserdata/felix.xcuserdatad/xcschemes/vibrate.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 39 | 40 | 41 | 42 | 43 | 44 | 54 | 56 | 62 | 63 | 64 | 65 | 68 | 69 | 70 | 71 | 72 | 73 | 79 | 81 | 87 | 88 | 89 | 90 | 92 | 93 | 96 | 97 | 98 | -------------------------------------------------------------------------------- /vibrate.xcodeproj/xcuserdata/felix.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | vibrate.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | DC0B52551DDABC1E0005E47D 16 | 17 | primary 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /vibrate/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // vibrate 4 | // 5 | // Created by Felix Lapalme on 2016-11-14. 6 | // Copyright © 2016 Felix Lapalme. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | void vibrateForever(); 12 | void vibrateFor(double time); 13 | void printHelp(); 14 | 15 | int main(int argc, const char * argv[]) { 16 | @autoreleasepool { 17 | 18 | if (argc == 1) { 19 | vibrateForever(); 20 | } 21 | 22 | if (argc == 2 && (strcmp(argv[1], "-h") || strcmp(argv[1], "--help"))) { 23 | printHelp(); 24 | } 25 | 26 | if (argc == 3 && (strcmp(argv[1], "-t") || strcmp(argv[1], "--time"))) { 27 | char *stringEnd; 28 | double time = strtod(argv[2], &stringEnd); 29 | if (!strcmp(stringEnd, argv[2])) { 30 | printf("Invalid time specified!\n"); 31 | } else { 32 | vibrateFor(time); 33 | } 34 | } 35 | } 36 | return 0; 37 | } 38 | 39 | void printHelp() { 40 | printf("\nUsage:\n\nVibrate for 0.5 seconds: \tvibrate -t 0.5\nVibrate for 30 seconds: \tvibrate --time 30\nVibrate forever: \t\tvibrate\n"); 41 | } 42 | 43 | void vibrateForever() { 44 | vibrateFor(HUGE); 45 | } 46 | 47 | void vibrateFor(double time) { 48 | NSDate *startDate = [NSDate date]; 49 | 50 | while ([[NSDate new] timeIntervalSinceDate:startDate] < time) { 51 | [[NSHapticFeedbackManager defaultPerformer] performFeedbackPattern:NSHapticFeedbackPatternGeneric performanceTime:NSHapticFeedbackPerformanceTimeNow]; 52 | usleep(5000); 53 | } 54 | 55 | NSLog(@"Finished in %f", [[NSDate new] timeIntervalSinceDate:startDate]); 56 | } 57 | --------------------------------------------------------------------------------