updated version to be based on utkarsh vermas dwmblocks

master
tiyn 7 months ago
parent 05b89ed6b5
commit ad879884b7

@ -1,36 +1,53 @@
PREFIX := /usr/local
CC := cc
CFLAGS := -pedantic -Wall -Wno-deprecated-declarations -Os
LDFLAGS := -lX11
.POSIX:
# FreeBSD (uncomment)
#LDFLAGS += -L/usr/local/lib -I/usr/local/include
# # OpenBSD (uncomment)
#LDFLAGS += -L/usr/X11R6/lib -I/usr/X11R6/include
BIN := dwmblocks
BUILD_DIR := build
SRC_DIR := src
INC_DIR := inc
all: options dwmblocks
VERBOSE := 0
options:
@echo dwmblocks build options:
@echo "CFLAGS = ${CFLAGS}"
@echo "LDFLAGS = ${LDFLAGS}"
@echo "CC = ${CC}"
PREFIX := /usr/local
CFLAGS := -Wall -Wextra -Ofast -I. -I$(INC_DIR)
CFLAGS += -Wall -Wextra -Wno-missing-field-initializers
LDLIBS := -lX11
dwmblocks: dwmblocks.c blocks.def.h blocks.h
${CC} -o dwmblocks dwmblocks.c ${CFLAGS} ${LDFLAGS}
VPATH := $(SRC_DIR)
OBJS := $(patsubst $(SRC_DIR)/%.c,$(BUILD_DIR)/%.o,$(wildcard $(SRC_DIR)/*.c))
OBJS += $(patsubst %.c,$(BUILD_DIR)/%.o,$(wildcard *.c))
blocks.h:
cp blocks.def.h $@
INSTALL_DIR := $(DESTDIR)$(PREFIX)/bin
# Prettify output
PRINTF := @printf "%-8s %s\n"
ifeq ($(VERBOSE), 0)
Q := @
endif
all: $(BUILD_DIR)/$(BIN)
$(BUILD_DIR)/$(BIN): $(OBJS)
$(PRINTF) "LD" $@
$Q$(LINK.o) $^ $(LDLIBS) -o $@
$(BUILD_DIR)/%.o: %.c config.h | $(BUILD_DIR)
$(PRINTF) "CC" $@
$Q$(COMPILE.c) -o $@ $<
$(BUILD_DIR):
$(PRINTF) "MKDIR" $@
$Qmkdir -p $@
clean:
rm -f *.o *.gch dwmblocks
$(PRINTF) "CLEAN" $(BUILD_DIR)
$Q$(RM) $(BUILD_DIR)/*
install: dwmblocks
mkdir -p ${DESTDIR}${PREFIX}/bin
cp -f dwmblocks ${DESTDIR}${PREFIX}/bin
chmod 755 ${DESTDIR}${PREFIX}/bin/dwmblocks
install: $(BUILD_DIR)/$(BIN)
$(PRINTF) "INSTALL" $(INSTALL_DIR)/$(BIN)
$Qinstall -D -m 755 $< $(INSTALL_DIR)/$(BIN)
uninstall:
rm -f ${DESTDIR}${PREFIX}/bin/dwmblocks
$(PRINTF) "RM" $(INSTALL_DIR)/$(BIN)
$Q$(RM) $(INSTALL_DIR)/$(BIN)
.PHONY: all options clean install uninstall
.PHONY: all clean install uninstall

@ -1,40 +1,36 @@
# dwmblocks
Modular status bar for dwm written in c.
This build is based on [lukesmithxyz's](https://github.com/lukesmithxyz) build.
It uses my personal scripts you can find in my [dotfiles](https://github.com/tiyn/.dotfiles) in the `~/.local/bin/tools/statusbar` folder.
This build is based on [Utkarsh Vermas](https://github.com/UtkarshVerma/dwmblocks-async) build.
It uses my personal scripts you can find in my [dotfiles](https://github.com/tiyn/.dotfiles) in the
`~/.local/bin/tools/statusbar` folder.
# Modifying blocks
The statusbar is made from text output from commandline programs. Blocks are
added and removed by editing the config.h file.
added and removed by editing the config.c file.
# Signaling changes
Most statusbars constantly rerun every script every several seconds to update.
This is an option here, but a superior choice is giving your module a signal
that you can signal to it to update on a relevant event, rather than having it
rerun idly.
This is an option here, but a superior choice is giving your module a signal that you can signal to
it to update on a relevant event, rather than having it rerun idly.
For example, the audio module has the update signal 10 by default. Thus,
running `pkill -RTMIN+10 dwmblocks` will update it.
For example, the audio module has the update signal 10 by default.
Thus, running `pkill -RTMIN+10 dwmblocks` will update it.
You can also run `kill -44 $(pidof dwmblocks)` which will have the same effect,
but is faster. Just add 34 to your typical signal number.
You can also run `kill -44 $(pidof dwmblocks)` which will have the same effect, but is faster.
Just add 34 to your typical signal number.
My volume module *never* updates on its own, instead I have this command run
along side my volume shortcuts in dwm to only update it when relevant.
My volume module *never* updates on its own, instead I have this command run along side my volume
shortcuts in dwm to only update it when relevant.
Note that if you signal an unexpected signal to dwmblocks, it will probably
crash. So if you disable a module, remember to also disable any cronjobs or
other scripts that might signal to that module.
Note that if you signal an unexpected signal to dwmblocks, it will probably crash.
So if you disable a module, remember to also disable any cronjobs or other scripts that might signal
to that module.
# Clickable modules
Like i3blocks, this build allows you to build in additional actions into your
scripts in response to click events. See the above linked scripts for examples
of this using the `$BLOCK_BUTTON` variable.
For this feature to work, you need the appropriate patch in dwm as well. See
[here](https://dwm.suckless.org/patches/statuscmd/).
Credit for those patches goes to Daniel Bylinka (daniel.bylinka@gmail.com).
Like i3blocks, this build allows you to build in additional actions into your scripts in response
to click events.
See the above linked scripts for examples of this using the `$BLOCK_BUTTON` variable.

@ -1,15 +0,0 @@
//Modify this file to change what commands output to your statusbar, and recompile using the make command.
static const Block blocks[] = {
/*Icon*/ /*Command*/ /*Update Interval*/ /*Update Signal*/
{"", "memory", 10, 6},
{"", "cpu", 10, 5},
{"", "disk /", 1800, 7},
{"", "volume", 0, 4},
{"", "battery BAT0", 5, 2},
{"", "internet", 5, 3},
{"", "clock", 60, 1},
};
//sets delimeter between status commands. NULL character ('\0') means no delimeter.
static char delim[] = "|";
static unsigned int delimLen = 5;

@ -1,15 +0,0 @@
//Modify this file to change what commands output to your statusbar, and recompile using the make command.
static const Block blocks[] = {
/*Icon*/ /*Command*/ /*Update Interval*/ /*Update Signal*/
{"", "memory", 10, 6},
{"", "cpu", 10, 5},
{"", "disk /", 1800, 7},
{"", "volume", 0, 4},
{"", "battery BAT0", 5, 2},
{"", "internet", 5, 3},
{"", "clock", 60, 1},
};
//sets delimeter between status commands. NULL character ('\0') means no delimeter.
static char delim[] = "|";
static unsigned int delimLen = 5;

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

@ -0,0 +1,16 @@
#include "config.h"
#include "block.h"
#include "util.h"
Block blocks[] = {
{"memory", 10, 6},
{"cpu", 10, 5},
{"disk /", 1800, 7},
{"volume", 0, 4},
{"battery BAT0", 5, 2},
{"internet", 5, 3},
{"clock", 60, 1},
};
const unsigned short blockCount = LEN(blocks);

@ -0,0 +1,6 @@
#pragma once
#define CLICKABLE_BLOCKS 0 // Enable clickability for blocks
#define CMDLENGTH 45 // Trim block output to this length
#define DELIMITER " " // Delimiter string used to separate blocks
#define LEADING_DELIMITER 0 // Whether a leading separator should be used

@ -1,213 +0,0 @@
#include<stdlib.h>
#include<stdio.h>
#include<string.h>
#include<unistd.h>
#include<signal.h>
#ifndef NO_X
#include<X11/Xlib.h>
#endif
#ifdef __OpenBSD__
#define SIGPLUS SIGUSR1+1
#define SIGMINUS SIGUSR1-1
#else
#define SIGPLUS SIGRTMIN
#define SIGMINUS SIGRTMIN
#endif
#define LENGTH(X) (sizeof(X) / sizeof (X[0]))
#define CMDLENGTH 50
#define MIN( a, b ) ( ( a < b) ? a : b )
#define STATUSLENGTH (LENGTH(blocks) * CMDLENGTH + 1)
typedef struct {
char* icon;
char* command;
unsigned int interval;
unsigned int signal;
} Block;
#ifndef __OpenBSD__
void dummysighandler(int num);
#endif
void sighandler(int num);
void getcmds(int time);
void getsigcmds(unsigned int signal);
void setupsignals();
void sighandler(int signum);
int getstatus(char *str, char *last);
void statusloop();
void termhandler();
void pstdout();
#ifndef NO_X
void setroot();
static void (*writestatus) () = setroot;
static int setupX();
static Display *dpy;
static int screen;
static Window root;
#else
static void (*writestatus) () = pstdout;
#endif
#include "blocks.h"
static char statusbar[LENGTH(blocks)][CMDLENGTH] = {0};
static char statusstr[2][STATUSLENGTH];
static int statusContinue = 1;
static int returnStatus = 0;
//opens process *cmd and stores output in *output
void getcmd(const Block *block, char *output)
{
strcpy(output, block->icon);
FILE *cmdf = popen(block->command, "r");
if (!cmdf)
return;
int i = strlen(block->icon);
fgets(output+i, CMDLENGTH-i-delimLen, cmdf);
i = strlen(output);
if (i == 0) {
//return if block and command output are both empty
pclose(cmdf);
return;
}
//only chop off newline if one is present at the end
i = output[i-1] == '\n' ? i-1 : i;
if (delim[0] != '\0') {
strncpy(output+i, delim, delimLen);
}
else
output[i++] = '\0';
pclose(cmdf);
}
void getcmds(int time)
{
const Block* current;
for (unsigned 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(unsigned int signal)
{
const Block *current;
for (unsigned int i = 0; i < LENGTH(blocks); i++) {
current = blocks + i;
if (current->signal == signal)
getcmd(current,statusbar[i]);
}
}
void setupsignals()
{
#ifndef __OpenBSD__
/* initialize all real time signals with dummy handler */
for (int i = SIGRTMIN; i <= SIGRTMAX; i++)
signal(i, dummysighandler);
#endif
for (unsigned int i = 0; i < LENGTH(blocks); i++) {
if (blocks[i].signal > 0)
signal(SIGMINUS+blocks[i].signal, sighandler);
}
}
int getstatus(char *str, char *last)
{
strcpy(last, str);
str[0] = '\0';
for (unsigned int i = 0; i < LENGTH(blocks); i++)
strcat(str, statusbar[i]);
str[strlen(str)-strlen(delim)] = '\0';
return strcmp(str, last);//0 if they are the same
}
#ifndef NO_X
void setroot()
{
if (!getstatus(statusstr[0], statusstr[1]))//Only set root if text has changed.
return;
XStoreName(dpy, root, statusstr[0]);
XFlush(dpy);
}
int setupX()
{
dpy = XOpenDisplay(NULL);
if (!dpy) {
fprintf(stderr, "dwmblocks: Failed to open display\n");
return 0;
}
screen = DefaultScreen(dpy);
root = RootWindow(dpy, screen);
return 1;
}
#endif
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();
int i = 0;
getcmds(-1);
while (1) {
getcmds(i++);
writestatus();
if (!statusContinue)
break;
sleep(1.0);
}
}
#ifndef __OpenBSD__
/* this signal handler should do nothing */
void dummysighandler(int signum)
{
return;
}
#endif
void sighandler(int signum)
{
getsigcmds(signum-SIGPLUS);
writestatus();
}
void termhandler()
{
statusContinue = 0;
}
int main(int argc, char** argv)
{
for (int i = 0; i < argc; i++) {//Handle command line arguments
if (!strcmp("-d",argv[i]))
strncpy(delim, argv[++i], delimLen);
else if (!strcmp("-p",argv[i]))
writestatus = pstdout;
}
#ifndef NO_X
if (!setupX())
return 1;
#endif
delimLen = MIN(delimLen, strlen(delim));
delim[delimLen++] = '\0';
signal(SIGTERM, termhandler);
signal(SIGINT, termhandler);
statusloop();
#ifndef NO_X
XCloseDisplay(dpy);
#endif
return 0;
}

@ -0,0 +1,15 @@
#pragma once
#include "block.h"
#include "config.h"
#include "util.h"
typedef struct {
char *current;
char *previous;
} BarStatus;
extern unsigned short debugMode;
void initStatus(BarStatus *);
void freeStatus(BarStatus *);
void writeStatus(BarStatus *);

@ -0,0 +1,17 @@
#pragma once
#include "config.h"
typedef struct {
const char *command;
const unsigned int interval;
const unsigned int signal;
int pipe[2];
char output[CMDLENGTH * 4 + 1];
} Block;
extern Block blocks[];
extern const unsigned short blockCount;
void execBlock(const Block *, const char *);
void execBlocks(unsigned int);
void updateBlock(Block *);

@ -0,0 +1,8 @@
#pragma once
#define LEN(arr) (sizeof(arr) / sizeof(arr[0]))
#define MAX(a, b) (a > b ? a : b)
int gcd(int, int);
void closePipe(int[2]);
void trimUTF8(char*, unsigned int);

@ -0,0 +1,5 @@
#pragma once
int setupX();
int closeX();
void setXRootName(char *);

@ -0,0 +1,62 @@
#include "bar.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "block.h"
#include "x11.h"
void initStatus(BarStatus *status) {
const unsigned int statusLength =
(blockCount * (LEN(blocks[0].output) - 1)) +
(blockCount - 1 + LEADING_DELIMITER) * (LEN(DELIMITER) - 1);
status->current = (char *)malloc(statusLength);
status->previous = (char *)malloc(statusLength);
status->current[0] = '\0';
status->previous[0] = '\0';
}
void freeStatus(BarStatus *status) {
free(status->current);
free(status->previous);
}
int updateStatus(BarStatus *status) {
strcpy(status->previous, status->current);
status->current[0] = '\0';
for (int i = 0; i < blockCount; i++) {
Block *block = blocks + i;
if (strlen(block->output)) {
#if LEADING_DELIMITER
strcat(status->current, DELIMITER);
#else
if (status->current[0]) strcat(status->current, DELIMITER);
#endif
#if CLICKABLE_BLOCKS
if (!debugMode && block->signal) {
char signal[] = {block->signal, '\0'};
strcat(status->current, signal);
}
#endif
strcat(status->current, block->output);
}
}
return strcmp(status->current, status->previous);
}
void writeStatus(BarStatus *status) {
// Only write out if status has changed
if (!updateStatus(status)) return;
if (debugMode) {
printf("%s\n", status->current);
return;
}
setXRootName(status->current);
}

@ -0,0 +1,72 @@
#include "block.h"
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "util.h"
static int execLock = 0;
void execBlock(const Block *block, const char *button) {
unsigned short i = block - blocks;
// Ensure only one child process exists per block at an instance
if (execLock & 1 << i) return;
// Lock execution of block until current instance finishes execution
execLock |= 1 << i;
if (fork() == 0) {
close(block->pipe[0]);
dup2(block->pipe[1], STDOUT_FILENO);
close(block->pipe[1]);
if (button) setenv("BLOCK_BUTTON", button, 1);
FILE *file = popen(block->command, "r");
if (!file) {
printf("\n");
exit(EXIT_FAILURE);
}
// Buffer will hold both '\n' and '\0'
char buffer[LEN(block->output) + 1];
if (fgets(buffer, LEN(buffer), file) == NULL) {
// Send an empty line in case of no output
printf("\n");
exit(EXIT_SUCCESS);
}
pclose(file);
// Trim to the max possible UTF-8 capacity
trimUTF8(buffer, LEN(buffer));
printf("%s\n", buffer);
exit(EXIT_SUCCESS);
}
}
void execBlocks(unsigned int time) {
for (int i = 0; i < blockCount; i++) {
const Block *block = blocks + i;
if (time == 0 ||
(block->interval != 0 && time % block->interval == 0)) {
execBlock(block, NULL);
}
}
}
void updateBlock(Block *block) {
char buffer[LEN(block->output)];
int bytesRead = read(block->pipe[0], buffer, LEN(buffer));
// String from pipe will always end with '\n'
buffer[bytesRead - 1] = '\0';
strcpy(block->output, buffer);
// Remove execution lock for the current block
execLock &= ~(1 << (block - blocks));
}

@ -0,0 +1,157 @@
#define _GNU_SOURCE
#include <signal.h>
#include <stdio.h>
#include <string.h>
#include <sys/epoll.h>
#include <sys/signalfd.h>
#include "bar.h"
#include "block.h"
#include "util.h"
#include "x11.h"
static unsigned short statusContinue = 1;
unsigned short debugMode = 0;
static int epollFD, signalFD;
static unsigned int timerTick = 0, maxInterval = 1;
void signalHandler() {
struct signalfd_siginfo info;
read(signalFD, &info, sizeof(info));
unsigned int signal = info.ssi_signo;
static unsigned int timer = 0;
switch (signal) {
case SIGALRM:
// Schedule the next timer event and execute blocks
alarm(timerTick);
execBlocks(timer);
// Wrap `timer` to the interval [1, `maxInterval`]
timer = (timer + timerTick - 1) % maxInterval + 1;
return;
case SIGUSR1:
// Update all blocks on receiving SIGUSR1
execBlocks(0);
return;
}
for (int j = 0; j < blockCount; j++) {
const Block *block = blocks + j;
if (block->signal == signal - SIGRTMIN) {
char button[4]; // value can't be more than 255;
sprintf(button, "%d", info.ssi_int & 0xff);
execBlock(block, button);
break;
}
}
}
void termHandler() {
statusContinue = 0;
}
void setupSignals() {
sigset_t handledSignals;
sigemptyset(&handledSignals);
sigaddset(&handledSignals, SIGUSR1);
sigaddset(&handledSignals, SIGALRM);
// Append all block signals to `handledSignals`
for (int i = 0; i < blockCount; i++)
if (blocks[i].signal > 0)
sigaddset(&handledSignals, SIGRTMIN + blocks[i].signal);
// Create a signal file descriptor for epoll to watch
signalFD = signalfd(-1, &handledSignals, 0);
// Block all realtime and handled signals
for (int i = SIGRTMIN; i <= SIGRTMAX; i++) sigaddset(&handledSignals, i);
sigprocmask(SIG_BLOCK, &handledSignals, NULL);
// Handle termination signals
signal(SIGINT, termHandler);
signal(SIGTERM, termHandler);
// Avoid zombie subprocesses
struct sigaction signalAction;
signalAction.sa_handler = SIG_DFL;
sigemptyset(&signalAction.sa_mask);
signalAction.sa_flags = SA_NOCLDWAIT;
sigaction(SIGCHLD, &signalAction, 0);
}
void statusLoop() {
// Update all blocks initially
raise(SIGALRM);
BarStatus status;
initStatus(&status);
struct epoll_event events[blockCount + 1];
while (statusContinue) {
int eventCount = epoll_wait(epollFD, events, LEN(events), 100);
for (int i = 0; i < eventCount; i++) {
unsigned short id = events[i].data.u32;
if (id < blockCount) {
updateBlock(blocks + id);
} else {
signalHandler();
}
}
if (eventCount != -1) writeStatus(&status);
}
freeStatus(&status);
}
void init() {
epollFD = epoll_create(blockCount);
struct epoll_event event = {
.events = EPOLLIN,
};
for (int i = 0; i < blockCount; i++) {
// Append each block's pipe's read end to `epollFD`
pipe(blocks[i].pipe);
event.data.u32 = i;
epoll_ctl(epollFD, EPOLL_CTL_ADD, blocks[i].pipe[0], &event);
// Calculate the max interval and tick size for the timer
if (blocks[i].interval) {
maxInterval = MAX(blocks[i].interval, maxInterval);
timerTick = gcd(blocks[i].interval, timerTick);
}
}
setupSignals();
// Watch signal file descriptor as well
event.data.u32 = blockCount;
epoll_ctl(epollFD, EPOLL_CTL_ADD, signalFD, &event);
}
int main(const int argc, const char *argv[]) {
if (setupX()) {
fprintf(stderr, "%s\n", "dwmblocks: Failed to open display");
return 1;
}
for (int i = 0; i < argc; i++) {
if (!strcmp("-d", argv[i])) {
debugMode = 1;
break;
}
}
init();
statusLoop();
if (closeX())
fprintf(stderr, "%s\n", "dwmblocks: Failed to close display");
close(epollFD);
close(signalFD);
for (int i = 0; i < blockCount; i++) closePipe(blocks[i].pipe);
return 0;
}

@ -0,0 +1,41 @@
#include "util.h"
#include <unistd.h>
int gcd(int a, int b) {
int temp;
while (b > 0) {
temp = a % b;
a = b;
b = temp;
}
return a;
}
void closePipe(int pipe[2]) {
close(pipe[0]);
close(pipe[1]);
}
void trimUTF8(char* buffer, unsigned int size) {
int length = (size - 1) / 4;
int count = 0, j = 0;
char ch = buffer[j];
while (ch != '\0' && ch != '\n' && count < length) {
// Skip continuation bytes, if any
int skip = 1;
while ((ch & 0xc0) > 0x80) {
ch <<= 1;
skip++;
}
j += skip;
ch = buffer[j];
count++;
}
// Trim trailing newline and spaces
buffer[j] = ' ';
while (j >= 0 && buffer[j] == ' ') j--;
buffer[j + 1] = '\0';
}

@ -0,0 +1,25 @@
#include "x11.h"
#include <X11/Xlib.h>
static Display *display;
static Window rootWindow;
int setupX() {
display = XOpenDisplay(NULL);
if (!display) {
return 1;
}
rootWindow = DefaultRootWindow(display);
return 0;
}
int closeX() {
return XCloseDisplay(display);
}
void setXRootName(char *str) {
XStoreName(display, rootWindow, str);
XFlush(display);
}
Loading…
Cancel
Save