html: move flow control logic to terminal

This commit is contained in:
Shuanglei Tao
2022-10-29 13:48:52 +08:00
parent f58d6f3520
commit 0c4feb0cab
4 changed files with 3266 additions and 3263 deletions

View File

@@ -1,7 +1,7 @@
import { h, Component } from 'preact';
import { ITerminalOptions, ITheme } from 'xterm';
import { ClientOptions, Xterm } from './terminal';
import { ClientOptions, FlowControl, Xterm } from './terminal';
if ((module as any).hot) {
// tslint:disable-next-line:no-var-requires
@@ -44,6 +44,11 @@ const termOptions = {
} as ITheme,
allowProposedApi: true,
} as ITerminalOptions;
const flowControl = {
limit: 100000,
highWater: 10,
lowWater: 4,
} as FlowControl;
export class App extends Component {
render() {
@@ -54,6 +59,7 @@ export class App extends Component {
tokenUrl={tokenUrl}
clientOptions={clientOptions}
termOptions={termOptions}
flowControl={flowControl}
/>
);
}

View File

@@ -7,7 +7,7 @@ import { FitAddon } from 'xterm-addon-fit';
import { WebLinksAddon } from 'xterm-addon-web-links';
import { ImageAddon } from 'xterm-addon-image';
import { OverlayAddon } from './overlay';
import { ZmodemAddon, FlowControl } from '../zmodem';
import { ZmodemAddon } from '../zmodem';
import 'xterm/css/xterm.css';
import worker from 'xterm-addon-image/lib/xterm-addon-image-worker';
@@ -46,12 +46,19 @@ export interface ClientOptions {
titleFixed: string;
}
export interface FlowControl {
limit: number;
highWater: number;
lowWater: number;
}
interface Props {
id: string;
wsUrl: string;
tokenUrl: string;
clientOptions: ClientOptions;
termOptions: ITerminalOptions;
flowControl: FlowControl;
}
export class Xterm extends Component<Props> {
@@ -60,6 +67,9 @@ export class Xterm extends Component<Props> {
private container: HTMLElement;
private terminal: Terminal;
private written = 0;
private pending = 0;
private fitAddon: FitAddon;
private overlayAddon: OverlayAddon;
private zmodemAddon: ZmodemAddon;
@@ -103,17 +113,9 @@ export class Xterm extends Component<Props> {
}
render({ id }: Props) {
const control = {
limit: 100000,
highWater: 10,
lowWater: 4,
pause: () => this.pause(),
resume: () => this.resume(),
} as FlowControl;
return (
<div id={id} ref={c => (this.container = c)}>
<ZmodemAddon ref={c => (this.zmodemAddon = c)} sender={this.sendData} control={control} />
<ZmodemAddon ref={c => (this.zmodemAddon = c)} sender={this.sendData} writer={this.writeData} />
</div>
);
}
@@ -131,12 +133,18 @@ export class Xterm extends Component<Props> {
}
@bind
private sendData(data: ArrayLike<number>) {
const { socket } = this;
const payload = new Uint8Array(data.length + 1);
payload[0] = Command.INPUT.charCodeAt(0);
payload.set(data, 1);
socket.send(payload);
private sendData(data: string | Uint8Array) {
const { socket, textEncoder } = this;
if (!socket || socket.readyState !== WebSocket.OPEN) return;
if (typeof data === 'string') {
socket.send(textEncoder.encode(Command.INPUT + data));
} else {
const payload = new Uint8Array(data.length + 1);
payload[0] = Command.INPUT.charCodeAt(0);
payload.set(data, 1);
socket.send(payload);
}
}
@bind
@@ -203,6 +211,29 @@ export class Xterm extends Component<Props> {
fitAddon.fit();
}
@bind
private writeData(data: string | Uint8Array) {
const { terminal, pause, resume } = this;
const { limit, highWater, lowWater } = this.props.flowControl;
this.written += data.length;
if (this.written > limit) {
terminal.write(data, () => {
this.pending = Math.max(this.pending - 1, 0);
if (this.pending < lowWater) {
resume();
}
});
this.pending++;
this.written = 0;
if (this.pending > highWater) {
pause();
}
} else {
terminal.write(data);
}
}
@bind
private connect() {
this.socket = new WebSocket(this.props.wsUrl, ['tty']);
@@ -427,9 +458,6 @@ export class Xterm extends Component<Props> {
@bind
private onTerminalData(data: string) {
const { socket, textEncoder } = this;
if (socket.readyState === WebSocket.OPEN) {
socket.send(textEncoder.encode(Command.INPUT + data));
}
this.sendData(data);
}
}

View File

@@ -6,18 +6,9 @@ import * as Zmodem from 'zmodem.js/src/zmodem_browser';
import { Modal } from '../modal';
export interface FlowControl {
limit: number;
highWater: number;
lowWater: number;
pause: () => void;
resume: () => void;
}
interface Props {
sender: (data: ArrayLike<number>) => void;
control: FlowControl;
sender: (data: string | Uint8Array) => void;
writer: (data: string | Uint8Array) => void;
}
interface State {
@@ -30,9 +21,6 @@ export class ZmodemAddon extends Component<Props, State> implements ITerminalAdd
private sentry: Zmodem.Sentry;
private session: Zmodem.Session;
private written = 0;
private pending = 0;
constructor(props: Props) {
super(props);
@@ -97,31 +85,12 @@ export class ZmodemAddon extends Component<Props, State> implements ITerminalAdd
@bind
private zmodemWrite(data: ArrayBuffer): void {
const { limit, highWater, lowWater, pause, resume } = this.props.control;
const { terminal } = this;
const rawData = new Uint8Array(data);
this.written += rawData.length;
if (this.written > limit) {
terminal.write(rawData, () => {
this.pending = Math.max(this.pending - 1, 0);
if (this.pending < lowWater) {
resume();
}
});
this.pending++;
this.written = 0;
if (this.pending > highWater) {
pause();
}
} else {
terminal.write(rawData);
}
this.props.writer(new Uint8Array(data));
}
@bind
private zmodemSend(data: ArrayLike<number>): void {
this.props.sender(data);
this.props.sender(new Uint8Array(data));
}
@bind
@@ -192,7 +161,7 @@ export class ZmodemAddon extends Component<Props, State> implements ITerminalAdd
const offset = offer.get_offset();
const percent = ((100 * offset) / size).toFixed(2);
terminal.write(`${name} ${percent}% ${bytesHuman(offset, 2)}/${bytesHuman(size, 2)}\r`);
this.props.writer(`${name} ${percent}% ${bytesHuman(offset, 2)}/${bytesHuman(size, 2)}\r`);
}
private bytesHuman(bytes: any, precision: number): string {

6412
src/html.h generated

File diff suppressed because it is too large Load Diff