Jump to content

Sky Slate Blueberry Blackcurrant Watermelon Strawberry Orange Banana Apple Emerald Chocolate
Photo

Wheel button emulation script


  • Please log in to reply
21 replies to this topic
The Iron Savior
  • Members
  • 7 posts
  • Last active: May 04 2013 12:33 PM
  • Joined: 16 Aug 2005
I suffer with SERIOUS RSI in both of my arms and hands. Using a mouse is the most painful thing I can think of these days. Lately I've been using different trackballs to see which ones I like best. The one that is most comfortable to me so far is the Logitech Marble Mouse. It's just beautiful, one light flick of my finger gets the cursor wherever I need it. It has precision, ease of manipulation, comfortable form, and best of all it can be used with either hand without too much trouble (I'm not ambidextrous, after all). This trackball has 4 buttons and NO SCROLL WHEEL! I was at a loss for words when I found out that the included software didn't have any way to compensate for this. I assumed that it would have some sort of function where I can click a button and then spin the ball to get that scrolling effect. Sigh... no such software was included.

I started scouring the internet for something useful and I came upon AHK, it looked like just the thing. Took me little time to grasp the language and I started banging out all kinds of crazy non-working code. Luckily, my experience from my previous career in software development (which was ended very early by RSI--I'm sure there's irony in there somewhere) kicked in and I got some code here for you all that I'm sure at least one of you can appreciate.

This script will make any mouse (sans wheel or otherwise) have access to a middle-click and scroll wheel function! If you use the default settings, the 3rd (perhaps more accurately the 4th since the scroll wheel is usually considered the 3rd) button on my trackball can be held down while I spin the ball foreward or back to generate WheelUp and WheelDown events. As an added bonus, I'm throwing in the middle-click for free. I always used the middle button for opening links in new tabs in Firefox. To generate a middle click event, just click the trigger key down and up without moving the mouse at all.

You Can't drag and drop with the middle click, but who drags and drops with the middle button anyway? Also, it doesn't work with joystick buttons as I have it here--Joystick buttons appearantly don't generate key-up events. As you can see in the code, I use KeyDown and KeyUp hotkeys to set a variable to the state of the key. I could have eliminated the KeyDown variable in favor of GetKeyState() within the Timer function, but I thought that might hit the system too hard. Anywho, it would be easy to drop KeyDown for GetKeyState(), if you needed to use a joystick.

Don't forget to configure it before using it. Your mouse may not have "XButton"s and you might prefer to use another key or combination of keys.

Here it is:
;;
;; Emulate_Scrolling_Middle_Button.ahk
;; Author: Erik Elmore <[email protected]>
;; Version: 1.1 (Aug 16, 2005)
;;
;; Enables you to use any key with cursor movement
;; to emulate a scrolling middle button.  While
;; the TriggerKey is held down, you may move the
;; mouse cursor up and down to send scroll wheel
;; events.  If the cursor does not move by the
;; time the TriggerKey is released, then a middle
;; button click is generated.  I wrote this for my
;; 4-button Logitech Marble Mouse (trackball),  
;; which has no middle button or scroll wheel.
;;

;; Configuration

;#NoTrayIcon

;; Higher numbers mean less sensitivity
esmb_Threshold = 5

;; This key/Button activates scrolling
esmb_TriggerKey = XButton1

;; End of configuration

#Persistent
CoordMode, Mouse, Screen
Hotkey, %esmb_TriggerKey%, esmb_TriggerKeyDown
HotKey, %esmb_TriggerKey% Up, esmb_TriggerKeyUp
esmb_KeyDown = n
SetTimer, esmb_CheckForScrollEventAndExecute, 10
return

esmb_TriggerKeyDown:
esmb_Moved = n
esmb_FirstIteration = y
esmb_KeyDown = y
MouseGetPos,, esmb_OldY
return

esmb_TriggerKeyUp:
esmb_KeyDown = n
;; Send a middle-click if we did not scroll
if esmb_Moved = n
    MouseClick, Middle
return

esmb_CheckForScrollEventAndExecute:
if esmb_KeyDown = n
    return

MouseGetPos,, esmb_NewY
esmb_Distance := esmb_NewY - esmb_OldY
if esmb_Distance
    esmb_Moved = y

;; Do not send clicks on the first iteration
if esmb_FirstIteration = y
    esmb_FirstIteration = n
else if esmb_Distance > %esmb_Threshold%
{
    esmb_OldY := esmb_OldY + esmb_Threshold
    MouseClick, WheelDown
}
else if esmb_Distance < -%esmb_Threshold%
{
    esmb_OldY := esmb_OldY - esmb_Threshold
    MouseClick, WheelUp
}

return
It's my first script ever for AHK, so try to keep the criticism constructive. I'm sure I can use it.

Enjoy!

[Edit]

Chris
  • Administrators
  • 10727 posts
  • Last active:
  • Joined: 02 Mar 2004
Anything that reduces RSI during mousing is a great thing to have in the forum. Thanks for sharing this carefully crafted technique.

toralf
  • Moderators
  • 4035 posts
  • Last active: Aug 20 2014 04:23 PM
  • Joined: 31 Jan 2005
Nice work,

Here are my comments:
- instead of using y/n for the status vars, you could use ":=True" and ":= False" and then evaluate them with "If var" or "If not Var". But that is cosmetics.
- another cosmetic: If you would indent the script syntactically it would help to read the code. (have a look at the script Auto-Syntax-Tidy, if you do not want to do it manually)
- And at last: What happens when you finished scrowlling? Does the mouse move back to where it has been when started? A mouse with a scroll wheel doesn't move it's pointer when scrolling. So I suggest to get the mouse position at the start of scrolling and place it at the same position after scrolling. Just an idea.
Ciao
toralf
 
I use the latest AHK version (1.1.15+)
Please ask questions in forum on ahkscript.org. Why?
For online reference please use these Docs.

The Iron Savior
  • Members
  • 7 posts
  • Last active: May 04 2013 12:33 PM
  • Joined: 16 Aug 2005
I wasn't 100% sure on how AHK handled booleans but most of the code I saw used y and n. I have always prferred "If x" and "If not x", anyway. I'll redo this when I get off work. I may be wrong here, but I'm pretty sure I already indented the code syntatically. Some elaboration would be appreciated if I'm breaking some sort of AHK script conventions. I'm pretty irritated that I had to use the style of blocks like this:
if whatever
{
    ...
}
when I MUCH prefer this:
if whatever {
    ...
}
The really irritating part is AHK didn't raise any flags when I did my braces my own way and that style of blocking was causing all sorts of undefined flow of execution.

About returning the cursor to its original location. THIS script as it is presented here (v1.1) does not move the mouse back. I did have a version before posting that would try to keep the mouse in the same spot all the time and it worked well and it was even a feature that you could turn off and on. In the end, it didn't really add anything valuable and I didn't like the way it made my code look, so I just scrapped the whole idea.

Chris
  • Administrators
  • 10727 posts
  • Last active:
  • Joined: 02 Mar 2004

I MUCH prefer this:

if whatever {
    ...
}

That can't be supported for old-style IF statements due to ambiguity (because the "{" might be a literal part of the line itself). I'll look into supporting it for expressions such as if (x > 3) {.

The really irritating part is AHK didn't raise any flags when I did my braces my own way

A script such as the following should produce the error "attempt to close a non-existent block":

if x = 3 {
msgbox
}

The Iron Savior
  • Members
  • 7 posts
  • Last active: May 04 2013 12:33 PM
  • Joined: 16 Aug 2005
I'm sure I messed up all kinds of other stuff while trying to learn the syntax, it might not have been that, exactly. I had a real hard time for a few hours trying to wrap my brain around the syntax.

dmitri926
  • Guests
  • Last active:
  • Joined: --
is it possible to make this script work with scrolling horizontally as it is with scrolling vertically

The Iron Savior
  • Members
  • 7 posts
  • Last active: May 04 2013 12:33 PM
  • Joined: 16 Aug 2005

is it possible to make this script work with scrolling horizontally as it is with scrolling vertically

Yes, very easily.

dmitri926
  • Members
  • 3 posts
  • Last active: May 05 2006 03:20 PM
  • Joined: 04 May 2006
Hey I used your script for a bit and it was awsome but i personally had a few issues with it.

I think you should give this one a shot, I found it somewere and tinkered with it for a bit to my liking.

If you click the XButton1 it acts as a regular MButton click (just a click), this works in firefox for opening/closing tabs, scrolling (you click it again to stop scrolling, etc). In addition to that, you can PRESS&HOLD XButton1 and move the cursor vert or horz to scroll. This works not only in firefox, but everywhere else also, whereas, the MButton mostly works in firefox because it has it's own mechanism for handling mice.

And XButton2 = back button...

This is the script i modified: http://www.autohotke...opic.php?t=5186

Also you would have to dl the images for the autoscroll icons: http://www.almondcro....com/Images.zip and place them in C:\mouse_images or wherever you want (just modify the script with new location)

DetectHiddenWindows, On
CoordMode, Mouse, Screen

;// AutoScroll: Root Directory for images
as_imageroot = C:\mouse_images

;// AutoScroll: One acceleration notch in pixels
as_notch = 20

;// Bind Middle Mouse Button to AutoScroll
$XButton1::GoSub, MouseButtonHandler
XButton2::Send, {BROWSER_BACK} 


;// Mouse Button Handler Function.
;// Any windows that you don't want AutoScroll to function in
;// can be filtered out here. I've filtered out FireFox; it
;// has its own AutoScroll mode.
MouseButtonHandler:
  MouseGetPos, mbh_x, mbh_y, mbh_id, mbh_ctrl, 1
  WinGetClass, mbh_class, ahk_id %mbh_id%

  if ( mbh_class = "1MozillaUIWindowClass" )
  {

    MouseClick, Middle
  }
  else
  {
	KeyWait, XButton1, T0.15
	if ErrorLevel
		GoSub, AutoScroll
	else
		MouseClick, Middle
    
  }
  return


;// Main AutoScroll Function
AutoScroll:

  ;// Store the point where the button was clicked
  as_startx = %mbh_x%
  as_starty = %mbh_y%

  ;// Figure out the center point of the image
  as_splashx := ( as_startx - 10 )
  as_splashy := ( as_starty - 10 )

  ;// Provide the first image name
  as_image = center
 
  ;// Initialize some values
  as_xlast = 0
  as_ylast = 0
  as_xlastvector = 0
  as_ylastvector = 0

  Loop
  {
    as_changed = 0
   
    ;// Get the latest mouse position
    MouseGetPos, as_x, as_y

    ;// Break out of the loop if the Left Mouse Button is clicked
 ;   GetKeyState, as_state, LButton, P
;    if as_state = D
 ;     break
    
	  	;//break ouf of loop if XButton1 is depressed
	GetKeyState, as_state2, XButton1, P
    if as_state2 = U
      break
  
	if as_x != %as_xlast%
    {
      as_xvector := round((as_startx - as_x)/100)

      if as_xvector != %as_xlastvector%
      {
        as_xspeed := 10 // Abs(as_xvector)
 
        if as_xvector = 0
          SetTimer, AutoScrollX, Off
        else
          SetTimer, AutoScrollX, %as_xspeed%
   
        as_xlastvector = %as_xvector%
      }
      as_xlast = %as_x%
      as_changed = 1
    }


 
    if as_y != %as_ylast%
    {
      as_yvector := round((as_starty - as_y)/as_notch)
      as_yvector := (as_yvector * Abs(as_yvector))

      if as_yvector != %as_ylastvector%
      {
        if as_yvector = 0
        {
          SetTimer, AutoScrollY, Off
        }
        else
        {
          as_yspeed := ((400 // Abs(as_yvector)) + 1)
          SetTimer, AutoScrollY, %as_yspeed%
        }
   
        as_ylastvector = %as_yvector%
      }
      as_ylast = %as_y%
      as_changed = 1
    }



    ;// Update the image if necessary
    if as_changed = 1
    {
      if (as_xvector=0 and as_yvector=0)
        as_image = center
      else if (as_xvector=0 and as_yvector>0)
        as_image = n
      else if (as_xvector=0 and as_yvector<0)
        as_image = s
      else if (as_xvector>0 and as_yvector=0)
        as_image = w
      else if (as_xvector<0 and as_yvector=0)
        as_image = e
      else if (as_xvector<0 and as_yvector>0)
        as_image = ne
      else if (as_xvector>0 and as_yvector>0)
        as_image = nw
      else if (as_xvector>0 and as_yvector<0)
        as_image = sw
      else if (as_xvector<0 and as_yvector<0)
        as_image = se

      if as_image != %as_lastimage%
      {
        SplashImage, %as_imageroot%\%as_image%.bmp, b X%as_splashx% Y%as_splashy% Hide, , , autoscrollsplash
        WinSet, TransColor, FF00FF , autoscrollsplash
        SplashImage, Show, , , , autoscrollsplash
        as_lastimage = %as_image%
      }
	  

    }

    sleep, 10
  }

  GoSub, AutoScrollQuit
  return

AutoScrollY:
  ControlGetFocus, asy_control, A
  if as_yvector > 0
    MouseClick, WU
  else
    MouseClick, WD
  return

AutoScrollX:
  ControlGetFocus, asx_control, A
  if as_xvector > 0
    SendMessage, 0x114, 0, 0, %asx_control%, A
  else
    SendMessage, 0x114, 1, 0, %asx_control%, A
  return

AutoScrollQuit:
  SplashImage, Off
  SetTimer, AutoScrollY, Off
  SetTimer, AutoScrollX, Off
  return 


Trabatar57
  • Guests
  • Last active:
  • Joined: --
Hey, this is a great script but in Microsoft Word it scrolls too quickly. Anyone know how to slow it down?

Thanks!

  • Guests
  • Last active:
  • Joined: --
Don't spin the ball so fast

rk_keys
  • Members
  • 1 posts
  • Last active: Apr 21 2007 07:42 PM
  • Joined: 15 Apr 2007
Thanks!.. this is such an useful script for me as my bluetooth mouse does not have a middle/scroll button. I did not know how to use the XButton1 and had to map it to delayed RightMouse Click to enable the scroll.

However, I got a question -- why doesn't this functionality work in Windows Explorer?

xordos
  • Guests
  • Last active:
  • Joined: --
Thanks a lot for this script, my mouse(actually trackball) don't have scrollwheel, this works great, but I found when scrolling, the point actually still moves, so for long docs, it may move to out of the window.

Following is my patch, give it a try, you may like it, may be not...:)
after line:
MouseGetPos,, esmb_OldY
add line:
MouseGetPos, esmb_OrigX, esmb_OrigY

Then at very end just before last line:
return
add lines:
else
{
MouseMove,esmb_OrigX,esmb_OrigY
MouseGetPos,, esmb_OldY
}

Hope you like the patch

yetanotherjosh
  • Members
  • 1 posts
  • Last active: Mar 02 2013 07:36 PM
  • Joined: 28 Feb 2013

Thanks so much for this script. Still works like a charm many years later! I have the same issue with RSI and am using the same trackball mouse as the OP. I liked xordos' idea of preventing the cursor from moving while the scrolling is engaged, but I've implemented what I think is a more robust solution to that problem:

 

;;
;; Emulate_Scrolling_Middle_Button.ahk
;; Author: Erik Elmore <[email protected]>
;; Version: 1.1 (Aug 16, 2005)
;;
;; Enables you to use any key with cursor movement
;; to emulate a scrolling middle button.  While
;; the TriggerKey is held down, you may move the
;; mouse cursor up and down to send scroll wheel
;; events.  If the cursor does not move by the
;; time the TriggerKey is released, then a middle
;; button click is generated.  I wrote this for my
;; 4-button Logitech Marble Mouse (trackball),  
;; which has no middle button or scroll wheel.
;;

;; Configuration

;#NoTrayIcon

;; Higher numbers mean less sensitivity
esmb_Threshold = 5

;; This key/Button activates scrolling
esmb_TriggerKey = XButton1

;; End of configuration

#Persistent
CoordMode, Mouse, Screen
Hotkey, %esmb_TriggerKey%, esmb_TriggerKeyDown
HotKey, %esmb_TriggerKey% Up, esmb_TriggerKeyUp
esmb_KeyDown = n
SetTimer, esmb_CheckForScrollEventAndExecute, 10
return

esmb_TriggerKeyDown:
  esmb_Moved = n
  esmb_FirstIteration = y
  esmb_KeyDown = y
  MouseGetPos, esmb_OrigX, esmb_OrigY
  esmb_AccumulatedDistance = 0
return

esmb_TriggerKeyUp:
  esmb_KeyDown = n
  ;; Send a middle-click if we did not scroll
  if esmb_Moved = n
    MouseClick, Middle
return

esmb_CheckForScrollEventAndExecute:
  if esmb_KeyDown = n
    return
  
  MouseGetPos,, esmb_NewY
  esmb_Distance := esmb_NewY - esmb_OrigY
  if esmb_Distance
    esmb_Moved = y

  esmb_AccumulatedDistance := (esmb_AccumulatedDistance + esmb_Distance)
  esmb_Ticks := (esmb_AccumulatedDistance // esmb_Threshold) ; floor divide
  esmb_AccumulatedDistance := (esmb_AccumulatedDistance - (esmb_Ticks * esmb_Threshold))
  esmb_WheelDirection := "WheelDown"
  if (esmb_Ticks < 0) {
    esmb_WheelDirection := "WheelUp"
    esmb_Ticks := (-1 * esmb_Ticks)
  }

  ;; Do not send clicks on the first iteration
  if esmb_FirstIteration = y
    esmb_FirstIteration = n
  else {
    Loop % esmb_Ticks {
      MouseClick, %esmb_WheelDirection%
    }
  }

  MouseMove,esmb_OrigX,esmb_OrigY,0
return



starpause
  • Members
  • 1 posts
  • Last active: Apr 08 2013 03:57 AM
  • Joined: 04 Apr 2013

@yetanotherjosh thanks much for sharing your updated script that returns the mouse cursor to it's original position, exactly what i was looking for!

 

However, this doesn't work in all programs for me. Specifically, MediaMonkey and MixedInKey both do not receive the WheelUp/WheelDownevents. Has anyone experienced similar of have any ideas regarding what's going on? Running Win7.