/trunk/Hush/Hush.cs |
@@ -8,19 +8,25 @@ |
using System.Threading; |
using System.Threading.Tasks; |
using System.Windows.Forms; |
using Gma.System.MouseKeyHook; |
using Hush.Chat; |
using Hush.Communication; |
using Hush.Discovery; |
using Hush.Properties; |
using Hush.Utilities; |
using MQTTnet.Extensions.ManagedClient; |
using MQTTnet.Server; |
using WingMan.Communication; |
using Message = System.Windows.Forms.Message; |
|
namespace Hush |
{ |
public partial class Hush : Form |
{ |
private static TaskScheduler FormTaskScheduler { get; set; } |
private static CancellationTokenSource FormCancellationTokenSource { get; set; } |
private static MqttCommunication MqttCommunication { get; set; } |
private static ChatMessageSynchronizer ChatMessageSynchronizer { get; set; } |
private static Discovery.Discovery Discovery { get; set; } |
|
public Hush() |
{ |
InitializeComponent(); |
@@ -28,18 +34,11 @@ |
FormTaskScheduler = TaskScheduler.FromCurrentSynchronizationContext(); |
FormCancellationTokenSource = new CancellationTokenSource(); |
|
// Initialize a message buffer. |
Messages = new List<string>(); |
|
// Bind to settings changed event. |
Settings.Default.SettingsLoaded += DefaultOnSettingsLoaded; |
Settings.Default.SettingsSaving += DefaultOnSettingsSaving; |
Settings.Default.PropertyChanged += DefaultOnPropertyChanged; |
|
// Set up keyboard hook. |
MouseKeyGlobalHook = Hook.GlobalEvents(); |
MouseKeyGlobalHook.KeyDown += MouseKeyGlobalHookOnKeyDown; |
|
// Set up discovery. |
Discovery = new Discovery.Discovery(Constants.AssemblyName, FormCancellationTokenSource.Token, |
FormTaskScheduler); |
@@ -47,216 +46,21 @@ |
|
// Bind to MQTT events. |
MqttCommunication = new MqttCommunication(FormTaskScheduler, FormCancellationTokenSource.Token); |
// TODO implement events. |
//MqttCommunication. |
MqttCommunication.OnClientConnectionFailed += MqttOnClientConnectionFailed; |
MqttCommunication.OnClientAuthenticationFailed += MqttOnClientAuthenticationFailed; |
MqttCommunication.OnClientConnected += MqttOnClientConnected; |
MqttCommunication.OnClientDisconnected += MqttOnClientDisconnected; |
MqttCommunication.OnServerClientConnected += MqttCommunicationOnOnServerClientConnected; |
MqttCommunication.OnServerClientDisconnected += MqttOnServerClientDisconnected; |
|
// Start message synchronizer. |
ChatMessageSynchronizer = new ChatMessageSynchronizer(Constants.MqttTopic, MqttCommunication, FormTaskScheduler, |
ChatMessageSynchronizer = new ChatMessageSynchronizer(Constants.MqttTopic, MqttCommunication, |
FormTaskScheduler, |
FormCancellationTokenSource.Token); |
ChatMessageSynchronizer.OnMessageReceived += OnMessageReceived; |
} |
|
private static IKeyboardMouseEvents MouseKeyGlobalHook { get; set; } |
private static TaskScheduler FormTaskScheduler { get; set; } |
private static CancellationTokenSource FormCancellationTokenSource { get; set; } |
private static MqttCommunication MqttCommunication { get; set; } |
private static ChatMessageSynchronizer ChatMessageSynchronizer { get; set; } |
private static Discovery.Discovery Discovery { get; set; } |
private static List<string> Messages { get; set; } |
|
private void MouseKeyGlobalHookOnKeyDown(object sender, KeyEventArgs keyEventArgs) |
{ |
// Bind to CTRL+C |
if (!keyEventArgs.Control || keyEventArgs.KeyCode != Keys.C) |
return; |
|
ActiveControl = messageTextBox; |
messageTextBox.Focus(); |
} |
|
private void OnDiscoveryPortMapFailed(object sender, DiscoveryFailedEventArgs args) |
{ |
notifyIcon1.BalloonTipIcon = ToolTipIcon.Warning; |
notifyIcon1.BalloonTipTitle = Strings.Network_warning; |
notifyIcon1.BalloonTipText = Strings.Failed_to_create_automatic_NAT_port_mapping; |
notifyIcon1.ShowBalloonTip(1000); |
} |
|
private void OnMessageReceived(object sender, ChatMessageReceivedEventArgs e) |
{ |
var message = $"{e.Nick} : {e.Message}"; |
|
Messages.Add(message); |
|
// Keep the content of the text box limited to the size of the displayed text. |
var lineHeight = TextRenderer.MeasureText(message, chatTextBox.Font).Height; |
var linesPerPage = (int) Math.Ceiling(1.0f * chatTextBox.ClientSize.Height / lineHeight); |
|
if (Messages.Count > linesPerPage) |
Messages.RemoveRange(0, Messages.Count - 1 - linesPerPage); |
|
chatTextBox.Text = string.Join(Environment.NewLine, Messages); |
} |
|
private async void DefaultOnPropertyChanged(object sender, PropertyChangedEventArgs e) |
{ |
if (string.Equals(e.PropertyName, "LaunchOnBoot")) |
LaunchOnBoot.Set(Settings.Default.LaunchOnBoot); |
|
if (string.Equals(e.PropertyName, "Start")) |
await ToggleStart(); |
} |
|
private async Task ToggleStart() |
{ |
switch (Settings.Default.Mode) |
{ |
case "Server": |
await StopServer(); |
|
if (!Settings.Default.Start) |
break; |
|
try |
{ |
await StartServer(); |
} |
catch (ToolTippedException ex) |
{ |
notifyIcon1.BalloonTipIcon = ex.Icon; |
notifyIcon1.BalloonTipTitle = ex.Title; |
notifyIcon1.BalloonTipText = ex.Body; |
notifyIcon1.ShowBalloonTip(1000); |
} |
|
break; |
case "Client": |
await StopClient(); |
|
if (!Settings.Default.Start) |
break; |
|
try |
{ |
await StartClient(); |
} |
catch (ToolTippedException ex) |
{ |
notifyIcon1.BalloonTipIcon = ex.Icon; |
notifyIcon1.BalloonTipTitle = ex.Title; |
notifyIcon1.BalloonTipText = ex.Body; |
notifyIcon1.ShowBalloonTip(1000); |
} |
|
break; |
} |
|
startToolStripMenuItem.Checked = Settings.Default.Start; |
} |
|
private async Task StopClient() |
{ |
// Stop the client if it is already started. |
await MqttCommunication.Stop(); |
} |
|
private async Task StopServer() |
{ |
// Remove UPnP and Pmp mappings. |
await Task.Delay(0, FormCancellationTokenSource.Token).ContinueWith(async _ => |
{ |
await Discovery.DeleteMapping(DiscoveryType.Upnp, MqttCommunication.Port); |
await Discovery.DeleteMapping(DiscoveryType.Pmp, MqttCommunication.Port); |
}, |
FormCancellationTokenSource.Token, TaskContinuationOptions.LongRunning, FormTaskScheduler); |
|
// Stop the MQTT server if it is running. |
await MqttCommunication.Stop(); |
} |
|
private async Task StartClient() |
{ |
if (!IPAddress.TryParse(Settings.Default.Address, out var address)) |
try |
{ |
var getHostAddresses = await Dns.GetHostAddressesAsync(Settings.Default.Address); |
if (!getHostAddresses.Any()) |
throw new Exception(); |
|
address = getHostAddresses.FirstOrDefault(); |
} |
catch |
{ |
throw new ToolTippedException(ToolTipIcon.Error, Strings.Network_error, |
$"{Strings.Unable_to_determine_address} {Settings.Default.Address}"); |
} |
|
if (!uint.TryParse(Settings.Default.Port, out var port)) |
throw new ToolTippedException(ToolTipIcon.Error, Strings.Network_error, |
$"{Strings.Unable_to_determine_port} {Settings.Default.Port}"); |
|
|
if (string.IsNullOrEmpty(Settings.Default.Nick)) |
throw new ToolTippedException(ToolTipIcon.Error, Strings.Network_error, Strings.No_nickname_set); |
|
if (string.IsNullOrEmpty(Settings.Default.Password)) |
throw new ToolTippedException(ToolTipIcon.Error, Strings.Network_error, Strings.No_password_set); |
|
if (!await MqttCommunication |
.Start(MqttCommunicationType.Client, address, (int) port, Settings.Default.Nick, |
Settings.Default.Password, |
new[] {Constants.MqttTopic})) |
throw new ToolTippedException(ToolTipIcon.Error, Strings.Network_error, Strings.Unable_to_start_client); |
} |
|
private async Task StartServer() |
{ |
if (!IPAddress.TryParse(Settings.Default.Address, out var address)) |
try |
{ |
address = Dns.GetHostAddresses(Settings.Default.Address).FirstOrDefault(); |
} |
catch |
{ |
throw new ToolTippedException(ToolTipIcon.Error, Strings.Network_error, |
$"{Strings.Unable_to_determine_address} {Settings.Default.Address}"); |
} |
|
if (!uint.TryParse(Settings.Default.Port, out var port)) |
throw new ToolTippedException(ToolTipIcon.Error, Strings.Network_error, |
$"{Strings.Unable_to_determine_port} {Settings.Default.Port}"); |
|
|
if (string.IsNullOrEmpty(Settings.Default.Nick)) |
throw new ToolTippedException(ToolTipIcon.Error, Strings.Network_error, Strings.No_nickname_set); |
|
if (string.IsNullOrEmpty(Settings.Default.Password)) |
throw new ToolTippedException(ToolTipIcon.Error, Strings.Network_error, Strings.No_password_set); |
|
// Try to reserve port: try UPnP followed by PMP. |
await Task.Delay(0, FormCancellationTokenSource.Token).ContinueWith(async _ => |
{ |
if (!await Discovery.CreateMapping(DiscoveryType.Upnp, (int) port)) |
await Discovery.CreateMapping(DiscoveryType.Pmp, (int) port); |
}, |
FormCancellationTokenSource.Token, TaskContinuationOptions.LongRunning, FormTaskScheduler); |
|
// Start the MQTT server. |
if (!await MqttCommunication |
.Start(MqttCommunicationType.Server, address, (int) port, Settings.Default.Nick, |
Settings.Default.Password, new[] {Constants.MqttTopic})) |
throw new ToolTippedException(ToolTipIcon.Error, Strings.Network_error, Strings.Unable_to_start_server); |
} |
|
private void DefaultOnSettingsSaving(object sender, CancelEventArgs e) |
{ |
} |
|
private void DefaultOnSettingsLoaded(object sender, SettingsLoadedEventArgs e) |
{ |
LaunchOnBoot.Set(Settings.Default.LaunchOnBoot); |
} |
|
/// <summary> |
/// Clean up any resources being used. |
/// </summary> |
@@ -263,9 +67,6 @@ |
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param> |
protected override void Dispose(bool disposing) |
{ |
// Unbind global key hook. |
MouseKeyGlobalHook.KeyDown -= MouseKeyGlobalHookOnKeyDown; |
|
// Unbind message synchronizer. |
ChatMessageSynchronizer.OnMessageReceived -= OnMessageReceived; |
|
@@ -278,14 +79,7 @@ |
base.Dispose(disposing); |
} |
|
private void OnMouseDown(object sender, MouseEventArgs e) |
{ |
if (e.Button == MouseButtons.Left) |
{ |
DllImports.ReleaseCapture(); |
DllImports.SendMessage(Handle, DllImports.WM_NCLBUTTONDOWN, DllImports.HT_CAPTION, 0); |
} |
} |
#region Overrides |
|
protected override void OnPaintBackground(PaintEventArgs e) |
{ |
@@ -370,6 +164,104 @@ |
base.WndProc(ref m); |
} |
|
#endregion |
|
#region Event Handlers |
|
private void MqttOnServerClientDisconnected(object sender, |
MqttClientDisconnectedEventArgs mqttClientDisconnectedEventArgs) |
{ |
notifyIcon1.BalloonTipIcon = ToolTipIcon.Warning; |
notifyIcon1.BalloonTipTitle = Strings.Network_warning; |
notifyIcon1.BalloonTipText = $"{Strings.Client_disconnected}"; |
notifyIcon1.ShowBalloonTip(1000); |
} |
|
|
private void MqttCommunicationOnOnServerClientConnected(object sender, |
MqttClientConnectedEventArgs mqttClientConnectedEventArgs) |
{ |
notifyIcon1.BalloonTipIcon = ToolTipIcon.Warning; |
notifyIcon1.BalloonTipTitle = Strings.Network_warning; |
notifyIcon1.BalloonTipText = $"{Strings.Client_connected}"; |
notifyIcon1.ShowBalloonTip(1000); |
} |
|
private void MqttOnClientDisconnected(object sender, |
MQTTnet.Client.MqttClientDisconnectedEventArgs mqttClientDisconnectedEventArgs) |
{ |
notifyIcon1.BalloonTipIcon = ToolTipIcon.Warning; |
notifyIcon1.BalloonTipTitle = Strings.Network_warning; |
notifyIcon1.BalloonTipText = $"{Strings.Client_disconnected}"; |
notifyIcon1.ShowBalloonTip(1000); |
} |
|
private void MqttOnClientConnected(object sender, |
MQTTnet.Client.MqttClientConnectedEventArgs mqttClientConnectedEventArgs) |
{ |
notifyIcon1.BalloonTipIcon = ToolTipIcon.Warning; |
notifyIcon1.BalloonTipTitle = Strings.Network_warning; |
notifyIcon1.BalloonTipText = $"{Strings.Client_connected}"; |
notifyIcon1.ShowBalloonTip(1000); |
} |
|
private void MqttOnClientAuthenticationFailed(object sender, |
MqttAuthenticationFailureEventArgs mqttAuthenticationFailureEventArgs) |
{ |
notifyIcon1.BalloonTipIcon = ToolTipIcon.Warning; |
notifyIcon1.BalloonTipTitle = Strings.Network_warning; |
notifyIcon1.BalloonTipText = $"{Strings.Failed_to_authenticate_to_server}"; |
notifyIcon1.ShowBalloonTip(1000); |
} |
|
private void MqttOnClientConnectionFailed(object sender, |
MqttManagedProcessFailedEventArgs mqttManagedProcessFailedEventArgs) |
{ |
notifyIcon1.BalloonTipIcon = ToolTipIcon.Warning; |
notifyIcon1.BalloonTipTitle = Strings.Network_warning; |
notifyIcon1.BalloonTipText = |
$"{Strings.Failed_to_connect_to_server} {mqttManagedProcessFailedEventArgs.Exception.Message}"; |
notifyIcon1.ShowBalloonTip(1000); |
} |
|
private void OnDiscoveryPortMapFailed(object sender, DiscoveryFailedEventArgs args) |
{ |
notifyIcon1.BalloonTipIcon = ToolTipIcon.Warning; |
notifyIcon1.BalloonTipTitle = Strings.Network_warning; |
notifyIcon1.BalloonTipText = Strings.Failed_to_create_automatic_NAT_port_mapping; |
notifyIcon1.ShowBalloonTip(1000); |
} |
|
private void OnMessageReceived(object sender, ChatMessageReceivedEventArgs e) |
{ |
var message = $"{e.Nick} : {e.Message}{Environment.NewLine}"; |
chatTextBox.AppendText(message); |
} |
|
private async void DefaultOnPropertyChanged(object sender, PropertyChangedEventArgs e) |
{ |
LaunchOnBoot.Set(Settings.Default.LaunchOnBoot); |
await ToggleStart(); |
} |
|
private void DefaultOnSettingsSaving(object sender, CancelEventArgs e) |
{ |
} |
|
private void DefaultOnSettingsLoaded(object sender, SettingsLoadedEventArgs e) |
{ |
LaunchOnBoot.Set(Settings.Default.LaunchOnBoot); |
} |
|
private void OnMouseDown(object sender, MouseEventArgs e) |
{ |
if (e.Button == MouseButtons.Left) |
{ |
DllImports.ReleaseCapture(); |
DllImports.SendMessage(Handle, DllImports.WM_NCLBUTTONDOWN, DllImports.HT_CAPTION, 0); |
} |
} |
|
private void HushFocus(object sender, EventArgs e) |
{ |
Opacity = .75; |
@@ -456,11 +348,147 @@ |
Settings.Default.Port = toolStripMenuItem.Text; |
} |
|
private void ChatTextBox_TextChanged(object sender, EventArgs e) |
#endregion |
|
private async Task ToggleStart() |
{ |
var textBox = (TextBox) sender; |
switch (Settings.Default.Mode) |
{ |
case "Server": |
await StopServer(); |
|
textBox.ScrollToCaret(); |
if (!Settings.Default.Start) |
break; |
|
try |
{ |
await StartServer(); |
} |
catch (ToolTippedException ex) |
{ |
notifyIcon1.BalloonTipIcon = ex.Icon; |
notifyIcon1.BalloonTipTitle = ex.Title; |
notifyIcon1.BalloonTipText = ex.Body; |
notifyIcon1.ShowBalloonTip(1000); |
} |
|
break; |
case "Client": |
await StopClient(); |
|
if (!Settings.Default.Start) |
break; |
|
try |
{ |
await StartClient(); |
} |
catch (ToolTippedException ex) |
{ |
notifyIcon1.BalloonTipIcon = ex.Icon; |
notifyIcon1.BalloonTipTitle = ex.Title; |
notifyIcon1.BalloonTipText = ex.Body; |
notifyIcon1.ShowBalloonTip(1000); |
} |
|
break; |
} |
|
startToolStripMenuItem.Checked = Settings.Default.Start; |
} |
|
private async Task StopClient() |
{ |
// Stop the client if it is already started. |
await MqttCommunication.Stop(); |
} |
|
private async Task StopServer() |
{ |
// Remove UPnP and Pmp mappings. |
await Task.Delay(0, FormCancellationTokenSource.Token).ContinueWith(async _ => |
{ |
await Discovery.DeleteMapping(DiscoveryType.Upnp, MqttCommunication.Port); |
await Discovery.DeleteMapping(DiscoveryType.Pmp, MqttCommunication.Port); |
}, |
FormCancellationTokenSource.Token, TaskContinuationOptions.LongRunning, FormTaskScheduler); |
|
// Stop the MQTT server if it is running. |
await MqttCommunication.Stop(); |
} |
|
private async Task StartClient() |
{ |
if (!IPAddress.TryParse(Settings.Default.Address, out var address)) |
try |
{ |
var getHostAddresses = await Dns.GetHostAddressesAsync(Settings.Default.Address); |
if (!getHostAddresses.Any()) |
throw new Exception(); |
|
address = getHostAddresses.FirstOrDefault(); |
} |
catch |
{ |
throw new ToolTippedException(ToolTipIcon.Error, Strings.Network_error, |
$"{Strings.Unable_to_determine_address} {Settings.Default.Address}"); |
} |
|
if (!uint.TryParse(Settings.Default.Port, out var port)) |
throw new ToolTippedException(ToolTipIcon.Error, Strings.Network_error, |
$"{Strings.Unable_to_determine_port} {Settings.Default.Port}"); |
|
|
if (string.IsNullOrEmpty(Settings.Default.Nick)) |
throw new ToolTippedException(ToolTipIcon.Error, Strings.Network_error, Strings.No_nickname_set); |
|
if (string.IsNullOrEmpty(Settings.Default.Password)) |
throw new ToolTippedException(ToolTipIcon.Error, Strings.Network_error, Strings.No_password_set); |
|
if (!await MqttCommunication |
.Start(MqttCommunicationType.Client, address, (int) port, Settings.Default.Nick, |
Settings.Default.Password, |
new[] {Constants.MqttTopic})) |
throw new ToolTippedException(ToolTipIcon.Error, Strings.Network_error, Strings.Unable_to_start_client); |
} |
|
private async Task StartServer() |
{ |
if (!IPAddress.TryParse(Settings.Default.Address, out var address)) |
try |
{ |
address = Dns.GetHostAddresses(Settings.Default.Address).FirstOrDefault(); |
} |
catch |
{ |
throw new ToolTippedException(ToolTipIcon.Error, Strings.Network_error, |
$"{Strings.Unable_to_determine_address} {Settings.Default.Address}"); |
} |
|
if (!uint.TryParse(Settings.Default.Port, out var port)) |
throw new ToolTippedException(ToolTipIcon.Error, Strings.Network_error, |
$"{Strings.Unable_to_determine_port} {Settings.Default.Port}"); |
|
|
if (string.IsNullOrEmpty(Settings.Default.Nick)) |
throw new ToolTippedException(ToolTipIcon.Error, Strings.Network_error, Strings.No_nickname_set); |
|
if (string.IsNullOrEmpty(Settings.Default.Password)) |
throw new ToolTippedException(ToolTipIcon.Error, Strings.Network_error, Strings.No_password_set); |
|
// Try to reserve port: try UPnP followed by PMP. |
await Task.Delay(0, FormCancellationTokenSource.Token).ContinueWith(async _ => |
{ |
if (!await Discovery.CreateMapping(DiscoveryType.Upnp, (int) port)) |
await Discovery.CreateMapping(DiscoveryType.Pmp, (int) port); |
}, |
FormCancellationTokenSource.Token, TaskContinuationOptions.LongRunning, FormTaskScheduler); |
|
// Start the MQTT server. |
if (!await MqttCommunication |
.Start(MqttCommunicationType.Server, address, (int) port, Settings.Default.Nick, |
Settings.Default.Password, new[] {Constants.MqttTopic})) |
throw new ToolTippedException(ToolTipIcon.Error, Strings.Network_error, Strings.Unable_to_start_server); |
} |
} |
} |