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