/** BSD-2-Clause license * * Copyright (c) 2018-2023 wsgate devs, NST , sss . * */ var webrdp = webrdp || {} webrdp.hasconsole = (typeof console !== 'undefined' && 'debug' in console && 'info' in console && 'warn' in console && 'error' in console); webrdp.WSrunner = new Class( { Implements: Events, initialize: function(url) { this.url = url; this.log = new webrdp.Log(); }, Run: function() { try { this.sock = new WebSocket(this.url); } catch (err) { } this.sock.binaryType = 'arraybuffer'; this.sock.onopen = this.onWSopen.bind(this); this.sock.onclose = this.onWSclose.bind(this); this.sock.onmessage = this.onWSmsg.bind(this); this.sock.onerror = this.onWSerr.bind(this); this.sock.sendd = this.sock.send; this.sock.send = (data) => { this.log.debug('wsout', data); this.sock.sendd(data); } } }); webrdp.RDP = new Class( { Extends: webrdp.WSrunner, initialize: function(url, canvas, controls, cssCursor, useTouch, vkbd) { this.canvas = canvas; this.cctx = canvas.getContext('2d'); this.cctx.strokeStyle = 'rgba(255,255,255,0)'; this.cctx.FillStyle = 'rgba(255,255,255,0)'; this.bstore = new Element('canvas', { 'width':this.canvas.width, 'height':this.canvas.height, }); this.bctx = this.bstore.getContext('2d'); this.aMF = 0; this.Tcool = true; this.pTe = null; this.ccnt = 0; this.clx = 0; this.cly = 0; this.clw = 0; this.clh = 0; this.mX = 0; this.mY = 0; this.chx = 10; this.chy = 10; this.modkeys = [144, ]; this.cursors = new Array(); this.sid = null; this.open = false; this.cssC = cssCursor; this.uT = useTouch; this.controls = controls; if (!cssCursor) { this.cI = new Element('img', { 'src': '/c_default.png', 'styles': { 'position': 'absolute', 'z-index': 998, 'left': this.mX - this.chx, 'top': this.mY - this.chy } }).inject(document.body); } if (vkbd) { vkbd.addEvent('vkpress', this.onKv.bind(this)); } //browser identiying variables this.msie = window.navigator.userAgent.indexOf('MSIE '); this.trident = window.navigator.userAgent.indexOf('Trident/'); this.parent(url); //add the toggle function to the keyboard language button $('keyboardlanguage').addEvent('click', this.ToggleLanguageButton.bind(this)); }, Disconnect: function() { this._reset(); }, SendKey: function(comb) { //code 0 : ctrl+alt+delete //code 1 : alt+tab //code 2 : alt+tab release if (this.sock.readyState == this.sock.OPEN) { this.log.debug('send special combination', comb); buf = new ArrayBuffer(12); a = new Uint32Array(buf); a[0] = 3; // WSOP_CS_SPECIALCOMB a[1] = comb; this.sock.send(buf); }; }, SendCredentials: function() { var infoJSONstring = JSON.stringify(settingsGetJSON()); var len = infoJSONstring.length; var buf = new ArrayBuffer((len + 1)*4); // 4 bytes for each char var bufView = new Uint32Array(buf); bufView[0] = 4; // WSOP_CS_CREDENTIAL_JSON for(var i = 0; i= this.clx) && (x <= (this.clx + this.clw)) && (y >= this.cly) && (y <= (this.cly + this.clh)) ); } // No clipping region return true; }, /** * Main message loop. */ _pmsg: function(data) { // process a binary RDP message from our queue // 0-12 code is WSGATE protocol with a little tuning // 13-XXX codes is our own protocol draft implementation var op, hdr, count, rects, bmdata, rgba, compressed, i, offs, x, y, sx, sy, w, h, dw, dh, bpp, color, len; op = new Uint32Array(data, 0, 1); switch (op[0]) { case 0x0: // BeginPaint // this.log.debug('BeginPaint'); this._ctxS(); break; case 0x01: // EndPaint // this.log.debug('EndPaint'); this._ctxR(); break; case 0x02: // Single bitmap // // 0 uint32 Destination X // 1 uint32 Destination Y // 2 uint32 Width // 3 uint32 Height // 4 uint32 Destination Width // 5 uint32 Destination Height // 6 uint32 Bits per Pixel // 7 uint32 Flag: Compressed // 8 uint32 DataSize // hdr = new Uint32Array(data, 4, 9); bmdata = new Uint8Array(data, 40); x = hdr[0]; y = hdr[1]; w = hdr[2]; h = hdr[3]; dw = hdr[4]; dh = hdr[5]; bpp = hdr[6]; compressed = (hdr[7] != 0); len = hdr[8]; if ((bpp == 16) || (bpp == 15)) { if (this._ckclp(x, y) && this._ckclp(x + dw, y + dh)) { // this.log.debug('BMi:',(compressed ? ' C ' : ' U '),' x=',x,'y=',y,' w=',w,' h=',h,' l=',len); var outB = this.cctx.createImageData(w, h); if (compressed) { webrdp.dRLE16_RGBA(bmdata, len, w, outB.data); webrdp.flipV(outB.data, w, h); } else { webrdp.dRGB162RGBA(bmdata, len, outB.data); } this.cctx.putImageData(outB, x, y, 0, 0, dw, dh); } else { // this.log.debug('BMc:',(compressed ? ' C ' : ' U '),' x=',x,'y=',y,' w=',w,' h=',h,' bpp=',bpp); // putImageData ignores the clipping region, so we must // clip ourselves: We first paint into a second canvas, // then use drawImage (which honors clipping). var outB = this.bctx.createImageData(w, h); if (compressed) { webrdp.dRLE16_RGBA(bmdata, len, w, outB.data); webrdp.flipV(outB.data, w, h); } else { webrdp.dRGB162RGBA(bmdata, len, outB.data); } this.bctx.putImageData(outB, 0, 0, 0, 0, dw, dh); this.cctx.drawImage(this.bstore, 0, 0, dw, dh, x, y, dw, dh); } } else { this.log.warn('BPP <> 15/16 not yet implemented'); } break; case 0x03: // Primary: OPAQUE_RECT_ORDER // x, y , w, h, color hdr = new Int32Array(data, 4, 4); rgba = new Uint8Array(data, 20, 4); // this.log.debug('Fill:',hdr[0], hdr[1], hdr[2], hdr[3], this._c2s(rgba)); this.cctx.fillStyle = this._c2s(rgba); this.cctx.fillRect(hdr[0], hdr[1], hdr[2], hdr[3]); break; case 0x04: // SetBounds // left, top, right, bottom hdr = new Int32Array(data, 4, 4); this._cR(hdr[0], hdr[1], hdr[2] - hdr[0], hdr[3] - hdr[1], true); break; case 0x05: // PatBlt if (28 == data.byteLength) { // Solid brush style // x, y, width, height, fgcolor, rop3 hdr = new Int32Array(data, 4, 4); x = hdr[0]; y = hdr[1]; w = hdr[2]; h = hdr[3]; rgba = new Uint8Array(data, 20, 4); this._ctxS(); this._cR(x, y, w, h, false); if (this._sROP(new Uint32Array(data, 24, 1)[0])) { this.cctx.fillStyle = this._c2s(rgba); this.cctx.fillRect(x, y, w, h); } this._ctxR(); } else { this.log.warn('PatBlt: Patterned brush not yet implemented'); } break; case 0x06: // Multi Opaque rect // color, nrects // rect1.x,rect1.y,rect1.w,rect1.h ... rectn.x,rectn.y,rectn.w,rectn.h rgba = new Uint8Array(data, 4, 4); count = new Uint32Array(data, 8, 1); rects = new Uint32Array(data, 12, count[0] * 4); // this.log.debug('MultiFill: ', count[0], " ", this._c2s(rgba)); this.cctx.fillStyle = this._c2s(rgba); offs = 0; // var c = this._c2s(rgba); for (i = 0; i < count[0]; ++i) { this.cctx.fillRect(rects[offs], rects[offs+1], rects[offs+2], rects[offs+3]); // this._fR(rects[offs], rects[offs+1], rects[offs+2], rects[offs+3], c); offs += 4; } break; case 0x07: // ScrBlt // rop3, x, y, w, h, sx, sy hdr = new Int32Array(data, 8, 6); x = hdr[0]; y = hdr[1]; w = hdr[2]; h = hdr[3]; sx = hdr[4]; sy = hdr[5]; if ((w > 0) && (h > 0)) { if (this._sROP(new Uint32Array(data, 4, 1)[0])) { if (this._ckclp(x, y) && this._ckclp(x + w, y + h)) { // No clipping necessary this.cctx.putImageData(this.cctx.getImageData(sx, sy, w, h), x, y); } else { // Clipping necessary this.bctx.putImageData(this.cctx.getImageData(sx, sy, w, h), 0, 0); this.cctx.drawImage(this.bstore, 0, 0, w, h, x, y, w, h); } } } else { this.log.warn('ScrBlt: width and/or height is zero'); } break; case 0x08: // PTR_NEW // id, xhot, yhot hdr = new Uint32Array(data, 4, 3); var inline_cursor_base64 = btoa(String.fromCharCode.apply(null, new Uint8Array(data, 16))); if (this.cssC) { this.cursors[hdr[0]] = (this.msie > 0 || this.trident > 0) ? 'url(/cur/' + this.sid + '/' + hdr[0] + '), none' : //IE is not suporting given hot spots 'url(data:image/vnd.microsoft.icon;base64,' + inline_cursor_base64 + ') ' + hdr[1] + ' ' + hdr[2] + ',default'; } else { this.cursors[hdr[0]] = (this.msie > 0 || this.trident > 0) ? { u: '/cur/' + this.sid + '/' + hdr[0] } : { u: inline_cursor_base64, x: hdr[1], y: hdr[2] }; } break; case 0x09: // PTR_FREE // id this.cursors[new Uint32Array(data, 4, 1)[0]] = undefined; break; case 0x0a: // PTR_SET // id // this.log.debug('PS:', this.cursors[new Uint32Array(data, 4, 1)[0]]); if (this.cssC) { if(this.textAreaInput) this.textAreaInput.setStyle('cursor', this.cursors[new Uint32Array(data, 4, 1)[0]]); } else { var cobj = this.cursors[new Uint32Array(data, 4, 1)[0]]; this.chx = cobj.x; this.chy = cobj.y; this.cI.src = cobj.u; } break; case 0x0b: // PTR_SETNULL if (this.cssC) { if(this.textAreaInput) this.textAreaInput.setStyle('cursor', 'none'); } else { this.cI.src = '/c_none.png'; } break; case 0x0c: // PTR_SETDEFAULT if (this.cssC) { if(this.textAreaInput) this.textAreaInput.setStyle('cursor', 'default'); } else { this.chx = 10; this.chy = 10; this.cI.src = '/c_default.png'; } break; // our draft protocol implementation follows case 0x0d: /* clipboard_send_formats_available * this message contain list of clipboard format on server * available for fetching * * typedef enum * { * clip_format_unsupported = -1, * clip_format_raw, * clip_format_text, * clip_format_FileGroupDescriptorW * }wrdp_enum_clip_format; * NOTE: "wrdp_enum_clip_format" must be kept in sync with * core api * */ { // console.log(data.toHexdump()); var fmts = new Uint8Array(data, 4); this.cbd.ClipbrdInfoParse(fmts); } break; case 0x0e: // clipboard_send_data // this message contain actual clipboard data // NOTE: not all clipboard formats will support data // fetching { // console.log(data.toHexdump()); this.cbd.reciveClipboardInfo(data); } break; case 0x0f: // clipboard_request_data // local clipboard data requested { // console.log(data.toHexdump()); var type = new Uint8Array(data, 4, 1); this.cbd.sendClipboardInfo(type[0]); } break; case 0x10: /* ft_request * this is filetransfer request * must contain file id from cached list */ { // console.log(data.toHexdump()); var file_id = new Uint32Array(data, 4, 1)[0]; var req_size = new BigUint64Array(data, 4 + 4, 1)[0]; var file_offset = new BigUint64Array(data, 4 + 4 + 8, 1)[0]; file = this.cbd.filelistOut[file_id]; if (!file) { console.log("Can`t find file for transfer") return; } file.sendChunk(req_size, file_offset); } break; case 0x11: // ft_chunk // this message contain transfer id and chunk of file data { // console.log(data.toHexdump()) var transfer_id = new Uint32Array(data,0,2)[1]; var file = null; this.cbd.filelistIn.forEach(function(f) { if (transfer_id == f.transfer_id) { file = f } }); if (!file) { this.cbd.filelistIn.forEach(function(f) { if (f.status == 2) { file = f } }); } if (file) { file.newChunk(data); } } break; case 0x12: // ft_finish // this message indicate what filetransfer is finished { // console.log(data.toHexdump()); this.cbd.update(); } break; default: this.log.warn('Unknown BINRESP: ', data.byteLength); } }, _cR: function(x, y, w, h, save) { if (save) { this.clx = x; this.cly = y; this.clw = w; this.clh = h; } // Replace clipping region, NO intersection. this.cctx.beginPath(); this.cctx.rect(0, 0, this.canvas.width, this.canvas.height); this.cctx.clip(); if (x == y == 0) { // All zero means: reset to full canvas size if ((w == h == 0) || ((w == this.canvas.width) && (h == this.canvas.height))) { return; } } // New clipping region this.cctx.beginPath(); this.cctx.rect(x, y, w, h); this.cctx.clip(); }, _fR: function(x, y, w, h, color) { return; if ((w < 2) || (h < 2)) { this.cctx.strokeStyle = color; this.cctx.beginPath(); this.cctx.moveTo(x, y); if (w > h) { this.cctx.lineWidth = h; this.cctx.lineTo(x + w, y); } else { this.cctx.lineWidth = w; this.cctx.lineTo(x, y + h); } this.cctx.stroke(); } else { this.cctx.fillStyle = color; this.cctx.fillRect(x, y, w, h); } }, _sROP: function(rop) { switch (rop) { case 0x005A0049: // GDI_PATINVERT: D = P ^ D this.cctx.globalCompositeOperation = 'xor'; return true; break; case 0x00F00021: // GDI_PATCOPY: D = P this.cctx.globalCompositeOperation = 'copy'; return true; break; case 0x00CC0020: // GDI_SRCCOPY: D = S this.cctx.globalCompositeOperation = 'source-over'; return true; break; default: this.log.warn('Unsupported raster op: ', rop.toString(16)); break; } return false; /* case 0x00EE0086: // GDI_SRCPAINT: D = S | D break; case 0x008800C6: // GDI_SRCAND: D = S & D break; case 0x00660046: // GDI_SRCINVERT: D = S ^ D break; case 0x00440328: // GDI_SRCERASE: D = S & ~D break; case 0x00330008: // GDI_NOTSRCCOPY: D = ~S break; case 0x001100A6: // GDI_NOTSRCERASE: D = ~S & ~D break; case 0x00C000CA: // GDI_MERGECOPY: D = S & P break; case 0x00BB0226: // GDI_MERGEPAINT: D = ~S | D break; case 0x00FB0A09: // GDI_PATPAINT: D = D | (P | ~S) break; case 0x00550009: // GDI_DSTINVERT: D = ~D break; case 0x00000042: // GDI_BLACKNESS: D = 0 break; case 0x00FF0062: // GDI_WHITENESS: D = 1 break; case 0x00E20746: // GDI_DSPDxax: D = (S & P) | (~S & D) break; case 0x00B8074A: // GDI_PSDPxax: D = (S & D) | (~S & P) break; case 0x000C0324: // GDI_SPna: D = S & ~P break; case 0x00220326: // GDI_DSna D = D & ~S break; case 0x00220326: // GDI_DSna: D = D & ~S break; case 0x00A000C9: // GDI_DPa: D = D & P break; case 0x00A50065: // GDI_PDxn: D = D ^ ~P break; */ }, /** * Reset our state to disconnected */ _reset: function() { this.log.setWS(null); this.fireEvent('disconnected'); if (this.sock.readyState == this.sock.OPEN) { this.sock.close(); } this.clx = 0; this.cly = 0; this.clw = 0; this.clh = 0; this.canvas.removeEvents(); document.removeEvents(); var drop_zone = document.getElementById('drop_zone'); if (drop_zone) { document.body.removeChild(drop_zone); } try{ this.textAreaInput.remove(); } catch(err){ } this.textAreaInput = null; while (this.ccnt > 0) { this.cctx.restore(); this.ccnt -= 1; } this.cctx.clearRect(0, 0, this.canvas.width, this.canvas.height); document.title = document.title.replace(/:.*/, ': offline'); if (this.cssC) { this.canvas.setStyle('cursor','default'); } else { this.cI.src = '/c_default.png'; } if (!this.cssC) { this.cI.removeEvents(); this.cI.destroy(); } }, fT: function() { delete this.fTid; if (this.pT) { this.fireEvent('touch' + this.pT); this.pT = 0; return; } if (this.pTe) { this.onMd(this.pTe); this.pTe = null; } }, cT: function() { this.log.debug('cT'); this.Tcool = true; }, /** * Event handler for touch start */ onTs: function(evt) { var tn = evt.targetTouches.length; this.log.debug('Ts:', tn); switch (tn) { default: break; case 1: this.pTe = evt; evt.preventDefault(); if ('number' == typeof(this.fTid)) { clearTimeout(this.fTid); } this.fTid = this.fT.delay(50, this); break; case 2: this.pT = 2; this.Tcool = false; evt.preventDefault(); if ('number' == typeof(this.fTid)) { clearTimeout(this.fTid); } this.cT.delay(500, this) this.fTid = this.fT.delay(50, this); break; case 3: this.pT = 3; this.Tcool = false; evt.preventDefault(); if ('number' == typeof(this.fTid)) { clearTimeout(this.fTid); } this.cT.delay(500, this) this.fTid = this.fT.delay(50, this); break; case 4: this.pT = 4; this.Tcool = false; evt.preventDefault(); if ('number' == typeof(this.fTid)) { clearTimeout(this.fTid); } this.cT.delay(500, this) this.fTid = this.fT.delay(50, this); break; } return true; }, /** * Event handler for touch start */ onTe: function(evt) { if ((0 == evt.targetTouches.length) && this.Tcool) { evt.preventDefault(); this.onMu(evt, evt.changedTouches[0].pageX, evt.changedTouches[0].pageY); } }, /** * Event handler for touch move */ onTm: function(evt) { // this.log.debug('Tm:', evt); if (1 == evt.targetTouches.length) { this.onMm(evt); } }, /** * Event handler for mouse move events */ onMm: function(evt) { var buf, a, x, y; evt.preventDefault(); x = (this.msie > 0 || this.trident > 0) ? evt.event.layerX - evt.event.currentTarget.offsetLeft : evt.event.layerX; y = (this.msie > 0 || this.trident > 0) ? evt.event.layerY - evt.event.currentTarget.offsetTop : evt.event.layerY; if (!this.cssC) { this.mX = x; this.mY = y; this.cP(); } // this.log.debug('mM x: ', x, ' y: ', y); if (this.sock.readyState == this.sock.OPEN) { buf = new ArrayBuffer(16); a = new Uint32Array(buf); a[0] = 0; // WSOP_CS_MOUSE a[1] = 0x0800; // PTR_FLAGS_MOVE a[2] = x; a[3] = y; this.sock.send(buf); } }, /** * Event handler for mouse down events */ onMd: function(evt) { var buf, a, x, y, which; this.textAreaInput.focus(); this.cbd.update(); if (this.Tcool) { if(evt.preventDefault) evt.preventDefault(); if(evt.stopPropagation) evt.stopPropagation(); if (evt.rightClick && evt.control && evt.alt) { this.fireEvent('touch3'); return; } x = (this.msie > 0 || this.trident > 0) ? evt.event.layerX - evt.event.currentTarget.offsetLeft : evt.event.layerX; y = (this.msie > 0 || this.trident > 0) ? evt.event.layerY - evt.event.currentTarget.offsetTop : evt.event.layerY; which = this._mB(evt); //this.log.debug('mD b: ', which, ' x: ', x, ' y: ', y); if (this.sock.readyState == this.sock.OPEN) { buf = new ArrayBuffer(16); a = new Uint32Array(buf); a[0] = 0; // WSOP_CS_MOUSE a[1] = 0x8000 | which; a[2] = x; a[3] = y; this.sock.send(buf); this.mouseDownStatus[which] = true; } } }, /** * Event handler for mouse up events */ onMu: function(evt, x, y) { var buf, a, x, y, which; if (this.Tcool) { evt.preventDefault(); x = (this.msie > 0 || this.trident > 0) ? evt.event.layerX - evt.event.currentTarget.offsetLeft : evt.event.layerX; y = (this.msie > 0 || this.trident > 0) ? evt.event.layerY - evt.event.currentTarget.offsetTop : evt.event.layerY; which = this._mB(evt); this.log.debug('mU b: ', which, ' x: ', x, ' y: ', y); if (this.aMF) { this.fireEvent('mouserelease'); } if (this.sock.readyState == this.sock.OPEN) { buf = new ArrayBuffer(16); a = new Uint32Array(buf); a[0] = 0; // WSOP_CS_MOUSE a[1] = which; a[2] = x; a[3] = y; this.sock.send(buf); this.mouseDownStatus[which] = false; } } }, /** * Event handler for mouse wheel events */ onMw: function(evt) { var buf, a, x, y; evt.preventDefault(); x = (this.msie > 0 || this.trident > 0) ? evt.event.layerX - evt.event.currentTarget.offsetLeft : evt.event.layerX; y = (this.msie > 0 || this.trident > 0) ? evt.event.layerY - evt.event.currentTarget.offsetTop : evt.event.layerY; // this.log.debug('mW d: ', evt.wheel, ' x: ', x, ' y: ', y); if (this.sock.readyState == this.sock.OPEN) { buf = new ArrayBuffer(16); a = new Uint32Array(buf); a[0] = 0; // WSOP_CS_MOUSE a[1] = 0x200 | ((evt.wheel > 0) ? 0x087 : 0x188); a[2] = 0; a[3] = 0; this.sock.send(buf); } }, /** * Field used to keep the states of the sent mouse events */ mouseDownStatus: {}, /** * Event handler for mouse leaving the canvas area * used to send mouse release for any unsent mouse releases */ onMouseLeave: function(evt){ for(var button in this.mouseDownStatus){ if(this.mouseDownStatus[button]){ var x = (this.msie > 0 || this.trident > 0) ? evt.event.layerX - evt.event.currentTarget.offsetLeft : evt.event.layerX; var y = (this.msie > 0 || this.trident > 0) ? evt.event.layerY - evt.event.currentTarget.offsetTop : evt.event.layerY; var maxX = $('textareainput').getStyle('width').toInt(); var maxY = $('textareainput').getStyle('height').toInt(); if (x < 0) x = 0; if (y < 0) y = 0; if (x > maxX) x = maxX; if (y > maxY) y = maxY; var which = button; if (this.sock.readyState == this.sock.OPEN) { buf = new ArrayBuffer(16); a = new Uint32Array(buf); a[0] = 0; // WSOP_CS_MOUSE a[1] = which; a[2] = x; a[3] = y; this.sock.send(buf); this.mouseDownStatus[which] = false; } } } }, /** * Event handler for sending array of keys to be pressed */ sendKeys: function(codes) { // var myStringArray = ["Hello","World"]; for (var i = 0; i < codes.length; i++) { alert(codes[i]); if(this.sock.readyState == this.sock.OPEN) { buf = new ArrayBuffer(12); } } }, /** * Sends a unicode char or string */ SendUnicodeString: function(str){ var len = str.length; buf = new ArrayBuffer(4 * len + 4); a = new Uint32Array(buf); a[0] = 5; // WSOP_CS_UNICODE for(var i = 0; i> 11; var pelG = (pel & 0x7E0) >> 5; var pelB = pel & 0x1F; // 656 -> 888 pelR = (pelR << 3 & ~0x7) | (pelR >> 2); pelG = (pelG << 2 & ~0x3) | (pelG >> 4); pelB = (pelB << 3 & ~0x7) | (pelB >> 2); outA[outI++] = inA[inI++] ^ pelR; outA[outI++] = inA[inI++] ^ pelG; outA[outI++] = inA[inI] ^ pelB; outA[outI] = 255; // alpha } webrdp.buf2RGBA = function(inA, inI, outA, outI) { var pel = inA[inI] | (inA[inI + 1] << 8); var pelR = (pel & 0xF800) >> 11; var pelG = (pel & 0x7E0) >> 5; var pelB = pel & 0x1F; // 656 -> 888 pelR = (pelR << 3 & ~0x7) | (pelR >> 2); pelG = (pelG << 2 & ~0x3) | (pelG >> 4); pelB = (pelB << 3 & ~0x7) | (pelB >> 2); outA[outI++] = pelR; outA[outI++] = pelG; outA[outI++] = pelB; outA[outI] = 255; // alpha } webrdp.pel2RGBA = function (pel, outA, outI) { var pelR = (pel & 0xF800) >> 11; var pelG = (pel & 0x7E0) >> 5; var pelB = pel & 0x1F; // 656 -> 888 pelR = (pelR << 3 & ~0x7) | (pelR >> 2); pelG = (pelG << 2 & ~0x3) | (pelG >> 4); pelB = (pelB << 3 & ~0x7) | (pelB >> 2); outA[outI++] = pelR; outA[outI++] = pelG; outA[outI++] = pelB; outA[outI] = 255; // alpha } webrdp.flipV = function(inA, width, height) { var sll = width * 4; var half = height / 2; var lbot = sll * (height - 1); var ltop = 0; var tmp = new Uint8Array(sll); var i, j; if ('subarray' in inA) { for (i = 0; i < half ; ++i) { tmp.set(inA.subarray(ltop, ltop + sll)); inA.set(inA.subarray(lbot, lbot + sll), ltop); inA.set(tmp, lbot); ltop += sll; lbot -= sll; } } else { for (i = 0; i < half ; ++i) { for (j = 0; j < sll; ++j) { tmp[j] = inA[ltop + j]; inA[ltop + j] = inA[lbot + j]; inA[lbot + j] = tmp[j]; } ltop += sll; lbot -= sll; } } } webrdp.dRGB162RGBA = function(inA, inLength, outA) { var inI = 0; var outI = 0; while (inI < inLength) { webrdp.buf2RGBA(inA, inI, outA, outI); inI += 2; outI += 4; } } webrdp.ExtractCodeId = function(bOrderHdr) { var code; switch (bOrderHdr) { case 0xF0: case 0xF1: case 0xF6: case 0xF8: case 0xF3: case 0xF2: case 0xF7: case 0xF4: case 0xF9: case 0xFA: case 0xFD: case 0xFE: return bOrderHdr; } code = bOrderHdr >> 5; switch (code) { case 0x00: case 0x01: case 0x03: case 0x02: case 0x04: return code; } return bOrderHdr >> 4; } webrdp.ExtractRunLength = function(code, inA, inI, advance) { var runLength = 0; var ladvance = 1; switch (code) { case 0x02: runLength = inA[inI] & 0x1F; if (0 == runLength) { runLength = inA[inI + 1] + 1; ladvance += 1; } else { runLength *= 8; } break; case 0x0D: runLength = inA[inI] & 0x0F; if (0 == runLength) { runLength = inA[inI + 1] + 1; ladvance += 1; } else { runLength *= 8; } break; case 0x00: case 0x01: case 0x03: case 0x04: runLength = inA[inI] & 0x1F; if (0 == runLength) { runLength = inA[inI + 1] + 32; ladvance += 1; } break; case 0x0C: case 0x0E: runLength = inA[inI] & 0x0F; if (0 == runLength) { runLength = inA[inI + 1] + 16; ladvance += 1; } break; case 0xF0: case 0xF1: case 0xF6: case 0xF8: case 0xF3: case 0xF2: case 0xF7: case 0xF4: runLength = inA[inI + 1] | (inA[inI + 2] << 8); ladvance += 2; break; } advance.val = ladvance; return runLength; } webrdp.WriteFgBgImage16toRGBA = function(outA, outI, rowDelta, bitmask, fgPel, cBits) { var cmpMask = 0x01; while (cBits-- > 0) { if (bitmask & cmpMask) { webrdp.xorbufRGBAPel16(outA, outI - rowDelta, outA, outI, fgPel); } else { webrdp.copyRGBA(outA, outI - rowDelta, outA, outI); } outI += 4; cmpMask <<= 1; } return outI; } webrdp.WriteFirstLineFgBgImage16toRGBA = function(outA, outI, bitmask, fgPel, cBits) { var cmpMask = 0x01; while (cBits-- > 0) { if (bitmask & cmpMask) { webrdp.pel2RGBA(fgPel, outA, outI); } else { webrdp.pel2RGBA(0, outA, outI); } outI += 4; cmpMask <<= 1; } return outI; } webrdp.dRLE16_RGBA = function(inA, inLength, width, outA) { var runLength; var code, pixelA, pixelB, bitmask; var inI = 0; var outI = 0; var fInsertFgPel = false; var fFirstLine = true; var fgPel = 0xFFFFFF; var rowDelta = width * 4; var advance = {val: 0}; while (inI < inLength) { if (fFirstLine) { if (outI >= rowDelta) { fFirstLine = false; fInsertFgPel = false; } } code = webrdp.ExtractCodeId(inA[inI]); if (code == 0x00 || code == 0xF0) { runLength = webrdp.ExtractRunLength(code, inA, inI, advance); inI += advance.val; if (fFirstLine) { if (fInsertFgPel) { webrdp.pel2RGBA(fgPel, outA, outI); outI += 4; runLength -= 1; } while (runLength > 0) { webrdp.pel2RGBA(0, outA, outI); runLength -= 1; outI += 4; } } else { if (fInsertFgPel) { webrdp.xorbufRGBAPel16(outA, outI - rowDelta, outA, outI, fgPel); outI += 4; runLength -= 1; } while (runLength > 0) { webrdp.copyRGBA(outA, outI - rowDelta, outA, outI); runLength -= 1; outI += 4; } } fInsertFgPel = true; continue; } fInsertFgPel = false; switch (code) { case 0x01: case 0xF1: case 0x0C: case 0xF6: runLength = webrdp.ExtractRunLength(code, inA, inI, advance); inI += advance.val; if (code == 0x0C || code == 0xF6) { fgPel = inA[inI] | (inA[inI + 1] << 8); inI += 2; } if (fFirstLine) { while (runLength > 0) { webrdp.pel2RGBA(fgPel, outA, outI); runLength -= 1; outI += 4; } } else { while (runLength > 0) { webrdp.xorbufRGBAPel16(outA, outI - rowDelta, outA, outI, fgPel); runLength -= 1; outI += 4; } } break; case 0x0E: case 0xF8: runLength = webrdp.ExtractRunLength(code, inA, inI, advance); inI += advance.val; pixelA = inA[inI] | (inA[inI + 1] << 8); inI += 2; pixelB = inA[inI] | (inA[inI + 1] << 8); inI += 2; while (runLength > 0) { webrdp.pel2RGBA(pixelA, outA, outI); outI += 4; webrdp.pel2RGBA(pixelB, outA, outI); outI += 4; runLength -= 1; } break; case 0x03: case 0xF3: runLength = webrdp.ExtractRunLength(code, inA, inI, advance); inI += advance.val; pixelA = inA[inI] | (inA[inI + 1] << 8); inI += 2; while (runLength > 0) { webrdp.pel2RGBA(pixelA, outA, outI); outI += 4; runLength -= 1; } break; case 0x02: case 0xF2: case 0x0D: case 0xF7: runLength = webrdp.ExtractRunLength(code, inA, inI, advance); inI += advance.val; if (code == 0x0D || code == 0xF7) { fgPel = inA[inI] | (inA[inI + 1] << 8); inI += 2; } if (fFirstLine) { while (runLength >= 8) { bitmask = inA[inI++]; outI = webrdp.WriteFirstLineFgBgImage16toRGBA(outA, outI, bitmask, fgPel, 8); runLength -= 8; } } else { while (runLength >= 8) { bitmask = inA[inI++]; outI = webrdp.WriteFgBgImage16toRGBA(outA, outI, rowDelta, bitmask, fgPel, 8); runLength -= 8; } } if (runLength > 0) { bitmask = inA[inI++]; if (fFirstLine) { outI = webrdp.WriteFirstLineFgBgImage16toRGBA(outA, outI, bitmask, fgPel, runLength); } else { outI = webrdp.WriteFgBgImage16toRGBA(outA, outI, rowDelta, bitmask, fgPel, runLength); } } break; case 0x04: case 0xF4: runLength = webrdp.ExtractRunLength(code, inA, inI, advance); inI += advance.val; while (runLength > 0) { webrdp.pel2RGBA(inA[inI] | (inA[inI + 1] << 8), outA, outI); inI += 2; outI += 4; runLength -= 1; } break; case 0xF9: inI += 1; if (fFirstLine) { outI = webrdp.WriteFirstLineFgBgImage16toRGBA(outA, outI, 0x03, fgPel, 8); } else { outI = webrdp.WriteFgBgImage16toRGBA(outA, outI, rowDelta, 0x03, fgPel, 8); } break; case 0xFA: inI += 1; if (fFirstLine) { outI = webrdp.WriteFirstLineFgBgImage16toRGBA(outA, outI, 0x05, fgPel, 8); } else { outI = webrdp.WriteFgBgImage16toRGBA(outA, outI, rowDelta, 0x05, fgPel, 8); } break; case 0xFD: inI += 1; webrdp.pel2RGBA(0xFFFF, outA, outI); outI += 4; break; case 0xFE: inI += 1; webrdp.pel2RGBA(0, outA, outI); outI += 4; break; } } } /** * Continuos refresh for the IMEhelper */ function refreshIMEhelper(){ setTimeout(this.refreshIMEhelper,20); //IME helper div if(rdp.IMEon){ $('IMEhelper').setStyle('visibility','visible'); $('IMEhelper').set('html',$('textareainput').get('value')); }else{ $('IMEhelper').setStyle('visibility','hidden'); } }