@@ -36,16 +36,17 @@ import { CharSizeService } from 'browser/services/CharSizeService';
3636import { CharacterJoinerService } from 'browser/services/CharacterJoinerService' ;
3737import { CoreBrowserService } from 'browser/services/CoreBrowserService' ;
3838import { LinkProviderService } from 'browser/services/LinkProviderService' ;
39+ import { MouseCoordsService } from 'browser/services/MouseCoordsService' ;
3940import { MouseService } from 'browser/services/MouseService' ;
4041import { RenderService } from 'browser/services/RenderService' ;
4142import { SelectionService } from 'browser/services/SelectionService' ;
42- import { ICharSizeService , ICharacterJoinerService , ICoreBrowserService , IKeyboardService , ILinkProviderService , IMouseService , IRenderService , ISelectionService , IThemeService } from 'browser/services/Services' ;
43+ import { ICharSizeService , ICharacterJoinerService , ICoreBrowserService , IKeyboardService , ILinkProviderService , IMouseCoordsService , IMouseService , IRenderService , ISelectionService , IThemeService } from 'browser/services/Services' ;
4344import { ThemeService } from 'browser/services/ThemeService' ;
4445import { KeyboardService } from 'browser/services/KeyboardService' ;
4546import { channels , color , rgb } from 'common/Color' ;
4647import { CoreTerminal } from 'common/CoreTerminal' ;
4748import * as Browser from 'common/Platform' ;
48- import { ColorRequestType , CoreMouseAction , CoreMouseButton , CoreMouseEventType , IColorEvent , ITerminalOptions , KeyboardResultType , SpecialColorIndex } from 'common/Types' ;
49+ import { ColorRequestType , IColorEvent , ITerminalOptions , KeyboardResultType , SpecialColorIndex } from 'common/Types' ;
4950import { DEFAULT_ATTR_DATA } from 'common/buffer/BufferLine' ;
5051import { IBuffer } from 'common/buffer/Types' ;
5152import { C0 , C1ESCAPED } from 'common/data/EscapeSequences' ;
@@ -87,6 +88,7 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal {
8788 // Optional browser services
8889 private _charSizeService : ICharSizeService | undefined ;
8990 private _coreBrowserService : ICoreBrowserService | undefined ;
91+ private _mouseCoordsService : IMouseCoordsService | undefined ;
9092 private _mouseService : IMouseService | undefined ;
9193 private _renderService : IRenderService | undefined ;
9294 private _themeService : IThemeService | undefined ;
@@ -543,8 +545,8 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal {
543545 this . _compositionHelper = this . _instantiationService . createInstance ( CompositionHelper , this . textarea , this . _compositionView ) ;
544546 this . _helperContainer . appendChild ( this . _compositionView ) ;
545547
546- this . _mouseService = this . _instantiationService . createInstance ( MouseService ) ;
547- this . _instantiationService . setService ( IMouseService , this . _mouseService ) ;
548+ this . _mouseCoordsService = this . _instantiationService . createInstance ( MouseCoordsService ) ;
549+ this . _instantiationService . setService ( IMouseCoordsService , this . _mouseCoordsService ) ;
548550
549551 const linkifier = this . _linkifier . value = this . _register ( this . _instantiationService . createInstance ( Linkifier , this . screenElement ) ) ;
550552
@@ -579,6 +581,9 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal {
579581 linkifier
580582 ) ) ;
581583 this . _instantiationService . setService ( ISelectionService , this . _selectionService ) ;
584+ this . _mouseService = this . _instantiationService . createInstance ( MouseService ) ;
585+ this . _mouseService . setCustomWheelEventHandler ( this . _customWheelEventHandler ) ;
586+ this . _instantiationService . setService ( IMouseService , this . _mouseService ) ;
582587 this . _register ( this . _selectionService . onRequestScrollLines ( e => this . scrollLines ( e . amount , e . suppressScrollEvent ) ) ) ;
583588 this . _register ( this . _selectionService . onSelectionChange ( ( ) => this . _onSelectionChange . fire ( ) ) ) ;
584589 this . _register ( this . _selectionService . onRequestRedraw ( e => this . _renderService ! . handleSelectionChanged ( e . start , e . end , e . columnSelectMode ) ) ) ;
@@ -638,285 +643,17 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal {
638643
639644 // Listen for mouse events and translate
640645 // them into terminal mouse protocols.
641- this . bindMouse ( ) ;
646+ this . _mouseService . bindMouse ( {
647+ element : this . element ! ,
648+ screenElement : this . screenElement ! ,
649+ document : this . _document !
650+ } , disposable => this . _register ( disposable ) , ( ) => this . focus ( ) ) ;
642651 }
643652
644653 private _createRenderer ( ) : IRenderer {
645654 return this . _instantiationService . createInstance ( DomRenderer , this , this . _document ! , this . element ! , this . screenElement ! , this . _viewportElement ! , this . _helperContainer ! , this . linkifier ! ) ;
646655 }
647656
648- /**
649- * Bind certain mouse events to the terminal.
650- * By default only 3 button + wheel up/down is ativated. For higher buttons
651- * no mouse report will be created. Typically the standard actions will be active.
652- *
653- * There are several reasons not to enable support for higher buttons/wheel:
654- * - Button 4 and 5 are typically used for history back and forward navigation,
655- * there is no straight forward way to supress/intercept those standard actions.
656- * - Support for higher buttons does not work in some platform/browser combinations.
657- * - Left/right wheel was not tested.
658- * - Emulators vary in mouse button support, typically only 3 buttons and
659- * wheel up/down work reliable.
660- *
661- * TODO: Move mouse event code into its own file.
662- */
663- public bindMouse ( ) : void {
664- const self = this ;
665- const el = this . element ! ;
666-
667- // send event to CoreMouseService
668- function sendEvent ( ev : MouseEvent | WheelEvent ) : boolean {
669- // Get mouse coordinates
670- const pos = self . _mouseService ?. getMouseReportCoords ( ev , self . screenElement ! ) ;
671- if ( ! pos ) {
672- return false ;
673- }
674-
675- let but : CoreMouseButton ;
676- let action : CoreMouseAction | undefined ;
677- switch ( ( ev as any ) . overrideType || ev . type ) {
678- case 'mousemove' :
679- action = CoreMouseAction . MOVE ;
680- if ( ev . buttons === undefined ) {
681- // buttons is not supported on macOS, try to get a value from button instead
682- but = CoreMouseButton . NONE ;
683- if ( ev . button !== undefined ) {
684- but = ev . button < 3 ? ev . button : CoreMouseButton . NONE ;
685- }
686- } else {
687- // according to MDN buttons only reports up to button 5 (AUX2)
688- but = ev . buttons & 1 ? CoreMouseButton . LEFT :
689- ev . buttons & 4 ? CoreMouseButton . MIDDLE :
690- ev . buttons & 2 ? CoreMouseButton . RIGHT :
691- CoreMouseButton . NONE ; // fallback to NONE
692- }
693- break ;
694- case 'mouseup' :
695- action = CoreMouseAction . UP ;
696- but = ev . button < 3 ? ev . button : CoreMouseButton . NONE ;
697- break ;
698- case 'mousedown' :
699- action = CoreMouseAction . DOWN ;
700- but = ev . button < 3 ? ev . button : CoreMouseButton . NONE ;
701- break ;
702- case 'wheel' :
703- if ( self . _customWheelEventHandler && self . _customWheelEventHandler ( ev as WheelEvent ) === false ) {
704- return false ;
705- }
706- const deltaY = ( ev as WheelEvent ) . deltaY ;
707- if ( deltaY === 0 ) {
708- return false ;
709- }
710- const lines = self . coreMouseService . consumeWheelEvent (
711- ev as WheelEvent ,
712- self . _renderService ?. dimensions ?. device ?. cell ?. height ,
713- self . _coreBrowserService ?. dpr
714- ) ;
715- if ( lines === 0 ) {
716- return false ;
717- }
718- action = deltaY < 0 ? CoreMouseAction . UP : CoreMouseAction . DOWN ;
719- but = CoreMouseButton . WHEEL ;
720- break ;
721- default :
722- // dont handle other event types by accident
723- return false ;
724- }
725-
726- // exit if we cannot determine valid button/action values
727- // do nothing for higher buttons than wheel
728- if ( action === undefined || but === undefined || but > CoreMouseButton . WHEEL ) {
729- return false ;
730- }
731-
732- return self . coreMouseService . triggerMouseEvent ( {
733- col : pos . col ,
734- row : pos . row ,
735- x : pos . x ,
736- y : pos . y ,
737- button : but ,
738- action,
739- ctrl : ev . ctrlKey ,
740- alt : ev . altKey ,
741- shift : ev . shiftKey
742- } ) ;
743- }
744-
745- /**
746- * Event listener state handling.
747- * We listen to the onProtocolChange event of CoreMouseService and put
748- * requested listeners in `requestedEvents`. With this the listeners
749- * have all bits to do the event listener juggling.
750- * Note: 'mousedown' currently is "always on" and not managed
751- * by onProtocolChange.
752- */
753- const requestedEvents : { [ key : string ] : ( ( ev : MouseEvent | WheelEvent ) => void ) | null } = {
754- mouseup : null ,
755- wheel : null ,
756- mousedrag : null ,
757- mousemove : null
758- } ;
759- const eventListeners : { [ key : string ] : ( ev : any ) => void | boolean } = {
760- mouseup : ( ev : MouseEvent ) => {
761- sendEvent ( ev ) ;
762- if ( ! ev . buttons ) {
763- // if no other button is held remove global handlers
764- this . _document ! . removeEventListener ( 'mouseup' , requestedEvents . mouseup ! ) ;
765- if ( requestedEvents . mousedrag ) {
766- this . _document ! . removeEventListener ( 'mousemove' , requestedEvents . mousedrag ) ;
767- }
768- }
769- } ,
770- wheel : ( ev : WheelEvent ) => {
771- sendEvent ( ev ) ;
772- ev . preventDefault ( ) ;
773- ev . stopPropagation ( ) ;
774- return false ;
775- } ,
776- mousedrag : ( ev : MouseEvent ) => {
777- // deal only with move while a button is held
778- if ( ev . buttons ) {
779- sendEvent ( ev ) ;
780- }
781- } ,
782- mousemove : ( ev : MouseEvent ) => {
783- // deal only with move without any button
784- if ( ! ev . buttons ) {
785- sendEvent ( ev ) ;
786- }
787- }
788- } ;
789- this . _register ( this . coreMouseService . onProtocolChange ( events => {
790- // apply global changes on events
791- if ( events ) {
792- if ( this . optionsService . rawOptions . logLevel === 'debug' ) {
793- this . _logService . debug ( 'Binding to mouse events:' , this . coreMouseService . explainEvents ( events ) ) ;
794- }
795- this . element ! . classList . add ( 'enable-mouse-events' ) ;
796- this . _selectionService ! . disable ( ) ;
797- } else {
798- this . _logService . debug ( 'Unbinding from mouse events.' ) ;
799- this . element ! . classList . remove ( 'enable-mouse-events' ) ;
800- this . _selectionService ! . enable ( ) ;
801- }
802-
803- // add/remove handlers from requestedEvents
804-
805- if ( ! ( events & CoreMouseEventType . MOVE ) ) {
806- el . removeEventListener ( 'mousemove' , requestedEvents . mousemove ! ) ;
807- requestedEvents . mousemove = null ;
808- } else if ( ! requestedEvents . mousemove ) {
809- el . addEventListener ( 'mousemove' , eventListeners . mousemove ) ;
810- requestedEvents . mousemove = eventListeners . mousemove ;
811- }
812-
813- if ( ! ( events & CoreMouseEventType . WHEEL ) ) {
814- el . removeEventListener ( 'wheel' , requestedEvents . wheel ! ) ;
815- requestedEvents . wheel = null ;
816- } else if ( ! requestedEvents . wheel ) {
817- el . addEventListener ( 'wheel' , eventListeners . wheel , { passive : false } ) ;
818- requestedEvents . wheel = eventListeners . wheel ;
819- }
820-
821- if ( ! ( events & CoreMouseEventType . UP ) ) {
822- this . _document ! . removeEventListener ( 'mouseup' , requestedEvents . mouseup ! ) ;
823- requestedEvents . mouseup = null ;
824- } else {
825- requestedEvents . mouseup ??= eventListeners . mouseup ;
826- }
827-
828- if ( ! ( events & CoreMouseEventType . DRAG ) ) {
829- this . _document ! . removeEventListener ( 'mousemove' , requestedEvents . mousedrag ! ) ;
830- requestedEvents . mousedrag = null ;
831- } else {
832- requestedEvents . mousedrag ??= eventListeners . mousedrag ;
833- }
834- } ) ) ;
835- // force initial onProtocolChange so we dont miss early mouse requests
836- this . coreMouseService . activeProtocol = this . coreMouseService . activeProtocol ;
837-
838- // Ensure document-level listeners are removed on dispose
839- this . _register ( toDisposable ( ( ) => {
840- if ( requestedEvents . mouseup ) {
841- this . _document ! . removeEventListener ( 'mouseup' , requestedEvents . mouseup ) ;
842- }
843- if ( requestedEvents . mousedrag ) {
844- this . _document ! . removeEventListener ( 'mousemove' , requestedEvents . mousedrag ) ;
845- }
846- } ) ) ;
847-
848- /**
849- * "Always on" event listeners.
850- */
851- this . _register ( addDisposableListener ( el , 'mousedown' , ( ev : MouseEvent ) => {
852- ev . preventDefault ( ) ;
853- this . focus ( ) ;
854-
855- // Don't send the mouse button to the pty if mouse events are disabled or
856- // if the selection manager is having selection forced (ie. a modifier is
857- // held).
858- if ( ! this . coreMouseService . areMouseEventsActive || this . _selectionService ! . shouldForceSelection ( ev ) ) {
859- return ;
860- }
861-
862- sendEvent ( ev ) ;
863-
864- // Register additional global handlers which should keep reporting outside
865- // of the terminal element.
866- // Note: Other emulators also do this for 'mousedown' while a button
867- // is held, we currently limit 'mousedown' to the terminal only.
868- if ( requestedEvents . mouseup ) {
869- this . _document ! . addEventListener ( 'mouseup' , requestedEvents . mouseup ) ;
870- }
871- if ( requestedEvents . mousedrag ) {
872- this . _document ! . addEventListener ( 'mousemove' , requestedEvents . mousedrag ) ;
873- }
874- } ) ) ;
875-
876- this . _register ( addDisposableListener ( el , 'wheel' , ( ev : WheelEvent ) => {
877- // do nothing, if app side handles wheel itself
878- if ( requestedEvents . wheel ) return ;
879-
880- if ( this . _customWheelEventHandler && this . _customWheelEventHandler ( ev ) === false ) {
881- return false ;
882- }
883-
884- if ( ! this . buffer . hasScrollback ) {
885- // Convert wheel events into up/down events when the buffer does not have scrollback, this
886- // enables scrolling in apps hosted in the alt buffer such as vim or tmux even when mouse
887- // events are not enabled.
888- // This used implementation used get the actual lines/partial lines scrolled from the
889- // viewport but since moving to the new viewport implementation has been simplified to
890- // simply send a single up or down sequence.
891-
892- // Do nothing if there's no vertical scroll
893- const deltaY = ( ev as WheelEvent ) . deltaY ;
894- if ( deltaY === 0 ) {
895- return false ;
896- }
897-
898- const lines = self . coreMouseService . consumeWheelEvent (
899- ev as WheelEvent ,
900- self . _renderService ?. dimensions ?. device ?. cell ?. height ,
901- self . _coreBrowserService ?. dpr
902- ) ;
903- if ( lines === 0 ) {
904- ev . preventDefault ( ) ;
905- ev . stopPropagation ( ) ;
906- return false ;
907- }
908-
909- // Construct and send sequences
910- const sequence = C0 . ESC + ( this . coreService . decPrivateModes . applicationCursorKeys ? 'O' : '[' ) + ( ev . deltaY < 0 ? 'A' : 'B' ) ;
911- this . coreService . triggerDataEvent ( sequence , true ) ;
912- ev . preventDefault ( ) ;
913- ev . stopPropagation ( ) ;
914- return false ;
915- }
916- } , { passive : false } ) ) ;
917- }
918-
919-
920657 /**
921658 * Tells the renderer to refresh terminal content between two rows (inclusive) at the next
922659 * opportunity.
@@ -991,6 +728,7 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal {
991728
992729 public attachCustomWheelEventHandler ( customWheelEventHandler : CustomWheelEventHandler ) : void {
993730 this . _customWheelEventHandler = customWheelEventHandler ;
731+ this . _mouseService ?. setCustomWheelEventHandler ( customWheelEventHandler ) ;
994732 }
995733
996734 public registerLinkProvider ( linkProvider : ILinkProvider ) : IDisposable {
0 commit comments