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:
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user