From 91c82a13d4da6b140b0e9e6ef6fe3130bd9794fe Mon Sep 17 00:00:00 2001 From: Jakob Kaivo Date: Fri, 20 Mar 2026 11:23:37 -0700 Subject: initial commit --- .gitignore | 2 + Makefile | 8 +++ autorotate.c | 207 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 217 insertions(+) create mode 100644 .gitignore create mode 100644 Makefile create mode 100644 autorotate.c diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..582d779 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +autorotate +*.o diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..18b0f42 --- /dev/null +++ b/Makefile @@ -0,0 +1,8 @@ +.POSIX: + +CFLAGS=-Wall -Werror +LDFLAGS=-lm +SOURCE=autorotate.c + +autorotate: $(SOURCE) + $(CC) $(CFLAGS) -o $@ $(SOURCE) $(LDFLAGS) diff --git a/autorotate.c b/autorotate.c new file mode 100644 index 0000000..66023ce --- /dev/null +++ b/autorotate.c @@ -0,0 +1,207 @@ +/* + * 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); + } +} -- cgit v1.2.3