From 7ad838265ccf8301b99657c062b2c4d2818b2e24 Mon Sep 17 00:00:00 2001 From: Ylian Saint-Hilaire Date: Mon, 10 Aug 2020 23:51:17 -0700 Subject: [PATCH] Started work on remote desktop client. --- KVMControl.Designer.cs | 56 +++ KVMControl.cs | 636 ++++++++++++++++++++++++ KVMControl.resx | 120 +++++ KVMResizeControl.Designer.cs | 128 +++++ KVMResizeControl.cs | 138 ++++++ KVMResizeControl.resx | 120 +++++ KVMSettingsForm.Designer.cs | 210 ++++++++ KVMSettingsForm.cs | 131 +++++ KVMSettingsForm.resx | 546 +++++++++++++++++++++ KVMViewer.Designer.cs | 404 +++++++++++++++ KVMViewer.cs | 813 +++++++++++++++++++++++++++++++ KVMViewer.resx | 555 +++++++++++++++++++++ MainForm.Designer.cs | 257 +++++----- MainForm.cs | 16 + MainForm.resx | 164 ++++--- MeshCentralRouter.csproj | 37 ++ MeshCentralServer.cs | 15 +- NodeClass.cs | 1 + Properties/Resources.Designer.cs | 10 + Properties/Resources.resx | 141 +++--- Resources/50-Computer.png | Bin 0 -> 6722 bytes Resources/Padlock.png | Bin 0 -> 5987 bytes Resources/User.png | Bin 0 -> 4761 bytes 23 files changed, 4227 insertions(+), 271 deletions(-) create mode 100644 KVMControl.Designer.cs create mode 100644 KVMControl.cs create mode 100644 KVMControl.resx create mode 100644 KVMResizeControl.Designer.cs create mode 100644 KVMResizeControl.cs create mode 100644 KVMResizeControl.resx create mode 100644 KVMSettingsForm.Designer.cs create mode 100644 KVMSettingsForm.cs create mode 100644 KVMSettingsForm.resx create mode 100644 KVMViewer.Designer.cs create mode 100644 KVMViewer.cs create mode 100644 KVMViewer.resx create mode 100644 Resources/50-Computer.png create mode 100644 Resources/Padlock.png create mode 100644 Resources/User.png diff --git a/KVMControl.Designer.cs b/KVMControl.Designer.cs new file mode 100644 index 0000000..07e2537 --- /dev/null +++ b/KVMControl.Designer.cs @@ -0,0 +1,56 @@ +namespace MeshCentralRouter +{ + partial class KVMControl + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Component Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.SuspendLayout(); + // + // KVMControl + // + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.None; + this.BackColor = System.Drawing.SystemColors.Control; + this.Margin = new System.Windows.Forms.Padding(4); + this.Name = "KVMControl"; + this.Size = new System.Drawing.Size(585, 364); + this.Paint += new System.Windows.Forms.PaintEventHandler(this.KVMControl_Paint); + this.KeyDown += new System.Windows.Forms.KeyEventHandler(this.KVMControl_KeyDown); + this.KeyPress += new System.Windows.Forms.KeyPressEventHandler(this.KVMControl_KeyPress); + this.KeyUp += new System.Windows.Forms.KeyEventHandler(this.KVMControl_KeyUp); + this.MouseDown += new System.Windows.Forms.MouseEventHandler(this.KVMControl_MouseDown); + this.MouseMove += new System.Windows.Forms.MouseEventHandler(this.KVMControl_MouseMove); + this.MouseUp += new System.Windows.Forms.MouseEventHandler(this.KVMControl_MouseUp); + this.ResumeLayout(false); + + } + + #endregion + + //private System.Windows.Forms.Integration.ElementHost mainElementHost; + + } +} diff --git a/KVMControl.cs b/KVMControl.cs new file mode 100644 index 0000000..bd7c04c --- /dev/null +++ b/KVMControl.cs @@ -0,0 +1,636 @@ +/* +Copyright 2009-2020 Intel Corporation + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +using System; +using System.IO; +using System.Net; +using System.Text; +using System.Drawing; +using System.Windows.Forms; +using System.ComponentModel; +using System.Collections.Generic; + +namespace MeshCentralRouter +{ + public partial class KVMControl : UserControl + { + private bool remotepause = true; + private Bitmap desktop = null; + private Graphics desktopGraphics = null; + private uint screenWidth = 0; + private uint screenHeight = 0; + private Pen RedPen = new Pen(System.Drawing.Color.Red); + private Font DebugFont = new Font(FontFamily.GenericSansSerif, 14); + private int compressionlevel = 60; // 60% compression + private int scalinglevel = 1024; // 100% scale + private int frameRate = 100; // Medium frame rate + private double scalefactor = 1; + public List displays = new List(); + public ushort currentDisp = 0; + public bool MouseButtonLeft = false; + public bool MouseButtonMiddle = false; + public bool MouseButtonRight = false; + public double DpiX = 96; + public double DpiY = 96; + public KVMViewer parent = null; + + private enum KvmCommands + { + Nop = 0, + Key = 1, + Mouse = 2, + Picture = 3, + Copy = 4, + Compression = 5, + Refresh = 6, + Screen = 7, + Pause = 8, + TerminalText = 9, + CtrlAltDel = 10, + GetDisplays = 11, + SetDisplay = 12, + FrameRateTimer = 13, + InitTouch = 14, + Touch = 15, + Message = 16, + Disconnect = 59, + Alert = 65, + MouseCursor = 88 + } + + private enum KvmMouseButtonCommands + { + MOUSEEVENTF_LEFTDOWN = 0x0002, + MOUSEEVENTF_RIGHTDOWN = 0x0008, + MOUSEEVENTF_MIDDLEDOWN = 0x0020, + MOUSEEVENTF_LEFTUP = 0x0004, + MOUSEEVENTF_RIGHTUP = 0x0010, + MOUSEEVENTF_MIDDLEUP = 0x0040 + } + + [Category("Action")] + [Description("Fires when the remote desktop size changes.")] + public event EventHandler DesktopSizeChanged; + + [Category("Action")] + [Description("Fires when it receives the display list.")] + public event EventHandler DisplaysReceived; + + public enum ConnectState + { + Disconnected, + Connecting, + Connected + } + + public int CompressionLevel { get { return compressionlevel; } set { compressionlevel = value; SendCompressionLevel(); } } + public int ScalingLevel { get { return scalinglevel; } set { scalinglevel = value; SendCompressionLevel(); } } + public int FrameRate { get { return frameRate; } set { frameRate = value; SendCompressionLevel(); } } + public double ScaleFactor { get { return scalefactor; } set { scalefactor = value; } } + + public void SetCompressionParams(int level, int scaling, int framerate) { compressionlevel = level; scalinglevel = scaling; frameRate = framerate; SendCompressionLevel(); } + public int DesktopWidth { get { return (int)screenWidth; } } + public int DesktopHeight { get { return (int)screenHeight; } } + + // Debug + public int bytesent = 0; + public int byterecv = 0; + public int tilecount = 0; + public int tilecopy = 0; + public bool debugmode = false; + + public KVMControl() + { + InitializeComponent(); + this.SetStyle(ControlStyles.Selectable, false); + this.SetStyle(ControlStyles.UserMouse, true); + this.SetStyle(ControlStyles.UserPaint, true); + this.SetStyle(ControlStyles.AllPaintingInWmPaint, true); + this.MouseWheel += new System.Windows.Forms.MouseEventHandler(KVMControl_MouseWheel); + } + + public int ProcessData(byte[] buffer, int off, int len) + { + if (len == 0) return 0; + if (len >= 4) + { + KvmCommands btype = (KvmCommands)IPAddress.NetworkToHostOrder((short)BitConverter.ToUInt16(buffer, off)); + ushort blen = (ushort)IPAddress.NetworkToHostOrder((short)BitConverter.ToUInt16(buffer, off + 2)); + if (len < blen) return 0; + + switch (btype) + { + case KvmCommands.Screen: + { + if (blen != 8) return blen; + uint w = (ushort)IPAddress.NetworkToHostOrder((short)BitConverter.ToUInt16(buffer, off + 4)); + uint h = (ushort)IPAddress.NetworkToHostOrder((short)BitConverter.ToUInt16(buffer, off + 6)); + if (screenWidth == w && screenHeight == h) break; + screenWidth = w; + screenHeight = h; + desktop = new Bitmap((int)screenWidth, (int)screenHeight, System.Drawing.Imaging.PixelFormat.Format24bppRgb); + desktopGraphics = Graphics.FromImage(desktop); + SendCompressionLevel(); + Invoke(new SetSizeHandler(SetSize)); + SendPause(false); + SendMouse(2, KvmMouseButtonCommands.MOUSEEVENTF_LEFTUP); + SendMouse(2, KvmMouseButtonCommands.MOUSEEVENTF_MIDDLEUP); + SendMouse(2, KvmMouseButtonCommands.MOUSEEVENTF_RIGHTUP); + return blen; + } + case KvmCommands.Picture: + { + Image newtile; + ushort tile_x = (ushort)IPAddress.NetworkToHostOrder((short)BitConverter.ToUInt16(buffer, off + 4)); + ushort tile_y = (ushort)IPAddress.NetworkToHostOrder((short)BitConverter.ToUInt16(buffer, off + 6)); + try { newtile = Image.FromStream(new System.IO.MemoryStream(buffer, off + 8, blen - 8)); } catch (Exception) { return blen; } + Rectangle r = new Rectangle((int)tile_x, (int)tile_y, newtile.Width, newtile.Height); + Rectangle r3 = new Rectangle((int)((double)tile_x / (double)scalefactor) - 2, (int)((double)tile_y / (double)scalefactor) - 2, (int)((double)newtile.Width / (double)scalefactor) + 4, (int)((double)newtile.Height / (double)scalefactor) + 4); + tilecount++; + + // Winform mode + if (desktop != null) + { + lock (desktop) + { + desktopGraphics.DrawImage(newtile, new Rectangle((int)tile_x, (int)tile_y, newtile.Width, newtile.Height), new Rectangle(0, 0, newtile.Width, newtile.Height), GraphicsUnit.Pixel); + if (debugmode) + { + desktopGraphics.DrawRectangle(RedPen, new Rectangle((int)tile_x, (int)tile_y, newtile.Width - 1, newtile.Height - 1)); + desktopGraphics.DrawString(string.Format("{0} / {1}kb", tilecount, blen / 2014), DebugFont, RedPen.Brush, new Point((int)tile_x, (int)tile_y)); + } + } + if (scalefactor == 1) Invalidate(r, false); else Invalidate(r3, false); + } + + return blen; + } + case KvmCommands.Copy: + { + ushort sourcex = (ushort)IPAddress.NetworkToHostOrder((short)BitConverter.ToUInt16(buffer, off + 4)); + ushort sourcey = (ushort)IPAddress.NetworkToHostOrder((short)BitConverter.ToUInt16(buffer, off + 6)); + ushort targetx = (ushort)IPAddress.NetworkToHostOrder((short)BitConverter.ToUInt16(buffer, off + 8)); + ushort targety = (ushort)IPAddress.NetworkToHostOrder((short)BitConverter.ToUInt16(buffer, off + 10)); + ushort tilew = (ushort)IPAddress.NetworkToHostOrder((short)BitConverter.ToUInt16(buffer, off + 12)); + ushort tileh = (ushort)IPAddress.NetworkToHostOrder((short)BitConverter.ToUInt16(buffer, off + 14)); + + Rectangle r1 = new Rectangle((int)sourcex, (int)sourcey, (int)tilew, (int)tileh); + Rectangle r2 = new Rectangle((int)targetx, (int)targety, (int)tilew, (int)tileh); + Rectangle r3 = new Rectangle((int)((double)targetx / (double)scalefactor) - 2, (int)((double)targety / (double)scalefactor) - 2, (int)((double)tilew / (double)scalefactor) + 4, (int)((double)tileh / (double)scalefactor) + 4); + + // WinForm mode + if (desktop != null) + { + lock (desktop) + { + Bitmap bmp = new Bitmap(tilew, tileh); + Graphics g = Graphics.FromImage(bmp); + g.DrawImage(desktop, 0, 0, r1, GraphicsUnit.Pixel); + g.Dispose(); + desktopGraphics.Flush(); + desktopGraphics.DrawImage(bmp, r2, 0, 0, tilew, tileh, GraphicsUnit.Pixel); + if (debugmode) { desktopGraphics.DrawString("COPY", DebugFont, RedPen.Brush, new Point((int)targetx, (int)targety + 20)); } + } + tilecopy++; + } + + return blen; + } + case KvmCommands.GetDisplays: + { + int i = 0; + ushort length = (ushort)IPAddress.NetworkToHostOrder((short)BitConverter.ToUInt16(buffer, off + 4)); + displays.Clear(); + if (length > 0) + { + for (i = 0; i < length; i++) + { + ushort num = (ushort)IPAddress.NetworkToHostOrder((short)BitConverter.ToUInt16(buffer, off + 6 + i * 2)); + if (num == 0xFFFF) + { + displays.Add("All Displays"); + } + else + { + displays.Add("Display " + num); + } + } + } + currentDisp = (ushort)IPAddress.NetworkToHostOrder((short)BitConverter.ToUInt16(buffer, off + 6 + i * 2)); + if (DisplaysReceived != null) DisplaysReceived(this, null); + break; + } + case KvmCommands.SetDisplay: + { + currentDisp = (ushort)IPAddress.NetworkToHostOrder((short)BitConverter.ToUInt16(buffer, off + 4)); + break; + } + case KvmCommands.MouseCursor: + { + if (blen != 5) return blen; + ChangeMouseCursor(buffer[off + 4]); + break; + } + default: + { + // MessageBox.Show("Should never happen!"); + //Disconnect(); + return 0; + } + } + return blen; + } + return 0; + } + + private delegate void ChangeMouseCursorHandler(int cursorId); + private void ChangeMouseCursor(int cursorId) + { + if (this.InvokeRequired) { this.Invoke(new ChangeMouseCursorHandler(ChangeMouseCursor), cursorId); return; } + if (cursorId == 0) { this.Cursor = System.Windows.Forms.Cursors.Default; return; } // default + if (cursorId == 1) { this.Cursor = System.Windows.Forms.Cursors.AppStarting; return; } // progress + if (cursorId == 2) { this.Cursor = System.Windows.Forms.Cursors.Cross; return; } // crosshair + if (cursorId == 3) { this.Cursor = System.Windows.Forms.Cursors.Default; return; } // pointer + if (cursorId == 4) { this.Cursor = System.Windows.Forms.Cursors.Help; return; } // help + if (cursorId == 5) { this.Cursor = System.Windows.Forms.Cursors.IBeam; return; } // text + if (cursorId == 6) { this.Cursor = System.Windows.Forms.Cursors.No; return; } // no-drop + if (cursorId == 7) { this.Cursor = System.Windows.Forms.Cursors.Arrow; return; } // move + if (cursorId == 8) { this.Cursor = System.Windows.Forms.Cursors.SizeNESW; return; } // nesw-resize + if (cursorId == 9) { this.Cursor = System.Windows.Forms.Cursors.SizeNS; return; } // ns-resize + if (cursorId == 10) { this.Cursor = System.Windows.Forms.Cursors.SizeNWSE; return; } // nwse-resize + if (cursorId == 11) { this.Cursor = System.Windows.Forms.Cursors.SizeWE; return; } // w-resize + if (cursorId == 12) { this.Cursor = System.Windows.Forms.Cursors.Arrow; return; } // alias + if (cursorId == 13) { this.Cursor = System.Windows.Forms.Cursors.WaitCursor; return; } // wait + if (cursorId == 14) { this.Cursor = System.Windows.Forms.Cursors.Default; return; } // none + if (cursorId == 15) { this.Cursor = System.Windows.Forms.Cursors.No; return; } // not-allowed + if (cursorId == 16) { this.Cursor = System.Windows.Forms.Cursors.VSplit; return; } // col-resize + if (cursorId == 17) { this.Cursor = System.Windows.Forms.Cursors.HSplit; return; } // row-resize + if (cursorId == 18) { this.Cursor = System.Windows.Forms.Cursors.Default; return; } // copy + if (cursorId == 19) { this.Cursor = System.Windows.Forms.Cursors.SizeAll; return; } // zoom-in + if (cursorId == 20) { this.Cursor = System.Windows.Forms.Cursors.SizeAll; return; } // zoom-out + } + + public void Repaint(Region region) + { + Invalidate(region); + } + + protected override void OnPaintBackground(PaintEventArgs e) + { + // Do not paint background. + } + + private delegate void SetSizeHandler(); + private void SetSize() + { + if (DesktopSizeChanged != null) { DesktopSizeChanged(this, null); } + } + + private void KVMControl_Paint(object sender, PaintEventArgs e) + { + Graphics g = e.Graphics; + if (desktop == null) + { + g.FillRectangle(new SolidBrush(Color.Black), e.ClipRectangle); + } + else + { + lock (desktop) + { + if (scalefactor == 1) + { + g.DrawImage(desktop, e.ClipRectangle, e.ClipRectangle, GraphicsUnit.Pixel); + } + else + { + //g.DrawImage((Image)desktop, new Rectangle(0, 0, Width, Height), 0, 0, screenWidth, screenHeight, GraphicsUnit.Pixel); + g.DrawImage((Image)desktop, e.ClipRectangle, (int)((double)e.ClipRectangle.Left * (double)scalefactor), (int)((double)e.ClipRectangle.Top * (double)scalefactor), (int)((double)e.ClipRectangle.Width * (double)scalefactor), (int)((double)e.ClipRectangle.Height * (double)scalefactor), GraphicsUnit.Pixel); + } + } + } + } + + public void SendKey(byte key, byte action) + { + //if (state != ConnectState.Connected) return; + + BinaryWriter bw = GetBinaryWriter(); + bw.Write(IPAddress.HostToNetworkOrder((short)KvmCommands.Key)); + bw.Write(IPAddress.HostToNetworkOrder((short)6)); + bw.Write((byte)action); + bw.Write((byte)key); + Send(bw); + } + + private void SendKey(KeyEventArgs e, byte action) + { + //if (state != ConnectState.Connected) return; + + BinaryWriter bw = GetBinaryWriter(); + bw.Write(IPAddress.HostToNetworkOrder((short)KvmCommands.Key)); + bw.Write(IPAddress.HostToNetworkOrder((short)6)); + bw.Write((byte)action); + bw.Write((byte)e.KeyCode); + Send(bw); + } + + private short LastX = 0; + private short LastY = 0; + private void SendMouse(MouseEventArgs e, byte action) + { + //if (state != ConnectState.Connected) return; + byte buttons = 0; + if (action == 1) + { + switch (e.Button) + { + case MouseButtons.Left: + buttons |= (byte)KvmMouseButtonCommands.MOUSEEVENTF_LEFTDOWN; + break; + case MouseButtons.Right: + buttons |= (byte)KvmMouseButtonCommands.MOUSEEVENTF_RIGHTDOWN; + break; + case MouseButtons.Middle: + buttons |= (byte)KvmMouseButtonCommands.MOUSEEVENTF_MIDDLEDOWN; + break; + } + } + else if (action == 2) + { + switch (e.Button) + { + case MouseButtons.Left: + buttons |= (byte)KvmMouseButtonCommands.MOUSEEVENTF_LEFTUP; + break; + case MouseButtons.Right: + buttons |= (byte)KvmMouseButtonCommands.MOUSEEVENTF_RIGHTUP; + break; + case MouseButtons.Middle: + buttons |= (byte)KvmMouseButtonCommands.MOUSEEVENTF_MIDDLEUP; + break; + } + } + + LastX = (short)((double)e.X * (double)scalefactor); + LastY = (short)((double)e.Y * (double)scalefactor); + + BinaryWriter bw = GetBinaryWriter(); + bw.Write(IPAddress.HostToNetworkOrder((short)KvmCommands.Mouse)); + bw.Write(IPAddress.HostToNetworkOrder((short)(e.Delta == 0 ? 10 : 12))); + bw.Write((byte)action); + bw.Write((byte)buttons); + bw.Write(IPAddress.HostToNetworkOrder(LastX)); + bw.Write(IPAddress.HostToNetworkOrder(LastY)); + if (e.Delta != 0) { bw.Write(IPAddress.HostToNetworkOrder((short)e.Delta)); } + Send(bw); + } + + public void SendMouseWheel(MouseEventArgs e, byte action) + { + //if (state != ConnectState.Connected) return; + + BinaryWriter bw = GetBinaryWriter(); + bw.Write(IPAddress.HostToNetworkOrder((short)KvmCommands.Mouse)); + bw.Write(IPAddress.HostToNetworkOrder((short)(e.Delta == 0 ? 10 : 12))); + bw.Write((short)0); + bw.Write(IPAddress.HostToNetworkOrder(LastX)); + bw.Write(IPAddress.HostToNetworkOrder(LastY)); + if (e.Delta != 0) { bw.Write(IPAddress.HostToNetworkOrder((short)e.Delta)); } + Send(bw); + } + + private void SendMouse(byte action, KvmMouseButtonCommands buttons) + { + //if (state != ConnectState.Connected) return; + + BinaryWriter bw = GetBinaryWriter(); + bw.Write(IPAddress.HostToNetworkOrder((short)KvmCommands.Mouse)); + bw.Write(IPAddress.HostToNetworkOrder((short)10)); + bw.Write((byte)action); + bw.Write((byte)buttons); + bw.Write((short)0); + bw.Write((short)0); + Send(bw); + } + + public void SendCompressionLevel() + { + //if (state != ConnectState.Connected) return; + + BinaryWriter bw = GetBinaryWriter(); + bw.Write(IPAddress.HostToNetworkOrder((short)KvmCommands.Compression)); + bw.Write(IPAddress.HostToNetworkOrder((short)10)); + bw.Write((byte)1); // Image Type (Reserved, 1 = JPEG) + bw.Write((byte)compressionlevel); // Compression Level + bw.Write(IPAddress.HostToNetworkOrder((short)scalinglevel)); // Scaling + bw.Write(IPAddress.HostToNetworkOrder((short)frameRate)); // Frame Rate + Send(bw); + } + + public void SendRefresh() + { + //if (state != ConnectState.Connected) return; + + BinaryWriter bw = GetBinaryWriter(); + bw.Write(IPAddress.HostToNetworkOrder((short)KvmCommands.Refresh)); + bw.Write(IPAddress.HostToNetworkOrder((short)4)); + Send(bw); + } + + public void SendPause(bool paused) + { + //if (remotepauseactive == false || paused == remotepause) return; + //if (paused == remotepause) return; + remotepause = paused; + + BinaryWriter bw = GetBinaryWriter(); + bw.Write(IPAddress.HostToNetworkOrder((short)KvmCommands.Pause)); + bw.Write(IPAddress.HostToNetworkOrder((short)5)); + bw.Write(paused ? (byte)1 : (byte)0); + Send(bw); + } + + public void Send(byte[] buffer) + { + //if (state == ConnectState.Disconnected) return; + try + { + parent.wc.WriteBinaryWebSocket(buffer, 0, buffer.Length); + bytesent += buffer.Length; + } + catch (Exception) { } + } + + public void Send(string str) + { + //if (state == ConnectState.Disconnected) return; + try + { + parent.wc.WriteStringWebSocket(str); + bytesent += str.Length; + } + catch (Exception) { } + } + + public void Send(BinaryWriter bw) + { + //if (state == ConnectState.Disconnected) { RecycleBinaryWriter(bw); return; } + try + { + parent.wc.WriteBinaryWebSocket(((MemoryStream)bw.BaseStream).GetBuffer(), 0, (int)((MemoryStream)bw.BaseStream).Length); + bytesent += (int)((MemoryStream)bw.BaseStream).Length; + } + catch (Exception) { } + RecycleBinaryWriter(bw); + } + + private void KVMControl_MouseDown(object sender, MouseEventArgs e) + { + SendMouse(e, 1); + } + + private void KVMControl_MouseUp(object sender, MouseEventArgs e) + { + SendMouse(e, 2); + } + + long lastMouseMove = 0; + + private void KVMControl_MouseMove(object sender, MouseEventArgs e) + { + long ct = DateTime.Now.Ticks; + if ((lastMouseMove + 30) < ct) { + lastMouseMove = ct; + SendMouse(e, 0); + } + } + + public void KVMControl_MouseWheel(object sender, MouseEventArgs e) + { + SendMouse(e, 0); + } + + private void KVMControl_KeyDown(object sender, KeyEventArgs e) + { + SendKey(e, 0); + e.Handled = true; + } + + private void KVMControl_KeyUp(object sender, KeyEventArgs e) + { + SendKey(e, 1); + e.Handled = true; + } + + private void KVMControl_KeyPress(object sender, KeyPressEventArgs e) + { + e.Handled = true; + } + + private void KVMControl_MouseEnter(object sender, EventArgs e) + { + this.Focus(); + } + + public void SendCtrlAltDel() + { + //if (state != ConnectState.Connected) return; + BinaryWriter bw = GetBinaryWriter(); + bw.Write(IPAddress.HostToNetworkOrder((short)KvmCommands.CtrlAltDel)); + bw.Write(IPAddress.HostToNetworkOrder((short)4)); + Send(bw); + } + + protected override bool ProcessCmdKey(ref Message msg, Keys keyData) + { + const int WM_KEYDOWN = 0x100; + const int WM_SYSKEYDOWN = 0x104; + + // Tab keys + if (msg.Msg == WM_KEYDOWN && msg.WParam.ToInt32() == 9) + { + SendKey((byte)msg.WParam.ToInt32(), 0); + return true; + } + + // Handle arrow keys + if (((msg.Msg == WM_KEYDOWN) || (msg.Msg == WM_SYSKEYDOWN)) && msg.WParam.ToInt32() >= 37 && msg.WParam.ToInt32() <= 40) + { + SendKey((byte)msg.WParam.ToInt32(), 0); + return true; + } + + return false; + } + + public void GetDisplayNumbers() + { + //if (state != ConnectState.Connected) return; + + BinaryWriter bw = GetBinaryWriter(); + bw.Write(IPAddress.HostToNetworkOrder((short)KvmCommands.GetDisplays)); + bw.Write(IPAddress.HostToNetworkOrder((short)4)); + Send(bw); + } + + public void SendDisplay(int dispNo) + { + //if (state != ConnectState.Connected) return; + + BinaryWriter bw = GetBinaryWriter(); + bw.Write(IPAddress.HostToNetworkOrder((short)KvmCommands.SetDisplay)); + bw.Write(IPAddress.HostToNetworkOrder((short)6)); + bw.Write(IPAddress.HostToNetworkOrder((short)dispNo)); + Send(bw); + } + + public void SendWindowsKey() + { + SendKey(0x5B, 4); // Windows key down + SendKey(0x5B, 3); // Windows key up + } + + public void SendCharmsKey() + { + SendKey(0x5B, 4); // Windows key down + SendKey(67, 1); // C down + SendKey(67, 2); // C up + SendKey(0x5B, 3); // Windows key up + } + + // BinaryWriter Object Recycling System + private static Stack BinaryWriteRecycleList = new Stack(); + public static BinaryWriter GetBinaryWriter() { lock (BinaryWriteRecycleList) { return (BinaryWriteRecycleList.Count == 0) ? new BinaryWriter(new MemoryStream(32000), Encoding.UTF8) : BinaryWriteRecycleList.Pop(); } } + public static void RecycleBinaryWriter(BinaryWriter obj) { lock (BinaryWriteRecycleList) { ((MemoryStream)obj.BaseStream).SetLength(0); BinaryWriteRecycleList.Push(obj); } } + + // MemoryStream Object Recycling System + private static Stack MemoryStreamRecycleList = new Stack(); + public static MemoryStream GetMemoryStream() { lock (MemoryStreamRecycleList) { return (MemoryStreamRecycleList.Count == 0) ? new MemoryStream(32000) : MemoryStreamRecycleList.Pop(); } } + public static void RecycleMemoryStream(MemoryStream obj) { lock (MemoryStreamRecycleList) { obj.SetLength(0); MemoryStreamRecycleList.Push(obj); } } + + // StringBuilder Object Recycling System + private static Stack StringBuilderRecycleList = new Stack(); + public static StringBuilder GetStringBuilder() { lock (StringBuilderRecycleList) { return (StringBuilderRecycleList.Count == 0) ? new StringBuilder(16000) : StringBuilderRecycleList.Pop(); } } + public static void RecycleStringBuilder(StringBuilder obj) { lock (StringBuilderRecycleList) { obj.Length = 0; StringBuilderRecycleList.Push(obj); } } + + // byte[] Object Recycling System + private static Stack ByteArrayRecycleList = new Stack(); + private static int ByteArrayRecycleListCount = 0; + public static byte[] GetByteArray() { lock (ByteArrayRecycleList) { if (ByteArrayRecycleList.Count == 0) ByteArrayRecycleListCount++; return (ByteArrayRecycleList.Count == 0) ? new byte[65535] : ByteArrayRecycleList.Pop(); } } + public static void RecycleByteArray(byte[] obj) { lock (ByteArrayRecycleList) { if (obj != null) ByteArrayRecycleList.Push(obj); } } + + } +} diff --git a/KVMControl.resx b/KVMControl.resx new file mode 100644 index 0000000..1af7de1 --- /dev/null +++ b/KVMControl.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/KVMResizeControl.Designer.cs b/KVMResizeControl.Designer.cs new file mode 100644 index 0000000..b60fc9b --- /dev/null +++ b/KVMResizeControl.Designer.cs @@ -0,0 +1,128 @@ +namespace MeshCentralRouter +{ + partial class KVMResizeControl + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Component Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.rightScrollPanel = new System.Windows.Forms.Panel(); + this.vScrollBar = new System.Windows.Forms.VScrollBar(); + this.cornerBlockPanel = new System.Windows.Forms.Panel(); + this.hScrollBar = new System.Windows.Forms.HScrollBar(); + this.clientPanel = new System.Windows.Forms.Panel(); + this.kvmControl = new MeshCentralRouter.KVMControl(); + this.rightScrollPanel.SuspendLayout(); + this.clientPanel.SuspendLayout(); + this.SuspendLayout(); + // + // rightScrollPanel + // + this.rightScrollPanel.Controls.Add(this.vScrollBar); + this.rightScrollPanel.Controls.Add(this.cornerBlockPanel); + this.rightScrollPanel.Dock = System.Windows.Forms.DockStyle.Right; + this.rightScrollPanel.Location = new System.Drawing.Point(1159, 0); + this.rightScrollPanel.Name = "rightScrollPanel"; + this.rightScrollPanel.Size = new System.Drawing.Size(17, 789); + this.rightScrollPanel.TabIndex = 12; + // + // vScrollBar + // + this.vScrollBar.Dock = System.Windows.Forms.DockStyle.Fill; + this.vScrollBar.LargeChange = 100; + this.vScrollBar.Location = new System.Drawing.Point(0, 0); + this.vScrollBar.Name = "vScrollBar"; + this.vScrollBar.Size = new System.Drawing.Size(17, 772); + this.vScrollBar.TabIndex = 10; + this.vScrollBar.ValueChanged += new System.EventHandler(this.vScrollBar_ValueChanged); + // + // cornerBlockPanel + // + this.cornerBlockPanel.BackColor = System.Drawing.SystemColors.Control; + this.cornerBlockPanel.Dock = System.Windows.Forms.DockStyle.Bottom; + this.cornerBlockPanel.Location = new System.Drawing.Point(0, 772); + this.cornerBlockPanel.Name = "cornerBlockPanel"; + this.cornerBlockPanel.Size = new System.Drawing.Size(17, 17); + this.cornerBlockPanel.TabIndex = 11; + this.cornerBlockPanel.Visible = false; + // + // hScrollBar + // + this.hScrollBar.Dock = System.Windows.Forms.DockStyle.Bottom; + this.hScrollBar.LargeChange = 100; + this.hScrollBar.Location = new System.Drawing.Point(0, 772); + this.hScrollBar.Name = "hScrollBar"; + this.hScrollBar.Size = new System.Drawing.Size(1159, 17); + this.hScrollBar.TabIndex = 13; + this.hScrollBar.ValueChanged += new System.EventHandler(this.hScrollBar_ValueChanged); + // + // clientPanel + // + this.clientPanel.Controls.Add(this.kvmControl); + this.clientPanel.Dock = System.Windows.Forms.DockStyle.Fill; + this.clientPanel.Location = new System.Drawing.Point(0, 0); + this.clientPanel.Name = "clientPanel"; + this.clientPanel.Size = new System.Drawing.Size(1159, 772); + this.clientPanel.TabIndex = 15; + // + // kvmControl + // + this.kvmControl.BackColor = System.Drawing.Color.LightCoral; + this.kvmControl.CompressionLevel = 60; + this.kvmControl.Location = new System.Drawing.Point(33, 31); + this.kvmControl.Margin = new System.Windows.Forms.Padding(4); + this.kvmControl.Name = "kvmControl"; + this.kvmControl.ScaleFactor = 1D; + this.kvmControl.ScalingLevel = 1024; + this.kvmControl.Size = new System.Drawing.Size(450, 314); + this.kvmControl.TabIndex = 14; + this.kvmControl.DesktopSizeChanged += new System.EventHandler(this.kvmControl_DesktopSizeChanged); + this.kvmControl.Resize += new System.EventHandler(this.kvmControl_Resize); + // + // KVMResizeControl + // + this.BackColor = System.Drawing.Color.Gray; + this.Controls.Add(this.clientPanel); + this.Controls.Add(this.hScrollBar); + this.Controls.Add(this.rightScrollPanel); + this.Name = "KVMResizeControl"; + this.Size = new System.Drawing.Size(1176, 789); + this.Resize += new System.EventHandler(this.ResizeKVMControl_Resize); + this.rightScrollPanel.ResumeLayout(false); + this.clientPanel.ResumeLayout(false); + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.Panel rightScrollPanel; + private System.Windows.Forms.VScrollBar vScrollBar; + private System.Windows.Forms.Panel cornerBlockPanel; + private System.Windows.Forms.HScrollBar hScrollBar; + private KVMControl kvmControl; + private System.Windows.Forms.Panel clientPanel; + } +} diff --git a/KVMResizeControl.cs b/KVMResizeControl.cs new file mode 100644 index 0000000..ef24877 --- /dev/null +++ b/KVMResizeControl.cs @@ -0,0 +1,138 @@ +/* +Copyright 2009-2020 Intel Corporation + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +using System; +using System.Windows.Forms; +using System.ComponentModel; + +namespace MeshCentralRouter +{ + public partial class KVMResizeControl : UserControl + { + private bool zoomtofit = false; + + public KVMControl KVM { get { return kvmControl; } } + public bool ZoomToFit { get { return zoomtofit; } set { zoomtofit = value; CenterKvmControl(false); } } + + [Category("Action")] + [Description("Fires when the connection state changes.")] + public event EventHandler StateChanged; + + [Category("Action")] + [Description("Fires when the display list is received.")] + public event EventHandler DisplaysReceived; + + public KVMResizeControl() + { + InitializeComponent(); + vScrollBar.LargeChange = 100; + vScrollBar.SmallChange = 10; + hScrollBar.LargeChange = 100; + hScrollBar.SmallChange = 10; + //KVM.StateChanged += new EventHandler(KVM_StateChanged); + KVM.DisplaysReceived += new EventHandler(KVM_DisplaysReceived); + CenterKvmControl(false); + } + + void KVM_StateChanged(object sender, EventArgs e) + { + if (InvokeRequired) { Invoke(new EventHandler(KVM_StateChanged), sender, e); return; } + CenterKvmControl(false); + kvmControl.Visible = false; + //kvmControl.Visible = (kvmControl.State == KVMControl.ConnectState.Connected); + if (StateChanged != null) StateChanged(this, e); + } + + void KVM_DisplaysReceived(object sender, EventArgs e) + { + if (InvokeRequired) { Invoke(new EventHandler(KVM_DisplaysReceived), sender, e); return; } + if (DisplaysReceived != null) DisplaysReceived(this, e); + } + + public void CenterKvmControl(bool forceRefresh) + { + int w = clientPanel.Width; + int h = clientPanel.Height; + int l = 0; + int t = 0; + bool invalidate = true; + + if (zoomtofit == false) + { + if (kvmControl.Width != kvmControl.DesktopWidth || kvmControl.Height != kvmControl.DesktopHeight || kvmControl.ScaleFactor != 1) + { + kvmControl.Size = new System.Drawing.Size(kvmControl.DesktopWidth, kvmControl.DesktopHeight); + kvmControl.ScaleFactor = 1; + invalidate = true; + } + hScrollBar.Visible = (w < kvmControl.DesktopWidth); + rightScrollPanel.Visible = vScrollBar.Visible = (h < kvmControl.DesktopHeight); + cornerBlockPanel.Visible = (hScrollBar.Visible & vScrollBar.Visible); + } + else + { + hScrollBar.Visible = false; + rightScrollPanel.Visible = false; + double sfx = (double)kvmControl.DesktopWidth / (double)Width; + double sfy = (double)kvmControl.DesktopHeight / (double)Height; + double sf = Math.Max(sfx, sfy); + sf = Math.Max(1, sf); + if ((kvmControl.ScaleFactor != sf) || (forceRefresh)) + { + kvmControl.Size = new System.Drawing.Size((int)((double)kvmControl.DesktopWidth / sf), (int)((double)kvmControl.DesktopHeight / sf)); + kvmControl.ScaleFactor = sf; + invalidate = true; + } + } + + if (w < kvmControl.Width) { hScrollBar.Maximum = (kvmControl.Width - w) + 100; } else { l = (w - kvmControl.Width) / 2; } + if (h < kvmControl.Height) { vScrollBar.Maximum = (kvmControl.Height - h) + 100; } else { t = (h - kvmControl.Height) / 2; } + if (vScrollBar.Visible) { kvmControl.Top = 0 - vScrollBar.Value; } else { kvmControl.Top = t; } + if (hScrollBar.Visible) { kvmControl.Left = 0 - hScrollBar.Value; } else { kvmControl.Left = l; } + if (invalidate) kvmControl.Invalidate(); + } + + private void kvmControl_Resize(object sender, EventArgs e) + { + CenterKvmControl(false); + } + + private void ResizeKVMControl_Resize(object sender, EventArgs e) + { + CenterKvmControl(false); + } + + private void vScrollBar_ValueChanged(object sender, EventArgs e) + { + kvmControl.Top = -vScrollBar.Value; + } + + private void hScrollBar_ValueChanged(object sender, EventArgs e) + { + kvmControl.Left = -hScrollBar.Value; + } + + private void kvmControl_DesktopSizeChanged(object sender, EventArgs e) + { + CenterKvmControl(true); + } + + public void MouseWheelEx(object sender, MouseEventArgs e) + { + //if (KVMControl.WPF) kvmControl.SendMouseWheel(e, 0); + } + } +} diff --git a/KVMResizeControl.resx b/KVMResizeControl.resx new file mode 100644 index 0000000..1af7de1 --- /dev/null +++ b/KVMResizeControl.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/KVMSettingsForm.Designer.cs b/KVMSettingsForm.Designer.cs new file mode 100644 index 0000000..340a393 --- /dev/null +++ b/KVMSettingsForm.Designer.cs @@ -0,0 +1,210 @@ +namespace MeshCentralRouter +{ + partial class KVMSettingsForm + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(KVMSettingsForm)); + this.cancelButton = new System.Windows.Forms.Button(); + this.okButton = new System.Windows.Forms.Button(); + this.groupBox1 = new System.Windows.Forms.GroupBox(); + this.pictureBox1 = new System.Windows.Forms.PictureBox(); + this.label4 = new System.Windows.Forms.Label(); + this.label3 = new System.Windows.Forms.Label(); + this.qualityComboBox = new System.Windows.Forms.ComboBox(); + this.scalingComboBox = new System.Windows.Forms.ComboBox(); + this.label1 = new System.Windows.Forms.Label(); + this.frameRateComboBox = new System.Windows.Forms.ComboBox(); + this.label2 = new System.Windows.Forms.Label(); + this.groupBox1.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).BeginInit(); + this.SuspendLayout(); + // + // cancelButton + // + this.cancelButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.cancelButton.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.cancelButton.Location = new System.Drawing.Point(312, 182); + this.cancelButton.Name = "cancelButton"; + this.cancelButton.Size = new System.Drawing.Size(75, 23); + this.cancelButton.TabIndex = 0; + this.cancelButton.Text = "Cancel"; + this.cancelButton.UseVisualStyleBackColor = true; + this.cancelButton.Click += new System.EventHandler(this.cancelButton_Click); + // + // okButton + // + this.okButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.okButton.Location = new System.Drawing.Point(231, 182); + this.okButton.Name = "okButton"; + this.okButton.Size = new System.Drawing.Size(75, 23); + this.okButton.TabIndex = 1; + this.okButton.Text = "OK"; + this.okButton.UseVisualStyleBackColor = true; + this.okButton.Click += new System.EventHandler(this.okButton_Click); + // + // groupBox1 + // + this.groupBox1.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.groupBox1.Controls.Add(this.frameRateComboBox); + this.groupBox1.Controls.Add(this.label2); + this.groupBox1.Controls.Add(this.scalingComboBox); + this.groupBox1.Controls.Add(this.label1); + this.groupBox1.Controls.Add(this.qualityComboBox); + this.groupBox1.Controls.Add(this.label3); + this.groupBox1.Location = new System.Drawing.Point(12, 68); + this.groupBox1.Name = "groupBox1"; + this.groupBox1.Size = new System.Drawing.Size(375, 108); + this.groupBox1.TabIndex = 2; + this.groupBox1.TabStop = false; + this.groupBox1.Text = "Compression Settings"; + // + // pictureBox1 + // + this.pictureBox1.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.pictureBox1.Image = global::MeshCentralRouter.Properties.Resources._50_Computer; + this.pictureBox1.Location = new System.Drawing.Point(337, 12); + this.pictureBox1.Name = "pictureBox1"; + this.pictureBox1.Size = new System.Drawing.Size(50, 50); + this.pictureBox1.TabIndex = 3; + this.pictureBox1.TabStop = false; + // + // label4 + // + this.label4.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.label4.Location = new System.Drawing.Point(12, 12); + this.label4.Name = "label4"; + this.label4.Size = new System.Drawing.Size(319, 50); + this.label4.TabIndex = 4; + this.label4.Text = "Remote desktop quality, scaling and frame rate settings. These can be adjusted de" + + "pending on the quality of the network connection."; + // + // label3 + // + this.label3.AutoSize = true; + this.label3.Location = new System.Drawing.Point(9, 24); + this.label3.Name = "label3"; + this.label3.Size = new System.Drawing.Size(39, 13); + this.label3.TabIndex = 4; + this.label3.Text = "Quality"; + // + // qualityComboBox + // + this.qualityComboBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.qualityComboBox.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.qualityComboBox.FormattingEnabled = true; + this.qualityComboBox.Location = new System.Drawing.Point(128, 21); + this.qualityComboBox.Name = "qualityComboBox"; + this.qualityComboBox.Size = new System.Drawing.Size(238, 21); + this.qualityComboBox.TabIndex = 5; + // + // scalingComboBox + // + this.scalingComboBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.scalingComboBox.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.scalingComboBox.FormattingEnabled = true; + this.scalingComboBox.Location = new System.Drawing.Point(128, 48); + this.scalingComboBox.Name = "scalingComboBox"; + this.scalingComboBox.Size = new System.Drawing.Size(238, 21); + this.scalingComboBox.TabIndex = 7; + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Location = new System.Drawing.Point(9, 51); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(42, 13); + this.label1.TabIndex = 6; + this.label1.Text = "Scaling"; + // + // frameRateComboBox + // + this.frameRateComboBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.frameRateComboBox.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.frameRateComboBox.FormattingEnabled = true; + this.frameRateComboBox.Location = new System.Drawing.Point(128, 75); + this.frameRateComboBox.Name = "frameRateComboBox"; + this.frameRateComboBox.Size = new System.Drawing.Size(238, 21); + this.frameRateComboBox.TabIndex = 9; + // + // label2 + // + this.label2.AutoSize = true; + this.label2.Location = new System.Drawing.Point(9, 78); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(57, 13); + this.label2.TabIndex = 8; + this.label2.Text = "Frame rate"; + // + // KVMSettingsForm + // + this.AcceptButton = this.okButton; + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.CancelButton = this.cancelButton; + this.ClientSize = new System.Drawing.Size(399, 217); + this.Controls.Add(this.label4); + this.Controls.Add(this.pictureBox1); + this.Controls.Add(this.groupBox1); + this.Controls.Add(this.okButton); + this.Controls.Add(this.cancelButton); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; + this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "KVMSettingsForm"; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; + this.Text = "Remote Desktop Settings"; + this.Load += new System.EventHandler(this.SettingsForm_Load); + this.groupBox1.ResumeLayout(false); + this.groupBox1.PerformLayout(); + ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).EndInit(); + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.Button cancelButton; + private System.Windows.Forms.Button okButton; + private System.Windows.Forms.GroupBox groupBox1; + private System.Windows.Forms.PictureBox pictureBox1; + private System.Windows.Forms.Label label4; + private System.Windows.Forms.ComboBox frameRateComboBox; + private System.Windows.Forms.Label label2; + private System.Windows.Forms.ComboBox scalingComboBox; + private System.Windows.Forms.Label label1; + private System.Windows.Forms.ComboBox qualityComboBox; + private System.Windows.Forms.Label label3; + } +} \ No newline at end of file diff --git a/KVMSettingsForm.cs b/KVMSettingsForm.cs new file mode 100644 index 0000000..a90c61e --- /dev/null +++ b/KVMSettingsForm.cs @@ -0,0 +1,131 @@ +/* +Copyright 2009-2011 Intel Corporation + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +using System; +using System.Data; +using System.Text; +using System.Drawing; +using System.Windows.Forms; +using System.ComponentModel; +using System.Collections.Generic; + +namespace MeshCentralRouter +{ + public partial class KVMSettingsForm : Form + { + public KVMSettingsForm() + { + InitializeComponent(); + qualityComboBox.Items.Add(new DropListItem(90, "90%")); + qualityComboBox.Items.Add(new DropListItem(80, "80%")); + qualityComboBox.Items.Add(new DropListItem(70, "70%")); + qualityComboBox.Items.Add(new DropListItem(60, "60%")); + qualityComboBox.Items.Add(new DropListItem(50, "50%")); + qualityComboBox.Items.Add(new DropListItem(40, "40%")); + qualityComboBox.Items.Add(new DropListItem(30, "30%")); + qualityComboBox.Items.Add(new DropListItem(20, "20%")); + qualityComboBox.Items.Add(new DropListItem(10, "10%")); + qualityComboBox.Items.Add(new DropListItem(5, "5%")); + qualityComboBox.Items.Add(new DropListItem(1, "1%")); + scalingComboBox.Items.Add(new DropListItem(1024, "100%")); + scalingComboBox.Items.Add(new DropListItem(896, "87.5%")); + scalingComboBox.Items.Add(new DropListItem(768, "75%")); + scalingComboBox.Items.Add(new DropListItem(640, "62.5%")); + scalingComboBox.Items.Add(new DropListItem(512, "50%")); + scalingComboBox.Items.Add(new DropListItem(384, "37.5%")); + scalingComboBox.Items.Add(new DropListItem(256, "25%")); + scalingComboBox.Items.Add(new DropListItem(128, "12.5%")); + frameRateComboBox.Items.Add(new DropListItem(50, "Fast")); + frameRateComboBox.Items.Add(new DropListItem(100, "Medium")); + frameRateComboBox.Items.Add(new DropListItem(400, "Slow")); + frameRateComboBox.Items.Add(new DropListItem(1000, "Very slow")); + qualityComboBox.SelectedIndex = 4; + scalingComboBox.SelectedIndex = 0; + frameRateComboBox.SelectedIndex = 1; + } + + private class DropListItem + { + public int value; + public string str; + + public DropListItem(int value, string str) { this.value = value; this.str = str; } + + public override string ToString() { return str; } + } + + private void SettingsForm_Load(object sender, EventArgs e) + { + } + + public int Compression + { + get { return ((DropListItem)qualityComboBox.SelectedItem).value; } + set + { + if (value >= 90) { qualityComboBox.SelectedIndex = 0; return; } + if (value >= 80) { qualityComboBox.SelectedIndex = 1; return; } + if (value >= 70) { qualityComboBox.SelectedIndex = 2; return; } + if (value >= 60) { qualityComboBox.SelectedIndex = 3; return; } + if (value >= 50) { qualityComboBox.SelectedIndex = 4; return; } + if (value >= 40) { qualityComboBox.SelectedIndex = 5; return; } + if (value >= 30) { qualityComboBox.SelectedIndex = 6; return; } + if (value >= 20) { qualityComboBox.SelectedIndex = 7; return; } + if (value >= 10) { qualityComboBox.SelectedIndex = 8; return; } + if (value >= 5) { qualityComboBox.SelectedIndex = 9; return; } + qualityComboBox.SelectedIndex = 10; + } + } + + public int Scaling + { + get { return ((DropListItem)scalingComboBox.SelectedItem).value; } + set + { + if (value >= 1024) { scalingComboBox.SelectedIndex = 0; return; } + if (value >= 896) { scalingComboBox.SelectedIndex = 1; return; } + if (value >= 768) { scalingComboBox.SelectedIndex = 2; return; } + if (value >= 640) { scalingComboBox.SelectedIndex = 3; return; } + if (value >= 512) { scalingComboBox.SelectedIndex = 4; return; } + if (value >= 384) { scalingComboBox.SelectedIndex = 5; return; } + if (value >= 256) { scalingComboBox.SelectedIndex = 6; return; } + scalingComboBox.SelectedIndex = 7; + } + } + + public int FrameRate + { + get { return ((DropListItem)frameRateComboBox.SelectedItem).value; } + set + { + if (value <= 50) { frameRateComboBox.SelectedIndex = 0; return; } + if (value <= 100) { frameRateComboBox.SelectedIndex = 1; return; } + if (value <= 400) { frameRateComboBox.SelectedIndex = 2; return; } + frameRateComboBox.SelectedIndex = 3; + } + } + + private void okButton_Click(object sender, EventArgs e) + { + DialogResult = System.Windows.Forms.DialogResult.OK; + } + + private void cancelButton_Click(object sender, EventArgs e) + { + DialogResult = System.Windows.Forms.DialogResult.Cancel; + } + } +} diff --git a/KVMSettingsForm.resx b/KVMSettingsForm.resx new file mode 100644 index 0000000..c930dbd --- /dev/null +++ b/KVMSettingsForm.resx @@ -0,0 +1,546 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + + AAABAAkAEBAQAAEABAAoAQAAlgAAABAQAAABAAgAaAUAAL4BAAAgIBAAAQAEAOgCAAAmBwAAICAAAAEA + CACoCAAADgoAADAwEAABAAQAaAYAALYSAAAwMAAAAQAIAKgOAAAeGQAAEBAAAAEAIABoBAAAxicAACAg + AAABACAAqBAAAC4sAAAwMAAAAQAgAKglAADWPAAAKAAAABAAAAAgAAAAAQAEAAAAAACAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAIAAAIAAAACAgACAAAAAgACAAICAAACAgIAAwMDAAAAA/wAA/wAAAP//AP8A + AAD/AP8A//8AAP///wAACIiHh4eAAAAPiIh4eAAAAAAIh3eAAACIiIiIiIh4eI+IiIiIiIiIiEREREZE + Z4iIRGRMfIyMiIhlxMTIeMeIiFZEbHjIjoiIbExMiIiHiIhWxsyIiIiIiExMx4iIiIiITGzIj4+IiIhM + TM6I6OiIiHiIiIeIiIgI+IiIiIiIiOAHAADgDwAA+B8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAoAAAAEAAAACAAAAABAAgAAAAAAAABAAAAAAAAAAAAAAAB + AAAAAQAAAAAAAIAAAACGCwkAlAAAAJQNAgCfEQAAnBsQAJgdEQCiDgAAqQ0AAKgSAACqFQAApxkNAKUc + DwCyEgAAsRUAALQXAAC/FgIAuBgAAL0YAACaJhwArCcVAKMnHQC7Mh8AmDAoAJ42LACqMCMApTctAKU+ + MwDAGwAAwRwAAMUdAADKGgAAyh4FANIbAADdGQAA4RoAAOcaAADRIQAA1SMAAPEnCQDkMxMAvkwqALpI + OADMSjYA81A1AKVIQACtSkEAr1ZMAM5dTADUXUgA1GZKANhgSwDdYUwA3WpXAON5ZgDgfWsA6HxoAPp9 + aADmgWgA7IBrAPiKawDqi3oA8JtyAPahdwD6qH8AiYuLAIyOjgCXmpoAmp2dAJ6fnwCeoaEAoKGhAKOj + pACipKQApKamAKaoqQCoqqoAqaytAKyurwCvsbEAsbS1ALO3uAC1uLkAtru8ALm+vgDvk4IA7ZuNAPOX + hQDzn4oA9ZyLAPuuiQD3qpsA+q6eAPu0kQD0saYA+7KjAPi1oAD+sqQA+7+iAP7DswD6xrQA/8a2AP7G + ugD/yboA+s2+ALzBwQC+xMQAvsvNAL/MzQDFyMkAwczNAMnMzADDztAAxM7QAMbQ0QDJ0dIAytLUAMzT + 1QDN1NUA1NbXANjY2ADc3NwA/9LGAP/UxgD/1cgA/9rLAP/i2QD/5twA/+nfAN7h4QDo6OgA/+rgAP/r + 5gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAA////AAAAAFVVUk1GRkZGRkYAAAAAAACDeXduU0tGTUYAAAAAAAAAAAB5T0RDQ24AAAAAAIJr + a1VVU1JPT09NTEtLRm53d3J0cnNuc3JucW1tbW1Ld08BAQIBAQMVFRUVFSttS3dNBxQHBAUKFzEyNDUq + bUt3TRgYBwoKDyw4Nzk8NG1Ld0wuHAcPDxE2V1ZYWjtuTXdLMBsIDxIhPl9cXWJZc013Sy8WDxIiKWJn + ZGRoX21Pd0saCR0nJC17fXt+fmVtT3dGDRAdJyU6hX9/hIRoc093SwwRISMoPWNeW0FAP3NSd0ZGRktL + S01PT09SUlNzUgB3d3d3d3d3d3d3c3dzcnjgBwAA4A8AAPgfAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAAAAKAAAACAAAABAAAAAAQAEAAAAAAAAAgAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAIAAAIAAAACAgACAAAAAgACAAICAAACAgIAAwMDAAAAA/wAA/wAAAP//AP8A + AAD/AP8A//8AAP///wAAAAAAAAAAj4AAAAAAAAAAAAAAAAAIj4iIiAAAAAAAAAAAAAAIj4+Pj4iIAAAA + AAAAAAAAiIj4+I+IiIcAAAAAAAAACIiIj4iIiIh4cAAAAAAAAAiIiPiPiIiHeAAAAAAAAAAIiIiIiIiH + d4cAAAAAAAAAAAiIiId3d3eIiIAAAAAAAAAAh3d3d3eIiIiAAAAAAAAAAACHd3iIiIiIgAAAAAAAAIeH + iIiIh8fEeHAAAAAId4iIiId3xExETH+AAAAAeIiId3RExExsbGx4gAAAD3d3RERERExnx8fHz3AAAACE + RERETExEx8fIx8+IAAAAhERkTERGxnyMjI54iAAAAIR8VkRGxEyMjnjIyIgAAACHRkxExEzGh3fIjIiI + AAAAh0d8RkxsTIeIh4joiAAAAIfHx0xExMiHd46HiIgAAACIR3xExsbHiI6IiHiHAAAAiEjERMTMyI6I + iIiIiAAAAAhHxMbGzGiIiIiIiIiAAAAIbEZMzGzPiIiIiIiIgAAACFZMTGzMj4+Pj4+IiIAAAAhsTGxM + bIj4+Pj/+PiAAAAIVETEzMz/+P+Pj4iIgAAACHTGzGzOiIiIiIiIj3AAAAiETEzGx4iIiIiI+PgAAAAI + hHd3iIiIj4//AAAAAAAACIiIj4+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/8f///4A///4AD//8A + AP/+AAB//gAA//4AAP//gAAf/8AAH//wAB//AAAf4AAAH8AAAB+AAAAfwAAAD8AAAA/AAAAPwAAAD8AA + AA/AAAAPwAAAD8AAAA/gAAAH4AAAB+AAAAfgAAAH4AAAB+AAAAfgAAAP4AAD/+AH////////KAAAACAA + AABAAAAAAQAIAAAAAAAABAAAAAAAAAAAAAAAAQAAAAEAAAAAAAB+AQAAZmdmAGlpagB2dnYAfX1+AIIA + AACMAgAAhQsCAIwMAQCSAAAAmAcAAJMLAQCbDgAAkwwIAIsUCgCUEgUAnRAAAJIYDACLHBQAkB4WAJoe + EgCmAAAAqgYAAKELAAC5CAAApBIAAKsUAACqGAYArBwJALIVAAC5EwAAsxgBALsZAACNJBwAkyQcAKYg + DwC2IA0AoCATAKwjEQCgJxoAsy0bALcxHgCSJiAAliskAJYvKACfMCUAmjIqAI85NACZNjAAmz01AJc+ + OAC0MiEAvDUhAL0/LQDFEwAAwhsAAMsdAADQHAAA2BwAAO8UAADkGwAA6x0AANMgAADbIgAA2iQMAMAs + FQDlIQAA5S4LAPIqAwDhLhgA9j8dAMs8JQDOPygAmUQ8AKhANgCgQTkAr0U6AMNCLwDDRTIAyUYyAMxI + MwDBTD8AzVI/ANZROQDaVj8A8UQiAP5UMgCZU00Al1RRAKRKRACuVUwAsVJHALBUSgCiWlUArFhRALFc + VQCzZF0AmmtqALFvZgC5a2QAr3FtALdwawC+dWsAoHVyALZ4cQDHU0IAzF1MANFWQQDVWUQA2FpFANVe + SAD1X0IAxGlaANVlUgDcZlIA3WhUANhuXQDiY0kA4mZQAOJqVADpcFoAxnRoAN10YgDjd2QA5nllAOp8 + ZgDyfGYA6IBtAPqFbgDjhHQA6YZ0AOyIdgDuingA8Y16APqNeQCDhIQAi4yOAIuOkgCOlZoAlZWVAJSV + mACWm5wAnZ2dAKGFhQCzhIEAp5KSAKCfnwCqnZ4AspOTAL2VkwC5nJsAuaGcAJeepACVoaYAnKCmAJit + qgCfrrEAoqKiAKukpwCmpqgApqmrAKurqwCyp6kAsKqsAKyssAClsLMArbKzAKS6tgCttbkAs7O0ALW5 + vAC6ursA0ZWSAO2WhgDrmIoA8piHAPWbigD7nowAy6KcAOKqnADtrJ0A8qOVAPikkwDwqZ0A/KubAPG1 + ngD/sJ0A9bieAMCtrADbtKoAwre0AMq1sQDFuLYAwbu9ANG1sADbuLMA4K6jAOu8pwDgtKgA57+tAPWw + ogD7tKUA+b+mAPS3qwD+vK0A/r+xANXCvgD1wKoA+8euAOjGuAD+xLIA/sm1AP7EuQD9y7sAvb/DAMG/ + wQC3wcQAvMLDALnGyAC+y8wAw8PDAMTFyADDyswAy8vLANXKxgDSzcsA2M/LAMLP0ADMz9AAx9XWAMzT + 1ADK1tgA09PTANLW2ADU2twA29zcAOzPwgD/zsEA/9LDAP/VyQD/2coA5NvXAOvc1gDs3tgA/93SAP/h + 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 + AADwAAAAAB8AAPAAAAAAHwAA8AAAAAAfAADwAAAAAB8AAPAAAAAAHwAA8AAAAAAfAADwAAAAAB8AAPgA + AAAAHwAA+AAAAAAPAAD4AAAAAB8AAPgAAAAAPwAA+AAAAD//AAD4AAA///8AAPgAP////wAA/D////// + AAD///////8AACgAAAAwAAAAYAAAAAEACAAAAAAAAAkAAAAAAAAAAAAAAAEAAAABAAAAAAAAX19fAGVl + ZQBrbGsAdHR1AHp6egCDAQAAhAoBAIsMAACUBgAAmQYAAJMNAACaDQAAhxEIAIoUCwCOGAwAlBIEAJwQ + AQCTFgkAnhsNAIwaEgCnDAAAtAYAAKMSAACqFAAAphgGAKMbCwCqHgwAshUAALYYAAC7GQAAsR4LAIsj + GgCVJRsAtSILALkgCwCnJBUAqyQSAK4oFwCxJRIAsCoXALsrFwCzLBoAuS4aALIwHwC6MBwAii8oAJUq + IwCJMCkAmTAnAJkzKwCcOS8Aijs0AJw8NACvNicApTYrALUzIAC8NSIAtzopALo7KQClPDIAwxsAAMsd + AADTHQAA5RoAAMYjCADYIQAAzzAVAOckAADwKAIA9TEKAO85FgD0OBIAwTciAMI5JADBPisAyT4oANY8 + IgClQzsAvUQ0ALdKNwDFQS0AykQvANBDLADCRjQAykUwAMRINgDKSjYAwko6AM5NOADXTjYA0VA8APRK + KACNSUEAk0pFAJtTTgCXWFQApktEAKpRSgCzUEUAuVBCALFWTAC4VEoArVdQAKtYUgCmXVgAtF1TALBf + WgCbY14At2BUAJVlYQCcfHkAomZhALhrYwC7c2wAoHRzAL54cAC2f3sAylVDANVaRgDiWkIA12JOANlo + VQDpZUwA+GFDAORrVQDncl0AxHVqAM56bgDRcGIA3XRiAN57awDBfHUA5XpnAPt+ZQCugXsA3oFvAMWF + fADYinwA5oBuAPOBawDqiHYA9Yp1APaRfQCCgoMAioqMAJ2EgwCIkI4AgJ2MAIyNkQCOkZUAjJOYAJKS + lACWlpkAmpqbAKSKigC0iIUAs42LALGSjgCmmJgAh6SYAJ2eogCaoaYAnaWpAJ+trACjo6MApKaqAKim + qACjqawAq6urALWpqQCurrAArLK1AK+4uwC0tLQAurO0ALW2uAC8t7gAtby/ALu7uwDOkIUAyZaPAMSf + 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 + 1v+kpab/r1ZM/6U3Lf+iDgD/sBYA/70YAP/KHgX/6ot6//Sxpv/3qpv/+q6e//6ypP/zn4r/ws3O/6iq + qv/O1NX/oqSk/61KQf+jJx3/sBQA/8EcAP/SGwD/5DMT//uyo//+xrr//sOz///Gtv//ybr/+LWg/8LO + z/+qrKz/zdTW/6Gjo/+qMCP/qQ0A/8AbAP/RIQD/4RoA//NQNf//0sb//9TI///Uxv//18j//9rL//rG + tP/Dzs//rK6v/87U1v+goaH/pRwP/7MWAP/FHQD/1SMA/+caAP/6fWj//+vm///i2f//5tz//+nf///q + 4P/6zb7/xM7Q/6+ysv/O1Nb/oKGh/6cZDf+4GAD/yhoA/90ZAP/xJwn/+Ipr//u/ov/7tJH/+66J//qo + f//2oXf/8Jty/8XO0P+xtLX/ztTW/6Chof+goaH/oKGh/6Gio/+jpaX/paen/6epqf+prKz/rK+v/6+x + sf+xtLX/tLi4/7a7vP/Fz9H/s7e4/87U1njO1Nb/ztTW/87U1v/N1Nb/zdPV/83T1f/M09X/y9LU/8rS + 1P/K0tP/ydHT/8jR0v/H0NH/xs/R/7a6u5XgBwAA4AcAAPAPAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKAAAACAAAABAAAAAAQAgAAAAAACAEAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACjo6MDurq6I7S0 + tFatra18t7e3hbm5uYeoqKiGjo6Of4KCgmB7e3stg4ODCwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAkJCQHbCw + sHzHx8fIzs7O8NXV1fvZ2dn+1tbW/s7Ozv7MzMz8wsLC9Kurq9WUlJRrjo6OMAAAAAAAAAAAAAAAAAAA + AAEAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJWV + lWu+vr7r0tLS/9fX1//b29v/3t7e/9zc3P/U1NT/zc3N/93d3f/b29v/wcHB/8XFxf+4uLj+eXl5UBMT + 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 + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIiIiCyqqqrOzc3N/83Nzf/Ly8v/z8/P/9PT + 0//X19f/2tra/9vb2//a2tr/19fX/9LS0v/MzMz/0dHR/9/f3//Z2dn/wMDA/7e3t/++vr7/ycnJ/7i4 + uP+wsLD9WVlZiAAAABYAAAAjAAAAKwAAACsAAAAlAAAAGQAAAA0AAAAGAAAAAwAAAAEAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAi4uLJbS0tOfIyMj/w8PD/8bG + xv/Ly8v/zs7O/9LS0v/X19f/29vb/9zc3P/c3Nz/2NjY/9HR0f/Kysr/09PT/+Hh4f/Q0ND/ubm5/8DA + wP/Pz8//urq6/62trf+urq7/ubm5/2FhYawAAABDAAAARgAAAEAAAAA2AAAAKQAAABwAAAARAAAACQAA + AAQAAAABAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACJiYkDq6urvcDA + wP++vr7/wsLC/8TExP/IyMj/zMzM/9HR0f/X19f/29vb/93d3f/c3Nz/2NjY/9HR0f/Jycn/0tLS/9vb + 2//FxcX/wcHB/8nJyf+6urr/sLCw/66urv+oqKj/oqKi/6qqqv8fHx93AAAATgAAAEkAAAA+AAAAMwAA + ACYAAAAbAAAAEQAAAAoAAAAEAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAChoaEXtra29Lm5uf+8vLz/v7+//8LCwv/FxcX/ycnJ/87Ozv/U1NT/2dnZ/9zc3P/b29v/1tbW/87O + zv/Hx8f/zMzM/8zMzP/AwMD/w8PD/7e3t/+ysrL/rq6u/6SkpP+ampr/l5eX/6qqqv9GRkaXAAAARwAA + AEgAAABAAAAANQAAACgAAAAfAAAAFgAAAA4AAAAHAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAC6uroWtLS08be3t/+5ubn/vLy8/7+/v//BwcH/xcXF/8nJyf/Pz8//1NTU/9bW + 1v/V1dX/0dHR/8rKyv/ExMT/xcXF/8DAwP++vr7/t7e3/7Gxsf+pqan/n5+f/5WVlf+SkpL/mJiY/6ur + q/9RUVGcAAAAPAAAAEEAAAA6AAAAMQAAACcAAAAeAAAAFgAAAA4AAAAHAAAAAwAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAC0tLQCtbW1y7W1tf+3t7f/ubm5/729vf+/v7//wcHB/8TE + xP/Hx8f/y8vL/83Nzf/MzMz/ycnJ/8XFxf/BwcH/v7+//7q6uv+ysrL/qamp/6Ghof+Xl5f/kpKS/5OT + k/+YmJj/nZ2d/6urq/8pKSltAAAALgAAADQAAAAzAAAALAAAACMAAAAaAAAAEwAAAAwAAAAHAAAAAgAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAqampQrOzs/e7u7v/t7e3/7q6 + uv+8vLz/vr6+/8DAwP/CwsL/xMTE/8bGxv/FxcX/wsLC/7y8vP+1tbX/ra2t/6ampv+bm5v/jIyM/3t7 + e/95eXn/ioqK/5SUlP+bm5v/pqam/4GBgcoWFhZFODk5YTk5Olpubm5aEhISKQAAABwAAAAWAAAADgAA + AAgAAAAEAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGdn + Z0WZmZnNu7u7/7a2tv+zs7P/tLS0/7S0tP+zs7P/srKy/7CwsP+tra3/pqam/5ubm/+SkpL/iIiI/3x8 + fP9qamn/X19f/2VlZf97e3z/jY6O/56en/+sra3/tre3/pubnM6urq/iycrK/7a2t+6IiIi+Z2dnKwAA + ABEAAAALAAAABwAAAAMAAAACAAAAAQAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAICAgGTk5ObJ2dneiurq7/qamp/6Ojo/+enp7/m5ub/5ycnP+Pj4//goGB/3p6 + ev9zc3P/bm5u/2tsbP91dnf/hIWG/5iYmv+goaL/sLCx/7e3uP+3t7f/u7u7/8HBwf/AwMD/v7+//L6+ + vv+Xl5ftgICAMQAAAAMAAAADAAAAAQAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAABAAAAAwAAAAYAAAAGAAAAABAQECNGRkZ0eXl5v4+PjuKgoKD4o6Oj/ouL + iv97e3v/gICB/4SEhf+IiYv/kJCT/5manP+oqKv/rq6w/66ur/+vr7D/r6+w/7a3t/+1t7f/u8LD/8LM + zf/Jzs//wcDA/by8vPyfn5/6b29vUwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAMAAAAGAAAACQAAAAwAAAAOAAAADQAAAAUAAAAAFhYXC0BA + QjBvb3GBjo+R7oqKjf+OjpL/kJCU/4+QlP+Skpb/lpaZ/56eof+lpaf/p6mr/6yxtP+1vsD/wMnL/87T + 1P/W09P/2cnG/9+4tP/Wvrn/wMXF/cHBwfyioqL8jIyMbgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBAQEARQUFQgtLS8XSkpMNWpq + bGaCgoaYj4+TxJSUmOWQkJT4jY2R/42Nkf+MjJH/i42R/4uOk/+Nkpf/nKSo/6+4u//Dycv/z87P/9TH + xv/Usq7/2p+U/9FwYv/HQS7/wBwE/7QGAP+zX1X/w87P/sTExPyhoaH+n5+fiwAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAaGhoHJCQlHT8/QTBbW15MfX2AgY6O + kbCYmJ3anJyh+Zubn/+Xl5v/jo6S/4yMkf+KjpL/jJOY/5ihpf+hqKv/rauv/7mwsv/HvLz/yK2q/8mW + j//Oem7/wko6/7kgC/+0FAD/rBAA/6URAP+lFgP/phoI/6YTAf+yVkv/xczN/8fHx/2kpKT/nJycqwAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAI+PkxNwcHM3dHV4ZouIjqKOjZHTkZGW7piY + nPufn6P/oKCk/5+fov+fn6P/nZ+j/5mfpP+fp6v/oquu/6usr/+8tbb/xra1/8SfnP++eHD/uFRK/682 + J/+nHg7/phgH/6UTAv+lFQP/phoJ/6gfDv+qIRD/rCMR/68kEf+wJRL/sSYT/7EeC/+5UEL/x8vM/8nJ + yf6pqan/nJycxgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAkZGVao+Pk+aTk5f/kpCW/4Cd + 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 + T//kiXr/5ol5/+iKef/qjHv/7I17/+2OfP/vj33/8JB+//KRf//zkoD/9JOB//WVgv/3kX7/7H1n/9TI + xv/W2dn/ra2t/5mZmdwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOfn5zO5vsH+noSE/4wc + Ff+mS0T/pEhB/6JGP/+kRj7/pkA2/5UWCP+VDQD/mw8A/6ISAP+nFAD/qhUA/64VAP+0FwD/uRkA/74a + AP/DGgD/yScM/+B0Yv/plYb/65SF/+yVhf/ulob/8JeH//GZiP/zmYj/9JqJ//abiv/2nIv/952M//ie + jf/5noz/84Jr/9W8uP/Y3N3/t7e3/5mZmeoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAO7u + 7xe9wMLwqJyd/4ojHP+pUEn/qFFK/6dOR/+sUUj/pDYs/5MOAP+ZDwD/nxEA/6YTAP+pFAD/rBUA/7IW + AP+4GAD/vhoA/8McAP/GGwD/zzAV/+aFdP/tn5H/7p2P//Cej//xn5D/86GR//Whkf/2o5L/96ST//il + lP/5ppX/+qaW//unl//8p5f/+4t0/9q0rP/Y3uD/vr6+/5mZmfSenp4HAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAPLy8ge/wMLbs7Gz/4ovKP+qUUv/q1hS/61YT/+wV07/nCQX/5UMAP+cEAD/ohIA/6gU + AP+rFQD/sBYA/7YYAP+7GQD/whsA/8YcAP/KGwD/1jwi/+uVhv/wp5v/8aaY//Onmf/1qJr/9qma//iq + m//5q5z/+ayc//utnv/8rp7//a+f//6woP//sKD//5aA/+SzqP/Y4OH/w8PD/5mZmfucnJwiAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADGxsi/vMDC/48+Of+qUEr/sF9a/7NhWf+xVUv/lhQG/5kN + AP+gEQD/pRMA/6kVAP+uFQD/tBcA/7kYAP+/GgD/xBwA/8gdAP/NHQD/3Ewz/++jlv/yr6P/86+i//Wv + ov/3sKP/+LGk//mypP/7s6X//LSm//62pv//tqf//7ao//+4qf//uar//6KN/+Wvo//a4eL/yMjI/5mZ + mf6bm5tJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADNzs+bw8jL/5hNSP+oTkj/tmpi/7lr + Y/+rRDr/lgwA/50QAP+jEgD/pxQA/6sVAP+xFgD/txgA/78aAP/GHQD/zB4A/9MfAP/aIwL/6WVM//e0 + p//5ua3/+7mr//y6rP/9u63//b2u//6+rv/+v6///8Gw///CsP//w7H//8Sy///Esv//xrT//7Sc/+iv + nP/b4OL/zc3N/5qamv+bm5tzAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADW1th3w8nM/5tT + Tv+oTET/u3Ns/7tsZP+eKBv/mAsA/6ERAP+oFAD/rhUA/7cYAP+/GwD/xx0A/88fAP/XIQD/3iIA/+Qj + AP/sLgn/9oFq//3Btf//wrX//8K1///Etv//xbb//8e3///IuP//ybj//8m5///Luv//zLv//827///O + vP//0L3//8Oq/+yulf/c3t//0dHR/5ubm/+ampqcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AADc3N5TxMvN/5dYVP+mR0D/wXx1/7ZbUv+aEgT/oxEA/6oUAP+xFgD/uRkA/8EbAP/HHQD/zR4A/9Qg + AP/aIgD/4CMA/+cjAP/vORb/+ZmF//7Jvv//yL3//8m9///Kvf//zL7//82////Ov///z8D//9DA///R + wf//0sH//9PC///Uw///1cT//8y2//Guk//b2tn/2NnZ/56env+ampq6AAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAADh4eI4xs3P/5dlYv+lQjn/xoF5/6s9Mf+eDAD/qBQA/6wVAP+0FwD/uxkA/8Ib + AP/JHQD/0B8A/9YhAP/cIwD/5CQA/+sjAP/ySyn//K6f///Pxv//zsP//8/E///QxP//0sX//9PF///U + xv//1cb//9bH///Xx///2Mj//9jJ///Zyv//28z//9XD//Wxlv/b09D/3+Dh/6Ghof+ZmZnTAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADU1NYbyc/R85x8ef+iPTP/wnZt/6AeD/+hDwD/qRQA/68V + AP+3GAD/vhoA/8QcAP/LHgD/0yAA/9ghAP/fIwD/6CQA/+0lAP/2YUP//8K2///VzP//1Mr//9XK///W + yv//18v//9jL///ZzP//2sz//9vN///czv//3c///93Q///f0f//4NP//93O//q5nv/dz8n/4+bn/6mp + qf+ZmZnkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADt7e4Jys3Q3qmZmP+cOS//s1BF/5sO + AP+kEgD/qxUA/7EWAP+5GQD/vxsA/8cdAP/OHgD/1SAA/9siAP/jJAD/6yUA//ArBP/6fmX//9LI///a + 0f//2dD//9rQ///b0f//3NH//93S///e0v//39L//+DT///h1f//4tb//+PX///k2P//5dn//+LW//3C + qf/lz8b/6Ozu/7Ozs/+YmJjynp6eBwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAzM7QuLq0 + tf+SKB7/nhsN/50PAP+mEwD/rBUA/7QXAP+8GQD/wRsA/8kdAP/RHwD/1yEA/90jAP/mJQD/7SQA//Q4 + Ev/+oI3//97X///e1///39b//9/X///g1///4df//+LY///j2P//5Nn//+Xa///m2///593//+fe///o + 3v//6d///+je///Mtv/mzMH/7fHz/8HBwf+Xl5f6nJycJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAwMHDlcPExf+NIxn/lwkA/6ARAP+nFAD/rhUA/7YXAP+9GgD/wxwA/8sdAP/TIAD/2SIA/98j + AP/oJQD/7iUA//dKJ///uq3//+Pd///i2///49v//+Pb///j2///5dz//+bc///n3P//597//+je///p + 4P//6uH//+vi///r4///7OT//+zk///Uwv/lyLz/7/P1/8zMzP+YmJj+nJycSQAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAy8vNdsLJyv+KMSj/mQgA/6ERAP+oFAD/rxYA/7gYAP++GgD/xBwA/80e + AP/VIAD/2yIA/+EkAP/pJQD/8CUA//piQ///z8X//+fh///l3///5t///+bf///n4P//6OD//+nh///q + 4f//6+L//+vj///s5P//7OT//+3l///s5P//7OT//+rg///Uv//owK7/8fX3/9bW1v+YmJj/m5ubawAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAArq6xVcjR0/+NSUH/mQYA/6MSAP+pFAD/sBYA/7kY + AP+/GwD/xhwA/84eAP/WIQD/3CIA/+MkAP/rJQD/8ioC//x+Zf//39j//+rl///p5P//6uT//+rk///q + 4///6uH//+nf///m3P//5Nj//+HU///czP//18T//9G8//3JtP/5w6v/9ryk//Gtjv/WppL/7/P1/+Xl + 5f+YmJj/np6ejAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA0tLUNsXNz/6UZWD/lwUA/6QS + AP+pFAD/sRYA/7oZAP/AGwD/xh0A/9AfAP/WIQD/3SIA/+QkAP/sJAD/9TEK//+TfP//39f//9/W///X + y///0MH//8m4//3DsP/3vqn/9Lqk//S5of/0tp3/9LOY/+mulv/frpr/2LOj/9i+tP/cy8T/4tbR/+bh + 3//m5ub/8fHx/+jo6P+mpqb6s7OzTQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAubm7F8HG + yO+dhIL/lAcA/6USAP+qFAD/sxYA/7wZAP/DGgD/yhsA/9UaAP/bGgD/4BoA/+YaAP/rGwD/7CsE/+1s + Uv/qmYf/4pyM/+GgkP/hpZX/2qqd/9Gxp//Rvrn/1MrH/9nSz//c19b/2dfX/9jZ2v/X29z/297f/9zf + 4Pvb3d3u2tvc39XW1tnb29vG3Nzcst7e3o3MzMxLAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAw8PGBby/wdeomZn/lQcA/6YJAP+oCwD/rw0A/7URAP+1GAD/tSIL/7c1IP+3Sjf/vGBQ/8Z1 + aP/Fin//wKCa/8W3tP/LxsX/zs/Q/9HW1//T2Nr/0tfZ/9PX2f/T1db50dPT7NDR0d3Nzs7Xz8/Pwc/P + z63U1NSP0NDQdNLS0k/Ozs8rysrKFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAALCytb2zra7/hTgw/5FKQ/+cZV//roF7/7GSjv+xoaD/tLGy/7vB + w/+/ys3/xdDT/8rU1v/N1db/ztPU983P0OvNzs7dy8vL1MvLy8HKysqmzs7Oj8vLzHnOzs5Oy8vLM8rK + yhrKysoDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAK2tr7PCw8P/t7/C/73Fx//Dy83/xMrM/cXJ + y/XHycrqxsfI3MbGx83Hx8jAx8fHp8rKyovIyMhzy8vLUsrKyjnKysoaysrKCwAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKCgo3a5ubvAvLy9wr+/ + wK28vL6NwMDBb8LCw1fHx8g8ysrKIMrKyhQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AADKysoOysrKAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD//4AD//8AAP/8 + AAD4fwAA//AAACAPAAD/4AAAAAcAAP/AAAAAAQAA/4AAAAABAAD/gAAAAAEAAP+AAAAAAQAA/4AAAAAB + AAD/wAAAAAEAAP/gAAAAAAAA//AAAAAHAAD/hAAAAH8AAP4BAAAAfwAA/4AAAAB/AAD8AAAAAH8AAMAA + AAAAfwAAgAAAAAB/AACAAAAAAH8AAIAAAAAAPwAAgAAAAAA/AACAAAAAAD8AAIAAAAAAPwAAwAAAAAA/ + AADAAAAAAD8AAMAAAAAAPwAAwAAAAAA/AADAAAAAAD8AAMAAAAAAHwAAwAAAAAAfAADgAAAAAB8AAOAA + AAAAHwAA4AAAAAAfAADgAAAAAB8AAOAAAAAAHwAA4AAAAAAfAADgAAAAAA8AAPAAAAAADwAA8AAAAAAP + AADwAAAAAA8AAPAAAAAADwAA8AAAAAAPAADwAAAAAB8AAPAAAAAD/wAA+AAAAf//AAD4AAH///8AAPgB + /////wAA/P//////AAA= + + + \ No newline at end of file diff --git a/KVMViewer.Designer.cs b/KVMViewer.Designer.cs new file mode 100644 index 0000000..cf6d852 --- /dev/null +++ b/KVMViewer.Designer.cs @@ -0,0 +1,404 @@ +using System.Windows.Forms; + +namespace MeshCentralRouter +{ + + public class BlankPanel : System.Windows.Forms.Panel + { + public BlankPanel() + { + + } + + protected override void OnPaintBackground(PaintEventArgs e) + { + // Do not paint background. + } + } + + partial class KVMViewer + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.components = new System.ComponentModel.Container(); + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(KVMViewer)); + this.mainMenu = new System.Windows.Forms.MenuStrip(); + this.toolStripMenuItem2 = new System.Windows.Forms.ToolStripMenuItem(); + this.MenuItemConnect = new System.Windows.Forms.ToolStripMenuItem(); + this.MenuItemDisconnect = new System.Windows.Forms.ToolStripMenuItem(); + this.toolStripMenuItem5 = new System.Windows.Forms.ToolStripSeparator(); + this.settingsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.toolStripMenuItem1 = new System.Windows.Forms.ToolStripSeparator(); + this.MenuItemExit = new System.Windows.Forms.ToolStripMenuItem(); + this.viewToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.zoomtofitToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.statusToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.debugToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.toolStripMenuItem6 = new System.Windows.Forms.ToolStripSeparator(); + this.refreshToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.pauseToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.actionsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.sendCtrlAltDelToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.mainStatusStrip = new System.Windows.Forms.StatusStrip(); + this.mainToolStripStatusLabel = new System.Windows.Forms.ToolStripStatusLabel(); + this.toolStripStatusLabel1 = new System.Windows.Forms.ToolStripStatusLabel(); + this.updateTimer = new System.Windows.Forms.Timer(this.components); + this.topPanel = new System.Windows.Forms.Panel(); + this.displaySelectComboBox = new System.Windows.Forms.ComboBox(); + this.settingsButton = new System.Windows.Forms.Button(); + this.zoomButton = new System.Windows.Forms.Button(); + this.cadButton = new System.Windows.Forms.Button(); + this.connectButton = new System.Windows.Forms.Button(); + this.resizeKvmControl = new MeshCentralRouter.KVMResizeControl(); + this.mainMenu.SuspendLayout(); + this.mainStatusStrip.SuspendLayout(); + this.topPanel.SuspendLayout(); + this.SuspendLayout(); + // + // mainMenu + // + this.mainMenu.BackColor = System.Drawing.SystemColors.Menu; + this.mainMenu.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.toolStripMenuItem2, + this.viewToolStripMenuItem, + this.actionsToolStripMenuItem}); + this.mainMenu.Location = new System.Drawing.Point(0, 0); + this.mainMenu.Name = "mainMenu"; + this.mainMenu.Size = new System.Drawing.Size(1227, 24); + this.mainMenu.TabIndex = 1; + this.mainMenu.Text = "menuStrip1"; + // + // toolStripMenuItem2 + // + this.toolStripMenuItem2.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.MenuItemConnect, + this.MenuItemDisconnect, + this.toolStripMenuItem5, + this.settingsToolStripMenuItem, + this.toolStripMenuItem1, + this.MenuItemExit}); + this.toolStripMenuItem2.Name = "toolStripMenuItem2"; + this.toolStripMenuItem2.Size = new System.Drawing.Size(59, 20); + this.toolStripMenuItem2.Text = "&Control"; + this.toolStripMenuItem2.DropDownOpening += new System.EventHandler(this.toolStripMenuItem2_DropDownOpening); + // + // MenuItemConnect + // + this.MenuItemConnect.Name = "MenuItemConnect"; + this.MenuItemConnect.Size = new System.Drawing.Size(167, 22); + this.MenuItemConnect.Text = "&Connect..."; + this.MenuItemConnect.Click += new System.EventHandler(this.MenuItemConnect_Click); + // + // MenuItemDisconnect + // + this.MenuItemDisconnect.Name = "MenuItemDisconnect"; + this.MenuItemDisconnect.Size = new System.Drawing.Size(167, 22); + this.MenuItemDisconnect.Text = "&Disconnect"; + this.MenuItemDisconnect.Click += new System.EventHandler(this.MenuItemDisconnect_Click); + // + // toolStripMenuItem5 + // + this.toolStripMenuItem5.Name = "toolStripMenuItem5"; + this.toolStripMenuItem5.Size = new System.Drawing.Size(164, 6); + // + // settingsToolStripMenuItem + // + this.settingsToolStripMenuItem.Name = "settingsToolStripMenuItem"; + this.settingsToolStripMenuItem.Size = new System.Drawing.Size(167, 22); + this.settingsToolStripMenuItem.Text = "Session Settings..."; + this.settingsToolStripMenuItem.Click += new System.EventHandler(this.settingsToolStripMenuItem_Click); + // + // toolStripMenuItem1 + // + this.toolStripMenuItem1.Name = "toolStripMenuItem1"; + this.toolStripMenuItem1.Size = new System.Drawing.Size(164, 6); + // + // MenuItemExit + // + this.MenuItemExit.Name = "MenuItemExit"; + this.MenuItemExit.Size = new System.Drawing.Size(167, 22); + this.MenuItemExit.Text = "E&xit"; + this.MenuItemExit.Click += new System.EventHandler(this.MenuItemExit_Click); + // + // viewToolStripMenuItem + // + this.viewToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.zoomtofitToolStripMenuItem, + this.statusToolStripMenuItem, + this.debugToolStripMenuItem, + this.toolStripMenuItem6, + this.refreshToolStripMenuItem, + this.pauseToolStripMenuItem}); + this.viewToolStripMenuItem.Name = "viewToolStripMenuItem"; + this.viewToolStripMenuItem.Size = new System.Drawing.Size(44, 20); + this.viewToolStripMenuItem.Text = "&View"; + this.viewToolStripMenuItem.DropDownOpening += new System.EventHandler(this.viewToolStripMenuItem_DropDownOpening); + // + // zoomtofitToolStripMenuItem + // + this.zoomtofitToolStripMenuItem.CheckOnClick = true; + this.zoomtofitToolStripMenuItem.Name = "zoomtofitToolStripMenuItem"; + this.zoomtofitToolStripMenuItem.Size = new System.Drawing.Size(138, 22); + this.zoomtofitToolStripMenuItem.Text = "&Zoom-to-fit"; + this.zoomtofitToolStripMenuItem.CheckStateChanged += new System.EventHandler(this.zoomtofitToolStripMenuItem_CheckStateChanged); + // + // statusToolStripMenuItem + // + this.statusToolStripMenuItem.Checked = true; + this.statusToolStripMenuItem.CheckOnClick = true; + this.statusToolStripMenuItem.CheckState = System.Windows.Forms.CheckState.Checked; + this.statusToolStripMenuItem.Name = "statusToolStripMenuItem"; + this.statusToolStripMenuItem.Size = new System.Drawing.Size(138, 22); + this.statusToolStripMenuItem.Text = "&Status"; + this.statusToolStripMenuItem.CheckedChanged += new System.EventHandler(this.statusToolStripMenuItem_CheckedChanged); + // + // debugToolStripMenuItem + // + this.debugToolStripMenuItem.CheckOnClick = true; + this.debugToolStripMenuItem.Name = "debugToolStripMenuItem"; + this.debugToolStripMenuItem.Size = new System.Drawing.Size(138, 22); + this.debugToolStripMenuItem.Text = "&Debug"; + this.debugToolStripMenuItem.Click += new System.EventHandler(this.debugToolStripMenuItem_Click); + // + // toolStripMenuItem6 + // + this.toolStripMenuItem6.Name = "toolStripMenuItem6"; + this.toolStripMenuItem6.Size = new System.Drawing.Size(135, 6); + // + // refreshToolStripMenuItem + // + this.refreshToolStripMenuItem.Name = "refreshToolStripMenuItem"; + this.refreshToolStripMenuItem.ShortcutKeys = System.Windows.Forms.Keys.F5; + this.refreshToolStripMenuItem.Size = new System.Drawing.Size(138, 22); + this.refreshToolStripMenuItem.Text = "&Refresh"; + // + // pauseToolStripMenuItem + // + this.pauseToolStripMenuItem.Name = "pauseToolStripMenuItem"; + this.pauseToolStripMenuItem.ShortcutKeys = System.Windows.Forms.Keys.F7; + this.pauseToolStripMenuItem.Size = new System.Drawing.Size(138, 22); + this.pauseToolStripMenuItem.Text = "&Pause"; + // + // actionsToolStripMenuItem + // + this.actionsToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.sendCtrlAltDelToolStripMenuItem}); + this.actionsToolStripMenuItem.Name = "actionsToolStripMenuItem"; + this.actionsToolStripMenuItem.Size = new System.Drawing.Size(59, 20); + this.actionsToolStripMenuItem.Text = "Actions"; + // + // sendCtrlAltDelToolStripMenuItem + // + this.sendCtrlAltDelToolStripMenuItem.Name = "sendCtrlAltDelToolStripMenuItem"; + this.sendCtrlAltDelToolStripMenuItem.Size = new System.Drawing.Size(164, 22); + this.sendCtrlAltDelToolStripMenuItem.Text = "Send &Ctrl-Alt-Del"; + this.sendCtrlAltDelToolStripMenuItem.Click += new System.EventHandler(this.sendCtrlAltDelToolStripMenuItem_Click); + // + // mainStatusStrip + // + this.mainStatusStrip.BackColor = System.Drawing.SystemColors.Menu; + this.mainStatusStrip.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.mainToolStripStatusLabel, + this.toolStripStatusLabel1}); + this.mainStatusStrip.Location = new System.Drawing.Point(0, 787); + this.mainStatusStrip.Name = "mainStatusStrip"; + this.mainStatusStrip.Size = new System.Drawing.Size(1227, 22); + this.mainStatusStrip.TabIndex = 9; + this.mainStatusStrip.Text = "statusStrip1"; + // + // mainToolStripStatusLabel + // + this.mainToolStripStatusLabel.Name = "mainToolStripStatusLabel"; + this.mainToolStripStatusLabel.Size = new System.Drawing.Size(1212, 17); + this.mainToolStripStatusLabel.Spring = true; + this.mainToolStripStatusLabel.Text = "---"; + this.mainToolStripStatusLabel.TextAlign = System.Drawing.ContentAlignment.MiddleLeft; + // + // toolStripStatusLabel1 + // + this.toolStripStatusLabel1.Name = "toolStripStatusLabel1"; + this.toolStripStatusLabel1.Size = new System.Drawing.Size(13, 17); + this.toolStripStatusLabel1.Text = "v"; + this.toolStripStatusLabel1.Visible = false; + // + // updateTimer + // + this.updateTimer.Enabled = true; + this.updateTimer.Interval = 1000; + this.updateTimer.Tick += new System.EventHandler(this.updateTimer_Tick); + // + // topPanel + // + this.topPanel.BackColor = System.Drawing.SystemColors.Control; + this.topPanel.Controls.Add(this.displaySelectComboBox); + this.topPanel.Controls.Add(this.settingsButton); + this.topPanel.Controls.Add(this.zoomButton); + this.topPanel.Controls.Add(this.cadButton); + this.topPanel.Controls.Add(this.connectButton); + this.topPanel.Dock = System.Windows.Forms.DockStyle.Top; + this.topPanel.Location = new System.Drawing.Point(0, 24); + this.topPanel.Name = "topPanel"; + this.topPanel.Size = new System.Drawing.Size(1227, 32); + this.topPanel.TabIndex = 11; + this.topPanel.Visible = false; + // + // displaySelectComboBox + // + this.displaySelectComboBox.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.displaySelectComboBox.Location = new System.Drawing.Point(383, 5); + this.displaySelectComboBox.Name = "displaySelectComboBox"; + this.displaySelectComboBox.Size = new System.Drawing.Size(128, 21); + this.displaySelectComboBox.TabIndex = 6; + this.displaySelectComboBox.TabStop = false; + this.displaySelectComboBox.Visible = false; + this.displaySelectComboBox.SelectionChangeCommitted += new System.EventHandler(this.displaySelectComboBox_SelectionChangeCommitted); + // + // settingsButton + // + this.settingsButton.Location = new System.Drawing.Point(288, 3); + this.settingsButton.Name = "settingsButton"; + this.settingsButton.Size = new System.Drawing.Size(89, 26); + this.settingsButton.TabIndex = 5; + this.settingsButton.TabStop = false; + this.settingsButton.Text = "Settings"; + this.settingsButton.UseVisualStyleBackColor = true; + this.settingsButton.Click += new System.EventHandler(this.settingsToolStripMenuItem_Click); + // + // zoomButton + // + this.zoomButton.Location = new System.Drawing.Point(193, 3); + this.zoomButton.Name = "zoomButton"; + this.zoomButton.Size = new System.Drawing.Size(89, 26); + this.zoomButton.TabIndex = 4; + this.zoomButton.TabStop = false; + this.zoomButton.Text = "Zoom-to-Fit"; + this.zoomButton.UseVisualStyleBackColor = true; + this.zoomButton.Click += new System.EventHandler(this.zoomButton_Click); + // + // cadButton + // + this.cadButton.Location = new System.Drawing.Point(98, 3); + this.cadButton.Name = "cadButton"; + this.cadButton.Size = new System.Drawing.Size(89, 26); + this.cadButton.TabIndex = 1; + this.cadButton.TabStop = false; + this.cadButton.Text = "Ctrl-Alt-Del"; + this.cadButton.UseVisualStyleBackColor = true; + this.cadButton.Click += new System.EventHandler(this.sendCtrlAltDelToolStripMenuItem_Click); + // + // connectButton + // + this.connectButton.Location = new System.Drawing.Point(3, 3); + this.connectButton.Name = "connectButton"; + this.connectButton.Size = new System.Drawing.Size(89, 26); + this.connectButton.TabIndex = 0; + this.connectButton.TabStop = false; + this.connectButton.Text = "Connect"; + this.connectButton.UseVisualStyleBackColor = true; + this.connectButton.Click += new System.EventHandler(this.MenuItemDisconnect_Click); + // + // resizeKvmControl + // + this.resizeKvmControl.BackColor = System.Drawing.Color.Gray; + this.resizeKvmControl.Dock = System.Windows.Forms.DockStyle.Fill; + this.resizeKvmControl.Location = new System.Drawing.Point(0, 56); + this.resizeKvmControl.Name = "resizeKvmControl"; + this.resizeKvmControl.Size = new System.Drawing.Size(1227, 731); + this.resizeKvmControl.TabIndex = 10; + this.resizeKvmControl.ZoomToFit = false; + this.resizeKvmControl.StateChanged += new System.EventHandler(this.kvmControl_StateChanged); + this.resizeKvmControl.DisplaysReceived += new System.EventHandler(this.resizeKvmControl_DisplaysReceived); + // + // KVMViewer + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.BackColor = System.Drawing.Color.Gray; + this.ClientSize = new System.Drawing.Size(1227, 809); + this.Controls.Add(this.resizeKvmControl); + this.Controls.Add(this.topPanel); + this.Controls.Add(this.mainStatusStrip); + this.Controls.Add(this.mainMenu); + this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); + this.Margin = new System.Windows.Forms.Padding(2); + this.Name = "KVMViewer"; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; + this.Text = "Remote Desktop"; + this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.Main_FormClosing); + this.Load += new System.EventHandler(this.MainForm_Load); + this.Resize += new System.EventHandler(this.MainForm_Resize); + this.mainMenu.ResumeLayout(false); + this.mainMenu.PerformLayout(); + this.mainStatusStrip.ResumeLayout(false); + this.mainStatusStrip.PerformLayout(); + this.topPanel.ResumeLayout(false); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private MenuStrip mainMenu; + private ToolStripMenuItem toolStripMenuItem2; + private ToolStripMenuItem MenuItemConnect; + private ToolStripMenuItem MenuItemExit; + private ToolStripMenuItem MenuItemDisconnect; + private StatusStrip mainStatusStrip; + private ToolStripStatusLabel mainToolStripStatusLabel; + private Timer updateTimer; + private ToolStripSeparator toolStripMenuItem1; + private ToolStripMenuItem viewToolStripMenuItem; + private ToolStripMenuItem statusToolStripMenuItem; + private ToolStripMenuItem debugToolStripMenuItem; + private ToolStripMenuItem settingsToolStripMenuItem; + private ToolStripMenuItem refreshToolStripMenuItem; + private ToolStripMenuItem pauseToolStripMenuItem; + private ToolStripSeparator toolStripMenuItem5; + private KVMResizeControl resizeKvmControl; + private ToolStripStatusLabel toolStripStatusLabel1; + private ToolStripMenuItem zoomtofitToolStripMenuItem; + private ToolStripMenuItem actionsToolStripMenuItem; + private ToolStripMenuItem sendCtrlAltDelToolStripMenuItem; + private Panel topPanel; + private Button connectButton; + private Button cadButton; + private Button zoomButton; + private Button settingsButton; + private ComboBox displaySelectComboBox; + private ToolStripSeparator toolStripMenuItem6; + + + + + + + + + } +} + diff --git a/KVMViewer.cs b/KVMViewer.cs new file mode 100644 index 0000000..d3a6ec6 --- /dev/null +++ b/KVMViewer.cs @@ -0,0 +1,813 @@ +/* +Copyright 2009-2020 Intel Corporation + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +using System; +using System.IO; +using System.Text; +using System.Drawing; +using System.Net.Sockets; +using System.Net.Security; +using System.Windows.Forms; +using System.Collections.Generic; +using System.Security.Cryptography; +using System.Security.Cryptography.X509Certificates; +using Microsoft.Win32; + +namespace MeshCentralRouter +{ + public partial class KVMViewer : Form + { + private KVMControl kvmControl = null; + private MeshCentralServer server = null; + private NodeClass node = null; + private int state = 0; + public webSocketClient wc = null; + + public KVMViewer(MeshCentralServer server, NodeClass node) + { + InitializeComponent(); + this.Text += " - " + node.name; + this.node = node; + this.server = server; + kvmControl = resizeKvmControl.KVM; + kvmControl.parent = this; + resizeKvmControl.ZoomToFit = true; + UpdateStatus(); + this.MouseWheel += MainForm_MouseWheel; + } + + private void Server_onStateChanged(int state) + { + UpdateStatus(); + } + + void MainForm_MouseWheel(object sender, MouseEventArgs e) + { + if (e.Delta == 0) return; + Control c = this.GetChildAtPoint(e.Location); + if (c != null && c == resizeKvmControl) resizeKvmControl.MouseWheelEx(sender, e); + } + + private void MainForm_Load(object sender, EventArgs e) + { + this.Controls.Remove(mainMenu); + this.Size = new Size(820, 480); + resizeKvmControl.CenterKvmControl(false); + topPanel.Visible = true; + } + + public void OnScreenChanged() + { + resizeKvmControl.CenterKvmControl(true); + } + + private void MenuItemExit_Click(object sender, EventArgs e) + { + node.desktopViewer = null; + Close(); + } + + public void MenuItemConnect_Click(object sender, EventArgs e) + { + if (wc != null) return; + state = 1; + string randomId = "abc"; // TODO + Uri u = new Uri(server.wsurl.ToString().Replace("/control.ashx", "/") + "meshrelay.ashx?browser=1&p=2&nodeid=" + node.nodeid + "&id=" + randomId + "&auth=" + server.authCookie); + wc = new webSocketClient(); + wc.Start(this, u, server.wshash); + } + + public void onWebSocketConnected() + { + string randomId = "abc"; // TODO + string u = "*/meshrelay.ashx?p=2&nodeid=" + node.nodeid + "&id=" + randomId + "&rauth=" + server.rauthCookie; + server.sendCommand("{ \"action\": \"msg\", \"type\": \"tunnel\", \"nodeid\": \"" + node.nodeid + "\", \"value\": \"" + u.ToString() + "\", \"usage\": 2 }"); + } + + public void processServerData(string data) { + if ((state == 1) && ((data == "c") || (data == "cr"))) { + state = 3; + kvmControl.Send("2"); + kvmControl.SendCompressionLevel(); + kvmControl.SendPause(false); + kvmControl.SendRefresh(); + UpdateStatus(); + return; + } + if (state != 3) return; + } + + public void processServerBinaryData(byte[] data, int offset, int len) + { + if (state != 3) return; + kvmControl.ProcessData(data, offset, len); + } + + private void MenuItemDisconnect_Click(object sender, EventArgs e) + { + if (wc != null) + { + // Disconnect + state = 0; + wc.Dispose(); + wc = null; + UpdateStatus(); + } else { + // Connect + MenuItemConnect_Click(null, null); + } + } + + + public delegate void UpdateStatusHandler(); + + private void UpdateStatus() + { + if (this.InvokeRequired) { this.Invoke(new UpdateStatusHandler(UpdateStatus)); return; } + + //if (kvmControl == null) return; + switch (state) + { + case 0: // Disconnected + mainToolStripStatusLabel.Text = "Disconnected"; + displaySelectComboBox.Visible = false; + kvmControl.Visible = false; + connectButton.Text = "Connect"; + break; + case 1: // Connecting + mainToolStripStatusLabel.Text = "Connecting..."; + displaySelectComboBox.Visible = false; + kvmControl.Visible = false; + connectButton.Text = "Disconnect"; + break; + case 2: // Setup + mainToolStripStatusLabel.Text = "Setup..."; + displaySelectComboBox.Visible = false; + kvmControl.Visible = false; + connectButton.Text = "Disconnect"; + break; + case 3: // Connected + //string extras = "."; + //if (kvmControl.touchEnabled) extras = ", touch enabled."; + //mainToolStripStatusLabel.Text = string.Format("Connected. {0} tiles received, {1} tiles copied, {2} received, {3} sent{4}", kvmControl.tilecount, kvmControl.tilecopy, MeshUtils.GetKiloShortString((ulong)kvmControl.byterecv), MeshUtils.GetKiloShortString((ulong)kvmControl.bytesent), extras); + mainToolStripStatusLabel.Text = "Connected."; + kvmControl.Visible = true; + connectButton.Text = "Disconnect"; + kvmControl.SendCompressionLevel(); + break; + } + } + + private void updateTimer_Tick(object sender, EventArgs e) + { + UpdateStatus(); + } + + private void Main_FormClosing(object sender, FormClosingEventArgs e) + { + node.desktopViewer = null; + //if (kvmControl != null) kvmControl.Disconnect(); + //controller.OnNodeStateUpdated -= new MeshControl.NodeUpdateHandler(controller_NodeStateUpdated); + //controller.OnPolicyUpdated -= new MeshControl.PolicyUpdateHandler(controller_OnPolicyUpdated); + } + + private void statusToolStripMenuItem_CheckedChanged(object sender, EventArgs e) + { + mainStatusStrip.Visible = statusToolStripMenuItem.Checked; + } + + private void toolStripMenuItem2_DropDownOpening(object sender, EventArgs e) + { + //MenuItemConnect.Enabled = (kvmControl.State == KVMControl.ConnectState.Disconnected); + //MenuItemDisconnect.Enabled = (kvmControl.State != KVMControl.ConnectState.Disconnected); + //serverConnectToolStripMenuItem.Enabled = (server == null && kvmControl.State == KVMControl.ConnectState.Disconnected); + //serviceDisconnectToolStripMenuItem.Enabled = (server != null && server.CurrentState != MeshSwarmServer.State.Disconnected); + } + + private void viewToolStripMenuItem_DropDownOpening(object sender, EventArgs e) + { + debugToolStripMenuItem.Checked = kvmControl.debugmode; + //pauseToolStripMenuItem.Checked = kvmControl.Pause; + } + + private void debugToolStripMenuItem_Click(object sender, EventArgs e) + { + if (kvmControl != null) kvmControl.debugmode = debugToolStripMenuItem.Checked; + } + + private void kvmControl_StateChanged(object sender, EventArgs e) + { + UpdateStatus(); + } + + private void settingsToolStripMenuItem_Click(object sender, EventArgs e) + { + if (kvmControl == null) return; + using (KVMSettingsForm form = new KVMSettingsForm()) + { + form.Compression = kvmControl.CompressionLevel; + form.Scaling = kvmControl.ScalingLevel; + form.FrameRate = kvmControl.FrameRate; + if (form.ShowDialog(this) == System.Windows.Forms.DialogResult.OK) { + kvmControl.SetCompressionParams(form.Compression, form.Scaling, form.FrameRate); + } + } + } + + private void MainForm_Resize(object sender, EventArgs e) + { + if (kvmControl != null) kvmControl.SendPause(WindowState == FormWindowState.Minimized); + } + + private void zoomtofitToolStripMenuItem_CheckStateChanged(object sender, EventArgs e) + { + resizeKvmControl.ZoomToFit = zoomtofitToolStripMenuItem.Checked; + } + + private void sendCtrlAltDelToolStripMenuItem_Click(object sender, EventArgs e) + { + if (kvmControl != null) kvmControl.SendCtrlAltDel(); + } + + private void debugButton_Click(object sender, EventArgs e) + { + if (kvmControl != null) kvmControl.debugmode = !kvmControl.debugmode; + } + + private void zoomButton_Click(object sender, EventArgs e) + { + resizeKvmControl.ZoomToFit = !resizeKvmControl.ZoomToFit; + } + + private void resizeKvmControl_DisplaysReceived(object sender, EventArgs e) + { + if (kvmControl == null || kvmControl.displays.Count == 0) return; + + if (kvmControl.displays.Count > 0) + { + displaySelectComboBox.Visible = true; + displaySelectComboBox.Items.Clear(); + displaySelectComboBox.Items.AddRange(kvmControl.displays.ToArray()); + if (kvmControl.currentDisp == 0xFFFF) + { + displaySelectComboBox.SelectedItem = "All Displays"; + } + else + { + displaySelectComboBox.SelectedItem = "Display " + kvmControl.currentDisp; + } + } + else + { + displaySelectComboBox.Visible = false; + displaySelectComboBox.Items.Clear(); + } + } + + private void displaySelectComboBox_SelectionChangeCommitted(object sender, EventArgs e) + { + string displayText = displaySelectComboBox.SelectedItem.ToString(); + int displaynum = 0; + if (displayText == "All Displays") displaynum = 0xFFFF; + if (displaynum != 0 || int.TryParse(displayText.Substring(8), out displaynum)) + { + //if (kvmControl != null) kvmControl.SendDisplay(displaynum); + } + } + + private void resizeKvmControl_TouchEnabledChanged(object sender, EventArgs e) + { + UpdateStatus(); + } + + private void commandsToolStripMenuItem_CheckStateChanged(object sender, EventArgs e) + { + //if (server == null) return; + //if (commandsToolStripMenuItem.Checked) server.ShowCommandViewer(); else server.HideCommandViewer(); + } + + private void emulateTouchToolStripMenuItem_CheckedChanged(object sender, EventArgs e) + { + //kvmControl.emulateTouch = emulateTouchToolStripMenuItem.Checked; + } + + private void packetsToolStripMenuItem_CheckStateChanged(object sender, EventArgs e) + { + //kvmControl.ShowPackets(packetsToolStripMenuItem.Checked); + } + + private void kVMCommandsToolStripMenuItem_Click(object sender, EventArgs e) + { + //kvmControl.ShowCommands(kVMCommandsToolStripMenuItem.Checked); + } + + private void winButton_Click(object sender, EventArgs e) + { + kvmControl.SendWindowsKey(); + } + + private void charmButton_Click(object sender, EventArgs e) + { + //kvmControl.SendCharmsKey(); + } + + public static void saveToRegistry(string name, string value) + { + try { Registry.SetValue(@"HKEY_CURRENT_USER\SOFTWARE\OpenSource\MeshRouter", name, value); } catch (Exception) { } + } + public static string loadFromRegistry(string name) + { + try { return Registry.GetValue(@"HKEY_CURRENT_USER\SOFTWARE\OpenSource\MeshRouter", name, "").ToString(); } catch (Exception) { return ""; } + } + + public static string GetProxyForUrlUsingPac(string DestinationUrl, string PacUri) + { + IntPtr WinHttpSession = Win32Api.WinHttpOpen("User", Win32Api.WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, IntPtr.Zero, IntPtr.Zero, 0); + + Win32Api.WINHTTP_AUTOPROXY_OPTIONS ProxyOptions = new Win32Api.WINHTTP_AUTOPROXY_OPTIONS(); + Win32Api.WINHTTP_PROXY_INFO ProxyInfo = new Win32Api.WINHTTP_PROXY_INFO(); + + ProxyOptions.dwFlags = Win32Api.WINHTTP_AUTOPROXY_CONFIG_URL; + ProxyOptions.dwAutoDetectFlags = (Win32Api.WINHTTP_AUTO_DETECT_TYPE_DHCP | Win32Api.WINHTTP_AUTO_DETECT_TYPE_DNS_A); + ProxyOptions.lpszAutoConfigUrl = PacUri; + + // Get Proxy + bool IsSuccess = Win32Api.WinHttpGetProxyForUrl(WinHttpSession, DestinationUrl, ref ProxyOptions, ref ProxyInfo); + Win32Api.WinHttpCloseHandle(WinHttpSession); + + if (IsSuccess) + { + return ProxyInfo.lpszProxy; + } + else + { + Console.WriteLine("Error: {0}", Win32Api.GetLastError()); + return null; + } + } + + public class webSocketClient : IDisposable + { + private KVMViewer parent = null; + private TcpClient wsclient = null; + private SslStream wsstream = null; + private NetworkStream wsrawstream = null; + private int state = 0; + private Uri url = null; + private byte[] readBuffer = new Byte[500]; + private int readBufferLen = 0; + private int accopcodes = 0; + private bool accmask = false; + private int acclen = 0; + private bool proxyInUse = false; + private string user = null; + private string pass = null; + private string token = null; + public bool xdebug = false; + public bool xignoreCert = false; + + public void Dispose() + { + try { wsstream.Close(); } catch (Exception) { } + try { wsstream.Dispose(); } catch (Exception) { } + wsstream = null; + wsclient = null; + state = -1; + } + + public void Debug(string msg) { if (xdebug) { try { File.AppendAllText("debug.log", "Debug-" + msg + "\r\n"); } catch (Exception) { } } } + + public bool Start(KVMViewer parent, Uri url, string fingerprint) + { + if (state != 0) return false; + state = 1; + this.url = url; + this.parent = parent; + Uri proxyUri = null; + + // Check if we need to use a HTTP proxy (Auto-proxy way) + try + { + RegistryKey registryKey = Registry.CurrentUser.OpenSubKey("Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings", true); + Object x = registryKey.GetValue("AutoConfigURL", null); + if ((x != null) && (x.GetType() == typeof(string))) + { + string proxyStr = GetProxyForUrlUsingPac("http" + ((url.Port == 80) ? "" : "s") + "://" + url.Host + ":" + url.Port, x.ToString()); + if (proxyStr != null) { proxyUri = new Uri("http://" + proxyStr); } + } + } + catch (Exception) { proxyUri = null; } + + // Check if we need to use a HTTP proxy (Normal way) + if (proxyUri == null) + { + var proxy = System.Net.HttpWebRequest.GetSystemWebProxy(); + proxyUri = proxy.GetProxy(url); + if ((url.Host.ToLower() == proxyUri.Host.ToLower()) && (url.Port == proxyUri.Port)) { proxyUri = null; } + } + + if (proxyUri != null) + { + // Proxy in use + proxyInUse = true; + wsclient = new TcpClient(); + wsclient.BeginConnect(proxyUri.Host, proxyUri.Port, new AsyncCallback(OnConnectSink), this); + } + else + { + // No proxy in use + proxyInUse = false; + wsclient = new TcpClient(); + wsclient.BeginConnect(url.Host, url.Port, new AsyncCallback(OnConnectSink), this); + } + return true; + } + + private void OnConnectSink(IAsyncResult ar) + { + if (wsclient == null) return; + + // Accept the connection + try + { + wsclient.EndConnect(ar); + } + catch (Exception ex) + { + Debug("Websocket TCP failed to connect: " + ex.ToString()); + Dispose(); + return; + } + + if (proxyInUse == true) + { + // Send proxy connection request + wsrawstream = wsclient.GetStream(); + byte[] proxyRequestBuf = UTF8Encoding.UTF8.GetBytes("CONNECT " + url.Host + ":" + url.Port + " HTTP/1.1\r\nHost: " + url.Host + ":" + url.Port + "\r\n\r\n"); + wsrawstream.Write(proxyRequestBuf, 0, proxyRequestBuf.Length); + wsrawstream.BeginRead(readBuffer, readBufferLen, readBuffer.Length - readBufferLen, new AsyncCallback(OnProxyResponseSink), this); + } + else + { + // Start TLS connection + Debug("Websocket TCP connected, doing TLS..."); + wsstream = new SslStream(wsclient.GetStream(), false, VerifyServerCertificate, null); + wsstream.BeginAuthenticateAsClient(url.Host, null, System.Security.Authentication.SslProtocols.Tls12, false, new AsyncCallback(OnTlsSetupSink), this); + } + } + + private void OnProxyResponseSink(IAsyncResult ar) + { + if (wsrawstream == null) return; + + int len = 0; + try { len = wsrawstream.EndRead(ar); } catch (Exception) { } + if (len == 0) + { + // Disconnect + Debug("Websocket proxy disconnected, length = 0."); + Dispose(); + return; + } + + readBufferLen += len; + string proxyResponse = UTF8Encoding.UTF8.GetString(readBuffer, 0, readBufferLen); + if (proxyResponse.IndexOf("\r\n\r\n") >= 0) + { + // We get a full proxy response, we should get something like "HTTP/1.1 200 Connection established\r\n\r\n" + if (proxyResponse.StartsWith("HTTP/1.1 200 ")) + { + // All good, start TLS setup. + readBufferLen = 0; + Debug("Websocket TCP connected, doing TLS..."); + wsstream = new SslStream(wsrawstream, false, VerifyServerCertificate, null); + wsstream.BeginAuthenticateAsClient(url.Host, null, System.Security.Authentication.SslProtocols.Tls12, false, new AsyncCallback(OnTlsSetupSink), this); + } + else + { + // Invalid response + Debug("Proxy connection failed: " + proxyResponse); + Dispose(); + } + } + else + { + if (readBufferLen == readBuffer.Length) + { + // Buffer overflow + Debug("Proxy connection failed"); + Dispose(); + } + else + { + // Read more proxy data + wsrawstream.BeginRead(readBuffer, readBufferLen, readBuffer.Length - readBufferLen, new AsyncCallback(OnProxyResponseSink), this); + } + } + } + + public string Base64Encode(string plainText) + { + var plainTextBytes = System.Text.Encoding.UTF8.GetBytes(plainText); + return System.Convert.ToBase64String(plainTextBytes); + } + + public string Base64Decode(string base64EncodedData) + { + var base64EncodedBytes = System.Convert.FromBase64String(base64EncodedData); + return System.Text.Encoding.UTF8.GetString(base64EncodedBytes); + } + + private void OnTlsSetupSink(IAsyncResult ar) + { + if (wsstream == null) return; + + // Accept the connection + try + { + wsstream.EndAuthenticateAsClient(ar); + } + catch (Exception ex) + { + // Disconnect + MessageBox.Show(ex.Message, "MeshRouter"); + Debug("Websocket TLS failed: " + ex.ToString()); + Dispose(); + return; + } + + // Fetch remote certificate + //parent.wshash = wsstream.RemoteCertificate.GetCertHashString(); + + // Setup extra headers if needed + string extraHeaders = ""; + if (user != null && pass != null && token != null) { extraHeaders = "x-meshauth: " + Base64Encode(user) + "," + Base64Encode(pass) + "," + Base64Encode(token) + "\r\n"; } + else if (user != null && pass != null) { extraHeaders = "x-meshauth: " + Base64Encode(user) + "," + Base64Encode(pass) + "\r\n"; } + + // Send the HTTP headers + Debug("Websocket TLS setup, sending HTTP header..."); + string header = "GET " + url.PathAndQuery + " HTTP/1.1\r\nHost: " + url.Host + "\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\nSec-WebSocket-Version: 13\r\n" + extraHeaders + "\r\n"; + wsstream.Write(UTF8Encoding.UTF8.GetBytes(header)); + + // Start receiving data + wsstream.BeginRead(readBuffer, readBufferLen, readBuffer.Length - readBufferLen, new AsyncCallback(OnTlsDataSink), this); + } + + private void OnTlsDataSink(IAsyncResult ar) + { + if (wsstream == null) return; + + int len = 0; + try { len = wsstream.EndRead(ar); } catch (Exception) { } + if (len == 0) + { + // Disconnect + Debug("Websocket disconnected, length = 0."); + Dispose(); + return; + } + //parent.Debug("#" + counter + ": Websocket got new data: " + len); + readBufferLen += len; + + // Consume all of the data + int consumed = 0; + int ptr = 0; + do + { + consumed = ProcessBuffer(readBuffer, ptr, readBufferLen - ptr); + if (consumed < 0) { Dispose(); return; } // Error, close the connection + ptr += consumed; + } while ((consumed > 0) && ((readBufferLen - consumed) > 0)); + + // Move the data forward + if ((ptr > 0) && (readBufferLen - ptr) > 0) + { + //Console.Write("MOVE FORWARD\r\n"); + Array.Copy(readBuffer, ptr, readBuffer, 0, (readBufferLen - ptr)); + } + readBufferLen = (readBufferLen - ptr); + + // If the buffer is too small, double the size here. + if (readBuffer.Length - readBufferLen == 0) + { + Debug("Increasing the read buffer size from " + readBuffer.Length + " to " + (readBuffer.Length * 2) + "."); + byte[] readBuffer2 = new byte[readBuffer.Length * 2]; + Array.Copy(readBuffer, 0, readBuffer2, 0, readBuffer.Length); + readBuffer = readBuffer2; + } + + // Receive more data + try { wsstream.BeginRead(readBuffer, readBufferLen, readBuffer.Length - readBufferLen, new AsyncCallback(OnTlsDataSink), this); } catch (Exception) { } + } + + private int ProcessBuffer(byte[] buffer, int offset, int len) + { + string ss = UTF8Encoding.UTF8.GetString(buffer, offset, len); + + if (state == 1) + { + // Look for the end of the http header + string header = UTF8Encoding.UTF8.GetString(buffer, offset, len); + int i = header.IndexOf("\r\n\r\n"); + if (i == -1) return 0; + Dictionary parsedHeader = ParseHttpHeader(header.Substring(0, i)); + if ((parsedHeader == null) || (parsedHeader["_Path"] != "101")) { Debug("Websocket bad header."); return -1; } // Bad header, close the connection + Debug("Websocket got setup upgrade header."); + state = 2; + this.parent.onWebSocketConnected(); + return len; // TODO: Technically we need to return the header length before UTF8 convert. + } + else if (state == 2) + { + // Parse a websocket fragment header + if (len < 2) return 0; + int headsize = 2; + accopcodes = buffer[offset]; + accmask = ((buffer[offset + 1] & 0x80) != 0); + acclen = (buffer[offset + 1] & 0x7F); + + if ((accopcodes & 0x0F) == 8) + { + // Close the websocket + Debug("Websocket got closed fragment."); + return -1; + } + + if (acclen == 126) + { + if (len < 4) return 0; + headsize = 4; + acclen = (buffer[offset + 2] << 8) + (buffer[offset + 3]); + } + else if (acclen == 127) + { + if (len < 10) return 0; + headsize = 10; + acclen = (buffer[offset + 6] << 24) + (buffer[offset + 7] << 16) + (buffer[offset + 8] << 8) + (buffer[offset + 9]); + Debug("Websocket receive large fragment: " + acclen); + } + if (accmask == true) + { + // TODO: Do unmasking here. + headsize += 4; + } + //parent.Debug("#" + counter + ": Websocket frag header - FIN: " + ((accopcodes & 0x80) != 0) + ", OP: " + (accopcodes & 0x0F) + ", LEN: " + acclen + ", MASK: " + accmask); + state = 3; + return headsize; + } + else if (state == 3) + { + // Parse a websocket fragment data + if (len < acclen) return 0; + //Console.Write("WSREAD: " + acclen + "\r\n"); + ProcessWsBuffer(buffer, offset, acclen, accopcodes); + state = 2; + return acclen; + } + return 0; + } + + private void ProcessWsBuffer(byte[] data, int offset, int len, int op) + { + bool binary = ((op & 1) == 0); + + Debug("Websocket got data."); + //try { parent.processServerData(UTF8Encoding.UTF8.GetString(data, offset, len)); } catch (Exception ex) { } + + if (binary == false) { + parent.processServerData(UTF8Encoding.UTF8.GetString(data, offset, len)); + } else { + parent.processServerBinaryData(data, offset, len); + } + } + + private Dictionary ParseHttpHeader(string header) + { + string[] lines = header.Replace("\r\n", "\r").Split('\r'); + if (lines.Length < 2) { return null; } + string[] directive = lines[0].Split(' '); + Dictionary values = new Dictionary(); + values["_Action"] = directive[0]; + values["_Path"] = directive[1]; + values["_Protocol"] = directive[2]; + for (int i = 1; i < lines.Length; i++) + { + var j = lines[i].IndexOf(":"); + values[lines[i].Substring(0, j).ToLower()] = lines[i].Substring(j + 1).Trim(); + } + return values; + } + + // Return a modified base64 SHA384 hash string of the certificate public key + public static string GetMeshKeyHash(X509Certificate cert) + { + return ByteArrayToHexString(new SHA384Managed().ComputeHash(cert.GetPublicKey())); + } + + // Return a modified base64 SHA384 hash string of the certificate + public static string GetMeshCertHash(X509Certificate cert) + { + return ByteArrayToHexString(new SHA384Managed().ComputeHash(cert.GetRawCertData())); + } + + public static string ByteArrayToHexString(byte[] Bytes) + { + StringBuilder Result = new StringBuilder(Bytes.Length * 2); + string HexAlphabet = "0123456789ABCDEF"; + foreach (byte B in Bytes) { Result.Append(HexAlphabet[(int)(B >> 4)]); Result.Append(HexAlphabet[(int)(B & 0xF)]); } + return Result.ToString(); + } + + private bool VerifyServerCertificate(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors) + { + return (parent.server.certHash == GetMeshKeyHash(certificate)); + } + + public void WriteStringWebSocket(string data) + { + if (state < 2) return; + + // Convert the string into a buffer with 4 byte of header space. + int len = UTF8Encoding.UTF8.GetByteCount(data); + byte[] buf = new byte[4 + len]; + UTF8Encoding.UTF8.GetBytes(data, 0, data.Length, buf, 4); + len = buf.Length - 4; + + // Check that everything is ok + if ((state < 2) || (len < 1) || (len > 65535)) { Dispose(); return; } + + //Console.Write("Length: " + len + "\r\n"); + //System.Threading.Thread.Sleep(0); + + if (len < 126) + { + // Small fragment + buf[2] = 129; // Fragment op code (129 = text, 130 = binary) + buf[3] = (byte)(len & 0x7F); + //try { wsstream.BeginWrite(buf, 2, len + 2, new AsyncCallback(WriteWebSocketAsyncDone), args); } catch (Exception) { Dispose(); return; } + wsstream.Write(buf, 2, len + 2); + } + else + { + // Large fragment + buf[0] = 129; // Fragment op code (129 = text, 130 = binary) + buf[1] = 126; + buf[2] = (byte)((len >> 8) & 0xFF); + buf[3] = (byte)(len & 0xFF); + //try { wsstream.BeginWrite(buf, 0, len + 4, new AsyncCallback(WriteWebSocketAsyncDone), args); } catch (Exception) { Dispose(); return; } + wsstream.Write(buf, 0, len + 4); + } + } + + public void WriteBinaryWebSocket(byte[] data, int offset, int len) + { + if (state < 2) return; + + // Convert the string into a buffer with 4 byte of header space. + byte[] buf = new byte[4 + len]; + Array.Copy(data, offset, buf, 4, len); + len = buf.Length - 4; + + // Check that everything is ok + if ((state < 2) || (len < 1) || (len > 65535)) { Dispose(); return; } + + //Console.Write("Length: " + len + "\r\n"); + //System.Threading.Thread.Sleep(0); + + if (len < 126) + { + // Small fragment + buf[2] = 130; // Fragment op code (129 = text, 130 = binary) + buf[3] = (byte)(len & 0x7F); + //try { wsstream.BeginWrite(buf, 2, len + 2, new AsyncCallback(WriteWebSocketAsyncDone), args); } catch (Exception) { Dispose(); return; } + wsstream.Write(buf, 2, len + 2); + } + else + { + // Large fragment + buf[0] = 130; // Fragment op code (129 = text, 130 = binary) + buf[1] = 126; + buf[2] = (byte)((len >> 8) & 0xFF); + buf[3] = (byte)(len & 0xFF); + //try { wsstream.BeginWrite(buf, 0, len + 4, new AsyncCallback(WriteWebSocketAsyncDone), args); } catch (Exception) { Dispose(); return; } + wsstream.Write(buf, 0, len + 4); + } + } + + } + + } +} diff --git a/KVMViewer.resx b/KVMViewer.resx new file mode 100644 index 0000000..8e8cd76 --- /dev/null +++ b/KVMViewer.resx @@ -0,0 +1,555 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 17, 17 + + + 127, 17 + + + 264, 17 + + + + + AAABAAkAEBAQAAEABAAoAQAAlgAAABAQAAABAAgAaAUAAL4BAAAgIBAAAQAEAOgCAAAmBwAAICAAAAEA + CACoCAAADgoAADAwEAABAAQAaAYAALYSAAAwMAAAAQAIAKgOAAAeGQAAEBAAAAEAIABoBAAAxicAACAg + AAABACAAqBAAAC4sAAAwMAAAAQAgAKglAADWPAAAKAAAABAAAAAgAAAAAQAEAAAAAACAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAIAAAIAAAACAgACAAAAAgACAAICAAACAgIAAwMDAAAAA/wAA/wAAAP//AP8A + AAD/AP8A//8AAP///wAACIiHh4eAAAAPiIh4eAAAAAAIh3eAAACIiIiIiIh4eI+IiIiIiIiIiEREREZE + Z4iIRGRMfIyMiIhlxMTIeMeIiFZEbHjIjoiIbExMiIiHiIhWxsyIiIiIiExMx4iIiIiITGzIj4+IiIhM + TM6I6OiIiHiIiIeIiIgI+IiIiIiIiOAHAADgDwAA+B8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAoAAAAEAAAACAAAAABAAgAAAAAAAABAAAAAAAAAAAAAAAB + AAAAAQAAAAAAAIAAAACGCwkAlAAAAJQNAgCfEQAAnBsQAJgdEQCiDgAAqQ0AAKgSAACqFQAApxkNAKUc + DwCyEgAAsRUAALQXAAC/FgIAuBgAAL0YAACaJhwArCcVAKMnHQC7Mh8AmDAoAJ42LACqMCMApTctAKU+ + MwDAGwAAwRwAAMUdAADKGgAAyh4FANIbAADdGQAA4RoAAOcaAADRIQAA1SMAAPEnCQDkMxMAvkwqALpI + OADMSjYA81A1AKVIQACtSkEAr1ZMAM5dTADUXUgA1GZKANhgSwDdYUwA3WpXAON5ZgDgfWsA6HxoAPp9 + aADmgWgA7IBrAPiKawDqi3oA8JtyAPahdwD6qH8AiYuLAIyOjgCXmpoAmp2dAJ6fnwCeoaEAoKGhAKOj + pACipKQApKamAKaoqQCoqqoAqaytAKyurwCvsbEAsbS1ALO3uAC1uLkAtru8ALm+vgDvk4IA7ZuNAPOX + hQDzn4oA9ZyLAPuuiQD3qpsA+q6eAPu0kQD0saYA+7KjAPi1oAD+sqQA+7+iAP7DswD6xrQA/8a2AP7G + ugD/yboA+s2+ALzBwQC+xMQAvsvNAL/MzQDFyMkAwczNAMnMzADDztAAxM7QAMbQ0QDJ0dIAytLUAMzT + 1QDN1NUA1NbXANjY2ADc3NwA/9LGAP/UxgD/1cgA/9rLAP/i2QD/5twA/+nfAN7h4QDo6OgA/+rgAP/r + 5gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAA////AAAAAFVVUk1GRkZGRkYAAAAAAACDeXduU0tGTUYAAAAAAAAAAAB5T0RDQ24AAAAAAIJr + a1VVU1JPT09NTEtLRm53d3J0cnNuc3JucW1tbW1Ld08BAQIBAQMVFRUVFSttS3dNBxQHBAUKFzEyNDUq + bUt3TRgYBwoKDyw4Nzk8NG1Ld0wuHAcPDxE2V1ZYWjtuTXdLMBsIDxIhPl9cXWJZc013Sy8WDxIiKWJn + ZGRoX21Pd0saCR0nJC17fXt+fmVtT3dGDRAdJyU6hX9/hIRoc093SwwRISMoPWNeW0FAP3NSd0ZGRktL + S01PT09SUlNzUgB3d3d3d3d3d3d3c3dzcnjgBwAA4A8AAPgfAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAAAAKAAAACAAAABAAAAAAQAEAAAAAAAAAgAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAIAAAIAAAACAgACAAAAAgACAAICAAACAgIAAwMDAAAAA/wAA/wAAAP//AP8A + AAD/AP8A//8AAP///wAAAAAAAAAAj4AAAAAAAAAAAAAAAAAIj4iIiAAAAAAAAAAAAAAIj4+Pj4iIAAAA + AAAAAAAAiIj4+I+IiIcAAAAAAAAACIiIj4iIiIh4cAAAAAAAAAiIiPiPiIiHeAAAAAAAAAAIiIiIiIiH + d4cAAAAAAAAAAAiIiId3d3eIiIAAAAAAAAAAh3d3d3eIiIiAAAAAAAAAAACHd3iIiIiIgAAAAAAAAIeH + iIiIh8fEeHAAAAAId4iIiId3xExETH+AAAAAeIiId3RExExsbGx4gAAAD3d3RERERExnx8fHz3AAAACE + RERETExEx8fIx8+IAAAAhERkTERGxnyMjI54iAAAAIR8VkRGxEyMjnjIyIgAAACHRkxExEzGh3fIjIiI + AAAAh0d8RkxsTIeIh4joiAAAAIfHx0xExMiHd46HiIgAAACIR3xExsbHiI6IiHiHAAAAiEjERMTMyI6I + iIiIiAAAAAhHxMbGzGiIiIiIiIiAAAAIbEZMzGzPiIiIiIiIgAAACFZMTGzMj4+Pj4+IiIAAAAhsTGxM + bIj4+Pj/+PiAAAAIVETEzMz/+P+Pj4iIgAAACHTGzGzOiIiIiIiIj3AAAAiETEzGx4iIiIiI+PgAAAAI + hHd3iIiIj4//AAAAAAAACIiIj4+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/8f///4A///4AD//8A + AP/+AAB//gAA//4AAP//gAAf/8AAH//wAB//AAAf4AAAH8AAAB+AAAAfwAAAD8AAAA/AAAAPwAAAD8AA + AA/AAAAPwAAAD8AAAA/gAAAH4AAAB+AAAAfgAAAH4AAAB+AAAAfgAAAP4AAD/+AH////////KAAAACAA + AABAAAAAAQAIAAAAAAAABAAAAAAAAAAAAAAAAQAAAAEAAAAAAAB+AQAAZmdmAGlpagB2dnYAfX1+AIIA + AACMAgAAhQsCAIwMAQCSAAAAmAcAAJMLAQCbDgAAkwwIAIsUCgCUEgUAnRAAAJIYDACLHBQAkB4WAJoe + EgCmAAAAqgYAAKELAAC5CAAApBIAAKsUAACqGAYArBwJALIVAAC5EwAAsxgBALsZAACNJBwAkyQcAKYg + DwC2IA0AoCATAKwjEQCgJxoAsy0bALcxHgCSJiAAliskAJYvKACfMCUAmjIqAI85NACZNjAAmz01AJc+ + OAC0MiEAvDUhAL0/LQDFEwAAwhsAAMsdAADQHAAA2BwAAO8UAADkGwAA6x0AANMgAADbIgAA2iQMAMAs + FQDlIQAA5S4LAPIqAwDhLhgA9j8dAMs8JQDOPygAmUQ8AKhANgCgQTkAr0U6AMNCLwDDRTIAyUYyAMxI + MwDBTD8AzVI/ANZROQDaVj8A8UQiAP5UMgCZU00Al1RRAKRKRACuVUwAsVJHALBUSgCiWlUArFhRALFc + VQCzZF0AmmtqALFvZgC5a2QAr3FtALdwawC+dWsAoHVyALZ4cQDHU0IAzF1MANFWQQDVWUQA2FpFANVe + SAD1X0IAxGlaANVlUgDcZlIA3WhUANhuXQDiY0kA4mZQAOJqVADpcFoAxnRoAN10YgDjd2QA5nllAOp8 + ZgDyfGYA6IBtAPqFbgDjhHQA6YZ0AOyIdgDuingA8Y16APqNeQCDhIQAi4yOAIuOkgCOlZoAlZWVAJSV + mACWm5wAnZ2dAKGFhQCzhIEAp5KSAKCfnwCqnZ4AspOTAL2VkwC5nJsAuaGcAJeepACVoaYAnKCmAJit + qgCfrrEAoqKiAKukpwCmpqgApqmrAKurqwCyp6kAsKqsAKyssAClsLMArbKzAKS6tgCttbkAs7O0ALW5 + vAC6ursA0ZWSAO2WhgDrmIoA8piHAPWbigD7nowAy6KcAOKqnADtrJ0A8qOVAPikkwDwqZ0A/KubAPG1 + ngD/sJ0A9bieAMCtrADbtKoAwre0AMq1sQDFuLYAwbu9ANG1sADbuLMA4K6jAOu8pwDgtKgA57+tAPWw + ogD7tKUA+b+mAPS3qwD+vK0A/r+xANXCvgD1wKoA+8euAOjGuAD+xLIA/sm1AP7EuQD9y7sAvb/DAMG/ + wQC3wcQAvMLDALnGyAC+y8wAw8PDAMTFyADDyswAy8vLANXKxgDSzcsA2M/LAMLP0ADMz9AAx9XWAMzT + 1ADK1tgA09PTANLW2ADU2twA29zcAOzPwgD/zsEA/9LDAP/VyQD/2coA5NvXAOvc1gDs3tgA/93SAP/h + 1AD/5NoA/+neANvf4ADe4OIA4+TkAObp6gD/6+QA//HrAP///wAAAAAAAAAAAAAAAAAAAOzs5QAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAA6enp6erl5d2sAAAAAAAAAAAAAAAAAAAAAAAAAAAA3enp7Oz66uXy + 7NjdrAAAAAAAAAAAAAAAAAAAAAAAAN3g4Onp7Prj4OzgrOCqrIwAAAAAAAAAAAAAAAAAAACs3d3d4Or6 + +ung6d3dqqKTp4wAAAAAAAAAAAAAAAAAAKqsrN3d6enp4N3drKqgjIyiAAAAAAAAAAAAAAAAAAAA7Kzd + rN3d4ODdrKqTiIiJk48AAAAAAAAAAAAAAAAAAAAAAKTYqqSioIyIBAIDiKCqqqysqgAAAAAAAAAAAAAA + AAAAAKCMoIyIBAWIjaKn2dzm5sKiAAAAAAAAAAAAAAAAAAAAAACgiYmKi6fb5OXgw63A3qAAAAAAAAAA + AAAAAAAAoqCbmZmdp9nCwbN6UikeFmnmkwAAAAAAAAAAoo6LoavZ29msvZZmTSccGhsbHScgZ+igAAAA + AAAAAIqcqNmswpdlWisMBwoKJCk0NCoqNUJx5qAAAAAAAAD6oJWRXisGBgEGBwwNERs0NjZOUFBRSWvm + ogAAAAAAAACfAQYJCAgICAkMEREbHE9qU2xsbm5Vb+Wq4AAAAAAAAKUTEyMTEw8JDBERHBsla3Jyc3R4 + eHh34dmsAAAAAAAAqjAjLSsjEgwRERsbHkJ1e3t8fX1+fnnP2aQAAAAAAACrWSMyMS4MDBocGx4fSIKC + g4OEhYaGf8TkoAAAAAAAANliLFpaLgwRHBsgITdUr66usLCxsbKHxeaTAAAAAAAA5JAsX1sVDRobGyE4 + N3O4tra2t7m5ubK466AAAAAAAADllC1hWw0NGxsgODg6gMzJycnJzcnNu7XskwAAAAAAAOqjLmZLCxob + ITg5O0Sy1c7OztTO1tbOvPmgAAAAAAAAAKQxZCYYHiE4P0A9Vs7v1u/u7u7u8e+87KDdAAAAAAAAqjNc + GBseODk/QD5w8PDw7vHx8fHx9svyp6wAAAAAAACrSigYGyE4OUBDPoH39fX19vb29/j41PSsqgAAAAAA + AKwzDBoeITg/QENFuf339/f3+Pj9/f3x9OWgAAAAAAAArFkOGx4hOT9DQ0fw/v39/f39/fj499Ht+ZMA + AAAAAADZaA4bIDg5QD08V/X38PDW1tTQxsjS0vT8ogAAAAAAAN6SDhcZGTc6QUZ2tMfHxM/l4+np6vn5 + /PsAAAAAAAAA4J8jSmBjkZjB3eTl6Orq7Ozs7AAAAAAAAAAAAAAAAADprNnk5erq6+v6AAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//x////gD///gAP//wAA//4A + AH/+AAD//gAA//+AAB//wAAf//AAH/8AAB/gAAAfwAAAH4AAAB/AAAAPwAAAD8AAAA/AAAAPwAAAD8AA + AA/AAAAPwAAAD+AAAAfgAAAH4AAAB+AAAAfgAAAH4AAAB+AAAA/gAAP/4Af///////8oAAAAMAAAAGAA + AAABAAQAAAAAAIAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAgAAAAICAAIAAAACAAIAAgIAAAICA + gADAwMAAAAD/AAD/AAAA//8A/wAAAP8A/wD//wAA////AAAAAAAAAAAAAAAIiIgAAAAAAAAAAAAAAAAA + AAAAAAAAiIj//4+IiIAAAAAAAAAAAAAAAAAAAACI+P+IiIj4iIiAAAAAAAAAAAAAAAAAAIiIj4j4+I+P + iIiIcAAAAAAAAAAAAAAACIiIiPj4j4j4iIh4hwAAAAAAAAAAAAAAiIiPiPiPiIj4iIiIeAAAAAAAAAAA + AAAAiIiIiI+Pj4iIiIeHeHAAAAAAAAAAAAAAiIiIj4j4iIiIiHh3iHAAAAAAAAAAAAAAiIiIiIiIiIiI + eHeHeAAAAAAAAAAAAAAACIeIiIiIiIiHd3d4hwAAAAAAAAAAAAAAAIiIiIiIiHd3d3eHiIiIAAAAAAAA + AAAAAAB4d3h3d3d3d3iIiIiHAAAAAAAAAAAAAAAAh4d3d3d3iIiIiIiIAAAAAAAAAAAAAAAAAIh3eHeH + h4iIiIiHAAAAAAAAAAAAAAAIh3d4d3iIiIh8xHiIgAAAAAAAAAAAiIeHd4eHiIh4xMTEbGiIgAAAAAAA + CHh4d4d4iIiHx0xMRsZMRHiIgAAAAAB4d3eHiIiIdWRERETGTFxlxs+IcAAAAAB3eIiIh3RERETETGx8 + fGXGx8iIgAAAAAB4iHdERERERMRExGXGx8bHx8iIcAAAAACHRERERERExERsRsfHx8fIx8iIgAAAAACH + REREREREZExExHx8fIyM7HiIgAAAAACIRGVkZExkxEbEbHyGfIbId8iPcAAAAACIRWxFxGRERMRMTH7I + yMjsjI6IgAAAAAD4dEdGR0TETGxGx8h8jnyHeMiIiAAAAAAIdMZcdMREbETExoyIyHjIyHz4iAAAAAAI + dHx0bERMRMbEx4d3h3iI6HiIhwAAAAAIdGVlxEZExkxMbIh+jIfId4yPiAAAAAAIhHx8dExMbExsyIyI + iIiIh4iIhwAAAAAIhHfHbERkxMTGyIiHeMjoh4yPhwAAAAAIhsd8RGxMRsbMeOiIiIiIeIiIiAAAAAAP + hXyHRMRMTExMyIh46IiIiI6IiAAAAAAAh8hsTETGxszGiIiIiIiIiIiI94AAAAAAh2d8RsbEzGzMiIiI + iIiIiIiIiIAAAAAAh8jERMTGzMbOiIiIiIiIiIjo94AAAAAAh8dMTExMbMzMiIiIj4j4j4+I94AAAAAA + iGXGRGxsxsbIj4+Pj4iPiPiI+HAAAAAA+EREzEzEzMzI+I+I+P+P+PiI+HAAAAAAiExsRsbMbGzoj4+P + j4j4+PiI+IAAAAAACHRExMTGzMzP+P+Pj4+Pj4+I+HAAAAAACGTEbExsxsyI/4+Pj4+IiOiO/3gAAAAA + CHRMTGzEzMaIiIiOiOjoiI+Pj4AAAAAACITEbExsxsx4joiIiI+I+Pj4/wAAAAAACIRMRMTHd4iIiIiI + +Pj/AAAAAAAAAAAACIR3eIiIiIiIj48AAAAAAAAAAAAAAAAACIiIiIiP/wAAAAAAAAAAAAAAAAAAAAAA + AIj4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP//+D///wAA//8AAf// + AAD//AAAf/8AAP/wAAAf/wAA/+AAAA//AAD/wAAAD/8AAP/AAAAH/wAA/8AAAAf/AAD/wAAAD/8AAP/g + AAAP/wAA//AAAAD/AAD//AAAAP8AAP//AAAA/wAA///AAAD/AAD//gAAAH8AAP/AAAAAfwAA+AAAAAB/ + AADAAAAAAH8AAMAAAAAAfwAAwAAAAAB/AADAAAAAAH8AAMAAAAAAfwAAwAAAAAB/AADAAAAAAH8AAMAA + AAAAPwAA4AAAAAA/AADgAAAAAD8AAOAAAAAAPwAA4AAAAAA/AADgAAAAAD8AAOAAAAAAPwAA4AAAAAA/ + AADwAAAAAB8AAPAAAAAAHwAA8AAAAAAfAADwAAAAAB8AAPAAAAAAHwAA8AAAAAAfAADwAAAAAB8AAPgA + AAAAHwAA+AAAAAAPAAD4AAAAAB8AAPgAAAAAPwAA+AAAAD//AAD4AAA///8AAPgAP////wAA/D////// + AAD///////8AACgAAAAwAAAAYAAAAAEACAAAAAAAAAkAAAAAAAAAAAAAAAEAAAABAAAAAAAAX19fAGVl + ZQBrbGsAdHR1AHp6egCDAQAAhAoBAIsMAACUBgAAmQYAAJMNAACaDQAAhxEIAIoUCwCOGAwAlBIEAJwQ + AQCTFgkAnhsNAIwaEgCnDAAAtAYAAKMSAACqFAAAphgGAKMbCwCqHgwAshUAALYYAAC7GQAAsR4LAIsj + GgCVJRsAtSILALkgCwCnJBUAqyQSAK4oFwCxJRIAsCoXALsrFwCzLBoAuS4aALIwHwC6MBwAii8oAJUq + IwCJMCkAmTAnAJkzKwCcOS8Aijs0AJw8NACvNicApTYrALUzIAC8NSIAtzopALo7KQClPDIAwxsAAMsd + AADTHQAA5RoAAMYjCADYIQAAzzAVAOckAADwKAIA9TEKAO85FgD0OBIAwTciAMI5JADBPisAyT4oANY8 + IgClQzsAvUQ0ALdKNwDFQS0AykQvANBDLADCRjQAykUwAMRINgDKSjYAwko6AM5NOADXTjYA0VA8APRK + KACNSUEAk0pFAJtTTgCXWFQApktEAKpRSgCzUEUAuVBCALFWTAC4VEoArVdQAKtYUgCmXVgAtF1TALBf + WgCbY14At2BUAJVlYQCcfHkAomZhALhrYwC7c2wAoHRzAL54cAC2f3sAylVDANVaRgDiWkIA12JOANlo + VQDpZUwA+GFDAORrVQDncl0AxHVqAM56bgDRcGIA3XRiAN57awDBfHUA5XpnAPt+ZQCugXsA3oFvAMWF + fADYinwA5oBuAPOBawDqiHYA9Yp1APaRfQCCgoMAioqMAJ2EgwCIkI4AgJ2MAIyNkQCOkZUAjJOYAJKS + lACWlpkAmpqbAKSKigC0iIUAs42LALGSjgCmmJgAh6SYAJ2eogCaoaYAnaWpAJ+trACjo6MApKaqAKim + qACjqawAq6urALWpqQCurrAArLK1AK+4uwC0tLQAurO0ALW2uAC8t7gAtby/ALu7uwDOkIUAyZaPAMSf + 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 + 1v+kpab/r1ZM/6U3Lf+iDgD/sBYA/70YAP/KHgX/6ot6//Sxpv/3qpv/+q6e//6ypP/zn4r/ws3O/6iq + qv/O1NX/oqSk/61KQf+jJx3/sBQA/8EcAP/SGwD/5DMT//uyo//+xrr//sOz///Gtv//ybr/+LWg/8LO + z/+qrKz/zdTW/6Gjo/+qMCP/qQ0A/8AbAP/RIQD/4RoA//NQNf//0sb//9TI///Uxv//18j//9rL//rG + tP/Dzs//rK6v/87U1v+goaH/pRwP/7MWAP/FHQD/1SMA/+caAP/6fWj//+vm///i2f//5tz//+nf///q + 4P/6zb7/xM7Q/6+ysv/O1Nb/oKGh/6cZDf+4GAD/yhoA/90ZAP/xJwn/+Ipr//u/ov/7tJH/+66J//qo + f//2oXf/8Jty/8XO0P+xtLX/ztTW/6Chof+goaH/oKGh/6Gio/+jpaX/paen/6epqf+prKz/rK+v/6+x + sf+xtLX/tLi4/7a7vP/Fz9H/s7e4/87U1njO1Nb/ztTW/87U1v/N1Nb/zdPV/83T1f/M09X/y9LU/8rS + 1P/K0tP/ydHT/8jR0v/H0NH/xs/R/7a6u5XgBwAA4AcAAPAPAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKAAAACAAAABAAAAAAQAgAAAAAACAEAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACjo6MDurq6I7S0 + tFatra18t7e3hbm5uYeoqKiGjo6Of4KCgmB7e3stg4ODCwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAkJCQHbCw + sHzHx8fIzs7O8NXV1fvZ2dn+1tbW/s7Ozv7MzMz8wsLC9Kurq9WUlJRrjo6OMAAAAAAAAAAAAAAAAAAA + AAEAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJWV + lWu+vr7r0tLS/9fX1//b29v/3t7e/9zc3P/U1NT/zc3N/93d3f/b29v/wcHB/8XFxf+4uLj+eXl5UBMT + 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 + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIiIiCyqqqrOzc3N/83Nzf/Ly8v/z8/P/9PT + 0//X19f/2tra/9vb2//a2tr/19fX/9LS0v/MzMz/0dHR/9/f3//Z2dn/wMDA/7e3t/++vr7/ycnJ/7i4 + uP+wsLD9WVlZiAAAABYAAAAjAAAAKwAAACsAAAAlAAAAGQAAAA0AAAAGAAAAAwAAAAEAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAi4uLJbS0tOfIyMj/w8PD/8bG + xv/Ly8v/zs7O/9LS0v/X19f/29vb/9zc3P/c3Nz/2NjY/9HR0f/Kysr/09PT/+Hh4f/Q0ND/ubm5/8DA + wP/Pz8//urq6/62trf+urq7/ubm5/2FhYawAAABDAAAARgAAAEAAAAA2AAAAKQAAABwAAAARAAAACQAA + AAQAAAABAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACJiYkDq6urvcDA + wP++vr7/wsLC/8TExP/IyMj/zMzM/9HR0f/X19f/29vb/93d3f/c3Nz/2NjY/9HR0f/Jycn/0tLS/9vb + 2//FxcX/wcHB/8nJyf+6urr/sLCw/66urv+oqKj/oqKi/6qqqv8fHx93AAAATgAAAEkAAAA+AAAAMwAA + ACYAAAAbAAAAEQAAAAoAAAAEAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAChoaEXtra29Lm5uf+8vLz/v7+//8LCwv/FxcX/ycnJ/87Ozv/U1NT/2dnZ/9zc3P/b29v/1tbW/87O + zv/Hx8f/zMzM/8zMzP/AwMD/w8PD/7e3t/+ysrL/rq6u/6SkpP+ampr/l5eX/6qqqv9GRkaXAAAARwAA + AEgAAABAAAAANQAAACgAAAAfAAAAFgAAAA4AAAAHAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAC6uroWtLS08be3t/+5ubn/vLy8/7+/v//BwcH/xcXF/8nJyf/Pz8//1NTU/9bW + 1v/V1dX/0dHR/8rKyv/ExMT/xcXF/8DAwP++vr7/t7e3/7Gxsf+pqan/n5+f/5WVlf+SkpL/mJiY/6ur + q/9RUVGcAAAAPAAAAEEAAAA6AAAAMQAAACcAAAAeAAAAFgAAAA4AAAAHAAAAAwAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAC0tLQCtbW1y7W1tf+3t7f/ubm5/729vf+/v7//wcHB/8TE + xP/Hx8f/y8vL/83Nzf/MzMz/ycnJ/8XFxf/BwcH/v7+//7q6uv+ysrL/qamp/6Ghof+Xl5f/kpKS/5OT + k/+YmJj/nZ2d/6urq/8pKSltAAAALgAAADQAAAAzAAAALAAAACMAAAAaAAAAEwAAAAwAAAAHAAAAAgAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAqampQrOzs/e7u7v/t7e3/7q6 + uv+8vLz/vr6+/8DAwP/CwsL/xMTE/8bGxv/FxcX/wsLC/7y8vP+1tbX/ra2t/6ampv+bm5v/jIyM/3t7 + e/95eXn/ioqK/5SUlP+bm5v/pqam/4GBgcoWFhZFODk5YTk5Olpubm5aEhISKQAAABwAAAAWAAAADgAA + AAgAAAAEAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGdn + Z0WZmZnNu7u7/7a2tv+zs7P/tLS0/7S0tP+zs7P/srKy/7CwsP+tra3/pqam/5ubm/+SkpL/iIiI/3x8 + fP9qamn/X19f/2VlZf97e3z/jY6O/56en/+sra3/tre3/pubnM6urq/iycrK/7a2t+6IiIi+Z2dnKwAA + ABEAAAALAAAABwAAAAMAAAACAAAAAQAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAICAgGTk5ObJ2dneiurq7/qamp/6Ojo/+enp7/m5ub/5ycnP+Pj4//goGB/3p6 + ev9zc3P/bm5u/2tsbP91dnf/hIWG/5iYmv+goaL/sLCx/7e3uP+3t7f/u7u7/8HBwf/AwMD/v7+//L6+ + vv+Xl5ftgICAMQAAAAMAAAADAAAAAQAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAABAAAAAwAAAAYAAAAGAAAAABAQECNGRkZ0eXl5v4+PjuKgoKD4o6Oj/ouL + iv97e3v/gICB/4SEhf+IiYv/kJCT/5manP+oqKv/rq6w/66ur/+vr7D/r6+w/7a3t/+1t7f/u8LD/8LM + zf/Jzs//wcDA/by8vPyfn5/6b29vUwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAMAAAAGAAAACQAAAAwAAAAOAAAADQAAAAUAAAAAFhYXC0BA + QjBvb3GBjo+R7oqKjf+OjpL/kJCU/4+QlP+Skpb/lpaZ/56eof+lpaf/p6mr/6yxtP+1vsD/wMnL/87T + 1P/W09P/2cnG/9+4tP/Wvrn/wMXF/cHBwfyioqL8jIyMbgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBAQEARQUFQgtLS8XSkpMNWpq + bGaCgoaYj4+TxJSUmOWQkJT4jY2R/42Nkf+MjJH/i42R/4uOk/+Nkpf/nKSo/6+4u//Dycv/z87P/9TH + xv/Usq7/2p+U/9FwYv/HQS7/wBwE/7QGAP+zX1X/w87P/sTExPyhoaH+n5+fiwAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAaGhoHJCQlHT8/QTBbW15MfX2AgY6O + kbCYmJ3anJyh+Zubn/+Xl5v/jo6S/4yMkf+KjpL/jJOY/5ihpf+hqKv/rauv/7mwsv/HvLz/yK2q/8mW + j//Oem7/wko6/7kgC/+0FAD/rBAA/6URAP+lFgP/phoI/6YTAf+yVkv/xczN/8fHx/2kpKT/nJycqwAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAI+PkxNwcHM3dHV4ZouIjqKOjZHTkZGW7piY + nPufn6P/oKCk/5+fov+fn6P/nZ+j/5mfpP+fp6v/oquu/6usr/+8tbb/xra1/8SfnP++eHD/uFRK/682 + J/+nHg7/phgH/6UTAv+lFQP/phoJ/6gfDv+qIRD/rCMR/68kEf+wJRL/sSYT/7EeC/+5UEL/x8vM/8nJ + yf6pqan/nJycxgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAkZGVao+Pk+aTk5f/kpCW/4Cd + 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 + T//kiXr/5ol5/+iKef/qjHv/7I17/+2OfP/vj33/8JB+//KRf//zkoD/9JOB//WVgv/3kX7/7H1n/9TI + xv/W2dn/ra2t/5mZmdwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOfn5zO5vsH+noSE/4wc + Ff+mS0T/pEhB/6JGP/+kRj7/pkA2/5UWCP+VDQD/mw8A/6ISAP+nFAD/qhUA/64VAP+0FwD/uRkA/74a + AP/DGgD/yScM/+B0Yv/plYb/65SF/+yVhf/ulob/8JeH//GZiP/zmYj/9JqJ//abiv/2nIv/952M//ie + jf/5noz/84Jr/9W8uP/Y3N3/t7e3/5mZmeoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAO7u + 7xe9wMLwqJyd/4ojHP+pUEn/qFFK/6dOR/+sUUj/pDYs/5MOAP+ZDwD/nxEA/6YTAP+pFAD/rBUA/7IW + AP+4GAD/vhoA/8McAP/GGwD/zzAV/+aFdP/tn5H/7p2P//Cej//xn5D/86GR//Whkf/2o5L/96ST//il + lP/5ppX/+qaW//unl//8p5f/+4t0/9q0rP/Y3uD/vr6+/5mZmfSenp4HAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAPLy8ge/wMLbs7Gz/4ovKP+qUUv/q1hS/61YT/+wV07/nCQX/5UMAP+cEAD/ohIA/6gU + AP+rFQD/sBYA/7YYAP+7GQD/whsA/8YcAP/KGwD/1jwi/+uVhv/wp5v/8aaY//Onmf/1qJr/9qma//iq + m//5q5z/+ayc//utnv/8rp7//a+f//6woP//sKD//5aA/+SzqP/Y4OH/w8PD/5mZmfucnJwiAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADGxsi/vMDC/48+Of+qUEr/sF9a/7NhWf+xVUv/lhQG/5kN + AP+gEQD/pRMA/6kVAP+uFQD/tBcA/7kYAP+/GgD/xBwA/8gdAP/NHQD/3Ewz/++jlv/yr6P/86+i//Wv + ov/3sKP/+LGk//mypP/7s6X//LSm//62pv//tqf//7ao//+4qf//uar//6KN/+Wvo//a4eL/yMjI/5mZ + mf6bm5tJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADNzs+bw8jL/5hNSP+oTkj/tmpi/7lr + Y/+rRDr/lgwA/50QAP+jEgD/pxQA/6sVAP+xFgD/txgA/78aAP/GHQD/zB4A/9MfAP/aIwL/6WVM//e0 + p//5ua3/+7mr//y6rP/9u63//b2u//6+rv/+v6///8Gw///CsP//w7H//8Sy///Esv//xrT//7Sc/+iv + nP/b4OL/zc3N/5qamv+bm5tzAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADW1th3w8nM/5tT + Tv+oTET/u3Ns/7tsZP+eKBv/mAsA/6ERAP+oFAD/rhUA/7cYAP+/GwD/xx0A/88fAP/XIQD/3iIA/+Qj + AP/sLgn/9oFq//3Btf//wrX//8K1///Etv//xbb//8e3///IuP//ybj//8m5///Luv//zLv//827///O + vP//0L3//8Oq/+yulf/c3t//0dHR/5ubm/+ampqcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AADc3N5TxMvN/5dYVP+mR0D/wXx1/7ZbUv+aEgT/oxEA/6oUAP+xFgD/uRkA/8EbAP/HHQD/zR4A/9Qg + AP/aIgD/4CMA/+cjAP/vORb/+ZmF//7Jvv//yL3//8m9///Kvf//zL7//82////Ov///z8D//9DA///R + wf//0sH//9PC///Uw///1cT//8y2//Guk//b2tn/2NnZ/56env+ampq6AAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAADh4eI4xs3P/5dlYv+lQjn/xoF5/6s9Mf+eDAD/qBQA/6wVAP+0FwD/uxkA/8Ib + AP/JHQD/0B8A/9YhAP/cIwD/5CQA/+sjAP/ySyn//K6f///Pxv//zsP//8/E///QxP//0sX//9PF///U + xv//1cb//9bH///Xx///2Mj//9jJ///Zyv//28z//9XD//Wxlv/b09D/3+Dh/6Ghof+ZmZnTAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADU1NYbyc/R85x8ef+iPTP/wnZt/6AeD/+hDwD/qRQA/68V + AP+3GAD/vhoA/8QcAP/LHgD/0yAA/9ghAP/fIwD/6CQA/+0lAP/2YUP//8K2///VzP//1Mr//9XK///W + yv//18v//9jL///ZzP//2sz//9vN///czv//3c///93Q///f0f//4NP//93O//q5nv/dz8n/4+bn/6mp + qf+ZmZnkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADt7e4Jys3Q3qmZmP+cOS//s1BF/5sO + AP+kEgD/qxUA/7EWAP+5GQD/vxsA/8cdAP/OHgD/1SAA/9siAP/jJAD/6yUA//ArBP/6fmX//9LI///a + 0f//2dD//9rQ///b0f//3NH//93S///e0v//39L//+DT///h1f//4tb//+PX///k2P//5dn//+LW//3C + qf/lz8b/6Ozu/7Ozs/+YmJjynp6eBwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAzM7QuLq0 + tf+SKB7/nhsN/50PAP+mEwD/rBUA/7QXAP+8GQD/wRsA/8kdAP/RHwD/1yEA/90jAP/mJQD/7SQA//Q4 + Ev/+oI3//97X///e1///39b//9/X///g1///4df//+LY///j2P//5Nn//+Xa///m2///593//+fe///o + 3v//6d///+je///Mtv/mzMH/7fHz/8HBwf+Xl5f6nJycJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAwMHDlcPExf+NIxn/lwkA/6ARAP+nFAD/rhUA/7YXAP+9GgD/wxwA/8sdAP/TIAD/2SIA/98j + AP/oJQD/7iUA//dKJ///uq3//+Pd///i2///49v//+Pb///j2///5dz//+bc///n3P//597//+je///p + 4P//6uH//+vi///r4///7OT//+zk///Uwv/lyLz/7/P1/8zMzP+YmJj+nJycSQAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAy8vNdsLJyv+KMSj/mQgA/6ERAP+oFAD/rxYA/7gYAP++GgD/xBwA/80e + AP/VIAD/2yIA/+EkAP/pJQD/8CUA//piQ///z8X//+fh///l3///5t///+bf///n4P//6OD//+nh///q + 4f//6+L//+vj///s5P//7OT//+3l///s5P//7OT//+rg///Uv//owK7/8fX3/9bW1v+YmJj/m5ubawAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAArq6xVcjR0/+NSUH/mQYA/6MSAP+pFAD/sBYA/7kY + AP+/GwD/xhwA/84eAP/WIQD/3CIA/+MkAP/rJQD/8ioC//x+Zf//39j//+rl///p5P//6uT//+rk///q + 4///6uH//+nf///m3P//5Nj//+HU///czP//18T//9G8//3JtP/5w6v/9ryk//Gtjv/WppL/7/P1/+Xl + 5f+YmJj/np6ejAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA0tLUNsXNz/6UZWD/lwUA/6QS + AP+pFAD/sRYA/7oZAP/AGwD/xh0A/9AfAP/WIQD/3SIA/+QkAP/sJAD/9TEK//+TfP//39f//9/W///X + y///0MH//8m4//3DsP/3vqn/9Lqk//S5of/0tp3/9LOY/+mulv/frpr/2LOj/9i+tP/cy8T/4tbR/+bh + 3//m5ub/8fHx/+jo6P+mpqb6s7OzTQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAubm7F8HG + yO+dhIL/lAcA/6USAP+qFAD/sxYA/7wZAP/DGgD/yhsA/9UaAP/bGgD/4BoA/+YaAP/rGwD/7CsE/+1s + Uv/qmYf/4pyM/+GgkP/hpZX/2qqd/9Gxp//Rvrn/1MrH/9nSz//c19b/2dfX/9jZ2v/X29z/297f/9zf + 4Pvb3d3u2tvc39XW1tnb29vG3Nzcst7e3o3MzMxLAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAw8PGBby/wdeomZn/lQcA/6YJAP+oCwD/rw0A/7URAP+1GAD/tSIL/7c1IP+3Sjf/vGBQ/8Z1 + aP/Fin//wKCa/8W3tP/LxsX/zs/Q/9HW1//T2Nr/0tfZ/9PX2f/T1db50dPT7NDR0d3Nzs7Xz8/Pwc/P + z63U1NSP0NDQdNLS0k/Ozs8rysrKFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAALCytb2zra7/hTgw/5FKQ/+cZV//roF7/7GSjv+xoaD/tLGy/7vB + w/+/ys3/xdDT/8rU1v/N1db/ztPU983P0OvNzs7dy8vL1MvLy8HKysqmzs7Oj8vLzHnOzs5Oy8vLM8rK + yhrKysoDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAK2tr7PCw8P/t7/C/73Fx//Dy83/xMrM/cXJ + y/XHycrqxsfI3MbGx83Hx8jAx8fHp8rKyovIyMhzy8vLUsrKyjnKysoaysrKCwAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKCgo3a5ubvAvLy9wr+/ + wK28vL6NwMDBb8LCw1fHx8g8ysrKIMrKyhQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AADKysoOysrKAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD//4AD//8AAP/8 + AAD4fwAA//AAACAPAAD/4AAAAAcAAP/AAAAAAQAA/4AAAAABAAD/gAAAAAEAAP+AAAAAAQAA/4AAAAAB + AAD/wAAAAAEAAP/gAAAAAAAA//AAAAAHAAD/hAAAAH8AAP4BAAAAfwAA/4AAAAB/AAD8AAAAAH8AAMAA + AAAAfwAAgAAAAAB/AACAAAAAAH8AAIAAAAAAPwAAgAAAAAA/AACAAAAAAD8AAIAAAAAAPwAAwAAAAAA/ + AADAAAAAAD8AAMAAAAAAPwAAwAAAAAA/AADAAAAAAD8AAMAAAAAAHwAAwAAAAAAfAADgAAAAAB8AAOAA + AAAAHwAA4AAAAAAfAADgAAAAAB8AAOAAAAAAHwAA4AAAAAAfAADgAAAAAA8AAPAAAAAADwAA8AAAAAAP + AADwAAAAAA8AAPAAAAAADwAA8AAAAAAPAADwAAAAAB8AAPAAAAAD/wAA+AAAAf//AAD4AAH///8AAPgB + /////wAA/P//////AAA= + + + \ No newline at end of file diff --git a/MainForm.Designer.cs b/MainForm.Designer.cs index b5d41ba..ada6bef 100644 --- a/MainForm.Designer.cs +++ b/MainForm.Designer.cs @@ -83,6 +83,19 @@ this.devicesTabControl = new System.Windows.Forms.TabControl(); this.devicesTabPage = new System.Windows.Forms.TabPage(); this.devicesPanel = new System.Windows.Forms.Panel(); + this.devicesListView = new System.Windows.Forms.ListView(); + this.nameColumnHeader = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.stateColumnHeader = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.devicesContextMenuStrip = new System.Windows.Forms.ContextMenuStrip(this.components); + this.addMapToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.addRelayMapToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.toolStripMenuItem3 = new System.Windows.Forms.ToolStripSeparator(); + this.httpToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.httpsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.rdpToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.sshToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.scpToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.devicesImageList = new System.Windows.Forms.ImageList(this.components); this.noSearchResultsLabel = new System.Windows.Forms.Label(); this.noDevicesLabel = new System.Windows.Forms.Label(); this.portMapTabPage = new System.Windows.Forms.TabPage(); @@ -110,19 +123,7 @@ this.saveMappingsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.openMapFileDialog = new System.Windows.Forms.OpenFileDialog(); this.saveMapFileDialog = new System.Windows.Forms.SaveFileDialog(); - this.devicesListView = new System.Windows.Forms.ListView(); - this.devicesImageList = new System.Windows.Forms.ImageList(this.components); - this.nameColumnHeader = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); - this.stateColumnHeader = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); - this.devicesContextMenuStrip = new System.Windows.Forms.ContextMenuStrip(this.components); - this.httpToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.httpsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.rdpToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.sshToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.scpToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.toolStripMenuItem3 = new System.Windows.Forms.ToolStripSeparator(); - this.addMapToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.addRelayMapToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.remoteDesktopToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.panel5.SuspendLayout(); this.mainPanel.SuspendLayout(); this.mainTabControl.SuspendLayout(); @@ -143,6 +144,7 @@ this.devicesTabControl.SuspendLayout(); this.devicesTabPage.SuspendLayout(); this.devicesPanel.SuspendLayout(); + this.devicesContextMenuStrip.SuspendLayout(); this.portMapTabPage.SuspendLayout(); this.mapPanel.SuspendLayout(); ((System.ComponentModel.ISupportInitialize)(this.settingsPictureBox)).BeginInit(); @@ -151,7 +153,6 @@ ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).BeginInit(); this.trayIconContextMenuStrip.SuspendLayout(); this.mappingsContextMenuStrip.SuspendLayout(); - this.devicesContextMenuStrip.SuspendLayout(); this.SuspendLayout(); // // panel5 @@ -554,6 +555,118 @@ this.devicesPanel.Controls.Add(this.noDevicesLabel); this.devicesPanel.Name = "devicesPanel"; // + // devicesListView + // + this.devicesListView.BackColor = System.Drawing.SystemColors.Window; + this.devicesListView.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] { + this.nameColumnHeader, + this.stateColumnHeader}); + this.devicesListView.ContextMenuStrip = this.devicesContextMenuStrip; + this.devicesListView.FullRowSelect = true; + this.devicesListView.GridLines = true; + this.devicesListView.HeaderStyle = System.Windows.Forms.ColumnHeaderStyle.Nonclickable; + this.devicesListView.LargeImageList = this.devicesImageList; + resources.ApplyResources(this.devicesListView, "devicesListView"); + this.devicesListView.MultiSelect = false; + this.devicesListView.Name = "devicesListView"; + this.devicesListView.SmallImageList = this.devicesImageList; + this.devicesListView.Sorting = System.Windows.Forms.SortOrder.Ascending; + this.devicesListView.UseCompatibleStateImageBehavior = false; + this.devicesListView.View = System.Windows.Forms.View.Details; + this.devicesListView.DoubleClick += new System.EventHandler(this.devicesListView_DoubleClick); + // + // nameColumnHeader + // + resources.ApplyResources(this.nameColumnHeader, "nameColumnHeader"); + // + // stateColumnHeader + // + resources.ApplyResources(this.stateColumnHeader, "stateColumnHeader"); + // + // devicesContextMenuStrip + // + this.devicesContextMenuStrip.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.addMapToolStripMenuItem, + this.addRelayMapToolStripMenuItem, + this.toolStripMenuItem3, + this.remoteDesktopToolStripMenuItem, + this.httpToolStripMenuItem, + this.httpsToolStripMenuItem, + this.rdpToolStripMenuItem, + this.sshToolStripMenuItem, + this.scpToolStripMenuItem}); + this.devicesContextMenuStrip.Name = "devicesContextMenuStrip"; + resources.ApplyResources(this.devicesContextMenuStrip, "devicesContextMenuStrip"); + this.devicesContextMenuStrip.Opening += new System.ComponentModel.CancelEventHandler(this.devicesContextMenuStrip_Opening); + // + // addMapToolStripMenuItem + // + resources.ApplyResources(this.addMapToolStripMenuItem, "addMapToolStripMenuItem"); + this.addMapToolStripMenuItem.Name = "addMapToolStripMenuItem"; + this.addMapToolStripMenuItem.Click += new System.EventHandler(this.addMapToolStripMenuItem_Click); + // + // addRelayMapToolStripMenuItem + // + this.addRelayMapToolStripMenuItem.Name = "addRelayMapToolStripMenuItem"; + resources.ApplyResources(this.addRelayMapToolStripMenuItem, "addRelayMapToolStripMenuItem"); + this.addRelayMapToolStripMenuItem.Click += new System.EventHandler(this.addRelayMapToolStripMenuItem_Click); + // + // toolStripMenuItem3 + // + this.toolStripMenuItem3.Name = "toolStripMenuItem3"; + resources.ApplyResources(this.toolStripMenuItem3, "toolStripMenuItem3"); + // + // httpToolStripMenuItem + // + this.httpToolStripMenuItem.Name = "httpToolStripMenuItem"; + resources.ApplyResources(this.httpToolStripMenuItem, "httpToolStripMenuItem"); + this.httpToolStripMenuItem.Click += new System.EventHandler(this.httpToolStripMenuItem_Click); + // + // httpsToolStripMenuItem + // + this.httpsToolStripMenuItem.Name = "httpsToolStripMenuItem"; + resources.ApplyResources(this.httpsToolStripMenuItem, "httpsToolStripMenuItem"); + this.httpsToolStripMenuItem.Click += new System.EventHandler(this.httpsToolStripMenuItem_Click); + // + // rdpToolStripMenuItem + // + this.rdpToolStripMenuItem.Name = "rdpToolStripMenuItem"; + resources.ApplyResources(this.rdpToolStripMenuItem, "rdpToolStripMenuItem"); + this.rdpToolStripMenuItem.Click += new System.EventHandler(this.rdpToolStripMenuItem_Click); + // + // sshToolStripMenuItem + // + this.sshToolStripMenuItem.Name = "sshToolStripMenuItem"; + resources.ApplyResources(this.sshToolStripMenuItem, "sshToolStripMenuItem"); + this.sshToolStripMenuItem.Click += new System.EventHandler(this.sshToolStripMenuItem_Click); + // + // scpToolStripMenuItem + // + this.scpToolStripMenuItem.Name = "scpToolStripMenuItem"; + resources.ApplyResources(this.scpToolStripMenuItem, "scpToolStripMenuItem"); + this.scpToolStripMenuItem.Click += new System.EventHandler(this.scpToolStripMenuItem_Click); + // + // devicesImageList + // + this.devicesImageList.ImageStream = ((System.Windows.Forms.ImageListStreamer)(resources.GetObject("devicesImageList.ImageStream"))); + this.devicesImageList.TransparentColor = System.Drawing.Color.Transparent; + this.devicesImageList.Images.SetKeyName(0, "icons01.png"); + this.devicesImageList.Images.SetKeyName(1, "icons01t.png"); + this.devicesImageList.Images.SetKeyName(2, "icons02.png"); + this.devicesImageList.Images.SetKeyName(3, "icons02t.png"); + this.devicesImageList.Images.SetKeyName(4, "icons03.png"); + this.devicesImageList.Images.SetKeyName(5, "icons03t.png"); + this.devicesImageList.Images.SetKeyName(6, "icons04.png"); + this.devicesImageList.Images.SetKeyName(7, "icons04t.png"); + this.devicesImageList.Images.SetKeyName(8, "icons05.png"); + this.devicesImageList.Images.SetKeyName(9, "icons05t.png"); + this.devicesImageList.Images.SetKeyName(10, "icons06.png"); + this.devicesImageList.Images.SetKeyName(11, "icons06t.png"); + this.devicesImageList.Images.SetKeyName(12, "icons07.png"); + this.devicesImageList.Images.SetKeyName(13, "icons07t.png"); + this.devicesImageList.Images.SetKeyName(14, "icons08.png"); + this.devicesImageList.Images.SetKeyName(15, "icons08t.png"); + // // noSearchResultsLabel // resources.ApplyResources(this.noSearchResultsLabel, "noSearchResultsLabel"); @@ -734,116 +847,11 @@ this.saveMapFileDialog.DefaultExt = "mcrouter"; resources.ApplyResources(this.saveMapFileDialog, "saveMapFileDialog"); // - // devicesListView + // remoteDesktopToolStripMenuItem // - this.devicesListView.BackColor = System.Drawing.SystemColors.Window; - this.devicesListView.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] { - this.nameColumnHeader, - this.stateColumnHeader}); - this.devicesListView.ContextMenuStrip = this.devicesContextMenuStrip; - this.devicesListView.FullRowSelect = true; - this.devicesListView.GridLines = true; - this.devicesListView.HeaderStyle = System.Windows.Forms.ColumnHeaderStyle.Nonclickable; - this.devicesListView.LargeImageList = this.devicesImageList; - resources.ApplyResources(this.devicesListView, "devicesListView"); - this.devicesListView.MultiSelect = false; - this.devicesListView.Name = "devicesListView"; - this.devicesListView.SmallImageList = this.devicesImageList; - this.devicesListView.Sorting = System.Windows.Forms.SortOrder.Ascending; - this.devicesListView.UseCompatibleStateImageBehavior = false; - this.devicesListView.View = System.Windows.Forms.View.Details; - this.devicesListView.DoubleClick += new System.EventHandler(this.devicesListView_DoubleClick); - // - // devicesImageList - // - this.devicesImageList.ImageStream = ((System.Windows.Forms.ImageListStreamer)(resources.GetObject("devicesImageList.ImageStream"))); - this.devicesImageList.TransparentColor = System.Drawing.Color.Transparent; - this.devicesImageList.Images.SetKeyName(0, "icons01.png"); - this.devicesImageList.Images.SetKeyName(1, "icons01t.png"); - this.devicesImageList.Images.SetKeyName(2, "icons02.png"); - this.devicesImageList.Images.SetKeyName(3, "icons02t.png"); - this.devicesImageList.Images.SetKeyName(4, "icons03.png"); - this.devicesImageList.Images.SetKeyName(5, "icons03t.png"); - this.devicesImageList.Images.SetKeyName(6, "icons04.png"); - this.devicesImageList.Images.SetKeyName(7, "icons04t.png"); - this.devicesImageList.Images.SetKeyName(8, "icons05.png"); - this.devicesImageList.Images.SetKeyName(9, "icons05t.png"); - this.devicesImageList.Images.SetKeyName(10, "icons06.png"); - this.devicesImageList.Images.SetKeyName(11, "icons06t.png"); - this.devicesImageList.Images.SetKeyName(12, "icons07.png"); - this.devicesImageList.Images.SetKeyName(13, "icons07t.png"); - this.devicesImageList.Images.SetKeyName(14, "icons08.png"); - this.devicesImageList.Images.SetKeyName(15, "icons08t.png"); - // - // nameColumnHeader - // - resources.ApplyResources(this.nameColumnHeader, "nameColumnHeader"); - // - // stateColumnHeader - // - resources.ApplyResources(this.stateColumnHeader, "stateColumnHeader"); - // - // devicesContextMenuStrip - // - this.devicesContextMenuStrip.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { - this.addMapToolStripMenuItem, - this.addRelayMapToolStripMenuItem, - this.toolStripMenuItem3, - this.httpToolStripMenuItem, - this.httpsToolStripMenuItem, - this.rdpToolStripMenuItem, - this.sshToolStripMenuItem, - this.scpToolStripMenuItem}); - this.devicesContextMenuStrip.Name = "devicesContextMenuStrip"; - resources.ApplyResources(this.devicesContextMenuStrip, "devicesContextMenuStrip"); - this.devicesContextMenuStrip.Opening += new System.ComponentModel.CancelEventHandler(this.devicesContextMenuStrip_Opening); - // - // httpToolStripMenuItem - // - this.httpToolStripMenuItem.Name = "httpToolStripMenuItem"; - resources.ApplyResources(this.httpToolStripMenuItem, "httpToolStripMenuItem"); - this.httpToolStripMenuItem.Click += new System.EventHandler(this.httpToolStripMenuItem_Click); - // - // httpsToolStripMenuItem - // - this.httpsToolStripMenuItem.Name = "httpsToolStripMenuItem"; - resources.ApplyResources(this.httpsToolStripMenuItem, "httpsToolStripMenuItem"); - this.httpsToolStripMenuItem.Click += new System.EventHandler(this.httpsToolStripMenuItem_Click); - // - // rdpToolStripMenuItem - // - this.rdpToolStripMenuItem.Name = "rdpToolStripMenuItem"; - resources.ApplyResources(this.rdpToolStripMenuItem, "rdpToolStripMenuItem"); - this.rdpToolStripMenuItem.Click += new System.EventHandler(this.rdpToolStripMenuItem_Click); - // - // sshToolStripMenuItem - // - this.sshToolStripMenuItem.Name = "sshToolStripMenuItem"; - resources.ApplyResources(this.sshToolStripMenuItem, "sshToolStripMenuItem"); - this.sshToolStripMenuItem.Click += new System.EventHandler(this.sshToolStripMenuItem_Click); - // - // scpToolStripMenuItem - // - this.scpToolStripMenuItem.Name = "scpToolStripMenuItem"; - resources.ApplyResources(this.scpToolStripMenuItem, "scpToolStripMenuItem"); - this.scpToolStripMenuItem.Click += new System.EventHandler(this.scpToolStripMenuItem_Click); - // - // toolStripMenuItem3 - // - this.toolStripMenuItem3.Name = "toolStripMenuItem3"; - resources.ApplyResources(this.toolStripMenuItem3, "toolStripMenuItem3"); - // - // addMapToolStripMenuItem - // - resources.ApplyResources(this.addMapToolStripMenuItem, "addMapToolStripMenuItem"); - this.addMapToolStripMenuItem.Name = "addMapToolStripMenuItem"; - this.addMapToolStripMenuItem.Click += new System.EventHandler(this.addMapToolStripMenuItem_Click); - // - // addRelayMapToolStripMenuItem - // - this.addRelayMapToolStripMenuItem.Name = "addRelayMapToolStripMenuItem"; - resources.ApplyResources(this.addRelayMapToolStripMenuItem, "addRelayMapToolStripMenuItem"); - this.addRelayMapToolStripMenuItem.Click += new System.EventHandler(this.addRelayMapToolStripMenuItem_Click); + this.remoteDesktopToolStripMenuItem.Name = "remoteDesktopToolStripMenuItem"; + resources.ApplyResources(this.remoteDesktopToolStripMenuItem, "remoteDesktopToolStripMenuItem"); + this.remoteDesktopToolStripMenuItem.Click += new System.EventHandler(this.remoteDesktopToolStripMenuItem_Click); // // MainForm // @@ -880,6 +888,7 @@ this.devicesTabControl.ResumeLayout(false); this.devicesTabPage.ResumeLayout(false); this.devicesPanel.ResumeLayout(false); + this.devicesContextMenuStrip.ResumeLayout(false); this.portMapTabPage.ResumeLayout(false); this.mapPanel.ResumeLayout(false); ((System.ComponentModel.ISupportInitialize)(this.settingsPictureBox)).EndInit(); @@ -888,7 +897,6 @@ ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).EndInit(); this.trayIconContextMenuStrip.ResumeLayout(false); this.mappingsContextMenuStrip.ResumeLayout(false); - this.devicesContextMenuStrip.ResumeLayout(false); this.ResumeLayout(false); } @@ -987,6 +995,7 @@ private System.Windows.Forms.ToolStripMenuItem addMapToolStripMenuItem; private System.Windows.Forms.ToolStripMenuItem addRelayMapToolStripMenuItem; private System.Windows.Forms.ToolStripSeparator toolStripMenuItem3; + private System.Windows.Forms.ToolStripMenuItem remoteDesktopToolStripMenuItem; } } diff --git a/MainForm.cs b/MainForm.cs index 6a7ef66..3bb0c30 100644 --- a/MainForm.cs +++ b/MainForm.cs @@ -1445,6 +1445,22 @@ namespace MeshCentralRouter addButton_Click(null, null); } + private void remoteDesktopToolStripMenuItem_Click(object sender, EventArgs e) + { + if (devicesListView.SelectedItems.Count != 1) { return; } + ListViewItem selecteditem = devicesListView.SelectedItems[0]; + NodeClass node = (NodeClass)selecteditem.Tag; + if ((node.conn & 1) == 0) { return; } // Agent not connected on this device + if (node.desktopViewer == null) + { + node.desktopViewer = new KVMViewer(meshcentral, node); + node.desktopViewer.Show(); + node.desktopViewer.MenuItemConnect_Click(null, null); + } else { + node.desktopViewer.Focus(); + } + } + /* private delegate void displayMessageHandler(string msg, int buttons, string extra, int progress); private void displayMessage(string msg, int buttons = 0, string extra = "", int progress = 0) diff --git a/MainForm.resx b/MainForm.resx index aec4c1f..5cd6965 100644 --- a/MainForm.resx +++ b/MainForm.resx @@ -1342,52 +1342,58 @@ Segoe UI, 9pt, style=Bold - 163, 22 + 170, 22 Add &Map... - 163, 22 + 170, 22 Add &Relay Map... - 160, 6 + 167, 6 + + + 170, 22 + + + Remote Desktop... - 163, 22 + 170, 22 HTTP - 163, 22 + 170, 22 HTTPS - 163, 22 + 170, 22 RDP - 163, 22 + 170, 22 SSH - 163, 22 + 170, 22 SCP - 164, 164 + 171, 208 devicesContextMenuStrip @@ -1403,7 +1409,7 @@ AAEAAAD/////AQAAAAAAAAAMAgAAAFdTeXN0ZW0uV2luZG93cy5Gb3JtcywgVmVyc2lvbj00LjAuMC4w LCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPWI3N2E1YzU2MTkzNGUwODkFAQAAACZTeXN0 ZW0uV2luZG93cy5Gb3Jtcy5JbWFnZUxpc3RTdHJlYW1lcgEAAAAERGF0YQcCAgAAAAkDAAAADwMAAADg - HQAAAk1TRnQBSQFMAgEBEAEAARgBAAEIAQABEAEAARABAAT/ARkBAAj/AUIBTQE2BwABNgMAASgDAAFA + HQAAAk1TRnQBSQFMAgEBEAEAATABAAEQAQABEAEAARABAAT/ARkBAAj/AUIBTQE2BwABNgMAASgDAAFA AwABUAMAAQEBAAEYBgABPP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AJYAA/0D+AP3A/sD/yEAA/0D+gP5 A/wD/xgAAfoB+wH6A/4qAAP9A/8tAAP7A88D1QPbA88D1AO+A9QYAAP8A90D4APjA90D4APUA+MSAAP8 AZEBjwF9AU4BaAEhAfQB9QH0JAAD/gPRA8YD+ioAAcYCxwHPAtAB2ALZA90DywPBA68DmgP3FQAD2APd @@ -1570,7 +1576,7 @@ 2, 88 - 454, 52 + 450, 52 5 @@ -1609,7 +1615,7 @@ 2, 88 - 454, 52 + 450, 52 4 @@ -1699,7 +1705,7 @@ 2, 79 - 454, 52 + 450, 52 4 @@ -5703,6 +5709,72 @@ Click "Add" to get started. System.Windows.Forms.ToolStripMenuItem, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + nameColumnHeader + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + stateColumnHeader + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + addMapToolStripMenuItem + + + System.Windows.Forms.ToolStripMenuItem, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + addRelayMapToolStripMenuItem + + + System.Windows.Forms.ToolStripMenuItem, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + toolStripMenuItem3 + + + System.Windows.Forms.ToolStripSeparator, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + httpToolStripMenuItem + + + System.Windows.Forms.ToolStripMenuItem, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + httpsToolStripMenuItem + + + System.Windows.Forms.ToolStripMenuItem, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + rdpToolStripMenuItem + + + System.Windows.Forms.ToolStripMenuItem, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + sshToolStripMenuItem + + + System.Windows.Forms.ToolStripMenuItem, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + scpToolStripMenuItem + + + System.Windows.Forms.ToolStripMenuItem, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + devicesImageList + + + System.Windows.Forms.ImageList, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + stateClearTimer @@ -5769,70 +5841,10 @@ Click "Add" to get started. System.Windows.Forms.SaveFileDialog, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - devicesImageList + + remoteDesktopToolStripMenuItem - - System.Windows.Forms.ImageList, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - nameColumnHeader - - - System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - stateColumnHeader - - - System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - httpToolStripMenuItem - - - System.Windows.Forms.ToolStripMenuItem, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - httpsToolStripMenuItem - - - System.Windows.Forms.ToolStripMenuItem, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - rdpToolStripMenuItem - - - System.Windows.Forms.ToolStripMenuItem, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - sshToolStripMenuItem - - - System.Windows.Forms.ToolStripMenuItem, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - scpToolStripMenuItem - - - System.Windows.Forms.ToolStripMenuItem, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - toolStripMenuItem3 - - - System.Windows.Forms.ToolStripSeparator, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - addMapToolStripMenuItem - - - System.Windows.Forms.ToolStripMenuItem, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - addRelayMapToolStripMenuItem - - + System.Windows.Forms.ToolStripMenuItem, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 diff --git a/MeshCentralRouter.csproj b/MeshCentralRouter.csproj index 45ac68e..f11ef8f 100644 --- a/MeshCentralRouter.csproj +++ b/MeshCentralRouter.csproj @@ -106,6 +106,30 @@ InstallForm.cs + + UserControl + + + KVMControl.cs + + + UserControl + + + KVMResizeControl.cs + + + Form + + + KVMSettingsForm.cs + + + Form + + + KVMViewer.cs + Form @@ -394,6 +418,18 @@ InstallForm.cs + + KVMControl.cs + + + KVMResizeControl.cs + + + KVMSettingsForm.cs + + + KVMViewer.cs + MainForm.cs @@ -667,6 +703,7 @@ + diff --git a/MeshCentralServer.cs b/MeshCentralServer.cs index 2101e93..e013247 100644 --- a/MeshCentralServer.cs +++ b/MeshCentralServer.cs @@ -35,7 +35,7 @@ namespace MeshCentralRouter public class MeshCentralServer { - private Uri wsurl = null; + public Uri wsurl = null; private string user = null; private string pass = null; private string token = null; @@ -52,6 +52,7 @@ namespace MeshCentralRouter public bool disconnectSms2FASent = false; public X509Certificate2 disconnectCert; public string authCookie = null; + public string rauthCookie = null; public string loginCookie = null; public string wshash = null; public string certHash = null; @@ -157,6 +158,15 @@ namespace MeshCentralRouter } } + public void sendCommand(string cmd) + { + if (wc != null) + { + if (debug) { try { File.AppendAllText("debug.log", "sendCommand: " + cmd + "\r\n"); } catch (Exception) { } } + wc.WriteStringWebSocket(cmd); + } + } + public void refreshCookies() { if (wc != null) { @@ -209,6 +219,7 @@ namespace MeshCentralRouter case "authcookie": { authCookie = jsonAction["cookie"].ToString(); + rauthCookie = jsonAction["rcookie"].ToString(); changeState(2); break; } @@ -541,7 +552,7 @@ namespace MeshCentralRouter public delegate void onLoginTokenChangedHandler(); public event onLoginTokenChangedHandler onLoginTokenChanged; - private class xwebclient : IDisposable + public class xwebclient : IDisposable { private MeshCentralServer parent = null; private TcpClient wsclient = null; diff --git a/NodeClass.cs b/NodeClass.cs index c2b32c6..3d2fb5f 100644 --- a/NodeClass.cs +++ b/NodeClass.cs @@ -20,6 +20,7 @@ namespace MeshCentralRouter public MeshClass mesh; public ListViewItem listitem; public DeviceUserControl control; + public KVMViewer desktopViewer; public Dictionary links; public override string ToString() { return name; } diff --git a/Properties/Resources.Designer.cs b/Properties/Resources.Designer.cs index fcbdf9c..fcdfe63 100644 --- a/Properties/Resources.Designer.cs +++ b/Properties/Resources.Designer.cs @@ -60,6 +60,16 @@ namespace MeshCentralRouter.Properties { } } + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap _50_Computer { + get { + object obj = ResourceManager.GetObject("50-Computer", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + /// /// Looks up a localized string similar to Agent. /// diff --git a/Properties/Resources.resx b/Properties/Resources.resx index a8877bc..a9b120e 100644 --- a/Properties/Resources.resx +++ b/Properties/Resources.resx @@ -121,109 +121,112 @@ ..\Resources\Help20.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a - - ..\Resources\HelpRelayMap.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a - ..\Resources\Folder36.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + Search + ..\Resources\MC2-Banner.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a - - ..\Resources\MeshCentral.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + PuTTY SSH client - - ..\Resources\Gear20.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + WinSCP client - - ..\Resources\xinstall.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a - - - ..\Resources\icons01.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a - - - ..\Resources\Empty-Banner.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a - - - Agent + + ..\Resources\HelpRelayMap.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a AMT + + Changing language will close this tool. Are you sure? + + + Starting... + + + Unable to connect + CIRA + + Send token to registered email address? + + + Agent + + + Stopped. + + + ..\Resources\MeshCentral.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + Offline + + + SMS sent + + + ..\Resources\Gear20.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + Relay + + + ..\Resources\xinstall.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + Email sent Email verification required - - Invalid username or password - - - Changing language will close this tool. Are you sure? - - - Languages - - - , {0} connections. + + Send token to registered phone number? MeshCentral Router - - MeshCentral - - - MQTT - - - Offline - - - , 1 connection. - - - PuTTY SSH client - - - Relay - - - Search - - - Send token to registered email address? - - - Send token to registered phone number? - - - SMS sent - - - Starting... - - - Stopped. - Stopped + + , {0} connections. + + + ..\Resources\icons01.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + Two-factor Authentication + + MQTT + + + MeshCentral + + + Languages + + + ..\Resources\Empty-Banner.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + , 1 connection. + Unable to bind to local port - - Unable to connect + + Invalid username or password - - WinSCP client + + ..\Resources\50-Computer.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a \ No newline at end of file diff --git a/Resources/50-Computer.png b/Resources/50-Computer.png new file mode 100644 index 0000000000000000000000000000000000000000..ee1af6ed70438bd22cb8b805653af270843cf8f3 GIT binary patch literal 6722 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 Y0HFQOa|(GPiU0rr07*qoM6N<$g18OD>i_@% literal 0 HcmV?d00001 diff --git a/Resources/Padlock.png b/Resources/Padlock.png new file mode 100644 index 0000000000000000000000000000000000000000..2788d39cfb726f9a7a3b9c9ac58c214fd042dc04 GIT binary patch literal 5987 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 R&p7}9002ovPDHLkV1oK6ht&W8 literal 0 HcmV?d00001 diff --git a/Resources/User.png b/Resources/User.png new file mode 100644 index 0000000000000000000000000000000000000000..9a107e4ab8d29308c207c04719a22199154fa1de GIT binary patch literal 4761 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 literal 0 HcmV?d00001