98 lines
4.1 KiB
C#
98 lines
4.1 KiB
C#
using System.Runtime.InteropServices;
|
|
using MusicBridge.Agent.Media;
|
|
using MusicBridge.Agent.Network;
|
|
|
|
namespace MusicBridge.Agent.Desktop;
|
|
|
|
internal sealed class DesktopHost(Func<MediaState> read, IMediaService media, PairingService pairing,
|
|
Func<string> network, Action showQr, Action exit, bool visible = true,
|
|
StartupSettings? startup = null, bool minimized = false) : IDisposable
|
|
{
|
|
private readonly object gate = new();
|
|
private readonly TaskCompletionSource ready = new(TaskCreationOptions.RunContinuationsAsynchronously);
|
|
private Thread? thread;
|
|
private AgentWindow? window;
|
|
private bool disposed;
|
|
|
|
public Task StartAsync()
|
|
{
|
|
thread = new Thread(Run) { IsBackground = true, Name = "MusicBridge Desktop" };
|
|
thread.SetApartmentState(ApartmentState.STA);
|
|
thread.Start();
|
|
return ready.Task;
|
|
}
|
|
|
|
private void Run()
|
|
{
|
|
try
|
|
{
|
|
Application.EnableVisualStyles();
|
|
using var view = new AgentWindow(read, media, pairing, network, showQr);
|
|
view.Opacity = visible ? 1 : 0;
|
|
view.ShowInTaskbar = visible;
|
|
using var menu = new ContextMenuStrip();
|
|
void ShowWindow() { view.Show(); view.WindowState = FormWindowState.Normal; view.Activate(); }
|
|
menu.Items.Add("Открыть MusicBridge", null, (_, _) => ShowWindow());
|
|
var toggle = menu.Items.Add("Play / Pause", null, async (_, _) => await view.SendAsync(new("toggle")));
|
|
menu.Items.Add("Подключить iPhone", null, (_, _) => { pairing.Open(); showQr(); });
|
|
if (startup is not null)
|
|
{
|
|
var autoStart = new ToolStripMenuItem("Запускать вместе с Windows");
|
|
try { autoStart.Checked = startup.Enabled; }
|
|
catch (Exception) { autoStart.Enabled = false; }
|
|
autoStart.Click += (_, _) =>
|
|
{
|
|
try { startup.SetEnabled(!startup.Enabled); autoStart.Checked = startup.Enabled; }
|
|
catch (Exception ex) { MessageBox.Show(view, ex.Message, "Автозапуск MusicBridge", MessageBoxButtons.OK, MessageBoxIcon.Warning); }
|
|
};
|
|
menu.Items.Add(autoStart);
|
|
}
|
|
menu.Items.Add(new ToolStripSeparator());
|
|
menu.Items.Add("Завершить MusicBridge", null, (_, _) => { view.AllowClose = true; view.Close(); });
|
|
using var icon = new NotifyIcon { Icon = SystemIcons.Application, Text = "MusicBridge", ContextMenuStrip = menu, Visible = visible };
|
|
icon.DoubleClick += (_, _) => ShowWindow();
|
|
using var timer = new System.Windows.Forms.Timer { Interval = 1000 };
|
|
timer.Tick += (_, _) =>
|
|
{
|
|
var state = read();
|
|
toggle.Enabled = state.Capabilities.Toggle;
|
|
var text = state.HasSession ? $"MusicBridge — {state.Title}" : "MusicBridge — нет плеера";
|
|
icon.Text = text.Length > 63 ? text[..63] : text;
|
|
};
|
|
view.Shown += (_, _) => { if (minimized) view.Hide(); timer.Start(); ready.TrySetResult(); };
|
|
lock (gate)
|
|
{
|
|
if (disposed) { ready.TrySetCanceled(); return; }
|
|
window = view;
|
|
}
|
|
Application.Run(view);
|
|
icon.Visible = false;
|
|
}
|
|
catch (Exception ex) { ready.TrySetException(ex); }
|
|
finally
|
|
{
|
|
lock (gate) window = null;
|
|
exit();
|
|
}
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
lock (gate)
|
|
{
|
|
disposed = true;
|
|
if (window is { IsHandleCreated: true } view)
|
|
{
|
|
try { view.BeginInvoke(() => { view.AllowClose = true; view.Close(); }); }
|
|
catch (InvalidOperationException) { }
|
|
}
|
|
}
|
|
thread?.Join(TimeSpan.FromSeconds(2));
|
|
}
|
|
|
|
// Detach only this process; never hide or close the user's shared terminal window.
|
|
[DllImport("kernel32.dll")]
|
|
[return: MarshalAs(UnmanagedType.Bool)]
|
|
internal static extern bool FreeConsole();
|
|
}
|