2026-05-07 11:09:56 -04:00
|
|
|
using System.Windows;
|
2026-05-08 00:55:57 -04:00
|
|
|
using System.Windows.Shapes;
|
2026-05-08 01:05:26 -04:00
|
|
|
using TeamsISO.App.Services;
|
2026-05-07 11:40:49 -04:00
|
|
|
using TeamsISO.App.ViewModels;
|
2026-05-07 11:09:56 -04:00
|
|
|
|
|
|
|
|
namespace TeamsISO.App;
|
|
|
|
|
|
|
|
|
|
public partial class MainWindow : Window
|
|
|
|
|
{
|
|
|
|
|
public MainWindow()
|
|
|
|
|
{
|
|
|
|
|
InitializeComponent();
|
2026-05-08 00:55:57 -04:00
|
|
|
StateChanged += OnWindowStateChanged;
|
2026-05-08 13:59:14 -04:00
|
|
|
SourceInitialized += OnSourceInitialized;
|
|
|
|
|
Closing += OnClosing;
|
2026-05-07 11:09:56 -04:00
|
|
|
}
|
2026-05-07 11:40:49 -04:00
|
|
|
|
|
|
|
|
public MainWindow(MainViewModel viewModel) : this()
|
|
|
|
|
{
|
|
|
|
|
DataContext = viewModel;
|
|
|
|
|
}
|
2026-05-08 00:55:57 -04:00
|
|
|
|
2026-05-08 13:59:14 -04:00
|
|
|
/// <summary>
|
|
|
|
|
/// Restore the window's previous placement after the HWND is created (so
|
|
|
|
|
/// SetWindowPos / WindowState transitions actually take effect). Falls
|
|
|
|
|
/// silently back to the XAML-default startup location if no snapshot exists.
|
|
|
|
|
/// </summary>
|
|
|
|
|
private void OnSourceInitialized(object? sender, EventArgs e)
|
|
|
|
|
{
|
|
|
|
|
WindowStateStore.TryApply(this);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>Persist the placement on close so next launch lands in the same spot.</summary>
|
|
|
|
|
private void OnClosing(object? sender, System.ComponentModel.CancelEventArgs e)
|
|
|
|
|
{
|
|
|
|
|
WindowStateStore.Save(this);
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-08 00:55:57 -04:00
|
|
|
/// <summary>Custom min button — chrome'd window has no system caption buttons.</summary>
|
|
|
|
|
private void OnMinimize(object sender, RoutedEventArgs e) =>
|
|
|
|
|
WindowState = WindowState.Minimized;
|
|
|
|
|
|
|
|
|
|
/// <summary>Toggles maximize/restore. Bound to the maximize button + double-click on the drag region.</summary>
|
|
|
|
|
private void OnMaximizeRestore(object sender, RoutedEventArgs e) =>
|
|
|
|
|
WindowState = WindowState == WindowState.Maximized ? WindowState.Normal : WindowState.Maximized;
|
|
|
|
|
|
|
|
|
|
/// <summary>Custom close button.</summary>
|
|
|
|
|
private void OnClose(object sender, RoutedEventArgs e) => Close();
|
|
|
|
|
|
feat: app icon, FPS, drops counter, --version, About dialog, Stop Teams toggle
Six related polish items, all building on tonight's groundwork.
1. App icon: teamsiso.ico generated from dragon-mark.png at 7 sizes (16-256), wired as ApplicationIcon in the WPF csproj, MainWindow.Icon, AboutWindow.Icon, and ARPPRODUCTICON in the WiX MSI. Taskbar / window / Add-Remove-Programs all show the dragon mark now.
2. Running incoming FPS: ring buffer of last 30 frame timestamps in IsoPipeline; ComputeFps() returns moving-average rate. Surfaced on IsoHealthStats.IncomingFps and shown in the Source column of the participants DataGrid as 'WxH · 59.94 fps'. Resets cleanly on every supervisor restart.
3. Drops counter: FrameProcessor.Stats already aggregated FramesDropped (closest-frame strategy when the receiver outpaces the processor) and FramesDuplicated; just plumbed _liveProcessor through IsoPipeline so GetStats() can read them. Exposed in the Live column under the in/out counters as a coral-tinted 'drop N'.
4. Console --version flag: prints engine version (with embedded git SHA), .NET version, OS, NDI runtime banner, expected prefix, exit-code legend, plus a wilddragon.net link. Useful for support tickets.
5. About dialog: chromeless modal with the dragon mark + version / .NET / OS / NDI runtime fields and a link to wilddragon.net. Triggered by clicking the rail logo.
6. Teams launcher Stop toggle: TeamsLauncher gains IsRunning() and StopAll(). The rail's Teams button now toggles — if Teams is up, ask to close all Teams windows via WM_CLOSE; otherwise launch as before. Confirms before stopping so we don't kill the user's call mid-transition.
Tests: 74/74 unit + 9/9 NDI integration green throughout. MSI builds clean and now embeds the dragon icon for ARP.
2026-05-08 13:50:19 -04:00
|
|
|
/// <summary>Opens the About dialog — version, NDI runtime, build SHA.</summary>
|
|
|
|
|
private void OnAboutClick(object sender, RoutedEventArgs e)
|
|
|
|
|
{
|
|
|
|
|
var about = new AboutWindow { Owner = this };
|
|
|
|
|
about.ShowDialog();
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-08 01:05:26 -04:00
|
|
|
/// <summary>
|
feat: app icon, FPS, drops counter, --version, About dialog, Stop Teams toggle
Six related polish items, all building on tonight's groundwork.
1. App icon: teamsiso.ico generated from dragon-mark.png at 7 sizes (16-256), wired as ApplicationIcon in the WPF csproj, MainWindow.Icon, AboutWindow.Icon, and ARPPRODUCTICON in the WiX MSI. Taskbar / window / Add-Remove-Programs all show the dragon mark now.
2. Running incoming FPS: ring buffer of last 30 frame timestamps in IsoPipeline; ComputeFps() returns moving-average rate. Surfaced on IsoHealthStats.IncomingFps and shown in the Source column of the participants DataGrid as 'WxH · 59.94 fps'. Resets cleanly on every supervisor restart.
3. Drops counter: FrameProcessor.Stats already aggregated FramesDropped (closest-frame strategy when the receiver outpaces the processor) and FramesDuplicated; just plumbed _liveProcessor through IsoPipeline so GetStats() can read them. Exposed in the Live column under the in/out counters as a coral-tinted 'drop N'.
4. Console --version flag: prints engine version (with embedded git SHA), .NET version, OS, NDI runtime banner, expected prefix, exit-code legend, plus a wilddragon.net link. Useful for support tickets.
5. About dialog: chromeless modal with the dragon mark + version / .NET / OS / NDI runtime fields and a link to wilddragon.net. Triggered by clicking the rail logo.
6. Teams launcher Stop toggle: TeamsLauncher gains IsRunning() and StopAll(). The rail's Teams button now toggles — if Teams is up, ask to close all Teams windows via WM_CLOSE; otherwise launch as before. Confirms before stopping so we don't kill the user's call mid-transition.
Tests: 74/74 unit + 9/9 NDI integration green throughout. MSI builds clean and now embeds the dragon icon for ARP.
2026-05-08 13:50:19 -04:00
|
|
|
/// Toggle behavior: if Teams is already running, ask to stop it; otherwise
|
|
|
|
|
/// launch via TeamsLauncher's fallback chain. First step toward the
|
|
|
|
|
/// Embedded-Teams roadmap (Phase E.1).
|
2026-05-08 01:05:26 -04:00
|
|
|
/// </summary>
|
|
|
|
|
private void OnLaunchTeamsClick(object sender, RoutedEventArgs e)
|
|
|
|
|
{
|
feat: app icon, FPS, drops counter, --version, About dialog, Stop Teams toggle
Six related polish items, all building on tonight's groundwork.
1. App icon: teamsiso.ico generated from dragon-mark.png at 7 sizes (16-256), wired as ApplicationIcon in the WPF csproj, MainWindow.Icon, AboutWindow.Icon, and ARPPRODUCTICON in the WiX MSI. Taskbar / window / Add-Remove-Programs all show the dragon mark now.
2. Running incoming FPS: ring buffer of last 30 frame timestamps in IsoPipeline; ComputeFps() returns moving-average rate. Surfaced on IsoHealthStats.IncomingFps and shown in the Source column of the participants DataGrid as 'WxH · 59.94 fps'. Resets cleanly on every supervisor restart.
3. Drops counter: FrameProcessor.Stats already aggregated FramesDropped (closest-frame strategy when the receiver outpaces the processor) and FramesDuplicated; just plumbed _liveProcessor through IsoPipeline so GetStats() can read them. Exposed in the Live column under the in/out counters as a coral-tinted 'drop N'.
4. Console --version flag: prints engine version (with embedded git SHA), .NET version, OS, NDI runtime banner, expected prefix, exit-code legend, plus a wilddragon.net link. Useful for support tickets.
5. About dialog: chromeless modal with the dragon mark + version / .NET / OS / NDI runtime fields and a link to wilddragon.net. Triggered by clicking the rail logo.
6. Teams launcher Stop toggle: TeamsLauncher gains IsRunning() and StopAll(). The rail's Teams button now toggles — if Teams is up, ask to close all Teams windows via WM_CLOSE; otherwise launch as before. Confirms before stopping so we don't kill the user's call mid-transition.
Tests: 74/74 unit + 9/9 NDI integration green throughout. MSI builds clean and now embeds the dragon icon for ARP.
2026-05-08 13:50:19 -04:00
|
|
|
if (TeamsLauncher.IsRunning())
|
|
|
|
|
{
|
|
|
|
|
var confirm = MessageBox.Show(
|
|
|
|
|
"Microsoft Teams is currently running.\n\nClose all Teams windows now?",
|
|
|
|
|
"TeamsISO — Stop Teams",
|
|
|
|
|
MessageBoxButton.YesNo,
|
|
|
|
|
MessageBoxImage.Question);
|
|
|
|
|
if (confirm != MessageBoxResult.Yes) return;
|
|
|
|
|
|
|
|
|
|
var asked = TeamsLauncher.StopAll();
|
|
|
|
|
if (TeamsLauncher.IsRunning())
|
|
|
|
|
{
|
|
|
|
|
MessageBox.Show(
|
|
|
|
|
asked == 0
|
|
|
|
|
? "No Teams windows responded to close."
|
|
|
|
|
: $"Sent close to {asked} Teams window(s); some may still be exiting.",
|
|
|
|
|
"TeamsISO — Stop Teams",
|
|
|
|
|
MessageBoxButton.OK,
|
|
|
|
|
MessageBoxImage.Information);
|
|
|
|
|
}
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-08 01:05:26 -04:00
|
|
|
if (!TeamsLauncher.TryLaunch(out var error))
|
|
|
|
|
{
|
|
|
|
|
MessageBox.Show(
|
|
|
|
|
$"Could not launch Microsoft Teams.\n\n{error}",
|
|
|
|
|
"TeamsISO — Launch Teams",
|
|
|
|
|
MessageBoxButton.OK,
|
|
|
|
|
MessageBoxImage.Warning);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-08 00:55:57 -04:00
|
|
|
/// <summary>
|
|
|
|
|
/// Swap the maximize-button glyph between the "single rectangle" (when normal) and the
|
|
|
|
|
/// "two-overlapping-rectangles" (when maximized) variants, matching the Windows 11
|
|
|
|
|
/// caption-button conventions.
|
|
|
|
|
/// </summary>
|
|
|
|
|
private void OnWindowStateChanged(object? sender, EventArgs e)
|
|
|
|
|
{
|
|
|
|
|
if (FindName("MaximizeIcon") is not Path icon) return;
|
|
|
|
|
icon.Data = WindowState == WindowState.Maximized
|
|
|
|
|
// Two-rectangle "restore" glyph
|
|
|
|
|
? System.Windows.Media.Geometry.Parse("M 2,0 L 10,0 L 10,8 M 0,2 L 8,2 L 8,10 L 0,10 Z")
|
|
|
|
|
// Single-rectangle "maximize" glyph
|
|
|
|
|
: System.Windows.Media.Geometry.Parse("M 0,0 L 10,0 L 10,10 L 0,10 Z");
|
|
|
|
|
}
|
2026-05-07 11:09:56 -04:00
|
|
|
}
|