/// <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); }
/// <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; }