Newer
Older
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
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
#define _POSIX_C_SOURCE 200809L
#include <fcntl.h>
#include <linux/wireless.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/errno.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <unistd.h>
#include <mpd/client.h>
#include <systemd/sd-daemon.h>
#include <systemd/sd-event.h>
#define LCD_DEV "/dev/lcd"
#define LCD_WIDTH 20
#define LCD_HEIGHT 4
#define WIFI_DEV "wlan0"
#define WIFI_POLL_SEC 2
struct display_data {
int wifi_strength;
int wifi_strength_last;
char playing;
const char *name;
const char *title;
};
struct handler_data {
int lcd_fd;
int sock_fd;
struct display_data *disp_data;
struct mpd_connection *conn;
};
void display_song(int fd, const struct display_data *data)
{
// rewrite from the top
dprintf(fd, "\033[Lx000y000;");
if (data->playing) {
dprintf(fd, "%.*s\033[Lk\n", LCD_WIDTH, data->name);
dprintf(fd, "%.*s\033[Lk\n", LCD_WIDTH, data->title);
// If the title is longer than our display, contine in the second line;
// if not then fill it with a blank line
if (strnlen(data->title, 21) > LCD_WIDTH) {
dprintf(fd, "%.*s\033[Lk\n", LCD_WIDTH, data->title + 20);
} else {
dprintf(fd, "\033[Lk\n");
}
} else {
dprintf(fd, "\033[Lk\n Keine Wiedergabe\033[Lk\n\033[Lk");
}
}
void display_wifi(int fd, struct display_data *data)
{
// don't redraw anything if nothing changed
if (data->wifi_strength == data->wifi_strength_last) {
return;
}
// rewrite from the bottom right
dprintf(fd, "\033[Lx017y003;");
if (data->wifi_strength < 1) {
dprintf(fd, " ");
}
else if (data->wifi_strength < 2) {
dprintf(fd, "\x01 ");
}
else if (data->wifi_strength < 3) {
dprintf(fd, "\x01\x02 ");
}
else {
dprintf(fd, "\x01\x02\x03");
}
data->wifi_strength_last = data->wifi_strength;
}
void init_display(int fd)
{
// clear everything
dprintf(fd, "\f");
// init the custom characters
dprintf(fd, "\033[LG4000e11040a000400;\033[LG10000000000ffff00;"
"\033[LG2000000ffffffff00;\033[LG300ffffffffffff00;");
// draw the wifi logo
dprintf(fd, "\033[Lx016y003;\x04");
}
int wifi_bars_from_dbm(int dbm)
{
if (dbm < -70) {
return 1;
} else if (dbm < -67) {
return 2;
} else {
return 3;
}
}
void update_wifi(int sock_fd, struct display_data *data)
{
struct iwreq req;
int r;
strcpy(req.ifr_name, WIFI_DEV);
req.u.data.pointer = (struct iw_statistics *) malloc(sizeof(struct iw_statistics));
req.u.data.length = sizeof(struct iw_statistics);
r = ioctl(sock_fd, SIOCGIWSTATS, &req);
if (r == -1) {
if (errno == ENOTSUP) { // returned when link is down -> disconnected
data->wifi_strength = 0;
} else {
perror("ioctl() failed");
}
return;
}
if (((struct iw_statistics *) req.u.data.pointer)->qual.updated & IW_QUAL_DBM) {
data->wifi_strength = wifi_bars_from_dbm(
((struct iw_statistics *) req.u.data.pointer)->qual.level - 256);
} else {
fprintf(stderr, "Warning: Could not get valid signal strength data\n");
}
}
void update_song(struct mpd_connection *conn, struct display_data *data)
{
enum mpd_error err;
struct mpd_status *status;
enum mpd_state state;
struct mpd_song *song;
status = mpd_run_status(conn);
if (status == NULL) {
err = mpd_connection_get_error(conn);
fprintf(stderr, "Warning: Could not get player status: %s\n",
mpd_connection_get_error_message(conn));
return;
}
state = mpd_status_get_state(status);
switch (state) {
case MPD_STATE_UNKNOWN:
fprintf(stderr, "Warning: MPD has unknown state!\n");
__attribute__((fallthrough));
case MPD_STATE_STOP:
case MPD_STATE_PAUSE:
data->playing = 0;
return;
case MPD_STATE_PLAY:
data->playing = 1;
break;
}
song = mpd_run_current_song(conn);
err = mpd_connection_get_error(conn);
if (err != MPD_ERROR_SUCCESS) {
fprintf(stderr, "Warning: Could not get current song data: %s\n",
mpd_connection_get_error_message(conn));
}
if (song == NULL) {
fprintf(stderr, "Warning: Current song is NULL!\n");
return;
}
data->name = mpd_song_get_tag(song, MPD_TAG_NAME, 0);
data->title = mpd_song_get_tag(song, MPD_TAG_TITLE, 0);
if (data->name == NULL) {
data->name = "";
}
if (data->title == NULL) {
data->title = "";
}
}
static int mpd_handler(sd_event_source *es, int fd, uint32_t revents, void *userdata)
{
(void) es;
(void) fd;
(void) revents;
struct handler_data *data = (struct handler_data *) userdata;
struct display_data *disp_data = data->disp_data;
struct mpd_connection *conn = data->conn;
int lcd_fd = data->lcd_fd;
enum mpd_idle event_types;
event_types = mpd_recv_idle(conn, 1);
fprintf(stderr, "Event: %x\n", event_types);
update_song(conn, disp_data);
display_song(lcd_fd, disp_data);
// TODO: error checking
mpd_send_idle_mask(conn, MPD_IDLE_PLAYER);
return 0;
}
static int timer_handler(sd_event_source *es, uint64_t usec, void *userdata)
{
struct handler_data *data = (struct handler_data *) userdata;
struct display_data *disp_data = data->disp_data;
int lcd_fd = data->lcd_fd;
int sock_fd = data->sock_fd;
int r = 0;
update_wifi(sock_fd, disp_data);
display_wifi(lcd_fd, disp_data);
r = sd_event_source_set_time(es, usec + (WIFI_POLL_SEC * 1000000));
if (r < 0) {
goto finish;
}
r = sd_event_source_set_enabled(es, SD_EVENT_ONESHOT);
if (r < 0) {
goto finish;
}
finish:
return r;
}
int main(void)
{
int r;
struct mpd_connection *mpd_conn = NULL;
int mpd_fd = -1;
sd_event_source *mpd_es = NULL;
sd_event_source *timer_es = NULL;
sd_event *event = NULL;
sigset_t ss;
int lcd_fd = -1;
struct display_data data;
struct handler_data userdata;
int sock_fd;
r = sd_event_default(&event);
if (r < 0) {
goto finish;
}
if (sigemptyset(&ss) < 0 ||
sigaddset(&ss, SIGTERM) < 0 ||
sigaddset(&ss, SIGINT) < 0) {
r = -errno;
goto finish;
}
/* Block SIGTERM first, so that the event loop can handle it */
if (sigprocmask(SIG_BLOCK, &ss, NULL) < 0) {
r = -errno;
goto finish;
}
/* Let's make use of the default handler and "floating" reference features of sd_event_add_signal() */
r = sd_event_add_signal(event, NULL, SIGTERM, NULL, NULL);
if (r < 0) {
goto finish;
}
r = sd_event_add_signal(event, NULL, SIGINT, NULL, NULL);
if (r < 0) {
goto finish;
}
mpd_conn = mpd_connection_new(NULL, 0, 0);
if (mpd_conn == NULL) {
r = -ENOMEM;
goto finish;
}
if (mpd_connection_get_error(mpd_conn) != MPD_ERROR_SUCCESS) {
fprintf(stderr, "Error getting MPD connection: %s\n",
mpd_connection_get_error_message(mpd_conn));
r = -EIO;
goto finish;
}
lcd_fd = open(LCD_DEV, O_WRONLY);
if (lcd_fd < 0) {
r = -errno;
goto finish;
}
update_song(mpd_conn, &data);
init_display(lcd_fd);
display_song(lcd_fd, &data);
if (!mpd_send_idle_mask(mpd_conn, MPD_IDLE_PLAYER)) {
r = -255;
goto finish;
}
mpd_fd = mpd_connection_get_fd(mpd_conn);
userdata.disp_data = &data;
userdata.conn = mpd_conn;
userdata.lcd_fd = lcd_fd;
r = sd_event_add_io(event, &mpd_es, mpd_fd, EPOLLIN, mpd_handler,
(void *) &userdata);
if (r < 0) {
goto finish;
}
sock_fd = socket(AF_INET, SOCK_DGRAM, 0);
if (sock_fd <= 0) {
perror("Could not open AF_INET socket for wifi stats");
}
userdata.sock_fd = sock_fd;
r = sd_event_add_time(event, &timer_es, CLOCK_MONOTONIC, 0, 1000000,
timer_handler, (void *) &userdata);
if (r < 0) {
goto finish;
}
r = sd_event_loop(event);
finish:
mpd_es = sd_event_source_unref(mpd_es);
timer_es = sd_event_source_unref(timer_es);
event = sd_event_unref(event);
mpd_connection_free(mpd_conn);
if (lcd_fd >= 0) {
(void) close(lcd_fd);
}
if (mpd_fd >= 0) {
(void) close(mpd_fd);
}
if (r < 0) {
fprintf(stderr, "Failure: %s\n", strerror(-r));
}
return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
}