1
0
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:
ylianst
2022-01-28 13:16:04 -08:00
parent e4c3af0682
commit 8743d00519
14 changed files with 1166 additions and 64 deletions

View File

@@ -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;
}
}
}