feat(build): use meson so I don't end up ripping my hair out

This commit is contained in:
2025-09-21 14:15:09 -04:00
parent 961e0472f5
commit 6e075bff4a
11 changed files with 102 additions and 89 deletions

91
src/acpi.c Normal file
View File

@@ -0,0 +1,91 @@
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>
#include <errno.h>
#include "log.h"
#include "acpi.h"
#include "utils.h"
int
acpi_parse(char *str, char **out)
{
int i, section_num, last_section_pos;
size_t section_size;
if (strlen(str) <= 1) {
log_warn("failed to parse acpid input string: it doesn't exist!");
return 1;
}
for (last_section_pos = section_num = i = 0; i <= strlen(str); i++) {
if (str[i] != ' ' && str[i] != '\n' && str[i] != '\0') {
continue;
}
/* grab correct information of the current section */
section_size = i - last_section_pos;
/* copy over the data into the new buffer */
out[section_num] = get_substring(str, last_section_pos, section_size);
/* set the last_section_pos for the next section */
last_section_pos = i + 1;
section_num++;
}
return 0;
}
void
acpi_clean_parse(char *out[4])
{
int i;
for (i = 0; i < 4; i++) {
if (out[i]) {
free(out[i]);
}
}
}
int
acpi_create_socket(char *socket_file)
{
int sock_fd;
struct sockaddr_un addr = { 0 };
/* create a new socket connection */
sock_fd = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0);
if (sock_fd < 0) {
log_fatal("failed to create a new socket: %s", strerror(errno));
return 1;
}
/* setup the socket */
addr.sun_family = AF_UNIX;
strncpy(addr.sun_path, socket_file, strlen(socket_file) + 1);
/* connect */
if (connect(sock_fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
if (close(sock_fd) < 0) {
log_fatal("failed to close socket close: %s", strerror(errno));
}
log_fatal("failed to connect to the socket: %s", strerror(errno));
return 1;
}
return sock_fd;
}
int
acpi_close_socket(int sockfd) {
if (close(sockfd) < 0) {
log_fatal("failed to close socket: %s", strerror(errno));
return 1;
}
return 0;
}

203
src/led.c Normal file
View File

@@ -0,0 +1,203 @@
#include <assert.h>
#include <errno.h>
#include <pthread.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/poll.h>
#include <unistd.h>
#include "led.h"
#include "log.h"
#define Z() if (data->rate > 0) { \
usleep(1000000 / data->rate); \
}
struct Data {
char *file_name;
FILE *pfile;
double rate;
};
/* global data accessed by all threads */
static struct Data *data;
pthread_t thread;
/* mutex to lock the thread */
static pthread_mutex_t mp = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t cv = PTHREAD_COND_INITIALIZER;
/**
* @brief helper function to set the led rate
*
* @param value set the contents of the led file
* @param d data
*/
static inline void
led_set(char value, struct Data *d)
{
d->pfile = fopen(d->file_name, "w");
if (d->pfile == NULL) {
log_warn("failed to open %s fopen: %s", d->file_name, strerror(errno));
return;
}
if (fputc(value, d->pfile) == EOF) {
log_warn("failed to write to %s fopen: %s", d->file_name, strerror(errno));
/* According to the man page, after erroring doing anything including
* fclose may result in undefined behavior. */
return;
}
fclose(d->pfile);
}
/**
* @brief main thread loop
*
* @param args args
*/
static void
*run(void *args)
{
int err;
struct Data *d = (struct Data *)args;
for (;;) {
/* turn off the led before doing anything else */
led_set('0', d);
/* wait for the condition to change */
while(d->rate == 0) {
if ((err = pthread_cond_wait(&cv, &mp)) != 0) {
log_fatal("pthread_cond_wait: %s", strerror(err));
pthread_exit(0);
}
}
/* exit if we're told to */
if (d->rate <= -1) {
log_debug("received rate of -1, exiting");
pthread_exit(0);
}
Z();
led_set('1', d);
Z();
}
pthread_exit(0);
}
/**
* @brief run before exiting the program
* this is for cleaning up all the messes we've made
*
* @param signal signal
*/
static void
pre_exit(int signal)
{
led_set_rate(-1);
pthread_cond_destroy(&cv);
pthread_mutex_destroy(&mp);
if (data->pfile) {
data->pfile = fopen(data->file_name, "w");
}
fputc('0', data->pfile);
/* make sure to cleanup the allocated data */
fclose(data->pfile);
free(data->file_name);
free(data);
exit(0);
}
int
led_create_thread(char *file)
{
int err;
struct sigaction act;
static bool running;
/* make sure the led thread cannot be created twice */
if (running) {
log_warn("led thread has already been created");
return 1;
}
/* zero the array, and setup sig handler and exit handler */
memset(&act, 0, sizeof(act));
act.sa_handler = &pre_exit;
if (sigaction(SIGINT, &act, NULL) < 0) {
log_fatal("failed to create sigaction: %s, memory is gonna leak",
strerror(errno));
}
/* setup data */
data = calloc(1, sizeof(struct Data));
data->file_name = malloc(sizeof(char) * (strlen(file) + 1));
if (data->file_name == NULL) {
log_fatal("failed to create led thread: malloc of led file name failed: %s",
strerror(errno));
exit(1);
}
strcpy(data->file_name, file);
data->rate = 0;
/* attempt to create the thread */
if ((err = pthread_create(&thread, NULL, run, data)) != 0) {
log_fatal("failed to create led thread: pthread_create: %s", strerror(err));
return -1;
}
/* detach the thread to make cleanup nice and easy */
if ((err = pthread_detach(thread)) != 0) {
log_fatal("failed to create led thread: pthread_detach: %s", strerror(err));
return -1;
}
log_info("started LED thread");
running = true;
return 0;
}
void
led_set_rate(double r)
{
int err;
static bool locked = false;
/* there's no reason to set the rate if it's already the same */
if (r == data->rate) {
return;
}
/* lock the thread, update, and unlock it */
if (!locked) {
if ((err = pthread_mutex_lock(&mp)) != 0) {
log_fatal("failed to lock the mutex: %s", strerror(errno));
}
}
/* set the rate */
data->rate = r;
log_debug("set rate to: %f", r);
pthread_cond_signal(&cv);
if (!locked) {
if ((err = pthread_mutex_unlock(&mp)) != 0) {
log_fatal("failed to lock the mutex: %s", strerror(errno));
}
}
/* make sure we don't lock the thread in an infinite loop */
locked = (r != 0);
}

159
src/main.c Normal file
View File

@@ -0,0 +1,159 @@
#include <errno.h>
#include <getopt.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <sys/poll.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/un.h>
#include <unistd.h>
#include <stdbool.h>
#include "log.h"
#include "led.h"
#include "acpi.h"
#include "utils.h"
#include "config.h"
#define MAX_BUFLEN 1024
char
*battery_percent(void)
{
char *r, c, *d;
int i;
FILE *f;
/* open up the battery capacity for reading */
d = concat(battery_dir, "/capacity");
f = fopen(d, "r");
free(d);
/* create enough space for the battery percentage */
r = calloc(4, sizeof(char));
/* read in the battery percentage */
i = 0;
while ((c = getc(f)) && i < 4) {
/* make sure to replace the newline with a string terminator */
if (c == '\n') {
r[i] = '\0';
break;
}
r[i] = c;
i++;
}
fclose(f);
return r;
}
bool
plugged_in(char *fn)
{
FILE *f;
char buf[1];
f = fopen(fn, "r");
if (read(fileno(f), buf, 1) < 0) {
return -1;
}
fclose(f);
return buf[0] - '0';
}
void
on_battery_event(char *percent, bool plugged)
{
char *ep;
int res;
double rate;
if (!plugged) {
/* convert battery percentage to an int */
res = strtol(percent, &ep, 10);
if (res <= blink_start) {
rate = blink_formula(res);
if (rate >= 1) {
led_set_rate(rate);
}
} else {
led_set_rate(0);
}
} else {
led_set_rate(0);
}
free(percent);
}
int
main(int argc, char *argv[])
{
bool plugged = plugged_in((char *)linefile);
char buffer[MAX_BUFLEN], *out[4];
int sock_fd;
/* set the logging level */
log_set_level(LOG_DEBUG);
/* open up socket address */
sock_fd = acpi_create_socket((char *)acpid_sock);
/* create the led thread */
led_create_thread((char *)ledfile);
/* run events that need to be run on start to ensure that the current state
* inside the program reflects that that is outside the program
*/
on_battery_event(battery_percent(), plugged);
/* zero buffer */
memset(out, 0, sizeof(out));
for (;;) {
/* read input */
memset(buffer, 0, MAX_BUFLEN);
if (read(sock_fd, buffer, MAX_BUFLEN) < 0) {
log_fatal("read: %s", strerror(errno));
return 1;
}
/* parse */
if (acpi_parse(buffer, out) != 0) {
continue;
}
/* rules */
if (strcmp(out[0], "ac_adapter") == 0) {
char *s = get_substring(out[1], 0, 2);
if (strcmp(s, "AC") == 0) {
plugged = plugged_in("/sys/class/power_supply/ACAD/online");
}
free(s);
} else if (strcmp(out[0], "battery") == 0) {
char *s = get_substring(out[1], 0, 3);
if (strcmp(s, "PNP") == 0) {
if (strcmp(out[2], "00000080") == 0) {
if (strcmp(out[3], "00000001") == 0) {
on_battery_event(battery_percent(), plugged);
}
}
}
free(s);
}
acpi_clean_parse(out);
}
acpi_close_socket(sock_fd);
}

57
src/utils.c Normal file
View File

@@ -0,0 +1,57 @@
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include "utils.h"
#include "log.h"
char
*get_substring(char *s, int pos, int l)
{
int i = 0;
char *out;
if (l <= 0) {
return NULL;
}
out = malloc((l + 1) * sizeof(char));
if (out == NULL) {
log_fatal("failed to get substring malloc: %s", strerror(errno));
exit(1);
}
/* Copy substring into ss */
while (i < l) {
out[i] = s[pos + i];
i++;
}
/* Null terminate the substring */
out[i] = '\0';
return out;
}
char
*concat(const char *s1, const char *s2)
{
int len1, len2;
char *result;
len1 = strlen(s1);
len2 = strlen(s2);
/* attempt to allocate size for the new string */
result = malloc((len1 + len2 + 1) * sizeof(char));
if (result == NULL) {
log_fatal("malloc: %s", strerror(errno));
exit(1);
}
memcpy(result, s1, len1);
memcpy(result + len1, s2, len2 + 1);
// + 1 copies the null terminator
return result;
}