terminal: make XtermTerminal support tmux integration
Bug: b/236205389
Change-Id: I05572d59ac03d144ffcdf91cee862b783687248b
Reviewed-on: https://chromium-review.googlesource.com/c/apps/libapps/+/3944190
Tested-by: kokoro <noreply+kokoro@google.com>
Reviewed-by: Joel Hockey <joelhockey@chromium.org>
diff --git a/terminal/js/terminal_emulator.js b/terminal/js/terminal_emulator.js
index aead328..d25a2af 100644
--- a/terminal/js/terminal_emulator.js
+++ b/terminal/js/terminal_emulator.js
@@ -9,6 +9,8 @@
// TODO(b/236205389): add tests. For example, we should enable the test in
// terminal_tests.js for XtermTerminal.
+// TODO(b/236205389): support option smoothScrollDuration?
+
import {LitElement, css, html} from './lit.js';
import {FontManager, ORIGINAL_URL, TERMINAL_EMULATORS, delayedScheduler,
fontManager, getOSInfo, sleep} from './terminal_common.js';
@@ -16,6 +18,7 @@
import {TerminalTooltip} from './terminal_tooltip.js';
import {Terminal, Unicode11Addon, WebLinksAddon, WebglAddon}
from './xterm.js';
+import {XtermInternal} from './terminal_xterm_internal.js';
/** @enum {number} */
@@ -82,6 +85,7 @@
* @typedef {{
* term: !Terminal,
* fontManager: !FontManager,
+ * xtermInternal: !XtermInternal,
* }}
*/
export let XtermTerminalTestParams;
@@ -264,6 +268,8 @@
// TODO: we should probably pass the initial prefs to the ctor.
this.term = testParams?.term || new Terminal({allowProposedApi: true});
+ this.xtermInternal_ = testParams?.xtermInternal ||
+ new XtermInternal(this.term);
this.fontManager_ = testParams?.fontManager || fontManager;
/** @type {?Element} */
@@ -291,7 +297,7 @@
// prompt, which only listens to onVTKeystroke().
this.term.onData((data) => this.io.onVTKeystroke(data));
this.term.onBinary((data) => this.io.onVTKeystroke(data));
- this.term.onTitleChange((title) => document.title = title);
+ this.term.onTitleChange((title) => this.setWindowTitle(title));
this.term.onSelectionChange(() => this.copySelection_());
this.term.onBell(() => this.ringBell());
@@ -335,10 +341,35 @@
}
/** @override */
+ setWindowTitle(title) {
+ document.title = title;
+ }
+
+ /** @override */
ringBell() {
this.bell_.ring();
}
+ /** @override */
+ print(str) {
+ this.xtermInternal_.print(str);
+ }
+
+ /** @override */
+ wipeContents() {
+ this.term.clear();
+ }
+
+ /** @override */
+ newLine() {
+ this.xtermInternal_.newLine();
+ }
+
+ /** @override */
+ cursorLeft(number) {
+ this.xtermInternal_.cursorLeft(number ?? 1);
+ }
+
/**
* Install stubs for stuff that we haven't implemented yet so that the code
* still runs.
@@ -405,6 +436,10 @@
return true;
});
+
+ this.xtermInternal_.installTmuxControlModeHandler(
+ (data) => this.onTmuxControlModeLine(data));
+ this.xtermInternal_.installEscKHandler();
}
/**
@@ -412,18 +447,22 @@
*
* @param {string|!Uint8Array} data string for UTF-16 data, Uint8Array for
* UTF-8 data
+ * @param {function()=} callback Optional callback that fires when the data
+ * was processed by the parser.
*/
- write(data) {
- this.term.write(data);
+ write(data, callback) {
+ this.term.write(data, callback);
}
/**
* Like `this.write()` but also write a line break.
*
* @param {string|!Uint8Array} data
+ * @param {function()=} callback Optional callback that fires when the data
+ * was processed by the parser.
*/
- writeln(data) {
- this.term.writeln(data);
+ writeln(data, callback) {
+ this.term.writeln(data, callback);
}
get screenSize() {
@@ -455,7 +494,6 @@
this.inited_ = true;
this.term.open(elem);
- this.scheduleFit_();
if (this.enableWebGL_) {
this.term.loadAddon(new WebglAddon());
}
@@ -485,6 +523,7 @@
}
});
+ await this.scheduleFit_();
this.onTerminalReady();
})();
}
@@ -620,12 +659,9 @@
return Math.floor((size - 2 * screenPaddingSize) / cellSize);
};
- // Unfortunately, it looks like we have to use private API from xterm.js.
- // Maybe we should patch the FitAddon so that it works for us.
- const dimensions = this.term._core._renderService.dimensions;
- const cols = calc(this.container_.offsetWidth, dimensions.actualCellWidth);
- const rows = calc(this.container_.offsetHeight,
- dimensions.actualCellHeight);
+ const cellDimensions = this.xtermInternal_.getActualCellDimensions();
+ const cols = calc(this.container_.offsetWidth, cellDimensions.width);
+ const rows = calc(this.container_.offsetHeight, cellDimensions.height);
if (cols >= 0 && rows >= 0) {
this.term.resize(cols, rows);
}
@@ -847,21 +883,6 @@
set(modifiers | Modifier.Shift, keyCode, func);
};
- // Temporary shortcut to refresh the rendering in case of rendering errors.
- // TODO(lxj): remove after this is fixed:
- // https://github.com/xtermjs/xterm.js/issues/3878
- set(Modifier.Ctrl | Modifier.Shift, keyCodes.L,
- /** @suppress {missingProperties} */
- () => {
- this.scheduleRefreshFont_();
- // Refresh the cursor layer.
- if (this.enableWebGL_) {
- this.term?._core?._renderService?._renderer?._renderLayers[1]
- ?._clearAll();
- }
- },
- );
-
// Ctrl+/
set(Modifier.Ctrl, 191, (ev) => {
ev.preventDefault();
@@ -961,6 +982,27 @@
(v) => fontManager.loadFont(/** @type {string} */(v)));
});
}
+
+ /**
+ * Write data to the terminal.
+ *
+ * @param {string|!Uint8Array} data string for UTF-16 data, Uint8Array for
+ * UTF-8 data
+ * @param {function()=} callback Optional callback that fires when the data
+ * was processed by the parser.
+ */
+ write(data, callback) {
+ if (typeof data === 'string') {
+ this.io.print(data);
+ } else {
+ this.io.writeUTF8(data);
+ }
+ // Hterm processes the data synchronously, so we can call the callback
+ // immediately.
+ if (callback) {
+ setTimeout(callback);
+ }
+ }
}
/**