// By Tom Smeding (2016) // modified for new laptop (2017) // modified for ddcutil support (2022) #include #include #include #include #include #include #include #include #include #include #include #include #include std::string read_process(const char *procname, const std::vector &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 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)); } } }