diff options
Diffstat (limited to 'brightness.cpp')
-rw-r--r-- | brightness.cpp | 236 |
1 files changed, 236 insertions, 0 deletions
diff --git a/brightness.cpp b/brightness.cpp new file mode 100644 index 0000000..c06427d --- /dev/null +++ b/brightness.cpp @@ -0,0 +1,236 @@ +// By Tom Smeding (2016) +// modified for new laptop (2017) +// modified for ddcutil support (2022) + +#include <iostream> +#include <fstream> +#include <sstream> +#include <string> +#include <vector> +#include <cstdlib> +#include <cstdarg> +#include <cstring> +#include <unistd.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/wait.h> + +std::string read_process(const char *procname, const std::vector<const char*> &args) { + int pi[2]; + int ret = pipe(pi); + if (ret != 0) { + perror("pipe"); + exit(1); + } + + pid_t pid = fork(); + if (pid < 0) { + perror("fork"); + exit(1); + } + if (pid == 0) { + dup2(pi[1], 1); + close(pi[0]); + close(pi[1]); + char **argv = new char*[args.size() + 2]; + argv[0] = strdup(procname); + for (size_t i = 0; i < args.size(); i++) { + argv[i + 1] = strdup(args[i]); + } + argv[args.size() + 1] = nullptr; + fprintf(stderr, "procname = <%s>\n", procname); + for (size_t i = 0; i < args.size() + 2; i++) { + fprintf(stderr, "argv[%zu] = <%s>\n", i, argv[i]); + } + execvp(procname, argv); + perror("execvp"); + exit(1); + } + + close(pi[1]); + std::string output; + while (true) { + output.resize(output.size() + 1024); + ssize_t nr = read(pi[0], &output[output.size() - 1024], 1024); + if (nr < 0) { + if (errno == EINTR) continue; + perror("read"); + exit(1); + } + if (nr == 0) break; + output.resize(output.size() - 1024 + nr); + } + close(pi[0]); + + while (true) { + int status; + int ret = waitpid(pid, &status, 0); + if (ret < 0) { + if (errno == EINTR) continue; + perror("waitpid"); + exit(1); + } + if (WIFEXITED(status)) break; + } + + return output; +} + +struct Backlight { + virtual ~Backlight() {} + + virtual int increment() const = 0; + + virtual bool suitable() = 0; + virtual int get_max_brightness() = 0; + virtual int get_brightness() = 0; + virtual void set_brightness(int n) = 0; + virtual void add_brightness(int n) { + n = get_brightness() + n; + if (n < 0) n = 0; + int maxval = get_max_brightness(); + if (n > maxval) n = maxval; + set_brightness(n); + } + virtual int convert_absolute_brightness(int n) = 0; // to percentage +}; + +struct IntelBacklight : public Backlight { +private: + int read_int_file(const char *fname) { + std::ifstream f{fname}; + if (!f) { + std::cerr << "Could not open brightness file" << std::endl; + exit(1); + } + int n; + f >> n; + return n; + } + +public: + int increment() const { return 100; } + + bool suitable() { + struct stat st; + int ret = stat("/sys/class/backlight/intel_backlight", &st); + return ret == 0; + } + + int get_max_brightness() { + return read_int_file("/sys/class/backlight/intel_backlight/max_brightness"); + } + + int get_brightness() { + return read_int_file("/sys/class/backlight/intel_backlight/brightness"); + } + + void set_brightness(int n) { + std::ofstream f{"/sys/class/backlight/intel_backlight/brightness"}; + if (!f) { + std::cerr << "Could not open brightness file" << std::endl; + exit(1); + } + f << n; + } + + int convert_absolute_brightness(int n) { + return n * 10; + } +}; + +struct DDCBacklight : public Backlight { +private: + bool have_read = false; + int brvalue, brmaxvalue; + + void read_current() { + std::string output = read_process("ddcutil", {"getvcp", "-t", "10"}); + std::istringstream ss{output}; + std::string word; + ss >> word; + if (word != "VCP") goto format_err; + ss >> word; + if (word != "10") goto format_err; + ss >> word; + if (word != "C") goto format_err; + ss >> brvalue; + ss >> brmaxvalue; + if (!ss) goto format_err; + have_read = true; + return; + + format_err: + std::cerr << "Invalid format from ddcutil" << std::endl; + exit(1); + } + +public: + int increment() const { return 10; } + + bool suitable() { + struct stat st; + int ret = stat("/usr/bin/ddcutil", &st); + return ret == 0; + } + + int get_brightness() { + if (!have_read) read_current(); + return brvalue; + } + + int get_max_brightness() { + if (!have_read) read_current(); + return brmaxvalue; + } + + void set_brightness(int n) { + std::string argstr = std::to_string(n); + read_process("ddcutil", {"setvcp", "10", argstr.data()}); + } + + void add_brightness(int n) { + if (n == 0) return; + std::string absargstr = std::to_string(abs(n)); + read_process("ddcutil", {"setvcp", "10", n > 0 ? "+" : "-", absargstr.data()}); + } + + int convert_absolute_brightness(int n) { + return n; + } +}; + +int main(int argc,char **argv){ + uid_t uid = geteuid(); + if (uid != 0) { + std::cerr << "brightness will only work with root." << std::endl; + return 1; + } + + std::vector<Backlight*> instances{new IntelBacklight{}, new DDCBacklight{}}; + Backlight *backlight = nullptr; + for (Backlight *b : instances) if (b->suitable()) { backlight = b; break; } + if (!backlight) { + std::cerr << "No idea how to control your monitor, sorry" << std::endl; + return 1; + } + + if (argc == 1) { + std::cout << "max: " << backlight->get_max_brightness() << std::endl; + std::cout << "current: " << backlight->get_brightness() << std::endl; + } else if (argc == 2) { + if (strcmp(argv[1], "-") == 0) { + backlight->add_brightness(-backlight->increment()); + } else if (strcmp(argv[1], "+") == 0) { + backlight->add_brightness(backlight->increment()); + } else { + char *endp; + int n = strtol(argv[1], &endp, 10); + if (argv[1][0] == '\0' || *endp != '\0') { + std::cerr << "Invalid number '" << argv[1] << "'" << std::endl; + } + backlight->set_brightness(backlight->convert_absolute_brightness(n)); + } + } +} |