5

I have two monitors. One is 1920x1080 (the left screen), and the other is 1440x900 (the right screen).

Right now, I have it set up such that the tops of both monitors are aligned. That way, when I have my mouse at the very top of the left screen and I move my mouse to the right, it will move seamlessly to the right screen. However, if my mouse is on the bottom 180 pixels of the left screen and I move my mouse to the right, it won't move to the right screen, so I have to move my mouse up before I move it to the right because the right screen is smaller than the left screen.

My desired functionality: if I am at the bottom of the left screen and I move my mouse to the right, it should appear at the bottom of the right screen.

4 Answers 4

2

I was running into the same issue and as others have pointed out Ubuntu sees your monitors looking something like this (maybe order is the other way around):

+----------++----------------+
|          ||                |
|          ||                |
+----------+|                |
            |                |
            +----------------+

In this example when your mouse is in the right monitor on the bottom and the mouse is moved straight left you hit a dead zone where you can't cross screens. I modified the bash file that cassm posted to have the mouse height be proportional when crossing so if you cross your mouse 3/4 the way up from the left monitor, the mouse appears 3/4 the way up on the right monitor.

To run the script I ran follow the instructions below:

1) Make sure to setup your monitors right with the picture in the script (ONE ABOVE IS DIFFERENT) in Ubuntu's Displays settings

2) Make a text file called "mousejumper.sh" or anything ending in .sh

3) Right click on file and change Properties to make it executable

4) Copy and paste the script into file

    #! /bin/bash
    # Cursor relocation script by Cass May <[email protected]>

    # LEFTX and RIGHTX are the screen boundaries, which triggers cursor relocation
    # LEFTMONHEIGHT and LEFTMONHEIGHT are the heights in pixels of the left and right monitor
            # minus 1, because computers start counting at 0... so 1920 pixels heigh becomes 1919
    LEFTX="1919"
    LEFTMONHEIGHT="1080"

    RIGHTX="1920"
    RIGHTMONHEIGHT="2159"

    # Size in pixels to jump over the screens with when you hit the boundary (ex: if 5, jumps from x = 1919 to x=1924)
    BUFFER="5"


    # In Ubuntu Displays settings window the two monitors must be setup to look like this:
    #             +----------------+
    #             |                |
    # +----------+|                |
    # |          ||                |
    # |          ||                |
    # +----------++----------------+
    #
    # If the monitor setup is in a different order (ex: larger screen on left side) 
    # or there are more than two monitors, then the script will need to be modified

    # Tip: Set "Sticky Edges" to On in the Displays settings window because 
    # the script checks coordinates every 0.### seconds, and sticky edges
    # keeps your mouse there longer


    while true; do # keep loop going to run all the time, do a slight pause for performance

        # grab cursor position, and extract x and y position
        CURSORPOSITION="$(xdotool getmouselocation)"
        XPOS="$(grep -o '[0-9]\+' <<< "$CURSORPOSITION" | awk 'NR==1{print $1}')"
        YPOS="$(grep -o '[0-9]\+' <<< "$CURSORPOSITION" | awk 'NR==2{print $1}')"
        #echo "Mouse position is: ($XPOS, $YPOS)"


        # if XPOS is 1920 then on right 4k monitor
        if [ $XPOS = $RIGHTX ] 
        then 
            #echo "Starting jump function"
            # find % height then convert
            # Input is 0-2159, Output is 1080-2159
            NEWYPOS=$(echo "(($LEFTMONHEIGHT+$YPOS/$RIGHTMONHEIGHT*$LEFTMONHEIGHT))" | bc -l)
            #echo "YPOS with floats is: $NEWYPOS"
            NEWYPOS=$(echo "$NEWYPOS" | bc -l | xargs printf "%1.0f") 
            NEWXPOS=$(($RIGHTX-$BUFFER))
            # Move Mouse to other screen with a few pixel buffer on the screen edge
            #echo "Mouse jumped from right to left from: ($XPOS, $YPOS) to ($NEWXPOS, $NEWYPOS)"
            xdotool mousemove "$NEWXPOS" "$NEWYPOS"


        # if XPOS is 1919 then on left 1080 monitor
        elif [ $XPOS = $LEFTX ] 
        then 
            #echo "Starting jump function"
            # find % height then convert
            # Input is 0-2159, Output is 1080-2159
            NEWYPOS=$(echo "((($YPOS-$LEFTMONHEIGHT)/$LEFTMONHEIGHT*$RIGHTMONHEIGHT))" | bc -l)
            #echo "YPOS with floats is: $NEWYPOS"
            NEWYPOS=$(echo "$NEWYPOS" | bc -l | xargs printf "%1.0f") 
            NEWXPOS=$(($LEFTX+$BUFFER))
            # Move Mouse to other screen with a few pixel buffer on the screen edge
            #echo "Mouse jumped from left to right from: ($XPOS, $YPOS) to ($NEWXPOS, $NEWYPOS)"
            xdotool mousemove "$NEWXPOS" "$NEWYPOS"

        fi
        sleep 0.05
    done

5) Change these values in the script to match your monitor specs

LEFTX="1919"
LEFTMONHEIGHT="1080"

RIGHTX="1920"
RIGHTMONHEIGHT="2159"

My monitors were 1920x1080 on the left and 3840x2160 on the right.

If you need help finding your monitors values use this little bash script and move your mouse around:

#! /bin/bash
    while true; do # keep loop going to run all the time, do a slight pause for performance
            # grab cursor position, and extract x and y position
        CURSORPOSITION="$(xdotool getmouselocation)"
        XPOS="$(grep -o '[0-9]\+' <<< "$CURSORPOSITION" | awk 'NR==1{print $1}')"
        YPOS="$(grep -o '[0-9]\+' <<< "$CURSORPOSITION" | awk 'NR==2{print $1}')"
        echo "Mouse position is: ($XPOS, $YPOS)"

        sleep 0.1
done

6) Run it in terminal with ./mousejumper.sh

I don't want to have this running all the time so I'm not going to have the system launch it on startup. I have a little utility I wrote to toggle scripts like this on and off manually.

1

I have knocked up something that seems to work on my machine. I have not managed to test it with multiple monitors, but I think the settings will work for you. It requires you to have installed xdotool.

If it works, put it in your .rclocal, as described here, and it will run on startup.

#! /bin/bash
# Cursor relocation script by Cass May <[email protected]>

# definitions of "hot zone", which triggers cursor relocation
XZONE="1919"
YZONE="900"

#target for cursor relocation
XTARGET="1920"
YTARGET="899"

while true; do
    # grab cursor position, and extract x and y position
    CURSORPOSITION="$(xdotool getmouselocation)"
    XPOS="$(grep -o '[0-9]\+' <<< "$CURSORPOSITION" | awk 'NR==1{print $1}')"
    YPOS="$(grep -o '[0-9]\+' <<< "$CURSORPOSITION" | awk 'NR==2{print $1}')"

    # perform tests. If you wish to reconfigure this behaviour, and do
    # not know where to start, look up bash numerical comparison operators.
    if [[ "$XPOS" -eq "$XZONE" ]] && [[ "$YPOS" -ge "$YZONE" ]]
    then
        xdotool mousemove "$XTARGET" "$YTARGET"
    fi
    sleep 0.1
done
0

The reason this is happening is due to the fact that your left monitor has a height of 1080 pixels, while the right screen has a height of 900. As the two are aligned at the top, your mouse will move smoothly if you move right to left and vice versa.

But this won't work for the bottom edge, since the bottom of the left screen has 180 pixels more height than the right screen. According to this question, it is a known bug in the X window system

The solution shown may work for you if you're using NVIDIA hardware and drivers, but otherwise you may be stuck. I don't have this issue running Windows 7 with different resolution dual monitors.

1
  • 1
    You have a correct assessment of the problem, but I don't think that the solution to the linked question helps. That problem talks about creating mouse voids to prevent the mouse from moving into spaces that are offscreen. My problem is that I want to move from screen to screen seamlessly on the bottom. Or did I miss something in those links?
    – Sam King
    Jun 10, 2012 at 5:09
0

I wrote this Python script inspired by the answers from @cubash and @cassm. It should be less resource-intensive than using bash and starting a coupld of new xdotool processes at every loop iteration.

This script polls the mouse position at the given FPS and warps the pointer to the other screen when it "bumps" against the edge.

This allows to teleport the mouse to a given location if it gets stuck at a certain x- or y-coordinate. However, it won't allow a smooth interpolation of the coordinates, mapping a 0..1080p to 0..3840, because for half of the edge of the screen, the cursor will go from the area of one monitor to the other without stopping at the edge, and the script won't have the time to detect its position (since it works by polling).

You could extend it to record the previous cursor location, and "scale" the coordinates when the previous vs. current locations are on different monitors (e.g. the cursor was at 1900x,540y (around the middle of the right edge of the HD screen) at the previous loop iteration, and is at 1950x,540y (around a quarter of the left edge of the 4K screen) at the current loop iteration, therefore there was a HD→4K transition, so the y coordinate can be multiplied by two to place the cursor in the correct position).

Other alternative: put a large amount of empty space between the monitors, like so xrandr --output DP-1 --pos 0x1080 --auto --output eDP-1 --pos 2920x0, so that the script has enough time to grab the mouse when it is in the limbo between the screens:

                         +------+
                         |      |
+----+                   |  4K  |
| HD | [1000px of space] |      |
+----+                   +------+

Other alternative: "bump" nearly everywhere by making the screens touch by just one pixel xrandr --output DP-1 --pos 0x3839 --auto --output eDP-1 --pos 1920x0:

      +------+
      |      |
      |  4K  |
      |      |
+----++------+
| HD |
+----+

It seems that if the screens are touching by at least one pixel, the mouse will bump against the edges (which gives time for the script to poll for its location and teleport it), but if the screens aren't touching, then the mouse is allowed to go in the "limbo" that is not mapped to any screen. There's probably a way to declare "dead zones" for the mouse, and prevent it from entering those, which would allow the cursor to consistently bump against the edge and let it be detected by the script.

#!/usr/bin/env python3
from Xlib import display
from time import sleep
from os import system

# Optional: set the coordinates of the monitors here, I have a 1680x1050 monitor centered on top of a 1920x1080 monitor:
system('xrandr --output DP-1 --pos 120x0 --auto --output eDP-1 --pos 0x1050')

fps=50.0
delay=1.0/fps
d=display.Display()

while True:
  sleep(delay)
  data = d.screen().root.query_pointer()._data
  x = data["root_x"]
  y = data["root_y"]
  # Change conditions and coordinates here, I teleport the two segments on the top left and top right of my 1920x1080 screen to the bottom left and bottom right corners of the 1680x1050 screen:
  if y == 1050 and (x < 120 or x >= 1800):
    if x < 120:
      d.screen().root.warp_pointer(120,1049)
    else:
      d.screen().root.warp_pointer(1799,1049)

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .