From 0a423e9e1f333e576535ee38df7b4559a9fe3a14 Mon Sep 17 00:00:00 2001 From: ericek111 Date: Sat, 24 Oct 2026 00:40:11 +0000 Subject: [PATCH] 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 --- src/Nonemm.App/Windows/TelnetWindow.axaml.cs | 12 ++++++++++-- src/Nonemm.Spotting/ClusterClient.cs | 6 ++++++ 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/src/Nonemm.App/Windows/TelnetWindow.axaml.cs b/src/Nonemm.App/Windows/TelnetWindow.axaml.cs index 36729db..099b097 100644 --- a/src/Nonemm.App/Windows/TelnetWindow.axaml.cs +++ b/src/Nonemm.App/Windows/TelnetWindow.axaml.cs @@ -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); diff --git a/src/Nonemm.Spotting/ClusterClient.cs b/src/Nonemm.Spotting/ClusterClient.cs index 6806f6a..c162b0f 100644 --- a/src/Nonemm.Spotting/ClusterClient.cs +++ b/src/Nonemm.Spotting/ClusterClient.cs @@ -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)