├── .gitignore
├── LICENSE
├── README.md
├── SimExamples
├── App.config
├── App.xaml
├── App.xaml.cs
├── MainWindow.xaml
├── MainWindow.xaml.cs
├── Properties
│ ├── AssemblyInfo.cs
│ ├── Resources.Designer.cs
│ ├── Resources.resx
│ ├── Settings.Designer.cs
│ └── Settings.settings
└── SimExamples.csproj
├── SimWinGamePad
├── GamePadControl.cs
├── Properties
│ ├── AssemblyInfo.cs
│ ├── Resources.Designer.cs
│ ├── Resources.resx
│ └── SimWinGamePad.nuspec
├── ScpBus.cs
├── ScpDriverInstaller.cs
├── ScpDriverInstaller.exe
├── SimGamePad.cs
├── SimWinGamePad.csproj
├── SimulatedGamePadState.cs
├── UserDeclinedDriverException.cs
└── XnaInputToScpBusReport.cs
├── SimWinInput.sln
├── SimWinKeyboard
├── InteropKeyboard.cs
├── Properties
│ ├── AssemblyInfo.cs
│ └── SimWinKeyboard.nuspec
├── SimKeyboard.cs
└── SimWinKeyboard.csproj
├── SimWinMouse
├── InteropMouse.cs
├── InteropScreen.cs
├── Properties
│ ├── AssemblyInfo.cs
│ └── SimWinMouse.nuspec
├── ScreenSize.cs
├── SimMouse.cs
└── SimWinMouse.csproj
└── packages
└── nuget.exe
/.gitignore:
--------------------------------------------------------------------------------
1 | *.suo
2 | *.user
3 | *.cache
4 | [Bb]in/
5 | [Oo]bj/
6 | /packages/
7 | .vs
8 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017 David Rieman
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # SimWinInput
2 | Simulate mouse, keyboard, and GamePad events on Windows.
3 | These provide convenient, small, easy-to-use APIs so you don't have to muck around in messy interop directly.
4 |
5 | Purpose: Accessibility, automation, augmented gaming, and more.
6 |
7 |
8 | ## Simulate Mouse
9 | NOTE: The modern .NET `System.Windows.Forms.Cursor` provides an effective means to control the mouse cursor position by setting the `Cursor.Position` property. If mouse _positioning_ is all that you need, try `Cursor.Position` first. However, `SimWinMouse` will help you accomplish additional scenarios, such as clicking.
10 |
11 | Install `SimWinMouse` [via NuGet](https://docs.microsoft.com/en-us/nuget/quickstart/use-a-package)
12 | or pull the source and add a project reference.
13 |
14 | To immediately move the mouse to the pixel at (100,50) and left-click:
15 | ```
16 | SimMouse.Click(MouseButtons.Left, 100, 50);
17 | ```
18 |
19 | To click and drag from (20, 20) to (50, 50):
20 | ```
21 | SimMouse.Act(SimMouse.Action.LeftButtonDown, 20, 20);
22 | Thread.Sleep(10);
23 | SimMouse.Act(SimMouse.Action.LeftButtonUp, 50, 50);
24 | ```
25 |
26 | ### Advanced Mouse Simulation
27 | If you have more advanced scenarios than the simple API above supports, check out the `InteropMouse.mouse_event` DLL import.
28 |
29 |
30 | ## Simulate Keyboard
31 | Install `SimWinKeyboard` [via NuGet](https://docs.microsoft.com/en-us/nuget/quickstart/use-a-package)
32 | or pull the source and add a project reference.
33 |
34 | To simulate a 'Q' keystroke:
35 | ```
36 | SimKeyboard.Press((byte)'Q');
37 | ```
38 |
39 | To hold the 'Q' key for a prolonged time until later releasing it:
40 | ```
41 | SimKeyboard.KeyDown((byte)'Q');
42 | ...
43 | SimKeyboard.KeyUp((byte)'Q');
44 | ```
45 |
46 | ### Advanced Keyboard Simulation
47 | If you have more advanced scenarios than the simple API above supports, check out the `InteropKeyboard.keybd_event` DLL import.
48 |
49 |
50 | ## Simulate GamePad
51 | Install `SimWinGamePad` [via NuGet](https://docs.microsoft.com/en-us/nuget/quickstart/use-a-package)
52 | or pull the source and add a project reference.
53 |
54 | Before issuing other commands, call `SimGamePad.Instance.Initialize()`.
55 |
56 | Upon first initialization on a given PC, the user may be prompted to accept automatic installation of the [ScpVBus](https://github.com/nefarius/ScpVBus) driver required to simulate Xbox 360 GamePads attached to Windows.
57 | (This is accomplished via the [ScpDriverInterface](https://github.com/DavidRieman/ScpDriverInterface/) installer, which is now an embedded resource in SimWinInput and extracted at runtime as needed. Thus you no longer need to explicitly include the installer executable with your own application/installers.)
58 | Recovery from missing driver requires neither reboot nor app restart, but Initialize will throw an exception if something prevents success (such as the user declining the prompt or UAC elevation for the installer).
59 |
60 | There can be up to four simulated GamePads, but they do not start plugged in. To plug one in as the first GamePad:
61 | ```
62 | SimGamePad.Instance.PlugIn();
63 | ```
64 |
65 | Then, to simulate pressing the 'A' button on the GamePad for a moment, before releasing it:
66 | ```
67 | SimGamePad.Instance.Use(GamePadControl.A);
68 | ```
69 |
70 | Even the analog controls can be simulated into maximum state for a moment, before returning them to their default, unheld positions:
71 | ```
72 | // Pull and release the left trigger.
73 | SimGamePad.Instance.Use(GamePadControl.LeftTrigger);
74 | // Move the right analog stick into the leftmost position, then return to neutral position.
75 | SimGamePad.Instance.Use(GamePadControl.RightStickLeft);
76 | ```
77 |
78 | To unplug the virtual GamePad:
79 | ```
80 | SimGamePad.Instance.Unplug();
81 | ```
82 |
83 | When exiting the program, you should always call `SimGamePad.Instance.ShutDown()`.
84 | This will unplug any remaining simulated GamePads and clean up any utilized resources, such as disposing the driver.
85 |
86 | ### Intermediate GamePad Simulation
87 | Should you need more than one GamePad simulated, each command can optionally specify an index from 0 to 3 for which one to drive. You can also specify hold times (in milliseconds) if the default is not suitable for your needs:
88 | ```
89 | SimGamePad.Instance.Use(GamePadControl.LeftTrigger, 2, 500);
90 | ```
91 |
92 | You can use GamePadControl as flags to designate multiple controls to use at the same time.
93 | You can also simulate controls in a held position until later asking to release them.
94 | This example demonstrates both:
95 | ```
96 | SimGamePad.Instance.SetControl(GamePadControl.RightTrigger | GamePadControl.RightBumper);
97 | ...
98 | SimGamePad.Instance.ReleaseControl(GamePadControl.RightTrigger | GamePadControl.RightBumper);
99 | ```
100 |
101 |
102 | ### Advanced GamePad Simulation
103 | For more advanced scenarios, you can exercise full control over all analog controls and buttons, including the guide button, by modifying and managing state updates manually. For example:
104 | ```
105 | // Get a reference to the state of the first GamePad:
106 | var simPad = SimGamePad.Instance;
107 | var state = simPad.State[0];
108 | // Pull the left trigger halfway back:
109 | state.LeftTrigger = 127;
110 | // Move the right analog stick three quarters of the way to the left:
111 | state.RightStickX = short.MinValue * 3 / 4;
112 | // Add the RightBumber to the set of held buttons:
113 | state.Buttons |= GamePadControl.RightShoulder;
114 | // Update the driver's simulated state with the above state changes:
115 | simPad.Update(0);
116 | ...
117 | // Reset the GamePad to the natural at-rest state:
118 | state.Reset();
119 | simPad.Update();
120 | ```
121 |
--------------------------------------------------------------------------------
/SimExamples/App.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/SimExamples/App.xaml:
--------------------------------------------------------------------------------
1 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/SimExamples/App.xaml.cs:
--------------------------------------------------------------------------------
1 | // This file is part of the SimWinInput project, which is released under MIT License.
2 | // For details, see: https://github.com/DavidRieman/SimWinInput
3 |
4 | namespace SimExamples
5 | {
6 | using System.Windows;
7 |
8 | /// Interaction logic for App.xaml
9 | public partial class App : Application
10 | {
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/SimExamples/MainWindow.xaml:
--------------------------------------------------------------------------------
1 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/SimExamples/MainWindow.xaml.cs:
--------------------------------------------------------------------------------
1 | // This file is part of the SimWinInput project, which is released under MIT License.
2 | // For details, see: https://github.com/DavidRieman/SimWinInput
3 |
4 | namespace SimExamples
5 | {
6 | using SimWinInput;
7 | using System;
8 | using System.Linq;
9 | using System.Threading;
10 | using System.Windows;
11 | using System.Windows.Forms;
12 | using System.Windows.Threading;
13 |
14 | public partial class MainWindow : Window
15 | {
16 | private DispatcherTimer updateTimer = new DispatcherTimer();
17 |
18 | public delegate void UpdateTextCallback(string message);
19 |
20 | public MainWindow()
21 | {
22 | InitializeComponent();
23 | updateTimer.Interval = TimeSpan.FromMilliseconds(100);
24 | updateTimer.Tick += UpdateMousePositionText;
25 | updateTimer.Start();
26 | }
27 |
28 | private void MoveCursorToCornerButton_Click(object sender, RoutedEventArgs e)
29 | {
30 | SimMouse.Act(SimMouse.Action.MoveOnly, 0, 0);
31 | }
32 |
33 | private void MoveCursorNearCornerButton_Click(object sender, RoutedEventArgs e)
34 | {
35 | SimMouse.Act(SimMouse.Action.MoveOnly, 1, 1);
36 | }
37 |
38 | private void MoveCursor3_Click(object sender, RoutedEventArgs e)
39 | {
40 | SimMouse.Act(SimMouse.Action.MoveOnly, 1893, 1061);
41 | }
42 |
43 | private void MoveCursorSecondScreen_Click(object sender, RoutedEventArgs e)
44 | {
45 | // Demonstrates failure of mouse_event to position onto additional screens:
46 | //SimMouse.Act(SimMouse.Action.MoveOnly, 3000, 500);
47 |
48 | // Demonstrate Cursor.position handling additional screen positioning tactics:
49 | if (Screen.AllScreens.Length > 1)
50 | {
51 | var screen = Screen.AllScreens.Last();
52 | var x = screen.Bounds.X + screen.Bounds.Width / 2;
53 | var y = screen.Bounds.Y + screen.Bounds.Height / 2;
54 | System.Windows.Forms.Cursor.Position = new System.Drawing.Point(x, y);
55 | }
56 | else
57 | {
58 | System.Windows.MessageBox.Show("You need more than one monitor to move the cursor to a secondary monitor.");
59 | }
60 | }
61 |
62 | private void TypeASDF_Click(object sender, RoutedEventArgs e)
63 | {
64 | this.TextEntry.Focus();
65 | SimKeyboard.Press((byte)'A');
66 | SimKeyboard.Press((byte)'S');
67 | SimKeyboard.Press((byte)'D');
68 | SimKeyboard.Press((byte)'F');
69 | }
70 |
71 | private void TypeControlA_Click(object sender, RoutedEventArgs e)
72 | {
73 | this.TextEntry.Focus();
74 | SimKeyboard.KeyDown(0xA2); // Left CONTROL
75 | SimKeyboard.KeyDown((byte)'A');
76 | Thread.Sleep(10);
77 | SimKeyboard.KeyUp(0xA2);
78 | SimKeyboard.KeyUp((byte)'A');
79 | }
80 |
81 | private void UpdateMousePositionText(object sender, EventArgs e)
82 | {
83 | var mousePos = System.Windows.Forms.Cursor.Position;
84 | this.MousePositionText.Text = string.Format("Mouse cursor: ({0},{1})", mousePos.X, mousePos.Y);
85 | }
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/SimExamples/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | // This file is part of the SimWinInput project, which is released under MIT License.
2 | // For details, see: https://github.com/DavidRieman/SimWinInput
3 |
4 | using System.Reflection;
5 | using System.Runtime.InteropServices;
6 | using System.Windows;
7 |
8 | // General Information about an assembly is controlled through the following
9 | // set of attributes. Change these attribute values to modify the information
10 | // associated with an assembly.
11 | [assembly: AssemblyTitle("SimExamples")]
12 | [assembly: AssemblyDescription("Usage examples of SimWinInput projects")]
13 | [assembly: AssemblyConfiguration("")]
14 | [assembly: AssemblyCompany("David Rieman")]
15 | [assembly: AssemblyProduct("SimExamples")]
16 | [assembly: AssemblyCopyright("Copyright © David Rieman 2018")]
17 | [assembly: AssemblyTrademark("")]
18 | [assembly: AssemblyCulture("")]
19 |
20 | // Setting ComVisible to false makes the types in this assembly not visible
21 | // to COM components. If you need to access a type in this assembly from
22 | // COM, set the ComVisible attribute to true on that type.
23 | [assembly: ComVisible(false)]
24 |
25 | //In order to begin building localizable applications, set
26 | //CultureYouAreCodingWith in your .csproj file
27 | //inside a . For example, if you are using US english
28 | //in your source files, set the to en-US. Then uncomment
29 | //the NeutralResourceLanguage attribute below. Update the "en-US" in
30 | //the line below to match the UICulture setting in the project file.
31 |
32 | //[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)]
33 |
34 |
35 | [assembly: ThemeInfo(
36 | ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
37 | //(used if a resource is not found in the page,
38 | // or application resource dictionaries)
39 | ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
40 | //(used if a resource is not found in the page,
41 | // app, or any theme specific resource dictionaries)
42 | )]
43 |
44 |
45 | [assembly: AssemblyVersion("1.0.0.0")]
46 | [assembly: AssemblyFileVersion("1.0.0.0")]
47 |
--------------------------------------------------------------------------------
/SimExamples/Properties/Resources.Designer.cs:
--------------------------------------------------------------------------------
1 | //------------------------------------------------------------------------------
2 | //
3 | // This code was generated by a tool.
4 | // Runtime Version:4.0.30319.42000
5 | //
6 | // Changes to this file may cause incorrect behavior and will be lost if
7 | // the code is regenerated.
8 | //
9 | //------------------------------------------------------------------------------
10 |
11 | namespace SimExamples.Properties {
12 | using System;
13 |
14 |
15 | ///
16 | /// A strongly-typed resource class, for looking up localized strings, etc.
17 | ///
18 | // This class was auto-generated by the StronglyTypedResourceBuilder
19 | // class via a tool like ResGen or Visual Studio.
20 | // To add or remove a member, edit your .ResX file then rerun ResGen
21 | // with the /str option, or rebuild your VS project.
22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
25 | internal class Resources {
26 |
27 | private static global::System.Resources.ResourceManager resourceMan;
28 |
29 | private static global::System.Globalization.CultureInfo resourceCulture;
30 |
31 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
32 | internal Resources() {
33 | }
34 |
35 | ///
36 | /// Returns the cached ResourceManager instance used by this class.
37 | ///
38 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
39 | internal static global::System.Resources.ResourceManager ResourceManager {
40 | get {
41 | if (object.ReferenceEquals(resourceMan, null)) {
42 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("SimExamples.Properties.Resources", typeof(Resources).Assembly);
43 | resourceMan = temp;
44 | }
45 | return resourceMan;
46 | }
47 | }
48 |
49 | ///
50 | /// Overrides the current thread's CurrentUICulture property for all
51 | /// resource lookups using this strongly typed resource class.
52 | ///
53 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
54 | internal static global::System.Globalization.CultureInfo Culture {
55 | get {
56 | return resourceCulture;
57 | }
58 | set {
59 | resourceCulture = value;
60 | }
61 | }
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/SimExamples/Properties/Resources.resx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 | text/microsoft-resx
110 |
111 |
112 | 2.0
113 |
114 |
115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
116 |
117 |
118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
119 |
120 |
--------------------------------------------------------------------------------
/SimExamples/Properties/Settings.Designer.cs:
--------------------------------------------------------------------------------
1 | //------------------------------------------------------------------------------
2 | //
3 | // This code was generated by a tool.
4 | // Runtime Version:4.0.30319.42000
5 | //
6 | // Changes to this file may cause incorrect behavior and will be lost if
7 | // the code is regenerated.
8 | //
9 | //------------------------------------------------------------------------------
10 |
11 | namespace SimExamples.Properties {
12 |
13 |
14 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
15 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "17.11.0.0")]
16 | internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
17 |
18 | private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
19 |
20 | public static Settings Default {
21 | get {
22 | return defaultInstance;
23 | }
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/SimExamples/Properties/Settings.settings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/SimExamples/SimExamples.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | AnyCPU
7 | {0B40A9B2-C350-46E1-B885-EAFC74CC9129}
8 | WinExe
9 | SimExamples
10 | SimExamples
11 | v4.8
12 | 512
13 | {60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
14 | 4
15 | true
16 | true
17 |
18 |
19 |
20 | AnyCPU
21 | true
22 | full
23 | false
24 | bin\Debug\
25 | DEBUG;TRACE
26 | prompt
27 | 4
28 |
29 |
30 | AnyCPU
31 | pdbonly
32 | true
33 | bin\Release\
34 | TRACE
35 | prompt
36 | 4
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 | 4.0
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 | MSBuild:Compile
59 | Designer
60 |
61 |
62 | MSBuild:Compile
63 | Designer
64 |
65 |
66 | App.xaml
67 | Code
68 |
69 |
70 | MainWindow.xaml
71 | Code
72 |
73 |
74 |
75 |
76 | Code
77 |
78 |
79 | True
80 | True
81 | Resources.resx
82 |
83 |
84 | True
85 | Settings.settings
86 | True
87 |
88 |
89 | ResXFileCodeGenerator
90 | Resources.Designer.cs
91 |
92 |
93 | SettingsSingleFileGenerator
94 | Settings.Designer.cs
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 | {46541c61-9956-43c1-a30d-43574c4e48b2}
103 | SimWinGamePad
104 |
105 |
106 | {51110ab1-5e63-472f-a4b0-ded9e0da4547}
107 | SimWinKeyboard
108 |
109 |
110 | {beae9c9a-3d3f-44ee-8568-c3faa3cd6c9e}
111 | SimWinMouse
112 |
113 |
114 |
115 |
--------------------------------------------------------------------------------
/SimWinGamePad/GamePadControl.cs:
--------------------------------------------------------------------------------
1 | // This file is part of the SimWinGamePad project, which is released under MIT License.
2 | // For details, see: https://github.com/DavidRieman/SimWinInput
3 |
4 | namespace SimWinInput
5 | {
6 | using System;
7 | using System.Collections.Generic;
8 |
9 | [Flags]
10 | public enum GamePadControl : Int32
11 | {
12 | None = 0,
13 |
14 | // This block of controls should map up with ScpBus expectations.
15 | DPadUp = 1 << 0,
16 | DPadDown = 1 << 1,
17 | DPadLeft = 1 << 2,
18 | DPadRight = 1 << 3,
19 | Start = 1 << 4,
20 | Back = 1 << 5,
21 | LeftStickClick = 1 << 6,
22 | RightStickClick = 1 << 7,
23 | LeftShoulder = 1 << 8,
24 | RightShoulder = 1 << 9,
25 | Guide = 1 << 10,
26 | A = 1 << 12,
27 | B = 1 << 13,
28 | X = 1 << 14,
29 | Y = 1 << 15,
30 |
31 | // The remaining flags are not associated with ScpBus, but can be used for other purposes.
32 | LeftTrigger = 1 << 16,
33 | RightTrigger = 1 << 17,
34 | LeftStickLeft = 1 << 18,
35 | LeftStickRight = 1 << 19,
36 | LeftStickUp = 1 << 20,
37 | LeftStickDown = 1 << 21,
38 | RightStickLeft = 1 << 22,
39 | RightStickRight = 1 << 23,
40 | RightStickUp = 1 << 24,
41 | RightStickDown = 1 << 25,
42 | LeftStickAsAnalog = 1 << 28,
43 | RightStickAsAnalog = 1 << 29,
44 | DPadAsAnalog = 1 << 30,
45 | }
46 |
47 | /// GamePadControls provides static helpers for handling GamePadControl information.
48 | public static class GamePadControls
49 | {
50 | /// Provides the set of GamePadControl entries which directly represent binary buttons which should have just on/off states.
51 | public static readonly GamePadControl[] BinaryControls = {
52 | GamePadControl.DPadUp, GamePadControl.DPadDown, GamePadControl.DPadLeft, GamePadControl.DPadRight,
53 | GamePadControl.Start, GamePadControl.Back,
54 | GamePadControl.LeftStickClick, GamePadControl.RightStickClick,
55 | GamePadControl.LeftShoulder, GamePadControl.RightShoulder,
56 | GamePadControl.Guide, GamePadControl.A, GamePadControl.B, GamePadControl.X, GamePadControl.Y
57 | };
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/SimWinGamePad/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | // This file is part of the SimWinGamePad project, which is released under MIT License.
2 | // For details, see: https://github.com/DavidRieman/SimWinInput
3 |
4 | using System.Reflection;
5 | using System.Runtime.InteropServices;
6 |
7 | [assembly: AssemblyTitle("SimWinGamePad")]
8 | [assembly: AssemblyDescription("Simulate Windows GamePad controller events with .NET")]
9 | [assembly: AssemblyConfiguration("")]
10 | [assembly: AssemblyCompany("David Rieman")]
11 | [assembly: AssemblyProduct("SimWinGamePad")]
12 | [assembly: AssemblyCopyright("Copyright © 2018 David Rieman")]
13 | [assembly: AssemblyTrademark("")]
14 | [assembly: AssemblyCulture("")]
15 |
16 | // Setting ComVisible to false makes the types in this assembly not visible
17 | // to COM components. If you need to access a type in this assembly from
18 | // COM, set the ComVisible attribute to true on that type.
19 | [assembly: ComVisible(false)]
20 |
21 | // The following GUID is for the ID of the typelib if this project is exposed to COM
22 | [assembly: Guid("46541c61-9956-43c1-a30d-43574c4e48b2")]
23 |
24 | [assembly: AssemblyVersion("1.1.0")]
25 | [assembly: AssemblyFileVersion("1.1.0")]
26 |
--------------------------------------------------------------------------------
/SimWinGamePad/Properties/Resources.Designer.cs:
--------------------------------------------------------------------------------
1 | //------------------------------------------------------------------------------
2 | //
3 | // This code was generated by a tool.
4 | // Runtime Version:4.0.30319.42000
5 | //
6 | // Changes to this file may cause incorrect behavior and will be lost if
7 | // the code is regenerated.
8 | //
9 | //------------------------------------------------------------------------------
10 |
11 | namespace SimWinGamePad.Properties {
12 | using System;
13 |
14 |
15 | ///
16 | /// A strongly-typed resource class, for looking up localized strings, etc.
17 | ///
18 | // This class was auto-generated by the StronglyTypedResourceBuilder
19 | // class via a tool like ResGen or Visual Studio.
20 | // To add or remove a member, edit your .ResX file then rerun ResGen
21 | // with the /str option, or rebuild your VS project.
22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
25 | internal class Resources {
26 |
27 | private static global::System.Resources.ResourceManager resourceMan;
28 |
29 | private static global::System.Globalization.CultureInfo resourceCulture;
30 |
31 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
32 | internal Resources() {
33 | }
34 |
35 | ///
36 | /// Returns the cached ResourceManager instance used by this class.
37 | ///
38 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
39 | internal static global::System.Resources.ResourceManager ResourceManager {
40 | get {
41 | if (object.ReferenceEquals(resourceMan, null)) {
42 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("SimWinGamePad.Properties.Resources", typeof(Resources).Assembly);
43 | resourceMan = temp;
44 | }
45 | return resourceMan;
46 | }
47 | }
48 |
49 | ///
50 | /// Overrides the current thread's CurrentUICulture property for all
51 | /// resource lookups using this strongly typed resource class.
52 | ///
53 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
54 | internal static global::System.Globalization.CultureInfo Culture {
55 | get {
56 | return resourceCulture;
57 | }
58 | set {
59 | resourceCulture = value;
60 | }
61 | }
62 |
63 | ///
64 | /// Looks up a localized resource of type System.Byte[].
65 | ///
66 | internal static byte[] ScpDriverInstaller {
67 | get {
68 | object obj = ResourceManager.GetObject("ScpDriverInstaller", resourceCulture);
69 | return ((byte[])(obj));
70 | }
71 | }
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/SimWinGamePad/Properties/Resources.resx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 | text/microsoft-resx
110 |
111 |
112 | 2.0
113 |
114 |
115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
116 |
117 |
118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
119 |
120 |
121 |
122 | ..\scpdriverinstaller.exe;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
123 |
124 |
--------------------------------------------------------------------------------
/SimWinGamePad/Properties/SimWinGamePad.nuspec:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | $author$
5 | $copyright$
6 | $description$
7 | $id$
8 | MIT
9 | $author$
10 | https://github.com/DavidRieman/SimWinInput
11 | Initial release, plus bug fixes, embedded driver installer, and documentation.
12 | false
13 | GamePad events simulate emulate Xbox 360 controller driver buttons triggers analog sticks Windows
14 | $title$
15 | $version$
16 |
17 |
--------------------------------------------------------------------------------
/SimWinGamePad/ScpBus.cs:
--------------------------------------------------------------------------------
1 | /*
2 | * ScpDriverInterface - by Mogzol (and of course Scarlet.Crush) - Jan, 2016
3 | *
4 | * This is a simple little DLL which allows you to use Scarlet.Crush's SCP Virtual
5 | * Bus Driver to emulate Xbox 360 controllers.
6 | *
7 | * Most of the code here has been ripped out of his ScpControl source code, mostly
8 | * from the ScpDevice and BusDevice classes, so obviously credit and major props to
9 | * Scarlet.Crush, without him this wouldn't be possible. You can download his
10 | * original source code from here:
11 | * http://forums.pcsx2.net/Thread-XInput-Wrapper-for-DS3-and-Play-com-USB-Dual-DS2-Controller
12 | *
13 | * Note that for this to work the SCP Virtual Bus Driver must be installed.
14 | *
15 | * For further details on the code this component was based on, see:
16 | * https://github.com/DavidRieman/ScpDriverInterface/
17 | */
18 |
19 | using System;
20 | using System.Globalization;
21 | using System.IO;
22 | using System.Runtime.InteropServices;
23 | using Microsoft.Win32.SafeHandles;
24 | [assembly: CLSCompliant(true)]
25 |
26 | namespace SimWinInput
27 | {
28 | ///
29 | /// Emulates Xbox 360 controllers via Scarlet.Crush's SCP Virtual Bus Driver.
30 | ///
31 | public class ScpBus : IDisposable
32 | {
33 | private const string SCP_BUS_CLASS_GUID = "{F679F562-3164-42CE-A4DB-E7DDBE723909}";
34 | private const int ReportSize = 28;
35 |
36 | private readonly SafeFileHandle _deviceHandle;
37 |
38 | ///
39 | /// Creates a new ScpBus object, which will then try to get a handle to the SCP Virtual Bus device. If it is unable to get the handle, an IOException will be thrown.
40 | ///
41 | public ScpBus() : this(0) { }
42 |
43 | ///
44 | /// Creates a new ScpBus object, which will then try to get a handle to the SCP Virtual Bus device. If it is unable to get the handle, an IOException will be thrown.
45 | ///
46 | /// Specifies which SCP Virtual Bus device to use. This is 0-based.
47 | public ScpBus(int instance)
48 | {
49 | string devicePath = "";
50 |
51 | if (Find(new Guid(SCP_BUS_CLASS_GUID), ref devicePath, instance))
52 | {
53 | _deviceHandle = GetHandle(devicePath);
54 | }
55 | else
56 | {
57 | throw new IOException("SCP Virtual Bus Device not found");
58 | }
59 | }
60 |
61 | ///
62 | /// Creates a new ScpBus object, which will then try to get a handle to the specified SCP Virtual Bus device. If it is unable to get the handle, an IOException will be thrown.
63 | ///
64 | /// The path to the SCP Virtual Bus device that you want to use.
65 | public ScpBus(string devicePath)
66 | {
67 | _deviceHandle = GetHandle(devicePath);
68 | }
69 |
70 | ///
71 | /// Closes the handle to the SCP Virtual Bus device. Call this when you are done with your instance of ScpBus.
72 | ///
73 | /// (This method does the same thing as the Dispose() method. Use one or the other.)
74 | ///
75 | public void Close()
76 | {
77 | Dispose();
78 | }
79 |
80 | ///
81 | /// Closes the handle to the SCP Virtual Bus device. Call this when you are done with your instance of ScpBus.
82 | ///
83 | public void Dispose()
84 | {
85 | Dispose(true);
86 | GC.SuppressFinalize(this);
87 | }
88 |
89 | protected virtual void Dispose(bool disposing)
90 | {
91 | if (_deviceHandle != null && !_deviceHandle.IsInvalid)
92 | {
93 | _deviceHandle.Dispose();
94 | }
95 | }
96 |
97 | ///
98 | /// Plugs in an emulated Xbox 360 controller.
99 | ///
100 | /// Used to identify the controller. Give each controller you plug in a different number. Number must be non-zero.
101 | /// True if the operation was successful, false otherwise.
102 | public bool PlugIn(int controllerNumber)
103 | {
104 | if (_deviceHandle.IsInvalid)
105 | throw new ObjectDisposedException("SCP Virtual Bus device handle is closed");
106 |
107 | int transfered = 0;
108 | byte[] buffer = new byte[16];
109 |
110 | buffer[0] = 0x10;
111 | buffer[1] = 0x00;
112 | buffer[2] = 0x00;
113 | buffer[3] = 0x00;
114 |
115 | buffer[4] = (byte)((controllerNumber) & 0xFF);
116 | buffer[5] = (byte)((controllerNumber >> 8) & 0xFF);
117 | buffer[6] = (byte)((controllerNumber >> 16) & 0xFF);
118 | buffer[7] = (byte)((controllerNumber >> 24) & 0xFF);
119 |
120 | return NativeMethods.DeviceIoControl(_deviceHandle, 0x2A4000, buffer, buffer.Length, null, 0, ref transfered, IntPtr.Zero);
121 | }
122 |
123 | ///
124 | /// Unplugs an emulated Xbox 360 controller.
125 | ///
126 | /// The controller you want to unplug.
127 | /// True if the operation was successful, false otherwise.
128 | public bool Unplug(int controllerNumber)
129 | {
130 | if (_deviceHandle.IsInvalid)
131 | throw new ObjectDisposedException("SCP Virtual Bus device handle is closed");
132 |
133 | int transfered = 0;
134 | byte[] buffer = new Byte[16];
135 |
136 | buffer[0] = 0x10;
137 | buffer[1] = 0x00;
138 | buffer[2] = 0x00;
139 | buffer[3] = 0x00;
140 |
141 | buffer[4] = (byte)((controllerNumber) & 0xFF);
142 | buffer[5] = (byte)((controllerNumber >> 8) & 0xFF);
143 | buffer[6] = (byte)((controllerNumber >> 16) & 0xFF);
144 | buffer[7] = (byte)((controllerNumber >> 24) & 0xFF);
145 |
146 | return NativeMethods.DeviceIoControl(_deviceHandle, 0x2A4004, buffer, buffer.Length, null, 0, ref transfered, IntPtr.Zero);
147 | }
148 |
149 | ///
150 | /// Unplugs all emulated Xbox 360 controllers.
151 | ///
152 | /// True if the operation was successful, false otherwise.
153 | public bool UnplugAll()
154 | {
155 | if (_deviceHandle.IsInvalid)
156 | throw new ObjectDisposedException("SCP Virtual Bus device handle is closed");
157 |
158 | int transfered = 0;
159 | byte[] buffer = new byte[16];
160 |
161 | buffer[0] = 0x10;
162 | buffer[1] = 0x00;
163 | buffer[2] = 0x00;
164 | buffer[3] = 0x00;
165 |
166 | return NativeMethods.DeviceIoControl(_deviceHandle, 0x2A4004, buffer, buffer.Length, null, 0, ref transfered, IntPtr.Zero);
167 | }
168 |
169 | ///
170 | /// Sends an input report for the current state of the specified emulated Xbox 360 controller. Note: Only use this if you don't care about rumble data, otherwise use the 3-parameter version of Report().
171 | ///
172 | /// The controller to report.
173 | /// The controller report. If using the included X360Controller class, this can be generated with the GetReport() method. Otherwise see http://free60.org/wiki/GamePad#Input_report for details.
174 | /// True if the operation was successful, false otherwise.
175 | public bool Report(int controllerNumber, byte[] controllerReport)
176 | {
177 | return Report(controllerNumber, controllerReport, null);
178 | }
179 |
180 | ///
181 | /// Sends an input report for the current state of the specified emulated Xbox 360 controller. If you care about rumble data, make sure you check the output report for rumble data every time you call this.
182 | ///
183 | /// The controller to report.
184 | /// The controller report. If using the included X360Controller class, this can be generated with the GetReport() method. Otherwise see http://free60.org/wiki/GamePad#Input_report for details.
185 | /// The buffer for the output report, which takes the form specified here: http://free60.org/wiki/GamePad#Output_report. Use an 8-byte buffer if you care about rumble data, or null otherwise.
186 | /// True if the operation was successful, false otherwise.
187 | public bool Report(int controllerNumber, byte[] controllerReport, byte[] outputBuffer)
188 | {
189 | if (_deviceHandle.IsInvalid)
190 | throw new ObjectDisposedException("SCP Virtual Bus device handle is closed");
191 |
192 | byte[] head = new byte[8];
193 |
194 | head[0] = 0x1C;
195 | head[4] = (byte)((controllerNumber) & 0xFF);
196 | head[5] = (byte)((controllerNumber >> 8) & 0xFF);
197 | head[6] = (byte)((controllerNumber >> 16) & 0xFF);
198 | head[7] = (byte)((controllerNumber >> 24) & 0xFF);
199 |
200 | byte[] fullReport = new byte[28];
201 |
202 | Buffer.BlockCopy(head, 0, fullReport, 0, head.Length);
203 | Buffer.BlockCopy(controllerReport, 0, fullReport, head.Length, controllerReport.Length);
204 |
205 | int transferred = 0;
206 | return NativeMethods.DeviceIoControl(_deviceHandle, 0x2A400C, fullReport, fullReport.Length, outputBuffer, outputBuffer?.Length ?? 0, ref transferred, IntPtr.Zero) && transferred > 0;
207 | }
208 |
209 | private static bool Find(Guid target, ref string path, int instance = 0)
210 | {
211 | IntPtr detailDataBuffer = IntPtr.Zero;
212 | IntPtr deviceInfoSet = IntPtr.Zero;
213 |
214 | try
215 | {
216 | NativeMethods.SP_DEVICE_INTERFACE_DATA DeviceInterfaceData = new NativeMethods.SP_DEVICE_INTERFACE_DATA(), da = new NativeMethods.SP_DEVICE_INTERFACE_DATA();
217 | int bufferSize = 0, memberIndex = 0;
218 |
219 | deviceInfoSet = NativeMethods.SetupDiGetClassDevs(ref target, IntPtr.Zero, IntPtr.Zero, NativeMethods.DIGCF_PRESENT | NativeMethods.DIGCF_DEVICEINTERFACE);
220 |
221 | DeviceInterfaceData.cbSize = da.cbSize = Marshal.SizeOf(DeviceInterfaceData);
222 |
223 | while (NativeMethods.SetupDiEnumDeviceInterfaces(deviceInfoSet, IntPtr.Zero, ref target, memberIndex, ref DeviceInterfaceData))
224 | {
225 | NativeMethods.SetupDiGetDeviceInterfaceDetail(deviceInfoSet, ref DeviceInterfaceData, IntPtr.Zero, 0, ref bufferSize, ref da);
226 | detailDataBuffer = Marshal.AllocHGlobal(bufferSize);
227 |
228 | Marshal.WriteInt32(detailDataBuffer, (IntPtr.Size == 4) ? (4 + Marshal.SystemDefaultCharSize) : 8);
229 |
230 | if (NativeMethods.SetupDiGetDeviceInterfaceDetail(deviceInfoSet, ref DeviceInterfaceData, detailDataBuffer, bufferSize, ref bufferSize, ref da))
231 | {
232 | IntPtr pDevicePathName = detailDataBuffer + 4;
233 |
234 | path = Marshal.PtrToStringAuto(pDevicePathName).ToUpper(CultureInfo.InvariantCulture);
235 | Marshal.FreeHGlobal(detailDataBuffer);
236 |
237 | if (memberIndex == instance) return true;
238 | }
239 | else Marshal.FreeHGlobal(detailDataBuffer);
240 |
241 |
242 | memberIndex++;
243 | }
244 | }
245 | finally
246 | {
247 | if (deviceInfoSet != IntPtr.Zero)
248 | {
249 | NativeMethods.SetupDiDestroyDeviceInfoList(deviceInfoSet);
250 | }
251 | }
252 |
253 | return false;
254 | }
255 |
256 | private static SafeFileHandle GetHandle(string devicePath)
257 | {
258 | devicePath = devicePath.ToUpper(CultureInfo.InvariantCulture);
259 |
260 | SafeFileHandle handle = NativeMethods.CreateFile(devicePath, (NativeMethods.GENERIC_WRITE | NativeMethods.GENERIC_READ), NativeMethods.FILE_SHARE_READ | NativeMethods.FILE_SHARE_WRITE, IntPtr.Zero, NativeMethods.OPEN_EXISTING, NativeMethods.FILE_ATTRIBUTE_NORMAL | NativeMethods.FILE_FLAG_OVERLAPPED, UIntPtr.Zero);
261 |
262 | if (handle == null || handle.IsInvalid)
263 | {
264 | throw new IOException("Unable to get SCP Virtual Bus Device handle");
265 | }
266 |
267 | return handle;
268 | }
269 | }
270 |
271 | internal static class NativeMethods
272 | {
273 | [StructLayout(LayoutKind.Sequential)]
274 | internal struct SP_DEVICE_INTERFACE_DATA
275 | {
276 | internal int cbSize;
277 | internal Guid InterfaceClassGuid;
278 | internal int Flags;
279 | internal IntPtr Reserved;
280 | }
281 |
282 | internal const uint FILE_ATTRIBUTE_NORMAL = 0x80;
283 | internal const uint FILE_FLAG_OVERLAPPED = 0x40000000;
284 | internal const uint FILE_SHARE_READ = 1;
285 | internal const uint FILE_SHARE_WRITE = 2;
286 | internal const uint GENERIC_READ = 0x80000000;
287 | internal const uint GENERIC_WRITE = 0x40000000;
288 | internal const uint OPEN_EXISTING = 3;
289 | internal const int DIGCF_PRESENT = 0x0002;
290 | internal const int DIGCF_DEVICEINTERFACE = 0x0010;
291 |
292 | [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
293 | internal static extern SafeFileHandle CreateFile(string lpFileName, uint dwDesiredAccess, uint dwShareMode, IntPtr lpSecurityAttributes, uint dwCreationDisposition, uint dwFlagsAndAttributes, UIntPtr hTemplateFile);
294 |
295 | [DllImport("kernel32.dll", SetLastError = true)]
296 | [return: MarshalAs(UnmanagedType.Bool)]
297 | internal static extern bool DeviceIoControl(SafeFileHandle hDevice, int dwIoControlCode, byte[] lpInBuffer, int nInBufferSize, byte[] lpOutBuffer, int nOutBufferSize, ref int lpBytesReturned, IntPtr lpOverlapped);
298 |
299 | [DllImport("setupapi.dll", SetLastError = true)]
300 | internal static extern int SetupDiDestroyDeviceInfoList(IntPtr deviceInfoSet);
301 |
302 | [DllImport("setupapi.dll", SetLastError = true)]
303 | [return: MarshalAs(UnmanagedType.Bool)]
304 | internal static extern bool SetupDiEnumDeviceInterfaces(IntPtr hDevInfo, IntPtr devInfo, ref Guid interfaceClassGuid, int memberIndex, ref SP_DEVICE_INTERFACE_DATA deviceInterfaceData);
305 |
306 | [DllImport("setupapi.dll", SetLastError = true, CharSet = CharSet.Auto)]
307 | internal static extern IntPtr SetupDiGetClassDevs(ref Guid classGuid, IntPtr enumerator, IntPtr hwndParent, int flags);
308 |
309 | [DllImport("setupapi.dll", SetLastError = true, CharSet = CharSet.Auto)]
310 | [return: MarshalAs(UnmanagedType.Bool)]
311 | internal static extern bool SetupDiGetDeviceInterfaceDetail(IntPtr hDevInfo, ref SP_DEVICE_INTERFACE_DATA deviceInterfaceData, IntPtr deviceInterfaceDetailData, int deviceInterfaceDetailDataSize, ref int requiredSize, ref SP_DEVICE_INTERFACE_DATA deviceInfoData);
312 | }
313 | }
--------------------------------------------------------------------------------
/SimWinGamePad/ScpDriverInstaller.cs:
--------------------------------------------------------------------------------
1 | // This file is part of the SimWinGamePad project, which is released under MIT License.
2 | // For details, see: https://github.com/DavidRieman/SimWinInput
3 |
4 | namespace SimWinInput
5 | {
6 | using System.Diagnostics;
7 | using System.IO;
8 | using System.Reflection;
9 |
10 | public class ScpDriverInstaller
11 | {
12 | public static void Install()
13 | {
14 | // If the installer was included already with the running executable, prefer to run it from there.
15 | var assemblyLocation = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
16 | var filePath = Path.Combine(assemblyLocation, "ScpDriverInstaller.exe");
17 |
18 | // If the file did not exist there, let's try to extract it there. (Or perhaps we should prefer system temp directory?)
19 | if (!File.Exists(filePath))
20 | {
21 | // Attempt to extract the executable as a resource
22 | File.WriteAllBytes(filePath, SimWinGamePad.Properties.Resources.ScpDriverInstaller);
23 | }
24 |
25 | var process = Process.Start(filePath, "/install /silent");
26 | process.WaitForExit();
27 | }
28 | }
29 | }
--------------------------------------------------------------------------------
/SimWinGamePad/ScpDriverInstaller.exe:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DavidRieman/SimWinInput/21a635441866a9f9c4e4659a8e7b4cd6e80db0d5/SimWinGamePad/ScpDriverInstaller.exe
--------------------------------------------------------------------------------
/SimWinGamePad/SimGamePad.cs:
--------------------------------------------------------------------------------
1 | // This file is part of the SimWinGamePad project, which is released under MIT License.
2 | // For details, see: https://github.com/DavidRieman/SimWinInput
3 |
4 | namespace SimWinInput
5 | {
6 | using System;
7 | using System.IO;
8 | using System.Linq;
9 | using System.Threading;
10 | using System.Windows.Forms;
11 |
12 | public class SimGamePad
13 | {
14 | private static readonly SimGamePad instance = new SimGamePad();
15 | private static readonly string NoDriverMessage = "The ScpVBus driver used for simulating GamePad input was not found. Would you like to install it and retry?" + Environment.NewLine + "This may prompt for administrative privileges.";
16 |
17 | private readonly bool[] isPluggedIn = new bool[4];
18 |
19 | // Note that ScpBus expects controller Numbers while we use Index; giving bus methods an arg of 0 for identifying controller is wrong.
20 | // While the ScpBus interface is well-established, we can at least use sensible indices and convert to number (via +1) at our own call sites.
21 | private ScpBus bus;
22 |
23 | private SimGamePad()
24 | {
25 | this.State = new SimulatedGamePadState[4] { new SimulatedGamePadState(), new SimulatedGamePadState(), new SimulatedGamePadState(), new SimulatedGamePadState() };
26 | }
27 |
28 | /// The singleton instance of the SimGamePad class.
29 | public static SimGamePad Instance { get { return instance; } }
30 |
31 | /// Direct access to the simulated game pad state, for cases where you need more manual control than the other methods.
32 | /// Use in conjunction with the "Update" method after your new state is fully configured.
33 | public SimulatedGamePadState[] State { get; private set; }
34 |
35 | /// Initialize SimGamePad, including the key driver for simulation.
36 | /// If true and the driver could not be loaded, asks the user if they'd like to install it, and automatically recovers if they do.
37 | public void Initialize(bool mayAutoInstallDriver = true)
38 | {
39 | if (bus != null)
40 | {
41 | return; // Already initialized.
42 | }
43 |
44 | bool retryInit = false;
45 | do
46 | {
47 | try
48 | {
49 | bus = new ScpBus();
50 | retryInit = false;
51 | }
52 | catch (IOException)
53 | {
54 | if (mayAutoInstallDriver)
55 | {
56 | var result = MessageBox.Show(NoDriverMessage, "Install", MessageBoxButtons.YesNo);
57 | if (result == DialogResult.Yes)
58 | {
59 | ScpDriverInstaller.Install();
60 | retryInit = true;
61 | }
62 | else
63 | {
64 | throw new UserDeclinedDriverException("ScpVBus");
65 | }
66 | }
67 | }
68 | } while (retryInit);
69 | }
70 |
71 | /// Shut down and clean up any disposable resources held by SimGamePad.
72 | ///
73 | /// This should be called before application shutdown is completed (whenever possible), as the underlying driver
74 | /// can be finicky. For example, you may want to call this upon Dispose of your main window/form or through other
75 | /// app shutdown eventing. This method can safely be called redundantly.
76 | ///
77 | public void ShutDown()
78 | {
79 | if (bus != null)
80 | {
81 | // Automatically unplug simulated controllers that we can't drive anymore.
82 | for (var i = 0; i < 4; i++)
83 | {
84 | Unplug(i);
85 | }
86 |
87 | bus.Dispose();
88 | bus = null;
89 | }
90 | }
91 |
92 | /// Simulate plugging in a controller.
93 | /// The index specifying which controller to plug in.
94 | public void PlugIn(int controllerIndex = 0)
95 | {
96 | EnsureInitialized();
97 | int i = controllerIndex;
98 | if (bus != null && !isPluggedIn[i])
99 | {
100 | bus.PlugIn(i + 1);
101 | isPluggedIn[i] = true;
102 | }
103 | }
104 |
105 | /// Unplug a simulated controller.
106 | /// The index specifying which controller to unplug.
107 | public void Unplug(int controllerIndex = 0)
108 | {
109 | EnsureInitialized();
110 | int i = controllerIndex;
111 | if (bus != null && isPluggedIn[i])
112 | {
113 | bus.Unplug(i + 1);
114 | isPluggedIn[i] = false;
115 | }
116 | }
117 |
118 | ///
119 | /// Use the specified control and release it to default state after a moment.
120 | /// For example, press and release a specified button, or move and return an extreme analog stick position.
121 | ///
122 | /// Which control to use.
123 | /// Which controller to use.
124 | /// How many milliseconds to hold the button down.
125 | public void Use(GamePadControl control, int controllerIndex = 0, int holdTimeMS = 50)
126 | {
127 | EnsureInitialized();
128 | SetControl(control, controllerIndex);
129 | Thread.Sleep(holdTimeMS);
130 | ReleaseControl(control, controllerIndex);
131 | }
132 |
133 | /// Puts the the specified control in the simulated "fully on" state.
134 | /// Which control to set.
135 | /// The index of the controller to set this state for.
136 | public void SetControl(GamePadControl control, int controllerIndex = 0)
137 | {
138 | EnsureInitialized();
139 | int i = controllerIndex;
140 | if (!isPluggedIn[i])
141 | {
142 | PlugIn(controllerIndex);
143 | }
144 |
145 | var flagsToSet = Enum.GetValues(typeof(GamePadControl)).Cast().Where(c => c != GamePadControl.None && (control & c) != 0);
146 | foreach (var flag in flagsToSet)
147 | {
148 | if (flag <= GamePadControl.Y)
149 | {
150 | State[i].Buttons |= flag;
151 | }
152 | else
153 | {
154 | switch (flag)
155 | {
156 | case GamePadControl.LeftTrigger: State[i].LeftTrigger = byte.MaxValue; break;
157 | case GamePadControl.RightTrigger: State[i].RightTrigger = byte.MaxValue; break;
158 | case GamePadControl.LeftStickLeft: State[i].LeftStickX = short.MinValue; break;
159 | case GamePadControl.LeftStickRight: State[i].LeftStickX = short.MaxValue; break;
160 | case GamePadControl.LeftStickUp: State[i].LeftStickY = short.MaxValue; break;
161 | case GamePadControl.LeftStickDown: State[i].LeftStickY = short.MinValue; break;
162 | case GamePadControl.RightStickLeft: State[i].RightStickX = short.MinValue; break;
163 | case GamePadControl.RightStickRight: State[i].RightStickX = short.MaxValue; break;
164 | case GamePadControl.RightStickUp: State[i].RightStickY = short.MaxValue; break;
165 | case GamePadControl.RightStickDown: State[i].RightStickY = short.MinValue; break;
166 | }
167 | }
168 | }
169 |
170 | Update(controllerIndex);
171 | }
172 |
173 | /// Puts the the specified control in the simulated "fully off" state.
174 | /// Which control to set.
175 | /// The index of the controller to set this state for.
176 | public void ReleaseControl(GamePadControl control, int controllerIndex = 0)
177 | {
178 | EnsureInitialized();
179 | int i = controllerIndex;
180 | if (isPluggedIn[i])
181 | {
182 | var flagsToSet = Enum.GetValues(typeof(GamePadControl)).Cast().Where(c => c != GamePadControl.None && (control & c) != 0);
183 | foreach (var flag in flagsToSet)
184 | {
185 | if (flag <= GamePadControl.Y)
186 | {
187 | State[i].Buttons &= ~flag;
188 | }
189 | else
190 | {
191 | switch (flag)
192 | {
193 | case GamePadControl.LeftTrigger: State[i].LeftTrigger = 0; break;
194 | case GamePadControl.RightTrigger: State[i].RightTrigger = 0; break;
195 | case GamePadControl.LeftStickLeft: State[i].LeftStickX = 0; break;
196 | case GamePadControl.LeftStickRight: State[i].LeftStickX = 0; break;
197 | case GamePadControl.LeftStickUp: State[i].LeftStickY = 0; break;
198 | case GamePadControl.LeftStickDown: State[i].LeftStickY = 0; break;
199 | case GamePadControl.RightStickLeft: State[i].RightStickX = 0; break;
200 | case GamePadControl.RightStickRight: State[i].RightStickX = 0; break;
201 | case GamePadControl.RightStickUp: State[i].RightStickY = 0; break;
202 | case GamePadControl.RightStickDown: State[i].RightStickY = 0; break;
203 | }
204 | }
205 | }
206 |
207 | Update(controllerIndex);
208 | }
209 | }
210 |
211 | /// Instruct the underlying driver to adopt the current tracked state.
212 | /// You should call this at opportune moments if you are self-managing game pad state through the 'State' property.
213 | /// The index of the controller whose state we want to commit.
214 | public void Update(int controllerIndex = 0)
215 | {
216 | EnsureInitialized();
217 | int i = controllerIndex;
218 | if (isPluggedIn[i])
219 | {
220 | var report = State[i].GetReport();
221 | // ScpBus uses a 1-based index rather than 0-based index.
222 | bus.Report(i + 1, report);
223 | }
224 | }
225 |
226 | private void EnsureInitialized()
227 | {
228 | if (bus == null)
229 | {
230 | throw new InvalidOperationException("Must Initialize SimGamePad before first usage.");
231 | }
232 | }
233 | }
234 | }
--------------------------------------------------------------------------------
/SimWinGamePad/SimWinGamePad.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | AnyCPU
7 | {46541C61-9956-43C1-A30D-43574C4E48B2}
8 | Library
9 | Properties
10 | SimWinGamePad
11 | SimWinGamePad
12 | v4.8
13 | 512
14 |
15 |
16 |
17 | true
18 | full
19 | false
20 | bin\Debug\
21 | DEBUG;TRACE
22 | prompt
23 | 4
24 | AnyCPU
25 | false
26 |
27 |
28 | pdbonly
29 | true
30 | bin\Release\
31 | TRACE
32 | prompt
33 | 5
34 | AnyCPU
35 | false
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 | True
47 | True
48 | Resources.resx
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 | ResXFileCodeGenerator
63 | Resources.Designer.cs
64 |
65 |
66 |
67 |
68 |
69 | if $(ConfigurationName) == Release $(SolutionDir)packages\nuget.exe pack $(ProjectPath) -properties Configuration=Release
70 |
71 |
--------------------------------------------------------------------------------
/SimWinGamePad/SimulatedGamePadState.cs:
--------------------------------------------------------------------------------
1 | // This file is part of the SimWinGamePad project, which is released under MIT License.
2 | // For details, see: https://github.com/DavidRieman/SimWinInput
3 | //
4 | // For further details on the code this component was based on, see:
5 | // https://github.com/DavidRieman/ScpDriverInterface/ (at X360Controller.cs)
6 |
7 | namespace SimWinInput
8 | {
9 | /// A simulated Xbox 360 controller.
10 | /// After setting the desired values, use the GetReport() method to generate a controller report that can be used with the ScpBus Report() method.
11 | public class SimulatedGamePadState
12 | {
13 | /// Generates a new SimulatedGamePad object with the default initial state (no buttons pressed, all analog inputs 0).
14 | public SimulatedGamePadState() { }
15 |
16 | /// The controller's currently pressed buttons.
17 | /// These are flags. (GamePadControl.A | GamePadControl.X) would be mean both A and X are pressed. Not all values are applicable as buttons.
18 | public GamePadControl Buttons { get; set; }
19 |
20 | /// The controller's left trigger analog input. Value can range from 0 to 255.
21 | public byte LeftTrigger { get; set; }
22 |
23 | /// The controller's right trigger analog input. Value can range from 0 to 255.
24 | public byte RightTrigger { get; set; }
25 |
26 | /// The controller's left stick X-axis. Value can range from -32,768 to 32,767.
27 | public short LeftStickX { get; set; }
28 |
29 | /// The controller's left stick Y-axis. Value can range from -32,768 to 32,767.
30 | public short LeftStickY { get; set; }
31 |
32 | /// The controller's right stick X-axis. Value can range from -32,768 to 32,767.
33 | public short RightStickX { get; set; }
34 |
35 | /// The controller's right stick Y-axis. Value can range from -32,768 to 32,767.
36 | public short RightStickY { get; set; }
37 |
38 | /// Reset all values to their default zero states.
39 | public void Reset()
40 | {
41 | this.Buttons = GamePadControl.None;
42 | this.LeftStickX = 0;
43 | this.LeftStickY = 0;
44 | this.LeftTrigger = 0;
45 | this.RightStickX = 0;
46 | this.RightStickY = 0;
47 | this.RightTrigger = 0;
48 | }
49 |
50 | /// Generates an Xbox 360 controller report which can be used with the ScpBus Report() method.
51 | /// See http://free60.org/wiki/GamePad#Input_report for report details.
52 | /// A 20-byte Xbox 360 controller report.
53 | public byte[] GetReport()
54 | {
55 | byte[] bytes = new byte[20];
56 |
57 | bytes[0] = 0x00; // Message type (input report)
58 | bytes[1] = 0x14; // Message size (20 bytes)
59 |
60 | bytes[2] = (byte)((ushort)Buttons & 0xFF); // Buttons low
61 | bytes[3] = (byte)((ushort)Buttons >> 8 & 0xFF); // Buttons high
62 |
63 | bytes[4] = LeftTrigger; // Left trigger
64 | bytes[5] = RightTrigger; // Right trigger
65 |
66 | bytes[6] = (byte)(LeftStickX & 0xFF); // Left stick X-axis low
67 | bytes[7] = (byte)(LeftStickX >> 8 & 0xFF); // Left stick X-axis high
68 | bytes[8] = (byte)(LeftStickY & 0xFF); // Left stick Y-axis low
69 | bytes[9] = (byte)(LeftStickY >> 8 & 0xFF); // Left stick Y-axis high
70 |
71 | bytes[10] = (byte)(RightStickX & 0xFF); // Right stick X-axis low
72 | bytes[11] = (byte)(RightStickX >> 8 & 0xFF); // Right stick X-axis high
73 | bytes[12] = (byte)(RightStickY & 0xFF); // Right stick Y-axis low
74 | bytes[13] = (byte)(RightStickY >> 8 & 0xFF); // Right stick Y-axis high
75 |
76 | // Remaining bytes are unused
77 |
78 | return bytes;
79 | }
80 | }
81 | }
--------------------------------------------------------------------------------
/SimWinGamePad/UserDeclinedDriverException.cs:
--------------------------------------------------------------------------------
1 | // This file is part of the SimWinGamePad project, which is released under MIT License.
2 | // For details, see: https://github.com/DavidRieman/SimWinInput
3 |
4 | namespace SimWinInput
5 | {
6 | using System;
7 |
8 | public class UserDeclinedDriverException : Exception
9 | {
10 | public UserDeclinedDriverException(string driverName) : base("User declined to install the " + driverName + " driver.")
11 | { }
12 | }
13 | }
--------------------------------------------------------------------------------
/SimWinGamePad/XnaInputToScpBusReport.cs:
--------------------------------------------------------------------------------
1 | // This file is part of the SimWinGamePad project, which is released under MIT License.
2 | // For details, see: https://github.com/DavidRieman/SimWinInput
3 |
4 | namespace SimWinInput
5 | {
6 | /* TODO: Dynamic to drop MonoGame.Framework hard dependency? Else move to separate small library when needed? Also, finish testing.
7 | using Microsoft.Xna.Framework.Input;
8 |
9 | public class XnaInputToScpBusReport
10 | {
11 | /// Generate an ScpBus-compatible "report" from a captured GamePadState.
12 | /// This can be used, for example, to mirror real GamePad input read through XNA/MonoGame from one controller to additional simulated ports.
13 | /// The game pad state.
14 | /// An ScpBus-compatible "report" (see http://free60.org/wiki/GamePad#Input_report for details).
15 | public static byte[] Generate(GamePadState gamePad)
16 | {
17 | byte[] bytes = new byte[20];
18 |
19 | bytes[0] = 0x00; // Message type (input report)
20 | bytes[1] = 0x14; // Message size (20 bytes)
21 |
22 | bytes[2] = GetButtonsLow(gamePad);
23 | bytes[3] = GetButtonsHigh(gamePad);
24 |
25 | bytes[4] = ScalePositiveNormalizedFloatToByte(gamePad.Triggers.Left);
26 | bytes[5] = ScalePositiveNormalizedFloatToByte(gamePad.Triggers.Right);
27 |
28 | short leftStickX = ScaleNormalizedFloatToShort(gamePad.ThumbSticks.Left.X);
29 | short leftStickY = ScaleNormalizedFloatToShort(gamePad.ThumbSticks.Left.Y);
30 | bytes[6] = (byte)(leftStickX & 0xFF); // Left stick X-axis low
31 | bytes[7] = (byte)(leftStickX >> 8 & 0xFF); // Left stick X-axis high
32 | bytes[8] = (byte)(leftStickY & 0xFF); // Left stick Y-axis low
33 | bytes[9] = (byte)(leftStickY >> 8 & 0xFF); // Left stick Y-axis high
34 |
35 | short rightStickX = ScaleNormalizedFloatToShort(gamePad.ThumbSticks.Right.X);
36 | short rightStickY = ScaleNormalizedFloatToShort(gamePad.ThumbSticks.Right.Y);
37 | bytes[10] = (byte)(rightStickX & 0xFF); // Right stick X-axis low
38 | bytes[11] = (byte)(rightStickX >> 8 & 0xFF); // Right stick X-axis high
39 | bytes[12] = (byte)(rightStickY & 0xFF); // Right stick Y-axis low
40 | bytes[13] = (byte)(rightStickY >> 8 & 0xFF); // Right stick Y-axis high
41 |
42 | // Remaining bytes are unused
43 |
44 | return bytes;
45 | }
46 |
47 | private static byte ScalePositiveNormalizedFloatToByte(float f)
48 | {
49 | return (byte)(f >= 1.0 ? 255 : f <= 0.0 ? 0 : f * 256);
50 | }
51 |
52 | private static short ScaleNormalizedFloatToShort(float f)
53 | {
54 | return f <= -1 ? short.MinValue : f >= 1 ? short.MaxValue : (short)(f * short.MaxValue);
55 | }
56 |
57 | private static byte GetButtonsLow(GamePadState gamePad)
58 | {
59 | byte b = 0;
60 | b |= gamePad.DPad.Up == ButtonState.Pressed ? (byte)GamePadControl.DPadUp : (byte)0;
61 | b |= gamePad.DPad.Down == ButtonState.Pressed ? (byte)GamePadControl.DPadDown : (byte)0;
62 | b |= gamePad.DPad.Left == ButtonState.Pressed ? (byte)GamePadControl.DPadLeft : (byte)0;
63 | b |= gamePad.DPad.Right == ButtonState.Pressed ? (byte)GamePadControl.DPadRight : (byte)0;
64 | b |= gamePad.Buttons.Start == ButtonState.Pressed ? (byte)GamePadControl.Start : (byte)0;
65 | b |= gamePad.Buttons.Back == ButtonState.Pressed ? (byte)GamePadControl.Back : (byte)0;
66 | b |= gamePad.Buttons.LeftStick == ButtonState.Pressed ? (byte)GamePadControl.LeftStickClick : (byte)0;
67 | b |= gamePad.Buttons.RightStick == ButtonState.Pressed ? (byte)GamePadControl.RightStickClick : (byte)0;
68 | return b;
69 | }
70 |
71 | private static byte GetButtonsHigh(GamePadState gamePad)
72 | {
73 | byte b = 0;
74 | b |= gamePad.Buttons.LeftShoulder == ButtonState.Pressed ? (byte)((int)GamePadControl.LeftShoulder >> 8) : (byte)0;
75 | b |= gamePad.Buttons.RightShoulder == ButtonState.Pressed ? (byte)((int)GamePadControl.RightShoulder >> 8) : (byte)0;
76 | // Can't read Guide button with public GamePadState; skipping GamePadControl.Guide bit.
77 | b |= gamePad.Buttons.A == ButtonState.Pressed ? (byte)((int)GamePadControl.A >> 8) : (byte)0;
78 | b |= gamePad.Buttons.B == ButtonState.Pressed ? (byte)((int)GamePadControl.B >> 8) : (byte)0;
79 | b |= gamePad.Buttons.X == ButtonState.Pressed ? (byte)((int)GamePadControl.X >> 8) : (byte)0;
80 | b |= gamePad.Buttons.Y == ButtonState.Pressed ? (byte)((int)GamePadControl.Y >> 8) : (byte)0;
81 | return b;
82 | }
83 | }*/
84 | }
--------------------------------------------------------------------------------
/SimWinInput.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio 15
4 | VisualStudioVersion = 15.0.28010.2048
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SimWinMouse", "SimWinMouse\SimWinMouse.csproj", "{BEAE9C9A-3D3F-44EE-8568-C3FAA3CD6C9E}"
7 | EndProject
8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SimWinKeyboard", "SimWinKeyboard\SimWinKeyboard.csproj", "{51110AB1-5E63-472F-A4B0-DED9E0DA4547}"
9 | EndProject
10 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SimWinGamePad", "SimWinGamePad\SimWinGamePad.csproj", "{46541C61-9956-43C1-A30D-43574C4E48B2}"
11 | EndProject
12 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SimExamples", "SimExamples\SimExamples.csproj", "{0B40A9B2-C350-46E1-B885-EAFC74CC9129}"
13 | EndProject
14 | Global
15 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
16 | Debug|Any CPU = Debug|Any CPU
17 | Release|Any CPU = Release|Any CPU
18 | EndGlobalSection
19 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
20 | {BEAE9C9A-3D3F-44EE-8568-C3FAA3CD6C9E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
21 | {BEAE9C9A-3D3F-44EE-8568-C3FAA3CD6C9E}.Debug|Any CPU.Build.0 = Debug|Any CPU
22 | {BEAE9C9A-3D3F-44EE-8568-C3FAA3CD6C9E}.Release|Any CPU.ActiveCfg = Release|Any CPU
23 | {BEAE9C9A-3D3F-44EE-8568-C3FAA3CD6C9E}.Release|Any CPU.Build.0 = Release|Any CPU
24 | {51110AB1-5E63-472F-A4B0-DED9E0DA4547}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
25 | {51110AB1-5E63-472F-A4B0-DED9E0DA4547}.Debug|Any CPU.Build.0 = Debug|Any CPU
26 | {51110AB1-5E63-472F-A4B0-DED9E0DA4547}.Release|Any CPU.ActiveCfg = Release|Any CPU
27 | {51110AB1-5E63-472F-A4B0-DED9E0DA4547}.Release|Any CPU.Build.0 = Release|Any CPU
28 | {46541C61-9956-43C1-A30D-43574C4E48B2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
29 | {46541C61-9956-43C1-A30D-43574C4E48B2}.Debug|Any CPU.Build.0 = Debug|Any CPU
30 | {46541C61-9956-43C1-A30D-43574C4E48B2}.Release|Any CPU.ActiveCfg = Release|Any CPU
31 | {46541C61-9956-43C1-A30D-43574C4E48B2}.Release|Any CPU.Build.0 = Release|Any CPU
32 | {0B40A9B2-C350-46E1-B885-EAFC74CC9129}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
33 | {0B40A9B2-C350-46E1-B885-EAFC74CC9129}.Debug|Any CPU.Build.0 = Debug|Any CPU
34 | {0B40A9B2-C350-46E1-B885-EAFC74CC9129}.Release|Any CPU.ActiveCfg = Release|Any CPU
35 | {0B40A9B2-C350-46E1-B885-EAFC74CC9129}.Release|Any CPU.Build.0 = Release|Any CPU
36 | EndGlobalSection
37 | GlobalSection(SolutionProperties) = preSolution
38 | HideSolutionNode = FALSE
39 | EndGlobalSection
40 | GlobalSection(ExtensibilityGlobals) = postSolution
41 | SolutionGuid = {D135FC13-5C7C-4D3E-9081-025ACC71E69D}
42 | EndGlobalSection
43 | EndGlobal
44 |
--------------------------------------------------------------------------------
/SimWinKeyboard/InteropKeyboard.cs:
--------------------------------------------------------------------------------
1 | // This file is part of the SimWinMouse project, which is released under MIT License.
2 | // For details, see: https://github.com/DavidRieman/SimWinInput
3 |
4 | namespace SimWinInput
5 | {
6 | using System;
7 | using System.Runtime.InteropServices;
8 |
9 | public class InteropKeyboard
10 | {
11 | /// Keyboard key actions.
12 | /// These values should match those expected by the keybd_event native method.
13 | [Flags]
14 | public enum KeyboardEventFlags : uint
15 | {
16 | /// The keyboard key is in a 'down' or 'pressed' state.
17 | KeyDown = 0x0,
18 |
19 | /// The keyboard key is an 'extended' key (KEYEVENTF_EXTENDEDKEY).
20 | ///
21 | KeyExtended = 0x1,
22 |
23 | /// The keyboard key is in an 'up' or 'unpressed' state (KEYEVENTF_KEYUP).
24 | KeyUp = 0x2,
25 | }
26 |
27 | /// Simulate a keyboard keystroke event.
28 | /// https://msdn.microsoft.com/en-us/library/windows/desktop/ms646304(v=vs.85).aspx
29 | [DllImport("user32.dll")]
30 | public static extern void keybd_event(byte bVk, byte bScan, uint dwFlags, int dwExtraInfo);
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/SimWinKeyboard/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | // This file is part of the SimWinMouse project, which is released under MIT License.
2 | // For details, see: https://github.com/DavidRieman/SimWinInput
3 |
4 | using System.Reflection;
5 | using System.Runtime.InteropServices;
6 |
7 | [assembly: AssemblyTitle("SimWinKeyboard")]
8 | [assembly: AssemblyDescription("Simulate Windows keyboard events with .NET")]
9 | [assembly: AssemblyConfiguration("")]
10 | [assembly: AssemblyCompany("David Rieman")]
11 | [assembly: AssemblyProduct("SimWinKeyboard")]
12 | [assembly: AssemblyCopyright("Copyright © 2017 David Rieman")]
13 | [assembly: AssemblyTrademark("")]
14 | [assembly: AssemblyCulture("")]
15 |
16 | // Setting ComVisible to false makes the types in this assembly not visible
17 | // to COM components. If you need to access a type in this assembly from
18 | // COM, set the ComVisible attribute to true on that type.
19 | [assembly: ComVisible(false)]
20 |
21 | // The following GUID is for the ID of the typelib if this project is exposed to COM
22 | [assembly: Guid("51110ab1-5e63-472f-a4b0-ded9e0da4547")]
23 |
24 | [assembly: AssemblyVersion("1.0.3")]
25 | [assembly: AssemblyFileVersion("1.0.3")]
26 |
--------------------------------------------------------------------------------
/SimWinKeyboard/Properties/SimWinKeyboard.nuspec:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | $author$
5 | $copyright$
6 | $description$
7 | $id$
8 | MIT
9 | $author$
10 | https://github.com/DavidRieman/SimWinInput
11 | Initial release.
12 | false
13 | keyboard events simulate emulate keystroke keydown keyup Windows
14 | $title$
15 | $version$
16 |
17 |
--------------------------------------------------------------------------------
/SimWinKeyboard/SimKeyboard.cs:
--------------------------------------------------------------------------------
1 | // This file is part of the SimWinMouse project, which is released under MIT License.
2 | // For details, see: https://github.com/DavidRieman/SimWinInput
3 |
4 | namespace SimWinInput
5 | {
6 | using System.Threading;
7 |
8 | /// Simulate keyboard events, such as pressing a key.
9 | public class SimKeyboard
10 | {
11 | /// Simulates a keyboard key in the 'pressed' state.
12 | /// The key code to simulate.
13 | ///
14 | /// This method is non-blocking; a key set to KeyDown state will remain so until a KeyUp state is set
15 | /// (either through another call to a key simulation method, or real key state changes occur).
16 | ///
17 | public static void KeyDown(byte keyCode)
18 | {
19 | InteropKeyboard.keybd_event(keyCode, 0, (uint)InteropKeyboard.KeyboardEventFlags.KeyDown, 0);
20 | }
21 |
22 | /// Simulates a keyboard key in the 'unpressed' state.
23 | /// The key code to simulate.
24 | ///
25 | /// This method is non-blocking; a key set to KeyDown state will remain so until a KeyUp state is set
26 | /// (either through another call to a key simulation method, or real key state changes occur).
27 | ///
28 | public static void KeyUp(byte keyCode)
29 | {
30 | InteropKeyboard.keybd_event(keyCode, 0, (uint)InteropKeyboard.KeyboardEventFlags.KeyUp, 0);
31 | }
32 |
33 | /// Simulates a keyboard keystroke (press and release).
34 | /// The key code to simulate.
35 | /// The time, in milliseconds, to simulate holding the key down.
36 | /// This method is thread-blocking for the duration of the simulated key press.
37 | public static void Press(byte keyCode, int holdKeyTime = 10)
38 | {
39 | KeyDown(keyCode);
40 | Thread.Sleep(holdKeyTime);
41 | KeyUp(keyCode);
42 | }
43 | }
44 | }
--------------------------------------------------------------------------------
/SimWinKeyboard/SimWinKeyboard.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | AnyCPU
7 | {51110AB1-5E63-472F-A4B0-DED9E0DA4547}
8 | Library
9 | Properties
10 | SimWinKeyboard
11 | SimWinKeyboard
12 | v2.0
13 | 512
14 |
15 |
16 |
17 | true
18 | full
19 | false
20 | bin\Debug\
21 | DEBUG;TRACE
22 | prompt
23 | 4
24 | AnyCPU
25 |
26 |
27 | pdbonly
28 | true
29 | bin\Release\
30 | TRACE
31 | prompt
32 | 4
33 | AnyCPU
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 | if $(ConfigurationName) == Release $(SolutionDir)packages\nuget.exe pack $(ProjectPath) -properties Configuration=Release
49 |
50 |
--------------------------------------------------------------------------------
/SimWinMouse/InteropMouse.cs:
--------------------------------------------------------------------------------
1 | // This file is part of the SimWinMouse project, which is released under MIT License.
2 | // For details, see: https://github.com/DavidRieman/SimWinInput
3 |
4 | namespace SimWinInput
5 | {
6 | using System;
7 | using System.Runtime.InteropServices;
8 |
9 | /// Exposes relevant mouse methods and data from DLLs.
10 | public static class InteropMouse
11 | {
12 | /// Flags for mouse_event.
13 | /// https://msdn.microsoft.com/en-us/library/windows/desktop/ms646260(v=vs.85).aspx
14 | [Flags]
15 | public enum MouseEventFlags : uint
16 | {
17 | Movement = 0x0001,
18 | LeftDown = 0x0002,
19 | LeftUp = 0x0004,
20 | RightDown = 0x0008,
21 | RightUp = 0x0010,
22 | MiddleDown = 0x0020,
23 | MiddleUp = 0x0040,
24 | XDown = 0x0080,
25 | XUp = 0x0100,
26 | Wheel = 0x0800,
27 | WheelTilt = 0x1000,
28 | Absolute = 0x8000,
29 | }
30 |
31 | /// Simulate a mouse event with the system.
32 | /// https://msdn.microsoft.com/en-us/library/windows/desktop/ms646260(v=vs.85).aspx
33 | [DllImport("user32.dll")]
34 | public static extern void mouse_event(uint flags, int x, int y, int data, int extraInfo);
35 | }
36 | }
--------------------------------------------------------------------------------
/SimWinMouse/InteropScreen.cs:
--------------------------------------------------------------------------------
1 | // This file is part of the SimWinMouse project, which is released under MIT License.
2 | // For details, see: https://github.com/DavidRieman/SimWinInput
3 |
4 | namespace SimWinInput
5 | {
6 | using System;
7 | using System.Runtime.InteropServices;
8 |
9 | public class InteropScreen
10 | {
11 | /// Options for GetDeviceCaps. There are many more; this is all we need for SimWinMouse though.
12 | internal enum DeviceCaps : int
13 | {
14 | HORZSIZE = 8,
15 | VERTSIZE = 10,
16 | }
17 |
18 | /// Retrieve device-specific information.
19 | /// https://msdn.microsoft.com/en-us/library/windows/desktop/dd144877(v=vs.85).aspx
20 | [DllImport("gdi32.dll")]
21 | public static extern int GetDeviceCaps(IntPtr windowHandle, int caps);
22 |
23 | /// Retrieve a handle to a device context.
24 | /// https://msdn.microsoft.com/en-us/library/windows/desktop/dd144871(v=vs.85).aspx
25 | [DllImport("User32.dll")]
26 | public extern static System.IntPtr GetDC(IntPtr windowHandle);
27 |
28 | /// Release a handle to a device context.
29 | /// https://msdn.microsoft.com/en-us/library/windows/desktop/dd162920(v=vs.85).aspx
30 | [DllImport("User32.dll")]
31 | public extern static int ReleaseDC(IntPtr windowHandle, IntPtr deviceContextHandle);
32 | }
33 | }
--------------------------------------------------------------------------------
/SimWinMouse/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | // This file is part of the SimWinMouse project, which is released under MIT License.
2 | // For details, see: https://github.com/DavidRieman/SimWinInput
3 |
4 | using System.Reflection;
5 | using System.Runtime.InteropServices;
6 |
7 | [assembly: AssemblyTitle("SimWinMouse")]
8 | [assembly: AssemblyDescription("Simulate Windows mouse events with .NET")]
9 | [assembly: AssemblyConfiguration("")]
10 | [assembly: AssemblyCompany("David Rieman")]
11 | [assembly: AssemblyProduct("SimWinMouse")]
12 | [assembly: AssemblyCopyright("Copyright © 2017 David Rieman")]
13 | [assembly: AssemblyTrademark("")]
14 | [assembly: AssemblyCulture("")]
15 |
16 | // Setting ComVisible to false makes the types in this assembly not visible
17 | // to COM components. If you need to access a type in this assembly from
18 | // COM, set the ComVisible attribute to true on that type.
19 | [assembly: ComVisible(false)]
20 |
21 | // The following GUID is for the ID of the typelib if this project is exposed to COM
22 | [assembly: Guid("beae9c9a-3d3f-44ee-8568-c3faa3cd6c9e")]
23 |
24 | [assembly: AssemblyVersion("1.0.3")]
25 | [assembly: AssemblyFileVersion("1.0.3")]
26 |
--------------------------------------------------------------------------------
/SimWinMouse/Properties/SimWinMouse.nuspec:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | $author$
5 | $copyright$
6 | $description$
7 | $id$
8 | MIT
9 | $author$
10 | https://github.com/DavidRieman/SimWinInput
11 | Initial release.
12 | false
13 | mouse events simulate emulate cursor click movement Windows
14 | $title$
15 | $version$
16 |
17 |
--------------------------------------------------------------------------------
/SimWinMouse/ScreenSize.cs:
--------------------------------------------------------------------------------
1 | // This file is part of the SimWinMouse project, which is released under MIT License.
2 | // For details, see: https://github.com/DavidRieman/SimWinInput
3 |
4 | namespace SimWinInput
5 | {
6 | using System;
7 |
8 | /// Methods for gathering screen size information.
9 | public class ScreenSize
10 | {
11 | /// Gets the width of the physical screen.
12 | static public int Width()
13 | {
14 | IntPtr globalDeviceContext = InteropScreen.GetDC(IntPtr.Zero);
15 | int i = InteropScreen.GetDeviceCaps(globalDeviceContext, (int)InteropScreen.DeviceCaps.HORZSIZE);
16 | InteropScreen.ReleaseDC(IntPtr.Zero, globalDeviceContext);
17 | return i;
18 | }
19 |
20 | /// Gets the height of the physical screen.
21 | static public int Height()
22 | {
23 | IntPtr globalDeviceContext = InteropScreen.GetDC(IntPtr.Zero);
24 | int i = InteropScreen.GetDeviceCaps(globalDeviceContext, (int)InteropScreen.DeviceCaps.VERTSIZE);
25 | InteropScreen.ReleaseDC(IntPtr.Zero, globalDeviceContext);
26 | return i;
27 | }
28 | }
29 | }
--------------------------------------------------------------------------------
/SimWinMouse/SimMouse.cs:
--------------------------------------------------------------------------------
1 | // This file is part of the SimWinMouse project, which is released under MIT License.
2 | // For details, see: https://github.com/DavidRieman/SimWinInput
3 |
4 | namespace SimWinInput
5 | {
6 | using Microsoft.Win32;
7 | using System;
8 | using System.Threading;
9 | using System.Windows.Forms;
10 |
11 | /// Simulate mouse events, such as moving the mouse cursor and clicking.
12 | public static class SimMouse
13 | {
14 | private const InteropMouse.MouseEventFlags AbsoluteMovement = InteropMouse.MouseEventFlags.Absolute | InteropMouse.MouseEventFlags.Movement;
15 |
16 | // Cache screen width/height as needed for mouse_event, but update them upon applicable display settings changes.
17 | private static int ScreenWidth, ScreenHeight;
18 |
19 | static SimMouse()
20 | {
21 | RecalculateScreen();
22 | SystemEvents.DisplaySettingsChanged += (sender, eventArgs) => RecalculateScreen();
23 | }
24 |
25 | /// Mouse input simulation options.
26 | ///
27 | /// This is a subset of MouseEventFlags pairings intended for easy usage with the Simulate method.
28 | /// If you need finer control, consider using Interop.mouse_event directly instead.
29 | ///
30 | public enum Action : uint
31 | {
32 | MoveOnly = AbsoluteMovement,
33 | LeftButtonDown = AbsoluteMovement | InteropMouse.MouseEventFlags.LeftDown,
34 | LeftButtonUp = AbsoluteMovement | InteropMouse.MouseEventFlags.LeftUp,
35 | MiddleButtonDown = AbsoluteMovement | InteropMouse.MouseEventFlags.MiddleDown,
36 | MiddleButtonUp = AbsoluteMovement | InteropMouse.MouseEventFlags.MiddleUp,
37 | RightButtonDown = AbsoluteMovement | InteropMouse.MouseEventFlags.RightDown,
38 | RightButtonUp = AbsoluteMovement | InteropMouse.MouseEventFlags.RightUp,
39 | }
40 |
41 | /// Simulates the specified mouse action.
42 | /// The mouse state to simulate.
43 | /// The horizontal pixel coordinate to place the mouse cursor at.
44 | /// The vertical pixel coordinate to place the mouse cursor at.
45 | public static void Act(Action mouseOption, int x, int y)
46 | {
47 | double absX = 65535.0 * (x + 1) / ScreenWidth;
48 | double absY = 65535.0 * (y + 1) / ScreenHeight;
49 | InteropMouse.mouse_event((uint)mouseOption, (int)absX, (int)absY, 0, 0);
50 | }
51 |
52 | // TODO: Simulation of mouse scroll wheel movements. Something like:
53 | //public void SimulateScroll(scrollAmount, int x, int y)
54 | //public void SimulateScroll(scrollAmount)
55 |
56 | // TODO: Simulation of click events which omit movement, to click wherever the cursor already is.
57 | //public static void SimulateClick(MouseButtons mouseButton, int holdClickTime = 10)
58 |
59 | /// Simulates a mouse click at the specified location.
60 | /// Which mouse button to simulate a click for.
61 | /// The horizontal pixel coordinate to place the mouse cursor at.
62 | /// The vertical pixel coordinate to place the mouse cursor at.
63 | /// How long to simulate holding the mouse button down, in milliseconds.
64 | ///
65 | /// Basically this is just a pair of simulated mouse "down" and mouse "up" simulations in sequence.
66 | /// This method is thread-blocking for the duration of the simulated button press.
67 | ///
68 | public static void Click(MouseButtons mouseButton, int x, int y, int holdClickTime = 10)
69 | {
70 | Action mouseDownOption, mouseUpOption;
71 | switch (mouseButton)
72 | {
73 | case MouseButtons.Left:
74 | mouseDownOption = Action.LeftButtonDown;
75 | mouseUpOption = Action.LeftButtonUp;
76 | break;
77 | case MouseButtons.Middle:
78 | mouseDownOption = Action.MiddleButtonDown;
79 | mouseUpOption = Action.MiddleButtonUp;
80 | break;
81 | case MouseButtons.Right:
82 | mouseDownOption = Action.RightButtonDown;
83 | mouseUpOption = Action.RightButtonUp;
84 | break;
85 | default:
86 | // TODO: Implement and test XButton1 and XButton2 with a fancy mouse. (Requires extra data.)
87 | throw new NotSupportedException("The selected mouse button is not yet supported: " + mouseButton.ToString());
88 | }
89 |
90 | Act(mouseDownOption, x, y);
91 | Thread.Sleep(holdClickTime);
92 | Act(mouseUpOption, x, y);
93 | }
94 |
95 | private static void RecalculateScreen()
96 | {
97 | ScreenWidth = ScreenSize.Width();
98 | ScreenHeight = ScreenSize.Height();
99 | }
100 | }
101 | }
--------------------------------------------------------------------------------
/SimWinMouse/SimWinMouse.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | AnyCPU
7 | {BEAE9C9A-3D3F-44EE-8568-C3FAA3CD6C9E}
8 | Library
9 | Properties
10 | SimWinMouse
11 | SimWinMouse
12 | v2.0
13 | 512
14 |
15 |
16 |
17 | true
18 | full
19 | false
20 | bin\Debug\
21 | DEBUG;TRACE
22 | prompt
23 | 4
24 | AnyCPU
25 |
26 |
27 | pdbonly
28 | true
29 | bin\Release\
30 | TRACE
31 | prompt
32 | 4
33 | AnyCPU
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 | if $(ConfigurationName) == Release $(SolutionDir)packages\nuget.exe pack $(ProjectPath) -properties Configuration=Release
52 |
53 |
--------------------------------------------------------------------------------
/packages/nuget.exe:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DavidRieman/SimWinInput/21a635441866a9f9c4e4659a8e7b4cd6e80db0d5/packages/nuget.exe
--------------------------------------------------------------------------------