-
Notifications
You must be signed in to change notification settings - Fork 430
Support for running Redshift outside X #42
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,280 @@ | ||
| /* gamma-drm.c -- DRM gamma adjustment source | ||
| This file is part of Redshift. | ||
|
|
||
| Redshift 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 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. If not, see <http://www.gnu.org/licenses/>. | ||
|
|
||
| Copyright (c) 2014 Mattias Andrée <maandree@member.fsf.org> | ||
| */ | ||
|
|
||
| #include <stdio.h> | ||
| #include <stdlib.h> | ||
| #include <stdint.h> | ||
| #include <string.h> | ||
| #include <alloca.h> | ||
| #include <sys/types.h> | ||
| #include <sys/stat.h> | ||
| #include <fcntl.h> | ||
| #include <unistd.h> | ||
|
|
||
| #ifdef ENABLE_NLS | ||
| # include <libintl.h> | ||
| # define _(s) gettext(s) | ||
| #else | ||
| # define _(s) s | ||
| #endif | ||
|
|
||
| #ifndef O_CLOEXEC | ||
| #define O_CLOEXEC 02000000 | ||
| #endif | ||
|
|
||
| #include "gamma-drm.h" | ||
| #include "colorramp.h" | ||
|
|
||
|
|
||
| int | ||
| drm_init(drm_state_t *state) | ||
| { | ||
| /* Initialize state. */ | ||
| state->card_num = 0; | ||
| state->crtc_num = -1; | ||
| state->fd = -1; | ||
| state->res = NULL; | ||
| state->crtcs = NULL; | ||
|
|
||
| return 0; | ||
| } | ||
|
|
||
| int | ||
| drm_start(drm_state_t *state) | ||
| { | ||
| /* Acquire access to a graphics card. */ | ||
| long maxlen = strlen(DRM_DIR_NAME) + strlen(DRM_DEV_NAME) + 10; | ||
| char *pathname = alloca(maxlen * sizeof(char)); | ||
|
|
||
| sprintf(pathname, DRM_DEV_NAME, DRM_DIR_NAME, state->card_num); | ||
|
|
||
| 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. */ | ||
| 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; | ||
| } | ||
|
|
||
| /* Create entries for selected CRTCs. */ | ||
| int crtc_count = state->res->count_crtcs; | ||
| if (state->crtc_num >= 0) { | ||
| if (state->crtc_num >= crtc_count) { | ||
| fprintf(stderr, _("CRTC %d does not exist. "), | ||
| state->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")); | ||
| } | ||
| close(state->fd); | ||
| state->fd = -1; | ||
| drmModeFreeResources(state->res); | ||
| state->res = NULL; | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This seems like a failure... shouldn't we |
||
| return -1; | ||
| } | ||
|
|
||
| state->crtcs = malloc(2 * sizeof(drm_crtc_state_t)); | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The extra array element is a dummy? Why not just store the length in
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes. I don't think it really makes a difference. |
||
| 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; | ||
| } | ||
| } | ||
|
|
||
| /* 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); | ||
| if (crtc_info == NULL) { | ||
| fprintf(stderr, _("CRTC %i lost, skipping\n"), crtcs->crtc_num); | ||
| continue; | ||
| } | ||
| crtcs->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); | ||
| continue; | ||
| } | ||
| /* Valgrind complains about us reading uninitialize memory if we just use malloc. */ | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Appears so, I have no idea why. |
||
| 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 { | ||
| perror("malloc"); | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. it seems that we could end here if drmModeCrtcGetGamma fails, in which case |
||
| 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; | ||
| } | ||
| } | ||
|
|
||
| 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); | ||
| } | ||
| 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++; | ||
| } | ||
| free(state->crtcs); | ||
| state->crtcs = NULL; | ||
| } | ||
| if (state->res != NULL) { | ||
| drmModeFreeResources(state->res); | ||
| state->res = NULL; | ||
| } | ||
| if (state->fd >= 0) { | ||
| close(state->fd); | ||
| state->fd = -1; | ||
| } | ||
| } | ||
|
|
||
| void | ||
| drm_print_help(FILE *f) | ||
| { | ||
| fputs(_("Adjust gamma ramps with Direct Rendering Manager.\n"), f); | ||
| fputs("\n", f); | ||
|
|
||
| /* TRANSLATORS: DRM help output | ||
| left column must not be translated */ | ||
| fputs(_(" card=N\tGraphics card to apply adjustments to\n" | ||
| " crtc=N\tCRTC to apply adjustments to\n"), f); | ||
| fputs("\n", f); | ||
| } | ||
|
|
||
| int | ||
| drm_set_option(drm_state_t *state, const char *key, const char *value) | ||
| { | ||
| if (strcasecmp(key, "card") == 0) { | ||
| state->card_num = atoi(value); | ||
| } 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")); | ||
| return -1; | ||
| } | ||
| } else { | ||
| fprintf(stderr, _("Unknown method parameter: `%s'.\n"), key); | ||
| return -1; | ||
| } | ||
|
|
||
| return 0; | ||
| } | ||
|
|
||
| int | ||
| drm_set_temperature(drm_state_t *state, int temp, float brightness, const float gamma[3]) | ||
| { | ||
| 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; | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Testing now on a computer that does not support gamma ramps and it gets stuck in an infinite loop here because you don't advance
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should be fixed now. |
||
| 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; | ||
| } | ||
| 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; | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
openshould be enough to determine if permissions are ok by checking errno (or maybe I misunderstand this TODO?).There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note well expressed TODO: The idea is that on failure the permission bits, owner and groups as well as you group membership should be read to determine why your are not able to open it and give a good explaination why you cannot use it. For example, the sysadmin might have given you membership to video but have change the permissions so that only root can get access to the graphics card as it can change monitor resolution which for example some schools (for some reason I do not understand) remove that permission from you.