-
Notifications
You must be signed in to change notification settings - Fork 0
/
ds4batlevel.c
177 lines (148 loc) · 3.6 KB
/
ds4batlevel.c
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
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <dirent.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <time.h>
#include <libnotify/notify.h>
#include "config.h"
enum Urgency {
/* >15% */
DS4_HEALTHY,
/* 15% */
DS4_LOW,
/* 10% */
DS4_LOWER,
/* 5% */
DS4_DEPLETED,
};
static char *argv0;
static int ds4_fd = -1;
static void init(int argc, char *argv[]);
static void cleanup(void);
static void handlesig(int signo);
static void notify(enum Urgency u);
static const char *get_urgency_str(enum Urgency u);
static int get_urgency_timeout(enum Urgency u);
static int read_battery_level(void);
static enum Urgency get_battery_urgency(void);
/* max wait time between polls */
const struct timespec wait_time = {
.tv_sec = 60,
.tv_nsec = 0,
};
int main(int argc, char *argv[])
{
init(argc, argv);
for (;;) {
const enum Urgency u = get_battery_urgency();
if (u != DS4_HEALTHY) {
notify(u);
}
nanosleep(&wait_time, NULL);
}
}
static void notify(enum Urgency u)
{
NotifyNotification *n = notify_notification_new(
"Dual Shock 4",
get_urgency_str(u),
"input-gamepad"
);
GError *err = NULL;
notify_notification_set_timeout(n, get_urgency_timeout(u));
notify_notification_set_urgency(n, NOTIFY_URGENCY_CRITICAL);
if (!notify_notification_show(n, &err)) {
/* well... what now?
* abort, I suppose. */
fprintf(stderr, "%s: failed to show notification to the user: %s\n", argv0, err->message);
g_error_free(err);
exit(1);
}
}
static const char *get_urgency_str(enum Urgency u)
{
switch (u) {
case DS4_LOW:
return "Battery is low (10%-15%).";
case DS4_LOWER:
return "Battery is very low (5%-10%)!";
case DS4_DEPLETED:
return "Battery is almost depleted (<5%)!";
}
}
static int get_urgency_timeout(enum Urgency u)
{
#define SECS(N) ((N) * 1000)
switch (u) {
case DS4_LOW:
return SECS(5);
case DS4_LOWER:
case DS4_DEPLETED:
return SECS(10);
}
}
static void handlesig(int signo)
{
switch (signo) {
case SIGINT:
case SIGHUP:
case SIGTERM:
case SIGQUIT:
exit(0);
}
}
static void init(int argc, char *argv[])
{
(void)argc;
argv0 = argv[0];
if (!notify_init("ds4batlevel")) {
fprintf(stderr, "%s: failed to initialize notifications\n", argv0);
exit(1);
}
atexit(cleanup);
ds4_fd = open(DS4_PATH, 0400, O_RDONLY);
if (ds4_fd == -1) {
fprintf(stderr, "%s: failed to open dual shock 4 path: %s\n", argv0, DS4_PATH);
exit(1);
}
/* install signal handlers; ignore return value for now lol */
signal(SIGINT, handlesig);
signal(SIGHUP, handlesig);
signal(SIGTERM, handlesig);
signal(SIGQUIT, handlesig);
}
static void cleanup(void)
{
notify_uninit();
if (ds4_fd != -1) {
close(ds4_fd);
}
}
static int read_battery_level(void)
{
static char buf[8];
const ssize_t n = read(ds4_fd, buf, sizeof(buf));
if (n == -1) {
fprintf(stderr, "%s: failed to read battery level: %s\n", argv0, strerror(errno));
exit(1);
}
lseek(ds4_fd, 0, SEEK_SET);
buf[n] = '\0';
return atoi(buf);
}
static enum Urgency get_battery_urgency(void)
{
const int level = read_battery_level();
if (level >= 15) {
return DS4_HEALTHY;
} else if (level >= 10 && level < 15) {
return DS4_LOW;
} else if (level >= 5 && level < 10) {
return DS4_LOWER;
} else {
return DS4_DEPLETED;
}
}