I want to send mouse input programmatically to anyone of my connected displays.
I know I can use the SendInput function to do this, but my current approach doesn't work on a multi-display setup.
I am using the following approach:
However, with my current code, the mouse input gets wrongly positioned. I am confused on how to correctly calculate the absolute coordinates.
At the moment, I do retrieve the absolute coordinates using the formula discussed on another question. To get the width/height of the virtual screen, I use GetSystemMetrics with SM_CXVIRTUALSCREEN/SM_CYVIRTUALSCREEN parameter.
// ________________________________________________
//
// GetAbsoluteCoordinate
//
// PURPOSE:
// Convert pixel coordinate to absolute coordinate (0-65535).
//
// RETURN VALUE:
// Absolute Coordinate
// ________________________________________________
//
UINT16 GetAbsoluteCoordinate(INT PixelCoordinate, INT ScreenResolution)
{
UINT16 AbsoluteCoordinate = ( (65536 * PixelCoordinate) / ScreenResolution ) + 1;
return AbsoluteCoordinate;
}
// ________________________________________________
//
// GetAbsoluteCoordinates
//
// PURPOSE:
// Retrieve virtual screen absolute coordinates from pixel coordinates.
//
// PARAMETERS:
// x, y coordinates passed by reference. These will be changed by the function and used as return values.
//
// RETURN VALUE:
// None (see parameters)
// ________________________________________________
//
void GetAbsoluteCoordinates(INT32 &X, INT32 &Y)
{
// Get multi-screen coordinates
MONITORINFO MonitorInfo = { 0 };
MonitorInfo.cbSize = sizeof(MonitorInfo);
if (GetMonitorInfoW(hMonitor, &MonitorInfo))
{
// 1) Get pixel coordinates of topleft pixel of target screen, relative to the virtual desktop ( coordinates should be 0,0 on Main screen);
// 2) Get pixel coordinates of mouse cursor, relative to the target screen;
// 3) Sum topleft margin pixel coordinates with mouse cursor coordinates;
X = MonitorInfo.rcMonitor.left + X;
Y = MonitorInfo.rcMonitor.top + Y;
// 4) Transform the resulting pixel coordinates into absolute coordinates.
X = GetAbsoluteCoordinate(X, GetSystemMetrics(SM_CXVIRTUALSCREEN));
Y = GetAbsoluteCoordinate(Y, GetSystemMetrics(SM_CYVIRTUALSCREEN));
}
}
// ________________________________________________
//
// SendMouseInput
//
// PURPOSE:
// Send mouse input, supporting any of the connected displays
//
// PARAMETERS:
// X, Y are pixel coordinates, relative to the current screen.
// ________________________________________________
//
void SendMouseInput(INT X, INT Y)
{
INPUT Input;
GetAbsoluteCoordinates(X, Y);
memset(&Input, 0, sizeof(INPUT));
Input.type = INPUT_MOUSE;
Input.mi.dx = X;
Input.mi.dy = Y;
Input.mi.dwFlags = MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_VIRTUALDESK | MOUSEEVENTF_MOVE | MOUSEEVENTF_LEFTDOWN;
SendInput(1, &Input, sizeof(Input));
}
- Get the pixel coordinates relative to the whole virtual screen: to do this, we sum the coordinates of the current screen margin (topleft pixel) with the coordinates of the point to click;
It looks like you are using the coordinates(X,Y
) relative to the monitor. How did you get the coordinates?
If so, then the sample is basically no problem. If your mouse coordinates are obtained by something similar to GetCursorPos
, then you do not need to calculate the coordinates relative to the virtual screen (it is).
The sample almost work for me, I've changed some code as below:
#include <windows.h>
#include <iostream>
using namespace std;
// ________________________________________________
//
// GetAbsoluteCoordinate
//
// PURPOSE:
// Convert pixel coordinate to absolute coordinate (0-65535).
//
// RETURN VALUE:
// Absolute Coordinate
// ________________________________________________
//
INT GetAbsoluteCoordinate(INT PixelCoordinate, INT ScreenResolution)
{
INT AbsoluteCoordinate = MulDiv(PixelCoordinate, 65535, ScreenResolution);
return AbsoluteCoordinate;
}
void GetAbsoluteCoordinates(HMONITOR hMonitor, INT32& X, INT32& Y)
{
// Get multi-screen coordinates
MONITORINFO MonitorInfo = { 0 };
MonitorInfo.cbSize = sizeof(MonitorInfo);
if (GetMonitorInfoW(hMonitor, &MonitorInfo))
{
// 1) Get pixel coordinates of topleft pixel of target screen, relative to the virtual desktop ( coordinates should be 0,0 on Main screen);
// 2) Get pixel coordinates of mouse cursor, relative to the target screen;
// 3) Sum topleft margin pixel coordinates with mouse cursor coordinates;
X = MonitorInfo.rcMonitor.left + X;
Y = MonitorInfo.rcMonitor.top + Y;
// 4) Transform the resulting pixel coordinates into absolute coordinates.
X = GetAbsoluteCoordinate(X, GetSystemMetrics(SM_CXVIRTUALSCREEN));
Y = GetAbsoluteCoordinate(Y, GetSystemMetrics(SM_CYVIRTUALSCREEN));
}
}
void SendMouseInput(HMONITOR hMonitor, INT X, INT Y)
{
INPUT Input[2];
GetAbsoluteCoordinates(hMonitor, X, Y);
memset(Input, 0, sizeof(INPUT));
Input[0].type = Input[1].type = INPUT_MOUSE;
Input[0].mi.dx = Input[1].mi.dx = X;
Input[0].mi.dy = Input[1].mi.dy = Y;
Input[0].mi.dwFlags = MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_LEFTDOWN | MOUSEEVENTF_MOVE | MOUSEEVENTF_VIRTUALDESK;
Input[1].mi.dwFlags = MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_LEFTUP | MOUSEEVENTF_MOVE | MOUSEEVENTF_VIRTUALDESK;
SendInput(2, Input, sizeof(INPUT));
}
BOOL CALLBACK Monitorenumproc(
HMONITOR Arg1,
HDC Arg2,
LPRECT Arg3,
LPARAM Arg4)
{
SendMouseInput(Arg1, 725, 85);
return TRUE;
}
int main(void)
{
EnumDisplayMonitors(NULL,NULL, Monitorenumproc,0);
return 0;
}
Result:
I have 2 monitors with the same display resolution(1920 x 1080) to test like:
The sample will click at the same place on each monitor.
Further reading: The Virtual Screen