Mouse click at (100,100)

I’m working on my first AppleScriptObjC project and naturally getting a touch muddled ” I only know as much Cocoa as one can deduce by wandering around the X-Code documentation. Anyway, I want a windowless AppleScriptObjC app to produce a mouse click at a specific screen location, say (100, 100). I’ve actually figured out how to do this in Cocoa:

#import <Foundation/Foundation.h>
#import <ApplicationServices/ApplicationServices.h>

int main(int argc, char *argv[]) {
  NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];

  CGPoint pt;
  pt.x = 100;
  pt.y = 100;

  CGEventRef mouseDownEv = CGEventCreateMouseEvent(NULL, kCGEventLeftMouseDown, pt, kCGMouseButtonLeft);
  CGEventPost (kCGHIDEventTap, mouseDownEv);

  CGEventRef mouseUpEv = CGEventCreateMouseEvent(NULL, kCGEventLeftMouseUp, pt, kCGMouseButtonLeft);
  CGEventPost (kCGHIDEventTap, mouseUpEv );

  [pool release];
  return 0;
}

This really works, but I have no idea how to translate the Core Graphics (CG) functions into AppleScriptObjC.
Would it be:

set mouseUpEv to CGEvent's CGEventCreateMouseEvent(missing value, "kCGEventLeftMouseUp", pt, "kCGMouseButtonLeft")

CGEvent is not a class, or is it?

The variable “pt” is a structure. Will

set pt to {100, 100}

work for that?

I could, alternatively, use the

“mouseEventWithType:location:modifierFlags:timestamp:windowNumber:context:eventNumber:clickCount:pressure:”

method for the “NSEvent” class, but I have no idea how to fill in all the parameters for that method.

You can’t use the CGEvent stuff because it’s a C API, not Objective-C.

You can create an event like this:

set theEvent to current application's NSEvent's mouseEventWithType_location_modifierFlags_timestamp_windowNumber_context_eventNumber_clickCount_pressure_(current application's NSLeftMouseDown, {x:100, y:100}, 0, 0, 0, missing value, 0, 1, 0)

But I don’t know how you dispatch it to another application.

I used the “NSEvent” method “eventWithCGEvent:” to convert my CGEvents into NSEvents, and it appears that you were basically right in your guesses for parameter values. However, “pressure” should be 1 not 0.

I tried to use these values to create an event using this code:

#import <Foundation/Foundation.h>
#import <ApplicationServices/ApplicationServices.h>
#import <AppKit/NSEvent.h>
#import <AppKit/NSGraphicsContext.h>
#import <AppKit/NSApplication.h>

int main(int argc, char *argv[]) {
  NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
  NSEvent *myEvent1 = [[NSEvent alloc] init];
  NSEvent *myEvent2 = [[NSEvent alloc] init];
  
  NSPoint pt;
  pt.x = 100;
  pt.y = 100;

	  myEvent1 = [NSEvent mouseEventWithType: 1
	  		location: pt
	  		modifierFlags: 0
	  		timestamp: 0
	  		windowNumber: 0
	  		context: NULL
	  		eventNumber: 0
	  		clickCount: 1
	  		pressure: 1];
	  		
	  [NSApp postEvent: myEvent1 atStart: YES];

	  myEvent2 = [NSEvent mouseEventWithType: 2
	  		location: pt
	  		modifierFlags: 0
	  		timestamp: 0
	  		windowNumber: 0
	  		context: NULL
	  		eventNumber: 0
	  		clickCount: 1
	  		pressure: 1];

	  [NSApp postEvent: myEvent2 atStart: YES];
 
  [pool release];
  return 0;
}

This code did nothing. I also replaced the NULL’s with “[NSGraphicsContext currentContext]” without effect and tried “[NSApp sendEvent: myEvent1]” in place of “[NSApp postEvent: myEvent1 atStart: YES]”. I think the problem maybe related to the timestamp, but I’m not sure.

I suspect that the problem is that you’re sending the event to your app, so it must be handled by your app. What you need to do is insert it somewhere before the app, which probably means using a lower-level I/O C API.

Assuming you are right and this approach is a dead end, this is what I did instead. I used my working Core Graphics program to make a sub-class of “NSEvent” ” “MyNSEvent”. I followed the Rectangle/Square example at

http://www.otierney.net/objective-c.html.

“MyNSEvent.h”


#import <AppKit/NSEvent.h>

@interface MyNSEvent : NSEvent 

+(int) clickAtLocation: (NSPoint) pt;

@end

“MyNSEvent.m”


#import <Foundation/Foundation.h>
#import <ApplicationServices/ApplicationServices.h>

#import "MyNSEvent.h"

@implementation MyNSEvent

+(int) clickAtLocation: (NSPoint) pt {
	
	NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
	int x = pt.x;
	int y = pt.y;
	
	CGPoint ptCG;
	ptCG.x = x;
	ptCG.y = y;
	
	CGEventRef mouseDownEv = CGEventCreateMouseEvent(
		NULL,kCGEventLeftMouseDown,pt,kCGMouseButtonLeft);
	CGEventPost (kCGHIDEventTap, mouseDownEv);
	
	
	CGEventRef mouseUpEv = CGEventCreateMouseEvent(
		NULL,kCGEventLeftMouseUp,pt,kCGMouseButtonLeft);
	CGEventPost (kCGHIDEventTap, mouseUpEv );

	[pool release];
	return 0;	
	
}
@end

Now,


MyNSEvent's clickAtLocation_({x:100, y:100})

produces a mouse click at (100, 100). Problem solved.

Neat. Is there any reason you have to subclass NSEvent – wouldn’t it still work fine with a subclass of NSObject?

You are right, but doing it this way give me the option later of returning the NSEvent (using the conversion method) instead of just an integer.

Also, you seem to be making a CGPoint from an NSPoint, but then using the NSPoint anyway.