Rewrite the entire code-base

The following changes are focused upon:
- Modularity
- Doing away with globals
- No heap allocations
- Better command line interface
- Switch from Xlib to XCB
- More verbose type definitions
- Implement a single-file config by utilising X-macros
This commit is contained in:
Utkarsh Verma
2023-10-24 08:44:31 +05:30
parent 2773129533
commit bc84d094cd
30 changed files with 918 additions and 396 deletions

View File

@@ -1,62 +0,0 @@
#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);
}

View File

@@ -1,72 +1,147 @@
#include "block.h"
#define _GNU_SOURCE
#include <bits/stdint-uintn.h>
#include <bits/types/FILE.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include "config.h"
#include "util.h"
static int execLock = 0;
int block_init(block *const block) {
if (pipe(block->pipe) != 0) {
(void)fprintf(stderr,
"error: could not create a pipe for \"%s\" block\n",
block->command);
return 1;
}
void execBlock(const Block *block, const char *button) {
unsigned short i = block - blocks;
block->fork_pid = -1;
// 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;
return 0;
}
if (fork() == 0) {
close(block->pipe[0]);
dup2(block->pipe[1], STDOUT_FILENO);
close(block->pipe[1]);
int block_deinit(block *const block) {
int status = close(block->pipe[READ_END]);
status |= close(block->pipe[WRITE_END]);
if (status != 0) {
(void)fprintf(stderr, "error: could not close \"%s\" block's pipe\n",
block->command);
return 1;
}
if (button) setenv("BLOCK_BUTTON", button, 1);
return 0;
}
FILE *file = popen(block->command, "r");
if (!file) {
printf("\n");
int block_execute(block *const block, const uint8_t button) {
// Ensure only one child process exists per block at an instance.
if (block->fork_pid != -1) {
return 0;
}
block->fork_pid = fork();
if (block->fork_pid == -1) {
(void)fprintf(
stderr, "error: could not create a subprocess for \"%s\" block\n",
block->command);
return 1;
}
if (block->fork_pid == 0) {
const int write_fd = block->pipe[WRITE_END];
int status = close(block->pipe[READ_END]);
if (button != 0) {
char button_str[4];
(void)snprintf(button_str, LEN(button_str), "%hhu", button);
status |= setenv("BLOCK_BUTTON", button_str, 1);
}
const char null = '\0';
if (status != 0) {
(void)write(write_fd, &null, sizeof(null));
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);
FILE *const file = popen(block->command, "r");
if (file == NULL) {
(void)write(write_fd, &null, sizeof(null));
exit(EXIT_FAILURE);
}
pclose(file);
// Trim to the max possible UTF-8 capacity
trimUTF8(buffer, LEN(buffer));
// Ensure null-termination since fgets() will leave buffer untouched on
// no output.
char buffer[LEN(block->output)] = {[0] = null};
(void)fgets(buffer, LEN(buffer), file);
// Remove trailing newlines.
const size_t length = strcspn(buffer, "\n");
buffer[length] = null;
// Exit if command execution failed or if file could not be closed.
if (pclose(file) != 0) {
(void)write(write_fd, &null, sizeof(null));
exit(EXIT_FAILURE);
}
const size_t output_size =
truncate_utf8_string(buffer, LEN(buffer), MAX_BLOCK_OUTPUT_LENGTH);
(void)write(write_fd, buffer, output_size);
printf("%s\n", buffer);
exit(EXIT_SUCCESS);
}
return 0;
}
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) {
int block_update(block *const 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';
const ssize_t bytes_read =
read(block->pipe[READ_END], buffer, LEN(buffer));
if (bytes_read == -1) {
(void)fprintf(stderr,
"error: could not fetch output of \"%s\" block\n",
block->command);
return 2;
}
strcpy(block->output, buffer);
// Collect exit-status of the subprocess to avoid zombification.
int fork_status = 0;
if (waitpid(block->fork_pid, &fork_status, 0) == -1) {
(void)fprintf(stderr,
"error: could not obtain exit status for \"%s\" block\n",
block->command);
return 2;
}
block->fork_pid = -1;
// Remove execution lock for the current block
execLock &= ~(1 << (block - blocks));
if (fork_status != 0) {
(void)fprintf(stderr,
"error: \"%s\" block exited with non-zero status\n",
block->command);
return 1;
}
(void)strcpy(block->output, buffer);
return 0;
}
bool block_must_run(const block *const block, const unsigned int time) {
if (time == 0) {
return true;
}
if (block->interval == 0) {
return false;
}
return time % block->interval == 0;
}

30
src/cli.c Normal file
View File

@@ -0,0 +1,30 @@
#include "cli.h"
#include <getopt.h>
#include <stdbool.h>
#include <stdio.h>
int cli_init(cli_arguments *const args, const char *const argv[],
const int argc) {
args->is_debug_mode = false;
int opt = -1;
opterr = 0; // Suppress getopt's built-in invalid opt message
while ((opt = getopt(argc, (char *const *)argv, "dh")) != -1) {
switch (opt) {
case 'd':
args->is_debug_mode = true;
break;
case 'h':
// fall through
case '?':
(void)fprintf(stderr, "error: unknown option `-%c'\n", optopt);
// fall through
default:
(void)fprintf(stderr, "usage: %s [-d]\n", BINARY);
return 1;
}
}
return 0;
}

View File

@@ -1,157 +1,180 @@
#define _GNU_SOURCE
#include <signal.h>
#include <stdio.h>
#include <string.h>
#include <sys/epoll.h>
#include <sys/signalfd.h>
#include "main.h"
#include <stdbool.h>
#include <stddef.h>
#include "bar.h"
#include "block.h"
#include "cli.h"
#include "config.h"
#include "signal-handler.h"
#include "status.h"
#include "timer.h"
#include "util.h"
#include "watcher.h"
#include "x11.h"
static unsigned short statusContinue = 1;
unsigned short debugMode = 0;
static int epollFD, signalFD;
static unsigned int timerTick = 0, maxInterval = 1;
#define BLOCK(cmd, period, sig) \
{ \
.command = cmd, \
.interval = period, \
.signal = sig, \
},
void signalHandler() {
struct signalfd_siginfo info;
read(signalFD, &info, sizeof(info));
unsigned int signal = info.ssi_signo;
block blocks[] = {BLOCKS(BLOCK)};
#undef BLOCK
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;
static int init_blocks(void) {
for (unsigned short i = 0; i < LEN(blocks); ++i) {
block *const block = &blocks[i];
if (block_init(block) != 0) {
return 1;
}
}
}
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;
}
static int deinit_blocks(void) {
for (unsigned short i = 0; i < LEN(blocks); ++i) {
block *const block = &blocks[i];
if (block_deinit(block) != 0) {
return 1;
}
}
return 0;
}
static int execute_blocks(const unsigned int time) {
for (unsigned short i = 0; i < LEN(blocks); ++i) {
block *const block = &blocks[i];
if (!block_must_run(block, time)) {
continue;
}
if (block_execute(&blocks[i], 0) != 0) {
return 1;
}
}
return 0;
}
static int trigger_event(timer *const timer) {
if (execute_blocks(timer->time) != 0) {
return 1;
}
if (timer_arm(timer) != 0) {
return 1;
}
return 0;
}
static int refresh_callback(void) {
if (execute_blocks(0) != 0) {
return 1;
}
return 0;
}
static int event_loop(const bool is_debug_mode,
x11_connection *const connection,
signal_handler *const signal_handler) {
timer timer = timer_new();
// Kickstart the event loop with an initial execution.
if (trigger_event(&timer) != 0) {
return 1;
}
watcher watcher;
if (watcher_init(&watcher, signal_handler->fd) != 0) {
return 1;
}
status status = status_new();
bool is_alive = true;
while (is_alive) {
const int event_count = watcher_poll(&watcher, -1);
if (event_count == -1) {
return 1;
}
int i = 0;
for (unsigned short j = 0; j < WATCHER_FD_COUNT; ++j) {
if (i == event_count) {
break;
}
const watcher_fd *const watcher_fd = &watcher.fds[j];
if (!watcher_fd_is_readable(watcher_fd)) {
continue;
}
++i;
if (j == SIGNAL_FD) {
is_alive = signal_handler_process(signal_handler, &timer) == 0;
continue;
}
block *const block = &blocks[j];
(void)block_update(block);
}
const bool has_status_changed = status_update(&status);
if (has_status_changed) {
if (status_write(&status, is_debug_mode, connection) != 0) {
return 1;
}
}
}
return 0;
}
int main(const int argc, const char *const argv[]) {
cli_arguments cli_args;
if (cli_init(&cli_args, argv, argc) != 0) {
return 1;
}
x11_connection *const connection = x11_connection_open();
if (connection == NULL) {
return 1;
}
int status = 0;
if (init_blocks() != 0) {
status = 1;
goto x11_close;
}
signal_handler signal_handler =
signal_handler_new(refresh_callback, trigger_event);
if (signal_handler_init(&signal_handler) != 0) {
status = 1;
goto deinit_blocks;
}
if (event_loop(cli_args.is_debug_mode, connection, &signal_handler) != 0) {
status = 1;
}
if (signal_handler_deinit(&signal_handler) != 0) {
status = 1;
}
deinit_blocks:
if (deinit_blocks() != 0) {
status = 1;
}
x11_close:
x11_connection_close(connection);
return status;
}

119
src/signal-handler.c Normal file
View File

@@ -0,0 +1,119 @@
#include "signal-handler.h"
#include <bits/stdint-uintn.h>
#include <bits/types/sigset_t.h>
#include <signal.h>
#include <stdio.h>
#include <sys/signalfd.h>
#include <sys/types.h>
#include <unistd.h>
#include "block.h"
#include "main.h"
#include "timer.h"
#include "util.h"
typedef struct signalfd_siginfo signal_info;
signal_handler signal_handler_new(
const signal_refresh_callback refresh_callback,
const signal_timer_callback timer_callback) {
signal_handler handler = {
.refresh_callback = refresh_callback,
.timer_callback = timer_callback,
};
return handler;
}
int signal_handler_init(signal_handler *const handler) {
signal_set set;
(void)sigemptyset(&set);
// Handle user-generated signal for refreshing the status.
(void)sigaddset(&set, REFRESH_SIGNAL);
// Handle SIGALRM generated by the timer.
(void)sigaddset(&set, TIMER_SIGNAL);
// Handle termination signals.
(void)sigaddset(&set, SIGINT);
(void)sigaddset(&set, SIGTERM);
for (unsigned short i = 0; i < LEN(blocks); ++i) {
const block *const block = &blocks[i];
if (blocks->signal > 0) {
if (sigaddset(&set, SIGRTMIN + block->signal) != 0) {
(void)fprintf(
stderr,
"error: invalid or unsupported signal specified for "
"\"%s\" block\n",
block->command);
return 1;
}
}
}
// Create a signal file descriptor for epoll to watch.
handler->fd = signalfd(-1, &set, 0);
if (handler->fd == -1) {
(void)fprintf(stderr,
"error: could not create file descriptor for signals\n");
return 1;
}
// Block all realtime and handled signals.
for (int i = SIGRTMIN; i <= SIGRTMAX; ++i) {
(void)sigaddset(&set, i);
}
(void)sigprocmask(SIG_BLOCK, &set, NULL);
return 0;
}
int signal_handler_deinit(signal_handler *const handler) {
if (close(handler->fd) != 0) {
(void)fprintf(stderr,
"error: could not close signal file descriptor\n");
}
return 0;
}
int signal_handler_process(signal_handler *const handler, timer *const timer) {
signal_info info;
const ssize_t bytes_read = read(handler->fd, &info, sizeof(info));
if (bytes_read == -1) {
(void)fprintf(stderr, "error: could not read info of incoming signal");
return 1;
}
const int signal = (int)info.ssi_signo;
switch (signal) {
case TIMER_SIGNAL:
if (handler->timer_callback(timer) != 0) {
return 1;
}
return 0;
case REFRESH_SIGNAL:
if (handler->refresh_callback() != 0) {
return 1;
}
return 0;
case SIGTERM:
// fall through
case SIGINT:
return 1;
}
for (unsigned short i = 0; i < LEN(blocks); ++i) {
block *const block = blocks + i;
if (block->signal == signal - SIGRTMIN) {
const uint8_t button = (uint8_t)info.ssi_int;
block_execute(block, button);
break;
}
}
return 0;
}

74
src/status.c Normal file
View File

@@ -0,0 +1,74 @@
#include "status.h"
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
#include "block.h"
#include "config.h"
#include "main.h"
#include "util.h"
#include "x11.h"
static bool has_status_changed(const status *const status) {
return strcmp(status->current, status->previous) != 0;
}
status status_new(void) {
status status = {
.current = {[0] = '\0'},
.previous = {[0] = '\0'},
};
return status;
}
bool status_update(status *const status) {
(void)strcpy(status->previous, status->current);
status->current[0] = '\0';
for (unsigned short i = 0; i < LEN(blocks); ++i) {
const block *const block = &blocks[i];
if (strlen(block->output) > 0) {
#if LEADING_DELIMITER
(void)strcat(status->current, DELIMITER);
#else
if (status->current[0] != '\0') {
(void)strcat(status->current, DELIMITER);
}
#endif
#if CLICKABLE_BLOCKS
if (block->signal > 0) {
const char signal[] = {(char)block->signal, '\0'};
(void)strcat(status->current, signal);
}
#endif
(void)strcat(status->current, block->output);
}
}
#if TRAILING_DELIMITER
if (status->current[0] != '\0') {
(void)strcat(status->current, DELIMITER);
}
#endif
return has_status_changed(status);
}
int status_write(const status *const status, const bool is_debug_mode,
x11_connection *const connection) {
if (is_debug_mode) {
(void)printf("%s\n", status->current);
return 0;
}
if (x11_set_root_name(connection, status->current) != 0) {
return 1;
}
return 0;
}

56
src/timer.c Normal file
View File

@@ -0,0 +1,56 @@
#include "timer.h"
#include <errno.h>
#include <stdio.h>
#include <unistd.h>
#include "block.h"
#include "main.h"
#include "util.h"
static unsigned int compute_tick(void) {
unsigned int tick = 0;
for (unsigned short i = 0; i < LEN(blocks); ++i) {
const block *const block = &blocks[i];
tick = gcd(block->interval, tick);
}
return tick;
}
static unsigned int compute_reset_value(void) {
unsigned int reset_value = 1;
for (unsigned short i = 0; i < LEN(blocks); ++i) {
const block *const block = &blocks[i];
reset_value = MAX(block->interval, reset_value);
}
return reset_value;
}
timer timer_new(void) {
timer timer = {
.time = 0,
.tick = compute_tick(),
.reset_value = compute_reset_value(),
};
return timer;
}
int timer_arm(timer *const timer) {
errno = 0;
(void)alarm(timer->tick);
if (errno != 0) {
(void)fprintf(stderr, "error: could not arm timer\n");
return 1;
}
// Wrap `time` to the interval [1, reset_value].
timer->time = (timer->time + timer->tick) % timer->reset_value + 1;
return 0;
}

View File

@@ -1,41 +1,50 @@
#include "util.h"
#include <unistd.h>
#include <stdio.h>
#define UTF8_MULTIBYTE_BIT BIT(7)
int gcd(int a, int b) {
int temp;
unsigned int gcd(unsigned int a, unsigned int b) {
while (b > 0) {
temp = a % b;
const unsigned int 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++;
size_t truncate_utf8_string(char* const buffer, const size_t size,
const size_t char_limit) {
size_t char_count = 0;
size_t i = 0;
while (char_count < char_limit) {
char ch = buffer[i];
if (ch == '\0') {
break;
}
j += skip;
ch = buffer[j];
count++;
unsigned short skip = 1;
// Multibyte unicode character
if ((ch & UTF8_MULTIBYTE_BIT) != 0) {
// Skip continuation bytes.
ch <<= 1;
while ((ch & UTF8_MULTIBYTE_BIT) != 0) {
ch <<= 1;
++skip;
}
}
// Avoid buffer overflow.
if (i + skip >= size) {
break;
}
++char_count;
i += skip;
}
// Trim trailing newline and spaces
buffer[j] = ' ';
while (j >= 0 && buffer[j] == ' ') j--;
buffer[j + 1] = '\0';
buffer[i] = '\0';
return i + 1;
}

53
src/watcher.c Normal file
View File

@@ -0,0 +1,53 @@
#include "watcher.h"
#include <errno.h>
#include <stdbool.h>
#include <stdio.h>
#include <sys/poll.h>
#include "main.h"
#include "util.h"
int watcher_init(watcher* const watcher, const int signal_fd) {
if (signal_fd == -1) {
fprintf(stderr,
"error: invalid signal file descriptor passed to watcher\n");
return 1;
}
watcher_fd* const fd = &watcher->fds[SIGNAL_FD];
fd->fd = signal_fd;
fd->events = POLLIN;
for (unsigned short i = 0; i < LEN(blocks); ++i) {
const int block_fd = blocks[i].pipe[READ_END];
if (block_fd == -1) {
fprintf(
stderr,
"error: invalid block file descriptors passed to watcher\n");
return 1;
}
watcher_fd* const fd = &watcher->fds[i];
fd->fd = block_fd;
fd->events = POLLIN;
}
return 0;
}
int watcher_poll(watcher* watcher, const int timeout_ms) {
const int event_count = poll(watcher->fds, LEN(watcher->fds), timeout_ms);
// Don't return non-zero status for signal interruptions.
if (event_count == -1 && errno != EINTR) {
(void)fprintf(stderr, "error: watcher could not poll blocks\n");
return -1;
}
return event_count;
}
bool watcher_fd_is_readable(const watcher_fd* const watcher_fd) {
return (watcher_fd->revents & POLLIN) != 0;
}

View File

@@ -1,25 +1,44 @@
#include "x11.h"
#include <X11/Xlib.h>
#include <stdio.h>
#include <string.h>
#include <xcb/xcb.h>
#include <xcb/xproto.h>
static Display *display;
static Window rootWindow;
x11_connection *x11_connection_open(void) {
xcb_connection_t *const connection = xcb_connect(NULL, NULL);
if (xcb_connection_has_error(connection)) {
(void)fprintf(stderr, "error: could not connect to the X server\n");
return NULL;
}
int setupX() {
display = XOpenDisplay(NULL);
if (!display) {
return connection;
}
void x11_connection_close(xcb_connection_t *const connection) {
xcb_disconnect(connection);
}
int x11_set_root_name(x11_connection *const connection, const char *name) {
xcb_screen_t *const screen =
xcb_setup_roots_iterator(xcb_get_setup(connection)).data;
const xcb_window_t root_window = screen->root;
const unsigned short name_format = 8;
const xcb_void_cookie_t cookie = xcb_change_property(
connection, XCB_PROP_MODE_REPLACE, root_window, XCB_ATOM_WM_NAME,
XCB_ATOM_STRING, name_format, strlen(name), name);
xcb_generic_error_t *error = xcb_request_check(connection, cookie);
if (error != NULL) {
(void)fprintf(stderr, "error: could not set X root name\n");
return 1;
}
if (xcb_flush(connection) <= 0) {
(void)fprintf(stderr, "error: could not flush X output buffer\n");
return 1;
}
rootWindow = DefaultRootWindow(display);
return 0;
}
int closeX() {
return XCloseDisplay(display);
}
void setXRootName(char *str) {
XStoreName(display, rootWindow, str);
XFlush(display);
}