2017-08-28 19:27:45 +03:00
/ * *
* @ description Remote Desktop
* @ author Ylian Saint - Hilaire
* @ version v0 . 0.2 g
* /
// Construct a MeshServer object
var CreateAmtRemoteDesktop = function ( divid , scrolldiv ) {
var obj = { } ;
obj . canvasid = divid ;
2018-03-31 01:26:36 +03:00
obj . CanvasId = Q ( divid ) ;
2017-08-28 19:27:45 +03:00
obj . scrolldiv = scrolldiv ;
2020-05-28 13:14:08 +03:00
obj . canvas = Q ( divid ) . getContext ( '2d' ) ;
2017-08-28 19:27:45 +03:00
obj . protocol = 2 ; // KVM
obj . state = 0 ;
2020-05-28 13:14:08 +03:00
obj . acc = null ;
2017-08-28 19:27:45 +03:00
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 ;
2019-03-10 01:28:08 +03:00
obj . localKeyMap = true ;
2017-08-28 19:27:45 +03:00
obj . spare = null ;
obj . sparew = 0 ;
obj . spareh = 0 ;
obj . sparew2 = 0 ;
obj . spareh2 = 0 ;
obj . sparecache = { } ;
obj . ZRLEfirst = 1 ;
obj . onScreenSizeChange = null ;
obj . frameRateDelay = 0 ;
// ###BEGIN###{DesktopRotation}
2018-01-19 02:43:43 +03:00
obj . noMouseRotate = false ;
2017-08-28 19:27:45 +03:00
obj . rotation = 0 ;
// ###END###{DesktopRotation}
2018-09-22 02:34:35 +03:00
// ###BEGIN###{DesktopInband}
obj . kvmDataSupported = false ;
obj . onKvmData = null ;
obj . onKvmDataPending = [ ] ;
obj . onKvmDataAck = - 1 ;
obj . holding = false ;
obj . lastKeepAlive = Date . now ( ) ;
// ###END###{DesktopInband}
2018-01-19 02:43:43 +03:00
2019-07-11 00:27:38 +03:00
obj . mNagleTimer = null ; // Mouse motion slowdown timer
2017-08-28 19:27:45 +03:00
obj . mx = 0 ; // Last mouse x position
obj . my = 0 ; // Last mouse y position
2019-07-11 00:27:38 +03:00
// ###BEGIN###{DesktopFocus}
2017-08-28 19:27:45 +03:00
obj . ox = - 1 ; // Old mouse x position
obj . oy = - 1 ; // Old mouse y position
obj . focusmode = 0 ;
// ###END###{DesktopFocus}
2018-01-19 02:43:43 +03:00
// ###BEGIN###{Inflate}
obj . inflate = ZLIB . inflateInit ( - 15 ) ;
// ###END###{Inflate}
2017-08-28 19:27:45 +03:00
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 ;
2019-02-06 23:05:55 +03:00
QS ( obj . canvasid ) . cursor = 'default' ;
2017-08-28 19:27:45 +03:00
} else {
2020-05-29 02:24:48 +03:00
QS ( obj . canvasid ) . cursor = obj . showmouse ? 'default' : 'none' ;
2017-08-28 19:27:45 +03:00
}
}
2018-01-19 02:43:43 +03:00
2020-05-28 13:14:08 +03:00
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 ) ) {
2017-08-28 19:27:45 +03:00
// Getting handshake & version
cmdsize = 12 ;
//if (obj.acc.substring(0, 4) != "RFB ") { return obj.Stop(); }
//var version = parseFloat(obj.acc.substring(4, 11));
2020-05-28 13:14:08 +03:00
//console.log("KVersion: " + version);
2017-08-28 19:27:45 +03:00
obj . state = 1 ;
2020-05-28 13:14:08 +03:00
obj . send ( 'RFB 003.008\n' ) ;
2017-08-28 19:27:45 +03:00
}
2020-05-28 13:14:08 +03:00
else if ( ( obj . state == 1 ) && ( obj . acc . byteLength >= 1 ) ) {
2017-08-28 19:27:45 +03:00
// Getting security options
2020-05-28 13:14:08 +03:00
cmdsize = obj . acc [ 0 ] + 1 ;
2018-02-12 04:13:26 +03:00
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.
2017-08-28 19:27:45 +03:00
obj . state = 2 ;
}
2020-05-28 13:14:08 +03:00
else if ( ( obj . state == 2 ) && ( obj . acc . byteLength >= 4 ) ) {
2017-08-28 19:27:45 +03:00
// Getting security response
cmdsize = 4 ;
2020-05-28 13:14:08 +03:00
if ( accview . getUint32 ( 0 ) != 0 ) { return obj . Stop ( ) ; }
2018-02-12 04:13:26 +03:00
obj . send ( String . fromCharCode ( 1 ) ) ; // Send share desktop flag
2017-08-28 19:27:45 +03:00
obj . state = 3 ;
}
2020-05-28 13:14:08 +03:00
else if ( ( obj . state == 3 ) && ( obj . acc . byteLength >= 24 ) ) {
2017-08-28 19:27:45 +03:00
// Getting server init
2018-01-19 02:43:43 +03:00
// ###BEGIN###{DesktopRotation}
obj . rotation = 0 ; // We don't currently support screen init while rotated.
// ###END###{DesktopRotation}
2020-05-28 13:14:08 +03:00
var namelen = accview . getUint32 ( 20 ) ;
if ( obj . acc . byteLength < 24 + namelen ) return ;
2017-08-28 19:27:45 +03:00
cmdsize = 24 + namelen ;
2020-05-28 13:14:08 +03:00
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 ) ;
2018-01-19 02:43:43 +03:00
2017-08-28 19:27:45 +03:00
// 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.
/ *
2020-05-28 13:14:08 +03:00
obj . xbpp = obj . acc [ 4 ] ;
obj . depth = obj . acc [ 5 ] ;
obj . bigend = obj . acc [ 6 ] ;
obj . truecolor = obj . acc [ 7 ] ;
2017-08-28 19:27:45 +03:00
obj . rmax = ReadShort ( obj . acc , 8 ) ;
obj . gmax = ReadShort ( obj . acc , 10 ) ;
obj . bmax = ReadShort ( obj . acc , 12 ) ;
2020-05-28 13:14:08 +03:00
obj . rsh = obj . acc [ 14 ] ;
obj . gsh = obj . acc [ 15 ] ;
obj . bsh = obj . acc [ 16 ] ;
2017-08-28 19:27:45 +03:00
var name = obj . acc . substring ( 24 , 24 + namelen ) ;
2020-05-28 13:14:08 +03:00
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 ) ;
2017-08-28 19:27:45 +03:00
* /
// 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 ) ;
2018-09-22 02:34:35 +03:00
// ###BEGIN###{DesktopInband}
supportedEncodings += IntToStr ( 1092 ) ;
// ###END###{DesktopInband}
2018-01-19 02:43:43 +03:00
2018-02-12 04:13:26 +03:00
obj . send ( String . fromCharCode ( 2 , 0 ) + ShortToStr ( ( supportedEncodings . length / 4 ) + 1 ) + supportedEncodings + IntToStr ( - 223 ) ) ; // Supported Encodings + Desktop Size
2018-01-19 02:43:43 +03:00
2017-08-28 19:27:45 +03:00
// Set the pixel encoding to something much smaller
2018-02-12 04:13:26 +03:00
// 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
2018-01-19 02:43:43 +03:00
2017-08-28 19:27:45 +03:00
obj . state = 4 ;
2019-08-13 02:16:52 +03:00
if ( obj . parent ) { obj . parent . xxStateChange ( 3 ) ; }
2017-08-28 19:27:45 +03:00
_SendRefresh ( ) ;
//obj.timer = setInterval(obj.xxOnTimer, 50);
2018-01-19 02:43:43 +03:00
2017-08-28 19:27:45 +03:00
// ###BEGIN###{DesktopFocus}
obj . ox = - 1 ; // Old mouse x position
// ###END###{DesktopFocus}
2018-01-19 02:43:43 +03:00
2017-08-28 19:27:45 +03:00
if ( obj . onScreenSizeChange != null ) { obj . onScreenSizeChange ( obj , obj . ScreenWidth , obj . ScreenHeight ) ; }
}
else if ( obj . state == 4 ) {
2020-05-28 13:14:08 +03:00
switch ( obj . acc [ 0 ] ) {
2018-09-22 02:34:35 +03:00
case 0 : // FramebufferUpdate
2020-05-28 13:14:08 +03:00
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.
2018-09-22 02:34:35 +03:00
cmdsize = 4 ;
break ;
case 2 : // This is the bell, do nothing.
cmdsize = 1 ;
break ;
case 3 : // This is ServerCutText
2020-05-28 13:14:08 +03:00
if ( obj . acc . byteLength < 8 ) return ;
var len = accview . getUint32 ( 4 ) + 8 ;
if ( obj . acc . byteLength < len ) return ;
cmdsize = handleServerCutText ( obj . acc , accview ) ;
2018-09-22 02:34:35 +03:00
break ;
2017-08-28 19:27:45 +03:00
}
}
2020-05-28 13:14:08 +03:00
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 ) ,
2017-08-28 19:27:45 +03:00
s = width * height ,
2020-05-28 13:14:08 +03:00
encoding = accview . getUint32 ( 8 ) ;
2018-01-19 02:43:43 +03:00
2017-08-28 19:27:45 +03:00
if ( encoding < 17 ) {
2020-05-28 13:14:08 +03:00
if ( ( width < 1 ) || ( width > 64 ) || ( height < 1 ) || ( height > 64 ) ) { console . log ( "Invalid tile size (" + width + "," + height + "), disconnecting." ) ; return obj . Stop ( ) ; }
2018-01-19 02:43:43 +03:00
2020-05-28 13:14:08 +03:00
// 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 ) ) {
2017-08-28 19:27:45 +03:00
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 ] ;
2019-05-28 20:10:56 +03:00
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.
}
2017-08-28 19:27:45 +03:00
}
}
2018-01-19 02:43:43 +03:00
2017-08-28 19:27:45 +03:00
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 ;
2018-02-12 04:13:26 +03:00
obj . send ( String . fromCharCode ( 3 , 0 , 0 , 0 , 0 , 0 ) + ShortToStr ( obj . width ) + ShortToStr ( obj . height ) ) ; // FramebufferUpdateRequest
2017-08-28 19:27:45 +03:00
cmdsize = 12 ;
if ( obj . onScreenSizeChange != null ) { obj . onScreenSizeChange ( obj , obj . ScreenWidth , obj . ScreenHeight ) ; }
2020-05-28 13:14:08 +03:00
//console.log("New desktop width: " + obj.width + ", height: " + obj.height);
} else if ( encoding == 0 ) {
2017-08-28 19:27:45 +03:00
// RAW encoding
var ptr = 12 , cs = 12 + ( s * obj . bpp ) ;
2020-05-28 13:14:08 +03:00
if ( obj . acc . byteLength < cs ) return ; // Check we have all the data needed and we can only draw 64x64 tiles.
2017-08-28 19:27:45 +03:00
cmdsize = cs ;
2018-01-19 02:43:43 +03:00
2017-08-28 19:27:45 +03:00
// CRITICAL LOOP, optimize this as much as possible
2019-05-28 20:10:56 +03:00
if ( obj . bpp == 2 ) {
2020-05-28 13:14:08 +03:00
for ( var i = 0 ; i < s ; i ++ ) { _setPixel16 ( accview . getUint16 ( ptr , true ) , i ) ; ptr += 2 ; }
2019-05-28 20:10:56 +03:00
} else {
2020-05-28 13:14:08 +03:00
for ( var i = 0 ; i < s ; i ++ ) { _setPixel8 ( obj . acc [ ptr ++ ] , i ) ; }
2019-05-28 20:10:56 +03:00
}
2017-08-28 19:27:45 +03:00
_putImage ( obj . spare , x , y ) ;
2020-05-28 13:14:08 +03:00
} else if ( encoding == 16 ) {
2017-08-28 19:27:45 +03:00
// ZRLE encoding
2020-05-28 13:14:08 +03:00
if ( obj . acc . byteLength < 16 ) return ;
var datalen = accview . getUint32 ( 12 ) ;
if ( obj . acc . byteLength < ( 16 + datalen ) ) return ;
2018-01-19 02:43:43 +03:00
2017-08-28 19:27:45 +03:00
// Process the ZLib header if this is the first block
var ptr = 16 , delta = 5 , dx = 0 ;
2018-01-19 02:43:43 +03:00
2020-05-28 13:14:08 +03:00
if ( ( datalen > 5 ) && ( obj . acc [ ptr ] == 0 ) && ( accview . getUint16 ( ptr + 1 , true ) == ( datalen - delta ) ) ) {
2017-08-28 19:27:45 +03:00
// This is an uncompressed ZLib data block
_decodeLRE ( obj . acc , ptr + 5 , x , y , width , height , s , datalen ) ;
}
2018-01-19 02:43:43 +03:00
// ###BEGIN###{Inflate}
2017-08-28 19:27:45 +03:00
else {
2020-05-28 13:14:08 +03:00
// This is compressed ZLib data, decompress and process it. (TODO: This need to be optimized, remove str/arr conversions)
2020-05-29 02:24:48 +03:00
var str = obj . inflate . inflate ( arrToStr ( new Uint8Array ( obj . acc . buffer . slice ( ptr , ptr + datalen - dx ) ) ) ) ;
2020-05-28 13:14:08 +03:00
if ( str . length > 0 ) { _decodeLRE ( strToArr ( str ) , 0 , x , y , width , height , s , str . length ) ; } else { console . log ( "Invalid deflate data" ) ; }
2017-08-28 19:27:45 +03:00
}
// ###END###{Inflate}
2018-01-19 02:43:43 +03:00
2017-08-28 19:27:45 +03:00
cmdsize = 16 + datalen ;
2020-05-28 13:14:08 +03:00
} else { return obj . Stop ( ) ; }
2017-08-28 19:27:45 +03:00
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
}
}
}
2018-01-19 02:43:43 +03:00
2020-05-28 13:14:08 +03:00
//console.log('cmdsize', cmdsize);
2017-08-28 19:27:45 +03:00
if ( cmdsize == 0 ) return ;
2020-05-29 02:24:48 +03:00
if ( cmdsize != obj . acc . byteLength ) { obj . acc = new Uint8Array ( obj . acc . buffer . slice ( cmdsize ) ) ; } else { obj . acc = null ; }
2017-08-28 19:27:45 +03:00
}
}
2018-01-19 02:43:43 +03:00
2017-08-28 19:27:45 +03:00
function _decodeLRE ( data , ptr , x , y , width , height , s , datalen ) {
2020-05-28 13:14:08 +03:00
var subencoding = data [ ptr ++ ] , index , v , runlengthdecode , palette = { } , rlecount = 0 , runlength = 0 , i ;
2017-08-28 19:27:45 +03:00
if ( subencoding == 0 ) {
// RAW encoding
2019-05-28 20:10:56 +03:00
if ( obj . bpp == 2 ) {
2020-05-28 13:14:08 +03:00
for ( i = 0 ; i < s ; i ++ ) { _setPixel16 ( data [ ptr ++ ] + ( data [ ptr ++ ] << 8 ) , i ) ; }
2019-05-28 20:10:56 +03:00
} else {
2020-05-28 13:14:08 +03:00
for ( i = 0 ; i < s ; i ++ ) { _setPixel8 ( data [ ptr ++ ] , i ) ; }
2019-05-28 20:10:56 +03:00
}
2017-08-28 19:27:45 +03:00
_putImage ( obj . spare , x , y ) ;
}
else if ( subencoding == 1 ) {
// Solid color tile
2020-05-28 13:14:08 +03:00
v = data [ ptr ++ ] + ( ( obj . bpp == 2 ) ? ( data [ ptr ++ ] << 8 ) : 0 ) ;
2017-08-28 19:27:45 +03:00
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 ) ) ) + ')' ;
2018-01-19 02:43:43 +03:00
2017-08-28 19:27:45 +03:00
// ###BEGIN###{DesktopRotation}
var xx = _rotX ( x , y ) ;
y = _rotY ( x , y ) ;
x = xx ;
// ###END###{DesktopRotation}
2018-01-19 02:43:43 +03:00
2017-08-28 19:27:45 +03:00
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.
2019-05-28 20:10:56 +03:00
if ( obj . bpp == 2 ) {
2020-05-28 13:14:08 +03:00
for ( i = 0 ; i < subencoding ; i ++ ) { palette [ i ] = data [ ptr ++ ] + ( data [ ptr ++ ] << 8 ) ; }
2019-05-28 20:10:56 +03:00
if ( subencoding == 2 ) { br = 1 ; bm = 1 ; } else if ( subencoding <= 4 ) { br = 2 ; bm = 3 ; } // Compute bits to read & bit mark
2020-05-28 13:14:08 +03:00
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
2019-05-28 20:10:56 +03:00
} else {
2020-05-28 13:14:08 +03:00
for ( i = 0 ; i < subencoding ; i ++ ) { palette [ i ] = data [ ptr ++ ] ; }
2019-05-28 20:10:56 +03:00
if ( subencoding == 2 ) { br = 1 ; bm = 1 ; } else if ( subencoding <= 4 ) { br = 2 ; bm = 3 ; } // Compute bits to read & bit mark
2020-05-29 02:24:48 +03:00
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
2019-05-28 20:10:56 +03:00
}
2017-08-28 19:27:45 +03:00
_putImage ( obj . spare , x , y ) ;
}
else if ( subencoding == 128 ) { // RLE encoded tile
2019-05-28 20:10:56 +03:00
if ( obj . bpp == 2 ) {
2020-05-28 13:14:08 +03:00
while ( rlecount < s && ptr < data . byteLength ) {
2019-05-28 20:10:56 +03:00
// Get the run color
2020-05-28 13:14:08 +03:00
v = data [ ptr ++ ] + ( data [ ptr ++ ] << 8 ) ;
2018-01-19 02:43:43 +03:00
2019-05-28 20:10:56 +03:00
// Decode the run length. This is the fastest and most compact way I found to do this.
2020-05-28 13:14:08 +03:00
runlength = 1 ; do { runlength += ( runlengthdecode = data [ ptr ++ ] ) ; } while ( runlengthdecode == 255 ) ;
2018-01-19 02:43:43 +03:00
2019-05-28 20:10:56 +03:00
// Draw a run
if ( obj . rotation == 0 ) {
_setPixel16run ( v , rlecount , runlength ) ; rlecount += runlength ;
} else {
while ( -- runlength >= 0 ) { _setPixel16 ( v , rlecount ++ ) ; }
}
}
} else {
2020-05-28 13:14:08 +03:00
while ( rlecount < s && ptr < data . byteLength ) {
2019-05-28 20:10:56 +03:00
// Get the run color
2020-05-28 13:14:08 +03:00
v = data [ ptr ++ ] ;
2019-05-28 20:10:56 +03:00
// Decode the run length. This is the fastest and most compact way I found to do this.
2020-05-28 13:14:08 +03:00
runlength = 1 ; do { runlength += ( runlengthdecode = data [ ptr ++ ] ) ; } while ( runlengthdecode == 255 ) ;
2019-05-28 20:10:56 +03:00
// Draw a run
if ( obj . rotation == 0 ) {
_setPixel8run ( v , rlecount , runlength ) ; rlecount += runlength ;
} else {
while ( -- runlength >= 0 ) { _setPixel8 ( v , rlecount ++ ) ; }
}
}
2017-08-28 19:27:45 +03:00
}
_putImage ( obj . spare , x , y ) ;
}
else if ( subencoding > 129 ) { // Palette RLE encoded tile
// Read the palette
2019-05-28 20:10:56 +03:00
if ( obj . bpp == 2 ) {
2020-05-29 02:24:48 +03:00
for ( i = 0 ; i < ( subencoding - 128 ) ; i ++ ) { palette [ i ] = data [ ptr ++ ] + ( data [ ptr ++ ] << 8 ) ; }
2019-05-28 20:10:56 +03:00
} else {
2020-05-29 02:24:48 +03:00
for ( i = 0 ; i < ( subencoding - 128 ) ; i ++ ) { palette [ i ] = data [ ptr ++ ] ; }
2019-05-28 20:10:56 +03:00
}
2018-01-19 02:43:43 +03:00
2017-08-28 19:27:45 +03:00
// Decode RLE on palette
2020-05-28 13:14:08 +03:00
while ( rlecount < s && ptr < data . byteLength ) {
2017-08-28 19:27:45 +03:00
// Setup the run, get the color index and get the color from the palette.
2020-05-28 13:14:08 +03:00
runlength = 1 ; index = data [ ptr ++ ] ; v = palette [ index % 128 ] ;
2018-01-19 02:43:43 +03:00
2017-08-28 19:27:45 +03:00
// If the index starts with high order bit 1, this is a run and decode the run length.
2020-05-28 13:14:08 +03:00
if ( index > 127 ) { do { runlength += ( runlengthdecode = data [ ptr ++ ] ) ; } while ( runlengthdecode == 255 ) ; }
2018-01-19 02:43:43 +03:00
2017-08-28 19:27:45 +03:00
// Draw a run
2019-05-28 20:10:56 +03:00
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 ++ ) ; }
}
}
2017-08-28 19:27:45 +03:00
}
_putImage ( obj . spare , x , y ) ;
}
}
2018-01-19 02:43:43 +03:00
2018-09-22 02:34:35 +03:00
// ###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}
2017-08-28 19:27:45 +03:00
function _putImage ( i , x , y ) {
2018-09-22 02:34:35 +03:00
// ###BEGIN###{DesktopInband}
if ( obj . holding == true ) return ;
// ###END###{DesktopInband}
2017-08-28 19:27:45 +03:00
// ###BEGIN###{DesktopRotation}
var xx = _arotX ( x , y ) ;
y = _arotY ( x , y ) ;
x = xx ;
// ###END###{DesktopRotation}
obj . canvas . putImageData ( i , x , y ) ;
}
2018-01-19 02:43:43 +03:00
2019-05-28 20:10:56 +03:00
// Set 8bit color RGB332
function _setPixel8 ( v , p ) {
var pp = p << 2 ;
2018-01-19 02:43:43 +03:00
2017-08-28 19:27:45 +03:00
// ###BEGIN###{DesktopRotation}
if ( obj . rotation > 0 ) {
2019-05-28 20:10:56 +03:00
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 ; }
2017-08-28 19:27:45 +03:00
else if ( obj . rotation == 2 ) { pp = ( obj . sparew * obj . spareh * 4 ) - 4 - pp ; }
2019-05-28 20:10:56 +03:00
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 ; }
2017-08-28 19:27:45 +03:00
}
// ###END###{DesktopRotation}
2018-01-19 02:43:43 +03:00
2019-05-28 20:10:56 +03:00
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 ; }
2017-08-28 19:27:45 +03:00
}
2019-05-28 20:10:56 +03:00
// ###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 ; }
2017-08-28 19:27:45 +03:00
}
2018-01-19 02:43:43 +03:00
2017-08-28 19:27:45 +03:00
// ###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 ;
}
2018-01-19 02:43:43 +03:00
2017-08-28 19:27:45 +03:00
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 ;
}
2018-01-19 02:43:43 +03:00
2017-08-28 19:27:45 +03:00
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 ;
}
2018-01-19 02:43:43 +03:00
2017-08-28 19:27:45 +03:00
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 ;
}
2018-01-19 02:43:43 +03:00
2017-08-28 19:27:45 +03:00
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 ;
}
2018-01-19 02:43:43 +03:00
2017-08-28 19:27:45 +03:00
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 ;
}
2018-01-19 02:43:43 +03:00
2017-08-28 19:27:45 +03:00
obj . tcanvas = null ;
obj . setRotation = function ( x ) {
while ( x < 0 ) { x += 4 ; }
var newrotation = x % 4 ;
2018-09-22 02:34:35 +03:00
//console.log('hard-rot: ' + newrotation);
// ###BEGIN###{DesktopInband}
if ( obj . holding == true ) { obj . rotation = newrotation ; return ; }
// ###END###{DesktopInband}
2017-08-28 19:27:45 +03:00
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 ; }
2018-01-19 02:43:43 +03:00
2017-08-28 19:27:45 +03:00
// 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 ) ;
2018-01-19 02:43:43 +03:00
2017-08-28 19:27:45 +03:00
// 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 ) ) ;
2018-01-19 02:43:43 +03:00
2017-08-28 19:27:45 +03:00
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}
2018-01-19 02:43:43 +03:00
2017-08-28 19:27:45 +03:00
function _fixColor ( c ) { return ( c > 127 ) ? ( c + 32 ) : c ; }
2018-01-19 02:43:43 +03:00
2017-08-28 19:27:45 +03:00
function _SendRefresh ( ) {
2018-09-22 02:34:35 +03:00
// ###BEGIN###{DesktopInband}
if ( obj . holding == true ) return ;
// ###END###{DesktopInband}
2017-08-28 19:27:45 +03:00
// ###BEGIN###{DesktopFocus}
if ( obj . focusmode > 0 ) {
// Request only pixels around the last mouse position
var df = obj . focusmode * 2 ;
2018-02-12 04:13:26 +03:00
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
2017-08-28 19:27:45 +03:00
obj . ox = obj . mx ;
obj . oy = obj . my ;
2018-01-19 02:43:43 +03:00
} else {
2020-05-29 02:24:48 +03:00
// ###END###{DesktopFocus}
2017-08-28 19:27:45 +03:00
// Request the entire screen
2018-02-12 04:13:26 +03:00
obj . send ( String . fromCharCode ( 3 , 1 , 0 , 0 , 0 , 0 ) + ShortToStr ( obj . rwidth ) + ShortToStr ( obj . rheight ) ) ; // FramebufferUpdateRequest
2020-05-29 02:24:48 +03:00
// ###BEGIN###{DesktopFocus}
2018-01-19 02:43:43 +03:00
}
// ###END###{DesktopFocus}
2017-08-28 19:27:45 +03:00
}
2018-01-19 02:43:43 +03:00
2017-08-28 19:27:45 +03:00
obj . Start = function ( ) {
obj . state = 0 ;
2020-05-28 13:14:08 +03:00
obj . acc = null ;
2017-08-28 19:27:45 +03:00
obj . ZRLEfirst = 1 ;
// ###BEGIN###{Inflate}
2018-01-19 02:43:43 +03:00
obj . inflate . inflateReset ( ) ;
2017-08-28 19:27:45 +03:00
// ###END###{Inflate}
2018-09-22 02:34:35 +03:00
// ###BEGIN###{DesktopInband}
obj . onKvmDataPending = [ ] ;
obj . onKvmDataAck = - 1 ;
obj . kvmDataSupported = false ;
// ###END###{DesktopInband}
2017-08-28 19:27:45 +03:00
for ( var i in obj . sparecache ) { delete obj . sparecache [ i ] ; }
}
2018-01-19 02:43:43 +03:00
2020-05-28 13:14:08 +03:00
obj . Stop = function ( ) { obj . UnGrabMouseInput ( ) ; obj . UnGrabKeyInput ( ) ; if ( obj . parent ) { obj . parent . Stop ( ) ; } }
obj . send = function ( x ) { if ( obj . parent ) { obj . parent . send ( x ) ; } }
2018-01-19 02:43:43 +03:00
2019-01-07 07:23:29 +03:00
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 ] ;
}
2017-08-28 19:27:45 +03:00
/ *
Intel AMT only recognizes a small subset of keysym characters defined in the keysymdef . h so you don <EFBFBD> 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 ; }
2019-01-07 07:23:29 +03:00
2019-03-10 01:28:08 +03:00
if ( e . code && ( obj . localKeyMap == false ) ) {
2019-01-07 07:23:29 +03:00
// 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 ; // \
2019-06-01 00:48:13 +03:00
if ( k == 221 ) kk = 93 ; // ]
2019-01-07 07:23:29 +03:00
if ( k == 222 ) kk = 39 ; // '
//console.log('Key' + d + ": " + k + " = " + kk);
obj . sendkey ( kk , d ) ;
}
2017-08-28 19:27:45 +03:00
return obj . haltEvent ( e ) ;
}
2018-01-19 02:43:43 +03:00
2018-04-29 01:36:10 +03:00
obj . sendkey = function ( k , d ) {
2019-07-29 00:32:54 +03:00
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 ) ) ;
}
2018-04-29 01:36:10 +03:00
}
2018-01-19 02:43:43 +03:00
2020-05-28 13:14:08 +03:00
function handleServerCutText ( acc , accview ) {
if ( acc . byteLength < 8 ) return 0 ;
var len = accview . getUint32 ( 4 ) + 8 ;
if ( acc . byteLength < len ) return 0 ;
2018-09-22 02:34:35 +03:00
// ###BEGIN###{DesktopInband}
if ( obj . onKvmData != null ) {
2020-05-28 13:14:08 +03:00
var d = arrToStr ( acc . slice ( 8 , len ) ) ;
2018-09-22 02:34:35 +03:00
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)); }
2018-09-29 01:27:26 +03:00
if ( d . length >= 16 ) { obj . onKvmData ( d . substring ( 16 ) ) ; } // Event the data and ack
2018-09-22 02:34:35 +03:00
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 {
2018-09-29 01:27:26 +03:00
//if (urlvars && urlvars['kvmdatatrace']) { console.log('KVM-Send(' + x.length + '): ' + x); }
2018-09-22 02:34:35 +03:00
x = '\0KvmDataChannel\0' + x ;
2018-09-29 01:27:26 +03:00
obj . send ( String . fromCharCode ( 6 , 0 , 0 , 0 ) + IntToStr ( x . length ) + x ) ;
2018-09-22 02:34:35 +03:00
obj . onKvmDataAck = false ;
}
}
// Send a HWKVM keep alive if it's not been sent in the last 5 seconds.
obj . sendKeepAlive = function ( ) {
2018-09-29 01:27:26 +03:00
if ( obj . lastKeepAlive < Date . now ( ) - 5000 ) { obj . lastKeepAlive = Date . now ( ) ; obj . send ( String . fromCharCode ( 6 , 0 , 0 , 0 ) + IntToStr ( 16 ) + '\0KvmDataChannel\0' ) ; }
2018-09-22 02:34:35 +03:00
}
// ###END###{DesktopInband}
2017-08-28 19:27:45 +03:00
obj . SendCtrlAltDelMsg = function ( ) { obj . sendcad ( ) ; }
2018-04-29 01:36:10 +03:00
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
2018-01-19 02:43:43 +03:00
2017-08-28 19:27:45 +03:00
var _MouseInputGrab = false ;
var _KeyInputGrab = false ;
2018-01-19 02:43:43 +03:00
2017-08-28 19:27:45 +03:00
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 ;
}
2018-01-19 02:43:43 +03:00
2017-08-28 19:27:45 +03:00
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 ;
}
2018-01-19 02:43:43 +03:00
2017-08-28 19:27:45 +03:00
obj . GrabKeyInput = function ( ) {
if ( _KeyInputGrab == true ) return ;
document . onkeyup = obj . handleKeyUp ;
document . onkeydown = obj . handleKeyDown ;
document . onkeypress = obj . handleKeys ;
_KeyInputGrab = true ;
}
2018-01-19 02:43:43 +03:00
2017-08-28 19:27:45 +03:00
obj . UnGrabKeyInput = function ( ) {
if ( _KeyInputGrab == false ) return ;
document . onkeyup = null ;
document . onkeydown = null ;
document . onkeypress = null ;
_KeyInputGrab = false ;
}
2018-01-19 02:43:43 +03:00
2017-08-28 19:27:45 +03:00
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 ; }
2018-01-19 02:43:43 +03:00
2017-08-28 19:27:45 +03:00
// RFB "PointerEvent" and mouse handlers
2019-01-24 05:07:02 +03:00
obj . mousedblclick = function ( e ) { }
2019-07-11 00:27:38 +03:00
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 ;
2019-04-27 02:10:17 +03:00
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 ; }
2018-01-19 02:43:43 +03:00
2017-08-28 19:27:45 +03:00
// ###BEGIN###{DesktopRotation}
2018-01-19 02:43:43 +03:00
if ( obj . noMouseRotate != true ) {
obj . mx2 = _crotX ( obj . mx , obj . my ) ;
obj . my = _crotY ( obj . mx , obj . my ) ;
obj . mx = obj . mx2 ;
}
2017-08-28 19:27:45 +03:00
// ###END###{DesktopRotation}
2018-01-19 02:43:43 +03:00
2019-07-11 00:27:38 +03:00
// 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 ) ;
}
}
2018-01-19 02:43:43 +03:00
2017-08-28 19:27:45 +03:00
// ###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}
2018-01-19 02:43:43 +03:00
2017-08-28 19:27:45 +03:00
return obj . haltEvent ( e ) ;
}
2018-01-19 02:43:43 +03:00
2017-08-28 19:27:45 +03:00
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 ;
}
2018-01-19 02:43:43 +03:00
2017-08-28 19:27:45 +03:00
return obj ;
}