Support for running Redshift without a graphical environment in Linux by using Direct Rendering Manager

Signed-off-by: Mattias Andrée <maandree@operamail.com>
This commit is contained in:
Mattias Andrée
2014-03-22 19:46:11 +01:00
parent f5da6bf71f
commit 67f479d057
9 changed files with 399 additions and 6 deletions
+5 -5
View File
@@ -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
+1
View File
@@ -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)
+26
View File
@@ -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])
@@ -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],
@@ -191,6 +216,7 @@ echo "
ldflags: ${LDFLAGS}
Adjustment methods:
DRM: ${enable_drm}
RANDR: ${enable_randr}
VidMode: ${enable_vidmode}
WinGDI: ${enable_wingdi}
+1
View File
@@ -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
+8
View File
@@ -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 \
@@ -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)
+280
View File
@@ -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;
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;
}
}
/* 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. */
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");
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;
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;
}
+58
View File
@@ -0,0 +1,58 @@
/* gamma-drm.h -- DRM gamma adjustment header
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>
*/
#ifndef _REDSHIFT_GAMMA_DRM_H
#define _REDSHIFT_GAMMA_DRM_H
#include <stdint.h>
#include <xf86drm.h>
#include <xf86drmMode.h>
typedef struct {
int crtc_num;
int crtc_id;
int gamma_size;
uint16_t* r_gamma;
uint16_t* g_gamma;
uint16_t* b_gamma;
} drm_crtc_state_t;
typedef struct {
int card_num;
int crtc_num;
int fd;
drmModeRes* res;
drm_crtc_state_t* crtcs;
} drm_state_t;
int drm_init(drm_state_t *state);
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);
void drm_restore(drm_state_t *state);
int drm_set_temperature(drm_state_t *state, int temp, float brightness, const float gamma[3]);
#endif /* ! _REDSHIFT_GAMMA_DRM_H */
+1 -1
View File
@@ -1,4 +1,4 @@
/* gamma-randr.c -- No-op gamma adjustment
/* gamma-dummy.c -- No-op gamma adjustment
This file is part of Redshift.
Redshift is free software: you can redistribute it and/or modify
+19
View File
@@ -53,6 +53,10 @@
#include "gamma-dummy.h"
#ifdef ENABLE_DRM
# include "gamma-drm.h"
#endif
#ifdef ENABLE_RANDR
# include "gamma-randr.h"
#endif
@@ -75,6 +79,9 @@
/* Union of state data for gamma adjustment methods */
typedef union {
#ifdef ENABLE_DRM
drm_state_t drm;
#endif
#ifdef ENABLE_RANDR
randr_state_t randr;
#endif
@@ -89,6 +96,18 @@ typedef union {
/* Gamma adjustment method structs */
static const gamma_method_t gamma_methods[] = {
#ifdef ENABLE_DRM
{
"drm", 0,
(gamma_method_init_func *)drm_init,
(gamma_method_start_func *)drm_start,
(gamma_method_free_func *)drm_free,
(gamma_method_print_help_func *)drm_print_help,
(gamma_method_set_option_func *)drm_set_option,
(gamma_method_restore_func *)drm_restore,
(gamma_method_set_temperature_func *)drm_set_temperature
},
#endif
#ifdef ENABLE_RANDR
{
"randr", 1,