summaryrefslogtreecommitdiff
path: root/www/js/webrdp-debug.js
diff options
context:
space:
mode:
authorsss <sss@dark-alexandr.net>2023-01-17 00:38:19 +0300
committersss <sss@dark-alexandr.net>2023-01-17 00:38:19 +0300
commitcc3f33db7a8d3c4ad373e646b199808e01bc5d9b (patch)
treeec09d690c7656ab5f2cc72607e05fb359c24d8b2 /www/js/webrdp-debug.js
added webrdp public code
Diffstat (limited to 'www/js/webrdp-debug.js')
-rw-r--r--www/js/webrdp-debug.js1707
1 files changed, 1707 insertions, 0 deletions
diff --git a/www/js/webrdp-debug.js b/www/js/webrdp-debug.js
new file mode 100644
index 0000000..12c7e52
--- /dev/null
+++ b/www/js/webrdp-debug.js
@@ -0,0 +1,1707 @@
+/** BSD-2-Clause license
+ *
+ * Copyright (c) 2018-2023 wsgate devs, NST <www.newinfosec.ru>, sss <sss at dark-alexandr dot net>.
+ *
+ */
+
+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<len; i++){
+ bufView[i+1] = infoJSONstring.charCodeAt(i);
+ }
+ this.sock.send(buf);
+ },
+ /**
+ * Multilanguage mode
+ */
+ useIME: false,
+ ToggleLanguageButton: function(){
+ if(this.useIME){
+ this.useIME = false;
+ $('keyboardlanguage').removeClass('extracommandshold');
+ }else{
+ this.useIME = true;
+ $('keyboardlanguage').addClass('extracommandshold');
+ }
+ },
+ /**
+ * Used when the special input method is on
+ */
+ IMEon: false,
+ /**
+ * The textarea element object
+ */
+ textAreaInput: null,
+ /**
+ * This function adds a textarea element on top of the canvas for the purpose of keyboard input
+ */
+ SetupCanvas: function(){
+ if(this.textAreaInput)return;
+
+ var pos = this.canvas.getPosition();
+ var size = this.canvas.getSize();
+
+ this.textAreaInput = document.createElement('textarea');
+ this.textAreaInput.rdp = this;
+
+ this.textAreaInput.set('id', 'textareainput');
+
+ this.textAreaInput.setStyle('width', size.x);
+ this.textAreaInput.setStyle('height', size.y);
+ this.textAreaInput.setStyle('position', 'absolute');
+ this.textAreaInput.setStyle('opacity', 0);
+ this.textAreaInput.setStyle('resize', 'none');
+ this.textAreaInput.setStyle('cursor', 'default');
+ this.canvas.setStyle('cursor', 'none');
+
+ this.textAreaInput.setPosition(pos);
+
+ this.textAreaInput.addEvent('keydown', this.KeyDownEvent.bind(this));
+ this.textAreaInput.addEvent('keypress', this.KeyPressEvent.bind(this));
+ this.textAreaInput.addEvent('keypress', this.KeyPressEvent.bind(this));
+ this.textAreaInput.addEvent('keyup', this.KeyUpEvent.bind(this));
+ this.textAreaInput.addEvent('copy', function(evt){
+ if (evt.preventDefault) evt.preventDefault();
+ if (evt.stopPropagation) evt.stopPropagation();
+ });
+ this.textAreaInput.addEvent('paste', function(evt){
+ if (evt.preventDefault) evt.preventDefault();
+ if (evt.stopPropagation) evt.stopPropagation();
+ // alert("pasted");
+ });
+
+
+ document.body.appendChild(this.textAreaInput);
+
+ //start the IME helper refresh
+ refreshIMEhelper();
+
+ //make sure the textarea is always on focus
+ this.textAreaInput.focus();
+ this.textAreaInput.addEvent('blur', function(){
+ setTimeout(function(){
+ if($('textareainput')){
+ var cbdcontent = document.getElementById('cbdcontent');
+ if (cbdcontent.style.visibility != "visible") {
+ $('textareainput').focus();
+ }
+ }
+ },20);
+ });
+
+ },
+ /**
+ * Creating clipboard object and setting drag`n`drop area
+ */
+ SetupDragnDrop: function(){
+ this.cbd = new ClipboardHandler(this);
+ var area = this.textAreaInput;
+ area.addEventListener("dragenter", function(evt) {
+ var drop_zone = document.createElement('div');
+ document.body.appendChild(drop_zone);
+ drop_zone.rdp = evt.target.rdp;
+
+ var html = lables.drop_here;
+ drop_zone.innerHTML = html;
+
+ var pos = event.target.getPosition();
+ // var size = event.target.getSize();
+
+ drop_zone.set('id', 'drop_zone');
+ // drop_zone.setStyle('z-index', '1000000');
+ drop_zone.setStyle('position', 'absolute');
+ drop_zone.setStyle('position', 'absolute');
+ drop_zone.setStyle('resize', 'none');
+ drop_zone.setPosition(pos);
+ drop_zone.setStyle('width', event.target.style.width);
+ drop_zone.setStyle('height', event.target.style.height);
+
+ drop_zone.addEventListener("dragover", function(evt) {
+ evt.stopPropagation();
+ evt.preventDefault();
+ evt.dataTransfer.dropEffect = 'copy'; // Explicitly show this is a copy.
+ }, false);
+
+ drop_zone.addEventListener("drop", function(evt) {
+ var cbd = evt.target.rdp.cbd;
+ evt.stopPropagation();
+ evt.preventDefault();
+ cbd.ClipoboardSendFilelist(evt.dataTransfer.files);
+ var drop_zone = document.getElementById('drop_zone');
+ document.body.removeChild(drop_zone);
+ }, false);
+
+ drop_zone.addEventListener("dragleave", function(evt) {
+ var drop_zone = document.getElementById('drop_zone');
+ document.body.removeChild(drop_zone);
+ }, false);
+
+ }, false);
+ },
+ /**
+ * Returns true when a non character is pressed
+ */
+ FunctionalKey: function(key){
+ return (key != 32 && ((key <= 46) || (91 <= key && key <= 145)));
+ },
+ /**
+ * Takes the contents of the textarea and sends them to the server
+ */
+ DumpTextArea: function(key){
+ if(key==13||key==0)
+ if(this.IMEon){
+ var textinput = this.textAreaInput.get("value");
+ if(textinput!=""){
+ if(textinput[0]=='\n'){
+ textinput = textinput.substring(1);
+ }
+ this.SendUnicodeString(textinput);
+ this.textAreaInput.set("value","");
+ }
+ this.IMEon=false;
+ }
+ }, /**
+ * Position cursor image
+ */
+ cP: function() {
+ this.cI.setStyles({'left': this.mX - this.chx, 'top': this.mY - this.chy});
+ },
+ /**
+ * Check, if a given point is inside the clipping region.
+ */
+ _ckclp: function(x, y) {
+ if (this.clw || this.clh) {
+ return (
+ (x >= 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<len; i++){
+ a[i+1] = str.charCodeAt(i);
+ }
+ this.sock.send(buf);
+ },
+ /**
+ * Sends a scancode key event
+ * down = 1
+ * up = 0
+ */
+ SendKeyUpDown: function(key, upDown){
+ if (this.sock.readyState == this.sock.OPEN) {
+ buf = new ArrayBuffer(12);
+ a = new Uint32Array(buf);
+ a[0] = 1; // WSOP_CS_KUPDOWN
+ a[1] = upDown;
+ a[2] = key;
+ this.sock.send(buf);
+ }
+ },
+ KeyDownEvent: function(evt){
+ if(!this.useIME){
+ this.SendKeyUpDown(evt.code, 1);
+ }else{
+ if(evt.code==229||evt.code==0)this.IMEon=true;
+ //send key presses only when IME is off
+ if(!this.IMEon){
+ if(this.FunctionalKey(evt.code)){
+ if(evt.preventDefault) evt.preventDefault();
+ if(evt.stopPropagation) evt.stopPropagation();
+ this.SendKeyUpDown(evt.code, 1);
+ }
+ }
+ }
+ },
+ KeyUpEvent: function(evt){
+ if(!this.useIME){
+ this.SendKeyUpDown(evt.code, 0);
+ this.textAreaInput.set("value","");
+ }else{
+ if(!this.IMEon)
+ if(this.FunctionalKey(evt.code)){
+ if(evt.preventDefault) evt.preventDefault();
+ if(evt.stopPropagation) evt.stopPropagation();
+ this.SendKeyUpDown(evt.code, 0);
+ }
+ this.DumpTextArea(evt.code);
+ }
+ },
+ /**
+ * Sends unicode to the server
+ */
+ KeyPressEvent: function(evt){
+ if(this.useIME)
+ if(!this.IMEon){
+ this.SendUnicodeString(String.fromCharCode(evt.code));
+ $('textareainput').set("value","");
+ if(evt.preventDefault) evt.preventDefault();
+ if(evt.stopPropagation) evt.stopPropagation();
+ }
+ this.DumpTextArea(evt.code);
+ },
+ /**
+ * Event handler for key down events
+ */
+ onKd: function(evt) {
+ var a, buf;
+ this.log.debug('kD code: ', evt.code, ' ', evt);
+ evt.preventDefault();
+ // this.log.debug('kD code: ', evt.code, ' ', evt);
+ if (this.sock.readyState == this.sock.OPEN) {
+ buf = new ArrayBuffer(12);
+ a = new Uint32Array(buf);
+ a[0] = 1; // WSOP_CS_KUPDOWN
+ a[1] = 1; // down
+ a[2] = evt.code;
+ this.sock.send(buf);
+ }
+ },
+ /**
+ * Event handler for key up events
+ */
+ onKu: function(evt) {
+ var a, buf;
+ evt.preventDefault();
+ this.log.debug('ku code: ', evt.code, ' ', evt);
+ if (this.sock.readyState == this.sock.OPEN) {
+ buf = new ArrayBuffer(12);
+ a = new Uint32Array(buf);
+ a[0] = 1; // WSOP_CS_KUPDOWN
+ a[1] = 0; // up
+ a[2] = evt.code;
+ this.sock.send(buf);
+ }
+ },
+ /**
+ * Event handler for virtual keyboard
+ */
+ onKv: function(evt) {
+ var a, buf;
+ if (this.sock.readyState == this.sock.OPEN) {
+ // this.log.debug('kP code: ', evt.code);
+ buf = new ArrayBuffer(12);
+ a = new Uint32Array(buf);
+ if (evt.special) {
+ a[0] = 1; // WSOP_CS_KUPDOWN
+ a[1] = 1; // down
+ a[2] = evt.code;
+ this.sock.send(buf);
+ a[0] = 1; // WSOP_CS_KUPDOWN
+ a[1] = 0; // up
+ a[2] = evt.code;
+ } else {
+ a[0] = 2; // WSOP_CS_KPRESS
+ a[1] = (evt.shift ? 1 : 0)|(evt.control ? 2 : 0)|(evt.alt ? 4 : 0)|(evt.meta ? 8 : 0);
+ a[2] = evt.code;
+ }
+ this.sock.send(buf);
+ }
+ },
+ /**
+ * Event handler for key pressed events
+ Obsv: not used anymore. Will be removed after checking it's dependants.
+ */
+ onKp: function(evt) {
+ return;
+ },
+ /**
+ * Event handler for WebSocket RX events
+ */
+ onWSmsg: function(evt) {
+ //hide the loading image when the actual streaming starts
+ if ($('dvLoading').getStyle("visibility") !== "hidden") {
+ $('dvLoading').setStyles({ 'visibility': 'hidden' });
+ }
+ switch (typeof(evt.data)) {
+ // We use text messages for alerts and debugging ...
+ case 'string':
+ // this.log.debug(evt.data);
+ switch (evt.data.substr(0,2)) {
+ case "T:":
+ this._reset();
+ break;
+ case "0:":
+ //handle control message here
+ break;
+ case "E:":
+ var msg = evt.data.substring(2);
+ if(msg.substring(0, 2)=='E:'){
+ embedded = true;
+ msg = msg.substring(2);
+ }
+ this.log.err(msg);
+ this.fireEvent('alert', msg);
+ this._reset();
+ break;
+ case 'I:':
+ this.log.info(evt.data.substring(2));
+ break;
+ case 'W:':
+ this.log.warn(evt.data.substring(2));
+ break;
+ case 'D:':
+ this.log.debug(evt.data.substring(2));
+ break;
+ case 'S:':
+ this.sid = evt.data.substring(2);
+ break;
+ case 'R:':
+ //resolution changed
+ resolution=evt.data.substr(2).split('x');
+ $('screen').width=resolution[0];
+ $('screen').height=resolution[1];
+ this.bstore.width=resolution[0];
+ this.bstore.height=resolution[1];
+ $('textareainput').setStyle('width', resolution[0]+'px');
+ $('textareainput').setStyle('height', resolution[1]+'px');
+ break;
+ case 'C:':
+ var msg = evt.data.substr(2);
+ if(msg.substr(0, 2) == 'E:'){
+ msg = msg.substr(2);
+ embedded = true;
+ }
+ if(msg == "RDP session connection started."){
+ //the connection worked so we can set the cookies
+ //settingsSet();
+ return;
+ }
+ break;
+ }
+ break;
+ // ... and binary messages for the actual RDP stuff.
+ case 'object':
+ this.log.debug('wsin', evt.data);
+ this._pmsg(evt.data);
+ break;
+ }
+
+ },
+ /**
+ * Event handler for WebSocket connect events
+ */
+ onWSopen: function(evt) {
+ this.open = true;
+ this.log.setWS(this.sock);
+ //add the textarea on top of the canvas
+ this.SetupCanvas();
+ this.SetupDragnDrop();
+ // Add listeners for the various input events
+ this.textAreaInput.addEvent('mousemove', this.onMm.bind(this));
+ this.textAreaInput.addEvent('mousedown', this.onMd.bind(this));
+ this.textAreaInput.addEvent('mouseup', this.onMu.bind(this));
+ this.textAreaInput.addEvent('mousewheel', this.onMw.bind(this));
+ this.textAreaInput.addEvent('mouseleave', this.onMouseLeave.bind(this));
+ // Disable the browser's context menu
+ this.textAreaInput.addEvent('contextmenu', function(e) {e.stop();});
+ // For touch devices
+ if (this.uT) {
+ this.textAreaInput.addEvent('touchstart', this.onTs.bind(this));
+ this.textAreaInput.addEvent('touchend', this.onTe.bind(this));
+ this.textAreaInput.addEvent('touchmove', this.onTm.bind(this));
+ }
+ if (!this.cssC) {
+ // Same events on pointer image
+ this.cI.addEvent('mousemove', this.onMm.bind(this));
+ this.cI.addEvent('mousedown', this.onMd.bind(this));
+ this.cI.addEvent('mouseup', this.onMu.bind(this));
+ this.cI.addEvent('mousewheel', this.onMw.bind(this));
+ this.cI.addEvent('contextmenu', function(e) {e.stop();});
+ if (this.uT) {
+ this.cI.addEvent('touchstart', this.onTs.bind(this));
+ this.cI.addEvent('touchend', this.onTe.bind(this));
+ this.cI.addEvent('touchmove', this.onTm.bind(this));
+ }
+ }
+ this.fireEvent('connected');
+ this.SendCredentials();
+ },
+ /**
+ * Event handler for WebSocket disconnect events
+ */
+ onWSclose: function(evt) {
+ /*if (Browser.name == 'chrome') {
+ // Current chrome is buggy in that it does not
+ // fire WebSockets error events, so we use the
+ // wasClean flag in the close event.
+ if ((!evt.wasClean) && (!this.open)) {
+ this.fireEvent('alert', 'Could not connect to WebSockets gateway');
+ }
+ }*/
+ //this.open = false;
+ this._reset();
+ },
+ /**
+ * Event handler for WebSocket error events
+ */
+ onWSerr: function (evt) {
+ this.open = false;
+ switch (this.sock.readyState) {
+ case this.sock.CONNECTING:
+ this.fireEvent('alert', 'Could not connect to WebSockets gateway');
+ break;
+ }
+ this._reset();
+ },
+ /**
+ * Convert a color value contained in an uint8 array into an rgba expression
+ * that can be used to parameterize the canvas.
+ */
+ _c2s: function(c) {
+ return 'rgba' + '(' + c[0] + ',' + c[1] + ',' + c[2] + ',' + ((0.0 + c[3]) / 255) + ')';
+ },
+ /**
+ * Save the canvas state and remember this in our object.
+ */
+ _ctxS: function() {
+ this.cctx.save();
+ this.ccnt += 1;
+ },
+ /**
+ * Restore the canvas state and remember this in our object.
+ */
+ _ctxR: function() {
+ this.cctx.restore();
+ this.ccnt -= 1;
+ },
+ /**
+ * Convert the button information of a mouse event into
+ * RDP-like flags.
+ */
+ _mB: function(evt) {
+ if (this.aMF) {
+ return this.aMF;
+ }
+ var bidx;
+ if ('event' in evt && 'button' in evt.event) {
+ bidx = evt.event.button;
+ } else {
+ bidx = evt.rightClick ? 2 : 0;
+ }
+ switch (bidx) {
+ case 0:
+ return 0x1000; // left button
+ case 1:
+ return 0x4000; // middle button
+ case 2:
+ return 0x2000; // right button
+ }
+ return 0x1000;
+ },
+ SetArtificialMouseFlags: function(mf) {
+ if (null == mf) {
+ this.aMF = 0;
+ return;
+ }
+ this.aMF = 0x1000; // left button
+ if (mf.r) {
+ this.aMF = 0x2000; // right
+ }
+ if (mf.m) {
+ this.aMF = 0x4000; // middle
+ }
+ }
+});
+webrdp.copyRGBA = function(inA, inI, outA, outI) {
+ if ('subarray' in inA) {
+ outA.set(inA.subarray(inI, inI + 4), outI);
+ } else {
+ outA[outI++] = inA[inI++];
+ outA[outI++] = inA[inI++];
+ outA[outI++] = inA[inI++];
+ outA[outI] = inA[inI];
+ }
+}
+webrdp.xorbufRGBAPel16 = function(inA, inI, outA, outI, pel) {
+ 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++] = 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');
+ }
+}