SuzuMagnifier/source/Form1.cs
Ray 5fa20668db Added format type to save options
Changed crosshair size to be used instead of show
Added readonly config file support
2026-06-28 13:02:58 +01:00

706 lines
22 KiB
C#

using System.ComponentModel;
using System.Diagnostics;
using System.Drawing.Drawing2D;
using System.Runtime.InteropServices;
using SuzuMagnifier.Dtos;
using static System.Windows.Forms.Design.AxImporter;
namespace SuzuMagnifier
{
public partial class Form1 : Form
{
[StructLayout(LayoutKind.Sequential)]
public struct POINT
{
public int X;
public int Y;
}
[DllImport("user32.dll")]
private static extern bool GetCursorPos(out POINT lpPoint);
public static Point GetMousePosition()
{
GetCursorPos(out var p);
return new Point(p.X, p.Y);
}
private readonly System.Windows.Forms.Timer _timer = new();
private Bitmap? _bitmap;
private string _jsonfigFilename;
public Form1()
{
InitializeComponent();
this.BackColor = Color.Black;
this.DoubleBuffered = true;
this.SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint | ControlStyles.OptimizedDoubleBuffer, true);
this.Text = Application.ProductName;
_timer.Interval = this.UpdateFrequency;
_timer.Tick += (sender, e) =>
{
UpdateMagnifier();
};
}
protected async override void OnLoad(EventArgs e)
{
base.OnLoad(e);
var args = GetCommandLine()!;
_jsonfigFilename = args.Where(x => (x.Key.Equals("o") || x.Key.Equals("open"))).Select(x => x.Value).FirstOrDefault();
if (string.IsNullOrWhiteSpace(_jsonfigFilename))
{
_jsonfigFilename = Path.ChangeExtension(Application.ExecutablePath, "jsonfig");
}
if (System.IO.File.Exists(_jsonfigFilename))
{
this.SavedOptions = await LoadSettings(_jsonfigFilename);
this.TopMost = this.SavedOptions!.ShowAlwaysOnTop;
this.HighQuality = this.SavedOptions.HighQuality;
this.ZoomFactor = this.SavedOptions.ZoomFactor;
this.UpdateFrequency = this.SavedOptions.UpdateFrequency;
this.CrosshairSize = this.SavedOptions.CrosshairSize;
if (!string.IsNullOrWhiteSpace(this.SavedOptions.CrosshairColour))
{
this.CrosshairColour = ColorTranslator.FromHtml(this.SavedOptions!.CrosshairColour);
}
if (this.SavedOptions!.AutoStart)
{
_timer.Start();
}
}
}
protected override void OnShown(EventArgs e)
{
base.OnShown(e);
//_timer.Start();
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
var g = e.Graphics;
if (!_timer.Enabled)
{
TextRenderer.DrawText(g, "Press [Space] To Start", this.Font, this.ClientRectangle, this.ForeColor, TextFormatFlags.HorizontalCenter | TextFormatFlags.VerticalCenter | TextFormatFlags.NoPadding);
}
if (_bitmap == null)
{
return;
}
if (this.HighQuality)
{
g.InterpolationMode = InterpolationMode.HighQualityBicubic;
g.PixelOffsetMode = PixelOffsetMode.HighQuality;
g.SmoothingMode = SmoothingMode.HighQuality;
}
else
{
g.InterpolationMode = InterpolationMode.NearestNeighbor;
g.PixelOffsetMode = PixelOffsetMode.None;
g.SmoothingMode = SmoothingMode.None;
}
g.DrawImage(_bitmap, this.ClientRectangle);
// Draw center crosshair
if (this.CrosshairSize > 0)
{
var cx = Divide(this.Width, 2);
var cy = Divide(this.Height, 2);
g.DrawLine(new Pen(this.CrosshairColour), cx - this.CrosshairSize, cy, cx + this.CrosshairSize, cy);
g.DrawLine(new Pen(this.CrosshairColour), cx, cy - this.CrosshairSize, cx, cy + this.CrosshairSize);
}
}
protected override void OnPreviewKeyDown(PreviewKeyDownEventArgs e)
{
base.OnPreviewKeyDown(e);
if (e.Control)
{
switch (e.KeyCode)
{
//case Keys.D0:
// this.ZoomFactor = 1;
// break;
//case Keys.OemMinus:
// this.ZoomFactor--;
// break;
//case Keys.Oemplus:
// this.ZoomFactor++;
// break;
//case Keys.F11:
// toolStripMenuItem5_Click(null, null);
// break;
default:
break;
}
}
else
{
switch (e.KeyCode)
{
case Keys.Space:
if (_timer.Enabled)
{
_timer.Stop();
}
else
{
_timer.Start();
}
break;
case Keys.Escape:
exitiToolStripMenuItem_Click(null, null);
break;
//case Keys.F11:
// toolStripMenuItem3_Click(null, null);
// break;
default:
break;
}
}
}
protected async override void OnFormClosing(FormClosingEventArgs e)
{
base.OnFormClosing(e);
if (this.SavedOptions.ReadOnly)
{
return;
}
var result = MessageBox.Show("Do you want to save settings to file?", "Save Settings?", MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question);
if (result == DialogResult.Yes)
{
var result2 = await SaveSettings(_jsonfigFilename);
if (result2)
{
return;
}
MessageBox.Show("Could not save settings. An error has occurred.", "Save Settings?", MessageBoxButtons.OK, MessageBoxIcon.Error);
e.Cancel = true;
}
else if (result == DialogResult.No)
{
// Do nothing
}
else
{
e.Cancel = true;
}
}
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public SavedOptions SavedOptions { get => field ?? new SavedOptions(); private set => field = value; }
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public bool HighQuality { get; set; } = false;
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public int ZoomFactor { get => field; set => field = Math.Clamp(value, 1, 20); } = 2;
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public int UpdateFrequency { get; set; } = 33;
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public int CrosshairSize { get => field; set => field = Math.Clamp(value, 0, 200); } = 20;
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public Color CrosshairColour { get; set; } = Color.FromArgb(255, 255, 0);
private void contextMenuStrip1_Opening(object sender, CancelEventArgs e)
{
toolStripMenuItem1.Enabled = !_timer.Enabled;
toolStripMenuItem2.Enabled = _timer.Enabled;
toolStripMenuItem5.Checked = this.TopMost;
toolStripMenuItem11.Checked = (this.CrosshairSize == 0);
smallToolStripMenuItem.Checked = (this.CrosshairSize == 10);
mediumToolStripMenuItem.Checked = (this.CrosshairSize == 20);
largeToolStripMenuItem.Checked = (this.CrosshairSize == 50);
resetToolStripMenuItem.Checked = (this.ZoomFactor == 1);
agentaToolStripMenuItem.Checked = (this.CrosshairColour == Color.FromArgb(255, 0, 255));
cyanToolStripMenuItem.Checked = (this.CrosshairColour == Color.FromArgb(0, 255, 255));
yellowToolStripMenuItem.Checked = (this.CrosshairColour == Color.FromArgb(255, 255, 0));
greenToolStripMenuItem.Checked = (this.CrosshairColour == Color.FromArgb(0, 255, 0));
redToolStripMenuItem.Checked = (this.CrosshairColour == Color.FromArgb(255, 0, 0));
blackToolStripMenuItem.Checked = (this.CrosshairColour == Color.FromArgb(0, 0, 0));
whiteToolStripMenuItem.Checked = (this.CrosshairColour == Color.FromArgb(255, 255, 255));
fasterToolStripMenuItem.Checked = !this.HighQuality;
fasterToolStripMenuItem.Enabled = this.HighQuality;
highToolStripMenuItem.Checked = this.HighQuality;
highToolStripMenuItem.Enabled = !this.HighQuality;
}
#region context menu
/// <summary>
/// Start
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void toolStripMenuItem1_Click(object sender, EventArgs e)
{
if (_timer.Enabled)
{
return;
}
_timer.Start();
}
/// <summary>
/// Stop
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void toolStripMenuItem2_Click(object sender, EventArgs e)
{
if (!_timer.Enabled)
{
return;
}
_timer.Stop();
}
/// <summary>
/// Quality, fast
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void fastToolStripMenuItem_Click(object sender, EventArgs e)
{
this.HighQuality = false;
}
/// <summary>
/// QUality, high
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void highToolStripMenuItem_Click(object sender, EventArgs e)
{
this.HighQuality = true;
}
/// <summary>
/// Zoom, increase
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void toolStripMenuItem7_Click(object sender, EventArgs e)
{
this.ZoomFactor++;
}
/// <summary>
/// Zoom, decrease
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void toolStripMenuItem8_Click(object sender, EventArgs e)
{
this.ZoomFactor--;
}
/// <summary>
/// Zoom, reset
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void resetToolStripMenuItem_Click(object sender, EventArgs e)
{
this.ZoomFactor = 1;
}
/// <summary>
/// Crosshair, colour, magenta
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void agentaToolStripMenuItem_Click(object sender, EventArgs e)
{
this.CrosshairColour = Color.FromArgb(255, 0, 255);
}
/// <summary>
/// Crosshair, colour, cyan
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void cyanToolStripMenuItem_Click(object sender, EventArgs e)
{
this.CrosshairColour = Color.FromArgb(0, 255, 255);
}
/// <summary>
/// Crosshair, colour, yellow
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void yellowToolStripMenuItem_Click(object sender, EventArgs e)
{
this.CrosshairColour = Color.FromArgb(255, 255, 0);
}
/// <summary>
/// Crosshair, colour, green
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void greenToolStripMenuItem_Click(object sender, EventArgs e)
{
this.CrosshairColour = Color.FromArgb(0, 255, 0);
}
/// <summary>
/// Crosshair, colour, red
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void redToolStripMenuItem_Click(object sender, EventArgs e)
{
this.CrosshairColour = Color.FromArgb(255, 0, 0);
}
/// <summary>
/// Crosshair, colour, black
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void blackToolStripMenuItem_Click(object sender, EventArgs e)
{
this.CrosshairColour = Color.FromArgb(0, 0, 0);
}
/// <summary>
/// Crosshair, colour, white
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void whiteToolStripMenuItem_Click(object sender, EventArgs e)
{
this.CrosshairColour = Color.FromArgb(255, 255, 255);
}
/// <summary>
/// ALways on top
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void toolStripMenuItem5_Click(object sender, EventArgs e)
{
this.TopMost = !this.TopMost;
}
/// <summary>
/// Fullscreen
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void toolStripMenuItem3_Click(object sender, EventArgs e)
{
if (this.WindowState == FormWindowState.Normal)
{
this.FormBorderStyle = FormBorderStyle.None;
this.WindowState = FormWindowState.Maximized;
}
else
{
this.FormBorderStyle = FormBorderStyle.Sizable;
this.WindowState = FormWindowState.Normal;
}
}
/// <summary>
/// Help, view help
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void viewHelpToolStripMenuItem_Click(object sender, EventArgs e)
{
try
{
Process.Start(new ProcessStartInfo
{
FileName = Resource1.AppHelpURL,
UseShellExecute = true
});
}
catch (Exception)
{
}
}
/// <summary>
/// Help, about
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void aboutToolStripMenuItem_Click(object sender, EventArgs e)
{
var version = Application.ProductVersion ?? string.Empty;
if (version.Contains("+"))
{
version = version.Substring(0, (version.IndexOf("+")));
}
MessageBox.Show($"{Application.ProductName} v{version}", "About", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
/// <summary>
/// Exit
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void exitiToolStripMenuItem_Click(object sender, EventArgs e)
{
if (_timer.Enabled)
{
_timer.Stop();
_timer.Dispose();
}
this.Close();
}
/// <summary>
/// Crosshair, size, off
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void toolStripMenuItem11_Click(object sender, EventArgs e)
{
this.CrosshairSize = 0;
}
/// <summary>
/// Crosshair, size, small
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void smallToolStripMenuItem_Click(object sender, EventArgs e)
{
this.CrosshairSize = 10;
}
/// <summary>
/// Crosshair, size, medium
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void mediumToolStripMenuItem_Click(object sender, EventArgs e)
{
this.CrosshairSize = 20;
}
/// <summary>
/// Crosshair, size, large
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void largeToolStripMenuItem_Click(object sender, EventArgs e)
{
this.CrosshairSize = 50;
}
#endregion
private void UpdateMagnifier()
{
//var mouse = Cursor.Position;
var mouse = GetMousePosition();
var w = Divide(this.Width, this.ZoomFactor);
var h = Divide(this.Height, this.ZoomFactor);
var cx = Divide(w, 2);
var cy = Divide(h, 2);
var x = mouse.X - cx;
var y = mouse.Y - cy;
var sourceRect = new Rectangle(x, y, w, h);
_bitmap?.Dispose();
_bitmap = null;
_bitmap = new Bitmap(sourceRect.Width, sourceRect.Height);
using Graphics g = Graphics.FromImage(_bitmap);
g.CopyFromScreen(sourceRect.Location, Point.Empty, sourceRect.Size);
this.Invalidate();
}
private int Divide(int x, int y)
{
return (int)Math.Floor((decimal)x / y);
}
/// <summary>
/// Get execution arguments.
/// </summary>
/// <returns></returns>
private List<KeyValuePair<string, string>> GetCommandLine()
{
var result = new List<KeyValuePair<string, string>>();
var args = Environment.GetCommandLineArgs() ?? new string[0];
if (args.Length <= 1)
{
return result;
}
var key = string.Empty;
var flag = false;
for (int i = 1; i < args.Length; i++)
{
var text = args[i];
if (string.IsNullOrWhiteSpace(text))
{
continue;
}
if (text.StartsWith("-"))
{
if (flag)
{
result.Add(new KeyValuePair<string, string>(key, string.Empty));
}
key = text?.TrimStart('-')?.Trim() ?? string.Empty;
flag = true;
}
else
{
result.Add(new KeyValuePair<string, string>(key, text?.Trim() ?? string.Empty));
flag = false;
}
}
return result;
}
private async Task<SavedOptions?> LoadSettings(string filename)
{
if (!System.IO.File.Exists(filename))
{
return null;
}
string sourceCode;
try
{
sourceCode = await System.IO.File.ReadAllTextAsync(filename);
}
catch (Exception)
{
return null;
}
if (string.IsNullOrWhiteSpace(sourceCode))
{
return null;
}
SavedOptions response;
try
{
response = System.Text.Json.JsonSerializer.Deserialize<SavedOptions>(sourceCode);
}
catch (Exception)
{
return null;
}
if (!response!.Format.Equals(Resource1.FileFormat, StringComparison.CurrentCultureIgnoreCase))
{
return null;
}
return response;
}
private async Task<bool> SaveSettings(string filename)
{
//_savedOptions.AutoStart = false;
this.SavedOptions.HighQuality = this.HighQuality;
this.SavedOptions.ZoomFactor = this.ZoomFactor;
this.SavedOptions.UpdateFrequency = this.UpdateFrequency;
this.SavedOptions.CrosshairSize = this.CrosshairSize;
this.SavedOptions.CrosshairColour = $"#{this.CrosshairColour.R:X2}{this.CrosshairColour.G:X2}{this.CrosshairColour.B:X2}";
this.SavedOptions.ShowAlwaysOnTop = this.TopMost;
string sourceCode = "";
try
{
sourceCode = System.Text.Json.JsonSerializer.Serialize(this.SavedOptions);
}
catch (Exception)
{
return false;
}
if (string.IsNullOrWhiteSpace(sourceCode))
{
return false;
}
if (System.IO.File.Exists(filename))
{
try
{
System.IO.File.Delete(filename);
}
catch (Exception)
{
return false;
}
}
try
{
await System.IO.File.WriteAllTextAsync(filename, sourceCode);
}
catch (Exception)
{
return false;
}
return true;
}
}
}