问题
I'm trying to calculate cursor acceleration / velocity.
I read Find the velocity of the mouse in C# and decided to take Hans's suggestion of using GetMouseMovePointsEx
(pinvoke.net, MSDN).
I made a demo program to test it out (see full code below) but it has a big limitation.
It won't return points once the cursor leaves the window.
In fact, the function returns -1 (win32Exception 1171, "The point passed to GetMouseMovePoints is not in the buffer") if execution isn't limited to points within the MainWindow.
- I suspect this may be because I'm using
Mouse.GetPosition()
to provide themp_in
value.
Could this be solved by using GetCursorPos possibly?
Moving or resizing the window causes myBounds.Contains(currentPosition)
to be false when it should be true.
- This is probably a dumb mistake related to how I am setting the bounds, but it's not immediately obvious to me why that is. It might also be connected to
Mouse.GetPosition()
. Whatever the reason, my comparisons aren't matching up. Please help?
What it looks like
MainWindow.xaml
<Window x:Class="MouseVelocity.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="450" Width="1200" Background="Gray"
PreviewMouseUp="pMouseUp" PreviewMouseDown="pMouseDown">
<Grid Margin="1">
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition Height="75"/>
</Grid.RowDefinitions>
<TextBlock x:Name="txtOutput" Background="LightGray" Margin="10" FontSize="18" Padding="20"/>
<Label x:Name="lbl_Velo" FontSize="22" FontWeight="Bold" Background="Black" Foreground="Red"
HorizontalContentAlignment="Center" VerticalContentAlignment="Center" Grid.Row="1" Margin="10"/>
</Grid>
MainWindow.xaml.cs
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Threading;
using System.Windows.Interop;
using System.Runtime.InteropServices;
namespace MouseVelocity
{
public partial class MainWindow : Window
{
DispatcherTimer GetMousePointsNow;
MOUSEMOVEPOINT[] mp_out = new MOUSEMOVEPOINT[64];
MOUSEMOVEPOINT LastMMP;
Rect myBounds;
double[] XVelocity;
public const int GMMP_USE_DISPLAY_POINTS = 1;
public const int GMMP_USE_HIGH_RESOLUTION_POINTS = 2;
int nNumPointsDesired = 20;
uint mode = GMMP_USE_DISPLAY_POINTS;
string DataCollectionTime;
int nVirtualWidth = GetSystemMetrics(SystemMetric.SM_CXVIRTUALSCREEN);
int nVirtualHeight = GetSystemMetrics(SystemMetric.SM_CYVIRTUALSCREEN);
int nVirtualLeft = GetSystemMetrics(SystemMetric.SM_XVIRTUALSCREEN);
int nVirtualTop = GetSystemMetrics(SystemMetric.SM_YVIRTUALSCREEN);
double ScreenWidth = System.Windows.SystemParameters.PrimaryScreenWidth;
double ScreenHeight = System.Windows.SystemParameters.PrimaryScreenHeight;
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public struct MOUSEMOVEPOINT
{
public int x;
public int y;
public int time;
public IntPtr dwExtraInfo;
}
[DllImport("user32.dll")]
static extern int GetSystemMetrics(SystemMetric smIndex);
[DllImport("user32.dll", ExactSpelling = true, CharSet = CharSet.Auto, SetLastError = true)]
internal static extern int GetMouseMovePointsEx(
uint cbSize,
[In] ref MOUSEMOVEPOINT pointsIn,
[Out] MOUSEMOVEPOINT[] pointsBufferOut,
int nBufPoints,
uint resolution
);
public MainWindow()
{
InitializeComponent();
GetMousePointsNow = new DispatcherTimer(new TimeSpan(0, 0, 0, 0, 20), DispatcherPriority.Background,
GetMousePointsNow_Tick, Dispatcher.CurrentDispatcher); GetMousePointsNow.IsEnabled = false;
myBounds = new Rect();
}
private void pMouseDown(object sender, MouseButtonEventArgs e) {
GetMousePointsNow.Start(); }
private void pMouseUp(object sender, MouseButtonEventArgs e) {
GetMousePointsNow.Stop(); }
private void GetMousePointsNow_Tick(object sender, EventArgs e)
{
double width = System.Windows.SystemParameters.PrimaryScreenWidth;
double height = System.Windows.SystemParameters.PrimaryScreenHeight;
myBounds.Location = new Point(this.Top, this.Left);
myBounds.Size = new Size(this.ActualWidth, this.ActualHeight);
Point currentPosition = PointToScreen(Mouse.GetPosition(this));
if (!myBounds.Contains(currentPosition)) {
GetMousePointsNow.Stop(); return; }
var mp_in = new MOUSEMOVEPOINT();
mp_in.x = ((int)currentPosition.X) & 0x0000FFFF;
mp_in.y = ((int)currentPosition.Y) & 0x0000FFFF;
int cpt = GetMouseMovePointsEx((uint)(Marshal.SizeOf(mp_in)), ref mp_in, mp_out, nNumPointsDesired, mode);
if (cpt == -1) {
int win32Error = Marshal.GetLastWin32Error();
throw new System.ComponentModel.Win32Exception(win32Error);
}
for (int i = 0; i < cpt; i++) { // Fix for multi-display environment
switch (mode) {
case GMMP_USE_DISPLAY_POINTS:
if (mp_out[i].x > 32767)
mp_out[i].x -= 65536;
if (mp_out[i].y > 32767)
mp_out[i].y -= 65536;
break;
case GMMP_USE_HIGH_RESOLUTION_POINTS:
mp_out[i].x = ((mp_out[i].x * (nVirtualWidth - 1)) - (nVirtualLeft * 65536)) / nVirtualWidth;
mp_out[i].y = ((mp_out[i].y * (nVirtualHeight - 1)) - (nVirtualTop * 65536)) / nVirtualHeight;
break;
}
}
DisplayMousePoints();
DisplayVelocity();
}
private void DisplayMousePoints()
{
string Result = "";
XVelocity = new double[20];
for (int i = 0; i < 20; i++)
{
MOUSEMOVEPOINT ThisMMP = mp_out[i];
int DeltaTime = LastMMP.time - ThisMMP.time;
if (ThisMMP.time == LastMMP.time) { } // Do nothing if same timestamp
else {
XVelocity[i] = (LastMMP.x - ThisMMP.x) / (double)DeltaTime; // V = x / t
if (DeltaTime > 0) // Don't include first point in the calculation
{
Result = Result +=
"X: " + mp_out[i].x + ", " +
"Timestamp: " + mp_out[i].time.ToString() + ", " +
"Change: (" + DeltaTime.ToString() + "), " +
"Point Velo: " + (XVelocity[i] * 100).ToString("0.000") + Environment.NewLine;
}
}
DataCollectionTime = DateTime.Now.Second.ToString("00") + DateTime.Now.Millisecond.ToString("000");
txtOutput.Text = "Time Collected: " + DataCollectionTime + Environment.NewLine + Result;
LastMMP = ThisMMP;
}
}
private void DisplayVelocity()
{
// Calculate a moving average of velocity values
double aggregate = 0;
double weight;
int item = 1;
int count = 1;
foreach (var d in XVelocity)
{
weight = (double)item / (double)count;
aggregate += (double)d * weight;
count++;
}
double result = (aggregate / count) * 100;
lbl_Velo.Content = result.ToString("0.000");
}
public enum SystemMetric
{
SM_XVIRTUALSCREEN = 76, // 0x4C
SM_YVIRTUALSCREEN = 77, // 0x4D
SM_CXVIRTUALSCREEN = 78, // 0x4E
SM_CYVIRTUALSCREEN = 79, // 0x4F
}
}
}
来源:https://stackoverflow.com/questions/25170813/getmousemovepointsex-bounds-mousemovepoint-in-mp-in-problems