aboutsummaryrefslogtreecommitdiffstats
path: root/radharc.c
diff options
context:
space:
mode:
authorMattias Andrée <m@maandree.se>2025-02-05 17:54:08 +0100
committerMattias Andrée <m@maandree.se>2025-02-05 17:54:08 +0100
commit0f60b84867f0a275a05353f5da13973c8d0d1910 (patch)
tree4baf40316a1e04eb42eb757728583b239006f85a /radharc.c
parentAdd support for SIGUSR1 and SIGUSR2 (diff)
downloadradharc-0f60b84867f0a275a05353f5da13973c8d0d1910.tar.gz
radharc-0f60b84867f0a275a05353f5da13973c8d0d1910.tar.bz2
radharc-0f60b84867f0a275a05353f5da13973c8d0d1910.tar.xz
Add support for -t get/? and transition from applied temperature when starting1.2
Signed-off-by: Mattias Andrée <m@maandree.se>
Diffstat (limited to 'radharc.c')
-rw-r--r--radharc.c216
1 files changed, 202 insertions, 14 deletions
diff --git a/radharc.c b/radharc.c
index c651b8f..0805b33 100644
--- a/radharc.c
+++ b/radharc.c
@@ -11,6 +11,14 @@
#include <libclut.h>
#include <libred.h>
+
+#if 0
+# define IF_LINEARISING(...) __VA_ARGS__
+#else
+# define IF_LINEARISING(...) ((void)0)
+#endif
+
+
/**
* The default filter priority for the program
*/
@@ -92,6 +100,21 @@ static int dflag = 0;
*/
static int xflag = 0;
+/**
+ * Whether the currently applied temperature should be
+ * printed when the program starts
+ */
+static int print_temperature = 0;
+
+
+/**
+ * Set to 1 by `handle_args` if the used arguments
+ * does not describe an action that requires the
+ * temperature to be modified; that is, only "-t get"
+ * (or "-t ?" is specified)
+ */
+static int no_temperature_change = 0;
+
/**
* If 0, SIGINT has not been received,
@@ -238,9 +261,13 @@ handle_opt(char *opt, char *arg)
xflag = 0;
return 1;
case 't':
- if (parse_double(&choosen_temperature, arg))
- usage();
- xflag = 0;
+ if (!strcmp(arg, "get") || !strcmp(arg, "?")) {
+ print_temperature = 1;
+ } else {
+ if (parse_double(&choosen_temperature, arg))
+ usage();
+ xflag = 0;
+ }
return 1;
case 'x':
xflag = 1;
@@ -268,7 +295,8 @@ handle_opt(char *opt, char *arg)
int
handle_args(int argc, char *argv[], char *prio)
{
- if (argc || (!xflag && !have_location && choosen_temperature < 0))
+ no_temperature_change = (!xflag && !have_location && choosen_temperature < 0);
+ if (argc || (no_temperature_change && !print_temperature))
usage();
return 0;
(void) argv;
@@ -291,9 +319,9 @@ fill_filter(libcoopgamma_filter_t *restrict filter, double red, double green, do
#define X(CONST, MEMBER, MAX, TYPE)\
case CONST:\
libclut_start_over(&(filter->ramps.MEMBER), MAX, TYPE, 1, 1, 1);\
- libclut_linearise(&(filter->ramps.MEMBER), MAX, TYPE, 1, 1, 1);\
+ IF_LINEARISING(libclut_linearise(&(filter->ramps.MEMBER), (MAX), TYPE, 1, 1, 1));\
libclut_rgb_brightness(&(filter->ramps.MEMBER), MAX, TYPE, red, green, blue);\
- libclut_standardise(&(filter->ramps.MEMBER), MAX, TYPE, 1, 1, 1);\
+ IF_LINEARISING(libclut_standardise(&(filter->ramps.MEMBER), (MAX), TYPE, 1, 1, 1));\
break;
LIST_DEPTHS
#undef X
@@ -320,7 +348,6 @@ set_ramps(double red, double green, double blue)
int r;
size_t i, j;
- libclut_model_standard_to_linear(&red, &green, &blue);
for (i = 0, r = 1; i < filters_n; i++) {
if (!crtc_updates[i].master || !crtc_info[crtc_updates[i].crtc].supported)
continue;
@@ -351,6 +378,34 @@ set_ramps(double red, double green, double blue)
/**
+ * Get the colour of a temperature
+ *
+ * @param t The temperature, in Kelvin
+ * @param r_out Output parameter for the red channel multiplier
+ * @param g_out Output parameter for the green channel multiplier
+ * @param b_out Output parameter for the blue channel multiplier
+ * @return 0 on success, -1 on failure
+ */
+static int
+get_colour(long int t, double *r_out, double *g_out, double *b_out)
+{
+ double x, y, z, max;
+ if (libred_get_colour_xy(t, &x, &y))
+ return -1;
+ libclut_model_ciexyy_to_ciexyz(x, y, 1.0, &x, &z);
+ libclut_model_ciexyz_to_linear(x, 1.0, z, r_out, g_out, b_out);
+ *r_out = fmax(0.0, *r_out);
+ *g_out = fmax(0.0, *g_out);
+ *b_out = fmax(0.0, *b_out);
+ max = fmax(fmax(*r_out, *g_out), *b_out);
+ *r_out /= max;
+ *g_out /= max;
+ *b_out /= max;
+ return 0;
+}
+
+
+/**
* Get the colour temperature for the current time
*
* @param tp Output parameter for the colour temperature
@@ -376,6 +431,120 @@ get_temperature(double *tp)
/**
+ * Get the currently applied colour temperature
+ *
+ * @param tp Output parameter for the colour temperature
+ * @return 0: Success
+ * -1: Error, `errno` set
+ * -2: Error, `cg.error` set
+ */
+static int
+get_applied_temperature(double *tp)
+{
+ libcoopgamma_filter_table_t table;
+ libcoopgamma_filter_query_t query;
+ libcoopgamma_ramps_t *restrict ramps;
+ size_t filter_i, i, tn = 0;
+ long double lred, lgreen, lblue;
+ double red, green, blue, t, tsum = 0;
+ double x, y, z;
+
+ if (libcoopgamma_set_nonblocking(&cg, 0) < 0)
+ return -1;
+
+ if (libcoopgamma_filter_table_initialise(&table) < 0)
+ return -1;
+ if (libcoopgamma_filter_query_initialise(&query) < 0)
+ return -1;
+ query.coalesce = 0;
+
+ for (filter_i = 0; filter_i < filters_n; filter_i++) {
+ query.crtc = crtc_updates[filter_i].filter.crtc;
+ while (libcoopgamma_get_gamma_sync(&query, &table, &cg) < 0) {
+ if (errno == EINTR)
+ continue;
+ return -1;
+ }
+
+ for (i = 0; i < table.filter_count; i++)
+ if (!strcmp(table.filters[i].class, crtc_updates[filter_i].filter.class))
+ break;
+ if (i == table.filter_count) {
+ no_filter:
+#if 0
+ fprintf(stderr, "%s: temperature on %s: not found\n", argv0, query.crtc);
+#endif
+ continue;
+ }
+
+ ramps = &table.filters[i].ramps;
+ if (!table.red_size || !table.green_size || !table.blue_size)
+ goto no_filter;
+
+ switch (table.depth) {
+#define X(CONST, MEMBER, MAX, TYPE)\
+ case CONST:\
+ IF_LINEARISING(libclut_linearise(&(ramps->MEMBER), (MAX), TYPE, 1, 1, 1));\
+ lred = (long double)ramps->MEMBER.red[table.red_size - 1U] / (MAX);\
+ lgreen = (long double)ramps->MEMBER.green[table.green_size - 1U] / (MAX);\
+ lblue = (long double)ramps->MEMBER.blue[table.blue_size - 1U] / (MAX);\
+ break
+ X(LIBCOOPGAMMA_DOUBLE, d, 1.0, double);
+ X(LIBCOOPGAMMA_FLOAT, f, 1.0, float);
+ X(LIBCOOPGAMMA_UINT8, u8, UINT8_MAX, uint8_t);
+ X(LIBCOOPGAMMA_UINT16, u16, UINT16_MAX, uint16_t);
+ X(LIBCOOPGAMMA_UINT32, u32, UINT32_MAX, uint32_t);
+ X(LIBCOOPGAMMA_UINT64, u64, UINT64_MAX, uint64_t);
+#undef X
+ default:
+ errno = EPROTO;
+ return -1;
+ }
+ red = (double)lred;
+ green = (double)lgreen;
+ blue = (double)lblue;
+
+ if (red > 0.996 && blue < 0.004) { /* out of gamut (1000K to 1900K) */
+ long int t1 = LIBRED_LOWEST_TEMPERATURE, t2;
+ double g1 = 0, r2, g2, b2;
+ for (t2 = LIBRED_LOWEST_TEMPERATURE; t2 <= 1900; t2 += LIBRED_DELTA_TEMPERATURE) {
+ libred_get_colour_xy(t2, &x, &y);
+ libclut_model_ciexyy_to_ciexyz(x, y, 1.0, &x, &z);
+ libclut_model_ciexyz_to_linear(x, 1.0, z, &r2, &g2, &b2);
+ g2 /= fmax(fmax(r2, g2), b2);
+ if (green <= g2) {
+ t = (green - g1) / (g2 - g1);
+ t = t1 + (t2 - t1) * t;
+ goto estimated;
+ }
+ t1 = t2;
+ g1 = g2;
+ }
+ t = 1900;
+ }
+
+ libclut_model_linear_to_ciexyz(red, green, blue, &x, &y, &z);
+ libclut_model_ciexyz_to_ciexyy(x, y, z, &x, &y);
+ t = libred_get_temperature_xy(x, y, &x, &y);
+#if 0
+ libred_get_colour_xy((long int)t, &x, &y);
+ x = sqrt(x*x + y*y);
+ fprintf(stderr, "%s: temperature on %s: %gK (error: %g)\n", argv0, query.crtc, t, x);
+#endif
+ estimated:
+ tsum += t;
+ tn += 1U;
+ }
+
+ if (libcoopgamma_set_nonblocking(&cg, 1) < 0)
+ return -1;
+
+ *tp = tn ? tsum / (double)tn : 6500;
+ return 0;
+}
+
+
+/**
* Called when SIGINT is received
*
* @param sig Always `SIGINT`
@@ -436,10 +605,10 @@ sigusr2_handler(int sig)
int
start(void)
{
- int r, tfd;
+ int r, tfd, initial_fade_in = 1;
size_t i;
double target_temperature;
- long int original_temperature;
+ long int original_temperature = 6500;
long int current_temperature = 6500;
double red = 1, green = 1, blue = 1;
uint64_t overrun;
@@ -476,6 +645,18 @@ start(void)
if (choosen_temperature < 0)
dflag = 1;
+ if (fade_in_cs || print_temperature) {
+ double t;
+ if ((r = get_applied_temperature(&t)))
+ return r;
+ original_temperature = (long int)(t + 0.5);
+ if (print_temperature)
+ printf("%liK\n", original_temperature);
+ }
+
+ if (no_temperature_change)
+ return 0;
+
if (xflag)
return set_ramps(1, 1, 1);
@@ -494,7 +675,14 @@ fade_in:
return -1;
fade_cs = (size_t)fade_in_cs;
-fade_in_have_timer:
+ if (initial_fade_in) {
+ initial_fade_in = 0;
+ if ((r = get_temperature(&target_temperature)) < 0)
+ return r;
+ } else {
+ fade_in_have_timer:
+ original_temperature = 6500;
+ }
for (i = 0; i < fade_cs;) {
if (sigint_received) {
goto reverse_fade_in;
@@ -512,8 +700,8 @@ fade_in_have_timer:
if (i % 600 == 0)
if ((r = get_temperature(&target_temperature)) < 0)
return r;
- current_temperature = (long int)(6500 - (6500 - target_temperature) * i / fade_cs);
- if (libred_get_colour(current_temperature, &red, &green, &blue))
+ current_temperature = (long int)(original_temperature - (original_temperature - target_temperature) * i / fade_cs);
+ if (get_colour(current_temperature, &red, &green, &blue))
return -1;
if ((r = set_ramps(red, green, blue)) < 0)
return r;
@@ -550,7 +738,7 @@ faded_in:
if ((r = get_temperature(&target_temperature)) < 0)
return r;
current_temperature = (long int)target_temperature;
- if (libred_get_colour(current_temperature, &red, &green, &blue))
+ if (get_colour(current_temperature, &red, &green, &blue))
return -1;
if ((r = set_ramps(red, green, blue)) < 0)
return r;
@@ -597,7 +785,7 @@ fade_out_have_timer:
}
current_temperature = original_temperature + (double)(6500 - original_temperature) * i / fade_cs;
- if (libred_get_colour(current_temperature, &red, &green, &blue))
+ if (get_colour(current_temperature, &red, &green, &blue))
goto fade_out_fail;
if ((r = set_ramps(red, green, blue)) < 0)
goto fade_out_fail_use_r;