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.
 
 
 
 

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