Block input from mouse and keyboard?

I execute a long running AS in the background. Sometimes it needs to activate (bring to front) an application (E.g., browsers in the background execute Javascript very slow and it might timeout. The fix is to bring the browser to the foreground.). If I am working on something else when this happens it is very easy to click on something in the window that was just brought to front, enter some text or something, which often interrupts the script.

Is it possible to block mouse and keyboard input for a few seconds in AS?

Why does it interrupt the script?
Do you mean the JavaScript or the AppleScript gets paused?

What typically happens is that the script brings a web browser to the front just when I am about to click something in the “manual” frontmost application and it happens so fast that I can’t stop myself from clicking and suddenly the browser is in an “unexpected” state which breaks the script.

Without seeing the script we have no idea what you may be doing wrong

I don’t do anything “wrong” - the script works fine if the computer is left unattended. The problem is that it takes 1-2 hours to finish and that I sometimes need to use the computer when it executes and when this happens it is easy to, by mistake, click on something that belongs to the script which makes it into an unexpected state.

I didn’t Imply that you,did something “WRONG”. Just that there might be a better way to modify the script that it would continue properly whether the app is frontmost or not. But since you won’t share the script…

i googled ‘macOS disable keyboard input via command line’ - which brings up multiple discussions that list various methods which may or may not work. you may already googled it yourself anyway. maybe some of them will work for you.

The theme of disabling mouse and keyboard is important and has been talked about for a long time.

We are afraid of being affected by erroneous operation of user mouse/keyboard during the script processing.

Because it is a long -thought -out theme, it is old and new.

If it is a desktop type Mac, it is an old solution to remove a USB mouse or keyboard, and the latest solution is to disable by software.

I disabled mouse by erasing mouse cursor. I wrote such a script 4 years ago.

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

And…you can disable user’s keyboard input by selecting blank keymap.
I found various way to change keymap. You can select what you like.

This is not a groundbreaking discussion. It’s not the first problem in the world.

1 Like

You could run the NSWindow in modal mode:
Which ignore NSEvent mouse/keyboard events unless they are associated with that window:

This script run in modal?

--
--	Created by: Takaaki Naganoya
--	Created on: 2023/09/02
--
--	Copyright © 2023 Piyomaru Software, All Rights Reserved
--
use AppleScript
use scripting additions
use framework "Foundation"
use framework "AppKit"

property |NSURL| : a reference to current application's |NSURL|
property NSData : a reference to current application's NSData
property NSView : a reference to current application's NSView
property NSScreen : a reference to current application's NSScreen
property NSButton : a reference to current application's NSButton
property NSWindow : a reference to current application's NSWindow
property NSWindowController : a reference to current application's NSWindowController

set aWidth to 600
set aHeight to 300
set myObject to {myWidth:aWidth, myHeight:aHeight, myTitle:"Modal TEST Window", myButtonMSG:"OK", myTimeOut:3}
my performSelectorOnMainThread:"dispModalWindowWithView:" withObject:(myObject) waitUntilDone:true
--my dispWindowWithView:myObject


on dispModalWindowWithView:anObject
	set aWidth to myWidth of anObject as integer
	set aHeight to myHeight of anObject as integer
	set aTitle to myTitle of anObject as string
	set aButtonMSG to myButtonMSG of anObject as string
	set timeOutSecs to myTimeOut of anObject as real
	
	--Make Button
	set bButton to (NSButton's alloc()'s initWithFrame:(current application's NSMakeRect(aWidth / 4, 0, aWidth / 2, 40)))
	bButton's setTitle:aButtonMSG
	bButton's setButtonType:(current application's NSMomentaryLightButton)
	bButton's setBezelStyle:(current application's NSRoundedBezelStyle)
	bButton's setKeyEquivalent:(return)
	bButton's setTarget:me
	bButton's setAction:("clicked:")
	
	--Make NSView
	set aNSV to NSView's alloc()'s initWithFrame:(current application's NSMakeRect(0, 0, aHeight, aWidth))
	aNSV's addSubview:bButton
	aNSV's setNeedsDisplay:true
	
	set aWin to makeWinWithView(aNSV, aWidth, aHeight, aTitle, 1.0)
	
	set wController to NSWindowController's alloc()
	wController's initWithWindow:aWin
	wController's showWindow:me
	wController's runModalForWindow:aWin
	
	my closeWin:aWin
end dispModalWindowWithView:


--Button Clicked Event Handler
on clicked:aSender
	set aWindow to aSender's |window|
	aWindow's |close|()
end clicked:


--make Window for Input
on makeWinWithView(aView, aWinWidth, aWinHeight, aTitle, alphaV)
	set aScreen to NSScreen's mainScreen()
	set aFrame to {{0, 0}, {aWinWidth, aWinHeight}}
	set aBacking to current application's NSTitledWindowMask
	set aDefer to current application's NSBackingStoreBuffered
	
	-- Window
	set aWin to NSWindow's alloc()
	(aWin's initWithContentRect:aFrame styleMask:aBacking backing:aDefer defer:false screen:aScreen)
	
	aWin's setTitle:aTitle
	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:alphaV --append
	aWin's setReleasedWhenClosed:true
	aWin's |center|()
	
	-- Set Custom View
	aWin's setContentView:aView
	
	return aWin
end makeWinWithView


--close win
on closeWin:aWindow
	aWindow's |close|()
end closeWin:

Are you sure this works? When I tried the pointer was hidden but when I clicked the click was registered and I could, e.g., open a menu.

I think I want it to work the other way around. That is, the scripted application must not accept any mouse or keyboard input, even if it is in front.

Oh…It is my wrong memory.
The mouse cursor disappears, but we can still click.

If I could add a full screen (maybe invisible) image on top of everything that received all input? I just need it for 3 seconds or so so I notice that the frontmost application has changed.

Hmm, I am not sure if I am off the mark here, but why don’t you adopt a pragmatic approach:
Before bringing the browser in the foreground, make the script (or you script runner) frontmost and display a dialog annoucing the application switch. You could make the dialog give up after a couple of seconds to be able to run it unattended.
In this way the user get’s a rather fail safe (I suppose) warning to keep his or her hands off the keyboard or the mouse because an applications switch is imminent.
After the frontmost app gets relegated to the background again, you could restore the previous app using System Events to signal that the user is allowed to touch the mouse or keyboard again.
Or something the like.

2 Likes

While it may be possible to implement a workaround that could potentially emulate this, it would surprise me if an API is available that permits this as it would represent a pretty big security vulnerability.

It’s also not something that should ever need to be done in a real-world situation, and if your use case does depend on this, it’s well worth considering whether the problem you’re wanting to solve can be approached differently.

Another red flag that points to an approach that might be rethinking is it being a “long-running AS in the background”. Scripting, by its very nature, is not intended for big tasks that run for a long time (ie. more than a few minutes, not including idle time).

To reduce running time, make sure you organise your code so that the executing of JavaScript is done in as few calls as possible (preferably, all in one call), and then organise your AppleScript code to process the return value of the JS as the last step, also ideally all at once.

Generally speaking, it isn’t usually necessary for a web browser to be at the front in order to inject some JavaScript into a web page (as long as the web page is still loaded into memory, i.e. not sleeping or hibernating). You also don’t need to use a web browser at all to inject JavaScript: with AppleScriptObjC, a web page can be loaded using NSURLSession and NSURLRequest methods, and attached to an NSWebView (although a view doesn’t need to be painted / displayed) in order to execute JavaScript code in the page’s document.