internal static class Functions
{
+ #region GetLastError
+
+ [DllImport("Kernel32.dll")]
+ internal static extern uint GetLastError();
+
+ #endregion
+
#region Window functions
#region SetWindowPos
#endregion
+ #region GetMessageTime
+
+ /// <summary>
+ /// Retrieves the message time for the last message retrieved by the
+ /// GetMessage function. The time is a long integer that specifies the
+ /// elapsed time, in milliseconds, from the time the system was started
+ /// to the time the message was created (that is, placed in the thread's
+ /// message queue).
+ /// </summary>
+ /// <returns>The return value specifies the message time.</returns>
+ [DllImport("User32.dll")]
+ internal static extern int GetMessageTime();
+
+ #endregion
+
#region SendMessage
[DllImport("user32.dll", CharSet = CharSet.Auto)]
[DllImport("user32.dll")]
public static extern bool SetCursorPos(int X, int Y);
+ /// <summary>
+ /// Retrieves a history of up to 64 previous coordinates of the mouse or pen.
+ /// </summary>
+ /// <param name="cbSize">The size, in bytes, of the MouseMovePoint structure.</param>
+ /// <param name="pointsIn">
+ /// A pointer to a MOUSEMOVEPOINT structure containing valid mouse
+ /// coordinates (in screen coordinates). It may also contain a time
+ /// stamp.
+ /// </param>
+ /// <param name="pointsBufferOut">
+ /// A pointer to a buffer that will receive the points. It should be at
+ /// least cbSize * nBufPoints in size.
+ /// </param>
+ /// <param name="nBufPoints">The number of points to be retrieved.</param>
+ /// <param name="resolution">
+ /// The resolution desired. This parameter can GMMP_USE_DISPLAY_POINTS
+ /// or GMMP_USE_HIGH_RESOLUTION_POINTS.
+ /// </param>
+ /// <returns></returns>
+ [DllImport("user32", ExactSpelling = true, CharSet = CharSet.Auto, SetLastError = true)]
+ unsafe internal static extern int GetMouseMovePointsEx(
+ uint cbSize, MouseMovePoint* pointsIn,
+ MouseMovePoint* pointsBufferOut, int nBufPoints, uint resolution);
+
#region Async input
#region GetCursorPos
internal static readonly IntPtr MESSAGE_ONLY = new IntPtr(-3);
internal static readonly IntPtr HKEY_LOCAL_MACHINE = new IntPtr(unchecked((int)0x80000002));
+
+ /// <summary>
+ /// Retrieves the points using the display resolution.
+ /// </summary>
+ internal const int GMMP_USE_DISPLAY_POINTS = 1;
+
+ /// <summary>
+ /// Retrieves high resolution points. Points can range from zero to
+ /// 65,535 (0xFFFF) in both x and y coordinates. This is the resolution
+ /// provided by absolute coordinate pointing devices such as drawing
+ /// tablets.
+ /// </summary>
+ internal const int GMMP_USE_HIGH_RESOLUTION_POINTS = 2;
}
#endregion
#endregion
+ #region MouseMovePoint
+
+ /// <summary>
+ /// Contains information about the mouse's location in screen coordinates.
+ /// </summary>
+ [StructLayout(LayoutKind.Sequential)]
+ public struct MouseMovePoint
+ {
+ /// <summary>
+ /// The x-coordinate of the mouse.
+ /// </summary>
+ public int X;
+ /// <summary>
+ /// The y-coordinate of the mouse.
+ /// </summary>
+ public int Y;
+ /// <summary>
+ /// The time stamp of the mouse coordinate.
+ /// </summary>
+ public int Time;
+ /// <summary>
+ /// Additional information associated with this coordinate.
+ /// </summary>
+ public IntPtr ExtraInfo;
+
+ public static readonly int SizeInBytes = Marshal.SizeOf(default(MouseMovePoint));
+ }
+
+ #endregion
+
#endregion
#region --- Enums ---
bool borderless_maximized_window_state = false; // Hack to get maximized mode with hidden border (not normally possible).
bool focused;
bool mouse_outside_window = true;
+ int mouse_last_timestamp = 0;
bool invisible_since_creation; // Set by WindowsMessage.CREATE and consumed by Visible = true (calls BringWindowToFront).
int suppress_resize; // Used in WindowBorder and WindowState in order to avoid rapid, consecutive resize events.
bool is_in_modal_loop; // set to true whenever we enter the modal resize/move event loop
void HandleMouseMove(IntPtr handle, WindowMessage message, IntPtr wParam, IntPtr lParam)
{
- Point point = new Point(
- (short)((uint)lParam.ToInt32() & 0x0000FFFF),
- (short)(((uint)lParam.ToInt32() & 0xFFFF0000) >> 16));
- mouse.Position = point;
+ unsafe
+ {
+ Point point = new Point(
+ (short)((uint)lParam.ToInt32() & 0x0000FFFF),
+ (short)(((uint)lParam.ToInt32() & 0xFFFF0000) >> 16));
+
+ // GetMouseMovePointsEx works with screen coordinates
+ Point screenPoint = point;
+ Functions.ClientToScreen(handle, ref screenPoint);
+ int timestamp = Functions.GetMessageTime();
+
+ // & 0xFFFF to handle multiple monitors http://support.microsoft.com/kb/269743
+ MouseMovePoint movePoint = new MouseMovePoint()
+ {
+ X = screenPoint.X & 0xFFFF,
+ Y = screenPoint.Y & 0xFFFF,
+ Time = timestamp,
+ };
+
+ // Max points GetMouseMovePointsEx can return is 64.
+ int numPoints = 64;
+
+ MouseMovePoint* movePoints = stackalloc MouseMovePoint[numPoints];
+
+ // GetMouseMovePointsEx fills in movePoints so that the most
+ // recent events are at low indices in the array.
+ int points = Functions.GetMouseMovePointsEx(
+ (uint)MouseMovePoint.SizeInBytes,
+ &movePoint, movePoints, numPoints,
+ Constants.GMMP_USE_DISPLAY_POINTS);
+
+ uint lastError = Functions.GetLastError();
+
+ // No points returned or search point not found
+ if (points == 0 || (points == -1 && lastError == 1171))
+ {
+ // Just use the mouse move position
+ mouse.Position = point;
+ }
+ else if (points == -1)
+ {
+ throw new System.ComponentModel.Win32Exception((int)lastError);
+ }
+ else
+ {
+ // Exclude the current position.
+ Point currentScreenPosition = new Point(mouse.X, mouse.Y);
+ Functions.ClientToScreen(handle, ref currentScreenPosition);
+
+ // Find the first move point we've already seen.
+ int i = 0;
+ for (i = 0; i < points; ++i)
+ {
+ if (movePoints[i].Time < mouse_last_timestamp)
+ break;
+ if (movePoints[i].Time == mouse_last_timestamp &&
+ movePoints[i].X == currentScreenPosition.X &&
+ movePoints[i].Y == currentScreenPosition.Y)
+ break;
+ }
+
+ // Now move the mouse to each point before the one just found.
+ while (--i >= 0)
+ {
+ Point position = new Point(movePoints[i].X, movePoints[i].Y);
+ // Handle multiple monitors http://support.microsoft.com/kb/269743
+ if (position.X > 32767)
+ {
+ position.X -= 65536;
+ }
+ if (position.Y > 32767)
+ {
+ position.Y -= 65536;
+ }
+ Functions.ScreenToClient(handle, ref position);
+ mouse.Position = position;
+ }
+ }
+ mouse_last_timestamp = timestamp;
+ }
if (mouse_outside_window)
{