dwmblocks.c 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272
  1. #include<stdlib.h>
  2. #include<stdio.h>
  3. #include<string.h>
  4. #include<unistd.h>
  5. #include<signal.h>
  6. #include <time.h>
  7. #include <math.h>
  8. #include <pthread.h>
  9. #ifndef NO_X
  10. #include<X11/Xlib.h>
  11. #endif
  12. #ifdef __OpenBSD__
  13. #define SIGPLUS (SIGUSR1 + 1)
  14. #define SIGMINUS (SIGUSR1 - 1)
  15. #else
  16. #define SIGPLUS SIGRTMIN
  17. #define SIGMINUS SIGRTMIN
  18. #endif
  19. #define LENGTH(X) (sizeof(X) / sizeof(X[0]))
  20. #define CMDLENGTH 50
  21. #define SDIRLENGTH 256
  22. #define MIN( a, b ) (a < b ? a : b)
  23. #define STATUSLENGTH (LENGTH(blocks) * CMDLENGTH + 1)
  24. typedef struct {
  25. char* icon;
  26. char* command;
  27. unsigned int interval;
  28. unsigned int signal;
  29. } Block;
  30. typedef void(*ThreadCallback)(pthread_t *);
  31. void getcmd(const Block *block, char *output);
  32. void getcmds(int time);
  33. void getsigcmds(unsigned int signal);
  34. #ifndef __OpenBSD__
  35. void dummysighandler(int signum);
  36. #endif
  37. void sighandler(int signum);
  38. void setupsignals(void);
  39. int getstatus(char *str, char *last);
  40. void pstdout(void);
  41. int gcd(int n1, int n2);
  42. void startthreads(void);
  43. void statusloop(void);
  44. void stopthreads(void);
  45. void termhandler(void);
  46. #ifndef NO_X
  47. void setroot(void);
  48. static void (*writestatus) (void) = setroot;
  49. static int setupx(void);
  50. static Display *dpy;
  51. static int screen;
  52. static Window root;
  53. #else
  54. static void (*writestatus) (void) = pstdout;
  55. #endif
  56. #include "blocks.h"
  57. static char statusbar[LENGTH(blocks)][CMDLENGTH] = {0};
  58. static char statusstr[2][STATUSLENGTH];
  59. static int statuscontinue = 1;
  60. static pthread_t main_thread;
  61. static pthread_t threads[LENGTH(thread_callbacks)];
  62. static int initdone = 0;
  63. //opens process *cmd and stores output in *output
  64. void getcmd(const Block *block, char *output) {
  65. strcpy(output, block->icon);
  66. FILE *cmdf = popen(block->command, "r");
  67. if (!cmdf) {
  68. return;
  69. }
  70. int i = strlen(block->icon);
  71. fgets(output + i, CMDLENGTH - i - delimLen, cmdf);
  72. i = strlen(output);
  73. if (i == 0) {
  74. //return if block and command output are both empty
  75. pclose(cmdf);
  76. return;
  77. }
  78. //only chop off newline if one is present at the end
  79. i = output[i - 1] == '\n' ? i - 1 : i;
  80. if (delim[0] != '\0') {
  81. strncpy(output + i, delim, delimLen);
  82. } else {
  83. output[i++] = '\0';
  84. }
  85. pclose(cmdf);
  86. }
  87. void getcmds(int time) {
  88. const Block* current;
  89. for (unsigned int i = 0; i < LENGTH(blocks); ++i) {
  90. current = blocks + i;
  91. if ((current->interval != 0 && time % current->interval == 0) || time == -1) {
  92. getcmd(current, statusbar[i]);
  93. }
  94. }
  95. }
  96. void getsigcmds(unsigned int signal) {
  97. const Block *current;
  98. for (unsigned int i = 0; i < LENGTH(blocks); ++i) {
  99. current = blocks + i;
  100. if (current->signal == signal) {
  101. getcmd(current, statusbar[i]);
  102. }
  103. }
  104. }
  105. #ifndef __OpenBSD__
  106. /* this signal handler should do nothing */
  107. void dummysighandler(int signum) {
  108. return;
  109. }
  110. #endif
  111. void sighandler(int signum) {
  112. getsigcmds(signum-SIGPLUS);
  113. writestatus();
  114. }
  115. void setupsignals() {
  116. #ifndef __OpenBSD__
  117. /* initialize all real time signals with dummy handler */
  118. for (int i = SIGRTMIN; i <= SIGRTMAX; ++i) {
  119. signal(i, dummysighandler);
  120. }
  121. #endif
  122. for (unsigned int i = 0; i < LENGTH(blocks); ++i) {
  123. if (blocks[i].signal > 0) {
  124. signal(SIGMINUS + blocks[i].signal, sighandler);
  125. }
  126. }
  127. }
  128. int getstatus(char *str, char *last) {
  129. strcpy(last, str);
  130. str[0] = '\0';
  131. for (unsigned int i = 0; i < LENGTH(blocks); ++i) {
  132. strcat(str, statusbar[i]);
  133. }
  134. str[strlen(str)-strlen(delim)] = '\0';
  135. return strcmp(str, last); //0 if they are the same
  136. }
  137. void pstdout() {
  138. if (!initdone || !getstatus(statusstr[0], statusstr[1])) { //Only write out if text has changed.
  139. return;
  140. }
  141. printf("%s\n", statusstr[0]);
  142. fflush(stdout);
  143. }
  144. void somebar_pstdout() {
  145. if (!initdone || !getstatus(statusstr[0], statusstr[1])) { //Only write out if text has changed.
  146. return;
  147. }
  148. printf("status %s\n", statusstr[0]);
  149. fflush(stdout);
  150. }
  151. int gcd(int n1, int n2) {
  152. int temp;
  153. while (n2 > 0){
  154. temp = n1 % n2;
  155. n1 = n2;
  156. n2 = temp;
  157. }
  158. return n1;
  159. }
  160. void startthreads() {
  161. main_thread = pthread_self();
  162. for (int i = 0; i < LENGTH(thread_callbacks); ++i) {
  163. pthread_create(&threads[i],
  164. NULL,
  165. (void *(*)(void *)) thread_callbacks[i],
  166. &main_thread);
  167. }
  168. }
  169. void statusloop() {
  170. setupsignals();
  171. unsigned int interval = -1;
  172. for (int i = 0; i < LENGTH(blocks); ++i){
  173. if (blocks[i].interval){
  174. interval = gcd(blocks[i].interval, interval);
  175. }
  176. }
  177. startthreads();
  178. int i = 0;
  179. getcmds(-1);
  180. long nsec_part = interval * 1000000;
  181. time_t sec_part = floor(nsec_part / 1000000000.0);
  182. nsec_part -= sec_part * 1000000000l;
  183. const struct timespec sleeptime = { sec_part, nsec_part };
  184. struct timespec tosleep = sleeptime;
  185. while (statuscontinue) {
  186. initdone = 1;
  187. if (nanosleep(&tosleep, &tosleep) < 0) {
  188. continue;
  189. }
  190. getcmds((i++) * interval);
  191. writestatus();
  192. tosleep = sleeptime;
  193. }
  194. }
  195. void stopthreads() {
  196. for (int i = 0; i < LENGTH(thread_callbacks); ++i) {
  197. pthread_cancel(threads[i]);
  198. pthread_join(threads[i], NULL);
  199. }
  200. }
  201. void termhandler() {
  202. statuscontinue = 0;
  203. }
  204. #ifndef NO_X
  205. void setroot() {
  206. if (!initdone || !getstatus(statusstr[0], statusstr[1])) { //Only set root if text has changed.
  207. return;
  208. }
  209. XStoreName(dpy, root, statusstr[0]);
  210. XFlush(dpy);
  211. }
  212. int setupx() {
  213. dpy = XOpenDisplay(NULL);
  214. if (!dpy) {
  215. fprintf(stderr, "dwmblocks: Failed to open display\n");
  216. return 0;
  217. }
  218. screen = DefaultScreen(dpy);
  219. root = RootWindow(dpy, screen);
  220. return 1;
  221. }
  222. #endif
  223. int main(int argc, char** argv) {
  224. for (int i = 0; i < argc; ++i) { //Handle command line arguments
  225. if (strcmp("-d", argv[i]) == 0) {
  226. strncpy(delim, argv[++i], delimLen);
  227. } else if (strcmp("-p", argv[i]) == 0) {
  228. writestatus = pstdout;
  229. } else if (strcmp("-s", argv[i]) == 0) {
  230. writestatus = somebar_pstdout;
  231. }
  232. }
  233. #ifndef NO_X
  234. if (!setupx()) {
  235. return 1;
  236. }
  237. #endif
  238. delimLen = MIN(delimLen, strlen(delim));
  239. delim[delimLen++] = '\0';
  240. signal(SIGTERM, (void(*)(int))termhandler);
  241. signal(SIGINT, (void(*)(int))termhandler);
  242. statusloop();
  243. #ifndef NO_X
  244. XCloseDisplay(dpy);
  245. #endif
  246. stopthreads();
  247. return 0;
  248. }