Skip to content

Commit b74fdfb

Browse files
authored
Merge pull request #103 from Poyoman39/patch-1
Add brand new "gnome console" terminal
2 parents cc96a7b + acb3056 commit b74fdfb

File tree

1 file changed

+68
-34
lines changed

1 file changed

+68
-34
lines changed

src/interceptors/terminal/fresh-terminal-interceptor.ts

Lines changed: 68 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import { Interceptor } from '..';
88
import { HtkConfig } from '../../config';
99
import { logError, addBreadcrumb } from '../../error-tracking';
1010
import { isErrorLike } from '../../util/error';
11-
import { canAccess, commandExists } from '../../util/fs';
11+
import { canAccess, commandExists, getRealPath, resolveCommandPath } from '../../util/fs';
1212
import { spawnToResult } from '../../util/process-management';
1313

1414
import { getTerminalEnvVars } from './terminal-env-overrides';
@@ -21,6 +21,9 @@ interface SpawnArgs {
2121
args?: string[];
2222
options?: SpawnOptions;
2323
skipStartupScripts?: true;
24+
envVars?: {
25+
[key: string]: string;
26+
};
2427
}
2528

2629
const getTerminalCommand = _.memoize(async (): Promise<SpawnArgs | null> => {
@@ -32,7 +35,10 @@ const getTerminalCommand = _.memoize(async (): Promise<SpawnArgs | null> => {
3235
else result = Promise.resolve(null);
3336

3437
result.then((terminal) => {
35-
if (terminal) addBreadcrumb('Found terminal', { data: { terminal } });
38+
if (terminal) {
39+
console.log(`Detected terminal command: ${terminal.command}`);
40+
addBreadcrumb('Found terminal', { data: { terminal } });
41+
}
3642
else logError('No terminal could be detected');
3743
});
3844

@@ -80,6 +86,7 @@ const getLinuxTerminalCommand = async (): Promise<SpawnArgs | null> => {
8086
const defaultTerminal = gSettingsTerminalKey && gSettingsTerminalKey.getValue();
8187
if (defaultTerminal && await commandExists(defaultTerminal)) {
8288
if (defaultTerminal.includes('gnome-terminal')) return getGnomeTerminalCommand(defaultTerminal);
89+
if (defaultTerminal.includes('kgx')) return getKgxCommand(defaultTerminal);
8390
if (defaultTerminal.includes('konsole')) return getKonsoleTerminalCommand(defaultTerminal);
8491
if (defaultTerminal.includes('xfce4-terminal')) return getXfceTerminalCommand(defaultTerminal);
8592
if (defaultTerminal.includes('x-terminal-emulator')) return getXTerminalCommand(defaultTerminal);
@@ -91,6 +98,7 @@ const getLinuxTerminalCommand = async (): Promise<SpawnArgs | null> => {
9198
// If a specific term like this is installed, it's probably the preferred one
9299
if (await commandExists('konsole')) return getKonsoleTerminalCommand();
93100
if (await commandExists('xfce4-terminal')) return getXfceTerminalCommand();
101+
if (await commandExists('kgx')) return getKgxCommand();
94102
if (await commandExists('kitty')) return { command: 'kitty' };
95103
if (await commandExists('urxvt')) return { command: 'urxvt' };
96104
if (await commandExists('rxvt')) return { command: 'rxvt' };
@@ -103,36 +111,8 @@ const getLinuxTerminalCommand = async (): Promise<SpawnArgs | null> => {
103111
return null;
104112
};
105113

106-
const getXTerminalCommand = async (command = 'x-terminal-emulator'): Promise<SpawnArgs> => {
107-
// x-terminal-emulator is a wrapper/symlink to the terminal of choice.
108-
// Unfortunately, we need to pass specific args that aren't supported by all terminals (to ensure
109-
// terminals run in the foreground), and the Debian XTE wrapper at least doesn't pass through
110-
// any of the args we want to use. To fix this, we parse --help to try and detect the underlying
111-
// terminal, and run it directly with the args we need.
112-
try {
113-
// Run the command with -h to get some output we can use to infer the terminal itself.
114-
// --version would be nice, but the debian wrapper ignores it. --help isn't supported by xterm.
115-
const { stdout } = await spawnToResult(command, ['-h']);
116-
const helpOutput = stdout.toLowerCase().replace(/[^\w\d]+/g, ' ');
117-
118-
if (helpOutput.includes('gnome terminal') && await commandExists('gnome-terminal')) {
119-
return getGnomeTerminalCommand();
120-
} else if (helpOutput.includes('xfce4 terminal') && await commandExists('xfce4-terminal')) {
121-
return getXfceTerminalCommand();
122-
} else if (helpOutput.includes('konsole') && await commandExists('konsole')) {
123-
return getKonsoleTerminalCommand();
124-
}
125-
} catch (e) {
126-
if (isErrorLike(e) && e.message?.includes('rxvt')) {
127-
// Bizarrely, rxvt -h prints help but always returns a non-zero exit code.
128-
// Doesn't need any special arguments anyway though, so just ignore it
129-
} else {
130-
logError(e);
131-
}
132-
}
133-
134-
// If there's an error, or we just don't recognize the console, give up & run it directly
135-
return { command };
114+
const getKgxCommand = async (command = 'kgx'): Promise<SpawnArgs> => {
115+
return { command, envVars: { DBUS_SESSION_BUS_ADDRESS: '' } };
136116
};
137117

138118
const getKonsoleTerminalCommand = async (command = 'konsole'): Promise<SpawnArgs> => {
@@ -185,6 +165,59 @@ const getXfceTerminalCommand = async (command = 'xfce4-terminal'): Promise<Spawn
185165
return { command, args: extraArgs };
186166
};
187167

168+
const X_TERMINAL_MATCHERS = [
169+
{ helpString: 'gnome terminal', command: 'gnome-terminal', commandBuilder: getGnomeTerminalCommand },
170+
{ helpString: 'xfce4 terminal', command: 'xfce4-terminal', commandBuilder: getXfceTerminalCommand },
171+
{ helpString: 'konsole', command: 'konsole', commandBuilder: getKonsoleTerminalCommand },
172+
{ helpString: 'kgx', command: 'kgx', commandBuilder: getKgxCommand }
173+
] as const;
174+
175+
const getXTerminalCommand = async (command = 'x-terminal-emulator'): Promise<SpawnArgs> => {
176+
// x-terminal-emulator is a wrapper/symlink to the terminal of choice.
177+
// Unfortunately, we need to pass specific args that aren't supported by all terminals (to ensure
178+
// terminals run in the foreground), and the Debian XTE wrapper at least doesn't pass through
179+
// any of the args we want to use. To fix this, we parse --help to try and detect the underlying
180+
// terminal, and run it directly with the args we need.
181+
try {
182+
// In parallel, get the command's help output, and its real resolved path on disk:
183+
const [helpOutput, commandPath] = await Promise.all([
184+
// Run the command with -h to get some output we can use to infer the terminal itself.
185+
// --version would be nice, but the debian wrapper ignores it. --help isn't supported by xterm.
186+
spawnToResult(command, ['-h'])
187+
.then(({ stdout }) => stdout.toLowerCase().replace(/[^\w\d]+/g, ' ')),
188+
// We find the command in PATH, and then follow any symbolic links, and see what the real
189+
// underlying path is:
190+
resolveCommandPath(command)
191+
.then((path) => { if (path) return getRealPath(path) })
192+
]);
193+
194+
for (let terminalPattern of X_TERMINAL_MATCHERS) {
195+
// We match the help output or command path for known strings:
196+
if (
197+
!helpOutput.includes(terminalPattern.helpString) &&
198+
!commandPath?.includes(command)
199+
) continue;
200+
201+
// If found, we use the terminal _if_ it exists globally with our expected command.
202+
// We can't use 'command' directly, because some terms (gnome-terminal, Debian generally)
203+
// use weird wrappers that don't behave properly.
204+
if (await commandExists(terminalPattern.command)) {
205+
return terminalPattern.commandBuilder();
206+
}
207+
}
208+
} catch (e) {
209+
if (isErrorLike(e) && e.message?.includes('rxvt')) {
210+
// Bizarrely, rxvt -h prints help but always returns a non-zero exit code.
211+
// Doesn't need any special arguments anyway though, so just ignore it
212+
} else {
213+
logError(e);
214+
}
215+
}
216+
217+
// If there's an error, or we just don't recognize the console, give up & run it directly:
218+
return { command };
219+
};
220+
188221
const terminals: _.Dictionary<ChildProcess[] | undefined> = {}
189222

190223
export class FreshTerminalInterceptor implements Interceptor {
@@ -206,7 +239,7 @@ export class FreshTerminalInterceptor implements Interceptor {
206239
const terminalSpawnArgs = await getTerminalCommand();
207240
if (!terminalSpawnArgs) throw new Error('Could not find a suitable terminal');
208241

209-
const { command, args, options, skipStartupScripts } = terminalSpawnArgs;
242+
const { command, args, options, skipStartupScripts, envVars } = terminalSpawnArgs;
210243

211244
// Our PATH override below may not work, e.g. because OSX's path_helper always prepends
212245
// the real paths over the top, and git-bash ignore env var paths overrides. To fix this,
@@ -229,6 +262,7 @@ export class FreshTerminalInterceptor implements Interceptor {
229262
env: {
230263
...currentEnv,
231264
...getTerminalEnvVars(proxyPort, this.config.https, currentEnv),
265+
...envVars,
232266
},
233267
cwd: currentEnv.HOME || currentEnv.USERPROFILE
234268
})
@@ -277,4 +311,4 @@ export class FreshTerminalInterceptor implements Interceptor {
277311

278312
await resetShellStartupScripts();
279313
}
280-
}
314+
}

0 commit comments

Comments
 (0)