Zzz – Blame information for rev 2

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.Diagnostics;
6 using System.Drawing;
7 using System.IO;
8 using System.Runtime;
9 using System.Threading;
10 using System.Threading.Tasks;
11 using System.Windows.Forms;
12 using Configuration;
13 using NetSparkleUpdater.Enums;
14 using NetSparkleUpdater.SignatureVerifiers;
15 using NetSparkleUpdater.UI.WinForms;
16 using NetSparkleUpdater;
17 using Serilog;
18 using Zzz.Action;
19 using Zzz.Clients;
20 using Zzz.Idle;
21 using Zzz.Properties;
22 using Zzz.Utilities;
23 using Zzz.Utilities.Serialization;
24 using System.Reflection;
25 using System.Net;
26 using MQTTnet.Client;
27 using MqttClient = Zzz.Clients.MqttClient;
28  
29 namespace Zzz
30 {
31 public partial class MainForm : Form
32 {
33 #region Public Enums, Properties and Fields
34  
35 public bool MemorySinkEnabled { get; set; }
36  
37 public Configuration.Configuration Configuration { get; set; }
38  
39 public ScheduledContinuation ChangedConfigurationContinuation { get; set; }
40  
41 #endregion
42  
43 #region Private Delegates, Events, Enums, Properties, Indexers and Fields
44  
45 private Idler _defaultIdler;
46  
47 private MqttClient _mqttClient;
48  
49 private readonly ActionTrigger _trigger;
50  
51 private AboutForm _aboutForm;
52  
53 private SettingsForm _settingsForm;
54  
55 private Idler _hibernateIdler;
56  
2 office 57 private LogMemorySink _memorySink = new LogMemorySink();
1 office 58  
59 private LogViewForm _logViewForm;
60  
61 private SparkleUpdater _sparkle;
62  
63 private readonly CancellationToken _cancellationToken;
64  
65 private readonly CancellationTokenSource _cancellationTokenSource;
66  
67 #endregion
68  
69 #region Constructors, Destructors and Finalizers
70  
71 public MainForm(Mutex mutex) : this()
72 {
73 InitializeComponent();
74  
2 office 75 Log.Logger = new LoggerConfiguration()
76 .MinimumLevel.Debug()
77 .WriteTo.Conditional(condition => MemorySinkEnabled, configureSink => configureSink.Sink(_memorySink))
78 .WriteTo.File(Path.Combine(Constants.UserApplicationDirectory, "Logs", $"{Constants.AssemblyName}.log"),
79 rollingInterval: RollingInterval.Day)
80 .CreateLogger();
1 office 81  
82 // Initialize the sleeper.
83 _trigger = new ActionTrigger(Handle);
84 _trigger.Action += Trigger_Action;
85  
86 // Start application update.
87 var manifestModuleName = Assembly.GetEntryAssembly().ManifestModule.FullyQualifiedName;
88 var icon = Icon.ExtractAssociatedIcon(manifestModuleName);
89  
90 _sparkle = new SparkleUpdater("https://zzz.grimore.org/update/appcast.xml",
91 new Ed25519Checker(SecurityMode.Strict, "LonrgxVjSF0GnY4hzwlRJnLkaxnDn2ikdmOifILzLJY="))
92 {
93 UIFactory = new UIFactory(icon),
2 office 94 RelaunchAfterUpdate = true
1 office 95 };
96 _sparkle.StartLoop(true, true);
97 }
98  
99 private MainForm()
100 {
101 _cancellationTokenSource = new CancellationTokenSource();
102 _cancellationToken = _cancellationTokenSource.Token;
103  
104 ChangedConfigurationContinuation = new ScheduledContinuation();
105 }
106  
107 /// <summary>
108 /// Clean up any resources being used.
109 /// </summary>
110 /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
111 protected override void Dispose(bool disposing)
112 {
113 if (disposing)
114 {
115 components?.Dispose();
116 }
117  
118 base.Dispose(disposing);
119 }
120  
121 #endregion
122  
123 #region Event Handlers
124  
125 private async void MainForm_Load(object sender, EventArgs e)
126 {
127 Configuration = await LoadConfiguration();
128  
129 // Initialize the idle timer.
130 _defaultIdler = new Idler(Configuration, Resources.Default);
131 if (Configuration.Enabled)
132 {
133 _defaultIdler.Start(TimeSpan.FromMinutes((int)Configuration.Timeout));
134 }
135 // Bind to the idle notification.
136 _defaultIdler.Idle += DefaultIdler_Idle;
137 _defaultIdler.IdleImminent += DefaultIdler_IdleImminent;
138  
139 _hibernateIdler = new Idler(Configuration, Resources.Hibernate);
140 if (Configuration.HibernateEnabled)
141 {
142 _hibernateIdler.Start(TimeSpan.FromMinutes((int)Configuration.HibernateTimeout));
143 }
144 _hibernateIdler.Idle += HibernateIdler_Idle;
145 _hibernateIdler.IdleImminent += HibernateIdler_IdleImminent;
146  
147 toolStripEnabledMenuItem.Checked = Configuration.Enabled;
148 hibernateToolStripMenuItem.Checked = Configuration.HibernateEnabled;
149  
150 // Initialize MQTT client.
151 _mqttClient = new MqttClient(Configuration);
152 _mqttClient.MqttSubscribeSucceeded += MqttClient_MqttSubscribeSucceeded;
153 _mqttClient.MqttSubscribeFailed += MqttClient_MqttSubscribeFailed;
154 _mqttClient.MqttStateReceived += MqttClient_MqttStateReceived;
155 _mqttClient.MqttActionReceived += MqttClient_MqttActionReceived;
156 #pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
157 _mqttClient.Start();
158 #pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
159  
160 }
161  
162 private void LogViewToolStripMenuItem_Click(object sender, EventArgs e)
163 {
164 if (_logViewForm != null)
165 {
166 return;
167 }
168  
2 office 169 _logViewForm = new LogViewForm(this, _memorySink, _cancellationToken);
170 _logViewForm.Closing += LogViewFormClosing;
171 _logViewForm.Show();
1 office 172 }
173  
174 private void LogViewFormClosing(object sender, CancelEventArgs e)
175 {
176 if (_logViewForm == null)
177 {
178 return;
179 }
180  
181 _logViewForm.Closing -= LogViewFormClosing;
182 _logViewForm.Close();
183 _logViewForm = null;
184 }
185  
186 private async void Trigger_Action(object sender, ActionEventArgs e)
187 {
188 if (Configuration.MqttEnable)
189 {
190 await _mqttClient.Publish(e.Action);
191 }
192 }
193  
194 private void MqttClient_MqttActionReceived(object sender, MqttActionReceivedEventArgs e)
195 {
196 Log.Information($"MQTT action {e.Action} received.");
197  
198 _trigger.Execute(e.Action);
199 }
200  
201 private void MqttClient_MqttStateReceived(object sender, MqttStateReceivedEventArgs e)
202 {
203 Log.Information($"MQTT state {e.ZzzState.State} received.");
204  
205 switch (e.ZzzState.State)
206 {
207 case State.State.Enabled:
208 Configuration.Enabled = true;
209  
210 this.InvokeIfRequired(form =>
211 {
212 form.toolStripEnabledMenuItem.Checked = true;
213 form.toolStripEnabledMenuItem.CheckState = CheckState.Checked;
214  
215 form.notifyIcon1.BalloonTipText =
216 Resources.Zzz_enabled;
217 form.notifyIcon1.BalloonTipIcon = ToolTipIcon.Info;
218 form.notifyIcon1.BalloonTipTitle = Resources.Enabled;
219 form.notifyIcon1.ShowBalloonTip(TimeSpan.MaxValue.Milliseconds);
220 });
221 break;
222 case State.State.Disabled:
223 Configuration.Enabled = false;
224  
225 this.InvokeIfRequired(form =>
226 {
227 form.toolStripEnabledMenuItem.Checked = false;
228 form.toolStripEnabledMenuItem.CheckState = CheckState.Unchecked;
229  
230 form.notifyIcon1.BalloonTipText =
231 Resources.Zzz_disabled;
232 form.notifyIcon1.BalloonTipIcon = ToolTipIcon.Info;
233 form.notifyIcon1.BalloonTipTitle = Resources.Disabled;
234 form.notifyIcon1.ShowBalloonTip(TimeSpan.MaxValue.Milliseconds);
235 });
236 break;
237 }
238 }
239  
240 private void MqttClient_MqttSubscribeFailed(object sender, MqttClientSubscribeResultCode e)
241 {
242 Log.Warning(Resources.Unable_to_subscribe_to_MQTT_topic);
243 }
244  
245 private void MqttClient_MqttSubscribeSucceeded(object sender, MqttClientSubscribeResultCode e)
246 {
247 Log.Information(Resources.Subscribed_to_MQTT_topic_and_waiting_for_messages);
248 }
249  
250 private void DefaultIdler_IdleImminent(object sender, IdleImminentEventArgs e)
251 {
252 this.InvokeIfRequired(form =>
253 {
254 form.notifyIcon1.BalloonTipText =
255 Resources.Your_computer_is_becoming_idle_and_will_go_to_sleep_in_a_minute;
256 form.notifyIcon1.BalloonTipIcon = ToolTipIcon.Warning;
257 form.notifyIcon1.BalloonTipTitle = Resources.Sleeping_soon;
258 form.notifyIcon1.ShowBalloonTip(TimeSpan.MaxValue.Milliseconds);
259 });
260 }
261  
262 private void DefaultIdler_Idle(object sender, IdleEventArgs e)
263 {
264 Log.Information(Resources.Sleeping);
265  
266 // Sleep!
267 _trigger.Execute(new ZzzAction(Configuration.Action));
268 }
269  
270 private void HibernateIdler_IdleImminent(object sender, IdleImminentEventArgs e)
271 {
272 this.InvokeIfRequired(form =>
273 {
274 form.notifyIcon1.BalloonTipText =
275 Resources.Your_computer_will_enter_hiberation;
276 form.notifyIcon1.BalloonTipIcon = ToolTipIcon.Warning;
277 form.notifyIcon1.BalloonTipTitle = Resources.Hibernating_soon;
278 form.notifyIcon1.ShowBalloonTip(TimeSpan.MaxValue.Milliseconds);
279 });
280 }
281  
282 private void HibernateIdler_Idle(object sender, IdleEventArgs e)
283 {
284 Log.Information(Resources.Hibernating);
285  
286 _trigger.Execute(new ZzzAction(Action.Action.Hibernate));
287 }
288  
289 private void OnContextMenuQuitClick(object sender, EventArgs e)
290 {
291 Environment.Exit(0);
292 }
293  
294 private void OnContextMenuAboutClick(object sender, EventArgs e)
295 {
296 if (_aboutForm != null)
297 {
298 return;
299 }
300  
301 // Show the about form.
302 _aboutForm = new AboutForm();
303 _aboutForm.Closing += AboutForm_Closing;
304 _aboutForm.Show();
305 }
306  
307 private void AboutForm_Closing(object sender, CancelEventArgs e)
308 {
309 if (_aboutForm == null)
310 {
311 return;
312 }
313  
314 _aboutForm.Closing -= AboutForm_Closing;
315 _aboutForm.Dispose();
316 _aboutForm = null;
317 }
318  
319 private void ToolStripMenuEnabledMenuItem_CheckedChanged(object sender, EventArgs e)
320 {
321 Configuration.Enabled = ((ToolStripMenuItem) sender).Checked;
322  
323 switch(Configuration.Enabled)
324 {
325 case true:
326 if(!_defaultIdler.IsRunning)
327 {
328 _defaultIdler.Start(TimeSpan.FromMinutes((int)Configuration.HibernateTimeout));
329 }
330 break;
331 default:
332 _defaultIdler.Stop();
333 break;
334 }
335 }
336  
337 private void HibernateToolStripMenuItem_CheckedChanged(object sender, EventArgs e)
338 {
339 Configuration.HibernateEnabled = ((ToolStripMenuItem)sender).Checked;
340  
341 switch (Configuration.HibernateEnabled)
342 {
343 case true:
344 if (!_hibernateIdler.IsRunning)
345 {
346 _hibernateIdler.Start(TimeSpan.FromMinutes((int)Configuration.HibernateTimeout));
347 }
348 break;
349 default:
350 _hibernateIdler.Stop();
351 break;
352 }
353 }
354  
355 private void OnNotifyIconMouseDoubleClick(object sender, MouseEventArgs e)
356 {
357 if (e.Button == MouseButtons.Right)
358 {
359 return;
360 }
361  
362 Task.Delay((int)Configuration.ClickActionDelay).ContinueWith(task =>
363 {
364 _trigger.Execute(new ZzzAction(Configuration.ActionDoubleClick));
365 });
366 }
367  
368 private void OnNotifyIconMouseClick(object sender, MouseEventArgs e)
369 {
370 if (e.Button == MouseButtons.Right)
371 {
372 return;
373 }
374  
375 Task.Delay((int)Configuration.ClickActionDelay).ContinueWith(task =>
376 {
377 _trigger.Execute(new ZzzAction(Configuration.ActionClick));
378 });
379 }
380  
381 private async void UpdateToolStripMenuItem_Click(object sender, EventArgs e)
382 {
383 // Manually check for updates, this will not show a ui
384 var result = await _sparkle.CheckForUpdatesQuietly();
385 if (result.Status == UpdateStatus.UpdateAvailable)
386 {
387 // if update(s) are found, then we have to trigger the UI to show it gracefully
388 _sparkle.ShowUpdateNeededUI();
389 return;
390 }
391  
392 MessageBox.Show(Resources.No_updates_available_at_this_time, Resources.Zzz, MessageBoxButtons.OK,
393 MessageBoxIcon.Asterisk,
394 MessageBoxDefaultButton.Button1, MessageBoxOptions.DefaultDesktopOnly, false);
395 }
396  
397 private void ToolStripMenuItem4_Click(object sender, EventArgs e)
398 {
399 if (_settingsForm != null)
400 {
401 return;
402 }
403  
404 _settingsForm = new SettingsForm(_mqttClient, Configuration);
405 _settingsForm.Closing += SettingsForm_Closing;
406 _settingsForm.Show();
407 }
408  
409 private async void SettingsForm_Closing(object sender, CancelEventArgs e)
410 {
411 if (_settingsForm == null)
412 {
413 return;
414 }
415  
416 _settingsForm.Closing -= SettingsForm_Closing;
417 _settingsForm.Dispose();
418 _settingsForm = null;
419  
420 // Commit the configuration.
421 ChangedConfigurationContinuation.Schedule(TimeSpan.FromSeconds(1),
422 async () => { await SaveConfiguration(); }, _cancellationToken);
423  
424 Miscellaneous.LaunchOnBootSet(Configuration.LaunchOnBoot);
425  
426 // Update idle timer parameters.
427 _defaultIdler.IdleTimeout = TimeSpan.FromMinutes((int)Configuration.Timeout);
428 _hibernateIdler.IdleTimeout = TimeSpan.FromMinutes((int)Configuration.HibernateTimeout);
429  
430 // Restart MQTT client.
431 try
432 {
433 await _mqttClient.Restart();
434 }
435 catch (Exception ex)
436 {
437 Log.Warning(ex, "Unable to restart MQTT client.");
438 }
439 }
440  
441 #endregion
442  
443 #region Public Methods
444 public async Task SaveConfiguration()
445 {
446 if (!Directory.Exists(Constants.UserApplicationDirectory))
447 {
448 Directory.CreateDirectory(Constants.UserApplicationDirectory);
449 }
450  
451 switch (await Serialization.Serialize(Configuration, Constants.ConfigurationFile, "Configuration",
452 "<!ATTLIST Configuration xmlns:xsi CDATA #IMPLIED xsi:noNamespaceSchemaLocation CDATA #IMPLIED>",
453 CancellationToken.None))
454 {
455 case SerializationSuccess<Configuration.Configuration> _:
456 Log.Information("Serialized configuration.");
457 break;
458 case SerializationFailure serializationFailure:
459 Log.Warning(serializationFailure.Exception.Message, "Failed to serialize configuration.");
460 break;
461 }
462 }
463  
464 public static async Task<Configuration.Configuration> LoadConfiguration()
465 {
466 if (!Directory.Exists(Constants.UserApplicationDirectory))
467 {
468 Directory.CreateDirectory(Constants.UserApplicationDirectory);
469 }
470  
471 var deserializationResult =
472 await Serialization.Deserialize<Configuration.Configuration>(Constants.ConfigurationFile,
473 Constants.ConfigurationNamespace, Constants.ConfigurationXsd, CancellationToken.None);
474  
475 switch (deserializationResult)
476 {
477 case SerializationSuccess<Configuration.Configuration> serializationSuccess:
478 return serializationSuccess.Result;
479 case SerializationFailure serializationFailure:
480 Log.Warning(serializationFailure.Exception, "Failed to load configuration.");
481 return new Configuration.Configuration();
482 default:
483 return new Configuration.Configuration();
484 }
485 }
486  
487 #endregion
488 }
489 }