Hush – Blame information for rev 4

Subversion Repositories:
Rev:
Rev Author Line No. Line
1 office 1 using System;
2 using System.Collections.Generic;
3 using System.ComponentModel;
4 using System.Configuration;
5 using System.Drawing;
6 using System.Linq;
7 using System.Net;
8 using System.Threading;
9 using System.Threading.Tasks;
10 using System.Windows.Forms;
2 office 11 using Hush.Chat;
1 office 12 using Hush.Communication;
13 using Hush.Discovery;
14 using Hush.Properties;
15 using Hush.Utilities;
3 office 16 using MQTTnet.Extensions.ManagedClient;
17 using MQTTnet.Server;
1 office 18 using WingMan.Communication;
19  
20 namespace Hush
21 {
22 public partial class Hush : Form
23 {
3 office 24 private static TaskScheduler FormTaskScheduler { get; set; }
25 private static CancellationTokenSource FormCancellationTokenSource { get; set; }
26 private static MqttCommunication MqttCommunication { get; set; }
27 private static ChatMessageSynchronizer ChatMessageSynchronizer { get; set; }
28 private static Discovery.Discovery Discovery { get; set; }
29  
1 office 30 public Hush()
31 {
32 InitializeComponent();
33  
34 FormTaskScheduler = TaskScheduler.FromCurrentSynchronizationContext();
35 FormCancellationTokenSource = new CancellationTokenSource();
36  
37 // Bind to settings changed event.
38 Settings.Default.SettingsLoaded += DefaultOnSettingsLoaded;
39 Settings.Default.SettingsSaving += DefaultOnSettingsSaving;
40 Settings.Default.PropertyChanged += DefaultOnPropertyChanged;
41  
42 // Set up discovery.
2 office 43 Discovery = new Discovery.Discovery(Constants.AssemblyName, FormCancellationTokenSource.Token,
1 office 44 FormTaskScheduler);
45 Discovery.OnPortMapFailed += OnDiscoveryPortMapFailed;
46  
47 // Bind to MQTT events.
48 MqttCommunication = new MqttCommunication(FormTaskScheduler, FormCancellationTokenSource.Token);
3 office 49 MqttCommunication.OnClientConnectionFailed += MqttOnClientConnectionFailed;
50 MqttCommunication.OnClientAuthenticationFailed += MqttOnClientAuthenticationFailed;
51 MqttCommunication.OnClientConnected += MqttOnClientConnected;
52 MqttCommunication.OnClientDisconnected += MqttOnClientDisconnected;
53 MqttCommunication.OnServerClientConnected += MqttCommunicationOnOnServerClientConnected;
54 MqttCommunication.OnServerClientDisconnected += MqttOnServerClientDisconnected;
1 office 55  
2 office 56 // Start message synchronizer.
3 office 57 ChatMessageSynchronizer = new ChatMessageSynchronizer(Constants.MqttTopic, MqttCommunication,
58 FormTaskScheduler,
1 office 59 FormCancellationTokenSource.Token);
2 office 60 ChatMessageSynchronizer.OnMessageReceived += OnMessageReceived;
1 office 61 }
62  
63  
64 /// <summary>
65 /// Clean up any resources being used.
66 /// </summary>
67 /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
68 protected override void Dispose(bool disposing)
69 {
2 office 70 // Unbind message synchronizer.
71 ChatMessageSynchronizer.OnMessageReceived -= OnMessageReceived;
72  
1 office 73 // Unbind settings handlers.
74 Settings.Default.SettingsLoaded -= DefaultOnSettingsLoaded;
75 Settings.Default.SettingsSaving -= DefaultOnSettingsSaving;
76 Settings.Default.PropertyChanged -= DefaultOnPropertyChanged;
77  
78 if (disposing && components != null) components.Dispose();
79 base.Dispose(disposing);
80 }
81  
3 office 82 #region Overrides
1 office 83  
84 protected override void OnPaintBackground(PaintEventArgs e)
85 {
86 base.OnPaintBackground(e); //comment this out to prevent default painting
87 using (var brush = new SolidBrush(Settings.Default.Color)) //any color you like
88 {
89 e.Graphics.FillRectangle(brush, e.ClipRectangle);
90 }
91 }
92  
93 protected override void WndProc(ref Message m)
94 {
95 const uint WM_NCHITTEST = 0x0084;
96 const uint WM_MOUSEMOVE = 0x0200;
97  
98 const uint HTLEFT = 10;
99 const uint HTRIGHT = 11;
100 const uint HTBOTTOMRIGHT = 17;
101 const uint HTBOTTOM = 15;
102 const uint HTBOTTOMLEFT = 16;
103 const uint HTTOP = 12;
104 const uint HTTOPLEFT = 13;
105 const uint HTTOPRIGHT = 14;
106  
107 const int RESIZE_HANDLE_SIZE = 10;
108 var handled = false;
109 if (m.Msg == WM_NCHITTEST || m.Msg == WM_MOUSEMOVE)
110 {
111 var formSize = Size;
112 var screenPoint = new Point(m.LParam.ToInt32());
113 var clientPoint = PointToClient(screenPoint);
114  
115 var boxes = new Dictionary<uint, Rectangle>
116 {
117 {
118 HTBOTTOMLEFT,
119 new Rectangle(0, formSize.Height - RESIZE_HANDLE_SIZE, RESIZE_HANDLE_SIZE, RESIZE_HANDLE_SIZE)
120 },
121 {
122 HTBOTTOM,
123 new Rectangle(RESIZE_HANDLE_SIZE, formSize.Height - RESIZE_HANDLE_SIZE,
124 formSize.Width - 2 * RESIZE_HANDLE_SIZE, RESIZE_HANDLE_SIZE)
125 },
126 {
127 HTBOTTOMRIGHT,
128 new Rectangle(formSize.Width - RESIZE_HANDLE_SIZE, formSize.Height - RESIZE_HANDLE_SIZE,
129 RESIZE_HANDLE_SIZE, RESIZE_HANDLE_SIZE)
130 },
131 {
132 HTRIGHT,
133 new Rectangle(formSize.Width - RESIZE_HANDLE_SIZE, RESIZE_HANDLE_SIZE, RESIZE_HANDLE_SIZE,
134 formSize.Height - 2 * RESIZE_HANDLE_SIZE)
135 },
136 {
137 HTTOPRIGHT,
138 new Rectangle(formSize.Width - RESIZE_HANDLE_SIZE, 0, RESIZE_HANDLE_SIZE, RESIZE_HANDLE_SIZE)
139 },
140 {
141 HTTOP,
142 new Rectangle(RESIZE_HANDLE_SIZE, 0, formSize.Width - 2 * RESIZE_HANDLE_SIZE,
143 RESIZE_HANDLE_SIZE)
144 },
145 {HTTOPLEFT, new Rectangle(0, 0, RESIZE_HANDLE_SIZE, RESIZE_HANDLE_SIZE)},
146 {
147 HTLEFT,
148 new Rectangle(0, RESIZE_HANDLE_SIZE, RESIZE_HANDLE_SIZE,
149 formSize.Height - 2 * RESIZE_HANDLE_SIZE)
150 }
151 };
152  
153 foreach (var hitBox in boxes)
154 if (WindowState != FormWindowState.Maximized
155 && hitBox.Value.Contains(clientPoint))
156 {
157 m.Result = (IntPtr) hitBox.Key;
158 handled = true;
159 break;
160 }
161 }
162  
163 if (!handled)
164 base.WndProc(ref m);
165 }
166  
3 office 167 #endregion
168  
169 #region Event Handlers
170  
171 private void MqttOnServerClientDisconnected(object sender,
172 MqttClientDisconnectedEventArgs mqttClientDisconnectedEventArgs)
173 {
174 notifyIcon1.BalloonTipIcon = ToolTipIcon.Warning;
175 notifyIcon1.BalloonTipTitle = Strings.Network_warning;
176 notifyIcon1.BalloonTipText = $"{Strings.Client_disconnected}";
177 notifyIcon1.ShowBalloonTip(1000);
178 }
179  
180  
181 private void MqttCommunicationOnOnServerClientConnected(object sender,
182 MqttClientConnectedEventArgs mqttClientConnectedEventArgs)
183 {
184 notifyIcon1.BalloonTipIcon = ToolTipIcon.Warning;
185 notifyIcon1.BalloonTipTitle = Strings.Network_warning;
186 notifyIcon1.BalloonTipText = $"{Strings.Client_connected}";
187 notifyIcon1.ShowBalloonTip(1000);
188 }
189  
190 private void MqttOnClientDisconnected(object sender,
191 MQTTnet.Client.MqttClientDisconnectedEventArgs mqttClientDisconnectedEventArgs)
192 {
193 notifyIcon1.BalloonTipIcon = ToolTipIcon.Warning;
194 notifyIcon1.BalloonTipTitle = Strings.Network_warning;
195 notifyIcon1.BalloonTipText = $"{Strings.Client_disconnected}";
196 notifyIcon1.ShowBalloonTip(1000);
197 }
198  
199 private void MqttOnClientConnected(object sender,
200 MQTTnet.Client.MqttClientConnectedEventArgs mqttClientConnectedEventArgs)
201 {
202 notifyIcon1.BalloonTipIcon = ToolTipIcon.Warning;
203 notifyIcon1.BalloonTipTitle = Strings.Network_warning;
204 notifyIcon1.BalloonTipText = $"{Strings.Client_connected}";
205 notifyIcon1.ShowBalloonTip(1000);
206 }
207  
208 private void MqttOnClientAuthenticationFailed(object sender,
209 MqttAuthenticationFailureEventArgs mqttAuthenticationFailureEventArgs)
210 {
211 notifyIcon1.BalloonTipIcon = ToolTipIcon.Warning;
212 notifyIcon1.BalloonTipTitle = Strings.Network_warning;
213 notifyIcon1.BalloonTipText = $"{Strings.Failed_to_authenticate_to_server}";
214 notifyIcon1.ShowBalloonTip(1000);
215 }
216  
217 private void MqttOnClientConnectionFailed(object sender,
218 MqttManagedProcessFailedEventArgs mqttManagedProcessFailedEventArgs)
219 {
220 notifyIcon1.BalloonTipIcon = ToolTipIcon.Warning;
221 notifyIcon1.BalloonTipTitle = Strings.Network_warning;
222 notifyIcon1.BalloonTipText =
223 $"{Strings.Failed_to_connect_to_server} {mqttManagedProcessFailedEventArgs.Exception.Message}";
224 notifyIcon1.ShowBalloonTip(1000);
225 }
226  
227 private void OnDiscoveryPortMapFailed(object sender, DiscoveryFailedEventArgs args)
228 {
229 notifyIcon1.BalloonTipIcon = ToolTipIcon.Warning;
230 notifyIcon1.BalloonTipTitle = Strings.Network_warning;
231 notifyIcon1.BalloonTipText = Strings.Failed_to_create_automatic_NAT_port_mapping;
232 notifyIcon1.ShowBalloonTip(1000);
233 }
234  
235 private void OnMessageReceived(object sender, ChatMessageReceivedEventArgs e)
236 {
237 var message = $"{e.Nick} : {e.Message}{Environment.NewLine}";
238 chatTextBox.AppendText(message);
239 }
240  
241 private async void DefaultOnPropertyChanged(object sender, PropertyChangedEventArgs e)
242 {
4 office 243 if (string.Equals(e.PropertyName, "LaunchOnBoot"))
244 LaunchOnBoot.Set(Settings.Default.LaunchOnBoot);
245  
246 if (string.Equals(e.PropertyName, "Start"))
247 await ToggleStart();
3 office 248 }
249  
250 private void DefaultOnSettingsSaving(object sender, CancelEventArgs e)
251 {
252 }
253  
254 private void DefaultOnSettingsLoaded(object sender, SettingsLoadedEventArgs e)
255 {
256 LaunchOnBoot.Set(Settings.Default.LaunchOnBoot);
257 }
258  
259 private void OnMouseDown(object sender, MouseEventArgs e)
260 {
261 if (e.Button == MouseButtons.Left)
262 {
263 DllImports.ReleaseCapture();
264 DllImports.SendMessage(Handle, DllImports.WM_NCLBUTTONDOWN, DllImports.HT_CAPTION, 0);
265 }
266 }
267  
2 office 268 private void HushFocus(object sender, EventArgs e)
1 office 269 {
270 Opacity = .75;
271 }
272  
2 office 273 private void HushUnfocus(object sender, EventArgs e)
1 office 274 {
275 Opacity = .33;
276 }
277  
278 private void ContextMenuOnClickAbout(object sender, EventArgs e)
279 {
280 new About().Show();
281 }
282  
283 private void ContextMenuOnClickQuit(object sender, EventArgs e)
284 {
285 Application.Exit();
286 }
287  
288 private async void MessageTextBoxOnKeyDown(object sender, KeyEventArgs e)
289 {
2 office 290 // Prevent the enter key to be passed to the text box or we get a nasty ding.
291 if (e.KeyCode == Keys.Enter)
292 e.SuppressKeyPress = true;
293  
294 if (e.KeyCode != Keys.Enter)
295 return;
296  
1 office 297 // Do not send messages if the communication is not running.
298 if (!MqttCommunication.Running)
299 return;
300  
301 if (string.IsNullOrEmpty(messageTextBox.Text))
302 return;
303  
304 // Send the message
2 office 305 await ChatMessageSynchronizer.Broadcast($"{messageTextBox.Text}");
1 office 306  
307 messageTextBox.Text = string.Empty;
308 }
309  
310 private void ToolStripNickTextBoxOnTextChanged(object sender, EventArgs e)
311 {
312 Settings.Default.Nick = ((ToolStripTextBox) sender).Text;
313 }
314  
315 private void ToolStripPasswordTextBoxOnTextChanged(object sender, EventArgs e)
316 {
317 Settings.Default.Password = ((ToolStripTextBox) sender).Text;
318 }
319  
320 private void toolStripMenuItem1_Click(object sender, EventArgs e)
321 {
322 Settings.Default.LaunchOnBoot = ((ToolStripMenuItem) sender).Checked;
323 }
324  
325 private void startToolStripMenuItem_CheckStateChanged(object sender, EventArgs e)
326 {
327 var toolStripMenuItem = (ToolStripMenuItem) sender;
328  
329 Settings.Default.Start = toolStripMenuItem.Checked;
330 }
331  
332 private void toolStripComboBox1_SelectedIndexChanged(object sender, EventArgs e)
333 {
334 var toolStripComboBox = (ToolStripComboBox) sender;
335  
336 Settings.Default.Start = false;
337 Settings.Default.Mode = toolStripComboBox.Items[toolStripComboBox.SelectedIndex].ToString();
338 }
339  
340 private void ToolStripAddressTextBoxOnTextChanged(object sender, EventArgs e)
341 {
342 var toolStripMenuItem = (ToolStripTextBox) sender;
343  
344 Settings.Default.Address = toolStripMenuItem.Text;
345 }
346  
347 private void ToolStripPortTextBoxOnTextChanged(object sender, EventArgs e)
348 {
349 var toolStripMenuItem = (ToolStripTextBox) sender;
350  
351 Settings.Default.Port = toolStripMenuItem.Text;
352 }
2 office 353  
3 office 354 #endregion
355  
356 private async Task ToggleStart()
2 office 357 {
3 office 358 switch (Settings.Default.Mode)
359 {
360 case "Server":
361 await StopServer();
2 office 362  
3 office 363 if (!Settings.Default.Start)
364 break;
365  
366 try
367 {
368 await StartServer();
369 }
370 catch (ToolTippedException ex)
371 {
372 notifyIcon1.BalloonTipIcon = ex.Icon;
373 notifyIcon1.BalloonTipTitle = ex.Title;
374 notifyIcon1.BalloonTipText = ex.Body;
375 notifyIcon1.ShowBalloonTip(1000);
376 }
377  
378 break;
379 case "Client":
380 await StopClient();
381  
382 if (!Settings.Default.Start)
383 break;
384  
385 try
386 {
387 await StartClient();
388 }
389 catch (ToolTippedException ex)
390 {
391 notifyIcon1.BalloonTipIcon = ex.Icon;
392 notifyIcon1.BalloonTipTitle = ex.Title;
393 notifyIcon1.BalloonTipText = ex.Body;
394 notifyIcon1.ShowBalloonTip(1000);
395 }
396  
397 break;
398 }
399  
400 startToolStripMenuItem.Checked = Settings.Default.Start;
2 office 401 }
3 office 402  
403 private async Task StopClient()
404 {
405 // Stop the client if it is already started.
406 await MqttCommunication.Stop();
407 }
408  
409 private async Task StopServer()
410 {
411 // Remove UPnP and Pmp mappings.
412 await Task.Delay(0, FormCancellationTokenSource.Token).ContinueWith(async _ =>
413 {
414 await Discovery.DeleteMapping(DiscoveryType.Upnp, MqttCommunication.Port);
415 await Discovery.DeleteMapping(DiscoveryType.Pmp, MqttCommunication.Port);
416 },
417 FormCancellationTokenSource.Token, TaskContinuationOptions.LongRunning, FormTaskScheduler);
418  
419 // Stop the MQTT server if it is running.
420 await MqttCommunication.Stop();
421 }
422  
423 private async Task StartClient()
424 {
425 if (!IPAddress.TryParse(Settings.Default.Address, out var address))
426 try
427 {
428 var getHostAddresses = await Dns.GetHostAddressesAsync(Settings.Default.Address);
429 if (!getHostAddresses.Any())
430 throw new Exception();
431  
432 address = getHostAddresses.FirstOrDefault();
433 }
434 catch
435 {
436 throw new ToolTippedException(ToolTipIcon.Error, Strings.Network_error,
437 $"{Strings.Unable_to_determine_address} {Settings.Default.Address}");
438 }
439  
440 if (!uint.TryParse(Settings.Default.Port, out var port))
441 throw new ToolTippedException(ToolTipIcon.Error, Strings.Network_error,
442 $"{Strings.Unable_to_determine_port} {Settings.Default.Port}");
443  
444  
445 if (string.IsNullOrEmpty(Settings.Default.Nick))
446 throw new ToolTippedException(ToolTipIcon.Error, Strings.Network_error, Strings.No_nickname_set);
447  
448 if (string.IsNullOrEmpty(Settings.Default.Password))
449 throw new ToolTippedException(ToolTipIcon.Error, Strings.Network_error, Strings.No_password_set);
450  
451 if (!await MqttCommunication
452 .Start(MqttCommunicationType.Client, address, (int) port, Settings.Default.Nick,
453 Settings.Default.Password,
454 new[] {Constants.MqttTopic}))
455 throw new ToolTippedException(ToolTipIcon.Error, Strings.Network_error, Strings.Unable_to_start_client);
456 }
457  
458 private async Task StartServer()
459 {
460 if (!IPAddress.TryParse(Settings.Default.Address, out var address))
461 try
462 {
463 address = Dns.GetHostAddresses(Settings.Default.Address).FirstOrDefault();
464 }
465 catch
466 {
467 throw new ToolTippedException(ToolTipIcon.Error, Strings.Network_error,
468 $"{Strings.Unable_to_determine_address} {Settings.Default.Address}");
469 }
470  
471 if (!uint.TryParse(Settings.Default.Port, out var port))
472 throw new ToolTippedException(ToolTipIcon.Error, Strings.Network_error,
473 $"{Strings.Unable_to_determine_port} {Settings.Default.Port}");
474  
475  
476 if (string.IsNullOrEmpty(Settings.Default.Nick))
477 throw new ToolTippedException(ToolTipIcon.Error, Strings.Network_error, Strings.No_nickname_set);
478  
479 if (string.IsNullOrEmpty(Settings.Default.Password))
480 throw new ToolTippedException(ToolTipIcon.Error, Strings.Network_error, Strings.No_password_set);
481  
482 // Try to reserve port: try UPnP followed by PMP.
483 await Task.Delay(0, FormCancellationTokenSource.Token).ContinueWith(async _ =>
484 {
485 if (!await Discovery.CreateMapping(DiscoveryType.Upnp, (int) port))
486 await Discovery.CreateMapping(DiscoveryType.Pmp, (int) port);
487 },
488 FormCancellationTokenSource.Token, TaskContinuationOptions.LongRunning, FormTaskScheduler);
489  
490 // Start the MQTT server.
491 if (!await MqttCommunication
492 .Start(MqttCommunicationType.Server, address, (int) port, Settings.Default.Nick,
493 Settings.Default.Password, new[] {Constants.MqttTopic}))
494 throw new ToolTippedException(ToolTipIcon.Error, Strings.Network_error, Strings.Unable_to_start_server);
495 }
1 office 496 }
497 }