2

For any who do not know what autoscrolling is, you can experience it in Chromium with the autoscroll extension (speed set to 3), and in firefox by enabling the 'use autoscroll' option.

Enabling middle-mouse scrolling is not that hard, in fact all I had to do was this:

xinput set-prop 8 "libinput Scroll Method Enabled" 0, 0, 1 

However it does not behave the way I want at all.

Functionality I want:

  • When clicked, it enables 'locked scrolling' so moving the mouse up or down without holding down the button should scroll. Until Middle Mouse is pressed again.
  • When clicked or held, a symbol indicating the starting position appears.
  • When clicked or held, moving the mouse up/down from the initial point where you clicked should enable 'automatic scrolling' in that direction with speeds based on how far you moved the mouse, moving the mouse back to the starting point stops scrolling

Functionality I've got:

  • When clicked it does nothing (well, does what it's bound to do by the DE, an option I'd like to disable as I am fully accustomed to ctrl+v for pasting things)
  • When held, mouse movement behaves like the scroll wheel, e.g. scrolling requires constant movement of the mouse, there is no 'automatic scrolling' available.

As you can see that's a lot of missing functionality. Sure, I can just use the autoscroll extension for chrome at the start and it works, for chrome. But I want it systemwide, which is how it works in windows.

Now, maybe it would be a bit much work to get the complete autoscrolling functionality systemwide (or would it? I wouldn't know...) and depending on how it's implemented, it could cause problems in some applications that use the middle mouse button for a keybind (overriding application keybinds is a big no-no for sure).

But I would be quite happy if I could just get the automatic scrolling enabled for when the middle mouse button is held, even if perhaps not the complete functionality, it would be close enough.

2

I don't have a better answer outside that i have improved (in my opinion) the script cestarian provided, I wanted the horizontal scroll, and I also had issues with my Logitech mouse not being picked up correctly.

I also didn't see the reason as to having so many loops where a single loop will be much faster and allows for multi direction scrolling on the same cycle :D

Tested on xUbuntu 18.04.2 LTS

sudo apt install xdotool xsel sxhkd

~/.config/sxkhd/sxhkdrc

~button2
    "~/.config/sxkhd/autoscroll.sh"
@button2

~control + c
    echo -n | xsel -n -i
~control + shift + c
    echo -n | xsel -n -i
~control + x
    echo -n | xsel -n -i

~/.config/sxkhd/autoscroll.sh

#!/bin/dash
# Autoscroll (hold only)

middlemousebutton=2

mousescrollup=4
mousescrolldown=5
mousescrollleft=6
mousescrollright=7

enableverticalscroll=1
enablehorizontalscroll=1

## Danger below :D

echo -n | xsel -n -i
eval $(xdotool getmouselocation --shell)
starty=$Y
startx=$X
toggle=$middlemousebutton
while [ $toggle -eq $middlemousebutton ]
do
    eval $(xdotool getmouselocation --shell)
    curry=$Y
    currx=$X
    if [ $enableverticalscroll -eq 1 ]
    then
        if [ $curry -gt $starty ]
        then
            speedy=$(expr $curry / 100 - $starty / 100)
            if [ $speedy -gt 0 ]
            then
                xdotool click --repeat $speedy --delay 1 $mousescrolldown
            fi
        else
            speedy=$(expr $curry / 100  - $starty / 100  | sed 's:-::')
            if [ $speedy -gt 0 ]
            then
                xdotool click --repeat $speedy --delay 1 $mousescrollup
            fi
        fi
    fi

    if [ $enablehorizontalscroll -eq 1 ]
    then
        if [ $currx -gt $startx ]
        then
            speedx=$(expr $currx / 100 - $startx / 100)
            if [ $speedx -gt 0 ]
            then
                xdotool click --repeat $speedx --delay 1 $mousescrollright
            fi
        else
            speedx=$(expr $currx / 100  - $startx / 100  | sed 's:-::')
            if [ $speedx -gt 0 ]
            then
                xdotool click --repeat $speedx --delay 1 $mousescrollleft
            fi
        fi
    fi

    toggle=$(xinput --list "Virtual core pointer" | grep -i -m 1 "Button state:" | grep -o "[$middlemousebutton]\+")
    sleep 0.02
done

chmod +x ~/.config/sxkhd/autoscroll.sh

Make sure you add sxhkd to Session and Startup -> Autostart Applications

and everything should work on next boot

4
  • Nice, I wanted horizontal scrolling too but was too lazy to implement it. Also that defaultstate thing, I don't even remember how that was supposed to work (messy little script isn't it?...), but setting it to 1 fixed it on my other mouse, your script definitely is nicer. My defaultstate should have been 2 though according to how I'm supposed to find it out, but it only works if set to 1.
    – Cestarian
    May 27 '19 at 3:24
  • good point on the getting the state, i made a mistake in regards to defaultstate, i have updated the above to reflect its actual purpose. it is now activestate and i updated the instructions to match ^^
    – Azerothian
    May 28 '19 at 5:43
  • 1
    I just had epiphany!!! we don't need to use the target mouse to check for the state of the mouse button, i found that we can look at the button state in the virtual core pointer, if 2 then middle mouse button is pressed. solves the issue when switching between two different mice. i have updated the script with my current solution.
    – Azerothian
    May 28 '19 at 6:05
  • 1
    Here, I got tired of scrolling through my tabs by accident when I release the middle mouse button with the cursor at the top of my screen so I created a workaround that prevents your cursor from actually going there. I also added clickscroll support back in, optionally, thought you might be interested in the update: pastebin.com/YAhiZir8
    – Cestarian
    Jan 6 '20 at 7:38
2

(The script here has been considerably improved by Azerothian, additionally here's a further modified version where I added in a few workarounds and tricks as well as adding click-scrolling back in (it's disabled by default, but you just need to set 'enableclickscroll=1' at the top of the file to get it) with some major improvements. The script uses bash by default but is dash compatible)

I have a working solution in the form of a bash script, it's not really perfect but it gets the job done, at least for my purposes.

The script requires xdotool and xinput and xsel. sxhkd is the recommended solution for binding the script to middle-mouse.

(Note: the script is dash compatible, change #!/bin/bash to #!/bin/dash to use the faster interpreter if it is installed)

#!/bin/bash
#Autoscroll (hold only)
echo -n | xsel -n -i
eval $(xdotool getmouselocation --shell)
starty=$Y
mouseid=$(xinput --list | grep -i -m 1 'mouse' | grep -o 'id=[0-9]\+' | grep -o '[0-9]\+')
toggle=0

while [ $toggle -eq 0 ]
do
    sleep 0.02
    mmouse=$(xinput --query-state $mouseid | grep 'button\[2' | cut -d \= -f2)
    toggle=$(xinput --query-state $mouseid | grep 'button\[2' | grep -c up)
    eval $(xdotool getmouselocation --shell)
    curry=$Y

    #Scroll down
    while [ $curry -gt $starty ] && [ $toggle -eq 0 ]
    do
        eval $(xdotool getmouselocation --shell)
        curry=$Y
        speed=$(expr $curry / 100 - $starty / 100)
        xdotool click --repeat $speed --delay 1 5
        toggle=$(xinput --query-state $mouseid | grep 'button\[2' | grep -c up)
        sleep 0.02
    done

    #Scroll up
    while [ $curry -lt $starty ] && [ $toggle -eq 0 ]
    do
        eval $(xdotool getmouselocation --shell)
        curry=$Y
        speed=$(expr $curry / 100  - $starty / 100  | sed 's:-::')
        xdotool click --repeat $speed --delay 1 4
        toggle=$(xinput --query-state $mouseid | grep 'button\[2' | grep -c up)
        sleep 0.02
    done

done

And here's the sxhkd configuration: ./.config/.sxhkd/sxhkdrc

~button2
    "/path/to/autoscroll"
@button2

~control + c
    echo -n | xsel -n -i
~control + shift + c
    echo -n | xsel -n -i
~control + x
    echo -n | xsel -n -i

The above script when triggered by sxhkd will allow for system-wide autoscrolling, but only when the middle mouse button is held. Clicking middle mouse will function as normal but with the pasting functionality disabled (except for circumstances where you do not use ctrl+c or ctrl+x to copy things).

If you prefer to leave middle click pasting enabled (not recommended, unless you like accidentally pasting things), use this instead:

~button2
    "/path/to/autoscroll"
@button2

And also comment out or remove the first line of the autoscroll script (echo -n | xsel -n -i line) There are a few reasons why I use sxhkd and not xbindkeys.

  • sxhkd allows you to make the click for the bound button go through normally first before triggering the script (this is what ~button2 does), this enables the middle mouse button to maintain it's prior functionality alongside with executing this script.
  • For some reason for both sxhkd and xbindkeys, if I tried to bind the script to middle mouse it would only work if the libinput mousewheel emulation is turned on, if I turn it off, the script simply won't run through these programs (even if it works fine on it's own) in short, the solution to this was sxhkd's @ prefix, hence @button2.

The good news is that holding down middle mouse to scroll works flawlessly (as long as you are ok with not being able to paste things with middle mouse).

The bad news is that clicking middle mouse to toggle auto-scrolling has some quite serious issues which is why I did not leave it in the default script.

In order to enable clicking to toggle autoscrolling replace all instances of:

toggle=$(xinput --query-state $mouseid | grep 'button\[2' | grep -c up)

With:

if [ "$mmouse" = "down" ]; then
    toggle=$(xinput --query-state $mouseid | grep 'button\[2' | grep -c up)
else
    toggle=$(xinput --query-state $mouseid | grep 'button\[' | sort | grep -c down)
fi

Issues with both holding and clicking to scroll:

  • It scrolls rather fast at minimum speed, while probably appropriate for most users, I personally would prefer slower speeds. The most likely solution for this is lowering the scrolling speed for libinput, this is sadly not as easy as it should be (hence not yet tested by me).
  • Horizontal scrolling is not implemented, however it would be easy to implement (I just never use it so I had no reason to). Currently the entire script is based around the Y axis, copying what is already here and including the X axis would be a simple matter, and instead of mouse button 4 and 5 which are for vertical scrolling, you would use mouse button 6 and 7 for horizontal scrolling. (Fixed in Azerothian's version & a modified version)
  • There is no icon to indicate the starting position of the scrolling, and there is no cursor change to indicate that scrolling is active. I assume this shouldn't be that hard to solve, but I have assumed wrong before...
  • If your mouse cursor is at a scrollable location (like your browser tabs, or your taskbar, where you can scroll between tasks/tabs) when you release the button, it will undesirably scroll through it. (Mitigated with a workaround in a modified version)

Issues only with clicking to scroll:

  • Does not always trigger (this is a fault of sxhkd's current configuration, if I only use @button2 for the script and omit ~button2, it works perfectly fine, however, it would also mean that you could no longer use middle mouse to say open links in a new tab in a browser because the click would no longer be forwarded through) (fixed in a modified version by adding a 'clickdelay' variable to control the grace period you have to release the middle mouse button to activate clickscrolling)
  • Does not detect when middle click was actually used for something else. (e.g. if you use middle click to open a link in a new tab, scrolling is most likely going to be activated as a side effect of that middle click, a possible solution would be having a program that detects the cursor status and would only activate the script if the cursor is neutral or hovering text, or at least does not activate when hovering links, however I know no such software)
  • Pressing middle mouse again to stop scrolling does not work (you can press any other mouse button to stop scrolling instead however, like left or right click, so it's no big deal really) this can probably be solved by fiddling with the script a little bit but I didn't get around to it. (Fixed in a modified version)
  • Scrolling is not bound to the cursor's starting position, rather the cursor itself (this is not an issue when holding down the button). What this means, is lets say you're in a browser and you use this autoscrolling script, if you move the mouse too high up the screen, instead of scrolling up and down on the current page you will start scrolling through your tabs instead, which is less than ideal for sure as it means you must be careful of where you move your cursor while scrolling. (Mitigated in a modified version)

The first issue can sort of be solved by configuring ./.config/.sxhkd/sxhkdrc like this

@button2
    "/path/to/autoscroll"

However not only does this break your ability to hold-to-scroll, your middle mouse button will only function for autoscrolling, you will not be able to use the middle mouse button to open links in new tabs anymore without the ~button2 line. If you never use that functionality and never use middle mouse to paste things, this may suit you fine, but otherwise I'd advise against it.

The bottom line is, it works great for holding middle mouse to autoscroll, and it works not well at all for clicking to toggle autoscroll (which is why I left it disabled)

This satisfies my needs as requested by the OP however I will be waiting for a while for better answers if anyone happens to have a better solution before I mark this answer as the solution.

1
  • 1
    AMAZING! Finally can do autoscrolling on Linux properly.
    – Jire
    Nov 22 '20 at 6:35

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.