Bläddra i källkod

添加项目文件。

Xing Cheng 3 år sedan
förälder
incheckning
8fda403de4

+ 4 - 0
.editorconfig

@@ -0,0 +1,4 @@
+[*.cs]
+
+# IDE1006: 命名样式
+dotnet_diagnostic.IDE1006.severity = none

BIN
Button_Checked.png


BIN
Button_Unchecked.png


BIN
DirectionScale.png


+ 46 - 0
GUI/BottomButton.cs

@@ -0,0 +1,46 @@
+using GUI.GUIUtils;
+using GUI.Properties;
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Diagnostics;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows.Forms;
+
+namespace GUI
+{
+    public partial class BottomButton : PictureBox, ICheckableUI
+    {
+        bool isChecked;
+
+        public bool IsChecked
+        {
+            get => isChecked;
+            set
+            {
+                isChecked = value;
+                if (isChecked) Image = Resources.Button_Checked;
+                else Image = Resources.Button_Unchecked;
+            }
+        }
+
+        public BottomButton()
+        {
+            Click += ButtonOnClick;
+            Image = Resources.Button_Unchecked;
+            SizeMode = PictureBoxSizeMode.Zoom;
+        }
+
+        void ButtonOnClick(object sender, EventArgs e)
+        {
+            IsChecked = !IsChecked;
+        }
+
+        public void ResetClick()
+        {
+            Click -= ButtonOnClick;
+        }
+    }
+}

+ 64 - 0
GUI/CheckUtil.cs

@@ -0,0 +1,64 @@
+namespace GUI
+{
+	public static class CheckUtil
+	{
+		public static void Func_sum_check(byte[] data, int offset, int len, byte[] check)
+		{
+			int i;
+			ushort result = 0;
+			for (i = 0; i < len; i++)
+			{
+				result += data[i + offset];
+			}
+			check[0] = (byte)result;
+			check[1] = (byte)(result >> 8);
+		}
+
+		public static void Func_Xor_check(byte[] data, int offset, int len, out byte check)
+		{
+			int i;
+			check = data[offset];
+			for (i = 1; i < len; i++)
+			{
+				check ^= data[i + offset];
+			}
+		}
+
+		public static bool ArrayEquals<T>(this T[] src, T[] other, int offset = 0)
+		{
+			for (int i = 0; i < src.Length; i++)
+				if (!src[i].Equals(other[i + offset]))
+					return false;
+			return true;
+		}
+
+		public static T[] ArrayCombine<T>(this T[] src, T[] other)
+		{
+			T[] newT = new T[src.Length + other.Length];
+			for (int i = 0; i < src.Length; i++) newT[i] = src[i];
+			for (int i = 0; i < other.Length; i++) newT[i + src.Length] = other[i];
+			return newT;
+		}
+
+		public static bool CheckData(this byte[] data)
+        {
+			byte[] check2 = new byte[2];
+			Func_sum_check(data, 0, data.Length - 3, check2);
+			if (!check2.ArrayEquals(data, data.Length - 3)) return false;
+			Func_Xor_check(data, 0, data.Length - 1, out byte check);
+			if (check != data[^1]) return false;
+			return true;
+        }
+
+		public static byte[] PackData(this byte[] data)
+		{
+			byte[] check2 = new byte[2];
+			Func_sum_check(data, 0, data.Length, check2);
+			byte[] withSumCheck = data.ArrayCombine(check2);
+			Func_Xor_check(withSumCheck, 0, withSumCheck.Length, out byte last);
+			return withSumCheck.ArrayCombine(new byte[1] { last });
+		}
+
+		public static byte t(this bool b) => (byte)(b ? 1 : 0);
+	}
+}

+ 131 - 0
GUI/ComAgent.cs

@@ -0,0 +1,131 @@
+using GUI.DataProvider;
+using System;
+using System.Collections.Concurrent;
+using System.Collections.Generic;
+using System.IO.Ports;
+using System.Linq;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace GUI
+{
+    public interface IComAgentDelegate
+    {
+        void ReceiveError(string msg);
+        void ReceiveSuccess();
+    }
+
+    public class ComAgent : SerialPort, IGUIDataSource
+    {
+        public float Yaw { get; set; } = 0f;
+        public float Roll { get; set; } = 0f;
+        public float Pitch { get; set; } = 0f;
+
+        public bool Q, W, E, A, S, D, L, R;
+        public sbyte dx, dy;
+        readonly Thread sendThread;
+        readonly ConcurrentQueue<Action> ioTasks;
+
+        public void Move(int dx, int dy)
+        {
+            this.dx = (sbyte)(this.dx + dx);
+            this.dy = (sbyte)(this.dy + dy);
+        }
+
+        public void ResetMove()
+        {
+            dx = dy = 0;
+        }
+
+        public IComAgentDelegate agentDelegate;
+
+        public ComAgent() : base()
+        {
+            DataReceived += receive;
+            ioTasks = new();
+            (sendThread = new(() =>
+            {
+                while (true)
+                {
+                    try
+                    {
+                        if (ioTasks.TryDequeue(out Action act)) act();
+                    }
+                    catch (Exception e)
+                    {
+                        Console.WriteLine($"{DateTime.Now} {e.Message}");
+                    }
+                    Thread.Sleep(10);
+                }
+            }) { IsBackground = true }).Start();
+        }
+
+        void EnqueueDiscardableIOTask(Action task)
+        {
+            int discarded = 0;
+            while (ioTasks.Count > 10)
+            {
+                ioTasks.TryDequeue(out Action _);
+                discarded++;
+            }
+            if (discarded > 0) Console.WriteLine($"{DateTime.Now} Discard {discarded} old io job due too much jobs blocking.");
+            ioTasks.Enqueue(task);
+        }
+
+        public void ThrowError(string reason, bool shutdown = false)
+        {
+            Console.WriteLine($"{DateTime.Now} Connection {(shutdown ? "breaked" : "warned")} due {reason}");
+            if (shutdown)
+            {
+                agentDelegate?.ReceiveError(reason);
+                Close();
+            }
+        }
+
+        public new void Close()
+        {
+            try { base.Close(); }
+            catch { }
+        }
+
+        public void SendFrame()
+        {
+            byte[] data = new byte[4];
+            data[0] = 0x0A;
+            data[1] = (byte)(Q.t() + W.t() * 2 + E.t() * 4 + A.t() * 8 + S.t() * 16 + D.t() * 32 + L.t() * 64 + R.t() * 128);
+            data[2] = (byte)dx;
+            data[3] = (byte)dy;
+            data = data.PackData();
+            dx = dy = 0;
+            EnqueueDiscardableIOTask(() => Write(data, 0, data.Length));
+        }
+
+        void receive(object sender, SerialDataReceivedEventArgs e)
+        {
+            byte[] buff = new byte[10];
+            int cursor = 0;
+            while (ReadByte() != 0x0A) ;
+            buff[cursor++] = 0x0A;
+            while ((cursor += Read(buff, cursor, buff.Length - cursor)) < buff.Length) ;
+            //Read(buff, 0, buff.Length);
+            if (buff[0] != 0x0A)
+            {
+                ThrowError($"Expect meta 0x0A, but got {buff[0]}", true);
+                return;
+            }
+            if (!buff.CheckData())
+            {
+                ThrowError($"Check fail, source data is {buff}");
+                return;
+            }
+            ushort yaw100 = BitConverter.ToUInt16(buff, 1),
+                roll100 = BitConverter.ToUInt16(buff, 3),
+                pitch100 = BitConverter.ToUInt16(buff, 5);
+            Yaw = yaw100 * 0.01f;
+            Roll = roll100 * 0.01f;
+            Pitch = pitch100 * 0.01f;
+            agentDelegate?.ReceiveSuccess();
+        }
+    }
+}

+ 185 - 0
GUI/ComConfig.Designer.cs

@@ -0,0 +1,185 @@
+
+namespace GUI
+{
+    partial class ComConfig
+    {
+        /// <summary>
+        /// Required designer variable.
+        /// </summary>
+        private System.ComponentModel.IContainer components = null;
+
+        /// <summary>
+        /// Clean up any resources being used.
+        /// </summary>
+        /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
+        protected override void Dispose(bool disposing)
+        {
+            if (disposing && (components != null))
+            {
+                components.Dispose();
+            }
+            base.Dispose(disposing);
+        }
+
+        #region Windows Form Designer generated code
+
+        /// <summary>
+        /// Required method for Designer support - do not modify
+        /// the contents of this method with the code editor.
+        /// </summary>
+        private void InitializeComponent()
+        {
+            this.label1 = new System.Windows.Forms.Label();
+            this.sPort = new System.Windows.Forms.ComboBox();
+            this.sRate = new System.Windows.Forms.ComboBox();
+            this.label2 = new System.Windows.Forms.Label();
+            this.scaleRule1 = new GUI.ScaleRule();
+            this.CloseButton = new System.Windows.Forms.Button();
+            this.button2 = new System.Windows.Forms.Button();
+            this.button3 = new System.Windows.Forms.Button();
+            this.serialOutput = new System.Windows.Forms.TextBox();
+            this.SuspendLayout();
+            // 
+            // label1
+            // 
+            this.label1.AutoSize = true;
+            this.label1.Location = new System.Drawing.Point(37, 44);
+            this.label1.Name = "label1";
+            this.label1.Size = new System.Drawing.Size(75, 28);
+            this.label1.TabIndex = 0;
+            this.label1.Text = "串口:";
+            // 
+            // sPort
+            // 
+            this.sPort.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
+            this.sPort.FormattingEnabled = true;
+            this.sPort.Location = new System.Drawing.Point(139, 41);
+            this.sPort.Name = "sPort";
+            this.sPort.Size = new System.Drawing.Size(287, 36);
+            this.sPort.TabIndex = 1;
+            this.sPort.SelectedIndexChanged += new System.EventHandler(this.sPort_SelectedIndexChanged);
+            // 
+            // sRate
+            // 
+            this.sRate.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
+            this.sRate.FormattingEnabled = true;
+            this.sRate.Items.AddRange(new object[] {
+            "4800",
+            "9600",
+            "19200",
+            "38400",
+            "115200"});
+            this.sRate.Location = new System.Drawing.Point(139, 87);
+            this.sRate.Name = "sRate";
+            this.sRate.Size = new System.Drawing.Size(287, 36);
+            this.sRate.TabIndex = 3;
+            this.sRate.SelectedIndexChanged += new System.EventHandler(this.sRate_SelectedIndexChanged);
+            // 
+            // label2
+            // 
+            this.label2.AutoSize = true;
+            this.label2.Location = new System.Drawing.Point(37, 90);
+            this.label2.Name = "label2";
+            this.label2.Size = new System.Drawing.Size(96, 28);
+            this.label2.TabIndex = 2;
+            this.label2.Text = "波特率:";
+            // 
+            // scaleRule1
+            // 
+            this.scaleRule1.BackColor = System.Drawing.Color.Transparent;
+            this.scaleRule1.Degree = 0F;
+            this.scaleRule1.Location = new System.Drawing.Point(156, 402);
+            this.scaleRule1.Margin = new System.Windows.Forms.Padding(6, 5, 6, 5);
+            this.scaleRule1.Name = "scaleRule1";
+            this.scaleRule1.Size = new System.Drawing.Size(8, 8);
+            this.scaleRule1.TabIndex = 4;
+            // 
+            // CloseButton
+            // 
+            this.CloseButton.Anchor = System.Windows.Forms.AnchorStyles.Bottom;
+            this.CloseButton.Enabled = false;
+            this.CloseButton.Location = new System.Drawing.Point(169, 576);
+            this.CloseButton.Name = "CloseButton";
+            this.CloseButton.Size = new System.Drawing.Size(138, 35);
+            this.CloseButton.TabIndex = 5;
+            this.CloseButton.Text = "进入控制";
+            this.CloseButton.UseVisualStyleBackColor = true;
+            this.CloseButton.Click += new System.EventHandler(this.CloseClick);
+            // 
+            // button2
+            // 
+            this.button2.Anchor = System.Windows.Forms.AnchorStyles.Bottom;
+            this.button2.Location = new System.Drawing.Point(99, 146);
+            this.button2.Name = "button2";
+            this.button2.Size = new System.Drawing.Size(138, 35);
+            this.button2.TabIndex = 6;
+            this.button2.Text = "刷新";
+            this.button2.UseVisualStyleBackColor = true;
+            this.button2.Click += new System.EventHandler(this.RefreshClick);
+            // 
+            // button3
+            // 
+            this.button3.Anchor = System.Windows.Forms.AnchorStyles.Bottom;
+            this.button3.Location = new System.Drawing.Point(243, 146);
+            this.button3.Name = "button3";
+            this.button3.Size = new System.Drawing.Size(138, 35);
+            this.button3.TabIndex = 7;
+            this.button3.Text = "测试连接";
+            this.button3.UseVisualStyleBackColor = true;
+            this.button3.Click += new System.EventHandler(this.TryConnectionClick);
+            // 
+            // serialOutput
+            // 
+            this.serialOutput.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
+            | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+            this.serialOutput.BorderStyle = System.Windows.Forms.BorderStyle.None;
+            this.serialOutput.Location = new System.Drawing.Point(37, 202);
+            this.serialOutput.Multiline = true;
+            this.serialOutput.Name = "serialOutput";
+            this.serialOutput.ReadOnly = true;
+            this.serialOutput.Size = new System.Drawing.Size(404, 350);
+            this.serialOutput.TabIndex = 8;
+            // 
+            // ComConfig
+            // 
+            this.AutoScaleDimensions = new System.Drawing.SizeF(13F, 28F);
+            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+            this.BackColor = System.Drawing.Color.White;
+            this.ClientSize = new System.Drawing.Size(476, 636);
+            this.Controls.Add(this.serialOutput);
+            this.Controls.Add(this.button3);
+            this.Controls.Add(this.button2);
+            this.Controls.Add(this.CloseButton);
+            this.Controls.Add(this.scaleRule1);
+            this.Controls.Add(this.sRate);
+            this.Controls.Add(this.label2);
+            this.Controls.Add(this.sPort);
+            this.Controls.Add(this.label1);
+            this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
+            this.MaximizeBox = false;
+            this.MinimizeBox = false;
+            this.Name = "ComConfig";
+            this.ShowIcon = false;
+            this.ShowInTaskbar = false;
+            this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
+            this.Text = "串口连接到机器人";
+            this.Load += new System.EventHandler(this.ComConfig_Load);
+            this.ResumeLayout(false);
+            this.PerformLayout();
+
+        }
+
+        #endregion
+
+        private System.Windows.Forms.Label label1;
+        private System.Windows.Forms.ComboBox sPort;
+        private System.Windows.Forms.ComboBox sRate;
+        private System.Windows.Forms.Label label2;
+        private ScaleRule scaleRule1;
+        private System.Windows.Forms.Button CloseButton;
+        private System.Windows.Forms.Button button2;
+        private System.Windows.Forms.Button button3;
+        private System.Windows.Forms.TextBox serialOutput;
+    }
+}

+ 127 - 0
GUI/ComConfig.cs

@@ -0,0 +1,127 @@
+using Microsoft.Win32;
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Data;
+using System.Drawing;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows.Forms;
+
+namespace GUI
+{
+    public partial class ComConfig : Form, IComAgentDelegate
+    {
+        ComAgent agent;
+
+        public ComConfig()
+        {
+            InitializeComponent();
+            serialOutput.ScrollBars = ScrollBars.Vertical;
+            agent = new ComAgent();
+            agent.agentDelegate = this;
+        }
+
+        void ClearOutput()
+        {
+            serialOutput.Text = "";
+            agent.Close();
+            CloseButton.Enabled = false;
+            goodMessageReceived = false;
+        }
+
+        void RefreshPort()
+        {
+            ClearOutput();
+            RegistryKey keyCom = Registry.LocalMachine.OpenSubKey("Hardware\\DeviceMap\\SerialComm");
+            if (keyCom != null)
+            {
+                string[] sSubKeys = keyCom.GetValueNames();
+                sPort.Items.Clear();
+                foreach (string sName in sSubKeys)
+                {
+                    string sValue = (string)keyCom.GetValue(sName);
+                    sPort.Items.Add(sValue);
+                }
+                if (sPort.Items.Count > 0)
+                    sPort.SelectedIndex = 0;
+            }
+        }
+
+        private void ComConfig_Load(object sender, EventArgs e)
+        {
+            RefreshPort();
+        }
+
+        private void sPort_SelectedIndexChanged(object sender, EventArgs e)
+        {
+            ClearOutput();
+            agent.PortName = sPort.Text;
+        }
+
+        private void sRate_SelectedIndexChanged(object sender, EventArgs e)
+        {
+            ClearOutput();
+            agent.BaudRate = int.Parse(sRate.Text);
+        }
+
+        private void CloseClick(object sender, EventArgs e)
+        {
+            if (!goodMessageReceived)
+            {
+                if (MessageBox.Show(
+                    "截止到目前,还没有从该串口接收到一条通过校验的数据,是否仍然继续连接?",
+                    "串口连接提示",
+                    MessageBoxButtons.OKCancel, MessageBoxIcon.Warning) == DialogResult.Cancel)
+                    return;
+            }
+            agent.agentDelegate = null;
+            Program.comAgent = agent;
+            Close();
+        }
+
+        private void RefreshClick(object sender, EventArgs e)
+        {
+            RefreshPort();
+        }
+
+        private void TryConnectionClick(object sender, EventArgs e)
+        {
+            try
+            {
+                agent.Open();
+                agent.SendFrame();
+                CloseButton.Enabled = true;
+            }
+            catch (Exception ex)
+            {
+                WriteLine(ex.Message);
+            }
+        }
+
+        void WriteLine(string msg)
+        {
+            serialOutput.BeginInvoke((Action)(() =>
+            {
+                if (serialOutput.Text.Length < 10000)
+                    serialOutput.AppendText(msg + "\r\n");
+            }));
+        }
+
+        public void ReceiveError(string msg)
+        {
+            ClearOutput();
+            WriteLine(msg);
+        }
+
+        bool goodMessageReceived = false;
+
+        public void ReceiveSuccess()
+        {
+            WriteLine("Receive good message.");
+            goodMessageReceived = true;
+            CloseButton.Enabled = true;
+        }
+    }
+}

+ 60 - 0
GUI/ComConfig.resx

@@ -0,0 +1,60 @@
+<root>
+  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+    <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
+    <xsd:element name="root" msdata:IsDataSet="true">
+      <xsd:complexType>
+        <xsd:choice maxOccurs="unbounded">
+          <xsd:element name="metadata">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" />
+              </xsd:sequence>
+              <xsd:attribute name="name" use="required" type="xsd:string" />
+              <xsd:attribute name="type" type="xsd:string" />
+              <xsd:attribute name="mimetype" type="xsd:string" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="assembly">
+            <xsd:complexType>
+              <xsd:attribute name="alias" type="xsd:string" />
+              <xsd:attribute name="name" type="xsd:string" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="data">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="resheader">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" />
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:choice>
+      </xsd:complexType>
+    </xsd:element>
+  </xsd:schema>
+  <resheader name="resmimetype">
+    <value>text/microsoft-resx</value>
+  </resheader>
+  <resheader name="version">
+    <value>2.0</value>
+  </resheader>
+  <resheader name="reader">
+    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <resheader name="writer">
+    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+</root>

+ 400 - 0
GUI/ControllerGUI.Designer.cs

@@ -0,0 +1,400 @@
+
+namespace GUI
+{
+    partial class ControllerGUI
+    {
+        /// <summary>
+        /// Required designer variable.
+        /// </summary>
+        private System.ComponentModel.IContainer components = null;
+
+        /// <summary>
+        /// Clean up any resources being used.
+        /// </summary>
+        /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
+        protected override void Dispose(bool disposing)
+        {
+            if (disposing && (components != null))
+            {
+                components.Dispose();
+            }
+            base.Dispose(disposing);
+        }
+
+        #region Windows Form Designer generated code
+
+        /// <summary>
+        /// Required method for Designer support - do not modify
+        /// the contents of this method with the code editor.
+        /// </summary>
+        private void InitializeComponent()
+        {
+            this.components = new System.ComponentModel.Container();
+            System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(ControllerGUI));
+            this.LayoutRight = new System.Windows.Forms.FlowLayoutPanel();
+            this.RightPanel1 = new System.Windows.Forms.Panel();
+            this.pictureBox1 = new System.Windows.Forms.PictureBox();
+            this.RightPane2 = new System.Windows.Forms.Panel();
+            this.pictureBox2 = new System.Windows.Forms.PictureBox();
+            this.RightPane3 = new System.Windows.Forms.Panel();
+            this.pictureBox3 = new System.Windows.Forms.PictureBox();
+            this.RightPane4 = new System.Windows.Forms.Panel();
+            this.pictureBox4 = new System.Windows.Forms.PictureBox();
+            this.BottomLayout = new System.Windows.Forms.FlowLayoutPanel();
+            this.bottomButton1 = new GUI.BottomButton();
+            this.bottomButton2 = new GUI.BottomButton();
+            this.bottomButton3 = new GUI.BottomButton();
+            this.panel2 = new System.Windows.Forms.Panel();
+            this.panel3 = new System.Windows.Forms.Panel();
+            this.panel1 = new System.Windows.Forms.Panel();
+            this.panel4 = new System.Windows.Forms.Panel();
+            this.mainImageView = new GUI.DoubleBufferedPanel();
+            this.DirectionRule = new GUI.ScaleRule();
+            this.roulette = new GUI.Roulette();
+            this.pictureBox5 = new System.Windows.Forms.PictureBox();
+            this.timer1 = new System.Windows.Forms.Timer(this.components);
+            this.mouseTimer = new System.Windows.Forms.Timer(this.components);
+            this.LayoutRight.SuspendLayout();
+            this.RightPanel1.SuspendLayout();
+            ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).BeginInit();
+            this.RightPane2.SuspendLayout();
+            ((System.ComponentModel.ISupportInitialize)(this.pictureBox2)).BeginInit();
+            this.RightPane3.SuspendLayout();
+            ((System.ComponentModel.ISupportInitialize)(this.pictureBox3)).BeginInit();
+            this.RightPane4.SuspendLayout();
+            ((System.ComponentModel.ISupportInitialize)(this.pictureBox4)).BeginInit();
+            this.BottomLayout.SuspendLayout();
+            ((System.ComponentModel.ISupportInitialize)(this.bottomButton1)).BeginInit();
+            ((System.ComponentModel.ISupportInitialize)(this.bottomButton2)).BeginInit();
+            ((System.ComponentModel.ISupportInitialize)(this.bottomButton3)).BeginInit();
+            this.panel2.SuspendLayout();
+            this.panel1.SuspendLayout();
+            this.panel4.SuspendLayout();
+            this.mainImageView.SuspendLayout();
+            this.roulette.SuspendLayout();
+            ((System.ComponentModel.ISupportInitialize)(this.pictureBox5)).BeginInit();
+            this.SuspendLayout();
+            // 
+            // LayoutRight
+            // 
+            this.LayoutRight.BackColor = System.Drawing.Color.Transparent;
+            this.LayoutRight.Controls.Add(this.RightPanel1);
+            this.LayoutRight.Controls.Add(this.RightPane2);
+            this.LayoutRight.Controls.Add(this.RightPane3);
+            this.LayoutRight.Controls.Add(this.RightPane4);
+            this.LayoutRight.Dock = System.Windows.Forms.DockStyle.Right;
+            this.LayoutRight.Location = new System.Drawing.Point(752, 0);
+            this.LayoutRight.Name = "LayoutRight";
+            this.LayoutRight.Size = new System.Drawing.Size(262, 635);
+            this.LayoutRight.TabIndex = 0;
+            // 
+            // RightPanel1
+            // 
+            this.RightPanel1.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+            this.RightPanel1.BackgroundImageLayout = System.Windows.Forms.ImageLayout.Stretch;
+            this.RightPanel1.Controls.Add(this.pictureBox1);
+            this.RightPanel1.Location = new System.Drawing.Point(3, 3);
+            this.RightPanel1.Name = "RightPanel1";
+            this.RightPanel1.Size = new System.Drawing.Size(256, 140);
+            this.RightPanel1.TabIndex = 2;
+            // 
+            // pictureBox1
+            // 
+            this.pictureBox1.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
+            | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+            this.pictureBox1.Image = global::GUI.Properties.Resources.Right_Image_Holder;
+            this.pictureBox1.Location = new System.Drawing.Point(11, 9);
+            this.pictureBox1.Name = "pictureBox1";
+            this.pictureBox1.Size = new System.Drawing.Size(236, 121);
+            this.pictureBox1.SizeMode = System.Windows.Forms.PictureBoxSizeMode.StretchImage;
+            this.pictureBox1.TabIndex = 0;
+            this.pictureBox1.TabStop = false;
+            // 
+            // RightPane2
+            // 
+            this.RightPane2.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+            this.RightPane2.BackgroundImageLayout = System.Windows.Forms.ImageLayout.Stretch;
+            this.RightPane2.Controls.Add(this.pictureBox2);
+            this.RightPane2.Location = new System.Drawing.Point(3, 149);
+            this.RightPane2.Name = "RightPane2";
+            this.RightPane2.Size = new System.Drawing.Size(256, 140);
+            this.RightPane2.TabIndex = 3;
+            // 
+            // pictureBox2
+            // 
+            this.pictureBox2.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
+            | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+            this.pictureBox2.Image = global::GUI.Properties.Resources.Right_Image_Holder;
+            this.pictureBox2.Location = new System.Drawing.Point(11, 9);
+            this.pictureBox2.Name = "pictureBox2";
+            this.pictureBox2.Size = new System.Drawing.Size(236, 121);
+            this.pictureBox2.SizeMode = System.Windows.Forms.PictureBoxSizeMode.StretchImage;
+            this.pictureBox2.TabIndex = 0;
+            this.pictureBox2.TabStop = false;
+            // 
+            // RightPane3
+            // 
+            this.RightPane3.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+            this.RightPane3.BackgroundImageLayout = System.Windows.Forms.ImageLayout.Stretch;
+            this.RightPane3.Controls.Add(this.pictureBox3);
+            this.RightPane3.Location = new System.Drawing.Point(3, 295);
+            this.RightPane3.Name = "RightPane3";
+            this.RightPane3.Size = new System.Drawing.Size(256, 140);
+            this.RightPane3.TabIndex = 3;
+            // 
+            // pictureBox3
+            // 
+            this.pictureBox3.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
+            | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+            this.pictureBox3.Image = global::GUI.Properties.Resources.Right_Image_Holder;
+            this.pictureBox3.Location = new System.Drawing.Point(11, 9);
+            this.pictureBox3.Name = "pictureBox3";
+            this.pictureBox3.Size = new System.Drawing.Size(236, 121);
+            this.pictureBox3.SizeMode = System.Windows.Forms.PictureBoxSizeMode.StretchImage;
+            this.pictureBox3.TabIndex = 0;
+            this.pictureBox3.TabStop = false;
+            // 
+            // RightPane4
+            // 
+            this.RightPane4.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+            this.RightPane4.BackgroundImageLayout = System.Windows.Forms.ImageLayout.Stretch;
+            this.RightPane4.Controls.Add(this.pictureBox4);
+            this.RightPane4.Location = new System.Drawing.Point(3, 441);
+            this.RightPane4.Name = "RightPane4";
+            this.RightPane4.Size = new System.Drawing.Size(256, 140);
+            this.RightPane4.TabIndex = 3;
+            // 
+            // pictureBox4
+            // 
+            this.pictureBox4.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
+            | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+            this.pictureBox4.Image = global::GUI.Properties.Resources.Right_Image_Holder;
+            this.pictureBox4.Location = new System.Drawing.Point(11, 9);
+            this.pictureBox4.Name = "pictureBox4";
+            this.pictureBox4.Size = new System.Drawing.Size(236, 121);
+            this.pictureBox4.SizeMode = System.Windows.Forms.PictureBoxSizeMode.StretchImage;
+            this.pictureBox4.TabIndex = 0;
+            this.pictureBox4.TabStop = false;
+            // 
+            // BottomLayout
+            // 
+            this.BottomLayout.BackColor = System.Drawing.Color.Transparent;
+            this.BottomLayout.Controls.Add(this.bottomButton1);
+            this.BottomLayout.Controls.Add(this.bottomButton2);
+            this.BottomLayout.Controls.Add(this.bottomButton3);
+            this.BottomLayout.Dock = System.Windows.Forms.DockStyle.Bottom;
+            this.BottomLayout.Location = new System.Drawing.Point(0, 556);
+            this.BottomLayout.Name = "BottomLayout";
+            this.BottomLayout.Size = new System.Drawing.Size(752, 79);
+            this.BottomLayout.TabIndex = 1;
+            // 
+            // bottomButton1
+            // 
+            this.bottomButton1.Image = ((System.Drawing.Image)(resources.GetObject("bottomButton1.Image")));
+            this.bottomButton1.IsChecked = false;
+            this.bottomButton1.Location = new System.Drawing.Point(3, 3);
+            this.bottomButton1.Name = "bottomButton1";
+            this.bottomButton1.Size = new System.Drawing.Size(242, 72);
+            this.bottomButton1.SizeMode = System.Windows.Forms.PictureBoxSizeMode.Zoom;
+            this.bottomButton1.TabIndex = 1;
+            this.bottomButton1.TabStop = false;
+            // 
+            // bottomButton2
+            // 
+            this.bottomButton2.Image = ((System.Drawing.Image)(resources.GetObject("bottomButton2.Image")));
+            this.bottomButton2.IsChecked = false;
+            this.bottomButton2.Location = new System.Drawing.Point(251, 3);
+            this.bottomButton2.Name = "bottomButton2";
+            this.bottomButton2.Size = new System.Drawing.Size(242, 72);
+            this.bottomButton2.SizeMode = System.Windows.Forms.PictureBoxSizeMode.Zoom;
+            this.bottomButton2.TabIndex = 2;
+            this.bottomButton2.TabStop = false;
+            // 
+            // bottomButton3
+            // 
+            this.bottomButton3.Image = ((System.Drawing.Image)(resources.GetObject("bottomButton3.Image")));
+            this.bottomButton3.IsChecked = false;
+            this.bottomButton3.Location = new System.Drawing.Point(499, 3);
+            this.bottomButton3.Name = "bottomButton3";
+            this.bottomButton3.Size = new System.Drawing.Size(242, 72);
+            this.bottomButton3.SizeMode = System.Windows.Forms.PictureBoxSizeMode.Zoom;
+            this.bottomButton3.TabIndex = 3;
+            this.bottomButton3.TabStop = false;
+            // 
+            // panel2
+            // 
+            this.panel2.BackgroundImage = global::GUI.Properties.Resources.Right_Image_Holder;
+            this.panel2.BackgroundImageLayout = System.Windows.Forms.ImageLayout.Stretch;
+            this.panel2.Controls.Add(this.panel3);
+            this.panel2.Location = new System.Drawing.Point(8, 8);
+            this.panel2.Name = "panel2";
+            this.panel2.Size = new System.Drawing.Size(256, 125);
+            this.panel2.TabIndex = 1;
+            // 
+            // panel3
+            // 
+            this.panel3.BackgroundImage = global::GUI.Properties.Resources.Right_Image_Holder;
+            this.panel3.BackgroundImageLayout = System.Windows.Forms.ImageLayout.Stretch;
+            this.panel3.Location = new System.Drawing.Point(8, 8);
+            this.panel3.Name = "panel3";
+            this.panel3.Size = new System.Drawing.Size(256, 125);
+            this.panel3.TabIndex = 1;
+            // 
+            // panel1
+            // 
+            this.panel1.BackgroundImageLayout = System.Windows.Forms.ImageLayout.Stretch;
+            this.panel1.Controls.Add(this.panel4);
+            this.panel1.Dock = System.Windows.Forms.DockStyle.Fill;
+            this.panel1.Location = new System.Drawing.Point(0, 0);
+            this.panel1.Name = "panel1";
+            this.panel1.Size = new System.Drawing.Size(752, 556);
+            this.panel1.TabIndex = 2;
+            // 
+            // panel4
+            // 
+            this.panel4.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
+            | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+            this.panel4.BackColor = System.Drawing.Color.Transparent;
+            this.panel4.BackgroundImage = global::GUI.Properties.Resources.MainVideo;
+            this.panel4.BackgroundImageLayout = System.Windows.Forms.ImageLayout.Stretch;
+            this.panel4.Controls.Add(this.mainImageView);
+            this.panel4.Location = new System.Drawing.Point(9, 12);
+            this.panel4.Name = "panel4";
+            this.panel4.Size = new System.Drawing.Size(737, 538);
+            this.panel4.TabIndex = 0;
+            // 
+            // mainImageView
+            // 
+            this.mainImageView.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
+            | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+            this.mainImageView.BackgroundImageLayout = System.Windows.Forms.ImageLayout.Stretch;
+            this.mainImageView.Controls.Add(this.DirectionRule);
+            this.mainImageView.Controls.Add(this.roulette);
+            this.mainImageView.Location = new System.Drawing.Point(12, 7);
+            this.mainImageView.Name = "mainImageView";
+            this.mainImageView.Size = new System.Drawing.Size(713, 518);
+            this.mainImageView.TabIndex = 1;
+            // 
+            // DirectionRule
+            // 
+            this.DirectionRule.Anchor = System.Windows.Forms.AnchorStyles.Top;
+            this.DirectionRule.BackColor = System.Drawing.Color.Transparent;
+            this.DirectionRule.Degree = 176F;
+            this.DirectionRule.Location = new System.Drawing.Point(80, 28);
+            this.DirectionRule.Margin = new System.Windows.Forms.Padding(6, 5, 6, 5);
+            this.DirectionRule.Name = "DirectionRule";
+            this.DirectionRule.Size = new System.Drawing.Size(553, 59);
+            this.DirectionRule.TabIndex = 0;
+            // 
+            // roulette
+            // 
+            this.roulette.Anchor = System.Windows.Forms.AnchorStyles.None;
+            this.roulette.BackgroundImage = ((System.Drawing.Image)(resources.GetObject("roulette.BackgroundImage")));
+            this.roulette.BackgroundImageLayout = System.Windows.Forms.ImageLayout.Center;
+            this.roulette.Controls.Add(this.pictureBox5);
+            this.roulette.Degree = 0F;
+            this.roulette.Location = new System.Drawing.Point(191, 143);
+            this.roulette.Name = "roulette";
+            this.roulette.Size = new System.Drawing.Size(331, 138);
+            this.roulette.TabIndex = 1;
+            // 
+            // pictureBox5
+            // 
+            this.pictureBox5.BackColor = System.Drawing.Color.Transparent;
+            this.pictureBox5.Image = global::GUI.Properties.Resources.Tri;
+            this.pictureBox5.Location = new System.Drawing.Point(159, 60);
+            this.pictureBox5.Name = "pictureBox5";
+            this.pictureBox5.Size = new System.Drawing.Size(15, 15);
+            this.pictureBox5.SizeMode = System.Windows.Forms.PictureBoxSizeMode.StretchImage;
+            this.pictureBox5.TabIndex = 6;
+            this.pictureBox5.TabStop = false;
+            // 
+            // timer1
+            // 
+            this.timer1.Enabled = true;
+            this.timer1.Interval = 10;
+            this.timer1.Tick += new System.EventHandler(this.timer1_Tick);
+            // 
+            // mouseTimer
+            // 
+            this.mouseTimer.Enabled = true;
+            this.mouseTimer.Interval = 5;
+            this.mouseTimer.Tick += new System.EventHandler(this.mouseTimer_Tick);
+            // 
+            // ControllerGUI
+            // 
+            this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 17F);
+            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+            this.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(0)))), ((int)(((byte)(64)))), ((int)(((byte)(64)))));
+            this.ClientSize = new System.Drawing.Size(1014, 635);
+            this.Controls.Add(this.panel1);
+            this.Controls.Add(this.BottomLayout);
+            this.Controls.Add(this.LayoutRight);
+            this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None;
+            this.MinimumSize = new System.Drawing.Size(1014, 630);
+            this.Name = "ControllerGUI";
+            this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
+            this.Text = "ControllerGUI - Author XCBOSA";
+            this.TransparencyKey = System.Drawing.Color.FromArgb(((int)(((byte)(0)))), ((int)(((byte)(64)))), ((int)(((byte)(64)))));
+            this.WindowState = System.Windows.Forms.FormWindowState.Maximized;
+            this.Resize += new System.EventHandler(this.ControllerGUI_Resize);
+            this.LayoutRight.ResumeLayout(false);
+            this.RightPanel1.ResumeLayout(false);
+            ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).EndInit();
+            this.RightPane2.ResumeLayout(false);
+            ((System.ComponentModel.ISupportInitialize)(this.pictureBox2)).EndInit();
+            this.RightPane3.ResumeLayout(false);
+            ((System.ComponentModel.ISupportInitialize)(this.pictureBox3)).EndInit();
+            this.RightPane4.ResumeLayout(false);
+            ((System.ComponentModel.ISupportInitialize)(this.pictureBox4)).EndInit();
+            this.BottomLayout.ResumeLayout(false);
+            ((System.ComponentModel.ISupportInitialize)(this.bottomButton1)).EndInit();
+            ((System.ComponentModel.ISupportInitialize)(this.bottomButton2)).EndInit();
+            ((System.ComponentModel.ISupportInitialize)(this.bottomButton3)).EndInit();
+            this.panel2.ResumeLayout(false);
+            this.panel1.ResumeLayout(false);
+            this.panel4.ResumeLayout(false);
+            this.mainImageView.ResumeLayout(false);
+            this.roulette.ResumeLayout(false);
+            ((System.ComponentModel.ISupportInitialize)(this.pictureBox5)).EndInit();
+            this.ResumeLayout(false);
+
+        }
+
+        #endregion
+
+        private System.Windows.Forms.FlowLayoutPanel LayoutRight;
+        private System.Windows.Forms.FlowLayoutPanel BottomLayout;
+        private BottomButton bottomButton1;
+        private BottomButton bottomButton2;
+        private BottomButton bottomButton3;
+        private System.Windows.Forms.Panel panel2;
+        private System.Windows.Forms.Panel panel3;
+        private System.Windows.Forms.Panel RightPanel1;
+        private System.Windows.Forms.PictureBox pictureBox1;
+        private System.Windows.Forms.Panel RightPane2;
+        private System.Windows.Forms.PictureBox pictureBox2;
+        private System.Windows.Forms.Panel RightPane3;
+        private System.Windows.Forms.PictureBox pictureBox3;
+        private System.Windows.Forms.Panel RightPane4;
+        private System.Windows.Forms.PictureBox pictureBox4;
+        private System.Windows.Forms.Panel panel1;
+        private System.Windows.Forms.Panel panel4;
+        private System.Windows.Forms.Timer timer1;
+        private DoubleBufferedPanel mainImageView;
+        private ScaleRule DirectionRule;
+        private Roulette roulette;
+        private System.Windows.Forms.PictureBox pictureBox5;
+        private System.Windows.Forms.Timer mouseTimer;
+    }
+}

+ 165 - 0
GUI/ControllerGUI.cs

@@ -0,0 +1,165 @@
+using GUI.DataProvider;
+using GUI.GUIUtils;
+using GUI.Hook;
+using System;
+using System.Diagnostics;
+using System.Drawing;
+using System.Windows.Forms;
+
+namespace GUI
+{
+    public partial class ControllerGUI : Form
+    {
+        readonly EventPerformer<ISizeChangedListener> sizeEvent;
+        readonly CheckGroup<BottomButton> bottomGroup;
+        readonly KeyboardHook keyboard;
+        readonly MouseHook mouse;
+
+        public IGUIDataSource dataSource;
+        ComAgent agent => Program.comAgent;
+        Point screenCenter => new(Screen.PrimaryScreen.Bounds.Width / 2, Screen.PrimaryScreen.Bounds.Height / 2);
+        Point screenRightDown => new(Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height);
+        
+        public ControllerGUI()
+        {
+            ComConfig guiCfg = new ComConfig();
+            guiCfg.ShowDialog();
+            if (Program.comAgent == null)
+                Process.GetCurrentProcess().Kill();
+            MessageBox.Show(
+                "按F12暂停和继续控制程序",
+                "控制器",
+                MessageBoxButtons.OK, MessageBoxIcon.Information);
+
+            InitializeComponent();
+
+            dataSource = agent;
+            sizeEvent = new();
+            sizeEvent.AddSafe(new GUIParagraph(BottomLayout));
+            sizeEvent.AddSafe(new GUIParagraph(LayoutRight)
+            {
+                Target = GUIParagraph.TargetXY.Y,
+                FixedPaneReferenceFunc = (c, f) => c.Size = new(f.Width - 3, c.Height)
+            });
+            bottomGroup = new();
+            bottomGroup.AddSafe(bottomButton1, bottomButton2, bottomButton3);
+            OnSizeChanged(null);
+
+            keyboard = new();
+            keyboard.SetHook();
+            keyboard.OnKeyDownEvent += keyDown;
+            keyboard.OnKeyUpEvent += keyUp;
+            mouse = new();
+            mouse.SetHook();
+            mouse.OnMouseMoveEvent += mouseMove;
+            mouse.OnMouseDownEvent += mouseDown;
+            mouse.OnMouseUpEvent += mouseUp;
+        }
+
+        protected override void OnFormClosing(FormClosingEventArgs e)
+        {
+            base.OnFormClosing(e);
+            keyboard.UnHook();
+        }
+
+        void keyDown(object sender, KeyEventArgs e)
+        {
+            switch (e.KeyCode)
+            {
+                case Keys.F12:
+                    Visible = !Visible;
+                    if (Visible)
+                    {
+                        mouseGotoCenter();
+                        Cursor.Hide();
+                        mouse.SetHook();
+                    }
+                    else
+                    {
+                        mouse.UnHook();
+                        Cursor.Show();
+                    }
+                    break;
+                case Keys.Q: agent.Q = true; break;
+                case Keys.W: agent.W = true; break;
+                case Keys.E: agent.E = true; break;
+                case Keys.A: agent.A = true; break;
+                case Keys.S: agent.S = true; break;
+                case Keys.D: agent.D = true; break;
+                case Keys.LButton: agent.L = true; break;
+                case Keys.RButton: agent.R = true; break;
+            }
+        }
+
+        void keyUp(object sender, KeyEventArgs e)
+        {
+            switch (e.KeyCode)
+            {
+                case Keys.Q: agent.Q = false; break;
+                case Keys.W: agent.W = false; break;
+                case Keys.E: agent.E = false; break;
+                case Keys.A: agent.A = false; break;
+                case Keys.S: agent.S = false; break;
+                case Keys.D: agent.D = false; break;
+                case Keys.LButton: agent.L = false; break;
+                case Keys.RButton: agent.R = false; break;
+            }
+        }
+
+        void mouseDown(object sender, MouseEventArgs e)
+        {
+            switch (e.Button)
+            {
+                case MouseButtons.Left: agent.L = true; break;
+                case MouseButtons.Right: agent.R = true; break;
+            }
+        }
+
+        void mouseUp(object sender, MouseEventArgs e)
+        {
+            switch (e.Button)
+            {
+                case MouseButtons.Left: agent.L = false; break;
+                case MouseButtons.Right: agent.R = false; break;
+            }
+        }
+
+        int sx, sy;
+
+        void mouseMove(object sender, MouseEventArgs e)
+        {
+            sx = e.X - screenCenter.X;
+            sy = e.Y - screenCenter.Y;
+        }
+
+        void ControllerGUI_Resize(object sender, EventArgs e)
+        {
+            LayoutRight.Size = new(Width / 4, LayoutRight.Height);
+            sizeEvent?.Perform(p => p.OnSizeChanged());
+        }
+
+        void mouseGotoCenter()
+        {
+            Win32Api.mouse_event(Win32Api.MOUSEEVENTF_MOVE | Win32Api.MOUSEEVENTF_ABSOLUTE,
+                screenCenter.X * 65535 / screenRightDown.X,
+                screenCenter.Y * 65535 / screenRightDown.Y, 0, 0);
+        }
+
+        private void mouseTimer_Tick(object sender, EventArgs e)
+        {
+            if (!Visible) return;
+            agent.Move(++sx, ++sy);
+            mouseGotoCenter();
+        }
+
+        void timer1_Tick(object sender, EventArgs e)
+        {
+            if (!Visible) return;
+            DirectionRule.Degree = dataSource?.Yaw ?? 0;
+            roulette.Degree = dataSource?.Roll ?? 0;
+
+            agent.SendFrame();
+            agent.ResetMove();
+        }
+    }
+}

+ 746 - 0
GUI/ControllerGUI.resx

@@ -0,0 +1,746 @@
+<root>
+  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+    <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
+    <xsd:element name="root" msdata:IsDataSet="true">
+      <xsd:complexType>
+        <xsd:choice maxOccurs="unbounded">
+          <xsd:element name="metadata">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" />
+              </xsd:sequence>
+              <xsd:attribute name="name" use="required" type="xsd:string" />
+              <xsd:attribute name="type" type="xsd:string" />
+              <xsd:attribute name="mimetype" type="xsd:string" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="assembly">
+            <xsd:complexType>
+              <xsd:attribute name="alias" type="xsd:string" />
+              <xsd:attribute name="name" type="xsd:string" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="data">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="resheader">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" />
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:choice>
+      </xsd:complexType>
+    </xsd:element>
+  </xsd:schema>
+  <resheader name="resmimetype">
+    <value>text/microsoft-resx</value>
+  </resheader>
+  <resheader name="version">
+    <value>2.0</value>
+  </resheader>
+  <resheader name="reader">
+    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <resheader name="writer">
+    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <assembly alias="System.Drawing.Common" name="System.Drawing.Common, Version=5.0.0.1, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51" />
+  <data name="bottomButton1.Image" type="System.Drawing.Bitmap, System.Drawing.Common" mimetype="application/x-microsoft.net.object.bytearray.base64">
+    <value>
+        iVBORw0KGgoAAAANSUhEUgAAAUwAAABUCAYAAADpnA4dAAAABGdBTUEAALGPC/xhBQAAA0JJREFUeF7t
+        3MFrFGccx+Fc+h94SHZN4rF/iRfPnj178eKll156EbGIRShIEXpohbaUwsxQFJEYgkGCUiylKYUeeigU
+        SsBe97J9J7zCsIz1t7uJZN95vvDcdt/N6cPsZJI1MzMzMzMzMzMzs1K3/lt1NXne8Qhghe11fJtTt/zy
+        4dPRfrW7db/+ZfOr+gXAqtv6sn61+V29n/r2OuduuaWD2qvJaXv4hZvNFKAk27ebf08kmOmQl20s26vK
+        vg8CWHVLBzO9+YPk5+NYftEc9n0IQAmWCmZ647nkz2Syda/5ve8DAEqxcDDTmz5sQ7l+WB1t323+6jsc
+        oCQLBTO94WIyXf+1+uPCrWbSdzBAaeYOZnpx+4zldONV5TfhwKDMFcz0whttLNtnLPsOAyhZOJjpRdeT
+        yfhxvdN3EEDp5gnmD6O9+ql7lsBQzRXMzW/q532HAAzBXME8/339rO8QgCGYJ5hfCyYwZIIJECSYAEGC
+        CRAkmABBggkQJJgAQYIJECSYAEGCCRAkmABBggkQJJgAQYIJECSYAEGCCRAkmABBggkQJJgAQYIJECSY
+        AEGCCRAkmABBggkQJJgAQYIJECSYAEGCCRAkmABBggkQJJgAQYIJECSYAEGCCRAkmABBggkQJJgAQdt3
+        miPBBAgYP6l3Ugv/yVl8+wQTGLLRXv00dXCafJKz+PYJJjBUo2fVm1hez0n8/wkmMETjx8dfw9tYXss5
+        fPcEExiaTixv5BTGJpjAkIx2F4xlO8EEhqJzz/KjnMD5JphA8W41k/HOAvcsZyeYQOnGD48fHZokH+f0
+        LTbBBEqWH0pvryw/zdlbfIIJlGquh9IjE0ygRJ17lrGH0iMTTKA0+Z5lG8vl7lnOTjCBknRiufw9y9kJ
+        JlCKztfwk49lO8EEStD5C56T/RrenWACq67zNXyxv+CJTjCBVTb+8RTvWc5OMIFV1fmvQ5/lpJ3uBBNY
+        RUv916FFlz6sEUxglXSuLE/3nuXs2mBuPqgP+n4ogLPmfFPv5li+vyvLN2uDuX5YHW28rPaP/VQdAJw5
+        B9VeatXfOZaf54S936UPPpdcnnEJ4Ay50rGR82VmZmZmZmZmZmZmZmb2rq2t/QezbIGIT0hmjwAAAABJ
+        RU5ErkJggg==
+</value>
+  </data>
+  <data name="bottomButton2.Image" type="System.Drawing.Bitmap, System.Drawing.Common" mimetype="application/x-microsoft.net.object.bytearray.base64">
+    <value>
+        iVBORw0KGgoAAAANSUhEUgAAAUwAAABUCAYAAADpnA4dAAAABGdBTUEAALGPC/xhBQAAA0JJREFUeF7t
+        3MFrFGccx+Fc+h94SHZN4rF/iRfPnj178eKll156EbGIRShIEXpohbaUwsxQFJEYgkGCUiylKYUeeigU
+        SsBe97J9J7zCsIz1t7uJZN95vvDcdt/N6cPsZJI1MzMzMzMzMzMzs1K3/lt1NXne8Qhghe11fJtTt/zy
+        4dPRfrW7db/+ZfOr+gXAqtv6sn61+V29n/r2OuduuaWD2qvJaXv4hZvNFKAk27ebf08kmOmQl20s26vK
+        vg8CWHVLBzO9+YPk5+NYftEc9n0IQAmWCmZ647nkz2Syda/5ve8DAEqxcDDTmz5sQ7l+WB1t323+6jsc
+        oCQLBTO94WIyXf+1+uPCrWbSdzBAaeYOZnpx+4zldONV5TfhwKDMFcz0whttLNtnLPsOAyhZOJjpRdeT
+        yfhxvdN3EEDp5gnmD6O9+ql7lsBQzRXMzW/q532HAAzBXME8/339rO8QgCGYJ5hfCyYwZIIJECSYAEGC
+        CRAkmABBggkQJJgAQYIJECSYAEGCCRAkmABBggkQJJgAQYIJECSYAEGCCRAkmABBggkQJJgAQYIJECSY
+        AEGCCRAkmABBggkQJJgAQYIJECSYAEGCCRAkmABBggkQJJgAQYIJECSYAEGCCRAkmABBggkQJJgAQdt3
+        miPBBAgYP6l3Ugv/yVl8+wQTGLLRXv00dXCafJKz+PYJJjBUo2fVm1hez0n8/wkmMETjx8dfw9tYXss5
+        fPcEExiaTixv5BTGJpjAkIx2F4xlO8EEhqJzz/KjnMD5JphA8W41k/HOAvcsZyeYQOnGD48fHZokH+f0
+        LTbBBEqWH0pvryw/zdlbfIIJlGquh9IjE0ygRJ17lrGH0iMTTKA0+Z5lG8vl7lnOTjCBknRiufw9y9kJ
+        JlCKztfwk49lO8EEStD5C56T/RrenWACq67zNXyxv+CJTjCBVTb+8RTvWc5OMIFV1fmvQ5/lpJ3uBBNY
+        RUv916FFlz6sEUxglXSuLE/3nuXs2mBuPqgP+n4ogLPmfFPv5li+vyvLN2uDuX5YHW28rPaP/VQdAJw5
+        B9VeatXfOZaf54S936UPPpdcnnEJ4Ay50rGR82VmZmZmZmZmZmZmZmb2rq2t/QezbIGIT0hmjwAAAABJ
+        RU5ErkJggg==
+</value>
+  </data>
+  <data name="bottomButton3.Image" type="System.Drawing.Bitmap, System.Drawing.Common" mimetype="application/x-microsoft.net.object.bytearray.base64">
+    <value>
+        iVBORw0KGgoAAAANSUhEUgAAAUwAAABUCAYAAADpnA4dAAAABGdBTUEAALGPC/xhBQAAA0JJREFUeF7t
+        3MFrFGccx+Fc+h94SHZN4rF/iRfPnj178eKll156EbGIRShIEXpohbaUwsxQFJEYgkGCUiylKYUeeigU
+        SsBe97J9J7zCsIz1t7uJZN95vvDcdt/N6cPsZJI1MzMzMzMzMzMzs1K3/lt1NXne8Qhghe11fJtTt/zy
+        4dPRfrW7db/+ZfOr+gXAqtv6sn61+V29n/r2OuduuaWD2qvJaXv4hZvNFKAk27ebf08kmOmQl20s26vK
+        vg8CWHVLBzO9+YPk5+NYftEc9n0IQAmWCmZ647nkz2Syda/5ve8DAEqxcDDTmz5sQ7l+WB1t323+6jsc
+        oCQLBTO94WIyXf+1+uPCrWbSdzBAaeYOZnpx+4zldONV5TfhwKDMFcz0whttLNtnLPsOAyhZOJjpRdeT
+        yfhxvdN3EEDp5gnmD6O9+ql7lsBQzRXMzW/q532HAAzBXME8/339rO8QgCGYJ5hfCyYwZIIJECSYAEGC
+        CRAkmABBggkQJJgAQYIJECSYAEGCCRAkmABBggkQJJgAQYIJECSYAEGCCRAkmABBggkQJJgAQYIJECSY
+        AEGCCRAkmABBggkQJJgAQYIJECSYAEGCCRAkmABBggkQJJgAQYIJECSYAEGCCRAkmABBggkQJJgAQdt3
+        miPBBAgYP6l3Ugv/yVl8+wQTGLLRXv00dXCafJKz+PYJJjBUo2fVm1hez0n8/wkmMETjx8dfw9tYXss5
+        fPcEExiaTixv5BTGJpjAkIx2F4xlO8EEhqJzz/KjnMD5JphA8W41k/HOAvcsZyeYQOnGD48fHZokH+f0
+        LTbBBEqWH0pvryw/zdlbfIIJlGquh9IjE0ygRJ17lrGH0iMTTKA0+Z5lG8vl7lnOTjCBknRiufw9y9kJ
+        JlCKztfwk49lO8EEStD5C56T/RrenWACq67zNXyxv+CJTjCBVTb+8RTvWc5OMIFV1fmvQ5/lpJ3uBBNY
+        RUv916FFlz6sEUxglXSuLE/3nuXs2mBuPqgP+n4ogLPmfFPv5li+vyvLN2uDuX5YHW28rPaP/VQdAJw5
+        B9VeatXfOZaf54S936UPPpdcnnEJ4Ay50rGR82VmZmZmZmZmZmZmZmb2rq2t/QezbIGIT0hmjwAAAABJ
+        RU5ErkJggg==
+</value>
+  </data>
+  <data name="roulette.BackgroundImage" type="System.Drawing.Bitmap, System.Drawing.Common" mimetype="application/x-microsoft.net.object.bytearray.base64">
+    <value>
+        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
+</value>
+  </data>
+</root>

+ 25 - 0
GUI/DataProvider/IGUIDataSource.cs

@@ -0,0 +1,25 @@
+using System;
+using System.Collections.Generic;
+using System.Drawing;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace GUI.DataProvider
+{
+    public interface IGUIDataSource
+    {
+        float Yaw { get; set; }
+        float Roll { get; set; }
+        float Pitch { get; set; }
+    }
+
+#if DEBUG
+    public class TestDataSource : IGUIDataSource
+    {
+        public float Yaw { get; set; }
+        public float Roll { get; set; }
+        public float Pitch { get; set; }
+    }
+#endif
+}

+ 28 - 0
GUI/DoubleBufferedPanel.cs

@@ -0,0 +1,28 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Diagnostics;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows.Forms;
+
+namespace GUI
+{
+    public partial class DoubleBufferedPanel : Panel
+    {
+        public DoubleBufferedPanel()
+        {
+            DoubleBuffered = true;
+            SetStyle(
+                ControlStyles.UserPaint |
+                ControlStyles.AllPaintingInWmPaint |
+                ControlStyles.DoubleBuffer, true);
+        }
+
+        protected override void WndProc(ref Message m)
+        {
+            base.WndProc(ref m);
+        }
+    }
+}

+ 33 - 0
GUI/GUI.csproj

@@ -0,0 +1,33 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+  <PropertyGroup>
+    <OutputType>WinExe</OutputType>
+    <TargetFramework>net5.0-windows</TargetFramework>
+    <UseWindowsForms>true</UseWindowsForms>
+    <StartupObject>GUI.Program</StartupObject>
+  </PropertyGroup>
+
+  <ItemGroup>
+    <None Include="..\.editorconfig" Link=".editorconfig" />
+  </ItemGroup>
+
+  <ItemGroup>
+    <PackageReference Include="System.IO.Ports" Version="5.0.1" />
+  </ItemGroup>
+
+  <ItemGroup>
+    <Compile Update="Properties\Resources.Designer.cs">
+      <DesignTime>True</DesignTime>
+      <AutoGen>True</AutoGen>
+      <DependentUpon>Resources.resx</DependentUpon>
+    </Compile>
+  </ItemGroup>
+
+  <ItemGroup>
+    <EmbeddedResource Update="Properties\Resources.resx">
+      <Generator>ResXFileCodeGenerator</Generator>
+      <LastGenOutput>Resources.Designer.cs</LastGenOutput>
+    </EmbeddedResource>
+  </ItemGroup>
+
+</Project>

+ 41 - 0
GUI/GUIUtils/CheckGroup.cs

@@ -0,0 +1,41 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows.Forms;
+
+namespace GUI.GUIUtils
+{
+    public class CheckGroup<UICheckableType> : LinkedList<WeakReference<UICheckableType>> where UICheckableType : Control, ICheckableUI
+    {
+        public void AddSafe(params UICheckableType[] checkable)
+        {
+            foreach (var it in checkable)
+            {
+                it.ResetClick();
+                it.Click += (sender, _) => CheckedObject = (UICheckableType)sender;
+                _ = AddFirst(new WeakReference<UICheckableType>(it));
+            }
+        }
+
+        WeakReference<UICheckableType> checkedObjectReference;
+
+        public UICheckableType CheckedObject
+        {
+            get
+            {
+                if (checkedObjectReference == null) return null;
+                if (checkedObjectReference.TryGetTarget(out UICheckableType target)) return target;
+                else checkedObjectReference = null;
+                return null;
+            }
+            set
+            {
+                foreach (var it in this)
+                    if (it.TryGetTarget(out UICheckableType target))
+                        target.IsChecked = target == value;
+            }
+        }
+    }
+}

+ 24 - 0
GUI/GUIUtils/EventPerformer.cs

@@ -0,0 +1,24 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace GUI.GUIUtils
+{
+    public class EventPerformer<ListenerType> : LinkedList<ListenerType> where ListenerType : class
+    {
+        public void AddSafe(ListenerType listener)
+        {
+            AddFirst(listener);
+        }
+
+        public void Perform(Action<ListenerType> performFunc)
+        {
+            foreach (var it in this)
+            {
+                performFunc?.Invoke(it);
+            }
+        }
+    }
+}

+ 64 - 0
GUI/GUIUtils/GUIParagraph.cs

@@ -0,0 +1,64 @@
+using System;
+using System.Collections.Generic;
+using System.Drawing;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows.Forms;
+
+namespace GUI.GUIUtils
+{
+    public class GUIParagraph : ISizeChangedListener
+    {
+        // 不要长期持有WinForm控件的引用(不要长期持有拥有非托管资源和/或拥有大量托管资源的对象,为了防止环状引用,在其销毁时立即释放(一代GC))
+        WeakReference<FlowLayoutPanel> reference;
+
+        public TargetXY Target { get; set; } = TargetXY.X;
+
+        public int SplitEdge { get; set; } = 10;
+
+        public enum TargetXY { X, Y }
+
+        public Action<Control, FlowLayoutPanel> FixedPaneReferenceFunc;
+
+        public GUIParagraph(FlowLayoutPanel weak_ref)
+        {
+            reference = new WeakReference<FlowLayoutPanel>(weak_ref);
+        }
+
+        public void OnSizeChanged()
+        {
+            if (reference.TryGetTarget(out FlowLayoutPanel pane))
+            {
+                var selectVal = Target switch
+                {
+                    TargetXY.X => pane.Width,
+                    TargetXY.Y => pane.Height,
+                    _ => throw new ArgumentOutOfRangeException(nameof(Target)),
+                };
+                int selectSize = selectVal - SplitEdge * 2;
+                selectSize /= pane.Controls.Count;
+                for (int i = 0; i < pane.Controls.Count; i++)
+                {
+                    var control = pane.Controls[i];
+                    switch (Target)
+                    {
+                        case TargetXY.X:
+                            {
+                                control.Size = new Size(selectSize, control.Size.Height);
+                                control.Location = new Point(SplitEdge + i * (selectSize), control.Location.Y);
+                            }
+                            break;
+                        case TargetXY.Y:
+                            {
+                                control.Size = new Size(control.Size.Width, selectSize);
+                                control.Location = new Point(control.Location.X, SplitEdge + i * selectSize);
+                            }
+                            break;
+                    }
+                    FixedPaneReferenceFunc?.Invoke(control, pane);
+                }
+            }
+        }
+    }
+}

+ 15 - 0
GUI/GUIUtils/ICheckableUI.cs

@@ -0,0 +1,15 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace GUI.GUIUtils
+{
+    public interface ICheckableUI
+    {
+        bool IsChecked { get; set; }
+
+        void ResetClick();
+    }
+}

+ 13 - 0
GUI/GUIUtils/ISizeChangedListener.cs

@@ -0,0 +1,13 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace GUI.GUIUtils
+{
+    public interface ISizeChangedListener
+    {
+        void OnSizeChanged();
+    }
+}

+ 14 - 0
GUI/Hook/IHook.cs

@@ -0,0 +1,14 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace GUI.Hook
+{
+    interface IHook
+    {
+        public void SetHook();
+        public void UnHook();
+    }
+}

+ 102 - 0
GUI/Hook/KeyboardHook.cs

@@ -0,0 +1,102 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Runtime.InteropServices;
+using System.Windows.Forms;
+
+namespace GUI.Hook
+{
+    public class KeyboardHook : IHook
+    {
+        int hHook;
+        Win32Api.HookProc KeyboardHookDelegate;
+
+        public event KeyEventHandler OnKeyDownEvent;
+        public event KeyEventHandler OnKeyUpEvent;
+        public event KeyPressEventHandler OnKeyPressEvent;
+
+        public KeyboardHook() { }
+
+        public void SetHook()
+        {
+            KeyboardHookDelegate = new Win32Api.HookProc(KeyboardHookProc);
+            Process cProcess = Process.GetCurrentProcess();
+            ProcessModule cModule = cProcess.MainModule;
+            var mh = Win32Api.GetModuleHandle(cModule.ModuleName);
+            hHook = Win32Api.SetWindowsHookEx(Win32Api.WH_KEYBOARD_LL, KeyboardHookDelegate, mh, 0);
+        }
+
+        public void UnHook()
+        {
+            Win32Api.UnhookWindowsHookEx(hHook);
+        }
+
+        readonly List<Keys> preKeysList = new();
+
+        int KeyboardHookProc(int nCode, int wParam, IntPtr lParam)
+        {
+            if ((nCode >= 0) && (OnKeyDownEvent != null || OnKeyUpEvent != null || OnKeyPressEvent != null))
+            {
+                Win32Api.KeyboardHookStruct KeyDataFromHook = (Win32Api.KeyboardHookStruct)Marshal.PtrToStructure(lParam, typeof(Win32Api.KeyboardHookStruct));
+                Keys keyData = (Keys)KeyDataFromHook.vkCode;
+                if ((OnKeyDownEvent != null || OnKeyPressEvent != null) && (wParam == Win32Api.WM_KEYDOWN || wParam == Win32Api.WM_SYSKEYDOWN))
+                {
+                    if (IsCtrlAltShiftKeys(keyData) && preKeysList.IndexOf(keyData) == -1)
+                    {
+                        preKeysList.Add(keyData);
+                    }
+                }
+                if (OnKeyDownEvent != null && (wParam == Win32Api.WM_KEYDOWN || wParam == Win32Api.WM_SYSKEYDOWN))
+                {
+                    KeyEventArgs e = new(GetDownKeys(keyData));
+                    OnKeyDownEvent(this, e);
+                }
+                if (OnKeyPressEvent != null && wParam == Win32Api.WM_KEYDOWN)
+                {
+                    byte[] keyState = new byte[256];
+                    _ = Win32Api.GetKeyboardState(keyState);
+                    byte[] inBuffer = new byte[2];
+                    if (Win32Api.ToAscii(KeyDataFromHook.vkCode, KeyDataFromHook.scanCode, keyState, inBuffer, KeyDataFromHook.flags) == 1)
+                    {
+                        KeyPressEventArgs e = new((char)inBuffer[0]);
+                        OnKeyPressEvent(this, e);
+                    }
+                }
+                if ((OnKeyDownEvent != null || OnKeyPressEvent != null) && (wParam == Win32Api.WM_KEYUP || wParam == Win32Api.WM_SYSKEYUP))
+                {
+                    if (IsCtrlAltShiftKeys(keyData))
+                    {
+                        for (int i = preKeysList.Count - 1; i >= 0; i--)
+                        {
+                            if (preKeysList[i] == keyData) { preKeysList.RemoveAt(i); }
+                        }
+                    }
+                }
+                if (OnKeyUpEvent != null && (wParam == Win32Api.WM_KEYUP || wParam == Win32Api.WM_SYSKEYUP))
+                {
+                    KeyEventArgs e = new(GetDownKeys(keyData));
+                    OnKeyUpEvent(this, e);
+                }
+            }
+            return Win32Api.CallNextHookEx(hHook, nCode, wParam, lParam);
+        }
+
+        Keys GetDownKeys(Keys key)
+        {
+            Keys rtnKey = Keys.None;
+            foreach (Keys i in preKeysList)
+            {
+                if (i == Keys.LControlKey || i == Keys.RControlKey) { rtnKey |= Keys.Control; }
+                if (i == Keys.LMenu || i == Keys.RMenu) { rtnKey |= Keys.Alt; }
+                if (i == Keys.LShiftKey || i == Keys.RShiftKey) { rtnKey |= Keys.Shift; }
+            }
+            return rtnKey | key;
+        }
+
+        static bool IsCtrlAltShiftKeys(Keys key)
+        {
+            if (key == Keys.LControlKey || key == Keys.RControlKey || key == Keys.LMenu || key == Keys.RMenu || key == Keys.LShiftKey || key == Keys.RShiftKey) { return true; }
+            return false;
+        }
+    }
+}

+ 47 - 0
GUI/Hook/MouseHook.cs

@@ -0,0 +1,47 @@
+using System;
+using System.Reflection;
+using System.Runtime.InteropServices;
+using System.Windows.Forms;
+
+namespace GUI.Hook
+{
+    public class MouseHook : IHook
+    {
+        int hHook = 0;
+        Win32Api.HookProc mouseHookDelegate;
+
+        public event MouseEventHandler OnMouseMoveEvent;
+        public event MouseEventHandler OnMouseDownEvent;
+        public event MouseEventHandler OnMouseUpEvent;
+
+        public MouseHook() { }
+
+        public void SetHook()
+        {
+            mouseHookDelegate = new Win32Api.HookProc(MouseHookProc);
+            hHook = Win32Api.SetWindowsHookEx(Win32Api.WH_MOUSE_LL, mouseHookDelegate, Marshal.GetHINSTANCE(Assembly.GetExecutingAssembly().GetModules()[0]), 0);
+        }
+
+        public void UnHook()
+        {
+            Win32Api.UnhookWindowsHookEx(hHook);
+        }
+
+        int MouseHookProc(int nCode, int wParam, IntPtr lParam)
+        {
+            Win32Api.MouseHookStruct msg = (Win32Api.MouseHookStruct)Marshal.PtrToStructure(lParam, typeof(Win32Api.MouseHookStruct));
+            if (nCode >= 0)
+            {
+                switch (wParam)
+                {
+                    case 513: OnMouseDownEvent?.Invoke(this, new(MouseButtons.Left, 0, msg.pt.x, msg.pt.y, 0)); break;
+                    case 514: OnMouseUpEvent?.Invoke(this, new(MouseButtons.Left, 0, msg.pt.x, msg.pt.y, 0)); break;
+                    case 516: OnMouseDownEvent?.Invoke(this, new(MouseButtons.Right, 0, msg.pt.x, msg.pt.y, 0)); break;
+                    case 517: OnMouseUpEvent?.Invoke(this, new(MouseButtons.Right, 0, msg.pt.x, msg.pt.y, 0)); break;
+                    default: OnMouseMoveEvent?.Invoke(this, new(MouseButtons.None, 0, msg.pt.x, msg.pt.y, 0)); break;
+                }
+            }
+            return Win32Api.CallNextHookEx(hHook, nCode, wParam, lParam);
+        }
+    }
+}

+ 25 - 0
GUI/Program.cs

@@ -0,0 +1,25 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+using System.Windows.Forms;
+
+namespace GUI
+{
+    static class Program
+    {
+        public static ComAgent comAgent;
+
+        [STAThread]
+        static void Main()
+        {
+#if DEBUG
+            Win32Api.AllocConsole();
+#endif
+            Application.SetHighDpiMode(HighDpiMode.SystemAware);
+            Application.EnableVisualStyles();
+            Application.SetCompatibleTextRenderingDefault(false);
+            Application.Run(new ControllerGUI());
+        }
+    }
+}

+ 133 - 0
GUI/Properties/Resources.Designer.cs

@@ -0,0 +1,133 @@
+//------------------------------------------------------------------------------
+// <auto-generated>
+//     此代码由工具生成。
+//     运行时版本:4.0.30319.42000
+//
+//     对此文件的更改可能会导致不正确的行为,并且如果
+//     重新生成代码,这些更改将会丢失。
+// </auto-generated>
+//------------------------------------------------------------------------------
+
+namespace GUI.Properties {
+    using System;
+    
+    
+    /// <summary>
+    ///   一个强类型的资源类,用于查找本地化的字符串等。
+    /// </summary>
+    // 此类是由 StronglyTypedResourceBuilder
+    // 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。
+    // 若要添加或移除成员,请编辑 .ResX 文件,然后重新运行 ResGen
+    // (以 /str 作为命令选项),或重新生成 VS 项目。
+    [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")]
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+    [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+    internal class Resources {
+        
+        private static global::System.Resources.ResourceManager resourceMan;
+        
+        private static global::System.Globalization.CultureInfo resourceCulture;
+        
+        [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+        internal Resources() {
+        }
+        
+        /// <summary>
+        ///   返回此类使用的缓存的 ResourceManager 实例。
+        /// </summary>
+        [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+        internal static global::System.Resources.ResourceManager ResourceManager {
+            get {
+                if (object.ReferenceEquals(resourceMan, null)) {
+                    global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("GUI.Properties.Resources", typeof(Resources).Assembly);
+                    resourceMan = temp;
+                }
+                return resourceMan;
+            }
+        }
+        
+        /// <summary>
+        ///   重写当前线程的 CurrentUICulture 属性,对
+        ///   使用此强类型资源类的所有资源查找执行重写。
+        /// </summary>
+        [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+        internal static global::System.Globalization.CultureInfo Culture {
+            get {
+                return resourceCulture;
+            }
+            set {
+                resourceCulture = value;
+            }
+        }
+        
+        /// <summary>
+        ///   查找 System.Drawing.Bitmap 类型的本地化资源。
+        /// </summary>
+        internal static System.Drawing.Bitmap Button_Checked {
+            get {
+                object obj = ResourceManager.GetObject("Button_Checked", resourceCulture);
+                return ((System.Drawing.Bitmap)(obj));
+            }
+        }
+        
+        /// <summary>
+        ///   查找 System.Drawing.Bitmap 类型的本地化资源。
+        /// </summary>
+        internal static System.Drawing.Bitmap Button_Unchecked {
+            get {
+                object obj = ResourceManager.GetObject("Button_Unchecked", resourceCulture);
+                return ((System.Drawing.Bitmap)(obj));
+            }
+        }
+        
+        /// <summary>
+        ///   查找 System.Drawing.Bitmap 类型的本地化资源。
+        /// </summary>
+        internal static System.Drawing.Bitmap DirectionScale {
+            get {
+                object obj = ResourceManager.GetObject("DirectionScale", resourceCulture);
+                return ((System.Drawing.Bitmap)(obj));
+            }
+        }
+        
+        /// <summary>
+        ///   查找 System.Drawing.Bitmap 类型的本地化资源。
+        /// </summary>
+        internal static System.Drawing.Bitmap MainVideo {
+            get {
+                object obj = ResourceManager.GetObject("MainVideo", resourceCulture);
+                return ((System.Drawing.Bitmap)(obj));
+            }
+        }
+        
+        /// <summary>
+        ///   查找 System.Drawing.Bitmap 类型的本地化资源。
+        /// </summary>
+        internal static System.Drawing.Bitmap Right_Image_Holder {
+            get {
+                object obj = ResourceManager.GetObject("Right_Image_Holder", resourceCulture);
+                return ((System.Drawing.Bitmap)(obj));
+            }
+        }
+        
+        /// <summary>
+        ///   查找 System.Drawing.Bitmap 类型的本地化资源。
+        /// </summary>
+        internal static System.Drawing.Bitmap Roulette {
+            get {
+                object obj = ResourceManager.GetObject("Roulette", resourceCulture);
+                return ((System.Drawing.Bitmap)(obj));
+            }
+        }
+        
+        /// <summary>
+        ///   查找 System.Drawing.Bitmap 类型的本地化资源。
+        /// </summary>
+        internal static System.Drawing.Bitmap Tri {
+            get {
+                object obj = ResourceManager.GetObject("Tri", resourceCulture);
+                return ((System.Drawing.Bitmap)(obj));
+            }
+        }
+    }
+}

+ 142 - 0
GUI/Properties/Resources.resx

@@ -0,0 +1,142 @@
+<?xml version="1.0" encoding="utf-8"?>
+<root>
+  <!-- 
+    Microsoft ResX Schema 
+    
+    Version 2.0
+    
+    The primary goals of this format is to allow a simple XML format 
+    that is mostly human readable. The generation and parsing of the 
+    various data types are done through the TypeConverter classes 
+    associated with the data types.
+    
+    Example:
+    
+    ... ado.net/XML headers & schema ...
+    <resheader name="resmimetype">text/microsoft-resx</resheader>
+    <resheader name="version">2.0</resheader>
+    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+        <value>[base64 mime encoded serialized .NET Framework object]</value>
+    </data>
+    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+        <comment>This is a comment</comment>
+    </data>
+                
+    There are any number of "resheader" rows that contain simple 
+    name/value pairs.
+    
+    Each data row contains a name, and value. The row also contains a 
+    type or mimetype. Type corresponds to a .NET class that support 
+    text/value conversion through the TypeConverter architecture. 
+    Classes that don't support this are serialized and stored with the 
+    mimetype set.
+    
+    The mimetype is used for serialized objects, and tells the 
+    ResXResourceReader how to depersist the object. This is currently not 
+    extensible. For a given mimetype the value must be set accordingly:
+    
+    Note - application/x-microsoft.net.object.binary.base64 is the format 
+    that the ResXResourceWriter will generate, however the reader can 
+    read any of the formats listed below.
+    
+    mimetype: application/x-microsoft.net.object.binary.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
+            : and then encoded with base64 encoding.
+    
+    mimetype: application/x-microsoft.net.object.soap.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+            : and then encoded with base64 encoding.
+
+    mimetype: application/x-microsoft.net.object.bytearray.base64
+    value   : The object must be serialized into a byte array 
+            : using a System.ComponentModel.TypeConverter
+            : and then encoded with base64 encoding.
+    -->
+  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+    <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
+    <xsd:element name="root" msdata:IsDataSet="true">
+      <xsd:complexType>
+        <xsd:choice maxOccurs="unbounded">
+          <xsd:element name="metadata">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" />
+              </xsd:sequence>
+              <xsd:attribute name="name" use="required" type="xsd:string" />
+              <xsd:attribute name="type" type="xsd:string" />
+              <xsd:attribute name="mimetype" type="xsd:string" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="assembly">
+            <xsd:complexType>
+              <xsd:attribute name="alias" type="xsd:string" />
+              <xsd:attribute name="name" type="xsd:string" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="data">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="resheader">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" />
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:choice>
+      </xsd:complexType>
+    </xsd:element>
+  </xsd:schema>
+  <resheader name="resmimetype">
+    <value>text/microsoft-resx</value>
+  </resheader>
+  <resheader name="version">
+    <value>2.0</value>
+  </resheader>
+  <resheader name="reader">
+    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <resheader name="writer">
+    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
+  <data name="Button_Checked" type="System.Resources.ResXFileRef, System.Windows.Forms">
+    <value>..\Resources\Button_Checked.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
+  </data>
+  <data name="Button_Unchecked" type="System.Resources.ResXFileRef, System.Windows.Forms">
+    <value>..\Resources\Button_Unchecked.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
+  </data>
+  <data name="DirectionScale" type="System.Resources.ResXFileRef, System.Windows.Forms">
+    <value>..\Resources\DirectionScale.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
+  </data>
+  <data name="MainVideo" type="System.Resources.ResXFileRef, System.Windows.Forms">
+    <value>..\Resources\MainVideo.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
+  </data>
+  <data name="Right_Image_Holder" type="System.Resources.ResXFileRef, System.Windows.Forms">
+    <value>..\Resources\Right_Image_Holder.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
+  </data>
+  <data name="Roulette" type="System.Resources.ResXFileRef, System.Windows.Forms">
+    <value>..\Resources\Roulette.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
+  </data>
+  <data name="Tri" type="System.Resources.ResXFileRef, System.Windows.Forms">
+    <value>..\Resources\Tri.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
+  </data>
+</root>

BIN
GUI/Resources/Button_Checked.png


BIN
GUI/Resources/Button_Unchecked.png


BIN
GUI/Resources/DirectionScale.png


BIN
GUI/Resources/MainVideo.png


BIN
GUI/Resources/Right_Image_Holder.png


BIN
GUI/Resources/Roulette.png


BIN
GUI/Resources/Tri.png


+ 52 - 0
GUI/Roulette.cs

@@ -0,0 +1,52 @@
+using GUI.Properties;
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Data;
+using System.Drawing;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows.Forms;
+
+namespace GUI
+{
+    public partial class Roulette : Panel
+    {
+        float degree;
+
+        public float Degree
+        {
+            get => degree;
+            set
+            {
+                degree = value % 360;
+                var bkup = BackgroundImage;
+                BackgroundImage = rotateImage(sourceImage, degree);
+                bkup?.Dispose();
+            }
+        }
+
+        Bitmap sourceImage;
+
+        public Roulette()
+        {
+            sourceImage = new(Resources.Roulette);
+            Degree = 0;
+            DoubleBuffered = true;
+        }
+
+        static Bitmap rotateImage(Bitmap b, float angle)
+        {
+            Bitmap returnBitmap = new Bitmap(b.Width, b.Height);
+            Graphics g = Graphics.FromImage(returnBitmap);
+            g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.NearestNeighbor;
+            g.TranslateTransform((float)b.Width / 2, (float)b.Height / 2);
+            g.RotateTransform(360 - angle);
+            g.TranslateTransform(-(float)b.Width / 2, -(float)b.Height / 2);
+            g.DrawImage(b, new Point(0, 0));
+            g.Dispose();
+            return returnBitmap;
+        }
+    }
+}

+ 108 - 0
GUI/ScaleRule.Designer.cs

@@ -0,0 +1,108 @@
+
+namespace GUI
+{
+    partial class ScaleRule
+    {
+        /// <summary> 
+        /// 必需的设计器变量。
+        /// </summary>
+        private System.ComponentModel.IContainer components = null;
+
+        /// <summary> 
+        /// 清理所有正在使用的资源。
+        /// </summary>
+        /// <param name="disposing">如果应释放托管资源,为 true;否则为 false。</param>
+        protected override void Dispose(bool disposing)
+        {
+            if (disposing && (components != null))
+            {
+                components.Dispose();
+            }
+            base.Dispose(disposing);
+        }
+
+        #region 组件设计器生成的代码
+
+        /// <summary> 
+        /// 设计器支持所需的方法 - 不要修改
+        /// 使用代码编辑器修改此方法的内容。
+        /// </summary>
+        private void InitializeComponent()
+        {
+            this.pictureBox1 = new System.Windows.Forms.PictureBox();
+            this.pictureBox2 = new System.Windows.Forms.PictureBox();
+            this.ScalePane = new System.Windows.Forms.Panel();
+            this.pictureBox3 = new System.Windows.Forms.PictureBox();
+            ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).BeginInit();
+            ((System.ComponentModel.ISupportInitialize)(this.pictureBox2)).BeginInit();
+            this.ScalePane.SuspendLayout();
+            ((System.ComponentModel.ISupportInitialize)(this.pictureBox3)).BeginInit();
+            this.SuspendLayout();
+            // 
+            // pictureBox1
+            // 
+            this.pictureBox1.BackColor = System.Drawing.Color.Transparent;
+            this.pictureBox1.Image = global::GUI.Properties.Resources.DirectionScale;
+            this.pictureBox1.Location = new System.Drawing.Point(0, 0);
+            this.pictureBox1.Name = "pictureBox1";
+            this.pictureBox1.Size = new System.Drawing.Size(556, 40);
+            this.pictureBox1.SizeMode = System.Windows.Forms.PictureBoxSizeMode.StretchImage;
+            this.pictureBox1.TabIndex = 0;
+            this.pictureBox1.TabStop = false;
+            // 
+            // pictureBox2
+            // 
+            this.pictureBox2.BackColor = System.Drawing.Color.Transparent;
+            this.pictureBox2.Image = global::GUI.Properties.Resources.DirectionScale;
+            this.pictureBox2.Location = new System.Drawing.Point(554, 0);
+            this.pictureBox2.Name = "pictureBox2";
+            this.pictureBox2.Size = new System.Drawing.Size(556, 40);
+            this.pictureBox2.SizeMode = System.Windows.Forms.PictureBoxSizeMode.StretchImage;
+            this.pictureBox2.TabIndex = 3;
+            this.pictureBox2.TabStop = false;
+            // 
+            // ScalePane
+            // 
+            this.ScalePane.Controls.Add(this.pictureBox2);
+            this.ScalePane.Controls.Add(this.pictureBox1);
+            this.ScalePane.Location = new System.Drawing.Point(0, 0);
+            this.ScalePane.Name = "ScalePane";
+            this.ScalePane.Size = new System.Drawing.Size(1110, 40);
+            this.ScalePane.TabIndex = 4;
+            // 
+            // pictureBox3
+            // 
+            this.pictureBox3.BackColor = System.Drawing.Color.Transparent;
+            this.pictureBox3.Image = global::GUI.Properties.Resources.Tri;
+            this.pictureBox3.Location = new System.Drawing.Point(269, 40);
+            this.pictureBox3.Name = "pictureBox3";
+            this.pictureBox3.Size = new System.Drawing.Size(15, 15);
+            this.pictureBox3.SizeMode = System.Windows.Forms.PictureBoxSizeMode.StretchImage;
+            this.pictureBox3.TabIndex = 5;
+            this.pictureBox3.TabStop = false;
+            // 
+            // ScaleRule
+            // 
+            this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 17F);
+            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+            this.BackColor = System.Drawing.Color.Transparent;
+            this.Controls.Add(this.pictureBox3);
+            this.Controls.Add(this.ScalePane);
+            this.Name = "ScaleRule";
+            this.Size = new System.Drawing.Size(553, 59);
+            ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).EndInit();
+            ((System.ComponentModel.ISupportInitialize)(this.pictureBox2)).EndInit();
+            this.ScalePane.ResumeLayout(false);
+            ((System.ComponentModel.ISupportInitialize)(this.pictureBox3)).EndInit();
+            this.ResumeLayout(false);
+
+        }
+
+        #endregion
+
+        private System.Windows.Forms.PictureBox pictureBox1;
+        private System.Windows.Forms.PictureBox pictureBox2;
+        private System.Windows.Forms.Panel ScalePane;
+        private System.Windows.Forms.PictureBox pictureBox3;
+    }
+}

+ 45 - 0
GUI/ScaleRule.cs

@@ -0,0 +1,45 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Data;
+using System.Drawing;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows.Forms;
+
+namespace GUI
+{
+    public partial class ScaleRule : UserControl
+    {
+        float degree;
+
+        public float Degree
+        {
+            get => degree;
+            set
+            {
+                value = Math.Abs(value % 360);
+                if (value > 174)
+                {
+                    float t = value - 174;
+                    float v = -t * 1.542f;
+                    ScalePane.Location = new Point((int)v, 0);
+                }
+                else
+                {
+                    float t = 174 - value;
+                    float v = t * 1.542f - 554f;
+                    ScalePane.Location = new Point((int)v, 0);
+                }
+                degree = value;
+            }
+        }
+
+        public ScaleRule()
+        {
+            InitializeComponent();
+            Degree = 0;
+        }
+    }
+}

+ 60 - 0
GUI/ScaleRule.resx

@@ -0,0 +1,60 @@
+<root>
+  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+    <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
+    <xsd:element name="root" msdata:IsDataSet="true">
+      <xsd:complexType>
+        <xsd:choice maxOccurs="unbounded">
+          <xsd:element name="metadata">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" />
+              </xsd:sequence>
+              <xsd:attribute name="name" use="required" type="xsd:string" />
+              <xsd:attribute name="type" type="xsd:string" />
+              <xsd:attribute name="mimetype" type="xsd:string" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="assembly">
+            <xsd:complexType>
+              <xsd:attribute name="alias" type="xsd:string" />
+              <xsd:attribute name="name" type="xsd:string" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="data">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="resheader">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" />
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:choice>
+      </xsd:complexType>
+    </xsd:element>
+  </xsd:schema>
+  <resheader name="resmimetype">
+    <value>text/microsoft-resx</value>
+  </resheader>
+  <resheader name="version">
+    <value>2.0</value>
+  </resheader>
+  <resheader name="reader">
+    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <resheader name="writer">
+    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+</root>

+ 75 - 0
GUI/Win32Api.cs

@@ -0,0 +1,75 @@
+using System;
+using System.Runtime.InteropServices;
+
+namespace GUI
+{
+    public class Win32Api
+    {
+        public const int WM_KEYDOWN = 0x100;
+        public const int WM_KEYUP = 0x101;
+        public const int WM_SYSKEYDOWN = 0x104;
+        public const int WM_SYSKEYUP = 0x105;
+        public const int WH_KEYBOARD_LL = 13;
+        public const int WH_MOUSE_LL = 14;
+
+        public const int MOUSEEVENTF_LEFTDOWN = 0x0002;
+        public const int MOUSEEVENTF_MOVE = 0x0001;
+        public const int MOUSEEVENTF_LEFTUP = 0x0004;
+        public const int MOUSEEVENTF_ABSOLUTE = 0x8000;
+        public const int MOUSEEVENTF_RIGHTDOWN = 0x0008;
+        public const int MOUSEEVENTF_RIGHTUP = 0x0010;
+        public const int MOUSEEVENTF_MIDDLEDOWN = 0x0020;
+        public const int MOUSEEVENTF_MIDDLEUP = 0x0040;
+
+        [StructLayout(LayoutKind.Sequential)]
+        public class KeyboardHookStruct
+        {
+            public int vkCode;
+            public int scanCode;
+            public int flags;
+            public int time;
+            public int dwExtraInfo;
+        }
+
+        [StructLayout(LayoutKind.Sequential)]
+        public class POINT
+        {
+            public int x;
+            public int y;
+        }
+
+        [StructLayout(LayoutKind.Sequential)]
+        public class MouseHookStruct
+        {
+            public POINT pt;
+            public int hwnd;
+            public int wHitTestCode;
+            public int dwExtraInfo;
+        }
+
+        public delegate int HookProc(int nCode, Int32 wParam, IntPtr lParam);
+        [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
+        public static extern int SetWindowsHookEx(int idHook, HookProc lpfn, IntPtr hInstance, int threadId);
+
+        [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
+        public static extern bool UnhookWindowsHookEx(int idHook);
+
+        [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
+        public static extern int CallNextHookEx(int idHook, int nCode, Int32 wParam, IntPtr lParam);
+
+        [DllImport("user32")]
+        public static extern int ToAscii(int uVirtKey, int uScanCode, byte[] lpbKeyState, byte[] lpwTransKey, int fuState);
+
+        [DllImport("user32")]
+        public static extern int GetKeyboardState(byte[] pbKeyState);
+
+        [DllImport("kernel32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
+        public static extern IntPtr GetModuleHandle(string lpModuleName);
+
+        [DllImport("kernel32.dll")]
+        public static extern void AllocConsole();
+
+        [DllImport("user32")]
+        public static extern void mouse_event(int dwFlags, int dx, int dy, int dwData, int dwExtraInfo);
+    }
+}

+ 30 - 0
GUIWinProg.sln

@@ -0,0 +1,30 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 16
+VisualStudioVersion = 16.0.31025.194
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GUI", "GUI\GUI.csproj", "{0C819CCA-61CA-45E1-934F-8D1122662533}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{7D1440A2-1E7C-4528-9DF1-8B0C190B3BB1}"
+	ProjectSection(SolutionItems) = preProject
+		.editorconfig = .editorconfig
+	EndProjectSection
+EndProject
+Global
+	GlobalSection(SolutionConfigurationPlatforms) = preSolution
+		Debug|Any CPU = Debug|Any CPU
+		Release|Any CPU = Release|Any CPU
+	EndGlobalSection
+	GlobalSection(ProjectConfigurationPlatforms) = postSolution
+		{0C819CCA-61CA-45E1-934F-8D1122662533}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{0C819CCA-61CA-45E1-934F-8D1122662533}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{0C819CCA-61CA-45E1-934F-8D1122662533}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{0C819CCA-61CA-45E1-934F-8D1122662533}.Release|Any CPU.Build.0 = Release|Any CPU
+	EndGlobalSection
+	GlobalSection(SolutionProperties) = preSolution
+		HideSolutionNode = FALSE
+	EndGlobalSection
+	GlobalSection(ExtensibilityGlobals) = postSolution
+		SolutionGuid = {B5B0A4A9-4F93-4B75-9CD9-8DF842185ACD}
+	EndGlobalSection
+EndGlobal

BIN
MainVideo.png


BIN
Right_Image_Holder.png


BIN
Roulette.png


BIN
Tri.png