using WirelessTextSyncer.Windows.Services; namespace WirelessTextSyncer.Windows.Tray; public sealed class TrayApplicationContext : ApplicationContext { private readonly KeyboardInjectionService keyboard; private readonly WebSocketServerService server; private readonly NotifyIcon notifyIcon; private ToolStripMenuItem? clipboardPasteMenuItem; private ToolStripMenuItem? sendInputTypingMenuItem; public TrayApplicationContext() { AppLogger.Info("Tray application starting."); keyboard = new KeyboardInjectionService(); var handler = new SyncMessageHandler(keyboard); server = new WebSocketServerService(handler); server.StatusChanged += (_, _) => UpdateTrayText(); notifyIcon = new NotifyIcon { Icon = SystemIcons.Application, Text = "WirelessTextSyncer starting...", Visible = true, ContextMenuStrip = BuildMenu() }; notifyIcon.MouseClick += OnTrayIconClicked; try { server.Start(8181); UpdateTrayText(); } catch (Exception ex) { AppLogger.Error("Failed to start tray application.", ex); notifyIcon.BalloonTipTitle = "WirelessTextSyncer"; notifyIcon.BalloonTipText = $"Failed to start: {ex.Message}"; notifyIcon.ShowBalloonTip(5000); } } protected override void Dispose(bool disposing) { if (disposing) { notifyIcon.Dispose(); server.Dispose(); } base.Dispose(disposing); } private ContextMenuStrip BuildMenu() { var menu = new ContextMenuStrip(); menu.Items.Add("Copy connection address", null, (_, _) => CopyConnectionAddress()); menu.Items.Add(BuildInputModeMenu()); menu.Items.Add("Exit", null, (_, _) => ExitThread()); return menu; } private ToolStripMenuItem BuildInputModeMenu() { var inputModeMenu = new ToolStripMenuItem("Input mode"); clipboardPasteMenuItem = new ToolStripMenuItem("Clipboard paste") { CheckOnClick = false }; sendInputTypingMenuItem = new ToolStripMenuItem("SendInput typing") { CheckOnClick = false }; clipboardPasteMenuItem.Click += (_, _) => SetInputMode(TextInjectionMode.ClipboardPaste); sendInputTypingMenuItem.Click += (_, _) => SetInputMode(TextInjectionMode.SendInputTyping); inputModeMenu.DropDownItems.Add(clipboardPasteMenuItem); inputModeMenu.DropDownItems.Add(sendInputTypingMenuItem); UpdateInputModeMenuChecks(); return inputModeMenu; } private void SetInputMode(TextInjectionMode mode) { keyboard.Mode = mode; AppLogger.Info($"Text injection mode changed to {mode}."); UpdateInputModeMenuChecks(); notifyIcon.BalloonTipTitle = "WirelessTextSyncer"; notifyIcon.BalloonTipText = $"Input mode: {GetInputModeLabel(mode)}"; notifyIcon.ShowBalloonTip(1500); } private void UpdateInputModeMenuChecks() { if (clipboardPasteMenuItem is not null) { clipboardPasteMenuItem.Checked = keyboard.Mode == TextInjectionMode.ClipboardPaste; } if (sendInputTypingMenuItem is not null) { sendInputTypingMenuItem.Checked = keyboard.Mode == TextInjectionMode.SendInputTyping; } } private static string GetInputModeLabel(TextInjectionMode mode) { return mode == TextInjectionMode.ClipboardPaste ? "Clipboard paste" : "SendInput typing"; } private void OnTrayIconClicked(object? sender, MouseEventArgs e) { if (e.Button == MouseButtons.Left) { CopyConnectionAddress(); } } private void CopyConnectionAddress() { Clipboard.SetText($"ws://{server.LocalIpAddress}:{server.Port}"); notifyIcon.BalloonTipTitle = "WirelessTextSyncer"; notifyIcon.BalloonTipText = "Connection address copied."; notifyIcon.ShowBalloonTip(1500); } private void UpdateTrayText() { var status = server.HasClient ? "connected" : "waiting"; var text = $"WirelessTextSyncer {server.LocalIpAddress}:{server.Port} ({status})"; notifyIcon.Text = text.Length > 63 ? text[..63] : text; } }