-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathTickerOverlayManager.cs
More file actions
162 lines (141 loc) · 6.13 KB
/
TickerOverlayManager.cs
File metadata and controls
162 lines (141 loc) · 6.13 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Windows.Forms;
using NaturalCommands.Models;
namespace NaturalCommands
{
/// <summary>
/// Centralized manager that ensures exactly one persistent TickerOverlayForm
/// exists per process and provides a thread-safe API to add messages.
/// </summary>
internal static class TickerOverlayManager
{
private static TickerOverlayForm? _form;
private static Thread? _uiThread;
private static ManualResetEventSlim? _handleReady;
private static readonly object _lock = new();
/// <summary>
/// Ensure a persistent ticker form exists and seed it with the provided lines.
/// If the form already exists, the lines are appended instead of recreating.
/// </summary>
public static void EnsurePersistentTicker(IEnumerable<string> initialLines, int cycleSeconds = 5, int maxCycles = 0, bool topPosition = false, bool hideOnDismiss = true, bool force = false)
{
if (initialLines == null) initialLines = Array.Empty<string>();
// Respect user setting to disable the ticker by default. Allow callers to override with `force=true`.
if (!force)
{
try
{
if (!AppSettings.Instance.Notifications.TickerEnabled)
{
try { Helpers.Logger.LogInfo("[TICKER-MGR] Ticker disabled in settings; skipping creation"); } catch { }
return;
}
}
catch (Exception ex)
{
try { Helpers.Logger.LogWarning($"[TICKER-MGR] Failed to read settings: {ex.Message}"); } catch { }
}
}
lock (_lock)
{
// If an active form exists, append the lines and return
if (_form != null && !_form.IsDisposed)
{
try
{
foreach (var m in initialLines.Select(TickerOverlayForm.ParseSingleLine))
{
_form.AddMessage(m);
}
}
catch { }
return;
}
// If UI thread already started, wait briefly for handle
if (_uiThread != null && _uiThread.IsAlive && _handleReady != null && !_handleReady.IsSet)
{
try { _handleReady.Wait(TimeSpan.FromSeconds(5)); } catch { }
if (_form != null && !_form.IsDisposed)
{
try
{
foreach (var m in initialLines.Select(TickerOverlayForm.ParseSingleLine))
_form.AddMessage(m);
}
catch { }
return;
}
}
// Otherwise create a new STA UI thread that owns the form
_handleReady?.Dispose();
_handleReady = new ManualResetEventSlim(false);
var linesCopy = initialLines.ToList();
_uiThread = new Thread(() =>
{
try
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
var form = new TickerOverlayForm(linesCopy, cycleSeconds: cycleSeconds, maxCycles: maxCycles, topPosition: topPosition, hideOnDismiss: hideOnDismiss);
form.FormClosed += (s, e) => { lock (_lock) { _form = null; } };
// Force handle creation so callers can Invoke safely
var _ = form.Handle;
lock (_lock) { _form = form; }
try { _handleReady.Set(); } catch { }
try { Helpers.Logger.LogInfo("[TICKER-MGR] Persistent ticker created"); } catch { }
Application.Run(form);
}
catch (Exception ex)
{
try { Helpers.Logger.LogError($"[TICKER-MGR] Failed to create ticker: {ex.Message}"); } catch { }
}
finally
{
lock (_lock)
{
try { _handleReady?.Dispose(); } catch { }
_handleReady = null;
_uiThread = null;
_form = null;
}
}
})
{
IsBackground = false
};
_uiThread.SetApartmentState(ApartmentState.STA);
_uiThread.Start();
try { _handleReady.Wait(TimeSpan.FromSeconds(5)); } catch { }
}
}
/// <summary>
/// Add messages to the persistent ticker. Ensures a persistent form exists.
/// </summary>
public static void AddMessages(IEnumerable<TickerMessage> messages)
{
if (messages == null) return;
lock (_lock)
{
if (_form == null || _form.IsDisposed)
{
// Create a persistent ticker with a placeholder so we have a UI owner
EnsurePersistentTicker(new[] { "Ticker ready" }, cycleSeconds: 5, maxCycles: 0, topPosition: false, hideOnDismiss: true);
}
if (_form != null && !_form.IsDisposed)
{
foreach (var msg in messages)
{
try { _form.AddMessage(msg); } catch (Exception ex) { try { Helpers.Logger.LogWarning($"[TICKER-MGR] Failed to enqueue message: {ex.Message}"); } catch { } }
}
}
}
}
public static bool IsAlive
{
get { lock (_lock) { return _form != null && !_form.IsDisposed; } }
}
}
}