/// <summary>
        ///     Subscribes to Absolute mouse movement
        /// </summary>
        /// <param name="id">The id of the Mouse</param>
        /// <param name="block">Whether or not to block the movement</param>
        /// <param name="callback">The callback to fire when the mouse moves</param>
        /// <param name="concurrent">Whether or not to execute callbacks concurrently</param>
        /// <returns></returns>
        public void SubscribeMouseMoveAbsolute(int id, bool block, dynamic callback, bool concurrent = false)
        {
            HelperFunctions.IsValidDeviceId(true, id);
            SetFilterState(false);

            _mouseMoveAbsoluteMappings[id] = new MappingOptions
            {
                Block = block, Concurrent = concurrent, Callback = callback
            };

            if (!concurrent)
            {
                if (!_workerThreads.ContainsKey(id))
                {
                    _workerThreads.TryAdd(id, new ConcurrentDictionary <ushort, WorkerThread>());
                }

                _workerThreads[id].TryAdd(7, new WorkerThread()); // Use 7 as second index for MouseMoveAbsolute
                _workerThreads[id][7].Start();
            }

            SetDeviceFilterState(id, true);
            SetFilterState(true);
            SetThreadState(true);
        }
        /// <summary>
        ///
        /// Subscribes to Absolute mouse movement
        /// </summary>
        /// <param name="id">The id of the Mouse</param>
        /// <param name="block">Whether or not to block the movement</param>
        /// <param name="callback">The callback to fire when the mouse moves</param>
        /// <returns></returns>
        public void SubscribeMouseMoveAbsolute(int id, bool block, dynamic callback)
        {
            IsValidDeviceId(true, id);

            _mouseMoveAbsoluteMappings[id] = new MappingOptions()
            {
                Block = block, Callback = callback
            };
            _filteredDevices[id] = true;
            SetFilterState(true);
            SetThreadState(true);
        }
Exemple #3
0
        /// <summary>
        /// Subscribes to Relative mouse movement
        /// </summary>
        /// <param name="id">The id of the Mouse</param>
        /// <param name="block">Whether or not to block the movement</param>
        /// <param name="callback">The callback to fire when the mouse moves</param>
        /// <returns></returns>
        public void SubscribeMouseMoveRelative(int id, bool block, dynamic callback)
        {
            IsValidDeviceId(true, id);

            _mouseMoveRelativeMappings[id] = new MappingOptions()
            {
                Block = block, Callback = callback
            };
            SetDeviceFilterState(id, true);
            SetFilterState(true);
            SetThreadState(true);
        }
        // ScanCode notes: https://www.win.tue.nl/~aeb/linux/kbd/scancodes-1.html
        private void DoPoll(object sender, EventArgs e)
        {
            _pollThreadRunning = true;
            var stroke = new ManagedWrapper.Stroke();

            // Iterate through all Keyboards
            for (var i = 1; i < 11; i++)
            {
                var isMonitoredKeyboard = IsMonitoredDevice(i) == 1;
                var hasSubscription     = false;
                var hasContext          = _contextCallbacks.ContainsKey(i);

                // Process any waiting input for this keyboard
                while (ManagedWrapper.Receive(_deviceContext, i, ref stroke, 1) > 0)
                {
                    var block = false;
                    // If this is not a monitored keyboard, skip.
                    // This check should not really be needed as the IsMonitoredDevice() predicate should only match monitored keyboards...
                    // ... but in case it does, we want to ignore this bit and pass the input through
                    if (isMonitoredKeyboard)
                    {
                        var            isKeyMapping   = false; // True if this is a mapping to a single key, else it would be a mapping to a whole device
                        var            processedState = HelperFunctions.KeyboardStrokeToKeyboardState(stroke);
                        var            code           = processedState.Code;
                        var            state          = processedState.State;
                        MappingOptions mapping        = null;

                        if (_keyboardMappings.ContainsKey(i))
                        {
                            mapping = _keyboardMappings[i];
                        }
                        else if (_keyboardKeyMappings.ContainsKey(i) && _keyboardKeyMappings[i].ContainsKey(code))
                        {
                            isKeyMapping = true;
                            mapping      = _keyboardKeyMappings[i][code];
                        }
                        if (mapping != null)
                        {
                            // Process Subscription Mode

                            #region KeyCode, State, Extended Flag translation

                            // Begin translation of incoming key code, state, extended flag etc...
                            var processMappings = true;

                            #endregion

                            if (processedState.Ignore)
                            {
                                // Set flag to stop Context Mode from firing
                                hasSubscription = true;
                                // Set flag to indicate disable mapping processing
                                processMappings = false;
                            }

                            // Code and state now normalized, proceed with checking for subscriptions...
                            if (processMappings)
                            {
                                hasSubscription = true;

                                if (mapping.Block)
                                {
                                    block = true;
                                }
                                if (mapping.Concurrent)
                                {
                                    if (isKeyMapping)
                                    {
                                        ThreadPool.QueueUserWorkItem(threadProc => mapping.Callback(state));
                                    }
                                    else
                                    {
                                        ThreadPool.QueueUserWorkItem(threadProc => mapping.Callback(code, state));
                                    }
                                }
                                else
                                {
                                    if (isKeyMapping)
                                    {
                                        _workerThreads[i][code]?.Actions.Add(() => mapping.Callback(state));
                                    }
                                    else
                                    {
                                        _deviceWorkerThreads[i]?.Actions.Add(() => mapping.Callback(code, state));
                                    }
                                }
                            }
                        }
                    }

                    // If the key was blocked by Subscription Mode, then move on to next key...
                    if (block)
                    {
                        continue;
                    }

                    // If this key had no subscriptions, but Context Mode is set for this keyboard...
                    // ... then set the Context before sending the key
                    if (!hasSubscription && hasContext)
                    {
                        _contextCallbacks[i](1);
                    }

                    // Pass the key through to the OS.
                    ManagedWrapper.Send(_deviceContext, i, ref stroke, 1);

                    // If we are processing Context Mode, then Unset the context variable after sending the key
                    if (!hasSubscription && hasContext)
                    {
                        _contextCallbacks[i](0);
                    }
                }
            }

            // Process Mice
            for (var i = 11; i < 21; i++)
            {
                var isMonitoredMouse = IsMonitoredDevice(i) == 1;
                var hasSubscription  = false;
                var hasContext       = _contextCallbacks.ContainsKey(i);

                while (ManagedWrapper.Receive(_deviceContext, i, ref stroke, 1) > 0)
                {
                    if (!isMonitoredMouse)
                    {
                        continue;
                    }

                    var moveRemoved = false;
                    var hasMove     = false;
                    var x           = stroke.mouse.x;
                    var y           = stroke.mouse.y;
                    //Debug.WriteLine($"AHK| Stroke Seen. State = {stroke.mouse.state}, Flags = {stroke.mouse.flags}, x={x}, y={y}");

                    // Process mouse movement
                    if (x != 0 || y != 0)
                    {
                        hasMove = true;
                        // Process Absolute Mouse Move
                        if ((stroke.mouse.flags & (ushort)ManagedWrapper.MouseFlag.MouseMoveAbsolute) == (ushort)ManagedWrapper.MouseFlag.MouseMoveAbsolute)
                        {
                            if (_mouseMoveAbsoluteMappings.ContainsKey(i))
                            {
                                var mapping = _mouseMoveAbsoluteMappings[i];
                                hasSubscription = true;
                                //var debugStr = $"AHK| Mouse stroke has absolute move of {x}, {y}...";

                                if (mapping.Concurrent)
                                {
                                    ThreadPool.QueueUserWorkItem(threadProc => mapping.Callback(x, y));
                                }
                                else if (_workerThreads.ContainsKey(i) && _workerThreads[i].ContainsKey(7))
                                {
                                    _workerThreads[i][7]?.Actions.Add(() => mapping.Callback(x, y));
                                }
                                if (mapping.Block)
                                {
                                    moveRemoved    = true;
                                    stroke.mouse.x = 0;
                                    stroke.mouse.y = 0;
                                    //debugStr += "Blocking";
                                }
                                else
                                {
                                    //debugStr += "Not Blocking";
                                }
                                //Debug.WriteLine(debugStr);
                            }
                        }

                        // Process Relative Mouse Move
                        //else if ((stroke.mouse.flags & (ushort) ManagedWrapper.MouseFlag.MouseMoveRelative) == (ushort) ManagedWrapper.MouseFlag.MouseMoveRelative) / flag is 0, so always true!
                        else
                        {
                            if (_mouseMoveRelativeMappings.ContainsKey(i))
                            {
                                var mapping = _mouseMoveRelativeMappings[i];
                                hasSubscription = true;
                                //var debugStr = $"AHK| Mouse stroke has relative move of {x}, {y}...";

                                if (mapping.Concurrent)
                                {
                                    ThreadPool.QueueUserWorkItem(threadProc => mapping.Callback(x, y));
                                }
                                else if (_workerThreads.ContainsKey(i) && _workerThreads[i].ContainsKey(8))
                                {
                                    _workerThreads[i][8]?.Actions.Add(() => mapping.Callback(x, y));
                                }
                                if (mapping.Block)
                                {
                                    moveRemoved    = true;
                                    stroke.mouse.x = 0;
                                    stroke.mouse.y = 0;
                                    //debugStr += "Blocking";
                                }
                                else
                                {
                                    //debugStr += "Not Blocking";
                                }
                                //Debug.WriteLine(debugStr);
                            }
                        }
                    }


                    var isMouseButtonsMapping = _mouseButtonsMappings.ContainsKey(i);

                    // Process Mouse Buttons - do this AFTER mouse movement, so that absolute mode has coordinates available at the point that the button callback is fired
                    if (stroke.mouse.state != 0 && _mouseButtonMappings.ContainsKey(i) || isMouseButtonsMapping)
                    {
                        var btnStates = HelperFunctions.MouseStrokeToButtonStates(stroke);
                        foreach (var btnState in btnStates)
                        {
                            if (!isMouseButtonsMapping && !_mouseButtonMappings[i].ContainsKey(btnState.Button))
                            {
                                continue;
                            }

                            hasSubscription = true;
                            MappingOptions mapping = null;
                            if (isMouseButtonsMapping)
                            {
                                mapping = _mouseButtonsMappings[i];
                            }
                            else
                            {
                                mapping = _mouseButtonMappings[i][btnState.Button];
                            }

                            var state = btnState;

                            if (mapping.Concurrent)
                            {
                                if (isMouseButtonsMapping)
                                {
                                    ThreadPool.QueueUserWorkItem(threadProc => mapping.Callback(btnState.Button, state.State));
                                }
                                else
                                {
                                    ThreadPool.QueueUserWorkItem(threadProc => mapping.Callback(state.State));
                                }
                            }
                            else
                            {
                                if (isMouseButtonsMapping)
                                {
                                    _deviceWorkerThreads[i]?.Actions
                                    .Add(() => mapping.Callback(btnState.Button, state.State));
                                }
                                else
                                {
                                    _workerThreads[i][btnState.Button]?.Actions
                                    .Add(() => mapping.Callback(state.State));
                                }
                            }


                            if (mapping.Block)
                            {
                                // Remove the event for this button from the stroke, leaving other button events intact
                                stroke.mouse.state -= btnState.Flag;
                                // If we are removing a mouse wheel event, then set rolling to 0 if no mouse wheel event left
                                if (btnState.Flag == 0x400 || btnState.Flag == 0x800)
                                {
                                    if ((stroke.mouse.state & 0x400) != 0x400 && (stroke.mouse.state & 0x800) != 0x800)
                                    {
                                        //Debug.WriteLine("AHK| Removing rolling flag from stroke");
                                        stroke.mouse.rolling = 0;
                                    }
                                }
                                //Debug.WriteLine($"AHK| Removing flag {btnState.Flag} from stoke, leaving state {stroke.mouse.state}");
                            }
                            else
                            {
                                //Debug.WriteLine($"AHK| Leaving flag {btnState.Flag} in stroke");
                            }
                        }
                    }


                    // Forward on the stroke if required
                    if (hasSubscription)
                    {
                        // Subscription mode
                        // If the stroke has a move that was not removed, OR it has remaining button events, then forward on the stroke
                        if ((hasMove && !moveRemoved) || stroke.mouse.state != 0)
                        {
                            //Debug.WriteLine($"AHK| Sending stroke. State = {stroke.mouse.state}. hasMove={hasMove}, moveRemoved={moveRemoved}");
                            ManagedWrapper.Send(_deviceContext, i, ref stroke, 1);
                        }
                        else
                        {
                            // Everything removed from stroke, do not forward
                            //Debug.WriteLine("AHK| Mouse stroke now empty, not forwarding");
                        }
                    }
                    else if (hasContext)
                    {
                        // Context Mode - forward stroke with context wrapping
                        _contextCallbacks[i](1);
                        ManagedWrapper.Send(_deviceContext, i, ref stroke, 1);
                        _contextCallbacks[i](0);
                    }
                    else
                    {
                        // No subscription or context mode - forward on
                        //Debug.WriteLine($"AHK| Sending stroke. State = {stroke.mouse.state}. hasMove={hasMove}, moveRemoved={moveRemoved}");
                        ManagedWrapper.Send(_deviceContext, i, ref stroke, 1);
                    }
                    //Debug.WriteLine($"AHK| ");
                }
            }

            _pollThreadRunning = false;
        }