├── .gitignore ├── ActionExecutor.h ├── ActionExecutor.m ├── Actions ├── ActionProtocol.h ├── ClickAction.h ├── ClickAction.m ├── ColorPickerAction.h ├── ColorPickerAction.m ├── DoubleclickAction.h ├── DoubleclickAction.m ├── DragDownAction.h ├── DragDownAction.m ├── DragMoveAction.h ├── DragMoveAction.m ├── DragUpAction.h ├── DragUpAction.m ├── KeyBaseAction.h ├── KeyBaseAction.m ├── KeyDownAction.h ├── KeyDownAction.m ├── KeyDownUpBaseAction.h ├── KeyDownUpBaseAction.m ├── KeyPressAction.h ├── KeyPressAction.m ├── KeyUpAction.h ├── KeyUpAction.m ├── MouseBaseAction.h ├── MouseBaseAction.m ├── MoveAction.h ├── MoveAction.m ├── PrintAction.h ├── PrintAction.m ├── RightClickAction.h ├── RightClickAction.m ├── TripleclickAction.h ├── TripleclickAction.m ├── TypeAction.h ├── TypeAction.m ├── WaitAction.h └── WaitAction.m ├── ExecutionOptions.h ├── KeycodeInformer.h ├── KeycodeInformer.m ├── LICENSE ├── Makefile ├── OutputHandler.h ├── OutputHandler.m ├── README-Characters.md ├── README.markdown ├── Test.sh ├── cliclick.m ├── cliclick.xcodeproj └── project.pbxproj ├── cliclick_Prefix.pch ├── generate-action-classes-macro.sh └── generate-characterinfo.php /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.mode1 3 | *.mode1v3 4 | *.mode2v3 5 | *.perspective 6 | *.perspectivev3 7 | *.pbxuser 8 | *.xcworkspace 9 | *.xcuserdatad 10 | /DerivedData 11 | /build 12 | cliclick 13 | ActionClassesMacro.h 14 | -------------------------------------------------------------------------------- /ActionExecutor.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2007-2021, Carsten Blüm 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * - Redistributions of source code must retain the above copyright notice, this 9 | * list of conditions and the following disclaimer. 10 | * - Redistributions in binary form must reproduce the above copyright notice, this 11 | * list of conditions and the following disclaimer in the documentation and/or 12 | * other materials provided with the distribution. 13 | * - Neither the name of Carsten Blüm nor the names of his contributors may be 14 | * used to endorse or promote products derived from this software without specific 15 | * prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 20 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 21 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 23 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 24 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 25 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | */ 28 | 29 | #import 30 | #import "ActionProtocol.h" 31 | #import "ExecutionOptions.h" 32 | 33 | @interface ActionExecutor : NSObject { 34 | 35 | } 36 | 37 | + (void)executeActions:(NSArray *)actions 38 | withOptions:(struct ExecutionOptions)options; 39 | 40 | + (NSDictionary *)shortcuts; 41 | 42 | + (NSArray *)actionClasses; 43 | 44 | @end 45 | -------------------------------------------------------------------------------- /ActionExecutor.m: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2007-2021, Carsten Blüm 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * - Redistributions of source code must retain the above copyright notice, this 9 | * list of conditions and the following disclaimer. 10 | * - Redistributions in binary form must reproduce the above copyright notice, this 11 | * list of conditions and the following disclaimer in the documentation and/or 12 | * other materials provided with the distribution. 13 | * - Neither the name of Carsten Blüm nor the names of his contributors may be 14 | * used to endorse or promote products derived from this software without specific 15 | * prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 20 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 21 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 23 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 24 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 25 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | */ 28 | 29 | #import "ActionExecutor.h" 30 | #include "ActionClassesMacro.h" 31 | #include "ExecutionOptions.h" 32 | 33 | @implementation ActionExecutor 34 | 35 | + (void)executeActions:(NSArray *)actions 36 | withOptions:(struct ExecutionOptions)options { 37 | 38 | NSDictionary *shortcuts = [self shortcuts]; 39 | 40 | struct timespec waitingtime; 41 | waitingtime.tv_sec = 0; 42 | 43 | if (options.waitTime < 100) { 44 | options.waitTime = 100; 45 | } 46 | 47 | if (options.waitTime > 999) { 48 | waitingtime.tv_sec = (int)floor(options.waitTime / 1000); 49 | waitingtime.tv_nsec = (options.waitTime - waitingtime.tv_sec * 1000) * 1000000; 50 | } else { 51 | waitingtime.tv_sec = 0; 52 | waitingtime.tv_nsec = options.waitTime * 1000000; 53 | } 54 | 55 | NSUInteger i, count = [actions count]; 56 | for (i = 0; i < count; i++) { 57 | NSArray *action = [[actions objectAtIndex:i] componentsSeparatedByString:@":"]; 58 | Class actionClass = [shortcuts objectForKey:[action objectAtIndex:0]]; 59 | if (nil == actionClass) { 60 | if ([[action objectAtIndex:0] isEqualToString:[actions objectAtIndex:i]]) { 61 | [NSException raise:@"InvalidCommandException" 62 | format:@"Unrecognized action shortcut “%@”", [action objectAtIndex:0]]; 63 | } else { 64 | [NSException raise:@"InvalidCommandException" 65 | format:@"Unrecognized action shortcut “%@” in “%@”", [action objectAtIndex:0], [actions objectAtIndex:i]]; 66 | } 67 | } 68 | 69 | id actionClassInstance = [[actionClass alloc] init]; 70 | 71 | if (![actionClassInstance conformsToProtocol:@protocol(ActionProtocol)]) { 72 | [NSException raise:@"InvalidCommandException" 73 | format:@"%@ does not conform to ActionProtocol", actionClass]; 74 | } 75 | 76 | options.isFirstAction = i == 0; 77 | options.isLastAction = i == count - 1; 78 | 79 | if ([action count] > 1) { 80 | [actionClassInstance performActionWithData:[[action subarrayWithRange:NSMakeRange(1, [action count] - 1)] componentsJoinedByString:@":"] 81 | withOptions:options]; 82 | } else { 83 | [actionClassInstance performActionWithData:@"" 84 | withOptions:options]; 85 | } 86 | 87 | [actionClassInstance release]; 88 | 89 | if (!options.isLastAction) { 90 | nanosleep(&waitingtime, NULL); 91 | } 92 | } 93 | } 94 | 95 | + (NSArray *)actionClasses { 96 | NSArray *actionClasses = [NSArray arrayWithObjects:ACTION_CLASSES]; 97 | return actionClasses; 98 | } 99 | 100 | + (NSDictionary *)shortcuts { 101 | 102 | NSArray *actionClasses = [[self class] actionClasses]; 103 | NSMutableDictionary *shortcuts = [NSMutableDictionary dictionaryWithCapacity:[actionClasses count]]; 104 | NSUInteger i, ii; 105 | 106 | for (i = 0, ii = [actionClasses count]; i < ii; i++) { 107 | NSString *classname = [actionClasses objectAtIndex:i]; 108 | Class actionClass = NSClassFromString(classname); 109 | NSString *shortcut = [actionClass commandShortcut]; 110 | if (nil != [shortcuts objectForKey:shortcut]) { 111 | [NSException raise:@"ShortcutConflictException" 112 | format:@"Shortcut “%@” is used by more than one action class", shortcut]; 113 | } 114 | [shortcuts setObject:actionClass forKey:shortcut]; 115 | } 116 | 117 | return [[shortcuts retain] autorelease]; 118 | } 119 | 120 | @end 121 | -------------------------------------------------------------------------------- /Actions/ActionProtocol.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2007-2021, Carsten Blüm 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * - Redistributions of source code must retain the above copyright notice, this 9 | * list of conditions and the following disclaimer. 10 | * - Redistributions in binary form must reproduce the above copyright notice, this 11 | * list of conditions and the following disclaimer in the documentation and/or 12 | * other materials provided with the distribution. 13 | * - Neither the name of Carsten Blüm nor the names of his contributors may be 14 | * used to endorse or promote products derived from this software without specific 15 | * prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 20 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 21 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 23 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 24 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 25 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | */ 28 | 29 | #import 30 | #import "ExecutionOptions.h" 31 | 32 | @protocol ActionProtocol 33 | 34 | /** 35 | * Returns the command's shortcut 36 | * 37 | * The command shortcut is the string which has to be used as command-line argument (typically followed by “:” plus some arguments) to invoke the command. 38 | * 39 | * @note The command shortcut has to be unique for each command. 40 | * @return Command shortcut 41 | */ 42 | + (NSString *)commandShortcut; 43 | 44 | /** 45 | * Returns the command description 46 | * 47 | * The command description is to be included in the help output and is formatted (i.e.: indented). It should include a description as well as at least one usage example. 48 | * 49 | * @return Command description 50 | */ 51 | + (NSString *)commandDescription; 52 | 53 | /** 54 | * Performs the action 55 | * 56 | * Depending on the `mode` argument, this can be the action, printing a description of the action to STDOUT or both. 57 | * 58 | * @param data Part of the argument remaining after stripping the leading command identifier 59 | * @param options 60 | */ 61 | - (void)performActionWithData:(NSString *)data 62 | withOptions:(struct ExecutionOptions)options; 63 | 64 | @end 65 | -------------------------------------------------------------------------------- /Actions/ClickAction.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2007-2021, Carsten Blüm 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * - Redistributions of source code must retain the above copyright notice, this 9 | * list of conditions and the following disclaimer. 10 | * - Redistributions in binary form must reproduce the above copyright notice, this 11 | * list of conditions and the following disclaimer in the documentation and/or 12 | * other materials provided with the distribution. 13 | * - Neither the name of Carsten Blüm nor the names of his contributors may be 14 | * used to endorse or promote products derived from this software without specific 15 | * prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 20 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 21 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 23 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 24 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 25 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | */ 28 | 29 | #import 30 | #import "ActionProtocol.h" 31 | #import "MouseBaseAction.h" 32 | 33 | @interface ClickAction : MouseBaseAction { 34 | 35 | } 36 | 37 | + (NSString *)commandShortcut; 38 | 39 | + (NSString *)commandDescription; 40 | 41 | - (NSString *)actionDescriptionString:(NSString *)locationDescription; 42 | 43 | - (void)performActionAtPoint:(CGPoint) p; 44 | 45 | @end 46 | -------------------------------------------------------------------------------- /Actions/ClickAction.m: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2007-2021, Carsten Blüm 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * - Redistributions of source code must retain the above copyright notice, this 9 | * list of conditions and the following disclaimer. 10 | * - Redistributions in binary form must reproduce the above copyright notice, this 11 | * list of conditions and the following disclaimer in the documentation and/or 12 | * other materials provided with the distribution. 13 | * - Neither the name of Carsten Blüm nor the names of his contributors may be 14 | * used to endorse or promote products derived from this software without specific 15 | * prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 20 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 21 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 23 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 24 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 25 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | */ 28 | 29 | #import "ClickAction.h" 30 | #include 31 | 32 | @implementation ClickAction 33 | 34 | #pragma mark - ActionProtocol 35 | 36 | + (NSString *)commandShortcut { 37 | return @"c"; 38 | } 39 | 40 | + (NSString *)commandDescription { 41 | return @" c:x,y Will CLICK at the point with the given coordinates.\n" 42 | " Example: “c:12,34” will click at the point with x coordinate\n" 43 | " 12 and y coordinate 34. Instead of x and y values, you may\n" 44 | " also use “.”, which means: the current position. Using “.” is\n" 45 | " equivalent to using relative zero values “c:+0,+0”."; 46 | } 47 | 48 | #pragma mark - MouseBaseAction 49 | 50 | - (NSString *)actionDescriptionString:(NSString *)locationDescription { 51 | return [NSString stringWithFormat:@"Click at %@", locationDescription]; 52 | } 53 | 54 | - (void)performActionAtPoint:(CGPoint) p { 55 | // Left button down 56 | CGEventRef leftDown = CGEventCreateMouseEvent(NULL, kCGEventLeftMouseDown, p, kCGMouseButtonLeft); 57 | CGEventPost(kCGHIDEventTap, leftDown); 58 | CFRelease(leftDown); 59 | 60 | usleep(15000); // Improve reliability 61 | 62 | // Left button up 63 | CGEventRef leftUp = CGEventCreateMouseEvent(NULL, kCGEventLeftMouseUp, p, kCGMouseButtonLeft); 64 | CGEventPost(kCGHIDEventTap, leftUp); 65 | CFRelease(leftUp); 66 | } 67 | 68 | @end 69 | -------------------------------------------------------------------------------- /Actions/ColorPickerAction.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2007-2021, Carsten Blüm 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * - Redistributions of source code must retain the above copyright notice, this 9 | * list of conditions and the following disclaimer. 10 | * - Redistributions in binary form must reproduce the above copyright notice, this 11 | * list of conditions and the following disclaimer in the documentation and/or 12 | * other materials provided with the distribution. 13 | * - Neither the name of Carsten Blüm nor the names of his contributors may be 14 | * used to endorse or promote products derived from this software without specific 15 | * prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 20 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 21 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 23 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 24 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 25 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | */ 28 | 29 | #import 30 | #import "ActionProtocol.h" 31 | 32 | @interface ColorPickerAction : NSObject { 33 | 34 | } 35 | 36 | + (NSString *)commandShortcut; 37 | 38 | + (NSString *)commandDescription; 39 | 40 | - (void)performActionWithData:(NSString *)data 41 | withOptions:(struct ExecutionOptions)options; 42 | 43 | @end 44 | -------------------------------------------------------------------------------- /Actions/ColorPickerAction.m: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2007-2021, Carsten Blüm 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * - Redistributions of source code must retain the above copyright notice, this 9 | * list of conditions and the following disclaimer. 10 | * - Redistributions in binary form must reproduce the above copyright notice, this 11 | * list of conditions and the following disclaimer in the documentation and/or 12 | * other materials provided with the distribution. 13 | * - Neither the name of Carsten Blüm nor the names of his contributors may be 14 | * used to endorse or promote products derived from this software without specific 15 | * prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 20 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 21 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 23 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 24 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 25 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | */ 28 | 29 | #import "ColorPickerAction.h" 30 | #import "MouseBaseAction.h" 31 | 32 | @implementation ColorPickerAction 33 | 34 | #pragma mark - ActionProtocol 35 | 36 | + (NSString *)commandShortcut { 37 | return @"cp"; 38 | } 39 | 40 | + (NSString *)commandDescription { 41 | return @" cp:str Will PRINT THE COLOR value at the given screen location.\n" 42 | " The color value is printed as three decimal 8-bit values,\n" 43 | " representing, in order, red, green, and blue.\n" 44 | " Example: “cp:123,456” might print “127 63 0”"; 45 | } 46 | 47 | - (void)performActionWithData:(NSString *)data 48 | withOptions:(struct ExecutionOptions)options { 49 | 50 | NSString *shortcut = [[self class] commandShortcut]; 51 | 52 | if ([data isEqualToString:@""]) { 53 | [NSException raise:@"InvalidCommandException" 54 | format:@"Missing argument to command “%@”: Expected two coordinates (separated by a comma) or “.”. Example: “%@:123,456” or “%@:.”", 55 | shortcut, shortcut, shortcut]; 56 | } else { 57 | NSArray *coords; 58 | 59 | if ([data isEqualToString:@"."]) { 60 | coords = [NSArray arrayWithObjects: @"+0", @"+0", nil]; 61 | } else { 62 | coords = [data componentsSeparatedByString:@","]; 63 | 64 | if ([coords count] != 2 || 65 | [[coords objectAtIndex:0] isEqualToString:@""] || 66 | [[coords objectAtIndex:1] isEqualToString:@""]) 67 | { 68 | [NSException raise:@"InvalidCommandException" 69 | format:@"Invalid argument “%@” to command “%@”: Expected two coordinates (separated by a comma) or “.”. Example: “%@:123,456” or “%@:.”", 70 | data, shortcut, shortcut, shortcut]; 71 | } 72 | } 73 | 74 | if (MODE_TEST == options.mode) { 75 | if ([data isEqualToString:@"."]) { 76 | [options.verbosityOutputHandler write:@"Print color at current mouse position"]; 77 | } else { 78 | [options.verbosityOutputHandler write:[NSString stringWithFormat:@"Print color at location %i,%i\n", [[coords objectAtIndex:0] intValue], [[coords objectAtIndex:1] intValue]]]; 79 | } 80 | } else { 81 | CGPoint p; 82 | p.x = [MouseBaseAction getCoordinate:[coords objectAtIndex:0] forAxis:XAXIS]; 83 | p.y = [MouseBaseAction getCoordinate:[coords objectAtIndex:1] forAxis:YAXIS]; 84 | 85 | CGRect imageRect = CGRectMake(p.x, p.y, 1, 1); 86 | CGImageRef imageRef = CGWindowListCreateImage(imageRect, kCGWindowListOptionOnScreenOnly, kCGNullWindowID, kCGWindowImageDefault); 87 | NSBitmapImageRep *bitmap = [[NSBitmapImageRep alloc] initWithCGImage:imageRef]; 88 | CGImageRelease(imageRef); 89 | NSColor *color = [bitmap colorAtX:0 y:0]; 90 | [bitmap release]; 91 | 92 | [options.commandOutputHandler write:[NSString stringWithFormat:@"%d %d %d\n", (int)(color.redComponent*255), (int)(color.greenComponent*255), (int)(color.blueComponent*255)]]; 93 | } 94 | } 95 | } 96 | 97 | @end 98 | -------------------------------------------------------------------------------- /Actions/DoubleclickAction.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2007-2021, Carsten Blüm 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * - Redistributions of source code must retain the above copyright notice, this 9 | * list of conditions and the following disclaimer. 10 | * - Redistributions in binary form must reproduce the above copyright notice, this 11 | * list of conditions and the following disclaimer in the documentation and/or 12 | * other materials provided with the distribution. 13 | * - Neither the name of Carsten Blüm nor the names of his contributors may be 14 | * used to endorse or promote products derived from this software without specific 15 | * prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 20 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 21 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 23 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 24 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 25 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | */ 28 | 29 | #import 30 | #import "ActionProtocol.h" 31 | #import "MouseBaseAction.h" 32 | 33 | @interface DoubleclickAction : MouseBaseAction { 34 | 35 | } 36 | 37 | + (NSString *)commandShortcut; 38 | 39 | - (NSString *)actionDescriptionString:(NSString *)locationDescription; 40 | 41 | - (void)performActionAtPoint:(CGPoint) p; 42 | 43 | @end 44 | -------------------------------------------------------------------------------- /Actions/DoubleclickAction.m: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2007-2021, Carsten Blüm 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * - Redistributions of source code must retain the above copyright notice, this 9 | * list of conditions and the following disclaimer. 10 | * - Redistributions in binary form must reproduce the above copyright notice, this 11 | * list of conditions and the following disclaimer in the documentation and/or 12 | * other materials provided with the distribution. 13 | * - Neither the name of Carsten Blüm nor the names of his contributors may be 14 | * used to endorse or promote products derived from this software without specific 15 | * prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 20 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 21 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 23 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 24 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 25 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | */ 28 | 29 | #import "DoubleclickAction.h" 30 | #include 31 | 32 | @implementation DoubleclickAction 33 | 34 | #pragma mark - ActionProtocol 35 | 36 | + (NSString *)commandShortcut { 37 | return @"dc"; 38 | } 39 | 40 | + (NSString *)commandDescription { 41 | return @" dc:x,y Will DOUBLE-CLICK at the point with the given coordinates.\n" 42 | " Example: “dc:12,34” will double-click at the point with x\n" 43 | " coordinate 12 and y coordinate 34. Instead of x and y values,\n" 44 | " you may also use “.”, which means: the current position."; 45 | } 46 | 47 | #pragma mark - MouseBaseAction 48 | 49 | - (NSString *)actionDescriptionString:(NSString *)locationDescription { 50 | return [NSString stringWithFormat:@"Double-click at %@", locationDescription]; 51 | } 52 | 53 | - (void)performActionAtPoint:(CGPoint) p { 54 | 55 | // Left button down 56 | CGEventRef mouseEvent = CGEventCreateMouseEvent(NULL, kCGEventLeftMouseDown, p, kCGMouseButtonLeft); 57 | CGEventPost(kCGHIDEventTap, mouseEvent); 58 | 59 | // Left button up 60 | CGEventSetType(mouseEvent, kCGEventLeftMouseUp); 61 | CGEventPost(kCGHIDEventTap, mouseEvent); 62 | 63 | usleep(200000); // Improve reliability 64 | 65 | // 2nd click 66 | CGEventSetIntegerValueField(mouseEvent, kCGMouseEventClickState, 2); 67 | 68 | CGEventSetType(mouseEvent, kCGEventLeftMouseDown); 69 | CGEventPost(kCGHIDEventTap, mouseEvent); 70 | 71 | CGEventSetType(mouseEvent, kCGEventLeftMouseUp); 72 | CGEventPost(kCGHIDEventTap, mouseEvent); 73 | 74 | CFRelease(mouseEvent); 75 | } 76 | 77 | @end 78 | -------------------------------------------------------------------------------- /Actions/DragDownAction.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2007-2021, Carsten Blüm 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * - Redistributions of source code must retain the above copyright notice, this 9 | * list of conditions and the following disclaimer. 10 | * - Redistributions in binary form must reproduce the above copyright notice, this 11 | * list of conditions and the following disclaimer in the documentation and/or 12 | * other materials provided with the distribution. 13 | * - Neither the name of Carsten Blüm nor the names of his contributors may be 14 | * used to endorse or promote products derived from this software without specific 15 | * prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 20 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 21 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 23 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 24 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 25 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | */ 28 | 29 | #import 30 | #import "ActionProtocol.h" 31 | #import "MouseBaseAction.h" 32 | 33 | @interface DragDownAction : MouseBaseAction { 34 | 35 | } 36 | 37 | + (NSString *)commandShortcut; 38 | 39 | + (NSString *)commandDescription; 40 | 41 | - (NSString *)actionDescriptionString:(NSString *)locationDescription; 42 | 43 | - (void)performActionAtPoint:(CGPoint) p; 44 | 45 | @end 46 | -------------------------------------------------------------------------------- /Actions/DragDownAction.m: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2007-2021, Carsten Blüm 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * - Redistributions of source code must retain the above copyright notice, this 9 | * list of conditions and the following disclaimer. 10 | * - Redistributions in binary form must reproduce the above copyright notice, this 11 | * list of conditions and the following disclaimer in the documentation and/or 12 | * other materials provided with the distribution. 13 | * - Neither the name of Carsten Blüm nor the names of his contributors may be 14 | * used to endorse or promote products derived from this software without specific 15 | * prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 20 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 21 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 23 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 24 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 25 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | */ 28 | 29 | #import "DragDownAction.h" 30 | 31 | @implementation DragDownAction 32 | 33 | #pragma mark - ActionProtocol 34 | 35 | + (NSString *)commandShortcut { 36 | return @"dd"; 37 | } 38 | 39 | + (NSString *)commandDescription { 40 | return @" dd:x,y Will press down to START A DRAG at the given coordinates.\n" 41 | " Example: “dd:12,34” will press down at the point with x\n" 42 | " coordinate 12 and y coordinate 34. Instead of x and y values,\n" 43 | " you may also use “.”, which means: the current position."; 44 | } 45 | 46 | #pragma mark - MouseBaseAction 47 | 48 | - (NSString *)actionDescriptionString:(NSString *)locationDescription { 49 | return [NSString stringWithFormat:@"Drag press down at %@", locationDescription]; 50 | } 51 | 52 | - (void)performActionAtPoint:(CGPoint) p { 53 | // Left button down, but don't release 54 | CGEventRef leftDown = CGEventCreateMouseEvent(NULL, kCGEventLeftMouseDown, p, kCGMouseButtonLeft); 55 | CGEventPost(kCGHIDEventTap, leftDown); 56 | CFRelease(leftDown); 57 | } 58 | 59 | @end 60 | -------------------------------------------------------------------------------- /Actions/DragMoveAction.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2007-2021, Carsten Blüm 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * - Redistributions of source code must retain the above copyright notice, this 9 | * list of conditions and the following disclaimer. 10 | * - Redistributions in binary form must reproduce the above copyright notice, this 11 | * list of conditions and the following disclaimer in the documentation and/or 12 | * other materials provided with the distribution. 13 | * - Neither the name of Carsten Blüm nor the names of his contributors may be 14 | * used to endorse or promote products derived from this software without specific 15 | * prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 20 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 21 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 23 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 24 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 25 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | */ 28 | 29 | #import 30 | #import "ActionProtocol.h" 31 | #import "MouseBaseAction.h" 32 | 33 | @interface DragMoveAction : MouseBaseAction { 34 | 35 | } 36 | 37 | + (NSString *)commandShortcut; 38 | 39 | + (NSString *)commandDescription; 40 | 41 | - (NSString *)actionDescriptionString:(NSString *)locationDescription; 42 | 43 | - (void)performActionAtPoint:(CGPoint) p; 44 | 45 | @end 46 | -------------------------------------------------------------------------------- /Actions/DragMoveAction.m: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2007-2021, Carsten Blüm 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * - Redistributions of source code must retain the above copyright notice, this 9 | * list of conditions and the following disclaimer. 10 | * - Redistributions in binary form must reproduce the above copyright notice, this 11 | * list of conditions and the following disclaimer in the documentation and/or 12 | * other materials provided with the distribution. 13 | * - Neither the name of Carsten Blüm nor the names of his contributors may be 14 | * used to endorse or promote products derived from this software without specific 15 | * prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 20 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 21 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 23 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 24 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 25 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | */ 28 | 29 | #import "DragMoveAction.h" 30 | #include 31 | 32 | @implementation DragMoveAction 33 | 34 | #pragma mark - ActionProtocol 35 | 36 | + (NSString *)commandShortcut { 37 | return @"dm"; 38 | } 39 | 40 | + (NSString *)commandDescription { 41 | return @" dm:x,y Will continue the DRAG event to the given coordinates.\n" 42 | " Example: “dm:112,134” will drag and continue to the point with x\n" 43 | " coordinate 112 and y coordinate 134."; 44 | } 45 | 46 | #pragma mark - MouseBaseAction 47 | 48 | - (NSString *)actionDescriptionString:(NSString *)locationDescription { 49 | return [NSString stringWithFormat:@"Drag move to %@", locationDescription]; 50 | } 51 | 52 | - (void)performActionAtPoint:(CGPoint) p { 53 | } 54 | 55 | - (uint32_t)getMoveEventConstant { 56 | return kCGEventLeftMouseDragged; 57 | } 58 | 59 | @end 60 | -------------------------------------------------------------------------------- /Actions/DragUpAction.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2007-2021, Carsten Blüm 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * - Redistributions of source code must retain the above copyright notice, this 9 | * list of conditions and the following disclaimer. 10 | * - Redistributions in binary form must reproduce the above copyright notice, this 11 | * list of conditions and the following disclaimer in the documentation and/or 12 | * other materials provided with the distribution. 13 | * - Neither the name of Carsten Blüm nor the names of his contributors may be 14 | * used to endorse or promote products derived from this software without specific 15 | * prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 20 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 21 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 23 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 24 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 25 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | */ 28 | 29 | #import 30 | #import "ActionProtocol.h" 31 | #import "MouseBaseAction.h" 32 | 33 | @interface DragUpAction : MouseBaseAction { 34 | 35 | } 36 | 37 | + (NSString *)commandShortcut; 38 | 39 | + (NSString *)commandDescription; 40 | 41 | - (NSString *)actionDescriptionString:(NSString *)locationDescription; 42 | 43 | - (void)performActionAtPoint:(CGPoint) p; 44 | 45 | @end 46 | -------------------------------------------------------------------------------- /Actions/DragUpAction.m: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2007-2021, Carsten Blüm 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * - Redistributions of source code must retain the above copyright notice, this 9 | * list of conditions and the following disclaimer. 10 | * - Redistributions in binary form must reproduce the above copyright notice, this 11 | * list of conditions and the following disclaimer in the documentation and/or 12 | * other materials provided with the distribution. 13 | * - Neither the name of Carsten Blüm nor the names of his contributors may be 14 | * used to endorse or promote products derived from this software without specific 15 | * prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 20 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 21 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 23 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 24 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 25 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | */ 28 | 29 | #import "DragUpAction.h" 30 | #include 31 | 32 | @implementation DragUpAction 33 | 34 | #pragma mark - ActionProtocol 35 | 36 | + (NSString *)commandShortcut { 37 | return @"du"; 38 | } 39 | 40 | + (NSString *)commandDescription { 41 | return @" du:x,y Will release to END A DRAG at the given coordinates.\n" 42 | " Example: “du:112,134” will release at the point with x\n" 43 | " coordinate 112 and y coordinate 134."; 44 | } 45 | 46 | #pragma mark - MouseBaseAction 47 | 48 | - (NSString *)actionDescriptionString:(NSString *)locationDescription { 49 | return [NSString stringWithFormat:@"Drag release at %@", locationDescription]; 50 | } 51 | 52 | - (void)performActionAtPoint:(CGPoint) p { 53 | // Left button up 54 | CGEventRef leftUp = CGEventCreateMouseEvent(NULL, kCGEventLeftMouseUp, p, kCGMouseButtonLeft); 55 | CGEventPost(kCGHIDEventTap, leftUp); 56 | CFRelease(leftUp); 57 | } 58 | 59 | - (uint32_t)getMoveEventConstant { 60 | return kCGEventLeftMouseDragged; 61 | } 62 | 63 | @end 64 | -------------------------------------------------------------------------------- /Actions/KeyBaseAction.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2007-2021, Carsten Blüm 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * - Redistributions of source code must retain the above copyright notice, this 9 | * list of conditions and the following disclaimer. 10 | * - Redistributions in binary form must reproduce the above copyright notice, this 11 | * list of conditions and the following disclaimer in the documentation and/or 12 | * other materials provided with the distribution. 13 | * - Neither the name of Carsten Blüm nor the names of his contributors may be 14 | * used to endorse or promote products derived from this software without specific 15 | * prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 20 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 21 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 23 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 24 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 25 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | */ 28 | 29 | #import 30 | #import "ActionProtocol.h" 31 | #import "ExecutionOptions.h" 32 | 33 | @interface KeyBaseAction : NSObject { 34 | 35 | } 36 | 37 | /** 38 | * Returns the keys that are supported by the command. 39 | * 40 | * @return An NSDictionary which has keyboard key name as dictionary keys and keyboard key codes (strings) as dictionary values. 41 | */ 42 | + (NSDictionary *)getSupportedKeycodes; 43 | 44 | /** 45 | * Returns the list of keys supported by the command 46 | * 47 | * @param indent String to use as indentation string at the beginning of each line 48 | * 49 | * @return Newline-separated string 50 | */ 51 | + (NSString *)getSupportedKeysIndentedWith:(NSString *)indent; 52 | 53 | /** 54 | * Returns a string describing the action performed be the command 55 | * 56 | * @param keyName Name of the key 57 | * @return Human-readable phrase such as @@"Press blahblah key" 58 | * @note This method must be overwritten by subclasses 59 | */ 60 | - (NSString *)actionDescriptionString:(NSString *)keyName; 61 | 62 | /** 63 | * Performs the command's action 64 | * 65 | * @param code The key code 66 | * 67 | * @note This method must be overwritten by subclasses 68 | */ 69 | - (void)performActionWithKeycode:(CGKeyCode)code; 70 | 71 | #pragma mark - ActionProtocol 72 | 73 | /** 74 | * Performs the action 75 | * 76 | * Depending on the `mode` argument, this can be the action, printing a description of the action to STDOUT or both. 77 | * 78 | * @param data Part of the argument remaining after stripping the leading command identifier 79 | * @param options 80 | */ 81 | - (void)performActionWithData:(NSString *)data 82 | withOptions:(struct ExecutionOptions)options; 83 | 84 | @end 85 | -------------------------------------------------------------------------------- /Actions/KeyBaseAction.m: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2007-2021, Carsten Blüm 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * - Redistributions of source code must retain the above copyright notice, this 9 | * list of conditions and the following disclaimer. 10 | * - Redistributions in binary form must reproduce the above copyright notice, this 11 | * list of conditions and the following disclaimer in the documentation and/or 12 | * other materials provided with the distribution. 13 | * - Neither the name of Carsten Blüm nor the names of his contributors may be 14 | * used to endorse or promote products derived from this software without specific 15 | * prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 20 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 21 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 23 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 24 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 25 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | */ 28 | 29 | #import "KeyBaseAction.h" 30 | #import "ExecutionOptions.h" 31 | 32 | @implementation KeyBaseAction 33 | 34 | + (NSDictionary *)getSupportedKeycodes { 35 | [NSException raise:@"InvalidCommandException" 36 | format:@"To be implemented by subclasses"]; 37 | return [NSDictionary dictionaryWithObject:@"Will never be reached, but makes Xcode happy" forKey:@"Foo"]; 38 | } 39 | 40 | + (NSString *)getSupportedKeysIndentedWith:(NSString *)indent { 41 | 42 | NSArray *sortedkeyNames = [[[[self class] getSupportedKeycodes] allKeys] sortedArrayUsingComparator:^(id obj1, id obj2) { 43 | return [obj1 compare:obj2 options:NSNumericSearch]; 44 | }]; 45 | 46 | return [NSString stringWithFormat:@"%@%@", indent, [sortedkeyNames componentsJoinedByString:[@"\n" stringByAppendingString:indent]]]; 47 | } 48 | 49 | - (NSString *)actionDescriptionString:(NSString *)keyName { 50 | [NSException raise:@"InvalidCommandException" 51 | format:@"To be implemented by subclasses"]; 52 | return @"Will never be reached, but makes Xcode happy"; 53 | } 54 | 55 | - (void)performActionWithKeycode:(CGKeyCode)code { 56 | [NSException raise:@"InvalidCommandException" 57 | format:@"To be implemented by subclasses"]; 58 | } 59 | 60 | #pragma mark - ActionProtocol 61 | 62 | - (void)performActionWithData:(NSString *)data 63 | withOptions:(struct ExecutionOptions)options { 64 | 65 | NSString *shortcut = [[self class] commandShortcut]; 66 | 67 | // Wait before executing the key event(s). If this is the very first action, use a longer 68 | // delay (as it could be observed that an initial keyboard was swallowed, cf. issue #39), 69 | // otherwise only a short delay. 70 | struct timespec waitingtime; 71 | waitingtime.tv_sec = 0; 72 | waitingtime.tv_nsec = (options.isFirstAction ? 65 : 20) * 1000000; // Milliseconds 73 | nanosleep(&waitingtime, NULL); 74 | 75 | if ([data isEqualToString:@""]) { 76 | [NSException raise:@"InvalidCommandException" 77 | format:@"Missing argument to command “%@”: Expected one or more keys (separated by a comma). Examples: “%@:ctrl” or “%@:cmd,alt”", 78 | shortcut, shortcut, shortcut]; 79 | } 80 | 81 | NSDictionary *keycodes = [[self class] getSupportedKeycodes]; 82 | NSArray *keys = [data componentsSeparatedByString:@","]; 83 | NSUInteger i, count = [keys count]; 84 | 85 | // First, validate the key names 86 | for (i = 0; i < count; i++) { 87 | NSObject *keyname = [keys objectAtIndex:i]; 88 | if (![keycodes objectForKey:keyname]) { 89 | [NSException raise:@"InvalidCommandException" 90 | format:@"Invalid key “%@” given as argument to command “%@”.\nThe key name may only be one of:\n%@", 91 | keyname, shortcut, [[self class] getSupportedKeysIndentedWith:@" - "]]; 92 | } 93 | } 94 | 95 | // Then, perform whatever action is requested 96 | for (i = 0; i < count; i++) { 97 | unsigned code = [[keycodes objectForKey:[keys objectAtIndex:i]] intValue]; 98 | 99 | if (i > 0) { 100 | // If non-first key, wait a little, as otherwise, the event will be swallowed 101 | waitingtime.tv_sec = 0; 102 | waitingtime.tv_nsec = 20 * 1000000; // Milliseconds 103 | nanosleep(&waitingtime, NULL); 104 | } 105 | 106 | if (MODE_REGULAR != options.mode) { 107 | [options.verbosityOutputHandler write:[self actionDescriptionString:[keys objectAtIndex:i]]]; 108 | } 109 | 110 | if (MODE_TEST != options.mode) { 111 | [self performActionWithKeycode:(CGKeyCode)code]; 112 | } 113 | } 114 | } 115 | 116 | @end 117 | -------------------------------------------------------------------------------- /Actions/KeyDownAction.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2007-2021, Carsten Blüm 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * - Redistributions of source code must retain the above copyright notice, this 9 | * list of conditions and the following disclaimer. 10 | * - Redistributions in binary form must reproduce the above copyright notice, this 11 | * list of conditions and the following disclaimer in the documentation and/or 12 | * other materials provided with the distribution. 13 | * - Neither the name of Carsten Blüm nor the names of his contributors may be 14 | * used to endorse or promote products derived from this software without specific 15 | * prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 20 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 21 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 23 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 24 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 25 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | */ 28 | 29 | #import 30 | #import "ActionProtocol.h" 31 | #import "KeyDownUpBaseAction.h" 32 | 33 | @interface KeyDownAction : KeyDownUpBaseAction { 34 | 35 | } 36 | 37 | @end 38 | -------------------------------------------------------------------------------- /Actions/KeyDownAction.m: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2007-2021, Carsten Blüm 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * - Redistributions of source code must retain the above copyright notice, this 9 | * list of conditions and the following disclaimer. 10 | * - Redistributions in binary form must reproduce the above copyright notice, this 11 | * list of conditions and the following disclaimer in the documentation and/or 12 | * other materials provided with the distribution. 13 | * - Neither the name of Carsten Blüm nor the names of his contributors may be 14 | * used to endorse or promote products derived from this software without specific 15 | * prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 20 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 21 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 23 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 24 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 25 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | */ 28 | 29 | #import "KeyDownAction.h" 30 | 31 | @implementation KeyDownAction 32 | 33 | #pragma mark - ActionProtocol 34 | 35 | + (NSString *)commandShortcut { 36 | return @"kd"; 37 | } 38 | 39 | + (NSString *)commandDescription { 40 | NSString *keyList = [[self class] getSupportedKeysIndentedWith:@" - "]; 41 | NSString *format = @" kd:keys Will trigger a KEY DOWN event for a comma-separated list of\n" 42 | " modifier keys. Possible keys are:\n%@\n" 43 | " Example: “kd:cmd,alt” will press the command key and the\n" 44 | " option key (and will keep them down until you release them\n" 45 | " with another command)"; 46 | return [NSString stringWithFormat:format, keyList]; 47 | } 48 | 49 | #pragma mark - KeyBaseAction 50 | 51 | - (void)performActionWithKeycode:(CGKeyCode)code { 52 | CGEventRef e = CGEventCreateKeyboardEvent(NULL, code, true); 53 | CGEventPost(kCGSessionEventTap, e); 54 | CFRelease(e); 55 | } 56 | 57 | - (NSString *)actionDescriptionString:(NSString *)keyName { 58 | return [NSString stringWithFormat:@"Hold %@ key down", keyName]; 59 | } 60 | 61 | @end 62 | -------------------------------------------------------------------------------- /Actions/KeyDownUpBaseAction.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2007-2021, Carsten Blüm 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * - Redistributions of source code must retain the above copyright notice, this 9 | * list of conditions and the following disclaimer. 10 | * - Redistributions in binary form must reproduce the above copyright notice, this 11 | * list of conditions and the following disclaimer in the documentation and/or 12 | * other materials provided with the distribution. 13 | * - Neither the name of Carsten Blüm nor the names of his contributors may be 14 | * used to endorse or promote products derived from this software without specific 15 | * prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 20 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 21 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 23 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 24 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 25 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | */ 28 | 29 | #import "KeyBaseAction.h" 30 | 31 | @interface KeyDownUpBaseAction : KeyBaseAction 32 | 33 | @end 34 | -------------------------------------------------------------------------------- /Actions/KeyDownUpBaseAction.m: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2007-2021, Carsten Blüm 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * - Redistributions of source code must retain the above copyright notice, this 9 | * list of conditions and the following disclaimer. 10 | * - Redistributions in binary form must reproduce the above copyright notice, this 11 | * list of conditions and the following disclaimer in the documentation and/or 12 | * other materials provided with the distribution. 13 | * - Neither the name of Carsten Blüm nor the names of his contributors may be 14 | * used to endorse or promote products derived from this software without specific 15 | * prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 20 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 21 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 23 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 24 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 25 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | */ 28 | 29 | #import "KeyDownUpBaseAction.h" 30 | 31 | @implementation KeyDownUpBaseAction 32 | 33 | #pragma mark - KeyBaseAction 34 | 35 | + (NSDictionary *)getSupportedKeycodes { 36 | return [NSDictionary dictionaryWithObjectsAndKeys: 37 | @"59", @"ctrl", 38 | @"55", @"cmd", 39 | @"58", @"alt", 40 | @"56", @"shift", 41 | @"63", @"fn", 42 | nil]; 43 | } 44 | 45 | @end 46 | -------------------------------------------------------------------------------- /Actions/KeyPressAction.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2007-2021, Carsten Blüm 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * - Redistributions of source code must retain the above copyright notice, this 9 | * list of conditions and the following disclaimer. 10 | * - Redistributions in binary form must reproduce the above copyright notice, this 11 | * list of conditions and the following disclaimer in the documentation and/or 12 | * other materials provided with the distribution. 13 | * - Neither the name of Carsten Blüm nor the names of his contributors may be 14 | * used to endorse or promote products derived from this software without specific 15 | * prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 20 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 21 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 23 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 24 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 25 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | */ 28 | 29 | #import 30 | #import "ActionProtocol.h" 31 | #import "KeyBaseAction.h" 32 | #import 33 | 34 | @interface KeyPressAction : KeyBaseAction { 35 | 36 | } 37 | 38 | @end 39 | -------------------------------------------------------------------------------- /Actions/KeyPressAction.m: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2007-2021, Carsten Blüm 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * - Redistributions of source code must retain the above copyright notice, this 9 | * list of conditions and the following disclaimer. 10 | * - Redistributions in binary form must reproduce the above copyright notice, this 11 | * list of conditions and the following disclaimer in the documentation and/or 12 | * other materials provided with the distribution. 13 | * - Neither the name of Carsten Blüm nor the names of his contributors may be 14 | * used to endorse or promote products derived from this software without specific 15 | * prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 20 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 21 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 23 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 24 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 25 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | */ 28 | 29 | #import "KeyPressAction.h" 30 | 31 | @implementation KeyPressAction 32 | 33 | #pragma mark - ActionProtocol 34 | 35 | + (NSString *)commandShortcut { 36 | return @"kp"; 37 | } 38 | 39 | + (NSString *)commandDescription { 40 | NSString *keyList = [[self class] getSupportedKeysIndentedWith:@" - "]; 41 | NSString *format = @" kp:key Will emulate PRESSING A KEY (key down + key up). Possible keys are:\n%@\n" 42 | " Example: “kp:return” will hit the return key."; 43 | return [NSString stringWithFormat:format, keyList]; 44 | } 45 | 46 | #pragma mark - KeyBaseAction 47 | 48 | + (NSDictionary *)getSupportedKeycodes { 49 | return [NSDictionary dictionaryWithObjectsAndKeys: 50 | // See /System/Library/Frameworks/Carbon.framework/Versions/A/Frameworks/HIToolbox.framework/Versions/A/Headers/Events.h 51 | @"36", @"return", 52 | @"76", @"enter", 53 | @"53", @"esc", 54 | @"48", @"tab", 55 | @"49", @"space", 56 | @"51", @"delete", 57 | @"117", @"fwd-delete", 58 | @"122", @"f1", 59 | @"120", @"f2", 60 | @"99", @"f3", 61 | @"118", @"f4", 62 | @"96", @"f5", 63 | @"97", @"f6", 64 | @"98", @"f7", 65 | @"100", @"f8", 66 | @"101", @"f9", 67 | @"109", @"f10", 68 | @"103", @"f11", 69 | @"111", @"f12", 70 | @"105", @"f13", 71 | @"107", @"f14", 72 | @"113", @"f15", 73 | @"106", @"f16", 74 | @"126", @"arrow-up", 75 | @"125", @"arrow-down", 76 | @"123", @"arrow-left", 77 | @"124", @"arrow-right", 78 | @"115", @"home", 79 | @"119", @"end", 80 | @"116", @"page-up", 81 | @"121", @"page-down", 82 | // The following keys are also from Events.h, but are hardware-dependent (= represent physical 83 | // keys, what is probably wanted when triggering numpad keys) 84 | @"82", @"num-0", 85 | @"83", @"num-1", 86 | @"84", @"num-2", 87 | @"85", @"num-3", 88 | @"86", @"num-4", 89 | @"87", @"num-5", 90 | @"88", @"num-6", 91 | @"89", @"num-7", 92 | @"91", @"num-8", 93 | @"92", @"num-9", 94 | @"71", @"num-clear", 95 | @"81", @"num-equals", 96 | @"75", @"num-divide", 97 | @"67", @"num-multiply", 98 | @"78", @"num-minus", 99 | @"69", @"num-plus", 100 | @"76", @"num-enter", 101 | 102 | // "NSSystemDefined" events, see list in IOKit/hidsystem/ev_keymap.h 103 | [NSString stringWithFormat:@"%i", NX_KEYTYPE_MUTE], @"mute", 104 | [NSString stringWithFormat:@"%i", NX_KEYTYPE_SOUND_UP], @"volume-up", 105 | [NSString stringWithFormat:@"%i", NX_KEYTYPE_SOUND_DOWN], @"volume-down", 106 | [NSString stringWithFormat:@"%i", NX_KEYTYPE_BRIGHTNESS_UP], @"brightness-up", 107 | [NSString stringWithFormat:@"%i", NX_KEYTYPE_BRIGHTNESS_DOWN], @"brightness-down", 108 | [NSString stringWithFormat:@"%i", NX_KEYTYPE_PLAY], @"play-pause", 109 | [NSString stringWithFormat:@"%i", NX_KEYTYPE_PREVIOUS], @"play-previous", 110 | [NSString stringWithFormat:@"%i", NX_KEYTYPE_NEXT], @"play-next", 111 | [NSString stringWithFormat:@"%i", NX_KEYTYPE_ILLUMINATION_TOGGLE], @"keys-light-toggle", 112 | [NSString stringWithFormat:@"%i", NX_KEYTYPE_ILLUMINATION_UP], @"keys-light-up", 113 | [NSString stringWithFormat:@"%i", NX_KEYTYPE_ILLUMINATION_DOWN], @"keys-light-down", 114 | nil]; 115 | } 116 | 117 | - (BOOL)keyCodeRequiresSystemDefinedEvent:(CGKeyCode)code { 118 | return code == NX_KEYTYPE_SOUND_UP || 119 | code == NX_KEYTYPE_SOUND_DOWN || 120 | code == NX_KEYTYPE_MUTE || 121 | code == NX_KEYTYPE_PLAY || 122 | code == NX_KEYTYPE_BRIGHTNESS_UP || 123 | code == NX_KEYTYPE_BRIGHTNESS_DOWN || 124 | code == NX_KEYTYPE_PLAY || 125 | code == NX_KEYTYPE_PREVIOUS || 126 | code == NX_KEYTYPE_NEXT || 127 | code == NX_KEYTYPE_ILLUMINATION_UP || 128 | code == NX_KEYTYPE_ILLUMINATION_DOWN || 129 | code == NX_KEYTYPE_ILLUMINATION_TOGGLE 130 | ; 131 | } 132 | 133 | - (void)performActionWithKeycode:(CGKeyCode)code { 134 | 135 | if ([self keyCodeRequiresSystemDefinedEvent:code]) { 136 | NSEvent *e1 = [NSEvent otherEventWithType:NSSystemDefined 137 | location:NSZeroPoint 138 | modifierFlags:0xa00 139 | timestamp:0 140 | windowNumber:0 141 | context:0 142 | subtype:8 143 | data1:((code << 16) | (0xa << 8)) 144 | data2:-1]; 145 | CGEventPost(0, [e1 CGEvent]); 146 | 147 | NSEvent *e2 = [NSEvent otherEventWithType:NSSystemDefined 148 | location:NSZeroPoint 149 | modifierFlags:0xb00 150 | timestamp:0 151 | windowNumber:0 152 | context:0 153 | subtype:8 154 | data1:((code << 16) | (0xb << 8)) 155 | data2:-1]; 156 | 157 | CGEventPost(0, [e2 CGEvent]); 158 | } else { 159 | CGEventRef e1 = CGEventCreateKeyboardEvent(NULL, code, true); 160 | CGEventRef e2 = CGEventCreateKeyboardEvent(NULL, code, false); 161 | CGEventPost(kCGSessionEventTap, e1); 162 | CGEventPost(kCGSessionEventTap, e2); 163 | CFRelease(e1); 164 | CFRelease(e2); 165 | } 166 | } 167 | 168 | - (NSString *)actionDescriptionString:(NSString *)keyName { 169 | return [NSString stringWithFormat:@"Press + release %@ key", keyName]; 170 | } 171 | 172 | @end 173 | -------------------------------------------------------------------------------- /Actions/KeyUpAction.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2007-2021, Carsten Blüm 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * - Redistributions of source code must retain the above copyright notice, this 9 | * list of conditions and the following disclaimer. 10 | * - Redistributions in binary form must reproduce the above copyright notice, this 11 | * list of conditions and the following disclaimer in the documentation and/or 12 | * other materials provided with the distribution. 13 | * - Neither the name of Carsten Blüm nor the names of his contributors may be 14 | * used to endorse or promote products derived from this software without specific 15 | * prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 20 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 21 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 23 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 24 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 25 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | */ 28 | 29 | #import 30 | #import "ActionProtocol.h" 31 | #import "KeyDownUpBaseAction.h" 32 | 33 | @interface KeyUpAction : KeyDownUpBaseAction { 34 | 35 | } 36 | 37 | @end 38 | -------------------------------------------------------------------------------- /Actions/KeyUpAction.m: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2007-2021, Carsten Blüm 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * - Redistributions of source code must retain the above copyright notice, this 9 | * list of conditions and the following disclaimer. 10 | * - Redistributions in binary form must reproduce the above copyright notice, this 11 | * list of conditions and the following disclaimer in the documentation and/or 12 | * other materials provided with the distribution. 13 | * - Neither the name of Carsten Blüm nor the names of his contributors may be 14 | * used to endorse or promote products derived from this software without specific 15 | * prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 20 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 21 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 23 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 24 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 25 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | */ 28 | 29 | #import "KeyUpAction.h" 30 | 31 | @implementation KeyUpAction 32 | 33 | #pragma mark - ActionProtocol 34 | 35 | + (NSString *)commandShortcut { 36 | return @"ku"; 37 | } 38 | 39 | + (NSString *)commandDescription { 40 | NSString *keyList = [[self class] getSupportedKeysIndentedWith:@" - "]; 41 | NSString *format = @" ku:keys Will trigger a KEY UP event for a comma-separated list of\n" 42 | " modifier keys. Possible keys are:\n%@\n" 43 | " Example: “ku:cmd,ctrl” will release the command key and the\n" 44 | " control key (which will only have an effect if you performed\n" 45 | " a “key down” before)"; 46 | return [NSString stringWithFormat:format, keyList]; 47 | } 48 | 49 | #pragma mark - KeyBaseAction 50 | 51 | - (void)performActionWithKeycode:(CGKeyCode)code { 52 | CGEventRef e = CGEventCreateKeyboardEvent(NULL, code, false); 53 | CGEventPost(kCGSessionEventTap, e); 54 | CFRelease(e); 55 | } 56 | 57 | - (NSString *)actionDescriptionString:(NSString *)keyName { 58 | return [NSString stringWithFormat:@"Release %@ key", keyName]; 59 | } 60 | 61 | @end 62 | -------------------------------------------------------------------------------- /Actions/MouseBaseAction.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2007-2021, Carsten Blüm 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * - Redistributions of source code must retain the above copyright notice, this 9 | * list of conditions and the following disclaimer. 10 | * - Redistributions in binary form must reproduce the above copyright notice, this 11 | * list of conditions and the following disclaimer in the documentation and/or 12 | * other materials provided with the distribution. 13 | * - Neither the name of Carsten Blüm nor the names of his contributors may be 14 | * used to endorse or promote products derived from this software without specific 15 | * prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 20 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 21 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 23 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 24 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 25 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | */ 28 | 29 | #import 30 | #import "ActionProtocol.h" 31 | 32 | typedef enum { 33 | XAXIS = 1, 34 | YAXIS = 2 35 | } CLICLICKAXIS; 36 | 37 | @interface MouseBaseAction : NSObject { 38 | 39 | } 40 | 41 | /** 42 | * Takes an unparsed position string for a single axis and returns the corresponding position 43 | * 44 | * @param unparsedValue String in one of the supported formats, such as @@"934", @@"+17" or @@"=218" 45 | * @param axis The axis 46 | */ 47 | + (int)getCoordinate:(NSString *)unparsedValue 48 | forAxis:(CLICLICKAXIS)axis; 49 | 50 | /** 51 | * Checks if the given string is an acceptable X or Y coordinate value and throws an exception if not 52 | * 53 | * @param string String to test. See +getCoordinate:forAxis: for supported syntaxes 54 | * @param axis The axis 55 | */ 56 | + (void)validateAxisValue:(NSString *)string 57 | forAxis:(CLICLICKAXIS)axis; 58 | 59 | /** 60 | * Returns a human-readable description of the action 61 | * 62 | * This should be a one-line string which will be used in “verbose” and in “test” mode. 63 | * 64 | * @param locationDescription A textual representation of the coordinates at which the action is performed. 65 | */ 66 | - (NSString *)actionDescriptionString:(NSString *)locationDescription; 67 | 68 | /** 69 | * Performs the mouse-related action an inheriting command provides 70 | * 71 | * This method is called as last step of method performActionWithData:withOptions: It should only perform the action, not print a description when in MODE_VERBOSE mode, as this is done by performActionWithData:withOptions: 72 | * 73 | * @note This method will only be invoked when in MODE_REGULAR or MODE_VERBOSE mode. 74 | */ 75 | - (void)performActionAtPoint:(CGPoint)p; 76 | 77 | /** 78 | * Performs the action 79 | * 80 | * Depending on the mode argument, this can be the action, printing a description of the action to STDOUT or both. This implementation performs the preparatory steps such as validating arguments, calculating the mouse position etc., but leaves performing the action to subclasses, whose performActionAtPoint: method it eventually invokes. 81 | * 82 | * @param data Part of the argument remaining after stripping the leading command identifier 83 | * @param options 84 | */ 85 | - (void)performActionWithData:(NSString *)data 86 | withOptions:(struct ExecutionOptions)options; 87 | 88 | - (void)postHumanizedMouseEventsWithEasingFactor:(unsigned)easing 89 | toX:(float)endX 90 | toY:(float)endY; 91 | 92 | - (uint32_t)getMoveEventConstant; 93 | 94 | @end 95 | -------------------------------------------------------------------------------- /Actions/MouseBaseAction.m: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2007-2021, Carsten Blüm 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * - Redistributions of source code must retain the above copyright notice, this 9 | * list of conditions and the following disclaimer. 10 | * - Redistributions in binary form must reproduce the above copyright notice, this 11 | * list of conditions and the following disclaimer in the documentation and/or 12 | * other materials provided with the distribution. 13 | * - Neither the name of Carsten Blüm nor the names of his contributors may be 14 | * used to endorse or promote products derived from this software without specific 15 | * prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 20 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 21 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 23 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 24 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 25 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | */ 28 | 29 | #import "MouseBaseAction.h" 30 | #include 31 | #include 32 | #include 33 | 34 | @implementation MouseBaseAction 35 | 36 | + (int)getCoordinate:(NSString *)unparsedValue 37 | forAxis:(CLICLICKAXIS)axis { 38 | 39 | [[self class] validateAxisValue:unparsedValue forAxis:axis]; 40 | 41 | if ([[unparsedValue substringToIndex:1] isEqualToString:@"+"] || 42 | [[unparsedValue substringToIndex:1] isEqualToString:@"-"]) { 43 | // Relative value 44 | CGEventRef dummyEvent = CGEventCreate(NULL); 45 | CGPoint ourLoc = CGEventGetLocation(dummyEvent); 46 | int positionDiff = [unparsedValue intValue]; 47 | int currentPosition = axis == XAXIS ? (int)ourLoc.x : (int)ourLoc.y; 48 | CFRelease(dummyEvent); 49 | 50 | return (int) currentPosition + positionDiff; 51 | } 52 | 53 | if ([[unparsedValue substringToIndex:1] isEqualToString:@"="]) { 54 | // Forced absolute value 55 | return [[unparsedValue substringFromIndex:1] intValue]; 56 | } 57 | 58 | // Else. Absolute value 59 | return [unparsedValue intValue]; 60 | } 61 | 62 | + (void)validateAxisValue:(NSString *)string 63 | forAxis:(CLICLICKAXIS)axis { 64 | NSString *regex = @"^=?[+-]?\\d+$"; 65 | NSPredicate *predicate = [NSPredicate predicateWithFormat:@"SELF MATCHES %@", regex]; 66 | if ([predicate evaluateWithObject:string] != YES) { 67 | [NSException raise:@"InvalidCommandException" 68 | format:@"Invalid %@ axis coordinate “%@” given", XAXIS == axis ? @"X" : @"Y", string]; 69 | } 70 | } 71 | 72 | - (NSString *)actionDescriptionString:(NSString *)locationDescription { 73 | [NSException raise:@"InvalidCommandException" 74 | format:@"To be implemented by subclasses"]; 75 | return @"Will never be reached, but makes Xcode happy ;-)"; 76 | } 77 | 78 | - (void)performActionAtPoint:(CGPoint)p { 79 | [NSException raise:@"InvalidCommandException" 80 | format:@"To be implemented by subclasses"]; 81 | } 82 | 83 | #pragma mark - ActionProtocol 84 | 85 | - (void)performActionWithData:(NSString *)data 86 | withOptions:(struct ExecutionOptions)options { 87 | 88 | CGPoint p; 89 | NSString *shortcut = [[self class] commandShortcut]; 90 | NSString *verboseLoc; 91 | 92 | CGEventRef ourEvent = CGEventCreate(NULL); 93 | CGPoint currentLocation = CGEventGetLocation(ourEvent); 94 | CFRelease(ourEvent); 95 | 96 | if ([data isEqualToString:@""]) { 97 | [NSException raise:@"InvalidCommandException" 98 | format:@"Missing argument to command “%@”: Expected two coordinates (separated by a comma) or “.”. Examples: “%@:123,456” or “%@:.”", 99 | shortcut, shortcut, shortcut]; 100 | } else if ([data isEqualToString:@"."]) { 101 | // Use current location 102 | p.x = (int)currentLocation.x; 103 | p.y = (int)currentLocation.y; 104 | verboseLoc = @"current location"; 105 | } else { 106 | NSArray *coords = [data componentsSeparatedByString:@","]; 107 | 108 | if ([coords count] != 2 || 109 | [[coords objectAtIndex:0] isEqualToString:@""] || 110 | [[coords objectAtIndex:1] isEqualToString:@""]) 111 | { 112 | [NSException raise:@"InvalidCommandException" 113 | format:@"Invalid argument “%@” to command “%@”: Expected two coordinates (separated by a comma) or “.”. Examples: “%@:123,456” or “%@:.”", 114 | data, shortcut, shortcut, shortcut]; 115 | } 116 | 117 | p.x = [[self class] getCoordinate:[coords objectAtIndex:0] forAxis:XAXIS]; 118 | p.y = [[self class] getCoordinate:[coords objectAtIndex:1] forAxis:YAXIS]; 119 | 120 | verboseLoc = [NSString stringWithFormat:@"%@,%@", [coords objectAtIndex:0], [coords objectAtIndex:1]]; 121 | } 122 | 123 | if (MODE_REGULAR != options.mode) { 124 | [options.verbosityOutputHandler write:[self actionDescriptionString:verboseLoc]]; 125 | } 126 | 127 | if (MODE_TEST == options.mode) { 128 | return; 129 | } 130 | 131 | if (options.easing) { 132 | // Eased move 133 | [self postHumanizedMouseEventsWithEasingFactor:options.easing 134 | toX:(float)p.x 135 | toY:(float)p.y]; 136 | } else { 137 | // Move 138 | CGEventRef move = CGEventCreateMouseEvent(NULL, [self getMoveEventConstant], p, kCGMouseButtonLeft); // kCGMouseButtonLeft is ignored 139 | CGEventPost(kCGHIDEventTap, move); 140 | CFRelease(move); 141 | } 142 | 143 | [self performActionAtPoint:p]; 144 | } 145 | 146 | - (uint32_t)getMoveEventConstant { 147 | return kCGEventMouseMoved; 148 | } 149 | 150 | - (void)postHumanizedMouseEventsWithEasingFactor:(unsigned)easing 151 | toX:(float)endX 152 | toY:(float)endY { 153 | 154 | CGEventRef ourEvent = CGEventCreate(NULL); 155 | CGPoint currentLocation = CGEventGetLocation(ourEvent); 156 | CFRelease(ourEvent); 157 | uint32_t eventConstant = [self getMoveEventConstant]; 158 | float startX = currentLocation.x; 159 | float startY = currentLocation.y; 160 | float distance = [self distanceBetweenPoint:NSPointFromCGPoint(currentLocation) andPoint:NSMakePoint(endX, endY)]; 161 | 162 | unsigned steps = ((int)(distance * easing / 100)) + 1; 163 | float xDiff = (endX - startX); 164 | float yDiff = (endY - startY); 165 | float stepSize = (float)1.0 / (float)steps; 166 | 167 | unsigned i; 168 | for (i = 0; i < steps; i ++) { 169 | float factor = [self cubicEaseInOut:(stepSize * i)]; 170 | CGEventRef eventRef = CGEventCreateMouseEvent(NULL, eventConstant, CGPointMake(startX + (factor * xDiff), startY + (factor * yDiff)), 0); 171 | CGEventPost(kCGHIDEventTap, eventRef); 172 | CFRelease(eventRef); 173 | usleep(220); 174 | } 175 | } 176 | 177 | - (float) distanceBetweenPoint:(NSPoint)a andPoint:(NSPoint)b { 178 | float dX = a.x - b.x, 179 | dY = a.y - b.y; 180 | return sqrtf(dX * dX + dY * dY); 181 | } 182 | 183 | // Modeled after the piecewise cubic 184 | // y = (1/2)((2x)^3) ; [0, 0.5] 185 | // y = (1/2)((2x-2)^3 + 2) ; [0.5, 1] 186 | // 187 | // Source: AHEasing, License: WTFPL 188 | // 189 | // Expects [whatever action] to be split up into small steps represented 190 | // by a float from 0 (start) to 1 (end). Method is to be called with the float 191 | // and returns an "eased float" for it. 192 | - (float)cubicEaseInOut:(float)p { 193 | if (p < 0.5) { 194 | return 4 * p * p * p; 195 | } else { 196 | float f = ((2 * p) - 2); 197 | return (float)0.5 * f * f * f + 1; 198 | } 199 | } 200 | 201 | @end 202 | -------------------------------------------------------------------------------- /Actions/MoveAction.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2007-2021, Carsten Blüm 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * - Redistributions of source code must retain the above copyright notice, this 9 | * list of conditions and the following disclaimer. 10 | * - Redistributions in binary form must reproduce the above copyright notice, this 11 | * list of conditions and the following disclaimer in the documentation and/or 12 | * other materials provided with the distribution. 13 | * - Neither the name of Carsten Blüm nor the names of his contributors may be 14 | * used to endorse or promote products derived from this software without specific 15 | * prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 20 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 21 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 23 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 24 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 25 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | */ 28 | 29 | #import 30 | #import "MouseBaseAction.h" 31 | #import "ActionProtocol.h" 32 | 33 | @interface MoveAction : MouseBaseAction { 34 | 35 | } 36 | 37 | + (NSString *)commandShortcut; 38 | 39 | + (NSString *)commandDescription; 40 | 41 | - (NSString *)actionDescriptionString:(NSString *)locationDescription; 42 | 43 | - (void)performActionAtPoint:(CGPoint) p; 44 | 45 | @end 46 | -------------------------------------------------------------------------------- /Actions/MoveAction.m: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2007-2021, Carsten Blüm 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * - Redistributions of source code must retain the above copyright notice, this 9 | * list of conditions and the following disclaimer. 10 | * - Redistributions in binary form must reproduce the above copyright notice, this 11 | * list of conditions and the following disclaimer in the documentation and/or 12 | * other materials provided with the distribution. 13 | * - Neither the name of Carsten Blüm nor the names of his contributors may be 14 | * used to endorse or promote products derived from this software without specific 15 | * prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 20 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 21 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 23 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 24 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 25 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | */ 28 | 29 | #import "MoveAction.h" 30 | 31 | @implementation MoveAction 32 | 33 | #pragma mark - ActionProtocol 34 | 35 | + (NSString *)commandShortcut { 36 | return @"m"; 37 | } 38 | 39 | + (NSString *)commandDescription { 40 | return @" m:x,y Will MOVE the mouse to the point with the given coordinates.\n" 41 | " Example: “m:12,34” will move the mouse to the point with\n" 42 | " x coordinate 12 and y coordinate 34."; 43 | } 44 | 45 | #pragma mark - MouseBaseAction 46 | 47 | - (NSString *)actionDescriptionString:(NSString *)locationDescription { 48 | return [NSString stringWithFormat:@"Move to %@", locationDescription]; 49 | } 50 | 51 | - (void)performActionAtPoint:(CGPoint) p { 52 | // Simply does nothing. Moving is done by the parent. 53 | } 54 | 55 | @end 56 | -------------------------------------------------------------------------------- /Actions/PrintAction.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2007-2021, Carsten Blüm 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * - Redistributions of source code must retain the above copyright notice, this 9 | * list of conditions and the following disclaimer. 10 | * - Redistributions in binary form must reproduce the above copyright notice, this 11 | * list of conditions and the following disclaimer in the documentation and/or 12 | * other materials provided with the distribution. 13 | * - Neither the name of Carsten Blüm nor the names of his contributors may be 14 | * used to endorse or promote products derived from this software without specific 15 | * prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 20 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 21 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 23 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 24 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 25 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | */ 28 | 29 | #import 30 | #import "ActionProtocol.h" 31 | 32 | @interface PrintAction : NSObject { 33 | 34 | } 35 | 36 | + (NSString *)commandShortcut; 37 | 38 | + (NSString *)commandDescription; 39 | 40 | - (void)performActionWithData:(NSString *)data 41 | withOptions:(struct ExecutionOptions)options; 42 | 43 | @end 44 | -------------------------------------------------------------------------------- /Actions/PrintAction.m: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2007-2021, Carsten Blüm 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * - Redistributions of source code must retain the above copyright notice, this 9 | * list of conditions and the following disclaimer. 10 | * - Redistributions in binary form must reproduce the above copyright notice, this 11 | * list of conditions and the following disclaimer in the documentation and/or 12 | * other materials provided with the distribution. 13 | * - Neither the name of Carsten Blüm nor the names of his contributors may be 14 | * used to endorse or promote products derived from this software without specific 15 | * prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 20 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 21 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 23 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 24 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 25 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | */ 28 | 29 | #import "PrintAction.h" 30 | 31 | @implementation PrintAction 32 | 33 | #pragma mark - ActionProtocol 34 | 35 | + (NSString *)commandShortcut { 36 | return @"p"; 37 | } 38 | 39 | + (NSString *)commandDescription { 40 | return @" p[:str] Will PRINT the given string. If the string is “.”, the current\n" 41 | " MOUSE POSITION is printed. As a convenience, you can skip the\n" 42 | " string completely and just write “p” to get the current position.\n" 43 | " Example: “p:.” or “p” will print the current mouse position\n" 44 | " Example: “p:'Hello world'” will print “Hello world”"; 45 | } 46 | 47 | - (void)performActionWithData:(NSString *)data 48 | withOptions:(struct ExecutionOptions)options { 49 | 50 | if ([data isEqualToString:@""] || 51 | [data isEqualToString:@"."]) { 52 | if (MODE_TEST == options.mode) { 53 | [options.verbosityOutputHandler write:@"Print the current mouse position"]; 54 | } else { 55 | CGEventRef ourEvent = CGEventCreate(NULL); 56 | CGPoint ourLoc = CGEventGetLocation(ourEvent); 57 | NSPoint point = NSPointFromCGPoint(ourLoc); 58 | [options.commandOutputHandler write:[NSString stringWithFormat: @"%.0f,%.0f", point.x, point.y]]; 59 | CFRelease(ourEvent); 60 | } 61 | return; 62 | } 63 | 64 | if (MODE_TEST == options.mode) { 65 | [options.verbosityOutputHandler write:[NSString stringWithFormat: @"Print message “%@”", data]]; 66 | } else { 67 | [options.commandOutputHandler write:data]; 68 | } 69 | } 70 | 71 | @end 72 | -------------------------------------------------------------------------------- /Actions/RightClickAction.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2007-2021, Carsten Blüm 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * - Redistributions of source code must retain the above copyright notice, this 9 | * list of conditions and the following disclaimer. 10 | * - Redistributions in binary form must reproduce the above copyright notice, this 11 | * list of conditions and the following disclaimer in the documentation and/or 12 | * other materials provided with the distribution. 13 | * - Neither the name of Carsten Blüm nor the names of his contributors may be 14 | * used to endorse or promote products derived from this software without specific 15 | * prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 20 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 21 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 23 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 24 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 25 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | */ 28 | 29 | #import 30 | #import "ActionProtocol.h" 31 | #import "MouseBaseAction.h" 32 | 33 | @interface RightClickAction : MouseBaseAction { 34 | 35 | } 36 | 37 | + (NSString *)commandShortcut; 38 | 39 | + (NSString *)commandDescription; 40 | 41 | - (NSString *)actionDescriptionString:(NSString *)locationDescription; 42 | 43 | - (void)performActionAtPoint:(CGPoint) p; 44 | 45 | @end 46 | -------------------------------------------------------------------------------- /Actions/RightClickAction.m: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2007-2021, Carsten Blüm 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * - Redistributions of source code must retain the above copyright notice, this 9 | * list of conditions and the following disclaimer. 10 | * - Redistributions in binary form must reproduce the above copyright notice, this 11 | * list of conditions and the following disclaimer in the documentation and/or 12 | * other materials provided with the distribution. 13 | * - Neither the name of Carsten Blüm nor the names of his contributors may be 14 | * used to endorse or promote products derived from this software without specific 15 | * prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 20 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 21 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 23 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 24 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 25 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | */ 28 | 29 | #import "RightClickAction.h" 30 | #include 31 | 32 | @implementation RightClickAction 33 | 34 | #pragma mark - ActionProtocol 35 | 36 | + (NSString *)commandShortcut { 37 | return @"rc"; 38 | } 39 | 40 | + (NSString *)commandDescription { 41 | return @" rc:x,y Will RIGHT-CLICK at the point with the given coordinates.\n" 42 | " Example: “rc:12,34” will right-click at the point with x coordinate\n" 43 | " 12 and y coordinate 34. Instead of x and y values, you may\n" 44 | " also use “.”, which means: the current position. Using “.” is\n" 45 | " equivalent to using relative zero values “c:+0,+0”."; 46 | } 47 | 48 | #pragma mark - MouseBaseAction 49 | 50 | - (NSString *)actionDescriptionString:(NSString *)locationDescription { 51 | return [NSString stringWithFormat:@"Right-click at %@", locationDescription]; 52 | } 53 | 54 | - (void)performActionAtPoint:(CGPoint) p { 55 | // Right button down 56 | CGEventRef rightDown = CGEventCreateMouseEvent(NULL, kCGEventRightMouseDown, p, kCGMouseButtonRight); 57 | CGEventPost(kCGHIDEventTap, rightDown); 58 | CFRelease(rightDown); 59 | 60 | usleep(15000); // Improve reliability 61 | 62 | // Right button up 63 | CGEventRef rightUp = CGEventCreateMouseEvent(NULL, kCGEventRightMouseUp, p, kCGMouseButtonRight); 64 | CGEventPost(kCGHIDEventTap, rightUp); 65 | CFRelease(rightUp); 66 | } 67 | 68 | @end 69 | -------------------------------------------------------------------------------- /Actions/TripleclickAction.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2007-2021, Carsten Blüm 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * - Redistributions of source code must retain the above copyright notice, this 9 | * list of conditions and the following disclaimer. 10 | * - Redistributions in binary form must reproduce the above copyright notice, this 11 | * list of conditions and the following disclaimer in the documentation and/or 12 | * other materials provided with the distribution. 13 | * - Neither the name of Carsten Blüm nor the names of his contributors may be 14 | * used to endorse or promote products derived from this software without specific 15 | * prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 20 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 21 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 23 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 24 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 25 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | */ 28 | 29 | #import 30 | #import "ActionProtocol.h" 31 | #import "MouseBaseAction.h" 32 | 33 | @interface TripleclickAction : MouseBaseAction { 34 | 35 | } 36 | 37 | + (NSString *)commandShortcut; 38 | 39 | - (NSString *)actionDescriptionString:(NSString *)locationDescription; 40 | 41 | - (void)performActionAtPoint:(CGPoint) p; 42 | 43 | @end 44 | -------------------------------------------------------------------------------- /Actions/TripleclickAction.m: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2007-2021, Carsten Blüm 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * - Redistributions of source code must retain the above copyright notice, this 9 | * list of conditions and the following disclaimer. 10 | * - Redistributions in binary form must reproduce the above copyright notice, this 11 | * list of conditions and the following disclaimer in the documentation and/or 12 | * other materials provided with the distribution. 13 | * - Neither the name of Carsten Blüm nor the names of his contributors may be 14 | * used to endorse or promote products derived from this software without specific 15 | * prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 20 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 21 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 23 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 24 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 25 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | */ 28 | 29 | #import "TripleclickAction.h" 30 | #include 31 | 32 | @implementation TripleclickAction 33 | 34 | #pragma mark - ActionProtocol 35 | 36 | + (NSString *)commandShortcut { 37 | return @"tc"; 38 | } 39 | 40 | + (NSString *)commandDescription { 41 | return @" tc:x,y Will TRIPLE-CLICK at the point with the given coordinates.\n" 42 | " Example: “tc:12,34” will triple-click at the point with x\n" 43 | " coordinate 12 and y coordinate 34. Instead of x and y values,\n" 44 | " you may also use “.”, which means: the current position.\n" 45 | " Note: If you find that this does not work in a target application,\n" 46 | " please try if double-clicking plus single-clicking does."; 47 | } 48 | 49 | #pragma mark - MouseBaseAction 50 | 51 | - (NSString *)actionDescriptionString:(NSString *)locationDescription { 52 | return [NSString stringWithFormat:@"Triple-click at %@", locationDescription]; 53 | } 54 | 55 | - (void)performActionAtPoint:(CGPoint) p { 56 | 57 | // Left button down 58 | CGEventRef mouseEvent = CGEventCreateMouseEvent(NULL, kCGEventLeftMouseDown, p, kCGMouseButtonLeft); 59 | CGEventPost(kCGHIDEventTap, mouseEvent); 60 | 61 | // Left button up 62 | CGEventSetType(mouseEvent, kCGEventLeftMouseUp); 63 | CGEventPost(kCGHIDEventTap, mouseEvent); 64 | 65 | usleep(200000); // Improve reliability 66 | 67 | // 2nd/3rd click 68 | CGEventSetIntegerValueField(mouseEvent, kCGMouseEventClickState, 3); 69 | 70 | CGEventSetType(mouseEvent, kCGEventLeftMouseDown); 71 | CGEventPost(kCGHIDEventTap, mouseEvent); 72 | 73 | CGEventSetType(mouseEvent, kCGEventLeftMouseUp); 74 | CGEventPost(kCGHIDEventTap, mouseEvent); 75 | 76 | CFRelease(mouseEvent); 77 | } 78 | 79 | @end 80 | -------------------------------------------------------------------------------- /Actions/TypeAction.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2007-2021, Carsten Blüm 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * - Redistributions of source code must retain the above copyright notice, this 9 | * list of conditions and the following disclaimer. 10 | * - Redistributions in binary form must reproduce the above copyright notice, this 11 | * list of conditions and the following disclaimer in the documentation and/or 12 | * other materials provided with the distribution. 13 | * - Neither the name of Carsten Blüm nor the names of his contributors may be 14 | * used to endorse or promote products derived from this software without specific 15 | * prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 20 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 21 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 23 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 24 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 25 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | */ 28 | 29 | #import 30 | #import "ActionProtocol.h" 31 | #import "KeyDownUpBaseAction.h" 32 | #import "KeycodeInformer.h" 33 | 34 | @interface TypeAction : NSObject { 35 | 36 | } 37 | 38 | @end 39 | -------------------------------------------------------------------------------- /Actions/TypeAction.m: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2007-2021, Carsten Blüm 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * - Redistributions of source code must retain the above copyright notice, this 9 | * list of conditions and the following disclaimer. 10 | * - Redistributions in binary form must reproduce the above copyright notice, this 11 | * list of conditions and the following disclaimer in the documentation and/or 12 | * other materials provided with the distribution. 13 | * - Neither the name of Carsten Blüm nor the names of his contributors may be 14 | * used to endorse or promote products derived from this software without specific 15 | * prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 20 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 21 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 23 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 24 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 25 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | */ 28 | 29 | #import "TypeAction.h" 30 | 31 | @implementation TypeAction 32 | 33 | #pragma mark - ActionProtocol 34 | 35 | + (NSString *)commandShortcut { 36 | return @"t"; 37 | } 38 | 39 | + (NSString *)commandDescription { 40 | return @" t:text Will TYPE the given TEXT into the frontmost application.\n" 41 | " If the text includes space(s), it must be enclosed in quotes.\n" 42 | " Example: “t:Test” will type “Test” \n" 43 | " Example: “t:'Viele Grüße'” will type “Viele Grüße”"; 44 | } 45 | 46 | #pragma mark - KeyBaseAction 47 | 48 | - (void)performActionWithKeycode:(CGKeyCode)code { 49 | CGEventRef e1 = CGEventCreateKeyboardEvent(NULL, code, true); 50 | CGEventPost(kCGSessionEventTap, e1); 51 | CFRelease(e1); 52 | 53 | CGEventRef e2 = CGEventCreateKeyboardEvent(NULL, code, false); 54 | CGEventPost(kCGSessionEventTap, e2); 55 | CFRelease(e2); 56 | } 57 | 58 | - (void)performActionWithData:(NSString *)data 59 | withOptions:(struct ExecutionOptions)options { 60 | 61 | struct timespec waitingtime; 62 | waitingtime.tv_sec = 0; 63 | waitingtime.tv_nsec = 10 * 1000000; // Milliseconds 64 | 65 | NSString *shortcut = [[self class] commandShortcut]; 66 | 67 | if ([data isEqualToString:@""]) { 68 | [NSException raise:@"InvalidCommandException" 69 | format:@"Missing argument to command “%@”: Expected a string. Examples: “%@:Hello” or “%@:'Hello world'”", 70 | shortcut, shortcut, shortcut]; 71 | } 72 | 73 | if (MODE_REGULAR != options.mode) { 74 | [options.verbosityOutputHandler write:[NSString stringWithFormat:@"Type: “%@”", data]]; 75 | if (MODE_TEST == options.mode) { 76 | return; 77 | } 78 | } 79 | 80 | // Generate the key code mapping 81 | KeycodeInformer *ki = [KeycodeInformer sharedInstance]; 82 | 83 | NSArray *keyCodeInfos = [ki keyCodesForString:data]; 84 | 85 | NSUInteger j, jj; 86 | 87 | for (j = 0, jj = [keyCodeInfos count]; j < jj; ++j) { 88 | 89 | NSArray *keyCodeInfo = [keyCodeInfos objectAtIndex:j]; 90 | 91 | CGKeyCode keyCode = [[keyCodeInfo objectAtIndex:0] intValue]; 92 | 93 | if ([[keyCodeInfo objectAtIndex:1] intValue] & MODIFIER_SHIFT) { 94 | CGEventRef e = CGEventCreateKeyboardEvent(NULL, KEYCODE_SHIFT, true); 95 | CGEventPost(kCGSessionEventTap, e); 96 | CFRelease(e); 97 | } 98 | 99 | nanosleep(&waitingtime, NULL); // Note: the delay is not needed for all keys. Strange, but true. 100 | 101 | if ([[keyCodeInfo objectAtIndex:1] intValue] & MODIFIER_ALT) { 102 | CGEventRef e = CGEventCreateKeyboardEvent(NULL, KEYCODE_ALT, true); 103 | CGEventPost(kCGSessionEventTap, e); 104 | CFRelease(e); 105 | } 106 | 107 | nanosleep(&waitingtime, NULL); 108 | 109 | CGEventRef keyDownEvent = CGEventCreateKeyboardEvent(NULL, keyCode, true); 110 | CGEventPost(kCGSessionEventTap, keyDownEvent); 111 | CFRelease(keyDownEvent); 112 | 113 | CGEventRef keyUpEvent = CGEventCreateKeyboardEvent(NULL, keyCode, false); 114 | CGEventPost(kCGSessionEventTap, keyUpEvent); 115 | CFRelease(keyUpEvent); 116 | 117 | nanosleep(&waitingtime, NULL); 118 | 119 | if ([[keyCodeInfo objectAtIndex:1] intValue] & MODIFIER_ALT) { 120 | CGEventRef e = CGEventCreateKeyboardEvent(NULL, KEYCODE_ALT, false); 121 | CGEventPost(kCGSessionEventTap, e); 122 | CFRelease(e); 123 | } 124 | 125 | nanosleep(&waitingtime, NULL); 126 | 127 | if ([[keyCodeInfo objectAtIndex:1] intValue] & MODIFIER_SHIFT) { 128 | CGEventRef e = CGEventCreateKeyboardEvent(NULL, KEYCODE_SHIFT, false); 129 | CGEventPost(kCGSessionEventTap, e); 130 | CFRelease(e); 131 | } 132 | 133 | nanosleep(&waitingtime, NULL); 134 | } 135 | } 136 | 137 | @end 138 | -------------------------------------------------------------------------------- /Actions/WaitAction.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2007-2021, Carsten Blüm 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * - Redistributions of source code must retain the above copyright notice, this 9 | * list of conditions and the following disclaimer. 10 | * - Redistributions in binary form must reproduce the above copyright notice, this 11 | * list of conditions and the following disclaimer in the documentation and/or 12 | * other materials provided with the distribution. 13 | * - Neither the name of Carsten Blüm nor the names of his contributors may be 14 | * used to endorse or promote products derived from this software without specific 15 | * prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 20 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 21 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 23 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 24 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 25 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | */ 28 | 29 | #import 30 | #import "ActionProtocol.h" 31 | 32 | @interface WaitAction : NSObject { 33 | 34 | } 35 | 36 | + (NSString *)commandShortcut; 37 | 38 | - (void)performActionWithData:(NSString *)data 39 | withOptions:(struct ExecutionOptions)options; 40 | 41 | @end 42 | -------------------------------------------------------------------------------- /Actions/WaitAction.m: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2007-2021, Carsten Blüm 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * - Redistributions of source code must retain the above copyright notice, this 9 | * list of conditions and the following disclaimer. 10 | * - Redistributions in binary form must reproduce the above copyright notice, this 11 | * list of conditions and the following disclaimer in the documentation and/or 12 | * other materials provided with the distribution. 13 | * - Neither the name of Carsten Blüm nor the names of his contributors may be 14 | * used to endorse or promote products derived from this software without specific 15 | * prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 20 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 21 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 23 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 24 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 25 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | */ 28 | 29 | #import "WaitAction.h" 30 | 31 | @implementation WaitAction 32 | 33 | #pragma mark - ActionProtocol 34 | 35 | + (NSString *)commandShortcut { 36 | return @"w"; 37 | } 38 | 39 | + (NSString *)commandDescription { 40 | return @" w:ms Will WAIT/PAUSE for the given number of milliseconds.\n" 41 | " Example: “w:500” will pause command execution for half a second"; 42 | } 43 | 44 | - (void)performActionWithData:(NSString *)data 45 | withOptions:(struct ExecutionOptions)options { 46 | 47 | unsigned milliseconds = abs([data intValue]); 48 | NSString *shortcut = [[self class] commandShortcut]; 49 | 50 | if ([data isEqualToString:@""] || 51 | !milliseconds) { 52 | [NSException raise:@"InvalidCommandException" 53 | format:@"Invalid or missing argument to command “%@”: Expected number of milliseconds. Example: “%@:50”", shortcut, shortcut]; 54 | } 55 | 56 | if (MODE_REGULAR != options.mode) { 57 | [options.verbosityOutputHandler write:[NSString stringWithFormat:@"Wait %i milliseconds", milliseconds]]; 58 | } 59 | 60 | if (MODE_TEST == options.mode) { 61 | return; 62 | } 63 | 64 | struct timespec waitingtime; 65 | if (milliseconds > 999) { 66 | waitingtime.tv_sec = (int)floor(milliseconds / 1000); 67 | waitingtime.tv_nsec = (milliseconds - waitingtime.tv_sec * 1000) * 1000000; 68 | } else { 69 | waitingtime.tv_sec = 0; 70 | waitingtime.tv_nsec = milliseconds * 1000000; 71 | } 72 | 73 | nanosleep(&waitingtime, NULL); 74 | } 75 | 76 | @end 77 | -------------------------------------------------------------------------------- /ExecutionOptions.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2007-2021, Carsten Blüm 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * - Redistributions of source code must retain the above copyright notice, this 9 | * list of conditions and the following disclaimer. 10 | * - Redistributions in binary form must reproduce the above copyright notice, this 11 | * list of conditions and the following disclaimer in the documentation and/or 12 | * other materials provided with the distribution. 13 | * - Neither the name of Carsten Blüm nor the names of his contributors may be 14 | * used to endorse or promote products derived from this software without specific 15 | * prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 20 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 21 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 23 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 24 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 25 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | */ 28 | 29 | #import "OutputHandler.h" 30 | 31 | #ifndef ExecutionOptions_h 32 | #define ExecutionOptions_h 33 | 34 | struct ExecutionOptions { 35 | unsigned mode; 36 | unsigned easing; 37 | unsigned waitTime; 38 | BOOL isFirstAction; 39 | BOOL isLastAction; 40 | OutputHandler *verbosityOutputHandler; 41 | OutputHandler *commandOutputHandler; 42 | }; 43 | 44 | #endif /* ExecutionOptions_h */ 45 | -------------------------------------------------------------------------------- /KeycodeInformer.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2007-2021, Carsten Blüm 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * - Redistributions of source code must retain the above copyright notice, this 9 | * list of conditions and the following disclaimer. 10 | * - Redistributions in binary form must reproduce the above copyright notice, this 11 | * list of conditions and the following disclaimer in the documentation and/or 12 | * other materials provided with the distribution. 13 | * - Neither the name of Carsten Blüm nor the names of his contributors may be 14 | * used to endorse or promote products derived from this software without specific 15 | * prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 20 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 21 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 23 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 24 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 25 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | */ 28 | 29 | #import 30 | #include 31 | #include 32 | 33 | @interface KeycodeInformer : NSObject { 34 | NSMutableDictionary *map; 35 | TISInputSourceRef keyboard; 36 | CFDataRef keyLayoutData; 37 | const UCKeyboardLayout *keyboardLayout; 38 | } 39 | 40 | + (id)sharedInstance; 41 | 42 | - (NSArray *)keyCodesForString:(NSString *)string; 43 | 44 | - (NSString *)stringForKeyCode:(CGKeyCode)keyCode andModifiers:(UInt32)modifiers; 45 | 46 | - (NSString *)prepareString:(NSString *)string; 47 | 48 | /** 49 | * Returns a map of characters which require typing two characters with the current keyboard layout 50 | * 51 | * @warning This method is incomplete. It not only supports only a few keyboard layout, but also lacks lots of characters even for the few supported keyboard layouts. 52 | * @return NSDictionary which has the characters as keys and a string containing the characters to be typed as values 53 | */ 54 | - (NSDictionary *)getReplacementMapForKeyboardLayoutNamed:(NSString *)layoutName; 55 | 56 | @end 57 | -------------------------------------------------------------------------------- /KeycodeInformer.m: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2007-2021, Carsten Blüm 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * - Redistributions of source code must retain the above copyright notice, this 9 | * list of conditions and the following disclaimer. 10 | * - Redistributions in binary form must reproduce the above copyright notice, this 11 | * list of conditions and the following disclaimer in the documentation and/or 12 | * other materials provided with the distribution. 13 | * - Neither the name of Carsten Blüm nor the names of his contributors may be 14 | * used to endorse or promote products derived from this software without specific 15 | * prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 20 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 21 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 23 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 24 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 25 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | */ 28 | 29 | #import "KeycodeInformer.h" 30 | 31 | @implementation KeycodeInformer 32 | 33 | static KeycodeInformer *sharedInstance = nil; 34 | 35 | + (id)sharedInstance { 36 | @synchronized(self) { 37 | if (sharedInstance == nil) { 38 | sharedInstance = [[super allocWithZone:NULL] init]; 39 | } 40 | } 41 | return sharedInstance; 42 | } 43 | 44 | + (id)allocWithZone:(NSZone *)zone { 45 | return [[self sharedInstance] retain]; 46 | } 47 | 48 | - (KeycodeInformer *)init { 49 | self = [super init]; 50 | if (self) { 51 | keyboard = TISCopyCurrentKeyboardInputSource(); 52 | keyLayoutData = TISGetInputSourceProperty(keyboard, kTISPropertyUnicodeKeyLayoutData); 53 | 54 | if (NULL == keyLayoutData) { 55 | keyboard = TISCopyCurrentASCIICapableKeyboardInputSource(); 56 | keyLayoutData = TISGetInputSourceProperty(keyboard, kTISPropertyUnicodeKeyLayoutData); 57 | if (NULL == keyLayoutData) { 58 | [NSException raise:@"KeyboardLayoutException" 59 | format:@"Sorry, cliclick cannot handle your keyboard layout, so emulating typing is not possible."]; 60 | } 61 | } 62 | 63 | keyboardLayout = (const UCKeyboardLayout *)CFDataGetBytePtr(keyLayoutData); 64 | 65 | map = [[NSMutableDictionary alloc] initWithCapacity:256]; 66 | 67 | NSArray *keyCodes = [NSArray arrayWithObjects: @0, @1, @2, @3, @4, @5, @6, @7, @8, @9, 68 | @10, @11, @12, @13, @14, @15, @16, @17, @18, @19, 69 | @20, @21, @22, @23, @24, @25, @26, @27, @28, @29, 70 | @30, @31, @32, @33, @34, @35, @37, @38, @39, 71 | @40, @41, @42, @43, @44, @45, @46, @47, @49, 72 | @50, nil]; 73 | 74 | for (NSNumber *keyCode in keyCodes) { 75 | NSString *string1 = [self stringForKeyCode:(CGKeyCode)[keyCode intValue] andModifiers:0]; 76 | [map setObject:[NSArray arrayWithObjects:keyCode, [NSNumber numberWithInt:0], nil] 77 | forKey:[string1 decomposedStringWithCanonicalMapping]]; 78 | 79 | NSString *string2 = [self stringForKeyCode:(CGKeyCode)[keyCode intValue] andModifiers:MODIFIER_SHIFT]; 80 | [map setObject:[NSArray arrayWithObjects:keyCode, NSNUMBER_MODIFIER_SHIFT, nil] 81 | forKey:[string2 decomposedStringWithCanonicalMapping]]; 82 | 83 | NSString *string3 = [self stringForKeyCode:(CGKeyCode)[keyCode intValue] andModifiers:MODIFIER_ALT]; 84 | [map setObject:[NSArray arrayWithObjects:keyCode, NSNUMBER_MODIFIER_ALT, nil] 85 | forKey:[string3 decomposedStringWithCanonicalMapping]]; 86 | 87 | NSString *string4 = [self stringForKeyCode:(CGKeyCode)[keyCode intValue] andModifiers:MODIFIER_SHIFT_ALT]; 88 | [map setObject:[NSArray arrayWithObjects:keyCode, NSNUMBER_MODIFIER_SHIFT_ALT, nil] 89 | forKey:[string4 decomposedStringWithCanonicalMapping]]; 90 | } 91 | } 92 | 93 | return self; 94 | } 95 | 96 | - (id)copyWithZone:(NSZone *)zone { 97 | return self; 98 | } 99 | 100 | - (id)retain { 101 | return self; 102 | } 103 | 104 | - (NSUInteger)retainCount { 105 | return UINT_MAX; 106 | } 107 | 108 | - (oneway void)release { 109 | CFRelease(keyLayoutData); 110 | CFRelease(keyboard); 111 | } 112 | 113 | - (id)autorelease { 114 | return self; 115 | } 116 | 117 | - (void)dealloc { 118 | [map release]; 119 | [super dealloc]; 120 | } 121 | 122 | - (NSArray *)keyCodesForString:(NSString *)string { 123 | NSMutableArray *keyCodes = [[NSMutableArray alloc] initWithCapacity:[string length]]; 124 | string = [[self prepareString:string] decomposedStringWithCanonicalMapping]; 125 | NSUInteger i, ii; 126 | 127 | for (i = 0, ii = [string length]; i < ii; i++) { 128 | 129 | NSRange range = [string rangeOfComposedCharacterSequenceAtIndex:i]; 130 | 131 | if (range.length > 1) { 132 | i += range.length - 1; 133 | } 134 | 135 | id keyCodeInfo = [map objectForKey:[string substringWithRange:range]]; 136 | if (keyCodeInfo) { 137 | [keyCodes addObject:keyCodeInfo]; 138 | } else { 139 | NSFileHandle *fh = [NSFileHandle fileHandleWithStandardError]; 140 | NSString *url = [NSString stringWithFormat:CHARINFO_URL_TEMPLATE, VERSION]; 141 | NSString *msg = [NSString stringWithFormat:@"Unable to get key code for %@ (see %@)\n", [string substringWithRange:range], url]; 142 | [fh writeData:[msg dataUsingEncoding:NSUTF8StringEncoding]]; 143 | } 144 | } 145 | 146 | return [keyCodes autorelease]; 147 | } 148 | 149 | - (NSString *)prepareString:(NSString *)string { 150 | NSString *layoutName = TISGetInputSourceProperty(keyboard, kTISPropertyLocalizedName); 151 | NSDictionary *replacementMap = [self getReplacementMapForKeyboardLayoutNamed:layoutName]; 152 | NSMutableString *tmp = [NSMutableString stringWithString:string]; 153 | NSEnumerator *enumerator = [replacementMap keyEnumerator]; 154 | NSString *key; 155 | 156 | DLog(@"Current keyboard layout: %@", layoutName); 157 | 158 | if (nil != replacementMap) { 159 | while ((key = [enumerator nextObject])) { 160 | [tmp replaceOccurrencesOfString:key withString:[replacementMap objectForKey:key] options:NSLiteralSearch range:NSMakeRange(0, [tmp length])]; 161 | } 162 | } 163 | 164 | return tmp; 165 | } 166 | 167 | - (NSString *)stringForKeyCode:(CGKeyCode)keyCode andModifiers:(UInt32)modifiers { 168 | UInt32 keysDown = 0; 169 | UniChar chars[4]; 170 | UniCharCount realLength; 171 | 172 | UCKeyTranslate(keyboardLayout, 173 | keyCode, 174 | kUCKeyActionDisplay, 175 | modifiers, 176 | LMGetKbdType(), 177 | kUCKeyTranslateNoDeadKeysBit, 178 | &keysDown, 179 | sizeof(chars) / sizeof(chars[0]), 180 | &realLength, 181 | chars); 182 | 183 | return [NSString stringWithCharacters:chars length:1]; 184 | } 185 | 186 | - (NSDictionary *)getReplacementMapForKeyboardLayoutNamed:(NSString *)layoutName { 187 | // Incomplete lists of characters which (dependent on the keyboard layout) cannot 188 | // be typed by a combination of keys, but require consecutive key presses. Many 189 | // characters are missing, as I did not yet find a way to auto-generate the map. 190 | 191 | if ([@"German" isEqualToString:layoutName]) { 192 | #pragma mark - German replacement map 193 | // #SUPPORTED German: ÄËÏÖÜŸäëïöüÿÁÉÍÓÚáéíóúÀÈÌÒÙàèìòùÂÊÎÔÛâêîôûÃÕÑãõñ 194 | // #KNOWN_UNSUPPORTED German: ŃńǸǹŇňŘřŠšŮů 195 | return @{ 196 | // Umlauts 197 | @"Ë": @"¨E", 198 | @"Ÿ": @"¨Y", 199 | @"ë": @"¨e", 200 | @"ï": @"¨i", 201 | @"ÿ": @"¨y", 202 | 203 | // Acute 204 | @"Á": @"´A", 205 | @"É": @"´E", 206 | @"Í": @"´I", 207 | @"Ó": @"´O", 208 | @"Ú": @"´U", 209 | @"á": @"´a", 210 | @"é": @"´e", 211 | @"í": @"´i", 212 | @"ó": @"´o", 213 | @"ú": @"´u", 214 | 215 | // Agrave 216 | @"À": @"`A", 217 | @"È": @"`E", 218 | @"Ì": @"`I", 219 | @"Ò": @"`O", 220 | @"Ù": @"`U", 221 | @"à": @"`a", 222 | @"è": @"`e", 223 | @"ì": @"`i", 224 | @"ò": @"`o", 225 | @"ù": @"`u", 226 | 227 | // Circumflex 228 | @"Â": @"^A", 229 | @"Ê": @"^E", 230 | @"Î": @"^I", 231 | @"Ô": @"^O", 232 | @"Û": @"^U", 233 | @"â": @"^a", 234 | @"ê": @"^e", 235 | @"î": @"^i", 236 | @"ô": @"^o", 237 | @"û": @"^u", 238 | 239 | // Tilde 240 | @"Ã": @"~A", 241 | @"Ñ": @"~N", 242 | @"Õ": @"~O", 243 | @"ã": @"~a", 244 | @"ñ": @"~n", 245 | @"õ": @"~o" 246 | }; 247 | } 248 | 249 | if ([@"U.S. Extended" isEqualToString:layoutName]) { 250 | #pragma mark - U.S. Extended replacement map 251 | // #SUPPORTED U.S. Extended: ÄËÏÖÜŸäëïöüÿÁÉÍÓÚáéíóúÀÈÌÒÙàèìòùÂÊÎÔÛâêîôûÃÕÑãõñŃńǸǹŇňŘřŠšÅåŮů 252 | // #KNOWN_UNSUPPORTED U.S. Extended: 253 | // See http://symbolcodes.tlt.psu.edu/accents/codemacext.html 254 | return @{ 255 | // Umlauts 256 | @"Ä": @"¨A", 257 | @"Ë": @"¨E", 258 | @"Ï": @"¨I", 259 | @"Ö": @"¨O", 260 | @"Ü": @"¨U", 261 | @"Ÿ": @"¨Y", 262 | @"ä": @"¨a", 263 | @"ë": @"¨e", 264 | @"ï": @"¨i", 265 | @"ö": @"¨o", 266 | @"ü": @"¨u", 267 | @"ÿ": @"¨y", 268 | 269 | // Acute 270 | @"Á": @"´A", 271 | @"É": @"´E", 272 | @"Í": @"´I", 273 | @"Ń": @"´N", 274 | @"Ó": @"´O", 275 | @"Ú": @"´U", 276 | @"á": @"´a", 277 | @"é": @"´e", 278 | @"í": @"´i", 279 | @"ń": @"´n", 280 | @"ó": @"´o", 281 | @"ú": @"´u", 282 | 283 | // Agrave 284 | @"À": @"`A", 285 | @"È": @"`E", 286 | @"Ì": @"`I", 287 | @"Ǹ": @"`N", 288 | @"Ò": @"`O", 289 | @"Ù": @"`U", 290 | @"à": @"`a", 291 | @"è": @"`e", 292 | @"ì": @"`i", 293 | @"ò": @"`o", 294 | @"ù": @"`u", 295 | @"ǹ": @"`n", 296 | 297 | // Circumflex 298 | @"Â": @"ˆA", 299 | @"Ê": @"ˆE", 300 | @"Î": @"ˆI", 301 | @"Ô": @"ˆO", 302 | @"Û": @"ˆU", 303 | @"â": @"ˆa", 304 | @"ê": @"ˆe", 305 | @"î": @"ˆi", 306 | @"ô": @"ˆo", 307 | @"û": @"ˆu", 308 | 309 | // Tilde 310 | @"Ã": @"˜A", 311 | @"Ñ": @"˜N", 312 | @"Õ": @"˜O", 313 | @"ã": @"˜a", 314 | @"ñ": @"˜n", 315 | @"õ": @"˜o", 316 | 317 | // Caron 318 | @"Ň": @"ˇN", 319 | @"Ř": @"ˇR", 320 | @"Š": @"ˇS", 321 | @"ň": @"ˇn", 322 | @"ř": @"ˇr", 323 | @"š": @"ˇs", 324 | 325 | // A ring 326 | @"Å": @"˚A", 327 | @"å": @"˚a", 328 | @"Ů": @"˚U", 329 | @"ů": @"˚u" 330 | }; 331 | } 332 | 333 | if ([@"Polish" isEqualToString:layoutName]) { 334 | #pragma mark - Polish replacement map 335 | // #SUPPORTED Polish: ÄÖÜäöüÁÉÍÓÚáéíóúŃńŇňŘřŠš 336 | // #KNOWN_UNSUPPORTED Polish: ËÏŸëïÿÀÈÌÒÙàèìòùǸǹÂÊÎÔÛâêîôûÃÕÑãõñŒœÅåØøÆæ 337 | return @{ 338 | // Umlauts 339 | @"Ä": @"¨A", 340 | @"Ö": @"¨O", 341 | @"Ü": @"¨U", 342 | @"ä": @"¨a", 343 | @"ö": @"¨o", 344 | @"ü": @"¨u", 345 | 346 | // Acute 347 | @"Á": @"´A", 348 | @"É": @"´E", 349 | @"Í": @"´I", 350 | @"Ú": @"´U", 351 | @"á": @"´a", 352 | @"é": @"´e", 353 | @"í": @"´i", 354 | @"ú": @"´u", 355 | 356 | // Caron 357 | @"Ň": @"ˇN", 358 | @"Ř": @"ˇR", 359 | @"Š": @"ˇS", 360 | @"ň": @"ˇn", 361 | @"ř": @"ˇr", 362 | @"š": @"ˇs" 363 | }; 364 | } 365 | 366 | if ([@"French" isEqualToString:layoutName]) { 367 | #pragma mark - French replacement map 368 | // #SUPPORTED French: ÄËÏÖÜŸäëïöüÿÁÉÍÓÚáéíóúÀÈÌÒÙàèìòùÂÊÎÔÛâêîôû 369 | // #KNOWN_UNSUPPORTED French: ÃÑÕãñõŃńǸǹŇňŘřŠšŮů 370 | return @{ 371 | // Umlauts 372 | @"Ä": @"¨A", 373 | @"Ë": @"¨E", 374 | @"Ö": @"¨O", 375 | @"Ü": @"¨U", 376 | @"ä": @"¨a", 377 | @"ë": @"¨e", 378 | @"ö": @"¨o", 379 | @"ü": @"¨u", 380 | @"ÿ": @"¨y", 381 | 382 | // Acute 383 | @"É": @"´E", 384 | @"á": @"´a", 385 | @"í": @"´i", 386 | @"ó": @"´o", 387 | @"ú": @"´u", 388 | 389 | // Agrave 390 | @"À": @"`A", 391 | @"ì": @"`i", 392 | @"ò": @"`o", 393 | 394 | // Circumflex 395 | @"â": @"^A", 396 | @"û": @"^U", 397 | @"Ǹ": @"`N", 398 | @"ǹ": @"`n" 399 | }; 400 | } 401 | 402 | if ([@"Canadian French - CSA" isEqualToString:layoutName]) { 403 | #pragma mark - Canadian French replacement map 404 | // #SUPPORTED Canadian French - CSA: ÄËÏÖÜŸäëïöüÿÁÉÍÓÚáéíóúÀÈÌÒÙàèìòùÂÊÎÔÛâêîôûÃÑÕãñõ 405 | // #KNOWN_UNSUPPORTED Canadian French - CSA: ŇŘŠňřšǸǹŃńÅåŮů 406 | return @{ 407 | // Umlauts 408 | @"Ä": @"¨A", 409 | @"Ë": @"¨E", 410 | @"Ï": @"¨I", 411 | @"Ö": @"¨O", 412 | @"Ü": @"¨U", 413 | @"Ÿ": @"¨Y", 414 | @"ä": @"¨a", 415 | @"ë": @"¨e", 416 | @"ï": @"¨i", 417 | @"ö": @"¨o", 418 | @"ü": @"¨u", 419 | @"ÿ": @"¨y", 420 | 421 | // Acute 422 | @"Á": @"´A", 423 | @"É": @"´E", 424 | @"Í": @"´I", 425 | @"Ó": @"´O", 426 | @"Ú": @"´U", 427 | @"á": @"´a", 428 | @"í": @"´i", 429 | @"ó": @"´o", 430 | @"ú": @"´u", 431 | 432 | // Agrave 433 | @"Ì": @"`I", 434 | @"Ò": @"`O", 435 | @"ì": @"`i", 436 | @"ò": @"`o", 437 | 438 | // Circumflex 439 | @"Â": @"^A", 440 | @"Ê": @"^E", 441 | @"Î": @"^I", 442 | @"Ô": @"^O", 443 | @"Û": @"^U", 444 | @"â": @"^a", 445 | @"ê": @"^e", 446 | @"î": @"^i", 447 | @"ô": @"^o", 448 | @"û": @"^u", 449 | 450 | // Tilde 451 | @"Ã": @"~A", 452 | @"Ñ": @"~N", 453 | @"Õ": @"~O", 454 | @"ã": @"~a", 455 | @"ñ": @"~n", 456 | @"õ": @"~o" 457 | }; 458 | } 459 | 460 | if ([@"Spanish" isEqualToString:layoutName]) { 461 | #pragma mark - Spanish replacement map 462 | // #SUPPORTED Spanish: ÄËÏÖÜŸäëïöüÿÁÉÍÓÚáéíóúÀÈÌÒÙàèìòùÂÊÎÔÛâêîôûÑñ 463 | // #KNOWN_UNSUPPORTED Spanish: ÃÕãõŇŘŠňřšǸǹŃńŮů 464 | return @{ 465 | // Umlauts 466 | @"Ä": @"¨A", 467 | @"Ë": @"¨E", 468 | @"Ö": @"¨O", 469 | @"Ü": @"¨U", 470 | @"ä": @"¨a", 471 | @"ë": @"¨e", 472 | @"ï": @"¨i", 473 | @"ö": @"¨o", 474 | @"ü": @"¨u", 475 | @"ÿ": @"¨y", 476 | 477 | // Acute 478 | @"É": @"´E", 479 | @"í": @"´i", 480 | @"á": @"´a", 481 | @"é": @"´e", 482 | @"ó": @"´o", 483 | @"ú": @"´u", 484 | 485 | // Agrave 486 | @"À": @"`A", 487 | @"à": @"`a", 488 | @"è": @"`e", 489 | @"ì": @"`i", 490 | @"ò": @"`o", 491 | @"ù": @"`u", 492 | 493 | // Circumflex 494 | @"â": @"^a", 495 | @"ê": @"^e", 496 | @"î": @"^i", 497 | @"ô": @"^o", 498 | @"û": @"^u" 499 | }; 500 | } 501 | 502 | if ([@"Portuguese" isEqualToString:layoutName]) { 503 | #pragma mark - Portuguese replacement map 504 | // #SUPPORTED Portuguese: ÄËÏÖÜäëïöüÿÁÉÍÓÚáéíóúÀÈÌÒÙàèìòùÂÊÎÔÛâêîôûÑñÃÕãõ 505 | // #KNOWN_UNSUPPORTED Portuguese: ŸŇŘŠňřšǸǹŃńŮů 506 | return @{ 507 | // Umlauts 508 | @"Ä": @"¨A", 509 | @"Ë": @"¨E", 510 | @"Ï": @"¨I", 511 | @"Ö": @"¨O", 512 | @"Ü": @"¨U", 513 | @"ä": @"¨a", 514 | @"ë": @"¨e", 515 | @"ï": @"¨i", 516 | @"ö": @"¨o", 517 | @"ü": @"¨u", 518 | @"ÿ": @"¨y", 519 | 520 | // Acute 521 | @"Á": @"´A", 522 | @"É": @"´E", 523 | @"Í": @"´I", 524 | @"Ó": @"´O", 525 | @"Ú": @"´U", 526 | @"á": @"´a", 527 | @"é": @"´e", 528 | @"í": @"´i", 529 | @"ó": @"´o", 530 | @"ú": @"´u", 531 | 532 | // Agrave 533 | @"À": @"`A", 534 | @"È": @"`E", 535 | @"Ì": @"`I", 536 | @"Ò": @"`O", 537 | @"Ù": @"`U", 538 | @"à": @"`a", 539 | @"è": @"`e", 540 | @"ì": @"`i", 541 | @"ò": @"`o", 542 | @"ù": @"`u", 543 | 544 | // Circumflex 545 | @"Â": @"^A", 546 | @"Ê": @"^E", 547 | @"Î": @"^I", 548 | @"Ô": @"^O", 549 | @"Û": @"^U", 550 | @"â": @"^a", 551 | @"ê": @"^e", 552 | @"î": @"^i", 553 | @"ô": @"^o", 554 | @"û": @"^u", 555 | 556 | // Tilde 557 | @"Ã": @"˜A", 558 | @"Õ": @"˜O", 559 | @"Ñ": @"˜N", 560 | @"ã": @"˜a", 561 | @"õ": @"˜o", 562 | @"ñ": @"˜n" 563 | }; 564 | } 565 | 566 | if ([@"Italian" isEqualToString:layoutName]) { 567 | #pragma mark - Italian replacement map 568 | // #SUPPORTED Italian: ÄËÏÖÜŸäëïöüÿÁÉÍÓÚáéíóúÀÈÌÒÙàèìòùÂÊÎÔÛâêîôûÑñÃÕãõ 569 | // #KNOWN_UNSUPPORTED Italian: ŸŇŘŠňřšǸǹŃńŮů 570 | return @{ 571 | // Umlauts 572 | @"Ä": @"¨A", 573 | @"Ë": @"¨E", 574 | @"Ï": @"¨I", 575 | @"Ö": @"¨O", 576 | @"Ü": @"¨U", 577 | @"Ÿ": @"¨Y", 578 | @"ä": @"¨a", 579 | @"ë": @"¨e", 580 | @"ï": @"¨i", 581 | @"ö": @"¨o", 582 | @"ü": @"¨u", 583 | @"ÿ": @"¨y", 584 | 585 | // Acute 586 | @"á": @"´a", 587 | @"í": @"´i", 588 | @"ó": @"´o", 589 | @"ú": @"´u", 590 | 591 | // Circumflex 592 | @"Â": @"ˆA", 593 | @"Ê": @"ˆE", 594 | @"Î": @"ˆI", 595 | @"Ô": @"ˆO", 596 | @"Û": @"ˆU", 597 | @"â": @"ˆa", 598 | @"ê": @"ˆe", 599 | @"î": @"ˆi", 600 | @"ô": @"ˆo", 601 | @"û": @"ˆu", 602 | 603 | // Tilde 604 | @"Ã": @"˜A", 605 | @"Ñ": @"˜N", 606 | @"Õ": @"˜O", 607 | @"ã": @"˜a", 608 | @"ñ": @"˜n", 609 | @"õ": @"˜o" 610 | }; 611 | } 612 | 613 | if ([@"Canadian English" isEqualToString:layoutName]) { 614 | #pragma mark - Canadian English replacement map 615 | 616 | // Note: When physically typing on a keyboard with this layout, typing with combining 617 | // characters (e.g. ¨ plus u) works. But for some reason, when typing programmatically, 618 | // they require a pretty long delay between keystrokes (50+ ms). And, more important, 619 | // in a sequence of those characters (e.g. "Äñé"), only the first one is correct, 620 | // while the others will result in the two characters (the right side of the map), 621 | // not in the character that should be typed (the left side of the map). 622 | // 623 | // Any hints why this is the case and how that could be fixed are welcome. 624 | 625 | return nil; 626 | } 627 | 628 | if ([@"Brazilian" isEqualToString:layoutName]) { 629 | #pragma mark - Brazilian replacement map 630 | 631 | // Note: When physically typing on a keyboard with this layout, typing with combining 632 | // characters (e.g. ¨ plus u) works. But for some reason, when typing programmatically, 633 | // they require a pretty long delay between keystrokes (50+ ms). And, more important, 634 | // in a sequence of those characters (e.g. "Äñé"), only the first one is correct, 635 | // while the others will result in the two characters (the right side of the map), 636 | // not in the character that should be typed (the left side of the map). 637 | // 638 | // Any hints why this is the case and how that could be fixed are welcome. 639 | 640 | return nil; 641 | } 642 | 643 | return nil; 644 | } 645 | 646 | @end 647 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | cliclick 2 | 3 | Copyright (c) 2007-2018, Carsten Blüm 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | * Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | * Redistributions in binary form must reproduce the above copyright notice, this 12 | list of conditions and the following disclaimer in the documentation and/or 13 | other materials provided with the distribution. 14 | * Neither the name of Carsten Blüm nor the names of his 15 | contributors may be used to endorse or promote products derived 16 | from this software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 22 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 25 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | CC = cc 2 | CFLAGS = -include cliclick_Prefix.pch -I Actions -I . 3 | 4 | all: macros cliclick 5 | 6 | macros: 7 | ./generate-action-classes-macro.sh 8 | 9 | cliclick: Actions/ClickAction.o \ 10 | Actions/ColorPickerAction.o \ 11 | Actions/DoubleclickAction.o \ 12 | Actions/DragDownAction.o \ 13 | Actions/DragUpAction.o \ 14 | Actions/DragMoveAction.o \ 15 | Actions/KeyBaseAction.o \ 16 | Actions/KeyDownAction.o \ 17 | Actions/KeyDownUpBaseAction.o \ 18 | Actions/KeyPressAction.o \ 19 | Actions/KeyUpAction.o \ 20 | Actions/MouseBaseAction.o \ 21 | Actions/MoveAction.o \ 22 | Actions/PrintAction.o \ 23 | Actions/RightClickAction.o \ 24 | Actions/TripleclickAction.o \ 25 | Actions/TypeAction.o \ 26 | Actions/WaitAction.o \ 27 | ActionExecutor.o \ 28 | KeycodeInformer.o \ 29 | OutputHandler.o \ 30 | cliclick.o 31 | $(CC) -o cliclick $^ -framework Cocoa -framework Carbon 32 | 33 | install: macros cliclick 34 | cp cliclick /usr/local/bin/ 35 | 36 | clean: 37 | $(RM) -v ActionClassesMacro.h 38 | $(RM) -v *.o Actions/*.o 39 | $(RM) -vr cliclick 40 | -------------------------------------------------------------------------------- /OutputHandler.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2007-2021, Carsten Blüm 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * - Redistributions of source code must retain the above copyright notice, this 9 | * list of conditions and the following disclaimer. 10 | * - Redistributions in binary form must reproduce the above copyright notice, this 11 | * list of conditions and the following disclaimer in the documentation and/or 12 | * other materials provided with the distribution. 13 | * - Neither the name of Carsten Blüm nor the names of his contributors may be 14 | * used to endorse or promote products derived from this software without specific 15 | * prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 20 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 21 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 23 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 24 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 25 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | */ 28 | 29 | #import 30 | 31 | @interface OutputHandler : NSObject { 32 | NSString *outputTarget; 33 | } 34 | 35 | - (id)initWithTarget:(NSString *)target; 36 | 37 | - (void)write:(NSString *)message; 38 | 39 | @end 40 | -------------------------------------------------------------------------------- /OutputHandler.m: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2007-2021, Carsten Blüm 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * - Redistributions of source code must retain the above copyright notice, this 9 | * list of conditions and the following disclaimer. 10 | * - Redistributions in binary form must reproduce the above copyright notice, this 11 | * list of conditions and the following disclaimer in the documentation and/or 12 | * other materials provided with the distribution. 13 | * - Neither the name of Carsten Blüm nor the names of his contributors may be 14 | * used to endorse or promote products derived from this software without specific 15 | * prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 20 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 21 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 23 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 24 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 25 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | */ 28 | 29 | #import "OutputHandler.h" 30 | 31 | @implementation OutputHandler 32 | 33 | - (id)initWithTarget:(NSString *)target { 34 | self = [super init]; 35 | 36 | if (self) { 37 | if (!target) { 38 | outputTarget = @"stdout"; 39 | } else if ([target isEqualToString:@"stdout"] || 40 | [target isEqualToString:@"stderr"]) { 41 | outputTarget = target; 42 | } else if ([target isEqualToString:@"clipboard"]) { 43 | outputTarget = target; 44 | NSPasteboard *pasteboard = [NSPasteboard generalPasteboard]; 45 | [pasteboard declareTypes:[NSArray arrayWithObject:NSStringPboardType] owner:nil]; 46 | [pasteboard setString:@"" 47 | forType:NSStringPboardType]; 48 | } else { 49 | NSFileManager *fm = [NSFileManager defaultManager]; 50 | if ([fm fileExistsAtPath:target]) { 51 | if (![fm isWritableFileAtPath:target]) { 52 | [NSException raise:@"InvalidDestinationException" 53 | format:@"Cannot write to the file “%@” specified as output destination.", target]; 54 | } 55 | } else { 56 | if (![fm createFileAtPath:target contents:nil attributes:nil]) { 57 | [NSException raise:@"InvalidDestinationException" 58 | format:@"Cannot create file “%@” specified as output destination.", target]; 59 | } 60 | } 61 | outputTarget = target; 62 | } 63 | } 64 | return self; 65 | } 66 | 67 | - (void)write:(NSString *)message { 68 | if ([outputTarget isEqualToString:@"stdout"]) { 69 | printf("%s\n", [message UTF8String]); 70 | return; 71 | } 72 | 73 | if ([outputTarget isEqualToString:@"stderr"]) { 74 | fprintf(stderr, "%s\n", [message UTF8String]); 75 | return; 76 | } 77 | 78 | if ([outputTarget isEqualToString:@"clipboard"]) { 79 | NSPasteboard *pasteboard = [NSPasteboard generalPasteboard]; 80 | [pasteboard setString:[NSString stringWithFormat:@"%@%@\n", [pasteboard stringForType:NSStringPboardType], message] 81 | forType:NSStringPboardType]; 82 | return; 83 | } 84 | 85 | // Still here? must be file target 86 | NSFileHandle *fileHandle = [NSFileHandle fileHandleForWritingAtPath:outputTarget]; 87 | [fileHandle seekToEndOfFile]; 88 | [fileHandle writeData:[[message stringByAppendingString:@"\n"] dataUsingEncoding:NSUTF8StringEncoding]]; 89 | [fileHandle closeFile]; 90 | } 91 | 92 | @end 93 | -------------------------------------------------------------------------------- /README-Characters.md: -------------------------------------------------------------------------------- 1 | Cliclick can emulate typing any character that can be typed on physical keyboard by hitting a regular key alone or combined with Shift and/or Alt, regardless of the keyboard layout. 2 | 3 | In addition, in most keyboards layouts, there are characters which can be entered by typing two characters consecutively, typically with the first one being a so-called “dead” key or “combining character”. For instance, on a German keyboard, there is no key for typing “é”, but you can enter it by first typing a “combining acute accent” (Unicode U+0301) followed by “e”. Below you will find an overview of some keyboard layouts (or rather their English names) and lists of the characters which cliclick is able to type, as well as (very incomplete) lists of the characters which are known to be untypeable for 4 | cliclick. If you find that cliclick cannot type a character you need, you might be interested in either adding code to support the keyboard layout (if cliclick does not know the layout at all) or in adding code on how to type the character using the current keyboard layout. The place to make these additions is method `getReplacementMapForKeyboardLayoutNamed:` in `KeycodeInformer.m`. The code in that method is pretty simple and you do not need to know much Objective-C to add more characters. This method is also the place from which data is read to auto-generate this file, so _do not edit it manually_. 5 | 6 | Having said that, here is the list of keyboard layouts with the supported and (some) unsupported characters: 7 | 8 | Canadian French - CSA 9 | * Supported: ÄËÏÖÜŸäëïöüÿÁÉÍÓÚáéíóúÀÈÌÒÙàèìòùÂÊÎÔÛâêîôûÃÑÕãñõ 10 | * Unsupported: ŇŘŠňřšǸǹŃńÅåŮů (incomplete list) 11 | 12 | French 13 | * Supported: ÄËÏÖÜŸäëïöüÿÁÉÍÓÚáéíóúÀÈÌÒÙàèìòùÂÊÎÔÛâêîôû 14 | * Unsupported: ÃÑÕãñõŃńǸǹŇňŘřŠšŮů (incomplete list) 15 | 16 | German 17 | * Supported: ÄËÏÖÜŸäëïöüÿÁÉÍÓÚáéíóúÀÈÌÒÙàèìòùÂÊÎÔÛâêîôûÃÕÑãõñ 18 | * Unsupported: ŃńǸǹŇňŘřŠšŮů (incomplete list) 19 | 20 | Italian 21 | * Supported: ÄËÏÖÜŸäëïöüÿÁÉÍÓÚáéíóúÀÈÌÒÙàèìòùÂÊÎÔÛâêîôûÑñÃÕãõ 22 | * Unsupported: ŸŇŘŠňřšǸǹŃńŮů (incomplete list) 23 | 24 | Polish 25 | * Supported: ÄÖÜäöüÁÉÍÓÚáéíóúŃńŇňŘřŠš 26 | * Unsupported: ËÏŸëïÿÀÈÌÒÙàèìòùǸǹÂÊÎÔÛâêîôûÃÕÑãõñŒœÅåØøÆæ (incomplete list) 27 | 28 | Portuguese 29 | * Supported: ÄËÏÖÜäëïöüÿÁÉÍÓÚáéíóúÀÈÌÒÙàèìòùÂÊÎÔÛâêîôûÑñÃÕãõ 30 | * Unsupported: ŸŇŘŠňřšǸǹŃńŮů (incomplete list) 31 | 32 | Spanish 33 | * Supported: ÄËÏÖÜŸäëïöüÿÁÉÍÓÚáéíóúÀÈÌÒÙàèìòùÂÊÎÔÛâêîôûÑñ 34 | * Unsupported: ÃÕãõŇŘŠňřšǸǹŃńŮů (incomplete list) 35 | 36 | U.S. Extended 37 | * Supported: ÄËÏÖÜŸäëïöüÿÁÉÍÓÚáéíóúÀÈÌÒÙàèìòùÂÊÎÔÛâêîôûÃÕÑãõñŃńǸǹŇňŘřŠšÅåŮů 38 | * Unsupported: [nothing] 39 | 40 | -------------------------------------------------------------------------------- /README.markdown: -------------------------------------------------------------------------------- 1 | cliclick Overview 2 | ========================= 3 | 4 | cliclick (short for “Command Line Interface Click”) is a tool for executing mouse- and keyboard-related actions from the shell/Terminal. It is written in Objective-C and runs on macOS 10.15 or later. 5 | 6 | Author: Carsten Blüm, [www.bluem.net](https://www.bluem.net/) 7 | 8 | Installation 9 | -------------- 10 | You can either build cliclick yourself (see below), choose a binary from the [releases](https://github.com/BlueM/cliclick/releases) or install it using either [Homebrew](https://formulae.brew.sh/formula/cliclick) or [MacPorts](https://ports.macports.org/port/cliclick/). 11 | 12 | *Important:* it is necessary to give Terminal (or iTerm or whatever you use) the permission to control the computer. This can be done in System Preferences ➔ Security ➔ Accessibility. If you invoke cliclick and nothing happens, the permission is probably missing. Since cliclick 5.1, in this case a warning will be written to stderr. 13 | 14 | Usage 15 | --------- 16 | To get a quick first impression, this is what you will get when you invoke `cliclick -h`: 17 | 18 | USAGE 19 | cliclick [-r] [-m ] [-d ] [-e ] [-f ] [-w ] command1 [command2] 20 | 21 | OPTIONS 22 | -r Restore initial mouse location when finished 23 | -m The mode can be either “verbose” (cliclick will print a 24 | description of each action to stdout just before it is 25 | performed) or “test” (cliclick will only print the 26 | description, but not perform the action) 27 | -d Specify the target when using the “p” (“print”) command. 28 | Possible values are: stdout, stderr, clipboard or the path 29 | to a file (which will be overwritten if it exists). 30 | By default (if option not given), stdout is used for printing 31 | -e Set an easing factor for mouse movements. The higher this 32 | value is (default: 0), the more will mouse movements seem 33 | “natural” or “human-like”, which also implies: will be slower. 34 | If this option is used, the actual speed will also depend 35 | on the distance between the start and the end position, i.e. 36 | the time needed for moving will be higher if the distance 37 | is larger. 38 | -f Instead of passing commands as arguments, you may instead 39 | specify a file from which cliclick will read the commands 40 | (or stdin, when - is given as filename). 41 | Each line in the file is expected to contain a command 42 | in the same format/syntax as commands given as arguments 43 | at the shell. Additionally, lines starting with the hash 44 | character # are regarded as comments, i.e.: ignored. Leading 45 | and trailing whitespace is ignored, too. 46 | -w Wait the given number of milliseconds after each event. 47 | If you find that you use the “w” command too often, 48 | using -w could make things easier. Please note that “w” 49 | is additive with -w. This means that invoking 50 | “cliclick -w 200 w:500” will wait for 700 milliseconds. 51 | The default (and minimum) value for -w is 20. 52 | -V Show cliclick version number and release date 53 | -o Open version history in a browser 54 | -n Send a donation 55 | 56 | COMMANDS 57 | To use cliclick, you pass an arbitrary number of commands as arguments. A command consists of a command identifier (a string that tells cliclick what kind of action to perform) and usually one or more arguments to the command, which are separated from the command identifier with a colon. Example: “c:123,456” is the command for clicking (the “c” is the command identifier for clicking) at the position with x coordinate 123 and y coordinate 456. See below for a list of all commands and the arguments they expect. 58 | Whenever a command expects a pair of coordinates, you may provide relative values by prefixing the number with “+” or “-”. For example, “m:+50,+0” will move the mouse 50 pixels to the right. Of course, relative and absolute values can be mixed, and negative values are possible, so “c:100,-20” would be perfectly valid. (If you need to specify absolute negative values in case you have a setup with a second display arranged to the left of your main display, prefix the number with “=”, for instance “c:100,=-200”.) 59 | 60 | LIST OF COMMANDS 61 | 62 | rc:x,y Will RIGHT-CLICK at the point with the given coordinates. 63 | Example: “rc:12,34” will right-click at the point with x coordinate 64 | 12 and y coordinate 34. Instead of x and y values, you may 65 | also use “.”, which means: the current position. Using “.” is 66 | equivalent to using relative zero values “c:+0,+0”. 67 | 68 | m:x,y Will MOVE the mouse to the point with the given coordinates. 69 | Example: “m:12,34” will move the mouse to the point with 70 | x coordinate 12 and y coordinate 34. 71 | 72 | kd:keys Will trigger a KEY DOWN event for a comma-separated list of 73 | modifier keys. Possible keys are: 74 | - alt 75 | - cmd 76 | - ctrl 77 | - fn 78 | - shift 79 | Example: “kd:cmd,alt” will press the command key and the 80 | option key (and will keep them down until you release them 81 | with another command) 82 | 83 | kp:key Will emulate PRESSING A KEY (key down + key up). Possible keys are: 84 | - arrow-down 85 | - arrow-left 86 | - arrow-right 87 | - arrow-up 88 | - brightness-down 89 | - brightness-up 90 | - delete 91 | - end 92 | - enter 93 | - esc 94 | - f1 95 | - f2 96 | - f3 97 | - f4 98 | - f5 99 | - f6 100 | - f7 101 | - f8 102 | - f9 103 | - f10 104 | - f11 105 | - f12 106 | - f13 107 | - f14 108 | - f15 109 | - f16 110 | - fwd-delete 111 | - home 112 | - keys-light-down 113 | - keys-light-toggle 114 | - keys-light-up 115 | - mute 116 | - num-0 117 | - num-1 118 | - num-2 119 | - num-3 120 | - num-4 121 | - num-5 122 | - num-6 123 | - num-7 124 | - num-8 125 | - num-9 126 | - num-clear 127 | - num-divide 128 | - num-enter 129 | - num-equals 130 | - num-minus 131 | - num-multiply 132 | - num-plus 133 | - page-down 134 | - page-up 135 | - play-next 136 | - play-pause 137 | - play-previous 138 | - return 139 | - space 140 | - tab 141 | - volume-down 142 | - volume-up 143 | Example: “kp:return” will hit the return key. 144 | 145 | tc:x,y Will TRIPLE-CLICK at the point with the given coordinates. 146 | Example: “tc:12,34” will triple-click at the point with x 147 | coordinate 12 and y coordinate 34. Instead of x and y values, 148 | you may also use “.”, which means: the current position. 149 | Note: If you find that this does not work in a target application, 150 | please try if double-clicking plus single-clicking does. 151 | 152 | ku:keys Will trigger a KEY UP event for a comma-separated list of 153 | modifier keys. Possible keys are: 154 | - alt 155 | - cmd 156 | - ctrl 157 | - fn 158 | - shift 159 | Example: “ku:cmd,ctrl” will release the command key and the 160 | control key (which will only have an effect if you performed 161 | a “key down” before) 162 | 163 | dm:x,y Will continue the DRAG event to the given coordinates. 164 | Example: “dm:112,134” will drag and continue to the point with x 165 | coordinate 112 and y coordinate 134. 166 | 167 | c:x,y Will CLICK at the point with the given coordinates. 168 | Example: “c:12,34” will click at the point with x coordinate 169 | 12 and y coordinate 34. Instead of x and y values, you may 170 | also use “.”, which means: the current position. Using “.” is 171 | equivalent to using relative zero values “c:+0,+0”. 172 | 173 | dd:x,y Will press down to START A DRAG at the given coordinates. 174 | Example: “dd:12,34” will press down at the point with x 175 | coordinate 12 and y coordinate 34. Instead of x and y values, 176 | you may also use “.”, which means: the current position. 177 | 178 | w:ms Will WAIT/PAUSE for the given number of milliseconds. 179 | Example: “w:500” will pause command execution for half a second 180 | 181 | p[:str] Will PRINT the given string. If the string is “.”, the current 182 | MOUSE POSITION is printed. As a convenience, you can skip the 183 | string completely and just write “p” to get the current position. 184 | Example: “p:.” or “p” will print the current mouse position 185 | Example: “p:'Hello world'” will print “Hello world” 186 | 187 | du:x,y Will release to END A DRAG at the given coordinates. 188 | Example: “du:112,134” will release at the point with x 189 | coordinate 112 and y coordinate 134. 190 | 191 | cp:str Will PRINT THE COLOR value at the given screen location. 192 | The color value is printed as three decimal 8-bit values, 193 | representing, in order, red, green, and blue. 194 | Example: “cp:123,456” might print “127 63 0” 195 | 196 | dc:x,y Will DOUBLE-CLICK at the point with the given coordinates. 197 | Example: “dc:12,34” will double-click at the point with x 198 | coordinate 12 and y coordinate 34. Instead of x and y values, 199 | you may also use “.”, which means: the current position. 200 | 201 | t:text Will TYPE the given TEXT into the frontmost application. 202 | If the text includes space(s), it must be enclosed in quotes. 203 | Example: “type:Test” will type “Test” 204 | Example: “type:'Viele Grüße'” will type “Viele Grüße” 205 | 206 | Limitations 207 | ----------- 208 | * It is not possible to use cliclick before a user logs in, i.e.: to control the login window. 209 | * In Citrix Viewer, clicks are not recognized. Moving the mouse works, but this seems futile without being able to click. 210 | 211 | 212 | Building cliclick 213 | ----------------- 214 | Either build in Xcode, as usual, or build from the shell by `cd`ing into the project directory and then invoking either `xcodebuild` or `make` (whatever you prefer). In either case, cliclick will not be installed, but you will simply get an executable called “cliclick” in the project directory which you can then move wherever you want to have it. (You can put it anywhere you like.) To install it to `/usr/local/bin`, you can also simply invoke `sudo make install`, which will do this for you. 215 | 216 | Please note that while the code will run on OS X 10.9 and later, Base SDK and architectures selected in the Xcode project are set to the current SDK. Therefore, if you want to build for an older system, be sure to change these settings accordingly. If you have problems when building and get a message complaining about undefined symbols, chances are that this can be fixed by disabling “Implicitly link Objective-C Runtime Support” in the build settings. 217 | 218 | Contributing 219 | ------------ 220 | If you would like to contribute a new feature, a bugfix or other improvement, please do so using a pull request. However, please take care that: 221 | 222 | * There is one pull request per topic. I.e.: if you would like to contribute a new feature and two bugfixes, open three pull requests. 223 | * All commit messages are in English. 224 | * Ideally, all non-obvious features or changes should be shortly explained. This might not only include *what* you committed, but also *why* you did it (motivation, usage scenario, …). 225 | -------------------------------------------------------------------------------- /Test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | cliclickPath="$1" 4 | 5 | if [[ "" = "$cliclickPath" ]] 6 | then 7 | echo "Usage: $0 /path/to/cliclick" >&2 8 | exit 1 9 | fi 10 | 11 | if [[ ! -x "$cliclickPath" ]] 12 | then 13 | echo "$cliclickPath does not exist or is not executable" >&2 14 | exit 1 15 | fi 16 | 17 | 18 | function assertStdoutOutput() { 19 | actual=$("$cliclickPath" $2) 20 | runStringComparison "$1" "$3" "$actual" 21 | } 22 | 23 | function assertStderrOutput() { 24 | actual=$("$cliclickPath" $2 3>&1 1>&2 2>&3) 25 | runStringComparison "$1" "$3" "$actual" 26 | } 27 | 28 | function assertClipboardContains() { 29 | local testDescription="$1" 30 | local expectedString="$2" 31 | 32 | pbContents=$(pbpaste) 33 | 34 | if [[ "$expectedString" = "$pbContents" ]] 35 | then 36 | echo "✅ $testDescription" 37 | else 38 | echo "⚠️ Failed test: $testDescription" 39 | echo " Expected: $expectedString" 40 | echo " Actual: $pbContents" 41 | fi 42 | } 43 | 44 | function assertFileContains() { 45 | local testDescription="$1" 46 | local path="$2" 47 | local expectedString="$3" 48 | fileContents=$(cat "$path") 49 | runStringComparison "$1" "$fileContents" "$3" 50 | } 51 | 52 | function runStringComparison { 53 | local testDescription="$1" 54 | local expected="$2" 55 | local actual="$3" 56 | local expectedLength=${#expected} 57 | 58 | if [[ "$expected" = ${actual:0:$expectedLength} ]] 59 | then 60 | echo "✅ $testDescription" 61 | else 62 | echo "⚠️ Failed test: $testDescription" 63 | echo " Expected: $expected" 64 | echo " Actual: $actual" 65 | fi 66 | } 67 | 68 | # No arguments 69 | assertStderrOutput \ 70 | "When called without argument, should write info to stderr" \ 71 | "" \ 72 | "You did not pass any commands as argument to cliclick."$'\n'"Call cliclick with option -h to see usage instructions." 73 | 74 | assertStderrOutput \ 75 | "When called with an unknown action, should write error to stderr" \ 76 | "z" \ 77 | "Unrecognized action shortcut “z”" 78 | 79 | 80 | # Assertions for move / m command 81 | assertStderrOutput \ 82 | "When using “move” (“m”) without coordinates, should write error to stderr" \ 83 | "m" \ 84 | "Missing argument to command “m”: Expected two coordinates (separated by a comma) or “.”" 85 | 86 | assertStderrOutput \ 87 | "When using “move” (“m”) with colon, but without coords, should write error to stderr" \ 88 | "m:" \ 89 | "Missing argument to command “m”: Expected two coordinates (separated by a comma) or “.”. Examples: “m:123,456” or “m:.”" 90 | 91 | assertStderrOutput \ 92 | "When using “move” (“m”) with invalid X value, should write error to stderr" \ 93 | "m:foobar,-123" \ 94 | "Invalid X axis coordinate “foobar” given" 95 | 96 | assertStderrOutput \ 97 | "When using “move” (“m”) with invalid Y value, should write error to stderr" \ 98 | "m:+123,xxx" \ 99 | "Invalid Y axis coordinate “xxx” given" 100 | 101 | assertStderrOutput \ 102 | "When using “move” (“m”) with missing Y value, should write error to stderr" \ 103 | "m:+123," \ 104 | "Invalid argument “+123,” to command “m”: Expected two coordinates (separated by a comma) or “.”. Examples: “m:123,456” or “m:.”" 105 | 106 | 107 | # Move, testing mode 108 | assertStdoutOutput \ 109 | "When using “move” (“m”) in test mode, should write action to stdout" \ 110 | "-m test m:123,456" \ 111 | "Running in test mode. These command(s) would be executed:"$'\n'"Move to 123,456" 112 | 113 | assertStdoutOutput \ 114 | "When using “move” (“m”) in test mode with relative coords, should write action to stdout" \ 115 | "-m test m:+20,-17" \ 116 | "Running in test mode. These command(s) would be executed:"$'\n'"Move to +20,-17" 117 | 118 | 119 | # Assertions for click / c command 120 | assertStderrOutput \ 121 | "When using “click” (“c”) without coords, should write error to stderr" \ 122 | "c" \ 123 | "Missing argument to command “c”: Expected two coordinates (separated by a comma) or “.”. Examples: “c:123,456” or “c:.”" 124 | 125 | assertStderrOutput \ 126 | "When using “click” (“c”) with colon, but without coordinates, should write error to stderr" \ 127 | "c" \ 128 | "Missing argument to command “c”: Expected two coordinates (separated by a comma) or “.”. Examples: “c:123,456” or “c:.”" 129 | 130 | assertStderrOutput \ 131 | "When using “click” (“c”) with invalid X value, should write error to stderr" \ 132 | "c:foobar,-123" \ 133 | "Invalid X axis coordinate “foobar” given" 134 | 135 | assertStderrOutput \ 136 | "When using “click” (“c”) with invalid Y value, should write error to stderr" \ 137 | "c:+123,xxx" \ 138 | "Invalid Y axis coordinate “xxx” given" 139 | 140 | assertStderrOutput \ 141 | "When using “click” (“c”) with missing Y value, should write error to stderr" \ 142 | "c:+123," \ 143 | "Invalid argument “+123,” to command “c”: Expected two coordinates (separated by a comma) or “.”. Examples: “c:123,456” or “c:.”" 144 | 145 | assertStdoutOutput \ 146 | "When using “click” (“c”) in test mode, should write action to stdout" \ 147 | "-m test c:123,456" \ 148 | "Running in test mode. These command(s) would be executed:"$'\n'"Click at 123,456" 149 | 150 | assertStdoutOutput \ 151 | "When using “click” (“c”) in test mode with relative coords, should write action to stdout" \ 152 | "-m test c:+20,-17" \ 153 | "Running in test mode. These command(s) would be executed:"$'\n'"Click at +20,-17" 154 | 155 | assertStdoutOutput \ 156 | "When using “click” (“c”) in test mode with absolute negative coords, should write action to stdout" \ 157 | "-m test c:=-200,-100" \ 158 | "Running in test mode. These command(s) would be executed:"$'\n'"Click at =-200,-100" 159 | 160 | 161 | # Assertions for double-click / command 162 | assertStderrOutput \ 163 | "When using “doubleclick” (“dc”) without coords, should write error to stderr" \ 164 | "dc" \ 165 | "Missing argument to command “dc”: Expected two coordinates (separated by a comma) or “.”. Examples: “dc:123,456” or “dc:.”" 166 | 167 | assertStdoutOutput \ 168 | "When using “doubleclick” (“dc”) in test mode, should write action to stdout" \ 169 | "-m test dc:1129,64" \ 170 | "Running in test mode. These command(s) would be executed:"$'\n'"Double-click at 1129,64" 171 | 172 | 173 | # Assertions for triple-click / command 174 | assertStderrOutput \ 175 | "When using “tripleclick” (“tc”) without coords, should write error to stderr" \ 176 | "tc" \ 177 | "Missing argument to command “tc”: Expected two coordinates (separated by a comma) or “.”. Examples: “tc:123,456” or “tc:.”" 178 | 179 | assertStdoutOutput \ 180 | "When using “tripleclick” (“tc”) in test mode, should write action to stdout" \ 181 | "-m test tc:1129,64" \ 182 | "Running in test mode. These command(s) would be executed:"$'\n'"Triple-click at 1129,64" 183 | 184 | 185 | # Assertions for right-click / command 186 | assertStderrOutput \ 187 | "When using “rightclick” (“rc”) without coords, should write error to stderr" \ 188 | "rc" \ 189 | "Missing argument to command “rc”: Expected two coordinates (separated by a comma) or “.”. Examples: “rc:123,456” or “rc:.”" 190 | 191 | assertStdoutOutput \ 192 | "When using “rightclick” (“rc”) in test mode, should write action to stdout" \ 193 | "-m test rc:1129,64" \ 194 | "Running in test mode. These command(s) would be executed:"$'\n'"Right-click at 1129,64" 195 | 196 | 197 | # Assertions for drag down / dd command 198 | assertStderrOutput \ 199 | "When using “drag down” (“dd) without coords, should write error to stderr" \ 200 | "dd" \ 201 | "Missing argument to command “dd”: Expected two coordinates (separated by a comma) or “.”. Examples: “dd:123,456” or “dd:.”" 202 | 203 | assertStdoutOutput \ 204 | "When using “drag down” (“dd”) in test mode, should write action to stdout" \ 205 | "-m test dd:1129,64" \ 206 | "Running in test mode. These command(s) would be executed:"$'\n'"Drag press down at 1129,64" 207 | 208 | # Assertions for drag up / du command 209 | assertStderrOutput \ 210 | "When using “drag up” (“du) without coords, should write error to stderr" \ 211 | "du:" \ 212 | "Missing argument to command “du”: Expected two coordinates (separated by a comma) or “.”. Examples: “du:123,456” or “du:.”" 213 | 214 | assertStdoutOutput \ 215 | "When using “drag up” (“rc”) in test mode, should write action to stdout" \ 216 | "-m test du:1129,64" \ 217 | "Running in test mode. These command(s) would be executed:"$'\n'"Drag release at 1129,64" 218 | 219 | 220 | # Assertions for drag move / dm command 221 | assertStderrOutput \ 222 | "When using “drag move” (“dm”) without coords, should write error to stderr" \ 223 | "dm" \ 224 | "Missing argument to command “dm”: Expected two coordinates (separated by a comma) or “.”. Examples: “dm:123,456” or “dm:.”" 225 | 226 | assertStdoutOutput \ 227 | "When using “drag move (“dm”) in test mode, should write action to stdout" \ 228 | "-m test dm:1129,64" \ 229 | "Running in test mode. These command(s) would be executed:"$'\n'"Drag move to 1129,64" 230 | 231 | 232 | # Assertions for wait / w command 233 | assertStderrOutput \ 234 | "When using “wait” (“w”) without argument, should write error to stderr" \ 235 | "w" \ 236 | "Invalid or missing argument to command “w”: Expected number of milliseconds. Example: “w:50”" 237 | 238 | assertStderrOutput \ 239 | "When using “wait” (“w”) with a non-numeric argument, should write error to stderr" \ 240 | "w:abc" \ 241 | "Invalid or missing argument to command “w”: Expected number of milliseconds. Example: “w:50”" 242 | 243 | assertStdoutOutput \ 244 | "When using “wait” (“w”) in test mode, should write the action to stdout" \ 245 | "-m test w:150" \ 246 | "Running in test mode. These command(s) would be executed:"$'\n'"Wait 150 milliseconds" 247 | 248 | 249 | # Assertions for key down / kd command 250 | assertStderrOutput \ 251 | "When using “key down” (“kd”) without argument, should write error to stderr" \ 252 | "kd" \ 253 | "Missing argument to command “kd”: Expected one or more keys (separated by a comma). Examples: “kd:ctrl” or “kd:cmd,alt”" 254 | 255 | assertStderrOutput \ 256 | "When using “key down” (“kd”) with an invalid key, should write error to stderr" \ 257 | "kd:abc" \ 258 | "Invalid key “abc” given as argument to command “kd”."$'\n'"The key name may only be one of:" 259 | 260 | assertStdoutOutput \ 261 | "When using “key down” (“kd”) in test mode, should write the action stdout" \ 262 | "-m test kd:ctrl" \ 263 | "Running in test mode. These command(s) would be executed:"$'\n'"Hold ctrl key down" 264 | 265 | assertStdoutOutput \ 266 | "When using “key down” (“kd”) in test mode several valid keys, should write the action stdout" \ 267 | "-m test kd:ctrl,cmd,alt" \ 268 | "Running in test mode. These command(s) would be executed:"$'\n'"Hold ctrl key down"$'\n'"Hold cmd key down"$'\n'"Hold alt key down" 269 | 270 | 271 | # Assertions for key up / ku command 272 | assertStderrOutput \ 273 | "When using “key up” (“ku”) without argument, should write error to stderr" \ 274 | "ku" \ 275 | "Missing argument to command “ku”" 276 | 277 | assertStderrOutput \ 278 | "When using “key up” (“ku”) with an invalid key, should write error to stderr" \ 279 | "ku:abc" \ 280 | "Invalid key “abc” given as argument to command “ku”" 281 | 282 | assertStdoutOutput \ 283 | "When using “key up” (“ku”) in test mode, should write the action to stdout" \ 284 | "-m test ku:ctrl" \ 285 | "Running in test mode. These command(s) would be executed:"$'\n'"Release ctrl key" 286 | 287 | assertStdoutOutput \ 288 | "When using “key up” (“ku”) in test mode with several valid keys, should write the action stdout" \ 289 | "-m test ku:ctrl,cmd,alt" \ 290 | "Running in test mode. These command(s) would be executed:"$'\n'"Release ctrl key"$'\n'"Release cmd key"$'\n'"Release alt key" 291 | 292 | 293 | # Assertions for key press / kp command 294 | assertStderrOutput \ 295 | "When using “key press” (“kp”) argument, should write error to stderr" \ 296 | "kp" \ 297 | "Missing argument to command “kp”" 298 | 299 | assertStderrOutput \ 300 | "When using “key press” (“kp”) with an invalid key, should write error to stderr" \ 301 | "kp:abc" \ 302 | "Invalid key “abc” given as argument to command “kp”" 303 | 304 | assertStdoutOutput \ 305 | "When using “key press” (“kp”) in test mode, should write the action to stdout" \ 306 | "-m test kp:return" \ 307 | "Running in test mode. These command(s) would be executed:"$'\n'"Press + release return key" 308 | 309 | 310 | # Assertions for print / p command 311 | assertStdoutOutput \ 312 | "When using “print” (“p”) in test mode without argument, should write action to stdout" \ 313 | "-m test p" \ 314 | "Running in test mode. These command(s) would be executed:"$'\n'"Print the current mouse position" 315 | 316 | assertStdoutOutput \ 317 | "When using “print” (“p”) with a dot as argument, should write action to stdout" \ 318 | "-m test p:." \ 319 | "Running in test mode. These command(s) would be executed:"$'\n'"Print the current mouse position" \ 320 | 321 | assertStdoutOutput \ 322 | "When using “print” (“p”) with a string, should print the string" \ 323 | "-m test p:Helloworld" \ 324 | "Running in test mode. These command(s) would be executed:"$'\n'"Print message “Helloworld”" 325 | 326 | # Assertions for type / t command 327 | assertStderrOutput \ 328 | "When using “type” (“t”) without argument, should write error to stderr" \ 329 | "t" \ 330 | "Missing argument to command “t”" 331 | 332 | assertStdoutOutput \ 333 | "When using “type” (“t”) in test mode with a string as argument, should write the action to stdout" \ 334 | "-m test t:Unimaginatively" \ 335 | "Running in test mode. These command(s) would be executed:"$'\n'"Type: “Unimaginatively”" 336 | 337 | 338 | # Assertions for color picker / cp command 339 | assertStderrOutput \ 340 | "When using “color picker (“cp”) without argument, should write error to stderr" \ 341 | "cp" \ 342 | "Missing argument to command “cp”" 343 | 344 | assertStderrOutput \ 345 | "When using “color picker (“cp”) with invalid coordinates, should write error to stderr" \ 346 | "cp:123" \ 347 | "Invalid argument “123” to command “cp”" 348 | 349 | assertStdoutOutput \ 350 | "When using color” (“cp”) in test mode with a dot as argument, should write the action to stdout" \ 351 | "-m test cp:." \ 352 | "Running in test mode. These command(s) would be executed:"$'\n'"Print color at current mouse position" 353 | 354 | assertStdoutOutput \ 355 | "When using color” (“cp”) in test mode with coordinates as argument, should write the action to stdout" \ 356 | "-m test cp:123,456" \ 357 | "Running in test mode. These command(s) would be executed:"$'\n'"Print color at location 123,456" 358 | 359 | assertStderrOutput \ 360 | "When setting the output destination for test mode to stderr, should write the action to stderr" \ 361 | "-m test:stderr c:. m:123,456" \ 362 | "Running in test mode. These command(s) would be executed:"$'\n'"Click at current location"$'\n'"Move to 123,456" 363 | 364 | 365 | # Test writing verbose messages to a file 366 | assertStderrOutput \ 367 | "When setting the output destination for test mode to an invalid path, should write the error to stderr" \ 368 | "-m test:/no/such/path.txt c:. m:123,456" \ 369 | "Cannot create file “/no/such/path.txt” specified as output destination" 370 | 371 | 372 | tempfilePath=$(mktemp) 373 | 374 | assertStdoutOutput \ 375 | "When setting output destination for test mode to a file, should write nothing to stdout" \ 376 | "-m test:$tempfilePath c:. m:123,456" \ 377 | "" 378 | 379 | assertFileContains \ 380 | "When setting output destination for test mode to a file, the file should contain the commands" \ 381 | "$tempfilePath" \ 382 | "Running in test mode. These command(s) would be executed:"$'\n'"Click at current location"$'\n'"Move to 123,456" 383 | 384 | # Test writing verbose messages to the clipboard 385 | assertStdoutOutput \ 386 | "When setting output destination for test mode to the clipboard, should write nothing to stdout" \ 387 | "-m test:clipboard c:. m:123,456" \ 388 | "" 389 | 390 | assertClipboardContains \ 391 | "When setting output destination for test mode to the clipboard, the clipboard should contain the commands" \ 392 | "Running in test mode. These command(s) would be executed:"$'\n'"Click at current location"$'\n'"Move to 123,456" 393 | 394 | 395 | # Test setting command output destination using -d 396 | assertStderrOutput \ 397 | "When setting destination for command output to stderr, should write action to stderr" \ 398 | "-d stderr p:OK" \ 399 | "OK" 400 | 401 | # Test setting command output destination using -d 402 | assertStdoutOutput \ 403 | "When setting destination for command output to stdout, should write the action to stdout" \ 404 | "-d stdout p:OK" \ 405 | "OK" 406 | -------------------------------------------------------------------------------- /cliclick.m: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2007-2021, Carsten Blüm 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * - Redistributions of source code must retain the above copyright notice, this 9 | * list of conditions and the following disclaimer. 10 | * - Redistributions in binary form must reproduce the above copyright notice, this 11 | * list of conditions and the following disclaimer in the documentation and/or 12 | * other materials provided with the distribution. 13 | * - Neither the name of Carsten Blüm nor the names of his contributors may be 14 | * used to endorse or promote products derived from this software without specific 15 | * prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 20 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 21 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 23 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 24 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 25 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | */ 28 | 29 | #include 30 | #include 31 | #include 32 | #include 33 | #import 34 | #import "ActionExecutor.h" 35 | #import "MoveAction.h" 36 | #import "OutputHandler.h" 37 | #import "ExecutionOptions.h" 38 | 39 | void error(void); 40 | void help(void); 41 | 42 | NSArray* parseCommandsFile(NSString *filepath); 43 | 44 | int main (int argc, const char * argv[]) { 45 | 46 | NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; 47 | 48 | struct ExecutionOptions executionOptions; 49 | executionOptions.easing = 0; 50 | executionOptions.waitTime = 0; 51 | executionOptions.mode = MODE_REGULAR; 52 | NSArray *modeOptionArg; 53 | NSString *verbosityOutputDestination = nil; 54 | NSString *filepath = nil; 55 | NSString *commandOutputDestination = nil; 56 | NSArray *actions; 57 | CGPoint initialMousePosition; 58 | BOOL restoreOption = NO; 59 | int optchar; 60 | 61 | if (! AXIsProcessTrusted()) { 62 | fprintf(stderr, "WARNING: Accessibility privileges not enabled. Many actions may fail.\n"); 63 | fprintf(stderr, " Enable the checkbox for the containing app in:\n"); 64 | fprintf(stderr, " System Preferences → Security & Privacy → Accessibility\n"); 65 | } 66 | 67 | while ((optchar = getopt(argc, (char * const *)argv, "horVne:f:d:m:w:")) != -1) { 68 | switch(optchar) { 69 | case 'h': 70 | help(); 71 | [pool release]; 72 | return EXIT_SUCCESS; 73 | case 'V': 74 | printf("%s\n", [[NSString stringWithFormat:@"cliclick %@, %@", VERSION, RELEASEDATE] UTF8String]); 75 | [pool release]; 76 | return EXIT_SUCCESS; 77 | case 'n': 78 | [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:DONATIONS_URL]]; 79 | [pool release]; 80 | return EXIT_SUCCESS; 81 | case 'o': 82 | [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:HISTORY_URL]]; 83 | [pool release]; 84 | return EXIT_SUCCESS; 85 | case 'm': 86 | modeOptionArg = [[NSString stringWithCString:optarg encoding:NSASCIIStringEncoding] componentsSeparatedByString:@":"]; 87 | if ([[modeOptionArg objectAtIndex:0] isEqualToString:@"verbose"]) { 88 | executionOptions.mode = MODE_VERBOSE; 89 | } else if ([[modeOptionArg objectAtIndex:0] isEqualToString:@"test"]) { 90 | executionOptions.mode = MODE_TEST; 91 | } else { 92 | fprintf(stderr, "Only “verbose” or “test” are valid values for the -m argument\n"); 93 | [pool release]; 94 | return EXIT_FAILURE; 95 | } 96 | if ([modeOptionArg count] > 1 && [modeOptionArg objectAtIndex:1]) { 97 | verbosityOutputDestination = [modeOptionArg objectAtIndex:1]; 98 | } 99 | break; 100 | case 'e': 101 | executionOptions.easing = atoi(optarg) > 0 ? atoi(optarg) : 0; 102 | break; 103 | case 'f': 104 | filepath = [NSString stringWithCString:optarg encoding:NSASCIIStringEncoding]; 105 | break; 106 | case 'd': 107 | commandOutputDestination = [NSString stringWithCString:optarg encoding:NSASCIIStringEncoding]; 108 | break; 109 | case 'r': 110 | restoreOption = YES; 111 | break; 112 | case 'w': 113 | executionOptions.waitTime = atoi(optarg) > 0 ? atoi(optarg) : 0; 114 | break; 115 | default: 116 | [pool release]; 117 | return EXIT_FAILURE; 118 | } 119 | } 120 | 121 | @try { 122 | executionOptions.commandOutputHandler = [[OutputHandler alloc] initWithTarget:commandOutputDestination]; 123 | executionOptions.verbosityOutputHandler = [[OutputHandler alloc] initWithTarget:verbosityOutputDestination]; 124 | } 125 | @catch (NSException *e) { 126 | fprintf(stderr, "%s\n", [[e reason] UTF8String]); 127 | [pool release]; 128 | return EXIT_FAILURE; 129 | } 130 | 131 | if (restoreOption) { 132 | CGEventRef ourEvent = CGEventCreate(NULL); 133 | initialMousePosition = CGEventGetLocation(ourEvent); 134 | } 135 | 136 | if (optind == argc && !filepath) { 137 | error(); 138 | [pool release]; 139 | return EXIT_FAILURE; 140 | } 141 | 142 | if (executionOptions.mode == MODE_TEST) { 143 | [executionOptions.verbosityOutputHandler write:@"Running in test mode. These command(s) would be executed:"]; 144 | } 145 | 146 | if (filepath) { 147 | NSFileManager *fm = [NSFileManager defaultManager]; 148 | if ([filepath isEqualToString:@""]) { 149 | fprintf(stderr, "Option -f expects a path: -f /path/to/the/file\n"); 150 | [pool release]; 151 | return EXIT_FAILURE; 152 | } 153 | if (![filepath isEqualToString:@"-"] && ![fm fileExistsAtPath:filepath]) { 154 | fprintf(stderr, "There is no file at %s\n", [filepath UTF8String]); 155 | [pool release]; 156 | return EXIT_FAILURE; 157 | } 158 | actions = parseCommandsFile(filepath); 159 | } else { 160 | NSArray *arguments = [[NSProcessInfo processInfo] arguments]; 161 | actions = [arguments subarrayWithRange:NSMakeRange(optind, argc - optind)]; 162 | } 163 | 164 | @try { 165 | [ActionExecutor executeActions:actions withOptions:executionOptions]; 166 | } 167 | @catch (NSException *e) { 168 | fprintf(stderr, "%s\n", [[e reason] UTF8String]); 169 | [pool release]; 170 | return EXIT_FAILURE; 171 | } 172 | 173 | if (restoreOption) { 174 | // Note: we have to prefix the coordinates with "=" to ensure they are not interpreted 175 | // as relative values, see https://github.com/BlueM/cliclick/issues/119 176 | NSString *positionString = [NSString stringWithFormat:@"=%d,=%d", (int)initialMousePosition.x, (int)initialMousePosition.y]; 177 | id moveAction = [[MoveAction alloc] init]; 178 | [moveAction performActionWithData:positionString 179 | withOptions:executionOptions]; 180 | [moveAction release]; 181 | } 182 | 183 | [pool release]; 184 | return EXIT_SUCCESS; 185 | } 186 | 187 | NSArray* parseCommandsFile(NSString *filepath) { 188 | NSMutableArray *commands = [[NSMutableArray alloc] initWithCapacity:32]; 189 | NSArray *lines; 190 | 191 | if ([filepath isEqualToString:@"-"]) { 192 | // stdin 193 | NSData *stdinData = [[NSFileHandle fileHandleWithStandardInput] readDataToEndOfFile]; 194 | NSString *configString = [[NSString alloc] initWithData:stdinData encoding:NSUTF8StringEncoding]; 195 | lines = [configString componentsSeparatedByCharactersInSet:[NSCharacterSet newlineCharacterSet]]; 196 | [configString release]; 197 | } else { 198 | // File 199 | NSString *fileContents = [NSString stringWithContentsOfFile:filepath 200 | encoding:NSUTF8StringEncoding 201 | error:nil]; 202 | lines = [fileContents componentsSeparatedByString:@"\n"]; 203 | } 204 | 205 | NSUInteger i, count = [lines count]; 206 | for (i = 0; i < count; i++) { 207 | NSString *command = [[lines objectAtIndex:i] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]]; 208 | if ([command isEqualToString:@""]) { 209 | continue; 210 | } 211 | if ([[command substringToIndex:1] isEqualToString:@"#"]) { 212 | continue; 213 | } 214 | [commands addObject:command]; 215 | } 216 | 217 | return [commands autorelease]; 218 | } 219 | 220 | void error() { 221 | fprintf(stderr, "You did not pass any commands as argument to cliclick.\n"); 222 | fprintf(stderr, "Call cliclick with option -h to see usage instructions.\n"); 223 | } 224 | 225 | void help() { 226 | 227 | NSArray *actionClasses = [ActionExecutor actionClasses]; 228 | NSUInteger i, count = [actionClasses count]; 229 | 230 | NSString *help = @"\ncliclick (short for “Command Line Interface Click”) is a tool for " 231 | "executing mouse- and keyboard-related actions from the shell/Terminal\n" 232 | "\n" 233 | "USAGE\n" 234 | " cliclick [-r] [-m ] [-d ] [-e ] [-f ] [-w ] command1 [command2]\n" 235 | "\n" 236 | "OPTIONS\n" 237 | " -r Restore initial mouse location when finished\n" 238 | " -m The mode can be either “verbose” (cliclick will print a\n" 239 | " description of each action to stdout just before it is\n" 240 | " performed) or “test” (cliclick will only print the\n" 241 | " description, but not perform the action)\n" 242 | " -d Specify the target when using the “p” (“print”) command.\n" 243 | " Possible values are: stdout, stderr, clipboard or the path \n" 244 | " to a file (which will be overwritten if it exists).\n" 245 | " By default (if option not given), stdout is used for printing\n" 246 | " -e Set an easing factor for mouse movements. The higher this\n" 247 | " value is (default: 0), the more will mouse movements seem\n" 248 | " “natural” or “human-like”, which also implies: will be slower.\n" 249 | " If this option is used, the actual speed will also depend\n" 250 | " on the distance between the start and the end position, i.e.\n" 251 | " the time needed for moving will be higher if the distance\n" 252 | " is larger.\n" 253 | " -f Instead of passing commands as arguments, you may instead\n" 254 | " specify a file from which cliclick will read the commands\n" 255 | " (or stdin, when - is given as filename).\n" 256 | " Each line in the file is expected to contain a command\n" 257 | " in the same format/syntax as commands given as arguments\n" 258 | " at the shell. Additionally, lines starting with the hash\n" 259 | " character # are regarded as comments, i.e.: ignored. Leading\n" 260 | " and trailing whitespace is ignored, too.\n" 261 | " -w Wait the given number of milliseconds after each event.\n" 262 | " If you find that you use the “w” command too often,\n" 263 | " using -w could make things easier. Please note that “w”\n" 264 | " is additive with -w. This means that invoking\n" 265 | " “cliclick -w 200 w:500” will wait for 700 milliseconds.\n" 266 | " The default (and minimum) value for -w is 20.\n" 267 | " -V Show cliclick version number and release date\n" 268 | " -o Open version history in a browser\n" 269 | " -n Send a donation\n" 270 | "\n" 271 | "COMMANDS\n" 272 | "To use cliclick, you pass an arbitrary number of commands as arguments. A command consists of a " 273 | "command identifier (a string that tells cliclick what kind of action to perform) and usually one " 274 | "or more arguments to the command, which are separated from the command identifier with a colon. " 275 | "Example: “c:123,456” is the command for clicking (the “c” is the command identifier for clicking) " 276 | "at the position with x coordinate 123 and y coordinate 456. See below for a list of all commands " 277 | "and the arguments they expect.\nWhenever a command expects a pair of coordinates, you may provide " 278 | "relative values by prefixing the number with “+” or “-”. For example, “m:+50,+0” will move the mouse 50 " 279 | "pixels to the right. Of course, relative and absolute values can be mixed, and negative values " 280 | "are possible, so “c:100,-20” would be perfectly valid. (If you need to specify absolute negative " 281 | "values in case you have a setup with a second display arranged to the left of your main display, " 282 | "prefix the number with “=”, for instance “c:100,=-200”.)\n\n" 283 | "LIST OF COMMANDS\n\n"; 284 | 285 | printf("%s", [help UTF8String]); 286 | 287 | for (i = 0; i < count; i++) { 288 | NSString *className = [actionClasses objectAtIndex:i]; 289 | printf("%s\n\n", [[NSClassFromString(className) commandDescription] UTF8String]); 290 | } 291 | 292 | NSString *author = [NSString stringWithFormat:@"Version %@, released %@\n" 293 | "Author: Carsten Blüm, \n" 294 | "List of contributors: https://github.com/BlueM/cliclick/graphs/contributors\n" 295 | "Website: https://www.bluem.net/jump/cliclick/\n\n", 296 | VERSION, 297 | RELEASEDATE]; 298 | printf("%s", [author UTF8String]); 299 | } 300 | -------------------------------------------------------------------------------- /cliclick_Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header for all source files of the 'cliclick' target in the 'cliclick' project. 3 | // 4 | 5 | #ifdef __OBJC__ 6 | #import 7 | #import 8 | #endif 9 | 10 | // Version or branch 11 | #define VERSION @"5.1" 12 | 13 | // Date as m/d/Y 14 | #define RELEASEDATE @"2022-08-14" 15 | 16 | #define MODE_REGULAR 0 17 | #define MODE_VERBOSE 1 18 | #define MODE_TEST 2 19 | 20 | #define MODIFIER_SHIFT 32 21 | #define MODIFIER_ALT 64 22 | #define MODIFIER_SHIFT_ALT (MODIFIER_SHIFT | MODIFIER_ALT) 23 | #define NSNUMBER_MODIFIER_SHIFT [NSNumber numberWithInt:MODIFIER_SHIFT] 24 | #define NSNUMBER_MODIFIER_ALT [NSNumber numberWithInt:MODIFIER_ALT] 25 | #define NSNUMBER_MODIFIER_SHIFT_ALT [NSNumber numberWithInt:MODIFIER_SHIFT_ALT] 26 | 27 | #define KEYCODE_SHIFT 56 28 | #define KEYCODE_ALT 58 29 | 30 | #define CHARINFO_URL_TEMPLATE @"https://github.com/BlueM/cliclick/blob/%@/README-Characters.md" 31 | #define HISTORY_URL @"https://github.com/BlueM/cliclick/releases" 32 | #define DONATIONS_URL @"https://www.bluem.net/jump/donations/" 33 | 34 | #ifdef DEBUG 35 | #define DLog(...) NSLog(@"%s %@", __PRETTY_FUNCTION__, [NSString stringWithFormat:__VA_ARGS__]) 36 | #else 37 | #define DLog(...) do { } while (0) 38 | #endif 39 | -------------------------------------------------------------------------------- /generate-action-classes-macro.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # If this script is not run by Xcode, PROJECT_DIR needs to be set 4 | if [ -z "$PROJECT_DIR" ]; then 5 | PROJECT_DIR="."; 6 | fi 7 | 8 | SCRIPTNAME=$(basename "$0") 9 | 10 | cd "$PROJECT_DIR/Actions" || exit 1 11 | echo -e "// DO NOT EDIT THIS FILE.\n// This file is written automatically by the script $SCRIPTNAME\n// and is triggered by an Xcode “Run Script” build phase or from the Makefile." > "../ActionClassesMacro.h" 12 | echo "#define ACTION_CLASSES \\" >> "../ActionClassesMacro.h" 13 | find . -name '*Action.m' ! -name '*BaseAction.m' | perl -p -e 's=^\./(.*)\.m$= @"$1", \\=;' >> "../ActionClassesMacro.h" 14 | echo " nil" >> "../ActionClassesMacro.h" 15 | -------------------------------------------------------------------------------- /generate-characterinfo.php: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | $info) { 34 | printf( 35 | "%s\n * Supported: %s\n * Unsupported: %s\n\n", 36 | str_replace('_', ' ', $layoutName), 37 | $info[0], 38 | $info[1] 39 | ); 40 | } 41 | --------------------------------------------------------------------------------