diff --git a/.gitignore b/.gitignore index 8742289f..dc6de478 100644 --- a/.gitignore +++ b/.gitignore @@ -45,6 +45,8 @@ src/redshift-gtk/redshift-gtk contrib/redshift.spec src/redshift src/redshift-gtk/__pycache__/ +/data/systemd/redshift.service +/data/systemd/redshift-gtk.service # gettext /po/POTFILES diff --git a/DESIGN b/DESIGN index 1b88aba1..7b708795 100644 --- a/DESIGN +++ b/DESIGN @@ -58,7 +58,7 @@ I've been mostly consistent with the naming throughout the source code (I hope). First adjustment methods: There is "randr" which is the preferred -because it has support for multiple outputs per X screen whic is lacking +because it has support for multiple outputs per X screen which is lacking in "vidmode". Both are APIs in the X server that allow for manipulation of gamma ramps, which is what Redshift uses to change the screen color temperature. There's also "wingdi" which is for the Windows version, diff --git a/redshift.1 b/redshift.1 index fbf7bdf6..2533b4e6 100644 --- a/redshift.1 +++ b/redshift.1 @@ -60,6 +60,9 @@ One shot mode (do not continuously adjust color temperature) \fB\-O\fR TEMP One shot manual mode (set color temperature) .TP +\fB\-P\fR +Preserve current calibrations +.TP \fB\-p\fR Print mode (only print parameters and exit) .TP @@ -106,6 +109,9 @@ Screen brightness at night \fBgamma\fR = R:G:B Gamma adjustment to apply .TP +\fBpreserve-calibrations\fR = 0 or 1 +Disable or enable preservation of currently applied calibrations. +.TP \fBadjustment\-method\fR = name Select adjustment method. Options for the adjustment method can be given under the configuration file heading of the same name. diff --git a/src/colorramp.c b/src/colorramp.c index f1988d4c..752884e6 100644 --- a/src/colorramp.c +++ b/src/colorramp.c @@ -18,6 +18,7 @@ Copyright (c) 2013 Ingo Thies */ +#include #include #include @@ -282,7 +283,8 @@ interpolate_color(float a, const float *c1, const float *c2, float *c) void colorramp_fill(uint16_t *gamma_r, uint16_t *gamma_g, uint16_t *gamma_b, - int size, int temp, float brightness, const float gamma[3]) + int size, int temp, float brightness, const float gamma[3], + uint16_t *calib_r, uint16_t *calib_g, uint16_t *calib_b) { /* Approximate white point */ float white_point[3]; @@ -298,4 +300,34 @@ colorramp_fill(uint16_t *gamma_r, uint16_t *gamma_g, uint16_t *gamma_b, gamma_g[i] = F((float)i/size, 1) * (UINT16_MAX+1); gamma_b[i] = F((float)i/size, 2) * (UINT16_MAX+1); } + + /* Apply gamma ramps used when Redshift started on top of + the effects of Redshift. It would be easier to put + Redshift's effects on top if this, but then calibrations + whould become incorrect. */ + if (calib_r == NULL) + return; + uint16_t *calib[3]; + calib[0] = calib_r; + calib[1] = calib_g; + calib[2] = calib_b; + uint16_t *filter[3]; + filter[0] = gamma_r; + filter[1] = gamma_g; + filter[2] = gamma_b; + int size_ = size - 1; + for (int c = 0; c < 3; c++) { + uint16_t *cfilter = filter[c]; + uint16_t *ccalib = calib[c]; + for (int i = 0; i < size; i++) { + /* We a rounding a bit. We could do linear + interpolation or even (precalculated) + polynomial interpolation, but it is + probably not worth it. */ + int y = cfilter[i]; + y = (float)(y * size_) / UINT16_MAX + 0.5; + y = y < 0 ? 0 : (y > size_ ? size_ : y); + cfilter[i] = ccalib[y]; + } + } } diff --git a/src/colorramp.h b/src/colorramp.h index dd0fa97d..5a27f59c 100644 --- a/src/colorramp.h +++ b/src/colorramp.h @@ -23,6 +23,7 @@ #include void colorramp_fill(uint16_t *gamma_r, uint16_t *gamma_g, uint16_t *gamma_b, - int size, int temp, float brightness, const float gamma[3]); + int size, int temp, float brightness, const float gamma[3], + uint16_t *calib_r, uint16_t *calib_g, uint16_t *calib_b); #endif /* ! REDSHIFT_COLORRAMP_H */ diff --git a/src/config-ini.c b/src/config-ini.c index 65751ddf..628a7140 100644 --- a/src/config-ini.c +++ b/src/config-ini.c @@ -297,3 +297,34 @@ config_ini_get_section(config_ini_state_t *state, const char *name) return NULL; } + +config_ini_section_t ** +config_ini_get_sections(config_ini_state_t *state, const char *name) +{ + config_ini_section_t **sections = malloc(1 * sizeof(config_ini_section_t*)); + if (sections == NULL) { + perror("malloc"); + return NULL; + } + + int ptr = 0; + config_ini_section_t *section = state->sections; + while (section != NULL) { + if (strcasecmp(section->name, name) == 0) { + sections[ptr++] = section; + + if ((ptr & -ptr) == ptr) { + sections = realloc(sections, (ptr << 1) * sizeof(config_ini_section_t*)); + if (sections == NULL) { + perror("realloc"); + return NULL; + } + } + } + + section = section->next; + } + + sections[ptr] = NULL; + return sections; +} diff --git a/src/config-ini.h b/src/config-ini.h index 5cdcc729..da1d3b3d 100644 --- a/src/config-ini.h +++ b/src/config-ini.h @@ -46,4 +46,7 @@ void config_ini_free(config_ini_state_t *state); config_ini_section_t *config_ini_get_section(config_ini_state_t *state, const char *name); +config_ini_section_t **config_ini_get_sections(config_ini_state_t *state, + const char *name); + #endif /* ! REDSHIFT_CONFIG_INI_H */ diff --git a/src/gamma-drm.c b/src/gamma-drm.c index e784b471..b04b89b2 100644 --- a/src/gamma-drm.c +++ b/src/gamma-drm.c @@ -40,17 +40,65 @@ #include "gamma-drm.h" #include "colorramp.h" +#include "redshift.h" int drm_init(drm_state_t *state) { + /* Check if we are in a display */ + char* env; + int in_a_dislay = 0; + if ((env = getenv("DISPLAY")) != NULL) { + if (strchr(env, ':')) + in_a_dislay = 1; + } else if ((env = getenv("WAYLAND_DISPLAY")) != NULL) { + if (strstr(env, "wayland-") == env) + in_a_dislay = 1; + } + if (in_a_dislay) { + fprintf(stderr, _("You appear to be inside a graphical environment\n" + "adjustments made using the DRM method will only\n" + "be applied when in a TTY.\n")); + } + + /* Count number of graphics cards. */ + long maxlen = strlen(DRM_DIR_NAME) + strlen(DRM_DEV_NAME) + 10; + char *pathname = alloca(maxlen * sizeof(char)); + int card_count; + for (card_count = 0;; card_count++) { + struct stat _attr; + sprintf(pathname, DRM_DEV_NAME, DRM_DIR_NAME, card_count); + if (stat(pathname, &_attr)) + break; + } + /* Initialize state. */ - state->card_num = 0; - state->crtc_num = -1; - state->fd = -1; - state->res = NULL; - state->crtcs = NULL; + state->selection_count = 0; + state->selections = malloc(sizeof(drm_selection_t)); + if (state->selections == NULL) { + perror("malloc"); + return -1; + } + state->selections->card_num = -1; + state->selections->crtc_num = -1; + state->selections->gamma[0] = DEFAULT_GAMMA; + state->selections->gamma[1] = DEFAULT_GAMMA; + state->selections->gamma[2] = DEFAULT_GAMMA; + state->card_count = card_count; + state->cards = card_count ? malloc(card_count * sizeof(drm_crtc_state_t)) : NULL; + if (state->cards == NULL) { + if (card_count) perror("malloc"); + else fprintf(stderr, _("Could not find any graphics card using DRM\n")); + return -1; + } + int card_index; + for (card_index = 0; card_index < card_count; card_index++) { + state->cards[card_index].fd = -1; + state->cards[card_index].res = NULL; + state->cards[card_index].crtcs_used = 0; + state->cards[card_index].crtcs = NULL; + } return 0; } @@ -58,153 +106,235 @@ drm_init(drm_state_t *state) int drm_start(drm_state_t *state) { - /* Acquire access to a graphics card. */ + /* Use default selection if no other selection is made. */ + if (state->selection_count == 0) { + /* Select card 0 if CRTC is selected but not a card. */ + if (state->selections->crtc_num != -1) + if (state->selections->card_num == -1) + state->selections->card_num = 0; + + /* Select CRTCs. */ + if (state->selections->card_num != -1) { + state->selections = realloc(state->selections, 2 * sizeof(drm_selection_t)); + state->selection_count = 1; + if (state->selections != NULL) + state->selections[1] = state->selections[0]; + } else { + state->selections = realloc(state->selections, + (state->card_count + 1) * sizeof(drm_selection_t)); + state->selection_count = state->card_count; + if (state->selections != NULL) { + int i; + for (i = 1; i <= state->card_count; i++) { + state->selections[i] = *(state->selections); + state->selections[i].card_num = i - 1; + } + } + } + if (state->selections == NULL) { + perror("realloc"); + return -1; + } + } + + /* Apply selections. */ long maxlen = strlen(DRM_DIR_NAME) + strlen(DRM_DEV_NAME) + 10; char *pathname = alloca(maxlen * sizeof(char)); + int skipped = 0; + int selection_index; + for (selection_index = 1; selection_index <= state->selection_count; selection_index++) { + drm_selection_t *selection = state->selections + selection_index - skipped; + drm_card_state_t *card = state->cards + selection->card_num; - sprintf(pathname, DRM_DEV_NAME, DRM_DIR_NAME, state->card_num); + if (card->fd == -1) { + /* Acquire access to a graphics card. */ + sprintf(pathname, DRM_DEV_NAME, DRM_DIR_NAME, selection->card_num); + card->fd = open(pathname, O_RDWR | O_CLOEXEC); + if (card->fd < 0) { + /* TODO check access permissions, normally root or + membership of the video group is required. */ + perror("open"); + return -1; + } - state->fd = open(pathname, O_RDWR | O_CLOEXEC); - if (state->fd < 0) { - /* TODO check if access permissions, normally root or - membership of the video group is required. */ - perror("open"); - return -1; - } + /* Acquire mode resources. */ + card->res = drmModeGetResources(card->fd); + if (card->res == NULL) { + fprintf(stderr, _("Failed to get DRM mode resources\n")); + return -1; + } + } - /* Acquire mode resources. */ - state->res = drmModeGetResources(state->fd); - if (state->res == NULL) { - fprintf(stderr, _("Failed to get DRM mode resources\n")); - close(state->fd); - state->fd = -1; - return -1; - } + int crtc_count = card->res->count_crtcs; - /* Create entries for selected CRTCs. */ - int crtc_count = state->res->count_crtcs; - if (state->crtc_num >= 0) { - if (state->crtc_num >= crtc_count) { + if (selection->crtc_num == -1) { + /* Select all CRTCs. */ + if (crtc_count < 1) + continue; + int n = state->selection_count + crtc_count + 1; + state->selections = realloc(state->selections, n * sizeof(drm_selection_t)); + selection = state->selections + selection_index; + if (state->selections == NULL) { + perror("realloc"); + return -1; + } + int crtc_i; + for (crtc_i = 0; crtc_i < crtc_count; crtc_i++) { + drm_selection_t *sel = state->selections + ++(state->selection_count); + *sel = *selection; + sel->crtc_num = crtc_i; + } + *selection = state->selections[state->selection_count--]; + } + + /* Verify CRT index. */ + if (selection->crtc_num >= crtc_count) { fprintf(stderr, _("CRTC %d does not exist. "), - state->crtc_num); + selection->crtc_num); if (crtc_count > 1) { fprintf(stderr, _("Valid CRTCs are [0-%d].\n"), - crtc_count-1); + crtc_count - 1); } else { fprintf(stderr, _("Only CRTC 0 exists.\n")); } - close(state->fd); - state->fd = -1; - drmModeFreeResources(state->res); - state->res = NULL; return -1; } - state->crtcs = malloc(2 * sizeof(drm_crtc_state_t)); - state->crtcs[1].crtc_num = -1; - - state->crtcs->crtc_num = state->crtc_num; - state->crtcs->crtc_id = -1; - state->crtcs->gamma_size = -1; - state->crtcs->r_gamma = NULL; - state->crtcs->g_gamma = NULL; - state->crtcs->b_gamma = NULL; - } else { - int crtc_num; - state->crtcs = malloc((crtc_count + 1) * sizeof(drm_crtc_state_t)); - state->crtcs[crtc_count].crtc_num = -1; - for (crtc_num = 0; crtc_num < crtc_count; crtc_num++) { - state->crtcs[crtc_num].crtc_num = crtc_num; - state->crtcs[crtc_num].crtc_id = -1; - state->crtcs[crtc_num].gamma_size = -1; - state->crtcs[crtc_num].r_gamma = NULL; - state->crtcs[crtc_num].g_gamma = NULL; - state->crtcs[crtc_num].b_gamma = NULL; + /* Prepare for adding another output. */ + if (card->crtcs_used++ == 0) + card->crtcs = malloc(1 * sizeof(drm_crtc_state_t)); + else + card->crtcs = realloc(card->crtcs, card->crtcs_used * sizeof(drm_crtc_state_t)); + if (card->crtcs == NULL) { + perror(card->crtcs_used == 1 ? "malloc" : "realloc"); + return -1; } - } - /* Load CRTC information and gamma ramps. */ - drm_crtc_state_t *crtcs = state->crtcs; - for (; crtcs->crtc_num >= 0; crtcs++) { - crtcs->crtc_id = state->res->crtcs[crtcs->crtc_num]; - drmModeCrtc* crtc_info = drmModeGetCrtc(state->fd, crtcs->crtc_id); + /* Create entry for other selected CRTC. */ + drm_crtc_state_t *crtc = card->crtcs + card->crtcs_used - 1; + crtc->crtc_num = selection->crtc_num; + crtc->crtc_id = -1; + crtc->gamma_size = -1; + crtc->saved_gamma_r = NULL; + crtc->saved_gamma_g = NULL; + crtc->saved_gamma_b = NULL; + crtc->gamma[0] = selection->gamma[0]; + crtc->gamma[1] = selection->gamma[1]; + crtc->gamma[2] = selection->gamma[2]; + crtc->gamma_r = NULL; + crtc->gamma_g = NULL; + crtc->gamma_b = NULL; + + /* Load CRTC information and gamma ramps. */ + crtc->crtc_id = card->res->crtcs[selection->crtc_num]; + drmModeCrtc* crtc_info = drmModeGetCrtc(card->fd, crtc->crtc_id); if (crtc_info == NULL) { - fprintf(stderr, _("CRTC %i lost, skipping\n"), crtcs->crtc_num); + fprintf(stderr, _("CRTC %i lost, skipping.\n"), crtc->crtc_num); + crtc->crtc_num = -1; continue; } - crtcs->gamma_size = crtc_info->gamma_size; + crtc->gamma_size = crtc_info->gamma_size; drmModeFreeCrtc(crtc_info); - if (crtcs->gamma_size <= 1) { - fprintf(stderr, _("Could not get gamma ramp size for CRTC %i\n" - "on graphics card %i, ignoring device.\n"), - crtcs->crtc_num, state->card_num); + if (crtc->gamma_size <= 1) { + fprintf(stderr, + _("DRM failed to use gamma ramps on CRTC %i\n" + "on graphics card %i, ignoring device.\n"), + crtc->crtc_num, selection->card_num); continue; } /* Valgrind complains about us reading uninitialize memory if we just use malloc. */ - crtcs->r_gamma = calloc(3 * crtcs->gamma_size, sizeof(uint16_t)); - crtcs->g_gamma = crtcs->r_gamma + crtcs->gamma_size; - crtcs->b_gamma = crtcs->g_gamma + crtcs->gamma_size; - if (crtcs->r_gamma != NULL) { - int r = drmModeCrtcGetGamma(state->fd, crtcs->crtc_id, crtcs->gamma_size, - crtcs->r_gamma, crtcs->g_gamma, crtcs->b_gamma); - if (r < 0) { - fprintf(stderr, _("DRM could not read gamma ramps on CRTC %i on\n" - "graphics card %i, ignoring device.\n"), - crtcs->crtc_num, state->card_num); - free(crtcs->r_gamma); - crtcs->r_gamma = NULL; - } - } else { + crtc->saved_gamma_r = calloc(3 * crtc->gamma_size, sizeof(uint16_t)); + crtc->saved_gamma_g = crtc->saved_gamma_r + crtc->gamma_size; + crtc->saved_gamma_b = crtc->saved_gamma_g + crtc->gamma_size; + if (crtc->saved_gamma_r == NULL) { + perror("malloc"); + return -1; + } + int r = drmModeCrtcGetGamma(card->fd, crtc->crtc_id, crtc->gamma_size, + crtc->saved_gamma_r, crtc->saved_gamma_g, + crtc->saved_gamma_b); + if (r < 0) { + free(crtc->saved_gamma_r); + crtc->saved_gamma_r = NULL; + fprintf(stderr, _("DRM could not read gamma ramps on CRTC %i\n" + "on graphics card %i, ignoring device.\n"), + crtc->crtc_num, selection->card_num); + } + + /* Allocate space for gamma ramps. */ + uint16_t *gamma_ramps = malloc(3 * crtc->gamma_size * sizeof(uint16_t)); + if (gamma_ramps == NULL) { perror("malloc"); - drmModeFreeResources(state->res); - state->res = NULL; - close(state->fd); - state->fd = -1; - while (crtcs-- != state->crtcs) - free(crtcs->r_gamma); - free(state->crtcs); - state->crtcs = NULL; return -1; } + + crtc->gamma_r = gamma_ramps; + crtc->gamma_g = crtc->gamma_r + crtc->gamma_size; + crtc->gamma_b = crtc->gamma_g + crtc->gamma_size; } + /* We do not longer need raw selection information, free it. */ + free(state->selections); + state->selections = NULL; + + state->selection_count -= skipped; return 0; } void drm_restore(drm_state_t *state) { - drm_crtc_state_t *crtcs = state->crtcs; - while (crtcs->crtc_num >= 0) { - if (crtcs->r_gamma != NULL) { - drmModeCrtcSetGamma(state->fd, crtcs->crtc_id, crtcs->gamma_size, - crtcs->r_gamma, crtcs->g_gamma, crtcs->b_gamma); + int card_index; + for (card_index = 0; card_index < state->card_count; card_index++) { + drm_card_state_t *card = state->cards + card_index; + drm_crtc_state_t *crtcs = card->crtcs; + int crtc_index; + for (crtc_index = 0; crtc_index < card->crtcs_used; crtc_index++) { + drm_crtc_state_t *crtc = card->crtcs + crtc_index; + if (crtc->gamma_size <= 1 || crtc->crtc_num < 0) + continue; + if (crtc->saved_gamma_r != NULL) + drmModeCrtcSetGamma(card->fd, crtcs->crtc_id, crtc->gamma_size, + crtc->saved_gamma_r, crtc->saved_gamma_g, + crtc->saved_gamma_b); } - crtcs++; } } void drm_free(drm_state_t *state) { - if (state->crtcs != NULL) { - drm_crtc_state_t *crtcs = state->crtcs; - while (crtcs->crtc_num >= 0) { - if (crtcs->r_gamma != NULL) - free(crtcs->r_gamma); - crtcs->crtc_num = -1; - crtcs++; + int card_index; + for (card_index = 0; card_index < state->card_count; card_index++) { + drm_card_state_t *card = state->cards + card_index; + if (card->crtcs != NULL) { + drm_crtc_state_t *crtcs = card->crtcs; + int crtc_index; + for (crtc_index = 0; crtc_index < card->crtcs_used; crtc_index++) { + if (crtcs[crtc_index].saved_gamma_r != NULL) + free(crtcs[crtc_index].saved_gamma_r); + if (crtcs[crtc_index].gamma_r != NULL) + free(crtcs[crtc_index].gamma_r); + } + free(card->crtcs); + card->crtcs = NULL; + } + if (card->res != NULL) { + drmModeFreeResources(card->res); + card->res = NULL; + } + if (card->fd >= 0) { + close(card->fd); + card->fd = -1; } - free(state->crtcs); - state->crtcs = NULL; } - if (state->res != NULL) { - drmModeFreeResources(state->res); - state->res = NULL; + if (state->cards != NULL) { + free(state->cards); + state->cards = NULL; } - if (state->fd >= 0) { - close(state->fd); - state->fd = -1; + if (state->selections != NULL) { + free(state->selections); + state->selections = NULL; } } @@ -222,16 +352,58 @@ drm_print_help(FILE *f) } int -drm_set_option(drm_state_t *state, const char *key, const char *value) +drm_set_option(drm_state_t *state, const char *key, const char *value, int section) { + if (section == state->selection_count) { + state->selections = realloc(state->selections, + (++(state->selection_count) + 1) * sizeof(drm_selection_t)); + + if (state->selections == NULL) { + perror("realloc"); + return -1; + } + + state->selections[section + 1] = *(state->selections); + state->selections[section + 1].card_num = 0; + state->selections[section + 1].crtc_num = 0; + } + if (strcasecmp(key, "card") == 0) { - state->card_num = atoi(value); + state->selections[section + 1].card_num = atoi(value); + if (state->selections[section + 1].card_num < 0) { + fprintf(stderr, _("Card must be a non-negative integer.\n")); + fprintf(stderr, "Invalid card.\n"); + return -1; + } else if (state->selections[section + 1].card_num >= state->card_count) { + fprintf(stderr, _("Valid Cards are [0-%d].\n"), + state->card_count - 1); + fprintf(stderr, "Invalid card.\n"); + return -1; + } } else if (strcasecmp(key, "crtc") == 0) { - state->crtc_num = atoi(value); - if (state->crtc_num < 0) { - fprintf(stderr, _("CRTC must be a non-negative integer\n")); + state->selections[section + 1].crtc_num = atoi(value); + if (state->selections[section + 1].crtc_num < 0) { + fprintf(stderr, _("CRTC must be a non-negative integer.\n")); + return -1; + } + } else if (strcasecmp(key, "gamma") == 0) { + float gamma[3]; + if (parse_gamma_string(value, gamma) < 0) { + fputs(_("Malformed gamma setting.\n"), + stderr); + return -1; + } + if (gamma[0] < MIN_GAMMA || gamma[0] > MAX_GAMMA || + gamma[1] < MIN_GAMMA || gamma[1] > MAX_GAMMA || + gamma[2] < MIN_GAMMA || gamma[2] > MAX_GAMMA) { + fprintf(stderr, + _("Gamma value must be between %.1f and %.1f.\n"), + MIN_GAMMA, MAX_GAMMA); return -1; } + state->selections[section + 1].gamma[0] = gamma[0]; + state->selections[section + 1].gamma[1] = gamma[1]; + state->selections[section + 1].gamma[2] = gamma[2]; } else { fprintf(stderr, _("Unknown method parameter: `%s'.\n"), key); return -1; @@ -241,40 +413,36 @@ drm_set_option(drm_state_t *state, const char *key, const char *value) } int -drm_set_temperature(drm_state_t *state, int temp, float brightness, const float gamma[3]) +drm_set_temperature(drm_state_t *state, int temp, float brightness, int calibrations) { - drm_crtc_state_t *crtcs = state->crtcs; - int last_gamma_size = 0; - uint16_t *r_gamma = NULL; - uint16_t *g_gamma = NULL; - uint16_t *b_gamma = NULL; - - for (; crtcs->crtc_num >= 0; crtcs++) { - if (crtcs->gamma_size <= 1) - continue; - if (crtcs->gamma_size != last_gamma_size) { - if (last_gamma_size == 0) { - r_gamma = malloc(3 * crtcs->gamma_size * sizeof(uint16_t)); - g_gamma = r_gamma + crtcs->gamma_size; - b_gamma = g_gamma + crtcs->gamma_size; - } else if (crtcs->gamma_size > last_gamma_size) { - r_gamma = realloc(r_gamma, 3 * crtcs->gamma_size * sizeof(uint16_t)); - g_gamma = r_gamma + crtcs->gamma_size; - b_gamma = g_gamma + crtcs->gamma_size; - } - if (r_gamma == NULL) { - perror(last_gamma_size == 0 ? "malloc" : "realloc"); - return -1; - } - last_gamma_size = crtcs->gamma_size; + int card_num; + + for (card_num = 0; card_num < state->card_count; card_num++) { + drm_card_state_t *card = state->cards + card_num; + int crtc_; + for (crtc_ = 0; crtc_ < card->crtcs_used; crtc_++) { + drm_crtc_state_t *crtc = card->crtcs + crtc_; + if (crtc->gamma_size <= 1 || crtc->crtc_num < 0) + continue; + + /* Fill gamma ramp and apply. */ + uint16_t *saved_gamma_r = calibrations ? crtc->saved_gamma_r : NULL; + uint16_t *saved_gamma_g = calibrations ? crtc->saved_gamma_g : NULL; + uint16_t *saved_gamma_b = calibrations ? crtc->saved_gamma_b : NULL; + colorramp_fill(crtc->gamma_r, crtc->gamma_g, crtc->gamma_b, + crtc->gamma_size, temp, brightness, crtc->gamma, + saved_gamma_r, saved_gamma_g, saved_gamma_b); + drmModeCrtcSetGamma(card->fd, crtc->crtc_id, crtc->gamma_size, + crtc->gamma_r, crtc->gamma_g, crtc->gamma_b); + + /* If Direct Rendering Manager is used to change the gamma ramps + while an graphical session is active in the foreground (and X + display is running on the active VT) Direct Rendering Manager + will ignore the request and report that you do not have + sufficient permissions. This rejection is ignored so nothing + funny happens if the users opens a VT with a graphical session. */ } - colorramp_fill(r_gamma, g_gamma, b_gamma, crtcs->gamma_size, - temp, brightness, gamma); - drmModeCrtcSetGamma(state->fd, crtcs->crtc_id, crtcs->gamma_size, - r_gamma, g_gamma, b_gamma); } - free(r_gamma); - return 0; } diff --git a/src/gamma-drm.h b/src/gamma-drm.h index f4c0df53..c94d677b 100644 --- a/src/gamma-drm.h +++ b/src/gamma-drm.h @@ -30,17 +30,33 @@ typedef struct { int crtc_num; int crtc_id; int gamma_size; - uint16_t* r_gamma; - uint16_t* g_gamma; - uint16_t* b_gamma; + uint16_t *saved_gamma_r; + uint16_t *saved_gamma_g; + uint16_t *saved_gamma_b; + float gamma[3]; + uint16_t *gamma_r; + uint16_t *gamma_g; + uint16_t *gamma_b; } drm_crtc_state_t; +typedef struct { + int fd; + drmModeRes *res; + int crtcs_used; + drm_crtc_state_t *crtcs; +} drm_card_state_t; + typedef struct { int card_num; int crtc_num; - int fd; - drmModeRes* res; - drm_crtc_state_t* crtcs; + float gamma[3]; +} drm_selection_t; + +typedef struct { + int selection_count; + drm_selection_t *selections; + int card_count; + drm_card_state_t *cards; } drm_state_t; @@ -49,10 +65,10 @@ int drm_start(drm_state_t *state); void drm_free(drm_state_t *state); void drm_print_help(FILE *f); -int drm_set_option(drm_state_t *state, const char *key, const char *value); +int drm_set_option(drm_state_t *state, const char *key, const char *value, int section); void drm_restore(drm_state_t *state); -int drm_set_temperature(drm_state_t *state, int temp, float brightness, const float gamma[3]); +int drm_set_temperature(drm_state_t *state, int temp, float brightness, int calibrations); #endif /* ! REDSHIFT_GAMMA_DRM_H */ diff --git a/src/gamma-dummy.c b/src/gamma-dummy.c index 0ebb12d1..cd3d99f1 100644 --- a/src/gamma-dummy.c +++ b/src/gamma-dummy.c @@ -19,6 +19,7 @@ #include #include +#include #ifdef ENABLE_NLS # include @@ -27,16 +28,20 @@ # define _(s) s #endif +#include "redshift.h" + int gamma_dummy_init(void *state) { + (void) state; return 0; } int gamma_dummy_start(void *state) { + (void) state; fputs(_("WARNING: Using dummy gamma method! Display will not be affected by this gamma method.\n"), stderr); return 0; } @@ -44,11 +49,13 @@ gamma_dummy_start(void *state) void gamma_dummy_restore(void *state) { + (void) state; } void gamma_dummy_free(void *state) { + (void) state; } void @@ -59,16 +66,40 @@ gamma_dummy_print_help(FILE *f) } int -gamma_dummy_set_option(void *state, const char *key, const char *value) +gamma_dummy_set_option(void *state, const char *key, const char *value, int section) { - fprintf(stderr, _("Unknown method parameter: `%s'.\n"), key); - return -1; + (void) state; + (void) section; + + if (strcasecmp(key, "gamma") == 0) { + float gamma[3]; + if (parse_gamma_string(value, gamma) < 0) { + fputs(_("Malformed gamma setting.\n"), + stderr); + return -1; + } + if (gamma[0] < MIN_GAMMA || gamma[0] > MAX_GAMMA || + gamma[1] < MIN_GAMMA || gamma[1] > MAX_GAMMA || + gamma[2] < MIN_GAMMA || gamma[2] > MAX_GAMMA) { + fprintf(stderr, + _("Gamma value must be between %.1f and %.1f.\n"), + MIN_GAMMA, MAX_GAMMA); + return -1; + } + } else { + fprintf(stderr, _("Unknown method parameter: `%s'.\n"), key); + return -1; + } + + return 0; } int -gamma_dummy_set_temperature(void *state, int temp, float brightness, - const float gamma[3]) +gamma_dummy_set_temperature(void *state, int temp, float brightness, int calibrations) { + (void) state; + (void) brightness; + (void) calibrations; printf(_("Temperature: %i\n"), temp); return 0; } diff --git a/src/gamma-dummy.h b/src/gamma-dummy.h index 64bc40d8..9ced3f04 100644 --- a/src/gamma-dummy.h +++ b/src/gamma-dummy.h @@ -28,11 +28,11 @@ int gamma_dummy_start(void *state); void gamma_dummy_free(void *state); void gamma_dummy_print_help(FILE *f); -int gamma_dummy_set_option(void *state, const char *key, const char *value); +int gamma_dummy_set_option(void *state, const char *key, const char *value, int section); void gamma_dummy_restore(void *state); int gamma_dummy_set_temperature(void *state, int temp, float brightness, - const float gamma[3]); + int calibrations); #endif /* ! REDSHIFT_GAMMA_DUMMY_H */ diff --git a/src/gamma-randr.c b/src/gamma-randr.c index 9e528b5d..01f332c6 100644 --- a/src/gamma-randr.c +++ b/src/gamma-randr.c @@ -15,6 +15,7 @@ along with Redshift. If not, see . Copyright (c) 2010 Jon Lund Steffensen + Copyright (c) 2014 Mattias Andrée */ #include @@ -34,6 +35,7 @@ #include "gamma-randr.h" #include "colorramp.h" +#include "redshift.h" #define RANDR_VERSION_MAJOR 1 @@ -43,11 +45,15 @@ int randr_init(randr_state_t *state) { - /* Initialize state. */ - state->screen_num = -1; - state->crtc_num = -1; - - state->crtc_count = 0; + /* Initialize state */ + state->conn = NULL; + state->selection_count = 0; + state->selections = malloc(1 * sizeof(randr_selection_t)); + if (state->selections == NULL) { + perror("malloc"); + return -1; + } + state->crtcs_used = 0; state->crtcs = NULL; xcb_generic_error_t *error; @@ -83,6 +89,13 @@ randr_init(randr_state_t *state) free(ver_reply); + /* Default selection */ + state->selections->screen_num = -1; + state->selections->crtc_num = -1; + state->selections->gamma[0] = DEFAULT_GAMMA; + state->selections->gamma[1] = DEFAULT_GAMMA; + state->selections->gamma[2] = DEFAULT_GAMMA; + return 0; } @@ -91,71 +104,167 @@ randr_start(randr_state_t *state) { xcb_generic_error_t *error; - int screen_num = state->screen_num; - if (screen_num < 0) screen_num = state->preferred_screen; - - /* Get screen */ + /* Get screens */ const xcb_setup_t *setup = xcb_get_setup(state->conn); + int screen_count = xcb_setup_roots_length(setup); xcb_screen_iterator_t iter = xcb_setup_roots_iterator(setup); - state->screen = NULL; + xcb_screen_t **screens = alloca(screen_count * sizeof(xcb_screen_t*)); - for (int i = 0; iter.rem > 0; i++) { - if (i == screen_num) { - state->screen = iter.data; - break; - } + for (int i = 0; i < screen_count; i++) { + screens[i] = iter.data; xcb_screen_next(&iter); } - if (state->screen == NULL) { - fprintf(stderr, _("Screen %i could not be found.\n"), - screen_num); - return -1; + /* Use default selection if no other selection is made */ + if (state->selection_count == 0) { + /* Select preferred screen if CRTC is selected but not a screen */ + if (state->selections->crtc_num != -1) + if (state->selections->screen_num == -1) + state->selections->screen_num = state->preferred_screen; + + /* Select CRTCs */ + if (state->selections->screen_num != -1) { + state->selections = realloc(state->selections, 2 * sizeof(randr_selection_t)); + state->selection_count = 1; + if (state->selections != NULL) + state->selections[1] = state->selections[0]; + } else { + state->selections = realloc(state->selections, + (screen_count + 1) * sizeof(randr_selection_t)); + state->selection_count = screen_count; + if (state->selections != NULL) { + int i; + for (i = 1; i <= screen_count; i++) { + state->selections[i] = *(state->selections); + state->selections[i].screen_num = i - 1; + } + } + } + if (state->selections == NULL) { + perror("realloc"); + return -1; + } } - /* Get list of CRTCs for the screen */ - xcb_randr_get_screen_resources_current_cookie_t res_cookie = - xcb_randr_get_screen_resources_current(state->conn, - state->screen->root); - xcb_randr_get_screen_resources_current_reply_t *res_reply = - xcb_randr_get_screen_resources_current_reply(state->conn, - res_cookie, - &error); + /* Prepare storage screen information */ + xcb_randr_get_screen_resources_current_reply_t **screen_reses = + alloca(screen_count * sizeof(xcb_randr_get_screen_resources_current_reply_t*)); + for (int i = 0; i < screen_count; i++) + screen_reses[i] = NULL; + + /* Load implied screens and CRTCs and screen information and verify indices */ + int selection_index; + for (selection_index = 1; selection_index <= state->selection_count; selection_index++) { + randr_selection_t *selection = state->selections + selection_index; + int screen_num = selection->screen_num; + + /* Verify screen index */ + if (screen_num >= screen_count) { + fprintf(stderr, _("Screen %d does not exist. "), + screen_num); + if (screen_count > 1) { + fprintf(stderr, _("Valid screens are [0-%d].\n"), + screen_count - 1); + } else { + fprintf(stderr, _("Only screen 0 exists.\n")); + } + fprintf(stderr, "Invalid screen.\n"); + goto fail; + } - if (error) { - fprintf(stderr, _("`%s' returned error %d\n"), - "RANDR Get Screen Resources Current", - error->error_code); - return -1; + /* Load screen information if not already loaded */ + if (screen_reses[screen_num] == NULL) { + /* Get list of CRTCs for the screens */ + xcb_randr_get_screen_resources_current_cookie_t res_cookie = + xcb_randr_get_screen_resources_current(state->conn, + screens[screen_num]->root); + xcb_randr_get_screen_resources_current_reply_t *res_reply = + xcb_randr_get_screen_resources_current_reply(state->conn, + res_cookie, + &error); + screen_reses[screen_num] = res_reply; + + if (error) { + fprintf(stderr, _("`%s' returned error %d\n"), + "RANDR Get Screen Resources Current", + error->error_code); + goto fail; + } + } + + xcb_randr_get_screen_resources_current_reply_t* res = screen_reses[screen_num]; + int crtc_count = res->num_crtcs; + + if (selection->crtc_num == -1) { + /* Select all CRTCs. */ + if (crtc_count < 1) + continue; + int n = state->selection_count + crtc_count + 1; + state->selections = realloc(state->selections, n * sizeof(randr_selection_t)); + selection = state->selections + selection_index; + if (state->selections == NULL) { + perror("realloc"); + goto fail; + } + int crtc_i; + for (crtc_i = 0; crtc_i < crtc_count; crtc_i++) { + randr_selection_t *sel = state->selections + ++(state->selection_count); + *sel = state->selections[selection_index]; + sel->crtc_num = crtc_i; + } + *selection = state->selections[state->selection_count--]; + } + + /* Verify CRT index. */ + if (selection->crtc_num >= crtc_count) { + fprintf(stderr, _("CRTC %d does not exist. "), + selection->crtc_num); + if (crtc_count > 1) { + fprintf(stderr, _("Valid CRTCs are [0-%d].\n"), + crtc_count - 1); + } else { + fprintf(stderr, _("Only CRTC 0 exists.\n")); + } + fprintf(stderr, "Invalid CRTC.\n"); + goto fail; + } } - state->crtc_count = res_reply->num_crtcs; - state->crtcs = calloc(state->crtc_count, sizeof(randr_crtc_state_t)); + /* Allocate memory for CRTCs */ + state->crtcs = calloc(state->selection_count, sizeof(randr_crtc_state_t)); if (state->crtcs == NULL) { perror("malloc"); - state->crtc_count = 0; - return -1; + goto fail; } - xcb_randr_crtc_t *crtcs = - xcb_randr_get_screen_resources_current_crtcs(res_reply); + /* Load CRTC information and store gamma */ + int skipped = 0; + for (selection_index = 1; selection_index <= state->selection_count; selection_index++) { + randr_selection_t *selection = state->selections + selection_index - skipped; + if (selection->crtc_num < 0) { + skipped++; + continue; + } + xcb_randr_get_screen_resources_current_reply_t *res = screen_reses[selection->screen_num]; + xcb_randr_crtc_t *crtcs = + xcb_randr_get_screen_resources_current_crtcs(res); + randr_crtc_state_t *crtc = &state->crtcs[state->crtcs_used++]; - /* Save CRTC identifier in state */ - for (int i = 0; i < state->crtc_count; i++) { - state->crtcs[i].crtc = crtcs[i]; - } + /* Store gamma correction from selection data */ + crtc->gamma[0] = selection->gamma[0]; + crtc->gamma[1] = selection->gamma[1]; + crtc->gamma[2] = selection->gamma[2]; - free(res_reply); + /* Save CRTC identifier in state */ + crtc->crtc = crtcs[selection->crtc_num]; - /* Save size and gamma ramps of all CRTCs. - Current gamma ramps are saved so we can restore them - at program exit. */ - for (int i = 0; i < state->crtc_count; i++) { - xcb_randr_crtc_t crtc = state->crtcs[i].crtc; + /* Save size and gamma ramps of all CRTCs. + Current gamma ramps are saved so we can restore them + at program exit. */ /* Request size of gamma ramps */ xcb_randr_get_crtc_gamma_size_cookie_t gamma_size_cookie = - xcb_randr_get_crtc_gamma_size(state->conn, crtc); + xcb_randr_get_crtc_gamma_size(state->conn, crtc->crtc); xcb_randr_get_crtc_gamma_size_reply_t *gamma_size_reply = xcb_randr_get_crtc_gamma_size_reply(state->conn, gamma_size_cookie, @@ -165,23 +274,23 @@ randr_start(randr_state_t *state) fprintf(stderr, _("`%s' returned error %d\n"), "RANDR Get CRTC Gamma Size", error->error_code); - return -1; + goto fail; } unsigned int ramp_size = gamma_size_reply->size; - state->crtcs[i].ramp_size = ramp_size; + crtc->ramp_size = ramp_size; free(gamma_size_reply); - if (ramp_size == 0) { + if (ramp_size <= 1) { fprintf(stderr, _("Gamma ramp size too small: %i\n"), ramp_size); - return -1; + goto fail; } /* Request current gamma ramps */ xcb_randr_get_crtc_gamma_cookie_t gamma_get_cookie = - xcb_randr_get_crtc_gamma(state->conn, crtc); + xcb_randr_get_crtc_gamma(state->conn, crtc->crtc); xcb_randr_get_crtc_gamma_reply_t *gamma_get_reply = xcb_randr_get_crtc_gamma_reply(state->conn, gamma_get_cookie, @@ -190,7 +299,7 @@ randr_start(randr_state_t *state) if (error) { fprintf(stderr, _("`%s' returned error %d\n"), "RANDR Get CRTC Gamma", error->error_code); - return -1; + goto fail; } uint16_t *gamma_r = @@ -201,26 +310,54 @@ randr_start(randr_state_t *state) xcb_randr_get_crtc_gamma_blue(gamma_get_reply); /* Allocate space for saved gamma ramps */ - state->crtcs[i].saved_ramps = - malloc(3*ramp_size*sizeof(uint16_t)); - if (state->crtcs[i].saved_ramps == NULL) { + crtc->saved_ramps = malloc(3*ramp_size*sizeof(uint16_t)); + if (crtc->saved_ramps == NULL) { perror("malloc"); free(gamma_get_reply); - return -1; + goto fail; } /* Copy gamma ramps into CRTC state */ - memcpy(&state->crtcs[i].saved_ramps[0*ramp_size], gamma_r, + memcpy(&crtc->saved_ramps[0*ramp_size], gamma_r, ramp_size*sizeof(uint16_t)); - memcpy(&state->crtcs[i].saved_ramps[1*ramp_size], gamma_g, + memcpy(&crtc->saved_ramps[1*ramp_size], gamma_g, ramp_size*sizeof(uint16_t)); - memcpy(&state->crtcs[i].saved_ramps[2*ramp_size], gamma_b, + memcpy(&crtc->saved_ramps[2*ramp_size], gamma_b, ramp_size*sizeof(uint16_t)); free(gamma_get_reply); + + /* Allocate space for gamma ramps */ + uint16_t *gamma_ramps = malloc(3*ramp_size*sizeof(uint16_t)); + if (gamma_ramps == NULL) { + perror("malloc"); + goto fail; + } + + crtc->gamma_r = &gamma_ramps[0*ramp_size]; + crtc->gamma_g = &gamma_ramps[1*ramp_size]; + crtc->gamma_b = &gamma_ramps[2*ramp_size]; } + /* Release screen resources */ + for (int i = 0; i < screen_count; i++) + if (screen_reses[i] != NULL) + free(screen_reses[i]); + + /* We do not longer need raw selection information, free it */ + free(state->selections); + state->selections = NULL; + + state->selection_count -= skipped; return 0; + +fail: + /* Release screen resources */ + for (int i = 0; i < screen_count; i++) + if (screen_reses[i] != NULL) + free(screen_reses[i]); + + return -1; } void @@ -229,17 +366,17 @@ randr_restore(randr_state_t *state) xcb_generic_error_t *error; /* Restore CRTC gamma ramps */ - for (int i = 0; i < state->crtc_count; i++) { - xcb_randr_crtc_t crtc = state->crtcs[i].crtc; + for (int i = 0; i < state->crtcs_used; i++) { + randr_crtc_state_t* crtc = &state->crtcs[i]; - unsigned int ramp_size = state->crtcs[i].ramp_size; - uint16_t *gamma_r = &state->crtcs[i].saved_ramps[0*ramp_size]; - uint16_t *gamma_g = &state->crtcs[i].saved_ramps[1*ramp_size]; - uint16_t *gamma_b = &state->crtcs[i].saved_ramps[2*ramp_size]; + unsigned int ramp_size = crtc->ramp_size; + uint16_t *gamma_r = &crtc->saved_ramps[0*ramp_size]; + uint16_t *gamma_g = &crtc->saved_ramps[1*ramp_size]; + uint16_t *gamma_b = &crtc->saved_ramps[2*ramp_size]; /* Set gamma ramps */ xcb_void_cookie_t gamma_set_cookie = - xcb_randr_set_crtc_gamma_checked(state->conn, crtc, + xcb_randr_set_crtc_gamma_checked(state->conn, crtc->crtc, ramp_size, gamma_r, gamma_g, gamma_b); error = xcb_request_check(state->conn, gamma_set_cookie); @@ -255,14 +392,31 @@ randr_restore(randr_state_t *state) void randr_free(randr_state_t *state) { - /* Free CRTC state */ - for (int i = 0; i < state->crtc_count; i++) { - free(state->crtcs[i].saved_ramps); + /* Free saved gamma ramps */ + for (int i = 0; i < state->crtcs_used; i++) { + if (state->crtcs[i].saved_ramps != NULL) + free(state->crtcs[i].saved_ramps); + if (state->crtcs[i].gamma_r != NULL) + free(state->crtcs[i].gamma_r); + } + + /* Free CRTC states */ + if (state->crtcs != NULL) { + free(state->crtcs); + state->crtcs = NULL; } - free(state->crtcs); /* Close connection */ - xcb_disconnect(state->conn); + if (state->conn != NULL) { + xcb_disconnect(state->conn); + state->conn = NULL; + } + + /* Free raw selection information */ + if (state->selections != NULL) { + free(state->selections); + state->selections = NULL; + } } void @@ -279,12 +433,54 @@ randr_print_help(FILE *f) } int -randr_set_option(randr_state_t *state, const char *key, const char *value) +randr_set_option(randr_state_t *state, const char *key, const char *value, int section) { + if (section == state->selection_count) { + state->selections = realloc(state->selections, + (++(state->selection_count) + 1) * sizeof(randr_selection_t)); + + if (state->selections == NULL) { + perror("realloc"); + return -1; + } + + state->selections[section + 1] = *(state->selections); + state->selections[section + 1].screen_num = state->preferred_screen; + state->selections[section + 1].crtc_num = 0; + } + if (strcasecmp(key, "screen") == 0) { - state->screen_num = atoi(value); + int screen_num = atoi(value); + state->selections[section + 1].screen_num = screen_num; + if (screen_num < 0) { + fprintf(stderr, _("Screen must be a non-negative integer.\n")); + return -1; + } } else if (strcasecmp(key, "crtc") == 0) { - state->crtc_num = atoi(value); + int crtc_num = atoi(value); + state->selections[section + 1].crtc_num = crtc_num; + if (crtc_num < 0) { + fprintf(stderr, _("CRTC must be a non-negative integer.\n")); + return -1; + } + } else if (strcasecmp(key, "gamma") == 0) { + float gamma[3]; + if (parse_gamma_string(value, gamma) < 0) { + fputs(_("Malformed gamma setting.\n"), + stderr); + return -1; + } + if (gamma[0] < MIN_GAMMA || gamma[0] > MAX_GAMMA || + gamma[1] < MIN_GAMMA || gamma[1] > MAX_GAMMA || + gamma[2] < MIN_GAMMA || gamma[2] > MAX_GAMMA) { + fprintf(stderr, + _("Gamma value must be between %.1f and %.1f.\n"), + MIN_GAMMA, MAX_GAMMA); + return -1; + } + state->selections[section + 1].gamma[0] = gamma[0]; + state->selections[section + 1].gamma[1] = gamma[1]; + state->selections[section + 1].gamma[2] = gamma[2]; } else { fprintf(stderr, _("Unknown method parameter: `%s'.\n"), key); return -1; @@ -295,77 +491,51 @@ randr_set_option(randr_state_t *state, const char *key, const char *value) static int randr_set_temperature_for_crtc(randr_state_t *state, int crtc_num, int temp, - float brightness, const float gamma[3]) + float brightness, int calibrations) { + randr_crtc_state_t *crtc = &state->crtcs[crtc_num]; xcb_generic_error_t *error; - - if (crtc_num >= state->crtc_count || crtc_num < 0) { - fprintf(stderr, _("CRTC %d does not exist. "), - state->crtc_num); - if (state->crtc_count > 1) { - fprintf(stderr, _("Valid CRTCs are [0-%d].\n"), - state->crtc_count-1); - } else { - fprintf(stderr, _("Only CRTC 0 exists.\n")); - } - return -1; - } - - xcb_randr_crtc_t crtc = state->crtcs[crtc_num].crtc; - unsigned int ramp_size = state->crtcs[crtc_num].ramp_size; + unsigned int ramp_size = crtc->ramp_size; /* Create new gamma ramps */ - uint16_t *gamma_ramps = malloc(3*ramp_size*sizeof(uint16_t)); - if (gamma_ramps == NULL) { - perror("malloc"); - return -1; + uint16_t *saved_gamma_r = NULL; + uint16_t *saved_gamma_g = NULL; + uint16_t *saved_gamma_b = NULL; + if (calibrations) { + saved_gamma_r = &crtc->saved_ramps[0*crtc->ramp_size]; + saved_gamma_g = &crtc->saved_ramps[1*crtc->ramp_size]; + saved_gamma_b = &crtc->saved_ramps[2*crtc->ramp_size]; } - - uint16_t *gamma_r = &gamma_ramps[0*ramp_size]; - uint16_t *gamma_g = &gamma_ramps[1*ramp_size]; - uint16_t *gamma_b = &gamma_ramps[2*ramp_size]; - - colorramp_fill(gamma_r, gamma_g, gamma_b, ramp_size, - temp, brightness, gamma); + colorramp_fill(crtc->gamma_r, crtc->gamma_g, crtc->gamma_b, + ramp_size, temp, brightness, crtc->gamma, + saved_gamma_r, saved_gamma_g, saved_gamma_b); /* Set new gamma ramps */ xcb_void_cookie_t gamma_set_cookie = - xcb_randr_set_crtc_gamma_checked(state->conn, crtc, - ramp_size, gamma_r, - gamma_g, gamma_b); + xcb_randr_set_crtc_gamma_checked(state->conn, crtc->crtc, + ramp_size, crtc->gamma_r, + crtc->gamma_g, crtc->gamma_b); error = xcb_request_check(state->conn, gamma_set_cookie); if (error) { fprintf(stderr, _("`%s' returned error %d\n"), "RANDR Set CRTC Gamma", error->error_code); - free(gamma_ramps); return -1; } - free(gamma_ramps); - return 0; } int -randr_set_temperature(randr_state_t *state, int temp, float brightness, - const float gamma[3]) +randr_set_temperature(randr_state_t *state, int temp, float brightness, int calibrations) { int r; - /* If no CRTC number has been specified, - set temperature on all CRTCs. */ - if (state->crtc_num < 0) { - for (int i = 0; i < state->crtc_count; i++) { - r = randr_set_temperature_for_crtc(state, i, - temp, brightness, - gamma); - if (r < 0) return -1; - } - } else { - return randr_set_temperature_for_crtc(state, state->crtc_num, - temp, brightness, gamma); + for (int i = 0; i < state->crtcs_used; i++) { + r = randr_set_temperature_for_crtc(state, i, temp, + brightness, calibrations); + if (r < 0) return -1; } return 0; diff --git a/src/gamma-randr.h b/src/gamma-randr.h index cef0021e..966c1426 100644 --- a/src/gamma-randr.h +++ b/src/gamma-randr.h @@ -33,15 +33,24 @@ typedef struct { xcb_randr_crtc_t crtc; unsigned int ramp_size; uint16_t *saved_ramps; + float gamma[3]; + uint16_t *gamma_r; + uint16_t *gamma_g; + uint16_t *gamma_b; } randr_crtc_state_t; typedef struct { - xcb_connection_t *conn; - xcb_screen_t *screen; - int preferred_screen; int screen_num; int crtc_num; - unsigned int crtc_count; + float gamma[3]; +} randr_selection_t; + +typedef struct { + xcb_connection_t *conn; + int selection_count; + randr_selection_t *selections; + int preferred_screen; + int crtcs_used; randr_crtc_state_t *crtcs; } randr_state_t; @@ -51,11 +60,12 @@ int randr_start(randr_state_t *state); void randr_free(randr_state_t *state); void randr_print_help(FILE *f); -int randr_set_option(randr_state_t *state, const char *key, const char *value); +int randr_set_option(randr_state_t *state, const char *key, + const char *value, int section); void randr_restore(randr_state_t *state); int randr_set_temperature(randr_state_t *state, int temp, float brightness, - const float gamma[3]); + int calibrations); #endif /* ! REDSHIFT_GAMMA_RANDR_H */ diff --git a/src/gamma-vidmode.c b/src/gamma-vidmode.c index 656ce007..9a0b2155 100644 --- a/src/gamma-vidmode.c +++ b/src/gamma-vidmode.c @@ -15,6 +15,7 @@ along with Redshift. If not, see . Copyright (c) 2010 Jon Lund Steffensen + Copyright (c) 2014 Mattias Andrée */ #include @@ -34,19 +35,41 @@ #include "gamma-vidmode.h" #include "colorramp.h" +#include "redshift.h" int vidmode_init(vidmode_state_t *state) { - state->screen_num = -1; - state->saved_ramps = NULL; + state->selection_count = 0; + state->selections = malloc(1 * sizeof(vidmode_selection_t)); + if (state->selections == NULL) { + perror("malloc"); + return -1; + } + state->selections->screen_num = -1; + state->selections->gamma[0] = DEFAULT_GAMMA; + state->selections->gamma[1] = DEFAULT_GAMMA; + state->selections->gamma[2] = DEFAULT_GAMMA; + state->screens_used = 0; + state->screens = NULL; /* Open display */ state->display = XOpenDisplay(NULL); if (state->display == NULL) { fprintf(stderr, _("X request failed: %s\n"), "XOpenDisplay"); + free(state->selections); + state->selections = NULL; + return -1; + } + + state->screen_count = ScreenCount(state->display); + if (state->screen_count < 1) { + fprintf(stderr, _("X request failed: %s\n"), + "ScreenCount"); + free(state->selections); + state->selections = NULL; return -1; } @@ -57,10 +80,6 @@ int vidmode_start(vidmode_state_t *state) { int r; - int screen_num = state->screen_num; - - if (screen_num < 0) screen_num = DefaultScreen(state->display); - state->screen_num = screen_num; /* Query extension version */ int major, minor; @@ -71,40 +90,105 @@ vidmode_start(vidmode_state_t *state) return -1; } - /* Request size of gamma ramps */ - r = XF86VidModeGetGammaRampSize(state->display, state->screen_num, - &state->ramp_size); - if (!r) { - fprintf(stderr, _("X request failed: %s\n"), - "XF86VidModeGetGammaRampSize"); - return -1; - } - - if (state->ramp_size == 0) { - fprintf(stderr, _("Gamma ramp size too small: %i\n"), - state->ramp_size); - return -1; - } - - /* Allocate space for saved gamma ramps */ - state->saved_ramps = malloc(3*state->ramp_size*sizeof(uint16_t)); - if (state->saved_ramps == NULL) { - perror("malloc"); - return -1; + /* Use default selection if no other selection is made. */ + if (state->selection_count == 0) { + /* Select screens. */ + if (state->selections->screen_num != -1) { + state->selections = realloc(state->selections, 2 * sizeof(vidmode_selection_t)); + state->selection_count = 1; + if (state->selections != NULL) + state->selections[1] = state->selections[0]; + } else { + state->selections = realloc(state->selections, + (state->screen_count + 1) * sizeof(vidmode_selection_t)); + state->selection_count = state->screen_count; + if (state->selections != NULL) { + int i; + for (i = 1; i <= state->screen_count; i++) { + state->selections[i] = *(state->selections); + state->selections[i].screen_num = i - 1; + } + } + } + if (state->selections == NULL) { + perror("realloc"); + return -1; + } } - 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]; - - /* 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) { - fprintf(stderr, _("X request failed: %s\n"), - "XF86VidModeGetGammaRamp"); - return -1; + int selection_index; + for (selection_index = 1; selection_index <= state->selection_count; selection_index++) { + vidmode_selection_t *selection = state->selections + selection_index; + + /* Prepare for adding another screen */ + if (state->screens_used++ == 0) + state->screens = malloc(1 * sizeof(vidmode_screen_state_t)); + else + state->screens = realloc(state->screens, + state->screens_used * sizeof(vidmode_screen_state_t)); + if (state->screens == NULL) { + perror(state->screens_used == 1 ? "malloc" : "realloc"); + return -1; + } + + /* Create entry for other selected CRTC */ + vidmode_screen_state_t *screen = state->screens + state->screens_used - 1; + screen->screen_num = selection->screen_num; + screen->ramp_size = -1; + screen->saved_ramps = NULL; + screen->gamma[0] = selection->gamma[0]; + screen->gamma[1] = selection->gamma[1]; + screen->gamma[2] = selection->gamma[2]; + screen->gamma_r = NULL; + screen->gamma_g = NULL; + screen->gamma_b = NULL; + + /* Request size of gamma ramps */ + r = XF86VidModeGetGammaRampSize(state->display, screen->screen_num, + &screen->ramp_size); + if (!r) { + fprintf(stderr, _("X request failed: %s\n"), + "XF86VidModeGetGammaRampSize"); + return -1; + } + + if (screen->ramp_size <= 1) { + fprintf(stderr, _("Gamma ramp size too small: %i\n"), + screen->ramp_size); + return -1; + } + + /* Allocate space for saved gamma ramps */ + screen->saved_ramps = malloc(3*screen->ramp_size*sizeof(uint16_t)); + if (screen->saved_ramps == NULL) { + perror("malloc"); + return -1; + } + + uint16_t *gamma_r = &screen->saved_ramps[0*screen->ramp_size]; + uint16_t *gamma_g = &screen->saved_ramps[1*screen->ramp_size]; + uint16_t *gamma_b = &screen->saved_ramps[2*screen->ramp_size]; + + /* Save current gamma ramps so we can restore them at program exit. */ + r = XF86VidModeGetGammaRamp(state->display, screen->screen_num, + screen->ramp_size, gamma_r, gamma_g, + gamma_b); + if (!r) { + fprintf(stderr, _("X request failed: %s\n"), + "XF86VidModeGetGammaRamp"); + return -1; + } + + /* Allocate space for gamma ramps */ + uint16_t *gamma_ramps = malloc(3*screen->ramp_size*sizeof(uint16_t)); + if (gamma_ramps == NULL) { + perror("malloc"); + return -1; + } + + screen->gamma_r = &gamma_ramps[0*screen->ramp_size]; + screen->gamma_g = &gamma_ramps[1*screen->ramp_size]; + screen->gamma_b = &gamma_ramps[2*screen->ramp_size]; } return 0; @@ -113,11 +197,31 @@ vidmode_start(vidmode_state_t *state) void vidmode_free(vidmode_state_t *state) { - /* Free saved ramps */ - free(state->saved_ramps); + /* Free ramps */ + for (int i = 0; i < state->screens_used; i++) { + if (state->screens[i].saved_ramps != NULL) + free(state->screens[i].saved_ramps); + if (state->screens[i].gamma_r != NULL) + free(state->screens[i].gamma_r); + } + + /* Free screen selections */ + if (state->screens != NULL) { + free(state->screens); + state->screens = NULL; + } + + /* Free raw selection information */ + if (state->selections != NULL) { + free(state->selections); + state->selections = NULL; + } /* Close display connection */ - XCloseDisplay(state->display); + if (state->display != NULL) { + XCloseDisplay(state->display); + state->display = NULL; + } } void @@ -133,10 +237,58 @@ vidmode_print_help(FILE *f) } int -vidmode_set_option(vidmode_state_t *state, const char *key, const char *value) +vidmode_set_option(vidmode_state_t *state, const char *key, const char *value, int section) { + if (section == state->selection_count) { + state->selections = realloc(state->selections, + (++(state->selection_count) + 1) * sizeof(vidmode_selection_t)); + + if (state->selections == NULL) { + perror("realloc"); + return -1; + } + + state->selections[section + 1] = *(state->selections); + state->selections[section + 1].screen_num = 0; + } + if (strcasecmp(key, "screen") == 0) { - state->screen_num = atoi(value); + int screen_num = atoi(value); + state->selections[section + 1].screen_num = screen_num; + if (screen_num < 0) { + fprintf(stderr, _("Screen must be a non-negative integer.\n")); + return -1; + } + if (screen_num >= state->screen_count) { + fprintf(stderr, _("Screen %d does not exist. "), + screen_num); + if (state->screen_count > 1) { + fprintf(stderr, _("Valid screens are [0-%d].\n"), + state->screen_count - 1); + } else { + fprintf(stderr, _("Only screen 0 exists.\n")); + } + fprintf(stderr, "Invalid screen.\n"); + return -1; + } + } else if (strcasecmp(key, "gamma") == 0) { + float gamma[3]; + if (parse_gamma_string(value, gamma) < 0) { + fputs(_("Malformed gamma setting.\n"), + stderr); + return -1; + } + if (gamma[0] < MIN_GAMMA || gamma[0] > MAX_GAMMA || + gamma[1] < MIN_GAMMA || gamma[1] > MAX_GAMMA || + gamma[2] < MIN_GAMMA || gamma[2] > MAX_GAMMA) { + fprintf(stderr, + _("Gamma value must be between %.1f and %.1f.\n"), + MIN_GAMMA, MAX_GAMMA); + return -1; + } + state->selections[section + 1].gamma[0] = gamma[0]; + state->selections[section + 1].gamma[1] = gamma[1]; + state->selections[section + 1].gamma[2] = gamma[2]; } else { fprintf(stderr, _("Unknown method parameter: `%s'.\n"), key); return -1; @@ -148,52 +300,56 @@ vidmode_set_option(vidmode_state_t *state, const char *key, const char *value) void vidmode_restore(vidmode_state_t *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]; - - /* Restore gamma ramps */ - int r = XF86VidModeSetGammaRamp(state->display, state->screen_num, - state->ramp_size, gamma_r, gamma_g, - gamma_b); - if (!r) { - fprintf(stderr, _("X request failed: %s\n"), - "XF86VidModeSetGammaRamp"); - } + int screen_index; + for (screen_index = 0; screen_index < state->screens_used; screen_index++) { + vidmode_screen_state_t *screen = state->screens + screen_index; + + uint16_t *gamma_r = &screen->saved_ramps[0*screen->ramp_size]; + uint16_t *gamma_g = &screen->saved_ramps[1*screen->ramp_size]; + uint16_t *gamma_b = &screen->saved_ramps[2*screen->ramp_size]; + + /* Restore gamma ramps */ + int r = XF86VidModeSetGammaRamp(state->display, screen->screen_num, + screen->ramp_size, gamma_r, gamma_g, + gamma_b); + if (!r) { + fprintf(stderr, _("X request failed: %s\n"), + "XF86VidModeSetGammaRamp"); + } + } } int -vidmode_set_temperature(vidmode_state_t *state, int temp, float brightness, - const float gamma[3]) +vidmode_set_temperature(vidmode_state_t *state, int temp, float brightness, int calibrations) { - int r; - - /* Create new gamma ramps */ - uint16_t *gamma_ramps = malloc(3*state->ramp_size*sizeof(uint16_t)); - if (gamma_ramps == NULL) { - perror("malloc"); - return -1; + int screen_index; + for (screen_index = 0; screen_index < state->screens_used; screen_index++) { + vidmode_screen_state_t *screen = state->screens + screen_index; + int r; + + /* Create new gamma ramps */ + uint16_t *saved_gamma_r = NULL; + uint16_t *saved_gamma_g = NULL; + uint16_t *saved_gamma_b = NULL; + if (calibrations) { + saved_gamma_r = &screen->saved_ramps[0*screen->ramp_size]; + saved_gamma_g = &screen->saved_ramps[1*screen->ramp_size]; + saved_gamma_b = &screen->saved_ramps[2*screen->ramp_size]; + } + colorramp_fill(screen->gamma_r, screen->gamma_g, screen->gamma_b, + screen->ramp_size, temp, brightness, screen->gamma, + saved_gamma_r, saved_gamma_g, saved_gamma_b); + + /* Set new gamma ramps */ + r = XF86VidModeSetGammaRamp(state->display, screen->screen_num, + screen->ramp_size, screen->gamma_r, + screen->gamma_g, screen->gamma_b); + if (!r) { + fprintf(stderr, _("X request failed: %s\n"), + "XF86VidModeSetGammaRamp"); + return -1; + } } - uint16_t *gamma_r = &gamma_ramps[0*state->ramp_size]; - uint16_t *gamma_g = &gamma_ramps[1*state->ramp_size]; - uint16_t *gamma_b = &gamma_ramps[2*state->ramp_size]; - - colorramp_fill(gamma_r, gamma_g, gamma_b, state->ramp_size, - temp, brightness, gamma); - - /* Set new gamma ramps */ - r = XF86VidModeSetGammaRamp(state->display, state->screen_num, - state->ramp_size, gamma_r, gamma_g, - gamma_b); - if (!r) { - fprintf(stderr, _("X request failed: %s\n"), - "XF86VidModeSetGammaRamp"); - free(gamma_ramps); - return -1; - } - - free(gamma_ramps); - return 0; } diff --git a/src/gamma-vidmode.h b/src/gamma-vidmode.h index 8afd8fd2..47b6726c 100644 --- a/src/gamma-vidmode.h +++ b/src/gamma-vidmode.h @@ -26,10 +26,27 @@ #include typedef struct { - Display *display; int screen_num; int ramp_size; uint16_t *saved_ramps; + float gamma[3]; + uint16_t *gamma_r; + uint16_t *gamma_g; + uint16_t *gamma_b; +} vidmode_screen_state_t; + +typedef struct { + int screen_num; + float gamma[3]; +} vidmode_selection_t; + +typedef struct { + Display *display; + int selection_count; + vidmode_selection_t *selections; + int screen_count; + int screens_used; + vidmode_screen_state_t *screens; } vidmode_state_t; @@ -39,11 +56,11 @@ void vidmode_free(vidmode_state_t *state); void vidmode_print_help(FILE *f); int vidmode_set_option(vidmode_state_t *state, const char *key, - const char *value); + const char *value, int section); void vidmode_restore(vidmode_state_t *state); int vidmode_set_temperature(vidmode_state_t *state, int temp, float brightness, - const float gamma[3]); + int calibrations); #endif /* ! REDSHIFT_GAMMA_VIDMODE_H */ diff --git a/src/gamma-w32gdi.c b/src/gamma-w32gdi.c index 7193bc91..653e66d8 100644 --- a/src/gamma-w32gdi.c +++ b/src/gamma-w32gdi.c @@ -19,6 +19,7 @@ #include #include +#include #ifndef WINVER # define WINVER 0x0500 @@ -35,6 +36,7 @@ #include "gamma-w32gdi.h" #include "colorramp.h" +#include "redshift.h" #define GAMMA_RAMP_SIZE 256 @@ -43,6 +45,12 @@ int w32gdi_init(w32gdi_state_t *state) { state->saved_ramps = NULL; + state->gamma[0] = DEFAULT_GAMMA; + state->gamma[1] = DEFAULT_GAMMA; + state->gamma[2] = DEFAULT_GAMMA; + state->gamma_r = NULL; + state->gamma_g = NULL; + state->gamma_b = NULL; return 0; } @@ -86,6 +94,18 @@ w32gdi_start(w32gdi_state_t *state) /* Release device context */ ReleaseDC(NULL, hDC); + /* Allocate space for gamma ramps */ + WORD* gamma_ramps = malloc(3*GAMMA_RAMP_SIZE*sizeof(WORD)); + if (gamma_ramps == NULL) { + perror("malloc"); + ReleaseDC(NULL, hDC); + return -1; + } + + state->gamma_r = gamma_ramps; + state->gamma_g = state->gamma_r + GAMMA_RAMP_SIZE; + state->gamma_b = state->gamma_g + GAMMA_RAMP_SIZE; + return 0; } @@ -94,6 +114,7 @@ w32gdi_free(w32gdi_state_t *state) { /* Free saved ramps */ free(state->saved_ramps); + free(state->gamma_r); } @@ -105,9 +126,32 @@ w32gdi_print_help(FILE *f) } int -w32gdi_set_option(w32gdi_state_t *state, const char *key, const char *value) +w32gdi_set_option(w32gdi_state_t *state, const char *key, const char *value, int section) { - return -1; + if (strcasecmp(key, "gamma") == 0) { + float gamma[3]; + if (parse_gamma_string(value, gamma) < 0) { + fputs(_("Malformed gamma setting.\n"), + stderr); + return -1; + } + if (gamma[0] < MIN_GAMMA || gamma[0] > MAX_GAMMA || + gamma[1] < MIN_GAMMA || gamma[1] > MAX_GAMMA || + gamma[2] < MIN_GAMMA || gamma[2] > MAX_GAMMA) { + fprintf(stderr, + _("Gamma value must be between %.1f and %.1f.\n"), + MIN_GAMMA, MAX_GAMMA); + return -1; + } + state->gamma[0] = gamma[0]; + state->gamma[1] = gamma[1]; + state->gamma[2] = gamma[2]; + } else { + fprintf(stderr, _("Unknown method parameter: `%s'.\n"), key); + return -1; + } + + return 0; } void @@ -129,8 +173,7 @@ w32gdi_restore(w32gdi_state_t *state) } int -w32gdi_set_temperature(w32gdi_state_t *state, int temp, float brightness, - const float gamma[3]) +w32gdi_set_temperature(w32gdi_state_t *state, int temp, float brightness, int calibrations) { BOOL r; @@ -142,34 +185,30 @@ w32gdi_set_temperature(w32gdi_state_t *state, int temp, float brightness, } /* Create new gamma ramps */ - WORD *gamma_ramps = malloc(3*GAMMA_RAMP_SIZE*sizeof(WORD)); - if (gamma_ramps == NULL) { - perror("malloc"); - ReleaseDC(NULL, hDC); - return -1; + uint16_t *saved_gamma_r = NULL; + uint16_t *saved_gamma_g = NULL; + uint16_t *saved_gamma_b = NULL; + if (calibrations) { + saved_gamma_r = &state->saved_ramps[0*GAMMA_RAMP_SIZE]; + saved_gamma_g = &state->saved_ramps[1*GAMMA_RAMP_SIZE]; + saved_gamma_b = &state->saved_ramps[2*GAMMA_RAMP_SIZE]; } - - WORD *gamma_r = &gamma_ramps[0*GAMMA_RAMP_SIZE]; - WORD *gamma_g = &gamma_ramps[1*GAMMA_RAMP_SIZE]; - WORD *gamma_b = &gamma_ramps[2*GAMMA_RAMP_SIZE]; - - colorramp_fill(gamma_r, gamma_g, gamma_b, GAMMA_RAMP_SIZE, - temp, brightness, gamma); + colorramp_fill(state->gamma_r, state->gamma_g, state->gamma_b, + GAMMA_RAMP_SIZE, temp, brightness, state->gamma, + saved_gamma_r, saved_gamma_g, saved_gamma_b); /* Set new gamma ramps */ - r = SetDeviceGammaRamp(hDC, gamma_ramps); + r = SetDeviceGammaRamp(hDC, state->gamma_r); if (!r) { /* TODO it happens that SetDeviceGammaRamp returns FALSE on occasions where the adjustment seems to be successful. Does this only happen with multiple monitors connected? */ fputs(_("Unable to set gamma ramps.\n"), stderr); - free(gamma_ramps); + free(state->gamma_r); ReleaseDC(NULL, hDC); return -1; } - free(gamma_ramps); - /* Release device context */ ReleaseDC(NULL, hDC); diff --git a/src/gamma-w32gdi.h b/src/gamma-w32gdi.h index e81f4c5e..892edc6c 100644 --- a/src/gamma-w32gdi.h +++ b/src/gamma-w32gdi.h @@ -26,6 +26,10 @@ typedef struct { WORD *saved_ramps; + float gamma[3]; + WORD *gamma_r; + WORD *gamma_g; + WORD *gamma_b; } w32gdi_state_t; @@ -35,11 +39,11 @@ void w32gdi_free(w32gdi_state_t *state); void w32gdi_print_help(FILE *f); int w32gdi_set_option(w32gdi_state_t *state, const char *key, - const char *value); + const char *value, int section); void w32gdi_restore(w32gdi_state_t *state); int w32gdi_set_temperature(w32gdi_state_t *state, int temp, float brightness, - const float gamma[3]); + int calibrations); #endif /* ! REDSHIFT_GAMMA_W32GDI_H */ diff --git a/src/location-manual.c b/src/location-manual.c index 30dfc9c6..9f7d8ff0 100644 --- a/src/location-manual.c +++ b/src/location-manual.c @@ -57,6 +57,7 @@ location_manual_start(location_manual_state_t *state) void location_manual_free(location_manual_state_t *state) { + (void) state; } void diff --git a/src/redshift.c b/src/redshift.c index beecd353..843e4add 100644 --- a/src/redshift.c +++ b/src/redshift.c @@ -15,6 +15,7 @@ along with Redshift. If not, see . Copyright (c) 2013 Jon Lund Steffensen + Copyright (c) 2014 Mattias Andrée */ #ifdef HAVE_CONFIG_H @@ -198,34 +199,6 @@ static const location_provider_t location_providers[] = { { NULL } }; -/* Bounds for parameters. */ -#define MIN_LAT -90.0 -#define MAX_LAT 90.0 -#define MIN_LON -180.0 -#define MAX_LON 180.0 -#define MIN_TEMP 1000 -#define MAX_TEMP 25000 -#define MIN_BRIGHTNESS 0.1 -#define MAX_BRIGHTNESS 1.0 -#define MIN_GAMMA 0.1 -#define MAX_GAMMA 10.0 - -/* Default values for parameters. */ -#define DEFAULT_DAY_TEMP 5500 -#define DEFAULT_NIGHT_TEMP 3500 -#define DEFAULT_BRIGHTNESS 1.0 -#define DEFAULT_GAMMA 1.0 - -/* The color temperature when no adjustment is applied. */ -#define NEUTRAL_TEMP 6500 - -/* Angular elevation of the sun at which the color temperature - transition period starts and ends (in degress). - Transition during twilight, and while the sun is lower than - 3.0 degrees above the horizon. */ -#define TRANSITION_LOW SOLAR_CIVIL_TWILIGHT_ELEV -#define TRANSITION_HIGH 3.0 - /* Program modes. */ typedef enum { PROGRAM_MODE_CONTINUAL, @@ -245,6 +218,7 @@ static volatile sig_atomic_t disable = 0; static void sigexit(int signo) { + (void) signo; exiting = 1; } @@ -252,6 +226,7 @@ sigexit(int signo) static void sigdisable(int signo) { + (void) signo; disable = 1; } @@ -338,6 +313,7 @@ print_help(const char *program_name) " -o\t\tOne shot mode (do not continuously adjust" " color temperature)\n" " -O TEMP\tOne shot manual mode (set color temperature)\n" + " -P\t\tPreserve current calibrations\n" " -p\t\tPrint mode (only print parameters and exit)\n" " -x\t\tReset mode (remove adjustment from screen)\n" " -r\t\tDisable temperature transitions\n" @@ -451,7 +427,7 @@ provider_try_start(const location_provider_t *provider, and for backwards compatability. We add the proper keys here before calling set_option(). */ if (strcmp(provider->name, "manual") == 0 && - i < sizeof(manual_keys)/sizeof(manual_keys[0])) { + (unsigned)i < sizeof(manual_keys)/sizeof(manual_keys[0])) { key = manual_keys[i]; value = args; } else { @@ -493,7 +469,8 @@ provider_try_start(const location_provider_t *provider, static int method_try_start(const gamma_method_t *method, gamma_state_t *state, - config_ini_state_t *config, char *args) + config_ini_state_t *config, char *args, + char *gamma) { int r; @@ -504,28 +481,46 @@ method_try_start(const gamma_method_t *method, return -1; } + /* Set default gamma. */ + if (gamma != NULL) { + r = method->set_option(state, "gamma", gamma, -1); + free(gamma); + if (r < 0) { + method->free(state); + return -1; + } + } + /* Set method options from config file. */ - config_ini_section_t *section = - config_ini_get_section(config, method->name); - if (section != NULL) { - config_ini_setting_t *setting = section->settings; - while (setting != NULL) { - r = method->set_option(state, setting->name, - setting->value); - if (r < 0) { - method->free(state); - fprintf(stderr, _("Failed to set %s" - " option.\n"), - method->name); - /* TRANSLATORS: `help' must not be - translated. */ - fprintf(stderr, _("Try `-m %s:help' for more" - " information.\n"), - method->name); - return -1; + config_ini_section_t **sections = + config_ini_get_sections(config, method->name); + if (sections != NULL) { + int section_i = 0; + while (sections[section_i] != NULL) { + config_ini_setting_t *setting = sections[section_i]->settings; + while (setting != NULL) { + r = method->set_option(state, setting->name, + setting->value, section_i); + if (r < 0) { + method->free(state); + fprintf(stderr, _("Failed to set %s" + " option.\n"), + method->name); + /* TRANSLATORS: `help' must not be + translated. */ + fprintf(stderr, _("Try `-m %s:help' for more" + " information.\n"), + method->name); + return -1; + } + setting = setting->next; } - setting = setting->next; + section_i++; } + free(sections); + } else { + method->free(state); + return -1; } /* Set method options from command line. */ @@ -543,7 +538,7 @@ method_try_start(const gamma_method_t *method, *(value++) = '\0'; } - r = method->set_option(state, key, value); + r = method->set_option(state, key, value, -1); if (r < 0) { method->free(state); fprintf(stderr, _("Failed to set %s option.\n"), @@ -571,7 +566,7 @@ method_try_start(const gamma_method_t *method, /* A gamma string contains either one floating point value, or three values separated by colon. */ -static int +int parse_gamma_string(const char *str, float gamma[]) { char *s = strchr(str, ':'); @@ -663,7 +658,7 @@ main(int argc, char *argv[]) int temp_set = -1; int temp_day = -1; int temp_night = -1; - float gamma[3] = { NAN, NAN, NAN }; + char *gamma = NULL; float brightness_day = NAN; float brightness_night = NAN; @@ -673,6 +668,7 @@ main(int argc, char *argv[]) const location_provider_t *provider = NULL; char *provider_args = NULL; + int preserve_calibrations = -1; int transition = -1; program_mode_t mode = PROGRAM_MODE_CONTINUAL; int verbose = 0; @@ -686,7 +682,8 @@ main(int argc, char *argv[]) /* Parse command line arguments. */ int opt; - while ((opt = getopt(argc, argv, "b:c:g:hl:m:oO:prt:vVx")) != -1) { + while ((opt = getopt(argc, argv, "b:c:g:hl:m:oO:pPrt:vVx")) != -1) { + float _gamma[3]; switch (opt) { case 'b': parse_brightness_string(optarg, &brightness_day, &brightness_night); @@ -696,7 +693,8 @@ main(int argc, char *argv[]) config_filepath = strdup(optarg); break; case 'g': - r = parse_gamma_string(optarg, gamma); + gamma = strdup(optarg); + r = parse_gamma_string(optarg, _gamma); if (r < 0) { fputs(_("Malformed gamma argument.\n"), stderr); @@ -794,6 +792,9 @@ main(int argc, char *argv[]) case 'p': mode = PROGRAM_MODE_PRINT; break; + case 'P': + preserve_calibrations = 1; + break; case 'r': transition = 0; break; @@ -857,6 +858,12 @@ main(int argc, char *argv[]) if (transition < 0) { transition = !!atoi(setting->value); } + } else if (strcasecmp(setting->name, + "preserve-calibrations") == 0) { + if (preserve_calibrations < 0 && + mode == PROGRAM_MODE_CONTINUAL) { + preserve_calibrations = !!atoi(setting->value); + } } else if (strcasecmp(setting->name, "brightness") == 0) { if (isnan(brightness_day)) { @@ -876,15 +883,8 @@ main(int argc, char *argv[]) brightness_night = atof(setting->value); } } else if (strcasecmp(setting->name, "gamma") == 0) { - if (isnan(gamma[0])) { - r = parse_gamma_string(setting->value, - gamma); - if (r < 0) { - fputs(_("Malformed gamma" - " setting.\n"), - stderr); - exit(EXIT_FAILURE); - } + if (gamma == NULL) { + gamma = strdup(setting->value); } } else if (strcasecmp(setting->name, "adjustment-method") == 0) { @@ -929,8 +929,8 @@ main(int argc, char *argv[]) if (temp_night < 0) temp_night = DEFAULT_NIGHT_TEMP; if (isnan(brightness_day)) brightness_day = DEFAULT_BRIGHTNESS; if (isnan(brightness_night)) brightness_night = DEFAULT_BRIGHTNESS; - if (isnan(gamma[0])) gamma[0] = gamma[1] = gamma[2] = DEFAULT_GAMMA; if (transition < 0) transition = 1; + if (preserve_calibrations < 0) preserve_calibrations = 0; float lat = NAN; float lon = NAN; @@ -1056,20 +1056,10 @@ main(int argc, char *argv[]) printf(_("Brightness: %.2f:%.2f\n"), brightness_day, brightness_night); } - /* Gamma */ - if (gamma[0] < MIN_GAMMA || gamma[0] > MAX_GAMMA || - gamma[1] < MIN_GAMMA || gamma[1] > MAX_GAMMA || - gamma[2] < MIN_GAMMA || gamma[2] > MAX_GAMMA) { - fprintf(stderr, - _("Gamma value must be between %.1f and %.1f.\n"), - MIN_GAMMA, MAX_GAMMA); - exit(EXIT_FAILURE); - } - - if (verbose) { + /*if (verbose) { TODO gamma is now set on outputs individually printf(_("Gamma: %.3f, %.3f, %.3f\n"), gamma[0], gamma[1], gamma[2]); - } + }*/ /* Initialize gamma adjustment method. If method is NULL try all methods until one that works is found. */ @@ -1080,15 +1070,18 @@ main(int argc, char *argv[]) if (method != NULL) { /* Use method specified on command line. */ r = method_try_start(method, &state, &config_state, - method_args); - if (r < 0) exit(EXIT_FAILURE); + method_args, gamma); + if (r < 0) { + config_ini_free(&config_state); + exit(EXIT_FAILURE); + } } else { /* Try all methods, use the first that works. */ for (int i = 0; gamma_methods[i].name != NULL; i++) { const gamma_method_t *m = &gamma_methods[i]; if (!m->autostart) continue; - r = method_try_start(m, &state, &config_state, NULL); + r = method_try_start(m, &state, &config_state, NULL, gamma); if (r < 0) { fputs(_("Trying next method...\n"), stderr); continue; @@ -1147,7 +1140,7 @@ main(int argc, char *argv[]) } /* Adjust temperature */ - r = method->set_temperature(&state, temp, brightness, gamma); + r = method->set_temperature(&state, temp, brightness, preserve_calibrations); if (r < 0) { fputs(_("Temperature adjustment failed.\n"), stderr); method->free(&state); @@ -1160,7 +1153,7 @@ main(int argc, char *argv[]) if (verbose) printf(_("Color temperature: %uK\n"), temp_set); /* Adjust temperature */ - r = method->set_temperature(&state, temp_set, brightness_day, gamma); + r = method->set_temperature(&state, temp_set, brightness_day, preserve_calibrations); if (r < 0) { fputs(_("Temperature adjustment failed.\n"), stderr); method->free(&state); @@ -1172,7 +1165,7 @@ main(int argc, char *argv[]) case PROGRAM_MODE_RESET: { /* Reset screen */ - r = method->set_temperature(&state, NEUTRAL_TEMP, 1.0, gamma); + r = method->set_temperature(&state, NEUTRAL_TEMP, 1.0, preserve_calibrations); if (r < 0) { fputs(_("Temperature adjustment failed.\n"), stderr); method->free(&state); @@ -1354,7 +1347,7 @@ main(int argc, char *argv[]) if (!disabled || short_trans) { r = method->set_temperature(&state, temp, brightness, - gamma); + preserve_calibrations); if (r < 0) { fputs(_("Temperature adjustment" " failed.\n"), stderr); diff --git a/src/redshift.h b/src/redshift.h index 3a878777..fcb72806 100644 --- a/src/redshift.h +++ b/src/redshift.h @@ -24,17 +24,45 @@ #include +/* Bounds for parameters. */ +#define MIN_LAT -90.0 +#define MAX_LAT 90.0 +#define MIN_LON -180.0 +#define MAX_LON 180.0 +#define MIN_TEMP 1000 +#define MAX_TEMP 25000 +#define MIN_BRIGHTNESS 0.1 +#define MAX_BRIGHTNESS 1.0 +#define MIN_GAMMA 0.1 +#define MAX_GAMMA 10.0 + +/* Default values for parameters. */ +#define DEFAULT_DAY_TEMP 5500 +#define DEFAULT_NIGHT_TEMP 3500 +#define DEFAULT_BRIGHTNESS 1.0 +#define DEFAULT_GAMMA 1.0 + +/* The color temperature when no adjustment is applied. */ +#define NEUTRAL_TEMP 6500 + +/* Angular elevation of the sun at which the color temperature + transition period starts and ends (in degress). + Transition during twilight, and while the sun is lower than + 3.0 degrees above the horizon. */ +#define TRANSITION_LOW SOLAR_CIVIL_TWILIGHT_ELEV +#define TRANSITION_HIGH 3.0 + + /* Gamma adjustment method */ typedef int gamma_method_init_func(void *state); typedef int gamma_method_start_func(void *state); typedef void gamma_method_free_func(void *state); typedef void gamma_method_print_help_func(FILE *f); typedef int gamma_method_set_option_func(void *state, const char *key, - const char *value); + const char *value, int section); typedef void gamma_method_restore_func(void *state); typedef int gamma_method_set_temperature_func(void *state, int temp, - float brightness, - const float gamma[3]); + float brightness, int calibrations); typedef struct { char *name; @@ -91,4 +119,8 @@ typedef struct { } location_provider_t; +int +parse_gamma_string(const char *str, float gamma[]); + + #endif /* ! REDSHIFT_REDSHIFT_H */