mirror of https://github.com/tiyn/dwmblocks
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…
Reference in new issue