 |
AutoHotkey Community Let's help each other out
|
| View previous topic :: View next topic |
| Author |
Message |
Laszlo
Joined: 14 Feb 2005 Posts: 4032 Location: Pittsburgh
|
Posted: Thu Mar 30, 2006 11:20 pm Post subject: |
|
|
| evl wrote: | | How do I "see how many wheel events are buffered"? | Run the script and scroll on a large website, like this one. If you turn the wheel once quickly down, you see the window still scrolling after a second, or so. Exit the script and turn the wheel again: the time of the window still scrolling after you released the wheel is much shorter.
| evl wrote: | | what does "120 << 16" do? | Shift left by two bytes.
Here is a more complete experimental script. I am not happy with its speed. Also, in MS Word, sometimes there is no scroll, only some flickering. | Code: | CoordMode Mouse, Screen
SetBatchLines -1
SetMouseDelay -1
Process Priority,,R
WheelTime = 500
WheelDelta:= 120 << 17 ; doubled
WheelMax := 5 * WheelDelta ; to optimize
CntDelta := 40 << 17 ; code
WheelDown:: ; scroll window under mouse
WheelUp::
Critical
If (A_ThisHotKey <> A_PriorHotKey OR A_TimeSincePriorHotkey > WheelTime)
WCnt = %WheelDelta%
Else If (WCnt < WheelMax)
WCnt+= CntDelta
MouseGetPos m_x, m_y
If (m_x <> m_x0 OR m_y <> m_y0) {
m_x0 = %m_x%
m_y0 = %m_y%
hw_m_target := DllCall("WindowFromPoint", "int",m_x, "int",m_y)
}
SendMessage 0x20A,((A_ThisHotKey="WheelUp")-.5)*WCnt,(m_y<<16)|m_x,,ahk_id %hw_m_target%
Return |
|
|
| Back to top |
|
 |
Laszlo
Joined: 14 Feb 2005 Posts: 4032 Location: Pittsburgh
|
Posted: Thu Mar 30, 2006 11:45 pm Post subject: |
|
|
Instead of buffering all the hotkeys with "critical", we can buffer only a certain number of them with the 2 lines below, added in front of the hotkey definitions (and removing critical) | Code: | #MaxThreadsBuffer On
#MaxThreadsPerHotkey 5 | With this shorter memory there is no such a long scrolling period, after the whell was released. |
|
| Back to top |
|
 |
Laszlo
Joined: 14 Feb 2005 Posts: 4032 Location: Pittsburgh
|
Posted: Fri Mar 31, 2006 5:17 am Post subject: |
|
|
This version seems to work the best, so far. It has geometrically increasing acceleration. Instead of SendMessage, it uses PostMessage, which does not wait for a response. Sleep 0 is used to give the application time to scroll. This way there is no need for buffering. | Code: | CoordMode Mouse, Screen
SetBatchLines -1
WheelTime = 500 ; when to reset counting
WheelDelta:= 120 << 17 ; doubled, to optimize code
WheelDown::
WheelUp::
If (A_ThisHotKey <> A_PriorHotKey OR A_TimeSincePriorHotkey > WheelTime)
WCnt = %WheelDelta%
Else WCnt+= WCnt>>2
MouseGetPos m_x, m_y
hw_m_target := DllCall("WindowFromPoint", "int",m_x, "int",m_y)
PostMessage 0x20A,((A_ThisHotKey="WheelUp")-.5)*WCnt,(m_y<<16)|m_x,,ahk_id %hw_m_target%
Sleep 0
Return |
Question: "MouseGetPos m_x, m_y, hw_m_target" should get the window ID under the mouse pointer (so there were no need for the dll call WindowFromPoint), but most of the time it returns the wrong value (MS Word seems to work, but most other applications don't). Is it a bug in AHK? |
|
| Back to top |
|
 |
Babis
Joined: 08 Dec 2005 Posts: 45
|
Posted: Fri Mar 31, 2006 8:02 am Post subject: |
|
|
Very nice thanks
With the last script, in some progs like editplus, if I scroll hard, the scroll bar after reaches the end starts again from the beginning. Small bug maybe?
I love the acceleration. |
|
| Back to top |
|
 |
evl
Joined: 24 Aug 2005 Posts: 1238
|
Posted: Fri Mar 31, 2006 2:11 pm Post subject: |
|
|
| There's still the same problem with, if you scroll too fast, then you end up not scrolling as far as if you'd just scrolled slowly. It can probably be worked around with the acceleration / time since previous hotkey, but maybe Chris knows the exact cause of the wheel up/down hotkeys seemingly not receiving all events when scrolled very fast? |
|
| Back to top |
|
 |
Chris Site Admin
Joined: 02 Mar 2004 Posts: 10467
|
Posted: Fri Mar 31, 2006 5:13 pm Post subject: |
|
|
The answer may be that the system combines fast consecutive wheel turns into a single event with a larger-than-usual delta/movement parameter. If this is in fact what happens, AHK will only generate one WheelUp/Down hotkey event even when the delta is larger than usual.
I will investigate and try to come up with a solution. Thanks for pointing it out. |
|
| Back to top |
|
 |
evl
Joined: 24 Aug 2005 Posts: 1238
|
Posted: Fri Mar 31, 2006 5:17 pm Post subject: |
|
|
Thanks for taking a look It can probably be worked around for a script like this (although it may be a bit ugly), but for other scripts that might need to keep a more accurate count it could be a big problem (at least worth mentioning in the manual if it can't be solved). |
|
| Back to top |
|
 |
Laszlo
Joined: 14 Feb 2005 Posts: 4032 Location: Pittsburgh
|
Posted: Fri Mar 31, 2006 8:31 pm Post subject: |
|
|
Chris: do you know why MouseGetPos returns a window ID which is different from what WindowFromPoint does? (My question a few posts ago.) It looks like the later returns the ID of the control. Who knew that controls have that, too?
But, we don't really need WindowFromPoint. MouseGetPos returns the window ID and the name of the control under the mouse pointer. If we use them both in SendMessage, it works: | Code: | ...
MouseGetPos m_x, m_y, WinID, Ctrl
PostMessage 0x20A,((A_ThisHotKey="WheelUp")-.5)*WCnt,(m_y<<16)|m_x,%Ctrl%,ahk_id %WinID%
... |
|
|
| Back to top |
|
 |
Chris Site Admin
Joined: 02 Mar 2004 Posts: 10467
|
Posted: Sun Apr 02, 2006 2:33 am Post subject: |
|
|
| Laszlo wrote: | | do you know why MouseGetPos returns a window ID which is different from what WindowFromPoint does? | It's designed to report the parent window's unique ID, never the control's. This is because AHK is largely oriented toward parent windows when it comes to unique IDs. This philosophy may shift in the future since there is growing demand to operate upon controls individually.
By the way, the ahk_id technique can be used to directly operate on a control via PostMessage and other windowing commands. But you have to know its unique ID (HWND), either via the GetChildHWND() function (on the DllCall page) or some other function like the WindowFromPoint() you mentioned. |
|
| Back to top |
|
 |
Laszlo
Joined: 14 Feb 2005 Posts: 4032 Location: Pittsburgh
|
Posted: Sun Apr 02, 2006 2:41 am Post subject: |
|
|
Thanks, Chris. The Control, WinID combination from MouseGetPos seems to work, though.
Here is yet another experimental version. It starts with setting up the acceleration profile, in this case a geometrically increasing sequence from 1 to 250, stored in the array S. (Be careful, not to use too large values. They could be interpreted as negative by some applications.)
The hotkeys start with determining the speed of the wheel. It fluctuates strongly, therefore the average speed of the last two wheel events are computed and stored in the variable Speed. For that, we need to buffer one hotkey (#MaxThreadsPerHotkey 2); otherwise the speed computation would be wrong at fast turning wheel, when an event could be lost. The tick counter sometimes does not change between the hotkey routine activations, so we add 10 ms to the time difference. It avoids divisions with 0, and also limits the speeds values to be between 1 and 31 (with the numerator 300).
The distance, how much we want to scroll is dependent on the speed of the wheel. Its value is taken from the array S, and accumulated in the variable WCnt.
If the last wheel event was sent to the application within 30 ms, we just return, and will send the next wheel event with a larger scroll value. This way we don't send too many messages.
If enough time has passed since the last scroll, look where the mouse pointer is, and send the corresponding control a WM_MOUSEWHEEL message with the accumulated scroll amount in WCnt. Its sign (scroll up or down) is determined from the current hotkey name.
Most applications don't need to know the mouse position, but some, like MS Internet Explorer do not scroll, if the absolute screen coordinates are not included in the message, as two 16-bit values.
Sometimes, when the wheel is turned too fast, some mice randomly generate wheel events in the wrong direction. To make the script more robust, we could check if after WheelUp events there were a few WheelDown events within a very short time (or the other way around). If there were, we should ignore them, but let's fix possible other problems first.
| Code: | CoordMode Mouse, Screen
Loop 31
S%A_index% := Round(.5*1.222**A_Index)*(120 << 17) ; doubled, to optimize code
#MaxThreadsPerHotkey 2
WheelDown::
WheelUp::
Speed := 1 + 300//(A_TickCount-Tick0+10)
Tick0 = %Tick%
Tick = %A_TickCount%
WCnt += S%Speed%
If (Tick < WTick + 30)
Return
WTick = %Tick%
MouseGetPos m_x, m_y, WinID, Ctrl
PostMessage 0x20A,((A_ThisHotKey="WheelUp")-.5)*WCnt,(m_y<<16)|m_x,%Ctrl%,ahk_id %WinID%
WCnt = 0
Return |
|
|
| Back to top |
|
 |
Chris Site Admin
Joined: 02 Mar 2004 Posts: 10467
|
Posted: Mon Apr 03, 2006 4:49 pm Post subject: |
|
|
In today's v1.0.43.03, mouse wheel hotkeys (WheelDown/Up) report the number of wheel turns in A_EventInfo, which allows distinguishing between fast and slow wheel movement.
Please give this a try and let me know if it helps enough. Thanks. |
|
| Back to top |
|
 |
Laszlo
Joined: 14 Feb 2005 Posts: 4032 Location: Pittsburgh
|
Posted: Mon Apr 03, 2006 9:06 pm Post subject: |
|
|
The A_EventInfo in mouse wheel hotkeys of AHK v1.0.43.03 works very well. Thanks, Chris!
The script below utilizes this new feature (this time with a linear acceleration profile). It is the simplest and the one closest in behavior to the original wheel handling of Windows, except, the control under the mouse pointer is scrolled, not the active one. | Code: | CoordMode Mouse, Screen
n = 20
Loop %n%
S%A_index% := (4*A_Index-3)*(120 << 17) ; Linear acceleration
#MaxThreadsPerHotkey 2 ; Buffer one wheel event
WheelDown::
WheelUp::
Events += A_EventInfo ; Accumulate events
IfLess A_TimeSincePriorHotkey,20, Return ; Not to send wheel events too often
Speed := 1 + 16*Events//A_TimeSincePriorHotkey
Events = 0 ; Reset counting
IfGreater Speed,%n%, SetEnv Speed,%n% ; Max speed at S%n%
MouseGetPos m_x, m_y, WinID, Ctrl
PostMessage 0x20A,((A_ThisHotKey="WheelUp")-.5)*S%Speed%,(m_y<<16)|m_x,%Ctrl%,ahk_id %WinID%
Return |
There are several parameters to control the acceleration:
- n = the number of different speed values we distinguish. 10 works reasonably well, too.
- The number 4 in setting up the acceleration profile (S%A_index% := (4*A_Index-3)...). A larger value increases the speedup at faster turning the wheel, but reduces the accuracy at low speeds. Subtract one less in the expression above, to have 1 scroll step at low speeds.
- The number 16 in calculating the speed of the wheel (Speed := 1 + 16*Events//A_TimeSincePriorHotkey). A larger value leads to higher speeds values computed, which reduces the speed-resolution.
With a simpler acceleration profile we could eliminate the S array all together. | Code: | CoordMode Mouse, Screen
WheelDown::
WheelUp::
Events += A_EventInfo ; Accumulate events
IfLess A_TimeSincePriorHotkey,20, Return ; Not to send wheel events too often
MouseGetPos m_x, m_y, WinID, Ctrl
PostMessage 0x20A,((A_ThisHotKey="WheelUp")-.5)*Events*(120<<17),(m_y<<16)|m_x,%Ctrl%,ahk_id %WinID%
Events = 0 ; Reset counting
Return | Sending wheel events too often to the applications (like MSIE6) leads to lazy reactions. If you don't mind it, the script can be further simplified, and still works OK. | Code: | CoordMode Mouse, Screen
WheelDown::
WheelUp::
MouseGetPos m_x, m_y, WinID, Ctrl
PostMessage 0x20A,((A_ThisHotKey="WheelUp")-.5)*A_EventInfo*(120<<17),(m_y<<16)|m_x,%Ctrl%,ahk_id %WinID%
Return |
|
|
| Back to top |
|
 |
evl
Joined: 24 Aug 2005 Posts: 1238
|
Posted: Mon Apr 03, 2006 11:40 pm Post subject: |
|
|
| I've tried testing both the short and longer versions but have found that PSPad and the main window in Avant Browser are ignoring the PostMessage - is there another way to scroll a control under the mouse? (I think Katmouse has some extra code to do with a few specific apps but, must use another method for generic programs like these). |
|
| Back to top |
|
 |
Laszlo
Joined: 14 Feb 2005 Posts: 4032 Location: Pittsburgh
|
Posted: Tue Apr 04, 2006 1:05 am Post subject: |
|
|
How about Shimanov's solution? | Code: | ...
MouseGetPos m_x, m_y
hw_m_target := DllCall("WindowFromPoint", "int",m_x, "int",m_y)
PostMessage 0x20A,((A_ThisHotKey="WheelUp")-.5)*A_EventInfo*(120<<17),(m_y<<16)|m_x,,ahk_id %hw_m_target%
... |
|
|
| Back to top |
|
 |
evl
Joined: 24 Aug 2005 Posts: 1238
|
Posted: Tue Apr 04, 2006 1:10 am Post subject: |
|
|
| Well that just scrolls the active control in the active window (like normal windows behaviour), not the control under the mouse - which is why I use KatMouse in the first place. |
|
| Back to top |
|
 |
|
|
You can post new topics in this forum You can reply to topics in this forum
|
Powered by phpBB © 2001, 2005 phpBB Group
|