diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0976ab1 --- /dev/null +++ b/.gitignore @@ -0,0 +1,10 @@ +*.swp +*.tmp +c/scream +c/xgetnewwindow +c/timer +c/boid +c/a.out +c/anaconda +c/colors +c/tmessage diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..5c1023e --- /dev/null +++ b/Makefile @@ -0,0 +1,75 @@ +include config.mk +all: mkc +install: man sh c all +.PHONY: man sh mkc c + +man: + mkdir -p $(DESTDIR)$(PREFIX)/man/man1 + cp -f man/* $(DESTDIR)$(PREFIX)/man/man1 + +sh: + mkdir -p $(DESTDIR)$(PREFIX)/bin + cp -f sh/paste $(DESTDIR)$(PREFIX)/bin + cp -f sh/snapcad $(DESTDIR)$(PREFIX)/bin + cp -f sh/sfeed_yt_add $(DESTDIR)$(PREFIX)/bin + cp -f sh/disp $(DESTDIR)$(PREFIX)/bin + cp -f sh/wallpaper $(DESTDIR)$(PREFIX)/bin + cp -f sh/connect $(DESTDIR)$(PREFIX)/bin + cp -f sh/nws $(DESTDIR)$(PREFIX)/bin + cp -f sh/vol $(DESTDIR)$(PREFIX)/bin + cp -f sh/pco $(DESTDIR)$(PREFIX)/bin + cp -f sh/git-survey $(DESTDIR)$(PREFIX)/bin + cp -f sh/vim-swap-handler $(DESTDIR)$(PREFIX)/bin + cp -f sh/status $(DESTDIR)$(PREFIX)/bin + cp -f sh/statusbar $(DESTDIR)$(PREFIX)/bin + cp -f sh/cfg $(DESTDIR)$(PREFIX)/bin + cp -f sh/fire $(DESTDIR)$(PREFIX)/bin + cp -f sh/pash-dmenu $(DESTDIR)$(PREFIX)/bin + cp -f sh/pash-dmenu-backend $(DESTDIR)$(PREFIX)/bin + cp -f sh/tmenu $(DESTDIR)$(PREFIX)/bin + cp -f sh/tmenu-backend $(DESTDIR)$(PREFIX)/bin + cp -f sh/tmenu_run $(DESTDIR)$(PREFIX)/bin + cp -f sh/ss $(DESTDIR)$(PREFIX)/bin + cp -f sh/net $(DESTDIR)$(PREFIX)/bin + cp -f sh/bspwm-toggle-gaps $(DESTDIR)$(PREFIX)/bin + cp -f sh/machine $(DESTDIR)$(PREFIX)/bin + cp -f sh/brightness $(DESTDIR)$(PREFIX)/bin + cp -f sh/git-credential-gitpass $(DESTDIR)$(PREFIX)/bin + cp -f sh/capture $(DESTDIR)$(PREFIX)/bin + cp -f sh/toggle-contingency-mode $(DESTDIR)$(PREFIX)/bin + cp -f sh/keyboard $(DESTDIR)$(PREFIX)/bin + ln -sf $(DESTDIR)$(PREFIX)/bin/tmenu_run $(DESTDIR)$(PREFIX)/bin/regenerate + cp -f sh/discord $(DESTDIR)$(PREFIX)/bin + +check: + shellcheck sh/* + +mkc: c/scream c/timer c/boid c/anaconda c/colors c/xgetnewwindow c/tmessage + +c/boid: + cc c/boid.c -o c/boid -lm -lX11 + +c/anaconda: + cc c/anaconda.c -o c/anaconda -lm -lX11 + +c/xgetnewwindow: + cc c/xgetnewwindow.c -o c/xgetnewwindow -lX11 + +c: + cp -f c/scream $(DESTDIR)$(PREFIX)/bin + cp -f c/timer $(DESTDIR)$(PREFIX)/bin + cp -f c/boid $(DESTDIR)$(PREFIX)/bin + cp -f c/anaconda $(DESTDIR)$(PREFIX)/bin + cp -f c/colors $(DESTDIR)$(PREFIX)/bin + cp -f c/xgetnewwindow $(DESTDIR)$(PREFIX)/bin + cp -f c/tmessage $(DESTDIR)$(PREFIX)/bin + +clean: + rm -f c/scream + rm -f c/timer + rm -f c/boid + rm -f c/anaconda + rm -f c/colors + rm -f c/simplestatus + rm -f c/xgetnewwindow + rm -f c/tmessage diff --git a/TODO b/TODO new file mode 100644 index 0000000..0d8b47b --- /dev/null +++ b/TODO @@ -0,0 +1,5 @@ +TODO LIST +--------- + +- fix paste +- make the c programs less buggy and let them not suck as much diff --git a/c/anaconda.c b/c/anaconda.c new file mode 100644 index 0000000..2018e4c --- /dev/null +++ b/c/anaconda.c @@ -0,0 +1,247 @@ +#include +#include +#include +#include +#include +#include + +#include + +typedef struct point { + double x; + double y; + struct point *next; +} point; + +typedef struct anaconda { + int score; + double rot; + struct point *chain; +} anaconda; + +Display *d; +Window w; +XEvent e; +int s; +GC gc; + +void xinit(void) { + d = XOpenDisplay(NULL); + s = DefaultScreen(d); + w = XCreateSimpleWindow(d, RootWindow(d, s), 10, 10, 100, 100, 1, + BlackPixel(d, s), WhitePixel(d, s)); + XSelectInput(d, w, ExposureMask | KeyPressMask | PointerMotionMask); + XMapWindow(d, w); + gc = XCreateGC(d, w, 0, NULL); +} + +/* thanks to + * https://stackoverflow.com/questions/3838329/how-can-i-check-if-two-segments-intersect/9997374#9997374 + * for functions ccw and intersect +*/ + +int ccw(point *a, point *b, point *c) { + return (c->y - a->y) * (b->x - a->x) > + (b->y - a->y) * (c->x - a->x); +} + +int intersect(point *a, point *b, point *c, point *d) { + return (ccw(a, c, d) != ccw(b, c, d)) && (ccw(a, b, c) != ccw(a, b, d)); +} + +int randrange(int b, int s) { + return rand() % (b - s + 1) + s; +} + +int eucliddist(point *a, point *b) { + return sqrt(pow(a->x - b->x, 2) + pow(a->y - b->y, 2)); +} + +point *mkPoint(double x, double y) { + point *ret = malloc(sizeof *ret); + + ret->x = x; + ret->y = y; + ret->next = NULL; + + return ret; +} + +point *rotate(point *p, double rot) { + double rad = rot * M_PI/180; + + return mkPoint( + p->x * cos(rad) - p->y * sin(rad), + p->x * sin(rad) + p->y * cos(rad) + ); +} + +void appendPoint(point *dest, point *origin) { + while(dest->next) dest = dest->next; + dest->next = origin; +} + +int updateAnaconda(anaconda *anaconda, point *basepoint, int w, int h, point *apple) { + point *temp, *new, *ptr; + new = anaconda->chain; + + temp = rotate(basepoint, anaconda->rot); + new = mkPoint( + new->x + temp->x, + new->y + temp->y + ); + new->next = anaconda->chain; + anaconda->chain = new; + + free(temp); + + if(eucliddist(new, apple) <= 30) { + anaconda->score += 30; + apple->x = randrange(20, w / 2); + apple->y = randrange(20, h / 2); + } else { + ptr = new; + + while(1) { + if(ptr->next) { + if(ptr->next->next) ptr = ptr->next; + else break; + } else break; + } + free(ptr->next); + ptr->next = NULL; + } + + ptr = anaconda->chain; + for(int i = 0; i < 3; i++) { + if(ptr->next) ptr = ptr->next; + else return 1; /* we're fine, the snake is too short to intersect itself */ + } + + while(ptr->next) { + if(intersect(new, new->next, ptr, ptr->next)) return 0; + ptr = ptr->next; + } + + if( + new->x >= w || new->x <= 0 || + new->y >= h || new->y <= 0 + ) return 0; + + if(eucliddist(new, apple) <= 30) { + anaconda->score += 30; + apple->x = randrange(20, w / 2); + apple->y = randrange(20, h / 2); + } + + return 1; +} + +void freeAnaconda(anaconda *anaconda) { + point *current = anaconda->chain; + point *next = NULL; + for(;;) { + if(!current) break; + next = current->next; + free(current); + current = next; + } + + free(anaconda); + + return; +} + +point *generateChain(int length) { + point *ret = mkPoint(100, 100); + point *head = ret; + + for(int i = 1; i < length - 1; i++) { + ret->next = mkPoint( + 10 * i + 100, + 5 * i + 100 + ); + ret = ret->next; + } + + return head; +} + +anaconda *mkAnaconda(point *point, double rot) { + anaconda *ret = malloc(sizeof *ret); + + ret->chain = point; + ret->rot = rot; + ret->score = 0; + + return ret; +} + +int main(void) { + anaconda *anaconda = mkAnaconda(generateChain(30), 0); + point *basepoint = mkPoint(10, 0); + char scorebuffer[30]; + xinit(); + srand(time(0)); + int width = DisplayWidth(d, s); + int height = DisplayHeight(d, s); + point *apple = mkPoint(randrange(20, width / 2 - 20), randrange(20, height / 2 - 20)); + int exposed = 0; + while(1) { + if(exposed) { + if(!updateAnaconda(anaconda, basepoint, width, height, apple)) { + freeAnaconda(anaconda); + free(apple); + free(basepoint); + return 0; + } + XClearWindow(d, w); + point *ptr = anaconda->chain; + while(ptr->next) { + XDrawLine(d, w, gc, ptr->x, ptr->y, ptr->next->x, ptr->next->y); + ptr = ptr->next; + } + XDrawArc(d, w, gc, apple->x - (5/2), apple->y - (5/2), 5, 5, 0, 360*64); + int len = snprintf(&scorebuffer, 30, "%i", anaconda->score); + XDrawString(d, w, gc, 25, 25, &scorebuffer, len); + } + while(XPending(d)) { + XNextEvent(d, &e); + switch(e.type) { + case KeyPress: + switch(e.xkey.keycode) { + case 113: /* left arrow key */ + anaconda->rot += 10; + break; + case 114: /* right arrow key */ + anaconda->rot -= 10; + break; + } + break; + case Expose: + /* hold off drawing until we get an expose event */ + exposed = 1; + + width = e.xexpose.width; + height = e.xexpose.height; + + if(apple->x > width) { + free(apple); + apple = mkPoint(randrange(20, width / 2 - 20), randrange(20, height / 2 - 20)); + } else if (apple->y > height) { + free(apple); + apple = mkPoint(randrange(20, width / 2 - 20), randrange(20, height / 2 - 20)); + } + + break; + } + } + usleep(100000); + } + + freeAnaconda(anaconda); + free(apple); + free(basepoint); + + return 0; +} diff --git a/c/boid.c b/c/boid.c new file mode 100644 index 0000000..629d3fc --- /dev/null +++ b/c/boid.c @@ -0,0 +1,206 @@ +#include +#include +#include +#include +#include +#include + +#include + +#define MIN(X, Y) (((X) < (Y)) ? (X) : (Y)) + +const double sprite[][2] = { + {0, 1}, + {-0.5, -1}, + {0, -0.5}, + {0.5, -1}, +}; +#define SIZE sizeof sprite / sizeof sprite[0] + +double buffer[SIZE][2]; +int boidcounter = 0; + +Display *d; +Window w; +XEvent e; +int s; +GC gc; + +typedef struct point { + double x; + double y; +} point; + +typedef struct boid { + int id; + point *p; + double rot; + struct boid *next; +} boid; + +void xinit(void) { + d = XOpenDisplay(NULL); + s = DefaultScreen(d); + w = XCreateSimpleWindow(d, RootWindow(d, s), 10, 10, 100, 100, 1, + BlackPixel(d, s), WhitePixel(d, s)); + XSelectInput(d, w, ExposureMask | KeyPressMask | PointerMotionMask); + XMapWindow(d, w); + gc = XCreateGC(d, w, 0, NULL); +} + +int sign(double x) { + return (x > 0) - (x < 0); +} + +point *rotate(double x, double y, double rot) { + point *ret = malloc(sizeof ret); + double rad = rot * M_PI/180; + + ret->x = x * cos(rad) - y * sin(rad); + ret->y = x * sin(rad) + y * cos(rad); + + return ret; +} + +double distance(point *a, point *b) { + return sqrt(pow(a->x - b->x, 2) + pow(a->y - b->y, 2)); +} + +void calculateRender( + double x, + double y, + double rot, + int scale +) { + memcpy(&buffer, &sprite, SIZE); + + for(int i = 0; i < SIZE; i++) { + point *p = rotate(sprite[i][0], sprite[i][1], rot); + + buffer[i][0] = p->x * scale + x; + buffer[i][1] = p->y * scale + y; + + free(p); + } +} + +void renderBuffer(void) { + for(int i = 0; i < SIZE - 1; i++) { + XDrawLine(d, w, gc, + buffer[i][0], buffer[i][1], + buffer[i + 1][0], buffer[i + 1][1]); + } + XDrawLine(d, w, gc, + buffer[0][0], buffer[0][1], + buffer[SIZE - 1][0], buffer[SIZE - 1][1]); +} + +void renderBoid(boid *boid) { + calculateRender( + boid->p->x, + boid->p->y, + boid->rot, + 5 + ); + renderBuffer(); +} + +boid *mkBoid(double x, double y, double rot) { + boid *b = malloc(sizeof b); + point *p = malloc(sizeof p); + b->p = p; + + b->p->x = x; + b->p->y = y; + b->rot = rot; + b->next = NULL; + b->id = boidcounter++; + + return b; +} + +void appendBoid(boid *destination, boid *source) { + destination->next = source; +} + +double averageHeading(boid *b) { + boid *c = b; + double sum; + int count; + while(c) { + sum += c->rot; + count++; + c = c->next; + } + return sum / count; +} + +void randomBoids(boid *b, int num) { + srand(time(0)); + boid *ptr = b; + for(int i = 0; i < num; i++) { + int w = rand() % DisplayWidth(d, s) + 1; + int h = rand() % DisplayHeight(d, s) + 1; + int deg = rand() % 360; + appendBoid(ptr, mkBoid(w, h, deg)); + ptr = ptr->next; + } +} + +void updateBoid(boid *b, boid *chain, int id, double average) { + point *p = rotate(5, 0, b->rot + 90); + b->p->x = b->p->x + p->x; + b->p->y = b->p->y + p->y; + double toTurn = 0; + + boid *c = chain; + while(c) { + if(c->id != id) { + int dist = distance(c->p, b->p); + if(dist < 50) toTurn += dist / 75; + } + int w = DisplayWidth(d, s); + int h = DisplayHeight(d, s); + int centerw = w / 2 - b->p->x; + int centerh = h / 2 - b->p->y; + double deg = atan((double)centerh / (double)centerw) * 180/M_PI; + toTurn += sign(deg) * 0.5; + toTurn += (average - c->rot) * 0.4; + if(b->p->x > w) b->p->x = 0; + else if(b->p->x < 0) b->p->x = w; + if(b->p->y > h) b->p->y = 0; + else if(b->p->y < 0) b->p->y = h; + + toTurn = MIN(toTurn / 4, 30); + + b->rot += toTurn; + c = c->next; + } + + free(p); +} + +int main() { + xinit(); + boid *b = mkBoid(100, 100, 0); + randomBoids(b, 100); + while(1) { + XClearWindow(d, w); + boid *ptr = b; + while(ptr) { + updateBoid(ptr, b, ptr->id, averageHeading(b)); + renderBoid(ptr); + XFlush(d); + ptr = ptr->next; + } + while(XPending(d)) { + XNextEvent(d, &e); + switch(e.type) { + case KeyPress: + break; + } + } + usleep(50000); + } + return 0; +} diff --git a/c/colors.c b/c/colors.c new file mode 100644 index 0000000..b6f52b5 --- /dev/null +++ b/c/colors.c @@ -0,0 +1,286 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct color { + int r; + int g; + int b; +}; + +void init_random(void) { + srand(time(0)); +} + +int rand_range(int u, int l) { + return (rand() % (u - l + 1)) + l; +} + +void exec_with_stdout(char *file, char *args[], char *buf, int length) { + int pfds[2]; + pipe(pfds); + int status = fork(); + + if(status == 0) { + close(1); + dup(pfds[1]); + close(pfds[0]); + + execvp(file, args); + } else { + if(!buf) return; /* don't read anything */ + int readin = read(pfds[0], buf, length - 1); + buf[readin] = '\0'; + } +} + +void imagemagick(char *filename, char *buffer, int size) { + char *execargs[] = { + "convert", + filename, + "-resize", + "25%", + "-colors", + "16", + "-unique-colors", + "-quiet", + "txt:-", + NULL + }; + char *name = "convert"; + + exec_with_stdout(name, execargs, buffer, size); +} + +struct color *get_raw_colors(char *image) { + char buf[2048]; + + imagemagick(image, (char *)&buf, 2048); + int hcount = 0; + char cbuf[3][3]; + static struct color colors[16]; + + for(int i = 0; (unsigned int)i < sizeof buf; i++) { + if(buf[i] == '#') { + hcount++; /* we have to ignore a comment, that's why there's a special case */ + if(hcount >= 2 && hcount - 2 < 16 && (unsigned int)(i + 6) < sizeof buf) { + memcpy((char *)&cbuf[0], buf + i + 1, 2); + memcpy((char *)&cbuf[1], buf + i + 3, 2); + memcpy((char *)&cbuf[2], buf + i + 5, 2); + cbuf[0][2] = '\0'; + cbuf[1][2] = '\0'; + cbuf[2][2] = '\0'; + colors[hcount - 2].r = (int)strtol((char *)&cbuf[0], NULL, 16); + colors[hcount - 2].g = (int)strtol((char *)&cbuf[1], NULL, 16); + colors[hcount - 2].b = (int)strtol((char *)&cbuf[2], NULL, 16); + } + } + } + + return (struct color *)&colors; +} + +void blend_color(struct color *a, struct color *b, struct color *c) { + c->r = (int)(0.5 * a->r + 0.5 * b->r); + c->g = (int)(0.5 * a->g + 0.5 * b->g); + c->b = (int)(0.5 * a->b + 0.5 * b->b); +} + +void darken_color(struct color *a, struct color *b, double percentage) { + b->r = (int)(a->r * (1 - percentage)); + b->g = (int)(a->g * (1 - percentage)); + b->b = (int)(a->b * (1 - percentage)); +} + +void adjust_colors(struct color *colors) { + /* #eeeeee */ + struct color e = {238, 238, 238}; + + /* if top digit != 0 */ + if(colors[0].r > 15) + darken_color(&colors[0], &colors[0], 0.40); + + blend_color(&colors[7], &e, &colors[7]); + darken_color(&colors[7], &colors[8], 0.30); + blend_color(&colors[15], &e, &colors[15]); +} + +int check_colors(struct color *colors) { + int j = 0; + for(int i = 0; i < 16; i++) { + if(colors[i].r + colors[i].g + colors[i].b == 0) j++; + } + if(j > 0) return 0; + return 1; +} + +struct color *get_colors(char *filename) { + /* check for permission */ + if(access(filename, F_OK | R_OK) != 0) { + errno = ENOENT; + return NULL; + } + + struct color *col = get_raw_colors(filename); + adjust_colors(col); + + return col; +} + +/* generalized function , + * used by get_conf_file and get_wal_dir */ +char *get_file_gen(char *initvar, char *backvar, char *postfix, char *file) { + static char buffer[256]; + int extension = 0; + char *path = getenv(initvar); + if(!path) { + path = getenv(backvar); + extension = 1; + } + if(!path) { + fprintf(stderr, "error: both initvar and backvar are undefined.\n"); + exit(1); + } + + snprintf(buffer, 256, "%s%s/%s", path, extension ? postfix : "", file); + return buffer; +} + +char *get_conf_file(char *file) { + return get_file_gen("XDG_CONFIG_HOME", "HOME", "/.config/cwal", file); +} + +/* pass NULL to get the base dir name, not specific file */ +char *get_wal_dir(char *file) { + if(!file) file = ""; + + return get_file_gen("XDG_DATA_HOME", "HOME", "/.local/share/wallpapers", file); +} + +char *select_random_rel(void) { + DIR *dir = opendir(get_wal_dir(NULL)); + struct dirent *file; + /* probably should move hardcoded constants somewhere that makes sense */ + static char names[50][256]; + int i = 0, random; + + init_random(); + + while(file = readdir(dir)) { + if(i == 50) break; + if(file->d_type != DT_REG) continue; + + memcpy(&names[i], file->d_name, 256); + i++; + } + + random = rand_range(i - 1, 0); + + return names[random]; +} + +void print_color(struct color *color) { + printf("#%02x%02x%02x\n", color->r, color->g, color->b); +} + +void run_handler(struct color *colors, char *wal) { + char chars[16][8]; + char *argv[19]; + + for(int i = 0; i < 16; i++) { + snprintf(&chars[i], 8, "#%02x%02x%02x", colors[i].r, colors[i].g, colors[i].b); + } + for(int i = 0; i < 16; i++) { + argv[i + 1] = &chars[i]; + } + argv[17] = wal; + argv[18] = NULL; + + char *conf = get_conf_file("handler"); + argv[0] = "handler"; + + if(access(conf, F_OK | R_OK | X_OK) != 0) { + printf("couldn't find %s!\n", conf); + return; + } + + exec_with_stdout(conf, argv, NULL, 0); +} + +struct settings { + int h:1; /* running hooks or not */ + int e:1; /* echo colors */ + int c:1; /* enable color check */ + int f:1; /* print filename used */ + char *wal; /* custom file if provided */ +} settings = { + .h = 1, + .e = 0, + .c = 0, + .f = 0, + .wal = NULL, +}; + +char *select_random_file(char *file) { + if(!file) file = select_random_rel(); + static char ret[256]; + file = get_wal_dir(file); + memcpy(&ret, file, 256); + + return ret; +} + +int main(int argc, char **argv) { + int c; + while((c = getopt(argc, argv, "hefcw:")) != -1) { + switch(c) { + case 'h': + settings.h = 0; + break; + case 'w': + settings.wal = optarg; + break; + case 'e': + settings.e = 1; + break; + case 'c': + settings.c = 1; + break; + case 'f': + settings.f = 1; + break; + } + } + + if(settings.h == 0) settings.e = 1; + + char *file = select_random_file(settings.wal); + struct color *colors = get_colors(file); + + if(settings.f) printf("%s\n", file); + + if(settings.e) { + for(int i = 0; i < 16; i++) { + print_color(&colors[i]); + } + } + + if(settings.c) { + c = check_colors(colors); + if(c == 0) { + return 1; + } + } + + if(settings.h) run_handler(colors, file); + + return 0; +} diff --git a/c/scream.c b/c/scream.c new file mode 100644 index 0000000..fe00aa7 --- /dev/null +++ b/c/scream.c @@ -0,0 +1,21 @@ +#include +#include + +/* written on new year's eve 2020 + * good night and good riddance */ + +char newchar(char c, int i) { + if(i % 2) { + if(islower(c)) return c - 32; + } else if(isupper(c)) return c + 32; + if(c == 33) return -2; + return c; +} +int main(void) { + char c; + int i = 0; + while((c = getchar()) != EOF) { + putchar(newchar(c, i)); + i++; + } +} diff --git a/c/timer.c b/c/timer.c new file mode 100644 index 0000000..256570a --- /dev/null +++ b/c/timer.c @@ -0,0 +1,174 @@ +#include +#include +#include +#include +#include + +struct timer { + int s; /* seconds */ + int d; /* data storage */ + void (*u)(struct timer *t); /* update function */ + int (*c)(struct timer *t); /* stop check function */ + int (*p)(struct timer *t); /* pause check function */ +}; + +struct settings { + unsigned int e:1; /* use escape (v assumed) */ + unsigned int v:1; /* verbose */ + unsigned int d:1; /* count down/up (1/0) */ + unsigned int b:1; /* ascii bel when done */ + unsigned int f:1; /* display hours */ + unsigned int t:1; /* tomato timer */ + unsigned int p:1; /* stop after tomato timer cycle finished */ + int s; /* seconds */ +} s = { + .e = 0, + .v = 0, + .d = 0, + .b = 0, + .f = 0, + .t = 0, + .p = 0, + .s = 0 +}; + +void timerdec(struct timer *t) { if (t->s) t->s--; } +void timerinc(struct timer *t) { t->s++; } +void timerupdate(struct timer *t) { if(t->u) t->u(t); } + +int timerstate(int (*f)(struct timer *t), struct timer *t) { + if(f) { + if(f(t)) return 1; + else return 0; + } + return 0; +} + +int timerstop(struct timer *t) { return timerstate(t->c, t); } +int timerpause(struct timer *t) { return timerstate(t->p, t); } + +int timerzero(struct timer *t) { + if(!t->s) return 1; + return 0; +} + +int seconds(int t) { return t % 60; } +int minutes(int t) { return (t / 60) % 60; } +int minute(int t) { return t / 60; } +int hours(int t) { return (t / 60) / 60; } + +struct timer *timerinit(void) { + struct timer *t = malloc(sizeof *t); + t->s = 0; + t->d = 0; + t->u = NULL; + t->c = NULL; + t->p = NULL; + return t; +} + +int timerissettings(struct timer *t) { + if(!t->s) return 0; + if(s.s == t->s) return 1; + return 0; +} + +int tomatotimer(struct timer *t) { + if(t->s) return 0; + if(t->d % 2) t->s = s.s / 2; + else t->s = s.s; + t->d++; + if(s.b) putchar('\a'); + if(s.p) return 0; + return 1; +} + +char *timerdisp(struct timer *t) { + char *str = malloc(20); + if(s.f) snprintf(str, 20, "%02i:%02i:%02i", + hours(t->s), minutes(t->s), seconds(t->s)); + /* TODO: give minute(...) and minutes(...) better names */ + else snprintf(str, 20, "%02i:%02i", minute(t->s), seconds(t->s)); + return str; +} + +void defaultSettings(struct settings s) { + s.e = 1; + s.b = 1; + s.f = 1; +} + +void timerloop() { + struct timer *t = timerinit(); + if(s.d) { + t->u = timerdec; + t->s = s.s; + t->c = timerzero; + } else { + t->u = timerinc; + t->c = timerissettings; + } + if(s.t) { + t->u = timerdec; + if(s.s == 0) s.s = 20 * 60; + t->s = s.s; + t->d = 1; + t->p = tomatotimer; + t->c = NULL; + } + + char *c; + int e; + struct pollfd p = { .fd = STDIN_FILENO, .events = POLLIN }; + for(;;) { + poll(&p, 1, 60); + if((e = (p.revents == POLLIN)) || timerpause(t)) { + /* TODO: make this nicer */ + if(e) getchar(); + if(s.e) { + c = timerdisp(t); + if(e) printf("\r\e[1A* %s", c); + else printf("\r* %s", c); + } + getchar(); + /* TODO: stop relying on hard assumptions */ + if(s.e) printf("\r\e[1A \r"); + } + c = timerdisp(t); + if(s.e) { + printf("%s\r", c); + fflush(stdout); + } + else if(s.v) printf("%s\n", c); + if(timerstop(t)) break; + free(c); + timerupdate(t); + sleep(1); + } + if(s.b) putchar('\a'); + free(t); + free(c); +} + +int main(int argc, char **argv) { + char c; + while((c = getopt(argc, argv, "evdbftpzh:m:s:")) != -1) { + switch(c) { + break; case 'e': s.e = 1; + break; case 'v': s.v = 1; + break; case 'd': s.d = 1; + break; case 'b': s.b = 1; + break; case 'f': s.f = 1; + break; case 't': s.t = 1; + break; case 'p': s.p = 1; + break; case 'z': s.e = 1; s.b = 1; s.f = 1; + break; case 'h': s.s = s.s + (atoi(optarg) * 3600); + break; case 'm': s.s = s.s + (atoi(optarg) * 60); + break; case 's': s.s = s.s + atoi(optarg); + break; case '?': return 1; + break; default: abort(); + } + } + timerloop(); + return 0; +} diff --git a/c/tmessage.c b/c/tmessage.c new file mode 100644 index 0000000..e696d4b --- /dev/null +++ b/c/tmessage.c @@ -0,0 +1,148 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#define ESC_CLEAR "\033[2J" +#define ESC_CUSHOME "\033[H" +#define ESC_NEWLINE "\033[E\033[1C" + +const char MSG_OK[] = "(O)k"; +const char MSG_CANCEL[] = "(C)ancel"; + +typedef struct winsize ws; + +int lines, cols; +char *message; +size_t message_len; + +struct termios original_termios; + +void render(char *message, int message_len, int lines, int cols); + +void rawmode_start() { + tcgetattr(STDIN_FILENO, &original_termios); + struct termios raw = original_termios; + raw.c_lflag &= ~(ECHO | ICANON); + tcsetattr(STDIN_FILENO, TCSAFLUSH, &raw); +} + +void rawmode_stop() { + tcsetattr(STDIN_FILENO, TCSAFLUSH, &original_termios); + printf("\n\n"); + fflush(stdout); +} + +ws *getWinSize(void) { + /* we're only going to use these values once per invocation, + * so it's fine that it's static. */ + static ws w; + ioctl(STDOUT_FILENO, TIOCGWINSZ, &w); + + return &w; +} + +void handler(int signal, siginfo_t *info, void *context) { + ws *w = getWinSize(); + lines = w->ws_row; + cols = w->ws_col; + + render(message, message_len, lines, cols); + return; +} + +void termhandler(int signal, siginfo_t *info, void *context) { + rawmode_stop(); + + return; +} + +int calculate_number_of_lines(int length, int cols) { + /* cols - 1 accounts for right padding */ + return length / (cols - 1) + 1; +} + +void go_to_initial_message_printing_pos(void) { + printf(ESC_CUSHOME "\033[1B\033[1C"); +} + +void print_message(char *message, int messagelen, int cols) { + int linecount = calculate_number_of_lines(messagelen, cols); + int adjcols = cols - 2; + int offset = 1; + for(int character = 0; character < messagelen; character++) { + if(character == adjcols * offset) { + printf(ESC_NEWLINE); + offset++; + } + putchar(message[character]); + } +} + +void render(char *message, int messagelen, int lines, int cols) { + int cancel_length = sizeof(MSG_CANCEL) / sizeof(MSG_CANCEL[0]); + + /* print the stuff */ + printf(ESC_CLEAR "" ESC_CUSHOME); + go_to_initial_message_printing_pos(); + print_message(message, message_len, cols); + + printf(ESC_NEWLINE); + printf(ESC_NEWLINE); + printf("%s", MSG_OK); + printf("\033[1D\033[%iC\033[%iD%s", cols, cancel_length, MSG_CANCEL); + fflush(stdout); +} + +int main(int argc, char **argv) { + int cancel_length = strlen(MSG_CANCEL); + char c; + struct sigaction resizeaction = {}; + struct sigaction termaction = {}; + ws *w; + + /* check if we have a message */ + if(argc > 1) { + message = argv[1]; /* second argument's a message */ + message_len = strlen(message); + } else { + return 1; + } + + /* setup sigaction handlers */ + resizeaction.sa_sigaction = &handler; + if(sigaction(SIGWINCH, &resizeaction, NULL) == -1) { + return 1; + } + + termaction.sa_sigaction = &termhandler; + if(sigaction(SIGTERM, &termaction, NULL) == -1) { + return 1; + } + + rawmode_start(); + + /* get window properties */ + w = getWinSize(); + lines = w->ws_row; + cols = w->ws_col; + + render(message, message_len, lines, cols); + + for(;;) { + read(STDIN_FILENO, &c, 1); + if(c == 'o') { + return 2; + rawmode_stop(); + } else if(c == 'c') { + return 3; + rawmode_stop(); + } + } + + return 0; +} diff --git a/c/xgetnewwindow.c b/c/xgetnewwindow.c new file mode 100644 index 0000000..923ee64 --- /dev/null +++ b/c/xgetnewwindow.c @@ -0,0 +1,29 @@ +#include +#include + +int main(void) { + Display* display = XOpenDisplay(NULL); + if(!display) { + printf("Error: Unable to open display.\n"); + return 1; + } + + int screen = DefaultScreen(display); + Window root = RootWindow(display, screen); + + /* SubstructureNotifyMask allows us to be notified of CreateNotify events */ + XSelectInput(display, root, SubstructureNotifyMask); + + XEvent event; + for(;;) { + XNextEvent(display, &event); + if(event.type == CreateNotify) { + /* print window id */ + printf("0x%x\n", event.xcreatewindow.window); + break; + } + } + + XCloseDisplay(display); + return 0; +} diff --git a/config.mk b/config.mk new file mode 100644 index 0000000..bcab3b0 --- /dev/null +++ b/config.mk @@ -0,0 +1,2 @@ +DESTDIR=$(HOME)/.local + diff --git a/etc/southwest_tracker.py b/etc/southwest_tracker.py new file mode 100644 index 0000000..2cf2258 --- /dev/null +++ b/etc/southwest_tracker.py @@ -0,0 +1,26 @@ +import httplib2 +import json +import math +import time + +# fun fact: i got bored so made this on a southwest flight + +while True: + request = httplib2.Http() + # you have to look in the network tab in 'inspect element', and find the '_' parameter, and paste that in + # of course, you have to be connected to southwest's onboard wifi + _param = "fill this in" + data = request.request("https://getconnected.southwestwifi.com/fp3d_fcgi-php/portal/public/index.php?_url=/index/getFile&path=last&_=" + _param) + data = json.loads(data[1]) + print("-- Update --") + print("Nautical Miles: " + str(data["distanceToDestinationNauticalMiles"])) + print("Phase: " + data["presentPhase"]) + print("Speed: " + str(data["groundSpeedKnots"]) + "kts") + seconds = data['timeToDestination'] + minutes = math.floor(seconds / 60) + seconds = seconds % 60 + print("Minutes: " + str(minutes) + " Seconds: " + str(seconds)) + print("Flight Number: " + data["flightNumber"] + " Registration: " + data["tailNumber"]) + + time.sleep(3) + print('\033[H\033[2J') diff --git a/man/colors.1 b/man/colors.1 new file mode 100644 index 0000000..e25818a --- /dev/null +++ b/man/colors.1 @@ -0,0 +1,32 @@ +.TH COLORS 1 colors +.SH NAME +colors \- generate colorscheme and run hooks +.SH SYNOPSIS +.B colors [-hefc] [-w path] +.SH OPTIONS +.TP +.B -h +disable running hooks (implies -e) +.TP +.B -e +echo colors to stdout +.TP +.B -c +check colors using color checking algorithm +.TP +.B -f +print wallpaper filename to stdout +.TP +.B -w +provide wallpaper path +.SH HOOKS +When -h is not used, a program at ~/.config/cwal/handler is executed. The first sixteen arguments are the colors in the colorscheme, and the seventeenth argument is the filename. +.SH EXIT CODES +.TP +.B 0 +Normal exit code +.TP +.B 1 +check_colors(...) failed. +.SH AUTHOR +randomuser diff --git a/man/rndutils.1 b/man/rndutils.1 new file mode 100644 index 0000000..8b50645 --- /dev/null +++ b/man/rndutils.1 @@ -0,0 +1,11 @@ +.TH RNDUTILS 1 rndutils +.SH NAME +rndutils \- simple shell script utilities +.SH INSTALLATION +.B make install +.SH LICENSE +The author, randomuser (randomuser at tilde dot club) hereby +releases the software in this distribution and all +accompanying texts into the public domain, thereby relieving +him of any warranty of any kind. + diff --git a/man/timer.1 b/man/timer.1 new file mode 100644 index 0000000..e06fcf4 --- /dev/null +++ b/man/timer.1 @@ -0,0 +1,87 @@ +.TH TIMER 1 timer +.SH NAME +timer \- simple command line timer program +.SH SYNOPSIS +.B timer [-evdbft] [-h int] [-m int] [-s int] +.SH OPTIONS +.TP +.B -e +enable usage of vt100 escape sequences for timer rendering (overrides -v) +.TP +.B -v +enable printing of timer status +.TP +.B -d +count down; when timer reaches 00:00, finish (use -h, -m, -s to set ending time) +.TP +.B -b +ASCII BEL when timer finish state reached +.TP +.B -f +timer display contains hour slot +.TP +.B -t +use a tomato timer (use -h, -m, -s to set work cycle time, rest cycle time is assumed to be half of the working time) +.TP +.B -p +disable pausing after finishing tomato timer cycle (required -t to work) +.TP +.B -z +enable build-specific runtime settings (default -ebf) +.TP +.B "-h int" +specify number of hours for timer parameters +.TP +.B "-m int" +specify number of minutes for timer parameters +.TP +.B "-s int" +specify number of seconds for timer parameters +.SH INTERACTIVE USAGE +.TP +.B pausing +a timer is paused when an astrisk prefixes the display (with -e) and/or when the timer ceases to update, e.g. count up or down. you can use the enter key to pause/unpause it (refer to the below example) +.nf +$ timer -e +* 05:45 +.fi +.TP +.B stopping a timer +should you wish to stop a timer prematurely, or a timer which has no specified end condition, you may press the key combination that sends a killing signal to the program (e.g. a la SIGTERM) in your shell, which is usually ^C. the timer has no major places to clean up. however, memory leaks might occur on some operating systems due to no free()ing of resources. +.SH EXIT CODES/TERMINATION SIGNALS +.TP +.B 0 +timer finished successfully +.TP +.B 1 / SIGABRT +error parsing arguments +.SH EXAMPLES +.B start a ten minute timer, counting up: +.nf +$ timer -m 10 +.fi +.B show the vt100 interface, too: +.nf +$ timer -em 10 +.fi +.B start a one hour sixty-two minute fourty-nine second timer counting down (with vt100 interface): +.nf +$ timer -edh 1 -m 62 -s 49 +.fi +.B start a tomato timer with a work interval of fourty minutes and a rest interval of twenty minutes: +.nf +$ timer -etm 40 +.fi +.SH REQUIRED HEADERS +.nf +#include +#include +#include +#include +#include +.fi +.SH BUGS +- when passing -p each tomato timer cycle is cut short one second +send email to random user at-sign tilde dot club +.SH AUTHOR +randomuser diff --git a/man/trss.1 b/man/trss.1 new file mode 100644 index 0000000..575a9e5 --- /dev/null +++ b/man/trss.1 @@ -0,0 +1,30 @@ +.TH TRSS 1 trss +.SH NAME +trss \- command oriented feed reader powered by sfeed +.SH SYNOPSIS +.B trss +.SH INTERFACE +.TP +.B ls +list all feed names in the 'feed home', or within a feed, list all items within +.TP +.B cd +focus another feed. in order to go back to the 'feed home', use 'cd' or 'cd ..' +.TP +.B sync +with no arguments and in the 'feed home', syncs all feeds. if in a specific feed, syncs that feed. a feed can be specified in an argument. +.TP +.B cat +open all feeds whose titles contain text as specified by an argument in 'lynx' + +.SH trss-yt-id +this script is designed to allow one to obtain an rss feed for the uploads of a youtube channel. this command takes the homepage of the youtube channel in question as an argument and then returns the rss feed for that channel. + +.SH BUGS +- when in a feed, you can't cd to another directly, you must go to to the 'feed home' and then cd + +.SH DEPENDS +sfeed + +.SH AUTHOR +randomuser diff --git a/man/xgetnewwindow.1 b/man/xgetnewwindow.1 new file mode 100644 index 0000000..8c5adab --- /dev/null +++ b/man/xgetnewwindow.1 @@ -0,0 +1,10 @@ +.TH XGETNEWWINDOW 1 xgetnewwindow +.SH NAME +xgetnewwindow +.SH SYNOPSIS +xgetnewwindow blocks until it recieves a CreateNotify event; that is, until a new window is created within the current Xorg instance. Once a new window is created, xgetnewwindow prints the window id (in hexadecimal) to stdout and exits. +.SH EXIT CODES +1 for abnormal termination, 0 for success. +.SH AUTHOR +randomuser + diff --git a/sh/brightness b/sh/brightness new file mode 100755 index 0000000..b431e44 --- /dev/null +++ b/sh/brightness @@ -0,0 +1,4 @@ +#!/bin/sh + +brightness=$(printf "250\n500\n1000\n2000\n3000\n4000\n" | tmenu) +st -c st-gpg-menu -e sh -c "sudo sh -c \"echo $brightness >> /sys/class/backlight/intel_backlight/brightness\"" diff --git a/sh/bspwm-toggle-gaps b/sh/bspwm-toggle-gaps new file mode 100755 index 0000000..776fc5a --- /dev/null +++ b/sh/bspwm-toggle-gaps @@ -0,0 +1,11 @@ +#!/bin/sh + +gaps="$(bspc config top_padding)" + +if [ "$gaps" -eq 20 ]; then + bspc config window_gap 0 + bspc config top_padding 18 +else + bspc config window_gap 3 + bspc config top_padding 20 +fi diff --git a/sh/capture b/sh/capture new file mode 100755 index 0000000..b251669 --- /dev/null +++ b/sh/capture @@ -0,0 +1,60 @@ +#!/bin/sh + +# get screen info and temporary file +tmp=$(mktemp) +res=$(xrandr | + grep ' connected' | + awk -F' ' '{print $1 " " $4}' | + awk -F'+' '{print $1}' | + fzy | + awk -F' ' '{print $2}' ) + +# still or motion +medium=$(printf ".mp4\n.png\n" | fzy) +output="$tmp$medium" + +# capture +case "$medium" in + *mp4*) + printf "> starting video capture...\n" + ffmpeg -video_size "$res" -f x11grab -framerate 60 -i $DISPLAY -preset ultrafast -pix_fmt yuv420p "$output" + ;; + *png*) + printf "> taking screenshot...\n" + # for a screenshot, we usually want an a s t h e t i c + ffmpeg -f x11grab -video_size "$res" -i $DISPLAY -vframes 1 "$output" -loglevel quiet # be quiet + ;; + *) + printf "not a choice\n" + exit 1 +esac + +# what to do with the capture? +printf "> written to %s\n" "$output" +while true; do + result=$(printf "delete\nmove to home directory\nupload to pastebin\nreview footage\nquit\n" | fzy --prompt="what next? ") + case "$result" in + *upload*) + paste "$output" | xclip -i + printf "> pasted! check your clipboard\n" + ;; + *review*) + [ "$medium" = ".mp4" ] && mpv "$output" && continue + feh "$output" + ;; + *delete*) + printf "> removing target file...\n" + rm "$output" + exit 1 + ;; + *home*) + printf "> warping your capture to home...\n" + name=$(echo "capture$medium" | fzy --prompt="name please? ") + mv "$output" "$HOME/$name" + exit 1 + ;; + *quit*) + exit 1 + ;; + esac +done diff --git a/sh/cfg b/sh/cfg new file mode 100755 index 0000000..58892df --- /dev/null +++ b/sh/cfg @@ -0,0 +1,6 @@ +#!/bin/sh + +sel="$(find -L ~/.config 2>/dev/null | fzy)" +[ "$?" -eq 1 ] && exit 1 + +nvim "$sel" diff --git a/sh/connect b/sh/connect new file mode 100755 index 0000000..c37240d --- /dev/null +++ b/sh/connect @@ -0,0 +1,44 @@ +#!/bin/sh + +[ "$(pgrep -c NetworkManager)" -eq 1 ] && + printf "NetworkManager is already running!\n" && + exit + +case $1 in + "c"*) + wpa_supplicant -iwlp3s0 -c/etc/wpa_supplicant/wpa_supplicant.conf -B + dhcpcd wlp3s0 + # make tor and nohup shut up in a posix-compliant way + nohup tor >/dev/null 2>&1 + ;; + "d"*) + killall -15 wpa_supplicant dhcpcd tor + ;; + "r"*) + sh "$0" d + sh "$0" c + ;; + "t"*) + [ "$(pgrep -c wpa_supplicant)" -eq 1 ] && sh "$0" d && exit + sh "$0" c + ;; + "l"*) + iw dev wlp3s0 scan | \ + grep 'SSID\|signal' | \ + grep -v 'SSID List' | \ + awk -F': ' '{print $2}' | \ + sed 'N;s/\n/ /' | \ + grep -v '\x00' + ;; + *) + echo " +simple wrapper for connecting to a network +${0} toggle to toggle wifi connection +${0} restart to restart wifi +${0} disconnect to disconnect +${0} connect to connect to network +${0} list to list networks +" + ;; +esac +exit 0 diff --git a/sh/discord b/sh/discord new file mode 100755 index 0000000..45f1bf8 --- /dev/null +++ b/sh/discord @@ -0,0 +1,3 @@ +#!/bin/sh + +chromium --app=https://discord.com/login --profile-directory="Profile 1" diff --git a/sh/disp b/sh/disp new file mode 100755 index 0000000..824559c --- /dev/null +++ b/sh/disp @@ -0,0 +1,27 @@ +#!/bin/sh + +exists() { + xrandr | grep ' connected' | grep -c "${1}" +} + +bspc monitor -d 1 2 3 4 5 6 7 8 9 + +if [ "$(exists "HDMI-2")" -eq 1 ] && [ "$(exists "LVDS-1")" -eq 1 ]; then + printf "two" + xrandr --output HDMI-2 --right-of LVDS-1 --auto + xrandr --output VGA-1 --off --auto + bspc monitor LVDS-1 -d 1 2 3 4 5 6 7 8 9 + bspc monitor HDMI-2 -d 1 2 3 4 5 6 7 8 9 + exit 0 +fi + +if [ "$(exists "HDMI-1")" -eq 1 ] && [ "$(exists "LVDS-1")" -eq 1 ]; then + printf "two" + xrandr --output HDMI-1 --right-of LVDS-1 --auto + xrandr --output VGA-1 --off --auto + bspc monitor LVDS-1 -d 1 2 3 4 5 6 7 8 9 + bspc monitor HDMI-1 -d 1 2 3 4 5 6 7 8 9 + exit 0 +fi + +# default configuration's fine diff --git a/sh/fire b/sh/fire new file mode 100755 index 0000000..298f915 --- /dev/null +++ b/sh/fire @@ -0,0 +1,3 @@ +#!/bin/sh + +HOME="/home/usr/.local/share/firefox" firefox "$@" diff --git a/sh/firew b/sh/firew new file mode 100755 index 0000000..cfae372 --- /dev/null +++ b/sh/firew @@ -0,0 +1,41 @@ +#!/bin/sh +# wrapper script to make firefox suck less + +RHOME="${HOME}" + +firefox_data_location="$HOME/firefoxdumpster" +[ -z "$3" ] || url="$3" + +# start a profile chooser +if [ -z "$2" ]; then + profile=$(printf "programming\nschool\ntmp-school\ntmp\nchromium\n" | tmenu) +else + profile="$2" +fi + +[ -z "$profile" ] && exit + +if [ "$profile" = "tmp" ]; then + # firefox doesn't start in a directory within /tmp + # so we create one in $HOME/.cache + + tmp=$(basename $(mktemp)) + mkdir -p "${RHOME}/.cache/${tmp}" + rm -r "/tmp/$tmp" + HOME="/home/$(whoami)/.local/share/firefox" firefox --profile "${RHOME}/.cache/${tmp}" --no-remote "$url" + rm -r "${RHOME}/.cache/${tmp}" + exit +fi + +if [ "$profile" = "chromium" ]; then + HOME="/home/$(whoami)/.local/share/firefox" chromium + exit +fi + +# start firefox +mkdir -p "${firefox_data_location}/profile" +HOME="/home/$(whoami)/.local/share/firefox" firefox --profile "${firefox_data_location}/${profile}" --no-remote "$url" +exit + +printf "failed to specify a profile. exiting.\n" +exit 1 diff --git a/sh/git-credential-gitpass b/sh/git-credential-gitpass new file mode 100755 index 0000000..749d7fb --- /dev/null +++ b/sh/git-credential-gitpass @@ -0,0 +1,3 @@ +#!/bin/sh + +printf "password=%s\n" "$(pash s tildegit)" diff --git a/sh/git-survey b/sh/git-survey new file mode 100755 index 0000000..8f6b862 --- /dev/null +++ b/sh/git-survey @@ -0,0 +1,12 @@ +#!/bin/sh + +for i in *; do + cd "$i" || continue + exists=$( + git status | + grep -c 'Your branch is ahead of ' + ) + + [ "$exists" -eq 0 ] || printf "> %s\n" "$i" + cd .. +done diff --git a/sh/keyboard b/sh/keyboard new file mode 100755 index 0000000..4917d4f --- /dev/null +++ b/sh/keyboard @@ -0,0 +1,7 @@ +#!/bin/sh + +setxkbmap -option caps:super +killall xcape +xcape -e 'Super_L=Escape' +xset -q | grep "Caps Lock:\s*on" && xdotool key Caps_Lock +xset r rate 200 40 diff --git a/sh/machine b/sh/machine new file mode 100755 index 0000000..3a77716 --- /dev/null +++ b/sh/machine @@ -0,0 +1,18 @@ +#!/bin/sh + +host="$(hostname)" + +case "$host" in + "mlg") + exit 1 + ;; + "toaster") + exit 2 + ;; + "fish") + exit 3 + ;; + "think") + exit 4 + ;; +esac diff --git a/sh/net b/sh/net new file mode 100755 index 0000000..e79c8f9 --- /dev/null +++ b/sh/net @@ -0,0 +1,4 @@ +#!/bin/sh +# designed to be called from sxhkd (x session) + +pgrep NetworkManager && st -e nmtui diff --git a/sh/nws b/sh/nws new file mode 100755 index 0000000..e1e52a1 --- /dev/null +++ b/sh/nws @@ -0,0 +1,104 @@ +#!/bin/sh + +[ -z "${XDG_CONFIG_DIR}" ] && XDG_CONFIG_DIR="${HOME}/.config" +[ -z "${NWS_CONFIG}" ] && NWS_CONFIG="${XDG_CONFIG_DIR}/nws" +[ -n "${NWS_ZONE}" ] || \ + [ -e "${NWS_CONFIG}" ] && NWS_ZONE=$(cat "${NWS_CONFIG}") || + NWS_ZONE="KGYX" +[ -z "${NWS_GIF}" ] && NWS_GIF="mpv --loop" +[ -z "${NWS_TORIFY}" ] && NWS_TORIFY="" + + +info () { + printf %s "\ +nws - wrapper for the National Weather Service's website + +=> [n]ational - View national weather mosaic +=> [l]ocal - View local weather mosaic +=> [r]ivers - View local river conditions +=> [w]eather - View local weather observations +=> [m]osaic [id] - View a given area's weather mosaic +=> [s]et [id] - Set local zone +=> [t]ext [id] - View text messages by catagory + +Default zone: export NWS_ZONE= +GIF player: export NWS_GIF= +Configuration: export NWS_CONFIG= +Torify wrapper: export NWS_TORIFY= +" +} +err () { + printf "err: %s\n" "${1}" + [ -z "${2}" ] && exit 1 + exit "${2}" +} +kstrip () { + printf %s "$1" | sed 's/^K\(.*\)/\1/' +} +national () { + mosaic CONUS-LARGE +} +# name interestingly to avoid keyword collision +localradar () { + mosaic ${NWS_ZONE} +} +mosaic () { + ${NWS_GIF} "https://radar.weather.gov/ridge/standard/${1}_loop.gif" +} +setzone () { + printf "%s" "${1}" > "${NWS_CONFIG}" +} +river () { + textmessage "RVA" +} +weather () { + textmessage "ZFP" +} +textmessage () { + curl --silent --fail "https://forecast.weather.gov/product.php?site=NWS&issuedby=$(kstrip ${NWS_ZONE})&product=${1}&format=TXT&version=1" | \ + sed -n '//,/<\/pre>/p' | \ + grep -v "a href" | \ + grep -v '' | \ + grep -v '<\/pre>' || \ + printf "${1} data not found for zone %s" ${NWS_ZONE} +} + +case $1 in + "n"*) + national + ;; + "l"*) + localradar + ;; + "r"*) + river + ;; + "w"*) + weather + ;; + "m"*) + if [ "$#" -eq 2 ]; then + mosaid "$2" + else + err "two args required" + fi + ;; + "s"*) + if [ "$#" -eq 2 ]; then + setzone "$2" + else + err "two args required" + fi + ;; + "t"*) + if [ "$#" -eq 2 ]; then + textmessage "$2" + else + err "two args required" + fi + ;; + *) + info + ;; +esac +exit 0 diff --git a/sh/pash-dmenu b/sh/pash-dmenu new file mode 100755 index 0000000..3929379 --- /dev/null +++ b/sh/pash-dmenu @@ -0,0 +1,7 @@ +#!/bin/sh + +sel="$(pash l | tmenu)" +[ "$?" -eq 1 ] && exit 0 + +# requires bspc rule -a st-gpg-menu state=floating +st -c st-gpg-menu -e pash-dmenu-backend "$sel" diff --git a/sh/pash-dmenu-backend b/sh/pash-dmenu-backend new file mode 100755 index 0000000..ab70c4f --- /dev/null +++ b/sh/pash-dmenu-backend @@ -0,0 +1,5 @@ +#!/bin/sh + +pash c "$1" +bspc node -d '^9' +sleep 15 diff --git a/sh/paste b/sh/paste new file mode 100755 index 0000000..87987e0 --- /dev/null +++ b/sh/paste @@ -0,0 +1,4 @@ +#!/bin/sh + +[ "$1" = "" ] && exit 1 +[ -f "$1" ] && curl -F"file=@${1}" https://0x0.st && exit 0 diff --git a/sh/pco b/sh/pco new file mode 100755 index 0000000..9f92017 --- /dev/null +++ b/sh/pco @@ -0,0 +1,88 @@ +#!/bin/sh +# script for interfacing with planning center online + +appid=$(cat "$HOME/.local/share/pco/appid") +token=$(cat "$HOME/.local/share/pco/token") +userid=$(cat "$HOME/.local/share/pco/userid") + +get_blockouts_online () { +curl -su "$appid:$token" "https://api.planningcenteronline.com/services/v2/people/$userid/blockouts" | \ + jq .data[].attributes.ends_at | \ + awk -F'"|-|T' ' +function y(x) { + if (x == 1) return "Jan" + if (x == 2) return "Feb" + if (x == 3) return "Mar" + if (x == 4) return "Apr" + if (x == 5) return "May" + if (x == 6) return "Jun" + if (x == 7) return "Jul" + if (x == 8) return "Aug" + if (x == 9) return "Sep" + if (x == 10) return "Oct" + if (x == 11) return "Nov" + if (x == 12) return "Dec" + return "Unk" +} + +{print y($3) " " $4 " " $2}' | \ + tr '\n' '|' | \ + sed 's/.\{1\}$//' +} + +get_blockouts_local () { + grep "MSG Blockout date" "$DOTREMINDERS" | \ + awk -F' ' '{print $2 " " $3 " " $4}' +} + +get_blockouts () { + all_dates=$(get_blockouts_online) + OLDIFS="$IFS" + IFS="|" + for i in $all_dates; do + results=$(grep -c "^REM $i MSG Blockout date" "$DOTREMINDERS") + [ "$results" -eq 0 ] && \ + printf "REM %s MSG Blockout date\n" "$i" >> "$DOTREMINDERS" && \ + printf "added %s to calendar\n" "$i" || \ + printf "omitted %s from the calendar, it's already there\n" "$i" + done + IFS="$OLDIFS" +} + +push_blockouts () { + file=$(mktemp) + get_blockouts_online | tr '|' '\n' >> "$file" + local=$(get_blockouts_local) + + # don't mess with the spacing + OLDIFS=$IFS + IFS=" +" + printf "temp file: %s\n" "$file" + + for i in $local; do + count=$(grep -c "^$i$" "$file") + if [ "$count" -eq 0 ]; then + printf "push: %s\n" "$i" + stddate=$(date --date="$i" "+%Y-%m-%dT06:00:00Z") + curl -X POST -H "Content-Type: application/json" -d " + { + \"data\": { + \"attributes\": { + \"reason\": \"n/a\", + \"repeat_frequency\": \"no_repeat\", + \"starts_at\": \"$stddate\", + \"ends_at\": \"$stddate\" + } + } + }" -su "$appid:$token" "https://api.planningcenteronline.com/services/v2/people/$userid/blockouts" + fi + done + + IFS=$OLDIFS + + rm "$file" +} + +get_blockouts +push_blockouts diff --git a/sh/sfeed_yt_add b/sh/sfeed_yt_add new file mode 100755 index 0000000..4cd2937 --- /dev/null +++ b/sh/sfeed_yt_add @@ -0,0 +1,23 @@ +#!/bin/sh + +[ -z "$1" ] && exit 1 +[ -z "$2" ] && exit 1 + +printf "[info] this utility is used for adding youtube urls as rss feeds\n" + +feed="$(curl "$1" -s | \ + grep 'youtube/www\.youtube\.com/channel/.\{24\}' -o | \ + awk -F'/' '{print "https://www.youtube.com/feeds/videos.xml?channel_id=" $NF}' | \ + sed 1q)" + +[ -z "$feed" ] && printf "[err] error processing the feed. are you sure it's *www*.youtube.com and not youtube.com?\n" && exit 1 + +sfeedrc="$HOME/.config/sfeed/sfeedrc" + +ed "$sfeedrc" </dev/null 2>&1 + kill "$i" +done + +# get all screens +screens="$(xrandr | grep ' connected' | sed 's/ primary//g' | awk -F' ' '{print $1 " " $3}')" + +# for every screen, create a statusbar +IFS=" +" +for i in $screens; do + screenstring=$(echo "$i" | awk -v a="$barwidth" -F'[ x+]' '{print $2 "x" a "+" $4 "+" $5}') + st -c statusbar -p -g "$screenstring" -e status & +done diff --git a/sh/tmenu b/sh/tmenu new file mode 100755 index 0000000..cbfeec6 --- /dev/null +++ b/sh/tmenu @@ -0,0 +1,8 @@ +#!/bin/sh + +file=$(mktemp) +cat - > "$file" + +st -c tmenu-prompt -g 40x10+0+0 -e tmenu-backend "$file" +cat "$file" +rm "$file" diff --git a/sh/tmenu-backend b/sh/tmenu-backend new file mode 100755 index 0000000..c44defb --- /dev/null +++ b/sh/tmenu-backend @@ -0,0 +1,6 @@ +#!/bin/sh + +printf "%s" "$1" + +fzy < "$1" > "${1}a" +mv "${1}a" "${1}" diff --git a/sh/tmenu_run b/sh/tmenu_run new file mode 100755 index 0000000..695e6b5 --- /dev/null +++ b/sh/tmenu_run @@ -0,0 +1,18 @@ +#!/bin/sh + +tmenu_path () { + IFS=" :" + for i in $PATH; do + for j in "$i"/*; do + [ -f "$j" ] && [ -x "$j" ] && printf "%s\n" "$j" | xargs basename + done + done +} + +if [ "$1" = "-g" ] || [ "$(basename $0)" = "regenerate" ]; then + mkdir -p $HOME/.local/share + tmenu_path > $HOME/.local/share/tmenu_cache + xmessage "regeneration complete" +else + cat $HOME/.local/share/tmenu_cache | tmenu | ${SHELL:-"/bin/sh"} & +fi diff --git a/sh/toggle-contingency-mode b/sh/toggle-contingency-mode new file mode 100755 index 0000000..9052a29 --- /dev/null +++ b/sh/toggle-contingency-mode @@ -0,0 +1,28 @@ +#!/bin/sh + +pid=$(pgrep sxhkd) + +for i in $pid; do + inv_id=$(cat /proc/$i/cmdline | awk -F'\0' '{print $3}') + + echo $inv_id + [ -z "$inv_id" ] && contingency_mode="off" + [ "$inv_id" = *"contingency" ] && contingency_mode="on" +done + +killall sxhkd + +trackpoint=$(xinput | grep "TrackPoint" | awk -F'\t' '{print $2}' | awk -F'=' '{print $2}') +touchpad=$(xinput | grep "TouchPad" | awk -F'\t' '{print $2}' | awk -F'=' '{print $2}') + +if [ "$contingency_mode" = "off" ]; then + sxhkd -c $HOME/.config/sxhkd/contingency & + xinput disable "$trackpoint" + xinput disable "$touchpad" + xmessage "contingency mode enabled." +else + sxhkd & + xinput enable "$trackpoint" + xinput enable "$touchpad" + killall xmessage +fi diff --git a/sh/vim-swap-handler b/sh/vim-swap-handler new file mode 100755 index 0000000..06082af --- /dev/null +++ b/sh/vim-swap-handler @@ -0,0 +1,28 @@ +#!/bin/sh +# when the vim SwapExists autocommand is fired, this script is +# called with the path of the file in question. + +# mad props to daniel conway for having this big brain idea + +# note to self: protect $1 from expansion as it can contain a +# ~ + +[ "$#" -eq 0 ] && exit 2 + +window=$( + xdotool search --name "$1" | \ + sed 1q +) + +desk=$( + xdotool get_desktop_for_window "$window" 2>/dev/null || printf "none" +) + +[ "$desk" = "none" ] && exit 1 +desk=$((desk + 1)) + +bspc desktop -f "^${desk}" +killall -10 simplestatus +xdotool set_window --urgency 1 "$window" + +exit 0 diff --git a/sh/vol b/sh/vol new file mode 100755 index 0000000..7bcd832 --- /dev/null +++ b/sh/vol @@ -0,0 +1,19 @@ +#!/bin/sh + +pulse=$(pgrep -c pulseaudio) + +[ "$1" = "inc" ] && sign="+" +[ "$1" = "dec" ] && sign="-" + +[ "$#" -eq 0 ] && exit 1 + +if [ "$pulse" -eq 1 ]; then + for SINK in $(pacmd list-sinks | grep 'index:' | cut -b12-) + do + pactl set-sink-volume "$SINK" "${sign}5%" + done +else + amixer -c 0 set Master "5%${sign}" +fi + +killall -12 status diff --git a/sh/wallpaper b/sh/wallpaper new file mode 100755 index 0000000..2b519eb --- /dev/null +++ b/sh/wallpaper @@ -0,0 +1,32 @@ +#!/bin/sh + +WALLDIR="${HOME}/.local/share/wallpapers" +BASECMD="feh --no-fehbg --bg-fill" + +generate_wall () { + GENWALL=$( \ + ls "$WALLDIR" | \ + grep "." | \ + shuf -n 1 + ) + GENWALL="${WALLDIR}/${GENWALL}" +} +wall () { + generate_wall + while [ "${GENWALL}" = "${1}" ]; do + generate_wall + done +} +displays () { + displays=$(xrandr | grep -c ' connected') +} + +cmd="" +displays +for i in $(seq 1 "$displays"); do + wall "${tmp}" + tmp="${GENWALL}" + cmd="${cmd} ${GENWALL}" +done +eval "${BASECMD}" "${cmd}" +exit 0