From 787740c1402ac3ce891b3c14094acc29a2552c40 Mon Sep 17 00:00:00 2001 From: Prashant Vaibhav Date: Thu, 11 Jun 2026 15:42:33 +0200 Subject: [PATCH 01/27] feat(tg5040): add Brick white point correction Add a Brick-only Display setting that toggles a system-wide white point correction on or off. The correction defaults to enabled and is applied during NextUI startup before normal UI/app use. The correction is implemented by displaycal.elf, which uploads a 256-entry packed RGB gamma LUT to the Allwinner display driver through /dev/disp using DISP_LCD_SET_GAMMA_TABLE and then enables gamma correction with DISP_LCD_GAMMA_CORRECTION_ENABLE. The LUT uses the Brick panel white point I measured for one of my Bricks, then applied to another Brick Hammer. I expect the variance between devices to be lesser than the overall distance of the panel from calibrated D65 white. The correction runs in hardware after upload, so it applies to NextUI, emulators, and third-party apps completely cost-free. Sleep/wake does not reset it. In this first iteration, Settings app only owns the public enabled=0/1 config and invokes displaycal.elf apply when the toggle changes. Currently no detailed calibration settings have been added. --- makefile | 1 + .../SYSTEM/tg5040/paks/MinUI.pak/launch.sh | 4 + workspace/all/settings/settings.cpp | 57 ++++ workspace/makefile | 2 + workspace/tg5040/displaycal/displaycal.c | 316 ++++++++++++++++++ workspace/tg5040/displaycal/makefile | 35 ++ 6 files changed, 415 insertions(+) create mode 100644 workspace/tg5040/displaycal/displaycal.c create mode 100644 workspace/tg5040/displaycal/makefile diff --git a/makefile b/makefile index fed3da9f6..56cff92be 100644 --- a/makefile +++ b/makefile @@ -109,6 +109,7 @@ ifneq (,$(filter $(PLATFORM),tg5040 tg5050)) ifeq ($(PLATFORM), tg5040) # Limbo fix cp ./workspace/$(PLATFORM)/poweroff_next/build/$(PLATFORM)/poweroff_next.elf ./build/SYSTEM/$(PLATFORM)/bin/poweroff_next + cp ./workspace/$(PLATFORM)/displaycal/build/$(PLATFORM)/displaycal.elf ./build/SYSTEM/$(PLATFORM)/bin/ endif endif ifneq (,$(filter $(PLATFORM),tg5040 tg5050 my355)) diff --git a/skeleton/SYSTEM/tg5040/paks/MinUI.pak/launch.sh b/skeleton/SYSTEM/tg5040/paks/MinUI.pak/launch.sh index 0aad231b8..241c696b2 100755 --- a/skeleton/SYSTEM/tg5040/paks/MinUI.pak/launch.sh +++ b/skeleton/SYSTEM/tg5040/paks/MinUI.pak/launch.sh @@ -103,6 +103,10 @@ syslogd -S export LD_LIBRARY_PATH=$SYSTEM_PATH/lib:/usr/trimui/lib:$LD_LIBRARY_PATH export PATH=$SYSTEM_PATH/bin:/usr/trimui/bin:$PATH +if [ "$DEVICE" = "brick" ] && [ -x "$SYSTEM_PATH/bin/displaycal.elf" ]; then + "$SYSTEM_PATH/bin/displaycal.elf" apply "$USERDATA_PATH/displaycal.cfg" > /dev/null 2>&1 +fi + # leds_off echo 0 > /sys/class/led_anim/max_scale if [ "$TRIMUI_MODEL" = "Trimui Brick" ]; then diff --git a/workspace/all/settings/settings.cpp b/workspace/all/settings/settings.cpp index 53fc6b7a6..506980dec 100644 --- a/workspace/all/settings/settings.cpp +++ b/workspace/all/settings/settings.cpp @@ -10,6 +10,7 @@ extern "C" } #include +#include #include #include #include @@ -177,6 +178,49 @@ static const std::vector ra_sort_labels = { }; namespace { + constexpr const char *DISPLAYCAL_CONFIG_PATH = USERDATA_PATH "/displaycal.cfg"; + constexpr const char *DISPLAYCAL_COMMAND = BIN_PATH "/displaycal.elf"; + + static bool getDisplayCalEnabled() + { + std::ifstream file(DISPLAYCAL_CONFIG_PATH); + if (!file) + return true; + + std::string line; + while (std::getline(file, line)) + { + if (line.rfind("enabled=", 0) == 0) + return std::atoi(line.c_str() + 8) != 0; + } + + return true; + } + + static void setDisplayCalEnabled(bool enabled) + { + std::ofstream file(DISPLAYCAL_CONFIG_PATH, std::ios::trunc); + if (file) + file << "enabled=" << (enabled ? 1 : 0) << "\n"; + } + + static void applyDisplayCalConfig() + { + std::string cmd = std::string("\"") + DISPLAYCAL_COMMAND + "\" apply \"" + DISPLAYCAL_CONFIG_PATH + "\" > /dev/null 2>&1"; + std::system(cmd.c_str()); + } + + static void saveAndApplyDisplayCalEnabled(bool enabled) + { + setDisplayCalEnabled(enabled); + applyDisplayCalConfig(); + } + + static void resetDisplayCal() + { + saveAndApplyDisplayCalEnabled(true); + } + struct ColorDef { int id; const char *name; const char *desc; uint32_t defaultColor; }; static const ColorDef g_colorDefs[] = { {1, "Main Color", "The color used to render main UI elements.", CFG_DEFAULT_COLOR1}, @@ -286,6 +330,10 @@ namespace { return m_platform == tg5040; } + bool hasDisplayColorCorrection() const { + return m_platform == tg5040 && m_model == Brick; + } + bool hasActiveCooling() const { return m_platform == tg5050; } @@ -480,6 +528,15 @@ int main(int argc, char *argv[]) []() { SetColortemp(SETTINGS_DEFAULT_COLORTEMP);}}); } + if(deviceInfo.hasDisplayColorCorrection()) + { + displayItems.push_back( + new MenuItem{ListItemType::Generic, "White point correction", "Corrects the Brick display white point", {false, true}, on_off, []() -> std::any + { return getDisplayCalEnabled(); }, [](const std::any &value) + { saveAndApplyDisplayCalEnabled(std::any_cast(value)); }, + []() { resetDisplayCal(); }}); + } + if(deviceInfo.hasContrastSaturation()) { displayItems.push_back( diff --git a/workspace/makefile b/workspace/makefile index f71af4d62..dcda004ed 100644 --- a/workspace/makefile +++ b/workspace/makefile @@ -33,6 +33,7 @@ else ifeq ($(PLATFORM), tg5040) cd ./$(PLATFORM)/rfkill && make all cd ./$(PLATFORM)/btmanager && make all + cd ./$(PLATFORM)/displaycal && make cd ./$(PLATFORM)/poweroff_next/ && make endif cd ./$(PLATFORM)/libmsettings && make @@ -83,6 +84,7 @@ ifneq ($(PLATFORM), desktop) ifeq ($(PLATFORM), tg5040) cd ./$(PLATFORM)/rfkill && make clean cd ./$(PLATFORM)/btmanager && make clean + cd ./$(PLATFORM)/displaycal && make clean cd ./$(PLATFORM)/poweroff_next/ && make clean endif endif diff --git a/workspace/tg5040/displaycal/displaycal.c b/workspace/tg5040/displaycal/displaycal.c new file mode 100644 index 000000000..f4cde4952 --- /dev/null +++ b/workspace/tg5040/displaycal/displaycal.c @@ -0,0 +1,316 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DISP_LCD_SET_GAMMA_TABLE 0x10b +#define DISP_LCD_GAMMA_CORRECTION_ENABLE 0x10c +#define DISP_LCD_GAMMA_CORRECTION_DISABLE 0x10d + +#define DISPLAYCAL_LUT_ENTRIES 256 +#define DISPLAYCAL_LUT_BYTES (DISPLAYCAL_LUT_ENTRIES * 4) + +#define DEFAULT_RED_GAIN 1.0000000000000000 +#define DEFAULT_GREEN_GAIN 0.9233642796405507 +#define DEFAULT_BLUE_GAIN 0.5833412353395729 + +typedef struct { + int enabled; + int screen; + double strength; + double red_gain; + double green_gain; + double blue_gain; + char format[4]; +} DisplayCalConfig; + +static void init_config(DisplayCalConfig *config) { + config->enabled = 1; + config->screen = 0; + config->strength = 1.0; + config->red_gain = DEFAULT_RED_GAIN; + config->green_gain = DEFAULT_GREEN_GAIN; + config->blue_gain = DEFAULT_BLUE_GAIN; + strcpy(config->format, "rgb"); +} + +static double clamp_unit(double v) { + if (v < 0.0) + return 0.0; + if (v > 1.0) + return 1.0; + return v; +} + +static unsigned char clamp_u8(double v) { + if (v < 0.0) + return 0; + if (v > 255.0) + return 255; + return (unsigned char)(v + 0.5); +} + +static double srgb_to_linear(double c) { + if (c <= 0.04045) + return c / 12.92; + return pow((c + 0.055) / 1.055, 2.4); +} + +static double linear_to_srgb(double c) { + if (c <= 0.0) + return 0.0; + if (c <= 0.0031308) + return c * 12.92; + return 1.055 * pow(c, 1.0 / 2.4) - 0.055; +} + +static double mix_gain(double gain, double strength) { + return 1.0 + (gain - 1.0) * clamp_unit(strength); +} + +static char *trim_token(char *s) { + while (*s == ' ' || *s == '\t' || *s == '\n' || *s == '\r') + s++; + + char *end = s + strlen(s); + while (end > s) { + char c = end[-1]; + if (c != ' ' && c != '\t' && c != '\n' && c != '\r') + break; + *--end = '\0'; + } + + return s; +} + +static int streq(const char *a, const char *b) { + return strcmp(a, b) == 0; +} + +static int valid_format(const char *format) { + return streq(format, "rgb") || + streq(format, "bgr") || + streq(format, "grb") || + streq(format, "gbr") || + streq(format, "rbg") || + streq(format, "brg"); +} + +static uint32_t pack_rgb(int r, int g, int b, const char *format) { + uint32_t rr = (uint32_t)r & 0xff; + uint32_t gg = (uint32_t)g & 0xff; + uint32_t bb = (uint32_t)b & 0xff; + + if (streq(format, "bgr")) + return (bb << 16) | (gg << 8) | rr; + if (streq(format, "grb")) + return (gg << 16) | (rr << 8) | bb; + if (streq(format, "gbr")) + return (gg << 16) | (bb << 8) | rr; + if (streq(format, "rbg")) + return (rr << 16) | (bb << 8) | gg; + if (streq(format, "brg")) + return (bb << 16) | (rr << 8) | gg; + + return (rr << 16) | (gg << 8) | bb; +} + +static void fill_linear_gain_table(uint32_t table[DISPLAYCAL_LUT_ENTRIES], const DisplayCalConfig *config) { + double red_gain = mix_gain(config->red_gain, config->strength); + double green_gain = mix_gain(config->green_gain, config->strength); + double blue_gain = mix_gain(config->blue_gain, config->strength); + + for (int i = 0; i < DISPLAYCAL_LUT_ENTRIES; i++) { + double linear = srgb_to_linear(i / 255.0); + int r = clamp_u8(linear_to_srgb(linear * red_gain) * 255.0); + int g = clamp_u8(linear_to_srgb(linear * green_gain) * 255.0); + int b = clamp_u8(linear_to_srgb(linear * blue_gain) * 255.0); + table[i] = pack_rgb(r, g, b, config->format); + } +} + +static void fill_identity_table(uint32_t table[DISPLAYCAL_LUT_ENTRIES]) { + for (int i = 0; i < DISPLAYCAL_LUT_ENTRIES; i++) + table[i] = ((uint32_t)i << 16) | ((uint32_t)i << 8) | (uint32_t)i; +} + +static void load_config(DisplayCalConfig *config, const char *path) { + if (!path || !*path) + return; + + FILE *file = fopen(path, "r"); + if (!file) + return; + + char line[160]; + while (fgets(line, sizeof(line), file)) { + char *comment = strchr(line, '#'); + if (comment) + *comment = '\0'; + + char *sep = strchr(line, '='); + if (!sep) + continue; + + *sep = '\0'; + char *key = trim_token(line); + char *value = trim_token(sep + 1); + + if (streq(key, "enabled")) + config->enabled = atoi(value) ? 1 : 0; + else if (streq(key, "screen")) + config->screen = atoi(value); + else if (streq(key, "strength")) + config->strength = atof(value); + else if (streq(key, "red") || streq(key, "r") || streq(key, "red_gain")) + config->red_gain = atof(value); + else if (streq(key, "green") || streq(key, "g") || streq(key, "green_gain")) + config->green_gain = atof(value); + else if (streq(key, "blue") || streq(key, "b") || streq(key, "blue_gain")) + config->blue_gain = atof(value); + else if (streq(key, "format") && valid_format(value)) + snprintf(config->format, sizeof(config->format), "%s", value); + } + + fclose(file); +} + +static int open_disp(void) { + int fd = open("/dev/disp", O_RDWR); + if (fd < 0) + fprintf(stderr, "displaycal: open /dev/disp failed: %s\n", strerror(errno)); + return fd; +} + +static int disable_gamma(int fd, int screen) { + unsigned long param[4] = { (unsigned long)screen, 0, 0, 0 }; + if (ioctl(fd, DISP_LCD_GAMMA_CORRECTION_DISABLE, param) < 0) { + fprintf(stderr, "displaycal: disable gamma failed: %s\n", strerror(errno)); + return -1; + } + return 0; +} + +static int apply_table(int fd, int screen, uint32_t table[DISPLAYCAL_LUT_ENTRIES]) { + unsigned long set_param[4] = { + (unsigned long)screen, + (unsigned long)table, + DISPLAYCAL_LUT_BYTES, + 0, + }; + + if (ioctl(fd, DISP_LCD_SET_GAMMA_TABLE, set_param) < 0) { + fprintf(stderr, "displaycal: set gamma table failed: %s\n", strerror(errno)); + return -1; + } + + unsigned long enable_param[4] = { (unsigned long)screen, 0, 0, 0 }; + if (ioctl(fd, DISP_LCD_GAMMA_CORRECTION_ENABLE, enable_param) < 0) { + fprintf(stderr, "displaycal: enable gamma failed: %s\n", strerror(errno)); + return -1; + } + + return 0; +} + +static int apply_config(const DisplayCalConfig *config) { + int fd = open_disp(); + if (fd < 0) + return -1; + + int ret = 0; + if (!config->enabled) { + ret = disable_gamma(fd, config->screen); + } else { + uint32_t table[DISPLAYCAL_LUT_ENTRIES]; + fill_linear_gain_table(table, config); + ret = apply_table(fd, config->screen, table); + } + + close(fd); + return ret; +} + +static int apply_identity(int screen) { + int fd = open_disp(); + if (fd < 0) + return -1; + + uint32_t table[DISPLAYCAL_LUT_ENTRIES]; + fill_identity_table(table); + int ret = apply_table(fd, screen, table); + close(fd); + return ret; +} + +static int disable_screen(int screen) { + int fd = open_disp(); + if (fd < 0) + return -1; + + int ret = disable_gamma(fd, screen); + close(fd); + return ret; +} + +static void print_status(const DisplayCalConfig *config) { + printf("enabled=%d\n", config->enabled); + printf("screen=%d\n", config->screen); + printf("strength=%.6f\n", config->strength); + printf("red=%.16f\n", config->red_gain); + printf("green=%.16f\n", config->green_gain); + printf("blue=%.16f\n", config->blue_gain); + printf("format=%s\n", config->format); +} + +static void usage(const char *argv0) { + fprintf(stderr, + "Usage:\n" + " %s apply [config]\n" + " %s status [config]\n" + " %s disable [screen]\n" + " %s identity [screen]\n", + argv0, argv0, argv0, argv0); +} + +int main(int argc, char **argv) { + if (argc < 2) { + usage(argv[0]); + return 2; + } + + if (streq(argv[1], "apply")) { + DisplayCalConfig config; + init_config(&config); + if (argc >= 3) + load_config(&config, argv[2]); + return apply_config(&config) < 0 ? 1 : 0; + } + + if (streq(argv[1], "status")) { + DisplayCalConfig config; + init_config(&config); + if (argc >= 3) + load_config(&config, argv[2]); + print_status(&config); + return 0; + } + + if (streq(argv[1], "disable")) { + int screen = argc >= 3 ? atoi(argv[2]) : 0; + return disable_screen(screen) < 0 ? 1 : 0; + } + + if (streq(argv[1], "identity")) { + int screen = argc >= 3 ? atoi(argv[2]) : 0; + return apply_identity(screen) < 0 ? 1 : 0; + } + + usage(argv[0]); + return 2; +} diff --git a/workspace/tg5040/displaycal/makefile b/workspace/tg5040/displaycal/makefile new file mode 100644 index 000000000..30629c35c --- /dev/null +++ b/workspace/tg5040/displaycal/makefile @@ -0,0 +1,35 @@ +########################################################### + +ifeq (,$(PLATFORM)) +PLATFORM=$(UNION_PLATFORM) +endif + +ifeq (,$(PLATFORM)) + $(error please specify PLATFORM, eg. PLATFORM=tg5040 make) +endif + +ifeq (,$(CROSS_COMPILE)) + $(error missing CROSS_COMPILE for this toolchain) +endif + +########################################################### + +include ../platform/makefile.env + +########################################################### + +TARGET = displaycal +SOURCE = $(TARGET).c + +CC = $(CROSS_COMPILE)gcc +CFLAGS = -mcpu=cortex-a53 -flto $(OPT) -std=gnu99 +LDFLAGS = -lm -s + +PRODUCT = build/$(PLATFORM)/$(TARGET).elf + +all: + mkdir -p build/$(PLATFORM) + $(CC) $(SOURCE) -o $(PRODUCT) $(CFLAGS) $(LDFLAGS) + +clean: + rm -f $(PRODUCT) From 48c016effba30809494c7f590d1c54e8c76bfd1a Mon Sep 17 00:00:00 2001 From: Prashant Vaibhav Date: Thu, 11 Jun 2026 17:31:12 +0200 Subject: [PATCH 02/27] feat(tg5040): add Brick white point controls Add white point red, green, and blue gain sliders in 0.01 steps to the Settings app (Display section). The correction remains enabled by default and reset restores the tuned defaults of red 1.00, green 0.92, and blue 0.58. Also saves the configured settings for r/g/b, not just enabled/disabled. --- workspace/all/settings/settings.cpp | 172 +++++++++++++++++++++-- workspace/tg5040/displaycal/displaycal.c | 4 +- 2 files changed, 166 insertions(+), 10 deletions(-) diff --git a/workspace/all/settings/settings.cpp b/workspace/all/settings/settings.cpp index 506980dec..ecedf8e9f 100644 --- a/workspace/all/settings/settings.cpp +++ b/workspace/all/settings/settings.cpp @@ -9,6 +9,7 @@ extern "C" #include "ra_sync.h" } +#include #include #include #include @@ -180,28 +181,143 @@ static const std::vector ra_sort_labels = { namespace { constexpr const char *DISPLAYCAL_CONFIG_PATH = USERDATA_PATH "/displaycal.cfg"; constexpr const char *DISPLAYCAL_COMMAND = BIN_PATH "/displaycal.elf"; + constexpr int DISPLAYCAL_DEFAULT_RED = 100; + constexpr int DISPLAYCAL_DEFAULT_GREEN = 92; + constexpr int DISPLAYCAL_DEFAULT_BLUE = 58; - static bool getDisplayCalEnabled() + enum class DisplayCalChannel + { + Red, + Green, + Blue + }; + + struct DisplayCalConfig + { + bool enabled = true; + int red = DISPLAYCAL_DEFAULT_RED; + int green = DISPLAYCAL_DEFAULT_GREEN; + int blue = DISPLAYCAL_DEFAULT_BLUE; + }; + + static int clampDisplayCalGain(int value) { + return std::max(0, std::min(200, value)); + } + + static int parseDisplayCalGain(const char *value) + { + return clampDisplayCalGain((int)(std::atof(value) * 100.0 + 0.5)); + } + + static std::string formatDisplayCalGain(int value) + { + char label[8]; + snprintf(label, sizeof(label), "%.2f", clampDisplayCalGain(value) / 100.0); + return label; + } + + static const std::vector &displayCalGainValues() + { + static const std::vector values = [] { + std::vector result; + for (int i = 0; i <= 200; i++) + result.push_back(i); + return result; + }(); + return values; + } + + static const std::vector &displayCalGainLabels() + { + static const std::vector labels = [] { + std::vector result; + for (int i = 0; i <= 200; i++) + result.push_back(formatDisplayCalGain(i)); + return result; + }(); + return labels; + } + + static DisplayCalConfig loadDisplayCalConfig() + { + DisplayCalConfig config; std::ifstream file(DISPLAYCAL_CONFIG_PATH); if (!file) - return true; + return config; std::string line; while (std::getline(file, line)) { if (line.rfind("enabled=", 0) == 0) - return std::atoi(line.c_str() + 8) != 0; + config.enabled = std::atoi(line.c_str() + 8) != 0; + else if (line.rfind("red=", 0) == 0) + config.red = parseDisplayCalGain(line.c_str() + 4); + else if (line.rfind("green=", 0) == 0) + config.green = parseDisplayCalGain(line.c_str() + 6); + else if (line.rfind("blue=", 0) == 0) + config.blue = parseDisplayCalGain(line.c_str() + 5); } - return true; + return config; } - static void setDisplayCalEnabled(bool enabled) + static void saveDisplayCalConfig(const DisplayCalConfig &config) { std::ofstream file(DISPLAYCAL_CONFIG_PATH, std::ios::trunc); - if (file) - file << "enabled=" << (enabled ? 1 : 0) << "\n"; + if (!file) + return; + + file << "enabled=" << (config.enabled ? 1 : 0) << "\n"; + file << "red=" << formatDisplayCalGain(config.red) << "\n"; + file << "green=" << formatDisplayCalGain(config.green) << "\n"; + file << "blue=" << formatDisplayCalGain(config.blue) << "\n"; + } + + static bool getDisplayCalEnabled() + { + return loadDisplayCalConfig().enabled; + } + + static void setDisplayCalEnabled(bool enabled) + { + DisplayCalConfig config = loadDisplayCalConfig(); + config.enabled = enabled; + saveDisplayCalConfig(config); + } + + static int getDisplayCalGain(DisplayCalChannel channel) + { + DisplayCalConfig config = loadDisplayCalConfig(); + switch (channel) + { + case DisplayCalChannel::Red: + return config.red; + case DisplayCalChannel::Green: + return config.green; + case DisplayCalChannel::Blue: + return config.blue; + } + return 100; + } + + static void setDisplayCalGain(DisplayCalChannel channel, int value) + { + DisplayCalConfig config = loadDisplayCalConfig(); + value = clampDisplayCalGain(value); + switch (channel) + { + case DisplayCalChannel::Red: + config.red = value; + break; + case DisplayCalChannel::Green: + config.green = value; + break; + case DisplayCalChannel::Blue: + config.blue = value; + break; + } + saveDisplayCalConfig(config); } static void applyDisplayCalConfig() @@ -216,9 +332,34 @@ namespace { applyDisplayCalConfig(); } + static void saveAndApplyDisplayCalGain(DisplayCalChannel channel, int value) + { + setDisplayCalGain(channel, value); + if (getDisplayCalEnabled()) + applyDisplayCalConfig(); + } + static void resetDisplayCal() { - saveAndApplyDisplayCalEnabled(true); + saveDisplayCalConfig(DisplayCalConfig{}); + applyDisplayCalConfig(); + } + + static void resetDisplayCalGain(DisplayCalChannel channel) + { + switch (channel) + { + case DisplayCalChannel::Red: + setDisplayCalGain(channel, DISPLAYCAL_DEFAULT_RED); + break; + case DisplayCalChannel::Green: + setDisplayCalGain(channel, DISPLAYCAL_DEFAULT_GREEN); + break; + case DisplayCalChannel::Blue: + setDisplayCalGain(channel, DISPLAYCAL_DEFAULT_BLUE); + break; + } + applyDisplayCalConfig(); } struct ColorDef { int id; const char *name; const char *desc; uint32_t defaultColor; }; @@ -535,6 +676,21 @@ int main(int argc, char *argv[]) { return getDisplayCalEnabled(); }, [](const std::any &value) { saveAndApplyDisplayCalEnabled(std::any_cast(value)); }, []() { resetDisplayCal(); }}); + displayItems.push_back( + new MenuItem{ListItemType::Generic, "Red gain", "White point correction red channel gain", displayCalGainValues(), displayCalGainLabels(), []() -> std::any + { return getDisplayCalGain(DisplayCalChannel::Red); }, [](const std::any &value) + { saveAndApplyDisplayCalGain(DisplayCalChannel::Red, std::any_cast(value)); }, + []() { resetDisplayCalGain(DisplayCalChannel::Red); }}); + displayItems.push_back( + new MenuItem{ListItemType::Generic, "Green gain", "White point correction green channel gain", displayCalGainValues(), displayCalGainLabels(), []() -> std::any + { return getDisplayCalGain(DisplayCalChannel::Green); }, [](const std::any &value) + { saveAndApplyDisplayCalGain(DisplayCalChannel::Green, std::any_cast(value)); }, + []() { resetDisplayCalGain(DisplayCalChannel::Green); }}); + displayItems.push_back( + new MenuItem{ListItemType::Generic, "Blue gain", "White point correction blue channel gain", displayCalGainValues(), displayCalGainLabels(), []() -> std::any + { return getDisplayCalGain(DisplayCalChannel::Blue); }, [](const std::any &value) + { saveAndApplyDisplayCalGain(DisplayCalChannel::Blue, std::any_cast(value)); }, + []() { resetDisplayCalGain(DisplayCalChannel::Blue); }}); } if(deviceInfo.hasContrastSaturation()) diff --git a/workspace/tg5040/displaycal/displaycal.c b/workspace/tg5040/displaycal/displaycal.c index f4cde4952..caeec3ba5 100644 --- a/workspace/tg5040/displaycal/displaycal.c +++ b/workspace/tg5040/displaycal/displaycal.c @@ -16,8 +16,8 @@ #define DISPLAYCAL_LUT_BYTES (DISPLAYCAL_LUT_ENTRIES * 4) #define DEFAULT_RED_GAIN 1.0000000000000000 -#define DEFAULT_GREEN_GAIN 0.9233642796405507 -#define DEFAULT_BLUE_GAIN 0.5833412353395729 +#define DEFAULT_GREEN_GAIN 0.9200000000000000 +#define DEFAULT_BLUE_GAIN 0.5800000000000000 typedef struct { int enabled; From 8da20a04fde1386fb7d9f091e8e2ff793609f8a4 Mon Sep 17 00:00:00 2001 From: Prashant Vaibhav Date: Thu, 11 Jun 2026 17:38:29 +0200 Subject: [PATCH 03/27] refactor(tg5040): keep displaycal defaults in one place The white point defaults now live in a small shared header used by both displaycal.elf and Settings.cpp --- workspace/all/common/displaycal.h | 18 +++++++++++++ workspace/all/settings/settings.cpp | 34 +++++++++++++----------- workspace/tg5040/displaycal/displaycal.c | 20 +++++++------- workspace/tg5040/displaycal/makefile | 2 +- 4 files changed, 46 insertions(+), 28 deletions(-) create mode 100644 workspace/all/common/displaycal.h diff --git a/workspace/all/common/displaycal.h b/workspace/all/common/displaycal.h new file mode 100644 index 000000000..b970e2192 --- /dev/null +++ b/workspace/all/common/displaycal.h @@ -0,0 +1,18 @@ +#ifndef DISPLAYCAL_H +#define DISPLAYCAL_H + +#define DISPLAYCAL_CONFIG_FILENAME "displaycal.cfg" + +#define DISPLAYCAL_DEFAULT_ENABLED 1 +#define DISPLAYCAL_DEFAULT_SCREEN 0 +#define DISPLAYCAL_DEFAULT_STRENGTH 1.0 +#define DISPLAYCAL_DEFAULT_RED_GAIN 1.0 +#define DISPLAYCAL_DEFAULT_GREEN_GAIN 0.92 +#define DISPLAYCAL_DEFAULT_BLUE_GAIN 0.58 +#define DISPLAYCAL_DEFAULT_FORMAT "rgb" + +#define DISPLAYCAL_GAIN_SCALE 100 +#define DISPLAYCAL_GAIN_MIN 0 +#define DISPLAYCAL_GAIN_MAX 200 + +#endif diff --git a/workspace/all/settings/settings.cpp b/workspace/all/settings/settings.cpp index ecedf8e9f..d7ce6029b 100644 --- a/workspace/all/settings/settings.cpp +++ b/workspace/all/settings/settings.cpp @@ -5,11 +5,11 @@ extern "C" #include "defines.h" #include "api.h" #include "utils.h" +#include "displaycal.h" #include "ra_auth.h" #include "ra_sync.h" } -#include #include #include #include @@ -179,11 +179,13 @@ static const std::vector ra_sort_labels = { }; namespace { - constexpr const char *DISPLAYCAL_CONFIG_PATH = USERDATA_PATH "/displaycal.cfg"; + constexpr const char *DISPLAYCAL_CONFIG_PATH = USERDATA_PATH "/" DISPLAYCAL_CONFIG_FILENAME; constexpr const char *DISPLAYCAL_COMMAND = BIN_PATH "/displaycal.elf"; - constexpr int DISPLAYCAL_DEFAULT_RED = 100; - constexpr int DISPLAYCAL_DEFAULT_GREEN = 92; - constexpr int DISPLAYCAL_DEFAULT_BLUE = 58; + + constexpr int displayCalGainToValue(double value) + { + return (int)(value * DISPLAYCAL_GAIN_SCALE + 0.5); + } enum class DisplayCalChannel { @@ -194,20 +196,20 @@ namespace { struct DisplayCalConfig { - bool enabled = true; - int red = DISPLAYCAL_DEFAULT_RED; - int green = DISPLAYCAL_DEFAULT_GREEN; - int blue = DISPLAYCAL_DEFAULT_BLUE; + bool enabled = DISPLAYCAL_DEFAULT_ENABLED != 0; + int red = displayCalGainToValue(DISPLAYCAL_DEFAULT_RED_GAIN); + int green = displayCalGainToValue(DISPLAYCAL_DEFAULT_GREEN_GAIN); + int blue = displayCalGainToValue(DISPLAYCAL_DEFAULT_BLUE_GAIN); }; static int clampDisplayCalGain(int value) { - return std::max(0, std::min(200, value)); + return std::max(DISPLAYCAL_GAIN_MIN, std::min(DISPLAYCAL_GAIN_MAX, value)); } static int parseDisplayCalGain(const char *value) { - return clampDisplayCalGain((int)(std::atof(value) * 100.0 + 0.5)); + return clampDisplayCalGain(displayCalGainToValue(std::atof(value))); } static std::string formatDisplayCalGain(int value) @@ -221,7 +223,7 @@ namespace { { static const std::vector values = [] { std::vector result; - for (int i = 0; i <= 200; i++) + for (int i = DISPLAYCAL_GAIN_MIN; i <= DISPLAYCAL_GAIN_MAX; i++) result.push_back(i); return result; }(); @@ -232,7 +234,7 @@ namespace { { static const std::vector labels = [] { std::vector result; - for (int i = 0; i <= 200; i++) + for (int i = DISPLAYCAL_GAIN_MIN; i <= DISPLAYCAL_GAIN_MAX; i++) result.push_back(formatDisplayCalGain(i)); return result; }(); @@ -350,13 +352,13 @@ namespace { switch (channel) { case DisplayCalChannel::Red: - setDisplayCalGain(channel, DISPLAYCAL_DEFAULT_RED); + setDisplayCalGain(channel, displayCalGainToValue(DISPLAYCAL_DEFAULT_RED_GAIN)); break; case DisplayCalChannel::Green: - setDisplayCalGain(channel, DISPLAYCAL_DEFAULT_GREEN); + setDisplayCalGain(channel, displayCalGainToValue(DISPLAYCAL_DEFAULT_GREEN_GAIN)); break; case DisplayCalChannel::Blue: - setDisplayCalGain(channel, DISPLAYCAL_DEFAULT_BLUE); + setDisplayCalGain(channel, displayCalGainToValue(DISPLAYCAL_DEFAULT_BLUE_GAIN)); break; } applyDisplayCalConfig(); diff --git a/workspace/tg5040/displaycal/displaycal.c b/workspace/tg5040/displaycal/displaycal.c index caeec3ba5..ba905e279 100644 --- a/workspace/tg5040/displaycal/displaycal.c +++ b/workspace/tg5040/displaycal/displaycal.c @@ -8,6 +8,8 @@ #include #include +#include "displaycal.h" + #define DISP_LCD_SET_GAMMA_TABLE 0x10b #define DISP_LCD_GAMMA_CORRECTION_ENABLE 0x10c #define DISP_LCD_GAMMA_CORRECTION_DISABLE 0x10d @@ -15,10 +17,6 @@ #define DISPLAYCAL_LUT_ENTRIES 256 #define DISPLAYCAL_LUT_BYTES (DISPLAYCAL_LUT_ENTRIES * 4) -#define DEFAULT_RED_GAIN 1.0000000000000000 -#define DEFAULT_GREEN_GAIN 0.9200000000000000 -#define DEFAULT_BLUE_GAIN 0.5800000000000000 - typedef struct { int enabled; int screen; @@ -30,13 +28,13 @@ typedef struct { } DisplayCalConfig; static void init_config(DisplayCalConfig *config) { - config->enabled = 1; - config->screen = 0; - config->strength = 1.0; - config->red_gain = DEFAULT_RED_GAIN; - config->green_gain = DEFAULT_GREEN_GAIN; - config->blue_gain = DEFAULT_BLUE_GAIN; - strcpy(config->format, "rgb"); + config->enabled = DISPLAYCAL_DEFAULT_ENABLED; + config->screen = DISPLAYCAL_DEFAULT_SCREEN; + config->strength = DISPLAYCAL_DEFAULT_STRENGTH; + config->red_gain = DISPLAYCAL_DEFAULT_RED_GAIN; + config->green_gain = DISPLAYCAL_DEFAULT_GREEN_GAIN; + config->blue_gain = DISPLAYCAL_DEFAULT_BLUE_GAIN; + strcpy(config->format, DISPLAYCAL_DEFAULT_FORMAT); } static double clamp_unit(double v) { diff --git a/workspace/tg5040/displaycal/makefile b/workspace/tg5040/displaycal/makefile index 30629c35c..4225fa1ef 100644 --- a/workspace/tg5040/displaycal/makefile +++ b/workspace/tg5040/displaycal/makefile @@ -22,7 +22,7 @@ TARGET = displaycal SOURCE = $(TARGET).c CC = $(CROSS_COMPILE)gcc -CFLAGS = -mcpu=cortex-a53 -flto $(OPT) -std=gnu99 +CFLAGS = -mcpu=cortex-a53 -flto $(OPT) -std=gnu99 -I../../all/common LDFLAGS = -lm -s PRODUCT = build/$(PLATFORM)/$(TARGET).elf From 95d577212810255850b59df160bd071d64d60184 Mon Sep 17 00:00:00 2001 From: Prashant Vaibhav Date: Thu, 11 Jun 2026 17:47:52 +0200 Subject: [PATCH 04/27] fix(tg5040): clear Brick gamma LUT when disabling correction When white point correction is turned off, displaycal now uploads an identity gamma table before issuing the disable ioctl. That prevents the previous correction table from hanging around in the display driver and interacting badly with color temperature or other panel controls. The explicit displaycal disable command follows the same path, while the identity command still leaves an identity table enabled for testing. --- workspace/tg5040/displaycal/displaycal.c | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/workspace/tg5040/displaycal/displaycal.c b/workspace/tg5040/displaycal/displaycal.c index ba905e279..35a00610d 100644 --- a/workspace/tg5040/displaycal/displaycal.c +++ b/workspace/tg5040/displaycal/displaycal.c @@ -194,7 +194,7 @@ static int disable_gamma(int fd, int screen) { return 0; } -static int apply_table(int fd, int screen, uint32_t table[DISPLAYCAL_LUT_ENTRIES]) { +static int set_gamma_table(int fd, int screen, uint32_t table[DISPLAYCAL_LUT_ENTRIES]) { unsigned long set_param[4] = { (unsigned long)screen, (unsigned long)table, @@ -207,6 +207,10 @@ static int apply_table(int fd, int screen, uint32_t table[DISPLAYCAL_LUT_ENTRIES return -1; } + return 0; +} + +static int enable_gamma(int fd, int screen) { unsigned long enable_param[4] = { (unsigned long)screen, 0, 0, 0 }; if (ioctl(fd, DISP_LCD_GAMMA_CORRECTION_ENABLE, enable_param) < 0) { fprintf(stderr, "displaycal: enable gamma failed: %s\n", strerror(errno)); @@ -216,6 +220,20 @@ static int apply_table(int fd, int screen, uint32_t table[DISPLAYCAL_LUT_ENTRIES return 0; } +static int apply_table(int fd, int screen, uint32_t table[DISPLAYCAL_LUT_ENTRIES]) { + if (set_gamma_table(fd, screen, table) < 0) + return -1; + return enable_gamma(fd, screen); +} + +static int reset_table_and_disable(int fd, int screen) { + uint32_t table[DISPLAYCAL_LUT_ENTRIES]; + fill_identity_table(table); + if (set_gamma_table(fd, screen, table) < 0) + return -1; + return disable_gamma(fd, screen); +} + static int apply_config(const DisplayCalConfig *config) { int fd = open_disp(); if (fd < 0) @@ -223,7 +241,7 @@ static int apply_config(const DisplayCalConfig *config) { int ret = 0; if (!config->enabled) { - ret = disable_gamma(fd, config->screen); + ret = reset_table_and_disable(fd, config->screen); } else { uint32_t table[DISPLAYCAL_LUT_ENTRIES]; fill_linear_gain_table(table, config); @@ -251,7 +269,7 @@ static int disable_screen(int screen) { if (fd < 0) return -1; - int ret = disable_gamma(fd, screen); + int ret = reset_table_and_disable(fd, screen); close(fd); return ret; } From be4619e3a2149c333eaf4898a4866cd37d57fe86 Mon Sep 17 00:00:00 2001 From: Prashant Vaibhav Date: Fri, 12 Jun 2026 03:39:32 +0200 Subject: [PATCH 05/27] fix(tg5040): harden displaycal config handling and debounce settings writes Tighten displaycal.c's config grammar to the keys settings.cpp writes (enabled/red/green/blue), clamp parsed gains to the shared 0.00-2.00 range on both sides, and discard the tail of overlong config lines so it can't be parsed as its own line. Drop the now-unused screen, strength, and format keys along with their defaults. In the settings app, keep the config in an in-memory cache and flush (save + apply) once ~250ms after the last change instead of rewriting the file and forking displaycal.elf on every key-repeat tick. Log apply failures instead of discarding them, clamp gains as doubles before the int conversion to avoid UB on out-of-range values, and collapse the per-channel switches and copy-pasted menu items into a pointer-to-member accessor and a table loop. --- workspace/all/common/displaycal.h | 3 - workspace/all/settings/settings.cpp | 159 ++++++++++++----------- workspace/tg5040/displaycal/displaycal.c | 129 +++++------------- 3 files changed, 111 insertions(+), 180 deletions(-) diff --git a/workspace/all/common/displaycal.h b/workspace/all/common/displaycal.h index b970e2192..3f509c281 100644 --- a/workspace/all/common/displaycal.h +++ b/workspace/all/common/displaycal.h @@ -4,12 +4,9 @@ #define DISPLAYCAL_CONFIG_FILENAME "displaycal.cfg" #define DISPLAYCAL_DEFAULT_ENABLED 1 -#define DISPLAYCAL_DEFAULT_SCREEN 0 -#define DISPLAYCAL_DEFAULT_STRENGTH 1.0 #define DISPLAYCAL_DEFAULT_RED_GAIN 1.0 #define DISPLAYCAL_DEFAULT_GREEN_GAIN 0.92 #define DISPLAYCAL_DEFAULT_BLUE_GAIN 0.58 -#define DISPLAYCAL_DEFAULT_FORMAT "rgb" #define DISPLAYCAL_GAIN_SCALE 100 #define DISPLAYCAL_GAIN_MIN 0 diff --git a/workspace/all/settings/settings.cpp b/workspace/all/settings/settings.cpp index d7ce6029b..cda70b2b7 100644 --- a/workspace/all/settings/settings.cpp +++ b/workspace/all/settings/settings.cpp @@ -10,6 +10,7 @@ extern "C" #include "ra_sync.h" } +#include #include #include #include @@ -181,6 +182,7 @@ static const std::vector ra_sort_labels = { namespace { constexpr const char *DISPLAYCAL_CONFIG_PATH = USERDATA_PATH "/" DISPLAYCAL_CONFIG_FILENAME; constexpr const char *DISPLAYCAL_COMMAND = BIN_PATH "/displaycal.elf"; + constexpr Uint32 DISPLAYCAL_APPLY_DELAY_MS = 250; constexpr int displayCalGainToValue(double value) { @@ -202,6 +204,20 @@ namespace { int blue = displayCalGainToValue(DISPLAYCAL_DEFAULT_BLUE_GAIN); }; + static int DisplayCalConfig::*displayCalField(DisplayCalChannel channel) + { + switch (channel) + { + case DisplayCalChannel::Red: + return &DisplayCalConfig::red; + case DisplayCalChannel::Green: + return &DisplayCalConfig::green; + case DisplayCalChannel::Blue: + return &DisplayCalConfig::blue; + } + return &DisplayCalConfig::red; + } + static int clampDisplayCalGain(int value) { return std::max(DISPLAYCAL_GAIN_MIN, std::min(DISPLAYCAL_GAIN_MAX, value)); @@ -209,7 +225,13 @@ namespace { static int parseDisplayCalGain(const char *value) { - return clampDisplayCalGain(displayCalGainToValue(std::atof(value))); + // clamp as a double: out-of-range double-to-int conversion is UB + double gain = std::atof(value); + if (std::isnan(gain)) + gain = 1.0; + gain = std::max((double)DISPLAYCAL_GAIN_MIN / DISPLAYCAL_GAIN_SCALE, + std::min((double)DISPLAYCAL_GAIN_MAX / DISPLAYCAL_GAIN_SCALE, gain)); + return displayCalGainToValue(gain); } static std::string formatDisplayCalGain(int value) @@ -276,91 +298,67 @@ namespace { file << "blue=" << formatDisplayCalGain(config.blue) << "\n"; } - static bool getDisplayCalEnabled() + // Setters only update this cache and mark it dirty; displayCalTick() persists + // and applies once the user pauses, so scrubbing a gain slider doesn't fork + // displaycal.elf and rewrite the config file on every key-repeat tick. + static DisplayCalConfig &displayCalConfig() { - return loadDisplayCalConfig().enabled; + static DisplayCalConfig config = loadDisplayCalConfig(); + return config; } - static void setDisplayCalEnabled(bool enabled) + static bool displayCalDirty = false; + static Uint32 displayCalLastChange = 0; + + static void markDisplayCalDirty() { - DisplayCalConfig config = loadDisplayCalConfig(); - config.enabled = enabled; - saveDisplayCalConfig(config); + displayCalDirty = true; + displayCalLastChange = SDL_GetTicks(); } - static int getDisplayCalGain(DisplayCalChannel channel) + static bool getDisplayCalEnabled() { - DisplayCalConfig config = loadDisplayCalConfig(); - switch (channel) - { - case DisplayCalChannel::Red: - return config.red; - case DisplayCalChannel::Green: - return config.green; - case DisplayCalChannel::Blue: - return config.blue; - } - return 100; + return displayCalConfig().enabled; } - static void setDisplayCalGain(DisplayCalChannel channel, int value) + static void setDisplayCalEnabled(bool enabled) { - DisplayCalConfig config = loadDisplayCalConfig(); - value = clampDisplayCalGain(value); - switch (channel) - { - case DisplayCalChannel::Red: - config.red = value; - break; - case DisplayCalChannel::Green: - config.green = value; - break; - case DisplayCalChannel::Blue: - config.blue = value; - break; - } - saveDisplayCalConfig(config); + displayCalConfig().enabled = enabled; + markDisplayCalDirty(); } - static void applyDisplayCalConfig() + static int getDisplayCalGain(DisplayCalChannel channel) { - std::string cmd = std::string("\"") + DISPLAYCAL_COMMAND + "\" apply \"" + DISPLAYCAL_CONFIG_PATH + "\" > /dev/null 2>&1"; - std::system(cmd.c_str()); + return displayCalConfig().*displayCalField(channel); } - static void saveAndApplyDisplayCalEnabled(bool enabled) + static void setDisplayCalGain(DisplayCalChannel channel, int value) { - setDisplayCalEnabled(enabled); - applyDisplayCalConfig(); + displayCalConfig().*displayCalField(channel) = clampDisplayCalGain(value); + markDisplayCalDirty(); } - static void saveAndApplyDisplayCalGain(DisplayCalChannel channel, int value) + static void resetDisplayCalGain(DisplayCalChannel channel) { - setDisplayCalGain(channel, value); - if (getDisplayCalEnabled()) - applyDisplayCalConfig(); + setDisplayCalGain(channel, DisplayCalConfig{}.*displayCalField(channel)); } - static void resetDisplayCal() + static void applyDisplayCalConfig() { - saveDisplayCalConfig(DisplayCalConfig{}); - applyDisplayCalConfig(); + std::string cmd = std::string("\"") + DISPLAYCAL_COMMAND + "\" apply \"" + DISPLAYCAL_CONFIG_PATH + "\" > /dev/null"; + int ret = std::system(cmd.c_str()); + if (ret != 0) + LOG_error("displaycal apply failed (%d)\n", ret); } - static void resetDisplayCalGain(DisplayCalChannel channel) + static void displayCalTick(bool force = false) { - switch (channel) - { - case DisplayCalChannel::Red: - setDisplayCalGain(channel, displayCalGainToValue(DISPLAYCAL_DEFAULT_RED_GAIN)); - break; - case DisplayCalChannel::Green: - setDisplayCalGain(channel, displayCalGainToValue(DISPLAYCAL_DEFAULT_GREEN_GAIN)); - break; - case DisplayCalChannel::Blue: - setDisplayCalGain(channel, displayCalGainToValue(DISPLAYCAL_DEFAULT_BLUE_GAIN)); - break; - } + if (!displayCalDirty) + return; + if (!force && SDL_GetTicks() - displayCalLastChange < DISPLAYCAL_APPLY_DELAY_MS) + return; + displayCalDirty = false; + saveDisplayCalConfig(displayCalConfig()); applyDisplayCalConfig(); } @@ -676,23 +674,24 @@ int main(int argc, char *argv[]) displayItems.push_back( new MenuItem{ListItemType::Generic, "White point correction", "Corrects the Brick display white point", {false, true}, on_off, []() -> std::any { return getDisplayCalEnabled(); }, [](const std::any &value) - { saveAndApplyDisplayCalEnabled(std::any_cast(value)); }, - []() { resetDisplayCal(); }}); - displayItems.push_back( - new MenuItem{ListItemType::Generic, "Red gain", "White point correction red channel gain", displayCalGainValues(), displayCalGainLabels(), []() -> std::any - { return getDisplayCalGain(DisplayCalChannel::Red); }, [](const std::any &value) - { saveAndApplyDisplayCalGain(DisplayCalChannel::Red, std::any_cast(value)); }, - []() { resetDisplayCalGain(DisplayCalChannel::Red); }}); - displayItems.push_back( - new MenuItem{ListItemType::Generic, "Green gain", "White point correction green channel gain", displayCalGainValues(), displayCalGainLabels(), []() -> std::any - { return getDisplayCalGain(DisplayCalChannel::Green); }, [](const std::any &value) - { saveAndApplyDisplayCalGain(DisplayCalChannel::Green, std::any_cast(value)); }, - []() { resetDisplayCalGain(DisplayCalChannel::Green); }}); - displayItems.push_back( - new MenuItem{ListItemType::Generic, "Blue gain", "White point correction blue channel gain", displayCalGainValues(), displayCalGainLabels(), []() -> std::any - { return getDisplayCalGain(DisplayCalChannel::Blue); }, [](const std::any &value) - { saveAndApplyDisplayCalGain(DisplayCalChannel::Blue, std::any_cast(value)); }, - []() { resetDisplayCalGain(DisplayCalChannel::Blue); }}); + { setDisplayCalEnabled(std::any_cast(value)); }, + []() { setDisplayCalEnabled(DisplayCalConfig{}.enabled); }}); + + struct DisplayCalGainDef { DisplayCalChannel channel; const char *name; const char *desc; }; + static const DisplayCalGainDef gainDefs[] = { + {DisplayCalChannel::Red, "Red gain", "White point correction red channel gain"}, + {DisplayCalChannel::Green, "Green gain", "White point correction green channel gain"}, + {DisplayCalChannel::Blue, "Blue gain", "White point correction blue channel gain"}, + }; + for (const auto &def : gainDefs) + { + const auto channel = def.channel; + displayItems.push_back( + new MenuItem{ListItemType::Generic, def.name, def.desc, displayCalGainValues(), displayCalGainLabels(), [channel]() -> std::any + { return getDisplayCalGain(channel); }, [channel](const std::any &value) + { setDisplayCalGain(channel, std::any_cast(value)); }, + [channel]() { resetDisplayCalGain(channel); }}); + } } if(deviceInfo.hasContrastSaturation()) @@ -1255,6 +1254,8 @@ int main(int argc, char *argv[]) ctx.menu->handleInput(ctx.dirty, appQuit); + displayCalTick(); + PWR_update(&ctx.dirty, &ctx.show_setting, nullptr, nullptr); int is_online = PWR_isOnline(); @@ -1332,6 +1333,8 @@ int main(int argc, char *argv[]) GFX_sync(); } + displayCalTick(true); + delete ctx.menu; delete appearanceMenu; delete systemMenu; diff --git a/workspace/tg5040/displaycal/displaycal.c b/workspace/tg5040/displaycal/displaycal.c index 35a00610d..5865f8fb3 100644 --- a/workspace/tg5040/displaycal/displaycal.c +++ b/workspace/tg5040/displaycal/displaycal.c @@ -19,29 +19,25 @@ typedef struct { int enabled; - int screen; - double strength; double red_gain; double green_gain; double blue_gain; - char format[4]; } DisplayCalConfig; static void init_config(DisplayCalConfig *config) { config->enabled = DISPLAYCAL_DEFAULT_ENABLED; - config->screen = DISPLAYCAL_DEFAULT_SCREEN; - config->strength = DISPLAYCAL_DEFAULT_STRENGTH; config->red_gain = DISPLAYCAL_DEFAULT_RED_GAIN; config->green_gain = DISPLAYCAL_DEFAULT_GREEN_GAIN; config->blue_gain = DISPLAYCAL_DEFAULT_BLUE_GAIN; - strcpy(config->format, DISPLAYCAL_DEFAULT_FORMAT); } -static double clamp_unit(double v) { - if (v < 0.0) - return 0.0; - if (v > 1.0) +static double clamp_gain(double v) { + if (isnan(v)) return 1.0; + if (v < (double)DISPLAYCAL_GAIN_MIN / DISPLAYCAL_GAIN_SCALE) + return (double)DISPLAYCAL_GAIN_MIN / DISPLAYCAL_GAIN_SCALE; + if (v > (double)DISPLAYCAL_GAIN_MAX / DISPLAYCAL_GAIN_SCALE) + return (double)DISPLAYCAL_GAIN_MAX / DISPLAYCAL_GAIN_SCALE; return v; } @@ -67,68 +63,17 @@ static double linear_to_srgb(double c) { return 1.055 * pow(c, 1.0 / 2.4) - 0.055; } -static double mix_gain(double gain, double strength) { - return 1.0 + (gain - 1.0) * clamp_unit(strength); -} - -static char *trim_token(char *s) { - while (*s == ' ' || *s == '\t' || *s == '\n' || *s == '\r') - s++; - - char *end = s + strlen(s); - while (end > s) { - char c = end[-1]; - if (c != ' ' && c != '\t' && c != '\n' && c != '\r') - break; - *--end = '\0'; - } - - return s; -} - static int streq(const char *a, const char *b) { return strcmp(a, b) == 0; } -static int valid_format(const char *format) { - return streq(format, "rgb") || - streq(format, "bgr") || - streq(format, "grb") || - streq(format, "gbr") || - streq(format, "rbg") || - streq(format, "brg"); -} - -static uint32_t pack_rgb(int r, int g, int b, const char *format) { - uint32_t rr = (uint32_t)r & 0xff; - uint32_t gg = (uint32_t)g & 0xff; - uint32_t bb = (uint32_t)b & 0xff; - - if (streq(format, "bgr")) - return (bb << 16) | (gg << 8) | rr; - if (streq(format, "grb")) - return (gg << 16) | (rr << 8) | bb; - if (streq(format, "gbr")) - return (gg << 16) | (bb << 8) | rr; - if (streq(format, "rbg")) - return (rr << 16) | (bb << 8) | gg; - if (streq(format, "brg")) - return (bb << 16) | (rr << 8) | gg; - - return (rr << 16) | (gg << 8) | bb; -} - static void fill_linear_gain_table(uint32_t table[DISPLAYCAL_LUT_ENTRIES], const DisplayCalConfig *config) { - double red_gain = mix_gain(config->red_gain, config->strength); - double green_gain = mix_gain(config->green_gain, config->strength); - double blue_gain = mix_gain(config->blue_gain, config->strength); - for (int i = 0; i < DISPLAYCAL_LUT_ENTRIES; i++) { double linear = srgb_to_linear(i / 255.0); - int r = clamp_u8(linear_to_srgb(linear * red_gain) * 255.0); - int g = clamp_u8(linear_to_srgb(linear * green_gain) * 255.0); - int b = clamp_u8(linear_to_srgb(linear * blue_gain) * 255.0); - table[i] = pack_rgb(r, g, b, config->format); + uint32_t r = clamp_u8(linear_to_srgb(linear * config->red_gain) * 255.0); + uint32_t g = clamp_u8(linear_to_srgb(linear * config->green_gain) * 255.0); + uint32_t b = clamp_u8(linear_to_srgb(linear * config->blue_gain) * 255.0); + table[i] = (r << 16) | (g << 8) | b; } } @@ -147,32 +92,21 @@ static void load_config(DisplayCalConfig *config, const char *path) { char line[160]; while (fgets(line, sizeof(line), file)) { - char *comment = strchr(line, '#'); - if (comment) - *comment = '\0'; - - char *sep = strchr(line, '='); - if (!sep) - continue; - - *sep = '\0'; - char *key = trim_token(line); - char *value = trim_token(sep + 1); - - if (streq(key, "enabled")) - config->enabled = atoi(value) ? 1 : 0; - else if (streq(key, "screen")) - config->screen = atoi(value); - else if (streq(key, "strength")) - config->strength = atof(value); - else if (streq(key, "red") || streq(key, "r") || streq(key, "red_gain")) - config->red_gain = atof(value); - else if (streq(key, "green") || streq(key, "g") || streq(key, "green_gain")) - config->green_gain = atof(value); - else if (streq(key, "blue") || streq(key, "b") || streq(key, "blue_gain")) - config->blue_gain = atof(value); - else if (streq(key, "format") && valid_format(value)) - snprintf(config->format, sizeof(config->format), "%s", value); + // discard the tail of an overlong line so it is not parsed as its own line + if (!strchr(line, '\n')) { + int c; + while ((c = fgetc(file)) != EOF && c != '\n') + ; + } + + if (strncmp(line, "enabled=", 8) == 0) + config->enabled = atoi(line + 8) ? 1 : 0; + else if (strncmp(line, "red=", 4) == 0) + config->red_gain = clamp_gain(atof(line + 4)); + else if (strncmp(line, "green=", 6) == 0) + config->green_gain = clamp_gain(atof(line + 6)); + else if (strncmp(line, "blue=", 5) == 0) + config->blue_gain = clamp_gain(atof(line + 5)); } fclose(file); @@ -241,11 +175,11 @@ static int apply_config(const DisplayCalConfig *config) { int ret = 0; if (!config->enabled) { - ret = reset_table_and_disable(fd, config->screen); + ret = reset_table_and_disable(fd, 0); } else { uint32_t table[DISPLAYCAL_LUT_ENTRIES]; fill_linear_gain_table(table, config); - ret = apply_table(fd, config->screen, table); + ret = apply_table(fd, 0, table); } close(fd); @@ -276,12 +210,9 @@ static int disable_screen(int screen) { static void print_status(const DisplayCalConfig *config) { printf("enabled=%d\n", config->enabled); - printf("screen=%d\n", config->screen); - printf("strength=%.6f\n", config->strength); - printf("red=%.16f\n", config->red_gain); - printf("green=%.16f\n", config->green_gain); - printf("blue=%.16f\n", config->blue_gain); - printf("format=%s\n", config->format); + printf("red=%.2f\n", config->red_gain); + printf("green=%.2f\n", config->green_gain); + printf("blue=%.2f\n", config->blue_gain); } static void usage(const char *argv0) { From 916bcaa338b548181c8a0c214d12b0eb630ae352 Mon Sep 17 00:00:00 2001 From: Prashant Vaibhav Date: Fri, 12 Jun 2026 03:39:37 +0200 Subject: [PATCH 06/27] fix(tg5040): re-apply Brick white point correction after resume The disp driver may reset the gamma LUT across suspend-to-RAM, so run displaycal apply at the start of the suspend script's wake path, gated the same way as the boot-time apply in launch.sh. --- skeleton/SYSTEM/tg5040/bin/suspend | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/skeleton/SYSTEM/tg5040/bin/suspend b/skeleton/SYSTEM/tg5040/bin/suspend index 593159423..1f2dcda71 100644 --- a/skeleton/SYSTEM/tg5040/bin/suspend +++ b/skeleton/SYSTEM/tg5040/bin/suspend @@ -40,6 +40,11 @@ before() { after() { >&2 echo "Resumed from suspend." + # The disp driver may reset the gamma LUT across suspend; re-apply correction. + if [ "${DEVICE:-}" = "brick" ] && [ -x "$SYSTEM_PATH/bin/displaycal.elf" ]; then + "$SYSTEM_PATH/bin/displaycal.elf" apply "$USERDATA_PATH/displaycal.cfg" >/dev/null 2>&1 || true + fi + # Run post-resume hooks in background immediately after wake. ( "$SYSTEM_PATH/bin/run_hooks.sh" post-resume.d ) >/dev/null 2>&1 & From d448387d5662b0239ca226db718a882149ddb1e5 Mon Sep 17 00:00:00 2001 From: Prashant Vaibhav Date: Sun, 14 Jun 2026 12:20:49 +0200 Subject: [PATCH 07/27] refactor(tg5040): simplify display calibration settings Persist Brick display calibration through the existing minuisettings.txt configuration path instead of maintaining a separate displaycal.cfg file and delayed dirty cache. The Settings UI now writes through CFG setters immediately and invokes displaycal.elf directly for live application. Simplify displaycal.elf to accept direct enable/disable commands with optional RGB gains, and update boot and resume hooks to reapply saved values through nextval.elf. This keeps boot, resume, and Settings using the same persisted source of truth. --- skeleton/SYSTEM/tg5040/bin/suspend | 37 +++- .../SYSTEM/tg5040/paks/MinUI.pak/launch.sh | 37 +++- workspace/all/common/config.c | 92 +++++++++ workspace/all/common/config.h | 21 +++ workspace/all/common/displaycal.h | 6 +- workspace/all/settings/settings.cpp | 177 ++++++------------ workspace/tg5040/displaycal/displaycal.c | 127 ++++--------- 7 files changed, 273 insertions(+), 224 deletions(-) diff --git a/skeleton/SYSTEM/tg5040/bin/suspend b/skeleton/SYSTEM/tg5040/bin/suspend index 1f2dcda71..a2e641bcd 100644 --- a/skeleton/SYSTEM/tg5040/bin/suspend +++ b/skeleton/SYSTEM/tg5040/bin/suspend @@ -13,6 +13,39 @@ sleep_retval= asound_state_dir=/tmp/asound-suspend +nextval_number() { + "$SYSTEM_PATH/bin/nextval.elf" "$1" | sed -n 's/.*: \([0-9]*\).*/\1/p' +} + +displaycal_gain() { + value=${1:-0} + printf '%d.%02d' "$((value / 100))" "$((value % 100))" +} + +apply_displaycal() { + if [ "${DEVICE:-}" != "brick" ] || [ ! -x "$SYSTEM_PATH/bin/displaycal.elf" ]; then + return + fi + + display_cal_enabled=$(nextval_number displayCalEnabled) + display_cal_enabled=${display_cal_enabled:-1} + if [ "$display_cal_enabled" -eq 0 ]; then + "$SYSTEM_PATH/bin/displaycal.elf" disable >/dev/null 2>&1 || true + return + fi + + display_cal_red=$(nextval_number displayCalRed) + display_cal_green=$(nextval_number displayCalGreen) + display_cal_blue=$(nextval_number displayCalBlue) + display_cal_red=${display_cal_red:-100} + display_cal_green=${display_cal_green:-100} + display_cal_blue=${display_cal_blue:-60} + "$SYSTEM_PATH/bin/displaycal.elf" enable \ + "$(displaycal_gain "$display_cal_red")" \ + "$(displaycal_gain "$display_cal_green")" \ + "$(displaycal_gain "$display_cal_blue")" >/dev/null 2>&1 || true +} + before() { >&2 echo "Preparing for suspend..." @@ -41,9 +74,7 @@ after() { >&2 echo "Resumed from suspend." # The disp driver may reset the gamma LUT across suspend; re-apply correction. - if [ "${DEVICE:-}" = "brick" ] && [ -x "$SYSTEM_PATH/bin/displaycal.elf" ]; then - "$SYSTEM_PATH/bin/displaycal.elf" apply "$USERDATA_PATH/displaycal.cfg" >/dev/null 2>&1 || true - fi + apply_displaycal # Run post-resume hooks in background immediately after wake. ( "$SYSTEM_PATH/bin/run_hooks.sh" post-resume.d ) >/dev/null 2>&1 & diff --git a/skeleton/SYSTEM/tg5040/paks/MinUI.pak/launch.sh b/skeleton/SYSTEM/tg5040/paks/MinUI.pak/launch.sh index 241c696b2..fe90040f9 100755 --- a/skeleton/SYSTEM/tg5040/paks/MinUI.pak/launch.sh +++ b/skeleton/SYSTEM/tg5040/paks/MinUI.pak/launch.sh @@ -103,9 +103,40 @@ syslogd -S export LD_LIBRARY_PATH=$SYSTEM_PATH/lib:/usr/trimui/lib:$LD_LIBRARY_PATH export PATH=$SYSTEM_PATH/bin:/usr/trimui/bin:$PATH -if [ "$DEVICE" = "brick" ] && [ -x "$SYSTEM_PATH/bin/displaycal.elf" ]; then - "$SYSTEM_PATH/bin/displaycal.elf" apply "$USERDATA_PATH/displaycal.cfg" > /dev/null 2>&1 -fi +nextval_number() { + "$SYSTEM_PATH/bin/nextval.elf" "$1" | sed -n 's/.*: \([0-9]*\).*/\1/p' +} + +displaycal_gain() { + value=${1:-0} + printf '%d.%02d' "$((value / 100))" "$((value % 100))" +} + +apply_displaycal() { + if [ "$DEVICE" != "brick" ] || [ ! -x "$SYSTEM_PATH/bin/displaycal.elf" ]; then + return + fi + + display_cal_enabled=$(nextval_number displayCalEnabled) + display_cal_enabled=${display_cal_enabled:-1} + if [ "$display_cal_enabled" -eq 0 ]; then + "$SYSTEM_PATH/bin/displaycal.elf" disable > /dev/null 2>&1 + return + fi + + display_cal_red=$(nextval_number displayCalRed) + display_cal_green=$(nextval_number displayCalGreen) + display_cal_blue=$(nextval_number displayCalBlue) + display_cal_red=${display_cal_red:-100} + display_cal_green=${display_cal_green:-100} + display_cal_blue=${display_cal_blue:-60} + "$SYSTEM_PATH/bin/displaycal.elf" enable \ + "$(displaycal_gain "$display_cal_red")" \ + "$(displaycal_gain "$display_cal_green")" \ + "$(displaycal_gain "$display_cal_blue")" > /dev/null 2>&1 +} + +apply_displaycal # leds_off echo 0 > /sys/class/led_anim/max_scale diff --git a/workspace/all/common/config.c b/workspace/all/common/config.c index ff5af1a62..f6560bee9 100644 --- a/workspace/all/common/config.c +++ b/workspace/all/common/config.c @@ -55,6 +55,10 @@ void CFG_defaults(NextUISettings *cfg) .gameSwitcherScaling = CFG_DEFAULT_GAMESWITCHERSCALING, .defaultView = CFG_DEFAULT_VIEW, .showQuickSwitcherUi = CFG_DEFAULT_SHOWQUICKWITCHERUI, + .displayCalEnabled = CFG_DEFAULT_DISPLAYCAL_ENABLED, + .displayCalRedGain = CFG_DEFAULT_DISPLAYCAL_RED_GAIN, + .displayCalGreenGain = CFG_DEFAULT_DISPLAYCAL_GREEN_GAIN, + .displayCalBlueGain = CFG_DEFAULT_DISPLAYCAL_BLUE_GAIN, .muteLeds = CFG_DEFAULT_MUTELEDS, @@ -297,6 +301,26 @@ void CFG_init(FontLoad_callback_t cb, ColorSet_callback_t ccb) CFG_setShowQuickswitcherUI(temp_value); continue; } + if (sscanf(line, "displayCalEnabled=%i", &temp_value) == 1) + { + settings.displayCalEnabled = (bool)temp_value; + continue; + } + if (sscanf(line, "displayCalRed=%i", &temp_value) == 1) + { + settings.displayCalRedGain = clamp(temp_value, DISPLAYCAL_GAIN_MIN, DISPLAYCAL_GAIN_MAX); + continue; + } + if (sscanf(line, "displayCalGreen=%i", &temp_value) == 1) + { + settings.displayCalGreenGain = clamp(temp_value, DISPLAYCAL_GAIN_MIN, DISPLAYCAL_GAIN_MAX); + continue; + } + if (sscanf(line, "displayCalBlue=%i", &temp_value) == 1) + { + settings.displayCalBlueGain = clamp(temp_value, DISPLAYCAL_GAIN_MIN, DISPLAYCAL_GAIN_MAX); + continue; + } if (sscanf(line, "wifiDiagnostics=%i", &temp_value) == 1) { CFG_setWifiDiagnostics(temp_value); @@ -805,6 +829,50 @@ void CFG_setShowQuickswitcherUI(bool on) CFG_sync(); } +bool CFG_getDisplayCalEnabled(void) +{ + return settings.displayCalEnabled; +} + +void CFG_setDisplayCalEnabled(bool enabled) +{ + settings.displayCalEnabled = enabled; + CFG_sync(); +} + +int CFG_getDisplayCalRedGain(void) +{ + return settings.displayCalRedGain; +} + +void CFG_setDisplayCalRedGain(int gain) +{ + settings.displayCalRedGain = clamp(gain, DISPLAYCAL_GAIN_MIN, DISPLAYCAL_GAIN_MAX); + CFG_sync(); +} + +int CFG_getDisplayCalGreenGain(void) +{ + return settings.displayCalGreenGain; +} + +void CFG_setDisplayCalGreenGain(int gain) +{ + settings.displayCalGreenGain = clamp(gain, DISPLAYCAL_GAIN_MIN, DISPLAYCAL_GAIN_MAX); + CFG_sync(); +} + +int CFG_getDisplayCalBlueGain(void) +{ + return settings.displayCalBlueGain; +} + +void CFG_setDisplayCalBlueGain(int gain) +{ + settings.displayCalBlueGain = clamp(gain, DISPLAYCAL_GAIN_MIN, DISPLAYCAL_GAIN_MAX); + CFG_sync(); +} + bool CFG_getWifiDiagnostics(void) { return settings.wifiDiagnostics; @@ -1240,6 +1308,22 @@ void CFG_get(const char *key, char *value) { sprintf(value, "%i", (int)(CFG_getShowQuickswitcherUI())); } + else if (strcmp(key, "displayCalEnabled") == 0) + { + sprintf(value, "%i", (int)(CFG_getDisplayCalEnabled())); + } + else if (strcmp(key, "displayCalRed") == 0) + { + sprintf(value, "%i", CFG_getDisplayCalRedGain()); + } + else if (strcmp(key, "displayCalGreen") == 0) + { + sprintf(value, "%i", CFG_getDisplayCalGreenGain()); + } + else if (strcmp(key, "displayCalBlue") == 0) + { + sprintf(value, "%i", CFG_getDisplayCalBlueGain()); + } else if (strcmp(key, "wifiDiagnostics") == 0) { sprintf(value, "%i", (int)(CFG_getWifiDiagnostics())); @@ -1374,6 +1458,10 @@ void CFG_sync(void) fprintf(file, "color5=0x%06X\n", settings.color5_255); fprintf(file, "color6=0x%06X\n", settings.color6_255); fprintf(file, "color7=0x%06X\n", settings.color7_255); + fprintf(file, "displayCalEnabled=%i\n", settings.displayCalEnabled); + fprintf(file, "displayCalRed=%i\n", settings.displayCalRedGain); + fprintf(file, "displayCalGreen=%i\n", settings.displayCalGreenGain); + fprintf(file, "displayCalBlue=%i\n", settings.displayCalBlueGain); fprintf(file, "radius=%i\n", settings.thumbRadius); fprintf(file, "showclock=%i\n", settings.showClock); fprintf(file, "clock24h=%i\n", settings.clock24h); @@ -1441,6 +1529,10 @@ void CFG_print(void) printf("\t\"color5\": \"0x%06X\",\n", settings.color5_255); printf("\t\"color6\": \"0x%06X\",\n", settings.color6_255); printf("\t\"color7\": \"0x%06X\",\n", settings.color7_255); + printf("\t\"displayCalEnabled\": %i,\n", settings.displayCalEnabled); + printf("\t\"displayCalRed\": %i,\n", settings.displayCalRedGain); + printf("\t\"displayCalGreen\": %i,\n", settings.displayCalGreenGain); + printf("\t\"displayCalBlue\": %i,\n", settings.displayCalBlueGain); printf("\t\"radius\": %i,\n", settings.thumbRadius); printf("\t\"showclock\": %i,\n", settings.showClock); printf("\t\"clock24h\": %i,\n", settings.clock24h); diff --git a/workspace/all/common/config.h b/workspace/all/common/config.h index d2c56b94b..0f856b88b 100644 --- a/workspace/all/common/config.h +++ b/workspace/all/common/config.h @@ -4,6 +4,8 @@ #include #include +#include "displaycal.h" + // portability, deprecated extern uint32_t THEME_COLOR1_255; extern uint32_t THEME_COLOR2_255; @@ -119,6 +121,12 @@ typedef struct bool showQuickSwitcherUi; int defaultView; + // Display calibration + bool displayCalEnabled; + int displayCalRedGain; + int displayCalGreenGain; + int displayCalBlueGain; + // Mute switch bool muteLeds; @@ -218,6 +226,10 @@ typedef struct #define CFG_DEFAULT_BLUETOOTH_MAXRATE 48000 #define CFG_DEFAULT_NTP false #define CFG_DEFAULT_TIMEZONE 320 // Europe/Berlin +#define CFG_DEFAULT_DISPLAYCAL_ENABLED (DISPLAYCAL_DEFAULT_ENABLED != 0) +#define CFG_DEFAULT_DISPLAYCAL_RED_GAIN ((int)(DISPLAYCAL_DEFAULT_RED_GAIN * DISPLAYCAL_GAIN_SCALE + 0.5)) +#define CFG_DEFAULT_DISPLAYCAL_GREEN_GAIN ((int)(DISPLAYCAL_DEFAULT_GREEN_GAIN * DISPLAYCAL_GAIN_SCALE + 0.5)) +#define CFG_DEFAULT_DISPLAYCAL_BLUE_GAIN ((int)(DISPLAYCAL_DEFAULT_BLUE_GAIN * DISPLAYCAL_GAIN_SCALE + 0.5)) // Notification defaults #define CFG_DEFAULT_NOTIFY_MANUAL_SAVE true @@ -343,6 +355,15 @@ void CFG_setDefaultView(int view); // Quick switcher UI painting on/off bool CFG_getShowQuickswitcherUI(void); void CFG_setShowQuickswitcherUI(bool on); +// Display calibration for devices that support it. +bool CFG_getDisplayCalEnabled(void); +void CFG_setDisplayCalEnabled(bool enabled); +int CFG_getDisplayCalRedGain(void); +void CFG_setDisplayCalRedGain(int gain); +int CFG_getDisplayCalGreenGain(void); +void CFG_setDisplayCalGreenGain(int gain); +int CFG_getDisplayCalBlueGain(void); +void CFG_setDisplayCalBlueGain(int gain); // WiFi diagnostic logging on/off bool CFG_getWifiDiagnostics(void); void CFG_setWifiDiagnostics(bool on); diff --git a/workspace/all/common/displaycal.h b/workspace/all/common/displaycal.h index 3f509c281..fc6f86ed6 100644 --- a/workspace/all/common/displaycal.h +++ b/workspace/all/common/displaycal.h @@ -1,12 +1,10 @@ #ifndef DISPLAYCAL_H #define DISPLAYCAL_H -#define DISPLAYCAL_CONFIG_FILENAME "displaycal.cfg" - #define DISPLAYCAL_DEFAULT_ENABLED 1 #define DISPLAYCAL_DEFAULT_RED_GAIN 1.0 -#define DISPLAYCAL_DEFAULT_GREEN_GAIN 0.92 -#define DISPLAYCAL_DEFAULT_BLUE_GAIN 0.58 +#define DISPLAYCAL_DEFAULT_GREEN_GAIN 1.0 +#define DISPLAYCAL_DEFAULT_BLUE_GAIN 0.6 #define DISPLAYCAL_GAIN_SCALE 100 #define DISPLAYCAL_GAIN_MIN 0 diff --git a/workspace/all/settings/settings.cpp b/workspace/all/settings/settings.cpp index cda70b2b7..115fc5edf 100644 --- a/workspace/all/settings/settings.cpp +++ b/workspace/all/settings/settings.cpp @@ -10,7 +10,6 @@ extern "C" #include "ra_sync.h" } -#include #include #include #include @@ -180,14 +179,7 @@ static const std::vector ra_sort_labels = { }; namespace { - constexpr const char *DISPLAYCAL_CONFIG_PATH = USERDATA_PATH "/" DISPLAYCAL_CONFIG_FILENAME; constexpr const char *DISPLAYCAL_COMMAND = BIN_PATH "/displaycal.elf"; - constexpr Uint32 DISPLAYCAL_APPLY_DELAY_MS = 250; - - constexpr int displayCalGainToValue(double value) - { - return (int)(value * DISPLAYCAL_GAIN_SCALE + 0.5); - } enum class DisplayCalChannel { @@ -196,44 +188,11 @@ namespace { Blue }; - struct DisplayCalConfig - { - bool enabled = DISPLAYCAL_DEFAULT_ENABLED != 0; - int red = displayCalGainToValue(DISPLAYCAL_DEFAULT_RED_GAIN); - int green = displayCalGainToValue(DISPLAYCAL_DEFAULT_GREEN_GAIN); - int blue = displayCalGainToValue(DISPLAYCAL_DEFAULT_BLUE_GAIN); - }; - - static int DisplayCalConfig::*displayCalField(DisplayCalChannel channel) - { - switch (channel) - { - case DisplayCalChannel::Red: - return &DisplayCalConfig::red; - case DisplayCalChannel::Green: - return &DisplayCalConfig::green; - case DisplayCalChannel::Blue: - return &DisplayCalConfig::blue; - } - return &DisplayCalConfig::red; - } - static int clampDisplayCalGain(int value) { return std::max(DISPLAYCAL_GAIN_MIN, std::min(DISPLAYCAL_GAIN_MAX, value)); } - static int parseDisplayCalGain(const char *value) - { - // clamp as a double: out-of-range double-to-int conversion is UB - double gain = std::atof(value); - if (std::isnan(gain)) - gain = 1.0; - gain = std::max((double)DISPLAYCAL_GAIN_MIN / DISPLAYCAL_GAIN_SCALE, - std::min((double)DISPLAYCAL_GAIN_MAX / DISPLAYCAL_GAIN_SCALE, gain)); - return displayCalGainToValue(gain); - } - static std::string formatDisplayCalGain(int value) { char label[8]; @@ -263,103 +222,85 @@ namespace { return labels; } - static DisplayCalConfig loadDisplayCalConfig() + static bool getDisplayCalEnabled() { - DisplayCalConfig config; - std::ifstream file(DISPLAYCAL_CONFIG_PATH); - if (!file) - return config; - - std::string line; - while (std::getline(file, line)) - { - if (line.rfind("enabled=", 0) == 0) - config.enabled = std::atoi(line.c_str() + 8) != 0; - else if (line.rfind("red=", 0) == 0) - config.red = parseDisplayCalGain(line.c_str() + 4); - else if (line.rfind("green=", 0) == 0) - config.green = parseDisplayCalGain(line.c_str() + 6); - else if (line.rfind("blue=", 0) == 0) - config.blue = parseDisplayCalGain(line.c_str() + 5); - } - - return config; + return CFG_getDisplayCalEnabled(); } - static void saveDisplayCalConfig(const DisplayCalConfig &config) + static int getDisplayCalGain(DisplayCalChannel channel) { - std::ofstream file(DISPLAYCAL_CONFIG_PATH, std::ios::trunc); - if (!file) - return; - - file << "enabled=" << (config.enabled ? 1 : 0) << "\n"; - file << "red=" << formatDisplayCalGain(config.red) << "\n"; - file << "green=" << formatDisplayCalGain(config.green) << "\n"; - file << "blue=" << formatDisplayCalGain(config.blue) << "\n"; + switch (channel) + { + case DisplayCalChannel::Red: + return CFG_getDisplayCalRedGain(); + case DisplayCalChannel::Green: + return CFG_getDisplayCalGreenGain(); + case DisplayCalChannel::Blue: + return CFG_getDisplayCalBlueGain(); + } + return CFG_DEFAULT_DISPLAYCAL_RED_GAIN; } - // Setters only update this cache and mark it dirty; displayCalTick() persists - // and applies once the user pauses, so scrubbing a gain slider doesn't fork - // displaycal.elf and rewrite the config file on every key-repeat tick. - static DisplayCalConfig &displayCalConfig() + static int defaultDisplayCalGain(DisplayCalChannel channel) { - static DisplayCalConfig config = loadDisplayCalConfig(); - return config; + switch (channel) + { + case DisplayCalChannel::Red: + return CFG_DEFAULT_DISPLAYCAL_RED_GAIN; + case DisplayCalChannel::Green: + return CFG_DEFAULT_DISPLAYCAL_GREEN_GAIN; + case DisplayCalChannel::Blue: + return CFG_DEFAULT_DISPLAYCAL_BLUE_GAIN; + } + return CFG_DEFAULT_DISPLAYCAL_RED_GAIN; } - static bool displayCalDirty = false; - static Uint32 displayCalLastChange = 0; - - static void markDisplayCalDirty() + static void applyDisplayCalFromSettings() { - displayCalDirty = true; - displayCalLastChange = SDL_GetTicks(); - } + std::string cmd; + if (CFG_getDisplayCalEnabled()) + { + cmd = std::string("\"") + DISPLAYCAL_COMMAND + "\" enable " + + formatDisplayCalGain(CFG_getDisplayCalRedGain()) + " " + + formatDisplayCalGain(CFG_getDisplayCalGreenGain()) + " " + + formatDisplayCalGain(CFG_getDisplayCalBlueGain()) + " > /dev/null 2>&1"; + } + else + { + cmd = std::string("\"") + DISPLAYCAL_COMMAND + "\" disable > /dev/null 2>&1"; + } - static bool getDisplayCalEnabled() - { - return displayCalConfig().enabled; + int ret = std::system(cmd.c_str()); + if (ret != 0) + LOG_error("displaycal apply failed (%d)\n", ret); } static void setDisplayCalEnabled(bool enabled) { - displayCalConfig().enabled = enabled; - markDisplayCalDirty(); - } - - static int getDisplayCalGain(DisplayCalChannel channel) - { - return displayCalConfig().*displayCalField(channel); + CFG_setDisplayCalEnabled(enabled); + applyDisplayCalFromSettings(); } static void setDisplayCalGain(DisplayCalChannel channel, int value) { - displayCalConfig().*displayCalField(channel) = clampDisplayCalGain(value); - markDisplayCalDirty(); + switch (channel) + { + case DisplayCalChannel::Red: + CFG_setDisplayCalRedGain(value); + break; + case DisplayCalChannel::Green: + CFG_setDisplayCalGreenGain(value); + break; + case DisplayCalChannel::Blue: + CFG_setDisplayCalBlueGain(value); + break; + } + applyDisplayCalFromSettings(); } static void resetDisplayCalGain(DisplayCalChannel channel) { - setDisplayCalGain(channel, DisplayCalConfig{}.*displayCalField(channel)); - } - - static void applyDisplayCalConfig() - { - std::string cmd = std::string("\"") + DISPLAYCAL_COMMAND + "\" apply \"" + DISPLAYCAL_CONFIG_PATH + "\" > /dev/null"; - int ret = std::system(cmd.c_str()); - if (ret != 0) - LOG_error("displaycal apply failed (%d)\n", ret); - } - - static void displayCalTick(bool force = false) - { - if (!displayCalDirty) - return; - if (!force && SDL_GetTicks() - displayCalLastChange < DISPLAYCAL_APPLY_DELAY_MS) - return; - displayCalDirty = false; - saveDisplayCalConfig(displayCalConfig()); - applyDisplayCalConfig(); + setDisplayCalGain(channel, defaultDisplayCalGain(channel)); } struct ColorDef { int id; const char *name; const char *desc; uint32_t defaultColor; }; @@ -675,7 +616,7 @@ int main(int argc, char *argv[]) new MenuItem{ListItemType::Generic, "White point correction", "Corrects the Brick display white point", {false, true}, on_off, []() -> std::any { return getDisplayCalEnabled(); }, [](const std::any &value) { setDisplayCalEnabled(std::any_cast(value)); }, - []() { setDisplayCalEnabled(DisplayCalConfig{}.enabled); }}); + []() { setDisplayCalEnabled(CFG_DEFAULT_DISPLAYCAL_ENABLED); }}); struct DisplayCalGainDef { DisplayCalChannel channel; const char *name; const char *desc; }; static const DisplayCalGainDef gainDefs[] = { @@ -1254,8 +1195,6 @@ int main(int argc, char *argv[]) ctx.menu->handleInput(ctx.dirty, appQuit); - displayCalTick(); - PWR_update(&ctx.dirty, &ctx.show_setting, nullptr, nullptr); int is_online = PWR_isOnline(); @@ -1333,8 +1272,6 @@ int main(int argc, char *argv[]) GFX_sync(); } - displayCalTick(true); - delete ctx.menu; delete appearanceMenu; delete systemMenu; diff --git a/workspace/tg5040/displaycal/displaycal.c b/workspace/tg5040/displaycal/displaycal.c index 5865f8fb3..faf51300a 100644 --- a/workspace/tg5040/displaycal/displaycal.c +++ b/workspace/tg5040/displaycal/displaycal.c @@ -18,17 +18,15 @@ #define DISPLAYCAL_LUT_BYTES (DISPLAYCAL_LUT_ENTRIES * 4) typedef struct { - int enabled; double red_gain; double green_gain; double blue_gain; -} DisplayCalConfig; +} DisplayCalGains; -static void init_config(DisplayCalConfig *config) { - config->enabled = DISPLAYCAL_DEFAULT_ENABLED; - config->red_gain = DISPLAYCAL_DEFAULT_RED_GAIN; - config->green_gain = DISPLAYCAL_DEFAULT_GREEN_GAIN; - config->blue_gain = DISPLAYCAL_DEFAULT_BLUE_GAIN; +static void init_gains(DisplayCalGains *gains) { + gains->red_gain = DISPLAYCAL_DEFAULT_RED_GAIN; + gains->green_gain = DISPLAYCAL_DEFAULT_GREEN_GAIN; + gains->blue_gain = DISPLAYCAL_DEFAULT_BLUE_GAIN; } static double clamp_gain(double v) { @@ -67,12 +65,12 @@ static int streq(const char *a, const char *b) { return strcmp(a, b) == 0; } -static void fill_linear_gain_table(uint32_t table[DISPLAYCAL_LUT_ENTRIES], const DisplayCalConfig *config) { +static void fill_linear_gain_table(uint32_t table[DISPLAYCAL_LUT_ENTRIES], const DisplayCalGains *gains) { for (int i = 0; i < DISPLAYCAL_LUT_ENTRIES; i++) { double linear = srgb_to_linear(i / 255.0); - uint32_t r = clamp_u8(linear_to_srgb(linear * config->red_gain) * 255.0); - uint32_t g = clamp_u8(linear_to_srgb(linear * config->green_gain) * 255.0); - uint32_t b = clamp_u8(linear_to_srgb(linear * config->blue_gain) * 255.0); + uint32_t r = clamp_u8(linear_to_srgb(linear * gains->red_gain) * 255.0); + uint32_t g = clamp_u8(linear_to_srgb(linear * gains->green_gain) * 255.0); + uint32_t b = clamp_u8(linear_to_srgb(linear * gains->blue_gain) * 255.0); table[i] = (r << 16) | (g << 8) | b; } } @@ -82,36 +80,6 @@ static void fill_identity_table(uint32_t table[DISPLAYCAL_LUT_ENTRIES]) { table[i] = ((uint32_t)i << 16) | ((uint32_t)i << 8) | (uint32_t)i; } -static void load_config(DisplayCalConfig *config, const char *path) { - if (!path || !*path) - return; - - FILE *file = fopen(path, "r"); - if (!file) - return; - - char line[160]; - while (fgets(line, sizeof(line), file)) { - // discard the tail of an overlong line so it is not parsed as its own line - if (!strchr(line, '\n')) { - int c; - while ((c = fgetc(file)) != EOF && c != '\n') - ; - } - - if (strncmp(line, "enabled=", 8) == 0) - config->enabled = atoi(line + 8) ? 1 : 0; - else if (strncmp(line, "red=", 4) == 0) - config->red_gain = clamp_gain(atof(line + 4)); - else if (strncmp(line, "green=", 6) == 0) - config->green_gain = clamp_gain(atof(line + 6)); - else if (strncmp(line, "blue=", 5) == 0) - config->blue_gain = clamp_gain(atof(line + 5)); - } - - fclose(file); -} - static int open_disp(void) { int fd = open("/dev/disp", O_RDWR); if (fd < 0) @@ -168,32 +136,14 @@ static int reset_table_and_disable(int fd, int screen) { return disable_gamma(fd, screen); } -static int apply_config(const DisplayCalConfig *config) { - int fd = open_disp(); - if (fd < 0) - return -1; - - int ret = 0; - if (!config->enabled) { - ret = reset_table_and_disable(fd, 0); - } else { - uint32_t table[DISPLAYCAL_LUT_ENTRIES]; - fill_linear_gain_table(table, config); - ret = apply_table(fd, 0, table); - } - - close(fd); - return ret; -} - -static int apply_identity(int screen) { +static int apply_gains(const DisplayCalGains *gains) { int fd = open_disp(); if (fd < 0) return -1; uint32_t table[DISPLAYCAL_LUT_ENTRIES]; - fill_identity_table(table); - int ret = apply_table(fd, screen, table); + fill_linear_gain_table(table, gains); + int ret = apply_table(fd, 0, table); close(fd); return ret; } @@ -208,21 +158,12 @@ static int disable_screen(int screen) { return ret; } -static void print_status(const DisplayCalConfig *config) { - printf("enabled=%d\n", config->enabled); - printf("red=%.2f\n", config->red_gain); - printf("green=%.2f\n", config->green_gain); - printf("blue=%.2f\n", config->blue_gain); -} - static void usage(const char *argv0) { fprintf(stderr, "Usage:\n" - " %s apply [config]\n" - " %s status [config]\n" - " %s disable [screen]\n" - " %s identity [screen]\n", - argv0, argv0, argv0, argv0); + " %s enable [red] [green] [blue]\n" + " %s disable\n", + argv0, argv0); } int main(int argc, char **argv) { @@ -231,31 +172,29 @@ int main(int argc, char **argv) { return 2; } - if (streq(argv[1], "apply")) { - DisplayCalConfig config; - init_config(&config); - if (argc >= 3) - load_config(&config, argv[2]); - return apply_config(&config) < 0 ? 1 : 0; - } + if (streq(argv[1], "enable")) { + if (argc > 5) { + usage(argv[0]); + return 2; + } - if (streq(argv[1], "status")) { - DisplayCalConfig config; - init_config(&config); + DisplayCalGains gains; + init_gains(&gains); if (argc >= 3) - load_config(&config, argv[2]); - print_status(&config); - return 0; + gains.red_gain = clamp_gain(atof(argv[2])); + if (argc >= 4) + gains.green_gain = clamp_gain(atof(argv[3])); + if (argc >= 5) + gains.blue_gain = clamp_gain(atof(argv[4])); + return apply_gains(&gains) < 0 ? 1 : 0; } if (streq(argv[1], "disable")) { - int screen = argc >= 3 ? atoi(argv[2]) : 0; - return disable_screen(screen) < 0 ? 1 : 0; - } - - if (streq(argv[1], "identity")) { - int screen = argc >= 3 ? atoi(argv[2]) : 0; - return apply_identity(screen) < 0 ? 1 : 0; + if (argc != 2) { + usage(argv[0]); + return 2; + } + return disable_screen(0) < 0 ? 1 : 0; } usage(argv[0]); From fddf82d272133413fdc926f9bc3b9a084f2211c9 Mon Sep 17 00:00:00 2001 From: Prashant Vaibhav Date: Sun, 14 Jun 2026 12:23:49 +0200 Subject: [PATCH 08/27] refactor(tg5040): stop reapplying display calibration on resume There is currently no evidence that suspend/resume clears the display gamma LUT, and the suspend script does not reapply other display settings either. So for now, do not duplicate the boot-time displaycal parsing and application in the resume path. If we later find that the LUT really is lost across resume, we can add this back with that evidence instead of carrying the extra code preemptively. --- skeleton/SYSTEM/tg5040/bin/suspend | 36 ------------------------------ 1 file changed, 36 deletions(-) diff --git a/skeleton/SYSTEM/tg5040/bin/suspend b/skeleton/SYSTEM/tg5040/bin/suspend index a2e641bcd..593159423 100644 --- a/skeleton/SYSTEM/tg5040/bin/suspend +++ b/skeleton/SYSTEM/tg5040/bin/suspend @@ -13,39 +13,6 @@ sleep_retval= asound_state_dir=/tmp/asound-suspend -nextval_number() { - "$SYSTEM_PATH/bin/nextval.elf" "$1" | sed -n 's/.*: \([0-9]*\).*/\1/p' -} - -displaycal_gain() { - value=${1:-0} - printf '%d.%02d' "$((value / 100))" "$((value % 100))" -} - -apply_displaycal() { - if [ "${DEVICE:-}" != "brick" ] || [ ! -x "$SYSTEM_PATH/bin/displaycal.elf" ]; then - return - fi - - display_cal_enabled=$(nextval_number displayCalEnabled) - display_cal_enabled=${display_cal_enabled:-1} - if [ "$display_cal_enabled" -eq 0 ]; then - "$SYSTEM_PATH/bin/displaycal.elf" disable >/dev/null 2>&1 || true - return - fi - - display_cal_red=$(nextval_number displayCalRed) - display_cal_green=$(nextval_number displayCalGreen) - display_cal_blue=$(nextval_number displayCalBlue) - display_cal_red=${display_cal_red:-100} - display_cal_green=${display_cal_green:-100} - display_cal_blue=${display_cal_blue:-60} - "$SYSTEM_PATH/bin/displaycal.elf" enable \ - "$(displaycal_gain "$display_cal_red")" \ - "$(displaycal_gain "$display_cal_green")" \ - "$(displaycal_gain "$display_cal_blue")" >/dev/null 2>&1 || true -} - before() { >&2 echo "Preparing for suspend..." @@ -73,9 +40,6 @@ before() { after() { >&2 echo "Resumed from suspend." - # The disp driver may reset the gamma LUT across suspend; re-apply correction. - apply_displaycal - # Run post-resume hooks in background immediately after wake. ( "$SYSTEM_PATH/bin/run_hooks.sh" post-resume.d ) >/dev/null 2>&1 & From fb1ef8734a533adcf3ce2df2d71661d9a1d8e00f Mon Sep 17 00:00:00 2001 From: Prashant Vaibhav Date: Sun, 14 Jun 2026 12:26:24 +0200 Subject: [PATCH 09/27] fix(tg5040): adjust display calibration defaults Change the default display calibration gains from 1.0/1.0/0.6 to 1.0/0.92/0.58 in the shared display calibration defaults. Update the tg5040 launch fallback values to match the scaled minuisettings.txt representation so boot-time application uses the same defaults when settings are missing. --- skeleton/SYSTEM/tg5040/paks/MinUI.pak/launch.sh | 4 ++-- workspace/all/common/displaycal.h | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/skeleton/SYSTEM/tg5040/paks/MinUI.pak/launch.sh b/skeleton/SYSTEM/tg5040/paks/MinUI.pak/launch.sh index fe90040f9..d24ef04fa 100755 --- a/skeleton/SYSTEM/tg5040/paks/MinUI.pak/launch.sh +++ b/skeleton/SYSTEM/tg5040/paks/MinUI.pak/launch.sh @@ -128,8 +128,8 @@ apply_displaycal() { display_cal_green=$(nextval_number displayCalGreen) display_cal_blue=$(nextval_number displayCalBlue) display_cal_red=${display_cal_red:-100} - display_cal_green=${display_cal_green:-100} - display_cal_blue=${display_cal_blue:-60} + display_cal_green=${display_cal_green:-92} + display_cal_blue=${display_cal_blue:-58} "$SYSTEM_PATH/bin/displaycal.elf" enable \ "$(displaycal_gain "$display_cal_red")" \ "$(displaycal_gain "$display_cal_green")" \ diff --git a/workspace/all/common/displaycal.h b/workspace/all/common/displaycal.h index fc6f86ed6..2dbd0674c 100644 --- a/workspace/all/common/displaycal.h +++ b/workspace/all/common/displaycal.h @@ -3,8 +3,8 @@ #define DISPLAYCAL_DEFAULT_ENABLED 1 #define DISPLAYCAL_DEFAULT_RED_GAIN 1.0 -#define DISPLAYCAL_DEFAULT_GREEN_GAIN 1.0 -#define DISPLAYCAL_DEFAULT_BLUE_GAIN 0.6 +#define DISPLAYCAL_DEFAULT_GREEN_GAIN 0.92 +#define DISPLAYCAL_DEFAULT_BLUE_GAIN 0.58 #define DISPLAYCAL_GAIN_SCALE 100 #define DISPLAYCAL_GAIN_MIN 0 From 69fc10a3af360a5a62f2fbef3bac268011c13cb8 Mon Sep 17 00:00:00 2001 From: Prashant Vaibhav Date: Sun, 14 Jun 2026 12:27:51 +0200 Subject: [PATCH 10/27] feat(tg5040): add displaycal version output Add a 1.0.0 version string and --version handling to displaycal. Expand the usage text so it documents the supported commands, acceptable gain range, and suggested Brick RGB gain values, while keeping unknown arguments on the usage path. --- workspace/tg5040/displaycal/displaycal.c | 25 ++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/workspace/tg5040/displaycal/displaycal.c b/workspace/tg5040/displaycal/displaycal.c index faf51300a..1b03d1278 100644 --- a/workspace/tg5040/displaycal/displaycal.c +++ b/workspace/tg5040/displaycal/displaycal.c @@ -14,6 +14,7 @@ #define DISP_LCD_GAMMA_CORRECTION_ENABLE 0x10c #define DISP_LCD_GAMMA_CORRECTION_DISABLE 0x10d +#define DISPLAYCAL_VERSION "1.0.0" #define DISPLAYCAL_LUT_ENTRIES 256 #define DISPLAYCAL_LUT_BYTES (DISPLAYCAL_LUT_ENTRIES * 4) @@ -160,10 +161,21 @@ static int disable_screen(int screen) { static void usage(const char *argv0) { fprintf(stderr, + "displaycal %s\n" "Usage:\n" " %s enable [red] [green] [blue]\n" - " %s disable\n", - argv0, argv0); + " %s disable\n" + " %s --version\n" + "\n" + "Gain range: %.2f to %.2f\n" + "Suggested Brick gains: %.2f %.2f %.2f\n", + DISPLAYCAL_VERSION, + argv0, argv0, argv0, + (double)DISPLAYCAL_GAIN_MIN / DISPLAYCAL_GAIN_SCALE, + (double)DISPLAYCAL_GAIN_MAX / DISPLAYCAL_GAIN_SCALE, + DISPLAYCAL_DEFAULT_RED_GAIN, + DISPLAYCAL_DEFAULT_GREEN_GAIN, + DISPLAYCAL_DEFAULT_BLUE_GAIN); } int main(int argc, char **argv) { @@ -172,6 +184,15 @@ int main(int argc, char **argv) { return 2; } + if (streq(argv[1], "--version")) { + if (argc != 2) { + usage(argv[0]); + return 2; + } + printf("displaycal %s\n", DISPLAYCAL_VERSION); + return 0; + } + if (streq(argv[1], "enable")) { if (argc > 5) { usage(argv[0]); From 460b62ab5f633f9f60f0855ddf54cf73cdf87aac Mon Sep 17 00:00:00 2001 From: Prashant Vaibhav Date: Sun, 14 Jun 2026 12:31:04 +0200 Subject: [PATCH 11/27] docs(tg5040): describe displaycal purpose Bump displaycal to 1.0.1 and add a short usage description explaining what it does. --- workspace/tg5040/displaycal/displaycal.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/workspace/tg5040/displaycal/displaycal.c b/workspace/tg5040/displaycal/displaycal.c index 1b03d1278..8dc5796ab 100644 --- a/workspace/tg5040/displaycal/displaycal.c +++ b/workspace/tg5040/displaycal/displaycal.c @@ -14,7 +14,7 @@ #define DISP_LCD_GAMMA_CORRECTION_ENABLE 0x10c #define DISP_LCD_GAMMA_CORRECTION_DISABLE 0x10d -#define DISPLAYCAL_VERSION "1.0.0" +#define DISPLAYCAL_VERSION "1.0.1" #define DISPLAYCAL_LUT_ENTRIES 256 #define DISPLAYCAL_LUT_BYTES (DISPLAYCAL_LUT_ENTRIES * 4) @@ -162,6 +162,8 @@ static int disable_screen(int screen) { static void usage(const char *argv0) { fprintf(stderr, "displaycal %s\n" + "Adjust the LCD panel white point in hardware with zero performance cost.\n" + "\n" "Usage:\n" " %s enable [red] [green] [blue]\n" " %s disable\n" From 06b0ae9b871f93cec5e06c59acd7ba9168c2ba20 Mon Sep 17 00:00:00 2001 From: Prashant Vaibhav Date: Sun, 14 Jun 2026 16:42:44 +0200 Subject: [PATCH 12/27] refactor(tg5040): integrate display calibration with msettings Move display calibration LUT handling into a shared library used directly by libmsettings, and persist the white point settings in the tg5040 msettings state alongside the existing display controls. Reapply calibration through the existing SetMute display replay path so the LUT is restored whenever brightness, color temperature, contrast, saturation, and exposure are replayed after resume-sensitive display updates. Remove the standalone displaycal CLI and the launch-script/nextval plumbing because runtime no longer needs to parse separate configuration or fork a helper. Settings now read and write the new libmsettings getters/setters directly, with Brick-only UI exposure and raw hardware application still guarded by DEVICE=brick. --- .../SYSTEM/tg5040/paks/MinUI.pak/launch.sh | 35 ----- workspace/all/common/config.c | 93 ------------- workspace/all/common/config.h | 22 --- .../displaycal => all/common}/displaycal.c | 129 +++++------------- workspace/all/common/displaycal.h | 34 ++++- workspace/all/settings/settings.cpp | 56 +++----- workspace/makefile | 2 - workspace/tg5040/displaycal/makefile | 35 ----- workspace/tg5040/libmsettings/makefile | 11 +- workspace/tg5040/libmsettings/msettings.c | 107 ++++++++++++++- workspace/tg5040/libmsettings/msettings.h | 15 ++ 11 files changed, 210 insertions(+), 329 deletions(-) rename workspace/{tg5040/displaycal => all/common}/displaycal.c (62%) delete mode 100644 workspace/tg5040/displaycal/makefile diff --git a/skeleton/SYSTEM/tg5040/paks/MinUI.pak/launch.sh b/skeleton/SYSTEM/tg5040/paks/MinUI.pak/launch.sh index d24ef04fa..0aad231b8 100755 --- a/skeleton/SYSTEM/tg5040/paks/MinUI.pak/launch.sh +++ b/skeleton/SYSTEM/tg5040/paks/MinUI.pak/launch.sh @@ -103,41 +103,6 @@ syslogd -S export LD_LIBRARY_PATH=$SYSTEM_PATH/lib:/usr/trimui/lib:$LD_LIBRARY_PATH export PATH=$SYSTEM_PATH/bin:/usr/trimui/bin:$PATH -nextval_number() { - "$SYSTEM_PATH/bin/nextval.elf" "$1" | sed -n 's/.*: \([0-9]*\).*/\1/p' -} - -displaycal_gain() { - value=${1:-0} - printf '%d.%02d' "$((value / 100))" "$((value % 100))" -} - -apply_displaycal() { - if [ "$DEVICE" != "brick" ] || [ ! -x "$SYSTEM_PATH/bin/displaycal.elf" ]; then - return - fi - - display_cal_enabled=$(nextval_number displayCalEnabled) - display_cal_enabled=${display_cal_enabled:-1} - if [ "$display_cal_enabled" -eq 0 ]; then - "$SYSTEM_PATH/bin/displaycal.elf" disable > /dev/null 2>&1 - return - fi - - display_cal_red=$(nextval_number displayCalRed) - display_cal_green=$(nextval_number displayCalGreen) - display_cal_blue=$(nextval_number displayCalBlue) - display_cal_red=${display_cal_red:-100} - display_cal_green=${display_cal_green:-92} - display_cal_blue=${display_cal_blue:-58} - "$SYSTEM_PATH/bin/displaycal.elf" enable \ - "$(displaycal_gain "$display_cal_red")" \ - "$(displaycal_gain "$display_cal_green")" \ - "$(displaycal_gain "$display_cal_blue")" > /dev/null 2>&1 -} - -apply_displaycal - # leds_off echo 0 > /sys/class/led_anim/max_scale if [ "$TRIMUI_MODEL" = "Trimui Brick" ]; then diff --git a/workspace/all/common/config.c b/workspace/all/common/config.c index f6560bee9..761e0af4e 100644 --- a/workspace/all/common/config.c +++ b/workspace/all/common/config.c @@ -55,11 +55,6 @@ void CFG_defaults(NextUISettings *cfg) .gameSwitcherScaling = CFG_DEFAULT_GAMESWITCHERSCALING, .defaultView = CFG_DEFAULT_VIEW, .showQuickSwitcherUi = CFG_DEFAULT_SHOWQUICKWITCHERUI, - .displayCalEnabled = CFG_DEFAULT_DISPLAYCAL_ENABLED, - .displayCalRedGain = CFG_DEFAULT_DISPLAYCAL_RED_GAIN, - .displayCalGreenGain = CFG_DEFAULT_DISPLAYCAL_GREEN_GAIN, - .displayCalBlueGain = CFG_DEFAULT_DISPLAYCAL_BLUE_GAIN, - .muteLeds = CFG_DEFAULT_MUTELEDS, .screenTimeoutSecs = CFG_DEFAULT_SCREENTIMEOUTSECS, @@ -301,26 +296,6 @@ void CFG_init(FontLoad_callback_t cb, ColorSet_callback_t ccb) CFG_setShowQuickswitcherUI(temp_value); continue; } - if (sscanf(line, "displayCalEnabled=%i", &temp_value) == 1) - { - settings.displayCalEnabled = (bool)temp_value; - continue; - } - if (sscanf(line, "displayCalRed=%i", &temp_value) == 1) - { - settings.displayCalRedGain = clamp(temp_value, DISPLAYCAL_GAIN_MIN, DISPLAYCAL_GAIN_MAX); - continue; - } - if (sscanf(line, "displayCalGreen=%i", &temp_value) == 1) - { - settings.displayCalGreenGain = clamp(temp_value, DISPLAYCAL_GAIN_MIN, DISPLAYCAL_GAIN_MAX); - continue; - } - if (sscanf(line, "displayCalBlue=%i", &temp_value) == 1) - { - settings.displayCalBlueGain = clamp(temp_value, DISPLAYCAL_GAIN_MIN, DISPLAYCAL_GAIN_MAX); - continue; - } if (sscanf(line, "wifiDiagnostics=%i", &temp_value) == 1) { CFG_setWifiDiagnostics(temp_value); @@ -829,50 +804,6 @@ void CFG_setShowQuickswitcherUI(bool on) CFG_sync(); } -bool CFG_getDisplayCalEnabled(void) -{ - return settings.displayCalEnabled; -} - -void CFG_setDisplayCalEnabled(bool enabled) -{ - settings.displayCalEnabled = enabled; - CFG_sync(); -} - -int CFG_getDisplayCalRedGain(void) -{ - return settings.displayCalRedGain; -} - -void CFG_setDisplayCalRedGain(int gain) -{ - settings.displayCalRedGain = clamp(gain, DISPLAYCAL_GAIN_MIN, DISPLAYCAL_GAIN_MAX); - CFG_sync(); -} - -int CFG_getDisplayCalGreenGain(void) -{ - return settings.displayCalGreenGain; -} - -void CFG_setDisplayCalGreenGain(int gain) -{ - settings.displayCalGreenGain = clamp(gain, DISPLAYCAL_GAIN_MIN, DISPLAYCAL_GAIN_MAX); - CFG_sync(); -} - -int CFG_getDisplayCalBlueGain(void) -{ - return settings.displayCalBlueGain; -} - -void CFG_setDisplayCalBlueGain(int gain) -{ - settings.displayCalBlueGain = clamp(gain, DISPLAYCAL_GAIN_MIN, DISPLAYCAL_GAIN_MAX); - CFG_sync(); -} - bool CFG_getWifiDiagnostics(void) { return settings.wifiDiagnostics; @@ -1308,22 +1239,6 @@ void CFG_get(const char *key, char *value) { sprintf(value, "%i", (int)(CFG_getShowQuickswitcherUI())); } - else if (strcmp(key, "displayCalEnabled") == 0) - { - sprintf(value, "%i", (int)(CFG_getDisplayCalEnabled())); - } - else if (strcmp(key, "displayCalRed") == 0) - { - sprintf(value, "%i", CFG_getDisplayCalRedGain()); - } - else if (strcmp(key, "displayCalGreen") == 0) - { - sprintf(value, "%i", CFG_getDisplayCalGreenGain()); - } - else if (strcmp(key, "displayCalBlue") == 0) - { - sprintf(value, "%i", CFG_getDisplayCalBlueGain()); - } else if (strcmp(key, "wifiDiagnostics") == 0) { sprintf(value, "%i", (int)(CFG_getWifiDiagnostics())); @@ -1458,10 +1373,6 @@ void CFG_sync(void) fprintf(file, "color5=0x%06X\n", settings.color5_255); fprintf(file, "color6=0x%06X\n", settings.color6_255); fprintf(file, "color7=0x%06X\n", settings.color7_255); - fprintf(file, "displayCalEnabled=%i\n", settings.displayCalEnabled); - fprintf(file, "displayCalRed=%i\n", settings.displayCalRedGain); - fprintf(file, "displayCalGreen=%i\n", settings.displayCalGreenGain); - fprintf(file, "displayCalBlue=%i\n", settings.displayCalBlueGain); fprintf(file, "radius=%i\n", settings.thumbRadius); fprintf(file, "showclock=%i\n", settings.showClock); fprintf(file, "clock24h=%i\n", settings.clock24h); @@ -1529,10 +1440,6 @@ void CFG_print(void) printf("\t\"color5\": \"0x%06X\",\n", settings.color5_255); printf("\t\"color6\": \"0x%06X\",\n", settings.color6_255); printf("\t\"color7\": \"0x%06X\",\n", settings.color7_255); - printf("\t\"displayCalEnabled\": %i,\n", settings.displayCalEnabled); - printf("\t\"displayCalRed\": %i,\n", settings.displayCalRedGain); - printf("\t\"displayCalGreen\": %i,\n", settings.displayCalGreenGain); - printf("\t\"displayCalBlue\": %i,\n", settings.displayCalBlueGain); printf("\t\"radius\": %i,\n", settings.thumbRadius); printf("\t\"showclock\": %i,\n", settings.showClock); printf("\t\"clock24h\": %i,\n", settings.clock24h); diff --git a/workspace/all/common/config.h b/workspace/all/common/config.h index 0f856b88b..894a4128a 100644 --- a/workspace/all/common/config.h +++ b/workspace/all/common/config.h @@ -4,8 +4,6 @@ #include #include -#include "displaycal.h" - // portability, deprecated extern uint32_t THEME_COLOR1_255; extern uint32_t THEME_COLOR2_255; @@ -121,12 +119,6 @@ typedef struct bool showQuickSwitcherUi; int defaultView; - // Display calibration - bool displayCalEnabled; - int displayCalRedGain; - int displayCalGreenGain; - int displayCalBlueGain; - // Mute switch bool muteLeds; @@ -226,11 +218,6 @@ typedef struct #define CFG_DEFAULT_BLUETOOTH_MAXRATE 48000 #define CFG_DEFAULT_NTP false #define CFG_DEFAULT_TIMEZONE 320 // Europe/Berlin -#define CFG_DEFAULT_DISPLAYCAL_ENABLED (DISPLAYCAL_DEFAULT_ENABLED != 0) -#define CFG_DEFAULT_DISPLAYCAL_RED_GAIN ((int)(DISPLAYCAL_DEFAULT_RED_GAIN * DISPLAYCAL_GAIN_SCALE + 0.5)) -#define CFG_DEFAULT_DISPLAYCAL_GREEN_GAIN ((int)(DISPLAYCAL_DEFAULT_GREEN_GAIN * DISPLAYCAL_GAIN_SCALE + 0.5)) -#define CFG_DEFAULT_DISPLAYCAL_BLUE_GAIN ((int)(DISPLAYCAL_DEFAULT_BLUE_GAIN * DISPLAYCAL_GAIN_SCALE + 0.5)) - // Notification defaults #define CFG_DEFAULT_NOTIFY_MANUAL_SAVE true #define CFG_DEFAULT_NOTIFY_LOAD true @@ -355,15 +342,6 @@ void CFG_setDefaultView(int view); // Quick switcher UI painting on/off bool CFG_getShowQuickswitcherUI(void); void CFG_setShowQuickswitcherUI(bool on); -// Display calibration for devices that support it. -bool CFG_getDisplayCalEnabled(void); -void CFG_setDisplayCalEnabled(bool enabled); -int CFG_getDisplayCalRedGain(void); -void CFG_setDisplayCalRedGain(int gain); -int CFG_getDisplayCalGreenGain(void); -void CFG_setDisplayCalGreenGain(int gain); -int CFG_getDisplayCalBlueGain(void); -void CFG_setDisplayCalBlueGain(int gain); // WiFi diagnostic logging on/off bool CFG_getWifiDiagnostics(void); void CFG_setWifiDiagnostics(bool on); diff --git a/workspace/tg5040/displaycal/displaycal.c b/workspace/all/common/displaycal.c similarity index 62% rename from workspace/tg5040/displaycal/displaycal.c rename to workspace/all/common/displaycal.c index 8dc5796ab..dba48498c 100644 --- a/workspace/tg5040/displaycal/displaycal.c +++ b/workspace/all/common/displaycal.c @@ -1,51 +1,56 @@ +#include "displaycal.h" + #include #include #include #include #include -#include #include #include #include -#include "displaycal.h" - #define DISP_LCD_SET_GAMMA_TABLE 0x10b #define DISP_LCD_GAMMA_CORRECTION_ENABLE 0x10c #define DISP_LCD_GAMMA_CORRECTION_DISABLE 0x10d -#define DISPLAYCAL_VERSION "1.0.1" #define DISPLAYCAL_LUT_ENTRIES 256 #define DISPLAYCAL_LUT_BYTES (DISPLAYCAL_LUT_ENTRIES * 4) -typedef struct { - double red_gain; - double green_gain; - double blue_gain; -} DisplayCalGains; - -static void init_gains(DisplayCalGains *gains) { +void DisplayCal_initGains(DisplayCalGains *gains) { gains->red_gain = DISPLAYCAL_DEFAULT_RED_GAIN; gains->green_gain = DISPLAYCAL_DEFAULT_GREEN_GAIN; gains->blue_gain = DISPLAYCAL_DEFAULT_BLUE_GAIN; } -static double clamp_gain(double v) { - if (isnan(v)) +double DisplayCal_clampGain(double value) { + if (isnan(value)) return 1.0; - if (v < (double)DISPLAYCAL_GAIN_MIN / DISPLAYCAL_GAIN_SCALE) + if (value < (double)DISPLAYCAL_GAIN_MIN / DISPLAYCAL_GAIN_SCALE) return (double)DISPLAYCAL_GAIN_MIN / DISPLAYCAL_GAIN_SCALE; - if (v > (double)DISPLAYCAL_GAIN_MAX / DISPLAYCAL_GAIN_SCALE) + if (value > (double)DISPLAYCAL_GAIN_MAX / DISPLAYCAL_GAIN_SCALE) return (double)DISPLAYCAL_GAIN_MAX / DISPLAYCAL_GAIN_SCALE; - return v; + return value; +} + +int DisplayCal_clampGainValue(int value) { + if (value < DISPLAYCAL_GAIN_MIN) + return DISPLAYCAL_GAIN_MIN; + if (value > DISPLAYCAL_GAIN_MAX) + return DISPLAYCAL_GAIN_MAX; + return value; } -static unsigned char clamp_u8(double v) { - if (v < 0.0) +void DisplayCal_formatGainValue(int value, char *output, size_t output_size) { + value = DisplayCal_clampGainValue(value); + snprintf(output, output_size, "%d.%02d", value / DISPLAYCAL_GAIN_SCALE, value % DISPLAYCAL_GAIN_SCALE); +} + +static unsigned char clamp_u8(double value) { + if (value < 0.0) return 0; - if (v > 255.0) + if (value > 255.0) return 255; - return (unsigned char)(v + 0.5); + return (unsigned char)(value + 0.5); } static double srgb_to_linear(double c) { @@ -62,10 +67,6 @@ static double linear_to_srgb(double c) { return 1.055 * pow(c, 1.0 / 2.4) - 0.055; } -static int streq(const char *a, const char *b) { - return strcmp(a, b) == 0; -} - static void fill_linear_gain_table(uint32_t table[DISPLAYCAL_LUT_ENTRIES], const DisplayCalGains *gains) { for (int i = 0; i < DISPLAYCAL_LUT_ENTRIES; i++) { double linear = srgb_to_linear(i / 255.0); @@ -137,7 +138,7 @@ static int reset_table_and_disable(int fd, int screen) { return disable_gamma(fd, screen); } -static int apply_gains(const DisplayCalGains *gains) { +int DisplayCal_applyGains(const DisplayCalGains *gains) { int fd = open_disp(); if (fd < 0) return -1; @@ -149,77 +150,21 @@ static int apply_gains(const DisplayCalGains *gains) { return ret; } -static int disable_screen(int screen) { +int DisplayCal_enableWithValues(int red_gain, int green_gain, int blue_gain) { + DisplayCalGains gains = { + .red_gain = (double)DisplayCal_clampGainValue(red_gain) / DISPLAYCAL_GAIN_SCALE, + .green_gain = (double)DisplayCal_clampGainValue(green_gain) / DISPLAYCAL_GAIN_SCALE, + .blue_gain = (double)DisplayCal_clampGainValue(blue_gain) / DISPLAYCAL_GAIN_SCALE, + }; + return DisplayCal_applyGains(&gains); +} + +int DisplayCal_disable(void) { int fd = open_disp(); if (fd < 0) return -1; - int ret = reset_table_and_disable(fd, screen); + int ret = reset_table_and_disable(fd, 0); close(fd); return ret; } - -static void usage(const char *argv0) { - fprintf(stderr, - "displaycal %s\n" - "Adjust the LCD panel white point in hardware with zero performance cost.\n" - "\n" - "Usage:\n" - " %s enable [red] [green] [blue]\n" - " %s disable\n" - " %s --version\n" - "\n" - "Gain range: %.2f to %.2f\n" - "Suggested Brick gains: %.2f %.2f %.2f\n", - DISPLAYCAL_VERSION, - argv0, argv0, argv0, - (double)DISPLAYCAL_GAIN_MIN / DISPLAYCAL_GAIN_SCALE, - (double)DISPLAYCAL_GAIN_MAX / DISPLAYCAL_GAIN_SCALE, - DISPLAYCAL_DEFAULT_RED_GAIN, - DISPLAYCAL_DEFAULT_GREEN_GAIN, - DISPLAYCAL_DEFAULT_BLUE_GAIN); -} - -int main(int argc, char **argv) { - if (argc < 2) { - usage(argv[0]); - return 2; - } - - if (streq(argv[1], "--version")) { - if (argc != 2) { - usage(argv[0]); - return 2; - } - printf("displaycal %s\n", DISPLAYCAL_VERSION); - return 0; - } - - if (streq(argv[1], "enable")) { - if (argc > 5) { - usage(argv[0]); - return 2; - } - - DisplayCalGains gains; - init_gains(&gains); - if (argc >= 3) - gains.red_gain = clamp_gain(atof(argv[2])); - if (argc >= 4) - gains.green_gain = clamp_gain(atof(argv[3])); - if (argc >= 5) - gains.blue_gain = clamp_gain(atof(argv[4])); - return apply_gains(&gains) < 0 ? 1 : 0; - } - - if (streq(argv[1], "disable")) { - if (argc != 2) { - usage(argv[0]); - return 2; - } - return disable_screen(0) < 0 ? 1 : 0; - } - - usage(argv[0]); - return 2; -} diff --git a/workspace/all/common/displaycal.h b/workspace/all/common/displaycal.h index 2dbd0674c..b3284381c 100644 --- a/workspace/all/common/displaycal.h +++ b/workspace/all/common/displaycal.h @@ -1,13 +1,41 @@ #ifndef DISPLAYCAL_H #define DISPLAYCAL_H +#include + +#define DISPLAYCAL_GAIN_SCALE 100 +#define DISPLAYCAL_GAIN_MIN 0 +#define DISPLAYCAL_GAIN_MAX 200 + #define DISPLAYCAL_DEFAULT_ENABLED 1 #define DISPLAYCAL_DEFAULT_RED_GAIN 1.0 #define DISPLAYCAL_DEFAULT_GREEN_GAIN 0.92 #define DISPLAYCAL_DEFAULT_BLUE_GAIN 0.58 -#define DISPLAYCAL_GAIN_SCALE 100 -#define DISPLAYCAL_GAIN_MIN 0 -#define DISPLAYCAL_GAIN_MAX 200 +#define DISPLAYCAL_DEFAULT_RED_GAIN_VALUE ((int)(DISPLAYCAL_DEFAULT_RED_GAIN * DISPLAYCAL_GAIN_SCALE + 0.5)) +#define DISPLAYCAL_DEFAULT_GREEN_GAIN_VALUE ((int)(DISPLAYCAL_DEFAULT_GREEN_GAIN * DISPLAYCAL_GAIN_SCALE + 0.5)) +#define DISPLAYCAL_DEFAULT_BLUE_GAIN_VALUE ((int)(DISPLAYCAL_DEFAULT_BLUE_GAIN * DISPLAYCAL_GAIN_SCALE + 0.5)) + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + double red_gain; + double green_gain; + double blue_gain; +} DisplayCalGains; + +void DisplayCal_initGains(DisplayCalGains *gains); +double DisplayCal_clampGain(double value); +int DisplayCal_clampGainValue(int value); +void DisplayCal_formatGainValue(int value, char *output, size_t output_size); +int DisplayCal_applyGains(const DisplayCalGains *gains); +int DisplayCal_enableWithValues(int red_gain, int green_gain, int blue_gain); +int DisplayCal_disable(void); + +#ifdef __cplusplus +} +#endif #endif diff --git a/workspace/all/settings/settings.cpp b/workspace/all/settings/settings.cpp index 115fc5edf..24ec14acc 100644 --- a/workspace/all/settings/settings.cpp +++ b/workspace/all/settings/settings.cpp @@ -179,8 +179,6 @@ static const std::vector ra_sort_labels = { }; namespace { - constexpr const char *DISPLAYCAL_COMMAND = BIN_PATH "/displaycal.elf"; - enum class DisplayCalChannel { Red, @@ -190,13 +188,13 @@ namespace { static int clampDisplayCalGain(int value) { - return std::max(DISPLAYCAL_GAIN_MIN, std::min(DISPLAYCAL_GAIN_MAX, value)); + return DisplayCal_clampGainValue(value); } static std::string formatDisplayCalGain(int value) { char label[8]; - snprintf(label, sizeof(label), "%.2f", clampDisplayCalGain(value) / 100.0); + DisplayCal_formatGainValue(value, label, sizeof(label)); return label; } @@ -224,7 +222,7 @@ namespace { static bool getDisplayCalEnabled() { - return CFG_getDisplayCalEnabled(); + return GetDisplayCalEnabled(); } static int getDisplayCalGain(DisplayCalChannel channel) @@ -232,13 +230,13 @@ namespace { switch (channel) { case DisplayCalChannel::Red: - return CFG_getDisplayCalRedGain(); + return GetDisplayCalRedGain(); case DisplayCalChannel::Green: - return CFG_getDisplayCalGreenGain(); + return GetDisplayCalGreenGain(); case DisplayCalChannel::Blue: - return CFG_getDisplayCalBlueGain(); + return GetDisplayCalBlueGain(); } - return CFG_DEFAULT_DISPLAYCAL_RED_GAIN; + return SETTINGS_DEFAULT_DISPLAYCAL_RED_GAIN; } static int defaultDisplayCalGain(DisplayCalChannel channel) @@ -246,39 +244,18 @@ namespace { switch (channel) { case DisplayCalChannel::Red: - return CFG_DEFAULT_DISPLAYCAL_RED_GAIN; + return SETTINGS_DEFAULT_DISPLAYCAL_RED_GAIN; case DisplayCalChannel::Green: - return CFG_DEFAULT_DISPLAYCAL_GREEN_GAIN; + return SETTINGS_DEFAULT_DISPLAYCAL_GREEN_GAIN; case DisplayCalChannel::Blue: - return CFG_DEFAULT_DISPLAYCAL_BLUE_GAIN; + return SETTINGS_DEFAULT_DISPLAYCAL_BLUE_GAIN; } - return CFG_DEFAULT_DISPLAYCAL_RED_GAIN; - } - - static void applyDisplayCalFromSettings() - { - std::string cmd; - if (CFG_getDisplayCalEnabled()) - { - cmd = std::string("\"") + DISPLAYCAL_COMMAND + "\" enable " - + formatDisplayCalGain(CFG_getDisplayCalRedGain()) + " " - + formatDisplayCalGain(CFG_getDisplayCalGreenGain()) + " " - + formatDisplayCalGain(CFG_getDisplayCalBlueGain()) + " > /dev/null 2>&1"; - } - else - { - cmd = std::string("\"") + DISPLAYCAL_COMMAND + "\" disable > /dev/null 2>&1"; - } - - int ret = std::system(cmd.c_str()); - if (ret != 0) - LOG_error("displaycal apply failed (%d)\n", ret); + return SETTINGS_DEFAULT_DISPLAYCAL_RED_GAIN; } static void setDisplayCalEnabled(bool enabled) { - CFG_setDisplayCalEnabled(enabled); - applyDisplayCalFromSettings(); + SetDisplayCalEnabled(enabled); } static void setDisplayCalGain(DisplayCalChannel channel, int value) @@ -286,16 +263,15 @@ namespace { switch (channel) { case DisplayCalChannel::Red: - CFG_setDisplayCalRedGain(value); + SetDisplayCalRedGain(value); break; case DisplayCalChannel::Green: - CFG_setDisplayCalGreenGain(value); + SetDisplayCalGreenGain(value); break; case DisplayCalChannel::Blue: - CFG_setDisplayCalBlueGain(value); + SetDisplayCalBlueGain(value); break; } - applyDisplayCalFromSettings(); } static void resetDisplayCalGain(DisplayCalChannel channel) @@ -616,7 +592,7 @@ int main(int argc, char *argv[]) new MenuItem{ListItemType::Generic, "White point correction", "Corrects the Brick display white point", {false, true}, on_off, []() -> std::any { return getDisplayCalEnabled(); }, [](const std::any &value) { setDisplayCalEnabled(std::any_cast(value)); }, - []() { setDisplayCalEnabled(CFG_DEFAULT_DISPLAYCAL_ENABLED); }}); + []() { setDisplayCalEnabled(SETTINGS_DEFAULT_DISPLAYCAL_ENABLED); }}); struct DisplayCalGainDef { DisplayCalChannel channel; const char *name; const char *desc; }; static const DisplayCalGainDef gainDefs[] = { diff --git a/workspace/makefile b/workspace/makefile index dcda004ed..f71af4d62 100644 --- a/workspace/makefile +++ b/workspace/makefile @@ -33,7 +33,6 @@ else ifeq ($(PLATFORM), tg5040) cd ./$(PLATFORM)/rfkill && make all cd ./$(PLATFORM)/btmanager && make all - cd ./$(PLATFORM)/displaycal && make cd ./$(PLATFORM)/poweroff_next/ && make endif cd ./$(PLATFORM)/libmsettings && make @@ -84,7 +83,6 @@ ifneq ($(PLATFORM), desktop) ifeq ($(PLATFORM), tg5040) cd ./$(PLATFORM)/rfkill && make clean cd ./$(PLATFORM)/btmanager && make clean - cd ./$(PLATFORM)/displaycal && make clean cd ./$(PLATFORM)/poweroff_next/ && make clean endif endif diff --git a/workspace/tg5040/displaycal/makefile b/workspace/tg5040/displaycal/makefile deleted file mode 100644 index 4225fa1ef..000000000 --- a/workspace/tg5040/displaycal/makefile +++ /dev/null @@ -1,35 +0,0 @@ -########################################################### - -ifeq (,$(PLATFORM)) -PLATFORM=$(UNION_PLATFORM) -endif - -ifeq (,$(PLATFORM)) - $(error please specify PLATFORM, eg. PLATFORM=tg5040 make) -endif - -ifeq (,$(CROSS_COMPILE)) - $(error missing CROSS_COMPILE for this toolchain) -endif - -########################################################### - -include ../platform/makefile.env - -########################################################### - -TARGET = displaycal -SOURCE = $(TARGET).c - -CC = $(CROSS_COMPILE)gcc -CFLAGS = -mcpu=cortex-a53 -flto $(OPT) -std=gnu99 -I../../all/common -LDFLAGS = -lm -s - -PRODUCT = build/$(PLATFORM)/$(TARGET).elf - -all: - mkdir -p build/$(PLATFORM) - $(CC) $(SOURCE) -o $(PRODUCT) $(CFLAGS) $(LDFLAGS) - -clean: - rm -f $(PRODUCT) diff --git a/workspace/tg5040/libmsettings/makefile b/workspace/tg5040/libmsettings/makefile index 06b41044e..7323da815 100644 --- a/workspace/tg5040/libmsettings/makefile +++ b/workspace/tg5040/libmsettings/makefile @@ -12,21 +12,24 @@ TARGET=msettings CC = $(CROSS_COMPILE)gcc -CFLAGS = -LDFLAGS = -ltinyalsa -ldl -lrt -s +CFLAGS = -I../../all/common +LDFLAGS = -ltinyalsa -ldl -lrt -lm -s OPTM=-Ofast build: $(CC) -c -Werror -fpic "$(TARGET).c" $(CFLAGS) -Wl,--no-as-needed $(LDFLAGS) - $(CC) -shared -o "lib$(TARGET).so" "$(TARGET).o" $(LDFLAGS) + $(CC) -c -Werror -fpic "../../all/common/displaycal.c" $(CFLAGS) -Wl,--no-as-needed $(LDFLAGS) + $(CC) -shared -o "lib$(TARGET).so" "$(TARGET).o" "displaycal.o" $(LDFLAGS) mkdir -p "$(PREFIX_LOCAL)/include" mkdir -p "$(PREFIX_LOCAL)/lib" cp "$(TARGET).h" "$(PREFIX_LOCAL)/include" + cp "../../all/common/displaycal.h" "$(PREFIX_LOCAL)/include" cp "lib$(TARGET).so" "$(PREFIX_LOCAL)/lib" clean: rm -f *.o rm -f "lib$(TARGET).so" rm -f $(PREFIX_LOCAL)/include/$(TARGET).h - rm -f $(PREFIX_LOCAL)/lib/lib$(TARGET).so \ No newline at end of file + rm -f $(PREFIX_LOCAL)/include/displaycal.h + rm -f $(PREFIX_LOCAL)/lib/lib$(TARGET).so diff --git a/workspace/tg5040/libmsettings/msettings.c b/workspace/tg5040/libmsettings/msettings.c index 8671ba41b..a028a9726 100644 --- a/workspace/tg5040/libmsettings/msettings.c +++ b/workspace/tg5040/libmsettings/msettings.c @@ -172,10 +172,46 @@ typedef struct SettingsV10 { int audiosink; // was bluetooth true/false before } SettingsV10; +typedef struct SettingsV11 { + int version; // future proofing + int brightness; + int colortemperature; + int headphones; + int speaker; + int mute; + int contrast; + int saturation; + int exposure; + int toggled_brightness; + int toggled_colortemperature; + int toggled_contrast; + int toggled_saturation; + int toggled_exposure; + int toggled_volume; + int disable_dpad_on_mute; + int emulate_joystick_on_mute; + int turbo_a; + int turbo_b; + int turbo_x; + int turbo_y; + int turbo_l1; + int turbo_l2; + int turbo_r1; + int turbo_r2; + int unused[2]; // for future use + // NOTE: doesn't really need to be persisted but still needs to be shared + int jack; + int audiosink; // was bluetooth true/false before + int displaycal_enabled; + int displaycal_red_gain; + int displaycal_green_gain; + int displaycal_blue_gain; +} SettingsV11; + // When incrementing SETTINGS_VERSION, update the Settings typedef and add // backwards compatibility to InitSettings! -#define SETTINGS_VERSION 10 -typedef SettingsV10 Settings; +#define SETTINGS_VERSION 11 +typedef SettingsV11 Settings; static Settings DefaultSettings = { .version = SETTINGS_VERSION, .brightness = SETTINGS_DEFAULT_BRIGHTNESS, @@ -204,6 +240,10 @@ static Settings DefaultSettings = { .turbo_r2 = 0, .jack = 0, .audiosink = AUDIO_SINK_DEFAULT, + .displaycal_enabled = SETTINGS_DEFAULT_DISPLAYCAL_ENABLED, + .displaycal_red_gain = SETTINGS_DEFAULT_DISPLAYCAL_RED_GAIN, + .displaycal_green_gain = SETTINGS_DEFAULT_DISPLAYCAL_GREEN_GAIN, + .displaycal_blue_gain = SETTINGS_DEFAULT_DISPLAYCAL_BLUE_GAIN, }; static Settings* settings; @@ -219,6 +259,7 @@ int scaleContrast(int); int scaleSaturation(int); int scaleExposure(int); int scaleVolume(int); +int clampDisplayCalGain(int); void disableDpad(int); void emulateJoystick(int); @@ -307,7 +348,15 @@ void InitSettings(void) { memcpy(settings, &DefaultSettings, shm_size); // overwrite with migrated data - if(version==9) { + if(version==10) { + printf("Found settings v10.\n"); + SettingsV10 old; + read(fd, &old, sizeof(SettingsV10)); + + memcpy(settings, &old, sizeof(SettingsV10)); + settings->version = SETTINGS_VERSION; + } + else if(version==9) { printf("Found settings v9.\n"); SettingsV9 old; read(fd, &old, sizeof(SettingsV9)); @@ -541,6 +590,22 @@ int GetExposure(void) return settings->exposure; } +int GetDisplayCalEnabled(void) +{ + return settings->displaycal_enabled; +} +int GetDisplayCalRedGain(void) +{ + return settings->displaycal_red_gain; +} +int GetDisplayCalGreenGain(void) +{ + return settings->displaycal_green_gain; +} +int GetDisplayCalBlueGain(void) +{ + return settings->displaycal_blue_gain; +} // monitored and set by thread in keymon int GetJack(void) { return settings->jack; @@ -664,6 +729,26 @@ void SetExposure(int value){ settings->exposure = value; SaveSettings(); } +void SetDisplayCalEnabled(int value) { + settings->displaycal_enabled = value ? 1 : 0; + SetRawDisplayCal(GetDisplayCalEnabled(), GetDisplayCalRedGain(), GetDisplayCalGreenGain(), GetDisplayCalBlueGain()); + SaveSettings(); +} +void SetDisplayCalRedGain(int value) { + settings->displaycal_red_gain = clampDisplayCalGain(value); + SetRawDisplayCal(GetDisplayCalEnabled(), GetDisplayCalRedGain(), GetDisplayCalGreenGain(), GetDisplayCalBlueGain()); + SaveSettings(); +} +void SetDisplayCalGreenGain(int value) { + settings->displaycal_green_gain = clampDisplayCalGain(value); + SetRawDisplayCal(GetDisplayCalEnabled(), GetDisplayCalRedGain(), GetDisplayCalGreenGain(), GetDisplayCalBlueGain()); + SaveSettings(); +} +void SetDisplayCalBlueGain(int value) { + settings->displaycal_blue_gain = clampDisplayCalGain(value); + SetRawDisplayCal(GetDisplayCalEnabled(), GetDisplayCalRedGain(), GetDisplayCalGreenGain(), GetDisplayCalBlueGain()); + SaveSettings(); +} void SetVolume(int value) // 0-20 { if (settings->mute && GetMutedVolume() != SETTINGS_DEFAULT_MUTE_NO_CHANGE) @@ -702,6 +787,7 @@ void SetMute(int value) { SetContrast(GetContrast()); SetSaturation(GetSaturation()); SetExposure(GetExposure()); + SetDisplayCalEnabled(GetDisplayCalEnabled()); if(is_brick && GetMuteDisablesDpad()) disableDpad(settings->mute); @@ -933,6 +1019,10 @@ int scaleVolume(int value) { return value * 5; // scale 0-20 to 0-100 } +int clampDisplayCalGain(int value) { + return DisplayCal_clampGainValue(value); +} + int scaleBrightness(int value) { int raw; if (is_brick) { @@ -1283,3 +1373,14 @@ void SetRawExposure(int val){ fclose(fd); } } +void SetRawDisplayCal(int enabled, int red_gain, int green_gain, int blue_gain) { + printf("SetRawDisplayCal(%i,%i,%i,%i)\n", enabled, red_gain, green_gain, blue_gain); fflush(stdout); + + if (!is_brick) + return; + + if (enabled) + DisplayCal_enableWithValues(red_gain, green_gain, blue_gain); + else + DisplayCal_disable(); +} diff --git a/workspace/tg5040/libmsettings/msettings.h b/workspace/tg5040/libmsettings/msettings.h index 5a03981e3..f07936d39 100644 --- a/workspace/tg5040/libmsettings/msettings.h +++ b/workspace/tg5040/libmsettings/msettings.h @@ -1,11 +1,17 @@ #ifndef __msettings_h__ #define __msettings_h__ +#include "displaycal.h" + #define SETTINGS_DEFAULT_BRIGHTNESS 2 #define SETTINGS_DEFAULT_COLORTEMP 20 #define SETTINGS_DEFAULT_CONTRAST 0 #define SETTINGS_DEFAULT_SATURATION 0 #define SETTINGS_DEFAULT_EXPOSURE 0 +#define SETTINGS_DEFAULT_DISPLAYCAL_ENABLED DISPLAYCAL_DEFAULT_ENABLED +#define SETTINGS_DEFAULT_DISPLAYCAL_RED_GAIN DISPLAYCAL_DEFAULT_RED_GAIN_VALUE +#define SETTINGS_DEFAULT_DISPLAYCAL_GREEN_GAIN DISPLAYCAL_DEFAULT_GREEN_GAIN_VALUE +#define SETTINGS_DEFAULT_DISPLAYCAL_BLUE_GAIN DISPLAYCAL_DEFAULT_BLUE_GAIN_VALUE #define SETTINGS_DEFAULT_VOLUME 8 #define SETTINGS_DEFAULT_HEADPHONE_VOLUME 4 #define SETTINGS_DEFAULT_FAN_SPEED 0 @@ -21,6 +27,10 @@ int GetColortemp(void); int GetContrast(void); int GetSaturation(void); int GetExposure(void); +int GetDisplayCalEnabled(void); +int GetDisplayCalRedGain(void); +int GetDisplayCalGreenGain(void); +int GetDisplayCalBlueGain(void); int GetVolume(void); void SetRawBrightness(int value); // 0-255 @@ -28,6 +38,7 @@ void SetRawColortemp(int value); // 0-255 void SetRawContrast(int value); // 0-100 void SetRawSaturation(int value); // 0-100 void SetRawExposure(int value); // 0-100 +void SetRawDisplayCal(int enabled, int red_gain, int green_gain, int blue_gain); void SetRawVolume(int value); // 0-100 void SetBrightness(int value); // 0-10 @@ -35,6 +46,10 @@ void SetColortemp(int value); // 0-40 void SetContrast(int value); // -4-5 void SetSaturation(int value); // -5-5 void SetExposure(int value); // -4-5 +void SetDisplayCalEnabled(int value); // 0-1 +void SetDisplayCalRedGain(int value); // 0-200 +void SetDisplayCalGreenGain(int value); // 0-200 +void SetDisplayCalBlueGain(int value); // 0-200 void SetVolume(int value); // 0-20 int GetJack(void); From 289f9fe546ba0cd3e5502f8d1f788aafe9f68ede Mon Sep 17 00:00:00 2001 From: Prashant Vaibhav Date: Sun, 14 Jun 2026 17:04:10 +0200 Subject: [PATCH 13/27] refactor(tg5040): narrow display calibration API Keep the shared display calibration helper focused on scaled integer gain values and hide the floating-point LUT representation inside the implementation. Move Brick-specific defaults back into tg5040 msettings so the common header does not encode device policy or become part of libmsettings' public include surface. Guard the Settings UI integration behind a tg5040 display calibration build flag while retaining the runtime device gate, and remove Brick-specific wording from user-visible copy. Restore whitespace-only config hunks so those files do not carry PR noise against main. --- workspace/all/common/config.c | 1 + workspace/all/common/config.h | 1 + workspace/all/common/displaycal.c | 24 +++++++--------------- workspace/all/common/displaycal.h | 25 +++++++---------------- workspace/all/settings/makefile | 2 +- workspace/all/settings/settings.cpp | 8 +++++++- workspace/tg5040/libmsettings/makefile | 2 -- workspace/tg5040/libmsettings/msettings.c | 8 +++++++- workspace/tg5040/libmsettings/msettings.h | 10 ++++----- 9 files changed, 35 insertions(+), 46 deletions(-) diff --git a/workspace/all/common/config.c b/workspace/all/common/config.c index 761e0af4e..ff5af1a62 100644 --- a/workspace/all/common/config.c +++ b/workspace/all/common/config.c @@ -55,6 +55,7 @@ void CFG_defaults(NextUISettings *cfg) .gameSwitcherScaling = CFG_DEFAULT_GAMESWITCHERSCALING, .defaultView = CFG_DEFAULT_VIEW, .showQuickSwitcherUi = CFG_DEFAULT_SHOWQUICKWITCHERUI, + .muteLeds = CFG_DEFAULT_MUTELEDS, .screenTimeoutSecs = CFG_DEFAULT_SCREENTIMEOUTSECS, diff --git a/workspace/all/common/config.h b/workspace/all/common/config.h index 894a4128a..d2c56b94b 100644 --- a/workspace/all/common/config.h +++ b/workspace/all/common/config.h @@ -218,6 +218,7 @@ typedef struct #define CFG_DEFAULT_BLUETOOTH_MAXRATE 48000 #define CFG_DEFAULT_NTP false #define CFG_DEFAULT_TIMEZONE 320 // Europe/Berlin + // Notification defaults #define CFG_DEFAULT_NOTIFY_MANUAL_SAVE true #define CFG_DEFAULT_NOTIFY_LOAD true diff --git a/workspace/all/common/displaycal.c b/workspace/all/common/displaycal.c index dba48498c..d147d55d8 100644 --- a/workspace/all/common/displaycal.c +++ b/workspace/all/common/displaycal.c @@ -16,21 +16,11 @@ #define DISPLAYCAL_LUT_ENTRIES 256 #define DISPLAYCAL_LUT_BYTES (DISPLAYCAL_LUT_ENTRIES * 4) -void DisplayCal_initGains(DisplayCalGains *gains) { - gains->red_gain = DISPLAYCAL_DEFAULT_RED_GAIN; - gains->green_gain = DISPLAYCAL_DEFAULT_GREEN_GAIN; - gains->blue_gain = DISPLAYCAL_DEFAULT_BLUE_GAIN; -} - -double DisplayCal_clampGain(double value) { - if (isnan(value)) - return 1.0; - if (value < (double)DISPLAYCAL_GAIN_MIN / DISPLAYCAL_GAIN_SCALE) - return (double)DISPLAYCAL_GAIN_MIN / DISPLAYCAL_GAIN_SCALE; - if (value > (double)DISPLAYCAL_GAIN_MAX / DISPLAYCAL_GAIN_SCALE) - return (double)DISPLAYCAL_GAIN_MAX / DISPLAYCAL_GAIN_SCALE; - return value; -} +typedef struct { + double red_gain; + double green_gain; + double blue_gain; +} DisplayCalGains; int DisplayCal_clampGainValue(int value) { if (value < DISPLAYCAL_GAIN_MIN) @@ -138,7 +128,7 @@ static int reset_table_and_disable(int fd, int screen) { return disable_gamma(fd, screen); } -int DisplayCal_applyGains(const DisplayCalGains *gains) { +static int apply_gains(const DisplayCalGains *gains) { int fd = open_disp(); if (fd < 0) return -1; @@ -156,7 +146,7 @@ int DisplayCal_enableWithValues(int red_gain, int green_gain, int blue_gain) { .green_gain = (double)DisplayCal_clampGainValue(green_gain) / DISPLAYCAL_GAIN_SCALE, .blue_gain = (double)DisplayCal_clampGainValue(blue_gain) / DISPLAYCAL_GAIN_SCALE, }; - return DisplayCal_applyGains(&gains); + return apply_gains(&gains); } int DisplayCal_disable(void) { diff --git a/workspace/all/common/displaycal.h b/workspace/all/common/displaycal.h index b3284381c..f11526c28 100644 --- a/workspace/all/common/displaycal.h +++ b/workspace/all/common/displaycal.h @@ -7,31 +7,20 @@ #define DISPLAYCAL_GAIN_MIN 0 #define DISPLAYCAL_GAIN_MAX 200 -#define DISPLAYCAL_DEFAULT_ENABLED 1 -#define DISPLAYCAL_DEFAULT_RED_GAIN 1.0 -#define DISPLAYCAL_DEFAULT_GREEN_GAIN 0.92 -#define DISPLAYCAL_DEFAULT_BLUE_GAIN 0.58 - -#define DISPLAYCAL_DEFAULT_RED_GAIN_VALUE ((int)(DISPLAYCAL_DEFAULT_RED_GAIN * DISPLAYCAL_GAIN_SCALE + 0.5)) -#define DISPLAYCAL_DEFAULT_GREEN_GAIN_VALUE ((int)(DISPLAYCAL_DEFAULT_GREEN_GAIN * DISPLAYCAL_GAIN_SCALE + 0.5)) -#define DISPLAYCAL_DEFAULT_BLUE_GAIN_VALUE ((int)(DISPLAYCAL_DEFAULT_BLUE_GAIN * DISPLAYCAL_GAIN_SCALE + 0.5)) - #ifdef __cplusplus extern "C" { #endif -typedef struct { - double red_gain; - double green_gain; - double blue_gain; -} DisplayCalGains; - -void DisplayCal_initGains(DisplayCalGains *gains); -double DisplayCal_clampGain(double value); +// Clamp a scaled integer gain value to the supported range. int DisplayCal_clampGainValue(int value); + +// Format a scaled integer gain value as a decimal string, e.g. 92 -> "0.92". void DisplayCal_formatGainValue(int value, char *output, size_t output_size); -int DisplayCal_applyGains(const DisplayCalGains *gains); + +// Apply the LUT using scaled integer red, green, and blue gains. int DisplayCal_enableWithValues(int red_gain, int green_gain, int blue_gain); + +// Load the identity LUT, then disable gamma correction. int DisplayCal_disable(void); #ifdef __cplusplus diff --git a/workspace/all/settings/makefile b/workspace/all/settings/makefile index da1aae07b..a89e174bc 100644 --- a/workspace/all/settings/makefile +++ b/workspace/all/settings/makefile @@ -34,7 +34,7 @@ CFLAGS += -fpermissive LDFLAGS += -lmsettings -lcrypto ifeq ($(PLATFORM), tg5040) # drop as soon as we use btagent on all platforms -CXXFLAGS += -DHAS_BTAGENT +CXXFLAGS += -DHAS_BTAGENT -DHAS_DISPLAYCAL CXXSOURCE += btagent.cpp CXXFLAGS += $$(pkg-config --cflags --libs gio-2.0 glib-2.0) endif diff --git a/workspace/all/settings/settings.cpp b/workspace/all/settings/settings.cpp index 24ec14acc..8f5020652 100644 --- a/workspace/all/settings/settings.cpp +++ b/workspace/all/settings/settings.cpp @@ -5,7 +5,9 @@ extern "C" #include "defines.h" #include "api.h" #include "utils.h" +#ifdef HAS_DISPLAYCAL #include "displaycal.h" +#endif #include "ra_auth.h" #include "ra_sync.h" } @@ -179,6 +181,7 @@ static const std::vector ra_sort_labels = { }; namespace { +#ifdef HAS_DISPLAYCAL enum class DisplayCalChannel { Red, @@ -278,6 +281,7 @@ namespace { { setDisplayCalGain(channel, defaultDisplayCalGain(channel)); } +#endif struct ColorDef { int id; const char *name; const char *desc; uint32_t defaultColor; }; static const ColorDef g_colorDefs[] = { @@ -586,10 +590,11 @@ int main(int argc, char *argv[]) []() { SetColortemp(SETTINGS_DEFAULT_COLORTEMP);}}); } +#ifdef HAS_DISPLAYCAL if(deviceInfo.hasDisplayColorCorrection()) { displayItems.push_back( - new MenuItem{ListItemType::Generic, "White point correction", "Corrects the Brick display white point", {false, true}, on_off, []() -> std::any + new MenuItem{ListItemType::Generic, "White point correction", "Corrects the display white point", {false, true}, on_off, []() -> std::any { return getDisplayCalEnabled(); }, [](const std::any &value) { setDisplayCalEnabled(std::any_cast(value)); }, []() { setDisplayCalEnabled(SETTINGS_DEFAULT_DISPLAYCAL_ENABLED); }}); @@ -610,6 +615,7 @@ int main(int argc, char *argv[]) [channel]() { resetDisplayCalGain(channel); }}); } } +#endif if(deviceInfo.hasContrastSaturation()) { diff --git a/workspace/tg5040/libmsettings/makefile b/workspace/tg5040/libmsettings/makefile index 7323da815..e666406ff 100644 --- a/workspace/tg5040/libmsettings/makefile +++ b/workspace/tg5040/libmsettings/makefile @@ -24,12 +24,10 @@ build: mkdir -p "$(PREFIX_LOCAL)/include" mkdir -p "$(PREFIX_LOCAL)/lib" cp "$(TARGET).h" "$(PREFIX_LOCAL)/include" - cp "../../all/common/displaycal.h" "$(PREFIX_LOCAL)/include" cp "lib$(TARGET).so" "$(PREFIX_LOCAL)/lib" clean: rm -f *.o rm -f "lib$(TARGET).so" rm -f $(PREFIX_LOCAL)/include/$(TARGET).h - rm -f $(PREFIX_LOCAL)/include/displaycal.h rm -f $(PREFIX_LOCAL)/lib/lib$(TARGET).so diff --git a/workspace/tg5040/libmsettings/msettings.c b/workspace/tg5040/libmsettings/msettings.c index a028a9726..793207bcc 100644 --- a/workspace/tg5040/libmsettings/msettings.c +++ b/workspace/tg5040/libmsettings/msettings.c @@ -12,6 +12,7 @@ #include #include +#include "displaycal.h" #include "msettings.h" /////////////////////////////////////// @@ -260,6 +261,7 @@ int scaleSaturation(int); int scaleExposure(int); int scaleVolume(int); int clampDisplayCalGain(int); +int supportsDisplayCal(void); void disableDpad(int); void emulateJoystick(int); @@ -1023,6 +1025,10 @@ int clampDisplayCalGain(int value) { return DisplayCal_clampGainValue(value); } +int supportsDisplayCal(void) { + return is_brick; +} + int scaleBrightness(int value) { int raw; if (is_brick) { @@ -1376,7 +1382,7 @@ void SetRawExposure(int val){ void SetRawDisplayCal(int enabled, int red_gain, int green_gain, int blue_gain) { printf("SetRawDisplayCal(%i,%i,%i,%i)\n", enabled, red_gain, green_gain, blue_gain); fflush(stdout); - if (!is_brick) + if (!supportsDisplayCal()) return; if (enabled) diff --git a/workspace/tg5040/libmsettings/msettings.h b/workspace/tg5040/libmsettings/msettings.h index f07936d39..9ad409300 100644 --- a/workspace/tg5040/libmsettings/msettings.h +++ b/workspace/tg5040/libmsettings/msettings.h @@ -1,17 +1,15 @@ #ifndef __msettings_h__ #define __msettings_h__ -#include "displaycal.h" - #define SETTINGS_DEFAULT_BRIGHTNESS 2 #define SETTINGS_DEFAULT_COLORTEMP 20 #define SETTINGS_DEFAULT_CONTRAST 0 #define SETTINGS_DEFAULT_SATURATION 0 #define SETTINGS_DEFAULT_EXPOSURE 0 -#define SETTINGS_DEFAULT_DISPLAYCAL_ENABLED DISPLAYCAL_DEFAULT_ENABLED -#define SETTINGS_DEFAULT_DISPLAYCAL_RED_GAIN DISPLAYCAL_DEFAULT_RED_GAIN_VALUE -#define SETTINGS_DEFAULT_DISPLAYCAL_GREEN_GAIN DISPLAYCAL_DEFAULT_GREEN_GAIN_VALUE -#define SETTINGS_DEFAULT_DISPLAYCAL_BLUE_GAIN DISPLAYCAL_DEFAULT_BLUE_GAIN_VALUE +#define SETTINGS_DEFAULT_DISPLAYCAL_ENABLED 1 +#define SETTINGS_DEFAULT_DISPLAYCAL_RED_GAIN 100 +#define SETTINGS_DEFAULT_DISPLAYCAL_GREEN_GAIN 92 +#define SETTINGS_DEFAULT_DISPLAYCAL_BLUE_GAIN 58 #define SETTINGS_DEFAULT_VOLUME 8 #define SETTINGS_DEFAULT_HEADPHONE_VOLUME 4 #define SETTINGS_DEFAULT_FAN_SPEED 0 From c56f0f2459d8907a5a15ca381062bcdca76f22c6 Mon Sep 17 00:00:00 2001 From: Prashant Vaibhav Date: Sun, 14 Jun 2026 17:07:06 +0200 Subject: [PATCH 14/27] fix(tg5040): stop packaging removed displaycal cli Remove the stale top-level packaging copy for displaycal.elf. Display calibration is now linked into libmsettings directly, so clean tg5040 package builds no longer produce a standalone displaycal executable to copy. --- makefile | 1 - workspace/all/settings/settings.cpp | 137 +++++++--------------------- 2 files changed, 32 insertions(+), 106 deletions(-) diff --git a/makefile b/makefile index 56cff92be..fed3da9f6 100644 --- a/makefile +++ b/makefile @@ -109,7 +109,6 @@ ifneq (,$(filter $(PLATFORM),tg5040 tg5050)) ifeq ($(PLATFORM), tg5040) # Limbo fix cp ./workspace/$(PLATFORM)/poweroff_next/build/$(PLATFORM)/poweroff_next.elf ./build/SYSTEM/$(PLATFORM)/bin/poweroff_next - cp ./workspace/$(PLATFORM)/displaycal/build/$(PLATFORM)/displaycal.elf ./build/SYSTEM/$(PLATFORM)/bin/ endif endif ifneq (,$(filter $(PLATFORM),tg5040 tg5050 my355)) diff --git a/workspace/all/settings/settings.cpp b/workspace/all/settings/settings.cpp index 8f5020652..ae66a1924 100644 --- a/workspace/all/settings/settings.cpp +++ b/workspace/all/settings/settings.cpp @@ -182,18 +182,6 @@ static const std::vector ra_sort_labels = { namespace { #ifdef HAS_DISPLAYCAL - enum class DisplayCalChannel - { - Red, - Green, - Blue - }; - - static int clampDisplayCalGain(int value) - { - return DisplayCal_clampGainValue(value); - } - static std::string formatDisplayCalGain(int value) { char label[8]; @@ -201,85 +189,24 @@ namespace { return label; } - static const std::vector &displayCalGainValues() + struct DisplayCalGainOptions { - static const std::vector values = [] { - std::vector result; - for (int i = DISPLAYCAL_GAIN_MIN; i <= DISPLAYCAL_GAIN_MAX; i++) - result.push_back(i); - return result; - }(); - return values; - } + std::vector values; + std::vector labels; + }; - static const std::vector &displayCalGainLabels() + static const DisplayCalGainOptions &displayCalGainOptions() { - static const std::vector labels = [] { - std::vector result; + static const DisplayCalGainOptions options = [] { + DisplayCalGainOptions result; for (int i = DISPLAYCAL_GAIN_MIN; i <= DISPLAYCAL_GAIN_MAX; i++) - result.push_back(formatDisplayCalGain(i)); + { + result.values.push_back(i); + result.labels.push_back(formatDisplayCalGain(i)); + } return result; }(); - return labels; - } - - static bool getDisplayCalEnabled() - { - return GetDisplayCalEnabled(); - } - - static int getDisplayCalGain(DisplayCalChannel channel) - { - switch (channel) - { - case DisplayCalChannel::Red: - return GetDisplayCalRedGain(); - case DisplayCalChannel::Green: - return GetDisplayCalGreenGain(); - case DisplayCalChannel::Blue: - return GetDisplayCalBlueGain(); - } - return SETTINGS_DEFAULT_DISPLAYCAL_RED_GAIN; - } - - static int defaultDisplayCalGain(DisplayCalChannel channel) - { - switch (channel) - { - case DisplayCalChannel::Red: - return SETTINGS_DEFAULT_DISPLAYCAL_RED_GAIN; - case DisplayCalChannel::Green: - return SETTINGS_DEFAULT_DISPLAYCAL_GREEN_GAIN; - case DisplayCalChannel::Blue: - return SETTINGS_DEFAULT_DISPLAYCAL_BLUE_GAIN; - } - return SETTINGS_DEFAULT_DISPLAYCAL_RED_GAIN; - } - - static void setDisplayCalEnabled(bool enabled) - { - SetDisplayCalEnabled(enabled); - } - - static void setDisplayCalGain(DisplayCalChannel channel, int value) - { - switch (channel) - { - case DisplayCalChannel::Red: - SetDisplayCalRedGain(value); - break; - case DisplayCalChannel::Green: - SetDisplayCalGreenGain(value); - break; - case DisplayCalChannel::Blue: - SetDisplayCalBlueGain(value); - break; - } - } - - static void resetDisplayCalGain(DisplayCalChannel channel) - { - setDisplayCalGain(channel, defaultDisplayCalGain(channel)); + return options; } #endif @@ -593,27 +520,27 @@ int main(int argc, char *argv[]) #ifdef HAS_DISPLAYCAL if(deviceInfo.hasDisplayColorCorrection()) { + const auto &gainOptions = displayCalGainOptions(); displayItems.push_back( - new MenuItem{ListItemType::Generic, "White point correction", "Corrects the display white point", {false, true}, on_off, []() -> std::any - { return getDisplayCalEnabled(); }, [](const std::any &value) - { setDisplayCalEnabled(std::any_cast(value)); }, - []() { setDisplayCalEnabled(SETTINGS_DEFAULT_DISPLAYCAL_ENABLED); }}); - - struct DisplayCalGainDef { DisplayCalChannel channel; const char *name; const char *desc; }; - static const DisplayCalGainDef gainDefs[] = { - {DisplayCalChannel::Red, "Red gain", "White point correction red channel gain"}, - {DisplayCalChannel::Green, "Green gain", "White point correction green channel gain"}, - {DisplayCalChannel::Blue, "Blue gain", "White point correction blue channel gain"}, - }; - for (const auto &def : gainDefs) - { - const auto channel = def.channel; - displayItems.push_back( - new MenuItem{ListItemType::Generic, def.name, def.desc, displayCalGainValues(), displayCalGainLabels(), [channel]() -> std::any - { return getDisplayCalGain(channel); }, [channel](const std::any &value) - { setDisplayCalGain(channel, std::any_cast(value)); }, - [channel]() { resetDisplayCalGain(channel); }}); - } + new MenuItem{ListItemType::Generic, "White point correction", "Corrects the display white point to better match the sRGB standard.", {false, true}, on_off, []() -> std::any + { return GetDisplayCalEnabled(); }, [](const std::any &value) + { SetDisplayCalEnabled(std::any_cast(value)); }, + []() { SetDisplayCalEnabled(SETTINGS_DEFAULT_DISPLAYCAL_ENABLED); }}); + displayItems.push_back( + new MenuItem{ListItemType::Generic, "Red gain", "White point correction red channel gain", gainOptions.values, gainOptions.labels, []() -> std::any + { return GetDisplayCalRedGain(); }, [](const std::any &value) + { SetDisplayCalRedGain(std::any_cast(value)); }, + []() { SetDisplayCalRedGain(SETTINGS_DEFAULT_DISPLAYCAL_RED_GAIN); }}); + displayItems.push_back( + new MenuItem{ListItemType::Generic, "Green gain", "White point correction green channel gain", gainOptions.values, gainOptions.labels, []() -> std::any + { return GetDisplayCalGreenGain(); }, [](const std::any &value) + { SetDisplayCalGreenGain(std::any_cast(value)); }, + []() { SetDisplayCalGreenGain(SETTINGS_DEFAULT_DISPLAYCAL_GREEN_GAIN); }}); + displayItems.push_back( + new MenuItem{ListItemType::Generic, "Blue gain", "White point correction blue channel gain", gainOptions.values, gainOptions.labels, []() -> std::any + { return GetDisplayCalBlueGain(); }, [](const std::any &value) + { SetDisplayCalBlueGain(std::any_cast(value)); }, + []() { SetDisplayCalBlueGain(SETTINGS_DEFAULT_DISPLAYCAL_BLUE_GAIN); }}); } #endif From 8a22f1b7febc07cefa5822e2525e0c10bdfbbae5 Mon Sep 17 00:00:00 2001 From: Prashant Vaibhav Date: Sun, 14 Jun 2026 17:28:22 +0200 Subject: [PATCH 15/27] refactor(displaycal): simplify white point gain ranges Replace the custom decimal-formatted gain option lists with the built-in 0-200 integer range menus for white point correction. This removes the unused formatting helper API and updates menu descriptions and comments to explain that 100 is the neutral gain value. --- workspace/all/common/displaycal.c | 5 ---- workspace/all/common/displaycal.h | 10 ++----- workspace/all/settings/settings.cpp | 36 ++--------------------- workspace/tg5040/libmsettings/msettings.h | 6 ++-- 4 files changed, 9 insertions(+), 48 deletions(-) diff --git a/workspace/all/common/displaycal.c b/workspace/all/common/displaycal.c index d147d55d8..eb870532d 100644 --- a/workspace/all/common/displaycal.c +++ b/workspace/all/common/displaycal.c @@ -30,11 +30,6 @@ int DisplayCal_clampGainValue(int value) { return value; } -void DisplayCal_formatGainValue(int value, char *output, size_t output_size) { - value = DisplayCal_clampGainValue(value); - snprintf(output, output_size, "%d.%02d", value / DISPLAYCAL_GAIN_SCALE, value % DISPLAYCAL_GAIN_SCALE); -} - static unsigned char clamp_u8(double value) { if (value < 0.0) return 0; diff --git a/workspace/all/common/displaycal.h b/workspace/all/common/displaycal.h index f11526c28..2114cdd9f 100644 --- a/workspace/all/common/displaycal.h +++ b/workspace/all/common/displaycal.h @@ -1,8 +1,6 @@ #ifndef DISPLAYCAL_H #define DISPLAYCAL_H -#include - #define DISPLAYCAL_GAIN_SCALE 100 #define DISPLAYCAL_GAIN_MIN 0 #define DISPLAYCAL_GAIN_MAX 200 @@ -11,13 +9,11 @@ extern "C" { #endif -// Clamp a scaled integer gain value to the supported range. +// Clamp a display calibration gain value to the supported 0-200 range. int DisplayCal_clampGainValue(int value); -// Format a scaled integer gain value as a decimal string, e.g. 92 -> "0.92". -void DisplayCal_formatGainValue(int value, char *output, size_t output_size); - -// Apply the LUT using scaled integer red, green, and blue gains. +// Apply the LUT using integer red, green, and blue gains in the 0-200 range. +// A value of 100 is neutral. int DisplayCal_enableWithValues(int red_gain, int green_gain, int blue_gain); // Load the identity LUT, then disable gamma correction. diff --git a/workspace/all/settings/settings.cpp b/workspace/all/settings/settings.cpp index ae66a1924..6f9ce896d 100644 --- a/workspace/all/settings/settings.cpp +++ b/workspace/all/settings/settings.cpp @@ -181,35 +181,6 @@ static const std::vector ra_sort_labels = { }; namespace { -#ifdef HAS_DISPLAYCAL - static std::string formatDisplayCalGain(int value) - { - char label[8]; - DisplayCal_formatGainValue(value, label, sizeof(label)); - return label; - } - - struct DisplayCalGainOptions - { - std::vector values; - std::vector labels; - }; - - static const DisplayCalGainOptions &displayCalGainOptions() - { - static const DisplayCalGainOptions options = [] { - DisplayCalGainOptions result; - for (int i = DISPLAYCAL_GAIN_MIN; i <= DISPLAYCAL_GAIN_MAX; i++) - { - result.values.push_back(i); - result.labels.push_back(formatDisplayCalGain(i)); - } - return result; - }(); - return options; - } -#endif - struct ColorDef { int id; const char *name; const char *desc; uint32_t defaultColor; }; static const ColorDef g_colorDefs[] = { {1, "Main Color", "The color used to render main UI elements.", CFG_DEFAULT_COLOR1}, @@ -520,24 +491,23 @@ int main(int argc, char *argv[]) #ifdef HAS_DISPLAYCAL if(deviceInfo.hasDisplayColorCorrection()) { - const auto &gainOptions = displayCalGainOptions(); displayItems.push_back( new MenuItem{ListItemType::Generic, "White point correction", "Corrects the display white point to better match the sRGB standard.", {false, true}, on_off, []() -> std::any { return GetDisplayCalEnabled(); }, [](const std::any &value) { SetDisplayCalEnabled(std::any_cast(value)); }, []() { SetDisplayCalEnabled(SETTINGS_DEFAULT_DISPLAYCAL_ENABLED); }}); displayItems.push_back( - new MenuItem{ListItemType::Generic, "Red gain", "White point correction red channel gain", gainOptions.values, gainOptions.labels, []() -> std::any + new MenuItem{ListItemType::Generic, "Red gain", "White point correction red channel gain (0 to 200, 100 is neutral)", DISPLAYCAL_GAIN_MIN, DISPLAYCAL_GAIN_MAX, "", []() -> std::any { return GetDisplayCalRedGain(); }, [](const std::any &value) { SetDisplayCalRedGain(std::any_cast(value)); }, []() { SetDisplayCalRedGain(SETTINGS_DEFAULT_DISPLAYCAL_RED_GAIN); }}); displayItems.push_back( - new MenuItem{ListItemType::Generic, "Green gain", "White point correction green channel gain", gainOptions.values, gainOptions.labels, []() -> std::any + new MenuItem{ListItemType::Generic, "Green gain", "White point correction green channel gain (0 to 200, 100 is neutral)", DISPLAYCAL_GAIN_MIN, DISPLAYCAL_GAIN_MAX, "", []() -> std::any { return GetDisplayCalGreenGain(); }, [](const std::any &value) { SetDisplayCalGreenGain(std::any_cast(value)); }, []() { SetDisplayCalGreenGain(SETTINGS_DEFAULT_DISPLAYCAL_GREEN_GAIN); }}); displayItems.push_back( - new MenuItem{ListItemType::Generic, "Blue gain", "White point correction blue channel gain", gainOptions.values, gainOptions.labels, []() -> std::any + new MenuItem{ListItemType::Generic, "Blue gain", "White point correction blue channel gain (0 to 200, 100 is neutral)", DISPLAYCAL_GAIN_MIN, DISPLAYCAL_GAIN_MAX, "", []() -> std::any { return GetDisplayCalBlueGain(); }, [](const std::any &value) { SetDisplayCalBlueGain(std::any_cast(value)); }, []() { SetDisplayCalBlueGain(SETTINGS_DEFAULT_DISPLAYCAL_BLUE_GAIN); }}); diff --git a/workspace/tg5040/libmsettings/msettings.h b/workspace/tg5040/libmsettings/msettings.h index 9ad409300..88a167612 100644 --- a/workspace/tg5040/libmsettings/msettings.h +++ b/workspace/tg5040/libmsettings/msettings.h @@ -45,9 +45,9 @@ void SetContrast(int value); // -4-5 void SetSaturation(int value); // -5-5 void SetExposure(int value); // -4-5 void SetDisplayCalEnabled(int value); // 0-1 -void SetDisplayCalRedGain(int value); // 0-200 -void SetDisplayCalGreenGain(int value); // 0-200 -void SetDisplayCalBlueGain(int value); // 0-200 +void SetDisplayCalRedGain(int value); // 0-200, 100 is neutral +void SetDisplayCalGreenGain(int value); // 0-200, 100 is neutral +void SetDisplayCalBlueGain(int value); // 0-200, 100 is neutral void SetVolume(int value); // 0-20 int GetJack(void); From e415bb24155c9d803ff2353550ca131f88e27429 Mon Sep 17 00:00:00 2001 From: Prashant Vaibhav Date: Sun, 14 Jun 2026 18:44:20 +0200 Subject: [PATCH 16/27] feat(displaycal): expose white point correction broadly Make the white point correction controls available on every settings build while keeping the feature disabled with neutral 100/100/100 gains by default outside Brick. Brick continues to receive the measured 100/92/58 correction and enabled-by-default behavior. Persist display calibration values in the tg5050 and desktop msettings implementations. Apply calibration on tg5040 and tg5050 whenever the setting changes and when display state is reapplied through SetMute or boot initialization; desktop keeps the API as a logged no-op for local use. --- workspace/all/settings/makefile | 2 +- workspace/all/settings/settings.cpp | 67 ++++++++------ workspace/desktop/libmsettings/makefile | 3 +- workspace/desktop/libmsettings/msettings.c | 88 +++++++++++++++++- workspace/desktop/libmsettings/msettings.h | 13 +++ workspace/tg5040/libmsettings/msettings.c | 53 +++++++---- workspace/tg5040/libmsettings/msettings.h | 6 +- workspace/tg5050/libmsettings/makefile | 9 +- workspace/tg5050/libmsettings/msettings.c | 100 +++++++++++++++++++-- workspace/tg5050/libmsettings/msettings.h | 13 +++ 10 files changed, 291 insertions(+), 63 deletions(-) diff --git a/workspace/all/settings/makefile b/workspace/all/settings/makefile index a89e174bc..da1aae07b 100644 --- a/workspace/all/settings/makefile +++ b/workspace/all/settings/makefile @@ -34,7 +34,7 @@ CFLAGS += -fpermissive LDFLAGS += -lmsettings -lcrypto ifeq ($(PLATFORM), tg5040) # drop as soon as we use btagent on all platforms -CXXFLAGS += -DHAS_BTAGENT -DHAS_DISPLAYCAL +CXXFLAGS += -DHAS_BTAGENT CXXSOURCE += btagent.cpp CXXFLAGS += $$(pkg-config --cflags --libs gio-2.0 glib-2.0) endif diff --git a/workspace/all/settings/settings.cpp b/workspace/all/settings/settings.cpp index 6f9ce896d..7e0e4aca8 100644 --- a/workspace/all/settings/settings.cpp +++ b/workspace/all/settings/settings.cpp @@ -5,9 +5,7 @@ extern "C" #include "defines.h" #include "api.h" #include "utils.h" -#ifdef HAS_DISPLAYCAL #include "displaycal.h" -#endif #include "ra_auth.h" #include "ra_sync.h" } @@ -290,8 +288,20 @@ namespace { return m_platform == tg5040; } - bool hasDisplayColorCorrection() const { - return m_platform == tg5040 && m_model == Brick; + bool defaultDisplayCalEnabled() const { + return m_model == Brick; + } + + int defaultDisplayCalRedGain() const { + return 100; + } + + int defaultDisplayCalGreenGain() const { + return m_model == Brick ? 92 : 100; + } + + int defaultDisplayCalBlueGain() const { + return m_model == Brick ? 58 : 100; } bool hasActiveCooling() const { @@ -488,31 +498,30 @@ int main(int argc, char *argv[]) []() { SetColortemp(SETTINGS_DEFAULT_COLORTEMP);}}); } -#ifdef HAS_DISPLAYCAL - if(deviceInfo.hasDisplayColorCorrection()) - { - displayItems.push_back( - new MenuItem{ListItemType::Generic, "White point correction", "Corrects the display white point to better match the sRGB standard.", {false, true}, on_off, []() -> std::any - { return GetDisplayCalEnabled(); }, [](const std::any &value) - { SetDisplayCalEnabled(std::any_cast(value)); }, - []() { SetDisplayCalEnabled(SETTINGS_DEFAULT_DISPLAYCAL_ENABLED); }}); - displayItems.push_back( - new MenuItem{ListItemType::Generic, "Red gain", "White point correction red channel gain (0 to 200, 100 is neutral)", DISPLAYCAL_GAIN_MIN, DISPLAYCAL_GAIN_MAX, "", []() -> std::any - { return GetDisplayCalRedGain(); }, [](const std::any &value) - { SetDisplayCalRedGain(std::any_cast(value)); }, - []() { SetDisplayCalRedGain(SETTINGS_DEFAULT_DISPLAYCAL_RED_GAIN); }}); - displayItems.push_back( - new MenuItem{ListItemType::Generic, "Green gain", "White point correction green channel gain (0 to 200, 100 is neutral)", DISPLAYCAL_GAIN_MIN, DISPLAYCAL_GAIN_MAX, "", []() -> std::any - { return GetDisplayCalGreenGain(); }, [](const std::any &value) - { SetDisplayCalGreenGain(std::any_cast(value)); }, - []() { SetDisplayCalGreenGain(SETTINGS_DEFAULT_DISPLAYCAL_GREEN_GAIN); }}); - displayItems.push_back( - new MenuItem{ListItemType::Generic, "Blue gain", "White point correction blue channel gain (0 to 200, 100 is neutral)", DISPLAYCAL_GAIN_MIN, DISPLAYCAL_GAIN_MAX, "", []() -> std::any - { return GetDisplayCalBlueGain(); }, [](const std::any &value) - { SetDisplayCalBlueGain(std::any_cast(value)); }, - []() { SetDisplayCalBlueGain(SETTINGS_DEFAULT_DISPLAYCAL_BLUE_GAIN); }}); - } -#endif + const bool defaultDisplayCalEnabled = deviceInfo.defaultDisplayCalEnabled(); + const int defaultDisplayCalRedGain = deviceInfo.defaultDisplayCalRedGain(); + const int defaultDisplayCalGreenGain = deviceInfo.defaultDisplayCalGreenGain(); + const int defaultDisplayCalBlueGain = deviceInfo.defaultDisplayCalBlueGain(); + displayItems.push_back( + new MenuItem{ListItemType::Generic, "White point correction", "Corrects the display white point to better match the sRGB standard.", {false, true}, on_off, []() -> std::any + { return GetDisplayCalEnabled(); }, [](const std::any &value) + { SetDisplayCalEnabled(std::any_cast(value)); }, + [defaultDisplayCalEnabled]() { SetDisplayCalEnabled(defaultDisplayCalEnabled); }}); + displayItems.push_back( + new MenuItem{ListItemType::Generic, "Red gain", "White point correction red channel gain (0 to 200, 100 is neutral)", DISPLAYCAL_GAIN_MIN, DISPLAYCAL_GAIN_MAX, "", []() -> std::any + { return GetDisplayCalRedGain(); }, [](const std::any &value) + { SetDisplayCalRedGain(std::any_cast(value)); }, + [defaultDisplayCalRedGain]() { SetDisplayCalRedGain(defaultDisplayCalRedGain); }}); + displayItems.push_back( + new MenuItem{ListItemType::Generic, "Green gain", "White point correction green channel gain (0 to 200, 100 is neutral)", DISPLAYCAL_GAIN_MIN, DISPLAYCAL_GAIN_MAX, "", []() -> std::any + { return GetDisplayCalGreenGain(); }, [](const std::any &value) + { SetDisplayCalGreenGain(std::any_cast(value)); }, + [defaultDisplayCalGreenGain]() { SetDisplayCalGreenGain(defaultDisplayCalGreenGain); }}); + displayItems.push_back( + new MenuItem{ListItemType::Generic, "Blue gain", "White point correction blue channel gain (0 to 200, 100 is neutral)", DISPLAYCAL_GAIN_MIN, DISPLAYCAL_GAIN_MAX, "", []() -> std::any + { return GetDisplayCalBlueGain(); }, [](const std::any &value) + { SetDisplayCalBlueGain(std::any_cast(value)); }, + [defaultDisplayCalBlueGain]() { SetDisplayCalBlueGain(defaultDisplayCalBlueGain); }}); if(deviceInfo.hasContrastSaturation()) { diff --git a/workspace/desktop/libmsettings/makefile b/workspace/desktop/libmsettings/makefile index fb0eacfd6..3b3cf34f2 100644 --- a/workspace/desktop/libmsettings/makefile +++ b/workspace/desktop/libmsettings/makefile @@ -17,7 +17,6 @@ CFLAGS += -pg LDFLAGS += -pg endif -#CFLAGS = -g #LDFLAGS = -ldl -lrt -s LDFLAGS = -ldl @@ -35,4 +34,4 @@ clean: rm -f *.o rm -f "lib$(TARGET).so" rm -f $(PREFIX_LOCAL)/include/$(TARGET).h - rm -f $(PREFIX_LOCAL)/lib/lib$(TARGET).so \ No newline at end of file + rm -f $(PREFIX_LOCAL)/lib/lib$(TARGET).so diff --git a/workspace/desktop/libmsettings/msettings.c b/workspace/desktop/libmsettings/msettings.c index ffad64125..62f970801 100644 --- a/workspace/desktop/libmsettings/msettings.c +++ b/workspace/desktop/libmsettings/msettings.c @@ -137,10 +137,45 @@ typedef struct SettingsV9 { int jack; } SettingsV9; +typedef struct SettingsV10 { + int version; // future proofing + int brightness; + int colortemperature; + int headphones; + int speaker; + int mute; + int contrast; + int saturation; + int exposure; + int toggled_brightness; + int toggled_colortemperature; + int toggled_contrast; + int toggled_saturation; + int toggled_exposure; + int toggled_volume; + int disable_dpad_on_mute; + int emulate_joystick_on_mute; + int turbo_a; + int turbo_b; + int turbo_x; + int turbo_y; + int turbo_l1; + int turbo_l2; + int turbo_r1; + int turbo_r2; + int unused[2]; // for future use + // NOTE: doesn't really need to be persisted but still needs to be shared + int jack; + int displaycal_enabled; + int displaycal_red_gain; + int displaycal_green_gain; + int displaycal_blue_gain; +} SettingsV10; + // When incrementing SETTINGS_VERSION, update the Settings typedef and add // backwards compatibility to InitSettings! -#define SETTINGS_VERSION 9 -typedef SettingsV9 Settings; +#define SETTINGS_VERSION 10 +typedef SettingsV10 Settings; static Settings DefaultSettings = { .version = SETTINGS_VERSION, .brightness = SETTINGS_DEFAULT_BRIGHTNESS, @@ -168,6 +203,10 @@ static Settings DefaultSettings = { .turbo_r1 = 0, .turbo_r2 = 0, .jack = 0, + .displaycal_enabled = SETTINGS_DEFAULT_DISPLAYCAL_ENABLED, + .displaycal_red_gain = SETTINGS_DEFAULT_DISPLAYCAL_RED_GAIN, + .displaycal_green_gain = SETTINGS_DEFAULT_DISPLAYCAL_GREEN_GAIN, + .displaycal_blue_gain = SETTINGS_DEFAULT_DISPLAYCAL_BLUE_GAIN, }; static Settings* msettings; @@ -204,7 +243,15 @@ void InitSettings(void){ memcpy(msettings, &DefaultSettings, sizeof(Settings)); // overwrite with migrated data - if(version==8) { + if(version==9) { + printf("Found settings v9.\n"); + SettingsV9 old; + read(fd, &old, sizeof(SettingsV9)); + + memcpy(msettings, &old, sizeof(SettingsV9)); + msettings->version = SETTINGS_VERSION; + } + else if(version==8) { printf("Found settings v8.\n"); SettingsV8 old; read(fd, &old, sizeof(SettingsV8)); @@ -325,6 +372,8 @@ void InitSettings(void){ // load defaults memcpy(msettings, &DefaultSettings, sizeof(Settings)); } + + SetDisplayCalEnabled(GetDisplayCalEnabled()); } static inline void SaveSettings(void) { FILE *file = fopen(SettingsPath, "w"); @@ -350,6 +399,10 @@ int GetColortemp(void) { return 0; } int GetContrast(void) { return 0; } int GetSaturation(void) { return 0; } int GetExposure(void) { return 0; } +int GetDisplayCalEnabled(void) { return msettings->displaycal_enabled; } +int GetDisplayCalRedGain(void) { return msettings->displaycal_red_gain; } +int GetDisplayCalGreenGain(void) { return msettings->displaycal_green_gain; } +int GetDisplayCalBlueGain(void) { return msettings->displaycal_blue_gain; } int GetVolume(void) { return 0; } int GetMutedBrightness(void) { return 0; } @@ -387,6 +440,9 @@ void SetMuteTurboR1(int value) {} void SetMuteTurboR2(int value) {} void SetRawBrightness(int value) {} +void SetRawDisplayCal(int enabled, int red_gain, int green_gain, int blue_gain) { + printf("SetRawDisplayCal(%i,%i,%i,%i)\n", enabled, red_gain, green_gain, blue_gain); fflush(stdout); +} void SetRawVolume(int value){} void SetBrightness(int value) {} @@ -394,6 +450,32 @@ void SetColortemp(int value) {} void SetContrast(int value) {} void SetSaturation(int value) {} void SetExposure(int value) {} +void SetDisplayCalEnabled(int value) { + msettings->displaycal_enabled = value ? 1 : 0; + SetRawDisplayCal(GetDisplayCalEnabled(), GetDisplayCalRedGain(), GetDisplayCalGreenGain(), GetDisplayCalBlueGain()); + SaveSettings(); +} +void SetDisplayCalRedGain(int value) { + if (value < 0) value = 0; + if (value > 200) value = 200; + msettings->displaycal_red_gain = value; + SetRawDisplayCal(GetDisplayCalEnabled(), GetDisplayCalRedGain(), GetDisplayCalGreenGain(), GetDisplayCalBlueGain()); + SaveSettings(); +} +void SetDisplayCalGreenGain(int value) { + if (value < 0) value = 0; + if (value > 200) value = 200; + msettings->displaycal_green_gain = value; + SetRawDisplayCal(GetDisplayCalEnabled(), GetDisplayCalRedGain(), GetDisplayCalGreenGain(), GetDisplayCalBlueGain()); + SaveSettings(); +} +void SetDisplayCalBlueGain(int value) { + if (value < 0) value = 0; + if (value > 200) value = 200; + msettings->displaycal_blue_gain = value; + SetRawDisplayCal(GetDisplayCalEnabled(), GetDisplayCalRedGain(), GetDisplayCalGreenGain(), GetDisplayCalBlueGain()); + SaveSettings(); +} void SetVolume(int value) {} int GetJack(void) { return 0; } diff --git a/workspace/desktop/libmsettings/msettings.h b/workspace/desktop/libmsettings/msettings.h index c01d81b9f..d8243a6d3 100644 --- a/workspace/desktop/libmsettings/msettings.h +++ b/workspace/desktop/libmsettings/msettings.h @@ -6,6 +6,10 @@ #define SETTINGS_DEFAULT_CONTRAST 0 #define SETTINGS_DEFAULT_SATURATION 0 #define SETTINGS_DEFAULT_EXPOSURE 0 +#define SETTINGS_DEFAULT_DISPLAYCAL_ENABLED 0 +#define SETTINGS_DEFAULT_DISPLAYCAL_RED_GAIN 100 +#define SETTINGS_DEFAULT_DISPLAYCAL_GREEN_GAIN 100 +#define SETTINGS_DEFAULT_DISPLAYCAL_BLUE_GAIN 100 #define SETTINGS_DEFAULT_VOLUME 8 #define SETTINGS_DEFAULT_HEADPHONE_VOLUME 4 #define SETTINGS_DEFAULT_FAN_SPEED 0 @@ -21,9 +25,14 @@ int GetColortemp(void); int GetContrast(void); int GetSaturation(void); int GetExposure(void); +int GetDisplayCalEnabled(void); +int GetDisplayCalRedGain(void); +int GetDisplayCalGreenGain(void); +int GetDisplayCalBlueGain(void); int GetVolume(void); void SetRawBrightness(int value); // 0-255 +void SetRawDisplayCal(int enabled, int red_gain, int green_gain, int blue_gain); void SetRawVolume(int value); // 0-160 void SetBrightness(int value); // 0-10 @@ -31,6 +40,10 @@ void SetColortemp(int value); // 0-40 void SetContrast(int value); // -4-5 void SetSaturation(int value); // -5-5 void SetExposure(int value); // -4-5 +void SetDisplayCalEnabled(int value); // 0-1 +void SetDisplayCalRedGain(int value); // 0-200, 100 is neutral +void SetDisplayCalGreenGain(int value); // 0-200, 100 is neutral +void SetDisplayCalBlueGain(int value); // 0-200, 100 is neutral void SetVolume(int value); // 0-20 int GetJack(void); diff --git a/workspace/tg5040/libmsettings/msettings.c b/workspace/tg5040/libmsettings/msettings.c index 793207bcc..14483e488 100644 --- a/workspace/tg5040/libmsettings/msettings.c +++ b/workspace/tg5040/libmsettings/msettings.c @@ -260,8 +260,6 @@ int scaleContrast(int); int scaleSaturation(int); int scaleExposure(int); int scaleVolume(int); -int clampDisplayCalGain(int); -int supportsDisplayCal(void); void disableDpad(int); void emulateJoystick(int); @@ -318,6 +316,29 @@ int peekVersion(const char *filename) { static int is_brick = 0; +static int defaultDisplayCalEnabledForDevice(void) { + return is_brick ? 1 : SETTINGS_DEFAULT_DISPLAYCAL_ENABLED; +} + +static int defaultDisplayCalRedGainForDevice(void) { + return 100; +} + +static int defaultDisplayCalGreenGainForDevice(void) { + return is_brick ? 92 : SETTINGS_DEFAULT_DISPLAYCAL_GREEN_GAIN; +} + +static int defaultDisplayCalBlueGainForDevice(void) { + return is_brick ? 58 : SETTINGS_DEFAULT_DISPLAYCAL_BLUE_GAIN; +} + +static void applyDisplayCalDefaultsForDevice(Settings *target) { + target->displaycal_enabled = defaultDisplayCalEnabledForDevice(); + target->displaycal_red_gain = defaultDisplayCalRedGainForDevice(); + target->displaycal_green_gain = defaultDisplayCalGreenGainForDevice(); + target->displaycal_blue_gain = defaultDisplayCalBlueGainForDevice(); +} + void InitSettings(void) { char* device = getenv("DEVICE"); is_brick = exactMatch("brick", device); @@ -348,6 +369,7 @@ void InitSettings(void) { else { // initialize with defaults memcpy(settings, &DefaultSettings, shm_size); + applyDisplayCalDefaultsForDevice(settings); // overwrite with migrated data if(version==10) { @@ -357,6 +379,7 @@ void InitSettings(void) { memcpy(settings, &old, sizeof(SettingsV10)); settings->version = SETTINGS_VERSION; + applyDisplayCalDefaultsForDevice(settings); } else if(version==9) { printf("Found settings v9.\n"); @@ -393,6 +416,7 @@ void InitSettings(void) { settings->speaker = old.speaker; settings->mute = old.mute; settings->jack = old.jack; + applyDisplayCalDefaultsForDevice(settings); } else if(version==8) { printf("Found settings v8.\n"); @@ -418,6 +442,7 @@ void InitSettings(void) { settings->speaker = old.speaker; settings->mute = old.mute; settings->jack = old.jack; + applyDisplayCalDefaultsForDevice(settings); } else if(version==7) { printf("Found settings v7.\n"); @@ -442,6 +467,7 @@ void InitSettings(void) { settings->speaker = old.speaker; settings->mute = old.mute; settings->jack = old.jack; + applyDisplayCalDefaultsForDevice(settings); } else if(version==6) { printf("Found settings v6.\n"); @@ -459,6 +485,7 @@ void InitSettings(void) { settings->speaker = old.speaker; settings->mute = old.mute; settings->jack = old.jack; + applyDisplayCalDefaultsForDevice(settings); } else if(version==5) { printf("Found settings v5.\n"); @@ -472,6 +499,7 @@ void InitSettings(void) { settings->speaker = old.speaker; settings->mute = old.mute; settings->jack = old.jack; + applyDisplayCalDefaultsForDevice(settings); } else if(version==4) { printf("Found settings v4.\n"); @@ -486,6 +514,7 @@ void InitSettings(void) { settings->speaker = old.speaker; settings->mute = old.mute; settings->jack = old.jack; + applyDisplayCalDefaultsForDevice(settings); } else if(version==3) { printf("Found settings v3.\n"); @@ -497,6 +526,7 @@ void InitSettings(void) { settings->speaker = old.speaker; settings->mute = old.mute; settings->jack = old.jack; + applyDisplayCalDefaultsForDevice(settings); } else { printf("Found unsupported settings version: %i.\n", version); @@ -508,11 +538,13 @@ void InitSettings(void) { else { // load defaults memcpy(settings, &DefaultSettings, shm_size); + applyDisplayCalDefaultsForDevice(settings); } } else { // load defaults memcpy(settings, &DefaultSettings, shm_size); + applyDisplayCalDefaultsForDevice(settings); } // these shouldn't be persisted @@ -737,17 +769,17 @@ void SetDisplayCalEnabled(int value) { SaveSettings(); } void SetDisplayCalRedGain(int value) { - settings->displaycal_red_gain = clampDisplayCalGain(value); + settings->displaycal_red_gain = DisplayCal_clampGainValue(value); SetRawDisplayCal(GetDisplayCalEnabled(), GetDisplayCalRedGain(), GetDisplayCalGreenGain(), GetDisplayCalBlueGain()); SaveSettings(); } void SetDisplayCalGreenGain(int value) { - settings->displaycal_green_gain = clampDisplayCalGain(value); + settings->displaycal_green_gain = DisplayCal_clampGainValue(value); SetRawDisplayCal(GetDisplayCalEnabled(), GetDisplayCalRedGain(), GetDisplayCalGreenGain(), GetDisplayCalBlueGain()); SaveSettings(); } void SetDisplayCalBlueGain(int value) { - settings->displaycal_blue_gain = clampDisplayCalGain(value); + settings->displaycal_blue_gain = DisplayCal_clampGainValue(value); SetRawDisplayCal(GetDisplayCalEnabled(), GetDisplayCalRedGain(), GetDisplayCalGreenGain(), GetDisplayCalBlueGain()); SaveSettings(); } @@ -1021,14 +1053,6 @@ int scaleVolume(int value) { return value * 5; // scale 0-20 to 0-100 } -int clampDisplayCalGain(int value) { - return DisplayCal_clampGainValue(value); -} - -int supportsDisplayCal(void) { - return is_brick; -} - int scaleBrightness(int value) { int raw; if (is_brick) { @@ -1382,9 +1406,6 @@ void SetRawExposure(int val){ void SetRawDisplayCal(int enabled, int red_gain, int green_gain, int blue_gain) { printf("SetRawDisplayCal(%i,%i,%i,%i)\n", enabled, red_gain, green_gain, blue_gain); fflush(stdout); - if (!supportsDisplayCal()) - return; - if (enabled) DisplayCal_enableWithValues(red_gain, green_gain, blue_gain); else diff --git a/workspace/tg5040/libmsettings/msettings.h b/workspace/tg5040/libmsettings/msettings.h index 88a167612..6957d9596 100644 --- a/workspace/tg5040/libmsettings/msettings.h +++ b/workspace/tg5040/libmsettings/msettings.h @@ -6,10 +6,10 @@ #define SETTINGS_DEFAULT_CONTRAST 0 #define SETTINGS_DEFAULT_SATURATION 0 #define SETTINGS_DEFAULT_EXPOSURE 0 -#define SETTINGS_DEFAULT_DISPLAYCAL_ENABLED 1 +#define SETTINGS_DEFAULT_DISPLAYCAL_ENABLED 0 #define SETTINGS_DEFAULT_DISPLAYCAL_RED_GAIN 100 -#define SETTINGS_DEFAULT_DISPLAYCAL_GREEN_GAIN 92 -#define SETTINGS_DEFAULT_DISPLAYCAL_BLUE_GAIN 58 +#define SETTINGS_DEFAULT_DISPLAYCAL_GREEN_GAIN 100 +#define SETTINGS_DEFAULT_DISPLAYCAL_BLUE_GAIN 100 #define SETTINGS_DEFAULT_VOLUME 8 #define SETTINGS_DEFAULT_HEADPHONE_VOLUME 4 #define SETTINGS_DEFAULT_FAN_SPEED 0 diff --git a/workspace/tg5050/libmsettings/makefile b/workspace/tg5050/libmsettings/makefile index cc516d79b..ae6aecf52 100644 --- a/workspace/tg5050/libmsettings/makefile +++ b/workspace/tg5050/libmsettings/makefile @@ -12,15 +12,16 @@ TARGET=msettings CC = $(CROSS_COMPILE)gcc -CFLAGS = -LDFLAGS = -ltinyalsa -ldl -lrt -s +CFLAGS = -I../../all/common +LDFLAGS = -ltinyalsa -ldl -lrt -lm -s #LDFLAGS = -ldl -lrt -s OPTM=-Ofast build: $(CC) -c -Werror -fpic "$(TARGET).c" $(CFLAGS) -Wl,--no-as-needed $(LDFLAGS) - $(CC) -shared -o "lib$(TARGET).so" "$(TARGET).o" $(LDFLAGS) + $(CC) -c -Werror -fpic "../../all/common/displaycal.c" $(CFLAGS) -Wl,--no-as-needed $(LDFLAGS) + $(CC) -shared -o "lib$(TARGET).so" "$(TARGET).o" "displaycal.o" $(LDFLAGS) mkdir -p "$(PREFIX_LOCAL)/include" mkdir -p "$(PREFIX_LOCAL)/lib" cp "$(TARGET).h" "$(PREFIX_LOCAL)/include" @@ -30,4 +31,4 @@ clean: rm -f *.o rm -f "lib$(TARGET).so" rm -f $(PREFIX_LOCAL)/include/$(TARGET).h - rm -f $(PREFIX_LOCAL)/lib/lib$(TARGET).so \ No newline at end of file + rm -f $(PREFIX_LOCAL)/lib/lib$(TARGET).so diff --git a/workspace/tg5050/libmsettings/msettings.c b/workspace/tg5050/libmsettings/msettings.c index a214ffe95..cc415e17f 100644 --- a/workspace/tg5050/libmsettings/msettings.c +++ b/workspace/tg5050/libmsettings/msettings.c @@ -13,6 +13,7 @@ #include #include "msettings.h" +#include "displaycal.h" /////////////////////////////////////// @@ -47,10 +48,45 @@ typedef struct SettingsV1 { int fanSpeed; // 0-100, -1 for auto } SettingsV1; +typedef struct SettingsV2 { + int version; // future proofing + int brightness; + int colortemperature; + int headphones; + int speaker; + int mute; + int contrast; + int saturation; + int exposure; + int toggled_brightness; + int toggled_colortemperature; + int toggled_contrast; + int toggled_saturation; + int toggled_exposure; + int toggled_volume; + int turbo_a; + int turbo_b; + int turbo_x; + int turbo_y; + int turbo_l1; + int turbo_l2; + int turbo_r1; + int turbo_r2; + int unused[2]; // for future use + // NOTE: doesn't really need to be persisted but still needs to be shared + int jack; + int audiosink; // was bluetooth true/false before + int fanSpeed; // 0-100, -1 for auto + int displaycal_enabled; + int displaycal_red_gain; + int displaycal_green_gain; + int displaycal_blue_gain; +} SettingsV2; + // When incrementing SETTINGS_VERSION, update the Settings typedef and add // backwards compatibility to InitSettings! -#define SETTINGS_VERSION 1 -typedef SettingsV1 Settings; +#define SETTINGS_VERSION 2 +typedef SettingsV2 Settings; static Settings DefaultSettings = { .version = SETTINGS_VERSION, .brightness = SETTINGS_DEFAULT_BRIGHTNESS, @@ -78,6 +114,10 @@ static Settings DefaultSettings = { .jack = 0, .audiosink = AUDIO_SINK_DEFAULT, .fanSpeed = SETTINGS_DEFAULT_FAN_SPEED, + .displaycal_enabled = SETTINGS_DEFAULT_DISPLAYCAL_ENABLED, + .displaycal_red_gain = SETTINGS_DEFAULT_DISPLAYCAL_RED_GAIN, + .displaycal_green_gain = SETTINGS_DEFAULT_DISPLAYCAL_GREEN_GAIN, + .displaycal_blue_gain = SETTINGS_DEFAULT_DISPLAYCAL_BLUE_GAIN, }; static Settings* settings; @@ -177,8 +217,12 @@ void InitSettings(void) { memcpy(settings, &DefaultSettings, shm_size); // overwrite with migrated data - if(version == 42) { - // do migration (TODO when needed) + if(version == 1) { + SettingsV1 old; + read(fd, &old, sizeof(SettingsV1)); + + memcpy(settings, &old, sizeof(SettingsV1)); + settings->version = SETTINGS_VERSION; } else { printf("Found unsupported settings version: %i.\n", version); @@ -280,6 +324,22 @@ int GetExposure(void) return settings->exposure; } +int GetDisplayCalEnabled(void) +{ + return settings->displaycal_enabled; +} +int GetDisplayCalRedGain(void) +{ + return settings->displaycal_red_gain; +} +int GetDisplayCalGreenGain(void) +{ + return settings->displaycal_green_gain; +} +int GetDisplayCalBlueGain(void) +{ + return settings->displaycal_blue_gain; +} // monitored and set by thread in keymon int GetJack(void) { return settings->jack; @@ -406,6 +466,26 @@ void SetExposure(int value){ settings->exposure = value; SaveSettings(); } +void SetDisplayCalEnabled(int value) { + settings->displaycal_enabled = value ? 1 : 0; + SetRawDisplayCal(GetDisplayCalEnabled(), GetDisplayCalRedGain(), GetDisplayCalGreenGain(), GetDisplayCalBlueGain()); + SaveSettings(); +} +void SetDisplayCalRedGain(int value) { + settings->displaycal_red_gain = DisplayCal_clampGainValue(value); + SetRawDisplayCal(GetDisplayCalEnabled(), GetDisplayCalRedGain(), GetDisplayCalGreenGain(), GetDisplayCalBlueGain()); + SaveSettings(); +} +void SetDisplayCalGreenGain(int value) { + settings->displaycal_green_gain = DisplayCal_clampGainValue(value); + SetRawDisplayCal(GetDisplayCalEnabled(), GetDisplayCalRedGain(), GetDisplayCalGreenGain(), GetDisplayCalBlueGain()); + SaveSettings(); +} +void SetDisplayCalBlueGain(int value) { + settings->displaycal_blue_gain = DisplayCal_clampGainValue(value); + SetRawDisplayCal(GetDisplayCalEnabled(), GetDisplayCalRedGain(), GetDisplayCalGreenGain(), GetDisplayCalBlueGain()); + SaveSettings(); +} void SetVolume(int value) { // 0-20 if (settings->mute && GetMutedVolume() != SETTINGS_DEFAULT_MUTE_NO_CHANGE) return SetRawVolume(scaleVolume(GetMutedVolume())); @@ -443,6 +523,7 @@ void SetMute(int value) { SetContrast(GetContrast()); SetSaturation(GetSaturation()); SetExposure(GetExposure()); + SetDisplayCalEnabled(GetDisplayCalEnabled()); if(GetMuteTurboA()) turboA(settings->mute); @@ -986,6 +1067,15 @@ void SetRawExposure(int val){ } } +void SetRawDisplayCal(int enabled, int red_gain, int green_gain, int blue_gain) { + printf("SetRawDisplayCal(%i,%i,%i,%i)\n", enabled, red_gain, green_gain, blue_gain); fflush(stdout); + + if (enabled) + DisplayCal_enableWithValues(red_gain, green_gain, blue_gain); + else + DisplayCal_disable(); +} + void SetRawColortemp(int val) { // 0 - 255 printf("SetRawColortemp(%i)\n", val); fflush(stdout); @@ -1028,4 +1118,4 @@ void SetRawFanSpeed(int val) { // 0-31, -1/-2-3 for auto low/med/high system(FAN_SPEED_CONTROL " &"); return; } -} \ No newline at end of file +} diff --git a/workspace/tg5050/libmsettings/msettings.h b/workspace/tg5050/libmsettings/msettings.h index ddb43d296..ba75b4379 100644 --- a/workspace/tg5050/libmsettings/msettings.h +++ b/workspace/tg5050/libmsettings/msettings.h @@ -6,6 +6,10 @@ #define SETTINGS_DEFAULT_CONTRAST 0 #define SETTINGS_DEFAULT_SATURATION 0 #define SETTINGS_DEFAULT_EXPOSURE 0 +#define SETTINGS_DEFAULT_DISPLAYCAL_ENABLED 0 +#define SETTINGS_DEFAULT_DISPLAYCAL_RED_GAIN 100 +#define SETTINGS_DEFAULT_DISPLAYCAL_GREEN_GAIN 100 +#define SETTINGS_DEFAULT_DISPLAYCAL_BLUE_GAIN 100 #define SETTINGS_DEFAULT_VOLUME 8 #define SETTINGS_DEFAULT_HEADPHONE_VOLUME 4 #define SETTINGS_DEFAULT_FAN_SPEED -2 // Default fan curve @@ -21,6 +25,10 @@ int GetColortemp(void); int GetContrast(void); int GetSaturation(void); int GetExposure(void); +int GetDisplayCalEnabled(void); +int GetDisplayCalRedGain(void); +int GetDisplayCalGreenGain(void); +int GetDisplayCalBlueGain(void); int GetVolume(void); int GetFanSpeed(void); @@ -29,6 +37,7 @@ void SetRawColortemp(int value); // 0-255 void SetRawContrast(int value); // 0-100 void SetRawSaturation(int value); // 0-100 void SetRawExposure(int value); // 0-100 +void SetRawDisplayCal(int enabled, int red_gain, int green_gain, int blue_gain); void SetRawVolume(int value); // 0-100 void SetRawFanSpeed(int value); // 0-31, -1/-2-3 for auto low/med/high @@ -37,6 +46,10 @@ void SetColortemp(int value); // 0-40 void SetContrast(int value); // -4-5 void SetSaturation(int value); // -5-5 void SetExposure(int value); // -4-5 +void SetDisplayCalEnabled(int value); // 0-1 +void SetDisplayCalRedGain(int value); // 0-200, 100 is neutral +void SetDisplayCalGreenGain(int value); // 0-200, 100 is neutral +void SetDisplayCalBlueGain(int value); // 0-200, 100 is neutral void SetVolume(int value); // 0-20 void SetFanSpeed(int value); // 0-100, -1 for auto From e9d25dd298a157c3c5f2034057bc94e6ab486ffb Mon Sep 17 00:00:00 2001 From: Prashant Vaibhav Date: Sun, 14 Jun 2026 19:00:14 +0200 Subject: [PATCH 17/27] refactor(displaycal): centralize calibration apply path Add a small applyDisplayCalSettings helper to the platform msettings implementations so display calibration setters, boot reapply, and mute reapply all use the same raw-apply path. This removes the repeated SetRawDisplayCal(GetDisplayCal...) orchestration while preserving the existing save behavior and keeping desktop as a log-only no-op backend. --- workspace/desktop/libmsettings/msettings.c | 15 ++++++++----- workspace/tg5040/libmsettings/msettings.c | 26 ++++++++++++++-------- workspace/tg5050/libmsettings/msettings.c | 26 ++++++++++++++-------- 3 files changed, 44 insertions(+), 23 deletions(-) diff --git a/workspace/desktop/libmsettings/msettings.c b/workspace/desktop/libmsettings/msettings.c index 62f970801..2f9ef62b6 100644 --- a/workspace/desktop/libmsettings/msettings.c +++ b/workspace/desktop/libmsettings/msettings.c @@ -212,6 +212,8 @@ static Settings* msettings; static char SettingsPath[256]; +static inline void applyDisplayCalSettings(void); + /////////////////////////////////////// int peekVersion(const char *filename) { @@ -373,7 +375,7 @@ void InitSettings(void){ memcpy(msettings, &DefaultSettings, sizeof(Settings)); } - SetDisplayCalEnabled(GetDisplayCalEnabled()); + applyDisplayCalSettings(); } static inline void SaveSettings(void) { FILE *file = fopen(SettingsPath, "w"); @@ -382,6 +384,9 @@ static inline void SaveSettings(void) { fclose(file); } } +static inline void applyDisplayCalSettings(void) { + SetRawDisplayCal(msettings->displaycal_enabled, msettings->displaycal_red_gain, msettings->displaycal_green_gain, msettings->displaycal_blue_gain); +} void QuitSettings(void){ SaveSettings(); // dealloc settings @@ -452,28 +457,28 @@ void SetSaturation(int value) {} void SetExposure(int value) {} void SetDisplayCalEnabled(int value) { msettings->displaycal_enabled = value ? 1 : 0; - SetRawDisplayCal(GetDisplayCalEnabled(), GetDisplayCalRedGain(), GetDisplayCalGreenGain(), GetDisplayCalBlueGain()); + applyDisplayCalSettings(); SaveSettings(); } void SetDisplayCalRedGain(int value) { if (value < 0) value = 0; if (value > 200) value = 200; msettings->displaycal_red_gain = value; - SetRawDisplayCal(GetDisplayCalEnabled(), GetDisplayCalRedGain(), GetDisplayCalGreenGain(), GetDisplayCalBlueGain()); + applyDisplayCalSettings(); SaveSettings(); } void SetDisplayCalGreenGain(int value) { if (value < 0) value = 0; if (value > 200) value = 200; msettings->displaycal_green_gain = value; - SetRawDisplayCal(GetDisplayCalEnabled(), GetDisplayCalRedGain(), GetDisplayCalGreenGain(), GetDisplayCalBlueGain()); + applyDisplayCalSettings(); SaveSettings(); } void SetDisplayCalBlueGain(int value) { if (value < 0) value = 0; if (value > 200) value = 200; msettings->displaycal_blue_gain = value; - SetRawDisplayCal(GetDisplayCalEnabled(), GetDisplayCalRedGain(), GetDisplayCalGreenGain(), GetDisplayCalBlueGain()); + applyDisplayCalSettings(); SaveSettings(); } void SetVolume(int value) {} diff --git a/workspace/tg5040/libmsettings/msettings.c b/workspace/tg5040/libmsettings/msettings.c index 14483e488..aaa42eea9 100644 --- a/workspace/tg5040/libmsettings/msettings.c +++ b/workspace/tg5040/libmsettings/msettings.c @@ -580,6 +580,10 @@ static inline void SaveSettings(void) { } } +static inline void applyDisplayCalSettings(void) { + SetRawDisplayCal(settings->displaycal_enabled, settings->displaycal_red_gain, settings->displaycal_green_gain, settings->displaycal_blue_gain); +} + ///////// Getters exposed in public API int GetBrightness(void) { // 0-10 @@ -764,23 +768,27 @@ void SetExposure(int value){ SaveSettings(); } void SetDisplayCalEnabled(int value) { - settings->displaycal_enabled = value ? 1 : 0; - SetRawDisplayCal(GetDisplayCalEnabled(), GetDisplayCalRedGain(), GetDisplayCalGreenGain(), GetDisplayCalBlueGain()); + value = value ? 1 : 0; + settings->displaycal_enabled = value; + applyDisplayCalSettings(); SaveSettings(); } void SetDisplayCalRedGain(int value) { - settings->displaycal_red_gain = DisplayCal_clampGainValue(value); - SetRawDisplayCal(GetDisplayCalEnabled(), GetDisplayCalRedGain(), GetDisplayCalGreenGain(), GetDisplayCalBlueGain()); + value = DisplayCal_clampGainValue(value); + settings->displaycal_red_gain = value; + applyDisplayCalSettings(); SaveSettings(); } void SetDisplayCalGreenGain(int value) { - settings->displaycal_green_gain = DisplayCal_clampGainValue(value); - SetRawDisplayCal(GetDisplayCalEnabled(), GetDisplayCalRedGain(), GetDisplayCalGreenGain(), GetDisplayCalBlueGain()); + value = DisplayCal_clampGainValue(value); + settings->displaycal_green_gain = value; + applyDisplayCalSettings(); SaveSettings(); } void SetDisplayCalBlueGain(int value) { - settings->displaycal_blue_gain = DisplayCal_clampGainValue(value); - SetRawDisplayCal(GetDisplayCalEnabled(), GetDisplayCalRedGain(), GetDisplayCalGreenGain(), GetDisplayCalBlueGain()); + value = DisplayCal_clampGainValue(value); + settings->displaycal_blue_gain = value; + applyDisplayCalSettings(); SaveSettings(); } void SetVolume(int value) // 0-20 @@ -821,7 +829,7 @@ void SetMute(int value) { SetContrast(GetContrast()); SetSaturation(GetSaturation()); SetExposure(GetExposure()); - SetDisplayCalEnabled(GetDisplayCalEnabled()); + applyDisplayCalSettings(); if(is_brick && GetMuteDisablesDpad()) disableDpad(settings->mute); diff --git a/workspace/tg5050/libmsettings/msettings.c b/workspace/tg5050/libmsettings/msettings.c index cc415e17f..a3301fdd0 100644 --- a/workspace/tg5050/libmsettings/msettings.c +++ b/workspace/tg5050/libmsettings/msettings.c @@ -280,6 +280,10 @@ static inline void SaveSettings(void) { } } +static inline void applyDisplayCalSettings(void) { + SetRawDisplayCal(settings->displaycal_enabled, settings->displaycal_red_gain, settings->displaycal_green_gain, settings->displaycal_blue_gain); +} + ///////// Getters exposed in public API int GetBrightness(void) { // 0-10 @@ -467,23 +471,27 @@ void SetExposure(int value){ SaveSettings(); } void SetDisplayCalEnabled(int value) { - settings->displaycal_enabled = value ? 1 : 0; - SetRawDisplayCal(GetDisplayCalEnabled(), GetDisplayCalRedGain(), GetDisplayCalGreenGain(), GetDisplayCalBlueGain()); + value = value ? 1 : 0; + settings->displaycal_enabled = value; + applyDisplayCalSettings(); SaveSettings(); } void SetDisplayCalRedGain(int value) { - settings->displaycal_red_gain = DisplayCal_clampGainValue(value); - SetRawDisplayCal(GetDisplayCalEnabled(), GetDisplayCalRedGain(), GetDisplayCalGreenGain(), GetDisplayCalBlueGain()); + value = DisplayCal_clampGainValue(value); + settings->displaycal_red_gain = value; + applyDisplayCalSettings(); SaveSettings(); } void SetDisplayCalGreenGain(int value) { - settings->displaycal_green_gain = DisplayCal_clampGainValue(value); - SetRawDisplayCal(GetDisplayCalEnabled(), GetDisplayCalRedGain(), GetDisplayCalGreenGain(), GetDisplayCalBlueGain()); + value = DisplayCal_clampGainValue(value); + settings->displaycal_green_gain = value; + applyDisplayCalSettings(); SaveSettings(); } void SetDisplayCalBlueGain(int value) { - settings->displaycal_blue_gain = DisplayCal_clampGainValue(value); - SetRawDisplayCal(GetDisplayCalEnabled(), GetDisplayCalRedGain(), GetDisplayCalGreenGain(), GetDisplayCalBlueGain()); + value = DisplayCal_clampGainValue(value); + settings->displaycal_blue_gain = value; + applyDisplayCalSettings(); SaveSettings(); } void SetVolume(int value) { // 0-20 @@ -523,7 +531,7 @@ void SetMute(int value) { SetContrast(GetContrast()); SetSaturation(GetSaturation()); SetExposure(GetExposure()); - SetDisplayCalEnabled(GetDisplayCalEnabled()); + applyDisplayCalSettings(); if(GetMuteTurboA()) turboA(settings->mute); From 1fab236af0692ca6d9e82a87f92c7fcceaa1cdcb Mon Sep 17 00:00:00 2001 From: Prashant Vaibhav Date: Sun, 14 Jun 2026 19:04:21 +0200 Subject: [PATCH 18/27] fix(displaycal): log raw apply failures Capture the return code from display calibration enable and disable calls in the hardware-backed msettings implementations. This keeps the existing void SetRawDisplayCal API but surfaces failed raw applies at the msettings layer so settings changes are easier to diagnose when the device LUT operation fails. --- workspace/tg5040/libmsettings/msettings.c | 12 ++++++++---- workspace/tg5050/libmsettings/msettings.c | 12 ++++++++---- 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/workspace/tg5040/libmsettings/msettings.c b/workspace/tg5040/libmsettings/msettings.c index aaa42eea9..77401759a 100644 --- a/workspace/tg5040/libmsettings/msettings.c +++ b/workspace/tg5040/libmsettings/msettings.c @@ -1414,8 +1414,12 @@ void SetRawExposure(int val){ void SetRawDisplayCal(int enabled, int red_gain, int green_gain, int blue_gain) { printf("SetRawDisplayCal(%i,%i,%i,%i)\n", enabled, red_gain, green_gain, blue_gain); fflush(stdout); - if (enabled) - DisplayCal_enableWithValues(red_gain, green_gain, blue_gain); - else - DisplayCal_disable(); + int ret = enabled + ? DisplayCal_enableWithValues(red_gain, green_gain, blue_gain) + : DisplayCal_disable(); + if (ret != 0) { + fprintf(stderr, "SetRawDisplayCal failed to %s display calibration: %i\n", + enabled ? "enable" : "disable", ret); + fflush(stderr); + } } diff --git a/workspace/tg5050/libmsettings/msettings.c b/workspace/tg5050/libmsettings/msettings.c index a3301fdd0..3353ec0bd 100644 --- a/workspace/tg5050/libmsettings/msettings.c +++ b/workspace/tg5050/libmsettings/msettings.c @@ -1078,10 +1078,14 @@ void SetRawExposure(int val){ void SetRawDisplayCal(int enabled, int red_gain, int green_gain, int blue_gain) { printf("SetRawDisplayCal(%i,%i,%i,%i)\n", enabled, red_gain, green_gain, blue_gain); fflush(stdout); - if (enabled) - DisplayCal_enableWithValues(red_gain, green_gain, blue_gain); - else - DisplayCal_disable(); + int ret = enabled + ? DisplayCal_enableWithValues(red_gain, green_gain, blue_gain) + : DisplayCal_disable(); + if (ret != 0) { + fprintf(stderr, "SetRawDisplayCal failed to %s display calibration: %i\n", + enabled ? "enable" : "disable", ret); + fflush(stderr); + } } void SetRawColortemp(int val) { // 0 - 255 From 45d5e72f81db6e05329c08c72fb618c59d051ec9 Mon Sep 17 00:00:00 2001 From: Prashant Vaibhav Date: Sun, 14 Jun 2026 19:20:24 +0200 Subject: [PATCH 19/27] fix(settings): return bool for display calibration toggle Make the white point correction menu getter return a bool to match its {false, true} value list. This fixes the MenuItem type assertion on Settings startup while preserving the stored integer msettings representation. --- workspace/all/settings/settings.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/workspace/all/settings/settings.cpp b/workspace/all/settings/settings.cpp index 7e0e4aca8..e1bb2f9ae 100644 --- a/workspace/all/settings/settings.cpp +++ b/workspace/all/settings/settings.cpp @@ -504,7 +504,7 @@ int main(int argc, char *argv[]) const int defaultDisplayCalBlueGain = deviceInfo.defaultDisplayCalBlueGain(); displayItems.push_back( new MenuItem{ListItemType::Generic, "White point correction", "Corrects the display white point to better match the sRGB standard.", {false, true}, on_off, []() -> std::any - { return GetDisplayCalEnabled(); }, [](const std::any &value) + { return GetDisplayCalEnabled() != 0; }, [](const std::any &value) { SetDisplayCalEnabled(std::any_cast(value)); }, [defaultDisplayCalEnabled]() { SetDisplayCalEnabled(defaultDisplayCalEnabled); }}); displayItems.push_back( From ea43e10f9f6b4c2248a159748b47373a26fd6668 Mon Sep 17 00:00:00 2001 From: Prashant Vaibhav Date: Sun, 14 Jun 2026 19:25:18 +0200 Subject: [PATCH 20/27] refactor(displaycal): share gain clamp with desktop Move the display calibration gain clamp into the shared header so desktop can reuse the same helper without linking the hardware LUT backend. Update the desktop msettings build include path and replace the local 0-200 clamp checks in its RGB gain setters. --- workspace/all/common/displaycal.c | 8 -------- workspace/all/common/displaycal.h | 8 +++++++- workspace/desktop/libmsettings/makefile | 1 + workspace/desktop/libmsettings/msettings.c | 10 ++++------ 4 files changed, 12 insertions(+), 15 deletions(-) diff --git a/workspace/all/common/displaycal.c b/workspace/all/common/displaycal.c index eb870532d..6a7cab98d 100644 --- a/workspace/all/common/displaycal.c +++ b/workspace/all/common/displaycal.c @@ -22,14 +22,6 @@ typedef struct { double blue_gain; } DisplayCalGains; -int DisplayCal_clampGainValue(int value) { - if (value < DISPLAYCAL_GAIN_MIN) - return DISPLAYCAL_GAIN_MIN; - if (value > DISPLAYCAL_GAIN_MAX) - return DISPLAYCAL_GAIN_MAX; - return value; -} - static unsigned char clamp_u8(double value) { if (value < 0.0) return 0; diff --git a/workspace/all/common/displaycal.h b/workspace/all/common/displaycal.h index 2114cdd9f..38c1c0ca0 100644 --- a/workspace/all/common/displaycal.h +++ b/workspace/all/common/displaycal.h @@ -10,7 +10,13 @@ extern "C" { #endif // Clamp a display calibration gain value to the supported 0-200 range. -int DisplayCal_clampGainValue(int value); +static inline int DisplayCal_clampGainValue(int value) { + if (value < DISPLAYCAL_GAIN_MIN) + return DISPLAYCAL_GAIN_MIN; + if (value > DISPLAYCAL_GAIN_MAX) + return DISPLAYCAL_GAIN_MAX; + return value; +} // Apply the LUT using integer red, green, and blue gains in the 0-200 range. // A value of 100 is neutral. diff --git a/workspace/desktop/libmsettings/makefile b/workspace/desktop/libmsettings/makefile index 3b3cf34f2..f525df7f9 100644 --- a/workspace/desktop/libmsettings/makefile +++ b/workspace/desktop/libmsettings/makefile @@ -11,6 +11,7 @@ TARGET=msettings .PHONY: clean CC = $(CROSS_COMPILE)gcc +CFLAGS = -I../../all/common ifeq ($(PROFILE), 1) CFLAGS += -pg diff --git a/workspace/desktop/libmsettings/msettings.c b/workspace/desktop/libmsettings/msettings.c index 2f9ef62b6..5a38c2088 100644 --- a/workspace/desktop/libmsettings/msettings.c +++ b/workspace/desktop/libmsettings/msettings.c @@ -1,4 +1,5 @@ #include "msettings.h" +#include "displaycal.h" // desktop #include @@ -461,22 +462,19 @@ void SetDisplayCalEnabled(int value) { SaveSettings(); } void SetDisplayCalRedGain(int value) { - if (value < 0) value = 0; - if (value > 200) value = 200; + value = DisplayCal_clampGainValue(value); msettings->displaycal_red_gain = value; applyDisplayCalSettings(); SaveSettings(); } void SetDisplayCalGreenGain(int value) { - if (value < 0) value = 0; - if (value > 200) value = 200; + value = DisplayCal_clampGainValue(value); msettings->displaycal_green_gain = value; applyDisplayCalSettings(); SaveSettings(); } void SetDisplayCalBlueGain(int value) { - if (value < 0) value = 0; - if (value > 200) value = 200; + value = DisplayCal_clampGainValue(value); msettings->displaycal_blue_gain = value; applyDisplayCalSettings(); SaveSettings(); From 70bac5e9eafb6c7fe1dc1c75d65ff9857f073edc Mon Sep 17 00:00:00 2001 From: Prashant Vaibhav Date: Sun, 14 Jun 2026 19:32:16 +0200 Subject: [PATCH 21/27] refactor(settings): gate display calibration menu items Add an explicit hasDisplayCal capability helper and use it to guard the white point correction toggle and RGB gain items, matching the surrounding display menu capability checks. Document that the feature is exposed everywhere, that desktop raw apply only logs, and that menu reset defaults come from the display calibration default helpers. --- workspace/all/settings/settings.cpp | 57 +++++++++++++++++------------ 1 file changed, 33 insertions(+), 24 deletions(-) diff --git a/workspace/all/settings/settings.cpp b/workspace/all/settings/settings.cpp index e1bb2f9ae..63a9e109a 100644 --- a/workspace/all/settings/settings.cpp +++ b/workspace/all/settings/settings.cpp @@ -288,6 +288,12 @@ namespace { return m_platform == tg5040; } + // Exposed on all platforms; desktop raw apply only logs. Menu reset + // defaults come from defaultDisplayCal*() below. + bool hasDisplayCal() const { + return true; + } + bool defaultDisplayCalEnabled() const { return m_model == Brick; } @@ -498,30 +504,33 @@ int main(int argc, char *argv[]) []() { SetColortemp(SETTINGS_DEFAULT_COLORTEMP);}}); } - const bool defaultDisplayCalEnabled = deviceInfo.defaultDisplayCalEnabled(); - const int defaultDisplayCalRedGain = deviceInfo.defaultDisplayCalRedGain(); - const int defaultDisplayCalGreenGain = deviceInfo.defaultDisplayCalGreenGain(); - const int defaultDisplayCalBlueGain = deviceInfo.defaultDisplayCalBlueGain(); - displayItems.push_back( - new MenuItem{ListItemType::Generic, "White point correction", "Corrects the display white point to better match the sRGB standard.", {false, true}, on_off, []() -> std::any - { return GetDisplayCalEnabled() != 0; }, [](const std::any &value) - { SetDisplayCalEnabled(std::any_cast(value)); }, - [defaultDisplayCalEnabled]() { SetDisplayCalEnabled(defaultDisplayCalEnabled); }}); - displayItems.push_back( - new MenuItem{ListItemType::Generic, "Red gain", "White point correction red channel gain (0 to 200, 100 is neutral)", DISPLAYCAL_GAIN_MIN, DISPLAYCAL_GAIN_MAX, "", []() -> std::any - { return GetDisplayCalRedGain(); }, [](const std::any &value) - { SetDisplayCalRedGain(std::any_cast(value)); }, - [defaultDisplayCalRedGain]() { SetDisplayCalRedGain(defaultDisplayCalRedGain); }}); - displayItems.push_back( - new MenuItem{ListItemType::Generic, "Green gain", "White point correction green channel gain (0 to 200, 100 is neutral)", DISPLAYCAL_GAIN_MIN, DISPLAYCAL_GAIN_MAX, "", []() -> std::any - { return GetDisplayCalGreenGain(); }, [](const std::any &value) - { SetDisplayCalGreenGain(std::any_cast(value)); }, - [defaultDisplayCalGreenGain]() { SetDisplayCalGreenGain(defaultDisplayCalGreenGain); }}); - displayItems.push_back( - new MenuItem{ListItemType::Generic, "Blue gain", "White point correction blue channel gain (0 to 200, 100 is neutral)", DISPLAYCAL_GAIN_MIN, DISPLAYCAL_GAIN_MAX, "", []() -> std::any - { return GetDisplayCalBlueGain(); }, [](const std::any &value) - { SetDisplayCalBlueGain(std::any_cast(value)); }, - [defaultDisplayCalBlueGain]() { SetDisplayCalBlueGain(defaultDisplayCalBlueGain); }}); + if(deviceInfo.hasDisplayCal()) + { + const bool defaultDisplayCalEnabled = deviceInfo.defaultDisplayCalEnabled(); + const int defaultDisplayCalRedGain = deviceInfo.defaultDisplayCalRedGain(); + const int defaultDisplayCalGreenGain = deviceInfo.defaultDisplayCalGreenGain(); + const int defaultDisplayCalBlueGain = deviceInfo.defaultDisplayCalBlueGain(); + displayItems.push_back( + new MenuItem{ListItemType::Generic, "White point correction", "Corrects the display white point to better match the sRGB standard.", {false, true}, on_off, []() -> std::any + { return GetDisplayCalEnabled() != 0; }, [](const std::any &value) + { SetDisplayCalEnabled(std::any_cast(value)); }, + [defaultDisplayCalEnabled]() { SetDisplayCalEnabled(defaultDisplayCalEnabled); }}); + displayItems.push_back( + new MenuItem{ListItemType::Generic, "Red gain", "White point correction red channel gain (0 to 200, 100 is neutral)", DISPLAYCAL_GAIN_MIN, DISPLAYCAL_GAIN_MAX, "", []() -> std::any + { return GetDisplayCalRedGain(); }, [](const std::any &value) + { SetDisplayCalRedGain(std::any_cast(value)); }, + [defaultDisplayCalRedGain]() { SetDisplayCalRedGain(defaultDisplayCalRedGain); }}); + displayItems.push_back( + new MenuItem{ListItemType::Generic, "Green gain", "White point correction green channel gain (0 to 200, 100 is neutral)", DISPLAYCAL_GAIN_MIN, DISPLAYCAL_GAIN_MAX, "", []() -> std::any + { return GetDisplayCalGreenGain(); }, [](const std::any &value) + { SetDisplayCalGreenGain(std::any_cast(value)); }, + [defaultDisplayCalGreenGain]() { SetDisplayCalGreenGain(defaultDisplayCalGreenGain); }}); + displayItems.push_back( + new MenuItem{ListItemType::Generic, "Blue gain", "White point correction blue channel gain (0 to 200, 100 is neutral)", DISPLAYCAL_GAIN_MIN, DISPLAYCAL_GAIN_MAX, "", []() -> std::any + { return GetDisplayCalBlueGain(); }, [](const std::any &value) + { SetDisplayCalBlueGain(std::any_cast(value)); }, + [defaultDisplayCalBlueGain]() { SetDisplayCalBlueGain(defaultDisplayCalBlueGain); }}); + } if(deviceInfo.hasContrastSaturation()) { From bd66cee1866bc7ee18ad6ccee170f00f0dfa1384 Mon Sep 17 00:00:00 2001 From: Prashant Vaibhav Date: Sun, 14 Jun 2026 20:00:27 +0200 Subject: [PATCH 22/27] refactor: centralize display calibration defaults Move neutral and Brick-specific display calibration defaults into the shared displaycal header so settings reset and tg5040 migration/init paths use the same values. Remove the duplicated SETTINGS_DEFAULT_DISPLAYCAL_* definitions from platform msettings headers and update libmsettings initializers to read the shared constants. --- workspace/all/common/displaycal.h | 25 ++++++++++++++++ workspace/all/settings/settings.cpp | 32 ++++----------------- workspace/desktop/libmsettings/msettings.c | 8 +++--- workspace/desktop/libmsettings/msettings.h | 4 --- workspace/tg5040/libmsettings/msettings.c | 33 ++++++---------------- workspace/tg5040/libmsettings/msettings.h | 4 --- workspace/tg5050/libmsettings/msettings.c | 8 +++--- workspace/tg5050/libmsettings/msettings.h | 4 --- 8 files changed, 48 insertions(+), 70 deletions(-) diff --git a/workspace/all/common/displaycal.h b/workspace/all/common/displaycal.h index 38c1c0ca0..cc9c15178 100644 --- a/workspace/all/common/displaycal.h +++ b/workspace/all/common/displaycal.h @@ -4,11 +4,36 @@ #define DISPLAYCAL_GAIN_SCALE 100 #define DISPLAYCAL_GAIN_MIN 0 #define DISPLAYCAL_GAIN_MAX 200 +#define DISPLAYCAL_DEFAULT_ENABLED 0 +#define DISPLAYCAL_DEFAULT_RED_GAIN 100 +#define DISPLAYCAL_DEFAULT_GREEN_GAIN 100 +#define DISPLAYCAL_DEFAULT_BLUE_GAIN 100 +#define DISPLAYCAL_BRICK_DEFAULT_ENABLED 1 +#define DISPLAYCAL_BRICK_DEFAULT_RED_GAIN DISPLAYCAL_DEFAULT_RED_GAIN +#define DISPLAYCAL_BRICK_DEFAULT_GREEN_GAIN 92 +#define DISPLAYCAL_BRICK_DEFAULT_BLUE_GAIN 58 #ifdef __cplusplus extern "C" { #endif +typedef struct DisplayCalDefaults { + int enabled; + int red_gain; + int green_gain; + int blue_gain; +} DisplayCalDefaults; + +static inline DisplayCalDefaults DisplayCal_getDefaultSettings(int use_brick_overrides) { + DisplayCalDefaults defaults = { + use_brick_overrides ? DISPLAYCAL_BRICK_DEFAULT_ENABLED : DISPLAYCAL_DEFAULT_ENABLED, + use_brick_overrides ? DISPLAYCAL_BRICK_DEFAULT_RED_GAIN : DISPLAYCAL_DEFAULT_RED_GAIN, + use_brick_overrides ? DISPLAYCAL_BRICK_DEFAULT_GREEN_GAIN : DISPLAYCAL_DEFAULT_GREEN_GAIN, + use_brick_overrides ? DISPLAYCAL_BRICK_DEFAULT_BLUE_GAIN : DISPLAYCAL_DEFAULT_BLUE_GAIN, + }; + return defaults; +} + // Clamp a display calibration gain value to the supported 0-200 range. static inline int DisplayCal_clampGainValue(int value) { if (value < DISPLAYCAL_GAIN_MIN) diff --git a/workspace/all/settings/settings.cpp b/workspace/all/settings/settings.cpp index 63a9e109a..eaef40791 100644 --- a/workspace/all/settings/settings.cpp +++ b/workspace/all/settings/settings.cpp @@ -288,28 +288,11 @@ namespace { return m_platform == tg5040; } - // Exposed on all platforms; desktop raw apply only logs. Menu reset - // defaults come from defaultDisplayCal*() below. + // Exposed on all platforms; desktop raw apply only logs. bool hasDisplayCal() const { return true; } - bool defaultDisplayCalEnabled() const { - return m_model == Brick; - } - - int defaultDisplayCalRedGain() const { - return 100; - } - - int defaultDisplayCalGreenGain() const { - return m_model == Brick ? 92 : 100; - } - - int defaultDisplayCalBlueGain() const { - return m_model == Brick ? 58 : 100; - } - bool hasActiveCooling() const { return m_platform == tg5050; } @@ -506,30 +489,27 @@ int main(int argc, char *argv[]) if(deviceInfo.hasDisplayCal()) { - const bool defaultDisplayCalEnabled = deviceInfo.defaultDisplayCalEnabled(); - const int defaultDisplayCalRedGain = deviceInfo.defaultDisplayCalRedGain(); - const int defaultDisplayCalGreenGain = deviceInfo.defaultDisplayCalGreenGain(); - const int defaultDisplayCalBlueGain = deviceInfo.defaultDisplayCalBlueGain(); + const DisplayCalDefaults defaultDisplayCal = DisplayCal_getDefaultSettings(deviceInfo.getModel() == Brick); displayItems.push_back( new MenuItem{ListItemType::Generic, "White point correction", "Corrects the display white point to better match the sRGB standard.", {false, true}, on_off, []() -> std::any { return GetDisplayCalEnabled() != 0; }, [](const std::any &value) { SetDisplayCalEnabled(std::any_cast(value)); }, - [defaultDisplayCalEnabled]() { SetDisplayCalEnabled(defaultDisplayCalEnabled); }}); + [defaultDisplayCal]() { SetDisplayCalEnabled(defaultDisplayCal.enabled); }}); displayItems.push_back( new MenuItem{ListItemType::Generic, "Red gain", "White point correction red channel gain (0 to 200, 100 is neutral)", DISPLAYCAL_GAIN_MIN, DISPLAYCAL_GAIN_MAX, "", []() -> std::any { return GetDisplayCalRedGain(); }, [](const std::any &value) { SetDisplayCalRedGain(std::any_cast(value)); }, - [defaultDisplayCalRedGain]() { SetDisplayCalRedGain(defaultDisplayCalRedGain); }}); + [defaultDisplayCal]() { SetDisplayCalRedGain(defaultDisplayCal.red_gain); }}); displayItems.push_back( new MenuItem{ListItemType::Generic, "Green gain", "White point correction green channel gain (0 to 200, 100 is neutral)", DISPLAYCAL_GAIN_MIN, DISPLAYCAL_GAIN_MAX, "", []() -> std::any { return GetDisplayCalGreenGain(); }, [](const std::any &value) { SetDisplayCalGreenGain(std::any_cast(value)); }, - [defaultDisplayCalGreenGain]() { SetDisplayCalGreenGain(defaultDisplayCalGreenGain); }}); + [defaultDisplayCal]() { SetDisplayCalGreenGain(defaultDisplayCal.green_gain); }}); displayItems.push_back( new MenuItem{ListItemType::Generic, "Blue gain", "White point correction blue channel gain (0 to 200, 100 is neutral)", DISPLAYCAL_GAIN_MIN, DISPLAYCAL_GAIN_MAX, "", []() -> std::any { return GetDisplayCalBlueGain(); }, [](const std::any &value) { SetDisplayCalBlueGain(std::any_cast(value)); }, - [defaultDisplayCalBlueGain]() { SetDisplayCalBlueGain(defaultDisplayCalBlueGain); }}); + [defaultDisplayCal]() { SetDisplayCalBlueGain(defaultDisplayCal.blue_gain); }}); } if(deviceInfo.hasContrastSaturation()) diff --git a/workspace/desktop/libmsettings/msettings.c b/workspace/desktop/libmsettings/msettings.c index 5a38c2088..9ca152dc3 100644 --- a/workspace/desktop/libmsettings/msettings.c +++ b/workspace/desktop/libmsettings/msettings.c @@ -204,10 +204,10 @@ static Settings DefaultSettings = { .turbo_r1 = 0, .turbo_r2 = 0, .jack = 0, - .displaycal_enabled = SETTINGS_DEFAULT_DISPLAYCAL_ENABLED, - .displaycal_red_gain = SETTINGS_DEFAULT_DISPLAYCAL_RED_GAIN, - .displaycal_green_gain = SETTINGS_DEFAULT_DISPLAYCAL_GREEN_GAIN, - .displaycal_blue_gain = SETTINGS_DEFAULT_DISPLAYCAL_BLUE_GAIN, + .displaycal_enabled = DISPLAYCAL_DEFAULT_ENABLED, + .displaycal_red_gain = DISPLAYCAL_DEFAULT_RED_GAIN, + .displaycal_green_gain = DISPLAYCAL_DEFAULT_GREEN_GAIN, + .displaycal_blue_gain = DISPLAYCAL_DEFAULT_BLUE_GAIN, }; static Settings* msettings; diff --git a/workspace/desktop/libmsettings/msettings.h b/workspace/desktop/libmsettings/msettings.h index d8243a6d3..0b302d08f 100644 --- a/workspace/desktop/libmsettings/msettings.h +++ b/workspace/desktop/libmsettings/msettings.h @@ -6,10 +6,6 @@ #define SETTINGS_DEFAULT_CONTRAST 0 #define SETTINGS_DEFAULT_SATURATION 0 #define SETTINGS_DEFAULT_EXPOSURE 0 -#define SETTINGS_DEFAULT_DISPLAYCAL_ENABLED 0 -#define SETTINGS_DEFAULT_DISPLAYCAL_RED_GAIN 100 -#define SETTINGS_DEFAULT_DISPLAYCAL_GREEN_GAIN 100 -#define SETTINGS_DEFAULT_DISPLAYCAL_BLUE_GAIN 100 #define SETTINGS_DEFAULT_VOLUME 8 #define SETTINGS_DEFAULT_HEADPHONE_VOLUME 4 #define SETTINGS_DEFAULT_FAN_SPEED 0 diff --git a/workspace/tg5040/libmsettings/msettings.c b/workspace/tg5040/libmsettings/msettings.c index 77401759a..27806e252 100644 --- a/workspace/tg5040/libmsettings/msettings.c +++ b/workspace/tg5040/libmsettings/msettings.c @@ -241,10 +241,10 @@ static Settings DefaultSettings = { .turbo_r2 = 0, .jack = 0, .audiosink = AUDIO_SINK_DEFAULT, - .displaycal_enabled = SETTINGS_DEFAULT_DISPLAYCAL_ENABLED, - .displaycal_red_gain = SETTINGS_DEFAULT_DISPLAYCAL_RED_GAIN, - .displaycal_green_gain = SETTINGS_DEFAULT_DISPLAYCAL_GREEN_GAIN, - .displaycal_blue_gain = SETTINGS_DEFAULT_DISPLAYCAL_BLUE_GAIN, + .displaycal_enabled = DISPLAYCAL_DEFAULT_ENABLED, + .displaycal_red_gain = DISPLAYCAL_DEFAULT_RED_GAIN, + .displaycal_green_gain = DISPLAYCAL_DEFAULT_GREEN_GAIN, + .displaycal_blue_gain = DISPLAYCAL_DEFAULT_BLUE_GAIN, }; static Settings* settings; @@ -316,27 +316,12 @@ int peekVersion(const char *filename) { static int is_brick = 0; -static int defaultDisplayCalEnabledForDevice(void) { - return is_brick ? 1 : SETTINGS_DEFAULT_DISPLAYCAL_ENABLED; -} - -static int defaultDisplayCalRedGainForDevice(void) { - return 100; -} - -static int defaultDisplayCalGreenGainForDevice(void) { - return is_brick ? 92 : SETTINGS_DEFAULT_DISPLAYCAL_GREEN_GAIN; -} - -static int defaultDisplayCalBlueGainForDevice(void) { - return is_brick ? 58 : SETTINGS_DEFAULT_DISPLAYCAL_BLUE_GAIN; -} - static void applyDisplayCalDefaultsForDevice(Settings *target) { - target->displaycal_enabled = defaultDisplayCalEnabledForDevice(); - target->displaycal_red_gain = defaultDisplayCalRedGainForDevice(); - target->displaycal_green_gain = defaultDisplayCalGreenGainForDevice(); - target->displaycal_blue_gain = defaultDisplayCalBlueGainForDevice(); + DisplayCalDefaults defaults = DisplayCal_getDefaultSettings(is_brick); + target->displaycal_enabled = defaults.enabled; + target->displaycal_red_gain = defaults.red_gain; + target->displaycal_green_gain = defaults.green_gain; + target->displaycal_blue_gain = defaults.blue_gain; } void InitSettings(void) { diff --git a/workspace/tg5040/libmsettings/msettings.h b/workspace/tg5040/libmsettings/msettings.h index 6957d9596..db46458bf 100644 --- a/workspace/tg5040/libmsettings/msettings.h +++ b/workspace/tg5040/libmsettings/msettings.h @@ -6,10 +6,6 @@ #define SETTINGS_DEFAULT_CONTRAST 0 #define SETTINGS_DEFAULT_SATURATION 0 #define SETTINGS_DEFAULT_EXPOSURE 0 -#define SETTINGS_DEFAULT_DISPLAYCAL_ENABLED 0 -#define SETTINGS_DEFAULT_DISPLAYCAL_RED_GAIN 100 -#define SETTINGS_DEFAULT_DISPLAYCAL_GREEN_GAIN 100 -#define SETTINGS_DEFAULT_DISPLAYCAL_BLUE_GAIN 100 #define SETTINGS_DEFAULT_VOLUME 8 #define SETTINGS_DEFAULT_HEADPHONE_VOLUME 4 #define SETTINGS_DEFAULT_FAN_SPEED 0 diff --git a/workspace/tg5050/libmsettings/msettings.c b/workspace/tg5050/libmsettings/msettings.c index 3353ec0bd..789ae36bc 100644 --- a/workspace/tg5050/libmsettings/msettings.c +++ b/workspace/tg5050/libmsettings/msettings.c @@ -114,10 +114,10 @@ static Settings DefaultSettings = { .jack = 0, .audiosink = AUDIO_SINK_DEFAULT, .fanSpeed = SETTINGS_DEFAULT_FAN_SPEED, - .displaycal_enabled = SETTINGS_DEFAULT_DISPLAYCAL_ENABLED, - .displaycal_red_gain = SETTINGS_DEFAULT_DISPLAYCAL_RED_GAIN, - .displaycal_green_gain = SETTINGS_DEFAULT_DISPLAYCAL_GREEN_GAIN, - .displaycal_blue_gain = SETTINGS_DEFAULT_DISPLAYCAL_BLUE_GAIN, + .displaycal_enabled = DISPLAYCAL_DEFAULT_ENABLED, + .displaycal_red_gain = DISPLAYCAL_DEFAULT_RED_GAIN, + .displaycal_green_gain = DISPLAYCAL_DEFAULT_GREEN_GAIN, + .displaycal_blue_gain = DISPLAYCAL_DEFAULT_BLUE_GAIN, }; static Settings* settings; diff --git a/workspace/tg5050/libmsettings/msettings.h b/workspace/tg5050/libmsettings/msettings.h index ba75b4379..ae756b649 100644 --- a/workspace/tg5050/libmsettings/msettings.h +++ b/workspace/tg5050/libmsettings/msettings.h @@ -6,10 +6,6 @@ #define SETTINGS_DEFAULT_CONTRAST 0 #define SETTINGS_DEFAULT_SATURATION 0 #define SETTINGS_DEFAULT_EXPOSURE 0 -#define SETTINGS_DEFAULT_DISPLAYCAL_ENABLED 0 -#define SETTINGS_DEFAULT_DISPLAYCAL_RED_GAIN 100 -#define SETTINGS_DEFAULT_DISPLAYCAL_GREEN_GAIN 100 -#define SETTINGS_DEFAULT_DISPLAYCAL_BLUE_GAIN 100 #define SETTINGS_DEFAULT_VOLUME 8 #define SETTINGS_DEFAULT_HEADPHONE_VOLUME 4 #define SETTINGS_DEFAULT_FAN_SPEED -2 // Default fan curve From 38eb92e6ac1bcc0ca21b71ebcc9ad773739d602c Mon Sep 17 00:00:00 2001 From: Prashant Vaibhav Date: Sun, 14 Jun 2026 20:23:22 +0200 Subject: [PATCH 23/27] fix: qualify Brick display calibration default check Use the DeviceInfo enum scope when checking whether the settings UI is running on Brick. This fixes the display calibration defaults refactor build failure where Brick was referenced outside the class scope. --- workspace/all/settings/settings.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/workspace/all/settings/settings.cpp b/workspace/all/settings/settings.cpp index eaef40791..2ba1b6542 100644 --- a/workspace/all/settings/settings.cpp +++ b/workspace/all/settings/settings.cpp @@ -489,7 +489,7 @@ int main(int argc, char *argv[]) if(deviceInfo.hasDisplayCal()) { - const DisplayCalDefaults defaultDisplayCal = DisplayCal_getDefaultSettings(deviceInfo.getModel() == Brick); + const DisplayCalDefaults defaultDisplayCal = DisplayCal_getDefaultSettings(deviceInfo.getModel() == DeviceInfo::Brick); displayItems.push_back( new MenuItem{ListItemType::Generic, "White point correction", "Corrects the display white point to better match the sRGB standard.", {false, true}, on_off, []() -> std::any { return GetDisplayCalEnabled() != 0; }, [](const std::any &value) From a9c55becf7739c9f201b969503a35c1e6f3e4b2e Mon Sep 17 00:00:00 2001 From: Prashant Vaibhav Date: Sun, 14 Jun 2026 20:43:08 +0200 Subject: [PATCH 24/27] fix: avoid redundant display calibration disable writes Skip raw display calibration writes when calibration is already disabled so boot and repeated reset paths do not issue unnecessary LUT or disable ioctls. Keep an explicit disable write for live on-to-off transitions, and mirror the behavior in desktop logging for parity. --- workspace/desktop/libmsettings/msettings.c | 16 ++++++++++++---- workspace/tg5040/libmsettings/msettings.c | 17 ++++++++++++----- workspace/tg5050/libmsettings/msettings.c | 17 ++++++++++++----- 3 files changed, 36 insertions(+), 14 deletions(-) diff --git a/workspace/desktop/libmsettings/msettings.c b/workspace/desktop/libmsettings/msettings.c index 9ca152dc3..d327a34d1 100644 --- a/workspace/desktop/libmsettings/msettings.c +++ b/workspace/desktop/libmsettings/msettings.c @@ -386,7 +386,8 @@ static inline void SaveSettings(void) { } } static inline void applyDisplayCalSettings(void) { - SetRawDisplayCal(msettings->displaycal_enabled, msettings->displaycal_red_gain, msettings->displaycal_green_gain, msettings->displaycal_blue_gain); + if (msettings->displaycal_enabled) + SetRawDisplayCal(1, msettings->displaycal_red_gain, msettings->displaycal_green_gain, msettings->displaycal_blue_gain); } void QuitSettings(void){ SaveSettings(); @@ -456,9 +457,16 @@ void SetColortemp(int value) {} void SetContrast(int value) {} void SetSaturation(int value) {} void SetExposure(int value) {} -void SetDisplayCalEnabled(int value) { - msettings->displaycal_enabled = value ? 1 : 0; - applyDisplayCalSettings(); +void SetDisplayCalEnabled(int is_enabled) { + int was_enabled = msettings->displaycal_enabled; + is_enabled = (is_enabled != 0); + msettings->displaycal_enabled = is_enabled; + + // Disabling only needs raw writes when we are turning an active LUT off. + if (is_enabled) + applyDisplayCalSettings(); + else if (was_enabled) + SetRawDisplayCal(0, msettings->displaycal_red_gain, msettings->displaycal_green_gain, msettings->displaycal_blue_gain); SaveSettings(); } void SetDisplayCalRedGain(int value) { diff --git a/workspace/tg5040/libmsettings/msettings.c b/workspace/tg5040/libmsettings/msettings.c index 27806e252..d23740b8e 100644 --- a/workspace/tg5040/libmsettings/msettings.c +++ b/workspace/tg5040/libmsettings/msettings.c @@ -566,7 +566,8 @@ static inline void SaveSettings(void) { } static inline void applyDisplayCalSettings(void) { - SetRawDisplayCal(settings->displaycal_enabled, settings->displaycal_red_gain, settings->displaycal_green_gain, settings->displaycal_blue_gain); + if (settings->displaycal_enabled) + SetRawDisplayCal(1, settings->displaycal_red_gain, settings->displaycal_green_gain, settings->displaycal_blue_gain); } ///////// Getters exposed in public API @@ -752,10 +753,16 @@ void SetExposure(int value){ settings->exposure = value; SaveSettings(); } -void SetDisplayCalEnabled(int value) { - value = value ? 1 : 0; - settings->displaycal_enabled = value; - applyDisplayCalSettings(); +void SetDisplayCalEnabled(int is_enabled) { + int was_enabled = settings->displaycal_enabled; + is_enabled = (is_enabled != 0); + settings->displaycal_enabled = is_enabled; + + // Disabling only needs hardware writes when we are turning an active LUT off. + if (is_enabled) + applyDisplayCalSettings(); + else if (was_enabled) + SetRawDisplayCal(0, settings->displaycal_red_gain, settings->displaycal_green_gain, settings->displaycal_blue_gain); SaveSettings(); } void SetDisplayCalRedGain(int value) { diff --git a/workspace/tg5050/libmsettings/msettings.c b/workspace/tg5050/libmsettings/msettings.c index 789ae36bc..13b7b10fe 100644 --- a/workspace/tg5050/libmsettings/msettings.c +++ b/workspace/tg5050/libmsettings/msettings.c @@ -281,7 +281,8 @@ static inline void SaveSettings(void) { } static inline void applyDisplayCalSettings(void) { - SetRawDisplayCal(settings->displaycal_enabled, settings->displaycal_red_gain, settings->displaycal_green_gain, settings->displaycal_blue_gain); + if (settings->displaycal_enabled) + SetRawDisplayCal(1, settings->displaycal_red_gain, settings->displaycal_green_gain, settings->displaycal_blue_gain); } ///////// Getters exposed in public API @@ -470,10 +471,16 @@ void SetExposure(int value){ settings->exposure = value; SaveSettings(); } -void SetDisplayCalEnabled(int value) { - value = value ? 1 : 0; - settings->displaycal_enabled = value; - applyDisplayCalSettings(); +void SetDisplayCalEnabled(int is_enabled) { + int was_enabled = settings->displaycal_enabled; + is_enabled = (is_enabled != 0); + settings->displaycal_enabled = is_enabled; + + // Disabling only needs hardware writes when we are turning an active LUT off. + if (is_enabled) + applyDisplayCalSettings(); + else if (was_enabled) + SetRawDisplayCal(0, settings->displaycal_red_gain, settings->displaycal_green_gain, settings->displaycal_blue_gain); SaveSettings(); } void SetDisplayCalRedGain(int value) { From e0f26a8636d4f4529982893f01a92b26ea9bedb3 Mon Sep 17 00:00:00 2001 From: Prashant Vaibhav Date: Sun, 14 Jun 2026 21:14:49 +0200 Subject: [PATCH 25/27] fix: disable display calibration on tg5050 Hide white point correction for tg5050 because the platform does not expose /dev/disp. Make the tg5050 settings backend force display calibration off, keep raw display calibration as an unsupported no-op, and stop linking displaycal.c for that target while preserving tg5040 support. --- workspace/all/settings/settings.cpp | 3 +-- workspace/tg5050/libmsettings/makefile | 3 +-- workspace/tg5050/libmsettings/msettings.c | 30 +++++++---------------- 3 files changed, 11 insertions(+), 25 deletions(-) diff --git a/workspace/all/settings/settings.cpp b/workspace/all/settings/settings.cpp index 2ba1b6542..6f75ac011 100644 --- a/workspace/all/settings/settings.cpp +++ b/workspace/all/settings/settings.cpp @@ -288,9 +288,8 @@ namespace { return m_platform == tg5040; } - // Exposed on all platforms; desktop raw apply only logs. bool hasDisplayCal() const { - return true; + return m_platform == tg5040; } bool hasActiveCooling() const { diff --git a/workspace/tg5050/libmsettings/makefile b/workspace/tg5050/libmsettings/makefile index ae6aecf52..804df7d52 100644 --- a/workspace/tg5050/libmsettings/makefile +++ b/workspace/tg5050/libmsettings/makefile @@ -20,8 +20,7 @@ OPTM=-Ofast build: $(CC) -c -Werror -fpic "$(TARGET).c" $(CFLAGS) -Wl,--no-as-needed $(LDFLAGS) - $(CC) -c -Werror -fpic "../../all/common/displaycal.c" $(CFLAGS) -Wl,--no-as-needed $(LDFLAGS) - $(CC) -shared -o "lib$(TARGET).so" "$(TARGET).o" "displaycal.o" $(LDFLAGS) + $(CC) -shared -o "lib$(TARGET).so" "$(TARGET).o" $(LDFLAGS) mkdir -p "$(PREFIX_LOCAL)/include" mkdir -p "$(PREFIX_LOCAL)/lib" cp "$(TARGET).h" "$(PREFIX_LOCAL)/include" diff --git a/workspace/tg5050/libmsettings/msettings.c b/workspace/tg5050/libmsettings/msettings.c index 13b7b10fe..a8ddea691 100644 --- a/workspace/tg5050/libmsettings/msettings.c +++ b/workspace/tg5050/libmsettings/msettings.c @@ -260,6 +260,7 @@ void InitSettings(void) { } // This will implicitly update all other settings based on FN switch state + settings->displaycal_enabled = 0; SetMute(settings->mute); SetFanSpeed(settings->fanSpeed); @@ -281,8 +282,7 @@ static inline void SaveSettings(void) { } static inline void applyDisplayCalSettings(void) { - if (settings->displaycal_enabled) - SetRawDisplayCal(1, settings->displaycal_red_gain, settings->displaycal_green_gain, settings->displaycal_blue_gain); + // tg5050 does not expose /dev/disp, so display calibration is unsupported. } ///////// Getters exposed in public API @@ -472,15 +472,8 @@ void SetExposure(int value){ SaveSettings(); } void SetDisplayCalEnabled(int is_enabled) { - int was_enabled = settings->displaycal_enabled; - is_enabled = (is_enabled != 0); - settings->displaycal_enabled = is_enabled; - - // Disabling only needs hardware writes when we are turning an active LUT off. - if (is_enabled) - applyDisplayCalSettings(); - else if (was_enabled) - SetRawDisplayCal(0, settings->displaycal_red_gain, settings->displaycal_green_gain, settings->displaycal_blue_gain); + (void)is_enabled; + settings->displaycal_enabled = 0; SaveSettings(); } void SetDisplayCalRedGain(int value) { @@ -1083,16 +1076,11 @@ void SetRawExposure(int val){ } void SetRawDisplayCal(int enabled, int red_gain, int green_gain, int blue_gain) { - printf("SetRawDisplayCal(%i,%i,%i,%i)\n", enabled, red_gain, green_gain, blue_gain); fflush(stdout); - - int ret = enabled - ? DisplayCal_enableWithValues(red_gain, green_gain, blue_gain) - : DisplayCal_disable(); - if (ret != 0) { - fprintf(stderr, "SetRawDisplayCal failed to %s display calibration: %i\n", - enabled ? "enable" : "disable", ret); - fflush(stderr); - } + (void)enabled; + (void)red_gain; + (void)green_gain; + (void)blue_gain; + printf("SetRawDisplayCal unsupported on tg5050\n"); fflush(stdout); } void SetRawColortemp(int val) { // 0 - 255 From 1767749b90e3396f8b9e13a8c43399d39f9d8c95 Mon Sep 17 00:00:00 2001 From: Prashant Vaibhav Date: Sun, 14 Jun 2026 21:16:44 +0200 Subject: [PATCH 26/27] fix: move display calibration gain controls Move the red, green, and blue display calibration gain controls to the bottom of the Display settings menu before reset. Simplify their descriptions by removing the neutral-value wording. --- workspace/all/settings/settings.cpp | 34 ++++++++++++++++------------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/workspace/all/settings/settings.cpp b/workspace/all/settings/settings.cpp index 6f75ac011..29207ded9 100644 --- a/workspace/all/settings/settings.cpp +++ b/workspace/all/settings/settings.cpp @@ -494,21 +494,6 @@ int main(int argc, char *argv[]) { return GetDisplayCalEnabled() != 0; }, [](const std::any &value) { SetDisplayCalEnabled(std::any_cast(value)); }, [defaultDisplayCal]() { SetDisplayCalEnabled(defaultDisplayCal.enabled); }}); - displayItems.push_back( - new MenuItem{ListItemType::Generic, "Red gain", "White point correction red channel gain (0 to 200, 100 is neutral)", DISPLAYCAL_GAIN_MIN, DISPLAYCAL_GAIN_MAX, "", []() -> std::any - { return GetDisplayCalRedGain(); }, [](const std::any &value) - { SetDisplayCalRedGain(std::any_cast(value)); }, - [defaultDisplayCal]() { SetDisplayCalRedGain(defaultDisplayCal.red_gain); }}); - displayItems.push_back( - new MenuItem{ListItemType::Generic, "Green gain", "White point correction green channel gain (0 to 200, 100 is neutral)", DISPLAYCAL_GAIN_MIN, DISPLAYCAL_GAIN_MAX, "", []() -> std::any - { return GetDisplayCalGreenGain(); }, [](const std::any &value) - { SetDisplayCalGreenGain(std::any_cast(value)); }, - [defaultDisplayCal]() { SetDisplayCalGreenGain(defaultDisplayCal.green_gain); }}); - displayItems.push_back( - new MenuItem{ListItemType::Generic, "Blue gain", "White point correction blue channel gain (0 to 200, 100 is neutral)", DISPLAYCAL_GAIN_MIN, DISPLAYCAL_GAIN_MAX, "", []() -> std::any - { return GetDisplayCalBlueGain(); }, [](const std::any &value) - { SetDisplayCalBlueGain(std::any_cast(value)); }, - [defaultDisplayCal]() { SetDisplayCalBlueGain(defaultDisplayCal.blue_gain); }}); } if(deviceInfo.hasContrastSaturation()) @@ -533,6 +518,25 @@ int main(int argc, char *argv[]) { SetExposure(std::any_cast(value)); }, []() { SetExposure(SETTINGS_DEFAULT_EXPOSURE);}}); } + if(deviceInfo.hasDisplayCal()) + { + const DisplayCalDefaults defaultDisplayCal = DisplayCal_getDefaultSettings(deviceInfo.getModel() == DeviceInfo::Brick); + displayItems.push_back( + new MenuItem{ListItemType::Generic, "Red gain", "White point correction red channel gain (0 to 200)", DISPLAYCAL_GAIN_MIN, DISPLAYCAL_GAIN_MAX, "", []() -> std::any + { return GetDisplayCalRedGain(); }, [](const std::any &value) + { SetDisplayCalRedGain(std::any_cast(value)); }, + [defaultDisplayCal]() { SetDisplayCalRedGain(defaultDisplayCal.red_gain); }}); + displayItems.push_back( + new MenuItem{ListItemType::Generic, "Green gain", "White point correction green channel gain (0 to 200)", DISPLAYCAL_GAIN_MIN, DISPLAYCAL_GAIN_MAX, "", []() -> std::any + { return GetDisplayCalGreenGain(); }, [](const std::any &value) + { SetDisplayCalGreenGain(std::any_cast(value)); }, + [defaultDisplayCal]() { SetDisplayCalGreenGain(defaultDisplayCal.green_gain); }}); + displayItems.push_back( + new MenuItem{ListItemType::Generic, "Blue gain", "White point correction blue channel gain (0 to 200)", DISPLAYCAL_GAIN_MIN, DISPLAYCAL_GAIN_MAX, "", []() -> std::any + { return GetDisplayCalBlueGain(); }, [](const std::any &value) + { SetDisplayCalBlueGain(std::any_cast(value)); }, + [defaultDisplayCal]() { SetDisplayCalBlueGain(defaultDisplayCal.blue_gain); }}); + } displayItems.push_back( new MenuItem{ListItemType::Button, "Reset to defaults", "Resets all options in this menu to their default values.", ResetCurrentMenu}); From 431245080b23e2cfcb87858fc0da842d7e6eb646 Mon Sep 17 00:00:00 2001 From: frysee Date: Sun, 28 Jun 2026 12:36:20 +0200 Subject: [PATCH 27/27] feat(tg5040): add white point preset for smartpro --- workspace/all/common/displaycal.h | 45 ++++++++++++++++++++--- workspace/all/settings/settings.cpp | 19 ++++------ workspace/tg5040/libmsettings/msettings.c | 20 ++++------ 3 files changed, 56 insertions(+), 28 deletions(-) diff --git a/workspace/all/common/displaycal.h b/workspace/all/common/displaycal.h index cc9c15178..8a0a65e3f 100644 --- a/workspace/all/common/displaycal.h +++ b/workspace/all/common/displaycal.h @@ -8,11 +8,17 @@ #define DISPLAYCAL_DEFAULT_RED_GAIN 100 #define DISPLAYCAL_DEFAULT_GREEN_GAIN 100 #define DISPLAYCAL_DEFAULT_BLUE_GAIN 100 + #define DISPLAYCAL_BRICK_DEFAULT_ENABLED 1 #define DISPLAYCAL_BRICK_DEFAULT_RED_GAIN DISPLAYCAL_DEFAULT_RED_GAIN #define DISPLAYCAL_BRICK_DEFAULT_GREEN_GAIN 92 #define DISPLAYCAL_BRICK_DEFAULT_BLUE_GAIN 58 +#define DISPLAYCAL_SMARTPRO_DEFAULT_ENABLED 1 +#define DISPLAYCAL_SMARTPRO_DEFAULT_RED_GAIN DISPLAYCAL_DEFAULT_RED_GAIN +#define DISPLAYCAL_SMARTPRO_DEFAULT_GREEN_GAIN 77 +#define DISPLAYCAL_SMARTPRO_DEFAULT_BLUE_GAIN 50 + #ifdef __cplusplus extern "C" { #endif @@ -24,12 +30,41 @@ typedef struct DisplayCalDefaults { int blue_gain; } DisplayCalDefaults; -static inline DisplayCalDefaults DisplayCal_getDefaultSettings(int use_brick_overrides) { +// TrimUI Brick, measured with X-Rite i1Display Pro, calibrated to sRGB D65 2.2 +static const struct DisplayCalDefaults DisplayCalDefaults_Brick = { + DISPLAYCAL_BRICK_DEFAULT_ENABLED, + DISPLAYCAL_BRICK_DEFAULT_RED_GAIN, + DISPLAYCAL_BRICK_DEFAULT_GREEN_GAIN, + DISPLAYCAL_BRICK_DEFAULT_BLUE_GAIN +}; + +// TrimUI Smart Pro, measured with Spyder 5 Pro and slightly modified by eye to match the Brick +static const struct DisplayCalDefaults DisplayCalDefaults_SmartPro = { + DISPLAYCAL_SMARTPRO_DEFAULT_ENABLED, + DISPLAYCAL_SMARTPRO_DEFAULT_RED_GAIN, + DISPLAYCAL_SMARTPRO_DEFAULT_GREEN_GAIN, + DISPLAYCAL_SMARTPRO_DEFAULT_BLUE_GAIN +}; + +enum DisplayCalPreset { + DISPLAYCAL_PRESET_DEFAULT = 0, + DISPLAYCAL_PRESET_BRICK, + DISPLAYCAL_PRESET_SMARTPRO +}; + +static inline DisplayCalDefaults DisplayCal_getDefaultSettings(enum DisplayCalPreset preset) { + if(preset == DISPLAYCAL_PRESET_SMARTPRO) { + return DisplayCalDefaults_SmartPro; + } + if(preset == DISPLAYCAL_PRESET_BRICK) { + return DisplayCalDefaults_Brick; + } + // Default preset DisplayCalDefaults defaults = { - use_brick_overrides ? DISPLAYCAL_BRICK_DEFAULT_ENABLED : DISPLAYCAL_DEFAULT_ENABLED, - use_brick_overrides ? DISPLAYCAL_BRICK_DEFAULT_RED_GAIN : DISPLAYCAL_DEFAULT_RED_GAIN, - use_brick_overrides ? DISPLAYCAL_BRICK_DEFAULT_GREEN_GAIN : DISPLAYCAL_DEFAULT_GREEN_GAIN, - use_brick_overrides ? DISPLAYCAL_BRICK_DEFAULT_BLUE_GAIN : DISPLAYCAL_DEFAULT_BLUE_GAIN, + DISPLAYCAL_DEFAULT_ENABLED, + DISPLAYCAL_DEFAULT_RED_GAIN, + DISPLAYCAL_DEFAULT_GREEN_GAIN, + DISPLAYCAL_DEFAULT_BLUE_GAIN, }; return defaults; } diff --git a/workspace/all/settings/settings.cpp b/workspace/all/settings/settings.cpp index 29207ded9..f40153e1c 100644 --- a/workspace/all/settings/settings.cpp +++ b/workspace/all/settings/settings.cpp @@ -486,16 +486,6 @@ int main(int argc, char *argv[]) []() { SetColortemp(SETTINGS_DEFAULT_COLORTEMP);}}); } - if(deviceInfo.hasDisplayCal()) - { - const DisplayCalDefaults defaultDisplayCal = DisplayCal_getDefaultSettings(deviceInfo.getModel() == DeviceInfo::Brick); - displayItems.push_back( - new MenuItem{ListItemType::Generic, "White point correction", "Corrects the display white point to better match the sRGB standard.", {false, true}, on_off, []() -> std::any - { return GetDisplayCalEnabled() != 0; }, [](const std::any &value) - { SetDisplayCalEnabled(std::any_cast(value)); }, - [defaultDisplayCal]() { SetDisplayCalEnabled(defaultDisplayCal.enabled); }}); - } - if(deviceInfo.hasContrastSaturation()) { displayItems.push_back( @@ -520,7 +510,14 @@ int main(int argc, char *argv[]) } if(deviceInfo.hasDisplayCal()) { - const DisplayCalDefaults defaultDisplayCal = DisplayCal_getDefaultSettings(deviceInfo.getModel() == DeviceInfo::Brick); + const DisplayCalDefaults defaultDisplayCal = DisplayCal_getDefaultSettings( + deviceInfo.getModel() == DeviceInfo::Brick ? DISPLAYCAL_PRESET_BRICK : + deviceInfo.getModel() == DeviceInfo::SmartPro ? DISPLAYCAL_PRESET_SMARTPRO : DISPLAYCAL_PRESET_DEFAULT); + displayItems.push_back( + new MenuItem{ListItemType::Generic, "White point correction", "Corrects the display white point to better match the \nsRGB standard, at the expense of some peak brightness.", {false, true}, on_off, []() -> std::any + { return GetDisplayCalEnabled() != 0; }, [](const std::any &value) + { SetDisplayCalEnabled(std::any_cast(value)); }, + [defaultDisplayCal]() { SetDisplayCalEnabled(defaultDisplayCal.enabled); }}); displayItems.push_back( new MenuItem{ListItemType::Generic, "Red gain", "White point correction red channel gain (0 to 200)", DISPLAYCAL_GAIN_MIN, DISPLAYCAL_GAIN_MAX, "", []() -> std::any { return GetDisplayCalRedGain(); }, [](const std::any &value) diff --git a/workspace/tg5040/libmsettings/msettings.c b/workspace/tg5040/libmsettings/msettings.c index d23740b8e..6ec93eefd 100644 --- a/workspace/tg5040/libmsettings/msettings.c +++ b/workspace/tg5040/libmsettings/msettings.c @@ -315,9 +315,13 @@ int peekVersion(const char *filename) { } static int is_brick = 0; +static int is_smartpro = 0; static void applyDisplayCalDefaultsForDevice(Settings *target) { - DisplayCalDefaults defaults = DisplayCal_getDefaultSettings(is_brick); + DisplayCalDefaults defaults = DisplayCal_getDefaultSettings( + is_brick ? DISPLAYCAL_PRESET_BRICK + : is_smartpro ? DISPLAYCAL_PRESET_SMARTPRO + : DISPLAYCAL_PRESET_DEFAULT); target->displaycal_enabled = defaults.enabled; target->displaycal_red_gain = defaults.red_gain; target->displaycal_green_gain = defaults.green_gain; @@ -327,6 +331,7 @@ static void applyDisplayCalDefaultsForDevice(Settings *target) { void InitSettings(void) { char* device = getenv("DEVICE"); is_brick = exactMatch("brick", device); + is_smartpro = exactMatch("smartpro", device); sprintf(SettingsPath, "%s/msettings.bin", getenv("USERDATA_PATH")); @@ -354,7 +359,6 @@ void InitSettings(void) { else { // initialize with defaults memcpy(settings, &DefaultSettings, shm_size); - applyDisplayCalDefaultsForDevice(settings); // overwrite with migrated data if(version==10) { @@ -364,7 +368,6 @@ void InitSettings(void) { memcpy(settings, &old, sizeof(SettingsV10)); settings->version = SETTINGS_VERSION; - applyDisplayCalDefaultsForDevice(settings); } else if(version==9) { printf("Found settings v9.\n"); @@ -401,7 +404,6 @@ void InitSettings(void) { settings->speaker = old.speaker; settings->mute = old.mute; settings->jack = old.jack; - applyDisplayCalDefaultsForDevice(settings); } else if(version==8) { printf("Found settings v8.\n"); @@ -427,7 +429,6 @@ void InitSettings(void) { settings->speaker = old.speaker; settings->mute = old.mute; settings->jack = old.jack; - applyDisplayCalDefaultsForDevice(settings); } else if(version==7) { printf("Found settings v7.\n"); @@ -452,7 +453,6 @@ void InitSettings(void) { settings->speaker = old.speaker; settings->mute = old.mute; settings->jack = old.jack; - applyDisplayCalDefaultsForDevice(settings); } else if(version==6) { printf("Found settings v6.\n"); @@ -470,7 +470,6 @@ void InitSettings(void) { settings->speaker = old.speaker; settings->mute = old.mute; settings->jack = old.jack; - applyDisplayCalDefaultsForDevice(settings); } else if(version==5) { printf("Found settings v5.\n"); @@ -484,7 +483,6 @@ void InitSettings(void) { settings->speaker = old.speaker; settings->mute = old.mute; settings->jack = old.jack; - applyDisplayCalDefaultsForDevice(settings); } else if(version==4) { printf("Found settings v4.\n"); @@ -499,7 +497,6 @@ void InitSettings(void) { settings->speaker = old.speaker; settings->mute = old.mute; settings->jack = old.jack; - applyDisplayCalDefaultsForDevice(settings); } else if(version==3) { printf("Found settings v3.\n"); @@ -511,7 +508,6 @@ void InitSettings(void) { settings->speaker = old.speaker; settings->mute = old.mute; settings->jack = old.jack; - applyDisplayCalDefaultsForDevice(settings); } else { printf("Found unsupported settings version: %i.\n", version); @@ -523,13 +519,11 @@ void InitSettings(void) { else { // load defaults memcpy(settings, &DefaultSettings, shm_size); - applyDisplayCalDefaultsForDevice(settings); } } else { // load defaults memcpy(settings, &DefaultSettings, shm_size); - applyDisplayCalDefaultsForDevice(settings); } // these shouldn't be persisted @@ -546,6 +540,8 @@ void InitSettings(void) { system("amixer sset 'DAC Swap' Off"); // Fix L/R channels } + applyDisplayCalDefaultsForDevice(settings); + // This will implicitly update all other settings based on FN switch state SetMute(settings->mute); }