Skip to content

Commit

Permalink
Add support to include modifiers with hotkeys (#81)
Browse files Browse the repository at this point in the history
  • Loading branch information
oriash93 authored Aug 25, 2024
1 parent 86cb2a8 commit 668f95d
Show file tree
Hide file tree
Showing 8 changed files with 115 additions and 37 deletions.
1 change: 1 addition & 0 deletions AutoClicker/Models/HotkeyChangedEventArgs.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,6 @@ public class HotkeyChangedEventArgs : EventArgs
{
public KeyMapping Hotkey { get; set; }
public Operation Operation { get; set; }
public bool IncludeModifiers { get; set; }
}
}
4 changes: 4 additions & 0 deletions AutoClicker/Models/HotkeySettings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,14 @@ public class HotkeySettings

public static readonly KeyMapping defaultToggleKeyMapping = KeyMappingUtils.GetKeyMappingByCode(Constants.DEFAULT_TOGGLE_HOTKEY);

public static readonly bool defaultIncludeModifiers = false;

public KeyMapping StartHotkey { get; set; } = defaultStartKeyMapping;

public KeyMapping StopHotkey { get; set; } = defaultStopKeyMapping;

public KeyMapping ToggleHotkey { get; set; } = defaultToggleKeyMapping;

public bool IncludeModifiers { get; set; } = defaultIncludeModifiers;
}
}
25 changes: 19 additions & 6 deletions AutoClicker/Utils/Constants.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
namespace AutoClicker.Utils
using System.Collections.Generic;
using System.Linq;

namespace AutoClicker.Utils
{
public static class Constants
{
Expand Down Expand Up @@ -30,13 +33,23 @@ public static class Constants
public const int MOUSEEVENTF_MIDDLEUP = 0x0040;

public const int MOD_NONE = 0x0;
public const int MOD_ALT = 0x0001;
public const int MOD_CONTROL = 0x0002;
public const int MOD_SHIFT = 0x0004;
public static readonly List<int> MODIFIERS = new List<int> { MOD_NONE, MOD_ALT, MOD_CONTROL, MOD_SHIFT };

public const int START_HOTKEY_ID = 9000;
public const int STOP_HOTKEY_ID = 9001;
public const int TOGGLE_HOTKEY_ID = 9002;
public const int STOP_HOTKEY_ID = 9004;
public const int TOGGLE_HOTKEY_ID = 9008;
public const int WM_HOTKEY = 0x0312;

public const int DEFAULT_START_HOTKEY = 0x75;
public const int DEFAULT_STOP_HOTKEY = 0x76;
public const int DEFAULT_TOGGLE_HOTKEY = 0x77;
public static readonly IEnumerable<int> START_HOTKEY_IDS = Enumerable.Range(START_HOTKEY_ID, MODIFIERS.Count);
public static readonly IEnumerable<int> STOP_HOTKEY_IDS = Enumerable.Range(STOP_HOTKEY_ID, MODIFIERS.Count);
public static readonly IEnumerable<int> TOGGLE_HOTKEY_IDS = Enumerable.Range(TOGGLE_HOTKEY_ID, MODIFIERS.Count);
public static readonly IEnumerable<int> ALL_HOTKEY_IDS = Enumerable.Union(START_HOTKEY_IDS, STOP_HOTKEY_IDS).Union(TOGGLE_HOTKEY_IDS);

public const int DEFAULT_START_HOTKEY = 0x75; // F6
public const int DEFAULT_STOP_HOTKEY = 0x76; // F7
public const int DEFAULT_TOGGLE_HOTKEY = 0x77; // F8
}
}
14 changes: 13 additions & 1 deletion AutoClicker/Utils/SettingsUtils.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,25 +39,37 @@ public static void SetStopHotKey(KeyMapping key)
CurrentSettings.HotkeySettings.StopHotkey = key;
NotifyChanges(CurrentSettings.HotkeySettings.StopHotkey, Operation.Stop);
}

public static void SetToggleHotKey(KeyMapping key)
{
CurrentSettings.HotkeySettings.ToggleHotkey = key;
NotifyChanges(CurrentSettings.HotkeySettings.ToggleHotkey, Operation.Toggle);
}

public static void SetIncludeModifiers(bool includeModifiers)
{
CurrentSettings.HotkeySettings.IncludeModifiers = includeModifiers;
NotifyChanges(CurrentSettings.HotkeySettings.StartHotkey, Operation.Start);
NotifyChanges(CurrentSettings.HotkeySettings.StopHotkey, Operation.Stop);
NotifyChanges(CurrentSettings.HotkeySettings.ToggleHotkey, Operation.Toggle);
}

public static void Reset()
{
Log.Information("Reset hotkey settings to default");
SetStartHotKey(HotkeySettings.defaultStartKeyMapping);
SetStopHotKey(HotkeySettings.defaultStopKeyMapping);
SetToggleHotKey(HotkeySettings.defaultToggleKeyMapping);
SetIncludeModifiers(HotkeySettings.defaultIncludeModifiers);
}

private static void NotifyChanges(KeyMapping hotkey, Operation operation)
{
HotkeyChangedEventArgs args = new HotkeyChangedEventArgs
{
Hotkey = hotkey,
Operation = operation
Operation = operation,
IncludeModifiers = CurrentSettings.HotkeySettings.IncludeModifiers
};
HotKeyChangedEvent.Invoke(null, args);

Expand Down
4 changes: 2 additions & 2 deletions AutoClicker/Utils/User32ApiUtils.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ public static class User32ApiUtils
[DllImport("user32.dll")]
internal static extern bool RegisterHotKey(IntPtr hWnd, int id, int fsModifiers, int vk);

[DllImport("user32.dll")]
internal static extern bool UnregisterHotKey(IntPtr hWnd, int id);
[DllImport("user32.dll", EntryPoint = "UnregisterHotKey")]
internal static extern bool DeregisterHotKey(IntPtr hWnd, int id);
}
}
61 changes: 38 additions & 23 deletions AutoClicker/Views/MainWindow.xaml.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Timers;
using System.Windows;
using System.Windows.Input;
Expand Down Expand Up @@ -95,9 +97,11 @@ protected override void OnClosed(EventArgs e)
_source.RemoveHook(StartStopHooks);

SettingsUtils.HotKeyChangedEvent -= SettingsUtils_HotKeyChangedEvent;
UnregisterHotkey(Constants.START_HOTKEY_ID);
UnregisterHotkey(Constants.STOP_HOTKEY_ID);
UnregisterHotkey(Constants.TOGGLE_HOTKEY_ID);

foreach (int hotkeyId in Constants.ALL_HOTKEY_IDS)
{
DeregisterHotKey(hotkeyId);
}

systemTrayIcon.Click -= SystemTrayIcon_Click;
systemTrayIcon.Dispose();
Expand Down Expand Up @@ -200,10 +204,7 @@ private void AboutCommand_Execute(object sender, ExecutedRoutedEventArgs e)
aboutWindow.Show();
}

private void CaptureMouseScreenCoordinatesCommand_Execute(
object sender,
ExecutedRoutedEventArgs e
)
private void CaptureMouseScreenCoordinatesCommand_Execute(object sender, ExecutedRoutedEventArgs e)
{
if (captureMouseCoordinatesWindow == null)
{
Expand Down Expand Up @@ -297,24 +298,38 @@ private void InitializeSystemTrayMenu()
systemTrayMenu.SystemTrayMenuActionEvent += SystemTrayMenu_SystemTrayMenuActionEvent;
}

private void ReRegisterHotkey(int hotkeyId, KeyMapping hotkey)
private void ReRegisterHotkey(IEnumerable<int> hotkeyIds, KeyMapping hotkey, bool includeModifiers)
{
UnregisterHotkey(hotkeyId);
RegisterHotkey(hotkeyId, hotkey);
foreach (int hotkeyId in hotkeyIds)
{
DeregisterHotKey(hotkeyId);
}
RegisterHotkey(hotkeyIds, hotkey, includeModifiers);
}

private void RegisterHotkey(int hotkeyId, KeyMapping hotkey)
private void RegisterHotkey(IEnumerable<int> hotkeyIds, KeyMapping hotkey, bool includeModifiers)
{
Log.Information("RegisterHotkey with hotkeyId {HotkeyId} and hotkey {Hotkey}", hotkeyId, hotkey.DisplayName);
User32ApiUtils.RegisterHotKey(_mainWindowHandle, hotkeyId, Constants.MOD_NONE, hotkey.VirtualKeyCode);
Log.Information("RegisterHotkey with hotkey={Hotkey}, includeModifiers={IncludeModifiers}", hotkey.DisplayName, includeModifiers);
IEnumerable<(int, int)> hotkeyIdsToModifiers = Enumerable.Zip(hotkeyIds, Constants.MODIFIERS, (first, second) => ValueTuple.Create(first, second));
if (includeModifiers)
{
foreach ((int, int) item in hotkeyIdsToModifiers)
{
User32ApiUtils.RegisterHotKey(_mainWindowHandle, item.Item1, item.Item2, hotkey.VirtualKeyCode);
}
}
else
{
User32ApiUtils.RegisterHotKey(_mainWindowHandle, hotkeyIdsToModifiers.ElementAt(0).Item1, hotkeyIdsToModifiers.ElementAt(0).Item2, hotkey.VirtualKeyCode);
}
}

private void UnregisterHotkey(int hotkeyId)
private void DeregisterHotKey(int hotkeyId)
{
Log.Information("UnregisterHotkey with hotkeyId {HotkeyId}", hotkeyId);
if (User32ApiUtils.UnregisterHotKey(_mainWindowHandle, hotkeyId))
Log.Information("DeregisterHotKey with hotkeyId={HotkeyId}", hotkeyId);
if (User32ApiUtils.DeregisterHotKey(_mainWindowHandle, hotkeyId))
return;
Log.Warning("No hotkey registered on {HotkeyId}", hotkeyId);
Log.Debug("No hotkey registered on {HotkeyId}", hotkeyId);
}

#endregion Helper Methods
Expand Down Expand Up @@ -359,7 +374,7 @@ private void PerformMouseClick(int mouseDownAction, int mouseUpAction, int xPos,
{
for (int i = 0; i < GetNumberOfMouseActions(); ++i)
{
var setCursorPos = User32ApiUtils.SetCursorPosition(xPos, yPos);
bool setCursorPos = User32ApiUtils.SetCursorPosition(xPos, yPos);
if (!setCursorPos)
{
Log.Error($"Could not set the mouse cursor.");
Expand All @@ -372,7 +387,7 @@ private void PerformMouseClick(int mouseDownAction, int mouseUpAction, int xPos,
private IntPtr StartStopHooks(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
int hotkeyId = wParam.ToInt32();
if (msg == Constants.WM_HOTKEY && hotkeyId == Constants.START_HOTKEY_ID || hotkeyId == Constants.STOP_HOTKEY_ID || hotkeyId == Constants.TOGGLE_HOTKEY_ID)
if (msg == Constants.WM_HOTKEY && Constants.ALL_HOTKEY_IDS.Contains(hotkeyId))
{
int virtualKey = ((int)lParam >> 16) & 0xFFFF;
if (virtualKey == SettingsUtils.CurrentSettings.HotkeySettings.StartHotkey.VirtualKeyCode && CanStartOperation())
Expand All @@ -394,19 +409,19 @@ private IntPtr StartStopHooks(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam

private void SettingsUtils_HotKeyChangedEvent(object sender, HotkeyChangedEventArgs e)
{
Log.Information("HotKeyChangedEvent with operation {Operation} and hotkey {Hotkey}", e.Operation, e.Hotkey.DisplayName);
Log.Information("HotKeyChangedEvent with operation={Operation}, hotkey={Hotkey}, includeModifiers={IncludeModifiers}", e.Operation, e.Hotkey.DisplayName, e.IncludeModifiers);
switch (e.Operation)
{
case Operation.Start:
ReRegisterHotkey(Constants.START_HOTKEY_ID, e.Hotkey);
ReRegisterHotkey(Constants.START_HOTKEY_IDS, e.Hotkey, e.IncludeModifiers);
startButton.Content = $"{Constants.MAIN_WINDOW_START_BUTTON_CONTENT} ({e.Hotkey.DisplayName})";
break;
case Operation.Stop:
ReRegisterHotkey(Constants.STOP_HOTKEY_ID, e.Hotkey);
ReRegisterHotkey(Constants.STOP_HOTKEY_IDS, e.Hotkey, e.IncludeModifiers);
stopButton.Content = $"{Constants.MAIN_WINDOW_STOP_BUTTON_CONTENT} ({e.Hotkey.DisplayName})";
break;
case Operation.Toggle:
ReRegisterHotkey(Constants.TOGGLE_HOTKEY_ID, e.Hotkey);
ReRegisterHotkey(Constants.TOGGLE_HOTKEY_IDS, e.Hotkey, e.IncludeModifiers);
toggleButton.Content = $"{Constants.MAIN_WINDOW_TOGGLE_BUTTON_CONTENT} ({e.Hotkey.DisplayName})";
break;
default:
Expand Down
16 changes: 13 additions & 3 deletions AutoClicker/Views/SettingsWindow.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
xmlns:commands="clr-namespace:AutoClicker.Commands"
Topmost="true"
ResizeMode="CanMinimize"
Height="180" Width="350"
Height="200" Width="350"
Icon="{StaticResource SettingsIcon}">
<Window.CommandBindings>
<CommandBinding Command="commands:SettingsWindowCommands.Save"
Expand All @@ -20,6 +20,7 @@
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="150"/>
Expand All @@ -43,6 +44,7 @@
KeyDown="StopKeyTextBox_KeyDown"
HorizontalAlignment="Stretch"
VerticalContentAlignment="Center" HorizontalContentAlignment="Center"/>

<Label Grid.Row="2" Grid.Column="0" Margin="5, 5, 5, 5"
Content="Toggle Hotkey: "/>
<TextBox Name="toggleKeyTextBox"
Expand All @@ -52,12 +54,20 @@
HorizontalAlignment="Stretch"
VerticalContentAlignment="Center" HorizontalContentAlignment="Center"/>

<CheckBox Name="includeModifiersCheckBox"
Grid.Row="3" Grid.Column="0" Margin="5, 5, 5, 5"
VerticalAlignment="Center"
Content="Include modifiers"
IsChecked="{Binding Path=IncludeModifiers, Mode=OneWay}"
Checked="IncludeModifiersCheckBox_CheckedChanged"
Unchecked="IncludeModifiersCheckBox_CheckedChanged"/>

<Button Name="saveSettingsButton"
Content="Save" Grid.Row="3" Grid.Column="0" Margin="5, 5, 5, 5"
Content="Save" Grid.Row="4" Grid.Column="0" Margin="5, 5, 5, 5"
Command="commands:SettingsWindowCommands.Save"/>

<Button Name="resetSettingsButton"
Content="Reset" Grid.Row="3" Grid.Column="1" Margin="5, 5, 5, 5"
Content="Reset" Grid.Row="4" Grid.Column="1" Margin="5, 5, 5, 5"
Command="commands:SettingsWindowCommands.Reset"/>
</Grid>
</Window>
27 changes: 25 additions & 2 deletions AutoClicker/Views/SettingsWindow.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using AutoClicker.Models;
using AutoClicker.Utils;
using Serilog;
using CheckBox = System.Windows.Controls.CheckBox;

namespace AutoClicker.Views
{
Expand Down Expand Up @@ -39,6 +40,15 @@ public KeyMapping SelectedToggleKey
public static readonly DependencyProperty SelectedToggleKeyProperty =
DependencyProperty.Register(nameof(SelectedToggleKey), typeof(KeyMapping), typeof(SettingsWindow));

public bool IncludeModifiers
{
get => (bool)GetValue(IncludeModifiersProperty);
set => SetValue(IncludeModifiersProperty, value);
}

public static readonly DependencyProperty IncludeModifiersProperty =
DependencyProperty.Register(nameof(IncludeModifiers), typeof(bool), typeof(SettingsWindow));

public List<KeyMapping> KeyMapping { get; set; }

#endregion Dependency Properties
Expand All @@ -54,6 +64,7 @@ public SettingsWindow()
SelectedStartKey = SettingsUtils.CurrentSettings.HotkeySettings.StartHotkey;
SelectedStopKey = SettingsUtils.CurrentSettings.HotkeySettings.StopHotkey;
SelectedToggleKey = SettingsUtils.CurrentSettings.HotkeySettings.ToggleHotkey;
IncludeModifiers = SettingsUtils.CurrentSettings.HotkeySettings.IncludeModifiers;

InitializeComponent();
}
Expand All @@ -72,10 +83,15 @@ private void SaveCommand_Execute(object sender, ExecutedRoutedEventArgs e)
{
SettingsUtils.SetStopHotKey(SelectedStopKey);
}
if (SelectedStopKey != SettingsUtils.CurrentSettings.HotkeySettings.ToggleHotkey)
if (SelectedToggleKey != SettingsUtils.CurrentSettings.HotkeySettings.ToggleHotkey)
{
SettingsUtils.SetToggleHotKey(SelectedToggleKey);
}
if (IncludeModifiers != SettingsUtils.CurrentSettings.HotkeySettings.IncludeModifiers)
{
SettingsUtils.SetIncludeModifiers(IncludeModifiers);
}
Close();
}

private void ResetCommand_Execute(object sender, ExecutedRoutedEventArgs e)
Expand All @@ -84,6 +100,7 @@ private void ResetCommand_Execute(object sender, ExecutedRoutedEventArgs e)
SelectedStartKey = SettingsUtils.CurrentSettings.HotkeySettings.StartHotkey;
SelectedStopKey = SettingsUtils.CurrentSettings.HotkeySettings.StopHotkey;
SelectedToggleKey = SettingsUtils.CurrentSettings.HotkeySettings.ToggleHotkey;
IncludeModifiers = SettingsUtils.CurrentSettings.HotkeySettings.IncludeModifiers;
}

#endregion Commands
Expand All @@ -105,6 +122,12 @@ private void ToggleKeyTextBox_KeyDown(object sender, KeyEventArgs e)
SelectedToggleKey = GenericKeyDownHandler(e) ?? SelectedToggleKey;
}

private void IncludeModifiersCheckBox_CheckedChanged(object sender, RoutedEventArgs e)
{
CheckBox checkbox = (CheckBox)sender;
IncludeModifiers = checkbox.IsChecked.Value;
}

private KeyMapping GenericKeyDownHandler(KeyEventArgs e)
{
KeyMapping newKeyMapping = GetNewKeyMapping(e.Key);
Expand All @@ -121,7 +144,7 @@ private KeyMapping GenericKeyDownHandler(KeyEventArgs e)
private KeyMapping GetNewKeyMapping(Key key)
{
int virtualKeyCode = KeyInterop.VirtualKeyFromKey(key);
Log.Debug("GetNewKeyMapping with virtualKeyCode {VirtualKeyCode}", virtualKeyCode);
Log.Debug("GetNewKeyMapping with virtualKeyCode={VirtualKeyCode}", virtualKeyCode);
return KeyMapping.FirstOrDefault(keyMapping => keyMapping.VirtualKeyCode == virtualKeyCode);
}

Expand Down

0 comments on commit 668f95d

Please sign in to comment.