From b959c5ff8a536061b79cf7518807f04cc2e1f278 Mon Sep 17 00:00:00 2001 From: Alexander Eckart Date: Sat, 4 May 2013 23:48:08 +0200 Subject: [PATCH] introduced controller framework, simple control test implementation --- .gitignore | 6 +- .../ClientServerLib/ClientServerLib.csproj | 1 + .../ClientServerLib/Vocaluxe/CCommands.cs | 50 +++-- .../ClientTest/ClientTest.Designer.cs | 207 +++++++++++------- ClientServerLib/ClientTest/ClientTest.cs | 48 ++++ Output/ClientServerLib.dll | Bin 38912 -> 39424 bytes Vocaluxe/Base/CController.cs | 2 + Vocaluxe/Base/Server/CClientHandler.cs | 6 + Vocaluxe/Base/Server/CVocaluxeServer.cs | 44 +++- Vocaluxe/Lib/Input/CControllerFramework.cs | 104 +++++++++ Vocaluxe/Vocaluxe.csproj | 1 + 11 files changed, 356 insertions(+), 113 deletions(-) create mode 100644 Vocaluxe/Lib/Input/CControllerFramework.cs diff --git a/.gitignore b/.gitignore index 0e90224b..0dafa6e4 100644 --- a/.gitignore +++ b/.gitignore @@ -43,7 +43,5 @@ Output/Songs /ClientServerLib/ClientServerLib/bin/Debug/ClientServerLib.dll /ClientServerLib/ClientTest/bin/Debug/ClientServerLib.dll /ClientServerLib/ClientServerLib/bin/ClientServerLib.dll -/ClientServerLib/ClientTest/bin/Debug/ClientTest.exe -/ClientServerLib/ClientTest/bin/Debug/ClientTest.vshost.exe -/ClientServerLib/ClientTest/bin/Release/ClientTest.vshost.exe -/ClientServerLib/ClientTest/bin/Release/ClientTest.vshost.exe.manifest +ClientTest.exe +*.vshost.* diff --git a/ClientServerLib/ClientServerLib/ClientServerLib.csproj b/ClientServerLib/ClientServerLib/ClientServerLib.csproj index b9a347bb..8264baeb 100644 --- a/ClientServerLib/ClientServerLib/ClientServerLib.csproj +++ b/ClientServerLib/ClientServerLib/ClientServerLib.csproj @@ -33,6 +33,7 @@ + diff --git a/ClientServerLib/ClientServerLib/Vocaluxe/CCommands.cs b/ClientServerLib/ClientServerLib/Vocaluxe/CCommands.cs index ef3a54b1..cec22606 100644 --- a/ClientServerLib/ClientServerLib/Vocaluxe/CCommands.cs +++ b/ClientServerLib/ClientServerLib/Vocaluxe/CCommands.cs @@ -6,6 +6,7 @@ using System.Runtime.Serialization.Formatters; using System.Runtime.Serialization.Formatters.Binary; using System.Text; using System.Security.Cryptography; +using System.Windows.Forms; namespace Vocaluxe.Base.Server @@ -14,30 +15,35 @@ namespace Vocaluxe.Base.Server { public static SHA256Managed SHA256 = new SHA256Managed(); + public const int ResponseOK = 1; + public const int ResponseNOK = 2; + public const int CommandLogin = 20; public const int ResponseLoginWrongPassword = 21; public const int ResponseLoginFailed = 22; public const int ResponseLoginOK = 23; + + public const int CommandSendKeyEvent = 100; + public const int CommandSendKeyStroke = 101; + + public const int CommandSendMouseMoveEvent = 200; + public const int CommandSendMouseLBDownEvent = 220; + public const int CommandSendMouseLBUpEvent = 221; + public const int CommandSendMouseRBDownEvent = 230; + public const int CommandSendMouseRBUpEvent = 231; + public const int ComamndSendMouseMBDownEvent = 240; + public const int ComamndSendMouseMBUpEvent = 241; + public const int CommandSendMouseWheelEvent = 250; + + #region General public static byte[] CreateCommandWithoutParams(int Command) { return BitConverter.GetBytes(Command); } + #endregion General - /* - public static byte[] CreateResponseSendUserNames(SUsers Users) - { - return Serialize(ResponseSendUserNames, Users); - } - - public static SUsers DecodeResponseSendUserNames(byte[] response) - { - SUsers users; - TryDeserialize(response, out users); - return users; - } - */ - + #region Login public static byte[] CreateCommandLogin(string Password) { SLoginData data = new SLoginData(); @@ -46,11 +52,25 @@ namespace Vocaluxe.Base.Server return Serialize(CommandLogin, data); } - public static bool ResponseCommandLogin(byte[] Message, out SLoginData LoginData) + public static bool DecodeCommandLogin(byte[] Message, out SLoginData LoginData) { return TryDeserialize(Message, out LoginData); } + #endregion Login + #region Keyboard + public static byte[] CreateCommandSendKeyEvent(Keys Key) + { + return Serialize(CommandSendKeyEvent, Key); + } + + public static bool DecodeCommandSendKeyEvent(byte[] Message, out Keys Key) + { + return TryDeserialize(Message, out Key); + } + #endregion Keyboard + + private static byte[] Serialize(int Command, T obj) { byte[] command = BitConverter.GetBytes(Command); diff --git a/ClientServerLib/ClientTest/ClientTest.Designer.cs b/ClientServerLib/ClientTest/ClientTest.Designer.cs index c1101555..3ff2a12d 100644 --- a/ClientServerLib/ClientTest/ClientTest.Designer.cs +++ b/ClientServerLib/ClientTest/ClientTest.Designer.cs @@ -28,60 +28,28 @@ /// private void InitializeComponent() { - this.lbServerIP = new System.Windows.Forms.Label(); - this.lbPort = new System.Windows.Forms.Label(); - this.tbServerIP = new System.Windows.Forms.TextBox(); - this.tbPort = new System.Windows.Forms.TextBox(); this.statusStrip1 = new System.Windows.Forms.StatusStrip(); this.lbConnectionStatus = new System.Windows.Forms.ToolStripStatusLabel(); this.lbConnectionStatusText = new System.Windows.Forms.ToolStripStatusLabel(); - this.btConnect = new System.Windows.Forms.Button(); - this.panel1 = new System.Windows.Forms.Panel(); this.tbDataSending = new System.Windows.Forms.TextBox(); this.lbDateSending = new System.Windows.Forms.Label(); this.lbDataReceiving = new System.Windows.Forms.Label(); this.tbDataReceiving = new System.Windows.Forms.TextBox(); + this.lbPort = new System.Windows.Forms.Label(); + this.tbPort = new System.Windows.Forms.TextBox(); + this.tbServerIP = new System.Windows.Forms.TextBox(); + this.btConnect = new System.Windows.Forms.Button(); + this.lbServerIP = new System.Windows.Forms.Label(); this.tbPassword = new System.Windows.Forms.TextBox(); this.lbPassword = new System.Windows.Forms.Label(); this.btLogin = new System.Windows.Forms.Button(); + this.btLeft = new System.Windows.Forms.Button(); + this.btRight = new System.Windows.Forms.Button(); + this.btUp = new System.Windows.Forms.Button(); + this.btDown = new System.Windows.Forms.Button(); this.statusStrip1.SuspendLayout(); - this.panel1.SuspendLayout(); this.SuspendLayout(); // - // lbServerIP - // - this.lbServerIP.AutoSize = true; - this.lbServerIP.Location = new System.Drawing.Point(3, 0); - this.lbServerIP.Name = "lbServerIP"; - this.lbServerIP.Size = new System.Drawing.Size(54, 13); - this.lbServerIP.TabIndex = 0; - this.lbServerIP.Text = "Server IP:"; - // - // lbPort - // - this.lbPort.AutoSize = true; - this.lbPort.Location = new System.Drawing.Point(3, 26); - this.lbPort.Name = "lbPort"; - this.lbPort.Size = new System.Drawing.Size(29, 13); - this.lbPort.TabIndex = 1; - this.lbPort.Text = "Port:"; - // - // tbServerIP - // - this.tbServerIP.Location = new System.Drawing.Point(85, -3); - this.tbServerIP.Name = "tbServerIP"; - this.tbServerIP.Size = new System.Drawing.Size(86, 20); - this.tbServerIP.TabIndex = 2; - this.tbServerIP.Text = "127.0.0.1"; - // - // tbPort - // - this.tbPort.Location = new System.Drawing.Point(85, 23); - this.tbPort.Name = "tbPort"; - this.tbPort.Size = new System.Drawing.Size(86, 20); - this.tbPort.TabIndex = 3; - this.tbPort.Text = "3000"; - // // statusStrip1 // this.statusStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { @@ -105,31 +73,6 @@ this.lbConnectionStatusText.Size = new System.Drawing.Size(79, 17); this.lbConnectionStatusText.Text = "Disconnected"; // - // btConnect - // - this.btConnect.Location = new System.Drawing.Point(6, 49); - this.btConnect.Name = "btConnect"; - this.btConnect.Size = new System.Drawing.Size(165, 23); - this.btConnect.TabIndex = 5; - this.btConnect.Text = "Connect"; - this.btConnect.UseVisualStyleBackColor = true; - this.btConnect.Click += new System.EventHandler(this.btConnect_Click); - // - // panel1 - // - this.panel1.Controls.Add(this.btLogin); - this.panel1.Controls.Add(this.lbPassword); - this.panel1.Controls.Add(this.tbPassword); - this.panel1.Controls.Add(this.lbServerIP); - this.panel1.Controls.Add(this.btConnect); - this.panel1.Controls.Add(this.tbServerIP); - this.panel1.Controls.Add(this.tbPort); - this.panel1.Controls.Add(this.lbPort); - this.panel1.Location = new System.Drawing.Point(822, 12); - this.panel1.Name = "panel1"; - this.panel1.Size = new System.Drawing.Size(174, 513); - this.panel1.TabIndex = 6; - // // tbDataSending // this.tbDataSending.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) @@ -137,7 +80,7 @@ this.tbDataSending.Location = new System.Drawing.Point(12, 25); this.tbDataSending.Multiline = true; this.tbDataSending.Name = "tbDataSending"; - this.tbDataSending.Size = new System.Drawing.Size(804, 120); + this.tbDataSending.Size = new System.Drawing.Size(810, 120); this.tbDataSending.TabIndex = 7; // // lbDateSending @@ -165,12 +108,56 @@ this.tbDataReceiving.Location = new System.Drawing.Point(12, 213); this.tbDataReceiving.Multiline = true; this.tbDataReceiving.Name = "tbDataReceiving"; - this.tbDataReceiving.Size = new System.Drawing.Size(804, 120); + this.tbDataReceiving.Size = new System.Drawing.Size(810, 120); this.tbDataReceiving.TabIndex = 9; // + // lbPort + // + this.lbPort.AutoSize = true; + this.lbPort.Location = new System.Drawing.Point(828, 41); + this.lbPort.Name = "lbPort"; + this.lbPort.Size = new System.Drawing.Size(29, 13); + this.lbPort.TabIndex = 1; + this.lbPort.Text = "Port:"; + // + // tbPort + // + this.tbPort.Location = new System.Drawing.Point(910, 38); + this.tbPort.Name = "tbPort"; + this.tbPort.Size = new System.Drawing.Size(86, 20); + this.tbPort.TabIndex = 3; + this.tbPort.Text = "3000"; + // + // tbServerIP + // + this.tbServerIP.Location = new System.Drawing.Point(910, 12); + this.tbServerIP.Name = "tbServerIP"; + this.tbServerIP.Size = new System.Drawing.Size(86, 20); + this.tbServerIP.TabIndex = 2; + this.tbServerIP.Text = "127.0.0.1"; + // + // btConnect + // + this.btConnect.Location = new System.Drawing.Point(831, 64); + this.btConnect.Name = "btConnect"; + this.btConnect.Size = new System.Drawing.Size(165, 23); + this.btConnect.TabIndex = 5; + this.btConnect.Text = "Connect"; + this.btConnect.UseVisualStyleBackColor = true; + this.btConnect.Click += new System.EventHandler(this.btConnect_Click); + // + // lbServerIP + // + this.lbServerIP.AutoSize = true; + this.lbServerIP.Location = new System.Drawing.Point(828, 15); + this.lbServerIP.Name = "lbServerIP"; + this.lbServerIP.Size = new System.Drawing.Size(54, 13); + this.lbServerIP.TabIndex = 0; + this.lbServerIP.Text = "Server IP:"; + // // tbPassword // - this.tbPassword.Location = new System.Drawing.Point(85, 113); + this.tbPassword.Location = new System.Drawing.Point(910, 128); this.tbPassword.Name = "tbPassword"; this.tbPassword.Size = new System.Drawing.Size(86, 20); this.tbPassword.TabIndex = 6; @@ -179,7 +166,7 @@ // lbPassword // this.lbPassword.AutoSize = true; - this.lbPassword.Location = new System.Drawing.Point(3, 116); + this.lbPassword.Location = new System.Drawing.Point(828, 131); this.lbPassword.Name = "lbPassword"; this.lbPassword.Size = new System.Drawing.Size(56, 13); this.lbPassword.TabIndex = 7; @@ -188,7 +175,7 @@ // btLogin // this.btLogin.Enabled = false; - this.btLogin.Location = new System.Drawing.Point(6, 139); + this.btLogin.Location = new System.Drawing.Point(831, 154); this.btLogin.Name = "btLogin"; this.btLogin.Size = new System.Drawing.Size(165, 23); this.btLogin.TabIndex = 8; @@ -196,23 +183,76 @@ this.btLogin.UseVisualStyleBackColor = true; this.btLogin.Click += new System.EventHandler(this.btLogin_Click); // + // btLeft + // + this.btLeft.Enabled = false; + this.btLeft.Location = new System.Drawing.Point(831, 238); + this.btLeft.Name = "btLeft"; + this.btLeft.Size = new System.Drawing.Size(69, 23); + this.btLeft.TabIndex = 10; + this.btLeft.Text = "Left"; + this.btLeft.UseVisualStyleBackColor = true; + this.btLeft.Click += new System.EventHandler(this.btLeft_Click); + // + // btRight + // + this.btRight.Enabled = false; + this.btRight.Location = new System.Drawing.Point(927, 238); + this.btRight.Name = "btRight"; + this.btRight.Size = new System.Drawing.Size(69, 23); + this.btRight.TabIndex = 11; + this.btRight.Text = "Right"; + this.btRight.UseVisualStyleBackColor = true; + this.btRight.Click += new System.EventHandler(this.brRight_Click); + // + // btUp + // + this.btUp.Enabled = false; + this.btUp.Location = new System.Drawing.Point(877, 209); + this.btUp.Name = "btUp"; + this.btUp.Size = new System.Drawing.Size(69, 23); + this.btUp.TabIndex = 12; + this.btUp.Text = "Up"; + this.btUp.UseVisualStyleBackColor = true; + this.btUp.Click += new System.EventHandler(this.btUp_Click); + // + // btDown + // + this.btDown.Enabled = false; + this.btDown.Location = new System.Drawing.Point(877, 267); + this.btDown.Name = "btDown"; + this.btDown.Size = new System.Drawing.Size(69, 23); + this.btDown.TabIndex = 13; + this.btDown.Text = "Down"; + this.btDown.UseVisualStyleBackColor = true; + this.btDown.Click += new System.EventHandler(this.btDown_Click); + // // ClientTest // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.ClientSize = new System.Drawing.Size(1008, 550); + this.Controls.Add(this.btDown); + this.Controls.Add(this.btUp); + this.Controls.Add(this.btRight); + this.Controls.Add(this.btLeft); + this.Controls.Add(this.btLogin); this.Controls.Add(this.tbDataReceiving); + this.Controls.Add(this.lbPassword); this.Controls.Add(this.lbDataReceiving); + this.Controls.Add(this.tbPassword); this.Controls.Add(this.lbDateSending); + this.Controls.Add(this.lbServerIP); this.Controls.Add(this.tbDataSending); - this.Controls.Add(this.panel1); + this.Controls.Add(this.btConnect); + this.Controls.Add(this.tbServerIP); this.Controls.Add(this.statusStrip1); + this.Controls.Add(this.tbPort); + this.Controls.Add(this.lbPort); this.Name = "ClientTest"; this.Text = "ClientTest"; this.statusStrip1.ResumeLayout(false); this.statusStrip1.PerformLayout(); - this.panel1.ResumeLayout(false); - this.panel1.PerformLayout(); this.ResumeLayout(false); this.PerformLayout(); @@ -220,22 +260,25 @@ #endregion - private System.Windows.Forms.Label lbServerIP; - private System.Windows.Forms.Label lbPort; - private System.Windows.Forms.TextBox tbServerIP; - private System.Windows.Forms.TextBox tbPort; private System.Windows.Forms.StatusStrip statusStrip1; private System.Windows.Forms.ToolStripStatusLabel lbConnectionStatus; private System.Windows.Forms.ToolStripStatusLabel lbConnectionStatusText; - private System.Windows.Forms.Button btConnect; - private System.Windows.Forms.Panel panel1; private System.Windows.Forms.TextBox tbDataSending; private System.Windows.Forms.Label lbDateSending; private System.Windows.Forms.Label lbDataReceiving; private System.Windows.Forms.TextBox tbDataReceiving; - private System.Windows.Forms.Button btLogin; - private System.Windows.Forms.Label lbPassword; + private System.Windows.Forms.Label lbPort; + private System.Windows.Forms.TextBox tbPort; + private System.Windows.Forms.TextBox tbServerIP; + private System.Windows.Forms.Button btConnect; + private System.Windows.Forms.Label lbServerIP; private System.Windows.Forms.TextBox tbPassword; + private System.Windows.Forms.Label lbPassword; + private System.Windows.Forms.Button btLogin; + private System.Windows.Forms.Button btLeft; + private System.Windows.Forms.Button btRight; + private System.Windows.Forms.Button btUp; + private System.Windows.Forms.Button btDown; } } diff --git a/ClientServerLib/ClientTest/ClientTest.cs b/ClientServerLib/ClientTest/ClientTest.cs index 96444eb4..50d9f4db 100644 --- a/ClientServerLib/ClientTest/ClientTest.cs +++ b/ClientServerLib/ClientTest/ClientTest.cs @@ -32,6 +32,14 @@ namespace ClientTest base.OnClosing(e); } + private void OnKeyDown(object sender, KeyEventArgs e) + { + if (!client.Connected || !loggedIn) + return; + + client.SendMessage(CCommands.CreateCommandSendKeyEvent(e.KeyCode), null); + } + private void btConnect_Click(object sender, EventArgs e) { if (!client.Connected) @@ -114,6 +122,10 @@ namespace ClientTest lbConnectionStatusText.Text = "Connected"; btConnect.Text = "Disconnect"; btLogin.Enabled = true; + btUp.Enabled = true; + btDown.Enabled = true; + btLeft.Enabled = true; + btRight.Enabled = true; }); } else @@ -126,6 +138,10 @@ namespace ClientTest btLogin.Text = "Login"; btLogin.Enabled = false; loggedIn = false; + btUp.Enabled = false; + btDown.Enabled = false; + btLeft.Enabled = false; + btRight.Enabled = false; }); } } @@ -163,5 +179,37 @@ namespace ClientTest tbDataReceiving.Text = text; }); } + + private void btUp_Click(object sender, EventArgs e) + { + if (!client.Connected || !loggedIn) + return; + + client.SendMessage(CCommands.CreateCommandSendKeyEvent(Keys.Up), null); + } + + private void btLeft_Click(object sender, EventArgs e) + { + if (!client.Connected || !loggedIn) + return; + + client.SendMessage(CCommands.CreateCommandSendKeyEvent(Keys.Left), null); + } + + private void btDown_Click(object sender, EventArgs e) + { + if (!client.Connected || !loggedIn) + return; + + client.SendMessage(CCommands.CreateCommandSendKeyEvent(Keys.Down), null); + } + + private void brRight_Click(object sender, EventArgs e) + { + if (!client.Connected || !loggedIn) + return; + + client.SendMessage(CCommands.CreateCommandSendKeyEvent(Keys.Right), null); + } } } diff --git a/Output/ClientServerLib.dll b/Output/ClientServerLib.dll index a0a4cb55f7172472d15026df75567428f9fbe311..23dc72c2f5c079005223d1a8b773bf06f3154177 100644 GIT binary patch literal 39424 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 literal 38912 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 VAAg^a@37HFIsVVz|9%|!e*qd$lBNIv diff --git a/Vocaluxe/Base/CController.cs b/Vocaluxe/Base/CController.cs index bbe42c4d..2ff16b92 100644 --- a/Vocaluxe/Base/CController.cs +++ b/Vocaluxe/Base/CController.cs @@ -20,6 +20,7 @@ using System; using System.Collections.Generic; +using Vocaluxe.Base.Server; using Vocaluxe.Lib.Input; using Vocaluxe.Lib.Input.WiiMote; using VocaluxeLib.Menu; @@ -40,6 +41,7 @@ namespace Vocaluxe.Base _MousePool = new List(); _Controller.Add(new CWiiMote()); + _Controller.Add(CVocaluxeServer.Controller); foreach (IController controller in _Controller) { diff --git a/Vocaluxe/Base/Server/CClientHandler.cs b/Vocaluxe/Base/Server/CClientHandler.cs index 7d0fdfcc..dc787c59 100644 --- a/Vocaluxe/Base/Server/CClientHandler.cs +++ b/Vocaluxe/Base/Server/CClientHandler.cs @@ -2,6 +2,11 @@ using System.Collections.Generic; using System.Linq; using System.Text; +using System.Windows.Forms; + +using Vocaluxe.Base; +using Vocaluxe.Lib.Input; +using VocaluxeLib.Menu; namespace Vocaluxe.Base.Server { @@ -13,6 +18,7 @@ namespace Vocaluxe.Base.Server public CClientHandler(int ConnectionID) { this.ConnectionID = ConnectionID; + LoggedIn = false; } } } diff --git a/Vocaluxe/Base/Server/CVocaluxeServer.cs b/Vocaluxe/Base/Server/CVocaluxeServer.cs index 4441801c..7407e42a 100644 --- a/Vocaluxe/Base/Server/CVocaluxeServer.cs +++ b/Vocaluxe/Base/Server/CVocaluxeServer.cs @@ -2,8 +2,11 @@ using System.Collections.Generic; using System.Linq; using System.Text; +using System.Windows.Forms; using ClientServerLib; +using Vocaluxe.Base; +using Vocaluxe.Lib.Input; using VocaluxeLib.Menu; namespace Vocaluxe.Base.Server @@ -13,10 +16,13 @@ namespace Vocaluxe.Base.Server private static CServer _Server; private static Dictionary _Clients; + public static CControllerFramework Controller = new CControllerFramework(); + public static void Init() { _Clients = new Dictionary(); _Server = new CServer(RequestHandler, CConfig.ServerPort); + Controller.Init(); } public static void Start() @@ -39,6 +45,7 @@ namespace Vocaluxe.Base.Server if (Message.Length < 4) return null; + bool loggedIn = false; lock (_Clients) { if (!_Clients.ContainsKey(ConnectionID)) @@ -46,6 +53,8 @@ namespace Vocaluxe.Base.Server CClientHandler client = new CClientHandler(ConnectionID); _Clients.Add(ConnectionID, client); } + + loggedIn = _Clients[ConnectionID].LoggedIn; } int command = BitConverter.ToInt32(Message, 0); @@ -56,28 +65,39 @@ namespace Vocaluxe.Base.Server case CCommands.CommandLogin: SLoginData data; - if (!CCommands.ResponseCommandLogin(Message, out data)) + if (!CCommands.DecodeCommandLogin(Message, out data)) answer = CCommands.CreateCommandWithoutParams(CCommands.ResponseLoginFailed); else { byte[] serverPW = CCommands.SHA256.ComputeHash(Encoding.UTF8.GetBytes(CConfig.ServerPassword)); - bool ok = true; - for (int i = 0; i < 32; i++) - { - if (serverPW[i] != data.SHA256[i]) - { - ok = false; - break; - } - } - - if (!ok) + if (!serverPW.SequenceEqual(data.SHA256)) answer = CCommands.CreateCommandWithoutParams(CCommands.ResponseLoginWrongPassword); else + { answer = CCommands.CreateCommandWithoutParams(CCommands.ResponseLoginOK); + _Clients[ConnectionID].LoggedIn = true; + } } break; + } + + if (!loggedIn) + return answer; + + switch (command) + { + case CCommands.CommandSendKeyEvent: + Keys key; + if (!CCommands.DecodeCommandSendKeyEvent(Message, out key)) + answer = CCommands.CreateCommandWithoutParams(CCommands.ResponseNOK); + else + { + answer = CCommands.CreateCommandWithoutParams(CCommands.ResponseOK); + } + + Controller.AddKeyEvent(new SKeyEvent(ESender.Keyboard, false, false, false, false, Char.MinValue, key)); + break; default: break; diff --git a/Vocaluxe/Lib/Input/CControllerFramework.cs b/Vocaluxe/Lib/Input/CControllerFramework.cs new file mode 100644 index 00000000..f4136efe --- /dev/null +++ b/Vocaluxe/Lib/Input/CControllerFramework.cs @@ -0,0 +1,104 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +using VocaluxeLib.Menu; + +namespace Vocaluxe.Lib.Input +{ + public class CControllerFramework : IController + { + private List _KeysPool; + private List _CurrentKeysPool; + private readonly Object _KeyCopyLock = new Object(); + + private List _MousePool; + private List _CurrentMousePool; + private readonly Object _MouseCopyLock = new Object(); + + public virtual void Init() + { + _KeysPool = new List(); + _CurrentKeysPool = new List(); + _MousePool = new List(); + _CurrentMousePool = new List(); + } + + public virtual void Close() + { + } + + public virtual void Connect() + { + } + + public virtual void Disconnect() + { + } + + public virtual bool IsConnected() + { + return true; + } + + public virtual void Update() + { + lock (_KeyCopyLock) + { + foreach (SKeyEvent e in _KeysPool) + _CurrentKeysPool.Add(e); + _KeysPool.Clear(); + } + + lock (_MouseCopyLock) + { + foreach (SMouseEvent e in _MousePool) + _CurrentMousePool.Add(e); + _MousePool.Clear(); + } + } + + public virtual bool PollKeyEvent(ref SKeyEvent keyEvent) + { + if (_CurrentKeysPool.Count > 0) + { + keyEvent = _CurrentKeysPool[0]; + _CurrentKeysPool.RemoveAt(0); + return true; + } + return false; + } + + public virtual bool PollMouseEvent(ref SMouseEvent mouseEvent) + { + if (_CurrentMousePool.Count > 0) + { + mouseEvent = _CurrentMousePool[0]; + _CurrentMousePool.RemoveAt(0); + return true; + } + return false; + } + + public virtual void SetRumble(float duration) + { + } + + public void AddKeyEvent(SKeyEvent keyEvent) + { + lock (_KeyCopyLock) + { + _KeysPool.Add(keyEvent); + } + } + + public void AddMouseEvent(SMouseEvent mouseEvent) + { + lock (_MousePool) + { + _MousePool.Add(mouseEvent); + } + } + } +} diff --git a/Vocaluxe/Vocaluxe.csproj b/Vocaluxe/Vocaluxe.csproj index 7105a451..2ed45688 100644 --- a/Vocaluxe/Vocaluxe.csproj +++ b/Vocaluxe/Vocaluxe.csproj @@ -207,6 +207,7 @@ +