pax_global_header00006660000000000000000000000064146370070740014522gustar00rootroot0000000000000052 comment=c69fb968e471b54ae47dded0b231b2cb90e4537b sct-2.3/000077500000000000000000000000001463700707400121575ustar00rootroot00000000000000sct-2.3/.github/000077500000000000000000000000001463700707400135175ustar00rootroot00000000000000sct-2.3/.github/workflows/000077500000000000000000000000001463700707400155545ustar00rootroot00000000000000sct-2.3/.github/workflows/build.yml000066400000000000000000000006361463700707400174030ustar00rootroot00000000000000name: Build Project on: push: branches: - master pull_request: branches: - '*' jobs: build: runs-on: ubuntu-latest steps: - name: Checkout Repository uses: actions/checkout@v4 - name: Install Build Dependencies run: sudo apt-get update && sudo apt-get install -y build-essential libx11-dev libxrandr-dev - name: Build Project run: make sct-2.3/.gitignore000066400000000000000000000000051463700707400141420ustar00rootroot00000000000000xsct sct-2.3/CHANGELOG000066400000000000000000000035731463700707400134010ustar00rootroot00000000000000PROJECT: https://github.com/faf0/sct 2.3: j-silv on 25 Jun 2024 * Address edge case where temp info is reset when brightness goes below 0.0 in delta mode * Add small fix to make sure both temp and brightness are provided when in delta mode 2.2: j-silv on 20 Jan 2024 * Option --delta now allows for shifting the brightness, in addition to the temperature 2.1: carlpitt on 31 Dec 2023 * Optimized code, eliminating compiler warnings 2.0: fac3plant on 27 Jul 2023 * Option --toggle added to toggle between night (4500 K) and day (6500 K) temperature 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 1.8: faf0 on 01 Jun 2022 * Options --screen and --crtc added to select a specific screen and CRTC to use 1.7: zikzik on 30 May 2022 * Options to specify the screen index and CRTC index to change the color temperature of a specific screen and CTRC combination only 1.6.1: sunweaver and faf0 on 24 March 2021 * Add `$(DESTDIR)` as prefix of installation paths 1.6: zvezdochiot and faf0 on 23 Aug 2020 * Option --delta to shift temperature by given value * Add WARNING 1.5: zvezdochiot on 08 Aug 2019 * Option --verbose to display debugging information 1.5: zvezdochiot on 07 Aug 2019 * Approximation of the `redshift` table from https://github.com/jonls/redshift/blob/04760afe31bff5b26cf18fe51606e7bdeac15504/src/colorramp.c#L30-L273 without limits 1.4: zvezdochiot on 04 Aug 2019 * Read current temperature 1.3: zvezdochiot on 02 Jan 2019 * Rename utilites for X11 * Add -h option to print usage information * Add license documentation * Add changelog * Add man 1.2: faf0 on 31 May 2016 * Fix crash when DISPLAY env variable is set to an invalid value 1.1: faf0 on 21 Nov 2015 * Fix memleak 1.0: faf0 on 21 Nov 2015 * Initial sct-2.3/LICENSE000066400000000000000000000022731463700707400131700ustar00rootroot00000000000000This is free and unencumbered software released into the public domain. Anyone is free to copy, modify, publish, use, compile, sell, or distribute this software, either in source code form or as a compiled binary, for any purpose, commercial or non-commercial, and by any means. In jurisdictions that recognize copyright laws, the author or authors of this software dedicate any and all copyright interest in the software to the public domain. We make this dedication for the benefit of the public at large and to the detriment of our heirs and successors. We intend this dedication to be an overt act of relinquishment in perpetuity of all present and future rights to this software under copyright law. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. For more information, please refer to sct-2.3/Makefile000066400000000000000000000011201463700707400136110ustar00rootroot00000000000000CC ?= gcc CFLAGS ?= -Wall -Wextra -Werror -pedantic -std=c99 -O2 -I /usr/X11R6/include LDFLAGS ?= -L /usr/X11R6/lib -s PREFIX ?= /usr BIN ?= $(PREFIX)/bin MAN ?= $(PREFIX)/share/man/man1 INSTALL ?= install PROG = xsct SRCS = src/xsct.c LIBS = -lX11 -lXrandr -lm $(PROG): $(SRCS) $(CC) $(CFLAGS) $^ -o $@ $(LDFLAGS) $(LIBS) install: $(PROG) $(PROG).1 $(INSTALL) -d $(DESTDIR)$(BIN) $(INSTALL) -m 0755 $(PROG) $(DESTDIR)$(BIN) $(INSTALL) -d $(DESTDIR)$(MAN) $(INSTALL) -m 0644 $(PROG).1 $(DESTDIR)$(MAN) uninstall: rm -f $(BIN)/$(PROG) rm -f $(MAN)/$(PROG).1 clean: rm -f $(PROG) sct-2.3/README.md000066400000000000000000000125761463700707400134510ustar00rootroot00000000000000![GitHub release (latest by date)](https://img.shields.io/github/v/release/faf0/sct) ![GitHub Release Date](https://img.shields.io/github/release-date/faf0/sct) ![GitHub repo size](https://img.shields.io/github/repo-size/faf0/sct) ![GitHub](https://img.shields.io/github/license/faf0/sct) # About Xsct (X11 set color temperature) is a UNIX tool which allows you to set the color temperature of your screen. It is simpler than [Redshift](https://github.com/jonls/redshift) and [f.lux](https://justgetflux.com/). Original code was published by Ted Unangst in the public domain: https://www.tedunangst.com/flak/post/sct-set-color-temperature Minor modifications were made in order to get sct to: - compile on Ubuntu 14.04 and later releases - iterate over all screens of the default display and change the color temperature - free the Display structure - fix memleaks - clean up code - return `EXIT_SUCCESS` # Installation ## Make-based On UNIX-based systems, a convenient method of building this software is using Make. Since the `Makefile` is simple and portable, both the BSD and [GNU make](https://www.gnu.org/software/make/) variants will have no problems parsing and executing it correctly. The simplest invocation is ~~~sh make ~~~ that will use the default values for all flags as provided in the `Makefile`. Overriding any of the following variables is supported by exporting a variable with the same name and your desired content to the environment: * `CC` * `CFLAGS` * `LDFLAGS` * `PREFIX` * `BIN` (the directory into which the resulting binary will be installed) * `MAN` (the directory into which the man page will be installed) * `INSTALL` (`install(1)` program used to create directories and copy files) Both example calls are equivalent and will build the software with the specified compiler and custom compiler flags: ~~~sh make CC='clang' CFLAGS='-O2 -pipe -g3 -ggdb3' LDFLAGS='-L/very/special/directory -flto' ~~~ ~~~sh export CC='clang' export CFLAGS='-O2 -pipe -g3 -ggdb3' export LDFLAGS='-L/very/special/directory -flto' make ~~~ The software can be installed by running the following command: ~~~sh make install ~~~ If you prefer a different installation location, override the `PREFIX` variable: ~~~sh make install PREFIX="${HOME}/xsct-prefix" ~~~ ~~~sh export PREFIX="${HOME}/xsct-prefix" make install ~~~ ## Manual compilation Compile the code using the following command: ~~~sh gcc -Wall -Wextra -Werror -pedantic -std=c99 -O2 -I /usr/X11R6/include xsct.c -o xsct -L /usr/X11R6/lib -lX11 -lXrandr -lm -s ~~~ # Usage Provided that xsct binary is located in your `$PATH`, execute it using the following command: ~~~sh xsct 3700 0.9 ~~~ The first parameter (`3700` above) represents the color temperature. The second parameter (`0.9` above) represents the brightness. The values are in the range `[0.0, 1.0]`. 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 - `-v`, `--verbose`: display debugging information - `-d `, `--delta `: temperature and brightness are shifted relatively and not absolutely - `-s `, `--screen ` `N`: use the screen specified by given zero-based index - `-t`, `--toggle`: toggle between night and day temperature - `-c `, `--crtc ` `N`: use the CRTC specified by given zero-based index Here are a few examples of how to control the brightness and the temperature using `xsct`: | Command | Explanation | |---------------------|-------------------------------------------------------------------------| | `xsct` | Estimate the current display temperature and brightness | | `xsct 0` | Set the temperature to `6500` and the brightness to `1.0` (default) | | `xsct 3700 0.9` | Set the temperature to `3700` and the brightness to `0.9` | | `xsct 3700` | Set the temperature to `3700` and the brightness to `1.0` (default) | | `xsct -d 1000` | Increase the temperature by `1000` | | `xsct -d -500` | Decrease the temperature by `500` | | `xsct -d 1000 0.2` | Increase the temperature by `1000` and increase the brightness by `0.2` | | `xsct -d -500 -0.3` | Decrease the temperature by `500` and decrease the brightness by `0.3` | | `xsct -d 0 0.2` | Increase the brightness by `0.2` | Test xsct using the following command: ~~~sh xsct 3700 0.9 && xsct ~~~ # Quirks If the delta mode is used to decrease the brightness to below 0.0 and then increased above 0.0, the temperature will reset to 6500 K, regardless of whatever it was before the brightness reached 0. This is because the temperature is reset to 0 K when the brightness is set equal to or below 0.0 (to verify this, you can run `xsct 0 0.0; xsct`). # Resources The following website by Mitchell Charity provides a table for the conversion between black-body temperatures and color pixel values: http://www.vendian.org/mncharity/dir3/blackbody/ --- https://github.com/faf0/sct/ sct-2.3/src/000077500000000000000000000000001463700707400127465ustar00rootroot00000000000000sct-2.3/src/xsct.c000066400000000000000000000260301463700707400140740ustar00rootroot00000000000000/* * xsct - X11 set color temperature * * Public domain, do as you wish. */ #include "xsct.h" static void usage(const char *const pname) { 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 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 consider temperature and brightness parameters as relative shifts\n" "\t-s, --screen N\t xsct will only select screen specified by given zero-based index\n" "\t-t, --toggle \t xsct will toggle between 'day' and 'night' mode\n" "\t-c, --crtc N\t xsct will only select CRTC specified by given zero-based index\n", XSCT_VERSION, pname); } static double DoubleTrim(double x, double a, double b) { const double buff[3] = {a, x, b}; return buff[ (int)(x > a) + (int)(x > b) ]; } 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 n, c; double t = 0.0; double gammar = 0.0, gammag = 0.0, gammab = 0.0; struct temp_status temp; temp.temp = 0; n = res->ncrtc; if ((icrtc >= 0) && (icrtc < n)) n = 1; else icrtc = 0; for (c = icrtc; c < (icrtc + n); c++) { RRCrtc crtcxid; int size; XRRCrtcGamma *crtc_gamma; crtcxid = res->crtcs[c]; crtc_gamma = XRRGetCrtcGamma(dpy, crtcxid); size = crtc_gamma->size; gammar += crtc_gamma->red[size - 1]; gammag += crtc_gamma->green[size - 1]; gammab += crtc_gamma->blue[size - 1]; XRRFreeGamma(crtc_gamma); } XFree(res); temp.brightness = (gammar > gammag) ? gammar : gammag; temp.brightness = (gammab > temp.brightness) ? gammab : temp.brightness; if (temp.brightness > 0.0 && n > 0) { 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); const double gammad = gammab - gammar; if (gammad < 0.0) { if (gammab > 0.0) { t = exp((gammag + 1.0 + gammad - (GAMMA_K0GR + GAMMA_K0BR)) / (GAMMA_K1GR + GAMMA_K1BR)) + TEMPERATURE_ZERO; } else { t = (gammag > 0.0) ? (exp((gammag - GAMMA_K0GR) / GAMMA_K1GR) + TEMPERATURE_ZERO) : TEMPERATURE_ZERO; } } else { 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.temp = (int)(t + 0.5); return temp; } static void sct_for_screen(Display *dpy, int screen, int icrtc, struct temp_status temp, int fdebug) { double t = 0.0, b = 1.0, gammar, gammag, gammab; int n, c; Window root = RootWindow(dpy, screen); XRRScreenResources *res = XRRGetScreenResourcesCurrent(dpy, root); t = (double)temp.temp; b = DoubleTrim(temp.brightness, 0.0, 1.0); if (temp.temp < TEMPERATURE_NORM) { gammar = 1.0; if (temp.temp > TEMPERATURE_ZERO) { const double 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 { const double g = log(t - (TEMPERATURE_NORM - TEMPERATURE_ZERO)); gammar = DoubleTrim(GAMMA_K0RB + GAMMA_K1RB * g, 0.0, 1.0); gammag = DoubleTrim(GAMMA_K0GB + GAMMA_K1GB * g, 0.0, 1.0); gammab = 1.0; } if (fdebug > 0) fprintf(stderr, "DEBUG: Gamma: %f, %f, %f, brightness: %f\n", gammar, gammag, gammab, b); n = res->ncrtc; if ((icrtc >= 0) && (icrtc < n)) n = 1; else icrtc = 0; for (c = icrtc; c < (icrtc + n); c++) { int size, i; RRCrtc crtcxid; XRRCrtcGamma *crtc_gamma; crtcxid = res->crtcs[c]; size = XRRGetCrtcGammaSize(dpy, crtcxid); crtc_gamma = XRRAllocGamma(size); for (i = 0; i < size; i++) { const double 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); } XRRSetCrtcGamma(dpy, crtcxid, crtc_gamma); XRRFreeGamma(crtc_gamma); } XFree(res); } static void bound_temp(struct temp_status *const temp) { if (temp->temp <= 0) { // identical behavior when xsct is called in absolute mode with temp == 0 fprintf(stderr, "WARNING! Temperatures below %d cannot be displayed.\n", 0); temp->temp = TEMPERATURE_NORM; } else if (temp->temp < TEMPERATURE_ZERO) { fprintf(stderr, "WARNING! Temperatures below %d cannot be displayed.\n", TEMPERATURE_ZERO); temp->temp = TEMPERATURE_ZERO; } if (temp->brightness < 0.0) { fprintf(stderr, "WARNING! Brightness values below 0.0 cannot be displayed.\n"); temp->brightness = 0.0; } else if (temp->brightness > 1.0) { fprintf(stderr, "WARNING! Brightness values above 1.0 cannot be displayed.\n"); temp->brightness = 1.0; } } int main(int argc, char **argv) { int i, screen, screens; int screen_specified, screen_first, screen_last, crtc_specified; struct temp_status temp; int fdebug = 0, fdelta = 0, fhelp = 0, toggle = 0; Display *dpy = XOpenDisplay(NULL); if (!dpy) { perror("XOpenDisplay(NULL) failed"); fprintf(stderr, "ERROR! Ensure DISPLAY is set correctly!\n"); return EXIT_FAILURE; } screens = XScreenCount(dpy); screen_first = 0; screen_last = screens - 1; screen_specified = -1; crtc_specified = -1; temp.temp = DELTA_MIN; temp.brightness = DELTA_MIN; for (i = 1; i < argc; i++) { if ((strcmp(argv[i],"-h") == 0) || (strcmp(argv[i],"--help") == 0)) fhelp = 1; else if ((strcmp(argv[i],"-v") == 0) || (strcmp(argv[i],"--verbose") == 0)) fdebug = 1; else if ((strcmp(argv[i],"-d") == 0) || (strcmp(argv[i],"--delta") == 0)) fdelta = 1; else if ((strcmp(argv[i],"-t") == 0) || (strcmp(argv[i],"--toggle") == 0)) toggle = 1; else if ((strcmp(argv[i],"-s") == 0) || (strcmp(argv[i],"--screen") == 0)) { i++; if (i < argc) { screen_specified = atoi(argv[i]); } else { fprintf(stderr, "ERROR! Required value for screen not specified!\n"); fhelp = 1; } } else if ((strcmp(argv[i],"-c") == 0) || (strcmp(argv[i],"--crtc") == 0)) { i++; if (i < argc) { crtc_specified = atoi(argv[i]); } else { fprintf(stderr, "ERROR! Required value for crtc not specified!\n"); fhelp = 1; } } else if (temp.temp == DELTA_MIN) temp.temp = atoi(argv[i]); else if (temp.brightness == DELTA_MIN) temp.brightness = atof(argv[i]); else { fprintf(stderr, "ERROR! Unknown parameter: %s\n!", argv[i]); fhelp = 1; } } if (fhelp > 0) { usage(argv[0]); } else if (screen_specified >= screens) { fprintf(stderr, "ERROR! Invalid screen index: %d!\n", screen_specified); } else { // Check if the temp is above 100 less than the norm and change to NIGHT if it is // The threashold was chosen to give some room for varients in temp if (toggle != 0) { for (screen = screen_first; screen <= screen_last; screen++) { temp = get_sct_for_screen(dpy, screen, crtc_specified, fdebug); if (temp.temp > (TEMPERATURE_NORM - 100)) { temp.temp = TEMPERATURE_NIGHT; } else { temp.temp = TEMPERATURE_NORM; } sct_for_screen(dpy, screen, crtc_specified, temp, fdebug); } } if ((temp.brightness == DELTA_MIN) && (fdelta == 0)) temp.brightness = 1.0; if (screen_specified >= 0) { screen_first = screen_specified; screen_last = screen_specified; } if ((temp.temp == DELTA_MIN) && (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 %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.temp == 0) { temp.temp = TEMPERATURE_NORM; } else { bound_temp(&temp); } for (screen = screen_first; screen <= screen_last; screen++) { sct_for_screen(dpy, screen, crtc_specified, temp, fdebug); } } else { // Delta mode: Shift temperature and optionally brightness of each screen by given value if (temp.temp == DELTA_MIN || temp.brightness == DELTA_MIN) { fprintf(stderr, "ERROR! Temperature and brightness delta must both be specified!\n"); } else { for (screen = screen_first; screen <= screen_last; screen++) { struct temp_status tempd = get_sct_for_screen(dpy, screen, crtc_specified, fdebug); tempd.temp += temp.temp; tempd.brightness += temp.brightness; bound_temp(&tempd); sct_for_screen(dpy, screen, crtc_specified, tempd, fdebug); } } } } } XCloseDisplay(dpy); return EXIT_SUCCESS; } sct-2.3/src/xsct.h000066400000000000000000000032361463700707400141040ustar00rootroot00000000000000/* * xsct - X11 set color temperature * * Public domain, do as you wish. */ #include #include #include #include #include #include #include #include #ifndef __XSCT_H #define __XSCT_H #define XSCT_VERSION "2.3" #define TEMPERATURE_NORM 6500 #define TEMPERATURE_NIGHT 4500 #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(const char *const 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); static void bound_temp(struct temp_status *const temp); #endif /* __XSCT_H */ sct-2.3/xsct.1000066400000000000000000000020331463700707400132200ustar00rootroot00000000000000.TH xsct 1 "Jun 2024" "2.3" "User Manual" .SH NAME xsct \- X11 set screen color temperature .SH SYNOPSIS .B xsct [options] .I [temperature] .I [brightness] .SH DESCRIPTION .B xsct sets the screen's color temperature. .SH OPTIONS .TP .B -h, --help Display usage information and exit .TP .B -v, --verbose Display debugging information .TP .B -d, --delta Shift temperature and brightness by temperature and brightness value .TP .B -s, --screen N Zero-based index of screen to use. .TP .B -t, --toggle Toggle between night and day temperature. .TP .B -c, --crtc N Zero-based index of CRTC to use. .TP .I [temperature] Black body temperature .br If the value is 0, xsct sets the color temperature to the default of 6500 .br If no arguments are passed, xsct estimates the current display temperature and brightness .TP .I [brightness] Linear brightness between 0.0 (inclusive) and 1.0 (inclusive). The default value is 1.0 if no parameter is passed. .SH AUTHOR xsct is based on sct by Ted Unangst .SH SEE ALSO redshift(1), xtemp(1)