diff --git a/GoToSleep.slnx b/GoToSleep.slnx new file mode 100644 index 0000000..b7bfb4c --- /dev/null +++ b/GoToSleep.slnx @@ -0,0 +1,3 @@ + + + diff --git a/GoToSleep/CountdownForm.Designer.cs b/GoToSleep/CountdownForm.Designer.cs new file mode 100644 index 0000000..0ec1ec3 --- /dev/null +++ b/GoToSleep/CountdownForm.Designer.cs @@ -0,0 +1,112 @@ +namespace GoToSleep +{ + partial class CountdownForm + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + countdown = new Label(); + cancel = new Button(); + execNow = new Button(); + tableLayoutPanel1 = new TableLayoutPanel(); + tableLayoutPanel1.SuspendLayout(); + SuspendLayout(); + // + // countdown + // + countdown.AutoSize = true; + tableLayoutPanel1.SetColumnSpan(countdown, 2); + countdown.Dock = DockStyle.Fill; + countdown.Font = new Font("Segoe UI", 27.75F, FontStyle.Regular, GraphicsUnit.Point, 238); + countdown.Location = new Point(3, 0); + countdown.Name = "countdown"; + countdown.Size = new Size(478, 63); + countdown.TabIndex = 0; + countdown.Text = "Time Remaining: 99:99:99"; + countdown.TextAlign = ContentAlignment.MiddleCenter; + countdown.Click += countdown_Click; + // + // cancel + // + cancel.Anchor = AnchorStyles.Left; + cancel.Location = new Point(245, 68); + cancel.Name = "cancel"; + cancel.Size = new Size(75, 23); + cancel.TabIndex = 1; + cancel.Text = "Cancel"; + cancel.UseVisualStyleBackColor = true; + cancel.Click += cancel_Click; + // + // execNow + // + execNow.Anchor = AnchorStyles.Right; + execNow.Location = new Point(164, 68); + execNow.Name = "execNow"; + execNow.Size = new Size(75, 23); + execNow.TabIndex = 2; + execNow.Text = "Execute"; + execNow.UseVisualStyleBackColor = true; + execNow.Click += execNow_Click; + // + // tableLayoutPanel1 + // + tableLayoutPanel1.ColumnCount = 2; + tableLayoutPanel1.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 50F)); + tableLayoutPanel1.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 50F)); + tableLayoutPanel1.Controls.Add(countdown, 0, 0); + tableLayoutPanel1.Controls.Add(cancel, 1, 1); + tableLayoutPanel1.Controls.Add(execNow, 0, 1); + tableLayoutPanel1.Dock = DockStyle.Fill; + tableLayoutPanel1.Location = new Point(0, 0); + tableLayoutPanel1.Name = "tableLayoutPanel1"; + tableLayoutPanel1.RowCount = 2; + tableLayoutPanel1.RowStyles.Add(new RowStyle(SizeType.Percent, 100F)); + tableLayoutPanel1.RowStyles.Add(new RowStyle(SizeType.Absolute, 33F)); + tableLayoutPanel1.Size = new Size(484, 96); + tableLayoutPanel1.TabIndex = 3; + // + // CountdownForm + // + AutoScaleDimensions = new SizeF(7F, 15F); + AutoScaleMode = AutoScaleMode.Font; + ClientSize = new Size(484, 96); + Controls.Add(tableLayoutPanel1); + MinimumSize = new Size(500, 135); + Name = "CountdownForm"; + Text = "CountdownForm"; + tableLayoutPanel1.ResumeLayout(false); + tableLayoutPanel1.PerformLayout(); + ResumeLayout(false); + } + + #endregion + + private Label countdown; + private Button cancel; + private Button execNow; + private TableLayoutPanel tableLayoutPanel1; + } +} \ No newline at end of file diff --git a/GoToSleep/CountdownForm.cs b/GoToSleep/CountdownForm.cs new file mode 100644 index 0000000..f125da1 --- /dev/null +++ b/GoToSleep/CountdownForm.cs @@ -0,0 +1,99 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.ComponentModel.Design; +using System.Data; +using System.Drawing; +using System.Text; +using System.Windows.Forms; +using System.Threading; + +namespace GoToSleep +{ + public partial class CountdownForm : Form + { + public bool active = true; + private PowerDownData task; + private Thread taskThread; + public CountdownForm(PowerDownData taskData) + { + task = taskData; + InitializeComponent(); + this.Text = task.actionType.ToString() + " Countdown"; + countdown.Text = getTimerText(); + taskThread = new Thread(threadFunction); + taskThread.Start(); + } + string getTimerText() + { + var timeRemaining = task.when - DateTime.Now; + return $"Time Remaining: {(int)timeRemaining.TotalHours:D2}:{timeRemaining.Minutes:D2}:{timeRemaining.Seconds:D2}"; + } + + void updateTimer() + { + + try + { + countdown.Invoke((MethodInvoker)delegate + { + countdown.Text = getTimerText(); + }); + } + catch (Exception e) + { + //the window most likely has been closed + return; + } + } + + void threadFunction() + { + while (DateTime.Now < task.when) + { + Thread.Sleep(1000); + if (!active) + { + return; + } + updateTimer(); + + } + executeStateChange(); + } + + void executeStateChange() + { + active = false; + task.onCountdownEnd(); + + this.Invoke((MethodInvoker)delegate + { + this.Close(); + }); + + } + + private void execNow_Click(object sender, EventArgs e) + { + executeStateChange(); + } + + private void cancel_Click(object sender, EventArgs e) + { + active = false; + this.Close(); + } + + protected override void OnFormClosing(FormClosingEventArgs e) + { + base.OnFormClosing(e); + active = false; + } + + private void countdown_Click(object sender, EventArgs e) + { + + } + } +} diff --git a/GoToSleep/CountdownForm.resx b/GoToSleep/CountdownForm.resx new file mode 100644 index 0000000..8b2ff64 --- /dev/null +++ b/GoToSleep/CountdownForm.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/GoToSleep/GoToSleep.Designer.cs b/GoToSleep/GoToSleep.Designer.cs new file mode 100644 index 0000000..e8611a1 --- /dev/null +++ b/GoToSleep/GoToSleep.Designer.cs @@ -0,0 +1,147 @@ +namespace GoToSleep +{ + partial class GoToSleep + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + comboBox1 = new ComboBox(); + shutdownButton = new Button(); + suspendButton = new Button(); + hibernateButton = new Button(); + textBox1 = new TextBox(); + tableLayoutPanel2 = new TableLayoutPanel(); + flowLayoutPanel1 = new FlowLayoutPanel(); + tableLayoutPanel2.SuspendLayout(); + flowLayoutPanel1.SuspendLayout(); + SuspendLayout(); + // + // comboBox1 + // + comboBox1.FormattingEnabled = true; + comboBox1.Items.AddRange(new object[] { "In", "At" }); + comboBox1.Location = new Point(3, 3); + comboBox1.MinimumSize = new Size(50, 0); + comboBox1.Name = "comboBox1"; + comboBox1.Size = new Size(50, 23); + comboBox1.TabIndex = 0; + comboBox1.SelectedIndexChanged += comboBox1_SelectedIndexChanged; + // + // shutdownButton + // + shutdownButton.Location = new Point(185, 3); + shutdownButton.Margin = new Padding(3, 3, 0, 3); + shutdownButton.Name = "shutdownButton"; + shutdownButton.Size = new Size(75, 23); + shutdownButton.TabIndex = 1; + shutdownButton.Text = "Shutdown"; + shutdownButton.UseVisualStyleBackColor = true; + shutdownButton.Click += shutdownButton_Click; + // + // suspendButton + // + suspendButton.Location = new Point(104, 3); + suspendButton.Name = "suspendButton"; + suspendButton.Size = new Size(75, 23); + suspendButton.TabIndex = 2; + suspendButton.Text = "Suspend"; + suspendButton.UseVisualStyleBackColor = true; + suspendButton.Click += suspendButton_Click; + // + // hibernateButton + // + hibernateButton.Location = new Point(23, 3); + hibernateButton.Name = "hibernateButton"; + hibernateButton.Size = new Size(75, 23); + hibernateButton.TabIndex = 3; + hibernateButton.Text = "Hibernate"; + hibernateButton.UseVisualStyleBackColor = true; + hibernateButton.Click += hibernateButton_Click; + // + // textBox1 + // + textBox1.Anchor = AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Right; + textBox1.Location = new Point(59, 3); + textBox1.Name = "textBox1"; + textBox1.Size = new Size(422, 23); + textBox1.TabIndex = 4; + // + // tableLayoutPanel2 + // + tableLayoutPanel2.AutoSizeMode = AutoSizeMode.GrowAndShrink; + tableLayoutPanel2.ColumnCount = 2; + tableLayoutPanel2.ColumnStyles.Add(new ColumnStyle()); + tableLayoutPanel2.ColumnStyles.Add(new ColumnStyle()); + tableLayoutPanel2.Controls.Add(comboBox1, 0, 0); + tableLayoutPanel2.Controls.Add(textBox1, 1, 0); + tableLayoutPanel2.Controls.Add(flowLayoutPanel1, 1, 1); + tableLayoutPanel2.Dock = DockStyle.Fill; + tableLayoutPanel2.Location = new Point(0, 0); + tableLayoutPanel2.Name = "tableLayoutPanel2"; + tableLayoutPanel2.RowCount = 2; + tableLayoutPanel2.RowStyles.Add(new RowStyle(SizeType.Absolute, 30F)); + tableLayoutPanel2.RowStyles.Add(new RowStyle()); + tableLayoutPanel2.Size = new Size(484, 64); + tableLayoutPanel2.TabIndex = 6; + // + // flowLayoutPanel1 + // + flowLayoutPanel1.Anchor = AnchorStyles.Top | AnchorStyles.Right; + flowLayoutPanel1.Controls.Add(shutdownButton); + flowLayoutPanel1.Controls.Add(suspendButton); + flowLayoutPanel1.Controls.Add(hibernateButton); + flowLayoutPanel1.FlowDirection = FlowDirection.RightToLeft; + flowLayoutPanel1.Location = new Point(221, 33); + flowLayoutPanel1.Name = "flowLayoutPanel1"; + flowLayoutPanel1.Size = new Size(260, 41); + flowLayoutPanel1.TabIndex = 7; + // + // GoToSleep + // + AutoScaleDimensions = new SizeF(7F, 15F); + AutoScaleMode = AutoScaleMode.Font; + ClientSize = new Size(484, 64); + Controls.Add(tableLayoutPanel2); + MinimumSize = new Size(500, 103); + Name = "GoToSleep"; + Text = "Go To Sleep"; + tableLayoutPanel2.ResumeLayout(false); + tableLayoutPanel2.PerformLayout(); + flowLayoutPanel1.ResumeLayout(false); + ResumeLayout(false); + } + + #endregion + + private ComboBox comboBox1; + private Button shutdownButton; + private Button suspendButton; + private Button hibernateButton; + private TextBox textBox1; + private TableLayoutPanel tableLayoutPanel2; + private FlowLayoutPanel flowLayoutPanel1; + } +} diff --git a/GoToSleep/GoToSleep.cs b/GoToSleep/GoToSleep.cs new file mode 100644 index 0000000..e06fec1 --- /dev/null +++ b/GoToSleep/GoToSleep.cs @@ -0,0 +1,73 @@ +namespace GoToSleep +{ + public partial class GoToSleep : Form + { + string timeSelector = "in"; + SystemPowerManager manager; + public GoToSleep() + { + InitializeComponent(); + comboBox1.SelectedIndex = 0; + textBox1.Select(); + manager = new SystemPowerManager(this); + } + + private void comboBox1_SelectedIndexChanged(object sender, EventArgs e) + { + switch (comboBox1.SelectedIndex) + { + case 0: + timeSelector = "in"; + break; + case 1: + timeSelector = ""; + break; + default: + //this should never happen, something went very wrong, the combo box should only have 2 options + throw new Exception("Invalid selection index"); + } + + + } + + private string getTimeString() + { + return timeSelector + " " + textBox1.Text; + } + + private void shutdownButton_Click(object sender, EventArgs e) + { + try + { + manager.shutdown(getTimeString()); + } + catch (Exception ex) { + MessageBox.Show(ex.Message); + } + } + + private void suspendButton_Click(object sender, EventArgs e) + { + try + { + manager.suspend(getTimeString()); + } + catch (Exception ex) + { + MessageBox.Show(ex.Message); + } + } + + private void hibernateButton_Click(object sender, EventArgs e) + { + try + { + manager.hibernate(getTimeString()); + } + catch (Exception ex) + { + MessageBox.Show(ex.Message); + } + } + } +} diff --git a/GoToSleep/GoToSleep.csproj b/GoToSleep/GoToSleep.csproj new file mode 100644 index 0000000..e94f50a --- /dev/null +++ b/GoToSleep/GoToSleep.csproj @@ -0,0 +1,17 @@ + + + + WinExe + net10.0-windows + enable + true + enable + + + + + + + + + \ No newline at end of file diff --git a/GoToSleep/GoToSleep.resx b/GoToSleep/GoToSleep.resx new file mode 100644 index 0000000..8b2ff64 --- /dev/null +++ b/GoToSleep/GoToSleep.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/GoToSleep/PowerDownData.cs b/GoToSleep/PowerDownData.cs new file mode 100644 index 0000000..cc77ff6 --- /dev/null +++ b/GoToSleep/PowerDownData.cs @@ -0,0 +1,54 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Text; + +namespace GoToSleep +{ + public class PowerDownData + { + public enum PowerDownType + { + Shutdown, + Suspend, + Hibernate + }; + + public PowerDownType actionType { get; } + + public DateTime when { get; } + + public delegate void Action(); + + public Action onCountdownEnd { get; } + + + private PowerDownData(PowerDownType _type, DateTime _when, Action action) + { + actionType = _type; + when = _when; + onCountdownEnd = action; + } + public static PowerDownData Shutdown(DateTime when) + { + return new PowerDownData(PowerDownType.Shutdown, when, () => { + var psi = new ProcessStartInfo("shutdown", "/s /t 0"); + psi.CreateNoWindow = true; + psi.UseShellExecute = false; + Process.Start(psi); + }); + } + public static PowerDownData Suspend(DateTime when) + { + return new PowerDownData(PowerDownType.Suspend, when, () => { + Application.SetSuspendState(PowerState.Suspend, false, false); + }); + } + public static PowerDownData Hibernate(DateTime when) + { + return new PowerDownData(PowerDownType.Hibernate, when, () => { + Application.SetSuspendState(PowerState.Hibernate, false, false); + }); + } + } +} diff --git a/GoToSleep/Program.cs b/GoToSleep/Program.cs new file mode 100644 index 0000000..8724daa --- /dev/null +++ b/GoToSleep/Program.cs @@ -0,0 +1,17 @@ +namespace GoToSleep +{ + internal static class Program + { + /// + /// The main entry point for the application. + /// + [STAThread] + static void Main() + { + // To customize application configuration such as set high DPI settings or default font, + // see https://aka.ms/applicationconfiguration. + ApplicationConfiguration.Initialize(); + Application.Run(new GoToSleep()); + } + } +} \ No newline at end of file diff --git a/GoToSleep/SystemPowerManager.cs b/GoToSleep/SystemPowerManager.cs new file mode 100644 index 0000000..2627b8a --- /dev/null +++ b/GoToSleep/SystemPowerManager.cs @@ -0,0 +1,150 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Windows.Forms.Design; +using Chronic; +using System.Diagnostics; +using System.Windows.Forms; +using System.Text.RegularExpressions; +using Microsoft.Recognizers.Text; +using Microsoft.Recognizers.Text.DateTime; +using System.Globalization; + +namespace GoToSleep +{ + internal class SystemPowerManager + { + [Serializable] + class DatetimeStringException : Exception + { + public DatetimeStringException(string message) : base(message) { } + public DatetimeStringException(Exception e) : base("Conversion failed.", e) { } + } + + [Serializable] + class DateTimeBeforeException : DatetimeStringException + { + public DateTimeBeforeException() : base("The entered date was in the past.") + { + } + } + + [Serializable] + class ParseFailedException : DatetimeStringException + { + public ParseFailedException(Exception e) : base(e) { } + } + + public SystemPowerManager(GoToSleep pf) + { + parentForm = pf; + } + Form parentForm; + + Chronic.Parser parser = new Chronic.Parser(); + + string prepareTimeString(string s) + { + // make sure there are spaces between numbers and units, for the parser + s = Regex.Replace(s, "([0-9]+)([a-zA-Z]+)", "$1 $2"); + s = Regex.Replace(s, "([a-zA-Z]+)([0-9]+)", "$1 $2"); + + // replace shorthands with full names, for the parser + s = Regex.Replace(s, "( )[hH]([ ]|$)", "$1hours$2"); + + s = Regex.Replace(s, "( )[mM]([ ]|$)", "$1minutes$2"); + + s = Regex.Replace(s, "( )[sS]([ ]|$)", "$1seconds$2"); + + return s; + } + + DateTime getTimeOffsetFromString(string timeString) + { + DateTime result; + try + { + timeString = prepareTimeString(timeString); + //var tmp = parser.Parse(timeString); + + var results = DateTimeRecognizer.RecognizeDateTime(timeString, CultureInfo.CurrentCulture.ToString()); + if (results == null || results.Count == 0) + { + results = DateTimeRecognizer.RecognizeDateTime(timeString, Culture.English); + if (results == null || results.Count == 0) + { + throw new ArgumentException(); + } + } + + result = DateTime.Parse((results[0].Resolution["values"] as List>)[0]["value"]); + + } + catch (Exception e) + { + throw new ParseFailedException(e); + } + + if (result < DateTime.Now) + { + throw new DateTimeBeforeException(); + } + + return result; + } + + public void shutdown(string timeString) + { + DateTime target; + try + { + target = getTimeOffsetFromString(timeString); + } + catch (Exception) + { + throw; + } + + using (var progressPopup = new CountdownForm(PowerDownData.Shutdown(target))) + { + progressPopup.ShowDialog(parentForm); + } + } + + public void suspend(string timeString) + { + DateTime target; + try + { + target = getTimeOffsetFromString(timeString); + } + catch (Exception) + { + throw; + } + + using (var progressPopup = new CountdownForm(PowerDownData.Suspend(target))) + { + progressPopup.ShowDialog(parentForm); + } + } + + public void hibernate(string timeString) + { + DateTime target; + try + { + target = getTimeOffsetFromString(timeString); + } + catch (Exception) + { + throw; + } + + using (var progressPopup = new CountdownForm(PowerDownData.Hibernate(target))) + { + progressPopup.ShowDialog(parentForm); + } + } + } +}