mirror of
https://github.com/hawkeye-stan/msfs-popout-panel-manager.git
synced 2024-11-21 21:30:12 +00:00
Version 2.2
This commit is contained in:
parent
4ca7a8e7b2
commit
9aa8f1d2db
19 changed files with 451 additions and 159 deletions
BIN
Config/preprocessingdata/separation_button.png
Normal file
BIN
Config/preprocessingdata/separation_button.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 8.7 KiB |
|
@ -4,8 +4,8 @@
|
|||
<OutputType>WinExe</OutputType>
|
||||
<TargetFramework>net5.0-windows</TargetFramework>
|
||||
<UseWindowsForms>true</UseWindowsForms>
|
||||
<Platforms>x64</Platforms>
|
||||
<Version>2.1.1.0</Version>
|
||||
<Platforms>x64;AnyCPU</Platforms>
|
||||
<Version>2.2.0.0</Version>
|
||||
<AssemblyName>MSFSPopoutPanelManager</AssemblyName>
|
||||
<RootNamespace>MSFSPopoutPanelManager</RootNamespace>
|
||||
<ApplicationIcon>WindowManager.ico</ApplicationIcon>
|
||||
|
@ -13,8 +13,10 @@
|
|||
<Product>MSFS 2020 Popout Panel Manager</Product>
|
||||
<PackageId>MSFS 2020 Popout Panel Manager</PackageId>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<AssemblyVersion>2.1.1.0</AssemblyVersion>
|
||||
<FileVersion>2.1.1.0</FileVersion>
|
||||
<AssemblyVersion>2.2.0.0</AssemblyVersion>
|
||||
<FileVersion>2.2.0.0</FileVersion>
|
||||
<DebugType>None</DebugType>
|
||||
<DebugSymbols>false</DebugSymbols>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
@ -40,6 +42,9 @@
|
|||
<None Remove="C:\Users\hawkeye\.nuget\packages\tesseract\4.1.1\build\\..\x86\tesseract41.dll" />
|
||||
<None Remove="Config\AnalysisData\analysisconfig.json" />
|
||||
<None Remove="Config\planeprofile.json" />
|
||||
<None Remove="LICENSE" />
|
||||
<None Remove="README.md" />
|
||||
<None Remove="VERSION.md" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
@ -48,6 +53,7 @@
|
|||
<PackageReference Include="DarkUI" Version="2.0.2" />
|
||||
<PackageReference Include="MouseKeyHook" Version="5.6.0" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
|
||||
<PackageReference Include="System.Text.Encoding.CodePages" Version="6.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
@ -65,6 +71,15 @@
|
|||
<Content Include="Config\AnalysisData\analysisconfig.json">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="LICENSE">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="README.md">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="VERSION.md">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
@ -206,10 +221,13 @@
|
|||
<None Update="Config\AnalysisData\g1000nxi\g1000nxi_pfd.png">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="Config\preprocessingdata\separation_button.png">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="Config\PreprocessingData\separation_button_qhd.png">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="Config\PreprocessingData\separation_button_uhd.png">
|
||||
<None Update="Config\preprocessingdata\test.svg">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="config\preprocess\blockmatch_wqhd1.png">
|
||||
|
@ -236,27 +254,15 @@
|
|||
<None Update="config\preprocess\g1000_pfd_wqhd.png">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="Config\PreprocessingData\separation_button_hd.png">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="config\preprocessingdata\separation_button_wqhd.png">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="images\transparent.png">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="LICENSE">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="VERSION.md">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="Vesion.md">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="README.md">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
@ -271,10 +277,6 @@
|
|||
<NativeLibs Remove="Logger.cs" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<NativeLibs Remove="Config\PreprocessingData\separation_button_hd.png" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<NativeLibs Remove="Config\PreprocessingData\separation_button_wqhd.png" />
|
||||
</ItemGroup>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 16
|
||||
VisualStudioVersion = 16.0.31424.327
|
||||
# Visual Studio Version 17
|
||||
VisualStudioVersion = 17.0.31919.166
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MSFSPopoutPanelManager", "MSFSPopoutPanelManager.csproj", "{1E89B7B3-DBD9-4644-A0EB-26924317DD83}"
|
||||
EndProject
|
||||
|
|
191
Modules/Autostart.cs
Normal file
191
Modules/Autostart.cs
Normal file
|
@ -0,0 +1,191 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Xml;
|
||||
using System.Xml.Serialization;
|
||||
|
||||
namespace MSFSPopoutPanelManager
|
||||
{
|
||||
public class Autostart
|
||||
{
|
||||
public static void Activate()
|
||||
{
|
||||
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
|
||||
var filePath = GetFilePath();
|
||||
var autoStartArg = new LaunchAddOn() { Name = "MSFS Popout Panel Manager", Disabled = "false", Path = $@"{Directory.GetCurrentDirectory()}\MSFSPopoutPanelManager.exe" };
|
||||
XmlSerializer serializer = new XmlSerializer(typeof(SimBaseDocument));
|
||||
|
||||
if (filePath != null && File.Exists(filePath))
|
||||
{
|
||||
// Create backup file if needed
|
||||
if (!File.Exists(filePath + ".backup"))
|
||||
{
|
||||
File.Copy(filePath, filePath + ".backup");
|
||||
}
|
||||
|
||||
SimBaseDocument data;
|
||||
|
||||
using (Stream stream = new FileStream(filePath, FileMode.Open))
|
||||
{
|
||||
data = (SimBaseDocument)serializer.Deserialize(stream);
|
||||
}
|
||||
|
||||
if (data != null)
|
||||
{
|
||||
if (data.LaunchAddOn.Count == 0)
|
||||
{
|
||||
data.LaunchAddOn.Add(autoStartArg);
|
||||
}
|
||||
else
|
||||
{
|
||||
var autoStartIndex = data.LaunchAddOn.FindIndex(x => x.Name == "MSFS Popout Panel Manager");
|
||||
|
||||
if (autoStartIndex > -1)
|
||||
data.LaunchAddOn[autoStartIndex] = autoStartArg;
|
||||
else
|
||||
data.LaunchAddOn.Add(autoStartArg);
|
||||
}
|
||||
|
||||
using (Stream stream = new FileStream(filePath, FileMode.Open))
|
||||
{
|
||||
stream.SetLength(0);
|
||||
serializer.Serialize(stream, data, new XmlSerializerNamespaces(new[] { XmlQualifiedName.Empty }));
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var data = new SimBaseDocument
|
||||
{
|
||||
Descr = "SimConnect",
|
||||
Filename = "SimConnect.xml",
|
||||
Disabled = "False",
|
||||
Type = "SimConnecct",
|
||||
Version = "1,0",
|
||||
LaunchAddOn = new List<LaunchAddOn>() { autoStartArg }
|
||||
};
|
||||
|
||||
using (var file = File.Create(filePath))
|
||||
{
|
||||
serializer.Serialize(file, data, new XmlSerializerNamespaces(new[] { XmlQualifiedName.Empty }));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void Deactivate()
|
||||
{
|
||||
//Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
|
||||
|
||||
var filePath = GetFilePath();
|
||||
|
||||
if (filePath != null && File.Exists(filePath))
|
||||
{
|
||||
SimBaseDocument data;
|
||||
XmlSerializer serializer = new XmlSerializer(typeof(SimBaseDocument));
|
||||
|
||||
using (Stream stream = new FileStream(filePath, FileMode.Open))
|
||||
{
|
||||
data = (SimBaseDocument)serializer.Deserialize(stream);
|
||||
}
|
||||
|
||||
if (data != null)
|
||||
{
|
||||
if (data.LaunchAddOn.Count > 0)
|
||||
{
|
||||
var autoStartIndex = data.LaunchAddOn.FindIndex(x => x.Name == "MSFS Popout Panel Manager");
|
||||
|
||||
if (autoStartIndex > -1)
|
||||
data.LaunchAddOn.RemoveAt(autoStartIndex);
|
||||
}
|
||||
|
||||
using (Stream stream = new FileStream(filePath, FileMode.Open))
|
||||
{
|
||||
stream.SetLength(0);
|
||||
serializer.Serialize(stream, data, new XmlSerializerNamespaces(new[] { XmlQualifiedName.Empty }));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static bool CheckIsAutoStart()
|
||||
{
|
||||
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
|
||||
|
||||
var filePath = GetFilePath();
|
||||
|
||||
if (filePath != null && File.Exists(filePath))
|
||||
{
|
||||
SimBaseDocument data;
|
||||
XmlSerializer serializer = new XmlSerializer(typeof(SimBaseDocument));
|
||||
|
||||
using (Stream stream = new FileStream(filePath, FileMode.Open))
|
||||
{
|
||||
data = (SimBaseDocument)serializer.Deserialize(stream);
|
||||
}
|
||||
|
||||
if (data != null)
|
||||
{
|
||||
if (data.LaunchAddOn.Count > 0)
|
||||
{
|
||||
var autoStartIndex = data.LaunchAddOn.FindIndex(x => x.Name == "MSFS Popout Panel Manager");
|
||||
|
||||
if (autoStartIndex > -1)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static string GetFilePath()
|
||||
{
|
||||
var filePathMSStore = Environment.ExpandEnvironmentVariables("%LocalAppData%") + @"\Packages\Microsoft.FlightSimulator_8wekyb3d8bbwe\LocalCache\";
|
||||
var filePathSteam = Environment.ExpandEnvironmentVariables("%AppData%") + @"\Microsoft Flight Simulator\LocalCache\";
|
||||
|
||||
if (Directory.Exists(filePathMSStore))
|
||||
return filePathMSStore + "exe.xml";
|
||||
else if (Directory.Exists(filePathSteam))
|
||||
return filePathSteam + "exe.xml";
|
||||
else
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
[XmlRoot(ElementName = "SimBase.Document")]
|
||||
public class SimBaseDocument
|
||||
{
|
||||
[XmlAttribute("Type")]
|
||||
public string Type { get; set; }
|
||||
|
||||
[XmlAttribute("version")]
|
||||
public string Version { get; set; }
|
||||
|
||||
[XmlElement(ElementName = "Descr")]
|
||||
public string Descr { get; set; }
|
||||
|
||||
[XmlElement(ElementName = "Filename")]
|
||||
public string Filename { get; set; }
|
||||
|
||||
[XmlElement(ElementName = "Disabled")]
|
||||
public string Disabled { get; set; }
|
||||
|
||||
[XmlElement(ElementName = "Launch.Addon")]
|
||||
public List<LaunchAddOn> LaunchAddOn { get; set; }
|
||||
}
|
||||
|
||||
public class LaunchAddOn
|
||||
{
|
||||
[XmlElement(ElementName = "Name")]
|
||||
public string Name { get; set; }
|
||||
|
||||
[XmlElement(ElementName = "Disabled")]
|
||||
public string Disabled { get; set; }
|
||||
|
||||
[XmlElement(ElementName = "Path")]
|
||||
public string Path { get; set; }
|
||||
}
|
||||
}
|
|
@ -273,7 +273,7 @@ namespace MSFSPopoutPanelManager
|
|||
return files;
|
||||
}
|
||||
|
||||
private static string GetFilePathByType(FilePathType filePathType)
|
||||
public static string GetFilePathByType(FilePathType filePathType)
|
||||
{
|
||||
switch (filePathType)
|
||||
{
|
||||
|
|
|
@ -1,25 +1,43 @@
|
|||
using AForge.Imaging;
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
|
||||
namespace MSFSPopoutPanelManager
|
||||
{
|
||||
public class ImageAnalysis
|
||||
{
|
||||
public static Point ExhaustiveTemplateMatchAnalysis(Bitmap sourceImage, Bitmap templateImage, int imageShrinkFactor, float similarityThreshHold)
|
||||
public static Point ExhaustiveTemplateMatchAnalysisAsync(Bitmap sourceImage, Bitmap templateImage, float similarityThreshHold, int panelStartingTop, int panelStartingLeft)
|
||||
{
|
||||
var x = panelStartingLeft - Convert.ToInt32(templateImage.Width * 1.5);
|
||||
var y = 0;
|
||||
var width = Convert.ToInt32(templateImage.Width * 1.5);
|
||||
var height = sourceImage.Height;
|
||||
|
||||
var searchZone = new Rectangle(x, y, width, height);
|
||||
|
||||
var point = AnalyzeExpandImageBitmap(sourceImage, templateImage, similarityThreshHold, searchZone);
|
||||
|
||||
if (point != Point.Empty)
|
||||
point.Y += panelStartingTop;
|
||||
|
||||
return point;
|
||||
}
|
||||
|
||||
public static Point AnalyzeExpandImageBitmap(Bitmap sourceImage, Bitmap templateImage, float similarityThreshHold, Rectangle searchZone)
|
||||
{
|
||||
// Full image pixel to pixel matching algorithm
|
||||
ExhaustiveTemplateMatching etm = new ExhaustiveTemplateMatching(similarityThreshHold);
|
||||
TemplateMatch[] templateMatches = etm.ProcessImage(sourceImage, templateImage);
|
||||
TemplateMatch[] templateMatches = etm.ProcessImage(sourceImage, templateImage, searchZone);
|
||||
|
||||
// Highlight the matchings that were found and saved a copy of the highlighted image
|
||||
if (templateMatches != null && templateMatches.Length > 0)
|
||||
{
|
||||
var match = templateMatches.OrderByDescending(x => x.Similarity).First(); // Just look at the first match since only one operation can be accomplished at a time on MSFS side
|
||||
var match = templateMatches.OrderByDescending(x => x.Similarity).First(); // Just look at the first match
|
||||
|
||||
var x = match.Rectangle.X * imageShrinkFactor + templateImage.Width * imageShrinkFactor / 4;
|
||||
var y = match.Rectangle.Y * imageShrinkFactor + templateImage.Height * imageShrinkFactor / 4;
|
||||
var xCoor = match.Rectangle.X + templateImage.Width / 12;
|
||||
var yCoor = match.Rectangle.Y + templateImage.Height / 4;
|
||||
|
||||
return new Point(x, y);
|
||||
return new Point(Convert.ToInt32(xCoor), Convert.ToInt32(yCoor));
|
||||
}
|
||||
|
||||
return Point.Empty;
|
||||
|
@ -38,19 +56,6 @@ namespace MSFSPopoutPanelManager
|
|||
var templateMatches = bm.ProcessImage(sourceImage, points, templateImage);
|
||||
|
||||
return templateMatches.Count;
|
||||
|
||||
// Full image pixel to pixel matching algorithm
|
||||
//ExhaustiveTemplateMatching etm = new ExhaustiveTemplateMatching(similarityThreshHold);
|
||||
//TemplateMatch[] templateMatches = etm.ProcessImage(sourceImage, templateImage);
|
||||
|
||||
//// Highlight the matchings that were found and saved a copy of the highlighted image
|
||||
//if (templateMatches != null && templateMatches.Length > 0)
|
||||
//{
|
||||
// var imageMatched = templateMatches.ToList().Max(x => x.Similarity);
|
||||
// return imageMatched;
|
||||
//}
|
||||
|
||||
//return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,9 +22,10 @@ namespace MSFSPopoutPanelManager
|
|||
return new Bitmap(new MemoryStream(bytes));
|
||||
}
|
||||
|
||||
public static Bitmap ResizeImage(Bitmap sourceImage, float width, float height)
|
||||
public static Bitmap ResizeImage(Bitmap sourceImage, double width, double height)
|
||||
{
|
||||
return new ResizeBilinear(Convert.ToInt32(width), Convert.ToInt32(height)).Apply(sourceImage);
|
||||
var bmp = new ResizeBilinear(Convert.ToInt32(width), Convert.ToInt32(height)).Apply(sourceImage);
|
||||
return ImageOperation.ConvertToFormat(bmp, PixelFormat.Format24bppRgb);
|
||||
}
|
||||
|
||||
public static Bitmap CropImage(Bitmap sourceImage, int x, int y, int width, int height)
|
||||
|
@ -36,7 +37,8 @@ namespace MSFSPopoutPanelManager
|
|||
{
|
||||
gr.DrawImage(sourceImage, new Rectangle(0, 0, bmp.Width, bmp.Height), crop, GraphicsUnit.Pixel);
|
||||
}
|
||||
return bmp;
|
||||
|
||||
return ImageOperation.ConvertToFormat(bmp, PixelFormat.Format24bppRgb);
|
||||
}
|
||||
|
||||
public static Bitmap ConvertToFormat(Bitmap image, PixelFormat format)
|
||||
|
@ -46,6 +48,7 @@ namespace MSFSPopoutPanelManager
|
|||
{
|
||||
gr.DrawImage(image, new Rectangle(0, 0, copy.Width, copy.Height));
|
||||
}
|
||||
|
||||
return copy;
|
||||
}
|
||||
|
||||
|
@ -70,14 +73,14 @@ namespace MSFSPopoutPanelManager
|
|||
var bottom = rect.Bottom;
|
||||
|
||||
var bounds = new Rectangle(left, top, right - left, bottom - top);
|
||||
var bitmap = new Bitmap(bounds.Width, bounds.Height);
|
||||
var bmp = new Bitmap(bounds.Width, bounds.Height);
|
||||
|
||||
using (Graphics g = Graphics.FromImage(bitmap))
|
||||
using (Graphics g = Graphics.FromImage(bmp))
|
||||
{
|
||||
g.CopyFromScreen(new Point(bounds.Left, bounds.Top), Point.Empty, bounds.Size);
|
||||
}
|
||||
|
||||
return bitmap;
|
||||
return ImageOperation.ConvertToFormat(bmp, PixelFormat.Format24bppRgb);
|
||||
}
|
||||
|
||||
public static Bitmap HighLightMatchedPattern(Bitmap sourceImage, List<Rect> rectBoxes)
|
||||
|
@ -96,5 +99,12 @@ namespace MSFSPopoutPanelManager
|
|||
|
||||
return sourceImage;
|
||||
}
|
||||
public static Bitmap GetExpandButtonImage(int windowHeight)
|
||||
{
|
||||
var image = new Bitmap(FileManager.LoadAsStream(FilePathType.PreprocessingData, "separation_button.png"));
|
||||
double template_image_ratio = Convert.ToDouble(windowHeight) / 1440; // expand button image was created on 1440p resolution
|
||||
|
||||
return ImageOperation.ResizeImage(image, Convert.ToInt32(image.Width * template_image_ratio), Convert.ToInt32(image.Height * template_image_ratio));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -37,7 +37,7 @@ namespace MSFSPopoutPanelManager
|
|||
{
|
||||
while (panelsToBeIdentified > 1) // Do not have to separate the last panel
|
||||
{
|
||||
var coordinate = AnalyzeMergedPopoutWindows(simulatorProcess.Handle, customPopout.Handle);
|
||||
var coordinate = AnalyzeMergedPopoutWindows(customPopout.Handle);
|
||||
if (!coordinate.IsEmpty)
|
||||
SeparateUntitledPanel(customPopout.Handle, coordinate.X, coordinate.Y);
|
||||
|
||||
|
@ -56,7 +56,6 @@ namespace MSFSPopoutPanelManager
|
|||
AnalyzePopoutWindows(simulatorProcess, profileId);
|
||||
}
|
||||
|
||||
|
||||
private WindowProcess GetProcessZero()
|
||||
{
|
||||
// Get process with PID of zero (PID zero launches all the popout windows for MSFS)
|
||||
|
@ -170,110 +169,56 @@ namespace MSFSPopoutPanelManager
|
|||
}
|
||||
}
|
||||
|
||||
public Point AnalyzeMergedPopoutWindows(IntPtr flightSimHandle, IntPtr windowHandle)
|
||||
public Point AnalyzeMergedPopoutWindows(IntPtr windowHandle)
|
||||
{
|
||||
var resolution = GetWindowResolution(flightSimHandle);
|
||||
float EXHAUSTIVE_TEMPLATE_MATCHING_SIMILARITY_THRESHOLD = 0.86f;
|
||||
|
||||
int EXHAUSTIVE_TEMPLATE_MATCHING_SHRINK_FACTOR = 1;
|
||||
float EXHAUSTIVE_TEMPLATE_MATCHING_SIMILARITY_THRESHOLD = 0.9f;
|
||||
var sourceImage = ImageOperation.TakeScreenShot(windowHandle, true);
|
||||
var templateImage = ImageOperation.GetExpandButtonImage(sourceImage.Height);
|
||||
|
||||
// This is to speed up pattern matching and still balance accuracy
|
||||
switch (resolution)
|
||||
{
|
||||
case FlightSimResolution.HD:
|
||||
EXHAUSTIVE_TEMPLATE_MATCHING_SHRINK_FACTOR = 2;
|
||||
EXHAUSTIVE_TEMPLATE_MATCHING_SIMILARITY_THRESHOLD = 0.88f;
|
||||
break;
|
||||
case FlightSimResolution.QHD:
|
||||
case FlightSimResolution.WQHD:
|
||||
case FlightSimResolution.UHD:
|
||||
EXHAUSTIVE_TEMPLATE_MATCHING_SHRINK_FACTOR = 3;
|
||||
EXHAUSTIVE_TEMPLATE_MATCHING_SIMILARITY_THRESHOLD = 0.85f;
|
||||
break;
|
||||
}
|
||||
|
||||
var panelsStartingTop = GetPanelsStartingTop(windowHandle, sourceImage);
|
||||
|
||||
var windowResolution = GetWindowResolution(flightSimHandle);
|
||||
var templateFileName = $"separation_button_{windowResolution}.png";
|
||||
|
||||
var source = ImageOperation.ConvertToFormat(ImageOperation.TakeScreenShot(windowHandle, true), PixelFormat.Format24bppRgb);
|
||||
var template = ImageOperation.ConvertToFormat(new Bitmap(FileManager.LoadAsStream(FilePathType.PreprocessingData, templateFileName)), PixelFormat.Format24bppRgb);
|
||||
|
||||
// Get the updated template image ratio based on how the initial window with all the popout panels in it are organized. This is used to resize the template image
|
||||
var templateImageRatio = GetTemplateToPanelHeightRatio(windowHandle, source, windowResolution, 1);
|
||||
|
||||
if (templateImageRatio == -1)
|
||||
{
|
||||
if (panelsStartingTop > sourceImage.Height / 2) // if usually the last panel occupied the entire window with no white menubar
|
||||
return Point.Empty;
|
||||
}
|
||||
|
||||
// Resize the source and template image to speed up exhaustive template matching analysis
|
||||
var sourceWidth = source.Width / EXHAUSTIVE_TEMPLATE_MATCHING_SHRINK_FACTOR;
|
||||
var sourceHeight = source.Height / EXHAUSTIVE_TEMPLATE_MATCHING_SHRINK_FACTOR;
|
||||
var templateWidth = template.Width * templateImageRatio / EXHAUSTIVE_TEMPLATE_MATCHING_SHRINK_FACTOR;
|
||||
var templateHeight = template.Height * templateImageRatio / EXHAUSTIVE_TEMPLATE_MATCHING_SHRINK_FACTOR;
|
||||
var panelsStartingLeft = GetPanelsStartingLeft(windowHandle, sourceImage, panelsStartingTop + 5);
|
||||
|
||||
var resizedSource = ImageOperation.ResizeImage(source, sourceWidth, sourceHeight);
|
||||
resizedSource = ImageOperation.ConvertToFormat(resizedSource, PixelFormat.Format24bppRgb);
|
||||
var templateImageRatios = GetExpandButtonHeightRatio(windowHandle, sourceImage, 1);
|
||||
|
||||
var resizedSource = ImageOperation.CropImage(sourceImage, 0, panelsStartingTop, sourceImage.Width, sourceImage.Height / 12); // add around 100px per 1440p resolution
|
||||
|
||||
resizedSource.Save(FileManager.GetFilePathByType(FilePathType.PreprocessingData) + "source.png");
|
||||
|
||||
var resizedTemplate = ImageOperation.ResizeImage(templateImage, templateImage.Width * templateImageRatios[0], templateImage.Height * templateImageRatios[0]);
|
||||
var point = ImageAnalysis.ExhaustiveTemplateMatchAnalysisAsync(resizedSource, resizedTemplate, EXHAUSTIVE_TEMPLATE_MATCHING_SIMILARITY_THRESHOLD, panelsStartingTop, panelsStartingLeft);
|
||||
|
||||
var resizedTemplate = ImageOperation.ResizeImage(template, templateWidth, templateHeight);
|
||||
|
||||
var point = ImageAnalysis.ExhaustiveTemplateMatchAnalysis(resizedSource, resizedTemplate, EXHAUSTIVE_TEMPLATE_MATCHING_SHRINK_FACTOR, EXHAUSTIVE_TEMPLATE_MATCHING_SIMILARITY_THRESHOLD);
|
||||
|
||||
if (point.IsEmpty)
|
||||
{
|
||||
template = ImageOperation.ConvertToFormat(new Bitmap(FileManager.LoadAsStream(FilePathType.PreprocessingData, templateFileName)), PixelFormat.Format24bppRgb);
|
||||
templateImageRatio = GetTemplateToPanelHeightRatio(windowHandle, source, windowResolution, 2); // maybe there are 2 rows of panels in the merged pop out window
|
||||
templateWidth = template.Width * templateImageRatio / EXHAUSTIVE_TEMPLATE_MATCHING_SHRINK_FACTOR;
|
||||
templateHeight = template.Height * templateImageRatio / EXHAUSTIVE_TEMPLATE_MATCHING_SHRINK_FACTOR;
|
||||
resizedTemplate = ImageOperation.ResizeImage(template, templateWidth, templateHeight);
|
||||
|
||||
point = ImageAnalysis.ExhaustiveTemplateMatchAnalysis(resizedSource, resizedTemplate, EXHAUSTIVE_TEMPLATE_MATCHING_SHRINK_FACTOR, EXHAUSTIVE_TEMPLATE_MATCHING_SIMILARITY_THRESHOLD);
|
||||
resizedTemplate = ImageOperation.ResizeImage(templateImage, templateImage.Width * templateImageRatios[1], templateImage.Height * templateImageRatios[1]);
|
||||
point = ImageAnalysis.ExhaustiveTemplateMatchAnalysisAsync(resizedSource, resizedTemplate, EXHAUSTIVE_TEMPLATE_MATCHING_SIMILARITY_THRESHOLD, panelsStartingTop, panelsStartingLeft);
|
||||
}
|
||||
|
||||
return point;
|
||||
}
|
||||
|
||||
private FlightSimResolution GetWindowResolution(IntPtr windowHandle)
|
||||
private List<double> GetExpandButtonHeightRatio(IntPtr windowHandle, Bitmap sourceImage, int numberOfRows, double percentFromLeft = 0.48)
|
||||
{
|
||||
var rect = new Rect();
|
||||
PInvoke.GetClientRect(windowHandle, out rect);
|
||||
var ratios = new List<double>();
|
||||
|
||||
switch (rect.Bottom)
|
||||
{
|
||||
case 1009:
|
||||
case 1080:
|
||||
return FlightSimResolution.HD;
|
||||
case 1369:
|
||||
case 1440:
|
||||
return FlightSimResolution.QHD;
|
||||
case 2089:
|
||||
case 2160:
|
||||
return FlightSimResolution.UHD;
|
||||
default:
|
||||
return FlightSimResolution.HD;
|
||||
}
|
||||
}
|
||||
|
||||
private float GetTemplateToPanelHeightRatio(IntPtr windowHandle, Bitmap sourceImage, FlightSimResolution windowResolution, int numberOfRows)
|
||||
{
|
||||
const int SW_MAXIMIZE = 3;
|
||||
PInvoke.ShowWindow(windowHandle, SW_MAXIMIZE);
|
||||
PInvoke.SetForegroundWindow(windowHandle);
|
||||
Thread.Sleep(500);
|
||||
Thread.Sleep(200);
|
||||
|
||||
Rect rect = new Rect();
|
||||
PInvoke.GetClientRect(windowHandle, out rect);
|
||||
|
||||
// Get a snippet of 1 pixel wide vertical strip of windows. We will choose the strip left of center.
|
||||
// This is to determine when the actual panel's vertical pixel starts in the window. This will allow accurate sizing of the template image
|
||||
// This is to determine when the actual panel's vertical pixel starts in the window. This will allow accurate sizing of the expand button image
|
||||
var clientWindowHeight = rect.Bottom - rect.Top;
|
||||
var left = Convert.ToInt32((rect.Right - rect.Left) * 0.48); // look at around 48% from the left
|
||||
var left = Convert.ToInt32((rect.Right - rect.Left) * percentFromLeft); // look at around 48% from the left
|
||||
var top = sourceImage.Height - clientWindowHeight;
|
||||
|
||||
if (top < 0 || left < 0)
|
||||
return -1;
|
||||
|
||||
// Using much faster image LockBits instead of GetPixel method
|
||||
unsafe
|
||||
{
|
||||
|
@ -284,9 +229,62 @@ namespace MSFSPopoutPanelManager
|
|||
int widthInBytes = stripData.Width * bytesPerPixel;
|
||||
byte* ptrFirstPixel = (byte*)stripData.Scan0;
|
||||
|
||||
// Find the first white pixel and have at least 4 more white pixels in a row (the panel title bar)
|
||||
const int WHITE_PIXEL_TO_COUNT = 4;
|
||||
int whitePixelCount = 0;
|
||||
// Find the first white pixel (the panel title bar)
|
||||
for (int y = 0; y < heightInPixels; y++)
|
||||
{
|
||||
byte* currentLine = ptrFirstPixel + (y * stripData.Stride);
|
||||
for (int x = 0; x < widthInBytes; x = x + bytesPerPixel)
|
||||
{
|
||||
int red = currentLine[x + 2];
|
||||
int green = currentLine[x + 1];
|
||||
int blue = currentLine[x];
|
||||
|
||||
if (red == 255 && green == 255 && blue == 255)
|
||||
{
|
||||
sourceImage.UnlockBits(stripData);
|
||||
|
||||
var unpopPanelSize = (clientWindowHeight - (y * 2)) / Convert.ToDouble(numberOfRows);
|
||||
|
||||
ratios.Add(unpopPanelSize / Convert.ToDouble(clientWindowHeight)); // 1 row of panel
|
||||
ratios.Add(unpopPanelSize / 2 / Convert.ToDouble(clientWindowHeight)); // 2 rows of panel
|
||||
return ratios;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sourceImage.UnlockBits(stripData);
|
||||
}
|
||||
|
||||
return GetExpandButtonHeightRatio(windowHandle, sourceImage, numberOfRows, percentFromLeft - 0.01);
|
||||
}
|
||||
|
||||
private int GetPanelsStartingTop(IntPtr windowHandle, Bitmap sourceImage, double percentFromLeft = 0.49)
|
||||
{
|
||||
const int SW_MAXIMIZE = 3;
|
||||
PInvoke.ShowWindow(windowHandle, SW_MAXIMIZE);
|
||||
PInvoke.SetForegroundWindow(windowHandle);
|
||||
Thread.Sleep(250);
|
||||
|
||||
Rect rect = new Rect();
|
||||
PInvoke.GetClientRect(windowHandle, out rect);
|
||||
|
||||
// Get a snippet of 1 pixel wide vertical strip of windows. We will choose the strip left of center.
|
||||
// This is to determine when the actual panel's vertical pixel starts in the window. This will allow accurate sizing of the template image
|
||||
var clientWindowHeight = rect.Bottom - rect.Top;
|
||||
var left = Convert.ToInt32((rect.Right - rect.Left) * percentFromLeft); // look at around 49% from the left
|
||||
var top = sourceImage.Height - clientWindowHeight;
|
||||
|
||||
if (top < 0 || left < 0)
|
||||
return -1;
|
||||
|
||||
unsafe
|
||||
{
|
||||
var stripData = sourceImage.LockBits(new Rectangle(left, top, 1, clientWindowHeight), ImageLockMode.ReadWrite, sourceImage.PixelFormat);
|
||||
|
||||
int bytesPerPixel = Bitmap.GetPixelFormatSize(stripData.PixelFormat) / 8;
|
||||
int heightInPixels = stripData.Height;
|
||||
int widthInBytes = stripData.Width * bytesPerPixel;
|
||||
byte* ptrFirstPixel = (byte*)stripData.Scan0;
|
||||
|
||||
for (int y = 0; y < heightInPixels; y++)
|
||||
{
|
||||
|
@ -299,19 +297,48 @@ namespace MSFSPopoutPanelManager
|
|||
|
||||
if (red == 255 && green == 255 && blue == 255)
|
||||
{
|
||||
whitePixelCount++;
|
||||
|
||||
if (whitePixelCount == WHITE_PIXEL_TO_COUNT)
|
||||
{
|
||||
sourceImage.UnlockBits(stripData);
|
||||
var unpopPanelSize = (clientWindowHeight - (y) * 2) / Convert.ToSingle(numberOfRows);
|
||||
var currentRatio = unpopPanelSize / Convert.ToSingle(clientWindowHeight);
|
||||
return currentRatio;
|
||||
}
|
||||
sourceImage.UnlockBits(stripData);
|
||||
return y + top;
|
||||
}
|
||||
else
|
||||
}
|
||||
}
|
||||
|
||||
sourceImage.UnlockBits(stripData);
|
||||
}
|
||||
|
||||
return GetPanelsStartingTop(windowHandle, sourceImage, percentFromLeft - 0.01);
|
||||
}
|
||||
|
||||
private int GetPanelsStartingLeft(IntPtr windowHandle, Bitmap sourceImage, int top)
|
||||
{
|
||||
Rect rect = new Rect();
|
||||
PInvoke.GetClientRect(windowHandle, out rect);
|
||||
|
||||
// Get a snippet of 1 pixel wide horizontal strip of windows
|
||||
var clientWindowWidth = rect.Right - rect.Left;
|
||||
|
||||
unsafe
|
||||
{
|
||||
var stripData = sourceImage.LockBits(new Rectangle(0, top, clientWindowWidth, 1), ImageLockMode.ReadWrite, sourceImage.PixelFormat);
|
||||
|
||||
int bytesPerPixel = Bitmap.GetPixelFormatSize(stripData.PixelFormat) / 8;
|
||||
int widthInPixels = stripData.Width;
|
||||
int heightInBytes = stripData.Height * bytesPerPixel;
|
||||
byte* ptrFirstPixel = (byte*)stripData.Scan0;
|
||||
|
||||
for (int x = 0; x < widthInPixels; x++)
|
||||
{
|
||||
byte* currentLine = ptrFirstPixel - (x * bytesPerPixel);
|
||||
for (int y = 0; y < heightInBytes; y = y + bytesPerPixel)
|
||||
{
|
||||
int red = currentLine[y + 2];
|
||||
int green = currentLine[y + 1];
|
||||
int blue = currentLine[y];
|
||||
|
||||
if (red == 255 && green == 255 && blue == 255)
|
||||
{
|
||||
whitePixelCount = 0;
|
||||
sourceImage.UnlockBits(stripData);
|
||||
return sourceImage.Width - x;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -331,11 +358,13 @@ namespace MSFSPopoutPanelManager
|
|||
Cursor.Position = new Point(point.X, point.Y);
|
||||
|
||||
// Wait for mouse to get into position
|
||||
Thread.Sleep(1000);
|
||||
Thread.Sleep(500);
|
||||
|
||||
PInvoke.mouse_event(MOUSEEVENTF_LEFTDOWN, point.X, point.Y, 0, 0);
|
||||
Thread.Sleep(200);
|
||||
PInvoke.mouse_event(MOUSEEVENTF_LEFTUP, point.X, point.Y, 0, 0);
|
||||
|
||||
Cursor.Position = new Point(point.X + 50, point.Y + 50);
|
||||
}
|
||||
|
||||
private void AnalyzePopoutWindows(WindowProcess simulatorProcess, int profileId)
|
||||
|
@ -374,7 +403,7 @@ namespace MSFSPopoutPanelManager
|
|||
|
||||
Thread.Sleep(300); // ** this delay is important to allow the window to go into focus before screenshot is taken
|
||||
|
||||
var srcImage = ImageOperation.ConvertToFormat(ImageOperation.TakeScreenShot(popout.Handle, false), PixelFormat.Format24bppRgb);
|
||||
var srcImage = ImageOperation.TakeScreenShot(popout.Handle, false);
|
||||
var srcImageBytes = ImageOperation.ImageToByte(srcImage);
|
||||
|
||||
Parallel.ForEach(templates, new ParallelOptions { MaxDegreeOfParallelism = Convert.ToInt32(Math.Ceiling((Environment.ProcessorCount * 0.75) * 1.0)) }, template =>
|
||||
|
|
|
@ -150,4 +150,4 @@ namespace MSFSPopoutPanelManager
|
|||
DrawPanelLocationOverlay();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -188,7 +188,10 @@ namespace MSFSPopoutPanelManager
|
|||
else
|
||||
Logger.LogStatus("No panel has been identified.");
|
||||
|
||||
ApplyPanelSettings();
|
||||
if(hasExistingData)
|
||||
ApplyPanelSettings();
|
||||
|
||||
PInvoke.SetForegroundWindow(_appForm.Handle);
|
||||
}
|
||||
|
||||
public void ApplyPanelSettings()
|
||||
|
@ -225,6 +228,8 @@ namespace MSFSPopoutPanelManager
|
|||
Logger.LogStatus("Please move the newly identified panels to their desire locations. Once everything is perfect, click 'Save Settings' and these settings will be used in future flights.");
|
||||
else
|
||||
Logger.LogStatus("No panel has been found.");
|
||||
|
||||
PInvoke.SetForegroundWindow(_appForm.Handle);
|
||||
}
|
||||
|
||||
public void SavePanelSettings()
|
||||
|
|
28
Program.cs
28
Program.cs
|
@ -1,4 +1,6 @@
|
|||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Threading;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace MSFSPopoutPanelManager
|
||||
|
@ -11,10 +13,28 @@ namespace MSFSPopoutPanelManager
|
|||
[STAThread]
|
||||
static void Main()
|
||||
{
|
||||
Application.SetHighDpiMode(HighDpiMode.SystemAware);
|
||||
Application.EnableVisualStyles();
|
||||
Application.SetCompatibleTextRenderingDefault(false);
|
||||
Application.Run(new StartupForm());
|
||||
bool createNew;
|
||||
|
||||
using var mutex = new Mutex(true, typeof(Program).Namespace, out createNew);
|
||||
|
||||
if (createNew)
|
||||
{
|
||||
Application.SetHighDpiMode(HighDpiMode.SystemAware);
|
||||
Application.EnableVisualStyles();
|
||||
Application.SetCompatibleTextRenderingDefault(false);
|
||||
Application.Run(new StartupForm());
|
||||
}
|
||||
else
|
||||
{
|
||||
var current = Process.GetCurrentProcess();
|
||||
|
||||
foreach (var process in Process.GetProcessesByName(current.ProcessName))
|
||||
{
|
||||
if (process.Id == current.Id) continue;
|
||||
PInvoke.SetForegroundWindow(process.MainWindowHandle);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -116,10 +116,9 @@ The user created plane profile and associate image recognition data are stored i
|
|||
## Common Problem Resolution
|
||||
|
||||
- Unable to pop out windows correctly - the predefined pop out panel coordinate may not line up correctly or movement of mouse is interfering with pop out execution. Please try to reposition the screen into pop out coordinates. Or you can close and restart the application, close all the opened pop outs, and try the analysis again.
|
||||
- Pop out windows are not recognized correctly - it is the current limitation of current implementation of image recognition algorithm. More sophisticated image recognition such as [SIFT](https://en.wikipedia.org/wiki/Scale-invariant_feature_transform) will be needed. This is to-do item on my list for future version of app. Also, the panel screen maybe blank which causes the image recognition engine to fail.
|
||||
- Night time or different world location causes image recognition to fail - application has builtin redundancy image recognition data for this purpose (such as MFD recognition for G1000 where 75% of the screen is a map). But I may not have anticipate all the use cases yet. Please provide feedback and it will help me to improve the image recognition engine.
|
||||
- Running on non-native monitor resolution does not work - for example running 1080p window resolution on 1440p monitor will not work because of image scaling and calculation issue. But in-game resolution scaling will not get affected. This issue can only be fixed when using scale invariant algorithm or more advanced algorithm.
|
||||
|
||||
- Pop out windows are not recognized correctly - it is limitation of current implementation of image recognition algorithm. More sophisticated image recognition such as [SIFT](https://en.wikipedia.org/wiki/Scale-invariant_feature_transform) will be needed. This is to-do item on my list for future version of app. Also, the panel screen maybe blank which causes the image recognition engine to fail.
|
||||
- Night time or different world location causes image recognition to fail - application has builtin redundancy image recognition data for this purpose (such as MFD recognition for G1000 where 75% of the screen is a map). But I may not have anticipate all the use cases yet. Please provide feedback and it will help me to improve the image recognition engine.
|
||||
|
||||
|
||||
## Author
|
||||
Stanley Kwok
|
||||
|
|
22
UI/StartupForm.Designer.cs
generated
22
UI/StartupForm.Designer.cs
generated
|
@ -42,6 +42,7 @@ namespace MSFSPopoutPanelManager
|
|||
this.darkLabel2 = new DarkUI.Controls.DarkLabel();
|
||||
this.checkBoxMinimizeToTray = new DarkUI.Controls.DarkCheckBox();
|
||||
this.lblVersion = new DarkUI.Controls.DarkLabel();
|
||||
this.checkBoxAutoStart = new DarkUI.Controls.DarkCheckBox();
|
||||
this.panelStatus.SuspendLayout();
|
||||
this.SuspendLayout();
|
||||
//
|
||||
|
@ -72,7 +73,7 @@ namespace MSFSPopoutPanelManager
|
|||
this.labelMsfsRunning.AutoSize = true;
|
||||
this.labelMsfsRunning.Font = new System.Drawing.Font("Segoe UI", 11F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point);
|
||||
this.labelMsfsRunning.ForeColor = System.Drawing.Color.Red;
|
||||
this.labelMsfsRunning.Location = new System.Drawing.Point(704, 553);
|
||||
this.labelMsfsRunning.Location = new System.Drawing.Point(704, 546);
|
||||
this.labelMsfsRunning.Name = "labelMsfsRunning";
|
||||
this.labelMsfsRunning.RightToLeft = System.Windows.Forms.RightToLeft.Yes;
|
||||
this.labelMsfsRunning.Size = new System.Drawing.Size(143, 20);
|
||||
|
@ -147,7 +148,7 @@ namespace MSFSPopoutPanelManager
|
|||
//
|
||||
this.checkBoxMinimizeToTray.AutoSize = true;
|
||||
this.checkBoxMinimizeToTray.Font = new System.Drawing.Font("Segoe UI", 11.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point);
|
||||
this.checkBoxMinimizeToTray.Location = new System.Drawing.Point(13, 552);
|
||||
this.checkBoxMinimizeToTray.Location = new System.Drawing.Point(13, 546);
|
||||
this.checkBoxMinimizeToTray.Name = "checkBoxMinimizeToTray";
|
||||
this.checkBoxMinimizeToTray.Size = new System.Drawing.Size(189, 24);
|
||||
this.checkBoxMinimizeToTray.TabIndex = 23;
|
||||
|
@ -157,17 +158,29 @@ namespace MSFSPopoutPanelManager
|
|||
//
|
||||
this.lblVersion.AutoSize = true;
|
||||
this.lblVersion.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(220)))), ((int)(((byte)(220)))), ((int)(((byte)(220)))));
|
||||
this.lblVersion.Location = new System.Drawing.Point(383, 561);
|
||||
this.lblVersion.Location = new System.Drawing.Point(383, 572);
|
||||
this.lblVersion.Name = "lblVersion";
|
||||
this.lblVersion.Size = new System.Drawing.Size(48, 15);
|
||||
this.lblVersion.TabIndex = 24;
|
||||
this.lblVersion.Text = "Version ";
|
||||
//
|
||||
// checkBoxAutoStart
|
||||
//
|
||||
this.checkBoxAutoStart.AutoSize = true;
|
||||
this.checkBoxAutoStart.Font = new System.Drawing.Font("Segoe UI", 11.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point);
|
||||
this.checkBoxAutoStart.Location = new System.Drawing.Point(222, 546);
|
||||
this.checkBoxAutoStart.Name = "checkBoxAutoStart";
|
||||
this.checkBoxAutoStart.Size = new System.Drawing.Size(95, 24);
|
||||
this.checkBoxAutoStart.TabIndex = 25;
|
||||
this.checkBoxAutoStart.Text = "Auto Start";
|
||||
this.checkBoxAutoStart.CheckedChanged += new System.EventHandler(this.checkBoxAutoStart_CheckedChanged);
|
||||
//
|
||||
// StartupForm
|
||||
//
|
||||
this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F);
|
||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||
this.ClientSize = new System.Drawing.Size(859, 583);
|
||||
this.ClientSize = new System.Drawing.Size(859, 591);
|
||||
this.Controls.Add(this.checkBoxAutoStart);
|
||||
this.Controls.Add(this.lblVersion);
|
||||
this.Controls.Add(this.checkBoxMinimizeToTray);
|
||||
this.Controls.Add(this.darkLabel2);
|
||||
|
@ -205,5 +218,6 @@ namespace MSFSPopoutPanelManager
|
|||
private DarkUI.Controls.DarkCheckBox checkBoxMinimizeToTray;
|
||||
private DarkUI.Controls.DarkLabel lblVersion;
|
||||
private DarkUI.Controls.DarkLabel darkLabel3;
|
||||
private DarkUI.Controls.DarkCheckBox checkBoxAutoStart;
|
||||
}
|
||||
}
|
|
@ -1,6 +1,5 @@
|
|||
using DarkUI.Forms;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Drawing;
|
||||
using System.Threading;
|
||||
|
@ -41,6 +40,8 @@ namespace MSFSPopoutPanelManager
|
|||
|
||||
_panelManager.OnAnalysisCompleted += (source, e) => { _ucPanelSelection.Visible = false; _ucApplySettings.Visible = true; };
|
||||
_panelManager.CheckSimulatorStarted();
|
||||
|
||||
checkBoxAutoStart.Checked = Autostart.CheckIsAutoStart();
|
||||
}
|
||||
|
||||
private void Logger_OnStatusLogged(object sender, EventArgs<StatusMessage> e)
|
||||
|
@ -101,6 +102,14 @@ namespace MSFSPopoutPanelManager
|
|||
|
||||
Process.Start(new ProcessStartInfo("https://github.com/hawkeye-stan/msfs-popout-panel-manager") { UseShellExecute = true });
|
||||
}
|
||||
|
||||
private void checkBoxAutoStart_CheckedChanged(object sender, EventArgs e)
|
||||
{
|
||||
if (checkBoxAutoStart.Checked)
|
||||
Autostart.Activate();
|
||||
else
|
||||
Autostart.Deactivate();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,14 @@
|
|||
# Version History
|
||||
<hr/>
|
||||
|
||||
## Vesion 2.2.0.0
|
||||
* Disabled ability to launch multiple instances of the application.
|
||||
* Added autostart feature when MSFS starts. The application will create or modify exe.xml. A backup copy of exe.xml will be created.
|
||||
* Added better support for 4K display resolution and non-standard display resolution.
|
||||
* Windows OS display resolution and in-game display resolution no longer have to match.
|
||||
* Improved panel pop out separation accuracy and performance.
|
||||
* Updated application packaging to single file executable to reduce file clutter.
|
||||
|
||||
## Vesion 2.1.1.0
|
||||
* Fixed panel separation issue for super ultrawide monitor (for example: 3840x1080)
|
||||
|
||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 3.6 KiB |
Binary file not shown.
Before Width: | Height: | Size: 4.4 KiB |
Binary file not shown.
Before Width: | Height: | Size: 8.7 KiB |
Binary file not shown.
Before Width: | Height: | Size: 50 KiB After Width: | Height: | Size: 50 KiB |
Loading…
Reference in a new issue