summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore2
-rw-r--r--Makefile8
-rw-r--r--autorotate.c207
3 files changed, 217 insertions, 0 deletions
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 <jkk@ung.org>
+ *
+ * *VERY* loosely based on https://gitlab.freedesktop.org/hadess/iio-sensor-proxy/-/raw/master/src/monitor-sensor.c
+ *
+ */
+
+#define _XOPEN_SOURCE 700
+
+#include <errno.h>
+#include <fcntl.h>
+#include <glob.h>
+#include <libgen.h>
+#include <limits.h>
+#include <locale.h>
+#include <math.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <unistd.h>
+
+#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 <device#>\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);
+ }
+}