diff options
| author | SoominIm <111330163+SoominIm@users.noreply.github.com> | 2024-03-02 14:45:42 -0600 |
|---|---|---|
| committer | SoominIm <111330163+SoominIm@users.noreply.github.com> | 2024-03-02 14:45:42 -0600 |
| commit | 5017790af4d8d7d9d7c9b41044a34ed015bc570e (patch) | |
| tree | 0c8adc7f95a396ac25c0e57b528f8a3f38b9ec16 /dwmblocks/dwmblocks.c | |
Initial commit
Diffstat (limited to 'dwmblocks/dwmblocks.c')
| -rwxr-xr-x | dwmblocks/dwmblocks.c | 295 |
1 files changed, 295 insertions, 0 deletions
diff --git a/dwmblocks/dwmblocks.c b/dwmblocks/dwmblocks.c new file mode 100755 index 0000000..6983418 --- /dev/null +++ b/dwmblocks/dwmblocks.c @@ -0,0 +1,295 @@ +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include <time.h> +#include <signal.h> +#include <errno.h> +#include <X11/Xlib.h> +#include <sys/signalfd.h> +#include <poll.h> +#define LENGTH(X) (sizeof(X) / sizeof (X[0])) +#define CMDLENGTH 50 + +typedef struct { + char* icon; + char* command; + unsigned int interval; + unsigned int signal; +} Block; +void sighandler(); +void buttonhandler(int ssi_int); +void replace(char *str, char old, char new); +void remove_all(char *str, char to_remove); +void getcmds(int time); +void getsigcmds(int signal); +void setupsignals(); +int getstatus(char *str, char *last); +void setroot(); +void statusloop(); +void termhandler(int signum); + + +#include "config.h" + +static Display *dpy; +static int screen; +static Window root; +static char statusbar[LENGTH(blocks)][CMDLENGTH] = {0}; +static char statusstr[2][256]; +static int statusContinue = 1; +static int signalFD; +static int timerInterval = -1; +static void (*writestatus) () = setroot; + +void replace(char *str, char old, char new) +{ + for(char * c = str; *c; c++) + if(*c == old) + *c = new; +} + +// the previous function looked nice but unfortunately it didnt work if to_remove was in any position other than the last character +// theres probably still a better way of doing this +void remove_all(char *str, char to_remove) { + char *read = str; + char *write = str; + while (*read) { + if (*read != to_remove) { + *write++ = *read; + } + ++read; + } + *write = '\0'; +} + +int gcd(int a, int b) +{ + int temp; + while (b > 0){ + temp = a % b; + + a = b; + b = temp; + } + return a; +} + + +//opens process *cmd and stores output in *output +void getcmd(const Block *block, char *output) +{ + if (block->signal) + { + output[0] = block->signal; + output++; + } + char *cmd = block->command; + FILE *cmdf = popen(cmd,"r"); + if (!cmdf){ + //printf("failed to run: %s, %d\n", block->command, errno); + return; + } + char tmpstr[CMDLENGTH] = ""; + // TODO decide whether its better to use the last value till next time or just keep trying while the error was the interrupt + // this keeps trying to read if it got nothing and the error was an interrupt + // could also just read to a separate buffer and not move the data over if interrupted + // this way will take longer trying to complete 1 thing but will get it done + // the other way will move on to keep going with everything and the part that failed to read will be wrong till its updated again + // either way you have to save the data to a temp buffer because when it fails it writes nothing and then then it gets displayed before this finishes + char * s; + int e; + do { + errno = 0; + s = fgets(tmpstr, CMDLENGTH-(strlen(delim)+1), cmdf); + e = errno; + } while (!s && e == EINTR); + pclose(cmdf); + int i = strlen(block->icon); + strcpy(output, block->icon); + strcpy(output+i, tmpstr); + remove_all(output, '\n'); + i = strlen(output); + if ((i > 0 && block != &blocks[LENGTH(blocks) - 1])){ + strcat(output, delim); + } + i+=strlen(delim); + output[i++] = '\0'; +} + +void getcmds(int time) +{ + const Block* current; + for(int i = 0; i < LENGTH(blocks); i++) + { + current = blocks + i; + if ((current->interval != 0 && time % current->interval == 0) || time == -1){ + getcmd(current,statusbar[i]); + } + } +} + +void getsigcmds(int signal) +{ + const Block *current; + for (int i = 0; i < LENGTH(blocks); i++) + { + current = blocks + i; + if (current->signal == signal){ + getcmd(current,statusbar[i]); + } + } +} + +void setupsignals() +{ + sigset_t signals; + sigemptyset(&signals); + sigaddset(&signals, SIGALRM); // Timer events + sigaddset(&signals, SIGUSR1); // Button events + // All signals assigned to blocks + for (size_t i = 0; i < LENGTH(blocks); i++) + if (blocks[i].signal > 0) + sigaddset(&signals, SIGRTMIN + blocks[i].signal); + // Create signal file descriptor for pooling + signalFD = signalfd(-1, &signals, 0); + // Block all real-time signals + for (int i = SIGRTMIN; i <= SIGRTMAX; i++) sigaddset(&signals, i); + sigprocmask(SIG_BLOCK, &signals, NULL); + // Do not transform children into zombies + struct sigaction sigchld_action = { + .sa_handler = SIG_DFL, + .sa_flags = SA_NOCLDWAIT + }; + sigaction(SIGCHLD, &sigchld_action, NULL); + +} + +int getstatus(char *str, char *last) +{ + strcpy(last, str); + str[0] = '\0'; + for(int i = 0; i < LENGTH(blocks); i++) { + strcat(str, statusbar[i]); + if (i == LENGTH(blocks) - 1) + strcat(str, " "); + } + str[strlen(str)-1] = '\0'; + return strcmp(str, last);//0 if they are the same +} + +void setroot() +{ + if (!getstatus(statusstr[0], statusstr[1]))//Only set root if text has changed. + return; + Display *d = XOpenDisplay(NULL); + if (d) { + dpy = d; + } + screen = DefaultScreen(dpy); + root = RootWindow(dpy, screen); + XStoreName(dpy, root, statusstr[0]); + XCloseDisplay(dpy); +} + +void pstdout() +{ + if (!getstatus(statusstr[0], statusstr[1]))//Only write out if text has changed. + return; + printf("%s\n",statusstr[0]); + fflush(stdout); +} + + +void statusloop() +{ + setupsignals(); + // first figure out the default wait interval by finding the + // greatest common denominator of the intervals + for(int i = 0; i < LENGTH(blocks); i++){ + if(blocks[i].interval){ + timerInterval = gcd(blocks[i].interval, timerInterval); + } + } + getcmds(-1); // Fist time run all commands + raise(SIGALRM); // Schedule first timer event + int ret; + struct pollfd pfd[] = {{.fd = signalFD, .events = POLLIN}}; + while (statusContinue) { + // Wait for new signal + ret = poll(pfd, sizeof(pfd) / sizeof(pfd[0]), -1); + if (ret < 0 || !(pfd[0].revents & POLLIN)) break; + sighandler(); // Handle signal + } +} + +void sighandler() +{ + static int time = 0; + struct signalfd_siginfo si; + int ret = read(signalFD, &si, sizeof(si)); + if (ret < 0) return; + int signal = si.ssi_signo; + switch (signal) { + case SIGALRM: + // Execute blocks and schedule the next timer event + getcmds(time); + alarm(timerInterval); + time += timerInterval; + break; + case SIGUSR1: + // Handle buttons + buttonhandler(si.ssi_int); + return; + default: + // Execute the block that has the given signal + getsigcmds(signal - SIGRTMIN); + break; + } + writestatus(); +} + +void buttonhandler(int ssi_int) +{ + char button[2] = {'0' + ssi_int & 0xff, '\0'}; + pid_t process_id = getpid(); + int sig = ssi_int >> 8; + if (fork() == 0) + { + const Block *current; + for (int i = 0; i < LENGTH(blocks); i++) + { + current = blocks + i; + if (current->signal == sig) + break; + } + char shcmd[1024]; + sprintf(shcmd,"%s && kill -%d %d",current->command, current->signal+34,process_id); + char *command[] = { "/bin/sh", "-c", shcmd, NULL }; + setenv("BLOCK_BUTTON", button, 1); + setsid(); + execvp(command[0], command); + exit(EXIT_SUCCESS); + } +} + + +void termhandler(int signum) +{ + statusContinue = 0; +} + +int main(int argc, char** argv) +{ + for(int i = 0; i < argc; i++) + { + if (!strcmp("-d",argv[i])) + delim = argv[++i]; + else if(!strcmp("-p",argv[i])) + writestatus = pstdout; + } + signal(SIGTERM, termhandler); + signal(SIGINT, termhandler); + statusloop(); + close(signalFD); +} |
