using System; using System.ComponentModel; using System.Configuration; using System.Drawing; using System.Linq; using System.Threading.Tasks.Dataflow; using System.Windows.Forms; using AutoUpdaterDotNET; using Gma.System.MouseKeyHook; using misu.Properties; namespace misu { public partial class MisuMainForm : Form { #region Private Delegates, Events, Enums, Properties, Indexers and Fields private BufferBlock PressedKeysBufferBlock { get; } private IKeyboardMouseEvents GlobalHook { get; } private AboutForm AboutForm { get; set; } private BindKeyForm BindKeyForm { get; set; } private volatile bool _lock; #endregion #region Constructors, Destructors and Finalizers public MisuMainForm() { InitializeComponent(); AutoUpdater.Start("http://misu.grimore.org/update/update.xml"); // Upgrade settings if required. if (!ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.PerUserRoamingAndLocal).HasFile) { Settings.Default.Upgrade(); } // Bind to settings changed event. Settings.Default.SettingsLoaded += Default_SettingsLoaded; Settings.Default.SettingsSaving += Default_SettingsSaving; Settings.Default.PropertyChanged += Default_PropertyChanged; PressedKeysBufferBlock = new BufferBlock(new DataflowBlockOptions {EnsureOrdered = true}); // Hook to global events. GlobalHook = Hook.GlobalEvents(); // Bind to receive keyboard presses. GlobalHook.KeyDown += GlobalHook_KeyDown; GlobalHook.KeyUp += GlobalHook_KeyUp; GlobalHook.MouseUpExt += GlobalHook_BlockMouse; GlobalHook.MouseDragFinishedExt += GlobalHook_BlockMouse; GlobalHook.MouseDragStartedExt += GlobalHook_BlockMouse; GlobalHook.MouseWheelExt += GlobalHook_BlockMouse; GlobalHook.MouseMoveExt += GlobalHook_BlockMouse; GlobalHook.MouseDownExt += GlobalHook_BlockMouse; if (Settings.Default.Keys != null) { Notify("Shortcut is bound.", $"Press {string.Join(" ", Settings.Default.Keys.OfType())} to toggle locking."); } } #endregion #region Event Handlers private static void Default_SettingsSaving(object sender, CancelEventArgs e) { } private static void Default_PropertyChanged(object sender, PropertyChangedEventArgs e) { Settings.Default.Save(); } private static void Default_SettingsLoaded(object sender, SettingsLoadedEventArgs e) { } private void GlobalHook_BlockMouse(object sender, MouseEventExtArgs e) { // Do not perform any operations while the binding form is visible. if (BindKeyForm != null && BindKeyForm.Visible) { return; } if (_lock && Settings.Default.Mouse) { e.Handled = true; } } private void GlobalHook_KeyUp(object sender, KeyEventArgs e) { // Do not perform any operations while the binding form is visible. if (BindKeyForm != null && BindKeyForm.Visible) { return; } if (!PressedKeysBufferBlock.TryReceiveAll(out var keys)) { return; } if (Settings.Default.Keys == null) { return; } if (!Settings.Default.Keys.OfType().SequenceEqual(keys.Select(key => key.KeyCode.ToString()))) { return; } _lock = !_lock; // Lock the cursor in position. switch (_lock) { case true: Cursor.Clip = new Rectangle(Cursor.Position, new Size(1, 1)); break; default: Cursor.Clip = Screen.PrimaryScreen.Bounds; break; } // Dismiss the current keypress to avoid typing the key. e.Handled = true; e.SuppressKeyPress = true; Notify($"Mouse and Keyboard are {(_lock ? "locked" : "unlocked")}.", $"Press {string.Join(" ", Settings.Default.Keys.OfType())} to toggle mouse and keyboard locking."); } private async void GlobalHook_KeyDown(object sender, KeyEventArgs e) { // Do not perform any operations while the binding form is visible. if (BindKeyForm != null && BindKeyForm.Visible) { return; } if (_lock && Settings.Default.Keyboard) { e.SuppressKeyPress = true; e.Handled = true; } await PressedKeysBufferBlock.SendAsync(e); } private void OnContextMenuAboutClick(object sender, EventArgs e) { // Show the about form. AboutForm = new AboutForm(); AboutForm.Show(); } private void OnContextMenuQuitClick(object sender, EventArgs e) { Close(); Environment.Exit(0); } private void OnContextMenuLaunchOnBootChanged(object sender, EventArgs e) { Settings.Default.LaunchOnBoot = ((ToolStripMenuItem) sender).Checked; Utilities.LaunchOnBootSet(Settings.Default.LaunchOnBoot); } private void OnContextMenuBindKeyClick(object sender, EventArgs e) { BindKeyForm = new BindKeyForm(GlobalHook); BindKeyForm.KeyBound += BindKeyForm_KeyBound; BindKeyForm.Closing += BindKeyForm_Closing; BindKeyForm.Show(); } private void BindKeyForm_Closing(object sender, CancelEventArgs e) { BindKeyForm.KeyBound -= BindKeyForm_KeyBound; BindKeyForm.Closing -= BindKeyForm_Closing; } private async void BindKeyForm_KeyBound(object sender, KeyBoundEventArgs e) { Notify("Shortcut key is bound.", $"Press {string.Join(" ", Settings.Default.Keys.OfType())} to toggle between locked and unlocked keyboard and mouse."); } private void KeyboardToolStripMenuItem_CheckedChanged(object sender, EventArgs e) { var item = (ToolStripMenuItem) sender; Settings.Default.Keyboard = item.Checked; } private void MouseToolStripMenuItem_CheckedChanged(object sender, EventArgs e) { var item = (ToolStripMenuItem) sender; Settings.Default.Mouse = item.Checked; } private void ContextMenuStrip_Opening(object sender, CancelEventArgs e) { // Apply the settings to the menu items. launchOnBootToolStripMenuItem.Checked = Settings.Default.LaunchOnBoot; Utilities.LaunchOnBootSet(Settings.Default.LaunchOnBoot); mouseToolStripMenuItem.Checked = Settings.Default.Mouse; keyboardToolStripMenuItem.Checked = Settings.Default.Keyboard; } #endregion #region Private Methods private void Notify(string title, string message, int timeout = 10000) { misuNotificationIcon.BalloonTipTitle = title; misuNotificationIcon.BalloonTipText = message; // the timeout parameter is not used for Windows Vista and up misuNotificationIcon.ShowBalloonTip(timeout); } #endregion } }