misu – Rev 5

Subversion Repositories:
Rev:
using System;
using System.ComponentModel;
using System.Drawing;
using System.Linq;
using System.Threading.Tasks.Dataflow;
using System.Windows.Forms;
using Configuration;
using Gma.System.MouseKeyHook;

namespace misu
{
    public partial class MisuMainForm : Form
    {
        #region Public Enums, Properties and Fields

        public Settings Settings { get; set; }

        #endregion

        #region Private Delegates, Events, Enums, Properties, Indexers and Fields

        private BufferBlock<KeyEventArgs> 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();

            PressedKeysBufferBlock = new BufferBlock<KeyEventArgs>(new DataflowBlockOptions {EnsureOrdered = true});

            // Load the settings.
            var serialization = Serialization.Deserialize(@"Settings.xml");
            switch (serialization)
            {
                case SerializationSuccess<Settings> serializationSuccess:
                    Settings = serializationSuccess.Result;
                    break;
                case SerializationFailure _:
                    Settings = new Settings();
                    break;
            }

            Settings.PropertyChanged += Settings_PropertyChanged;
            Settings.Binding.PropertyChanged += Binding_PropertyChanged;

            // 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.Binding.Keys.Any())
            {
                Notify("Shortcut is bound.",
                    $"Press {Settings.Binding} to toggle locking.");
            }
        }

        #endregion

        #region Event Handlers

        private async void Binding_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            await Settings.Serialize(@"Settings.xml");
        }

        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.Mouse)
            {
                e.Handled = true;
            }
        }

        private async void Settings_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            // Serialize the settings to the configuration file on form closing.
            await Settings.Serialize(@"Settings.xml");
        }

        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.Binding == null)
            {
                return;
            }

            if (Settings.Binding.Keys.SequenceContains(keys.Select(key => key.KeyCode)))
            {
                _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 {Settings.Binding} 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.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)
        {
            Application.Exit();
        }

        private void OnContextMenuLaunchOnBootChanged(object sender, EventArgs e)
        {
            Settings.LaunchOnBoot = ((ToolStripMenuItem) sender).Checked;

            Utilities.LaunchOnBootSet(Settings.LaunchOnBoot);
        }

        private void OnContextMenuBindKeyClick(object sender, EventArgs e)
        {
            BindKeyForm = new BindKeyForm(GlobalHook, Settings);
            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 {e.Binding} to toggle between locked and unlocked keyboard and mouse.");

            Settings.Binding.Keys.AddRange(e.Binding.Keys);

            await Settings.Serialize(@"Settings.xml");
        }

        private void KeyboardToolStripMenuItem_CheckedChanged(object sender, EventArgs e)
        {
            var item = (ToolStripMenuItem) sender;

            Settings.Keyboard = item.Checked;
        }

        private void MouseToolStripMenuItem_CheckedChanged(object sender, EventArgs e)
        {
            var item = (ToolStripMenuItem) sender;

            Settings.Mouse = item.Checked;
        }

        private void ContextMenuStrip_Opening(object sender, CancelEventArgs e)
        {
            // Apply the settings to the menu items.
            launchOnBootToolStripMenuItem.Checked = Settings.LaunchOnBoot;
            Utilities.LaunchOnBootSet(Settings.LaunchOnBoot);

            mouseToolStripMenuItem.Checked = Settings.Mouse;
            keyboardToolStripMenuItem.Checked = Settings.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
    }
}

Generated by GNU Enscript 1.6.5.90.