1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
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);
}
}
|