rfb.js 58 KB

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