Another copy of my dotfiles. Because I don't completely trust GitHub.
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.

357 lines
12 KiB

4 years ago
  1. #include <errno.h>
  2. #include <fcntl.h>
  3. #include <limits.h>
  4. #include <signal.h>
  5. #include <stdio.h>
  6. #include <stdlib.h>
  7. #include <string.h>
  8. #include <unistd.h>
  9. #include <X11/Xlib.h>
  10. #define CMDLENGTH 150
  11. #define STTLENGTH 256
  12. #define NILL INT_MIN
  13. #define LOCKFILE "/tmp/dwmblocks.pid"
  14. typedef struct {
  15. char *pathu;
  16. char *pathc;
  17. const int interval;
  18. const int signal;
  19. char cmdoutcur[CMDLENGTH];
  20. char cmdoutprv[CMDLENGTH];
  21. } Block;
  22. #include "blocks.h"
  23. static void buttonhandler(int signal, siginfo_t *si, void *ucontext);
  24. static void getcmd(Block *block, int sigval);
  25. static void setroot();
  26. static void setupsignals();
  27. static void sighandler(int signal, siginfo_t *si, void *ucontext);
  28. static void statusloop();
  29. static void termhandler(int signum);
  30. static int updatestatus();
  31. static void writepid();
  32. static int statuscontinue = 1;
  33. static char statusstr[STTLENGTH];
  34. static size_t delimlength;
  35. static Display *dpy;
  36. static sigset_t blocksigmask;
  37. void
  38. buttonhandler(int signal, siginfo_t *si, void *ucontext)
  39. {
  40. signal = si->si_value.sival_int >> 8;
  41. switch (fork()) {
  42. case -1:
  43. perror("buttonhandler - fork");
  44. exit(1);
  45. case 0:
  46. close(ConnectionNumber(dpy));
  47. for (Block *current = blocks; current->pathu; current++) {
  48. if (current->signal == signal) {
  49. char button[] = { '0' + (si->si_value.sival_int & 0xff), '\0' };
  50. char *arg[] = { current->pathc, button, NULL };
  51. setsid();
  52. execv(arg[0], arg);
  53. perror("buttonhandler - child - execv");
  54. _exit(127);
  55. }
  56. }
  57. exit(0);
  58. }
  59. }
  60. void
  61. getcmd(Block *block, int sigval)
  62. {
  63. int fd[2];
  64. if (pipe(fd) == -1) {
  65. perror("getcmd - pipe");
  66. exit(1);
  67. }
  68. switch (fork()) {
  69. case -1:
  70. perror("getcmd - fork");
  71. exit(1);
  72. case 0:
  73. close(ConnectionNumber(dpy));
  74. close(fd[0]);
  75. if (fd[1] != STDOUT_FILENO) {
  76. if (dup2(fd[1], STDOUT_FILENO) != STDOUT_FILENO) {
  77. perror("getcmd - child - dup2");
  78. exit(1);
  79. }
  80. close(fd[1]);
  81. }
  82. if (sigval == NILL) {
  83. char *arg[] = { block->pathu, NULL };
  84. execv(arg[0], arg);
  85. } else {
  86. char buf[12];
  87. char *arg[] = { block->pathu, buf, NULL };
  88. snprintf(buf, sizeof buf, "%d", sigval);
  89. execv(arg[0], arg);
  90. }
  91. perror("getcmd - child - execv");
  92. _exit(127);
  93. default:
  94. close(fd[1]);
  95. if (read(fd[0], block->cmdoutcur, CMDLENGTH) == -1) {
  96. perror("getcmd - read");
  97. exit(1);
  98. }
  99. close(fd[0]);
  100. }
  101. }
  102. void
  103. setroot()
  104. {
  105. if (updatestatus()) {
  106. XStoreName(dpy, DefaultRootWindow(dpy), statusstr);
  107. XSync(dpy, False);
  108. }
  109. }
  110. void
  111. setupsignals()
  112. {
  113. struct sigaction sa;
  114. /* to handle HUP, INT and TERM */
  115. sa.sa_flags = SA_RESTART;
  116. sigemptyset(&sa.sa_mask);
  117. sa.sa_handler = termhandler;
  118. sigaction(SIGHUP, &sa, NULL);
  119. sigaction(SIGINT, &sa, NULL);
  120. sigaction(SIGTERM, &sa, NULL);
  121. /* to ignore unused realtime signals */
  122. // sa.sa_flags = SA_RESTART;
  123. // sigemptyset(&sa.sa_mask);
  124. sa.sa_handler = SIG_IGN;
  125. for (int i = SIGRTMIN + 1; i <= SIGRTMAX; i++)
  126. sigaction(i, &sa, NULL);
  127. /* to prevent forked children from becoming zombies */
  128. sa.sa_flags = SA_NOCLDSTOP | SA_NOCLDWAIT | SA_RESTART;
  129. // sigemptyset(&sa.sa_mask);
  130. sa.sa_handler = SIG_DFL;
  131. sigaction(SIGCHLD, &sa, NULL);
  132. /* to handle signals generated by dwm on click events */
  133. sa.sa_flags = SA_RESTART | SA_SIGINFO;
  134. // sigemptyset(&sa.sa_mask);
  135. sa.sa_sigaction = buttonhandler;
  136. sigaction(SIGRTMIN, &sa, NULL);
  137. /* to handle update signals for individual blocks */
  138. sa.sa_flags |= SA_NODEFER;
  139. sa.sa_mask = blocksigmask;
  140. sa.sa_sigaction = sighandler;
  141. for (Block *current = blocks; current->pathu; current++)
  142. if (current->signal > 0)
  143. sigaction(SIGRTMIN + current->signal, &sa, NULL);
  144. }
  145. void
  146. sighandler(int signal, siginfo_t *si, void *ucontext)
  147. {
  148. signal -= SIGRTMIN;
  149. for (Block *current = blocks; current->pathu; current++)
  150. if (current->signal == signal)
  151. getcmd(current, si->si_value.sival_int);
  152. setroot();
  153. }
  154. void
  155. statusloop()
  156. {
  157. int i;
  158. /* first run */
  159. sigprocmask(SIG_BLOCK, &blocksigmask, NULL);
  160. for (Block *current = blocks; current->pathu; current++)
  161. if (current->interval >= 0)
  162. getcmd(current, NILL);
  163. setroot();
  164. sigprocmask(SIG_UNBLOCK, &blocksigmask, NULL);
  165. sleep(SLEEPINTERVAL);
  166. i = SLEEPINTERVAL;
  167. /* main loop */
  168. while (statuscontinue) {
  169. sigprocmask(SIG_BLOCK, &blocksigmask, NULL);
  170. for (Block *current = blocks; current->pathu; current++)
  171. if (current->interval > 0 && i % current->interval == 0)
  172. getcmd(current, NILL);
  173. setroot();
  174. sigprocmask(SIG_UNBLOCK, &blocksigmask, NULL);
  175. sleep(SLEEPINTERVAL);
  176. i += SLEEPINTERVAL;
  177. }
  178. }
  179. void
  180. termhandler(int signum)
  181. {
  182. statuscontinue = 0;
  183. }
  184. /* returns whether block outputs have changed and updates statusstr if they have */
  185. int
  186. updatestatus()
  187. {
  188. char *s = statusstr;
  189. char *c, *p; /* for cmdoutcur and cmdoutprv */
  190. const char *d; /* for delimiter */
  191. Block *current = blocks;
  192. /* checking half of the function */
  193. /* find the first non-empty block */
  194. for (;; current++) {
  195. /* all blocks are empty */
  196. if (!current->pathu)
  197. return 0;
  198. /* contents of the current block just changed */
  199. if (*current->cmdoutcur != *current->cmdoutprv)
  200. goto update0;
  201. /* no delimiter before the first non-empty block */
  202. if (*current->cmdoutcur != '\n' && *current->cmdoutcur != '\0')
  203. goto skipdelimc;
  204. }
  205. /* main loop */
  206. for (; current->pathu; current++) {
  207. /* contents of the current block just changed */
  208. if (*current->cmdoutcur != *current->cmdoutprv)
  209. goto update1;
  210. /* delimiter handler */
  211. /* current block is non-empty */
  212. if (*current->cmdoutcur != '\n' && *current->cmdoutcur != '\0')
  213. s += delimlength;
  214. /* skip over empty blocks */
  215. else
  216. continue;
  217. skipdelimc:
  218. /* checking for the first byte has been done */
  219. c = current->cmdoutcur + 1, p = current->cmdoutprv + 1;
  220. for (; *c != '\n' && *c != '\0'; c++, p++)
  221. /* contents of the current block just changed */
  222. if (*c != *p) {
  223. s += c - current->cmdoutcur;
  224. goto update2;
  225. }
  226. s += c - current->cmdoutcur;
  227. /* byte containing info about signal number for the block */
  228. if (current->pathc && current->signal)
  229. s++;
  230. }
  231. return 0;
  232. /* updating half of the function */
  233. /* find the first non-empty block */
  234. for (;; current++) {
  235. /* all blocks are empty */
  236. if (!current->pathu)
  237. return 1;
  238. update0:
  239. /* skip delimiter before the first non-empty block */
  240. if (*current->cmdoutcur != '\n' && *current->cmdoutcur != '\0')
  241. goto skipdelimu;
  242. *current->cmdoutprv = *current->cmdoutcur;
  243. }
  244. /* main loop */
  245. for (; current->pathu; current++) {
  246. update1:
  247. /* delimiter handler */
  248. /* current block is non-empty */
  249. if (*current->cmdoutcur != '\n' && *current->cmdoutcur != '\0') {
  250. d = delim;
  251. while (*d != '\0')
  252. *(s++) = *(d++);
  253. *(s++) = '\n'; /* to mark the end of delimiter */
  254. /* skip over empty blocks */
  255. } else {
  256. *current->cmdoutprv = *current->cmdoutcur;
  257. continue;
  258. }
  259. skipdelimu:
  260. c = current->cmdoutcur, p = current->cmdoutprv;
  261. update2:
  262. do {
  263. *(s++) = *c;
  264. *p = *c;
  265. c++, p++;
  266. } while (*c != '\n' && *c != '\0');
  267. if (current->pathc && current->signal)
  268. *(s++) = current->signal;
  269. }
  270. *s = '\0';
  271. return 1;
  272. }
  273. void
  274. writepid()
  275. {
  276. int fd;
  277. struct flock fl;
  278. fd = open(LOCKFILE, O_RDWR|O_CREAT, 0644);
  279. if (fd == -1) {
  280. perror("writepid - fd");
  281. exit(1);
  282. }
  283. fl.l_type = F_WRLCK;
  284. fl.l_start = 0;
  285. fl.l_whence = SEEK_SET;
  286. fl.l_len = 0;
  287. if (fcntl(fd, F_SETLK, &fl) == -1) {
  288. if (errno == EACCES || errno == EAGAIN) {
  289. fputs("Error: another instance of dwmblocks is already running.\n", stderr);
  290. exit(2);
  291. }
  292. perror("writepid - fcntl");
  293. exit(1);
  294. }
  295. if (ftruncate(fd, 0) == -1) {
  296. perror("writepid - ftruncate");
  297. exit(1);
  298. }
  299. if (dprintf(fd, "%ld", (long)getpid()) < 0) {
  300. perror("writepid - dprintf");
  301. exit(1);
  302. }
  303. }
  304. int
  305. main(int argc, char *argv[])
  306. {
  307. writepid();
  308. if (argc > 2)
  309. if (strcmp(argv[1], "-d") == 0)
  310. delim = argv[2];
  311. delimlength = strlen(delim) + 1;
  312. if (!(dpy = XOpenDisplay(NULL))) {
  313. fputs("Error: could not open display.\n", stderr);
  314. return 1;
  315. }
  316. sigemptyset(&blocksigmask);
  317. sigaddset(&blocksigmask, SIGHUP);
  318. sigaddset(&blocksigmask, SIGINT);
  319. sigaddset(&blocksigmask, SIGTERM);
  320. for (Block *current = blocks; current->pathu; current++)
  321. if (current->signal > 0)
  322. sigaddset(&blocksigmask, SIGRTMIN + current->signal);
  323. setupsignals();
  324. statusloop();
  325. unlink(LOCKFILE);
  326. XStoreName(dpy, DefaultRootWindow(dpy), "");
  327. XCloseDisplay(dpy);
  328. return 0;
  329. }