backlight.c

Wed, 29 Jan 2020 06:03:40 +0100

author
David Demelier <markand@malikania.fr>
date
Wed, 29 Jan 2020 06:03:40 +0100
changeset 18
281cc3a22f1b
parent 17
37d1b7dd73ee
child 19
0de3886f98ad
permissions
-rw-r--r--

backlight: fix FreeBSD implementation

/*
 * main.c -- adjust laptop backlight using ACPI
 *
 * Copyright (c) 2010-2019 David Demelier <markand@malikania.fr>
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#include <errno.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/*
 * Functions to implement for your system:
 *
 * All of these functions are allowed to exit on failures.
 */

/* Set the level explicitly to the nearest value */
static void
set(unsigned int);

/* Get the current value */
static unsigned int
get(void);

static void
increase(void)
{
	unsigned int value = get();

	if (value + 2 > 100)
		value = 100;
	else
		value += 2;

	set(value);
}

static void
decrease(void)
{
	unsigned int value = get();

	if ((int)value - 2 < 0)
		value = 0;
	else
		value -= 2;

	set(value);
}


static void
usage(const char *name)
{
	fprintf(stderr, "usage: %s decrease\n", name);
	fprintf(stderr, "       %s get\n", name);
	fprintf(stderr, "       %s increase\n", name);
	fprintf(stderr, "       %s set percentage\n", name);
	exit(1);
}

static void
die(const char *fmt, ...)
{
	va_list ap;

	va_start(ap, fmt);
	fprintf(stderr, "abort: ");
	vfprintf(stderr, fmt, ap);
	va_end(ap);

	exit(1);
}

/* {{{ Support for FreeBSD */

#if defined(__FreeBSD__)

#include <sys/types.h>
#include <sys/sysctl.h>

static int
upper_bound(int *levels, size_t length, unsigned int percent)
{
	size_t low = 0;
	size_t high = length - 1;

	while (low < high) {
		size_t mid = (low + high) / 2;

		if (percent <= (unsigned)levels[mid])
		    high = mid;
		else
		    low = mid + 1;
	}

	return low;
}

static int
min(const void *v1, const void *v2)
{
	return *(int *)v1 - *(int *)v2;
}

static void
sort(int *tab, size_t size)
{
	qsort(tab, size, sizeof (int), min);
}

static void
set(unsigned int percent)
{
	int levels[200] = {0};
	size_t length = sizeof (levels);

	if (sysctlbyname("hw.acpi.video.lcd0.levels", levels, &length, NULL, 0) < 0)
		die("sysctl: %s\n", strerror(errno));

	length /= sizeof (int);

	/* Find appropriate nearest level */
	if (percent > 0 && percent < 100) {
		sort(levels, length);
		percent = levels[upper_bound(levels, length, percent)];
	}

	if (sysctlbyname("hw.acpi.video.lcd0.brightness", NULL, NULL,
	    &percent, sizeof (int)) < 0)
		die("sysctl: %s\n", strerror(errno));

}

static unsigned int
get(void)
{
	int current = 0;
	size_t length = sizeof (int);

	if (sysctlbyname("hw.acpi.video.lcd0.brightness", &current, &length, NULL, 0) == -1)
		die("sysctl: %s\n", strerror(errno));

	return current;
}

/* }}} */

/* {{{ Support for Linux */

#elif defined(__linux__)

#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
#include <fcntl.h>
#include <unistd.h>

#define SYS_PATH "/sys/class/backlight"

static unsigned int
read_int(int dfd, const char *file)
{
	char buf[32] = {0};
	int fd;

	if ((fd = openat(dfd, file, O_RDONLY)) < 0)
		die("open: %s\n", strerror(errno));
	if (read(fd, &buf[0], sizeof (buf) - 1) < 0)
		die("read: %s\n", strerror(errno));

	close(fd);

	return atoi(buf);
}

static int
write_int(int dfd, const char *file, unsigned int value)
{
	char buf[32] = {0};
	int length;
	int fd;

	if ((fd = openat(dfd, file, O_WRONLY)) < 0)
		die("open: %s\n", strerror(errno));
	if ((length = snprintf(buf, sizeof (buf), "%d\n", value)) < 0)
		die("snprintf: %s\n", strerror(errno));
	if (write(fd, buf, length) != length)
		die("write: %s\n", strerror(errno));

	/* Re-read in case kernel change the value. */
	return read_int(dfd, "brightness");
}

static int
try_adaptor(int parent_fd, const char *path)
{
	int dfd;
	struct stat st;

	if ((dfd = openat(parent_fd, path, O_RDONLY)) < 0)
		return -1;
	if (fstatat(dfd, "brightness", &st, 0) < 0) {
		close(dfd);
		return -1;
	}

	return dfd;
}

static int
find_adaptor(void)
{
	int dfd, adaptorfd = -1;
	DIR *dirp;
	struct dirent *dirent;

	if ((dfd = open(SYS_PATH, O_RDONLY)) < 0)
		die("open: %s: %s\n", SYS_PATH, strerror(errno));
	if (!(dirp = fdopendir(dfd)))
		die("fdopendir: %s: %s\n", SYS_PATH, strerror(errno));

	while ((dirent = readdir(dirp))) {
		if (strcmp(dirent->d_name, ".") == 0 ||
		    strcmp(dirent->d_name, "..") == 0)
			continue;

		if ((adaptorfd = try_adaptor(dfd, dirent->d_name)) >= 0)
			break;
	}

	closedir(dirp);
	close(dfd);

	if (adaptorfd < 0)
		die("could not find card adaptor\n");

	return adaptorfd;
}

static unsigned int
downscale(int dfd, unsigned int value)
{
	return value * 100 / read_int(dfd, "max_brightness");
}

static unsigned int
upscale(int dfd, unsigned int value)
{
	return value * read_int(dfd, "max_brightness") / 100;
}

static void
set(unsigned int percent)
{
	int dfd = find_adaptor();
	unsigned int value = upscale(dfd, percent);

	write_int(dfd, "brightness", value);
}

static unsigned int
get(void)
{
	int dfd = find_adaptor();
	int value = read_int(dfd, "brightness");

	return downscale(dfd, value);
}

/* }}} */

/* {{{ Non supported shims */

#else

static void
notsupported(void)
{
	die("backlight is not supported on this system\n");
}

static void
set(unsigned int v)
{
	(void)v;

	notsupported();
}

static unsigned int
get(void)
{
	notsupported();

	return -1;
}

#endif

/* }}} */

int
main(int argc, char *argv[])
{
	-- argc;
	++ argv;

	if (argc < 1)
		usage("backlight");

	if (strcmp(argv[0], "get") == 0)
		printf("%d\n", get());
	else if (strcmp(argv[0], "set") == 0) {
		if (argc != 2)
			usage(argv[0]);

		unsigned int value = atoi(argv[1]);

		if (value > 100)
			die("value %u is out of range\n", value);

		set(value);
	} else if (strcmp(argv[0], "increase") == 0)
		increase();
	else if (strcmp(argv[0], "decrease") == 0)
		decrease();
}

mercurial