Zzz – Blame information for rev 5

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