aboutsummaryrefslogtreecommitdiffstats
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
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>
-rw-r--r--README31
-rw-r--r--TODO5
-rw-r--r--cg-base.c2
-rw-r--r--radharc.131
-rw-r--r--radharc.c216
5 files changed, 266 insertions, 19 deletions
diff --git a/README b/README
index 5ecfe2e..8b820f5 100644
--- a/README
+++ b/README
@@ -94,11 +94,42 @@ OPTIONS
-t temperature
Colour temperature, in Kelvins, to apply.
+ If temperature is '?' or 'get', the utility will print the
+ currently applied temperature.
+
-x
Remove the currently applied filter.
OPERANDS
No operands are supported.
+SIGNALS
+ The following signals have non-default meaning:
+
+ SIGINT
+ Gradually fade out the effect and terminate the process.
+
+ If the fade out time is 0 (default) or if the signal is sent
+ twice, the effect is removed immediately.
+
+ SIGHUP
+ Terminate the process after any current fade in or fade out
+ action, but leaf the effect in place.
+
+ SIGUSR1
+ Gradually fade out the effect but do not terminate the process.
+
+ If the fade out time is 0 (default) or if the signal is sent
+ twice, the effect is removed immediately.
+
+ SIGUSR2
+ Gradually fade the effect back in (from SIGUSR1).
+
+ If the fade in time is 0 (default) or if the signal is sent
+ twice, the effect is restored immediately.
+
+ If sent during the initial fade in, the fade in is cancelled
+ and the full effect is immediately applied.
+
SEE ALSO
coopgammad(1), cg-tools(7), redshift(1), blueshift(1)
diff --git a/TODO b/TODO
index f187ecb..6b315ef 100644
--- a/TODO
+++ b/TODO
@@ -1,4 +1,3 @@
-On fade in, query active setting and start from there. (requires libred>=1.1)
Use bus for changing settings and CRTCs online.
-Add "-t ?" and "-t get" for getting the current temperature. (requires libred>=1.1)
-Add PF_UNIX SOCK_DGRAM socket with abstract address @/proc/<pid>/radharc for IPC
+Add PF_UNIX SOCK_DGRAM socket with abstract address @/proc/<pid>/<class> for IPC
+Add support for non-sRGB, RGB monitors
diff --git a/cg-base.c b/cg-base.c
index 1df9e0d..3ed84fd 100644
--- a/cg-base.c
+++ b/cg-base.c
@@ -697,7 +697,7 @@ main(int argc, char *argv[])
fprintf(stderr, "%s: no CRTC:s are available\n", argv0);
goto custom_fail;
}
-
+
if (!*class_suffixes) {
classes = &class;
classes_n = 1;
diff --git a/radharc.1 b/radharc.1
index 1f4e030..cea8802 100644
--- a/radharc.1
+++ b/radharc.1
@@ -153,6 +153,14 @@ for local display 0 when using X.
.TP
.BR -t \ \fItemperature\fP
Colour temperature, in Kelvins, to apply.
+
+If
+.I temperature
+is
+.RB ' ? '
+or
+.RB ' get ',
+the utility will print the currently applied temperature.
.TP
.B -x
Remove the currently applied filter.
@@ -187,7 +195,28 @@ If the fade in time is 0 (default) or if the signal is
sent twice, the effect is restored immediately.
If sent during the initial fade in, the fade in is cancelled
-and the full effect is immediately applied
+and the full effect is immediately applied.
+
+.SH STDOUT
+The standard output is normally not used, however if
+.I temperature
+has been specified with
+.RB ' get '
+or
+.RB ' ? ',
+the colour temperature that was set before when the
+program started is printed to standard output on the
+following format:
+.P
+.RS
+.nf
+\fB\(dq%uK\en\(dq, \fP<\fIpreapplied temperature\fP>
+.fi
+.RE
+.P
+where it is garanteed that
+.I preapplied temperature
+is within [1000, 40000].
.SH SEE ALSO
.BR coopgammad (1),
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;