handle non-running terminal keypresses

This commit is contained in:
sawka
2022-10-13 18:58:21 -07:00
parent f73baf440c
commit 4744eaa491
4 changed files with 93 additions and 32 deletions
+2 -5
View File
@@ -12,6 +12,7 @@ import {TermWrap} from "./term";
import type {SessionDataType, LineType, CmdDataType, RemoteType, RemoteStateType, RemoteInstanceType, RemotePtrType, HistoryItem, HistoryQueryOpts, RemoteEditType} from "./types";
import localizedFormat from 'dayjs/plugin/localizedFormat';
import {GlobalModel, GlobalCommandRunner, Session, Cmd, Window, Screen, ScreenWindow, riToRPtr, widthToCols, termWidthFromCols, termHeightFromRows} from "./model";
import {isModKeyPress} from "./util";
dayjs.extend(localizedFormat)
@@ -439,10 +440,6 @@ class TextAreaInput extends React.Component<{}, {}> {
}
}
isModKeyPress(e : any) {
return e.code.match(/^(Control|Meta|Alt|Shift)(Left|Right)$/);
}
getLinePos(elem : any) : {numLines : number, linePos : number} {
let numLines = elem.value.split("\n").length;
let linePos = elem.value.substr(0, elem.selectionStart).split("\n").length;
@@ -452,7 +449,7 @@ class TextAreaInput extends React.Component<{}, {}> {
@mobx.action @boundMethod
onKeyDown(e : any) {
mobx.action(() => {
if (this.isModKeyPress(e)) {
if (isModKeyPress(e)) {
return;
}
let model = GlobalModel;
+51 -11
View File
@@ -2,7 +2,7 @@ import * as mobx from "mobx";
import {sprintf} from "sprintf-js";
import {boundMethod} from "autobind-decorator";
import {debounce} from "throttle-debounce";
import {handleJsonFetchResponse, base64ToArray, genMergeData, genMergeSimpleData, boundInt} from "./util";
import {handleJsonFetchResponse, base64ToArray, genMergeData, genMergeSimpleData, boundInt, isModKeyPress} from "./util";
import {TermWrap} from "./term";
import {v4 as uuidv4} from "uuid";
import type {SessionDataType, WindowDataType, LineType, RemoteType, HistoryItem, RemoteInstanceType, RemotePtrType, CmdDataType, FeCmdPacketType, TermOptsType, RemoteStateType, ScreenDataType, ScreenWindowType, ScreenOptsType, LayoutType, PtyDataUpdateType, ModelUpdateType, UpdateMessage, InfoType, CmdLineUpdateType, UIContextType, HistoryInfoType, HistoryQueryOpts, FeInputPacketType, TermWinSize, RemoteInputPacketType} from "./types";
@@ -171,7 +171,8 @@ class Cmd {
return cmdStatusIsRunning(data.status);
}
handleKey(event : any) {
handleData(data : string, termWrap : TermWrap) : void {
console.log("handle data", {data: data});
if (!this.isRunning()) {
return;
}
@@ -179,7 +180,7 @@ class Cmd {
type: "feinput",
ck: this.sessionId + "/" + this.cmdId,
remote: this.remote,
inputdata64: btoa(event.key),
inputdata64: btoa(data),
};
GlobalModel.sendInputPacket(inputPacket);
}
@@ -460,6 +461,33 @@ class ScreenWindow {
}
}
termCustomKeyHandlerInternal(e : any, termWrap : TermWrap) : void {
if (e.code == "ArrowUp") {
termWrap.terminal.scrollLines(-1);
return;
}
if (e.code == "ArrowDown") {
termWrap.terminal.scrollLines(1);
return;
}
if (e.code == "PageUp") {
termWrap.terminal.scrollPages(-1);
return;
}
if (e.code == "PageDown") {
termWrap.terminal.scrollPages(1);
return;
}
}
termCustomKeyHandler(e : any, termWrap : TermWrap) : boolean {
if (e.type != "keydown" || isModKeyPress(e)) {
return termWrap.isRunning;
}
this.termCustomKeyHandlerInternal(e, termWrap);
return termWrap.isRunning;
}
connectElem(elem : Element, line : LineType, cmd : Cmd, width : number) {
let cmdId = cmd.cmdId;
let termWrap = this.getTermWrap(cmdId);
@@ -469,9 +497,16 @@ class ScreenWindow {
}
let cols = widthToCols(width);
let usedRows = GlobalModel.getTUR(this.sessionId, cmdId, cols);
termWrap = new TermWrap(
elem, {sessionId: this.sessionId, cmdId: cmdId}, usedRows, cmd.getTermOpts(), {height: 0, width: width},
cmd.handleKey.bind(cmd), (focus : boolean) => this.setTermFocus(line.linenum, focus), cmd.isRunning());
termWrap = new TermWrap(elem, {
termContext: {sessionId: this.sessionId, cmdId: cmdId},
usedRows: usedRows,
termOpts: cmd.getTermOpts(),
winSize: {height: 0, width: width},
dataHandler: cmd.handleData.bind(cmd),
focusHandler: (focus : boolean) => this.setTermFocus(line.linenum, focus),
isRunning: cmd.isRunning(),
customKeyHandler: this.termCustomKeyHandler.bind(this),
});
this.terms[cmdId] = termWrap;
if ((this.focusType.get() == "cmd" || this.focusType.get() == "cmd-fg") && this.selectedLine.get() == line.linenum) {
termWrap.focusTerminal();
@@ -1331,7 +1366,7 @@ class InputModel {
})();
}
termKeyHandler(remoteId : string, event : any) : void {
termKeyHandler(remoteId : string, event : any, termWrap : TermWrap) : void {
let remote = GlobalModel.getRemote(remoteId);
if (remote == null) {
return;
@@ -1366,10 +1401,15 @@ class InputModel {
}
else {
let termOpts = {rows: RemotePtyRows, cols: RemotePtyCols, flexrows: false, maxptysize: 64*1024};
this.remoteTermWrap = new TermWrap(
elem, {remoteId: remoteId}, RemotePtyRows, termOpts, null,
(e) => { this.termKeyHandler(remoteId, e)},
this.setRemoteTermWrapFocus.bind(this), true);
this.remoteTermWrap = new TermWrap(elem, {
termContext: {remoteId: remoteId},
usedRows: RemotePtyRows,
termOpts: termOpts,
winSize: null,
keyHandler: (e, termWrap) => { this.termKeyHandler(remoteId, e, termWrap)},
focusHandler: this.setRemoteTermWrapFocus.bind(this),
isRunning: true,
});
}
}
}
+35 -15
View File
@@ -22,6 +22,18 @@ const MaxTermCols = 1024;
type TermContext = {sessionId? : string, cmdId? : string, remoteId? : string};
type TermWrapOpts = {
termContext : TermContext,
usedRows? : number,
termOpts : TermOptsType,
winSize : WindowSize,
keyHandler? : (event : any, termWrap : TermWrap) => void,
focusHandler? : (focus : boolean) => void,
dataHandler? : (data : string, termWrap : TermWrap) => void,
isRunning : boolean,
customKeyHandler? : (event : any, termWrap : TermWrap) => boolean,
};
// cmd-instance
class TermWrap {
terminal : any;
@@ -40,27 +52,28 @@ class TermWrap {
focusHandler : (focus : boolean) => void;
isRunning : boolean;
constructor(elem : Element, termContext : TermContext, usedRows : number, termOpts : TermOptsType, winSize : WindowSize, keyHandler : (event : any) => void, focusHandler : (focus : boolean) => void, isRunning : boolean) {
this.termContext = termContext;
constructor(elem : Element, opts : TermWrapOpts) {
opts = opts ?? ({} as any);
this.termContext = opts.termContext;
this.connectedElem = elem;
this.flexRows = termOpts.flexrows ?? false;
this.winSize = winSize;
this.focusHandler = focusHandler;
this.isRunning = isRunning;
this.flexRows = opts.termOpts.flexrows ?? false;
this.winSize = opts.winSize;
this.focusHandler = opts.focusHandler;
this.isRunning = opts.isRunning;
if (this.flexRows) {
this.atRowMax = false;
this.usedRows = mobx.observable.box(usedRows ?? (isRunning ? 2 : 0));
this.usedRows = mobx.observable.box(opts.usedRows ?? (opts.isRunning ? 2 : 0));
}
else {
this.atRowMax = true;
this.usedRows = mobx.observable.box(termOpts.rows);
this.usedRows = mobx.observable.box(opts.termOpts.rows);
}
if (winSize == null) {
this.termSize = {rows: termOpts.rows, cols: termOpts.cols};
if (opts.winSize == null) {
this.termSize = {rows: opts.termOpts.rows, cols: opts.termOpts.cols};
}
else {
let cols = widthToCols(winSize.width);
this.termSize = {rows: termOpts.rows, cols: cols};
let cols = widthToCols(opts.winSize.width);
this.termSize = {rows: opts.termOpts.rows, cols: cols};
}
let theme = {
foreground: "#d3d7cf",
@@ -71,8 +84,11 @@ class TermWrap {
return state;
});
this.terminal.open(elem);
if (keyHandler != null) {
this.terminal.onKey(keyHandler);
if (opts.keyHandler != null) {
this.terminal.onKey((e) => opts.keyHandler(e, this));
}
if (opts.dataHandler != null) {
this.terminal.onData((e) => opts.dataHandler(e, this));
}
this.terminal.textarea.addEventListener("focus", () => {
if (this.focusHandler != null) {
@@ -88,6 +104,9 @@ class TermWrap {
}
});
elem.addEventListener("scroll", this.elemScrollHandler);
if (opts.customKeyHandler != null) {
this.terminal.attachCustomKeyEventHandler((e) => opts.customKeyHandler(e, this));
}
this.reloadTerminal(0);
}
@@ -97,7 +116,8 @@ class TermWrap {
// xterm.js renders a textarea that handles focus. when it focuses and a space is typed the browser
// will scroll to make it visible (even though our terminal element has overflow hidden)
// this will undo that scroll.
if (e.target.scrollTop == 0) {
console.log("scroll", this.atRowMax, e.target.scrollTop);
if (this.atRowMax || e.target.scrollTop == 0) {
return;
}
e.target.scrollTop = 0;
+5 -1
View File
@@ -172,4 +172,8 @@ function boundInt(ival : number, minVal : number, maxVal : number) : number {
return ival;
}
export {handleJsonFetchResponse, base64ToArray, genMergeData, genMergeSimpleData, parseEnv0, boundInt};
function isModKeyPress(e : any) {
return e.code.match(/^(Control|Meta|Alt|Shift)(Left|Right)$/);
}
export {handleJsonFetchResponse, base64ToArray, genMergeData, genMergeSimpleData, parseEnv0, boundInt, isModKeyPress};