diff --git a/Makefile b/Makefile index 75109ac..6fab7cd 100644 --- a/Makefile +++ b/Makefile @@ -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 diff --git a/README.md b/README.md index e245aa8..627fdcd 100644 --- a/README.md +++ b/README.md @@ -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. diff --git a/blocks.def.h b/blocks.def.h deleted file mode 100644 index d0a1436..0000000 --- a/blocks.def.h +++ /dev/null @@ -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; diff --git a/blocks.h b/blocks.h deleted file mode 100644 index d0a1436..0000000 --- a/blocks.h +++ /dev/null @@ -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; diff --git a/build/bar.o b/build/bar.o new file mode 100644 index 0000000..ae8e081 Binary files /dev/null and b/build/bar.o differ diff --git a/build/block.o b/build/block.o new file mode 100644 index 0000000..657f54e Binary files /dev/null and b/build/block.o differ diff --git a/build/config.o b/build/config.o new file mode 100644 index 0000000..a334f90 Binary files /dev/null and b/build/config.o differ diff --git a/build/dwmblocks b/build/dwmblocks new file mode 100755 index 0000000..cb93da5 Binary files /dev/null and b/build/dwmblocks differ diff --git a/build/main.o b/build/main.o new file mode 100644 index 0000000..07eeaf3 Binary files /dev/null and b/build/main.o differ diff --git a/build/util.o b/build/util.o new file mode 100644 index 0000000..558a9d8 Binary files /dev/null and b/build/util.o differ diff --git a/build/x11.o b/build/x11.o new file mode 100644 index 0000000..08fcc33 Binary files /dev/null and b/build/x11.o differ diff --git a/config.c b/config.c new file mode 100644 index 0000000..408d28b --- /dev/null +++ b/config.c @@ -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); diff --git a/config.h b/config.h new file mode 100644 index 0000000..8f47394 --- /dev/null +++ b/config.h @@ -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 diff --git a/dwmblocks.c b/dwmblocks.c deleted file mode 100644 index 19df6d0..0000000 --- a/dwmblocks.c +++ /dev/null @@ -1,213 +0,0 @@ -#include -#include -#include -#include -#include -#ifndef NO_X -#include -#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; -} diff --git a/inc/bar.h b/inc/bar.h new file mode 100644 index 0000000..377886e --- /dev/null +++ b/inc/bar.h @@ -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 *); diff --git a/inc/block.h b/inc/block.h new file mode 100644 index 0000000..f0d264c --- /dev/null +++ b/inc/block.h @@ -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 *); diff --git a/inc/util.h b/inc/util.h new file mode 100644 index 0000000..bfc030d --- /dev/null +++ b/inc/util.h @@ -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); diff --git a/inc/x11.h b/inc/x11.h new file mode 100644 index 0000000..abb4f0f --- /dev/null +++ b/inc/x11.h @@ -0,0 +1,5 @@ +#pragma once + +int setupX(); +int closeX(); +void setXRootName(char *); diff --git a/src/bar.c b/src/bar.c new file mode 100644 index 0000000..9f8261c --- /dev/null +++ b/src/bar.c @@ -0,0 +1,62 @@ +#include "bar.h" + +#include +#include +#include + +#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); +} diff --git a/src/block.c b/src/block.c new file mode 100644 index 0000000..ae50ea5 --- /dev/null +++ b/src/block.c @@ -0,0 +1,72 @@ +#include "block.h" + +#define _GNU_SOURCE +#include +#include +#include +#include + +#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)); +} diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..8b87b63 --- /dev/null +++ b/src/main.c @@ -0,0 +1,157 @@ +#define _GNU_SOURCE +#include +#include +#include +#include +#include + +#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; +} diff --git a/src/util.c b/src/util.c new file mode 100644 index 0000000..157e626 --- /dev/null +++ b/src/util.c @@ -0,0 +1,41 @@ +#include "util.h" + +#include + +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'; +} diff --git a/src/x11.c b/src/x11.c new file mode 100644 index 0000000..63c5d14 --- /dev/null +++ b/src/x11.c @@ -0,0 +1,25 @@ +#include "x11.h" + +#include + +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); +}