diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 0000000..1f8dd0a
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,244 @@
+# Remove the line below if you want to inherit .editorconfig settings from higher directories
+root = true
+
+# C# files
+[*.cs]
+
+#### Core EditorConfig Options ####
+
+# Indentation and spacing
+indent_size = 4
+indent_style = space
+tab_width = 4
+
+# New line preferences
+end_of_line = crlf
+insert_final_newline = false
+
+#### .NET Code Actions ####
+
+# Type members
+dotnet_hide_advanced_members = false
+dotnet_member_insertion_location = with_other_members_of_the_same_kind
+dotnet_property_generation_behavior = prefer_throwing_properties
+
+# Symbol search
+dotnet_search_reference_assemblies = true
+
+#### .NET Coding Conventions ####
+
+# Organize usings
+dotnet_separate_import_directive_groups = false
+dotnet_sort_system_directives_first = false
+file_header_template = Copyright (C) 2024 Ryan Luu\n\nThis file is part of Fluent Auto Clicker.\n\nFluent Auto Clicker is free software: you can redistribute it and/or modify\nit under the terms of the GNU Affero General Public License as published\nby the Free Software Foundation, either version 3 of the License, or\n(at your option) any later version.\n\nFluent Auto Clicker is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\nGNU Affero General Public License for more details.\n\nYou should have received a copy of the GNU Affero General Public License\nalong with Fluent Auto Clicker. If not, see .
+
+# this. and Me. preferences
+dotnet_style_qualification_for_event = false
+dotnet_style_qualification_for_field = false
+dotnet_style_qualification_for_method = false
+dotnet_style_qualification_for_property = false
+
+# Language keywords vs BCL types preferences
+dotnet_style_predefined_type_for_locals_parameters_members = true
+dotnet_style_predefined_type_for_member_access = true
+
+# Parentheses preferences
+dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity
+dotnet_style_parentheses_in_other_binary_operators = always_for_clarity
+dotnet_style_parentheses_in_other_operators = never_if_unnecessary
+dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity
+
+# Modifier preferences
+dotnet_style_require_accessibility_modifiers = for_non_interface_members
+
+# Expression-level preferences
+dotnet_prefer_system_hash_code = true
+dotnet_style_coalesce_expression = true
+dotnet_style_collection_initializer = true
+dotnet_style_explicit_tuple_names = true
+dotnet_style_namespace_match_folder = true
+dotnet_style_null_propagation = true
+dotnet_style_object_initializer = true
+dotnet_style_operator_placement_when_wrapping = beginning_of_line
+dotnet_style_prefer_auto_properties = true
+dotnet_style_prefer_collection_expression = when_types_loosely_match
+dotnet_style_prefer_compound_assignment = true
+dotnet_style_prefer_conditional_expression_over_assignment = true
+dotnet_style_prefer_conditional_expression_over_return = true
+dotnet_style_prefer_foreach_explicit_cast_in_source = when_strongly_typed
+dotnet_style_prefer_inferred_anonymous_type_member_names = true
+dotnet_style_prefer_inferred_tuple_names = true
+dotnet_style_prefer_is_null_check_over_reference_equality_method = true
+dotnet_style_prefer_simplified_boolean_expressions = true
+dotnet_style_prefer_simplified_interpolation = true
+
+# Field preferences
+dotnet_style_readonly_field = true
+
+# Parameter preferences
+dotnet_code_quality_unused_parameters = all
+
+# Suppression preferences
+dotnet_remove_unnecessary_suppression_exclusions = none
+
+# New line preferences
+dotnet_style_allow_multiple_blank_lines_experimental = true
+dotnet_style_allow_statement_immediately_after_block_experimental = true
+
+#### C# Coding Conventions ####
+
+# var preferences
+csharp_style_var_elsewhere = false
+csharp_style_var_for_built_in_types = false
+csharp_style_var_when_type_is_apparent = false
+
+# Expression-bodied members
+csharp_style_expression_bodied_accessors = true
+csharp_style_expression_bodied_constructors = false
+csharp_style_expression_bodied_indexers = true
+csharp_style_expression_bodied_lambdas = true
+csharp_style_expression_bodied_local_functions = false
+csharp_style_expression_bodied_methods = false
+csharp_style_expression_bodied_operators = false
+csharp_style_expression_bodied_properties = true
+
+# Pattern matching preferences
+csharp_style_pattern_matching_over_as_with_null_check = true
+csharp_style_pattern_matching_over_is_with_cast_check = true
+csharp_style_prefer_extended_property_pattern = true
+csharp_style_prefer_not_pattern = true
+csharp_style_prefer_pattern_matching = true
+csharp_style_prefer_switch_expression = true
+
+# Null-checking preferences
+csharp_style_conditional_delegate_call = true
+
+# Modifier preferences
+csharp_prefer_static_anonymous_function = true
+csharp_prefer_static_local_function = true
+csharp_preferred_modifier_order = public,private,protected,internal,file,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,required,volatile,async
+csharp_style_prefer_readonly_struct = true
+csharp_style_prefer_readonly_struct_member = true
+
+# Code-block preferences
+csharp_prefer_braces = true
+csharp_prefer_simple_using_statement = true
+csharp_prefer_system_threading_lock = true
+csharp_style_namespace_declarations = file_scoped
+csharp_style_prefer_method_group_conversion = true
+csharp_style_prefer_primary_constructors = true
+csharp_style_prefer_top_level_statements = true
+
+# Expression-level preferences
+csharp_prefer_simple_default_expression = true
+csharp_style_deconstructed_variable_declaration = true
+csharp_style_implicit_object_creation_when_type_is_apparent = true
+csharp_style_inlined_variable_declaration = true
+csharp_style_prefer_index_operator = true
+csharp_style_prefer_local_over_anonymous_function = true
+csharp_style_prefer_null_check_over_type_check = true
+csharp_style_prefer_range_operator = true
+csharp_style_prefer_tuple_swap = true
+csharp_style_prefer_utf8_string_literals = true
+csharp_style_throw_expression = true
+csharp_style_unused_value_assignment_preference = discard_variable
+csharp_style_unused_value_expression_statement_preference = discard_variable
+
+# 'using' directive preferences
+csharp_using_directive_placement = outside_namespace
+
+# New line preferences
+csharp_style_allow_blank_line_after_colon_in_constructor_initializer_experimental = true
+csharp_style_allow_blank_line_after_token_in_arrow_expression_clause_experimental = true
+csharp_style_allow_blank_line_after_token_in_conditional_expression_experimental = true
+csharp_style_allow_blank_lines_between_consecutive_braces_experimental = true
+csharp_style_allow_embedded_statements_on_same_line_experimental = true
+
+#### C# Formatting Rules ####
+
+# New line preferences
+csharp_new_line_before_catch = true
+csharp_new_line_before_else = true
+csharp_new_line_before_finally = true
+csharp_new_line_before_members_in_anonymous_types = true
+csharp_new_line_before_members_in_object_initializers = true
+csharp_new_line_before_open_brace = all
+csharp_new_line_between_query_expression_clauses = true
+
+# Indentation preferences
+csharp_indent_block_contents = true
+csharp_indent_braces = false
+csharp_indent_case_contents = true
+csharp_indent_case_contents_when_block = true
+csharp_indent_labels = one_less_than_current
+csharp_indent_switch_labels = true
+
+# Space preferences
+csharp_space_after_cast = false
+csharp_space_after_colon_in_inheritance_clause = true
+csharp_space_after_comma = true
+csharp_space_after_dot = false
+csharp_space_after_keywords_in_control_flow_statements = true
+csharp_space_after_semicolon_in_for_statement = true
+csharp_space_around_binary_operators = before_and_after
+csharp_space_around_declaration_statements = false
+csharp_space_before_colon_in_inheritance_clause = true
+csharp_space_before_comma = false
+csharp_space_before_dot = false
+csharp_space_before_open_square_brackets = false
+csharp_space_before_semicolon_in_for_statement = false
+csharp_space_between_empty_square_brackets = false
+csharp_space_between_method_call_empty_parameter_list_parentheses = false
+csharp_space_between_method_call_name_and_opening_parenthesis = false
+csharp_space_between_method_call_parameter_list_parentheses = false
+csharp_space_between_method_declaration_empty_parameter_list_parentheses = false
+csharp_space_between_method_declaration_name_and_open_parenthesis = false
+csharp_space_between_method_declaration_parameter_list_parentheses = false
+csharp_space_between_parentheses = false
+csharp_space_between_square_brackets = false
+
+# Wrapping preferences
+csharp_preserve_single_line_blocks = true
+csharp_preserve_single_line_statements = true
+
+#### Naming styles ####
+
+# Naming rules
+
+dotnet_naming_rule.interface_should_be_begins_with_i.severity = suggestion
+dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface
+dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i
+
+dotnet_naming_rule.types_should_be_pascal_case.severity = suggestion
+dotnet_naming_rule.types_should_be_pascal_case.symbols = types
+dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case
+
+dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = suggestion
+dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members
+dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case
+
+# Symbol specifications
+
+dotnet_naming_symbols.interface.applicable_kinds = interface
+dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
+dotnet_naming_symbols.interface.required_modifiers =
+
+dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum
+dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
+dotnet_naming_symbols.types.required_modifiers =
+
+dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method
+dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
+dotnet_naming_symbols.non_field_members.required_modifiers =
+
+# Naming styles
+
+dotnet_naming_style.pascal_case.required_prefix =
+dotnet_naming_style.pascal_case.required_suffix =
+dotnet_naming_style.pascal_case.word_separator =
+dotnet_naming_style.pascal_case.capitalization = pascal_case
+
+dotnet_naming_style.begins_with_i.required_prefix = I
+dotnet_naming_style.begins_with_i.required_suffix =
+dotnet_naming_style.begins_with_i.word_separator =
+dotnet_naming_style.begins_with_i.capitalization = pascal_case
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
new file mode 100644
index 0000000..16cf501
--- /dev/null
+++ b/.github/workflows/ci.yml
@@ -0,0 +1,65 @@
+name: Continuous Integration
+
+on:
+ push:
+ branches: [main]
+ pull_request:
+ branches: [main]
+
+jobs:
+ build:
+ name: Build
+ runs-on: windows-latest
+
+ strategy:
+ matrix:
+ configuration: [Release]
+ platform: [x64, x86, ARM64]
+
+ env:
+ Solution_Name: FluentAutoClicker.sln
+ Dir_Name: FluentAutoClicker
+
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v4
+ with:
+ fetch-depth: 0
+
+ - name: Install .NET Core
+ uses: actions/setup-dotnet@v4
+ with:
+ dotnet-version: 9.0.x
+
+ - name: Setup MSBuild.exe
+ uses: microsoft/setup-msbuild@v2
+
+ - name: Restore the application
+ run: msbuild $env:Solution_Name /t:Restore /p:Configuration=$env:Configuration
+ env:
+ Configuration: ${{ matrix.configuration }}
+
+ - name: Decode the pfx
+ run: |
+ $pfx_cert_byte = [System.Convert]::FromBase64String("${{ secrets.BASE64_ENCODED_PFX }}")
+ $certificatePath = "GitHubActionsWorkflow.pfx"
+ [IO.File]::WriteAllBytes("$certificatePath", $pfx_cert_byte)
+
+ - name: Create the app package
+ run: msbuild $env:Solution_Name /p:Configuration=$env:Configuration /p:Platform=$env:Platform /p:UapAppxPackageBuildMode=$env:Appx_Package_Build_Mode /p:AppxBundle=$env:Appx_Bundle /p:PackageCertificateKeyFile=GitHubActionsWorkflow.pfx /p:AppxPackageDir="$env:Appx_Package_Dir" /p:GenerateAppxPackageOnBuild=true
+ env:
+ Appx_Bundle: Never
+ Appx_Package_Build_Mode: SideloadOnly
+ Appx_Package_Dir: Packages\
+ Configuration: ${{ matrix.configuration }}
+ Platform: ${{ matrix.platform }}
+
+ - name: Remove the pfx
+ run: Remove-Item -path GitHubActionsWorkflow.pfx
+
+ - name: Upload MSIX package
+ uses: actions/upload-artifact@v4
+ with:
+ name: MSIX Package (${{ matrix.configuration }}, ${{ matrix.platform }})
+ path: ${{ github.workspace }}\${{ env.Dir_Name }}\Packages\**
+ compression-level: 0
diff --git a/.github/workflows/dependency-review.yml b/.github/workflows/dependency-review.yml
index 6e59ccb..0cc3a72 100644
--- a/.github/workflows/dependency-review.yml
+++ b/.github/workflows/dependency-review.yml
@@ -1,5 +1,8 @@
-name: 'Dependency Review'
-on: [pull_request]
+name: Dependency Review
+
+on:
+ pull_request:
+ types: [opened]
permissions:
contents: read
@@ -7,12 +10,14 @@ permissions:
jobs:
dependency-review:
+ name: Review
runs-on: ubuntu-latest
+
steps:
- - name: 'Checkout Repository'
+ - name: Checkout repository
uses: actions/checkout@v4
- - name: 'Dependency Review'
- uses: actions/dependency-review-action@v4
+
+ - name: Run dependency review
+ uses: actions/actions/dependency-review-action@v4
with:
- allow-licenses: MIT, Apache-2.0, Unlicense, BSL-1.0
comment-summary-in-pr: always
diff --git a/.imgbotconfig b/.imgbotconfig
deleted file mode 100644
index ea2afad..0000000
--- a/.imgbotconfig
+++ /dev/null
@@ -1,5 +0,0 @@
-{
- "schedule": "daily",
- "aggressiveCompression": "true",
- "compressWiki": "true",
-}
diff --git a/.vsconfig b/.vsconfig
index 1a966e8..2389b29 100644
--- a/.vsconfig
+++ b/.vsconfig
@@ -3,24 +3,7 @@
"components": [
"Microsoft.VisualStudio.Component.CoreEditor",
"Microsoft.VisualStudio.Workload.CoreEditor",
- "Microsoft.VisualStudio.Component.Roslyn.Compiler",
- "Microsoft.VisualStudio.Component.Roslyn.LanguageServices",
- "Microsoft.VisualStudio.Component.NuGet",
- "Microsoft.VisualStudio.Component.SQL.CLR",
- "Microsoft.NetCore.Component.Runtime.8.0",
- "Microsoft.NetCore.Component.SDK",
- "Microsoft.VisualStudio.Component.AppInsights.Tools",
- "Microsoft.VisualStudio.Component.DiagnosticTools",
- "Microsoft.NetCore.Component.Runtime.6.0",
- "Microsoft.VisualStudio.ComponentGroup.WindowsAppSDK.Cs",
- "Microsoft.ComponentGroup.Blend",
- "Microsoft.Component.NetFX.Native",
- "Microsoft.VisualStudio.Component.Graphics",
- "Microsoft.VisualStudio.ComponentGroup.WindowsAppDevelopment.Prerequisites",
- "Microsoft.VisualStudio.ComponentGroup.UWP.NetCoreAndStandard",
"Microsoft.VisualStudio.Workload.Universal"
],
- "extensions": [
- "https://marketplace.visualstudio.com/items?itemName=TeamXavalon.XAMLStyler2022"
- ]
-}
\ No newline at end of file
+ "extensions": []
+}
diff --git a/FluentAutoClicker/App.xaml b/FluentAutoClicker/App.xaml
index 64214c3..9cb920c 100644
--- a/FluentAutoClicker/App.xaml
+++ b/FluentAutoClicker/App.xaml
@@ -1,24 +1,4 @@
-
-
-
-
+
-
+
-
+
diff --git a/FluentAutoClicker/App.xaml.cs b/FluentAutoClicker/App.xaml.cs
index 279bcf0..ab9d74e 100644
--- a/FluentAutoClicker/App.xaml.cs
+++ b/FluentAutoClicker/App.xaml.cs
@@ -1,53 +1,50 @@
-/*
- Copyright (C) 2024 Ryan Luu
-
- This file is part of Fluent Auto Clicker.
-
- Fluent Auto Clicker is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as published
- by the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- Fluent Auto Clicker is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with Fluent Auto Clicker. If not, see .
-*/
+// Copyright (C) 2024 Ryan Luu
+//
+// This file is part of Fluent Auto Clicker.
+//
+// Fluent Auto Clicker is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published
+// by the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Fluent Auto Clicker is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with Fluent Auto Clicker. If not, see .
using Microsoft.UI.Xaml;
// To learn more about WinUI, the WinUI project structure,
// and more about our project templates, see: http://aka.ms/winui-project-info.
-namespace FluentAutoClicker
+namespace FluentAutoClicker;
+
+///
+/// Provides application-specific behavior to supplement the default Application class.
+///
+public partial class App : Application
{
///
- /// Provides application-specific behavior to supplement the default Application class.
+ /// Initializes the singleton application object. This is the first line of authored code
+ /// executed, and as such is the logical equivalent of main() or WinMain().
///
- public partial class App : Application
+ public App()
{
- ///
- /// Initializes the singleton application object. This is the first line of authored code
- /// executed, and as such is the logical equivalent of main() or WinMain().
- ///
- public App()
- {
- this.InitializeComponent();
- }
-
- ///
- /// Invoked when the application is launched.
- ///
- /// Details about the launch request and process.
- protected override void OnLaunched(Microsoft.UI.Xaml.LaunchActivatedEventArgs args)
- {
- Window = new MainWindow();
- Window.Activate();
- }
+ InitializeComponent();
+ }
- public static Window Window { get; private set; }
+ ///
+ /// Invoked when the application is launched.
+ ///
+ /// Details about the launch request and process.
+ protected override void OnLaunched(Microsoft.UI.Xaml.LaunchActivatedEventArgs args)
+ {
+ Window = new MainWindow();
+ Window.Activate();
}
+
+ public static Window Window { get; private set; }
}
\ No newline at end of file
diff --git a/FluentAutoClicker/FluentAutoClicker.csproj b/FluentAutoClicker/FluentAutoClicker.csproj
index f438d4c..915ddf4 100644
--- a/FluentAutoClicker/FluentAutoClicker.csproj
+++ b/FluentAutoClicker/FluentAutoClicker.csproj
@@ -1,57 +1,30 @@
-
-
-
+WinExenet8.0-windows10.0.19041.010.0.17763.0
+ 10.0.19041.54FluentAutoClickerapp.manifestx86;x64;ARM64
- win-x86;win-x64;win-arm64
- win10-x86;win10-x64;win10-arm64
- 8305
- WMC1501
+ win-x86;win-x64;win-arm64win-$(Platform).pubxml
+ enabletruetrue
- 10.0.19041.35-preview
- true
- Fluent Auto Clicker
+ DISABLE_XAML_GENERATED_MAIN
+ Ryan Luu
+ Copyright (C) 2024 Ryan LuuAssets\WindowIcon.ico
+ Fluent Auto Clicker
-
-
-
-
-
-
+
-
-
-
-
+
+
+ False
+
+
+
+
+ False
+ False
+ False
+
+
+ False
+ False
+ False
+
+
+ False
+ False
+ False
+
+
+ False
+ True
+ True
+
+
+ True
+ True
+ True
+
+
+ True
+ True
+ True
+
+
diff --git a/FluentAutoClicker/Helpers/AutoClicker.cs b/FluentAutoClicker/Helpers/AutoClicker.cs
index 70bb12e..3da99d2 100644
--- a/FluentAutoClicker/Helpers/AutoClicker.cs
+++ b/FluentAutoClicker/Helpers/AutoClicker.cs
@@ -1,26 +1,21 @@
-/*
- Copyright (C) 2024 Ryan Luu
+// Copyright (C) 2024 Ryan Luu
+//
+// This file is part of Fluent Auto Clicker.
+//
+// Fluent Auto Clicker is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published
+// by the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Fluent Auto Clicker is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with Fluent Auto Clicker. If not, see .
- This file is part of Fluent Auto Clicker.
-
- Fluent Auto Clicker is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as published
- by the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- Fluent Auto Clicker is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with Fluent Auto Clicker. If not, see .
-*/
-
-using System;
using System.Runtime.InteropServices;
-using System.Threading;
-using System.Threading.Tasks;
namespace FluentAutoClicker.Helpers;
@@ -33,34 +28,47 @@ public static class AutoClicker
private static extern uint SendInput(uint nInputs, Input[] pInputs, int cbSize);
private static Thread _autoClickerThread;
- private static bool IsAutoClickerRunning;
-
- public static void StartAutoClicker(int ClickInterval, int RepeatAmount, int MouseButton, int ClickOffset)
+ private static bool _isAutoClickerRunning;
+
+ ///
+ /// Starts the auto clicker thread.
+ ///
+ /// The number of milliseconds to wait before clicks.
+ /// The number of clicks before stopping the auto clicker thread.
+ /// The mouse button used to click.
+ /// The amount of time in milliseconds to add randomly to the millisecond delay between clicks.
+ public static void StartAutoClicker(int millisecondsDelay, int clickAmount, int mouseButtonType, int clickDelayOffset)
{
- IsAutoClickerRunning = true;
- _autoClickerThread = new Thread(() => AutoClickerThread(ClickInterval, RepeatAmount, MouseButton, ClickOffset));
+ // TODO: Evaluate whether a thread is necessary for this.
+ _isAutoClickerRunning = true;
+ _autoClickerThread = new Thread(() => AutoClickerThread(millisecondsDelay, clickAmount, mouseButtonType, clickDelayOffset));
_autoClickerThread.Start();
}
+ ///
+ /// Stops the auto clicker thread.
+ ///
public static void StopAutoClicker()
{
- IsAutoClickerRunning = false;
+ _isAutoClickerRunning = false;
+ // HACK: Incorrectly stops the thread, but it works for now.
_autoClickerThread?.Join();
}
- private static async void AutoClickerThread(int ClickInterval, int RepeatAmount, int MouseButton, int ClickOffset)
+ private static async void AutoClickerThread(int clickInterval, int repeatAmount, int mouseButton, int clickOffset)
{
- var clickCount = 0;
- var random = new Random();
- while (IsAutoClickerRunning)
+ int clickCount = 0;
+ Random random = new();
+ while (_isAutoClickerRunning)
{
- if (clickCount >= RepeatAmount && RepeatAmount != 0)
+ if (clickCount >= repeatAmount && repeatAmount != 0)
{
StopAutoClicker();
break;
}
- switch (MouseButton)
+ // TODO: Move this to a enum instead of a number
+ switch (mouseButton)
{
case 0:
MouseEvent(0, 0, (uint)MouseEventF.LeftDown, 0, 0, IntPtr.Zero);
@@ -76,19 +84,22 @@ private static async void AutoClickerThread(int ClickInterval, int RepeatAmount,
break;
}
- if (RepeatAmount > 0) clickCount++;
+ if (repeatAmount > 0)
+ {
+ clickCount++;
+ }
- int randomClickOffset = random.Next(0, ClickOffset);
- await Task.Delay(ClickInterval + randomClickOffset);
+ int randomClickOffset = random.Next(0, clickOffset);
+ await Task.Delay(clickInterval + randomClickOffset);
}
}
private static void MouseEvent(int dx, int dy, uint dwFlags, uint dwData, uint time, nint dwExtraInfo)
{
- var inputs = new Input[2];
+ Input[] inputs = new Input[2];
inputs[0] = MouseInput(dx, dy, dwData, dwFlags, time, dwExtraInfo);
inputs[1] = MouseInput(dx, dy, dwData, dwFlags, time, dwExtraInfo);
- SendInput((uint)inputs.Length, inputs, Marshal.SizeOf(typeof(Input)));
+ _ = SendInput((uint)inputs.Length, inputs, Marshal.SizeOf());
}
private static Input MouseInput(int dx, int dy, uint mouseData, uint dwFlags, uint time, nint dwExtraInfo)
@@ -136,4 +147,4 @@ private enum MouseEventF : uint
MiddleDown = 0x0020,
MiddleUp = 0x0040
}
-}
+}
\ No newline at end of file
diff --git a/FluentAutoClicker/Helpers/ResourceExtensions.cs b/FluentAutoClicker/Helpers/ResourceExtensions.cs
index a001efd..0a0b6cb 100644
--- a/FluentAutoClicker/Helpers/ResourceExtensions.cs
+++ b/FluentAutoClicker/Helpers/ResourceExtensions.cs
@@ -1,30 +1,36 @@
-/*
- Copyright (C) 2024 Ryan Luu
-
- This file is part of Fluent Auto Clicker.
-
- Fluent Auto Clicker is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as published
- by the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- Fluent Auto Clicker is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with Fluent Auto Clicker. If not, see .
-*/
+// Copyright (C) 2024 Ryan Luu
+//
+// This file is part of Fluent Auto Clicker.
+//
+// Fluent Auto Clicker is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published
+// by the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Fluent Auto Clicker is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with Fluent Auto Clicker. If not, see .
using Microsoft.Windows.ApplicationModel.Resources;
namespace FluentAutoClicker.Helpers;
+///
+/// Helper for getting localized strings from resources.
+///
public static class ResourceExtensions
{
private static readonly ResourceLoader ResourceLoader = new();
+ ///
+ /// Gets the localized string for the resource key.
+ ///
+ /// The resource key for the returned localized string.
+ /// The localized string for the specified resource key.
public static string GetLocalized(this string resourceKey)
{
return ResourceLoader.GetString(resourceKey);
diff --git a/FluentAutoClicker/Helpers/WindowMessageHook.cs b/FluentAutoClicker/Helpers/WindowMessageHook.cs
index dba96b3..ddc6ca4 100644
--- a/FluentAutoClicker/Helpers/WindowMessageHook.cs
+++ b/FluentAutoClicker/Helpers/WindowMessageHook.cs
@@ -1,38 +1,33 @@
-/*
- Copyright (C) 2024 Ryan Luu
-
- This file is part of Fluent Auto Clicker.
-
- Fluent Auto Clicker is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as published
- by the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- Fluent Auto Clicker is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with Fluent Auto Clicker. If not, see .
-*/
+// Copyright (C) 2024 Ryan Luu
+//
+// This file is part of Fluent Auto Clicker.
+//
+// Fluent Auto Clicker is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published
+// by the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Fluent Auto Clicker is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with Fluent Auto Clicker. If not, see .
using Microsoft.UI.Xaml;
-using System;
using System.Collections.Concurrent;
-using System.Collections.Generic;
using System.ComponentModel;
using System.Runtime.InteropServices;
-using System.Threading;
namespace FluentAutoClicker.Helpers;
public class WindowMessageHook : IEquatable, IDisposable
{
- private delegate nint SUBCLASSPROC(nint hWnd, uint uMsg, nint wParam, nint lParam, nint uIdSubclass, uint dwRefData);
+ private delegate nint Subclassproc(nint hWnd, uint uMsg, nint wParam, nint lParam, nint uIdSubclass, uint dwRefData);
- private static readonly ConcurrentDictionary _hooks = new();
- private static readonly SUBCLASSPROC _proc = SubclassProc;
+ private static readonly ConcurrentDictionary Hooks = new();
+ private static readonly Subclassproc Proc = SubclassProc;
public event EventHandler Message;
private nint _hWnd;
@@ -41,28 +36,44 @@ public WindowMessageHook(Window window) : this(GetHandle(window)) { }
public WindowMessageHook(nint hWnd)
{
if (hWnd == 0)
+ {
throw new ArgumentException(null, nameof(hWnd));
+ }
_hWnd = hWnd;
- _hooks.AddOrUpdate(hWnd, this, (k, o) =>
+ _ = Hooks.AddOrUpdate(hWnd, this, (k, o) =>
{
- if (Equals(o)) return o;
+ if (Equals(o))
+ {
+ return o;
+ }
+
o.Dispose();
return this;
});
- if (!SetWindowSubclass(hWnd, _proc, 0, 0))
+ if (!SetWindowSubclass(hWnd, Proc, 0, 0))
+ {
throw new Win32Exception(Marshal.GetLastWin32Error());
+ }
+ }
+
+ protected virtual void OnMessage(object sender, MessageEventArgs e)
+ {
+ Message?.Invoke(sender, e);
}
- protected virtual void OnMessage(object sender, MessageEventArgs e) => Message?.Invoke(sender, e);
protected virtual void Dispose(bool disposing)
{
- if (!disposing) return;
- var hWnd = Interlocked.Exchange(ref _hWnd, IntPtr.Zero);
+ if (!disposing)
+ {
+ return;
+ }
+
+ nint hWnd = Interlocked.Exchange(ref _hWnd, IntPtr.Zero);
if (hWnd != IntPtr.Zero)
{
- RemoveWindowSubclass(hWnd, _proc, 0);
- _hooks.Remove(hWnd, out _);
+ _ = RemoveWindowSubclass(hWnd, Proc, 0);
+ _ = Hooks.Remove(hWnd, out _);
}
}
@@ -70,13 +81,13 @@ protected virtual void Dispose(bool disposing)
public void Dispose() { Dispose(disposing: true); GC.SuppressFinalize(this); }
[DllImport("comctl32", SetLastError = true)]
- private static extern bool SetWindowSubclass(nint hWnd, SUBCLASSPROC pfnSubclass, uint uIdSubclass, uint dwRefData);
+ private static extern bool SetWindowSubclass(nint hWnd, Subclassproc pfnSubclass, uint uIdSubclass, uint dwRefData);
[DllImport("comctl32", SetLastError = true)]
private static extern nint DefSubclassProc(nint hWnd, uint uMsg, nint wParam, nint lParam);
[DllImport("comctl32", SetLastError = true)]
- private static extern bool RemoveWindowSubclass(nint hWnd, SUBCLASSPROC pfnSubclass, uint uIdSubclass);
+ private static extern bool RemoveWindowSubclass(nint hWnd, Subclassproc pfnSubclass, uint uIdSubclass);
private static nint GetHandle(Window window)
{
@@ -86,20 +97,37 @@ private static nint GetHandle(Window window)
private static nint SubclassProc(nint hWnd, uint uMsg, nint wParam, nint lParam, nint uIdSubclass, uint dwRefData)
{
- if (_hooks.TryGetValue(hWnd, out var hook))
+ if (Hooks.TryGetValue(hWnd, out WindowMessageHook hook))
{
- var e = new MessageEventArgs(hWnd, uMsg, wParam, lParam);
+ MessageEventArgs e = new(hWnd, uMsg, wParam, lParam);
hook.OnMessage(hook, e);
if (e.Result.HasValue)
+ {
return e.Result.Value;
+ }
}
return DefSubclassProc(hWnd, uMsg, wParam, lParam);
}
- public override int GetHashCode() => _hWnd.GetHashCode();
- public override string ToString() => _hWnd.ToString();
- public override bool Equals(object obj) => Equals(obj as WindowMessageHook);
- public virtual bool Equals(WindowMessageHook other) => other != null && _hWnd.Equals(other._hWnd);
+ public override int GetHashCode()
+ {
+ return _hWnd.GetHashCode();
+ }
+
+ public override string ToString()
+ {
+ return _hWnd.ToString();
+ }
+
+ public override bool Equals(object obj)
+ {
+ return Equals(obj as WindowMessageHook);
+ }
+
+ public virtual bool Equals(WindowMessageHook other)
+ {
+ return other != null && _hWnd.Equals(other._hWnd);
+ }
}
public class MessageEventArgs : EventArgs
@@ -117,4 +145,4 @@ public MessageEventArgs(nint hWnd, uint uMsg, nint wParam, nint lParam)
public nint WParam { get; }
public nint LParam { get; }
public virtual nint? Result { get; set; }
-}
+}
\ No newline at end of file
diff --git a/FluentAutoClicker/MainPage.xaml b/FluentAutoClicker/MainPage.xaml
index 23036bc..1a56715 100644
--- a/FluentAutoClicker/MainPage.xaml
+++ b/FluentAutoClicker/MainPage.xaml
@@ -1,24 +1,4 @@
-
-
-
+
@@ -37,12 +18,9 @@
-
+
+
+
@@ -54,7 +32,6 @@
-
+
-
-
-
-
-
+ SelectedIndex="0">
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
.
-*/
+// Copyright (C) 2024 Ryan Luu
+//
+// This file is part of Fluent Auto Clicker.
+//
+// Fluent Auto Clicker is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published
+// by the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Fluent Auto Clicker is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with Fluent Auto Clicker. If not, see .
using FluentAutoClicker.Helpers;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Automation.Peers;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Media;
-using System;
using System.ComponentModel;
using System.Globalization;
using System.Runtime.InteropServices;
-using System.Threading.Tasks;
using Windows.System;
-namespace FluentAutoClicker
+namespace FluentAutoClicker;
+
+///
+/// The main page containing all controls displayed on the main window.
+///
+public sealed partial class MainPage : Page
{
- public sealed partial class MainPage : Page
+ public MainPage()
{
- public MainPage()
- {
- InitializeComponent();
- Loaded += MainPage_Loaded;
- }
+ InitializeComponent();
+ Loaded += MainPage_Loaded;
+ }
- private void MainPage_Loaded(object sender, RoutedEventArgs e)
- {
- var hook = new WindowMessageHook(App.Window);
- Unloaded += (s, e) => hook.Dispose(); // unhook on close
- hook.Message += (s, e) =>
- {
- const int WM_HOTKEY = 0x312;
- if (e.Message == WM_HOTKEY)
- {
- // click on the button using UI Automation
- var pattern = (ToggleButtonAutomationPeer)FrameworkElementAutomationPeer.FromElement(StartToggleButton).GetPattern(PatternInterface.Toggle);
- pattern.Toggle();
- }
- };
-
- // register CTRL + B as a global hotkey
- var hwnd = WinRT.Interop.WindowNative.GetWindowHandle(App.Window);
- var id = 1; // some arbitrary hotkey identifier
- if (!RegisterHotKey(hwnd, id, MOD.MOD_NOREPEAT, VirtualKey.F6))
- throw new Win32Exception(Marshal.GetLastWin32Error());
-
- Unloaded += (s, e) => UnregisterHotKey(hwnd, id); // unregister hotkey on window close
- }
+ private void MainPage_Loaded(object sender, RoutedEventArgs e)
+ {
+ WindowMessageHook hook = new(App.Window);
+ Unloaded += (s, e) => hook.Dispose();
- private void SetControlsEnabled(bool isEnabled)
+ hook.Message += (s, e) =>
{
- NumberBoxHours.IsEnabled = isEnabled;
- NumberBoxMinutes.IsEnabled = isEnabled;
- NumberBoxSeconds.IsEnabled = isEnabled;
- NumberBoxMilliseconds.IsEnabled = isEnabled;
- MouseButtonTypeComboBox.IsEnabled = isEnabled;
- ClickRepeatCheckBox.IsEnabled = isEnabled;
- ClickOffsetCheckBox.IsEnabled = isEnabled;
- //HotkeyButton.IsEnabled = isEnabled;
-
- if (ClickOffsetCheckBox.IsChecked == true)
- {
- ClickOffsetAmount.IsEnabled = isEnabled;
- }
-
- if (ClickRepeatCheckBox.IsChecked == true)
+ const int wmHotkey = 0x312;
+ if (e.Message == wmHotkey)
{
- ClickRepeatAmount.IsEnabled = isEnabled;
+ // Toggle the StartToggleButton when the hotkey is pressed
+ ToggleButtonAutomationPeer pattern = (ToggleButtonAutomationPeer)FrameworkElementAutomationPeer.FromElement(StartToggleButton).GetPattern(PatternInterface.Toggle);
+ pattern.Toggle();
}
+ };
- // Gray out text if disabled
- if (!isEnabled)
- {
- ClickIntervalTextBlock.Foreground = Application.Current.Resources["SystemControlForegroundBaseMediumLowBrush"] as Brush;
- HotkeyTextBlock.Foreground = Application.Current.Resources["SystemControlForegroundBaseMediumLowBrush"] as Brush;
- }
- else
- {
- ClickIntervalTextBlock.Foreground = Application.Current.Resources["SystemControlForegroundBaseHighBrush"] as Brush;
- HotkeyTextBlock.Foreground = Application.Current.Resources["SystemControlForegroundBaseHighBrush"] as Brush;
- }
- }
+ nint hwnd = WinRT.Interop.WindowNative.GetWindowHandle(App.Window);
+ int id = 1;
- private int GetIntervalMilliseconds()
+ // Register the F6 key
+ if (!RegisterHotKey(hwnd, id, Mod.ModNoRepeat, VirtualKey.F6))
{
- if (!Int32.TryParse(NumberBoxHours.Value.ToString(CultureInfo.InvariantCulture), out var hours))
- {
- hours = 0;
- NumberBoxHours.Value = hours;
- }
+ throw new Win32Exception(Marshal.GetLastWin32Error());
+ }
- if (!Int32.TryParse(NumberBoxMinutes.Value.ToString(CultureInfo.InvariantCulture), out var minutes))
- {
- minutes = 0;
- NumberBoxMinutes.Value = minutes;
- }
+ Unloaded += (s, e) => UnregisterHotKey(hwnd, id);
+ }
- if (!Int32.TryParse(NumberBoxSeconds.Value.ToString(CultureInfo.InvariantCulture), out var seconds))
- {
- seconds = 0;
- NumberBoxSeconds.Value = seconds;
- }
+ private void SetControlsEnabled(bool isEnabled)
+ {
+ NumberBoxHours.IsEnabled = isEnabled;
+ NumberBoxMinutes.IsEnabled = isEnabled;
+ NumberBoxSeconds.IsEnabled = isEnabled;
+ NumberBoxMilliseconds.IsEnabled = isEnabled;
+ MouseButtonTypeComboBox.IsEnabled = isEnabled;
+ ClickRepeatCheckBox.IsEnabled = isEnabled;
+ ClickOffsetCheckBox.IsEnabled = isEnabled;
+
+ ClickOffsetAmount.IsEnabled = ClickOffsetCheckBox.IsChecked == true && isEnabled;
+ ClickRepeatAmount.IsEnabled = ClickRepeatCheckBox.IsChecked == true && isEnabled;
+
+ // TODO: Change this to use a custom control. See https://github.com/RyanLua/FluentAutoClicker/issues/42
+ string brushKey = isEnabled ? "SystemControlForegroundBaseHighBrush" : "SystemControlForegroundBaseMediumLowBrush";
+ ClickIntervalTextBlock.Foreground = Application.Current.Resources[brushKey] as Brush;
+ HotkeyTextBlock.Foreground = Application.Current.Resources[brushKey] as Brush;
+ }
- if (!Int32.TryParse(NumberBoxMilliseconds.Value.ToString(CultureInfo.InvariantCulture), out var milliseconds))
- {
- milliseconds = 100;
- NumberBoxMilliseconds.Value = milliseconds;
- }
+ private int GetNumberBoxValue(NumberBox numberBox, int defaultValue)
+ {
+ if (!int.TryParse(numberBox.Value.ToString(CultureInfo.InvariantCulture), out int value))
+ {
+ value = defaultValue;
+ numberBox.Value = value;
+ }
+ return value;
+ }
- var totalTimeInMilliseconds = ((hours * 60 + minutes) * 60 + seconds) * 1000 + milliseconds;
+ private int GetIntervalMilliseconds()
+ {
+ int hours = GetNumberBoxValue(NumberBoxHours, 0);
+ int minutes = GetNumberBoxValue(NumberBoxMinutes, 0);
+ int seconds = GetNumberBoxValue(NumberBoxSeconds, 0);
+ int milliseconds = GetNumberBoxValue(NumberBoxMilliseconds, 100);
- if (totalTimeInMilliseconds == 0)
- {
- totalTimeInMilliseconds = 1;
- NumberBoxMilliseconds.Value = 1;
- }
+ int totalTimeInMilliseconds = (((((hours * 60) + minutes) * 60) + seconds) * 1000) + milliseconds;
- return totalTimeInMilliseconds;
+ if (totalTimeInMilliseconds == 0)
+ {
+ totalTimeInMilliseconds = 1;
+ NumberBoxMilliseconds.Value = 1;
}
- private async void StartToggleButton_OnChecked(object sender, RoutedEventArgs e)
- {
- StartToggleButton.IsEnabled = false;
- SetControlsEnabled(false);
+ return totalTimeInMilliseconds;
+ }
- // 3-second countdown
- for (var i = 3; i > 0; i--)
- {
- StartToggleButton.Content = i.ToString();
- await Task.Delay(1000);
- }
+ private async void StartToggleButton_OnChecked(object sender, RoutedEventArgs e)
+ {
+ StartToggleButton.IsEnabled = false;
+ SetControlsEnabled(false);
- StartToggleButton.IsEnabled = true;
- StartToggleButton.Content = "Stop";
+ // 3-second countdown
+ for (int i = 3; i > 0; i--)
+ {
+ StartToggleButton.Content = i.ToString();
+ await Task.Delay(1000);
+ }
- int clickInterval = GetIntervalMilliseconds();
- int repeatAmount = 0;
- if (ClickRepeatCheckBox.IsEnabled == true)
- {
- repeatAmount = Convert.ToInt32(ClickRepeatAmount.Value);
- }
- else
- {
- repeatAmount = 0;
- }
- int mouseButton = MouseButtonTypeComboBox.SelectedIndex;
- int clickOffset = 0;
- if (ClickOffsetCheckBox.IsChecked == true)
- {
- clickOffset = Convert.ToInt32(ClickOffsetAmount.Value);
- }
- else
- {
- clickOffset = 0;
- }
+ StartToggleButton.IsEnabled = true;
+ StartToggleButton.Content = "Stop";
- AutoClicker.StartAutoClicker(clickInterval, repeatAmount, mouseButton, clickOffset);
- }
+ int clickInterval = GetIntervalMilliseconds();
+ int repeatAmount = ClickRepeatCheckBox.IsEnabled == true ? Convert.ToInt32(ClickRepeatAmount.Value) : 0;
+ int mouseButton = MouseButtonTypeComboBox.SelectedIndex;
+ int clickOffset = ClickOffsetCheckBox.IsChecked == true ? Convert.ToInt32(ClickOffsetAmount.Value) : 0;
+ AutoClicker.StartAutoClicker(clickInterval, repeatAmount, mouseButton, clickOffset);
+ }
- private void StartToggleButton_OnUnchecked(object sender, RoutedEventArgs e)
- {
- StartToggleButton.Content = "Start";
- AutoClicker.StopAutoClicker();
- SetControlsEnabled(true);
- }
+ private void StartToggleButton_OnUnchecked(object sender, RoutedEventArgs e)
+ {
+ StartToggleButton.Content = "Start";
+ AutoClicker.StopAutoClicker();
+ SetControlsEnabled(true);
+ }
- private void ClickRepeatCheckBox_Unchecked(object sender, RoutedEventArgs e)
- {
- ClickRepeatAmount.IsEnabled = false;
- }
+ private void ClickRepeatCheckBox_Unchecked(object sender, RoutedEventArgs e)
+ {
+ ClickRepeatAmount.IsEnabled = false;
+ }
- private void ClickRepeatCheckBox_Checked(object sender, RoutedEventArgs e)
- {
- ClickRepeatAmount.IsEnabled = true;
- }
+ private void ClickRepeatCheckBox_Checked(object sender, RoutedEventArgs e)
+ {
+ ClickRepeatAmount.IsEnabled = true;
+ }
- // interop code for Windows API hotkey functions
- [DllImport("user32", SetLastError = true)]
- private static extern bool RegisterHotKey(nint hWnd, int id, MOD fsModifiers, VirtualKey vk);
+ // interop code for Windows API hotkey functions
+ [DllImport("user32", SetLastError = true)]
+ private static extern bool RegisterHotKey(nint hWnd, int id, Mod fsModifiers, VirtualKey vk);
- [DllImport("user32", SetLastError = true)]
- private static extern bool UnregisterHotKey(nint hWnd, int id);
+ [DllImport("user32", SetLastError = true)]
+ private static extern bool UnregisterHotKey(nint hWnd, int id);
- [Flags]
- private enum MOD
- {
- MOD_ALT = 0x1,
- MOD_CONTROL = 0x2,
- MOD_SHIFT = 0x4,
- MOD_WIN = 0x8,
- MOD_NOREPEAT = 0x4000,
- }
- private void ClickOffsetCheckBox_Unchecked(object sender, RoutedEventArgs e)
- {
- ClickOffsetAmount.IsEnabled = false;
- }
+ [Flags]
+ private enum Mod
+ {
+ ModAlt = 0x1,
+ ModControl = 0x2,
+ ModShift = 0x4,
+ ModWin = 0x8,
+ ModNoRepeat = 0x4000,
+ }
+ private void ClickOffsetCheckBox_Unchecked(object sender, RoutedEventArgs e)
+ {
+ ClickOffsetAmount.IsEnabled = false;
+ }
- private void ClickOffsetCheckBox_Checked(object sender, RoutedEventArgs e)
- {
- ClickOffsetAmount.IsEnabled = true;
- }
+ private void ClickOffsetCheckBox_Checked(object sender, RoutedEventArgs e)
+ {
+ ClickOffsetAmount.IsEnabled = true;
}
-}
+}
\ No newline at end of file
diff --git a/FluentAutoClicker/MainWindow.xaml b/FluentAutoClicker/MainWindow.xaml
index b3d547a..f56690b 100644
--- a/FluentAutoClicker/MainWindow.xaml
+++ b/FluentAutoClicker/MainWindow.xaml
@@ -1,24 +1,4 @@
-
-
-
@@ -45,11 +25,11 @@
-
-
-
+
+
+
+
+
diff --git a/FluentAutoClicker/MainWindow.xaml.cs b/FluentAutoClicker/MainWindow.xaml.cs
index 75f9ab4..2115f53 100644
--- a/FluentAutoClicker/MainWindow.xaml.cs
+++ b/FluentAutoClicker/MainWindow.xaml.cs
@@ -1,21 +1,19 @@
-/*
- Copyright (C) 2024 Ryan Luu
-
- This file is part of Fluent Auto Clicker.
-
- Fluent Auto Clicker is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as published
- by the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- Fluent Auto Clicker is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with Fluent Auto Clicker. If not, see .
-*/
+// Copyright (C) 2024 Ryan Luu
+//
+// This file is part of Fluent Auto Clicker.
+//
+// Fluent Auto Clicker is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published
+// by the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Fluent Auto Clicker is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with Fluent Auto Clicker. If not, see .
using FluentAutoClicker.Helpers;
@@ -25,7 +23,7 @@ You should have received a copy of the GNU Affero General Public License
namespace FluentAutoClicker;
///
-/// An empty window that can be used on its own or navigated to within a Frame.
+/// An window that displays a page's contents.
///
public sealed partial class MainWindow
{
@@ -33,11 +31,12 @@ public MainWindow()
{
InitializeComponent();
+ // Set up window
Title = "AppDisplayName".GetLocalized();
- ExtendsContentIntoTitleBar = true;
- SetTitleBar(AppTitleBar);
AppWindow.SetIcon("Assets/WindowIcon.ico");
+ // Set up window title bar
+ ExtendsContentIntoTitleBar = true;
AppTitleBar.Title = "AppDisplayName".GetLocalized();
}
}
\ No newline at end of file
diff --git a/FluentAutoClicker/Package.appxmanifest b/FluentAutoClicker/Package.appxmanifest
index 7067732..b54ee96 100644
--- a/FluentAutoClicker/Package.appxmanifest
+++ b/FluentAutoClicker/Package.appxmanifest
@@ -8,15 +8,15 @@
IgnorableNamespaces="uap rescap">
+ Name="RyanLuu.FluentAutoClicker"
+ Publisher="CN=Ryan Luu, O=Ryan Luu, C=US"
+ Version="1.0.0.0" />
- ms-resource:AppDisplayName
- ryanl
+ ms-resource:AppDisplayName
+ Ryan LuuAssets\StoreLogo.png
diff --git a/FluentAutoClicker/Program.cs b/FluentAutoClicker/Program.cs
new file mode 100644
index 0000000..2d9bf17
--- /dev/null
+++ b/FluentAutoClicker/Program.cs
@@ -0,0 +1,116 @@
+// Copyright (C) 2024 Ryan Luu
+//
+// This file is part of Fluent Auto Clicker.
+//
+// Fluent Auto Clicker is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published
+// by the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Fluent Auto Clicker is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with Fluent Auto Clicker. If not, see .
+
+using Microsoft.UI.Dispatching;
+using Microsoft.UI.Xaml;
+using Microsoft.Windows.AppLifecycle;
+using System.Diagnostics;
+using System.Runtime.InteropServices;
+
+namespace FluentAutoClicker;
+
+///
+/// Customized Program.cs file to implement single-instancing in a WinUI app with C#. Single-instanced apps only allow one instance of the app running at a time.
+///
+public class Program
+{
+ [STAThread]
+ private static int Main(string[] args)
+ {
+ WinRT.ComWrappersSupport.InitializeComWrappers();
+ bool isRedirect = DecideRedirection();
+
+ if (!isRedirect)
+ {
+ Application.Start((p) =>
+ {
+ DispatcherQueueSynchronizationContext context = new(
+ DispatcherQueue.GetForCurrentThread());
+ SynchronizationContext.SetSynchronizationContext(context);
+ _ = new App();
+ });
+ }
+
+ return 0;
+ }
+
+ private static bool DecideRedirection()
+ {
+ bool isRedirect = false;
+ AppActivationArguments args = AppInstance.GetCurrent().GetActivatedEventArgs();
+ ExtendedActivationKind kind = args.Kind;
+ AppInstance keyInstance = AppInstance.FindOrRegisterForKey("MySingleInstanceApp");
+
+ if (keyInstance.IsCurrent)
+ {
+ keyInstance.Activated += OnActivated;
+ }
+ else
+ {
+ isRedirect = true;
+ RedirectActivationTo(args, keyInstance);
+ }
+
+ return isRedirect;
+ }
+
+ [DllImport("kernel32.dll", CharSet = CharSet.Unicode)]
+ private static extern IntPtr CreateEvent(
+ IntPtr lpEventAttributes, bool bManualReset,
+ bool bInitialState, string lpName);
+
+ [DllImport("kernel32.dll")]
+ private static extern bool SetEvent(IntPtr hEvent);
+
+ [DllImport("ole32.dll")]
+ private static extern uint CoWaitForMultipleObjects(
+ uint dwFlags, uint dwMilliseconds, ulong nHandles,
+ IntPtr[] pHandles, out uint dwIndex);
+
+ [DllImport("user32.dll")]
+ private static extern bool SetForegroundWindow(IntPtr hWnd);
+
+ private static IntPtr _redirectEventHandle = IntPtr.Zero;
+
+ // Do the redirection on another thread, and use a non-blocking
+ // wait method to wait for the redirection to complete.
+ public static void RedirectActivationTo(AppActivationArguments args,
+ AppInstance keyInstance)
+ {
+ _redirectEventHandle = CreateEvent(IntPtr.Zero, true, false, null);
+ _ = Task.Run(() =>
+ {
+ keyInstance.RedirectActivationToAsync(args).AsTask().Wait();
+ _ = SetEvent(_redirectEventHandle);
+ });
+
+ uint cwmoDefault = 0;
+ uint infinite = 0xFFFFFFFF;
+ _ = CoWaitForMultipleObjects(
+ cwmoDefault, infinite, 1,
+ [_redirectEventHandle], out uint handleIndex);
+
+ // Bring the window to the foreground
+ Process process = Process.GetProcessById((int)keyInstance.ProcessId);
+ _ = SetForegroundWindow(process.MainWindowHandle);
+ }
+
+ private static void OnActivated(object sender, AppActivationArguments args)
+ {
+ _ = args.Kind;
+ }
+}
\ No newline at end of file
diff --git a/FluentAutoClicker/Properties/PublishProfiles/win-arm64.pubxml b/FluentAutoClicker/Properties/PublishProfiles/win-arm64.pubxml
index 06da89e..8953cce 100644
--- a/FluentAutoClicker/Properties/PublishProfiles/win-arm64.pubxml
+++ b/FluentAutoClicker/Properties/PublishProfiles/win-arm64.pubxml
@@ -6,14 +6,9 @@ https://go.microsoft.com/fwlink/?LinkID=208121.
FileSystemARM64
- win-arm64
- win10-arm64
+ win-arm64bin\$(Configuration)\$(TargetFramework)\$(RuntimeIdentifier)\publish\trueFalse
- False
- True
- False
- True
\ No newline at end of file
diff --git a/FluentAutoClicker/Properties/PublishProfiles/win-x64.pubxml b/FluentAutoClicker/Properties/PublishProfiles/win-x64.pubxml
index 3568f8d..cd99561 100644
--- a/FluentAutoClicker/Properties/PublishProfiles/win-x64.pubxml
+++ b/FluentAutoClicker/Properties/PublishProfiles/win-x64.pubxml
@@ -6,14 +6,9 @@ https://go.microsoft.com/fwlink/?LinkID=208121.
FileSystemx64
- win-x64
- win10-x64
+ win-x64bin\$(Configuration)\$(TargetFramework)\$(RuntimeIdentifier)\publish\trueFalse
- False
- True
- False
- True
\ No newline at end of file
diff --git a/FluentAutoClicker/Properties/PublishProfiles/win-x86.pubxml b/FluentAutoClicker/Properties/PublishProfiles/win-x86.pubxml
index 4ed30a5..a70c694 100644
--- a/FluentAutoClicker/Properties/PublishProfiles/win-x86.pubxml
+++ b/FluentAutoClicker/Properties/PublishProfiles/win-x86.pubxml
@@ -6,14 +6,9 @@ https://go.microsoft.com/fwlink/?LinkID=208121.
FileSystemx86
- win-x86
- win10-x86
+ win-x86bin\$(Configuration)\$(TargetFramework)\$(RuntimeIdentifier)\publish\trueFalse
- False
- True
- False
- True
\ No newline at end of file
diff --git a/FluentAutoClicker/Strings/en-us/Resources.resw b/FluentAutoClicker/Strings/en-us/Resources.resw
index e75eea6..468f297 100644
--- a/FluentAutoClicker/Strings/en-us/Resources.resw
+++ b/FluentAutoClicker/Strings/en-us/Resources.resw
@@ -1,24 +1,4 @@
-
-
-
-
-
+
+4
- 16
+ 8816,8,16,16