import { ChangeDetectionStrategy , Component , ElementRef , HostListener , Input , OnDestroy , OnInit , Output , Renderer2 , ViewChild , AfterViewChecked , } from '@angular/core' ; import { Client , Display , Keyboard , Mouse } from '@illgrenoble/guacamole-common-js' ; import { BehaviorSubject , Subscription } from 'rxjs' ; import { Observable } from 'rxjs/Observable' ; import { RemoteDesktopManager } from '../services' ; @ Component ( { selector : 'ngx-remote-desktop-display' , host : { class : 'ngx-remote-desktop-viewport' } , template : `
        <div class="ngx-remote-desktop-display" #display>
        </div>
    ` , changeDetection : ChangeDetectionStrategy . OnPush } ) export class DisplayComponent implements OnInit , OnDestroy , AfterViewChecked { /**
     * Emit the mouse move events to any subscribers
     */ @ Output ( ) public onMouseMove = new BehaviorSubject ( null ) ; /**
     * Remote desktop manager
     */ @ Input ( ) private manager : RemoteDesktopManager ; @ ViewChild ( 'display' ) private display : ElementRef ; /**
     * Remote desktop keyboard
     */ private keyboard : Keyboard ; /**
     * Remote desktop mouse
     */ private mouse : Mouse ; /**
     * Subscriptions
     */ private subscriptions : Subscription [ ] = [ ] ; constructor ( private viewport , private renderer ) { } /**
     * Create the display canvas when initialising the component
     */ ngOnInit ( ) { this . createDisplayCanvas ( ) ; this . bindSubscriptions ( ) ; } /**
     * Unbind all display input listeners when destroying the component
     */ ngOnDestroy ( ) { this . removeDisplay ( ) ; this . removeDisplayInputListeners ( ) ; this . unbindSubscriptions ( ) ; } ngAfterViewChecked ( ) { this . setDisplayScale ( ) ; } /**
     * Bind all subscriptions
     */ private bindSubscriptions ( ) { this . subscriptions . push ( this . manager . onKeyboardReset . subscribe ( _ => this . resetKeyboard ( ) ) ) ; this . subscriptions . push ( this . manager . onFocused . subscribe ( this . handleFocused . bind ( this ) ) ) ; } /**
     * Unbind all subscriptions
     */ private unbindSubscriptions ( ) { this . subscriptions . forEach ( subscription => subscription . unsubscribe ( ) ) ; } /**
     * Bind input listeners if display is focused, otherwise, unbind
     */ private handleFocused ( newFocused ) { if ( newFocused ) { this . bindDisplayInputListeners ( ) ; } else { this . removeDisplayInputListeners ( ) ; } } param event @ HostListener ( 'window:blur' , [ '$event' ] ) private onWindowBlur ( event ) { this . resetKeyboard ( ) ; } param event @ HostListener ( 'window:resize' , [ '$event' ] ) private onWindowResize ( event ) { this . setDisplayScale ( ) ; } /**
     * Create the remote desktop display and bind the event handlers
     */ private createDisplayCanvas ( ) { this . createDisplay ( ) ; this . createDisplayInputs ( ) ; this . bindDisplayInputListeners ( ) ; } /**
     * Get the remote desktop display and set the scale
     */ private setDisplayScale ( ) { const display = this . getDisplay ( ) ; const scale = this . calculateDisplayScale ( display ) ; display . scale ( scale ) ; } /**
     * Get the remote desktop display
     */ private getDisplay ( ) { return this . manager . getClient ( ) . getDisplay ( ) ; } /**
     * Get the remote desktop client
     */ private getClient ( ) { return this . manager . getClient ( ) ; } /**
     * Calculate the scale for the display
     */ private calculateDisplayScale ( display ) { const viewportElement = this . viewport . nativeElement ; const scale = Math . min ( viewportElement . clientWidth / display . getWidth ( ) , viewportElement . clientHeight / display . getHeight ( ) ) ; return scale ; } /**
     * Assign the display to the client
     */ private createDisplay ( ) { const element = this . display . nativeElement ; const display = this . getDisplay ( ) ; this . renderer . appendChild ( element , display . getElement ( ) ) ; } /**
     * Remove the display
     */ private removeDisplay ( ) { const element = this . display . nativeElement ; const display = this . getDisplay ( ) ; this . renderer . removeChild ( element , display . getElement ( ) ) ; } /**
     * Bind input listeners for keyboard and mouse
     */ private bindDisplayInputListeners ( ) { this . removeDisplayInputListeners ( ) ; this . mouse . onmousedown = this . mouse . onmouseup = this . mouse . onmousemove = this . handleMouseState . bind ( this ) ; this . keyboard . onkeyup = this . handleKeyUp . bind ( this ) ; this . keyboard . onkeydown = this . handleKeyDown . bind ( this ) ; } /**
     * Remove all input listeners
     */ private removeDisplayInputListeners ( ) { if ( this . keyboard ) { this . keyboard . onkeydown = null ; this . keyboard . onkeyup = null ; } if ( this . mouse ) { this . mouse . onmousedown = this . mouse . onmouseup = this . mouse . onmousemove = null ; } } /**
     * Create the keyboard and mouse inputs
     */ private createDisplayInputs ( ) { const display = this . display . nativeElement . children [ 0 ] ; this . mouse = new Mouse ( display ) ; this . keyboard = new Keyboard ( window . document ) ; } param mouseState private handleMouseState ( mouseState ) { const display = this . getDisplay ( ) ; const scale = display . getScale ( ) ; const scaledState = new Mouse . State ( mouseState . x / scale , mouseState . y / scale , mouseState . left , mouseState . middle , mouseState . right , mouseState . up , mouseState . down ) ; this . getClient ( ) . sendMouseState ( scaledState ) ; this . onMouseMove . next ( mouseState ) ; } /**
     * Resetting the keyboard will release all keys
     */ private resetKeyboard ( ) { if ( this . keyboard ) { this . keyboard . reset ( ) ; } } param key private handleKeyDown ( key ) { this . getClient ( ) . sendKeyEvent ( 1 , key ) ; } param key private handleKeyUp ( key ) { this . getClient ( ) . sendKeyEvent ( 0 , key ) ; } }