1
0
Fork 0
mirror of https://github.com/hawkeye-stan/msfs-popout-panel-manager.git synced 2024-10-16 06:00:45 +00:00

Added dynamic lod with auto fps

This commit is contained in:
hawkeye 2024-03-14 18:50:32 -04:00
parent b0a6079800
commit c68ec210d1
28 changed files with 1066 additions and 1166 deletions

View file

@ -11,9 +11,9 @@
<PackageProjectUrl>https://github.com/hawkeye-stan/msfs-popout-panel-manager</PackageProjectUrl> <PackageProjectUrl>https://github.com/hawkeye-stan/msfs-popout-panel-manager</PackageProjectUrl>
<RootNamespace>MSFSPopoutPanelManager.DomainModel</RootNamespace> <RootNamespace>MSFSPopoutPanelManager.DomainModel</RootNamespace>
<Platforms>x64</Platforms> <Platforms>x64</Platforms>
<Version>4.1.0.4</Version> <Version>4.1.1.0</Version>
<AssemblyVersion>4.1.0.4</AssemblyVersion> <AssemblyVersion>4.1.1.0</AssemblyVersion>
<FileVersion>4.1.0.4</FileVersion> <FileVersion>4.1.1.0</FileVersion>
<RuntimeIdentifier>win-x64</RuntimeIdentifier> <RuntimeIdentifier>win-x64</RuntimeIdentifier>
<DebugType>Embedded</DebugType> <DebugType>Embedded</DebugType>
<Configurations>Debug;Release;Local</Configurations> <Configurations>Debug;Release;Local</Configurations>

View file

@ -0,0 +1,38 @@
using MSFSPopoutPanelManager.Shared;
namespace MSFSPopoutPanelManager.DomainModel.DynamicLod
{
public class DynamicLodSimData : ObservableObject
{
public int Fps { get; set; }
public int Tlod { get; set; }
public int Olod { get; set; }
public double Agl { get; set; }
public string CloudQuality { get; set; } = "N/A";
public double AltAboveGround { get; set; }
public double AltAboveGroundMinusCg { get; set; }
public double GroundVelocity { get; set; }
public bool PlaneOnGround { get; set; } = true;
public void Clear()
{
Fps = 0;
Tlod = 0;
Olod = 0;
Agl = 0;
CloudQuality = "N/A";
AltAboveGround = 0;
AltAboveGroundMinusCg = 0;
GroundVelocity = 0;
PlaneOnGround = true;
}
}
}

View file

@ -1,11 +0,0 @@
using MSFSPopoutPanelManager.Shared;
namespace MSFSPopoutPanelManager.DomainModel.DynamicLod
{
public class LodConfig : ObservableObject
{
public int Agl { get; set; }
public int Lod { get; set; }
}
}

View file

@ -1,72 +0,0 @@
using System.Collections.Generic;
using System.Collections.Specialized;
namespace MSFSPopoutPanelManager.DomainModel.DynamicLod
{
public class ObservableLodConfigLinkedList : LinkedList<LodConfig>, INotifyCollectionChanged
{
public event NotifyCollectionChangedEventHandler CollectionChanged;
public void OnNotifyCollectionChanged(NotifyCollectionChangedAction action, LodConfig item)
{
if (CollectionChanged == null)
return;
switch (action)
{
case NotifyCollectionChangedAction.Add:
case NotifyCollectionChangedAction.Remove:
case NotifyCollectionChangedAction.Reset:
CollectionChanged(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
break;
}
}
public new void AddFirst(LodConfig item)
{
base.AddFirst(item);
OnNotifyCollectionChanged(NotifyCollectionChangedAction.Add, item);
}
public new void AddLast(LodConfig item)
{
base.AddLast(item);
OnNotifyCollectionChanged(NotifyCollectionChangedAction.Add, item);
}
public new void AddBefore(LinkedListNode<LodConfig> node, LinkedListNode<LodConfig> newNode)
{
base.AddBefore(node, newNode);
OnNotifyCollectionChanged(NotifyCollectionChangedAction.Add, newNode.Value);
}
public new void AddAfter(LinkedListNode<LodConfig> node, LinkedListNode<LodConfig> newNode)
{
base.AddAfter(node, newNode);
OnNotifyCollectionChanged(NotifyCollectionChangedAction.Add, newNode.Value);
}
public new void Remove(LodConfig item)
{
base.Remove(item);
OnNotifyCollectionChanged(NotifyCollectionChangedAction.Remove, item);
}
public new void RemoveFirst()
{
if (First == null)
return;
base.RemoveFirst();
OnNotifyCollectionChanged(NotifyCollectionChangedAction.Remove, First.Value);
}
public new void RemoveLast()
{
if (Last == null)
return;
base.RemoveLast();
OnNotifyCollectionChanged(NotifyCollectionChangedAction.Remove, Last.Value);
}
}
}

View file

@ -1,8 +1,4 @@
using MSFSPopoutPanelManager.DomainModel.DynamicLod; using MSFSPopoutPanelManager.Shared;
using MSFSPopoutPanelManager.Shared;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Runtime.Serialization;
namespace MSFSPopoutPanelManager.DomainModel.Setting namespace MSFSPopoutPanelManager.DomainModel.Setting
{ {
@ -11,31 +7,13 @@ namespace MSFSPopoutPanelManager.DomainModel.Setting
public DynamicLodSetting() public DynamicLodSetting()
{ {
InitializeChildPropertyChangeBinding(); InitializeChildPropertyChangeBinding();
TlodConfigs.CollectionChanged += (_, e) =>
{
if (e.Action == NotifyCollectionChangedAction.Add)
{
if (e.NewItems?[0] is LodConfig item)
item.PropertyChanged += (_, _) => TlodConfigs.OnNotifyCollectionChanged(NotifyCollectionChangedAction.Reset, item);
}
};
OlodConfigs.CollectionChanged += (_, e) =>
{
if (e.Action == NotifyCollectionChangedAction.Add)
{
if (e.NewItems?[0] is LodConfig item)
item.PropertyChanged += (_, _) => OlodConfigs.OnNotifyCollectionChanged(NotifyCollectionChangedAction.Reset, item);
}
};
} }
public bool IsEnabled { get; set; } = false; public bool IsEnabled { get; set; } = false;
public ObservableLodConfigLinkedList TlodConfigs { get; set; } = new(); public bool PauseWhenMsfsLoseFocus { get; set; } = true;
public ObservableLodConfigLinkedList OlodConfigs { get; set; } = new(); public bool PauseOutsideCockpitView { get; set; } = true;
public bool ResetEnabled { get; set; } = false; public bool ResetEnabled { get; set; } = false;
@ -43,29 +21,28 @@ namespace MSFSPopoutPanelManager.DomainModel.Setting
public int ResetOlod { get; set; } = 100; public int ResetOlod { get; set; } = 100;
public void AddDefaultTLodConfigs() public int TargetedFps { get; set; } = 60;
{
TlodConfigs.AddLast(new LinkedListNode<LodConfig>(new LodConfig { Agl = 0, Lod = 100 }));
TlodConfigs.AddLast(new LinkedListNode<LodConfig>(new LodConfig { Agl = 5000, Lod = 200 }));
TlodConfigs.AddLast(new LinkedListNode<LodConfig>(new LodConfig { Agl = 10000, Lod = 300 }));
TlodConfigs.AddLast(new LinkedListNode<LodConfig>(new LodConfig { Agl = 20000, Lod = 400 }));
}
public void AddDefaultOLodConfigs() public int FpsTolerance { get; set; } = 5;
{
OlodConfigs.AddLast(new LinkedListNode<LodConfig>(new LodConfig { Agl = 0, Lod = 200 }));
OlodConfigs.AddLast(new LinkedListNode<LodConfig>(new LodConfig { Agl = 1000, Lod = 150 }));
OlodConfigs.AddLast(new LinkedListNode<LodConfig>(new LodConfig { Agl = 5000, Lod = 100 }));
}
[OnDeserialized] public bool TlodMinOnGround { get; set; } = true;
private void OnDeserialization(StreamingContext context)
{
foreach (var item in TlodConfigs)
item.PropertyChanged += (_, _) => TlodConfigs.OnNotifyCollectionChanged(NotifyCollectionChangedAction.Reset, item);
foreach (var item in OlodConfigs) public int AltTlodBase { get; set; } = 1000;
item.PropertyChanged += (_, _) => OlodConfigs.OnNotifyCollectionChanged(NotifyCollectionChangedAction.Reset, item);
} public int TlodMin { get; set; } = 50;
public int TlodMax { get; set; } = 400;
public int CloudRecoveryTlod { get; set; } = 100;
public bool DecreaseCloudQuality { get; set; } = true;
public int OlodTop { get; set; } = 20;
public int OlodBase { get; set; } = 200;
public int AltOlodBase { get; set; } = 1000;
public int AltOlodTop { get; set; } = 10000;
} }
} }

View file

@ -53,10 +53,11 @@ namespace MSFSPopoutPanelManager.MainApp
services.AddSingleton(s => new AppOrchestrator(SharedStorage, s.GetRequiredService<PanelConfigurationOrchestrator>(), s.GetRequiredService<FlightSimOrchestrator>(), s.GetRequiredService<HelpOrchestrator>(), s.GetRequiredService<KeyboardOrchestrator>())); services.AddSingleton(s => new AppOrchestrator(SharedStorage, s.GetRequiredService<PanelConfigurationOrchestrator>(), s.GetRequiredService<FlightSimOrchestrator>(), s.GetRequiredService<HelpOrchestrator>(), s.GetRequiredService<KeyboardOrchestrator>()));
services.AddSingleton(_ => new ProfileOrchestrator(SharedStorage)); services.AddSingleton(_ => new ProfileOrchestrator(SharedStorage));
services.AddSingleton(_ => new DynamicLodOrchestrator(SharedStorage));
services.AddSingleton(s => new PanelSourceOrchestrator(SharedStorage, s.GetRequiredService<FlightSimOrchestrator>())); services.AddSingleton(s => new PanelSourceOrchestrator(SharedStorage, s.GetRequiredService<FlightSimOrchestrator>()));
services.AddSingleton(s => new PanelPopOutOrchestrator(SharedStorage, s.GetRequiredService<FlightSimOrchestrator>(), s.GetRequiredService<PanelSourceOrchestrator>(), s.GetRequiredService<PanelConfigurationOrchestrator>(), s.GetRequiredService<KeyboardOrchestrator>())); services.AddSingleton(s => new PanelPopOutOrchestrator(SharedStorage, s.GetRequiredService<FlightSimOrchestrator>(), s.GetRequiredService<PanelSourceOrchestrator>(), s.GetRequiredService<PanelConfigurationOrchestrator>(), s.GetRequiredService<KeyboardOrchestrator>()));
services.AddSingleton(s => new PanelConfigurationOrchestrator(SharedStorage, s.GetRequiredService<FlightSimOrchestrator>(), s.GetRequiredService<KeyboardOrchestrator>())); services.AddSingleton(s => new PanelConfigurationOrchestrator(SharedStorage, s.GetRequiredService<FlightSimOrchestrator>(), s.GetRequiredService<KeyboardOrchestrator>()));
services.AddSingleton(_ => new FlightSimOrchestrator(SharedStorage)); services.AddSingleton(s => new FlightSimOrchestrator(SharedStorage, s.GetRequiredService<DynamicLodOrchestrator>()));
services.AddSingleton(_ => new KeyboardOrchestrator(SharedStorage)); services.AddSingleton(_ => new KeyboardOrchestrator(SharedStorage));
services.AddSingleton(_ => new HelpOrchestrator()); services.AddSingleton(_ => new HelpOrchestrator());

View file

@ -2,13 +2,13 @@
x:Class="MSFSPopoutPanelManager.MainApp.AppUserControl.Dialog.AddProfileDialog" x:Class="MSFSPopoutPanelManager.MainApp.AppUserControl.Dialog.AddProfileDialog"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:converter="clr-namespace:MSFSPopoutPanelManager.MainApp.Converter"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:MSFSPopoutPanelManager.MainApp" xmlns:local="clr-namespace:MSFSPopoutPanelManager.MainApp"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes" xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:viewmodel="clr-namespace:MSFSPopoutPanelManager.MainApp.ViewModel" xmlns:viewmodel="clr-namespace:MSFSPopoutPanelManager.MainApp.ViewModel"
xmlns:wpf="clr-namespace:MaterialDesignThemes.Wpf;assembly=MaterialDesignThemes.Wpf" xmlns:wpf="clr-namespace:MaterialDesignThemes.Wpf;assembly=MaterialDesignThemes.Wpf"
xmlns:converter="clr-namespace:MSFSPopoutPanelManager.MainApp.Converter"
Width="400" Width="400"
Height="210" Height="210"
DataContext="{Binding RelativeSource={RelativeSource Self}, Path=ViewModel}" DataContext="{Binding RelativeSource={RelativeSource Self}, Path=ViewModel}"

View file

@ -3,26 +3,33 @@
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mainApp="clr-namespace:MSFSPopoutPanelManager.MainApp"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes" xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:system="clr-namespace:System;assembly=System.Runtime" xmlns:system="clr-namespace:System;assembly=System.Runtime"
xmlns:viewModel="clr-namespace:MSFSPopoutPanelManager.MainApp.ViewModel" xmlns:viewModel="clr-namespace:MSFSPopoutPanelManager.MainApp.ViewModel"
xmlns:appUserControl="clr-namespace:MSFSPopoutPanelManager.MainApp.AppUserControl"
d:DesignHeight="800" d:DesignHeight="800"
d:DesignWidth="800" d:DesignWidth="800"
mc:Ignorable="d"> mc:Ignorable="d">
<UserControl.Resources> <UserControl.Resources>
<system:Double x:Key="IconSize">22</system:Double> <system:Double x:Key="IconSize">22</system:Double>
<system:Double x:Key="ButtonSize">22</system:Double> <system:Double x:Key="ButtonSize">22</system:Double>
<Style
x:Key="TextBlockHeading"
BasedOn="{StaticResource {x:Type TextBlock}}"
TargetType="TextBlock">
<Setter Property="TextWrapping" Value="Wrap" />
<Setter Property="Width" Value="Auto" />
<Setter Property="Margin" Value="0,5,0,0" />
<Setter Property="FontSize" Value="14" />
<Setter Property="FontWeight" Value="Bold" />
</Style>
<Style <Style
x:Key="TextBlockLabel" x:Key="TextBlockLabel"
BasedOn="{StaticResource {x:Type TextBlock}}" BasedOn="{StaticResource {x:Type TextBlock}}"
TargetType="TextBlock"> TargetType="TextBlock">
<Setter Property="FontSize" Value="14" /> <Setter Property="FontSize" Value="12" />
<Setter Property="TextWrapping" Value="Wrap" /> <Setter Property="TextWrapping" Value="Wrap" />
<Setter Property="Width" Value="650" /> <Setter Property="Margin" Value="0,7,0,0" />
<Setter Property="Margin" Value="5,0,0,0" />
<Setter Property="LineHeight" Value="20" /> <Setter Property="LineHeight" Value="20" />
</Style> </Style>
<Style <Style
@ -33,522 +40,238 @@
</Style> </Style>
</UserControl.Resources> </UserControl.Resources>
<Grid d:DataContext="{d:DesignInstance viewModel:ApplicationViewModel}"> <Grid d:DataContext="{d:DesignInstance viewModel:ApplicationViewModel}">
<WrapPanel Margin="30,10,0,0"> <WrapPanel Orientation="Vertical">
<!-- Terrain level of detail --> <WrapPanel Margin="120,10,0,0">
<WrapPanel Margin="20,0,0,0" Orientation="Vertical"> <GroupBox
<TextBlock
Margin="0,0,0,5"
HorizontalAlignment="Center"
FontSize="16"
FontWeight="Bold">
Terrain Level of Detail (TLOD)
</TextBlock>
<DataGrid
Name="TlodGrid"
Width="272"
Height="248"
Margin="0" Margin="0"
HorizontalAlignment="Center" Padding="5,2,5,2"
AutoGenerateColumns="False" Header="Sim Values"
BorderThickness="1" Style="{StaticResource MaterialDesignGroupBox}">
CanUserAddRows="False" <GroupBox.HeaderTemplate>
CanUserDeleteRows="False" <HierarchicalDataTemplate>
CanUserReorderColumns="False" <Label
CanUserResizeColumns="False" Height="12"
CanUserResizeRows="False" Margin="10,1,1,1"
CanUserSortColumns="False" Padding="0"
GridLinesVisibility="None" FontSize="12">
HeadersVisibility="Column" Sim Values
HorizontalScrollBarVisibility="Disabled" </Label>
ItemsSource="{Binding AppSettingData.ApplicationSetting.DynamicLodSetting.TlodConfigs}" </HierarchicalDataTemplate>
SelectionUnit="FullRow" </GroupBox.HeaderTemplate>
VerticalGridLinesBrush="#B9B9B9"> <WrapPanel Margin="3">
<DataGrid.RowStyle> <TextBox
<Style TargetType="DataGridRow"> Width="75"
<Setter Property="Background" Value="{x:Null}" /> Margin="20,0,0,0"
<Setter Property="BorderBrush" Value="{x:Null}" /> materialDesign:HintAssist.FloatingScale="0.75"
</Style> materialDesign:HintAssist.Hint="FPS"
</DataGrid.RowStyle> IsHitTestVisible="False"
<DataGrid.ColumnHeaderStyle> IsReadOnly="True"
<Style TargetType="DataGridColumnHeader"> PreviewTextInput="TxtBox_NumbersOnly"
<Setter Property="ContentTemplate"> Style="{StaticResource MaterialDesignFloatingHintTextBox}"
<Setter.Value> Text="{Binding FlightSimData.DynamicLodSimData.Fps, StringFormat='{}{0:0}'}" />
<DataTemplate> <TextBox
<TextBlock Width="75"
FontSize="14" Margin="20,0,0,0"
Text="{Binding}" materialDesign:HintAssist.FloatingScale="0.75"
TextAlignment="Center" materialDesign:HintAssist.Hint="TLOD"
TextWrapping="Wrap" /> IsHitTestVisible="False"
</DataTemplate> IsReadOnly="True"
</Setter.Value> PreviewTextInput="TxtBox_NumbersOnly"
</Setter> Style="{StaticResource MaterialDesignFloatingHintTextBox}"
<Setter Property="HorizontalContentAlignment" Value="Center" /> Text="{Binding FlightSimData.DynamicLodSimData.Tlod}" />
<Setter Property="Height" Value="30" /> <TextBox
<Setter Property="Background" Value="#FF576573" /> Width="75"
<Setter Property="BorderThickness" Value="1" /> Margin="20,0,0,0"
<Setter Property="BorderBrush" Value="#FFB9B9B9" /> materialDesign:HintAssist.FloatingScale="0.75"
</Style> materialDesign:HintAssist.Hint="OLOD"
</DataGrid.ColumnHeaderStyle> IsHitTestVisible="False"
<DataGrid.CellStyle> IsReadOnly="True"
<Style TargetType="DataGridCell"> PreviewTextInput="TxtBox_NumbersOnly"
<Setter Property="TextBlock.TextAlignment" Value="Center" /> Style="{StaticResource MaterialDesignFloatingHintTextBox}"
<Setter Property="BorderBrush" Value="#FFB9B9B9" /> Text="{Binding FlightSimData.DynamicLodSimData.Olod}" />
<Setter Property="Background" Value="Transparent" /> <TextBox
<Setter Property="Foreground" Value="White" /> Width="75"
</Style> Margin="20,0,0,0"
</DataGrid.CellStyle> materialDesign:HintAssist.FloatingScale="0.75"
<DataGrid.Columns> materialDesign:HintAssist.Hint="AGL"
<DataGridTemplateColumn Width="100" Header="AGL"> IsHitTestVisible="False"
<DataGridTemplateColumn.CellTemplate> IsReadOnly="True"
<DataTemplate> PreviewTextInput="TxtBox_NumbersOnly"
<TextBox Style="{StaticResource MaterialDesignFloatingHintTextBox}"
Width="100" Text="{Binding FlightSimData.DynamicLodSimData.Agl, StringFormat='{}{0:0}'}" />
materialDesign:ValidationAssist.HorizontalAlignment="Center" <TextBox
BorderThickness="0" Width="75"
FontSize="14"> Margin="20,0,0,0"
<TextBox.Text> materialDesign:HintAssist.FloatingScale="0.75"
<Binding materialDesign:HintAssist.Hint="Clouds"
Mode="TwoWay" IsHitTestVisible="False"
Path="Agl" IsReadOnly="True"
UpdateSourceTrigger="LostFocus"> PreviewTextInput="TxtBox_NumbersOnly"
<Binding.ValidationRules> Style="{StaticResource MaterialDesignFloatingHintTextBox}"
<appUserControl:AglValidationRule /> Text="{Binding FlightSimData.DynamicLodSimData.CloudQuality}" />
</Binding.ValidationRules> </WrapPanel>
</Binding> </GroupBox>
</TextBox.Text>
</TextBox>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Width="100" Header="LOD">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBox
Width="100"
materialDesign:ValidationAssist.HorizontalAlignment="Center"
BorderThickness="0"
FontSize="14">
<TextBox.Text>
<Binding
Mode="TwoWay"
Path="Lod"
UpdateSourceTrigger="LostFocus">
<Binding.ValidationRules>
<appUserControl:LodValidationRule />
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Width="70" Header="">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Button
Width="{StaticResource ButtonSize}"
Height="{StaticResource ButtonSize}"
Margin="0"
HorizontalAlignment="Center"
Click="TLodDelete_Click"
KeyboardNavigation.AcceptsReturn="False"
Style="{StaticResource MaterialDesignIconForegroundButton}"
ToolTip="Delete TLOD configuration">
<materialDesign:PackIcon
Width="{StaticResource IconSize}"
Height="{StaticResource IconSize}"
Kind="Delete" />
</Button>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
<WrapPanel Margin="0,5,0,0">
<DataGrid
Name="AddTlodGrid"
Width="272"
Height="50"
Margin="0"
HorizontalAlignment="Center"
AutoGenerateColumns="False"
BorderThickness="0"
CanUserAddRows="False"
CanUserDeleteRows="False"
CanUserReorderColumns="False"
CanUserResizeColumns="False"
CanUserResizeRows="False"
CanUserSortColumns="False"
DataContext="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}}"
GridLinesVisibility="None"
HeadersVisibility="None"
HorizontalScrollBarVisibility="Disabled"
ItemsSource="{Binding AddTlodConfigs}"
SelectionUnit="FullRow"
VerticalGridLinesBrush="#B9B9B9"
VerticalScrollBarVisibility="Disabled">
<DataGrid.RowStyle>
<Style TargetType="DataGridRow">
<Setter Property="Background" Value="{x:Null}" />
<Setter Property="BorderBrush" Value="{x:Null}" />
</Style>
</DataGrid.RowStyle>
<DataGrid.CellStyle>
<Style TargetType="DataGridCell">
<Setter Property="TextBlock.TextAlignment" Value="Center" />
<Setter Property="BorderBrush" Value="#FFB9B9B9" />
<Setter Property="Background" Value="Transparent" />
<Setter Property="Foreground" Value="White" />
</Style>
</DataGrid.CellStyle>
<DataGrid.Columns>
<DataGridTemplateColumn Width="100" Header="AGL">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBox
Width="100"
materialDesign:HintAssist.Hint="New AGL"
materialDesign:ValidationAssist.HorizontalAlignment="Center"
BorderThickness="0"
FontSize="14"
SourceUpdated="AddTlod_SourceUpdated">
<TextBox.Text>
<Binding
Mode="TwoWay"
NotifyOnSourceUpdated="True"
Path="Agl"
UpdateSourceTrigger="LostFocus">
<Binding.ValidationRules>
<appUserControl:AglValidationRule />
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Width="100" Header="LOD">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBox
Width="100"
materialDesign:HintAssist.Hint="New LOD"
materialDesign:ValidationAssist.HorizontalAlignment="Center"
BorderThickness="0"
FontSize="14"
SourceUpdated="AddTlod_SourceUpdated">
<TextBox.Text>
<Binding
Mode="TwoWay"
NotifyOnSourceUpdated="True"
Path="Lod"
UpdateSourceTrigger="LostFocus">
<Binding.ValidationRules>
<appUserControl:LodValidationRule />
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Width="70" Header="">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<materialDesign:PackIcon
Width="{StaticResource IconSize}"
Height="{StaticResource IconSize}"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Foreground="White"
Kind="Add" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
</WrapPanel>
</WrapPanel> </WrapPanel>
<WrapPanel Margin="47,10,0,0" Orientation="Vertical">
<!-- Object level of detail --> <WrapPanel Margin="0,0,0,0">
<WrapPanel Margin="50,0,0,0" Orientation="Vertical"> <TextBlock Style="{StaticResource TextBlockHeading}">General Options</TextBlock>
<TextBlock <Line
Margin="0,0,0,5" Stretch="Fill"
HorizontalAlignment="Center" Stroke="Gray"
FontSize="16" X2="1" />
FontWeight="Bold">
Object Level of Detail (OLOD)
</TextBlock>
<DataGrid
Name="OlodGrid"
Width="272"
Height="248"
Margin="0"
HorizontalAlignment="Center"
AutoGenerateColumns="False"
BorderThickness="1"
CanUserAddRows="False"
CanUserDeleteRows="False"
CanUserReorderColumns="False"
CanUserResizeColumns="False"
CanUserResizeRows="False"
CanUserSortColumns="False"
GridLinesVisibility="None"
HeadersVisibility="Column"
HorizontalScrollBarVisibility="Disabled"
ItemsSource="{Binding AppSettingData.ApplicationSetting.DynamicLodSetting.OlodConfigs}"
SelectionUnit="FullRow"
VerticalGridLinesBrush="#B9B9B9">
<DataGrid.RowStyle>
<Style TargetType="DataGridRow">
<Setter Property="Background" Value="{x:Null}" />
<Setter Property="BorderBrush" Value="{x:Null}" />
</Style>
</DataGrid.RowStyle>
<DataGrid.ColumnHeaderStyle>
<Style TargetType="DataGridColumnHeader">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<TextBlock
FontSize="14"
Text="{Binding}"
TextAlignment="Center"
TextWrapping="Wrap" />
</DataTemplate>
</Setter.Value>
</Setter>
<Setter Property="HorizontalContentAlignment" Value="Center" />
<Setter Property="Height" Value="30" />
<Setter Property="Background" Value="#FF576573" />
<Setter Property="BorderThickness" Value="1" />
<Setter Property="BorderBrush" Value="#FFB9B9B9" />
</Style>
</DataGrid.ColumnHeaderStyle>
<DataGrid.CellStyle>
<Style TargetType="DataGridCell">
<Setter Property="TextBlock.TextAlignment" Value="Center" />
<Setter Property="BorderBrush" Value="#FFB9B9B9" />
<Setter Property="Background" Value="Transparent" />
<Setter Property="Foreground" Value="White" />
</Style>
</DataGrid.CellStyle>
<DataGrid.Columns>
<DataGridTemplateColumn Width="100" Header="AGL">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBox
Width="100"
materialDesign:ValidationAssist.HorizontalAlignment="Center"
BorderThickness="0"
FontSize="14">
<TextBox.Text>
<Binding
Mode="TwoWay"
Path="Agl"
UpdateSourceTrigger="LostFocus">
<Binding.ValidationRules>
<appUserControl:AglValidationRule />
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Width="100" Header="LOD">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBox
Width="100"
materialDesign:ValidationAssist.HorizontalAlignment="Center"
BorderThickness="0"
FontSize="14">
<TextBox.Text>
<Binding
Mode="TwoWay"
Path="Lod"
UpdateSourceTrigger="LostFocus">
<Binding.ValidationRules>
<appUserControl:LodValidationRule />
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Width="70" Header="">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Button
Width="{StaticResource ButtonSize}"
Height="{StaticResource ButtonSize}"
Margin="0"
HorizontalAlignment="Center"
Click="OLodDelete_Click"
KeyboardNavigation.AcceptsReturn="False"
Style="{StaticResource MaterialDesignIconForegroundButton}"
ToolTip="Delete OLOD configuration">
<materialDesign:PackIcon
Width="{StaticResource IconSize}"
Height="{StaticResource IconSize}"
Kind="Delete" />
</Button>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
<WrapPanel Margin="0,5,0,0">
<DataGrid
Name="AddOlodGrid"
Width="272"
Height="50"
Margin="0"
HorizontalAlignment="Center"
AutoGenerateColumns="False"
BorderThickness="0"
CanUserAddRows="False"
CanUserDeleteRows="False"
CanUserReorderColumns="False"
CanUserResizeColumns="False"
CanUserResizeRows="False"
CanUserSortColumns="False"
DataContext="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}}"
GridLinesVisibility="None"
HeadersVisibility="None"
HorizontalScrollBarVisibility="Disabled"
ItemsSource="{Binding AddOlodConfigs}"
SelectionUnit="FullRow"
VerticalGridLinesBrush="#B9B9B9"
VerticalScrollBarVisibility="Disabled">
<DataGrid.RowStyle>
<Style TargetType="DataGridRow">
<Setter Property="Background" Value="{x:Null}" />
<Setter Property="BorderBrush" Value="{x:Null}" />
</Style>
</DataGrid.RowStyle>
<DataGrid.CellStyle>
<Style TargetType="DataGridCell">
<Setter Property="TextBlock.TextAlignment" Value="Center" />
<Setter Property="BorderBrush" Value="#FFB9B9B9" />
<Setter Property="Background" Value="Transparent" />
<Setter Property="Foreground" Value="White" />
</Style>
</DataGrid.CellStyle>
<DataGrid.Columns>
<DataGridTemplateColumn Width="100" Header="AGL">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBox
Width="100"
materialDesign:HintAssist.Hint="New AGL"
materialDesign:ValidationAssist.HorizontalAlignment="Center"
BorderThickness="0"
FontSize="14"
SourceUpdated="AddOlod_SourceUpdated">
<TextBox.Text>
<Binding
Mode="TwoWay"
NotifyOnSourceUpdated="True"
Path="Agl"
UpdateSourceTrigger="LostFocus">
<Binding.ValidationRules>
<appUserControl:AglValidationRule />
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Width="100" Header="LOD">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBox
Width="100"
materialDesign:HintAssist.Hint="New LOD"
materialDesign:ValidationAssist.HorizontalAlignment="Center"
BorderThickness="0"
FontSize="14"
SourceUpdated="AddOlod_SourceUpdated">
<TextBox.Text>
<Binding
Mode="TwoWay"
NotifyOnSourceUpdated="True"
Path="Lod"
UpdateSourceTrigger="LostFocus">
<Binding.ValidationRules>
<appUserControl:LodValidationRule />
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Width="70" Header="">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<materialDesign:PackIcon
Width="{StaticResource IconSize}"
Height="{StaticResource IconSize}"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Foreground="White"
Kind="Add" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
</WrapPanel>
</WrapPanel>
<WrapPanel Margin="0,20,0,0" Orientation="Horizontal">
<ToggleButton IsChecked="{Binding AppSettingData.ApplicationSetting.DynamicLodSetting.ResetEnabled, Mode=TwoWay}" Style="{StaticResource ToggleButton}" />
<TextBlock Style="{StaticResource TextBlockLabel}">
Enable reset of TLOD and OLOD to the following values when flight session ends.
</TextBlock>
<WrapPanel IsEnabled="{Binding AppSettingData.ApplicationSetting.DynamicLodSetting.ResetEnabled}" Orientation="Horizontal">
<TextBox <TextBox
Width="100" Width="100"
Height="40" Margin="0,0,0,0"
Margin="45,5,0,0" Padding="5,5,0,0"
VerticalAlignment="Center" materialDesign:HintAssist.FloatingScale="0.75"
HorizontalContentAlignment="Center" materialDesign:HintAssist.Foreground="White"
materialDesign:HintAssist.Hint="Reset TLOD" materialDesign:HintAssist.Hint="Targeted FPS"
materialDesign:ValidationAssist.HorizontalAlignment="Center" Background="#607D8B"
BorderThickness="1" PreviewTextInput="TxtBox_NumbersOnly"
FontSize="14" Style="{StaticResource MaterialDesignFloatingHintTextBox}"
Style="{StaticResource MaterialDesignFloatingHintTextBox}"> Text="{Binding AppSettingData.ApplicationSetting.DynamicLodSetting.TargetedFps, Mode=TwoWay}" />
<TextBox.Text> <TextBox
<Binding Mode="TwoWay" Path="AppSettingData.ApplicationSetting.DynamicLodSetting.ResetTlod"> Width="75"
<Binding.ValidationRules> Margin="40,0,0,0"
<appUserControl:LodValidationRule /> materialDesign:HintAssist.FloatingScale="0.75"
</Binding.ValidationRules> materialDesign:HintAssist.Hint="FPS Tolerance"
</Binding> materialDesign:TextFieldAssist.SuffixText="%"
</TextBox.Text> PreviewTextInput="TxtBox_NumbersOnly"
</TextBox> Style="{StaticResource MaterialDesignFloatingHintTextBox}"
Text="{Binding AppSettingData.ApplicationSetting.DynamicLodSetting.FpsTolerance, Mode=TwoWay}" />
<WrapPanel Margin="40,10,0,0">
<ToggleButton IsChecked="{Binding AppSettingData.ApplicationSetting.DynamicLodSetting.PauseWhenMsfsLoseFocus, Mode=TwoWay}" Style="{StaticResource ToggleButton}" />
<TextBlock Style="{StaticResource TextBlockLabel}">Pause when MSFS loses focus</TextBlock>
</WrapPanel>
<WrapPanel Margin="20,10,0,0">
<ToggleButton IsChecked="{Binding AppSettingData.ApplicationSetting.DynamicLodSetting.PauseOutsideCockpitView, Mode=TwoWay}" Style="{StaticResource ToggleButton}" />
<TextBlock Style="{StaticResource TextBlockLabel}">Pause when not in cockpit view</TextBlock>
</WrapPanel>
</WrapPanel>
<WrapPanel Margin="0,15,0,0">
<TextBlock Style="{StaticResource TextBlockHeading}">TLOD Options</TextBlock>
<Line
Stretch="Fill"
Stroke="Gray"
X2="1" />
<TextBox
Width="75"
Margin="0,0,0,0"
materialDesign:HintAssist.FloatingScale="0.75"
materialDesign:HintAssist.Hint="TLOD Minimum"
PreviewTextInput="TxtBox_NumbersOnly"
Style="{StaticResource MaterialDesignFloatingHintTextBox}"
Text="{Binding AppSettingData.ApplicationSetting.DynamicLodSetting.TlodMin, Mode=TwoWay}" />
<TextBox
Width="75"
Margin="40,0,0,0"
materialDesign:HintAssist.FloatingScale="0.75"
materialDesign:HintAssist.Hint="TLOD Maximum"
PreviewTextInput="TxtBox_NumbersOnly"
Style="{StaticResource MaterialDesignFloatingHintTextBox}"
Text="{Binding AppSettingData.ApplicationSetting.DynamicLodSetting.TlodMax, Mode=TwoWay}" />
<TextBox
Width="75"
Margin="40,0,0,0"
materialDesign:HintAssist.FloatingScale="0.75"
materialDesign:HintAssist.Hint="TLOD Base Alt"
materialDesign:TextFieldAssist.SuffixText="ft"
PreviewTextInput="TxtBox_NumbersOnly"
Style="{StaticResource MaterialDesignFloatingHintTextBox}"
Text="{Binding AppSettingData.ApplicationSetting.DynamicLodSetting.AltTlodBase, Mode=TwoWay}" />
<WrapPanel Margin="40,10,0,0">
<ToggleButton IsChecked="{Binding AppSettingData.ApplicationSetting.DynamicLodSetting.TlodMinOnGround, Mode=TwoWay}" Style="{StaticResource ToggleButton}" />
<TextBlock Style="{StaticResource TextBlockLabel}">TLOD minimum on ground/TLOD Base Altitude</TextBlock>
</WrapPanel>
</WrapPanel>
<WrapPanel Margin="0,15,0,0">
<TextBlock Style="{StaticResource TextBlockHeading}">OLOD Options</TextBlock>
<Line
Stretch="Fill"
Stroke="Gray"
X2="1" />
<TextBox
Width="75"
Margin="0,0,0,0"
materialDesign:HintAssist.FloatingScale="0.75"
materialDesign:HintAssist.Hint="OLOD at Base Alt"
PreviewTextInput="TxtBox_NumbersOnly"
Style="{StaticResource MaterialDesignFloatingHintTextBox}"
Text="{Binding AppSettingData.ApplicationSetting.DynamicLodSetting.OlodBase, Mode=TwoWay}" />
<TextBox
Width="75"
Margin="40,0,0,0"
materialDesign:HintAssist.FloatingScale="0.75"
materialDesign:HintAssist.Hint="OLOD at Top Alt"
PreviewTextInput="TxtBox_NumbersOnly"
Style="{StaticResource MaterialDesignFloatingHintTextBox}"
Text="{Binding AppSettingData.ApplicationSetting.DynamicLodSetting.OlodTop, Mode=TwoWay}" />
<TextBox
Width="75"
Margin="40,0,0,0"
materialDesign:HintAssist.FloatingScale="0.75"
materialDesign:HintAssist.Hint="OLOD Base Alt"
materialDesign:TextFieldAssist.SuffixText="ft"
PreviewTextInput="TxtBox_NumbersOnly"
Style="{StaticResource MaterialDesignFloatingHintTextBox}"
Text="{Binding AppSettingData.ApplicationSetting.DynamicLodSetting.AltOlodBase, Mode=TwoWay}" />
<TextBox
Width="75"
Margin="40,0,0,0"
materialDesign:HintAssist.FloatingScale="0.75"
materialDesign:HintAssist.Hint="OLOD Top Alt"
materialDesign:TextFieldAssist.SuffixText="ft"
PreviewTextInput="TxtBox_NumbersOnly"
Style="{StaticResource MaterialDesignFloatingHintTextBox}"
Text="{Binding AppSettingData.ApplicationSetting.DynamicLodSetting.AltOlodTop, Mode=TwoWay}" />
</WrapPanel>
<WrapPanel Margin="0,15,0,0">
<TextBlock Style="{StaticResource TextBlockHeading}">Cloud Options</TextBlock>
<Line
Stretch="Fill"
Stroke="Gray"
X2="1" />
<TextBox <TextBox
Width="100" Width="100"
Height="40" Margin="0,0,0,0"
Margin="20,5,0,0" materialDesign:HintAssist.FloatingScale="0.75"
VerticalAlignment="Center" materialDesign:HintAssist.Hint="Cloud Adjust at TLOD"
HorizontalContentAlignment="Center" PreviewTextInput="TxtBox_NumbersOnly"
materialDesign:HintAssist.Hint="Reset OLOD" Style="{StaticResource MaterialDesignFloatingHintTextBox}"
materialDesign:ValidationAssist.HorizontalAlignment="Center" Text="{Binding AppSettingData.ApplicationSetting.DynamicLodSetting.CloudRecoveryTlod, Mode=TwoWay}" />
BorderThickness="1" <WrapPanel Margin="20,10,0,0">
FontSize="14" <ToggleButton IsChecked="{Binding AppSettingData.ApplicationSetting.DynamicLodSetting.DecreaseCloudQuality, Mode=TwoWay}" Style="{StaticResource ToggleButton}" />
Style="{StaticResource MaterialDesignFloatingHintTextBox}"> <TextBlock Style="{StaticResource TextBlockLabel}">Decrease cloud quality by one level to archive desired FPS</TextBlock>
<TextBox.Text> </WrapPanel>
<Binding Mode="TwoWay" Path="AppSettingData.ApplicationSetting.DynamicLodSetting.ResetOlod"> </WrapPanel>
<Binding.ValidationRules> <WrapPanel Margin="0,15,0,0">
<appUserControl:LodValidationRule /> <TextBlock Style="{StaticResource TextBlockHeading}">Reset TLOD and OLOD on Exit</TextBlock>
</Binding.ValidationRules> <Line
</Binding> Stretch="Fill"
</TextBox.Text> Stroke="Gray"
</TextBox> X2="1" />
<WrapPanel Margin="0,0,0,0">
<WrapPanel Margin="0,7,0,0">
<ToggleButton IsChecked="{Binding AppSettingData.ApplicationSetting.DynamicLodSetting.ResetEnabled, Mode=TwoWay}" Style="{StaticResource ToggleButton}" />
<TextBlock Style="{StaticResource TextBlockLabel}">Reset on Exit</TextBlock>
</WrapPanel>
<TextBox
Width="75"
Margin="40,0,0,0"
materialDesign:HintAssist.FloatingScale="0.75"
materialDesign:HintAssist.Hint="TLOD"
PreviewTextInput="TxtBox_NumbersOnly"
Style="{StaticResource MaterialDesignFloatingHintTextBox}"
Text="{Binding AppSettingData.ApplicationSetting.DynamicLodSetting.ResetTlod, Mode=TwoWay}" />
<TextBox
Width="75"
Margin="40,0,0,0"
materialDesign:HintAssist.FloatingScale="0.75"
materialDesign:HintAssist.Hint="OLOD"
PreviewTextInput="TxtBox_NumbersOnly"
Style="{StaticResource MaterialDesignFloatingHintTextBox}"
Text="{Binding AppSettingData.ApplicationSetting.DynamicLodSetting.ResetOlod, Mode=TwoWay}" />
</WrapPanel>
</WrapPanel> </WrapPanel>
</WrapPanel> </WrapPanel>
</WrapPanel> </WrapPanel>

View file

@ -1,22 +1,14 @@
using System; using MSFSPopoutPanelManager.MainApp.ViewModel;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel; using System.ComponentModel;
using System.Globalization; using System.Globalization;
using System.Linq;
using System.Windows; using System.Windows;
using System.Windows.Controls; using System.Windows.Controls;
using System.Windows.Data; using System.Windows.Input;
using MSFSPopoutPanelManager.DomainModel.DynamicLod;
using MSFSPopoutPanelManager.MainApp.ViewModel;
namespace MSFSPopoutPanelManager.MainApp.AppUserControl namespace MSFSPopoutPanelManager.MainApp.AppUserControl
{ {
public partial class DynamicLodPreference public partial class DynamicLodPreference
{ {
private ObservableLodConfigLinkedList _tlodConfigs;
private ObservableLodConfigLinkedList _olodConfigs;
public DynamicLodPreference() public DynamicLodPreference()
{ {
if (DesignerProperties.GetIsInDesignMode(new DependencyObject())) if (DesignerProperties.GetIsInDesignMode(new DependencyObject()))
@ -24,146 +16,21 @@ namespace MSFSPopoutPanelManager.MainApp.AppUserControl
InitializeComponent(); InitializeComponent();
return; return;
} }
AddTlodConfigs = new ObservableCollection<TempLodConfig>() { new () };
AddOlodConfigs = new ObservableCollection<TempLodConfig>() { new () };
Loaded += (_, _) => Loaded += (_, _) =>
{ {
InitializeComponent(); InitializeComponent();
var dataContext = DataContext as ApplicationViewModel; var dataContext = DataContext as ApplicationViewModel;
_tlodConfigs = dataContext?.AppSettingData.ApplicationSetting.DynamicLodSetting.TlodConfigs;
_olodConfigs = dataContext?.AppSettingData.ApplicationSetting.DynamicLodSetting.OlodConfigs;
}; };
} }
public ObservableCollection<TempLodConfig> AddTlodConfigs { get; set; }
public ObservableCollection<TempLodConfig> AddOlodConfigs { get; set; } private void TxtBox_NumbersOnly(object sender, TextCompositionEventArgs e)
private void AddTlod_SourceUpdated(object sender, DataTransferEventArgs e)
{ {
var textBox = sender as TextBox; e.Handled = !(int.TryParse(e.Text, out _) || (e.Text.Trim() == "-"));
var lodConfig = textBox?.DataContext as TempLodConfig;
if (lodConfig?.Agl == null || lodConfig.Lod == null)
return;
if (UpdateTlodDuplicate(lodConfig))
{
RebindTLodGrid();
return;
}
var targetLodConfig = _tlodConfigs.LastOrDefault(x => lodConfig.Agl >= x.Agl);
var newLodConfig = new LodConfig { Agl = (int)lodConfig.Agl, Lod = (int)lodConfig.Lod };
if (targetLodConfig == null)
_tlodConfigs.AddFirst(newLodConfig);
else
_tlodConfigs.AddAfter(_tlodConfigs.Find(targetLodConfig), new LinkedListNode<LodConfig>(newLodConfig));
RebindTLodGrid();
}
private void TLodDelete_Click(object sender, RoutedEventArgs e)
{
var button = e.Source as Button;
if(button?.DataContext is not LodConfig lodConfig)
return;
_tlodConfigs.Remove(lodConfig);
RebindTLodGrid();
}
private void RebindTLodGrid()
{
this.TlodGrid.ItemsSource = null;
this.TlodGrid.ItemsSource = _tlodConfigs.ToList();
AddTlodConfigs.Clear();
AddTlodConfigs.Add(new TempLodConfig());
}
private bool UpdateTlodDuplicate(TempLodConfig lodConfig)
{
var tlodConfig = _tlodConfigs.FirstOrDefault(x => x.Agl == lodConfig.Agl);
if(tlodConfig == null)
return false;
tlodConfig.Lod = Convert.ToInt32(lodConfig.Lod);
return true;
}
private void AddOlod_SourceUpdated(object sender, DataTransferEventArgs e)
{
var textBox = sender as TextBox;
var lodConfig = textBox?.DataContext as TempLodConfig;
if (lodConfig?.Agl == null || lodConfig.Lod == null)
return;
if (UpdateOlodDuplicate(lodConfig))
{
RebindOLodGrid();
return;
}
var targetLodConfig = _olodConfigs.LastOrDefault(x => lodConfig.Agl >= x.Agl);
var newLodConfig = new LodConfig() { Agl = (int)lodConfig.Agl, Lod = (int)lodConfig.Lod };
if (targetLodConfig == null)
_olodConfigs.AddFirst(newLodConfig);
else
_olodConfigs.AddAfter(_olodConfigs.Find(targetLodConfig), new LinkedListNode<LodConfig>(newLodConfig));
RebindOLodGrid();
}
private void OLodDelete_Click(object sender, RoutedEventArgs e)
{
var button = e.Source as Button;
if (button?.DataContext is not LodConfig lodConfig)
return;
_olodConfigs.Remove(lodConfig);
RebindOLodGrid();
}
private void RebindOLodGrid()
{
this.OlodGrid.ItemsSource = null;
this.OlodGrid.ItemsSource = _olodConfigs.ToList();
AddOlodConfigs.Clear();
AddOlodConfigs.Add(new TempLodConfig());
}
private bool UpdateOlodDuplicate(TempLodConfig lodConfig)
{
var olodConfig = _olodConfigs.FirstOrDefault(x => x.Agl == lodConfig.Agl);
if (olodConfig == null)
return false;
olodConfig.Lod = Convert.ToInt32(lodConfig.Lod);
return true;
} }
} }
public class TempLodConfig
{
public int? Agl { get; set; }
public int? Lod { get; set; }
}
public class AglValidationRule : ValidationRule public class AglValidationRule : ValidationRule
{ {

View file

@ -633,21 +633,23 @@
<WrapPanel Visibility="{c:Binding LocalCompileOnly}"> <WrapPanel Visibility="{c:Binding LocalCompileOnly}">
<WrapPanel Orientation="Vertical" Visibility="{Binding ElementName=CategoryDynamicLodSettings, Path=IsSelected, Converter={StaticResource BooleanToVisibilityConverter}, Mode=OneWay}"> <WrapPanel Orientation="Vertical" Visibility="{Binding ElementName=CategoryDynamicLodSettings, Path=IsSelected, Converter={StaticResource BooleanToVisibilityConverter}, Mode=OneWay}">
<WrapPanel Margin="0,0,20,20" Orientation="Vertical"> <WrapPanel Margin="0,0,20,20" Orientation="Vertical">
<TextBlock Style="{StaticResource TextBlockHeading}">Dynamically adjust Terrain and Object Level of Details (Not Supported)</TextBlock> <WrapPanel>
<TextBlock
Padding="0,0,5,0"
Foreground="Red"
Style="{StaticResource TextBlockHeading}">
(Unsupported Feature)
</TextBlock>
<TextBlock Style="{StaticResource TextBlockHeading}">Dynamically adjust Terrain and Object Level of Details</TextBlock>
</WrapPanel>
<Line <Line
Stretch="Fill" Stretch="Fill"
Stroke="Gray" Stroke="Gray"
X2="1" /> X2="1" />
<TextBlock
Margin="0,10,0,10"
Foreground="Red"
Style="{StaticResource TextBlockLabel}">
*** Experimental and not supported. Use at your own risk since this feature directly modifies MSFS memory. ***
</TextBlock>
<WrapPanel> <WrapPanel>
<ToggleButton IsChecked="{Binding AppSettingData.ApplicationSetting.DynamicLodSetting.IsEnabled, Mode=TwoWay}" Style="{StaticResource ToggleButton}" /> <ToggleButton IsChecked="{Binding AppSettingData.ApplicationSetting.DynamicLodSetting.IsEnabled, Mode=TwoWay}" Style="{StaticResource ToggleButton}" />
<TextBlock Style="{StaticResource TextBlockLabel}"> <TextBlock Style="{StaticResource TextBlockLabel}">
Enable automatic adjustment of terrain level of detail (TLOD) and object level of detail (OLOD) based on current aircraft's above ground level (in feet). Enable automatic adjustments of TLOD and OLOD to get the desired targeted frame rate (FPS).
</TextBlock> </TextBlock>
</WrapPanel> </WrapPanel>
<WrapPanel Visibility="{Binding Path=AppSettingData.ApplicationSetting.DynamicLodSetting.IsEnabled, Converter={StaticResource BooleanToVisibilityConverter}, Mode=OneWay}"> <WrapPanel Visibility="{Binding Path=AppSettingData.ApplicationSetting.DynamicLodSetting.IsEnabled, Converter={StaticResource BooleanToVisibilityConverter}, Mode=OneWay}">

View file

@ -14,9 +14,9 @@
<RootNamespace>MSFSPopoutPanelManager.MainApp</RootNamespace> <RootNamespace>MSFSPopoutPanelManager.MainApp</RootNamespace>
<ApplicationIcon>logo.ico</ApplicationIcon> <ApplicationIcon>logo.ico</ApplicationIcon>
<Platforms>x64</Platforms> <Platforms>x64</Platforms>
<Version>4.1.0.4</Version> <Version>4.1.1.0</Version>
<AssemblyVersion>4.1.0.4</AssemblyVersion> <AssemblyVersion>4.1.1.0</AssemblyVersion>
<FileVersion>4.1.0.4</FileVersion> <FileVersion>4.1.1.0</FileVersion>
<DebugType>embedded</DebugType> <DebugType>embedded</DebugType>
<SatelliteResourceLanguages>en</SatelliteResourceLanguages> <SatelliteResourceLanguages>en</SatelliteResourceLanguages>
<!-- Publishing options --> <!-- Publishing options -->

View file

@ -33,15 +33,6 @@ namespace MSFSPopoutPanelManager.Orchestration
public void Initialize() public void Initialize()
{ {
// Add default dynamic LOD configs
if (!AppSettingData.ApplicationSetting.DynamicLodSetting.IsEnabled &&
AppSettingData.ApplicationSetting.DynamicLodSetting.TlodConfigs.Count == 0 &&
AppSettingData.ApplicationSetting.DynamicLodSetting.OlodConfigs.Count == 0)
{
AppSettingData.ApplicationSetting.DynamicLodSetting.AddDefaultTLodConfigs();
AppSettingData.ApplicationSetting.DynamicLodSetting.AddDefaultOLodConfigs();
}
if (AppSettingData.ApplicationSetting.GeneralSetting.CheckForUpdate) if (AppSettingData.ApplicationSetting.GeneralSetting.CheckForUpdate)
CheckForAutoUpdate(); CheckForAutoUpdate();

View file

@ -38,17 +38,6 @@ namespace MSFSPopoutPanelManager.Orchestration
break; break;
} }
}; };
ApplicationSetting.DynamicLodSetting.TlodConfigs.CollectionChanged += (_, e) =>
{
if (e.Action == NotifyCollectionChangedAction.Reset)
AppSettingDataManager.WriteAppSetting(ApplicationSetting);
};
ApplicationSetting.DynamicLodSetting.OlodConfigs.CollectionChanged += (_, _) =>
{
AppSettingDataManager.WriteAppSetting(ApplicationSetting);
};
} }
} }
} }

View file

@ -1,257 +0,0 @@
using MSFSPopoutPanelManager.DomainModel.DynamicLod;
using MSFSPopoutPanelManager.WindowsAgent;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Runtime.InteropServices;
namespace MSFSPopoutPanelManager.Orchestration
{
public class DynamicLodManager
{
private const string SIMMODULE_NAME = "WwiseLibPCx64P.dll";
private const int PROCESS_VM_OPERATION = 0x0008;
private const int PROCESS_VM_READ = 0x0010;
private const int PROCESS_VM_WRITE = 0x0020;
private const long OFFSET_MODULE_BASE =0x004B2368;
private const long OFFSET_POINTER_MAIN = 0x3D0;
private const long OFFSET_POINTER_TLOD_VR = 0x114;
private const long OFFSET_POINTER_TLOD = 0xC;
private const long OFFSET_POINTER_OLOD = 0xC;
private static bool _isActive;
private static WindowProcess _process;
private static IntPtr _processHandle;
private static long _processModuleAddress;
private static long _addressMain;
private static long _addressTlod;
private static long _addressOlod;
private static long _addressTlodVr;
private static long _addressOlodVr;
private static FlightSimData _flightSimData;
private static AppSettingData _appSettingData;
private static LinkedListNode<LodConfig> _nextTlod;
private static LinkedListNode<LodConfig> _nextOlod;
private static LinkedListNode<LodConfig> _currentTlod;
private static LinkedListNode<LodConfig> _currentOlod;
public static void Attach(FlightSimData flightSimData, AppSettingData appSettingData)
{
if (appSettingData == null || _isActive)
return;
_flightSimData = flightSimData;
_appSettingData = appSettingData;
_flightSimData.OnAltAboveGroundChanged -= HandleOnAltAboveGroundChanged;
_flightSimData.OnAltAboveGroundChanged += HandleOnAltAboveGroundChanged;
_process = WindowProcessManager.SimulatorProcess;
_processModuleAddress = GetSimModuleAddress();
if (_process == null || _processModuleAddress == IntPtr.Zero)
return;
_processHandle = PInvoke.OpenProcess(PROCESS_VM_OPERATION | PROCESS_VM_READ | PROCESS_VM_WRITE, false, _process.ProcessId);
if (_processHandle == IntPtr.Zero)
return;
_addressMain = ReadMemory<long>(_processModuleAddress + OFFSET_MODULE_BASE) + OFFSET_POINTER_MAIN;
_addressTlod = ReadMemory<long>(_addressMain) + OFFSET_POINTER_TLOD;
_addressTlodVr = ReadMemory<long>(_addressMain) + OFFSET_POINTER_TLOD_VR;
_addressOlod = _addressTlod + OFFSET_POINTER_OLOD;
_addressOlodVr = _addressTlodVr + OFFSET_POINTER_OLOD;
// Set initial LOD if already in cockpit
InitializeData();
_isActive = true;
}
public static void Detach()
{
if (_appSettingData == null)
return;
if (_appSettingData.ApplicationSetting.DynamicLodSetting.ResetEnabled)
{
WriteTlod(_appSettingData.ApplicationSetting.DynamicLodSetting.ResetTlod);
WriteOlod(_appSettingData.ApplicationSetting.DynamicLodSetting.ResetOlod);
}
_isActive = false;
Debug.WriteLine($"Reset to custom LOD: TLOD: {_appSettingData.ApplicationSetting.DynamicLodSetting.ResetTlod}, OLOD: {_appSettingData.ApplicationSetting.DynamicLodSetting.ResetOlod}");
}
private static void HandleOnAltAboveGroundChanged(object sender, EventArgs e)
{
if (!_flightSimData.IsFlightStarted)
return;
var agl = _flightSimData.PlaneAltAboveGround < 0 ? 0 : _flightSimData.PlaneAltAboveGround;
if (_nextTlod != null && _nextTlod.Value.Agl <= agl)
{
_currentTlod = _nextTlod;
_nextTlod = _currentTlod.Next;
DynamicLodManager.WriteTlod(_currentTlod.Value.Lod);
}
else if (_currentTlod is { Previous: not null } && _currentTlod.Value.Agl > agl)
{
_currentTlod = _currentTlod.Previous;
if (_currentTlod != null)
{
_nextTlod = _currentTlod.Next;
DynamicLodManager.WriteTlod(_currentTlod.Value.Lod);
}
}
if (_nextOlod != null && _nextOlod.Value.Agl <= agl)
{
_currentOlod = _nextOlod;
_nextOlod = _currentOlod.Next;
DynamicLodManager.WriteOlod(_currentOlod.Value.Lod);
}
else if (_currentOlod is { Previous: not null } && _currentOlod.Value.Agl > agl)
{
_currentOlod = _currentOlod.Previous;
if (_currentOlod != null)
{
_nextOlod = _currentOlod.Next;
DynamicLodManager.WriteOlod(_currentOlod.Value.Lod);
}
}
if(_currentTlod != null && _currentOlod != null)
Debug.WriteLine($"Altitude: {agl}, TLOD: {_currentTlod.Value.Lod}, OLOD: {_currentOlod.Value.Lod}");
}
private static void WriteTlod(int value, bool isVr = false)
{
WriteMemory(isVr ? _addressTlodVr : _addressTlod, value / 100.0f);
}
private static void WriteOlod(int value, bool isVr = false)
{
WriteMemory(isVr ? _addressOlodVr : _addressOlod, value / 100.0f);
}
private static long GetSimModuleAddress()
{
if (_process == null)
return -1;
foreach (ProcessModule processModule in _process.Modules)
{
if (processModule.ModuleName == SIMMODULE_NAME)
return processModule.BaseAddress;
}
return -1;
}
private static void WriteMemory(long address, object value)
{
try
{
var buffer = StructureToByteArray(value);
PInvoke.NtWriteVirtualMemory(checked((int)_processHandle), address, buffer, buffer.Length, out _);
}
catch
{
// ignored
}
}
private static T ReadMemory<T>(long address) where T : struct
{
try
{
var byteSize = Marshal.SizeOf(typeof(T));
var buffer = new byte[byteSize];
PInvoke.NtReadVirtualMemory(checked((int)_processHandle), address, buffer, buffer.Length, out _);
return ByteArrayToStructure<T>(buffer);
}
catch
{
// ignored
}
return default(T);
}
private static byte[] StructureToByteArray(object obj)
{
var length = Marshal.SizeOf(obj);
var array = new byte[length];
var pointer = Marshal.AllocHGlobal(length);
Marshal.StructureToPtr(obj, pointer, true);
Marshal.Copy(pointer, array, 0, length);
Marshal.FreeHGlobal(pointer);
return array;
}
private static T ByteArrayToStructure<T>(byte[] bytes) where T : struct
{
var handle = GCHandle.Alloc(bytes, GCHandleType.Pinned);
try
{
var result = Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(T));
if(result != null)
return (T)result;
return default(T);
}
finally
{
handle.Free();
}
}
private static void InitializeData()
{
var agl = _flightSimData.PlaneAltAboveGround < 0 ? 0 : _flightSimData.PlaneAltAboveGround;
var tlod = _appSettingData.ApplicationSetting.DynamicLodSetting.TlodConfigs.FirstOrDefault(x => x.Agl > agl);
_nextTlod = _appSettingData.ApplicationSetting.DynamicLodSetting.TlodConfigs.Find(tlod);
tlod = _appSettingData.ApplicationSetting.DynamicLodSetting.TlodConfigs.LastOrDefault(x => x.Agl <= agl);
_currentTlod = _appSettingData.ApplicationSetting.DynamicLodSetting.TlodConfigs.Find(tlod) ?? _appSettingData.ApplicationSetting.DynamicLodSetting.TlodConfigs.Last;
if (_currentTlod != null)
DynamicLodManager.WriteTlod(_currentTlod.Value.Lod);
var olod = _appSettingData.ApplicationSetting.DynamicLodSetting.OlodConfigs.FirstOrDefault(x => x.Agl > agl);
_nextOlod = _appSettingData.ApplicationSetting.DynamicLodSetting.OlodConfigs.Find(olod);
olod = _appSettingData.ApplicationSetting.DynamicLodSetting.OlodConfigs.LastOrDefault(x => x.Agl <= agl);
_currentOlod = _appSettingData.ApplicationSetting.DynamicLodSetting.OlodConfigs.Find(olod) ?? _appSettingData.ApplicationSetting.DynamicLodSetting.OlodConfigs.Last;
if (_currentOlod != null)
DynamicLodManager.WriteOlod(_currentOlod.Value.Lod);
if (_currentTlod != null && _currentOlod != null)
Debug.WriteLine($"Initialize Altitude: {agl}, TLOD: {_currentTlod.Value.Lod}, OLOD: {_currentOlod.Value.Lod}");
}
//private static int ReadTlod(bool isVr = false)
//{
// return Convert.ToInt32(ReadMemory<float>(isVr ? _addressTlodVr : _addressTlod) * 100.0f);
//}
//private static int ReadOlod(bool isVr = false)
//{
// return Convert.ToInt32(ReadMemory<float>(isVr ? _addressOlodVr : _addressOlod) * 100.0f);
//}
}
}

View file

@ -0,0 +1,336 @@
using MSFSPopoutPanelManager.DomainModel.DynamicLod;
using MSFSPopoutPanelManager.DomainModel.Setting;
using MSFSPopoutPanelManager.Shared;
using MSFSPopoutPanelManager.SimConnectAgent;
using MSFSPopoutPanelManager.WindowsAgent;
using Newtonsoft.Json.Linq;
using System;
using System.Diagnostics;
using System.Dynamic;
using System.Runtime.InteropServices;
namespace MSFSPopoutPanelManager.Orchestration
{
public class DynamicLodOrchestrator : BaseOrchestrator
{
private const string SIMMODULE_NAME = "WwiseLibPCx64P.dll";
private const int PROCESS_VM_OPERATION = 0x0008;
private const int PROCESS_VM_READ = 0x0010;
private const int PROCESS_VM_WRITE = 0x0020;
private const long OFFSET_MODULE_BASE =0x004B2368;
private const long OFFSET_POINTER_MAIN = 0x3D0;
private const long OFFSET_POINTER_TLOD_VR = 0x114;
private const long OFFSET_POINTER_TLOD = 0xC;
private const long OFFSET_POINTER_OLOD = 0xC;
private const long OFFSET_POINTER_CLOUDQ = 0x44;
private const long OFFSET_POINTER_CLOUDQ_VR = 0x108;
private const long OFFSET_POINTER_VR_MODE = 0x1C;
private const long OFFSET_POINTER_FG_MODE = 0x4A;
private const long OFFSET_POINTER_ANSIO_FILTER = -0x18;
private const long OFFSET_POINTER_WATER_WAVES = 0x3C;
private bool _isActive;
private WindowProcess _process;
private IntPtr _processHandle;
private long _processModuleAddress;
private long _addressTlod;
private long _addressOlod;
private long _addressTlodVr;
private long _addressOlodVr;
private long _addressCloudQ;
private long _addressCloudQVr;
private long _addressVrMode;
private long _addressFgMode;
private DynamicLodSetting DynamicLodSetting => AppSettingData.ApplicationSetting.DynamicLodSetting;
private DynamicLodSimData SimData => FlightSimData.DynamicLodSimData;
private DateTime _lastLodUpdateTime = DateTime.Now;
private bool _isDecreasedCloudQualityActive = false;
public DynamicLodOrchestrator(SharedStorage sharedStorage) : base(sharedStorage) {}
public void Attach()
{
if (AppSettingData == null || _isActive)
return;
_process = WindowProcessManager.SimulatorProcess;
_processModuleAddress = GetSimModuleAddress();
if (_process == null || _processModuleAddress == IntPtr.Zero)
return;
_processHandle = PInvoke.OpenProcess(PROCESS_VM_OPERATION | PROCESS_VM_READ | PROCESS_VM_WRITE, false, _process.ProcessId);
if (_processHandle == IntPtr.Zero)
return;
_addressTlod = ReadMemory<long>(_processModuleAddress + OFFSET_MODULE_BASE) + OFFSET_POINTER_MAIN;
if (_addressTlod > 0)
{
_addressTlodVr = ReadMemory<long>(_addressTlod) + OFFSET_POINTER_TLOD_VR;
_addressTlod = ReadMemory<long>(_addressTlod) + OFFSET_POINTER_TLOD;
_addressOlod = _addressTlod + OFFSET_POINTER_OLOD;
_addressOlodVr = _addressTlodVr + OFFSET_POINTER_OLOD;
_addressCloudQ = _addressTlod + OFFSET_POINTER_CLOUDQ;
_addressCloudQVr = _addressCloudQ + OFFSET_POINTER_CLOUDQ_VR;
_addressVrMode = _addressTlod - OFFSET_POINTER_VR_MODE;
_addressFgMode = _addressTlod - OFFSET_POINTER_FG_MODE;
if (!MemoryBoundaryTest())
{
FileLogger.WriteLog("Unable to validate memory space for Dynamic LOD", StatusMessageType.Error);
return;
}
}
_isActive = true;
_lastLodUpdateTime = DateTime.Now;
_isDecreasedCloudQualityActive = false;
}
public void Detach()
{
if (DynamicLodSetting == null)
return;
if (DynamicLodSetting.ResetEnabled)
{
var isVr = ReadIsVr();
WriteMemory(isVr ? _addressTlodVr : _addressTlod, DynamicLodSetting.ResetTlod / 100.0f);
WriteMemory(isVr ? _addressOlodVr : _addressOlod, DynamicLodSetting.ResetOlod / 100.0f);
}
_isActive = false;
Debug.WriteLine($"Reset to custom LOD: TLOD: {DynamicLodSetting.ResetTlod}, OLOD: {DynamicLodSetting.ResetOlod}");
}
public int ReadTlod(bool isVr = false)
{
return Convert.ToInt32(ReadMemory<float>(isVr ? _addressTlodVr : _addressTlod) * 100.0f);
}
public int ReadOlod(bool isVr = false)
{
return Convert.ToInt32(ReadMemory<float>(isVr ? _addressOlodVr : _addressOlod) * 100.0f);
}
public string ReadCloudQuality(bool isVr = false)
{
return ReadCloudQualitySimValue(isVr) switch
{
0 => "Low",
1 => "Medium",
2 => "High",
3 => "Ultra",
_ => "N/A"
};
}
public int ReadCloudQualitySimValue(bool isVr = false)
{
return Convert.ToInt32(ReadMemory<int>(isVr ? _addressCloudQVr : _addressCloudQ));
}
public bool ReadIsVr()
{
return ReadMemory<int>(_addressVrMode) == 1;
}
public bool ReadIsFg(bool isVr)
{
if (isVr)
return false;
return ReadMemory<byte>(_addressFgMode) == 1;
}
public void UpdateLod(bool isVr)
{
if (DateTime.Now - _lastLodUpdateTime <= TimeSpan.FromSeconds(1))
return;
if (!FlightSimData.IsFlightStarted || !FlightSimData.IsInCockpit || (DynamicLodSetting.PauseOutsideCockpitView && FlightSimData.CameraState != CameraState.Cockpit))
return;
var deltaFps = SimData.Fps - DynamicLodSetting.TargetedFps;
if (Math.Abs(deltaFps) < DynamicLodSetting.TargetedFps * DynamicLodSetting.FpsTolerance / 100.0) // within FPS tolerance
return;
_lastLodUpdateTime = DateTime.Now;
SetTlod(deltaFps);
SetOlod();
}
private long GetSimModuleAddress()
{
if (_process == null)
return -1;
foreach (ProcessModule processModule in _process.Modules)
{
if (processModule.ModuleName == SIMMODULE_NAME)
return processModule.BaseAddress;
}
return -1;
}
private void WriteMemory(long address, object value)
{
try
{
var buffer = StructureToByteArray(value);
PInvoke.NtWriteVirtualMemory(checked((int)_processHandle), address, buffer, buffer.Length, out _);
}
catch
{
// ignored
}
}
private T ReadMemory<T>(long address) where T : struct
{
try
{
var byteSize = Marshal.SizeOf(typeof(T));
var buffer = new byte[byteSize];
PInvoke.NtReadVirtualMemory(checked((int)_processHandle), address, buffer, buffer.Length, out _);
return ByteArrayToStructure<T>(buffer);
}
catch
{
// ignored
}
return default(T);
}
private byte[] StructureToByteArray(object obj)
{
var length = Marshal.SizeOf(obj);
var array = new byte[length];
var pointer = Marshal.AllocHGlobal(length);
Marshal.StructureToPtr(obj, pointer, true);
Marshal.Copy(pointer, array, 0, length);
Marshal.FreeHGlobal(pointer);
return array;
}
private T ByteArrayToStructure<T>(byte[] bytes) where T : struct
{
var handle = GCHandle.Alloc(bytes, GCHandleType.Pinned);
try
{
var result = Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(T));
if(result != null)
return (T)result;
return default(T);
}
finally
{
handle.Free();
}
}
private bool MemoryBoundaryTest()
{
// Boundary check a few known setting memory addresses to see if any fail which likely indicates MSFS memory map has changed
if (ReadTlod() < 10 || ReadTlod() > 1000 || ReadTlod(true) < 10 || ReadTlod(true) > 1000
|| ReadOlod() < 10 || ReadOlod() > 1000 || ReadOlod(true) < 10 || ReadOlod(true) > 1000
|| ReadCloudQuality() == "N/A" || ReadCloudQuality(true) == "N/A"
|| ReadMemory<int>(_addressVrMode) < 0 || ReadMemory<int>(_addressVrMode) > 1
|| ReadMemory<int>(_addressTlod + OFFSET_POINTER_ANSIO_FILTER) < 1 || ReadMemory<int>(_addressTlod + OFFSET_POINTER_ANSIO_FILTER) > 16
|| !(ReadMemory<int>(_addressTlod + OFFSET_POINTER_WATER_WAVES) == 128 || ReadMemory<int>(_addressTlod + OFFSET_POINTER_WATER_WAVES) == 256 || ReadMemory<int>(_addressTlod + OFFSET_POINTER_WATER_WAVES) == 512))
{
return false;
}
return true;
}
private void SetTlod(int deltaFps, bool isVr = false)
{
var tlodStep = Math.Max(5, Math.Abs(deltaFps / 2));
var newTlod = SimData.Tlod + Math.Sign(deltaFps) * tlodStep;
if (DynamicLodSetting.TlodMinOnGround && SimData.AltAboveGround <= DynamicLodSetting.AltTlodBase)
{
newTlod = DynamicLodSetting.TlodMin;
}
else if (newTlod < DynamicLodSetting.TlodMin)
{
newTlod = DynamicLodSetting.TlodMin;
}
else if (newTlod > DynamicLodSetting.TlodMax)
{
newTlod = DynamicLodSetting.TlodMax;
}
if (ReadTlod(isVr) == newTlod)
return;
// Adjust cloud quality if applicable
if (deltaFps < 0 && newTlod < DynamicLodSetting.CloudRecoveryTlod && !_isDecreasedCloudQualityActive)
{
_isDecreasedCloudQualityActive = true;
WriteMemory(isVr ? _addressCloudQVr : _addressCloudQ, ReadCloudQualitySimValue(isVr) - 1); // High
_lastLodUpdateTime = _lastLodUpdateTime.AddSeconds(2); // Add extra delay for cloud setting to take effect
Debug.WriteLine("New Cloud Quality written - 2.");
return;
}
if (deltaFps > 0 && newTlod >= DynamicLodSetting.CloudRecoveryTlod && _isDecreasedCloudQualityActive)
{
_isDecreasedCloudQualityActive = false;
WriteMemory(isVr ? _addressCloudQVr : _addressCloudQ, ReadCloudQualitySimValue(isVr) + 1); // Ultra
_lastLodUpdateTime = _lastLodUpdateTime.AddSeconds(2);
Debug.WriteLine("New Cloud Quality written - 3.");
return;
}
Debug.WriteLine($"New TLOD written - {newTlod}.");
WriteMemory(isVr ? _addressTlodVr : _addressTlod, newTlod / 100.0f);
}
private void SetOlod(bool isVr = false)
{
int newOlod;
if (SimData.AltAboveGround < DynamicLodSetting.AltOlodBase)
{
newOlod = DynamicLodSetting.OlodBase;
}
else if (SimData.AltAboveGround > DynamicLodSetting.AltOlodTop)
{
newOlod = DynamicLodSetting.OlodTop;
}
else
{
newOlod = Convert.ToInt32(DynamicLodSetting.OlodBase - (DynamicLodSetting.OlodBase - DynamicLodSetting.OlodTop) * (SimData.AltAboveGround - DynamicLodSetting.AltOlodBase) / (DynamicLodSetting.AltOlodTop - DynamicLodSetting.AltOlodBase));
}
if (ReadOlod(isVr) == newOlod)
return;
Debug.WriteLine($"New OLOD written - {newOlod}.");
WriteMemory(isVr ? _addressOlodVr : _addressOlod, newOlod / 100.0f);
}
}
}

View file

@ -2,6 +2,7 @@
using MSFSPopoutPanelManager.Shared; using MSFSPopoutPanelManager.Shared;
using System; using System;
using System.ComponentModel; using System.ComponentModel;
using MSFSPopoutPanelManager.DomainModel.DynamicLod;
using MSFSPopoutPanelManager.SimConnectAgent; using MSFSPopoutPanelManager.SimConnectAgent;
namespace MSFSPopoutPanelManager.Orchestration namespace MSFSPopoutPanelManager.Orchestration
@ -15,8 +16,6 @@ namespace MSFSPopoutPanelManager.Orchestration
IsSimConnectActive = false; IsSimConnectActive = false;
} }
public event EventHandler OnAltAboveGroundChanged;
public bool IsSimConnectActive { get; set; } public bool IsSimConnectActive { get; set; }
public string AircraftName { get; set; } public string AircraftName { get; set; }
@ -43,8 +42,6 @@ namespace MSFSPopoutPanelManager.Orchestration
public bool PlaneInParkingSpot { get; set; } public bool PlaneInParkingSpot { get; set; }
public int PlaneAltAboveGround { get; set; }
public bool IsSimulatorStarted { get; set; } public bool IsSimulatorStarted { get; set; }
public bool IsSimConnectDataReceived { get; set; } public bool IsSimConnectDataReceived { get; set; }
@ -55,6 +52,8 @@ namespace MSFSPopoutPanelManager.Orchestration
public IHudBarData HudBarData { get; set; } public IHudBarData HudBarData { get; set; }
public DynamicLodSimData DynamicLodSimData { get; set; } = new();
[IgnorePropertyChanged] [IgnorePropertyChanged]
internal ProfileData ProfileDataRef { get; set; } internal ProfileData ProfileDataRef { get; set; }
@ -65,9 +64,6 @@ namespace MSFSPopoutPanelManager.Orchestration
// Automatic switching of active profile when SimConnect active aircraft change // Automatic switching of active profile when SimConnect active aircraft change
if (e.PropertyName == "AircraftName") if (e.PropertyName == "AircraftName")
ProfileDataRef.AutoSwitchProfile(); ProfileDataRef.AutoSwitchProfile();
if(e.PropertyName == "PlaneAltAboveGround")
OnAltAboveGroundChanged?.Invoke(this, EventArgs.Empty);
} }
public void Reset() public void Reset()
@ -85,7 +81,6 @@ namespace MSFSPopoutPanelManager.Orchestration
PlaneInParkingSpot = false; PlaneInParkingSpot = false;
CameraState = CameraState.Unknown; CameraState = CameraState.Unknown;
IsSimulatorStarted = false; IsSimulatorStarted = false;
PlaneAltAboveGround = 0;
CameraViewTypeAndIndex1Max = 0; CameraViewTypeAndIndex1Max = 0;
CameraViewTypeAndIndex2Max = 0; CameraViewTypeAndIndex2Max = 0;
} }

View file

@ -15,11 +15,13 @@ namespace MSFSPopoutPanelManager.Orchestration
private System.Timers.Timer _msfsGameExitDetectionTimer; private System.Timers.Timer _msfsGameExitDetectionTimer;
private SimConnectProvider _simConnectProvider; private SimConnectProvider _simConnectProvider;
private DynamicLodOrchestrator _dynamicLodOrchestrator;
private bool _isTurnedOnPower; private bool _isTurnedOnPower;
private bool _isTurnedOnAvionics; private bool _isTurnedOnAvionics;
public FlightSimOrchestrator(SharedStorage sharedStorage) : base(sharedStorage) public FlightSimOrchestrator(SharedStorage sharedStorage, DynamicLodOrchestrator dynamicLodOrchestrator) : base(sharedStorage)
{ {
_dynamicLodOrchestrator = dynamicLodOrchestrator;
_simConnectProvider = new SimConnectProvider(); _simConnectProvider = new SimConnectProvider();
} }
@ -40,9 +42,7 @@ namespace MSFSPopoutPanelManager.Orchestration
WindowProcessManager.GetSimulatorProcess(); // refresh simulator process WindowProcessManager.GetSimulatorProcess(); // refresh simulator process
DetectMsfsExit(); DetectMsfsExit();
// Attach in memory override for Dynamic LOD StartDynamicLod();
if (AppSettingData != null && AppSettingData.ApplicationSetting.DynamicLodSetting.IsEnabled)
DynamicLodManager.Attach(FlightSimData, AppSettingData);
}; };
_simConnectProvider.OnDisconnected += (_, _) => _simConnectProvider.OnDisconnected += (_, _) =>
@ -61,61 +61,39 @@ namespace MSFSPopoutPanelManager.Orchestration
_simConnectProvider.OnSimConnectDataRequiredRefreshed += (_, e) => _simConnectProvider.OnSimConnectDataRequiredRefreshed += (_, e) =>
{ {
var electricalMasterBattery = Convert.ToBoolean(e.Find(d => d.PropertyName == SimDataDefinitions.PropName.ElectricalMasterBattery).Value); MapRequiredSimConnectData(e);
if (electricalMasterBattery != FlightSimData.ElectricalMasterBatteryStatus)
FlightSimData.ElectricalMasterBatteryStatus = electricalMasterBattery;
var avionicsMasterSwitch = Convert.ToBoolean(e.Find(d => d.PropertyName == SimDataDefinitions.PropName.AvionicsMasterSwitch).Value);
if (avionicsMasterSwitch != FlightSimData.AvionicsMasterSwitchStatus)
FlightSimData.AvionicsMasterSwitchStatus = avionicsMasterSwitch;
var trackIR = Convert.ToBoolean(e.Find(d => d.PropertyName == SimDataDefinitions.PropName.TrackIREnable).Value);
if (trackIR != FlightSimData.TrackIRStatus)
FlightSimData.TrackIRStatus = trackIR;
var cameraStateInt = Convert.ToInt32(e.Find(d => d.PropertyName == SimDataDefinitions.PropName.CameraState).Value);
var result = Enum.TryParse<CameraState>(cameraStateInt.ToString(), out var cameraState);
if (!result)
cameraState = CameraState.Unknown;
if (cameraState != FlightSimData.CameraState)
FlightSimData.CameraState = cameraState;
var cockpitCameraZoom = Convert.ToInt32(e.Find(d => d.PropertyName == SimDataDefinitions.PropName.CockpitCameraZoom).Value);
if (cockpitCameraZoom != FlightSimData.CockpitCameraZoom)
FlightSimData.CockpitCameraZoom = cockpitCameraZoom;
var planeAltAboveGround = Convert.ToInt32(e.Find(d => d.PropertyName == SimDataDefinitions.PropName.PlaneAltAboveGround).Value);
if (planeAltAboveGround != FlightSimData.PlaneAltAboveGround)
FlightSimData.PlaneAltAboveGround = planeAltAboveGround;
var cameraViewTypeAndIndex0 = Convert.ToInt32(e.Find(d => d.PropertyName == SimDataDefinitions.PropName.CameraViewTypeAndIndex0).Value);
if (cameraViewTypeAndIndex0 != FlightSimData.CameraViewTypeAndIndex0)
FlightSimData.CameraViewTypeAndIndex0 = cameraViewTypeAndIndex0;
var cameraViewTypeAndIndex1 = Convert.ToInt32(e.Find(d => d.PropertyName == SimDataDefinitions.PropName.CameraViewTypeAndIndex1).Value);
if (cameraViewTypeAndIndex1 != FlightSimData.CameraViewTypeAndIndex1)
FlightSimData.CameraViewTypeAndIndex1 = cameraViewTypeAndIndex1;
var cameraViewTypeAndIndex1Max = Convert.ToInt32(e.Find(d => d.PropertyName == SimDataDefinitions.PropName.CameraViewTypeAndIndex1Max).Value);
if (cameraViewTypeAndIndex1Max != FlightSimData.CameraViewTypeAndIndex1Max)
FlightSimData.CameraViewTypeAndIndex1Max = cameraViewTypeAndIndex1Max;
var cameraViewTypeAndIndex2Max = Convert.ToInt32(e.Find(d => d.PropertyName == SimDataDefinitions.PropName.CameraViewTypeAndIndex2Max).Value);
if (cameraViewTypeAndIndex2Max != FlightSimData.CameraViewTypeAndIndex2Max)
FlightSimData.CameraViewTypeAndIndex2Max = cameraViewTypeAndIndex2Max;
FlightSimData.IsSimConnectDataReceived = true;
}; };
_simConnectProvider.OnSimConnectDataHudBarRefreshed += (_, e) => _simConnectProvider.OnSimConnectDataHudBarRefreshed += (_, e) =>
{ {
if (ProfileData.ActiveProfile.ProfileSetting.HudBarConfig.IsEnabled) if (!ProfileData.ActiveProfile.ProfileSetting.HudBarConfig.IsEnabled || !FlightSimData.IsFlightStarted)
MapHudBarSimConnectData(e); return;
MapHudBarSimConnectData(e);
};
_simConnectProvider.OnSimConnectDataDynamicLodRefreshed += (_, e) =>
{
if (!AppSettingData.ApplicationSetting.DynamicLodSetting.IsEnabled || !FlightSimData.IsFlightStarted || !WindowActionManager.IsMsfsInFocus())
return;
var isVr = _dynamicLodOrchestrator.ReadIsVr();
MapDynamicLodSimConnectData(e, isVr);
_dynamicLodOrchestrator.UpdateLod(isVr);
};
_simConnectProvider.OnSimConnectDataEventFrameRefreshed += (_, e) =>
{
if (!AppSettingData.ApplicationSetting.DynamicLodSetting.IsEnabled || !FlightSimData.IsFlightStarted)
return;
MapEventFrameData(e);
}; };
_simConnectProvider.OnActiveAircraftChanged += (_, e) => _simConnectProvider.OnActiveAircraftChanged += (_, e) =>
{ {
var aircraftName = String.IsNullOrEmpty(e) ? null : e; var aircraftName = string.IsNullOrEmpty(e) ? null : e;
if (FlightSimData.AircraftName != aircraftName) if (FlightSimData.AircraftName != aircraftName)
{ {
FlightSimData.AircraftName = aircraftName; FlightSimData.AircraftName = aircraftName;
@ -392,9 +370,7 @@ namespace MSFSPopoutPanelManager.Orchestration
FlightSimData.IsFlightStarted = true; FlightSimData.IsFlightStarted = true;
// Attach in memory override for Dynamic LOD StartDynamicLod();
if (AppSettingData != null && AppSettingData.ApplicationSetting.DynamicLodSetting.IsEnabled)
DynamicLodManager.Attach(FlightSimData, AppSettingData);
} }
private void HandleOnFlightStopped(object sender, EventArgs e) private void HandleOnFlightStopped(object sender, EventArgs e)
@ -409,9 +385,8 @@ namespace MSFSPopoutPanelManager.Orchestration
FlightSimData.IsFlightStarted = false; FlightSimData.IsFlightStarted = false;
// Detach in memory override for Dynamic LOD StopDynamicLod();
if (AppSettingData != null && AppSettingData.ApplicationSetting.DynamicLodSetting.IsEnabled) FlightSimData.DynamicLodSimData.Clear();
DynamicLodManager.Detach();
} }
private void DetectMsfsExit() private void DetectMsfsExit()
@ -436,6 +411,50 @@ namespace MSFSPopoutPanelManager.Orchestration
}; };
} }
private void MapRequiredSimConnectData(List<SimDataItem> simData)
{
var electricalMasterBattery = Convert.ToBoolean(simData.Find(d => d.PropertyName == SimDataDefinitions.PropName.ElectricalMasterBattery).Value);
if (electricalMasterBattery != FlightSimData.ElectricalMasterBatteryStatus)
FlightSimData.ElectricalMasterBatteryStatus = electricalMasterBattery;
var avionicsMasterSwitch = Convert.ToBoolean(simData.Find(d => d.PropertyName == SimDataDefinitions.PropName.AvionicsMasterSwitch).Value);
if (avionicsMasterSwitch != FlightSimData.AvionicsMasterSwitchStatus)
FlightSimData.AvionicsMasterSwitchStatus = avionicsMasterSwitch;
var trackIR = Convert.ToBoolean(simData.Find(d => d.PropertyName == SimDataDefinitions.PropName.TrackIREnable).Value);
if (trackIR != FlightSimData.TrackIRStatus)
FlightSimData.TrackIRStatus = trackIR;
var cameraStateInt = Convert.ToInt32(simData.Find(d => d.PropertyName == SimDataDefinitions.PropName.CameraState).Value);
var result = Enum.TryParse<CameraState>(cameraStateInt.ToString(), out var cameraState);
if (!result)
cameraState = CameraState.Unknown;
if (cameraState != FlightSimData.CameraState)
FlightSimData.CameraState = cameraState;
var cockpitCameraZoom = Convert.ToInt32(simData.Find(d => d.PropertyName == SimDataDefinitions.PropName.CockpitCameraZoom).Value);
if (cockpitCameraZoom != FlightSimData.CockpitCameraZoom)
FlightSimData.CockpitCameraZoom = cockpitCameraZoom;
var cameraViewTypeAndIndex0 = Convert.ToInt32(simData.Find(d => d.PropertyName == SimDataDefinitions.PropName.CameraViewTypeAndIndex0).Value);
if (cameraViewTypeAndIndex0 != FlightSimData.CameraViewTypeAndIndex0)
FlightSimData.CameraViewTypeAndIndex0 = cameraViewTypeAndIndex0;
var cameraViewTypeAndIndex1 = Convert.ToInt32(simData.Find(d => d.PropertyName == SimDataDefinitions.PropName.CameraViewTypeAndIndex1).Value);
if (cameraViewTypeAndIndex1 != FlightSimData.CameraViewTypeAndIndex1)
FlightSimData.CameraViewTypeAndIndex1 = cameraViewTypeAndIndex1;
var cameraViewTypeAndIndex1Max = Convert.ToInt32(simData.Find(d => d.PropertyName == SimDataDefinitions.PropName.CameraViewTypeAndIndex1Max).Value);
if (cameraViewTypeAndIndex1Max != FlightSimData.CameraViewTypeAndIndex1Max)
FlightSimData.CameraViewTypeAndIndex1Max = cameraViewTypeAndIndex1Max;
var cameraViewTypeAndIndex2Max = Convert.ToInt32(simData.Find(d => d.PropertyName == SimDataDefinitions.PropName.CameraViewTypeAndIndex2Max).Value);
if (cameraViewTypeAndIndex2Max != FlightSimData.CameraViewTypeAndIndex2Max)
FlightSimData.CameraViewTypeAndIndex2Max = cameraViewTypeAndIndex2Max;
FlightSimData.IsSimConnectDataReceived = true;
}
private void MapHudBarSimConnectData(List<SimDataItem> simData) private void MapHudBarSimConnectData(List<SimDataItem> simData)
{ {
if (CompareSimConnectData(simData, SimDataDefinitions.PropName.ElevatorTrim, FlightSimData.HudBarData.ElevatorTrim, out var newValue)) if (CompareSimConnectData(simData, SimDataDefinitions.PropName.ElevatorTrim, FlightSimData.HudBarData.ElevatorTrim, out var newValue))
@ -466,6 +485,49 @@ namespace MSFSPopoutPanelManager.Orchestration
FlightSimData.HudBarData.SimRate = newValue; FlightSimData.HudBarData.SimRate = newValue;
} }
private void MapDynamicLodSimConnectData(List<SimDataItem> simData, bool isVr)
{
if (CompareSimConnectData(simData, SimDataDefinitions.PropName.PlaneAltAboveGround, FlightSimData.DynamicLodSimData.Agl, out var newValue))
FlightSimData.DynamicLodSimData.Agl = newValue;
if (CompareSimConnectData(simData, SimDataDefinitions.PropName.PlaneAltAboveGround, FlightSimData.DynamicLodSimData.AltAboveGround, out newValue))
FlightSimData.DynamicLodSimData.AltAboveGround = newValue;
if (CompareSimConnectData(simData, SimDataDefinitions.PropName.PlaneAltAboveGroundMinusCg, FlightSimData.DynamicLodSimData.AltAboveGroundMinusCg, out newValue))
FlightSimData.DynamicLodSimData.AltAboveGroundMinusCg = newValue;
if (CompareSimConnectData(simData, SimDataDefinitions.PropName.GroundVelocity, FlightSimData.DynamicLodSimData.GroundVelocity, out newValue))
FlightSimData.DynamicLodSimData.GroundVelocity = newValue;
if (CompareSimConnectData(simData, SimDataDefinitions.PropName.SimOnGround, 1.0f, out newValue))
FlightSimData.DynamicLodSimData.PlaneOnGround = Convert.ToBoolean(newValue);
var tlod = _dynamicLodOrchestrator.ReadTlod(isVr);
if (FlightSimData.DynamicLodSimData.Tlod != tlod)
FlightSimData.DynamicLodSimData.Tlod = tlod;
var olod = _dynamicLodOrchestrator.ReadOlod(isVr);
if (FlightSimData.DynamicLodSimData.Olod != olod)
FlightSimData.DynamicLodSimData.Olod = olod;
var cloudQuality = _dynamicLodOrchestrator.ReadCloudQuality(isVr);
if (FlightSimData.DynamicLodSimData.CloudQuality != cloudQuality)
FlightSimData.DynamicLodSimData.CloudQuality = cloudQuality;
if (FlightSimData.IsFlightStarted && FlightSimData.IsInCockpit &&
(!AppSettingData.ApplicationSetting.DynamicLodSetting.PauseOutsideCockpitView || (AppSettingData.ApplicationSetting.DynamicLodSetting.PauseOutsideCockpitView && FlightSimData.CameraState == CameraState.Cockpit)))
FlightSimData.DynamicLodSimData.Fps = FpsCalc.GetAverageFps(_dynamicLodOrchestrator.ReadIsFg(isVr) ? _currentFps * 2 : _currentFps);
}
private int _currentFps;
private void MapEventFrameData(int fps)
{
if (!AppSettingData.ApplicationSetting.DynamicLodSetting.IsEnabled)
return;
_currentFps = fps;
}
private bool CompareSimConnectData(List<SimDataItem> simData, string propName, double source, out double newValue) private bool CompareSimConnectData(List<SimDataItem> simData, string propName, double source, out double newValue)
{ {
var propData = simData.Find(d => d.PropertyName == propName); var propData = simData.Find(d => d.PropertyName == propName);
@ -486,5 +548,26 @@ namespace MSFSPopoutPanelManager.Orchestration
newValue = 0; newValue = 0;
return false; return false;
} }
private void StartDynamicLod()
{
if (_simConnectProvider != null)
{
// Attach in memory override for Dynamic LOD
if (AppSettingData != null && AppSettingData.ApplicationSetting.DynamicLodSetting.IsEnabled)
_dynamicLodOrchestrator.Attach();
_simConnectProvider.StartDynamicLod();
}
}
private void StopDynamicLod()
{
// Detach in memory override for Dynamic LOD
if (AppSettingData != null && AppSettingData.ApplicationSetting.DynamicLodSetting.IsEnabled)
_dynamicLodOrchestrator.Detach();
_simConnectProvider.StopDynamicLod();
}
} }
} }

View file

@ -11,9 +11,9 @@
<PackageProjectUrl>https://github.com/hawkeye-stan/msfs-popout-panel-manager</PackageProjectUrl> <PackageProjectUrl>https://github.com/hawkeye-stan/msfs-popout-panel-manager</PackageProjectUrl>
<RootNamespace>MSFSPopoutPanelManager.Orchestration</RootNamespace> <RootNamespace>MSFSPopoutPanelManager.Orchestration</RootNamespace>
<Platforms>x64</Platforms> <Platforms>x64</Platforms>
<Version>4.1.0.4</Version> <Version>4.1.1.0</Version>
<AssemblyVersion>4.1.0.4</AssemblyVersion> <AssemblyVersion>4.1.1.0</AssemblyVersion>
<FileVersion>4.1.0.4</FileVersion> <FileVersion>4.1.1.0</FileVersion>
<RuntimeIdentifier>win-x64</RuntimeIdentifier> <RuntimeIdentifier>win-x64</RuntimeIdentifier>
<DebugType>Embedded</DebugType> <DebugType>Embedded</DebugType>
<Configurations>Debug;Release;Local</Configurations> <Configurations>Debug;Release;Local</Configurations>

View file

@ -11,9 +11,9 @@
<PackageProjectUrl>https://github.com/hawkeye-stan/msfs-popout-panel-manager</PackageProjectUrl> <PackageProjectUrl>https://github.com/hawkeye-stan/msfs-popout-panel-manager</PackageProjectUrl>
<RootNamespace>MSFSPopoutPanelManager.Shared</RootNamespace> <RootNamespace>MSFSPopoutPanelManager.Shared</RootNamespace>
<Platforms>x64</Platforms> <Platforms>x64</Platforms>
<Version>4.1.0.4</Version> <Version>4.1.1.0</Version>
<AssemblyVersion>4.1.0.4</AssemblyVersion> <AssemblyVersion>4.1.1.0</AssemblyVersion>
<FileVersion>4.1.0.4</FileVersion> <FileVersion>4.1.1.0</FileVersion>
<RuntimeIdentifier>win-x64</RuntimeIdentifier> <RuntimeIdentifier>win-x64</RuntimeIdentifier>
<DebugType>Embedded</DebugType> <DebugType>Embedded</DebugType>
<Configurations>Debug;Release;Local</Configurations> <Configurations>Debug;Release;Local</Configurations>

View file

@ -4,6 +4,7 @@
{ {
REQUIRED_DEFINITION = 0, REQUIRED_DEFINITION = 0,
HUDBAR_DEFINITION, HUDBAR_DEFINITION,
DYNAMICLOD_DEFINITION,
WRITABLE_TRACK_IR_DEFINITION, WRITABLE_TRACK_IR_DEFINITION,
WRITABLE_COCKPIT_CAMERA_ZOOM_DEFINITION, WRITABLE_COCKPIT_CAMERA_ZOOM_DEFINITION,
WRITABLE_COCKPIT_CAMERA_STATE_DEFINITION, WRITABLE_COCKPIT_CAMERA_STATE_DEFINITION,
@ -17,6 +18,7 @@
{ {
REQUIRED_REQUEST = 0, REQUIRED_REQUEST = 0,
HUDBAR_REQUEST, HUDBAR_REQUEST,
DYNAMICLOD_REQUEST,
NA NA
} }

View file

@ -0,0 +1,36 @@
using System;
using System.Linq;
namespace MSFSPopoutPanelManager.SimConnectAgent
{
public class FpsCalc
{
private const int FpsLen = 50;
private static readonly float[] FpsStatistic = new float[FpsLen];
private static int _fpsIndex = -1;
public static int GetAverageFps(int newValue)
{
if (_fpsIndex == -1)
{
for (var i = 0; i < FpsLen; i++)
FpsStatistic[i] = newValue;
_fpsIndex = 1;
}
else
{
FpsStatistic[_fpsIndex] = newValue;
_fpsIndex++;
if (_fpsIndex >= FpsLen)
_fpsIndex = 0;
}
var fps = 0;
if (_fpsIndex != -1)
fps = Convert.ToInt32(FpsStatistic.Sum() / FpsLen);
return fps;
}
}
}

View file

@ -5,15 +5,17 @@
SIM_START, SIM_START,
SIM_STOP, SIM_STOP,
AIRCRAFT_LOADED, AIRCRAFT_LOADED,
VIEW VIEW,
FRAME
} }
public enum ActionEvent public enum ActionEvent
{ {
DUMMY1, // must register 4 dummy events to reserve space for SimConnectEvent above DUMMY1, // must register 5 dummy events to reserve space for SimConnectEvent above
DUMMY2, DUMMY2,
DUMMY3, DUMMY3,
DUMMY4, DUMMY4,
DUMMY5,
MASTER_BATTERY_SET, MASTER_BATTERY_SET,
AVIONICS_MASTER_SET, AVIONICS_MASTER_SET,
PAUSE_SET, PAUSE_SET,

View file

@ -11,6 +11,7 @@ namespace MSFSPopoutPanelManager.SimConnectAgent
{ {
private const int MSFS_DATA_REFRESH_TIMEOUT = 500; private const int MSFS_DATA_REFRESH_TIMEOUT = 500;
private const int MSFS_HUDBAR_DATA_REFRESH_TIMEOUT = 200; private const int MSFS_HUDBAR_DATA_REFRESH_TIMEOUT = 200;
private const int MSFS_DYNAMICLOD_DATA_REFRESH_TIMEOUT = 300;
private readonly SimConnector _simConnector; private readonly SimConnector _simConnector;
@ -19,6 +20,7 @@ namespace MSFSPopoutPanelManager.SimConnectAgent
private System.Timers.Timer _requiredRequestDataTimer; private System.Timers.Timer _requiredRequestDataTimer;
private System.Timers.Timer _hudBarRequestDataTimer; private System.Timers.Timer _hudBarRequestDataTimer;
private System.Timers.Timer _dynamicLodRequestDataTimer;
private bool _isPowerOnForPopOut; private bool _isPowerOnForPopOut;
private bool _isAvionicsOnForPopOut; private bool _isAvionicsOnForPopOut;
private bool _isTrackIRManaged; private bool _isTrackIRManaged;
@ -33,6 +35,8 @@ namespace MSFSPopoutPanelManager.SimConnectAgent
public event EventHandler OnException; public event EventHandler OnException;
public event EventHandler<List<SimDataItem>> OnSimConnectDataRequiredRefreshed; public event EventHandler<List<SimDataItem>> OnSimConnectDataRequiredRefreshed;
public event EventHandler<List<SimDataItem>> OnSimConnectDataHudBarRefreshed; public event EventHandler<List<SimDataItem>> OnSimConnectDataHudBarRefreshed;
public event EventHandler<List<SimDataItem>> OnSimConnectDataDynamicLodRefreshed;
public event EventHandler<int> OnSimConnectDataEventFrameRefreshed;
public event EventHandler<string> OnActiveAircraftChanged; public event EventHandler<string> OnActiveAircraftChanged;
public SimConnectProvider() public SimConnectProvider()
@ -43,7 +47,9 @@ namespace MSFSPopoutPanelManager.SimConnectAgent
_simConnector.OnException += HandleSimException; _simConnector.OnException += HandleSimException;
_simConnector.OnReceiveSystemEvent += HandleReceiveSystemEvent; _simConnector.OnReceiveSystemEvent += HandleReceiveSystemEvent;
_simConnector.OnReceivedRequiredData += HandleRequiredDataReceived; _simConnector.OnReceivedRequiredData += HandleRequiredDataReceived;
_simConnector.OnReceivedHudBarData += HandleHudBarDataReceived; _simConnector.OnReceivedHudBarData += (_, e) => OnSimConnectDataHudBarRefreshed?.Invoke(this, e);
_simConnector.OnReceivedDynamicLodData += (_, e) => OnSimConnectDataDynamicLodRefreshed?.Invoke(this, e);
_simConnector.OnReceivedEventFrameData += (_, e) => OnSimConnectDataEventFrameRefreshed?.Invoke(this, e);
_simConnector.OnActiveAircraftChanged += (_, e) => OnActiveAircraftChanged?.Invoke(this, e); _simConnector.OnActiveAircraftChanged += (_, e) => OnActiveAircraftChanged?.Invoke(this, e);
_isHandlingCriticalError = false; _isHandlingCriticalError = false;
@ -108,6 +114,29 @@ namespace MSFSPopoutPanelManager.SimConnectAgent
_hudBarRequestDataTimer.Stop(); _hudBarRequestDataTimer.Stop();
} }
public void StartDynamicLod()
{
if (_dynamicLodRequestDataTimer.Enabled)
return;
// shut down data request and wait for the last request to be completed
_dynamicLodRequestDataTimer.Stop();
Thread.Sleep(MSFS_DYNAMICLOD_DATA_REFRESH_TIMEOUT);
_simConnector.SetSimConnectDynamicLodDataDefinition();
_dynamicLodRequestDataTimer.Start();
_simConnector.StartReceiveFrameData();
}
public void StopDynamicLod()
{
_dynamicLodRequestDataTimer.Stop();
_simConnector.StopReceiveFrameData();
}
public void TurnOnPower(bool isRequiredForColdStart) public void TurnOnPower(bool isRequiredForColdStart)
{ {
if (!isRequiredForColdStart || _requiredSimData == null) if (!isRequiredForColdStart || _requiredSimData == null)
@ -295,10 +324,29 @@ namespace MSFSPopoutPanelManager.SimConnectAgent
// ignored // ignored
} }
}; };
if (_isHudBarDataActive) if (_isHudBarDataActive)
SetHudBarConfig(_activeHudBarType); SetHudBarConfig(_activeHudBarType);
// Setup dynamic data request timer
_dynamicLodRequestDataTimer = new()
{
Interval = MSFS_DYNAMICLOD_DATA_REFRESH_TIMEOUT,
};
_dynamicLodRequestDataTimer.Stop();
_dynamicLodRequestDataTimer.Elapsed += (_, _) =>
{
try
{
_simConnector.RequestDynamicLodData();
}
catch
{
// ignored
}
};
OnConnected?.Invoke(this, EventArgs.Empty); OnConnected?.Invoke(this, EventArgs.Empty);
} }
@ -306,6 +354,7 @@ namespace MSFSPopoutPanelManager.SimConnectAgent
{ {
_requiredRequestDataTimer.Stop(); _requiredRequestDataTimer.Stop();
_hudBarRequestDataTimer.Stop(); _hudBarRequestDataTimer.Stop();
_dynamicLodRequestDataTimer.Stop();
OnDisconnected?.Invoke(this, EventArgs.Empty); OnDisconnected?.Invoke(this, EventArgs.Empty);
StopAndReconnect(); StopAndReconnect();
} }
@ -316,6 +365,7 @@ namespace MSFSPopoutPanelManager.SimConnectAgent
_requiredRequestDataTimer.Stop(); _requiredRequestDataTimer.Stop();
_hudBarRequestDataTimer.Stop(); _hudBarRequestDataTimer.Stop();
_dynamicLodRequestDataTimer.Stop();
if (!_isHandlingCriticalError) if (!_isHandlingCriticalError)
{ {
@ -332,11 +382,6 @@ namespace MSFSPopoutPanelManager.SimConnectAgent
OnSimConnectDataRequiredRefreshed?.Invoke(this, e); OnSimConnectDataRequiredRefreshed?.Invoke(this, e);
} }
private void HandleHudBarDataReceived(object sender, List<SimDataItem> e)
{
OnSimConnectDataHudBarRefreshed?.Invoke(this, e);
}
private CameraState _currentCameraState = CameraState.Unknown; private CameraState _currentCameraState = CameraState.Unknown;
private void DetectFlightStartedOrStopped(List<SimDataItem> simData) private void DetectFlightStartedOrStopped(List<SimDataItem> simData)
@ -375,6 +420,8 @@ namespace MSFSPopoutPanelManager.SimConnectAgent
_isHudBarDataActive = false; _isHudBarDataActive = false;
_hudBarRequestDataTimer.Stop(); _hudBarRequestDataTimer.Stop();
_dynamicLodRequestDataTimer.Stop();
} }
break; break;
} }

View file

@ -20,15 +20,18 @@ namespace MSFSPopoutPanelManager.SimConnectAgent
private bool _isDisabledReconnect; private bool _isDisabledReconnect;
private readonly List<SimConnectDataDefinition> _simConnectRequiredDataDefinitions = SimDataDefinitions.GetRequiredDefinitions(); private readonly List<SimConnectDataDefinition> _simConnectRequiredDataDefinitions = SimDataDefinitions.GetRequiredDefinitions();
private readonly List<SimConnectDataDefinition> _simConnectDynamicLodDataDefinitions = SimDataDefinitions.GetDynamicLodDefinitions();
private List<SimConnectDataDefinition> _simConnectHudBarDataDefinitions; private List<SimConnectDataDefinition> _simConnectHudBarDataDefinitions;
private readonly FieldInfo[] _simConnectStructFields = typeof(SimConnectStruct).GetFields(BindingFlags.Public | BindingFlags.Instance); private readonly FieldInfo[] _simConnectStructFields = typeof(SimConnectStruct).GetFields(BindingFlags.Public | BindingFlags.Instance);
public event EventHandler<string> OnException; public event EventHandler<string> OnException;
public event EventHandler<List<SimDataItem>> OnReceivedRequiredData; public event EventHandler<List<SimDataItem>> OnReceivedRequiredData;
public event EventHandler<List<SimDataItem>> OnReceivedHudBarData; public event EventHandler<List<SimDataItem>> OnReceivedHudBarData;
public event EventHandler<List<SimDataItem>> OnReceivedDynamicLodData;
public event EventHandler OnConnected; public event EventHandler OnConnected;
public event EventHandler OnDisconnected; public event EventHandler OnDisconnected;
public event EventHandler<SimConnectEvent> OnReceiveSystemEvent; public event EventHandler<SimConnectEvent> OnReceiveSystemEvent;
public event EventHandler<int> OnReceivedEventFrameData;
public event EventHandler<string> OnActiveAircraftChanged; public event EventHandler<string> OnActiveAircraftChanged;
public bool Connected { get; set; } public bool Connected { get; set; }
@ -66,6 +69,11 @@ namespace MSFSPopoutPanelManager.SimConnectAgent
AddHudBarDataDefinitions(); AddHudBarDataDefinitions();
} }
public void SetSimConnectDynamicLodDataDefinition()
{
AddDynamicLodDataDefinitions();
}
private void HandleOnRecvOpen(SimConnect sender, SIMCONNECT_RECV_OPEN data) private void HandleOnRecvOpen(SimConnect sender, SIMCONNECT_RECV_OPEN data)
{ {
ReceiveMessage(); ReceiveMessage();
@ -124,6 +132,26 @@ namespace MSFSPopoutPanelManager.SimConnectAgent
} }
} }
public void RequestDynamicLodData()
{
if (_simConnect == null || !Connected)
return;
try
{
if (_simConnectDynamicLodDataDefinitions != null)
_simConnect.RequestDataOnSimObjectType(DataRequest.DYNAMICLOD_REQUEST, DataDefinition.DYNAMICLOD_DEFINITION, 0, SIMCONNECT_SIMOBJECT_TYPE.USER);
}
catch
{
if (!_isDisabledReconnect)
_isDisabledReconnect = true;
OnException?.Invoke(this, null);
}
}
public void ReceiveMessage() public void ReceiveMessage()
{ {
if (_simConnect == null) if (_simConnect == null)
@ -208,6 +236,25 @@ namespace MSFSPopoutPanelManager.SimConnectAgent
} }
} }
public void StartReceiveFrameData()
{
if (_simConnect == null)
return;
_simConnect.OnRecvEventFrame -= HandleOnRecvEventFrame;
_simConnect.OnRecvEventFrame += HandleOnRecvEventFrame;
_simConnect.UnsubscribeFromSystemEvent(SimConnectEvent.FRAME);
_simConnect.SubscribeToSystemEvent(SimConnectEvent.FRAME, "Frame");
}
public void StopReceiveFrameData()
{
_simConnect.OnRecvEventFrame -= HandleOnRecvEventFrame;
_simConnect.UnsubscribeFromSystemEvent(SimConnectEvent.FRAME);
}
private void InitializeSimConnect() private void InitializeSimConnect()
{ {
Debug.WriteLine("Trying to start simConnect"); Debug.WriteLine("Trying to start simConnect");
@ -234,8 +281,7 @@ namespace MSFSPopoutPanelManager.SimConnectAgent
_simConnect.SubscribeToSystemEvent(SimConnectEvent.VIEW, "View"); _simConnect.SubscribeToSystemEvent(SimConnectEvent.VIEW, "View");
_simConnect.UnsubscribeFromSystemEvent(SimConnectEvent.AIRCRAFT_LOADED); _simConnect.UnsubscribeFromSystemEvent(SimConnectEvent.AIRCRAFT_LOADED);
_simConnect.SubscribeToSystemEvent(SimConnectEvent.AIRCRAFT_LOADED, "AircraftLoaded"); _simConnect.SubscribeToSystemEvent(SimConnectEvent.AIRCRAFT_LOADED, "AircraftLoaded");
AddRequiredDataDefinitions(); AddRequiredDataDefinitions();
SetupActionEvents(); SetupActionEvents();
@ -256,6 +302,14 @@ namespace MSFSPopoutPanelManager.SimConnectAgent
Connected = true; Connected = true;
} }
private void HandleOnRecvEventFrame(SimConnect sender, SIMCONNECT_RECV_EVENT_FRAME data)
{
if (data == null)
return;
OnReceivedEventFrameData?.Invoke(this, Convert.ToInt32(data.fFrameRate));
}
private void HandleOnRecvSystemState(SimConnect sender, SIMCONNECT_RECV_SYSTEM_STATE data) private void HandleOnRecvSystemState(SimConnect sender, SIMCONNECT_RECV_SYSTEM_STATE data)
{ {
switch ((SystemStateRequestId)Enum.Parse(typeof(SystemStateRequestId), data.dwRequestID.ToString())) switch ((SystemStateRequestId)Enum.Parse(typeof(SystemStateRequestId), data.dwRequestID.ToString()))
@ -361,6 +415,41 @@ namespace MSFSPopoutPanelManager.SimConnectAgent
_simConnect.RegisterDataDefineStruct<SimConnectStruct>(DataDefinition.HUDBAR_DEFINITION); _simConnect.RegisterDataDefineStruct<SimConnectStruct>(DataDefinition.HUDBAR_DEFINITION);
} }
private void AddDynamicLodDataDefinitions()
{
if (_simConnect == null)
return;
_simConnect.ClearDataDefinition(DataDefinition.DYNAMICLOD_DEFINITION);
if (_simConnectDynamicLodDataDefinitions == null)
return;
foreach (var definition in _simConnectDynamicLodDataDefinitions)
{
if (definition.DefinitionId != DataDefinition.DYNAMICLOD_DEFINITION ||
definition.DataDefinitionType != DataDefinitionType.SimConnect) continue;
SIMCONNECT_DATATYPE simConnectDataType;
switch (definition.DataType)
{
case DataType.String:
simConnectDataType = SIMCONNECT_DATATYPE.STRING256;
break;
case DataType.Float64:
simConnectDataType = SIMCONNECT_DATATYPE.FLOAT64;
break;
default:
simConnectDataType = SIMCONNECT_DATATYPE.FLOAT64;
break;
}
_simConnect.AddToDataDefinition(definition.DefinitionId, definition.VariableName, definition.SimConnectUnit, simConnectDataType, 0.0f, SimConnect.SIMCONNECT_UNUSED);
}
_simConnect.RegisterDataDefineStruct<SimConnectStruct>(DataDefinition.DYNAMICLOD_DEFINITION);
}
private void HandleOnRecvQuit(SimConnect sender, SIMCONNECT_RECV data) private void HandleOnRecvQuit(SimConnect sender, SIMCONNECT_RECV data)
{ {
Stop(); Stop();
@ -404,6 +493,9 @@ namespace MSFSPopoutPanelManager.SimConnectAgent
case (int)DataRequest.HUDBAR_REQUEST: case (int)DataRequest.HUDBAR_REQUEST:
ParseHudBarReceivedSimData(data); ParseHudBarReceivedSimData(data);
break; break;
case (int)DataRequest.DYNAMICLOD_REQUEST:
ParseDynamicLodReceivedSimData(data);
break;
} }
} }
@ -483,6 +575,46 @@ namespace MSFSPopoutPanelManager.SimConnectAgent
} }
} }
private void ParseDynamicLodReceivedSimData(SIMCONNECT_RECV_SIMOBJECT_DATA_BYTYPE data)
{
try
{
if (_simConnectDynamicLodDataDefinitions == null)
return;
var simData = new List<SimDataItem>();
var simDataStruct = (SimConnectStruct)data.dwData[0];
var i = 0;
lock (_simConnectDynamicLodDataDefinitions)
{
foreach (var definition in _simConnectDynamicLodDataDefinitions)
{
if (definition.DataDefinitionType != DataDefinitionType.SimConnect)
continue;
var dataValue = _simConnectStructFields[i].GetValue(simDataStruct);
var simDataItem = new SimDataItem
{
PropertyName = definition.PropName,
Value = dataValue == null ? 0 : (double)dataValue
};
simData.Add(simDataItem);
i++;
}
}
OnReceivedDynamicLodData?.Invoke(this, simData);
}
catch (Exception ex)
{
FileLogger.WriteException($"SimConnector: SimConnect received dynamic lod data exception - {ex.Message}", ex);
StopAndReconnect();
}
}
private void SetActiveAircraftTitle(string aircraftFilePath) private void SetActiveAircraftTitle(string aircraftFilePath)
{ {
var filePathToken = aircraftFilePath.Split(@"\"); var filePathToken = aircraftFilePath.Split(@"\");

View file

@ -14,7 +14,6 @@ namespace MSFSPopoutPanelManager.SimConnectAgent
new() { DefinitionId = DataDefinition.REQUIRED_DEFINITION, RequestId = DataRequest.REQUIRED_REQUEST, DataDefinitionType = DataDefinitionType.SimConnect, PropName = PropName.PlaneInParkingSpot, VariableName = "ATC ON PARKING SPOT", SimConnectUnit = "Bool", DataType = DataType.Float64 }, new() { DefinitionId = DataDefinition.REQUIRED_DEFINITION, RequestId = DataRequest.REQUIRED_REQUEST, DataDefinitionType = DataDefinitionType.SimConnect, PropName = PropName.PlaneInParkingSpot, VariableName = "ATC ON PARKING SPOT", SimConnectUnit = "Bool", DataType = DataType.Float64 },
new() { DefinitionId = DataDefinition.REQUIRED_DEFINITION, RequestId = DataRequest.REQUIRED_REQUEST, DataDefinitionType = DataDefinitionType.SimConnect, PropName = PropName.CameraState, VariableName = "CAMERA STATE", SimConnectUnit = "Number", DataType = DataType.Float64 }, new() { DefinitionId = DataDefinition.REQUIRED_DEFINITION, RequestId = DataRequest.REQUIRED_REQUEST, DataDefinitionType = DataDefinitionType.SimConnect, PropName = PropName.CameraState, VariableName = "CAMERA STATE", SimConnectUnit = "Number", DataType = DataType.Float64 },
new() { DefinitionId = DataDefinition.REQUIRED_DEFINITION, RequestId = DataRequest.REQUIRED_REQUEST, DataDefinitionType = DataDefinitionType.SimConnect, PropName = PropName.CockpitCameraZoom, VariableName = "COCKPIT CAMERA ZOOM", SimConnectUnit = "Percentage", DataType = DataType.Float64 }, new() { DefinitionId = DataDefinition.REQUIRED_DEFINITION, RequestId = DataRequest.REQUIRED_REQUEST, DataDefinitionType = DataDefinitionType.SimConnect, PropName = PropName.CockpitCameraZoom, VariableName = "COCKPIT CAMERA ZOOM", SimConnectUnit = "Percentage", DataType = DataType.Float64 },
new() { DefinitionId = DataDefinition.REQUIRED_DEFINITION, RequestId = DataRequest.REQUIRED_REQUEST, DataDefinitionType = DataDefinitionType.SimConnect, PropName = PropName.PlaneAltAboveGround, VariableName = "PLANE ALT ABOVE GROUND", SimConnectUnit = "Feet", DataType = DataType.Float64 },
new() { DefinitionId = DataDefinition.REQUIRED_DEFINITION, RequestId = DataRequest.REQUIRED_REQUEST, DataDefinitionType = DataDefinitionType.SimConnect, PropName = PropName.CameraViewTypeAndIndex0, VariableName = "CAMERA VIEW TYPE AND INDEX:0", SimConnectUnit = "Enum", DataType = DataType.Float64 }, new() { DefinitionId = DataDefinition.REQUIRED_DEFINITION, RequestId = DataRequest.REQUIRED_REQUEST, DataDefinitionType = DataDefinitionType.SimConnect, PropName = PropName.CameraViewTypeAndIndex0, VariableName = "CAMERA VIEW TYPE AND INDEX:0", SimConnectUnit = "Enum", DataType = DataType.Float64 },
new() { DefinitionId = DataDefinition.REQUIRED_DEFINITION, RequestId = DataRequest.REQUIRED_REQUEST, DataDefinitionType = DataDefinitionType.SimConnect, PropName = PropName.CameraViewTypeAndIndex1, VariableName = "CAMERA VIEW TYPE AND INDEX:1", SimConnectUnit = "Enum", DataType = DataType.Float64 }, new() { DefinitionId = DataDefinition.REQUIRED_DEFINITION, RequestId = DataRequest.REQUIRED_REQUEST, DataDefinitionType = DataDefinitionType.SimConnect, PropName = PropName.CameraViewTypeAndIndex1, VariableName = "CAMERA VIEW TYPE AND INDEX:1", SimConnectUnit = "Enum", DataType = DataType.Float64 },
new() { DefinitionId = DataDefinition.REQUIRED_DEFINITION, RequestId = DataRequest.REQUIRED_REQUEST, DataDefinitionType = DataDefinitionType.SimConnect, PropName = PropName.CameraViewTypeAndIndex1Max, VariableName = "CAMERA VIEW TYPE AND INDEX MAX:1", SimConnectUnit = "Number", DataType = DataType.Float64 }, new() { DefinitionId = DataDefinition.REQUIRED_DEFINITION, RequestId = DataRequest.REQUIRED_REQUEST, DataDefinitionType = DataDefinitionType.SimConnect, PropName = PropName.CameraViewTypeAndIndex1Max, VariableName = "CAMERA VIEW TYPE AND INDEX MAX:1", SimConnectUnit = "Number", DataType = DataType.Float64 },
@ -44,6 +43,18 @@ namespace MSFSPopoutPanelManager.SimConnectAgent
} }
} }
public static List<SimConnectDataDefinition> GetDynamicLodDefinitions()
{
var definitions = new List<SimConnectDataDefinition>
{
new() { DefinitionId = DataDefinition.DYNAMICLOD_DEFINITION, RequestId = DataRequest.DYNAMICLOD_REQUEST, DataDefinitionType = DataDefinitionType.SimConnect, PropName = PropName.PlaneAltAboveGround, VariableName = "PLANE ALT ABOVE GROUND", SimConnectUnit = "feet", DataType = DataType.Float64 },
new() { DefinitionId = DataDefinition.DYNAMICLOD_DEFINITION, RequestId = DataRequest.DYNAMICLOD_REQUEST, DataDefinitionType = DataDefinitionType.SimConnect, PropName = PropName.PlaneAltAboveGroundMinusCg, VariableName = "PLANE ALT ABOVE GROUND MINUS CG", SimConnectUnit = "feet", DataType = DataType.Float64 },
new() { DefinitionId = DataDefinition.DYNAMICLOD_DEFINITION, RequestId = DataRequest.DYNAMICLOD_REQUEST, DataDefinitionType = DataDefinitionType.SimConnect, PropName = PropName.SimOnGround, VariableName = "SIM ON GROUND", SimConnectUnit = "Bool", DataType = DataType.Float64 },
new() { DefinitionId = DataDefinition.DYNAMICLOD_DEFINITION, RequestId = DataRequest.DYNAMICLOD_REQUEST, DataDefinitionType = DataDefinitionType.SimConnect, PropName = PropName.GroundVelocity, VariableName = "GROUND VELOCITY", SimConnectUnit = "knots", DataType = DataType.Float64 }
};
return definitions;
}
private static List<SimConnectDataDefinition> GetSharedHudBarDefinitions() private static List<SimConnectDataDefinition> GetSharedHudBarDefinitions()
{ {
var definitions = new List<SimConnectDataDefinition> var definitions = new List<SimConnectDataDefinition>
@ -80,7 +91,7 @@ namespace MSFSPopoutPanelManager.SimConnectAgent
}; };
return definitions; return definitions;
} }
public static class PropName public static class PropName
{ {
public static string ElectricalMasterBattery = "ElectricalMasterBattery"; public static string ElectricalMasterBattery = "ElectricalMasterBattery";
@ -96,6 +107,9 @@ namespace MSFSPopoutPanelManager.SimConnectAgent
// Dynamic LOD // Dynamic LOD
public static string PlaneAltAboveGround = "PlaneAltAboveGround"; public static string PlaneAltAboveGround = "PlaneAltAboveGround";
public static string PlaneAltAboveGroundMinusCg = "PlaneAltAboveGroundMinusCg";
public static string SimOnGround = "SimOnGround";
public static string GroundVelocity = "GroundVelocity";
// Hud Bar data // Hud Bar data
public static string ElevatorTrim = "ElevatorTrim"; public static string ElevatorTrim = "ElevatorTrim";

View file

@ -11,9 +11,9 @@
<PackageProjectUrl>https://github.com/hawkeye-stan/msfs-popout-panel-manager</PackageProjectUrl> <PackageProjectUrl>https://github.com/hawkeye-stan/msfs-popout-panel-manager</PackageProjectUrl>
<RootNamespace>MSFSPopoutPanelManager.SimConnectAgent</RootNamespace> <RootNamespace>MSFSPopoutPanelManager.SimConnectAgent</RootNamespace>
<Platforms>x64</Platforms> <Platforms>x64</Platforms>
<Version>4.1.0.4</Version> <Version>4.1.1.0</Version>
<AssemblyVersion>4.1.0.4</AssemblyVersion> <AssemblyVersion>4.1.1.0</AssemblyVersion>
<FileVersion>4.1.0.4</FileVersion> <FileVersion>4.1.1.0</FileVersion>
<RuntimeIdentifier>win-x64</RuntimeIdentifier> <RuntimeIdentifier>win-x64</RuntimeIdentifier>
<DebugType>Embedded</DebugType> <DebugType>Embedded</DebugType>
<Configurations>Debug;Release;Local</Configurations> <Configurations>Debug;Release;Local</Configurations>

View file

@ -293,7 +293,12 @@ namespace MSFSPopoutPanelManager.WindowsAgent
var bottomEdge = rect.Y + rect.Height; var bottomEdge = rect.Y + rect.Height;
return point.X >= rect.X && point.X <= rightEdge && point.Y >= rect.Y && point.Y <= bottomEdge; return point.X >= rect.X && point.X <= rightEdge && point.Y >= rect.Y && point.Y <= bottomEdge;
}
public static bool IsMsfsInFocus()
{
var handle = PInvoke.GetForegroundWindow();
return PInvoke.GetWindowText(handle).Substring(0, 26).Equals("Microsoft Flight Simulator", StringComparison.InvariantCultureIgnoreCase);
} }
} }
} }

View file

@ -11,9 +11,9 @@
<PackageProjectUrl>https://github.com/hawkeye-stan/msfs-popout-panel-manager</PackageProjectUrl> <PackageProjectUrl>https://github.com/hawkeye-stan/msfs-popout-panel-manager</PackageProjectUrl>
<RootNamespace>MSFSPopoutPanelManager.WindowsAgent</RootNamespace> <RootNamespace>MSFSPopoutPanelManager.WindowsAgent</RootNamespace>
<Platforms>x64</Platforms> <Platforms>x64</Platforms>
<Version>4.1.0.4</Version> <Version>4.1.1.0</Version>
<AssemblyVersion>4.1.0.4</AssemblyVersion> <AssemblyVersion>4.1.1.0</AssemblyVersion>
<FileVersion>4.1.0.4</FileVersion> <FileVersion>4.1.1.0</FileVersion>
<RuntimeIdentifier>win-x64</RuntimeIdentifier> <RuntimeIdentifier>win-x64</RuntimeIdentifier>
<DebugType>Embedded</DebugType> <DebugType>Embedded</DebugType>
<Configurations>Debug;Release;Local</Configurations> <Configurations>Debug;Release;Local</Configurations>