Introduction
I wanted something to control a mouse using a joystick in a radial fashion on the screen. I've always been disappointed in top-down RPGs that refuse to offer WASD or joystick support. So I did it for myself. I don't feel this is a hack or cheat because this doesn't circumvent any of the balance features that were offered as counter arguments to offering this mode of movement: stopping to attack, etc.
The other interesting thing I did was add a feature to save off the mouse position. I then modified it to let you alter the mouse position while moving the joystick. A red dot in the form of a clipped window shows the hidden position. This method works with all the major titles out there.
If you want to use a joystick in a game that only supports a mouse, this has that potential, but you'll need to implement a mouse "hold"
functionality, as this one currently supports a mouse click.
At a very high radius, it may actually be useful for people who can't use a mouse, and don't like using a joystick to "move" a mouse,
and would rather move in a fixed fashion.
Background
It's really hard to find something that does this all in one place. I'm using "deprecated" direct X input.
The capture joystick class is not mine, but I can't source the author and the code I found doesn't include any comments at all.
Using the code
The important thing is, as this grabs the device, the wrapper will regularly trigger an event. That event is handled and then converted into mouse coordinates.
The relevant code here is shown here.
Notice anything? I'm starting up a thread to do a sendkeys.sendwait
. For some reason
sendkeys.send
throws all kinds of exceptions if the active window isn't setup
to handle keystrokes a certain way. I didn't care to figure that out, and sendwait
is slow, so I started a thread.
void cap_StateRefresh(object sender, JoystickStateEventArgs e)
{
Rectangle resolution = Screen.PrimaryScreen.Bounds;
if (SaveMouseOption.Checked)
{
if (!_Saved)
{
_Saved = true;
_SavePoint = Cursor.Position;
}
}
if ((Math.Abs(e.State.X) < (_JoySettings.XMax * _JoySettings.XDeadzone)) &&
(Math.Abs(e.State.Y) < (_JoySettings.YMax * _JoySettings.YDeadzone)))
{
if (SaveMouseOption.Checked)
{
Cursor.Position = _SavePoint;
_Saved = false;
}
return;
}
int radiusPixels;
if (!int.TryParse(RadiusPixels.Text, out radiusPixels))
{
radiusPixels = 150;
}
int X = (e.State.X < 0) ? e.State.X + (int)(e.State.X * _JoySettings.XDeadzone) :
e.State.X - (int)(e.State.X * _JoySettings.XDeadzone);
int Y = (e.State.Y < 0) ? e.State.Y + (int)(e.State.Y * _JoySettings.YDeadzone) :
e.State.Y - (int)(e.State.Y * _JoySettings.YDeadzone);
POINT point;
GetCursorPos(out point);
int XOffset = (e.State.X < 0) ? (int)(e.State.X * radiusPixels / Math.Abs(_JoySettings.XMin)) :
(int)(e.State.X * radiusPixels / _JoySettings.XMax);
int YOffset = (e.State.Y < 0) ? (int)(e.State.Y * radiusPixels / Math.Abs(_JoySettings.YMin)) :
(int)(e.State.Y * radiusPixels / _JoySettings.YMax);
point.X = (resolution.Width / 2) + XOffset;
point.Y = (resolution.Height / 2) + YOffset;
Cursor.Position = new System.Drawing.Point(point.X, point.Y);
if (MoveMouseClickOption.Checked)
{
DoMouseClick();
}
else if (MoveF12Option.Checked)
{
DoF12();
}
else
{
DoKey();
}
}
public void DoMouseClick()
{
mouse_event(MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0);
mouse_event(MOUSEEVENTF_LEFTUP, 0, 0, 0, 0);
}
public void DoF12()
{
new System.Threading.Thread((System.Threading.ThreadStart)delegate
{
SendKeys.SendWait("{F12}");
});
}
public void DoKey()
{
new System.Threading.Thread((System.Threading.ThreadStart)delegate
{
try
{
SendKeys.SendWait(OtherKeyText.Text);
}
catch (InvalidOperationException)
{
return;
}
catch (Exception)
{
if (InvalidKeyLabel.InvokeRequired)
{
InvalidKeyLabel.BeginInvoke((MethodInvoker)delegate()
{
InvalidKeyLabel.Visible = true;
}, null);
}
else
{
InvalidKeyLabel.Visible = true;
}
}
});
}
[DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
public static extern void mouse_event(long dwFlags, long dx, long dy, long cButtons, long dwExtraInfo);
Points of Interest
This can lock up really bad if you don't include some dead zone space. Fair warning.
History
- Initial version: 6/13/2012
- Update: Save mouse is more like record mouse now. It paints a red dot showing
the mouse position before the joystick hijack, but then records offsets during
hijack, so when you restore, you restore back to the new recorded location. All
in real time. This essentially gives two interactive locations for the mouse cursor!!!! I've outdone myself.