forked from X11-good-tools/xsct
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathxsct.c
235 lines (212 loc) · 6.97 KB
/
xsct.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
/*
* xsct - X11 set color temperature
*
* Original code published by Ted Unangst:
* http://www.tedunangst.com/flak/post/sct-set-color-temperature
*
* Modified by Fabian Foerg in order to:
* - compile on Ubuntu 14.04
* - iterate over all screens of the default display and change the color
* temperature
* - fix memleaks
* - clean up code
* - return EXIT_SUCCESS
*
* Public domain, do as you wish.
*
* Compile the code using the following command:
* gcc -Wall -Wextra -Werror -pedantic -std=c99 -O2 -I /usr/X11R6/include xsct.c -o xsct -L /usr/X11R6/lib -lX11 -lXrandr -lm -s
*
*/
#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>
static void usage(char * pname)
{
printf("Xsct (1.6)\n"
"Usage: %s [options] [temperature]\n"
"\tIf the argument is 0, xsct resets the display to the default temperature (6500K)\n"
"\tIf the argument lesser than 1000, xsct will increase (or decrease) current display temperature by given argument\n"
"\tIf no arguments are passed, xsct estimates the current display temperature\n"
"Options:\n"
"\t-v, --verbose \t xsct will display debugging information\n"
"\t-h, --help \t xsct will display this usage information\n", 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 fdebug)
{
Window root = RootWindow(dpy, screen);
XRRScreenResources *res = XRRGetScreenResourcesCurrent(dpy, root);
int temp = 0, n, c;
double t = 0.0;
double gammar = 0.0, gammag = 0.0, gammab = 0.0, gammam = 1.0, gammad = 0.0;
n = res->ncrtc;
for (c = 0; c < 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);
gammam = (gammar > gammag) ? gammar : gammag;
gammam = (gammab > gammam) ? gammab : gammam;
if (gammam > 0.0)
{
gammar /= gammam;
gammag /= gammam;
gammab /= gammam;
if (fdebug > 0) fprintf(stderr, "DEBUG: Gamma: %f, %f, %f\n", gammar, gammag, gammab);
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);
}
}
temp = (int)(t + 0.5);
return temp;
}
static void sct_for_screen(Display *dpy, int screen, int temp, int fdebug)
{
double t = 0.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)
{
gammar = 1.0;
if (temp < TEMPERATURE_ZERO)
{
gammag = 0.0;
gammab = 0.0;
}
else
{
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
{
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\n", gammar, gammag, gammab);
n = res->ncrtc;
for (c = 0; c < 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++)
{
g = GAMMA_MULT * (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);
}
int main(int argc, char **argv)
{
int i, screen, screens, temp;
int fdebug = 0, fhelp = 0;
Display *dpy = XOpenDisplay(NULL);
if (!dpy)
{
perror("XOpenDisplay(NULL) failed");
fprintf(stderr, "Make sure DISPLAY is set correctly.\n");
return EXIT_FAILURE;
}
screens = XScreenCount(dpy);
temp = -1;
for (i = 1; i < argc; i++)
{
if ((strcmp(argv[i],"-v") == 0) || (strcmp(argv[i],"--verbose") == 0)) fdebug = 1;
else if ((strcmp(argv[i],"-h") == 0) || (strcmp(argv[i],"--help") == 0)) fhelp = 1;
else temp = atoi(argv[i]);
}
if (fhelp > 0)
{
usage(argv[0]);
}
else
{
if (temp == -1)
{
for (screen = 0; screen < screens; screen++)
{
temp = get_sct_for_screen(dpy, screen, fdebug);
printf("Screen %d: temperature ~ %d\n", screen, temp);
}
}
else
{
temp = (temp == 0) ? TEMPERATURE_NORM : temp;
for (screen = 0; screen < screens; screen++)
{
temp = (temp > 1000) ? temp : (temp + (temp != 0)*get_sct_for_screen(dpy, screen, fdebug));
sct_for_screen(dpy, screen, temp, fdebug);
}
}
}
XCloseDisplay(dpy);
return EXIT_SUCCESS;
}