Skip to content

Commit

Permalink
[xsct 1.9] Fix issue #32 by adding linear brightness parameter (PR #33)
Browse files Browse the repository at this point in the history
Add second argument: Linear brightness

Usage:
```sh
./xsct 4000 0.5 && ./xsct
```

Fixes #32 .
  • Loading branch information
zvezdochiot authored Dec 16, 2022
1 parent 1bfe075 commit f2aa7c8
Show file tree
Hide file tree
Showing 6 changed files with 140 additions and 82 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
PROJECT: https://github.com/faf0/sct

1.9: zvezdochiot on 15 Dec 2022
* Add [brightness] - linear brightness {0.0 .. 1.0}

1.8.1: Ionic and faf0 on 27 Jul 2022
* Allow Makefile default settings to be overridden by environment variables; patch
contributed by Ionic
Expand Down
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ MAN ?= $(PREFIX)/share/man/man1
INSTALL ?= install

PROG = xsct
SRCS = xsct.c
SRCS = src/xsct.c

LIBS = -lX11 -lXrandr -lm

Expand Down
14 changes: 9 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,14 +80,18 @@ gcc -Wall -Wextra -Werror -pedantic -std=c99 -O2 -I /usr/X11R6/include xsct.c -o

Provided that xsct binary is located in your `$PATH`, execute it using the following command:
~~~sh
xsct 3700
xsct 3700 0.9
~~~

The first parameter (`3700` above) represents the color temperature.
The first parameter (`3700` above) represents the color temperature.

If `xsct` is called with parameter 0, the color temperature is set to `6500`.
The second parameter (`0.9` above) represents the brightness. The values are in the range `[0.0, 1.0]`.

If `xsct` is called without parameters, the current display temperature is estimated.
If `xsct` is called with parameter 0, the color temperature is set to `6500`.

If `xsct` is called with the color temperature parameter only, the brightness is set to `1.0`.

If `xsct` is called without parameters, the current display temperature and brightness are estimated.

The following options, which can be specified before the optional temperature parameter, are supported:
- `-h`, `--help`: display the help page
Expand All @@ -98,7 +102,7 @@ The following options, which can be specified before the optional temperature pa

Test xsct using the following command:
~~~sh
xsct 3700 && xsct
xsct 3700 0.9 && xsct
~~~

# Resources
Expand Down
138 changes: 64 additions & 74 deletions xsct.c → src/xsct.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,70 +4,46 @@
* Public domain, do as you wish.
*/

#include <X11/Xatom.h>
#include <X11/Xlib.h>
#include <X11/Xproto.h>
#include <X11/extensions/Xrandr.h>

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include "xsct.h"

static void usage(char * pname)
{
printf("Xsct (1.8)\n"
"Usage: %s [options] [temperature]\n"
printf("Xsct (%s)\n"
"Usage: %s [options] [temperature] [brightness]\n"
"\tIf the argument is 0, xsct resets the display to the default temperature (6500K)\n"
"\tIf no arguments are passed, xsct estimates the current display temperature\n"
"\tIf no arguments are passed, xsct estimates the current display temperature and brightness\n"
"Options:\n"
"\t-h, --help \t xsct will display this usage information\n"
"\t-v, --verbose \t xsct will display debugging information\n"
"\t-d, --delta\t xsct will shift temperature by the temperature value\n"
"\t-s, --screen N\t xsct will only select screen specified by given zero-based index\n"
"\t-c, --crtc N\t xsct will only select CRTC specified by given zero-based index\n", pname);
"\t-c, --crtc N\t xsct will only select CRTC specified by given zero-based index\n", XSCT_VERSION, pname);
}

#define TEMPERATURE_NORM 6500
#define TEMPERATURE_ZERO 700
#define GAMMA_MULT 65535.0
// Approximation of the `redshift` table from
// https://github.com/jonls/redshift/blob/04760afe31bff5b26cf18fe51606e7bdeac15504/src/colorramp.c#L30-L273
// without limits:
// GAMMA = K0 + K1 * ln(T - T0)
// Red range (T0 = TEMPERATURE_ZERO)
// Green color
#define GAMMA_K0GR -1.47751309139817
#define GAMMA_K1GR 0.28590164772055
// Blue color
#define GAMMA_K0BR -4.38321650114872
#define GAMMA_K1BR 0.6212158769447
// Blue range (T0 = TEMPERATURE_NORM - TEMPERATURE_ZERO)
// Red color
#define GAMMA_K0RB 1.75390204039018
#define GAMMA_K1RB -0.1150805671482
// Green color
#define GAMMA_K0GB 1.49221604915144
#define GAMMA_K1GB -0.07513509588921

static double DoubleTrim(double x, double a, double b)
{
double buff[3] = {a, x, b};
return buff[ (int)(x > a) + (int)(x > b) ];
}

static int get_sct_for_screen(Display *dpy, int screen, int icrtc, int fdebug)
static struct temp_status get_sct_for_screen(Display *dpy, int screen, int icrtc, int fdebug)
{
Window root = RootWindow(dpy, screen);
XRRScreenResources *res = XRRGetScreenResourcesCurrent(dpy, root);

int temp = 0, n, c;
int n, c;
double t = 0.0;
double gammar = 0.0, gammag = 0.0, gammab = 0.0, gammam = 1.0, gammad = 0.0;
double gammar = 0.0, gammag = 0.0, gammab = 0.0, gammad = 0.0;
struct temp_status temp;
temp.temp = 0;
temp.brightness = 1.0;

n = res->ncrtc;
int icrtc_specified = icrtc >= 0 && icrtc < n;
for (c = icrtc_specified ? icrtc : 0; c < (icrtc_specified ? icrtc + 1 : n); c++)
if ((icrtc >= 0) && (icrtc < n))
n = 1;
else
icrtc = 0;
for (c = icrtc; c < (icrtc + n); c++)
{
RRCrtc crtcxid;
int size;
Expand All @@ -82,14 +58,17 @@ static int get_sct_for_screen(Display *dpy, int screen, int icrtc, int fdebug)
XRRFreeGamma(crtc_gamma);
}
XFree(res);
gammam = (gammar > gammag) ? gammar : gammag;
gammam = (gammab > gammam) ? gammab : gammam;
if (gammam > 0.0)
temp.brightness = (gammar > gammag) ? gammar : gammag;
temp.brightness = (gammab > temp.brightness) ? gammab : temp.brightness;
if (temp.brightness > 0.0 && n > 0)
{
gammar /= gammam;
gammag /= gammam;
gammab /= gammam;
if (fdebug > 0) fprintf(stderr, "DEBUG: Gamma: %f, %f, %f\n", gammar, gammag, gammab);
gammar /= temp.brightness;
gammag /= temp.brightness;
gammab /= temp.brightness;
temp.brightness /= n;
temp.brightness /= BRIGHTHESS_DIV;
temp.brightness = DoubleTrim(temp.brightness, 0.0, 1.0);
if (fdebug > 0) fprintf(stderr, "DEBUG: Gamma: %f, %f, %f, brightness: %f\n", gammar, gammag, gammab, temp.brightness);
gammad = gammab - gammar;
if (gammad < 0.0)
{
Expand All @@ -107,34 +86,37 @@ static int get_sct_for_screen(Display *dpy, int screen, int icrtc, int fdebug)
t = exp((gammag + 1.0 - gammad - (GAMMA_K0GB + GAMMA_K0RB)) / (GAMMA_K1GB + GAMMA_K1RB)) + (TEMPERATURE_NORM - TEMPERATURE_ZERO);
}
}
else
temp.brightness = DoubleTrim(temp.brightness, 0.0, 1.0);

temp = (int)(t + 0.5);
temp.temp = (int)(t + 0.5);

return temp;
}

static void sct_for_screen(Display *dpy, int screen, int icrtc, int temp, int fdebug)
static void sct_for_screen(Display *dpy, int screen, int icrtc, struct temp_status temp, int fdebug)
{
double t = 0.0, g = 0.0, gammar, gammag, gammab;
double t = 0.0, b = 1.0, g = 0.0, gammar, gammag, gammab;
int n, c;
Window root = RootWindow(dpy, screen);
XRRScreenResources *res = XRRGetScreenResourcesCurrent(dpy, root);

t = (double)temp;
if (temp < TEMPERATURE_NORM)
t = (double)temp.temp;
b = DoubleTrim(temp.brightness, 0.0, 1.0);
if (temp.temp < TEMPERATURE_NORM)
{
gammar = 1.0;
if (temp < TEMPERATURE_ZERO)
{
gammag = 0.0;
gammab = 0.0;
}
else
if (temp.temp > TEMPERATURE_ZERO)
{
g = log(t - TEMPERATURE_ZERO);
gammag = DoubleTrim(GAMMA_K0GR + GAMMA_K1GR * g, 0.0, 1.0);
gammab = DoubleTrim(GAMMA_K0BR + GAMMA_K1BR * g, 0.0, 1.0);
}
else
{
gammag = 0.0;
gammab = 0.0;
}
}
else
{
Expand All @@ -143,10 +125,13 @@ static void sct_for_screen(Display *dpy, int screen, int icrtc, int temp, int fd
gammag = DoubleTrim(GAMMA_K0GB + GAMMA_K1GB * g, 0.0, 1.0);
gammab = 1.0;
}
if (fdebug > 0) fprintf(stderr, "DEBUG: Gamma: %f, %f, %f\n", gammar, gammag, gammab);
if (fdebug > 0) fprintf(stderr, "DEBUG: Gamma: %f, %f, %f, brightness: %f\n", gammar, gammag, gammab, b);
n = res->ncrtc;
int icrtc_specified = icrtc >= 0 && icrtc < n;
for (c = icrtc_specified ? icrtc : 0; c < (icrtc_specified ? icrtc + 1 : n); c++)
if ((icrtc >= 0) && (icrtc < n))
n = 1;
else
icrtc = 0;
for (c = icrtc; c < (icrtc + n); c++)
{
int size, i;
RRCrtc crtcxid;
Expand All @@ -158,7 +143,7 @@ static void sct_for_screen(Display *dpy, int screen, int icrtc, int temp, int fd

for (i = 0; i < size; i++)
{
g = GAMMA_MULT * (double)i / (double)size;
g = GAMMA_MULT * b * (double)i / (double)size;
crtc_gamma->red[i] = (unsigned short int)(g * gammar + 0.5);
crtc_gamma->green[i] = (unsigned short int)(g * gammag + 0.5);
crtc_gamma->blue[i] = (unsigned short int)(g * gammab + 0.5);
Expand All @@ -173,8 +158,9 @@ static void sct_for_screen(Display *dpy, int screen, int icrtc, int temp, int fd

int main(int argc, char **argv)
{
int i, screen, screens, temp;
int i, screen, screens;
int screen_specified, screen_first, screen_last, crtc_specified;
struct temp_status temp;
int fdebug = 0, fdelta = 0, fhelp = 0;
Display *dpy = XOpenDisplay(NULL);

Expand All @@ -187,9 +173,10 @@ int main(int argc, char **argv)
screens = XScreenCount(dpy);
screen_first = 0;
screen_last = screens - 1;
temp = -1;
screen_specified = -1;
crtc_specified = -1;
temp.temp = DELTA_MIN;
temp.brightness = -1.0;
for (i = 1; i < argc; i++)
{
if ((strcmp(argv[i],"-h") == 0) || (strcmp(argv[i],"--help") == 0)) fhelp = 1;
Expand Down Expand Up @@ -217,7 +204,8 @@ int main(int argc, char **argv)
fhelp = 1;
}
}
else if (temp == -1) temp = atoi(argv[i]);
else if (temp.temp == DELTA_MIN) temp.temp = atoi(argv[i]);
else if (temp.brightness < 0.0) temp.brightness = atof(argv[i]);
else
{
fprintf(stderr, "ERROR! Unknown parameter: %s\n!", argv[i]);
Expand All @@ -235,33 +223,34 @@ int main(int argc, char **argv)
}
else
{
if (temp.brightness < 0.0) temp.brightness = 1.0;
if (screen_specified >= 0)
{
screen_first = screen_specified;
screen_last = screen_specified;
}
if ((temp < 0) && (fdelta == 0))
if ((temp.temp < 0) && (fdelta == 0))
{
// No arguments, so print estimated temperature for each screen
for (screen = screen_first; screen <= screen_last; screen++)
{
temp = get_sct_for_screen(dpy, screen, crtc_specified, fdebug);
printf("Screen %d: temperature ~ %d\n", screen, temp);
printf("Screen %d: temperature ~ %d %f\n", screen, temp.temp, temp.brightness);
}
}
else
{
if (fdelta == 0)
{
// Set temperature to given value or default for a value of 0
if (temp == 0)
if (temp.temp == 0)
{
temp = TEMPERATURE_NORM;
temp.temp = TEMPERATURE_NORM;
}
else if (temp < TEMPERATURE_ZERO)
else if (temp.temp < TEMPERATURE_ZERO)
{
fprintf(stderr, "WARNING! Temperatures below %d cannot be displayed.\n", TEMPERATURE_ZERO);
temp = TEMPERATURE_ZERO;
temp.temp = TEMPERATURE_ZERO;
}
for (screen = screen_first; screen <= screen_last; screen++)
{
Expand All @@ -273,11 +262,12 @@ int main(int argc, char **argv)
// Delta mode: Shift temperature of each screen by given value
for (screen = screen_first; screen <= screen_last; screen++)
{
int tempd = temp + get_sct_for_screen(dpy, screen, crtc_specified, fdebug);
if (tempd < TEMPERATURE_ZERO)
struct temp_status tempd = get_sct_for_screen(dpy, screen, crtc_specified, fdebug);
tempd.temp += temp.temp;
if (tempd.temp < TEMPERATURE_ZERO)
{
fprintf(stderr, "WARNING! Temperatures below %d cannot be displayed.\n", TEMPERATURE_ZERO);
tempd = TEMPERATURE_ZERO;
tempd.temp = TEMPERATURE_ZERO;
}
sct_for_screen(dpy, screen, crtc_specified, tempd, fdebug);
}
Expand Down
57 changes: 57 additions & 0 deletions src/xsct.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/*
* xsct - X11 set color temperature
*
* Public domain, do as you wish.
*/

#include <X11/Xatom.h>
#include <X11/Xlib.h>
#include <X11/Xproto.h>
#include <X11/extensions/Xrandr.h>

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>

#ifndef __XSCT_H
#define __XSCT_H

#define XSCT_VERSION "1.9"

#define TEMPERATURE_NORM 6500
#define TEMPERATURE_ZERO 700
#define GAMMA_MULT 65535.0
// Approximation of the `redshift` table from
// https://github.com/jonls/redshift/blob/04760afe31bff5b26cf18fe51606e7bdeac15504/src/colorramp.c#L30-L273
// without limits:
// GAMMA = K0 + K1 * ln(T - T0)
// Red range (T0 = TEMPERATURE_ZERO)
// Green color
#define GAMMA_K0GR -1.47751309139817
#define GAMMA_K1GR 0.28590164772055
// Blue color
#define GAMMA_K0BR -4.38321650114872
#define GAMMA_K1BR 0.6212158769447
// Blue range (T0 = TEMPERATURE_NORM - TEMPERATURE_ZERO)
// Red color
#define GAMMA_K0RB 1.75390204039018
#define GAMMA_K1RB -0.1150805671482
// Green color
#define GAMMA_K0GB 1.49221604915144
#define GAMMA_K1GB -0.07513509588921
#define BRIGHTHESS_DIV 65470.988
#define DELTA_MIN -1000000

struct temp_status
{
int temp;
double brightness;
};

static void usage(char * pname);
static double DoubleTrim(double x, double a, double b);
static struct temp_status get_sct_for_screen(Display *dpy, int screen, int icrtc, int fdebug);
static void sct_for_screen(Display *dpy, int screen, int icrtc, struct temp_status temp, int fdebug);

#endif /* __XSCT_H */
Loading

0 comments on commit f2aa7c8

Please sign in to comment.