Compare commits

...
2 Commits
Author SHA1 Message Date
Maassoft d0d3bb4682 upgrade to .NET, fix webOS 26 compatibility, run new UI in separate process 2026-05-02 17:39:33 +02:00
月鉴风华andcyc19950621 8cb427f0b0 fix: update LG webOS register payload for webOS 26 compatibility (#577)
Co-authored-by: cyc19950621 <cyc19950621@users.noreply.github.com>

I will add a toggle so users have the option to activate/deactivate the new handshake (in case the new one does not work or reduces functionality on older webOS).
2026-05-02 14:07:29 +02:00
81 changed files with 1586 additions and 989 deletions

No files matched your search

+70 -23
View File
@@ -1,7 +1,10 @@
using ColorControl.Shared.Common;
using ColorControl.Shared.Contracts;
using ColorControl.UI.Pages.Generic;
using ColorControl.UI.Services;
using Microsoft.AspNetCore.Hosting.Server;
using Microsoft.AspNetCore.Hosting.Server.Features;
using NStandard;
using System.Net;
namespace ColorControl.UI;
@@ -9,17 +12,15 @@ namespace ColorControl.UI;
public static class Blazor
{
private static WebApplication? CurrentApplication;
private static int Port;
private static bool AllowRemoteConnections;
public static bool IsRunning(Config config) => CurrentApplication != null && Port == config.UiPort && AllowRemoteConnections == config.UiAllowRemoteConnections;
public static CustomComponentBase? CurrentComponent { get; private set; }
public static async Task Start(Config config)
public static async Task Start(string[] args)
{
if (IsRunning(config))
{
return;
}
var mutexId = args.GetValueOrDefault(0);
var useDarkMode = args.GetValueOrDefault(1) == "True";
var uiPort = Utils.ParseInt(args.GetValueOrDefault(2));
var allowRemoteConnections = args.GetValueOrDefault(3) == "True";
await Stop();
@@ -29,7 +30,7 @@ public static class Blazor
builder.Services.AddRazorComponents()
.AddInteractiveServerComponents();
builder.Services.AddSingleton<AppState>(new AppState { SelectedTheme = config.UseDarkMode ? "dark" : "light" });
builder.Services.AddSingleton(new AppState { SelectedTheme = useDarkMode ? "dark" : "light" });
builder.Services.AddTransient<RpcUiClientService>();
builder.Services.AddTransient<JSHelper>();
builder.Services.AddHttpContextAccessor();
@@ -37,11 +38,11 @@ public static class Blazor
builder.Services.Configure<HostOptions>(opts => opts.ShutdownTimeout = TimeSpan.FromSeconds(3));
var listenPort = config.UiPort > 0 ? config.UiPort : 0;
var listenPort = uiPort > 0 ? uiPort : 0;
builder.WebHost.ConfigureKestrel(options =>
{
if (config.UiAllowRemoteConnections)
if (allowRemoteConnections)
{
options.ListenAnyIP(listenPort);
}
@@ -51,6 +52,8 @@ public static class Blazor
}
});
builder.WebHost.UseStaticWebAssets();
var app = builder.Build();
// Configure the HTTP request pipeline.
@@ -61,17 +64,56 @@ public static class Blazor
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStatusCodePagesWithReExecute("/not-found", createScopeForStatusCodePages: true);
//app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseAntiforgery();
app.MapStaticAssets();
//app.UsePathBase("/");
app.MapRazorComponents<Components.App>()
.AddInteractiveServerRenderMode();
CurrentApplication = app;
Port = config.UiPort;
AllowRemoteConnections = config.UiAllowRemoteConnections;
var _ = Task.Run(async () =>
{
await Task.Delay(500);
var port = GetCurrentPort();
if (port == -1)
{
return;
}
var rpcServer = app.Services.GetRequiredService<RpcUiClientService>();
await rpcServer.CallAsync<bool>("OptionsService", "SetCurrentUiSettings", port, allowRemoteConnections);
if (!mutexId.IsNullOrWhiteSpace())
{
Console.WriteLine("Waiting for mutex: " + mutexId);
var mutex = new Mutex(false, mutexId, out var createdNew);
if (createdNew)
{
Console.WriteLine("Mutex was created, skipping.");
return;
}
try
{
mutex.WaitOne();
Console.WriteLine("Waiting done.");
}
catch (Exception ex)
{
Console.WriteLine("Wait completed: " + ex.Message);
}
Environment.Exit(0);
}
});
await app.RunAsync();
}
@@ -85,14 +127,7 @@ public static class Blazor
}
}
public static string GetCurrentUrl()
{
var port = GetCurrentPort();
return $"http://localhost:{port}";
}
public static int GetCurrentPort()
public static int GetCurrentPort(Config? config = null)
{
if (CurrentApplication == null)
{
@@ -115,4 +150,16 @@ public static class Blazor
return -1;
}
public static void SetCurrentComponent(CustomComponentBase? customComponentBase)
{
CurrentComponent = customComponentBase;
}
public static void ClearCurrentComponent(CustomComponentBase customComponentBase)
{
if (CurrentComponent == customComponentBase)
{
CurrentComponent = null;
}
}
}
+3 -3
View File
@@ -1,15 +1,15 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net9.0-windows</TargetFramework>
<TargetFramework>net10.0-windows</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<OutputType>Library</OutputType>
<ResolveAssemblyWarnOrErrorOnTargetArchitectureMismatch>None</ResolveAssemblyWarnOrErrorOnTargetArchitectureMismatch>
<BlazorDisableThrowNavigationException>true</BlazorDisableThrowNavigationException>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="NLog" Version="5.3.4" />
<PackageReference Include="NLog" Version="6.1.2" />
</ItemGroup>
<ItemGroup>
+22 -4
View File
@@ -1,5 +1,6 @@
@using ColorControl.Shared.Common
@using ColorControl.Shared.Contracts
@using ColorControl.UI.Pages.Generic
@using ColorControl.UI.Services
@using ColorControl.UI.Components.Pages.Generic
@using Microsoft.AspNetCore.Components.Server.ProtectedBrowserStorage
@@ -9,6 +10,7 @@
@inject NavigationManager _navigationManager
@inject NotificationService _notificationService
@inject ProtectedSessionStorage _sessionStorage
@inject JSHelper _jsHelper
@implements IDisposable
@attribute [StreamRendering]
@@ -20,19 +22,26 @@
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<base href="/" />
<ResourcePreloader />
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous">
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz" crossorigin="anonymous"></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css">
<link rel="stylesheet" href="app.css" />
<link rel="stylesheet" href="ColorControl.UI.styles.css" />
<ImportMap />
<link rel="icon" type="image/png" href="favicon.png" />
<HeadOutlet />
<HeadOutlet @rendermode="InteractiveServer" />
<script>
addEventListener('focus', (event) => {
var isModalShown = document.getElementsByClassName("modal show").length > 0;
DotNet.invokeMethodAsync("ColorControl.UI", "RefreshSummary", isModalShown);
});
</script>
</head>
<body>
<Routes />
<script src="_framework/blazor.web.js"></script>
<Routes @rendermode="InteractiveServer" />
<script src="@Assets["_framework/blazor.web.js"]"></script>
<NotificationsComponent></NotificationsComponent>
</body>
@@ -59,4 +68,13 @@
{
AppState.OnChange -= StateHasChanged;
}
[JSInvokable]
public static async Task RefreshSummary(bool isModalShown)
{
if (Blazor.CurrentComponent is SummaryComponentBase summaryComponent && !isModalShown)
{
await summaryComponent.RefreshExternal();
}
}
}
@@ -3,10 +3,12 @@
@using ColorControl.Shared.Common
@using ColorControl.Shared.Contracts
@using ColorControl.Shared.Contracts.AMD
@using ColorControl.UI.Pages.Generic
@using ColorControl.UI.Services;
@using ColorControl.UI.Components.Pages.Generic
@using ColorControl.UI.Generics
@using Microsoft.AspNetCore.Components.Server.ProtectedBrowserStorage
@inherits SummaryComponentBase
@rendermode Constants.RenderMode
@@ -144,6 +146,13 @@ else
private PresetOrder PresetOrder;
protected override async Task OnInitializedAsync()
{
await base.OnInitializedAsync();
await Refresh();
}
protected override async Task Refresh()
{
await RefreshPresets();
DisplayPresets = await _rpcClientService.CallAsync<List<AmdPreset>>("AmdService", "GetDisplayPresets");
@@ -5,7 +5,9 @@
@using ColorControl.Shared.Contracts.DisplayInfo
@using ColorControl.UI.Components.Pages.Generic
@using ColorControl.UI.Generics
@using ColorControl.UI.Pages.Generic
@using ColorControl.UI.Services
@inherits SummaryComponentBase
@rendermode Constants.RenderMode
@@ -190,7 +192,15 @@ else
private FieldDefinition AskHdrAssociationField = new FieldDefinition { Label = "Associate as HDR profile", FieldType = FieldType.CheckBox, Value = false };
protected override async Task OnInitializedAsync()
{
await base.OnInitializedAsync();
await Refresh();
}
protected override async Task Refresh()
{
await RefreshDisplays();
}
@@ -3,7 +3,6 @@
@using ColorControl.Shared.Contracts.Game
@using ColorControl.UI.Services
@using ColorControl.UI.Components.Pages.Generic
@using NvAPIWrapper.Native.Display
@using ColorControl.Shared.Common
@rendermode Constants.RenderMode
@@ -0,0 +1,20 @@
using Microsoft.AspNetCore.Components;
namespace ColorControl.UI.Pages.Generic;
public abstract class CustomComponentBase : ComponentBase, IDisposable
{
public void Dispose()
{
GC.SuppressFinalize(this);
Blazor.ClearCurrentComponent(this);
}
protected override Task OnInitializedAsync()
{
Blazor.SetCurrentComponent(this);
return Task.CompletedTask;
}
}
@@ -44,9 +44,9 @@
try
{
var options = new RpcUiClientOptions
{
Timeout = 60000
};
{
Timeout = 60000
};
var result = await _rpcClientService.CallWithOptionsAsync<string>("GameService", "SelectPath", options);
@@ -0,0 +1,13 @@
namespace ColorControl.UI.Pages.Generic;
public abstract class SummaryComponentBase : CustomComponentBase
{
protected abstract Task Refresh();
public async Task RefreshExternal()
{
await Refresh();
await InvokeAsync(StateHasChanged);
}
}
@@ -7,16 +7,20 @@
@if (Triggers != null)
{
if (!Triggers.Any())
if (Triggers.Count < 2)
{
<div>
<label class="form-label">No triggers found. Click to add:</label>
<label class="form-label">Add trigger:</label>
<button class="btn btn-primary btn-sm" type="button" @onclick="() => Triggers.Add(new PresetTrigger())">Add</button>
</div>
}
foreach (var trigger in Triggers)
{
<div class="clearfix">
<label class="form-label">Trigger @(Triggers.IndexOf(trigger) + 1)</label>
<button class="btn btn-danger btn-sm float-end" type="button" @onclick="() => Triggers.Remove(trigger)">Remove</button>
</div>
<div class="mb-2">
<label class="form-label" for="triggerType">Trigger type</label>
<select class="form-select" id="triggerType" @bind="trigger.Trigger">
@@ -26,7 +30,7 @@
}
</select>
</div>
<FlagsInput Label="Conditions" Value="@((uint)trigger.Conditions)" Labels="ConditionLabels" Col="6" ValueChanged="ConditionsChanged"></FlagsInput>
<FlagsInput Label="Conditions" Value="@((uint)trigger.Conditions)" Labels="ConditionLabels" Col="6" ValueChanged="value => ConditionsChanged(value, trigger)"></FlagsInput>
<div class="mb-2">
<label class="form-label" for="includedProcesses">Included processes</label>
<input class="form-control" type="text" @bind="trigger.IncludedProcessesAsString" />
@@ -40,6 +44,11 @@
<input class="form-control" type="text" @bind="trigger.ConnectedDisplaysRegex"
title="The preset will be executed only if the supplied Regular Expression matches the device name of at least one of the displays that are connected at the moment the trigger is fired." />
</div>
@if (Triggers.IndexOf(trigger) < Triggers.Count - 1)
{
<div class="border-bottom mb-2"></div>
}
}
}
@@ -49,14 +58,8 @@
private List<string> ConditionLabels = Utils.GetDescriptions<PresetConditionType>(fromValue: 1).ToList();
public void ConditionsChanged(uint value)
public void ConditionsChanged(uint value, PresetTrigger trigger)
{
if (Triggers == null || !Triggers.Any())
{
return;
}
var trigger = Triggers.First();
trigger.Conditions = (PresetConditionType)value;
}
}
@@ -1,7 +1,7 @@
@using ColorControl.Shared.Contracts
@using ColorControl.Shared.Contracts.LG
@using ColorControl.UI.Services
@using NvAPIWrapper.Native.Display
@using Microsoft.AspNetCore.Components.Web
@using ColorControl.Shared.Common
@using ColorControl.UI.Components.Pages.Generic
@@ -154,6 +154,10 @@
<input class="form-check-input" type="checkbox" role="switch" id="checkUseSecureConnection" @bind="Device.Options.UseSecureConnection">
<label class="form-check-label" for="checkUseSecureConnection">Use secure connection</label>
</div>
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" role="switch" id="checkUseNewHandshake" @bind="Device.Options.UseNewHandshake">
<label class="form-check-label" for="checkUseNewHandshake">Enable webOS 26 compatibility</label>
</div>
</div>
</div>
</div>
@@ -2,7 +2,6 @@
@using ColorControl.Shared.Contracts.LG
@using ColorControl.UI.Services
@using ColorControl.UI.Components.Pages.Generic
@using NvAPIWrapper.Native.Display
@using ColorControl.Shared.Common
@using static ColorControl.UI.Components.Pages.Generic.StepsInput<ColorControl.Shared.Contracts.LG.LgPreset>
@@ -5,10 +5,12 @@
@using ColorControl.Shared.Contracts.NVIDIA
@using ColorControl.Shared.Contracts.LG
@using ColorControl.UI.Components.Pages.RemoteControl
@using ColorControl.UI.Pages.Generic
@using ColorControl.UI.Services;
@using ColorControl.UI.Components.Pages.Generic
@using ColorControl.UI.Generics
@using Microsoft.AspNetCore.Components.Server.ProtectedBrowserStorage
@inherits SummaryComponentBase
@rendermode Constants.RenderMode
@@ -239,6 +241,13 @@ else
private List<LgPreset> RcPresets = [LgPresets.InStartPreset, LgPresets.EzAdjustPreset];
protected override async Task OnInitializedAsync()
{
await base.OnInitializedAsync();
await Refresh();
}
protected override async Task Refresh()
{
Config = await _rpcClientService.CallAsync<LgServiceConfigDto>("LgService", "GetConfig");
await RefreshPresets();
@@ -1,7 +1,6 @@
@using ColorControl.Shared.Contracts
@using ColorControl.Shared.Contracts.NVIDIA
@using ColorControl.UI.Services;
@using NvAPIWrapper.Native.Display
@using ColorControl.Shared.Common;
@rendermode Constants.RenderMode
@@ -2,7 +2,6 @@
@using ColorControl.Shared.Contracts.NVIDIA
@using ColorControl.UI.Components.Pages.Generic
@using ColorControl.UI.Services;
@using NvAPIWrapper.Native.Display
@using ColorControl.Shared.Common;
@rendermode Constants.RenderMode
@@ -2,7 +2,6 @@
@using ColorControl.Shared.Contracts.NVIDIA
@using ColorControl.UI.Components.Pages.Generic
@using ColorControl.UI.Services;
@using NvAPIWrapper.Native.Display
@using ColorControl.Shared.Common;
@rendermode Constants.RenderMode
@@ -1,7 +1,6 @@
@using ColorControl.Shared.Contracts
@using ColorControl.Shared.Contracts.NVIDIA
@using ColorControl.UI.Services;
@using NvAPIWrapper.Native.Display
@using ColorControl.Shared.Common;
@using static ColorControl.Shared.Common.NestedItemsBuilder
@@ -45,50 +45,62 @@
}
<div class="accordion accordion-flush" id="presetAccordion">
@if (!Preset.IsDisplayPreset)
{
<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; visibility: hidden" type="checkbox">
}
<button class="accordion-button collapsed py-2" type="button" data-bs-toggle="collapse" data-bs-target="#displaySettings">
Display
</button>
</span>
</h2>
<div id="displaySettings" class="accordion-collapse collapse" data-bs-parent="#presetAccordion">
<div class="accordion-body">
<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; visibility: hidden" type="checkbox">
}
<button class="accordion-button collapsed py-2" type="button" data-bs-toggle="collapse" data-bs-target="#displaySettings">
Display
</button>
</span>
</h2>
<div id="displaySettings" class="accordion-collapse collapse" data-bs-parent="#presetAccordion">
<div class="accordion-body">
@if (!Preset.IsDisplayPreset)
{
<div class="mb-2">
<input class="form-check-input" type="checkbox" @bind="Preset.primaryDisplay" id="primaryDisplay">
<label class="form-check-label" for="primaryDisplay">
Primary display
</label>
</div>
@if (displayPresets != null)
}
@if (displayPresets != null)
{
if (!Preset.IsDisplayPreset)
{
<div class="mb-2">
<label class="form-label" for="mode">Display</label>
<select class="form-select" id="mode" @bind="Preset.DisplayId" disabled="@(Preset.primaryDisplay)">
<select class="form-select" id="mode" @bind="Preset.DisplayId" disabled="@Preset.primaryDisplay">
@foreach (var display in displayPresets)
{
<option value="@display.DisplayId">@display.displayName</option>
}
</select>
</div>
<div class="mb-2">
<input class="form-check-input" type="checkbox" @bind="Preset.DisplayConfig.IsPrimary" id="setAsPrimaryDisplay" disabled="@(Preset.primaryDisplay)">
<label class="form-check-label" for="setAsPrimaryDisplay">
Set display as primary display
</label>
</div>
}
<div class="mb-2">
<input class="form-check-input" type="checkbox" @bind="Preset.DisplayConfig.IsPrimary" id="setAsPrimaryDisplay" disabled="@(Preset.IsDisplayPreset && displayPresets.Count == 1)">
<label class="form-check-label" for="setAsPrimaryDisplay">
Set display as primary display
</label>
</div>
}
<div class="mb-2">
<label class="form-label" for="connectivity">Connectivity</label>
<select class="form-select" id="scaling" @bind="Preset.DisplayConfig.Connectivity">
@foreach (var connectivity in Enum.GetValues<Shared.Contracts.DisplayConnectivity>())
{
<option value="@connectivity.ToString()">@connectivity.ToString()</option>
}
</select>
</div>
</div>
</div>
}
</div>
@if (resolutions != null)
{
@@ -745,7 +757,7 @@
ApplyImmediately = false;
Preset = PresetParam == null ? null : PresetParam.CloneWithId();
Preset = PresetParam?.CloneWithId();
displayPresets = await _rpcClientService.CallAsync<List<NvPreset>>("NvService", "GetDisplayPresets");
if (Preset?.IsDisplayPreset == true)
@@ -2,11 +2,13 @@
@using ColorControl.Shared.Contracts
@using ColorControl.Shared.Contracts.NVIDIA
@using ColorControl.UI.Pages.Generic
@using ColorControl.UI.Services;
@using ColorControl.UI.Components.Pages.Generic
@using ColorControl.Shared.Common;
@using ColorControl.UI.Generics
@using Microsoft.AspNetCore.Components.Server.ProtectedBrowserStorage
@inherits SummaryComponentBase
@rendermode Constants.RenderMode
@@ -276,6 +278,13 @@ else
private PresetOrder PresetOrder;
protected override async Task OnInitializedAsync()
{
await base.OnInitializedAsync();
await Refresh();
}
protected override async Task Refresh()
{
NvConfig = await _rpcClientService.CallAsync<NvServiceConfig>("NvService", "GetConfig");
await RefreshPresets();
@@ -2,7 +2,6 @@
@using ColorControl.Shared.Contracts.Base
@using ColorControl.Shared.Contracts.RemoteControl
@using ColorControl.UI.Services
@using NvAPIWrapper.Native.Display
@using ColorControl.Shared.Common
@using System.Globalization
@@ -2,7 +2,6 @@
@using ColorControl.Shared.Contracts.Samsung
@using ColorControl.UI.Components.Pages.Generic
@using ColorControl.UI.Services
@using NvAPIWrapper.Native.Display
@using ColorControl.Shared.Common
@rendermode Constants.RenderMode
@@ -2,7 +2,6 @@
@using ColorControl.Shared.Contracts.Samsung
@using ColorControl.UI.Services
@using ColorControl.UI.Components.Pages.Generic
@using NvAPIWrapper.Native.Display
@using ColorControl.Shared.Common
@using static ColorControl.UI.Components.Pages.Generic.StepsInput<ColorControl.Shared.Contracts.Samsung.SamsungPreset>
@@ -102,8 +102,8 @@
SecondaryAction = FactoryMenuSecondStepEnterWithoutExecute;
Fields =
[
AddField("Service Menu - Advanced", null, FieldType.Label, "If the service menu opened and you want to access the Advanced settings, click 'Next >' or click 'Skip' to skip"),
AddField("Advanced Unlock Type", AdvancedUnlockType.TV, subLabel: "Select the desired unlock type for the Advanced section. If the target is a TV choose TV, otherwise choose Monitor")
AddField("Service Menu - Advanced", null, FieldType.Label, "If the service menu opened and you want to access the Advanced settings, click 'Next >' or click 'Skip' to skip. You can also manually enter the unlock codes 0098 (for TV's) and 0038 (for monitors) while the 'Advanced' item is highlighted."),
AddField("Advanced Unlock Type", AdvancedUnlockType.TV, subLabel: "Select the desired unlock type for the Advanced section. If the target is a TV choose TV, otherwise choose Monitor")
];
StateHasChanged();
@@ -5,10 +5,12 @@
@using ColorControl.Shared.Contracts.NVIDIA
@using ColorControl.Shared.Contracts.Samsung
@using ColorControl.UI.Components.Pages.RemoteControl
@using ColorControl.UI.Pages.Generic
@using ColorControl.UI.Services;
@using ColorControl.UI.Components.Pages.Generic
@using ColorControl.UI.Generics
@using Microsoft.AspNetCore.Components.Server.ProtectedBrowserStorage
@inherits SummaryComponentBase
@rendermode Constants.RenderMode
@@ -220,6 +222,13 @@ else
private PresetOrder PresetOrder;
protected override async Task OnInitializedAsync()
{
await base.OnInitializedAsync();
await Refresh();
}
protected override async Task Refresh()
{
Config = await _rpcClientService.CallAsync<SamsungServiceConfigDto>("SamsungService", "GetConfig");
await RefreshPresets();
-2
View File
@@ -12,7 +12,5 @@ public static class Constants
public const string Light = "light";
public const string Dark = "dark";
public static IComponentRenderMode RenderMode = new InteractiveServerRenderMode(prerender: false);
}
+16 -28
View File
@@ -1,32 +1,20 @@
//using ColorControl.UI;
using ColorControl.Shared.Contracts;
using ColorControl.UI;
using ColorControl.UI.Services;
using System.Diagnostics;
//Blazor.Start();
var arguments = args;
//var builder = WebApplication.CreateBuilder(args);
if (Debugger.IsAttached)
{
var rpcServer = new RpcUiClientService(null);
var options = new RpcUiClientOptions
{
Timeout = 10000
};
//// Add services to the container.
//builder.Services.AddRazorComponents()
// .AddInteractiveServerComponents();
var config = await rpcServer.CallWithOptionsAsync<Config>("OptionsService", "GetConfig", options);
arguments = ["", config.UseDarkMode.ToString(), config.UiPort.ToString(), config.UiAllowRemoteConnections.ToString()];
}
//builder.Services.AddSingleton<AppState>();
//builder.Services.AddSingleton<OptionsService>();
//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<App>()
// .AddInteractiveServerRenderMode();
//app.Run();
await Blazor.Start(arguments);
@@ -1,13 +1,5 @@
{
"$schema": "http://json.schemastore.org/launchsettings.json",
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:31433",
"sslPort": 44330
}
},
"profiles": {
"http": {
"commandName": "Project",
@@ -26,13 +18,6 @@
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}
+5
View File
@@ -29,6 +29,11 @@ public class JSHelper(IJSRuntime jsRuntime)
await InvokeVoidAsync("CloseModal", id);
}
public async Task<bool> IsModalShown()
{
return await InvokeAsync<bool>("IsModalShown");
}
public async Task ShowToast(string id)
{
await InvokeVoidAsync("ShowToast", id);
@@ -10,7 +10,7 @@ public class RpcUiClientOptions
public int Timeout { get; set; } = PipeUtils.DefaultTimeout;
}
public class RpcUiClientService(NotificationService notificationService)
public class RpcUiClientService(NotificationService? notificationService)
{
public string? LastErrorMessage { get; private set; }
@@ -46,7 +46,7 @@ public class RpcUiClientService(NotificationService notificationService)
return true;
}
if (result?.ErrorMessages.Count > 0)
if (result?.ErrorMessages.Count > 0 && notificationService != null)
{
await notificationService.SendNotificationDirect(new NotificationDto(result.ErrorMessages.First(), Constants.Danger));
}
@@ -74,7 +74,7 @@ public class RpcUiClientService(NotificationService notificationService)
if (resultJson == null)
{
notificationService.SendNotification(new NotificationDto("Unknown communication error", Constants.Danger));
notificationService?.SendNotification(new NotificationDto("Unknown communication error", Constants.Danger));
return default;
}
@@ -85,7 +85,10 @@ public class RpcUiClientService(NotificationService notificationService)
{
LastErrorMessage = resultMessage?.ErrorMessage ?? "Unknown communication error";
await notificationService.SendNotificationDirect(new NotificationDto(LastErrorMessage, Constants.Danger));
if (notificationService != null)
{
await notificationService.SendNotificationDirect(new NotificationDto(LastErrorMessage, Constants.Danger));
}
return default;
}
+5 -1
View File
@@ -26,4 +26,8 @@ export function GetElementClassName(elementId) {
export function NavigateTo(uri, timeout = 0) {
setTimeout(() => window.location = uri, timeout);
}
}
export function IsModalShown() {
return document.getElementsByClassName("modal show").length > 0;
}
-111
View File
@@ -1,111 +0,0 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.0.32014.148
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ColorControl", "ColorControl\ColorControl.csproj", "{A6005D08-70F0-44CB-A384-9219EBA84F21}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nspector", "Nspector\Nspector.csproj", "{76797290-48F5-4A82-86C3-FAD25DAE4500}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NvAPIWrapper", "NvAPIWrapper\NvAPIWrapper.csproj", "{42E2B4A6-6857-4427-AEF1-DF4C7EF8CC7C}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "novideo_srgb", "novideo_srgb\novideo_srgb.csproj", "{A6A97834-7BE1-474A-B92F-A512DB1D5186}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NLogViewer", "NLogViewer\NLogViewer.csproj", "{D5E4D314-CFCD-4859-98FC-68F660AC7FB4}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Shared", "Shared\Shared.csproj", "{8F38D6B0-82DA-4DD1-89EB-918D8A18AEB1}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LittleCms", "LittleCms\LittleCms.csproj", "{42D251AE-42AF-4C0D-B163-7C7A13C4A9A6}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MHC2Gen", "MHC2Gen\MHC2Gen.csproj", "{9FDF741F-0ECF-46DB-8671-AB11ACEB9F41}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ColorControl.UI", "ColorControl.UI\ColorControl.UI.csproj", "{85EA3079-BB73-4CE5-A72F-E56906187051}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Debug|x64 = Debug|x64
Release|Any CPU = Release|Any CPU
Release|x64 = Release|x64
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{A6005D08-70F0-44CB-A384-9219EBA84F21}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A6005D08-70F0-44CB-A384-9219EBA84F21}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A6005D08-70F0-44CB-A384-9219EBA84F21}.Debug|x64.ActiveCfg = Debug|Any CPU
{A6005D08-70F0-44CB-A384-9219EBA84F21}.Debug|x64.Build.0 = Debug|Any CPU
{A6005D08-70F0-44CB-A384-9219EBA84F21}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A6005D08-70F0-44CB-A384-9219EBA84F21}.Release|Any CPU.Build.0 = Release|Any CPU
{A6005D08-70F0-44CB-A384-9219EBA84F21}.Release|x64.ActiveCfg = Release|Any CPU
{A6005D08-70F0-44CB-A384-9219EBA84F21}.Release|x64.Build.0 = Release|Any CPU
{76797290-48F5-4A82-86C3-FAD25DAE4500}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{76797290-48F5-4A82-86C3-FAD25DAE4500}.Debug|Any CPU.Build.0 = Debug|Any CPU
{76797290-48F5-4A82-86C3-FAD25DAE4500}.Debug|x64.ActiveCfg = Debug|Any CPU
{76797290-48F5-4A82-86C3-FAD25DAE4500}.Debug|x64.Build.0 = Debug|Any CPU
{76797290-48F5-4A82-86C3-FAD25DAE4500}.Release|Any CPU.ActiveCfg = Release|Any CPU
{76797290-48F5-4A82-86C3-FAD25DAE4500}.Release|Any CPU.Build.0 = Release|Any CPU
{76797290-48F5-4A82-86C3-FAD25DAE4500}.Release|x64.ActiveCfg = Release|Any CPU
{76797290-48F5-4A82-86C3-FAD25DAE4500}.Release|x64.Build.0 = Release|Any CPU
{42E2B4A6-6857-4427-AEF1-DF4C7EF8CC7C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{42E2B4A6-6857-4427-AEF1-DF4C7EF8CC7C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{42E2B4A6-6857-4427-AEF1-DF4C7EF8CC7C}.Debug|x64.ActiveCfg = Debug|Any CPU
{42E2B4A6-6857-4427-AEF1-DF4C7EF8CC7C}.Debug|x64.Build.0 = Debug|Any CPU
{42E2B4A6-6857-4427-AEF1-DF4C7EF8CC7C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{42E2B4A6-6857-4427-AEF1-DF4C7EF8CC7C}.Release|Any CPU.Build.0 = Release|Any CPU
{42E2B4A6-6857-4427-AEF1-DF4C7EF8CC7C}.Release|x64.ActiveCfg = Release|Any CPU
{42E2B4A6-6857-4427-AEF1-DF4C7EF8CC7C}.Release|x64.Build.0 = Release|Any CPU
{A6A97834-7BE1-474A-B92F-A512DB1D5186}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A6A97834-7BE1-474A-B92F-A512DB1D5186}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A6A97834-7BE1-474A-B92F-A512DB1D5186}.Debug|x64.ActiveCfg = Debug|Any CPU
{A6A97834-7BE1-474A-B92F-A512DB1D5186}.Debug|x64.Build.0 = Debug|Any CPU
{A6A97834-7BE1-474A-B92F-A512DB1D5186}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A6A97834-7BE1-474A-B92F-A512DB1D5186}.Release|Any CPU.Build.0 = Release|Any CPU
{A6A97834-7BE1-474A-B92F-A512DB1D5186}.Release|x64.ActiveCfg = Release|Any CPU
{A6A97834-7BE1-474A-B92F-A512DB1D5186}.Release|x64.Build.0 = Release|Any CPU
{D5E4D314-CFCD-4859-98FC-68F660AC7FB4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D5E4D314-CFCD-4859-98FC-68F660AC7FB4}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D5E4D314-CFCD-4859-98FC-68F660AC7FB4}.Debug|x64.ActiveCfg = Debug|Any CPU
{D5E4D314-CFCD-4859-98FC-68F660AC7FB4}.Debug|x64.Build.0 = Debug|Any CPU
{D5E4D314-CFCD-4859-98FC-68F660AC7FB4}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D5E4D314-CFCD-4859-98FC-68F660AC7FB4}.Release|Any CPU.Build.0 = Release|Any CPU
{D5E4D314-CFCD-4859-98FC-68F660AC7FB4}.Release|x64.ActiveCfg = Release|Any CPU
{D5E4D314-CFCD-4859-98FC-68F660AC7FB4}.Release|x64.Build.0 = Release|Any CPU
{8F38D6B0-82DA-4DD1-89EB-918D8A18AEB1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{8F38D6B0-82DA-4DD1-89EB-918D8A18AEB1}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8F38D6B0-82DA-4DD1-89EB-918D8A18AEB1}.Debug|x64.ActiveCfg = Debug|Any CPU
{8F38D6B0-82DA-4DD1-89EB-918D8A18AEB1}.Debug|x64.Build.0 = Debug|Any CPU
{8F38D6B0-82DA-4DD1-89EB-918D8A18AEB1}.Release|Any CPU.ActiveCfg = Release|Any CPU
{8F38D6B0-82DA-4DD1-89EB-918D8A18AEB1}.Release|Any CPU.Build.0 = Release|Any CPU
{8F38D6B0-82DA-4DD1-89EB-918D8A18AEB1}.Release|x64.ActiveCfg = Release|Any CPU
{8F38D6B0-82DA-4DD1-89EB-918D8A18AEB1}.Release|x64.Build.0 = Release|Any CPU
{42D251AE-42AF-4C0D-B163-7C7A13C4A9A6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{42D251AE-42AF-4C0D-B163-7C7A13C4A9A6}.Debug|Any CPU.Build.0 = Debug|Any CPU
{42D251AE-42AF-4C0D-B163-7C7A13C4A9A6}.Debug|x64.ActiveCfg = Debug|Any CPU
{42D251AE-42AF-4C0D-B163-7C7A13C4A9A6}.Debug|x64.Build.0 = Debug|Any CPU
{42D251AE-42AF-4C0D-B163-7C7A13C4A9A6}.Release|Any CPU.ActiveCfg = Release|Any CPU
{42D251AE-42AF-4C0D-B163-7C7A13C4A9A6}.Release|Any CPU.Build.0 = Release|Any CPU
{42D251AE-42AF-4C0D-B163-7C7A13C4A9A6}.Release|x64.ActiveCfg = Release|Any CPU
{42D251AE-42AF-4C0D-B163-7C7A13C4A9A6}.Release|x64.Build.0 = Release|Any CPU
{9FDF741F-0ECF-46DB-8671-AB11ACEB9F41}.Debug|Any CPU.ActiveCfg = Debug|x64
{9FDF741F-0ECF-46DB-8671-AB11ACEB9F41}.Debug|Any CPU.Build.0 = Debug|x64
{9FDF741F-0ECF-46DB-8671-AB11ACEB9F41}.Debug|x64.ActiveCfg = Debug|x64
{9FDF741F-0ECF-46DB-8671-AB11ACEB9F41}.Debug|x64.Build.0 = Debug|x64
{9FDF741F-0ECF-46DB-8671-AB11ACEB9F41}.Release|Any CPU.ActiveCfg = Release|x64
{9FDF741F-0ECF-46DB-8671-AB11ACEB9F41}.Release|Any CPU.Build.0 = Release|x64
{9FDF741F-0ECF-46DB-8671-AB11ACEB9F41}.Release|x64.ActiveCfg = Release|x64
{9FDF741F-0ECF-46DB-8671-AB11ACEB9F41}.Release|x64.Build.0 = Release|x64
{85EA3079-BB73-4CE5-A72F-E56906187051}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{85EA3079-BB73-4CE5-A72F-E56906187051}.Debug|Any CPU.Build.0 = Debug|Any CPU
{85EA3079-BB73-4CE5-A72F-E56906187051}.Debug|x64.ActiveCfg = Debug|Any CPU
{85EA3079-BB73-4CE5-A72F-E56906187051}.Debug|x64.Build.0 = Debug|Any CPU
{85EA3079-BB73-4CE5-A72F-E56906187051}.Release|Any CPU.ActiveCfg = Release|Any CPU
{85EA3079-BB73-4CE5-A72F-E56906187051}.Release|Any CPU.Build.0 = Release|Any CPU
{85EA3079-BB73-4CE5-A72F-E56906187051}.Release|x64.ActiveCfg = Release|Any CPU
{85EA3079-BB73-4CE5-A72F-E56906187051}.Release|x64.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {34BA158F-AE57-4A7B-A372-11FEFBBC1F98}
EndGlobalSection
EndGlobal
+17
View File
@@ -0,0 +1,17 @@
<Solution>
<Configurations>
<Platform Name="Any CPU" />
<Platform Name="x64" />
</Configurations>
<Project Path="ColorControl.UI/ColorControl.UI.csproj" />
<Project Path="ColorControl/ColorControl.csproj" />
<Project Path="LittleCms/LittleCms.csproj" />
<Project Path="MHC2Gen/MHC2Gen.csproj">
<Platform Project="x64" />
</Project>
<Project Path="NLogViewer/NLogViewer.csproj" />
<Project Path="novideo_srgb/novideo_srgb.csproj" />
<Project Path="Nspector/Nspector.csproj" />
<Project Path="NvAPIWrapper/NvAPIWrapper.csproj" />
<Project Path="Shared/Shared.csproj" />
</Solution>
+111
View File
@@ -0,0 +1,111 @@
using ColorControl.Shared.Common;
using ColorControl.Shared.Contracts;
using ColorControl.Shared.Services;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Diagnostics;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
namespace ColorControl;
public static class BlazorUiManager
{
private const string ImageName = "ColorControl.UI.exe";
private const string ProcessName = "ColorControl.UI";
private const bool DebugUi = true;
private static bool FirsTime = true;
private static Mutex UiMutex;
private static string MutexId;
public static bool IsRunning(Config config)
{
return Process.GetProcessesByName(ProcessName).Length > 0 &&
(config.UiPort == 0 || config.CurrentUiPort == config.UiPort) &&
config.CurrentUiAllowRemoteConnections == config.UiAllowRemoteConnections;
}
public static async Task<bool> Start(Config config)
{
if ((!FirsTime || Debugger.IsAttached && DebugUi) && IsRunning(config))
{
return false;
}
FirsTime = false;
await Stop();
var uiExe = Path.Combine(Environment.CurrentDirectory, ImageName);
if (!File.Exists(uiExe))
{
uiExe = Path.Combine(Environment.CurrentDirectory.Replace("ColorControl\\bin", "ColorControl.UI\\bin"), ImageName);
if (!File.Exists(uiExe))
{
throw new InvalidOperationException("UI executable not found");
}
}
MutexId = $"Global\\{ProcessName}_{Guid.NewGuid()}";
Utils.ExecuteOnMainThread(() =>
{
UiMutex = new Mutex(false, MutexId);
UiMutex.WaitOne();
});
var windowStyle = ProcessWindowStyle.Hidden;
if (Debugger.IsAttached)
{
windowStyle = ProcessWindowStyle.Normal;
}
var processStartInfo = new ProcessStartInfo(uiExe, [MutexId, config.UseDarkMode.ToString(), config.UiPort.ToString(), config.UiAllowRemoteConnections.ToString()])
{
WindowStyle = windowStyle
};
Process.Start(processStartInfo);
return true;
}
public static async Task Stop()
{
if (UiMutex != null)
{
Utils.ExecuteOnMainThread(() =>
{
UiMutex.ReleaseMutex();
UiMutex.Close();
UiMutex = null;
});
return;
}
var processes = Process.GetProcessesByName(ProcessName);
if (processes.Length <= 0)
{
return;
}
var winApi = Program.ServiceProvider.GetRequiredService<WinApiAdminService>();
winApi.StopProcessByName(ProcessName);
}
public static string GetCurrentUrl(Config config)
{
var port = GetCurrentPort(config);
return $"http://localhost:{port}";
}
public static int GetCurrentPort(Config config = null)
{
return config?.CurrentUiPort ?? -1;
}
}
+11 -12
View File
@@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net9.0-windows</TargetFramework>
<TargetFramework>net10.0-windows</TargetFramework>
<OutputType>WinExe</OutputType>
<IsWebBootstrapper>false</IsWebBootstrapper>
<PublishUrl>publish\</PublishUrl>
@@ -11,8 +11,8 @@
<PublisherName>Maassoft</PublisherName>
<Company>Maassoft</Company>
<ApplicationRevision>0</ApplicationRevision>
<ApplicationVersion>10.4.0.0</ApplicationVersion>
<Version>10.4.0.0</Version>
<ApplicationVersion>11.0.0.0</ApplicationVersion>
<Version>11.0.0.0</Version>
<UseApplicationTrust>false</UseApplicationTrust>
<PublishWizardCompleted>true</PublishWizardCompleted>
<BootstrapperEnabled>false</BootstrapperEnabled>
@@ -72,6 +72,7 @@
<SubType>UserControl</SubType>
</Compile>
<EmbeddedResource Include="Resources\LgRemotes.json" />
<EmbeddedResource Include="Resources\LG_register_legacy.json" />
<EmbeddedResource Include="Resources\LG_register.json" />
<EmbeddedResource Include="Resources\RC_coords.txt" />
<EmbeddedResource Include="Resources\LG_remote.jpg" />
@@ -96,16 +97,14 @@
</COMReference>
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.CSharp" Version="4.7.0" />
<PackageReference Include="Microsoft.Extensions.Hosting.WindowsServices" Version="9.0.0" />
<PackageReference Include="Microsoft.Web.WebView2" Version="1.0.2903.40" />
<PackageReference Include="System.Configuration.ConfigurationManager" Version="9.0.0" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="NLog" Version="5.3.4" />
<PackageReference Include="NStandard" Version="0.83.1" />
<PackageReference Include="Microsoft.Extensions.Hosting.WindowsServices" Version="10.0.7" />
<PackageReference Include="Microsoft.Web.WebView2" Version="1.0.3912.50" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.4" />
<PackageReference Include="NLog" Version="6.1.2" />
<PackageReference Include="NStandard" Version="0.110.7" />
<PackageReference Include="NWin32" Version="1.2.8" />
<PackageReference Include="System.Management" Version="9.0.0" />
<PackageReference Include="TaskScheduler" Version="2.11.0" />
<PackageReference Include="System.Management" Version="10.0.7" />
<PackageReference Include="TaskScheduler" Version="2.12.2" />
</ItemGroup>
<ItemGroup>
<Page Include="XForms\ColorProfileWindow.xaml">
+6 -1
View File
@@ -199,6 +199,11 @@ namespace LgTv
{
try
{
if (string.IsNullOrEmpty(message))
{
throw new InvalidOperationException("Empty response received. Assuming connection closed.");
}
var obj = JsonConvert.DeserializeObject<dynamic>(message);
var id = (string)obj.id;
var type = (string)obj.type;
@@ -217,7 +222,7 @@ namespace LgTv
{
if (id == "register_0")
{
//taskCompletion.TrySetResult(obj.payload);
//taskCompletion.TrySetResult(null);
return;
}
if (obj.type == "error")
+2
View File
@@ -177,6 +177,8 @@ internal class MainWorker
private void StartScreenSaver()
{
//NativeMethods.SendMessageW(-1, NativeConstants.WM_SYSCOMMAND, NativeConstants.SC_MONITORPOWER, (int)WinApi.MonitorState.OFF);
var screenSaver = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.System), "scrnsave.scr");
Process.Start("explorer.exe", screenSaver);
}
+29 -22
View File
@@ -12,7 +12,6 @@ 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;
@@ -44,6 +43,7 @@ namespace ColorControl
private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
private static Mutex _mutex;
public static string MutexId;
public static MainForm _mainForm;
private static LoggingRule _loggingRule;
@@ -96,11 +96,11 @@ namespace ColorControl
Logger.Debug($"Result of setting SetHighDpiMode: {result}");
}
var mutexId = $"Global\\{typeof(MainForm).GUID}";
MutexId = $"Global\\{typeof(MainForm).GUID}";
var startUpParams = StartUpParams.Parse(args);
AppContext = new GlobalContext(Config, startUpParams, DataDir, _loggingRule, null, startTime, mutexId);
AppContext = new GlobalContext(Config, startUpParams, DataDir, _loggingRule, null, startTime, MutexId);
var host = CreateHostBuilder().Build();
ServiceProvider = host.Services;
@@ -168,6 +168,8 @@ namespace ColorControl
var winApiAdminService = ServiceProvider.GetRequiredService<WinApiAdminService>();
winApiAdminService.StopService();
}
await BlazorUiManager.Stop();
}
catch (Exception ex)
{
@@ -233,31 +235,43 @@ namespace ColorControl
public static async Task OpenNewUi()
{
if (await StartUiServer())
{
await Task.Delay(500);
}
var winApi = AppContext.ServiceProvider.GetRequiredService<WinApiAdminService>();
winApi.StartProcess(Blazor.GetCurrentUrl());
await OpenNewUi(false);
}
public static async Task OpenNewUiEmbedded()
{
await OpenNewUi(true);
}
public static async Task OpenNewUi(bool embedded)
{
if (await StartUiServer())
{
await Task.Delay(500);
await Task.Delay(1000);
}
BrowserWindow.CreateAndShow(Blazor.GetCurrentUrl());
if (BlazorUiManager.GetCurrentPort(Config) <= 0)
{
await OpenMainForm();
return;
}
if (embedded)
{
BrowserWindow.CreateAndShow(BlazorUiManager.GetCurrentUrl(Config));
}
else
{
var winApi = AppContext.ServiceProvider.GetRequiredService<WinApiAdminService>();
winApi.StartProcess(BlazorUiManager.GetCurrentUrl(Config));
}
}
public static async Task<bool> StartOrStopUiServer()
{
if (Config.UiType == UiType.WinForms)
{
var _ = Task.Run(() => UI.Blazor.Stop());
var _ = Task.Run(() => BlazorUiManager.Stop());
return true;
}
@@ -266,11 +280,6 @@ namespace ColorControl
public static async Task<bool> StartUiServer()
{
if (UI.Blazor.IsRunning(Config))
{
return false;
}
var backgroundService = ServiceProvider.GetRequiredService<ColorControlBackgroundService>();
if (backgroundService.PipeName != PipeUtils.MainPipe)
@@ -280,9 +289,7 @@ namespace ColorControl
await backgroundService.StartAsync(CancellationToken.None);
}
var _ = Task.Run(() => UI.Blazor.Start(Config));
return true;
return await BlazorUiManager.Start(Config);
}
private static void InitLogger(bool runAsService, bool runElevated)
+69 -77
View File
@@ -1,77 +1,69 @@
{
"type":"register",
"id":"register_0",
"payload":{
"forcePairing":false,
"pairingType":"PROMPT",
"client-key":"CLIENTKEYGOESHERE",
"manifest":{
"manifestVersion":1,
"appVersion":"1.1",
"signed":{
"created":"20140509",
"appId":"com.lge.test",
"vendorId":"com.lge",
"localizedAppNames":{
"":"LG Remote App",
"ko-KR":"리모컨 앱",
"zxx-XX":"ЛГ Rэмotэ AПП"
},
"localizedVendorNames":{
"":"LG Electronics"
},
"permissions":[
"TEST_SECURE",
"CONTROL_INPUT_TEXT",
"CONTROL_MOUSE_AND_KEYBOARD",
"READ_INSTALLED_APPS",
"READ_LGE_SDX",
"READ_NOTIFICATIONS",
"SEARCH",
"WRITE_SETTINGS",
"WRITE_NOTIFICATION_ALERT",
"CONTROL_POWER",
"READ_CURRENT_CHANNEL",
"READ_RUNNING_APPS",
"READ_UPDATE_INFO",
"UPDATE_FROM_REMOTE_APP",
"READ_LGE_TV_INPUT_EVENTS",
"READ_TV_CURRENT_TIME"
],
"serial":"2f930e2d2cfe083771f68e4fe7bb07"
},
"permissions":[
"LAUNCH",
"LAUNCH_WEBAPP",
"APP_TO_APP",
"CLOSE",
"TEST_OPEN",
"TEST_PROTECTED",
"CONTROL_AUDIO",
"CONTROL_DISPLAY",
"CONTROL_INPUT_JOYSTICK",
"CONTROL_INPUT_MEDIA_RECORDING",
"CONTROL_INPUT_MEDIA_PLAYBACK",
"CONTROL_INPUT_TV",
"CONTROL_POWER",
"CONTROL_TV_SCREEN",
"READ_APP_STATUS",
"READ_CURRENT_CHANNEL",
"READ_INPUT_DEVICE_LIST",
"READ_NETWORK_STATE",
"READ_RUNNING_APPS",
"READ_TV_CHANNEL_LIST",
"WRITE_NOTIFICATION_TOAST",
"READ_POWER_STATE",
"READ_COUNTRY_INFO",
"READ_SETTINGS"
],
"signatures":[
{
"signatureVersion":1,
"signature":"eyJhbGdvcml0aG0iOiJSU0EtU0hBMjU2Iiwia2V5SWQiOiJ0ZXN0LXNpZ25pbmctY2VydCIsInNpZ25hdHVyZVZlcnNpb24iOjF9.hrVRgjCwXVvE2OOSpDZ58hR+59aFNwYDyjQgKk3auukd7pcegmE2CzPCa0bJ0ZsRAcKkCTJrWo5iDzNhMBWRyaMOv5zWSrthlf7G128qvIlpMT0YNY+n/FaOHE73uLrS/g7swl3/qH/BGFG2Hu4RlL48eb3lLKqTt2xKHdCs6Cd4RMfJPYnzgvI4BNrFUKsjkcu+WD4OO2A27Pq1n50cMchmcaXadJhGrOqH5YmHdOCj5NSHzJYrsW0HPlpuAx/ECMeIZYDh6RMqaFM2DXzdKX9NmmyqzJ3o/0lkk/N97gfVRLW5hA29yeAwaCViZNCP8iC9aO0q9fQojoa7NQnAtw=="
}
]
}
}
}
{
"type": "register",
"id": "register_0",
"payload": {
"forcePairing": false,
"pairingType": "PROMPT",
"client-key": "CLIENTKEYGOESHERE",
"manifest": {
"manifestVersion": 1,
"appVersion": "1.0",
"signed": {
"appId": "com.colorcontrol.app",
"created": "20260426",
"localizedAppNames": {
"": "ColorControl"
},
"localizedVendorNames": {
"": "ColorControl"
},
"permissions": [
"CONTROL_POWER",
"READ_POWER_STATE",
"READ_CURRENT_CHANNEL",
"READ_RUNNING_APPS",
"READ_NETWORK_STATE",
"READ_INPUT_DEVICE_LIST",
"READ_TV_CHANNEL_LIST",
"READ_SETTINGS",
"READ_COUNTRY_INFO",
"WRITE_NOTIFICATION_TOAST",
"CONTROL_AUDIO",
"CONTROL_DISPLAY",
"CONTROL_INPUT_JOYSTICK",
"CONTROL_INPUT_MEDIA_PLAYBACK",
"CONTROL_INPUT_TV",
"CONTROL_TV_SCREEN",
"LAUNCH",
"LAUNCH_WEBAPP",
"APP_TO_APP",
"CLOSE"
],
"serial": "colorcontrol-webos26"
},
"permissions": [
"LAUNCH",
"LAUNCH_WEBAPP",
"APP_TO_APP",
"CLOSE",
"CONTROL_AUDIO",
"CONTROL_DISPLAY",
"CONTROL_INPUT_JOYSTICK",
"CONTROL_INPUT_MEDIA_PLAYBACK",
"CONTROL_INPUT_TV",
"CONTROL_POWER",
"CONTROL_TV_SCREEN",
"READ_APP_STATUS",
"READ_CURRENT_CHANNEL",
"READ_INPUT_DEVICE_LIST",
"READ_NETWORK_STATE",
"READ_RUNNING_APPS",
"READ_TV_CHANNEL_LIST",
"WRITE_NOTIFICATION_TOAST",
"READ_POWER_STATE",
"READ_COUNTRY_INFO",
"READ_SETTINGS"
]
}
}
}
@@ -0,0 +1,77 @@
{
"type":"register",
"id":"register_0",
"payload":{
"forcePairing":false,
"pairingType":"PROMPT",
"client-key":"CLIENTKEYGOESHERE",
"manifest":{
"manifestVersion":1,
"appVersion":"1.1",
"signed":{
"created":"20140509",
"appId":"com.lge.test",
"vendorId":"com.lge",
"localizedAppNames":{
"":"LG Remote App",
"ko-KR":"리모컨 앱",
"zxx-XX":"ЛГ Rэмotэ AПП"
},
"localizedVendorNames":{
"":"LG Electronics"
},
"permissions":[
"TEST_SECURE",
"CONTROL_INPUT_TEXT",
"CONTROL_MOUSE_AND_KEYBOARD",
"READ_INSTALLED_APPS",
"READ_LGE_SDX",
"READ_NOTIFICATIONS",
"SEARCH",
"WRITE_SETTINGS",
"WRITE_NOTIFICATION_ALERT",
"CONTROL_POWER",
"READ_CURRENT_CHANNEL",
"READ_RUNNING_APPS",
"READ_UPDATE_INFO",
"UPDATE_FROM_REMOTE_APP",
"READ_LGE_TV_INPUT_EVENTS",
"READ_TV_CURRENT_TIME"
],
"serial":"2f930e2d2cfe083771f68e4fe7bb07"
},
"permissions":[
"LAUNCH",
"LAUNCH_WEBAPP",
"APP_TO_APP",
"CLOSE",
"TEST_OPEN",
"TEST_PROTECTED",
"CONTROL_AUDIO",
"CONTROL_DISPLAY",
"CONTROL_INPUT_JOYSTICK",
"CONTROL_INPUT_MEDIA_RECORDING",
"CONTROL_INPUT_MEDIA_PLAYBACK",
"CONTROL_INPUT_TV",
"CONTROL_POWER",
"CONTROL_TV_SCREEN",
"READ_APP_STATUS",
"READ_CURRENT_CHANNEL",
"READ_INPUT_DEVICE_LIST",
"READ_NETWORK_STATE",
"READ_RUNNING_APPS",
"READ_TV_CHANNEL_LIST",
"WRITE_NOTIFICATION_TOAST",
"READ_POWER_STATE",
"READ_COUNTRY_INFO",
"READ_SETTINGS"
],
"signatures":[
{
"signatureVersion":1,
"signature":"eyJhbGdvcml0aG0iOiJSU0EtU0hBMjU2Iiwia2V5SWQiOiJ0ZXN0LXNpZ25pbmctY2VydCIsInNpZ25hdHVyZVZlcnNpb24iOjF9.hrVRgjCwXVvE2OOSpDZ58hR+59aFNwYDyjQgKk3auukd7pcegmE2CzPCa0bJ0ZsRAcKkCTJrWo5iDzNhMBWRyaMOv5zWSrthlf7G128qvIlpMT0YNY+n/FaOHE73uLrS/g7swl3/qH/BGFG2Hu4RlL48eb3lLKqTt2xKHdCs6Cd4RMfJPYnzgvI4BNrFUKsjkcu+WD4OO2A27Pq1n50cMchmcaXadJhGrOqH5YmHdOCj5NSHzJYrsW0HPlpuAx/ECMeIZYDh6RMqaFM2DXzdKX9NmmyqzJ3o/0lkk/N97gfVRLW5hA29yeAwaCViZNCP8iC9aO0q9fQojoa7NQnAtw=="
}
]
}
}
}
@@ -91,6 +91,11 @@ abstract class GraphicsService<T> : ServiceBase<T> where T : PresetBase, new()
currentDisplayConfig.Rotation = displayConfig.Rotation;
}
if (displayConfig.Connectivity != DisplayConnectivity.Unchanged)
{
currentDisplayConfig.Connectivity = displayConfig.Connectivity;
}
return CCD.SetDisplayConfig(displayName, currentDisplayConfig, updateRegistry);
}
@@ -2,7 +2,6 @@
using ColorControl.Shared.Contracts;
using ColorControl.Shared.EventDispatcher;
using ColorControl.Shared.Services;
using ColorControl.UI;
using NLog;
using System;
using System.Collections.Generic;
@@ -35,7 +34,7 @@ public class OptionsService
public Config GetConfig()
{
_globalContext.Config.AutoStart = _winApiService.TaskExists(Program.TS_TASKNAME);
_globalContext.Config.CurrentUiPort = Blazor.GetCurrentPort();
_globalContext.Config.CurrentUiPort = BlazorUiManager.GetCurrentPort(Program.Config);
return _globalContext.Config;
}
@@ -51,6 +50,14 @@ public class OptionsService
return true;
}
public bool SetCurrentUiSettings(int port, bool allowRemoteConnections)
{
_globalContext.Config.CurrentUiPort = port;
_globalContext.Config.CurrentUiAllowRemoteConnections = allowRemoteConnections;
return true;
}
public bool SaveSettings()
{
_serviceManager.Save();
+165 -165
View File
@@ -12,212 +12,212 @@ namespace ColorControl.Services.Common;
public class UpdateManager
{
private readonly GlobalContext _globalContext;
private readonly ServiceManager _serviceManager;
private readonly NotifyIconManager _notifyIconManager;
private readonly WinApiService _winApiService;
private readonly WinApiAdminService _winApiAdminService;
private readonly Config _config;
private readonly GlobalContext _globalContext;
private readonly ServiceManager _serviceManager;
private readonly NotifyIconManager _notifyIconManager;
private readonly WinApiService _winApiService;
private readonly WinApiAdminService _winApiAdminService;
private readonly Config _config;
public UpdateManager(GlobalContext globalContext, ServiceManager serviceManager, NotifyIconManager notifyIconManager, WinApiService winApiService, WinApiAdminService winApiAdminService)
{
_globalContext = globalContext;
_serviceManager = serviceManager;
_notifyIconManager = notifyIconManager;
_winApiService = winApiService;
_winApiAdminService = winApiAdminService;
_config = globalContext.Config;
}
public UpdateManager(GlobalContext globalContext, ServiceManager serviceManager, NotifyIconManager notifyIconManager, WinApiService winApiService, WinApiAdminService winApiAdminService)
{
_globalContext = globalContext;
_serviceManager = serviceManager;
_notifyIconManager = notifyIconManager;
_winApiService = winApiService;
_winApiAdminService = winApiAdminService;
_config = globalContext.Config;
}
private void trayIconBalloonTip_Clicked(object sender, EventArgs e)
{
if (_globalContext.UpdateInfo?.HtmlUrl != null)
{
_winApiAdminService.StartProcess(_globalContext.UpdateInfo?.HtmlUrl);
}
}
private void trayIconBalloonTip_Clicked(object sender, EventArgs e)
{
if (_globalContext.UpdateInfo?.HtmlUrl != null)
{
_winApiAdminService.StartProcess(_globalContext.UpdateInfo?.HtmlUrl);
}
}
public async Task CheckForUpdatesAndInstall()
{
var updateInfo = await CheckForUpdates();
public async Task CheckForUpdatesAndInstall()
{
var updateInfo = await CheckForUpdates();
if (!updateInfo.UpdateAvailable)
{
return;
}
if (!updateInfo.UpdateAvailable)
{
return;
}
_notifyIconManager.NotifyIcon.BalloonTipClicked += trayIconBalloonTip_Clicked;
var newVersion = updateInfo.NewVersionNumber;
_notifyIconManager.NotifyIcon.BalloonTipClicked += trayIconBalloonTip_Clicked;
var newVersion = updateInfo.NewVersionNumber;
if (!string.IsNullOrEmpty(updateInfo.DownloadUrl) && _winApiService.IsServiceRunning())
{
if (_config.AutoInstallUpdates)
{
await InstallUpdate();
if (!string.IsNullOrEmpty(updateInfo.DownloadUrl) && _winApiService.IsServiceRunning())
{
if (_config.AutoInstallUpdates)
{
await InstallUpdate();
return;
}
return;
}
if (_notifyIconManager.NotifyIcon.Visible)
{
_notifyIconManager.NotifyIcon.ShowBalloonTip(5000, "Update available", $"Version {newVersion} is available. Click on the Update-button to update", ToolTipIcon.Info);
}
else
{
MessageForms.InfoOk($"New version {newVersion} is available. Click on the Update-button to update", "Update available", $"https://github.com/Maassoft/ColorControl/releases/tag/v{newVersion}");
}
if (_notifyIconManager.NotifyIcon.Visible)
{
_notifyIconManager.NotifyIcon.ShowBalloonTip(5000, "Update available", $"Version {newVersion} is available. Click on the Update-button to update", ToolTipIcon.Info);
}
else
{
MessageForms.InfoOk($"New version {newVersion} is available. Click on the Update-button to update", "Update available", $"https://github.com/Maassoft/ColorControl/releases/tag/v{newVersion}");
}
return;
}
return;
}
if (_notifyIconManager.NotifyIcon.Visible)
{
_notifyIconManager.NotifyIcon.ShowBalloonTip(5000, "Update available", $"Version {newVersion} is available. Click to open the GitHub page", ToolTipIcon.Info);
}
else
{
MessageForms.InfoOk($"New version {newVersion} is available. Click on the Help-button to open the GitHub page.", "Update available", $"https://github.com/Maassoft/ColorControl/releases/tag/v{newVersion}");
}
}
if (_notifyIconManager.NotifyIcon.Visible)
{
_notifyIconManager.NotifyIcon.ShowBalloonTip(5000, "Update available", $"Version {newVersion} is available. Click to open the GitHub page", ToolTipIcon.Info);
}
else
{
MessageForms.InfoOk($"New version {newVersion} is available. Click on the Help-button to open the GitHub page.", "Update available", $"https://github.com/Maassoft/ColorControl/releases/tag/v{newVersion}");
}
}
public bool UpdateAvailable() => _globalContext.UpdateInfo?.UpdateAvailable == true;
public bool UpdateAvailable() => _globalContext.UpdateInfo?.UpdateAvailable == true;
public async Task<UpdateInfoDto> CheckForUpdates()
{
if (_globalContext.UpdateInfo != null)
{
return _globalContext.UpdateInfo;
}
public async Task<UpdateInfoDto> CheckForUpdates()
{
if (_globalContext.UpdateInfo != null)
{
return _globalContext.UpdateInfo;
}
//if (Debugger.IsAttached)
//{
// return _globalContext.UpdateInfo = new UpdateInfoDto
// {
// UpdateAvailable = true,
// NewVersionNumber = "10.0.0.2",
// DownloadUrl = "http://testurl"
// };
//}
//if (Debugger.IsAttached)
//{
// return _globalContext.UpdateInfo = new UpdateInfoDto
// {
// UpdateAvailable = true,
// NewVersionNumber = "10.0.0.2",
// DownloadUrl = "http://testurl"
// };
//}
if (Debugger.IsAttached)
{
return new UpdateInfoDto();
}
if (Debugger.IsAttached)
{
return new UpdateInfoDto();
}
var json = await Utils.GetRestJsonAsync("https://api.github.com/repos/maassoft/colorcontrol/releases/latest");
var json = await Utils.GetRestJsonAsync("https://api.github.com/repos/maassoft/colorcontrol/releases/latest");
if (json == null)
{
return new UpdateInfoDto();
}
if (json == null)
{
return new UpdateInfoDto();
}
_globalContext.UpdateInfo = GetUpdateInfo(json);
_globalContext.UpdateInfo = GetUpdateInfo(json);
return _globalContext.UpdateInfo;
}
return _globalContext.UpdateInfo;
}
private static UpdateInfoDto GetUpdateInfo(dynamic json)
{
var updateInfo = new UpdateInfoDto();
private static UpdateInfoDto GetUpdateInfo(dynamic json)
{
var updateInfo = new UpdateInfoDto();
if (json?.tag_name == null)
{
return updateInfo;
}
if (json?.tag_name == null)
{
return updateInfo;
}
var currentVersion = Application.ProductVersion;
var cvParts = currentVersion.Split(".");
var currentVersion = Application.ProductVersion;
var cvParts = currentVersion.Split(".");
var newVersion = (string)json.tag_name.Value.Substring(1);
var nvParts = newVersion.Split(".");
var newVersion = (string)json.tag_name.Value.Substring(1);
var nvParts = newVersion.Split(".");
bool CompareVersions()
{
var result = true;
var aNumberIsLarger = false;
bool CompareVersions()
{
var result = true;
var aNumberIsLarger = false;
for (var i = 0; i < nvParts.Length; i++)
{
var part = Utils.ParseInt(nvParts[i]);
var cvPart = Utils.ParseInt(cvParts[i]);
for (var i = 0; i < nvParts.Length; i++)
{
var part = Utils.ParseInt(nvParts[i]);
var cvPart = Utils.ParseInt(cvParts[i]);
if (part > cvPart)
{
aNumberIsLarger = true;
break;
}
if (part > cvPart)
{
aNumberIsLarger = true;
break;
}
if (part == cvPart)
{
continue;
}
if (part == cvPart)
{
continue;
}
result = false;
break;
}
result = false;
break;
}
return result && aNumberIsLarger;
}
return result && aNumberIsLarger;
}
updateInfo.NewVersionNumber = newVersion;
updateInfo.UpdateAvailable = nvParts.Length != cvParts.Length || CompareVersions();
updateInfo.NewVersionNumber = newVersion;
updateInfo.UpdateAvailable = nvParts.Length != cvParts.Length || CompareVersions();
updateInfo.HtmlUrl = json.html_url.Value;
updateInfo.HtmlUrl = json.html_url.Value;
if (json.assets != null)
{
var asset = json.assets[0];
updateInfo.DownloadUrl = asset.browser_download_url.Value;
}
if (json.assets != null)
{
var asset = json.assets[0];
updateInfo.DownloadUrl = asset.browser_download_url.Value;
}
return updateInfo;
}
return updateInfo;
}
public async Task<bool> InstallUpdate()
{
if (_globalContext.UpdateInfo?.DownloadUrl == null)
{
return false;
}
public async Task<bool> InstallUpdate()
{
if (_globalContext.UpdateInfo?.DownloadUrl == null)
{
return false;
}
if (Debugger.IsAttached)
{
await Task.Delay(5000);
return true;
}
if (Debugger.IsAttached)
{
await Task.Delay(5000);
return true;
}
var message = new SvcInstallUpdateMessage
{
DownloadUrl = _globalContext.UpdateInfo?.DownloadUrl,
ClientPath = new FileInfo(Application.ExecutablePath).Directory.FullName
};
var message = new SvcInstallUpdateMessage
{
DownloadUrl = _globalContext.UpdateInfo?.DownloadUrl,
ClientPath = new FileInfo(Application.ExecutablePath).Directory.FullName
};
var result = await PipeUtils.SendMessageAsync(message, 30000);
var result = await PipeUtils.SendMessageAsync(message, 30000);
if (result != null && !result.Result)
{
if (Program.IsMainFormOpened())
{
MessageForms.ErrorOk($"Error while updating: {result.ErrorMessage}");
}
if (result != null && !result.Result)
{
if (Program.IsMainFormOpened())
{
MessageForms.ErrorOk($"Error while updating: {result.ErrorMessage}");
}
return false;
}
return false;
}
await PipeUtils.SendMessageAsync(SvcMessageType.RestartAfterUpdate);
await PipeUtils.SendMessageAsync(SvcMessageType.RestartAfterUpdate);
if (Program.IsMainFormOpened() && MessageForms.QuestionYesNo("Update installed successfully. Do you want to restart the application?") == DialogResult.Yes)
{
Program.Restart();
}
if (Program.IsMainFormOpened() && MessageForms.QuestionYesNo("Update installed successfully. Do you want to restart the application?") == DialogResult.Yes)
{
Program.Restart();
}
return true;
}
return true;
}
public Task<bool> RestartAfterUpdate()
{
Program.Restart();
public Task<bool> RestartAfterUpdate()
{
Program.Restart();
return Task.FromResult(true);
}
return Task.FromResult(true);
}
}
@@ -337,7 +337,7 @@ namespace ColorControl.Services.GameLauncher
public string SelectPath()
{
var fileInfo = Utils.SelectFile();
var fileInfo = Utils.SelectFileFromNonUiThread(_globalContext.SynchronizationContext);
if (fileInfo == null)
{
+4 -1
View File
@@ -107,6 +107,8 @@ namespace ColorControl.Services.LG
public bool UseSecureConnection { get; set; } = true;
public bool UseNewHandshake { get; set; }
private List<string> _actionsForGameBar;
public List<string> ActionsOnGameBar
@@ -311,6 +313,7 @@ namespace ColorControl.Services.LG
private void UpdateOptions(LgDeviceOptions options)
{
UseSecureConnection = options.UseSecureConnection;
UseNewHandshake = options.UseNewHandshake;
TurnScreenOffOnScreenSaver = options.TurnScreenOffOnScreenSaver;
TurnScreenOnAfterScreenSaver = options.TurnScreenOnAfterScreenSaver;
ScreenSaverMinimalDuration = options.ScreenSaverMinimalDuration;
@@ -501,7 +504,7 @@ namespace ColorControl.Services.LG
try
{
await DisposeConnection();
_lgTvApi = await LgTvApi.CreateLgTvApi(IpAddress, retries, UseSecureConnection);
_lgTvApi = await LgTvApi.CreateLgTvApi(IpAddress, retries, UseSecureConnection, UseNewHandshake);
//Test();
//_lgTvApi.Test3();
+7 -7
View File
@@ -179,7 +179,7 @@
lblLgPresetConnectedDisplaysRegex.Location = new System.Drawing.Point(475, 404);
lblLgPresetConnectedDisplaysRegex.Margin = new System.Windows.Forms.Padding(6, 0, 6, 0);
lblLgPresetConnectedDisplaysRegex.Name = "lblLgPresetConnectedDisplaysRegex";
lblLgPresetConnectedDisplaysRegex.Size = new System.Drawing.Size(149, 15);
lblLgPresetConnectedDisplaysRegex.Size = new System.Drawing.Size(148, 15);
lblLgPresetConnectedDisplaysRegex.TabIndex = 67;
lblLgPresetConnectedDisplaysRegex.Text = "Connected Displays Regex:";
//
@@ -330,7 +330,7 @@
lblLgPresetExcludedProcesses.Location = new System.Drawing.Point(475, 370);
lblLgPresetExcludedProcesses.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0);
lblLgPresetExcludedProcesses.Name = "lblLgPresetExcludedProcesses";
lblLgPresetExcludedProcesses.Size = new System.Drawing.Size(112, 15);
lblLgPresetExcludedProcesses.Size = new System.Drawing.Size(111, 15);
lblLgPresetExcludedProcesses.TabIndex = 52;
lblLgPresetExcludedProcesses.Text = "Excluded processes:";
//
@@ -395,7 +395,7 @@
lblLgPresetTrigger.Location = new System.Drawing.Point(475, 277);
lblLgPresetTrigger.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0);
lblLgPresetTrigger.Name = "lblLgPresetTrigger";
lblLgPresetTrigger.Size = new System.Drawing.Size(46, 15);
lblLgPresetTrigger.Size = new System.Drawing.Size(47, 15);
lblLgPresetTrigger.TabIndex = 45;
lblLgPresetTrigger.Text = "Trigger:";
//
@@ -834,7 +834,7 @@
clbLgPower.Anchor = System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right;
clbLgPower.ColumnWidth = 240;
clbLgPower.FormattingEnabled = true;
clbLgPower.Items.AddRange(new object[] { "Power on after startup", "Power on after resume from standby", "Power off on shutdown", "Power off on standby", "Power off when screensaver activates", "Power on when screensaver deactivates", "Power on even after manual power off", "Allow triggers to be fired for this device", "Use Windows power settings to power on", "Use Windows power settings to power off", "Power off when locking the pc", "Power on when unlocking the pc", "Use secure connection" });
clbLgPower.Items.AddRange(new object[] { "Power on after startup", "Power on after resume from standby", "Power off on shutdown", "Power off on standby", "Power off when screensaver activates", "Power on when screensaver deactivates", "Power on even after manual power off", "Allow triggers to be fired for this device", "Use Windows power settings to power on", "Use Windows power settings to power off", "Power off when locking the pc", "Power on when unlocking the pc", "Use secure connection", "Enable webOS 26 compatibility" });
clbLgPower.Location = new System.Drawing.Point(6, 70);
clbLgPower.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
clbLgPower.MultiColumn = true;
@@ -860,19 +860,19 @@
mnuLgSettings.ImageScalingSize = new System.Drawing.Size(20, 20);
mnuLgSettings.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { miTestPowerOffOn, miNvSettings });
mnuLgSettings.Name = "mnuLgButtons";
mnuLgSettings.Size = new System.Drawing.Size(168, 48);
mnuLgSettings.Size = new System.Drawing.Size(169, 48);
//
// miTestPowerOffOn
//
miTestPowerOffOn.Name = "miTestPowerOffOn";
miTestPowerOffOn.Size = new System.Drawing.Size(167, 22);
miTestPowerOffOn.Size = new System.Drawing.Size(168, 22);
miTestPowerOffOn.Text = "Test power off/on";
miTestPowerOffOn.Click += miTestPowerOffOn_Click;
//
// miNvSettings
//
miNvSettings.Name = "miNvSettings";
miNvSettings.Size = new System.Drawing.Size(167, 22);
miNvSettings.Size = new System.Drawing.Size(168, 22);
miNvSettings.Text = "Settings";
miNvSettings.Click += miNvSettings_Click;
//
+8 -15
View File
@@ -321,11 +321,11 @@ namespace ColorControl.Services.LG
FormUtils.BuildComboBox(cbxLgPresetTrigger, PresetTriggerType.Reserved5);
if (!string.IsNullOrEmpty(_lgTabMessage))
{
MessageForms.WarningOk(_lgTabMessage);
_lgTabMessage = null;
}
//if (!string.IsNullOrEmpty(_lgTabMessage))
//{
// MessageForms.WarningOk(_lgTabMessage);
// _lgTabMessage = null;
//}
}
private void FillLgDevices()
@@ -349,16 +349,7 @@ namespace ColorControl.Services.LG
if (!devices.Any())
{
var message = "It seems there's no LG TV available! Please make sure it's connected to the same network as this PC.";
if (Visible /*tcMain.SelectedTab == tabLG*/)
{
MessageForms.WarningOk(message);
}
else
{
_lgTabMessage = message;
}
_lgTabMessage = "It seems there's no LG TV available! Please make sure it's connected to the same network as this PC.";
}
if (cbxLgApps.Items.Count == 0 && device != null)
@@ -396,6 +387,7 @@ namespace ColorControl.Services.LG
clbLgPower.SetItemChecked(10, device?.PowerOffAfterSessionLock ?? false);
clbLgPower.SetItemChecked(11, device?.PowerOnAfterSessionUnlock ?? false);
clbLgPower.SetItemChecked(12, device?.UseSecureConnection ?? false);
clbLgPower.SetItemChecked(13, device?.UseNewHandshake ?? false);
}
finally
{
@@ -674,6 +666,7 @@ You can also activate this option by using the Expert-button and selecting Wake-
device.PowerOffAfterSessionLock = clbLgPower.GetItemChecked(10);
device.PowerOnAfterSessionUnlock = clbLgPower.GetItemChecked(11);
device.UseSecureConnection = clbLgPower.GetItemChecked(12);
device.UseNewHandshake = clbLgPower.GetItemChecked(13);
if (e.Index == 12)
{
+2 -1
View File
@@ -1174,7 +1174,8 @@ namespace ColorControl.Services.LG
TriggersEnabled = d.TriggersEnabled,
TurnScreenOffOnScreenSaver = d.TurnScreenOffOnScreenSaver,
TurnScreenOnAfterScreenSaver = d.TurnScreenOnAfterScreenSaver,
UseSecureConnection = d.UseSecureConnection
UseSecureConnection = d.UseSecureConnection,
UseNewHandshake = d.UseNewHandshake,
},
IsConnected = d.IsConnected(),
IsSelected = d == SelectedDevice
@@ -15,7 +15,7 @@ namespace ColorControl.Services.NVIDIA
public string Name { get; private set; }
public string DisplayId { get; private set; }
public string DisplayId { get; set; }
public NvDisplayInfo(Display display, List<string> values, string infoLine, string name = null)
{
+1 -1
View File
@@ -1214,7 +1214,7 @@ namespace ColorControl.Services.NVIDIA
if (preset.IsDisplayPreset)
{
_nvService.SetHDRState(preset.Display, !_nvService.IsHDREnabled());
_nvService.SetHDRState(preset.Display, !NvService.IsHDREnabled(preset.Display));
UpdateDisplayInfoItems();
return;
+89 -33
View File
@@ -235,7 +235,7 @@ namespace ColorControl.Services.NVIDIA
}
}
public Display GetCurrentDisplay(bool checkDisplays = true)
public Display GetCurrentDisplay(bool checkDisplays = true, bool useFirstIfNoneSet = true)
{
if (checkDisplays && !HasDisplaysAttached())
{
@@ -243,7 +243,7 @@ namespace ColorControl.Services.NVIDIA
return null;
}
if (_currentDisplay == null)
if (_currentDisplay == null && useFirstIfNoneSet)
{
_currentDisplay = Display.GetDisplays()[0];
}
@@ -396,7 +396,7 @@ namespace ColorControl.Services.NVIDIA
var display = await WaitForDisplayAsync(preset);
if (display == null)
if (display == null && preset.DisplayConfig.Connectivity != DisplayConnectivity.Enabled)
{
var error = $"Cannot apply preset {preset.IdOrName} because the associated display is not active/found";
@@ -405,6 +405,9 @@ namespace ColorControl.Services.NVIDIA
return GenericBoolResult.FromError(error);
}
//ReadDDC(display);
//WriteDDC(display, 0, 0);
if (preset.DdcSettings.ApplyDdc)
{
var ddcResult = await ApplyDdcSettings(display, preset.DdcSettings);
@@ -414,7 +417,7 @@ namespace ColorControl.Services.NVIDIA
}
}
var hdrEnabled = IsHDREnabled();
var hdrEnabled = IsHDREnabled(display);
var newHdrEnabled = preset.applyHDR && (preset.HDREnabled || (preset.toggleHDR && !hdrEnabled));
var applyHdr = preset.applyHDR && (preset.toggleHDR || preset.HDREnabled != hdrEnabled);
@@ -427,11 +430,14 @@ namespace ColorControl.Services.NVIDIA
if ((preset.DisplayConfig.ApplyRefreshRate && preset.DisplayConfig.RefreshRate.MilliValue < timing.Extra.FrequencyInMillihertz) ||
(preset.DisplayConfig.ApplyResolution && preset.DisplayConfig.Resolution.ActiveWidth < timing.HorizontalVisible))
{
SetMode(preset.DisplayConfig, true);
SetMode(preset.DisplayConfig, true, displayId: preset.DisplayId);
}
}
SetColorData(display, preset.colorData);
if (!SetColorData(display, preset.colorData))
{
result.AddError("Could not apply color settings");
}
}
if (preset.applyHDR)
@@ -457,9 +463,9 @@ namespace ColorControl.Services.NVIDIA
}
}
if (preset.DisplayConfig.ApplyRefreshRate || preset.DisplayConfig.ApplyResolution)
if (preset.DisplayConfig.ApplyRefreshRate || preset.DisplayConfig.ApplyResolution || preset.DisplayConfig.IsPrimary != null || preset.DisplayConfig.Connectivity != DisplayConnectivity.Unchanged)
{
if (!SetMode(preset.DisplayConfig, true))
if (!SetMode(preset.DisplayConfig, true, displayId: preset.DisplayId))
{
result.AddError(WinError.GetMessage());
}
@@ -531,6 +537,8 @@ namespace ColorControl.Services.NVIDIA
PresetApplied();
GetDisplayInfos(false);
_currentDisplay = null;
return result;
}
@@ -1068,15 +1076,15 @@ namespace ColorControl.Services.NVIDIA
return SetMode(display.Name, resolution, refreshRate, updateRegistry);
}
public bool SetMode(DisplayConfig displayConfig, bool updateRegistry = false, NvPreset preset = null)
public bool SetMode(DisplayConfig displayConfig, bool updateRegistry = false, NvPreset preset = null, string displayId = null)
{
var display = preset == null ? GetCurrentDisplay() : GetPresetDisplay(preset);
if (display == null)
var display = preset == null ? GetCurrentDisplay(useFirstIfNoneSet: false) : GetPresetDisplay(preset);
if (display == null && displayId == null)
{
return false;
}
return SetMode(display.Name, displayConfig, updateRegistry);
return SetMode(display?.Name ?? displayId, displayConfig, updateRegistry);
}
public List<Rational> GetAvailableRefreshRatesV2(NvPreset preset = null)
@@ -1141,9 +1149,8 @@ namespace ColorControl.Services.NVIDIA
}
}
public bool IsHDREnabled(Display currentDisplay = null)
public static bool IsHDREnabled(Display display)
{
var display = currentDisplay ?? GetCurrentDisplay();
if (display == null)
{
return false;
@@ -1325,29 +1332,54 @@ 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;
// }
public void ReadDDC(Display display)
{
if (display == null)
{
return;
}
// byte i2cDeviceAddr = 0x37;
byte i2cDeviceAddr = 0x37;
// //short value = 8704;
//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 };
//var data = new byte[] { 0x84, 0x03, 0x10, 0x00, 0x64, 0xDD };
//var data = new byte[] { 0x84, 0x03, 0x15, 0x00, 0x2D, 0xDD };
// I2CInfoV3.FillDDCCIChecksum((byte)(i2cDeviceAddr << 1), [0x51], data);
var info = new I2CInfoV2(display.Output.OutputId, true, i2cDeviceAddr, [0x51], 256);
// var info = new I2CInfoV2(display.Output.OutputId, true, i2cDeviceAddr, [0x51], data);
var data = display.DisplayDevice.PhysicalGPU.ReadI2C(info);
// display.DisplayDevice.PhysicalGPU.WriteI2C(info);
//}
File.WriteAllBytes($"d:\\ddc\\lg_data.dat", data);
}
public void WriteDDC(Display display, byte vcpCode, uint value)
{
if (display == null)
{
return;
}
// Switch input
vcpCode = 0xF4;
value = 0x91;
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), [0x50], data);
var info = new I2CInfoV2(display.Output.OutputId, true, i2cDeviceAddr, [0x50], data);
display.DisplayDevice.PhysicalGPU.WriteI2C(info);
}
public GenericResult<VcpInfo> GetVcpInfo(string displayId, byte vcpCode)
{
@@ -1518,6 +1550,16 @@ namespace ColorControl.Services.NVIDIA
list.Add(displayInfo);
}
//foreach (var display in WindowsDisplayAPI.UnAttachedDisplay.GetUnAttachedDisplays())
//{
// var displayInfo = new NvDisplayInfo(null, null, null, display.DeviceName)
// {
// DisplayId = CCD.GetDisplayIdByPath(display.DevicePath)
// };
// list.Add(displayInfo);
//}
return list;
}
@@ -1537,6 +1579,12 @@ namespace ColorControl.Services.NVIDIA
foreach (var displayInfo in displayInfos)
{
var preset = GetPresetForDisplay(displayInfo, true, displayInfos.Count());
if (preset == null)
{
continue;
}
var values = preset.GetDisplayValues(_globalContext.Config);
displayInfo.Values = values;
@@ -1574,8 +1622,11 @@ namespace ColorControl.Services.NVIDIA
foreach (var displayInfo in displayInfos)
{
var preset = GetPresetForDisplay(displayInfo, true, displayInfos.Count());
preset.MonitorConnectionType = preset?.Display?.DisplayDevice?.ConnectionType ?? NvAPIWrapper.Native.GPU.MonitorConnectionType.Unknown;
list.Add(preset);
if (preset != null)
{
preset.MonitorConnectionType = preset?.Display?.DisplayDevice?.ConnectionType ?? NvAPIWrapper.Native.GPU.MonitorConnectionType.Unknown;
list.Add(preset);
}
}
return list;
@@ -1604,6 +1655,11 @@ namespace ColorControl.Services.NVIDIA
var display = displayInfo.Display;
if (display == null)
{
return new NvPreset { displayName = displayInfo.Name, DisplayId = displayInfo.DisplayId };
}
var isPrimaryDisplay = displayCount == 1 || display.DisplayDevice.DisplayId == DisplayDevice.GetGDIPrimaryDisplayDevice()?.DisplayId;
var preset = new NvPreset { Display = display, IsDisplayPreset = true, applyColorData = false, primaryDisplay = isPrimaryDisplay };
@@ -813,7 +813,7 @@ namespace ColorControl.Services.Samsung
new FieldDefinition
{
Label = "Service Menu - Advanced",
SubLabel = "If the service menu opened and you want to access the Advanced settings, click 'Next >' or click 'X' to skip",
SubLabel = "If the service menu opened and you want to access the Advanced settings, manually navigate to 'Advanced' first and then click 'Next >' or click 'X' to skip. You can also manually enter the unlock codes 0098 (for TV's) and 0038 (for monitors) while the 'Advanced' item is highlighted.",
FieldType = FieldType.Label
} }, okButtonText: "Next >");
+6 -15
View File
@@ -328,11 +328,11 @@ namespace ColorControl.Services.Samsung
FormUtils.BuildComboBox(cbxSamsungPresetTrigger, PresetTriggerType.Reserved5);
if (!string.IsNullOrEmpty(_tabMessage))
{
MessageForms.WarningOk(_tabMessage);
_tabMessage = null;
}
//if (!string.IsNullOrEmpty(_tabMessage))
//{
// MessageForms.WarningOk(_tabMessage);
// _tabMessage = null;
//}
}
private void FillSamsungDevices()
@@ -361,16 +361,7 @@ namespace ColorControl.Services.Samsung
if (!devices.Any())
{
var message = "It seems there's no Samsung TV available! Please make sure it's connected to the same network as this PC.";
if (Visible)
{
MessageForms.WarningOk(message);
}
else
{
_tabMessage = message;
}
_tabMessage = "It seems there's no Samsung TV available! Please make sure it's connected to the same network as this PC.";
}
//if (cbxSamsungApps.Items.Count == 0 && device != null)
@@ -88,9 +88,19 @@ namespace ColorControl.Svc
{
Logger.Error(ex, $"Error while handling message: {message}");
var result = SvcResultMessage.FromResult(false, ex.Message);
var resultJson = JsonConvert.SerializeObject(result);
ss.WriteString(resultJson);
if (!ex.Message.Contains("Pipe is broken"))
{
try
{
var result = SvcResultMessage.FromResult(false, ex.Message);
var resultJson = JsonConvert.SerializeObject(result);
ss.WriteString(resultJson);
}
catch (Exception ex2)
{
Logger.Error(ex2, $"Error while sending error message: {ex.Message}");
}
}
}
pipeServer.Close();
+1 -1
View File
@@ -16,6 +16,6 @@
d:DesignHeight="720" d:DesignWidth="1280" Loaded="Window_Loaded">
<DockPanel>
<wv2:WebView2 Name="webView" DefaultBackgroundColor="#212529"
Source="{Binding Url}" NavigationStarting="webView_NavigationStarting"/>
Source="{Binding Url}" NavigationStarting="webView_NavigationStarting" NavigationCompleted="webView_NavigationCompleted"/>
</DockPanel>
</src:BaseWindow>
+25 -8
View File
@@ -1,6 +1,5 @@
using ColorControl.Shared.Common;
using ColorControl.Shared.XForms;
using ColorControl.UI;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.ComponentModel;
@@ -46,16 +45,22 @@ public partial class BrowserWindow : BaseWindow
var reload = _window != null && _window._currentUrl != url;
//if (reload && _window != null)
//{
// _window.Close();
// _window = null;
//}
_window ??= Program.ServiceProvider.GetRequiredService<BrowserWindow>();
_window.Url = url;
_window._currentUrl = url;
if (reload)
{
_window.webView.Source = new Uri(url);
}
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;
@@ -112,11 +117,23 @@ public partial class BrowserWindow : BaseWindow
{
e.Cancel = true;
var port = Blazor.GetCurrentPort();
var port = BlazorUiManager.GetCurrentPort(Program.Config);
var newUrl = e.Uri.Replace(":0/", $":{port}/");
CreateAndShow(newUrl);
}
}
private void webView_NavigationCompleted(object sender, Microsoft.Web.WebView2.Core.CoreWebView2NavigationCompletedEventArgs e)
{
if (e.WebErrorStatus > 0)
{
var port = BlazorUiManager.GetCurrentPort(Program.Config);
var newUrl = $"http://localhost:{port}";
CreateAndShow(newUrl, false);
}
}
}
+247 -243
View File
@@ -17,306 +17,310 @@ namespace ColorControl.XForms;
public class CustomDisplay
{
public Display Display { get; set; }
public Display Display { get; set; }
public string DisplayName => Display.DisplayName;
public string DevicePath => Display.DevicePath;
public bool IsHDRSupportedAndEnabled()
{
var advancedColor = CCD.GetAdvancedColorSupportedAndEnabled(DisplayName);
public string DisplayName => Display.DisplayName;
public string DevicePath => Display.DevicePath;
public bool IsHDRSupportedAndEnabled()
{
var advancedColor = CCD.GetAdvancedColorSupportedAndEnabled(DisplayName);
return advancedColor.isSupported && advancedColor.isEnabled;
}
return advancedColor.isSupported && advancedColor.isEnabled;
}
public CustomDisplay(Display display)
{
Display = display;
}
public CustomDisplay(Display display)
{
Display = display;
}
public override string ToString()
{
return $"{CCD.GetDisplayInfo(Display.DisplayName)} on {Display.Adapter.DeviceName}";
}
public override string ToString()
{
return $"{CCD.GetDisplayInfo(Display.DisplayName)} on {Display.Adapter.DeviceName}";
}
}
public class ColorPointModel : BaseViewModel
{
private double _x;
public double X { get => _x; set { _x = value; OnPropertyChanged(); } }
private double _x;
public double X { get => _x; set { _x = value; OnPropertyChanged(); } }
private double _y;
public double Y { get => _y; set { _y = value; OnPropertyChanged(); } }
private double _y;
public double Y { get => _y; set { _y = value; OnPropertyChanged(); } }
public ColorPointModel()
{
public ColorPointModel()
{
}
}
public ColorPointModel(CIExy coord)
{
X = coord.x;
Y = coord.y;
}
public ColorPointModel(CIExy coord)
{
X = coord.x;
Y = coord.y;
}
}
internal class ColorProfileViewModel : BaseViewModel
{
[StringLength(30)]
public string Description { get; set; }
[StringLength(30)]
public string Description { get; set; }
public ColorPointModel RedPoint { get; set; } = new ColorPointModel(RgbPrimaries.sRGB.Red);
public ColorPointModel GreenPoint { get; set; } = new ColorPointModel(RgbPrimaries.sRGB.Green);
public ColorPointModel BluePoint { get; set; } = new ColorPointModel(RgbPrimaries.sRGB.Blue);
public ColorPointModel WhitePoint { get; set; } = new ColorPointModel(RgbPrimaries.sRGB.White);
public Dictionary<DisplayPrimariesSource, string> PrimariesSources { get; } = Utils.EnumToDictionary<DisplayPrimariesSource>();
public DisplayPrimariesSource PrimariesSource { get; set; } = DisplayPrimariesSource.EDID;
public Dictionary<ColorGamut, string> ColorGamuts { get; } = Utils.EnumToDictionary<ColorGamut>();
public ColorGamut ColorGamut { get; set; } = ColorGamut.Native;
public Dictionary<SDRTransferFunction, string> SDRTransferFunctions { get; } = Utils.EnumToDictionary<SDRTransferFunction>();
public SDRTransferFunction SDRTransferFunction { get; set; } = SDRTransferFunction.PurePower;
[Range(0.1, 10)]
public double CustomGamma { get; set; } = 2.20;
[Range(0, 10)]
public double MinCLL { get; set; } = 0;
[Range(11, 10000)]
public double MaxCLL { get; set; } = 1000;
[Range(0, 10)]
public double BlackLuminance { get; set; } = 0;
[Range(11, 10000)]
public double WhiteLuminance { get; set; } = 1000;
[Range(0, 10)]
public double SDRMinBrightness { get; set; } = 0;
[Range(11, 10000)]
public double SDRMaxBrightness { get; set; } = 100;
[Range(-50, 50)]
public double SDRBrightnessBoost { get; set; }
public ColorPointModel RedPoint { get; set; } = new ColorPointModel(RgbPrimaries.sRGB.Red);
public ColorPointModel GreenPoint { get; set; } = new ColorPointModel(RgbPrimaries.sRGB.Green);
public ColorPointModel BluePoint { get; set; } = new ColorPointModel(RgbPrimaries.sRGB.Blue);
public ColorPointModel WhitePoint { get; set; } = new ColorPointModel(RgbPrimaries.sRGB.White);
public Dictionary<DisplayPrimariesSource, string> PrimariesSources { get; } = Utils.EnumToDictionary<DisplayPrimariesSource>();
public DisplayPrimariesSource PrimariesSource { get; set; } = DisplayPrimariesSource.EDID;
public Dictionary<ColorGamut, string> ColorGamuts { get; } = Utils.EnumToDictionary<ColorGamut>();
public ColorGamut ColorGamut { get; set; } = ColorGamut.Native;
public Dictionary<SDRTransferFunction, string> SDRTransferFunctions { get; } = Utils.EnumToDictionary<SDRTransferFunction>();
public SDRTransferFunction SDRTransferFunction { get; set; } = SDRTransferFunction.PurePower;
[Range(0.1, 10)]
public double CustomGamma { get; set; } = 2.20;
[Range(0, 10)]
public double MinCLL { get; set; } = 0;
[Range(11, 10000)]
public double MaxCLL { get; set; } = 1000;
[Range(0, 10)]
public double BlackLuminance { get; set; } = 0;
[Range(11, 10000)]
public double WhiteLuminance { get; set; } = 1000;
[Range(0, 10)]
public double SDRMinBrightness { get; set; } = 0;
[Range(11, 10000)]
public double SDRMaxBrightness { get; set; } = 100;
[Range(-50, 50)]
public double SDRBrightnessBoost { get; set; }
[Range(-200, 200)]
public double ShadowDetailBoost { get; set; } = 0;
[Range(0, 100)]
public double ShadowDetailRange { get; set; } = 0;
public CustomDisplay SelectedDisplay { get; set; }
public IEnumerable<CustomDisplay> Displays { get; set; }
public CustomDisplay SelectedDisplay { get; set; }
public IEnumerable<CustomDisplay> Displays { get; set; }
public string SelectedDisplayName => SelectedDisplay?.DisplayName;
public string SelectedDisplayName => SelectedDisplay?.DisplayName;
public ObservableCollection<string> ExistingProfiles { get; set; }
public string SelectedExistingProfile { get; set; } = CreateANewProfile;
public string NewProfileName { get; set; }
public string ProfileName => SelectedExistingProfile == CreateANewProfile ? NewProfileName : SelectedExistingProfile;
public bool IsHDR { get; set; } = true;
public Dictionary<SaveOption, string> SaveOptions { get; } = Utils.EnumToDictionary<SaveOption>();
public SaveOption SaveOption { get; set; } = SaveOption.InstallAndSetAsDefault;
public ObservableCollection<string> ExistingProfiles { get; set; }
public string SelectedExistingProfile { get; set; } = CreateANewProfile;
public string NewProfileName { get; set; }
public string ProfileName => SelectedExistingProfile == CreateANewProfile ? NewProfileName : SelectedExistingProfile;
public bool IsHDR { get; set; } = true;
public Dictionary<SaveOption, string> SaveOptions { get; } = Utils.EnumToDictionary<SaveOption>();
public SaveOption SaveOption { get; set; } = SaveOption.InstallAndSetAsDefault;
public bool SDRSettingsEnabled { get; set; }
public Visibility VisibilityHDRSettings => IsHDR ? Visibility.Visible : Visibility.Collapsed;
public bool IsLoadEnabled { get; set; }
public bool SetMinMaxTml { get; set; } = true;
public bool PrimariesEnabled { get; set; } = true;
public bool SDRSettingsEnabled { get; set; }
public Visibility VisibilityHDRSettings => IsHDR ? Visibility.Visible : Visibility.Collapsed;
public bool IsLoadEnabled { get; set; }
public bool SetMinMaxTml { get; set; } = true;
public bool PrimariesEnabled { get; set; } = true;
[Range(400, 10000)]
public double ToneMappingFromLuminance { get; set; } = 400;
[Range(400, 10000)]
public double ToneMappingToLuminance { get; set; } = 400;
[Range(400, 10000)]
public double ToneMappingFromLuminance { get; set; } = 400;
[Range(400, 10000)]
public double ToneMappingToLuminance { get; set; } = 400;
public bool ToneMappingSettingsEnabled { get; set; }
public bool ToneMappingSettingsEnabled { get; set; }
public bool BrightnessBoostSettingsEnabled { get; set; }
public bool BrightnessBoostSettingsEnabled { get; set; }
[Range(.04, 25)]
public double HdrBrightnessMultiplier { get; set; } = 1;
[Range(.04, 25)]
public double HdrBrightnessMultiplier { get; set; } = 1;
[Range(.04, 25)]
public double HdrGammaMultiplier { get; set; } = 1;
[Range(.04, 25)]
public double HdrGammaMultiplier { get; set; } = 1;
public override string this[string columnName]
{
get
{
var baseResult = base[columnName];
public override string this[string columnName]
{
get
{
var baseResult = base[columnName];
if (columnName == nameof(SelectedDisplay))
{
UpdateDisplay();
}
else if (columnName == nameof(SDRTransferFunction))
{
if (SDRTransferFunction == SDRTransferFunction.PurePower)
{
SDRSettingsEnabled = true;
ToneMappingSettingsEnabled = false;
BrightnessBoostSettingsEnabled = true;
OnPropertyChanged(nameof(SDRSettingsEnabled));
OnPropertyChanged(nameof(ToneMappingSettingsEnabled));
OnPropertyChanged(nameof(BrightnessBoostSettingsEnabled));
}
else if (SDRTransferFunction == SDRTransferFunction.ToneMappedPiecewise)
{
if (columnName == nameof(SelectedDisplay))
{
UpdateDisplay();
}
else if (columnName == nameof(SDRTransferFunction))
{
if (SDRTransferFunction == SDRTransferFunction.PurePower)
{
SDRSettingsEnabled = true;
ToneMappingSettingsEnabled = false;
BrightnessBoostSettingsEnabled = true;
OnPropertyChanged(nameof(SDRSettingsEnabled));
OnPropertyChanged(nameof(ToneMappingSettingsEnabled));
OnPropertyChanged(nameof(BrightnessBoostSettingsEnabled));
}
else if (SDRTransferFunction == SDRTransferFunction.ToneMappedPiecewise)
{
ToneMappingSettingsEnabled = true;
SDRSettingsEnabled = false;
BrightnessBoostSettingsEnabled = false;
OnPropertyChanged(nameof(ToneMappingSettingsEnabled));
OnPropertyChanged(nameof(SDRSettingsEnabled));
OnPropertyChanged(nameof(BrightnessBoostSettingsEnabled));
}
else
{
ToneMappingSettingsEnabled = false;
SDRSettingsEnabled = false;
BrightnessBoostSettingsEnabled = true;
OnPropertyChanged(nameof(ToneMappingSettingsEnabled));
OnPropertyChanged(nameof(SDRSettingsEnabled));
OnPropertyChanged(nameof(BrightnessBoostSettingsEnabled));
}
ToneMappingSettingsEnabled = true;
SDRSettingsEnabled = false;
BrightnessBoostSettingsEnabled = false;
OnPropertyChanged(nameof(ToneMappingSettingsEnabled));
OnPropertyChanged(nameof(SDRSettingsEnabled));
OnPropertyChanged(nameof(BrightnessBoostSettingsEnabled));
}
else
{
ToneMappingSettingsEnabled = false;
SDRSettingsEnabled = false;
BrightnessBoostSettingsEnabled = true;
OnPropertyChanged(nameof(ToneMappingSettingsEnabled));
OnPropertyChanged(nameof(SDRSettingsEnabled));
OnPropertyChanged(nameof(BrightnessBoostSettingsEnabled));
}
}
else if (columnName == nameof(SelectedExistingProfile))
{
IsLoadEnabled = SelectedExistingProfile != CreateANewProfile;
OnPropertyChanged(nameof(IsLoadEnabled));
}
else if (columnName == nameof(PrimariesSource))
{
PrimariesEnabled = PrimariesSource == DisplayPrimariesSource.Custom;
OnPropertyChanged(nameof(PrimariesEnabled));
UpdateDisplay();
}
}
else if (columnName == nameof(SelectedExistingProfile))
{
IsLoadEnabled = SelectedExistingProfile != CreateANewProfile;
OnPropertyChanged(nameof(IsLoadEnabled));
}
else if (columnName == nameof(PrimariesSource))
{
PrimariesEnabled = PrimariesSource == DisplayPrimariesSource.Custom;
OnPropertyChanged(nameof(PrimariesEnabled));
UpdateDisplay();
}
return baseResult;
}
}
return baseResult;
}
}
public override bool PreValidate()
{
return base.PreValidate() && Displays.Any();
}
public override bool PreValidate()
{
return base.PreValidate() && Displays.Any();
}
public const string CreateANewProfile = "<create a new profile>";
public const string CreateANewProfile = "<create a new profile>";
public ColorProfileViewModel(bool isHDR = true)
{
IsHDR = isHDR;
public ColorProfileViewModel(bool isHDR = true)
{
IsHDR = isHDR;
RefreshDisplays();
RefreshDisplays();
ExistingProfiles = new ObservableCollection<string> { CreateANewProfile };
NewProfileName = $"CC_{(isHDR ? "HDR" : "SDR")}_profile_{DateTime.Now:yyyyMMddHHmmss}.icm";
Description = $"CC_{(isHDR ? "HDR" : "SDR")}";
ExistingProfiles = new ObservableCollection<string> { CreateANewProfile };
NewProfileName = $"CC_{(isHDR ? "HDR" : "SDR")}_profile_{DateTime.Now:yyyyMMddHHmmss}.icm";
Description = $"CC_{(isHDR ? "HDR" : "SDR")}";
if (Displays.Any())
{
SelectedDisplay = Displays.First();
if (Displays.Any())
{
SelectedDisplay = Displays.First();
UpdateDisplay();
}
}
UpdateDisplay();
}
}
public void RefreshDisplays()
{
Displays = Display.GetDisplays().Select(d => new CustomDisplay(d))
.Where(d => IsHDR == d.IsHDRSupportedAndEnabled())
.ToList();
}
public void RefreshDisplays()
{
Displays = Display.GetDisplays().Select(d => new CustomDisplay(d))
.Where(d => IsHDR == d.IsHDRSupportedAndEnabled())
.ToList();
}
public void Update()
{
foreach (var property in GetType().GetProperties().Where(p => !new[] { "Display", "PrimariesSource" }.Any(s => p.Name.Contains(s))))
{
OnPropertyChanged(property.Name);
}
}
public void Update()
{
foreach (var property in GetType().GetProperties().Where(p => !new[] { "Display", "PrimariesSource" }.Any(s => p.Name.Contains(s))))
{
OnPropertyChanged(property.Name);
}
}
private void UpdateDisplay()
{
if (SelectedDisplay == null)
{
return;
}
private void UpdateDisplay()
{
if (SelectedDisplay == null)
{
return;
}
var colorParams = CCD.GetColorParams(SelectedDisplay.DisplayName);
var colorParams = CCD.GetColorParams(SelectedDisplay.DisplayName);
if (colorParams.MaxLuminance > 0)
{
if (PrimariesSource == DisplayPrimariesSource.Windows)
{
var divider = colorParams.RedPointX <= 1 << 10 ? 1 << 10 : 1 << 20;
if (colorParams.MaxLuminance > 0)
{
if (PrimariesSource == DisplayPrimariesSource.Windows)
{
var divider = colorParams.RedPointX <= 1 << 10 ? 1 << 10 : 1 << 20;
RedPoint.X = (double)colorParams.RedPointX / divider;
RedPoint.Y = (double)colorParams.RedPointY / divider;
GreenPoint.X = (double)colorParams.GreenPointX / divider;
GreenPoint.Y = (double)colorParams.GreenPointY / divider;
BluePoint.X = (double)colorParams.BluePointX / divider;
BluePoint.Y = (double)colorParams.BluePointY / divider;
WhitePoint.X = (double)colorParams.WhitePointX / divider;
WhitePoint.Y = (double)colorParams.WhitePointY / divider;
}
RedPoint.X = (double)colorParams.RedPointX / divider;
RedPoint.Y = (double)colorParams.RedPointY / divider;
GreenPoint.X = (double)colorParams.GreenPointX / divider;
GreenPoint.Y = (double)colorParams.GreenPointY / divider;
BluePoint.X = (double)colorParams.BluePointX / divider;
BluePoint.Y = (double)colorParams.BluePointY / divider;
WhitePoint.X = (double)colorParams.WhitePointX / divider;
WhitePoint.Y = (double)colorParams.WhitePointY / divider;
}
WhiteLuminance = (double)colorParams.MaxLuminance / 10000;
OnPropertyChanged(nameof(WhiteLuminance));
BlackLuminance = (double)colorParams.MinLuminance / 10000;
OnPropertyChanged(nameof(BlackLuminance));
MinCLL = BlackLuminance;
OnPropertyChanged(nameof(MinCLL));
MaxCLL = WhiteLuminance;
OnPropertyChanged(nameof(MaxCLL));
}
WhiteLuminance = (double)colorParams.MaxLuminance / 10000;
OnPropertyChanged(nameof(WhiteLuminance));
BlackLuminance = (double)colorParams.MinLuminance / 10000;
OnPropertyChanged(nameof(BlackLuminance));
MinCLL = BlackLuminance;
OnPropertyChanged(nameof(MinCLL));
MaxCLL = WhiteLuminance;
OnPropertyChanged(nameof(MaxCLL));
}
if (PrimariesSource == DisplayPrimariesSource.EDID)
{
GetEDID();
}
if (PrimariesSource == DisplayPrimariesSource.EDID)
{
GetEDID();
}
if (!CCD.GetUsePerUserDisplayProfiles(SelectedDisplay.DisplayName))
{
if (MessageForms.QuestionYesNo("To be able to select/activate a color profile, user specific settings have to be enabled. Do you want to enable this?") == DialogResult.Yes)
{
CCD.SetUsePerUserDisplayProfiles(SelectedDisplay.DisplayName, true);
}
}
if (!CCD.GetUsePerUserDisplayProfiles(SelectedDisplay.DisplayName))
{
if (MessageForms.QuestionYesNo("To be able to select/activate a color profile, user specific settings have to be enabled. Do you want to enable this?") == DialogResult.Yes)
{
CCD.SetUsePerUserDisplayProfiles(SelectedDisplay.DisplayName, true);
}
}
ExistingProfiles = new ObservableCollection<string> { CreateANewProfile };
ExistingProfiles = new ObservableCollection<string> { CreateANewProfile };
var profiles = CCD.GetDisplayColorProfiles(SelectedDisplay.DisplayName);
var profiles = CCD.GetDisplayColorProfiles(SelectedDisplay.DisplayName);
foreach (var profile in profiles)
{
ExistingProfiles.Add(profile);
}
}
foreach (var profile in profiles)
{
ExistingProfiles.Add(profile);
}
}
private void GetEDID()
{
if (SelectedDisplay == null)
{
return;
}
private void GetEDID()
{
if (SelectedDisplay == null)
{
return;
}
var path = SelectedDisplay.DevicePath;
var path = SelectedDisplay.DevicePath;
var edid = ColorProfileService.GetEDIDInternal(path);
var primaries = edid?.DisplayParameters?.ChromaticityCoordinates;
var edid = ColorProfileService.GetEDIDInternal(path);
var primaries = edid?.DisplayParameters?.ChromaticityCoordinates;
if (primaries == null)
{
return;
}
if (primaries == null)
{
return;
}
RedPoint.X = primaries.RedX;
RedPoint.Y = primaries.RedY;
GreenPoint.X = primaries.GreenX;
GreenPoint.Y = primaries.GreenY;
BluePoint.X = primaries.BlueX;
BluePoint.Y = primaries.BlueY;
WhitePoint.X = primaries.WhiteX;
WhitePoint.Y = primaries.WhiteY;
}
RedPoint.X = primaries.RedX;
RedPoint.Y = primaries.RedY;
GreenPoint.X = primaries.GreenX;
GreenPoint.Y = primaries.GreenY;
BluePoint.X = primaries.BlueX;
BluePoint.Y = primaries.BlueY;
WhitePoint.X = primaries.WhiteX;
WhitePoint.Y = primaries.WhiteY;
}
internal RgbPrimaries GetDevicePrimaries()
{
return new RgbPrimaries(
new CIExy { x = RedPoint.X, y = RedPoint.Y },
new CIExy { x = GreenPoint.X, y = GreenPoint.Y },
new CIExy { x = BluePoint.X, y = BluePoint.Y },
new CIExy { x = WhitePoint.X, y = WhitePoint.Y });
}
internal RgbPrimaries GetDevicePrimaries()
{
return new RgbPrimaries(
new CIExy { x = RedPoint.X, y = RedPoint.Y },
new CIExy { x = GreenPoint.X, y = GreenPoint.Y },
new CIExy { x = BluePoint.X, y = BluePoint.Y },
new CIExy { x = WhitePoint.X, y = WhitePoint.Y });
}
}
+9 -5
View File
@@ -52,8 +52,8 @@
<TextBox Grid.Column="1" HorizontalAlignment="Left" Margin="412,236,0,0" TextWrapping="Wrap" Text="{Binding WhiteLuminance, NotifyOnValidationError=True, ValidatesOnDataErrors=True}" VerticalAlignment="Top" Width="120" Name="tbMaxLuminance" Height="18" InputScope="Number"/>
</Grid>
</GroupBox>
<GroupBox Header="Advanced HDR Settings" Padding="6" Height="199" Width="734" HorizontalAlignment="Left" Visibility="{Binding VisibilityHDRSettings}">
<Grid Height="150">
<GroupBox Header="Advanced HDR Settings" Padding="6" Height="216" Width="734" HorizontalAlignment="Left" Visibility="{Binding VisibilityHDRSettings}">
<Grid Height="187" VerticalAlignment="Top">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="179*"/>
<ColumnDefinition Width="735*"/>
@@ -74,11 +74,15 @@
<Label Content="HDR Luminance to convert (nits):" HorizontalAlignment="Left" Margin="194,94,0,0" VerticalAlignment="Top" Width="211" Height="26" Grid.Column="1"/>
<TextBox IsEnabled="{Binding ToneMappingSettingsEnabled}" Grid.Column="1" HorizontalAlignment="Left" Margin="405,98,0,0" TextWrapping="Wrap" Text="{Binding ToneMappingToLuminance, NotifyOnValidationError=True, ValidatesOnDataErrors=True}" VerticalAlignment="Top" Width="120" x:Name="tbNitsTo" Height="18" InputScope="Number"/>
<Label Content="Brightness multiplier:" HorizontalAlignment="Left" Margin="-5,120,0,4" Width="208" Grid.ColumnSpan="2"/>
<Label Content="Brightness multiplier:" HorizontalAlignment="Left" Margin="-5,120,0,4" Width="208" Grid.ColumnSpan="2" VerticalAlignment="Top"/>
<TextBox IsEnabled="{Binding ToneMappingSettingsEnabled}" Grid.Column="1" HorizontalAlignment="Left" Margin="69,128,0,0" TextWrapping="Wrap" Text="{Binding HdrBrightnessMultiplier, NotifyOnValidationError=True, ValidatesOnDataErrors=True}" VerticalAlignment="Top" Width="120" x:Name="tbhdrbrightness" Height="18" InputScope="Number"/>
<Label Content="Gamma multiplier:" HorizontalAlignment="Left" Margin="194,124,0,0" Width="211" Grid.Column="1"/>
<Label Content="Gamma multiplier:" HorizontalAlignment="Left" Margin="194,124,0,0" Width="211" Grid.Column="1" VerticalAlignment="Top"/>
<TextBox IsEnabled="{Binding ToneMappingSettingsEnabled}" Grid.Column="1" HorizontalAlignment="Left" Margin="405,128,0,0" TextWrapping="Wrap" Text="{Binding HdrGammaMultiplier, NotifyOnValidationError=True, ValidatesOnDataErrors=True}" VerticalAlignment="Top" Width="120" x:Name="tbhdrgamma" Height="18" InputScope="Number"/>
<Label Content="Shadow detail boost (%):" HorizontalAlignment="Left" Margin="-5,148,0,0" Width="208" Grid.ColumnSpan="2" VerticalAlignment="Top"/>
<TextBox IsEnabled="{Binding BrightnessBoostSettingsEnabled}" Grid.Column="1" HorizontalAlignment="Left" Margin="69,156,0,0" TextWrapping="Wrap" Text="{Binding ShadowDetailBoost, NotifyOnValidationError=True, ValidatesOnDataErrors=True}" VerticalAlignment="Top" Width="120" x:Name="tbShadowDetailBoost" Height="18" InputScope="Number"/>
<Label Content="Shadow detail range (%):" HorizontalAlignment="Left" Margin="194,152,0,0" Width="211" Grid.Column="1" VerticalAlignment="Top"/>
<TextBox IsEnabled="{Binding BrightnessBoostSettingsEnabled}" Grid.Column="1" HorizontalAlignment="Left" Margin="405,156,0,0" TextWrapping="Wrap" Text="{Binding ShadowDetailRange, NotifyOnValidationError=True, ValidatesOnDataErrors=True}" VerticalAlignment="Top" Width="120" x:Name="tbShadowDetailRange" Height="18" InputScope="Number"/>
</Grid>
</GroupBox>
@@ -94,7 +98,7 @@
<TextBox Grid.Column="1" HorizontalAlignment="Left" Margin="400,10,0,0" TextWrapping="Wrap" Text="{Binding MaxCLL, NotifyOnValidationError=True, ValidatesOnDataErrors=True}" VerticalAlignment="Top" Width="120" x:Name="tbMinLuminance_Copy1" Height="18" InputScope="Number"/>
</Grid>
</GroupBox>
<GroupBox DockPanel.Dock="Right" Margin="0,0,0,0" Header="Generate Options" Padding="6" Height="225" Width="733" HorizontalAlignment="Left">
<GroupBox DockPanel.Dock="Right" Margin="0,0,0,0" Header="Generate Options" Padding="6" Height="227" Width="733" HorizontalAlignment="Left">
<Grid Height="194" Margin="-2,0,2,0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="8*"/>
@@ -113,6 +113,8 @@ namespace ColorControl.XForms
SDRMaxBrightness = _viewModel.SDRMaxBrightness,
SDRTransferFunction = _viewModel.SDRTransferFunction,
SDRBrightnessBoost = _viewModel.SDRBrightnessBoost,
ShadowDetailBoost = _viewModel.ShadowDetailBoost,
ShadowDetailRange = _viewModel.ShadowDetailRange,
ColorGamut = _viewModel.ColorGamut,
Gamma = _viewModel.CustomGamma,
DevicePrimaries = _viewModel.GetDevicePrimaries(),
@@ -208,6 +210,8 @@ namespace ColorControl.XForms
_viewModel.SDRMinBrightness = profileProperties.SDRMinBrightness;
_viewModel.SDRMaxBrightness = profileProperties.SDRMaxBrightness;
_viewModel.SDRBrightnessBoost = profileProperties.SDRBrightnessBoost;
_viewModel.ShadowDetailBoost = profileProperties.ShadowDetailBoost;
_viewModel.ShadowDetailRange = profileProperties.ShadowDetailRange;
_viewModel.MinCLL = profileProperties.MinCLL;
_viewModel.MaxCLL = profileProperties.MaxCLL;
+35 -16
View File
@@ -24,19 +24,21 @@ namespace LgTv
private string _ip;
private string _currentPairKey;
private string webSocketUri;
private string _registerJson;
protected static readonly NLog.Logger Logger = NLog.LogManager.GetCurrentClassLogger();
public static async Task<LgTvApi> CreateLgTvApi(string ip, int retries = 1, bool useSecureWs = true)
public static async Task<LgTvApi> CreateLgTvApi(string ip, int retries = 1, bool useSecureWs = true, bool useNewHandshake = false)
{
var instance = new LgTvApi(ip, useSecureWs);
var instance = new LgTvApi(ip, useSecureWs, useNewHandshake);
return await instance.Connect(retries);
}
private LgTvApi(string ip, bool useSecureWs)
private LgTvApi(string ip, bool useSecureWs, bool useNewHandshake)
{
webSocketUri = useSecureWs ? $"wss://{ip}:3001" : $"ws://{ip}:3000";
_registerJson = Utils.GetResourceFile(useNewHandshake ? "LG_register.json" : "LG_register_legacy.json");
_ip = ip;
_connection = new LgTvApiCoreCws();
_keyStore = new ClientKeyStore(ip);
@@ -68,26 +70,43 @@ namespace LgTv
return _ip;
}
public async Task MakeHandShake()
public async Task<bool> MakeHandShake()
{
var registerJson = Utils.GetResourceFile("LG_register.json");
_currentPairKey = _keyStore.GetClientKey();
if (!string.IsNullOrWhiteSpace(_currentPairKey) && !_currentPairKey.All(k => k == '\0') && _keyStore.HasValidHandShake(registerJson))
if (!string.IsNullOrWhiteSpace(_currentPairKey) && !_currentPairKey.All(k => k == '\0') && _keyStore.HasValidHandShake(_registerJson))
{
var key = registerJson.Replace("CLIENTKEYGOESHERE", _currentPairKey);
var conn = await _connection.SendCommandAsync(key);
_keyStore.SaveClientKey((string)conn.clientKey);
return;
var key = _registerJson.Replace("CLIENTKEYGOESHERE", _currentPairKey);
try
{
var conn = await _connection.SendCommandAsync(key);
ArgumentNullException.ThrowIfNull(conn);
_keyStore.SaveClientKey((string)conn.clientKey);
return true;
}
catch (Exception)
{
// Re-register below
}
}
var registerJsonRemovedKey = registerJson.Replace("CLIENTKEYGOESHERE", string.Empty);
dynamic result = await _connection.SendCommandAsync(registerJsonRemovedKey);
_keyStore.SaveClientKey(result.clientKey);
_keyStore.SaveHandShake(registerJson);
var registerJsonRemovedKey = _registerJson.Replace("CLIENTKEYGOESHERE", string.Empty);
try
{
var result = await _connection.SendCommandAsync(registerJsonRemovedKey);
if (result?.clientKey == null)
{
throw new InvalidOperationException("No valid client key received");
}
_keyStore.SaveClientKey(result.clientKey);
_keyStore.SaveHandShake(_registerJson);
return true;
}
catch (Exception)
{
return false;
}
}
public async Task<bool> Connect()
{
var ctx = _connection.Connect(new Uri(webSocketUri));
+1 -1
View File
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net9.0-windows</TargetFramework>
<TargetFramework>net10.0-windows</TargetFramework>
<Nullable>enable</Nullable>
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
</PropertyGroup>
+4 -4
View File
@@ -121,7 +121,7 @@ namespace MHC2Gen
var value = (double)i / lutSizeDiv;
value = ApplyGain(gain, value, i, lutSizeDiv);
value = ApplyShadowDetailBoost(shadowDetailBoost, shadowDetailRange, value, i);
value = ApplyShadowDetailBoost(shadowDetailBoost, shadowDetailRange, value, i, false);
RegammaLUT[c, i] = value * amplifier;
}
@@ -140,14 +140,14 @@ namespace MHC2Gen
return value;
}
public static double ApplyShadowDetailBoost(double boost, double range, double value, int lutIndex)
public static double ApplyShadowDetailBoost(double boost, double range, double value, int lutIndex, bool piecewiseAverage = true)
{
if (boost != 0 && lutIndex < range)
{
var correctedBoost = (boost * (range - lutIndex)) / range;
// Average between sSRGB and Piecewise gamma
if (true)
// Average between sRGB and Piecewise gamma
if (piecewiseAverage)
{
var piecewiseValue = (double)lutIndex / 1023;
+1 -1
View File
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net9.0-windows</TargetFramework>
<TargetFramework>net10.0-windows</TargetFramework>
<Nullable>enable</Nullable>
<Platforms>x64</Platforms>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
+3 -3
View File
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net9.0-windows</TargetFramework>
<TargetFramework>net10.0-windows</TargetFramework>
<UseWPF>true</UseWPF>
<RootNamespace>DJ</RootNamespace>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
@@ -18,8 +18,8 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="NLog" Version="5.3.4" />
<PackageReference Include="System.Reactive" Version="6.0.1" />
<PackageReference Include="NLog" Version="6.1.2" />
<PackageReference Include="System.Reactive" Version="6.1.0" />
</ItemGroup>
<ItemGroup>
+1 -5
View File
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net9.0-windows</TargetFramework>
<TargetFramework>net10.0-windows</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<UseWindowsForms>true</UseWindowsForms>
<Nullable>enable</Nullable>
@@ -13,10 +13,6 @@
<PathMap Condition="'$(Configuration)' == 'Release'">$(MSBuildThisFileDirectory)=.\</PathMap>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="System.Resources.Extensions" Version="9.0.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Shared\Shared.csproj" />
</ItemGroup>
+134 -5
View File
@@ -7776,23 +7776,23 @@
<HexValue>0x00000000</HexValue>
</CustomSettingValue>
<CustomSettingValue>
<UserfriendlyName>Enabled (Low Quality) (0x06)</UserfriendlyName>
<UserfriendlyName>Enabled via driver (No Debanding) (0x06)</UserfriendlyName>
<HexValue>0x00000006</HexValue>
</CustomSettingValue>
<CustomSettingValue>
<UserfriendlyName>Enabled (Medium Quality) (0x0A)</UserfriendlyName>
<UserfriendlyName>Enabled via driver (Medium Debanding) (0x0A)</UserfriendlyName>
<HexValue>0x0000000A</HexValue>
</CustomSettingValue>
<CustomSettingValue>
<UserfriendlyName>Enabled (VeryHigh Quality) (0x02)</UserfriendlyName>
<UserfriendlyName>Enabled via driver (VeryHigh Debanding) (0x02)</UserfriendlyName>
<HexValue>0x00000002</HexValue>
</CustomSettingValue>
<CustomSettingValue>
<UserfriendlyName>Enable + Indicator (0x03)</UserfriendlyName>
<UserfriendlyName>Enabled + Indicator (VeryHigh) (0x03)</UserfriendlyName>
<HexValue>0x00000003</HexValue>
</CustomSettingValue>
<CustomSettingValue>
<UserfriendlyName>Enable + Indicator + Debug (0x23)</UserfriendlyName>
<UserfriendlyName>Enabled + Indicator + Debug (VeryHigh) (0x23)</UserfriendlyName>
<HexValue>0x00000023</HexValue>
</CustomSettingValue>
</SettingValues>
@@ -7906,6 +7906,26 @@
<GroupName>8 - Extra</GroupName>
<MinRequiredDriverVersion>0</MinRequiredDriverVersion>
<SettingValues>
<CustomSettingValue>
<UserfriendlyName>N/A</UserfriendlyName>
<HexValue>0x00000000</HexValue>
</CustomSettingValue>
<CustomSettingValue>
<UserfriendlyName>Preset A</UserfriendlyName>
<HexValue>0x00000001</HexValue>
</CustomSettingValue>
<CustomSettingValue>
<UserfriendlyName>Preset B</UserfriendlyName>
<HexValue>0x00000002</HexValue>
</CustomSettingValue>
<CustomSettingValue>
<UserfriendlyName>Preset C</UserfriendlyName>
<HexValue>0x00000003</HexValue>
</CustomSettingValue>
<CustomSettingValue>
<UserfriendlyName>Preset D</UserfriendlyName>
<HexValue>0x00000004</HexValue>
</CustomSettingValue>
<CustomSettingValue>
<UserfriendlyName>Preset E</UserfriendlyName>
<HexValue>0x00000005</HexValue>
@@ -7914,6 +7934,18 @@
<UserfriendlyName>Preset F</UserfriendlyName>
<HexValue>0x00000006</HexValue>
</CustomSettingValue>
<CustomSettingValue>
<UserfriendlyName>Preset G (unused)</UserfriendlyName>
<HexValue>0x00000007</HexValue>
</CustomSettingValue>
<CustomSettingValue>
<UserfriendlyName>Preset H (unused)</UserfriendlyName>
<HexValue>0x00000008</HexValue>
</CustomSettingValue>
<CustomSettingValue>
<UserfriendlyName>Preset I (unused)</UserfriendlyName>
<HexValue>0x00000009</HexValue>
</CustomSettingValue>
<CustomSettingValue>
<UserfriendlyName>Preset J</UserfriendlyName>
<HexValue>0x0000000A</HexValue>
@@ -7922,6 +7954,103 @@
<UserfriendlyName>Preset K</UserfriendlyName>
<HexValue>0x0000000B</HexValue>
</CustomSettingValue>
<CustomSettingValue>
<UserfriendlyName>Preset L</UserfriendlyName>
<HexValue>0x0000000C</HexValue>
</CustomSettingValue>
<CustomSettingValue>
<UserfriendlyName>Preset M</UserfriendlyName>
<HexValue>0x0000000D</HexValue>
</CustomSettingValue>
<CustomSettingValue>
<UserfriendlyName>Preset N (unused)</UserfriendlyName>
<HexValue>0x0000000E</HexValue>
</CustomSettingValue>
<CustomSettingValue>
<UserfriendlyName>Preset O (unused)</UserfriendlyName>
<HexValue>0x0000000F</HexValue>
</CustomSettingValue>
<CustomSettingValue>
<UserfriendlyName>Always use latest</UserfriendlyName>
<HexValue>0x00FFFFFF</HexValue>
</CustomSettingValue>
</SettingValues>
<SettingMasks/>
</CustomSetting>
<CustomSetting>
<UserfriendlyName>DLSS-RR - Forced Preset Letter</UserfriendlyName>
<HexSettingID>0x10E41DF7</HexSettingID>
<GroupName>5 - Common</GroupName>
<MinRequiredDriverVersion>0</MinRequiredDriverVersion>
<SettingValues>
<CustomSettingValue>
<UserfriendlyName>N/A</UserfriendlyName>
<HexValue>0x00000000</HexValue>
</CustomSettingValue>
<CustomSettingValue>
<UserfriendlyName>Preset A</UserfriendlyName>
<HexValue>0x00000001</HexValue>
</CustomSettingValue>
<CustomSettingValue>
<UserfriendlyName>Preset B</UserfriendlyName>
<HexValue>0x00000002</HexValue>
</CustomSettingValue>
<CustomSettingValue>
<UserfriendlyName>Preset C</UserfriendlyName>
<HexValue>0x00000003</HexValue>
</CustomSettingValue>
<CustomSettingValue>
<UserfriendlyName>Preset D</UserfriendlyName>
<HexValue>0x00000004</HexValue>
</CustomSettingValue>
<CustomSettingValue>
<UserfriendlyName>Preset E</UserfriendlyName>
<HexValue>0x00000005</HexValue>
</CustomSettingValue>
<CustomSettingValue>
<UserfriendlyName>Preset F (unused)</UserfriendlyName>
<HexValue>0x00000006</HexValue>
</CustomSettingValue>
<CustomSettingValue>
<UserfriendlyName>Preset G (unused)</UserfriendlyName>
<HexValue>0x00000007</HexValue>
</CustomSettingValue>
<CustomSettingValue>
<UserfriendlyName>Preset H (unused)</UserfriendlyName>
<HexValue>0x00000008</HexValue>
</CustomSettingValue>
<CustomSettingValue>
<UserfriendlyName>Preset I (unused)</UserfriendlyName>
<HexValue>0x00000009</HexValue>
</CustomSettingValue>
<CustomSettingValue>
<UserfriendlyName>Preset J (unused)</UserfriendlyName>
<HexValue>0x0000000A</HexValue>
</CustomSettingValue>
<CustomSettingValue>
<UserfriendlyName>Preset K (unused)</UserfriendlyName>
<HexValue>0x0000000B</HexValue>
</CustomSettingValue>
<CustomSettingValue>
<UserfriendlyName>Preset L (unused)</UserfriendlyName>
<HexValue>0x0000000C</HexValue>
</CustomSettingValue>
<CustomSettingValue>
<UserfriendlyName>Preset M (unused)</UserfriendlyName>
<HexValue>0x0000000D</HexValue>
</CustomSettingValue>
<CustomSettingValue>
<UserfriendlyName>Preset N (unused)</UserfriendlyName>
<HexValue>0x0000000E</HexValue>
</CustomSettingValue>
<CustomSettingValue>
<UserfriendlyName>Preset O (unused)</UserfriendlyName>
<HexValue>0x0000000F</HexValue>
</CustomSettingValue>
<CustomSettingValue>
<UserfriendlyName>Always use latest</UserfriendlyName>
<HexValue>0x00FFFFFF</HexValue>
</CustomSettingValue>
</SettingValues>
<SettingMasks/>
</CustomSetting>
+1 -1
View File
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net9.0-windows</TargetFramework>
<TargetFramework>net10.0-windows</TargetFramework>
<Version>0.8.1.106</Version>
<Company>falahati.net</Company>
<Description>NvAPIWrapper is a .Net wrapper for NVIDIA public API</Description>
+2 -2
View File
@@ -5,8 +5,8 @@ Easily change NVIDIA and AMD display settings, control LG and Samsung tv's, Game
Just extract the .zip file in a folder of your preference. Run ColorControl.exe to start the application.
Requires .NET 9 with the following runtimes:
* .NET Desktop Runtime 9
* .NET ASP.NET Core Runtime 9
* .NET Desktop Runtime 10
* .NET ASP.NET Core Runtime 10
ColorControl stores its settings in the following folder:
* C:\Users\username\AppData\Roaming\Maassoft\ColorControl
+2
View File
@@ -21,6 +21,7 @@ namespace ColorControl.Shared.Common
public DateTime StartTime { get; private set; }
public string MutexId { get; private set; }
public nint MainHandle { get; set; }
public int MainThreadId { get; private set; }
public IServiceProvider ServiceProvider { get; set; }
public string ApplicationTitle { get; private set; }
@@ -37,6 +38,7 @@ namespace ColorControl.Shared.Common
ServiceProvider = serviceProvider;
StartTime = startTime;
MutexId = mutexId;
MainThreadId = Environment.CurrentManagedThreadId;
CurrentContext = this;
+62 -47
View File
@@ -50,46 +50,6 @@ These methods are available:
The best and suggested method to provide this is via a Windows Service. Only when installing the service a User Account Control window may popup.";
public static Bitmap SubPixelShift(Bitmap bitmap)
{
Bitmap bitmap2 = (Bitmap)bitmap.Clone();
int shift = -1;
bool shiftRed = true;
bool shiftGreen = true;
bool shiftBlue = false;
for (int i = 0; i < bitmap.Width; i++)
{
for (int j = 0; j < bitmap.Height; j++)
{
Color pixel = bitmap.GetPixel(i, j);
if (shift > 0 && i < bitmap.Width - shift)
{
Color pixel2 = bitmap2.GetPixel(i + shift, j);
Color pixel3 = Color.FromArgb(shiftRed ? pixel.R : pixel2.R, shiftGreen ? pixel.G : pixel2.G, shiftBlue ? pixel.B : pixel2.B);
bitmap2.SetPixel(i + shift, j, pixel3);
}
else if (i + shift >= 0)
{
Color pixel2 = bitmap2.GetPixel(i + shift, j);
Color pixel3 = Color.FromArgb(shiftRed ? pixel.R : pixel2.R, shiftGreen ? pixel.G : pixel2.G, shiftBlue ? pixel.B : pixel2.B);
bitmap2.SetPixel(i + shift, j, pixel3);
}
}
}
return bitmap2;
//bitmap2.Save("d:\\ss_shifted.bmp");
}
public static string GetDataPath(string subDir = "")
{
var path = Directory.GetParent(Application.UserAppDataPath).FullName;
@@ -465,22 +425,77 @@ The best and suggested method to provide this is via a Windows Service. Only whe
public static FileInfo SelectFile(string ext = "*.exe", string filter = "Application EXE Name|*.exe|Application Absolute Path|*.exe")
{
var openDialog = new System.Windows.Forms.OpenFileDialog();
openDialog.DefaultExt = ext;
openDialog.Filter = filter;
var openDialog = new OpenFileDialog
{
DefaultExt = ext,
Filter = filter
};
if (openDialog.ShowDialog() == DialogResult.OK)
{
return new FileInfo(openDialog.FileName);
//if (openDialog.FilterIndex == 2)
// applicationName = openDialog.FileName;
//return applicationName;
}
return null;
}
public static FileInfo SelectFileFromNonUiThread(SynchronizationContext synchronizationContext, string ext = "*.exe", string filter = "Application EXE Name|*.exe|Application Absolute Path|*.exe")
{
var completionSource = new TaskCompletionSource<FileInfo>();
var workerThread = new Thread(() =>
{
// Post back to the main thread
synchronizationContext?.Post(_ =>
{
var openDialog = new OpenFileDialog
{
DefaultExt = ext,
Filter = filter
};
if (openDialog.ShowDialog() == DialogResult.OK)
{
completionSource.SetResult(new FileInfo(openDialog.FileName));
}
else
{
completionSource.SetResult(null);
}
}, null);
});
workerThread.Start();
completionSource.Task.Wait();
return completionSource.Task.Result;
}
public static void ExecuteOnMainThread(Action action)
{
if (GlobalContext.CurrentContext.MainThreadId == Environment.CurrentManagedThreadId)
{
action();
return;
}
var completionSource = new TaskCompletionSource();
var workerThread = new Thread(() =>
{
// Post back to the main thread
GlobalContext.CurrentContext.SynchronizationContext?.Post(_ =>
{
action();
completionSource.SetResult();
}, null);
});
workerThread.Start();
completionSource.Task.Wait();
}
public static bool NormEquals(this string str1, string str2)
{
return str1.Trim().Replace(" ", string.Empty).Equals(str2.Trim().Replace(" ", string.Empty), StringComparison.OrdinalIgnoreCase);
+4 -2
View File
@@ -61,9 +61,11 @@ namespace ColorControl.Shared.Contracts
public bool DisableErrorPopupWhenApplyingPreset { get; set; }
public UiType UiType { get; set; }
public int UiPort { get; set; }
public bool UiAllowRemoteConnections { get; set; }
[JsonIgnore]
public int CurrentUiPort { get; set; }
public bool UiAllowRemoteConnections { get; set; }
[JsonIgnore]
public bool CurrentUiAllowRemoteConnections { get; set; }
public Config()
{
@@ -83,7 +85,7 @@ namespace ColorControl.Shared.Contracts
ElevationMethod = ElevationMethod.None;
ElevationMethodAsked = false;
ProcessMonitorPollingInterval = 1000;
UiType = UiType.WebEmbedded;
UiType = UiType.WinForms;
UiPort = 0; // Let .NET decide
UiAllowRemoteConnections = false;
LgPresetsSortState = new ListViewSortState();
+2
View File
@@ -12,6 +12,7 @@ public class DisplayConfig
public DisplayConfigScaling Scaling { get; set; }
public DisplayConfigRotation Rotation { get; set; }
public bool? IsPrimary { get; set; }
public DisplayConnectivity Connectivity { get; set; }
public DisplayConfig()
{
@@ -30,6 +31,7 @@ public class DisplayConfig
Scaling = displayConfig.Scaling;
Rotation = displayConfig.Rotation;
IsPrimary = displayConfig.IsPrimary;
Connectivity = displayConfig.Connectivity;
}
public string GetResolutionDesc()
+8
View File
@@ -0,0 +1,8 @@
namespace ColorControl.Shared.Contracts;
public enum DisplayConnectivity
{
Unchanged = 0,
Enabled = 1,
Disabled = 2
}
+2 -2
View File
@@ -21,5 +21,5 @@ public class LgDeviceOptions
public int HDMIPortNumber { get; set; }
public bool UseSecureConnection { get; set; } = true;
}
public bool UseNewHandshake { get; set; }
}
+4 -6
View File
@@ -149,7 +149,8 @@ namespace ColorControl.Shared.Contracts.NVIDIA
applyDithering = keepChanges && applyDithering || (ditheringEnabled != currentSettings.ditheringEnabled || ditheringMode != currentSettings.ditheringMode || ditheringBits != currentSettings.ditheringBits);
applyHDR = keepChanges && applyHDR || (HDREnabled != currentSettings.HDREnabled || toggleHDR || SDRBrightness != currentSettings.SDRBrightness || HdrSettings.OutputMode != currentSettings.HdrSettings.OutputMode);
DisplayConfig.ApplyResolution = keepChanges && DisplayConfig.ApplyResolution || (DisplayConfig.Resolution.IsDifferent(currentSettings.DisplayConfig.Resolution) ||
DisplayConfig.Scaling != currentSettings.DisplayConfig.Scaling || DisplayConfig.Rotation != currentSettings.DisplayConfig.Rotation || DisplayConfig.IsPrimary != null && DisplayConfig.IsPrimary != currentSettings.DisplayConfig.IsPrimary);
DisplayConfig.Scaling != currentSettings.DisplayConfig.Scaling || DisplayConfig.Rotation != currentSettings.DisplayConfig.Rotation || DisplayConfig.IsPrimary != null && DisplayConfig.IsPrimary != currentSettings.DisplayConfig.IsPrimary ||
DisplayConfig.Connectivity != DisplayConnectivity.Unchanged);
DisplayConfig.ApplyRefreshRate = keepChanges && DisplayConfig.ApplyRefreshRate || !DisplayConfig.RefreshRate.Equals(currentSettings.DisplayConfig.RefreshRate);
applyOther = keepChanges && applyOther || (scaling != currentSettings.scaling || ColorProfileSettings.ProfileName != currentSettings.ColorProfileSettings.ProfileName);
applyDriverSettings = keepChanges && applyDriverSettings || driverSettings.Any(s => !currentSettings.driverSettings.Any(o => o.Key == s.Key && o.Value == s.Value));
@@ -216,11 +217,8 @@ namespace ColorControl.Shared.Contracts.NVIDIA
MonitorConnectionType = preset.MonitorConnectionType;
var trigger = preset.Triggers.FirstOrDefault();
if (trigger != null)
{
UpdateTrigger(trigger.Trigger, trigger.Conditions, trigger.IncludedProcessesAsString, trigger.ExcludedProcessesAsString, trigger.ConnectedDisplaysRegex);
}
UpdateTriggers(preset.Triggers);
Steps.Clear();
Steps.AddRange(preset.Steps);
}
+23 -6
View File
@@ -166,6 +166,19 @@ public class PresetTrigger
{
return processes.Contains("*") ? "all processes" : string.Join(", ", processes);
}
internal void Update(PresetTrigger updatedTrigger)
{
Trigger = updatedTrigger.Trigger;
Conditions = updatedTrigger.Conditions;
IncludedProcesses ??= [];
ExcludedProcesses ??= [];
Utils.ParseWords(IncludedProcesses, updatedTrigger.IncludedProcessesAsString);
Utils.ParseWords(ExcludedProcesses, updatedTrigger.ExcludedProcessesAsString);
ConnectedDisplaysRegex = updatedTrigger.ConnectedDisplaysRegex;
}
}
public class PresetTriggerContext
@@ -282,16 +295,20 @@ public abstract class PresetBase
trigger.ConnectedDisplaysRegex = connectedDisplaysRegex;
}
public PresetTrigger AddTrigger()
public void UpdateTriggers(List<PresetTrigger> updatedTriggers)
{
if (!Triggers.Any())
{
Triggers.Add(new PresetTrigger());
}
Triggers.Clear();
return Triggers.First();
foreach (var updatedTrigger in updatedTriggers)
{
var newTrigger = new PresetTrigger();
newTrigger.Update(updatedTrigger);
Triggers.Add(newTrigger);
}
}
public void SetSteps(List<string> steps)
{
Steps = steps;
+6 -6
View File
@@ -2,17 +2,17 @@
public static class SamsungFactoryMenuPresets
{
public static readonly SamsungPreset Preset1 = new("ServiceMenuStep1", null, ["KEY_INFO", "KEY_FACTORY:2000"]);
public static readonly SamsungPreset PresetAdvanced = new("ServiceMenuAdvanced", null, ["KEY_UP", "KEY_0", "KEY_0", "KEY_9", "KEY_8"]);
public static readonly SamsungPreset PresetAdvancedMonitor = new("ServiceMenuAdvancedMonitor", null, ["KEY_UP", "KEY_0", "KEY_0", "KEY_3", "KEY_8"]);
public static readonly SamsungPreset Exit = new("ServiceMenuStep3", null, ["KEY_FACTORY:1500", "KEY_FACTORY"]);
public static readonly SamsungPreset Preset1 = new("ServiceMenuStep1", null, ["KEY_INFO:600", "KEY_FACTORY:3000"]);
public static readonly SamsungPreset PresetAdvanced = new("ServiceMenuAdvanced", null, ["KEY_0:800", "KEY_0:800", "KEY_9:800", "KEY_8:800"]);
public static readonly SamsungPreset PresetAdvancedMonitor = new("ServiceMenuAdvancedMonitor", null, ["KEY_0:800", "KEY_0:800", "KEY_3:800", "KEY_8:800"]);
public static readonly SamsungPreset Exit = new("ServiceMenuStep3", null, ["KEY_FACTORY:2500", "KEY_FACTORY"]);
public static readonly SamsungPreset KeyFactory = new("ServiceMenuKeyFactory", null, ["KEY_FACTORY"]);
}
public static class SamsungHospitalityMenuPresets
{
public static readonly SamsungPreset Preset1 = new("ServiceMenuStep1", null, ["KEY_MUTE:600", "KEY_1:600", "KEY_1:600", "KEY_9:600", "KEY_ENTER:2000"]);
public static readonly SamsungPreset Preset2 = new("ServiceMenuStep2", null, ["KEY_1", "KEY_2", "KEY_3", "KEY_4", "KEY_UP"]);
public static readonly SamsungPreset Preset1 = new("ServiceMenuStep1", null, ["KEY_MUTE:800", "KEY_1:800", "KEY_1:800", "KEY_9:800", "KEY_ENTER:3000"]);
public static readonly SamsungPreset Preset2 = new("ServiceMenuStep2", null, ["KEY_1:800", "KEY_2:800", "KEY_3:800", "KEY_4:800", "KEY_UP"]);
public static readonly SamsungPreset Preset3 = new("ServiceMenuStep3", null, ["KEY_VOLUP:5100:Press", "KEY_VOLUP:500:Release"]);
}
+69 -8
View File
@@ -175,6 +175,49 @@ namespace ColorControl.Shared.Native
var sourceIndex = pathsArray[pathIndex].sourceInfo.modeInfoIdx;
var targetIndex = pathsArray[pathIndex].targetInfo.modeInfoIdx;
if (displayConfig.Connectivity == DisplayConnectivity.Disabled)
{
var newPaths = pathsArray.Where((p, i) => i != pathIndex).ToArray();
var newModes = modesArray.Where((m, i) => i != sourceIndex && i != targetIndex).ToArray();
for (var i = 0; i < newPaths.Length; i++)
{
var sourceDiff = 0U;
var targetDiff = 0U;
if (newPaths[i].sourceInfo.modeInfoIdx > sourceIndex)
{
sourceDiff++;
}
if (newPaths[i].sourceInfo.modeInfoIdx > targetIndex)
{
sourceDiff++;
}
if (newPaths[i].targetInfo.modeInfoIdx > sourceIndex)
{
targetDiff++;
}
if (newPaths[i].targetInfo.modeInfoIdx > targetIndex)
{
targetDiff++;
}
newPaths[i].sourceInfo.modeInfoIdx -= sourceDiff;
newPaths[i].targetInfo.modeInfoIdx -= targetDiff;
}
var flags = SdcFlags.Apply | SdcFlags.UseSuppliedDisplayConfig | SdcFlags.AllowChanges;
if (updateRegistry)
{
flags |= SdcFlags.SaveToDatabase;
}
err = NativeMethods.SetDisplayConfig(pathCount - 1, newPaths, modeCount - 2, newModes, flags);
return err == 0;
}
var resolution = displayConfig.Resolution;
var refreshRate = displayConfig.RefreshRate;
@@ -322,7 +365,6 @@ namespace ColorControl.Shared.Native
return NativeMethods.UninstallColorProfileW(null, name, true);
}
private static bool MinRequiredVersion = true;
public static bool AddDisplayColorProfile(string displayName, string profileName, bool setAsDefault = true, bool isHdrColorProfile = false)
@@ -1061,6 +1103,31 @@ namespace ColorControl.Shared.Native
return true;
}
if (displayName.StartsWith("\\\\"))
{
return MatchDisplayNameByDeviceName(path, displayName);
}
// get display name
var info = new DISPLAYCONFIG_TARGET_DEVICE_NAME();
info.header.type = DISPLAYCONFIG_DEVICE_INFO_TYPE.DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_NAME;
info.header.size = Marshal.SizeOf<DISPLAYCONFIG_TARGET_DEVICE_NAME>();
info.header.adapterId = path.targetInfo.adapterId;
info.header.id = path.targetInfo.id;
var err = NativeMethods.DisplayConfigGetDeviceInfo(ref info);
if (err != NativeConstants.ERROR_SUCCESS)
{
return false;
}
var deviceName = CCD.GetDisplayIdByPath(info.monitorDevicePath);
return deviceName == displayName;
}
private static bool MatchDisplayNameByDeviceName(DisplayConfigPathInfo path, string deviceName)
{
// get display name
var info = new DISPLAYCONFIG_SOURCE_DEVICE_NAME();
info.header.type = DISPLAYCONFIG_DEVICE_INFO_TYPE.DISPLAYCONFIG_DEVICE_INFO_GET_SOURCE_NAME;
@@ -1074,13 +1141,7 @@ namespace ColorControl.Shared.Native
return false;
}
var deviceName = info.viewGdiDeviceName;
if (!EqualDisplayNames(deviceName, displayName))
{
return false;
}
return true;
return EqualDisplayNames(deviceName, info.viewGdiDeviceName);
}
private static uint GetSDRWhiteLevelForDisplayConfig(DisplayConfigModeInfo displayConfigModeInfo)
+23
View File
@@ -166,6 +166,29 @@ namespace ColorControl.Shared.Services
return true;
}
public bool StopProcessByName(string name)
{
if (!_winApiService.IsAdministrator())
{
return _rpcService.Call<bool>(nameof(StopProcessByName), name);
}
var processes = Process.GetProcessesByName(name);
var result = false;
foreach (var item in processes)
{
try
{
item.Kill();
result = true;
}
catch { }
}
return result;
}
public bool SetProcessAffinity(int processId, uint affinityMask)
{
if (!_winApiService.IsAdministrator())
+8 -9
View File
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net9.0-windows</TargetFramework>
<TargetFramework>net10.0-windows</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>disable</Nullable>
<UseWPF>true</UseWPF>
@@ -25,15 +25,14 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.CSharp" Version="4.7.0" />
<PackageReference Include="Microsoft.Extensions.Hosting.WindowsServices" Version="9.0.0" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="NLog" Version="5.3.4" />
<PackageReference Include="NStandard" Version="0.83.1" />
<PackageReference Include="Microsoft.Extensions.Hosting.WindowsServices" Version="10.0.7" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.4" />
<PackageReference Include="NLog" Version="6.1.2" />
<PackageReference Include="NStandard" Version="0.110.7" />
<PackageReference Include="NWin32" Version="1.2.8" />
<PackageReference Include="System.Management" Version="9.0.0" />
<PackageReference Include="TaskScheduler" Version="2.11.0" />
<PackageReference Include="Vanara.PInvoke.IpHlpApi" Version="4.0.4" />
<PackageReference Include="System.Management" Version="10.0.7" />
<PackageReference Include="TaskScheduler" Version="2.12.2" />
<PackageReference Include="Vanara.PInvoke.IpHlpApi" Version="5.0.4" />
<PackageReference Include="RawInput.Sharp" Version="0.1.3" />
</ItemGroup>
+1 -1
View File
@@ -47,7 +47,7 @@ namespace novideo_srgb
try
{
var hdrActive = hdrPaths.Contains(path);
var hdrActive = hdrPaths?.Contains(path) == true;
var settings = config?.FirstOrDefault(x => (string?)x.Attribute("path") == path);
MonitorData monitor;
+2 -7
View File
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net9.0-windows</TargetFramework>
<TargetFramework>net10.0-windows</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<AppDesignerFolder>Properties</AppDesignerFolder>
@@ -16,13 +16,8 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="NLog" Version="5.3.4" />
<PackageReference Include="System.Resources.Extensions" Version="9.0.0" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="NLog" Version="6.1.2" />
<PackageReference Include="Microsoft.NETCore.UniversalWindowsPlatform" Version="6.2.14" />
<PackageReference Include="Microsoft.CSharp" Version="4.7.0" />
<PackageReference Include="EDIDParser" Version="1.2.5.4" />
<PackageReference Include="WindowsDisplayAPI" Version="1.3.0.13" />
</ItemGroup>