Notifications
Clear all

[Closed] Mouse button tracking or catching

Hi all.

I need to catch the state of the mouse, i mean to know when the mouse has the left button clicked, when it has the middle button clicked and when it has the right button clicked, i don’t care about the keyboard, and i want to use this inside a function like


local lb = "where i will know if the left button is clicked"
local mb = "where i will know if the middle button is clicked"
local rb = "where i will know if the right button is clicked"

if mb == true do (print "mb)

(this is not a correct script, only to explain what i need)

even if the script cathes if ANY of the buttons is pressed it works for me, i only need to know if anyone of the three buttons is pressed to execute a function or not.

Cheers and thanks!!

6 Replies

you’re gonna be SOL for the right mouse button with standard MaxScript – they never fixed that (if it even -can- be fixed);

Mouse Cursors[left]mouse.buttonStates
[/left]
[left]
A read only variable to get the state of the mouse buttons as a 3 element <bitArray>. The order of the bits is: #{Left, Middle, Right}
[/left]
[left]
NOTE:

[/left]
[left]Right mouse button state is not always correct. If you right-click to bring up a right-click menu and then click in the viewport to dismiss the menu, the right mouse button is still reported as down.
[/left]

But you might be able to work something with .NET; you’ll need a timer ( I’m using it in a rollout ‘cos .net timers are the devil’s work ) to poll the state… unless you have some events upon which time you want to check the state, then you can just do that


 rollout roll_test "test" (
 	timer tick_tock interval:100 active:true
 	label lbl_button "Button: "
 	local dnc_control, mouseButtonStates, lButton, mButton, rButton
 
 	on tick_tock tick do (
 		mouseButtonStates = dnc_control.MouseButtons.value__
 		theButtonStr = ""
 		if (dotnet.CompareEnums mouseButtonStates lButton) do ( theButtonStr += "Left " )
 		if (dotnet.CompareEnums mouseButtonStates mButton) do ( theButtonStr += "Middle " )
 		if (dotnet.CompareEnums mouseButtonStates rButton) do ( theButtonStr += "Right" )
 		lbl_button.text = "Button: " + theButtonStr
 	)
 	on roll_test open do (
 		dnc_control = dotNetClass "Control"
 		mouseButtons = dnc_control.MouseButtons
 		lButton = mouseButtons.Left.value__
 		mButton = mouseButtons.Middle.value__
 		rButton = mouseButtons.Right.value__
 	)
 )
 createDialog roll_test
 

I’m lucky because the buttons i need to track are the left and middle mouse button, so with the standard maxscript maybe enought, but if it’s not working fine i’ll test with the dotnet script that you posted

Thanks!

Just out of curiosity, why do you say that?

(shoot! why doesn’t cgtalk quote quoted quotes? gah!)

Just out of curiosity, why do you say that?

I could phrase it differently and answer the question read both ways at the same time: .NET timers require greater setup and careful consideration of potential errors, scope and lifetime.

The ‘setup’ bit (setting the interval etc.) can be abstracted with a function, so I’ll skip that part.

Let’s try the scope/lifetime one…


rollout roll_test "test" (
	label lbl_test "test"
)
createDialog roll_test
theTimer = dotNetObject "System.Windows.Forms.Timer" 
fn theTimerTick = ( roll_test.lbl_test.text = (timeStamp()) as string )
dotnet.addEventHandler theTimer "tick" theTimerTick
theTimer.interval = 1000 
theTimer.start() 

That should create a small dialog with a label that changes to the timeStamp() value on every tick.

Let’s create a second one:


rollout roll_test2 "test" (
	label lbl_test "test"
)
createDialog roll_test2
theTimer = dotNetObject "System.Windows.Forms.Timer" 
fn theTimerTick = ( roll_test2.lbl_test.text = (timeStamp()) as string )
dotnet.addEventHandler theTimer "tick" theTimerTick
theTimer.interval = 1000 
theTimer.start() 

So now we have two timers ticking away.

We can stop the second one:


theTimer.stop()

But the first one will happily keep on ticking. Because we used the same variable name, it will keep ticking, too, until you garbage collect. Unless you know you’ve got a duplicate timer variable name somewhere, the erratic behavior of a timer suddenly stopping is confusing at best.

Should you be setting up two timers with the same name? of course not. But it’s one of those things to keep in mind. Rollout timers simply don’t have that problem as their lifetime and scope is tied to the rollout.

Now let’s try the error thing… let’s set up a timer that we’ll be using to poll an object’s radius property…


rollout roll_test "test" (
	label lbl_test "test"
	timer tick_tock interval:100 active:true
	on tick_tock tick do ( lbl_test.text = $.radius as string )
)
select (sphere())
createDialog roll_test

Now de-select the sphere…
You’ll get a single messagebox informing you that there’s no ‘radius’ property in there being no selection. you’ll probably hear an annoying beep every 100ms, but closing the dialog that houses the timer will kill that outright.

Now let’s do the same thing with a .NET timer. (If you’re working in max, now’s a good time to make sure you don’t need to save anything.)


rollout roll_test "test" (
	label lbl_test "test"
)
createDialog roll_test

select (sphere())

theTimer = dotNetObject "System.Windows.Forms.Timer"
fn theTimerTick = ( roll_test.lbl_test.text = $.radius as string )
dotnet.addEventHandler theTimer "tick" theTimerTick
theTimer.interval = 100
theTimer.start()

Now de-select the sphere here and re-live the memories of ‘funny’ javascript pages that would pop up alert() after alert() forcing you to end-task the browser as the equivalent happens in 3ds Max. A new dotnet exception handler error messagebox pops up every 100ms until, eventually, 3ds Max exhausts its allowed resources and crashes. Ouch.

Coding error? Of course. Should be trapped for? Absolutely. Impedes basic timer testing? Oh yes.

All of these problems -can- be avoided with good coding practices – but if having a dialog on-screen, where one can immediately kill a misbehaving timer and not run into any potential duplicate naming/scope issues, is not a problem (e.g. if you already have some dialog up anyway), I think I’d much prefer a rollout timer.

Great information. Thanks. I’ve run into each of those problems myself.

Back to capturing mouse buttons… Running your script (which required a small fix to add “dnc_control” to the local declaration of the rollout), if you’re not hovering right over the rollout, pressing the right mouse button causes the quad menu to pop up instead of just reporting that it’s been pressed. Do you know of any way to disable the quad menu and keep it from interfering with the mouse capture testing?

Annoying, aren’t they? :\

whoops – must’ve had it in a global scope somewhere – fixed in my post

Nope… and that may be part of why the right mouse button is ill-supported via max itself. On the up side, however, .NET reports the right mouse button state correctly -after- the quad menu closes… whereas the max method often leaves the right mouse button stuck in a virtual ‘down’ position.

I guess maybe if you disable all of the quad menus, then it wouldn’t pop up… but I haven’t tried it, nor do I think it safe to try