rfb.js 58 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854
  1. /*
  2. * noVNC: HTML5 VNC client
  3. * Copyright (C) 2012 Joel Martin
  4. * Licensed under MPL 2.0 (see LICENSE.txt)
  5. *
  6. * See README.md for usage and integration instructions.
  7. *
  8. * TIGHT decoder portion:
  9. * (c) 2012 Michael Tinglof, Joe Balaz, Les Piech (Mercuri.ca)
  10. */
  11. /*jslint white: false, browser: true, bitwise: false, plusplus: false */
  12. /*global window, Util, Display, Keyboard, Mouse, Websock, Websock_native, Base64, DES */
  13. function RFB(defaults) {
  14. "use strict";
  15. var that = {}, // Public API methods
  16. conf = {}, // Configuration attributes
  17. // Pre-declare private functions used before definitions (jslint)
  18. init_vars, updateState, fail, handle_message,
  19. init_msg, normal_msg, framebufferUpdate, print_stats,
  20. pixelFormat, clientEncodings, fbUpdateRequest, fbUpdateRequests,
  21. keyEvent, pointerEvent, clientCutText,
  22. getTightCLength, extract_data_uri,
  23. keyPress, mouseButton, mouseMove,
  24. checkEvents, // Overridable for testing
  25. //
  26. // Private RFB namespace variables
  27. //
  28. rfb_host = '',
  29. rfb_port = 5900,
  30. rfb_password = '',
  31. rfb_path = '',
  32. rfb_state = 'disconnected',
  33. rfb_version = 0,
  34. rfb_max_version= 3.8,
  35. rfb_auth_scheme= '',
  36. // In preference order
  37. encodings = [
  38. ['COPYRECT', 0x01 ],
  39. ['TIGHT', 0x07 ],
  40. ['TIGHT_PNG', -260 ],
  41. ['HEXTILE', 0x05 ],
  42. ['RRE', 0x02 ],
  43. ['RAW', 0x00 ],
  44. ['DesktopSize', -223 ],
  45. ['Cursor', -239 ],
  46. // Psuedo-encoding settings
  47. //['JPEG_quality_lo', -32 ],
  48. ['JPEG_quality_med', -26 ],
  49. //['JPEG_quality_hi', -23 ],
  50. //['compress_lo', -255 ],
  51. ['compress_hi', -247 ],
  52. ['last_rect', -224 ]
  53. ],
  54. encHandlers = {},
  55. encNames = {},
  56. encStats = {}, // [rectCnt, rectCntTot]
  57. ws = null, // Websock object
  58. display = null, // Display object
  59. keyboard = null, // Keyboard input handler object
  60. mouse = null, // Mouse input handler object
  61. sendTimer = null, // Send Queue check timer
  62. connTimer = null, // connection timer
  63. disconnTimer = null, // disconnection timer
  64. msgTimer = null, // queued handle_message timer
  65. // Frame buffer update state
  66. FBU = {
  67. rects : 0,
  68. subrects : 0, // RRE
  69. lines : 0, // RAW
  70. tiles : 0, // HEXTILE
  71. bytes : 0,
  72. x : 0,
  73. y : 0,
  74. width : 0,
  75. height : 0,
  76. encoding : 0,
  77. subencoding : -1,
  78. background : null,
  79. zlibs : [] // TIGHT zlib streams
  80. },
  81. fb_Bpp = 4,
  82. fb_depth = 3,
  83. fb_width = 0,
  84. fb_height = 0,
  85. fb_name = "",
  86. last_req_time = 0,
  87. rre_chunk_sz = 100,
  88. timing = {
  89. last_fbu : 0,
  90. fbu_total : 0,
  91. fbu_total_cnt : 0,
  92. full_fbu_total : 0,
  93. full_fbu_cnt : 0,
  94. fbu_rt_start : 0,
  95. fbu_rt_total : 0,
  96. fbu_rt_cnt : 0,
  97. pixels : 0
  98. },
  99. test_mode = false,
  100. def_con_timeout = Websock_native ? 2 : 5,
  101. /* Mouse state */
  102. mouse_buttonMask = 0,
  103. mouse_arr = [],
  104. viewportDragging = false,
  105. viewportDragPos = {};
  106. // Configuration attributes
  107. Util.conf_defaults(conf, that, defaults, [
  108. ['target', 'wo', 'dom', null, 'VNC display rendering Canvas object'],
  109. ['focusContainer', 'wo', 'dom', document, 'DOM element that captures keyboard input'],
  110. ['encrypt', 'rw', 'bool', false, 'Use TLS/SSL/wss encryption'],
  111. ['true_color', 'rw', 'bool', true, 'Request true color pixel data'],
  112. ['local_cursor', 'rw', 'bool', false, 'Request locally rendered cursor'],
  113. ['shared', 'rw', 'bool', true, 'Request shared mode'],
  114. ['view_only', 'rw', 'bool', false, 'Disable client mouse/keyboard'],
  115. ['connectTimeout', 'rw', 'int', def_con_timeout, 'Time (s) to wait for connection'],
  116. ['disconnectTimeout', 'rw', 'int', 3, 'Time (s) to wait for disconnection'],
  117. // UltraVNC repeater ID to connect to
  118. ['repeaterID', 'rw', 'str', '', 'RepeaterID to connect to'],
  119. ['viewportDrag', 'rw', 'bool', false, 'Move the viewport on mouse drags'],
  120. ['check_rate', 'rw', 'int', 217, 'Timing (ms) of send/receive check'],
  121. ['fbu_req_rate', 'rw', 'int', 1413, 'Timing (ms) of frameBufferUpdate requests'],
  122. // Callback functions
  123. ['onUpdateState', 'rw', 'func', function() { },
  124. 'onUpdateState(rfb, state, oldstate, statusMsg): RFB state update/change '],
  125. ['onPasswordRequired', 'rw', 'func', function() { },
  126. 'onPasswordRequired(rfb): VNC password is required '],
  127. ['onClipboard', 'rw', 'func', function() { },
  128. 'onClipboard(rfb, text): RFB clipboard contents received'],
  129. ['onBell', 'rw', 'func', function() { },
  130. 'onBell(rfb): RFB Bell message received '],
  131. ['onFBUReceive', 'rw', 'func', function() { },
  132. 'onFBUReceive(rfb, fbu): RFB FBU received but not yet processed '],
  133. ['onFBUComplete', 'rw', 'func', function() { },
  134. 'onFBUComplete(rfb, fbu): RFB FBU received and processed '],
  135. ['onFBResize', 'rw', 'func', function() { },
  136. 'onFBResize(rfb, width, height): frame buffer resized'],
  137. // These callback names are deprecated
  138. ['updateState', 'rw', 'func', function() { },
  139. 'obsolete, use onUpdateState'],
  140. ['clipboardReceive', 'rw', 'func', function() { },
  141. 'obsolete, use onClipboard']
  142. ]);
  143. // Override/add some specific configuration getters/setters
  144. that.set_local_cursor = function(cursor) {
  145. if ((!cursor) || (cursor in {'0':1, 'no':1, 'false':1})) {
  146. conf.local_cursor = false;
  147. } else {
  148. if (display.get_cursor_uri()) {
  149. conf.local_cursor = true;
  150. } else {
  151. Util.Warn("Browser does not support local cursor");
  152. }
  153. }
  154. };
  155. // These are fake configuration getters
  156. that.get_display = function() { return display; };
  157. that.get_keyboard = function() { return keyboard; };
  158. that.get_mouse = function() { return mouse; };
  159. //
  160. // Setup routines
  161. //
  162. // Create the public API interface and initialize values that stay
  163. // constant across connect/disconnect
  164. function constructor() {
  165. var i, rmode;
  166. Util.Debug(">> RFB.constructor");
  167. // Create lookup tables based encoding number
  168. for (i=0; i < encodings.length; i+=1) {
  169. encHandlers[encodings[i][1]] = encHandlers[encodings[i][0]];
  170. encNames[encodings[i][1]] = encodings[i][0];
  171. encStats[encodings[i][1]] = [0, 0];
  172. }
  173. // Initialize display, mouse, keyboard, and websock
  174. try {
  175. display = new Display({'target': conf.target});
  176. } catch (exc) {
  177. Util.Error("Display exception: " + exc);
  178. updateState('fatal', "No working Display");
  179. }
  180. keyboard = new Keyboard({'target': conf.focusContainer,
  181. 'onKeyPress': keyPress});
  182. mouse = new Mouse({'target': conf.target,
  183. 'onMouseButton': mouseButton,
  184. 'onMouseMove': mouseMove});
  185. rmode = display.get_render_mode();
  186. ws = new Websock();
  187. ws.on('message', handle_message);
  188. ws.on('open', function() {
  189. if (rfb_state === "connect") {
  190. updateState('ProtocolVersion', "Starting VNC handshake");
  191. } else {
  192. fail("Got unexpected WebSockets connection");
  193. }
  194. });
  195. ws.on('close', function(e) {
  196. Util.Warn("WebSocket on-close event");
  197. var msg = "";
  198. if (e.code) {
  199. msg = " (code: " + e.code;
  200. if (e.reason) {
  201. msg += ", reason: " + e.reason;
  202. }
  203. msg += ")";
  204. }
  205. if (rfb_state === 'disconnect') {
  206. updateState('disconnected', 'VNC disconnected' + msg);
  207. } else if (rfb_state === 'ProtocolVersion') {
  208. fail('Failed to connect to server' + msg);
  209. } else if (rfb_state in {'failed':1, 'disconnected':1}) {
  210. Util.Error("Received onclose while disconnected" + msg);
  211. } else {
  212. fail('Server disconnected' + msg);
  213. }
  214. });
  215. ws.on('error', function(e) {
  216. Util.Warn("WebSocket on-error event");
  217. //fail("WebSock reported an error");
  218. });
  219. init_vars();
  220. /* Check web-socket-js if no builtin WebSocket support */
  221. if (Websock_native) {
  222. Util.Info("Using native WebSockets");
  223. updateState('loaded', 'noVNC ready: native WebSockets, ' + rmode);
  224. } else {
  225. Util.Warn("Using web-socket-js bridge. Flash version: " +
  226. Util.Flash.version);
  227. if ((! Util.Flash) ||
  228. (Util.Flash.version < 9)) {
  229. updateState('fatal', "WebSockets or <a href='http://get.adobe.com/flashplayer'>Adobe Flash<\/a> is required");
  230. } else if (document.location.href.substr(0, 7) === "file://") {
  231. updateState('fatal',
  232. "'file://' URL is incompatible with Adobe Flash");
  233. } else {
  234. updateState('loaded', 'noVNC ready: WebSockets emulation, ' + rmode);
  235. }
  236. }
  237. Util.Debug("<< RFB.constructor");
  238. return that; // Return the public API interface
  239. }
  240. function connect() {
  241. Util.Debug(">> RFB.connect");
  242. var uri;
  243. if (typeof UsingSocketIO !== "undefined") {
  244. uri = "http://" + rfb_host + ":" + rfb_port + "/" + rfb_path;
  245. } else {
  246. if (conf.encrypt) {
  247. uri = "wss://";
  248. } else {
  249. uri = "ws://";
  250. }
  251. uri += rfb_host + ":" + rfb_port + "/" + rfb_path;
  252. }
  253. Util.Info("connecting to " + uri);
  254. // TODO: make protocols a configurable
  255. ws.open(uri, ['binary', 'base64']);
  256. Util.Debug("<< RFB.connect");
  257. }
  258. // Initialize variables that are reset before each connection
  259. init_vars = function() {
  260. var i;
  261. /* Reset state */
  262. ws.init();
  263. FBU.rects = 0;
  264. FBU.subrects = 0; // RRE and HEXTILE
  265. FBU.lines = 0; // RAW
  266. FBU.tiles = 0; // HEXTILE
  267. FBU.zlibs = []; // TIGHT zlib encoders
  268. mouse_buttonMask = 0;
  269. mouse_arr = [];
  270. // Clear the per connection encoding stats
  271. for (i=0; i < encodings.length; i+=1) {
  272. encStats[encodings[i][1]][0] = 0;
  273. }
  274. for (i=0; i < 4; i++) {
  275. //FBU.zlibs[i] = new InflateStream();
  276. FBU.zlibs[i] = new TINF();
  277. FBU.zlibs[i].init();
  278. }
  279. };
  280. // Print statistics
  281. print_stats = function() {
  282. var i, s;
  283. Util.Info("Encoding stats for this connection:");
  284. for (i=0; i < encodings.length; i+=1) {
  285. s = encStats[encodings[i][1]];
  286. if ((s[0] + s[1]) > 0) {
  287. Util.Info(" " + encodings[i][0] + ": " +
  288. s[0] + " rects");
  289. }
  290. }
  291. Util.Info("Encoding stats since page load:");
  292. for (i=0; i < encodings.length; i+=1) {
  293. s = encStats[encodings[i][1]];
  294. if ((s[0] + s[1]) > 0) {
  295. Util.Info(" " + encodings[i][0] + ": " +
  296. s[1] + " rects");
  297. }
  298. }
  299. };
  300. //
  301. // Utility routines
  302. //
  303. /*
  304. * Page states:
  305. * loaded - page load, equivalent to disconnected
  306. * disconnected - idle state
  307. * connect - starting to connect (to ProtocolVersion)
  308. * normal - connected
  309. * disconnect - starting to disconnect
  310. * failed - abnormal disconnect
  311. * fatal - failed to load page, or fatal error
  312. *
  313. * RFB protocol initialization states:
  314. * ProtocolVersion
  315. * Security
  316. * Authentication
  317. * password - waiting for password, not part of RFB
  318. * SecurityResult
  319. * ClientInitialization - not triggered by server message
  320. * ServerInitialization (to normal)
  321. */
  322. updateState = function(state, statusMsg) {
  323. var func, cmsg, oldstate = rfb_state;
  324. if (state === oldstate) {
  325. /* Already here, ignore */
  326. Util.Debug("Already in state '" + state + "', ignoring.");
  327. return;
  328. }
  329. /*
  330. * These are disconnected states. A previous connect may
  331. * asynchronously cause a connection so make sure we are closed.
  332. */
  333. if (state in {'disconnected':1, 'loaded':1, 'connect':1,
  334. 'disconnect':1, 'failed':1, 'fatal':1}) {
  335. if (sendTimer) {
  336. clearInterval(sendTimer);
  337. sendTimer = null;
  338. }
  339. if (msgTimer) {
  340. clearInterval(msgTimer);
  341. msgTimer = null;
  342. }
  343. if (display && display.get_context()) {
  344. keyboard.ungrab();
  345. mouse.ungrab();
  346. display.defaultCursor();
  347. if ((Util.get_logging() !== 'debug') ||
  348. (state === 'loaded')) {
  349. // Show noVNC logo on load and when disconnected if
  350. // debug is off
  351. display.clear();
  352. }
  353. }
  354. ws.close();
  355. }
  356. if (oldstate === 'fatal') {
  357. Util.Error("Fatal error, cannot continue");
  358. }
  359. if ((state === 'failed') || (state === 'fatal')) {
  360. func = Util.Error;
  361. } else {
  362. func = Util.Warn;
  363. }
  364. cmsg = typeof(statusMsg) !== 'undefined' ? (" Msg: " + statusMsg) : "";
  365. func("New state '" + state + "', was '" + oldstate + "'." + cmsg);
  366. if ((oldstate === 'failed') && (state === 'disconnected')) {
  367. // Do disconnect action, but stay in failed state
  368. rfb_state = 'failed';
  369. } else {
  370. rfb_state = state;
  371. }
  372. if (connTimer && (rfb_state !== 'connect')) {
  373. Util.Debug("Clearing connect timer");
  374. clearInterval(connTimer);
  375. connTimer = null;
  376. }
  377. if (disconnTimer && (rfb_state !== 'disconnect')) {
  378. Util.Debug("Clearing disconnect timer");
  379. clearInterval(disconnTimer);
  380. disconnTimer = null;
  381. }
  382. switch (state) {
  383. case 'normal':
  384. if ((oldstate === 'disconnected') || (oldstate === 'failed')) {
  385. Util.Error("Invalid transition from 'disconnected' or 'failed' to 'normal'");
  386. }
  387. break;
  388. case 'connect':
  389. connTimer = setTimeout(function () {
  390. fail("Connect timeout");
  391. }, conf.connectTimeout * 1000);
  392. init_vars();
  393. connect();
  394. // WebSocket.onopen transitions to 'ProtocolVersion'
  395. break;
  396. case 'disconnect':
  397. if (! test_mode) {
  398. disconnTimer = setTimeout(function () {
  399. fail("Disconnect timeout");
  400. }, conf.disconnectTimeout * 1000);
  401. }
  402. print_stats();
  403. // WebSocket.onclose transitions to 'disconnected'
  404. break;
  405. case 'failed':
  406. if (oldstate === 'disconnected') {
  407. Util.Error("Invalid transition from 'disconnected' to 'failed'");
  408. }
  409. if (oldstate === 'normal') {
  410. Util.Error("Error while connected.");
  411. }
  412. if (oldstate === 'init') {
  413. Util.Error("Error while initializing.");
  414. }
  415. // Make sure we transition to disconnected
  416. setTimeout(function() { updateState('disconnected'); }, 50);
  417. break;
  418. default:
  419. // No state change action to take
  420. }
  421. if ((oldstate === 'failed') && (state === 'disconnected')) {
  422. // Leave the failed message
  423. conf.updateState(that, state, oldstate); // Obsolete
  424. conf.onUpdateState(that, state, oldstate);
  425. } else {
  426. conf.updateState(that, state, oldstate, statusMsg); // Obsolete
  427. conf.onUpdateState(that, state, oldstate, statusMsg);
  428. }
  429. };
  430. fail = function(msg) {
  431. updateState('failed', msg);
  432. return false;
  433. };
  434. handle_message = function() {
  435. //Util.Debug(">> handle_message ws.rQlen(): " + ws.rQlen());
  436. //Util.Debug("ws.rQslice(0,20): " + ws.rQslice(0,20) + " (" + ws.rQlen() + ")");
  437. if (ws.rQlen() === 0) {
  438. Util.Warn("handle_message called on empty receive queue");
  439. return;
  440. }
  441. switch (rfb_state) {
  442. case 'disconnected':
  443. case 'failed':
  444. Util.Error("Got data while disconnected");
  445. break;
  446. case 'normal':
  447. if (normal_msg() && ws.rQlen() > 0) {
  448. // true means we can continue processing
  449. // Give other events a chance to run
  450. if (msgTimer === null) {
  451. Util.Debug("More data to process, creating timer");
  452. msgTimer = setTimeout(function () {
  453. msgTimer = null;
  454. handle_message();
  455. }, 10);
  456. } else {
  457. Util.Debug("More data to process, existing timer");
  458. }
  459. }
  460. break;
  461. default:
  462. init_msg();
  463. break;
  464. }
  465. };
  466. function genDES(password, challenge) {
  467. var i, passwd = [];
  468. for (i=0; i < password.length; i += 1) {
  469. passwd.push(password.charCodeAt(i));
  470. }
  471. return (new DES(passwd)).encrypt(challenge);
  472. }
  473. function flushClient() {
  474. if (mouse_arr.length > 0) {
  475. //send(mouse_arr.concat(fbUpdateRequests()));
  476. ws.send(mouse_arr);
  477. setTimeout(function() {
  478. ws.send(fbUpdateRequests());
  479. }, 50);
  480. mouse_arr = [];
  481. return true;
  482. } else {
  483. return false;
  484. }
  485. }
  486. // overridable for testing
  487. checkEvents = function() {
  488. var now;
  489. if (rfb_state === 'normal' && !viewportDragging) {
  490. if (! flushClient()) {
  491. now = new Date().getTime();
  492. if (now > last_req_time + conf.fbu_req_rate) {
  493. last_req_time = now;
  494. ws.send(fbUpdateRequests());
  495. }
  496. }
  497. }
  498. setTimeout(checkEvents, conf.check_rate);
  499. };
  500. keyPress = function(keysym, down) {
  501. var arr;
  502. if (conf.view_only) { return; } // View only, skip keyboard events
  503. arr = keyEvent(keysym, down);
  504. arr = arr.concat(fbUpdateRequests());
  505. ws.send(arr);
  506. };
  507. mouseButton = function(x, y, down, bmask) {
  508. if (down) {
  509. mouse_buttonMask |= bmask;
  510. } else {
  511. mouse_buttonMask ^= bmask;
  512. }
  513. if (conf.viewportDrag) {
  514. if (down && !viewportDragging) {
  515. viewportDragging = true;
  516. viewportDragPos = {'x': x, 'y': y};
  517. // Skip sending mouse events
  518. return;
  519. } else {
  520. viewportDragging = false;
  521. ws.send(fbUpdateRequests()); // Force immediate redraw
  522. }
  523. }
  524. if (conf.view_only) { return; } // View only, skip mouse events
  525. mouse_arr = mouse_arr.concat(
  526. pointerEvent(display.absX(x), display.absY(y)) );
  527. flushClient();
  528. };
  529. mouseMove = function(x, y) {
  530. //Util.Debug('>> mouseMove ' + x + "," + y);
  531. var deltaX, deltaY;
  532. if (viewportDragging) {
  533. //deltaX = x - viewportDragPos.x; // drag viewport
  534. deltaX = viewportDragPos.x - x; // drag frame buffer
  535. //deltaY = y - viewportDragPos.y; // drag viewport
  536. deltaY = viewportDragPos.y - y; // drag frame buffer
  537. viewportDragPos = {'x': x, 'y': y};
  538. display.viewportChange(deltaX, deltaY);
  539. // Skip sending mouse events
  540. return;
  541. }
  542. if (conf.view_only) { return; } // View only, skip mouse events
  543. mouse_arr = mouse_arr.concat(
  544. pointerEvent(display.absX(x), display.absY(y)) );
  545. };
  546. //
  547. // Server message handlers
  548. //
  549. // RFB/VNC initialisation message handler
  550. init_msg = function() {
  551. //Util.Debug(">> init_msg [rfb_state '" + rfb_state + "']");
  552. var strlen, reason, length, sversion, cversion, repeaterID,
  553. i, types, num_types, challenge, response, bpp, depth,
  554. big_endian, red_max, green_max, blue_max, red_shift,
  555. green_shift, blue_shift, true_color, name_length, is_repeater;
  556. //Util.Debug("ws.rQ (" + ws.rQlen() + ") " + ws.rQslice(0));
  557. switch (rfb_state) {
  558. case 'ProtocolVersion' :
  559. if (ws.rQlen() < 12) {
  560. return fail("Incomplete protocol version");
  561. }
  562. sversion = ws.rQshiftStr(12).substr(4,7);
  563. Util.Info("Server ProtocolVersion: " + sversion);
  564. is_repeater = 0;
  565. switch (sversion) {
  566. case "000.000": is_repeater = 1; break; // UltraVNC repeater
  567. case "003.003": rfb_version = 3.3; break;
  568. case "003.006": rfb_version = 3.3; break; // UltraVNC
  569. case "003.889": rfb_version = 3.3; break; // Apple Remote Desktop
  570. case "003.007": rfb_version = 3.7; break;
  571. case "003.008": rfb_version = 3.8; break;
  572. case "004.000": rfb_version = 3.8; break; // Intel AMT KVM
  573. case "004.001": rfb_version = 3.8; break; // RealVNC 4.6
  574. default:
  575. return fail("Invalid server version " + sversion);
  576. }
  577. if (is_repeater) {
  578. repeaterID = conf.repeaterID;
  579. while (repeaterID.length < 250) {
  580. repeaterID += "\0";
  581. }
  582. ws.send_string(repeaterID);
  583. break;
  584. }
  585. if (rfb_version > rfb_max_version) {
  586. rfb_version = rfb_max_version;
  587. }
  588. if (! test_mode) {
  589. sendTimer = setInterval(function() {
  590. // Send updates either at a rate of one update
  591. // every 50ms, or whatever slower rate the network
  592. // can handle.
  593. ws.flush();
  594. }, 50);
  595. }
  596. cversion = "00" + parseInt(rfb_version,10) +
  597. ".00" + ((rfb_version * 10) % 10);
  598. ws.send_string("RFB " + cversion + "\n");
  599. updateState('Security', "Sent ProtocolVersion: " + cversion);
  600. break;
  601. case 'Security' :
  602. if (rfb_version >= 3.7) {
  603. // Server sends supported list, client decides
  604. num_types = ws.rQshift8();
  605. if (ws.rQwait("security type", num_types, 1)) { return false; }
  606. if (num_types === 0) {
  607. strlen = ws.rQshift32();
  608. reason = ws.rQshiftStr(strlen);
  609. return fail("Security failure: " + reason);
  610. }
  611. rfb_auth_scheme = 0;
  612. types = ws.rQshiftBytes(num_types);
  613. Util.Debug("Server security types: " + types);
  614. for (i=0; i < types.length; i+=1) {
  615. if ((types[i] > rfb_auth_scheme) && (types[i] < 3)) {
  616. rfb_auth_scheme = types[i];
  617. }
  618. }
  619. if (rfb_auth_scheme === 0) {
  620. return fail("Unsupported security types: " + types);
  621. }
  622. ws.send([rfb_auth_scheme]);
  623. } else {
  624. // Server decides
  625. if (ws.rQwait("security scheme", 4)) { return false; }
  626. rfb_auth_scheme = ws.rQshift32();
  627. }
  628. updateState('Authentication',
  629. "Authenticating using scheme: " + rfb_auth_scheme);
  630. init_msg(); // Recursive fallthrough (workaround JSLint complaint)
  631. break;
  632. // Triggered by fallthough, not by server message
  633. case 'Authentication' :
  634. //Util.Debug("Security auth scheme: " + rfb_auth_scheme);
  635. switch (rfb_auth_scheme) {
  636. case 0: // connection failed
  637. if (ws.rQwait("auth reason", 4)) { return false; }
  638. strlen = ws.rQshift32();
  639. reason = ws.rQshiftStr(strlen);
  640. return fail("Auth failure: " + reason);
  641. case 1: // no authentication
  642. if (rfb_version >= 3.8) {
  643. updateState('SecurityResult');
  644. return;
  645. }
  646. // Fall through to ClientInitialisation
  647. break;
  648. case 2: // VNC authentication
  649. if (rfb_password.length === 0) {
  650. // Notify via both callbacks since it is kind of
  651. // a RFB state change and a UI interface issue.
  652. updateState('password', "Password Required");
  653. conf.onPasswordRequired(that);
  654. return;
  655. }
  656. if (ws.rQwait("auth challenge", 16)) { return false; }
  657. challenge = ws.rQshiftBytes(16);
  658. //Util.Debug("Password: " + rfb_password);
  659. //Util.Debug("Challenge: " + challenge +
  660. // " (" + challenge.length + ")");
  661. response = genDES(rfb_password, challenge);
  662. //Util.Debug("Response: " + response +
  663. // " (" + response.length + ")");
  664. //Util.Debug("Sending DES encrypted auth response");
  665. ws.send(response);
  666. updateState('SecurityResult');
  667. return;
  668. default:
  669. fail("Unsupported auth scheme: " + rfb_auth_scheme);
  670. return;
  671. }
  672. updateState('ClientInitialisation', "No auth required");
  673. init_msg(); // Recursive fallthrough (workaround JSLint complaint)
  674. break;
  675. case 'SecurityResult' :
  676. if (ws.rQwait("VNC auth response ", 4)) { return false; }
  677. switch (ws.rQshift32()) {
  678. case 0: // OK
  679. // Fall through to ClientInitialisation
  680. break;
  681. case 1: // failed
  682. if (rfb_version >= 3.8) {
  683. length = ws.rQshift32();
  684. if (ws.rQwait("SecurityResult reason", length, 8)) {
  685. return false;
  686. }
  687. reason = ws.rQshiftStr(length);
  688. fail(reason);
  689. } else {
  690. fail("Authentication failed");
  691. }
  692. return;
  693. case 2: // too-many
  694. return fail("Too many auth attempts");
  695. }
  696. updateState('ClientInitialisation', "Authentication OK");
  697. init_msg(); // Recursive fallthrough (workaround JSLint complaint)
  698. break;
  699. // Triggered by fallthough, not by server message
  700. case 'ClientInitialisation' :
  701. ws.send([conf.shared ? 1 : 0]); // ClientInitialisation
  702. updateState('ServerInitialisation', "Authentication OK");
  703. break;
  704. case 'ServerInitialisation' :
  705. if (ws.rQwait("server initialization", 24)) { return false; }
  706. /* Screen size */
  707. fb_width = ws.rQshift16();
  708. fb_height = ws.rQshift16();
  709. /* PIXEL_FORMAT */
  710. bpp = ws.rQshift8();
  711. depth = ws.rQshift8();
  712. big_endian = ws.rQshift8();
  713. true_color = ws.rQshift8();
  714. red_max = ws.rQshift16();
  715. green_max = ws.rQshift16();
  716. blue_max = ws.rQshift16();
  717. red_shift = ws.rQshift8();
  718. green_shift = ws.rQshift8();
  719. blue_shift = ws.rQshift8();
  720. ws.rQshiftStr(3); // padding
  721. Util.Info("Screen: " + fb_width + "x" + fb_height +
  722. ", bpp: " + bpp + ", depth: " + depth +
  723. ", big_endian: " + big_endian +
  724. ", true_color: " + true_color +
  725. ", red_max: " + red_max +
  726. ", green_max: " + green_max +
  727. ", blue_max: " + blue_max +
  728. ", red_shift: " + red_shift +
  729. ", green_shift: " + green_shift +
  730. ", blue_shift: " + blue_shift);
  731. if (big_endian !== 0) {
  732. Util.Warn("Server native endian is not little endian");
  733. }
  734. if (red_shift !== 16) {
  735. Util.Warn("Server native red-shift is not 16");
  736. }
  737. if (blue_shift !== 0) {
  738. Util.Warn("Server native blue-shift is not 0");
  739. }
  740. /* Connection name/title */
  741. name_length = ws.rQshift32();
  742. fb_name = ws.rQshiftStr(name_length);
  743. if (conf.true_color && fb_name === "Intel(r) AMT KVM")
  744. {
  745. Util.Warn("Intel AMT KVM only support 8/16 bit depths. Disabling true color");
  746. conf.true_color = false;
  747. }
  748. display.set_true_color(conf.true_color);
  749. conf.onFBResize(that, fb_width, fb_height);
  750. display.resize(fb_width, fb_height);
  751. keyboard.grab();
  752. mouse.grab();
  753. if (conf.true_color) {
  754. fb_Bpp = 4;
  755. fb_depth = 3;
  756. } else {
  757. fb_Bpp = 1;
  758. fb_depth = 1;
  759. }
  760. response = pixelFormat();
  761. response = response.concat(clientEncodings());
  762. response = response.concat(fbUpdateRequests());
  763. timing.fbu_rt_start = (new Date()).getTime();
  764. timing.pixels = 0;
  765. ws.send(response);
  766. /* Start pushing/polling */
  767. setTimeout(checkEvents, conf.check_rate);
  768. if (conf.encrypt) {
  769. updateState('normal', "Connected (encrypted) to: " + fb_name);
  770. } else {
  771. updateState('normal', "Connected (unencrypted) to: " + fb_name);
  772. }
  773. break;
  774. }
  775. //Util.Debug("<< init_msg");
  776. };
  777. /* Normal RFB/VNC server message handler */
  778. normal_msg = function() {
  779. //Util.Debug(">> normal_msg");
  780. var ret = true, msg_type, length, text,
  781. c, first_colour, num_colours, red, green, blue;
  782. if (FBU.rects > 0) {
  783. msg_type = 0;
  784. } else {
  785. msg_type = ws.rQshift8();
  786. }
  787. switch (msg_type) {
  788. case 0: // FramebufferUpdate
  789. ret = framebufferUpdate(); // false means need more data
  790. break;
  791. case 1: // SetColourMapEntries
  792. Util.Debug("SetColourMapEntries");
  793. ws.rQshift8(); // Padding
  794. first_colour = ws.rQshift16(); // First colour
  795. num_colours = ws.rQshift16();
  796. if (ws.rQwait("SetColourMapEntries", num_colours*6, 6)) { return false; }
  797. for (c=0; c < num_colours; c+=1) {
  798. red = ws.rQshift16();
  799. //Util.Debug("red before: " + red);
  800. red = parseInt(red / 256, 10);
  801. //Util.Debug("red after: " + red);
  802. green = parseInt(ws.rQshift16() / 256, 10);
  803. blue = parseInt(ws.rQshift16() / 256, 10);
  804. display.set_colourMap([blue, green, red], first_colour + c);
  805. }
  806. Util.Debug("colourMap: " + display.get_colourMap());
  807. Util.Info("Registered " + num_colours + " colourMap entries");
  808. //Util.Debug("colourMap: " + display.get_colourMap());
  809. break;
  810. case 2: // Bell
  811. Util.Debug("Bell");
  812. conf.onBell(that);
  813. break;
  814. case 3: // ServerCutText
  815. Util.Debug("ServerCutText");
  816. if (ws.rQwait("ServerCutText header", 7, 1)) { return false; }
  817. ws.rQshiftBytes(3); // Padding
  818. length = ws.rQshift32();
  819. if (ws.rQwait("ServerCutText", length, 8)) { return false; }
  820. text = ws.rQshiftStr(length);
  821. conf.clipboardReceive(that, text); // Obsolete
  822. conf.onClipboard(that, text);
  823. break;
  824. default:
  825. fail("Disconnected: illegal server message type " + msg_type);
  826. Util.Debug("ws.rQslice(0,30):" + ws.rQslice(0,30));
  827. break;
  828. }
  829. //Util.Debug("<< normal_msg");
  830. return ret;
  831. };
  832. framebufferUpdate = function() {
  833. var now, hdr, fbu_rt_diff, ret = true;
  834. if (FBU.rects === 0) {
  835. //Util.Debug("New FBU: ws.rQslice(0,20): " + ws.rQslice(0,20));
  836. if (ws.rQwait("FBU header", 3)) {
  837. ws.rQunshift8(0); // FBU msg_type
  838. return false;
  839. }
  840. ws.rQshift8(); // padding
  841. FBU.rects = ws.rQshift16();
  842. //Util.Debug("FramebufferUpdate, rects:" + FBU.rects);
  843. FBU.bytes = 0;
  844. timing.cur_fbu = 0;
  845. if (timing.fbu_rt_start > 0) {
  846. now = (new Date()).getTime();
  847. Util.Info("First FBU latency: " + (now - timing.fbu_rt_start));
  848. }
  849. }
  850. while (FBU.rects > 0) {
  851. if (rfb_state !== "normal") {
  852. return false;
  853. }
  854. if (ws.rQwait("FBU", FBU.bytes)) { return false; }
  855. if (FBU.bytes === 0) {
  856. if (ws.rQwait("rect header", 12)) { return false; }
  857. /* New FramebufferUpdate */
  858. hdr = ws.rQshiftBytes(12);
  859. FBU.x = (hdr[0] << 8) + hdr[1];
  860. FBU.y = (hdr[2] << 8) + hdr[3];
  861. FBU.width = (hdr[4] << 8) + hdr[5];
  862. FBU.height = (hdr[6] << 8) + hdr[7];
  863. FBU.encoding = parseInt((hdr[8] << 24) + (hdr[9] << 16) +
  864. (hdr[10] << 8) + hdr[11], 10);
  865. conf.onFBUReceive(that,
  866. {'x': FBU.x, 'y': FBU.y,
  867. 'width': FBU.width, 'height': FBU.height,
  868. 'encoding': FBU.encoding,
  869. 'encodingName': encNames[FBU.encoding]});
  870. if (encNames[FBU.encoding]) {
  871. // Debug:
  872. /*
  873. var msg = "FramebufferUpdate rects:" + FBU.rects;
  874. msg += " x: " + FBU.x + " y: " + FBU.y;
  875. msg += " width: " + FBU.width + " height: " + FBU.height;
  876. msg += " encoding:" + FBU.encoding;
  877. msg += "(" + encNames[FBU.encoding] + ")";
  878. msg += ", ws.rQlen(): " + ws.rQlen();
  879. Util.Debug(msg);
  880. */
  881. } else {
  882. fail("Disconnected: unsupported encoding " +
  883. FBU.encoding);
  884. return false;
  885. }
  886. }
  887. timing.last_fbu = (new Date()).getTime();
  888. ret = encHandlers[FBU.encoding]();
  889. now = (new Date()).getTime();
  890. timing.cur_fbu += (now - timing.last_fbu);
  891. if (ret) {
  892. encStats[FBU.encoding][0] += 1;
  893. encStats[FBU.encoding][1] += 1;
  894. timing.pixels += FBU.width * FBU.height;
  895. }
  896. if (timing.pixels >= (fb_width * fb_height)) {
  897. if (((FBU.width === fb_width) &&
  898. (FBU.height === fb_height)) ||
  899. (timing.fbu_rt_start > 0)) {
  900. timing.full_fbu_total += timing.cur_fbu;
  901. timing.full_fbu_cnt += 1;
  902. Util.Info("Timing of full FBU, cur: " +
  903. timing.cur_fbu + ", total: " +
  904. timing.full_fbu_total + ", cnt: " +
  905. timing.full_fbu_cnt + ", avg: " +
  906. (timing.full_fbu_total /
  907. timing.full_fbu_cnt));
  908. }
  909. if (timing.fbu_rt_start > 0) {
  910. fbu_rt_diff = now - timing.fbu_rt_start;
  911. timing.fbu_rt_total += fbu_rt_diff;
  912. timing.fbu_rt_cnt += 1;
  913. Util.Info("full FBU round-trip, cur: " +
  914. fbu_rt_diff + ", total: " +
  915. timing.fbu_rt_total + ", cnt: " +
  916. timing.fbu_rt_cnt + ", avg: " +
  917. (timing.fbu_rt_total /
  918. timing.fbu_rt_cnt));
  919. timing.fbu_rt_start = 0;
  920. }
  921. }
  922. if (! ret) {
  923. return ret; // false ret means need more data
  924. }
  925. }
  926. conf.onFBUComplete(that,
  927. {'x': FBU.x, 'y': FBU.y,
  928. 'width': FBU.width, 'height': FBU.height,
  929. 'encoding': FBU.encoding,
  930. 'encodingName': encNames[FBU.encoding]});
  931. return true; // We finished this FBU
  932. };
  933. //
  934. // FramebufferUpdate encodings
  935. //
  936. encHandlers.RAW = function display_raw() {
  937. //Util.Debug(">> display_raw (" + ws.rQlen() + " bytes)");
  938. var cur_y, cur_height;
  939. if (FBU.lines === 0) {
  940. FBU.lines = FBU.height;
  941. }
  942. FBU.bytes = FBU.width * fb_Bpp; // At least a line
  943. if (ws.rQwait("RAW", FBU.bytes)) { return false; }
  944. cur_y = FBU.y + (FBU.height - FBU.lines);
  945. cur_height = Math.min(FBU.lines,
  946. Math.floor(ws.rQlen()/(FBU.width * fb_Bpp)));
  947. display.blitImage(FBU.x, cur_y, FBU.width, cur_height,
  948. ws.get_rQ(), ws.get_rQi());
  949. ws.rQshiftBytes(FBU.width * cur_height * fb_Bpp);
  950. FBU.lines -= cur_height;
  951. if (FBU.lines > 0) {
  952. FBU.bytes = FBU.width * fb_Bpp; // At least another line
  953. } else {
  954. FBU.rects -= 1;
  955. FBU.bytes = 0;
  956. }
  957. //Util.Debug("<< display_raw (" + ws.rQlen() + " bytes)");
  958. return true;
  959. };
  960. encHandlers.COPYRECT = function display_copy_rect() {
  961. //Util.Debug(">> display_copy_rect");
  962. var old_x, old_y;
  963. if (ws.rQwait("COPYRECT", 4)) { return false; }
  964. display.renderQ_push({
  965. 'type': 'copy',
  966. 'old_x': ws.rQshift16(),
  967. 'old_y': ws.rQshift16(),
  968. 'x': FBU.x,
  969. 'y': FBU.y,
  970. 'width': FBU.width,
  971. 'height': FBU.height});
  972. FBU.rects -= 1;
  973. FBU.bytes = 0;
  974. return true;
  975. };
  976. encHandlers.RRE = function display_rre() {
  977. //Util.Debug(">> display_rre (" + ws.rQlen() + " bytes)");
  978. var color, x, y, width, height, chunk;
  979. if (FBU.subrects === 0) {
  980. if (ws.rQwait("RRE", 4+fb_Bpp)) { return false; }
  981. FBU.subrects = ws.rQshift32();
  982. color = ws.rQshiftBytes(fb_Bpp); // Background
  983. display.fillRect(FBU.x, FBU.y, FBU.width, FBU.height, color);
  984. }
  985. while ((FBU.subrects > 0) && (ws.rQlen() >= (fb_Bpp + 8))) {
  986. color = ws.rQshiftBytes(fb_Bpp);
  987. x = ws.rQshift16();
  988. y = ws.rQshift16();
  989. width = ws.rQshift16();
  990. height = ws.rQshift16();
  991. display.fillRect(FBU.x + x, FBU.y + y, width, height, color);
  992. FBU.subrects -= 1;
  993. }
  994. //Util.Debug(" display_rre: rects: " + FBU.rects +
  995. // ", FBU.subrects: " + FBU.subrects);
  996. if (FBU.subrects > 0) {
  997. chunk = Math.min(rre_chunk_sz, FBU.subrects);
  998. FBU.bytes = (fb_Bpp + 8) * chunk;
  999. } else {
  1000. FBU.rects -= 1;
  1001. FBU.bytes = 0;
  1002. }
  1003. //Util.Debug("<< display_rre, FBU.bytes: " + FBU.bytes);
  1004. return true;
  1005. };
  1006. encHandlers.HEXTILE = function display_hextile() {
  1007. //Util.Debug(">> display_hextile");
  1008. var subencoding, subrects, color, cur_tile,
  1009. tile_x, x, w, tile_y, y, h, xy, s, sx, sy, wh, sw, sh,
  1010. rQ = ws.get_rQ(), rQi = ws.get_rQi();
  1011. if (FBU.tiles === 0) {
  1012. FBU.tiles_x = Math.ceil(FBU.width/16);
  1013. FBU.tiles_y = Math.ceil(FBU.height/16);
  1014. FBU.total_tiles = FBU.tiles_x * FBU.tiles_y;
  1015. FBU.tiles = FBU.total_tiles;
  1016. }
  1017. /* FBU.bytes comes in as 1, ws.rQlen() at least 1 */
  1018. while (FBU.tiles > 0) {
  1019. FBU.bytes = 1;
  1020. if (ws.rQwait("HEXTILE subencoding", FBU.bytes)) { return false; }
  1021. subencoding = rQ[rQi]; // Peek
  1022. if (subencoding > 30) { // Raw
  1023. fail("Disconnected: illegal hextile subencoding " + subencoding);
  1024. //Util.Debug("ws.rQslice(0,30):" + ws.rQslice(0,30));
  1025. return false;
  1026. }
  1027. subrects = 0;
  1028. cur_tile = FBU.total_tiles - FBU.tiles;
  1029. tile_x = cur_tile % FBU.tiles_x;
  1030. tile_y = Math.floor(cur_tile / FBU.tiles_x);
  1031. x = FBU.x + tile_x * 16;
  1032. y = FBU.y + tile_y * 16;
  1033. w = Math.min(16, (FBU.x + FBU.width) - x);
  1034. h = Math.min(16, (FBU.y + FBU.height) - y);
  1035. /* Figure out how much we are expecting */
  1036. if (subencoding & 0x01) { // Raw
  1037. //Util.Debug(" Raw subencoding");
  1038. FBU.bytes += w * h * fb_Bpp;
  1039. } else {
  1040. if (subencoding & 0x02) { // Background
  1041. FBU.bytes += fb_Bpp;
  1042. }
  1043. if (subencoding & 0x04) { // Foreground
  1044. FBU.bytes += fb_Bpp;
  1045. }
  1046. if (subencoding & 0x08) { // AnySubrects
  1047. FBU.bytes += 1; // Since we aren't shifting it off
  1048. if (ws.rQwait("hextile subrects header", FBU.bytes)) { return false; }
  1049. subrects = rQ[rQi + FBU.bytes-1]; // Peek
  1050. if (subencoding & 0x10) { // SubrectsColoured
  1051. FBU.bytes += subrects * (fb_Bpp + 2);
  1052. } else {
  1053. FBU.bytes += subrects * 2;
  1054. }
  1055. }
  1056. }
  1057. /*
  1058. Util.Debug(" tile:" + cur_tile + "/" + (FBU.total_tiles - 1) +
  1059. " (" + tile_x + "," + tile_y + ")" +
  1060. " [" + x + "," + y + "]@" + w + "x" + h +
  1061. ", subenc:" + subencoding +
  1062. "(last: " + FBU.lastsubencoding + "), subrects:" +
  1063. subrects +
  1064. ", ws.rQlen():" + ws.rQlen() + ", FBU.bytes:" + FBU.bytes +
  1065. " last:" + ws.rQslice(FBU.bytes-10, FBU.bytes) +
  1066. " next:" + ws.rQslice(FBU.bytes-1, FBU.bytes+10));
  1067. */
  1068. if (ws.rQwait("hextile", FBU.bytes)) { return false; }
  1069. /* We know the encoding and have a whole tile */
  1070. FBU.subencoding = rQ[rQi];
  1071. rQi += 1;
  1072. if (FBU.subencoding === 0) {
  1073. if (FBU.lastsubencoding & 0x01) {
  1074. /* Weird: ignore blanks after RAW */
  1075. Util.Debug(" Ignoring blank after RAW");
  1076. } else {
  1077. display.fillRect(x, y, w, h, FBU.background);
  1078. }
  1079. } else if (FBU.subencoding & 0x01) { // Raw
  1080. display.blitImage(x, y, w, h, rQ, rQi);
  1081. rQi += FBU.bytes - 1;
  1082. } else {
  1083. if (FBU.subencoding & 0x02) { // Background
  1084. FBU.background = rQ.slice(rQi, rQi + fb_Bpp);
  1085. rQi += fb_Bpp;
  1086. }
  1087. if (FBU.subencoding & 0x04) { // Foreground
  1088. FBU.foreground = rQ.slice(rQi, rQi + fb_Bpp);
  1089. rQi += fb_Bpp;
  1090. }
  1091. display.startTile(x, y, w, h, FBU.background);
  1092. if (FBU.subencoding & 0x08) { // AnySubrects
  1093. subrects = rQ[rQi];
  1094. rQi += 1;
  1095. for (s = 0; s < subrects; s += 1) {
  1096. if (FBU.subencoding & 0x10) { // SubrectsColoured
  1097. color = rQ.slice(rQi, rQi + fb_Bpp);
  1098. rQi += fb_Bpp;
  1099. } else {
  1100. color = FBU.foreground;
  1101. }
  1102. xy = rQ[rQi];
  1103. rQi += 1;
  1104. sx = (xy >> 4);
  1105. sy = (xy & 0x0f);
  1106. wh = rQ[rQi];
  1107. rQi += 1;
  1108. sw = (wh >> 4) + 1;
  1109. sh = (wh & 0x0f) + 1;
  1110. display.subTile(sx, sy, sw, sh, color);
  1111. }
  1112. }
  1113. display.finishTile();
  1114. }
  1115. ws.set_rQi(rQi);
  1116. FBU.lastsubencoding = FBU.subencoding;
  1117. FBU.bytes = 0;
  1118. FBU.tiles -= 1;
  1119. }
  1120. if (FBU.tiles === 0) {
  1121. FBU.rects -= 1;
  1122. }
  1123. //Util.Debug("<< display_hextile");
  1124. return true;
  1125. };
  1126. // Get 'compact length' header and data size
  1127. getTightCLength = function (arr) {
  1128. var header = 1, data = 0;
  1129. data += arr[0] & 0x7f;
  1130. if (arr[0] & 0x80) {
  1131. header += 1;
  1132. data += (arr[1] & 0x7f) << 7;
  1133. if (arr[1] & 0x80) {
  1134. header += 1;
  1135. data += arr[2] << 14;
  1136. }
  1137. }
  1138. return [header, data];
  1139. };
  1140. function display_tight(isTightPNG) {
  1141. //Util.Debug(">> display_tight");
  1142. if (fb_depth === 1) {
  1143. fail("Tight protocol handler only implements true color mode");
  1144. }
  1145. var ctl, cmode, clength, color, img, data;
  1146. var filterId = -1, resetStreams = 0, streamId = -1;
  1147. var rQ = ws.get_rQ(), rQi = ws.get_rQi();
  1148. FBU.bytes = 1; // compression-control byte
  1149. if (ws.rQwait("TIGHT compression-control", FBU.bytes)) { return false; }
  1150. var checksum = function(data) {
  1151. var sum=0, i;
  1152. for (i=0; i<data.length;i++) {
  1153. sum += data[i];
  1154. if (sum > 65536) sum -= 65536;
  1155. }
  1156. return sum;
  1157. }
  1158. var decompress = function(data) {
  1159. for (var i=0; i<4; i++) {
  1160. if ((resetStreams >> i) & 1) {
  1161. FBU.zlibs[i].reset();
  1162. Util.Info("Reset zlib stream " + i);
  1163. }
  1164. }
  1165. var uncompressed = FBU.zlibs[streamId].uncompress(data, 0);
  1166. if (uncompressed.status !== 0) {
  1167. Util.Error("Invalid data in zlib stream");
  1168. }
  1169. //Util.Warn("Decompressed " + data.length + " to " +
  1170. // uncompressed.data.length + " checksums " +
  1171. // checksum(data) + ":" + checksum(uncompressed.data));
  1172. return uncompressed.data;
  1173. }
  1174. var handlePalette = function() {
  1175. var numColors = rQ[rQi + 2] + 1;
  1176. var paletteSize = numColors * fb_depth;
  1177. FBU.bytes += paletteSize;
  1178. if (ws.rQwait("TIGHT palette " + cmode, FBU.bytes)) { return false; }
  1179. var bpp = (numColors <= 2) ? 1 : 8;
  1180. var rowSize = Math.floor((FBU.width * bpp + 7) / 8);
  1181. var raw = false;
  1182. if (rowSize * FBU.height < 12) {
  1183. raw = true;
  1184. clength = [0, rowSize * FBU.height];
  1185. } else {
  1186. clength = getTightCLength(ws.rQslice(3 + paletteSize,
  1187. 3 + paletteSize + 3));
  1188. }
  1189. FBU.bytes += clength[0] + clength[1];
  1190. if (ws.rQwait("TIGHT " + cmode, FBU.bytes)) { return false; }
  1191. // Shift ctl, filter id, num colors, palette entries, and clength off
  1192. ws.rQshiftBytes(3);
  1193. var palette = ws.rQshiftBytes(paletteSize);
  1194. ws.rQshiftBytes(clength[0]);
  1195. if (raw) {
  1196. data = ws.rQshiftBytes(clength[1]);
  1197. } else {
  1198. data = decompress(ws.rQshiftBytes(clength[1]));
  1199. }
  1200. // Convert indexed (palette based) image data to RGB
  1201. // TODO: reduce number of calculations inside loop
  1202. var dest = [];
  1203. var x, y, b, w, w1, dp, sp;
  1204. if (numColors === 2) {
  1205. w = Math.floor((FBU.width + 7) / 8);
  1206. w1 = Math.floor(FBU.width / 8);
  1207. for (y = 0; y < FBU.height; y++) {
  1208. for (x = 0; x < w1; x++) {
  1209. for (b = 7; b >= 0; b--) {
  1210. dp = (y*FBU.width + x*8 + 7-b) * 3;
  1211. sp = (data[y*w + x] >> b & 1) * 3;
  1212. dest[dp ] = palette[sp ];
  1213. dest[dp+1] = palette[sp+1];
  1214. dest[dp+2] = palette[sp+2];
  1215. }
  1216. }
  1217. for (b = 7; b >= 8 - FBU.width % 8; b--) {
  1218. dp = (y*FBU.width + x*8 + 7-b) * 3;
  1219. sp = (data[y*w + x] >> b & 1) * 3;
  1220. dest[dp ] = palette[sp ];
  1221. dest[dp+1] = palette[sp+1];
  1222. dest[dp+2] = palette[sp+2];
  1223. }
  1224. }
  1225. } else {
  1226. for (y = 0; y < FBU.height; y++) {
  1227. for (x = 0; x < FBU.width; x++) {
  1228. dp = (y*FBU.width + x) * 3;
  1229. sp = data[y*FBU.width + x] * 3;
  1230. dest[dp ] = palette[sp ];
  1231. dest[dp+1] = palette[sp+1];
  1232. dest[dp+2] = palette[sp+2];
  1233. }
  1234. }
  1235. }
  1236. display.renderQ_push({
  1237. 'type': 'blitRgb',
  1238. 'data': dest,
  1239. 'x': FBU.x,
  1240. 'y': FBU.y,
  1241. 'width': FBU.width,
  1242. 'height': FBU.height});
  1243. return true;
  1244. }
  1245. var handleCopy = function() {
  1246. var raw = false;
  1247. var uncompressedSize = FBU.width * FBU.height * fb_depth;
  1248. if (uncompressedSize < 12) {
  1249. raw = true;
  1250. clength = [0, uncompressedSize];
  1251. } else {
  1252. clength = getTightCLength(ws.rQslice(1, 4));
  1253. }
  1254. FBU.bytes = 1 + clength[0] + clength[1];
  1255. if (ws.rQwait("TIGHT " + cmode, FBU.bytes)) { return false; }
  1256. // Shift ctl, clength off
  1257. ws.rQshiftBytes(1 + clength[0]);
  1258. if (raw) {
  1259. data = ws.rQshiftBytes(clength[1]);
  1260. } else {
  1261. data = decompress(ws.rQshiftBytes(clength[1]));
  1262. }
  1263. display.renderQ_push({
  1264. 'type': 'blitRgb',
  1265. 'data': data,
  1266. 'x': FBU.x,
  1267. 'y': FBU.y,
  1268. 'width': FBU.width,
  1269. 'height': FBU.height});
  1270. return true;
  1271. }
  1272. ctl = ws.rQpeek8();
  1273. // Keep tight reset bits
  1274. resetStreams = ctl & 0xF;
  1275. // Figure out filter
  1276. ctl = ctl >> 4;
  1277. streamId = ctl & 0x3;
  1278. if (ctl === 0x08) cmode = "fill";
  1279. else if (ctl === 0x09) cmode = "jpeg";
  1280. else if (ctl === 0x0A) cmode = "png";
  1281. else if (ctl & 0x04) cmode = "filter";
  1282. else if (ctl < 0x04) cmode = "copy";
  1283. else return fail("Illegal tight compression received, ctl: " + ctl);
  1284. if (isTightPNG && (cmode === "filter" || cmode === "copy")) {
  1285. return fail("filter/copy received in tightPNG mode");
  1286. }
  1287. switch (cmode) {
  1288. // fill uses fb_depth because TPIXELs drop the padding byte
  1289. case "fill": FBU.bytes += fb_depth; break; // TPIXEL
  1290. case "jpeg": FBU.bytes += 3; break; // max clength
  1291. case "png": FBU.bytes += 3; break; // max clength
  1292. case "filter": FBU.bytes += 2; break; // filter id + num colors if palette
  1293. case "copy": break;
  1294. }
  1295. if (ws.rQwait("TIGHT " + cmode, FBU.bytes)) { return false; }
  1296. //Util.Debug(" ws.rQslice(0,20): " + ws.rQslice(0,20) + " (" + ws.rQlen() + ")");
  1297. //Util.Debug(" cmode: " + cmode);
  1298. // Determine FBU.bytes
  1299. switch (cmode) {
  1300. case "fill":
  1301. ws.rQshift8(); // shift off ctl
  1302. color = ws.rQshiftBytes(fb_depth);
  1303. display.renderQ_push({
  1304. 'type': 'fill',
  1305. 'x': FBU.x,
  1306. 'y': FBU.y,
  1307. 'width': FBU.width,
  1308. 'height': FBU.height,
  1309. 'color': [color[2], color[1], color[0]] });
  1310. break;
  1311. case "png":
  1312. case "jpeg":
  1313. clength = getTightCLength(ws.rQslice(1, 4));
  1314. FBU.bytes = 1 + clength[0] + clength[1]; // ctl + clength size + jpeg-data
  1315. if (ws.rQwait("TIGHT " + cmode, FBU.bytes)) { return false; }
  1316. // We have everything, render it
  1317. //Util.Debug(" jpeg, ws.rQlen(): " + ws.rQlen() + ", clength[0]: " +
  1318. // clength[0] + ", clength[1]: " + clength[1]);
  1319. ws.rQshiftBytes(1 + clength[0]); // shift off ctl + compact length
  1320. img = new Image();
  1321. img.src = "data:image/" + cmode +
  1322. extract_data_uri(ws.rQshiftBytes(clength[1]));
  1323. display.renderQ_push({
  1324. 'type': 'img',
  1325. 'img': img,
  1326. 'x': FBU.x,
  1327. 'y': FBU.y});
  1328. img = null;
  1329. break;
  1330. case "filter":
  1331. filterId = rQ[rQi + 1];
  1332. if (filterId === 1) {
  1333. if (!handlePalette()) { return false; }
  1334. } else {
  1335. // Filter 0, Copy could be valid here, but servers don't send it as an explicit filter
  1336. // Filter 2, Gradient is valid but not used if jpeg is enabled
  1337. throw("Unsupported tight subencoding received, filter: " + filterId);
  1338. }
  1339. break;
  1340. case "copy":
  1341. if (!handleCopy()) { return false; }
  1342. break;
  1343. }
  1344. FBU.bytes = 0;
  1345. FBU.rects -= 1;
  1346. //Util.Debug(" ending ws.rQslice(0,20): " + ws.rQslice(0,20) + " (" + ws.rQlen() + ")");
  1347. //Util.Debug("<< display_tight_png");
  1348. return true;
  1349. }
  1350. extract_data_uri = function(arr) {
  1351. //var i, stra = [];
  1352. //for (i=0; i< arr.length; i += 1) {
  1353. // stra.push(String.fromCharCode(arr[i]));
  1354. //}
  1355. //return "," + escape(stra.join(''));
  1356. return ";base64," + Base64.encode(arr);
  1357. };
  1358. encHandlers.TIGHT = function () { return display_tight(false); };
  1359. encHandlers.TIGHT_PNG = function () { return display_tight(true); };
  1360. encHandlers.last_rect = function last_rect() {
  1361. //Util.Debug(">> last_rect");
  1362. FBU.rects = 0;
  1363. //Util.Debug("<< last_rect");
  1364. return true;
  1365. };
  1366. encHandlers.DesktopSize = function set_desktopsize() {
  1367. Util.Debug(">> set_desktopsize");
  1368. fb_width = FBU.width;
  1369. fb_height = FBU.height;
  1370. conf.onFBResize(that, fb_width, fb_height);
  1371. display.resize(fb_width, fb_height);
  1372. timing.fbu_rt_start = (new Date()).getTime();
  1373. // Send a new non-incremental request
  1374. ws.send(fbUpdateRequests());
  1375. FBU.bytes = 0;
  1376. FBU.rects -= 1;
  1377. Util.Debug("<< set_desktopsize");
  1378. return true;
  1379. };
  1380. encHandlers.Cursor = function set_cursor() {
  1381. var x, y, w, h, pixelslength, masklength;
  1382. Util.Debug(">> set_cursor");
  1383. x = FBU.x; // hotspot-x
  1384. y = FBU.y; // hotspot-y
  1385. w = FBU.width;
  1386. h = FBU.height;
  1387. pixelslength = w * h * fb_Bpp;
  1388. masklength = Math.floor((w + 7) / 8) * h;
  1389. FBU.bytes = pixelslength + masklength;
  1390. if (ws.rQwait("cursor encoding", FBU.bytes)) { return false; }
  1391. //Util.Debug(" set_cursor, x: " + x + ", y: " + y + ", w: " + w + ", h: " + h);
  1392. display.changeCursor(ws.rQshiftBytes(pixelslength),
  1393. ws.rQshiftBytes(masklength),
  1394. x, y, w, h);
  1395. FBU.bytes = 0;
  1396. FBU.rects -= 1;
  1397. Util.Debug("<< set_cursor");
  1398. return true;
  1399. };
  1400. encHandlers.JPEG_quality_lo = function set_jpeg_quality() {
  1401. Util.Error("Server sent jpeg_quality pseudo-encoding");
  1402. };
  1403. encHandlers.compress_lo = function set_compress_level() {
  1404. Util.Error("Server sent compress level pseudo-encoding");
  1405. };
  1406. /*
  1407. * Client message routines
  1408. */
  1409. pixelFormat = function() {
  1410. //Util.Debug(">> pixelFormat");
  1411. var arr;
  1412. arr = [0]; // msg-type
  1413. arr.push8(0); // padding
  1414. arr.push8(0); // padding
  1415. arr.push8(0); // padding
  1416. arr.push8(fb_Bpp * 8); // bits-per-pixel
  1417. arr.push8(fb_depth * 8); // depth
  1418. arr.push8(0); // little-endian
  1419. arr.push8(conf.true_color ? 1 : 0); // true-color
  1420. arr.push16(255); // red-max
  1421. arr.push16(255); // green-max
  1422. arr.push16(255); // blue-max
  1423. arr.push8(16); // red-shift
  1424. arr.push8(8); // green-shift
  1425. arr.push8(0); // blue-shift
  1426. arr.push8(0); // padding
  1427. arr.push8(0); // padding
  1428. arr.push8(0); // padding
  1429. //Util.Debug("<< pixelFormat");
  1430. return arr;
  1431. };
  1432. clientEncodings = function() {
  1433. //Util.Debug(">> clientEncodings");
  1434. var arr, i, encList = [];
  1435. for (i=0; i<encodings.length; i += 1) {
  1436. if ((encodings[i][0] === "Cursor") &&
  1437. (! conf.local_cursor)) {
  1438. Util.Debug("Skipping Cursor pseudo-encoding");
  1439. } else {
  1440. //Util.Debug("Adding encoding: " + encodings[i][0]);
  1441. encList.push(encodings[i][1]);
  1442. }
  1443. }
  1444. arr = [2]; // msg-type
  1445. arr.push8(0); // padding
  1446. arr.push16(encList.length); // encoding count
  1447. for (i=0; i < encList.length; i += 1) {
  1448. arr.push32(encList[i]);
  1449. }
  1450. //Util.Debug("<< clientEncodings: " + arr);
  1451. return arr;
  1452. };
  1453. fbUpdateRequest = function(incremental, x, y, xw, yw) {
  1454. //Util.Debug(">> fbUpdateRequest");
  1455. if (typeof(x) === "undefined") { x = 0; }
  1456. if (typeof(y) === "undefined") { y = 0; }
  1457. if (typeof(xw) === "undefined") { xw = fb_width; }
  1458. if (typeof(yw) === "undefined") { yw = fb_height; }
  1459. var arr;
  1460. arr = [3]; // msg-type
  1461. arr.push8(incremental);
  1462. arr.push16(x);
  1463. arr.push16(y);
  1464. arr.push16(xw);
  1465. arr.push16(yw);
  1466. //Util.Debug("<< fbUpdateRequest");
  1467. return arr;
  1468. };
  1469. // Based on clean/dirty areas, generate requests to send
  1470. fbUpdateRequests = function() {
  1471. var cleanDirty = display.getCleanDirtyReset(),
  1472. arr = [], i, cb, db;
  1473. cb = cleanDirty.cleanBox;
  1474. if (cb.w > 0 && cb.h > 0) {
  1475. // Request incremental for clean box
  1476. arr = arr.concat(fbUpdateRequest(1, cb.x, cb.y, cb.w, cb.h));
  1477. }
  1478. for (i = 0; i < cleanDirty.dirtyBoxes.length; i++) {
  1479. db = cleanDirty.dirtyBoxes[i];
  1480. // Force all (non-incremental for dirty box
  1481. arr = arr.concat(fbUpdateRequest(0, db.x, db.y, db.w, db.h));
  1482. }
  1483. return arr;
  1484. };
  1485. keyEvent = function(keysym, down) {
  1486. //Util.Debug(">> keyEvent, keysym: " + keysym + ", down: " + down);
  1487. var arr;
  1488. arr = [4]; // msg-type
  1489. arr.push8(down);
  1490. arr.push16(0);
  1491. arr.push32(keysym);
  1492. //Util.Debug("<< keyEvent");
  1493. return arr;
  1494. };
  1495. pointerEvent = function(x, y) {
  1496. //Util.Debug(">> pointerEvent, x,y: " + x + "," + y +
  1497. // " , mask: " + mouse_buttonMask);
  1498. var arr;
  1499. arr = [5]; // msg-type
  1500. arr.push8(mouse_buttonMask);
  1501. arr.push16(x);
  1502. arr.push16(y);
  1503. //Util.Debug("<< pointerEvent");
  1504. return arr;
  1505. };
  1506. clientCutText = function(text) {
  1507. //Util.Debug(">> clientCutText");
  1508. var arr, i, n;
  1509. arr = [6]; // msg-type
  1510. arr.push8(0); // padding
  1511. arr.push8(0); // padding
  1512. arr.push8(0); // padding
  1513. arr.push32(text.length);
  1514. n = text.length;
  1515. for (i=0; i < n; i+=1) {
  1516. arr.push(text.charCodeAt(i));
  1517. }
  1518. //Util.Debug("<< clientCutText:" + arr);
  1519. return arr;
  1520. };
  1521. //
  1522. // Public API interface functions
  1523. //
  1524. that.connect = function(host, port, password, path) {
  1525. //Util.Debug(">> connect");
  1526. rfb_host = host;
  1527. rfb_port = port;
  1528. rfb_password = (password !== undefined) ? password : "";
  1529. rfb_path = (path !== undefined) ? path : "";
  1530. if ((!rfb_host) || (!rfb_port)) {
  1531. return fail("Must set host and port");
  1532. }
  1533. updateState('connect');
  1534. //Util.Debug("<< connect");
  1535. };
  1536. that.disconnect = function() {
  1537. //Util.Debug(">> disconnect");
  1538. updateState('disconnect', 'Disconnecting');
  1539. //Util.Debug("<< disconnect");
  1540. };
  1541. that.sendPassword = function(passwd) {
  1542. rfb_password = passwd;
  1543. rfb_state = "Authentication";
  1544. setTimeout(init_msg, 1);
  1545. };
  1546. that.sendCtrlAltDel = function() {
  1547. if (rfb_state !== "normal" || conf.view_only) { return false; }
  1548. Util.Info("Sending Ctrl-Alt-Del");
  1549. var arr = [];
  1550. arr = arr.concat(keyEvent(0xFFE3, 1)); // Control
  1551. arr = arr.concat(keyEvent(0xFFE9, 1)); // Alt
  1552. arr = arr.concat(keyEvent(0xFFFF, 1)); // Delete
  1553. arr = arr.concat(keyEvent(0xFFFF, 0)); // Delete
  1554. arr = arr.concat(keyEvent(0xFFE9, 0)); // Alt
  1555. arr = arr.concat(keyEvent(0xFFE3, 0)); // Control
  1556. arr = arr.concat(fbUpdateRequests());
  1557. ws.send(arr);
  1558. };
  1559. // Send a key press. If 'down' is not specified then send a down key
  1560. // followed by an up key.
  1561. that.sendKey = function(code, down) {
  1562. if (rfb_state !== "normal" || conf.view_only) { return false; }
  1563. var arr = [];
  1564. if (typeof down !== 'undefined') {
  1565. Util.Info("Sending key code (" + (down ? "down" : "up") + "): " + code);
  1566. arr = arr.concat(keyEvent(code, down ? 1 : 0));
  1567. } else {
  1568. Util.Info("Sending key code (down + up): " + code);
  1569. arr = arr.concat(keyEvent(code, 1));
  1570. arr = arr.concat(keyEvent(code, 0));
  1571. }
  1572. arr = arr.concat(fbUpdateRequests());
  1573. ws.send(arr);
  1574. };
  1575. that.clipboardPasteFrom = function(text) {
  1576. if (rfb_state !== "normal") { return; }
  1577. //Util.Debug(">> clipboardPasteFrom: " + text.substr(0,40) + "...");
  1578. ws.send(clientCutText(text));
  1579. //Util.Debug("<< clipboardPasteFrom");
  1580. };
  1581. // Override internal functions for testing
  1582. that.testMode = function(override_send) {
  1583. test_mode = true;
  1584. that.recv_message = ws.testMode(override_send);
  1585. checkEvents = function () { /* Stub Out */ };
  1586. that.connect = function(host, port, password) {
  1587. rfb_host = host;
  1588. rfb_port = port;
  1589. rfb_password = password;
  1590. updateState('ProtocolVersion', "Starting VNC handshake");
  1591. };
  1592. };
  1593. return constructor(); // Return the public API interface
  1594. } // End of RFB()