add new statusbar program

This commit is contained in:
stupidcomputer 2024-05-12 13:23:26 -05:00
parent 56193d308a
commit c713065135
26 changed files with 202 additions and 415 deletions

View File

@ -0,0 +1,2 @@
{ pkgs ? import <nixpkgs> {} }:
pkgs.callPackage ./derivation.nix {}

View File

@ -0,0 +1,10 @@
{ lib, python3Packages }:
with python3Packages;
buildPythonApplication {
pname = "statusbar";
version = "1.0";
propagatedBuildInputs = [ ];
src = ./.;
}

23
builds/statusbar/setup.py Normal file
View File

@ -0,0 +1,23 @@
from setuptools import setup, find_packages
setup(
name = 'pystatus',
version = '1.0.0',
author = 'stupidcomputer',
author_email = 'ryan@beepboop.systems',
url = 'https://git.beepboop.systems/stupidcomputer/dot_testing',
description = 'simple statusbar content program',
license = 'MIT',
entry_points = {
'console_scripts': [
'statusbar = statusbar.statusbar:main'
]
},
classifiers = (
"Programming Language :: Python :: 3",
"License :: OSI Approved :: GNU General Public License v3 (GPLv3)",
"Operating System :: POSIX :: Linux",
"Environment :: Console"
),
zip_safe = False
)

View File

View File

@ -0,0 +1,160 @@
from multiprocessing import Process, Queue
from collections import defaultdict
from sys import argv
from sys import stdout
from math import floor
import datetime
import signal
import subprocess
import re
import os, socket, time
def find_all_belonging_to_monitor(splitted, monitor):
splitted = [i[1:] if i[1:] == monitor else i for i in splitted]
our_monitor = splitted.index(monitor)
next_monitor = None
for i in range(our_monitor, len(splitted)):
if splitted[i][0].lower() == "m":
next_monitor = i
break
if not next_monitor:
next_monitor = len(splitted) - 1
return splitted[our_monitor:next_monitor]
def generate_desktop_string(monitor_array):
output = []
for i in monitor_array:
if i[0] == "O":
output.append("*{}".format(i[1:]))
if i[0] == "o":
output.append("{}".format(i[1:]))
if i[0] == "F":
output.append("*{}".format(i[1:]))
return ' '.join(output)
def bspwm(queue, monitor):
client = socket.socket(
socket.AF_UNIX,
socket.SOCK_STREAM
)
client.connect("/tmp/bspwm_1_0-socket")
message = "subscribe\0".encode()
client.send(message)
while True:
resp = client.recv(1024).decode().rstrip()
splitted = resp[1:].split(":")
if not monitor == "all":
monitor_array = find_all_belonging_to_monitor(splitted, monitor)
else:
monitor_array = splitted
queue.put({
"module": "bspwm",
"data": generate_desktop_string(monitor_array)
})
client.close()
def clock(queue, _):
while True:
queue.put({
"module": "clock",
"data": datetime.datetime.now().strftime("%d/%m | %H:%M")
})
time.sleep(60)
def filecheckerfactory(filename: str, modname: str, timeout=60):
def filechecker(queue, _):
while True:
try:
file = open(filename)
buf = file.read(128).rstrip()
queue.put({
"module": modname,
"data": buf
})
except FileNotFoundError:
pass
time.sleep(timeout)
return filechecker
battery = filecheckerfactory("/sys/class/power_supply/BAT0/capacity", "bat")
batterystatus = filecheckerfactory("/sys/class/power_supply/BAT0/status", "batstat")
def render(modules) -> str:
columns, _ = os.get_terminal_size(0)
left = "{} | {}".format(modules["clock"], modules["bspwm"])
right = "{}({})".format(modules["bat"], modules["batstat"])
padding = " " * (columns - len(left) - len(right) - 0)
output = left + padding + right
# special battery trickery
try:
batt_percentage = int(modules["bat"]) / 100
except ValueError:
batt_percentage = 1
highlighted = floor(columns * batt_percentage)
output = "\033[?25l\033[2J\033[H\033[4m" + \
output[:highlighted] + \
"\033[24m" + \
output[highlighted:]
print(output, end='')
stdout.flush()
def main():
if argv[1] == "start_statusbars":
# signal.signal(signal.SIGINT, signal.SIG_IGN)
# os.system("pkill statusbar")
# signal.signal(signal.SIGINT, signal.SIG_DFL)
# get the monitors
xrandr = subprocess.Popen(['xrandr'], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
output = list(xrandr.stdout)
output = [i.decode("utf-8") for i in output if " connected" in i.decode("utf-8")]
serialized = []
for i in output:
splitted = i.split(' ')
print(splitted)
displayname = splitted[0]
geometry = splitted[2]
if geometry == "primary":
geometry = splitted[3]
try:
geometry_splitted = [int(i) for i in geometry.replace('x', '+').split('+')]
except ValueError:
continue
geometry_splitted[1] = 20
print(displayname, geometry_splitted)
os.system("st -c statusbar -p -g {}x{}+{}+{} -e statusbar {} & disown".format(
*map(str, geometry_splitted),
displayname
))
return
queue = Queue()
modules = [bspwm, clock, battery, batterystatus]
[Process(target=module, args=(queue, argv[1])).start() for module in modules]
module_outputs = defaultdict(lambda: "")
while True:
result = queue.get()
module_outputs[result["module"]] = result["data"]
render(module_outputs)
if __name__ == "__main__":
main()

View File

@ -1,8 +1,6 @@
{ stdenv
, lib
# for statusbar
, pkg-config
, libxcb
, pkgs
# shell scripts stuff
, makeWrapper
, sshuttle
@ -16,6 +14,7 @@
, figlet
, curl
, ytfzf
, herbe
, xrandr
, svkbd
, xkbset
@ -30,13 +29,8 @@ stdenv.mkDerivation rec {
src = ./utils;
nativeBuildInputs = [ makeWrapper pkg-config libxcb ];
buildInputs = [ libxcb bash feh xrandr jq curl fzy ytfzf ffmpeg sshuttle svkbd scrcpy xkbset rbw xclip libsForQt5.kolourpaint ];
buildPhase = ''
ls
make
'';
nativeBuildInputs = [ makeWrapper ];
buildInputs = [ bash feh xrandr jq curl fzy ytfzf ffmpeg sshuttle svkbd scrcpy xkbset rbw xclip libsForQt5.kolourpaint ];
installPhase = ''
mkdir -p $out/bin
@ -44,9 +38,7 @@ stdenv.mkDerivation rec {
for i in $(ls $src/sh); do
cp $src/sh/$i $out/bin
ln -sf $out/bin/tmenu_run $out/bin/regenerate
wrapProgram $out/bin/$i --prefix PATH : ${lib.makeBinPath [ sxhkd bash feh xrandr jq figlet curl fzy ytfzf ffmpeg sshuttle svkbd scrcpy libsForQt5.kolourpaint ]}
wrapProgram $out/bin/$i --prefix PATH : ${lib.makeBinPath [ sxhkd bash feh xrandr jq figlet curl fzy ytfzf herbe ffmpeg sshuttle svkbd scrcpy libsForQt5.kolourpaint ]}
done
cp c/status/main $out/bin/statusbar
'';
}

View File

@ -1,3 +0,0 @@
.PHONY: main
main:
make -C c/status -f Makefile

View File

@ -1,2 +0,0 @@
main
*.o

View File

@ -1,9 +0,0 @@
LDFLAGS=`pkg-config --cflags --libs xcb`
CFLAGS=-ggdb -fsanitize=address
main: battery.o bspwm.o time.o battstatus.o message.o
clean:
rm *.o main
run:
./main

View File

@ -1,4 +0,0 @@
status
------
a simple statusbar script thing (tm)

View File

@ -1,38 +0,0 @@
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <fcntl.h>
#include "battery.h"
#include "common.h"
/* config contains a path to the battery */
int mod_battery(char *config, char *name, char *pipename) {
struct message msg;
strcpy(msg.name, name);
int fd = open(pipename, O_WRONLY);
int battery;
int recvd;
chdir("/sys/class/power_supply");
chdir(config);
for(;;) {
battery = open("capacity", O_RDONLY);
recvd = read(battery, msg.content, 3);
msg.content[3] = '\0';
if (msg.content[2] == '\n') {
msg.content[2] = '\0';
}
close(battery);
write(fd, &msg, sizeof(msg));
sleep(30);
}
return 0;
}

View File

@ -1,6 +0,0 @@
#ifndef STATUS_BATTERY_H
#define STATUS_BATTERY_H
int mod_battery(char *config, char *name, char *pipename);
#endif

View File

@ -1,48 +0,0 @@
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include "battstatus.h"
#include "common.h"
int mod_battstatus(char *config, char *name, char *pipename) {
char status;
int battery;
struct message msg;
strcpy(msg.name, name);
int fd = open(pipename, O_WRONLY);
chdir("/sys/class/power_supply");
chdir(config);
for(;;) {
battery = open("status", O_RDONLY);
read(battery, msg.content, 1);
switch(msg.content[0]) {
case 'N': /* not charging */
msg.content[0] = '-';
break;
case 'C': /* charging */
msg.content[0] = '^';
break;
case 'D': /* discharging */
msg.content[0] = 'U';
break;
case 'U': /* unknown */
msg.content[0] = '?';
break;
default: /* what's going on? */
msg.content[0] = '!';
break;
}
msg.content[1] = '\0';
close(battery);
write(fd, &msg, sizeof(msg));
sleep(30);
}
return 0;
}

View File

@ -1,6 +0,0 @@
#ifndef STATUS_BATTSTAT_H
#define STATUS_BATTSTAT_H
int mod_battstatus(char *config, char *name, char *pipename);
#endif

View File

@ -1,73 +0,0 @@
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <xcb/xcb.h>
#include <fcntl.h>
#include "bspwm.h"
#include "common.h"
const char subscribe[] = "subscribe";
int get_socket(void) {
struct sockaddr_un sock;
char *host;
int displaynumber, screennumber;
int fd;
xcb_parse_display(NULL, &host, &displaynumber, &screennumber);
sock.sun_family = AF_UNIX;
snprintf(
sock.sun_path,
sizeof(sock.sun_path), "/tmp/bspwm%s_%i_%i-socket",
host, displaynumber, screennumber
);
free(host);
fd = socket(AF_UNIX, SOCK_STREAM, 0);
if (connect(
fd,
(struct sockaddr *) &sock,
sizeof(sock)
) == -1) {
return -1;
} else {
return fd;
}
}
int should_be_shown(char c) {
return c == 'O' || c == 'o' || c == 'F' || c == 'U' || c == 'u';
}
int is_a_desktop(char c) {
return c == 'O' || c == 'o' || c == 'F' || c == 'f' || c == 'U' || c == 'u';
}
int mod_bspwm(char *config, char *name, char *pipename) {
struct message msg;
int fd, bspcfd;
char in[BUFFER_SIZE];
strcpy(msg.name, name);
msg.flags = 0;
fd = open(pipename, O_WRONLY);
bspcfd = get_socket();
send(bspcfd, subscribe, sizeof(subscribe), 0);
for(;;) {
int recvd = recv(bspcfd, msg.content, sizeof(msg.content), 0);
msg.content[recvd - 1] = '\0';
write(fd, &msg, sizeof(msg));
memset(msg.content, 0, 512);
}
return 0;
}

View File

@ -1,6 +0,0 @@
#ifndef STATUS_BSPWM_H
#define STATUS_BSPWM_H
int mod_bspwm(char *config, char *name, char *pipename);
#endif

View File

@ -1,21 +0,0 @@
#ifndef STATUS_COMMON_H
#define STATUS_COMMON_H
#define LENGTH(x) sizeof(x) / sizeof(x[0])
#define BUFFER_SIZE 512
struct module {
int (*fork_callback)(char *config, char *name, char *pipename);
char name[16];
char config[512];
char buffer[512];
int buflen;
};
struct message {
int flags;
char name[16];
char content[512];
};
#endif

View File

@ -1,88 +0,0 @@
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/un.h>
#include <time.h>
#include <fcntl.h>
#include <xcb/xcb.h>
#include <string.h>
#include "common.h"
#include "battery.h"
#include "battstatus.h"
#include "bspwm.h"
#include "time.h"
#include "message.h"
struct module mods[] = {
{mod_battery, "battery", "BAT0", { '\0' }},
{mod_battstatus, "battstatus", "BAT0", { '\0' }},
{mod_time, "time", "", { '\0' }},
{mod_bspwm, "bspwm", "", { '\0' }},
/* {mod_message, "message", "/home/usr/.cache/statusbar_notification", { '\0' }}, */
};
void create_module_proc(int index, char *pipename) {
pid_t pid = fork();
if (pid == 0) { /* we're the child */
mods[index].fork_callback(
mods[index].config,
mods[index].name,
pipename
);
}
}
void create_module_procs(char *pipename) {
for(int i = 0; i < LENGTH(mods); i++) {
create_module_proc(i, pipename);
}
}
void redraw() {
/* get the progress' module's value, convert it to int, and then
* figure out how much of the screen should be shaded in */
printf("\033[H\033[2J");
for(int i = 0; i < LENGTH(mods); i++) {
if (i == 0) printf("%s ", mods[i].buffer);
else printf("| %s ", mods[i].buffer);
}
fflush(stdout);
}
static char NAMED_PIPE[] = "/home/usr/.cache/statusbar_pipe";
int main(void) {
srand(time(NULL));
mkfifo(&NAMED_PIPE, 0666); /* it's okay if this fails */
int fd = open(&NAMED_PIPE, O_RDWR);
struct message msg;
create_module_procs(&NAMED_PIPE);
for (;;) {
int ret = read(fd, &msg, sizeof(msg));
if(ret < 0) {
printf("error while reading message from child\n");
}
for(int i = 0; i < LENGTH(mods); i++) {
if(strcmp(mods[i].name, msg.name) == 0) {
mods[i].buflen = strlen(msg.content);
strcpy(mods[i].buffer, msg.content);
redraw();
break;
}
}
}
return 0;
}

View File

@ -1,35 +0,0 @@
#include <unistd.h>
#include <stdio.h>
#include <fcntl.h>
#include <string.h>
#include <sys/inotify.h>
#include "message.h"
#include "common.h"
int mod_message(char *config, char *name, char *pipename) {
struct message msg;
struct inotify_event buf;
strcpy(msg.name, name);
int fd = inotify_init();
int outfd = open(pipename, O_WRONLY);
for(;;) {
int watchdesc = inotify_add_watch(fd, config, IN_MODIFY);
read:
int watchread = read(fd, &buf, sizeof(struct inotify_event));
/* the file's changed, so reread it */
int filefd = open(config, O_RDONLY, 0);
int read_in = read(filefd, msg.content, sizeof(msg.content));
msg.content[read_in - 1] = '\0';
close(filefd);
/* write the new */
write(outfd, &msg, sizeof(msg));
inotify_rm_watch(fd, watchdesc); /* not sure why this is needed */
}
return 0;
}

View File

@ -1,6 +0,0 @@
#ifndef STATUS_MESSAGE_H
#define STATUS_MESSAGE_H
int mod_message(char *config, char *name, char *pipename);
#endif

View File

@ -1,9 +0,0 @@
with import <nixpkgs> {};
pkgs.mkShell {
nativeBuildInputs = [
gdb
gnumake
pkg-config
xorg.libxcb
];
}

View File

@ -1,27 +0,0 @@
#include <unistd.h>
#include <fcntl.h>
#include <time.h>
#include <string.h>
#include "common.h"
#include "time.h"
int mod_time(char *config, char *name, char *pipename) {
struct message msg;
time_t now;
struct tm *tm;
int fd;
strcpy(msg.name, name);
msg.flags = 0;
fd = open(pipename, O_WRONLY);
for(;;) {
time(&now);
tm = localtime(&now);
strftime(msg.content, 512, "%H:%M", tm);
write(fd, &msg, sizeof(msg));
sleep(60);
}
}

View File

@ -1,6 +0,0 @@
#ifndef STATUS_TIME_H
#define STATUS_TIME_H
int mod_time(char *config, char *name, char *pipename);
#endif

View File

@ -138,7 +138,7 @@ esac
# initial post-wm setup
keyboard
statuswrap
statusbar start_statusbars
set_walls
screensaver

View File

@ -1,14 +0,0 @@
#!/bin/sh
geos=$(
xrandr | \
grep ' connected' | \
grep -o '[0-9]*x[0-9]*+[0-9]*+[0-9]*' | \
awk -F'[x+]' '{print $1 "x20+" $3 "+" $4}'
)
pkill statusbar
for i in $geos; do
st -c statusbar -p -g "$i" -e statusbar & disown
done

View File

@ -9,6 +9,7 @@
(pkgs.callPackage ../builds/rebuild.nix {})
(pkgs.callPackage ../builds/st.nix {})
(pkgs.callPackage ../builds/utils.nix {})
(pkgs.callPackage ../builds/statusbar {})
pkgs.man-pages
];