WingMan

Subversion Repositories:
Compare Path: Rev
With Path: Rev
?path1? @ 22  →  ?path2? @ 23
/trunk/WingMan/AutoCompletion/AutoCompletion.cs
@@ -0,0 +1,168 @@
using System;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using Mono.Data.Sqlite;
 
namespace WingMan.AutoCompletion
{
public class AutoCompletion : IDisposable
{
public delegate void LoadFailed(object sender, AutoCompletionFailedEventArgs args);
 
 
public delegate void SaveFailed(object sender, AutoCompletionFailedEventArgs args);
 
public AutoCompletion()
{
}
 
public AutoCompletion(TaskScheduler taskScheduler, CancellationToken cancellationToken) : this()
{
TaskScheduler = taskScheduler;
CancellationToken = cancellationToken;
}
 
private TaskScheduler TaskScheduler { get; }
private CancellationToken CancellationToken { get; }
 
public void Dispose()
{
}
 
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool SetDllDirectory(string lpPathName);
 
public event SaveFailed OnSaveFailed;
 
public event LoadFailed OnLoadFailed;
 
public async Task Save(string name, AutoCompleteStringCollection autoCompleteStringCollection)
{
SetDllDirectory(Environment.Is64BitOperatingSystem ? "x64" : "x86");
 
try
{
using (var sqliteConnection =
new SqliteConnection($"URI=file:{"Autocomplete.db"}"))
{
await sqliteConnection.OpenAsync(CancellationToken);
 
// Create table if it does not exist.
using (var sqliteCommand =
new SqliteCommand($"CREATE TABLE IF NOT EXISTS {name} (data text UNIQUE NOT NULL)",
sqliteConnection))
{
using (var dbtransaction = sqliteConnection.BeginTransaction())
{
try
{
await sqliteCommand.ExecuteReaderAsync(CancellationToken);
 
dbtransaction.Commit();
}
catch
{
dbtransaction.Rollback();
throw;
}
}
}
 
// Add all items to the database.
await Task.Delay(0, CancellationToken).ContinueWith(async _ =>
{
foreach (var sourceItem in autoCompleteStringCollection)
{
var data = sourceItem.ToString();
 
using (var sqliteCommand =
new SqliteCommand($"REPLACE INTO {name} (data) VALUES (:data)",
sqliteConnection))
{
sqliteCommand
.Parameters
.Add(new SqliteParameter("data", data));
 
using (var dbtransaction = sqliteConnection.BeginTransaction())
{
try
{
await sqliteCommand.ExecuteReaderAsync(CancellationToken);
 
dbtransaction.Commit();
}
catch
{
dbtransaction.Rollback();
throw;
}
}
}
}
}, CancellationToken,
TaskContinuationOptions.None, TaskScheduler);
}
}
catch (Exception ex)
{
await Task.Delay(0, CancellationToken).ContinueWith(_ => OnSaveFailed?.Invoke(this,
new AutoCompletionFailedEventArgs(AutoCompletionFailedType.Save, name, ex)), CancellationToken,
TaskContinuationOptions.None, TaskScheduler);
}
}
 
public async Task Load(string name, AutoCompleteStringCollection autoCompleteStringCollection)
{
SetDllDirectory(Environment.Is64BitOperatingSystem ? "x64" : "x86");
 
try
{
using (var sqliteConnection =
new SqliteConnection($"URI=file:{"Autocomplete.db"}"))
{
await sqliteConnection.OpenAsync(CancellationToken);
using (var sqliteCommand =
new SqliteCommand($"SELECT data FROM {name}", sqliteConnection))
{
using (var dbtransaction = sqliteConnection.BeginTransaction())
{
try
{
using (var reader = await sqliteCommand.ExecuteReaderAsync(CancellationToken))
{
while (await reader.ReadAsync(CancellationToken))
for (var i = 0; i < reader.FieldCount; ++i)
{
var value = reader.GetString(i);
if (string.IsNullOrEmpty(value))
continue;
 
await Task.Delay(0, CancellationToken).ContinueWith(
_ => autoCompleteStringCollection.Add(value), CancellationToken,
TaskContinuationOptions.None, TaskScheduler);
}
}
 
dbtransaction.Commit();
}
catch
{
dbtransaction.Rollback();
throw;
}
}
}
}
}
catch (Exception ex)
{
await Task.Delay(0, CancellationToken).ContinueWith(_ => OnLoadFailed?.Invoke(this,
new AutoCompletionFailedEventArgs(AutoCompletionFailedType.Load, name, ex)), CancellationToken,
TaskContinuationOptions.None, TaskScheduler);
}
}
}
}
/trunk/WingMan/AutoCompletion/AutoCompletionFailedEventArgs.cs
@@ -0,0 +1,18 @@
using System;
 
namespace WingMan.AutoCompletion
{
public class AutoCompletionFailedEventArgs : EventArgs
{
public AutoCompletionFailedEventArgs(AutoCompletionFailedType type, string name, Exception ex)
{
Type = type;
Name = name;
Exception = ex;
}
 
public AutoCompletionFailedType Type { get; set; }
public Exception Exception { get; set; }
public string Name { get; set; }
}
}
/trunk/WingMan/AutoCompletion/AutoCompletionFailedType.cs
@@ -0,0 +1,8 @@
namespace WingMan.AutoCompletion
{
public enum AutoCompletionFailedType
{
Load,
Save
}
}
/trunk/WingMan/Bindings/KeySimulator.cs
@@ -91,7 +91,7 @@
if (!KeyConversion.StringToKeys.TryGetValue(key, out var press))
continue;
 
InputSimulator.Keyboard.KeyUp((VirtualKeyCode)press);
InputSimulator.Keyboard.KeyUp((VirtualKeyCode) press);
}
}
}
/trunk/WingMan/Properties/Strings.Designer.cs
@@ -151,6 +151,15 @@
}
/// <summary>
/// Looks up a localized string similar to Failed loading autocomplete source.
/// </summary>
internal static string Failed_loading_autocomplete_source {
get {
return ResourceManager.GetString("Failed_loading_autocomplete_source", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Failed loading local bindings.
/// </summary>
internal static string Failed_loading_local_bindings {
@@ -169,6 +178,15 @@
}
/// <summary>
/// Looks up a localized string similar to Failed saving autocomplete source.
/// </summary>
internal static string Failed_saving_autocomplete_source {
get {
return ResourceManager.GetString("Failed_saving_autocomplete_source", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Failed saving local bindings.
/// </summary>
internal static string Failed_saving_local_bindings {
/trunk/WingMan/Properties/Strings.resx
@@ -147,6 +147,9 @@
<data name="Executing_binding_from_remote_client" xml:space="preserve">
<value>Executing binding from remote client</value>
</data>
<data name="Failed_loading_autocomplete_source" xml:space="preserve">
<value>Failed loading autocomplete source</value>
</data>
<data name="Failed_loading_local_bindings" xml:space="preserve">
<value>Failed loading local bindings</value>
</data>
@@ -153,6 +156,9 @@
<data name="Failed_loading_remote_bindings" xml:space="preserve">
<value>Failed loading remote bindings</value>
</data>
<data name="Failed_saving_autocomplete_source" xml:space="preserve">
<value>Failed saving autocomplete source</value>
</data>
<data name="Failed_saving_local_bindings" xml:space="preserve">
<value>Failed saving local bindings</value>
</data>
/trunk/WingMan/WingMan.csproj
@@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="..\packages\SQLite.Native.3.12.3\build\net45\SQLite.Native.props" Condition="Exists('..\packages\SQLite.Native.3.12.3\build\net45\SQLite.Native.props')" />
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
@@ -23,6 +24,7 @@
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
@@ -40,6 +42,9 @@
<Reference Include="Gma.System.MouseKeyHook, Version=5.6.130.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\MouseKeyHook.5.6.0\lib\net40\Gma.System.MouseKeyHook.dll</HintPath>
</Reference>
<Reference Include="Mono.Data.Sqlite, Version=2.0.0.0, Culture=neutral, PublicKeyToken=0738eb9f132ed756, processorArchitecture=MSIL">
<HintPath>..\packages\Mono.Data.Sqlite.Portable.1.0.3.5\lib\net4\Mono.Data.Sqlite.dll</HintPath>
</Reference>
<Reference Include="MQTTnet, Version=2.8.4.0, Culture=neutral, PublicKeyToken=b69712f52770c0a7, processorArchitecture=MSIL">
<HintPath>..\packages\MQTTnet.2.8.4\lib\net452\MQTTnet.dll</HintPath>
</Reference>
@@ -51,9 +56,16 @@
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Data.Portable, Version=4.0.0.0, Culture=neutral, PublicKeyToken=59e704a76bc4613a, processorArchitecture=MSIL">
<HintPath>..\packages\Mono.Data.Sqlite.Portable.1.0.3.5\lib\net4\System.Data.Portable.dll</HintPath>
</Reference>
<Reference Include="System.Threading.Tasks.Dataflow, Version=4.5.24.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Tpl.Dataflow.4.5.24\lib\portable-net45+win8+wpa81\System.Threading.Tasks.Dataflow.dll</HintPath>
</Reference>
<Reference Include="System.Transactions" />
<Reference Include="System.Transactions.Portable, Version=4.0.0.0, Culture=neutral, PublicKeyToken=59e704a76bc4613a, processorArchitecture=MSIL">
<HintPath>..\packages\Mono.Data.Sqlite.Portable.1.0.3.5\lib\net4\System.Transactions.Portable.dll</HintPath>
</Reference>
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
@@ -68,6 +80,9 @@
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="AutoCompletion\AutoCompletion.cs" />
<Compile Include="AutoCompletion\AutoCompletionFailedEventArgs.cs" />
<Compile Include="AutoCompletion\AutoCompletionFailedType.cs" />
<Compile Include="Communication\MqttAuthenticationFailureEventArgs.cs" />
<Compile Include="Lobby\LobbyMessageReceivedEventArgs.cs" />
<Compile Include="Lobby\LobbyMessageSynchronizer.cs" />
@@ -140,4 +155,15 @@
<Content Include="wingman.ico" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Import Project="..\packages\Mono.Data.Sqlite.Portable.1.0.3.5\tools\Mono.Data.Sqlite.Portable.targets" Condition="Exists('..\packages\Mono.Data.Sqlite.Portable.1.0.3.5\tools\Mono.Data.Sqlite.Portable.targets')" />
<Target Name="EnsureMonoDataSqlitePortableImported" BeforeTargets="BeforeBuild" Condition="'$(MonoDataSqlitePortableImported)' == ''">
<Error Condition="!Exists('..\packages\Mono.Data.Sqlite.Portable.1.0.3.5\tools\Mono.Data.Sqlite.Portable.targets')" Text="This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them." />
<Error Condition="Exists('..\packages\Mono.Data.Sqlite.Portable.1.0.3.5\tools\Mono.Data.Sqlite.Portable.targets')" Text="The build restored NuGet packages. Build the project again to include these packages in the build." />
</Target>
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('..\packages\SQLite.Native.3.12.3\build\net45\SQLite.Native.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\SQLite.Native.3.12.3\build\net45\SQLite.Native.props'))" />
</Target>
</Project>
/trunk/WingMan/WingManForm.Designer.cs
@@ -273,7 +273,7 @@
// label6
//
this.label6.AutoSize = true;
this.label6.Location = new System.Drawing.Point(279, 20);
this.label6.Location = new System.Drawing.Point(287, 20);
this.label6.Name = "label6";
this.label6.Size = new System.Drawing.Size(53, 13);
this.label6.TabIndex = 10;
@@ -282,10 +282,10 @@
// Password
//
this.Password.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
this.Password.Location = new System.Drawing.Point(336, 16);
this.Password.Location = new System.Drawing.Point(344, 16);
this.Password.Name = "Password";
this.Password.PasswordChar = '*';
this.Password.Size = new System.Drawing.Size(104, 20);
this.Password.Size = new System.Drawing.Size(152, 20);
this.Password.TabIndex = 9;
//
// label5
@@ -299,6 +299,8 @@
//
// Nick
//
this.Nick.AutoCompleteMode = System.Windows.Forms.AutoCompleteMode.Suggest;
this.Nick.AutoCompleteSource = System.Windows.Forms.AutoCompleteSource.CustomSource;
this.Nick.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
this.Nick.Location = new System.Drawing.Point(152, 48);
this.Nick.Name = "Nick";
@@ -319,7 +321,7 @@
// label4
//
this.label4.AutoSize = true;
this.label4.Location = new System.Drawing.Point(185, 20);
this.label4.Location = new System.Drawing.Point(194, 20);
this.label4.Name = "label4";
this.label4.Size = new System.Drawing.Size(26, 13);
this.label4.TabIndex = 3;
@@ -338,8 +340,10 @@
//
// Port
//
this.Port.AutoCompleteMode = System.Windows.Forms.AutoCompleteMode.Suggest;
this.Port.AutoCompleteSource = System.Windows.Forms.AutoCompleteSource.CustomSource;
this.Port.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
this.Port.Location = new System.Drawing.Point(215, 16);
this.Port.Location = new System.Drawing.Point(224, 16);
this.Port.Name = "Port";
this.Port.Size = new System.Drawing.Size(60, 20);
this.Port.TabIndex = 2;
@@ -349,7 +353,7 @@
// label3
//
this.label3.AutoSize = true;
this.label3.Location = new System.Drawing.Point(40, 20);
this.label3.Location = new System.Drawing.Point(8, 20);
this.label3.Name = "label3";
this.label3.Size = new System.Drawing.Size(29, 13);
this.label3.TabIndex = 1;
@@ -357,10 +361,12 @@
//
// Address
//
this.Address.AutoCompleteMode = System.Windows.Forms.AutoCompleteMode.Suggest;
this.Address.AutoCompleteSource = System.Windows.Forms.AutoCompleteSource.CustomSource;
this.Address.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
this.Address.Location = new System.Drawing.Point(76, 16);
this.Address.Location = new System.Drawing.Point(40, 16);
this.Address.Name = "Address";
this.Address.Size = new System.Drawing.Size(104, 20);
this.Address.Size = new System.Drawing.Size(152, 20);
this.Address.TabIndex = 0;
this.Address.Text = "0.0.0.0";
this.Address.Click += new System.EventHandler(this.AddressTextBoxClick);
/trunk/WingMan/WingManForm.cs
@@ -11,6 +11,7 @@
using Gma.System.MouseKeyHook;
using MQTTnet.Extensions.ManagedClient;
using MQTTnet.Server;
using WingMan.AutoCompletion;
using WingMan.Bindings;
using WingMan.Communication;
using WingMan.Lobby;
@@ -28,6 +29,15 @@
FormTaskScheduler = TaskScheduler.FromCurrentSynchronizationContext();
FormCancellationTokenSource = new CancellationTokenSource();
 
// Set up autocompletion.
AutoCompletion = new AutoCompletion.AutoCompletion(FormTaskScheduler, FormCancellationTokenSource.Token);
AutoCompletion.OnSaveFailed += AutoCompletionOnSaveFailed;
AutoCompletion.OnLoadFailed += AutoCompletionOnLoadFailed;
 
Task.Run(() => AutoCompletion.Load(Address.Name, Address.AutoCompleteCustomSource));
Task.Run(() => AutoCompletion.Load(Port.Name, Address.AutoCompleteCustomSource));
Task.Run(() => AutoCompletion.Load(Nick.Name, Nick.AutoCompleteCustomSource));
 
MqttCommunication = new MqttCommunication(FormTaskScheduler, FormCancellationTokenSource.Token);
MqttCommunication.OnClientAuthenticationFailed += OnMqttClientAuthenticationFailed;
MqttCommunication.OnClientConnectionFailed += OnMqttClientConnectionFailed;
@@ -84,6 +94,7 @@
KeySimulator.OnMouseKeyBindingExecuting += OnMouseKeyBindingExecuting;
}
 
private static AutoCompletion.AutoCompletion AutoCompletion { get; set; }
private static CancellationTokenSource FormCancellationTokenSource { get; set; }
 
private static TaskScheduler FormTaskScheduler { get; set; }
@@ -112,6 +123,18 @@
 
public KeySimulator KeySimulator { get; set; }
 
private void AutoCompletionOnLoadFailed(object sender, AutoCompletionFailedEventArgs args)
{
ActivityTextBox.AppendText(
$"{Strings.Failed_loading_autocomplete_source} : {args.Name} : {args.Exception.Message}{Environment.NewLine}");
}
 
private void AutoCompletionOnSaveFailed(object sender, AutoCompletionFailedEventArgs args)
{
ActivityTextBox.AppendText(
$"{Strings.Failed_saving_autocomplete_source} : {args.Name} : {args.Exception.Message}{Environment.NewLine}");
}
 
/// <inheritdoc />
/// <summary>
/// Clean up any resources being used.
@@ -292,6 +315,8 @@
if (!ValidateConnectionParameters(out var ipAddress, out var port, out var nick, out var password))
return;
 
StoreConnectionAutocomplete();
 
// Start the MQTT server.
if (!await MqttCommunication
.Start(MqttCommunicationType.Server, ipAddress, port, nick, password))
@@ -311,6 +336,21 @@
Password.Enabled = false;
}
 
private async void StoreConnectionAutocomplete()
{
Address.AutoCompleteCustomSource.Add(Address.Text);
 
await AutoCompletion.Save(Address.Name, Address.AutoCompleteCustomSource);
 
Port.AutoCompleteCustomSource.Add(Port.Text);
 
await AutoCompletion.Save(Port.Name, Port.AutoCompleteCustomSource);
 
Nick.AutoCompleteCustomSource.Add(Nick.Text);
 
await AutoCompletion.Save(Nick.Name, Nick.AutoCompleteCustomSource);
}
 
private bool ValidateConnectionParameters(
out IPAddress address,
out int port,
@@ -399,6 +439,8 @@
if (!ValidateConnectionParameters(out var ipAddress, out var port, out var nick, out var password))
return;
 
StoreConnectionAutocomplete();
 
if (!await MqttCommunication
.Start(MqttCommunicationType.Client, ipAddress, port, nick, password))
{
@@ -620,6 +662,22 @@
}
}
 
private void WingManFormResized(object sender, EventArgs e)
{
if (WindowState == FormWindowState.Minimized)
{
Hide();
notifyIcon1.Visible = true;
}
}
 
private void NotifyIconDoubleClick(object sender, EventArgs e)
{
Show();
WindowState = FormWindowState.Normal;
notifyIcon1.Visible = false;
}
 
#region Saving and loading
 
private async Task SaveLocalMouseKeyBindings()
@@ -725,21 +783,5 @@
}
 
#endregion
 
private void WingManFormResized(object sender, EventArgs e)
{
if (WindowState == FormWindowState.Minimized)
{
Hide();
notifyIcon1.Visible = true;
}
}
 
private void NotifyIconDoubleClick(object sender, EventArgs e)
{
Show();
WindowState = FormWindowState.Normal;
notifyIcon1.Visible = false;
}
}
}
/trunk/WingMan/packages.config
@@ -3,8 +3,10 @@
<packages>
<package id="InputSimulatorPlus" version="1.0.7" targetFramework="net452" />
<package id="Microsoft.Tpl.Dataflow" version="4.5.24" targetFramework="net452" />
<package id="Mono.Data.Sqlite.Portable" version="1.0.3.5" targetFramework="net452" />
<package id="MouseKeyHook" version="5.6.0" targetFramework="net452" />
<package id="MQTTnet" version="2.8.4" targetFramework="net452" />
<package id="MQTTnet.Extensions.ManagedClient" version="2.8.4" targetFramework="net452" />
<package id="Open.NAT" version="2.1.0.0" targetFramework="net452" />
<package id="SQLite.Native" version="3.12.3" targetFramework="net452" />
</packages>