Choose the telnet window's colours on the window thread

The window picked a brush on the socket thread, and Avalonia crashed the
program the next time it painted that line. A line that arrives is now
posted to the window thread first. Spots no longer show the bell
character as a box, and an unexpected error in the cluster loop is
shown instead of ending it silently.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-24 00:40:11 +00:00
co-authored by Claude Sonnet 5.5
parent 085584a281
commit 0a423e9e1f
2 changed files with 16 additions and 2 deletions
+10 -2
View File
@@ -107,8 +107,15 @@ public sealed partial class TelnetWindow : RefreshableWindow
cluster.ConnectionChanged -= OnConnectionChanged;
}
private void OnLineArrived(object? sender, string line)
/// The cluster raises its events on a socket thread, and a brush belongs to
/// the thread that made it, so the colour is chosen on the window thread.
private void OnLineArrived(object? sender, string line) =>
Dispatcher.UIThread.Post(() => ShowArrived(line));
private void ShowArrived(string line)
{
// nodes end a spot with the bell character, which draws as a box
line = line.Replace("\a", "");
if (line.StartsWith("***", StringComparison.Ordinal))
{
Show(line, NoticeColour);
@@ -128,7 +135,8 @@ public sealed partial class TelnetWindow : RefreshableWindow
session.Settings.SpotFilter.PreferredSpotters.Any(one =>
one.Length > 0 && spotter.StartsWith(one, StringComparison.OrdinalIgnoreCase));
private void OnLineSent(object? sender, string line) => Show($"> {line}", SentColour);
private void OnLineSent(object? sender, string line) =>
Dispatcher.UIThread.Post(() => Show($"> {line}", SentColour));
private void OnConnectionChanged(object? sender, bool connected) =>
Dispatcher.UIThread.Post(Refresh);
+6
View File
@@ -170,6 +170,12 @@ public sealed class ClusterClient : IDisposable
catch (OperationCanceledException)
{
}
catch (Exception e)
{
// anything else would end the loop with nothing on the screen
Notice($"*** {Host}:{Port} stopped: {e}");
SetConnected(false);
}
}
private async Task SessionAsync(CancellationToken cancellation)