summaryrefslogtreecommitdiff
path: root/brightness.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'brightness.cpp')
-rw-r--r--brightness.cpp236
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));
+ }
+ }
+}