My slstatus configuration
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

897 lines
18 KiB

  1. /* See LICENSE file for copyright and license details. */
  2. #include <dirent.h>
  3. #include <err.h>
  4. #include <errno.h>
  5. #include <fcntl.h>
  6. #include <ifaddrs.h>
  7. #include <limits.h>
  8. #include <linux/wireless.h>
  9. #include <locale.h>
  10. #include <netdb.h>
  11. #include <pwd.h>
  12. #include <signal.h>
  13. #include <stdarg.h>
  14. #include <stdio.h>
  15. #include <stdlib.h>
  16. #include <string.h>
  17. #include <sys/ioctl.h>
  18. #include <sys/stat.h>
  19. #include <sys/statvfs.h>
  20. #include <sys/socket.h>
  21. #include <sys/soundcard.h>
  22. #include <sys/sysinfo.h>
  23. #include <sys/types.h>
  24. #include <sys/utsname.h>
  25. #include <time.h>
  26. #include <unistd.h>
  27. #include <X11/Xlib.h>
  28. #include "arg.h"
  29. #define LEN(x) (sizeof (x) / sizeof *(x))
  30. struct arg {
  31. const char *(*func)();
  32. const char *fmt;
  33. const char *args;
  34. };
  35. static const char *battery_perc(const char *bat);
  36. static const char *battery_power(const char *bat);
  37. static const char *battery_state(const char *bat);
  38. static const char *cpu_freq(void);
  39. static const char *cpu_perc(void);
  40. static const char *cpu_iowait(void);
  41. static const char *datetime(const char *fmt);
  42. static const char *disk_free(const char *mnt);
  43. static const char *disk_perc(const char *mnt);
  44. static const char *disk_total(const char *mnt);
  45. static const char *disk_used(const char *mnt);
  46. static const char *entropy(void);
  47. static const char *gid(void);
  48. static const char *hostname(void);
  49. static const char *ipv4(const char *iface);
  50. static const char *ipv6(const char *iface);
  51. static const char *kernel_release(void);
  52. static const char *keyboard_indicators(void);
  53. static const char *load_avg(const char *fmt);
  54. static const char *num_files(const char *dir);
  55. static const char *ram_free(void);
  56. static const char *ram_perc(void);
  57. static const char *ram_used(void);
  58. static const char *ram_total(void);
  59. static const char *run_command(const char *cmd);
  60. static const char *swap_free(void);
  61. static const char *swap_perc(void);
  62. static const char *swap_used(void);
  63. static const char *swap_total(void);
  64. static const char *temp(const char *file);
  65. static const char *uid(void);
  66. static const char *uptime(void);
  67. static const char *username(void);
  68. static const char *vol_perc(const char *card);
  69. static const char *wifi_perc(const char *iface);
  70. static const char *wifi_essid(const char *iface);
  71. char *argv0;
  72. static unsigned short int done;
  73. static Display *dpy;
  74. #include "config.h"
  75. static char buf[MAXLEN];
  76. static const char *
  77. bprintf(const char *fmt, ...)
  78. {
  79. va_list ap;
  80. size_t len;
  81. va_start(ap, fmt);
  82. len = vsnprintf(buf, sizeof(buf) - 1, fmt, ap);
  83. va_end(ap);
  84. if (len >= sizeof(buf))
  85. buf[sizeof(buf)-1] = '\0';
  86. return buf;
  87. }
  88. int
  89. pscanf(const char *path, const char *fmt, ...)
  90. {
  91. FILE *fp;
  92. va_list ap;
  93. int n;
  94. if (!(fp = fopen(path, "r"))) {
  95. warn("fopen %s: %s\n", path, strerror(errno));
  96. return -1;
  97. }
  98. va_start(ap, fmt);
  99. n = vfscanf(fp, fmt, ap);
  100. va_end(ap);
  101. fclose(fp);
  102. return (n == EOF) ? -1 : n;
  103. }
  104. static const char *
  105. battery_perc(const char *bat)
  106. {
  107. int perc;
  108. char path[PATH_MAX];
  109. snprintf(path, sizeof(path), "%s%s%s", "/sys/class/power_supply/", bat, "/capacity");
  110. return (pscanf(path, "%i", &perc) == 1) ?
  111. bprintf("%d", perc) : unknown_str;
  112. }
  113. static const char *
  114. battery_power(const char *bat)
  115. {
  116. int watts;
  117. char path[PATH_MAX];
  118. snprintf(path, sizeof(path), "%s%s%s", "/sys/class/power_supply/", bat, "/power_now");
  119. return (pscanf(path, "%i", &watts) == 1) ?
  120. bprintf("%d", (watts + 500000) / 1000000) : unknown_str;
  121. }
  122. static const char *
  123. battery_state(const char *bat)
  124. {
  125. struct {
  126. char *state;
  127. char *symbol;
  128. } map[] = {
  129. { "Charging", "+" },
  130. { "Discharging", "-" },
  131. { "Full", "=" },
  132. { "Unknown", "/" },
  133. };
  134. size_t i;
  135. char path[PATH_MAX], state[12];
  136. snprintf(path, sizeof(path), "%s%s%s", "/sys/class/power_supply/", bat, "/status");
  137. if (pscanf(path, "%12s", state) != 1) {
  138. return unknown_str;
  139. }
  140. for (i = 0; i < LEN(map); i++) {
  141. if (!strcmp(map[i].state, state)) {
  142. break;
  143. }
  144. }
  145. return (i == LEN(map)) ? "?" : map[i].symbol;
  146. }
  147. static const char *
  148. cpu_freq(void)
  149. {
  150. int freq;
  151. return (pscanf("/sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq",
  152. "%i", &freq) == 1) ?
  153. bprintf("%d", (freq + 500) / 1000) : unknown_str;
  154. }
  155. static const char *
  156. cpu_perc(void)
  157. {
  158. int perc;
  159. static long double a[7];
  160. static int valid;
  161. long double b[7];
  162. memcpy(b, a, sizeof(b));
  163. if (pscanf("/proc/stat", "%*s %Lf %Lf %Lf %Lf %Lf %Lf %Lf", &a[0], &a[1], &a[2],
  164. &a[3], &a[4], &a[5], &a[6]) != 7) {
  165. return unknown_str;
  166. }
  167. if (!valid) {
  168. valid = 1;
  169. return unknown_str;
  170. }
  171. perc = 100 * ((b[0]+b[1]+b[2]+b[5]+b[6]) - (a[0]+a[1]+a[2]+a[5]+a[6])) /
  172. ((b[0]+b[1]+b[2]+b[3]+b[4]+b[5]+b[6]) - (a[0]+a[1]+a[2]+a[3]+a[4]+a[5]+a[6]));
  173. return bprintf("%d", perc);
  174. }
  175. static const char *
  176. cpu_iowait(void)
  177. {
  178. int perc;
  179. static int valid;
  180. static long double a[7];
  181. long double b[7];
  182. memcpy(b, a, sizeof(b));
  183. if (pscanf("/proc/stat", "%*s %Lf %Lf %Lf %Lf %Lf %Lf %Lf", &a[0], &a[1], &a[2],
  184. &a[3], &a[4], &a[5], &a[6]) != 7) {
  185. return unknown_str;
  186. }
  187. if (!valid) {
  188. valid = 1;
  189. return unknown_str;
  190. }
  191. perc = 100 * ((b[4]) - (a[4])) /
  192. ((b[0]+b[1]+b[2]+b[3]+b[4]+b[5]+b[6]) - (a[0]+a[1]+a[2]+a[3]+a[4]+a[5]+a[6]));
  193. return bprintf("%d", perc);
  194. }
  195. static const char *
  196. datetime(const char *fmt)
  197. {
  198. time_t t;
  199. t = time(NULL);
  200. if (strftime(buf, sizeof(buf), fmt, localtime(&t)) == 0)
  201. return unknown_str;
  202. return buf;
  203. }
  204. static const char *
  205. disk_free(const char *mnt)
  206. {
  207. struct statvfs fs;
  208. if (statvfs(mnt, &fs) < 0) {
  209. warn("Failed to get filesystem info");
  210. return unknown_str;
  211. }
  212. return bprintf("%f", (float)fs.f_bsize * (float)fs.f_bfree / 1024 / 1024 / 1024);
  213. }
  214. static const char *
  215. disk_perc(const char *mnt)
  216. {
  217. int perc;
  218. struct statvfs fs;
  219. if (statvfs(mnt, &fs) < 0) {
  220. warn("Failed to get filesystem info");
  221. return unknown_str;
  222. }
  223. perc = 100 * (1.0f - ((float)fs.f_bfree / (float)fs.f_blocks));
  224. return bprintf("%d", perc);
  225. }
  226. static const char *
  227. disk_total(const char *mnt)
  228. {
  229. struct statvfs fs;
  230. if (statvfs(mnt, &fs) < 0) {
  231. warn("Failed to get filesystem info");
  232. return unknown_str;
  233. }
  234. return bprintf("%f", (float)fs.f_bsize * (float)fs.f_blocks / 1024 / 1024 / 1024);
  235. }
  236. static const char *
  237. disk_used(const char *mnt)
  238. {
  239. struct statvfs fs;
  240. if (statvfs(mnt, &fs) < 0) {
  241. warn("Failed to get filesystem info");
  242. return unknown_str;
  243. }
  244. return bprintf("%f", (float)fs.f_bsize * ((float)fs.f_blocks - (float)fs.f_bfree) / 1024 / 1024 / 1024);
  245. }
  246. static const char *
  247. entropy(void)
  248. {
  249. int num;
  250. return (pscanf("/proc/sys/kernel/random/entropy_avail", "%d", &num) == 1) ?
  251. bprintf("%d", num) : unknown_str;
  252. }
  253. static const char *
  254. gid(void)
  255. {
  256. return bprintf("%d", getgid());
  257. }
  258. static const char *
  259. hostname(void)
  260. {
  261. if (gethostname(buf, sizeof(buf)) == -1) {
  262. warn("hostname");
  263. return unknown_str;
  264. }
  265. return buf;
  266. }
  267. static const char *
  268. ipv4(const char *iface)
  269. {
  270. struct ifaddrs *ifaddr, *ifa;
  271. int s;
  272. char host[NI_MAXHOST];
  273. if (getifaddrs(&ifaddr) == -1) {
  274. warn("Failed to get IPv4 address for interface %s", iface);
  275. return unknown_str;
  276. }
  277. for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) {
  278. if (ifa->ifa_addr == NULL) {
  279. continue;
  280. }
  281. s = getnameinfo(ifa->ifa_addr, sizeof(struct sockaddr_in), host, NI_MAXHOST, NULL, 0, NI_NUMERICHOST);
  282. if ((strcmp(ifa->ifa_name, iface) == 0) && (ifa->ifa_addr->sa_family == AF_INET)) {
  283. if (s != 0) {
  284. warnx("Failed to get IPv4 address for interface %s", iface);
  285. return unknown_str;
  286. }
  287. return bprintf("%s", host);
  288. }
  289. }
  290. freeifaddrs(ifaddr);
  291. return unknown_str;
  292. }
  293. static const char *
  294. ipv6(const char *iface)
  295. {
  296. struct ifaddrs *ifaddr, *ifa;
  297. int s;
  298. char host[NI_MAXHOST];
  299. if (getifaddrs(&ifaddr) == -1) {
  300. warn("Failed to get IPv6 address for interface %s", iface);
  301. return unknown_str;
  302. }
  303. for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) {
  304. if (ifa->ifa_addr == NULL) {
  305. continue;
  306. }
  307. s = getnameinfo(ifa->ifa_addr, sizeof(struct sockaddr_in6), host, NI_MAXHOST, NULL, 0, NI_NUMERICHOST);
  308. if ((strcmp(ifa->ifa_name, iface) == 0) && (ifa->ifa_addr->sa_family == AF_INET6)) {
  309. if (s != 0) {
  310. warnx("Failed to get IPv6 address for interface %s", iface);
  311. return unknown_str;
  312. }
  313. return bprintf("%s", host);
  314. }
  315. }
  316. freeifaddrs(ifaddr);
  317. return unknown_str;
  318. }
  319. static const char *
  320. kernel_release(void)
  321. {
  322. struct utsname udata;
  323. if (uname(&udata) < 0) {
  324. return unknown_str;
  325. }
  326. return bprintf("%s", udata.release);
  327. }
  328. static const char *
  329. keyboard_indicators(void)
  330. {
  331. Display *dpy = XOpenDisplay(NULL);
  332. XKeyboardState state;
  333. if (dpy == NULL) {
  334. warnx("XOpenDisplay failed");
  335. return unknown_str;
  336. }
  337. XGetKeyboardControl(dpy, &state);
  338. XCloseDisplay(dpy);
  339. switch (state.led_mask) {
  340. case 1:
  341. return "c";
  342. case 2:
  343. return "n";
  344. case 3:
  345. return "cn";
  346. default:
  347. return "";
  348. }
  349. }
  350. static const char *
  351. load_avg(const char *fmt)
  352. {
  353. double avgs[3];
  354. if (getloadavg(avgs, 3) < 0) {
  355. warnx("Failed to get the load avg");
  356. return unknown_str;
  357. }
  358. return bprintf(fmt, avgs[0], avgs[1], avgs[2]);
  359. }
  360. static const char *
  361. num_files(const char *dir)
  362. {
  363. struct dirent *dp;
  364. DIR *fd;
  365. int num = 0;
  366. if ((fd = opendir(dir)) == NULL) {
  367. warn("Failed to get number of files in directory %s", dir);
  368. return unknown_str;
  369. }
  370. while ((dp = readdir(fd)) != NULL) {
  371. if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, ".."))
  372. continue; /* skip self and parent */
  373. num++;
  374. }
  375. closedir(fd);
  376. return bprintf("%d", num);
  377. }
  378. static const char *
  379. ram_free(void)
  380. {
  381. long free;
  382. return (pscanf("/proc/meminfo", "MemFree: %ld kB\n", &free) == 1) ?
  383. bprintf("%f", (float)free / 1024 / 1024) : unknown_str;
  384. }
  385. static const char *
  386. ram_perc(void)
  387. {
  388. long total, free, buffers, cached;
  389. return (pscanf("/proc/meminfo",
  390. "MemTotal: %ld kB\n"
  391. "MemFree: %ld kB\n"
  392. "MemAvailable: %ld kB\nBuffers: %ld kB\n"
  393. "Cached: %ld kB\n",
  394. &total, &free, &buffers, &buffers, &cached) == 5) ?
  395. bprintf("%d", 100 * ((total - free) - (buffers + cached)) / total) :
  396. unknown_str;
  397. }
  398. static const char *
  399. ram_total(void)
  400. {
  401. long total;
  402. return (pscanf("/proc/meminfo", "MemTotal: %ld kB\n", &total) == 1) ?
  403. bprintf("%f", (float)total / 1024 / 1024) : unknown_str;
  404. }
  405. static const char *
  406. ram_used(void)
  407. {
  408. long total, free, buffers, cached;
  409. return (pscanf("/proc/meminfo",
  410. "MemTotal: %ld kB\n"
  411. "MemFree: %ld kB\n"
  412. "MemAvailable: %ld kB\nBuffers: %ld kB\n"
  413. "Cached: %ld kB\n",
  414. &total, &free, &buffers, &buffers, &cached) == 5) ?
  415. bprintf("%f", (float)(total - free - buffers - cached) / 1024 / 1024) :
  416. unknown_str;
  417. }
  418. static const char *
  419. run_command(const char *cmd)
  420. {
  421. char *p;
  422. FILE *fp;
  423. fp = popen(cmd, "r");
  424. if (fp == NULL) {
  425. warn("Failed to get command output for %s", cmd);
  426. return unknown_str;
  427. }
  428. p = fgets(buf, sizeof(buf) - 1, fp);
  429. pclose(fp);
  430. if (!p)
  431. return unknown_str;
  432. if ((p = strrchr(buf, '\n')) != NULL)
  433. p[0] = '\0';
  434. return buf[0] ? buf : unknown_str;
  435. }
  436. static const char *
  437. swap_free(void)
  438. {
  439. long total, free;
  440. FILE *fp;
  441. size_t bytes_read;
  442. char *match;
  443. fp = fopen("/proc/meminfo", "r");
  444. if (fp == NULL) {
  445. warn("Failed to open file /proc/meminfo");
  446. return unknown_str;
  447. }
  448. if ((bytes_read = fread(buf, sizeof(char), sizeof(buf) - 1, fp)) == 0) {
  449. warn("swap_free: read error");
  450. fclose(fp);
  451. return unknown_str;
  452. }
  453. fclose(fp);
  454. if ((match = strstr(buf, "SwapTotal")) == NULL)
  455. return unknown_str;
  456. sscanf(match, "SwapTotal: %ld kB\n", &total);
  457. if ((match = strstr(buf, "SwapFree")) == NULL)
  458. return unknown_str;
  459. sscanf(match, "SwapFree: %ld kB\n", &free);
  460. return bprintf("%f", (float)free / 1024 / 1024);
  461. }
  462. static const char *
  463. swap_perc(void)
  464. {
  465. long total, free, cached;
  466. FILE *fp;
  467. size_t bytes_read;
  468. char *match;
  469. fp = fopen("/proc/meminfo", "r");
  470. if (fp == NULL) {
  471. warn("Failed to open file /proc/meminfo");
  472. return unknown_str;
  473. }
  474. if ((bytes_read = fread(buf, sizeof(char), sizeof(buf) - 1, fp)) == 0) {
  475. warn("swap_perc: read error");
  476. fclose(fp);
  477. return unknown_str;
  478. }
  479. fclose(fp);
  480. if ((match = strstr(buf, "SwapTotal")) == NULL)
  481. return unknown_str;
  482. sscanf(match, "SwapTotal: %ld kB\n", &total);
  483. if ((match = strstr(buf, "SwapCached")) == NULL)
  484. return unknown_str;
  485. sscanf(match, "SwapCached: %ld kB\n", &cached);
  486. if ((match = strstr(buf, "SwapFree")) == NULL)
  487. return unknown_str;
  488. sscanf(match, "SwapFree: %ld kB\n", &free);
  489. return bprintf("%d", 100 * (total - free - cached) / total);
  490. }
  491. static const char *
  492. swap_total(void)
  493. {
  494. long total;
  495. FILE *fp;
  496. size_t bytes_read;
  497. char *match;
  498. fp = fopen("/proc/meminfo", "r");
  499. if (fp == NULL) {
  500. warn("Failed to open file /proc/meminfo");
  501. return unknown_str;
  502. }
  503. if ((bytes_read = fread(buf, sizeof(char), sizeof(buf) - 1, fp)) == 0) {
  504. warn("swap_total: read error");
  505. fclose(fp);
  506. return unknown_str;
  507. }
  508. fclose(fp);
  509. if ((match = strstr(buf, "SwapTotal")) == NULL)
  510. return unknown_str;
  511. sscanf(match, "SwapTotal: %ld kB\n", &total);
  512. return bprintf("%f", (float)total / 1024 / 1024);
  513. }
  514. static const char *
  515. swap_used(void)
  516. {
  517. long total, free, cached;
  518. FILE *fp;
  519. size_t bytes_read;
  520. char *match;
  521. fp = fopen("/proc/meminfo", "r");
  522. if (fp == NULL) {
  523. warn("Failed to open file /proc/meminfo");
  524. return unknown_str;
  525. }
  526. if ((bytes_read = fread(buf, sizeof(char), sizeof(buf) - 1, fp)) == 0) {
  527. warn("swap_used: read error");
  528. fclose(fp);
  529. return unknown_str;
  530. }
  531. fclose(fp);
  532. if ((match = strstr(buf, "SwapTotal")) == NULL)
  533. return unknown_str;
  534. sscanf(match, "SwapTotal: %ld kB\n", &total);
  535. if ((match = strstr(buf, "SwapCached")) == NULL)
  536. return unknown_str;
  537. sscanf(match, "SwapCached: %ld kB\n", &cached);
  538. if ((match = strstr(buf, "SwapFree")) == NULL)
  539. return unknown_str;
  540. sscanf(match, "SwapFree: %ld kB\n", &free);
  541. return bprintf("%f", (float)(total - free - cached) / 1024 / 1024);
  542. }
  543. static const char *
  544. temp(const char *file)
  545. {
  546. int temp;
  547. return (pscanf(file, "%d", &temp) == 1) ?
  548. bprintf("%d", temp / 1000) : unknown_str;
  549. }
  550. static const char *
  551. uptime(void)
  552. {
  553. struct sysinfo info;
  554. int h = 0;
  555. int m = 0;
  556. sysinfo(&info);
  557. h = info.uptime / 3600;
  558. m = (info.uptime - h * 3600 ) / 60;
  559. return bprintf("%dh %dm", h, m);
  560. }
  561. static const char *
  562. username(void)
  563. {
  564. struct passwd *pw = getpwuid(geteuid());
  565. if (pw == NULL) {
  566. warn("Failed to get username");
  567. return unknown_str;
  568. }
  569. return bprintf("%s", pw->pw_name);
  570. }
  571. static const char *
  572. uid(void)
  573. {
  574. return bprintf("%d", geteuid());
  575. }
  576. static const char *
  577. vol_perc(const char *card)
  578. {
  579. unsigned int i;
  580. int v, afd, devmask;
  581. char *vnames[] = SOUND_DEVICE_NAMES;
  582. afd = open(card, O_RDONLY | O_NONBLOCK);
  583. if (afd == -1) {
  584. warn("Cannot open %s", card);
  585. return unknown_str;
  586. }
  587. if (ioctl(afd, SOUND_MIXER_READ_DEVMASK, &devmask) == -1) {
  588. warn("Cannot get volume for %s", card);
  589. close(afd);
  590. return unknown_str;
  591. }
  592. for (i = 0; i < LEN(vnames); i++) {
  593. if (devmask & (1 << i) && !strcmp("vol", vnames[i])) {
  594. if (ioctl(afd, MIXER_READ(i), &v) == -1) {
  595. warn("vol_perc: ioctl");
  596. close(afd);
  597. return unknown_str;
  598. }
  599. }
  600. }
  601. close(afd);
  602. return bprintf("%d", v & 0xff);
  603. }
  604. static const char *
  605. wifi_perc(const char *iface)
  606. {
  607. int i, perc;
  608. char *p, *datastart;
  609. char path[PATH_MAX];
  610. char status[5];
  611. FILE *fp;
  612. snprintf(path, sizeof(path), "%s%s%s", "/sys/class/net/", iface, "/operstate");
  613. fp = fopen(path, "r");
  614. if (fp == NULL) {
  615. warn("Failed to open file %s", path);
  616. return unknown_str;
  617. }
  618. p = fgets(status, 5, fp);
  619. fclose(fp);
  620. if(!p || strcmp(status, "up\n") != 0) {
  621. return unknown_str;
  622. }
  623. fp = fopen("/proc/net/wireless", "r");
  624. if (fp == NULL) {
  625. warn("Failed to open file /proc/net/wireless");
  626. return unknown_str;
  627. }
  628. for (i = 0; i < 3; i++) {
  629. if (!(p = fgets(buf, sizeof(buf) - 1, fp)))
  630. break;
  631. }
  632. fclose(fp);
  633. if (i < 2 || !p)
  634. return unknown_str;
  635. if ((datastart = strstr(buf, iface)) == NULL)
  636. return unknown_str;
  637. datastart = (datastart+(strlen(iface)+1));
  638. sscanf(datastart + 1, " %*d %d %*d %*d %*d %*d %*d %*d %*d %*d", &perc);
  639. return bprintf("%d", perc);
  640. }
  641. static const char *
  642. wifi_essid(const char *iface)
  643. {
  644. static char id[IW_ESSID_MAX_SIZE+1];
  645. int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
  646. struct iwreq wreq;
  647. memset(&wreq, 0, sizeof(struct iwreq));
  648. wreq.u.essid.length = IW_ESSID_MAX_SIZE+1;
  649. snprintf(wreq.ifr_name, sizeof(wreq.ifr_name), "%s", iface);
  650. if (sockfd == -1) {
  651. warn("Failed to get ESSID for interface %s", iface);
  652. return unknown_str;
  653. }
  654. wreq.u.essid.pointer = id;
  655. if (ioctl(sockfd,SIOCGIWESSID, &wreq) == -1) {
  656. warn("Failed to get ESSID for interface %s", iface);
  657. return unknown_str;
  658. }
  659. close(sockfd);
  660. if (strcmp(id, "") == 0)
  661. return unknown_str;
  662. else
  663. return id;
  664. }
  665. static void
  666. terminate(const int signo)
  667. {
  668. done = 1;
  669. }
  670. static void
  671. difftimespec(struct timespec *res, struct timespec *a, struct timespec *b)
  672. {
  673. res->tv_sec = a->tv_sec - b->tv_sec - (a->tv_nsec < b->tv_nsec);
  674. res->tv_nsec = a->tv_nsec - b->tv_nsec +
  675. (a->tv_nsec < b->tv_nsec) * 1000000000;
  676. }
  677. static void
  678. usage(void)
  679. {
  680. fprintf(stderr, "usage: %s [-s]\n", argv0);
  681. exit(1);
  682. }
  683. int
  684. main(int argc, char *argv[])
  685. {
  686. struct sigaction act;
  687. struct timespec start, current, diff, intspec, wait;
  688. size_t i, len;
  689. int sflag = 0;
  690. char status[MAXLEN];
  691. ARGBEGIN {
  692. case 's':
  693. sflag = 1;
  694. break;
  695. default:
  696. usage();
  697. } ARGEND
  698. if (argc) {
  699. usage();
  700. }
  701. setlocale(LC_ALL, "");
  702. memset(&act, 0, sizeof(act));
  703. act.sa_handler = terminate;
  704. sigaction(SIGINT, &act, NULL);
  705. sigaction(SIGTERM, &act, NULL);
  706. if (!sflag && !(dpy = XOpenDisplay(NULL))) {
  707. fprintf(stderr, "slstatus: cannot open display");
  708. return 1;
  709. }
  710. while (!done) {
  711. clock_gettime(CLOCK_MONOTONIC, &start);
  712. status[0] = '\0';
  713. for (i = len = 0; i < LEN(args); i++) {
  714. len += snprintf(status + len, sizeof(status) - len,
  715. args[i].fmt, args[i].func(args[i].args));
  716. if (len >= sizeof(status)) {
  717. status[sizeof(status) - 1] = '\0';
  718. }
  719. }
  720. if (sflag) {
  721. printf("%s\n", status);
  722. } else {
  723. XStoreName(dpy, DefaultRootWindow(dpy), status);
  724. XSync(dpy, False);
  725. }
  726. if (!done) {
  727. clock_gettime(CLOCK_MONOTONIC, &current);
  728. difftimespec(&diff, &current, &start);
  729. intspec.tv_sec = interval / 1000;
  730. intspec.tv_nsec = (interval % 1000) * 1000000;
  731. difftimespec(&wait, &intspec, &diff);
  732. if (wait.tv_sec >= 0) {
  733. nanosleep(&wait, NULL);
  734. }
  735. }
  736. }
  737. if (!sflag) {
  738. XStoreName(dpy, DefaultRootWindow(dpy), NULL);
  739. XCloseDisplay(dpy);
  740. }
  741. return 0;
  742. }