using System; using System.ComponentModel; using System.Diagnostics; using System.Drawing; using System.Runtime.InteropServices; using System.Threading.Tasks; using System.Windows.Forms; using RyzStudio.Windows.Forms; namespace CursorGuardRail { public partial class MainForm : Form { [StructLayout(LayoutKind.Sequential)] public struct RECT { public int Left; public int Top; public int Right; public int Bottom; } [DllImport("user32.dll", SetLastError = true)] private static extern bool ClipCursor(ref RECT lpRect); [DllImport("user32.dll", SetLastError = true)] private static extern bool ClipCursor(IntPtr lpRect); private bool _requestExit = false; private Rectangle? _lockCursorArea; private readonly Timer _timer; public MainForm() { InitializeComponent(); notifyIcon1.Visible = this.ShowTrayIcon; screensLayout1.Padding = new Padding(10); _timer = new() { Interval = 150 }; _timer.Tick += (_, _) => { if (Cursor.Clip != _lockCursorArea.Value) { ClipCursor(_lockCursorArea.Value); } }; } protected override void OnLoad(EventArgs e) { base.OnLoad(e); ClipCursor(Rectangle.Empty); } protected async override void OnShown(EventArgs e) { base.OnShown(e); await Task.Run(() => { UIControl.Invoke(screensLayout1, (_) => screensLayout1.InvalidateScreens()); }); } protected override void OnFormClosing(FormClosingEventArgs e) { if ((this.HideOnClose && !_requestExit) || _timer.Enabled) { e.Cancel = true; this.Hide(); return; } if (_timer.Enabled) _timer.Stop(); ClipCursor(Rectangle.Empty); base.OnFormClosing(e); } protected override void OnResize(EventArgs e) { if (this.WindowState == FormWindowState.Minimized) { if (this.ShowTrayIcon) this.Hide(); } base.OnResize(e); } public new void Show() { notifyIcon1.Visible = this.ShowTrayIcon || _timer.Enabled; this.WindowState = FormWindowState.Normal; this.BringToFront(); base.Show(); } public new void Hide() { notifyIcon1.Visible = true; this.WindowState = FormWindowState.Normal; base.Hide(); } [Browsable(false), EditorBrowsable(EditorBrowsableState.Never), DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)] public bool IsCursorLocked { get => field; set { field = value; dialogFooter1.Button1.Checked = value; dialogFooter1.Button1Text = value ? "&Unlock Cursor" : "&Lock Cursor"; if (value) { _lockCursorArea = GetMaxScreenBound(); if (!_lockCursorArea.HasValue || _lockCursorArea == Rectangle.Empty) { this.IsCursorLocked = false; return; } _timer.Start(); //notifyIcon1.Visible = true; if (this.HideOnLock) this.Close(); } else { ClipCursor(Rectangle.Empty); notifyIcon1.Visible = value || !this.Visible; if (_timer.Enabled) _timer.Stop(); } } } = false; public bool HideOnLock { get; private set; } = true; public bool HideOnClose { get; private set; } = false; public bool ShowTrayIcon { get => field; private set { field = value; notifyIcon1.Visible = value || _timer.Enabled; } } = false; private void menuStrip1_MenuActivate(object sender, EventArgs e) { toolStripMenuItem3.Checked = this.IsCursorLocked; hideOnLockToolStripMenuItem.Checked = this.HideOnLock; hideOnCloseToolStripMenuItem.Checked = this.HideOnClose; alwaysShowSystemTrayIconToolStripMenuItem.Checked = this.ShowTrayIcon; } private void contextMenuStrip1_Opening(object sender, CancelEventArgs e) { toolStripMenuItem2.Checked = this.IsCursorLocked; } #region Main Menu /// /// New /// /// /// private async void newToolStripMenuItem_Click(object sender, EventArgs e) { refreshToolStripMenuItem_Click(sender, e); } /// /// Close /// /// /// private void exitToolStripMenuItem2_Click(object sender, EventArgs e) { _requestExit = true; this.Close(); } /// /// Refresh /// /// /// private async void refreshToolStripMenuItem_Click(object sender, EventArgs e) { await Task.Run(() => { this.IsCursorLocked = false; ClipCursor(Rectangle.Empty); UIControl.Invoke(screensLayout1, (_) => screensLayout1.InvalidateScreens()); }); } /// /// Hide on lock /// /// /// private void hideOnLockToolStripMenuItem_Click(object sender, EventArgs e) { this.HideOnLock = !this.HideOnLock; } /// /// Hide on close /// /// /// private void hideOnCloseToolStripMenuItem_Click(object sender, EventArgs e) { this.HideOnClose = !this.HideOnClose; } /// /// Always show system tray icon /// /// /// private void alwaysShowSystemTrayIconToolStripMenuItem_Click(object sender, EventArgs e) { this.ShowTrayIcon = !this.ShowTrayIcon; } /// /// Options /// /// /// private void optionsToolStripMenuItem_Click(object sender, EventArgs e) { // Do nothing } /// /// Help /// /// /// private void viewHelpToolStripMenuItem1_Click(object sender, EventArgs e) { RyzStudio.Diagnostics.Process.Execute(AppResource.AppHelpURL); } /// /// About /// /// /// private void aboutToolStripMenuItem1_Click(object sender, EventArgs e) { var form = new RyzStudio.Forms.T3AboutForm(); form.ProductURL = AppResource.AppProductURL; form.AuthorURL = AppResource.AppAuthorURL; form.CompanyURL = AppResource.AppCompanyURL; form.ProductCopyrightStartYear = 2026; form.ProductLogo = AppResource.icon_64; form.ShowDialog(); } #endregion /// /// Refresh /// /// /// private async void button1_MouseClick(object sender, MouseEventArgs e) { if (e.Button == MouseButtons.Left) { refreshToolStripMenuItem_Click(sender, e); } } /// /// Open display settings /// /// /// private async void button2_MouseClick(object sender, MouseEventArgs e) { if (e.Button == MouseButtons.Left) { try { Process.Start(new ProcessStartInfo { FileName = "ms-settings:display", UseShellExecute = true }); } catch (Exception) { // Do nothing } } } /// /// Lock/unlock cursor /// /// /// private void toolStripMenuItem2_Click(object sender, EventArgs e) { this.IsCursorLocked = !this.IsCursorLocked; } #region System tray icon /// /// Hide/show /// /// /// private void notifyIcon1_MouseDoubleClick(object sender, MouseEventArgs e) { if (this.Visible) { this.Hide(); } else { this.Show(); } } /// /// Show /// /// /// private void toolStripMenuItem5_Click(object sender, EventArgs e) { this.Show(); } #endregion /// /// Enable /// /// /// private async void dialogFooter1_OnButton1Click(object sender, MouseEventArgs e) { this.IsCursorLocked = !this.IsCursorLocked; } /// /// Close /// /// /// private void dialogFooter1_OnButton2Click(object sender, MouseEventArgs e) { this.Close(); } private Rectangle GetMaxScreenBound() { Rectangle? rect = null; for (var i = 0; i < screensLayout1.Screens.Count; i++) { if (!screensLayout1.Screens[i].IsSelected) { continue; } if (rect == null) { rect = Screen.AllScreens[i].Bounds; } else { rect = Rectangle.Union(Screen.AllScreens[i].Bounds, rect.Value); } } return rect ?? Rectangle.Empty; } private void ClipCursor(Rectangle rect) { if (rect == Rectangle.Empty) { ClipCursor(IntPtr.Zero); } else { var rect2 = new RECT { Left = rect.Left, Top = rect.Top, Right = rect.Right, Bottom = rect.Bottom }; ClipCursor(ref rect2); } } } }