summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--Makefile8
-rw-r--r--brightness.c76
-rw-r--r--brightness.cpp236
4 files changed, 241 insertions, 80 deletions
diff --git a/.gitignore b/.gitignore
index 72eb06f..71ba227 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1,2 @@
+.ccls-cache/
brightness
diff --git a/Makefile b/Makefile
index 8317a0b..8584d48 100644
--- a/Makefile
+++ b/Makefile
@@ -1,5 +1,5 @@
-CC = gcc
-CFLAGS = -Wall -Wextra -O2 -std=c11 -fwrapv
+CXX = g++
+CXXFLAGS = -Wall -Wextra -O2 -std=c++11
INSTALL_DIR = $(HOME)/prefix/bin
BIN = brightness
@@ -16,5 +16,5 @@ install:
sudo chmod +s $(INSTALL_DIR)/brightness
-$(BIN): brightness.c $(wildcard *.h)
- $(CC) $(CFLAGS) $(filter %.c,$^) -o $@
+$(BIN): brightness.cpp $(wildcard *.h)
+ $(CXX) $(CXXFLAGS) $(filter %.cpp,$^) -o $@
diff --git a/brightness.c b/brightness.c
deleted file mode 100644
index feee058..0000000
--- a/brightness.c
+++ /dev/null
@@ -1,76 +0,0 @@
-// By Tom Smeding (2016)
-// modified for new laptop (2017)
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <stdarg.h>
-#include <string.h>
-#include <unistd.h>
-#include <sys/types.h>
-
-#define INCREMENT 100
-
-__attribute__((noreturn))
-__attribute__((format (printf, 1, 2)))
-void die(const char *format,...){
- va_list ap;
- va_start(ap,format);
- vprintf(format,ap);
- va_end(ap);
- putchar('\n');
- exit(1);
-}
-
-int get_max_brightness(void){
- FILE *f=fopen("/sys/class/backlight/intel_backlight/max_brightness","r");
- if(!f)die("Could not open max_brightness file");
- int n;
- fscanf(f,"%d",&n);
- fclose(f);
- return n;
-}
-
-int get_brightness(void){
- FILE *f=fopen("/sys/class/backlight/intel_backlight/brightness","r");
- if(!f)die("Could not open brightness file");
- int n;
- fscanf(f,"%d",&n);
- fclose(f);
- return n;
-}
-
-void set_brightness(int n){
- FILE *f=fopen("/sys/class/backlight/intel_backlight/brightness","w");
- if(!f)die("Could not open brightness file");
- fprintf(f,"%d",n);
- fflush(f);
- fclose(f);
-}
-
-int main(int argc,char **argv){
- uid_t uid=geteuid();
- if(uid!=0){
- printf("brightness will only work with root.\n");
- return 1;
- }
-
- if(argc==1){
- printf("max: %d\n",get_max_brightness());
- printf("current: %d\n",get_brightness());
- } else if(argc==2){
- int n;
- if(strcmp(argv[1],"-")==0){
- n=get_brightness()-INCREMENT;
- if(n<0)n=0;
- } else if(strcmp(argv[1],"+")==0){
- n=get_brightness()+INCREMENT;
- int mx=get_max_brightness();
- if(n>mx)n=mx;
- } else {
- char *endp;
- n=strtol(argv[1],&endp,10);
- if(argv[1][0]=='\0'||*endp!='\0')die("Invalid number '%s'",argv[1]);
- }
- set_brightness(n);
- }
-}
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));
+ }
+ }
+}