Mouse button hold timer

Become a member of the CGSociety

Connect, Share, and Learn with our Large Growing CG Art Community. It's Free!

THREAD CLOSED
 
Thread Tools Display Modes
  06 June 2011
Mouse button hold timer

What would be the cleanest way to detect when the (left) mousebutton has been continuously pressed for x milliseconds without movement?

like a "pressed-and-held-in-place" event..
__________________
The GPU revolution will not be rasterized! - http://www.jdbgraphics.nl
 
  06 June 2011
within a rollout/form you have control over or just anywhere in the 3dsmax UI?
 
  06 June 2011
Originally Posted by lo: within a rollout/form you have control over or just anywhere in the 3dsmax UI?


Directly in the viewport.

It's for my ProSelect script (see http://www.scriptspot.com/3ds-max/scripts/proselect) Its now triggers on a selection change, which doesn't work in a shaded view when the front most object is already selected. Since clicking that doesn't trigger this event cause it's already selected.

So I want to invoke the script with this.
__________________
The GPU revolution will not be rasterized! - http://www.jdbgraphics.nl
 
  06 June 2011
I've research a little bit and I think you could do this by using Windows Hooks to monitor mouse messages (which I guess would give you access to the mousedown and mouseup event, thus disabling the need for the timer) But I never did nothing with windows hooks and if I could help you it would probably need a separate .dll as I dont know how to dynamically compile stuff in max

But one thing for sure, I bet Denis could help out

Apart from that, using only maxscript doesnt seem possible because you can only track mouse click...

Here are some links:
http://msdn.microsoft.com/en-us/lib...v=VS.85%29.aspx
http://msdn.microsoft.com/en-us/library/ms645601(v=vs.85).aspx
__________________
Artur Leao | Co-Founder / Project Manager
You can do it! VFX
Porto/Lisbon - Portugal
http://www.ycdivfx.com
 
  06 June 2011
the ultimate solution is to use the low-level mouse hook (google for WH_MOUSE_LL).
but much easier (i used it for many years) is timer + virtual keys... something like that:

global MouseOps
fn CreateMouseOps forceRecompile:on =
(
	if forceRecompile or not iskindof MouseOps dotnetobject or (MouseOps.GetType()).name != "Assembly" do
	(

source = ""
source += "using System;\n"
source += "using System.Runtime.InteropServices;\n"
source += "public class VMouse\n"
source += "{\n"
source += "	private const int VK_LBUTTON = 0x01;\n"
source += "	private const int VK_RBUTTON = 0x02;\n"
source += "	private const int VK_MBUTTON = 0x04;\n"
source += "	private const int KEY_PRESSED = 0x80;\n"
source += "	[DllImport(\"user32.dll\", CharSet = CharSet.Auto, ExactSpelling = true)]\n"
source += "	public static extern short GetKeyState(int virtualKeyCode);\n"
source += "	public int IsPressed(int key) { return (GetKeyState(key) & KEY_PRESSED); }\n"
source += "	public int MouseButtons()\n"
source += "	{\n"
source += "		int buttons = 0;\n"
source += "		buttons += IsPressed(VK_LBUTTON) >> 5;\n"
source += "		buttons += IsPressed(VK_MBUTTON) >> 6;\n"
source += "		buttons += IsPressed(VK_RBUTTON) >> 7;\n"
source += "		return buttons;\n"
source += "	}\n"
source += "}\n"

		csharpProvider = dotnetobject "Microsoft.CSharp.CSharpCodeProvider"
		compilerParams = dotnetobject "System.CodeDom.Compiler.CompilerParameters"

		compilerParams.ReferencedAssemblies.Add "System.dll"

		compilerParams.GenerateInMemory = true
		compilerResults = csharpProvider.CompileAssemblyFromSource compilerParams #(source)
		
		MouseOps = compilerResults.CompiledAssembly
		MouseOps.CreateInstance "VMouse"
	)
)
global VMouse = CreateMouseOps()
global getMouseButtons = VMouse.MouseButtons

/***********************************************/
 try(mouse_timer.Dispose()) catch()

mouse_timer = dotnetobject "Timer"
mouse_timer.interval = 100
fn mouseTimerTick s e =
(
--	format "tick... buttons: %\n" (getMouseButtons())
	if VMouse.MouseButtons() == 5 do 
	(
		mouse_timer.Stop()
		format "stop...\n"
	)
)
dotnet.addEventHandler mouse_timer "Tick" mouseTimerTick
mouse_timer.Start()


usually interval ~100ms is enough. The function doesn't take time or memory.
 
  06 June 2011
Originally Posted by denisT: the ultimate solution is to use the low-level mouse hook (google for WH_MOUSE_LL).
but much easier (i used it for many years) is timer + virtual keys... something like that:


if you are going with a timer, why not just access mouse.buttonStates on the timer event?
 
  06 June 2011
Lol... see? Denis you the man :P

But, does that catch the click or it catches while the button is pressed? One other thing, I thought that when using something like windows hooks you didnt need a timer to listen to the messages. ... After turning on my brain a little, this isn't the window hooks example... can you enlighten us on how to do it?
__________________
Artur Leao | Co-Founder / Project Manager
You can do it! VFX
Porto/Lisbon - Portugal
http://www.ycdivfx.com
 
  06 June 2011
Originally Posted by lo: if you are going with a timer, why not just access mouse.buttonStates on the timer event?


mouse.buttonStates works in max viewport only. it doesn't work over any dialog, pop-up menu, etc.
 
  06 June 2011
Originally Posted by lo: if you are going with a timer, why not just access mouse.buttonStates on the timer event?


Yeah, I think in the case of the code Denis posted, mousetrack() would have the same effect, that's why I talked about the WM_MOUSE or like Denis said, WH_MOUSE_LL which contains this:

wParam [in]
Type: WPARAM

The identifier of the mouse message. This parameter can be one of the following messages: WM_LBUTTONDOWN, WM_LBUTTONUP, WM_MOUSEMOVE, WM_MOUSEWHEEL, WM_MOUSEHWHEEL, WM_RBUTTONDOWN, or WM_RBUTTONUP.


Thus enabling the user to track the mousedown, then the mouseup.
__________________
Artur Leao | Co-Founder / Project Manager
You can do it! VFX
Porto/Lisbon - Portugal
http://www.ycdivfx.com
 
  06 June 2011
Originally Posted by denisT: mouse.buttonStates works in max viewport only. it doesn't work over any dialog, pop-up menu, etc.


Ahh great input, thx guys! "Works in max viewport only".. that is actually the only place it has to work

I'm going to try and see what works best, I'll be back with the results!
__________________
The GPU revolution will not be rasterized! - http://www.jdbgraphics.nl

Last edited by jonadb : 06 June 2011 at 03:30 PM.
 
  06 June 2011
Originally Posted by Kameleon: Lol... see? Denis you the man :P

But, does that catch the click or it catches while the button is pressed? One other thing, I thought that when using something like windows hooks you didnt need a timer to listen to the messages. ... After turning on my brain a little, this isn't the window hooks example... can you enlighten us on how to do it?


using timer and vkey you can check the state of the vkey.

using mouse hook you don't need timer. you can hook any window mouse event and call any function from mousehook procedure. I don't like call max functions from c#, so I use events. My MouseHook class has three events: MouseMove, MouseDown, and MouseUp. It's enough for all my cases.
sample? ... hmm... i will think about possibility to show the sample.
 
  06 June 2011
Originally Posted by jonadb: Ahh great input, thx guys! "Works in max viewport only".. that is actually the only place it has to work

I'm going to try and see what works best, I'll be back with the results!


it's hard to stay in viewport all the time ... you can start some operation in viewport, drag cursor, and finish the operation out of viewport or over a dialog.
 
  06 June 2011
following Denis's advice, this a very quick and dirty implementation of low level mousehooks, most of which I stole off an MSDN blog, and added event delegates, etc.

(
	str="using System;\n"
	str+="using System.Collections.Generic;\n"
	str+="using System.Text;\n"
	str+="using System.Runtime.InteropServices;\n"
	str+="using System.Windows.Forms;\n"
	str+="using System.Diagnostics;\n"
	str+="using System.Drawing;\n"

	str+="class InterceptMouse\n"
	str+="{\n"
	str+="    private static IntPtr _hookID = IntPtr.Zero;\n"

	str+="public delegate void MouseDownHandler(object Sender, MouseEventArgs e);\n"
	str+="public event MouseDownHandler MouseDown;\n"
	str+="public delegate void MouseUpHandler(object Sender, MouseEventArgs e);\n"
	str+="public event MouseUpHandler MouseUp;\n"
	str+="public delegate void MouseMoveHandler(object Sender, MouseEventArgs e);\n"
	str+="public event MouseMoveHandler MouseMove;\n"
		
	str+="public InterceptMouse()\n"
	str+="{\n"
	str+="    _hookID = SetHook(HookCallback);\n"
	str+="}\n"

	str+="public void Release()\n"
	str+="{\n"
	str+="	UnhookWindowsHookEx(_hookID);\n"
	str+="}\n"
	
	str+="private static IntPtr SetHook(LowLevelMouseProc proc)\n"
	str+="{\n"
	str+="    using (Process curProcess = Process.GetCurrentProcess())\n"
	str+="    using (ProcessModule curModule = curProcess.MainModule)\n"
	str+="    {\n"
	str+="        return SetWindowsHookEx(WH_MOUSE_LL, proc,\n"
	str+="            GetModuleHandle(curModule.ModuleName), 0);\n"
	str+="    }\n"
	str+="}\n"

	str+="private delegate IntPtr LowLevelMouseProc(int nCode, IntPtr wParam, IntPtr lParam);\n"

	str+="private IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam)\n"
	str+="{\n"
	str+="   if (nCode >= 0)\n"
	str+="	{\n"
	str+="		MSLLHOOKSTRUCT hookStruct = (MSLLHOOKSTRUCT)Marshal.PtrToStructure(lParam, typeof(MSLLHOOKSTRUCT));\n"
	str+="		if (MouseMessages.WM_LBUTTONDOWN == (MouseMessages)wParam)\n"
	str+="		{\n"
	str+="			MouseEventArgs e = new MouseEventArgs(MouseButtons.Left,1,hookStruct.pt.x  ,hookStruct.pt.y,0);\n"
	str+="         	MouseDown(this, e);\n"
	str+="		}\n"
	str+="		else if (MouseMessages.WM_LBUTTONUP == (MouseMessages)wParam)\n"
	str+="		{			\n"
	str+="			MouseEventArgs e = new MouseEventArgs(MouseButtons.Left,1,hookStruct.pt.x  ,hookStruct.pt.y,0);\n"
	str+="			MouseUp(this, e);\n"
	str+="		}\n"
	str+="		else if (MouseMessages.WM_MOUSEMOVE == (MouseMessages)wParam)\n"
	str+="		{\n"
	str+="				MouseEventArgs e = new MouseEventArgs(MouseButtons.None,0,hookStruct.pt.x  ,hookStruct.pt.y,0);\n"
	str+="           	MouseMove(this, e);\n"
	str+="		}\n"
	str+="			else if (MouseMessages.WM_RBUTTONDOWN == (MouseMessages)wParam)\n"
	str+="{\n"
	str+="MouseEventArgs e = new MouseEventArgs(MouseButtons.Right,1,hookStruct.pt.  x,hookStruct.pt.y,0);\n"
	str+="            	MouseDown(this, e);\n"
	str+="			}\n"
	str+="			else if (MouseMessages.WM_RBUTTONUP == (MouseMessages)wParam)\n"
	str+="			{			\n"
	str+="				MouseEventArgs e = new MouseEventArgs(MouseButtons.Right,1,hookStruct.pt.  x,hookStruct.pt.y,0);\n"
	str+="				MouseUp(this, e);\n"
	str+="			}\n"
	str+="		}\n"
	str+="       return CallNextHookEx(_hookID, nCode, wParam, lParam);\n"
	str+="   }\n"

	str+="   private const int WH_MOUSE_LL = 14;\n"

	str+="   private enum MouseMessages\n"
	str+="  {\n"
	str+="      WM_LBUTTONDOWN = 0x0201,\n"
	str+="       WM_LBUTTONUP = 0x0202,\n"
	str+="      WM_MOUSEMOVE = 0x0200,\n"
	str+="      WM_MOUSEWHEEL = 0x020A,\n"
	str+="      WM_RBUTTONDOWN = 0x0204,\n"
	str+="       WM_RBUTTONUP = 0x0205\n"
	str+="   }\n"

	str+="    [StructLayout(LayoutKind.Sequential)]\n"
	str+="   private struct POINT\n"
	str+="   {\n"
	str+="       public int x;\n"
	str+="        public int y;\n"
	str+="    }\n"
	str+="   [StructLayout(LayoutKind.Sequential)]\n"
	str+="    private struct MSLLHOOKSTRUCT\n"
	str+="   {\n"
	str+="      public POINT pt;\n"
	str+="      public uint mouseData;\n"
	str+="     public uint flags;\n"
	str+="     public uint time;\n"
	str+="     public IntPtr dwExtraInfo;\n"
	str+="   }\n"

	str+="    [DllImport(\"user32.dll\", CharSet = CharSet.Auto, SetLastError = true)]\n"
	str+="    private static extern IntPtr SetWindowsHookEx(int idHook,\n"
	str+="       LowLevelMouseProc lpfn, IntPtr hMod, uint dwThreadId);\n"

	str+="   [DllImport(\"user32.dll\", CharSet = CharSet.Auto, SetLastError = true)]\n"
	str+="   [return: MarshalAs(UnmanagedType.Bool)]\n"
	str+="   private static extern bool UnhookWindowsHookEx(IntPtr hhk);\n"

	str+="   [DllImport(\"user32.dll\", CharSet = CharSet.Auto, SetLastError = true)]\n"
	str+="   private static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode,\n"
	str+="       IntPtr wParam, IntPtr lParam);\n"

	str+="   [DllImport(\"kernel32.dll\", CharSet = CharSet.Auto, SetLastError = true)]\n"
	str+="  private static extern IntPtr GetModuleHandle(string lpModuleName);\n"
		
	str+="}\n"


	global mouseHook
	struct mouseHookStr
	(
		mouseOps,
		
		fn CreateWinAssembly =
		(
			local csharpProvider = dotnetobject "Microsoft.CSharp.CSharpCodeProvider"
			local compilerParams = dotnetobject "System.CodeDom.Compiler.CompilerParameters"
				
			compilerParams.ReferencedAssemblies.addRange #("System.dll","System.Windows.Forms.dll","System.Drawing.dll")
			compilerParams.GenerateInMemory = on
			local compilerResults = csharpProvider.CompileAssemblyFromSource compilerParams #(str)
			
			for er =0 to compilerResults.errors.count-1 do print (compilerResults.errors.item[er].tostring())
			mouseOps = compilerResults.CompiledAssembly.CreateInstance "InterceptMouse"
		),
		
		fn mouseDown s e = print "mouse down",
		fn mouseUp s e = print "mouse up",
		fn mouseMove s e = print "mouse move",

		fn initStruct =
		(
			CreateWinAssembly()
			dotNet.addEventHandler mouseOps "MouseDown" mouseDown
			dotNet.addEventHandler mouseOps "MouseUp" mouseUp
			dotNet.addEventHandler mouseOps "MouseMove" mouseMove
		),
		
		_init = initStruct()	
	)
)
mouseHook = mouseHookStr()

-- call mousehook.mouseops.release() to stop it
 
  06 June 2011
Pure awesomeness
__________________
Artur Leao | Co-Founder / Project Manager
You can do it! VFX
Porto/Lisbon - Portugal
http://www.ycdivfx.com
 
  06 June 2011
Originally Posted by lo: following Denis's advice, this a very quick and dirty implementation of low level mousehooks, most of which I stole off an MSDN blog, and added event delegates, etc.



everything looks right. I would add an option to fire events when mouse in max window only.
 
Thread Closed share thread



Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

vB code is On
Smilies are On
[IMG] code is On
HTML code is Off
CGSociety
Society of Digital Artists
www.cgsociety.org

Powered by vBulletin
Copyright 2000 - 2006,
Jelsoft Enterprises Ltd.
Minimize Ads
Forum Jump
Miscellaneous

All times are GMT. The time now is 08:30 PM.


Powered by vBulletin
Copyright ©2000 - 2018, Jelsoft Enterprises Ltd.