mirror of
https://github.com/rustdesk/rustdesk.git
synced 2026-08-24 07:06:32 +00:00
* fix(terminal): send SGR mouse wheel reports with the button codes apps expect xterm.dart 4.0.0 encodes the wheel buttons as 64+4..64+7 rather than 64+0..64+3, so the low bits land on the modifier field and every wheel report the terminal emits reads as wheel-with-Shift. Strict full-screen applications reject the modified event, which is why neither the mouse wheel nor the trackpad scrolls anything once the peer application takes over the alternate screen. Install a mouse handler that keeps every upstream reporting decision and only re-encodes the wheel buttons as 64..67. Non-wheel reports pass through untouched, and the emitted bytes stay identical once upstream ships the same fix, so this can be dropped without a behavior change. Upstream: TerminalStudio/xterm.dart#238 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(terminal): correct the wheel report row, drop the wasted report build Address review feedback on the wheel button fix: - The X10/utf row was encoded as `32 + y + 1` while y is already 1-based, so every normal-mode report pointed one row too low and the `y > limit` guard disagreed with what it emitted. - Gate the wheel path on `mouseMode.reportScroll` and the button state instead of building and discarding a full report string from `defaultMouseHandler` on every scroll tick. This also makes the hardcoded SGR 'M' provably right, since a wheel release now returns before the report is built. - Derive the wire code as `id - 4` and drop `_wheelButtonId`, whose `default` branch was unreachable and defeated enum exhaustiveness. - Assign `mouseHandler` after construction so the `Terminal(...)` line stays untouched. Cover the utf, urxvt, null-byte overflow and click-only branches, and assert that TerminalModel actually installs the handler. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
69 lines
1.9 KiB
Dart
69 lines
1.9 KiB
Dart
import 'dart:async';
|
|
|
|
import 'package:flutter_hbb/models/model.dart';
|
|
import 'package:flutter_hbb/models/terminal_model.dart';
|
|
import 'package:flutter_test/flutter_test.dart';
|
|
import 'package:xterm/xterm.dart';
|
|
|
|
class _FakeFFI implements FFI {
|
|
@override
|
|
String id = 'test-peer';
|
|
|
|
@override
|
|
dynamic noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation);
|
|
}
|
|
|
|
void main() {
|
|
test('ignores paste that completes after the terminal model is disposed',
|
|
() async {
|
|
final model = TerminalModel(_FakeFFI());
|
|
final delayedClipboardText = Completer<String>();
|
|
|
|
// This mirrors Ctrl/Cmd+V: clipboard access starts first, then the page and
|
|
// model are disposed before the asynchronous read supplies its text.
|
|
final paste = delayedClipboardText.future.then(model.pasteText);
|
|
model.dispose();
|
|
delayedClipboardText.complete('late clipboard text');
|
|
await paste;
|
|
|
|
expect(model.debugBufferedInputCount, 0);
|
|
});
|
|
|
|
test('ignores terminal text input after the terminal model is disposed', () {
|
|
final model = TerminalModel(_FakeFFI());
|
|
var checkedCtrlLock = false;
|
|
var clearedCtrlLock = false;
|
|
|
|
model.isCtrlLocked = () {
|
|
checkedCtrlLock = true;
|
|
return true;
|
|
};
|
|
model.clearCtrlLock = () {
|
|
clearedCtrlLock = true;
|
|
};
|
|
|
|
model.dispose();
|
|
model.terminal.textInput('d');
|
|
|
|
expect(checkedCtrlLock, isFalse);
|
|
expect(clearedCtrlLock, isFalse);
|
|
expect(model.debugBufferedInputCount, 0);
|
|
});
|
|
|
|
test('builds its terminal with the wheel button fix', () {
|
|
final model = TerminalModel(_FakeFFI());
|
|
addTearDown(model.dispose);
|
|
|
|
final captured = <String>[];
|
|
model.terminal.onOutput = captured.add;
|
|
model.terminal.write('\x1b[?1000h\x1b[?1006h');
|
|
model.terminal.mouseInput(
|
|
TerminalMouseButton.wheelUp,
|
|
TerminalMouseButtonState.down,
|
|
const CellOffset(10, 5),
|
|
);
|
|
|
|
expect(captured.single, '\x1b[<64;11;6M');
|
|
});
|
|
}
|