/* * Copyright (c) 2024-2026 Jakob Kaivo * * *VERY* loosely based on https://gitlab.freedesktop.org/hadess/iio-sensor-proxy/-/raw/master/src/monitor-sensor.c * */ #define _XOPEN_SOURCE 700 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifndef O_SEARCH #define O_SEARCH 0 #endif #define DEFAULT_OUTPUT "eDP-1" #define SWAY_MAGIC "i3-ipc" #define SWAY_RUN_COMMAND 0 #define RADIANS_TO_DEGREES (180.0/M_PI) #define THRESHOLD (35) typedef enum { NORMAL = 0, RIGHT = 1, INVERT = 2, LEFT = 3, } orientation; static char *angles[] = { "0", "90", "180", "270" }; static void rotate(int sway_socket, const char *out, orientation angle) { char buf[128]; uint32_t msg_type = SWAY_RUN_COMMAND; uint32_t msg_length = snprintf(buf, sizeof(buf), "output %s transform %s", out, angles[angle]); write(sway_socket, SWAY_MAGIC, strlen(SWAY_MAGIC)); write(sway_socket, &msg_length, sizeof(msg_length)); write(sway_socket, &msg_type, sizeof(msg_type)); write(sway_socket, buf, msg_length); /* read and discard the response */ /* it will mostly be: [ { "success": true } ] */ read(sway_socket, buf, strlen(SWAY_MAGIC)); read(sway_socket, &msg_length, sizeof(msg_length)); read(sway_socket, &msg_type, sizeof(msg_type)); while (msg_length != 0) { size_t toread = msg_length > sizeof(buf) ? sizeof(buf) : msg_length; ssize_t nread = read(sway_socket, buf, toread); msg_length -= nread; } } static int find_accel(char *devno) { if (devno) { char path[PATH_MAX] = ""; snprintf(path, sizeof(path), "/sys/bus/iio/devices/iio:device%s", devno); return open(path, O_DIRECTORY | O_SEARCH); } int dirfd = -1; glob_t gl = { 0 }; glob("/sys/bus/iio/devices/iio:device*/in_accel_x_raw", 0, NULL, &gl); if (gl.gl_pathc == 1) { dirfd = open(dirname(gl.gl_pathv[0]), O_DIRECTORY | O_SEARCH); if (dirfd == -1) { perror("opening device directory"); exit(1); } } else if (gl.gl_pathc == 0) { fprintf(stderr, "no IIO accelerometers found\n"); exit(1); } else { fprintf(stderr, "multiple IIO accelerometers found:\n"); for (size_t i = 0; i < gl.gl_pathc; i++) { fprintf(stderr, "%s\n", basename(dirname(gl.gl_pathv[i]))); } /* TODO: implement this */ fprintf(stderr, "select one with -a \n"); exit(1); } globfree(&gl); return dirfd; } static double read_val(int dirfd, const char *path) { int fd = openat(dirfd, path, O_RDONLY); char buf[32] = {0}; read(fd, buf, sizeof(buf)); double ret = 0; sscanf(buf, "%lg", &ret); close(fd); return ret; } int open_swaysock(void) { char *swaysock = getenv("SWAYSOCK"); if (swaysock == NULL) { fprintf(stderr, "SWAYSOCK not set\n"); exit(1); } int sway_socket = socket(AF_UNIX, SOCK_STREAM, 0); if (sway_socket == -1) { perror("socket()"); exit(1); } struct sockaddr_un sun = { .sun_family = AF_UNIX, }; /* This is it - the one time it's appropriate to use str*n*cpy */ strncpy(sun.sun_path, swaysock, sizeof(sun.sun_path)); if (connect(sway_socket, (struct sockaddr *)&sun, sizeof(sun)) == -1) { perror("connect()"); exit(1); } return sway_socket; } int main(int argc, char *argv[]) { char *output = DEFAULT_OUTPUT; char *accel = NULL; setlocale(LC_ALL, ""); int c; while ((c = getopt(argc, argv, "a:o:l:n:r:u:i:")) != -1) { switch (c) { case 'a': accel = optarg; break; case 'o': output = optarg; break; case 'n': angles[NORMAL] = optarg; break; case 'r': angles[RIGHT] = optarg; break; case 'i': angles[INVERT] = optarg; break; case 'l': angles[LEFT] = optarg; break; default: return 1; } } int devdir = find_accel(accel); int sway_socket = open_swaysock(); for (;;) { double x_raw = read_val(devdir, "in_accel_x_raw"); double y_raw = read_val(devdir, "in_accel_y_raw"); double z_raw = read_val(devdir, "in_accel_z_raw"); double scale = read_val(devdir, "scale"); int x = (int)(x_raw * scale); int y = (int)(y_raw * scale); int z = (int)(z_raw * scale); int portrait = round(atan2(x, sqrt(x * y + z * z)) * RADIANS_TO_DEGREES); int landscape = round(atan2(y, sqrt(x * x + z * z)) * RADIANS_TO_DEGREES); if (abs(portrait) > THRESHOLD) { rotate(sway_socket, output, portrait < 0 ? LEFT : RIGHT); } else if (abs(landscape) > THRESHOLD) { rotate(sway_socket, output, landscape < 0 ? INVERT : NORMAL); } sleep(1); } }