PDA

View Full Version : Mouse button hold timer


jonadb
06-16-2011, 08:33 AM
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..

lo
06-16-2011, 01:17 PM
within a rollout/form you have control over or just anywhere in the 3dsmax UI?

jonadb
06-16-2011, 01:40 PM
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.

Kameleon
06-16-2011, 03:37 PM
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 :D

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

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/library/ms632589%28v=VS.85%29.aspx
http://msdn.microsoft.com/en-us/library/ms645601(v=vs.85).aspx

denisT
06-16-2011, 04:07 PM
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.

lo
06-16-2011, 04:11 PM
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?

Kameleon
06-16-2011, 04:18 PM
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? :D

denisT
06-16-2011, 04:20 PM
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.

Kameleon
06-16-2011, 04:20 PM
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.

jonadb
06-16-2011, 04:27 PM
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!

denisT
06-16-2011, 04:32 PM
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? :D

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.

denisT
06-16-2011, 04:37 PM
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.

lo
06-16-2011, 04:57 PM
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

Kameleon
06-16-2011, 05:33 PM
Pure awesomeness :D

denisT
06-16-2011, 05:33 PM
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.

lo
06-16-2011, 05:36 PM
I also haven't implemented mousewheel events. Anyone, feel free to pick it up.

denisT
06-16-2011, 05:37 PM
next is to realize CBTHook and forget the DialogMonitorOPS...

lo
06-16-2011, 05:39 PM
And after that rewrite 3dsmax :)

jonadb
06-16-2011, 10:12 PM
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.

Nice catch! :)

jonadb
06-16-2011, 10:19 PM
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.

-code-



Nice, it even works outside max's window :) Sometimes it stops working but the idea is great!

I'm suddenly flooded in work so somewhere next week I'll hope to find the time to play around a bit more..

lo
06-17-2011, 08:15 AM
Added max restriction (optional) and mousewheel event.
For some reason though max hangs for a few seconds every time I close a window... does anyone know why?

(
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+=" private IntPtr MaxHwnd;\n"
str+=" private bool restrictToMax;\n"

str+=" public IntPtr Handle\n"
str+=" {\n"
str+=" get\n"
str+=" {\n"
str+=" return MaxHwnd;\n"
str+=" }\n"
str+=" set\n"
str+=" {\n"
str+=" if (value!=MaxHwnd)\n"
str+=" {\n"
str+=" MaxHwnd=value;\n"
str+=" }\n"
str+=" }\n"
str+=" }\n"

str+=" public bool RestrictToMax\n"
str+=" {\n"
str+=" get\n"
str+=" {\n"
str+=" return restrictToMax;\n"
str+=" }\n"
str+=" set\n"
str+=" {\n"
str+=" if (value==true && MaxHwnd!=IntPtr.Zero || value==false) restrictToMax=value;\n"
str+=" }\n"
str+=" }\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 delegate void MouseWheelHandler(object Sender, MouseEventArgs e);\n"
str+=" public event MouseWheelHandler MouseWheel;\n"

str+=" public InterceptMouse()\n"
str+=" {\n"
str+=" _hookID = SetHook(HookCallback);\n"
str+=" MaxHwnd = IntPtr.Zero;\n"
str+=" restrictToMax = false;\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 bool IsMaxParent()\n"
str+=" {\n"
str+=" return (GetAncestor(GetForegroundWindow(),3)==MaxHwnd);\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 ((!restrictToMax || IsMaxParent()) && 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+=" else if (MouseMessages.WM_MOUSEWHEEL == (MouseMessages)wParam)\n"
str+=" {\n"
str+=" int delta = (int)((hookStruct.mouseData & (0xFFFF << 16)))/120;\n"
str+=" MouseEventArgs e = new MouseEventArgs(MouseButtons.None,1,hookStruct.pt. x,hookStruct.pt.y,delta);\n"
str+=" MouseWheel(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+=" public static extern IntPtr GetAncestor(IntPtr hWnd, int type);\n"
str+=" [DllImport(\"user32.dll\", CharSet = CharSet.Auto, SetLastError = true)]\n"
str+=" public static extern IntPtr GetForegroundWindow();\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"

try(mousehook.mouseops.release())catch()
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"
mouseOps.handle = (dotNetObject "System.IntPtr" (windows.GetMaxHwnd()))
mouseOps.restrictToMax = on
),

fn mouseDown s e = print "mouse down",
fn mouseUp s e = print "mouse up",
fn mouseMove s e = print "mouse move",
fn mouseWheel s e = format "Mousewheel delta: %\n" e.delta,

fn initStruct =
(
CreateWinAssembly()
dotNet.addEventHandler mouseOps "MouseDown" mouseDown
dotNet.addEventHandler mouseOps "MouseUp" mouseUp
--dotNet.addEventHandler mouseOps "MouseMove" mouseMove
dotNet.addEventHandler mouseOps "MouseWheel" mouseWheel
),

_init = initStruct()
)

mouseHook = mouseHookStr()
)

-- call mousehook.mouseops.release() to stop it

lo
06-17-2011, 11:42 AM
i realize now i could implement the max restriction by supplying the callback with the 3dsmax thread id instead of the null pointer.
--EDIT: That seems incorrect.

i will also try to load the callback on a seperate thread to try to avoid the hang on title bar buttons.

denisT
06-17-2011, 06:11 PM
The cool thing about Hooks is that the hook procedure can return nonzero value to prevent the system from passing the message to the target window procedure. If you are using the Events system it's easy to add Handled property to Event Argument to control passing the message. For example, when user Right-Clicks in viewport it causes popping-up the Quad Menu. In some situation when you want to change this behavior you can cancel passing the message to the target window (viewport) using MouseDown event.

lo
06-17-2011, 08:10 PM
Apparently the issue with the title bar is a known bug cause by windows themes.

jonadb
07-28-2011, 02:50 PM
Doing some test with the mouse hook code posted above, did anyone ever solve the delay while closing windows?

lo
07-28-2011, 03:26 PM
I spent a few hours trying, but all dead ends :shrug:

jonadb
07-28-2011, 04:18 PM
I did some searching and found this:



Question

I have a long delay when closing applications using hooks by clicking the x button in the titlebar. If I close the application via another event (button click) for example, that works fine.

Answer

It's a known bug of Microsoft. It has to do with the Windows themes. If you disable the Windows themes, the problem goes away. Another choice is to have the hook code run in a secondary thread.



http://www.codeproject.com/KB/cs/globalhook.aspx

I'll have a shot at it..

lo
07-28-2011, 04:21 PM
I tried the recommendation on that page and could not get it to work... when launched on a secondary thread, the callback did not work.

Please let us know if you manage to get it to work.

jonadb
07-28-2011, 07:12 PM
I use this modification to start it in a new thread (not sure if this is the way to do it)

str+=" public InterceptMouse()\n"
str+=" {\n"
str+=" Thread thread = new Thread(new ThreadStart(WorkThreadFunction)); \n"
str+=" thread.Start(); \n"

str+=" }\n"


str+=" public void WorkThreadFunction()\n"
str+=" {\n"
str+=" _hookID = SetHook(HookCallback);\n"
str+=" MaxHwnd = IntPtr.Zero;\n"
str+=" restrictToMax = false;\n"
str+=" }\n"


As predicted this stop the hook from working, maybe it's a message pump failing somehow? Maybe it needs a DispatchMessage() somewhere.. my magic is weak in this area :)

Kickflipkid687
01-19-2012, 10:41 PM
I tried using this, but instead of printing I set another Global to the state, so mouse move, up, down.

However, when I check my global, it keeps reporting moving and not up/down like it should when I click the mouse. It does at first, but then loses track or something?

CGTalk Moderation
01-19-2012, 10:41 PM
This thread has been automatically closed as it remained inactive for 12 months. If you wish to continue the discussion, please create a new thread in the appropriate forum.