Looking for a tool that monitors a mouse location and triggers an ApplesScript

I’m looking for some tool that monitors a mouse location and triggers an AppleScript according to the location.

I want to trigger an AppleScript just by moving the mouse near the upper-right corner of screen without clicking. I looked into Keyboard Maestro, and they list that feature on the wish list, but the developers do not seem very interested in implementing it.

I had been using DragThing for quit some time, and it allowed me to pop up a palette just by placing the mouse in some area. I’m now using iCollections and want to replicate that functionality.

You can easily get mouse cursor location. I wrote such a script in 4 years ago.

http://piyocast.com/as/archives/8447

Yes, I thought about doing what I want to do solely using AppleScript, but I was worried about the system overhead. I want the checking of mouse location to take place every second or so. So I surmise that a true application would do this much faster than an AppleScript.

With NSControl (NSView inherits)
You can set up and add a trackingRectangle.
And the objects that’s registered to that will receive mouseEntered and mouseExited events. So you don’t have to keep polling and checking where the location is.

1 Like

Thank you for your reply. Developing an app that takes advantage of that programming interface would be an ideal solution, but I’m just a scripter not a programmer. Is there any way for me to implement that functionality solely within AppleScript?

Yes you should be able to using AscObjC.
I can help just might be slowly over some time.
Basically creating a tracking area
With Location / Dimensions and what it Tracks (mouse enter/Exit).

And assign that to a NSView ( your app window)

Can you provide some specific sample? That would be very helpful.

1 Like

Let me give you a few tasks to research and get yourself to start…

  1. Need to get the Current Applications NSApp’s mainWindow into a property…
  2. learn how to create a NSRect. Assign it to a property with the Location / dimensions you want

Start with that until I’m at my computer and have time to help… on my phone right now

Here’s how to do it.
You’ll need to apply it to your scripts own NSView.

read about it here:

use AppleScript version "2.5" -- Yosemite (10.10) or later
use framework "Foundation"
use framework "AppKit"
use scripting additions


-- classes, constants, and enums used
property NSTrackingActiveAlways : a reference to 128
property NSTrackingMouseEnteredAndExited : a reference to 1

property aWindow : missing value
property aView : missing value
property aRect : missing value
property aTrackingArea : missing value
property aNewView : missing value
property aViewsSize : missing value
property aNewViewsSize : missing value
property mouseLocation : missing value

my setUpTrackingArea()


on setUpTrackingArea()
	set aWindow to current application's NSApp's mainWindow()
	set aView to aWindow's contentView()
	set aViewsSize to aView's frame()
	set aRect to current application's NSMakeRect(0, 0, 200, 200)
	
	set trackingOptions to NSTrackingMouseEnteredAndExited + NSTrackingActiveAlways
	set aTrackingArea to current application's NSTrackingArea's alloc()'s ¬
		initWithRect:aRect ¬
			options:trackingOptions ¬
			owner:me ¬
			userInfo:(missing value)
	
	set aNewView to (current application's NSView's alloc())'s initWithFrame:aRect
	set aNewViewsSize to aNewView's |bounds|()
	aNewView's addTrackingArea:aTrackingArea
	aView's addSubview:aNewView
	aView's updateTrackingAreas()
end setUpTrackingArea


on mouseEntered:anNSEvent
	set aMouseLocation to anNSEvent's mouseLocation()
	log {"Mouse Entered at location:", aMouseLocation}
end mouseEntered:

on mouseExited:anNSEvent
	set aMouseLocation to anNSEvent's mouseLocation()
	log {"Mouse Exited at location:", aMouseLocation}
end mouseExited:



Dear technomorph,

Thank you very much for providing the sample. I’m very grateful for your help.

I added a display-dialog command inside the mouseEntered and mouseExited, and save it as a stay-open app. Then I moved my mouse into and out of the upper-left corner of the screen, but nothing seems to have happened.

I believe I don’t understand how AscObjC works, but I don’t know where to begin to learn about it.

In your “app” you may need to create a window for it.

I’ve reread your post and you want it to trigger an AppleScript.

What you may need to do is create a system top menuBarItem. That will have the tracking area in it. This menuBarItem will run in the background.

I see this happening with little snitch. When you come close to the menubar item it triggers the info window.

I think I’ve figured out a solution to create a
NSMenuBarItem and the add he trackingArea to some part of that (It’s button or menu)

I can make it run in the background and be “hidden”. You can then probably just add you code to this script and have it called from the
mouseEntered: handler.

I’m just playing with that right now and with post the results when I get back to my computer and refine the code.

Dear technomorph,

Thank you very much for your update. I’m looking forward to testing your upgrade.

It seems to me that the OP is asking for tracking of an application window that is not a given applet (current script). And NSTrackingArea tracks the window of the applet itself.

If you still need the latter, you need to first create a new, real application window. Moreover, it must be greater than or equal to the tracked rectangle. Here’s an example of how it works:
 

use AppleScript version "2.4" -- Yosemite (10.10) or later
use framework "Foundation"
use framework "AppKit"
use scripting additions

on run
	set aScreen to current application's NSScreen's mainScreen()
	set aFrame to {{0, 0}, {1200, 600}}
	set aBacking to current application's NSTitledWindowMask
	set aDefer to current application's NSBackingStoreBuffered
	set aWindow to my createWindow(aScreen, aFrame, aBacking, aDefer)
	set aView to aWindow's contentView()
	set aRect to current application's NSMakeRect(0, 0, 200, 200)
	set aTrackingArea to current application's NSTrackingArea's alloc()'s initWithRect:aRect options:545 owner:me userInfo:(missing value)
	set aNewView to (current application's NSView's alloc())'s initWithFrame:aRect
	aNewView's addTrackingArea:aTrackingArea
	aView's addSubview:aNewView
	aView's updateTrackingAreas()
end run

----------------------------------------------------  OTHER HANDLERS ----------------------------------------

on mouseEntered:theNSEvent
	display notification "Mouse Entered"
end mouseEntered:

on mouseExited:anNSEvent
	display notification "Mouse Exited"
end mouseExited:

on createWindow(aScreen, aFrame, aBacking, aDefer)
	set aWin to current application's NSWindow's alloc()
	(aWin's initWithContentRect:aFrame styleMask:aBacking backing:aDefer defer:false screen:aScreen)
	aWin's setTitle:"You are welcome! Please, set your preferences here."
	aWin's setDelegate:me
	aWin's setDisplaysWhenScreenProfileChanges:true
	aWin's setHasShadow:true
	aWin's setIgnoresMouseEvents:false
	aWin's setLevel:(current application's NSNormalWindowLevel)
	aWin's setOpaque:false
	aWin's setAlphaValue:1.0
	aWin's setReleasedWhenClosed:true
	aWin's setBackgroundColor:(current application's NSColor's blueColor())
	aWin's makeKeyAndOrderFront:(me)
	return aWin
end createWindow

 

NOTE: the track area here is bottom-left corner of the window.

KniazidisR,

Thank you very much for your code. It is great and works fine.

For my purposes, I need two points of improvement:

  1. Is it possible to hide the window and still get the notifications? When I hid the window, I got no notifications.

  2. Is it possible to set up the window where the menu bar exists? I changed the coordinate in different ways, but the window refused to overlap with the menu bar.

tkumashi.

  1. As I already warned, the script will only monitor the specified part of ITS window. If you hide the window, it is temporarily gone. No window - no event, and therefore there will be no reaction (notification).

Instead, make the window transparent. In the mouseEntered handler, add code to temporarily hide the window. This way the window will hide after receiving the event, which is what you need in your case.

  1. The computer coordinate system and the AsObjC coordinate system are different. In AsObjC the coordinate (0,0) is in the lower left corner. Create a window exactly equal to menubaritemm in size and position.

  2. I still don’t quite understand what you are trying to achieve. And that’s because there is no code or screenshots from yourself. I have a suspicion that what you really need is a solution similar to the one proposed by Fredrik71. And this is completely different.

KniazidisR,

I’m sorry for being a little vague. Please find attached the code that I currently use to achieve what I want to accomplish. It is based on Fredrik71’s code.

I wanted to find a way to achieve the same function in a faster and more reliable way using less system resources. I simply assumed AsObjC would make it possible, but if it is not, as you explained, I believe I just have to live with what I have now.

use framework "Foundation"
use framework "AppKit"
use scripting additions

global screenWidth
global screenHeight
global theX
global theY

on run
	getMouseLocation()
	
	if (theX > screenWidth - 250) and (theX < screenWidth - 100) and (theY < 5) then
		delay 0.5
		getMouseLocation()
		
		if (theX > screenWidth - 250) and (theX < screenWidth - 100) and (theY < 5) then
			do shell script "osascript '/Users/tkumashi/Documents/iCollections/ TK/Open collection.scpt'"
		end if
	end if
end run

on getMouseLocation()
	set screens to current application's NSScreen's screens
	set theScreen to (screens's objectAtIndex:0)'s frame() -- main screen
	set {screenWidth, screenHeight} to item 2 of theScreen
	
	set theMouseLocation to current application's NSEvent's mouseLocation()
	set {theX, theY} to {x of theMouseLocation, screenHeight - (Y of theMouseLocation)}
end getMouseLocation

on idle
	run
	return 1.5
end idle

Litlle faster and more reliable way using less system resources (than the Fredrik71 stay-open application) is creating daemon (agent) with launchd. Look here:

Creating Launch Daemons and Agents
Make your agent to run periodically, every 1 second.

I was thinking about eventually moving to launchd, but I have two concerns. The start interval must be an integer so a number like 1.5 seconds would not be accepted.

If I set it to 1 second, the next instance would probably run before the previous one finishes running, potentially causing problems. This is prevented in an AppleScript app because the idle handler would not kick in until the run handler finishes.

It looks like I should live with the AppleScript-app solution.

I’m still working on the menuBarItem
Have found there’s a truck need to do with adding two tracking areas.

Another option but won’t work with an AppleScript because it takes a block is:
Adding a globalEventMonitor with a cusorUpdate mask.
Then check the cursourPosition and if it passes your test (inRect:) then launch your AppleScript