Mercurial > hg > ucis.core
annotate VNCServer/VNCServer.cs @ 111:df53bdd49507 default tip
Merge
author | Ivo Smits <Ivo@UCIS.nl> |
---|---|
date | Fri, 07 Nov 2014 18:37:39 +0100 |
parents | 2ecb82eea559 |
children |
rev | line source |
---|---|
23 | 1 ???using System; |
2 using System.Collections.Generic; | |
3 using System.Diagnostics; | |
4 using System.Drawing; | |
5 using System.Drawing.Drawing2D; | |
6 using System.Drawing.Imaging; | |
7 using System.IO; | |
8 using System.Net; | |
9 using System.Net.Sockets; | |
10 using System.Security.Cryptography; | |
11 using System.Text; | |
12 using System.Threading; | |
13 using System.Windows.Forms; | |
14 using UCIS.Net.HTTP; | |
15 | |
16 namespace UCIS.VNCServer { | |
17 public class VNCServerManager : IFramebuffer { | |
18 private VNCFramebuffer fb; | |
19 private List<VNCServerConnection> clients = new List<VNCServerConnection>(); | |
20 private VNCServer server; | |
21 | |
22 public event MouseEventHandler MouseDown; | |
23 public event MouseEventHandler MouseMove; | |
24 public event MouseEventHandler MouseUp; | |
25 public event KeyEventHandler KeyDown; | |
26 public event KeyEventHandler KeyUp; | |
27 | |
28 public VNCServerManager(int w, int h) : this(w, h, 5901) { } | |
29 public VNCServerManager(int w, int h, int p) { | |
30 fb = new VNCFramebuffer(w, h); | |
31 server = new VNCServer(p); | |
32 server.ClientConnected += delegate(object sender, VNCClientConnectedEventArgs e) { | |
33 e.Client.Framebuffer = fb; | |
34 e.Client.MouseDown += MouseDown; | |
35 e.Client.MouseMove += MouseMove; | |
36 e.Client.MouseUp += MouseUp; | |
37 e.Client.KeyDown += KeyDown; | |
38 e.Client.KeyUp += KeyUp; | |
39 e.Client.Disconnected += ClientDisconnected; | |
40 lock (clients) clients.Add(e.Client); | |
41 }; | |
42 server.Listen(); | |
43 } | |
44 | |
45 private void ClientDisconnected(Object sender, EventArgs e) { | |
46 lock (clients) clients.Remove((VNCServerConnection)sender); | |
47 } | |
48 | |
49 public void Close() { | |
50 server.Close(); | |
51 foreach (VNCServerConnection c in clients.ToArray()) c.Close(); | |
52 } | |
53 | |
54 public int Width { | |
55 get { return fb.Width; } | |
56 } | |
57 public int Height { | |
58 get { return fb.Height; } | |
59 } | |
60 public void Clear() { | |
61 fb.Clear(); | |
62 } | |
63 public void DrawImage(Image image, Rectangle srcrect, Point dest) { | |
64 fb.DrawImage(image, srcrect, dest); | |
65 } | |
66 public void DrawPixels(int[] bitmap, int bmwidth, Rectangle srcrect, Point dest) { | |
67 fb.DrawPixels(bitmap, bmwidth, srcrect, dest); | |
68 } | |
69 public void DrawPixels(IntPtr bitmap, int bmwidth, Rectangle srcrect, Point dest) { | |
70 fb.DrawPixels(bitmap, bmwidth, srcrect, dest); | |
71 } | |
72 public void CopyRectangle(Rectangle srcrect, Point dest) { | |
73 fb.CopyRectangle(srcrect, dest); | |
74 } | |
75 public void CopyRectangleTo(Rectangle srcrect, IFramebuffer destbuffer, Point destposition) { | |
76 fb.CopyRectangleTo(srcrect, destbuffer, destposition); | |
77 } | |
78 | |
79 public void Resize(int w, int h) { | |
80 fb = new VNCFramebuffer(w, h); | |
81 foreach (VNCServerConnection c in clients) c.Framebuffer = fb; | |
82 } | |
83 } | |
84 public class VNCFramebufferUpdateEventArgs : EventArgs { | |
85 public VNCFramebuffer Framebuffer { get; private set; } | |
86 public Rectangle Area { get; private set; } | |
87 internal VNCFramebufferUpdateEventArgs(VNCFramebuffer fb, Rectangle a) { | |
88 this.Framebuffer = fb; | |
89 this.Area = a; | |
90 } | |
91 } | |
92 public class VNCFramebuffer : IFramebuffer { | |
93 public event EventHandler<VNCFramebufferUpdateEventArgs> Update; | |
94 internal Int32[] Framebuffer { get; private set; } | |
95 public int Width { get; private set; } | |
96 public int Height { get; private set; } | |
97 public VNCFramebuffer(int width, int height) { | |
98 if (width <= 0) throw new ArgumentOutOfRangeException("width"); | |
99 if (height <= 0) throw new ArgumentOutOfRangeException("height"); | |
100 this.Width = width; | |
101 this.Height = height; | |
102 this.Framebuffer = new Int32[width * height]; | |
103 } | |
104 public void Clear() { | |
105 for (int i = 0; i < Width * Height; i++) Framebuffer[i] = 0; | |
106 EventHandler<VNCFramebufferUpdateEventArgs> eh = Update; | |
107 if (eh != null) eh(this, new VNCFramebufferUpdateEventArgs(this, new Rectangle(0, 0, Width, Height))); | |
108 } | |
109 public unsafe void DrawImage(Image image, Rectangle srcrect, Point dest) { | |
110 if (srcrect.Width == 0 || srcrect.Height == 0) return; | |
111 fixed (int* fbptr = Framebuffer) { | |
112 using (Bitmap b = new Bitmap(Width, Height, Width * 4, PixelFormat.Format32bppRgb, (IntPtr)fbptr)) { | |
113 using (Graphics g = Graphics.FromImage(b)) { | |
114 g.CompositingMode = CompositingMode.SourceCopy; | |
115 g.DrawImage(image, new Rectangle(dest, srcrect.Size), srcrect, GraphicsUnit.Pixel); | |
116 } | |
117 } | |
118 } | |
119 EventHandler<VNCFramebufferUpdateEventArgs> eh = Update; | |
120 if (eh != null) eh(this, new VNCFramebufferUpdateEventArgs(this, new Rectangle(dest, srcrect.Size))); | |
121 } | |
122 public void DrawBitmap(Bitmap bitmap, Rectangle srcrect, Point dest) { | |
123 DrawImage(bitmap, srcrect, dest); | |
124 } | |
125 public unsafe void DrawPixels(int[] bitmap, int bmwidth, Rectangle srcrect, Point dest) { | |
126 fixed (int* bmp = bitmap) DrawPixels(bmp, bmwidth, srcrect, dest); | |
127 } | |
128 public unsafe void DrawPixels(IntPtr bitmap, int bmwidth, Rectangle srcrect, Point dest) { | |
129 DrawPixels((int*)bitmap, bmwidth, srcrect, dest); | |
130 } | |
131 public unsafe void DrawPixels(int* bitmap, int bmwidth, Rectangle srcrect, Point dest) { | |
132 if (srcrect.X < 0 || srcrect.Y < 0 || srcrect.Width < 0 || srcrect.Height < 0) throw new ArgumentOutOfRangeException("srcrect"); | |
133 if (dest.X < 0 || dest.Y < 0 || dest.X + srcrect.Width > Width || dest.Y + srcrect.Height > Height) throw new ArgumentOutOfRangeException("dest"); | |
134 if (srcrect.Width == 0 || srcrect.Height == 0) return; | |
135 int* bmin = bitmap + srcrect.Y * bmwidth + srcrect.X; | |
136 //Optionally detect regions that have actually changed. This produces many small regions which may slow down the Tight JPEG encoder (and ZLib based codecs) | |
137 //DrawChangedPixels(Framebuffer, dest.Y * Width + dest.X, bmin, srcrect.Width, srcrect.Height, bmwidth, dest.X, dest.Y); | |
138 //return; | |
139 fixed (int* fbptr = Framebuffer) { | |
140 int* bmout = fbptr + dest.Y * Width + dest.X; | |
141 for (int y = 0; y < srcrect.Height; y++) { | |
142 int* bminl = bmin + y * bmwidth; | |
143 int* bmoutl = bmout + y * Width; | |
144 for (int x = 0; x < srcrect.Width; x++) { | |
145 bmoutl[x] = bminl[x]; | |
146 } | |
147 } | |
148 } | |
149 EventHandler<VNCFramebufferUpdateEventArgs> eh = Update; | |
150 if (eh != null) eh(this, new VNCFramebufferUpdateEventArgs(this, new Rectangle(dest, srcrect.Size))); | |
151 } | |
152 private unsafe void DrawChangedPixels(int[] shadow, int shadowoffset, int* pixels, int width, int height, int bmwidth, int xoffset, int yoffset) { | |
153 EventHandler<VNCFramebufferUpdateEventArgs> eh = Update; | |
154 if (eh == null) return; | |
155 int firstx = -1, lastx = -1, firsty = -1, lasty = -1; | |
156 for (int y = 0; y < height; y++) { | |
157 int firstxline = -1, lastxline = -1; | |
158 for (int x = 0; x < width; x++) { | |
159 if (shadow[shadowoffset] != *pixels) { | |
160 if (firstxline == -1) firstxline = x; | |
161 lastxline = x; | |
162 shadow[shadowoffset] = *pixels; | |
163 } | |
164 shadowoffset++; | |
165 pixels++; | |
166 } | |
167 shadowoffset += Width - width; | |
168 pixels += bmwidth - width; | |
169 if (firsty != -1 && firstxline == -1) { | |
170 eh(this, new VNCFramebufferUpdateEventArgs(this, new Rectangle(firstx + xoffset, firsty + yoffset, lastx - firstx + 1, lasty - firsty + 1))); | |
171 firsty = lasty = -1; | |
172 } else if (firstxline != -1) { | |
173 if (firsty == -1) { | |
174 firsty = y; | |
175 firstx = firstxline; | |
176 lastx = lastxline; | |
177 } else { | |
178 if (firstxline < firstx) firstx = firstxline; | |
179 if (lastxline > lastx) lastx = lastxline; | |
180 } | |
181 lasty = y; | |
182 } | |
183 } | |
184 if (firsty != -1) { | |
185 eh(this, new VNCFramebufferUpdateEventArgs(this, new Rectangle(firstx + xoffset, firsty + yoffset, lastx - firstx + 1, lasty - firsty + 1))); | |
186 } | |
187 } | |
188 public void CopyRectangle(Rectangle srcrect, Point dest) { | |
189 DrawPixels(Framebuffer, Width, srcrect, dest); | |
190 } | |
191 public void CopyRectangleTo(Rectangle srcrect, IFramebuffer destbuffer, Point destposition) { | |
192 destbuffer.DrawPixels(Framebuffer, Width, srcrect, destposition); | |
193 } | |
194 } | |
195 struct RFBPixelFormat { | |
196 public Byte BitsPerPixel { get; set; } | |
197 public Byte ColorDepth { get; set; } | |
198 public Boolean BigEndian { get; set; } | |
199 public Boolean TrueColor { get; set; } | |
200 public UInt16 RedMax { get; set; } | |
201 public UInt16 GreenMax { get; set; } | |
202 public UInt16 BlueMax { get; set; } | |
203 public Byte RedShift { get; set; } | |
204 public Byte GreenShift { get; set; } | |
205 public Byte BlueShift { get; set; } | |
206 } | |
207 public class VNCClientConnectedEventArgs : EventArgs { | |
208 public VNCServer Server { get; private set; } | |
209 public EndPoint RemoteEndPoint { get; private set; } | |
210 public VNCServerConnection Client { get; private set; } | |
211 public Boolean Drop { get; set; } | |
212 public Boolean AllowNoAuthentication { get; set; } | |
213 public Boolean AllowPasswordAuthentication { get; set; } | |
214 public VNCClientConnectedEventArgs(VNCServer serv, VNCServerConnection c, EndPoint ep) { | |
215 this.Server = serv; | |
216 this.RemoteEndPoint = ep; | |
217 this.Client = c; | |
218 this.Drop = false; | |
219 this.AllowNoAuthentication = true; | |
220 this.AllowPasswordAuthentication = false; | |
221 } | |
222 } | |
223 public class VNCClientAuthenticationEventArgs : EventArgs { | |
224 public VNCServerConnection Client { get; private set; } | |
225 public Boolean Drop { get; set; } | |
226 public Boolean UsedPasswordAuthentication { get; internal set; } | |
227 public String DesktopName { get; set; } | |
228 internal Byte[] VNCAuthChallenge { private get; set; } | |
229 internal Byte[] VNCAuthResponse { private get; set; } | |
230 internal VNCClientAuthenticationEventArgs(VNCServerConnection c) { | |
231 this.Client = c; | |
232 this.Drop = false; | |
233 } | |
234 public Boolean CheckPassword(String password) { | |
235 return CheckPassword(Encoding.ASCII.GetBytes(password)); | |
236 } | |
237 public Boolean CheckPassword(Byte[] password) { | |
238 Byte[] passwordtransform = new Byte[8]; | |
239 for (int i = 0; i < 8 && i < password.Length; i++) { | |
240 Byte a = password[i], b = 0; | |
241 for (int j = 0; j < 8; j++) b |= (Byte)(((a >> (7 - j)) & 1) << j); | |
242 passwordtransform[i] = b; | |
243 } | |
244 byte[] check; | |
245 using (DES des = new DESCryptoServiceProvider()) { | |
246 des.Mode = CipherMode.ECB; | |
247 des.Padding = PaddingMode.None; | |
248 using (ICryptoTransform transform = des.CreateEncryptor(passwordtransform, null)) { | |
249 check = transform.TransformFinalBlock(VNCAuthChallenge, 0, 16); | |
250 } | |
251 } | |
252 for (int i = 0; i < 16; i++) if (VNCAuthResponse[i] != check[i]) return false; | |
253 return true; | |
254 } | |
255 } | |
29
2ecb82eea559
VNCServer: small change in web client handling
Ivo Smits <Ivo@UCIS.nl>
parents:
23
diff
changeset
|
256 public class VNCServer : IHTTPContentProvider { |
23 | 257 public event EventHandler<VNCClientConnectedEventArgs> ClientConnected; |
258 public EndPoint LocalEndPoint { get; protected set; } | |
259 public Boolean Listening { get; protected set; } | |
260 private Socket socket = null; | |
261 public VNCServer() : this(null) { } | |
262 public VNCServer(int port) : this(new IPEndPoint(IPAddress.Any, port)) { } | |
263 public VNCServer(EndPoint ep) { | |
264 LocalEndPoint = ep; | |
265 } | |
266 public void Listen() { | |
267 if (LocalEndPoint == null) throw new ArgumentNullException("LocalEndPoint"); | |
268 Listen(LocalEndPoint); | |
269 } | |
270 public void Listen(int port) { | |
271 Listen(new IPEndPoint(IPAddress.Any, port)); | |
272 } | |
273 public virtual void Listen(EndPoint ep) { | |
274 if (Listening) throw new InvalidOperationException("The server is already listening"); | |
275 socket = new Socket(ep.AddressFamily, SocketType.Stream, ProtocolType.Unspecified); | |
276 socket.Bind(ep); | |
277 socket.Listen(5); | |
278 LocalEndPoint = ep; | |
279 Listening = true; | |
280 socket.BeginAccept(AcceptCallback, socket); | |
281 } | |
282 public virtual void Close() { | |
283 if (!Listening) throw new InvalidOperationException("The server is not listening"); | |
284 Listening = false; | |
285 socket.Close(); | |
286 } | |
287 private void AcceptCallback(IAsyncResult ar) { | |
288 Socket socket = ar.AsyncState as Socket; | |
289 try { | |
290 Socket clientsock = socket.EndAccept(ar); | |
291 if (clientsock.ProtocolType == ProtocolType.Tcp) clientsock.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, true); | |
292 ClientAccepted(new VNCServerConnection(clientsock)); | |
293 } catch (SocketException ex) { | |
294 Debug.WriteLine(ex); | |
295 } catch (ObjectDisposedException ex) { | |
296 Debug.WriteLine(ex); | |
297 } | |
298 if (Listening) socket.BeginAccept(AcceptCallback, socket); | |
299 } | |
29
2ecb82eea559
VNCServer: small change in web client handling
Ivo Smits <Ivo@UCIS.nl>
parents:
23
diff
changeset
|
300 void IHTTPContentProvider.ServeRequest(HTTPContext context) { |
2ecb82eea559
VNCServer: small change in web client handling
Ivo Smits <Ivo@UCIS.nl>
parents:
23
diff
changeset
|
301 WebSocketPacketStream stream = new WebSocketPacketStream(context); |
2ecb82eea559
VNCServer: small change in web client handling
Ivo Smits <Ivo@UCIS.nl>
parents:
23
diff
changeset
|
302 ClientAccepted(new VNCServerConnection(stream, context.Socket)); |
2ecb82eea559
VNCServer: small change in web client handling
Ivo Smits <Ivo@UCIS.nl>
parents:
23
diff
changeset
|
303 } |
23 | 304 protected void ClientAccepted(VNCServerConnection client) { |
305 VNCClientConnectedEventArgs clientargs = new VNCClientConnectedEventArgs(this, client, client.RemoteEndPoint); | |
306 if (ClientConnected != null) ClientConnected(this, clientargs); | |
307 if (clientargs.Drop) { | |
308 client.Close(); | |
309 } else { | |
310 client.RunAsync(clientargs); | |
311 } | |
312 } | |
313 } | |
314 public class WebVNCServer : VNCServer { | |
315 private HTTPServer httpserver; | |
316 public WebVNCServer() : this(null) { } | |
317 public WebVNCServer(int port) : this(new IPEndPoint(IPAddress.Any, port)) { } | |
318 public WebVNCServer(EndPoint ep) : base(ep) { | |
319 httpserver = new HTTPServer(); | |
29
2ecb82eea559
VNCServer: small change in web client handling
Ivo Smits <Ivo@UCIS.nl>
parents:
23
diff
changeset
|
320 httpserver.ContentProvider = this; |
23 | 321 httpserver.ServeFlashPolicyFile = true; |
322 } | |
323 public override void Listen(EndPoint ep) { | |
324 httpserver.Listen(ep); | |
325 } | |
326 public override void Close() { | |
327 httpserver.Dispose(); | |
328 } | |
329 } | |
330 public interface IZLibCompressor { | |
331 byte[] Compress(Byte[] data, int offset, int count); | |
332 } | |
333 public delegate IZLibCompressor ZLibCompressorFactory(); | |
334 public class VNCServerConnection { | |
335 VNCFramebuffer framebuffer = null; | |
336 List<Rectangle> dirtyrects = new List<Rectangle>(); | |
337 int waitingforupdate = 0; | |
338 Rectangle waitingforupdaterect = Rectangle.Empty; | |
339 Int32[] SupportedEncodings = new Int32[] { }; | |
340 MouseButtons mousebuttons = MouseButtons.None; | |
341 Boolean resized = false; | |
342 Thread worker = null; | |
343 Keys modifierKeys = Keys.None; | |
344 MemoryStream SendBuffer = null; | |
345 Stream socket; | |
346 Socket realsocket; | |
347 Int32 protover; | |
348 RFBPixelFormat pixelformat; | |
349 int jpegCounter = 0; | |
350 Rectangle blurryrect = Rectangle.Empty; | |
351 int blurryrecoveryline = 0; | |
352 Point mousePosition = new Point(-1, -1); | |
353 ZLibCompressorFactory ZLibFactory = null; | |
354 | |
355 public int Width { get; private set; } | |
356 public int Height { get; private set; } | |
357 public Object Tag { get; set; } | |
358 | |
359 public EndPoint RemoteEndPoint { get { return realsocket == null ? null : realsocket.RemoteEndPoint; } } | |
360 public VNCFramebuffer Framebuffer { | |
361 get { return framebuffer; } | |
362 set { | |
363 if (framebuffer != null) { | |
364 framebuffer.Update -= FBUpdate; | |
365 } | |
366 framebuffer = value; | |
367 if (value == null) return; | |
368 lock (dirtyrects) { | |
369 resized = true; | |
370 dirtyrects.Clear(); | |
371 framebuffer.Update += FBUpdate; | |
372 } | |
373 FBUpdate(this, new VNCFramebufferUpdateEventArgs(value, new Rectangle(0, 0, framebuffer.Width, framebuffer.Height))); | |
374 } | |
375 } | |
376 | |
377 public event MouseEventHandler MouseDown; | |
378 public event MouseEventHandler MouseMove; | |
379 public event MouseEventHandler MouseUp; | |
380 public event KeyEventHandler KeyDown; | |
381 public event KeyEventHandler KeyUp; | |
382 | |
383 public event EventHandler Disconnected; | |
384 | |
385 public event EventHandler UpdateRequested; | |
386 public event EventHandler WaitingForUpdate; | |
387 | |
388 public event EventHandler<VNCClientAuthenticationEventArgs> ClientAuthentication; | |
389 public event EventHandler ConnectionComplete; | |
390 | |
391 public VNCServerConnection(Socket client) : this(new NetworkStream(client, true), client) { | |
392 realsocket.SendBufferSize = 1024 * 1024; | |
393 } | |
394 public VNCServerConnection(Stream client) : this(client, null) { } | |
395 public VNCServerConnection(Stream client, Socket socket) { | |
396 this.socket = client; | |
397 this.realsocket = socket; | |
398 pixelformat = new RFBPixelFormat() { | |
399 BigEndian = false, BitsPerPixel = 32, ColorDepth = 24, TrueColor = true, | |
400 BlueMax = 255, BlueShift = 0, GreenMax = 255, GreenShift = 8, RedMax = 255, RedShift = 16 | |
401 }; | |
402 } | |
403 | |
404 public void Close() { | |
405 socket.Close(); | |
406 } | |
407 | |
408 public void RunAsync(VNCClientConnectedEventArgs args) { | |
409 worker = new Thread(RunSafe); | |
410 worker.Start(args); | |
411 } | |
412 private void RunSafe(Object state) { | |
413 try { | |
414 RunProc((VNCClientConnectedEventArgs)state); | |
415 } catch (Exception ex) { | |
416 Console.Error.WriteLine(ex); | |
417 } finally { | |
418 try { socket.Close(); } catch (Exception ex) { Console.Error.WriteLine(ex); } | |
419 if (Disconnected != null) Disconnected(this, new EventArgs()); | |
420 } | |
421 } | |
422 private void RunProc(VNCClientConnectedEventArgs connargs) { | |
423 Initialisation(connargs); | |
424 ReceiveLoop(); | |
425 } | |
426 | |
427 private void Initialisation(VNCClientConnectedEventArgs connargs) { | |
428 { | |
429 Byte[] protovbuf = Encoding.ASCII.GetBytes("RFB 003.008\n"); | |
430 SendAll(protovbuf); | |
431 FlushSendBuffer(); | |
432 protovbuf = ReceiveAll(12); | |
433 String protovs = Encoding.ASCII.GetString(protovbuf); | |
434 protover = int.Parse(protovs.Substring(4, 3)) * 1000 + int.Parse(protovs.Substring(8, 3)); | |
435 } | |
436 //Console.WriteLine("Client protocol is {0} = {1}", protovs.TrimEnd('\n'), protover); | |
437 if (protover < 3003) throw new InvalidOperationException("Unsupported protocol version"); | |
438 VNCClientAuthenticationEventArgs authargs = new VNCClientAuthenticationEventArgs(this); | |
439 if (protover >= 3007) { | |
440 if (connargs.AllowNoAuthentication && connargs.AllowPasswordAuthentication) { | |
441 SendAll(new Byte[] { 2, 1, 2 }); //2 security types, no security, VNC security | |
442 } else if (connargs.AllowNoAuthentication) { | |
443 SendAll(new Byte[] { 1, 1 }); //1 security type, none security | |
444 } else if (connargs.AllowPasswordAuthentication) { | |
445 SendAll(new Byte[] { 1, 2 }); //1 security type, VNC security | |
446 } else { | |
447 SendAll(new Byte[] { 0 }); //no security types, drop connection | |
448 throw new InvalidOperationException("No security types allowed"); | |
449 } | |
450 FlushSendBuffer(); | |
451 Byte[] sectype = ReceiveAll(1); | |
452 if (sectype[0] == 1 && connargs.AllowNoAuthentication) { | |
453 authargs.UsedPasswordAuthentication = false; | |
454 } else if (sectype[0] == 2 && connargs.AllowPasswordAuthentication) { | |
455 authargs.UsedPasswordAuthentication = true; | |
456 } else throw new InvalidOperationException("Unsupported security type"); | |
457 } else if (protover == 3003) { | |
458 if (connargs.AllowNoAuthentication) { | |
459 SendUInt32(1); //Security type is none | |
460 authargs.UsedPasswordAuthentication = false; | |
461 } else if (connargs.AllowPasswordAuthentication) { | |
462 SendUInt32(2); //Security type is VNC security | |
463 authargs.UsedPasswordAuthentication = true; | |
464 } else { | |
465 SendUInt32(0); //no security types, drop connection | |
466 throw new InvalidOperationException("No security types allowed"); | |
467 } | |
468 FlushSendBuffer(); | |
469 } else { | |
470 throw new InvalidOperationException("Unsupported protocol version"); | |
471 } | |
472 if (authargs.UsedPasswordAuthentication) { | |
473 Byte[] challenge = new Byte[16]; | |
474 (new RNGCryptoServiceProvider()).GetBytes(challenge); | |
475 SendAll(challenge); | |
476 FlushSendBuffer(); | |
477 authargs.VNCAuthChallenge = challenge; | |
478 authargs.VNCAuthResponse = ReceiveAll(16); | |
479 } | |
480 if (ClientAuthentication != null) ClientAuthentication(this, authargs); | |
481 if (authargs.Drop) { | |
482 SendUInt32(1); //Security not OK | |
483 FlushSendBuffer(); | |
484 throw new Exception("Authentication rejected"); | |
485 } | |
486 if (authargs.UsedPasswordAuthentication || protover >= 3008) SendUInt32(0); //Security OK | |
487 FlushSendBuffer(); | |
488 Byte[] clientinit = ReceiveAll(1); | |
489 //Console.WriteLine("Shared = {0}", clientinit[0]); | |
490 { | |
491 VNCFramebuffer fb = framebuffer; | |
492 if (fb != null) { | |
493 Width = fb.Width; | |
494 Height = fb.Height; | |
495 } else { | |
496 Width = 1024; | |
497 Height = 768; | |
498 } | |
499 } | |
500 resized = false; | |
501 SendUInt16((UInt16)Width); | |
502 SendUInt16((UInt16)Height); | |
503 SendPixelFormat(pixelformat); | |
504 { | |
505 Byte[] desknamestr; | |
506 if (authargs.DesktopName == null) desknamestr = new Byte[0]; | |
507 else desknamestr = Encoding.ASCII.GetBytes(authargs.DesktopName); | |
508 SendUInt32((UInt32)desknamestr.Length); | |
509 SendAll(desknamestr); | |
510 } | |
511 FlushSendBuffer(); | |
512 if (ConnectionComplete != null) ConnectionComplete(this, new EventArgs()); | |
513 } | |
514 | |
515 private void ReceiveLoop() { | |
516 while (true) { | |
517 Byte mtype = ReceiveByte(); | |
518 switch (mtype) { | |
519 case 0: //SetPixelFormat | |
520 ReceiveMessageSetPixelFormat(); | |
521 break; | |
522 case 2: //SetEncodings | |
523 Byte[] b = ReceiveAll(3); | |
524 b = ReceiveAll(4 * ((b[1] << 8) | b[2])); | |
525 SupportedEncodings = new Int32[b.Length / 4]; | |
526 for (int i = 0; i < b.Length; i += 4) SupportedEncodings[i / 4] = (b[i] << 24) | (b[i + 1] << 16) | (b[i + 2] << 8) | b[i + 3]; | |
527 break; | |
528 case 3: //FramebufferUpdateRequest | |
529 b = ReceiveAll(9); | |
530 Rectangle r = new Rectangle((b[1] << 8) | b[2], (b[3] << 8) | b[4], (b[5] << 8) | b[6], (b[7] << 8) | b[8]); | |
531 SendQueuedRectangles(r, b[0] != 0); | |
532 break; | |
533 case 4: //KeyEvent | |
534 ReceiveMessageKeyboard(); | |
535 break; | |
536 case 5: //PointerEvent | |
537 ReceiveMessageMouse(); | |
538 break; | |
539 case 6: //ClientCutText | |
540 b = ReceiveAll(7); | |
541 ReceiveAll((b[3] << 24) | (b[4] << 16) | (b[5] << 8) | b[6]); | |
542 break; | |
543 default: | |
544 throw new InvalidOperationException("Received unknown message type, synchronization is lost"); | |
545 } | |
546 } | |
547 } | |
548 private void ReceiveMessageSetPixelFormat() { | |
549 Byte[] b = ReceiveAll(3); | |
550 pixelformat = ReceivePixelFormat(); | |
551 if (!pixelformat.TrueColor) { | |
552 //I don't want to use a pallette, so I cheat by sending a 8bpp "true color" pallette | |
553 pixelformat.TrueColor = true; | |
554 pixelformat.BigEndian = false; | |
555 pixelformat.RedShift = 0; | |
556 pixelformat.RedMax = 3; | |
557 pixelformat.GreenShift = 2; | |
558 pixelformat.GreenMax = 7; | |
559 pixelformat.BlueShift = 5; | |
560 pixelformat.BlueMax = 7; | |
561 lock (socket) { | |
562 SendUInt8(1); //SetColourMapEntries | |
563 SendUInt8(0); //Padding | |
564 SendUInt16(0); //First colour | |
565 SendUInt16(256); //Number of colours | |
566 for (UInt16 blue = 0; blue < 256; blue += 32) { | |
567 for (UInt16 green = 0; green < 256; green += 32) { | |
568 for (UInt16 red = 0; red < 256; red += 64) { | |
569 SendUInt16((UInt16)(red << 8)); | |
570 SendUInt16((UInt16)(green << 8)); | |
571 SendUInt16((UInt16)(blue << 8)); | |
572 } | |
573 } | |
574 } | |
575 FlushSendBuffer(); | |
576 } | |
577 } | |
578 } | |
579 private void ReceiveMessageKeyboard() { | |
580 Byte[] b = ReceiveAll(7); | |
581 Boolean down = b[0] != 0; | |
582 uint key = (uint)((b[3] << 24) | (b[4] << 16) | (b[5] << 8) | b[6]); | |
583 //Console.WriteLine("KeyEvent code=0x{0:x} down={1}", key, down); | |
584 Keys keyval = Keys.None; | |
585 //see: http://cgit.freedesktop.org/xorg/proto/x11proto/plain/keysymdef.h | |
586 if (key >= 'A' && key <= 'Z') keyval = (Keys)(key + (int)Keys.A - 'A') | Keys.Shift; | |
587 else if (key >= 'a' && key <= 'z') keyval = (Keys)(key + (int)Keys.A - 'a'); | |
588 else if (key >= '0' && key <= '9') keyval = (Keys)(key + (int)Keys.D0 - '0'); | |
589 else if (key >= 0xffb0 && key <= 0xffb9) keyval = (Keys)(key + (int)Keys.NumPad0 - 0xffb0); | |
590 else if (key >= 0xffbe && key <= 0xffd5) keyval = (Keys)(key + (int)Keys.F1 - 0xffbe); //all the way to F35... | |
591 else switch (key) { | |
592 case 0xff08: keyval = Keys.Back; break; | |
593 case 0xff09: keyval = Keys.Tab; break; | |
594 case 0xff0a: keyval = Keys.LineFeed; break; | |
595 case 0xff0b: keyval = Keys.Clear; break; | |
596 case 0xff0d: keyval = Keys.Return; break; | |
597 case 0xff13: keyval = Keys.Pause; break; | |
598 case 0xff14: keyval = Keys.Scroll; break; | |
599 case 0xff15: keyval = Keys.None; break; //Sys req | |
600 case 0xff1b: keyval = Keys.Escape; break; | |
601 case 0xffff: keyval = Keys.Delete; break; | |
602 case 0xff50: keyval = Keys.Home; break; | |
603 case 0xff51: keyval = Keys.Left; break; | |
604 case 0xff52: keyval = Keys.Up; break; | |
605 case 0xff53: keyval = Keys.Right; break; | |
606 case 0xff54: keyval = Keys.Down; break; | |
607 case 0xff55: keyval = Keys.PageUp; break; | |
608 case 0xff56: keyval = Keys.PageDown; break; | |
609 case 0xff57: keyval = Keys.End; break; | |
610 case 0xff58: keyval = Keys.Home; break; | |
611 case 0xff60: keyval = Keys.Select; break; | |
612 case 0xff61: keyval = Keys.Print; break; | |
613 case 0xff62: keyval = Keys.Execute; break; | |
614 case 0xff63: keyval = Keys.Insert; break; | |
615 case 0xff65: keyval = Keys.None; break; //Undo | |
616 case 0xff66: keyval = Keys.None; break; //Redo | |
617 case 0xff67: keyval = Keys.Apps; break; | |
618 case 0xff68: keyval = Keys.BrowserSearch; break; | |
619 case 0xff69: keyval = Keys.Cancel; break; | |
620 case 0xff6a: keyval = Keys.Help; break; | |
621 case 0xff6b: keyval = Keys.Pause; break; | |
622 case 0xff7e: keyval = Keys.None; break; //Character set switch | |
623 case 0xff7f: keyval = Keys.NumLock; break; | |
624 case 0xff80: keyval = Keys.Space; break; | |
625 case 0xff89: keyval = Keys.Tab; break; | |
626 case 0xff8d: keyval = Keys.Enter; break; | |
627 case 0xff91: keyval = Keys.F1; break; | |
628 case 0xff92: keyval = Keys.F2; break; | |
629 case 0xff93: keyval = Keys.F3; break; | |
630 case 0xff94: keyval = Keys.F4; break; | |
631 case 0xff95: keyval = Keys.Home; break; | |
632 case 0xff96: keyval = Keys.Left; break; | |
633 case 0xff97: keyval = Keys.Up; break; | |
634 case 0xff98: keyval = Keys.Right; break; | |
635 case 0xff99: keyval = Keys.Down; break; | |
636 case 0xff9a: keyval = Keys.PageUp; break; | |
637 case 0xff9b: keyval = Keys.PageDown; break; | |
638 case 0xff9c: keyval = Keys.End; break; | |
639 case 0xff9d: keyval = Keys.Home; break; | |
640 case 0xff9e: keyval = Keys.Insert; break; | |
641 case 0xff9f: keyval = Keys.Delete; break; | |
642 case 0xffbd: keyval = Keys.None; break; //keypad equals | |
643 case 0xffaa: keyval = Keys.Multiply; break; | |
644 case 0xffab: keyval = Keys.Add; break; | |
645 case 0xffac: keyval = Keys.Separator; break; | |
646 case 0xffad: keyval = Keys.Subtract; break; | |
647 case 0xffae: keyval = Keys.Decimal; break; | |
648 case 0xffaf: keyval = Keys.Divide; break; | |
649 case 0xffe1: keyval = Keys.LShiftKey; break; | |
650 case 0xffe2: keyval = Keys.RShiftKey; break; | |
651 case 0xffe3: keyval = Keys.LControlKey; break; | |
652 case 0xffe4: keyval = Keys.RControlKey; break; | |
653 case 0xffe5: keyval = Keys.CapsLock; break; | |
654 case 0xffe6: keyval = Keys.CapsLock; break; //shift lock!? | |
655 case 0xffe7: keyval = Keys.None; break; //Left meta!? | |
656 case 0xffe8: keyval = Keys.None; break; //Right meta!? | |
657 case 0xffe9: keyval = Keys.LMenu; break; | |
658 case 0xffea: keyval = Keys.Menu; break; //right alt | |
659 case 0xffeb: keyval = Keys.LWin; break; | |
660 case 0xffec: keyval = Keys.RWin; break; | |
661 case 0xffed: keyval = Keys.None; break; //Left hyper | |
662 case 0xffee: keyval = Keys.None; break; //Right hyper | |
663 //Some X11 specific stuff | |
664 case 0x20: keyval = Keys.Space; break; | |
665 case 0x21: keyval = Keys.D1 | Keys.Shift; break; //! | |
666 case 0x22: keyval = Keys.OemQuotes | Keys.Shift; break; //double quotes | |
667 case 0x23: keyval = Keys.D3 | Keys.Shift; break; //number sign? # | |
668 case 0x24: keyval = Keys.D4 | Keys.Shift; break; //dollar | |
669 case 0x25: keyval = Keys.D5 | Keys.Shift; break; //percent | |
670 case 0x26: keyval = Keys.D7 | Keys.Shift; break; //ampersand | |
671 case 0x27: keyval = Keys.OemQuotes; break; //apostrophe | |
672 case 0x28: keyval = Keys.D9 | Keys.Shift; break; //parenleft | |
673 case 0x29: keyval = Keys.D0 | Keys.Shift; break; //parenright | |
674 case 0x2a: keyval = Keys.D8 | Keys.Shift; break; //askerisk | |
675 case 0x2b: keyval = Keys.Oemplus | Keys.Shift; break; //plus | |
676 case 0x2c: keyval = Keys.Oemcomma; break; //comma | |
677 case 0x2d: keyval = Keys.OemMinus; break; //minus | |
678 case 0x2e: keyval = Keys.OemPeriod; break; //period | |
679 case 0x2f: keyval = Keys.OemQuestion; break; //slash | |
680 //digits handled above | |
681 case 0x3a: keyval = Keys.OemSemicolon | Keys.Shift; break; //colon | |
682 case 0x3b: keyval = Keys.OemSemicolon; break; //semicolon | |
683 case 0x3c: keyval = Keys.Oemcomma | Keys.Shift; break; //less than | |
684 case 0x3d: keyval = Keys.Oemplus; break; //equals | |
685 case 0x3e: keyval = Keys.OemPeriod | Keys.Shift; break; //greater than | |
686 case 0x3f: keyval = Keys.OemQuestion | Keys.Shift; break; //question mark | |
687 case 0x40: keyval = Keys.D2 | Keys.Shift; break; //commercial at | |
688 //capital letters handled above | |
689 case 0x5b: keyval = Keys.OemOpenBrackets; break; //left square bracker | |
690 case 0x5c: keyval = Keys.OemBackslash; break; //backslash | |
691 case 0x5d: keyval = Keys.OemCloseBrackets; break; //right square bracker | |
692 case 0x5e: keyval = Keys.D6 | Keys.Shift; break; //CIRCUMFLEX ACCENT | |
693 case 0x5f: keyval = Keys.OemMinus | Keys.Shift; break; //underscore | |
694 case 0x60: keyval = Keys.Oemtilde; break; //grave accent | |
695 //small letters handled above | |
696 case 0x7b: keyval = Keys.OemOpenBrackets | Keys.Shift; break; //left curly bracket | |
697 case 0x7c: keyval = Keys.OemBackslash | Keys.Shift; break; //vertical line | |
698 case 0x7d: keyval = Keys.OemCloseBrackets | Keys.Shift; break; //right curly bracket | |
699 case 0x7e: keyval = Keys.Oemtilde | Keys.Shift; break; //tilde | |
700 //blah blah | |
701 //experimental: | |
702 case 0xfe03: keyval = Keys.RMenu; break; //Alt gr or XK_ISO_Level3_Shift | |
703 } | |
704 switch (keyval) { | |
705 case Keys.LShiftKey: | |
706 case Keys.RShiftKey: | |
707 case Keys.ShiftKey: | |
708 if (down) modifierKeys |= Keys.Shift; else modifierKeys &= ~Keys.Shift; | |
709 break; | |
710 case Keys.LControlKey: | |
711 case Keys.RControlKey: | |
712 case Keys.ControlKey: | |
713 if (down) modifierKeys |= Keys.Control; else modifierKeys &= ~Keys.Control; | |
714 break; | |
715 case Keys.LMenu: | |
716 case Keys.RMenu: | |
717 case Keys.Menu: | |
718 if (down) modifierKeys |= Keys.Alt; else modifierKeys &= ~Keys.Alt; | |
719 break; | |
720 } | |
721 keyval |= modifierKeys; | |
722 if (down && KeyDown != null) KeyDown(this, new KeyEventArgs(keyval)); | |
723 else if (!down && KeyUp != null) KeyUp(this, new KeyEventArgs(keyval)); | |
724 } | |
725 private void ReceiveMessageMouse() { | |
726 Byte[] b = ReceiveAll(5); | |
727 Point p = new Point((b[1] << 8) | b[2], (b[3] << 8) | b[4]); | |
728 MouseButtons mb = MouseButtons.None; | |
729 if ((b[0] & 1) != 0) mb |= MouseButtons.Left; | |
730 if ((b[0] & 2) != 0) mb |= MouseButtons.Middle; | |
731 if ((b[0] & 4) != 0) mb |= MouseButtons.Right; | |
732 if ((b[0] & 32) != 0) mb |= MouseButtons.XButton1; | |
733 if ((b[0] & 64) != 0) mb |= MouseButtons.XButton2; | |
734 int dd = 0; | |
735 if ((b[0] & 8) != 0) dd = 120; | |
736 if ((b[0] & 16) != 0) dd = -120; | |
737 //Console.WriteLine("PointerEvent x={0} y={1} buttons={2} delta={3}", p.X, p.Y, mb, dd); | |
738 foreach (MouseButtons mbi in Enum.GetValues(typeof(MouseButtons))) if ((mousebuttons & mbi) != 0 && (mb & mbi) == 0) if (MouseUp != null) MouseUp(this, new MouseEventArgs(mbi, 0, p.X, p.Y, 0)); | |
739 if ((dd != 0 || mousePosition != p) && MouseMove != null) MouseMove(this, new MouseEventArgs(mb & mousebuttons, 0, p.X, p.Y, dd)); | |
740 foreach (MouseButtons mbi in Enum.GetValues(typeof(MouseButtons))) if ((mousebuttons & mbi) == 0 && (mb & mbi) != 0) if (MouseDown != null) MouseDown(this, new MouseEventArgs(mbi, 0, p.X, p.Y, 0)); | |
741 mousePosition = p; | |
742 mousebuttons = mb; | |
743 } | |
744 | |
745 private void FBUpdate(Object sender, VNCFramebufferUpdateEventArgs e) { | |
746 if (e.Framebuffer != framebuffer) return; | |
747 Rectangle r = e.Area; | |
748 r.Intersect(new Rectangle(0, 0, e.Framebuffer.Width, e.Framebuffer.Height)); | |
749 if (r.Width == 0 || r.Height == 0) return; | |
750 Boolean send; | |
751 lock (dirtyrects) { | |
752 for (int i = 0; i < dirtyrects.Count; i++) { | |
753 Rectangle r2 = dirtyrects[i]; | |
754 if (r.IntersectsWith(r2)) { | |
755 dirtyrects.RemoveAt(i); | |
756 i = -1; | |
757 r = Rectangle.Union(r, r2); | |
758 } | |
759 } | |
760 dirtyrects.Add(r); | |
761 send = waitingforupdate > 0; | |
762 } | |
763 if (send) { | |
764 Interlocked.Decrement(ref waitingforupdate); | |
765 SendQueuedRectangles(waitingforupdaterect, false); | |
766 } | |
767 } | |
768 | |
769 private void ClearIntersectingQueuedRectangles(Rectangle r) { | |
770 lock (dirtyrects) { | |
771 for (int i = 0; i < dirtyrects.Count; i++) { | |
772 Rectangle r2 = dirtyrects[i]; | |
773 if (!r2.IntersectsWith(r)) continue; | |
774 dirtyrects.RemoveAt(i); | |
775 i--; | |
776 } | |
777 dirtyrects.TrimExcess(); | |
778 } | |
779 } | |
780 private void SendQueuedRectangles(Rectangle r, Boolean incremental) { | |
781 lock (socket) { | |
782 if (resized) { | |
783 resized = false; | |
784 jpegCounter = 0; | |
785 VNCFramebuffer fb = framebuffer; | |
786 SendUInt8(0); //FramebufferUpdate | |
787 SendUInt8(0); //Padding | |
788 if (Array.IndexOf(SupportedEncodings, -223) == -1) { | |
789 Console.Error.WriteLine("VNC: Desktop size update not supported"); | |
790 SendUInt16(fb == null ? (UInt16)1 : (UInt16)2); //Number of rectangles | |
791 int[] empty = new int[r.Width * r.Height]; | |
792 SendFBRectangle(empty, new Rectangle(0, 0, r.Width, r.Height), r.Width); | |
793 ClearIntersectingQueuedRectangles(r); | |
794 blurryrect = Rectangle.Empty; | |
795 if (fb != null) SendFBRectangle(fb.Framebuffer, r, fb.Width); | |
796 } else { | |
797 if (fb != null) { | |
798 Width = fb.Width; | |
799 Height = fb.Height; | |
800 } | |
801 Console.Error.WriteLine("VNC: Sending desktop size update {0}x{1}", Width, Height); | |
802 SendUInt16(1); //Number of rectangles | |
803 SendUInt16(0); | |
804 SendUInt16(0); | |
805 SendUInt16((UInt16)Width); | |
806 SendUInt16((UInt16)Height); | |
807 SendUInt32(unchecked((UInt32)(-223))); //Encoding type | |
808 } | |
809 FlushSendBuffer(); | |
810 } else { | |
811 if (UpdateRequested != null) UpdateRequested(this, new EventArgs()); | |
812 VNCFramebuffer fb = framebuffer; | |
813 if (!incremental) { | |
814 jpegCounter = 0; | |
815 ClearIntersectingQueuedRectangles(r); | |
816 blurryrect = Rectangle.Empty; | |
817 SendUInt8(0); //FramebufferUpdate | |
818 SendUInt8(0); //Padding | |
819 SendUInt16(1); //Number of rectangles | |
820 SendFBRectangle(fb.Framebuffer, r, fb.Width); | |
821 } else { //incremental | |
822 List<Rectangle> sending = new List<Rectangle>(); | |
823 lock (dirtyrects) { | |
824 if (dirtyrects.Count == 0) { | |
825 if (jpegCounter > 0) jpegCounter--; | |
826 } else { | |
827 if (jpegCounter < 10) jpegCounter++; | |
828 } | |
829 for (int i = 0; i < dirtyrects.Count; i++) { | |
830 Rectangle r2 = dirtyrects[i]; | |
831 if (!r2.IntersectsWith(r)) continue; | |
832 dirtyrects.RemoveAt(i); | |
833 i--; | |
834 r2.Intersect(r); | |
835 sending.Add(r2); | |
836 } | |
837 dirtyrects.TrimExcess(); | |
838 if (sending.Count == 0 && blurryrect.IsEmpty) { | |
839 Interlocked.Increment(ref waitingforupdate); | |
840 waitingforupdaterect = r; | |
841 } | |
842 } | |
843 if (sending.Count > 0 || !blurryrect.IsEmpty) { | |
844 SendUInt8(0); //FramebufferUpdate | |
845 SendUInt8(0); //Padding | |
846 SendUInt16((UInt16)(sending.Count + (blurryrect.IsEmpty ? 0 : 1))); //Number of rectangles | |
847 if (!blurryrect.IsEmpty) { | |
848 //The idea here is to use a lossless compression for a small area to "recover" textual/static content from the JPEG artifacts | |
849 //Only a small area is updated here each time because compressing a full frame takes too much CPU time | |
850 Rectangle fixrect = blurryrect; | |
851 if (blurryrecoveryline < fixrect.Top) blurryrecoveryline = fixrect.Top; | |
852 else if (blurryrecoveryline >= fixrect.Bottom) blurryrecoveryline = fixrect.Top; | |
853 fixrect.Intersect(new Rectangle(0, blurryrecoveryline, Int16.MaxValue, 10)); | |
854 if (fixrect.IsEmpty) fixrect = blurryrect; | |
855 int oldjpeg = jpegCounter; | |
856 jpegCounter = 0; | |
857 SendFBRectangle(fb.Framebuffer, Rectangle.Intersect(fixrect, r), fb.Width); | |
858 jpegCounter = oldjpeg; | |
859 blurryrecoveryline = fixrect.Bottom; | |
860 if (fixrect.Top <= blurryrect.Top && fixrect.Bottom >= blurryrect.Top) { | |
861 blurryrect.Intersect(new Rectangle(0, fixrect.Bottom, Int16.MaxValue, Int16.MaxValue)); | |
862 } else if (fixrect.Top <= blurryrect.Bottom && fixrect.Bottom >= blurryrect.Bottom) { | |
863 blurryrect.Intersect(new Rectangle(0, 0, fixrect.Top, Int16.MaxValue)); | |
864 } | |
865 if (blurryrect.Height == 0) blurryrect = Rectangle.Empty; | |
866 } | |
867 foreach (Rectangle r2 in sending) { | |
868 SendFBRectangle(fb.Framebuffer, r2, fb.Width); | |
869 } | |
870 } else { | |
871 if (WaitingForUpdate != null) WaitingForUpdate(this, new EventArgs()); | |
872 } | |
873 } | |
874 } | |
875 FlushSendBuffer(); | |
876 } | |
877 } | |
878 | |
879 private void SendFBRectangle(Int32[] fb, Rectangle r, Int32 fbwidth) { | |
880 r.Intersect(new Rectangle(0, 0, fbwidth, fb.Length / fbwidth)); | |
881 r.Intersect(new Rectangle(0, 0, Width, Height)); | |
882 Boolean sent = false; | |
883 foreach (Int32 enc in SupportedEncodings) { | |
884 switch (enc) { | |
885 case 0: | |
886 SendFBRectangleRaw(fb, r, fbwidth); sent = true; break; | |
887 case 2: | |
888 if (r.Height < 8) break; | |
889 SendFBRectangleRRE(fb, r, fbwidth); sent = true; break; | |
890 case 5: | |
891 if (r.Width < 16 || r.Height < 16) break; | |
892 SendFBRectangleHextile(fb, r, fbwidth); sent = true; break; | |
893 case 6: | |
894 sent = SendFBRectangleZLib(fb, r, fbwidth); | |
895 break; | |
896 case 7: | |
897 sent = SendFBRectangleTight(fb, r, fbwidth); | |
898 break; | |
899 } | |
900 if (sent) break; | |
901 } | |
902 if (!sent) SendFBRectangleRaw(fb, r, fbwidth); | |
903 } | |
904 private void SendFBRectangleHeader(Rectangle r, UInt32 encoding) { | |
905 SendUInt16((UInt16)r.X); | |
906 SendUInt16((UInt16)r.Y); | |
907 SendUInt16((UInt16)r.Width); | |
908 SendUInt16((UInt16)r.Height); | |
909 SendUInt32(encoding); | |
910 } | |
911 IZLibCompressor TightZLib = null; | |
912 private unsafe Boolean SendFBRectangleTight(Int32[] framebuffer, Rectangle r, Int32 fbwidth) { | |
913 if (jpegCounter > 3 && r.Width * r.Height > 100 * 100) { | |
914 SendFBRectangleHeader(r, 7); | |
915 SendUInt8(0x90); //Jpeg encoding | |
916 MemoryStream jpeg = new MemoryStream(); | |
917 if (r.Width > 0 && r.Height > 0) { | |
918 fixed (int* fbptr = framebuffer) { | |
919 using (Bitmap bmp = new Bitmap(r.Width, r.Height, fbwidth * 4, PixelFormat.Format32bppRgb, (IntPtr)(fbptr + (r.Top * fbwidth + r.Left)))) { | |
920 ImageCodecInfo ici = Array.Find(ImageCodecInfo.GetImageEncoders(), delegate(ImageCodecInfo item) { return item.FormatID == ImageFormat.Jpeg.Guid; }); | |
921 EncoderParameters ep = new EncoderParameters(1); | |
922 ep.Param[0] = new EncoderParameter(System.Drawing.Imaging.Encoder.Quality, 50L); | |
923 bmp.Save(jpeg, ici, ep); | |
924 //bmp.Save(jpeg, ImageFormat.Jpeg); | |
925 } | |
926 } | |
927 } | |
928 jpeg.Seek(0, SeekOrigin.Begin); | |
929 int length = (int)jpeg.Length; | |
930 Byte b1 = (Byte)(length & 0x7F); | |
931 Byte b2 = (Byte)((length >> 7) & 0x7F); | |
932 Byte b3 = (Byte)((length >> 14) & 0x7F); | |
933 if (b3 != 0) b2 |= 0x80; | |
934 if (b2 != 0) b1 |= 0x80; | |
935 SendUInt8(b1); | |
936 if (b2 != 0) SendUInt8(b2); | |
937 if (b3 != 0) SendUInt8(b3); | |
938 SendAll(jpeg.ToArray()); | |
939 blurryrect = blurryrect.IsEmpty ? r : Rectangle.Union(blurryrect, r); | |
940 } else { | |
941 if (TightZLib == null && ZLibFactory == null) return false; | |
942 SendFBRectangleHeader(r, 7); | |
943 SendUInt8(0x00); //Basic encoding, copy filter (raw), zlib stream 0 | |
944 Byte[] row; | |
945 int i = 0; | |
946 if (pixelformat.BitsPerPixel == 32 && pixelformat.ColorDepth == 24) { | |
947 row = new Byte[r.Width * r.Height * 3]; | |
948 for (int y = r.Top; y < r.Bottom; y++) { | |
949 for (int x = r.Left; x < r.Right; x++) { | |
950 UInt32 pixel = (UInt32)framebuffer[y * fbwidth + x]; | |
951 row[i++] = (Byte)(pixel >> 16); | |
952 row[i++] = (Byte)(pixel >> 8); | |
953 row[i++] = (Byte)(pixel >> 0); | |
954 } | |
955 } | |
956 } else { | |
957 row = new Byte[r.Width * r.Height * pixelformat.BitsPerPixel / 8]; | |
958 for (int y = r.Top; y < r.Bottom; y++) { | |
959 for (int x = r.Left; x < r.Right; x++) { | |
960 UInt32 pixel = (UInt32)framebuffer[y * fbwidth + x]; | |
961 UInt32 encoded = 0; | |
962 encoded |= ((((pixel >> 16) & 0xff) * (pixelformat.RedMax + 1u) / 256) & pixelformat.RedMax) << pixelformat.RedShift; | |
963 encoded |= ((((pixel >> 8) & 0xff) * (pixelformat.GreenMax + 1u) / 256) & pixelformat.GreenMax) << pixelformat.GreenShift; | |
964 encoded |= ((((pixel >> 0) & 0xff) * (pixelformat.BlueMax + 1u) / 256) & pixelformat.BlueMax) << pixelformat.BlueShift; | |
965 if (pixelformat.BigEndian) { | |
966 for (int b = pixelformat.BitsPerPixel - 8; b >= 0; b -= 8) row[i++] = (Byte)(encoded >> b); | |
967 } else { | |
968 for (int b = 0; b < pixelformat.BitsPerPixel; b += 8) row[i++] = (Byte)(encoded >> b); | |
969 } | |
970 } | |
971 } | |
972 } | |
973 if (i >= 12) { | |
974 if (TightZLib == null) TightZLib = ZLibFactory(); | |
975 Byte[] compressed = TightZLib.Compress(row, 0, i); | |
976 int length = compressed.Length; | |
977 Byte b1 = (Byte)(length & 0x7F); | |
978 Byte b2 = (Byte)((length >> 7) & 0x7F); | |
979 Byte b3 = (Byte)((length >> 14) & 0x7F); | |
980 if (b3 != 0) b2 |= 0x80; | |
981 if (b2 != 0) b1 |= 0x80; | |
982 SendUInt8(b1); | |
983 if (b2 != 0) SendUInt8(b2); | |
984 if (b3 != 0) SendUInt8(b3); | |
985 SendAll(compressed); | |
986 } else { | |
987 SendAll(row, 0, i); | |
988 } | |
989 } | |
990 return true; | |
991 } | |
992 private void SendFBRectangleRaw(Int32[] framebuffer, Rectangle r, int fbwidth) { | |
993 SendFBRectangleHeader(r, 0); | |
994 for (int y = r.Top; y < r.Bottom; y++) { | |
995 Byte[] row = new Byte[r.Width * pixelformat.BitsPerPixel / 8]; | |
996 int i = 0; | |
997 for (int x = r.Left; x < r.Right; x++) { | |
998 UInt32 pixel = (UInt32)framebuffer[y * fbwidth + x]; | |
999 UInt32 encoded = 0; | |
1000 encoded |= ((((pixel >> 16) & 0xff) * (pixelformat.RedMax + 1u) / 256) & pixelformat.RedMax) << pixelformat.RedShift; | |
1001 encoded |= ((((pixel >> 8) & 0xff) * (pixelformat.GreenMax + 1u) / 256) & pixelformat.GreenMax) << pixelformat.GreenShift; | |
1002 encoded |= ((((pixel >> 0) & 0xff) * (pixelformat.BlueMax + 1u) / 256) & pixelformat.BlueMax) << pixelformat.BlueShift; | |
1003 if (pixelformat.BigEndian) { | |
1004 for (int b = pixelformat.BitsPerPixel - 8; b >= 0; b -= 8) row[i++] = (Byte)(encoded >> b); | |
1005 } else { | |
1006 for (int b = 0; b < pixelformat.BitsPerPixel; b += 8) row[i++] = (Byte)(encoded >> b); | |
1007 } | |
1008 } | |
1009 SendAll(row); | |
1010 } | |
1011 } | |
1012 | |
1013 IZLibCompressor ZLibStream = null; | |
1014 private Boolean SendFBRectangleZLib(Int32[] framebuffer, Rectangle r, int fbwidth) { | |
1015 if (ZLibStream == null && ZLibFactory == null) return false; | |
1016 SendFBRectangleHeader(r, 6); | |
1017 Byte[] row = new Byte[r.Width * r.Height * pixelformat.BitsPerPixel / 8]; | |
1018 int i = 0; | |
1019 for (int y = r.Top; y < r.Bottom; y++) { | |
1020 for (int x = r.Left; x < r.Right; x++) { | |
1021 UInt32 pixel = (UInt32)framebuffer[y * fbwidth + x]; | |
1022 UInt32 encoded = 0; | |
1023 encoded |= ((((pixel >> 16) & 0xff) * (pixelformat.RedMax + 1u) / 256) & pixelformat.RedMax) << pixelformat.RedShift; | |
1024 encoded |= ((((pixel >> 8) & 0xff) * (pixelformat.GreenMax + 1u) / 256) & pixelformat.GreenMax) << pixelformat.GreenShift; | |
1025 encoded |= ((((pixel >> 0) & 0xff) * (pixelformat.BlueMax + 1u) / 256) & pixelformat.BlueMax) << pixelformat.BlueShift; | |
1026 if (pixelformat.BigEndian) { | |
1027 for (int b = pixelformat.BitsPerPixel - 8; b >= 0; b -= 8) row[i++] = (Byte)(encoded >> b); | |
1028 } else { | |
1029 for (int b = 0; b < pixelformat.BitsPerPixel; b += 8) row[i++] = (Byte)(encoded >> b); | |
1030 } | |
1031 } | |
1032 } | |
1033 if (ZLibStream == null) ZLibStream = ZLibFactory(); | |
1034 Byte[] compressed = ZLibStream.Compress(row, 0, i); | |
1035 SendUInt32((UInt32)compressed.Length); | |
1036 SendAll(compressed); | |
1037 return true; | |
1038 } | |
1039 | |
1040 | |
1041 private void SendFBRectangleRRE(Int32[] framebuffer, Rectangle r, int fbwidth) { | |
1042 SendFBRectangleHeader(r, 2); | |
1043 int basecolor = framebuffer[r.Y * fbwidth + r.X]; | |
1044 List<Rectangle> subrects = new List<Rectangle>(); | |
1045 if (false) { | |
1046 for (int y = r.Top; y < r.Bottom; y++) { | |
1047 int color = basecolor; | |
1048 int runstart = r.Left; | |
1049 int runlength = 0; | |
1050 for (int x = r.Left; x < r.Right; x++) { | |
1051 int newcolor = framebuffer[y * fbwidth + x]; | |
1052 if (color != newcolor) { | |
1053 if (color != basecolor && runlength > 0) { | |
1054 Rectangle r2 = new Rectangle(runstart, y, runlength, 1); | |
1055 if (r2.Y > 0 && framebuffer[(r2.Y - 1) * fbwidth + r2.X] == color) { | |
1056 Boolean hasadjacent = false; | |
1057 Rectangle adjacent = r2; | |
1058 foreach (Rectangle r3 in subrects) { | |
1059 if (r3.Left == r2.Left && r3.Width == r2.Width && r3.Top + r3.Height == r2.Top) { | |
1060 adjacent = r3; | |
1061 hasadjacent = true; | |
1062 break; | |
1063 } | |
1064 } | |
1065 if (hasadjacent) { | |
1066 subrects.Remove(adjacent); | |
1067 r2 = Rectangle.Union(r2, adjacent); | |
1068 } | |
1069 } | |
1070 subrects.Add(r2); | |
1071 } | |
1072 runstart = x; | |
1073 runlength = 0; | |
1074 color = newcolor; | |
1075 } | |
1076 runlength++; | |
1077 } | |
1078 if (color != basecolor && runlength > 0) subrects.Add(new Rectangle(runstart, y, runlength, 1)); | |
1079 } | |
1080 } else { | |
1081 Queue<Rectangle> remaining = new Queue<Rectangle>(); | |
1082 remaining.Enqueue(r); | |
1083 while (remaining.Count > 0) { | |
1084 Rectangle r2 = remaining.Dequeue(); | |
1085 int color = framebuffer[r2.Y * fbwidth + r2.X]; | |
1086 int rw = -1, rh = -1; | |
1087 for (int x = r2.Left; x < r2.Right && rw == -1; x++) { | |
1088 if (color != framebuffer[r2.Y * fbwidth + x]) rw = x - r2.Left; | |
1089 } | |
1090 if (rw == -1) rw = r2.Width; | |
1091 for (int y = r2.Top + 1; y < r2.Bottom && rh == -1; y++) { | |
1092 Boolean success = true; | |
1093 for (int x = r2.Left; x < r2.Left + rw && success; x++) { | |
1094 if (color != framebuffer[y * fbwidth + x]) success = false; | |
1095 } | |
1096 if (!success) rh = y - r2.Top; | |
1097 } | |
1098 if (rh == -1) rh = r2.Height; | |
1099 if (rw != 0 && rh != 0) subrects.Add(new Rectangle(r2.X, r2.Y, rw, rh)); | |
1100 //if (r2.Width - rw > 0 && rh != 0) remaining.Enqueue(new Rectangle(r2.X + rw, r2.Y, r2.Width - rw, rh)); | |
1101 if (r2.Height - rh > 0 && rw != 0) remaining.Enqueue(new Rectangle(r2.X, r2.Y + rh, rw, r2.Height - rh)); | |
1102 //if (r2.Width - rw > 0 && r2.Height - rh > 0) remaining.Enqueue(new Rectangle(r2.X + rw, r2.Y + rh, r2.Width - rw, r2.Height - rh)); | |
1103 //if (r2.Height - rh > 0) remaining.Enqueue(new Rectangle(r2.X, r2.Y + rh, r2.Width, r2.Height - rh)); | |
1104 if (r2.Width - rw > 0) remaining.Enqueue(new Rectangle(r2.X + rw, r2.Y, r2.Width - rw, r2.Height)); | |
1105 } | |
1106 } | |
1107 SendUInt32((UInt32)subrects.Count); | |
1108 SendPixel(basecolor); | |
1109 foreach (Rectangle r2 in subrects) { | |
1110 SendPixel(framebuffer[r2.Y * fbwidth + r2.X]); | |
1111 SendUInt16((UInt16)(r2.Left - r.Left)); | |
1112 SendUInt16((UInt16)(r2.Top - r.Top)); | |
1113 SendUInt16((UInt16)r2.Width); | |
1114 SendUInt16((UInt16)r2.Height); | |
1115 } | |
1116 } | |
1117 private void SendFBRectangleHextile(Int32[] framebuffer, Rectangle r, int fbwidth) { | |
1118 SendFBRectangleHeader(r, 5); | |
1119 const int hextileRaw = 1; | |
1120 const int hextileBgSpecified = 2; | |
1121 const int hextileFgSpecified = 4; | |
1122 const int hextileAnySubrects = 8; | |
1123 const int hextileSubrectsColoured = 16; | |
1124 int oldBg = 0, oldFg = 0; | |
1125 bool oldBgValid = false; | |
1126 bool oldFgValid = false; | |
1127 Rectangle t = new Rectangle(); | |
1128 for (t.Y = r.Top; t.Top < r.Bottom; t.Y += 16) { | |
1129 t.Height = Math.Min(r.Bottom, t.Top + 16) - t.Top; | |
1130 for (t.X = r.Left; t.Left < r.Right; t.X += 16) { | |
1131 t.Width = Math.Min(r.Right, t.Left + 16) - t.Left; | |
1132 int tileType = 0; | |
1133 int bg = framebuffer[t.Y * fbwidth + t.X]; | |
1134 int fg = 0; | |
1135 if (!oldBgValid || oldBg != bg) { | |
1136 tileType |= hextileBgSpecified; | |
1137 oldBg = bg; | |
1138 oldBgValid = true; | |
1139 } | |
1140 Boolean foundfg = false; | |
1141 Boolean foundcol = false; | |
1142 int subrects = 0; | |
1143 for (int y = t.Top; y < t.Bottom; y++) { | |
1144 int color = bg; | |
1145 int length = 0; | |
1146 for (int x = t.Left; x < t.Right; x++) { | |
1147 int pixel = framebuffer[y * fbwidth + x]; | |
1148 if (pixel == bg) { | |
1149 } else if (!foundfg) { | |
1150 fg = pixel; | |
1151 foundfg = true; | |
1152 } else if (pixel == fg) { | |
1153 } else { | |
1154 foundcol = true; | |
1155 } | |
1156 if (color != pixel && length > 0) { | |
1157 if (color != bg) subrects++; | |
1158 length = 0; | |
1159 } | |
1160 length++; | |
1161 color = pixel; | |
1162 } | |
1163 if (length > 0 && color != bg) subrects++; | |
1164 } | |
1165 if (foundcol) { | |
1166 tileType |= hextileSubrectsColoured | hextileAnySubrects; | |
1167 oldFgValid = false; | |
1168 } else if (foundfg) { | |
1169 tileType |= hextileAnySubrects; | |
1170 if (!oldFgValid || oldFg != fg) { | |
1171 tileType |= hextileFgSpecified; | |
1172 oldFg = fg; | |
1173 oldFgValid = true; | |
1174 } | |
1175 } | |
1176 int encbytes = 0; | |
1177 if ((tileType & hextileBgSpecified) != 0) encbytes += pixelformat.BitsPerPixel / 8; | |
1178 if ((tileType & hextileFgSpecified) != 0) encbytes += pixelformat.BitsPerPixel / 8; | |
1179 if ((tileType & hextileAnySubrects) != 0) { | |
1180 int pertile = 2; | |
1181 if ((tileType & hextileSubrectsColoured) != 0) pertile += pixelformat.BitsPerPixel / 8; | |
1182 encbytes += pertile * subrects; | |
1183 } | |
1184 if (t.Width * t.Height * pixelformat.BitsPerPixel / 8 <= encbytes) { | |
1185 SendUInt8(hextileRaw); | |
1186 for (int y = t.Top; y < t.Bottom; y++) for (int x = t.Left; x < t.Right; x++) SendPixel(framebuffer[y * fbwidth + x]); | |
1187 oldBgValid = oldFgValid = false; | |
1188 continue; | |
1189 } | |
1190 SendUInt8((Byte)tileType); | |
1191 if ((tileType & hextileBgSpecified) != 0) SendPixel(bg); | |
1192 if ((tileType & hextileFgSpecified) != 0) SendPixel(fg); | |
1193 if ((tileType & hextileAnySubrects) != 0) { | |
1194 SendUInt8((Byte)subrects); | |
1195 int subrectsa = 0; | |
1196 for (int y = t.Top; y < t.Bottom; y++) { | |
1197 int color = bg; | |
1198 int length = 0; | |
1199 int start = 0; | |
1200 for (int x = t.Left; x < t.Right; x++) { | |
1201 int newcolor = framebuffer[y * fbwidth + x]; | |
1202 if (color != newcolor && length > 0) { | |
1203 if (color != bg && subrectsa < subrects) { | |
1204 if ((tileType & hextileSubrectsColoured) != 0) SendPixel(color); | |
1205 SendUInt8((Byte)(((start & 0xF) << 4) | ((y - t.Top) & 0xF))); | |
1206 SendUInt8((Byte)((((length - 1) & 0xF) << 4) | (0 & 0xF))); | |
1207 subrectsa++; | |
1208 } | |
1209 length = 0; | |
1210 start = x - t.Left; | |
1211 } | |
1212 length++; | |
1213 color = newcolor; | |
1214 } | |
1215 if (length > 0 && color != bg && subrectsa < subrects) { | |
1216 if ((tileType & hextileSubrectsColoured) != 0) SendPixel(color); | |
1217 SendUInt8((Byte)(((start & 0xF) << 4) | ((y - t.Top) & 0xF))); | |
1218 SendUInt8((Byte)((((length - 1) & 0xF) << 4) | (0 & 0xF))); | |
1219 subrectsa++; | |
1220 } | |
1221 } | |
1222 for (int i = subrectsa; i < subrects; i++) { | |
1223 if ((tileType & hextileSubrectsColoured) != 0) SendPixel(0); | |
1224 SendUInt16(0); | |
1225 subrectsa++; | |
1226 } | |
1227 if (subrects != subrectsa) throw new Exception("subrects != subrectsa"); | |
1228 } | |
1229 } | |
1230 //Flush(); | |
1231 } | |
1232 } | |
1233 | |
1234 private void SendPixel(int pixeli) { | |
1235 UInt32 encoded = 0; | |
1236 UInt32 pixel = (UInt32)pixeli; | |
1237 encoded |= ((((pixel >> 16) & 0xff) * (pixelformat.RedMax + 1u) / 256) & pixelformat.RedMax) << pixelformat.RedShift; | |
1238 encoded |= ((((pixel >> 8) & 0xff) * (pixelformat.GreenMax + 1u) / 256) & pixelformat.GreenMax) << pixelformat.GreenShift; | |
1239 encoded |= ((((pixel >> 0) & 0xff) * (pixelformat.BlueMax + 1u) / 256) & pixelformat.BlueMax) << pixelformat.BlueShift; | |
1240 byte[] row = new Byte[pixelformat.BitsPerPixel / 8]; | |
1241 int i = 0; | |
1242 if (pixelformat.BigEndian) { | |
1243 for (int b = pixelformat.BitsPerPixel - 8; b >= 0; b -= 8) row[i++] = (Byte)(encoded >> b); | |
1244 } else { | |
1245 for (int b = 0; b < pixelformat.BitsPerPixel; b += 8) row[i++] = (Byte)(encoded >> b); | |
1246 } | |
1247 SendAll(row); | |
1248 } | |
1249 | |
1250 private void SendPixelFormat(RFBPixelFormat pf) { | |
1251 SendUInt8(pf.BitsPerPixel); //bits per pixel | |
1252 SendUInt8(pf.ColorDepth); //depth | |
1253 SendUInt8(pf.BigEndian ? (Byte)1 : (Byte)0); //big endian | |
1254 SendUInt8(pf.TrueColor ? (Byte)1 : (Byte)0); //true color | |
1255 SendUInt16(pf.RedMax); //red max | |
1256 SendUInt16(pf.GreenMax); //green max | |
1257 SendUInt16(pf.BlueMax); //blue max | |
1258 SendUInt8(pf.RedShift); //red shift | |
1259 SendUInt8(pf.GreenShift); //green shift | |
1260 SendUInt8(pf.BlueShift); //blue shift | |
1261 SendUInt8(0); //padding | |
1262 SendUInt8(0); //padding | |
1263 SendUInt8(0); //padding | |
1264 } | |
1265 private RFBPixelFormat ReceivePixelFormat() { | |
1266 Byte[] b = ReceiveAll(16); | |
1267 RFBPixelFormat pf = new RFBPixelFormat(); | |
1268 pf.BitsPerPixel = b[0]; | |
1269 pf.ColorDepth = b[1]; | |
1270 pf.BigEndian = b[2] != 0; | |
1271 pf.TrueColor = b[3] != 0; | |
1272 pf.RedMax = (UInt16)((b[4] << 8) | b[5]); | |
1273 pf.GreenMax = (UInt16)((b[6] << 8) | b[7]); | |
1274 pf.BlueMax = (UInt16)((b[8] << 8) | b[9]); | |
1275 pf.RedShift = b[10]; | |
1276 pf.GreenShift = b[11]; | |
1277 pf.BlueShift = b[12]; | |
1278 return pf; | |
1279 } | |
1280 | |
1281 private void SendUInt32(UInt32 value) { | |
1282 SendAll(new Byte[] { (Byte)(value >> 24), (Byte)(value >> 16), (Byte)(value >> 8), (Byte)value }); | |
1283 } | |
1284 private void SendUInt16(UInt16 value) { | |
1285 SendAll(new Byte[] { (Byte)(value >> 8), (Byte)value }); | |
1286 } | |
1287 private void SendUInt8(Byte value) { | |
1288 SendAll(new Byte[] { value }); | |
1289 } | |
1290 private void SendAll(Byte[] buffer) { | |
1291 SendAll(buffer, 0, buffer.Length); | |
1292 } | |
1293 | |
1294 private void SendAll(Byte[] buffer, int off, int len) { | |
1295 if (SendBuffer == null) SendBuffer = new MemoryStream(); | |
1296 SendBuffer.Write(buffer, off, len); | |
1297 if (SendBuffer.Length > 102400) FlushSendBuffer(); | |
1298 } | |
1299 private void FlushSendBuffer() { | |
1300 if (SendBuffer == null) return; | |
1301 SendBuffer.Seek(0, SeekOrigin.Begin); | |
1302 SendBuffer.WriteTo(socket); | |
1303 SendBuffer.SetLength(0); | |
1304 } | |
1305 | |
1306 private Byte ReceiveByte() { | |
1307 Byte[] buffer = new Byte[1]; | |
1308 ReceiveAll(buffer, 0, 1); | |
1309 return buffer[0]; | |
1310 } | |
1311 private Byte[] ReceiveAll(int len) { | |
1312 Byte[] buffer = new Byte[len]; | |
1313 ReceiveAll(buffer, 0, len); | |
1314 return buffer; | |
1315 } | |
1316 private void ReceiveAll(Byte[] buffer, int off, int len) { | |
1317 while (len > 0) { | |
1318 int sent = socket.Read(buffer, off, len); | |
1319 if (sent == 0) throw new EndOfStreamException(); | |
1320 len -= sent; | |
1321 off += sent; | |
1322 } | |
1323 } | |
1324 } | |
1325 } |