VNCServer.Mod 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813
  1. MODULE VNCServer; (** AUTHOR "TF"; PURPOSE "New VNC Server"; *)
  2. IMPORT
  3. SYSTEM, Streams, TCP, IP, WMRectangles,
  4. KernelLog, DES, Random, Machine, Kernel, Inputs, Raster,
  5. Strings;
  6. CONST
  7. Version = "RFB 003.003";
  8. TraceVersion = 0;
  9. TraceAuthentication = 1;
  10. TraceMsg = 2;
  11. TraceKeyEvent = 3;
  12. Trace = { } ;
  13. (* encodings *)
  14. EncRaw = 0; EncCopyRect = 1; EncRRE = 2; EncCoRRE= 4; EncHextile =5; EncZRLE = 16;
  15. (* Authentication constants *)
  16. AuthNone = 1; AuthVNC = 2; AuthOk = 0; AuthFailed = 1;
  17. (* hextile flags *)
  18. HexRaw = 1; HexBGSpecified = 2; HexFGSpecified = 4; HexAnySubrects = 8; HexSubrectsColoured = 16;
  19. MaxRect = 40;
  20. MaxWidth = 4096;
  21. MaxCutSize = 64 * 1024;
  22. BundleRectangles = TRUE;
  23. BigPackets = TRUE;
  24. SendFBUpdatePacketEarly = TRUE; (* the value of this is questionable *)
  25. TYPE
  26. Rectangle = WMRectangles.Rectangle;
  27. RectBuf = POINTER TO ARRAY OF Rectangle;
  28. WorkBuf = POINTER TO ARRAY OF CHAR;
  29. String = Strings.String;
  30. VNCMouseListener* = PROCEDURE {DELEGATE} (x, y, dz : LONGINT; keys : SET);
  31. VNCKeyboardListener* = PROCEDURE {DELEGATE} (ucs : LONGINT; flags : SET; keysym : LONGINT);
  32. VNCClipboardListener* = PROCEDURE {DELEGATE} (text : String);
  33. VNCNofClientsActiveListener* = PROCEDURE {DELEGATE} (nofClients : LONGINT);
  34. PFHextile = ARRAY 16 * 16 OF LONGINT;
  35. VNCInfo* = OBJECT
  36. VAR
  37. name*, password* : ARRAY 64 OF CHAR;
  38. img* : Raster.Image;
  39. ml* : VNCMouseListener;
  40. kl* : VNCKeyboardListener;
  41. cutl* : VNCClipboardListener;
  42. ncal* : VNCNofClientsActiveListener;
  43. width*, height* : LONGINT;
  44. (** only used in service case *)
  45. connection* : TCP.Connection;
  46. agent* : VNCAgent; (** not valid in service init *)
  47. END VNCInfo;
  48. (* the service must fill in the VNCInfo structure. img must not be NIL *)
  49. VNCService* = PROCEDURE {DELEGATE} (vncInfo : VNCInfo);
  50. Agent = OBJECT
  51. VAR
  52. client: TCP.Connection;
  53. next: Agent; s: Server;
  54. END Agent;
  55. PixelFormat = RECORD
  56. sr, sg, sb : LONGINT;
  57. bpp, depth, rmax, gmax, bmax, rshift, gshift, bshift : LONGINT;
  58. bigendian, truecolor, native16 : BOOLEAN;
  59. END;
  60. UpdateQ = OBJECT
  61. VAR buffer : RectBuf;
  62. nofRect : LONGINT;
  63. clip : Rectangle;
  64. agent : VNCAgent;
  65. alive, allowed : BOOLEAN;
  66. PROCEDURE &Init*(agent : VNCAgent; w, h : LONGINT);
  67. BEGIN
  68. SELF.agent := agent; alive := TRUE;
  69. NEW(buffer, MaxRect);
  70. nofRect := 0; clip := WMRectangles.MakeRect(0, 0, w, h)
  71. END Init;
  72. PROCEDURE Add(VAR r : Rectangle);
  73. VAR i, a : LONGINT; e : Rectangle; done : BOOLEAN;
  74. BEGIN {EXCLUSIVE}
  75. WMRectangles.ClipRect(r, clip);
  76. IF WMRectangles.RectEmpty(r) THEN RETURN END;
  77. IF nofRect = 0 THEN buffer[0] := r; nofRect := 1
  78. ELSE
  79. a := WMRectangles.Area(r);
  80. i := 0; done := FALSE;
  81. WHILE ~done & (i < nofRect) DO
  82. e := r; WMRectangles.ExtendRect(e, buffer[i]);
  83. IF WMRectangles.Area(e) <= WMRectangles.Area(buffer[i]) + a THEN buffer[i] := e; done := TRUE END;
  84. INC(i)
  85. END;
  86. IF ~done THEN
  87. IF nofRect < MaxRect THEN buffer[nofRect] := r; INC(nofRect)
  88. ELSE WMRectangles.ExtendRect(buffer[0], r);
  89. END
  90. END
  91. END
  92. END Add;
  93. PROCEDURE GetBuffer(VAR nof : LONGINT; drawBuf : RectBuf);
  94. VAR r, e : Rectangle; i, j, a : LONGINT; done : BOOLEAN;
  95. BEGIN {EXCLUSIVE}
  96. drawBuf[0] := buffer[0]; nof := 1;
  97. FOR j := 1 TO nofRect - 1 DO
  98. r := buffer[j]; a := WMRectangles.Area(r);
  99. i := 0; done := FALSE;
  100. WHILE ~done & (i < nof) DO
  101. e := r; WMRectangles.ExtendRect(e, drawBuf[i]);
  102. IF WMRectangles.Area(e) <= WMRectangles.Area(drawBuf[i]) + a THEN drawBuf[i] := e; done := TRUE END;
  103. INC(i)
  104. END;
  105. IF ~done THEN
  106. ASSERT(nof < MaxRect);
  107. drawBuf[nof] := r; INC(nof)
  108. END
  109. END;
  110. nofRect := 0
  111. END GetBuffer;
  112. PROCEDURE Close;
  113. BEGIN {EXCLUSIVE}
  114. alive := FALSE
  115. END Close;
  116. PROCEDURE SetAllowed;
  117. BEGIN {EXCLUSIVE}
  118. allowed := TRUE
  119. END SetAllowed;
  120. BEGIN {ACTIVE}
  121. LOOP
  122. BEGIN {EXCLUSIVE}
  123. AWAIT(~alive OR allowed & (nofRect > 0));
  124. allowed := FALSE
  125. END;
  126. IF ~alive THEN EXIT END;
  127. agent.DoUpdates
  128. END
  129. END UpdateQ;
  130. VNCAgent* = OBJECT(Agent)
  131. VAR vncInfo : VNCInfo;
  132. in : Streams.Reader; out : Streams.Writer;
  133. pf : PixelFormat;
  134. traceStr : ARRAY 64 OF CHAR;
  135. encodings : SET; (* sencodings the client supports *)
  136. keyState : SET; (* set of currently pressed grey keys *)
  137. updateQ : UpdateQ;
  138. drawRectBuffer : RectBuf; (* declared as field to avoid stack clearing *)
  139. workBuffer : WorkBuf;
  140. allowUpdate : BOOLEAN;
  141. mode : Raster.Mode; (* chached mode to avoid bind *)
  142. pfHextile : PFHextile;
  143. PROCEDURE &Init*(server : Server; client : TCP.Connection; vncInfo : VNCInfo);
  144. BEGIN
  145. s := server; SELF.client := client; SELF.vncInfo := vncInfo;
  146. client.DelaySend(FALSE);
  147. NEW(in, client.Receive, 1024);
  148. NEW(out, client.Send, 4096);
  149. (* defaults *)
  150. pf.bigendian := FALSE; pf.truecolor := TRUE;
  151. pf.rmax := 31; pf.gmax := 63; pf.bmax := 31;
  152. pf.rshift := 11; pf.gshift := 5; pf.bshift := 0;
  153. pf.bpp := 16; pf.depth := 16;
  154. InitPixelFormat(pf);
  155. allowUpdate := FALSE;
  156. Raster.InitMode(mode, Raster.srcCopy);
  157. NEW(drawRectBuffer, MaxRect);
  158. NEW(workBuffer, MaxWidth * 4);
  159. NEW(updateQ, SELF, vncInfo.width, vncInfo.height)
  160. END Init;
  161. PROCEDURE SendVersion() : BOOLEAN;
  162. VAR clientVersion : ARRAY 12 OF CHAR; len : LONGINT;
  163. BEGIN
  164. out.String(Version); out.Char(0AX); out.Update();
  165. IF out.res # Streams.Ok THEN RETURN FALSE END;
  166. in.Bytes(clientVersion, 0, 12, len); clientVersion[11] := 0X;
  167. IF TraceVersion IN Trace THEN KernelLog.String("Client Version : "); KernelLog.String(clientVersion); KernelLog.Ln END;
  168. RETURN (clientVersion = Version)
  169. END SendVersion;
  170. PROCEDURE Authenticate() : BOOLEAN;
  171. VAR challenge, response, clear : ARRAY 16 OF CHAR;
  172. des : DES.DES; seq : Random.Sequence;
  173. i, len : LONGINT; ok : BOOLEAN;
  174. BEGIN
  175. Machine.AtomicInc(NnofAuthenticate);
  176. IF vncInfo.password = "" THEN
  177. Machine.AtomicInc(NnofAuthNone);
  178. out.Net32(AuthNone); out.Update; RETURN out.res = Streams.Ok
  179. ELSE
  180. Machine.AtomicInc(NnofAuthVNC);
  181. out.Net32(AuthVNC);
  182. (* initialize random number generator for challenge *)
  183. NEW(seq); seq.InitSeed(Kernel.GetTicks());
  184. (* generate and send challenge *)
  185. FOR i:=0 TO 15 DO challenge[i] := CHR(seq.Dice(256)); out.Char(challenge[i]) END; out.Update();
  186. IF out.res # Streams.Ok THEN RETURN FALSE END;
  187. NEW(des); des.SetKey(vncInfo.password);
  188. in.Bytes(response, 0, 16, len);
  189. des.Decrypt(response, 0, clear, 0); des.Decrypt(response, 8, clear, 8);
  190. (* check decrypted response against challenge *)
  191. ok := TRUE; FOR i := 0 TO 15 DO IF clear[i] # challenge[i] THEN ok := FALSE END END;
  192. (* inform client *)
  193. IF ~ok THEN
  194. IF TraceAuthentication IN Trace THEN KernelLog.String("Authentication error."); KernelLog.Ln END;
  195. Machine.AtomicInc(NnofAuthFailed);
  196. out.Net32(AuthFailed)
  197. ELSE
  198. IF TraceAuthentication IN Trace THEN KernelLog.String("Authenticated."); KernelLog.Ln END;
  199. Machine.AtomicInc(NnofAuthOk);
  200. out.Net32(AuthOk)
  201. END;
  202. out.Update;
  203. RETURN ok & (out.res = Streams.Ok)
  204. END
  205. END Authenticate;
  206. PROCEDURE CloseAllOtherClients;
  207. BEGIN
  208. s.CloseAllOthers(SELF)
  209. END CloseAllOtherClients;
  210. PROCEDURE Setup() : BOOLEAN;
  211. VAR len : LONGINT;
  212. BEGIN
  213. (* read the client initialization 5.1.3 *)
  214. IF in.Get() # 01X THEN CloseAllOtherClients END; (* Service *)
  215. (* send server initialization 5.1.4 *)
  216. out.Net16(vncInfo.width); out.Net16(vncInfo.height);
  217. (* pixelformat *)
  218. out.Char(CHR(pf.bpp)); out.Char(CHR(pf.depth));
  219. IF pf.bigendian THEN out.Char(1X) ELSE out.Char(0X) END;
  220. IF pf.truecolor THEN out.Char(1X) ELSE out.Char(0X) END;
  221. out.Net16(pf.rmax); out.Net16(pf.gmax); out.Net16(pf.bmax);
  222. out.Char(CHR(pf.rshift)); out.Char(CHR(pf.gshift)); out.Char(CHR(pf.bshift));
  223. out.Char(0X); out.Char(0X); out.Char(0X); (* padding *)
  224. (* name *)
  225. len := 0; WHILE vncInfo.name[len] # 0X DO INC(len) END;
  226. out.Net32(len); out.String(vncInfo.name);
  227. out.Update;
  228. RETURN out.res = Streams.Ok
  229. END Setup;
  230. PROCEDURE SetPixelFormat; (* 5.2.1 *)
  231. BEGIN
  232. (* skip padding *)
  233. in.SkipBytes(3);
  234. (* Pixel format *)
  235. pf.bpp := ORD(in.Get());
  236. pf.depth := ORD(in.Get());
  237. pf.bigendian := in.Get() = 1X;
  238. pf.truecolor := in.Get() = 1X;
  239. pf.rmax := in.Net16();
  240. pf.gmax := in.Net16();
  241. pf.bmax := in.Net16();
  242. pf.rshift := ORD(in.Get());
  243. pf.gshift := ORD(in.Get());
  244. pf.bshift := ORD(in.Get());
  245. (* skip padding *)
  246. in.SkipBytes(3);
  247. InitPixelFormat(pf)
  248. END SetPixelFormat;
  249. PROCEDURE InitPixelFormat(VAR pf : PixelFormat);
  250. VAR t : LONGINT;
  251. BEGIN
  252. t := pf.rmax; pf.sr := 0; WHILE t > 0 DO t := t DIV 2; INC(pf.sr) END; pf.sr := 8 - pf.sr;
  253. t := pf.gmax; pf.sg := 0; WHILE t > 0 DO t := t DIV 2; INC(pf.sg) END; pf.sg := 8 - pf.sg;
  254. t := pf.bmax; pf.sb := 0; WHILE t > 0 DO t := t DIV 2; INC(pf.sb) END; pf.sb := 8 - pf.sb;
  255. pf.native16 := (pf.rmax = 31) & (pf.gmax = 63) & (pf.bmax = 31) & (pf.rshift = 11) & (pf.gshift = 5) & (pf.bshift = 0)
  256. END InitPixelFormat;
  257. (* PROCEDURE TracePixelFormat(VAR pf : PixelFormat);
  258. BEGIN
  259. KernelLog.String("bpp: "); KernelLog.Int(pf.bpp, 4); KernelLog.Ln;
  260. KernelLog.String("depth: "); KernelLog.Int(pf.depth, 4); KernelLog.Ln;
  261. KernelLog.String("bigendian:");
  262. IF pf.bigendian THEN KernelLog.String("TRUE") ELSE KernelLog.String("FALSE") END; KernelLog.Ln;
  263. KernelLog.String("truecolor:");
  264. IF pf.truecolor THEN KernelLog.String("TRUE") ELSE KernelLog.String("FALSE") END; KernelLog.Ln;
  265. KernelLog.String("rmax: "); KernelLog.Int(pf.rmax, 4); KernelLog.Ln;
  266. KernelLog.String("gmax: "); KernelLog.Int(pf.gmax, 4); KernelLog.Ln;
  267. KernelLog.String("bmax: "); KernelLog.Int(pf.bmax, 4); KernelLog.Ln;
  268. KernelLog.String("rshift: "); KernelLog.Int(pf.rshift, 4); KernelLog.Ln;
  269. KernelLog.String("gshift: "); KernelLog.Int(pf.gshift, 4); KernelLog.Ln;
  270. KernelLog.String("bshift: "); KernelLog.Int(pf.bshift, 4); KernelLog.Ln;
  271. END TracePixelFormat;
  272. *)
  273. (** is no longer specified in the 2002 revision of the protocol *)
  274. PROCEDURE FixupColorMapEntries;
  275. VAR nof, first : LONGINT;
  276. BEGIN
  277. KernelLog.String("FixupColorMapEntries no longer supported... "); KernelLog.Ln;
  278. in.SkipBytes(1); first := in.Net16(); nof := in.Net16(); WHILE nof > 0 DO in.SkipBytes(6); DEC(nof) END
  279. END FixupColorMapEntries;
  280. (* supported encodings 5.2.3 *)
  281. PROCEDURE SetEncodings;
  282. VAR nof, e : LONGINT;
  283. BEGIN
  284. (* skip padding *)
  285. in.SkipBytes(1);
  286. encodings := {};
  287. nof := in.Net16();
  288. WHILE (nof > 0) DO e := in.Net32(); IF (e>=0) & (e < 32) THEN INCL(encodings, e) END; DEC(nof) END
  289. END SetEncodings;
  290. PROCEDURE SendRect(VAR r : Rectangle);
  291. BEGIN
  292. out.Net16(r.l); out.Net16(r.t); out.Net16(r.r - r.l); out.Net16(r.b - r.t);
  293. IF EncHextile IN encodings THEN
  294. out.Net32(5);
  295. SendHextile(out, vncInfo.img, mode, pf, workBuffer, pfHextile, r)
  296. ELSE
  297. out.Net32(0);
  298. SendRawRect(out, vncInfo.img, mode, pf, workBuffer, r)
  299. END
  300. END SendRect;
  301. PROCEDURE DoUpdates;
  302. VAR nof, i : LONGINT;
  303. BEGIN {EXCLUSIVE} (* must be exclusive to avoid sending collisions *)
  304. updateQ.GetBuffer(nof, drawRectBuffer);
  305. IF BundleRectangles THEN
  306. out.Char(0X); (* message type *) out.Char(0X); (* padding *)
  307. out.Net16(nof); (* number of rectangles *)
  308. IF SendFBUpdatePacketEarly THEN out.Update END;
  309. FOR i := 0 TO nof - 1 DO SendRect(drawRectBuffer[i]); IF ~BigPackets THEN out.Update END END;
  310. IF BigPackets THEN out.Update END
  311. ELSE
  312. FOR i := 0 TO nof - 1 DO
  313. out.Char(0X); (* message type *) out.Char(0X); (* padding *)
  314. out.Net16(1); (* number of rectangles *)
  315. SendRect(drawRectBuffer[i]);
  316. IF ~BigPackets THEN out.Update END
  317. END;
  318. IF BigPackets THEN out.Update END
  319. END;
  320. END DoUpdates;
  321. PROCEDURE AddDirty*(r : Rectangle);
  322. BEGIN
  323. updateQ.Add(r)
  324. END AddDirty;
  325. (* 5.2.4 *)
  326. PROCEDURE FBUpdateRequest;
  327. VAR rect, r : Rectangle;
  328. BEGIN
  329. IF in.Get() # 1X THEN r := WMRectangles.MakeRect(0, 0, vncInfo.width, vncInfo.height); updateQ.Add(r) END;
  330. rect.l := in.Net16(); rect.t := in.Net16();
  331. rect.r := rect.l + in.Net16(); rect.b := rect.t + in.Net16();
  332. (* ignoring the rect for now *)
  333. updateQ.SetAllowed
  334. END FBUpdateRequest;
  335. (* 5.2.5 *)
  336. PROCEDURE KeyEvent;
  337. VAR down : BOOLEAN;
  338. flags, greyKey : SET;
  339. ucs, keysym : LONGINT;
  340. BEGIN
  341. down := in.Get() = 1X;
  342. IF down THEN flags := {} ELSE flags := {Inputs.Release} END;
  343. in.SkipBytes(2); (* skip padding *)
  344. keysym := in.Net32();
  345. IF down & (keysym < 80H) THEN ucs := keysym ELSE ucs := 0 END;
  346. (* flags *)
  347. greyKey := {};
  348. CASE keysym OF
  349. | Inputs.KsShiftL : greyKey := {Inputs.LeftShift}
  350. | Inputs.KsShiftR : greyKey := {Inputs.RightShift}
  351. | Inputs.KsControlL : greyKey := {Inputs.LeftCtrl}
  352. | Inputs.KsControlR : greyKey := {Inputs.RightCtrl}
  353. | Inputs.KsMetaL : greyKey := {Inputs.LeftMeta}
  354. | Inputs.KsMetaR : greyKey := {Inputs.RightMeta}
  355. | Inputs.KsAltL : greyKey := {Inputs.LeftAlt}
  356. | Inputs.KsAltR : greyKey := {Inputs.RightAlt}
  357. ELSE
  358. END;
  359. IF down THEN
  360. CASE keysym OF
  361. | Inputs.KsBackSpace : ucs := 7FH (* backspace *)
  362. | Inputs.KsTab : ucs := 09H (* tab *)
  363. | Inputs.KsReturn : ucs := 0DH (* return/enter *)
  364. | Inputs.KsEscape : ucs := 01BH (* escape *)
  365. | Inputs.KsInsert : ucs := 0A0H (* insert *)
  366. | Inputs.KsDelete : ucs := 0A1H (* delete *)
  367. | Inputs.KsHome : ucs := 0A8H (* home *)
  368. | Inputs.KsEnd : ucs := 0A9H (* end *)
  369. | Inputs.KsPageUp : ucs := 0A2H (* pgup *)
  370. | Inputs.KsPageDown : ucs := 0A3H (* pgdn *)
  371. | Inputs.KsLeft : ucs := 0C4H (* left *)
  372. | Inputs.KsUp : ucs := 0C1H (* up *)
  373. | Inputs.KsRight : ucs := 0C3H (* right *)
  374. | Inputs.KsDown : ucs := 0C2H (* down *)
  375. | Inputs.KsF1: ucs := 0A4H (* f1 *)
  376. | Inputs.KsF2: ucs := 0A5H (* f2 *)
  377. | Inputs.KsF3: ucs := 01BH (* f3 *)
  378. | Inputs.KsF4: ucs := 0A7H (* f4 *)
  379. | Inputs.KsF5: ucs := 0F5H (* f5 *)
  380. | Inputs.KsF6: ucs := 0F6H (* f6 *)
  381. | Inputs.KsF7: ucs := 0F7H (* f7 *)
  382. | Inputs.KsF8: ucs := 0F8H (* f8 *)
  383. | Inputs.KsF9: ucs := 0F9H (* f9 *)
  384. | Inputs.KsF10: ucs := 0FAH (* f10 *)
  385. | Inputs.KsF11: ucs := 0FBH (* f11 *)
  386. | Inputs.KsF12: ucs := 0FCH (* f12 *)
  387. ELSE
  388. END
  389. END;
  390. IF down THEN keyState := keyState + greyKey ELSE keyState := keyState - greyKey END;
  391. IF keyState * Inputs.Ctrl # {} THEN
  392. IF (ucs > ORD('A')) & (ucs < ORD('Z')) THEN keysym := ucs - ORD('A') + 1 END; (* Ctrl-A - Ctrl-Z *)
  393. IF (ucs > ORD('a')) & (ucs < ORD('z')) THEN keysym := ucs - ORD('a') + 1 END; (* Ctrl-a - Ctrl-z *)
  394. ucs := 0;
  395. END;
  396. flags := flags + keyState;
  397. IF TraceKeyEvent IN Trace THEN KernelLog.String("Keysym = "); KernelLog.Hex(keysym, -4); KernelLog.Ln END;
  398. IF vncInfo.kl # NIL THEN vncInfo.kl(ucs, flags, keysym) END
  399. END KeyEvent;
  400. (* 5.2.6 *)
  401. PROCEDURE PointerEvent;
  402. VAR buttons : LONGINT; keys : SET; x, y, dz : LONGINT;
  403. BEGIN
  404. buttons := ORD(in.Get()); x := in.Net16(); y := in.Net16();
  405. keys := {};
  406. IF buttons MOD 2 = 1 THEN INCL(keys, 0) END;
  407. IF buttons DIV 2 MOD 2 = 1 THEN INCL(keys, 1) END;
  408. IF buttons DIV 4 MOD 2 = 1 THEN INCL(keys, 2) END;
  409. IF buttons DIV 8 MOD 2 = 1 THEN dz := -1 END;
  410. IF buttons DIV 16 MOD 2 = 1 THEN dz := +1 END;
  411. IF vncInfo.ml # NIL THEN vncInfo.ml(x, y, dz, keys) END
  412. END PointerEvent;
  413. (* 5.2.7 *)
  414. PROCEDURE ClientCutText;
  415. VAR i, len : LONGINT;
  416. text : String;
  417. skip : CHAR;
  418. BEGIN
  419. in.SkipBytes(3); (* padding *)
  420. len := in.Net32();
  421. NEW(text, MIN(MaxCutSize, len + 1));
  422. FOR i := 0 TO len - 1 DO
  423. IF len < MaxCutSize - 1 THEN text[i] := in.Get() ELSE skip := in.Get() END;
  424. END;
  425. text[MIN(MaxCutSize - 1, len)] := 0X;
  426. IF vncInfo.cutl # NIL THEN vncInfo.cutl(text) END;
  427. END ClientCutText;
  428. (** send a text as clipboard content to the client *)
  429. PROCEDURE SendClipboard*(text : String);
  430. VAR len : LONGINT;
  431. BEGIN {EXCLUSIVE}
  432. out.Char(3X); (* message type *)
  433. out.Char(0X); out.Char(0X); out.Char(0X); (* padding *)
  434. len := Strings.Length(text^);
  435. out.Net32(len);
  436. out.String(text^);
  437. END SendClipboard;
  438. (** 5.4.2 CopyRect Returns FALSE, if client does not support CopyRect
  439. this does not wait for fbupdatereq, does not flush *)
  440. PROCEDURE CopyRect*(srcx, srcy : LONGINT; dst : Rectangle) : BOOLEAN;
  441. BEGIN {EXCLUSIVE}
  442. IF ~(EncCopyRect IN encodings) THEN RETURN FALSE END;
  443. out.Char(0X); (* message type *) out.Char(0X); (* padding *)
  444. out.Net16(1); (* number of rectangles *)
  445. out.Net16(dst.l); out.Net16(dst.t); out.Net16(dst.r - dst.l); out.Net16(dst.b - dst.t);
  446. out.Net32(1);
  447. out.Net16(srcx); out.Net16(srcy);
  448. RETURN TRUE
  449. END CopyRect;
  450. PROCEDURE Serve;
  451. VAR msgType : CHAR;
  452. BEGIN
  453. REPEAT
  454. msgType := in.Get();
  455. IF in.res = Streams.Ok THEN
  456. CASE msgType OF
  457. 0X : SetPixelFormat; IF TraceMsg IN Trace THEN traceStr := "SetPixelFormat" END
  458. |1X: FixupColorMapEntries; IF TraceMsg IN Trace THEN traceStr := "FixupColorMapEntries" END
  459. |2X: SetEncodings; IF TraceMsg IN Trace THEN traceStr := "Encoding" END
  460. |3X: FBUpdateRequest; IF TraceMsg IN Trace THEN traceStr := "FBUpdate" END
  461. |4X: KeyEvent; IF TraceMsg IN Trace THEN traceStr := "KeyEvent" END
  462. |5X: PointerEvent; IF TraceMsg IN Trace THEN traceStr := "PointerEvent" END
  463. |6X: ClientCutText; IF TraceMsg IN Trace THEN traceStr := "ClientCutText" END
  464. ELSE IF TraceMsg IN Trace THEN traceStr := "unknown" END
  465. END;
  466. IF TraceMsg IN Trace THEN KernelLog.String("VNC request: "); KernelLog.String(traceStr); KernelLog.Ln END
  467. END
  468. UNTIL in.res # Streams.Ok
  469. END Serve;
  470. BEGIN {ACTIVE}
  471. IF SendVersion() & Authenticate() & Setup() THEN
  472. Machine.AtomicInc(NnofEnteredServe);
  473. Serve;
  474. Machine.AtomicInc(NnofLeftServer);
  475. END;
  476. client.Close;
  477. updateQ.Close;
  478. s.Remove(SELF);
  479. END VNCAgent;
  480. (** Wait for new TCP connections, start a VNC agent as soon as needed *)
  481. Server* = OBJECT
  482. VAR res: WORD; service, client: TCP.Connection; root : Agent; agent : VNCAgent;
  483. vncInfo : VNCInfo;
  484. nofAgents : LONGINT;
  485. stopped : BOOLEAN;
  486. init : VNCService;
  487. PROCEDURE &Open*(port: LONGINT; vncInfo : VNCInfo; init : VNCService; VAR res: WORD);
  488. BEGIN
  489. stopped := FALSE; SELF.vncInfo := vncInfo; SELF.init := init;
  490. NEW(service); service.Open(port, IP.NilAdr, TCP.NilPort, res);
  491. IF res = Streams.Ok THEN NEW(root); root.next := NIL
  492. ELSE service := NIL (* stop active body *)
  493. END;
  494. nofAgents := 0
  495. END Open;
  496. PROCEDURE CloseAllOthers(this : Agent);
  497. VAR p : Agent;
  498. BEGIN {EXCLUSIVE}
  499. p := root.next;
  500. WHILE p # NIL DO IF p # this THEN p.client.Close() END; p := p.next END;
  501. END CloseAllOthers;
  502. PROCEDURE Remove(a: Agent);
  503. VAR p: Agent;
  504. BEGIN
  505. BEGIN {EXCLUSIVE}
  506. p := root;
  507. WHILE (p.next # NIL) & (p.next # a) DO p := p.next END;
  508. IF p.next = a THEN p.next := a.next END;
  509. DEC(nofAgents)
  510. END;
  511. IF vncInfo.ncal # NIL THEN vncInfo.ncal(nofAgents) END
  512. END Remove;
  513. PROCEDURE AddDirty*(r : Rectangle);
  514. VAR p: Agent;
  515. BEGIN {EXCLUSIVE}
  516. p := root.next;
  517. WHILE (p # NIL) DO p(VNCAgent).AddDirty(r); p := p.next END;
  518. END AddDirty;
  519. PROCEDURE SendClipboard*(t : String);
  520. VAR p: Agent;
  521. BEGIN {EXCLUSIVE}
  522. p := root.next;
  523. WHILE (p # NIL) DO p(VNCAgent).SendClipboard(t); p := p.next END;
  524. END SendClipboard;
  525. PROCEDURE Close*;
  526. VAR p : Agent;
  527. BEGIN {EXCLUSIVE}
  528. service.Close();
  529. p := root.next;
  530. WHILE p # NIL DO p.client.Close(); p := p.next END;
  531. AWAIT(root.next = NIL); (* wait for all agents to remove themselves *)
  532. AWAIT(stopped) (* wait for service to terminate *)
  533. END Close;
  534. BEGIN {ACTIVE}
  535. IF service # NIL THEN
  536. LOOP
  537. service.Accept(client, res);
  538. IF res # Streams.Ok THEN EXIT END;
  539. IF init # NIL THEN
  540. NEW(vncInfo);
  541. vncInfo.connection := client;
  542. init(vncInfo);
  543. vncInfo.width := vncInfo.img.width;
  544. vncInfo.height := vncInfo.img.height
  545. END;
  546. NEW(agent, SELF, client, vncInfo);
  547. vncInfo.agent := agent;
  548. BEGIN {EXCLUSIVE}
  549. INC(nofAgents);
  550. agent.next := root.next; root.next := agent;
  551. END;
  552. IF vncInfo.ncal # NIL THEN vncInfo.ncal(nofAgents) END
  553. END
  554. END;
  555. BEGIN {EXCLUSIVE} stopped := TRUE END
  556. END Server;
  557. VAR
  558. NnofAuthenticate-,
  559. NnofAuthNone-,
  560. NnofAuthVNC-, NnofAuthOk-, NnofAuthFailed-,
  561. NnofEnteredServe-, NnofLeftServer- : LONGINT;
  562. PROCEDURE SendPixel(out : Streams.Writer; pix : LONGINT; VAR pf : PixelFormat);
  563. BEGIN
  564. IF pf.depth = 8 THEN out.Char(CHR(pix))
  565. ELSIF pf.depth = 16 THEN
  566. IF pf.bigendian THEN out.Net16(pix) ELSE out.Char(CHR(pix MOD 100H)); out.Char(CHR(pix DIV 100H)) END
  567. ELSE
  568. IF pf.bigendian THEN out.Net32(pix) ELSE
  569. out.Char(CHR(pix MOD 100H)); out.Char(CHR(pix DIV 100H MOD 100H));
  570. out.Char(CHR(pix DIV 10000H MOD 100H)); out.Char(CHR(pix DIV 1000000H MOD 100H))
  571. END
  572. END
  573. END SendPixel;
  574. PROCEDURE SendRawRect(out : Streams.Writer; img : Raster.Image; VAR mode : Raster.Mode; VAR pf : PixelFormat; buf : WorkBuf; r : Rectangle);
  575. VAR i, j, rh, rw : LONGINT; cb, cg, cr : LONGINT;
  576. BEGIN
  577. rh := r.b - r.t; rw := r.r - r.l;
  578. (* KernelLog.String("w/h"); KernelLog.Int(rw, 5); KernelLog.Int(rh, 5);
  579. KernelLog.String("rect :"); KernelLog.Int(r.l, 5); KernelLog.Int(r.t, 5);KernelLog.Int(r.r, 5); KernelLog.Int(r.b, 5); KernelLog.Ln; *)
  580. IF pf.native16 THEN (* optimized 16 bit case *)
  581. FOR i := 0 TO rh - 1 DO
  582. Raster.GetPixels(img, r.l, r.t + i, rw, Raster.BGR565, buf^, 0, mode);
  583. out.Bytes(buf^, 0, 2*rw)
  584. END
  585. ELSE (* not so optimized generic case *)
  586. FOR i := 0 TO rh - 1 DO
  587. Raster.GetPixels(img, r.l, r.t + i, rw, Raster.BGR888, buf^, 0, mode);
  588. FOR j := 0 TO rw - 1 DO
  589. cb := LSH(ORD(buf[j * 3]), -pf.sb);
  590. cg := LSH(ORD(buf[j * 3 + 1]), -pf.sg);
  591. cr := LSH(ORD(buf[j * 3 + 2]), -pf.sr);
  592. SendPixel(out, LSH(cg, pf.gshift) + LSH(cb, pf.bshift) + LSH(cr, pf.rshift), pf)
  593. END
  594. END
  595. END
  596. END SendRawRect;
  597. PROCEDURE AnalyzeColors(VAR hextile : PFHextile; nofPixels : LONGINT; VAR bg, fg : LONGINT; VAR solid, mono : BOOLEAN);
  598. VAR i, n0, n1, c0, c1, c : LONGINT;
  599. BEGIN
  600. n0 := 0; n1 := 0; solid := TRUE; mono := TRUE;
  601. FOR i := 0 TO nofPixels - 1 DO
  602. c := hextile[i];
  603. IF n0 = 0 THEN c0 := c END;
  604. IF c = c0 THEN INC(n0)
  605. ELSE
  606. IF n1 = 0 THEN c1 := c; solid := FALSE END;
  607. IF c = c1 THEN INC(n1)
  608. ELSE mono := FALSE
  609. END
  610. END
  611. END;
  612. IF n0 > n1 THEN bg := c0; fg := c1 ELSE bg := c1; fg := c0 END
  613. END AnalyzeColors;
  614. PROCEDURE EncodeHextile(hextile : PFHextile; buf : WorkBuf; VAR pf : PixelFormat; w, h : LONGINT; bg, fg : LONGINT; mono : BOOLEAN;
  615. VAR nofRects : LONGINT) : LONGINT;
  616. VAR pos, x, y, c, tx, ty, bypp, i, j : LONGINT; eq : BOOLEAN;
  617. BEGIN
  618. pos := 0; nofRects := 0;
  619. IF pf.depth = 8 THEN bypp := 1
  620. ELSIF pf.depth = 16 THEN bypp := 2
  621. ELSE bypp := 4
  622. END;
  623. FOR y := 0 TO h - 1 DO
  624. FOR x := 0 TO w - 1 DO
  625. c := hextile[y * w + x];
  626. IF c # bg THEN
  627. tx := x + 1;
  628. (* in x direction *)
  629. WHILE (tx < w) & (hextile[y * w + tx] = c) DO INC(tx) END;
  630. ty := y + 1; eq := TRUE;
  631. WHILE (ty < h) & eq DO
  632. (* check a line *)
  633. j := x; WHILE (j < tx) & (hextile[ty * w + j] = c) DO INC(j) END;
  634. IF j < tx THEN eq := FALSE ELSE INC(ty) END;
  635. END;
  636. IF ~mono THEN
  637. (* send the pixel (move into procedure ?) *)
  638. IF pf.depth = 8 THEN buf[pos] := CHR(c); INC(pos)
  639. ELSIF pf.depth = 16 THEN
  640. IF pf.bigendian THEN buf[pos] := CHR(c DIV 256); INC(pos); buf[pos] := CHR(c MOD 256); INC(pos)
  641. ELSE buf[pos] := CHR(c MOD 256); INC(pos); buf[pos] := CHR(c DIV 256); INC(pos)
  642. END
  643. ELSE
  644. IF pf.bigendian THEN
  645. buf[pos] := CHR(c DIV 1000000H MOD 100H); INC(pos);
  646. buf[pos] := CHR(c DIV 10000H MOD 100H); INC(pos);
  647. buf[pos] := CHR(c DIV 100H MOD 100H); INC(pos);
  648. buf[pos] := CHR(c MOD 100H); INC(pos)
  649. ELSE
  650. buf[pos] := CHR(c MOD 100H); INC(pos);
  651. buf[pos] := CHR(c DIV 100H MOD 100H); INC(pos);
  652. buf[pos] := CHR(c DIV 10000H MOD 100H); INC(pos);
  653. buf[pos] := CHR(c DIV 1000000H MOD 100H); INC(pos)
  654. END
  655. END
  656. END;
  657. (* send rectangle coordinates *)
  658. buf[pos] := CHR(x * 16 + y); INC(pos);
  659. (* w, h *)
  660. buf[pos] := CHR((tx- x - 1) * 16 + (ty - y) - 1); INC(pos);
  661. INC(nofRects);
  662. (* clear the rectangle with bg col *)
  663. FOR j := y TO ty - 1 DO FOR i := x TO tx - 1 DO hextile[j * w + i] := bg END END;
  664. (* check if hextile is shorter than raw encoding *)
  665. IF pos >= w * h * bypp THEN RETURN 0 END
  666. END
  667. END
  668. END;
  669. RETURN pos
  670. END EncodeHextile;
  671. PROCEDURE SendHextile(out : Streams.Writer; img : Raster.Image; VAR mode : Raster.Mode; VAR pf : PixelFormat;
  672. buf : WorkBuf; VAR hextile : PFHextile; r : Rectangle);
  673. VAR x, y, w, h, ofs, i, cb, cg, cr, bg, fg : LONGINT;
  674. validbg, validfg, mono, solid : BOOLEAN;
  675. newbg, newfg, encBytes, nofRects, flags : LONGINT;
  676. hextileFlags : SET;
  677. BEGIN
  678. validbg := FALSE;
  679. y := r.t;
  680. WHILE y < r.b DO
  681. x := r.l;
  682. h := 16; IF r.b - y < 16 THEN h := r.b - y END; (* current hextile height *)
  683. WHILE x < r.r DO
  684. w := 16; IF r.r - x < 16 THEN w := r.r - x END; (* current hextile width *)
  685. (* copy the hexile pixels into workbuffer *)
  686. ofs := 0; FOR i := 0 TO h - 1 DO Raster.GetPixels(img, x, y + i, w, Raster.BGR888, buf^, ofs, mode); INC(ofs, w * 3) END;
  687. (* to pixelformat *)
  688. FOR i := 0 TO w * h - 1 DO
  689. cb := LSH(ORD(buf[i * 3]), -pf.sb);
  690. cg := LSH(ORD(buf[i * 3 + 1]), -pf.sg);
  691. cr := LSH(ORD(buf[i * 3 + 2]), -pf.sr);
  692. hextile[i] := LSH(cg, pf.gshift) + LSH(cb, pf.bshift) + LSH(cr, pf.rshift);
  693. END;
  694. AnalyzeColors(hextile, w * h, newbg, newfg, solid, mono);
  695. hextileFlags := {};
  696. IF ~validbg OR (newbg # bg) THEN validbg := TRUE; bg := newbg; INCL(hextileFlags, HexBGSpecified) END;
  697. IF ~solid THEN
  698. INCL(hextileFlags, HexAnySubrects);
  699. IF mono THEN IF ~validfg OR (newfg # fg) THEN validfg := TRUE; fg := newfg; INCL(hextileFlags, HexFGSpecified) END
  700. ELSE validfg := FALSE; INCL(hextileFlags, HexSubrectsColoured)
  701. END;
  702. encBytes := EncodeHextile(hextile, buf, pf, w, h, bg, fg, mono, nofRects);
  703. IF encBytes = 0 THEN (* hextile would need more bytes than raw *)
  704. validbg := FALSE; validfg := FALSE;
  705. hextileFlags := { HexRaw }
  706. END
  707. END;
  708. flags := 0; FOR i := 0 TO 31 DO IF i IN hextileFlags THEN INC(flags, i) END END;
  709. out.Char(CHR(flags));
  710. IF HexBGSpecified IN hextileFlags THEN SendPixel(out, bg, pf) END;
  711. IF HexFGSpecified IN hextileFlags THEN SendPixel(out, fg, pf) END;
  712. IF HexRaw IN hextileFlags THEN FOR i := 0 TO w * h - 1 DO SendPixel(out, hextile[i], pf); END
  713. ELSIF HexAnySubrects IN hextileFlags THEN
  714. out.Char(CHR(nofRects));
  715. out.Bytes(buf^, 0, encBytes)
  716. END;
  717. INC(x, 16)
  718. END;
  719. INC(y, 16)
  720. END
  721. END SendHextile;
  722. PROCEDURE OpenServer*(port : LONGINT; img : Raster.Image; name, password : ARRAY OF CHAR; ml : VNCMouseListener;
  723. kl : VNCKeyboardListener; cl : VNCClipboardListener; ncal : VNCNofClientsActiveListener) : Server;
  724. VAR server : Server;
  725. vncInfo : VNCInfo; res : WORD;
  726. BEGIN
  727. NEW(vncInfo);
  728. COPY(password, vncInfo.password);
  729. COPY(name, vncInfo.name);
  730. vncInfo.width := img.width;
  731. vncInfo.height := img.height;
  732. vncInfo.img := img;
  733. vncInfo.ml := ml; vncInfo.kl := kl; vncInfo.cutl := cl; vncInfo.ncal := ncal;
  734. NEW(server, port, vncInfo, NIL, res);
  735. IF res # 0 THEN server := NIL END;
  736. RETURN server;
  737. END OpenServer;
  738. PROCEDURE OpenService*(port : LONGINT; init : VNCService) : Server;
  739. VAR server : Server; res : WORD;
  740. BEGIN
  741. NEW(server, port, NIL, init, res);
  742. IF res # 0 THEN server := NIL END;
  743. RETURN server;
  744. END OpenService;
  745. END VNCServer.