123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935 |
- (* Aos, Copyright 2001, Pieter Muller, ETH Zurich *)
- MODULE VNC; (** AUTHOR "pjm/jkreienb"; PURPOSE "VNC client"; *)
- (*
- VNC viewer for Aos - based on Oberon VNC viewer by Jörg Kreienbühl.
- This version is based on the window manager.
- References:
- 1. Tristan Richardson and Kenneth R. Wood, "The RFB Protocol: Version 3.3", ORL, Cambridge, January 1998
- *)
- IMPORT SYSTEM, Streams, KernelLog, Objects, Commands, Network, IP, TCP, DNS, DES,
- Inputs, Raster, WMWindowManager, Rect := WMRectangles, Dialogs := WMDialogs, Beep, Files;
- CONST
- OpenTimeout = 10000;
- CloseTimeout = 2000;
- PollTimeout = 0; (* set to 0 for old-style polling on every received event *)
- Shared = TRUE;
- AlphaCursor = 128;
- InBufSize = 8192; (* network input buffer *)
- OutBufSize = 4096; (* network output buffer *)
- ImgBufSize = 8192; (* image buffer for ReceiveRaw *)
- BellDelay = 20; (* ms *)
- BellFreq = 550; (* Hz *)
- Trace = FALSE;
- TraceVisual = TRUE;
- TraceAudio = FALSE;
- Ok = TCP.Ok;
- TYPE
- Connection* = POINTER TO RECORD
- next: Connection; (* link in connection pool *)
- pcb: TCP.Connection;
- w: Window;
- res: WORD;
- id: LONGINT;
- receiver: Receiver;
- sender: Sender;
- nb: Raster.Image;
- fmt: Raster.Format; (* network transfer format *)
- mode: Raster.Mode;
- bytesPerPixel: LONGINT; (* network transfer format size *)
- rcvbuf, imgbuf: POINTER TO ARRAY OF CHAR;
- rcvbufpos, rcvbuflen: LONGINT;
- fip: IP.Adr
- END;
- TYPE
- EnumProc = PROCEDURE (c: Connection; out : Streams.Writer);
- ConnectionPool = OBJECT
- VAR head, tail: Connection; id: LONGINT;
- PROCEDURE Empty(): BOOLEAN;
- BEGIN (* read head pointer atomically *)
- RETURN head = NIL
- END Empty;
- PROCEDURE Add(c: Connection);
- BEGIN {EXCLUSIVE}
- c.next := NIL; c.id := id; INC(id);
- IF head = NIL THEN head := c ELSE tail.next := c END;
- tail := c
- END Add;
- PROCEDURE Remove(c: Connection);
- VAR p, q: Connection;
- BEGIN {EXCLUSIVE}
- p := NIL; q := head;
- WHILE (q # NIL) & (q # c) DO p := q; q := q.next END;
- IF q = c THEN (* found *)
- IF p # NIL THEN p.next := q.next ELSE head := NIL; tail := NIL END
- END
- END Remove;
- PROCEDURE Enumerate(p: EnumProc; out : Streams.Writer);
- VAR c: Connection;
- BEGIN (* may traverse list concurrently with Add and Remove *)
- c := head; WHILE c # NIL DO p(c, out); c := c.next END
- END Enumerate;
- PROCEDURE Find(id: LONGINT): Connection;
- VAR c: Connection;
- BEGIN (* may traverse list concurrently with Add and Remove *)
- c := head; WHILE (c # NIL) & (c.id # id) DO c := c.next END;
- RETURN c
- END Find;
- PROCEDURE &Init*;
- BEGIN
- head := NIL; tail := NIL; id := 0
- END Init;
- END ConnectionPool;
- TYPE
- Window = OBJECT (WMWindowManager.BufferWindow)
- VAR sender: Sender;
- PROCEDURE PointerDown*(x, y: LONGINT; keys: SET);
- BEGIN
- IF sender # NIL THEN sender.Pointer(x, y, keys) END
- END PointerDown;
- PROCEDURE PointerMove*(x, y: LONGINT; keys: SET);
- BEGIN
- IF sender # NIL THEN sender.Pointer(x, y, keys) END
- END PointerMove;
- PROCEDURE WheelMove*(dz : LONGINT);
- BEGIN
- IF sender # NIL THEN sender.Wheel(dz) END
- END WheelMove;
- PROCEDURE PointerUp*(x, y: LONGINT; keys: SET);
- BEGIN
- IF sender # NIL THEN sender.Pointer(x, y, keys) END
- END PointerUp;
- PROCEDURE KeyEvent*(ucs : LONGINT; flags: SET; keysym: LONGINT);
- BEGIN
- IF (keysym # Inputs.KsNil) & (sender # NIL) THEN sender.Key(keysym, flags) END
- END KeyEvent;
- PROCEDURE Close*;
- BEGIN
- IF sender # NIL THEN CloseVNC(sender.c) END
- END Close;
- END Window;
- TYPE
- Receiver = OBJECT
- VAR c: Connection; exception, double: BOOLEAN;
- PROCEDURE &Init*(c: Connection);
- BEGIN
- SELF.c := c; exception := FALSE; double := FALSE
- END Init;
- BEGIN {ACTIVE, SAFE}
- IF exception THEN
- IF TRUE OR Trace THEN KernelLog.Enter; KernelLog.String("Receiver exception"); KernelLog.Exit END;
- IF double THEN RETURN END;
- double := TRUE
- ELSE
- exception := TRUE;
- IF Trace THEN KernelLog.Enter; KernelLog.String("Receiver enter"); KernelLog.Exit END;
- REPEAT
- IF (PollTimeout = 0) & (c.sender # NIL) THEN c.sender.HandleTimeout END;
- AwaitResponse(c)
- UNTIL c.res # Ok;
- IF Trace THEN KernelLog.Enter; KernelLog.String("Receiver exit"); KernelLog.Exit END
- END;
- IF c.sender # NIL THEN c.sender.Terminate END;
- IF c.w # NIL THEN
- c.w.manager.Remove(c.w);
- c.w := NIL
- END
- END Receiver;
- TYPE
- Sender = OBJECT
- VAR
- c: Connection;
- head, middle, tail, lx, ly: LONGINT;
- res: WORD;
- lkeys : SET;
- buf: ARRAY OutBufSize OF CHAR;
- done, poll: BOOLEAN;
- timer: Objects.Timer;
- PROCEDURE Available(): LONGINT;
- BEGIN
- RETURN (head - tail - 1) MOD LEN(buf)
- END Available;
- PROCEDURE Put(x: CHAR);
- BEGIN
- ASSERT((tail+1) MOD LEN(buf) # head);
- buf[tail] := x; tail := (tail+1) MOD LEN(buf)
- END Put;
- PROCEDURE PutInt(x: LONGINT);
- BEGIN
- Put(CHR(x DIV 100H)); Put(CHR(x MOD 100H))
- END PutInt;
- PROCEDURE Pointer(x, y: LONGINT; keys: SET);
- BEGIN {EXCLUSIVE}
- IF (x >= 0) & (x < c.w.img.width) & (y >= 0) & (y < c.w.img.height) & (Available() >= 6) THEN
- IF Trace THEN
- KernelLog.Enter; KernelLog.String("Ptr "); KernelLog.Int(x, 5); KernelLog.Int(y, 5); KernelLog.Exit
- END;
- Put(5X); (* PointerEvent (sec. 5.2.6) *)
- Put(CHR(SYSTEM.VAL(LONGINT, keys)));
- PutInt(x); PutInt(y);
- lx := x; ly := y; lkeys := keys
- END
- END Pointer;
- PROCEDURE Wheel(dz : LONGINT);
- VAR keys : SET;
- BEGIN {EXCLUSIVE}
- IF (Available() >= 6) THEN
- IF Trace THEN
- KernelLog.Enter; KernelLog.String("Wheel "); KernelLog.Int(dz, 5); KernelLog.Exit
- END;
- Put(5X); (* PointerEvent (sec. 5.2.6) *)
- keys := lkeys;
- IF dz < 0 THEN INCL(keys, 3) END;
- IF dz > 0 THEN INCL(keys, 4) END;
- Put(CHR(SYSTEM.VAL(LONGINT, keys)));
- PutInt(lx); PutInt(ly)
- END
- END Wheel;
- PROCEDURE Key(keysym: LONGINT; flags: SET);
- BEGIN {EXCLUSIVE}
- IF Available() >= 8 THEN
- Put(4X); (* KeyEvent (sec. 5.2.5) *)
- IF Inputs.Release IN flags THEN Put(0X) ELSE Put(1X) END;
- PutInt(0); PutInt(0); PutInt(keysym)
- END
- END Key;
- PROCEDURE Paste(r: Streams.Reader);
- VAR key: LONGINT;
- BEGIN {EXCLUSIVE}
- LOOP
- key := ORD(r.Get());
- IF r.res # 0 THEN EXIT END;
- AWAIT(Available() >= 16);
- (* down key *)
- Put(4X); (* KeyEvent (sec. 5.2.5) *)
- Put(1X); PutInt(0); PutInt(0); PutInt(key);
- (* up key *)
- Put(4X); (* KeyEvent (sec. 5.2.5) *)
- Put(0X); PutInt(0); PutInt(0); PutInt(key)
- END
- END Paste;
- PROCEDURE AwaitEvent;
- BEGIN {EXCLUSIVE}
- AWAIT((head # tail) OR poll OR done);
- IF ~done & (Available() >= 10) THEN
- Put(3X); (* FramebufferUpdateRequest (sec. 5.2.4) *)
- Put(1X); (* incremental *)
- PutInt(0); PutInt(0); PutInt(c.w.img.width); PutInt(c.w.img.height)
- END;
- middle := tail; poll := FALSE
- END AwaitEvent;
- PROCEDURE SendEvents;
- BEGIN
- IF middle >= head THEN
- c.pcb.Send(buf, head, middle-head, FALSE, res)
- ELSE (* split buffer *)
- c.pcb.Send(buf, head, LEN(buf)-head, FALSE, res);
- IF res = Ok THEN c.pcb.Send(buf, 0, middle, FALSE, res) END
- END;
- head := middle
- END SendEvents;
- PROCEDURE Terminate;
- BEGIN {EXCLUSIVE}
- done := TRUE
- END Terminate;
- PROCEDURE HandleTimeout;
- BEGIN {EXCLUSIVE}
- poll := TRUE;
- IF (PollTimeout > 0) & ~done THEN
- Objects.SetTimeout(timer, SELF.HandleTimeout, PollTimeout)
- END
- END HandleTimeout;
- PROCEDURE &Init*(c: Connection);
- BEGIN
- NEW(timer);
- SELF.c := c; head := 0; middle := 0; tail := 0; res := Ok; done := FALSE
- END Init;
- BEGIN {ACTIVE}
- IF Trace THEN KernelLog.Enter; KernelLog.String("Sender enter"); KernelLog.Exit END;
- LOOP
- AwaitEvent;
- IF done THEN EXIT END;
- IF TraceAudio THEN Beep.Beep(BellFreq) END;
- IF Trace THEN
- KernelLog.Enter; KernelLog.String("Events "); KernelLog.Int(head, 5); KernelLog.Int(middle, 5); KernelLog.Exit
- END;
- SendEvents;
- IF TraceAudio THEN Beep.Beep(0) END;
- IF res # Ok THEN EXIT END
- END;
- Objects.CancelTimeout(timer);
- IF Trace THEN KernelLog.Enter; KernelLog.String("Sender exit"); KernelLog.Exit END
- END Sender;
- TYPE
- Bell = OBJECT
- VAR timer: Objects.Timer;
- PROCEDURE Ring;
- BEGIN {EXCLUSIVE}
- IF timer = NIL THEN NEW(timer) END;
- Objects.SetTimeout(timer, SELF.HandleTimeout, BellDelay); (* ignore race with expired, but unscheduled timer *)
- Beep.Beep(BellFreq)
- END Ring;
- PROCEDURE HandleTimeout;
- BEGIN {EXCLUSIVE}
- Beep.Beep(0)
- END HandleTimeout;
- END Bell;
- VAR
- pool: ConnectionPool;
- bell: Bell;
- PROCEDURE ReceiveBytes(c: Connection; VAR buf: ARRAY OF CHAR; size: LONGINT; VAR len: LONGINT);
- VAR dst, n: LONGINT;
- BEGIN
- IF c.res = Ok THEN
- dst := 0; len := 0;
- LOOP
- IF size <= 0 THEN EXIT END;
- n := MIN(c.rcvbuflen, size); (* n is number of bytes to copy from buffer now *)
- IF n = 0 THEN (* buffer empty *)
- (* attempt to read at least size bytes, but at most a full buffer *)
- c.pcb.Receive(c.rcvbuf^, 0, LEN(c.rcvbuf), size, n, c.res);
- IF c.res # Ok THEN EXIT END;
- c.rcvbufpos := 0; c.rcvbuflen := n;
- n := MIN(n, size) (* n is number of bytes to copy from buffer now *)
- END;
- ASSERT(dst+n <= LEN(buf)); (* index check *)
- SYSTEM.MOVE(ADDRESSOF(c.rcvbuf[c.rcvbufpos]), ADDRESSOF(buf[dst]), n);
- INC(c.rcvbufpos, n); DEC(c.rcvbuflen, n);
- INC(dst, n); DEC(size, n); INC(len, n)
- END
- ELSE
- buf[0] := 0X; len := 0
- END
- END ReceiveBytes;
- PROCEDURE Receive(c: Connection; VAR ch: CHAR);
- VAR len: LONGINT; buf: ARRAY 1 OF CHAR;
- BEGIN
- IF c.rcvbuflen > 0 THEN
- ch := c.rcvbuf[c.rcvbufpos]; INC(c.rcvbufpos); DEC(c.rcvbuflen)
- ELSE
- ReceiveBytes(c, buf, 1, len);
- ch := buf[0]
- END
- END Receive;
- PROCEDURE ReceiveInt(c: Connection; VAR x: LONGINT);
- VAR len: LONGINT; buf: ARRAY 2 OF CHAR;
- BEGIN
- ReceiveBytes(c, buf, 2, len);
- x := Network.GetNet2(buf, 0)
- END ReceiveInt;
- PROCEDURE ReceiveLInt(c: Connection; VAR x: LONGINT);
- VAR len: LONGINT; buf: ARRAY 4 OF CHAR;
- BEGIN
- ReceiveBytes(c, buf, 4, len);
- x := Network.GetNet4(buf, 0)
- END ReceiveLInt;
- PROCEDURE ReceiveIgnore(c: Connection; len: LONGINT);
- VAR ch: CHAR;
- BEGIN
- WHILE (len > 0) & (c.res = Ok) DO Receive(c, ch); DEC(len) END
- END ReceiveIgnore;
- PROCEDURE Send(c: Connection; x: CHAR);
- VAR buf: ARRAY 1 OF CHAR;
- BEGIN
- buf[0] := x; c.pcb.Send(buf, 0, 1, FALSE, c.res)
- END Send;
- (* Get the server's version number and send our version number. *)
- PROCEDURE DoVersion(c: Connection): BOOLEAN;
- VAR buf: ARRAY 16 OF CHAR; len: LONGINT;
- BEGIN
- ReceiveBytes(c, buf, 12, len);
- IF c.res = Ok THEN
- IF Trace THEN
- buf[11] := 0X;
- KernelLog.Enter; KernelLog.String("Version="); KernelLog.String(buf); KernelLog.Exit
- END;
- buf := "RFB 003.003"; buf[11] := 0AX;
- c.pcb.Send(buf, 0, 12, FALSE, c.res)
- END;
- RETURN c.res = Ok
- END DoVersion;
- (* Authenticate ourself with the server. *)
- PROCEDURE DoAuthentication(c: Connection; VAR pwd: ARRAY OF CHAR): BOOLEAN;
- VAR x, len, len0: LONGINT; buf: ARRAY 64 OF CHAR; cipher: ARRAY 16 OF CHAR; d: DES.DES;
- BEGIN
- ReceiveLInt(c, x);
- IF c.res = Ok THEN
- IF Trace THEN
- KernelLog.Enter; KernelLog.String("Scheme="); KernelLog.Int(x, 1); KernelLog.Exit
- END;
- IF x = 0 THEN (* failed *)
- ReceiveLInt(c, len); (* read reason *)
- WHILE (len > 0) & (c.res = Ok) DO
- len0 := MIN(len, LEN(buf));
- ReceiveBytes(c, buf, len0, len0);
- DEC(len, len0)
- END;
- IF Trace & (c.res = Ok) THEN (* write last part of reason (typically only one part) *)
- IF len0 = LEN(buf) THEN DEC(len0) END;
- buf[len0] := 0X;
- KernelLog.Enter; KernelLog.String("Reason="); KernelLog.String(buf); KernelLog.Exit
- END
- ELSIF x = 2 THEN (* VNC authentication *)
- ReceiveBytes(c, buf, 16, len); (* challenge *)
- IF c.res = Ok THEN
- NEW(d);
- d.SetKey(pwd);
- d.Encrypt(buf, 0, cipher, 0); (* Two 8-Byte-Blocks *)
- d.Encrypt(buf, 8, cipher, 8);
- c.pcb.Send(cipher, 0, 16, FALSE, c.res);
- IF c.res = Ok THEN
- ReceiveLInt(c, x);
- IF c.res = Ok THEN
- c.res := x (* 0=Ok, 1=failed, 2=too-many *)
- END
- END
- END
- ELSE (* no or unknown authentication *)
- (* skip *)
- END
- END;
- RETURN c.res = Ok
- END DoAuthentication;
- (* Set up an RFB encodings message. "code" contains the codes in preferred order. "len" returns the message length. *)
- PROCEDURE PutEncodings(VAR buf: ARRAY OF CHAR; ofs: LONGINT; code: ARRAY OF CHAR; VAR len: LONGINT);
- VAR i: LONGINT;
- BEGIN
- buf[ofs] := 2X; (* SetEncodings (sec. 5.2.3) *)
- buf[ofs+1] := 0X; (* padding *)
- i := 0;
- WHILE code[i] # 0X DO
- Network.PutNet4(buf, ofs + 4*(i+1), ORD(code[i])-ORD("0"));
- INC(i)
- END;
- Network.PutNet2(buf, ofs+2, i); (* number-of-encodings *)
- len := 4*(i+1)
- END PutEncodings;
- (* Initialise the transfer format. *)
- PROCEDURE DoInit(c: Connection): BOOLEAN;
- VAR len, len0, w, h: LONGINT; buf: ARRAY 64 OF CHAR; pixel: Raster.Pixel; ptr: WMWindowManager.PointerInfo;
- BEGIN
- IF Shared THEN Send(c, 1X) ELSE Send(c, 0X) END;
- IF c.res = Ok THEN
- ReceiveBytes(c, buf, 24, len); (* initialization message *)
- IF c.res = Ok THEN
- w := Network.GetNet2(buf, 0); h := Network.GetNet2(buf, 2);
- len := Network.GetNet4(buf, 20);
- IF Trace THEN
- KernelLog.Enter;
- KernelLog.String("Server: width="); KernelLog.Int(w, 1);
- KernelLog.String(" height="); KernelLog.Int(h, 1);
- KernelLog.String(" bpp="); KernelLog.Int(ORD(buf[4]), 1);
- KernelLog.String(" depth="); KernelLog.Int(ORD(buf[5]), 1);
- KernelLog.String(" bigendian="); KernelLog.Int(ORD(buf[6]), 1);
- KernelLog.String(" truecolor="); KernelLog.Int(ORD(buf[7]), 1); KernelLog.Ln;
- KernelLog.String(" redmax="); KernelLog.Int(Network.GetNet2(buf, 8), 1);
- KernelLog.String(" greenmax="); KernelLog.Int(Network.GetNet2(buf, 10), 1);
- KernelLog.String(" bluemax="); KernelLog.Int(Network.GetNet2(buf, 12), 1);
- KernelLog.String(" redshift="); KernelLog.Int(ORD(buf[14]), 1);
- KernelLog.String(" greenshift="); KernelLog.Int(ORD(buf[15]), 1);
- KernelLog.String(" blueshift="); KernelLog.Int(ORD(buf[16]), 1);
- KernelLog.String(" len="); KernelLog.Int(len, 1);
- KernelLog.Exit
- END;
- WHILE (len > 0) & (c.res = Ok) DO
- len0 := MIN(len, LEN(buf));
- ReceiveBytes(c, buf, len0, len0);
- DEC(len, len0)
- END;
- IF c.res = Ok THEN
- IF Trace THEN (* write last part of name (typically only one part) *)
- IF len0 = LEN(buf) THEN DEC(len0) END;
- buf[len0] := 0X;
- KernelLog.Enter; KernelLog.String("Name="); KernelLog.String(buf); KernelLog.Exit
- END;
- (* choose our preferred format *)
- Raster.InitMode(c.mode, Raster.srcCopy);
- NEW(c.w, w, h, FALSE);
- NEW(ptr); ptr.hotX := 2; ptr.hotY := 2;
- NEW(ptr.img); Raster.Create(ptr.img, 4, 4, Raster.BGRA8888);
- Raster.SetRGBA(pixel, 255, 255, 255, AlphaCursor);
- Raster.Fill(ptr.img, 0, 0, 4, 4, pixel, c.mode);
- Raster.SetRGBA(pixel, 0, 0, 0, AlphaCursor);
- Raster.Fill(ptr.img, 1, 1, 3, 3, pixel, c.mode);
- c.w.SetPointerInfo(ptr);
- WMWindowManager.DefaultAddWindow(c.w);
- Raster.SetRGB(pixel, 0, 0, 0);
- Raster.Fill(c.w.img, 0, 0, c.w.img.width, c.w.img.height, pixel, c.mode);
- c.w.Invalidate(Rect.MakeRect(0, 0, c.w.img.width, c.w.img.height));
- NEW(c.nb);
- IF c.w.img.fmt.code IN {Raster.bgr888, Raster.bgra8888} THEN
- c.fmt := Raster.BGRA8888
- ELSE
- c.fmt := Raster.BGR565
- END;
- c.bytesPerPixel := c.fmt.bpp DIV 8;
- ASSERT(ImgBufSize >= w*c.bytesPerPixel); (* at least one full line will fit buffer *)
- NEW(c.imgbuf, ImgBufSize);
- (* set up client format message *)
- buf[0] := 0X; (* SetPixelFormat message (sec. 5.2.1) *)
- buf[1] := 0X; buf[2] := 0X; buf[3] := 0X; (* padding *)
- buf[4] := CHR(c.bytesPerPixel*8); (* bits-per-pixel (8, 16 or 32) on wire *)
- buf[5] := CHR(c.fmt.bpp); (* depth (8, 16, 24 or 32) *)
- buf[6] := 0X; (* big-endian-flag *)
- buf[7] := 1X; (* true-colour-flag *)
- CASE c.fmt.code OF
- Raster.bgr565:
- Network.PutNet2(buf, 8, 31); (* red-max *)
- Network.PutNet2(buf, 10, 63); (* green-max *)
- Network.PutNet2(buf, 12, 31); (* blue-max *)
- buf[14] := CHR(11); (* red-shift *)
- buf[15] := CHR(5); (* green-shift *)
- buf[16] := CHR(0) (* blue-shift *)
- |Raster.bgra8888:
- Network.PutNet2(buf, 8, 255); (* red-max *)
- Network.PutNet2(buf, 10, 255); (* green-max *)
- Network.PutNet2(buf, 12, 255); (* blue-max *)
- buf[14] := CHR(16); (* red-shift *)
- buf[15] := CHR(8); (* green-shift *)
- buf[16] := CHR(0) (* blue-shift *)
- END;
- PutEncodings(buf, 20, "15420", len); (* 0=raw, 1=copy rectangle, 2=RRE, 4=CoRRE, 5=hextile *)
- IF Trace THEN
- KernelLog.Enter; KernelLog.String("Client:"); KernelLog.Ln;
- KernelLog.Buffer(buf, 0, 20+len); KernelLog.Exit
- END;
- c.pcb.Send(buf, 0, 20+len, FALSE, c.res)
- END
- END
- END;
- RETURN c.res = Ok
- END DoInit;
- (* Send a framebuffer update request. *)
- PROCEDURE SendRequest(c: Connection; inc: BOOLEAN; x, y, w, h: LONGINT);
- VAR buf: ARRAY 10 OF CHAR;
- BEGIN
- IF Trace THEN
- KernelLog.Enter; KernelLog.String("Req"); KernelLog.Int(x, 5); KernelLog.Int(y, 5);
- KernelLog.Int(w, 5); KernelLog.Int(h, 5);
- IF inc THEN KernelLog.String(" inc") END;
- KernelLog.Exit
- END;
- buf[0] := 3X; (* FramebufferUpdateRequest (sec. 5.2.4) *)
- IF inc THEN buf[1] := 1X ELSE buf[1] := 0X END;
- Network.PutNet2(buf, 2, x); Network.PutNet2(buf, 4, y);
- Network.PutNet2(buf, 6, w); Network.PutNet2(buf, 8, h);
- c.pcb.Send(buf, 0, 10, FALSE, c.res)
- END SendRequest;
- (* Update an area of the display. *)
- PROCEDURE UpdateDisplay(c: Connection; x, y, w, h: LONGINT);
- (*VAR pixel: Raster.Pixel; mode: Raster.Mode;*)
- BEGIN
- (*
- Raster.SetRGB(pixel, 255, 255, 255);
- Raster.InitMode(mode, Raster.InvDst);
- Raster.Fill(c.w.img, 0, 0, 5, 5, pixel, mode);
- IF (x # 0) OR (y # 0) THEN c.w.AddDirty(0, 0, 10, 10) END;
- *)
- c.w.Invalidate(Rect.MakeRect(x, y, x + w, y + h))
- END UpdateDisplay;
- (* Receive a raw rectangle. *)
- PROCEDURE ReceiveRaw(c: Connection; x, y, w, h: LONGINT);
- VAR bh, h0, len, i: LONGINT;
- BEGIN
- IF Trace THEN
- KernelLog.Enter; KernelLog.String("Raw"); KernelLog.Int(x, 5); KernelLog.Int(y, 5);
- KernelLog.Int(w, 5); KernelLog.Int(h, 5); KernelLog.Exit
- END;
- bh := LEN(c.imgbuf^) DIV (w*c.bytesPerPixel); (* number of lines that will fit in buffer *)
- Raster.Init(c.nb, w, bh, c.fmt, w*c.bytesPerPixel, ADDRESSOF(c.imgbuf[0]));
- WHILE h > 0 DO
- IF h >= bh THEN h0 := bh ELSE h0 := h END;
- len := h0*w*c.bytesPerPixel;
- ReceiveBytes(c, c.imgbuf^, len, len);
- IF c.res # Ok THEN RETURN END;
- IF c.bytesPerPixel = 4 THEN (* fix alpha values *)
- FOR i := 0 TO len-1 BY 4 DO c.imgbuf[i+Raster.a] := 0FFX END
- END;
- Raster.Copy(c.nb, c.w.img, 0, 0, w, h0, x, y, c.mode);
- DEC(h, h0); INC(y, h0)
- END
- END ReceiveRaw;
- (* Receive a copy rectangle message. *)
- PROCEDURE ReceiveCopyRect(c: Connection; x, y, w, h: LONGINT);
- VAR sx, sy: LONGINT;
- BEGIN
- ReceiveInt(c, sx); (* src-x-position *)
- IF c.res = Ok THEN
- ReceiveInt(c, sy); (* src-y-position *)
- IF c.res = Ok THEN
- IF Trace THEN
- KernelLog.Enter; KernelLog.String("Copy"); KernelLog.Int(x, 5); KernelLog.Int(y, 5);
- KernelLog.Int(w, 5); KernelLog.Int(h, 5); KernelLog.Int(sx, 5); KernelLog.Int(sy, 5); KernelLog.Exit
- END;
- Raster.Copy(c.w.img, c.w.img, sx, sy, sx+w, sy+h, x, y, c.mode)
- END
- END
- END ReceiveCopyRect;
- (* Receive a pixel. *)
- PROCEDURE ReceivePixel(c: Connection; VAR pixel: Raster.Pixel);
- VAR len: LONGINT; buf: ARRAY 4 OF CHAR;
- BEGIN
- ReceiveBytes(c, buf, c.bytesPerPixel, len);
- c.fmt.unpack(c.fmt, ADDRESSOF(buf[0]), 0, pixel);
- pixel[Raster.a] := 0FFX
- END ReceivePixel;
- (* Receive an RRE rectangle message. *)
- PROCEDURE ReceiveRRE(c: Connection; x, y, w, h: LONGINT);
- VAR n, len, sx, sy: LONGINT; pixel: Raster.Pixel; buf: ARRAY 8 OF CHAR;
- BEGIN
- IF Trace THEN
- KernelLog.Enter; KernelLog.String("RRE"); KernelLog.Int(x, 5); KernelLog.Int(y, 5);
- KernelLog.Int(w, 5); KernelLog.Int(h, 5); KernelLog.Exit
- END;
- ReceiveLInt(c, n); (* number-of-subrectangles *)
- IF c.res = Ok THEN
- ReceivePixel(c, pixel);
- IF c.res = Ok THEN
- Raster.Fill(c.w.img, x, y, x+w, y+h, pixel, c.mode);
- WHILE n > 0 DO
- ReceivePixel(c, pixel);
- IF c.res # Ok THEN RETURN END;
- ReceiveBytes(c, buf, 8, len);
- IF c.res # Ok THEN RETURN END;
- sx := x+Network.GetNet2(buf, 0); sy := y+Network.GetNet2(buf, 2);
- Raster.Fill(c.w.img, sx, sy, sx+Network.GetNet2(buf, 4), sy+Network.GetNet2(buf, 6), pixel, c.mode);
- DEC(n)
- END
- END
- END
- END ReceiveRRE;
- (* Receive a CoRRE rectangle message. *)
- PROCEDURE ReceiveCoRRE(c: Connection; x, y, w, h: LONGINT);
- VAR n, len, sx, sy: LONGINT; pixel: Raster.Pixel; buf: ARRAY 4 OF CHAR;
- BEGIN
- IF Trace THEN
- KernelLog.Enter; KernelLog.String("CoRRE"); KernelLog.Int(x, 5); KernelLog.Int(y, 5);
- KernelLog.Int(w, 5); KernelLog.Int(h, 5); KernelLog.Exit
- END;
- ReceiveLInt(c, n); (* number-of-subrectangles *)
- IF c.res = Ok THEN
- ReceivePixel(c, pixel);
- IF c.res = Ok THEN
- Raster.Fill(c.w.img, x, y, x+w, y+h, pixel, c.mode);
- WHILE n > 0 DO
- ReceivePixel(c, pixel);
- IF c.res # Ok THEN RETURN END;
- ReceiveBytes(c, buf, 4, len);
- IF c.res # Ok THEN RETURN END;
- sx := x+ORD(buf[0]); sy := y+ORD(buf[1]);
- Raster.Fill(c.w.img, sx, sy, sx+ORD(buf[2]), sy+ORD(buf[3]), pixel, c.mode);
- DEC(n)
- END
- END
- END
- END ReceiveCoRRE;
- (* Receive a hextile rectangle message. *)
- PROCEDURE ReceiveHextile(c: Connection; x, y, w, h: LONGINT);
- CONST
- Raw = 0; BackgroundSpecified = 1; ForegroundSpecified = 2; AnySubrects = 3; SubrectsColoured = 4;
- VAR
- row, col, i, tw, th, wmin, hmin, sx, sy, sw, sh: LONGINT;
- bg, fg, pixel: Raster.Pixel; sub: SET; ch: CHAR;
- BEGIN
- IF Trace THEN
- KernelLog.Enter; KernelLog.String("Hex"); KernelLog.Int(x, 5); KernelLog.Int(y, 5);
- KernelLog.Int(w, 5); KernelLog.Int(h, 5); KernelLog.Exit
- END;
- wmin := (w-1) MOD 16 + 1; hmin := (h-1) MOD 16 + 1;
- FOR row := 0 TO (h-1) DIV 16 DO
- IF row < (h-1) DIV 16 THEN th := 16 ELSE th := hmin END;
- FOR col := 0 TO (w-1) DIV 16 DO
- IF col < (w-1) DIV 16 THEN tw := 16 ELSE tw := wmin END;
- Receive(c, ch);
- IF c.res # Ok THEN RETURN END;
- sub := SYSTEM.VAL(SET, LONG(ORD(ch)));
- IF Raw IN sub THEN
- ReceiveRaw(c, x + 16*col, y + 16*row, tw, th)
- ELSE
- IF BackgroundSpecified IN sub THEN ReceivePixel(c, bg) END;
- IF ForegroundSpecified IN sub THEN ReceivePixel(c, fg) END;
- Raster.Fill(c.w.img, x + 16*col, y + 16*row, x + 16*col + tw, y + 16*row + th, bg, c.mode);
- IF AnySubrects IN sub THEN
- Receive(c, ch);
- IF c.res # Ok THEN RETURN END;
- FOR i := 1 TO ORD(ch) DO
- IF SubrectsColoured IN sub THEN ReceivePixel(c, pixel) ELSE pixel := fg END;
- Receive(c, ch);
- IF c.res # Ok THEN RETURN END;
- sx := ORD(ch) DIV 16; sy := ORD(ch) MOD 16;
- Receive(c, ch);
- IF c.res # Ok THEN RETURN END;
- sw := ORD(ch) DIV 16 + 1; sh := ORD(ch) MOD 16 + 1;
- Raster.Fill(c.w.img, x + 16*col + sx, y + 16*row + sy, x + 16*col + sx + sw,
- y + 16*row + sy + sh, pixel, c.mode)
- END
- END
- END
- END;
- IF TraceVisual THEN UpdateDisplay(c, x, y + 16*row, w, th) END
- END
- END ReceiveHextile;
- (* Receive a rectangle message. *)
- PROCEDURE ReceiveRectangle(c: Connection);
- VAR len, x, y, w, h: LONGINT; buf: ARRAY 12 OF CHAR;
- BEGIN
- ReceiveBytes(c, buf, 12, len);
- x := Network.GetNet2(buf, 0); y := Network.GetNet2(buf, 2);
- w := Network.GetNet2(buf, 4); h := Network.GetNet2(buf, 6);
- CASE Network.GetNet4(buf, 8) OF (* encoding-type *)
- 0: ReceiveRaw(c, x, y, w, h)
- |1: ReceiveCopyRect(c, x, y, w, h)
- |2: ReceiveRRE(c, x, y, w, h)
- |4: ReceiveCoRRE(c, x, y, w, h)
- |5: ReceiveHextile(c, x, y, w, h)
- END;
- UpdateDisplay(c, x, y, w, h)
- END ReceiveRectangle;
- (* Receive and react on one message from the server. *)
- PROCEDURE AwaitResponse(c: Connection);
- VAR len: LONGINT; ch: CHAR;
- BEGIN
- Receive(c, ch);
- IF c.res = Ok THEN
- CASE ORD(ch) OF
- 0: (* FramebufferUpdate (sec. 5.3.1) *)
- Receive(c, ch); (* padding *)
- IF c.res = Ok THEN ReceiveInt(c, len) END; (* number-of-rectangles *)
- WHILE (c.res = Ok) & (len > 0) DO
- ReceiveRectangle(c); DEC(len)
- END
- |1: (* SetColourMapEntries (sec. 5.3.2) *)
- Receive(c, ch); (* padding *)
- IF c.res = Ok THEN ReceiveInt(c, len) END; (* first-colour *)
- IF c.res = Ok THEN ReceiveInt(c, len) END; (* number-of-colours *)
- IF (c.res = Ok) & (len > 0) THEN ReceiveIgnore(c, len*6) END
- |2: (* Bell (sec. 5.3.3) *)
- bell.Ring
- |3: (* ServerCutText (sec. 5.3.4) *)
- ReceiveIgnore(c, 3); (* padding *)
- ReceiveLInt(c, len);
- IF (c.res = Ok) & (len > 0) THEN ReceiveIgnore(c, len) END
- END
- END
- END AwaitResponse;
- (* Open a VNC connection to the specified server and port. *)
- PROCEDURE OpenVNC*(c: Connection; server: IP.Adr; port: LONGINT; pwd: ARRAY OF CHAR);
- BEGIN
- NEW(c.pcb); c.fip := server;
- c.pcb.Open(TCP.NilPort, server, port, c.res);
- c.pcb.DelaySend(FALSE);
- IF c.res = Ok THEN
- c.pcb.AwaitState(TCP.OpenStates, TCP.ClosedStates, OpenTimeout, c.res)
- END;
- IF c.res = Ok THEN
- NEW(c.rcvbuf, InBufSize); c.rcvbufpos := 0; c.rcvbuflen := 0;
- IF DoVersion(c) & DoAuthentication(c, pwd) & DoInit(c) THEN
- SendRequest(c, FALSE, 0, 0, c.w.img.width, c.w.img.height);
- IF c.res = Ok THEN
- NEW(c.receiver, c);
- NEW(c.sender, c);
- c.w.sender := c.sender;
- IF PollTimeout # 0 THEN c.sender.HandleTimeout END (* start the timer *)
- ELSE
- CloseVNC(c)
- END
- ELSE
- CloseVNC(c)
- END
- END;
- IF Trace & (c # NIL) THEN
- KernelLog.Enter; KernelLog.String("OpenVNC="); KernelLog.Int(c.res, 1); KernelLog.Exit
- END
- END OpenVNC;
- (* Close a VNC connection. *)
- PROCEDURE CloseVNC*(VAR c: Connection);
- VAR res: WORD;
- BEGIN
- pool.Remove(c);
- c.pcb.Close();
- c.pcb.AwaitState(TCP.ClosedStates, {}, CloseTimeout, res);
- IF Trace THEN
- KernelLog.Enter; KernelLog.String("CloseVNC="); KernelLog.Int(res, 1); KernelLog.Exit
- END;
- (*c.pcb := NIL*)
- END CloseVNC;
- PROCEDURE PrintConnection(c: Connection; out : Streams.Writer);
- VAR res: WORD; name: ARRAY 128 OF CHAR;
- BEGIN
- out.Int(c.id, 1);
- CASE c.fmt.code OF
- Raster.bgr565:
- out.String(" 16-bit")
- |Raster.bgra8888:
- out.String(" 32-bit")
- END;
- IF (c.w # NIL) & (c.w.img # NIL) THEN
- out.Char(" "); out.Int(c.w.img.width, 1);
- out.Char("x"); out.Int(c.w.img.height, 1)
- END;
- DNS.HostByNumber(c.fip, name, res);
- out.Char(" "); out.String(name);
- out.Ln
- END PrintConnection;
- PROCEDURE Show*(context : Commands.Context);
- BEGIN
- IF ~pool.Empty() THEN
- context.out.String("VNC connections"); context.out.Ln;
- pool.Enumerate(PrintConnection, context.out);
- ELSE
- context.out.String("No open connections"); context.out.Ln
- END;
- END Show;
- PROCEDURE ReadString(r: Streams.Reader; VAR s: ARRAY OF CHAR);
- VAR i: LONGINT;
- BEGIN
- i := 0; WHILE (r.res = 0) & (r.Peek() # " ") DO r.Char(s[i]); INC(i) END;
- s[i] := 0X; r.SkipBytes(1)
- END ReadString;
- PROCEDURE Open*(context : Commands.Context); (** server[pwd|?] port *)
- VAR
- server: IP.Adr; res: WORD; port: LONGINT;
- c: Connection; pwd: ARRAY 32 OF CHAR; svr, title: ARRAY 128 OF CHAR;
- BEGIN
- context.arg.SkipWhitespace; context.arg.String(svr); context.arg.SkipWhitespace;
- IF (context.arg.Peek() < "0") OR (context.arg.Peek() > "9") THEN context.arg.String(pwd) END;
- context.arg.SkipWhitespace; context.arg.Int(port, FALSE);
- IF (context.arg.res = Streams.Ok) OR (context.arg.res = Streams.EOF) THEN
- DNS.HostByName(svr, server, res);
- IF (res = Ok) & (port # 0) THEN
- IF pwd = "?" THEN
- IF Dialogs.QueryPassword("Enter VNC Password", pwd) # Dialogs.ResOk THEN RETURN END
- END;
- NEW(c);
- OpenVNC(c, server, port, pwd);
- IF c.res = Ok THEN
- pool.Add(c);
- COPY(svr, title); Files.AppendStr(" Port ", title); Files.AppendInt(port, title); Files.AppendStr(" - VNC ", title); Files.AppendInt(c.id, title);
- c.w.SetTitle(WMWindowManager.NewString(title));
- Show(context)
- ELSE
- context.error.String("Error "); context.error.Int(c.res, 1); context.error.Ln
- END
- ELSE
- context.error.String("Error: not found"); context.error.Ln
- END
- ELSE
- context.error.String("Error: expected server[ pwd] port"); context.error.Ln
- END;
- END Open;
- PROCEDURE Paste*(context : Commands.Context); (** connection text *)
- VAR i: LONGINT; c: Connection;
- BEGIN
- context.arg.SkipWhitespace; context.arg.Int(i, FALSE);
- c := pool.Find(i);
- IF (c # NIL) & (c.sender # NIL) THEN
- IF context.arg.Peek() = " " THEN context.arg.SkipBytes(1) END;
- c.sender.Paste(context.arg);
- END;
- END Paste;
- BEGIN
- NEW(bell); NEW(pool)
- END VNC.
- VNC.Open portnoy.ethz.ch 5901 ~
- VNC.Show
- VNC.Paste 0 Hello world~
- System.Free VNC ~
|