Initial commit
These used to be part of my dwm fork repository.
This commit is contained in:
commit
a91e24930c
|
@ -0,0 +1,9 @@
|
||||||
|
# Build files
|
||||||
|
/build
|
||||||
|
|
||||||
|
# Qt Creator files
|
||||||
|
/CMakeLists.txt.user*
|
||||||
|
/desktop-tools.config
|
||||||
|
/desktop-tools.files
|
||||||
|
/desktop-tools.creator*
|
||||||
|
/desktop-tools.includes
|
|
@ -0,0 +1,3 @@
|
||||||
|
[submodule "liberty"]
|
||||||
|
path = liberty
|
||||||
|
url = https://github.com/pjanouch/liberty.git
|
|
@ -0,0 +1,67 @@
|
||||||
|
project (desktop-tools C)
|
||||||
|
cmake_minimum_required (VERSION 2.8.11)
|
||||||
|
|
||||||
|
# Moar warnings
|
||||||
|
if ("${CMAKE_C_COMPILER_ID}" MATCHES "GNU" OR CMAKE_COMPILER_IS_GNUC)
|
||||||
|
# -Wunused-function is pretty annoying here, as everything is static
|
||||||
|
set (CMAKE_C_FLAGS "-std=c99 -Wall -Wextra -Wno-unused-function")
|
||||||
|
endif ("${CMAKE_C_COMPILER_ID}" MATCHES "GNU" OR CMAKE_COMPILER_IS_GNUC)
|
||||||
|
|
||||||
|
# Version
|
||||||
|
set (project_VERSION_MAJOR "0")
|
||||||
|
set (project_VERSION_MINOR "1")
|
||||||
|
set (project_VERSION_PATCH "0")
|
||||||
|
|
||||||
|
set (project_VERSION "${project_VERSION_MAJOR}")
|
||||||
|
set (project_VERSION "${project_VERSION}.${project_VERSION_MINOR}")
|
||||||
|
set (project_VERSION "${project_VERSION}.${project_VERSION_PATCH}")
|
||||||
|
|
||||||
|
# For custom modules
|
||||||
|
set (CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake)
|
||||||
|
|
||||||
|
# Dependencies
|
||||||
|
set (CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/liberty/cmake)
|
||||||
|
include (AddThreads)
|
||||||
|
|
||||||
|
find_package (PkgConfig REQUIRED)
|
||||||
|
pkg_check_modules (dependencies REQUIRED libpulse x11)
|
||||||
|
|
||||||
|
set (project_libraries ${dependencies_LIBRARIES})
|
||||||
|
include_directories (${dependencies_INCLUDE_DIRS})
|
||||||
|
|
||||||
|
# Generate a configuration file
|
||||||
|
configure_file (${PROJECT_SOURCE_DIR}/config.h.in ${PROJECT_BINARY_DIR}/config.h)
|
||||||
|
include_directories (${PROJECT_BINARY_DIR})
|
||||||
|
|
||||||
|
# Build
|
||||||
|
add_executable (dwmstatus dwmstatus.c)
|
||||||
|
target_link_libraries (dwmstatus ${project_libraries})
|
||||||
|
add_threads (dwmstatus)
|
||||||
|
|
||||||
|
add_executable (brightness brightness.c)
|
||||||
|
target_link_libraries (brightness ${project_libraries})
|
||||||
|
add_threads (brightness)
|
||||||
|
|
||||||
|
# The files to be installed
|
||||||
|
include (GNUInstallDirs)
|
||||||
|
install (TARGETS dwmstatus brightness DESTINATION ${CMAKE_INSTALL_BINDIR})
|
||||||
|
install (FILES LICENSE DESTINATION ${CMAKE_INSTALL_DOCDIR})
|
||||||
|
|
||||||
|
# CPack
|
||||||
|
set (CPACK_PACKAGE_DESCRIPTION_SUMMARY "Desktop tools")
|
||||||
|
set (CPACK_PACKAGE_VENDOR "Premysl Janouch")
|
||||||
|
set (CPACK_PACKAGE_CONTACT "Přemysl Janouch <p.janouch@gmail.com>")
|
||||||
|
set (CPACK_RESOURCE_FILE_LICENSE "${PROJECT_SOURCE_DIR}/LICENSE")
|
||||||
|
set (CPACK_PACKAGE_VERSION_MAJOR ${project_VERSION_MAJOR})
|
||||||
|
set (CPACK_PACKAGE_VERSION_MINOR ${project_VERSION_MINOR})
|
||||||
|
set (CPACK_PACKAGE_VERSION_PATCH ${project_VERSION_PATCH})
|
||||||
|
set (CPACK_GENERATOR "TGZ;ZIP")
|
||||||
|
set (CPACK_PACKAGE_FILE_NAME
|
||||||
|
"${PROJECT_NAME}-${project_VERSION}-${CMAKE_SYSTEM_NAME}-${CMAKE_SYSTEM_PROCESSOR}")
|
||||||
|
set (CPACK_PACKAGE_INSTALL_DIRECTORY "${PROJECT_NAME}-${project_VERSION}")
|
||||||
|
set (CPACK_SOURCE_GENERATOR "TGZ;ZIP")
|
||||||
|
set (CPACK_SOURCE_IGNORE_FILES "/\\\\.git;/build;/CMakeLists.txt.user")
|
||||||
|
set (CPACK_SOURCE_PACKAGE_FILE_NAME "${PROJECT_NAME}-${project_VERSION}")
|
||||||
|
|
||||||
|
set (CPACK_SET_DESTDIR TRUE)
|
||||||
|
include (CPack)
|
|
@ -0,0 +1,14 @@
|
||||||
|
Copyright (c) 2015 - 2016, Přemysl Janouch <p.janouch@gmail.com>
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Permission to use, copy, modify, and/or 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.
|
|
@ -0,0 +1,49 @@
|
||||||
|
desktop-tools
|
||||||
|
=============
|
||||||
|
:compact-option:
|
||||||
|
|
||||||
|
'desktop-tools' is a collection of tools to run my desktop that might be useful
|
||||||
|
to other people as well.
|
||||||
|
|
||||||
|
- 'dwmstatus' does literally everything my dwm doesn't but I'd like it to. It
|
||||||
|
includes PulseAudio volume management and hand-written NUT and MPD clients,
|
||||||
|
all in the name of liberation from GPL-licensed software of course
|
||||||
|
- 'brightness' allows me to change the brightness of w/e display device I have.
|
||||||
|
|
||||||
|
Don't expect them to work under any OS that isn't Linux.
|
||||||
|
|
||||||
|
Building and Running
|
||||||
|
--------------------
|
||||||
|
Build dependencies: CMake, pkg-config, liberty (included) +
|
||||||
|
Runtime dependencies: libpulse, libx11
|
||||||
|
|
||||||
|
$ git clone --recursive https://github.com/pjanouch/desktop-tools.git
|
||||||
|
$ mkdir desktop-tools/build
|
||||||
|
$ cd desktop-tools/build
|
||||||
|
$ cmake .. -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_BUILD_TYPE=Debug
|
||||||
|
$ make
|
||||||
|
|
||||||
|
To install the applications, you can do either the usual:
|
||||||
|
|
||||||
|
# make install
|
||||||
|
|
||||||
|
Or you can try telling CMake to make a package for you. For Debian it is:
|
||||||
|
|
||||||
|
$ cpack -G DEB
|
||||||
|
# dpkg -i desktop-tools-*.deb
|
||||||
|
|
||||||
|
Contributing and Support
|
||||||
|
------------------------
|
||||||
|
Use this project's GitHub to report any bugs, request features, or submit pull
|
||||||
|
requests. If you want to discuss this project, or maybe just hang out with
|
||||||
|
the developer, feel free to join me at irc://anathema.irc.so, channel #anathema.
|
||||||
|
|
||||||
|
License
|
||||||
|
-------
|
||||||
|
'desktop-tools' is written by Přemysl Janouch <p.janouch@gmail.com>.
|
||||||
|
|
||||||
|
You may use the software under the terms of the ISC license, the text of which
|
||||||
|
is included within the package, or, at your option, you may relicense the work
|
||||||
|
under the MIT or the Modified BSD License, as listed at the following site:
|
||||||
|
|
||||||
|
http://www.gnu.org/licenses/license-list.html
|
|
@ -0,0 +1,445 @@
|
||||||
|
/*
|
||||||
|
* brightness.c: set display brightness via DDC/CI - Linux only
|
||||||
|
*
|
||||||
|
* Copyright (c) 2015, Přemysl Janouch <p.janouch@gmail.com>
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and/or 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.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Sources: ddcciv1r1.pdf, i2c-dev.c in Linux, ddccontrol source code,
|
||||||
|
// http://www.boichat.ch/nicolas/ddcci/specs.html was also helpful
|
||||||
|
|
||||||
|
// This makes openat() available even though I set _POSIX_C_SOURCE and
|
||||||
|
// _XOPEN_SOURCE to a version of POSIX older than 2008
|
||||||
|
#define _GNU_SOURCE
|
||||||
|
|
||||||
|
// Undo some dwm Makefile damage and import my everything-library
|
||||||
|
#include "config.h"
|
||||||
|
#undef PROGRAM_NAME
|
||||||
|
#define PROGRAM_NAME "brightness"
|
||||||
|
#include "liberty/liberty.c"
|
||||||
|
|
||||||
|
#include <sys/ioctl.h>
|
||||||
|
#include <dirent.h>
|
||||||
|
|
||||||
|
#include <linux/i2c.h>
|
||||||
|
#include <linux/i2c-dev.h>
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
|
||||||
|
static void
|
||||||
|
log_message_custom (void *user_data, const char *quote, const char *fmt,
|
||||||
|
va_list ap)
|
||||||
|
{
|
||||||
|
(void) user_data;
|
||||||
|
FILE *stream = stdout;
|
||||||
|
|
||||||
|
fprintf (stream, PROGRAM_NAME ": ");
|
||||||
|
fputs (quote, stream);
|
||||||
|
vfprintf (stream, fmt, ap);
|
||||||
|
fputs ("\n", stream);
|
||||||
|
}
|
||||||
|
|
||||||
|
#define FAIL(...) \
|
||||||
|
BLOCK_START \
|
||||||
|
error_set (e, __VA_ARGS__); \
|
||||||
|
return false; \
|
||||||
|
BLOCK_END
|
||||||
|
|
||||||
|
static void
|
||||||
|
wait_ms (long ms)
|
||||||
|
{
|
||||||
|
struct timespec ts = { 0, ms * 1000 * 1000 };
|
||||||
|
nanosleep (&ts, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
xstrtol (const char *s, long *out)
|
||||||
|
{
|
||||||
|
char *end;
|
||||||
|
errno = 0;
|
||||||
|
*out = strtol (s, &end, 10);
|
||||||
|
return errno == 0 && !*end && end != s;
|
||||||
|
}
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
|
||||||
|
#define DDC_LENGTH_XOR 0x80
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
DDC_ADDRESS_HOST = 0x50, ///< Bus master's base address
|
||||||
|
DDC_ADDRESS_DISPLAY = 0x6E ///< The display's base address
|
||||||
|
};
|
||||||
|
|
||||||
|
enum { I2C_WRITE, I2C_READ };
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
DDC_GET_VCP_FEATURE = 0x01, ///< Request info about a feature
|
||||||
|
DDC_GET_VCP_FEATURE_REPLY = 0x02, ///< Feature info response
|
||||||
|
DDC_SET_VCP_FEATURE = 0x03 ///< Set or activate a feature
|
||||||
|
};
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
VCP_BRIGHTNESS = 0x10, ///< Standard VCP opcode for brightness
|
||||||
|
VCP_CONTRAST = 0x12 ///< Standard VCP opcode for contrast
|
||||||
|
};
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
|
||||||
|
static bool
|
||||||
|
check_edid (int fd, struct error **e)
|
||||||
|
{
|
||||||
|
uint8_t edid_req[] = { 0x00 };
|
||||||
|
uint8_t buf[8] = "";
|
||||||
|
struct i2c_msg bufs[] =
|
||||||
|
{
|
||||||
|
{ .addr = 0x50, .flags = 0,
|
||||||
|
.len = 1, .buf = edid_req },
|
||||||
|
{ .addr = 0x50, .flags = I2C_M_RD,
|
||||||
|
.len = sizeof buf, .buf = buf },
|
||||||
|
};
|
||||||
|
|
||||||
|
struct i2c_rdwr_ioctl_data data;
|
||||||
|
data.msgs = bufs;
|
||||||
|
data.nmsgs = 2;
|
||||||
|
|
||||||
|
if (ioctl (fd, I2C_RDWR, &data) < 0)
|
||||||
|
FAIL ("%s: %s", "ioctl", strerror (errno));
|
||||||
|
if (memcmp ("\x00\xFF\xFF\xFF\xFF\xFF\xFF\x00", buf, sizeof buf))
|
||||||
|
FAIL ("invalid EDID");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
is_a_display (int fd, struct error **e)
|
||||||
|
{
|
||||||
|
struct stat st;
|
||||||
|
if (fstat (fd, &st) < 0)
|
||||||
|
FAIL ("%s: %s", "fstat", strerror (errno));
|
||||||
|
|
||||||
|
unsigned long funcs;
|
||||||
|
if (!(st.st_mode & S_IFCHR)
|
||||||
|
|| ioctl (fd, I2C_FUNCS, &funcs) < 0
|
||||||
|
|| !(funcs & I2C_FUNC_I2C))
|
||||||
|
FAIL ("not an I2C device");
|
||||||
|
|
||||||
|
return check_edid (fd, e);
|
||||||
|
}
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
|
||||||
|
static bool
|
||||||
|
ddc_send (int fd, unsigned command, void *args, size_t args_len,
|
||||||
|
struct error **e)
|
||||||
|
{
|
||||||
|
struct str buf;
|
||||||
|
str_init (&buf);
|
||||||
|
str_pack_u8 (&buf, DDC_ADDRESS_HOST | I2C_READ);
|
||||||
|
str_pack_u8 (&buf, DDC_LENGTH_XOR | (args_len + 1));
|
||||||
|
str_pack_u8 (&buf, command);
|
||||||
|
str_append_data (&buf, args, args_len);
|
||||||
|
|
||||||
|
unsigned xor = DDC_ADDRESS_DISPLAY;
|
||||||
|
for (size_t i = 0; i < buf.len; i++)
|
||||||
|
xor ^= buf.str[i];
|
||||||
|
str_pack_u8 (&buf, xor);
|
||||||
|
|
||||||
|
struct i2c_msg msg =
|
||||||
|
{
|
||||||
|
// The driver unshifts it back
|
||||||
|
.addr = DDC_ADDRESS_DISPLAY >> 1, .flags = 0,
|
||||||
|
.len = buf.len, .buf = (uint8_t *) buf.str,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct i2c_rdwr_ioctl_data data;
|
||||||
|
data.msgs = &msg;
|
||||||
|
data.nmsgs = 1;
|
||||||
|
|
||||||
|
bool failed = ioctl (fd, I2C_RDWR, &data) < 0;
|
||||||
|
str_free (&buf);
|
||||||
|
if (failed)
|
||||||
|
FAIL ("%s: %s", "ioctl", strerror (errno));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
ddc_read (int fd, unsigned *command, void *out_buf, size_t *n_read,
|
||||||
|
struct error **e)
|
||||||
|
{
|
||||||
|
uint8_t buf[128] = "";
|
||||||
|
struct i2c_msg msg =
|
||||||
|
{
|
||||||
|
// The driver unshifts it back
|
||||||
|
.addr = DDC_ADDRESS_DISPLAY >> 1, .flags = I2C_M_RD,
|
||||||
|
.len = sizeof buf, .buf = buf,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct i2c_rdwr_ioctl_data data;
|
||||||
|
data.msgs = &msg;
|
||||||
|
data.nmsgs = 1;
|
||||||
|
|
||||||
|
if (ioctl (fd, I2C_RDWR, &data) < 0)
|
||||||
|
FAIL ("%s: %s", "ioctl", strerror (errno));
|
||||||
|
|
||||||
|
struct msg_unpacker unpacker;
|
||||||
|
msg_unpacker_init (&unpacker, buf, sizeof buf);
|
||||||
|
|
||||||
|
uint8_t sender, length, cmd;
|
||||||
|
(void) msg_unpacker_u8 (&unpacker, &sender);
|
||||||
|
(void) msg_unpacker_u8 (&unpacker, &length);
|
||||||
|
(void) msg_unpacker_u8 (&unpacker, &cmd);
|
||||||
|
|
||||||
|
if (sender != (DDC_ADDRESS_DISPLAY | I2C_WRITE) || !(length & 0x80))
|
||||||
|
FAIL ("invalid response");
|
||||||
|
if (!(length ^= 0x80))
|
||||||
|
FAIL ("NULL response");
|
||||||
|
|
||||||
|
// TODO: also check the checksum
|
||||||
|
|
||||||
|
*command = cmd;
|
||||||
|
memcpy (out_buf, unpacker.data + unpacker.offset, (*n_read = length - 1));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
|
||||||
|
static bool
|
||||||
|
set_brightness (int fd, long diff, struct error **e)
|
||||||
|
{
|
||||||
|
uint8_t get_req[] = { VCP_BRIGHTNESS };
|
||||||
|
if (!ddc_send (fd, DDC_GET_VCP_FEATURE, get_req, sizeof get_req, e))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
wait_ms (40);
|
||||||
|
|
||||||
|
unsigned command = 0;
|
||||||
|
uint8_t buf[128] = "";
|
||||||
|
size_t len = 0;
|
||||||
|
if (!ddc_read (fd, &command, buf, &len, e))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (command != DDC_GET_VCP_FEATURE_REPLY || len != 7)
|
||||||
|
FAIL ("invalid response");
|
||||||
|
|
||||||
|
struct msg_unpacker unpacker;
|
||||||
|
msg_unpacker_init (&unpacker, buf, len);
|
||||||
|
|
||||||
|
uint8_t result; msg_unpacker_u8 (&unpacker, &result);
|
||||||
|
uint8_t vcp_opcode; msg_unpacker_u8 (&unpacker, &vcp_opcode);
|
||||||
|
uint8_t type; msg_unpacker_u8 (&unpacker, &type);
|
||||||
|
int16_t max; msg_unpacker_i16 (&unpacker, &max);
|
||||||
|
int16_t cur; msg_unpacker_i16 (&unpacker, &cur);
|
||||||
|
|
||||||
|
if (result == 0x01)
|
||||||
|
FAIL ("error reported by monitor");
|
||||||
|
|
||||||
|
if (result != 0x00
|
||||||
|
|| vcp_opcode != VCP_BRIGHTNESS)
|
||||||
|
FAIL ("invalid response");
|
||||||
|
|
||||||
|
// These are unsigned but usually just one byte long
|
||||||
|
if (max < 0 || cur < 0)
|
||||||
|
FAIL ("capability range overflow");
|
||||||
|
|
||||||
|
int16_t req = (cur * 100 + diff * max + 50) / 100;
|
||||||
|
if (req > max) req = max;
|
||||||
|
if (req < 0) req = 0;
|
||||||
|
|
||||||
|
uint8_t set_req[] = { VCP_BRIGHTNESS, req >> 8, req };
|
||||||
|
if (!ddc_send (fd, DDC_SET_VCP_FEATURE, set_req, sizeof set_req, e))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
wait_ms (50);
|
||||||
|
|
||||||
|
printf ("brightness set to %.2f%%\n", 100. * req / max);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
i2c (long diff)
|
||||||
|
{
|
||||||
|
DIR *dev = opendir ("/dev");
|
||||||
|
if (!dev)
|
||||||
|
{
|
||||||
|
print_error ("cannot access %s: %s: %s",
|
||||||
|
"/dev", "opendir", strerror (errno));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct dirent *entry;
|
||||||
|
while ((entry = readdir (dev)))
|
||||||
|
{
|
||||||
|
if (strncmp (entry->d_name, "i2c-", 4))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
printf ("Trying %s... ", entry->d_name);
|
||||||
|
int fd = openat (dirfd (dev), entry->d_name, O_RDONLY);
|
||||||
|
if (fd < 0)
|
||||||
|
{
|
||||||
|
print_error ("%s: %s", "openat", strerror (errno));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct error *e = NULL;
|
||||||
|
if (!is_a_display (fd, &e)
|
||||||
|
|| !set_brightness (fd, diff, &e))
|
||||||
|
{
|
||||||
|
printf ("%s\n", e->message);
|
||||||
|
error_free (e);
|
||||||
|
}
|
||||||
|
|
||||||
|
close (fd);
|
||||||
|
}
|
||||||
|
closedir (dev);
|
||||||
|
}
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
|
||||||
|
static long
|
||||||
|
read_value (int dir, const char *filename, struct error **e)
|
||||||
|
{
|
||||||
|
int fd = openat (dir, filename, O_RDONLY);
|
||||||
|
if (fd < 0)
|
||||||
|
{
|
||||||
|
error_set (e, "%s: %s: %s", filename, "openat", strerror (errno));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
FILE *fp = fdopen (fd, "r");
|
||||||
|
if (!fp)
|
||||||
|
{
|
||||||
|
error_set (e, "%s: %s: %s", filename, "fdopen", strerror (errno));
|
||||||
|
close (fd);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct str s;
|
||||||
|
str_init (&s);
|
||||||
|
|
||||||
|
long value;
|
||||||
|
if (!read_line (fp, &s)
|
||||||
|
|| !xstrtol (s.str, &value))
|
||||||
|
{
|
||||||
|
value = -1;
|
||||||
|
error_set (e, "%s: %s", filename, "failed reading an integer value");
|
||||||
|
}
|
||||||
|
|
||||||
|
str_free (&s);
|
||||||
|
fclose (fp);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
set_backlight (int dir, long diff, struct error **e)
|
||||||
|
{
|
||||||
|
long cur, max;
|
||||||
|
struct error *error = NULL;
|
||||||
|
if ((cur = read_value (dir, "brightness", &error), error)
|
||||||
|
|| (max = read_value (dir, "max_brightness", &error), error))
|
||||||
|
{
|
||||||
|
error_propagate (e, error);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cur < 0 || max < 0)
|
||||||
|
{
|
||||||
|
error_set (e, "invalid range or current value");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
long req = (cur * 100 + diff * max + 50) / 100;
|
||||||
|
if (req > max) req = max;
|
||||||
|
if (req < 0) req = 0;
|
||||||
|
|
||||||
|
int fd = openat (dir, "brightness", O_WRONLY);
|
||||||
|
if (fd < 0)
|
||||||
|
{
|
||||||
|
error_set (e, "%s: %s: %s", "brightness", "openat", strerror (errno));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct str s;
|
||||||
|
str_init (&s);
|
||||||
|
str_append_printf (&s, "%ld", req);
|
||||||
|
bool result = write (fd, s.str, s.len) == s.len;
|
||||||
|
str_free (&s);
|
||||||
|
|
||||||
|
if (!result)
|
||||||
|
error_set (e, "%s: %s: %s", "brightness", "write", strerror (errno));
|
||||||
|
|
||||||
|
close (fd);
|
||||||
|
printf ("brightness set to %.2f%%\n", 100. * req / max);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
backlight (long diff)
|
||||||
|
{
|
||||||
|
DIR *backlight = opendir ("/sys/class/backlight");
|
||||||
|
if (!backlight)
|
||||||
|
{
|
||||||
|
print_error ("cannot access %s: %s: %s",
|
||||||
|
"/sys/class/backlight", "opendir", strerror (errno));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct dirent *entry;
|
||||||
|
while ((entry = readdir (backlight)))
|
||||||
|
{
|
||||||
|
const char *device_name = entry->d_name;
|
||||||
|
if (device_name[0] == '.')
|
||||||
|
continue;
|
||||||
|
|
||||||
|
printf ("Trying %s... ", entry->d_name);
|
||||||
|
int dir = openat (dirfd (backlight), entry->d_name, O_RDONLY);
|
||||||
|
if (dir < 0)
|
||||||
|
{
|
||||||
|
print_error ("%s: %s", "openat", strerror (errno));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct error *e = NULL;
|
||||||
|
if (!set_backlight (dir, diff, &e))
|
||||||
|
{
|
||||||
|
printf ("%s\n", e->message);
|
||||||
|
error_free (e);
|
||||||
|
}
|
||||||
|
|
||||||
|
close (dir);
|
||||||
|
}
|
||||||
|
closedir (backlight);
|
||||||
|
}
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
|
||||||
|
int
|
||||||
|
main (int argc, char *argv[])
|
||||||
|
{
|
||||||
|
g_log_message_real = log_message_custom;
|
||||||
|
|
||||||
|
long diff = 0;
|
||||||
|
if (argc > 1 && !xstrtol (argv[1], &diff))
|
||||||
|
{
|
||||||
|
printf ("Usage: %s <percentage diff>\n", argv[0]);
|
||||||
|
exit (EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
i2c (diff);
|
||||||
|
backlight (diff);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
#ifndef CONFIG_H
|
||||||
|
#define CONFIG_H
|
||||||
|
|
||||||
|
#define PROGRAM_NAME "${PROJECT_NAME}"
|
||||||
|
#define PROGRAM_VERSION "${project_VERSION}"
|
||||||
|
|
||||||
|
#endif // ! CONFIG_H
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1 @@
|
||||||
|
Subproject commit 9e3cb2b6aa2db3ca0ea6a854fb3f89a163c84235
|
Loading…
Reference in New Issue