fix some issues, add ddc settings, make new ui default

This commit is contained in:
Maassoft committed 2025-09-28 15:10:25 +02:00
1 parent 827157f30f
commit 1c22b2daf8
52 files changed
+5258 -3770

No files matched your search

+102 -27
View File
@@ -1,43 +1,118 @@
using ColorControl.Shared.Contracts;
using ColorControl.UI.Services;
using Microsoft.AspNetCore.Hosting.Server;
using Microsoft.AspNetCore.Hosting.Server.Features;
using System.Net;
namespace ColorControl.UI;
public static class Blazor
{
public static void Start(Config? config = null)
{
var builder = WebApplication.CreateBuilder();
private static WebApplication? CurrentApplication;
private static int Port;
private static bool AllowRemoteConnections;
// Add services to the container.
builder.Services.AddRazorComponents()
.AddInteractiveServerComponents();
public static bool IsRunning(Config config) => CurrentApplication != null && Port == config.UiPort && AllowRemoteConnections == config.UiAllowRemoteConnections;
builder.Services.AddSingleton<AppState>(new AppState { SelectedTheme = (config?.UseDarkMode ?? true) ? "dark" : "light" });
builder.Services.AddTransient<RpcUiClientService>();
builder.Services.AddTransient<JSHelper>();
builder.Services.AddHttpContextAccessor();
builder.Services.AddSingleton<NotificationService>();
builder.WebHost.ConfigureKestrel(o => o.ListenAnyIP(config?.UiPort > 0 ? config.UiPort : 5000));
public static async Task Start(Config config)
{
if (IsRunning(config))
{
return;
}
var app = builder.Build();
await Stop();
// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error", createScopeForErrors: true);
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
var builder = WebApplication.CreateBuilder();
app.UseHttpsRedirection();
// Add services to the container.
builder.Services.AddRazorComponents()
.AddInteractiveServerComponents();
app.UseStaticFiles();
app.UseAntiforgery();
builder.Services.AddSingleton<AppState>(new AppState { SelectedTheme = config.UseDarkMode ? "dark" : "light" });
builder.Services.AddTransient<RpcUiClientService>();
builder.Services.AddTransient<JSHelper>();
builder.Services.AddHttpContextAccessor();
builder.Services.AddSingleton<NotificationService>();
app.MapRazorComponents<Components.App>()
.AddInteractiveServerRenderMode();
builder.Services.Configure<HostOptions>(opts => opts.ShutdownTimeout = TimeSpan.FromSeconds(3));
var listenPort = config.UiPort > 0 ? config.UiPort : 0;
builder.WebHost.ConfigureKestrel(options =>
{
if (config.UiAllowRemoteConnections)
{
options.ListenAnyIP(listenPort);
}
else
{
options.Listen(IPAddress.Parse("127.0.0.1"), listenPort);
}
});
var app = builder.Build();
// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error", createScopeForErrors: true);
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseAntiforgery();
app.MapRazorComponents<Components.App>()
.AddInteractiveServerRenderMode();
CurrentApplication = app;
Port = config.UiPort;
AllowRemoteConnections = config.UiAllowRemoteConnections;
await app.RunAsync();
}
public static async Task Stop()
{
if (CurrentApplication != null)
{
await CurrentApplication.StopAsync();
CurrentApplication = null;
}
}
public static string GetCurrentUrl()
{
var port = GetCurrentPort();
return $"http://localhost:{port}";
}
public static int GetCurrentPort()
{
if (CurrentApplication == null)
{
return -1;
}
var server = CurrentApplication.Services.GetService<IServer>();
var addressFeature = server?.Features.Get<IServerAddressesFeature>();
if (addressFeature == null)
{
return -1;
}
foreach (var address in addressFeature.Addresses)
{
return int.Parse(address.Split(':').Last());
}
return -1;
}
app.Run();
}
}
@@ -51,6 +51,16 @@
<label class="form-label" for="description">Description</label>
<input class="form-control" type="text" id="description" @bind="ColorProfile.Description" />
</div>
<div class="mb-2">
<label class="form-label" for="renderingIntent">Rendering intent</label>
<select class="form-select" id="renderingIntent" @bind="ColorProfile.RenderingIntent">
@foreach (var renderingIntent in Enum.GetValues<LittleCms.RenderingIntent>().Where(ri => ri <= LittleCms.RenderingIntent.ABSOLUTE_COLORIMETRIC))
{
<option value="@renderingIntent">@renderingIntent.GetDescription()</option>
}
</select>
</div>
<div class="accordion accordion-flush" id="presetAccordion">
<div class="accordion-item">
@@ -236,21 +246,49 @@
</div>
</div>
</div>
<div class="row">
<div class="col">
<div class="mb-2">
<label class="form-label" for="brightnessBoost">Brightness boost (%)</label>
<input class="form-control" id="brightnessBoost" type="number" min="-100" max="100" @bind="ColorProfile.SDRBrightnessBoost" disabled="@(ColorProfile.SDRTransferFunction == SDRTransferFunction.ToneMappedPiecewise)" />
</div>
</div>
<div class="col">
<div class="mb-2">
<label class="form-label" for="shadowDetailBoost">Shadow detail boost (%)</label>
<input class="form-control" id="shadowDetailBoost" type="number" min="0" max="100" @bind="ColorProfile.ShadowDetailBoost" disabled="@(ColorProfile.SDRTransferFunction == SDRTransferFunction.Piecewise || ColorProfile.SDRTransferFunction == SDRTransferFunction.ToneMappedPiecewise)" />
</div>
}
<div class="row">
<div class="col">
<div class="mb-2">
<label class="form-label" for="brightnessBoost">Brightness boost (%)</label>
<input class="form-control" id="brightnessBoost" type="number" min="-100" max="100" @bind="ColorProfile.SDRBrightnessBoost" disabled="@(ColorProfile.SDRTransferFunction == SDRTransferFunction.ToneMappedPiecewise)" />
</div>
</div>
<div class="col">
<div class="mb-2">
<label class="form-label" for="shadowDetailBoost">Shadow detail boost (%)</label>
<input class="form-control" id="shadowDetailBoost" type="number" min="-50" max="200" @bind="ColorProfile.ShadowDetailBoost" disabled="@(ColorProfile.SDRTransferFunction == SDRTransferFunction.ToneMappedPiecewise)" />
</div>
</div>
<div class="col">
<div class="mb-2">
<label class="form-label" for="shadowDetailRange">Shadow detail range (%)</label>
<input class="form-control" id="shadowDetailRange" type="number" min="0" max="100" @bind="ColorProfile.ShadowDetailRange" disabled="@(ColorProfile.SDRTransferFunction == SDRTransferFunction.ToneMappedPiecewise)" />
</div>
</div>
</div>
<div class="row">
<div class="col">
<div class="mb-2">
<label class="form-label" for="redGain">Red gain (%)</label>
<input class="form-control" id="redGain" type="number" min="1" max="200" step="0.01" @bind="ColorProfile.RedGain" disabled="@(ColorProfile.SDRTransferFunction == SDRTransferFunction.ToneMappedPiecewise)" />
</div>
</div>
<div class="col">
<div class="mb-2">
<label class="form-label" for="greenGain">Green gain (%)</label>
<input class="form-control" id="greenGain" type="number" min="1" max="200" step="0.01" @bind="ColorProfile.GreenGain" disabled="@(ColorProfile.SDRTransferFunction == SDRTransferFunction.ToneMappedPiecewise)" />
</div>
</div>
<div class="col">
<div class="mb-2">
<label class="form-label" for="blueGain">Blue gain (%)</label>
<input class="form-control" id="blueGain" type="number" min="1" max="200" step="0.01" @bind="ColorProfile.BlueGain" disabled="@(ColorProfile.SDRTransferFunction == SDRTransferFunction.ToneMappedPiecewise)" />
</div>
</div>
</div>
@if (ColorProfile.IsHDRProfile)
{
<div class="row">
<div class="col">
<div class="mb-2">
@@ -280,17 +318,6 @@
</div>
</div>
}
else
{
<div class="row">
<div class="col">
<div class="mb-2">
<label class="form-label" for="shadowDetailBoost">Shadow detail boost (%)</label>
<input class="form-control" id="shadowDetailBoost" type="number" min="0" max="100" @bind="ColorProfile.ShadowDetailBoost" disabled="@(ColorProfile.SDRTransferFunction == SDRTransferFunction.Piecewise)" />
</div>
</div>
</div>
}
</div>
</div>
</div>
@@ -68,6 +68,10 @@ else
{
<span class="ms-2 badge text-bg-warning" title="Show in Quick Access">QA</span>
}
@if (!preset.PathExists)
{
<span class="ms-2 badge text-bg-danger" title="@($"Path '{preset.Path}' is not found")">Not found</span>
}
</h5>
</div>
<span class="d-none d-lg-block">
@@ -46,7 +46,7 @@
<div class="mb-2">
<label class="form-label" for="@fieldId">@field.Label</label>
<select class="form-select" id="@fieldId" @bind="field.StringValue">
<select class="form-select" id="@fieldId" @bind="field.StringValue" @bind:after="() => FieldBindAfter(field)">
@if (field.ValueType != null)
{
@foreach (var item in Enum.GetValues(field.ValueType))
@@ -111,4 +111,12 @@
@code {
[Parameter]
public List<FieldDefinition>? Fields { get; set; }
[Parameter]
public EventCallback<FieldDefinition> FieldValueChanged { get; set; }
private async Task FieldBindAfter(FieldDefinition field)
{
await FieldValueChanged.InvokeAsync(field);
}
}
@@ -112,5 +112,4 @@
ExtraLabelStyling = LabelStylingFunc?.Invoke(Value);
}
}
@@ -19,89 +19,170 @@
<div class="d-flex ms-auto">
@if (NvPresets != null || SamsungPresets != null || LgPresets != null || CustomItems != null)
{
<div class="dropdown dropend mb-1">
<div class="dropdown dropstart 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: 360px">
@if (NvPresets != null && NvPresetsEnabled)
{
<div class="mb-2">
<label class="form-label" for="nvPreset">NVIDIA preset</label>
<select class="form-select" multiple id="nvPreset" @onchange="NvPresetChange">
@foreach (var preset in NvPresets)
{
<option value="@preset.id">@preset.IdOrName</option>
}
</select>
</div>
}
@if (SamsungPresets != null && SamsungPresetsEnabled)
{
<div class="mb-2">
<label class="form-label" for="samsungPreset">Samsung preset</label>
<select class="form-select" id="samsungPreset" @bind="SamsungPresetToAddId">
<option value="">Select preset</option>
@foreach (var preset in SamsungPresets)
{
<option value="@preset.id">@preset.IdOrName</option>
}
</select>
</div>
}
@if (LgPresets != null && LgPresetsEnabled)
{
<div class="mb-2">
<label class="form-label" for="lgPreset">LG preset</label>
<select class="form-select" id="lgPreset" @bind="LgPresetToAddId">
<option value="">Select preset</option>
@foreach (var preset in LgPresets)
{
<option value="@preset.id">@preset.IdOrName</option>
}
</select>
</div>
}
@if (CustomItems != null)
{
foreach (var customItem in CustomItems)
{
<div class="mb-2">
<label class="form-label" for="customItem">@customItem.Name</label>
<select class="form-select" id="customItem" @bind="customItem.SelectedItem">
<option value="">Select item</option>
@foreach (var item in customItem.Items)
{
<option value="@item">@item</option>
}
</select>
</div>
<form class="dropdown-menu p-3 dropdown-menu-end" style="width: 21rem; height: 430px">
<ul class="nav nav-tabs" id="presetTab" role="tablist">
@{
var linkActive = "active";
}
}
<StartProgramInput @bind-Path="StartProgramPath" @bind-Parameters="StartProgramParameters"></StartProgramInput>
@if (ActionItems != null)
{
foreach (var customItem in ActionItems)
@if (NvPresets != null && NvPresetsEnabled)
{
<div class="mb-2">
<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)
<li class="nav-item" role="presentation">
<button class="nav-link px-2 @(linkActive)" id="nvidia-tab" data-bs-toggle="tab" data-bs-target="#nvidia-tab-pane" type="button" role="tab" aria-controls="nvidia-tab-pane" aria-selected="true">NVIDIA</button>
</li>
linkActive = "";
}
@if (SamsungPresets != null && SamsungPresetsEnabled)
{
<li class="nav-item" role="presentation">
<button class="nav-link px-2 @(linkActive)" id="samsung-tab" data-bs-toggle="tab" data-bs-target="#samsung-tab-pane" type="button" role="tab" aria-controls="samsung-tab-pane" aria-selected="true">Samsung</button>
</li>
linkActive = "";
}
@if (LgPresets != null && LgPresetsEnabled)
{
<li class="nav-item" role="presentation">
<button class="nav-link px-2 @(linkActive)" id="lg-tab" data-bs-toggle="tab" data-bs-target="#lg-tab-pane" type="button" role="tab" aria-controls="lg-tab-pane" aria-selected="true">LG</button>
</li>
linkActive = "";
}
@if (CustomItems != null)
{
<li class="nav-item" role="presentation">
<button class="nav-link px-2 @(linkActive)" id="custom-tab" data-bs-toggle="tab" data-bs-target="#custom-tab-pane" type="button" role="tab" aria-controls="custom-tab-pane" aria-selected="true">Custom</button>
</li>
linkActive = "";
}
<li class="nav-item" role="presentation">
<button class="nav-link px-2 @(linkActive)" id="program-tab" data-bs-toggle="tab" data-bs-target="#program-tab-pane" type="button" role="tab" aria-controls="program-tab-pane" aria-selected="true">Program</button>
</li>
@if (ActionItems != null)
{
linkActive = "";
<li class="nav-item" role="presentation">
<button class="nav-link px-2 @(linkActive)" id="action-tab" data-bs-toggle="tab" data-bs-target="#action-tab-pane" type="button" role="tab" aria-controls="action-tab-pane" aria-selected="true">Action</button>
</li>
}
</ul>
<div class="tab-content" id="presetTabContent">
@{
var tabActive = "show active";
}
@if (NvPresets != null && NvPresetsEnabled)
{
<div class="tab-pane fade @(tabActive)" id="nvidia-tab-pane" role="tabpanel" aria-labelledby="nvidia-tab">
<div class="mt-3 mb-3">
<label class="form-label" for="nvPreset">NVIDIA preset</label>
<select class="form-select" multiple id="nvPreset" @onchange="NvPresetChange" style="height: 250px">
@foreach (var preset in NvPresets)
{
<option value="@preset.id">@preset.IdOrName</option>
}
</select>
</div>
<button type="button" class="btn btn-sm btn-primary" @onclick="() => AddNvPresetOnClick()" disabled="@(NvPresetsToAdd.Count == 0)">Add item(s)</button>
</div>
tabActive = "";
}
@if (SamsungPresets != null && SamsungPresetsEnabled)
{
<div class="tab-pane fade @(tabActive)" id="samsung-tab-pane" role="tabpanel" aria-labelledby="samsung-tab">
<div class="mt-3 mb-3">
<label class="form-label" for="samsungPreset">Samsung preset</label>
<select class="form-select" id="samsungPreset" @bind="SamsungPresetToAddId">
<option value="">Select preset</option>
@foreach (var preset in SamsungPresets)
{
<option value="@preset.id">@preset.IdOrName</option>
}
</select>
</div>
<button type="button" class="btn btn-sm btn-primary" @onclick="() => AddSamsungPresetOnClick()">Add item</button>
</div>
tabActive = "";
}
@if (LgPresets != null && LgPresetsEnabled)
{
<div class="tab-pane fade @(tabActive)" id="lg-tab-pane" role="tabpanel" aria-labelledby="lg-tab">
<div class="mt-3 mb-3">
<label class="form-label" for="lgPreset">LG preset</label>
<select class="form-select" id="lgPreset" @bind="LgPresetToAddId">
<option value="">Select preset</option>
@foreach (var preset in LgPresets)
{
<option value="@preset.id">@preset.IdOrName</option>
}
</select>
</div>
<button type="button" class="btn btn-sm btn-primary" @onclick="() => AddLgPresetOnClick()">Add item</button>
</div>
tabActive = "";
}
@if (CustomItems != null)
{
<div class="tab-pane fade @(tabActive)" id="custom-tab-pane" role="tabpanel" aria-labelledby="custom-tab">
@foreach (var customItem in CustomItems)
{
<div class="mt-3 mb-3">
<label class="form-label" for="customItem">@customItem.Name</label>
<select class="form-select" id="customItem" @bind="customItem.SelectedItem">
<option value="">Select item</option>
@foreach (var item in customItem.Items)
{
<option value="@item.Name">@item.Title</option>
<option value="@item">@item</option>
}
</optgroup>
}
</select>
</select>
</div>
}
<button type="button" class="btn btn-sm btn-primary" @onclick="() => AddCustomItemsOnClick()">Add item</button>
</div>
<FieldDefs Fields="ActionFields"></FieldDefs>
tabActive = "";
}
}
<button type="button" class="btn btn-sm btn-primary" @onclick="() => AddPresetOnClick()">Add items</button>
<div class="tab-pane fade @(tabActive)" id="program-tab-pane" role="tabpanel" aria-labelledby="program-tab">
<div class="mt-3 mb-3">
<StartProgramInput @bind-Path="StartProgramPath" @bind-Parameters="StartProgramParameters"></StartProgramInput>
</div>
<button type="button" class="btn btn-sm btn-primary" @onclick="() => AddStartProgramOnClick()">Add item</button>
</div>
@{
tabActive = "";
}
@if (ActionItems != null)
{
<div class="tab-pane fade @(tabActive)" id="action-tab-pane" role="tabpanel" aria-labelledby="action-tab">
<div class="mt-3 mb-3">
@foreach (var customItem in ActionItems)
{
<div class="mb-2">
<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>
}
</select>
</div>
<FieldDefs Fields="ActionFields"></FieldDefs>
}
</div>
<button type="button" class="btn btn-sm btn-primary" @onclick="() => AddActionItemsOnClick()">Add item</button>
</div>
}
</div>
</form>
</div>
}
@@ -121,7 +202,7 @@
{
var stepIndex = index;
<button type="button" class="btn btn-outline-primary dropdown-toggle text-break text-wrap" data-bs-toggle="dropdown" aria-expanded="false" data-bs-auto-close="outside">@step.Raw</button>
<button type="button" class="btn btn-outline-primary dropdown-toggle text-break text-wrap" data-bs-toggle="dropdown" aria-expanded="false" data-bs-auto-close="outside" @onclick="() => PresetStepClick(step)">@step.Raw</button>
<form class="dropdown-menu p-4 dropdown-menu-end" style="width: 200px">
<div class="mb-2">
<button type="button" class="btn btn-primary btn-sm" data-bs-dismiss="dropdown" title="Move left" disabled="@(stepIndex == 0)" @onclick="() => MoveLeftOnClick(stepIndex)">&lt;</button>
@@ -129,6 +210,10 @@
<button type="button" class="btn btn-primary btn-sm" data-bs-dismiss="dropdown" @onclick="() => RemoveOnClick(stepIndex)">Remove</button>
</div>
<RangeInput TValue="int" @bind-Value="step.Delay" Id="delay" Label="Delay" Unit="ms" Max="10000" @bind-Value:after="() => AfterDelayChanged()" />
@if (ActionFieldsEdit != null)
{
<FieldDefs Fields="ActionFieldsEdit" FieldValueChanged="ActionFieldValueChanged"></FieldDefs>
}
</form>
index++;
@@ -167,6 +252,8 @@
[Parameter]
public bool LgPresetsEnabled { get; set; } = true;
private PresetStep? CurrentPresetStep;
public class ActionStepItem
{
public string Name { get; private set; }
@@ -219,6 +306,7 @@
private string? RawText;
private List<FieldDefinition>? ActionFields;
private List<FieldDefinition>? ActionFieldsEdit;
private string? StartProgramPath;
private string? StartProgramParameters;
@@ -229,9 +317,16 @@
{
if (Steps != null)
{
var stepIndex = CurrentPresetStep != null ? PresetSteps.IndexOf(CurrentPresetStep) : -1;
PresetSteps.Clear();
PresetSteps.AddRange(Steps.Select(s => PresetStep.Parse(s)));
RawText = string.Join(", ", PresetSteps);
if (stepIndex > -1 && stepIndex < PresetSteps.Count)
{
CurrentPresetStep = PresetSteps[stepIndex];
}
}
}
@@ -296,6 +391,72 @@
await EmitSteps();
}
private async Task AddNvPresetOnClick()
{
NvPresetsToAdd.ForEach(id => AddPreset(NvPresets, "NvPreset", id));
await EmitSteps();
}
private async Task AddSamsungPresetOnClick()
{
AddPreset(SamsungPresets, "SamsungPreset", SamsungPresetToAddId);
await EmitSteps();
}
private async Task AddLgPresetOnClick()
{
AddPreset(LgPresets, "LgPreset", LgPresetToAddId);
await EmitSteps();
}
private async Task AddCustomItemsOnClick()
{
if (CustomItems != null && Steps != null)
{
foreach (var customItem in CustomItems)
{
if (string.IsNullOrWhiteSpace(customItem.SelectedItem))
{
continue;
}
PresetSteps.Add(PresetStep.Parse(customItem.SelectedItem));
}
}
await EmitSteps();
}
private async Task AddActionItemsOnClick()
{
if (ActionItems != null && Steps != null)
{
foreach (var customItem in ActionItems)
{
if (string.IsNullOrWhiteSpace(customItem.SelectedActionName))
{
continue;
}
var stepString = customItem.GetStepString(ActionFields);
PresetSteps.Add(PresetStep.Parse(stepString));
}
}
await EmitSteps();
}
private async Task AddStartProgramOnClick()
{
AddStartProgram();
await EmitSteps();
}
private async Task AddPresetOnClick()
{
NvPresetsToAdd.ForEach(id => AddPreset(NvPresets, "NvPreset", id));
@@ -457,4 +618,34 @@
NvPresetsToAdd.AddRange(items.Select(i => int.Parse(i)));
}
}
private void PresetStepClick(PresetStep step)
{
CurrentPresetStep = step;
var action = ActionItems?.SelectMany(ai => ai.Actions).FirstOrDefault(a => a.Name.Equals(step.ActionName, StringComparison.OrdinalIgnoreCase));
if (action == null)
{
ActionFieldsEdit = null;
return;
}
var field = CreateField(action);
field.StringValue = step.ActionValue;
ActionFieldsEdit = [field];
}
private async Task ActionFieldValueChanged(FieldDefinition field)
{
if (CurrentPresetStep == null)
{
return;
}
CurrentPresetStep.ActionValue = field.StringValue;
await EmitSteps();
}
}
@@ -524,6 +524,86 @@
</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.DdcSettings.ApplyDdc" id="applyDdc">
}
<button class="accordion-button collapsed py-2 @(Preset.DdcSettings.ApplyDdc ? "text-warning fw-bold" : "")" type="button" data-bs-toggle="collapse" data-bs-target="#ddcSettings">
DDC settings
</button>
</span>
</h2>
<div id="ddcSettings" class="accordion-collapse collapse" data-bs-parent="#presetAccordion">
<div class="accordion-body">
@if (!Preset.DdcSettings.Settings.Any())
{
<div>
<label class="form-label">No settings found. Click to add:</label>
<button class="btn btn-primary btn-sm" type="button" @onclick="() => Preset.DdcSettings.Settings.Add(new DdcSetting())">Add</button>
</div>
}
@foreach (var ddcSetting in Preset.DdcSettings.Settings)
{
<div class="mb-2">
<label class="form-label" for="vcpCode">VCP code</label>
<select class="form-select" id="vcpCode" @bind="ddcSetting.VcpHexCode" @bind:after="() => AfterVcpChanged(ddcSetting)">
<option value="">Not selected</option>
@foreach (var vcpCodeKeyValue in VcpCodes.CodeList)
{
<option value="@vcpCodeKeyValue.Key">@($"{vcpCodeKeyValue.Key}: {(string.IsNullOrEmpty(vcpCodeKeyValue.Value) ? "Unknown" : vcpCodeKeyValue.Value)}")</option>
}
</select>
</div>
<div class="mb-2">
<label class="form-label" for="ddcValue">Value</label>
<input class="form-control" type="number" @bind="ddcSetting.Value" id="ddcValue">
@if (ddcSetting.MaxValue != 0)
{
<div id="ddcValue_help" class="form-text">
Max. value: @ddcSetting.MaxValue
</div>
}
</div>
@if (!Preset.IsDisplayPreset)
{
<div class="mb-2">
<label class="form-label" for="ddcValueChangeType">Value change type</label>
<select class="form-select" id="ddcValueChangeType" @bind="ddcSetting.ValueChangeType">
@foreach (var changeType in Enum.GetValues<DdcSetting.ValueChangeTypes>())
{
<option value="@changeType">@changeType.GetDescription()</option>
}
</select>
</div>
}
}
@if (!string.IsNullOrEmpty(DdcErrorMessage))
{
<div class="alert alert-danger align-items-center" role="alert">
<div>
@DdcErrorMessage
</div>
</div>
}
<div class="mb-2">
<input class="form-check-input" type="checkbox" @bind="Preset.DdcSettings.RemoveOutOfRangeOsd" id="ddcRemoveOsd">
<label class="form-check-label" for="ddcRemoveOsd">
Try to remove "Out of range" OSD
</label>
</div>
</div>
</div>
</div>
@if (ShowOverclocking)
{
<div class="accordion-item">
@@ -560,39 +640,40 @@
</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>
@if (!Preset.IsDisplayPreset)
{
<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>
<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 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>
}
</div>
}
@@ -647,6 +728,8 @@
private NvPreset? OriginalPreset;
private bool Applying;
private bool Queued;
private bool SkipNextApply;
private string? DdcErrorMessage;
protected override async Task OnParametersSetAsync()
{
@@ -674,6 +757,11 @@
{
resolutions = await _rpcClientService.CallAsync<List<VirtualResolution>>("NvService", "GetAvailableResolutionsV2", Preset);
refreshRates = await _rpcClientService.CallAsync<List<Rational>>("NvService", "GetAvailableRefreshRatesV2", Preset);
if (Preset.IsDisplayPreset && !Preset.DdcSettings.Settings.Any())
{
Preset.DdcSettings.Settings.Add(new DdcSetting());
}
}
selectedActiveResolution = Preset?.DisplayConfig.Resolution?.GetActiveString();
selectedVirtualResolution = Preset?.DisplayConfig.Resolution?.GetVirtualString();
@@ -698,6 +786,12 @@
private async Task DoApplyImmediately()
{
if (SkipNextApply)
{
SkipNextApply = false;
return;
}
if (IsVisible && OriginalPreset != null && Preset?.IsDisplayPreset == true)
{
if (Applying)
@@ -722,7 +816,7 @@
Applying = false;
}
while (Queued);
while (Queued && !SkipNextApply);
}
}
@@ -743,9 +837,18 @@
if (Preset.IsDisplayPreset)
{
Preset.UpdateAutoApplySettings(PresetParam);
await ApplyDisplayPreset();
if (OriginalPreset != null)
{
Preset.UpdateAutoApplySettings(OriginalPreset, true);
}
if (Preset.HasApplicableSettings)
{
Preset.UpdateAutoApplySettings(PresetParam);
await ApplyDisplayPreset();
}
}
else
{
@@ -768,7 +871,7 @@
private async Task ApplyDisplayPreset()
{
var result = await _rpcClientService.CallAsync<bool>("NvService", "ApplyPreset", Preset);
var result = await _rpcClientService.CallGenericBoolResultAsync("NvService", "ApplyPresetWithResult", Preset);
}
private void HdmiSettingsOnInput(ChangeEventArgs e)
@@ -864,4 +967,30 @@
{
await _localStorage.SetAsync($"{nameof(NvPreset)}_{nameof(ApplyImmediately)}", ApplyImmediately);
}
private async Task AfterVcpChanged(DdcSetting ddcSetting)
{
SkipNextApply = true;
if (Preset?.IsDisplayPreset != true || Preset.DisplayId == null)
{
return;
}
var result = await _rpcClientService.CallAsync<GenericResult<VcpInfo>>("NvService", "GetVcpInfo", Preset.DisplayId, ddcSetting.VcpCode);
SkipNextApply = true;
if (result.HasResult)
{
DdcErrorMessage = null;
ddcSetting.Value = result.Result.Value;
ddcSetting.MaxValue = result.Result.MaxValue;
}
else
{
DdcErrorMessage = result.ErrorMessages.FirstOrDefault();
ddcSetting.Value = 0;
ddcSetting.MaxValue = 0;
}
}
}
@@ -245,6 +245,10 @@ else
{
<li class="list-group-item p-1">Color profile: @preset.ColorProfileSettings.ProfileName</li>
}
@if (preset.DdcSettings.ApplyDdc && preset.DdcSettings.Settings.Count > 0)
{
<li class="list-group-item p-1">DDC: @preset.DdcSettings.Settings[0]</li>
}
</ul>
</small>
<small class="text-muted">@preset.shortcut</small>
+57 -2
View File
@@ -11,6 +11,8 @@
@inject AppState AppState
@inject NavigationManager _navigationManager
@inject NotificationService _notificationService
@inject JSHelper _jsHelper;
@inject IHttpContextAccessor _httpContextAccessor
<PageTitle>Options</PageTitle>
@@ -81,9 +83,26 @@ else
</div>
<div class="mb-3">
<label class="form-label" for="webUiPort">
Web UI port
Web UI port (use 0 for automatic assignment)
</label>
@if (IsChangingUiPort)
{
<div class="alert alert-warning mb-2" role="alert">
Changing the UI port. If the UI is not reloaded correctly, close this window and relaunch the UI.
</div>
}
<input class="form-control" type="number" @bind="config.UiPort" id="webUiPort" @bind:after="UiPortBlur">
@if (config.CurrentUiPort > 0)
{
<div id="uiPortHelp" class="form-text">Currently listening on port @config.CurrentUiPort</div>
}
</div>
<div class="mb-3 form-check">
<input class="form-check-input" type="checkbox" @bind="config.UiAllowRemoteConnections" id="uiAllowRemoteConnections" @bind:after="UiAllowRemoteConnectionsChange">
<label class="form-check-label" for="uiAllowRemoteConnections">
Web UI: Allow remote connections
</label>
<div id="uiAllowRemoteConnectionsHelp" class="form-text">Allows or disallows remote connections to the UI. Only enable this if you want to access the UI from another device (eg. your phone) than the system ColorControl is running on</div>
</div>
<div class="mb-3 form-check">
<input class="form-check-input" type="checkbox" @bind="config.MinimizeOnClose" id="minimizeOnClose" @oninput="MinimizeOnCloseInput">
@@ -183,10 +202,13 @@ else
@code {
private Config? config;
private bool IsEmbeddedBrowser;
private bool IsChangingUiPort;
protected override async Task OnInitializedAsync()
{
config = await _rpcClientService.CallAsync<Config>("OptionsService", "GetConfig");
IsEmbeddedBrowser = _httpContextAccessor.HttpContext?.Request.Headers["user-agent"].ToString().Contains("ColorControlEmbedded") == true;
}
private async Task SaveConfig()
@@ -313,6 +335,39 @@ else
private async Task UiPortBlur()
{
await _rpcClientService.CallAsync<bool>("OptionsService", "SetUiPort", config!.UiPort);
if (config == null)
{
return;
}
IsChangingUiPort = true;
if (config.UiPort > 0 || IsEmbeddedBrowser)
{
await NavigateToNewPort(config.UiPort, 5000);
}
var result = await _rpcClientService.CallAsync<bool>("OptionsService", "SetUiPort", config.UiPort);
}
private async Task UiAllowRemoteConnectionsChange()
{
await _rpcClientService.CallAsync<bool>("OptionsService", "SetUiAllowRemoteConnections", config!.UiAllowRemoteConnections);
}
private async Task NavigateToNewPort(int port, int timeout = 0)
{
var uri = new UriBuilder(_navigationManager.Uri);
uri.Port = port;
var newUri = uri.Uri.ToString();
if (timeout == 0)
{
_navigationManager.NavigateTo(uri.ToString(), true);
}
else
{
await _jsHelper.NavigateTo(newUri, timeout);
}
}
}
+5
View File
@@ -34,6 +34,11 @@ public class JSHelper(IJSRuntime jsRuntime)
await InvokeVoidAsync("ShowToast", id);
}
public async Task NavigateTo(string uri, int timeout = 0)
{
await InvokeVoidAsync("NavigateTo", uri, timeout);
}
public async Task<RectDimension> GetElementDimensions(string id)
{
jsModule = await LoadModule();
+64 -54
View File
@@ -7,83 +7,93 @@ namespace ColorControl.UI.Services;
public class RpcUiClientOptions
{
public int Timeout { get; set; } = PipeUtils.DefaultTimeout;
public int Timeout { get; set; } = PipeUtils.DefaultTimeout;
}
public class RpcUiClientService(NotificationService notificationService)
{
public string? LastErrorMessage { get; private set; }
public string? LastErrorMessage { get; private set; }
public async Task<T> CallAsync<T>(string serviceName, string method, params object?[] arguments)
{
var rpcMessage = new SvcRpcMessage
{
ServiceName = serviceName,
MethodName = method,
Arguments = arguments
};
public async Task<T> CallAsync<T>(string serviceName, string method, params object?[] arguments)
{
var rpcMessage = new SvcRpcMessage
{
ServiceName = serviceName,
MethodName = method,
Arguments = arguments
};
var timeout = Debugger.IsAttached ? 60000 : PipeUtils.DefaultTimeout;
var timeout = Debugger.IsAttached ? 60000 : PipeUtils.DefaultTimeout;
return await SendRpcMessageAsync<T>(rpcMessage, timeout, pipeName: PipeUtils.MainPipe);
}
return await SendRpcMessageAsync<T>(rpcMessage, timeout, pipeName: PipeUtils.MainPipe);
}
public async Task<T> CallWithOptionsAsync<T>(string serviceName, string method, RpcUiClientOptions options, params object?[] arguments)
{
var rpcMessage = new SvcRpcMessage
{
ServiceName = serviceName,
MethodName = method,
Arguments = arguments
};
public async Task<bool> CallGenericBoolResultAsync(string serviceName, string method, params object?[] arguments)
{
var rpcMessage = new SvcRpcMessage
{
ServiceName = serviceName,
MethodName = method,
Arguments = arguments
};
return await SendRpcMessageAsync<T>(rpcMessage, options.Timeout, pipeName: PipeUtils.MainPipe);
}
var timeout = Debugger.IsAttached ? 60000 : PipeUtils.DefaultTimeout;
public T Call<T>(string serviceName, string method, params object[] arguments)
{
var rpcMessage = new SvcRpcMessage
{
ServiceName = serviceName,
MethodName = method,
Arguments = arguments
};
var result = await SendRpcMessageAsync<GenericBoolResult>(rpcMessage, timeout, pipeName: PipeUtils.MainPipe);
var timeout = Debugger.IsAttached ? 60000 : 2000;
if (result?.Result == true)
{
return true;
}
return PipeUtils.SendRpcMessage<T>(rpcMessage, timeout, pipeName: PipeUtils.MainPipe);
}
if (result?.ErrorMessages.Count > 0)
{
await notificationService.SendNotificationDirect(new NotificationDto(result.ErrorMessages.First(), Constants.Danger));
}
private async Task<T> SendRpcMessageAsync<T>(SvcRpcMessage message, int timeout = PipeUtils.DefaultTimeout, string pipeName = PipeUtils.ServicePipe)
{
var messageJson = JsonConvert.SerializeObject(message);
return false;
}
var resultJson = await PipeUtils.SendMessageAsync(messageJson, timeout, pipeName);
public async Task<T> CallWithOptionsAsync<T>(string serviceName, string method, RpcUiClientOptions options, params object?[] arguments)
{
var rpcMessage = new SvcRpcMessage
{
ServiceName = serviceName,
MethodName = method,
Arguments = arguments
};
if (resultJson == null)
{
notificationService.SendNotification(new NotificationDto("Unknown communication error", Constants.Danger));
return await SendRpcMessageAsync<T>(rpcMessage, options.Timeout, pipeName: PipeUtils.MainPipe);
}
return default;
}
private async Task<T> SendRpcMessageAsync<T>(SvcRpcMessage message, int timeout = PipeUtils.DefaultTimeout, string pipeName = PipeUtils.ServicePipe)
{
var messageJson = JsonConvert.SerializeObject(message);
var resultMessage = JsonConvert.DeserializeObject<SvcResultMessage>(resultJson);
var resultJson = await PipeUtils.SendMessageAsync(messageJson, timeout, pipeName);
if (resultMessage?.Data == null)
{
LastErrorMessage = resultMessage?.ErrorMessage ?? "Unknown communication error";
if (resultJson == null)
{
notificationService.SendNotification(new NotificationDto("Unknown communication error", Constants.Danger));
await notificationService.SendNotificationDirect(new NotificationDto(LastErrorMessage, Constants.Danger));
return default;
}
return default;
}
var resultMessage = JsonConvert.DeserializeObject<SvcResultMessage>(resultJson);
LastErrorMessage = null;
if (resultMessage?.Data == null)
{
LastErrorMessage = resultMessage?.ErrorMessage ?? "Unknown communication error";
var result = JsonConvert.DeserializeObject<T>(resultMessage.Data);
await notificationService.SendNotificationDirect(new NotificationDto(LastErrorMessage, Constants.Danger));
return result;
}
return default;
}
LastErrorMessage = null;
var result = JsonConvert.DeserializeObject<T>(resultMessage.Data);
return result;
}
}
+4
View File
@@ -22,4 +22,8 @@ export function GetElementDimensions(elementId) {
export function GetElementClassName(elementId) {
return document.getElementById(elementId).className;
}
export function NavigateTo(uri, timeout = 0) {
setTimeout(() => window.location = uri, timeout);
}
+3 -2
View File
@@ -11,8 +11,8 @@
<PublisherName>Maassoft</PublisherName>
<Company>Maassoft</Company>
<ApplicationRevision>0</ApplicationRevision>
<ApplicationVersion>10.3.0.0</ApplicationVersion>
<Version>10.3.0.0</Version>
<ApplicationVersion>10.4.0.0</ApplicationVersion>
<Version>10.4.0.0</Version>
<UseApplicationTrust>false</UseApplicationTrust>
<PublishWizardCompleted>true</PublishWizardCompleted>
<BootstrapperEnabled>false</BootstrapperEnabled>
@@ -36,6 +36,7 @@
</PropertyGroup>
<PropertyGroup>
<PathMap Condition="'$(Configuration)' == 'Release'">$(MSBuildThisFileDirectory)=.\</PathMap>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<ItemGroup>
<None Remove="Resources\LgRemotes.json" />
+1 -1
View File
@@ -386,6 +386,6 @@ Currently ColorControl is {(_winApiService.IsAdministrator() ? "" : "not ")}runn
return;
}
_config.UiType = (UiType)cbxUiType.SelectedIndex;
_optionsService.SetUiType((UiType)cbxUiType.SelectedIndex);
}
}
+317 -332
View File
@@ -16,347 +16,332 @@ using NWin32;
using System;
using System.Drawing;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace ColorControl
{
public partial class MainForm : Form
{
private static bool SystemShutdown = false;
private static bool EndSession = false;
public partial class MainForm : Form
{
private static bool SystemShutdown = false;
private static bool EndSession = false;
private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
private readonly ServiceManager _serviceManager;
private readonly IServiceProvider _serviceProvider;
private readonly ElevationService _elevationService;
private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
private readonly ServiceManager _serviceManager;
private readonly IServiceProvider _serviceProvider;
private readonly ElevationService _elevationService;
private NvPanel _nvPanel;
private NvPanel _nvPanel;
private readonly KeyboardShortcutDispatcher _keyboardShortcutDispatcher;
private readonly UpdateManager _updateManager;
private readonly WindowMessageDispatcher _windowMessageDispatcher;
private Config _config;
private readonly KeyboardShortcutDispatcher _keyboardShortcutDispatcher;
private readonly UpdateManager _updateManager;
private readonly WindowMessageDispatcher _windowMessageDispatcher;
private Config _config;
private LgPanel _lgPanel;
private SamsungPanel _samsungPanel;
private LgPanel _lgPanel;
private SamsungPanel _samsungPanel;
private AmdPanel _amdPanel;
private bool _skipResize;
private AmdPanel _amdPanel;
private bool _skipResize;
private GamePanel _gamePanel;
private GamePanel _gamePanel;
public MainForm(GlobalContext globalContext, ServiceManager serviceManager, IServiceProvider serviceProvider, ElevationService elevationService,
KeyboardShortcutDispatcher keyboardShortcutDispatcher, UpdateManager updateManager, WindowMessageDispatcher windowMessageDispatcher)
{
InitializeComponent();
public MainForm(GlobalContext globalContext, ServiceManager serviceManager, IServiceProvider serviceProvider, ElevationService elevationService,
KeyboardShortcutDispatcher keyboardShortcutDispatcher, UpdateManager updateManager, WindowMessageDispatcher windowMessageDispatcher)
{
InitializeComponent();
Icon = Resources.AppIcon;
_serviceManager = serviceManager;
_serviceProvider = serviceProvider;
_elevationService = elevationService;
_keyboardShortcutDispatcher = keyboardShortcutDispatcher;
_updateManager = updateManager;
_windowMessageDispatcher = windowMessageDispatcher;
_config = Program.Config;
Text = globalContext.ApplicationTitleAdmin;
LoadConfig();
MessageForms.MainForm = this;
InitModules();
Task.Run(AfterInitialized);
if (_config.UseDarkMode)
{
SetTheme(_config.UseDarkMode);
}
}
private void InitModules()
{
var _ = tcMain.Handle;
_nvPanel = InitPanel<NvPanel>("NVIDIA controller");
_amdPanel = InitPanel<AmdPanel>("AMD controller");
_lgPanel = InitPanel<LgPanel>("LG controller");
_samsungPanel = InitPanel<SamsungPanel>("Samsung controller");
_gamePanel = InitPanel<GamePanel>("Game launcher");
tcMain.SelectedIndex = 0;
}
private T InitPanel<T>(string displayName) where T : UserControl, IModulePanel
{
var module = _config.Modules.FirstOrDefault(m => m.IsActive && m.DisplayName == displayName);
if (module == null)
{
return default;
}
try
{
var panel = _serviceProvider.GetRequiredService<T>();
panel.Anchor = AnchorStyles.Top | AnchorStyles.Bottom | AnchorStyles.Left | AnchorStyles.Right;
panel.Init();
var tabPage = new TabPage(module.DisplayName);
tcMain.TabPages.Insert(tcMain.TabPages.Count - 2, tabPage);
tabPage.Controls.Add(panel);
panel.Size = tabPage.ClientSize;
panel.BackColor = SystemColors.Window;
//Logger.Debug($"Panel {panel.GetType().Name}, size: {panel.Size.Width}x{panel.Size.Height}");
return panel;
}
catch (Exception ex)
{
Logger.Debug($"Error creating panel {typeof(T).Name}: {ex.ToLogString()}");
return null;
}
}
public void OpenForm()
{
Show();
WindowState = FormWindowState.Normal;
Activate();
btnUpdate.Visible = _updateManager.UpdateAvailable();
}
private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
{
if (Program.UserExit)
{
return;
}
if (!(SystemShutdown || EndSession) && _config.MinimizeOnClose)
{
e.Cancel = true;
WindowState = FormWindowState.Minimized;
Program.UserExit = false;
return;
}
GlobalSave();
if (SystemShutdown)
{
Logger.Debug($"MainForm_FormClosing: SystemShutdown");
}
Program.Exit();
}
private void GlobalSave()
{
_nvPanel?.Save();
_amdPanel?.Save();
_lgPanel?.Save();
_gamePanel?.Save();
_samsungPanel?.Save();
if (WindowState != FormWindowState.Minimized)
{
_config.FormWidth = Width;
_config.FormHeight = Height;
}
}
protected override void WndProc(ref Message m)
{
if (m.Msg == NativeConstants.WM_QUERYENDSESSION)
{
SystemShutdown = true;
EndSession = true;
_windowMessageDispatcher.DispatchEvent(WindowMessageDispatcher.Event_WindowMessageQueryEndSession, new WindowMessageEventArgs(m));
}
else if (m.Msg == Utils.WM_BRINGTOFRONT)
{
Logger.Debug("WM_BRINGTOFRONT message received, opening form");
OpenForm();
}
base.WndProc(ref m);
}
private void MainForm_Resize(object sender, EventArgs e)
{
if (_skipResize)
{
return;
}
if (WindowState == FormWindowState.Minimized)
{
if (_config.MinimizeToTray)
{
Hide();
}
}
else if (WindowState == FormWindowState.Normal && _config != null)
{
_config.FormWidth = Width;
_config.FormHeight = Height;
}
}
private void LoadConfig()
{
_skipResize = true;
try
{
Width = _config.FormWidth;
Height = _config.FormHeight;
}
finally
{
_skipResize = false;
}
}
private void MainForm_Shown(object sender, EventArgs e)
{
InitSelectedTab();
_elevationService.CheckElevationMethod();
}
private void tabControl1_SelectedIndexChanged(object sender, EventArgs e)
{
InitSelectedTab();
}
private void InitSelectedTab()
{
if (tcMain.SelectedTab == tabInfo)
{
InitInfoTab();
}
else if (tcMain.SelectedTab == tabOptions)
{
InitOptionsTab();
}
else if (tcMain.SelectedTab?.Controls.Count > 0 && tcMain.SelectedTab.Controls[0] is IModulePanel panel)
{
panel.UpdateInfo();
}
}
private void InitOptionsTab()
{
if (tabOptions.Controls.Count > 0)
{
return;
}
var control = _serviceProvider.GetRequiredService<OptionsPanel>();
control.Anchor = AnchorStyles.Top | AnchorStyles.Bottom | AnchorStyles.Left | AnchorStyles.Right;
tabOptions.Controls.Add(control);
control.Size = tabInfo.ClientSize;
}
private void InitInfoTab()
{
if (tabInfo.Controls.Count > 0)
{
return;
}
var control = _serviceProvider.GetRequiredService<InfoPanel>();
control.Anchor = AnchorStyles.Top | AnchorStyles.Bottom | AnchorStyles.Left | AnchorStyles.Right;
tabInfo.Controls.Add(control);
control.Size = tabInfo.ClientSize;
//control.BackColor = SystemColors.Window;
}
private void MainForm_Activated(object sender, EventArgs e)
{
if (tcMain.SelectedTab?.Controls.Count > 0 && tcMain.SelectedTab.Controls[0] is IModulePanel panel)
{
panel.UpdateInfo();
}
}
private async Task AfterInitialized()
{
if (_nvPanel != null)
{
await _nvPanel.AfterInitialized();
}
if (_amdPanel != null)
{
await _amdPanel.AfterInitialized();
}
}
private void MainForm_Deactivate(object sender, EventArgs e)
{
GlobalSave();
}
private void MainForm_ResizeBegin(object sender, EventArgs e)
{
SuspendLayout();
}
private void MainForm_ResizeEnd(object sender, EventArgs e)
{
ResumeLayout(true);
}
private void MainForm_Click(object sender, EventArgs e)
{
_serviceManager.NvService?.TestResolution();
}
public void SetTheme(bool toDark)
{
this.UpdateTheme(toDark);
InitSelectedTab();
}
private async void btnUpdate_Click(object sender, EventArgs e)
{
await _updateManager.InstallUpdate();
}
internal void CloseForRestart()
{
Hide();
}
protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
{
if ((keyData == (Keys.Control | Keys.PageUp) || keyData == (Keys.Control | Keys.PageDown)) && KeyboardShortcutDispatcher.IsShortcutControlFocused())
{
var control = this.FindFocusedControl() as TextBox;
if (control != null)
{
var keyEvent = new KeyEventArgs(keyData);
control.Text = KeyboardShortcutDispatcher.FormatKeyboardShortcut(keyEvent);
}
return true;
}
return base.ProcessCmdKey(ref msg, keyData);
}
}
Icon = Resources.AppIcon;
_serviceManager = serviceManager;
_serviceProvider = serviceProvider;
_elevationService = elevationService;
_keyboardShortcutDispatcher = keyboardShortcutDispatcher;
_updateManager = updateManager;
_windowMessageDispatcher = windowMessageDispatcher;
_config = Program.Config;
Text = globalContext.ApplicationTitleAdmin;
LoadConfig();
MessageForms.MainForm = this;
InitModules();
if (_config.UseDarkMode)
{
SetTheme(_config.UseDarkMode);
}
}
private void InitModules()
{
var _ = tcMain.Handle;
_nvPanel = InitPanel<NvPanel>("NVIDIA controller");
_amdPanel = InitPanel<AmdPanel>("AMD controller");
_lgPanel = InitPanel<LgPanel>("LG controller");
_samsungPanel = InitPanel<SamsungPanel>("Samsung controller");
_gamePanel = InitPanel<GamePanel>("Game launcher");
tcMain.SelectedIndex = 0;
}
private T InitPanel<T>(string displayName) where T : UserControl, IModulePanel
{
var module = _config.Modules.FirstOrDefault(m => m.IsActive && m.DisplayName == displayName);
if (module == null)
{
return default;
}
try
{
var panel = _serviceProvider.GetRequiredService<T>();
panel.Anchor = AnchorStyles.Top | AnchorStyles.Bottom | AnchorStyles.Left | AnchorStyles.Right;
panel.Init();
var tabPage = new TabPage(module.DisplayName);
tcMain.TabPages.Insert(tcMain.TabPages.Count - 2, tabPage);
tabPage.Controls.Add(panel);
panel.Size = tabPage.ClientSize;
panel.BackColor = SystemColors.Window;
//Logger.Debug($"Panel {panel.GetType().Name}, size: {panel.Size.Width}x{panel.Size.Height}");
return panel;
}
catch (Exception ex)
{
Logger.Debug($"Error creating panel {typeof(T).Name}: {ex.ToLogString()}");
return null;
}
}
public void OpenForm()
{
Show();
WindowState = FormWindowState.Normal;
Activate();
btnUpdate.Visible = _updateManager.UpdateAvailable();
}
private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
{
if (Program.UserExit)
{
return;
}
if (!(SystemShutdown || EndSession) && _config.MinimizeOnClose)
{
e.Cancel = true;
WindowState = FormWindowState.Minimized;
Program.UserExit = false;
return;
}
GlobalSave();
if (SystemShutdown)
{
Logger.Debug($"MainForm_FormClosing: SystemShutdown");
}
Program.Exit();
}
private void GlobalSave()
{
_nvPanel?.Save();
_amdPanel?.Save();
_lgPanel?.Save();
_gamePanel?.Save();
_samsungPanel?.Save();
if (WindowState != FormWindowState.Minimized)
{
_config.FormWidth = Width;
_config.FormHeight = Height;
}
}
protected override void WndProc(ref Message m)
{
if (m.Msg == NativeConstants.WM_QUERYENDSESSION)
{
SystemShutdown = true;
EndSession = true;
_windowMessageDispatcher.DispatchEvent(WindowMessageDispatcher.Event_WindowMessageQueryEndSession, new WindowMessageEventArgs(m));
}
else if (m.Msg == Utils.WM_BRINGTOFRONT)
{
Logger.Debug("WM_BRINGTOFRONT message received, opening form");
OpenForm();
}
base.WndProc(ref m);
}
private void MainForm_Resize(object sender, EventArgs e)
{
if (_skipResize)
{
return;
}
if (WindowState == FormWindowState.Minimized)
{
if (_config.MinimizeToTray)
{
Hide();
}
}
else if (WindowState == FormWindowState.Normal && _config != null)
{
_config.FormWidth = Width;
_config.FormHeight = Height;
}
}
private void LoadConfig()
{
_skipResize = true;
try
{
Width = _config.FormWidth;
Height = _config.FormHeight;
}
finally
{
_skipResize = false;
}
}
private void MainForm_Shown(object sender, EventArgs e)
{
InitSelectedTab();
_elevationService.CheckElevationMethod();
}
private void tabControl1_SelectedIndexChanged(object sender, EventArgs e)
{
InitSelectedTab();
}
private void InitSelectedTab()
{
if (tcMain.SelectedTab == tabInfo)
{
InitInfoTab();
}
else if (tcMain.SelectedTab == tabOptions)
{
InitOptionsTab();
}
else if (tcMain.SelectedTab?.Controls.Count > 0 && tcMain.SelectedTab.Controls[0] is IModulePanel panel)
{
panel.UpdateInfo();
}
}
private void InitOptionsTab()
{
if (tabOptions.Controls.Count > 0)
{
return;
}
var control = _serviceProvider.GetRequiredService<OptionsPanel>();
control.Anchor = AnchorStyles.Top | AnchorStyles.Bottom | AnchorStyles.Left | AnchorStyles.Right;
tabOptions.Controls.Add(control);
control.Size = tabInfo.ClientSize;
}
private void InitInfoTab()
{
if (tabInfo.Controls.Count > 0)
{
return;
}
var control = _serviceProvider.GetRequiredService<InfoPanel>();
control.Anchor = AnchorStyles.Top | AnchorStyles.Bottom | AnchorStyles.Left | AnchorStyles.Right;
tabInfo.Controls.Add(control);
control.Size = tabInfo.ClientSize;
//control.BackColor = SystemColors.Window;
}
private void MainForm_Activated(object sender, EventArgs e)
{
if (tcMain.SelectedTab?.Controls.Count > 0 && tcMain.SelectedTab.Controls[0] is IModulePanel panel)
{
panel.UpdateInfo();
}
}
private void MainForm_Deactivate(object sender, EventArgs e)
{
GlobalSave();
}
private void MainForm_ResizeBegin(object sender, EventArgs e)
{
SuspendLayout();
}
private void MainForm_ResizeEnd(object sender, EventArgs e)
{
ResumeLayout(true);
}
private void MainForm_Click(object sender, EventArgs e)
{
_serviceManager.NvService?.TestResolution();
}
public void SetTheme(bool toDark)
{
this.UpdateTheme(toDark);
InitSelectedTab();
}
private async void btnUpdate_Click(object sender, EventArgs e)
{
await _updateManager.InstallUpdate();
}
internal void CloseForRestart()
{
Hide();
}
protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
{
if ((keyData == (Keys.Control | Keys.PageUp) || keyData == (Keys.Control | Keys.PageDown)) && KeyboardShortcutDispatcher.IsShortcutControlFocused())
{
var control = this.FindFocusedControl() as TextBox;
if (control != null)
{
var keyEvent = new KeyEventArgs(keyData);
control.Text = KeyboardShortcutDispatcher.FormatKeyboardShortcut(keyEvent);
}
return true;
}
return base.ProcessCmdKey(ref msg, keyData);
}
}
}
+20 -13
View File
@@ -12,6 +12,7 @@ using ColorControl.Shared.Forms;
using ColorControl.Shared.Native;
using ColorControl.Shared.Services;
using ColorControl.Svc;
using ColorControl.UI;
using ColorControl.XForms;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
@@ -239,12 +240,7 @@ namespace ColorControl
var winApi = AppContext.ServiceProvider.GetRequiredService<WinApiAdminService>();
winApi.StartProcess(GetNewUiUrl());
}
public static string GetNewUiUrl()
{
return $"http://localhost:{(Config.UiPort > 0 ? Config.UiPort : 5000)}";
winApi.StartProcess(Blazor.GetCurrentUrl());
}
public static async Task OpenNewUiEmbedded()
@@ -254,26 +250,37 @@ namespace ColorControl
await Task.Delay(500);
}
BrowserWindow.CreateAndShow(GetNewUiUrl());
BrowserWindow.CreateAndShow(Blazor.GetCurrentUrl());
}
private static bool UiServerStarted;
public static async Task<bool> StartOrStopUiServer()
{
if (Config.UiType == UiType.WinForms)
{
var _ = Task.Run(() => UI.Blazor.Stop());
return true;
}
return await StartUiServer();
}
public static async Task<bool> StartUiServer()
{
if (UiServerStarted)
if (UI.Blazor.IsRunning(Config))
{
return false;
}
var backgroundService = ServiceProvider.GetRequiredService<ColorControlBackgroundService>();
backgroundService.PipeName = PipeUtils.MainPipe;
if (backgroundService.PipeName != PipeUtils.MainPipe)
{
backgroundService.PipeName = PipeUtils.MainPipe;
await backgroundService.StartAsync(CancellationToken.None);
await backgroundService.StartAsync(CancellationToken.None);
}
var _ = Task.Run(() => ColorControl.UI.Blazor.Start(Config));
UiServerStarted = true;
var _ = Task.Run(() => UI.Blazor.Start(Config));
return true;
}
-39
View File
@@ -121,45 +121,6 @@ namespace ColorControl.Services.AMD
ServiceFormUtils.AddOrUpdateListItem(lvAmdPresets, _amdService.GetPresets(), _config, preset);
}
public async Task AfterInitialized()
{
await ApplyAmdPresetOnStartup();
}
private async Task ApplyAmdPresetOnStartup(int attempts = 5)
{
var startUpParams = Shared.Common.GlobalContext.CurrentContext.StartUpParams;
var presetIdOrName = !string.IsNullOrEmpty(startUpParams.AmdPresetIdOrName) ? startUpParams.AmdPresetIdOrName : _config.AmdPresetId_ApplyOnStartup.ToString();
if (!string.IsNullOrEmpty(presetIdOrName))
{
var preset = _amdService?.GetPresetByIdOrName(presetIdOrName);
if (preset == null)
{
if (string.IsNullOrEmpty(startUpParams.AmdPresetIdOrName))
{
_config.AmdPresetId_ApplyOnStartup = 0;
}
}
else if (_amdService != null)
{
if (_amdService.HasDisplaysAttached())
{
await _amdService.ApplyPresetUi(preset);
}
else
{
attempts--;
if (attempts > 0)
{
await Task.Delay(2000);
await ApplyAmdPresetOnStartup(attempts);
}
}
}
}
}
internal void Save()
{
FormUtils.SaveSortState(lvAmdPresets.ListViewItemSorter, _config.AmdPresetsSortState);
+36
View File
@@ -72,6 +72,42 @@ namespace ColorControl.Services.AMD
base.InstallEventHandlers();
SetShortcuts(SHORTCUTID_AMDQA, Config.QuickAccessShortcut);
var _ = ApplyAmdPresetOnStartup();
}
private async Task ApplyAmdPresetOnStartup(int attempts = 5)
{
var startUpParams = _globalContext.StartUpParams;
var presetIdOrName = !string.IsNullOrEmpty(startUpParams.AmdPresetIdOrName) ? startUpParams.AmdPresetIdOrName : _globalContext.Config.AmdPresetId_ApplyOnStartup.ToString();
if (!string.IsNullOrEmpty(presetIdOrName))
{
var preset = GetPresetByIdOrName(presetIdOrName);
if (preset == null)
{
if (string.IsNullOrEmpty(startUpParams.AmdPresetIdOrName))
{
_globalContext.Config.AmdPresetId_ApplyOnStartup = 0;
}
}
else
{
if (HasDisplaysAttached())
{
await ApplyPreset(preset);
}
else
{
attempts--;
if (attempts > 0)
{
await Task.Delay(2000);
await ApplyAmdPresetOnStartup(attempts);
}
}
}
}
}
private void LoadConfig()
@@ -15,375 +15,385 @@ namespace ColorControl.Services.Common;
public class ColorProfileService
{
private readonly GlobalContext _globalContext;
private readonly RpcClientService _rpcClientService;
private readonly WinApiService _winApiService;
private readonly ServiceManager _serviceManager;
private readonly WinApiAdminService _winApiAdminService;
private readonly GlobalContext _globalContext;
private readonly RpcClientService _rpcClientService;
private readonly WinApiService _winApiService;
private readonly ServiceManager _serviceManager;
private readonly WinApiAdminService _winApiAdminService;
public ColorProfileService(GlobalContext globalContext, RpcClientService rpcClientService, WinApiService winApiService, ServiceManager serviceManager, WinApiAdminService winApiAdminService)
{
_globalContext = globalContext;
_rpcClientService = rpcClientService;
_winApiService = winApiService;
_serviceManager = serviceManager;
_winApiAdminService = winApiAdminService;
public ColorProfileService(GlobalContext globalContext, RpcClientService rpcClientService, WinApiService winApiService, ServiceManager serviceManager, WinApiAdminService winApiAdminService)
{
_globalContext = globalContext;
_rpcClientService = rpcClientService;
_winApiService = winApiService;
_serviceManager = serviceManager;
_winApiAdminService = winApiAdminService;
_rpcClientService.Name = nameof(ColorProfileService);
}
_rpcClientService.Name = nameof(ColorProfileService);
}
public bool InstallColorProfile(string profileName)
{
return CCD.InstallColorProfile(profileName);
}
public bool InstallColorProfile(string profileName)
{
return CCD.InstallColorProfile(profileName);
}
public bool UninstallColorProfile(string profileName)
{
return CCD.UninstallColorProfile(profileName);
}
public bool UninstallColorProfile(string profileName)
{
return CCD.UninstallColorProfile(profileName);
}
public bool AddDisplayColorProfile(string displayName, string profileName, bool setAsDefault = true, bool isHdrColorProfile = false)
{
return CCD.AddDisplayColorProfile(displayName, profileName, setAsDefault, isHdrColorProfile);
}
public bool AddDisplayColorProfile(string displayName, string profileName, bool setAsDefault = true, bool isHdrColorProfile = false)
{
return CCD.AddDisplayColorProfile(displayName, profileName, setAsDefault, isHdrColorProfile);
}
public bool RemoveDisplayColorProfile(string displayName, string profileName, bool isHdrColorProfile = false)
{
return CCD.RemoveDisplayColorProfile(displayName, profileName, isHdrColorProfile);
}
public bool RemoveDisplayColorProfile(string displayName, string profileName, bool isHdrColorProfile = false)
{
return CCD.RemoveDisplayColorProfile(displayName, profileName, isHdrColorProfile);
}
public bool SetDisplayDefaultColorProfile(string displayName, string profileName, bool fixMaxTML = true, bool isHdrColorProfile = false)
{
return CCD.SetDisplayDefaultColorProfile(displayName, profileName, fixMaxTML, isHdrColorProfile);
}
public bool SetDisplayDefaultColorProfile(string displayName, string profileName, bool fixMaxTML = true, bool isHdrColorProfile = false)
{
return CCD.SetDisplayDefaultColorProfile(displayName, profileName, fixMaxTML, isHdrColorProfile);
}
public string GetDisplayDefaultColorProfile(string displayName, bool isHdrColorProfile)
{
return CCD.GetDisplayDefaultColorProfile(displayName, isHdrColorProfile);
}
public string GetDisplayDefaultColorProfile(string displayName, bool isHdrColorProfile)
{
return CCD.GetDisplayDefaultColorProfile(displayName, isHdrColorProfile);
}
public List<string> GetDisplayColorProfiles(string displayName, bool isHdrColorProfile = false)
{
if (displayName != null && !displayName.StartsWith("\\\\"))
{
displayName = GetDisplayNameByDisplayId(displayName);
}
public List<string> GetDisplayColorProfiles(string displayName, bool isHdrColorProfile = false)
{
if (displayName != null && !displayName.StartsWith("\\\\"))
{
displayName = GetDisplayNameByDisplayId(displayName);
}
return CCD.GetDisplayColorProfiles(displayName, isHdrColorProfile);
}
return CCD.GetDisplayColorProfiles(displayName, isHdrColorProfile);
}
public List<ColorProfileDto> GetAllDisplayColorProfiles(string displayName)
{
if (displayName != null && !displayName.StartsWith("\\\\"))
{
displayName = GetDisplayNameByDisplayId(displayName);
}
public List<ColorProfileDto> GetAllDisplayColorProfiles(string displayName)
{
if (displayName != null && !displayName.StartsWith("\\\\"))
{
displayName = GetDisplayNameByDisplayId(displayName);
}
var display = GetDisplayByGdiName(displayName);
var display = GetDisplayByGdiName(displayName);
if (display == null)
{
return [];
}
if (display == null)
{
return [];
}
var advancedColor = CCD.GetAdvancedColorSupportedAndEnabled(display.DisplayName);
var advancedColor = CCD.GetAdvancedColorSupportedAndEnabled(display.DisplayName);
return GetAllDisplayColorProfiles(display, advancedColor.isSupported);
}
return GetAllDisplayColorProfiles(display, advancedColor.isSupported);
}
public List<ColorProfileDto> GetAllDisplayColorProfiles(Display display, bool isHdrSupported = false)
{
var colorProfiles = GetDisplayColorProfiles(display.DisplayName, false).Select(p => new ColorProfileDto { Name = p, IsHDRProfile = false }).ToList();
if (isHdrSupported)
{
colorProfiles = colorProfiles.Union(GetDisplayColorProfiles(display.DisplayName, true).Select(p => new ColorProfileDto { Name = p, IsHDRProfile = true })).ToList();
}
public List<ColorProfileDto> GetAllDisplayColorProfiles(Display display, bool isHdrSupported = false)
{
var colorProfiles = GetDisplayColorProfiles(display.DisplayName, false).Select(p => new ColorProfileDto { Name = p, IsHDRProfile = false }).ToList();
if (isHdrSupported)
{
colorProfiles = colorProfiles.Union(GetDisplayColorProfiles(display.DisplayName, true).Select(p => new ColorProfileDto { Name = p, IsHDRProfile = true })).ToList();
}
return colorProfiles;
}
return colorProfiles;
}
public List<string> GetAllColorProfileNames()
{
if (!_winApiService.IsAdministrator())
{
return _rpcClientService.Call<List<string>>(nameof(GetAllColorProfileNames));
}
public List<string> GetAllColorProfileNames()
{
if (!_winApiService.IsAdministrator())
{
return _rpcClientService.Call<List<string>>(nameof(GetAllColorProfileNames));
}
return CCD.GetAllColorProfileNames();
}
return CCD.GetAllColorProfileNames();
}
private string GetDisplayNameByDisplayId(string displayName)
{
var deviceName = default(string);
private string GetDisplayNameByDisplayId(string displayName)
{
var deviceName = default(string);
Display.GetDisplays().FirstOrDefault(d =>
{
var ccdDisplayInfo = CCD.GetDisplayInfo(d.DisplayName);
Display.GetDisplays().FirstOrDefault(d =>
{
var ccdDisplayInfo = CCD.GetDisplayInfo(d.DisplayName);
if (ccdDisplayInfo.DisplayId == displayName)
{
deviceName = ccdDisplayInfo.DisplayName;
if (ccdDisplayInfo.DisplayId == displayName)
{
deviceName = ccdDisplayInfo.DisplayName;
return true;
}
return false;
});
return true;
}
return false;
});
return deviceName;
}
return deviceName;
}
public Display GetDisplayByGdiName(string displayName)
{
return Display.GetDisplays().FirstOrDefault(d => d.DisplayName == displayName);
}
public Display GetDisplayByGdiName(string displayName)
{
return Display.GetDisplays().FirstOrDefault(d => d.DisplayName == displayName);
}
public bool GetUsePerUserDisplayProfiles(string displayName)
{
return CCD.GetUsePerUserDisplayProfiles(displayName);
}
public bool GetUsePerUserDisplayProfiles(string displayName)
{
return CCD.GetUsePerUserDisplayProfiles(displayName);
}
public bool SetUsePerUserDisplayProfiles(string displayName, bool usePerUserProfiles)
{
return CCD.SetUsePerUserDisplayProfiles(displayName, usePerUserProfiles);
}
public bool SetUsePerUserDisplayProfiles(string displayName, bool usePerUserProfiles)
{
return CCD.SetUsePerUserDisplayProfiles(displayName, usePerUserProfiles);
}
public bool DeleteColorProfile(string name)
{
return CCD.UninstallColorProfile(name);
}
public bool DeleteColorProfile(string name)
{
return CCD.UninstallColorProfile(name);
}
public ColorProfileDto GetColorProfile(string name, string displayName = null)
{
var isAssociatedAsHdr = CCD.IsColorProfileAssociatedAsHdr(displayName, name);
public ColorProfileDto GetColorProfile(string name, string displayName = null)
{
var isAssociatedAsHdr = CCD.IsColorProfileAssociatedAsHdr(displayName, name);
var data = MHC2Wrapper.LoadProfile(name, isAssociatedAsHdr);
var data = MHC2Wrapper.LoadProfile(name, isAssociatedAsHdr);
return new ColorProfileDto
{
Name = name,
HasExtraInfo = data.HasExtraInfo,
BlackLuminance = data.BlackLuminance,
ColorGamut = data.ColorGamut,
Description = data.Description,
DevicePrimaries = new RgbPrimariesDto(data.DevicePrimaries),
Gamma = data.Gamma,
IsHDRProfile = data.IsHDRProfile,
MaxCLL = data.MaxCLL,
MinCLL = data.MinCLL,
SDRBrightnessBoost = data.SDRBrightnessBoost,
SDRMaxBrightness = data.SDRMaxBrightness,
SDRMinBrightness = data.SDRMinBrightness,
SDRTransferFunction = data.SDRTransferFunction,
ShadowDetailBoost = data.ShadowDetailBoost,
WhiteLuminance = data.WhiteLuminance,
};
}
return new ColorProfileDto
{
Name = name,
RenderingIntent = data.RenderingIntent,
HasExtraInfo = data.HasExtraInfo,
BlackLuminance = data.BlackLuminance,
ColorGamut = data.ColorGamut,
Description = data.Description,
DevicePrimaries = new RgbPrimariesDto(data.DevicePrimaries),
Gamma = data.Gamma,
IsHDRProfile = data.IsHDRProfile,
MaxCLL = data.MaxCLL,
MinCLL = data.MinCLL,
SDRBrightnessBoost = data.SDRBrightnessBoost,
SDRMaxBrightness = data.SDRMaxBrightness,
SDRMinBrightness = data.SDRMinBrightness,
SDRTransferFunction = data.SDRTransferFunction,
ShadowDetailBoost = data.ShadowDetailBoost,
ShadowDetailRange = data.ShadowDetailRange,
RedGain = data.RedGain,
GreenGain = data.GreenGain,
BlueGain = data.BlueGain,
WhiteLuminance = data.WhiteLuminance,
};
}
public bool UpdateColorProfile(ColorProfileDto colorProfile, string displayName = null)
{
var command = new GenerateProfileCommand
{
Description = colorProfile.Description,
IsHDRProfile = colorProfile.IsHDRProfile,
MinCLL = colorProfile.MinCLL,
MaxCLL = colorProfile.MaxCLL,
BlackLuminance = colorProfile.BlackLuminance,
WhiteLuminance = colorProfile.WhiteLuminance,
SDRMinBrightness = colorProfile.SDRMinBrightness,
SDRMaxBrightness = colorProfile.SDRMaxBrightness,
SDRTransferFunction = colorProfile.SDRTransferFunction,
SDRBrightnessBoost = colorProfile.SDRBrightnessBoost,
ShadowDetailBoost = colorProfile.ShadowDetailBoost,
ColorGamut = colorProfile.ColorGamut,
Gamma = colorProfile.Gamma,
DevicePrimaries = colorProfile.DevicePrimaries.ToInternal(),
ToneMappingFromLuminance = colorProfile.ToneMappingFromLuminance,
ToneMappingToLuminance = colorProfile.ToneMappingToLuminance,
HdrBrightnessMultiplier = colorProfile.HdrBrightnessMultiplier,
HdrGammaMultiplier = colorProfile.HdrGammaMultiplier,
};
public bool UpdateColorProfile(ColorProfileDto colorProfile, string displayName = null)
{
var command = new GenerateProfileCommand
{
RenderingIntent = colorProfile.RenderingIntent,
Description = colorProfile.Description,
IsHDRProfile = colorProfile.IsHDRProfile,
MinCLL = colorProfile.MinCLL,
MaxCLL = colorProfile.MaxCLL,
BlackLuminance = colorProfile.BlackLuminance,
WhiteLuminance = colorProfile.WhiteLuminance,
SDRMinBrightness = colorProfile.SDRMinBrightness,
SDRMaxBrightness = colorProfile.SDRMaxBrightness,
SDRTransferFunction = colorProfile.SDRTransferFunction,
SDRBrightnessBoost = colorProfile.SDRBrightnessBoost,
ShadowDetailBoost = colorProfile.ShadowDetailBoost,
ShadowDetailRange = colorProfile.ShadowDetailRange,
RedGain = colorProfile.RedGain,
GreenGain = colorProfile.GreenGain,
BlueGain = colorProfile.BlueGain,
ColorGamut = colorProfile.ColorGamut,
Gamma = colorProfile.Gamma,
DevicePrimaries = colorProfile.DevicePrimaries.ToInternal(),
var bytes = MHC2Wrapper.GenerateSdrAcmProfile(command);
ToneMappingFromLuminance = colorProfile.ToneMappingFromLuminance,
ToneMappingToLuminance = colorProfile.ToneMappingToLuminance,
HdrBrightnessMultiplier = colorProfile.HdrBrightnessMultiplier,
HdrGammaMultiplier = colorProfile.HdrGammaMultiplier,
};
var tempFilename = Path.GetFullPath(colorProfile.Name, Path.GetTempPath());
var bytes = MHC2Wrapper.GenerateSdrAcmProfile(command);
if (File.Exists(tempFilename))
{
File.Delete(tempFilename);
}
var tempFilename = Path.GetFullPath(colorProfile.Name, Path.GetTempPath());
File.WriteAllBytes(tempFilename, bytes);
if (File.Exists(tempFilename))
{
File.Delete(tempFilename);
}
_winApiAdminService.UninstallColorProfile(tempFilename);
File.WriteAllBytes(tempFilename, bytes);
var result = CCD.InstallColorProfile(tempFilename);
_winApiAdminService.UninstallColorProfile(tempFilename);
if (result && displayName != null)
{
CCD.AddDisplayColorProfile(displayName, colorProfile.Name, false, command.IsHDRProfile);
}
var result = CCD.InstallColorProfile(tempFilename);
return result;
}
if (result && displayName != null)
{
CCD.AddDisplayColorProfile(displayName, colorProfile.Name, false, command.IsHDRProfile);
}
public bool InstallAndActivateColorProfile(ColorProfileDto colorProfile, string displayName)
{
var installed = UpdateColorProfile(colorProfile);
return result;
}
if (!installed)
{
return false;
}
public bool InstallAndActivateColorProfile(ColorProfileDto colorProfile, string displayName)
{
var installed = UpdateColorProfile(colorProfile);
var profileName = colorProfile.Name;
if (!installed)
{
return false;
}
return CCD.SetDisplayDefaultColorProfile(displayName, profileName, _globalContext.Config.SetMinTmlAndMaxTml, colorProfile.IsHDRProfile);
}
var profileName = colorProfile.Name;
public List<DisplayInfoDto> GetDisplayInfos(bool? onlyHdrEnabled = null)
{
return Display.GetDisplays().Select(d =>
{
var ccdDisplayInfo = CCD.GetDisplayInfo(d.DisplayName);
var advancedColor = CCD.GetAdvancedColorSupportedAndEnabled(d.DisplayName);
var colorParams = CCD.GetColorParams(d.DisplayName);
return CCD.SetDisplayDefaultColorProfile(displayName, profileName, _globalContext.Config.SetMinTmlAndMaxTml, colorProfile.IsHDRProfile);
}
var displayColors = new List<DisplayColorInfo>
{
new DisplayColorInfo
{
DisplayPrimariesSource = MHC2Gen.DisplayPrimariesSource.Custom,
CustomName = "sRGB",
RgbPrimaries = new RgbPrimariesDto(RgbPrimaries.sRGB),
BlackLuminance = 0,
WhiteLuminance = 100,
MinCLL = 0,
MaxCLL = 100
}
};
public List<DisplayInfoDto> GetDisplayInfos(bool? onlyHdrEnabled = null)
{
return Display.GetDisplays().Select(d =>
{
var ccdDisplayInfo = CCD.GetDisplayInfo(d.DisplayName);
var advancedColor = CCD.GetAdvancedColorSupportedAndEnabled(d.DisplayName);
var colorParams = CCD.GetColorParams(d.DisplayName);
if (!colorParams.Equals(default(ColorParams)))
{
displayColors.Add(new DisplayColorInfo
{
DisplayPrimariesSource = MHC2Gen.DisplayPrimariesSource.Windows,
RgbPrimaries = ColorParamsToRgbPrimaries(colorParams),
BlackLuminance = (double)colorParams.MinLuminance / 10000,
WhiteLuminance = (double)colorParams.MaxLuminance / 10000,
MinCLL = (double)colorParams.MinLuminance / 10000,
MaxCLL = (double)colorParams.MaxLuminance / 10000
});
}
var displayColors = new List<DisplayColorInfo>
{
new DisplayColorInfo
{
DisplayPrimariesSource = MHC2Gen.DisplayPrimariesSource.Custom,
CustomName = "sRGB",
RgbPrimaries = new RgbPrimariesDto(RgbPrimaries.sRGB),
BlackLuminance = 0,
WhiteLuminance = 100,
MinCLL = 0,
MaxCLL = 100
}
};
var edid = GetEDIDInternal(d.DevicePath);
if (edid != null)
{
displayColors.Add(new DisplayColorInfo
{
DisplayPrimariesSource = MHC2Gen.DisplayPrimariesSource.EDID,
RgbPrimaries = EdidToRgbPrimaries(edid),
BlackLuminance = (double)colorParams.MinLuminance / 10000,
WhiteLuminance = (double)colorParams.MaxLuminance / 10000,
MinCLL = (double)colorParams.MinLuminance / 10000,
MaxCLL = (double)colorParams.MaxLuminance / 10000
});
}
if (!colorParams.Equals(default(ColorParams)))
{
displayColors.Add(new DisplayColorInfo
{
DisplayPrimariesSource = MHC2Gen.DisplayPrimariesSource.Windows,
RgbPrimaries = ColorParamsToRgbPrimaries(colorParams),
BlackLuminance = (double)colorParams.MinLuminance / 10000,
WhiteLuminance = (double)colorParams.MaxLuminance / 10000,
MinCLL = (double)colorParams.MinLuminance / 10000,
MaxCLL = (double)colorParams.MaxLuminance / 10000
});
}
var colorProfiles = GetAllDisplayColorProfiles(d, advancedColor.isSupported);
var edid = GetEDIDInternal(d.DevicePath);
if (edid != null)
{
displayColors.Add(new DisplayColorInfo
{
DisplayPrimariesSource = MHC2Gen.DisplayPrimariesSource.EDID,
RgbPrimaries = EdidToRgbPrimaries(edid),
BlackLuminance = (double)colorParams.MinLuminance / 10000,
WhiteLuminance = (double)colorParams.MaxLuminance / 10000,
MinCLL = (double)colorParams.MinLuminance / 10000,
MaxCLL = (double)colorParams.MaxLuminance / 10000
});
}
return new DisplayInfoDto
{
DisplayName = ccdDisplayInfo.ExtendedName,
DevicePath = ccdDisplayInfo.DevicePath,
DisplayId = ccdDisplayInfo.DisplayId,
IsActive = true,
DeviceName = d.DisplayName,
AdapterName = d.Adapter.DeviceName,
IsAdvancedColorEnabled = advancedColor.isEnabled,
IsAdvancedColorSupported = advancedColor.isSupported,
DisplayColors = displayColors,
ColorProfiles = colorProfiles,
DefaultSdrColorProfile = GetDisplayDefaultColorProfile(d.DisplayName, false),
DefaultHdrColorProfile = GetDisplayDefaultColorProfile(d.DisplayName, true),
UsePerUserSettings = CCD.GetUsePerUserDisplayProfiles(d.DisplayName)
};
})
.Where(d => !onlyHdrEnabled.HasValue || onlyHdrEnabled == d.IsHdrSupportedAndEnabled)
.ToList();
}
var colorProfiles = GetAllDisplayColorProfiles(d, advancedColor.isSupported);
public RgbPrimariesDto ColorParamsToRgbPrimaries(ColorParams colorParams)
{
var divider = colorParams.RedPointX <= 1 << 10 ? 1 << 10 : 1 << 20;
return new DisplayInfoDto
{
DisplayName = ccdDisplayInfo.ExtendedName,
DevicePath = ccdDisplayInfo.DevicePath,
DisplayId = ccdDisplayInfo.DisplayId,
IsActive = true,
DeviceName = d.DisplayName,
AdapterName = d.Adapter.DeviceName,
IsAdvancedColorEnabled = advancedColor.isEnabled,
IsAdvancedColorSupported = advancedColor.isSupported,
DisplayColors = displayColors,
ColorProfiles = colorProfiles,
DefaultSdrColorProfile = GetDisplayDefaultColorProfile(d.DisplayName, false),
DefaultHdrColorProfile = GetDisplayDefaultColorProfile(d.DisplayName, true),
UsePerUserSettings = CCD.GetUsePerUserDisplayProfiles(d.DisplayName)
};
})
.Where(d => !onlyHdrEnabled.HasValue || onlyHdrEnabled == d.IsHdrSupportedAndEnabled)
.ToList();
}
return new RgbPrimariesDto
{
Red = new ColorPoint
{
X = (double)colorParams.RedPointX / divider,
Y = (double)colorParams.RedPointY / divider
},
Green = new ColorPoint
{
X = (double)colorParams.GreenPointX / divider,
Y = (double)colorParams.GreenPointY / divider
},
Blue = new ColorPoint
{
X = (double)colorParams.BluePointX / divider,
Y = (double)colorParams.BluePointY / divider
},
White = new ColorPoint
{
X = (double)colorParams.WhitePointX / divider,
Y = (double)colorParams.WhitePointY / divider
}
};
}
public RgbPrimariesDto ColorParamsToRgbPrimaries(ColorParams colorParams)
{
var divider = colorParams.RedPointX <= 1 << 10 ? 1 << 10 : 1 << 20;
public RgbPrimariesDto EdidToRgbPrimaries(EDID edid)
{
var coords = edid.DisplayParameters.ChromaticityCoordinates;
return new RgbPrimariesDto
{
Red = new ColorPoint
{
X = (double)colorParams.RedPointX / divider,
Y = (double)colorParams.RedPointY / divider
},
Green = new ColorPoint
{
X = (double)colorParams.GreenPointX / divider,
Y = (double)colorParams.GreenPointY / divider
},
Blue = new ColorPoint
{
X = (double)colorParams.BluePointX / divider,
Y = (double)colorParams.BluePointY / divider
},
White = new ColorPoint
{
X = (double)colorParams.WhitePointX / divider,
Y = (double)colorParams.WhitePointY / divider
}
};
}
return new RgbPrimariesDto
{
Red = new ColorPoint
{
X = coords.RedX,
Y = coords.RedY
},
Green = new ColorPoint
{
X = coords.GreenX,
Y = coords.GreenY
},
Blue = new ColorPoint
{
X = coords.BlueX,
Y = coords.BlueY
},
White = new ColorPoint
{
X = coords.WhiteX,
Y = coords.WhiteY
}
};
}
public RgbPrimariesDto EdidToRgbPrimaries(EDID edid)
{
var coords = edid.DisplayParameters.ChromaticityCoordinates;
public static EDID GetEDIDInternal(string path)
{
try
{
var registryPath = "HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Enum\\DISPLAY\\";
registryPath += string.Join("\\", path.Split('#').Skip(1).Take(2));
return new EDID((byte[])Registry.GetValue(registryPath + "\\Device Parameters", "EDID", null));
}
catch
{
return null;
}
}
return new RgbPrimariesDto
{
Red = new ColorPoint
{
X = coords.RedX,
Y = coords.RedY
},
Green = new ColorPoint
{
X = coords.GreenX,
Y = coords.GreenY
},
Blue = new ColorPoint
{
X = coords.BlueX,
Y = coords.BlueY
},
White = new ColorPoint
{
X = coords.WhiteX,
Y = coords.WhiteY
}
};
}
public static EDID GetEDIDInternal(string path)
{
try
{
var registryPath = "HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Enum\\DISPLAY\\";
registryPath += string.Join("\\", path.Split('#').Skip(1).Take(2));
return new EDID((byte[])Registry.GetValue(registryPath + "\\Device Parameters", "EDID", null));
}
catch
{
return null;
}
}
}
+295 -107
View File
@@ -7,154 +7,342 @@ using NWin32.NativeTypes;
using Shared.Native;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
using static ColorControl.Shared.Native.WinApi;
namespace ColorControl.Services.Common
namespace ColorControl.Services.Common;
abstract class GraphicsService<T> : ServiceBase<T> where T : PresetBase, new()
{
abstract class GraphicsService<T> : ServiceBase<T> where T : PresetBase, new()
{
private static readonly NLog.Logger Logger = NLog.LogManager.GetCurrentClassLogger();
private static readonly NLog.Logger Logger = NLog.LogManager.GetCurrentClassLogger();
private int _lastSetSDRBrightness = -1;
private int _lastReadSDRBrightness = -1;
private int _lastSetSDRBrightness = -1;
private int _lastReadSDRBrightness = -1;
public GraphicsService(GlobalContext globalContext) : base(globalContext)
{
}
internal class SafePhysicalMonitorHandle : SafeHandle
{
public SafePhysicalMonitorHandle(IntPtr handle) : base(IntPtr.Zero, true)
{
this.handle = handle; // IntPtr.Zero may be a valid handle.
}
~GraphicsService()
{
Uninitialize();
}
public override bool IsInvalid => false; // The validity cannot be checked by the handle.
public abstract bool HasDisplaysAttached(bool reinitialize = false);
protected override bool ReleaseHandle()
{
return true;
}
}
public bool SetMode(string displayName, VirtualResolution resolution = null, Rational refreshRate = null, bool updateRegistry = false)
{
var displayConfig = CCD.GetDisplayConfig(displayName);
public GraphicsService(GlobalContext globalContext) : base(globalContext)
{
}
if (refreshRate != null)
{
displayConfig.ApplyRefreshRate = true;
displayConfig.RefreshRate = refreshRate;
}
~GraphicsService()
{
Uninitialize();
}
if (resolution != null)
{
displayConfig.ApplyResolution = true;
displayConfig.Resolution = resolution;
}
public abstract bool HasDisplaysAttached(bool reinitialize = false);
return SetMode(displayName, displayConfig, updateRegistry);
}
public bool SetMode(string displayName, VirtualResolution resolution = null, Rational refreshRate = null, bool updateRegistry = false)
{
var displayConfig = CCD.GetDisplayConfig(displayName);
public bool SetMode(string displayName, DisplayConfig displayConfig, bool updateRegistry = false)
{
var currentDisplayConfig = CCD.GetDisplayConfig(displayName);
if (refreshRate != null)
{
displayConfig.ApplyRefreshRate = true;
displayConfig.RefreshRate = refreshRate;
}
if (displayConfig.ApplyRefreshRate)
{
currentDisplayConfig.RefreshRate = displayConfig.RefreshRate;
}
if (resolution != null)
{
displayConfig.ApplyResolution = true;
displayConfig.Resolution = resolution;
}
if (displayConfig.ApplyResolution)
{
currentDisplayConfig.Resolution = displayConfig.Resolution;
}
return SetMode(displayName, displayConfig, updateRegistry);
}
if (displayConfig.Scaling != CCD.DisplayConfigScaling.Zero)
{
currentDisplayConfig.Scaling = displayConfig.Scaling;
}
public bool SetMode(string displayName, DisplayConfig displayConfig, bool updateRegistry = false)
{
var currentDisplayConfig = CCD.GetDisplayConfig(displayName);
if (displayConfig.Rotation != CCD.DisplayConfigRotation.Zero)
{
currentDisplayConfig.Rotation = displayConfig.Rotation;
}
if (displayConfig.ApplyRefreshRate)
{
currentDisplayConfig.RefreshRate = displayConfig.RefreshRate;
}
return CCD.SetDisplayConfig(displayName, currentDisplayConfig, updateRegistry);
}
if (displayConfig.ApplyResolution)
{
currentDisplayConfig.Resolution = displayConfig.Resolution;
}
public void SetSDRBrightness(string displayName, int brightnessPercent)
{
var hmon = FormUtils.GetMonitorForDisplayName(displayName);
if (displayConfig.Scaling != CCD.DisplayConfigScaling.Zero)
{
currentDisplayConfig.Scaling = displayConfig.Scaling;
}
if (hmon == IntPtr.Zero)
{
hmon = NativeMethods.MonitorFromWindow(0, 1);
}
if (displayConfig.Rotation != CCD.DisplayConfigRotation.Zero)
{
currentDisplayConfig.Rotation = displayConfig.Rotation;
}
var brightness = 1 + (double)brightnessPercent / 20;
return CCD.SetDisplayConfig(displayName, currentDisplayConfig, updateRegistry);
}
WinApi.DwmpSDRToHDRBoostPtr(hmon, brightness);
public void SetSDRBrightness(string displayName, int brightnessPercent)
{
var hmon = FormUtils.GetMonitorForDisplayName(displayName);
_lastSetSDRBrightness = brightnessPercent;
}
if (hmon == IntPtr.Zero)
{
hmon = NativeMethods.MonitorFromWindow(0, 1);
}
public int GetSDRBrightness(string displayName)
{
var whiteLevel = CCD.GetSDRWhiteLevel(displayName);
var brightness = 1 + (double)brightnessPercent / 20;
if (whiteLevel >= 1000)
{
var newBrightnessPercent = (int)(whiteLevel - 1000) / 50;
WinApi.DwmpSDRToHDRBoostPtr(hmon, brightness);
if (_lastSetSDRBrightness >= 0 && newBrightnessPercent == _lastReadSDRBrightness)
{
return _lastSetSDRBrightness;
}
_lastSetSDRBrightness = brightnessPercent;
}
_lastReadSDRBrightness = newBrightnessPercent;
public int GetSDRBrightness(string displayName)
{
var whiteLevel = CCD.GetSDRWhiteLevel(displayName);
return newBrightnessPercent;
}
if (whiteLevel >= 1000)
{
var newBrightnessPercent = (int)(whiteLevel - 1000) / 50;
return 0;
}
if (_lastSetSDRBrightness >= 0 && newBrightnessPercent == _lastReadSDRBrightness)
{
return _lastSetSDRBrightness;
}
protected List<Rational> GetAvailableRefreshRatesV2(string displayName, int horizontal, int vertical)
{
var dxWrapper = new DXWrapper();
_lastReadSDRBrightness = newBrightnessPercent;
var modes = dxWrapper.GetModes(displayName, (uint)horizontal, (uint)vertical);
return newBrightnessPercent;
}
var refreshRates = modes.Select(m => m.RefreshRate).DistinctBy(r => r.ToString())
.Select(r => new Rational(r.Numerator, r.Denominator));
return 0;
}
return refreshRates.ToList();
}
protected List<Rational> GetAvailableRefreshRatesV2(string displayName, int horizontal, int vertical)
{
var dxWrapper = new DXWrapper();
protected List<VirtualResolution> GetAvailableResolutionsInternalV2(string displayName, Rational refreshRate = null)
{
var dxWrapper = new DXWrapper();
var modes = dxWrapper.GetModes(displayName, (uint)horizontal, (uint)vertical);
var modes = dxWrapper.GetModes(displayName);
var refreshRates = modes.Select(m => m.RefreshRate).DistinctBy(r => r.ToString())
.Select(r => new Rational(r.Numerator, r.Denominator));
var refreshRates = modes.DistinctBy(m => $"{m.Resolution.width}x{m.Resolution.height}")
.Select(m => new VirtualResolution(m.Resolution.width, m.Resolution.height));
return refreshRates.ToList();
}
return refreshRates.ToList();
}
protected List<VirtualResolution> GetAvailableResolutionsInternalV2(string displayName, Rational refreshRate = null)
{
var dxWrapper = new DXWrapper();
protected bool SetMonitorScaling()
{
var devMode = new DEVMODEW();
devMode.dmSize = (ushort)Marshal.SizeOf<DEVMODEW>();
devMode.dmFields = NativeConstants.DM_LOGPIXELS;
devMode.dmLogPixels = 96 * 96 / 100;
var modes = dxWrapper.GetModes(displayName);
var ptr = Marshal.AllocHGlobal(Marshal.SizeOf<DEVMODEW>());
var refreshRates = modes.DistinctBy(m => $"{m.Resolution.width}x{m.Resolution.height}")
.Select(m => new VirtualResolution(m.Resolution.width, m.Resolution.height));
Marshal.StructureToPtr(devMode, ptr, false);
return refreshRates.ToList();
}
if (NativeMethods.ChangeDisplaySettingsExW(null, ptr, 0, 0, 0) == NativeConstants.DISP_CHANGE_SUCCESSFUL)
{
var result = NativeMethods.SystemParametersInfoW(0x009F, uint.MaxValue - 6, 0, NativeConstants.SPIF_SENDCHANGE | NativeConstants.SPIF_UPDATEINIFILE);
}
protected bool SetMonitorScaling()
{
var devMode = new DEVMODEW();
devMode.dmSize = (ushort)Marshal.SizeOf<DEVMODEW>();
devMode.dmFields = NativeConstants.DM_LOGPIXELS;
devMode.dmLogPixels = 96 * 96 / 100;
return true;
var ptr = Marshal.AllocHGlobal(Marshal.SizeOf<DEVMODEW>());
}
}
Marshal.StructureToPtr(devMode, ptr, false);
if (NativeMethods.ChangeDisplaySettingsExW(null, ptr, 0, 0, 0) == NativeConstants.DISP_CHANGE_SUCCESSFUL)
{
var result = NativeMethods.SystemParametersInfoW(0x009F, uint.MaxValue - 6, 0, NativeConstants.SPIF_SENDCHANGE | NativeConstants.SPIF_UPDATEINIFILE);
}
return true;
}
protected static IEnumerable<SafePhysicalMonitorHandle> EnumeratePhysicalMonitors(IntPtr monitorHandle, bool verbose = false)
{
if (!GetNumberOfPhysicalMonitorsFromHMONITOR(
monitorHandle,
out uint count))
{
Debug.WriteLine($"Failed to get the number of physical monitors. {WinError.GetMessage()}");
yield break;
}
if (count == 0)
{
yield break;
}
var physicalMonitors = new PHYSICAL_MONITOR[count];
try
{
if (!GetPhysicalMonitorsFromHMONITOR(
monitorHandle,
count,
physicalMonitors))
{
Debug.WriteLine($"Failed to get an array of physical monitors. {WinError.GetMessage()}");
yield break;
}
int monitorIndex = 0;
foreach (var physicalMonitor in physicalMonitors)
{
var handle = new SafePhysicalMonitorHandle(physicalMonitor.hPhysicalMonitor);
//Debug.WriteLine($"Description: {physicalMonitor.szPhysicalMonitorDescription}");
//Debug.WriteLine($"Handle: {physicalMonitor.hPhysicalMonitor}");
yield return handle;
monitorIndex++;
}
}
finally
{
// The physical monitor handles should be destroyed at a later stage.
}
}
public static GenericResult<VcpInfo> ReadDDC(string displayName, byte vcpCode)
{
// Do this via: GetVCPFeatureAndVCPFeatureReply: https://learn.microsoft.com/en-us/windows/win32/api/lowlevelmonitorconfigurationapi/nf-lowlevelmonitorconfigurationapi-getvcpfeatureandvcpfeaturereply
var handle = GetPhysicalMonitorHandle(displayName);
if (handle == -1)
{
return null;
}
try
{
var result = GetVCPFeatureAndVCPFeatureReply(handle, vcpCode, out var codeType, out var currentValue, out var maxValue);
if (!result)
{
var (errorCode, msg) = WinError.GetCodeMessage();
Logger.Warn("Error while reading VCP code of display: {0}", msg);
return GenericResult<VcpInfo>.FromError(msg);
}
return GenericResult<VcpInfo>.FromSuccess(new VcpInfo
{
Value = currentValue,
MaxValue = maxValue,
CodeType = codeType
});
}
finally
{
//first.DangerousRelease();
}
}
public static async Task<GenericBoolResult> WriteDDC(string displayName, byte vcpCode, uint value, DdcSetting.ValueChangeTypes changeType = DdcSetting.ValueChangeTypes.Fixed, bool validate = true)
{
var handle = GetPhysicalMonitorHandle(displayName);
if (handle == -1)
{
return GenericBoolResult.FromError("No monitor handle");
}
if (changeType != DdcSetting.ValueChangeTypes.Fixed)
{
var readResult = ReadDDC(displayName, vcpCode);
if (!readResult.HasResult)
{
return GenericBoolResult.FromError("Cannot apply increase or decrease");
}
var currentValue = readResult.Result.Value;
if (changeType == DdcSetting.ValueChangeTypes.Increase)
{
value += currentValue;
if (value > readResult.Result.MaxValue)
{
value = readResult.Result.MaxValue;
}
}
else if (changeType == DdcSetting.ValueChangeTypes.Decrease)
{
if (((int)currentValue - value) < 0)
{
value = 0;
}
else
{
value = currentValue - value;
}
}
}
var result = SetVCPFeature(handle, vcpCode, value);
if (!result)
{
var (errorCode, msg) = WinError.GetCodeMessage();
Logger.Warn("Error while writing VCP code of display: {0}", msg);
return GenericBoolResult.FromError(msg);
}
if (validate)
{
await Task.Delay(50);
var readResult = ReadDDC(displayName, vcpCode);
if (readResult.Result?.Value != value)
{
return GenericBoolResult.FromError("The monitor did not apply the specified value");
}
}
return GenericBoolResult.Success;
}
public static nint GetPhysicalMonitorHandle(string displayName)
{
if (displayName == null)
{
return -1;
}
var hMonitor = FormUtils.GetMonitorForDisplayName(displayName);
var physicalHandles = EnumeratePhysicalMonitors(hMonitor);
var first = physicalHandles.FirstOrDefault();
if (first == null)
{
return -1;
}
return first.DangerousGetHandle();
}
}
+199 -199
View File
@@ -18,253 +18,253 @@ namespace ColorControl.Services.Common;
public class NotifyIconManager
{
private readonly GlobalContext _globalContext;
private readonly ServiceManager _serviceManager;
private readonly WinApiAdminService _winApiAdminService;
private readonly WinApiService _winApiService;
private readonly Config _config;
private readonly GlobalContext _globalContext;
private readonly ServiceManager _serviceManager;
private readonly WinApiAdminService _winApiAdminService;
private readonly WinApiService _winApiService;
private readonly Config _config;
public NotifyIcon NotifyIcon { get; private set; }
public NotifyIcon NotifyIcon { get; private set; }
private ToolStripMenuItem _nvTrayMenu;
private ToolStripMenuItem _novideoTrayMenu;
private ToolStripMenuItem _amdTrayMenu;
private ToolStripMenuItem _lgTrayMenu;
private ToolStripMenuItem _samsungTrayMenu;
private ToolStripMenuItem _gameTrayMenu;
private ToolStripMenuItem _nvTrayMenu;
private ToolStripMenuItem _novideoTrayMenu;
private ToolStripMenuItem _amdTrayMenu;
private ToolStripMenuItem _lgTrayMenu;
private ToolStripMenuItem _samsungTrayMenu;
private ToolStripMenuItem _gameTrayMenu;
private ToolStripMenuItem _stopServiceAndExitMenuItem;
private ToolStripMenuItem _openWinFormsMenuItem;
private ToolStripMenuItem _openNewUiMenuItem;
private ToolStripMenuItem _openNewUiEmbeddedMenuItem;
private ToolStripMenuItem _stopServiceAndExitMenuItem;
private ToolStripMenuItem _openWinFormsMenuItem;
private ToolStripMenuItem _openNewUiMenuItem;
private ToolStripMenuItem _openNewUiEmbeddedMenuItem;
public NotifyIconManager(GlobalContext globalContext, ServiceManager serviceManager, WinApiAdminService winApiAdminService, WinApiService winApiService)
{
_globalContext = globalContext;
_serviceManager = serviceManager;
_winApiAdminService = winApiAdminService;
_winApiService = winApiService;
_config = globalContext.Config;
}
public NotifyIconManager(GlobalContext globalContext, ServiceManager serviceManager, WinApiAdminService winApiAdminService, WinApiService winApiService)
{
_globalContext = globalContext;
_serviceManager = serviceManager;
_winApiAdminService = winApiAdminService;
_winApiService = winApiService;
_config = globalContext.Config;
}
public void Build()
{
if (_nvTrayMenu != null)
{
return;
}
public void Build()
{
if (_nvTrayMenu != null)
{
return;
}
_nvTrayMenu = new ToolStripMenuItem("NVIDIA presets");
_novideoTrayMenu = new ToolStripMenuItem("Novideo sRGB");
_amdTrayMenu = new ToolStripMenuItem("AMD presets");
_lgTrayMenu = new ToolStripMenuItem("LG presets");
_samsungTrayMenu = new ToolStripMenuItem("Samsung presets");
_gameTrayMenu = new ToolStripMenuItem("Game Launcher");
NotifyIcon = new NotifyIcon()
{
Icon = Resources.AppIcon,
ContextMenuStrip = new ContextMenuStrip(),
Visible = _config.MinimizeToTray
};
_nvTrayMenu = new ToolStripMenuItem("NVIDIA presets");
_novideoTrayMenu = new ToolStripMenuItem("Novideo sRGB");
_amdTrayMenu = new ToolStripMenuItem("AMD presets");
_lgTrayMenu = new ToolStripMenuItem("LG presets");
_samsungTrayMenu = new ToolStripMenuItem("Samsung presets");
_gameTrayMenu = new ToolStripMenuItem("Game Launcher");
NotifyIcon = new NotifyIcon()
{
Icon = Resources.AppIcon,
ContextMenuStrip = new ContextMenuStrip(),
Visible = _config.MinimizeToTray || _config.UiType != UiType.WinForms
};
SetText();
SetText();
NotifyIcon.ContextMenuStrip.Items.AddRange(new ToolStripItem[] {
_nvTrayMenu,
_novideoTrayMenu,
_amdTrayMenu,
_lgTrayMenu,
_samsungTrayMenu,
_gameTrayMenu,
new ToolStripSeparator(),
new ToolStripMenuItem("Open", null, OpenForm),
_openWinFormsMenuItem = new ToolStripMenuItem("Open WinForms UI", null, OpenWinFormsUi),
_openNewUiMenuItem = new ToolStripMenuItem("Open web UI (browser)", null, OpenNewUi),
_openNewUiEmbeddedMenuItem = new ToolStripMenuItem("Open web UI (embedded)", null, OpenNewUiEmbedded),
new ToolStripSeparator(),
new ToolStripMenuItem("Restart", null, Restart),
_stopServiceAndExitMenuItem = new ToolStripMenuItem("Stop Service And Exit", null, StopServiceAndExit),
new ToolStripMenuItem("Exit", null, Exit)
});
NotifyIcon.ContextMenuStrip.Items.AddRange(new ToolStripItem[] {
_nvTrayMenu,
_novideoTrayMenu,
_amdTrayMenu,
_lgTrayMenu,
_samsungTrayMenu,
_gameTrayMenu,
new ToolStripSeparator(),
new ToolStripMenuItem("Open", null, OpenForm),
_openWinFormsMenuItem = new ToolStripMenuItem("Open WinForms UI", null, OpenWinFormsUi),
_openNewUiMenuItem = new ToolStripMenuItem("Open web UI (browser)", null, OpenNewUi),
_openNewUiEmbeddedMenuItem = new ToolStripMenuItem("Open web UI (embedded)", null, OpenNewUiEmbedded),
new ToolStripSeparator(),
new ToolStripMenuItem("Restart", null, Restart),
_stopServiceAndExitMenuItem = new ToolStripMenuItem("Stop Service And Exit", null, StopServiceAndExit),
new ToolStripMenuItem("Exit", null, Exit)
});
NotifyIcon.MouseDoubleClick += trayIcon_MouseDoubleClick;
NotifyIcon.ContextMenuStrip.Opened += trayIconContextMenu_Popup;
}
NotifyIcon.MouseDoubleClick += trayIcon_MouseDoubleClick;
NotifyIcon.ContextMenuStrip.Opened += trayIconContextMenu_Popup;
}
public void HideIcon()
{
if (NotifyIcon != null)
{
NotifyIcon.Visible = false;
}
}
public void HideIcon()
{
if (NotifyIcon != null)
{
NotifyIcon.Visible = false;
}
}
private void StopServiceAndExit(object sender, EventArgs e)
{
_winApiAdminService.StopService();
private void StopServiceAndExit(object sender, EventArgs e)
{
_winApiAdminService.StopService();
Program.Exit();
}
Program.Exit();
}
private void Exit(object sender, EventArgs e)
{
Program.Exit();
}
private void Exit(object sender, EventArgs e)
{
Program.Exit();
}
private void Restart(object sender, EventArgs e)
{
Program.Restart();
}
private void Restart(object sender, EventArgs e)
{
Program.Restart();
}
private void OpenForm(object sender, EventArgs e)
{
Program.OpenDefaultUi();
}
private void OpenForm(object sender, EventArgs e)
{
Program.OpenDefaultUi();
}
private void OpenWinFormsUi(object sender, EventArgs e)
{
_ = Program.OpenMainForm();
}
private void OpenWinFormsUi(object sender, EventArgs e)
{
_ = Program.OpenMainForm();
}
private async void OpenNewUi(object sender, EventArgs e)
{
await Program.OpenNewUi();
}
private async void OpenNewUi(object sender, EventArgs e)
{
await Program.OpenNewUi();
}
private async void OpenNewUiEmbedded(object sender, EventArgs e)
{
await Program.OpenNewUiEmbedded();
}
private async void OpenNewUiEmbedded(object sender, EventArgs e)
{
await Program.OpenNewUiEmbedded();
}
private void trayIcon_MouseDoubleClick(object sender, MouseEventArgs e)
{
Program.OpenDefaultUi();
}
private void trayIcon_MouseDoubleClick(object sender, MouseEventArgs e)
{
Program.OpenDefaultUi();
}
private void trayIconContextMenu_Popup(object sender, EventArgs e)
{
_nvTrayMenu.Visible = _serviceManager.NvService != null;
if (_nvTrayMenu.Visible)
{
var presets = _serviceManager.NvService.GetPresets().Where(x => x.applyColorData || x.applyDithering || x.applyHDR || x.DisplayConfig.ApplyRefreshRate || x.DisplayConfig.ApplyResolution || x.applyDriverSettings || x.applyOther || x.applyOverclocking || x.ApplyColorEnhancements || x.applyHdmiSettings);
private void trayIconContextMenu_Popup(object sender, EventArgs e)
{
_nvTrayMenu.Visible = _serviceManager.NvService != null;
if (_nvTrayMenu.Visible)
{
var presets = _serviceManager.NvService.GetPresets().Where(x => x.applyColorData || x.applyDithering || x.applyHDR || x.DisplayConfig.ApplyRefreshRate || x.DisplayConfig.ApplyResolution || x.applyDriverSettings || x.applyOther || x.applyOverclocking || x.ApplyColorEnhancements || x.applyHdmiSettings);
UpdateTrayMenu(_nvTrayMenu, presets, TrayMenuItemNv_Click);
}
UpdateTrayMenu(_nvTrayMenu, presets, TrayMenuItemNv_Click);
}
_novideoTrayMenu.Visible = _serviceManager.NvService != null && MainWindow.IsCreated();
if (_novideoTrayMenu.Visible)
{
MainWindow.UpdateContextMenu(_novideoTrayMenu);
}
_novideoTrayMenu.Visible = _serviceManager.NvService != null && MainWindow.IsCreated();
if (_novideoTrayMenu.Visible)
{
MainWindow.UpdateContextMenu(_novideoTrayMenu);
}
_amdTrayMenu.Visible = _serviceManager.AmdService != null;
if (_amdTrayMenu.Visible)
{
var presets = _serviceManager.AmdService.GetPresets().Where(x => x.applyColorData || x.applyDithering || x.applyHDR || x.DisplayConfig.ApplyRefreshRate || x.DisplayConfig.ApplyResolution);
_amdTrayMenu.Visible = _serviceManager.AmdService != null;
if (_amdTrayMenu.Visible)
{
var presets = _serviceManager.AmdService.GetPresets().Where(x => x.applyColorData || x.applyDithering || x.applyHDR || x.DisplayConfig.ApplyRefreshRate || x.DisplayConfig.ApplyResolution);
UpdateTrayMenu(_amdTrayMenu, presets, TrayMenuItemAmd_Click);
}
UpdateTrayMenu(_amdTrayMenu, presets, TrayMenuItemAmd_Click);
}
_lgTrayMenu.Visible = _serviceManager.LgService != null;
if (_lgTrayMenu.Visible)
{
var presets = _serviceManager.LgService.GetPresets().Where(x => !string.IsNullOrEmpty(x.appId) || x.Steps.Any());
_lgTrayMenu.Visible = _serviceManager.LgService != null;
if (_lgTrayMenu.Visible)
{
var presets = _serviceManager.LgService.GetPresets().Where(x => !string.IsNullOrEmpty(x.appId) || x.Steps.Any());
_lgTrayMenu.DropDownItems.Clear();
_lgTrayMenu.DropDownItems.Clear();
UpdateTrayMenu(_lgTrayMenu, presets, TrayMenuItemLg_Click);
}
UpdateTrayMenu(_lgTrayMenu, presets, TrayMenuItemLg_Click);
}
_samsungTrayMenu.Visible = _serviceManager.SamsungService != null;
if (_samsungTrayMenu.Visible)
{
var presets = _serviceManager.SamsungService.GetPresets().Where(x => x.Steps.Any());
_samsungTrayMenu.Visible = _serviceManager.SamsungService != null;
if (_samsungTrayMenu.Visible)
{
var presets = _serviceManager.SamsungService.GetPresets().Where(x => x.Steps.Any());
_samsungTrayMenu.DropDownItems.Clear();
_samsungTrayMenu.DropDownItems.Clear();
UpdateTrayMenu(_samsungTrayMenu, presets, TrayMenuItemSamsung_Click);
}
UpdateTrayMenu(_samsungTrayMenu, presets, TrayMenuItemSamsung_Click);
}
_gameTrayMenu.Visible = _serviceManager.GameService != null;
if (_gameTrayMenu.Visible)
{
var presets = _serviceManager.GameService.GetPresets();
_gameTrayMenu.Visible = _serviceManager.GameService != null;
if (_gameTrayMenu.Visible)
{
var presets = _serviceManager.GameService.GetPresets();
_gameTrayMenu.DropDownItems.Clear();
_gameTrayMenu.DropDownItems.Clear();
UpdateTrayMenu(_gameTrayMenu, presets, TrayMenuItemGame_Click);
}
UpdateTrayMenu(_gameTrayMenu, presets, TrayMenuItemGame_Click);
}
_stopServiceAndExitMenuItem.Visible = _winApiService.IsServiceRunning();
}
_stopServiceAndExitMenuItem.Visible = _winApiService.IsServiceRunning();
}
private void UpdateTrayMenu(ToolStripMenuItem menu, IEnumerable<PresetBase> presets, EventHandler eventHandler)
{
menu.DropDownItems.Clear();
private void UpdateTrayMenu(ToolStripMenuItem menu, IEnumerable<PresetBase> presets, EventHandler eventHandler)
{
menu.DropDownItems.Clear();
foreach (var preset in presets.OrderBy(p => p.name))
{
var name = preset.GetTextForMenuItem();
var keys = Keys.None;
foreach (var preset in presets.OrderBy(p => p.name))
{
var name = preset.GetTextForMenuItem();
var keys = Keys.None;
if (!string.IsNullOrEmpty(preset.shortcut))
{
name += " " + preset.shortcut;
//keys = KeyboardShortcutDispatcher.ShortcutToKeys(preset.shortcut);
}
if (!string.IsNullOrEmpty(preset.shortcut))
{
name += " " + preset.shortcut;
//keys = KeyboardShortcutDispatcher.ShortcutToKeys(preset.shortcut);
}
var item = new ToolStripMenuItem(name, null, null, keys);
item.Tag = preset;
item.Click += eventHandler;
item.ForeColor = FormUtils.MenuItemForeColor;
menu.DropDownItems.Add(item);
}
}
var item = new ToolStripMenuItem(name, null, null, keys);
item.Tag = preset;
item.Click += eventHandler;
item.ForeColor = FormUtils.MenuItemForeColor;
menu.DropDownItems.Add(item);
}
}
private async void TrayMenuItemNv_Click(object sender, EventArgs e)
{
var item = sender as ToolStripMenuItem;
var preset = (NvPreset)item.Tag;
private async void TrayMenuItemNv_Click(object sender, EventArgs e)
{
var item = sender as ToolStripMenuItem;
var preset = (NvPreset)item.Tag;
await _serviceManager.NvService?.ApplyPresetUi(preset);
}
await _serviceManager.NvService?.ApplyPresetUi(preset);
}
private async void TrayMenuItemAmd_Click(object sender, EventArgs e)
{
var item = sender as ToolStripMenuItem;
var preset = (AmdPreset)item.Tag;
private async void TrayMenuItemAmd_Click(object sender, EventArgs e)
{
var item = sender as ToolStripMenuItem;
var preset = (AmdPreset)item.Tag;
await _serviceManager.AmdService?.ApplyPresetUi(preset);
}
await _serviceManager.AmdService?.ApplyPresetUi(preset);
}
private async void TrayMenuItemGame_Click(object sender, EventArgs e)
{
var item = sender as ToolStripMenuItem;
var preset = (GamePreset)item.Tag;
private async void TrayMenuItemGame_Click(object sender, EventArgs e)
{
var item = sender as ToolStripMenuItem;
var preset = (GamePreset)item.Tag;
await _serviceManager.GameService?.ApplyPresetUi(preset);
}
await _serviceManager.GameService?.ApplyPresetUi(preset);
}
private async void TrayMenuItemLg_Click(object sender, EventArgs e)
{
var item = sender as ToolStripMenuItem;
var preset = (LgPreset)item.Tag;
private async void TrayMenuItemLg_Click(object sender, EventArgs e)
{
var item = sender as ToolStripMenuItem;
var preset = (LgPreset)item.Tag;
await _serviceManager.LgService?.ApplyPresetUi(preset);
}
await _serviceManager.LgService?.ApplyPresetUi(preset);
}
private async void TrayMenuItemSamsung_Click(object sender, EventArgs e)
{
var item = sender as ToolStripMenuItem;
var preset = (SamsungPreset)item.Tag;
private async void TrayMenuItemSamsung_Click(object sender, EventArgs e)
{
var item = sender as ToolStripMenuItem;
var preset = (SamsungPreset)item.Tag;
await _serviceManager.SamsungService?.ApplyPresetUi(preset);
}
await _serviceManager.SamsungService?.ApplyPresetUi(preset);
}
internal void SetText(string text = null)
{
var fullText = $"{_globalContext.ApplicationTitleAdmin}{(text == null ? "" : $"\n{text}")}";
internal void SetText(string text = null)
{
var fullText = $"{_globalContext.ApplicationTitleAdmin}{(text == null ? "" : $"\n{text}")}";
FormUtils.SetNotifyIconText(NotifyIcon, fullText.Trim());
}
FormUtils.SetNotifyIconText(NotifyIcon, fullText.Trim());
}
}
+17 -2
View File
@@ -2,6 +2,7 @@
using ColorControl.Shared.Contracts;
using ColorControl.Shared.EventDispatcher;
using ColorControl.Shared.Services;
using ColorControl.UI;
using NLog;
using System;
using System.Collections.Generic;
@@ -34,6 +35,7 @@ public class OptionsService
public Config GetConfig()
{
_globalContext.Config.AutoStart = _winApiService.TaskExists(Program.TS_TASKNAME);
_globalContext.Config.CurrentUiPort = Blazor.GetCurrentPort();
return _globalContext.Config;
}
@@ -109,7 +111,7 @@ public class OptionsService
public bool SetMinimizeToTray(bool value)
{
_globalContext.Config.MinimizeToTray = value;
Program.GetNotifyIcon().Visible = _globalContext.Config.MinimizeToTray;
Program.GetNotifyIcon().Visible = _globalContext.Config.MinimizeToTray || _globalContext.Config.UiType != UiType.WinForms;
return true;
}
@@ -147,12 +149,14 @@ public class OptionsService
{
_globalContext.Config.UiType = uiType;
var _ = Program.StartOrStopUiServer();
return true;
}
public bool SetUiPort(int uiPort)
{
if (uiPort is <= 0 or > 65535)
if (uiPort is < 0 or > 65535)
{
return false;
}
@@ -164,6 +168,17 @@ public class OptionsService
_globalContext.Config.UiPort = uiPort;
var _ = Program.StartOrStopUiServer();
return true;
}
public bool SetUiAllowRemoteConnections(bool value)
{
_globalContext.Config.UiAllowRemoteConnections = value;
var _ = Program.StartOrStopUiServer();
return true;
}
+1 -1
View File
@@ -58,7 +58,7 @@ namespace ColorControl.Services.Common
public abstract List<string> GetInfo();
public List<T> GetPresets()
public virtual List<T> GetPresets()
{
return _presets;
}
@@ -304,6 +304,16 @@ namespace ColorControl.Services.GameLauncher
return true;
}
public override List<GamePreset> GetPresets()
{
_presets.ForEach(p =>
{
p.PathExists = p.Path?.Contains(":\\") != true || File.Exists(p.Path);
});
return _presets;
}
public bool UpdatePreset(GamePreset specPreset)
{
ValidatePreset(specPreset);
+15 -8
View File
@@ -235,10 +235,10 @@ namespace ColorControl.Services.LG
AddGenericPictureAction("noiseReduction", typeof(OffToHigh), title: "Noise Reduction");
AddGenericPictureAction("mpegNoiseReduction", typeof(OffToHigh), title: "MPEG Noise Reduction");
AddHdmiPictureAction("gameMode_hdmi", typeof(OffToOn), category: "other", title: "Game Optimizer HDMI1", fromModelYear: ModelYear.Series2021);
AddHdmiPictureAction("gameMode_hdmi", typeof(OffToOn), category: "other", title: "Game Optimizer HDMIX", fromModelYear: ModelYear.Series2021);
AddGenericPictureAction("gameOptimization", typeof(OffToOn), category: "other", title: "VRR & G-Sync", fromModelYear: ModelYear.Series2021);
AddGenericPictureAction("inputOptimization", typeof(InputOptimization), category: "other", title: "Prevent Input Delay(Input Lag)", fromModelYear: ModelYear.Series2021);
AddHdmiPictureAction("gameOptimizationHDMI", typeof(OffToOn), category: "other");
AddHdmiPictureAction("gameOptimizationHDMI", typeof(OffToOn), category: "other", fromModelYear: ModelYear.Series2019);
AddGenericPictureAction("freesync", typeof(OffToOn), category: "other", title: "AMD FreeSync Premium", fromModelYear: ModelYear.Series2020);
AddHdmiPictureAction("freesyncOLEDHDMI", typeof(OffToOn), category: "other", fromModelYear: ModelYear.Series2020);
AddGenericPictureAction("enableQFT", typeof(OffToOn), category: "other", title: "Quick Frame Transport", fromModelYear: ModelYear.Series2025);
@@ -507,14 +507,21 @@ namespace ColorControl.Services.LG
//_lgTvApi.Test3();
if (_lgTvApi != null && !Utils.ConsoleOpened)
{
var info = await _lgTvApi.GetSystemInfo("modelName");
if (info != null)
try
{
ModelName = info.modelName;
SetModelYear();
}
var info = await _lgTvApi.GetSystemInfo("modelName");
if (info != null)
{
ModelName = info.modelName;
SetModelYear();
}
Logger.Debug($"LG TV with ip-address {IpAddress} has model name {ModelName}");
Logger.Debug($"LG TV with ip-address {IpAddress} has model name {ModelName}");
}
catch (Exception ex)
{
Logger.Warn($"Could not get model name for LG TV with ip-address {IpAddress}: {ex.Message}");
}
await _lgTvApi.SubscribeVolume(VolumeChanged);
await _lgTvApi.SubscribePowerState(PowerStateChanged);
+44 -43
View File
@@ -3,49 +3,50 @@ using System.Collections.Generic;
namespace ColorControl.Services.LG
{
class LgServiceConfig
{
public bool PowerOnAfterStartup { get; set; }
public int PowerOnDelayAfterResume { get; set; }
public int PowerOnRetries { get; set; }
public string PreferredMacAddress { get; set; }
public string DeviceSearchKey { get; set; }
public List<LgDevice> Devices { get; set; }
public bool ShowRemoteControl { get; set; }
public bool ShowAdvancedActions { get; set; }
public string GameBarShortcut { get; set; }
public int GameBarTop { get; set; }
public int GameBarLeft { get; set; }
public int ShutdownDelay { get; set; }
public bool SetSelectedDeviceByPowerOn { get; set; }
public string QuickAccessShortcut { get; set; }
public int DefaultButtonDelay { get; set; }
public int GameBarShowingTime { get; set; }
class LgServiceConfig
{
public bool PowerOnAfterStartup { get; set; }
public int PowerOnDelayAfterResume { get; set; }
public int PowerOnRetries { get; set; }
public string PreferredMacAddress { get; set; }
public string DeviceSearchKey { get; set; }
public List<LgDevice> Devices { get; set; }
public bool ShowRemoteControl { get; set; }
public bool ShowAdvancedActions { get; set; }
public string GameBarShortcut { get; set; }
public int GameBarTop { get; set; }
public int GameBarLeft { get; set; }
public int ShutdownDelay { get; set; }
public bool SetSelectedDeviceByPowerOn { get; set; }
public string QuickAccessShortcut { get; set; }
public int DefaultButtonDelay { get; set; }
public int GameBarShowingTime { get; set; }
public LgServiceConfig()
{
PowerOnDelayAfterResume = 5000;
ShutdownDelay = 1000;
DeviceSearchKey = "[LG]";
PowerOnRetries = 10;
Devices = new List<LgDevice>();
GameBarTop = 100;
GameBarLeft = 100;
DefaultButtonDelay = 200;
GameBarShowingTime = 5000;
}
public LgServiceConfig()
{
PowerOnDelayAfterResume = 5000;
ShutdownDelay = 1000;
DeviceSearchKey = "[LG]";
PowerOnRetries = 10;
Devices = new List<LgDevice>();
GameBarTop = 100;
GameBarLeft = 100;
DefaultButtonDelay = 200;
GameBarShowingTime = 5000;
}
internal void Update(LgServiceConfigDto config)
{
PowerOnAfterStartup = config.PowerOnAfterStartup;
ShutdownDelay = config.ShutdownDelay;
DeviceSearchKey = config.DeviceSearchKey;
PowerOnRetries = config.PowerOnRetries;
DefaultButtonDelay = config.DefaultButtonDelay;
GameBarShowingTime = config.GameBarShowingTime;
ShowAdvancedActions = config.ShowAdvancedActions;
QuickAccessShortcut = config.QuickAccessShortcut;
SetSelectedDeviceByPowerOn = config.SetSelectedDeviceByPowerOn;
}
}
internal void Update(LgServiceConfigDto config)
{
PowerOnAfterStartup = config.PowerOnAfterStartup;
ShutdownDelay = config.ShutdownDelay;
DeviceSearchKey = config.DeviceSearchKey;
PowerOnRetries = config.PowerOnRetries;
DefaultButtonDelay = config.DefaultButtonDelay;
GameBarShowingTime = config.GameBarShowingTime;
ShowAdvancedActions = config.ShowAdvancedActions;
QuickAccessShortcut = config.QuickAccessShortcut;
SetSelectedDeviceByPowerOn = config.SetSelectedDeviceByPowerOn;
GameBarShortcut = config.GameBarShortcut;
}
}
}
File diff suppressed because it is too large. Load diff
+124 -7
View File
@@ -36,7 +36,7 @@ using static ColorControl.Shared.Contracts.NVIDIA.NvHdrSettings;
namespace ColorControl.Services.NVIDIA
{
class NvService : GraphicsService<NvPreset>
partial class NvService : GraphicsService<NvPreset>
{
private static readonly NLog.Logger Logger = NLog.LogManager.GetCurrentClassLogger();
@@ -179,6 +179,33 @@ namespace ColorControl.Services.NVIDIA
_powerEventDispatcher.RegisterAsyncEventHandler(PowerEventDispatcher.Event_Resume, PowerModeResume);
_powerEventDispatcher.RegisterEventHandler(PowerEventDispatcher.Event_Startup, PowerModeChanged);
_powerEventDispatcher.RegisterEventHandler(PowerEventDispatcher.Event_Shutdown, PowerModeChanged);
var _ = ApplyNvPresetOnStartup();
}
private async Task ApplyNvPresetOnStartup()
{
var startUpParams = _globalContext.StartUpParams;
var presetIdOrName = !string.IsNullOrEmpty(startUpParams.NvidiaPresetIdOrName) ? startUpParams.NvidiaPresetIdOrName : _globalContext.Config.NvPresetId_ApplyOnStartup.ToString();
if (string.IsNullOrEmpty(presetIdOrName))
{
return;
}
var preset = GetPresetByIdOrName(presetIdOrName);
if (preset == null)
{
if (string.IsNullOrEmpty(startUpParams.NvidiaPresetIdOrName))
{
_globalContext.Config.NvPresetId_ApplyOnStartup = 0;
}
return;
}
preset.IsStartupPreset = true;
await ApplyPreset(preset);
}
public override List<string> GetInfo()
@@ -360,15 +387,31 @@ namespace ColorControl.Services.NVIDIA
public override async Task<bool> ApplyPreset(NvPreset preset)
{
var result = true;
return (await ApplyPresetWithResult(preset)).Result;
}
public async Task<GenericBoolResult> ApplyPresetWithResult(NvPreset preset)
{
var result = GenericBoolResult.Success;
var display = await WaitForDisplayAsync(preset);
if (display == null)
{
Logger.Warn($"Cannot apply preset {preset.IdOrName} because the associated display is not active/found");
var error = $"Cannot apply preset {preset.IdOrName} because the associated display is not active/found";
return false;
Logger.Warn(error);
return GenericBoolResult.FromError(error);
}
if (preset.DdcSettings.ApplyDdc)
{
var ddcResult = await ApplyDdcSettings(display, preset.DdcSettings);
if (!ddcResult.Result && ddcResult.ErrorMessages.Count > 0)
{
result.AddError(ddcResult.ErrorMessages[0]);
}
}
var hdrEnabled = IsHDREnabled();
@@ -418,7 +461,7 @@ namespace ColorControl.Services.NVIDIA
{
if (!SetMode(preset.DisplayConfig, true))
{
result = false;
result.AddError(WinError.GetMessage());
}
}
@@ -449,7 +492,7 @@ namespace ColorControl.Services.NVIDIA
{
if (!SetDithering(preset.DitherState, preset: preset))
{
result = false;
result.Result = false;
}
}
@@ -471,13 +514,15 @@ namespace ColorControl.Services.NVIDIA
if (Config.ShowOverclocking && preset.applyOverclocking)
{
result = ApplyOverclocking(preset.ocSettings);
result.Result = ApplyOverclocking(preset.ocSettings);
}
await ExecuteStepsAsync(preset, _globalContext);
//SetMonitorScaling();
//CCD.SetScaling(100);
//WriteDDC(display);
//ReadDDC(display, 0x10);
_lastAppliedPreset = preset;
@@ -489,6 +534,45 @@ namespace ColorControl.Services.NVIDIA
return result;
}
private async Task<GenericBoolResult> ApplyDdcSettings(Display display, DdcSettings ddcSettings)
{
foreach (var ddcSetting in ddcSettings.Settings)
{
var result = await WriteDDC(display.Name, ddcSetting.VcpCode, ddcSetting.Value, ddcSetting.ValueChangeType);
if (result.Result != true)
{
return result;
}
if (ddcSetting != ddcSettings.Settings.Last())
{
await Task.Delay(50);
}
}
if (ddcSettings.RemoveOutOfRangeOsd)
{
var _ = RemoveOutOfRangeOsd(display, ddcSettings.Settings.Count > 0);
}
return GenericBoolResult.Success;
}
private static async Task RemoveOutOfRangeOsd(Display display, bool delay)
{
if (delay)
{
await Task.Delay(5000);
}
foreach (var value in Enumerable.Range(0, 16).Reverse())
{
await WriteDDC(display.Name, 0xCC, (uint)value, validate: false);
await Task.Delay(180);
}
}
private void SetNovideoSettings(string displayId, NovideoSettings novideoSettings)
{
MainWindow.ApplySettings(displayId, novideoSettings.ApplyClamp, (int)novideoSettings.ColorSpace);
@@ -1241,6 +1325,39 @@ namespace ColorControl.Services.NVIDIA
CCD.SetDisplayDefaultColorProfile(display.Name, name, _globalContext.Config.SetMinTmlAndMaxTml, IsHDREnabled(display));
}
//public void WriteDDC(Display display, byte vcpCode, uint value)
//{
// if (display == null)
// {
// return;
// }
// byte i2cDeviceAddr = 0x37;
// //short value = 8704;
// //var data = new byte[] { 0x84, 0x03, 0x10, 0x00, 0x64, 0xDD };
// //var data = new byte[] { 0x84, 0x03, 0x15, 0x00, 0x2D, 0xDD };
// var value1 = (byte)(value >> 8);
// var value2 = (byte)(value & 0xFF);
// var data = new byte[] { 0x84, 0x03, vcpCode, value1, value2, 0xDD };
// I2CInfoV3.FillDDCCIChecksum((byte)(i2cDeviceAddr << 1), [0x51], data);
// var info = new I2CInfoV2(display.Output.OutputId, true, i2cDeviceAddr, [0x51], data);
// display.DisplayDevice.PhysicalGPU.WriteI2C(info);
//}
public GenericResult<VcpInfo> GetVcpInfo(string displayId, byte vcpCode)
{
var displays = GetSimpleDisplayInfos();
var display = displays.FirstOrDefault(d => d.DisplayId == displayId)?.Display;
return ReadDDC(display?.Name, vcpCode);
}
public void TestResolution()
{
//CCD.SetDisplayConfig();
@@ -996,8 +996,10 @@ namespace ColorControl.Services.Samsung
return true;
}
var newPreset = new SamsungPreset(specPreset);
newPreset.name = specPreset.name;
var newPreset = new SamsungPreset(specPreset)
{
name = specPreset.name
};
_presets.Add(newPreset);
@@ -25,6 +25,7 @@ namespace ColorControl.Svc
private readonly WinApiAdminService _winApiAdminService;
private readonly WolService _wolService;
private static string LgConfigFile = "LgConfig.json";
public bool IsRunning { get; private set; }
public string PipeName { get; set; } = PipeUtils.ServicePipe;
@@ -57,6 +58,8 @@ namespace ColorControl.Svc
Logger.Debug($"EXECUTING IN BACKGROUND: {PipeName}");
IsRunning = true;
while (!stoppingToken.IsCancellationRequested)
{
Logger.Debug("WAITING FOR MESSAGE...");
@@ -113,6 +116,7 @@ namespace ColorControl.Svc
// recovery options, we need to terminate the process with a non-zero exit code.
//Environment.Exit(1);
}
IsRunning = false;
}
private async Task<SvcResultMessage> HandleMessageAsync(string json)
+88 -68
View File
@@ -1,5 +1,6 @@
using ColorControl.Shared.Common;
using ColorControl.Shared.XForms;
using ColorControl.UI;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.ComponentModel;
@@ -10,93 +11,112 @@ namespace ColorControl.XForms;
public partial class BrowserWindow : BaseWindow
{
private static BrowserWindow _window;
private readonly GlobalContext _globalContext;
public string Url { get; private set; }
private static BrowserWindow _window;
private readonly GlobalContext _globalContext;
public BrowserWindow(GlobalContext globalContext)
{
InitializeComponent();
private string _currentUrl;
public string Url { get; private set; }
DataContext = this;
public BrowserWindow(GlobalContext globalContext)
{
InitializeComponent();
_globalContext = globalContext;
DataContext = this;
Title = _globalContext.ApplicationTitleAdmin;
_globalContext = globalContext;
Width = _globalContext.Config.XFormWidth;
Height = _globalContext.Config.XFormHeight;
}
Title = _globalContext.ApplicationTitleAdmin;
private void OnRequestNavigate(object sender, RequestNavigateEventArgs e)
{
var processStartInfo = new System.Diagnostics.ProcessStartInfo(e.Uri.AbsoluteUri)
{
UseShellExecute = true,
};
System.Diagnostics.Process.Start(processStartInfo);
}
Width = _globalContext.Config.XFormWidth;
Height = _globalContext.Config.XFormHeight;
}
public static void CreateAndShow(string url, bool show = true)
{
Utils.EnsureApplication();
private void OnRequestNavigate(object sender, RequestNavigateEventArgs e)
{
var processStartInfo = new System.Diagnostics.ProcessStartInfo(e.Uri.AbsoluteUri)
{
UseShellExecute = true,
};
System.Diagnostics.Process.Start(processStartInfo);
}
_window ??= Program.ServiceProvider.GetRequiredService<BrowserWindow>();
public static void CreateAndShow(string url, bool show = true)
{
Utils.EnsureApplication();
if (show)
{
_window.Url = url;
_window.WindowState = WindowState.Normal;
_window.Show();
_window.Topmost = true;
_window.Topmost = false;
}
}
var reload = _window != null && _window._currentUrl != url;
protected override void OnClosing(CancelEventArgs e)
{
e.Cancel = true;
_window ??= Program.ServiceProvider.GetRequiredService<BrowserWindow>();
_globalContext.Config.XFormWidth = Width;
_globalContext.Config.XFormHeight = Height;
if (show)
{
_window.Url = url;
_window._currentUrl = url;
if (reload)
{
_window.webView.Source = new Uri(url);
}
_window.WindowState = WindowState.Normal;
_window.Show();
_window.Topmost = true;
_window.Topmost = false;
}
}
if (_globalContext.Config.MinimizeOnClose)
{
Hide();
protected override void OnClosing(CancelEventArgs e)
{
e.Cancel = true;
return;
}
_globalContext.Config.XFormWidth = Width;
_globalContext.Config.XFormHeight = Height;
Program.Exit();
}
if (_globalContext.Config.MinimizeOnClose)
{
Hide();
protected override void OnStateChanged(EventArgs e)
{
if (WindowState == WindowState.Minimized)
{
Hide();
}
return;
}
base.OnStateChanged(e);
}
Program.Exit();
}
private void Close_Click(object sender, RoutedEventArgs e)
{
Close();
}
protected override void OnStateChanged(EventArgs e)
{
if (WindowState == WindowState.Minimized)
{
Hide();
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
}
base.OnStateChanged(e);
}
private void webView_NavigationStarting(object sender, Microsoft.Web.WebView2.Core.CoreWebView2NavigationStartingEventArgs e)
{
var core = _window.webView.CoreWebView2;
private void Close_Click(object sender, RoutedEventArgs e)
{
Close();
}
if (core != null && !core.Settings.UserAgent.Contains("ColorControlEmbedded"))
{
core.Settings.UserAgent = core.Settings.UserAgent + " (ColorControlEmbedded)";
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
}
}
private void webView_NavigationStarting(object sender, Microsoft.Web.WebView2.Core.CoreWebView2NavigationStartingEventArgs e)
{
var core = _window.webView.CoreWebView2;
if (core != null && !core.Settings.UserAgent.Contains("ColorControlEmbedded"))
{
core.Settings.UserAgent = core.Settings.UserAgent + " (ColorControlEmbedded)";
}
if (e.Uri.ToString().Contains(":0/"))
{
e.Cancel = true;
var port = Blazor.GetCurrentPort();
var newUrl = e.Uri.Replace(":0/", $":{port}/");
CreateAndShow(newUrl);
}
}
}
+1 -1
View File
@@ -8,8 +8,8 @@ namespace LittleCms
public class IccProfile : CmsObject
{
public override CmsContext Context { get; }
public byte[]? RawBytes { get; }
public byte[]? RawBytes { get; }
public IccProfile(IntPtr handle, bool moveOwnership) : base(handle, moveOwnership)
{
Context = CmsContext.GetFromHandle(cmsGetProfileContextID(handle));
+6 -1
View File
@@ -1,10 +1,11 @@
using System;
using LittleCms;
namespace MHC2Gen;
public class GenerateProfileCommand
{
public string Description { get; set; } = "";
public RenderingIntent RenderingIntent { get; set; } = RenderingIntent.PERCEPTUAL;
public bool HasExtraInfo { get; set; }
public bool IsHDRProfile { get; set; }
public RgbPrimaries DevicePrimaries { get; set; } = new RgbPrimaries(RgbPrimaries.sRGB);
@@ -18,6 +19,10 @@ public class GenerateProfileCommand
public double SDRMaxBrightness { get; set; } = 100;
public double SDRBrightnessBoost { get; set; }
public double ShadowDetailBoost { get; set; }
public double ShadowDetailRange { get; set; } = 30;
public double RedGain { get; set; }
public double GreenGain { get; set; }
public double BlueGain { get; set; }
public double Gamma { get; set; } = 2.2;
public double ToneMappingFromLuminance { get; set; }
+93 -45
View File
@@ -77,54 +77,89 @@ namespace MHC2Gen
}
}
public void ApplySdrAcm(double whiteLuminance = 120.0, double blackLuminance = 0.0, double gamma = 2.2, double boostPercentage = 0, double shadowDetailBoost = 0)
public void ApplySdrAcm(double whiteLuminance = 120.0, double blackLuminance = 0.0, double gamma = 2.2, double boostPercentage = 0, double shadowDetailBoost = 0, double shadowDetailRange = 30, double redGain = 100, double greenGain = 100, double blueGain = 100)
{
var lutSize = 1024;
var lutSizeDiv = lutSize - 1;
var amplifier = boostPercentage == 0 ? 1 : 1 + boostPercentage / 100;
shadowDetailRange = lutSize * shadowDetailRange / 100;
RegammaLUT = new double[3, lutSize];
for (var i = 0; i < lutSize; i++)
for (var c = 0; c < 3; c++)
{
var (_, value) = CmsFunctions.SrgbAcm(i, whiteLuminance, blackLuminance, gamma, lutSize - 1);
var gain = c == 0 ? redGain : c == 1 ? greenGain : blueGain;
// Average between sSRGB and Piecewise gamma
if (shadowDetailBoost > 0)
for (var i = 0; i < lutSize; i++)
{
var piecewiseValue = (double)i / 1023;
var (_, value) = CmsFunctions.SrgbAcm(i, whiteLuminance, blackLuminance, gamma, lutSize - 1);
var correctedBoost = (shadowDetailBoost * (1023 - i)) / 1023;
value = ApplyGain(gain, value, i, lutSizeDiv);
value = ApplyShadowDetailBoost(shadowDetailBoost, shadowDetailRange, value, i);
value = ((value * (100 - correctedBoost)) + (piecewiseValue * correctedBoost)) / 100;
RegammaLUT[c, i] = value * amplifier;
}
}
for (var c = 0; c < 3; c++)
}
public void ApplyPiecewise(double boostPercentage = 0, double shadowDetailBoost = 0, double shadowDetailRange = 30, double redGain = 100, double greenGain = 100, double blueGain = 100)
{
var lutSize = 1024;
var lutSizeDiv = lutSize - 1;
var amplifier = boostPercentage == 0 ? 1 : 1 + boostPercentage / 100;
shadowDetailRange = lutSize * shadowDetailRange / 100;
RegammaLUT = new double[3, lutSize];
for (var c = 0; c < 3; c++)
{
var gain = c == 0 ? redGain : c == 1 ? greenGain : blueGain;
for (var i = 0; i < lutSize; i++)
{
//var tempPercentage = boostPercentage * ((double)i / lutSize);
var value = (double)i / lutSizeDiv;
//var amplifier = tempPercentage == 0 ? 1 : tempPercentage > 0 ? 1 + tempPercentage / 100 : 1 - tempPercentage / 100;
value = ApplyGain(gain, value, i, lutSizeDiv);
value = ApplyShadowDetailBoost(shadowDetailBoost, shadowDetailRange, value, i);
RegammaLUT[c, i] = value * amplifier;
}
}
}
public void ApplyPiecewise(double boostPercentage = 0)
public static double ApplyGain(double gain, double value, int lutIndex, int lutSizeDiv)
{
var lutSize = 1024;
var amplifier = boostPercentage == 0 ? 1 : 1 + boostPercentage / 100;
RegammaLUT = new double[3, lutSize];
for (var i = 0; i < lutSize; i++)
if (gain != 100 && gain >= 1 && gain <= 200)
{
var value = (double)i / 1023;
var skewedGain = (((lutSizeDiv - (double)lutIndex) / lutSizeDiv) * (gain - 100)) + 100;
for (var c = 0; c < 3; c++)
value *= skewedGain / 100;
}
return value;
}
public static double ApplyShadowDetailBoost(double boost, double range, double value, int lutIndex)
{
if (boost != 0 && lutIndex < range)
{
var correctedBoost = (boost * (range - lutIndex)) / range;
// Average between sSRGB and Piecewise gamma
if (true)
{
RegammaLUT[c, i] = value * amplifier;
var piecewiseValue = (double)lutIndex / 1023;
value = ((value * (100 - correctedBoost)) + (piecewiseValue * correctedBoost)) / 100;
}
else
{
value *= (100 + correctedBoost) / 100;
}
}
return value;
}
public void ApplyToneMappingCurve(double maxInputNits = 400, double maxOutputNits = 400, double curve_like = 400)
@@ -223,30 +258,22 @@ namespace MHC2Gen
return L;
}
public void ApplyGamma(double gamma = 2.2, double shadowDetailBoost = 0)
public void ApplyGamma(double gamma = 2.2, double shadowDetailBoost = 0, double shadowDetailRange = 30)
{
var lutSize = 1024;
var lutSizeDiv = lutSize - 1;
shadowDetailRange = lutSize * shadowDetailRange / 100;
RegammaLUT = new double[3, lutSize];
for (var i = 0; i < lutSize; i++)
for (var c = 0; c < 3; c++)
{
var value = CmsFunctions.RgbToLinear((double)i / (lutSize - 1), gamma);
// Average between sSRGB and Piecewise gamma
if (shadowDetailBoost > 0)
for (var i = 0; i < lutSize; i++)
{
var piecewiseValue = (double)i / lutSize;
var value = CmsFunctions.RgbToLinear((double)i / lutSizeDiv, gamma);
var correctedBoost = (shadowDetailBoost * (lutSize - i)) / lutSize;
value = ApplyShadowDetailBoost(shadowDetailBoost, shadowDetailRange, value, i);
value = ((value * (100 - correctedBoost)) + (piecewiseValue * correctedBoost)) / 100;
}
for (var c = 0; c < 3; c++)
{
RegammaLUT[c, i] = value;
}
}
@@ -355,10 +382,13 @@ namespace MHC2Gen
public double SDRMaxBrightness { get; set; }
public double SDRBrightnessBoost { get; set; }
public double ShadowDetailBoost { get; set; }
public double ShadowDetailRange { get; set; }
public double RedGain { get; set; }
public double GreenGain { get; set; }
public double BlueGain { get; set; }
public ColorGamut TargetGamut { get; set; }
public double ToneMappingFromLuminance { get; set; }
public double ToneMappingToLuminance { get; set; }
public double HdrGammaMultiplier { get; set; }
public double HdrBrightnessMultiplier { get; set; }
}
@@ -568,6 +598,21 @@ namespace MHC2Gen
if (json != null)
{
ExtraInfoTag = JsonSerializer.Deserialize<ExtraInfoTag>(json);
if (ExtraInfoTag != null)
{
if (ExtraInfoTag.RedGain == 0)
{
ExtraInfoTag.RedGain = 100;
}
if (ExtraInfoTag.GreenGain == 0)
{
ExtraInfoTag.GreenGain = 100;
}
if (ExtraInfoTag.BlueGain == 0)
{
ExtraInfoTag.BlueGain = 100;
}
}
}
}
}
@@ -1089,19 +1134,18 @@ namespace MHC2Gen
{
if (command.SDRTransferFunction == SDRTransferFunction.Piecewise)
{
MHC2.ApplyPiecewise(command.SDRBrightnessBoost);
MHC2.ApplyPiecewise(command.SDRBrightnessBoost, command.ShadowDetailBoost, command.ShadowDetailRange, command.RedGain, command.GreenGain, command.BlueGain);
}
else if (command.SDRTransferFunction == SDRTransferFunction.PurePower)
{
MHC2.ApplySdrAcm(command.SDRMaxBrightness, command.SDRMinBrightness, command.Gamma, command.SDRBrightnessBoost, command.ShadowDetailBoost);
MHC2.ApplySdrAcm(command.SDRMaxBrightness, command.SDRMinBrightness, command.Gamma, command.SDRBrightnessBoost, command.ShadowDetailBoost, command.ShadowDetailRange, command.RedGain, command.GreenGain, command.BlueGain);
}
else if (command.SDRTransferFunction == SDRTransferFunction.BT_1886)
{
MHC2.ApplySdrAcm(120, 0.03, 2.4, command.SDRBrightnessBoost, command.ShadowDetailBoost);
MHC2.ApplySdrAcm(120, 0.03, 2.4, command.SDRBrightnessBoost, command.ShadowDetailBoost, command.ShadowDetailRange, command.RedGain, command.GreenGain, command.BlueGain);
}
else if (command.SDRTransferFunction == SDRTransferFunction.ToneMappedPiecewise)
{
double from_nits = command.ToneMappingFromLuminance;
double to_nits = command.ToneMappingToLuminance;
@@ -1114,7 +1158,6 @@ namespace MHC2Gen
MHC2.ApplyToneMappingCurve(from_nits, from_nits, curve_like);
MHC2.ApplyToneMappingCurveGamma(from_nits, from_nits, gamma_like);
}
}
else
@@ -1125,7 +1168,7 @@ namespace MHC2Gen
}
else
{
MHC2.ApplyPiecewise(command.SDRBrightnessBoost);
MHC2.ApplyPiecewise(command.SDRBrightnessBoost, command.ShadowDetailBoost, command.ShadowDetailRange, command.RedGain, command.GreenGain, command.BlueGain);
}
}
@@ -1162,7 +1205,7 @@ namespace MHC2Gen
outputProfile.HeaderManufacturer = profile.HeaderManufacturer;
outputProfile.HeaderModel = profile.HeaderModel;
outputProfile.HeaderAttributes = profile.HeaderAttributes;
outputProfile.HeaderRenderingIntent = RenderingIntent.PERCEPTUAL;
outputProfile.HeaderRenderingIntent = command.RenderingIntent;
var descAppendix = $" ({GetDeviceDescription()})";
var new_desc = command.Description ?? "";
@@ -1181,11 +1224,16 @@ namespace MHC2Gen
SDRMaxBrightness = command.SDRMaxBrightness,
SDRBrightnessBoost = command.SDRBrightnessBoost,
ShadowDetailBoost = command.ShadowDetailBoost,
ShadowDetailRange = command.ShadowDetailRange,
RedGain = command.RedGain,
GreenGain = command.GreenGain,
BlueGain = command.BlueGain,
TargetGamut = command.ColorGamut,
ToneMappingFromLuminance = command.ToneMappingFromLuminance,
ToneMappingToLuminance = command.ToneMappingToLuminance,
HdrGammaMultiplier = command.HdrGammaMultiplier,
HdrBrightnessMultiplier = command.HdrBrightnessMultiplier
HdrBrightnessMultiplier = command.HdrBrightnessMultiplier,
};
var ccDesc = JsonSerializer.Serialize(extraInfoTag);
+5
View File
@@ -54,6 +54,7 @@ public class MHC2Wrapper
return new GenerateProfileCommand
{
RenderingIntent = profile.HeaderRenderingIntent,
Description = deviceContext.GetDescription(),
HasExtraInfo = deviceContext.ExtraInfoTag != null,
IsHDRProfile = isAssociatedAsHdr,
@@ -68,6 +69,10 @@ public class MHC2Wrapper
SDRTransferFunction = deviceContext.ExtraInfoTag?.SDRTransferFunction ?? SDRTransferFunction.PurePower,
SDRBrightnessBoost = deviceContext.ExtraInfoTag?.SDRBrightnessBoost ?? 0,
ShadowDetailBoost = deviceContext.ExtraInfoTag?.ShadowDetailBoost ?? 0,
ShadowDetailRange = deviceContext.ExtraInfoTag?.ShadowDetailRange ?? 30,
RedGain = deviceContext.ExtraInfoTag?.RedGain ?? 100,
GreenGain = deviceContext.ExtraInfoTag?.GreenGain ?? 100,
BlueGain = deviceContext.ExtraInfoTag?.BlueGain ?? 100,
Gamma = deviceContext.ExtraInfoTag?.Gamma ?? 2.2,
ToneMappingFromLuminance = deviceContext.ExtraInfoTag?.ToneMappingFromLuminance ?? 400,
ToneMappingToLuminance = deviceContext.ExtraInfoTag?.ToneMappingToLuminance ?? 400,
File diff suppressed because it is too large. Load diff
+3
View File
@@ -48,6 +48,9 @@ namespace NvAPIWrapper.Native.Display
[Description("Display - No Scaling")]
GPUScanOutToClosest = 7,
[Description("GPU - Integer Scaling")]
GPUIntegerAspectScaling = 8,
/// <summary>
/// Customized scaling - For future use
/// </summary>
+50
View File
@@ -0,0 +1,50 @@
namespace ColorControl.Shared.Common;
public class GenericBoolResult : GenericResult<bool>
{
public static GenericBoolResult Success => new() { HasResult = true, Result = true };
public static new GenericBoolResult FromError(string error)
{
return new GenericBoolResult
{
HasResult = false,
ErrorMessages = [error]
};
}
public override void AddError(string error)
{
base.AddError(error);
Result = false;
}
}
public class GenericResult<T>
{
public bool HasResult { get; set; }
public List<string> ErrorMessages { get; protected set; } = [];
public T Result { get; set; }
public static GenericResult<T> FromError(string error)
{
return new GenericResult<T>
{
HasResult = false,
ErrorMessages = [error]
};
}
public static GenericResult<T> FromSuccess(T result)
{
return new GenericResult<T>
{
HasResult = true,
Result = result
};
}
public virtual void AddError(string error)
{
ErrorMessages.Add(error);
}
}
+115 -110
View File
@@ -1,122 +1,127 @@

using System.ComponentModel;
using System.Text.Json.Serialization;
namespace ColorControl.Shared.Contracts
{
public enum ElevationMethod
{
None = 0,
[Description("Run as administrator")]
RunAsAdmin = 1,
[Description("Use service")]
UseService = 2,
[Description("Use elevated process")]
UseElevatedProcess = 3
}
public enum ElevationMethod
{
None = 0,
[Description("Run as administrator")]
RunAsAdmin = 1,
[Description("Use service")]
UseService = 2,
[Description("Use elevated process")]
UseElevatedProcess = 3
}
public enum UiType
{
WinForms = 0,
[Description("Web (default browser)")]
WebDefaultBrowser = 1,
[Description("Web (embedded browser)")]
WebEmbedded = 2
}
public enum UiType
{
WinForms = 0,
[Description("Web (default browser)")]
WebDefaultBrowser = 1,
[Description("Web (embedded browser)")]
WebEmbedded = 2
}
public class Config
{
public bool AutoStart { get; set; }
public bool StartMinimized { get; set; }
public bool MinimizeOnClose { get; set; }
public bool MinimizeToTray { get; set; }
public bool CheckForUpdates { get; set; }
public bool AutoInstallUpdates { get; set; }
public bool UseDarkMode { get; set; }
public int DisplaySettingsDelay { get; set; }
public string ScreenSaverShortcut { get; set; }
public int FormWidth { get; set; }
public int FormHeight { get; set; }
public double XFormWidth { get; set; }
public double XFormHeight { get; set; }
public int NvPresetId_ApplyOnStartup { get; set; }
public int AmdPresetId_ApplyOnStartup { get; set; }
public bool FixChromeFonts { get; set; }
public bool UseGdiScaling { get; set; }
public ListViewSortState NvPresetsSortState { get; set; }
public ListViewSortState AmdPresetsSortState { get; set; }
public ListViewSortState LgPresetsSortState { get; set; }
public ListViewSortState SamsungPresetsSortState { get; set; }
public ListViewSortState GamePresetsSortState { get; set; }
public string AmdQuickAccessShortcut { get; set; }
public string LgQuickAccessShortcut { get; set; }
public bool UseDedicatedElevatedProcess { get; set; }
public ElevationMethod ElevationMethod { get; set; }
public bool ElevationMethodAsked { get; set; }
public int ProcessMonitorPollingInterval { get; set; }
public string LogLevel { get; set; }
public List<Module> Modules { get; set; }
public bool UseRawInput { get; set; }
public bool SetMinTmlAndMaxTml { get; set; }
public bool DisableErrorPopupWhenApplyingPreset { get; set; }
public UiType UiType { get; set; }
public int UiPort { get; set; }
public class Config
{
public bool AutoStart { get; set; }
public bool StartMinimized { get; set; }
public bool MinimizeOnClose { get; set; }
public bool MinimizeToTray { get; set; }
public bool CheckForUpdates { get; set; }
public bool AutoInstallUpdates { get; set; }
public bool UseDarkMode { get; set; }
public int DisplaySettingsDelay { get; set; }
public string ScreenSaverShortcut { get; set; }
public int FormWidth { get; set; }
public int FormHeight { get; set; }
public double XFormWidth { get; set; }
public double XFormHeight { get; set; }
public int NvPresetId_ApplyOnStartup { get; set; }
public int AmdPresetId_ApplyOnStartup { get; set; }
public bool FixChromeFonts { get; set; }
public bool UseGdiScaling { get; set; }
public ListViewSortState NvPresetsSortState { get; set; }
public ListViewSortState AmdPresetsSortState { get; set; }
public ListViewSortState LgPresetsSortState { get; set; }
public ListViewSortState SamsungPresetsSortState { get; set; }
public ListViewSortState GamePresetsSortState { get; set; }
public string AmdQuickAccessShortcut { get; set; }
public string LgQuickAccessShortcut { get; set; }
public bool UseDedicatedElevatedProcess { get; set; }
public ElevationMethod ElevationMethod { get; set; }
public bool ElevationMethodAsked { get; set; }
public int ProcessMonitorPollingInterval { get; set; }
public string LogLevel { get; set; }
public List<Module> Modules { get; set; }
public bool UseRawInput { get; set; }
public bool SetMinTmlAndMaxTml { get; set; }
public bool DisableErrorPopupWhenApplyingPreset { get; set; }
public UiType UiType { get; set; }
public int UiPort { get; set; }
[JsonIgnore]
public int CurrentUiPort { get; set; }
public bool UiAllowRemoteConnections { get; set; }
public Config()
{
AutoStart = false;
LogLevel = "DEBUG";
DisplaySettingsDelay = 1000;
ScreenSaverShortcut = string.Empty;
FormWidth = 900;
FormHeight = 600;
XFormWidth = 1280;
XFormHeight = 720;
MinimizeToTray = true;
CheckForUpdates = true;
FixChromeFonts = false;
UseGdiScaling = true;
UseDedicatedElevatedProcess = false;
ElevationMethod = ElevationMethod.None;
ElevationMethodAsked = false;
ProcessMonitorPollingInterval = 1000;
UiType = UiType.WinForms;
UiPort = 5000;
LgPresetsSortState = new ListViewSortState();
NvPresetsSortState = new ListViewSortState();
AmdPresetsSortState = new ListViewSortState();
SamsungPresetsSortState = new ListViewSortState();
GamePresetsSortState = new ListViewSortState();
Modules = new List<Module>();
}
public Config()
{
AutoStart = false;
LogLevel = "DEBUG";
DisplaySettingsDelay = 1000;
ScreenSaverShortcut = string.Empty;
FormWidth = 900;
FormHeight = 600;
XFormWidth = 1280;
XFormHeight = 720;
MinimizeToTray = true;
CheckForUpdates = true;
FixChromeFonts = false;
UseGdiScaling = true;
UseDedicatedElevatedProcess = false;
ElevationMethod = ElevationMethod.None;
ElevationMethodAsked = false;
ProcessMonitorPollingInterval = 1000;
UiType = UiType.WebEmbedded;
UiPort = 0; // Let .NET decide
UiAllowRemoteConnections = false;
LgPresetsSortState = new ListViewSortState();
NvPresetsSortState = new ListViewSortState();
AmdPresetsSortState = new ListViewSortState();
SamsungPresetsSortState = new ListViewSortState();
GamePresetsSortState = new ListViewSortState();
Modules = [];
}
public Config(Config config) : this()
{
Update(config);
}
public Config(Config config) : this()
{
Update(config);
}
public void Update(Config config)
{
AutoStart = config.AutoStart;
StartMinimized = config.StartMinimized;
MinimizeOnClose = config.MinimizeOnClose;
MinimizeToTray = config.MinimizeToTray;
CheckForUpdates = config.CheckForUpdates;
AutoInstallUpdates = config.AutoInstallUpdates;
UseDarkMode = config.UseDarkMode;
ScreenSaverShortcut = config.ScreenSaverShortcut;
FixChromeFonts = config.FixChromeFonts;
UseGdiScaling = config.UseGdiScaling;
ProcessMonitorPollingInterval = config.ProcessMonitorPollingInterval;
UseRawInput = config.UseRawInput;
SetMinTmlAndMaxTml = config.SetMinTmlAndMaxTml;
DisableErrorPopupWhenApplyingPreset = config.DisableErrorPopupWhenApplyingPreset;
UiType = config.UiType;
UiPort = config.UiPort;
}
public void Update(Config config)
{
AutoStart = config.AutoStart;
StartMinimized = config.StartMinimized;
MinimizeOnClose = config.MinimizeOnClose;
MinimizeToTray = config.MinimizeToTray;
CheckForUpdates = config.CheckForUpdates;
AutoInstallUpdates = config.AutoInstallUpdates;
UseDarkMode = config.UseDarkMode;
ScreenSaverShortcut = config.ScreenSaverShortcut;
FixChromeFonts = config.FixChromeFonts;
UseGdiScaling = config.UseGdiScaling;
ProcessMonitorPollingInterval = config.ProcessMonitorPollingInterval;
UseRawInput = config.UseRawInput;
SetMinTmlAndMaxTml = config.SetMinTmlAndMaxTml;
DisableErrorPopupWhenApplyingPreset = config.DisableErrorPopupWhenApplyingPreset;
UiType = config.UiType;
UiPort = config.UiPort;
}
public bool HasModule(string name)
{
return Modules?.Any(m => m.DisplayName == name) == true;
}
}
public bool HasModule(string name)
{
return Modules?.Any(m => m.DisplayName == name) == true;
}
}
}
+62
View File
@@ -0,0 +1,62 @@
using System.Text.Json.Serialization;
namespace ColorControl.Shared.Contracts;
public class DdcSetting
{
public enum ValueChangeTypes
{
Fixed = 0,
Increase = 1,
Decrease = 2
}
public byte VcpCode { get; set; }
public uint Value { get; set; }
public ValueChangeTypes ValueChangeType { get; set; }
[JsonIgnore]
public uint MaxValue { get; set; }
public string VcpHexCode
{
get => $"{VcpCode:X2}";
set => VcpCode = byte.Parse(value, System.Globalization.NumberStyles.HexNumber);
}
public DdcSetting()
{
}
public DdcSetting(DdcSetting setting)
{
setting ??= new DdcSetting();
VcpCode = setting.VcpCode;
Value = setting.Value;
ValueChangeType = setting.ValueChangeType;
}
public override string ToString()
{
return $"VCP: {GetVcpName()}, Value: {GetVcpValueName()}";
}
public string GetVcpName()
{
var code = VcpCode.ToString("X2");
var value = VcpCodes.CodeList[code];
return value == "" ? $"Unknown ({code})" : value;
}
public string GetVcpValueName()
{
return ValueChangeType switch
{
ValueChangeTypes.Increase => $"+{Value}",
ValueChangeTypes.Decrease => $"-{Value}",
_ => $"{Value}"
};
}
}
+27
View File
@@ -0,0 +1,27 @@
namespace ColorControl.Shared.Contracts;
public class DdcSettings
{
public bool ApplyDdc { get; set; }
public List<DdcSetting> Settings { get; set; } = [];
public bool RemoveOutOfRangeOsd { get; set; }
public DdcSettings()
{
}
public DdcSettings(DdcSettings settings)
{
settings ??= new DdcSettings();
ApplyDdc = settings.ApplyDdc;
Settings.Clear();
Settings.AddRange(settings.Settings.Select(x => new DdcSetting(x)));
RemoveOutOfRangeOsd = settings.RemoveOutOfRangeOsd;
}
public override string ToString()
{
return $"Settings: {Settings.Count}";
}
}
+50 -44
View File
@@ -1,58 +1,64 @@
using MHC2Gen;
using LittleCms;
using MHC2Gen;
namespace ColorControl.Shared.Contracts.DisplayInfo;
public class ColorProfileDto
{
public string Name { get; set; }
public string Description { get; set; }
public bool HasExtraInfo { get; set; }
public bool IsHDRProfile { get; set; }
public RgbPrimariesDto DevicePrimaries { get; set; } = new RgbPrimariesDto(RgbPrimaries.sRGB);
public SDRTransferFunction SDRTransferFunction { get; set; }
public ColorGamut ColorGamut { get; set; }
public double MinCLL { get; set; }
public double MaxCLL { get; set; }
public double WhiteLuminance { get; set; }
public double BlackLuminance { get; set; }
public double SDRMinBrightness { get; set; }
public double SDRMaxBrightness { get; set; }
public double SDRBrightnessBoost { get; set; }
public double ShadowDetailBoost { get; set; }
public double Gamma { get; set; } = 2.2;
public string Name { get; set; }
public string Description { get; set; }
public RenderingIntent RenderingIntent { get; set; }
public bool HasExtraInfo { get; set; }
public bool IsHDRProfile { get; set; }
public RgbPrimariesDto DevicePrimaries { get; set; } = new RgbPrimariesDto(RgbPrimaries.sRGB);
public SDRTransferFunction SDRTransferFunction { get; set; }
public ColorGamut ColorGamut { get; set; }
public double MinCLL { get; set; }
public double MaxCLL { get; set; }
public double WhiteLuminance { get; set; }
public double BlackLuminance { get; set; }
public double SDRMinBrightness { get; set; }
public double SDRMaxBrightness { get; set; }
public double SDRBrightnessBoost { get; set; }
public double ShadowDetailBoost { get; set; }
public double ShadowDetailRange { get; set; }
public double RedGain { get; set; } = 100;
public double GreenGain { get; set; } = 100;
public double BlueGain { get; set; } = 100;
public double Gamma { get; set; } = 2.2;
public double ToneMappingFromLuminance { get; set; } = 400;
public double ToneMappingToLuminance { get; set; } = 400;
public double HdrGammaMultiplier { get; set; } = 1;
public double HdrBrightnessMultiplier { get; set; } = 1;
public void UpdatePrimariesAndLuminance(DisplayColorInfo displayColorInfo)
{
BlackLuminance = displayColorInfo.BlackLuminance;
WhiteLuminance = displayColorInfo.WhiteLuminance;
MinCLL = displayColorInfo.MinCLL;
MaxCLL = displayColorInfo.MaxCLL;
DevicePrimaries = new RgbPrimariesDto(displayColorInfo.RgbPrimaries);
}
{
BlackLuminance = displayColorInfo.BlackLuminance;
WhiteLuminance = displayColorInfo.WhiteLuminance;
MinCLL = displayColorInfo.MinCLL;
MaxCLL = displayColorInfo.MaxCLL;
DevicePrimaries = new RgbPrimariesDto(displayColorInfo.RgbPrimaries);
}
public override string ToString()
{
return Name == "None" ? Name : $"{(IsHDRProfile ? "HDR" : "SDR")}: {Name}";
}
public override string ToString()
{
return Name == "None" ? Name : $"{(IsHDRProfile ? "HDR" : "SDR")}: {Name}";
}
public static ColorProfileDto CreateDefault(bool isHdrProfile)
{
return new ColorProfileDto
{
IsHDRProfile = isHdrProfile,
HasExtraInfo = true,
MinCLL = 0,
MaxCLL = 1000,
BlackLuminance = 0,
WhiteLuminance = 1000,
SDRMinBrightness = 0,
SDRMaxBrightness = 100,
SDRTransferFunction = isHdrProfile ? SDRTransferFunction.Piecewise : SDRTransferFunction.PurePower
};
}
public static ColorProfileDto CreateDefault(bool isHdrProfile)
{
return new ColorProfileDto
{
IsHDRProfile = isHdrProfile,
RenderingIntent = RenderingIntent.PERCEPTUAL,
HasExtraInfo = true,
MinCLL = 0,
MaxCLL = 1000,
BlackLuminance = 0,
WhiteLuminance = 1000,
SDRMinBrightness = 0,
SDRMaxBrightness = 100,
SDRTransferFunction = isHdrProfile ? SDRTransferFunction.Piecewise : SDRTransferFunction.PurePower
};
}
}
+1
View File
@@ -53,6 +53,7 @@ public class GamePreset : PresetBase
public List<string> FinalizeSteps { get; set; }
public uint ProcessAffinityMask { get; set; }
public uint ProcessPriorityClass { get; set; }
public bool PathExists { get; set; }
[JsonIgnore]
public GamePriorityClass GameProcessPriorityClass
+10 -1
View File
@@ -85,6 +85,7 @@ namespace ColorControl.Shared.Contracts.NVIDIA
public bool ApplyNovideoSettings { get; set; }
public NovideoSettings NovideoSettings { get; set; }
public DpiScaling DpiScaling { get; set; }
public DdcSettings DdcSettings { get; set; }
public bool SetDitherRegistryKey { get; set; }
public bool RestartDriver { get; set; }
@@ -123,12 +124,14 @@ namespace ColorControl.Shared.Contracts.NVIDIA
HdrSettings = new NvHdrSettings();
NovideoSettings = new NovideoSettings();
DpiScaling = new DpiScaling();
DdcSettings = new DdcSettings();
}
public NvPreset(ColorData colorData) : this()
{
id = GetNewId();
this.colorData = colorData;
this.applyColorData = true;
}
public NvPreset(NvPreset preset) : this()
@@ -157,6 +160,9 @@ namespace ColorControl.Shared.Contracts.NVIDIA
ColorEnhancementSettings.DigitalVibranceLevel != currentSettings.ColorEnhancementSettings.DigitalVibranceLevel ||
ColorEnhancementSettings.HueAngle != currentSettings.ColorEnhancementSettings.HueAngle;
DpiScaling.ApplyScaling = keepChanges && DpiScaling.ApplyScaling || DpiScaling.Percentage != currentSettings.DpiScaling.Percentage;
DdcSettings.ApplyDdc = keepChanges && DdcSettings.ApplyDdc ||
(!(DdcSettings.Settings.Count == 1 && DdcSettings.Settings[0].VcpCode == 0) &&
!DdcSettings.Settings.All(s => currentSettings.DdcSettings.Settings.Any(cs => cs.VcpCode == s.VcpCode && cs.Value == s.Value)));
}
public void Update(NvPreset preset)
@@ -179,6 +185,8 @@ namespace ColorControl.Shared.Contracts.NVIDIA
applyDithering = preset.applyDithering;
ditheringEnabled = preset.ditheringEnabled;
ditheringMode = preset.ditheringMode;
ditheringBits = preset.ditheringBits;
DisplayConfig = new DisplayConfig(preset.DisplayConfig);
@@ -204,6 +212,7 @@ namespace ColorControl.Shared.Contracts.NVIDIA
ocSettings = preset.ocSettings.Select(s => new NvGpuOcSettings(s)).ToList();
DpiScaling = new DpiScaling(preset.DpiScaling);
DdcSettings = new DdcSettings(preset.DdcSettings);
MonitorConnectionType = preset.MonitorConnectionType;
@@ -235,7 +244,7 @@ namespace ColorControl.Shared.Contracts.NVIDIA
public bool HasApplicableSettings => applyColorData || applyDithering || applyDriverSettings || applyHdmiSettings || applyHDR || applyOther ||
applyOverclocking || ApplyColorEnhancements || ApplyNovideoSettings ||
DisplayConfig.ApplyRefreshRate || DisplayConfig.ApplyResolution || DpiScaling.ApplyScaling;
DisplayConfig.ApplyRefreshRate || DisplayConfig.ApplyResolution || DpiScaling.ApplyScaling || DdcSettings.ApplyDdc;
public bool HasEqualOverclockingSettings(NvPreset preset)
{
+24 -3
View File
@@ -19,10 +19,14 @@ public class PresetStep
{
public string Raw { get; set; }
public string UpdatedRaw => $"{Step}{(Delay > 0 ? $":{Delay}" : "")}{(Delay > 0 && Option != "" ? $":{Option}" : "")}";
public string UpdatedRaw => $"{(ActionName != null ? $"{ActionName}({ActionValue})" : $"{Step}")}{(Delay > 0 ? $":{Delay}" : "")}{(Delay > 0 && Option != "" ? $":{Option}" : "")}";
public string Step { get; set; }
public string ActionName { get; set; }
public string ActionValue { get; set; }
public string[] Parameters { get; set; }
public int Delay { get; set; }
@@ -34,10 +38,25 @@ public class PresetStep
public static PresetStep Parse(string step, int defaultDelay = 0, string defaultOption = "")
{
var parts = new List<string>();
var index = step.IndexOf('(');
var actionName = step;
string actionValue = null;
if (index > 0)
{
actionName = step[..index];
}
var index2 = step.IndexOf(")");
if (index2 > 0)
{
if (index > 0)
{
actionValue = step.Substring(index + 1, (index2 - index - 1));
}
parts.Add(step.Substring(0, index2 + 1));
if (index2 < step.Length - 2)
{
@@ -70,9 +89,9 @@ public class PresetStep
cmd = parts[2];
}
var index = key.IndexOf("(");
var index3 = key.IndexOf("(");
string[] parameters = null;
if (index > -1)
if (index3 > -1)
{
var keyValue = key.Split('(');
parameters = keyValue[1].Substring(0, keyValue[1].Length - 1).Split(';');
@@ -82,6 +101,8 @@ public class PresetStep
{
Raw = step,
Step = key,
ActionName = actionName,
ActionValue = actionValue,
Parameters = parameters,
Delay = delay,
Option = cmd
@@ -31,6 +31,7 @@ public class SamsungPreset : PresetBase
public void Update(SamsungPreset preset)
{
name = preset.name;
shortcut = preset.shortcut;
Description = preset.Description;
AppId = preset.AppId;
DeviceMacAddress = preset.DeviceMacAddress;
+264
View File
@@ -0,0 +1,264 @@
namespace ColorControl.Shared.Contracts;
public static class VcpCodes
{
public static readonly Dictionary<string, string> CodeList = new()
{
{ "00", "VCP Code Page" },
{ "01", "Degauss" },
{ "02", "New Control Value" },
{ "03", "Soft Controls" },
{ "04", "Restore Factory Defaults" },
{ "05", "Restore Factory Luminance/ Contrast Defaults" },
{ "06", "Restore Factory Geometry Defaults" },
{ "07", "" },
{ "08", "Restore Factory Color Defaults" },
{ "09", "" },
{ "0A", "Restore Factory TV Defaults" },
{ "0B", "Color Temperature Increment" },
{ "0C", "Color Temperature Request" },
{ "0D", "" },
{ "0E", "Clock" },
{ "0F", "" },
{ "10", "Luminance" },
{ "11", "Flesh Tone Enhancement" },
{ "12", "Contrast" },
{ "13", "Backlight Control" },
{ "14", "Select Color Preset" },
{ "15", "" },
{ "16", "Video Gain (Drive): Red" },
{ "17", "User Color Vision Compensation" },
{ "18", "Video Gain (Drive): Green" },
{ "19", "" },
{ "1A", "Video Gain (Drive): Blue" },
{ "1B", "" },
{ "1C", "Focus" },
{ "1D", "" },
{ "1E", "Auto Setup" },
{ "1F", "Auto Color Setup" },
{ "20", "Horizontal Position (Phase)" },
{ "21", "" },
{ "22", "Horizontal Size" },
{ "23", "" },
{ "24", "Horizontal Pincushion" },
{ "25", "" },
{ "26", "Horizontal Pincushion Balance" },
{ "27", "" },
{ "28", "Horizontal Convergence R / B" },
{ "29", "Horizontal Convergence M / G" },
{ "2A", "Horizontal Linearity" },
{ "2B", "" },
{ "2C", "Horizontal Linearity Balance" },
{ "2D", "" },
{ "2E", "Gray Scale Expansion" },
{ "2F", "" },
{ "30", "Vertical Position (Phase)" },
{ "31", "" },
{ "32", "Vertical Size" },
{ "33", "" },
{ "34", "Vertical Pincushion" },
{ "35", "" },
{ "36", "Vertical Pincushion Balance" },
{ "37", "" },
{ "38", "Vertical Convergence R/B" },
{ "39", "Vertical Convergence M/G" },
{ "3A", "Vertical Linearity" },
{ "3B", "" },
{ "3C", "Vertical Linearity Balance" },
{ "3D", "" },
{ "3E", "Clock Phase" },
{ "3F", "" },
{ "40", "Horizontal Parallelogram" },
{ "41", "Vertical Parallelogram" },
{ "42", "Horizontal Keystone" },
{ "43", "Vertical Keystone" },
{ "44", "Rotation" },
{ "45", "" },
{ "46", "Top Corner Flare" },
{ "47", "" },
{ "48", "Top Corner Hook" },
{ "49", "" },
{ "4A", "Bottom Corner Flare" },
{ "4B", "" },
{ "4C", "Bottom Corner Hook" },
{ "4D", "" },
{ "4E", "" },
{ "4F", "" },
{ "50", "" },
{ "51", "" },
{ "52", "Active Control" },
{ "53", "" },
{ "54", "Performance Preservation" },
{ "55", "" },
{ "56", "H Moiré" },
{ "57", "" },
{ "58", "V Moiré" },
{ "59", "6 Axis Saturation Control: Red" },
{ "5A", "6 Axis Saturation Control: Yellow" },
{ "5B", "6 Axis Saturation Control: Green" },
{ "5C", "6 Axis Saturation Control: Cyan" },
{ "5D", "6 Axis Saturation Control: Blue" },
{ "5E", "6 Axis Saturation Control: Magenta" },
{ "5F", "" },
{ "60", "Input Select" },
{ "61", "" },
{ "62", "Audio: Speaker Volume" },
{ "63", "Audio: Speaker Pair Select" },
{ "64", "Audio: Microphone Volume" },
{ "65", "Audio: Jack Connection Status" },
{ "66", "Ambient Light Sensor" },
{ "67", "" },
{ "68", "" },
{ "69", "" },
{ "6A", "" },
{ "6B", "Backlight Level: White" },
{ "6C", "Video Black Level: Red" },
{ "6D", "Backlight Level: Red" },
{ "6E", "Video Black Level: Green" },
{ "6F", "Backlight Level: Green" },
{ "70", "Video Black Level: Blue" },
{ "71", "Backlight Level: Blue" },
{ "72", "Gamma" },
{ "73", "LUT Size" },
{ "74", "Single Point LUT Operation" },
{ "75", "Block LUT Operation" },
{ "76", "Remote Procedure Call" },
{ "77", "" },
{ "78", "Display Identification Data Operation" },
{ "79", "" },
{ "7A", "" },
{ "7B", "" },
{ "7C", "Adjust Zoom" },
{ "7D", "" },
{ "7E", "" },
{ "7F", "" },
{ "80", "" },
{ "81", "" },
{ "82", "Horizontal Mirror (Flip)" },
{ "83", "" },
{ "84", "Vertical Mirror (Flip)" },
{ "85", "" },
{ "86", "Display Scaling" },
{ "87", "Sharpness" },
{ "88", "Velocity Scan Modulation" },
{ "89", "" },
{ "8A", "Color Saturation" },
{ "8B", "TV Channel Up / Down" },
{ "8C", "TV Sharpness" },
{ "8D", "Audio Mute / Screen Blank" },
{ "8E", "TV Contrast" },
{ "8F", "Audio Treble" },
{ "90", "Hue" },
{ "91", "Audio Bass" },
{ "92", "TV Black Level / Luminance" },
{ "93", "Audio Balance L / R" },
{ "94", "Audio Processor Mode" },
{ "95", "" },
{ "96", "" },
{ "97", "" },
{ "98", "" },
{ "99", "" },
{ "9A", "Window Background" },
{ "9B", "6 Axis Color Control: Red" },
{ "9C", "6 Axis Color Control: Yellow" },
{ "9D", "6 Axis Color Control: Green" },
{ "9E", "6 Axis Color Control: Cyan" },
{ "9F", "6 Axis Color Control: Blue" },
{ "A0", "6 Axis Color Control: Magenta" },
{ "A1", "" },
{ "A2", "Auto Setup On / Off" },
{ "A3", "" },
{ "A4", "" },
{ "A5", "" },
{ "A6", "" },
{ "A7", "" },
{ "A8", "" },
{ "A9", "" },
{ "AA", "Screen Orientation" },
{ "AB", "" },
{ "AC", "Horizontal Frequency" },
{ "AD", "" },
{ "AE", "Vertical Frequency" },
{ "AF", "" },
{ "B0", "Settings" },
{ "B1", "" },
{ "B2", "Flat Panel Sub-Pixel Layout" },
{ "B3", "" },
{ "B4", "Source Timing Mode" },
{ "B5", "Source Color Coding" },
{ "B6", "Display Technology Type" },
{ "B7", "" },
{ "B8", "" },
{ "B9", "" },
{ "BA", "" },
{ "BB", "" },
{ "BC", "" },
{ "BD", "" },
{ "BE", "" },
{ "BF", "" },
{ "C0", "Display Usage Time" },
{ "C1", "" },
{ "C2", "Display Descriptor Length" },
{ "C3", "Transmit Display Descriptor" },
{ "C4", "Enable Display of ‘Display Descriptor’" },
{ "C5", "" },
{ "C6", "" },
{ "C7", "" },
{ "C8", "" },
{ "C9", "" },
{ "CA", "" },
{ "CB", "" },
{ "CC", "OSD Language" },
{ "CD", "Status Indicators" },
{ "CE", "" },
{ "CF", "" },
{ "D0", "Output Selection" },
{ "D1", "" },
{ "D2", "Asset Tag" },
{ "D3", "" },
{ "D4", "Stereo Video Mode" },
{ "D5", "" },
{ "D6", "Power Mode" },
{ "D7", "" },
{ "D8", "" },
{ "D9", "" },
{ "DA", "Scan Mode" },
{ "DB", "Image Mode" },
{ "DC", "Display Application" },
{ "DD", "" },
{ "DE", "" },
{ "DF", "VCP Version" },
{ "E0", "Manufacturer Specific" },
{ "E1", "Manufacturer Specific" },
{ "E2", "Manufacturer Specific" },
{ "E3", "Manufacturer Specific" },
{ "E4", "Manufacturer Specific" },
{ "E5", "Manufacturer Specific" },
{ "E6", "Manufacturer Specific" },
{ "E7", "Manufacturer Specific" },
{ "E8", "Manufacturer Specific" },
{ "E9", "Manufacturer Specific" },
{ "EA", "Manufacturer Specific" },
{ "EB", "Manufacturer Specific" },
{ "EC", "Manufacturer Specific" },
{ "ED", "Manufacturer Specific" },
{ "EE", "Manufacturer Specific" },
{ "EF", "Manufacturer Specific" },
{ "F0", "Manufacturer Specific" },
{ "F1", "Manufacturer Specific" },
{ "F2", "Manufacturer Specific" },
{ "F3", "Manufacturer Specific" },
{ "F4", "Manufacturer Specific" },
{ "F5", "Manufacturer Specific" },
{ "F6", "Manufacturer Specific" },
{ "F7", "Manufacturer Specific" },
{ "F8", "Manufacturer Specific" },
{ "F9", "Manufacturer Specific" },
{ "FA", "Manufacturer Specific" },
{ "FB", "Manufacturer Specific" },
{ "FC", "Manufacturer Specific" },
{ "FD", "Manufacturer Specific" },
{ "FE", "Manufacturer Specific" },
{ "FF", "Manufacturer Specific" }
};
}
+10
View File
@@ -0,0 +1,10 @@
using static ColorControl.Shared.Native.WinApi;
namespace ColorControl.Shared.Contracts;
public class VcpInfo
{
public uint Value { get; set; }
public uint MaxValue { get; set; }
public MC_VCP_CODE_TYPE CodeType { get; set; }
}
+6 -5
View File
@@ -1,12 +1,12 @@
using System.ComponentModel;
using System.IO;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using ColorControl.Shared.Contracts;
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
{
@@ -1824,6 +1824,7 @@ namespace ColorControl.Shared.Native
[DllImport("user32.dll")]
public extern static bool GetMonitorInfo(IntPtr hMonitor, ref MONITORINFOEX lpmi);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern bool EnumDisplayDevices(string lpDevice, uint iDevNum, ref DISPLAYDEVICE lpDisplayDevice, uint dwFlags);
}
+39
View File
@@ -81,4 +81,43 @@ public static class WinApi
[DllImport("dwmapi.dll", EntryPoint = "#171")]
public static extern void DwmpSDRToHDRBoostPtr(IntPtr monitor, double brightness);
public enum MC_VCP_CODE_TYPE
{
MC_MOMENTARY,
MC_SET_PARAMETER
};
[DllImport("Dxva2.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool GetVCPFeatureAndVCPFeatureReply(nint hMonitor, byte vcpCode, out MC_VCP_CODE_TYPE codeType, out uint currentValue, out uint maximumValue);
[DllImport("Dxva2.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool SetVCPFeature(nint hMonitor, byte vcpCode, uint newValue);
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public struct PHYSICAL_MONITOR
{
public IntPtr hPhysicalMonitor;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
public string szPhysicalMonitorDescription;
}
[DllImport("Dxva2.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool GetNumberOfPhysicalMonitorsFromHMONITOR(IntPtr hMonitor, out uint pdwNumberOfPhysicalMonitors);
[DllImport("Dxva2.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool GetPhysicalMonitorsFromHMONITOR(IntPtr hMonitor, uint dwPhysicalMonitorArraySize, [Out] PHYSICAL_MONITOR[] pPhysicalMonitorArray);
[DllImport("Dxva2.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool DestroyPhysicalMonitors(uint dwPhysicalMonitorArraySize, [In] PHYSICAL_MONITOR[] pPhysicalMonitorArray);
[DllImport("Dxva2.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool DestroyPhysicalMonitor(IntPtr hMonitor);
}
+51
View File
@@ -0,0 +1,51 @@
using System.Runtime.InteropServices;
using System.Text;
namespace ColorControl.Shared.Native;
public static class WinError
{
#region Win32
[DllImport("Kernel32.dll", SetLastError = true)]
private static extern uint FormatMessage(
uint dwFlags,
IntPtr lpSource,
uint dwMessageId,
uint dwLanguageId,
StringBuilder lpBuffer,
int nSize,
IntPtr Arguments);
private const uint FORMAT_MESSAGE_FROM_SYSTEM = 0x00001000;
#endregion
public static (int errorCode, string message) GetCodeMessage()
{
var errorCode = Marshal.GetLastWin32Error();
return (errorCode, GetMessage(errorCode));
}
public static string GetMessage() => GetMessage(Marshal.GetLastWin32Error());
public static string GetMessage(int errorCode)
{
var message = new StringBuilder($"Code: 0x{errorCode:X8}");
var buffer = new StringBuilder(512); // This 512 capacity is arbitrary.
if (FormatMessage(
FORMAT_MESSAGE_FROM_SYSTEM,
IntPtr.Zero,
(uint)errorCode,
0x0409, // US (English)
buffer,
buffer.Capacity,
IntPtr.Zero) > 0)
{
message.Append(", Message: ").Append(buffer);
}
return message.ToString();
}
}
+148 -148
View File
@@ -7,201 +7,201 @@ namespace ColorControl.Shared.Services;
public class RpcServerService
{
private static readonly NLog.Logger Logger = NLog.LogManager.GetCurrentClassLogger();
private static readonly NLog.Logger Logger = NLog.LogManager.GetCurrentClassLogger();
private readonly IServiceProvider _serviceProvider;
private readonly IServiceProvider _serviceProvider;
public static List<Type> ServiceTypes = new List<Type>();
public static List<Type> ServiceTypes = new List<Type>();
public RpcServerService(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
public RpcServerService(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
public async Task<SvcResultMessage> ExecuteRpcAsync(SvcRpcMessage message)
{
try
{
var service = GetServiceInstance(message.ServiceName);
public async Task<SvcResultMessage> ExecuteRpcAsync(SvcRpcMessage message)
{
try
{
var service = GetServiceInstance(message.ServiceName);
var method = GetSuitableMethod(service, message.MethodName, message.Arguments);
var method = GetSuitableMethod(service, message.MethodName, message.Arguments);
if (method == null)
{
throw new InvalidOperationException($"No suitable method {message.MethodName} found in service {service.GetType().Name} with {message.Arguments.Length} arguments");
}
if (method == null)
{
throw new InvalidOperationException($"No suitable method {message.MethodName} found in service {service.GetType().Name} with {message.Arguments.Length} arguments");
}
var convertedArgs = new List<object>();
var convertedArgs = new List<object>();
var methodParams = method.GetParameters();
for (var i = 0; i < message.Arguments.Length; i++)
{
var arg = message.Arguments[i];
var methodParam = methodParams[i];
var methodParams = method.GetParameters();
for (var i = 0; i < message.Arguments.Length; i++)
{
var arg = message.Arguments[i];
var methodParam = methodParams[i];
AddArgument(arg, methodParam.ParameterType, convertedArgs);
}
AddArgument(arg, methodParam.ParameterType, convertedArgs);
}
while (convertedArgs.Count < methodParams.Length)
{
convertedArgs.Add(Type.Missing);
}
while (convertedArgs.Count < methodParams.Length)
{
convertedArgs.Add(Type.Missing);
}
Logger.Debug($"Executing RPC: {message.ServiceName}.{message.MethodName}({string.Join(", ", convertedArgs)})");
Logger.Debug($"Executing RPC: {message.ServiceName}.{message.MethodName}({string.Join(", ", convertedArgs)})");
var result = method.Invoke(service, convertedArgs.ToArray());
var result = method.Invoke(service, convertedArgs.ToArray());
if (result is Task task)
{
await task;
if (result is Task task)
{
await task;
result = task.GetType().GetProperty("Result").GetValue(task);
}
result = task.GetType().GetProperty("Result").GetValue(task);
}
var data = JsonConvert.SerializeObject(result);
var data = JsonConvert.SerializeObject(result);
return SvcResultMessage.FromResult(data);
}
catch (Exception ex)
{
Logger.Error(ex, $"ExecuteRpcAsync: {ex.Message}");
return SvcResultMessage.FromResult(data);
}
catch (Exception ex)
{
Logger.Error(ex, $"ExecuteRpcAsync: {ex.Message}");
return new SvcResultMessage { ErrorMessage = ex.InnerException?.Message ?? ex.Message, Result = false };
}
}
return new SvcResultMessage { ErrorMessage = ex.InnerException?.Message ?? ex.Message, Result = false };
}
}
private void AddArgument(object arg, Type type, List<object> list)
{
if (arg != null && arg.GetType() != type)
{
if (arg.GetType().Name == "JObject" || arg.GetType().Name == "JArray")
{
var jsonString = arg.ToString();
var obj = jsonString == "{}" ? Type.Missing : JsonConvert.DeserializeObject(jsonString, type);
list.Add(obj);
return;
}
if (type.IsEnum)
{
// Add enum directly to list and not return as 'object'
list.Add(Enum.ToObject(type, arg));
return;
}
private void AddArgument(object arg, Type type, List<object> list)
{
if (arg != null && arg.GetType() != type)
{
if (arg.GetType().Name == "JObject" || arg.GetType().Name == "JArray")
{
var jsonString = arg.ToString();
var obj = jsonString == "{}" && !type.Name.StartsWith("Dictionary") ? Type.Missing : JsonConvert.DeserializeObject(jsonString, type);
list.Add(obj);
return;
}
if (type.IsEnum)
{
// Add enum directly to list and not return as 'object'
list.Add(Enum.ToObject(type, arg));
return;
}
list.Add(Convert.ChangeType(arg, type));
return;
}
list.Add(Convert.ChangeType(arg, type));
return;
}
list.Add(arg);
}
list.Add(arg);
}
public async Task<SvcResultMessage> ExecuteRpcTypedAsync(SvcRpcMessageTyped message)
{
try
{
var service = GetServiceInstance(message.ServiceName);
public async Task<SvcResultMessage> ExecuteRpcTypedAsync(SvcRpcMessageTyped message)
{
try
{
var service = GetServiceInstance(message.ServiceName);
var method = service.GetType().GetMethod(message.MethodName);
var method = service.GetType().GetMethod(message.MethodName);
var props = message.GetType().GetProperties(BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.Instance);
var methodParams = method.GetParameters();
var props = message.GetType().GetProperties(BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.Instance);
var methodParams = method.GetParameters();
var convertedArgs = new List<object>();
var paramIndex = 0;
var convertedArgs = new List<object>();
var paramIndex = 0;
foreach (var prop in props)
{
Logger.Debug($"Prop: {prop.Name}");
foreach (var prop in props)
{
Logger.Debug($"Prop: {prop.Name}");
var arg = prop.GetValue(message);
var methodParam = methodParams[paramIndex];
var arg = prop.GetValue(message);
var methodParam = methodParams[paramIndex];
AddArgument(arg, methodParam.ParameterType, convertedArgs);
AddArgument(arg, methodParam.ParameterType, convertedArgs);
paramIndex++;
}
paramIndex++;
}
while (convertedArgs.Count < methodParams.Length)
{
convertedArgs.Add(Type.Missing);
}
while (convertedArgs.Count < methodParams.Length)
{
convertedArgs.Add(Type.Missing);
}
Logger.Debug($"Executing RPC-typed: {message.ServiceName}.{message.MethodName}({string.Join(", ", convertedArgs)})");
Logger.Debug($"Executing RPC-typed: {message.ServiceName}.{message.MethodName}({string.Join(", ", convertedArgs)})");
var result = method.Invoke(service, convertedArgs.ToArray());
var result = method.Invoke(service, convertedArgs.ToArray());
if (result is Task)
{
await (result as Task);
}
if (result is Task)
{
await (result as Task);
}
var data = JsonConvert.SerializeObject(result);
var data = JsonConvert.SerializeObject(result);
return SvcResultMessage.FromResult(data);
}
catch (Exception ex)
{
Logger.Error(ex, $"ExecuteRpcTypedAsync: {ex.Message}");
return SvcResultMessage.FromResult(data);
}
catch (Exception ex)
{
Logger.Error(ex, $"ExecuteRpcTypedAsync: {ex.Message}");
return new SvcResultMessage { ErrorMessage = ex.Message, Result = false };
}
}
return new SvcResultMessage { ErrorMessage = ex.Message, Result = false };
}
}
private object GetServiceInstance(string name)
{
var type = Type.GetType($"ColorControl.Shared.Services.{name}");
private object GetServiceInstance(string name)
{
var type = Type.GetType($"ColorControl.Shared.Services.{name}");
type ??= Type.GetType($"ColorControl.Services.NVIDIA.{name}");
type ??= Type.GetType($"ColorControl.Services.NVIDIA.{name}");
type ??= ServiceTypes.FirstOrDefault(t => t.Name == name);
type ??= ServiceTypes.FirstOrDefault(t => t.Name == name);
if (type == null)
{
throw new InvalidOperationException($"Type not found: {name}");
}
if (type == null)
{
throw new InvalidOperationException($"Type not found: {name}");
}
var service = _serviceProvider.GetRequiredService(type);
var service = _serviceProvider.GetRequiredService(type);
return service;
}
return service;
}
private MethodInfo GetSuitableMethod(object service, string name, object[] arguments)
{
var methods = service.GetType().GetMethods().Where(m => m.Name == name).ToArray();
private MethodInfo GetSuitableMethod(object service, string name, object[] arguments)
{
var methods = service.GetType().GetMethods().Where(m => m.Name == name).ToArray();
if (methods.Length == 1)
{
return methods[0];
}
if (methods.Length == 1)
{
return methods[0];
}
foreach (var method in methods)
{
var convertedArgs = new List<object>();
var methodParams = method.GetParameters();
foreach (var method in methods)
{
var convertedArgs = new List<object>();
var methodParams = method.GetParameters();
if (methodParams.Length != arguments.Length)
{
continue;
}
if (methodParams.Length != arguments.Length)
{
continue;
}
try
{
for (var i = 0; i < arguments.Length; i++)
{
var arg = arguments[i];
var methodParam = methodParams[i];
try
{
for (var i = 0; i < arguments.Length; i++)
{
var arg = arguments[i];
var methodParam = methodParams[i];
AddArgument(arg, methodParam.ParameterType, convertedArgs);
}
AddArgument(arg, methodParam.ParameterType, convertedArgs);
}
return method;
}
catch (Exception)
{
continue;
}
}
return method;
}
catch (Exception)
{
continue;
}
}
return null;
}
return null;
}
}