import { EventEmitter } from 'events' ; import Codec , { MessageType , MouseButton , ButtonState , ScrollAxis , } from './codec' ; import Logger from 'shared/libs/logger' ; export enum TdpClientEvent { IMAGE_FRAGMENT = 'imgfrag' , TDP_ERROR = 'tdperr' , WS_OPEN = 'wsopen' , WS_CLOSE = 'wsclose' } export default class Client extends EventEmitter { codec : Codec ; socket : WebSocket ; socketAddr : string ; username : string ; logger = Logger . create ( 'TDPClient' ) ; constructor ( socketAddr , username ) { super ( ) ; this . socketAddr = socketAddr ; this . codec = new Codec ( ) ; this . username = username ; } init ( ) { this . socket = new WebSocket ( this . socketAddr ) ; this . socket . binaryType = 'arraybuffer' ; this . socket . onopen = ( ) => { this . logger . info ( 'websocket is open' ) ; this . emit ( TdpClientEvent . WS_OPEN ) ; } ; this . socket . onmessage = ( ev ) => { this . processMessage ( ev . data as ArrayBuffer ) ; } ; this . socket . onerror = null ; this . socket . onclose = ( ) => { this . logger . info ( 'websocket is closed' ) ; this . socket . onopen = null ; this . socket . onmessage = null ; this . socket . onclose = null ; this . socket = null ; this . emit ( TdpClientEvent . WS_CLOSE ) ; } ; } processMessage ( buffer ) { const messageType = this . codec . decodeMessageType ( buffer ) ; try { if ( messageType === MessageType . PNG_FRAME ) { this . processFrame ( buffer ) ; } else if ( messageType === MessageType . ERROR ) { this . handleError ( new Error ( this . codec . decodeErrorMessage ( buffer ) ) ) ; } else { this . handleError ( new Error ( `recieved unsupported message type ${ messageType }` ) ) ; } } catch ( err ) { this . handleError ( err ) ; } } processFrame ( buffer ) { const { left , top } = this . codec . decodeRegion ( buffer ) ; const image = new Image ( ) ; image . onload = ( ) => this . emit ( TdpClientEvent . IMAGE_FRAGMENT , { image , left , top } ) ; image . src = this . codec . decodePng ( buffer ) ; } sendUsername ( username ) { this . socket ?. send ( this . codec . encodeUsername ( username ) ) ; } sendMouseMove ( x , y ) { this . socket . send ( this . codec . encodeMouseMove ( x , y ) ) ; } sendMouseButton ( button , state ) { this . socket . send ( this . codec . encodeMouseButton ( button , state ) ) ; } sendMouseWheelScroll ( axis , delta ) { this . socket . send ( this . codec . encodeMouseWheelScroll ( axis , delta ) ) ; } sendKeyboardInput ( code , state ) { const msg = this . codec . encodeKeyboardInput ( code , state ) ; if ( msg ) this . socket . send ( msg ) ; } resize ( w , h ) { this . socket ?. send ( this . codec . encodeScreenSpec ( w , h ) ) ; } handleError ( err ) { this . logger . error ( err ) ; this . emit ( TdpClientEvent . TDP_ERROR , err ) ; this . socket ?. close ( ) ; } nuke ( ) { this . removeAllListeners ( ) ; this . socket ?. close ( ) ; } } export type ImageFragment = { image : HTMLImageElement ; left : number ; top : number ; } ;