diff --git a/public/scripts/amt-desktop-0.0.2-old.js b/public/scripts/amt-desktop-0.0.2-old.js new file mode 100644 index 00000000..3bc6fe39 --- /dev/null +++ b/public/scripts/amt-desktop-0.0.2-old.js @@ -0,0 +1,908 @@ +/** +* @description Remote Desktop +* @author Ylian Saint-Hilaire +* @version v0.0.2g +*/ + +// Construct a MeshServer object +var CreateAmtRemoteDesktop = function (divid, scrolldiv) { + var obj = {}; + obj.canvasid = divid; + obj.CanvasId = Q(divid); + obj.scrolldiv = scrolldiv; + obj.canvas = Q(divid).getContext('2d'); + obj.protocol = 2; // KVM + obj.state = 0; + obj.acc = null; + obj.ScreenWidth = 960; + obj.ScreenHeight = 700; + obj.width = 0; + obj.height = 0; + obj.rwidth = 0; + obj.rheight = 0; + obj.bpp = 2; // Bytes per pixel (1 or 2 supported) + obj.useZRLE = true; + obj.showmouse = true; + obj.buttonmask = 0; + obj.localKeyMap = true; + obj.spare = null; + obj.sparew = 0; + obj.spareh = 0; + obj.sparew2 = 0; + obj.spareh2 = 0; + obj.sparecache = {}; + obj.ZRLEfirst = 1; + obj.onScreenSizeChange = null; + //obj.onPreDrawImage = null; + obj.frameRateDelay = 0; + // ###BEGIN###{DesktopRotation} + obj.noMouseRotate = false; + obj.rotation = 0; + // ###END###{DesktopRotation} + // ###BEGIN###{DesktopInband} + obj.kvmDataSupported = false; + obj.onKvmData = null; + obj.onKvmDataPending = []; + obj.onKvmDataAck = -1; + obj.holding = false; + obj.lastKeepAlive = Date.now(); + // ###END###{DesktopInband} + obj.SwapMouse = false; + + obj.mNagleTimer = null; // Mouse motion slowdown timer + obj.mx = 0; // Last mouse x position + obj.my = 0; // Last mouse y position + // ###BEGIN###{DesktopFocus} + obj.ox = -1; // Old mouse x position + obj.oy = -1; // Old mouse y position + obj.focusmode = 0; + // ###END###{DesktopFocus} + // ###BEGIN###{Inflate} + obj.inflate = ZLIB.inflateInit(-15); + // ###END###{Inflate} + + obj.xxStateChange = function (newstate) { + if (newstate == 0) { + obj.canvas.fillStyle = '#000000'; + obj.canvas.fillRect(0, 0, obj.width, obj.height); + obj.canvas.canvas.width = obj.rwidth = obj.width = 640; + obj.canvas.canvas.height = obj.rheight = obj.height = 400; + QS(obj.canvasid).cursor = 'default'; + } else { + QS(obj.canvasid).cursor = obj.showmouse ? 'default' : 'none'; + } + } + + function arrToStr(arr) { return String.fromCharCode.apply(null, arr); } + 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 } + + obj.ProcessBinaryData = function (data) { + // Append to accumulator + if (obj.acc == null) { + obj.acc = new Uint8Array(data); + } else { + var tmp = new Uint8Array(obj.acc.byteLength + data.byteLength); + tmp.set(obj.acc, 0); + tmp.set(new Uint8Array(data), obj.acc.byteLength); + obj.acc = tmp; + } + + while ((obj.acc != null) && (obj.acc.byteLength > 0)) { + //console.log('KAcc', obj.state, obj.acc); + var cmdsize = 0, accview = new DataView(obj.acc.buffer); + if ((obj.state == 0) && (obj.acc.byteLength >= 12)) { + // Getting handshake & version + cmdsize = 12; + //if (obj.acc.substring(0, 4) != "RFB ") { return obj.Stop(); } + //var version = parseFloat(obj.acc.substring(4, 11)); + //console.log("KVersion: " + version); + obj.state = 1; + obj.send('RFB 003.008\n'); + } + else if ((obj.state == 1) && (obj.acc.byteLength >= 1)) { + // Getting security options + cmdsize = obj.acc[0] + 1; + 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. + obj.state = 2; + } + else if ((obj.state == 2) && (obj.acc.byteLength >= 4)) { + // Getting security response + cmdsize = 4; + if (accview.getUint32(0) != 0) { return obj.Stop(); } + obj.send(String.fromCharCode(1)); // Send share desktop flag + obj.state = 3; + } + else if ((obj.state == 3) && (obj.acc.byteLength >= 24)) { + // Getting server init + // ###BEGIN###{DesktopRotation} + obj.rotation = 0; // We don't currently support screen init while rotated. + // ###END###{DesktopRotation} + var namelen = accview.getUint32(20); + if (obj.acc.byteLength < 24 + namelen) return; + cmdsize = 24 + namelen; + obj.canvas.canvas.width = obj.rwidth = obj.width = obj.ScreenWidth = accview.getUint16(0); + obj.canvas.canvas.height = obj.rheight = obj.height = obj.ScreenHeight = accview.getUint16(2); + + // 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. + // Makes the javascript code smaller and maybe a bit faster. + /* + obj.xbpp = obj.acc[4]; + obj.depth = obj.acc[5]; + obj.bigend = obj.acc[6]; + obj.truecolor = obj.acc[7]; + obj.rmax = ReadShort(obj.acc, 8); + obj.gmax = ReadShort(obj.acc, 10); + obj.bmax = ReadShort(obj.acc, 12); + obj.rsh = obj.acc[14]; + obj.gsh = obj.acc[15]; + obj.bsh = obj.acc[16]; + var name = obj.acc.substring(24, 24 + namelen); + console.log("name: " + name); + console.log("width: " + obj.width + ", height: " + obj.height); + console.log("bits-per-pixel: " + obj.xbpp); + console.log("depth: " + obj.depth); + console.log("big-endian-flag: " + obj.bigend); + console.log("true-colour-flag: " + obj.truecolor); + console.log("rgb max: " + obj.rmax + "," + obj.gmax + "," + obj.bmax); + console.log("rgb shift: " + obj.rsh + "," + obj.gsh + "," + obj.bsh); + */ + + // SetEncodings, with AMT we can't omit RAW, must be specified. + // Intel AMT supports encodings: RAW (0), ZRLE (16), Desktop Size (0xFFFFFF21, -223) + + var supportedEncodings = ''; + if (obj.useZRLE) supportedEncodings += IntToStr(16); + supportedEncodings += IntToStr(0); + // ###BEGIN###{DesktopInband} + supportedEncodings += IntToStr(1092); + // ###END###{DesktopInband} + + obj.send(String.fromCharCode(2, 0) + ShortToStr((supportedEncodings.length / 4) + 1) + supportedEncodings + IntToStr(-223)); // Supported Encodings + Desktop Size + + // Set the pixel encoding to something much smaller + // 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) + 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 + + obj.state = 4; + if (obj.parent) { obj.parent.xxStateChange(3); } + _SendRefresh(); + //obj.timer = setInterval(obj.xxOnTimer, 50); + + // ###BEGIN###{DesktopFocus} + obj.ox = -1; // Old mouse x position + // ###END###{DesktopFocus} + + if (obj.onScreenSizeChange != null) { obj.onScreenSizeChange(obj, obj.ScreenWidth, obj.ScreenHeight); } + } + else if (obj.state == 4) { + switch (obj.acc[0]) { + case 0: // FramebufferUpdate + if (obj.acc.byteLength < 4) return; + 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. + cmdsize = 4; + if (obj.onPreDrawImage != null) obj.onPreDrawImage(); // Notify that we are about to draw on the canvas. + break; + case 2: // This is the bell, do nothing. + cmdsize = 1; + break; + case 3: // This is ServerCutText + if (obj.acc.byteLength < 8) return; + var len = accview.getUint32(4) + 8; + if (obj.acc.byteLength < len) return; + cmdsize = handleServerCutText(obj.acc, accview); + break; + } + } + else if ((obj.state > 100) && (obj.acc.byteLength >= 12)) { + var x = accview.getUint16(0), + y = accview.getUint16(2), + width = accview.getUint16(4), + height = accview.getUint16(6), + s = width * height, + encoding = accview.getUint32(8); + + if (encoding < 17) { + if ((width < 1) || (width > 64) || (height < 1) || (height > 64)) { console.log("Invalid tile size (" + width + "," + height + "), disconnecting."); return obj.Stop(); } + + // 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. + if ((obj.sparew != width) || (obj.spareh != height)) { + obj.sparew = obj.sparew2 = width; + obj.spareh = obj.spareh2 = height; + // ###BEGIN###{DesktopRotation} + if (obj.rotation == 1 || obj.rotation == 3) { obj.sparew2 = height, obj.spareh2 = width; } + // ###END###{DesktopRotation} + var xspacecachename = obj.sparew2 + 'x' + obj.spareh2; + obj.spare = obj.sparecache[xspacecachename]; + if (!obj.spare) { + obj.sparecache[xspacecachename] = obj.spare = obj.canvas.createImageData(obj.sparew2, obj.spareh2); + var j = (obj.sparew2 * obj.spareh2) << 2; + for (var i = 3; i < j; i += 4) { obj.spare.data[i] = 0xFF; } // Set alpha channel to opaque. + } + } + } + + if (encoding == 0xFFFFFF21) { + // Desktop Size (0xFFFFFF21, -223) + obj.canvas.canvas.width = obj.rwidth = obj.width = width; + obj.canvas.canvas.height = obj.rheight = obj.height = height; + obj.send(String.fromCharCode(3, 0, 0, 0, 0, 0) + ShortToStr(obj.width) + ShortToStr(obj.height)); // FramebufferUpdateRequest + cmdsize = 12; + if (obj.onScreenSizeChange != null) { obj.onScreenSizeChange(obj, obj.ScreenWidth, obj.ScreenHeight); } + //console.log("New desktop width: " + obj.width + ", height: " + obj.height); + } else if (encoding == 0) { + // RAW encoding + var ptr = 12, cs = 12 + (s * obj.bpp); + if (obj.acc.byteLength < cs) return; // Check we have all the data needed and we can only draw 64x64 tiles. + cmdsize = cs; + + // CRITICAL LOOP, optimize this as much as possible + if (obj.bpp == 2) { + for (var i = 0; i < s; i++) { _setPixel16(accview.getUint16(ptr, true), i); ptr += 2; } + } else { + for (var i = 0; i < s; i++) { _setPixel8(obj.acc[ptr++], i); } + } + _putImage(obj.spare, x, y); + } else if (encoding == 16) { + // ZRLE encoding + if (obj.acc.byteLength < 16) return; + var datalen = accview.getUint32(12); + if (obj.acc.byteLength < (16 + datalen)) return; + + // Process the ZLib header if this is the first block + var ptr = 16, delta = 5, dx = 0; + + if ((datalen > 5) && (obj.acc[ptr] == 0) && (accview.getUint16(ptr + 1, true) == (datalen - delta))) { + // This is an uncompressed ZLib data block + _decodeLRE(obj.acc, ptr + 5, x, y, width, height, s, datalen); + } + // ###BEGIN###{Inflate} + else { + // This is compressed ZLib data, decompress and process it. (TODO: This need to be optimized, remove str/arr conversions) + var str = obj.inflate.inflate(arrToStr(new Uint8Array(obj.acc.buffer.slice(ptr, ptr + datalen - dx)))); + if (str.length > 0) { _decodeLRE(strToArr(str), 0, x, y, width, height, s, str.length); } else { console.log("Invalid deflate data"); } + } + // ###END###{Inflate} + + cmdsize = 16 + datalen; + } else { return obj.Stop(); } + if (--obj.state == 100) { + obj.state = 4; + if (obj.frameRateDelay == 0) { + _SendRefresh(); // Ask for new frame + } else { + setTimeout(_SendRefresh, obj.frameRateDelay); // Hold x miliseconds before asking for a new frame + } + } + } + + //console.log('cmdsize', cmdsize); + if (cmdsize == 0) return; + if (cmdsize != obj.acc.byteLength) { obj.acc = new Uint8Array(obj.acc.buffer.slice(cmdsize)); } else { obj.acc = null; } + } + } + + function _decodeLRE(data, ptr, x, y, width, height, s, datalen) { + var subencoding = data[ptr++], index, v, runlengthdecode, palette = {}, rlecount = 0, runlength = 0, i; + if (subencoding == 0) { + // RAW encoding + if (obj.bpp == 2) { + for (i = 0; i < s; i++) { _setPixel16(data[ptr++] + (data[ptr++] << 8), i); } + } else { + for (i = 0; i < s; i++) { _setPixel8(data[ptr++], i); } + } + _putImage(obj.spare, x, y); + } + else if (subencoding == 1) { + // Solid color tile + v = data[ptr++] + ((obj.bpp == 2) ? (data[ptr++] << 8) : 0); + 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))) + ')'; + + // ###BEGIN###{DesktopRotation} + var xx = _rotX(x, y); + y = _rotY(x, y); + x = xx; + // ###END###{DesktopRotation} + + obj.canvas.fillRect(x, y, width, height); + } + else if (subencoding > 1 && subencoding < 17) { // Packed palette encoded tile + // Read the palette + 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. + if (obj.bpp == 2) { + for (i = 0; i < subencoding; i++) { palette[i] = data[ptr++] + (data[ptr++] << 8); } + if (subencoding == 2) { br = 1; bm = 1; } else if (subencoding <= 4) { br = 2; bm = 3; } // Compute bits to read & bit mark + 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 + } else { + for (i = 0; i < subencoding; i++) { palette[i] = data[ptr++]; } + if (subencoding == 2) { br = 1; bm = 1; } else if (subencoding <= 4) { br = 2; bm = 3; } // Compute bits to read & bit mark + 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 + } + _putImage(obj.spare, x, y); + } + else if (subencoding == 128) { // RLE encoded tile + if (obj.bpp == 2) { + while (rlecount < s && ptr < data.byteLength) { + // Get the run color + v = data[ptr++] + (data[ptr++] << 8); + + // Decode the run length. This is the fastest and most compact way I found to do this. + runlength = 1; do { runlength += (runlengthdecode = data[ptr++]); } while (runlengthdecode == 255); + + // Draw a run + if (obj.rotation == 0) { + _setPixel16run(v, rlecount, runlength); rlecount += runlength; + } else { + while (--runlength >= 0) { _setPixel16(v, rlecount++); } + } + } + } else { + while (rlecount < s && ptr < data.byteLength) { + // Get the run color + v = data[ptr++]; + + // Decode the run length. This is the fastest and most compact way I found to do this. + runlength = 1; do { runlength += (runlengthdecode = data[ptr++]); } while (runlengthdecode == 255); + + // Draw a run + if (obj.rotation == 0) { + _setPixel8run(v, rlecount, runlength); rlecount += runlength; + } else { + while (--runlength >= 0) { _setPixel8(v, rlecount++); } + } + } + } + _putImage(obj.spare, x, y); + } + else if (subencoding > 129) { // Palette RLE encoded tile + // Read the palette + if (obj.bpp == 2) { + for (i = 0; i < (subencoding - 128); i++) { palette[i] = data[ptr++] + (data[ptr++] << 8); } + } else { + for (i = 0; i < (subencoding - 128); i++) { palette[i] = data[ptr++]; } + } + + // Decode RLE on palette + while (rlecount < s && ptr < data.byteLength) { + // Setup the run, get the color index and get the color from the palette. + runlength = 1; index = data[ptr++]; v = palette[index % 128]; + + // If the index starts with high order bit 1, this is a run and decode the run length. + if (index > 127) { do { runlength += (runlengthdecode = data[ptr++]); } while (runlengthdecode == 255); } + + // Draw a run + if (obj.rotation == 0) { + if (obj.bpp == 2) { + _setPixel16run(v, rlecount, runlength); rlecount += runlength; + } else { + _setPixel8run(v, rlecount, runlength); rlecount += runlength; + } + } else { + if (obj.bpp == 2) { + while (--runlength >= 0) { _setPixel16(v, rlecount++); } + } else { + while (--runlength >= 0) { _setPixel8(v, rlecount++); } + } + } + } + _putImage(obj.spare, x, y); + } + } + + // ###BEGIN###{DesktopInband} + obj.hold = function (holding) { + if (obj.holding == holding) return; + obj.holding = holding; + obj.canvas.fillStyle = '#000000'; + obj.canvas.fillRect(0, 0, obj.width, obj.height); // Paint black + if (obj.holding == false) { + // Go back to normal operations + // Set canvas size and ask for full screen refresh + if ((obj.canvas.canvas.width != obj.width) || (obj.canvas.canvas.height != obj.height)) { + obj.canvas.canvas.width = obj.width; obj.canvas.canvas.height = obj.height; + if (obj.onScreenSizeChange != null) { obj.onScreenSizeChange(obj, obj.ScreenWidth, obj.ScreenHeight); } // ??? + } + obj.Send(String.fromCharCode(3, 0, 0, 0, 0, 0) + ShortToStr(obj.width) + ShortToStr(obj.height)); // FramebufferUpdateRequest + } else { + obj.UnGrabMouseInput(); + obj.UnGrabKeyInput(); + } + } + // ###END###{DesktopInband} + + function _putImage(i, x, y) { + // ###BEGIN###{DesktopInband} + if (obj.holding == true) return; + // ###END###{DesktopInband} + // ###BEGIN###{DesktopRotation} + var xx = _arotX(x, y); + y = _arotY(x, y); + x = xx; + // ###END###{DesktopRotation} + obj.canvas.putImageData(i, x, y); + } + + // Set 8bit color RGB332 + function _setPixel8(v, p) { + var pp = p << 2; + + // ###BEGIN###{DesktopRotation} + if (obj.rotation > 0) { + 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; } + else if (obj.rotation == 2) { pp = (obj.sparew * obj.spareh * 4) - 4 - pp; } + 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; } + } + // ###END###{DesktopRotation} + + obj.spare.data[pp] = v & 224; + obj.spare.data[pp + 1] = (v & 28) << 3; + obj.spare.data[pp + 2] = _fixColor((v & 3) << 6); + } + + // Set 16bit color RGB565 + function _setPixel16(v, p) { + var pp = p << 2; + + // ###BEGIN###{DesktopRotation} + if (obj.rotation > 0) { + 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; } + else if (obj.rotation == 2) { pp = (obj.sparew * obj.spareh * 4) - 4 - pp; } + 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; } + } + // ###END###{DesktopRotation} + + obj.spare.data[pp] = (v >> 8) & 248; + obj.spare.data[pp + 1] = (v >> 3) & 252; + obj.spare.data[pp + 2] = (v & 31) << 3; + } + + // Set a run of 8bit color RGB332 + function _setPixel8run(v, p, run) { + var pp = (p << 2), r = (v & 224), g = ((v & 28) << 3), b = (_fixColor((v & 3) << 6)); + while (--run >= 0) { obj.spare.data[pp] = r; obj.spare.data[pp + 1] = g; obj.spare.data[pp + 2] = b; pp += 4; } + } + + // Set a run of 16bit color RGB565 + function _setPixel16run(v, p, run) { + var pp = (p << 2), r = ((v >> 8) & 248), g = ((v >> 3) & 252), b = ((v & 31) << 3); + while (--run >= 0) { obj.spare.data[pp] = r; obj.spare.data[pp + 1] = g; obj.spare.data[pp + 2] = b; pp += 4; } + } + + // ###BEGIN###{DesktopRotation} + function _arotX(x, y) { + if (obj.rotation == 0) return x; + if (obj.rotation == 1) return obj.canvas.canvas.width - obj.sparew2 - y; + if (obj.rotation == 2) return obj.canvas.canvas.width - obj.sparew2 - x; + if (obj.rotation == 3) return y; + return 0; + } + + function _arotY(x, y) { + if (obj.rotation == 0) return y; + if (obj.rotation == 1) return x; + if (obj.rotation == 2) return obj.canvas.canvas.height - obj.spareh2 - y; + if (obj.rotation == 3) return obj.canvas.canvas.height - obj.spareh - x; + return 0; + } + + function _crotX(x, y) { + if (obj.rotation == 0) return x; + if (obj.rotation == 1) return y; + if (obj.rotation == 2) return obj.canvas.canvas.width - x; + if (obj.rotation == 3) return obj.canvas.canvas.height - y; + return 0; + } + + function _crotY(x, y) { + if (obj.rotation == 0) return y; + if (obj.rotation == 1) return obj.canvas.canvas.width - x; + if (obj.rotation == 2) return obj.canvas.canvas.height - y; + if (obj.rotation == 3) return x; + return 0; + } + + function _rotX(x, y) { + if (obj.rotation == 0) return x; + if (obj.rotation == 1) return x; + if (obj.rotation == 2) return x - obj.canvas.canvas.width; + if (obj.rotation == 3) return x - obj.canvas.canvas.height; + return 0; + } + + function _rotY(x, y) { + if (obj.rotation == 0) return y; + if (obj.rotation == 1) return y - obj.canvas.canvas.width; + if (obj.rotation == 2) return y - obj.canvas.canvas.height; + if (obj.rotation == 3) return y; + return 0; + } + + obj.tcanvas = null; + obj.setRotation = function (x) { + while (x < 0) { x += 4; } + var newrotation = x % 4; + //console.log('hard-rot: ' + newrotation); + // ###BEGIN###{DesktopInband} + if (obj.holding == true) { obj.rotation = newrotation; return; } + // ###END###{DesktopInband} + + if (newrotation == obj.rotation) return true; + var rw = obj.canvas.canvas.width; + var rh = obj.canvas.canvas.height; + if (obj.rotation == 1 || obj.rotation == 3) { rw = obj.canvas.canvas.height; rh = obj.canvas.canvas.width; } + + // Copy the canvas, put it back in the correct direction + if (obj.tcanvas == null) obj.tcanvas = document.createElement('canvas'); + var tcanvasctx = obj.tcanvas.getContext('2d'); + tcanvasctx.setTransform(1, 0, 0, 1, 0, 0); + tcanvasctx.canvas.width = rw; + tcanvasctx.canvas.height = rh; + tcanvasctx.rotate((obj.rotation * -90) * Math.PI / 180); + if (obj.rotation == 0) tcanvasctx.drawImage(obj.canvas.canvas, 0, 0); + if (obj.rotation == 1) tcanvasctx.drawImage(obj.canvas.canvas, -obj.canvas.canvas.width, 0); + if (obj.rotation == 2) tcanvasctx.drawImage(obj.canvas.canvas, -obj.canvas.canvas.width, -obj.canvas.canvas.height); + if (obj.rotation == 3) tcanvasctx.drawImage(obj.canvas.canvas, 0, -obj.canvas.canvas.height); + + // Change the size and orientation and copy the canvas back into the rotation + if (obj.rotation == 0 || obj.rotation == 2) { obj.canvas.canvas.height = rw; obj.canvas.canvas.width = rh; } + if (obj.rotation == 1 || obj.rotation == 3) { obj.canvas.canvas.height = rh; obj.canvas.canvas.width = rw; } + obj.canvas.setTransform(1, 0, 0, 1, 0, 0); + obj.canvas.rotate((newrotation * 90) * Math.PI / 180); + obj.rotation = newrotation; + obj.canvas.drawImage(obj.tcanvas, _rotX(0, 0), _rotY(0, 0)); + + obj.width = obj.canvas.canvas.width; + obj.height = obj.canvas.canvas.height; + if (obj.onScreenResize != null) obj.onScreenResize(obj, obj.width, obj.height, obj.CanvasId); + return true; + } + // ###END###{DesktopRotation} + + function _fixColor(c) { return (c > 127) ? (c + 32) : c; } + + function _SendRefresh() { + // ###BEGIN###{DesktopInband} + if (obj.holding == true) return; + // ###END###{DesktopInband} + // ###BEGIN###{DesktopFocus} + if (obj.focusmode > 0) { + // Request only pixels around the last mouse position + var df = obj.focusmode * 2; + 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 + obj.ox = obj.mx; + obj.oy = obj.my; + } else { + // ###END###{DesktopFocus} + // Request the entire screen + obj.send(String.fromCharCode(3, 1, 0, 0, 0, 0) + ShortToStr(obj.rwidth) + ShortToStr(obj.rheight)); // FramebufferUpdateRequest + // ###BEGIN###{DesktopFocus} + } + // ###END###{DesktopFocus} + } + + obj.Start = function () { + obj.state = 0; + obj.acc = null; + obj.ZRLEfirst = 1; + // ###BEGIN###{Inflate} + obj.inflate.inflateReset(); + // ###END###{Inflate} + // ###BEGIN###{DesktopInband} + obj.onKvmDataPending = []; + obj.onKvmDataAck = -1; + obj.kvmDataSupported = false; + // ###END###{DesktopInband} + for (var i in obj.sparecache) { delete obj.sparecache[i]; } + } + + obj.Stop = function () { obj.UnGrabMouseInput(); obj.UnGrabKeyInput(); if (obj.parent) { obj.parent.Stop(); } } + obj.send = function (x) { if (obj.parent) { obj.parent.send(x); } } + + var convertAmtKeyCodeTable = { + "Pause": 19, + "CapsLock": 20, + "Space": 32, + "Quote": 39, + "Minus": 45, + "NumpadMultiply": 42, + "NumpadAdd": 43, + "PrintScreen": 44, + "Comma": 44, + "NumpadSubtract": 45, + "NumpadDecimal": 46, + "Period": 46, + "Slash": 47, + "NumpadDivide": 47, + "Semicolon": 59, + "Equal": 61, + "OSLeft": 91, + "BracketLeft": 91, + "OSRight": 91, + "Backslash": 92, + "BracketRight": 93, + "ContextMenu": 93, + "Backquote": 96, + "NumLock": 144, + "ScrollLock": 145, + "Backspace": 0xff08, + "Tab": 0xff09, + "Enter": 0xff0d, + "NumpadEnter": 0xff0d, + "Escape": 0xff1b, + "Delete": 0xffff, + "Home": 0xff50, + "PageUp": 0xff55, + "PageDown": 0xff56, + "ArrowLeft": 0xff51, + "ArrowUp": 0xff52, + "ArrowRight": 0xff53, + "ArrowDown": 0xff54, + "End": 0xff57, + "Insert": 0xff63, + "F1": 0xffbe, + "F2": 0xffbf, + "F3": 0xffc0, + "F4": 0xffc1, + "F5": 0xffc2, + "F6": 0xffc3, + "F7": 0xffc4, + "F8": 0xffc5, + "F9": 0xffc6, + "F10": 0xffc7, + "F11": 0xffc8, + "F12": 0xffc9, + "ShiftLeft": 0xffe1, + "ShiftRight": 0xffe2, + "ControlLeft": 0xffe3, + "ControlRight": 0xffe4, + "AltLeft": 0xffe9, + "AltRight": 0xffea, + "MetaLeft": 0xffe7, + "MetaRight": 0xffe8 + } + function convertAmtKeyCode(e) { + if (e.code.startsWith('Key') && e.code.length == 4) { return e.code.charCodeAt(3) + ((e.shiftKey == false) ? 32 : 0); } + if (e.code.startsWith('Digit') && e.code.length == 6) { return e.code.charCodeAt(5); } + if (e.code.startsWith('Numpad') && e.code.length == 7) { return e.code.charCodeAt(6); } + return convertAmtKeyCodeTable[e.code]; + } + + /* + Intel AMT only recognizes a small subset of keysym characters defined in the keysymdef.h so you don’t need to + implement all the languages (this is taken care by the USB Scancode Extension in RFB4.0 protocol). + 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. + In addition to keysymdef.h symbols there are 6 japanese extra keys that we do support: + + #define XK_Intel_EU_102kbd_backslash_pipe_45 0x17170056 // European 102-key: 45 (backslash/pipe), usb Usage: 0x64 + #define XK_Intel_JP_106kbd_yen_pipe 0x1717007d // Japanese 106-key: 14 (Yen/pipe), usb Usage: 0x89 + #define XK_Intel_JP_106kbd_backslash_underbar 0x17170073 // Japanese 106-key: 56 (backslash/underbar), usb Usage: 0x87 + #define XK_Intel_JP_106kbd_NoConvert 0x1717007b // Japanese 106-key: 131 (NoConvert), usb Usage: 0x8b + #define XK_Intel_JP_106kbd_Convert 0x17170079 // Japanese 106-key: 132 (Convert), usb Usage: 0x8a + #define XK_Intel_JP_106kbd_Hirigana_Katakana 0x17170070 // Japanese 106-key: 133 (Hirigana/Katakana), usb Usage: 0x88 + */ + + function _keyevent(d, e) { + if (!e) { e = window.event; } + + if (e.code && (obj.localKeyMap == false)) { + // For new browsers, this mapping is keyboard language independent + var k = convertAmtKeyCode(e); + if (k != null) { obj.sendkey(k, d); } + } else { + // For older browsers, this mapping works best for EN-US keyboard + var k = e.keyCode, kk = k; + if (e.shiftKey == false && k >= 65 && k <= 90) kk = k + 32; + if (k >= 112 && k <= 124) kk = k + 0xFF4E; + if (k == 8) kk = 0xff08; // Backspace + if (k == 9) kk = 0xff09; // Tab + if (k == 13) kk = 0xff0d; // Return + if (k == 16) kk = 0xffe1; // Shift (Left) + if (k == 17) kk = 0xffe3; // Ctrl (Left) + if (k == 18) kk = 0xffe9; // Alt (Left) + if (k == 27) kk = 0xff1b; // ESC + if (k == 33) kk = 0xff55; // PageUp + if (k == 34) kk = 0xff56; // PageDown + if (k == 35) kk = 0xff57; // End + if (k == 36) kk = 0xff50; // Home + if (k == 37) kk = 0xff51; // Left + if (k == 38) kk = 0xff52; // Up + if (k == 39) kk = 0xff53; // Right + if (k == 40) kk = 0xff54; // Down + if (k == 45) kk = 0xff63; // Insert + if (k == 46) kk = 0xffff; // Delete + if (k >= 96 && k <= 105) kk = k - 48; // Key pad numbers + if (k == 106) kk = 42; // Pad * + if (k == 107) kk = 43; // Pad + + if (k == 109) kk = 45; // Pad - + if (k == 110) kk = 46; // Pad . + if (k == 111) kk = 47; // Pad / + if (k == 186) kk = 59; // ; + if (k == 187) kk = 61; // = + if (k == 188) kk = 44; // , + if (k == 189) kk = 45; // - + if (k == 190) kk = 46; // . + if (k == 191) kk = 47; // / + if (k == 192) kk = 96; // ` + if (k == 219) kk = 91; // [ + if (k == 220) kk = 92; // \ + if (k == 221) kk = 93; // ] + if (k == 222) kk = 39; // ' + //console.log('Key' + d + ": " + k + " = " + kk); + obj.sendkey(kk, d); + } + return obj.haltEvent(e); + } + + obj.sendkey = function (k, d) { + if (typeof k == 'object') { + var buf = ''; for (var i in k) { buf += (String.fromCharCode(4, k[i][1], 0, 0) + IntToStr(k[i][0])); } obj.send(buf); + } else { + obj.send(String.fromCharCode(4, d, 0, 0) + IntToStr(k)); + } + } + + function handleServerCutText(acc, accview) { + if (acc.byteLength < 8) return 0; + var len = accview.getUint32(4) + 8; + if (acc.byteLength < len) return 0; + // ###BEGIN###{DesktopInband} + if (obj.onKvmData != null) { + var d = arrToStr(acc.slice(8, len)); + if ((d.length >= 16) && (d.substring(0, 15) == '\0KvmDataChannel')) { + if (obj.kvmDataSupported == false) { obj.kvmDataSupported = true; console.log('KVM Data Channel Supported.'); } + if (((obj.onKvmDataAck == -1) && (d.length == 16)) || (d.charCodeAt(15) != 0)) { obj.onKvmDataAck = true; } + //if (urlvars && urlvars['kvmdatatrace']) { console.log('KVM-Recv(' + (d.length - 16) + '): ' + d.substring(16)); } + if (d.length >= 16) { obj.onKvmData(d.substring(16)); } // Event the data and ack + if ((obj.onKvmDataAck == true) && (obj.onKvmDataPending.length > 0)) { obj.sendKvmData(obj.onKvmDataPending.shift()); } // Send pending data + } + } + // ###END###{DesktopInband} + return len; + } + + // ###BEGIN###{DesktopInband} + obj.sendKvmData = function (x) { + if (obj.onKvmDataAck !== true) { + obj.onKvmDataPending.push(x); + } else { + //if (urlvars && urlvars['kvmdatatrace']) { console.log('KVM-Send(' + x.length + '): ' + x); } + x = '\0KvmDataChannel\0' + x; + obj.send(String.fromCharCode(6, 0, 0, 0) + IntToStr(x.length) + x); + obj.onKvmDataAck = false; + } + } + + // Send a HWKVM keep alive if it's not been sent in the last 5 seconds. + obj.sendKeepAlive = function () { + if (obj.lastKeepAlive < Date.now() - 5000) { obj.lastKeepAlive = Date.now(); obj.send(String.fromCharCode(6, 0, 0, 0) + IntToStr(16) + '\0KvmDataChannel\0'); } + } + // ###END###{DesktopInband} + + obj.SendCtrlAltDelMsg = function () { obj.sendcad(); } + 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 + + var _MouseInputGrab = false; + var _KeyInputGrab = false; + + obj.GrabMouseInput = function () { + if (_MouseInputGrab == true) return; + var c = obj.canvas.canvas; + c.onmouseup = obj.mouseup; + c.onmousedown = obj.mousedown; + c.onmousemove = obj.mousemove; + //if (navigator.userAgent.match(/mozilla/i)) c.DOMMouseScroll = obj.xxDOMMouseScroll; else c.onmousewheel = obj.xxMouseWheel; + _MouseInputGrab = true; + } + + obj.UnGrabMouseInput = function () { + if (_MouseInputGrab == false) return; + var c = obj.canvas.canvas; + c.onmousemove = null; + c.onmouseup = null; + c.onmousedown = null; + //if (navigator.userAgent.match(/mozilla/i)) c.DOMMouseScroll = null; else c.onmousewheel = null; + _MouseInputGrab = false; + } + + obj.GrabKeyInput = function () { + if (_KeyInputGrab == true) return; + document.onkeyup = obj.handleKeyUp; + document.onkeydown = obj.handleKeyDown; + document.onkeypress = obj.handleKeys; + _KeyInputGrab = true; + } + + obj.UnGrabKeyInput = function () { + if (_KeyInputGrab == false) return; + document.onkeyup = null; + document.onkeydown = null; + document.onkeypress = null; + _KeyInputGrab = false; + } + + obj.handleKeys = function (e) { return obj.haltEvent(e); } + obj.handleKeyUp = function (e) { return _keyevent(0, e); } + obj.handleKeyDown = function (e) { return _keyevent(1, e); } + obj.haltEvent = function (e) { if (e.preventDefault) e.preventDefault(); if (e.stopPropagation) e.stopPropagation(); return false; } + + // RFB "PointerEvent" and mouse handlers + obj.mousedblclick = function (e) { } + obj.mousedown = function (e) { + var b = e.button; + if (obj.SwapMouse) { if (b == 0) { b = 2; } else if (b == 2) { b = 0; } } + obj.buttonmask |= (1 << b); return obj.mousemove(e, 1); + } + obj.mouseup = function (e) { + var b = e.button; + if (obj.SwapMouse) { if (b == 0) { b = 2; } else if (b == 2) { b = 0; } } + obj.buttonmask &= (0xFFFF - (1 << b)); return obj.mousemove(e, 1); + } + obj.mousemove = function (e, force) { + if (obj.state < 4) return true; + var ScaleFactorHeight = (obj.canvas.canvas.height / Q(obj.canvasid).offsetHeight); + var ScaleFactorWidth = (obj.canvas.canvas.width / Q(obj.canvasid).offsetWidth); + var Offsets = obj.getPositionOfControl(Q(obj.canvasid)); + obj.mx = ((event.pageX - Offsets[0]) * ScaleFactorWidth); + obj.my = ((event.pageY - Offsets[1]) * ScaleFactorHeight); + if (event.addx) { obj.mx += event.addx; } + if (event.addy) { obj.my += event.addy; } + + // ###BEGIN###{DesktopRotation} + if ((obj.rotation == 1) || (obj.rotation == 3)) { + obj.mx = ((obj.mx * obj.rwidth) / obj.width); + obj.my = ((obj.my * obj.rheight) / obj.height); + } + if (obj.noMouseRotate != true) { + var mx2 = _crotX(obj.mx, obj.my); + obj.my = _crotY(obj.mx, obj.my); + obj.mx = mx2; + } + // ###END###{DesktopRotation} + + // This is the mouse motion nagle timer. Slow down the mouse motion event rate. + if (force == 1) { + obj.send(String.fromCharCode(5, obj.buttonmask) + ShortToStr(obj.mx) + ShortToStr(obj.my)); + if (obj.mNagleTimer != null) { clearTimeout(obj.mNagleTimer); obj.mNagleTimer = null; } + } else { + if (obj.mNagleTimer == null) { + obj.mNagleTimer = setTimeout(function () { + obj.send(String.fromCharCode(5, obj.buttonmask) + ShortToStr(obj.mx) + ShortToStr(obj.my)); + obj.mNagleTimer = null; + }, 50); + } + } + + // ###BEGIN###{DesktopFocus} + // Update focus area if we are in focus mode + QV('DeskFocus', obj.focusmode); + if (obj.focusmode != 0) { + var x = Math.min(obj.mx, obj.canvas.canvas.width - obj.focusmode), + y = Math.min(obj.my, obj.canvas.canvas.height - obj.focusmode), + df = obj.focusmode * 2, + c = Q(obj.canvasid), + qx = c.offsetHeight / obj.canvas.canvas.height, + qy = c.offsetWidth / obj.canvas.canvas.width, + q = QS('DeskFocus'), + ppos = obj.getPositionOfControl(Q(obj.canvasid).parentElement); + q.left = (Math.max(((x - obj.focusmode) * qx), 0) + (pos[0] - ppos[0])) + 'px'; + q.top = (Math.max(((y - obj.focusmode) * qy), 0) + (pos[1] - ppos[1])) + 'px'; + q.width = ((df * qx) - 6) + 'px'; + q.height = ((df * qx) - 6) + 'px'; + } + // ###END###{DesktopFocus} + + return obj.haltEvent(e); + } + + obj.getPositionOfControl = function (Control) { + var Position = Array(2); + Position[0] = Position[1] = 0; + while (Control) { + Position[0] += Control.offsetLeft; + Position[1] += Control.offsetTop; + Control = Control.offsetParent; + } + return Position; + } + + return obj; +} diff --git a/public/scripts/amt-desktop-0.0.2.js b/public/scripts/amt-desktop-0.0.2.js index 3bc6fe39..b14f5352 100644 --- a/public/scripts/amt-desktop-0.0.2.js +++ b/public/scripts/amt-desktop-0.0.2.js @@ -21,7 +21,7 @@ var CreateAmtRemoteDesktop = function (divid, scrolldiv) { obj.rwidth = 0; obj.rheight = 0; obj.bpp = 2; // Bytes per pixel (1 or 2 supported) - obj.useZRLE = true; + obj.useRLE = true; obj.showmouse = true; obj.buttonmask = 0; obj.localKeyMap = true; @@ -31,9 +31,7 @@ var CreateAmtRemoteDesktop = function (divid, scrolldiv) { obj.sparew2 = 0; obj.spareh2 = 0; obj.sparecache = {}; - obj.ZRLEfirst = 1; obj.onScreenSizeChange = null; - //obj.onPreDrawImage = null; obj.frameRateDelay = 0; // ###BEGIN###{DesktopRotation} obj.noMouseRotate = false; @@ -46,8 +44,13 @@ var CreateAmtRemoteDesktop = function (divid, scrolldiv) { obj.onKvmDataAck = -1; obj.holding = false; obj.lastKeepAlive = Date.now(); + obj.kvmExt = {}; + obj.kvmExtChanged = null; + obj.useZLib = false; + obj.decimationMode = 0; // 0 = Don't set, 1 = Disable, 2 = Automatic, 3 = Enabled + obj.graymode = false; + obj.lowcolor = false; // ###END###{DesktopInband} - obj.SwapMouse = false; obj.mNagleTimer = null; // Mouse motion slowdown timer obj.mx = 0; // Last mouse x position @@ -77,6 +80,11 @@ var CreateAmtRemoteDesktop = function (divid, scrolldiv) { 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 } obj.ProcessBinaryData = function (data) { + // ###BEGIN###{DesktopRecorder} + // Record the data if needed + if ((obj.recordedData != null) && (obj.recordedHolding !== true)) { obj.recordedData.push(recordingEntry(2, 1, String.fromCharCode.apply(null, new Uint8Array(data)))); } + // ###END###{DesktopRecorder} + // Append to accumulator if (obj.acc == null) { obj.acc = new Uint8Array(data); @@ -93,16 +101,17 @@ var CreateAmtRemoteDesktop = function (divid, scrolldiv) { if ((obj.state == 0) && (obj.acc.byteLength >= 12)) { // Getting handshake & version cmdsize = 12; - //if (obj.acc.substring(0, 4) != "RFB ") { return obj.Stop(); } + //if (obj.acc.substring(0, 4) != 'RFB ') { return obj.Stop(); } //var version = parseFloat(obj.acc.substring(4, 11)); - //console.log("KVersion: " + version); + //console.log('KVersion: ' + version); obj.state = 1; + if (obj.parent) { delete obj.parent.connectTime; } obj.send('RFB 003.008\n'); } else if ((obj.state == 1) && (obj.acc.byteLength >= 1)) { // Getting security options cmdsize = obj.acc[0] + 1; - 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. + 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. obj.state = 2; } else if ((obj.state == 2) && (obj.acc.byteLength >= 4)) { @@ -111,6 +120,7 @@ var CreateAmtRemoteDesktop = function (divid, scrolldiv) { if (accview.getUint32(0) != 0) { return obj.Stop(); } obj.send(String.fromCharCode(1)); // Send share desktop flag obj.state = 3; + if (obj.parent) { obj.parent.disconnectCode = 50000; } // If Intel AMT disconnects at exactly this moment, indicates we need RLE8 or unsupported GPU. } else if ((obj.state == 3) && (obj.acc.byteLength >= 24)) { // Getting server init @@ -122,6 +132,11 @@ var CreateAmtRemoteDesktop = function (divid, scrolldiv) { cmdsize = 24 + namelen; obj.canvas.canvas.width = obj.rwidth = obj.width = obj.ScreenWidth = accview.getUint16(0); obj.canvas.canvas.height = obj.rheight = obj.height = obj.ScreenHeight = accview.getUint16(2); + //console.log('Initial Desktop width: ' + obj.width + ', height: ' + obj.height); + + // ###BEGIN###{DesktopRecorder} + obj.DeskRecordServerInit = String.fromCharCode.apply(null, new Uint8Array(obj.acc.buffer.slice(0, 24 + namelen))); + // ###END###{DesktopRecorder} // 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. // Makes the javascript code smaller and maybe a bit faster. @@ -137,21 +152,21 @@ var CreateAmtRemoteDesktop = function (divid, scrolldiv) { obj.gsh = obj.acc[15]; obj.bsh = obj.acc[16]; var name = obj.acc.substring(24, 24 + namelen); - console.log("name: " + name); - console.log("width: " + obj.width + ", height: " + obj.height); - console.log("bits-per-pixel: " + obj.xbpp); - console.log("depth: " + obj.depth); - console.log("big-endian-flag: " + obj.bigend); - console.log("true-colour-flag: " + obj.truecolor); - console.log("rgb max: " + obj.rmax + "," + obj.gmax + "," + obj.bmax); - console.log("rgb shift: " + obj.rsh + "," + obj.gsh + "," + obj.bsh); + console.log('name: ' + name); + console.log('width: ' + obj.width + ', height: ' + obj.height); + console.log('bits-per-pixel: ' + obj.xbpp); + console.log('depth: ' + obj.depth); + console.log('big-endian-flag: ' + obj.bigend); + console.log('true-colour-flag: ' + obj.truecolor); + console.log('rgb max: ' + obj.rmax + ',' + obj.gmax + ',' + obj.bmax); + console.log('rgb shift: ' + obj.rsh + ',' + obj.gsh + ',' + obj.bsh); */ // SetEncodings, with AMT we can't omit RAW, must be specified. - // Intel AMT supports encodings: RAW (0), ZRLE (16), Desktop Size (0xFFFFFF21, -223) + // Intel AMT supports encodings: RAW (0), RLE (16), Desktop Size (0xFFFFFF21, -223) var supportedEncodings = ''; - if (obj.useZRLE) supportedEncodings += IntToStr(16); + if (obj.useRLE) supportedEncodings += IntToStr(16); supportedEncodings += IntToStr(0); // ###BEGIN###{DesktopInband} supportedEncodings += IntToStr(1092); @@ -159,20 +174,47 @@ var CreateAmtRemoteDesktop = function (divid, scrolldiv) { obj.send(String.fromCharCode(2, 0) + ShortToStr((supportedEncodings.length / 4) + 1) + supportedEncodings + IntToStr(-223)); // Supported Encodings + Desktop Size - // Set the pixel encoding to something much smaller - // 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) - 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 + if (obj.graymode == false) { + // Set the pixel encoding to something much smaller + // 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) + 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 + } else { + // Gray scale modes + if (obj.bpp == 2) { obj.bpp = 1; } + if (obj.lowcolor == false) { + 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 + } else { + 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 + } + } obj.state = 4; - if (obj.parent) { obj.parent.xxStateChange(3); } - _SendRefresh(); + if (obj.parent) { + obj.parent.connectTime = Date.now(); + obj.parent.disconnectCode = 0; + obj.parent.xxStateChange(3); + } //obj.timer = setInterval(obj.xxOnTimer, 50); // ###BEGIN###{DesktopFocus} obj.ox = -1; // Old mouse x position // ###END###{DesktopFocus} + // ###BEGIN###{DesktopInband} + if (obj.kvmExtChanged != null) { + if (obj.decimationMode > 0) { obj.sendKvmExtCmd(2, obj.decimationMode); } // Set Decimation Mode (0 = Do not set, 1 = Disable, 2 = Auto, 3 = Enable) + obj.sendKvmExtCmd(4, (obj.useZLib === true) ? 1 : 0); // Set ZLib state (0 = Disabled, 1 = Enabled) + } + // ###END###{DesktopInband} + _SendRefresh(); if (obj.onScreenSizeChange != null) { obj.onScreenSizeChange(obj, obj.ScreenWidth, obj.ScreenHeight); } + if (obj.parent) { + obj.parent.disconnectCode = 50001; // Everything looks good, a disconnection here would be Intel AMT initiated. + + // Check if the screen size is larger than Intel AMT should be able to handle + //console.log('KVM Buffer Size: ' + (obj.bpp * obj.width * obj.height)); + if ((obj.bpp * obj.width * obj.height) > 9216000) { obj.parent.disconnectCode = 50002; } // Display buffer too large. + } } else if (obj.state == 4) { switch (obj.acc[0]) { @@ -180,7 +222,12 @@ var CreateAmtRemoteDesktop = function (divid, scrolldiv) { if (obj.acc.byteLength < 4) return; 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. cmdsize = 4; - if (obj.onPreDrawImage != null) obj.onPreDrawImage(); // Notify that we are about to draw on the canvas. + + // ###BEGIN###{DesktopRecorder} + // This is the start of a new frame, start recording now if needed. + if (obj.recordedHolding === true) { delete obj.recordedHolding; obj.recordedData.push(recordingEntry(2, 1, String.fromCharCode.apply(null, obj.acc))); } + // ###END###{DesktopRecorder} + break; case 2: // This is the bell, do nothing. cmdsize = 1; @@ -202,7 +249,7 @@ var CreateAmtRemoteDesktop = function (divid, scrolldiv) { encoding = accview.getUint32(8); if (encoding < 17) { - if ((width < 1) || (width > 64) || (height < 1) || (height > 64)) { console.log("Invalid tile size (" + width + "," + height + "), disconnecting."); return obj.Stop(); } + if ((width < 1) || (width > 64) || (height < 1) || (height > 64)) { console.log('Invalid tile size (' + width + ',' + height + '), disconnecting.'); return obj.Stop(); } // 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. if ((obj.sparew != width) || (obj.spareh != height)) { @@ -228,7 +275,11 @@ var CreateAmtRemoteDesktop = function (divid, scrolldiv) { obj.send(String.fromCharCode(3, 0, 0, 0, 0, 0) + ShortToStr(obj.width) + ShortToStr(obj.height)); // FramebufferUpdateRequest cmdsize = 12; if (obj.onScreenSizeChange != null) { obj.onScreenSizeChange(obj, obj.ScreenWidth, obj.ScreenHeight); } - //console.log("New desktop width: " + obj.width + ", height: " + obj.height); + //console.log('Desktop width: ' + obj.width + ', height: ' + obj.height); + + // Check if the screen size is larger than Intel AMT should be able to handle + //console.log('KVM Buffer Size: ' + (obj.bpp * obj.width * obj.height)); + if ((obj.parent) && ((obj.bpp * obj.width * obj.height) > 9216000)) { obj.parent.disconnectCode = 50002; } // Display buffer too large. } else if (encoding == 0) { // RAW encoding var ptr = 12, cs = 12 + (s * obj.bpp); @@ -243,7 +294,7 @@ var CreateAmtRemoteDesktop = function (divid, scrolldiv) { } _putImage(obj.spare, x, y); } else if (encoding == 16) { - // ZRLE encoding + // RLE encoding if (obj.acc.byteLength < 16) return; var datalen = accview.getUint32(12); if (obj.acc.byteLength < (16 + datalen)) return; @@ -259,7 +310,7 @@ var CreateAmtRemoteDesktop = function (divid, scrolldiv) { else { // This is compressed ZLib data, decompress and process it. (TODO: This need to be optimized, remove str/arr conversions) var str = obj.inflate.inflate(arrToStr(new Uint8Array(obj.acc.buffer.slice(ptr, ptr + datalen - dx)))); - if (str.length > 0) { _decodeLRE(strToArr(str), 0, x, y, width, height, s, str.length); } else { console.log("Invalid deflate data"); } + if (str.length > 0) { _decodeLRE(strToArr(str), 0, x, y, width, height, s, str.length); } else { console.log('Invalid deflate data'); } } // ###END###{Inflate} @@ -294,8 +345,14 @@ var CreateAmtRemoteDesktop = function (divid, scrolldiv) { } else if (subencoding == 1) { // Solid color tile - v = data[ptr++] + ((obj.bpp == 2) ? (data[ptr++] << 8) : 0); - 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))) + ')'; + if (obj.graymode) { + v = data[ptr++]; + if (obj.lowcolor) { v = v << 4; } + obj.canvas.fillStyle = 'rgb(' + v + ',' + v + ',' + v + ')'; + } else { + v = data[ptr++] + ((obj.bpp == 2) ? (data[ptr++] << 8) : 0); + 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))) + ')'; + } // ###BEGIN###{DesktopRotation} var xx = _rotX(x, y); @@ -401,7 +458,7 @@ var CreateAmtRemoteDesktop = function (divid, scrolldiv) { obj.canvas.canvas.width = obj.width; obj.canvas.canvas.height = obj.height; if (obj.onScreenSizeChange != null) { obj.onScreenSizeChange(obj, obj.ScreenWidth, obj.ScreenHeight); } // ??? } - obj.Send(String.fromCharCode(3, 0, 0, 0, 0, 0) + ShortToStr(obj.width) + ShortToStr(obj.height)); // FramebufferUpdateRequest + obj.send(String.fromCharCode(3, 0, 0, 0, 0, 0) + ShortToStr(obj.width) + ShortToStr(obj.height)); // FramebufferUpdateRequest } else { obj.UnGrabMouseInput(); obj.UnGrabKeyInput(); @@ -433,9 +490,14 @@ var CreateAmtRemoteDesktop = function (divid, scrolldiv) { } // ###END###{DesktopRotation} - obj.spare.data[pp] = v & 224; - obj.spare.data[pp + 1] = (v & 28) << 3; - obj.spare.data[pp + 2] = _fixColor((v & 3) << 6); + if (obj.graymode) { + if (obj.lowcolor) { v = v << 4; } + obj.spare.data[pp] = obj.spare.data[pp + 1] = obj.spare.data[pp + 2] = v; + } else { + obj.spare.data[pp] = v & 224; + obj.spare.data[pp + 1] = (v & 28) << 3; + obj.spare.data[pp + 2] = _fixColor((v & 3) << 6); + } } // Set 16bit color RGB565 @@ -457,8 +519,14 @@ var CreateAmtRemoteDesktop = function (divid, scrolldiv) { // Set a run of 8bit color RGB332 function _setPixel8run(v, p, run) { - var pp = (p << 2), r = (v & 224), g = ((v & 28) << 3), b = (_fixColor((v & 3) << 6)); - while (--run >= 0) { obj.spare.data[pp] = r; obj.spare.data[pp + 1] = g; obj.spare.data[pp + 2] = b; pp += 4; } + if (obj.graymode) { + var pp = (p << 2); + if (obj.lowcolor) { v = v << 4; } + while (--run >= 0) { obj.spare.data[pp] = obj.spare.data[pp + 1] = obj.spare.data[pp + 2] = v; pp += 4; } + } else { + var pp = (p << 2), r = (v & 224), g = ((v & 28) << 3), b = (_fixColor((v & 3) << 6)); + while (--run >= 0) { obj.spare.data[pp] = r; obj.spare.data[pp + 1] = g; obj.spare.data[pp + 2] = b; pp += 4; } + } } // Set a run of 16bit color RGB565 @@ -582,7 +650,6 @@ var CreateAmtRemoteDesktop = function (divid, scrolldiv) { obj.Start = function () { obj.state = 0; obj.acc = null; - obj.ZRLEfirst = 1; // ###BEGIN###{Inflate} obj.inflate.inflateReset(); // ###END###{Inflate} @@ -590,6 +657,7 @@ var CreateAmtRemoteDesktop = function (divid, scrolldiv) { obj.onKvmDataPending = []; obj.onKvmDataAck = -1; obj.kvmDataSupported = false; + obj.kvmExt = {}; // ###END###{DesktopInband} for (var i in obj.sparecache) { delete obj.sparecache[i]; } } @@ -598,66 +666,66 @@ var CreateAmtRemoteDesktop = function (divid, scrolldiv) { obj.send = function (x) { if (obj.parent) { obj.parent.send(x); } } var convertAmtKeyCodeTable = { - "Pause": 19, - "CapsLock": 20, - "Space": 32, - "Quote": 39, - "Minus": 45, - "NumpadMultiply": 42, - "NumpadAdd": 43, - "PrintScreen": 44, - "Comma": 44, - "NumpadSubtract": 45, - "NumpadDecimal": 46, - "Period": 46, - "Slash": 47, - "NumpadDivide": 47, - "Semicolon": 59, - "Equal": 61, - "OSLeft": 91, - "BracketLeft": 91, - "OSRight": 91, - "Backslash": 92, - "BracketRight": 93, - "ContextMenu": 93, - "Backquote": 96, - "NumLock": 144, - "ScrollLock": 145, - "Backspace": 0xff08, - "Tab": 0xff09, - "Enter": 0xff0d, - "NumpadEnter": 0xff0d, - "Escape": 0xff1b, - "Delete": 0xffff, - "Home": 0xff50, - "PageUp": 0xff55, - "PageDown": 0xff56, - "ArrowLeft": 0xff51, - "ArrowUp": 0xff52, - "ArrowRight": 0xff53, - "ArrowDown": 0xff54, - "End": 0xff57, - "Insert": 0xff63, - "F1": 0xffbe, - "F2": 0xffbf, - "F3": 0xffc0, - "F4": 0xffc1, - "F5": 0xffc2, - "F6": 0xffc3, - "F7": 0xffc4, - "F8": 0xffc5, - "F9": 0xffc6, - "F10": 0xffc7, - "F11": 0xffc8, - "F12": 0xffc9, - "ShiftLeft": 0xffe1, - "ShiftRight": 0xffe2, - "ControlLeft": 0xffe3, - "ControlRight": 0xffe4, - "AltLeft": 0xffe9, - "AltRight": 0xffea, - "MetaLeft": 0xffe7, - "MetaRight": 0xffe8 + 'Pause': 19, + 'CapsLock': 20, + 'Space': 32, + 'Quote': 39, + 'Minus': 45, + 'NumpadMultiply': 42, + 'NumpadAdd': 43, + 'PrintScreen': 44, + 'Comma': 44, + 'NumpadSubtract': 45, + 'NumpadDecimal': 46, + 'Period': 46, + 'Slash': 47, + 'NumpadDivide': 47, + 'Semicolon': 59, + 'Equal': 61, + 'OSLeft': 91, + 'BracketLeft': 91, + 'OSRight': 91, + 'Backslash': 92, + 'BracketRight': 93, + 'ContextMenu': 93, + 'Backquote': 96, + 'NumLock': 144, + 'ScrollLock': 145, + 'Backspace': 0xff08, + 'Tab': 0xff09, + 'Enter': 0xff0d, + 'NumpadEnter': 0xff0d, + 'Escape': 0xff1b, + 'Delete': 0xffff, + 'Home': 0xff50, + 'PageUp': 0xff55, + 'PageDown': 0xff56, + 'ArrowLeft': 0xff51, + 'ArrowUp': 0xff52, + 'ArrowRight': 0xff53, + 'ArrowDown': 0xff54, + 'End': 0xff57, + 'Insert': 0xff63, + 'F1': 0xffbe, + 'F2': 0xffbf, + 'F3': 0xffc0, + 'F4': 0xffc1, + 'F5': 0xffc2, + 'F6': 0xffc3, + 'F7': 0xffc4, + 'F8': 0xffc5, + 'F9': 0xffc6, + 'F10': 0xffc7, + 'F11': 0xffc8, + 'F12': 0xffc9, + 'ShiftLeft': 0xffe1, + 'ShiftRight': 0xffe2, + 'ControlLeft': 0xffe3, + 'ControlRight': 0xffe4, + 'AltLeft': 0xffe9, + 'AltRight': 0xffea, + 'MetaLeft': 0xffe7, + 'MetaRight': 0xffe8 } function convertAmtKeyCode(e) { if (e.code.startsWith('Key') && e.code.length == 4) { return e.code.charCodeAt(3) + ((e.shiftKey == false) ? 32 : 0); } @@ -726,7 +794,7 @@ var CreateAmtRemoteDesktop = function (divid, scrolldiv) { if (k == 220) kk = 92; // \ if (k == 221) kk = 93; // ] if (k == 222) kk = 39; // ' - //console.log('Key' + d + ": " + k + " = " + kk); + //console.log('Key' + d + ': ' + k + ' = ' + kk); obj.sendkey(kk, d); } return obj.haltEvent(e); @@ -746,13 +814,27 @@ var CreateAmtRemoteDesktop = function (divid, scrolldiv) { if (acc.byteLength < len) return 0; // ###BEGIN###{DesktopInband} if (obj.onKvmData != null) { - var d = arrToStr(acc.slice(8, len)); + var d = arrToStr(new Uint8Array(acc.buffer.slice(8, len))); if ((d.length >= 16) && (d.substring(0, 15) == '\0KvmDataChannel')) { - if (obj.kvmDataSupported == false) { obj.kvmDataSupported = true; console.log('KVM Data Channel Supported.'); } + if (obj.kvmDataSupported == false) { obj.kvmDataSupported = true; /*console.log('KVM Data Channel Supported.');*/ } if (((obj.onKvmDataAck == -1) && (d.length == 16)) || (d.charCodeAt(15) != 0)) { obj.onKvmDataAck = true; } - //if (urlvars && urlvars['kvmdatatrace']) { console.log('KVM-Recv(' + (d.length - 16) + '): ' + d.substring(16)); } + if (urlvars && urlvars['kvmdatatrace']) { console.log('KVM-DataChannel-Recv(' + (d.length - 16) + '): ' + d.substring(16)); } if (d.length >= 16) { obj.onKvmData(d.substring(16)); } // Event the data and ack if ((obj.onKvmDataAck == true) && (obj.onKvmDataPending.length > 0)) { obj.sendKvmData(obj.onKvmDataPending.shift()); } // Send pending data + } else if ((d.length >= 13) && (d.substring(0, 11) == '\0KvmExtCmd\0')) { + var cmd = d.charCodeAt(11), val = d.charCodeAt(12); + //console.log('Received KvmExtCmd', cmd, val, d.length); + if (cmd == 1) { + obj.kvmExt.decimationMode = val; + if (d.length > 13) { obj.kvmExt.decimationState = d.charCodeAt(13); } + if (obj.kvmExtChanged != null) { obj.kvmExtChanged(1, obj.kvmExt, obj.kvmExt); } + } + if (cmd == 2) { obj.sendKvmExtCmd(1); } + if (cmd == 3) { obj.kvmExt.compression = val; if (obj.kvmExtChanged != null) { obj.kvmExtChanged(3, obj.kvmExt); } } + if (cmd == 4) { obj.sendKvmExtCmd(3); } + } else { + console.log('Got KVM clipboard data:', d); + if (urlvars && urlvars['kvmdatatrace']) { console.log('KVM-ClipBoard-Recv(' + d.length + '): ' + rstr2hex(d) + ', ' + d); } } } // ###END###{DesktopInband} @@ -760,11 +842,17 @@ var CreateAmtRemoteDesktop = function (divid, scrolldiv) { } // ###BEGIN###{DesktopInband} + obj.sendKvmExtCmd = function (cmd, val) { + //console.log('Sending KvmExtCmd', cmd, val); + var x = '\0KvmExtCmd\0' + String.fromCharCode(cmd) + (val != null ? String.fromCharCode(val) : ''); + obj.send(String.fromCharCode(6, 0, 0, 0) + IntToStr(x.length) + x); + } + obj.sendKvmData = function (x) { if (obj.onKvmDataAck !== true) { obj.onKvmDataPending.push(x); } else { - //if (urlvars && urlvars['kvmdatatrace']) { console.log('KVM-Send(' + x.length + '): ' + x); } + if (urlvars && urlvars['kvmdatatrace']) { console.log('KVM-DataChannel-Send(' + x.length + '): ' + x); } x = '\0KvmDataChannel\0' + x; obj.send(String.fromCharCode(6, 0, 0, 0) + IntToStr(x.length) + x); obj.onKvmDataAck = false; @@ -777,6 +865,13 @@ var CreateAmtRemoteDesktop = function (divid, scrolldiv) { } // ###END###{DesktopInband} + // ###BEGIN###{DesktopClipboard} + obj.sendClipboardData = function (x) { + if (urlvars && urlvars['kvmdatatrace']) { console.log('KVM-ClipBoard-Send(' + x.length + '): ' + rstr2hex(x) + ', ' + x); } + obj.send(String.fromCharCode(6, 0, 0, 0) + IntToStr(x.length) + x); + } + // ###END###{DesktopClipboard} + obj.SendCtrlAltDelMsg = function () { obj.sendcad(); } 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 @@ -824,18 +919,10 @@ var CreateAmtRemoteDesktop = function (divid, scrolldiv) { obj.handleKeyDown = function (e) { return _keyevent(1, e); } obj.haltEvent = function (e) { if (e.preventDefault) e.preventDefault(); if (e.stopPropagation) e.stopPropagation(); return false; } - // RFB "PointerEvent" and mouse handlers + // RFB 'PointerEvent' and mouse handlers obj.mousedblclick = function (e) { } - obj.mousedown = function (e) { - var b = e.button; - if (obj.SwapMouse) { if (b == 0) { b = 2; } else if (b == 2) { b = 0; } } - obj.buttonmask |= (1 << b); return obj.mousemove(e, 1); - } - obj.mouseup = function (e) { - var b = e.button; - if (obj.SwapMouse) { if (b == 0) { b = 2; } else if (b == 2) { b = 0; } } - obj.buttonmask &= (0xFFFF - (1 << b)); return obj.mousemove(e, 1); - } + obj.mousedown = function (e) { obj.buttonmask |= (1 << e.button); return obj.mousemove(e, 1); } + obj.mouseup = function (e) { obj.buttonmask &= (0xFFFF - (1 << e.button)); return obj.mousemove(e, 1); } obj.mousemove = function (e, force) { if (obj.state < 4) return true; var ScaleFactorHeight = (obj.canvas.canvas.height / Q(obj.canvasid).offsetHeight); @@ -904,5 +991,44 @@ var CreateAmtRemoteDesktop = function (divid, scrolldiv) { return Position; } + // ###BEGIN###{DesktopRecorder} + obj.StartRecording = function () { + if ((obj.recordedData != null) && (obj.DeskRecordServerInit != null)) return false; + obj.recordedHolding = true; + obj.recordedData = []; + obj.recordedStart = Date.now(); + obj.recordedSize = 0; + obj.recordedData.push(recordingEntry(1, 0, JSON.stringify({ magic: 'MeshCentralRelaySession', ver: 1, time: new Date().toLocaleString(), protocol: 200, bpp: obj.bpp, graymode: obj.graymode, lowcolor: obj.lowcolor, screenSize: [obj.width, obj.height] }))); // Metadata, 200 = Midstream Intel AMT KVM + obj.DeskRecordServerInit = String.fromCharCode((obj.width >> 8), (obj.width & 0xFF), (obj.height >> 8), (obj.height & 0xFF)) + obj.DeskRecordServerInit.substring(4); + obj.recordedData.push(recordingEntry(2, 1, obj.DeskRecordServerInit)); // This is the server init command + obj.recordedData.push(recordingEntry(3, 0, atob(obj.CanvasId.toDataURL('image/png').split(',')[1]))); // Take a screen shot + return true; + } + + obj.StopRecording = function () { + if (obj.recordedData == null) return; + var r = obj.recordedData; + r.push(recordingEntry(3, 0, 'MeshCentralMCREC')); + delete obj.recordedData; + delete obj.recordedStart; + delete obj.recordedSize; + return r; + } + + function recordingEntry(type, flags, data) { + //console.log('recordingEntry', type, flags, (typeof data == 'number')?data:data.length); + // Header: Type (2) + Flags (2) + Size(4) + Time(8) + // Type (1 = Header, 2 = Network Data), Flags (1 = Binary, 2 = User), Size (4 bytes), Time (8 bytes) + var now = Date.now(); + if (typeof data == 'number') { + obj.recordedSize += data; + return ShortToStr(type) + ShortToStr(flags) + IntToStr(data) + IntToStr(now >> 32) + IntToStr(now & 32); + } else { + obj.recordedSize += data.length; + return ShortToStr(type) + ShortToStr(flags) + IntToStr(data.length) + IntToStr(now >> 32) + IntToStr(now & 32) + data; + } + } + // ###END###{DesktopRecorder} + return obj; } diff --git a/views/player.handlebars b/views/player.handlebars index bf64fb8a..e7af6490 100644 --- a/views/player.handlebars +++ b/views/player.handlebars @@ -405,8 +405,11 @@ else if (p == 200) { p = "Intel® AMT KVM"; } x += addInfoNoEsc("Protocol", p); } - if (recFileMetadata.bpp == 1) { x += addInfoNoEsc("Color Quality", "256 colors"); } - if (recFileMetadata.bpp == 2) { x += addInfoNoEsc("Color Quality", "65536 colors"); } + var encQualityStr = "2 byte-per-pixel"; + if (recFileMetadata.bpp == 1) { encQualityStr = "1 byte-per-pixel" } + if (recFileMetadata.graymode) { if (recFileMetadata.lowcolor) { encQualityStr += ", 16 grays"; } else { encQualityStr += ", 256 grays"; } } + x += addInfoNoEsc("Encoding Quality", encQualityStr); + if (recFileMetadata.indexInterval) { recFileIndexBasePtr = recFilePtr; x += addInfoNoEsc("Seeking", format("Indexed every {0} seconds", recFileMetadata.indexInterval)); @@ -457,7 +460,10 @@ amtDesktop = CreateAmtRemoteDesktop('Desk'); amtDesktop.onScreenSizeChange = deskAdjust; amtDesktop.onPreDrawImage = preCanvasDraw; + if (recFileMetadata.screenSize) { amtDesktop.width = recFileMetadata.screenSize[0]; amtDesktop.height = recFileMetadata.screenSize[1]; } if (recFileMetadata.bpp) { amtDesktop.bpp = recFileMetadata.bpp; } + if (recFileMetadata.graymode) { amtDesktop.graymode = recFileMetadata.graymode; } + if (recFileMetadata.lowcolor) { amtDesktop.lowcolor = recFileMetadata.lowcolor; } amtDesktop.State = 3; amtDesktop.Start(); deskAdjust(); @@ -475,7 +481,10 @@ amtDesktop.onPreDrawImage = preCanvasDraw; amtDesktop.State = 3; amtDesktop.Start(); + if (recFileMetadata.screenSize) { amtDesktop.width = recFileMetadata.screenSize[0]; amtDesktop.height = recFileMetadata.screenSize[1]; } if (recFileMetadata.bpp) { amtDesktop.bpp = recFileMetadata.bpp; } + if (recFileMetadata.graymode) { amtDesktop.graymode = recFileMetadata.graymode; } + if (recFileMetadata.lowcolor) { amtDesktop.lowcolor = recFileMetadata.lowcolor; } amtDesktop.state = 3; deskAdjust(); QV('ConvertAsWebM', browser == 'chrome'); // Only show the "Convert to WebM button when in Chrome @@ -583,7 +592,9 @@ // User --> Device data if (recFileProtocol == 101) { // Intel AMT KVM - if (rstr2hex(data) == '0000000008080001000700070003050200000000') { amtDesktop.bpp = 1; } // Switch to 1 byte per pixel. + if (rstr2hex(data) == '0000000008080001000700070003050200000000') { console.log('RGB8'); amtDesktop.bpp = 1; } // Switch to 1 byte per pixel, 256 colors + if (rstr2hex(data) == '000000000808000100FF00000000000000000000') { console.log('GRAY8'); amtDesktop.bpp = 1; obj.graymode = true; } // Switch to 1 byte per pixel, 256 grays + if (rstr2hex(data) == '0000000008040001000F00000000000000000000') { console.log('GRAY4'); amtDesktop.bpp = 1; obj.graymode = true; obj.lowcolor = true; } // Switch to 1 byte per pixel, 16 grays } } @@ -824,7 +835,13 @@ amtDesktop.onScreenSizeChange = deskAdjust; amtDesktop.State = 3; amtDesktop.Start(); - if (recFileMetadata.protocol == 200) { if (recFileMetadata.bpp) { amtDesktop.bpp = recFileMetadata.bpp; } amtDesktop.state = 3; } + if (recFileMetadata.protocol == 200) { + if (recFileMetadata.screenSize) { amtDesktop.width = recFileMetadata.screenSize[0]; amtDesktop.height = recFileMetadata.screenSize[1]; } + if (recFileMetadata.bpp) { amtDesktop.bpp = recFileMetadata.bpp; } + if (recFileMetadata.graymode) { amtDesktop.graymode = recFileMetadata.graymode; } + if (recFileMetadata.lowcolor) { amtDesktop.lowcolor = recFileMetadata.lowcolor; } + amtDesktop.state = 3; + } } else if (agentTerminal) { agentTerminal = null; }