Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
335 changes: 305 additions & 30 deletions .gitignore

Large diffs are not rendered by default.

6 changes: 6 additions & 0 deletions Directory.Build.props
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<Project>
<PropertyGroup>
<Nullable>enable</Nullable>
<AvaloniaVersion>11.1.0</AvaloniaVersion>
</PropertyGroup>
</Project>
74 changes: 47 additions & 27 deletions MouseJiggler/Helpers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
//
// Created by: Alistair J R Young (avatar) at 2021/01/20 7:40 PM.
// Updates by: Dimitris Panokostas (midwan)
// Updates by: Dennis

#endregion

Expand All @@ -12,8 +13,6 @@
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using Windows.Win32;
using Windows.Win32.UI.Input.KeyboardAndMouse;

#endregion

Expand All @@ -30,40 +29,61 @@ internal static class Helpers

#endregion Console management

#region Jiggling

/// <summary>
/// Jiggle the mouse; i.e., fake a mouse movement event.
/// </summary>
/// <param name="delta">The mouse will be moved by delta pixels along both X and Y.</param>
[DllImport("user32.dll", SetLastError = true)]
private static extern uint SendInput(uint nInputs, INPUT[] pInputs, int cbSize);

[StructLayout(LayoutKind.Sequential)]
internal struct INPUT
{
public uint type;
public MOUSEINPUT mi;
}

[StructLayout(LayoutKind.Sequential)]
internal struct MOUSEINPUT
{
public int dx;
public int dy;
public uint mouseData;
public uint dwFlags;
public uint time;
public IntPtr dwExtraInfo;
}

internal static class INPUT_TYPE
{
public const uint INPUT_MOUSE = 0;
}

internal static class MOUSE_EVENT_FLAGS
{
public const uint MOUSEEVENTF_MOVE = 0x0001;
}

internal static void Jiggle(int delta)
{
var inp = new INPUT
var input = new INPUT
{
type = INPUT_TYPE.INPUT_MOUSE,
Anonymous = new INPUT._Anonymous_e__Union
{
type = INPUT_TYPE.INPUT_MOUSE,
mi = new MOUSEINPUT
{
dx = delta,
dy = delta,
mouseData = 0,
dwFlags = MOUSE_EVENT_FLAGS.MOUSEEVENTF_MOVE,
time = 0,
dwExtraInfo = 0
dx = delta,
dy = delta,
mouseData = 0,
dwFlags = MOUSE_EVENT_FLAGS.MOUSEEVENTF_MOVE,
time = 0,
dwExtraInfo = IntPtr.Zero
}
}
};

var returnValue = PInvoke.SendInput(new ReadOnlySpan<INPUT>(in inp), Marshal.SizeOf<INPUT>());

if (returnValue == 1) return;
var errorCode = Marshal.GetLastWin32Error();
var inputs = new INPUT[] { input };
var result = SendInput((uint)inputs.Length, inputs, Marshal.SizeOf<INPUT>());

Debugger.Log(1,
"Jiggle",
$"failed to insert event to input stream; retval={returnValue}, errcode=0x{errorCode:x8}\n");
if (result == 0)
{
var errorCode = Marshal.GetLastWin32Error();
Debugger.Log(1, "Jiggle", $"SendInput failed: error code 0x{errorCode:X8}\n");
}
}

#endregion Jiggling
}
6 changes: 3 additions & 3 deletions MouseJiggler/MouseJiggler.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -52,13 +52,13 @@ Command line options:

<ItemGroup>
<PackageReference Include="JetBrains.Annotations" Version="2024.3.0" />
<PackageReference Include="Microsoft.Windows.CsWin32" Version="0.3.162">
<PackageReference Include="Microsoft.Windows.CsWin32" Version="0.3.183">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="System.CommandLine" Version="2.0.0-beta1.20574.7" />
<PackageReference Include="System.Memory" Version="4.6.0" />
<PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="6.1.0" />
<PackageReference Include="System.Memory" Version="4.6.3" />
<PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="6.1.2" />
</ItemGroup>

<ItemGroup>
Expand Down
16 changes: 14 additions & 2 deletions MouseJiggler/MouseJiggler.sln
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.30804.86
# Visual Studio Version 17
VisualStudioVersion = 17.13.35919.96 d17.13
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MouseJiggler", "MouseJiggler.csproj", "{C5CC87B3-C8D5-474B-A97A-2B6B1316FD5B}"
EndProject
Expand All @@ -10,6 +10,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
..\README.md = ..\README.md
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MouseJigglerAvalonia", "..\MouseJigglerAvalonia\MouseJigglerAvalonia.csproj", "{1558C4B3-9652-DB6C-C5E6-EF91B7B66049}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MouseJigglerAvalonia.Desktop", "..\MouseJigglerAvalonia.Desktop\MouseJigglerAvalonia.Desktop.csproj", "{1DC3C492-2F9B-C870-9CC6-FDF18725B99B}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand All @@ -20,6 +24,14 @@ Global
{C5CC87B3-C8D5-474B-A97A-2B6B1316FD5B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C5CC87B3-C8D5-474B-A97A-2B6B1316FD5B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C5CC87B3-C8D5-474B-A97A-2B6B1316FD5B}.Release|Any CPU.Build.0 = Release|Any CPU
{1558C4B3-9652-DB6C-C5E6-EF91B7B66049}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{1558C4B3-9652-DB6C-C5E6-EF91B7B66049}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1558C4B3-9652-DB6C-C5E6-EF91B7B66049}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1558C4B3-9652-DB6C-C5E6-EF91B7B66049}.Release|Any CPU.Build.0 = Release|Any CPU
{1DC3C492-2F9B-C870-9CC6-FDF18725B99B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{1DC3C492-2F9B-C870-9CC6-FDF18725B99B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1DC3C492-2F9B-C870-9CC6-FDF18725B99B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1DC3C492-2F9B-C870-9CC6-FDF18725B99B}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down
19 changes: 19 additions & 0 deletions MouseJigglerAvalonia.Desktop/MouseJigglerAvalonia.Desktop.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>WinExe</OutputType>
<!--If you are willing to use Windows/MacOS native APIs you will need to create 3 projects.
One for Windows with net8.0-windows TFM, one for MacOS with net8.0-macos and one with net8.0 TFM for Linux.-->
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<BuiltInComInteropSupport>true</BuiltInComInteropSupport>
<ApplicationManifest>app.manifest</ApplicationManifest>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Avalonia.Desktop" Version="$(AvaloniaVersion)" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\MouseJigglerAvalonia\MouseJigglerAvalonia.csproj" />
</ItemGroup>
</Project>
23 changes: 23 additions & 0 deletions MouseJigglerAvalonia.Desktop/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
using System;

using Avalonia;

namespace MouseJigglerAvalonia.Desktop;

class Program
{
// Initialization code. Don't use any Avalonia, third-party APIs or any
// SynchronizationContext-reliant code before AppMain is called: things aren't initialized
// yet and stuff might break.
[STAThread]
public static void Main(string[] args) => BuildAvaloniaApp()
.StartWithClassicDesktopLifetime(args);

// Avalonia configuration, don't remove; also used by visual designer.
public static AppBuilder BuildAvaloniaApp()
=> AppBuilder.Configure<App>()
.UsePlatformDetect()
.WithInterFont()
.LogToTrace();

}
18 changes: 18 additions & 0 deletions MouseJigglerAvalonia.Desktop/app.manifest
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
<!-- This manifest is used on Windows only.
Don't remove it as it might cause problems with window transparency and embeded controls.
For more details visit https://learn.microsoft.com/en-us/windows/win32/sbscs/application-manifests -->
<assemblyIdentity version="1.0.0.0" name="AvaloniaTest.Desktop"/>

<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
<application>
<!-- A list of the Windows versions that this application has been tested on
and is designed to work with. Uncomment the appropriate elements
and Windows will automatically select the most compatible environment. -->

<!-- Windows 10 -->
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />
</application>
</compatibility>
</assembly>
10 changes: 10 additions & 0 deletions MouseJigglerAvalonia/App.axaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<Application xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="MouseJigglerAvalonia.App"
RequestedThemeVariant="Default">
<!-- "Default" ThemeVariant follows system theme variant. "Dark" or "Light" are other available options. -->

<Application.Styles>
<FluentTheme />
</Application.Styles>
</Application>
41 changes: 41 additions & 0 deletions MouseJigglerAvalonia/App.axaml.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
using Avalonia;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Data.Core.Plugins;
using Avalonia.Markup.Xaml;

using MouseJigglerAvalonia.ViewModels;
using MouseJigglerAvalonia.Views;

namespace MouseJigglerAvalonia;

public partial class App : Application
{
public override void Initialize()
{
AvaloniaXamlLoader.Load(this);
}

public override void OnFrameworkInitializationCompleted()
{
// Line below is needed to remove Avalonia data validation.
// Without this line you will get duplicate validations from both Avalonia and CT
BindingPlugins.DataValidators.RemoveAt(0);

if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
{
desktop.MainWindow = new MainWindow
{
DataContext = new MainViewModel()
};
}
else if (ApplicationLifetime is ISingleViewApplicationLifetime singleViewPlatform)
{
singleViewPlatform.MainView = new MainView
{
DataContext = new MainViewModel()
};
}

base.OnFrameworkInitializationCompleted();
}
}
Binary file added MouseJigglerAvalonia/Assets/avalonia-logo.ico
Binary file not shown.
21 changes: 21 additions & 0 deletions MouseJigglerAvalonia/MouseJigglerAvalonia.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<LangVersion>latest</LangVersion>
</PropertyGroup>

<ItemGroup>
<AvaloniaResource Include="Assets\**" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="Avalonia" Version="$(AvaloniaVersion)" />
<PackageReference Include="Avalonia.Themes.Fluent" Version="$(AvaloniaVersion)" />
<PackageReference Include="Avalonia.Fonts.Inter" Version="$(AvaloniaVersion)" />
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.2.0" />

<!--Condition below is needed to remove Avalonia.Diagnostics package from build output in Release configuration.-->
<PackageReference Condition="'$(Configuration)' == 'Debug'" Include="Avalonia.Diagnostics" Version="$(AvaloniaVersion)" />
</ItemGroup>
</Project>
6 changes: 6 additions & 0 deletions MouseJigglerAvalonia/ViewModels/MainViewModel.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
namespace MouseJigglerAvalonia.ViewModels;

public partial class MainViewModel : ViewModelBase
{
public string Greeting => "Welcome to Avalonia!";
}
7 changes: 7 additions & 0 deletions MouseJigglerAvalonia/ViewModels/ViewModelBase.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
using CommunityToolkit.Mvvm.ComponentModel;

namespace MouseJigglerAvalonia.ViewModels;

public class ViewModelBase : ObservableObject
{
}
27 changes: 27 additions & 0 deletions MouseJigglerAvalonia/Views/MainView.axaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:vm="clr-namespace:MouseJigglerAvalonia.ViewModels"
mc:Ignorable="d"
d:DesignWidth="320" d:DesignHeight="199"
Width="320" Height="199"
MinWidth="320" MinHeight="199"
MaxWidth="320" MaxHeight="199"
x:Class="MouseJigglerAvalonia.Views.MainView"
x:DataType="vm:MainViewModel">
<Design.DataContext>
<!-- This only sets the DataContext for the previewer in an IDE,
to set the actual DataContext for runtime, set the DataContext property in code (look at App.axaml.cs) -->
<vm:MainViewModel />
</Design.DataContext>
<StackPanel Orientation="Vertical">
<StackPanel Orientation="Horizontal">
<CheckBox Content="Jiggling?"/>
<CheckBox Content="Settings..."/>
<Button Content="?"/>
<Button Content="🔽"/>
<!--<TextBlock Text="{Binding Greeting}" HorizontalAlignment="Center" VerticalAlignment="Center"/> -->
</StackPanel>
</StackPanel>
</UserControl>
11 changes: 11 additions & 0 deletions MouseJigglerAvalonia/Views/MainView.axaml.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
using Avalonia.Controls;

namespace MouseJigglerAvalonia.Views;

public partial class MainView : UserControl
{
public MainView()
{
InitializeComponent();
}
}
12 changes: 12 additions & 0 deletions MouseJigglerAvalonia/Views/MainWindow.axaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<Window xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vm="using:MouseJigglerAvalonia.ViewModels"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:views="clr-namespace:MouseJigglerAvalonia.Views"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="MouseJigglerAvalonia.Views.MainWindow"
Icon="/Assets/avalonia-logo.ico"
Title="MouseJigglerAvalonia">
<views:MainView />
</Window>
11 changes: 11 additions & 0 deletions MouseJigglerAvalonia/Views/MainWindow.axaml.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
using Avalonia.Controls;

namespace MouseJigglerAvalonia.Views;

public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
}