2

I am working on an application that will draw a screen capture box on the screen around the user's mouse coordinates. I am trying to get this to work across multiple monitors. Using Cursor.Position I can get the global coordinates and determine what screen the user is on, but because the coordinates I receive are global and not relative to the monitor it is on, I am running into difficulties converting the global coordinates to screen-relative positioning.

I can use basic logic to get the relative coordinates when the monitors are all lined up vertically, but I am unsure how to approach the problem when the monitors are set up in a unique fashion (i.e. Not all monitors are the same size, one is to the right of two vertical monitors, etc.)

Here is what I have so far:

_screens = ScreenHelper.GetMonitorsInfo();
CursorPosition = Cursor.Position;
var currentDevice = Screen.FromPoint(CursorPosition);

if (!currentDevice.Primary)
{
    // If the current screen is not the primary monitor, we need to calculate the cursor's current position relative to the screen.
    //Find the position in the screens array that the cursor is located, then the position of the primary display.
    var cursorIndex = _screens.IndexOf(_screens.Find(x => x.DeviceName == currentDevice.DeviceName));
    var primaryIndex = _screens.IndexOf(_screens.Find(x => x.DeviceName == Screen.PrimaryScreen.DeviceName));

    //Cursor is to the right of primary screen.
    if (primaryIndex > cursorIndex)
    {
        for (int i = cursorIndex + 1; i <= primaryIndex; i++)
        {
            CursorPosition = new Point(CursorPosition.X - _screens[i].HorizontalResolution, CursorPosition.Y);
        }
    }
    //Cursor is to the left of primary screen.
    else
    {
        for (int i = cursorIndex - 1; i >= primaryIndex; i--)
        {
            CursorPosition = new Point(CursorPosition.X + _screens[i].HorizontalResolution, CursorPosition.Y);
        }
    }
}

public static List<DeviceInfo> GetMonitorsInfo()
{
    _result = new List<DeviceInfo>();
    EnumDisplayMonitors(IntPtr.Zero, IntPtr.Zero, MonitorEnum, IntPtr.Zero);
    return _result;
}

[DllImport("user32.dll")]
private static extern bool EnumDisplayMonitors(IntPtr hdc, IntPtr lprcClip, MonitorEnumDelegate lpfnEnum, IntPtr dwData);

I'm part way there, but I am unsure how to take into account horizontal or even diagonally relative monitor positioning. How can I effectively get the Mouse Coordinates relative to the screen the cursor is currently on?

Bobby Byrnes
  • 411
  • 5
  • 18
  • How is `_screens` instantiated? – KDecker Sep 29 '17 at 13:24
  • Possible duplicate of [Finding Monitor/Screen on which mouse pointer is present](https://stackoverflow.com/questions/26402955/finding-monitor-screen-on-which-mouse-pointer-is-present) – Sinatr Sep 29 '17 at 13:24
  • KDecker, I updated the OP to include that information. – Bobby Byrnes Sep 29 '17 at 13:28
  • Also, I am looking to get the coordinates relative to the screen the cursor is on, not just which screen is the current screen. – Bobby Byrnes Sep 29 '17 at 13:30
  • Hard to see how this info could ever be useful. But you simply need to subtract Screen.Bounds.X and Y. – Hans Passant Sep 29 '17 at 13:35
  • 1
    Essentially I am creating fullscreen transparent forms on each screen, then drawing a box around the mouse cursor on the form. The global coordinates I have do not help me because I need to draw on the form using relative form coordinates. Could you explain in a bit more detail how to subtract those values? – Bobby Byrnes Sep 29 '17 at 13:39

2 Answers2

1

It turns out I don't need to work so hard to hand-roll the conversion. The PointToClient method did the trick of converting global coordinates to form-relative coordinates. In my case, I just had a transparent form spawn on each screen, determined which form was the active form (the one containing the mouse cursor) by using the currentDevice variable above, then used the PointToClient method on the active form to convert the coordinates.

https://stackoverflow.com/a/19165028/3911065

Bobby Byrnes
  • 411
  • 5
  • 18
1

There's a very easy way to do this:

Helper functions:

// to get the currently active display's bounding rectangle
public Rectangle GetCurrentDisplaySize()
{
  return System.Windows.Forms.Screen.FromPoint(System.Windows.Forms.Cursor.Position).Bounds;
}

// to get the current cursor position
public System.Drawing.Point GetCurrentCursorPosition()
{
  return System.Windows.Forms.Cursor.Position;
}

Usage:

// always reload the display properties as the active display could change in multi-monitor setups
Rectangle displayDimensions = GetCurrentDisplaySize();
Point cursorPos = GetCurrentCursorPosition();
int cursorRelativeToDisplayX = cursorPos.X - displayDimensions.X
int cursorRelativeToDisplayY = cursorPos.Y - displayDimensions.Y

This will subtract the active display's position offset from the cursor position, resulting in the cursor position relative to the active display's origin (0,0).
And that's it, there's nothing more to it ¯\_(ツ)_/¯

  • I think this answer should be marked as the solution! Thank you Chaphasilor, I had the same "problem" and your answer really helped me! – Simos Sigma Dec 30 '22 at 13:59