Compare commits
20 Commits
458a466c15
...
master
Author | SHA1 | Date | |
---|---|---|---|
15bee9bf62 | |||
32758cf062 | |||
f4524161ac | |||
12ac48aa76 | |||
62f92708c2 | |||
88005c2125 | |||
df936e187d | |||
833355cd60 | |||
0b7aaa2f99 | |||
274a75b93d | |||
3b038b0fc9 | |||
69fd02e331 | |||
87bd2132e5 | |||
1cdfba0b14 | |||
0e3f2005d1 | |||
c4e9007f2b | |||
1f314d5c4b | |||
f234279135 | |||
baeadba638 | |||
bad2313c18 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -2,3 +2,4 @@
|
|||||||
obj
|
obj
|
||||||
bin
|
bin
|
||||||
.vs
|
.vs
|
||||||
|
packages
|
||||||
|
@@ -1,6 +1,14 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<configuration>
|
<configuration>
|
||||||
<startup>
|
<startup>
|
||||||
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.8" />
|
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.8" />
|
||||||
</startup>
|
</startup>
|
||||||
|
<runtime>
|
||||||
|
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
|
||||||
|
<dependentAssembly>
|
||||||
|
<assemblyIdentity name="System.Runtime.CompilerServices.Unsafe" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
|
||||||
|
<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" />
|
||||||
|
</dependentAssembly>
|
||||||
|
</assemblyBinding>
|
||||||
|
</runtime>
|
||||||
</configuration>
|
</configuration>
|
||||||
|
@@ -37,8 +37,39 @@
|
|||||||
<Prefer32bit>false</Prefer32bit>
|
<Prefer32bit>false</Prefer32bit>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<Reference Include="Microsoft.Bcl.AsyncInterfaces, Version=9.0.0.8, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\packages\Microsoft.Bcl.AsyncInterfaces.9.0.8\lib\net462\Microsoft.Bcl.AsyncInterfaces.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
<Reference Include="System" />
|
<Reference Include="System" />
|
||||||
|
<Reference Include="System.Buffers, Version=4.0.3.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\packages\System.Buffers.4.5.1\lib\net461\System.Buffers.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
<Reference Include="System.Core" />
|
<Reference Include="System.Core" />
|
||||||
|
<Reference Include="System.IO.Pipelines, Version=9.0.0.8, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\packages\System.IO.Pipelines.9.0.8\lib\net462\System.IO.Pipelines.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.Memory, Version=4.0.1.2, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\packages\System.Memory.4.5.5\lib\net461\System.Memory.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.Numerics" />
|
||||||
|
<Reference Include="System.Numerics.Vectors, Version=4.1.4.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\packages\System.Numerics.Vectors.4.5.0\lib\net46\System.Numerics.Vectors.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.Runtime.CompilerServices.Unsafe, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\packages\System.Runtime.CompilerServices.Unsafe.6.0.0\lib\net461\System.Runtime.CompilerServices.Unsafe.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.Text.Encodings.Web, Version=9.0.0.8, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\packages\System.Text.Encodings.Web.9.0.8\lib\net462\System.Text.Encodings.Web.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.Text.Json, Version=9.0.0.8, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\packages\System.Text.Json.9.0.8\lib\net462\System.Text.Json.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.Threading.Tasks.Extensions, Version=4.2.0.1, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\packages\System.Threading.Tasks.Extensions.4.5.4\lib\net461\System.Threading.Tasks.Extensions.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.ValueTuple, Version=4.0.3.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\packages\System.ValueTuple.4.5.0\lib\net47\System.ValueTuple.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
<Reference Include="System.Xml.Linq" />
|
<Reference Include="System.Xml.Linq" />
|
||||||
<Reference Include="System.Data.DataSetExtensions" />
|
<Reference Include="System.Data.DataSetExtensions" />
|
||||||
<Reference Include="Microsoft.CSharp" />
|
<Reference Include="Microsoft.CSharp" />
|
||||||
@@ -56,6 +87,15 @@
|
|||||||
<Compile Include="Form1.Designer.cs">
|
<Compile Include="Form1.Designer.cs">
|
||||||
<DependentUpon>Form1.cs</DependentUpon>
|
<DependentUpon>Form1.cs</DependentUpon>
|
||||||
</Compile>
|
</Compile>
|
||||||
|
<Compile Include="SettingsForm.cs">
|
||||||
|
<SubType>Form</SubType>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="WindowPanelForm.cs">
|
||||||
|
<SubType>UserControl</SubType>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="WindowPanelForm.Designer.cs">
|
||||||
|
<DependentUpon>WindowPanelForm.cs</DependentUpon>
|
||||||
|
</Compile>
|
||||||
<Compile Include="HotKeyManager.cs" />
|
<Compile Include="HotKeyManager.cs" />
|
||||||
<Compile Include="KeyboardHook.cs" />
|
<Compile Include="KeyboardHook.cs" />
|
||||||
<Compile Include="Program.cs" />
|
<Compile Include="Program.cs" />
|
||||||
@@ -69,6 +109,13 @@
|
|||||||
<AutoGen>True</AutoGen>
|
<AutoGen>True</AutoGen>
|
||||||
<DependentUpon>Resources.resx</DependentUpon>
|
<DependentUpon>Resources.resx</DependentUpon>
|
||||||
</Compile>
|
</Compile>
|
||||||
|
<EmbeddedResource Include="SettingsForm.resx">
|
||||||
|
<DependentUpon>SettingsForm.cs</DependentUpon>
|
||||||
|
</EmbeddedResource>
|
||||||
|
<EmbeddedResource Include="WindowPanelForm.resx">
|
||||||
|
<DependentUpon>WindowPanelForm.cs</DependentUpon>
|
||||||
|
</EmbeddedResource>
|
||||||
|
<None Include="packages.config" />
|
||||||
<None Include="Properties\Settings.settings">
|
<None Include="Properties\Settings.settings">
|
||||||
<Generator>SettingsSingleFileGenerator</Generator>
|
<Generator>SettingsSingleFileGenerator</Generator>
|
||||||
<LastGenOutput>Settings.Designer.cs</LastGenOutput>
|
<LastGenOutput>Settings.Designer.cs</LastGenOutput>
|
||||||
@@ -84,6 +131,9 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Content Include="beep.wav" />
|
<Content Include="beep.wav" />
|
||||||
|
<Content Include="app.ico">
|
||||||
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
|
</Content>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||||
</Project>
|
</Project>
|
58
DD2Switcher/Form1.Designer.cs
generated
58
DD2Switcher/Form1.Designer.cs
generated
@@ -26,11 +26,67 @@
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private void InitializeComponent() {
|
private void InitializeComponent() {
|
||||||
this.components = new System.ComponentModel.Container();
|
this.components = new System.ComponentModel.Container();
|
||||||
|
this.notifyIcon = new System.Windows.Forms.NotifyIcon(this.components);
|
||||||
|
this.trayMenu = new System.Windows.Forms.ContextMenuStrip(this.components);
|
||||||
|
this.settingsMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||||
|
this.separatorMenuItem = new System.Windows.Forms.ToolStripSeparator();
|
||||||
|
this.exitMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||||
|
this.trayMenu.SuspendLayout();
|
||||||
|
this.SuspendLayout();
|
||||||
|
//
|
||||||
|
// notifyIcon
|
||||||
|
//
|
||||||
|
this.notifyIcon.ContextMenuStrip = this.trayMenu;
|
||||||
|
this.notifyIcon.Text = "DD2Switcher";
|
||||||
|
this.notifyIcon.Visible = true;
|
||||||
|
this.notifyIcon.MouseDoubleClick +=
|
||||||
|
new System.Windows.Forms.MouseEventHandler(this.notifyIcon_MouseDoubleClick);
|
||||||
|
//
|
||||||
|
// trayMenu
|
||||||
|
//
|
||||||
|
this.trayMenu.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
|
||||||
|
this.settingsMenuItem, this.separatorMenuItem, this.exitMenuItem
|
||||||
|
});
|
||||||
|
this.trayMenu.Name = "trayMenu";
|
||||||
|
this.trayMenu.Size = new System.Drawing.Size(120, 54);
|
||||||
|
//
|
||||||
|
// settingsMenuItem
|
||||||
|
//
|
||||||
|
this.settingsMenuItem.Name = "settingsMenuItem";
|
||||||
|
this.settingsMenuItem.Size = new System.Drawing.Size(119, 22);
|
||||||
|
this.settingsMenuItem.Text = "Settings";
|
||||||
|
this.settingsMenuItem.Click += new System.EventHandler(this.settingsMenuItem_Click);
|
||||||
|
//
|
||||||
|
// separatorMenuItem
|
||||||
|
//
|
||||||
|
this.separatorMenuItem.Name = "separatorMenuItem";
|
||||||
|
this.separatorMenuItem.Size = new System.Drawing.Size(116, 6);
|
||||||
|
//
|
||||||
|
// exitMenuItem
|
||||||
|
//
|
||||||
|
this.exitMenuItem.Name = "exitMenuItem";
|
||||||
|
this.exitMenuItem.Size = new System.Drawing.Size(119, 22);
|
||||||
|
this.exitMenuItem.Text = "Exit";
|
||||||
|
this.exitMenuItem.Click += new System.EventHandler(this.exitMenuItem_Click);
|
||||||
|
//
|
||||||
|
// Form1
|
||||||
|
//
|
||||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||||
this.ClientSize = new System.Drawing.Size(800, 450);
|
this.ClientSize = new System.Drawing.Size(800, 450);
|
||||||
this.Text = "Form1";
|
this.Text = "DD2Switcher";
|
||||||
|
this.WindowState = System.Windows.Forms.FormWindowState.Minimized;
|
||||||
|
this.ShowInTaskbar = false;
|
||||||
|
this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.Form1_FormClosing);
|
||||||
|
this.trayMenu.ResumeLayout(false);
|
||||||
|
this.ResumeLayout(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
private System.Windows.Forms.NotifyIcon notifyIcon;
|
||||||
|
private System.Windows.Forms.ContextMenuStrip trayMenu;
|
||||||
|
private System.Windows.Forms.ToolStripMenuItem settingsMenuItem;
|
||||||
|
private System.Windows.Forms.ToolStripSeparator separatorMenuItem;
|
||||||
|
private System.Windows.Forms.ToolStripMenuItem exitMenuItem;
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -1,9 +1,60 @@
|
|||||||
using System.Windows.Forms;
|
using System;
|
||||||
|
using System.Windows.Forms;
|
||||||
|
|
||||||
namespace DD2Switcher {
|
namespace DD2Switcher {
|
||||||
public partial class Form1 : Form {
|
public partial class Form1 : Form {
|
||||||
|
private SettingsForm settingsForm;
|
||||||
|
|
||||||
public Form1() {
|
public Form1() {
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
|
LoadIcons();
|
||||||
|
this.Load += Form1_Load;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Form1_Load(object sender, EventArgs e) {
|
||||||
|
// Hide the form initially since we're running in system tray
|
||||||
|
this.Hide();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void notifyIcon_MouseDoubleClick(object sender, MouseEventArgs e) {
|
||||||
|
ShowSettings();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void settingsMenuItem_Click(object sender, EventArgs e) {
|
||||||
|
ShowSettings();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void exitMenuItem_Click(object sender, EventArgs e) {
|
||||||
|
Application.Exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Form1_FormClosing(object sender, FormClosingEventArgs e) {
|
||||||
|
// Prevent the form from closing, just hide it
|
||||||
|
if (e.CloseReason == CloseReason.UserClosing) {
|
||||||
|
e.Cancel = true;
|
||||||
|
this.Hide();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ShowSettings() {
|
||||||
|
if (settingsForm == null || settingsForm.IsDisposed) {
|
||||||
|
settingsForm = new SettingsForm();
|
||||||
|
}
|
||||||
|
|
||||||
|
settingsForm.Show();
|
||||||
|
settingsForm.BringToFront();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void LoadIcons() {
|
||||||
|
try {
|
||||||
|
string iconPath = System.IO.Path.Combine(
|
||||||
|
System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location),
|
||||||
|
"app.ico");
|
||||||
|
this.Icon = new System.Drawing.Icon(iconPath);
|
||||||
|
this.notifyIcon.Icon = new System.Drawing.Icon(iconPath);
|
||||||
|
} catch {
|
||||||
|
// Use default icon if custom icon not found
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -37,13 +37,15 @@ class KeyboardHook {
|
|||||||
int pero = (int)wParam * 1000 + vkCode;
|
int pero = (int)wParam * 1000 + vkCode;
|
||||||
if (pero != previousEvent) {
|
if (pero != previousEvent) {
|
||||||
if (wParam == (IntPtr)WM_KEYDOWN) {
|
if (wParam == (IntPtr)WM_KEYDOWN) {
|
||||||
|
Console.WriteLine($"KeyboardHook: KeyDown event for key {vkCode}");
|
||||||
KeyDown?.Invoke(null, vkCode);
|
KeyDown?.Invoke(null, vkCode);
|
||||||
} else if (wParam == (IntPtr)WM_KEYUP) {
|
} else if (wParam == (IntPtr)WM_KEYUP) {
|
||||||
|
Console.WriteLine($"KeyboardHook: KeyUp event for key {vkCode}");
|
||||||
KeyUp?.Invoke(null, vkCode);
|
KeyUp?.Invoke(null, vkCode);
|
||||||
}
|
}
|
||||||
previousEvent = pero;
|
previousEvent = pero;
|
||||||
} else {
|
} else {
|
||||||
Console.WriteLine("Same event");
|
Console.WriteLine($"KeyboardHook: Same event filtered out - vkCode: {vkCode}, wParam: {wParam}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return CallNextHookEx(_hookID, nCode, wParam, lParam);
|
return CallNextHookEx(_hookID, nCode, wParam, lParam);
|
||||||
|
@@ -1,18 +1,265 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
using System.Text.Json;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace DD2Switcher {
|
namespace DD2Switcher {
|
||||||
|
public class Settings {
|
||||||
|
public Keys SequenceKeybind { get; set; } = Keys.F1;
|
||||||
|
public int SequenceTimerDelay { get; set; } = 100;
|
||||||
|
}
|
||||||
|
|
||||||
internal static class Program {
|
internal static class Program {
|
||||||
private static int NumProc = 19;
|
private static int NumProc = 19;
|
||||||
private static Process[] windows = new Process[NumProc];
|
private static Process[] windows = new Process[NumProc];
|
||||||
|
private static string settingsPath =
|
||||||
|
Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), "DD2Switcher.json");
|
||||||
|
|
||||||
|
// Public access to tracked windows for the settings form
|
||||||
|
public static Process[] GetTrackedWindows() {
|
||||||
|
return windows;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void UntrackWindow(int index) {
|
||||||
|
if (index >= 0 && index < NumProc) {
|
||||||
|
windows[index] = null;
|
||||||
|
|
||||||
|
// Update ActiveIndex if needed
|
||||||
|
if (ActiveIndex == index) {
|
||||||
|
ActiveIndex = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update first/last indices if needed
|
||||||
|
if (FirstIndex == index) {
|
||||||
|
FirstIndex = -1;
|
||||||
|
}
|
||||||
|
if (LastIndex == index) {
|
||||||
|
LastIndex = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void StartSequenceMode() {
|
||||||
|
Console.WriteLine($"StartSequenceMode called. FirstIndex: {FirstIndex}, LastIndex: {LastIndex}");
|
||||||
|
|
||||||
|
// Compute indices only when sequence starts, not stored permanently
|
||||||
|
int sequenceFirstIndex = FirstIndex;
|
||||||
|
int sequenceLastIndex = LastIndex;
|
||||||
|
|
||||||
|
// If no user indices, use absolute first and last windows
|
||||||
|
if (sequenceFirstIndex == -1 || sequenceLastIndex == -1) {
|
||||||
|
sequenceFirstIndex = FindFirstNonNullWindow();
|
||||||
|
sequenceLastIndex = FindLastNonNullWindow();
|
||||||
|
Console.WriteLine(
|
||||||
|
$"Computed sequence indices: FirstIndex={sequenceFirstIndex}, LastIndex={sequenceLastIndex}");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sequenceFirstIndex >= 0 && sequenceLastIndex >= 0 && sequenceFirstIndex <= sequenceLastIndex) {
|
||||||
|
CurrentSequenceIndex = sequenceFirstIndex;
|
||||||
|
Console.WriteLine($"Starting sequence mode, tabbing to index {CurrentSequenceIndex + 1}");
|
||||||
|
TabTo(CurrentSequenceIndex + 1); // Tab to first window
|
||||||
|
CurrentState = SequenceState.WAITING_FOR_EVENT;
|
||||||
|
Console.WriteLine($"State changed to: {CurrentState}");
|
||||||
|
} else {
|
||||||
|
Console.WriteLine("Cannot start sequence mode - invalid first/last indices");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void ExitSequenceMode() {
|
||||||
|
CurrentState = SequenceState.INACTIVE;
|
||||||
|
CurrentSequenceIndex = -1;
|
||||||
|
Console.WriteLine($"State changed to: {CurrentState}");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool IsInSequenceMode() {
|
||||||
|
return CurrentState != SequenceState.INACTIVE;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void LoadSettings() {
|
||||||
|
Console.WriteLine($"Attempting to load settings from: {settingsPath}");
|
||||||
|
try {
|
||||||
|
if (File.Exists(settingsPath)) {
|
||||||
|
Console.WriteLine("Settings file exists, reading...");
|
||||||
|
string json = File.ReadAllText(settingsPath);
|
||||||
|
Console.WriteLine($"Read JSON: {json}");
|
||||||
|
var settings = JsonSerializer.Deserialize<Settings>(json);
|
||||||
|
SequenceKeybind = settings.SequenceKeybind;
|
||||||
|
SequenceTimerDelay = settings.SequenceTimerDelay;
|
||||||
|
Console.WriteLine(
|
||||||
|
$"Loaded settings: Keybind={SequenceKeybind}, TimerDelay={SequenceTimerDelay} (First/Last indices NOT loaded)");
|
||||||
|
} else {
|
||||||
|
Console.WriteLine($"Settings file does not exist at: {settingsPath}");
|
||||||
|
Console.WriteLine("Using default settings");
|
||||||
|
}
|
||||||
|
} catch (Exception ex) {
|
||||||
|
Console.WriteLine($"Error loading settings: {ex.Message}");
|
||||||
|
Console.WriteLine($"Stack trace: {ex.StackTrace}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void SaveSettings() {
|
||||||
|
try {
|
||||||
|
var settings =
|
||||||
|
new Settings { SequenceKeybind = SequenceKeybind, SequenceTimerDelay = SequenceTimerDelay };
|
||||||
|
string json = JsonSerializer.Serialize(settings, new JsonSerializerOptions { WriteIndented = true });
|
||||||
|
File.WriteAllText(settingsPath, json);
|
||||||
|
Console.WriteLine($"Saved settings: Keybind={SequenceKeybind}, TimerDelay={SequenceTimerDelay}");
|
||||||
|
} catch (Exception ex) {
|
||||||
|
Console.WriteLine($"Error saving settings: {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int FindFirstNonNullWindow() {
|
||||||
|
for (int i = 0; i < NumProc; i++) {
|
||||||
|
if (windows[i] != null && windows[i].MainWindowHandle != IntPtr.Zero) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int FindLastNonNullWindow() {
|
||||||
|
for (int i = NumProc - 1; i >= 0; i--) {
|
||||||
|
if (windows[i] != null && windows[i].MainWindowHandle != IntPtr.Zero) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int FindNextNonNullWindow(int startIndex) {
|
||||||
|
for (int i = startIndex; i < NumProc; i++) {
|
||||||
|
if (windows[i] != null && windows[i].MainWindowHandle != IntPtr.Zero) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void SetDefaultFirstLastIndices() {
|
||||||
|
if (FirstIndex == -1) {
|
||||||
|
FirstIndex = FindFirstNonNullWindow();
|
||||||
|
Console.WriteLine($"Set default FirstIndex to: {FirstIndex}");
|
||||||
|
}
|
||||||
|
if (LastIndex == -1) {
|
||||||
|
LastIndex = FindLastNonNullWindow();
|
||||||
|
Console.WriteLine($"Set default LastIndex to: {LastIndex}");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure LastIndex is different from FirstIndex if there are multiple windows
|
||||||
|
if (FirstIndex != -1 && LastIndex != -1 && FirstIndex == LastIndex) {
|
||||||
|
// Find the next non-null window after FirstIndex
|
||||||
|
int nextWindow = FindNextNonNullWindow(FirstIndex + 1);
|
||||||
|
if (nextWindow != -1) {
|
||||||
|
LastIndex = nextWindow;
|
||||||
|
Console.WriteLine($"Adjusted LastIndex to: {LastIndex} (different from FirstIndex)");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void UpdateSequenceHotkey(Keys newKey) {
|
||||||
|
Console.WriteLine($"UpdateSequenceHotkey called with new key: {newKey} (code: {(int)newKey})");
|
||||||
|
Console.WriteLine($"Old SequenceKeybind before update: {SequenceKeybind} (code: {(int)SequenceKeybind})");
|
||||||
|
|
||||||
|
// Unregister old hotkey
|
||||||
|
if (sequenceHotkeyId != -1) {
|
||||||
|
Console.WriteLine($"Unregistering old hotkey ID: {sequenceHotkeyId}");
|
||||||
|
HotKeyManager.UnregisterHotKey(sequenceHotkeyId);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Register new hotkey
|
||||||
|
sequenceHotkeyId = HotKeyManager.RegisterHotKey(newKey, KeyModifiers.NoRepeat);
|
||||||
|
Console.WriteLine($"Registered new hotkey ID: {sequenceHotkeyId} for key: {newKey}");
|
||||||
|
SequenceKeybind = newKey;
|
||||||
|
Console.WriteLine($"New SequenceKeybind after update: {SequenceKeybind} (code: {(int)SequenceKeybind})");
|
||||||
|
SaveSettings();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void SaveFirstLastIndices() {
|
||||||
|
SaveSettings();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Static properties for first/last selection persistence
|
||||||
|
public static int FirstIndex { get; set; } = -1;
|
||||||
|
public static int LastIndex { get; set; } = -1;
|
||||||
|
|
||||||
|
// Sequence mode state engine
|
||||||
|
public enum SequenceState { INACTIVE, WAITING_FOR_EVENT, WAITING_TO_ADVANCE, ADVANCE }
|
||||||
|
|
||||||
|
public static SequenceState CurrentState { get; set; } = SequenceState.INACTIVE;
|
||||||
|
public static int CurrentSequenceIndex { get; set; } = -1;
|
||||||
|
public static Keys SequenceKeybind { get; set; } = Keys.F1;
|
||||||
|
public static int SequenceTimerDelay { get; set; } = 100;
|
||||||
|
private static int sequenceHotkeyId = -1;
|
||||||
private static int ActiveIndex = -1;
|
private static int ActiveIndex = -1;
|
||||||
private static bool AltPressed = false;
|
private static bool AltPressed = false;
|
||||||
|
|
||||||
|
// State handlers
|
||||||
|
private static readonly Dictionary<SequenceState, Action> stateHandlers =
|
||||||
|
new Dictionary<SequenceState, Action> { { SequenceState.INACTIVE, HandleInactive },
|
||||||
|
{ SequenceState.WAITING_FOR_EVENT, HandleWaitingForEvent },
|
||||||
|
{ SequenceState.WAITING_TO_ADVANCE, HandleWaitingToAdvance },
|
||||||
|
{ SequenceState.ADVANCE, HandleAdvance } };
|
||||||
|
|
||||||
|
private static void HandleInactive() {
|
||||||
|
// Inactive state - do nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void HandleWaitingForEvent() {
|
||||||
|
// Event happened, transition to waiting to advance
|
||||||
|
Console.WriteLine($"Event detected in state: {CurrentState}, transitioning to WAITING_TO_ADVANCE");
|
||||||
|
CurrentState = SequenceState.WAITING_TO_ADVANCE;
|
||||||
|
Console.WriteLine($"State changed to: {CurrentState}");
|
||||||
|
HandleWaitingToAdvance();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void HandleWaitingToAdvance() {
|
||||||
|
// Start timer to advance after N milliseconds
|
||||||
|
Console.WriteLine($"Starting {SequenceTimerDelay}ms timer in WAITING_TO_ADVANCE state");
|
||||||
|
Task.Delay(SequenceTimerDelay)
|
||||||
|
.ContinueWith(
|
||||||
|
_ => {
|
||||||
|
if (CurrentState == SequenceState.WAITING_TO_ADVANCE) {
|
||||||
|
CurrentState = SequenceState.ADVANCE;
|
||||||
|
Console.WriteLine($"Timer expired, transitioning to ADVANCE state");
|
||||||
|
HandleSequenceEvent();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void HandleAdvance() {
|
||||||
|
Console.WriteLine($"Advancing sequence from ADVANCE state");
|
||||||
|
|
||||||
|
CurrentSequenceIndex++;
|
||||||
|
Console.WriteLine($"Advanced to index {CurrentSequenceIndex}");
|
||||||
|
|
||||||
|
// Get the actual last index for this sequence (computed or user-set)
|
||||||
|
int sequenceLastIndex = (LastIndex != -1) ? LastIndex : FindLastNonNullWindow();
|
||||||
|
|
||||||
|
if (CurrentSequenceIndex > sequenceLastIndex) {
|
||||||
|
// End of sequence - tab back to first
|
||||||
|
int sequenceFirstIndex = (FirstIndex != -1) ? FirstIndex : FindFirstNonNullWindow();
|
||||||
|
Console.WriteLine("End of sequence reached, tabbing back to first window");
|
||||||
|
TabTo(sequenceFirstIndex + 1);
|
||||||
|
ExitSequenceMode();
|
||||||
|
} else {
|
||||||
|
Console.WriteLine($"Tabbing to index {CurrentSequenceIndex + 1}");
|
||||||
|
TabTo(CurrentSequenceIndex + 1);
|
||||||
|
CurrentState = SequenceState.WAITING_FOR_EVENT;
|
||||||
|
Console.WriteLine($"State changed to: {CurrentState}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void HandleSequenceEvent() {
|
||||||
|
stateHandlers[CurrentState]?.Invoke();
|
||||||
|
}
|
||||||
|
|
||||||
// Simple list to track last 5 windows
|
// Simple list to track last 5 windows
|
||||||
private static List<int> lastWindows = new List<int>();
|
private static List<int> lastWindows = new List<int>();
|
||||||
|
|
||||||
@@ -26,9 +273,20 @@ namespace DD2Switcher {
|
|||||||
[DllImport("user32.dll")]
|
[DllImport("user32.dll")]
|
||||||
public static extern bool SetForegroundWindow(IntPtr hWnd);
|
public static extern bool SetForegroundWindow(IntPtr hWnd);
|
||||||
|
|
||||||
|
[DllImport("user32.dll")]
|
||||||
|
private static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
|
||||||
|
|
||||||
|
[DllImport("user32.dll")]
|
||||||
|
private static extern bool BringWindowToTop(IntPtr hWnd);
|
||||||
|
|
||||||
|
[DllImport("user32.dll")]
|
||||||
|
private static extern IntPtr SetActiveWindow(IntPtr hWnd);
|
||||||
|
|
||||||
[DllImport("user32.dll")]
|
[DllImport("user32.dll")]
|
||||||
private static extern short GetKeyState(int nVirtKey);
|
private static extern short GetKeyState(int nVirtKey);
|
||||||
|
|
||||||
|
private const int SW_RESTORE = 9;
|
||||||
|
|
||||||
[DllImport("kernel32.dll", SetLastError = true)]
|
[DllImport("kernel32.dll", SetLastError = true)]
|
||||||
[return:MarshalAs(UnmanagedType.Bool)]
|
[return:MarshalAs(UnmanagedType.Bool)]
|
||||||
static extern bool AllocConsole();
|
static extern bool AllocConsole();
|
||||||
@@ -36,6 +294,42 @@ namespace DD2Switcher {
|
|||||||
[DllImport("user32.dll")]
|
[DllImport("user32.dll")]
|
||||||
private static extern void keybd_event(byte bVk, byte bScan, uint dwFlags, int dwExtraInfo);
|
private static extern void keybd_event(byte bVk, byte bScan, uint dwFlags, int dwExtraInfo);
|
||||||
|
|
||||||
|
[DllImport("user32.dll")]
|
||||||
|
private static extern IntPtr SetWindowsHookEx(int idHook, IntPtr lpfn, IntPtr hMod, uint dwThreadId);
|
||||||
|
|
||||||
|
[DllImport("user32.dll")]
|
||||||
|
private static extern bool UnhookWindowsHookEx(IntPtr hhk);
|
||||||
|
|
||||||
|
[DllImport("user32.dll")]
|
||||||
|
private static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam);
|
||||||
|
|
||||||
|
[DllImport("kernel32.dll")]
|
||||||
|
private static extern IntPtr GetModuleHandle(string lpModuleName);
|
||||||
|
|
||||||
|
private const int WH_MOUSE_LL = 14;
|
||||||
|
private const int WM_LBUTTONDOWN = 0x0201;
|
||||||
|
private const int WM_RBUTTONDOWN = 0x0204;
|
||||||
|
private const int WM_MBUTTONDOWN = 0x0207;
|
||||||
|
private static IntPtr mouseHookId = IntPtr.Zero;
|
||||||
|
private static IntPtr mouseHookProc = IntPtr.Zero;
|
||||||
|
private static MouseHookProc mouseHookDelegate; // Keep reference to prevent GC
|
||||||
|
|
||||||
|
private delegate IntPtr MouseHookProc(int nCode, IntPtr wParam, IntPtr lParam);
|
||||||
|
|
||||||
|
private static IntPtr MouseHookCallback(int nCode, IntPtr wParam, IntPtr lParam) {
|
||||||
|
if (nCode >= 0) {
|
||||||
|
int wParamInt = (int)wParam;
|
||||||
|
if (wParamInt == WM_LBUTTONDOWN || wParamInt == WM_RBUTTONDOWN || wParamInt == WM_MBUTTONDOWN) {
|
||||||
|
if (CurrentState == SequenceState.WAITING_FOR_EVENT) {
|
||||||
|
Console.WriteLine($"Mouse click detected in state: {CurrentState}");
|
||||||
|
// Handle sequence event asynchronously to avoid interfering with mouse event processing
|
||||||
|
Task.Run(() => HandleSequenceEvent());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return CallNextHookEx(mouseHookId, nCode, wParam, lParam);
|
||||||
|
}
|
||||||
|
|
||||||
private static void CleanWindows() {
|
private static void CleanWindows() {
|
||||||
for (int i = 0; i < NumProc; i++) {
|
for (int i = 0; i < NumProc; i++) {
|
||||||
var window = windows[i];
|
var window = windows[i];
|
||||||
@@ -222,6 +516,7 @@ namespace DD2Switcher {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static void TabTo(int index) {
|
private static void TabTo(int index) {
|
||||||
|
Console.WriteLine($"TabTo called with index: {index}");
|
||||||
index = (index - 1) % NumProc;
|
index = (index - 1) % NumProc;
|
||||||
if (index < 0)
|
if (index < 0)
|
||||||
index = NumProc - 1;
|
index = NumProc - 1;
|
||||||
@@ -230,7 +525,29 @@ namespace DD2Switcher {
|
|||||||
CleanWindows();
|
CleanWindows();
|
||||||
Console.WriteLine($"Tab to window at index {index}");
|
Console.WriteLine($"Tab to window at index {index}");
|
||||||
|
|
||||||
|
// Find the next non-null window if the target is null
|
||||||
|
int originalIndex = index;
|
||||||
|
while (index < NumProc && (windows[index] == null || windows[index].MainWindowHandle == IntPtr.Zero)) {
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
if (index >= NumProc) {
|
||||||
|
// Try from the beginning
|
||||||
|
index = 0;
|
||||||
|
while (index < originalIndex &&
|
||||||
|
(windows[index] == null || windows[index].MainWindowHandle == IntPtr.Zero)) {
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (index >= NumProc || windows[index] == null || windows[index].MainWindowHandle == IntPtr.Zero) {
|
||||||
|
Console.WriteLine("No valid windows found to tab to");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
var window = windows[index];
|
var window = windows[index];
|
||||||
|
Console.WriteLine(
|
||||||
|
$"Window at index {index}: {(window == null ? "NULL" : window.ProcessName + " PID:" + window.Id)}");
|
||||||
|
|
||||||
if (window == null || window.MainWindowHandle == IntPtr.Zero) {
|
if (window == null || window.MainWindowHandle == IntPtr.Zero) {
|
||||||
Console.WriteLine($"Window at index {index} does not exist, removing from tracked windows");
|
Console.WriteLine($"Window at index {index} does not exist, removing from tracked windows");
|
||||||
windows[index] = null;
|
windows[index] = null;
|
||||||
@@ -238,19 +555,30 @@ namespace DD2Switcher {
|
|||||||
if (ActiveIndex != -1)
|
if (ActiveIndex != -1)
|
||||||
PushHistory(ActiveIndex);
|
PushHistory(ActiveIndex);
|
||||||
|
|
||||||
|
Console.WriteLine($"Setting foreground window to: {window.ProcessName} PID:{window.Id}");
|
||||||
SetForegroundWindow(window.MainWindowHandle);
|
SetForegroundWindow(window.MainWindowHandle);
|
||||||
|
ShowWindow(window.MainWindowHandle, SW_RESTORE);
|
||||||
|
BringWindowToTop(window.MainWindowHandle);
|
||||||
|
SetActiveWindow(window.MainWindowHandle);
|
||||||
|
|
||||||
ActiveIndex = index;
|
ActiveIndex = index;
|
||||||
AdjustAffinities();
|
AdjustAffinities();
|
||||||
AdjustPriorities();
|
AdjustPriorities();
|
||||||
|
Console.WriteLine($"Successfully switched to window at index {index}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void TabToPrevious() {
|
private static void TabToPrevious() {
|
||||||
|
return;
|
||||||
|
try {
|
||||||
var foreground = GetForegroundProcess();
|
var foreground = GetForegroundProcess();
|
||||||
if (!ProcessTracked(foreground.Id)) {
|
if (!ProcessTracked(foreground.Id)) {
|
||||||
Console.WriteLine("Foreground process not tracked, skipping");
|
Console.WriteLine("Foreground process not tracked, skipping");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
Console.WriteLine($"Error setting foreground window: {e}");
|
||||||
|
}
|
||||||
|
|
||||||
if (lastWindows.Count == 0) {
|
if (lastWindows.Count == 0) {
|
||||||
Console.WriteLine("No previous window to switch to");
|
Console.WriteLine("No previous window to switch to");
|
||||||
@@ -284,7 +612,12 @@ namespace DD2Switcher {
|
|||||||
|
|
||||||
[STAThread]
|
[STAThread]
|
||||||
private static void Main() {
|
private static void Main() {
|
||||||
// AllocConsole();
|
// AllocConsole(); // Enable console for debug output
|
||||||
|
Application.EnableVisualStyles();
|
||||||
|
Application.SetCompatibleTextRenderingDefault(false);
|
||||||
|
|
||||||
|
// Load settings
|
||||||
|
LoadSettings();
|
||||||
|
|
||||||
var processes = Process.GetProcesses();
|
var processes = Process.GetProcesses();
|
||||||
var currentProcess = Process.GetCurrentProcess();
|
var currentProcess = Process.GetCurrentProcess();
|
||||||
@@ -297,6 +630,12 @@ namespace DD2Switcher {
|
|||||||
|
|
||||||
bool onlyAlt = false;
|
bool onlyAlt = false;
|
||||||
KeyboardHook.Start();
|
KeyboardHook.Start();
|
||||||
|
|
||||||
|
// Set up mouse hook for sequence mode
|
||||||
|
mouseHookDelegate = new MouseHookProc(MouseHookCallback);
|
||||||
|
mouseHookProc = Marshal.GetFunctionPointerForDelegate(mouseHookDelegate);
|
||||||
|
mouseHookId = SetWindowsHookEx(WH_MOUSE_LL, mouseHookProc,
|
||||||
|
GetModuleHandle(Process.GetCurrentProcess().MainModule.ModuleName), 0);
|
||||||
KeyboardHook.KeyDown += (sender, e) => {
|
KeyboardHook.KeyDown += (sender, e) => {
|
||||||
Console.WriteLine($"Key down: {e}");
|
Console.WriteLine($"Key down: {e}");
|
||||||
if (e == 164 && !onlyAlt) {
|
if (e == 164 && !onlyAlt) {
|
||||||
@@ -314,6 +653,20 @@ namespace DD2Switcher {
|
|||||||
onlyAlt = false;
|
onlyAlt = false;
|
||||||
TabToPrevious();
|
TabToPrevious();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Handle sequence mode event detection with KeyUp
|
||||||
|
if (CurrentState == SequenceState.WAITING_FOR_EVENT) {
|
||||||
|
// Ignore the sequence keybind itself
|
||||||
|
if (e != (int)SequenceKeybind) {
|
||||||
|
Console.WriteLine($"Key up detected in state: {CurrentState} - Key: {e}");
|
||||||
|
HandleSequenceEvent();
|
||||||
|
} else {
|
||||||
|
Console.WriteLine($"Ignoring sequence keybind release: {e}");
|
||||||
|
}
|
||||||
|
} else if (CurrentState == SequenceState.WAITING_TO_ADVANCE) {
|
||||||
|
// Ignore key events while waiting to advance
|
||||||
|
Console.WriteLine($"Ignoring key event in WAITING_TO_ADVANCE state: {e}");
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
HotKeyManager.RegisterHotKey(Keys.Capital, KeyModifiers.NoRepeat);
|
HotKeyManager.RegisterHotKey(Keys.Capital, KeyModifiers.NoRepeat);
|
||||||
@@ -324,15 +677,48 @@ namespace DD2Switcher {
|
|||||||
for (int i = 0; i < 9; i++) HotKeyManager.RegisterHotKey(Keys.NumPad1 + i, KeyModifiers.Alt);
|
for (int i = 0; i < 9; i++) HotKeyManager.RegisterHotKey(Keys.NumPad1 + i, KeyModifiers.Alt);
|
||||||
|
|
||||||
HotKeyManager.RegisterHotKey(Keys.Oemtilde, KeyModifiers.Alt);
|
HotKeyManager.RegisterHotKey(Keys.Oemtilde, KeyModifiers.Alt);
|
||||||
|
sequenceHotkeyId = HotKeyManager.RegisterHotKey(SequenceKeybind, KeyModifiers.NoRepeat);
|
||||||
|
Console.WriteLine(
|
||||||
|
$"Initial sequence hotkey registration - ID: {sequenceHotkeyId}, Key: {SequenceKeybind} (code: {(int)SequenceKeybind})");
|
||||||
HotKeyManager.HotKeyPressed += HotKeyManager_HotKeyPressed;
|
HotKeyManager.HotKeyPressed += HotKeyManager_HotKeyPressed;
|
||||||
|
|
||||||
void HotKeyManager_HotKeyPressed(object sender, HotKeyEventArgs e) {
|
void HotKeyManager_HotKeyPressed(object sender, HotKeyEventArgs e) {
|
||||||
|
Console.WriteLine($"Hotkey pressed: {e.Key} with modifiers {e.Modifiers}");
|
||||||
|
Console.WriteLine($"Current sequence keybind: {SequenceKeybind}");
|
||||||
|
Console.WriteLine($"Key codes - Pressed: {(int)e.Key}, Expected: {(int)SequenceKeybind}");
|
||||||
|
|
||||||
|
// Cancel sequence mode on any manual window switching
|
||||||
|
if (CurrentState != SequenceState.INACTIVE && e.Modifiers == KeyModifiers.Alt) {
|
||||||
|
Console.WriteLine("Manual window switching detected, cancelling sequence mode");
|
||||||
|
ExitSequenceMode();
|
||||||
|
}
|
||||||
|
|
||||||
if (e.Key == Keys.Oemtilde && e.Modifiers == KeyModifiers.Alt && IsCapsLockOn()) {
|
if (e.Key == Keys.Oemtilde && e.Modifiers == KeyModifiers.Alt && IsCapsLockOn()) {
|
||||||
TrackProcess();
|
TrackProcess();
|
||||||
ToggleCapsLock();
|
ToggleCapsLock();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check for sequence mode keybind
|
||||||
|
Console.WriteLine(
|
||||||
|
$"Checking sequence keybind - Key: {e.Key} == {SequenceKeybind} = {e.Key == SequenceKeybind}");
|
||||||
|
Console.WriteLine($"Checking modifiers - Received: {e.Modifiers}, Expected: {KeyModifiers.NoRepeat}");
|
||||||
|
if (e.Key == SequenceKeybind) {
|
||||||
|
Console.WriteLine("Sequence keybind detected!");
|
||||||
|
if (CurrentState != SequenceState.INACTIVE) {
|
||||||
|
Console.WriteLine("Advancing sequence step");
|
||||||
|
HandleSequenceEvent();
|
||||||
|
} else {
|
||||||
|
Console.WriteLine("Starting sequence mode");
|
||||||
|
StartSequenceMode();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
Console.WriteLine(
|
||||||
|
$"Sequence keybind check failed - Key match: {e.Key == SequenceKeybind}, Modifiers match: {e.Modifiers == KeyModifiers.NoRepeat}");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
int index;
|
int index;
|
||||||
if (e.Key >= Keys.D0 && e.Key <= Keys.D9) {
|
if (e.Key >= Keys.D0 && e.Key <= Keys.D9) {
|
||||||
index = e.Key - Keys.D0;
|
index = e.Key - Keys.D0;
|
||||||
@@ -352,9 +738,14 @@ namespace DD2Switcher {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Console.CancelKeyPress += (sender, e) => { Process.GetCurrentProcess().Kill(); };
|
// Run the WinForms application with Form1 as the main form
|
||||||
Application.Run();
|
Application.Run(new Form1());
|
||||||
KeyboardHook.Stop();
|
KeyboardHook.Stop();
|
||||||
|
|
||||||
|
// Clean up mouse hook
|
||||||
|
if (mouseHookId != IntPtr.Zero) {
|
||||||
|
UnhookWindowsHookEx(mouseHookId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
316
DD2Switcher/SettingsForm.cs
Normal file
316
DD2Switcher/SettingsForm.cs
Normal file
@@ -0,0 +1,316 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Drawing;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Windows.Forms;
|
||||||
|
|
||||||
|
namespace DD2Switcher {
|
||||||
|
public partial class SettingsForm : Form {
|
||||||
|
private TabControl tabControl;
|
||||||
|
private TabPage windowsTab;
|
||||||
|
private TabPage sequenceTab;
|
||||||
|
private FlowLayoutPanel windowsPanel;
|
||||||
|
private Label sequenceKeybindLabel;
|
||||||
|
private TextBox sequenceKeybindTextBox;
|
||||||
|
private Label sequenceTimerDelayLabel;
|
||||||
|
private NumericUpDown sequenceTimerDelayNumericUpDown;
|
||||||
|
|
||||||
|
public SettingsForm() {
|
||||||
|
InitializeComponent();
|
||||||
|
LoadIcon();
|
||||||
|
RefreshWindowsList();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void InitializeComponent() {
|
||||||
|
this.tabControl = new System.Windows.Forms.TabControl();
|
||||||
|
this.windowsTab = new System.Windows.Forms.TabPage();
|
||||||
|
this.windowsPanel = new System.Windows.Forms.FlowLayoutPanel();
|
||||||
|
this.sequenceTab = new System.Windows.Forms.TabPage();
|
||||||
|
this.sequenceKeybindTextBox = new System.Windows.Forms.TextBox();
|
||||||
|
this.sequenceKeybindLabel = new System.Windows.Forms.Label();
|
||||||
|
this.sequenceTimerDelayNumericUpDown = new System.Windows.Forms.NumericUpDown();
|
||||||
|
this.sequenceTimerDelayLabel = new System.Windows.Forms.Label();
|
||||||
|
this.tabControl.SuspendLayout();
|
||||||
|
this.windowsTab.SuspendLayout();
|
||||||
|
this.sequenceTab.SuspendLayout();
|
||||||
|
((System.ComponentModel.ISupportInitialize)(this.sequenceTimerDelayNumericUpDown)).BeginInit();
|
||||||
|
this.SuspendLayout();
|
||||||
|
//
|
||||||
|
// tabControl
|
||||||
|
//
|
||||||
|
this.tabControl.Controls.Add(this.windowsTab);
|
||||||
|
this.tabControl.Controls.Add(this.sequenceTab);
|
||||||
|
this.tabControl.Location = new System.Drawing.Point(12, 12);
|
||||||
|
this.tabControl.Name = "tabControl";
|
||||||
|
this.tabControl.SelectedIndex = 0;
|
||||||
|
this.tabControl.Size = new System.Drawing.Size(576, 396);
|
||||||
|
this.tabControl.TabIndex = 0;
|
||||||
|
//
|
||||||
|
// windowsTab
|
||||||
|
//
|
||||||
|
this.windowsTab.Controls.Add(this.windowsPanel);
|
||||||
|
this.windowsTab.Location = new System.Drawing.Point(4, 22);
|
||||||
|
this.windowsTab.Name = "windowsTab";
|
||||||
|
this.windowsTab.Padding = new System.Windows.Forms.Padding(3);
|
||||||
|
this.windowsTab.Size = new System.Drawing.Size(568, 370);
|
||||||
|
this.windowsTab.TabIndex = 0;
|
||||||
|
this.windowsTab.Text = "Windows";
|
||||||
|
this.windowsTab.UseVisualStyleBackColor = true;
|
||||||
|
//
|
||||||
|
// windowsPanel
|
||||||
|
//
|
||||||
|
this.windowsPanel.AutoScroll = true;
|
||||||
|
this.windowsPanel.BackColor = System.Drawing.Color.White;
|
||||||
|
this.windowsPanel.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
|
||||||
|
this.windowsPanel.Location = new System.Drawing.Point(6, 6);
|
||||||
|
this.windowsPanel.Name = "windowsPanel";
|
||||||
|
this.windowsPanel.Padding = new System.Windows.Forms.Padding(10);
|
||||||
|
this.windowsPanel.Size = new System.Drawing.Size(556, 358);
|
||||||
|
this.windowsPanel.TabIndex = 0;
|
||||||
|
//
|
||||||
|
// sequenceTab
|
||||||
|
//
|
||||||
|
this.sequenceTab.Controls.Add(this.sequenceTimerDelayNumericUpDown);
|
||||||
|
this.sequenceTab.Controls.Add(this.sequenceTimerDelayLabel);
|
||||||
|
this.sequenceTab.Controls.Add(this.sequenceKeybindTextBox);
|
||||||
|
this.sequenceTab.Controls.Add(this.sequenceKeybindLabel);
|
||||||
|
this.sequenceTab.Location = new System.Drawing.Point(4, 22);
|
||||||
|
this.sequenceTab.Name = "sequenceTab";
|
||||||
|
this.sequenceTab.Padding = new System.Windows.Forms.Padding(3);
|
||||||
|
this.sequenceTab.Size = new System.Drawing.Size(568, 370);
|
||||||
|
this.sequenceTab.TabIndex = 1;
|
||||||
|
this.sequenceTab.Text = "Sequence";
|
||||||
|
this.sequenceTab.UseVisualStyleBackColor = true;
|
||||||
|
//
|
||||||
|
// sequenceKeybindTextBox
|
||||||
|
//
|
||||||
|
this.sequenceKeybindTextBox.Location = new System.Drawing.Point(126, 17);
|
||||||
|
this.sequenceKeybindTextBox.Name = "sequenceKeybindTextBox";
|
||||||
|
this.sequenceKeybindTextBox.Size = new System.Drawing.Size(111, 20);
|
||||||
|
this.sequenceKeybindTextBox.TabIndex = 1;
|
||||||
|
this.sequenceKeybindTextBox.Text = Program.SequenceKeybind.ToString();
|
||||||
|
this.sequenceKeybindTextBox.Leave += new System.EventHandler(this.sequenceKeybindTextBox_Leave);
|
||||||
|
//
|
||||||
|
// sequenceKeybindLabel
|
||||||
|
//
|
||||||
|
this.sequenceKeybindLabel.AutoSize = true;
|
||||||
|
this.sequenceKeybindLabel.Location = new System.Drawing.Point(20, 20);
|
||||||
|
this.sequenceKeybindLabel.Name = "sequenceKeybindLabel";
|
||||||
|
this.sequenceKeybindLabel.Size = new System.Drawing.Size(100, 13);
|
||||||
|
this.sequenceKeybindLabel.TabIndex = 0;
|
||||||
|
this.sequenceKeybindLabel.Text = "Sequence Keybind:";
|
||||||
|
//
|
||||||
|
// sequenceTimerDelayLabel
|
||||||
|
//
|
||||||
|
this.sequenceTimerDelayLabel.AutoSize = true;
|
||||||
|
this.sequenceTimerDelayLabel.Location = new System.Drawing.Point(20, 50);
|
||||||
|
this.sequenceTimerDelayLabel.Name = "sequenceTimerDelayLabel";
|
||||||
|
this.sequenceTimerDelayLabel.Size = new System.Drawing.Size(100, 13);
|
||||||
|
this.sequenceTimerDelayLabel.TabIndex = 2;
|
||||||
|
this.sequenceTimerDelayLabel.Text = "Timer Delay (ms):";
|
||||||
|
//
|
||||||
|
// sequenceTimerDelayNumericUpDown
|
||||||
|
//
|
||||||
|
this.sequenceTimerDelayNumericUpDown.Location = new System.Drawing.Point(126, 47);
|
||||||
|
this.sequenceTimerDelayNumericUpDown.Name = "sequenceTimerDelayNumericUpDown";
|
||||||
|
this.sequenceTimerDelayNumericUpDown.Size = new System.Drawing.Size(111, 20);
|
||||||
|
this.sequenceTimerDelayNumericUpDown.TabIndex = 3;
|
||||||
|
this.sequenceTimerDelayNumericUpDown.Minimum = 10;
|
||||||
|
this.sequenceTimerDelayNumericUpDown.Maximum = 10000;
|
||||||
|
this.sequenceTimerDelayNumericUpDown.Value = Program.SequenceTimerDelay;
|
||||||
|
this.sequenceTimerDelayNumericUpDown.ValueChanged +=
|
||||||
|
new System.EventHandler(this.sequenceTimerDelayNumericUpDown_ValueChanged);
|
||||||
|
//
|
||||||
|
// SettingsForm
|
||||||
|
//
|
||||||
|
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
||||||
|
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||||
|
this.ClientSize = new System.Drawing.Size(600, 420);
|
||||||
|
this.Controls.Add(this.tabControl);
|
||||||
|
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle;
|
||||||
|
this.MaximizeBox = false;
|
||||||
|
this.MinimizeBox = false;
|
||||||
|
this.Name = "SettingsForm";
|
||||||
|
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
|
||||||
|
this.Text = "DD2Switcher Settings";
|
||||||
|
this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.SettingsForm_FormClosing);
|
||||||
|
this.tabControl.ResumeLayout(false);
|
||||||
|
this.windowsTab.ResumeLayout(false);
|
||||||
|
this.sequenceTab.ResumeLayout(false);
|
||||||
|
this.sequenceTab.PerformLayout();
|
||||||
|
((System.ComponentModel.ISupportInitialize)(this.sequenceTimerDelayNumericUpDown)).EndInit();
|
||||||
|
this.ResumeLayout(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void closeButton_Click(object sender, EventArgs e) {
|
||||||
|
this.Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void refreshButton_Click(object sender, EventArgs e) {
|
||||||
|
RefreshWindowsList();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RefreshWindowsList() {
|
||||||
|
windowsPanel.Controls.Clear();
|
||||||
|
|
||||||
|
// Get tracked windows from Program
|
||||||
|
var trackedWindows = GetTrackedWindows();
|
||||||
|
|
||||||
|
if (trackedWindows.Count == 0) {
|
||||||
|
var noWindowsLabel = new Label();
|
||||||
|
noWindowsLabel.Text = "No windows currently tracked";
|
||||||
|
noWindowsLabel.AutoSize = true;
|
||||||
|
noWindowsLabel.ForeColor = Color.Gray;
|
||||||
|
noWindowsLabel.Font = new Font("Segoe UI", 10F);
|
||||||
|
windowsPanel.Controls.Add(noWindowsLabel);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < trackedWindows.Count; i++) {
|
||||||
|
var window = trackedWindows[i];
|
||||||
|
if (window == null)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
var windowPanelForm = new WindowPanelForm();
|
||||||
|
windowPanelForm.WindowIndex = i;
|
||||||
|
windowPanelForm.WindowProcess = window;
|
||||||
|
windowPanelForm.IsFirst = (i == Program.FirstIndex);
|
||||||
|
windowPanelForm.IsLast = (i == Program.LastIndex);
|
||||||
|
windowPanelForm.UpdateDisplay();
|
||||||
|
|
||||||
|
windowPanelForm.PickClicked += (sender, index) => {
|
||||||
|
// If clicking on a window that's already first or last, remove that setting
|
||||||
|
if (index == Program.FirstIndex) {
|
||||||
|
Program.FirstIndex = -1;
|
||||||
|
Console.WriteLine($"Removed first index: {index}");
|
||||||
|
} else if (index == Program.LastIndex) {
|
||||||
|
Program.LastIndex = -1;
|
||||||
|
Console.WriteLine($"Removed last index: {index}");
|
||||||
|
} else if (Program.FirstIndex == -1) {
|
||||||
|
// First pick - set both first and last
|
||||||
|
Program.FirstIndex = index;
|
||||||
|
Program.LastIndex = index;
|
||||||
|
Console.WriteLine($"Set first and last index: {index}");
|
||||||
|
} else if (Program.LastIndex == -1) {
|
||||||
|
// Second pick - set last (must be >= first)
|
||||||
|
if (index >= Program.FirstIndex) {
|
||||||
|
Program.LastIndex = index;
|
||||||
|
} else {
|
||||||
|
Program.LastIndex = Program.FirstIndex;
|
||||||
|
Program.FirstIndex = index;
|
||||||
|
}
|
||||||
|
Console.WriteLine($"Set last index: {Program.LastIndex}");
|
||||||
|
} else {
|
||||||
|
// Subsequent picks - determine which becomes first
|
||||||
|
int distanceToFirst = Math.Abs(index - Program.FirstIndex);
|
||||||
|
int distanceToLast = Math.Abs(index - Program.LastIndex);
|
||||||
|
|
||||||
|
if (distanceToFirst <= distanceToLast) {
|
||||||
|
// New index is closer to first, so first moves
|
||||||
|
if (index <= Program.LastIndex) {
|
||||||
|
Program.FirstIndex = index;
|
||||||
|
} else {
|
||||||
|
Program.LastIndex = Program.FirstIndex;
|
||||||
|
Program.FirstIndex = index;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// New index is closer to last, so last moves
|
||||||
|
if (index >= Program.FirstIndex) {
|
||||||
|
Program.LastIndex = index;
|
||||||
|
} else {
|
||||||
|
Program.FirstIndex = Program.LastIndex;
|
||||||
|
Program.LastIndex = index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Console.WriteLine($"Updated indices: First={Program.FirstIndex}, Last={Program.LastIndex}");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure last >= first
|
||||||
|
if (Program.LastIndex < Program.FirstIndex) {
|
||||||
|
int tmp = Program.LastIndex;
|
||||||
|
Program.LastIndex = Program.FirstIndex;
|
||||||
|
Program.FirstIndex = tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
RefreshWindowsList();
|
||||||
|
};
|
||||||
|
windowPanelForm.UntrackClicked += (sender, index) => {
|
||||||
|
UntrackWindow(index);
|
||||||
|
RefreshWindowsList();
|
||||||
|
};
|
||||||
|
|
||||||
|
windowsPanel.Controls.Add(windowPanelForm);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private string GetFirstLastText(int index) {
|
||||||
|
if (index == Program.FirstIndex && index == Program.LastIndex) {
|
||||||
|
return "First & Last";
|
||||||
|
} else if (index == Program.FirstIndex) {
|
||||||
|
return "First";
|
||||||
|
} else if (index == Program.LastIndex) {
|
||||||
|
return "Last";
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<Process> GetTrackedWindows() {
|
||||||
|
var windows = Program.GetTrackedWindows();
|
||||||
|
return windows.ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UntrackWindow(int index) {
|
||||||
|
Program.UntrackWindow(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void LoadIcon() {
|
||||||
|
try {
|
||||||
|
this.Icon = new Icon(System.IO.Path.Combine(
|
||||||
|
System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location),
|
||||||
|
"app.ico"));
|
||||||
|
} catch {
|
||||||
|
// Use default icon if custom icon not found
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void sequenceKeybindTextBox_Leave(object sender, EventArgs e) {
|
||||||
|
Console.WriteLine($"sequenceKeybindTextBox_Leave called with text: {sequenceKeybindTextBox.Text}");
|
||||||
|
try {
|
||||||
|
KeysConverter converter = new KeysConverter();
|
||||||
|
Keys newKey = (Keys)converter.ConvertFromString(sequenceKeybindTextBox.Text);
|
||||||
|
Console.WriteLine($"Converted key: {newKey} (code: {(int)newKey})");
|
||||||
|
Program.UpdateSequenceHotkey(newKey);
|
||||||
|
Console.WriteLine("Keybind updated successfully!");
|
||||||
|
} catch (Exception ex) {
|
||||||
|
Console.WriteLine($"Error converting key: {ex.Message}");
|
||||||
|
sequenceKeybindTextBox.Text = Program.SequenceKeybind.ToString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void sequenceTimerDelayNumericUpDown_ValueChanged(object sender, EventArgs e) {
|
||||||
|
Console.WriteLine(
|
||||||
|
$"sequenceTimerDelayNumericUpDown_ValueChanged called with value: {sequenceTimerDelayNumericUpDown.Value}");
|
||||||
|
try {
|
||||||
|
int newDelay = (int)sequenceTimerDelayNumericUpDown.Value;
|
||||||
|
Program.SequenceTimerDelay = newDelay;
|
||||||
|
Program.SaveSettings();
|
||||||
|
Console.WriteLine($"Timer delay updated to: {newDelay}ms");
|
||||||
|
} catch (Exception ex) {
|
||||||
|
Console.WriteLine($"Error updating timer delay: {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SettingsForm_FormClosing(object sender, FormClosingEventArgs e) {
|
||||||
|
Console.WriteLine("SettingsForm closing - saving keybind");
|
||||||
|
try {
|
||||||
|
KeysConverter converter = new KeysConverter();
|
||||||
|
Keys newKey = (Keys)converter.ConvertFromString(sequenceKeybindTextBox.Text);
|
||||||
|
Console.WriteLine($"Saving keybind on close: {newKey}");
|
||||||
|
Program.UpdateSequenceHotkey(newKey);
|
||||||
|
} catch (Exception ex) {
|
||||||
|
Console.WriteLine($"Error saving keybind on close: {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
120
DD2Switcher/SettingsForm.resx
Normal file
120
DD2Switcher/SettingsForm.resx
Normal file
@@ -0,0 +1,120 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<root>
|
||||||
|
<!--
|
||||||
|
Microsoft ResX Schema
|
||||||
|
|
||||||
|
Version 2.0
|
||||||
|
|
||||||
|
The primary goals of this format is to allow a simple XML format
|
||||||
|
that is mostly human readable. The generation and parsing of the
|
||||||
|
various data types are done through the TypeConverter classes
|
||||||
|
associated with the data types.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
... ado.net/XML headers & schema ...
|
||||||
|
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||||
|
<resheader name="version">2.0</resheader>
|
||||||
|
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||||
|
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||||
|
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||||
|
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||||
|
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||||
|
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||||
|
</data>
|
||||||
|
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||||
|
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||||
|
<comment>This is a comment</comment>
|
||||||
|
</data>
|
||||||
|
|
||||||
|
There are any number of "resheader" rows that contain simple
|
||||||
|
name/value pairs.
|
||||||
|
|
||||||
|
Each data row contains a name, and value. The row also contains a
|
||||||
|
type or mimetype. Type corresponds to a .NET class that support
|
||||||
|
text/value conversion through the TypeConverter architecture.
|
||||||
|
Classes that don't support this are serialized and stored with the
|
||||||
|
mimetype set.
|
||||||
|
|
||||||
|
The mimetype is used for serialized objects, and tells the
|
||||||
|
ResXResourceReader how to depersist the object. This is currently not
|
||||||
|
extensible. For a given mimetype the value must be set accordingly:
|
||||||
|
|
||||||
|
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||||
|
that the ResXResourceWriter will generate, however the reader can
|
||||||
|
read any of the formats listed below.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.binary.base64
|
||||||
|
value : The object must be serialized with
|
||||||
|
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.soap.base64
|
||||||
|
value : The object must be serialized with
|
||||||
|
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||||
|
value : The object must be serialized into a byte array
|
||||||
|
: using a System.ComponentModel.TypeConverter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
-->
|
||||||
|
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||||
|
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||||
|
<xsd:element name="root" msdata:IsDataSet="true">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:choice maxOccurs="unbounded">
|
||||||
|
<xsd:element name="metadata">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||||
|
<xsd:attribute name="type" type="xsd:string" />
|
||||||
|
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||||
|
<xsd:attribute ref="xml:space" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="assembly">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:attribute name="alias" type="xsd:string" />
|
||||||
|
<xsd:attribute name="name" type="xsd:string" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="data">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||||
|
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||||
|
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||||
|
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||||
|
<xsd:attribute ref="xml:space" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="resheader">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
</xsd:choice>
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
</xsd:schema>
|
||||||
|
<resheader name="resmimetype">
|
||||||
|
<value>text/microsoft-resx</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="version">
|
||||||
|
<value>2.0</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="reader">
|
||||||
|
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="writer">
|
||||||
|
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
|
</resheader>
|
||||||
|
</root>
|
133
DD2Switcher/WindowPanelForm.Designer.cs
generated
Normal file
133
DD2Switcher/WindowPanelForm.Designer.cs
generated
Normal file
@@ -0,0 +1,133 @@
|
|||||||
|
namespace DD2Switcher {
|
||||||
|
partial class WindowPanelForm {
|
||||||
|
/// <summary>
|
||||||
|
/// Required designer variable.
|
||||||
|
/// </summary>
|
||||||
|
private System.ComponentModel.IContainer components = null;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Clean up any resources being used.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
|
||||||
|
protected override void Dispose(bool disposing) {
|
||||||
|
if (disposing && (components != null)) {
|
||||||
|
components.Dispose();
|
||||||
|
}
|
||||||
|
base.Dispose(disposing);
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Windows Form Designer generated code
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Required method for Designer support - do not modify
|
||||||
|
/// the contents of this method with the code editor.
|
||||||
|
/// </summary>
|
||||||
|
private void InitializeComponent() {
|
||||||
|
this.indexLabel = new System.Windows.Forms.Label();
|
||||||
|
this.nameLabel = new System.Windows.Forms.Label();
|
||||||
|
this.pidLabel = new System.Windows.Forms.Label();
|
||||||
|
this.titleLabel = new System.Windows.Forms.Label();
|
||||||
|
this.firstLastLabel = new System.Windows.Forms.Label();
|
||||||
|
this.pickButton = new System.Windows.Forms.Button();
|
||||||
|
this.untrackButton = new System.Windows.Forms.Button();
|
||||||
|
this.SuspendLayout();
|
||||||
|
//
|
||||||
|
// indexLabel
|
||||||
|
//
|
||||||
|
this.indexLabel.AutoSize = true;
|
||||||
|
this.indexLabel.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Bold);
|
||||||
|
this.indexLabel.Location = new System.Drawing.Point(10, 5);
|
||||||
|
this.indexLabel.Name = "indexLabel";
|
||||||
|
this.indexLabel.Size = new System.Drawing.Size(52, 15);
|
||||||
|
this.indexLabel.TabIndex = 0;
|
||||||
|
this.indexLabel.Text = "Index: 0";
|
||||||
|
//
|
||||||
|
// nameLabel
|
||||||
|
//
|
||||||
|
this.nameLabel.AutoSize = true;
|
||||||
|
this.nameLabel.Font = new System.Drawing.Font("Segoe UI", 9F);
|
||||||
|
this.nameLabel.Location = new System.Drawing.Point(12, 35);
|
||||||
|
this.nameLabel.Name = "nameLabel";
|
||||||
|
this.nameLabel.Size = new System.Drawing.Size(45, 15);
|
||||||
|
this.nameLabel.TabIndex = 1;
|
||||||
|
this.nameLabel.Text = "Name: ";
|
||||||
|
//
|
||||||
|
// pidLabel
|
||||||
|
//
|
||||||
|
this.pidLabel.AutoSize = true;
|
||||||
|
this.pidLabel.Font = new System.Drawing.Font("Segoe UI", 9F);
|
||||||
|
this.pidLabel.Location = new System.Drawing.Point(12, 50);
|
||||||
|
this.pidLabel.Name = "pidLabel";
|
||||||
|
this.pidLabel.Size = new System.Drawing.Size(28, 15);
|
||||||
|
this.pidLabel.TabIndex = 2;
|
||||||
|
this.pidLabel.Text = "PID:";
|
||||||
|
//
|
||||||
|
// titleLabel
|
||||||
|
//
|
||||||
|
this.titleLabel.AutoSize = true;
|
||||||
|
this.titleLabel.Font = new System.Drawing.Font("Segoe UI", 9F);
|
||||||
|
this.titleLabel.Location = new System.Drawing.Point(12, 20);
|
||||||
|
this.titleLabel.MaximumSize = new System.Drawing.Size(200, 0);
|
||||||
|
this.titleLabel.Name = "titleLabel";
|
||||||
|
this.titleLabel.Size = new System.Drawing.Size(36, 15);
|
||||||
|
this.titleLabel.TabIndex = 3;
|
||||||
|
this.titleLabel.Text = "Title: ";
|
||||||
|
//
|
||||||
|
// firstLastLabel
|
||||||
|
//
|
||||||
|
this.firstLastLabel.AutoSize = true;
|
||||||
|
this.firstLastLabel.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Bold);
|
||||||
|
this.firstLastLabel.ForeColor = System.Drawing.Color.DarkBlue;
|
||||||
|
this.firstLastLabel.Location = new System.Drawing.Point(200, 45);
|
||||||
|
this.firstLastLabel.Name = "firstLastLabel";
|
||||||
|
this.firstLastLabel.Size = new System.Drawing.Size(0, 15);
|
||||||
|
this.firstLastLabel.TabIndex = 4;
|
||||||
|
//
|
||||||
|
// pickButton
|
||||||
|
//
|
||||||
|
this.pickButton.Location = new System.Drawing.Point(420, 10);
|
||||||
|
this.pickButton.Name = "pickButton";
|
||||||
|
this.pickButton.Size = new System.Drawing.Size(88, 25);
|
||||||
|
this.pickButton.TabIndex = 5;
|
||||||
|
this.pickButton.Text = "Pick";
|
||||||
|
this.pickButton.UseVisualStyleBackColor = true;
|
||||||
|
this.pickButton.Click += new System.EventHandler(this.pickButton_Click);
|
||||||
|
//
|
||||||
|
// untrackButton
|
||||||
|
//
|
||||||
|
this.untrackButton.Location = new System.Drawing.Point(420, 45);
|
||||||
|
this.untrackButton.Name = "untrackButton";
|
||||||
|
this.untrackButton.Size = new System.Drawing.Size(88, 25);
|
||||||
|
this.untrackButton.TabIndex = 6;
|
||||||
|
this.untrackButton.Text = "Untrack";
|
||||||
|
this.untrackButton.UseVisualStyleBackColor = true;
|
||||||
|
this.untrackButton.Click += new System.EventHandler(this.untrackButton_Click);
|
||||||
|
//
|
||||||
|
// WindowPanelForm
|
||||||
|
//
|
||||||
|
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
||||||
|
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||||
|
this.Controls.Add(this.indexLabel);
|
||||||
|
this.Controls.Add(this.nameLabel);
|
||||||
|
this.Controls.Add(this.pidLabel);
|
||||||
|
this.Controls.Add(this.titleLabel);
|
||||||
|
this.Controls.Add(this.firstLastLabel);
|
||||||
|
this.Controls.Add(this.pickButton);
|
||||||
|
this.Controls.Add(this.untrackButton);
|
||||||
|
this.Name = "WindowPanelForm";
|
||||||
|
this.Size = new System.Drawing.Size(520, 80);
|
||||||
|
this.ResumeLayout(false);
|
||||||
|
this.PerformLayout();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
private System.Windows.Forms.Label indexLabel;
|
||||||
|
private System.Windows.Forms.Label nameLabel;
|
||||||
|
private System.Windows.Forms.Label pidLabel;
|
||||||
|
private System.Windows.Forms.Label titleLabel;
|
||||||
|
private System.Windows.Forms.Label firstLastLabel;
|
||||||
|
private System.Windows.Forms.Button pickButton;
|
||||||
|
private System.Windows.Forms.Button untrackButton;
|
||||||
|
}
|
||||||
|
}
|
47
DD2Switcher/WindowPanelForm.cs
Normal file
47
DD2Switcher/WindowPanelForm.cs
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
using System;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Drawing;
|
||||||
|
using System.Windows.Forms;
|
||||||
|
|
||||||
|
namespace DD2Switcher {
|
||||||
|
public partial class WindowPanelForm : UserControl {
|
||||||
|
public event EventHandler<int> PickClicked;
|
||||||
|
public event EventHandler<int> UntrackClicked;
|
||||||
|
|
||||||
|
public int WindowIndex { get; set; }
|
||||||
|
public Process WindowProcess { get; set; }
|
||||||
|
public bool IsFirst { get; set; }
|
||||||
|
public bool IsLast { get; set; }
|
||||||
|
|
||||||
|
public WindowPanelForm() {
|
||||||
|
InitializeComponent();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UpdateDisplay() {
|
||||||
|
if (WindowProcess != null) {
|
||||||
|
indexLabel.Text = $"Index: {WindowIndex}";
|
||||||
|
nameLabel.Text = $"Name: {WindowProcess.ProcessName}";
|
||||||
|
pidLabel.Text = $"PID: {WindowProcess.Id}";
|
||||||
|
titleLabel.Text = $"Title: {WindowProcess.MainWindowTitle}";
|
||||||
|
|
||||||
|
if (IsFirst && IsLast) {
|
||||||
|
firstLastLabel.Text = "First & Last";
|
||||||
|
} else if (IsFirst) {
|
||||||
|
firstLastLabel.Text = "First";
|
||||||
|
} else if (IsLast) {
|
||||||
|
firstLastLabel.Text = "Last";
|
||||||
|
} else {
|
||||||
|
firstLastLabel.Text = "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void pickButton_Click(object sender, EventArgs e) {
|
||||||
|
PickClicked?.Invoke(this, WindowIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void untrackButton_Click(object sender, EventArgs e) {
|
||||||
|
UntrackClicked?.Invoke(this, WindowIndex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
120
DD2Switcher/WindowPanelForm.resx
Normal file
120
DD2Switcher/WindowPanelForm.resx
Normal file
@@ -0,0 +1,120 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<root>
|
||||||
|
<!--
|
||||||
|
Microsoft ResX Schema
|
||||||
|
|
||||||
|
Version 2.0
|
||||||
|
|
||||||
|
The primary goals of this format is to allow a simple XML format
|
||||||
|
that is mostly human readable. The generation and parsing of the
|
||||||
|
various data types are done through the TypeConverter classes
|
||||||
|
associated with the data types.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
... ado.net/XML headers & schema ...
|
||||||
|
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||||
|
<resheader name="version">2.0</resheader>
|
||||||
|
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||||
|
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||||
|
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||||
|
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||||
|
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||||
|
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||||
|
</data>
|
||||||
|
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||||
|
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||||
|
<comment>This is a comment</comment>
|
||||||
|
</data>
|
||||||
|
|
||||||
|
There are any number of "resheader" rows that contain simple
|
||||||
|
name/value pairs.
|
||||||
|
|
||||||
|
Each data row contains a name, and value. The row also contains a
|
||||||
|
type or mimetype. Type corresponds to a .NET class that support
|
||||||
|
text/value conversion through the TypeConverter architecture.
|
||||||
|
Classes that don't support this are serialized and stored with the
|
||||||
|
mimetype set.
|
||||||
|
|
||||||
|
The mimetype is used for serialized objects, and tells the
|
||||||
|
ResXResourceReader how to depersist the object. This is currently not
|
||||||
|
extensible. For a given mimetype the value must be set accordingly:
|
||||||
|
|
||||||
|
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||||
|
that the ResXResourceWriter will generate, however the reader can
|
||||||
|
read any of the formats listed below.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.binary.base64
|
||||||
|
value : The object must be serialized with
|
||||||
|
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.soap.base64
|
||||||
|
value : The object must be serialized with
|
||||||
|
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||||
|
value : The object must be serialized into a byte array
|
||||||
|
: using a System.ComponentModel.TypeConverter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
-->
|
||||||
|
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||||
|
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||||
|
<xsd:element name="root" msdata:IsDataSet="true">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:choice maxOccurs="unbounded">
|
||||||
|
<xsd:element name="metadata">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||||
|
<xsd:attribute name="type" type="xsd:string" />
|
||||||
|
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||||
|
<xsd:attribute ref="xml:space" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="assembly">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:attribute name="alias" type="xsd:string" />
|
||||||
|
<xsd:attribute name="name" type="xsd:string" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="data">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||||
|
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||||
|
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||||
|
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||||
|
<xsd:attribute ref="xml:space" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="resheader">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
</xsd:choice>
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
</xsd:schema>
|
||||||
|
<resheader name="resmimetype">
|
||||||
|
<value>text/microsoft-resx</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="version">
|
||||||
|
<value>2.0</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="reader">
|
||||||
|
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="writer">
|
||||||
|
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
|
</resheader>
|
||||||
|
</root>
|
BIN
DD2Switcher/app.ico
Normal file
BIN
DD2Switcher/app.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 106 KiB |
13
DD2Switcher/packages.config
Normal file
13
DD2Switcher/packages.config
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<packages>
|
||||||
|
<package id="Microsoft.Bcl.AsyncInterfaces" version="9.0.8" targetFramework="net48" />
|
||||||
|
<package id="System.Buffers" version="4.5.1" targetFramework="net48" />
|
||||||
|
<package id="System.IO.Pipelines" version="9.0.8" targetFramework="net48" />
|
||||||
|
<package id="System.Memory" version="4.5.5" targetFramework="net48" />
|
||||||
|
<package id="System.Numerics.Vectors" version="4.5.0" targetFramework="net48" />
|
||||||
|
<package id="System.Runtime.CompilerServices.Unsafe" version="6.0.0" targetFramework="net48" />
|
||||||
|
<package id="System.Text.Encodings.Web" version="9.0.8" targetFramework="net48" />
|
||||||
|
<package id="System.Text.Json" version="9.0.8" targetFramework="net48" />
|
||||||
|
<package id="System.Threading.Tasks.Extensions" version="4.5.4" targetFramework="net48" />
|
||||||
|
<package id="System.ValueTuple" version="4.5.0" targetFramework="net48" />
|
||||||
|
</packages>
|
Reference in New Issue
Block a user