diff --git a/configure.ac b/configure.ac index 6b54bb30..ba42a044 100644 --- a/configure.ac +++ b/configure.ac @@ -73,7 +73,7 @@ AC_ARG_ENABLE(drm-compositor, [ --enable-drm-compositor],, AM_CONDITIONAL(ENABLE_DRM_COMPOSITOR, test x$enable_drm_compositor = xyes) if test x$enable_drm_compositor = xyes; then AC_DEFINE([BUILD_DRM_COMPOSITOR], [1], [Build the DRM compositor]) - PKG_CHECK_MODULES(DRM_COMPOSITOR, [libbacklight libudev >= 136 libdrm >= 2.4.30 gbm]) + PKG_CHECK_MODULES(DRM_COMPOSITOR, [libudev >= 136 libdrm >= 2.4.30 gbm]) fi diff --git a/src/Makefile.am b/src/Makefile.am index 5788df30..f1003f70 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -61,7 +61,13 @@ drm_backend = drm-backend.la drm_backend_la_LDFLAGS = -module -avoid-version drm_backend_la_LIBADD = $(COMPOSITOR_LIBS) $(DRM_COMPOSITOR_LIBS) drm_backend_la_CFLAGS = $(DRM_COMPOSITOR_CFLAGS) $(GCC_CFLAGS) -drm_backend_la_SOURCES = compositor-drm.c tty.c evdev.c evdev.h +drm_backend_la_SOURCES = \ + compositor-drm.c \ + tty.c \ + evdev.c \ + evdev.h \ + libbacklight.c \ + libbacklight.h endif if ENABLE_WAYLAND_COMPOSITOR diff --git a/src/libbacklight.c b/src/libbacklight.c new file mode 100644 index 00000000..b2e19bf9 --- /dev/null +++ b/src/libbacklight.c @@ -0,0 +1,322 @@ +/* + * libbacklight - userspace interface to Linux backlight control + * + * Copyright © 2012 Intel Corporation + * Copyright 2010 Red Hat + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Matthew Garrett + * Tiago Vignatti + */ + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static const char *output_names[] = { "Unknown", + "VGA", + "DVI-I", + "DVI-D", + "DVI-A", + "Composite", + "SVIDEO", + "LVDS", + "Component", + "9-pin DIN", + "DisplayPort" + "HDMI Type A", + "HDMI Type B", + "TV", + "Embedded DisplayPort" +}; + +static long backlight_get(struct backlight *backlight, char *node) +{ + char buffer[100]; + char *path; + int fd; + long value, ret; + + if (asprintf(&path, "%s/%s", backlight->path, node) < 0) + return -ENOMEM +; + fd = open(path, O_RDONLY); + if (fd < 0) { + ret = -1; + goto out; + } + + ret = read(fd, &buffer, sizeof(buffer)); + if (ret < 1) { + ret = -1; + goto out; + } + + value = strtol(buffer, NULL, 10); + ret = value; +out: + close(fd); + if (path) + free(path); + return ret; +} + +long backlight_get_brightness(struct backlight *backlight) +{ + return backlight_get(backlight, "brightness"); +} + +long backlight_get_max_brightness(struct backlight *backlight) +{ + return backlight_get(backlight, "max_brightness"); +} + +long backlight_get_actual_brightness(struct backlight *backlight) +{ + return backlight_get(backlight, "actual_brightness"); +} + +long backlight_set_brightness(struct backlight *backlight, long brightness) +{ + char *path; + char *buffer = NULL; + int fd; + long ret; + + if (asprintf(&path, "%s/%s", backlight->path, "brightness") < 0) + return -ENOMEM; + + fd = open(path, O_RDWR); + if (fd < 0) { + ret = -1; + goto out; + } + + ret = read(fd, &buffer, sizeof(buffer)); + if (ret < 1) { + ret = -1; + goto out; + } + + if (asprintf(&buffer, "%ld", brightness) < 0) + return -ENOMEM; + + ret = write(fd, buffer, strlen(buffer)); + if (ret < 0) { + ret = -1; + goto out; + } + + ret = backlight_get_brightness(backlight); + backlight->brightness = ret; +out: + if (buffer) + free(buffer); + if (path) + free(path); + close(fd); + return ret; +} + +void backlight_destroy(struct backlight *backlight) +{ + if (!backlight) + return; + + if (backlight->path) + free(backlight->path); + + free(backlight); +} + +struct backlight *backlight_init(struct udev_device *drm_device, + uint32_t connector_type) +{ + const char *syspath = NULL; + char *pci_name = NULL; + char *chosen_path = NULL; + char *path = NULL; + DIR *backlights; + struct dirent *entry; + enum backlight_type type = 0; + char buffer[100]; + struct backlight *backlight; + int err, ret; + + if (!drm_device) + return NULL; + + syspath = udev_device_get_syspath(drm_device); + if (!syspath) + return NULL; + + if (asprintf(&path, "%s/%s", syspath, "device") < 0) + return NULL; + + ret = readlink(path, buffer, sizeof(buffer)); + free(path); + if (ret < 0) + return NULL; + + buffer[ret] = '\0'; + pci_name = basename(buffer); + + if (connector_type <= 0) + return NULL; + + backlights = opendir("/sys/class/backlight"); + if (!backlights) + return NULL; + + /* Find the "best" backlight for the device. Firmware + interfaces are preferred over platform interfaces are + preferred over raw interfaces. For raw interfaces we'll + check if the device ID in the form of pci match, while + for firmware interfaces we require the pci ID to + match. It's assumed that platform interfaces always match, + since we can't actually associate them with IDs. + + A further awkwardness is that, while it's theoretically + possible for an ACPI interface to include support for + changing the backlight of external devices, it's unlikely + to ever be done. It's effectively impossible for a platform + interface to do so. So if we get asked about anything that + isn't LVDS or eDP, we pretty much have to require that the + control be supplied via a raw interface */ + + while ((entry = readdir(backlights))) { + char *backlight_path; + char *parent; + enum backlight_type entry_type; + int fd; + + if (entry->d_name[0] == '.') + continue; + + if (asprintf(&backlight_path, "%s/%s", "/sys/class/backlight", + entry->d_name) < 0) + return NULL; + + if (asprintf(&path, "%s/%s", backlight_path, "type") < 0) + return NULL; + + fd = open(path, O_RDONLY); + + if (fd < 0) + goto out; + + ret = read (fd, &buffer, sizeof(buffer)); + close (fd); + + if (ret < 1) + goto out; + + buffer[ret] = '\0'; + + if (!strncmp(buffer, "raw\n", sizeof(buffer))) + entry_type = BACKLIGHT_RAW; + else if (!strncmp(buffer, "platform\n", sizeof(buffer))) + entry_type = BACKLIGHT_PLATFORM; + else if (!strncmp(buffer, "firmware\n", sizeof(buffer))) + entry_type = BACKLIGHT_FIRMWARE; + else + goto out; + + if (connector_type != DRM_MODE_CONNECTOR_LVDS && + connector_type != DRM_MODE_CONNECTOR_eDP) { + /* External displays are assumed to require + gpu control at the moment */ + if (entry_type != BACKLIGHT_RAW) + goto out; + } + + free (path); + + if (asprintf(&path, "%s/%s", backlight_path, "device") < 0) + return NULL; + + ret = readlink(path, buffer, sizeof(buffer)); + + if (ret < 0) + goto out; + + buffer[ret] = '\0'; + + parent = basename(buffer); + + /* Perform matching for raw and firmware backlights - + platform backlights have to be assumed to match */ + if (entry_type == BACKLIGHT_RAW || + entry_type == BACKLIGHT_FIRMWARE) { + if (!(pci_name && !strcmp(pci_name, parent))) + goto out; + } + + if (entry_type < type) + goto out; + + type = entry_type; + + if (chosen_path) + free(chosen_path); + chosen_path = strdup(backlight_path); + + out: + free(backlight_path); + free(path); + } + + if (!chosen_path) + return NULL; + + backlight = malloc(sizeof(struct backlight)); + + if (!backlight) + goto err; + + backlight->path = chosen_path; + backlight->type = type; + + backlight->max_brightness = backlight_get_max_brightness(backlight); + if (backlight->max_brightness < 0) + goto err; + + backlight->brightness = backlight_get_actual_brightness(backlight); + if (backlight->brightness < 0) + goto err; + + return backlight; +err: + if (chosen_path) + free(chosen_path); + free (backlight); + return NULL; +} diff --git a/src/libbacklight.h b/src/libbacklight.h new file mode 100644 index 00000000..cea34c26 --- /dev/null +++ b/src/libbacklight.h @@ -0,0 +1,49 @@ +#ifndef LIBBACKLIGHT_H +#define LIBBACKLIGHT_H +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +enum backlight_type { + BACKLIGHT_RAW, + BACKLIGHT_PLATFORM, + BACKLIGHT_FIRMWARE, +}; + +struct backlight { + char *path; + int max_brightness; + int brightness; + enum backlight_type type; +}; + +/* + * Find and set up a backlight for a valid udev connector device, i.e. one + * matching drm subsytem and with status of connected. + */ +struct backlight *backlight_init(struct udev_device *drm_device, + uint32_t connector_type); + +/* Free backlight resources */ +void backlight_destroy(struct backlight *backlight); + +/* Provide the maximum backlight value */ +long backlight_get_max_brightness(struct backlight *backlight); + +/* Provide the cached backlight value */ +long backlight_get_brightness(struct backlight *backlight); + +/* Provide the hardware backlight value */ +long backlight_get_actual_brightness(struct backlight *backlight); + +/* Set the backlight to a value between 0 and max */ +long backlight_set_brightness(struct backlight *backlight, long brightness); + +#ifdef __cplusplus +} +#endif + +#endif /* LIBBACKLIGHT_H */