/* gamma-vidmode.c -- X VidMode gamma adjustment source
* This file is part of redshift-ng.
*
* redshift-ng is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* redshift-ng is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with redshift-ng. If not, see .
*
* Copyright (c) 2010-2017 Jon Lund Steffensen
* Copyright (c) 2025 Mattias Andrée
*/
#include "common.h"
#include
#include
struct gamma_state {
Display *display;
int screen_num;
int ramp_size;
uint16_t *saved_ramps;
};
static int
vidmode_init(struct gamma_state **state)
{
struct gamma_state *s;
s = *state = emalloc(sizeof(struct gamma_state));
s->screen_num = -1;
s->saved_ramps = NULL;
/* Open display */
s->display = XOpenDisplay(NULL);
if (!s->display) {
weprintf(_("X request failed: %s"), "XOpenDisplay");
return -1;
}
return 0;
}
static int
vidmode_start(struct gamma_state *state, enum program_mode mode)
{
int r;
int screen_num = state->screen_num;
int major, minor;
uint16_t *gamma_r, *gamma_g, *gamma_b;
(void) mode;
if (screen_num < 0)
screen_num = DefaultScreen(state->display);
state->screen_num = screen_num;
/* Query extension version */
r = XF86VidModeQueryVersion(state->display, &major, &minor);
if (!r) {
weprintf(_("X request failed: %s"), "XF86VidModeQueryVersion");
return -1;
}
/* Request size of gamma ramps */
r = XF86VidModeGetGammaRampSize(state->display, state->screen_num, &state->ramp_size);
if (!r) {
weprintf(_("X request failed: %s"), "XF86VidModeGetGammaRampSize");
return -1;
}
if (!state->ramp_size) {
weprintf(_("Gamma ramp size too small: %zu"), (size_t)state->ramp_size);
return -1;
}
/* Allocate space for saved gamma ramps */
state->saved_ramps = emalloc(3 * state->ramp_size * sizeof(uint16_t));
gamma_r = &state->saved_ramps[0 * state->ramp_size];
gamma_g = &state->saved_ramps[1 * state->ramp_size];
gamma_b = &state->saved_ramps[2 * state->ramp_size];
/* Save current gamma ramps so we can restore them at program exit. */
r = XF86VidModeGetGammaRamp(state->display, state->screen_num,
state->ramp_size, gamma_r, gamma_g, gamma_b);
if (!r) {
weprintf(_("X request failed: %s"), "XF86VidModeGetGammaRamp");
return -1;
}
return 0;
}
static void
vidmode_free(struct gamma_state *state)
{
free(state->saved_ramps);
XCloseDisplay(state->display);
free(state);
}
static void
vidmode_print_help(FILE *f)
{
fputs(_("Adjust gamma ramps with the X VidMode extension.\n"), f);
fputs("\n", f);
/* TRANSLATORS: VidMode help output left column must not be translated */
fputs(_(" screen=N\t\tX screen to apply adjustments to\n"), f);
fputs("\n", f);
}
static int
vidmode_set_option(struct gamma_state *state, const char *key, const char *value)
{
if (!strcasecmp(key, "screen")) {
state->screen_num = atoi(value);
} else if (!strcasecmp(key, "preserve")) {
weprintf(_("Parameter `%s' is now always on; use the `%s' command-line option to disable."), key, "-P");
} else {
weprintf(_("Unknown method parameter: `%s'."), key);
return -1;
}
return 0;
}
static void
vidmode_restore(struct gamma_state *state)
{
uint16_t *gamma_r = &state->saved_ramps[0 * state->ramp_size];
uint16_t *gamma_g = &state->saved_ramps[1 * state->ramp_size];
uint16_t *gamma_b = &state->saved_ramps[2 * state->ramp_size];
int r;
/* Restore gamma ramps */
r = XF86VidModeSetGammaRamp(state->display, state->screen_num,
state->ramp_size, gamma_r, gamma_g, gamma_b);
if (!r)
weprintf(_("X request failed: %s"), "XF86VidModeSetGammaRamp");
}
static int
vidmode_set_temperature(
struct gamma_state *state, const struct color_setting *setting, int preserve)
{
uint16_t value, *gamma_ramps, *gamma_r, *gamma_g, *gamma_b;
int r, i;
/* Create new gamma ramps */
gamma_ramps = emalloc(3 * state->ramp_size * sizeof(uint16_t));
gamma_r = &gamma_ramps[0 * state->ramp_size];
gamma_g = &gamma_ramps[1 * state->ramp_size];
gamma_b = &gamma_ramps[2 * state->ramp_size];
if (preserve) {
/* Initialize gamma ramps from saved state */
memcpy(gamma_ramps, state->saved_ramps, 3 * state->ramp_size * sizeof(uint16_t));
} else {
/* Initialize gamma ramps to pure state */
for (i = 0; i < state->ramp_size; i++) {
value = (double)i / (state->ramp_size - 1) * UINT16_MAX;
gamma_r[i] = value;
gamma_g[i] = value;
gamma_b[i] = value;
}
}
colorramp_fill_u16(gamma_r, gamma_g, gamma_b, state->ramp_size,
state->ramp_size, state->ramp_size, setting);
/* Set new gamma ramps */
r = XF86VidModeSetGammaRamp(state->display, state->screen_num,
state->ramp_size, gamma_r, gamma_g, gamma_b);
if (!r) {
weprintf(_("X request failed: %s"), "XF86VidModeSetGammaRamp");
free(gamma_ramps);
return -1;
}
free(gamma_ramps);
return 0;
}
const struct gamma_method vidmode_gamma_method = {
"vidmode", 1,
&vidmode_init,
&vidmode_start,
&vidmode_free,
&vidmode_print_help,
&vidmode_set_option,
&vidmode_restore,
&vidmode_set_temperature
};