RDP-Verbindungen als Tabs (mstsc/FreeRDP eingebettet); App-Kürzel im Terminal auf Strg+Shift, damit Strg+W/K/T bei nano & Co. ankommen
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
+65
-1
@@ -8,9 +8,16 @@ const { Store } = require('./store');
|
||||
const { SshManager } = require('./ssh');
|
||||
const rdp = require('./rdp');
|
||||
const { Updater } = require('./updater');
|
||||
const { createEmbed, embedSupported } = require('./rdp-embed');
|
||||
|
||||
let win;
|
||||
const store = new Store();
|
||||
|
||||
// Unter Linux muss MrTerm für eingebettete RDP-Tabs als X11-Client laufen (unter Wayland via XWayland).
|
||||
// Die Wahl wird unverschlüsselt in launch.json gespeichert, weil sie vor dem Start des Fensters gebraucht wird.
|
||||
const launchFile = path.join(app.getPath('userData'), 'launch.json');
|
||||
function readLaunch() { try { return JSON.parse(fs.readFileSync(launchFile, 'utf8')); } catch { return {}; } }
|
||||
if (process.platform === 'linux' && readLaunch().x11 !== false) app.commandLine.appendSwitch('ozone-platform', 'x11');
|
||||
const pendingSecrets = new Map();
|
||||
|
||||
function send(channel, ...args) {
|
||||
@@ -85,7 +92,10 @@ ipcMain.on('win:close', () => win.close());
|
||||
handle('vault:get', () => ({ ...store.get(), encrypted: store.encrypted, platform: process.platform }));
|
||||
handle('vault:upsert', (col, item) => store.upsert(col, item));
|
||||
handle('vault:remove', (col, id) => store.remove(col, id));
|
||||
handle('vault:settings', (s) => store.setSettings(s));
|
||||
handle('vault:settings', (s) => {
|
||||
store.setSettings(s);
|
||||
if ('rdpEmbed' in s) fs.writeFileSync(launchFile, JSON.stringify({ ...readLaunch(), x11: s.rdpEmbed !== false }));
|
||||
});
|
||||
handle('vault:forgetHost', (id) => { delete store.get().knownHosts[id]; store.save(); });
|
||||
handle('vault:export', async () => {
|
||||
const r = await dialog.showSaveDialog(win, { defaultPath: 'mrterm-backup.json', filters: [{ name: 'JSON', extensions: ['json'] }] });
|
||||
@@ -244,6 +254,60 @@ handle('rdp:launch', (hostId) => {
|
||||
return r.client;
|
||||
});
|
||||
|
||||
// Eingebettete RDP-Sitzungen (Anzeige im Tab)
|
||||
const rdpSessions = new Map();
|
||||
handle('rdp:embedSupported', () => {
|
||||
const s = embedSupported();
|
||||
if (s.ok && process.platform === 'linux' && !rdp.linuxClient(null, true)) return { ok: false, reason: 'xfreerdp3 fehlt (sudo pacman -S freerdp)' };
|
||||
return s;
|
||||
});
|
||||
handle('rdp:open', async (id, hostRef, bounds) => {
|
||||
const host = hostWithOverrides(hostRef);
|
||||
if (host.id) store.addHistory({ hostId: host.id, at: Date.now() });
|
||||
const emb = createEmbed(win.getNativeWindowHandle());
|
||||
const sess = { emb, cancelled: false };
|
||||
rdpSessions.set(id, sess);
|
||||
const size = { width: bounds.width, height: bounds.height };
|
||||
let proc;
|
||||
if (process.platform === 'win32') {
|
||||
const file = rdp.writeRdpFile(host, size);
|
||||
proc = await new Promise((resolve) => rdp.withWinCredentials(host, () => resolve(emb.start({ rdpFile: file }))));
|
||||
} else {
|
||||
proc = emb.start({ client: rdp.linuxClient(null, true), args: rdp.freerdpArgs(host, size), password: host.password });
|
||||
}
|
||||
proc.on('error', (e) => send('rdp:event', id, 'exit', { code: -1, message: e.message }));
|
||||
proc.on('exit', (code) => {
|
||||
const err = (emb.err || '').split('\n').filter((l) => /ERROR|error/.test(l)).slice(-1)[0] || '';
|
||||
if (rdpSessions.get(id) === sess) send('rdp:event', id, 'exit', { code, message: err });
|
||||
rdpSessions.delete(id);
|
||||
});
|
||||
const ok = await emb.attach(bounds, () => sess.cancelled);
|
||||
if (ok) {
|
||||
if (sess.hidden) emb.hide();
|
||||
send('rdp:event', id, 'attached');
|
||||
}
|
||||
return ok;
|
||||
});
|
||||
ipcMain.on('rdp:bounds', (_e, id, b) => { try { rdpSessions.get(id)?.emb.setBounds(b); } catch {} });
|
||||
ipcMain.on('rdp:show', (_e, id) => { const s = rdpSessions.get(id); if (s) { s.hidden = false; try { s.emb.show(); s.emb.focus(); } catch {} } });
|
||||
ipcMain.on('rdp:hide', (_e, id) => { const s = rdpSessions.get(id); if (s) { s.hidden = true; try { s.emb.hide(); } catch {} } });
|
||||
ipcMain.on('rdp:focus', (_e, id) => { try { rdpSessions.get(id)?.emb.focus(); } catch {} });
|
||||
ipcMain.on('rdp:close', (_e, id) => {
|
||||
const s = rdpSessions.get(id);
|
||||
if (!s) return;
|
||||
s.cancelled = true;
|
||||
rdpSessions.delete(id);
|
||||
s.emb.kill();
|
||||
});
|
||||
handle('rdp:detach', (id) => {
|
||||
const s = rdpSessions.get(id);
|
||||
if (!s) return false;
|
||||
if (process.platform !== 'win32') return false;
|
||||
s.emb.detach();
|
||||
rdpSessions.delete(id); // läuft als eigenständiges Fenster weiter
|
||||
return true;
|
||||
});
|
||||
|
||||
// ---------- Import aus anderen Programmen (z. B. Devolutions RDM) ----------
|
||||
handle('import:pickFile', async (title, extensions) => {
|
||||
const r = await dialog.showOpenDialog(win, { title, filters: [{ name: title, extensions }, { name: 'Alle Dateien', extensions: ['*'] }], properties: ['openFile'] });
|
||||
|
||||
Reference in New Issue
Block a user