@xterm/xterm 6.1.0-beta.183 → 6.1.0-beta.184
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/xterm.js +1 -1
- package/lib/xterm.js.map +1 -1
- package/lib/xterm.mjs +8 -8
- package/lib/xterm.mjs.map +4 -4
- package/package.json +2 -2
- package/src/browser/CoreBrowserTerminal.ts +15 -277
- package/src/browser/Linkifier.ts +8 -8
- package/src/browser/services/MouseCoordsService.ts +47 -0
- package/src/browser/services/MouseService.ts +309 -30
- package/src/browser/services/SelectionService.ts +4 -4
- package/src/browser/services/Services.ts +16 -3
- package/src/common/Version.ts +1 -1
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xterm/xterm",
|
|
3
3
|
"description": "Full xterm terminal, in your browser",
|
|
4
|
-
"version": "6.1.0-beta.
|
|
4
|
+
"version": "6.1.0-beta.184",
|
|
5
5
|
"main": "lib/xterm.js",
|
|
6
6
|
"module": "lib/xterm.mjs",
|
|
7
7
|
"style": "css/xterm.css",
|
|
@@ -119,5 +119,5 @@
|
|
|
119
119
|
"ws": "^8.2.3",
|
|
120
120
|
"xterm-benchmark": "^0.3.1"
|
|
121
121
|
},
|
|
122
|
-
"commit": "
|
|
122
|
+
"commit": "1400398abbba1c6849eff034d82da556ea7753d1"
|
|
123
123
|
}
|
|
@@ -36,16 +36,17 @@ import { CharSizeService } from 'browser/services/CharSizeService';
|
|
|
36
36
|
import { CharacterJoinerService } from 'browser/services/CharacterJoinerService';
|
|
37
37
|
import { CoreBrowserService } from 'browser/services/CoreBrowserService';
|
|
38
38
|
import { LinkProviderService } from 'browser/services/LinkProviderService';
|
|
39
|
+
import { MouseCoordsService } from 'browser/services/MouseCoordsService';
|
|
39
40
|
import { MouseService } from 'browser/services/MouseService';
|
|
40
41
|
import { RenderService } from 'browser/services/RenderService';
|
|
41
42
|
import { 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';
|
|
43
44
|
import { ThemeService } from 'browser/services/ThemeService';
|
|
44
45
|
import { KeyboardService } from 'browser/services/KeyboardService';
|
|
45
46
|
import { channels, color, rgb } from 'common/Color';
|
|
46
47
|
import { CoreTerminal } from 'common/CoreTerminal';
|
|
47
48
|
import * as Browser from 'common/Platform';
|
|
48
|
-
import { ColorRequestType,
|
|
49
|
+
import { ColorRequestType, IColorEvent, ITerminalOptions, KeyboardResultType, SpecialColorIndex } from 'common/Types';
|
|
49
50
|
import { DEFAULT_ATTR_DATA } from 'common/buffer/BufferLine';
|
|
50
51
|
import { IBuffer } from 'common/buffer/Types';
|
|
51
52
|
import { C0, C1ESCAPED } from 'common/data/EscapeSequences';
|
|
@@ -87,6 +88,7 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal {
|
|
|
87
88
|
// Optional browser services
|
|
88
89
|
private _charSizeService: ICharSizeService | undefined;
|
|
89
90
|
private _coreBrowserService: ICoreBrowserService | undefined;
|
|
91
|
+
private _mouseCoordsService: IMouseCoordsService | undefined;
|
|
90
92
|
private _mouseService: IMouseService | undefined;
|
|
91
93
|
private _renderService: IRenderService | undefined;
|
|
92
94
|
private _themeService: IThemeService | undefined;
|
|
@@ -543,8 +545,8 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal {
|
|
|
543
545
|
this._compositionHelper = this._instantiationService.createInstance(CompositionHelper, this.textarea, this._compositionView);
|
|
544
546
|
this._helperContainer.appendChild(this._compositionView);
|
|
545
547
|
|
|
546
|
-
this.
|
|
547
|
-
this._instantiationService.setService(
|
|
548
|
+
this._mouseCoordsService = this._instantiationService.createInstance(MouseCoordsService);
|
|
549
|
+
this._instantiationService.setService(IMouseCoordsService, this._mouseCoordsService);
|
|
548
550
|
|
|
549
551
|
const linkifier = this._linkifier.value = this._register(this._instantiationService.createInstance(Linkifier, this.screenElement));
|
|
550
552
|
|
|
@@ -579,6 +581,9 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal {
|
|
|
579
581
|
linkifier
|
|
580
582
|
));
|
|
581
583
|
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);
|
|
582
587
|
this._register(this._selectionService.onRequestScrollLines(e => this.scrollLines(e.amount, e.suppressScrollEvent)));
|
|
583
588
|
this._register(this._selectionService.onSelectionChange(() => this._onSelectionChange.fire()));
|
|
584
589
|
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 {
|
|
|
638
643
|
|
|
639
644
|
// Listen for mouse events and translate
|
|
640
645
|
// 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());
|
|
642
651
|
}
|
|
643
652
|
|
|
644
653
|
private _createRenderer(): IRenderer {
|
|
645
654
|
return this._instantiationService.createInstance(DomRenderer, this, this._document!, this.element!, this.screenElement!, this._viewportElement!, this._helperContainer!, this.linkifier!);
|
|
646
655
|
}
|
|
647
656
|
|
|
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
|
-
|
|
920
657
|
/**
|
|
921
658
|
* Tells the renderer to refresh terminal content between two rows (inclusive) at the next
|
|
922
659
|
* opportunity.
|
|
@@ -991,6 +728,7 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal {
|
|
|
991
728
|
|
|
992
729
|
public attachCustomWheelEventHandler(customWheelEventHandler: CustomWheelEventHandler): void {
|
|
993
730
|
this._customWheelEventHandler = customWheelEventHandler;
|
|
731
|
+
this._mouseService?.setCustomWheelEventHandler(customWheelEventHandler);
|
|
994
732
|
}
|
|
995
733
|
|
|
996
734
|
public registerLinkProvider(linkProvider: ILinkProvider): IDisposable {
|
package/src/browser/Linkifier.ts
CHANGED
|
@@ -7,7 +7,7 @@ import { IBufferCellPosition, ILink, ILinkDecorations, ILinkWithState, ILinkifie
|
|
|
7
7
|
import { Disposable, dispose, toDisposable } from 'common/Lifecycle';
|
|
8
8
|
import { IDisposable } from 'common/Types';
|
|
9
9
|
import { IBufferService } from 'common/services/Services';
|
|
10
|
-
import { ILinkProviderService,
|
|
10
|
+
import { ILinkProviderService, IMouseCoordsService, IRenderService } from './services/Services';
|
|
11
11
|
import { Emitter } from 'common/Event';
|
|
12
12
|
import { addDisposableListener } from 'browser/Dom';
|
|
13
13
|
|
|
@@ -30,7 +30,7 @@ export class Linkifier extends Disposable implements ILinkifier2 {
|
|
|
30
30
|
|
|
31
31
|
constructor(
|
|
32
32
|
private readonly _element: HTMLElement,
|
|
33
|
-
@
|
|
33
|
+
@IMouseCoordsService private readonly _mouseCoordsService: IMouseCoordsService,
|
|
34
34
|
@IRenderService private readonly _renderService: IRenderService,
|
|
35
35
|
@IBufferService private readonly _bufferService: IBufferService,
|
|
36
36
|
@ILinkProviderService private readonly _linkProviderService: ILinkProviderService
|
|
@@ -60,7 +60,7 @@ export class Linkifier extends Disposable implements ILinkifier2 {
|
|
|
60
60
|
private _handleMouseMove(event: MouseEvent): void {
|
|
61
61
|
this._lastMouseEvent = event;
|
|
62
62
|
|
|
63
|
-
const position = this._positionFromMouseEvent(event, this._element
|
|
63
|
+
const position = this._positionFromMouseEvent(event, this._element);
|
|
64
64
|
if (!position) {
|
|
65
65
|
return;
|
|
66
66
|
}
|
|
@@ -222,7 +222,7 @@ export class Linkifier extends Disposable implements ILinkifier2 {
|
|
|
222
222
|
return;
|
|
223
223
|
}
|
|
224
224
|
|
|
225
|
-
const position = this._positionFromMouseEvent(event, this._element
|
|
225
|
+
const position = this._positionFromMouseEvent(event, this._element);
|
|
226
226
|
if (!position) {
|
|
227
227
|
return;
|
|
228
228
|
}
|
|
@@ -251,7 +251,7 @@ export class Linkifier extends Disposable implements ILinkifier2 {
|
|
|
251
251
|
return;
|
|
252
252
|
}
|
|
253
253
|
|
|
254
|
-
const position = this._positionFromMouseEvent(this._lastMouseEvent, this._element
|
|
254
|
+
const position = this._positionFromMouseEvent(this._lastMouseEvent, this._element);
|
|
255
255
|
|
|
256
256
|
if (!position) {
|
|
257
257
|
return;
|
|
@@ -312,7 +312,7 @@ export class Linkifier extends Disposable implements ILinkifier2 {
|
|
|
312
312
|
this._clearCurrentLink(start, end);
|
|
313
313
|
if (this._lastMouseEvent) {
|
|
314
314
|
// re-eval previously active link after changes
|
|
315
|
-
const position = this._positionFromMouseEvent(this._lastMouseEvent, this._element
|
|
315
|
+
const position = this._positionFromMouseEvent(this._lastMouseEvent, this._element);
|
|
316
316
|
if (position) {
|
|
317
317
|
this._askForLink(position, false);
|
|
318
318
|
}
|
|
@@ -378,8 +378,8 @@ export class Linkifier extends Disposable implements ILinkifier2 {
|
|
|
378
378
|
* Get the buffer position from a mouse event
|
|
379
379
|
* @param event
|
|
380
380
|
*/
|
|
381
|
-
private _positionFromMouseEvent(event: MouseEvent, element: HTMLElement
|
|
382
|
-
const coords =
|
|
381
|
+
private _positionFromMouseEvent(event: MouseEvent, element: HTMLElement): IBufferCellPosition | undefined {
|
|
382
|
+
const coords = this._mouseCoordsService.getCoords(event, element, this._bufferService.cols, this._bufferService.rows);
|
|
383
383
|
if (!coords) {
|
|
384
384
|
return;
|
|
385
385
|
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2026 The xterm.js authors. All rights reserved.
|
|
3
|
+
* @license MIT
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { getWindow } from 'browser/Dom';
|
|
7
|
+
import { getCoords, getCoordsRelativeToElement } from 'browser/input/Mouse';
|
|
8
|
+
import { ICharSizeService, IMouseCoordsService, IRenderService } from 'browser/services/Services';
|
|
9
|
+
|
|
10
|
+
export class MouseCoordsService implements IMouseCoordsService {
|
|
11
|
+
public serviceBrand: undefined;
|
|
12
|
+
|
|
13
|
+
constructor(
|
|
14
|
+
@ICharSizeService private readonly _charSizeService: ICharSizeService,
|
|
15
|
+
@IRenderService private readonly _renderService: IRenderService
|
|
16
|
+
) {
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
public getCoords(event: {clientX: number, clientY: number}, element: HTMLElement, colCount: number, rowCount: number, isSelection?: boolean): [number, number] | undefined {
|
|
20
|
+
return getCoords(
|
|
21
|
+
window,
|
|
22
|
+
event,
|
|
23
|
+
element,
|
|
24
|
+
colCount,
|
|
25
|
+
rowCount,
|
|
26
|
+
this._charSizeService.hasValidSize,
|
|
27
|
+
this._renderService.dimensions.css.cell.width,
|
|
28
|
+
this._renderService.dimensions.css.cell.height,
|
|
29
|
+
isSelection
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
public getMouseReportCoords(event: MouseEvent, element: HTMLElement): { col: number, row: number, x: number, y: number } | undefined {
|
|
34
|
+
const coords = getCoordsRelativeToElement(getWindow(element), event, element);
|
|
35
|
+
if (!this._charSizeService.hasValidSize) {
|
|
36
|
+
return undefined;
|
|
37
|
+
}
|
|
38
|
+
coords[0] = Math.min(Math.max(coords[0], 0), this._renderService.dimensions.css.canvas.width - 1);
|
|
39
|
+
coords[1] = Math.min(Math.max(coords[1], 0), this._renderService.dimensions.css.canvas.height - 1);
|
|
40
|
+
return {
|
|
41
|
+
col: Math.floor(coords[0] / this._renderService.dimensions.css.cell.width),
|
|
42
|
+
row: Math.floor(coords[1] / this._renderService.dimensions.css.cell.height),
|
|
43
|
+
x: Math.floor(coords[0]),
|
|
44
|
+
y: Math.floor(coords[1])
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
}
|