├── .github ├── dependabot.yml └── workflows │ └── release.yml ├── .gitignore ├── LICENSE ├── MouseTester ├── MouseTester.sln └── MouseTester │ ├── FodyWeavers.xml │ ├── FodyWeavers.xsd │ ├── Form1.Designer.cs │ ├── Form1.cs │ ├── Form1.resx │ ├── GraphicsRenderContext.cs │ ├── MouseEvent.cs │ ├── MouseLog.cs │ ├── MousePlot.Designer.cs │ ├── MousePlot.cs │ ├── MousePlot.resx │ ├── MouseTester.csproj │ ├── OxyPlot.WindowsForms.dll │ ├── OxyPlot.dll │ ├── Program.cs │ ├── Properties │ ├── AssemblyInfo.cs │ ├── Resources.Designer.cs │ ├── Resources.resx │ ├── Settings.Designer.cs │ ├── Settings.settings │ └── app.manifest │ ├── RawMouse.cs │ ├── app.config │ └── packages.config └── README.md /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | 3 | updates: 4 | - package-ecosystem: "github-actions" 5 | directory: "/" 6 | schedule: 7 | interval: "weekly" 8 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Create release 2 | run-name: Create release 3 | 4 | on: 5 | workflow_dispatch: 6 | inputs: 7 | version: 8 | description: "Version of the program to build. This will be used for the tag and release name." 9 | required: true 10 | 11 | pre-release: 12 | description: "Pre-release" 13 | required: true 14 | default: false 15 | type: boolean 16 | 17 | permissions: 18 | contents: write 19 | 20 | jobs: 21 | build: 22 | runs-on: windows-latest 23 | 24 | steps: 25 | - name: Checkout repository 26 | uses: actions/checkout@v4 27 | 28 | - name: Set up MSVC environment 29 | uses: microsoft/setup-msbuild@v2 30 | 31 | - name: Install packages 32 | run: nuget restore .\MouseTester\MouseTester.sln 33 | 34 | - name: Build executable 35 | run: | 36 | MSBuild.exe .\MouseTester\MouseTester.sln -p:Configuration=Release -p:Platform=x64 37 | 38 | - name: Create release 39 | uses: ncipollo/release-action@v1 40 | with: 41 | tag: MouseTester_v${{ inputs.version }} 42 | name: MouseTester_v${{ inputs.version }} 43 | prerelease: ${{ inputs.pre-release }} 44 | artifacts: ./MouseTester/MouseTester/bin/x64/Release/MouseTester.exe 45 | generateReleaseNotes: true 46 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Build Folders (you can keep bin if you'd like, to store dlls and pdbs) 2 | [Bb]in/ 3 | [Oo]bj/ 4 | 5 | # mstest test results 6 | TestResults 7 | 8 | ## Ignore Visual Studio temporary files, build results, and 9 | ## files generated by popular Visual Studio add-ons. 10 | 11 | # User-specific files 12 | *.suo 13 | *.user 14 | *.sln.docstates 15 | 16 | # Build results 17 | [Dd]ebug/ 18 | [Rr]elease/ 19 | x64/ 20 | *_i.c 21 | *_p.c 22 | *.ilk 23 | *.meta 24 | *.obj 25 | *.pch 26 | *.pdb 27 | *.pgc 28 | *.pgd 29 | *.rsp 30 | *.sbr 31 | *.tlb 32 | *.tli 33 | *.tlh 34 | *.tmp 35 | *.log 36 | *.vspscc 37 | *.vssscc 38 | .builds 39 | 40 | # Visual C++ cache files 41 | ipch/ 42 | *.aps 43 | *.ncb 44 | *.opensdf 45 | *.sdf 46 | 47 | # Visual Studio profiler 48 | *.psess 49 | *.vsp 50 | *.vspx 51 | 52 | # Guidance Automation Toolkit 53 | *.gpState 54 | 55 | # ReSharper is a .NET coding add-in 56 | _ReSharper* 57 | 58 | # NCrunch 59 | *.ncrunch* 60 | .*crunch*.local.xml 61 | 62 | # Installshield output folder 63 | [Ee]xpress 64 | 65 | # DocProject is a documentation generator add-in 66 | DocProject/buildhelp/ 67 | DocProject/Help/*.HxT 68 | DocProject/Help/*.HxC 69 | DocProject/Help/*.hhc 70 | DocProject/Help/*.hhk 71 | DocProject/Help/*.hhp 72 | DocProject/Help/Html2 73 | DocProject/Help/html 74 | 75 | # Click-Once directory 76 | publish 77 | 78 | # Publish Web Output 79 | *.Publish.xml 80 | 81 | # NuGet Packages Directory 82 | packages 83 | 84 | # Windows Azure Build Output 85 | csx 86 | *.build.csdef 87 | 88 | # Windows Store app package directory 89 | AppPackages/ 90 | 91 | # Others 92 | [Bb]in 93 | [Oo]bj 94 | sql 95 | TestResults 96 | [Tt]est[Rr]esult* 97 | *.Cache 98 | ClientBin 99 | [Ss]tyle[Cc]op.* 100 | ~$* 101 | *.dbmdl 102 | Generated_Code #added for RIA/Silverlight projects 103 | 104 | # Backup & report files from converting an old project file to a newer 105 | # Visual Studio version. Backup files are not needed, because we have git ;-) 106 | _UpgradeReport_Files/ 107 | Backup*/ 108 | UpgradeLog*.XML 109 | /MouseTester/.vs 110 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013 microe1 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | 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, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /MouseTester/MouseTester.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.30717.126 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MouseTester", "MouseTester\MouseTester.csproj", "{B0953B29-AF9B-4C37-A2EC-947E98F683B3}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Debug|x64 = Debug|x64 12 | Debug|x86 = Debug|x86 13 | Release|Any CPU = Release|Any CPU 14 | Release|x64 = Release|x64 15 | Release|x86 = Release|x86 16 | EndGlobalSection 17 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 18 | {B0953B29-AF9B-4C37-A2EC-947E98F683B3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 19 | {B0953B29-AF9B-4C37-A2EC-947E98F683B3}.Debug|Any CPU.Build.0 = Debug|Any CPU 20 | {B0953B29-AF9B-4C37-A2EC-947E98F683B3}.Debug|x64.ActiveCfg = Debug|x64 21 | {B0953B29-AF9B-4C37-A2EC-947E98F683B3}.Debug|x64.Build.0 = Debug|x64 22 | {B0953B29-AF9B-4C37-A2EC-947E98F683B3}.Debug|x86.ActiveCfg = Debug|x86 23 | {B0953B29-AF9B-4C37-A2EC-947E98F683B3}.Debug|x86.Build.0 = Debug|x86 24 | {B0953B29-AF9B-4C37-A2EC-947E98F683B3}.Release|Any CPU.ActiveCfg = Release|Any CPU 25 | {B0953B29-AF9B-4C37-A2EC-947E98F683B3}.Release|Any CPU.Build.0 = Release|Any CPU 26 | {B0953B29-AF9B-4C37-A2EC-947E98F683B3}.Release|x64.ActiveCfg = Release|x64 27 | {B0953B29-AF9B-4C37-A2EC-947E98F683B3}.Release|x64.Build.0 = Release|x64 28 | {B0953B29-AF9B-4C37-A2EC-947E98F683B3}.Release|x86.ActiveCfg = Release|x86 29 | {B0953B29-AF9B-4C37-A2EC-947E98F683B3}.Release|x86.Build.0 = Release|x86 30 | EndGlobalSection 31 | GlobalSection(SolutionProperties) = preSolution 32 | HideSolutionNode = FALSE 33 | EndGlobalSection 34 | GlobalSection(ExtensibilityGlobals) = postSolution 35 | SolutionGuid = {FEE93D0A-FE0A-4153-BD9B-A612BE91F7FC} 36 | EndGlobalSection 37 | EndGlobal 38 | -------------------------------------------------------------------------------- /MouseTester/MouseTester/FodyWeavers.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | -------------------------------------------------------------------------------- /MouseTester/MouseTester/FodyWeavers.xsd: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | A list of assembly names to exclude from the default action of "embed all Copy Local references", delimited with line breaks 13 | 14 | 15 | 16 | 17 | A list of assembly names to include from the default action of "embed all Copy Local references", delimited with line breaks. 18 | 19 | 20 | 21 | 22 | A list of runtime assembly names to exclude from the default action of "embed all Copy Local references", delimited with line breaks 23 | 24 | 25 | 26 | 27 | A list of runtime assembly names to include from the default action of "embed all Copy Local references", delimited with line breaks. 28 | 29 | 30 | 31 | 32 | A list of unmanaged 32 bit assembly names to include, delimited with line breaks. 33 | 34 | 35 | 36 | 37 | A list of unmanaged 64 bit assembly names to include, delimited with line breaks. 38 | 39 | 40 | 41 | 42 | The order of preloaded assemblies, delimited with line breaks. 43 | 44 | 45 | 46 | 47 | 48 | This will copy embedded files to disk before loading them into memory. This is helpful for some scenarios that expected an assembly to be loaded from a physical file. 49 | 50 | 51 | 52 | 53 | Controls if .pdbs for reference assemblies are also embedded. 54 | 55 | 56 | 57 | 58 | Controls if runtime assemblies are also embedded. 59 | 60 | 61 | 62 | 63 | Controls whether the runtime assemblies are embedded with their full path or only with their assembly name. 64 | 65 | 66 | 67 | 68 | Embedded assemblies are compressed by default, and uncompressed when they are loaded. You can turn compression off with this option. 69 | 70 | 71 | 72 | 73 | As part of Costura, embedded assemblies are no longer included as part of the build. This cleanup can be turned off. 74 | 75 | 76 | 77 | 78 | Costura by default will load as part of the module initialization. This flag disables that behavior. Make sure you call CosturaUtility.Initialize() somewhere in your code. 79 | 80 | 81 | 82 | 83 | Costura will by default use assemblies with a name like 'resources.dll' as a satellite resource and prepend the output path. This flag disables that behavior. 84 | 85 | 86 | 87 | 88 | A list of assembly names to exclude from the default action of "embed all Copy Local references", delimited with | 89 | 90 | 91 | 92 | 93 | A list of assembly names to include from the default action of "embed all Copy Local references", delimited with |. 94 | 95 | 96 | 97 | 98 | A list of runtime assembly names to exclude from the default action of "embed all Copy Local references", delimited with | 99 | 100 | 101 | 102 | 103 | A list of runtime assembly names to include from the default action of "embed all Copy Local references", delimited with |. 104 | 105 | 106 | 107 | 108 | A list of unmanaged 32 bit assembly names to include, delimited with |. 109 | 110 | 111 | 112 | 113 | A list of unmanaged 64 bit assembly names to include, delimited with |. 114 | 115 | 116 | 117 | 118 | The order of preloaded assemblies, delimited with |. 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed. 127 | 128 | 129 | 130 | 131 | A comma-separated list of error codes that can be safely ignored in assembly verification. 132 | 133 | 134 | 135 | 136 | 'false' to turn off automatic generation of the XML Schema file. 137 | 138 | 139 | 140 | 141 | -------------------------------------------------------------------------------- /MouseTester/MouseTester/Form1.Designer.cs: -------------------------------------------------------------------------------- 1 | namespace MouseTester 2 | { 3 | partial class Form1 4 | { 5 | /// 6 | /// Required designer variable. 7 | /// 8 | private System.ComponentModel.IContainer components = null; 9 | 10 | /// 11 | /// Clean up any resources being used. 12 | /// 13 | /// true if managed resources should be disposed; otherwise, false. 14 | protected override void Dispose(bool disposing) 15 | { 16 | if (disposing && (components != null)) 17 | { 18 | components.Dispose(); 19 | } 20 | base.Dispose(disposing); 21 | } 22 | 23 | #region Windows Form Designer generated code 24 | 25 | /// 26 | /// Required method for Designer support - do not modify 27 | /// the contents of this method with the code editor. 28 | /// 29 | private void InitializeComponent() 30 | { 31 | this.groupBox1 = new System.Windows.Forms.GroupBox(); 32 | this.textBoxDesc = new System.Windows.Forms.TextBox(); 33 | this.textBoxCPI = new System.Windows.Forms.TextBox(); 34 | this.groupBox2 = new System.Windows.Forms.GroupBox(); 35 | this.label1 = new System.Windows.Forms.Label(); 36 | this.buttonMeasure = new System.Windows.Forms.Button(); 37 | this.groupBox3 = new System.Windows.Forms.GroupBox(); 38 | this.buttonSave = new System.Windows.Forms.Button(); 39 | this.buttonLoad = new System.Windows.Forms.Button(); 40 | this.buttonPlot = new System.Windows.Forms.Button(); 41 | this.groupBox4 = new System.Windows.Forms.GroupBox(); 42 | this.buttonLog = new System.Windows.Forms.Button(); 43 | this.buttonCollect = new System.Windows.Forms.Button(); 44 | this.textBox1 = new System.Windows.Forms.TextBox(); 45 | this.statusStrip1 = new System.Windows.Forms.StatusStrip(); 46 | this.toolStripStatusLabel1 = new System.Windows.Forms.ToolStripStatusLabel(); 47 | this.groupBox1.SuspendLayout(); 48 | this.groupBox2.SuspendLayout(); 49 | this.groupBox3.SuspendLayout(); 50 | this.groupBox4.SuspendLayout(); 51 | this.statusStrip1.SuspendLayout(); 52 | this.SuspendLayout(); 53 | // 54 | // groupBox1 55 | // 56 | this.groupBox1.Controls.Add(this.textBoxDesc); 57 | this.groupBox1.Location = new System.Drawing.Point(13, 13); 58 | this.groupBox1.Name = "groupBox1"; 59 | this.groupBox1.Size = new System.Drawing.Size(267, 50); 60 | this.groupBox1.TabIndex = 0; 61 | this.groupBox1.TabStop = false; 62 | this.groupBox1.Text = "Description"; 63 | // 64 | // textBoxDesc 65 | // 66 | this.textBoxDesc.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 67 | | System.Windows.Forms.AnchorStyles.Left) 68 | | System.Windows.Forms.AnchorStyles.Right))); 69 | this.textBoxDesc.Location = new System.Drawing.Point(6, 19); 70 | this.textBoxDesc.Name = "textBoxDesc"; 71 | this.textBoxDesc.Size = new System.Drawing.Size(250, 20); 72 | this.textBoxDesc.TabIndex = 0; 73 | // 74 | // textBoxCPI 75 | // 76 | this.textBoxCPI.Location = new System.Drawing.Point(88, 21); 77 | this.textBoxCPI.Name = "textBoxCPI"; 78 | this.textBoxCPI.Size = new System.Drawing.Size(100, 20); 79 | this.textBoxCPI.TabIndex = 0; 80 | this.textBoxCPI.Validated += new System.EventHandler(this.textBoxCPI_Validated); 81 | // 82 | // groupBox2 83 | // 84 | this.groupBox2.Controls.Add(this.label1); 85 | this.groupBox2.Controls.Add(this.buttonMeasure); 86 | this.groupBox2.Controls.Add(this.textBoxCPI); 87 | this.groupBox2.Location = new System.Drawing.Point(13, 69); 88 | this.groupBox2.Name = "groupBox2"; 89 | this.groupBox2.Size = new System.Drawing.Size(267, 50); 90 | this.groupBox2.TabIndex = 1; 91 | this.groupBox2.TabStop = false; 92 | this.groupBox2.Text = "Resolution"; 93 | // 94 | // label1 95 | // 96 | this.label1.AutoSize = true; 97 | this.label1.Location = new System.Drawing.Point(194, 24); 98 | this.label1.Name = "label1"; 99 | this.label1.Size = new System.Drawing.Size(21, 13); 100 | this.label1.TabIndex = 2; 101 | this.label1.Text = "cpi"; 102 | // 103 | // buttonMeasure 104 | // 105 | this.buttonMeasure.Location = new System.Drawing.Point(6, 19); 106 | this.buttonMeasure.Name = "buttonMeasure"; 107 | this.buttonMeasure.Size = new System.Drawing.Size(75, 23); 108 | this.buttonMeasure.TabIndex = 1; 109 | this.buttonMeasure.Text = "Measure"; 110 | this.buttonMeasure.UseVisualStyleBackColor = true; 111 | this.buttonMeasure.Click += new System.EventHandler(this.buttonMeasure_Click); 112 | // 113 | // groupBox3 114 | // 115 | this.groupBox3.Controls.Add(this.buttonSave); 116 | this.groupBox3.Controls.Add(this.buttonLoad); 117 | this.groupBox3.Location = new System.Drawing.Point(13, 182); 118 | this.groupBox3.Name = "groupBox3"; 119 | this.groupBox3.Size = new System.Drawing.Size(267, 50); 120 | this.groupBox3.TabIndex = 2; 121 | this.groupBox3.TabStop = false; 122 | this.groupBox3.Text = "LogFile"; 123 | // 124 | // buttonSave 125 | // 126 | this.buttonSave.Location = new System.Drawing.Point(88, 20); 127 | this.buttonSave.Name = "buttonSave"; 128 | this.buttonSave.Size = new System.Drawing.Size(75, 23); 129 | this.buttonSave.TabIndex = 1; 130 | this.buttonSave.Text = "Save"; 131 | this.buttonSave.UseVisualStyleBackColor = true; 132 | this.buttonSave.Click += new System.EventHandler(this.buttonSave_Click); 133 | // 134 | // buttonLoad 135 | // 136 | this.buttonLoad.Location = new System.Drawing.Point(7, 20); 137 | this.buttonLoad.Name = "buttonLoad"; 138 | this.buttonLoad.Size = new System.Drawing.Size(75, 23); 139 | this.buttonLoad.TabIndex = 0; 140 | this.buttonLoad.Text = "Load"; 141 | this.buttonLoad.UseVisualStyleBackColor = true; 142 | this.buttonLoad.Click += new System.EventHandler(this.buttonLoad_Click); 143 | // 144 | // buttonPlot 145 | // 146 | this.buttonPlot.Location = new System.Drawing.Point(168, 19); 147 | this.buttonPlot.Name = "buttonPlot"; 148 | this.buttonPlot.Size = new System.Drawing.Size(75, 23); 149 | this.buttonPlot.TabIndex = 2; 150 | this.buttonPlot.Text = "Plot (F3)"; 151 | this.buttonPlot.UseVisualStyleBackColor = true; 152 | this.buttonPlot.Click += new System.EventHandler(this.buttonPlot_Click); 153 | // 154 | // groupBox4 155 | // 156 | this.groupBox4.Controls.Add(this.buttonLog); 157 | this.groupBox4.Controls.Add(this.buttonCollect); 158 | this.groupBox4.Controls.Add(this.buttonPlot); 159 | this.groupBox4.Location = new System.Drawing.Point(13, 126); 160 | this.groupBox4.Name = "groupBox4"; 161 | this.groupBox4.Size = new System.Drawing.Size(267, 50); 162 | this.groupBox4.TabIndex = 3; 163 | this.groupBox4.TabStop = false; 164 | this.groupBox4.Text = "MouseData"; 165 | // 166 | // buttonLog 167 | // 168 | this.buttonLog.Location = new System.Drawing.Point(87, 19); 169 | this.buttonLog.Name = "buttonLog"; 170 | this.buttonLog.Size = new System.Drawing.Size(75, 23); 171 | this.buttonLog.TabIndex = 4; 172 | this.buttonLog.Text = "Start (F1)"; 173 | this.buttonLog.UseVisualStyleBackColor = true; 174 | this.buttonLog.Click += new System.EventHandler(this.buttonLog_Click); 175 | // 176 | // buttonCollect 177 | // 178 | this.buttonCollect.Location = new System.Drawing.Point(6, 19); 179 | this.buttonCollect.Name = "buttonCollect"; 180 | this.buttonCollect.Size = new System.Drawing.Size(75, 23); 181 | this.buttonCollect.TabIndex = 3; 182 | this.buttonCollect.Text = "Collect"; 183 | this.buttonCollect.UseVisualStyleBackColor = true; 184 | this.buttonCollect.Click += new System.EventHandler(this.buttonCollect_Click); 185 | // 186 | // textBox1 187 | // 188 | this.textBox1.Location = new System.Drawing.Point(13, 239); 189 | this.textBox1.Multiline = true; 190 | this.textBox1.Name = "textBox1"; 191 | this.textBox1.ReadOnly = true; 192 | this.textBox1.Size = new System.Drawing.Size(267, 100); 193 | this.textBox1.TabIndex = 4; 194 | // 195 | // statusStrip1 196 | // 197 | this.statusStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { 198 | this.toolStripStatusLabel1}); 199 | this.statusStrip1.Location = new System.Drawing.Point(0, 339); 200 | this.statusStrip1.Name = "statusStrip1"; 201 | this.statusStrip1.Size = new System.Drawing.Size(294, 22); 202 | this.statusStrip1.TabIndex = 5; 203 | this.statusStrip1.Text = "statusStrip1"; 204 | // 205 | // toolStripStatusLabel1 206 | // 207 | this.toolStripStatusLabel1.Name = "toolStripStatusLabel1"; 208 | this.toolStripStatusLabel1.Size = new System.Drawing.Size(118, 17); 209 | this.toolStripStatusLabel1.Text = "toolStripStatusLabel1"; 210 | // 211 | // Form1 212 | // 213 | this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); 214 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; 215 | this.ClientSize = new System.Drawing.Size(294, 361); 216 | this.Controls.Add(this.statusStrip1); 217 | this.Controls.Add(this.textBox1); 218 | this.Controls.Add(this.groupBox4); 219 | this.Controls.Add(this.groupBox3); 220 | this.Controls.Add(this.groupBox2); 221 | this.Controls.Add(this.groupBox1); 222 | this.MaximizeBox = false; 223 | this.MaximumSize = new System.Drawing.Size(310, 400); 224 | this.MinimumSize = new System.Drawing.Size(300, 400); 225 | this.Name = "Form1"; 226 | this.SizeGripStyle = System.Windows.Forms.SizeGripStyle.Hide; 227 | this.groupBox1.ResumeLayout(false); 228 | this.groupBox1.PerformLayout(); 229 | this.groupBox2.ResumeLayout(false); 230 | this.groupBox2.PerformLayout(); 231 | this.groupBox3.ResumeLayout(false); 232 | this.groupBox4.ResumeLayout(false); 233 | this.statusStrip1.ResumeLayout(false); 234 | this.statusStrip1.PerformLayout(); 235 | this.ResumeLayout(false); 236 | this.PerformLayout(); 237 | 238 | } 239 | 240 | #endregion 241 | 242 | private System.Windows.Forms.GroupBox groupBox1; 243 | private System.Windows.Forms.TextBox textBoxDesc; 244 | private System.Windows.Forms.TextBox textBoxCPI; 245 | private System.Windows.Forms.GroupBox groupBox2; 246 | private System.Windows.Forms.Button buttonMeasure; 247 | private System.Windows.Forms.GroupBox groupBox3; 248 | private System.Windows.Forms.Button buttonPlot; 249 | private System.Windows.Forms.Button buttonSave; 250 | private System.Windows.Forms.Button buttonLoad; 251 | private System.Windows.Forms.GroupBox groupBox4; 252 | private System.Windows.Forms.Button buttonCollect; 253 | private System.Windows.Forms.Label label1; 254 | private System.Windows.Forms.TextBox textBox1; 255 | private System.Windows.Forms.StatusStrip statusStrip1; 256 | private System.Windows.Forms.ToolStripStatusLabel toolStripStatusLabel1; 257 | private System.Windows.Forms.Button buttonLog; 258 | 259 | 260 | } 261 | } 262 | 263 | -------------------------------------------------------------------------------- /MouseTester/MouseTester/Form1.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel; 4 | using System.Data; 5 | using System.Diagnostics; 6 | using System.Drawing; 7 | using System.Linq; 8 | using System.Runtime.InteropServices; 9 | using System.Text; 10 | using System.Threading; 11 | using System.Windows.Forms; 12 | 13 | using OxyPlot; 14 | 15 | namespace MouseTester 16 | { 17 | using OxyPlot.Series; 18 | 19 | public partial class Form1 : Form 20 | { 21 | private MouseLog mlog = new MouseLog(); 22 | enum state { idle, measure_wait, measure, collect_wait, collect, log }; 23 | private state test_state = state.idle; 24 | private long pFreq; 25 | 26 | public Form1() 27 | { 28 | InitializeComponent(); 29 | 30 | this.Text = $"MouseTester v{Program.version}"; 31 | 32 | try { 33 | // Process.GetCurrentProcess().ProcessorAffinity = new IntPtr(2); // Use only the second core 34 | // Process.GetCurrentProcess().PriorityClass = ProcessPriorityClass.RealTime; // Set highest process priority 35 | // Thread.CurrentThread.Priority = ThreadPriority.Highest; // Set highest thread priority 36 | } catch (Exception ex) { 37 | Debug.WriteLine(ex.ToString()); 38 | } 39 | 40 | this.RegisterRawInputMouse(Handle); 41 | this.textBoxDesc.Text = this.mlog.Desc.ToString(); 42 | this.textBoxCPI.Text = this.mlog.Cpi.ToString(); 43 | this.textBox1.Text = "Enter the correct CPI" + 44 | "\r\n or\r\n" + 45 | "Press the Measure button" + 46 | "\r\n or\r\n" + 47 | "Press the Load button"; 48 | this.toolStripStatusLabel1.Text = ""; 49 | this.KeyPreview = true; 50 | this.KeyDown += new KeyEventHandler(Form1_KeyDown); 51 | 52 | QueryPerformanceFrequency(out pFreq); 53 | // Debug.WriteLine("PerformanceCounterFrequency: " + pFreq.ToString() + " Hz\n"); 54 | } 55 | 56 | private void Form1_KeyDown(object sender, KeyEventArgs e) 57 | { 58 | // TODO: Replace with Global Hotkey 59 | // https://social.msdn.microsoft.com/Forums/vstudio/en-US/c061954b-19bf-463b-a57d-b09c98a3fe7d/assign-global-hotkey-to-a-system-tray-application-in-c?forum=csharpgeneral 60 | 61 | if (e.KeyCode == Keys.F1) 62 | { 63 | buttonLog.PerformClick(); 64 | e.Handled = true; 65 | } 66 | if (e.KeyCode == Keys.F2) 67 | { 68 | buttonLog.PerformClick(); 69 | e.Handled = true; 70 | } 71 | else if (e.KeyCode == Keys.F3) 72 | { 73 | buttonPlot.PerformClick(); 74 | e.Handled = false; 75 | } 76 | } 77 | 78 | protected override void WndProc(ref Message m) 79 | { 80 | QueryPerformanceCounter(out long pCounter); 81 | if (m.Msg == WM_INPUT) 82 | { 83 | RAWINPUT raw = new RAWINPUT(); 84 | uint size = (uint)Marshal.SizeOf(typeof(RAWINPUT)); 85 | int outsize = GetRawInputData(m.LParam, RID_INPUT, out raw, ref size, (uint)Marshal.SizeOf(typeof(RAWINPUTHEADER))); 86 | if (outsize != -1) 87 | { 88 | if (raw.header.dwType == RIM_TYPEMOUSE) 89 | { 90 | logMouseEvent(new MouseEvent(raw.data.mouse.buttonsStr.usButtonFlags, raw.data.mouse.lLastX, -(raw.data.mouse.lLastY), pCounter)); 91 | } 92 | } 93 | } 94 | base.WndProc(ref m); 95 | } 96 | 97 | private void logMouseEvent(MouseEvent mevent) 98 | { 99 | // Debug.WriteLine(mevent.pcounter + ", " + mevent.lastx + ", " + mevent.lasty + ", " + mevent.buttonflags); 100 | if (this.test_state == state.idle) 101 | { 102 | 103 | } 104 | else if (this.test_state == state.measure_wait) 105 | { 106 | if (mevent.buttonflags == 0x0001) 107 | { 108 | this.mlog.Add(mevent); 109 | this.toolStripStatusLabel1.Text = "Measuring"; 110 | this.test_state = state.measure; 111 | } 112 | } 113 | else if (this.test_state == state.measure) 114 | { 115 | this.mlog.Add(mevent); 116 | if (mevent.buttonflags == 0x0002) 117 | { 118 | double x = 0.0; 119 | double y = 0.0; 120 | foreach (MouseEvent e in this.mlog.Events) 121 | { 122 | x += (double)e.lastx; 123 | y += (double)e.lasty; 124 | } 125 | tsCalc(); 126 | this.mlog.Cpi = Math.Round(Math.Sqrt((x * x) + (y * y)) / (10 / 2.54)); 127 | this.textBoxCPI.Text = this.mlog.Cpi.ToString(); 128 | this.textBox1.Text = "Press the Collect or Log Start button\r\n"; 129 | this.toolStripStatusLabel1.Text = ""; 130 | this.test_state = state.idle; 131 | } 132 | } 133 | else if (this.test_state == state.collect_wait) 134 | { 135 | if (mevent.buttonflags == 0x0001) 136 | { 137 | this.mlog.Add(mevent); 138 | this.toolStripStatusLabel1.Text = "Collecting"; 139 | this.test_state = state.collect; 140 | } 141 | } 142 | else if (this.test_state == state.collect) 143 | { 144 | this.mlog.Add(mevent); 145 | if (mevent.buttonflags == 0x0002) 146 | { 147 | tsCalc(); 148 | this.textBox1.Text = "Press the plot button to view data\r\n" + 149 | " or\r\n" + 150 | "Press the save button to save log file\r\n" + 151 | "Events: " + this.mlog.Events.Count.ToString() + "\r\n" + 152 | "Sum X: " + this.mlog.deltaX().ToString() + " counts " + Math.Abs(this.mlog.deltaX() / this.mlog.Cpi * 2.54).ToString("0.0") + " cm\r\n" + 153 | "Sum Y: " + this.mlog.deltaY().ToString() + " counts " + Math.Abs(this.mlog.deltaY() / this.mlog.Cpi * 2.54).ToString("0.0") + " cm\r\n" + 154 | "Path: " + this.mlog.path().ToString("0") + " counts " + (this.mlog.path() / this.mlog.Cpi * 2.54).ToString("0.0") + " cm"; 155 | this.toolStripStatusLabel1.Text = ""; 156 | this.test_state = state.idle; 157 | } 158 | } 159 | else if (this.test_state == state.log) 160 | { 161 | this.mlog.Add(mevent); 162 | } 163 | } 164 | 165 | private void buttonMeasure_Click(object sender, EventArgs e) 166 | { 167 | if (this.test_state == state.idle) { 168 | this.textBox1.Text = "1. Press and hold the left mouse button\r\n" + 169 | "2. Move the mouse 10 cm in a straight line\r\n" + 170 | "3. Release the left mouse button\r\n"; 171 | this.toolStripStatusLabel1.Text = "Press the left mouse button"; 172 | this.mlog.Clear(); 173 | this.test_state = state.measure_wait; 174 | } 175 | } 176 | 177 | private void buttonCollect_Click(object sender, EventArgs e) 178 | { 179 | if (this.test_state == state.idle) 180 | { 181 | this.textBox1.Text = "1. Press and hold the left mouse button\r\n" + 182 | "2. Move the mouse\r\n" + 183 | "3. Release the left mouse button\r\n"; 184 | this.toolStripStatusLabel1.Text = "Press the left mouse button"; 185 | this.mlog.Clear(); 186 | this.test_state = state.collect_wait; 187 | } 188 | } 189 | 190 | private void buttonLog_Click(object sender, EventArgs e) 191 | { 192 | if (this.test_state == state.idle) 193 | { 194 | this.textBox1.Text = "1. Press the Log Stop button\r\n"; 195 | this.toolStripStatusLabel1.Text = "Logging..."; 196 | this.mlog.Clear(); 197 | this.test_state = state.log; 198 | buttonLog.Text = "Stop (F2)"; 199 | } 200 | else if (this.test_state == state.log) 201 | { 202 | tsCalc(); 203 | this.textBox1.Text = "Press the plot button to view data\r\n" + 204 | " or\r\n" + 205 | "Press the save button to save log file\r\n" + 206 | "Events: " + this.mlog.Events.Count.ToString() + "\r\n" + 207 | "Sum X: " + this.mlog.deltaX().ToString() + " counts " + Math.Abs(this.mlog.deltaX() / this.mlog.Cpi * 2.54).ToString("0.0") + " cm\r\n" + 208 | "Sum Y: " + this.mlog.deltaY().ToString() + " counts " + Math.Abs(this.mlog.deltaY() / this.mlog.Cpi * 2.54).ToString("0.0") + " cm\r\n" + 209 | "Path: " + this.mlog.path().ToString("0") + " counts " + (this.mlog.path() / this.mlog.Cpi * 2.54).ToString("0.0") + " cm"; 210 | this.toolStripStatusLabel1.Text = ""; 211 | this.test_state = state.idle; 212 | buttonLog.Text = "Start (F1)"; 213 | } 214 | } 215 | 216 | private void buttonPlot_Click(object sender, EventArgs e) 217 | { 218 | if (this.mlog.Events.Count > 0 && this.test_state != state.log && this.test_state != state.collect_wait) 219 | { 220 | this.mlog.Desc = textBoxDesc.Text; 221 | MousePlot mousePlot = new MousePlot(this.mlog); 222 | mousePlot.Show(); 223 | } 224 | } 225 | 226 | private void buttonLoad_Click(object sender, EventArgs e) 227 | { 228 | OpenFileDialog openFileDialog1 = new OpenFileDialog(); 229 | openFileDialog1.Filter = "CSV Files (*.csv)|*.csv|All Files(*.*)|*.*"; 230 | openFileDialog1.FilterIndex = 1; 231 | openFileDialog1.Multiselect = false; 232 | if (openFileDialog1.ShowDialog() == System.Windows.Forms.DialogResult.OK) 233 | { 234 | this.mlog.Load(openFileDialog1.FileName); 235 | } 236 | this.textBox1.Text = "Events: " + this.mlog.Events.Count.ToString() + "\r\n" + 237 | "Sum X: " + this.mlog.deltaX().ToString() + " counts " + Math.Abs(this.mlog.deltaX() / this.mlog.Cpi * 2.54).ToString("0.0") + " cm\r\n" + 238 | "Sum Y: " + this.mlog.deltaY().ToString() + " counts " + Math.Abs(this.mlog.deltaY() / this.mlog.Cpi * 2.54).ToString("0.0") + " cm\r\n" + 239 | "Path: " + this.mlog.path().ToString("0") + " counts " + (this.mlog.path() / this.mlog.Cpi * 2.54).ToString("0.0") + " cm"; 240 | this.textBoxDesc.Text = this.mlog.Desc.ToString(); 241 | this.textBoxCPI.Text = this.mlog.Cpi.ToString(); 242 | if (this.mlog.Events.Count > 0) 243 | { 244 | MousePlot mousePlot = new MousePlot(this.mlog); 245 | mousePlot.Show(); 246 | } 247 | } 248 | 249 | private void buttonSave_Click(object sender, EventArgs e) 250 | { 251 | SaveFileDialog saveFileDialog1 = new SaveFileDialog(); 252 | saveFileDialog1.Filter = "CSV Files (*.csv)|*.csv|All Files(*.*)|*.*"; 253 | saveFileDialog1.FilterIndex = 1; 254 | if (saveFileDialog1.ShowDialog() == System.Windows.Forms.DialogResult.OK) 255 | { 256 | this.mlog.Desc = textBoxDesc.Text; 257 | this.mlog.Save(saveFileDialog1.FileName); 258 | } 259 | } 260 | 261 | private void textBoxCPI_Validated(object sender, EventArgs e) 262 | { 263 | try 264 | { 265 | this.mlog.Cpi = double.Parse(this.textBoxCPI.Text); 266 | } 267 | catch //(Exception ex) 268 | { 269 | MessageBox.Show("Invalid CPI, resetting to previous value"); 270 | this.textBoxCPI.Text = this.mlog.Cpi.ToString(); 271 | } 272 | this.textBox1.Text = "Press the Collect or Log Start button\r\n"; 273 | } 274 | 275 | private void tsCalc() 276 | { 277 | long pcounter_min = this.mlog.Events[0].pcounter; 278 | foreach (MouseEvent me in this.mlog.Events) 279 | { 280 | me.ts = (me.pcounter - pcounter_min) * 1000.0 / pFreq; 281 | } 282 | } 283 | } 284 | } 285 | -------------------------------------------------------------------------------- /MouseTester/MouseTester/Form1.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 | 17, 17 122 | 123 | -------------------------------------------------------------------------------- /MouseTester/MouseTester/GraphicsRenderContext.cs: -------------------------------------------------------------------------------- 1 | // -------------------------------------------------------------------------------------------------------------------- 2 | // 3 | // The MIT License (MIT) 4 | // 5 | // Copyright (c) 2012 Oystein Bjorke 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a 8 | // copy of this software and associated documentation files (the 9 | // "Software"), to deal in the Software without restriction, including 10 | // without limitation the rights to use, copy, modify, merge, publish, 11 | // distribute, sublicense, and/or sell copies of the Software, and to 12 | // permit persons to whom the Software is furnished to do so, subject to 13 | // the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be included 16 | // in all copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 19 | // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 20 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 21 | // IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | // CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 23 | // TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 24 | // SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 25 | // 26 | // 27 | // The graphics render context. 28 | // 29 | // -------------------------------------------------------------------------------------------------------------------- 30 | namespace OxyPlot.WindowsForms 31 | { 32 | using System; 33 | using System.Collections.Generic; 34 | using System.Drawing; 35 | using System.Drawing.Drawing2D; 36 | using System.Drawing.Imaging; 37 | using System.IO; 38 | using System.Linq; 39 | 40 | using OxyPlot; 41 | 42 | /// 43 | /// The graphics render context. 44 | /// 45 | internal class GraphicsRenderContext : RenderContextBase 46 | { 47 | /// 48 | /// The font size factor. 49 | /// 50 | private const float FontsizeFactor = 0.8f; 51 | 52 | /// 53 | /// The GDI+ drawing surface. 54 | /// 55 | private Graphics g; 56 | 57 | /// 58 | /// Initializes a new instance of the class. 59 | /// 60 | public GraphicsRenderContext() 61 | { 62 | } 63 | 64 | /// 65 | /// Sets the graphics target. 66 | /// 67 | /// The graphics surface. 68 | public void SetGraphicsTarget(Graphics graphics) 69 | { 70 | this.g = graphics; 71 | this.g.CompositingMode = CompositingMode.SourceOver; 72 | this.g.CompositingQuality = CompositingQuality.HighQuality; 73 | this.g.InterpolationMode = InterpolationMode.HighQualityBicubic; 74 | this.g.PixelOffsetMode = PixelOffsetMode.HighQuality; 75 | this.g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias; 76 | this.g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAliasGridFit; 77 | } 78 | 79 | /// 80 | /// Draws the ellipse. 81 | /// 82 | /// The rect. 83 | /// The fill. 84 | /// The stroke. 85 | /// The thickness. 86 | public override void DrawEllipse(OxyRect rect, OxyColor fill, OxyColor stroke, double thickness) 87 | { 88 | if (fill != null) 89 | { 90 | this.g.FillEllipse( 91 | this.ToBrush(fill), (float)rect.Left, (float)rect.Top, (float)rect.Width, (float)rect.Height); 92 | } 93 | 94 | if (stroke == null || thickness <= 0) 95 | { 96 | return; 97 | } 98 | 99 | using (var pen = new Pen(this.ToColor(stroke), (float)thickness)) 100 | { 101 | this.g.SmoothingMode = SmoothingMode.HighQuality; 102 | this.g.DrawEllipse(pen, (float)rect.Left, (float)rect.Top, (float)rect.Width, (float)rect.Height); 103 | } 104 | } 105 | 106 | /// 107 | /// Draws the line. 108 | /// 109 | /// The points. 110 | /// The stroke. 111 | /// The thickness. 112 | /// The dash array. 113 | /// The line join. 114 | /// if set to true [aliased]. 115 | public override void DrawLine( 116 | IList points, 117 | OxyColor stroke, 118 | double thickness, 119 | double[] dashArray, 120 | OxyPenLineJoin lineJoin, 121 | bool aliased) 122 | { 123 | if (stroke == null || thickness <= 0 || points.Count < 2) 124 | { 125 | return; 126 | } 127 | 128 | this.g.SmoothingMode = aliased ? SmoothingMode.None : SmoothingMode.HighQuality; 129 | using (var pen = new Pen(this.ToColor(stroke), (float)thickness)) 130 | { 131 | 132 | if (dashArray != null) 133 | { 134 | pen.DashPattern = this.ToFloatArray(dashArray); 135 | } 136 | 137 | switch (lineJoin) 138 | { 139 | case OxyPenLineJoin.Round: 140 | pen.LineJoin = LineJoin.Round; 141 | break; 142 | case OxyPenLineJoin.Bevel: 143 | pen.LineJoin = LineJoin.Bevel; 144 | break; 145 | 146 | // The default LineJoin is Miter 147 | } 148 | 149 | this.g.DrawLines(pen, this.ToPoints(points)); 150 | } 151 | } 152 | 153 | /// 154 | /// Draws the polygon. 155 | /// 156 | /// The points. 157 | /// The fill. 158 | /// The stroke. 159 | /// The thickness. 160 | /// The dash array. 161 | /// The line join. 162 | /// if set to true [aliased]. 163 | public override void DrawPolygon( 164 | IList points, 165 | OxyColor fill, 166 | OxyColor stroke, 167 | double thickness, 168 | double[] dashArray, 169 | OxyPenLineJoin lineJoin, 170 | bool aliased) 171 | { 172 | if (points.Count < 2) 173 | { 174 | return; 175 | } 176 | 177 | this.g.SmoothingMode = aliased ? SmoothingMode.None : SmoothingMode.HighQuality; 178 | 179 | PointF[] pts = this.ToPoints(points); 180 | if (fill != null) 181 | { 182 | this.g.FillPolygon(this.ToBrush(fill), pts); 183 | } 184 | 185 | if (stroke != null && thickness > 0) 186 | { 187 | using (var pen = new Pen(this.ToColor(stroke), (float)thickness)) 188 | { 189 | 190 | if (dashArray != null) 191 | { 192 | pen.DashPattern = this.ToFloatArray(dashArray); 193 | } 194 | 195 | switch (lineJoin) 196 | { 197 | case OxyPenLineJoin.Round: 198 | pen.LineJoin = LineJoin.Round; 199 | break; 200 | case OxyPenLineJoin.Bevel: 201 | pen.LineJoin = LineJoin.Bevel; 202 | break; 203 | 204 | // The default LineJoin is Miter 205 | } 206 | 207 | this.g.DrawPolygon(pen, pts); 208 | } 209 | } 210 | } 211 | 212 | /// 213 | /// The draw rectangle. 214 | /// 215 | /// 216 | /// The rect. 217 | /// 218 | /// 219 | /// The fill. 220 | /// 221 | /// 222 | /// The stroke. 223 | /// 224 | /// 225 | /// The thickness. 226 | /// 227 | public override void DrawRectangle(OxyRect rect, OxyColor fill, OxyColor stroke, double thickness) 228 | { 229 | if (fill != null) 230 | { 231 | this.g.FillRectangle( 232 | this.ToBrush(fill), (float)rect.Left, (float)rect.Top, (float)rect.Width, (float)rect.Height); 233 | } 234 | 235 | if (stroke == null || thickness <= 0) 236 | { 237 | return; 238 | } 239 | 240 | using (var pen = new Pen(this.ToColor(stroke), (float)thickness)) 241 | { 242 | this.g.DrawRectangle(pen, (float)rect.Left, (float)rect.Top, (float)rect.Width, (float)rect.Height); 243 | } 244 | } 245 | 246 | /// 247 | /// The draw text. 248 | /// 249 | /// 250 | /// The p. 251 | /// 252 | /// 253 | /// The text. 254 | /// 255 | /// 256 | /// The fill. 257 | /// 258 | /// 259 | /// The font family. 260 | /// 261 | /// 262 | /// The font size. 263 | /// 264 | /// 265 | /// The font weight. 266 | /// 267 | /// 268 | /// The rotate. 269 | /// 270 | /// 271 | /// The halign. 272 | /// 273 | /// 274 | /// The valign. 275 | /// 276 | /// 277 | /// The maximum size of the text. 278 | /// 279 | public override void DrawText( 280 | ScreenPoint p, 281 | string text, 282 | OxyColor fill, 283 | string fontFamily, 284 | double fontSize, 285 | double fontWeight, 286 | double rotate, 287 | HorizontalAlignment halign, 288 | VerticalAlignment valign, 289 | OxySize? maxSize) 290 | { 291 | var fs = FontStyle.Regular; 292 | if (fontWeight >= 700) 293 | { 294 | fs = FontStyle.Bold; 295 | } 296 | 297 | using (var font = new Font(fontFamily, (float)fontSize * FontsizeFactor, fs)) 298 | { 299 | using (var sf = new StringFormat { Alignment = StringAlignment.Near }) 300 | { 301 | var size = this.g.MeasureString(text, font); 302 | if (maxSize != null) 303 | { 304 | if (size.Width > maxSize.Value.Width) 305 | { 306 | size.Width = (float)maxSize.Value.Width; 307 | } 308 | 309 | if (size.Height > maxSize.Value.Height) 310 | { 311 | size.Height = (float)maxSize.Value.Height; 312 | } 313 | } 314 | 315 | float dx = 0; 316 | if (halign == HorizontalAlignment.Center) 317 | { 318 | dx = -size.Width / 2; 319 | } 320 | 321 | if (halign == HorizontalAlignment.Right) 322 | { 323 | dx = -size.Width; 324 | } 325 | 326 | float dy = 0; 327 | sf.LineAlignment = StringAlignment.Near; 328 | if (valign == VerticalAlignment.Middle) 329 | { 330 | dy = -size.Height / 2; 331 | } 332 | 333 | if (valign == VerticalAlignment.Bottom) 334 | { 335 | dy = -size.Height; 336 | } 337 | 338 | this.g.TranslateTransform((float)p.X, (float)p.Y); 339 | if (Math.Abs(rotate) > double.Epsilon) 340 | { 341 | this.g.RotateTransform((float)rotate); 342 | } 343 | 344 | this.g.TranslateTransform(dx, dy); 345 | 346 | var layoutRectangle = new RectangleF(0, 0, size.Width, size.Height); 347 | this.g.DrawString(text, font, this.ToBrush(fill), layoutRectangle, sf); 348 | 349 | this.g.ResetTransform(); 350 | } 351 | } 352 | } 353 | 354 | /// 355 | /// The measure text. 356 | /// 357 | /// The text. 358 | /// The font family. 359 | /// The font size. 360 | /// The font weight. 361 | /// The size of the text. 362 | public override OxySize MeasureText(string text, string fontFamily, double fontSize, double fontWeight) 363 | { 364 | if (text == null) 365 | { 366 | return OxySize.Empty; 367 | } 368 | 369 | var fs = FontStyle.Regular; 370 | if (fontWeight >= 700) 371 | { 372 | fs = FontStyle.Bold; 373 | } 374 | 375 | using (var font = new Font(fontFamily, (float)fontSize * FontsizeFactor, fs)) 376 | { 377 | var size = this.g.MeasureString(text, font); 378 | return new OxySize(size.Width, size.Height); 379 | } 380 | } 381 | 382 | /// 383 | /// Converts a fill color to a System.Drawing.Brush. 384 | /// 385 | /// 386 | /// The fill color. 387 | /// 388 | /// 389 | /// The brush. 390 | /// 391 | private Brush ToBrush(OxyColor fill) 392 | { 393 | if (fill != null) 394 | { 395 | return new SolidBrush(this.ToColor(fill)); 396 | } 397 | 398 | return null; 399 | } 400 | 401 | /// 402 | /// Converts a color to a System.Drawing.Color. 403 | /// 404 | /// 405 | /// The color. 406 | /// 407 | /// 408 | /// The System.Drawing.Color. 409 | /// 410 | private Color ToColor(OxyColor c) 411 | { 412 | return Color.FromArgb(c.A, c.R, c.G, c.B); 413 | } 414 | 415 | /// 416 | /// Converts a double array to a float array. 417 | /// 418 | /// 419 | /// The a. 420 | /// 421 | /// 422 | /// The float array. 423 | /// 424 | private float[] ToFloatArray(double[] a) 425 | { 426 | if (a == null) 427 | { 428 | return null; 429 | } 430 | 431 | var r = new float[a.Length]; 432 | for (int i = 0; i < a.Length; i++) 433 | { 434 | r[i] = (float)a[i]; 435 | } 436 | 437 | return r; 438 | } 439 | 440 | /// 441 | /// Converts a list of point to an array of PointF. 442 | /// 443 | /// 444 | /// The points. 445 | /// 446 | /// 447 | /// An array of points. 448 | /// 449 | private PointF[] ToPoints(IList points) 450 | { 451 | if (points == null) 452 | { 453 | return null; 454 | } 455 | 456 | var r = new PointF[points.Count()]; 457 | int i = 0; 458 | foreach (ScreenPoint p in points) 459 | { 460 | r[i++] = new PointF((float)p.X, (float)p.Y); 461 | } 462 | 463 | return r; 464 | } 465 | 466 | public override void CleanUp() 467 | { 468 | var imagesToRelease = imageCache.Keys.Where(i => !imagesInUse.Contains(i)); 469 | foreach (var i in imagesToRelease) 470 | { 471 | var image = this.GetImage(i); 472 | image.Dispose(); 473 | imageCache.Remove(i); 474 | } 475 | 476 | imagesInUse.Clear(); 477 | } 478 | 479 | public override OxyImageInfo GetImageInfo(OxyImage source) 480 | { 481 | var image = this.GetImage(source); 482 | return image == null ? null : new OxyImageInfo { Width = (uint)image.Width, Height = (uint)image.Height, DpiX = image.HorizontalResolution, DpiY = image.VerticalResolution }; 483 | } 484 | 485 | public override void DrawImage(OxyImage source, uint srcX, uint srcY, uint srcWidth, uint srcHeight, double x, double y, double w, double h, double opacity, bool interpolate) 486 | { 487 | var image = this.GetImage(source); 488 | if (image != null) 489 | { 490 | ImageAttributes ia = null; 491 | if (opacity < 1) 492 | { 493 | var cm = new ColorMatrix 494 | { 495 | Matrix00 = 1f, 496 | Matrix11 = 1f, 497 | Matrix22 = 1f, 498 | Matrix33 = 1f, 499 | Matrix44 = (float)opacity 500 | }; 501 | 502 | ia = new ImageAttributes(); 503 | ia.SetColorMatrix(cm, ColorMatrixFlag.Default, ColorAdjustType.Bitmap); 504 | } 505 | 506 | g.InterpolationMode = interpolate ? InterpolationMode.HighQualityBicubic : InterpolationMode.NearestNeighbor; 507 | int sx = (int)Math.Round(x); 508 | int sy = (int)Math.Round(y); 509 | int sw = (int)Math.Round(x + w) - sx; 510 | int sh = (int)Math.Round(y + h) - sy; 511 | g.DrawImage(image, new Rectangle(sx, sy, sw, sh), srcX, srcY, srcWidth, srcHeight, GraphicsUnit.Pixel, ia); 512 | } 513 | } 514 | 515 | private HashSet imagesInUse = new HashSet(); 516 | 517 | private Dictionary imageCache = new Dictionary(); 518 | 519 | 520 | private Image GetImage(OxyImage source) 521 | { 522 | if (source == null) 523 | { 524 | return null; 525 | } 526 | 527 | if (!this.imagesInUse.Contains(source)) 528 | { 529 | this.imagesInUse.Add(source); 530 | } 531 | 532 | Image src; 533 | if (this.imageCache.TryGetValue(source, out src)) 534 | { 535 | return src; 536 | } 537 | 538 | if (source != null) 539 | { 540 | Image btm; 541 | using (var ms = new MemoryStream(source.GetData())) 542 | { 543 | btm = Image.FromStream(ms); 544 | } 545 | 546 | this.imageCache.Add(source, btm); 547 | return btm; 548 | } 549 | 550 | return null; 551 | } 552 | 553 | public override bool SetClip(OxyRect rect) 554 | { 555 | this.g.SetClip(rect.ToRect(false)); 556 | return true; 557 | } 558 | 559 | public override void ResetClip() 560 | { 561 | this.g.ResetClip(); 562 | } 563 | } 564 | } -------------------------------------------------------------------------------- /MouseTester/MouseTester/MouseEvent.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace MouseTester 7 | { 8 | public class MouseEvent 9 | { 10 | public ushort buttonflags; 11 | public int lastx; 12 | public int lasty; 13 | public long pcounter; 14 | public double ts; 15 | 16 | public MouseEvent(ushort buttonflags, int lastx, int lasty, long pcounter) 17 | { 18 | this.buttonflags = buttonflags; 19 | this.lastx = lastx; 20 | this.lasty = lasty; 21 | this.pcounter = pcounter; 22 | this.ts = 0.0; 23 | } 24 | public MouseEvent(ushort buttonflags, int lastx, int lasty, double ts) 25 | { 26 | this.buttonflags = buttonflags; 27 | this.lastx = lastx; 28 | this.lasty = lasty; 29 | this.pcounter = 0; 30 | this.ts = ts; 31 | } 32 | 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /MouseTester/MouseTester/MouseLog.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.Globalization; 5 | using System.IO; 6 | using System.Linq; 7 | using System.Text; 8 | 9 | namespace MouseTester 10 | { 11 | public class MouseLog 12 | { 13 | private string desc = "MouseTester"; 14 | private double cpi = 400.0; 15 | private List events = new List(); 16 | 17 | public double Cpi 18 | { 19 | get 20 | { 21 | return this.cpi; 22 | } 23 | set 24 | { 25 | cpi = value; 26 | } 27 | } 28 | 29 | public string Desc 30 | { 31 | get 32 | { 33 | return this.desc; 34 | } 35 | set 36 | { 37 | this.desc = value; 38 | } 39 | } 40 | 41 | public List Events 42 | { 43 | get 44 | { 45 | return this.events; 46 | } 47 | } 48 | 49 | public void Add(MouseEvent e) 50 | { 51 | this.events.Add(e); 52 | } 53 | 54 | public void Clear() 55 | { 56 | this.events.Clear(); 57 | } 58 | 59 | public void Load(string fname) 60 | { 61 | this.Clear(); 62 | 63 | try 64 | { 65 | using (StreamReader sr = File.OpenText(fname)) 66 | { 67 | this.desc = sr.ReadLine(); 68 | this.cpi = double.Parse(sr.ReadLine()); 69 | string headerline = sr.ReadLine(); 70 | while (sr.Peek() > -1) 71 | { 72 | string line = sr.ReadLine(); 73 | string[] values = line.Split(','); 74 | if (values.Length == 4) 75 | { 76 | this.Add(new MouseEvent(ushort.Parse(values[3]), int.Parse(values[0]), int.Parse(values[1]), double.Parse(values[2]))); 77 | } 78 | else if (values.Length == 3) 79 | { 80 | this.Add(new MouseEvent(0, int.Parse(values[0]), int.Parse(values[1]), double.Parse(values[2]))); 81 | } 82 | } 83 | } 84 | } 85 | catch (Exception ex) 86 | { 87 | Debug.WriteLine(ex.ToString()); 88 | } 89 | } 90 | 91 | public void Save(string fname) 92 | { 93 | try 94 | { 95 | using (StreamWriter sw = File.CreateText(fname)) 96 | { 97 | sw.WriteLine(this.desc); 98 | sw.WriteLine(this.cpi.ToString()); 99 | sw.WriteLine("xCount,yCount,Time (ms),buttonflags"); 100 | foreach (MouseEvent e in this.events) 101 | { 102 | sw.WriteLine(e.lastx.ToString() + "," + e.lasty.ToString() + "," + e.ts.ToString(CultureInfo.InvariantCulture) + "," + e.buttonflags.ToString()); 103 | } 104 | } 105 | } 106 | catch (Exception ex) 107 | { 108 | Debug.WriteLine(ex.ToString()); 109 | } 110 | } 111 | 112 | public int deltaX() 113 | { 114 | return this.events.Sum(e => e.lastx); 115 | } 116 | 117 | public int deltaY() 118 | { 119 | return this.events.Sum(e => e.lasty); 120 | } 121 | 122 | public double path() 123 | { 124 | return this.events.Sum(e => Math.Sqrt((e.lastx * e.lastx) + (e.lasty * e.lasty))); 125 | } 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /MouseTester/MouseTester/MousePlot.Designer.cs: -------------------------------------------------------------------------------- 1 | namespace MouseTester 2 | { 3 | partial class MousePlot 4 | { 5 | /// 6 | /// Required designer variable. 7 | /// 8 | private System.ComponentModel.IContainer components = null; 9 | 10 | /// 11 | /// Clean up any resources being used. 12 | /// 13 | /// true if managed resources should be disposed; otherwise, false. 14 | protected override void Dispose(bool disposing) 15 | { 16 | if (disposing && (components != null)) 17 | { 18 | components.Dispose(); 19 | } 20 | base.Dispose(disposing); 21 | } 22 | 23 | #region Windows Form Designer generated code 24 | 25 | /// 26 | /// Required method for Designer support - do not modify 27 | /// the contents of this method with the code editor. 28 | /// 29 | private void InitializeComponent() 30 | { 31 | this.splitContainer1 = new System.Windows.Forms.SplitContainer(); 32 | this.plot1 = new OxyPlot.WindowsForms.Plot(); 33 | this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel(); 34 | this.statisticsGroupBox = new System.Windows.Forms.GroupBox(); 35 | this.tableLayoutPanel3 = new System.Windows.Forms.TableLayoutPanel(); 36 | this.label4 = new System.Windows.Forms.Label(); 37 | this.label2 = new System.Windows.Forms.Label(); 38 | this.maxInterval = new System.Windows.Forms.Label(); 39 | this.avgInterval = new System.Windows.Forms.Label(); 40 | this.label11 = new System.Windows.Forms.Label(); 41 | this.label12 = new System.Windows.Forms.Label(); 42 | this.rangeInterval = new System.Windows.Forms.Label(); 43 | this.medianInterval = new System.Windows.Forms.Label(); 44 | this.label14 = new System.Windows.Forms.Label(); 45 | this.label13 = new System.Windows.Forms.Label(); 46 | this.minInterval = new System.Windows.Forms.Label(); 47 | this.stdevInterval = new System.Windows.Forms.Label(); 48 | this.firstPercentileMetricLabel = new System.Windows.Forms.Label(); 49 | this.secondPercentileMetricLabel = new System.Windows.Forms.Label(); 50 | this.firstPercentileInterval = new System.Windows.Forms.Label(); 51 | this.secondPercentileInterval = new System.Windows.Forms.Label(); 52 | this.groupBox1 = new System.Windows.Forms.GroupBox(); 53 | this.comboBoxPlotType = new System.Windows.Forms.ComboBox(); 54 | this.groupBox2 = new System.Windows.Forms.GroupBox(); 55 | this.numericUpDownStart = new System.Windows.Forms.NumericUpDown(); 56 | this.groupBox3 = new System.Windows.Forms.GroupBox(); 57 | this.numericUpDownEnd = new System.Windows.Forms.NumericUpDown(); 58 | this.tableLayoutPanel2 = new System.Windows.Forms.TableLayoutPanel(); 59 | this.checkBoxLines = new System.Windows.Forms.CheckBox(); 60 | this.checkBoxStem = new System.Windows.Forms.CheckBox(); 61 | this.buttonSavePNG = new System.Windows.Forms.Button(); 62 | ((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).BeginInit(); 63 | this.splitContainer1.Panel1.SuspendLayout(); 64 | this.splitContainer1.Panel2.SuspendLayout(); 65 | this.splitContainer1.SuspendLayout(); 66 | this.tableLayoutPanel1.SuspendLayout(); 67 | this.statisticsGroupBox.SuspendLayout(); 68 | this.tableLayoutPanel3.SuspendLayout(); 69 | this.groupBox1.SuspendLayout(); 70 | this.groupBox2.SuspendLayout(); 71 | ((System.ComponentModel.ISupportInitialize)(this.numericUpDownStart)).BeginInit(); 72 | this.groupBox3.SuspendLayout(); 73 | ((System.ComponentModel.ISupportInitialize)(this.numericUpDownEnd)).BeginInit(); 74 | this.tableLayoutPanel2.SuspendLayout(); 75 | this.SuspendLayout(); 76 | // 77 | // splitContainer1 78 | // 79 | this.splitContainer1.Dock = System.Windows.Forms.DockStyle.Fill; 80 | this.splitContainer1.Location = new System.Drawing.Point(0, 0); 81 | this.splitContainer1.Name = "splitContainer1"; 82 | this.splitContainer1.Orientation = System.Windows.Forms.Orientation.Horizontal; 83 | // 84 | // splitContainer1.Panel1 85 | // 86 | this.splitContainer1.Panel1.Controls.Add(this.plot1); 87 | // 88 | // splitContainer1.Panel2 89 | // 90 | this.splitContainer1.Panel2.Controls.Add(this.tableLayoutPanel1); 91 | this.splitContainer1.Size = new System.Drawing.Size(1299, 791); 92 | this.splitContainer1.SplitterDistance = 699; 93 | this.splitContainer1.TabIndex = 0; 94 | // 95 | // plot1 96 | // 97 | this.plot1.BackColor = System.Drawing.Color.White; 98 | this.plot1.Dock = System.Windows.Forms.DockStyle.Fill; 99 | this.plot1.KeyboardPanHorizontalStep = 0.1D; 100 | this.plot1.KeyboardPanVerticalStep = 0.1D; 101 | this.plot1.Location = new System.Drawing.Point(0, 0); 102 | this.plot1.Name = "plot1"; 103 | this.plot1.PanCursor = System.Windows.Forms.Cursors.Hand; 104 | this.plot1.Size = new System.Drawing.Size(1299, 699); 105 | this.plot1.TabIndex = 0; 106 | this.plot1.Text = "plot1"; 107 | this.plot1.ZoomHorizontalCursor = System.Windows.Forms.Cursors.SizeWE; 108 | this.plot1.ZoomRectangleCursor = System.Windows.Forms.Cursors.SizeNWSE; 109 | this.plot1.ZoomVerticalCursor = System.Windows.Forms.Cursors.SizeNS; 110 | // 111 | // tableLayoutPanel1 112 | // 113 | this.tableLayoutPanel1.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 114 | | System.Windows.Forms.AnchorStyles.Left) 115 | | System.Windows.Forms.AnchorStyles.Right))); 116 | this.tableLayoutPanel1.ColumnCount = 7; 117 | this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); 118 | this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); 119 | this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); 120 | this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); 121 | this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); 122 | this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); 123 | this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); 124 | this.tableLayoutPanel1.Controls.Add(this.statisticsGroupBox, 3, 0); 125 | this.tableLayoutPanel1.Controls.Add(this.groupBox1, 0, 0); 126 | this.tableLayoutPanel1.Controls.Add(this.groupBox2, 1, 0); 127 | this.tableLayoutPanel1.Controls.Add(this.groupBox3, 2, 0); 128 | this.tableLayoutPanel1.Controls.Add(this.tableLayoutPanel2, 5, 0); 129 | this.tableLayoutPanel1.Controls.Add(this.buttonSavePNG, 6, 0); 130 | this.tableLayoutPanel1.Location = new System.Drawing.Point(3, 3); 131 | this.tableLayoutPanel1.Name = "tableLayoutPanel1"; 132 | this.tableLayoutPanel1.RowCount = 1; 133 | this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle()); 134 | this.tableLayoutPanel1.Size = new System.Drawing.Size(1293, 73); 135 | this.tableLayoutPanel1.TabIndex = 1; 136 | // 137 | // statisticsGroupBox 138 | // 139 | this.statisticsGroupBox.AutoSize = true; 140 | this.statisticsGroupBox.Controls.Add(this.tableLayoutPanel3); 141 | this.statisticsGroupBox.Location = new System.Drawing.Point(501, 3); 142 | this.statisticsGroupBox.Name = "statisticsGroupBox"; 143 | this.statisticsGroupBox.Padding = new System.Windows.Forms.Padding(0, 0, 5, 0); 144 | this.statisticsGroupBox.Size = new System.Drawing.Size(337, 71); 145 | this.statisticsGroupBox.TabIndex = 6; 146 | this.statisticsGroupBox.TabStop = false; 147 | this.statisticsGroupBox.Text = "Statistics"; 148 | // 149 | // tableLayoutPanel3 150 | // 151 | this.tableLayoutPanel3.AutoSize = true; 152 | this.tableLayoutPanel3.ColumnCount = 8; 153 | this.tableLayoutPanel3.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); 154 | this.tableLayoutPanel3.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); 155 | this.tableLayoutPanel3.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); 156 | this.tableLayoutPanel3.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); 157 | this.tableLayoutPanel3.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); 158 | this.tableLayoutPanel3.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); 159 | this.tableLayoutPanel3.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); 160 | this.tableLayoutPanel3.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); 161 | this.tableLayoutPanel3.Controls.Add(this.label4, 0, 1); 162 | this.tableLayoutPanel3.Controls.Add(this.label2, 0, 0); 163 | this.tableLayoutPanel3.Controls.Add(this.maxInterval, 1, 0); 164 | this.tableLayoutPanel3.Controls.Add(this.avgInterval, 1, 1); 165 | this.tableLayoutPanel3.Controls.Add(this.label11, 2, 0); 166 | this.tableLayoutPanel3.Controls.Add(this.label12, 2, 1); 167 | this.tableLayoutPanel3.Controls.Add(this.rangeInterval, 5, 0); 168 | this.tableLayoutPanel3.Controls.Add(this.medianInterval, 5, 1); 169 | this.tableLayoutPanel3.Controls.Add(this.label14, 4, 1); 170 | this.tableLayoutPanel3.Controls.Add(this.label13, 4, 0); 171 | this.tableLayoutPanel3.Controls.Add(this.minInterval, 3, 0); 172 | this.tableLayoutPanel3.Controls.Add(this.stdevInterval, 3, 1); 173 | this.tableLayoutPanel3.Controls.Add(this.firstPercentileMetricLabel, 6, 0); 174 | this.tableLayoutPanel3.Controls.Add(this.secondPercentileMetricLabel, 6, 1); 175 | this.tableLayoutPanel3.Controls.Add(this.firstPercentileInterval, 7, 0); 176 | this.tableLayoutPanel3.Controls.Add(this.secondPercentileInterval, 7, 1); 177 | this.tableLayoutPanel3.Location = new System.Drawing.Point(6, 17); 178 | this.tableLayoutPanel3.Margin = new System.Windows.Forms.Padding(0); 179 | this.tableLayoutPanel3.Name = "tableLayoutPanel3"; 180 | this.tableLayoutPanel3.Padding = new System.Windows.Forms.Padding(0, 2, 0, 0); 181 | this.tableLayoutPanel3.RowCount = 2; 182 | this.tableLayoutPanel3.RowStyles.Add(new System.Windows.Forms.RowStyle()); 183 | this.tableLayoutPanel3.RowStyles.Add(new System.Windows.Forms.RowStyle()); 184 | this.tableLayoutPanel3.Size = new System.Drawing.Size(326, 41); 185 | this.tableLayoutPanel3.TabIndex = 1; 186 | // 187 | // label4 188 | // 189 | this.label4.AutoSize = true; 190 | this.label4.Location = new System.Drawing.Point(0, 25); 191 | this.label4.Margin = new System.Windows.Forms.Padding(0); 192 | this.label4.Name = "label4"; 193 | this.label4.Size = new System.Drawing.Size(50, 13); 194 | this.label4.TabIndex = 3; 195 | this.label4.Text = "Average:"; 196 | // 197 | // label2 198 | // 199 | this.label2.AutoSize = true; 200 | this.label2.Location = new System.Drawing.Point(0, 2); 201 | this.label2.Margin = new System.Windows.Forms.Padding(0, 0, 0, 10); 202 | this.label2.Name = "label2"; 203 | this.label2.Size = new System.Drawing.Size(54, 13); 204 | this.label2.TabIndex = 1; 205 | this.label2.Text = "Maximum:"; 206 | // 207 | // maxInterval 208 | // 209 | this.maxInterval.AutoSize = true; 210 | this.maxInterval.Location = new System.Drawing.Point(54, 2); 211 | this.maxInterval.Margin = new System.Windows.Forms.Padding(0, 0, 10, 0); 212 | this.maxInterval.Name = "maxInterval"; 213 | this.maxInterval.Size = new System.Drawing.Size(13, 13); 214 | this.maxInterval.TabIndex = 4; 215 | this.maxInterval.Text = "0"; 216 | // 217 | // avgInterval 218 | // 219 | this.avgInterval.AutoSize = true; 220 | this.avgInterval.Location = new System.Drawing.Point(54, 25); 221 | this.avgInterval.Margin = new System.Windows.Forms.Padding(0, 0, 10, 0); 222 | this.avgInterval.Name = "avgInterval"; 223 | this.avgInterval.Size = new System.Drawing.Size(13, 13); 224 | this.avgInterval.TabIndex = 5; 225 | this.avgInterval.Text = "0"; 226 | // 227 | // label11 228 | // 229 | this.label11.AutoSize = true; 230 | this.label11.Location = new System.Drawing.Point(77, 2); 231 | this.label11.Margin = new System.Windows.Forms.Padding(0); 232 | this.label11.Name = "label11"; 233 | this.label11.Size = new System.Drawing.Size(51, 13); 234 | this.label11.TabIndex = 6; 235 | this.label11.Text = "Minimum:"; 236 | // 237 | // label12 238 | // 239 | this.label12.AutoSize = true; 240 | this.label12.Location = new System.Drawing.Point(77, 25); 241 | this.label12.Margin = new System.Windows.Forms.Padding(0); 242 | this.label12.Name = "label12"; 243 | this.label12.Size = new System.Drawing.Size(46, 13); 244 | this.label12.TabIndex = 7; 245 | this.label12.Text = "STDEV:"; 246 | // 247 | // rangeInterval 248 | // 249 | this.rangeInterval.AutoSize = true; 250 | this.rangeInterval.Location = new System.Drawing.Point(196, 2); 251 | this.rangeInterval.Margin = new System.Windows.Forms.Padding(0, 0, 10, 0); 252 | this.rangeInterval.Name = "rangeInterval"; 253 | this.rangeInterval.Size = new System.Drawing.Size(13, 13); 254 | this.rangeInterval.TabIndex = 12; 255 | this.rangeInterval.Text = "0"; 256 | // 257 | // medianInterval 258 | // 259 | this.medianInterval.AutoSize = true; 260 | this.medianInterval.Location = new System.Drawing.Point(196, 25); 261 | this.medianInterval.Margin = new System.Windows.Forms.Padding(0, 0, 10, 0); 262 | this.medianInterval.Name = "medianInterval"; 263 | this.medianInterval.Size = new System.Drawing.Size(13, 13); 264 | this.medianInterval.TabIndex = 13; 265 | this.medianInterval.Text = "0"; 266 | // 267 | // label14 268 | // 269 | this.label14.AutoSize = true; 270 | this.label14.Location = new System.Drawing.Point(151, 25); 271 | this.label14.Margin = new System.Windows.Forms.Padding(0); 272 | this.label14.Name = "label14"; 273 | this.label14.Size = new System.Drawing.Size(45, 13); 274 | this.label14.TabIndex = 11; 275 | this.label14.Text = "Median:"; 276 | // 277 | // label13 278 | // 279 | this.label13.AutoSize = true; 280 | this.label13.Location = new System.Drawing.Point(151, 2); 281 | this.label13.Margin = new System.Windows.Forms.Padding(0); 282 | this.label13.Name = "label13"; 283 | this.label13.Size = new System.Drawing.Size(42, 13); 284 | this.label13.TabIndex = 9; 285 | this.label13.Text = "Range:"; 286 | // 287 | // minInterval 288 | // 289 | this.minInterval.AutoSize = true; 290 | this.minInterval.Location = new System.Drawing.Point(128, 2); 291 | this.minInterval.Margin = new System.Windows.Forms.Padding(0, 0, 10, 0); 292 | this.minInterval.Name = "minInterval"; 293 | this.minInterval.Size = new System.Drawing.Size(13, 13); 294 | this.minInterval.TabIndex = 14; 295 | this.minInterval.Text = "0"; 296 | // 297 | // stdevInterval 298 | // 299 | this.stdevInterval.AutoSize = true; 300 | this.stdevInterval.Location = new System.Drawing.Point(128, 25); 301 | this.stdevInterval.Margin = new System.Windows.Forms.Padding(0, 0, 10, 0); 302 | this.stdevInterval.Name = "stdevInterval"; 303 | this.stdevInterval.Size = new System.Drawing.Size(13, 13); 304 | this.stdevInterval.TabIndex = 15; 305 | this.stdevInterval.Text = "0"; 306 | // 307 | // firstPercentileMetricLabel 308 | // 309 | this.firstPercentileMetricLabel.AutoSize = true; 310 | this.firstPercentileMetricLabel.Location = new System.Drawing.Point(219, 2); 311 | this.firstPercentileMetricLabel.Margin = new System.Windows.Forms.Padding(0); 312 | this.firstPercentileMetricLabel.Name = "firstPercentileMetricLabel"; 313 | this.firstPercentileMetricLabel.Size = new System.Drawing.Size(72, 13); 314 | this.firstPercentileMetricLabel.TabIndex = 16; 315 | this.firstPercentileMetricLabel.Text = "99 Percentile:"; 316 | // 317 | // secondPercentileMetricLabel 318 | // 319 | this.secondPercentileMetricLabel.AutoSize = true; 320 | this.secondPercentileMetricLabel.Location = new System.Drawing.Point(219, 25); 321 | this.secondPercentileMetricLabel.Margin = new System.Windows.Forms.Padding(0); 322 | this.secondPercentileMetricLabel.Name = "secondPercentileMetricLabel"; 323 | this.secondPercentileMetricLabel.Size = new System.Drawing.Size(81, 13); 324 | this.secondPercentileMetricLabel.TabIndex = 17; 325 | this.secondPercentileMetricLabel.Text = "99.9 Percentile:"; 326 | // 327 | // firstPercentileInterval 328 | // 329 | this.firstPercentileInterval.AutoSize = true; 330 | this.firstPercentileInterval.Location = new System.Drawing.Point(300, 2); 331 | this.firstPercentileInterval.Margin = new System.Windows.Forms.Padding(0, 0, 10, 0); 332 | this.firstPercentileInterval.Name = "firstPercentileInterval"; 333 | this.firstPercentileInterval.Size = new System.Drawing.Size(13, 13); 334 | this.firstPercentileInterval.TabIndex = 18; 335 | this.firstPercentileInterval.Text = "0"; 336 | // 337 | // secondPercentileInterval 338 | // 339 | this.secondPercentileInterval.AutoSize = true; 340 | this.secondPercentileInterval.Location = new System.Drawing.Point(300, 25); 341 | this.secondPercentileInterval.Margin = new System.Windows.Forms.Padding(0, 0, 10, 0); 342 | this.secondPercentileInterval.Name = "secondPercentileInterval"; 343 | this.secondPercentileInterval.Size = new System.Drawing.Size(13, 13); 344 | this.secondPercentileInterval.TabIndex = 19; 345 | this.secondPercentileInterval.Text = "0"; 346 | // 347 | // groupBox1 348 | // 349 | this.groupBox1.Controls.Add(this.comboBoxPlotType); 350 | this.groupBox1.Location = new System.Drawing.Point(3, 3); 351 | this.groupBox1.Name = "groupBox1"; 352 | this.groupBox1.Size = new System.Drawing.Size(210, 71); 353 | this.groupBox1.TabIndex = 4; 354 | this.groupBox1.TabStop = false; 355 | this.groupBox1.Text = "Plot Type"; 356 | // 357 | // comboBoxPlotType 358 | // 359 | this.comboBoxPlotType.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; 360 | this.comboBoxPlotType.FormattingEnabled = true; 361 | this.comboBoxPlotType.Items.AddRange(new object[] { 362 | "xCount vs. Time", 363 | "yCount vs. Time", 364 | "xyCount vs. Time", 365 | "Interval vs. Time", 366 | "Frequency vs. Time", 367 | "xVelocity vs. Time", 368 | "yVelocity vs. Time", 369 | "xyVelocity vs. Time", 370 | "X vs. Y"}); 371 | this.comboBoxPlotType.Location = new System.Drawing.Point(6, 30); 372 | this.comboBoxPlotType.Name = "comboBoxPlotType"; 373 | this.comboBoxPlotType.Size = new System.Drawing.Size(200, 21); 374 | this.comboBoxPlotType.TabIndex = 1; 375 | // 376 | // groupBox2 377 | // 378 | this.groupBox2.Controls.Add(this.numericUpDownStart); 379 | this.groupBox2.Location = new System.Drawing.Point(219, 3); 380 | this.groupBox2.Name = "groupBox2"; 381 | this.groupBox2.Size = new System.Drawing.Size(135, 71); 382 | this.groupBox2.TabIndex = 5; 383 | this.groupBox2.TabStop = false; 384 | this.groupBox2.Text = "Time Start (ms)"; 385 | // 386 | // numericUpDownStart 387 | // 388 | this.numericUpDownStart.Location = new System.Drawing.Point(6, 30); 389 | this.numericUpDownStart.Name = "numericUpDownStart"; 390 | this.numericUpDownStart.Size = new System.Drawing.Size(120, 20); 391 | this.numericUpDownStart.TabIndex = 3; 392 | // 393 | // groupBox3 394 | // 395 | this.groupBox3.Controls.Add(this.numericUpDownEnd); 396 | this.groupBox3.Location = new System.Drawing.Point(360, 3); 397 | this.groupBox3.Name = "groupBox3"; 398 | this.groupBox3.Size = new System.Drawing.Size(135, 71); 399 | this.groupBox3.TabIndex = 6; 400 | this.groupBox3.TabStop = false; 401 | this.groupBox3.Text = "Time End (ms)"; 402 | // 403 | // numericUpDownEnd 404 | // 405 | this.numericUpDownEnd.Location = new System.Drawing.Point(6, 30); 406 | this.numericUpDownEnd.Name = "numericUpDownEnd"; 407 | this.numericUpDownEnd.Size = new System.Drawing.Size(120, 20); 408 | this.numericUpDownEnd.TabIndex = 2; 409 | // 410 | // tableLayoutPanel2 411 | // 412 | this.tableLayoutPanel2.ColumnCount = 1; 413 | this.tableLayoutPanel2.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); 414 | this.tableLayoutPanel2.Controls.Add(this.checkBoxLines, 0, 0); 415 | this.tableLayoutPanel2.Controls.Add(this.checkBoxStem, 0, 1); 416 | this.tableLayoutPanel2.Location = new System.Drawing.Point(844, 3); 417 | this.tableLayoutPanel2.Name = "tableLayoutPanel2"; 418 | this.tableLayoutPanel2.RowCount = 2; 419 | this.tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle()); 420 | this.tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle()); 421 | this.tableLayoutPanel2.Size = new System.Drawing.Size(66, 63); 422 | this.tableLayoutPanel2.TabIndex = 7; 423 | // 424 | // checkBoxLines 425 | // 426 | this.checkBoxLines.AutoSize = true; 427 | this.checkBoxLines.Location = new System.Drawing.Point(3, 15); 428 | this.checkBoxLines.Margin = new System.Windows.Forms.Padding(3, 15, 3, 3); 429 | this.checkBoxLines.Name = "checkBoxLines"; 430 | this.checkBoxLines.Size = new System.Drawing.Size(51, 17); 431 | this.checkBoxLines.TabIndex = 9; 432 | this.checkBoxLines.Text = "Lines"; 433 | this.checkBoxLines.UseVisualStyleBackColor = true; 434 | // 435 | // checkBoxStem 436 | // 437 | this.checkBoxStem.AutoSize = true; 438 | this.checkBoxStem.Location = new System.Drawing.Point(3, 38); 439 | this.checkBoxStem.Name = "checkBoxStem"; 440 | this.checkBoxStem.Size = new System.Drawing.Size(50, 17); 441 | this.checkBoxStem.TabIndex = 8; 442 | this.checkBoxStem.Text = "Stem"; 443 | this.checkBoxStem.UseVisualStyleBackColor = true; 444 | // 445 | // buttonSavePNG 446 | // 447 | this.buttonSavePNG.Anchor = System.Windows.Forms.AnchorStyles.Right; 448 | this.buttonSavePNG.Location = new System.Drawing.Point(1209, 27); 449 | this.buttonSavePNG.Margin = new System.Windows.Forms.Padding(3, 3, 9, 3); 450 | this.buttonSavePNG.Name = "buttonSavePNG"; 451 | this.buttonSavePNG.Size = new System.Drawing.Size(75, 23); 452 | this.buttonSavePNG.TabIndex = 7; 453 | this.buttonSavePNG.Text = "SavePNG"; 454 | this.buttonSavePNG.UseVisualStyleBackColor = true; 455 | this.buttonSavePNG.Click += new System.EventHandler(this.buttonSavePNG_Click); 456 | // 457 | // MousePlot 458 | // 459 | this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); 460 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; 461 | this.AutoSize = true; 462 | this.ClientSize = new System.Drawing.Size(1299, 791); 463 | this.Controls.Add(this.splitContainer1); 464 | this.MinimumSize = new System.Drawing.Size(1315, 830); 465 | this.Name = "MousePlot"; 466 | this.WindowState = System.Windows.Forms.FormWindowState.Maximized; 467 | this.splitContainer1.Panel1.ResumeLayout(false); 468 | this.splitContainer1.Panel2.ResumeLayout(false); 469 | ((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).EndInit(); 470 | this.splitContainer1.ResumeLayout(false); 471 | this.tableLayoutPanel1.ResumeLayout(false); 472 | this.tableLayoutPanel1.PerformLayout(); 473 | this.statisticsGroupBox.ResumeLayout(false); 474 | this.statisticsGroupBox.PerformLayout(); 475 | this.tableLayoutPanel3.ResumeLayout(false); 476 | this.tableLayoutPanel3.PerformLayout(); 477 | this.groupBox1.ResumeLayout(false); 478 | this.groupBox2.ResumeLayout(false); 479 | ((System.ComponentModel.ISupportInitialize)(this.numericUpDownStart)).EndInit(); 480 | this.groupBox3.ResumeLayout(false); 481 | ((System.ComponentModel.ISupportInitialize)(this.numericUpDownEnd)).EndInit(); 482 | this.tableLayoutPanel2.ResumeLayout(false); 483 | this.tableLayoutPanel2.PerformLayout(); 484 | this.ResumeLayout(false); 485 | 486 | } 487 | 488 | #endregion 489 | 490 | private System.Windows.Forms.SplitContainer splitContainer1; 491 | private OxyPlot.WindowsForms.Plot plot1; 492 | private System.Windows.Forms.ComboBox comboBoxPlotType; 493 | private System.Windows.Forms.NumericUpDown numericUpDownStart; 494 | private System.Windows.Forms.NumericUpDown numericUpDownEnd; 495 | private System.Windows.Forms.GroupBox groupBox3; 496 | private System.Windows.Forms.GroupBox groupBox2; 497 | private System.Windows.Forms.GroupBox groupBox1; 498 | private System.Windows.Forms.Button buttonSavePNG; 499 | private System.Windows.Forms.CheckBox checkBoxStem; 500 | private System.Windows.Forms.CheckBox checkBoxLines; 501 | private System.Windows.Forms.GroupBox statisticsGroupBox; 502 | private System.Windows.Forms.TableLayoutPanel tableLayoutPanel1; 503 | private System.Windows.Forms.TableLayoutPanel tableLayoutPanel2; 504 | private System.Windows.Forms.TableLayoutPanel tableLayoutPanel3; 505 | private System.Windows.Forms.Label label4; 506 | private System.Windows.Forms.Label label2; 507 | private System.Windows.Forms.Label maxInterval; 508 | private System.Windows.Forms.Label avgInterval; 509 | private System.Windows.Forms.Label label11; 510 | private System.Windows.Forms.Label label12; 511 | private System.Windows.Forms.Label rangeInterval; 512 | private System.Windows.Forms.Label medianInterval; 513 | private System.Windows.Forms.Label label14; 514 | private System.Windows.Forms.Label label13; 515 | private System.Windows.Forms.Label minInterval; 516 | private System.Windows.Forms.Label stdevInterval; 517 | private System.Windows.Forms.Label firstPercentileMetricLabel; 518 | private System.Windows.Forms.Label secondPercentileMetricLabel; 519 | private System.Windows.Forms.Label firstPercentileInterval; 520 | private System.Windows.Forms.Label secondPercentileInterval; 521 | } 522 | } -------------------------------------------------------------------------------- /MouseTester/MouseTester/MousePlot.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel; 4 | using System.Data; 5 | using System.Drawing; 6 | using System.Linq; 7 | using System.Text; 8 | using System.Windows.Forms; 9 | using OxyPlot; 10 | 11 | namespace MouseTester 12 | { 13 | using System.Drawing; 14 | using System.Drawing.Imaging; 15 | using OxyPlot.WindowsForms; 16 | using OxyPlot.Annotations; 17 | using OxyPlot.Axes; 18 | using OxyPlot.Series; 19 | 20 | public partial class MousePlot : Form 21 | { 22 | private MouseLog mlog; 23 | private int last_start; 24 | private double last_start_time; 25 | private int last_end; 26 | private double last_end_time; 27 | double x_min; 28 | double x_max; 29 | double y_min; 30 | double y_max; 31 | private string xlabel = ""; 32 | private string ylabel = ""; 33 | 34 | public MousePlot(MouseLog Mlog) 35 | { 36 | this.Text = $"MousePlot v{Program.version}"; 37 | InitializeComponent(); 38 | this.mlog = new MouseLog(); 39 | this.mlog.Desc = Mlog.Desc; 40 | this.mlog.Cpi = Mlog.Cpi; 41 | int i = 1; 42 | int x = Mlog.Events[0].lastx; 43 | int y = Mlog.Events[0].lasty; 44 | ushort buttonflags = Mlog.Events[0].buttonflags; 45 | double ts = Mlog.Events[0].ts; 46 | while (i < Mlog.Events.Count) 47 | { 48 | this.mlog.Add(new MouseEvent(buttonflags, x, y, ts)); 49 | x = Mlog.Events[i].lastx; 50 | y = Mlog.Events[i].lasty; 51 | buttonflags = Mlog.Events[i].buttonflags; 52 | ts = Mlog.Events[i].ts; 53 | i++; 54 | } 55 | this.mlog.Add(new MouseEvent(buttonflags, x, y, ts)); 56 | 57 | this.last_end = mlog.Events.Count - 1; 58 | this.last_end_time = Mlog.Events[this.last_end].ts; 59 | 60 | this.last_start_time = mlog.Events[last_end].ts > 100 ? 100 : 0; 61 | initialize_plot(); 62 | 63 | this.comboBoxPlotType.SelectedIndex = 0; 64 | this.comboBoxPlotType.SelectedIndexChanged += new System.EventHandler(this.comboBox1_SelectedIndexChanged); 65 | 66 | this.numericUpDownStart.Minimum = 0; 67 | this.numericUpDownStart.Maximum = (decimal)last_end_time; 68 | this.numericUpDownStart.Value = (decimal)last_start_time; 69 | this.numericUpDownStart.DecimalPlaces = 3; 70 | this.numericUpDownStart.Increment = 10; 71 | this.numericUpDownStart.ValueChanged += new System.EventHandler(this.numericUpDownStart_ValueChanged); 72 | 73 | this.numericUpDownEnd.Minimum = 0; 74 | this.numericUpDownEnd.Maximum = (decimal)last_end_time; 75 | this.numericUpDownEnd.Value = (decimal)last_end_time; 76 | this.numericUpDownEnd.DecimalPlaces = 3; 77 | this.numericUpDownEnd.Increment = 10; 78 | this.numericUpDownEnd.ValueChanged += new System.EventHandler(this.numericUpDownEnd_ValueChanged); 79 | 80 | this.checkBoxStem.Checked = false; 81 | this.checkBoxStem.CheckedChanged += new System.EventHandler(this.checkBoxStem_CheckedChanged); 82 | 83 | this.checkBoxLines.Checked = false; 84 | this.checkBoxLines.CheckedChanged += new System.EventHandler(this.checkBoxLines_CheckedChanged); 85 | 86 | // open MousePlot to "Interval vs. Time" selection 87 | comboBoxPlotType.SelectedItem = "Interval vs. Time"; 88 | 89 | refresh_plot(); 90 | } 91 | 92 | private void initialize_plot() 93 | { 94 | var pm = new PlotModel(this.mlog.Desc.ToString()) 95 | { 96 | PlotType = PlotType.Cartesian, 97 | Background = OxyColors.White, 98 | Subtitle = this.mlog.Cpi.ToString() + " cpi" 99 | }; 100 | plot1.Model = pm; 101 | } 102 | 103 | private void refresh_plot() 104 | { 105 | for (int j = 0; j < mlog.Events.Count; j++) { 106 | double currentTimeStamp = mlog.Events[j].ts; 107 | 108 | if (currentTimeStamp >= last_start_time) { 109 | last_start = j; 110 | last_start_time = currentTimeStamp; 111 | numericUpDownStart.Value = (decimal)currentTimeStamp; 112 | break; 113 | } 114 | } 115 | 116 | for (int j = mlog.Events.Count - 1; j >= 0; j--) { 117 | double currentTimeStamp = mlog.Events[j].ts; 118 | 119 | if (currentTimeStamp <= last_end_time) { 120 | last_end = j; 121 | last_end_time = currentTimeStamp; 122 | numericUpDownEnd.Value = (decimal)currentTimeStamp; 123 | break; 124 | } 125 | } 126 | 127 | PlotModel pm = plot1.Model; 128 | pm.Series.Clear(); 129 | pm.Axes.Clear(); 130 | OxyColor singleLineColor = OxyColors.Green; 131 | 132 | var scatterSeries1 = new ScatterSeries 133 | { 134 | BinSize = 8, 135 | MarkerFill = OxyColors.Blue, 136 | MarkerSize = 1.5, 137 | MarkerStroke = OxyColors.Blue, 138 | MarkerStrokeThickness = 1.0, 139 | MarkerType = MarkerType.Circle 140 | }; 141 | 142 | var scatterSeries2 = new ScatterSeries 143 | { 144 | BinSize = 8, 145 | MarkerFill = OxyColors.Red, 146 | MarkerSize = 1.5, 147 | MarkerStroke = OxyColors.Red, 148 | MarkerStrokeThickness = 1.0, 149 | MarkerType = MarkerType.Circle 150 | }; 151 | 152 | var lineSeries1 = new LineSeries 153 | { 154 | Color = OxyColors.Blue, 155 | LineStyle = LineStyle.Solid, 156 | StrokeThickness = 2.0, 157 | Smooth = true 158 | }; 159 | 160 | var lineSeries2 = new LineSeries 161 | { 162 | Color = OxyColors.Red, 163 | LineStyle = LineStyle.Solid, 164 | StrokeThickness = 2.0, 165 | Smooth = true 166 | }; 167 | 168 | if (checkBoxLines.Checked) 169 | { 170 | lineSeries1.Smooth = false; 171 | lineSeries2.Smooth = false; 172 | } 173 | 174 | var stemSeries1 = new StemSeries 175 | { 176 | Color = OxyColors.Blue, 177 | StrokeThickness = 1.0, 178 | }; 179 | 180 | var stemSeries2 = new StemSeries 181 | { 182 | Color = OxyColors.Red, 183 | StrokeThickness = 1.0 184 | }; 185 | 186 | // Only display statistics when "Interval" or "Frequency" is selected 187 | statisticsGroupBox.Visible = comboBoxPlotType.Text.Contains("Interval") || comboBoxPlotType.Text.Contains("Frequency"); 188 | 189 | if (comboBoxPlotType.Text.Contains("xyCount")) 190 | { 191 | plot_xycounts_vs_time(scatterSeries1, scatterSeries2, stemSeries1, stemSeries2, lineSeries1, lineSeries2); 192 | pm.Series.Add(scatterSeries1); 193 | pm.Series.Add(scatterSeries2); 194 | if (!checkBoxLines.Checked) 195 | { 196 | plot_fit(scatterSeries1, lineSeries1); 197 | plot_fit(scatterSeries2, lineSeries2); 198 | } 199 | pm.Series.Add(lineSeries1); 200 | pm.Series.Add(lineSeries2); 201 | if (checkBoxStem.Checked) 202 | { 203 | pm.Series.Add(stemSeries1); 204 | pm.Series.Add(stemSeries2); 205 | } 206 | } 207 | else if (comboBoxPlotType.Text.Contains("xCount")) 208 | { 209 | plot_xcounts_vs_time(scatterSeries1, stemSeries1, lineSeries1); 210 | pm.Series.Add(scatterSeries1); 211 | if (!checkBoxLines.Checked) 212 | { 213 | plot_fit(scatterSeries1, lineSeries1); 214 | lineSeries1.Color = singleLineColor; 215 | } 216 | pm.Series.Add(lineSeries1); 217 | if (checkBoxStem.Checked) 218 | { 219 | pm.Series.Add(stemSeries1); 220 | } 221 | } 222 | else if (comboBoxPlotType.Text.Contains("yCount")) 223 | { 224 | plot_ycounts_vs_time(scatterSeries1, stemSeries1, lineSeries1); 225 | pm.Series.Add(scatterSeries1); 226 | if (!checkBoxLines.Checked) 227 | { 228 | plot_fit(scatterSeries1, lineSeries1); 229 | lineSeries1.Color = singleLineColor; 230 | } 231 | pm.Series.Add(lineSeries1); 232 | if (checkBoxStem.Checked) 233 | { 234 | pm.Series.Add(stemSeries1); 235 | } 236 | 237 | } 238 | else if (comboBoxPlotType.Text.Contains("Interval") || comboBoxPlotType.Text.Contains("Frequency")) 239 | { 240 | plot_interval_vs_time(scatterSeries1, stemSeries1, lineSeries1, comboBoxPlotType.Text.Contains("Interval"), (value) => comboBoxPlotType.Text.Contains("Interval") ? value : 1000 / value); 241 | pm.Series.Add(scatterSeries1); 242 | if (!checkBoxLines.Checked) 243 | { 244 | plot_fit(scatterSeries1, lineSeries1); 245 | lineSeries1.Color = singleLineColor; 246 | } 247 | pm.Series.Add(lineSeries1); 248 | if (checkBoxStem.Checked) 249 | { 250 | pm.Series.Add(stemSeries1); 251 | } 252 | 253 | } 254 | else if (comboBoxPlotType.Text.Contains("xyVelocity")) 255 | { 256 | plot_xyvelocity_vs_time(scatterSeries1, scatterSeries2, stemSeries1, stemSeries2, lineSeries1, lineSeries2); 257 | pm.Series.Add(scatterSeries1); 258 | pm.Series.Add(scatterSeries2); 259 | if (!checkBoxLines.Checked) 260 | { 261 | plot_fit(scatterSeries1, lineSeries1); 262 | plot_fit(scatterSeries2, lineSeries2); 263 | } 264 | pm.Series.Add(lineSeries1); 265 | pm.Series.Add(lineSeries2); 266 | if (checkBoxStem.Checked) 267 | { 268 | pm.Series.Add(stemSeries1); 269 | pm.Series.Add(stemSeries2); 270 | } 271 | 272 | } 273 | else if (comboBoxPlotType.Text.Contains("xVelocity")) 274 | { 275 | plot_xvelocity_vs_time(scatterSeries1, stemSeries1, lineSeries1); 276 | pm.Series.Add(scatterSeries1); 277 | if (!checkBoxLines.Checked) 278 | { 279 | plot_fit(scatterSeries1, lineSeries1); 280 | lineSeries1.Color = singleLineColor; 281 | } 282 | pm.Series.Add(lineSeries1); 283 | if (checkBoxStem.Checked) 284 | { 285 | pm.Series.Add(stemSeries1); 286 | } 287 | } 288 | else if (comboBoxPlotType.Text.Contains("yVelocity")) 289 | { 290 | plot_yvelocity_vs_time(scatterSeries1, stemSeries1, lineSeries1); 291 | pm.Series.Add(scatterSeries1); 292 | if (!checkBoxLines.Checked) 293 | { 294 | plot_fit(scatterSeries1, lineSeries1); 295 | lineSeries1.Color = singleLineColor; 296 | } 297 | pm.Series.Add(lineSeries1); 298 | if (checkBoxStem.Checked) 299 | { 300 | pm.Series.Add(stemSeries1); 301 | } 302 | } 303 | else if (comboBoxPlotType.Text.Contains("X vs. Y")) 304 | { 305 | plot_x_vs_y(scatterSeries1, lineSeries1); 306 | pm.Series.Add(scatterSeries1); 307 | if (checkBoxLines.Checked) 308 | { 309 | pm.Series.Add(lineSeries1); 310 | lineSeries1.Color = singleLineColor; 311 | } 312 | } 313 | 314 | var linearAxis1 = new LinearAxis(); 315 | linearAxis1.AbsoluteMinimum = x_min - (x_max - x_min) / 20.0; 316 | linearAxis1.AbsoluteMaximum = x_max + (x_max - x_min) / 20.0; 317 | linearAxis1.MajorGridlineColor = OxyColor.FromArgb(40, 0, 0, 139); 318 | linearAxis1.MajorGridlineStyle = LineStyle.Solid; 319 | linearAxis1.MinorGridlineColor = OxyColor.FromArgb(20, 0, 0, 139); 320 | linearAxis1.MinorGridlineStyle = LineStyle.Solid; 321 | linearAxis1.Position = AxisPosition.Bottom; 322 | linearAxis1.Title = xlabel; 323 | pm.Axes.Add(linearAxis1); 324 | 325 | var linearAxis2 = new LinearAxis(); 326 | linearAxis2.AbsoluteMinimum = y_min - (y_max - y_min) / 20.0; 327 | linearAxis2.AbsoluteMaximum = y_max + (y_max - y_min) / 20.0; 328 | linearAxis2.MajorGridlineColor = OxyColor.FromArgb(40, 0, 0, 139); 329 | linearAxis2.MajorGridlineStyle = LineStyle.Solid; 330 | linearAxis2.MinorGridlineColor = OxyColor.FromArgb(20, 0, 0, 139); 331 | linearAxis2.MinorGridlineStyle = LineStyle.Solid; 332 | linearAxis2.Title = ylabel; 333 | pm.Axes.Add(linearAxis2); 334 | 335 | plot1.RefreshPlot(true); 336 | } 337 | 338 | private void reset_minmax() 339 | { 340 | x_min = double.MaxValue; 341 | x_max = double.MinValue; 342 | y_min = double.MaxValue; 343 | y_max = double.MinValue; 344 | } 345 | 346 | private void update_minmax(double x, double y) 347 | { 348 | if (x < x_min) 349 | { 350 | x_min = x; 351 | } 352 | if (x > x_max) 353 | { 354 | x_max = x; 355 | } 356 | if (y < y_min) 357 | { 358 | y_min = y; 359 | } 360 | if (y > y_max) 361 | { 362 | y_max = y; 363 | } 364 | } 365 | 366 | private void plot_xcounts_vs_time(ScatterSeries scatterSeries1, StemSeries stemSeries1, LineSeries lineSeries1) 367 | { 368 | xlabel = "Time (ms)"; 369 | ylabel = "xCounts"; 370 | reset_minmax(); 371 | for (int i = last_start; i <= last_end; i++) 372 | { 373 | double x = this.mlog.Events[i].ts; 374 | double y = this.mlog.Events[i].lastx; 375 | update_minmax(x, y); 376 | scatterSeries1.Points.Add(new ScatterPoint(x, y)); 377 | lineSeries1.Points.Add(new DataPoint(x, y)); 378 | stemSeries1.Points.Add(new DataPoint(x, y)); 379 | } 380 | } 381 | 382 | private void plot_ycounts_vs_time(ScatterSeries scatterSeries1, StemSeries stemSeries1, LineSeries lineSeries1) 383 | { 384 | xlabel = "Time (ms)"; 385 | ylabel = "yCounts"; 386 | reset_minmax(); 387 | for (int i = last_start; i <= last_end; i++) 388 | { 389 | double x = this.mlog.Events[i].ts; 390 | double y = this.mlog.Events[i].lasty; 391 | update_minmax(x, y); 392 | scatterSeries1.Points.Add(new ScatterPoint(x, y)); 393 | lineSeries1.Points.Add(new DataPoint(x, y)); 394 | stemSeries1.Points.Add(new DataPoint(x, y)); 395 | } 396 | } 397 | 398 | private void plot_xycounts_vs_time(ScatterSeries scatterSeries1, ScatterSeries scatterSeries2, StemSeries stemSeries1, StemSeries stemSeries2, LineSeries lineSeries1, LineSeries lineSeries2) 399 | { 400 | xlabel = "Time (ms)"; 401 | ylabel = "Counts [x = Blue, y = Red]"; 402 | reset_minmax(); 403 | for (int i = last_start; i <= last_end; i++) 404 | { 405 | double x = this.mlog.Events[i].ts; 406 | double y = this.mlog.Events[i].lastx; 407 | update_minmax(x, y); 408 | scatterSeries1.Points.Add(new ScatterPoint(x, y)); 409 | lineSeries1.Points.Add(new DataPoint(x, y)); 410 | stemSeries1.Points.Add(new DataPoint(x, y)); 411 | } 412 | 413 | for (int i = last_start; i <= last_end; i++) 414 | { 415 | double x = this.mlog.Events[i].ts; 416 | double y = this.mlog.Events[i].lasty; 417 | update_minmax(x, y); 418 | scatterSeries2.Points.Add(new ScatterPoint(x, y)); 419 | lineSeries2.Points.Add(new DataPoint(x, y)); 420 | stemSeries2.Points.Add(new DataPoint(x, y)); 421 | } 422 | } 423 | 424 | private void plot_interval_vs_time(ScatterSeries scatterSeries1, StemSeries stemSeries1, LineSeries lineSeries1, bool isInterval, Func transformFunction) 425 | { 426 | xlabel = "Time (ms)"; 427 | 428 | double firstPercentileMetric; 429 | double secondPercentileMetric; 430 | 431 | if (isInterval) { 432 | ylabel = "Update Time (ms)"; 433 | firstPercentileMetric = 99; 434 | firstPercentileMetricLabel.Text = "99 Percentile:"; 435 | secondPercentileMetric = 99.9; 436 | secondPercentileMetricLabel.Text = "99.9 Percentile:"; 437 | } else { 438 | ylabel = "Frequency (Hz)"; 439 | firstPercentileMetric = 1; 440 | firstPercentileMetricLabel.Text = "1 Percentile:"; 441 | secondPercentileMetric = 0.1; 442 | secondPercentileMetricLabel.Text = "0.1 Percentile:"; 443 | } 444 | 445 | List intervals = new List(); 446 | reset_minmax(); 447 | for (int i = last_start; i <= last_end; i++) 448 | { 449 | double x = this.mlog.Events[i].ts; 450 | double y; 451 | if (i == 0) 452 | { 453 | y = 0.0; 454 | } 455 | else 456 | { 457 | y = this.mlog.Events[i].ts - this.mlog.Events[i - 1].ts; 458 | } 459 | intervals.Add(y); 460 | update_minmax(x, transformFunction(y)); 461 | scatterSeries1.Points.Add(new ScatterPoint(x, transformFunction(y))); 462 | lineSeries1.Points.Add(new DataPoint(x, transformFunction(y))); 463 | stemSeries1.Points.Add(new DataPoint(x, transformFunction(y))); 464 | } 465 | 466 | // Calculate statistics 467 | 468 | List intervals_descending = intervals.OrderByDescending(x => x).ToList(); 469 | List intervals_ascending = intervals.OrderBy(x => x).ToList(); 470 | 471 | double sum = intervals_descending.Sum(); 472 | int count = intervals_descending.Count(); 473 | double average = transformFunction(sum / count); 474 | double squared_deviations = 0.0; 475 | int middle_index = count / 2; 476 | int last_index = count - 1; 477 | double range = intervals_descending[0] - intervals_descending[last_index]; 478 | 479 | foreach (double interval in intervals) 480 | { 481 | squared_deviations += Math.Pow(transformFunction(interval) - average, 2); 482 | } 483 | double maximumValue = transformFunction(isInterval ? intervals_descending[0] : intervals_descending[last_index]); 484 | double minimumValue = transformFunction(isInterval ? intervals_descending[last_index] : intervals_descending[0]); 485 | 486 | maxInterval.Text = $"{maximumValue:0.0000####}"; 487 | minInterval.Text = $"{minimumValue:0.0000####}"; 488 | avgInterval.Text = $"{average:0.0000####}"; 489 | stdevInterval.Text = $"{Math.Sqrt(squared_deviations / (last_index)):0.0000####}"; 490 | rangeInterval.Text = $"{maximumValue - minimumValue:0.0000####}"; 491 | medianInterval.Text = $"{(transformFunction(count % 2 == 1 ? intervals_descending[middle_index] : (intervals_descending[middle_index - 1] + intervals_descending[middle_index]) / 2)):0.0000####}"; 492 | 493 | List percentiles_list = isInterval ? intervals_ascending : intervals_descending; 494 | 495 | firstPercentileInterval.Text = $"{transformFunction(percentiles_list[(int) Math.Ceiling(firstPercentileMetric / 100 * count) - 1]):0.0000####}"; 496 | secondPercentileInterval.Text = $"{transformFunction(percentiles_list[(int)Math.Ceiling(secondPercentileMetric / 100 * count) - 1]):0.0000####}"; 497 | } 498 | 499 | private void plot_xvelocity_vs_time(ScatterSeries scatterSeries1, StemSeries stemSeries1, LineSeries lineSeries1) 500 | { 501 | xlabel = "Time (ms)"; 502 | ylabel = "xVelocity (m/s)"; 503 | reset_minmax(); 504 | if (this.mlog.Cpi > 0) 505 | { 506 | for (int i = last_start; i <= last_end; i++) 507 | { 508 | double x = this.mlog.Events[i].ts; 509 | double y; 510 | if (i == 0) 511 | { 512 | y = 0.0; 513 | } 514 | else 515 | { 516 | y = (this.mlog.Events[i].lastx) / (this.mlog.Events[i].ts - this.mlog.Events[i - 1].ts) / this.mlog.Cpi * 25.4; 517 | } 518 | update_minmax(x, y); 519 | scatterSeries1.Points.Add(new ScatterPoint(x, y)); 520 | lineSeries1.Points.Add(new DataPoint(x, y)); 521 | stemSeries1.Points.Add(new DataPoint(x, y)); 522 | } 523 | } 524 | else 525 | { 526 | MessageBox.Show("CPI value is invalid, please run Measure"); 527 | } 528 | } 529 | 530 | private void plot_yvelocity_vs_time(ScatterSeries scatterSeries1, StemSeries stemSeries1, LineSeries lineSeries1) 531 | { 532 | xlabel = "Time (ms)"; 533 | ylabel = "yVelocity (m/s)"; 534 | reset_minmax(); 535 | if (this.mlog.Cpi > 0) 536 | { 537 | for (int i = last_start; i <= last_end; i++) 538 | { 539 | double x = this.mlog.Events[i].ts; 540 | double y; 541 | if (i == 0) 542 | { 543 | y = 0.0; 544 | } 545 | else 546 | { 547 | y = (this.mlog.Events[i].lasty) / (this.mlog.Events[i].ts - this.mlog.Events[i - 1].ts) / this.mlog.Cpi * 25.4; 548 | } 549 | update_minmax(x, y); 550 | scatterSeries1.Points.Add(new ScatterPoint(x, y)); 551 | lineSeries1.Points.Add(new DataPoint(x, y)); 552 | stemSeries1.Points.Add(new DataPoint(x, y)); 553 | } 554 | } 555 | else 556 | { 557 | MessageBox.Show("CPI value is invalid, please run Measure"); 558 | } 559 | } 560 | 561 | private void plot_xyvelocity_vs_time(ScatterSeries scatterSeries1, ScatterSeries scatterSeries2, StemSeries stemSeries1, StemSeries stemSeries2, LineSeries lineSeries1, LineSeries lineSeries2) 562 | { 563 | xlabel = "Time (ms)"; 564 | ylabel = "Velocity (m/s) [x = Blue, y = Red]"; 565 | reset_minmax(); 566 | if (this.mlog.Cpi > 0) 567 | { 568 | for (int i = last_start; i <= last_end; i++) 569 | { 570 | double x = this.mlog.Events[i].ts; 571 | double y; 572 | if (i == 0) 573 | { 574 | y = 0.0; 575 | } 576 | else 577 | { 578 | y = (this.mlog.Events[i].lastx) / (this.mlog.Events[i].ts - this.mlog.Events[i - 1].ts) / this.mlog.Cpi * 25.4; 579 | } 580 | update_minmax(x, y); 581 | scatterSeries1.Points.Add(new ScatterPoint(x, y)); 582 | lineSeries1.Points.Add(new DataPoint(x, y)); 583 | stemSeries1.Points.Add(new DataPoint(x, y)); 584 | } 585 | 586 | for (int i = last_start; i <= last_end; i++) 587 | { 588 | double x = this.mlog.Events[i].ts; 589 | double y; 590 | if (i == 0) 591 | { 592 | y = 0.0; 593 | } 594 | else 595 | { 596 | y = (this.mlog.Events[i].lasty) / (this.mlog.Events[i].ts - this.mlog.Events[i - 1].ts) / this.mlog.Cpi * 25.4; 597 | } 598 | update_minmax(x, y); 599 | scatterSeries2.Points.Add(new ScatterPoint(x, y)); 600 | lineSeries2.Points.Add(new DataPoint(x, y)); 601 | stemSeries2.Points.Add(new DataPoint(x, y)); 602 | } 603 | } 604 | else 605 | { 606 | MessageBox.Show("CPI value is invalid, please run Measure"); 607 | } 608 | } 609 | 610 | private void plot_x_vs_y(ScatterSeries scatterSeries1, LineSeries lineSeries1) 611 | { 612 | xlabel = "xCounts"; 613 | ylabel = "yCounts"; 614 | reset_minmax(); 615 | double x = 0.0; 616 | double y = 0.0; 617 | for (int i = last_start; i <= last_end; i++) 618 | { 619 | x += this.mlog.Events[i].lastx; 620 | y += this.mlog.Events[i].lasty; 621 | update_minmax(x, x); 622 | update_minmax(y, y); 623 | scatterSeries1.Points.Add(new ScatterPoint(x, y)); 624 | lineSeries1.Points.Add(new DataPoint(x, y)); 625 | } 626 | } 627 | 628 | #if false 629 | // Window based smoothing 630 | private void plot_fit(ScatterSeries scatterSeries1, LineSeries lineSeries1) 631 | { 632 | double sum = 0.0; 633 | 634 | lineSeries1.Points.Clear(); 635 | 636 | for (int i = 0; ((i < 8) && (i < scatterSeries1.Points.Count)); i++) 637 | //for (int i = 0; ((i < 4) && (i < scatterSeries1.Points.Count)); i++) 638 | { 639 | sum = sum + scatterSeries1.Points[i].Y; 640 | } 641 | 642 | for (int i = 3; i < scatterSeries1.Points.Count - 5; i++) 643 | //for (int i = 1; i < scatterSeries1.Points.Count - 3; i++) 644 | { 645 | double x = (scatterSeries1.Points[i].X + scatterSeries1.Points[i + 1].X) / 2.0; 646 | double y = sum; 647 | lineSeries1.Points.Add(new DataPoint(x, y / 8.0)); 648 | sum = sum - scatterSeries1.Points[i - 3].Y; 649 | sum = sum + scatterSeries1.Points[i + 5].Y; 650 | //lineSeries1.Points.Add(new DataPoint(x, y / 4.0)); 651 | //sum = sum - scatterSeries1.Points[i - 1].Y; 652 | //sum = sum + scatterSeries1.Points[i + 3].Y; 653 | 654 | } 655 | } 656 | #else 657 | // Time based smoothing 658 | private void plot_fit(ScatterSeries scatterSeries1, LineSeries lineSeries1) 659 | { 660 | double hz = 125; 661 | double ms = 1000.0 / hz; 662 | lineSeries1.Points.Clear(); 663 | 664 | int ind = 0; 665 | for (double x = ms; x <= scatterSeries1.Points[scatterSeries1.Points.Count - 1].X; x += ms) 666 | { 667 | double sum = 0.0; 668 | int count = 0; 669 | while (scatterSeries1.Points[ind].X <= x) 670 | { 671 | sum += scatterSeries1.Points[ind++].Y; 672 | count++; 673 | } 674 | lineSeries1.Points.Add(new DataPoint(x - (ms / 2.0), sum / count)); 675 | } 676 | } 677 | #endif 678 | 679 | private void comboBox1_SelectedIndexChanged(object sender, EventArgs e) 680 | { 681 | refresh_plot(); 682 | } 683 | 684 | private void numericUpDownStart_ValueChanged(object sender, EventArgs e) 685 | { 686 | if (numericUpDownStart.Value >= numericUpDownEnd.Value) 687 | { 688 | numericUpDownStart.Value = (decimal)last_start_time; 689 | } 690 | else 691 | { 692 | last_start_time = (double)numericUpDownStart.Value; 693 | refresh_plot(); 694 | } 695 | } 696 | 697 | private void numericUpDownEnd_ValueChanged(object sender, EventArgs e) 698 | { 699 | if (numericUpDownEnd.Value <= numericUpDownStart.Value) 700 | { 701 | numericUpDownEnd.Value = (decimal)last_end_time; 702 | } 703 | else 704 | { 705 | last_end_time = (double)numericUpDownEnd.Value; 706 | refresh_plot(); 707 | } 708 | } 709 | 710 | private void checkBoxStem_CheckedChanged(object sender, EventArgs e) 711 | { 712 | refresh_plot(); 713 | } 714 | 715 | private void checkBoxLines_CheckedChanged(object sender, EventArgs e) 716 | { 717 | refresh_plot(); 718 | } 719 | 720 | private void buttonSavePNG_Click(object sender, EventArgs e) 721 | { 722 | SaveFileDialog saveFileDialog1 = new SaveFileDialog(); 723 | saveFileDialog1.Filter = "PNG Files (*.png)|*.png"; 724 | saveFileDialog1.FilterIndex = 1; 725 | if (saveFileDialog1.ShowDialog() == System.Windows.Forms.DialogResult.OK) 726 | { 727 | Bitmap bitmap = new Bitmap(splitContainer1.Width, splitContainer1.Height); 728 | 729 | using (Graphics graphics = Graphics.FromImage(bitmap)) { 730 | graphics.Clear(Color.White); 731 | splitContainer1.DrawToBitmap(bitmap, new Rectangle(0, 0, splitContainer1.Width, splitContainer1.Height)); 732 | } 733 | 734 | bitmap.Save(saveFileDialog1.FileName, ImageFormat.Png); 735 | } 736 | } 737 | } 738 | } 739 | -------------------------------------------------------------------------------- /MouseTester/MouseTester/MousePlot.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 | -------------------------------------------------------------------------------- /MouseTester/MouseTester/MouseTester.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | x86 7 | 8.0.30703 8 | 2.0 9 | {B0953B29-AF9B-4C37-A2EC-947E98F683B3} 10 | WinExe 11 | Properties 12 | MouseTester 13 | MouseTester 14 | v4.8 15 | 16 | 17 | 512 18 | false 19 | publish\ 20 | true 21 | Disk 22 | false 23 | Foreground 24 | 7 25 | Days 26 | false 27 | false 28 | true 29 | 0 30 | 1.6.1.%2a 31 | false 32 | true 33 | 34 | 35 | 36 | 37 | x86 38 | true 39 | full 40 | false 41 | bin\Debug\ 42 | DEBUG;TRACE 43 | prompt 44 | 4 45 | false 46 | 47 | 48 | x86 49 | pdbonly 50 | true 51 | bin\Release\ 52 | TRACE 53 | prompt 54 | 4 55 | false 56 | 57 | 58 | Internet 59 | 60 | 61 | false 62 | 63 | 64 | Properties\app.manifest 65 | 66 | 67 | true 68 | bin\Debug\ 69 | DEBUG;TRACE 70 | full 71 | AnyCPU 72 | 7.3 73 | prompt 74 | false 75 | 76 | 77 | bin\Release\ 78 | TRACE 79 | true 80 | pdbonly 81 | AnyCPU 82 | 7.3 83 | prompt 84 | false 85 | 86 | 87 | true 88 | bin\x64\Debug\ 89 | DEBUG;TRACE 90 | full 91 | x64 92 | 7.3 93 | prompt 94 | false 95 | 96 | 97 | bin\x64\Release\ 98 | TRACE 99 | true 100 | pdbonly 101 | x64 102 | 7.3 103 | prompt 104 | false 105 | 106 | 107 | 108 | 109 | 110 | 111 | ..\packages\Costura.Fody.5.7.0\lib\netstandard1.0\Costura.dll 112 | 113 | 114 | ..\packages\Microsoft.Win32.Primitives.4.3.0\lib\net46\Microsoft.Win32.Primitives.dll 115 | True 116 | True 117 | 118 | 119 | False 120 | .\OxyPlot.dll 121 | 122 | 123 | False 124 | .\OxyPlot.WindowsForms.dll 125 | 126 | 127 | ..\..\..\..\..\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.8\System.dll 128 | 129 | 130 | ..\packages\System.AppContext.4.3.0\lib\net463\System.AppContext.dll 131 | True 132 | True 133 | 134 | 135 | ..\packages\System.Buffers.4.5.1\lib\net461\System.Buffers.dll 136 | 137 | 138 | 139 | ..\packages\System.Console.4.3.1\lib\net46\System.Console.dll 140 | True 141 | True 142 | 143 | 144 | 145 | ..\packages\System.Diagnostics.DiagnosticSource.8.0.1\lib\net462\System.Diagnostics.DiagnosticSource.dll 146 | 147 | 148 | ..\packages\System.Diagnostics.Tracing.4.3.0\lib\net462\System.Diagnostics.Tracing.dll 149 | True 150 | True 151 | 152 | 153 | ..\packages\System.Globalization.Calendars.4.3.0\lib\net46\System.Globalization.Calendars.dll 154 | True 155 | True 156 | 157 | 158 | ..\packages\System.IO.4.3.0\lib\net462\System.IO.dll 159 | True 160 | True 161 | 162 | 163 | ..\packages\System.IO.Compression.4.3.0\lib\net46\System.IO.Compression.dll 164 | True 165 | True 166 | 167 | 168 | 169 | ..\packages\System.IO.Compression.ZipFile.4.3.0\lib\net46\System.IO.Compression.ZipFile.dll 170 | True 171 | True 172 | 173 | 174 | ..\packages\System.IO.FileSystem.4.3.0\lib\net46\System.IO.FileSystem.dll 175 | True 176 | True 177 | 178 | 179 | ..\packages\System.IO.FileSystem.Primitives.4.3.0\lib\net46\System.IO.FileSystem.Primitives.dll 180 | True 181 | True 182 | 183 | 184 | ..\packages\System.Linq.4.3.0\lib\net463\System.Linq.dll 185 | True 186 | True 187 | 188 | 189 | ..\packages\System.Linq.Expressions.4.3.0\lib\net463\System.Linq.Expressions.dll 190 | True 191 | True 192 | 193 | 194 | ..\packages\System.Memory.4.5.5\lib\net461\System.Memory.dll 195 | 196 | 197 | ..\packages\System.Net.Http.4.3.4\lib\net46\System.Net.Http.dll 198 | True 199 | True 200 | 201 | 202 | ..\packages\System.Net.Sockets.4.3.0\lib\net46\System.Net.Sockets.dll 203 | True 204 | True 205 | 206 | 207 | 208 | ..\packages\System.Numerics.Vectors.4.5.0\lib\net46\System.Numerics.Vectors.dll 209 | 210 | 211 | ..\packages\System.Reflection.4.3.0\lib\net462\System.Reflection.dll 212 | True 213 | True 214 | 215 | 216 | ..\packages\System.Runtime.4.3.1\lib\net462\System.Runtime.dll 217 | True 218 | True 219 | 220 | 221 | ..\packages\System.Runtime.CompilerServices.Unsafe.6.0.0\lib\net461\System.Runtime.CompilerServices.Unsafe.dll 222 | 223 | 224 | ..\packages\System.Runtime.Extensions.4.3.1\lib\net462\System.Runtime.Extensions.dll 225 | True 226 | True 227 | 228 | 229 | ..\packages\System.Runtime.InteropServices.4.3.0\lib\net463\System.Runtime.InteropServices.dll 230 | True 231 | True 232 | 233 | 234 | ..\packages\System.Runtime.InteropServices.RuntimeInformation.4.3.0\lib\net45\System.Runtime.InteropServices.RuntimeInformation.dll 235 | True 236 | True 237 | 238 | 239 | ..\packages\System.Security.Cryptography.Algorithms.4.3.1\lib\net463\System.Security.Cryptography.Algorithms.dll 240 | True 241 | True 242 | 243 | 244 | ..\packages\System.Security.Cryptography.Encoding.4.3.0\lib\net46\System.Security.Cryptography.Encoding.dll 245 | True 246 | True 247 | 248 | 249 | ..\packages\System.Security.Cryptography.Primitives.4.3.0\lib\net46\System.Security.Cryptography.Primitives.dll 250 | True 251 | True 252 | 253 | 254 | ..\packages\System.Security.Cryptography.X509Certificates.4.3.2\lib\net461\System.Security.Cryptography.X509Certificates.dll 255 | True 256 | True 257 | 258 | 259 | ..\packages/System.Text.RegularExpressions.4.3.1/lib/net463/System.Text.RegularExpressions.dll 260 | True 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | ..\packages\System.Xml.ReaderWriter.4.3.1\lib\net46\System.Xml.ReaderWriter.dll 272 | True 273 | True 274 | 275 | 276 | 277 | 278 | Form 279 | 280 | 281 | Form1.cs 282 | 283 | 284 | 285 | 286 | 287 | Form 288 | 289 | 290 | MousePlot.cs 291 | 292 | 293 | 294 | 295 | Form 296 | 297 | 298 | Form1.cs 299 | Designer 300 | 301 | 302 | MousePlot.cs 303 | 304 | 305 | ResXFileCodeGenerator 306 | Resources.Designer.cs 307 | Designer 308 | 309 | 310 | True 311 | Resources.resx 312 | True 313 | 314 | 315 | 316 | 317 | 318 | SettingsSingleFileGenerator 319 | Settings.Designer.cs 320 | 321 | 322 | True 323 | Settings.settings 324 | True 325 | 326 | 327 | 328 | 329 | False 330 | Microsoft .NET Framework 4 Client Profile %28x86 and x64%29 331 | true 332 | 333 | 334 | False 335 | .NET Framework 3.5 SP1 Client Profile 336 | false 337 | 338 | 339 | False 340 | .NET Framework 3.5 SP1 341 | false 342 | 343 | 344 | False 345 | Windows Installer 3.1 346 | true 347 | 348 | 349 | 350 | 351 | 352 | This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. 353 | 354 | 355 | 356 | 357 | 358 | 359 | 360 | 361 | 362 | 369 | -------------------------------------------------------------------------------- /MouseTester/MouseTester/OxyPlot.WindowsForms.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/valleyofdoom/MouseTester/daa5b90896d6e26ccedb30bcf2aa8f97c46c731d/MouseTester/MouseTester/OxyPlot.WindowsForms.dll -------------------------------------------------------------------------------- /MouseTester/MouseTester/OxyPlot.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/valleyofdoom/MouseTester/daa5b90896d6e26ccedb30bcf2aa8f97c46c731d/MouseTester/MouseTester/OxyPlot.dll -------------------------------------------------------------------------------- /MouseTester/MouseTester/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Windows.Forms; 5 | 6 | namespace MouseTester 7 | { 8 | static class Program 9 | { 10 | public static string version = "1.7.2"; 11 | 12 | /// 13 | /// The main entry point for the application. 14 | /// 15 | [STAThread] 16 | static void Main() 17 | { 18 | Application.EnableVisualStyles(); 19 | Application.SetCompatibleTextRenderingDefault(false); 20 | Application.Run(new Form1()); 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /MouseTester/MouseTester/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("MouseTester")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("MouseTester")] 13 | [assembly: AssemblyCopyright("Copyright © 2021")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("057b69bb-f19d-457e-a0a7-a0bc68b8c2c4")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.7.2.0")] 36 | [assembly: AssemblyFileVersion("1.7.2.0")] 37 | -------------------------------------------------------------------------------- /MouseTester/MouseTester/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 MouseTester.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("MouseTester.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 | -------------------------------------------------------------------------------- /MouseTester/MouseTester/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 | text/microsoft-resx 107 | 108 | 109 | 2.0 110 | 111 | 112 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 113 | 114 | 115 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | -------------------------------------------------------------------------------- /MouseTester/MouseTester/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 MouseTester.Properties { 12 | 13 | 14 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 15 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "17.6.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 | -------------------------------------------------------------------------------- /MouseTester/MouseTester/Properties/Settings.settings: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /MouseTester/MouseTester/Properties/app.manifest: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 47 | -------------------------------------------------------------------------------- /MouseTester/MouseTester/RawMouse.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Diagnostics; 5 | using System.Text; 6 | using System.Runtime.InteropServices; 7 | using System.Windows.Forms; 8 | 9 | namespace MouseTester 10 | { 11 | public partial class Form1 12 | { 13 | private const int RIDEV_INPUTSINK = 0x00000100; 14 | private const int RID_INPUT = 0x10000003; 15 | private const int WM_INPUT = 0x00FF; 16 | private const int RIM_TYPEMOUSE = 0; 17 | private const int RI_MOUSE_LEFT_BUTTON_DOWN = 0x0001; 18 | private const ushort RI_MOUSE_LEFT_BUTTON_UP = 0x0002; 19 | private const int RI_MOUSE_RIGHT_BUTTON_DOWN = 0x0004; 20 | private const ushort RI_MOUSE_RIGHT_BUTTON_UP = 0x0008; 21 | 22 | [StructLayout(LayoutKind.Sequential)] 23 | internal struct RAWINPUTDEVICE 24 | { 25 | [MarshalAs(UnmanagedType.U2)] 26 | public ushort usUsagePage; 27 | [MarshalAs(UnmanagedType.U2)] 28 | public ushort usUsage; 29 | [MarshalAs(UnmanagedType.U4)] 30 | public int dwFlags; 31 | public IntPtr hwndTarget; 32 | } 33 | 34 | [StructLayout(LayoutKind.Sequential)] 35 | internal struct RAWINPUTHEADER 36 | { 37 | [MarshalAs(UnmanagedType.U4)] 38 | public int dwType; 39 | [MarshalAs(UnmanagedType.U4)] 40 | public int dwSize; 41 | public IntPtr hDevice; 42 | [MarshalAs(UnmanagedType.U4)] 43 | public int wParam; 44 | } 45 | 46 | [StructLayout(LayoutKind.Sequential)] 47 | internal struct RAWHID 48 | { 49 | [MarshalAs(UnmanagedType.U4)] 50 | public int dwSizHid; 51 | [MarshalAs(UnmanagedType.U4)] 52 | public int dwCount; 53 | } 54 | 55 | [StructLayout(LayoutKind.Sequential)] 56 | internal struct BUTTONSSTR 57 | { 58 | [MarshalAs(UnmanagedType.U2)] 59 | public ushort usButtonFlags; 60 | [MarshalAs(UnmanagedType.U2)] 61 | public ushort usButtonData; 62 | } 63 | 64 | [StructLayout(LayoutKind.Explicit)] 65 | internal struct RAWMOUSE 66 | { 67 | [MarshalAs(UnmanagedType.U2)] 68 | [FieldOffset(0)] 69 | public ushort usFlags; 70 | [MarshalAs(UnmanagedType.U4)] 71 | [FieldOffset(4)] 72 | public uint ulButtons; 73 | [FieldOffset(4)] 74 | public BUTTONSSTR buttonsStr; 75 | [MarshalAs(UnmanagedType.U4)] 76 | [FieldOffset(8)] 77 | public uint ulRawButtons; 78 | [FieldOffset(12)] 79 | public int lLastX; 80 | [FieldOffset(16)] 81 | public int lLastY; 82 | [MarshalAs(UnmanagedType.U4)] 83 | [FieldOffset(20)] 84 | public uint ulExtraInformation; 85 | } 86 | 87 | [StructLayout(LayoutKind.Sequential)] 88 | internal struct RAWKEYBOARD 89 | { 90 | [MarshalAs(UnmanagedType.U2)] 91 | public ushort MakeCode; 92 | [MarshalAs(UnmanagedType.U2)] 93 | public ushort Flags; 94 | [MarshalAs(UnmanagedType.U2)] 95 | public ushort Reserved; 96 | [MarshalAs(UnmanagedType.U2)] 97 | public ushort VKey; 98 | [MarshalAs(UnmanagedType.U4)] 99 | public uint Message; 100 | [MarshalAs(UnmanagedType.U4)] 101 | public uint ExtraInformation; 102 | } 103 | 104 | [StructLayout(LayoutKind.Sequential)] 105 | internal struct RAWINPUT 106 | { 107 | public RAWINPUTHEADER header; 108 | public Union data; 109 | [StructLayout(LayoutKind.Explicit)] 110 | public struct Union 111 | { 112 | [FieldOffset(0)] 113 | public RAWMOUSE mouse; 114 | [FieldOffset(0)] 115 | public RAWKEYBOARD keyboard; 116 | [FieldOffset(0)] 117 | public RAWHID hid; 118 | } 119 | } 120 | 121 | [DllImport("user32.dll")] 122 | extern static bool RegisterRawInputDevices(RAWINPUTDEVICE[] pRawInputDevices, 123 | uint uiNumDevices, 124 | uint cbSize); 125 | 126 | [DllImport("User32.dll")] 127 | extern static int GetRawInputData(IntPtr hRawInput, 128 | uint uiCommand, 129 | out RAWINPUT pData, //IntPtr pData, 130 | ref uint pcbSize, 131 | uint cbSizeHeader); 132 | 133 | public void RegisterRawInputMouse(IntPtr hwnd) 134 | { 135 | RAWINPUTDEVICE[] rid = new RAWINPUTDEVICE[1]; 136 | rid[0].usUsagePage = 1; 137 | rid[0].usUsage = 2; 138 | rid[0].dwFlags = RIDEV_INPUTSINK; 139 | rid[0].hwndTarget = hwnd; 140 | 141 | if (!RegisterRawInputDevices(rid, (uint)rid.Length, (uint)Marshal.SizeOf(rid[0]))) 142 | { 143 | Debug.WriteLine("RegisterRawInputDevices() Failed"); 144 | } 145 | } 146 | 147 | [DllImport("Kernel32.dll")] 148 | private static extern bool QueryPerformanceCounter(out long lpPerformanceCount); 149 | 150 | [DllImport("Kernel32.dll")] 151 | private static extern bool QueryPerformanceFrequency(out long lpFrequency); 152 | } 153 | } 154 | -------------------------------------------------------------------------------- /MouseTester/MouseTester/app.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /MouseTester/MouseTester/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | MouseTester 2 | 3 | [![Downloads](https://img.shields.io/github/downloads/valleyofdoom/MouseTester/total.svg)](https://github.com/valleyofdoom/MouseTester/releases) 4 | --------------------------------------------------------------------------------