Skip to content
This repository was archived by the owner on Apr 1, 2026. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions DESIGN
Original file line number Diff line number Diff line change
Expand Up @@ -58,11 +58,11 @@ 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 some features that are lacking in "vidmode" (IIRC it
has to do with multiple monitors). 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.
because it has support for multiple outputs per X screen whic 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,
and "drm" which allows manipulation of gamma ramps in a TTY in Linux.

Then there are location providers: "manual", "gnome-clock" and
"geoclue". Some time ago there was only one way to specify the
Expand Down
1 change: 1 addition & 0 deletions HACKING
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ Depenencies
-----------

* autotools, gettext
* libdrm (Optional, for DRM support)
* libxcb, libxcb-randr (Optional, for RandR support)
* libX11, libXxf86vm (Optional, for VidMode support)
* geoclue (Optional, for geoclue support)
Expand Down
26 changes: 26 additions & 0 deletions configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ AC_PROG_CC_C99
AM_GNU_GETTEXT_VERSION([0.17])
AM_GNU_GETTEXT([external])

PKG_CHECK_MODULES([DRM], [libdrm], [have_drm=yes], [have_drm=no])
PKG_CHECK_MODULES([X11], [x11], [have_x11=yes], [have_x11=no])
PKG_CHECK_MODULES([XF86VM], [xxf86vm], [have_xf86vm=yes], [have_xf86vm=no])
PKG_CHECK_MODULES([XCB], [xcb], [have_xcb=yes], [have_xcb=no])
Expand All @@ -29,6 +30,30 @@ AC_CHECK_HEADER([windows.h], [have_windows_h=yes], [have_windows_h=no])
# Check for Python
AM_PATH_PYTHON([3.3], [have_python=yes], [have_python=no])

# Check DRM method
AC_MSG_CHECKING([whether to enable DRM method])
AC_ARG_ENABLE([drm], [AC_HELP_STRING([--enable-drm],
[enable DRM method])],
[enable_drm=$enableval],[enable_drm=maybe])
AS_IF([test "x$enable_drm" != xno], [
AS_IF([test $have_drm = yes], [
AC_DEFINE([ENABLE_DRM], 1,
[Define to 1 to enable DRM method])
AC_MSG_RESULT([yes])
enable_drm=yes
], [
AC_MSG_RESULT([missing dependencies])
AS_IF([test "x$enable_drm" = xyes], [
AC_MSG_ERROR([missing dependencies for DRM method])
])
enable_drm=no
])
], [
AC_MSG_RESULT([no])
enable_drm=no
])
AM_CONDITIONAL([ENABLE_DRM], [test "x$enable_drm" = xyes])

# Check RANDR method
AC_MSG_CHECKING([whether to enable RANDR method])
AC_ARG_ENABLE([randr], [AC_HELP_STRING([--enable-randr],
Expand Down Expand Up @@ -191,6 +216,7 @@ echo "
ldflags: ${LDFLAGS}

Adjustment methods:
DRM: ${enable_drm}
RANDR: ${enable_randr}
VidMode: ${enable_vidmode}
WinGDI: ${enable_wingdi}
Expand Down
1 change: 1 addition & 0 deletions po/POTFILES.in
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ src/redshift.c

src/config-ini.c

src/gamma-drm.c
src/gamma-randr.c
src/gamma-vidmode.c
src/gamma-w32gdi.c
Expand Down
8 changes: 8 additions & 0 deletions src/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ redshift_SOURCES = \
gamma-dummy.c gamma-dummy.h

EXTRA_redshift_SOURCES = \
gamma-drm.c gamma-drm.h \
gamma-randr.c gamma-randr.h \
gamma-vidmode.c gamma-vidmode.h \
gamma-w32gdi.c gamma-w32gdi.h \
Expand All @@ -27,6 +28,13 @@ AM_CFLAGS =
redshift_LDADD = @LIBINTL@
EXTRA_DIST =

if ENABLE_DRM
redshift_SOURCES += gamma-drm.c gamma-drm.h
AM_CFLAGS += $(DRM_CFLAGS)
redshift_LDADD += \
$(DRM_LIBS) $(DRM_CFLAGS)
endif

if ENABLE_RANDR
redshift_SOURCES += gamma-randr.c gamma-randr.h
AM_CFLAGS += $(XCB_CFLAGS) $(XCB_RANDR_CFLAGS)
Expand Down
280 changes: 280 additions & 0 deletions src/gamma-drm.c
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. */
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

open should be enough to determine if permissions are ok by checking errno (or maybe I misunderstand this TODO?).

Copy link
Copy Markdown
Contributor Author

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.

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;
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems like a failure... shouldn't we return -1?

return -1;
}

state->crtcs = malloc(2 * sizeof(drm_crtc_state_t));
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The 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 drm_state_t?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. I don't think it really makes a difference.
Changing this now may lead to unnecessary
merge conflicts when pulling #44, where this
is changes to store the lenght, so I do not think
it is worth changing.

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. */
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So drmModeCrtcGetGamma() is reading the uninitialized memory?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The 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");
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The 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 perror("malloc") is misleading.

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;
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The 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 crtcs.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The 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;
}
Loading