mirror of
https://github.com/Maassoft/ColorControl.git
synced 2026-10-18 23:07:15 -05:00
fix several issues, add remote controls to new UI
This commit is contained in:
1 parent
c6c7e1ccc0
commit
827157f30f
91 files changed
+4140
-2530
No files matched your search
@@ -72,6 +72,21 @@
|
||||
<Content Update="wwwroot\helper.js">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Update="wwwroot\remotes\LG_FactorySVC.jpg">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Update="wwwroot\remotes\LG_MR20GA.jpg">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Update="wwwroot\remotes\LG_MR23GN.jpg">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Update="wwwroot\remotes\Samsung_AA81-00243A.jpg">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Update="wwwroot\remotes\Samsung_BN59-01315B.jpg">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -4,8 +4,6 @@
|
||||
@rendermode Constants.RenderMode
|
||||
|
||||
@inject RpcUiClientService _rpcClientService
|
||||
@inject AppState AppState
|
||||
@inject NavigationManager _navigationManager;
|
||||
|
||||
<div class="top-row ps-3 navbar navbar-dark">
|
||||
<div class="container-fluid">
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
{
|
||||
<input class="form-check-input me-1" style="font-size: 1rem; visibility: hidden" type="checkbox">
|
||||
}
|
||||
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#displaySettings">
|
||||
<button class="accordion-button collapsed py-2" type="button" data-bs-toggle="collapse" data-bs-target="#displaySettings">
|
||||
Display
|
||||
</button>
|
||||
</span>
|
||||
@@ -264,10 +264,7 @@
|
||||
</label>
|
||||
</div>
|
||||
}
|
||||
<div class="mb-2">
|
||||
<label class="form-range" for="sdrBrightness">SDR brightness (@Preset.SDRBrightness%)</label>
|
||||
<input class="form-range" type="range" min="0" max="100" @bind="Preset.SDRBrightness" id="sdrBrightness">
|
||||
</div>
|
||||
<NullableRangeInput TValue="int" @bind-Value="Preset.SDRBrightness" Id="sdrBrightness" Label="SDR brightness" Unit="%" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
}
|
||||
else
|
||||
{
|
||||
<div class="row mb-3">
|
||||
<div class="row mb-2">
|
||||
@if (!Displays.Any())
|
||||
{
|
||||
<span>No displays found</span>
|
||||
@@ -54,6 +54,13 @@ else
|
||||
}
|
||||
else
|
||||
{
|
||||
<div class="row mb-3">
|
||||
<div class="form-check form-switch ms-3">
|
||||
<input class="form-check-input" type="checkbox" role="switch" id="usePerUserSettings" @bind="SelectedDisplay.UsePerUserSettings" @bind:after="AfterUsePerUseSettings">
|
||||
<label class="form-check-label" for="usePerUserSettings">Use my settings for this device</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-2 d-flex align-items-center">
|
||||
<h2>Display Associated Color profiles</h2>
|
||||
<div class="d-flex ms-auto">
|
||||
@@ -320,4 +327,19 @@ else
|
||||
|
||||
await SelectedDisplayChanged();
|
||||
}
|
||||
|
||||
private async Task AfterUsePerUseSettings()
|
||||
{
|
||||
if (SelectedDisplay == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var result = await _rpcClientService.CallAsync<bool>("ColorProfileService", "SetUsePerUserDisplayProfiles", SelectedDisplay.DeviceName, SelectedDisplay.UsePerUserSettings);
|
||||
|
||||
if (result)
|
||||
{
|
||||
await RefreshDisplays();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -9,272 +9,235 @@
|
||||
@rendermode Constants.RenderMode
|
||||
|
||||
@inject RpcUiClientService _rpcClientService
|
||||
@inject AppState AppState
|
||||
@inject NavigationManager _navigationManager;
|
||||
|
||||
<div class="modal" id="presetModal" tabindex="-1" data-bs-backdrop="static" data-bs-keyboard="false" @onfocus="ModalOnFocus">
|
||||
<div class="modal-dialog modal-dialog-scrollable modal-dialog-centered">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">@(Preset?.id == 0 ? $"Settings for new preset" : $"Settings of preset {Preset?.IdOrName}")</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close" @onclick="() => IsVisible = false"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form class="row g-1 needs-validation @WasValidated" novalidate>
|
||||
@if (Preset == null)
|
||||
{
|
||||
<div>Loading...</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
if (RecentApps != null)
|
||||
{
|
||||
<div class="mb-2">
|
||||
<label class="form-label" for="recentApp">Recent app</label>
|
||||
<select class="form-select" id="recentApp" @bind="RecentAppFilename" @bind:after="RecentAppSelected">
|
||||
@foreach (var app in RecentApps)
|
||||
{
|
||||
<option value="@app.Filename" title="@app.Path">@app.FriendlyName</option>
|
||||
}
|
||||
</select>
|
||||
</div>
|
||||
}
|
||||
<div class="mb-2">
|
||||
<label class="form-label" for="name">Name</label>
|
||||
<input class="form-control" id="name" type="text" @bind="Preset.name" required />
|
||||
</div>
|
||||
<div class="form-check mb-2">
|
||||
<input class="form-check-input" type="checkbox" @bind="Preset.ShowInQuickAccess" id="showInQuickAccess">
|
||||
<label class="form-check-label" for="showInQuickAccess">
|
||||
Show in Quick Access
|
||||
</label>
|
||||
</div>
|
||||
<div class="mb-2">
|
||||
<label class="form-label" for="path">File/URI</label>
|
||||
<div class="input-group mb-2">
|
||||
<input class="form-control" id="path" type="text" @bind="Preset.Path" required />
|
||||
<button type="button" class="btn btn-primary" @onclick="SelectFileOnClick" disabled="@SelectingFile">
|
||||
<span class="@(SelectingFile ? "spinner-border spinner-border-sm" : "")" aria-hidden="true"></span>
|
||||
<span role="status">Select...</span>
|
||||
</button>
|
||||
</div>
|
||||
<div id="selectFileHelp" class="form-text">
|
||||
Selecting a file opens a file dialog outside of the browser on the system where the ColorControl application is running.
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-2">
|
||||
<label class="form-label" for="parameters">Parameters</label>
|
||||
<input class="form-control" id="parameters" type="text" @bind="Preset.Parameters" />
|
||||
</div>
|
||||
<div class="modal-dialog modal-dialog-scrollable modal-dialog-centered">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">@(Preset?.id == 0 ? $"Settings for new preset" : $"Settings of preset {Preset?.IdOrName}")</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close" @onclick="() => IsVisible = false"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form class="row g-1 needs-validation @WasValidated" novalidate>
|
||||
@if (Preset == null)
|
||||
{
|
||||
<div>Loading...</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
if (RecentApps != null)
|
||||
{
|
||||
<div class="mb-2">
|
||||
<label class="form-label" for="recentApp">Recent app</label>
|
||||
<select class="form-select" id="recentApp" @bind="RecentAppFilename" @bind:after="RecentAppSelected">
|
||||
@foreach (var app in RecentApps)
|
||||
{
|
||||
<option value="@app.Filename" title="@app.Path">@app.FriendlyName</option>
|
||||
}
|
||||
</select>
|
||||
</div>
|
||||
}
|
||||
<div class="mb-2">
|
||||
<label class="form-label" for="name">Name</label>
|
||||
<input class="form-control" id="name" type="text" @bind="Preset.name" required />
|
||||
</div>
|
||||
<div class="form-check mb-2">
|
||||
<input class="form-check-input" type="checkbox" @bind="Preset.ShowInQuickAccess" id="showInQuickAccess">
|
||||
<label class="form-check-label" for="showInQuickAccess">
|
||||
Show in Quick Access
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="accordion accordion-flush" id="presetAccordion">
|
||||
<StartProgramInput @bind-Path="Preset.Path" @bind-Parameters="Preset.Parameters" @bind-Path:after="PathChanged"></StartProgramInput>
|
||||
|
||||
<div class="accordion-item">
|
||||
<h2 class="accordion-header">
|
||||
<button class="accordion-button collapsed py-2" type="button" data-bs-toggle="collapse" data-bs-target="#advancedSettings">
|
||||
Advanced settings
|
||||
</button>
|
||||
</h2>
|
||||
<div id="advancedSettings" class="accordion-collapse collapse" data-bs-parent="#presetAccordion">
|
||||
<div class="accordion-body">
|
||||
<div class="form-check mb-2">
|
||||
<input class="form-check-input" type="checkbox" @bind="Preset.RunAsAdministrator" id="runAsAdministrator">
|
||||
<label class="form-check-label" for="runAsAdministrator">
|
||||
Run as administrator
|
||||
</label>
|
||||
</div>
|
||||
<FlagsInput Label="Process affinity" Labels="CpuAffinityLabels" @bind-Value="Preset.ProcessAffinityMask"></FlagsInput>
|
||||
<div class="mb-2">
|
||||
<label class="form-label" for="priority">Process priority</label>
|
||||
<select class="form-select" id="priority" @bind="Preset.GameProcessPriorityClass">
|
||||
@foreach (var priority in Enum.GetValues<GamePriorityClass>())
|
||||
{
|
||||
<option value="@priority.ToString()">@priority.GetDescription()</option>
|
||||
}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="accordion-item">
|
||||
<h2 class="accordion-header">
|
||||
<button class="accordion-button collapsed py-2" type="button" data-bs-toggle="collapse" data-bs-target="#autoApplySettings">
|
||||
Auto-apply settings
|
||||
</button>
|
||||
</h2>
|
||||
<div id="autoApplySettings" class="accordion-collapse collapse" data-bs-parent="#presetAccordion">
|
||||
<div class="accordion-body">
|
||||
<div class="form-check mb-2">
|
||||
<input class="form-check-input" type="checkbox" @bind="Preset.AutoSettings.AllowAutoApply" id="autoApply">
|
||||
<label class="form-check-label" for="autoApply">
|
||||
Automatically execute preset when process starts
|
||||
</label>
|
||||
</div>
|
||||
<div class="mb-2">
|
||||
<label class="form-label" for="autoApplyAction">Auto-apply action</label>
|
||||
<select class="form-select" id="priority" @bind="Preset.AutoSettings.ProcessAutoAction">
|
||||
@foreach (var action in Enum.GetValues<ProcessAutoAction>())
|
||||
{
|
||||
<option value="@action.ToString()">@action.GetDescription()</option>
|
||||
}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="accordion-item">
|
||||
<h2 class="accordion-header">
|
||||
<button class="accordion-button collapsed py-2" type="button" data-bs-toggle="collapse" data-bs-target="#steps">
|
||||
Steps
|
||||
</button>
|
||||
</h2>
|
||||
<div id="steps" class="accordion-collapse collapse" data-bs-parent="#presetAccordion">
|
||||
<div class="accordion-body">
|
||||
<StepsInput Label="Pre-launch" @bind-Steps="Preset.PreLaunchSteps" TPreset="GamePreset"></StepsInput>
|
||||
<StepsInput Label="Post-launch" @bind-Steps="Preset.PostLaunchSteps" TPreset="GamePreset"></StepsInput>
|
||||
<StepsInput Label="Finalize" @bind-Steps="Preset.FinalizeSteps" TPreset="GamePreset"></StepsInput>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="accordion accordion-flush" id="presetAccordion">
|
||||
|
||||
</div>
|
||||
<div class="accordion-item">
|
||||
<h2 class="accordion-header">
|
||||
<button class="accordion-button collapsed py-2" type="button" data-bs-toggle="collapse" data-bs-target="#advancedSettings">
|
||||
Advanced settings
|
||||
</button>
|
||||
</h2>
|
||||
<div id="advancedSettings" class="accordion-collapse collapse" data-bs-parent="#presetAccordion">
|
||||
<div class="accordion-body">
|
||||
<div class="form-check mb-2">
|
||||
<input class="form-check-input" type="checkbox" @bind="Preset.RunAsAdministrator" id="runAsAdministrator">
|
||||
<label class="form-check-label" for="runAsAdministrator">
|
||||
Run as administrator
|
||||
</label>
|
||||
</div>
|
||||
<FlagsInput Label="Process affinity" Labels="CpuAffinityLabels" @bind-Value="Preset.ProcessAffinityMask"></FlagsInput>
|
||||
<div class="mb-2">
|
||||
<label class="form-label" for="priority">Process priority</label>
|
||||
<select class="form-select" id="priority" @bind="Preset.GameProcessPriorityClass">
|
||||
@foreach (var priority in Enum.GetValues<GamePriorityClass>())
|
||||
{
|
||||
<option value="@priority.ToString()">@priority.GetDescription()</option>
|
||||
}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="accordion-item">
|
||||
<h2 class="accordion-header">
|
||||
<button class="accordion-button collapsed py-2" type="button" data-bs-toggle="collapse" data-bs-target="#autoApplySettings">
|
||||
Auto-apply settings
|
||||
</button>
|
||||
</h2>
|
||||
<div id="autoApplySettings" class="accordion-collapse collapse" data-bs-parent="#presetAccordion">
|
||||
<div class="accordion-body">
|
||||
<div class="form-check mb-2">
|
||||
<input class="form-check-input" type="checkbox" @bind="Preset.AutoSettings.AllowAutoApply" id="autoApply">
|
||||
<label class="form-check-label" for="autoApply">
|
||||
Automatically execute preset when process starts
|
||||
</label>
|
||||
</div>
|
||||
<div class="mb-2">
|
||||
<label class="form-label" for="autoApplyAction">Auto-apply action</label>
|
||||
<select class="form-select" id="priority" @bind="Preset.AutoSettings.ProcessAutoAction">
|
||||
@foreach (var action in Enum.GetValues<ProcessAutoAction>())
|
||||
{
|
||||
<option value="@action.ToString()">@action.GetDescription()</option>
|
||||
}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="accordion-item">
|
||||
<h2 class="accordion-header">
|
||||
<button class="accordion-button collapsed py-2" type="button" data-bs-toggle="collapse" data-bs-target="#steps">
|
||||
Steps
|
||||
</button>
|
||||
</h2>
|
||||
<div id="steps" class="accordion-collapse collapse" data-bs-parent="#presetAccordion">
|
||||
<div class="accordion-body">
|
||||
<StepsInput Label="Pre-launch" @bind-Steps="Preset.PreLaunchSteps" TPreset="GamePreset"></StepsInput>
|
||||
<StepsInput Label="Post-launch" @bind-Steps="Preset.PostLaunchSteps" TPreset="GamePreset"></StepsInput>
|
||||
<StepsInput Label="Finalize" @bind-Steps="Preset.FinalizeSteps" TPreset="GamePreset"></StepsInput>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
}
|
||||
</form>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal" @onclick="() => IsVisible = false">Close</button>
|
||||
<button type="submit" class="btn btn-primary" data-bs-dismiss="@(IsValid() ? "modal" : "")" @onclick="ApplyClick">Save</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
}
|
||||
</form>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal" @onclick="() => IsVisible = false">Close</button>
|
||||
<button type="submit" class="btn btn-primary" data-bs-dismiss="@(IsValid() ? "modal" : "")" @onclick="ApplyClick">Save</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@code {
|
||||
private GamePreset? Preset;
|
||||
private GamePreset? Preset;
|
||||
|
||||
[Parameter]
|
||||
public GamePreset? PresetParam { get; set; }
|
||||
[Parameter]
|
||||
public GamePreset? PresetParam { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public Func<GamePreset, Task>? AfterApply { get; set; }
|
||||
[Parameter]
|
||||
public Func<GamePreset, Task>? AfterApply { get; set; }
|
||||
|
||||
public List<string> CpuAffinityLabels = Enumerable.Range(0, Environment.ProcessorCount).Select(i => $"CPU #{i}").ToList();
|
||||
private List<GameApp>? RecentApps;
|
||||
private string RecentAppFilename = "";
|
||||
public List<string> CpuAffinityLabels = Enumerable.Range(0, Environment.ProcessorCount).Select(i => $"CPU #{i}").ToList();
|
||||
private List<GameApp>? RecentApps;
|
||||
private string RecentAppFilename = "";
|
||||
|
||||
public bool SelectingFile { get; set; }
|
||||
public bool SelectingFile { get; set; }
|
||||
|
||||
private string WasValidated = "";
|
||||
private bool IsVisible;
|
||||
private string WasValidated = "";
|
||||
private bool IsVisible;
|
||||
|
||||
protected override async Task OnParametersSetAsync()
|
||||
{
|
||||
if (PresetParam == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
protected override async Task OnParametersSetAsync()
|
||||
{
|
||||
if (PresetParam == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Preset = PresetParam.CloneWithId();
|
||||
Preset = PresetParam.CloneWithId();
|
||||
|
||||
if (Preset.id == 0)
|
||||
{
|
||||
RecentApps = await _rpcClientService.CallAsync<List<GameApp>>("GameService", "GetMruApps", true);
|
||||
RecentAppFilename = "";
|
||||
if (Preset.id == 0)
|
||||
{
|
||||
RecentApps = await _rpcClientService.CallAsync<List<GameApp>>("GameService", "GetMruApps", true);
|
||||
RecentAppFilename = "";
|
||||
|
||||
if (RecentApps != null)
|
||||
{
|
||||
RecentApps.Insert(0, new GameApp { Filename = "", FriendlyName = "Choose a recent app to fill values" });
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
RecentApps = null;
|
||||
}
|
||||
}
|
||||
if (RecentApps != null)
|
||||
{
|
||||
RecentApps.Insert(0, new GameApp { Filename = "", FriendlyName = "Choose a recent app to fill values" });
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
RecentApps = null;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task ApplyClick(MouseEventArgs e)
|
||||
{
|
||||
if (Preset == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
private async Task ApplyClick(MouseEventArgs e)
|
||||
{
|
||||
if (Preset == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!IsValid())
|
||||
{
|
||||
WasValidated = "was-validated";
|
||||
return;
|
||||
}
|
||||
if (!IsValid())
|
||||
{
|
||||
WasValidated = "was-validated";
|
||||
return;
|
||||
}
|
||||
|
||||
var result = await _rpcClientService.CallAsync<bool>("GameService", "UpdatePreset", Preset);
|
||||
var result = await _rpcClientService.CallAsync<bool>("GameService", "UpdatePreset", Preset);
|
||||
|
||||
if (AfterApply != null)
|
||||
{
|
||||
await AfterApply.Invoke(Preset);
|
||||
}
|
||||
if (AfterApply != null)
|
||||
{
|
||||
await AfterApply.Invoke(Preset);
|
||||
}
|
||||
|
||||
IsVisible = false;
|
||||
}
|
||||
IsVisible = false;
|
||||
}
|
||||
|
||||
private bool IsValid()
|
||||
{
|
||||
return !string.IsNullOrEmpty(Preset?.name) && !string.IsNullOrEmpty(Preset?.Path);
|
||||
}
|
||||
private bool IsValid()
|
||||
{
|
||||
return !string.IsNullOrEmpty(Preset?.name) && !string.IsNullOrEmpty(Preset?.Path);
|
||||
}
|
||||
|
||||
private async Task SelectFileOnClick(MouseEventArgs e)
|
||||
{
|
||||
if (Preset == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
private void RecentAppSelected()
|
||||
{
|
||||
if (RecentApps == null || Preset == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
SelectingFile = true;
|
||||
try
|
||||
{
|
||||
var options = new RpcUiClientOptions
|
||||
{
|
||||
Timeout = 60000
|
||||
};
|
||||
var app = RecentApps.FirstOrDefault(a => a.Filename == RecentAppFilename);
|
||||
|
||||
var result = await _rpcClientService.CallWithOptionsAsync<string>("GameService", "SelectPath", options);
|
||||
if (app != null)
|
||||
{
|
||||
Preset.name = app.FriendlyName;
|
||||
Preset.Path = app.Path;
|
||||
}
|
||||
}
|
||||
|
||||
if (result != null)
|
||||
{
|
||||
Preset.Path = result;
|
||||
if (string.IsNullOrEmpty(Preset.name))
|
||||
{
|
||||
Preset.name = Path.GetFileNameWithoutExtension(result);
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
SelectingFile = false;
|
||||
}
|
||||
}
|
||||
private void ModalOnFocus(FocusEventArgs e)
|
||||
{
|
||||
if (!IsVisible)
|
||||
{
|
||||
WasValidated = "";
|
||||
IsVisible = true;
|
||||
}
|
||||
}
|
||||
private void PathChanged()
|
||||
{
|
||||
if (Preset == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
private void RecentAppSelected()
|
||||
{
|
||||
if (RecentApps == null || Preset == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var app = RecentApps.FirstOrDefault(a => a.Filename == RecentAppFilename);
|
||||
|
||||
if (app != null)
|
||||
{
|
||||
Preset.name = app.FriendlyName;
|
||||
Preset.Path = app.Path;
|
||||
}
|
||||
}
|
||||
|
||||
private void ModalOnFocus(FocusEventArgs e)
|
||||
{
|
||||
if (!IsVisible)
|
||||
{
|
||||
WasValidated = "";
|
||||
IsVisible = true;
|
||||
}
|
||||
}
|
||||
if (Preset.Path != null && string.IsNullOrEmpty(Preset.name))
|
||||
{
|
||||
Preset.name = Path.GetFileNameWithoutExtension(Preset.Path);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
@rendermode Constants.RenderMode
|
||||
|
||||
<FieldsModal ModalId="@ModalId" Fields="Fields" Title="@Title" OnConfirm="ConfirmClick"></FieldsModal>
|
||||
<FieldsModal ModalId="@ModalId" Fields="Fields" Title="@Title" OnConfirm="ConfirmClick" NextModalId="@NextModalId"></FieldsModal>
|
||||
|
||||
@code {
|
||||
[Parameter]
|
||||
@@ -19,6 +19,9 @@
|
||||
[Parameter]
|
||||
public string ModalId { get; set; } = "confirmModal";
|
||||
|
||||
[Parameter]
|
||||
public string? NextModalId { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public List<FieldDefinition>? ExtraFields { get; set; }
|
||||
|
||||
|
||||
@@ -4,8 +4,6 @@
|
||||
|
||||
@rendermode Constants.RenderMode
|
||||
|
||||
@inject NavigationManager _navigationManager;
|
||||
|
||||
@if (Fields == null)
|
||||
{
|
||||
return;
|
||||
@@ -106,16 +104,7 @@
|
||||
{
|
||||
var fieldId = $"fld_{Random.Shared.Next()}";
|
||||
|
||||
<div class="mb-2">
|
||||
<label class="form-range" for="@fieldId">@field.Label (Current value: @field.IntValue)</label>
|
||||
<input class="form-range" type="range" min="@((int)field.MinValue)" max="@((int)field.MaxValue)" @bind="field.IntValue" id="@fieldId">
|
||||
@if (field.SubLabel != null)
|
||||
{
|
||||
<div id="@(fieldId)_help" class="form-text">
|
||||
@field.SubLabel
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
<RangeInput TValue="int" @bind-Value="field.IntValue" Id="@fieldId" Label="@field.Label" Min="@((int)field.MinValue)" Max="@((int)field.MaxValue)" SubLabel="@field.SubLabel" />
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,8 +3,7 @@
|
||||
@using ColorControl.Shared.Common;
|
||||
|
||||
@rendermode Constants.RenderMode
|
||||
|
||||
@inject NavigationManager _navigationManager;
|
||||
@inject JSHelper jsHelper;
|
||||
|
||||
<div class="modal" id="@ModalId" tabindex="-1" data-bs-backdrop="static" data-bs-keyboard="false" @onfocus="ModalFocus">
|
||||
<div class="modal-dialog modal-dialog-scrollable modal-dialog-centered">
|
||||
@@ -41,6 +40,9 @@
|
||||
[Parameter]
|
||||
public string? ModalId { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public string? NextModalId { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public string? ConfirmCaption { get; set; } = "Yes";
|
||||
|
||||
@@ -120,8 +122,13 @@
|
||||
}
|
||||
}
|
||||
|
||||
private void OnClose(MouseEventArgs e)
|
||||
private async Task OnClose(MouseEventArgs e)
|
||||
{
|
||||
IsVisible = false;
|
||||
|
||||
if (!string.IsNullOrEmpty(NextModalId))
|
||||
{
|
||||
await jsHelper.OpenModal(NextModalId);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
@using ColorControl.Shared.Contracts
|
||||
@using ColorControl.Shared.EventDispatcher
|
||||
@using ColorControl.UI.Services;
|
||||
@using ColorControl.Shared.Common;
|
||||
|
||||
@* @rendermode Constants.RenderMode *@
|
||||
|
||||
@typeparam TValue where TValue : struct, IComparable, IFormattable, IConvertible, IComparable<TValue>, IEquatable<TValue>
|
||||
|
||||
@if (Id != null)
|
||||
{
|
||||
<div class="mb-2">
|
||||
<label class="form-label @(ExtraLabelStyling ?? "")" for="@Id">@Label (@Value@Unit)</label>
|
||||
<input class="form-range" type="range" min="@(Values?.Min() ?? Min)" max="@(Values?.Max() ?? Max)" @bind="Value" id="@Id" @oninput="e => RangeOnChange(e)">
|
||||
</div>
|
||||
}
|
||||
|
||||
@code {
|
||||
|
||||
[Parameter]
|
||||
public TValue? Value { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public string? Label { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public string? Unit { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public string? Id { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public TValue? Min { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public TValue? Max { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public List<TValue>? Values { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public EventCallback<TValue?> ValueChanged { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public Func<TValue?, string>? LabelStylingFunc { get; set; }
|
||||
|
||||
private string? ExtraLabelStyling;
|
||||
|
||||
protected override void OnParametersSet()
|
||||
{
|
||||
ExtraLabelStyling = LabelStylingFunc?.Invoke(Value);
|
||||
Min ??= (TValue)Convert.ChangeType(0, typeof(TValue));
|
||||
Max ??= (TValue)Convert.ChangeType(100, typeof(TValue));
|
||||
}
|
||||
|
||||
private async Task RangeOnChange(ChangeEventArgs eventArgs)
|
||||
{
|
||||
if (typeof(TValue) == typeof(int))
|
||||
{
|
||||
Value = (TValue)Convert.ChangeType(int.Parse(eventArgs.Value?.ToString() ?? "0"), typeof(TValue));
|
||||
|
||||
}
|
||||
else if (typeof(TValue) == typeof(uint))
|
||||
{
|
||||
Value = (TValue)Convert.ChangeType(uint.Parse(eventArgs.Value?.ToString() ?? "0"), typeof(TValue));
|
||||
}
|
||||
|
||||
await ValueChanged.InvokeAsync(Value);
|
||||
|
||||
ExtraLabelStyling = LabelStylingFunc?.Invoke(Value);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
@using ColorControl.Shared.Contracts
|
||||
@using ColorControl.Shared.EventDispatcher
|
||||
@using ColorControl.UI.Services;
|
||||
@using ColorControl.Shared.Common;
|
||||
|
||||
@* @rendermode Constants.RenderMode *@
|
||||
|
||||
@typeparam TValue where TValue : struct, IComparable, IFormattable, IConvertible, IComparable<TValue>, IEquatable<TValue>
|
||||
|
||||
@if (Id != null)
|
||||
{
|
||||
<div class="mb-2">
|
||||
<label class="form-label @(ExtraLabelStyling ?? "")" for="@Id">@Label: @GetValueDisplay()@Unit</label>
|
||||
<input class="form-range" type="range" step="@Step" min="@(Values?.Min() ?? Min)" max="@(Values?.Max() ?? Max)" @bind="Value" id="@Id" @oninput="e => RangeOnChange(e)">
|
||||
@if (SubLabel != null)
|
||||
{
|
||||
<div id="@(Id)_help" class="form-text">
|
||||
@SubLabel
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
|
||||
@code {
|
||||
|
||||
[Parameter]
|
||||
public TValue Value { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public string? Label { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public string? SubLabel { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public string? Unit { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public string? UnitString { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public string? Id { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public TValue? Min { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public TValue? Max { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public TValue? Step { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public List<TValue>? Values { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public EventCallback<TValue> ValueChanged { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public Func<TValue, string>? LabelStylingFunc { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public Func<TValue, string>? CustomLabelFunc { get; set; }
|
||||
|
||||
private string? ExtraLabelStyling;
|
||||
|
||||
protected override void OnParametersSet()
|
||||
{
|
||||
ExtraLabelStyling = LabelStylingFunc?.Invoke(Value);
|
||||
Min ??= (TValue)Convert.ChangeType(0, typeof(TValue));
|
||||
Max ??= (TValue)Convert.ChangeType(100, typeof(TValue));
|
||||
Step ??= (TValue)Convert.ChangeType(1, typeof(TValue));
|
||||
}
|
||||
|
||||
private string? GetValueDisplay()
|
||||
{
|
||||
if (CustomLabelFunc != null)
|
||||
{
|
||||
return CustomLabelFunc(Value);
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(UnitString))
|
||||
{
|
||||
return Value.ToString();
|
||||
}
|
||||
|
||||
if (Value is uint uintValue)
|
||||
{
|
||||
return uintValue.ToUnitString(UnitString);
|
||||
}
|
||||
if (Value is int intValue)
|
||||
{
|
||||
return intValue.ToSignedUnitString(UnitString);
|
||||
}
|
||||
|
||||
return Value.ToString();
|
||||
}
|
||||
|
||||
private async Task RangeOnChange(ChangeEventArgs eventArgs)
|
||||
{
|
||||
if (typeof(TValue) == typeof(int))
|
||||
{
|
||||
Value = (TValue)Convert.ChangeType(int.Parse(eventArgs.Value?.ToString() ?? "0"), typeof(TValue));
|
||||
|
||||
}
|
||||
else if (typeof(TValue) == typeof(uint))
|
||||
{
|
||||
Value = (TValue)Convert.ChangeType(uint.Parse(eventArgs.Value?.ToString() ?? "0"), typeof(TValue));
|
||||
}
|
||||
|
||||
await ValueChanged.InvokeAsync(Value);
|
||||
|
||||
ExtraLabelStyling = LabelStylingFunc?.Invoke(Value);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -19,7 +19,8 @@
|
||||
<button class="btn btn-secondary dropdown-toggle" type="button" data-bs-toggle="dropdown" aria-expanded="false">
|
||||
</button>
|
||||
<ul class="dropdown-menu">
|
||||
@foreach (var modifier in Modifiers) {
|
||||
@foreach (var modifier in Modifiers)
|
||||
{
|
||||
<li>
|
||||
<a class="dropdown-item" @onclick="() => ModifierOnClick(modifier.Key)">
|
||||
<input class="form-check-input me-2" type="checkbox" id="flexCheckChecked" checked="@modifier.Value" @onchange="(e) => ModifierKeyChanged(e, modifier.Key)">@modifier.Key
|
||||
@@ -44,9 +45,9 @@
|
||||
private Dictionary<string, bool> Modifiers = new() {
|
||||
{ "Shift", false },
|
||||
{ "Control", false },
|
||||
{ "Alt", false },
|
||||
{ "Win", false }
|
||||
};
|
||||
{ "Alt", false },
|
||||
{ "Win", false }
|
||||
};
|
||||
|
||||
protected override void OnParametersSet()
|
||||
{
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
@using ColorControl.Shared.Contracts
|
||||
@using ColorControl.Shared.EventDispatcher
|
||||
@using ColorControl.UI.Services;
|
||||
@using ColorControl.Shared.Common;
|
||||
|
||||
@inject RpcUiClientService _rpcClientService
|
||||
|
||||
<div class="mb-2">
|
||||
<label class="form-label" for="path">File/URI</label>
|
||||
<div class="input-group mb-2">
|
||||
<input class="form-control" id="path" type="text" @bind="Path" required @onblur="() => PathChanged.InvokeAsync(Path)" />
|
||||
<button type="button" class="btn btn-primary" @onclick="SelectFileOnClick" disabled="@SelectingFile">
|
||||
<span class="@(SelectingFile ? "spinner-border spinner-border-sm" : "")" aria-hidden="true"></span>
|
||||
<span role="status">Select...</span>
|
||||
</button>
|
||||
</div>
|
||||
<div id="selectFileHelp" class="form-text">
|
||||
Selecting a file opens a file dialog outside of the browser on the system where the ColorControl application is running.
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-2">
|
||||
<label class="form-label" for="parameters">Parameters</label>
|
||||
<input class="form-control" id="parameters" type="text" @bind="Parameters" @bind:after="() => ParametersChanged.InvokeAsync(Parameters)" />
|
||||
</div>
|
||||
|
||||
@code {
|
||||
[Parameter]
|
||||
public string? Path { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public string? Parameters { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public EventCallback<string?> PathChanged { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public EventCallback<string?> ParametersChanged { get; set; }
|
||||
|
||||
private bool SelectingFile;
|
||||
|
||||
private async Task SelectFileOnClick(MouseEventArgs e)
|
||||
{
|
||||
SelectingFile = true;
|
||||
try
|
||||
{
|
||||
var options = new RpcUiClientOptions
|
||||
{
|
||||
Timeout = 60000
|
||||
};
|
||||
|
||||
var result = await _rpcClientService.CallWithOptionsAsync<string>("GameService", "SelectPath", options);
|
||||
|
||||
if (result != null)
|
||||
{
|
||||
Path = result;
|
||||
await PathChanged.InvokeAsync(Path);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
SelectingFile = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -12,22 +12,21 @@
|
||||
@if (Label != null && Steps != null)
|
||||
{
|
||||
var index = 0;
|
||||
|
||||
|
||||
<div class="mb-2">
|
||||
<div class="d-flex align-items-center">
|
||||
<label class="form-label" for="name">@Label</label>
|
||||
<div class="d-flex ms-auto">
|
||||
@if (NvPresets != null || SamsungPresets != null || CustomItems != null)
|
||||
@if (NvPresets != null || SamsungPresets != null || LgPresets != null || CustomItems != null)
|
||||
{
|
||||
<div class="dropdown dropend mb-1">
|
||||
<button type="button" class="btn btn-primary btn-sm dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false" data-bs-auto-close="outside">Add</button>
|
||||
<form class="dropdown-menu p-4 dropdown-menu-end" style="width: 200px">
|
||||
<form class="dropdown-menu p-4 dropdown-menu-end" style="width: 360px">
|
||||
@if (NvPresets != null && NvPresetsEnabled)
|
||||
{
|
||||
<div class="mb-2">
|
||||
<label class="form-label" for="nvPreset">NVIDIA preset</label>
|
||||
<select class="form-select" id="nvPreset" @bind="NvPresetToAddId">
|
||||
<option value="">Select preset</option>
|
||||
<select class="form-select" multiple id="nvPreset" @onchange="NvPresetChange">
|
||||
@foreach (var preset in NvPresets)
|
||||
{
|
||||
<option value="@preset.id">@preset.IdOrName</option>
|
||||
@@ -77,6 +76,9 @@
|
||||
</div>
|
||||
}
|
||||
}
|
||||
|
||||
<StartProgramInput @bind-Path="StartProgramPath" @bind-Parameters="StartProgramParameters"></StartProgramInput>
|
||||
|
||||
@if (ActionItems != null)
|
||||
{
|
||||
foreach (var customItem in ActionItems)
|
||||
@@ -85,15 +87,15 @@
|
||||
<label class="form-label" for="advancedCustomItem">@customItem.Name</label>
|
||||
<select class="form-select" id="advancedCustomItem" @bind="customItem.SelectedActionName" @bind:after="() => AdvancedItemChanged(customItem)">
|
||||
<option value="">Select action</option>
|
||||
@foreach (var keyValue in customItem.GroupedActions)
|
||||
{
|
||||
<optgroup label="@keyValue.Key">
|
||||
@foreach (var item in keyValue.Value)
|
||||
{
|
||||
<option value="@item.Name">@item.Title</option>
|
||||
}
|
||||
</optgroup>
|
||||
}
|
||||
@foreach (var keyValue in customItem.GroupedActions)
|
||||
{
|
||||
<optgroup label="@keyValue.Key">
|
||||
@foreach (var item in keyValue.Value)
|
||||
{
|
||||
<option value="@item.Name">@item.Title</option>
|
||||
}
|
||||
</optgroup>
|
||||
}
|
||||
</select>
|
||||
</div>
|
||||
<FieldDefs Fields="ActionFields"></FieldDefs>
|
||||
@@ -126,10 +128,7 @@
|
||||
<button type="button" class="btn btn-primary btn-sm" data-bs-dismiss="dropdown" title="Move right" disabled="@(stepIndex == PresetSteps.Count - 1)" @onclick="() => MoveRightOnClick(stepIndex)">></button>
|
||||
<button type="button" class="btn btn-primary btn-sm" data-bs-dismiss="dropdown" @onclick="() => RemoveOnClick(stepIndex)">Remove</button>
|
||||
</div>
|
||||
<div class="mb-2">
|
||||
<label class="form-label" for="delay">Delay (@(step.Delay)ms)</label>
|
||||
<input class="form-range" id="delay" type="range" min="0" max="10000" step="50" @bind="step.Delay" @bind:after="() => AfterDelayChanged()">
|
||||
</div>
|
||||
<RangeInput TValue="int" @bind-Value="step.Delay" Id="delay" Label="Delay" Unit="ms" Max="10000" @bind-Value:after="() => AfterDelayChanged()" />
|
||||
</form>
|
||||
|
||||
index++;
|
||||
@@ -212,7 +211,6 @@
|
||||
private List<SamsungPreset>? SamsungPresets { get; set; }
|
||||
private List<LgPreset>? LgPresets { get; set; }
|
||||
|
||||
private int? NvPresetToAddId { get; set; }
|
||||
private int? SamsungPresetToAddId { get; set; }
|
||||
private int? LgPresetToAddId { get; set; }
|
||||
|
||||
@@ -222,6 +220,11 @@
|
||||
|
||||
private List<FieldDefinition>? ActionFields;
|
||||
|
||||
private string? StartProgramPath;
|
||||
private string? StartProgramParameters;
|
||||
|
||||
private List<int> NvPresetsToAdd = [];
|
||||
|
||||
protected override void OnParametersSet()
|
||||
{
|
||||
if (Steps != null)
|
||||
@@ -295,7 +298,7 @@
|
||||
|
||||
private async Task AddPresetOnClick()
|
||||
{
|
||||
AddPreset(NvPresets, "NvPreset", NvPresetToAddId);
|
||||
NvPresetsToAdd.ForEach(id => AddPreset(NvPresets, "NvPreset", id));
|
||||
AddPreset(SamsungPresets, "SamsungPreset", SamsungPresetToAddId);
|
||||
AddPreset(LgPresets, "LgPreset", LgPresetToAddId);
|
||||
|
||||
@@ -328,6 +331,8 @@
|
||||
}
|
||||
}
|
||||
|
||||
AddStartProgram();
|
||||
|
||||
await EmitSteps();
|
||||
}
|
||||
|
||||
@@ -343,6 +348,22 @@
|
||||
PresetSteps.Add(PresetStep.Parse($"{type}({preset.name})"));
|
||||
}
|
||||
|
||||
private void AddStartProgram()
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(StartProgramPath))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var path = StartProgramPath;
|
||||
if (!string.IsNullOrWhiteSpace(StartProgramParameters))
|
||||
{
|
||||
path += $"; {string.Join("; ", StartProgramParameters.Split(' '))}";
|
||||
}
|
||||
|
||||
PresetSteps.Add(PresetStep.Parse($"StartProgram({path})"));
|
||||
}
|
||||
|
||||
private async Task EmitSteps()
|
||||
{
|
||||
if (Steps != null)
|
||||
@@ -427,4 +448,13 @@
|
||||
|
||||
return field;
|
||||
}
|
||||
|
||||
private void NvPresetChange(ChangeEventArgs eventArgs)
|
||||
{
|
||||
if (eventArgs.Value is IEnumerable<string> items)
|
||||
{
|
||||
NvPresetsToAdd.Clear();
|
||||
NvPresetsToAdd.AddRange(items.Select(i => int.Parse(i)));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,6 +7,14 @@
|
||||
|
||||
@if (Triggers != null)
|
||||
{
|
||||
if (!Triggers.Any())
|
||||
{
|
||||
<div>
|
||||
<label class="form-label">No triggers found. Click to add:</label>
|
||||
<button class="btn btn-primary btn-sm" type="button" @onclick="() => Triggers.Add(new PresetTrigger())">Add</button>
|
||||
</div>
|
||||
}
|
||||
|
||||
foreach (var trigger in Triggers)
|
||||
{
|
||||
<div class="mb-2">
|
||||
|
||||
@@ -9,8 +9,6 @@
|
||||
@rendermode Constants.RenderMode
|
||||
|
||||
@inject RpcUiClientService _rpcClientService
|
||||
@inject AppState AppState
|
||||
@inject NavigationManager _navigationManager;
|
||||
@inject NotificationService _notificationService
|
||||
@inject ProtectedSessionStorage _sessionStorage
|
||||
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
@rendermode Constants.RenderMode
|
||||
|
||||
@inject RpcUiClientService _rpcClientService
|
||||
@inject AppState AppState
|
||||
@inject NavigationManager _navigationManager;
|
||||
@inject IHttpContextAccessor _httpContextAccessor
|
||||
|
||||
|
||||
@@ -3,14 +3,13 @@
|
||||
@using ColorControl.UI.Services
|
||||
@using NvAPIWrapper.Native.Display
|
||||
@using ColorControl.Shared.Common
|
||||
@using ColorControl.UI.Components.Pages.Generic
|
||||
|
||||
@rendermode Constants.RenderMode
|
||||
|
||||
@inject RpcUiClientService _rpcClientService
|
||||
@inject AppState AppState
|
||||
@inject NavigationManager _navigationManager;
|
||||
|
||||
<div class="modal" id="deviceModal" tabindex="-1">
|
||||
<div class="modal" id="lgDeviceModal" tabindex="-1">
|
||||
<div class="modal-dialog modal-dialog-scrollable modal-dialog-centered">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
@@ -73,7 +72,7 @@
|
||||
|
||||
<div class="accordion-item">
|
||||
<h2 class="accordion-header">
|
||||
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#deviceOptions">
|
||||
<button class="accordion-button collapsed py-2" type="button" data-bs-toggle="collapse" data-bs-target="#deviceOptions">
|
||||
Device options
|
||||
</button>
|
||||
</h2>
|
||||
@@ -165,6 +164,10 @@
|
||||
</form>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
@if (Device?.IsCustom == true)
|
||||
{
|
||||
<button type="button" class="btn btn-danger" data-bs-toggle="modal" data-bs-target="#removeDeviceModal">Remove</button>
|
||||
}
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
|
||||
<button type="button" class="btn btn-primary" data-bs-dismiss="modal" @onclick="ApplyClick">Save</button>
|
||||
</div>
|
||||
@@ -172,6 +175,8 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ConfirmModal ModalId="removeDeviceModal" Title="Remove device" Message="Are you sure you want to remove this device?" OnConfirm="DeviceOnRemoveConfirmed" NextModalId="lgDeviceModal" />
|
||||
|
||||
@code {
|
||||
private LgDeviceDto? _device;
|
||||
|
||||
@@ -208,6 +213,21 @@
|
||||
}
|
||||
}
|
||||
|
||||
private async Task DeviceOnRemoveConfirmed()
|
||||
{
|
||||
if (Device == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var result = await _rpcClientService.CallAsync<bool>("LgService", "RemoveDevice", Device);
|
||||
|
||||
if (AfterApply != null)
|
||||
{
|
||||
await AfterApply.Invoke(Device);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task TestConnectionClick(MouseEventArgs e)
|
||||
{
|
||||
IsTestingConnection = true;
|
||||
|
||||
@@ -9,8 +9,6 @@
|
||||
@rendermode Constants.RenderMode
|
||||
|
||||
@inject RpcUiClientService _rpcClientService
|
||||
@inject AppState AppState
|
||||
@inject NavigationManager _navigationManager;
|
||||
|
||||
<div class="modal" id="presetModal" tabindex="-1" data-bs-backdrop="static" data-bs-keyboard="false" @onfocus="ModalOnFocus">
|
||||
<div class="modal-dialog modal-dialog-scrollable modal-dialog-centered">
|
||||
@@ -74,7 +72,7 @@
|
||||
</h2>
|
||||
<div id="steps" class="accordion-collapse collapse" data-bs-parent="#presetAccordion">
|
||||
<div class="accordion-body">
|
||||
<StepsInput Label="Steps" @bind-Steps="Preset.steps" CustomItems="CustomItems" ActionItems="CustomActions" LgPresetsEnabled="false" TPreset="LgPreset"></StepsInput>
|
||||
<StepsInput Label="Steps" @bind-Steps="Preset.Steps" CustomItems="CustomItems" ActionItems="CustomActions" LgPresetsEnabled="false" TPreset="LgPreset"></StepsInput>
|
||||
<div class="mb-2">
|
||||
<label class="form-label" for="description">Description</label>
|
||||
<textarea class="form-control" id="description" type="text" @bind="Preset.Description" />
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
@using ColorControl.Shared.Contracts
|
||||
@using ColorControl.Shared.Contracts.NVIDIA
|
||||
@using ColorControl.Shared.Contracts.LG
|
||||
@using ColorControl.UI.Components.Pages.RemoteControl
|
||||
@using ColorControl.UI.Services;
|
||||
@using ColorControl.UI.Components.Pages.Generic
|
||||
@using ColorControl.UI.Generics
|
||||
@@ -22,6 +23,9 @@
|
||||
<div class="d-flex align-items-center">
|
||||
<h1>LG controller</h1>
|
||||
<div class="d-flex ms-auto">
|
||||
<button type="button" class="btn btn-sm btn-primary me-2" data-bs-toggle="offcanvas" data-bs-target="#rcCanvasRight" aria-controls="rcCanvasRight">
|
||||
Remote control
|
||||
</button>
|
||||
<div class="btn-group">
|
||||
<button type="button" class="btn btn-sm btn-primary dropdown-toggle me-2" data-bs-toggle="dropdown" aria-expanded="false" data-bs-auto-close="outside">
|
||||
Buttons
|
||||
@@ -64,7 +68,18 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<LgDevicePage Device="CurrentDevice" />
|
||||
<div class="offcanvas offcanvas-end" style="width: 280px" data-bs-scroll="true" tabindex="-1" id="rcCanvasRight" aria-labelledby="rcCanvasRightRightLabel">
|
||||
<div class="offcanvas-header">
|
||||
<h5 class="offcanvas-title" id="rcCanvasRightRightLabel">Remote control</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="offcanvas" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="offcanvas-body">
|
||||
<GenericRcPage ServiceName="LgService" Device="CurrentDevice" TDevice="LgDeviceDto" TPreset="LgPreset" TButtonEnum="ButtonType" Presets="RcPresets"></GenericRcPage>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<LgDevicePage Device="CurrentDevice" AfterApply="AfterDeviceApplied" />
|
||||
<LgPresetPage PresetParam="CurrentPreset" Devices="Devices" AfterApply="AfterPresetApplied" />
|
||||
<ConfirmModal Title="Delete preset" Message="Are you sure you want to delete the preset?" OnConfirm="PresetOnDeleteConfirmed" />
|
||||
|
||||
@@ -84,7 +99,7 @@ else
|
||||
}
|
||||
<span role="status">Refresh</span>
|
||||
</button>
|
||||
<button type="button" class="btn btn-sm btn-primary" data-bs-toggle="modal" data-bs-target="#deviceModal" @onclick="() => AddDeviceOnClick()">
|
||||
<button type="button" class="btn btn-sm btn-primary" data-bs-toggle="modal" data-bs-target="#lgDeviceModal" @onclick="() => AddDeviceOnClick()">
|
||||
Add
|
||||
</button>
|
||||
</div>
|
||||
@@ -119,7 +134,7 @@ else
|
||||
{
|
||||
<button class="btn btn-sm btn-secondary ms-2" @onclick="() => SetAsPrimaryOnClick(device)">Set as primary</button>
|
||||
}
|
||||
<button type="button" class="btn btn-sm btn-primary ms-2" data-bs-toggle="modal" data-bs-target="#deviceModal" @onclick="() => DeviceOnClick(device)">
|
||||
<button type="button" class="btn btn-sm btn-primary ms-2" data-bs-toggle="modal" data-bs-target="#lgDeviceModal" @onclick="() => DeviceOnClick(device)">
|
||||
Settings
|
||||
</button>
|
||||
</div>
|
||||
@@ -221,6 +236,7 @@ else
|
||||
private InvokableActionDto<LgPreset>? CurrentInvokableAction;
|
||||
|
||||
private PresetOrder PresetOrder;
|
||||
private List<LgPreset> RcPresets = [LgPresets.InStartPreset, LgPresets.EzAdjustPreset];
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
@@ -297,7 +313,13 @@ else
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
private async Task RefreshDevicesOnClick(MouseEventArgs e)
|
||||
private async Task AfterDeviceApplied(LgDeviceDto device)
|
||||
{
|
||||
Devices = await _rpcClientService.CallAsync<List<LgDeviceDto>>("LgService", "GetDevices");
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
private async Task RefreshDevicesOnClick()
|
||||
{
|
||||
IsRefreshDisabled = true;
|
||||
try
|
||||
|
||||
@@ -6,8 +6,6 @@
|
||||
@rendermode Constants.RenderMode
|
||||
|
||||
@inject RpcUiClientService _rpcClientService
|
||||
@inject AppState AppState
|
||||
@inject NavigationManager _navigationManager;
|
||||
|
||||
<PageTitle>Logging</PageTitle>
|
||||
|
||||
|
||||
@@ -7,8 +7,6 @@
|
||||
@rendermode Constants.RenderMode
|
||||
|
||||
@inject RpcUiClientService _rpcClientService
|
||||
@inject AppState AppState
|
||||
@inject NavigationManager _navigationManager;
|
||||
|
||||
<div class="modal modal-lg" id="ditheringModal" tabindex="-1">
|
||||
<div class="modal-dialog modal-dialog-centered">
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
@using ColorControl.Shared.Contracts
|
||||
@using ColorControl.Shared.Contracts.NVIDIA
|
||||
@using ColorControl.UI.Components.Pages.Generic
|
||||
@using ColorControl.UI.Services;
|
||||
@using NvAPIWrapper.Native.Display
|
||||
@using ColorControl.Shared.Common;
|
||||
@@ -26,14 +27,14 @@
|
||||
<div class="modal-body">
|
||||
<form class="row g-1">
|
||||
|
||||
@if (ProfileNames != null)
|
||||
@if (Profiles != null)
|
||||
{
|
||||
<div class="mb-2">
|
||||
<label class="form-label" for="profileName">Profile</label>
|
||||
<select class="form-select" id="profileName" @bind="CurrentProfileName" @bind:after="ProfileOnChange">
|
||||
@foreach (var profileName in ProfileNames)
|
||||
@foreach (var profile in Profiles)
|
||||
{
|
||||
<option value="@profileName">@profileName</option>
|
||||
<option value="@profile.Name" title="@string.Join(", ", profile.Apps)">@profile.Name</option>
|
||||
}
|
||||
</select>
|
||||
</div>
|
||||
@@ -64,10 +65,8 @@
|
||||
{
|
||||
if (NvSettingConstants.RangeDriverSettings.Contains(setting.SettingId) && setting.Values != null)
|
||||
{
|
||||
<div class="mb-2">
|
||||
<label class="form-label @(setting.Value != setting.DefaultValue && setting.Value != NvSettingConstants.UnsetDwordValue ? "text-warning fw-bold" : "")" for="@setting.SettingId">@setting.SettingText (@setting.Value)</label>
|
||||
<input class="form-range" type="range" min="@setting.Values.Min(v => v.Value)" max="@setting.Values.Max(v => v.Value)" @bind="setting.Value" id="@setting.SettingId">
|
||||
</div>
|
||||
<RangeInput TValue="uint" @bind-Value="setting.Value" Id="@setting.SettingId.ToString()" Label="@setting.SettingText" Values="setting.Values.Select(v => v.Value).ToList()"
|
||||
LabelStylingFunc="value => RangeLabelStyleFunc(value, setting)" />
|
||||
}
|
||||
else if (setting.Values != null)
|
||||
{
|
||||
@@ -137,7 +136,7 @@
|
||||
|
||||
private List<GroupedSettings>? Groups { get; set; }
|
||||
private List<NvSettingItemDto>? DriverSettings { get; set; }
|
||||
private List<string>? ProfileNames { get; set; }
|
||||
private List<NvProfile>? Profiles { get; set; }
|
||||
private string? CurrentProfileName { get; set; }
|
||||
|
||||
private bool IsVisible { get; set; }
|
||||
@@ -244,7 +243,7 @@
|
||||
|
||||
private async Task OnlyFoundProgramsOnChange()
|
||||
{
|
||||
ProfileNames = await _rpcClientService.CallAsync<List<string>>("NvService", "GetDriverProfileNames", OnlyFoundPrograms);
|
||||
Profiles = await _rpcClientService.CallAsync<List<NvProfile>>("NvService", "GetDriverProfileNames", OnlyFoundPrograms);
|
||||
}
|
||||
|
||||
private async Task OnFocus(FocusEventArgs e)
|
||||
@@ -252,8 +251,18 @@
|
||||
if (!IsVisible)
|
||||
{
|
||||
IsVisible = true;
|
||||
ProfileNames ??= await _rpcClientService.CallAsync<List<string>>("NvService", "GetDriverProfileNames", OnlyFoundPrograms);
|
||||
Profiles ??= await _rpcClientService.CallAsync<List<NvProfile>>("NvService", "GetDriverProfileNames", OnlyFoundPrograms);
|
||||
await LoadDriverSettings();
|
||||
}
|
||||
}
|
||||
|
||||
private void RangeOnChange(ChangeEventArgs eventArgs, NvSettingItemDto s)
|
||||
{
|
||||
s.Value = uint.Parse(eventArgs.Value?.ToString() ?? "0");
|
||||
}
|
||||
|
||||
private string RangeLabelStyleFunc(uint value, NvSettingItemDto setting)
|
||||
{
|
||||
return value != setting.DefaultValue && value != NvSettingConstants.UnsetDwordValue ? "text-warning fw-bold" : "";
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
@using ColorControl.Shared.Contracts
|
||||
@using ColorControl.Shared.Contracts.NVIDIA
|
||||
@using ColorControl.UI.Components.Pages.Generic
|
||||
@using ColorControl.UI.Services;
|
||||
@using NvAPIWrapper.Native.Display
|
||||
@using ColorControl.Shared.Common;
|
||||
@@ -53,47 +54,33 @@
|
||||
|
||||
@if (OverclockSettings.Type is NvGpuOcType.Curve or NvGpuOcType.Offset)
|
||||
{
|
||||
<div class="mb-2">
|
||||
<label class="form-label" for="coreOffset">Core offset in MHz: @OverclockSettings.GraphicsOffsetMHz.ToSignedUnitString() (Min: @GpuInfo.MinCoreDeltaInMHz.ToSignedUnitString(), Max: @GpuInfo.MaxCoreDeltaInMHz.ToSignedUnitString())</label>
|
||||
<input class="form-range" type="range" step="@GpuInfo.MinCoreStepInMHz" min="@GpuInfo.CorrectedMinCoreDeltaInMHz" max="@GpuInfo.MaxCoreDeltaInMHz" @bind="OverclockSettings.GraphicsOffsetMHz" id="coreOffset">
|
||||
</div>
|
||||
<div class="mb-2">
|
||||
<label class="form-label" for="memoryOffset">Memory offset in MHz: @OverclockSettings.MemoryOffsetMHz.ToSignedUnitString() (Min: @GpuInfo.MinMemoryDeltaInMHz.ToSignedUnitString(), Max: @GpuInfo.MaxMemoryDeltaInMHz.ToSignedUnitString())</label>
|
||||
<input class="form-range" type="range" step="1" min="@GpuInfo.MinMemoryDeltaInMHz" max="@GpuInfo.MaxMemoryDeltaInMHz" @bind="OverclockSettings.MemoryOffsetMHz" id="memoryOffset">
|
||||
</div>
|
||||
<RangeInput TValue="int" Min="@GpuInfo.CorrectedMinCoreDeltaInMHz" Max="@GpuInfo.MaxCoreDeltaInMHz" Step="@GpuInfo.MinCoreStepInMHz" @bind-Value="OverclockSettings.GraphicsOffsetMHz" Id="coreOffset" Label="Core offset" UnitString="MHz"
|
||||
SubLabel="@($"Min: {GpuInfo.MinCoreDeltaInMHz.ToSignedUnitString()}, Max: {GpuInfo.MaxCoreDeltaInMHz.ToSignedUnitString()}")" />
|
||||
<RangeInput TValue="int" Min="@GpuInfo.MinMemoryDeltaInMHz" Max="@GpuInfo.MaxMemoryDeltaInMHz" @bind-Value="OverclockSettings.MemoryOffsetMHz" Id="memoryOffset" Label="Memory offset" UnitString="MHz"
|
||||
SubLabel="@($"Min: {GpuInfo.MinMemoryDeltaInMHz.ToSignedUnitString()}, Max: {GpuInfo.MaxMemoryDeltaInMHz.ToSignedUnitString()}")" />
|
||||
}
|
||||
@if (OverclockSettings.Type is NvGpuOcType.Curve or NvGpuOcType.BoostLock)
|
||||
{
|
||||
<div class="mb-2">
|
||||
<label class="form-label" for="maxCoreFrequency">Maximum core frequency MHz: @OverclockSettings.MaximumFrequencyMHz.ToUnitString() (Min: @GpuInfo.MinCurveFrequencyInMHz.ToUnitString(), Max: @GpuInfo.MaxCurveFrequencyInMHz.ToUnitString())</label>
|
||||
<input class="form-range" type="range" step="@GpuInfo.MinCoreStepInMHz" min="@GpuInfo.MinCurveFrequencyInMHz" max="@GpuInfo.MaxCurveFrequencyInMHz" @bind="OverclockSettings.MaximumFrequencyMHz" id="maxCoreFrequency">
|
||||
<div id="maxCoreFrequencyHelp" class="form-text">Setting this value to anything else than '0' will limit the maximum frequency of the curve.</div>
|
||||
</div>
|
||||
<div class="mb-2">
|
||||
<label class="form-label" for="maxCoreVoltage">Maximum core voltage in millivolt: @OverclockSettings.MaximumVoltageMv.ToUnitString("mv") (Min: @GpuInfo.MinCurveVoltageInMv.ToUnitString("mv"), Max: @GpuInfo.MaxCurveVoltageInMv.ToUnitString("mv"))</label>
|
||||
<input class="form-range" type="range" step="5" min="@GpuInfo.MinCurveVoltageInMv" max="@GpuInfo.MaxCurveVoltageInMv" @bind="OverclockSettings.MaximumVoltageMv" id="maxCoreVoltage">
|
||||
<div id="maxCoreFrequencyHelp" class="form-text">Setting this value to anything else than '0' will limit the maximum voltage (and also the frequency) of the curve.</div>
|
||||
</div>
|
||||
<RangeInput TValue="uint" Min="@GpuInfo.MinCurveFrequencyInMHz" Max="@GpuInfo.MaxCurveFrequencyInMHz" Step="@((uint)GpuInfo.MinCoreStepInMHz)" @bind-Value="OverclockSettings.MaximumFrequencyMHz" Id="maxCoreFrequency" Label="Maximum core frequency" UnitString="MHz"
|
||||
SubLabel="@($"Setting this value to anything else than '0' will limit the maximum frequency of the curve. Min: {GpuInfo.MinCurveFrequencyInMHz.ToSignedUnitString()}, Max: {GpuInfo.MaxCurveFrequencyInMHz.ToSignedUnitString()}")" />
|
||||
<RangeInput TValue="uint" Min="@GpuInfo.MinCurveVoltageInMv" Max="@GpuInfo.MaxCurveVoltageInMv" Step="5" @bind-Value="OverclockSettings.MaximumVoltageMv" Id="maxCoreVoltage" Label="Maximum core voltage" UnitString="mv"
|
||||
SubLabel="@($"Setting this value to anything else than '0' will limit the maximum voltage (and also the frequency) of the curve. Min: {GpuInfo.MinCurveVoltageInMv.ToUnitString("mv")}, Max: {GpuInfo.MaxCurveVoltageInMv.ToUnitString("mv")}")" />
|
||||
}
|
||||
@if (OverclockSettings.Type != NvGpuOcType.None)
|
||||
{
|
||||
<div class="mb-2">
|
||||
<label class="form-label" for="voltageBoost">Voltage boost in %: @OverclockSettings.VoltageBoostPercent.ToUnitString("%")</label>
|
||||
<input class="form-range" type="range" step="1" min="0" max="100" @bind="OverclockSettings.VoltageBoostPercent" id="voltageBoost">
|
||||
</div>
|
||||
<RangeInput TValue="uint" @bind-Value="OverclockSettings.VoltageBoostPercent" Id="voltageBoost" Label="Voltage boost" UnitString="%" />
|
||||
|
||||
@if (GpuInfo.DefaultPowerInMilliWatts > 0 && GpuInfo.MaxPowerInMilliWatts > 0 && GpuInfo.MinPowerInMilliWatts > 0 && GpuInfo.MaxPowerInMilliWatts > GpuInfo.MinPowerInMilliWatts)
|
||||
{
|
||||
<div class="mb-2">
|
||||
<label class="form-label" for="powerLimitWatts">Power limit in watts: @(((uint)(OverclockSettings.PowerPCM / 100000f * GpuInfo.DefaultPowerInMilliWatts)).ToKiloUnitString(units: "W")) (@OverclockSettings.PowerPCM.ToKiloUnitString(units: "%")) (Min: @GpuInfo.MinPowerInMilliWatts.ToKiloUnitString(units: "W"), Max: @GpuInfo.MaxPowerInMilliWatts.ToKiloUnitString(units: "W"))</label>
|
||||
<input class="form-range" type="range" step="1" min="@GpuInfo.MinimumPowerInPCM" max="@GpuInfo.MaximumPowerInPCM" @bind="OverclockSettings.PowerPCM" id="powerLimitWatts">
|
||||
</div>
|
||||
<RangeInput TValue="uint" Min="@GpuInfo.MinimumPowerInPCM" Max="@GpuInfo.MaximumPowerInPCM" @bind-Value="OverclockSettings.PowerPCM" Id="powerLimitWatts" Label="Power limit"
|
||||
CustomLabelFunc='value => $"{((uint)(OverclockSettings.PowerPCM / 100000f * GpuInfo.DefaultPowerInMilliWatts)).ToKiloUnitString(units: "W")} ({OverclockSettings.PowerPCM.ToKiloUnitString(units: "%")})"'
|
||||
SubLabel="@($"Min: {GpuInfo.MinPowerInMilliWatts.ToKiloUnitString(units: "W")}, Max: {GpuInfo.MaxPowerInMilliWatts.ToKiloUnitString(units: "W")}")" />
|
||||
}
|
||||
else
|
||||
{
|
||||
<div class="mb-2">
|
||||
<label class="form-label" for="powerLimitPercentage">Power limit in percentage: @OverclockSettings.PowerPCM.ToKiloUnitString(units: "%") (Min: @GpuInfo.MinimumPowerInPCM.ToKiloUnitString(units: "%"), Max: @GpuInfo.MaximumPowerInPCM.ToKiloUnitString(units: "%")</label>
|
||||
<input class="form-range" type="range" step="1" min="@GpuInfo.MinimumPowerInPCM" max="@GpuInfo.MaximumPowerInPCM" @bind="OverclockSettings.PowerPCM" id="powerLimitPercentage">
|
||||
</div>
|
||||
<RangeInput TValue="uint" Min="@GpuInfo.MinimumPowerInPCM" Max="@GpuInfo.MaximumPowerInPCM" @bind-Value="OverclockSettings.PowerPCM" Id="powerLimitPercentage" Label="Power limit"
|
||||
CustomLabelFunc='value => $"{OverclockSettings.PowerPCM.ToKiloUnitString(units: "%")}"'
|
||||
SubLabel="@($"Min: {GpuInfo.MinimumPowerInPCM.ToKiloUnitString(units: "%")}, Max: {GpuInfo.MaximumPowerInPCM.ToKiloUnitString(units: "%")}")" />
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -138,6 +125,11 @@
|
||||
|
||||
protected override async Task OnParametersSetAsync()
|
||||
{
|
||||
if (!IsVisible)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (_preset != Preset)
|
||||
{
|
||||
_preset = Preset;
|
||||
|
||||
@@ -0,0 +1,109 @@
|
||||
@using ColorControl.Shared.Contracts
|
||||
@using ColorControl.Shared.Contracts.NVIDIA
|
||||
@using ColorControl.UI.Services;
|
||||
@using NvAPIWrapper.Native.Display
|
||||
@using ColorControl.Shared.Common;
|
||||
@using static ColorControl.Shared.Common.NestedItemsBuilder
|
||||
|
||||
@rendermode Constants.RenderMode
|
||||
|
||||
@inject RpcUiClientService _rpcClientService
|
||||
@inject JSHelper jsHelper
|
||||
|
||||
<div class="modal modal-lg" id="infoModal" tabindex="-1">
|
||||
<div class="modal-dialog modal-dialog-centered">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">Info</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form class="row g-1">
|
||||
<div class="row">
|
||||
@if (InfoItems != null)
|
||||
{
|
||||
@RenderItems(InfoItems)
|
||||
}
|
||||
</div>
|
||||
|
||||
</form>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@code {
|
||||
private bool _visible;
|
||||
|
||||
[Parameter]
|
||||
public bool Visible { get; set; }
|
||||
|
||||
private string? SelectedDisplayId { get; set; }
|
||||
private List<NestedItem>? InfoItems;
|
||||
|
||||
protected override async Task OnParametersSetAsync()
|
||||
{
|
||||
if (_visible != Visible)
|
||||
{
|
||||
_visible = Visible;
|
||||
|
||||
if (Visible)
|
||||
{
|
||||
await GetSettingsAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected async Task GetSettingsAsync()
|
||||
{
|
||||
InfoItems = await _rpcClientService.CallAsync<List<NestedItem>>("NvService", "GetInfoItems");
|
||||
if (InfoItems != null)
|
||||
{
|
||||
InfoItems.ForEach(i => i.Expanded = true);
|
||||
}
|
||||
}
|
||||
|
||||
RenderFragment<List<NestedItem>> RenderItems => items =>
|
||||
@<div class="accordion accordion-flush" id="@items.GetHashCode()">
|
||||
@foreach(var item in items)
|
||||
{
|
||||
@if (item.NestedItems.Count > 0)
|
||||
{
|
||||
<div class="accordion-item">
|
||||
<h2 class="accordion-header">
|
||||
<button class="accordion-button py-2 @(item.Expanded ? "show" : "collapsed")" type="button" data-bs-toggle="collapse" data-bs-target="#@item.GetHashCode()" aria-expanded="false" aria-controls="flush-collapseOne" @onclick="() => OnItemClick(item)">
|
||||
@item.Value
|
||||
</button>
|
||||
</h2>
|
||||
<div id="@item.GetHashCode()" class="accordion-collapse collapse @(item.Expanded ? "show" : "")" data-bs-parent="#@items.GetHashCode()">
|
||||
<div class="accordion-body">
|
||||
@if (item.Expanded)
|
||||
{
|
||||
@RenderItems(item.NestedItems)
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
<div class="mt-1" style="margin-left: 1.25rem">
|
||||
@item.Value
|
||||
</div>
|
||||
}
|
||||
}
|
||||
</div>
|
||||
;
|
||||
|
||||
private async Task OnItemClick(NestedItem item)
|
||||
{
|
||||
await Task.Delay(400);
|
||||
|
||||
var classes = await jsHelper.GetElementClassName(item.GetHashCode().ToString());
|
||||
|
||||
item.Expanded = classes?.Contains(" show") == true;
|
||||
}
|
||||
}
|
||||
@@ -1,17 +1,18 @@
|
||||
@using ColorControl.Shared.Contracts
|
||||
@using ColorControl.Shared.Contracts.DisplayInfo
|
||||
@using ColorControl.Shared.Contracts.NVIDIA
|
||||
@using ColorControl.UI.Services;
|
||||
@using ColorControl.UI.Services
|
||||
@using ColorControl.UI.Components.Pages.Generic
|
||||
@using Microsoft.AspNetCore.Components.Server.ProtectedBrowserStorage
|
||||
@using NvAPIWrapper.Native.Display
|
||||
@using ColorControl.Shared.Common;
|
||||
@using ColorControl.Shared.Common
|
||||
@using ColorControl.UI.Generics
|
||||
|
||||
@rendermode Constants.RenderMode
|
||||
|
||||
@inject RpcUiClientService _rpcClientService
|
||||
@inject AppState AppState
|
||||
@inject NavigationManager _navigationManager;
|
||||
@inject JSHelper jsHelper;
|
||||
@inject JSHelper jsHelper
|
||||
@inject ProtectedLocalStorage _localStorage
|
||||
|
||||
<div class="modal" id="presetModal" tabindex="-1" data-bs-backdrop="static" data-bs-keyboard="false" @onfocus="ModalOnFocus">
|
||||
<div class="modal-dialog modal-dialog-scrollable modal-dialog-centered">
|
||||
@@ -109,7 +110,7 @@
|
||||
{
|
||||
<div class="mb-2">
|
||||
<label class="form-label" for="activeResolution">Active resolution</label>
|
||||
<select class="form-select" id="activeResolution" @bind="selectedActiveResolution">
|
||||
<select class="form-select" id="activeResolution" @bind="selectedActiveResolution" @bind:after="() => AfterResolutionChange()">
|
||||
<option value="0x0">Not set</option>
|
||||
@foreach (var resolution in resolutions)
|
||||
{
|
||||
@@ -119,7 +120,7 @@
|
||||
</div>
|
||||
<div class="mb-2">
|
||||
<label class="form-label" for="virtualResolution">Virtual resolution</label>
|
||||
<select class="form-select" id="virtualResolution" @bind="selectedVirtualResolution">
|
||||
<select class="form-select" id="virtualResolution" @bind="selectedVirtualResolution" @bind:after="() => AfterResolutionChange()">
|
||||
<option value="0x0">Not set</option>
|
||||
@foreach (var resolution in resolutions)
|
||||
{
|
||||
@@ -170,7 +171,7 @@
|
||||
{
|
||||
<div class="mb-2">
|
||||
<label class="form-label" for="refreshRate">Refresh Rate</label>
|
||||
<select class="form-select" id="refreshRate" @bind="selectedRefreshRate">
|
||||
<select class="form-select" id="refreshRate" @bind="selectedRefreshRate" @bind:after="() => AfterRefreshRateChange()">
|
||||
@foreach (var refreshRate in refreshRates)
|
||||
{
|
||||
<option value="@refreshRate.ToString()">@refreshRate.ToString() Hz</option>
|
||||
@@ -182,6 +183,34 @@
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
<div class="accordion-item">
|
||||
<h2 class="accordion-header">
|
||||
<span class="d-flex align-items-baseline">
|
||||
@if (!Preset.IsDisplayPreset)
|
||||
{
|
||||
<input class="form-check-input me-1" style="font-size: 1rem" title="Apply these settings" type="checkbox" @bind="Preset.DpiScaling.ApplyScaling" id="applyDpiScaling">
|
||||
}
|
||||
<button class="accordion-button collapsed py-2 @(Preset.DpiScaling.ApplyScaling ? "text-warning fw-bold" : "")" type="button" data-bs-toggle="collapse" data-bs-target="#dpiScalingSettings">
|
||||
Dpi Scaling
|
||||
</button>
|
||||
</span>
|
||||
</h2>
|
||||
<div id="dpiScalingSettings" class="accordion-collapse collapse" data-bs-parent="#presetAccordion">
|
||||
<div class="accordion-body">
|
||||
<div class="mb-2">
|
||||
<label class="form-label" for="dpiScaling">Percentage</label>
|
||||
<select class="form-select" id="dpiScaling" @bind="Preset.DpiScaling.Percentage">
|
||||
@foreach (var dpiScale in Shared.Native.CCD.DpiValues)
|
||||
{
|
||||
<option value="@dpiScale.ToString()">@dpiScale.ToString()</option>
|
||||
}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="accordion-item">
|
||||
<h2 class="accordion-header">
|
||||
<span class="d-flex align-items-baseline">
|
||||
@@ -256,14 +285,8 @@
|
||||
</h2>
|
||||
<div id="colorEnhancements" class="accordion-collapse collapse" data-bs-parent="#presetAccordion">
|
||||
<div class="accordion-body">
|
||||
<div class="mb-2">
|
||||
<label class="form-label" for="digitalVibrance">Digital vibrance: @Preset.ColorEnhancementSettings.DigitalVibranceLevel</label>
|
||||
<input class="form-range" type="range" min="0" max="100" @bind="Preset.ColorEnhancementSettings.DigitalVibranceLevel" id="digitalVibrance">
|
||||
</div>
|
||||
<div class="mb-2">
|
||||
<label class="form-label" for="hue">Hue: @Preset.ColorEnhancementSettings.HueAngle°</label>
|
||||
<input class="form-range" type="range" min="0" max="359" @bind="Preset.ColorEnhancementSettings.HueAngle" id="hue">
|
||||
</div>
|
||||
<RangeInput TValue="int" @bind-Value="Preset.ColorEnhancementSettings.DigitalVibranceLevel" Id="digitalVibrance" Label="Digital vibrance" />
|
||||
<RangeInput TValue="int" @bind-Value="Preset.ColorEnhancementSettings.HueAngle" Id="hue" Label="Hue" Unit="°" Max="359" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -405,10 +428,8 @@
|
||||
}
|
||||
</select>
|
||||
</div>
|
||||
<div class="mb-2">
|
||||
<label class="form-range" for="sdrBrightness">SDR brightness (@Preset.SDRBrightness%)</label>
|
||||
<input class="form-range" type="range" min="0" max="100" @bind="Preset.SDRBrightness" id="sdrBrightness">
|
||||
</div>
|
||||
|
||||
<NullableRangeInput TValue="int" @bind-Value="Preset.SDRBrightness" Id="sdrBrightness" Label="SDR brightness" Unit="%" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -539,11 +560,52 @@
|
||||
</div>
|
||||
}
|
||||
|
||||
<div class="accordion accordion-flush" id="presetAccordion">
|
||||
|
||||
<div class="accordion-item">
|
||||
<h2 class="accordion-header">
|
||||
<button class="accordion-button collapsed py-2" type="button" data-bs-toggle="collapse" data-bs-target="#advancedSettings">
|
||||
Triggers
|
||||
</button>
|
||||
</h2>
|
||||
<div id="advancedSettings" class="accordion-collapse collapse" data-bs-parent="#presetAccordion">
|
||||
<div class="accordion-body">
|
||||
<TriggerInput Triggers="Preset.Triggers"></TriggerInput>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="accordion-item">
|
||||
<h2 class="accordion-header">
|
||||
<button class="accordion-button collapsed py-2" type="button" data-bs-toggle="collapse" data-bs-target="#steps">
|
||||
Steps
|
||||
</button>
|
||||
</h2>
|
||||
<div id="steps" class="accordion-collapse collapse" data-bs-parent="#presetAccordion">
|
||||
<div class="accordion-body">
|
||||
<StepsInput Label="Steps" @bind-Steps="Preset.Steps" NvPresetsEnabled="false" TPreset="NvPreset"></StepsInput>
|
||||
<div class="mb-2">
|
||||
<label class="form-label" for="description">Description</label>
|
||||
<textarea class="form-control" id="description" type="text" @bind="Preset.Description" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
}
|
||||
</form>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
@if (Preset?.IsDisplayPreset == true)
|
||||
{
|
||||
<input class="form-check-input" type="checkbox" @bind="ApplyImmediately" id="directApply" @bind:after="ApplyImmediatelyAfter">
|
||||
<label class="form-check-label" for="directApply">
|
||||
Apply changes immediately
|
||||
</label>
|
||||
}
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal" @onclick="() => IsVisible = false">Close</button>
|
||||
<button type="submit" class="btn btn-primary" disabled="@(!IsValid())" @onclick="ApplyClick">@(Preset?.IsDisplayPreset == true ? "Apply" : "Save")</button>
|
||||
</div>
|
||||
@@ -581,14 +643,33 @@
|
||||
private bool IsGpuSettingsOpened;
|
||||
private bool ShowOverclocking;
|
||||
|
||||
private bool ApplyImmediately;
|
||||
private NvPreset? OriginalPreset;
|
||||
private bool Applying;
|
||||
private bool Queued;
|
||||
|
||||
protected override async Task OnParametersSetAsync()
|
||||
{
|
||||
if (!IsVisible)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if ((IsDriverSettingsOpened || IsGpuSettingsOpened) && Preset != null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ApplyImmediately = false;
|
||||
|
||||
Preset = PresetParam == null ? null : PresetParam.CloneWithId();
|
||||
|
||||
displayPresets = await _rpcClientService.CallAsync<List<NvPreset>>("NvService", "GetDisplayPresets");
|
||||
if (Preset?.IsDisplayPreset == true)
|
||||
{
|
||||
Preset = displayPresets.FirstOrDefault(p => p.DisplayId == Preset.DisplayId) ?? Preset;
|
||||
}
|
||||
|
||||
if (Preset != null && PresetParam != null)
|
||||
{
|
||||
resolutions = await _rpcClientService.CallAsync<List<VirtualResolution>>("NvService", "GetAvailableResolutionsV2", Preset);
|
||||
@@ -598,27 +679,51 @@
|
||||
selectedVirtualResolution = Preset?.DisplayConfig.Resolution?.GetVirtualString();
|
||||
selectedRefreshRate = Preset?.DisplayConfig.RefreshRate?.ToString();
|
||||
|
||||
if (Preset?.IsDisplayPreset == false)
|
||||
{
|
||||
displayPresets = await _rpcClientService.CallAsync<List<NvPreset>>("NvService", "GetDisplayPresets");
|
||||
}
|
||||
|
||||
var deviceName = Preset?.IsDisplayPreset == true ? Preset.DisplayId : Preset?.DisplayId ?? displayPresets?.FirstOrDefault()?.DisplayId;
|
||||
|
||||
if (Preset != null)
|
||||
{
|
||||
ColorProfiles = await _rpcClientService.CallAsync<List<ColorProfileDto>>("ColorProfileService", "GetAllDisplayColorProfiles", deviceName);
|
||||
OriginalPreset = Preset.Clone();
|
||||
}
|
||||
|
||||
ApplyImmediately = await _localStorage.TryGet<bool>($"{nameof(NvPreset)}_{nameof(ApplyImmediately)}");
|
||||
ShowOverclocking = true;
|
||||
}
|
||||
|
||||
protected override void OnAfterRender(bool firstRender)
|
||||
protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||
{
|
||||
// if (IsVisible && Preset?.IsDisplayPreset == false)
|
||||
// {
|
||||
// Preset.UpdateAutoApplySettings(keepChanges: true);
|
||||
// }
|
||||
await DoApplyImmediately();
|
||||
}
|
||||
|
||||
private async Task DoApplyImmediately()
|
||||
{
|
||||
if (IsVisible && OriginalPreset != null && Preset?.IsDisplayPreset == true)
|
||||
{
|
||||
if (Applying)
|
||||
{
|
||||
Queued = true;
|
||||
return;
|
||||
}
|
||||
|
||||
do
|
||||
{
|
||||
Applying = true;
|
||||
Queued = false;
|
||||
|
||||
Preset.UpdateAutoApplySettings(OriginalPreset, true);
|
||||
|
||||
if (ApplyImmediately && Preset.HasApplicableSettings)
|
||||
{
|
||||
await ApplyDisplayPreset();
|
||||
}
|
||||
|
||||
OriginalPreset = Preset.Clone();
|
||||
|
||||
Applying = false;
|
||||
}
|
||||
while (Queued);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task ApplyClick(MouseEventArgs e)
|
||||
@@ -634,29 +739,6 @@
|
||||
return;
|
||||
}
|
||||
|
||||
var newResolution = resolutions?.FirstOrDefault(r => r.GetActiveString() == selectedActiveResolution);
|
||||
if (newResolution != null)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(selectedVirtualResolution) && selectedActiveResolution != selectedVirtualResolution)
|
||||
{
|
||||
newResolution = new VirtualResolution(newResolution.ActiveWidth, newResolution.ActiveHeight);
|
||||
var parts = selectedVirtualResolution.Split('x');
|
||||
newResolution.VirtualWidth = uint.Parse(parts[0]);
|
||||
newResolution.VirtualHeight = uint.Parse(parts[1]);
|
||||
}
|
||||
Preset.DisplayConfig.Resolution = newResolution;
|
||||
}
|
||||
else
|
||||
{
|
||||
Preset.DisplayConfig.Resolution = new VirtualResolution(0, 0);
|
||||
}
|
||||
|
||||
var newRefreshRate = refreshRates?.FirstOrDefault(r => r.ToString() == selectedRefreshRate);
|
||||
if (newRefreshRate != null)
|
||||
{
|
||||
Preset.DisplayConfig.RefreshRate = newRefreshRate;
|
||||
}
|
||||
|
||||
Preset.ApplyNovideoSettings = Preset.NovideoSettings.ApplyClamp;
|
||||
|
||||
if (Preset.IsDisplayPreset)
|
||||
@@ -734,4 +816,52 @@
|
||||
{
|
||||
return Preset?.IsDisplayPreset == true || !string.IsNullOrEmpty(Preset?.name);
|
||||
}
|
||||
|
||||
private async Task AfterResolutionChange()
|
||||
{
|
||||
if (Preset == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var newResolution = resolutions?.FirstOrDefault(r => r.GetActiveString() == selectedActiveResolution);
|
||||
if (newResolution != null)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(selectedVirtualResolution) && selectedActiveResolution != selectedVirtualResolution)
|
||||
{
|
||||
newResolution = new VirtualResolution(newResolution.ActiveWidth, newResolution.ActiveHeight);
|
||||
var parts = selectedVirtualResolution.Split('x');
|
||||
newResolution.VirtualWidth = uint.Parse(parts[0]);
|
||||
newResolution.VirtualHeight = uint.Parse(parts[1]);
|
||||
}
|
||||
Preset.DisplayConfig.Resolution = newResolution;
|
||||
}
|
||||
else
|
||||
{
|
||||
Preset.DisplayConfig.Resolution = new VirtualResolution(0, 0);
|
||||
}
|
||||
|
||||
await DoApplyImmediately();
|
||||
}
|
||||
|
||||
private async Task AfterRefreshRateChange()
|
||||
{
|
||||
if (Preset == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var newRefreshRate = refreshRates?.FirstOrDefault(r => r.ToString() == selectedRefreshRate);
|
||||
if (newRefreshRate != null)
|
||||
{
|
||||
Preset.DisplayConfig.RefreshRate = newRefreshRate;
|
||||
}
|
||||
|
||||
await DoApplyImmediately();
|
||||
}
|
||||
|
||||
private async Task ApplyImmediatelyAfter()
|
||||
{
|
||||
await _localStorage.SetAsync($"{nameof(NvPreset)}_{nameof(ApplyImmediately)}", ApplyImmediately);
|
||||
}
|
||||
}
|
||||
@@ -19,6 +19,7 @@
|
||||
<NvidiaDithering Visible="DitheringVisible" />
|
||||
<NvidiaDriverSettings Preset="CurrentDriverSettingsPreset" />
|
||||
<NvidiaGpuSettings Preset="CurrentGpuSettingsPreset" GpuInfo="CurrentGpuInfo" />
|
||||
<NvidiaInfo Visible="InfoVisible" />
|
||||
|
||||
<div class="d-flex align-items-center">
|
||||
<h1>NVIDIA controller</h1>
|
||||
@@ -30,6 +31,7 @@
|
||||
<ul class="dropdown-menu">
|
||||
<li><a class="dropdown-item" data-bs-toggle="modal" data-bs-target="#ditheringModal" @onclick="() => DitheringVisible = true">Test dithering</a></li>
|
||||
<li><a class="dropdown-item" data-bs-toggle="modal" data-bs-target="#driverSettingsModal" @onclick="() => CurrentDriverSettingsPreset = null">Driver settings</a></li>
|
||||
<li><a class="dropdown-item" data-bs-toggle="modal" data-bs-target="#infoModal" @onclick="() => InfoVisible = true">Info</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<button type="button" class="btn btn-sm btn-primary" data-bs-toggle="modal" data-bs-target="#settingsModal">
|
||||
@@ -75,6 +77,7 @@ else
|
||||
<div class="card-body">
|
||||
<small class="text-body">@preset.DisplayConfig.Resolution.ToString() @@ @preset.DisplayConfig.RefreshRate.ToString()Hz</small>
|
||||
<small class="text-muted">@preset.colorData</small>
|
||||
<small class="text-muted">Scale: @preset.DpiScaling.ToString()</small>
|
||||
@if (preset.DitherState != NvDitherState.Auto)
|
||||
{
|
||||
<br />
|
||||
@@ -93,22 +96,7 @@ else
|
||||
@if (preset.driverSettings?.Any() == true)
|
||||
{
|
||||
<br />
|
||||
<div class="dropdown">
|
||||
<span role="button" class="text-decoration-underline dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false" data-bs-auto-close="outside" @onclick="() => DriverSettingsOnClick(preset)">
|
||||
<small>@preset.driverSettings.Count() driver settings changed</small>
|
||||
</span>
|
||||
<form class="dropdown-menu">
|
||||
@if (DriverSettings != null)
|
||||
{
|
||||
<ul class="list-group p-0">
|
||||
@foreach (var setting in DriverSettings)
|
||||
{
|
||||
<li class="list-group-item py-0 px-1"><small>@setting.FriendlyName</small></li>
|
||||
}
|
||||
</ul>
|
||||
}
|
||||
</form>
|
||||
</div>
|
||||
@RenderPresetSettings(preset)
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
@@ -239,7 +227,7 @@ else
|
||||
}
|
||||
@if (preset.applyDriverSettings)
|
||||
{
|
||||
<li class="list-group-item p-1">@preset.driverSettings.Count changed driver settings</li>
|
||||
<li class="list-group-item p-1">@RenderPresetSettings(preset)</li>
|
||||
}
|
||||
@if (preset.ApplyNovideoSettings)
|
||||
{
|
||||
@@ -278,6 +266,7 @@ else
|
||||
private NvPreset? CurrentGpuSettingsPreset;
|
||||
private NvGpuInfoDto? CurrentGpuInfo;
|
||||
private bool DitheringVisible;
|
||||
private bool InfoVisible;
|
||||
private List<NvSettingItemDto>? DriverSettings;
|
||||
|
||||
private PresetOrder PresetOrder;
|
||||
@@ -301,9 +290,42 @@ else
|
||||
}
|
||||
}
|
||||
|
||||
RenderFragment<NvPreset> RenderPresetSettings => preset =>
|
||||
@<div class="dropdown">
|
||||
<span role="button" class="text-decoration-underline dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false" data-bs-auto-close="outside" @onclick="() => DriverSettingsOnClick(preset)" @onclick:preventDefault @onclick:stopPropagation>
|
||||
<small>@preset.driverSettings.Count() driver settings changed</small>
|
||||
</span>
|
||||
<form class="dropdown-menu" style="width: max-content">
|
||||
@if (DriverSettings != null)
|
||||
{
|
||||
<ul class="list-group p-0">
|
||||
@foreach (var settingId in preset.driverSettings.Keys)
|
||||
{
|
||||
var setting = DriverSettings.FirstOrDefault(s => s.SettingId == settingId);
|
||||
if (setting == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
var settingValue = setting.Values.FirstOrDefault(v => v.Value == preset.driverSettings[settingId]);
|
||||
if (settingValue == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
<li class="list-group-item py-0 px-1"><small>@setting.Name: @settingValue.ValueName</small></li>
|
||||
}
|
||||
</ul>
|
||||
}
|
||||
</form>
|
||||
</div>
|
||||
;
|
||||
|
||||
private async Task PresetOnClick(NvPreset preset)
|
||||
{
|
||||
await _rpcClientService.CallAsync<bool>("NvService", "ApplyPresetWithId", preset.id);
|
||||
var result = await _rpcClientService.CallAsync<bool>("NvService", "ApplyPresetWithId", preset.id);
|
||||
if (result)
|
||||
{
|
||||
DisplayPresets = await _rpcClientService.CallAsync<List<NvPreset>>("NvService", "GetDisplayPresets");
|
||||
}
|
||||
}
|
||||
|
||||
private void PresetOnEditClick(NvPreset preset)
|
||||
@@ -395,11 +417,6 @@ else
|
||||
|
||||
private async Task DriverSettingsOnClick(NvPreset preset)
|
||||
{
|
||||
var driverSettings = await _rpcClientService.CallAsync<List<NvSettingItemDto>>("NvService", "GetDriverSettings", Type.Missing, true);
|
||||
|
||||
if (driverSettings != null)
|
||||
{
|
||||
DriverSettings = driverSettings.Where(s => s.Value != s.DefaultValue && s.State == 2).ToList();
|
||||
}
|
||||
DriverSettings ??= await _rpcClientService.CallAsync<List<NvSettingItemDto>>("NvService", "GetDriverSettings", Type.Missing, true);
|
||||
}
|
||||
}
|
||||
@@ -134,13 +134,9 @@ else
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">Advanced</h5>
|
||||
<form>
|
||||
<div class="mb-3 form-check">
|
||||
<label class="form-range" for="processMonitorPollingInterval">
|
||||
Polling interval of process monitor (@config.ProcessMonitorPollingInterval milliseconds)
|
||||
</label>
|
||||
<input class="form-range" type="range" min="50" max="10000" step="50" @bind="config.ProcessMonitorPollingInterval" id="processMonitorPollingInterval" aria-describedby="processMonitorPollingIntervalHelp" @bind:after="PollingIntervalOnInput">
|
||||
<div id="processMonitorPollingIntervalHelp" class="form-text">Decreasing this delay may execute triggered presets sooner but can cause a higher CPU load</div>
|
||||
</div>
|
||||
<RangeInput TValue="int" Min="50" Max="10000" Step="50" @bind-Value="config.ProcessMonitorPollingInterval" Id="processMonitorPollingInterval" Label="Polling interval of process monitor" Unit=" milliseconds"
|
||||
SubLabel="Decreasing this delay may execute triggered presets sooner but can cause a higher CPU load"
|
||||
/>
|
||||
<div class="mb-3 form-check">
|
||||
<input class="form-check-input" type="checkbox" @bind="config.UseRawInput" id="useRawInput" aria-describedby="useRawInputHelp" @oninput="UseRawInputInput">
|
||||
<label class="form-check-label" for="useRawInput">
|
||||
|
||||
@@ -0,0 +1,120 @@
|
||||
@using ColorControl.Shared.Contracts
|
||||
@using ColorControl.Shared.Contracts.AMD
|
||||
@using ColorControl.Shared.Contracts.RemoteControl
|
||||
@using ColorControl.UI.Services
|
||||
@using ColorControl.Shared.Common
|
||||
|
||||
@* @rendermode Constants.RenderMode *@
|
||||
|
||||
@typeparam TPreset where TPreset : PresetBase
|
||||
@typeparam TButtonEnum where TButtonEnum : struct, Enum
|
||||
|
||||
@inject RpcUiClientService _rpcClientService
|
||||
|
||||
<div class="modal" id="rcButtonModal" data-bs-backdrop="false" tabindex="-1">
|
||||
<div class="modal-dialog modal-dialog-scrollable modal-dialog-centered">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">RC button</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close" @onclick="() => Button = null"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form class="row g-1">
|
||||
@if (Button == null)
|
||||
{
|
||||
<div>Loading...</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
<div class="mb-2">
|
||||
<label class="form-label" for="name">Name</label>
|
||||
<input class="form-control" id="name" type="text" @bind="Button.Name" required />
|
||||
</div>
|
||||
<div class="mb-2">
|
||||
<label class="form-label" for="button">Button</label>
|
||||
<select class="form-select" id="button" @bind="Button.Button">
|
||||
@foreach (var buttonType in Enum.GetValues<TButtonEnum>())
|
||||
{
|
||||
<option value="@buttonType.ToString()">@buttonType.GetDescription()</option>
|
||||
}
|
||||
</select>
|
||||
</div>
|
||||
@if (Presets?.Any() == true)
|
||||
{
|
||||
<div class="mb-2">
|
||||
<label class="form-label" for="preset">Preset</label>
|
||||
<select class="form-select" id="preset" @bind="PresetName">
|
||||
<option value="">Select preset</option>
|
||||
@foreach (var preset in Presets)
|
||||
{
|
||||
<option value="@preset.name">@preset.name</option>
|
||||
}
|
||||
</select>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
</form>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-danger" data-bs-dismiss="modal" @onclick="RemoveClick">Remove</button>
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal" @onclick="() => Button = null">Close</button>
|
||||
<button type="button" class="btn btn-primary" data-bs-dismiss="modal" @onclick="ApplyClick">Save</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@code {
|
||||
[Parameter]
|
||||
public BaseRcButtonDto<TPreset, TButtonEnum>? ButtonParam { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public Func<BaseRcButtonDto<TPreset, TButtonEnum>, Task>? AfterApply { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public List<TPreset>? Presets { get; set; }
|
||||
|
||||
private BaseRcButtonDto<TPreset, TButtonEnum>? Button { get; set; }
|
||||
private string? PresetName;
|
||||
|
||||
protected override void OnParametersSet()
|
||||
{
|
||||
if (ButtonParam == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Button = new BaseRcButtonDto<TPreset, TButtonEnum>(ButtonParam);
|
||||
PresetName = Button.Preset?.name;
|
||||
}
|
||||
|
||||
private async Task ApplyClick(MouseEventArgs e)
|
||||
{
|
||||
if (Button == null || ButtonParam == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Button.Preset = (Presets?.FirstOrDefault(p => p.name == PresetName))!;
|
||||
|
||||
ButtonParam.Update(Button);
|
||||
|
||||
if (AfterApply != null)
|
||||
{
|
||||
await AfterApply.Invoke(ButtonParam);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task RemoveClick(MouseEventArgs e)
|
||||
{
|
||||
if (Button == null || ButtonParam == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (AfterApply != null)
|
||||
{
|
||||
await AfterApply.Invoke(null!);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,319 @@
|
||||
@using ColorControl.Shared.Contracts
|
||||
@using ColorControl.Shared.Contracts.Base
|
||||
@using ColorControl.Shared.Contracts.RemoteControl
|
||||
@using ColorControl.UI.Services
|
||||
@using NvAPIWrapper.Native.Display
|
||||
@using ColorControl.Shared.Common
|
||||
@using System.Globalization
|
||||
|
||||
@* @rendermode Constants.RenderMode *@
|
||||
|
||||
@typeparam TDevice where TDevice : BaseDeviceDto
|
||||
@typeparam TPreset where TPreset : PresetBase, new()
|
||||
@typeparam TButtonEnum where TButtonEnum : struct, Enum
|
||||
|
||||
@inject RpcUiClientService _rpcClientService
|
||||
@inject JSHelper jsHelper
|
||||
|
||||
<style>
|
||||
.myhover {
|
||||
border-radius: 25px;
|
||||
width: @(ButtonSize)%;
|
||||
aspect-ratio: 1;
|
||||
opacity: 0.2;
|
||||
border-width: 0;
|
||||
}
|
||||
|
||||
.myhover:hover {
|
||||
opacity: 0.4;
|
||||
background-color: white;
|
||||
}
|
||||
</style>
|
||||
|
||||
<GenericRcButtonPage ButtonParam="CurrentButton" AfterApply="AfterApplyButton" TPreset="TPreset" TButtonEnum="TButtonEnum" Presets="Presets"></GenericRcButtonPage>
|
||||
|
||||
<div>
|
||||
<div>
|
||||
<h5>@(Device?.Name == null ? $"No device selected" : Device.Name)</h5>
|
||||
</div>
|
||||
<div>
|
||||
<form class="row g-1 needs-validation @WasValidated" novalidate>
|
||||
@if (Remotes == null)
|
||||
{
|
||||
<div>Loading...</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
<select class="form-select" id="remoteControl" @bind="SelectedRemoteName" @bind:after="RemoteChanged">
|
||||
@foreach (var remote in Remotes)
|
||||
{
|
||||
<option id="@remote.Name">@remote.Name</option>
|
||||
}
|
||||
</select>
|
||||
if (RemoteController == null)
|
||||
{
|
||||
<div>Select a remote</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
@if (AllowEditing)
|
||||
{
|
||||
<button type="button" class="btn btn-primary" data-bs-dismiss="@(IsValid() ? "modal" : "")" @onclick="ApplyClick">Save</button>
|
||||
}
|
||||
@* <p>Left: @CurrentX, Top: @CurrentY</p> *@
|
||||
<div style="position: relative" @onclick="Button_Clicked" @oncontextmenu:preventDefault="true" @oncontextmenu="e => NewRcButtonRightClick(e)">
|
||||
<img src="remotes/@RemoteController.ImageFileName" id="rcImage" class="w-100" />
|
||||
@foreach (var button in RemoteController.Buttons)
|
||||
{
|
||||
<button class="myhover" type="button"
|
||||
style="position: absolute; top: @(button.Top.ToString(CultureInfo.InvariantCulture))%; left: @(button.Left.ToString(CultureInfo.InvariantCulture))%"
|
||||
@onclick="() => RcButtonOnClick(button)" @oncontextmenu="e => RcButtonRightClick(e, button)" @oncontextmenu:preventDefault="true" @oncontextmenu:stopPropagation>
|
||||
</button>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
}
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@code {
|
||||
private TDevice? _device;
|
||||
|
||||
[Parameter]
|
||||
public TDevice? Device { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public Func<TDevice, Task>? AfterApply { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public string? ServiceName { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public List<TPreset>? Presets { get; set; }
|
||||
|
||||
private string WasValidated = "";
|
||||
private bool IsVisible;
|
||||
|
||||
private string? SelectedRemoteName;
|
||||
private BaseRcDto<TPreset, TButtonEnum>? RemoteController;
|
||||
private List<BaseRcDto<TPreset, TButtonEnum>>? Remotes;
|
||||
|
||||
private double CurrentX, CurrentY;
|
||||
|
||||
private RectDimension? ImageDimension;
|
||||
|
||||
private BaseRcButtonDto<TPreset, TButtonEnum>? CurrentButton;
|
||||
|
||||
private double ButtonSize = 13;
|
||||
private bool AllowEditing = false;
|
||||
|
||||
protected override async void OnParametersSet()
|
||||
{
|
||||
if (ServiceName != null && (_device != Device || Device == null))
|
||||
{
|
||||
_device = Device;
|
||||
|
||||
Remotes = await _rpcClientService.CallAsync<List<BaseRcDto<TPreset, TButtonEnum>>>(ServiceName, "GetRemotes");
|
||||
|
||||
if (Remotes?.Any() == true)
|
||||
{
|
||||
RemoteController = Remotes.First();
|
||||
SelectedRemoteName = RemoteController.Name;
|
||||
StateHasChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async Task ApplyClick(MouseEventArgs e)
|
||||
{
|
||||
if (RemoteController == null || ServiceName == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!IsValid())
|
||||
{
|
||||
WasValidated = "was-validated";
|
||||
return;
|
||||
}
|
||||
|
||||
var result = await _rpcClientService.CallAsync<bool>(ServiceName, "UpdateRemote", RemoteController);
|
||||
|
||||
// if (AfterApply != null)
|
||||
// {
|
||||
// await AfterApply.Invoke(Device);
|
||||
// }
|
||||
}
|
||||
|
||||
private void ModalOnFocus(FocusEventArgs e)
|
||||
{
|
||||
if (!IsVisible)
|
||||
{
|
||||
WasValidated = "";
|
||||
IsVisible = true;
|
||||
}
|
||||
}
|
||||
|
||||
private bool IsValid()
|
||||
{
|
||||
return true; // !string.IsNullOrEmpty(Device?.Name) && (Device?.IsCustom != true || !string.IsNullOrEmpty(Device?.IpAddress));
|
||||
}
|
||||
|
||||
private async Task RcButtonOnClick(BaseRcButtonDto<TPreset, TButtonEnum> button)
|
||||
{
|
||||
if (ServiceName == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var rcButton = button.Button;
|
||||
|
||||
var preset = button.Preset;
|
||||
if (preset == null)
|
||||
{
|
||||
preset = new TPreset();
|
||||
preset.name = rcButton.ToString();
|
||||
preset.SetSteps([rcButton.ToString()]);
|
||||
|
||||
}
|
||||
|
||||
await _rpcClientService.CallAsync<bool>(ServiceName, "ApplyPreset", preset);
|
||||
}
|
||||
|
||||
public async Task Button_Clicked(MouseEventArgs e)
|
||||
{
|
||||
if (ImageDimension == null)
|
||||
{
|
||||
ImageDimension = await jsHelper.GetElementDimensions("rcImage");
|
||||
}
|
||||
|
||||
if (ImageDimension != null)
|
||||
{
|
||||
CurrentX = Math.Round(((double)e.OffsetX / ImageDimension.Width) * 100, 4);
|
||||
CurrentY = Math.Round(((double)e.OffsetY / ImageDimension.Height) * 100, 4);
|
||||
return;
|
||||
}
|
||||
|
||||
CurrentX = e.OffsetX;
|
||||
CurrentY = e.OffsetY;
|
||||
}
|
||||
|
||||
private async Task RcButtonRightClick(MouseEventArgs e, BaseRcButtonDto<TPreset, TButtonEnum> button)
|
||||
{
|
||||
if (!AllowEditing)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
CurrentButton = button;
|
||||
|
||||
if (e.ShiftKey)
|
||||
{
|
||||
AlignItemsHorizontally(button);
|
||||
return;
|
||||
}
|
||||
if (e.CtrlKey)
|
||||
{
|
||||
AlignItemsVertically(button);
|
||||
return;
|
||||
}
|
||||
|
||||
await jsHelper.OpenModal("rcButtonModal");
|
||||
}
|
||||
|
||||
private void AlignItemsHorizontally(BaseRcButtonDto<TPreset, TButtonEnum> button)
|
||||
{
|
||||
if (RemoteController?.Buttons == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var alignedButtons = RemoteController.Buttons.Where(b => b.Top - 2 <= button.Top && b.Top + 2 >= button.Top);
|
||||
|
||||
foreach (var b in alignedButtons)
|
||||
{
|
||||
b.Top = button.Top;
|
||||
}
|
||||
}
|
||||
|
||||
private void AlignItemsVertically(BaseRcButtonDto<TPreset, TButtonEnum> button)
|
||||
{
|
||||
if (RemoteController?.Buttons == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var alignedButtons = RemoteController.Buttons.Where(b => b.Top - 15 <= button.Top && b.Top + 15 >= button.Top && b.Left - 8 <= button.Left && b.Left + 8 >= button.Left);
|
||||
|
||||
foreach (var b in alignedButtons)
|
||||
{
|
||||
b.Left = button.Left;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task NewRcButtonRightClick(MouseEventArgs e)
|
||||
{
|
||||
if (!AllowEditing)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (ImageDimension == null)
|
||||
{
|
||||
ImageDimension = await jsHelper.GetElementDimensions("rcImage");
|
||||
}
|
||||
|
||||
if (ImageDimension != null)
|
||||
{
|
||||
var posX = Math.Round(((double)e.OffsetX / ImageDimension.Width) * 100, 4);
|
||||
var posY = Math.Round(((double)e.OffsetY / ImageDimension.Height) * 100, 4);
|
||||
|
||||
posX -= (ButtonSize / 2);
|
||||
posY -= ((double)ImageDimension.Width / ImageDimension.Height) * ((ButtonSize / 2) - 0.5);
|
||||
|
||||
CurrentButton = new BaseRcButtonDto<TPreset, TButtonEnum>
|
||||
{
|
||||
Top = posY,
|
||||
Left = posX
|
||||
};
|
||||
await jsHelper.OpenModal("rcButtonModal");
|
||||
}
|
||||
}
|
||||
|
||||
private Task AfterApplyButton(BaseRcButtonDto<TPreset, TButtonEnum>? button)
|
||||
{
|
||||
if (RemoteController == null)
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
if (button == null)
|
||||
{
|
||||
RemoteController.Buttons.Remove(CurrentButton);
|
||||
}
|
||||
else if (!RemoteController.Buttons.Contains(button))
|
||||
{
|
||||
RemoteController.Buttons.Add(button);
|
||||
}
|
||||
|
||||
StateHasChanged();
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private void KeyDown(KeyboardEventArgs e)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
private void KeyUp(KeyboardEventArgs e)
|
||||
{
|
||||
|
||||
}
|
||||
private void RemoteChanged()
|
||||
{
|
||||
RemoteController = Remotes?.FirstOrDefault(r => r.Name == SelectedRemoteName);
|
||||
ImageDimension = null;
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
@using ColorControl.Shared.Contracts
|
||||
@using ColorControl.Shared.Contracts.Samsung
|
||||
@using ColorControl.UI.Components.Pages.Generic
|
||||
@using ColorControl.UI.Services
|
||||
@using NvAPIWrapper.Native.Display
|
||||
@using ColorControl.Shared.Common
|
||||
@@ -7,10 +8,8 @@
|
||||
@rendermode Constants.RenderMode
|
||||
|
||||
@inject RpcUiClientService _rpcClientService
|
||||
@inject AppState AppState
|
||||
@inject NavigationManager _navigationManager;
|
||||
|
||||
<div class="modal" id="deviceModal" tabindex="-1" data-bs-backdrop="static" data-bs-keyboard="false" @onfocus="ModalOnFocus">
|
||||
<div class="modal" id="samsungDeviceModal" tabindex="-1" data-bs-backdrop="static" data-bs-keyboard="false" @onfocus="ModalOnFocus">
|
||||
<div class="modal-dialog modal-dialog-scrollable modal-dialog-centered">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
@@ -73,7 +72,7 @@
|
||||
|
||||
<div class="accordion-item">
|
||||
<h2 class="accordion-header">
|
||||
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#deviceOptions">
|
||||
<button class="accordion-button collapsed py-2" type="button" data-bs-toggle="collapse" data-bs-target="#deviceOptions">
|
||||
Device options
|
||||
</button>
|
||||
</h2>
|
||||
@@ -165,6 +164,10 @@
|
||||
</form>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
@if (Device?.IsCustom == true)
|
||||
{
|
||||
<button type="button" class="btn btn-danger" data-bs-toggle="modal" data-bs-target="#removeDeviceModal">Remove</button>
|
||||
}
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal" @onclick="() => IsVisible = false">Close</button>
|
||||
<button type="button" class="btn btn-primary" data-bs-dismiss="@(IsValid() ? "modal" : "")" @onclick="ApplyClick">Save</button>
|
||||
</div>
|
||||
@@ -172,6 +175,8 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ConfirmModal ModalId="removeDeviceModal" Title="Remove device" Message="Are you sure you want to remove this device?" OnConfirm="DeviceOnRemoveConfirmed" NextModalId="samsungDeviceModal" />
|
||||
|
||||
@code {
|
||||
private SamsungDeviceDto? _device;
|
||||
|
||||
@@ -216,6 +221,21 @@
|
||||
}
|
||||
}
|
||||
|
||||
private async Task DeviceOnRemoveConfirmed()
|
||||
{
|
||||
if (Device == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var result = await _rpcClientService.CallAsync<bool>("SamsungService", "RemoveDevice", Device);
|
||||
|
||||
if (AfterApply != null)
|
||||
{
|
||||
await AfterApply.Invoke(Device);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task TestConnectionClick(MouseEventArgs e)
|
||||
{
|
||||
IsTestingConnection = true;
|
||||
|
||||
@@ -9,8 +9,6 @@
|
||||
@rendermode Constants.RenderMode
|
||||
|
||||
@inject RpcUiClientService _rpcClientService
|
||||
@inject AppState AppState
|
||||
@inject NavigationManager _navigationManager;
|
||||
|
||||
<div class="modal" id="presetModal" tabindex="-1" data-bs-backdrop="static" data-bs-keyboard="false" @onfocus="ModalOnFocus">
|
||||
<div class="modal-dialog modal-dialog-scrollable modal-dialog-centered">
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
@using ColorControl.Shared.Contracts
|
||||
@using ColorControl.Shared.Contracts.NVIDIA
|
||||
@using ColorControl.Shared.Contracts.Samsung
|
||||
@using ColorControl.UI.Components.Pages.RemoteControl
|
||||
@using ColorControl.UI.Services;
|
||||
@using ColorControl.UI.Components.Pages.Generic
|
||||
@using ColorControl.UI.Generics
|
||||
@@ -22,6 +23,9 @@
|
||||
<div class="d-flex align-items-center">
|
||||
<h1>Samsung controller</h1>
|
||||
<div class="d-flex ms-auto">
|
||||
<button type="button" class="btn btn-sm btn-primary me-2" data-bs-toggle="offcanvas" data-bs-target="#rcCanvasRight" aria-controls="rcCanvasRight">
|
||||
Remote control
|
||||
</button>
|
||||
<div class="btn-group">
|
||||
<button type="button" class="btn btn-sm btn-primary dropdown-toggle me-2" data-bs-toggle="dropdown" aria-expanded="false" data-bs-auto-close="outside">
|
||||
Buttons
|
||||
@@ -62,6 +66,16 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="offcanvas offcanvas-end" style="width: 280px" data-bs-scroll="true" tabindex="-1" id="rcCanvasRight" aria-labelledby="rcCanvasRightRightLabel">
|
||||
<div class="offcanvas-header">
|
||||
<h5 class="offcanvas-title" id="rcCanvasRightRightLabel">Remote control</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="offcanvas" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="offcanvas-body">
|
||||
<GenericRcPage ServiceName="SamsungService" Device="CurrentDevice" TDevice="SamsungDeviceDto" TPreset="SamsungPreset" TButtonEnum="ButtonType"></GenericRcPage>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<SamsungDevicePage Device="CurrentDevice" AfterApply="AfterDeviceApplied" />
|
||||
<SamsungPresetPage PresetParam="CurrentPreset" Devices="Devices" AfterApply="AfterPresetApplied" />
|
||||
<ConfirmModal Title="Delete preset" Message="Are you sure you want to delete the preset?" OnConfirm="PresetOnDeleteConfirmed" />
|
||||
@@ -82,7 +96,7 @@ else
|
||||
}
|
||||
<span role="status">Refresh</span>
|
||||
</button>
|
||||
<button type="button" class="btn btn-sm btn-primary" data-bs-toggle="modal" data-bs-target="#deviceModal" @onclick="() => AddDeviceOnClick()">
|
||||
<button type="button" class="btn btn-sm btn-primary" data-bs-toggle="modal" data-bs-target="#samsungDeviceModal" @onclick="() => AddDeviceOnClick()">
|
||||
Add
|
||||
</button>
|
||||
</div>
|
||||
@@ -117,7 +131,7 @@ else
|
||||
{
|
||||
<button class="btn btn-sm btn-secondary ms-2" @onclick="() => SetAsPrimaryOnClick(device)">Set as primary</button>
|
||||
}
|
||||
<button type="button" class="btn btn-sm btn-primary ms-2" data-bs-toggle="modal" data-bs-target="#deviceModal" @onclick="() => DeviceOnClick(device)">
|
||||
<button type="button" class="btn btn-sm btn-primary ms-2" data-bs-toggle="modal" data-bs-target="#samsungDeviceModal" @onclick="() => DeviceOnClick(device)">
|
||||
Settings
|
||||
</button>
|
||||
</div>
|
||||
@@ -210,6 +224,7 @@ else
|
||||
Config = await _rpcClientService.CallAsync<SamsungServiceConfigDto>("SamsungService", "GetConfig");
|
||||
await RefreshPresets();
|
||||
Devices = await _rpcClientService.CallAsync<List<SamsungDeviceDto>>("SamsungService", "GetDevices");
|
||||
CurrentDevice = Devices?.FirstOrDefault(d => d.MacAddress == Config?.PreferredMacAddress) ?? Devices?.FirstOrDefault();
|
||||
InvokableActions = await _rpcClientService.CallAsync<List<InvokableActionDto<SamsungPreset>>>("SamsungService", "GetInvokableActions");
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,14 @@
|
||||
|
||||
namespace ColorControl.UI.Services;
|
||||
|
||||
public class RectDimension
|
||||
{
|
||||
public double X { get; set; }
|
||||
public double Y { get; set; }
|
||||
public double Width { get; set; }
|
||||
public double Height { get; set; }
|
||||
}
|
||||
|
||||
public class JSHelper(IJSRuntime jsRuntime)
|
||||
{
|
||||
private IJSObjectReference? jsModule;
|
||||
@@ -26,6 +34,20 @@ public class JSHelper(IJSRuntime jsRuntime)
|
||||
await InvokeVoidAsync("ShowToast", id);
|
||||
}
|
||||
|
||||
public async Task<RectDimension> GetElementDimensions(string id)
|
||||
{
|
||||
jsModule = await LoadModule();
|
||||
|
||||
return await jsModule.InvokeAsync<RectDimension>("GetElementDimensions", id);
|
||||
}
|
||||
|
||||
public async Task<string> GetElementClassName(string id)
|
||||
{
|
||||
jsModule = await LoadModule();
|
||||
|
||||
return await jsModule.InvokeAsync<string>("GetElementClassName", id);
|
||||
}
|
||||
|
||||
private async ValueTask<T> InvokeAsync<T>(string identifier, params object?[]? names) where T : struct
|
||||
{
|
||||
jsModule = await LoadModule();
|
||||
|
||||
@@ -14,4 +14,12 @@ export function OpenModal(modalId) {
|
||||
export function ShowToast(toastId) {
|
||||
const toastBootstrap = bootstrap.Toast.getOrCreateInstance('#' + toastId);
|
||||
toastBootstrap?.show();
|
||||
}
|
||||
|
||||
export function GetElementDimensions(elementId) {
|
||||
return document.getElementById(elementId).getBoundingClientRect();
|
||||
}
|
||||
|
||||
export function GetElementClassName(elementId) {
|
||||
return document.getElementById(elementId).className;
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 41 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 39 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 52 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 65 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 81 KiB |
@@ -6,20 +6,13 @@
|
||||
<PublishUrl>publish\</PublishUrl>
|
||||
<Install>true</Install>
|
||||
<InstallFrom>Disk</InstallFrom>
|
||||
<UpdateEnabled>false</UpdateEnabled>
|
||||
<UpdateMode>Foreground</UpdateMode>
|
||||
<UpdateInterval>7</UpdateInterval>
|
||||
<UpdateIntervalUnits>Days</UpdateIntervalUnits>
|
||||
<UpdatePeriodically>false</UpdatePeriodically>
|
||||
<UpdateRequired>false</UpdateRequired>
|
||||
<MapFileExtensions>false</MapFileExtensions>
|
||||
<UpdateUrl>https://www.maassoft.com/downloads/ColorControl/</UpdateUrl>
|
||||
<ProductName>ColorControl</ProductName>
|
||||
<PublisherName>Maassoft</PublisherName>
|
||||
<Company>Maassoft</Company>
|
||||
<ApplicationRevision>0</ApplicationRevision>
|
||||
<ApplicationVersion>10.2.0.0</ApplicationVersion>
|
||||
<Version>10.2.0.0</Version>
|
||||
<ApplicationVersion>10.3.0.0</ApplicationVersion>
|
||||
<Version>10.3.0.0</Version>
|
||||
<UseApplicationTrust>false</UseApplicationTrust>
|
||||
<PublishWizardCompleted>true</PublishWizardCompleted>
|
||||
<BootstrapperEnabled>false</BootstrapperEnabled>
|
||||
@@ -30,13 +23,6 @@
|
||||
<ApplicationIcon>Resources\pngbarn.ico</ApplicationIcon>
|
||||
<Copyright>2020-2025 Maassoft</Copyright>
|
||||
<Authors>Maassoft</Authors>
|
||||
<ManifestCertificateThumbprint>CF0BC153EA664CD97AC6F234B91F45D34630958F</ManifestCertificateThumbprint>
|
||||
<ManifestKeyFile>ColorControl_1_TemporaryKey.pfx</ManifestKeyFile>
|
||||
<GenerateManifests>true</GenerateManifests>
|
||||
<TargetZone>LocalIntranet</TargetZone>
|
||||
<ApplicationManifest>Properties\app.manifest</ApplicationManifest>
|
||||
<SignManifests>true</SignManifests>
|
||||
<AssemblyOriginatorKeyFile>ColorControl_TemporaryKey.pfx</AssemblyOriginatorKeyFile>
|
||||
<IncludeSourceRevisionInInformationalVersion>false</IncludeSourceRevisionInInformationalVersion>
|
||||
<ResolveAssemblyWarnOrErrorOnTargetArchitectureMismatch>None</ResolveAssemblyWarnOrErrorOnTargetArchitectureMismatch>
|
||||
</PropertyGroup>
|
||||
@@ -52,6 +38,8 @@
|
||||
<PathMap Condition="'$(Configuration)' == 'Release'">$(MSBuildThisFileDirectory)=.\</PathMap>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<None Remove="Resources\LgRemotes.json" />
|
||||
<None Remove="Resources\SamsungRemotes.json" />
|
||||
<None Remove="XForms\BrowserWindow.xaml" />
|
||||
<None Remove="XForms\ColorProfileWindow.xaml" />
|
||||
<None Remove="XForms\LogWindow.xaml" />
|
||||
@@ -82,10 +70,12 @@
|
||||
<Compile Update="Services\Samsung\SamsungPanel.cs">
|
||||
<SubType>UserControl</SubType>
|
||||
</Compile>
|
||||
<EmbeddedResource Include="Resources\LgRemotes.json" />
|
||||
<EmbeddedResource Include="Resources\LG_register.json" />
|
||||
<EmbeddedResource Include="Resources\RC_coords.txt" />
|
||||
<EmbeddedResource Include="Resources\LG_remote.jpg" />
|
||||
<EmbeddedResource Include="Resources\pngbarn.ico" />
|
||||
<EmbeddedResource Include="Resources\SamsungRemotes.json" />
|
||||
<Content Include="LgPresets.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
using ColorControl.Services.AMD;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Forms;
|
||||
using ColorControl.Services.AMD;
|
||||
using ColorControl.Services.LG;
|
||||
using ColorControl.Services.NVIDIA;
|
||||
using ColorControl.Services.Samsung;
|
||||
@@ -6,10 +10,6 @@ using ColorControl.Shared.Common;
|
||||
using ColorControl.Shared.Contracts;
|
||||
using ColorControl.Svc;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace ColorControl
|
||||
{
|
||||
@@ -23,20 +23,18 @@ namespace ColorControl
|
||||
return true;
|
||||
}
|
||||
|
||||
var useConsole = startUpParams.NoGui || existingProcess != null;
|
||||
var useConsole = startUpParams.NoGui || existingProcess?.SessionId > 0;
|
||||
|
||||
if (!useConsole)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
startUpParams.NoGui = true;
|
||||
|
||||
var result = false;
|
||||
|
||||
if (startUpParams.ExecuteHelp)
|
||||
{
|
||||
Utils.OpenConsole();
|
||||
PrepareConsole(startUpParams);
|
||||
|
||||
Console.WriteLine();
|
||||
Console.WriteLine($@"ColorControl CLI {Application.ProductVersion}
|
||||
@@ -56,7 +54,7 @@ Options:
|
||||
|
||||
if (startUpParams.ExecuteLgPreset)
|
||||
{
|
||||
Utils.OpenConsole();
|
||||
PrepareConsole(startUpParams);
|
||||
|
||||
Console.WriteLine($"Executing LG-preset '{startUpParams.LgPresetName}'...");
|
||||
await LgService.ExecutePresetAsync(startUpParams.LgPresetName);
|
||||
@@ -68,7 +66,7 @@ Options:
|
||||
|
||||
if (startUpParams.ExecuteSamsungPreset)
|
||||
{
|
||||
Utils.OpenConsole();
|
||||
PrepareConsole(startUpParams);
|
||||
|
||||
Console.WriteLine($"Executing Samsung-preset '{startUpParams.SamsungPresetName}'...");
|
||||
await SamsungService.ExecutePresetAsync(startUpParams.SamsungPresetName);
|
||||
@@ -80,7 +78,7 @@ Options:
|
||||
|
||||
if (startUpParams.ExecuteNvidiaPreset)
|
||||
{
|
||||
Utils.OpenConsole();
|
||||
PrepareConsole(startUpParams);
|
||||
|
||||
Console.WriteLine($"Executing NVIDIA-preset '{startUpParams.NvidiaPresetIdOrName}'...");
|
||||
await NvService.ExecutePresetAsync(startUpParams.NvidiaPresetIdOrName);
|
||||
@@ -92,7 +90,7 @@ Options:
|
||||
|
||||
if (startUpParams.ExecuteAmdPreset)
|
||||
{
|
||||
Utils.OpenConsole();
|
||||
PrepareConsole(startUpParams);
|
||||
|
||||
Console.WriteLine($"Executing AMD-preset '{startUpParams.AmdPresetIdOrName}'...");
|
||||
await AmdService.ExecutePresetAsync(startUpParams.AmdPresetIdOrName);
|
||||
@@ -105,6 +103,12 @@ Options:
|
||||
return result;
|
||||
}
|
||||
|
||||
private static void PrepareConsole(StartUpParams startUpParams)
|
||||
{
|
||||
Utils.OpenConsole();
|
||||
startUpParams.NoGui = true;
|
||||
}
|
||||
|
||||
private static async Task StartElevated()
|
||||
{
|
||||
try
|
||||
|
||||
+442
-442
@@ -29,446 +29,446 @@ using System.Windows.Forms;
|
||||
|
||||
namespace ColorControl
|
||||
{
|
||||
static class Program
|
||||
{
|
||||
public const string TS_TASKNAME = "ColorControl";
|
||||
public static string DataDir { get; private set; }
|
||||
public static string ConfigFilename { get; private set; }
|
||||
public static string LogFilename { get; private set; }
|
||||
public static Config Config { get; private set; }
|
||||
public static GlobalContext AppContext { get; private set; }
|
||||
|
||||
public static bool IsRestarting { get; private set; }
|
||||
public static bool UserExit = false;
|
||||
|
||||
private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
|
||||
private static Mutex _mutex;
|
||||
public static MainForm _mainForm;
|
||||
private static LoggingRule _loggingRule;
|
||||
|
||||
/// <summary>
|
||||
/// The main entry point for the application.
|
||||
/// </summary>
|
||||
[STAThread] // autogenerated
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
// STA
|
||||
MainAsync(args).GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
static async Task MainAsync(string[] args)
|
||||
{
|
||||
DataDir = Utils.GetDataPath();
|
||||
|
||||
var runAsService = args.Contains("--service") || WinApiService.IsAdministratorStatic() && Process.GetCurrentProcess().Parent()?.ProcessName?.Equals("services", StringComparison.InvariantCultureIgnoreCase) == true;
|
||||
var runElevated = args.Contains("--elevated");
|
||||
|
||||
InitLogger(runAsService, runElevated);
|
||||
|
||||
Logger.Debug($"Using data path: {DataDir}");
|
||||
//Logger.Debug("Parent process: " + Process.GetCurrentProcess().Parent()?.ProcessName);
|
||||
|
||||
if (runAsService)
|
||||
{
|
||||
await RunService(args);
|
||||
return;
|
||||
}
|
||||
|
||||
await RunApp(args);
|
||||
}
|
||||
|
||||
static async Task RunApp(string[] args)
|
||||
{
|
||||
var startTime = DateTime.Now;
|
||||
var currentDomain = AppDomain.CurrentDomain;
|
||||
// Handler for unhandled exceptions.
|
||||
currentDomain.UnhandledException += GlobalUnhandledExceptionHandler;
|
||||
// Handler for exceptions in threads behind forms.
|
||||
Application.ThreadException += GlobalThreadExceptionHandler;
|
||||
|
||||
LoadConfig();
|
||||
|
||||
if (Config.UseGdiScaling)
|
||||
{
|
||||
var result = Application.SetHighDpiMode(HighDpiMode.DpiUnawareGdiScaled);
|
||||
|
||||
Logger.Debug($"Result of setting SetHighDpiMode: {result}");
|
||||
}
|
||||
|
||||
var mutexId = $"Global\\{typeof(MainForm).GUID}";
|
||||
|
||||
var startUpParams = StartUpParams.Parse(args);
|
||||
|
||||
AppContext = new GlobalContext(Config, startUpParams, DataDir, _loggingRule, null, startTime, mutexId);
|
||||
|
||||
var host = CreateHostBuilder().Build();
|
||||
ServiceProvider = host.Services;
|
||||
AppContext.ServiceProvider = ServiceProvider;
|
||||
|
||||
MessageForms.Title = AppContext.ApplicationTitle;
|
||||
|
||||
var existingProcess = Utils.GetProcessByName("ColorControl");
|
||||
|
||||
try
|
||||
{
|
||||
if (await CommandLineHandler.HandleStartupParams(startUpParams, existingProcess))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
Utils.CloseConsole();
|
||||
}
|
||||
|
||||
_mutex = new Mutex(true, AppContext.MutexId, out var mutexCreated);
|
||||
try
|
||||
{
|
||||
if (!mutexCreated)
|
||||
{
|
||||
if (existingProcess != null && existingProcess.Threads.Count > 0)
|
||||
{
|
||||
var thread = existingProcess.Threads[0];
|
||||
NativeMethods.EnumThreadWindows((uint)thread.Id, EnumThreadWindows, IntPtr.Zero);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
MessageBox.Show("Only one instance of this program can be active.", "ColorControl");
|
||||
}
|
||||
else
|
||||
{
|
||||
_mutex.WaitOne();
|
||||
try
|
||||
{
|
||||
Application.EnableVisualStyles();
|
||||
Application.SetCompatibleTextRenderingDefault(false);
|
||||
|
||||
if (Debugger.IsAttached)
|
||||
{
|
||||
//Utils.StartService();
|
||||
var winApiService = ServiceProvider.GetRequiredService<WinApiService>();
|
||||
|
||||
if (winApiService.IsAdministrator())
|
||||
{
|
||||
var backgroundService = ServiceProvider.GetRequiredService<ColorControlBackgroundService>();
|
||||
backgroundService.PipeName = PipeUtils.ElevatedPipe;
|
||||
|
||||
_ = Task.Run(async () => await backgroundService.StartAsync(CancellationToken.None));
|
||||
}
|
||||
}
|
||||
|
||||
var worker = ServiceProvider.GetRequiredService<MainWorker>();
|
||||
|
||||
await worker.DoWork();
|
||||
|
||||
if (Debugger.IsAttached && !IsRestarting)
|
||||
{
|
||||
var winApiAdminService = ServiceProvider.GetRequiredService<WinApiAdminService>();
|
||||
winApiAdminService.StopService();
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
MessageBox.Show("Error while initializing application: " + ex.ToLogString(Environment.StackTrace), "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (mutexCreated)
|
||||
{
|
||||
_mutex?.Close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void OpenDefaultUi()
|
||||
{
|
||||
var _ = Config.UiType switch
|
||||
{
|
||||
UiType.WebDefaultBrowser => OpenNewUi(),
|
||||
UiType.WebEmbedded => OpenNewUiEmbedded(),
|
||||
_ => OpenMainForm()
|
||||
};
|
||||
}
|
||||
|
||||
public static Task OpenMainForm()
|
||||
{
|
||||
_mainForm ??= ServiceProvider.GetRequiredService<MainForm>();
|
||||
_mainForm.OpenForm();
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public static bool IsMainFormOpened()
|
||||
{
|
||||
return _mainForm != null;
|
||||
}
|
||||
|
||||
public static void Exit()
|
||||
{
|
||||
UserExit = true;
|
||||
_mainForm?.Close();
|
||||
|
||||
if (AppContext.MainHandle != 0)
|
||||
{
|
||||
NativeMethods.SendMessageW(AppContext.MainHandle, NativeConstants.WM_CLOSE, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
public static void Restart()
|
||||
{
|
||||
IsRestarting = true;
|
||||
|
||||
_mainForm?.CloseForRestart();
|
||||
|
||||
_mutex?.Close();
|
||||
_mutex = null;
|
||||
|
||||
Application.Restart();
|
||||
Environment.Exit(0);
|
||||
}
|
||||
|
||||
public static async Task OpenNewUi()
|
||||
{
|
||||
if (await StartUiServer())
|
||||
{
|
||||
await Task.Delay(500);
|
||||
}
|
||||
|
||||
var winApi = AppContext.ServiceProvider.GetRequiredService<WinApiAdminService>();
|
||||
|
||||
winApi.StartProcess(GetNewUiUrl());
|
||||
}
|
||||
|
||||
public static string GetNewUiUrl()
|
||||
{
|
||||
return $"http://localhost:{(Config.UiPort > 0 ? Config.UiPort : 5000)}";
|
||||
}
|
||||
|
||||
public static async Task OpenNewUiEmbedded()
|
||||
{
|
||||
if (await StartUiServer())
|
||||
{
|
||||
await Task.Delay(500);
|
||||
}
|
||||
|
||||
BrowserWindow.CreateAndShow(GetNewUiUrl());
|
||||
}
|
||||
|
||||
private static bool UiServerStarted;
|
||||
|
||||
public static async Task<bool> StartUiServer()
|
||||
{
|
||||
if (UiServerStarted)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var backgroundService = ServiceProvider.GetRequiredService<ColorControlBackgroundService>();
|
||||
|
||||
backgroundService.PipeName = PipeUtils.MainPipe;
|
||||
|
||||
await backgroundService.StartAsync(CancellationToken.None);
|
||||
|
||||
var _ = Task.Run(() => ColorControl.UI.Blazor.Start(Config));
|
||||
UiServerStarted = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static void InitLogger(bool runAsService, bool runElevated)
|
||||
{
|
||||
var config = new LoggingConfiguration();
|
||||
|
||||
// Targets where to log to: File and Console
|
||||
var logFileAppendix = runAsService ? "_svc" : runElevated ? "_elevated" : "";
|
||||
|
||||
LogFilename = Path.Combine(DataDir, $"LogFile{logFileAppendix}.txt");
|
||||
|
||||
var logfile = new NLog.Targets.FileTarget("logfile") { FileName = LogFilename };
|
||||
|
||||
_loggingRule = new LoggingRule("*", LogLevel.Trace, LogLevel.Fatal, logfile);
|
||||
_loggingRule.RuleName = "ColorControl";
|
||||
|
||||
// Rules for mapping loggers to targets
|
||||
config.AddRule(_loggingRule);
|
||||
|
||||
// Apply config
|
||||
LogManager.Configuration = config;
|
||||
}
|
||||
|
||||
private static void LoadConfig()
|
||||
{
|
||||
ConfigFilename = Path.Combine(DataDir, "Settings.json");
|
||||
|
||||
try
|
||||
{
|
||||
if (File.Exists(ConfigFilename))
|
||||
{
|
||||
var data = File.ReadAllText(ConfigFilename);
|
||||
Config = JsonConvert.DeserializeObject<Config>(data);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Error($"LoadConfig: {ex.Message}");
|
||||
}
|
||||
Config ??= new Config();
|
||||
|
||||
var minLogLevel = LogLevel.FromString(Config.LogLevel);
|
||||
|
||||
if (minLogLevel != LogLevel.Trace)
|
||||
{
|
||||
_loggingRule.SetLoggingLevels(minLogLevel, LogLevel.Fatal);
|
||||
LogManager.ReconfigExistingLoggers();
|
||||
}
|
||||
}
|
||||
|
||||
public static IServiceProvider ServiceProvider { get; private set; }
|
||||
private static IHostBuilder CreateHostBuilder()
|
||||
{
|
||||
return Host.CreateDefaultBuilder()
|
||||
.ConfigureServices((context, services) =>
|
||||
{
|
||||
//services.AddSingleton<NvService>();
|
||||
services.RegisterSharedServices();
|
||||
services.AddSingleton<MainWorker>();
|
||||
services.AddSingleton<NotifyIconManager>();
|
||||
services.AddSingleton<LgService>();
|
||||
services.AddSingleton<GameService>();
|
||||
services.AddSingleton<SamsungService>();
|
||||
services.AddSingleton<AmdService>();
|
||||
services.AddSingleton<PowerEventDispatcher>();
|
||||
services.AddSingleton<ProcessEventDispatcher>();
|
||||
services.AddSingleton<WindowMessageDispatcher>();
|
||||
services.AddSingleton<KeyboardShortcutDispatcher>();
|
||||
services.AddSingleton<DisplayChangeEventDispatcher>();
|
||||
services.AddSingleton<MainForm>();
|
||||
services.AddSingleton<LogWindow>();
|
||||
services.AddSingleton<BrowserWindow>();
|
||||
services.AddTransient<ColorProfileWindow>();
|
||||
services.AddSingleton<RestartDetector>();
|
||||
services.AddSingleton<InfoPanel>();
|
||||
services.AddSingleton<OptionsPanel>();
|
||||
services.AddSingleton<ElevationService>();
|
||||
services.AddSingleton<UpdateManager>();
|
||||
|
||||
services.AddSingleton<OptionsService>();
|
||||
services.AddSingleton<LoggingService>();
|
||||
|
||||
services.AddSingleton<AmdPanel>();
|
||||
services.AddSingleton<NvPanel>();
|
||||
services.AddSingleton<LgPanel>();
|
||||
services.AddSingleton<SamsungPanel>();
|
||||
services.AddSingleton<GamePanel>();
|
||||
});
|
||||
}
|
||||
private static async Task RunService(string[] args)
|
||||
{
|
||||
var startTime = DateTime.Now;
|
||||
|
||||
Logger.Debug("RUNNING SERVICE");
|
||||
|
||||
AppContext = new GlobalContext(null, new StartUpParams(), DataDir, _loggingRule, null, startTime);
|
||||
|
||||
using IHost host = Host.CreateDefaultBuilder(args)
|
||||
.UseWindowsService(options =>
|
||||
{
|
||||
options.ServiceName = "ColorControl Service";
|
||||
})
|
||||
.ConfigureServices(services =>
|
||||
{
|
||||
services.RegisterSharedServices();
|
||||
services.AddSingleton<PowerEventDispatcher>();
|
||||
services.AddHostedService<ColorControlBackgroundService>();
|
||||
})
|
||||
.Build();
|
||||
|
||||
ServiceProvider = host.Services;
|
||||
AppContext.ServiceProvider = ServiceProvider;
|
||||
|
||||
await host.RunAsync();
|
||||
}
|
||||
|
||||
private static void RegisterSharedServices(this IServiceCollection services)
|
||||
{
|
||||
services.AddSingleton(AppContext);
|
||||
services.AddSingleton<SessionSwitchDispatcher>();
|
||||
services.AddSingleton<WinApiAdminService>();
|
||||
services.AddSingleton<WinApiService>();
|
||||
services.AddSingleton<WinElevatedProcessManager>();
|
||||
services.AddTransient<RpcClientService>();
|
||||
services.AddTransient<RpcServerService>();
|
||||
services.AddSingleton<NvService>();
|
||||
services.AddSingleton<WolService>();
|
||||
services.AddSingleton<ColorControlBackgroundService>();
|
||||
services.AddSingleton<ServiceManager>();
|
||||
services.AddSingleton<ColorProfileService>();
|
||||
|
||||
RpcServerService.ServiceTypes.Add(typeof(NvService));
|
||||
RpcServerService.ServiceTypes.Add(typeof(GameService));
|
||||
RpcServerService.ServiceTypes.Add(typeof(OptionsService));
|
||||
RpcServerService.ServiceTypes.Add(typeof(LoggingService));
|
||||
RpcServerService.ServiceTypes.Add(typeof(SamsungService));
|
||||
RpcServerService.ServiceTypes.Add(typeof(LgService));
|
||||
RpcServerService.ServiceTypes.Add(typeof(AmdService));
|
||||
RpcServerService.ServiceTypes.Add(typeof(ColorProfileService));
|
||||
}
|
||||
|
||||
public static int EnumThreadWindows(IntPtr handle, IntPtr param)
|
||||
{
|
||||
NativeMethods.SendMessageW(handle, Utils.WM_BRINGTOFRONT, UIntPtr.Zero, IntPtr.Zero);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
private static void GlobalUnhandledExceptionHandler(object sender, UnhandledExceptionEventArgs e)
|
||||
{
|
||||
GlobalHandleException((Exception)e.ExceptionObject, "Unhandled exception");
|
||||
}
|
||||
|
||||
private static void GlobalThreadExceptionHandler(object sender, ThreadExceptionEventArgs e)
|
||||
{
|
||||
GlobalHandleException(e.Exception, "Exception in thread");
|
||||
}
|
||||
|
||||
private static void GlobalHandleException(Exception exception, string type)
|
||||
{
|
||||
if (UserExit)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var trace = exception.ToLogString(Environment.StackTrace);
|
||||
var message = $"{type}: {trace}";
|
||||
|
||||
Logger.Error(message);
|
||||
|
||||
MessageBox.Show(message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||
}
|
||||
|
||||
internal static void SetTheme(bool useDarkMode)
|
||||
{
|
||||
DarkModeUtils.SetDarkMode(useDarkMode);
|
||||
|
||||
WinApi.RefreshImmersiveColorPolicyState();
|
||||
|
||||
DarkModeUtils.InitWpfTheme();
|
||||
|
||||
var notifyIconManager = ServiceProvider.GetRequiredService<NotifyIconManager>();
|
||||
|
||||
DarkModeUtils.SetContextMenuForeColor(notifyIconManager.NotifyIcon.ContextMenuStrip, FormUtils.CurrentForeColor);
|
||||
|
||||
_mainForm?.SetTheme(useDarkMode);
|
||||
}
|
||||
|
||||
internal static NotifyIcon GetNotifyIcon()
|
||||
{
|
||||
var notifyIconManager = ServiceProvider.GetRequiredService<NotifyIconManager>();
|
||||
|
||||
return notifyIconManager.NotifyIcon;
|
||||
}
|
||||
}
|
||||
static class Program
|
||||
{
|
||||
public const string TS_TASKNAME = "ColorControl";
|
||||
public static string DataDir { get; private set; }
|
||||
public static string ConfigFilename { get; private set; }
|
||||
public static string LogFilename { get; private set; }
|
||||
public static Config Config { get; private set; }
|
||||
public static GlobalContext AppContext { get; private set; }
|
||||
|
||||
public static bool IsRestarting { get; private set; }
|
||||
public static bool UserExit = false;
|
||||
|
||||
private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
|
||||
private static Mutex _mutex;
|
||||
public static MainForm _mainForm;
|
||||
private static LoggingRule _loggingRule;
|
||||
|
||||
/// <summary>
|
||||
/// The main entry point for the application.
|
||||
/// </summary>
|
||||
[STAThread] // autogenerated
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
// STA
|
||||
MainAsync(args).GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
static async Task MainAsync(string[] args)
|
||||
{
|
||||
DataDir = Utils.GetDataPath();
|
||||
|
||||
var runAsService = args.Contains("--service") || WinApiService.IsAdministratorStatic() && Process.GetCurrentProcess().Parent()?.ProcessName?.Equals("services", StringComparison.InvariantCultureIgnoreCase) == true;
|
||||
var runElevated = args.Contains("--elevated");
|
||||
|
||||
InitLogger(runAsService, runElevated);
|
||||
|
||||
Logger.Debug($"Using data path: {DataDir}");
|
||||
//Logger.Debug("Parent process: " + Process.GetCurrentProcess().Parent()?.ProcessName);
|
||||
|
||||
if (runAsService)
|
||||
{
|
||||
await RunService(args);
|
||||
return;
|
||||
}
|
||||
|
||||
await RunApp(args);
|
||||
}
|
||||
|
||||
static async Task RunApp(string[] args)
|
||||
{
|
||||
var startTime = DateTime.Now;
|
||||
var currentDomain = AppDomain.CurrentDomain;
|
||||
// Handler for unhandled exceptions.
|
||||
currentDomain.UnhandledException += GlobalUnhandledExceptionHandler;
|
||||
// Handler for exceptions in threads behind forms.
|
||||
Application.ThreadException += GlobalThreadExceptionHandler;
|
||||
|
||||
LoadConfig();
|
||||
|
||||
if (Config.UseGdiScaling)
|
||||
{
|
||||
var result = Application.SetHighDpiMode(HighDpiMode.DpiUnawareGdiScaled);
|
||||
|
||||
Logger.Debug($"Result of setting SetHighDpiMode: {result}");
|
||||
}
|
||||
|
||||
var mutexId = $"Global\\{typeof(MainForm).GUID}";
|
||||
|
||||
var startUpParams = StartUpParams.Parse(args);
|
||||
|
||||
AppContext = new GlobalContext(Config, startUpParams, DataDir, _loggingRule, null, startTime, mutexId);
|
||||
|
||||
var host = CreateHostBuilder().Build();
|
||||
ServiceProvider = host.Services;
|
||||
AppContext.ServiceProvider = ServiceProvider;
|
||||
|
||||
MessageForms.Title = AppContext.ApplicationTitle;
|
||||
|
||||
var existingProcess = Utils.GetProcessByName("ColorControl");
|
||||
|
||||
try
|
||||
{
|
||||
if (await CommandLineHandler.HandleStartupParams(startUpParams, existingProcess))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
Utils.CloseConsole();
|
||||
}
|
||||
|
||||
_mutex = new Mutex(true, AppContext.MutexId, out var mutexCreated);
|
||||
try
|
||||
{
|
||||
if (!mutexCreated)
|
||||
{
|
||||
if (existingProcess != null && existingProcess.Threads.Count > 0)
|
||||
{
|
||||
var thread = existingProcess.Threads[0];
|
||||
NativeMethods.EnumThreadWindows((uint)thread.Id, EnumThreadWindows, IntPtr.Zero);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
MessageBox.Show("Only one instance of this program can be active.", "ColorControl");
|
||||
}
|
||||
else
|
||||
{
|
||||
_mutex.WaitOne();
|
||||
try
|
||||
{
|
||||
Application.EnableVisualStyles();
|
||||
Application.SetCompatibleTextRenderingDefault(false);
|
||||
|
||||
if (Debugger.IsAttached)
|
||||
{
|
||||
//Utils.StartService();
|
||||
var winApiService = ServiceProvider.GetRequiredService<WinApiService>();
|
||||
|
||||
if (winApiService.IsAdministrator())
|
||||
{
|
||||
var backgroundService = ServiceProvider.GetRequiredService<ColorControlBackgroundService>();
|
||||
backgroundService.PipeName = PipeUtils.ElevatedPipe;
|
||||
|
||||
_ = Task.Run(async () => await backgroundService.StartAsync(CancellationToken.None));
|
||||
}
|
||||
}
|
||||
|
||||
var worker = ServiceProvider.GetRequiredService<MainWorker>();
|
||||
|
||||
await worker.DoWork();
|
||||
|
||||
if (Debugger.IsAttached && !IsRestarting)
|
||||
{
|
||||
var winApiAdminService = ServiceProvider.GetRequiredService<WinApiAdminService>();
|
||||
winApiAdminService.StopService();
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
MessageBox.Show("Error while initializing application: " + ex.ToLogString(Environment.StackTrace), "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (mutexCreated)
|
||||
{
|
||||
_mutex?.Close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void OpenDefaultUi()
|
||||
{
|
||||
var _ = Config.UiType switch
|
||||
{
|
||||
UiType.WebDefaultBrowser => OpenNewUi(),
|
||||
UiType.WebEmbedded => OpenNewUiEmbedded(),
|
||||
_ => OpenMainForm()
|
||||
};
|
||||
}
|
||||
|
||||
public static Task OpenMainForm()
|
||||
{
|
||||
_mainForm ??= ServiceProvider.GetRequiredService<MainForm>();
|
||||
_mainForm.OpenForm();
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public static bool IsMainFormOpened()
|
||||
{
|
||||
return _mainForm != null;
|
||||
}
|
||||
|
||||
public static void Exit()
|
||||
{
|
||||
UserExit = true;
|
||||
_mainForm?.Close();
|
||||
|
||||
if (AppContext.MainHandle != 0)
|
||||
{
|
||||
NativeMethods.SendMessageW(AppContext.MainHandle, NativeConstants.WM_CLOSE, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
public static void Restart()
|
||||
{
|
||||
IsRestarting = true;
|
||||
|
||||
_mainForm?.CloseForRestart();
|
||||
|
||||
_mutex?.Close();
|
||||
_mutex = null;
|
||||
|
||||
Application.Restart();
|
||||
Environment.Exit(0);
|
||||
}
|
||||
|
||||
public static async Task OpenNewUi()
|
||||
{
|
||||
if (await StartUiServer())
|
||||
{
|
||||
await Task.Delay(500);
|
||||
}
|
||||
|
||||
var winApi = AppContext.ServiceProvider.GetRequiredService<WinApiAdminService>();
|
||||
|
||||
winApi.StartProcess(GetNewUiUrl());
|
||||
}
|
||||
|
||||
public static string GetNewUiUrl()
|
||||
{
|
||||
return $"http://localhost:{(Config.UiPort > 0 ? Config.UiPort : 5000)}";
|
||||
}
|
||||
|
||||
public static async Task OpenNewUiEmbedded()
|
||||
{
|
||||
if (await StartUiServer())
|
||||
{
|
||||
await Task.Delay(500);
|
||||
}
|
||||
|
||||
BrowserWindow.CreateAndShow(GetNewUiUrl());
|
||||
}
|
||||
|
||||
private static bool UiServerStarted;
|
||||
|
||||
public static async Task<bool> StartUiServer()
|
||||
{
|
||||
if (UiServerStarted)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var backgroundService = ServiceProvider.GetRequiredService<ColorControlBackgroundService>();
|
||||
|
||||
backgroundService.PipeName = PipeUtils.MainPipe;
|
||||
|
||||
await backgroundService.StartAsync(CancellationToken.None);
|
||||
|
||||
var _ = Task.Run(() => ColorControl.UI.Blazor.Start(Config));
|
||||
UiServerStarted = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static void InitLogger(bool runAsService, bool runElevated)
|
||||
{
|
||||
var config = new LoggingConfiguration();
|
||||
|
||||
// Targets where to log to: File and Console
|
||||
var logFileAppendix = runAsService ? "_svc" : runElevated ? "_elevated" : "";
|
||||
|
||||
LogFilename = Path.Combine(DataDir, $"LogFile{logFileAppendix}.txt");
|
||||
|
||||
var logfile = new NLog.Targets.FileTarget("logfile") { FileName = LogFilename };
|
||||
|
||||
_loggingRule = new LoggingRule("*", LogLevel.Trace, LogLevel.Fatal, logfile);
|
||||
_loggingRule.RuleName = "ColorControl";
|
||||
|
||||
// Rules for mapping loggers to targets
|
||||
config.AddRule(_loggingRule);
|
||||
|
||||
// Apply config
|
||||
LogManager.Configuration = config;
|
||||
}
|
||||
|
||||
private static void LoadConfig()
|
||||
{
|
||||
ConfigFilename = Path.Combine(DataDir, "Settings.json");
|
||||
|
||||
try
|
||||
{
|
||||
if (File.Exists(ConfigFilename))
|
||||
{
|
||||
var data = File.ReadAllText(ConfigFilename);
|
||||
Config = JsonConvert.DeserializeObject<Config>(data);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Error($"LoadConfig: {ex.Message}");
|
||||
}
|
||||
Config ??= new Config();
|
||||
|
||||
var minLogLevel = LogLevel.FromString(Config.LogLevel);
|
||||
|
||||
if (minLogLevel != LogLevel.Trace)
|
||||
{
|
||||
_loggingRule.SetLoggingLevels(minLogLevel, LogLevel.Fatal);
|
||||
LogManager.ReconfigExistingLoggers();
|
||||
}
|
||||
}
|
||||
|
||||
public static IServiceProvider ServiceProvider { get; private set; }
|
||||
private static IHostBuilder CreateHostBuilder()
|
||||
{
|
||||
return Host.CreateDefaultBuilder()
|
||||
.ConfigureServices((context, services) =>
|
||||
{
|
||||
//services.AddSingleton<NvService>();
|
||||
services.RegisterSharedServices();
|
||||
services.AddSingleton<MainWorker>();
|
||||
services.AddSingleton<NotifyIconManager>();
|
||||
services.AddSingleton<LgService>();
|
||||
services.AddSingleton<GameService>();
|
||||
services.AddSingleton<SamsungService>();
|
||||
services.AddSingleton<AmdService>();
|
||||
services.AddSingleton<PowerEventDispatcher>();
|
||||
services.AddSingleton<ProcessEventDispatcher>();
|
||||
services.AddSingleton<WindowMessageDispatcher>();
|
||||
services.AddSingleton<KeyboardShortcutDispatcher>();
|
||||
services.AddSingleton<DisplayChangeEventDispatcher>();
|
||||
services.AddSingleton<MainForm>();
|
||||
services.AddSingleton<LogWindow>();
|
||||
services.AddSingleton<BrowserWindow>();
|
||||
services.AddTransient<ColorProfileWindow>();
|
||||
services.AddSingleton<RestartDetector>();
|
||||
services.AddSingleton<InfoPanel>();
|
||||
services.AddSingleton<OptionsPanel>();
|
||||
services.AddSingleton<ElevationService>();
|
||||
services.AddSingleton<UpdateManager>();
|
||||
|
||||
services.AddSingleton<OptionsService>();
|
||||
services.AddSingleton<LoggingService>();
|
||||
|
||||
services.AddSingleton<AmdPanel>();
|
||||
services.AddSingleton<NvPanel>();
|
||||
services.AddSingleton<LgPanel>();
|
||||
services.AddSingleton<SamsungPanel>();
|
||||
services.AddSingleton<GamePanel>();
|
||||
});
|
||||
}
|
||||
private static async Task RunService(string[] args)
|
||||
{
|
||||
var startTime = DateTime.Now;
|
||||
|
||||
Logger.Debug("RUNNING SERVICE");
|
||||
|
||||
AppContext = new GlobalContext(null, new StartUpParams(), DataDir, _loggingRule, null, startTime);
|
||||
|
||||
using IHost host = Host.CreateDefaultBuilder(args)
|
||||
.UseWindowsService(options =>
|
||||
{
|
||||
options.ServiceName = "ColorControl Service";
|
||||
})
|
||||
.ConfigureServices(services =>
|
||||
{
|
||||
services.RegisterSharedServices();
|
||||
services.AddSingleton<PowerEventDispatcher>();
|
||||
services.AddHostedService<ColorControlBackgroundService>();
|
||||
})
|
||||
.Build();
|
||||
|
||||
ServiceProvider = host.Services;
|
||||
AppContext.ServiceProvider = ServiceProvider;
|
||||
|
||||
await host.RunAsync();
|
||||
}
|
||||
|
||||
private static void RegisterSharedServices(this IServiceCollection services)
|
||||
{
|
||||
services.AddSingleton(AppContext);
|
||||
services.AddSingleton<SessionSwitchDispatcher>();
|
||||
services.AddSingleton<WinApiAdminService>();
|
||||
services.AddSingleton<WinApiService>();
|
||||
services.AddSingleton<WinElevatedProcessManager>();
|
||||
services.AddTransient<RpcClientService>();
|
||||
services.AddTransient<RpcServerService>();
|
||||
services.AddSingleton<NvService>();
|
||||
services.AddSingleton<WolService>();
|
||||
services.AddSingleton<ColorControlBackgroundService>();
|
||||
services.AddSingleton<ServiceManager>();
|
||||
services.AddSingleton<ColorProfileService>();
|
||||
|
||||
RpcServerService.ServiceTypes.Add(typeof(NvService));
|
||||
RpcServerService.ServiceTypes.Add(typeof(GameService));
|
||||
RpcServerService.ServiceTypes.Add(typeof(OptionsService));
|
||||
RpcServerService.ServiceTypes.Add(typeof(LoggingService));
|
||||
RpcServerService.ServiceTypes.Add(typeof(SamsungService));
|
||||
RpcServerService.ServiceTypes.Add(typeof(LgService));
|
||||
RpcServerService.ServiceTypes.Add(typeof(AmdService));
|
||||
RpcServerService.ServiceTypes.Add(typeof(ColorProfileService));
|
||||
}
|
||||
|
||||
public static int EnumThreadWindows(IntPtr handle, IntPtr param)
|
||||
{
|
||||
NativeMethods.SendMessageW(handle, Utils.WM_BRINGTOFRONT, UIntPtr.Zero, IntPtr.Zero);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
private static void GlobalUnhandledExceptionHandler(object sender, UnhandledExceptionEventArgs e)
|
||||
{
|
||||
GlobalHandleException((Exception)e.ExceptionObject, "Unhandled exception");
|
||||
}
|
||||
|
||||
private static void GlobalThreadExceptionHandler(object sender, ThreadExceptionEventArgs e)
|
||||
{
|
||||
GlobalHandleException(e.Exception, "Exception in thread");
|
||||
}
|
||||
|
||||
private static void GlobalHandleException(Exception exception, string type)
|
||||
{
|
||||
if (UserExit)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var trace = exception.ToLogString(Environment.StackTrace);
|
||||
var message = $"{type}: {trace}";
|
||||
|
||||
Logger.Error(message);
|
||||
|
||||
MessageBox.Show(message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||
}
|
||||
|
||||
internal static void SetTheme(bool useDarkMode)
|
||||
{
|
||||
DarkModeUtils.SetDarkMode(useDarkMode);
|
||||
|
||||
WinApi.RefreshImmersiveColorPolicyState();
|
||||
|
||||
DarkModeUtils.InitWpfTheme();
|
||||
|
||||
var notifyIconManager = ServiceProvider.GetRequiredService<NotifyIconManager>();
|
||||
|
||||
DarkModeUtils.SetContextMenuForeColor(notifyIconManager.NotifyIcon.ContextMenuStrip, FormUtils.CurrentForeColor);
|
||||
|
||||
_mainForm?.SetTheme(useDarkMode);
|
||||
}
|
||||
|
||||
internal static NotifyIcon GetNotifyIcon()
|
||||
{
|
||||
var notifyIconManager = ServiceProvider.GetRequiredService<NotifyIconManager>();
|
||||
|
||||
return notifyIconManager.NotifyIcon;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
[{"Name":"MR20GA","ImageFileName":"LG_MR20GA.jpg","Buttons":[{"Name":null,"Button":23,"Preset":null,"Top":32.506480223994814,"Left":16.7759},{"Name":null,"Button":24,"Preset":null,"Top":39.65128022399482,"Left":17.6379},{"Name":null,"Button":12,"Preset":null,"Top":13.376880223994814,"Left":19.3621},{"Name":null,"Button":13,"Preset":null,"Top":13.376880223994814,"Left":41.7759},{"Name":null,"Button":22,"Preset":null,"Top":7.038780223994813,"Left":22.8103},{"Name":null,"Button":14,"Preset":null,"Top":13.376880223994814,"Left":64.6207},{"Name":null,"Button":15,"Preset":null,"Top":17.986380223994814,"Left":19.3621},{"Name":null,"Button":16,"Preset":null,"Top":17.986380223994814,"Left":41.7759},{"Name":null,"Button":17,"Preset":null,"Top":17.986380223994814,"Left":65.0517},{"Name":null,"Button":18,"Preset":null,"Top":22.365480223994815,"Left":19.3621},{"Name":null,"Button":19,"Preset":null,"Top":22.365480223994815,"Left":41.7759},{"Name":null,"Button":20,"Preset":null,"Top":22.365480223994815,"Left":65.0517},{"Name":null,"Button":21,"Preset":null,"Top":26.629280223994815,"Left":41.7759},{"Name":null,"Button":32,"Preset":null,"Top":26.744580223994816,"Left":64.6207},{"Name":null,"Button":25,"Preset":null,"Top":32.39128022399481,"Left":42.6379},{"Name":null,"Button":0,"Preset":null,"Top":44.49128022399481,"Left":12.465499999999999},{"Name":null,"Button":26,"Preset":null,"Top":44.49128022399481,"Left":72.3793},{"Name":null,"Button":29,"Preset":null,"Top":32.506480223994814,"Left":67.2069},{"Name":null,"Button":30,"Preset":null,"Top":39.42078022399482,"Left":67.6379},{"Name":null,"Button":4,"Preset":null,"Top":48.17888022399482,"Left":42.2069},{"Name":null,"Button":5,"Preset":null,"Top":61.89228022399482,"Left":43.069},{"Name":null,"Button":6,"Preset":null,"Top":55.093180223994814,"Left":17.2069},{"Name":null,"Button":7,"Preset":null,"Top":55.20848022399482,"Left":68.931},{"Name":null,"Button":2,"Preset":null,"Top":54.97798022399481,"Left":43.069},{"Name":null,"Button":1,"Preset":null,"Top":65.0036802239948,"Left":12.8966},{"Name":null,"Button":40,"Preset":null,"Top":64.7732802239948,"Left":72.8103},{"Name":null,"Button":42,"Preset":null,"Top":70.1894802239948,"Left":15.482800000000001},{"Name":null,"Button":41,"Preset":null,"Top":69.95898022399481,"Left":71.5172},{"Name":null,"Button":35,"Preset":null,"Top":79.6389802239948,"Left":44.3621},{"Name":null,"Button":31,"Preset":null,"Top":26.744580223994816,"Left":18.931},{"Name":null,"Button":36,"Preset":null,"Top":79.86948022399481,"Left":66.3448},{"Name":null,"Button":8,"Preset":null,"Top":75.2599802239948,"Left":15.0517},{"Name":null,"Button":9,"Preset":null,"Top":74.7989802239948,"Left":33.5862},{"Name":null,"Button":10,"Preset":null,"Top":75.14468022399481,"Left":52.9828},{"Name":null,"Button":11,"Preset":null,"Top":75.02948022399481,"Left":71.0862}]},{"Name":"MR23GN","ImageFileName":"LG_MR23GN.jpg","Buttons":[{"Name":null,"Button":22,"Preset":null,"Top":3.2806882676774514,"Left":30.569000000000003},{"Name":null,"Button":12,"Preset":null,"Top":8.671588267677452,"Left":16.3448},{"Name":null,"Button":13,"Preset":null,"Top":8.671588267677452,"Left":43.931},{"Name":null,"Button":14,"Preset":null,"Top":8.671588267677452,"Left":71.0862},{"Name":null,"Button":15,"Preset":null,"Top":13.581088267677451,"Left":16.3448},{"Name":null,"Button":16,"Preset":null,"Top":13.773588267677452,"Left":43.931},{"Name":null,"Button":17,"Preset":null,"Top":13.869888267677451,"Left":71.5172},{"Name":null,"Button":18,"Preset":null,"Top":18.77938826767745,"Left":16.3448},{"Name":null,"Button":19,"Preset":null,"Top":18.68318826767745,"Left":43.931},{"Name":null,"Button":20,"Preset":null,"Top":18.68318826767745,"Left":71.0862},{"Name":null,"Button":21,"Preset":null,"Top":23.88148826767745,"Left":43.931},{"Name":null,"Button":40,"Preset":null,"Top":23.78518826767745,"Left":16.3448},{"Name":null,"Button":32,"Preset":null,"Top":24.073988267677453,"Left":70.6552},{"Name":null,"Button":23,"Preset":null,"Top":29.272288267677453,"Left":15.482800000000001},{"Name":null,"Button":24,"Preset":null,"Top":37.069788267677446,"Left":15.913799999999998},{"Name":null,"Button":25,"Preset":null,"Top":33.02668826767745,"Left":43.069},{"Name":null,"Button":29,"Preset":null,"Top":29.368588267677453,"Left":71.0862},{"Name":null,"Button":24,"Preset":null,"Top":37.16608826767745,"Left":71.0862},{"Name":null,"Button":0,"Preset":null,"Top":42.171888267677446,"Left":15.482800000000001},{"Name":null,"Button":2,"Preset":null,"Top":51.894688267677445,"Left":43.069},{"Name":null,"Button":6,"Preset":null,"Top":51.50958826767745,"Left":15.0517},{"Name":null,"Button":7,"Preset":null,"Top":51.894688267677445,"Left":70.2241},{"Name":null,"Button":4,"Preset":null,"Top":45.15608826767745,"Left":42.6379},{"Name":null,"Button":5,"Preset":null,"Top":57.86308826767745,"Left":42.6379},{"Name":null,"Button":26,"Preset":null,"Top":60.94358826767745,"Left":71.0862},{"Name":null,"Button":1,"Preset":null,"Top":60.94358826767745,"Left":15.051Line truncated
|
||||
@@ -0,0 +1 @@
|
||||
[{"Name":"DefaultRC","ImageFileName":"Samsung_BN59-01315B.jpg","Buttons":[{"Name":null,"Button":26,"Preset":null,"Top":10.623333333333333,"Left":16.0},{"Name":null,"Button":13,"Preset":null,"Top":4.25,"Left":66.2604},{"Name":null,"Button":27,"Preset":null,"Top":10.623333333333333,"Left":42.2249},{"Name":null,"Button":28,"Preset":null,"Top":10.623333333333333,"Left":66.2604},{"Name":null,"Button":0,"Preset":null,"Top":4.373333333333333,"Left":16.0},{"Name":null,"Button":22,"Preset":null,"Top":35.456633333333336,"Left":16.4852},{"Name":null,"Button":23,"Preset":null,"Top":43.873333333333335,"Left":16.7811},{"Name":null,"Button":24,"Preset":null,"Top":34.790033333333334,"Left":41.3373},{"Name":null,"Button":12,"Preset":null,"Top":79.87333333333333,"Left":11.1598},{"Name":null,"Button":8,"Preset":null,"Top":69.70663333333333,"Left":14.414200000000001},{"Name":null,"Button":7,"Preset":null,"Top":63.20663333333333,"Left":41.3373},{"Name":null,"Button":10,"Preset":null,"Top":49.540033333333334,"Left":41.3373},{"Name":null,"Button":9,"Preset":null,"Top":69.70663333333333,"Left":66.1893},{"Name":null,"Button":14,"Preset":null,"Top":56.206633333333336,"Left":65.5976},{"Name":null,"Button":16,"Preset":null,"Top":79.87333333333333,"Left":31.278100000000002},{"Name":null,"Button":1,"Preset":null,"Top":56.623333333333335,"Left":41.3373},{"Name":null,"Button":3,"Preset":null,"Top":63.20663333333333,"Left":15.3018},{"Name":null,"Button":4,"Preset":null,"Top":63.20663333333333,"Left":67.3728},{"Name":null,"Button":2,"Preset":null,"Top":69.70663333333333,"Left":41.3373},{"Name":null,"Button":29,"Preset":null,"Top":15.841666666666667,"Left":16.0},{"Name":null,"Button":30,"Preset":null,"Top":15.841666666666667,"Left":40.929},{"Name":null,"Button":31,"Preset":null,"Top":15.841666666666667,"Left":66.2604},{"Name":null,"Button":32,"Preset":null,"Top":21.508366666666664,"Left":16.0},{"Name":null,"Button":33,"Preset":null,"Top":21.508366666666664,"Left":41.3373},{"Name":null,"Button":34,"Preset":null,"Top":21.508366666666664,"Left":66.2604},{"Name":null,"Button":25,"Preset":null,"Top":27.008366666666664,"Left":41.3373},{"Name":null,"Button":17,"Preset":null,"Top":75.09166666666667,"Left":12.526599999999998},{"Name":null,"Button":18,"Preset":null,"Top":75.09166666666667,"Left":30.573999999999998},{"Name":null,"Button":19,"Preset":null,"Top":75.09166666666667,"Left":50.1006},{"Name":null,"Button":20,"Preset":null,"Top":75.09166666666667,"Left":68.1479},{"Name":null,"Button":11,"Preset":null,"Top":42.041965520561135,"Left":40.7273}]},{"Name":"ServiceRC","ImageFileName":"Samsung_AA81-00243A.jpg","Buttons":[{"Name":null,"Button":16,"Preset":null,"Top":71.10894129883307,"Left":22.3828},{"Name":null,"Button":45,"Preset":null,"Top":35.282841298833084,"Left":15.2984},{"Name":null,"Button":0,"Preset":null,"Top":7.5743412988330805,"Left":20.748},{"Name":null,"Button":13,"Preset":null,"Top":7.79084129883308,"Left":67.8869},{"Name":null,"Button":15,"Preset":null,"Top":17.965041298833082,"Left":73.3365},{"Name":null,"Button":9,"Preset":null,"Top":35.39104129883308,"Left":74.9714},{"Name":null,"Button":7,"Preset":null,"Top":27.05684129883308,"Left":44.4537},{"Name":null,"Button":1,"Preset":null,"Top":19.480341298833082,"Left":44.9986},{"Name":null,"Button":3,"Preset":null,"Top":26.94864129883308,"Left":25.6526},{"Name":null,"Button":4,"Preset":null,"Top":26.94864129883308,"Left":64.3447},{"Name":null,"Button":2,"Preset":null,"Top":34.41694129883308,"Left":45.5436},{"Name":null,"Button":12,"Preset":null,"Top":17.423841298833082,"Left":15.8433},{"Name":null,"Button":26,"Preset":null,"Top":51.30174129883308,"Left":22.3828},{"Name":null,"Button":27,"Preset":null,"Top":50.97704129883308,"Left":46.361},{"Name":null,"Button":28,"Preset":null,"Top":51.08524129883308,"Left":68.9768},{"Name":null,"Button":29,"Preset":null,"Top":57.90414129883308,"Left":22.6553},{"Name":null,"Button":30,"Preset":null,"Top":57.68764129883308,"Left":45.8161},{"Name":null,"Button":31,"Preset":null,"Top":57.79594129883308,"Left":68.7044},{"Name":null,"Button":32,"Preset":null,"Top":64.61474129883307,"Left":22.1104},{"Name":null,"Button":33,"Preset":null,"Top":64.50654129883307,"Left":45.2711},{"Name":null,"Button":34,"Preset":null,"Top":64.29004129883307,"Left":68.9768},{"Name":null,"Button":25,"Preset":null,"Top":70.89244129883308,"Left":45.8161}]}]
|
||||
@@ -307,6 +307,7 @@ public class ColorProfileService
|
||||
ColorProfiles = colorProfiles,
|
||||
DefaultSdrColorProfile = GetDisplayDefaultColorProfile(d.DisplayName, false),
|
||||
DefaultHdrColorProfile = GetDisplayDefaultColorProfile(d.DisplayName, true),
|
||||
UsePerUserSettings = CCD.GetUsePerUserDisplayProfiles(d.DisplayName)
|
||||
};
|
||||
})
|
||||
.Where(d => !onlyHdrEnabled.HasValue || onlyHdrEnabled == d.IsHdrSupportedAndEnabled)
|
||||
|
||||
@@ -168,7 +168,7 @@ public class NotifyIconManager
|
||||
_lgTrayMenu.Visible = _serviceManager.LgService != null;
|
||||
if (_lgTrayMenu.Visible)
|
||||
{
|
||||
var presets = _serviceManager.LgService.GetPresets().Where(x => !string.IsNullOrEmpty(x.appId) || x.steps.Any());
|
||||
var presets = _serviceManager.LgService.GetPresets().Where(x => !string.IsNullOrEmpty(x.appId) || x.Steps.Any());
|
||||
|
||||
_lgTrayMenu.DropDownItems.Clear();
|
||||
|
||||
|
||||
@@ -245,6 +245,7 @@ namespace ColorControl.Services.Common
|
||||
|
||||
// Hack to convert incorrect triggers
|
||||
json = json.Replace(@"""Triggers"":0", @"""Triggers"":[]");
|
||||
json = json.Replace(@"""steps"":[", @"""Steps"":[");
|
||||
|
||||
_presets = JsonConvert.DeserializeObject<List<T>>(json, _jsonConverters.ToArray());
|
||||
|
||||
@@ -281,10 +282,7 @@ namespace ColorControl.Services.Common
|
||||
Logger.Error($"General error while loading presets, reverting to empty list: {ex.Message}");
|
||||
_loadPresetsError = ex.Message;
|
||||
}
|
||||
if (_presets == null)
|
||||
{
|
||||
_presets = new List<T>();
|
||||
}
|
||||
_presets ??= [];
|
||||
|
||||
AfterPresetsLoaded();
|
||||
}
|
||||
|
||||
@@ -89,7 +89,9 @@ namespace ColorControl.Services.GameLauncher
|
||||
|
||||
private Task<bool> HandleStartedProcess(Process process)
|
||||
{
|
||||
GamePreset preset;
|
||||
GamePreset preset = null;
|
||||
|
||||
var presets = _presets.Where(p => p.AutoSettings.AllowAutoApply);
|
||||
|
||||
if (_winApiService.IsAdministrator())
|
||||
{
|
||||
@@ -102,15 +104,16 @@ namespace ColorControl.Services.GameLauncher
|
||||
|
||||
var fileName = mainModule.FileName;
|
||||
|
||||
preset = _presets.FirstOrDefault(p => fileName.Contains(Path.GetDirectoryName(p.Path), System.StringComparison.OrdinalIgnoreCase));
|
||||
}
|
||||
else
|
||||
{
|
||||
var name = process.ProcessName.Replace("-Win64-Shipping", "");
|
||||
preset = _presets.FirstOrDefault(p => Path.GetFileName(p.Path).Contains(name, System.StringComparison.OrdinalIgnoreCase));
|
||||
preset = presets.FirstOrDefault(p => !Path.GetDirectoryName(p.Path).Equals("") && fileName.Contains(Path.GetDirectoryName(p.Path), System.StringComparison.OrdinalIgnoreCase));
|
||||
}
|
||||
|
||||
if (preset == null || !preset.AutoSettings.AllowAutoApply)
|
||||
if (preset == null)
|
||||
{
|
||||
var name = process.ProcessName.Replace("-Win64-Shipping", "");
|
||||
preset = presets.FirstOrDefault(p => Path.GetFileName(p.Path).Contains(name, System.StringComparison.OrdinalIgnoreCase));
|
||||
}
|
||||
|
||||
if (preset == null)
|
||||
{
|
||||
return Task.FromResult(false);
|
||||
}
|
||||
|
||||
@@ -275,10 +275,10 @@ namespace ColorControl.Services.LG
|
||||
AddInvokableAction("turnScreenOff", TurnScreenOffAction);
|
||||
AddInvokableAction("turnScreenOn", TurnScreenOnAction);
|
||||
|
||||
AddInternalPresetAction(new LgPreset("InStart", "com.webos.app.factorywin", new[] { "0", "4", "1", "3" }, new { id = "executeFactory", irKey = "inStart" }));
|
||||
AddInternalPresetAction(new LgPreset("EzAdjust", "com.webos.app.factorywin", new[] { "0", "4", "1", "3" }, new { id = "executeFactory", irKey = "ezAdjust" }));
|
||||
AddInternalPresetAction(LgPresets.InStartPreset);
|
||||
AddInternalPresetAction(LgPresets.EzAdjustPreset);
|
||||
//AddInternalPresetAction(new LgPreset("PictureCheck", "com.webos.app.factorywin", null, new { id = "executeFactory", irKey = "pCheck" }));
|
||||
AddInternalPresetAction(new LgPreset("Software Update", "com.webos.app.softwareupdate", null, new { mode = "user", flagUpdate = true }));
|
||||
AddInternalPresetAction(LgPresets.SoftwareUpdatePreset);
|
||||
|
||||
AddSetDeviceConfigAction("HDMI_1_icon", typeof(HdmiIcon), "HDMI 1 icon");
|
||||
AddSetDeviceConfigAction("HDMI_2_icon", typeof(HdmiIcon), "HDMI 2 icon");
|
||||
@@ -707,7 +707,7 @@ namespace ColorControl.Services.LG
|
||||
{
|
||||
var hasApp = !string.IsNullOrEmpty(preset.appId);
|
||||
|
||||
var hasWOL = preset.steps.Any(s => s.Equals("WOL", StringComparison.OrdinalIgnoreCase));
|
||||
var hasWOL = preset.Steps.Any(s => s.Equals("WOL", StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
if (hasWOL)
|
||||
{
|
||||
@@ -770,7 +770,7 @@ namespace ColorControl.Services.LG
|
||||
}
|
||||
}
|
||||
|
||||
if (preset.steps.Any())
|
||||
if (preset.Steps.Any())
|
||||
{
|
||||
if (hasApp)
|
||||
{
|
||||
@@ -803,7 +803,7 @@ namespace ColorControl.Services.LG
|
||||
{
|
||||
LgWebOsMouseService mouse = null;
|
||||
|
||||
foreach (var step in preset.steps)
|
||||
foreach (var step in preset.Steps)
|
||||
{
|
||||
var keySpec = step.Split(':');
|
||||
|
||||
|
||||
@@ -184,7 +184,7 @@ namespace ColorControl.Services.LG
|
||||
var lgApps = _lgService?.GetApps();
|
||||
cbxLgApps.SelectedIndex = lgApps == null ? -1 : lgApps.FindIndex(x => x.appId.Equals(preset.appId));
|
||||
edtShortcutLg.Text = preset.shortcut;
|
||||
edtStepsLg.Text = preset.steps.Aggregate("", (a, b) => (string.IsNullOrEmpty(a) ? "" : a + ", ") + b);
|
||||
edtStepsLg.Text = preset.Steps.Aggregate("", (a, b) => (string.IsNullOrEmpty(a) ? "" : a + ", ") + b);
|
||||
|
||||
var index = -1;
|
||||
|
||||
@@ -291,7 +291,7 @@ namespace ColorControl.Services.LG
|
||||
|
||||
var text = edtStepsLg.Text;
|
||||
|
||||
Utils.ParseWords(preset.steps, text);
|
||||
Utils.ParseWords(preset.Steps, text);
|
||||
|
||||
AddOrUpdateItemLg();
|
||||
|
||||
|
||||
@@ -26,7 +26,8 @@ namespace ColorControl.Services.LG
|
||||
{
|
||||
protected static readonly NLog.Logger Logger = NLog.LogManager.GetCurrentClassLogger();
|
||||
|
||||
public static string LgListAppsJson = "listApps.json";
|
||||
public const string LgListAppsJson = "listApps.json";
|
||||
public const string RemotesFilename = "LgRemotes.json";
|
||||
|
||||
public override string ServiceName => "LG";
|
||||
|
||||
@@ -67,6 +68,9 @@ namespace ColorControl.Services.LG
|
||||
private readonly WinApiAdminService _winApiAdminService;
|
||||
private LgGameBar _gameBarForm;
|
||||
|
||||
private List<LgRcDto> _remotes;
|
||||
private string _remotesFilename;
|
||||
|
||||
public ProcessChangedEventArgs CurrentProcessEvent => _processEventDispatcher?.MonitorContext;
|
||||
|
||||
public static readonly int SHORTCUTID_LGQA = -202;
|
||||
@@ -99,6 +103,75 @@ namespace ColorControl.Services.LG
|
||||
LoadConfig();
|
||||
LoadPresets();
|
||||
LoadRemoteControlButtons();
|
||||
LoadRemotes();
|
||||
}
|
||||
|
||||
private void LoadRemotes()
|
||||
{
|
||||
_remotesFilename = Path.Combine(_dataPath, RemotesFilename);
|
||||
|
||||
try
|
||||
{
|
||||
var remotesExists = File.Exists(_remotesFilename);
|
||||
if (remotesExists)
|
||||
{
|
||||
try
|
||||
{
|
||||
var json = File.ReadAllText(_remotesFilename);
|
||||
|
||||
_remotes = JsonConvert.DeserializeObject<List<LgRcDto>>(json);
|
||||
}
|
||||
catch (Exception ex1)
|
||||
{
|
||||
Logger.Error($"Error while loading remotes, reverting to default remotes: {ex1.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Error($"General error while loading remotes, reverting to empty list: {ex.Message}");
|
||||
}
|
||||
|
||||
_remotes ??= [];
|
||||
|
||||
var defaultRemotes = GetDefaultRemotes();
|
||||
|
||||
_remotes.AddRange(defaultRemotes.Where(r => !_remotes.Any(sr => sr.Name == r.Name)));
|
||||
}
|
||||
|
||||
protected static List<LgRcDto> GetDefaultRemotes()
|
||||
{
|
||||
var json = Utils.GetResourceFile("LgRemotes.json");
|
||||
|
||||
return JsonConvert.DeserializeObject<List<LgRcDto>>(json);
|
||||
}
|
||||
|
||||
public List<LgRcDto> GetRemotes()
|
||||
{
|
||||
return _remotes;
|
||||
}
|
||||
|
||||
public bool UpdateRemote(LgRcDto remoteSpec)
|
||||
{
|
||||
var remote = _remotes.FirstOrDefault(r => r.Name == remoteSpec.Name);
|
||||
|
||||
if (remote != null)
|
||||
{
|
||||
remote.Update(remoteSpec);
|
||||
}
|
||||
else
|
||||
{
|
||||
_remotes.Add(remoteSpec);
|
||||
}
|
||||
|
||||
GlobalSave();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void DeleteRemote(string remoteName)
|
||||
{
|
||||
_remotes.RemoveAll(r => r.Name == remoteName);
|
||||
}
|
||||
|
||||
private string LgPreset_GetAppName(string appId)
|
||||
@@ -175,6 +248,7 @@ namespace ColorControl.Services.LG
|
||||
SaveConfig();
|
||||
SavePresets();
|
||||
SaveRemoteControlButtons();
|
||||
Utils.WriteObject(_remotesFilename, _remotes);
|
||||
|
||||
SendConfigToService();
|
||||
}
|
||||
@@ -297,7 +371,7 @@ namespace ColorControl.Services.LG
|
||||
preset.shortcut = shortcut;
|
||||
if (appId == null)
|
||||
{
|
||||
preset.steps.Add(step == null ? name : step);
|
||||
preset.Steps.Add(step == null ? name : step);
|
||||
}
|
||||
|
||||
return preset;
|
||||
@@ -323,7 +397,7 @@ namespace ColorControl.Services.LG
|
||||
{
|
||||
var customIpAddresses = Devices.Where(d => d.IsCustom).Select(d => d.IpAddress);
|
||||
|
||||
var pnpDevices = await Utils.GetPnpDevices(Config.DeviceSearchKey);
|
||||
var pnpDevices = Utils.GetPnpDevices(Config.DeviceSearchKey);
|
||||
|
||||
var autoDevices = pnpDevices.Where(p => !customIpAddresses.Contains(p.IpAddress)).Select(d => new LgDevice(d.Name, d.IpAddress, d.MacAddress, false)).ToList();
|
||||
var autoIpAddresses = pnpDevices.Select(d => d.IpAddress);
|
||||
@@ -370,13 +444,11 @@ namespace ColorControl.Services.LG
|
||||
}
|
||||
else
|
||||
{
|
||||
if (afterStartUp)
|
||||
var connectTask = SelectedDevice.Connect(afterStartUp ? 3 : 1);
|
||||
|
||||
if (!afterStartUp)
|
||||
{
|
||||
var _ = SelectedDevice.Connect();
|
||||
}
|
||||
else
|
||||
{
|
||||
return await SelectedDevice.Connect(1);
|
||||
return await connectTask;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1267,9 +1339,28 @@ namespace ColorControl.Services.LG
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool RemoveDevice(LgDeviceDto deviceDto)
|
||||
{
|
||||
if (deviceDto == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var device = MatchDevice(deviceDto);
|
||||
if (device?.IsCustom != true)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
RemoveCustomDevice(device);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private LgDevice MatchDevice(LgDeviceDto deviceDto)
|
||||
{
|
||||
return Devices?.FirstOrDefault(d => string.IsNullOrEmpty(deviceDto.MacAddress) ? d.IpAddress == deviceDto.IpAddress : d.MacAddress == deviceDto.MacAddress);
|
||||
return Devices?.FirstOrDefault(d => string.IsNullOrEmpty(deviceDto.MacAddress) ? d.IpAddress == deviceDto.IpAddress : d.MacAddress == deviceDto.MacAddress && d.IpAddress == deviceDto.IpAddress) ??
|
||||
Devices?.FirstOrDefault(d => string.IsNullOrEmpty(deviceDto.MacAddress) ? d.IpAddress == deviceDto.IpAddress : d.MacAddress == deviceDto.MacAddress);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -69,7 +69,7 @@ namespace ColorControl.Services.LG
|
||||
|
||||
if (preset != null)
|
||||
{
|
||||
if (preset.steps.Contains("POWER") && _lgService.SelectedDevice?.CurrentState != LgDevice.PowerState.Active)
|
||||
if (preset.Steps.Contains("POWER") && _lgService.SelectedDevice?.CurrentState != LgDevice.PowerState.Active)
|
||||
{
|
||||
Logger.Debug("Executing WOL instead of POWER");
|
||||
|
||||
|
||||
@@ -31,6 +31,7 @@ using System.Security.AccessControl;
|
||||
using System.Security.Principal;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using static ColorControl.Shared.Common.NestedItemsBuilder;
|
||||
using static ColorControl.Shared.Contracts.NVIDIA.NvHdrSettings;
|
||||
|
||||
namespace ColorControl.Services.NVIDIA
|
||||
@@ -174,10 +175,10 @@ namespace ColorControl.Services.NVIDIA
|
||||
MainWindow.CreateAndShow(false);
|
||||
}
|
||||
|
||||
// TODO: implement later
|
||||
//_powerEventDispatcher.RegisterEventHandler(PowerEventDispatcher.Event_Suspend, PowerModeChanged);
|
||||
//_powerEventDispatcher.RegisterAsyncEventHandler(PowerEventDispatcher.Event_Resume, PowerModeResume);
|
||||
//_powerEventDispatcher.RegisterEventHandler(PowerEventDispatcher.Event_Shutdown, PowerModeChanged);
|
||||
_powerEventDispatcher.RegisterEventHandler(PowerEventDispatcher.Event_Suspend, PowerModeChanged);
|
||||
_powerEventDispatcher.RegisterAsyncEventHandler(PowerEventDispatcher.Event_Resume, PowerModeResume);
|
||||
_powerEventDispatcher.RegisterEventHandler(PowerEventDispatcher.Event_Startup, PowerModeChanged);
|
||||
_powerEventDispatcher.RegisterEventHandler(PowerEventDispatcher.Event_Shutdown, PowerModeChanged);
|
||||
}
|
||||
|
||||
public override List<string> GetInfo()
|
||||
@@ -421,6 +422,11 @@ namespace ColorControl.Services.NVIDIA
|
||||
}
|
||||
}
|
||||
|
||||
if (preset.DpiScaling.ApplyScaling)
|
||||
{
|
||||
CCD.SetDpiScaling(preset.DpiScaling.Percentage, display.Name);
|
||||
}
|
||||
|
||||
if (preset.applyOther)
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(preset.ColorProfileSettings.ProfileName))
|
||||
@@ -468,6 +474,8 @@ namespace ColorControl.Services.NVIDIA
|
||||
result = ApplyOverclocking(preset.ocSettings);
|
||||
}
|
||||
|
||||
await ExecuteStepsAsync(preset, _globalContext);
|
||||
|
||||
//SetMonitorScaling();
|
||||
//CCD.SetScaling(100);
|
||||
|
||||
@@ -1442,6 +1450,8 @@ namespace ColorControl.Services.NVIDIA
|
||||
{
|
||||
var list = new List<NvPreset>();
|
||||
|
||||
RefreshProfileSettings();
|
||||
|
||||
var displayInfos = GetSimpleDisplayInfos();
|
||||
|
||||
foreach (var displayInfo in displayInfos)
|
||||
@@ -1493,6 +1503,12 @@ namespace ColorControl.Services.NVIDIA
|
||||
preset.ColorProfileSettings.ProfileName = CCD.GetDisplayDefaultColorProfile(display.Name, preset.HDREnabled);
|
||||
|
||||
preset.DisplayConfig = CCD.GetDisplayConfig(display.Name);
|
||||
preset.DpiScaling.Percentage = CCD.GetDpiScalingInPercentage(display.Name);
|
||||
|
||||
var novideoSettings = MainWindow.GetSettings(preset.DisplayId);
|
||||
|
||||
preset.NovideoSettings.ApplyClamp = novideoSettings.clamped;
|
||||
preset.NovideoSettings.ColorSpace = (NovideoColorSpace)novideoSettings.targetColorSpace;
|
||||
|
||||
var ditherInfo = GetDithering(display);
|
||||
|
||||
@@ -1590,6 +1606,7 @@ namespace ColorControl.Services.NVIDIA
|
||||
|
||||
return new NvSettingItemDto
|
||||
{
|
||||
Name = settingMeta.SettingName,
|
||||
GroupName = s.GroupName,
|
||||
IsApiExposed = s.IsApiExposed,
|
||||
IsSettingHidden = s.IsSettingHidden,
|
||||
@@ -1608,40 +1625,39 @@ namespace ColorControl.Services.NVIDIA
|
||||
}).ToList();
|
||||
}
|
||||
|
||||
public List<string> GetDriverProfileNames(bool onlyFoundPrograms = false)
|
||||
public List<NvProfile> GetDriverProfileNames(bool onlyFoundPrograms = false)
|
||||
{
|
||||
if (onlyFoundPrograms)
|
||||
{
|
||||
var profileNames = new List<string>();
|
||||
var profiles = new List<NvProfile>();
|
||||
|
||||
// First check game service for programs
|
||||
profileNames.AddRange(GetProfileNamesOfGamePresets());
|
||||
profiles.AddRange(GetProfileNamesOfGamePresets());
|
||||
|
||||
// Other ways: check MRU in registry:
|
||||
// - Computer\HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\ComDlg32\OpenSavePidlMRU\exe
|
||||
// - Computer\HKEY_CLASSES_ROOT\Local Settings\Software\Microsoft\Windows\Shell\MuiCache
|
||||
profileNames.AddRange(GetProfileNamesOfMru());
|
||||
profiles.AddRange(GetProfileNamesOfMru());
|
||||
|
||||
profileNames = profileNames.Distinct().ToList();
|
||||
profileNames.Sort();
|
||||
profiles = profiles.DistinctBy(p => p.Name).OrderBy(p => p.Name).ToList();
|
||||
|
||||
profileNames.Insert(0, _baseProfileName);
|
||||
profiles.Insert(0, new NvProfile { IsBase = true, Name = _baseProfileName });
|
||||
|
||||
return profileNames;
|
||||
return profiles;
|
||||
}
|
||||
|
||||
var baseProfileName = "";
|
||||
|
||||
var allProfiles = DrsServiceLocator.SettingService.GetProfileNames(ref baseProfileName).Where(s => !s.StartsWith("0x")).OrderBy(s => s).ToList();
|
||||
|
||||
return allProfiles;
|
||||
return allProfiles.Select(p => new NvProfile { Name = p }).ToList();
|
||||
}
|
||||
|
||||
private List<string> GetProfileNamesOfGamePresets()
|
||||
private List<NvProfile> GetProfileNamesOfGamePresets()
|
||||
{
|
||||
if (_serviceManager.GameService == null)
|
||||
{
|
||||
return new List<string>();
|
||||
return [];
|
||||
}
|
||||
|
||||
var gamePresets = _serviceManager.GameService.GetPresets();
|
||||
@@ -1652,11 +1668,11 @@ namespace ColorControl.Services.NVIDIA
|
||||
return profiles;
|
||||
}
|
||||
|
||||
private List<string> GetProfileNamesOfMru()
|
||||
private List<NvProfile> GetProfileNamesOfMru()
|
||||
{
|
||||
if (_serviceManager.GameService == null)
|
||||
{
|
||||
return new List<string>();
|
||||
return [];
|
||||
}
|
||||
|
||||
var apps = _serviceManager.GameService.GetMruApps();
|
||||
@@ -1853,6 +1869,21 @@ namespace ColorControl.Services.NVIDIA
|
||||
return true;
|
||||
}
|
||||
|
||||
public List<NestedItem> GetInfoItems()
|
||||
{
|
||||
var items = new List<NestedItem>();
|
||||
|
||||
var displays = Display.GetDisplays();
|
||||
for (var i = 0; i < displays.Length; i++)
|
||||
{
|
||||
var display = displays[i];
|
||||
var item = NestedItemsBuilder.CreateTree(display, $"Display[{i}]");
|
||||
items.Add(item);
|
||||
}
|
||||
|
||||
return items;
|
||||
}
|
||||
|
||||
internal Display ResolveDisplay(NvPreset preset)
|
||||
{
|
||||
if (preset.Display == null)
|
||||
@@ -1868,7 +1899,7 @@ namespace ColorControl.Services.NVIDIA
|
||||
Logger.Debug($"PowerModeChanged: {e.State}");
|
||||
|
||||
// Wait
|
||||
await Task.Delay(30000);
|
||||
await Task.Delay(5000);
|
||||
|
||||
await ExecutePresetsForEvent(PresetTriggerType.Resume);
|
||||
}
|
||||
@@ -1884,6 +1915,11 @@ namespace ColorControl.Services.NVIDIA
|
||||
_ = ExecutePresetsForEvent(PresetTriggerType.Standby);
|
||||
break;
|
||||
}
|
||||
case PowerOnOffState.StartUp:
|
||||
{
|
||||
_ = ExecutePresetsForEvent(PresetTriggerType.Startup);
|
||||
break;
|
||||
}
|
||||
case PowerOnOffState.ShutDown:
|
||||
{
|
||||
_ = ExecutePresetsForEvent(PresetTriggerType.Shutdown);
|
||||
@@ -1937,5 +1973,42 @@ namespace ColorControl.Services.NVIDIA
|
||||
return string.Join(useNewLines ? "\r\n" : ", ", values);
|
||||
}
|
||||
|
||||
private async Task ExecuteStepsAsync(NvPreset preset, Shared.Common.GlobalContext appContext)
|
||||
{
|
||||
foreach (var step in preset.Steps)
|
||||
{
|
||||
var keySpec = step.Split(':');
|
||||
|
||||
var delay = 100;
|
||||
var key = step;
|
||||
if (keySpec.Length >= 2)
|
||||
{
|
||||
delay = Utils.ParseInt(keySpec[1]);
|
||||
if (delay > 0)
|
||||
{
|
||||
key = keySpec[0];
|
||||
}
|
||||
}
|
||||
|
||||
var index = key.IndexOf("(");
|
||||
string[] parameters = null;
|
||||
if (index > -1)
|
||||
{
|
||||
var keyValue = key.Split('(');
|
||||
key = keyValue[0];
|
||||
parameters = keyValue[1].Substring(0, keyValue[1].Length - 1).Split(';');
|
||||
}
|
||||
|
||||
if (parameters != null)
|
||||
{
|
||||
await _serviceManager.HandleExternalServiceAsync(key, parameters);
|
||||
}
|
||||
|
||||
if (delay > 0)
|
||||
{
|
||||
await Task.Delay(delay);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,9 @@
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.Net.Security;
|
||||
using System;
|
||||
using System.Net.WebSockets;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace ColorControl.Services.Samsung
|
||||
{
|
||||
@@ -47,7 +45,10 @@ namespace ColorControl.Services.Samsung
|
||||
{
|
||||
Logger.Debug($"Connecting to {uri}");
|
||||
|
||||
await _clientWebSocket.ConnectAsync(uri, CancellationToken.None).WaitAsync(TimeSpan.FromMilliseconds(connectTimeout));
|
||||
var source = new CancellationTokenSource();
|
||||
source.CancelAfter(connectTimeout);
|
||||
|
||||
await _clientWebSocket.ConnectAsync(uri, source.Token);
|
||||
|
||||
Logger.Debug($"Connected to {uri}");
|
||||
ConnectionClosed = false;
|
||||
@@ -74,11 +75,9 @@ namespace ColorControl.Services.Samsung
|
||||
private void CreateWebSocket()
|
||||
{
|
||||
_clientWebSocket = new ClientWebSocket();
|
||||
_clientWebSocket.Options.RemoteCertificateValidationCallback += CertificationValidationCallback;
|
||||
_clientWebSocket.Options.RemoteCertificateValidationCallback += (_, _, _, _) => true;
|
||||
}
|
||||
|
||||
private bool CertificationValidationCallback(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors policyErrors) => true;
|
||||
|
||||
public async Task SendMessageAsync(string message, bool reconnect = true)
|
||||
{
|
||||
try
|
||||
|
||||
@@ -44,8 +44,13 @@ namespace ColorControl.Services.Samsung
|
||||
private List<SamsungApp> _samsungApps = new List<SamsungApp>();
|
||||
private Shared.Common.GlobalContext _appContext;
|
||||
|
||||
public const string RemotesFilename = "SamsungRemotes.json";
|
||||
|
||||
public List<SamsungDevice> Devices { get; private set; }
|
||||
|
||||
private List<SamsungRcDto> _samsungRemotes;
|
||||
private string _remotesFilename;
|
||||
|
||||
public static readonly int SHORTCUTID_SAMSUNGQA = -204;
|
||||
|
||||
public SamsungService(
|
||||
@@ -71,6 +76,75 @@ namespace ColorControl.Services.Samsung
|
||||
|
||||
LoadConfig();
|
||||
LoadPresets();
|
||||
LoadRemotes();
|
||||
}
|
||||
|
||||
private void LoadRemotes()
|
||||
{
|
||||
_remotesFilename = Path.Combine(_dataPath, RemotesFilename);
|
||||
|
||||
try
|
||||
{
|
||||
var remotesExists = File.Exists(_remotesFilename);
|
||||
if (remotesExists)
|
||||
{
|
||||
try
|
||||
{
|
||||
var json = File.ReadAllText(_remotesFilename);
|
||||
|
||||
_samsungRemotes = JsonConvert.DeserializeObject<List<SamsungRcDto>>(json);
|
||||
}
|
||||
catch (Exception ex1)
|
||||
{
|
||||
Logger.Error($"Error while loading remotes, reverting to default remotes: {ex1.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Error($"General error while loading remotes, reverting to empty list: {ex.Message}");
|
||||
}
|
||||
|
||||
_samsungRemotes ??= [];
|
||||
|
||||
var defaultRemotes = GetDefaultRemotes();
|
||||
|
||||
_samsungRemotes.AddRange(defaultRemotes.Where(r => !_samsungRemotes.Any(sr => sr.Name == r.Name)));
|
||||
}
|
||||
|
||||
protected static List<SamsungRcDto> GetDefaultRemotes()
|
||||
{
|
||||
var json = Utils.GetResourceFile("SamsungRemotes.json");
|
||||
|
||||
return JsonConvert.DeserializeObject<List<SamsungRcDto>>(json);
|
||||
}
|
||||
|
||||
public List<SamsungRcDto> GetRemotes()
|
||||
{
|
||||
return _samsungRemotes;
|
||||
}
|
||||
|
||||
public bool UpdateRemote(SamsungRcDto remoteSpec)
|
||||
{
|
||||
var remote = _samsungRemotes.FirstOrDefault(r => r.Name == remoteSpec.Name);
|
||||
|
||||
if (remote != null)
|
||||
{
|
||||
remote.Update(remoteSpec);
|
||||
}
|
||||
else
|
||||
{
|
||||
_samsungRemotes.Add(remoteSpec);
|
||||
}
|
||||
|
||||
GlobalSave();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void DeleteRemote(string remoteName)
|
||||
{
|
||||
_samsungRemotes.RemoveAll(r => r.Name == remoteName);
|
||||
}
|
||||
|
||||
private string SamsungPreset_GetDeviceName(string macAddress)
|
||||
@@ -90,7 +164,7 @@ namespace ColorControl.Services.Samsung
|
||||
{
|
||||
var samsungService = Program.ServiceProvider.GetRequiredService<SamsungService>();
|
||||
|
||||
await samsungService.RefreshDevices(afterStartUp: true);
|
||||
await samsungService.RefreshDevices(connect: false, afterStartUp: true);
|
||||
|
||||
var result = await samsungService.ApplyPreset(presetName);
|
||||
|
||||
@@ -265,7 +339,7 @@ namespace ColorControl.Services.Samsung
|
||||
{
|
||||
var customIpAddresses = Devices.Where(d => d.IsCustom).Select(d => d.IpAddress);
|
||||
|
||||
var pnpDevices = await Utils.GetPnpDevices(Config.DeviceSearchKey, Utils.DEV_CLASS_DISPLAY_TV_LCD);
|
||||
var pnpDevices = Utils.GetPnpDevices(Config.DeviceSearchKey, Utils.DEV_CLASS_DISPLAY_TV_LCD);
|
||||
|
||||
var autoDevices = pnpDevices.Where(p => !customIpAddresses.Contains(p.IpAddress)).Select(d => new SamsungDevice(d.Name, d.IpAddress, d.MacAddress, false)).ToList();
|
||||
var autoIpAddresses = pnpDevices.Select(d => d.IpAddress);
|
||||
@@ -308,7 +382,12 @@ namespace ColorControl.Services.Samsung
|
||||
}
|
||||
else
|
||||
{
|
||||
var _ = SelectedDevice.ConnectAsync();
|
||||
var connectTask = SelectedDevice.ConnectAsync();
|
||||
|
||||
if (!afterStartUp)
|
||||
{
|
||||
return await connectTask;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -341,6 +420,7 @@ namespace ColorControl.Services.Samsung
|
||||
{
|
||||
SavePresets();
|
||||
SaveConfig();
|
||||
Utils.WriteObject(_remotesFilename, _samsungRemotes);
|
||||
}
|
||||
|
||||
public SamsungDevice GetPresetDevice(SamsungPreset preset)
|
||||
@@ -966,5 +1046,29 @@ namespace ColorControl.Services.Samsung
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool RemoveDevice(SamsungDeviceDto deviceDto)
|
||||
{
|
||||
if (deviceDto == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var device = MatchDevice(deviceDto);
|
||||
if (device?.IsCustom != true)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
RemoveCustomDevice(device);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private SamsungDevice MatchDevice(SamsungDeviceDto deviceDto)
|
||||
{
|
||||
return Devices?.FirstOrDefault(d => string.IsNullOrEmpty(deviceDto.MacAddress) ? d.IpAddress == deviceDto.IpAddress : d.MacAddress == deviceDto.MacAddress && d.IpAddress == deviceDto.IpAddress) ??
|
||||
Devices?.FirstOrDefault(d => string.IsNullOrEmpty(deviceDto.MacAddress) ? d.IpAddress == deviceDto.IpAddress : d.MacAddress == deviceDto.MacAddress);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -435,7 +435,9 @@ namespace LgTv
|
||||
|
||||
var lunauri = "luna://com.webos.settingsservice/setSystemSettings";
|
||||
|
||||
var json = @"{ ""category"": """ + category + @""", ""settings"": { """ + key + @""": " + jsonValue + @" } }";
|
||||
var extra = key == "truMotionMode" ? @", ""current_app"": true " : "";
|
||||
|
||||
var json = @"{ ""category"": """ + category + @""", ""settings"": { """ + key + @""": " + jsonValue + @" }" + extra + " }";
|
||||
|
||||
var @params = JObject.Parse(json);
|
||||
|
||||
|
||||
+1147
-1137
File diff suppressed because it is too large.
Load diff
@@ -68,7 +68,11 @@ public class MHC2Wrapper
|
||||
SDRTransferFunction = deviceContext.ExtraInfoTag?.SDRTransferFunction ?? SDRTransferFunction.PurePower,
|
||||
SDRBrightnessBoost = deviceContext.ExtraInfoTag?.SDRBrightnessBoost ?? 0,
|
||||
ShadowDetailBoost = deviceContext.ExtraInfoTag?.ShadowDetailBoost ?? 0,
|
||||
Gamma = deviceContext.ExtraInfoTag?.Gamma ?? 2.2
|
||||
Gamma = deviceContext.ExtraInfoTag?.Gamma ?? 2.2,
|
||||
ToneMappingFromLuminance = deviceContext.ExtraInfoTag?.ToneMappingFromLuminance ?? 400,
|
||||
ToneMappingToLuminance = deviceContext.ExtraInfoTag?.ToneMappingToLuminance ?? 400,
|
||||
HdrGammaMultiplier = deviceContext.ExtraInfoTag?.HdrGammaMultiplier ?? 1,
|
||||
HdrBrightnessMultiplier = deviceContext.ExtraInfoTag?.HdrBrightnessMultiplier ?? 1
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using nspector.Common.Helper;
|
||||
using ColorControl.Shared.Contracts.NVIDIA;
|
||||
using nspector.Common.Helper;
|
||||
using nspector.Common.Meta;
|
||||
using nspector.Native.NVAPI2;
|
||||
using System.Diagnostics;
|
||||
@@ -227,9 +228,9 @@ namespace nspector.Common
|
||||
return name;
|
||||
}
|
||||
|
||||
public List<string> GetProfileNamesByApps(List<string> appNames)
|
||||
public List<NvProfile> GetProfileNamesByApps(List<string> appNames)
|
||||
{
|
||||
var profileNames = new List<string>();
|
||||
var profiles = new List<NvProfile>();
|
||||
|
||||
DrsSession((hSession) =>
|
||||
{
|
||||
@@ -238,15 +239,22 @@ namespace nspector.Common
|
||||
{
|
||||
var apps = GetProfileApplications(hSession, hProfile, true);
|
||||
|
||||
if (apps?.Any(a => appNames.Any(pn => a.appName.Equals(pn, StringComparison.OrdinalIgnoreCase))) == true)
|
||||
if (apps?.Any(a => appNames.Any(pn => a.appName.Contains(pn, StringComparison.OrdinalIgnoreCase))) == true)
|
||||
{
|
||||
var profile = GetProfileInfo(hSession, hProfile);
|
||||
profileNames.Add(profile.profileName);
|
||||
|
||||
var nvProfile = new NvProfile
|
||||
{
|
||||
Name = profile.profileName,
|
||||
Apps = apps.Select(a => a.appName).ToList()
|
||||
};
|
||||
|
||||
profiles.Add(nvProfile);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return profileNames;
|
||||
return profiles;
|
||||
}
|
||||
|
||||
public void CreateProfile(string profileName, string? applicationName = null)
|
||||
|
||||
@@ -17,6 +17,10 @@
|
||||
<PackageReference Include="System.Resources.Extensions" Version="9.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Shared\Shared.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Update="frmBitEditor.cs">
|
||||
<SubType>Form</SubType>
|
||||
|
||||
@@ -7931,7 +7931,11 @@
|
||||
<UserfriendlyName>Preset J</UserfriendlyName>
|
||||
<HexValue>0x0000000A</HexValue>
|
||||
</CustomSettingValue>
|
||||
</SettingValues>
|
||||
<CustomSettingValue>
|
||||
<UserfriendlyName>Preset K</UserfriendlyName>
|
||||
<HexValue>0x0000000B</HexValue>
|
||||
</CustomSettingValue>
|
||||
</SettingValues>
|
||||
<SettingMasks />
|
||||
</CustomSetting>
|
||||
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<Title>NvAPIWrapper (for NVAPI 410)</Title>
|
||||
<PackageId>NvAPIWrapper.Net</PackageId>
|
||||
<AssemblyOriginatorKeyFile>OpenSourceStrongNameSignKey.pfx</AssemblyOriginatorKeyFile>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||
<OutputPath>..\Debug</OutputPath>
|
||||
|
||||
@@ -4,9 +4,9 @@ Easily change NVIDIA and AMD display settings, control LG and Samsung tv's, Game
|
||||
## Installation
|
||||
|
||||
Just extract the .zip file in a folder of your preference. Run ColorControl.exe to start the application.
|
||||
Requires .NET 8 with the following runtimes:
|
||||
* .NET Desktop Runtime 8
|
||||
* .NET ASP.NET Core Runtime 8
|
||||
Requires .NET 9 with the following runtimes:
|
||||
* .NET Desktop Runtime 9
|
||||
* .NET ASP.NET Core Runtime 9
|
||||
|
||||
ColorControl stores its settings in the following folder:
|
||||
* C:\Users\username\AppData\Roaming\Maassoft\ColorControl
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using Newtonsoft.Json.Serialization;
|
||||
|
||||
namespace ColorControl.Shared.Common;
|
||||
|
||||
public class NestedItemsBuilder
|
||||
{
|
||||
public class NestedItem
|
||||
{
|
||||
public bool Expanded { get; set; }
|
||||
public string Name { get; set; }
|
||||
public string Value { get; set; }
|
||||
public List<NestedItem> NestedItems { get; set; } = [];
|
||||
}
|
||||
|
||||
public static NestedItem CreateTree(object obj, string text)
|
||||
{
|
||||
var serialized = JsonConvert.SerializeObject(obj, Formatting.None,
|
||||
new JsonSerializerSettings
|
||||
{
|
||||
ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
|
||||
Error = delegate (object sender, ErrorEventArgs args)
|
||||
{
|
||||
//errors.Add(args.ErrorContext.Error.Message);
|
||||
args.ErrorContext.Handled = true;
|
||||
},
|
||||
Converters = [new Newtonsoft.Json.Converters.StringEnumConverter()]
|
||||
});
|
||||
var dic = JsonConvert.DeserializeObject<Dictionary<string, object>>(serialized);
|
||||
var root = new NestedItem
|
||||
{
|
||||
Value = text
|
||||
};
|
||||
BuildTree(dic, root);
|
||||
return root;
|
||||
}
|
||||
|
||||
private static void BuildTree(object item, NestedItem node)
|
||||
{
|
||||
if (item is KeyValuePair<string, object> kv)
|
||||
{
|
||||
var keyValueNode = new NestedItem();
|
||||
keyValueNode.Name = kv.Key;
|
||||
keyValueNode.Value = kv.Key + ": " + GetValueAsString(kv.Value);
|
||||
node.NestedItems.Add(keyValueNode);
|
||||
BuildTree(kv.Value, keyValueNode);
|
||||
}
|
||||
else if (item.GetType() == typeof(JArray))
|
||||
{
|
||||
var list = (JArray)item;
|
||||
int index = 0;
|
||||
foreach (object value in list)
|
||||
{
|
||||
var arrayItem = new NestedItem();
|
||||
arrayItem.Name = $"[{index}]";
|
||||
arrayItem.Value = $"[{index}]";
|
||||
node.NestedItems.Add(arrayItem);
|
||||
BuildTree(value, arrayItem);
|
||||
index++;
|
||||
}
|
||||
}
|
||||
else if (item is Dictionary<string, object> dictionary)
|
||||
{
|
||||
foreach (KeyValuePair<string, object> d in dictionary)
|
||||
{
|
||||
BuildTree(d, node);
|
||||
}
|
||||
}
|
||||
else if (item.GetType() == typeof(JObject))
|
||||
{
|
||||
var jobject = item as JObject;
|
||||
foreach (var property in jobject.Properties())
|
||||
{
|
||||
BuildTree(new KeyValuePair<string, object>(property.Name, property.Value), node);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static string GetValueAsString(object value)
|
||||
{
|
||||
if (value == null)
|
||||
return "null";
|
||||
var type = value.GetType();
|
||||
if (type.IsArray)
|
||||
{
|
||||
return "[]";
|
||||
}
|
||||
|
||||
if (value.GetType() == typeof(JArray))
|
||||
{
|
||||
var arr = value as JArray;
|
||||
return $"[{arr.Count}]";
|
||||
}
|
||||
|
||||
if (type.IsGenericType || value.GetType() == typeof(JObject))
|
||||
{
|
||||
return "{}";
|
||||
}
|
||||
|
||||
return value.ToString();
|
||||
}
|
||||
}
|
||||
@@ -138,12 +138,12 @@ The best and suggested method to provide this is via a Windows Service. Only whe
|
||||
return values;
|
||||
}
|
||||
|
||||
public static async Task<List<PnpDev>> GetPnpDevices(string deviceName, string category = null)
|
||||
public static List<PnpDev> GetPnpDevices(string deviceName, string category = null)
|
||||
{
|
||||
var devices = new List<PnpDev>();
|
||||
|
||||
var queryString = $"SELECT * FROM Win32_PnPEntity WHERE PNPClass='DigitalMediaDevices' AND NAME LIKE '%{deviceName}%'";
|
||||
using ManagementObjectSearcher mos = new ManagementObjectSearcher(queryString);
|
||||
using var mos = new ManagementObjectSearcher(queryString);
|
||||
|
||||
foreach (var mo in mos.Get().OfType<ManagementObject>())
|
||||
{
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
namespace ColorControl.Shared.Contracts.Base;
|
||||
|
||||
public abstract class BaseDeviceDto
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public string IpAddress { get; set; }
|
||||
public string MacAddress { get; set; }
|
||||
public bool IsCustom { get; set; }
|
||||
public string Token { get; set; }
|
||||
|
||||
public bool IsDummy { get; set; }
|
||||
|
||||
public bool PoweredOn { get; set; }
|
||||
|
||||
public DateTimeOffset PoweredOffAt { get; set; }
|
||||
|
||||
public bool IsConnected { get; set; }
|
||||
|
||||
public bool IsSelected { get; set; }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
if (IpAddress != null)
|
||||
{
|
||||
return $"{Name} ({IpAddress})";
|
||||
}
|
||||
|
||||
return Name;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,6 +29,7 @@ public class DisplayConfig
|
||||
RefreshRate = new Rational(displayConfig.RefreshRate.Numerator, displayConfig.RefreshRate.Denominator);
|
||||
Scaling = displayConfig.Scaling;
|
||||
Rotation = displayConfig.Rotation;
|
||||
IsPrimary = displayConfig.IsPrimary;
|
||||
}
|
||||
|
||||
public string GetResolutionDesc()
|
||||
|
||||
@@ -63,6 +63,7 @@ public class DisplayInfoDto
|
||||
public string DeviceName { get; set; }
|
||||
public string DevicePath { get; set; }
|
||||
public string DisplayId { get; set; }
|
||||
public bool UsePerUserSettings { get; set; }
|
||||
|
||||
public bool IsAdvancedColorSupported { get; set; }
|
||||
public bool IsAdvancedColorEnabled { get; set; }
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
namespace ColorControl.Shared.Contracts;
|
||||
|
||||
public class DpiScaling
|
||||
{
|
||||
public bool ApplyScaling { get; set; }
|
||||
public uint Percentage { get; set; }
|
||||
|
||||
public DpiScaling()
|
||||
{
|
||||
Percentage = 100;
|
||||
}
|
||||
|
||||
public DpiScaling(DpiScaling dpiScaling)
|
||||
{
|
||||
ApplyScaling = dpiScaling.ApplyScaling;
|
||||
Percentage = dpiScaling.Percentage;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"{Percentage:0}%";
|
||||
}
|
||||
}
|
||||
@@ -6,133 +6,140 @@ namespace ColorControl.Shared.Contracts.Game;
|
||||
|
||||
public enum GameStepType
|
||||
{
|
||||
[Description("Pre-launch steps")]
|
||||
PreLaunch = 0,
|
||||
[Description("Post-launch steps")]
|
||||
PostLaunch = 1,
|
||||
[Description("Finalize steps")]
|
||||
Finalize = 2,
|
||||
[Description("Pre-launch steps")]
|
||||
PreLaunch = 0,
|
||||
[Description("Post-launch steps")]
|
||||
PostLaunch = 1,
|
||||
[Description("Finalize steps")]
|
||||
Finalize = 2,
|
||||
}
|
||||
|
||||
public enum GamePriorityClass
|
||||
{
|
||||
Idle = NativeConstants.IDLE_PRIORITY_CLASS,
|
||||
BelowNormal = NativeConstants.BELOW_NORMAL_PRIORITY_CLASS,
|
||||
Normal = NativeConstants.NORMAL_PRIORITY_CLASS,
|
||||
AboveNormal = NativeConstants.ABOVE_NORMAL_PRIORITY_CLASS,
|
||||
High = NativeConstants.HIGH_PRIORITY_CLASS,
|
||||
RealTime = NativeConstants.REALTIME_PRIORITY_CLASS
|
||||
Idle = NativeConstants.IDLE_PRIORITY_CLASS,
|
||||
BelowNormal = NativeConstants.BELOW_NORMAL_PRIORITY_CLASS,
|
||||
Normal = NativeConstants.NORMAL_PRIORITY_CLASS,
|
||||
AboveNormal = NativeConstants.ABOVE_NORMAL_PRIORITY_CLASS,
|
||||
High = NativeConstants.HIGH_PRIORITY_CLASS,
|
||||
RealTime = NativeConstants.REALTIME_PRIORITY_CLASS
|
||||
}
|
||||
|
||||
public enum ProcessAutoAction
|
||||
{
|
||||
None = 0,
|
||||
Suspend = 1,
|
||||
Restart = 2
|
||||
None = 0,
|
||||
Suspend = 1,
|
||||
Restart = 2
|
||||
}
|
||||
|
||||
public class AutoSettings
|
||||
{
|
||||
public bool AllowAutoApply { get; set; }
|
||||
public ProcessAutoAction ProcessAutoAction { get; set; }
|
||||
public bool AllowAutoApply { get; set; }
|
||||
public ProcessAutoAction ProcessAutoAction { get; set; }
|
||||
|
||||
public void Update(AutoSettings autoSettings)
|
||||
{
|
||||
AllowAutoApply = autoSettings.AllowAutoApply;
|
||||
ProcessAutoAction = autoSettings.ProcessAutoAction;
|
||||
}
|
||||
}
|
||||
|
||||
public class GamePreset : PresetBase
|
||||
{
|
||||
public string Path { get; set; }
|
||||
public string Parameters { get; set; }
|
||||
public bool RunAsAdministrator { get; set; }
|
||||
public List<string> PreLaunchSteps { get; set; }
|
||||
public List<string> PostLaunchSteps { get; set; }
|
||||
public List<string> FinalizeSteps { get; set; }
|
||||
public uint ProcessAffinityMask { get; set; }
|
||||
public uint ProcessPriorityClass { get; set; }
|
||||
public string Path { get; set; }
|
||||
public string Parameters { get; set; }
|
||||
public bool RunAsAdministrator { get; set; }
|
||||
public List<string> PreLaunchSteps { get; set; }
|
||||
public List<string> PostLaunchSteps { get; set; }
|
||||
public List<string> FinalizeSteps { get; set; }
|
||||
public uint ProcessAffinityMask { get; set; }
|
||||
public uint ProcessPriorityClass { get; set; }
|
||||
|
||||
[JsonIgnore]
|
||||
public GamePriorityClass GameProcessPriorityClass
|
||||
{
|
||||
get
|
||||
{
|
||||
return (GamePriorityClass)ProcessPriorityClass;
|
||||
}
|
||||
set
|
||||
{
|
||||
ProcessPriorityClass = (uint)value;
|
||||
}
|
||||
}
|
||||
[JsonIgnore]
|
||||
public GamePriorityClass GameProcessPriorityClass
|
||||
{
|
||||
get
|
||||
{
|
||||
return (GamePriorityClass)ProcessPriorityClass;
|
||||
}
|
||||
set
|
||||
{
|
||||
ProcessPriorityClass = (uint)value;
|
||||
}
|
||||
}
|
||||
|
||||
public AutoSettings AutoSettings { get; set; }
|
||||
public AutoSettings AutoSettings { get; set; }
|
||||
|
||||
public GamePreset() : base()
|
||||
{
|
||||
PreLaunchSteps = new List<string>();
|
||||
PostLaunchSteps = new List<string>();
|
||||
FinalizeSteps = new List<string>();
|
||||
AutoSettings = new AutoSettings();
|
||||
}
|
||||
public GamePreset() : base()
|
||||
{
|
||||
PreLaunchSteps = new List<string>();
|
||||
PostLaunchSteps = new List<string>();
|
||||
FinalizeSteps = new List<string>();
|
||||
AutoSettings = new AutoSettings();
|
||||
}
|
||||
|
||||
public GamePreset(GamePreset preset) : this()
|
||||
{
|
||||
id = GetNewId();
|
||||
public GamePreset(GamePreset preset) : this()
|
||||
{
|
||||
id = GetNewId();
|
||||
|
||||
Update(preset);
|
||||
}
|
||||
Update(preset);
|
||||
}
|
||||
|
||||
public void Update(GamePreset preset)
|
||||
{
|
||||
name = preset.name;
|
||||
ShowInQuickAccess = preset.ShowInQuickAccess;
|
||||
Path = preset.Path;
|
||||
Parameters = preset.Parameters;
|
||||
RunAsAdministrator = preset.RunAsAdministrator;
|
||||
ProcessAffinityMask = preset.ProcessAffinityMask;
|
||||
ProcessPriorityClass = preset.ProcessPriorityClass;
|
||||
PreLaunchSteps.Clear();
|
||||
PreLaunchSteps.AddRange(preset.PreLaunchSteps);
|
||||
PostLaunchSteps.Clear();
|
||||
PostLaunchSteps.AddRange(preset.PostLaunchSteps);
|
||||
FinalizeSteps.Clear();
|
||||
FinalizeSteps.AddRange(preset.FinalizeSteps);
|
||||
}
|
||||
public void Update(GamePreset preset)
|
||||
{
|
||||
name = preset.name;
|
||||
ShowInQuickAccess = preset.ShowInQuickAccess;
|
||||
Path = preset.Path;
|
||||
Parameters = preset.Parameters;
|
||||
RunAsAdministrator = preset.RunAsAdministrator;
|
||||
ProcessAffinityMask = preset.ProcessAffinityMask;
|
||||
ProcessPriorityClass = preset.ProcessPriorityClass;
|
||||
AutoSettings.Update(preset.AutoSettings);
|
||||
PreLaunchSteps.Clear();
|
||||
PreLaunchSteps.AddRange(preset.PreLaunchSteps);
|
||||
PostLaunchSteps.Clear();
|
||||
PostLaunchSteps.AddRange(preset.PostLaunchSteps);
|
||||
FinalizeSteps.Clear();
|
||||
FinalizeSteps.AddRange(preset.FinalizeSteps);
|
||||
}
|
||||
|
||||
public GamePreset Clone()
|
||||
{
|
||||
var preset = new GamePreset(this);
|
||||
public GamePreset Clone()
|
||||
{
|
||||
var preset = new GamePreset(this);
|
||||
|
||||
preset.name += " (copy)";
|
||||
preset.name += " (copy)";
|
||||
|
||||
return preset;
|
||||
}
|
||||
return preset;
|
||||
}
|
||||
|
||||
public GamePreset CloneWithId()
|
||||
{
|
||||
var preset = new GamePreset(this);
|
||||
preset.SetId(id);
|
||||
preset.name = name;
|
||||
preset.shortcut = shortcut;
|
||||
public GamePreset CloneWithId()
|
||||
{
|
||||
var preset = new GamePreset(this);
|
||||
preset.SetId(id);
|
||||
preset.name = name;
|
||||
preset.shortcut = shortcut;
|
||||
|
||||
return preset;
|
||||
}
|
||||
return preset;
|
||||
}
|
||||
|
||||
public static string[] GetColumnNames()
|
||||
{
|
||||
return new[] { "Name|160", "File/URI|400", "Parameters|200", "Pre-launch steps|300", "Post-launch steps|300", "Finalize steps|300" };
|
||||
}
|
||||
public static string[] GetColumnNames()
|
||||
{
|
||||
return new[] { "Name|160", "File/URI|400", "Parameters|200", "Pre-launch steps|300", "Post-launch steps|300", "Finalize steps|300" };
|
||||
}
|
||||
|
||||
public override List<string> GetDisplayValues(Config config = null)
|
||||
{
|
||||
var values = new List<string>
|
||||
{
|
||||
name,
|
||||
Path,
|
||||
Parameters,
|
||||
public override List<string> GetDisplayValues(Config config = null)
|
||||
{
|
||||
var values = new List<string>
|
||||
{
|
||||
name,
|
||||
Path,
|
||||
Parameters,
|
||||
|
||||
string.Join(", ", PreLaunchSteps),
|
||||
string.Join(", ", PostLaunchSteps),
|
||||
string.Join(", ", FinalizeSteps)
|
||||
};
|
||||
string.Join(", ", PreLaunchSteps),
|
||||
string.Join(", ", PostLaunchSteps),
|
||||
string.Join(", ", FinalizeSteps)
|
||||
};
|
||||
|
||||
return values;
|
||||
}
|
||||
return values;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,32 +1,9 @@
|
||||
namespace ColorControl.Shared.Contracts.LG;
|
||||
using ColorControl.Shared.Contracts.Base;
|
||||
|
||||
public class LgDeviceDto
|
||||
namespace ColorControl.Shared.Contracts.LG;
|
||||
|
||||
public class LgDeviceDto : BaseDeviceDto
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public string IpAddress { get; set; }
|
||||
public string MacAddress { get; set; }
|
||||
public bool IsCustom { get; set; }
|
||||
public string Token { get; set; }
|
||||
public LgDeviceOptions Options { get; set; } = new();
|
||||
|
||||
public bool IsDummy { get; set; }
|
||||
|
||||
public bool PoweredOn { get; set; }
|
||||
|
||||
public DateTimeOffset PoweredOffAt { get; set; }
|
||||
|
||||
public bool IsConnected { get; set; }
|
||||
|
||||
public bool IsSelected { get; set; }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
if (IpAddress != null)
|
||||
{
|
||||
return $"{Name} ({IpAddress})";
|
||||
}
|
||||
|
||||
return Name;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,117 +4,115 @@ namespace ColorControl.Shared.Contracts.LG;
|
||||
|
||||
public class LgPreset : PresetBase
|
||||
{
|
||||
public static event GetValueByKey GetDeviceName;
|
||||
public static event GetValueByKey GetAppName;
|
||||
public static event GetValueByKey GetDeviceName;
|
||||
public static event GetValueByKey GetAppName;
|
||||
|
||||
public string appId { get; set; }
|
||||
public List<string> steps { get; set; }
|
||||
public string DeviceMacAddress { get; set; }
|
||||
public dynamic AppParams { get; set; }
|
||||
public string appId { get; set; }
|
||||
public string DeviceMacAddress { get; set; }
|
||||
public dynamic AppParams { get; set; }
|
||||
|
||||
[JsonIgnore]
|
||||
public int X { get; set; }
|
||||
[JsonIgnore]
|
||||
public int Y { get; set; }
|
||||
[JsonIgnore]
|
||||
public int X { get; set; }
|
||||
[JsonIgnore]
|
||||
public int Y { get; set; }
|
||||
|
||||
public LgPreset() : base()
|
||||
{
|
||||
steps = new List<string>();
|
||||
}
|
||||
public LgPreset() : base()
|
||||
{
|
||||
}
|
||||
|
||||
public LgPreset(LgPreset preset) : this()
|
||||
{
|
||||
id = GetNewId();
|
||||
public LgPreset(LgPreset preset) : this()
|
||||
{
|
||||
id = GetNewId();
|
||||
|
||||
Update(preset);
|
||||
Update(preset);
|
||||
|
||||
name = preset.name + " (copy)";
|
||||
shortcut = null;
|
||||
}
|
||||
name = preset.name + " (copy)";
|
||||
shortcut = null;
|
||||
}
|
||||
|
||||
public LgPreset(string name, string appId, IEnumerable<string> steps = null, dynamic appParams = null) : this()
|
||||
{
|
||||
this.name = name;
|
||||
this.appId = appId;
|
||||
if (steps != null)
|
||||
{
|
||||
this.steps.AddRange(steps);
|
||||
}
|
||||
AppParams = appParams;
|
||||
}
|
||||
public LgPreset(string name, string appId, IEnumerable<string> steps = null, dynamic appParams = null) : this()
|
||||
{
|
||||
this.name = name;
|
||||
this.appId = appId;
|
||||
if (steps != null)
|
||||
{
|
||||
Steps.AddRange(steps);
|
||||
}
|
||||
AppParams = appParams;
|
||||
}
|
||||
|
||||
public LgPreset Clone()
|
||||
{
|
||||
var preset = new LgPreset(this);
|
||||
public LgPreset Clone()
|
||||
{
|
||||
var preset = new LgPreset(this);
|
||||
|
||||
return preset;
|
||||
}
|
||||
return preset;
|
||||
}
|
||||
|
||||
public LgPreset CloneWithId()
|
||||
{
|
||||
var preset = new LgPreset(this);
|
||||
preset.SetId(id);
|
||||
preset.name = name;
|
||||
preset.shortcut = shortcut;
|
||||
public LgPreset CloneWithId()
|
||||
{
|
||||
var preset = new LgPreset(this);
|
||||
preset.SetId(id);
|
||||
preset.name = name;
|
||||
preset.shortcut = shortcut;
|
||||
|
||||
return preset;
|
||||
}
|
||||
return preset;
|
||||
}
|
||||
|
||||
public void Update(LgPreset preset)
|
||||
{
|
||||
name = preset.name;
|
||||
appId = preset.appId;
|
||||
shortcut = preset.shortcut;
|
||||
steps.Clear();
|
||||
steps.AddRange(preset.steps);
|
||||
Description = preset.Description;
|
||||
DeviceMacAddress = preset.DeviceMacAddress;
|
||||
ShowInQuickAccess = preset.ShowInQuickAccess;
|
||||
public void Update(LgPreset preset)
|
||||
{
|
||||
name = preset.name;
|
||||
appId = preset.appId;
|
||||
shortcut = preset.shortcut;
|
||||
Steps.Clear();
|
||||
Steps.AddRange(preset.Steps);
|
||||
Description = preset.Description;
|
||||
DeviceMacAddress = preset.DeviceMacAddress;
|
||||
ShowInQuickAccess = preset.ShowInQuickAccess;
|
||||
|
||||
var trigger = preset.Triggers.FirstOrDefault();
|
||||
if (trigger != null)
|
||||
{
|
||||
UpdateTrigger(trigger.Trigger, trigger.Conditions, trigger.IncludedProcessesAsString, trigger.ExcludedProcessesAsString, trigger.ConnectedDisplaysRegex);
|
||||
}
|
||||
}
|
||||
var trigger = preset.Triggers.FirstOrDefault();
|
||||
if (trigger != null)
|
||||
{
|
||||
UpdateTrigger(trigger.Trigger, trigger.Conditions, trigger.IncludedProcessesAsString, trigger.ExcludedProcessesAsString, trigger.ConnectedDisplaysRegex);
|
||||
}
|
||||
}
|
||||
|
||||
public static List<string> GetColumnNames()
|
||||
{
|
||||
return new List<string>() { "Name", "Device|140", "App|200", "Steps|400", "Shortcut", "Trigger" };
|
||||
}
|
||||
public static List<string> GetColumnNames()
|
||||
{
|
||||
return new List<string>() { "Name", "Device|140", "App|200", "Steps|400", "Shortcut", "Trigger" };
|
||||
}
|
||||
|
||||
public override List<string> GetDisplayValues(Config config = null)
|
||||
{
|
||||
var values = new List<string>();
|
||||
public override List<string> GetDisplayValues(Config config = null)
|
||||
{
|
||||
var values = new List<string>();
|
||||
|
||||
values.Add(name);
|
||||
values.Add(name);
|
||||
|
||||
var deviceString = "Global";
|
||||
if (!string.IsNullOrEmpty(DeviceMacAddress))
|
||||
{
|
||||
deviceString = GetDeviceName?.Invoke(DeviceMacAddress) ?? DeviceMacAddress;
|
||||
}
|
||||
values.Add(deviceString);
|
||||
var deviceString = "Global";
|
||||
if (!string.IsNullOrEmpty(DeviceMacAddress))
|
||||
{
|
||||
deviceString = GetDeviceName?.Invoke(DeviceMacAddress) ?? DeviceMacAddress;
|
||||
}
|
||||
values.Add(deviceString);
|
||||
|
||||
var app = appId;
|
||||
if (!string.IsNullOrEmpty(appId))
|
||||
{
|
||||
app = GetAppName?.Invoke(appId) ?? appId;
|
||||
}
|
||||
values.Add(app);
|
||||
values.Add(GetStepsDisplay());
|
||||
values.Add(shortcut);
|
||||
var app = appId;
|
||||
if (!string.IsNullOrEmpty(appId))
|
||||
{
|
||||
app = GetAppName?.Invoke(appId) ?? appId;
|
||||
}
|
||||
values.Add(app);
|
||||
values.Add(GetStepsDisplay());
|
||||
values.Add(shortcut);
|
||||
|
||||
if (Triggers.Any())
|
||||
{
|
||||
values.Add(Triggers.First().ToString());
|
||||
}
|
||||
if (Triggers.Any())
|
||||
{
|
||||
values.Add(Triggers.First().ToString());
|
||||
}
|
||||
|
||||
return values;
|
||||
}
|
||||
return values;
|
||||
}
|
||||
|
||||
public string GetStepsDisplay()
|
||||
{
|
||||
return steps.Aggregate("", (a, b) => (string.IsNullOrEmpty(a) ? "" : a + ", ") + b);
|
||||
}
|
||||
public string GetStepsDisplay()
|
||||
{
|
||||
return Steps.Aggregate("", (a, b) => (string.IsNullOrEmpty(a) ? "" : a + ", ") + b);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
namespace ColorControl.Shared.Contracts.LG;
|
||||
|
||||
public static class LgPresets
|
||||
{
|
||||
public static readonly LgPreset InStartPreset = new("InStart", "com.webos.app.factorywin", ["0", "4", "1", "3"], new { id = "executeFactory", irKey = "inStart" });
|
||||
public static readonly LgPreset EzAdjustPreset = new("EzAdjust", "com.webos.app.factorywin", ["0", "4", "1", "3"], new { id = "executeFactory", irKey = "ezAdjust" });
|
||||
public static readonly LgPreset SoftwareUpdatePreset = new("Software Update", "com.webos.app.softwareupdate", null, new { mode = "user", flagUpdate = true });
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
|
||||
using ColorControl.Shared.Contracts.RemoteControl;
|
||||
|
||||
namespace ColorControl.Shared.Contracts.LG;
|
||||
|
||||
public class LgRcDto : BaseRcDto<LgPreset, ButtonType>
|
||||
{
|
||||
}
|
||||
@@ -66,6 +66,19 @@ namespace ColorControl.Shared.Contracts.NVIDIA
|
||||
PowerPCM = settings.PowerPCM;
|
||||
}
|
||||
|
||||
public bool Equals(NvGpuOcSettings other)
|
||||
{
|
||||
return PCIIdentifier == other.PCIIdentifier &&
|
||||
MemoryOffsetKHz == other.MemoryOffsetKHz &&
|
||||
Type == other.Type &&
|
||||
GraphicsOffsetKHz == other.GraphicsOffsetKHz &&
|
||||
MaximumFrequencyKHz == other.MaximumFrequencyKHz &&
|
||||
MaximumVoltageUv == other.MaximumVoltageUv &&
|
||||
FrequencyPreferred == other.FrequencyPreferred &&
|
||||
VoltageBoostPercent == other.VoltageBoostPercent &&
|
||||
PowerPCM == other.PowerPCM;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
var value = "";
|
||||
|
||||
@@ -84,6 +84,7 @@ namespace ColorControl.Shared.Contracts.NVIDIA
|
||||
public NvHdrSettings HdrSettings { get; set; }
|
||||
public bool ApplyNovideoSettings { get; set; }
|
||||
public NovideoSettings NovideoSettings { get; set; }
|
||||
public DpiScaling DpiScaling { get; set; }
|
||||
|
||||
public bool SetDitherRegistryKey { get; set; }
|
||||
public bool RestartDriver { get; set; }
|
||||
@@ -121,6 +122,7 @@ namespace ColorControl.Shared.Contracts.NVIDIA
|
||||
ColorEnhancementSettings = new NvColorEnhancementSettings();
|
||||
HdrSettings = new NvHdrSettings();
|
||||
NovideoSettings = new NovideoSettings();
|
||||
DpiScaling = new DpiScaling();
|
||||
}
|
||||
|
||||
public NvPreset(ColorData colorData) : this()
|
||||
@@ -150,10 +152,11 @@ namespace ColorControl.Shared.Contracts.NVIDIA
|
||||
applyDriverSettings = keepChanges && applyDriverSettings || driverSettings.Any(s => !currentSettings.driverSettings.Any(o => o.Key == s.Key && o.Value == s.Value));
|
||||
applyHdmiSettings = keepChanges && applyHdmiSettings || HdmiInfoFrameSettings.IsDifferent(currentSettings.HdmiInfoFrameSettings);
|
||||
ApplyNovideoSettings = keepChanges && ApplyNovideoSettings || (NovideoSettings.ApplyClamp && !HDREnabled || applyHDR && !HDREnabled && !NovideoSettings.ApplyClamp);
|
||||
applyOverclocking = keepChanges && applyOverclocking || ocSettings.Any();
|
||||
applyOverclocking = keepChanges && applyOverclocking || !HasEqualOverclockingSettings(currentSettings);
|
||||
ApplyColorEnhancements = keepChanges && ApplyColorEnhancements ||
|
||||
ColorEnhancementSettings.DigitalVibranceLevel != currentSettings.ColorEnhancementSettings.DigitalVibranceLevel ||
|
||||
ColorEnhancementSettings.HueAngle != currentSettings.ColorEnhancementSettings.HueAngle;
|
||||
DpiScaling.ApplyScaling = keepChanges && DpiScaling.ApplyScaling || DpiScaling.Percentage != currentSettings.DpiScaling.Percentage;
|
||||
}
|
||||
|
||||
public void Update(NvPreset preset)
|
||||
@@ -200,7 +203,17 @@ namespace ColorControl.Shared.Contracts.NVIDIA
|
||||
applyOverclocking = preset.applyOverclocking;
|
||||
ocSettings = preset.ocSettings.Select(s => new NvGpuOcSettings(s)).ToList();
|
||||
|
||||
DpiScaling = new DpiScaling(preset.DpiScaling);
|
||||
|
||||
MonitorConnectionType = preset.MonitorConnectionType;
|
||||
|
||||
var trigger = preset.Triggers.FirstOrDefault();
|
||||
if (trigger != null)
|
||||
{
|
||||
UpdateTrigger(trigger.Trigger, trigger.Conditions, trigger.IncludedProcessesAsString, trigger.ExcludedProcessesAsString, trigger.ConnectedDisplaysRegex);
|
||||
}
|
||||
Steps.Clear();
|
||||
Steps.AddRange(preset.Steps);
|
||||
}
|
||||
|
||||
public NvPreset Clone()
|
||||
@@ -220,6 +233,15 @@ namespace ColorControl.Shared.Contracts.NVIDIA
|
||||
return preset;
|
||||
}
|
||||
|
||||
public bool HasApplicableSettings => applyColorData || applyDithering || applyDriverSettings || applyHdmiSettings || applyHDR || applyOther ||
|
||||
applyOverclocking || ApplyColorEnhancements || ApplyNovideoSettings ||
|
||||
DisplayConfig.ApplyRefreshRate || DisplayConfig.ApplyResolution || DpiScaling.ApplyScaling;
|
||||
|
||||
public bool HasEqualOverclockingSettings(NvPreset preset)
|
||||
{
|
||||
return preset.ocSettings.All(poc => ocSettings.Any(oc => oc.Equals(poc)));
|
||||
}
|
||||
|
||||
public static string[] GetColumnNames()
|
||||
{
|
||||
return new[] { "Name", "Display|140", "Color settings (BPC, format, dyn. range, color space)|260", "Color enhancements", "Refresh rate|120", "Resolution|150", "Dithering", "HDR", "Driver settings|300", "HDMI settings|200", "Other|200", "Overclocking|300", "Shortcut", "Apply on startup" };
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
namespace ColorControl.Shared.Contracts.NVIDIA;
|
||||
|
||||
public class NvProfile
|
||||
{
|
||||
public bool IsBase { get; set; }
|
||||
public string Name { get; set; }
|
||||
|
||||
public List<string> Apps { get; set; }
|
||||
|
||||
public NvProfile()
|
||||
{
|
||||
Apps = [];
|
||||
}
|
||||
}
|
||||
@@ -4,65 +4,67 @@ namespace ColorControl.Shared.Contracts.NVIDIA;
|
||||
|
||||
public enum NvSettingType
|
||||
{
|
||||
DwordType,
|
||||
BinaryType
|
||||
DwordType,
|
||||
BinaryType
|
||||
}
|
||||
|
||||
public class NvSettingItemDto
|
||||
{
|
||||
public uint SettingId { get; set; }
|
||||
public uint SettingId { get; set; }
|
||||
|
||||
public string SettingText { get; set; }
|
||||
public string SettingText { get; set; }
|
||||
|
||||
public string ValueText { get; set; }
|
||||
public string ValueText { get; set; }
|
||||
|
||||
public string ValueRaw { get; set; }
|
||||
public string ValueRaw { get; set; }
|
||||
|
||||
[JsonIgnore]
|
||||
public bool ValueChanged { get; set; }
|
||||
[JsonIgnore]
|
||||
public bool ValueChanged { get; set; }
|
||||
|
||||
private uint _value;
|
||||
private uint _value;
|
||||
|
||||
public uint Value
|
||||
{
|
||||
get
|
||||
{
|
||||
return _value;
|
||||
}
|
||||
public uint Value
|
||||
{
|
||||
get
|
||||
{
|
||||
return _value;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
_value = value;
|
||||
ValueChanged = true;
|
||||
}
|
||||
}
|
||||
set
|
||||
{
|
||||
_value = value;
|
||||
ValueChanged = true;
|
||||
}
|
||||
}
|
||||
|
||||
public string GroupName { get; set; }
|
||||
public string GroupName { get; set; }
|
||||
|
||||
public int State { get; set; }
|
||||
public int State { get; set; }
|
||||
|
||||
public bool IsStringValue { get; set; }
|
||||
public bool IsStringValue { get; set; }
|
||||
|
||||
public bool IsApiExposed { get; set; }
|
||||
public bool IsApiExposed { get; set; }
|
||||
|
||||
public bool IsSettingHidden { get; set; }
|
||||
public bool IsSettingHidden { get; set; }
|
||||
|
||||
public NvSettingType SettingType { get; set; }
|
||||
public NvSettingType SettingType { get; set; }
|
||||
|
||||
public uint DefaultValue { get; set; }
|
||||
public uint DefaultValue { get; set; }
|
||||
|
||||
public List<NvSettingItemValue> Values { get; set; }
|
||||
public List<NvSettingItemValue> Values { get; set; }
|
||||
|
||||
public string FriendlyName { get; set; }
|
||||
public string Name { get; set; }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return string.Format("{0}; 0x{1:X8}; {2}; {3}; {4};", State, SettingId, SettingText, ValueText, ValueRaw);
|
||||
}
|
||||
public string FriendlyName { get; set; }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return string.Format("{0}; 0x{1:X8}; {2}; {3}; {4};", State, SettingId, SettingText, ValueText, ValueRaw);
|
||||
}
|
||||
}
|
||||
|
||||
public class NvSettingItemValue
|
||||
{
|
||||
public uint Value { get; set; }
|
||||
public string ValueName { get; set; }
|
||||
public uint Value { get; set; }
|
||||
public string ValueName { get; set; }
|
||||
}
|
||||
@@ -185,6 +185,7 @@ public abstract class PresetBase
|
||||
public string name { get; set; }
|
||||
public string shortcut { get; set; }
|
||||
public List<PresetTrigger> Triggers { get; set; }
|
||||
public List<string> Steps { get; set; }
|
||||
public string Description { get; set; }
|
||||
public bool ShowInQuickAccess { get; set; }
|
||||
public DateTime? LastUsed { get; set; }
|
||||
@@ -194,7 +195,8 @@ public abstract class PresetBase
|
||||
|
||||
protected PresetBase()
|
||||
{
|
||||
Triggers = new List<PresetTrigger>();
|
||||
Triggers = [];
|
||||
Steps = [];
|
||||
}
|
||||
|
||||
public int id
|
||||
@@ -289,4 +291,9 @@ public abstract class PresetBase
|
||||
|
||||
return Triggers.First();
|
||||
}
|
||||
|
||||
public void SetSteps(List<string> steps)
|
||||
{
|
||||
Steps = steps;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
namespace ColorControl.Shared.Contracts.RemoteControl;
|
||||
|
||||
public class BaseRcButtonDto<TPreset, TButtonEnum> where TPreset : PresetBase where TButtonEnum : Enum
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public TButtonEnum Button { get; set; }
|
||||
public TPreset Preset { get; set; }
|
||||
public double Top { get; set; }
|
||||
public double Left { get; set; }
|
||||
|
||||
public BaseRcButtonDto()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public BaseRcButtonDto(BaseRcButtonDto<TPreset, TButtonEnum> button)
|
||||
{
|
||||
Update(button);
|
||||
}
|
||||
|
||||
public void Update(BaseRcButtonDto<TPreset, TButtonEnum> button)
|
||||
{
|
||||
Name = button.Name;
|
||||
Button = button.Button;
|
||||
Preset = button.Preset;
|
||||
Top = button.Top;
|
||||
Left = button.Left;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
|
||||
namespace ColorControl.Shared.Contracts.RemoteControl;
|
||||
|
||||
public class BaseRcDto<TPreset, TButtonEnum> where TPreset : PresetBase where TButtonEnum : Enum
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public string ImageFileName { get; set; }
|
||||
public List<BaseRcButtonDto<TPreset, TButtonEnum>> Buttons { get; set; }
|
||||
|
||||
public void Update(BaseRcDto<TPreset, TButtonEnum> remoteSpec)
|
||||
{
|
||||
Name = remoteSpec.Name;
|
||||
ImageFileName = remoteSpec.ImageFileName;
|
||||
Buttons = remoteSpec.Buttons;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ public enum ButtonType
|
||||
KEY_CHDOWN,
|
||||
KEY_ENTER,
|
||||
KEY_RETURN,
|
||||
KEY_EXIT,
|
||||
KEY_HOME,
|
||||
KEY_CH_LIST,
|
||||
KEY_MENU,
|
||||
|
||||
@@ -1,32 +1,9 @@
|
||||
namespace ColorControl.Shared.Contracts.Samsung;
|
||||
using ColorControl.Shared.Contracts.Base;
|
||||
|
||||
public class SamsungDeviceDto
|
||||
namespace ColorControl.Shared.Contracts.Samsung;
|
||||
|
||||
public class SamsungDeviceDto : BaseDeviceDto
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public string IpAddress { get; set; }
|
||||
public string MacAddress { get; set; }
|
||||
public bool IsCustom { get; set; }
|
||||
public string Token { get; set; }
|
||||
public SamsungDeviceOptions Options { get; set; } = new();
|
||||
|
||||
public bool IsDummy { get; set; }
|
||||
|
||||
public bool PoweredOn { get; set; }
|
||||
|
||||
public DateTimeOffset PoweredOffAt { get; set; }
|
||||
|
||||
public bool IsConnected { get; set; }
|
||||
|
||||
public bool IsSelected { get; set; }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
if (IpAddress != null)
|
||||
{
|
||||
return $"{Name} ({IpAddress})";
|
||||
}
|
||||
|
||||
return Name;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,12 +5,10 @@ public class SamsungPreset : PresetBase
|
||||
public static event GetValueByKey GetDeviceName;
|
||||
|
||||
public string AppId { get; set; }
|
||||
public List<string> Steps { get; set; }
|
||||
public string DeviceMacAddress { get; set; }
|
||||
|
||||
public SamsungPreset() : base()
|
||||
{
|
||||
Steps = new List<string>();
|
||||
}
|
||||
|
||||
public SamsungPreset(SamsungPreset preset) : this()
|
||||
|
||||
@@ -1,23 +1,22 @@
|
||||
namespace ColorControl.Shared.Contracts.Samsung;
|
||||
|
||||
|
||||
public static class SamsungFactoryMenuPresets
|
||||
{
|
||||
public static SamsungPreset Preset1 = new("ServiceMenuStep1", null, ["KEY_INFO", "KEY_FACTORY:2000"]);
|
||||
public static SamsungPreset PresetAdvanced = new("ServiceMenuAdvanced", null, ["KEY_UP", "KEY_0", "KEY_0", "KEY_9", "KEY_8"]);
|
||||
public static SamsungPreset PresetAdvancedMonitor = new("ServiceMenuAdvancedMonitor", null, ["KEY_UP", "KEY_0", "KEY_0", "KEY_3", "KEY_8"]);
|
||||
public static SamsungPreset Exit = new("ServiceMenuStep3", null, ["KEY_FACTORY:1500", "KEY_FACTORY"]);
|
||||
public static SamsungPreset KeyFactory = new("ServiceMenuKeyFactory", null, ["KEY_FACTORY"]);
|
||||
public static readonly SamsungPreset Preset1 = new("ServiceMenuStep1", null, ["KEY_INFO", "KEY_FACTORY:2000"]);
|
||||
public static readonly SamsungPreset PresetAdvanced = new("ServiceMenuAdvanced", null, ["KEY_UP", "KEY_0", "KEY_0", "KEY_9", "KEY_8"]);
|
||||
public static readonly SamsungPreset PresetAdvancedMonitor = new("ServiceMenuAdvancedMonitor", null, ["KEY_UP", "KEY_0", "KEY_0", "KEY_3", "KEY_8"]);
|
||||
public static readonly SamsungPreset Exit = new("ServiceMenuStep3", null, ["KEY_FACTORY:1500", "KEY_FACTORY"]);
|
||||
public static readonly SamsungPreset KeyFactory = new("ServiceMenuKeyFactory", null, ["KEY_FACTORY"]);
|
||||
}
|
||||
|
||||
public static class SamsungHospitalityMenuPresets
|
||||
{
|
||||
public static SamsungPreset Preset1 = new("ServiceMenuStep1", null, ["KEY_MUTE:600", "KEY_1:600", "KEY_1:600", "KEY_9:600", "KEY_ENTER:2000"]);
|
||||
public static SamsungPreset Preset2 = new("ServiceMenuStep2", null, ["KEY_1", "KEY_2", "KEY_3", "KEY_4", "KEY_UP"]);
|
||||
public static SamsungPreset Preset3 = new("ServiceMenuStep3", null, ["KEY_VOLUP:5100:Press", "KEY_VOLUP:500:Release"]);
|
||||
public static readonly SamsungPreset Preset1 = new("ServiceMenuStep1", null, ["KEY_MUTE:600", "KEY_1:600", "KEY_1:600", "KEY_9:600", "KEY_ENTER:2000"]);
|
||||
public static readonly SamsungPreset Preset2 = new("ServiceMenuStep2", null, ["KEY_1", "KEY_2", "KEY_3", "KEY_4", "KEY_UP"]);
|
||||
public static readonly SamsungPreset Preset3 = new("ServiceMenuStep3", null, ["KEY_VOLUP:5100:Press", "KEY_VOLUP:500:Release"]);
|
||||
}
|
||||
|
||||
public static class SamsungGenericPresets
|
||||
{
|
||||
public static SamsungPreset Reboot = new("ServiceMenuStep4", null, ["KEY_POWER:5000", "WOL"]);
|
||||
public static readonly SamsungPreset Reboot = new("ServiceMenuStep4", null, ["KEY_POWER:5000", "WOL"]);
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
|
||||
using ColorControl.Shared.Contracts.RemoteControl;
|
||||
|
||||
namespace ColorControl.Shared.Contracts.Samsung;
|
||||
|
||||
public class SamsungRcDto : BaseRcDto<SamsungPreset, ButtonType>
|
||||
{
|
||||
}
|
||||
@@ -1,8 +1,7 @@
|
||||
using ColorControl.Shared.Common;
|
||||
using ColorControl.Shared.Forms;
|
||||
using ColorControl.Shared.Services;
|
||||
using System.Diagnostics;
|
||||
using System.Diagnostics;
|
||||
using System.Management;
|
||||
using ColorControl.Shared.Common;
|
||||
using ColorControl.Shared.Forms;
|
||||
|
||||
namespace ColorControl.Shared.EventDispatcher;
|
||||
|
||||
|
||||
@@ -46,6 +46,13 @@ public class WindowMessageDispatcher : EventDispatcher<WindowMessageEventArgs>
|
||||
|
||||
public WindowMessageDispatcher(GlobalContext globalContext)
|
||||
{
|
||||
// If no GUI is active (using CLI), creating a form may block connections started by the ClientWebSocket.
|
||||
// In that case, skip creating the form and return.
|
||||
if (globalContext.StartUpParams.NoGui)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
MessageForm = new DummyForm();
|
||||
MessageForm.OnMessage += MessageForm_OnMessage;
|
||||
_globalContext = globalContext;
|
||||
|
||||
@@ -1,100 +1,28 @@
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using Newtonsoft.Json.Serialization;
|
||||
using ColorControl.Shared.Common;
|
||||
using static ColorControl.Shared.Common.NestedItemsBuilder;
|
||||
|
||||
namespace ColorControl.Shared.Forms
|
||||
namespace ColorControl.Shared.Forms;
|
||||
|
||||
public class TreeNodeBuilder
|
||||
{
|
||||
public class TreeNodeBuilder
|
||||
public static TreeNode CreateTree(object obj, string text)
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public string Value { get; set; }
|
||||
public List<TreeNode> Children { get; set; } = new List<TreeNode>();
|
||||
var nestedItem = NestedItemsBuilder.CreateTree(obj, text);
|
||||
|
||||
public static TreeNode CreateTree(object obj, string text)
|
||||
return BuildTree(nestedItem);
|
||||
}
|
||||
|
||||
private static TreeNode BuildTree(NestedItem nestedItem)
|
||||
{
|
||||
var treeNode = new TreeNode
|
||||
{
|
||||
var serialized = JsonConvert.SerializeObject(obj, Formatting.None,
|
||||
new JsonSerializerSettings
|
||||
{
|
||||
ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
|
||||
Error = delegate (object sender, ErrorEventArgs args)
|
||||
{
|
||||
//errors.Add(args.ErrorContext.Error.Message);
|
||||
args.ErrorContext.Handled = true;
|
||||
},
|
||||
Converters = new[] { new Newtonsoft.Json.Converters.StringEnumConverter() }
|
||||
});
|
||||
var dic = JsonConvert.DeserializeObject<Dictionary<string, object>>(serialized);
|
||||
var root = new TreeNode();
|
||||
root.Text = text;
|
||||
BuildTree(dic, root);
|
||||
return root;
|
||||
}
|
||||
Text = nestedItem.Value,
|
||||
Name = nestedItem.Name
|
||||
};
|
||||
var nodes = nestedItem.NestedItems.Select(BuildTree);
|
||||
|
||||
private static void BuildTree(object item, TreeNode node)
|
||||
{
|
||||
if (item is KeyValuePair<string, object>)
|
||||
{
|
||||
KeyValuePair<string, object> kv = (KeyValuePair<string, object>)item;
|
||||
TreeNode keyValueNode = new TreeNode();
|
||||
keyValueNode.Name = kv.Key;
|
||||
keyValueNode.Text = kv.Key + ": " + GetValueAsString(kv.Value);
|
||||
node.Nodes.Add(keyValueNode);
|
||||
BuildTree(kv.Value, keyValueNode);
|
||||
}
|
||||
else if (item.GetType() == typeof(JArray))
|
||||
{
|
||||
var list = (JArray)item;
|
||||
int index = 0;
|
||||
foreach (object value in list)
|
||||
{
|
||||
TreeNode arrayItem = new TreeNode();
|
||||
arrayItem.Name = $"[{index}]";
|
||||
arrayItem.Text = $"[{index}]";
|
||||
node.Nodes.Add(arrayItem);
|
||||
BuildTree(value, arrayItem);
|
||||
index++;
|
||||
}
|
||||
}
|
||||
else if (item is Dictionary<string, object>)
|
||||
{
|
||||
Dictionary<string, object> dictionary = (Dictionary<string, object>)item;
|
||||
foreach (KeyValuePair<string, object> d in dictionary)
|
||||
{
|
||||
BuildTree(d, node);
|
||||
}
|
||||
}
|
||||
else if (item.GetType() == typeof(JObject))
|
||||
{
|
||||
var jobject = item as JObject;
|
||||
foreach (var property in jobject.Properties())
|
||||
{
|
||||
BuildTree(new KeyValuePair<string, object>(property.Name, property.Value), node);
|
||||
}
|
||||
}
|
||||
}
|
||||
treeNode.Nodes.AddRange([.. nodes]);
|
||||
|
||||
private static string GetValueAsString(object value)
|
||||
{
|
||||
if (value == null)
|
||||
return "null";
|
||||
var type = value.GetType();
|
||||
if (type.IsArray)
|
||||
{
|
||||
return "[]";
|
||||
}
|
||||
|
||||
if (value.GetType() == typeof(JArray))
|
||||
{
|
||||
var arr = value as JArray;
|
||||
return $"[{arr.Count}]";
|
||||
}
|
||||
|
||||
if (type.IsGenericType || value.GetType() == typeof(JObject))
|
||||
{
|
||||
return "{}";
|
||||
}
|
||||
|
||||
return value.ToString();
|
||||
}
|
||||
return treeNode;
|
||||
}
|
||||
}
|
||||
+57
-18
@@ -1,12 +1,12 @@
|
||||
using ColorControl.Shared.Contracts;
|
||||
using System.ComponentModel;
|
||||
using System.IO;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using ColorControl.Shared.Contracts;
|
||||
using ColorControl.Shared.Forms;
|
||||
using MHC2Gen;
|
||||
using NStandard;
|
||||
using NWin32;
|
||||
using System.ComponentModel;
|
||||
using System.IO;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace ColorControl.Shared.Native
|
||||
{
|
||||
@@ -357,6 +357,12 @@ namespace ColorControl.Shared.Native
|
||||
|
||||
public static bool SetDisplayDefaultColorProfile(string displayName, string profileName, bool fixMaxTML = true, bool isHdrColorProfile = false)
|
||||
{
|
||||
var usePerUser = GetUsePerUserDisplayProfiles(displayName);
|
||||
if (!usePerUser)
|
||||
{
|
||||
SetUsePerUserDisplayProfiles(displayName, true);
|
||||
}
|
||||
|
||||
var adapterId = GetAdapterId(displayName);
|
||||
var sourceId = GetSourceId(displayName);
|
||||
|
||||
@@ -754,7 +760,27 @@ namespace ColorControl.Shared.Native
|
||||
);
|
||||
}
|
||||
|
||||
public static DISPLAYCONFIG_SOURCE_DPI_SCALE_GET GetScaling(string displayName = null)
|
||||
public static readonly uint[] DpiValues = [100, 125, 150, 175, 200, 225, 250, 300, 350, 400, 450, 500];
|
||||
|
||||
public static uint GetDpiScalingInPercentage(string displayName = null)
|
||||
{
|
||||
var dpiInfo = GetDpiScaling(displayName);
|
||||
|
||||
var dpiIndex = Math.Abs(dpiInfo.minScaleRel) + dpiInfo.curScaleRel;
|
||||
|
||||
if (dpiIndex < 0)
|
||||
{
|
||||
dpiIndex = 0;
|
||||
}
|
||||
else if (dpiIndex >= DpiValues.Length)
|
||||
{
|
||||
dpiIndex = DpiValues.Length - 1;
|
||||
}
|
||||
|
||||
return DpiValues[dpiIndex];
|
||||
}
|
||||
|
||||
public static DISPLAYCONFIG_SOURCE_DPI_SCALE_GET GetDpiScaling(string displayName = null)
|
||||
{
|
||||
return ExecuteForModeConfig((modeInfo) =>
|
||||
{
|
||||
@@ -781,23 +807,36 @@ namespace ColorControl.Shared.Native
|
||||
);
|
||||
}
|
||||
|
||||
public static bool SetScaling(int percentage, string displayName = null)
|
||||
public static bool SetDpiScaling(uint percentage, string displayName = null)
|
||||
{
|
||||
var dpiInfo = GetScaling(displayName);
|
||||
var dpiInfo = GetDpiScaling(displayName);
|
||||
|
||||
if (percentage == dpiInfo.curScaleRel)
|
||||
var minAbs = Math.Abs(dpiInfo.minScaleRel);
|
||||
if (DpiValues.Length < (minAbs + dpiInfo.maxScaleRel + 1))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var dpiIndex = DpiValues.IndexOf(percentage);
|
||||
if (dpiIndex < 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
var scaleRel = dpiInfo.minScaleRel + dpiIndex;
|
||||
|
||||
if (scaleRel == dpiInfo.curScaleRel)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (percentage < dpiInfo.minScaleRel)
|
||||
{
|
||||
percentage = dpiInfo.minScaleRel;
|
||||
}
|
||||
else if (percentage > dpiInfo.maxScaleRel)
|
||||
{
|
||||
percentage = dpiInfo.maxScaleRel;
|
||||
}
|
||||
//if (percentage < dpiInfo.minScaleRel)
|
||||
//{
|
||||
// percentage = dpiInfo.minScaleRel;
|
||||
//}
|
||||
//else if (percentage > dpiInfo.maxScaleRel)
|
||||
//{
|
||||
// percentage = dpiInfo.maxScaleRel;
|
||||
//}
|
||||
|
||||
return ExecuteForModeConfig((modeInfo) =>
|
||||
{
|
||||
@@ -811,7 +850,7 @@ namespace ColorControl.Shared.Native
|
||||
setPacket.header.adapterId = modeInfo.adapterId;
|
||||
setPacket.header.id = modeInfo.id;
|
||||
|
||||
setPacket.scaleRel = -7;
|
||||
setPacket.scaleRel = scaleRel;
|
||||
|
||||
var err = NativeMethods.DisplayConfigSetDeviceInfo(ref setPacket);
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
using ColorControl.Shared.Common;
|
||||
using System.ComponentModel;
|
||||
using System.Windows;
|
||||
using ColorControl.Shared.Common;
|
||||
using ColorControl.Shared.XForms;
|
||||
using Microsoft.Win32;
|
||||
using System.ComponentModel;
|
||||
using System.Windows;
|
||||
|
||||
namespace novideo_srgb
|
||||
{
|
||||
@@ -160,6 +160,16 @@ namespace novideo_srgb
|
||||
_novideoWindow?.ApplyClamp(monitorId, clamp, targetColorSpace);
|
||||
}
|
||||
|
||||
public static (bool clamped, int targetColorSpace) GetSettings(string monitorId)
|
||||
{
|
||||
if (_novideoWindow == null)
|
||||
{
|
||||
return (false, 0);
|
||||
}
|
||||
|
||||
return _novideoWindow.GetSettingsForMonitor(monitorId);
|
||||
}
|
||||
|
||||
private void ApplyClamp(string monitorId, bool clamp, int targetColorSpace)
|
||||
{
|
||||
var monitor = _viewModel.Monitors.FirstOrDefault(m => monitorId == null || m.Path.Contains(monitorId));
|
||||
@@ -172,5 +182,17 @@ namespace novideo_srgb
|
||||
monitor.Target = targetColorSpace;
|
||||
monitor.Clamped = clamp;
|
||||
}
|
||||
|
||||
private (bool clamped, int targetColorSpace) GetSettingsForMonitor(string monitorId)
|
||||
{
|
||||
var monitor = _viewModel.Monitors.FirstOrDefault(m => monitorId == null || m.Path.Contains(monitorId));
|
||||
|
||||
if (monitor == null)
|
||||
{
|
||||
return (false, 0);
|
||||
}
|
||||
|
||||
return (monitor.Clamped, monitor.Target);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user