Skip to content

Commit ecb7f42

Browse files
authored
Merge branch 'master' into 1937_uncached_char_top_fix
2 parents 9f2aec1 + ce09ae4 commit ecb7f42

File tree

5 files changed

+48
-15
lines changed

5 files changed

+48
-15
lines changed

docker-compose.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ services:
77
volumes:
88
- ./:/usr/src/app
99
ports:
10-
- ${XTERMJS_PORT:3000}:3000
10+
- ${XTERMJS_PORT:-3000}:3000
1111
command: ["npm", "start"]
1212

1313
watch:

src/InputHandler.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1284,7 +1284,9 @@ export class InputHandler extends Disposable implements IInputHandler {
12841284
if (this._terminal.element) {
12851285
this._terminal.element.classList.add('enable-mouse-events');
12861286
}
1287-
this._terminal.selectionManager.disable();
1287+
if (this._terminal.selectionManager) {
1288+
this._terminal.selectionManager.disable();
1289+
}
12881290
this._terminal.log('Binding to mouse events.');
12891291
break;
12901292
case 1004: // send focusin/focusout events
@@ -1474,7 +1476,9 @@ export class InputHandler extends Disposable implements IInputHandler {
14741476
if (this._terminal.element) {
14751477
this._terminal.element.classList.remove('enable-mouse-events');
14761478
}
1477-
this._terminal.selectionManager.enable();
1479+
if (this._terminal.selectionManager) {
1480+
this._terminal.selectionManager.enable();
1481+
}
14781482
break;
14791483
case 1004: // send focusin/focusout events
14801484
this._terminal.sendFocus = false;

src/Terminal.ts

Lines changed: 24 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -64,10 +64,12 @@ const document = (typeof window !== 'undefined') ? window.document : null;
6464
const WRITE_BUFFER_PAUSE_THRESHOLD = 5;
6565

6666
/**
67-
* The number of writes to perform in a single batch before allowing the
68-
* renderer to catch up with a 0ms setTimeout.
67+
* The max number of ms to spend on writes before allowing the renderer to
68+
* catch up with a 0ms setTimeout. A value of < 33 to keep us close to
69+
* 30fps, and a value of < 16 to try to run at 60fps. Of course, the real FPS
70+
* depends on the time it takes for the renderer to draw the frame.
6971
*/
70-
const WRITE_BATCH_SIZE = 300;
72+
const WRITE_TIMEOUT_MS = 12;
7173

7274
const MINIMUM_COLS = 2; // Less than 2 can mess with wide chars
7375
const MINIMUM_ROWS = 1;
@@ -738,6 +740,11 @@ export class Terminal extends EventEmitter implements ITerminal, IDisposable, II
738740
this.mouseHelper = new MouseHelper(this.renderer);
739741
// apply mouse event classes set by escape codes before terminal was attached
740742
this.element.classList.toggle('enable-mouse-events', this.mouseEvents);
743+
if (this.mouseEvents) {
744+
this.selectionManager.disable();
745+
} else {
746+
this.selectionManager.enable();
747+
}
741748

742749
if (this.options.screenReaderMode) {
743750
// Note that this must be done *after* the renderer is created in order to
@@ -1343,19 +1350,20 @@ export class Terminal extends EventEmitter implements ITerminal, IDisposable, II
13431350
}
13441351
}
13451352

1346-
protected _innerWrite(): void {
1353+
protected _innerWrite(bufferOffset: number = 0): void {
13471354
// Ensure the terminal isn't disposed
13481355
if (this._isDisposed) {
13491356
this.writeBuffer = [];
13501357
}
13511358

1352-
const writeBatch = this.writeBuffer.splice(0, WRITE_BATCH_SIZE);
1353-
while (writeBatch.length > 0) {
1354-
const data = writeBatch.shift();
1359+
const startTime = Date.now();
1360+
while (this.writeBuffer.length > bufferOffset) {
1361+
const data = this.writeBuffer[bufferOffset];
1362+
bufferOffset++;
13551363

13561364
// If XOFF was sent in order to catch up with the pty process, resume it if
1357-
// the writeBuffer is empty to allow more data to come in.
1358-
if (this._xoffSentToCatchUp && writeBatch.length === 0 && this.writeBuffer.length === 0) {
1365+
// we reached the end of the writeBuffer to allow more data to come in.
1366+
if (this._xoffSentToCatchUp && this.writeBuffer.length === bufferOffset) {
13591367
this.handler(C0.DC1);
13601368
this._xoffSentToCatchUp = false;
13611369
}
@@ -1373,12 +1381,17 @@ export class Terminal extends EventEmitter implements ITerminal, IDisposable, II
13731381

13741382
this.updateRange(this.buffer.y);
13751383
this.refresh(this._refreshStart, this._refreshEnd);
1384+
1385+
if (Date.now() - startTime >= WRITE_TIMEOUT_MS) {
1386+
break;
1387+
}
13761388
}
1377-
if (this.writeBuffer.length > 0) {
1389+
if (this.writeBuffer.length > bufferOffset) {
13781390
// Allow renderer to catch up before processing the next batch
1379-
setTimeout(() => this._innerWrite(), 0);
1391+
setTimeout(() => this._innerWrite(bufferOffset), 0);
13801392
} else {
13811393
this._writeInProgress = false;
1394+
this.writeBuffer = [];
13821395
}
13831396
}
13841397

src/renderer/Renderer.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ export class Renderer extends EventEmitter implements IRenderer {
7070
// Detect whether IntersectionObserver is detected and enable renderer pause
7171
// and resume based on terminal visibility if so
7272
if ('IntersectionObserver' in window) {
73-
const observer = new IntersectionObserver(e => this.onIntersectionChange(e[0]), { threshold: 0 });
73+
const observer = new IntersectionObserver(e => this.onIntersectionChange(e[e.length - 1]), { threshold: 0 });
7474
observer.observe(this._terminal.element);
7575
this.register({ dispose: () => observer.disconnect() });
7676
}

src/ui/MouseZoneManager.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ export class MouseZoneManager extends Disposable implements IMouseZoneManager {
2323

2424
private _areZonesActive: boolean = false;
2525
private _mouseMoveListener: (e: MouseEvent) => any;
26+
private _mouseLeaveListener: (e: MouseEvent) => any;
2627
private _clickListener: (e: MouseEvent) => any;
2728

2829
private _tooltipTimeout: number = null;
@@ -38,6 +39,7 @@ export class MouseZoneManager extends Disposable implements IMouseZoneManager {
3839

3940
// These events are expensive, only listen to it when mouse zones are active
4041
this._mouseMoveListener = e => this._onMouseMove(e);
42+
this._mouseLeaveListener = e => this._onMouseLeave(e);
4143
this._clickListener = e => this._onClick(e);
4244
}
4345

@@ -89,6 +91,7 @@ export class MouseZoneManager extends Disposable implements IMouseZoneManager {
8991
if (!this._areZonesActive) {
9092
this._areZonesActive = true;
9193
this._terminal.element.addEventListener('mousemove', this._mouseMoveListener);
94+
this._terminal.element.addEventListener('mouseleave', this._mouseLeaveListener);
9295
this._terminal.element.addEventListener('click', this._clickListener);
9396
}
9497
}
@@ -97,6 +100,7 @@ export class MouseZoneManager extends Disposable implements IMouseZoneManager {
97100
if (this._areZonesActive) {
98101
this._areZonesActive = false;
99102
this._terminal.element.removeEventListener('mousemove', this._mouseMoveListener);
103+
this._terminal.element.removeEventListener('mouseleave', this._mouseLeaveListener);
100104
this._terminal.element.removeEventListener('click', this._clickListener);
101105
}
102106
}
@@ -169,6 +173,18 @@ export class MouseZoneManager extends Disposable implements IMouseZoneManager {
169173
}
170174
}
171175

176+
private _onMouseLeave(e: MouseEvent): void {
177+
// Fire the hover end callback and cancel any existing timer if the mouse
178+
// leaves the terminal element
179+
if (this._currentZone) {
180+
this._currentZone.leaveCallback();
181+
this._currentZone = null;
182+
if (this._tooltipTimeout) {
183+
clearTimeout(this._tooltipTimeout);
184+
}
185+
}
186+
}
187+
172188
private _onClick(e: MouseEvent): void {
173189
// Find the active zone and click it if found
174190
const zone = this._findZoneEventAt(e);

0 commit comments

Comments
 (0)