Pass mouse events through tmux

I have noticed that scrolling in tmux is a hazzle and was wondering if it was possible to have tmux pass them along to the application or terminal.

I may be understanding things wrong here but I have observed that I can scroll man pages in Terminology (my terminal emulator) but not when tmux is running. Mouse scrolling in man pages does not work in xterm even when tmux is not running, which makes me think that the terminal handles the mouse event AND THEN sends them along to the application.

So my current understanding of mouse events' flow is this:

terminal -> tmux

So the issue seems to be that tmux is blocking the events from getting to the underlying application. What is want is this:

terminal -> tmux -> application

Ideally I would combine the native scrolling of Terminology with tmux's copy-mode (given mouse-mode was enabled and that it entered and exited copy-mode automatically) but it doesn't seem to be possible as tmux handles scrolling on its own.

This is as close to what I want as I've come. Mouse scrolling works and automatically enters and exits copy-mode but in applications such as man it will scroll out of the application to old terminal history. Also the solution includes an unofficial patch which makes portability an issue.

So is it possible to have tmux just completely ignore mouse signals and send them along to the underlying terminal or application?


Solution 1:

If you view a manual page directly in terminology, the following happens:

less (the manual pager) switches the terminal to the so-called alternate screen, the one that does not have a scrollback buffer and from which when you quit your application you'll get back to the previous contents of the terminal. This mode is typically used by fullscreen applications (viewers, editors, file managers etc.).

less is not able to handle mouse at all, and terminology is perfectly aware that the application running inside is not interested in mouse events.

When terminology (in fact most of the terminal emulators) realizes that these two conditions are met (i.e. alternate screen, no interest in mouse), it converts scroll events into up and down keystrokes. So less doesn't see any mouse event, it only sees keypresses.


Now let's put tmux in between.

tmux is on one side just an application running in your terminal. Depending on its configuration it may or may not ask for mouse events; I assume you have it enabled since that's the default. It is not necessary for applications running inside to have any mouse support at all. tmux also switches to alternate screen.

tmux, on the other end, is itself a terminal emulator, just like terminology. It keeps track (for each window/pane) whether the application running there is interested in mouse, and whether the application has switched to alternate screen. These states are not reflected towards terminology, it's tmux's private business.

Theoretically there's nothing stopping tmux from behaving like graphical emulators do, and turn these mouse scroll events that it gets from terminology into up/down keystrokes for less running inside, since tmux is perfectly aware that the application running inside is using the alternate screen and is not interested in mouse events.

This is probably a feature that's missing from tmux, and I recommend you to file a feature request against them.


Let's make it more complicated if you're interested in juicy details. Let's take out tmux for now.

There's a small problem here to note. With traditional mouse the scroll wheel usually generates more than 1 keypress, maybe around 3-5, because otherwise scrolling would be way too slow. Touchpads are able to report much smoother coordinates, some terminal emulators (including terminology) do recognize these and hence report up/down keypresses one by one (let's say a single keypress after each 1/3rd unit of scrolling, unit meaning what the mouse wheel would do at once). (I don't have a mouse right now so I cannot verify that terminology with a mouse indeed jumps by multiple lines, but I assume so.)

Reporting multiple keypresses at once doesn't make sense in some circumstances, e.g. at the search prompt of less it goes back by maybe 3 entries of the history at once, it's totally useless and is a nice demonstration that this is indeed what's happening behind the scenes. With a terminal emulator like terminology plus a touchpad this is not an issue if an application does not care about mouse, so you can scroll line by line in e.g. less. However, if the application cares about mouse, you're back at the rough scrolling experience because the mouse protocol inside terminals can't report fine granularity, only the old-fashioned mouse scroll unit. E.g. in mcview you can't scroll smoothly, only by multiple lines at a time.

Let's put tmux back in the game. It's an alternate-screen mouse-aware app, just like mcview, so it can only receive mouse scroll events of old-fashioned large units. It should presumably send multiple up/down keystrokes at once, just as actual terminal emulators do on old-fashioned mouse scrolls, otherwise scrolling would be unbearably slow. So, even if tmux developers go ahead and implement this feature, you'll lose the smoothness of scrolling.

I've opened the bug https://bugzilla.gnome.org/show_bug.cgi?id=755183 to work on improving this, but no actual work has been done so far.

Solution 2:

This tmux issue has a .tmux.conf workaround that has worked quite well for me (I modified it to send three Up/Down signals instead of one per scroll event):

# Emulate scrolling by sending up and down keys if these commands are running in the pane
tmux_commands_with_legacy_scroll="nano less more man"

bind-key -T root WheelUpPane \
    if-shell -Ft= '#{?mouse_any_flag,1,#{pane_in_mode}}' \
        'send -Mt=' \
        'if-shell -t= "#{?alternate_on,true,false} || echo \"#{tmux_commands_with_legacy_scroll}\" | grep -q \"#{pane_current_command}\"" \
            "send -t= Up Up Up" "copy-mode -et="'

bind-key -T root WheelDownPane \
    if-shell -Ft = '#{?pane_in_mode,1,#{mouse_any_flag}}' \
        'send -Mt=' \
        'if-shell -t= "#{?alternate_on,true,false} || echo \"#{tmux_commands_with_legacy_scroll}\" | grep -q \"#{pane_current_command}\"" \
            "send -t= Down Down Down" "send -Mt="'

egmont's answer is spot on and led me to this workaround.

I opened another tmux issue requesting functionality similar to other terminal emulators.

Solution 3:

Mouse scrolling in less can be enabled using the --mouse command line option. You can avoid having to include the --mouse option every time you use less by defining an alias somewhere in your dot files:

alias less="less --mouse"

However, this alias will not enable mouse scrolling in man and git even though both commands use less to display their output in pages.

A better alternative, would be to define options for less using the LESS environment variable (see less CLI options for details).

Adding the following line to your dot files will enable mouse scrolling for all three commands: less, man and git.

export LESS="--mouse"