mirror of
https://github.com/Ylianst/MeshCentralRouter
synced 2025-12-16 00:03:28 +00:00
Added remote desktop split screen mode
This commit is contained in:
170
KVMControl.cs
170
KVMControl.cs
@@ -28,7 +28,7 @@ namespace MeshCentralRouter
|
||||
public partial class KVMControl : UserControl
|
||||
{
|
||||
private bool remotepause = true;
|
||||
private Bitmap desktop = null;
|
||||
public Bitmap desktop = null;
|
||||
private Graphics desktopGraphics = null;
|
||||
public uint screenWidth = 0;
|
||||
public uint screenHeight = 0;
|
||||
@@ -49,13 +49,17 @@ namespace MeshCentralRouter
|
||||
public double DpiX = 96;
|
||||
public double DpiY = 96;
|
||||
public KVMViewer parent = null;
|
||||
public KVMViewerExtra parentEx = null;
|
||||
private readonly KVMControlHook ControlHook;
|
||||
private readonly KVMControlHook.KVMCallback KeyboardCallback;
|
||||
private bool isHookWanted;
|
||||
private bool isHookPriority;
|
||||
private bool keyboardIsAttached;
|
||||
private long killNextKeyPress = 0;
|
||||
|
||||
private bool controlLoaded = false;
|
||||
public Rectangle[] displayInfo = null;
|
||||
public Rectangle displayCrop = Rectangle.Empty;
|
||||
public Point displayOrigin = Point.Empty;
|
||||
|
||||
private enum KvmCommands
|
||||
{
|
||||
@@ -79,6 +83,7 @@ namespace MeshCentralRouter
|
||||
Jumbo = 27,
|
||||
Disconnect = 59,
|
||||
Alert = 65,
|
||||
DisplayInfo = 82,
|
||||
KeyUnicode = 85,
|
||||
MouseCursor = 88
|
||||
}
|
||||
@@ -101,6 +106,9 @@ namespace MeshCentralRouter
|
||||
[Description("Fires when it receives the display list.")]
|
||||
public event EventHandler DisplaysReceived;
|
||||
|
||||
public delegate void ScreenAreaUpdatedHandler(Bitmap desktop, Rectangle r);
|
||||
public event ScreenAreaUpdatedHandler ScreenAreaUpdated;
|
||||
|
||||
public enum ConnectState
|
||||
{
|
||||
Disconnected,
|
||||
@@ -118,8 +126,14 @@ namespace MeshCentralRouter
|
||||
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; } }
|
||||
public int DesktopWidth
|
||||
{
|
||||
get { if (displayCrop == Rectangle.Empty) { return (int)screenWidth; } else { return displayCrop.Width; } }
|
||||
}
|
||||
public int DesktopHeight
|
||||
{
|
||||
get { if (displayCrop == Rectangle.Empty) { return (int)screenHeight; } else { return displayCrop.Height; } }
|
||||
}
|
||||
|
||||
// Debug
|
||||
public int bytesent = 0;
|
||||
@@ -217,8 +231,6 @@ namespace MeshCentralRouter
|
||||
ushort tile_x = (ushort)((buffer[off + 4] << 8) + buffer[off + 5]);
|
||||
ushort tile_y = (ushort)((buffer[off + 6] << 8) + buffer[off + 7]);
|
||||
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
|
||||
@@ -233,7 +245,40 @@ namespace MeshCentralRouter
|
||||
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);
|
||||
|
||||
// Update extra displays if needed
|
||||
Rectangle r = new Rectangle((int)tile_x, (int)tile_y, newtile.Width, newtile.Height);
|
||||
Rectangle rx = new Rectangle(r.X + displayOrigin.X, r.Y + displayOrigin.Y, r.Width, r.Height);
|
||||
if (ScreenAreaUpdated != null) ScreenAreaUpdated(desktop, rx);
|
||||
|
||||
if (displayCrop == Rectangle.Empty)
|
||||
{
|
||||
if (scalefactor == 1)
|
||||
{
|
||||
Invalidate(r, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
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);
|
||||
Invalidate(r3, false);
|
||||
}
|
||||
}
|
||||
else if (displayCrop.IntersectsWith(rx) == true)
|
||||
{
|
||||
Rectangle r2 = new Rectangle(rx.X, rx.Y, rx.Width, rx.Height);
|
||||
r2.Intersect(displayCrop);
|
||||
|
||||
if (scalefactor == 1)
|
||||
{
|
||||
Rectangle r3 = new Rectangle(r2.X - displayCrop.X, r2.Y - displayCrop.Y, r2.Width, r2.Height);
|
||||
Invalidate(r, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
Rectangle r3 = new Rectangle((int)((double)(r2.X - displayCrop.X) / (double)scalefactor) - 2, (int)((double)(r2.Y - displayCrop.Y) / (double)scalefactor) - 2, (int)((double)r2.Width / (double)scalefactor) + 4, (int)((double)r2.Height / (double)scalefactor) + 4);
|
||||
Invalidate(r3, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return blen + jumboHeaderSize;
|
||||
@@ -289,6 +334,31 @@ namespace MeshCentralRouter
|
||||
ChangeMouseCursor(buffer[off + 4]);
|
||||
break;
|
||||
}
|
||||
case KvmCommands.DisplayInfo:
|
||||
{
|
||||
if ((blen < 4) || (((blen - 4) % 10) != 0)) break;
|
||||
int screenCount = ((blen - 4) / 10);
|
||||
int ptr = off + 4;
|
||||
Rectangle[] xDisplayInfo = new Rectangle[screenCount];
|
||||
|
||||
for (var i = 0; i < screenCount; i++)
|
||||
{
|
||||
int id = ((buffer[ptr + 0] << 8) + buffer[ptr + 1]);
|
||||
int x = ((buffer[ptr + 2] << 8) + buffer[ptr + 3]);
|
||||
int y = ((buffer[ptr + 4] << 8) + buffer[ptr + 5]);
|
||||
int w = ((buffer[ptr + 6] << 8) + buffer[ptr + 7]);
|
||||
int h = ((buffer[ptr + 8] << 8) + buffer[ptr + 9]);
|
||||
if (x > 32766) { x -= 65536; }
|
||||
if (y > 32766) { y -= 65536; }
|
||||
Rectangle r = new Rectangle(x, y, w, h);
|
||||
xDisplayInfo[id - 1] = r;
|
||||
ptr += 10;
|
||||
}
|
||||
|
||||
// Set display information
|
||||
displayInfo = xDisplayInfo;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
// MessageBox.Show("Should never happen!");
|
||||
@@ -333,6 +403,11 @@ namespace MeshCentralRouter
|
||||
Invalidate(region);
|
||||
}
|
||||
|
||||
public void Repaint(Rectangle rect)
|
||||
{
|
||||
Invalidate(rect);
|
||||
}
|
||||
|
||||
protected override void OnPaintBackground(PaintEventArgs e)
|
||||
{
|
||||
// Do not paint background.
|
||||
@@ -355,14 +430,28 @@ namespace MeshCentralRouter
|
||||
{
|
||||
lock (desktop)
|
||||
{
|
||||
if (scalefactor == 1)
|
||||
if (displayCrop == Rectangle.Empty) // No cropping
|
||||
{
|
||||
g.DrawImage(desktop, e.ClipRectangle, e.ClipRectangle, GraphicsUnit.Pixel);
|
||||
if (scalefactor == 1)
|
||||
{
|
||||
g.DrawImage(desktop, e.ClipRectangle, e.ClipRectangle, GraphicsUnit.Pixel);
|
||||
}
|
||||
else
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
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);
|
||||
if (scalefactor == 1) // Cropping in effect, this is when we show different displays in different windows
|
||||
{
|
||||
g.DrawImage(desktop, e.ClipRectangle, new Rectangle(e.ClipRectangle.X - displayOrigin.X + displayCrop.X, e.ClipRectangle.Y - displayOrigin.Y + displayCrop.Y, e.ClipRectangle.Width, e.ClipRectangle.Height), GraphicsUnit.Pixel);
|
||||
}
|
||||
else
|
||||
{
|
||||
Rectangle srcRect = new Rectangle((int)((double)(e.ClipRectangle.Left) * (double)scalefactor) - displayOrigin.X + displayCrop.X, (int)((double)(e.ClipRectangle.Top) * (double)scalefactor) - displayOrigin.Y + displayCrop.Y, (int)((double)e.ClipRectangle.Width * (double)scalefactor), (int)((double)e.ClipRectangle.Height * (double)scalefactor));
|
||||
g.DrawImage((Image)desktop, e.ClipRectangle, srcRect, GraphicsUnit.Pixel);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -479,8 +568,16 @@ namespace MeshCentralRouter
|
||||
}
|
||||
}
|
||||
|
||||
LastX = (short)((double)e.X * (double)scalefactor);
|
||||
LastY = (short)((double)e.Y * (double)scalefactor);
|
||||
if (displayCrop == Rectangle.Empty)
|
||||
{
|
||||
LastX = (short)((double)e.X * (double)scalefactor);
|
||||
LastY = (short)((double)e.Y * (double)scalefactor);
|
||||
}
|
||||
else
|
||||
{
|
||||
LastX = (short)((int)((double)e.X * (double)scalefactor) - displayOrigin.X + displayCrop.X);
|
||||
LastY = (short)((int)((double)e.Y * (double)scalefactor) - displayOrigin.Y + displayCrop.Y);
|
||||
}
|
||||
|
||||
BinaryWriter bw = GetBinaryWriter();
|
||||
bw.Write(IPAddress.HostToNetworkOrder((short)KvmCommands.Mouse));
|
||||
@@ -563,9 +660,16 @@ namespace MeshCentralRouter
|
||||
//if (state == ConnectState.Disconnected) return;
|
||||
try
|
||||
{
|
||||
parent.bytesOut += buffer.Length;
|
||||
parent.bytesOutCompressed += parent.wc.SendBinary(buffer, 0, buffer.Length);
|
||||
bytesent += buffer.Length;
|
||||
if (parent != null)
|
||||
{
|
||||
parent.bytesOut += buffer.Length;
|
||||
parent.bytesOutCompressed += parent.wc.SendBinary(buffer, 0, buffer.Length);
|
||||
bytesent += buffer.Length;
|
||||
}
|
||||
else if (parentEx != null)
|
||||
{
|
||||
parentEx.mainKvmControl.Send(buffer);
|
||||
}
|
||||
}
|
||||
catch (Exception) { }
|
||||
}
|
||||
@@ -575,9 +679,16 @@ namespace MeshCentralRouter
|
||||
//if (state == ConnectState.Disconnected) return;
|
||||
try
|
||||
{
|
||||
parent.bytesOut += str.Length;
|
||||
parent.bytesOutCompressed += parent.wc.SendString(str);
|
||||
bytesent += str.Length;
|
||||
if (parent != null)
|
||||
{
|
||||
parent.bytesOut += str.Length;
|
||||
parent.bytesOutCompressed += parent.wc.SendString(str);
|
||||
bytesent += str.Length;
|
||||
}
|
||||
else if (parentEx != null)
|
||||
{
|
||||
parentEx.mainKvmControl.Send(str);
|
||||
}
|
||||
}
|
||||
catch (Exception) { }
|
||||
}
|
||||
@@ -595,6 +706,10 @@ namespace MeshCentralRouter
|
||||
}
|
||||
catch (Exception) { }
|
||||
}
|
||||
else if (parentEx != null)
|
||||
{
|
||||
parentEx.mainKvmControl.Send(bw);
|
||||
}
|
||||
RecycleBinaryWriter(bw);
|
||||
}
|
||||
|
||||
@@ -738,5 +853,22 @@ namespace MeshCentralRouter
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public void cropDisplay(Point o, Rectangle r)
|
||||
{
|
||||
if (IsDisposed || Disposing) return;
|
||||
displayCrop = r;
|
||||
displayOrigin = o;
|
||||
if (controlLoaded)
|
||||
{
|
||||
Invoke(new SetSizeHandler(SetSize));
|
||||
Invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
private void KVMControl_Load(object sender, EventArgs e)
|
||||
{
|
||||
controlLoaded = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user