mirror of
https://github.com/Eugeny/tabby.git
synced 2025-07-19 18:07:58 +00:00
moved more electron stuff out of tabby-local
This commit is contained in:
@@ -16,16 +16,15 @@
|
||||
],
|
||||
"author": "Eugene Pankov",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"hasbin": "^1.2.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@angular/core": "^9.1.9",
|
||||
"tabby-local": "*"
|
||||
},
|
||||
"devDependencies": {
|
||||
"electron-promise-ipc": "^2.2.4",
|
||||
"ps-node": "^0.1.6",
|
||||
"tmp-promise": "^3.0.2",
|
||||
"hasbin": "^1.2.3",
|
||||
"winston": "^3.3.3"
|
||||
}
|
||||
}
|
||||
|
@@ -2,7 +2,7 @@ import { NgModule } from '@angular/core'
|
||||
import { PlatformService, LogService, UpdaterService, DockingService, HostAppService, ThemesService, Platform, AppService, ConfigService, WIN_BUILD_FLUENT_BG_SUPPORTED, isWindowsBuild, HostWindowService, HotkeyProvider, ConfigProvider, FileProvider } from 'tabby-core'
|
||||
import { TerminalColorSchemeProvider } from 'tabby-terminal'
|
||||
import { SFTPContextMenuItemProvider, SSHProfileImporter, AutoPrivateKeyLocator } from 'tabby-ssh'
|
||||
import { ShellProvider, UACService } from 'tabby-local'
|
||||
import { PTYInterface, ShellProvider, UACService } from 'tabby-local'
|
||||
import { auditTime } from 'rxjs'
|
||||
|
||||
import { HyperColorSchemes } from './colorSchemes'
|
||||
@@ -16,11 +16,13 @@ import { ElectronFileProvider } from './services/fileProvider.service'
|
||||
import { ElectronHostAppService } from './services/hostApp.service'
|
||||
import { ElectronService } from './services/electron.service'
|
||||
import { DockMenuService } from './services/dockMenu.service'
|
||||
import { ElectronUACService } from './services/uac.service'
|
||||
|
||||
import { ElectronHotkeyProvider } from './hotkeys'
|
||||
import { ElectronConfigProvider } from './config'
|
||||
import { EditSFTPContextMenu } from './sftpContextMenu'
|
||||
import { OpenSSHImporter, PrivateKeyLocator, StaticFileImporter } from './sshImporters'
|
||||
import { ElectronUACService } from './services/uac.service'
|
||||
import { ElectronPTYInterface } from './pty'
|
||||
|
||||
import { CmderShellProvider } from './shells/cmder'
|
||||
import { Cygwin32ShellProvider } from './shells/cygwin32'
|
||||
@@ -69,6 +71,8 @@ import { VSDevToolsProvider } from './shells/vs'
|
||||
|
||||
{ provide: UACService, useClass: ElectronUACService },
|
||||
|
||||
{ provide: PTYInterface, useClass: ElectronPTYInterface },
|
||||
|
||||
// For WindowsDefaultShellProvider
|
||||
PowerShellCoreShellProvider,
|
||||
WSLShellProvider,
|
||||
|
140
tabby-electron/src/pty.ts
Normal file
140
tabby-electron/src/pty.ts
Normal file
@@ -0,0 +1,140 @@
|
||||
import * as psNode from 'ps-node'
|
||||
import { ipcRenderer } from 'electron'
|
||||
import { ChildProcess, PTYInterface, PTYProxy } from 'tabby-local'
|
||||
import { getWorkingDirectoryFromPID } from 'native-process-working-directory'
|
||||
|
||||
/* eslint-disable block-scoped-var */
|
||||
|
||||
try {
|
||||
var macOSNativeProcessList = require('macos-native-processlist') // eslint-disable-line @typescript-eslint/no-var-requires, no-var
|
||||
} catch { }
|
||||
|
||||
try {
|
||||
var windowsProcessTree = require('windows-process-tree') // eslint-disable-line @typescript-eslint/no-var-requires, no-var
|
||||
} catch { }
|
||||
|
||||
export class ElectronPTYInterface extends PTYInterface {
|
||||
async spawn (...options: any[]): Promise<PTYProxy> {
|
||||
const id = ipcRenderer.sendSync('pty:spawn', ...options)
|
||||
return new ElectronPTYProxy(id)
|
||||
}
|
||||
|
||||
async restore (id: string): Promise<ElectronPTYProxy|null> {
|
||||
if (ipcRenderer.sendSync('pty:exists', id)) {
|
||||
return new ElectronPTYProxy(id)
|
||||
}
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-extraneous-class
|
||||
export class ElectronPTYProxy extends PTYProxy {
|
||||
private subscriptions: Map<string, any> = new Map()
|
||||
private truePID: Promise<number>
|
||||
|
||||
constructor (
|
||||
private id: string,
|
||||
) {
|
||||
super()
|
||||
this.truePID = new Promise(async (resolve) => {
|
||||
let pid = await this.getPID()
|
||||
try {
|
||||
await new Promise(r => setTimeout(r, 2000))
|
||||
|
||||
// Retrieve any possible single children now that shell has fully started
|
||||
let processes = await this.getChildProcessesInternal(pid)
|
||||
while (pid && processes.length === 1) {
|
||||
if (!processes[0].pid) {
|
||||
break
|
||||
}
|
||||
pid = processes[0].pid
|
||||
processes = await this.getChildProcessesInternal(pid)
|
||||
}
|
||||
} finally {
|
||||
resolve(pid)
|
||||
}
|
||||
})
|
||||
this.truePID = this.truePID.catch(() => this.getPID())
|
||||
}
|
||||
|
||||
getID (): string {
|
||||
return this.id
|
||||
}
|
||||
|
||||
getTruePID(): Promise<number> {
|
||||
return this.truePID
|
||||
}
|
||||
|
||||
async getPID (): Promise<number> {
|
||||
return ipcRenderer.sendSync('pty:get-pid', this.id)
|
||||
}
|
||||
|
||||
subscribe (event: string, handler: (..._: any[]) => void): void {
|
||||
const key = `pty:${this.id}:${event}`
|
||||
const newHandler = (_event, ...args) => handler(...args)
|
||||
this.subscriptions.set(key, newHandler)
|
||||
ipcRenderer.on(key, newHandler)
|
||||
}
|
||||
|
||||
ackData (length: number): void {
|
||||
ipcRenderer.send('pty:ack-data', this.id, length)
|
||||
}
|
||||
|
||||
unsubscribeAll (): void {
|
||||
for (const k of this.subscriptions.keys()) {
|
||||
ipcRenderer.off(k, this.subscriptions.get(k))
|
||||
}
|
||||
}
|
||||
|
||||
async resize (columns: number, rows: number): Promise<void> {
|
||||
ipcRenderer.send('pty:resize', this.id, columns, rows)
|
||||
}
|
||||
|
||||
async write (data: Buffer): Promise<void> {
|
||||
ipcRenderer.send('pty:write', this.id, data)
|
||||
}
|
||||
|
||||
async kill (signal?: string): Promise<void> {
|
||||
ipcRenderer.send('pty:kill', this.id, signal)
|
||||
}
|
||||
|
||||
async getChildProcesses (): Promise<ChildProcess[]> {
|
||||
return this.getChildProcessesInternal(await this.getTruePID())
|
||||
}
|
||||
|
||||
async getChildProcessesInternal (truePID: number): Promise<ChildProcess[]> {
|
||||
if (process.platform === 'darwin') {
|
||||
const processes = await macOSNativeProcessList.getProcessList()
|
||||
return processes.filter(x => x.ppid === truePID).map(p => ({
|
||||
pid: p.pid,
|
||||
ppid: p.ppid,
|
||||
command: p.name,
|
||||
}))
|
||||
}
|
||||
if (process.platform === 'win32') {
|
||||
return new Promise<ChildProcess[]>(resolve => {
|
||||
windowsProcessTree.getProcessTree(truePID, tree => {
|
||||
resolve(tree ? tree.children.map(child => ({
|
||||
pid: child.pid,
|
||||
ppid: tree.pid,
|
||||
command: child.name,
|
||||
})) : [])
|
||||
})
|
||||
})
|
||||
}
|
||||
return new Promise<ChildProcess[]>((resolve, reject) => {
|
||||
psNode.lookup({ ppid: truePID }, (err, processes) => {
|
||||
if (err) {
|
||||
reject(err)
|
||||
return
|
||||
}
|
||||
resolve(processes as ChildProcess[])
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
async getWorkingDirectory (): Promise<string|null> {
|
||||
return getWorkingDirectoryFromPID(await this.getTruePID())
|
||||
}
|
||||
|
||||
}
|
@@ -93,6 +93,11 @@ concat-map@0.0.1:
|
||||
resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
|
||||
integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=
|
||||
|
||||
connected-domain@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/connected-domain/-/connected-domain-1.0.0.tgz#bfe77238c74be453a79f0cb6058deeb4f2358e93"
|
||||
integrity sha512-lHlohUiJxlpunvDag2Y0pO20bnvarMjnrdciZeuJUqRwrf/5JHNhdpiPIr5GQ8IkqrFj5TDMQwcCjblGo1oeuA==
|
||||
|
||||
define-properties@^1.1.3:
|
||||
version "1.1.3"
|
||||
resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.3.tgz#cf88da6cbee26fe6db7094f61d870cbd84cee9f1"
|
||||
@@ -368,6 +373,13 @@ path-is-absolute@^1.0.0:
|
||||
resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f"
|
||||
integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18=
|
||||
|
||||
ps-node@^0.1.6:
|
||||
version "0.1.6"
|
||||
resolved "https://registry.yarnpkg.com/ps-node/-/ps-node-0.1.6.tgz#9af67a99d7b1d0132e51a503099d38a8d2ace2c3"
|
||||
integrity sha512-w7QJhUTbu70hpDso0YXDRNKCPNuchV8UTUZsAv0m7Qj5g85oHOJfr9drA1EjvK4nQK/bG8P97W4L6PJ3IQLoOA==
|
||||
dependencies:
|
||||
table-parser "^0.1.3"
|
||||
|
||||
readable-stream@^3.4.0, readable-stream@^3.6.0:
|
||||
version "3.6.0"
|
||||
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198"
|
||||
@@ -436,6 +448,13 @@ string_decoder@^1.1.1:
|
||||
dependencies:
|
||||
safe-buffer "~5.2.0"
|
||||
|
||||
table-parser@^0.1.3:
|
||||
version "0.1.3"
|
||||
resolved "https://registry.yarnpkg.com/table-parser/-/table-parser-0.1.3.tgz#0441cfce16a59481684c27d1b5a67ff15a43c7b0"
|
||||
integrity sha512-LCYeuvqqoPII3lzzYaXKbC3Forb+d2u4bNwhk/9FlivuGRxPE28YEWAYcujeSlLLDlMfvy29+WPybFJZFiKMYg==
|
||||
dependencies:
|
||||
connected-domain "^1.0.0"
|
||||
|
||||
text-hex@1.0.x:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/text-hex/-/text-hex-1.0.0.tgz#69dc9c1b17446ee79a92bf5b884bb4b9127506f5"
|
||||
|
Reference in New Issue
Block a user