mirror of
https://github.com/rustdesk/rustdesk.git
synced 2026-05-08 15:18:13 +03:00
fix(terminal): avoid reconnect stalls and delayed layout writes
Signed-off-by: fufesou <linlong1266@gmail.com>
This commit is contained in:
@@ -32,6 +32,7 @@ class TerminalModel with ChangeNotifier {
|
|||||||
static const int _kMaxOutputBufferChars = 8 * 1024;
|
static const int _kMaxOutputBufferChars = 8 * 1024;
|
||||||
// View ready state: true when terminal has valid dimensions, safe to write
|
// View ready state: true when terminal has valid dimensions, safe to write
|
||||||
bool _terminalViewReady = false;
|
bool _terminalViewReady = false;
|
||||||
|
bool _markViewReadyScheduled = false;
|
||||||
bool _suppressTerminalOutput = false;
|
bool _suppressTerminalOutput = false;
|
||||||
bool _suppressNextTerminalDataOutput = false;
|
bool _suppressNextTerminalDataOutput = false;
|
||||||
|
|
||||||
@@ -91,7 +92,7 @@ class TerminalModel with ChangeNotifier {
|
|||||||
// Mark terminal view as ready and flush any buffered output on first valid resize.
|
// Mark terminal view as ready and flush any buffered output on first valid resize.
|
||||||
// Must be after onResizeExternal so the view layer has valid dimensions before flushing.
|
// Must be after onResizeExternal so the view layer has valid dimensions before flushing.
|
||||||
if (!_terminalViewReady) {
|
if (!_terminalViewReady) {
|
||||||
_markViewReady();
|
_scheduleMarkViewReady();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_terminalOpened) {
|
if (_terminalOpened) {
|
||||||
@@ -296,7 +297,7 @@ class TerminalModel with ChangeNotifier {
|
|||||||
if (!_terminalViewReady &&
|
if (!_terminalViewReady &&
|
||||||
terminal.viewWidth > 0 &&
|
terminal.viewWidth > 0 &&
|
||||||
terminal.viewHeight > 0) {
|
terminal.viewHeight > 0) {
|
||||||
_markViewReady();
|
_scheduleMarkViewReady();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Process any buffered input
|
// Process any buffered input
|
||||||
@@ -447,6 +448,18 @@ class TerminalModel with ChangeNotifier {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Mark terminal view as ready and flush buffered output.
|
/// Mark terminal view as ready and flush buffered output.
|
||||||
|
void _scheduleMarkViewReady() {
|
||||||
|
if (_disposed || _terminalViewReady || _markViewReadyScheduled) return;
|
||||||
|
_markViewReadyScheduled = true;
|
||||||
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
|
_markViewReadyScheduled = false;
|
||||||
|
if (_disposed || _terminalViewReady) return;
|
||||||
|
if (terminal.viewWidth > 0 && terminal.viewHeight > 0) {
|
||||||
|
_markViewReady();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
void _markViewReady() {
|
void _markViewReady() {
|
||||||
if (_terminalViewReady) return;
|
if (_terminalViewReady) return;
|
||||||
_terminalViewReady = true;
|
_terminalViewReady = true;
|
||||||
@@ -474,6 +487,7 @@ class TerminalModel with ChangeNotifier {
|
|||||||
_pendingOutputChunks.clear();
|
_pendingOutputChunks.clear();
|
||||||
_pendingOutputSuppressFlags.clear();
|
_pendingOutputSuppressFlags.clear();
|
||||||
_pendingOutputSize = 0;
|
_pendingOutputSize = 0;
|
||||||
|
_markViewReadyScheduled = false;
|
||||||
_suppressNextTerminalDataOutput = false;
|
_suppressNextTerminalDataOutput = false;
|
||||||
// Terminal cleanup is handled server-side when service closes
|
// Terminal cleanup is handled server-side when service closes
|
||||||
super.dispose();
|
super.dispose();
|
||||||
|
|||||||
@@ -485,6 +485,17 @@ impl OutputBuffer {
|
|||||||
} else {
|
} else {
|
||||||
self.total_size -= removed.len();
|
self.total_size -= removed.len();
|
||||||
}
|
}
|
||||||
|
if self.lines.is_empty() {
|
||||||
|
self.last_line_incomplete = false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log::error!(
|
||||||
|
"OutputBuffer trim invariant broken: total_size={}, lines_len=0",
|
||||||
|
self.total_size
|
||||||
|
);
|
||||||
|
self.total_size = 0;
|
||||||
|
self.last_line_incomplete = false;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1607,18 +1618,10 @@ impl TerminalServiceProxy {
|
|||||||
data: &TerminalData,
|
data: &TerminalData,
|
||||||
) -> Result<Option<TerminalResponse>> {
|
) -> Result<Option<TerminalResponse>> {
|
||||||
if let Some(session_arc) = session {
|
if let Some(session_arc) = session {
|
||||||
let mut session = match session_arc.lock() {
|
let input = {
|
||||||
Ok(guard) => guard,
|
let mut session = session_arc.lock().unwrap();
|
||||||
Err(e) => {
|
|
||||||
return Err(anyhow!(
|
|
||||||
"Failed to lock terminal session {} for input handling: {}",
|
|
||||||
data.terminal_id,
|
|
||||||
e
|
|
||||||
));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
session.update_activity();
|
session.update_activity();
|
||||||
if let Some(input_tx) = &session.input_tx {
|
if let Some(input_tx) = session.input_tx.clone() {
|
||||||
// Encode data for helper mode or send raw for direct PTY mode
|
// Encode data for helper mode or send raw for direct PTY mode
|
||||||
#[cfg(target_os = "windows")]
|
#[cfg(target_os = "windows")]
|
||||||
let msg = if session.is_helper_mode {
|
let msg = if session.is_helper_mode {
|
||||||
@@ -1629,7 +1632,14 @@ impl TerminalServiceProxy {
|
|||||||
#[cfg(not(target_os = "windows"))]
|
#[cfg(not(target_os = "windows"))]
|
||||||
let msg = data.data.to_vec();
|
let msg = data.data.to_vec();
|
||||||
|
|
||||||
// Send data to writer thread
|
Some((input_tx, msg))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some((input_tx, msg)) = input {
|
||||||
|
// Send outside the session lock; SyncSender::send can block when full.
|
||||||
if let Err(e) = input_tx.send(msg) {
|
if let Err(e) = input_tx.send(msg) {
|
||||||
log::error!(
|
log::error!(
|
||||||
"Failed to send data to terminal {}: {}",
|
"Failed to send data to terminal {}: {}",
|
||||||
|
|||||||
Reference in New Issue
Block a user