Zzz – Blame information for rev 1

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