fix: catch crash of fg service (#669)

This commit is contained in:
lollipopkit🏳️‍⚧️
2025-01-04 16:22:20 +08:00
committed by GitHub
parent 0bbd0b43b3
commit 7f58237589
40 changed files with 497 additions and 272 deletions

View File

@@ -76,7 +76,6 @@ class SSHPageState extends State<SSHPage>
@override
void dispose() {
super.dispose();
_virtKeyLongPressTimer?.cancel();
_terminalController.dispose();
_discontinuityTimer?.cancel();
@@ -87,6 +86,7 @@ class SSHPageState extends State<SSHPage>
MethodChans.stopService();
}
}
super.dispose();
}
@override
@@ -270,6 +270,161 @@ class SSHPageState extends State<SSHPage>
);
}
@override
bool get wantKeepAlive => true;
void _initStoredCfg() {
final fontFamilly = Stores.setting.fontPath.fetch().getFileName();
final textSize = Stores.setting.termFontSize.fetch();
final textStyle = TextStyle(
fontFamily: fontFamilly,
fontSize: textSize,
);
_terminalStyle = TerminalStyle.fromTextStyle(textStyle);
}
Future<void> _showHelp() async {
if (Stores.setting.sshTermHelpShown.fetch()) return;
return await context.showRoundDialog(
title: libL10n.doc,
child: Text(l10n.sshTermHelp),
actions: [
TextButton(
onPressed: () {
Stores.setting.sshTermHelpShown.put(true);
context.pop();
},
child: Text(l10n.noPromptAgain),
),
],
);
}
@override
FutureOr<void> afterFirstLayout(BuildContext context) async {
await _showHelp();
await _initTerminal();
if (Stores.setting.sshWakeLock.fetch()) WakelockPlus.enable();
}
}
extension _Init on SSHPageState {
Future<void> _initTerminal() async {
_writeLn(l10n.waitConnection);
_client ??= await genClient(
widget.spi,
onStatus: (p0) {
_writeLn(p0.toString());
},
onKeyboardInteractive: _onKeyboardInteractive,
);
_writeLn('${libL10n.execute}: Shell');
final session = await _client?.shell(
pty: SSHPtyConfig(
width: _terminal.viewWidth,
height: _terminal.viewHeight,
),
environment: widget.spi.envs,
);
//_setupDiscontinuityTimer();
if (session == null) {
_writeLn(libL10n.fail);
return;
}
_terminal.buffer.clear();
_terminal.buffer.setCursor(0, 0);
_terminal.onOutput = (data) {
session.write(utf8.encode(data));
};
_terminal.onResize = (width, height, pixelWidth, pixelHeight) {
session.resizeTerminal(width, height);
};
_listen(session.stdout);
_listen(session.stderr);
for (final snippet in SnippetProvider.snippets.value) {
if (snippet.autoRunOn?.contains(widget.spi.id) == true) {
snippet.runInTerm(_terminal, widget.spi);
}
}
if (widget.initCmd != null) {
_terminal.textInput(widget.initCmd!);
_terminal.keyInput(TerminalKey.enter);
}
if (widget.initSnippet != null) {
widget.initSnippet!.runInTerm(_terminal, widget.spi);
}
widget.focusNode?.requestFocus();
await session.done;
if (mounted && widget.notFromTab) {
context.pop();
}
widget.onSessionEnd?.call();
}
void _listen(Stream<Uint8List>? stream) {
if (stream == null) {
return;
}
stream
.cast<List<int>>()
.transform(const Utf8Decoder())
.listen(_terminal.write);
}
void _setupDiscontinuityTimer() {
_discontinuityTimer = Timer.periodic(
const Duration(seconds: 5),
(_) async {
var throwTimeout = true;
Future.delayed(const Duration(seconds: 3), () {
if (throwTimeout) {
_catchTimeout();
}
});
await _client?.ping();
throwTimeout = false;
},
);
}
void _catchTimeout() {
_discontinuityTimer?.cancel();
if (!mounted) return;
_writeLn('\n\nConnection lost\r\n');
context.showRoundDialog(
title: libL10n.attention,
child: Text('${l10n.disconnected}\n${l10n.goBackQ}'),
barrierDismiss: false,
actions: Btn.ok(
onTap: () {
if (mounted) {
context.pop();
}
},
).toList,
);
}
void _writeLn(String p0) {
_terminal.write('$p0\r\n');
}
}
extension _VirtKey on SSHPageState {
void _doVirtualKey(VirtKey item) {
if (item.func != null) {
HapticFeedback.mediumImpact();
@@ -380,10 +535,6 @@ class SSHPageState extends State<SSHPage>
return _terminal.buffer.getText(range);
}
void _writeLn(String p0) {
_terminal.write('$p0\r\n');
}
void _initVirtKeys() {
final virtKeys = VirtKeyX.loadFromStore();
for (int len = 0; len < virtKeys.length; len += 7) {
@@ -398,151 +549,4 @@ class SSHPageState extends State<SSHPage>
FutureOr<List<String>?> _onKeyboardInteractive(SSHUserInfoRequest req) {
return KeybordInteractive.defaultHandle(widget.spi, ctx: context);
}
Future<void> _initTerminal() async {
_writeLn(l10n.waitConnection);
_client ??= await genClient(
widget.spi,
onStatus: (p0) {
_writeLn(p0.toString());
},
onKeyboardInteractive: _onKeyboardInteractive,
);
_writeLn('${libL10n.execute}: Shell');
final session = await _client?.shell(
pty: SSHPtyConfig(
width: _terminal.viewWidth,
height: _terminal.viewHeight,
),
environment: widget.spi.envs,
);
//_setupDiscontinuityTimer();
if (session == null) {
_writeLn(libL10n.fail);
return;
}
_terminal.buffer.clear();
_terminal.buffer.setCursor(0, 0);
_terminal.onOutput = (data) {
session.write(utf8.encode(data));
};
_terminal.onResize = (width, height, pixelWidth, pixelHeight) {
session.resizeTerminal(width, height);
};
_listen(session.stdout);
_listen(session.stderr);
for (final snippet in SnippetProvider.snippets.value) {
if (snippet.autoRunOn?.contains(widget.spi.id) == true) {
snippet.runInTerm(_terminal, widget.spi);
}
}
if (widget.initCmd != null) {
_terminal.textInput(widget.initCmd!);
_terminal.keyInput(TerminalKey.enter);
}
if (widget.initSnippet != null) {
widget.initSnippet!.runInTerm(_terminal, widget.spi);
}
widget.focusNode?.requestFocus();
await session.done;
if (mounted && widget.notFromTab) {
context.pop();
}
widget.onSessionEnd?.call();
}
void _listen(Stream<Uint8List>? stream) {
if (stream == null) {
return;
}
stream
.cast<List<int>>()
.transform(const Utf8Decoder())
.listen(_terminal.write);
}
void _setupDiscontinuityTimer() {
_discontinuityTimer = Timer.periodic(
const Duration(seconds: 5),
(_) async {
var throwTimeout = true;
Future.delayed(const Duration(seconds: 3), () {
if (throwTimeout) {
_catchTimeout();
}
});
await _client?.ping();
throwTimeout = false;
},
);
}
void _catchTimeout() {
_discontinuityTimer?.cancel();
if (!mounted) return;
_writeLn('\n\nConnection lost\r\n');
context.showRoundDialog(
title: libL10n.attention,
child: Text('${l10n.disconnected}\n${l10n.goBackQ}'),
barrierDismiss: false,
actions: Btn.ok(
onTap: () {
if (mounted) {
context.pop();
}
},
).toList,
);
}
@override
bool get wantKeepAlive => true;
void _initStoredCfg() {
final fontFamilly = Stores.setting.fontPath.fetch().getFileName();
final textSize = Stores.setting.termFontSize.fetch();
final textStyle = TextStyle(
fontFamily: fontFamilly,
fontSize: textSize,
);
_terminalStyle = TerminalStyle.fromTextStyle(textStyle);
}
Future<void> _showHelp() async {
if (Stores.setting.sshTermHelpShown.fetch()) return;
return await context.showRoundDialog(
title: libL10n.doc,
child: Text(l10n.sshTermHelp),
actions: [
TextButton(
onPressed: () {
Stores.setting.sshTermHelpShown.put(true);
context.pop();
},
child: Text(l10n.noPromptAgain),
),
],
);
}
@override
FutureOr<void> afterFirstLayout(BuildContext context) async {
await _showHelp();
await _initTerminal();
if (Stores.setting.sshWakeLock.fetch()) WakelockPlus.enable();
}
}

View File

@@ -67,30 +67,6 @@ class _SSHTabPageState extends State<SSHTabPage>
);
}
void _onTapTab(int idx) async {
await _toPage(idx);
}
void _onTapClose(String name) async {
final confirm = await showDialog<bool>(
context: context,
builder: (context) {
return AlertDialog(
title: Text(libL10n.attention),
content: Text('${libL10n.close} SSH ${l10n.conn}($name) ?'),
actions: Btnx.okReds,
);
},
);
Future.delayed(Durations.short1, FocusScope.of(context).unfocus);
if (confirm != true) return;
_tabMap.remove(name);
_tabRN.notify();
_pageCtrl.previousPage(
duration: Durations.medium1, curve: Curves.fastEaseInToSlowEaseOut);
}
Widget _buildBody() {
return ListenBuilder(
listenable: _tabRN,
@@ -109,6 +85,11 @@ class _SSHTabPageState extends State<SSHTabPage>
);
}
@override
bool get wantKeepAlive => true;
}
extension on _SSHTabPageState {
void _onTapInitCard(Spi spi) async {
final name = () {
final reg = RegExp('${spi.name}\\((\\d+)\\)');
@@ -155,8 +136,29 @@ class _SSHTabPageState extends State<SSHTabPage>
}
}
@override
bool get wantKeepAlive => true;
void _onTapTab(int idx) async {
await _toPage(idx);
}
void _onTapClose(String name) async {
final confirm = await showDialog<bool>(
context: context,
builder: (context) {
return AlertDialog(
title: Text(libL10n.attention),
content: Text('${libL10n.close} SSH ${l10n.conn}($name) ?'),
actions: Btnx.okReds,
);
},
);
Future.delayed(Durations.short1, FocusScope.of(context).unfocus);
if (confirm != true) return;
_tabMap.remove(name);
_tabRN.notify();
_pageCtrl.previousPage(
duration: Durations.medium1, curve: Curves.fastEaseInToSlowEaseOut);
}
}
final class _TabBar extends StatelessWidget implements PreferredSizeWidget {