C#以高于监视器分辨率的分辨率跟踪鼠标移动
本文关键字:分辨率 跟踪 鼠标 移动 高于 监视器 | 更新日期: 2023-09-27 18:19:58
问题
我使用C#应用程序来记录键盘和鼠标的移动,处理这些移动,并将串行数据发送到一个微控制器,该控制器解释这些数据并移动一组伺服系统。在过去,我创建了一个方框或图像,它是我的伺服系统能够达到的分辨率(步数),将鼠标剪切到那个方框或图像上,并处理光标在那个方框中的位置,并将数据发送到我的伺服服务器,以移动到那个位置。
这样做很好,直到我需要移动比监视器分辨率更大的步数。
因此,我的问题是,我可以选择哪些选项来跟踪X轴和Y轴上高达10000步/分辨率的鼠标移动?
可能的解决方案路线
跳出框框思考,我想我可以将鼠标隐藏在屏幕上并居中,记录鼠标在mousemoved事件中移动的量,处理这些数据,然后将鼠标重新居中放在屏幕上,使我在每个轴上都可以无限移动。
下面附上我的PIC18F2420代码。目前,它是通过串行通信从我的C#应用程序提供x和y位置。当数据被接收并尽快处理时,数据被存储在环形缓冲器中。
图片编码
#include <p18f2420.h>
#include <cType.h>
#include <usart.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <timers.h>
#include <delays.h>
/**********************************************************************************************/
//DEFINES
#define switch_0 PORTCbits.RC4
#define switch_1 PORTCbits.RC5
#define bufferSize 48
/**********************************************************************************************/
/**********************************************************************************************/
//Function prototypes
void high_isr(void);
void int2ASCII(unsigned int output);
void UART_putChar(unsigned char value);
char readBuffer();
char emptyBuffer();
char peekBuffer();
void limitServo0(); //limit movement via predetermined min/max
void limitServo1();
/**********************************************************************************************/
unsigned char hertz = 75; //value to generate 5-=60 hertz wave default value 75
unsigned int timer0, servo0Min, servo0Max;
unsigned int timer1, servo1Min, servo1Max;
unsigned char servo0Rate = 10;
unsigned char ByteOut;
char array[bufferSize]; //input rs-232 buffer
char valueArray[bufferSize];
char dataArray[bufferSize];
char tempArray[bufferSize];
unsigned char tempIndex;
unsigned char head = 0;
unsigned char tail = 0;
//variables used to disect the comma delimited string
char CVdata; //do we have a command and value?
char CVvalue; //bool value like above
//BOOLEAN IF values
/**********************************************************************************************/
//Interrupt Service Routine
#pragma code high_vector=0x08
void interrupt_at_high_vector (void)
{
_asm GOTO high_isr _endasm
}
#pragma code /* return to the default code section */
#pragma interrupt high_isr
void high_isr (void)
{
if(PIR1bits.TMR2IF == 1)
{
//T0CONbits.TMR0ON = 0;
//T1CONbits.TMR1ON = 0;
INTCONbits.TMR0IF = 0; //Turn off Int Flag
PIR1bits.TMR1IF = 0;
PIR1bits.TMR2IF = 0; //Turn off Int Flag
LATCbits.LATC3 = 1; //Turn on data line
TMR0H = timer0/256; //Extract HIGH byte always do Hbyte first
TMR0L = timer0; //Extract LOW byte
}
if(PIR1bits.TMR1IF == 1)
{
PIR1bits.TMR1IF = 0;
//T1CONbits.TMR1ON = 0;
//PIR1bits.TMR2IF = 0; //Turn off Int Flag
INTCONbits.TMR0IF = 0; //Turn off Int Flag
LATCbits.LATC2 = 0;
PR2 = hertz; //Generate 50-60hertz pulse
}
if(INTCONbits.TMR0IF == 1)
{
LATCbits.LATC2 = 1;
//PIR1bits.TMR1IF = 0;
//PIR1bits.TMR2IF = 0; //Turn off Int Flag
//T0CONbits.TMR0ON = 0;
//T1CONbits.TMR1ON = 1;
INTCONbits.TMR0IF = 0; //Turn off Int Flag
LATCbits.LATC3 = 0;
TMR1H = timer1/256;
TMR1L = timer1;
}
if(PIR1bits.RCIF == 1)
{
PIR1bits.RCIF = 0;
array[tail] = RCREG;
//array[tail] = ReadUSART();
tail++;
if(tail == bufferSize)
{
tail = 0;
}
/* Clear the interrupt flag */
}
}
/**********************************************************************************************/
void main(void)
{
/**********************************************************************************************/
//Initialize
memset(array, ''0' , bufferSize);
memset(tempArray, ''0' , bufferSize);
memset(dataArray, ''0' , bufferSize);
memset(valueArray, ''0' , bufferSize);
TRISC = 0b10110000;//RC4 and RC5 inputs for switches
servo0Max = 65000; //Max value allowed PAN 65000
servo0Min = 62000; //Min value allowed 63500
servo1Max = 65000; //Tilt 64138
servo1Min = 62000; //TILT 63864
timer0 = 64250; //Initial position
timer1 = 64200;
CVdata = 0;
CVvalue = 0;
tempIndex = 0;
LATCbits.LATC0 = 0;
/**********************************************************************************************/
//USART
OpenUSART(USART_TX_INT_OFF &
USART_RX_INT_ON &
USART_CONT_RX &
USART_ASYNCH_MODE &
USART_EIGHT_BIT &
USART_BRGH_HIGH
, 16);//change back to 16 for 57.6 103 for 9.6
RCSTAbits.ADDEN = 0;//Testing this out might not help with overflow
TXSTAbits.SYNC = 0;
INTCONbits.GIE = 1;
INTCONbits.PEIE = 1;
TXSTAbits.BRGH = 1;
BAUDCONbits.BRG16 = 1;
/**********************************************************************************************/
//Initialize Timer0
OpenTimer0(TIMER_INT_ON &
T0_SOURCE_INT &
T0_16BIT);
T0CONbits.PSA = 1;
INTCONbits.TMR0IF = 0;
/**********************************************************************************************/
/**********************************************************************************************/
//Initialize Timer1
OpenTimer1(TIMER_INT_ON &
T1_16BIT_RW &
T1_SOURCE_INT &
T1_PS_1_1);
T1CONbits.T1CKPS1 = 0; // bits 5-4 Prescaler Rate Select bits
T1CONbits.T1CKPS0 = 0; // bit 4
T1CONbits.T1OSCEN = 1; // bit 3 Timer1 Oscillator Enable Control bit 1 = on
T1CONbits.T1SYNC = 1; // bit 2 Timer1 External Clock Input Synchronization Control bit...1 = Do not synchronize external clock input
T1CONbits.TMR1CS = 0; // bit 1 Timer1 Clock Source Select bit...0 = Internal clock (FOSC/4)
T1CONbits.TMR1ON = 1; // bit 0 enables timer
/**********************************************************************************************/
/**********************************************************************************************/
//Initialize Timer2
OpenTimer2( TIMER_INT_ON &
T2_PS_1_16 &
T2_POST_1_16);
PR2 = hertz;
PIE1bits.TMR2IE = 1;
IPR1bits.TMR2IP = 1;
INTCONbits.GIEH = 1; //enable global interrupts
INTCONbits.GIEL = 1;
/**********************************************************************************************/
while(1)
{
while(emptyBuffer());
if(CVdata == 0 && CVvalue == 1)
{
//ERROR THIS SHOULDN't HAPPEN! FLUSH BUFFER
CVdata = 0;
CVvalue = 0;
}
if(CVdata == 0 && CVvalue == 0)
{
if(peekBuffer() != ',')
{
tempArray[tempIndex] = readBuffer();
tempIndex++;
}
else
{
readBuffer();//if comma sent first read it and throw away
if(tempIndex > 0) //comma read and data in buffer
{
memcpy(dataArray, tempArray, tempIndex);
tempIndex = 0;
CVdata = 1;
memset(tempArray, 'a' , bufferSize);
}
}
}
if(CVdata ==1 && CVvalue == 0)
{
if(peekBuffer() != ',')
{
if(isdigit(peekBuffer()))
{
tempArray[tempIndex] = readBuffer();
tempIndex++;
}
else
readBuffer();
}
else
{
//readBuffer();
if(tempIndex > 0)
{
memcpy(valueArray, tempArray, tempIndex);
tempIndex = 0;
CVvalue = 1;
memset(tempArray, 'a', bufferSize);
}
}
}
if(CVdata == 1 && CVvalue == 1)
{
switch(dataArray[0])
{
case 'x':
case 'X':
{
//timer0 = current = atof(valueArray);//ISSUE HERE first char null
timer0 = (unsigned int)atoi(valueArray);
break;
}
case 'y':
case 'Y':
{
timer1 = (unsigned int)atoi(valueArray);
break;
}
}
CVdata = 0;
CVvalue = 0;
memset(dataArray, 'a' , bufferSize);
memset(valueArray, 'a' , bufferSize);
}
limitServo0();
limitServo1();
}
}
/**********************************************************************************************/
//Functions
void int2ASCII(unsigned int output)
{
unsigned char digit = 0;
while (output >= 10000) { output -= 10000; digit++; } UART_putChar(digit + 0x30); digit = 0;
while (output >= 1000) { output -= 1000; digit++; } UART_putChar(digit + 0x30); digit = 0;
while (output >= 100) { output -= 100; digit++; } UART_putChar(digit + 0x30); digit = 0;
while (output >= 10) { output -= 10; digit++; } UART_putChar(digit + 0x30); digit = 0;
while (output >= 1) { output -= 1; digit++; } UART_putChar(digit + 0x30);
}
void UART_putChar(unsigned char value)
{
while(PIR1bits.TXIF == 0);
TXREG = value;
}
char readBuffer()
{
if(tail != head)
{
ByteOut = array[head];
head++;
if(head == bufferSize)
{
head = 0;
}
return ByteOut;
}
//LATCbits.LATC0 = 1;
}
char peekBuffer()
{
return array[head];
}
char emptyBuffer()
{
if(tail == head)
return 1;
else
return 0;
}
void limitServo0()
{
if(timer0 > servo0Max)
{
timer0 = servo0Max;
}
if(timer0 < servo0Min)
{
timer0 = servo0Min;
}
}
void limitServo1()
{
if(timer1 > servo1Max)
{
timer1 = servo1Max;
}
if(timer1 < servo1Min)
{
timer1 = servo1Min;
}
}
我以前通过位图跟踪的示例可以在我的youtube频道上查看,网址为:http://www.youtube.com/watch?v=rBhkV3dnyiU&list=UULGlw5rGZfETaiPs49JBEuA&索引=41
更新
经过一些研究,我似乎可以编写一个XNA应用程序,捕捉鼠标移动,并输出串行通信。我真的很喜欢windows窗体解决方案,但我确实有XNA的经验,所以我想我会一直转换我的应用程序,直到出现另一个解决方案。
我最初设计的解决方案成功了。在trackingEnabled上,我将鼠标居中,检查它每100毫秒移动了多少,然后重新居中并将数据发送到控制器。
using System;
using System.Timers;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using PIC18F_Servo_Control_V2;
namespace FSI_Grid1
{
public partial class Form1 : Form
{
TabControl LeftControlTab;
System.Timers.Timer myTimer;
public delegate void UpdateStatusBarDelegate();
TabPage myTabPage;
Grid myGrid;
serialConnection myConnection;
int timerDelay = 100;
int initialX, initialY, currentX, currentY, minX, minY, maxX, maxY;
int MouseCurrentX, MouseCurrentY, MouseMovedX, MouseMovedY;//tracking mousemovement
int offsetX, offsetY;//how much each arrow click moves the servos
bool trackingActive;//are we in tracking mode?
bool MouseMovedFlag;
int YOffsetValue;//used to offset dynamically generated buttons in tab groups
int XCenter, YCenter;
enum States { Startup, MouseTracking, KeyboardTracking, Script, Idle };//state engine
States CurrentState;
public Form1()
{
currentX = initialX = 63503;
currentY = initialY = 64012;
minX = 62000;
maxX = 65000;
minY = 62000;
maxY = 65000;
offsetX = 10;
offsetY = 10;
trackingActive = false;
YOffsetValue = 0;
CurrentState = States.Startup;
MouseMovedFlag = false;
myTimer = new System.Timers.Timer(timerDelay);
myTimer.Elapsed += new ElapsedEventHandler(OnTimedEvent);
myTimer.Elapsed += new ElapsedEventHandler(TrackMouse);
myTimer.Enabled = false;
InitializeComponent();
InitializeGrid();
InitializeLeftControlTab();
InitializeSerial();
//Initialize StatusBar
RadioButton button = (RadioButton)this.Controls.Find("SelectKeyboardRadioButton", true)[0];
button.Checked = true;
activeStatus.Text = "Keyboard Tracking DEACTIVATED";
activeStatus.BackColor = Color.Red;
ConnectionStatus.Text = "Disconnected!";
xOffsetStatus.Text = "X offset value " + offsetX.ToString();
yOffsetStatus.Text = "Y offset value " + offsetY.ToString();
//this.MouseMove += new MouseEventHandler(Form_MouseMove);
XCenter = this.Location.X + this.Width / 2;
YCenter = this.Location.Y + this.Height / 2;
}
~Form1()
{
if (myConnection.connected)
myConnection.disconnect();
}
private void widthTextBox_KeyPress(object sender, KeyPressEventArgs e)
{
e.Handled = !char.IsDigit(e.KeyChar) && !char.IsControl(e.KeyChar);
}
private void widthTextBox_KeyUp(object sender, KeyEventArgs e)
{
TextBox text = (TextBox)this.Controls.Find("widthTextBox", true)[0];
xOffsetStatus.Text = text.Text;
offsetX = Convert.ToInt16(text.Text);
}
private void heightTextBox_KeyPress(object sender, KeyPressEventArgs e)
{
e.Handled = !char.IsDigit(e.KeyChar) && !char.IsControl(e.KeyChar);
}
private void heightTextBox_KeyUp(object sender, KeyEventArgs e)
{
TextBox text = (TextBox)this.Controls.Find("heightTextBox", true)[0];
yOffsetStatus.Text = text.Text;
offsetY = Convert.ToInt16(text.Text);
}
private void LeftControlTab_DrawItem(object sender, DrawItemEventArgs e)
{
Graphics g = e.Graphics;
Brush _textBrush;
// Get the item from the collection.
TabPage _tabPage = LeftControlTab.TabPages[e.Index];
// Get the real bounds for the tab rectangle.
Rectangle _tabBounds = LeftControlTab.GetTabRect(e.Index);
if (e.State == DrawItemState.Selected)
{
// Draw a different background color, and don't paint a focus rectangle.
_textBrush = new SolidBrush(Color.Red);
g.FillRectangle(Brushes.White, e.Bounds);
}
else
{
_textBrush = new System.Drawing.SolidBrush(e.ForeColor);
g.FillRectangle(Brushes.LightGray, e.Bounds);
//e.DrawBackground();
}
// Use our own font.
Font _tabFont = new Font("Arial", (float)10.0, FontStyle.Bold, GraphicsUnit.Pixel);
// Draw string. Center the text.
StringFormat _stringFlags = new StringFormat();
_stringFlags.Alignment = StringAlignment.Center;
_stringFlags.LineAlignment = StringAlignment.Center;
g.DrawString(_tabPage.Text, _tabFont, _textBrush, _tabBounds, new StringFormat(_stringFlags));
}
private void InitializeLeftControlTab()
{
LeftControlTab = new TabControl();
LeftControlTab.Location = new Point(10, 30);
LeftControlTab.Size = new Size(300, 500);
LeftControlTab.Alignment = TabAlignment.Left;
LeftControlTab.SizeMode = TabSizeMode.Fixed;
LeftControlTab.ItemSize = new Size(30, 90);
LeftControlTab.DrawMode = TabDrawMode.OwnerDrawFixed;
/*EVENT HANDLER*/
LeftControlTab.DrawItem += new DrawItemEventHandler(LeftControlTab_DrawItem);
/*TABS*/
int offset = 100; //how far to the right the edit boxes are
myTabPage = new TabPage();
myTabPage.Text = "Appearance";
LeftControlTab.Controls.Add(myTabPage);
myTabPage = new TabPage();
myTabPage.Text = "Settings";
/*LABEL*/
Label OffsetLabel = new Label();
OffsetLabel.Text = "Step resolution";
OffsetLabel.Location = new Point(0,YOffset());
myTabPage.Controls.Add(OffsetLabel);
/*WIDTH LABEL*/
Label widthLabel = new Label();
widthLabel.Text = "Width";
widthLabel.Location = new Point(0, YOffset());
myTabPage.Controls.Add(widthLabel);
/*WIDTH TEXTBOX*/
TextBox widthTextBox = new TextBox();
widthTextBox.Name = "widthTextBox";
widthTextBox.Text = myGrid.Width.ToString();
widthTextBox.Location = new Point(widthLabel.Location.X + offset, widthLabel.Location.Y);
myTabPage.Controls.Add(widthTextBox);
widthTextBox.KeyPress += new KeyPressEventHandler(widthTextBox_KeyPress); //EVENT HANDLER
widthTextBox.KeyUp += new KeyEventHandler(widthTextBox_KeyUp); //EVENT HANDLER
/*HEIGHT LABEL*/
Label heightLabel = new Label();
heightLabel.Text = "Height";
heightLabel.Location = new Point(0, YOffset());
myTabPage.Controls.Add(heightLabel);
/*HEIGHT TEXTBOX*/
TextBox heightTextBox = new TextBox();
heightTextBox.Name = "heightTextBox";
heightTextBox.Text = myGrid.Height.ToString();
heightTextBox.Location = new Point(heightLabel.Location.X + offset, heightLabel.Location.Y);
myTabPage.Controls.Add(heightTextBox);
/*RADIOBUTTON LABEL*/
GroupBox RadioLabel = new GroupBox();
RadioLabel.Text = "Tracking Style";
RadioLabel.Location = new Point(0, YOffset());
myTabPage.Controls.Add(RadioLabel);
/*RADIO BUTTONS*/
RadioButton SelectMouse = new RadioButton();
SelectMouse.Location = new Point(10, 20);
SelectMouse.Text = "Mouse";
SelectMouse.Name = "SelectMouseRadioButton";
SelectMouse.CheckedChanged += new EventHandler(RadioButtons_CheckedChanged);
RadioLabel.Controls.Add(SelectMouse);
RadioButton SelectKeyboard = new RadioButton();
SelectKeyboard.Location = new Point(10, 42);
SelectKeyboard.Text = "Keyboard";
SelectKeyboard.Name = "SelectKeyboardRadioButton";
SelectKeyboard.CheckedChanged += new EventHandler(RadioButtons_CheckedChanged);
RadioLabel.Controls.Add(SelectKeyboard);
heightTextBox.KeyPress += new KeyPressEventHandler(heightTextBox_KeyPress); //EVENT HANDLER
heightTextBox.KeyUp += new KeyEventHandler(heightTextBox_KeyUp); //EVENT HANDLER
//EVENT HANDLER
LeftControlTab.Controls.Add(myTabPage);
Controls.Add(LeftControlTab);
}
private void InitializeGrid()
{
myGrid = new Grid(offsetX, offsetY);
}
private void connectToolStripMenuItem_Click(object sender, EventArgs e)
{
serialConnectionDialogBox serialBox = new serialConnectionDialogBox();
Point temp = this.Location;
temp.X += 30;
temp.Y += 70;
serialBox.Location = temp;
DialogResult results = serialBox.ShowDialog();
if (results == DialogResult.Yes && !myConnection.connected)
{
myConnection.setCOMMPort(serialBox.commPortComboBox.Text);
myConnection.setBaudRate(Convert.ToInt32(serialBox.baudRateComboBox.Text));
myConnection.connect();
}
if (myConnection.connected)
{
ConnectionStatus.Text = "X " + currentX.ToString() + "Y " + currentY.ToString();
}
}
private void InitializeSerial()
{
myConnection = new serialConnection();
}
protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
{
bool updatePos = false;
switch (keyData)
{
case Keys.Left:
currentX += offsetX;
updatePos = true;
break;
case Keys.Right:
currentX -= offsetX;
updatePos = true;
break;
case Keys.Up:
currentY += offsetY;
updatePos = true;
break;
case Keys.Down:
currentY -= offsetY;
updatePos = true;
break;
case Keys.F5:
if (trackingActive)
{
trackingActive = false;
LeftControlTab.Enabled = true;
if(CurrentState == States.KeyboardTracking)
activeStatus.Text = "Keyboard Tracking DEACTIVATED";
if (CurrentState == States.MouseTracking)
activeStatus.Text = "Mouse Tracking DEACTIVATED";
activeStatus.BackColor = Color.Red;
myTimer.Enabled = false;
}
else
{
trackingActive = true;
LeftControlTab.Enabled = false;
if (CurrentState == States.KeyboardTracking)
activeStatus.Text = "Keyboard Tracking ACTIVATED";
if (CurrentState == States.MouseTracking)
activeStatus.Text = "Mouse Tracking ACTIVATED";
activeStatus.BackColor = Color.Green;
myTimer.Enabled = true;
}
break;
}
if (updatePos == true)
{
updatePos = false;
Point temp = new Point();
temp.X = currentX = clipX(currentX);
temp.Y = currentY = clipY(currentY);
String tx = "x," + Convert.ToString(temp.X) + ",y," + Convert.ToString(temp.Y) + ",";
myConnection.sendData(tx);
ConnectionStatus.Text = "X " + currentX.ToString() + "Y " + currentY.ToString();
}
return base.ProcessCmdKey(ref msg, keyData);
}
private void disconnectToolStripMenuItem_Click_1(object sender, EventArgs e)
{
if (myConnection.connected)
{
myConnection.disconnect();
ConnectionStatus.Text = "Disconnected!";
}
}
private void RadioButtons_CheckedChanged(object sender, EventArgs e)
{
if (sender == (RadioButton)this.Controls.Find("SelectMouseRadioButton", true)[0])
{
CurrentState = States.MouseTracking;
activeStatus.Text = "Mouse Tracking ";
}
if (sender == (RadioButton)this.Controls.Find("SelectKeyboardRadioButton", true)[0])
{
CurrentState = States.KeyboardTracking;
activeStatus.Text = "Keyboard Tracking ";
}
if (trackingActive)
activeStatus.Text += "ACTIVATED";
else
activeStatus.Text += "DEACTIVATED";
}
private void TrackMouse(object source, ElapsedEventArgs e)
{
if (trackingActive && CurrentState == States.MouseTracking)
{
MouseMovedFlag = true;
MouseMovedX = -1 * (Cursor.Position.X - XCenter);
MouseMovedY = -1 * (Cursor.Position.Y - YCenter);
currentX += MouseMovedX;
currentX = clipX(currentX);
currentY += MouseMovedY;
currentY = clipY(currentY);
statusStrip1.Invoke(new UpdateStatusBarDelegate(this.UpdateStatusBar), null);
Cursor.Position = new Point(XCenter, YCenter);
}
}
private int clipX(int tempX)
{
if(tempX < minX)
tempX = minX;
if(tempX > maxX)
tempX = maxX;
return tempX;
}
private int clipY(int tempY)
{
if(tempY < minY)
tempY = minY;
if (tempY > maxY)
tempY = maxY;
return tempY;
}
private int YOffset()
{
int tempValue = YOffsetValue;
if (tempValue == 0)
{
YOffsetValue += 22;
return tempValue;
}
else
{
YOffsetValue += 22;
return tempValue;
}
}
void OnTimedEvent(object source, ElapsedEventArgs e)
{
if (true)
{
if (MouseMovedFlag || trackingActive)
{
Point temp = new Point();
temp.X = currentX;
temp.Y = currentY;
String tx = "x," + Convert.ToString(temp.X) + ",y," + Convert.ToString(temp.Y) + ",";
myConnection.sendData(tx);
}
}
}
void UpdateStatusBar()
{
ConnectionStatus.Text = "X " + currentX.ToString() + "Y " + currentY.ToString();
ConnectionStatus.Invalidate();
this.Update();
}
}
}