@xterm/xterm 6.1.0-beta.183 → 6.1.0-beta.185

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/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.183",
4
+ "version": "6.1.0-beta.185",
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": "73adfdfd56b411b6abcc162ba48cb9fbd071aaee"
122
+ "commit": "89980fc565c178d13588e135f8eb975f44a47712"
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, CoreMouseAction, CoreMouseButton, CoreMouseEventType, IColorEvent, ITerminalOptions, KeyboardResultType, SpecialColorIndex } from 'common/Types';
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';
@@ -77,7 +78,6 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal {
77
78
  public browser: IBrowser = Browser as any;
78
79
 
79
80
  private _customKeyEventHandler: CustomKeyEventHandler | undefined;
80
- private _customWheelEventHandler: CustomWheelEventHandler | undefined;
81
81
 
82
82
  // Browser services
83
83
  private readonly _decorationService: DecorationService;
@@ -87,6 +87,7 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal {
87
87
  // Optional browser services
88
88
  private _charSizeService: ICharSizeService | undefined;
89
89
  private _coreBrowserService: ICoreBrowserService | undefined;
90
+ private _mouseCoordsService: IMouseCoordsService | undefined;
90
91
  private _mouseService: IMouseService | undefined;
91
92
  private _renderService: IRenderService | undefined;
92
93
  private _themeService: IThemeService | undefined;
@@ -543,8 +544,8 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal {
543
544
  this._compositionHelper = this._instantiationService.createInstance(CompositionHelper, this.textarea, this._compositionView);
544
545
  this._helperContainer.appendChild(this._compositionView);
545
546
 
546
- this._mouseService = this._instantiationService.createInstance(MouseService);
547
- this._instantiationService.setService(IMouseService, this._mouseService);
547
+ this._mouseCoordsService = this._instantiationService.createInstance(MouseCoordsService);
548
+ this._instantiationService.setService(IMouseCoordsService, this._mouseCoordsService);
548
549
 
549
550
  const linkifier = this._linkifier.value = this._register(this._instantiationService.createInstance(Linkifier, this.screenElement));
550
551
 
@@ -579,6 +580,8 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal {
579
580
  linkifier
580
581
  ));
581
582
  this._instantiationService.setService(ISelectionService, this._selectionService);
583
+ this._mouseService = this._instantiationService.createInstance(MouseService);
584
+ this._instantiationService.setService(IMouseService, this._mouseService);
582
585
  this._register(this._selectionService.onRequestScrollLines(e => this.scrollLines(e.amount, e.suppressScrollEvent)));
583
586
  this._register(this._selectionService.onSelectionChange(() => this._onSelectionChange.fire()));
584
587
  this._register(this._selectionService.onRequestRedraw(e => this._renderService!.handleSelectionChanged(e.start, e.end, e.columnSelectMode)));
@@ -602,7 +605,7 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal {
602
605
  this._register(addDisposableListener(this.element, 'mousedown', (e: MouseEvent) => this._selectionService!.handleMouseDown(e)));
603
606
 
604
607
  // apply mouse event classes set by escape codes before terminal was attached
605
- if (this.coreMouseService.areMouseEventsActive) {
608
+ if (this.mouseStateService.areMouseEventsActive) {
606
609
  this._selectionService.disable();
607
610
  this.element.classList.add('enable-mouse-events');
608
611
  } else {
@@ -638,285 +641,17 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal {
638
641
 
639
642
  // Listen for mouse events and translate
640
643
  // them into terminal mouse protocols.
641
- this.bindMouse();
644
+ this._mouseService.bindMouse({
645
+ element: this.element!,
646
+ screenElement: this.screenElement!,
647
+ document: this._document!
648
+ }, disposable => this._register(disposable), () => this.focus());
642
649
  }
643
650
 
644
651
  private _createRenderer(): IRenderer {
645
652
  return this._instantiationService.createInstance(DomRenderer, this, this._document!, this.element!, this.screenElement!, this._viewportElement!, this._helperContainer!, this.linkifier!);
646
653
  }
647
654
 
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
655
  /**
921
656
  * Tells the renderer to refresh terminal content between two rows (inclusive) at the next
922
657
  * opportunity.
@@ -990,7 +725,7 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal {
990
725
  }
991
726
 
992
727
  public attachCustomWheelEventHandler(customWheelEventHandler: CustomWheelEventHandler): void {
993
- this._customWheelEventHandler = customWheelEventHandler;
728
+ this.mouseStateService.setCustomWheelEventHandler(customWheelEventHandler);
994
729
  }
995
730
 
996
731
  public registerLinkProvider(linkProvider: ILinkProvider): IDisposable {
@@ -1360,6 +1095,7 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal {
1360
1095
 
1361
1096
  this._setup();
1362
1097
  super.reset();
1098
+ this._mouseService?.reset();
1363
1099
  this._selectionService?.reset();
1364
1100
  this._decorationService.reset();
1365
1101
 
@@ -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, IMouseService, IRenderService } from './services/Services';
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
- @IMouseService private readonly _mouseService: IMouseService,
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, this._mouseService);
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, this._mouseService);
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, this._mouseService);
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, this._mouseService!);
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, mouseService: IMouseService): IBufferCellPosition | undefined {
382
- const coords = mouseService.getCoords(event, element, this._bufferService.cols, this._bufferService.rows);
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
  }
@@ -6,7 +6,7 @@
6
6
  import { ICoreBrowserService, IRenderService, IThemeService } from 'browser/services/Services';
7
7
  import { ViewportConstants } from 'browser/shared/Constants';
8
8
  import { Disposable, toDisposable } from 'common/Lifecycle';
9
- import { IBufferService, ICoreMouseService, IOptionsService } from 'common/services/Services';
9
+ import { IBufferService, IMouseStateService, IOptionsService } from 'common/services/Services';
10
10
  import { CoreMouseEventType } from 'common/Types';
11
11
  import { addDisposableListener, scheduleAtNextAnimationFrame } from 'browser/Dom';
12
12
  import { SmoothScrollableElement } from 'browser/scrollable/scrollableElement';
@@ -34,7 +34,7 @@ export class Viewport extends Disposable {
34
34
  screenElement: HTMLElement,
35
35
  @IBufferService private readonly _bufferService: IBufferService,
36
36
  @ICoreBrowserService coreBrowserService: ICoreBrowserService,
37
- @ICoreMouseService coreMouseService: ICoreMouseService,
37
+ @IMouseStateService mouseStateService: IMouseStateService,
38
38
  @IThemeService themeService: IThemeService,
39
39
  @IOptionsService private readonly _optionsService: IOptionsService,
40
40
  @IRenderService private readonly _renderService: IRenderService
@@ -65,7 +65,7 @@ export class Viewport extends Disposable {
65
65
  'scrollbar'
66
66
  ], () => this._scrollableElement.updateOptions(this._getChangeOptions())));
67
67
  // Don't handle mouse wheel if wheel events are supported by the current mouse prototcol
68
- this._register(coreMouseService.onProtocolChange(type => {
68
+ this._register(mouseStateService.onProtocolChange(type => {
69
69
  this._scrollableElement.updateOptions({
70
70
  handleMouseWheel: !(type & CoreMouseEventType.WHEEL)
71
71
  });
@@ -103,7 +103,7 @@ export class Terminal extends Disposable implements ITerminalApi {
103
103
  public get modes(): IModes {
104
104
  const m = this._core.coreService.decPrivateModes;
105
105
  let mouseTrackingMode: 'none' | 'x10' | 'vt200' | 'drag' | 'any' = 'none';
106
- switch (this._core.coreMouseService.activeProtocol) {
106
+ switch (this._core.mouseStateService.activeProtocol) {
107
107
  case 'X10': mouseTrackingMode = 'x10'; break;
108
108
  case 'VT200': mouseTrackingMode = 'vt200'; break;
109
109
  case 'DRAG': mouseTrackingMode = 'drag'; break;
@@ -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
+ }