mirror of https://github.com/tiyn/dwmblocks
parent
05b89ed6b5
commit
ad879884b7
@ -1,36 +1,53 @@
|
|||||||
PREFIX := /usr/local
|
.POSIX:
|
||||||
CC := cc
|
|
||||||
CFLAGS := -pedantic -Wall -Wno-deprecated-declarations -Os
|
|
||||||
LDFLAGS := -lX11
|
|
||||||
|
|
||||||
# FreeBSD (uncomment)
|
BIN := dwmblocks
|
||||||
#LDFLAGS += -L/usr/local/lib -I/usr/local/include
|
BUILD_DIR := build
|
||||||
# # OpenBSD (uncomment)
|
SRC_DIR := src
|
||||||
#LDFLAGS += -L/usr/X11R6/lib -I/usr/X11R6/include
|
INC_DIR := inc
|
||||||
|
|
||||||
all: options dwmblocks
|
VERBOSE := 0
|
||||||
|
|
||||||
options:
|
PREFIX := /usr/local
|
||||||
@echo dwmblocks build options:
|
CFLAGS := -Wall -Wextra -Ofast -I. -I$(INC_DIR)
|
||||||
@echo "CFLAGS = ${CFLAGS}"
|
CFLAGS += -Wall -Wextra -Wno-missing-field-initializers
|
||||||
@echo "LDFLAGS = ${LDFLAGS}"
|
LDLIBS := -lX11
|
||||||
@echo "CC = ${CC}"
|
|
||||||
|
|
||||||
dwmblocks: dwmblocks.c blocks.def.h blocks.h
|
VPATH := $(SRC_DIR)
|
||||||
${CC} -o dwmblocks dwmblocks.c ${CFLAGS} ${LDFLAGS}
|
OBJS := $(patsubst $(SRC_DIR)/%.c,$(BUILD_DIR)/%.o,$(wildcard $(SRC_DIR)/*.c))
|
||||||
|
OBJS += $(patsubst %.c,$(BUILD_DIR)/%.o,$(wildcard *.c))
|
||||||
|
|
||||||
blocks.h:
|
INSTALL_DIR := $(DESTDIR)$(PREFIX)/bin
|
||||||
cp blocks.def.h $@
|
|
||||||
|
# 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:
|
clean:
|
||||||
rm -f *.o *.gch dwmblocks
|
$(PRINTF) "CLEAN" $(BUILD_DIR)
|
||||||
|
$Q$(RM) $(BUILD_DIR)/*
|
||||||
|
|
||||||
install: dwmblocks
|
install: $(BUILD_DIR)/$(BIN)
|
||||||
mkdir -p ${DESTDIR}${PREFIX}/bin
|
$(PRINTF) "INSTALL" $(INSTALL_DIR)/$(BIN)
|
||||||
cp -f dwmblocks ${DESTDIR}${PREFIX}/bin
|
$Qinstall -D -m 755 $< $(INSTALL_DIR)/$(BIN)
|
||||||
chmod 755 ${DESTDIR}${PREFIX}/bin/dwmblocks
|
|
||||||
|
|
||||||
uninstall:
|
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
|
# dwmblocks
|
||||||
|
|
||||||
Modular status bar for dwm written in c.
|
Modular status bar for dwm written in c.
|
||||||
This build is based on [lukesmithxyz's](https://github.com/lukesmithxyz) build.
|
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.
|
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
|
# Modifying blocks
|
||||||
|
|
||||||
The statusbar is made from text output from commandline programs. Blocks are
|
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
|
# Signaling changes
|
||||||
|
|
||||||
Most statusbars constantly rerun every script every several seconds to update.
|
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
|
This is an option here, but a superior choice is giving your module a signal that you can signal to
|
||||||
that you can signal to it to update on a relevant event, rather than having it
|
it to update on a relevant event, rather than having it rerun idly.
|
||||||
rerun idly.
|
|
||||||
|
|
||||||
For example, the audio module has the update signal 10 by default. Thus,
|
For example, the audio module has the update signal 10 by default.
|
||||||
running `pkill -RTMIN+10 dwmblocks` will update it.
|
Thus, running `pkill -RTMIN+10 dwmblocks` will update it.
|
||||||
|
|
||||||
You can also run `kill -44 $(pidof dwmblocks)` which will have the same effect,
|
You can also run `kill -44 $(pidof dwmblocks)` which will have the same effect, but is faster.
|
||||||
but is faster. Just add 34 to your typical signal number.
|
Just add 34 to your typical signal number.
|
||||||
|
|
||||||
My volume module *never* updates on its own, instead I have this command run
|
My volume module *never* updates on its own, instead I have this command run along side my volume
|
||||||
along side my volume shortcuts in dwm to only update it when relevant.
|
shortcuts in dwm to only update it when relevant.
|
||||||
|
|
||||||
Note that if you signal an unexpected signal to dwmblocks, it will probably
|
Note that if you signal an unexpected signal to dwmblocks, it will probably crash.
|
||||||
crash. So if you disable a module, remember to also disable any cronjobs or
|
So if you disable a module, remember to also disable any cronjobs or other scripts that might signal
|
||||||
other scripts that might signal to that module.
|
to that module.
|
||||||
|
|
||||||
# Clickable modules
|
# Clickable modules
|
||||||
|
|
||||||
Like i3blocks, this build allows you to build in additional actions into your
|
Like i3blocks, this build allows you to build in additional actions into your scripts in response
|
||||||
scripts in response to click events. See the above linked scripts for examples
|
to click events.
|
||||||
of this using the `$BLOCK_BUTTON` variable.
|
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).
|
|
||||||
|
@ -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…
Reference in new issue