slstatus.c (3578B) download
1/* See LICENSE file for copyright and license details. */
2#include <errno.h>
3#include <fcntl.h>
4#include <signal.h>
5#include <stdio.h>
6#include <stdlib.h>
7#include <string.h>
8#include <time.h>
9#include <unistd.h>
10#include <poll.h>
11#include <X11/Xlib.h>
12
13#include "arg.h"
14#include "slstatus.h"
15#include "util.h"
16
17struct arg {
18 const char *(*func)(const char *);
19 int (*doenable)(const char *);
20 const char *fmt;
21 const char *args;
22};
23
24char buf[1024];
25static volatile sig_atomic_t done;
26static Display *dpy;
27
28int always(const char *unused) {
29 (void)unused;
30 return 1;
31}
32
33int never(const char *unused) {
34 (void)unused;
35 return 0;
36}
37
38#include "config.h"
39
40static void
41terminate(const int signo)
42{
43 if (signo != SIGUSR1)
44 done = 1;
45}
46
47static void
48difftimespec(struct timespec *res, struct timespec *a, struct timespec *b)
49{
50 res->tv_sec = a->tv_sec - b->tv_sec - (a->tv_nsec < b->tv_nsec);
51 res->tv_nsec = a->tv_nsec - b->tv_nsec +
52 (a->tv_nsec < b->tv_nsec) * 1E9;
53}
54
55static int
56getnotify(int fifo, char* buffer, size_t size)
57{
58 struct pollfd fds = { fifo, POLLIN, 0 };
59
60 if (poll(&fds, 1, 0) <= 0)
61 return 0;
62
63 int len = 0;
64 while (read(fifo, buffer + len, 1) > 0 && buffer[len] != '\n' && len < size)
65 len++;
66 buffer[len] = '\0';
67 return len;
68}
69
70static void
71usage(void)
72{
73 die("usage: %s [-v] [-s] [-1] [-p fifo]", argv0);
74}
75
76int
77main(int argc, char *argv[])
78{
79 struct sigaction act;
80 struct timespec start, current, diff, intspec, wait;
81 size_t i, len;
82 int sflag, ret;
83 int fifo, msgcycle = 0;
84 char status[MAXLEN];
85 const char *res;
86 const char *fifopath = NULL;
87 char *nl;
88
89 sflag = 0;
90 ARGBEGIN {
91 case 'v':
92 die("slstatus-"VERSION);
93 case '1':
94 done = 1;
95 /* FALLTHROUGH */
96 case 's':
97 sflag = 1;
98 break;
99 case 'p':
100 fifopath = EARGF(usage());
101 break;
102 default:
103 usage();
104 } ARGEND
105
106 if (argc)
107 usage();
108
109 memset(&act, 0, sizeof(act));
110 act.sa_handler = terminate;
111 sigaction(SIGINT, &act, NULL);
112 sigaction(SIGTERM, &act, NULL);
113 act.sa_flags |= SA_RESTART;
114 sigaction(SIGUSR1, &act, NULL);
115
116 if (fifopath && (fifo = open(fifopath, O_RDONLY)) == -1)
117 die("open(fifo): ");
118
119 if (!sflag && !(dpy = XOpenDisplay(NULL)))
120 die("XOpenDisplay: Failed to open display");
121
122 do {
123 if (clock_gettime(CLOCK_MONOTONIC, &start) < 0)
124 die("clock_gettime:");
125
126 if (msgcycle > 0) {
127 msgcycle--;
128 } else if (fifopath && getnotify(fifo, status, sizeof(status)) > 0) {
129 if (!*status)
130 continue;
131
132 msgcycle = maxmsgcycle;
133 } else {
134 status[0] = '\0';
135 for (i = len = 0; i < LEN(args); i++) {
136 if (!args[i].doenable(args[i].args))
137 continue;
138
139 if (!(res = args[i].func(args[i].args)))
140 res = unknown_str;
141
142 if ((ret = esnprintf(status + len, sizeof(status) - len,
143 args[i].fmt, res)) < 0)
144 break;
145
146 len += ret;
147 }
148 }
149
150 if (sflag) {
151 puts(status);
152 fflush(stdout);
153 if (ferror(stdout))
154 die("puts:");
155 } else {
156 if (XStoreName(dpy, DefaultRootWindow(dpy), status) < 0)
157 die("XStoreName: Allocation failed");
158 XFlush(dpy);
159 }
160
161 if (!done) {
162 if (clock_gettime(CLOCK_MONOTONIC, ¤t) < 0)
163 die("clock_gettime:");
164 difftimespec(&diff, ¤t, &start);
165
166 intspec.tv_sec = interval / 1000;
167 intspec.tv_nsec = (interval % 1000) * 1E6;
168 difftimespec(&wait, &intspec, &diff);
169
170 if (wait.tv_sec >= 0 &&
171 nanosleep(&wait, NULL) < 0 &&
172 errno != EINTR)
173 die("nanosleep:");
174 }
175 } while (!done);
176
177 if (!sflag) {
178 XStoreName(dpy, DefaultRootWindow(dpy), NULL);
179 if (XCloseDisplay(dpy) < 0)
180 die("XCloseDisplay: Failed to close display");
181 }
182
183 return 0;
184}