vnc.js 29 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024
  1. Array.prototype.shift8 = function () {
  2. return this.shift();
  3. }
  4. Array.prototype.push8 = function (num) {
  5. this.push(num & 0xFF);
  6. }
  7. Array.prototype.shift16 = function () {
  8. return (this.shift() << 8) +
  9. (this.shift() );
  10. }
  11. Array.prototype.push16 = function (num) {
  12. this.push((num >> 8) & 0xFF,
  13. (num ) & 0xFF );
  14. }
  15. Array.prototype.shift32 = function () {
  16. return (this.shift() << 24) +
  17. (this.shift() << 16) +
  18. (this.shift() << 8) +
  19. (this.shift() );
  20. }
  21. Array.prototype.push32 = function (num) {
  22. this.push((num >> 24) & 0xFF,
  23. (num >> 16) & 0xFF,
  24. (num >> 8) & 0xFF,
  25. (num ) & 0xFF );
  26. }
  27. Array.prototype.shiftStr = function (len) {
  28. var arr = this.splice(0, len);
  29. return arr.map(function (num) {
  30. return String.fromCharCode(num); } ).join('');
  31. }
  32. Array.prototype.pushStr = function (str) {
  33. var n = str.length;
  34. for (var i=0; i < n; i++) {
  35. this.push(str.charCodeAt(i));
  36. }
  37. }
  38. Array.prototype.shiftBytes = function (len) {
  39. return this.splice(0, len);
  40. }
  41. /*
  42. * Frame buffer update state
  43. */
  44. FBU = {
  45. rects : 0,
  46. subrects : 0, // RRE and HEXTILE
  47. lines : 0, // RAW
  48. tiles : 0, // HEXTILE
  49. bytes : 0,
  50. x : 0,
  51. y : 0,
  52. width : 0,
  53. height : 0,
  54. encoding : 0,
  55. subencoding : -1,
  56. background: null};
  57. /*
  58. * Mouse state
  59. */
  60. Mouse = {
  61. buttonmask : 0,
  62. arr : []
  63. };
  64. /*
  65. * RFB namespace
  66. */
  67. RQ = []; // Receive Queue
  68. RQ_reorder = []; // Receive Queue re-order list
  69. RQ_seq_num = 0; // Expected sequence number
  70. SQ = ""; // Send Queue
  71. RFB = {
  72. ws : null, // Web Socket object
  73. sendID : null,
  74. use_seq : false,
  75. version : "RFB 003.003\n",
  76. state : 'disconnected',
  77. cuttext : 'none', // ServerCutText wait state
  78. ct_length : 0,
  79. clipboardFocus: false,
  80. shared : 1,
  81. check_rate : 217,
  82. req_rate : 1413,
  83. last_req : 0,
  84. host : '',
  85. port : 5900,
  86. password : '',
  87. fb_width : 0,
  88. fb_height : 0,
  89. fb_name : "",
  90. fb_Bpp : 4,
  91. rre_chunk : 100,
  92. /*
  93. * Server message handlers
  94. */
  95. /* RFB/VNC initialisation */
  96. init_msg: function () {
  97. console.log(">> init_msg");
  98. switch (RFB.state) {
  99. case 'ProtocolVersion' :
  100. if (RQ.length != 12) {
  101. updateStatus('failed', "Disconnected: invalid RFB protocol version received");
  102. return;
  103. }
  104. var server_version = RQ.shiftStr(12);
  105. console.log("Server ProtocolVersion: " + server_version.substr(0,11));
  106. RFB.send_string(RFB.version);
  107. RFB.updateState('Authentication', "Sent ProtocolVersion: " + RFB.version.substr(0,11));
  108. break;
  109. case 'Authentication' :
  110. if (RQ.length < 4) {
  111. RFB.updateState('reset', "Invalid auth frame");
  112. return;
  113. }
  114. var scheme = RQ.shift32();
  115. console.log("Auth scheme: " + scheme);
  116. switch (scheme) {
  117. case 0: // connection failed
  118. var strlen = RQ.shift32();
  119. var reason = RQ.shiftStr(strlen);
  120. RFB.updateState('failed', "Disconnected: auth failure: " + reason);
  121. return;
  122. case 1: // no authentication
  123. RFB.send_array([RFB.shared]); // ClientInitialisation
  124. RFB.updateState('ServerInitialisation');
  125. break;
  126. case 2: // VNC authentication
  127. var challenge = RQ.shiftBytes(16);
  128. console.log("Password: " + RFB.password);
  129. console.log("Challenge: " + challenge + "(" + challenge.length + ")");
  130. passwd = RFB.passwdTwiddle(RFB.password);
  131. response = des(passwd, challenge, 1);
  132. RFB.send_array(response);
  133. RFB.updateState('SecurityResult');
  134. break;
  135. default:
  136. RFB.updateState('failed', "Disconnected: unsupported auth scheme " + scheme);
  137. return;
  138. }
  139. break;
  140. case 'SecurityResult' :
  141. if (RQ.length != 4) {
  142. RFB.updateState('reset', "Invalid VNC auth response");
  143. return;
  144. }
  145. var resp = RQ.shift32();
  146. switch (resp) {
  147. case 0: // OK
  148. RFB.updateState('ServerInitialisation', "Authentication OK");
  149. break;
  150. case 1: // failed
  151. RFB.updateState('reset', "Authentication failed");
  152. return;
  153. case 2: // too-many
  154. RFB.updateState('failed', "Disconnected: too many auth attempts");
  155. return;
  156. }
  157. RFB.send_array([RFB.shared]); // ClientInitialisation
  158. break;
  159. case 'ServerInitialisation' :
  160. if (RQ.length < 24) {
  161. RFB.updateState('reset', "Invalid server initialisation");
  162. return;
  163. }
  164. /* Screen size */
  165. RFB.fb_width = RQ.shift16();
  166. RFB.fb_height = RQ.shift16();
  167. console.log("Screen size: " + RFB.fb_width + "x" + RFB.fb_height);
  168. /* PIXEL_FORMAT */
  169. var bpp = RQ.shift8();
  170. var depth = RQ.shift8();
  171. var big_endian = RQ.shift8();
  172. var true_color = RQ.shift8();
  173. console.log("bpp: " + bpp);
  174. console.log("depth: " + depth);
  175. console.log("big_endian: " + big_endian);
  176. console.log("true_color: " + true_color);
  177. /* Connection name/title */
  178. RQ.shiftStr(12);
  179. var name_length = RQ.shift32();
  180. RFB.fb_name = RQ.shiftStr(name_length);
  181. Canvas.init('vnc', RFB.fb_width, RFB.fb_height,
  182. RFB.keyDown, RFB.keyUp,
  183. RFB.mouseDown, RFB.mouseUp, RFB.mouseMove);
  184. var init = [];
  185. init = init.concat(RFB.pixelFormat());
  186. init = init.concat(RFB.encodings());
  187. init = init.concat(RFB.fbUpdateRequest(0));
  188. RFB.send_array(init);
  189. /* Start pushing/polling */
  190. RFB.checkEvents.delay(RFB.check_rate);
  191. RFB.updateState('normal', "Connected to: " + RFB.fb_name);
  192. break;
  193. }
  194. console.log("<< init_msg");
  195. },
  196. /* Normal RFB/VNC server messages */
  197. normal_msg: function () {
  198. //console.log(">> normal_msg");
  199. var ret = true;
  200. if (FBU.rects > 0) {
  201. var msg_type = 0;
  202. } else if (RFB.cuttext != 'none') {
  203. var msg_type = 3;
  204. } else {
  205. var msg_type = RQ.shift8();
  206. }
  207. switch (msg_type) {
  208. case 0: // FramebufferUpdate
  209. if (FBU.rects == 0) {
  210. if (RQ.length < 3) {
  211. RQ.unshift(msg_type);
  212. console.log(" waiting for FBU header bytes");
  213. return false;
  214. }
  215. RQ.shift8();
  216. FBU.rects = RQ.shift16();
  217. //console.log("FramebufferUpdate, rects:" + FBU.rects);
  218. FBU.bytes = 0;
  219. }
  220. while ((FBU.rects > 0) && (RQ.length >= FBU.bytes)) {
  221. if (FBU.bytes == 0) {
  222. if (RQ.length < 12) {
  223. console.log(" waiting for rect header bytes");
  224. return false;
  225. }
  226. /* New FramebufferUpdate */
  227. FBU.x = RQ.shift16();
  228. FBU.y = RQ.shift16();
  229. FBU.width = RQ.shift16();
  230. FBU.height = RQ.shift16();
  231. FBU.encoding = parseInt(RQ.shift32(), 10);
  232. // Debug:
  233. /*
  234. var msg = "FramebufferUpdate rects:" + FBU.rects + " encoding:" + FBU.encoding
  235. switch (FBU.encoding) {
  236. case 0: msg += "(RAW)"; break;
  237. case 1: msg += "(COPY-RECT)"; break;
  238. case 2: msg += "(RRE)"; break;
  239. case 5: msg += "(HEXTILE " + FBU.tiles + " tiles)"; break;
  240. default:
  241. RFB.updateState('failed', "Disconnected: unsupported encoding " + FBU.encoding);
  242. return false;
  243. }
  244. msg += ", RQ.length: " + RQ.length
  245. console.log(msg);
  246. */
  247. }
  248. switch (FBU.encoding) {
  249. case 0: ret = RFB.display_raw(); break; // Raw
  250. case 1: ret = RFB.display_copy_rect(); break; // Copy-Rect
  251. case 2: ret = RFB.display_rre(); break; // RRE
  252. case 5: ret = RFB.display_hextile(); break; // hextile
  253. }
  254. if (RFB.state != "normal") return true;
  255. }
  256. break;
  257. case 1: // SetColourMapEntries
  258. console.log("SetColourMapEntries (unsupported)");
  259. RQ.shift8(); // Padding
  260. RQ.shift16(); // First colour
  261. var num_colours = RQ.shift16();
  262. RQ.shiftBytes(num_colours * 6);
  263. break;
  264. case 2: // Bell
  265. console.log("Bell (unsupported)");
  266. break;
  267. case 3: // ServerCutText
  268. console.log("ServerCutText");
  269. console.log("RQ:" + RQ.slice(0,20));
  270. if (RFB.cuttext == 'none') {
  271. RFB.cuttext = 'header';
  272. }
  273. if (RFB.cuttext == 'header') {
  274. if (RQ.length < 7) {
  275. console.log("waiting for ServerCutText header");
  276. return false;
  277. }
  278. RQ.shiftBytes(3); // Padding
  279. RFB.ct_length = RQ.shift32();
  280. }
  281. RFB.cuttext = 'bytes';
  282. if (RQ.length < RFB.ct_length) {
  283. console.log("waiting for ServerCutText bytes");
  284. return false;
  285. }
  286. RFB.clipboardCopyTo(RQ.shiftStr(RFB.ct_length));
  287. RFB.cuttext = 'none';
  288. break;
  289. default:
  290. RFB.updateState('failed', "Disconnected: illegal server message type " + msg_type);
  291. console.log("RQ.slice(0,30):" + RQ.slice(0,30));
  292. break;
  293. }
  294. //console.log("<< normal_msg");
  295. return ret;
  296. },
  297. /*
  298. * FramebufferUpdate encodings
  299. */
  300. display_raw: function () {
  301. //console.log(">> display_raw");
  302. if (FBU.lines == 0) {
  303. FBU.lines = FBU.height;
  304. }
  305. FBU.bytes = FBU.width * RFB.fb_Bpp; // At least a line
  306. if (RQ.length < FBU.bytes) {
  307. //console.log(" waiting for " + (FBU.bytes - RQ.length) + " RAW bytes");
  308. return;
  309. }
  310. var cur_y = FBU.y + (FBU.height - FBU.lines);
  311. var cur_height = Math.min(FBU.lines, Math.floor(RQ.length/(FBU.width * RFB.fb_Bpp)));
  312. Canvas.rgbxImage(FBU.x, cur_y, FBU.width, cur_height, RQ);
  313. RQ.shiftBytes(FBU.width * cur_height * RFB.fb_Bpp);
  314. FBU.lines -= cur_height;
  315. if (FBU.lines > 0) {
  316. FBU.bytes = FBU.width * RFB.fb_Bpp; // At least another line
  317. } else {
  318. FBU.rects --;
  319. FBU.bytes = 0;
  320. }
  321. },
  322. display_copy_rect: function () {
  323. //console.log(">> display_copy_rect");
  324. if (RQ.length < 4) {
  325. //console.log(" waiting for " + (FBU.bytes - RQ.length) + " COPY-RECT bytes");
  326. return;
  327. }
  328. var old_x = RQ.shift16();
  329. var old_y = RQ.shift16();
  330. Canvas.copyImage(old_x, old_y, FBU.x, FBU.y, FBU.width, FBU.height);
  331. FBU.rects --;
  332. FBU.bytes = 0;
  333. },
  334. display_rre: function () {
  335. //console.log(">> display_rre (" + RQ.length + " bytes)");
  336. if (FBU.subrects == 0) {
  337. ;
  338. if (RQ.length < 4 + RFB.fb_Bpp) {
  339. //console.log(" waiting for " + (4 + RFB.fb_Bpp - RQ.length) + " RRE bytes");
  340. return;
  341. }
  342. FBU.subrects = RQ.shift32();
  343. var color = RQ.shiftBytes(RFB.fb_Bpp); // Background
  344. Canvas.fillRect(FBU.x, FBU.y, FBU.width, FBU.height, color);
  345. }
  346. while ((FBU.subrects > 0) && (RQ.length >= (RFB.fb_Bpp + 8))) {
  347. var color = RQ.shiftBytes(RFB.fb_Bpp);
  348. var x = RQ.shift16();
  349. var y = RQ.shift16();
  350. var width = RQ.shift16();
  351. var height = RQ.shift16();
  352. Canvas.fillRect(FBU.x + x, FBU.y + y, width, height, color);
  353. FBU.subrects --;
  354. }
  355. //console.log(" display_rre: rects: " + FBU.rects + ", FBU.subrects: " + FBU.subrects);
  356. if (FBU.subrects > 0) {
  357. var chunk = Math.min(RFB.rre_chunk, FBU.subrects);
  358. FBU.bytes = (RFB.fb_Bpp + 8) * chunk;
  359. } else {
  360. FBU.rects --;
  361. FBU.bytes = 0;
  362. }
  363. //console.log("<< display_rre, FBU.bytes: " + FBU.bytes);
  364. },
  365. display_hextile: function() {
  366. //console.log(">> display_hextile");
  367. var subencoding, subrects, cur_tile, tile_x, x, w, tile_y, y, h;
  368. if (FBU.tiles == 0) {
  369. FBU.tiles_x = Math.ceil(FBU.width/16);
  370. FBU.tiles_y = Math.ceil(FBU.height/16);
  371. FBU.total_tiles = FBU.tiles_x * FBU.tiles_y;
  372. FBU.tiles = FBU.total_tiles;
  373. }
  374. /* FBU.bytes comes in as 1, RQ.length at least 1 */
  375. while (FBU.tiles > 0) {
  376. FBU.bytes = 1;
  377. if (RQ.length < FBU.bytes) {
  378. console.log(" waiting for HEXTILE subencoding byte");
  379. return;
  380. }
  381. subencoding = RQ[0]; // Peek
  382. if (subencoding > 30) { // Raw
  383. RFB.updateState('failed', "Disconnected: illegal hextile subencoding " + subencoding);
  384. console.log("RQ.slice(0,30):" + RQ.slice(0,30));
  385. return;
  386. }
  387. subrects = 0;
  388. cur_tile = FBU.total_tiles - FBU.tiles;
  389. tile_x = cur_tile % FBU.tiles_x;
  390. tile_y = Math.floor(cur_tile / FBU.tiles_x);
  391. x = FBU.x + tile_x * 16;
  392. y = FBU.y + tile_y * 16;
  393. w = Math.min(16, (FBU.x + FBU.width) - x)
  394. h = Math.min(16, (FBU.y + FBU.height) - y)
  395. /* Figure out how much we are expecting */
  396. if (subencoding & 0x01) { // Raw
  397. //console.log(" Raw subencoding");
  398. FBU.bytes += w * h * RFB.fb_Bpp;
  399. } else {
  400. if (subencoding & 0x02) { // Background
  401. FBU.bytes += RFB.fb_Bpp;
  402. }
  403. if (subencoding & 0x04) { // Foreground
  404. FBU.bytes += RFB.fb_Bpp;
  405. }
  406. if (subencoding & 0x08) { // AnySubrects
  407. FBU.bytes++; // Since we aren't shifting it off
  408. if (RQ.length < FBU.bytes) {
  409. /* Wait for subrects byte */
  410. console.log(" waiting for hextile subrects header byte");
  411. return;
  412. }
  413. subrects = RQ[FBU.bytes-1]; // Peek
  414. if (subencoding & 0x10) { // SubrectsColoured
  415. FBU.bytes += subrects * (RFB.fb_Bpp + 2);
  416. } else {
  417. FBU.bytes += subrects * 2;
  418. }
  419. }
  420. }
  421. //console.log(" tile:" + cur_tile + "/" + (FBU.total_tiles - 1) + ", subencoding:" + subencoding + "(last: " + FBU.lastsubencoding + "), subrects:" + subrects + ", tile:" + tile_x + "," + tile_y + " [" + x + "," + y + "]@" + w + "x" + h + ", d.length:" + RQ.length + ", bytes:" + FBU.bytes + " last:" + RQ.slice(FBU.bytes-10, FBU.bytes) + " next:" + RQ.slice(FBU.bytes-1, FBU.bytes+10));
  422. if (RQ.length < FBU.bytes) {
  423. //console.log(" waiting for " + (FBU.bytes - RQ.length) + " hextile bytes");
  424. return;
  425. }
  426. /* We know the encoding and have a whole tile */
  427. FBU.subencoding = RQ.shift8();
  428. FBU.bytes--;
  429. if (FBU.subencoding == 0) {
  430. if (FBU.lastsubencoding & 0x01) {
  431. /* Weird: ignore blanks after RAW */
  432. console.log(" Ignoring blank after RAW");
  433. continue;
  434. }
  435. Canvas.fillRect(x, y, w, h, FBU.background);
  436. } else if (FBU.subencoding & 0x01) { // Raw
  437. Canvas.rgbxImage(x, y, w, h, RQ);
  438. } else {
  439. var idx = 0;
  440. if (FBU.subencoding & 0x02) { // Background
  441. FBU.background = RQ.slice(idx, idx + RFB.fb_Bpp);
  442. idx += RFB.fb_Bpp;
  443. }
  444. if (FBU.subencoding & 0x04) { // Foreground
  445. FBU.foreground = RQ.slice(idx, idx + RFB.fb_Bpp);
  446. idx += RFB.fb_Bpp;
  447. }
  448. Canvas.fillRect(x, y, w, h, FBU.background);
  449. if (FBU.subencoding & 0x08) { // AnySubrects
  450. subrects = RQ[idx];
  451. idx++;
  452. var color, xy, sx, sy, wh, sw, sh;
  453. for (var i = 0; i < subrects; i ++) {
  454. if (FBU.subencoding & 0x10) { // SubrectsColoured
  455. color = RQ.slice(idx, idx + RFB.fb_Bpp);
  456. idx += RFB.fb_Bpp;
  457. } else {
  458. color = FBU.foreground;
  459. }
  460. xy = RQ[idx];
  461. idx++;
  462. sx = x + (xy >> 4);
  463. sy = y + (xy & 0x0f);
  464. wh = RQ[idx];
  465. idx++;
  466. sw = (wh >> 4) + 1;
  467. sh = (wh & 0x0f) + 1;
  468. Canvas.fillRect(sx, sy, sw, sh, color);
  469. }
  470. }
  471. }
  472. RQ.shiftBytes(FBU.bytes);
  473. FBU.lastsubencoding = FBU.subencoding;
  474. FBU.bytes = 0;
  475. FBU.tiles --;
  476. }
  477. if (FBU.tiles == 0) {
  478. FBU.rects --;
  479. }
  480. //console.log("<< display_hextile");
  481. },
  482. /*
  483. * Client message routines
  484. */
  485. pixelFormat: function () {
  486. console.log(">> setPixelFormat");
  487. var arr;
  488. arr = [0]; // msg-type
  489. arr.push8(0); // padding
  490. arr.push8(0); // padding
  491. arr.push8(0); // padding
  492. arr.push8(RFB.fb_Bpp * 8); // bits-per-pixel
  493. arr.push8(24); // depth
  494. arr.push8(0); // little-endian
  495. arr.push8(1); // true-color
  496. arr.push16(255); // red-max
  497. arr.push16(255); // green-max
  498. arr.push16(255); // blue-max
  499. arr.push8(0); // red-shift
  500. arr.push8(8); // green-shift
  501. arr.push8(16); // blue-shift
  502. arr.push8(0); // padding
  503. arr.push8(0); // padding
  504. arr.push8(0); // padding
  505. console.log("<< setPixelFormat");
  506. return arr;
  507. },
  508. fixColourMapEntries: function () {
  509. },
  510. encodings: function () {
  511. console.log(">> setEncodings");
  512. var arr;
  513. arr = [2]; // msg-type
  514. arr.push8(0); // padding
  515. //arr.push16(3); // encoding count
  516. arr.push16(4); // encoding count
  517. arr.push32(5); // hextile encoding
  518. arr.push32(2); // RRE encoding
  519. arr.push32(1); // copy-rect encoding
  520. arr.push32(0); // raw encoding
  521. console.log("<< setEncodings");
  522. return arr;
  523. },
  524. fbUpdateRequest: function (incremental, x, y, xw, yw) {
  525. //console.log(">> fbUpdateRequest");
  526. if (!x) x = 0;
  527. if (!y) y = 0;
  528. if (!xw) xw = RFB.fb_width;
  529. if (!yw) yw = RFB.fb_height;
  530. var arr;
  531. arr = [3]; // msg-type
  532. arr.push8(incremental);
  533. arr.push16(x);
  534. arr.push16(y);
  535. arr.push16(xw);
  536. arr.push16(yw);
  537. //console.log("<< fbUpdateRequest");
  538. return arr;
  539. },
  540. keyEvent: function (keysym, down) {
  541. //console.log(">> keyEvent, keysym: " + keysym + ", down: " + down);
  542. var arr;
  543. arr = [4]; // msg-type
  544. arr.push8(down);
  545. arr.push16(0);
  546. arr.push32(keysym);
  547. //console.log("<< keyEvent");
  548. return arr;
  549. },
  550. pointerEvent: function (x, y) {
  551. //console.log(">> pointerEvent, x,y: " + x + "," + y + " , mask: " + Mouse.buttonMask);
  552. var arr;
  553. arr = [5]; // msg-type
  554. arr.push8(Mouse.buttonMask);
  555. arr.push16(x);
  556. arr.push16(y);
  557. //console.log("<< pointerEvent");
  558. return arr;
  559. },
  560. clientCutText: function (text) {
  561. console.log(">> clientCutText");
  562. var arr;
  563. arr = [6]; // msg-type
  564. arr.push8(0); // padding
  565. arr.push8(0); // padding
  566. arr.push8(0); // padding
  567. arr.push32(text.length);
  568. arr.pushStr(text);
  569. console.log("<< clientCutText");
  570. return arr;
  571. },
  572. /*
  573. * Utility routines
  574. */
  575. recv_message: function(e) {
  576. //console.log(">> recv_message");
  577. RQ = RQ.concat(Base64.decode(e.data, 0));
  578. RFB.handle_message();
  579. //console.log("<< recv_message");
  580. },
  581. recv_message_reorder: function(e) {
  582. //console.log(">> recv_message_reorder");
  583. var offset = e.data.indexOf(":") + 1;
  584. var seq_num = parseInt(e.data.substr(0, offset-1));
  585. if (RQ_seq_num == seq_num) {
  586. RQ = RQ.concat(Base64.decode(e.data, offset));
  587. RQ_seq_num++;
  588. } else {
  589. console.warn("sequence number mismatch RQ_seq_num:" + RQ_seq_num + ", seq_num:" + seq_num);
  590. if (RQ_reorder.length > 20) {
  591. RFB.updateState('failed', "Re-order queue too long");
  592. } else {
  593. RQ_reorder = RQ_reorder.concat(e.data.substr(0));
  594. var i = 0;
  595. while (i < RQ_reorder.length) {
  596. var offset = RQ_reorder[i].indexOf(":") + 1;
  597. var seq_num = parseInt(RQ_reorder[i].substr(0, offset-1));
  598. console.log("Searching reorder list item " + i + ", seq_num " + seq_num);
  599. if (seq_num == RQ_seq_num) {
  600. /* Remove it from reorder queue, decode it and
  601. * add it to the receive queue */
  602. console.log("Found re-ordered packet seq_num " + seq_num);
  603. RQ = RQ.concat(Base64.decode(RQ_reorder.splice(i, 1)[0], offset));
  604. RQ_seq_num++;
  605. i = 0; // Start search again for next one
  606. } else {
  607. i++;
  608. }
  609. }
  610. }
  611. }
  612. RFB.handle_message();
  613. //console.log("<< recv_message_reorder");
  614. },
  615. handle_message: function () {
  616. switch (RFB.state) {
  617. case 'disconnected':
  618. console.error("Got data while disconnected");
  619. break;
  620. case 'reset':
  621. /* close and reset connection */
  622. RFB.disconnect();
  623. RFB.init_ws();
  624. break;
  625. case 'failed':
  626. console.log("Giving up!");
  627. RFB.disconnect();
  628. break;
  629. case 'normal':
  630. RFB.normal_msg();
  631. /*
  632. while (RQ.length > 0) {
  633. if (RFB.normal_msg() && RFB.state == 'normal') {
  634. console.log("More to process");
  635. } else {
  636. break;
  637. }
  638. }
  639. */
  640. break;
  641. default:
  642. RFB.init_msg();
  643. break;
  644. }
  645. },
  646. send_string: function (str) {
  647. //console.log(">> send_string: " + str);
  648. RFB.send_array(str.split('').map(
  649. function (chr) { return chr.charCodeAt(0) } ) );
  650. },
  651. send_array: function (arr) {
  652. //console.log(">> send_array: " + arr);
  653. //console.log(">> send_array: " + Base64.encode(arr));
  654. SQ = SQ + Base64.encode(arr);
  655. if (RFB.ws.bufferedAmount == 0) {
  656. RFB.ws.send(SQ);
  657. SQ = ""
  658. } else {
  659. console.log("Delaying send");
  660. }
  661. },
  662. /* Mirror bits of each character and return as array */
  663. passwdTwiddle: function (passwd) {
  664. var arr;
  665. arr = [];
  666. for (var i=0; i< passwd.length; i++) {
  667. var c = passwd.charCodeAt(i);
  668. arr.push( ((c & 0x80) >> 7) +
  669. ((c & 0x40) >> 5) +
  670. ((c & 0x20) >> 3) +
  671. ((c & 0x10) >> 1) +
  672. ((c & 0x08) << 1) +
  673. ((c & 0x04) << 3) +
  674. ((c & 0x02) << 5) +
  675. ((c & 0x01) << 7) );
  676. }
  677. return arr;
  678. },
  679. flushClient: function () {
  680. var arr = [];
  681. if (Mouse.arr.length > 0) {
  682. //RFB.send_array(Mouse.arr.concat(RFB.fbUpdateRequest(1)));
  683. RFB.send_array(Mouse.arr)
  684. setTimeout(function() {
  685. RFB.send_array(RFB.fbUpdateRequest(1));
  686. }, 50);
  687. Mouse.arr = [];
  688. return true;
  689. } else {
  690. return false;
  691. }
  692. },
  693. checkEvents: function () {
  694. if (RFB.state == 'normal') {
  695. if (! RFB.flushClient()) {
  696. var now = new Date().getTime();
  697. if (now > RFB.last_req + RFB.req_rate) {
  698. RFB.last_req = now;
  699. RFB.send_array(RFB.fbUpdateRequest(1));
  700. }
  701. }
  702. }
  703. RFB.checkEvents.delay(RFB.check_rate);
  704. },
  705. _keyX: function (e, down) {
  706. if (RFB.clipboardFocus) {
  707. return true;
  708. }
  709. e.stop();
  710. var arr = RFB.keyEvent(Canvas.getKeysym(e), down);
  711. arr = arr.concat(RFB.fbUpdateRequest(1));
  712. RFB.send_array(arr);
  713. },
  714. keyDown: function (e) {
  715. //console.log(">> keyDown: " + Canvas.getKeysym(e));
  716. RFB._keyX(e, 1);
  717. },
  718. keyUp: function (e) {
  719. //console.log(">> keyUp: " + Canvas.getKeysym(e));
  720. RFB._keyX(e, 0);
  721. },
  722. mouseDown: function(e) {
  723. var evt = e.event || window.event;
  724. var x, y;
  725. x = (evt.clientX - Canvas.c_x);
  726. y = (evt.clientY - Canvas.c_y);
  727. //console.log('>> mouseDown ' + evt.which + '/' + evt.button + " " + x + "," + y);
  728. Mouse.buttonMask |= 1 << evt.button;
  729. Mouse.arr = Mouse.arr.concat( RFB.pointerEvent(x, y) );
  730. RFB.flushClient();
  731. },
  732. mouseUp: function(e) {
  733. var evt = e.event || window.event;
  734. var x, y;
  735. x = (evt.clientX - Canvas.c_x);
  736. y = (evt.clientY - Canvas.c_y);
  737. //console.log('>> mouseUp ' + evt.which + '/' + evt.button + " " + x + "," + y);
  738. Mouse.buttonMask ^= 1 << evt.button;
  739. Mouse.arr = Mouse.arr.concat( RFB.pointerEvent(x, y) );
  740. RFB.flushClient();
  741. },
  742. mouseMove: function(e) {
  743. var evt = e.event || window.event;
  744. var x, y;
  745. x = (evt.clientX - Canvas.c_x);
  746. y = (evt.clientY - Canvas.c_y);
  747. //console.log('>> mouseMove ' + x + "," + y);
  748. Mouse.arr = Mouse.arr.concat( RFB.pointerEvent(x, y) );
  749. },
  750. clipboardCopyTo: function (text) {
  751. console.log(">> clipboardCopyTo: " + text.substr(0,40) + "...");
  752. $('clipboard').value = text;
  753. console.log("<< clipboardCopyTo");
  754. },
  755. clipboardPasteFrom: function () {
  756. if (RFB.state != "normal") return;
  757. var text = $('clipboard').value;
  758. console.log(">> clipboardPasteFrom: " + text.substr(0,40) + "...");
  759. RFB.send_array(RFB.clientCutText(text));
  760. console.log("<< clipboardPasteFrom");
  761. },
  762. clipboardClear: function () {
  763. $('clipboard').value = '';
  764. RFB.clipboardPasteFrom();
  765. },
  766. updateState: function(state, statusMsg) {
  767. var s = $('status');
  768. var c = $('connectButton');
  769. var func = function(msg) { console.log(msg) };
  770. switch (state) {
  771. case 'failed':
  772. func = function(msg) { console.error(msg) };
  773. c.disabled = true;
  774. s.style.fontColor = "#880000";
  775. break;
  776. case 'normal':
  777. c.value = "Disconnect";
  778. c.onclick = RFB.disconnect;
  779. c.disabled = false;
  780. s.style.fontColor = "#000000";
  781. break;
  782. case 'disconnected':
  783. c.value = "Connect";
  784. c.onclick = RFB.connect;
  785. c.disabled = false;
  786. s.style.fontColor = "#000000";
  787. break;
  788. default:
  789. func = function(msg) { console.warn(msg) };
  790. c.disabled = true;
  791. s.style.fontColor = "#444400";
  792. break;
  793. }
  794. RFB.state = state;
  795. var cmsg = typeof(statusMsg) != 'undefined' ? (" Msg: " + statusMsg) : "";
  796. func("New state '" + state + "'." + cmsg);
  797. if (typeof(statusMsg) != 'undefined') {
  798. s.innerHTML = statusMsg;
  799. }
  800. },
  801. /*
  802. * Setup routines
  803. */
  804. init_ws: function () {
  805. console.log(">> init_ws");
  806. var scheme = "ws://";
  807. if ($('encrypt').checked) {
  808. scheme = "wss://";
  809. }
  810. var uri = scheme + RFB.host + ":" + RFB.port + "/?b64encode";
  811. if (RFB.use_seq) {
  812. uri += "&seq_num";
  813. }
  814. console.log("connecting to " + uri);
  815. RFB.ws = new WebSocket(uri);
  816. if (RFB.use_seq) {
  817. RFB.ws.onmessage = RFB.recv_message_reorder;
  818. } else {
  819. RFB.ws.onmessage = RFB.recv_message;
  820. }
  821. RFB.ws.onopen = function(e) {
  822. console.log(">> WebSocket.onopen");
  823. RFB.updateState('ProtocolVersion', "Starting VNC handshake");
  824. RFB.sendID = setInterval(function() {
  825. /*
  826. * Send updates either at a rate of one update every 50ms,
  827. * or whatever slower rate the network can handle
  828. */
  829. if (RFB.ws.bufferedAmount == 0) {
  830. if (SQ) {
  831. RFB.ws.send(SQ);
  832. SQ = "";
  833. }
  834. } else {
  835. console.log("Delaying send");
  836. }
  837. }, 50);
  838. console.log("<< WebSocket.onopen");
  839. };
  840. RFB.ws.onclose = function(e) {
  841. console.log(">> WebSocket.onclose");
  842. clearInterval(RFB.sendID);
  843. if (RFB.state != 'disconnected') {
  844. if (RFB.state == 'failed') {
  845. RFB.updateState('disconnected');
  846. } else {
  847. RFB.updateState('disconnected', 'VNC disconnected');
  848. }
  849. }
  850. console.log("<< WebSocket.onclose");
  851. };
  852. RFB.ws.onerror = function(e) {
  853. console.error(">> WebSocket.onerror");
  854. console.error(" " + e);
  855. console.error("<< WebSocket.onerror");
  856. };
  857. console.log("<< init_ws");
  858. },
  859. init_vars: function () {
  860. /* Reset state */
  861. RFB.cuttext = 'none';
  862. RFB.ct_length = 0;
  863. RQ = [];
  864. RQ_seq_num = 0;
  865. SQ = "";
  866. FBU.rects = 0;
  867. FBU.subrects = 0; // RRE and HEXTILE
  868. FBU.lines = 0, // RAW
  869. FBU.tiles = 0, // HEXTILE
  870. Mouse.buttonmask = 0;
  871. Mouse.arr = [];
  872. },
  873. connect: function () {
  874. console.log(">> connect");
  875. RFB.host = $('host').value;
  876. RFB.port = $('port').value;
  877. RFB.password = $('password').value;
  878. if ((!RFB.host) || (!RFB.port)) {
  879. console.log("must set host and port");
  880. return;
  881. }
  882. RFB.init_vars();
  883. if ((RFB.ws) && (RFB.ws.readyState == WebSocket.OPEN)) {
  884. RFB.ws.close();
  885. }
  886. RFB.init_ws();
  887. RFB.updateState('ProtocolVersion');
  888. console.log("<< connect");
  889. },
  890. disconnect: function () {
  891. console.log(">> disconnect");
  892. if ((RFB.ws) && (RFB.ws.readyState == WebSocket.OPEN)) {
  893. RFB.updateState('closed');
  894. RFB.ws.close();
  895. }
  896. if (Canvas.ctx) {
  897. Canvas.stop();
  898. if (! /__debug__$/i.test(document.location.href)) {
  899. Canvas.clear();
  900. }
  901. }
  902. if (RFB.state == 'failed') {
  903. RFB.updateState('disconnected');
  904. } else {
  905. RFB.updateState('disconnected', 'Disconnected');
  906. }
  907. console.log("<< disconnect");
  908. }
  909. }; /* End of RFB */