amt-desktop-0.0.2.js 49 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054
  1. /**
  2. * @description Remote Desktop
  3. * @author Ylian Saint-Hilaire
  4. * @version v0.0.2g
  5. */
  6. // Construct a MeshServer object
  7. var CreateAmtRemoteDesktop = function (divid, scrolldiv) {
  8. var obj = {};
  9. obj.canvasid = divid;
  10. obj.CanvasId = Q(divid);
  11. obj.scrolldiv = scrolldiv;
  12. obj.canvas = Q(divid).getContext('2d');
  13. obj.protocol = 2; // KVM
  14. obj.state = 0;
  15. obj.acc = null;
  16. obj.ScreenWidth = 960;
  17. obj.ScreenHeight = 700;
  18. obj.width = 0;
  19. obj.height = 0;
  20. obj.rwidth = 0;
  21. obj.rheight = 0;
  22. obj.bpp = 2; // Bytes per pixel (1 or 2 supported)
  23. obj.useRLE = true;
  24. obj.showmouse = true;
  25. obj.buttonmask = 0;
  26. obj.localKeyMap = true;
  27. obj.spare = null;
  28. obj.sparew = 0;
  29. obj.spareh = 0;
  30. obj.sparew2 = 0;
  31. obj.spareh2 = 0;
  32. obj.sparecache = {};
  33. obj.onScreenSizeChange = null;
  34. obj.frameRateDelay = 0;
  35. // ###BEGIN###{DesktopRotation}
  36. obj.noMouseRotate = false;
  37. obj.rotation = 0;
  38. // ###END###{DesktopRotation}
  39. // ###BEGIN###{DesktopInband}
  40. obj.kvmDataSupported = false;
  41. obj.onKvmData = null;
  42. obj.onKvmDataPending = [];
  43. obj.onKvmDataAck = -1;
  44. obj.holding = false;
  45. obj.lastKeepAlive = Date.now();
  46. obj.kvmExt = {};
  47. obj.kvmExtChanged = null;
  48. obj.useZLib = false;
  49. obj.decimationMode = 0; // 0 = Don't set, 1 = Disable, 2 = Automatic, 3 = Enabled
  50. obj.graymode = false;
  51. obj.lowcolor = false;
  52. // ###END###{DesktopInband}
  53. obj.mNagleTimer = null; // Mouse motion slowdown timer
  54. obj.mx = 0; // Last mouse x position
  55. obj.my = 0; // Last mouse y position
  56. // ###BEGIN###{DesktopFocus}
  57. obj.ox = -1; // Old mouse x position
  58. obj.oy = -1; // Old mouse y position
  59. obj.focusmode = 0;
  60. // ###END###{DesktopFocus}
  61. // ###BEGIN###{Inflate}
  62. obj.inflate = ZLIB.inflateInit(-15);
  63. // ###END###{Inflate}
  64. obj.xxStateChange = function (newstate) {
  65. if (newstate == 0) {
  66. obj.canvas.fillStyle = '#000000';
  67. obj.canvas.fillRect(0, 0, obj.width, obj.height);
  68. obj.canvas.canvas.width = obj.rwidth = obj.width = 640;
  69. obj.canvas.canvas.height = obj.rheight = obj.height = 400;
  70. QS(obj.canvasid).cursor = 'default';
  71. } else {
  72. QS(obj.canvasid).cursor = obj.showmouse ? 'default' : 'none';
  73. }
  74. }
  75. function arrToStr(arr) { return String.fromCharCode.apply(null, arr); }
  76. function strToArr(str) { var arr = new Uint8Array(str.length); for (var i = 0, j = str.length; i < j; ++i) { arr[i] = str.charCodeAt(i); } return arr }
  77. obj.ProcessBinaryData = function (data) {
  78. // ###BEGIN###{DesktopRecorder}
  79. // Record the data if needed
  80. if ((obj.recordedData != null) && (obj.recordedHolding !== true)) { obj.recordedData.push(recordingEntry(2, 1, String.fromCharCode.apply(null, new Uint8Array(data)))); }
  81. // ###END###{DesktopRecorder}
  82. // Append to accumulator
  83. if (obj.acc == null) {
  84. obj.acc = new Uint8Array(data);
  85. } else {
  86. var tmp = new Uint8Array(obj.acc.byteLength + data.byteLength);
  87. tmp.set(obj.acc, 0);
  88. tmp.set(new Uint8Array(data), obj.acc.byteLength);
  89. obj.acc = tmp;
  90. }
  91. while ((obj.acc != null) && (obj.acc.byteLength > 0)) {
  92. //console.log('KAcc', obj.state, obj.acc);
  93. var cmdsize = 0, accview = new DataView(obj.acc.buffer);
  94. if ((obj.state == 0) && (obj.acc.byteLength >= 12)) {
  95. // Getting handshake & version
  96. cmdsize = 12;
  97. //if (obj.acc.substring(0, 4) != 'RFB ') { return obj.Stop(); }
  98. //var version = parseFloat(obj.acc.substring(4, 11));
  99. //console.log('KVersion: ' + version);
  100. obj.state = 1;
  101. if (obj.parent) { delete obj.parent.connectTime; }
  102. obj.send('RFB 003.008\n');
  103. }
  104. else if ((obj.state == 1) && (obj.acc.byteLength >= 1)) {
  105. // Getting security options
  106. cmdsize = obj.acc[0] + 1;
  107. obj.send(String.fromCharCode(1)); // Send the 'None' security type. Since we already authenticated using redirection digest auth, we don't need to do this again.
  108. obj.state = 2;
  109. }
  110. else if ((obj.state == 2) && (obj.acc.byteLength >= 4)) {
  111. // Getting security response
  112. cmdsize = 4;
  113. if (accview.getUint32(0) != 0) { return obj.Stop(); }
  114. obj.send(String.fromCharCode(1)); // Send share desktop flag
  115. obj.state = 3;
  116. if (obj.parent) { obj.parent.disconnectCode = 50000; } // If Intel AMT disconnects at exactly this moment, indicates we need RLE8 or unsupported GPU.
  117. }
  118. else if ((obj.state == 3) && (obj.acc.byteLength >= 24)) {
  119. // Getting server init
  120. // ###BEGIN###{DesktopRotation}
  121. obj.rotation = 0; // We don't currently support screen init while rotated.
  122. // ###END###{DesktopRotation}
  123. var namelen = accview.getUint32(20);
  124. if (obj.acc.byteLength < 24 + namelen) return;
  125. cmdsize = 24 + namelen;
  126. obj.canvas.canvas.width = obj.rwidth = obj.width = obj.ScreenWidth = accview.getUint16(0);
  127. obj.canvas.canvas.height = obj.rheight = obj.height = obj.ScreenHeight = accview.getUint16(2);
  128. //console.log('Initial Desktop width: ' + obj.width + ', height: ' + obj.height);
  129. // ###BEGIN###{DesktopRecorder}
  130. obj.DeskRecordServerInit = String.fromCharCode.apply(null, new Uint8Array(obj.acc.buffer.slice(0, 24 + namelen)));
  131. // ###END###{DesktopRecorder}
  132. // These are all values we don't really need, we are going to only run in RGB565 or RGB332 and not use the flexibility provided by these settings.
  133. // Makes the javascript code smaller and maybe a bit faster.
  134. /*
  135. obj.xbpp = obj.acc[4];
  136. obj.depth = obj.acc[5];
  137. obj.bigend = obj.acc[6];
  138. obj.truecolor = obj.acc[7];
  139. obj.rmax = ReadShort(obj.acc, 8);
  140. obj.gmax = ReadShort(obj.acc, 10);
  141. obj.bmax = ReadShort(obj.acc, 12);
  142. obj.rsh = obj.acc[14];
  143. obj.gsh = obj.acc[15];
  144. obj.bsh = obj.acc[16];
  145. var name = obj.acc.substring(24, 24 + namelen);
  146. console.log('name: ' + name);
  147. console.log('width: ' + obj.width + ', height: ' + obj.height);
  148. console.log('bits-per-pixel: ' + obj.xbpp);
  149. console.log('depth: ' + obj.depth);
  150. console.log('big-endian-flag: ' + obj.bigend);
  151. console.log('true-colour-flag: ' + obj.truecolor);
  152. console.log('rgb max: ' + obj.rmax + ',' + obj.gmax + ',' + obj.bmax);
  153. console.log('rgb shift: ' + obj.rsh + ',' + obj.gsh + ',' + obj.bsh);
  154. */
  155. // SetEncodings, with AMT we can't omit RAW, must be specified.
  156. // Intel AMT supports encodings: RAW (0), RLE (16), Desktop Size (0xFFFFFF21, -223)
  157. var supportedEncodings = '';
  158. if (obj.useRLE) supportedEncodings += IntToStr(16);
  159. supportedEncodings += IntToStr(0);
  160. // ###BEGIN###{DesktopInband}
  161. supportedEncodings += IntToStr(1092);
  162. // ###END###{DesktopInband}
  163. obj.send(String.fromCharCode(2, 0) + ShortToStr((supportedEncodings.length / 4) + 1) + supportedEncodings + IntToStr(-223)); // Supported Encodings + Desktop Size
  164. if (obj.graymode == false) {
  165. // Set the pixel encoding to something much smaller
  166. // obj.send(String.fromCharCode(0, 0, 0, 0, 16, 16, 0, 1) + ShortToStr(31) + ShortToStr(63) + ShortToStr(31) + String.fromCharCode(11, 5, 0, 0, 0, 0)); // Setup 16 bit color RGB565 (This is the default, so we don't need to set it)
  167. if (obj.bpp == 1) obj.send(String.fromCharCode(0, 0, 0, 0, 8, 8, 0, 1) + ShortToStr(7) + ShortToStr(7) + ShortToStr(3) + String.fromCharCode(5, 2, 0, 0, 0, 0)); // Setup 8 bit color RGB332
  168. } else {
  169. // Gray scale modes
  170. if (obj.bpp == 2) { obj.bpp = 1; }
  171. if (obj.lowcolor == false) {
  172. obj.send(String.fromCharCode(0, 0, 0, 0, 8, 8, 0, 1) + ShortToStr(255) + ShortToStr(0) + ShortToStr(0) + String.fromCharCode(0, 0, 0, 0, 0, 0)); // Setup 8 bit black and white RGB800
  173. } else {
  174. obj.send(String.fromCharCode(0, 0, 0, 0, 8, 4, 0, 1) + ShortToStr(15) + ShortToStr(0) + ShortToStr(0) + String.fromCharCode(0, 0, 0, 0, 0, 0)); // Setup 4 bit black and white RGB400
  175. }
  176. }
  177. obj.state = 4;
  178. if (obj.parent) {
  179. obj.parent.connectTime = Date.now();
  180. obj.parent.disconnectCode = 0;
  181. obj.parent.xxStateChange(3);
  182. }
  183. //obj.timer = setInterval(obj.xxOnTimer, 50);
  184. // ###BEGIN###{DesktopFocus}
  185. obj.ox = -1; // Old mouse x position
  186. // ###END###{DesktopFocus}
  187. // ###BEGIN###{DesktopInband}
  188. if (obj.kvmExtChanged != null) {
  189. if (obj.decimationMode > 0) { obj.sendKvmExtCmd(2, obj.decimationMode); } // Set Decimation Mode (0 = Do not set, 1 = Disable, 2 = Auto, 3 = Enable)
  190. obj.sendKvmExtCmd(4, (obj.useZLib === true) ? 1 : 0); // Set ZLib state (0 = Disabled, 1 = Enabled)
  191. }
  192. // ###END###{DesktopInband}
  193. _SendRefresh();
  194. if (obj.onScreenSizeChange != null) { obj.onScreenSizeChange(obj, obj.ScreenWidth, obj.ScreenHeight); }
  195. if (obj.parent) {
  196. obj.parent.disconnectCode = 50001; // Everything looks good, a disconnection here would be Intel AMT initiated.
  197. // Check if the screen size is larger than Intel AMT should be able to handle
  198. //console.log('KVM Buffer Size: ' + (obj.bpp * obj.width * obj.height));
  199. if ((obj.bpp * obj.width * obj.height) > 9216000) { obj.parent.disconnectCode = 50002; } // Display buffer too large.
  200. }
  201. }
  202. else if (obj.state == 4) {
  203. switch (obj.acc[0]) {
  204. case 0: // FramebufferUpdate
  205. if (obj.acc.byteLength < 4) return;
  206. obj.state = 100 + accview.getUint16(2); // Read the number of tiles that are going to be sent, add 100 and use that as our protocol state.
  207. cmdsize = 4;
  208. // ###BEGIN###{DesktopRecorder}
  209. // This is the start of a new frame, start recording now if needed.
  210. if (obj.recordedHolding === true) { delete obj.recordedHolding; obj.recordedData.push(recordingEntry(2, 1, String.fromCharCode.apply(null, obj.acc))); }
  211. // ###END###{DesktopRecorder}
  212. break;
  213. case 2: // This is the bell, do nothing.
  214. cmdsize = 1;
  215. break;
  216. case 3: // This is ServerCutText
  217. if (obj.acc.byteLength < 8) return;
  218. var len = accview.getUint32(4) + 8;
  219. if (obj.acc.byteLength < len) return;
  220. cmdsize = handleServerCutText(obj.acc, accview);
  221. break;
  222. }
  223. }
  224. else if ((obj.state > 100) && (obj.acc.byteLength >= 12)) {
  225. var x = accview.getUint16(0),
  226. y = accview.getUint16(2),
  227. width = accview.getUint16(4),
  228. height = accview.getUint16(6),
  229. s = width * height,
  230. encoding = accview.getUint32(8);
  231. if (encoding < 17) {
  232. if ((width < 1) || (width > 64) || (height < 1) || (height > 64)) { console.log('Invalid tile size (' + width + ',' + height + '), disconnecting.'); return obj.Stop(); }
  233. // Set the spare bitmap to the right size if it's not already. This allows us to recycle the spare most if not all the time.
  234. if ((obj.sparew != width) || (obj.spareh != height)) {
  235. obj.sparew = obj.sparew2 = width;
  236. obj.spareh = obj.spareh2 = height;
  237. // ###BEGIN###{DesktopRotation}
  238. if (obj.rotation == 1 || obj.rotation == 3) { obj.sparew2 = height, obj.spareh2 = width; }
  239. // ###END###{DesktopRotation}
  240. var xspacecachename = obj.sparew2 + 'x' + obj.spareh2;
  241. obj.spare = obj.sparecache[xspacecachename];
  242. if (!obj.spare) {
  243. obj.sparecache[xspacecachename] = obj.spare = obj.canvas.createImageData(obj.sparew2, obj.spareh2);
  244. var j = (obj.sparew2 * obj.spareh2) << 2;
  245. for (var i = 3; i < j; i += 4) { obj.spare.data[i] = 0xFF; } // Set alpha channel to opaque.
  246. }
  247. }
  248. }
  249. if (encoding == 0xFFFFFF21) {
  250. // Desktop Size (0xFFFFFF21, -223)
  251. obj.canvas.canvas.width = obj.rwidth = obj.width = width;
  252. obj.canvas.canvas.height = obj.rheight = obj.height = height;
  253. obj.send(String.fromCharCode(3, 0, 0, 0, 0, 0) + ShortToStr(obj.width) + ShortToStr(obj.height)); // FramebufferUpdateRequest
  254. cmdsize = 12;
  255. if (obj.onScreenSizeChange != null) { obj.onScreenSizeChange(obj, obj.ScreenWidth, obj.ScreenHeight); }
  256. //console.log('Desktop width: ' + obj.width + ', height: ' + obj.height);
  257. // Check if the screen size is larger than Intel AMT should be able to handle
  258. //console.log('KVM Buffer Size: ' + (obj.bpp * obj.width * obj.height));
  259. if ((obj.parent) && ((obj.bpp * obj.width * obj.height) > 9216000)) { obj.parent.disconnectCode = 50002; } // Display buffer too large.
  260. } else if (encoding == 0) {
  261. // RAW encoding
  262. var ptr = 12, cs = 12 + (s * obj.bpp);
  263. if (obj.acc.byteLength < cs) return; // Check we have all the data needed and we can only draw 64x64 tiles.
  264. cmdsize = cs;
  265. // CRITICAL LOOP, optimize this as much as possible
  266. if (obj.bpp == 2) {
  267. for (var i = 0; i < s; i++) { _setPixel16(accview.getUint16(ptr, true), i); ptr += 2; }
  268. } else {
  269. for (var i = 0; i < s; i++) { _setPixel8(obj.acc[ptr++], i); }
  270. }
  271. _putImage(obj.spare, x, y);
  272. } else if (encoding == 16) {
  273. // RLE encoding
  274. if (obj.acc.byteLength < 16) return;
  275. var datalen = accview.getUint32(12);
  276. if (obj.acc.byteLength < (16 + datalen)) return;
  277. // Process the ZLib header if this is the first block
  278. var ptr = 16, delta = 5, dx = 0;
  279. if ((datalen > 5) && (obj.acc[ptr] == 0) && (accview.getUint16(ptr + 1, true) == (datalen - delta))) {
  280. // This is an uncompressed ZLib data block
  281. _decodeLRE(obj.acc, ptr + 5, x, y, width, height, s, datalen);
  282. }
  283. // ###BEGIN###{Inflate}
  284. else {
  285. // This is compressed ZLib data, decompress and process it. (TODO: This need to be optimized, remove str/arr conversions)
  286. var str = obj.inflate.inflate(arrToStr(new Uint8Array(obj.acc.buffer.slice(ptr, ptr + datalen - dx))));
  287. if (str.length > 0) { _decodeLRE(strToArr(str), 0, x, y, width, height, s, str.length); } else { console.log('Invalid deflate data'); }
  288. }
  289. // ###END###{Inflate}
  290. cmdsize = 16 + datalen;
  291. } else { return obj.Stop(); }
  292. if (--obj.state == 100) {
  293. obj.state = 4;
  294. if (obj.frameRateDelay == 0) {
  295. _SendRefresh(); // Ask for new frame
  296. } else {
  297. setTimeout(_SendRefresh, obj.frameRateDelay); // Hold x miliseconds before asking for a new frame
  298. }
  299. }
  300. }
  301. //console.log('cmdsize', cmdsize);
  302. if (cmdsize == 0) return;
  303. if (cmdsize != obj.acc.byteLength) { obj.acc = new Uint8Array(obj.acc.buffer.slice(cmdsize)); } else { obj.acc = null; }
  304. }
  305. }
  306. function _decodeLRE(data, ptr, x, y, width, height, s, datalen) {
  307. var subencoding = data[ptr++], index, v, runlengthdecode, palette = {}, rlecount = 0, runlength = 0, i;
  308. if (subencoding == 0) {
  309. // RAW encoding
  310. if (obj.bpp == 2) {
  311. for (i = 0; i < s; i++) { _setPixel16(data[ptr++] + (data[ptr++] << 8), i); }
  312. } else {
  313. for (i = 0; i < s; i++) { _setPixel8(data[ptr++], i); }
  314. }
  315. _putImage(obj.spare, x, y);
  316. }
  317. else if (subencoding == 1) {
  318. // Solid color tile
  319. if (obj.graymode) {
  320. v = data[ptr++];
  321. if (obj.lowcolor) { v = v << 4; }
  322. obj.canvas.fillStyle = 'rgb(' + v + ',' + v + ',' + v + ')';
  323. } else {
  324. v = data[ptr++] + ((obj.bpp == 2) ? (data[ptr++] << 8) : 0);
  325. obj.canvas.fillStyle = 'rgb(' + ((obj.bpp == 1) ? ((v & 224) + ',' + ((v & 28) << 3) + ',' + _fixColor((v & 3) << 6)) : (((v >> 8) & 248) + ',' + ((v >> 3) & 252) + ',' + ((v & 31) << 3))) + ')';
  326. }
  327. // ###BEGIN###{DesktopRotation}
  328. var xx = _rotX(x, y);
  329. y = _rotY(x, y);
  330. x = xx;
  331. // ###END###{DesktopRotation}
  332. obj.canvas.fillRect(x, y, width, height);
  333. }
  334. else if (subencoding > 1 && subencoding < 17) { // Packed palette encoded tile
  335. // Read the palette
  336. var br = 4, bm = 15; // br is BitRead and bm is BitMask. By adjusting these two we can support all the variations in this encoding.
  337. if (obj.bpp == 2) {
  338. for (i = 0; i < subencoding; i++) { palette[i] = data[ptr++] + (data[ptr++] << 8); }
  339. if (subencoding == 2) { br = 1; bm = 1; } else if (subencoding <= 4) { br = 2; bm = 3; } // Compute bits to read & bit mark
  340. while (rlecount < s && ptr < data.byteLength) { v = data[ptr++]; for (i = (8 - br); i >= 0; i -= br) { _setPixel16(palette[(v >> i) & bm], rlecount++); } } // Display all the bits
  341. } else {
  342. for (i = 0; i < subencoding; i++) { palette[i] = data[ptr++]; }
  343. if (subencoding == 2) { br = 1; bm = 1; } else if (subencoding <= 4) { br = 2; bm = 3; } // Compute bits to read & bit mark
  344. while (rlecount < s && ptr < data.byteLength) { v = data[ptr++]; for (i = (8 - br); i >= 0; i -= br) { _setPixel8(palette[(v >> i) & bm], rlecount++); } } // Display all the bits
  345. }
  346. _putImage(obj.spare, x, y);
  347. }
  348. else if (subencoding == 128) { // RLE encoded tile
  349. if (obj.bpp == 2) {
  350. while (rlecount < s && ptr < data.byteLength) {
  351. // Get the run color
  352. v = data[ptr++] + (data[ptr++] << 8);
  353. // Decode the run length. This is the fastest and most compact way I found to do this.
  354. runlength = 1; do { runlength += (runlengthdecode = data[ptr++]); } while (runlengthdecode == 255);
  355. // Draw a run
  356. if (obj.rotation == 0) {
  357. _setPixel16run(v, rlecount, runlength); rlecount += runlength;
  358. } else {
  359. while (--runlength >= 0) { _setPixel16(v, rlecount++); }
  360. }
  361. }
  362. } else {
  363. while (rlecount < s && ptr < data.byteLength) {
  364. // Get the run color
  365. v = data[ptr++];
  366. // Decode the run length. This is the fastest and most compact way I found to do this.
  367. runlength = 1; do { runlength += (runlengthdecode = data[ptr++]); } while (runlengthdecode == 255);
  368. // Draw a run
  369. if (obj.rotation == 0) {
  370. _setPixel8run(v, rlecount, runlength); rlecount += runlength;
  371. } else {
  372. while (--runlength >= 0) { _setPixel8(v, rlecount++); }
  373. }
  374. }
  375. }
  376. _putImage(obj.spare, x, y);
  377. }
  378. else if (subencoding > 129) { // Palette RLE encoded tile
  379. // Read the palette
  380. if (obj.bpp == 2) {
  381. for (i = 0; i < (subencoding - 128); i++) { palette[i] = data[ptr++] + (data[ptr++] << 8); }
  382. } else {
  383. for (i = 0; i < (subencoding - 128); i++) { palette[i] = data[ptr++]; }
  384. }
  385. // Decode RLE on palette
  386. while (rlecount < s && ptr < data.byteLength) {
  387. // Setup the run, get the color index and get the color from the palette.
  388. runlength = 1; index = data[ptr++]; v = palette[index % 128];
  389. // If the index starts with high order bit 1, this is a run and decode the run length.
  390. if (index > 127) { do { runlength += (runlengthdecode = data[ptr++]); } while (runlengthdecode == 255); }
  391. // Draw a run
  392. if (obj.rotation == 0) {
  393. if (obj.bpp == 2) {
  394. _setPixel16run(v, rlecount, runlength); rlecount += runlength;
  395. } else {
  396. _setPixel8run(v, rlecount, runlength); rlecount += runlength;
  397. }
  398. } else {
  399. if (obj.bpp == 2) {
  400. while (--runlength >= 0) { _setPixel16(v, rlecount++); }
  401. } else {
  402. while (--runlength >= 0) { _setPixel8(v, rlecount++); }
  403. }
  404. }
  405. }
  406. _putImage(obj.spare, x, y);
  407. }
  408. }
  409. // ###BEGIN###{DesktopInband}
  410. obj.hold = function (holding) {
  411. if (obj.holding == holding) return;
  412. obj.holding = holding;
  413. obj.canvas.fillStyle = '#000000';
  414. obj.canvas.fillRect(0, 0, obj.width, obj.height); // Paint black
  415. if (obj.holding == false) {
  416. // Go back to normal operations
  417. // Set canvas size and ask for full screen refresh
  418. if ((obj.canvas.canvas.width != obj.width) || (obj.canvas.canvas.height != obj.height)) {
  419. obj.canvas.canvas.width = obj.width; obj.canvas.canvas.height = obj.height;
  420. if (obj.onScreenSizeChange != null) { obj.onScreenSizeChange(obj, obj.ScreenWidth, obj.ScreenHeight); } // ???
  421. }
  422. obj.send(String.fromCharCode(3, 0, 0, 0, 0, 0) + ShortToStr(obj.width) + ShortToStr(obj.height)); // FramebufferUpdateRequest
  423. } else {
  424. obj.UnGrabMouseInput();
  425. obj.UnGrabKeyInput();
  426. }
  427. }
  428. // ###END###{DesktopInband}
  429. function _putImage(i, x, y) {
  430. // ###BEGIN###{DesktopInband}
  431. if (obj.holding == true) return;
  432. // ###END###{DesktopInband}
  433. // ###BEGIN###{DesktopRotation}
  434. var xx = _arotX(x, y);
  435. y = _arotY(x, y);
  436. x = xx;
  437. // ###END###{DesktopRotation}
  438. obj.canvas.putImageData(i, x, y);
  439. }
  440. // Set 8bit color RGB332
  441. function _setPixel8(v, p) {
  442. var pp = p << 2;
  443. // ###BEGIN###{DesktopRotation}
  444. if (obj.rotation > 0) {
  445. if (obj.rotation == 1) { var x = p % obj.sparew, y = Math.floor(p / obj.sparew); p = (x * obj.sparew2) + (obj.sparew2 - 1 - y); pp = p << 2; }
  446. else if (obj.rotation == 2) { pp = (obj.sparew * obj.spareh * 4) - 4 - pp; }
  447. else if (obj.rotation == 3) { var x = p % obj.sparew, y = Math.floor(p / obj.sparew); p = ((obj.sparew2 - 1 - x) * obj.sparew2) + (y); pp = p << 2; }
  448. }
  449. // ###END###{DesktopRotation}
  450. if (obj.graymode) {
  451. if (obj.lowcolor) { v = v << 4; }
  452. obj.spare.data[pp] = obj.spare.data[pp + 1] = obj.spare.data[pp + 2] = v;
  453. } else {
  454. obj.spare.data[pp] = v & 224;
  455. obj.spare.data[pp + 1] = (v & 28) << 3;
  456. obj.spare.data[pp + 2] = _fixColor((v & 3) << 6);
  457. }
  458. }
  459. // Set 16bit color RGB565
  460. function _setPixel16(v, p) {
  461. var pp = p << 2;
  462. // ###BEGIN###{DesktopRotation}
  463. if (obj.rotation > 0) {
  464. if (obj.rotation == 1) { var x = p % obj.sparew, y = Math.floor(p / obj.sparew); p = (x * obj.sparew2) + (obj.sparew2 - 1 - y); pp = p << 2; }
  465. else if (obj.rotation == 2) { pp = (obj.sparew * obj.spareh * 4) - 4 - pp; }
  466. else if (obj.rotation == 3) { var x = p % obj.sparew, y = Math.floor(p / obj.sparew); p = ((obj.sparew2 - 1 - x) * obj.sparew2) + (y); pp = p << 2; }
  467. }
  468. // ###END###{DesktopRotation}
  469. obj.spare.data[pp] = (v >> 8) & 248;
  470. obj.spare.data[pp + 1] = (v >> 3) & 252;
  471. obj.spare.data[pp + 2] = (v & 31) << 3;
  472. }
  473. // Set a run of 8bit color RGB332
  474. function _setPixel8run(v, p, run) {
  475. if (obj.graymode) {
  476. var pp = (p << 2);
  477. if (obj.lowcolor) { v = v << 4; }
  478. while (--run >= 0) { obj.spare.data[pp] = obj.spare.data[pp + 1] = obj.spare.data[pp + 2] = v; pp += 4; }
  479. } else {
  480. var pp = (p << 2), r = (v & 224), g = ((v & 28) << 3), b = (_fixColor((v & 3) << 6));
  481. while (--run >= 0) { obj.spare.data[pp] = r; obj.spare.data[pp + 1] = g; obj.spare.data[pp + 2] = b; pp += 4; }
  482. }
  483. }
  484. // Set a run of 16bit color RGB565
  485. function _setPixel16run(v, p, run) {
  486. var pp = (p << 2), r = ((v >> 8) & 248), g = ((v >> 3) & 252), b = ((v & 31) << 3);
  487. while (--run >= 0) { obj.spare.data[pp] = r; obj.spare.data[pp + 1] = g; obj.spare.data[pp + 2] = b; pp += 4; }
  488. }
  489. // ###BEGIN###{DesktopRotation}
  490. function _arotX(x, y) {
  491. if (obj.rotation == 0) return x;
  492. if (obj.rotation == 1) return obj.canvas.canvas.width - obj.sparew2 - y;
  493. if (obj.rotation == 2) return obj.canvas.canvas.width - obj.sparew2 - x;
  494. if (obj.rotation == 3) return y;
  495. return 0;
  496. }
  497. function _arotY(x, y) {
  498. if (obj.rotation == 0) return y;
  499. if (obj.rotation == 1) return x;
  500. if (obj.rotation == 2) return obj.canvas.canvas.height - obj.spareh2 - y;
  501. if (obj.rotation == 3) return obj.canvas.canvas.height - obj.spareh - x;
  502. return 0;
  503. }
  504. function _crotX(x, y) {
  505. if (obj.rotation == 0) return x;
  506. if (obj.rotation == 1) return y;
  507. if (obj.rotation == 2) return obj.canvas.canvas.width - x;
  508. if (obj.rotation == 3) return obj.canvas.canvas.height - y;
  509. return 0;
  510. }
  511. function _crotY(x, y) {
  512. if (obj.rotation == 0) return y;
  513. if (obj.rotation == 1) return obj.canvas.canvas.width - x;
  514. if (obj.rotation == 2) return obj.canvas.canvas.height - y;
  515. if (obj.rotation == 3) return x;
  516. return 0;
  517. }
  518. function _rotX(x, y) {
  519. if (obj.rotation == 0) return x;
  520. if (obj.rotation == 1) return x;
  521. if (obj.rotation == 2) return x - obj.canvas.canvas.width;
  522. if (obj.rotation == 3) return x - obj.canvas.canvas.height;
  523. return 0;
  524. }
  525. function _rotY(x, y) {
  526. if (obj.rotation == 0) return y;
  527. if (obj.rotation == 1) return y - obj.canvas.canvas.width;
  528. if (obj.rotation == 2) return y - obj.canvas.canvas.height;
  529. if (obj.rotation == 3) return y;
  530. return 0;
  531. }
  532. obj.tcanvas = null;
  533. obj.setRotation = function (x) {
  534. while (x < 0) { x += 4; }
  535. var newrotation = x % 4;
  536. //console.log('hard-rot: ' + newrotation);
  537. // ###BEGIN###{DesktopInband}
  538. if (obj.holding == true) { obj.rotation = newrotation; return; }
  539. // ###END###{DesktopInband}
  540. if (newrotation == obj.rotation) return true;
  541. var rw = obj.canvas.canvas.width;
  542. var rh = obj.canvas.canvas.height;
  543. if (obj.rotation == 1 || obj.rotation == 3) { rw = obj.canvas.canvas.height; rh = obj.canvas.canvas.width; }
  544. // Copy the canvas, put it back in the correct direction
  545. if (obj.tcanvas == null) obj.tcanvas = document.createElement('canvas');
  546. var tcanvasctx = obj.tcanvas.getContext('2d');
  547. tcanvasctx.setTransform(1, 0, 0, 1, 0, 0);
  548. tcanvasctx.canvas.width = rw;
  549. tcanvasctx.canvas.height = rh;
  550. tcanvasctx.rotate((obj.rotation * -90) * Math.PI / 180);
  551. if (obj.rotation == 0) tcanvasctx.drawImage(obj.canvas.canvas, 0, 0);
  552. if (obj.rotation == 1) tcanvasctx.drawImage(obj.canvas.canvas, -obj.canvas.canvas.width, 0);
  553. if (obj.rotation == 2) tcanvasctx.drawImage(obj.canvas.canvas, -obj.canvas.canvas.width, -obj.canvas.canvas.height);
  554. if (obj.rotation == 3) tcanvasctx.drawImage(obj.canvas.canvas, 0, -obj.canvas.canvas.height);
  555. // Change the size and orientation and copy the canvas back into the rotation
  556. if (obj.rotation == 0 || obj.rotation == 2) { obj.canvas.canvas.height = rw; obj.canvas.canvas.width = rh; }
  557. if (obj.rotation == 1 || obj.rotation == 3) { obj.canvas.canvas.height = rh; obj.canvas.canvas.width = rw; }
  558. obj.canvas.setTransform(1, 0, 0, 1, 0, 0);
  559. obj.canvas.rotate((newrotation * 90) * Math.PI / 180);
  560. obj.rotation = newrotation;
  561. obj.canvas.drawImage(obj.tcanvas, _rotX(0, 0), _rotY(0, 0));
  562. obj.width = obj.canvas.canvas.width;
  563. obj.height = obj.canvas.canvas.height;
  564. if (obj.onScreenResize != null) obj.onScreenResize(obj, obj.width, obj.height, obj.CanvasId);
  565. return true;
  566. }
  567. // ###END###{DesktopRotation}
  568. function _fixColor(c) { return (c > 127) ? (c + 32) : c; }
  569. function _SendRefresh() {
  570. // ###BEGIN###{DesktopInband}
  571. if (obj.holding == true) return;
  572. // ###END###{DesktopInband}
  573. // ###BEGIN###{DesktopFocus}
  574. if (obj.focusmode > 0) {
  575. // Request only pixels around the last mouse position
  576. var df = obj.focusmode * 2;
  577. obj.send(String.fromCharCode(3, 1) + ShortToStr(Math.max(Math.min(obj.ox, obj.mx) - obj.focusmode, 0)) + ShortToStr(Math.max(Math.min(obj.oy, obj.my) - obj.focusmode, 0)) + ShortToStr(df + Math.abs(obj.ox - obj.mx)) + ShortToStr(df + Math.abs(obj.oy - obj.my))); // FramebufferUpdateRequest
  578. obj.ox = obj.mx;
  579. obj.oy = obj.my;
  580. } else {
  581. // ###END###{DesktopFocus}
  582. // Request the entire screen
  583. obj.send(String.fromCharCode(3, 1, 0, 0, 0, 0) + ShortToStr(obj.rwidth) + ShortToStr(obj.rheight)); // FramebufferUpdateRequest
  584. // ###BEGIN###{DesktopFocus}
  585. }
  586. // ###END###{DesktopFocus}
  587. }
  588. obj.Start = function () {
  589. obj.state = 0;
  590. obj.acc = null;
  591. // ###BEGIN###{Inflate}
  592. obj.inflate.inflateReset();
  593. // ###END###{Inflate}
  594. // ###BEGIN###{DesktopInband}
  595. obj.onKvmDataPending = [];
  596. obj.onKvmDataAck = -1;
  597. obj.kvmDataSupported = false;
  598. obj.kvmExt = {};
  599. // ###END###{DesktopInband}
  600. for (var i in obj.sparecache) { delete obj.sparecache[i]; }
  601. }
  602. obj.Stop = function () { obj.UnGrabMouseInput(); obj.UnGrabKeyInput(); if (obj.parent) { obj.parent.Stop(); } }
  603. obj.send = function (x) { if (obj.parent) { obj.parent.send(x); } }
  604. var convertAmtKeyCodeTable = {
  605. 'Pause': 19,
  606. 'CapsLock': 20,
  607. 'Space': 32,
  608. 'Quote': 39,
  609. 'Minus': 45,
  610. 'NumpadMultiply': 42,
  611. 'NumpadAdd': 43,
  612. 'PrintScreen': 44,
  613. 'Comma': 44,
  614. 'NumpadSubtract': 45,
  615. 'NumpadDecimal': 46,
  616. 'Period': 46,
  617. 'Slash': 47,
  618. 'NumpadDivide': 47,
  619. 'Semicolon': 59,
  620. 'Equal': 61,
  621. 'OSLeft': 91,
  622. 'BracketLeft': 91,
  623. 'OSRight': 91,
  624. 'Backslash': 92,
  625. 'BracketRight': 93,
  626. 'ContextMenu': 93,
  627. 'Backquote': 96,
  628. 'NumLock': 144,
  629. 'ScrollLock': 145,
  630. 'Backspace': 0xff08,
  631. 'Tab': 0xff09,
  632. 'Enter': 0xff0d,
  633. 'NumpadEnter': 0xff0d,
  634. 'Escape': 0xff1b,
  635. 'Delete': 0xffff,
  636. 'Home': 0xff50,
  637. 'PageUp': 0xff55,
  638. 'PageDown': 0xff56,
  639. 'ArrowLeft': 0xff51,
  640. 'ArrowUp': 0xff52,
  641. 'ArrowRight': 0xff53,
  642. 'ArrowDown': 0xff54,
  643. 'End': 0xff57,
  644. 'Insert': 0xff63,
  645. 'F1': 0xffbe,
  646. 'F2': 0xffbf,
  647. 'F3': 0xffc0,
  648. 'F4': 0xffc1,
  649. 'F5': 0xffc2,
  650. 'F6': 0xffc3,
  651. 'F7': 0xffc4,
  652. 'F8': 0xffc5,
  653. 'F9': 0xffc6,
  654. 'F10': 0xffc7,
  655. 'F11': 0xffc8,
  656. 'F12': 0xffc9,
  657. 'ShiftLeft': 0xffe1,
  658. 'ShiftRight': 0xffe2,
  659. 'ControlLeft': 0xffe3,
  660. 'ControlRight': 0xffe4,
  661. 'AltLeft': 0xffe9,
  662. 'AltRight': 0xffea,
  663. 'MetaLeft': 0xffe7,
  664. 'MetaRight': 0xffe8
  665. }
  666. function convertAmtKeyCode(e) {
  667. if (e.code.startsWith('Key') && e.code.length == 4) { return e.code.charCodeAt(3) + ((e.shiftKey == false) ? 32 : 0); }
  668. if (e.code.startsWith('Digit') && e.code.length == 6) { return e.code.charCodeAt(5); }
  669. if (e.code.startsWith('Numpad') && e.code.length == 7) { return e.code.charCodeAt(6); }
  670. return convertAmtKeyCodeTable[e.code];
  671. }
  672. /*
  673. Intel AMT only recognizes a small subset of keysym characters defined in the keysymdef.h so you dont need to
  674. implement all the languages (this is taken care by the USB Scancode Extension in RFB4.0 protocol).
  675. The only subset recognized by the FW is the defined by the following sets : XK_LATIN1 , XK_MISCELLANY, XK_3270, XK_XKB_KEYS, XK_KATAKANA.
  676. In addition to keysymdef.h symbols there are 6 japanese extra keys that we do support:
  677. #define XK_Intel_EU_102kbd_backslash_pipe_45 0x17170056 // European 102-key: 45 (backslash/pipe), usb Usage: 0x64
  678. #define XK_Intel_JP_106kbd_yen_pipe 0x1717007d // Japanese 106-key: 14 (Yen/pipe), usb Usage: 0x89
  679. #define XK_Intel_JP_106kbd_backslash_underbar 0x17170073 // Japanese 106-key: 56 (backslash/underbar), usb Usage: 0x87
  680. #define XK_Intel_JP_106kbd_NoConvert 0x1717007b // Japanese 106-key: 131 (NoConvert), usb Usage: 0x8b
  681. #define XK_Intel_JP_106kbd_Convert 0x17170079 // Japanese 106-key: 132 (Convert), usb Usage: 0x8a
  682. #define XK_Intel_JP_106kbd_Hirigana_Katakana 0x17170070 // Japanese 106-key: 133 (Hirigana/Katakana), usb Usage: 0x88
  683. */
  684. function _keyevent(d, e) {
  685. if (!e) { e = window.event; }
  686. if (e.code && (obj.localKeyMap == false)) {
  687. // For new browsers, this mapping is keyboard language independent
  688. var k = convertAmtKeyCode(e);
  689. if (k != null) { obj.sendkey(k, d); }
  690. } else {
  691. // For older browsers, this mapping works best for EN-US keyboard
  692. var k = e.keyCode, kk = k;
  693. if (e.shiftKey == false && k >= 65 && k <= 90) kk = k + 32;
  694. if (k >= 112 && k <= 124) kk = k + 0xFF4E;
  695. if (k == 8) kk = 0xff08; // Backspace
  696. if (k == 9) kk = 0xff09; // Tab
  697. if (k == 13) kk = 0xff0d; // Return
  698. if (k == 16) kk = 0xffe1; // Shift (Left)
  699. if (k == 17) kk = 0xffe3; // Ctrl (Left)
  700. if (k == 18) kk = 0xffe9; // Alt (Left)
  701. if (k == 27) kk = 0xff1b; // ESC
  702. if (k == 33) kk = 0xff55; // PageUp
  703. if (k == 34) kk = 0xff56; // PageDown
  704. if (k == 35) kk = 0xff57; // End
  705. if (k == 36) kk = 0xff50; // Home
  706. if (k == 37) kk = 0xff51; // Left
  707. if (k == 38) kk = 0xff52; // Up
  708. if (k == 39) kk = 0xff53; // Right
  709. if (k == 40) kk = 0xff54; // Down
  710. if (k == 45) kk = 0xff63; // Insert
  711. if (k == 46) kk = 0xffff; // Delete
  712. if (k >= 96 && k <= 105) kk = k - 48; // Key pad numbers
  713. if (k == 106) kk = 42; // Pad *
  714. if (k == 107) kk = 43; // Pad +
  715. if (k == 109) kk = 45; // Pad -
  716. if (k == 110) kk = 46; // Pad .
  717. if (k == 111) kk = 47; // Pad /
  718. if (k == 186) kk = 59; // ;
  719. if (k == 187) kk = 61; // =
  720. if (k == 188) kk = 44; // ,
  721. if (k == 189) kk = 45; // -
  722. if (k == 190) kk = 46; // .
  723. if (k == 191) kk = 47; // /
  724. if (k == 192) kk = 96; // `
  725. if (k == 219) kk = 91; // [
  726. if (k == 220) kk = 92; // \
  727. if (k == 221) kk = 93; // ]
  728. if (k == 222) kk = 39; // '
  729. //console.log('Key' + d + ': ' + k + ' = ' + kk);
  730. obj.sendkey(kk, d);
  731. }
  732. return obj.haltEvent(e);
  733. }
  734. obj.sendkey = function (k, d) {
  735. if (typeof k == 'object') {
  736. var buf = ''; for (var i in k) { buf += (String.fromCharCode(4, k[i][1], 0, 0) + IntToStr(k[i][0])); } obj.send(buf);
  737. } else {
  738. obj.send(String.fromCharCode(4, d, 0, 0) + IntToStr(k));
  739. }
  740. }
  741. function handleServerCutText(acc, accview) {
  742. if (acc.byteLength < 8) return 0;
  743. var len = accview.getUint32(4) + 8;
  744. if (acc.byteLength < len) return 0;
  745. // ###BEGIN###{DesktopInband}
  746. if (obj.onKvmData != null) {
  747. var d = arrToStr(new Uint8Array(acc.buffer.slice(8, len)));
  748. if ((d.length >= 16) && (d.substring(0, 15) == '\0KvmDataChannel')) {
  749. if (obj.kvmDataSupported == false) { obj.kvmDataSupported = true; /*console.log('KVM Data Channel Supported.');*/ }
  750. if (((obj.onKvmDataAck == -1) && (d.length == 16)) || (d.charCodeAt(15) != 0)) { obj.onKvmDataAck = true; }
  751. try { if (urlvars && urlvars['kvmdatatrace']) { console.log('KVM-DataChannel-Recv(' + (d.length - 16) + '): ' + d.substring(16)); } } catch (ex) { }
  752. if (d.length >= 16) { obj.onKvmData(d.substring(16)); } // Event the data and ack
  753. if ((obj.onKvmDataAck == true) && (obj.onKvmDataPending.length > 0)) { obj.sendKvmData(obj.onKvmDataPending.shift()); } // Send pending data
  754. } else if ((d.length >= 13) && (d.substring(0, 11) == '\0KvmExtCmd\0')) {
  755. var cmd = d.charCodeAt(11), val = d.charCodeAt(12);
  756. //console.log('Received KvmExtCmd', cmd, val, d.length);
  757. if (cmd == 1) {
  758. obj.kvmExt.decimationMode = val;
  759. if (d.length > 13) { obj.kvmExt.decimationState = d.charCodeAt(13); }
  760. if (obj.kvmExtChanged != null) { obj.kvmExtChanged(1, obj.kvmExt, obj.kvmExt); }
  761. }
  762. if (cmd == 2) { obj.sendKvmExtCmd(1); }
  763. if (cmd == 3) { obj.kvmExt.compression = val; if (obj.kvmExtChanged != null) { obj.kvmExtChanged(3, obj.kvmExt); } }
  764. if (cmd == 4) { obj.sendKvmExtCmd(3); }
  765. } else {
  766. console.log('Got KVM clipboard data:', d);
  767. try { if (urlvars && urlvars['kvmdatatrace']) { console.log('KVM-ClipBoard-Recv(' + d.length + '): ' + rstr2hex(d) + ', ' + d); } } catch (ex) { }
  768. }
  769. }
  770. // ###END###{DesktopInband}
  771. return len;
  772. }
  773. // ###BEGIN###{DesktopInband}
  774. obj.sendKvmExtCmd = function (cmd, val) {
  775. //console.log('Sending KvmExtCmd', cmd, val);
  776. var x = '\0KvmExtCmd\0' + String.fromCharCode(cmd) + (val != null ? String.fromCharCode(val) : '');
  777. obj.send(String.fromCharCode(6, 0, 0, 0) + IntToStr(x.length) + x);
  778. }
  779. obj.sendKvmData = function (x) {
  780. if (obj.onKvmDataAck !== true) {
  781. obj.onKvmDataPending.push(x);
  782. } else {
  783. try { if (urlvars && urlvars['kvmdatatrace']) { console.log('KVM-DataChannel-Send(' + x.length + '): ' + x); } } catch (ex) { }
  784. x = '\0KvmDataChannel\0' + x;
  785. obj.send(String.fromCharCode(6, 0, 0, 0) + IntToStr(x.length) + x);
  786. obj.onKvmDataAck = false;
  787. }
  788. }
  789. // Send a HWKVM keep alive if it's not been sent in the last 5 seconds.
  790. obj.sendKeepAlive = function () {
  791. if (obj.lastKeepAlive < Date.now() - 5000) { obj.lastKeepAlive = Date.now(); obj.send(String.fromCharCode(6, 0, 0, 0) + IntToStr(16) + '\0KvmDataChannel\0'); }
  792. }
  793. // ###END###{DesktopInband}
  794. // ###BEGIN###{DesktopClipboard}
  795. obj.sendClipboardData = function (x) {
  796. try { if (urlvars && urlvars['kvmdatatrace']) { console.log('KVM-ClipBoard-Send(' + x.length + '): ' + rstr2hex(x) + ', ' + x); } } catch (ex) { }
  797. obj.send(String.fromCharCode(6, 0, 0, 0) + IntToStr(x.length) + x);
  798. }
  799. // ###END###{DesktopClipboard}
  800. obj.SendCtrlAltDelMsg = function () { obj.sendcad(); }
  801. obj.sendcad = function () { obj.sendkey([[0xFFE3, 1], [0xFFE9, 1], [0xFFFF, 1], [0xFFFF, 0], [0xFFE9, 0], [0xFFE3, 0]]); } // Control down, Alt down, Delete down, Delete up , Alt up , Control up
  802. var _MouseInputGrab = false;
  803. var _KeyInputGrab = false;
  804. obj.GrabMouseInput = function () {
  805. if (_MouseInputGrab == true) return;
  806. var c = obj.canvas.canvas;
  807. c.onmouseup = obj.mouseup;
  808. c.onmousedown = obj.mousedown;
  809. c.onmousemove = obj.mousemove;
  810. //c.onmousewheel = obj.mousewheel;
  811. c.onwheel = obj.mousewheel;
  812. //if (navigator.userAgent.match(/mozilla/i)) c.DOMMouseScroll = obj.xxDOMMouseScroll; else c.onmousewheel = obj.xxMouseWheel;
  813. _MouseInputGrab = true;
  814. }
  815. obj.UnGrabMouseInput = function () {
  816. if (_MouseInputGrab == false) return;
  817. var c = obj.canvas.canvas;
  818. c.onmousemove = null;
  819. c.onmouseup = null;
  820. c.onmousedown = null;
  821. //c.onmousewheel = null;
  822. c.onwheel = null;
  823. //if (navigator.userAgent.match(/mozilla/i)) c.DOMMouseScroll = null; else c.onmousewheel = null;
  824. _MouseInputGrab = false;
  825. }
  826. obj.GrabKeyInput = function () {
  827. if (_KeyInputGrab == true) return;
  828. document.onkeyup = obj.handleKeyUp;
  829. document.onkeydown = obj.handleKeyDown;
  830. document.onkeypress = obj.handleKeys;
  831. _KeyInputGrab = true;
  832. }
  833. obj.UnGrabKeyInput = function () {
  834. if (_KeyInputGrab == false) return;
  835. document.onkeyup = null;
  836. document.onkeydown = null;
  837. document.onkeypress = null;
  838. _KeyInputGrab = false;
  839. }
  840. obj.handleKeys = function (e) { return obj.haltEvent(e); }
  841. obj.handleKeyUp = function (e) { return _keyevent(0, e); }
  842. obj.handleKeyDown = function (e) { return _keyevent(1, e); }
  843. obj.haltEvent = function (e) { if (e.preventDefault) e.preventDefault(); if (e.stopPropagation) e.stopPropagation(); return false; }
  844. // RFB 'PointerEvent' and mouse handlers
  845. obj.mousedblclick = function (e) { }
  846. obj.mousewheel = function (e) {
  847. var v = 0;
  848. if (typeof e.deltaY == 'number') { v = -1 * e.deltaY; }
  849. else if (typeof e.detail == 'number') { v = -1 * e.detail; }
  850. else if (typeof e.wheelDelta == 'number') { v = e.wheelDelta; }
  851. if (v == 0) return;
  852. // Reverse mouse wheel if needed
  853. if (obj.ReverseMouseWheel) { v = -1 * v; }
  854. var tmpmask = obj.buttonmask;
  855. obj.buttonmask |= (1 << ((v > 0) ? 3 : 4));
  856. obj.mousemove(e, 1);
  857. obj.buttonmask = tmpmask;
  858. return obj.mousemove(e, 1);
  859. }
  860. obj.mousedown = function (e) { obj.buttonmask |= (1 << e.button); return obj.mousemove(e, 1); }
  861. obj.mouseup = function (e) { obj.buttonmask &= (0xFFFF - (1 << e.button)); return obj.mousemove(e, 1); }
  862. obj.mousemove = function (e, force) {
  863. if (obj.state < 4) return true;
  864. var ScaleFactorHeight = (obj.canvas.canvas.height / Q(obj.canvasid).offsetHeight);
  865. var ScaleFactorWidth = (obj.canvas.canvas.width / Q(obj.canvasid).offsetWidth);
  866. var Offsets = obj.getPositionOfControl(Q(obj.canvasid));
  867. obj.mx = ((event.pageX - Offsets[0]) * ScaleFactorWidth);
  868. obj.my = ((event.pageY - Offsets[1]) * ScaleFactorHeight);
  869. if (event.addx) { obj.mx += event.addx; }
  870. if (event.addy) { obj.my += event.addy; }
  871. // ###BEGIN###{DesktopRotation}
  872. if ((obj.rotation == 1) || (obj.rotation == 3)) {
  873. obj.mx = ((obj.mx * obj.rwidth) / obj.width);
  874. obj.my = ((obj.my * obj.rheight) / obj.height);
  875. }
  876. if (obj.noMouseRotate != true) {
  877. var mx2 = _crotX(obj.mx, obj.my);
  878. obj.my = _crotY(obj.mx, obj.my);
  879. obj.mx = mx2;
  880. }
  881. // ###END###{DesktopRotation}
  882. // This is the mouse motion nagle timer. Slow down the mouse motion event rate.
  883. if (force == 1) {
  884. obj.send(String.fromCharCode(5, obj.buttonmask) + ShortToStr(obj.mx) + ShortToStr(obj.my));
  885. if (obj.mNagleTimer != null) { clearTimeout(obj.mNagleTimer); obj.mNagleTimer = null; }
  886. } else {
  887. if (obj.mNagleTimer == null) {
  888. obj.mNagleTimer = setTimeout(function () {
  889. obj.send(String.fromCharCode(5, obj.buttonmask) + ShortToStr(obj.mx) + ShortToStr(obj.my));
  890. obj.mNagleTimer = null;
  891. }, 50);
  892. }
  893. }
  894. // ###BEGIN###{DesktopFocus}
  895. // Update focus area if we are in focus mode
  896. QV('DeskFocus', obj.focusmode);
  897. if (obj.focusmode != 0) {
  898. var x = Math.min(obj.mx, obj.canvas.canvas.width - obj.focusmode),
  899. y = Math.min(obj.my, obj.canvas.canvas.height - obj.focusmode),
  900. df = obj.focusmode * 2,
  901. c = Q(obj.canvasid),
  902. qx = c.offsetHeight / obj.canvas.canvas.height,
  903. qy = c.offsetWidth / obj.canvas.canvas.width,
  904. q = QS('DeskFocus'),
  905. ppos = obj.getPositionOfControl(Q(obj.canvasid).parentElement);
  906. q.left = (Math.max(((x - obj.focusmode) * qx), 0) + (pos[0] - ppos[0])) + 'px';
  907. q.top = (Math.max(((y - obj.focusmode) * qy), 0) + (pos[1] - ppos[1])) + 'px';
  908. q.width = ((df * qx) - 6) + 'px';
  909. q.height = ((df * qx) - 6) + 'px';
  910. }
  911. // ###END###{DesktopFocus}
  912. return obj.haltEvent(e);
  913. }
  914. obj.getPositionOfControl = function (Control) {
  915. var Position = Array(2);
  916. Position[0] = Position[1] = 0;
  917. while (Control) {
  918. Position[0] += Control.offsetLeft;
  919. Position[1] += Control.offsetTop;
  920. Control = Control.offsetParent;
  921. }
  922. return Position;
  923. }
  924. // ###BEGIN###{DesktopRecorder}
  925. obj.StartRecording = function () {
  926. if ((obj.recordedData != null) && (obj.DeskRecordServerInit != null)) return false;
  927. obj.recordedHolding = true;
  928. obj.recordedData = [];
  929. obj.recordedStart = Date.now();
  930. obj.recordedSize = 0;
  931. obj.recordedData.push(recordingEntry(1, 0, JSON.stringify({ magic: 'MeshCentralRelaySession', ver: 1, time: new Date().toLocaleString(), protocol: 102, bpp: obj.bpp, graymode: obj.graymode, lowcolor: obj.lowcolor, screenSize: [obj.width, obj.height] }))); // Metadata, 102 = Midstream Intel AMT KVM
  932. obj.DeskRecordServerInit = String.fromCharCode((obj.width >> 8), (obj.width & 0xFF), (obj.height >> 8), (obj.height & 0xFF)) + obj.DeskRecordServerInit.substring(4);
  933. obj.recordedData.push(recordingEntry(2, 1, obj.DeskRecordServerInit)); // This is the server init command
  934. obj.recordedData.push(recordingEntry(3, 0, atob(obj.CanvasId.toDataURL('image/png').split(',')[1]))); // Take a screen shot
  935. return true;
  936. }
  937. obj.StopRecording = function () {
  938. if (obj.recordedData == null) return;
  939. var r = obj.recordedData;
  940. r.push(recordingEntry(3, 0, 'MeshCentralMCREC'));
  941. delete obj.recordedData;
  942. delete obj.recordedStart;
  943. delete obj.recordedSize;
  944. return r;
  945. }
  946. function recordingEntry(type, flags, data) {
  947. //console.log('recordingEntry', type, flags, (typeof data == 'number')?data:data.length);
  948. // Header: Type (2) + Flags (2) + Size(4) + Time(8)
  949. // Type (1 = Header, 2 = Network Data), Flags (1 = Binary, 2 = User), Size (4 bytes), Time (8 bytes)
  950. var now = Date.now();
  951. if (typeof data == 'number') {
  952. obj.recordedSize += data;
  953. return ShortToStr(type) + ShortToStr(flags) + IntToStr(data) + IntToStr(now >> 32) + IntToStr(now & 32);
  954. } else {
  955. obj.recordedSize += data.length;
  956. return ShortToStr(type) + ShortToStr(flags) + IntToStr(data.length) + IntToStr(now >> 32) + IntToStr(now & 32) + data;
  957. }
  958. }
  959. // ###END###{DesktopRecorder}
  960. return obj;
  961. }