feat(src): improve logging and refactor console output

Enhancements
- Replace printf and LOG_ERROR with LOG_INFO, LOG_WARN, LOG_PERROR throughout src/app/process.c, src/app/watch.c, and src/audio/config.c
- Use LOG_INFO for headings and formatted tables in sound‑pack listing
- Add missing includes and clean up header organization

Bug Fixes
- Use LOG_PERROR for pipe, fork, and execl failures to log errno correctly
- Ensure proper newline handling in config‑watcher log messages

New Features
- Introduce structured logging for configuration file changes and reload notifications
- Provide clearer usage guidance via LOG_INFO
This commit is contained in:
sankalp committed 2026-02-22 02:35:17 +05:45
1 parent a776be0bf3
commit 563ebdbbab
9 files changed
+368 -490

No files matched your search

+5 -5
View File
@@ -100,12 +100,12 @@ int start_children(const char *sound_dir, const char *config_path, int volume,
(void)mouse_sound_dir;
int pipefd[2];
if (pipe(pipefd) == -1) {
LOG_ERROR("pipe: %s", strerror(errno));
LOG_PERROR("pipe");
return 0;
}
sound_pid = fork();
if (sound_pid == -1) {
LOG_ERROR("fork: %s", strerror(errno));
LOG_PERROR("fork");
close(pipefd[0]);
close(pipefd[1]);
return 0;
@@ -141,12 +141,12 @@ int start_children(const char *sound_dir, const char *config_path, int volume,
verbose_str, mute_str, mouse_config_path, mouse_volume_str,
keyboard_mute_str, mouse_mute_str, keyboard_enabled_str,
mouse_enabled_str, system_volume_following_str, (char *)NULL);
LOG_ERROR("execl vbx-audio: %s", strerror(errno));
LOG_PERROR("execl vbx-audio");
exit(1);
}
keyboard_pid = fork();
if (keyboard_pid == -1) {
LOG_ERROR("fork: %s", strerror(errno));
LOG_PERROR("fork");
kill(sound_pid, SIGTERM);
close(pipefd[0]);
close(pipefd[1]);
@@ -160,7 +160,7 @@ int start_children(const char *sound_dir, const char *config_path, int volume,
dup2(pipefd[1], STDOUT_FILENO);
close(pipefd[1]);
execl(get_key_presses_path, "vbx-input", (char *)NULL);
LOG_ERROR("execl vbx-input: %s", strerror(errno));
LOG_PERROR("execl vbx-input");
exit(1);
}
close(pipefd[0]);
+8 -8
View File
@@ -1,6 +1,7 @@
#include "app/watch.h"
#include "common/utils.h"
#include "app/process.h"
#include "common/log.h"
#include "common/utils.h"
#include <errno.h>
#include <pthread.h>
#include <stdio.h>
@@ -17,18 +18,17 @@ static void *inotify_thread_fn(void *arg) {
struct inotify_thread_args *a = (struct inotify_thread_args *)arg;
int fd = inotify_init1(IN_NONBLOCK);
if (fd < 0) {
printf("Failed to initialize inotify: %s\n", strerror(errno));
LOG_PERROR("Failed to initialize inotify");
return NULL;
}
int wd = inotify_add_watch(
fd, a->path, IN_CLOSE_WRITE | IN_MOVED_TO | IN_ATTRIB | IN_MODIFY);
if (wd < 0) {
printf("Failed to add inotify watch for %s: %s\n", a->path,
strerror(errno));
LOG_PERROR("Failed to add inotify watch");
close(fd);
return NULL;
}
printf("Watching config file: %s\n", a->path);
LOG_INFO("Watching config file: %s", a->path);
char buf[4096] __attribute__((aligned(__alignof__(struct inotify_event))));
while (1) {
ssize_t len = read(fd, buf, sizeof(buf));
@@ -40,10 +40,10 @@ static void *inotify_thread_fn(void *arg) {
nanosleep(&ts, NULL);
continue;
}
printf("inotify read error: %s\n", strerror(errno));
LOG_PERROR("inotify read error");
break;
}
printf("Config file changed, requesting reload...\n");
LOG_INFO("Config file changed, requesting reload...");
handle_sighup(0);
}
inotify_rm_watch(fd, wd);
@@ -60,7 +60,7 @@ int start_config_watcher(const char *path) {
pthread_detach(inotify_thread);
return 1;
} else {
printf("Warning: Config file %s does not exist, file watching disabled\n",
LOG_WARN("Warning: Config file %s does not exist, file watching disabled",
in_args.path);
return 0;
}
+112 -133
View File
@@ -1,4 +1,6 @@
#include "audio/config.h"
#include "audio/types.h"
#include "common/log.h"
#include "common/utils.h"
#include <errno.h>
#include <json-c/json.h>
@@ -12,7 +14,6 @@ SoundPack g_sound_pack = {0};
SoundPack g_mouse_sound_pack = {0};
float g_volume = 1.0f;
float g_mouse_volume = 1.0f;
int g_verbose = 0;
int g_mute = 0;
int g_keyboard_mute = 0;
int g_mouse_mute = 0;
@@ -32,12 +33,36 @@ static void get_full_path(char *buffer, size_t buffer_size,
}
}
static short *load_pcm_data(const char *path, uint32_t *out_samples, SF_INFO *out_info) {
if (!path || strlen(path) == 0) return NULL;
SF_INFO sf_info = {0};
SNDFILE *sf = sf_open(path, SFM_READ, &sf_info);
if (!sf) return NULL;
int load_sound_config(const char *config_path) {
uint32_t total_samples = (uint32_t)(sf_info.frames * sf_info.channels);
short *buffer = malloc(total_samples * sizeof(short));
if (!buffer) {
sf_close(sf);
return NULL;
}
sf_count_t read = sf_readf_short(sf, buffer, sf_info.frames);
if (read < sf_info.frames) {
// Partial read or error
}
sf_close(sf);
if (out_samples) *out_samples = total_samples;
if (out_info) *out_info = sf_info;
return buffer;
}
int load_sound_config(const char *config_path, SoundPack *pack) {
memset(pack, 0, sizeof(SoundPack));
FILE *file = fopen(config_path, "r");
if (!file) {
safe_fprintf(stderr, "Error: Cannot open sound pack config: %s\n", config_path);
perror("fopen");
LOG_ERROR("Error: Cannot open sound pack config: %s", config_path);
LOG_PERROR("fopen");
return -1;
}
char config_path_copy[1024];
@@ -47,20 +72,20 @@ int load_sound_config(const char *config_path) {
long size = ftell(file);
if (size < 0) {
fclose(file);
safe_fprintf(stderr, "Error: ftell failed while reading %s\n", config_path);
LOG_ERROR("Error: ftell failed while reading %s", config_path);
return -1;
}
errno = 0;
rewind(file);
if (errno != 0) {
fclose(file);
safe_fprintf(stderr, "Error: rewind failed while reading %s\n", config_path);
LOG_ERROR("Error: rewind failed while reading %s", config_path);
return -1;
}
char *json_string = malloc((size_t)size + 1);
if (!json_string) {
fclose(file);
safe_fprintf(stderr, "Error: Memory allocation failed\n");
LOG_ERROR("Error: Memory allocation failed");
return -1;
}
fread(json_string, 1, size, file);
@@ -69,171 +94,125 @@ int load_sound_config(const char *config_path) {
json_object *root = json_tokener_parse(json_string);
free(json_string);
if (!root) {
safe_fprintf(stderr, "Error: Invalid JSON in sound pack config\n");
safe_fprintf(stderr, "Check that the config.json file is properly formatted.\n");
LOG_ERROR("Error: Invalid JSON in sound pack config");
return -1;
}
const char *key_type = "single";
json_object *obj;
if (json_object_object_get_ex(root, "key_define_type", &obj))
key_type = json_object_get_string(obj);
g_sound_pack.is_multi = strcmp(key_type, "multi") == 0;
if (g_verbose) {
printf("Config loaded: Using %s mode\n",
g_sound_pack.is_multi ? "multi" : "single");
}
if (g_sound_pack.is_multi) {
g_sound_pack.num_generic_press_files = 0;
pack->is_multi = strcmp(key_type, "multi") == 0;
LOG_DEBUG("Config loaded: Using %s mode", pack->is_multi ? "multi" : "single");
if (pack->is_multi) {
if (json_object_object_get_ex(root, "sound", &obj)) {
const char *pattern = json_object_get_string(obj);
if (g_verbose)
printf("Sound pattern: %s\n", pattern);
if (strstr(pattern, "%d") || strstr(pattern, "{")) {
for (int i = 0; i <= 4; i++) {
char temp_filename[256];
if (strstr(pattern, "{")) {
char temp_pattern[256];
safe_strncpy(temp_pattern, pattern, sizeof(temp_pattern));
char *brace_start = strstr(temp_pattern, "{");
char *brace_end = strstr(temp_pattern, "}");
if (brace_start && brace_end) {
*brace_start = '%';
*(brace_start + 1) = 'd';
safe_memmove(brace_start + 2, brace_end + 1,
strlen(brace_end + 1) + 1);
}
safe_snprintf_wrapper(temp_filename, sizeof(temp_filename), temp_pattern, i);
} else {
safe_snprintf_wrapper(temp_filename, sizeof(temp_filename), pattern, i);
}
get_full_path(g_sound_pack.generic_press_files[i],
sizeof(g_sound_pack.generic_press_files[i]), config_dir,
temp_filename);
if (access(g_sound_pack.generic_press_files[i], R_OK) == 0) {
g_sound_pack.num_generic_press_files = i + 1;
} else {
if (g_verbose)
printf("Generic sound file not found: %s\n",
g_sound_pack.generic_press_files[i]);
break;
}
// Simple pattern replacement for {0}, {1}, etc.
safe_snprintf(temp_filename, sizeof(temp_filename), "%s", pattern);
char *brace = strstr(temp_filename, "{");
if (brace) {
*brace = '0' + i;
safe_memmove(brace + 1, brace + 3, strlen(brace + 3) + 1);
}
} else {
get_full_path(g_sound_pack.generic_press_files[0],
sizeof(g_sound_pack.generic_press_files[0]), config_dir,
pattern);
if (access(g_sound_pack.generic_press_files[0], R_OK) == 0) {
g_sound_pack.num_generic_press_files = 1;
if (g_verbose)
printf("Found single generic sound file: %s\n",
g_sound_pack.generic_press_files[0]);
safe_snprintf(temp_filename, sizeof(temp_filename), pattern, i);
}
get_full_path(pack->generic_press_files[i], sizeof(pack->generic_press_files[i]), config_dir, temp_filename);
if (access(pack->generic_press_files[i], R_OK) == 0) {
pack->num_generic_press_files = i + 1;
pack->generic_press_data[i] = load_pcm_data(pack->generic_press_files[i], &pack->generic_press_samples[i], NULL);
} else break;
}
} else {
get_full_path(pack->generic_press_files[0], sizeof(pack->generic_press_files[0]), config_dir, pattern);
if (access(pack->generic_press_files[0], R_OK) == 0) {
pack->num_generic_press_files = 1;
pack->generic_press_data[0] = load_pcm_data(pack->generic_press_files[0], &pack->generic_press_samples[0], NULL);
LOG_DEBUG("Generic sound cached: %s", pack->generic_press_files[0]);
}
}
if (g_verbose)
printf("Total generic press sound files: %d\n",
g_sound_pack.num_generic_press_files);
}
if (json_object_object_get_ex(root, "soundup", &obj)) {
char temp_release_file[256];
safe_strncpy(temp_release_file, json_object_get_string(obj),
sizeof(temp_release_file));
get_full_path(g_sound_pack.release_file,
sizeof(g_sound_pack.release_file), config_dir,
temp_release_file);
if (g_verbose)
printf("Release sound file: %s\n", g_sound_pack.release_file);
get_full_path(pack->release_file, sizeof(pack->release_file), config_dir, json_object_get_string(obj));
pack->release_data = load_pcm_data(pack->release_file, &pack->release_samples, NULL);
LOG_DEBUG("Release sound cached: %s", pack->release_file);
}
if (json_object_object_get_ex(root, "defines", &obj)) {
json_object_object_foreach(obj, key, val) {
int key_code;
int key_code = -1;
int is_release = 0;
if (strcmp(key, "MouseLeft") == 0) {
key_code = 272;
} else if (strcmp(key, "MouseRight") == 0) {
key_code = 273;
} else if (strcmp(key, "MouseMiddle") == 0) {
key_code = 274;
} else {
char *dash = strstr(key, "-up");
if (dash) {
char key_copy[16];
safe_strncpy(key_copy, key, dash - key + 1);
key_copy[dash - key] = '\0';
key_code = atoi(key_copy);
is_release = 1;
} else {
key_code = atoi(key);
}
if (strcmp(key, "MouseLeft") == 0) key_code = 272;
else if (strcmp(key, "MouseRight") == 0) key_code = 273;
else if (strcmp(key, "MouseMiddle") == 0) key_code = 274;
else {
char *up = strstr(key, "-up");
if (up) { is_release = 1; key_code = atoi(key); }
else key_code = atoi(key);
}
if (key_code >= 0 && key_code < 512) {
const char *filename_relative = json_object_get_string(val);
char full_filename[1024];
get_full_path(full_filename, sizeof(full_filename), config_dir,
filename_relative);
char full_path[1024];
get_full_path(full_path, sizeof(full_path), config_dir, json_object_get_string(val));
if (is_release) {
if (g_sound_pack.multi_key_mappings[key_code].release)
free(g_sound_pack.multi_key_mappings[key_code].release);
g_sound_pack.multi_key_mappings[key_code].release =
xstrdup(full_filename);
pack->multi_key_mappings[key_code].release = xstrdup(full_path);
pack->multi_key_mappings[key_code].release_data = load_pcm_data(full_path, &pack->multi_key_mappings[key_code].release_samples, NULL);
LOG_DEBUG("Key %d-up cached: %s", key_code, full_path);
} else {
if (g_sound_pack.multi_key_mappings[key_code].press)
free(g_sound_pack.multi_key_mappings[key_code].press);
g_sound_pack.multi_key_mappings[key_code].press =
xstrdup(full_filename);
pack->multi_key_mappings[key_code].press = xstrdup(full_path);
pack->multi_key_mappings[key_code].press_data = load_pcm_data(full_path, &pack->multi_key_mappings[key_code].press_samples, NULL);
LOG_DEBUG("Key %d cached: %s", key_code, full_path);
}
}
}
}
} else {
if (json_object_object_get_ex(root, "sound", &obj) ||
json_object_object_get_ex(root, "audio_file", &obj)) {
char temp_sound_file[256];
safe_strncpy(temp_sound_file, json_object_get_string(obj),
sizeof(temp_sound_file));
get_full_path(g_sound_pack.sound_file, sizeof(g_sound_pack.sound_file),
config_dir, temp_sound_file);
if (g_verbose)
printf("Single mode sound file: %s\n", g_sound_pack.sound_file);
if (json_object_object_get_ex(root, "sound", &obj) || json_object_object_get_ex(root, "audio_file", &obj)) {
get_full_path(pack->sound_file, sizeof(pack->sound_file), config_dir, json_object_get_string(obj));
uint32_t s;
short *d = load_pcm_data(pack->sound_file, &s, &pack->sf_info);
if (d) free(d);
LOG_DEBUG("Single mode sound file: %s (metadata loaded)", pack->sound_file);
}
json_object *defines_obj = NULL;
if (json_object_object_get_ex(root, "defines", &defines_obj) ||
json_object_object_get_ex(root, "definitions", &defines_obj)) {
if (json_object_object_get_ex(root, "defines", &defines_obj) || json_object_object_get_ex(root, "definitions", &defines_obj)) {
json_object_object_foreach(defines_obj, key, val) {
int key_code;
if (strcmp(key, "MouseLeft") == 0) {
key_code = 272;
} else if (strcmp(key, "MouseRight") == 0) {
key_code = 273;
} else if (strcmp(key, "MouseMiddle") == 0) {
key_code = 274;
} else {
key_code = atoi(key);
}
int key_code = -1;
if (strcmp(key, "MouseLeft") == 0) key_code = 272;
else if (strcmp(key, "MouseRight") == 0) key_code = 273;
else if (strcmp(key, "MouseMiddle") == 0) key_code = 274;
else key_code = atoi(key);
if (key_code >= 0 && key_code < 512) {
if (json_object_is_type(val, json_type_array)) {
if (json_object_array_length(val) >= 2) {
g_sound_pack.key_mappings[key_code].start_ms =
json_object_get_int(json_object_array_get_idx(val, 0));
g_sound_pack.key_mappings[key_code].duration_ms =
json_object_get_int(json_object_array_get_idx(val, 1));
}
pack->key_mappings[key_code].start_ms = json_object_get_int(json_object_array_get_idx(val, 0));
pack->key_mappings[key_code].duration_ms = json_object_get_int(json_object_array_get_idx(val, 1));
} else if (json_object_is_type(val, json_type_object)) {
json_object *timing_array;
if (json_object_object_get_ex(val, "timing", &timing_array) &&
json_object_is_type(timing_array, json_type_array) &&
json_object_array_length(timing_array) > 0) {
json_object *first_timing =
json_object_array_get_idx(timing_array, 0);
if (json_object_is_type(first_timing, json_type_array) &&
json_object_array_length(first_timing) >= 2) {
g_sound_pack.key_mappings[key_code].start_ms =
json_object_get_int(
json_object_array_get_idx(first_timing, 0));
g_sound_pack.key_mappings[key_code].duration_ms =
json_object_get_int(
json_object_array_get_idx(first_timing, 1));
json_object *timings;
if (json_object_object_get_ex(val, "timing", &timings) && json_object_array_length(timings) > 0) {
json_object *t = json_object_array_get_idx(timings, 0);
pack->key_mappings[key_code].start_ms = json_object_get_int(json_object_array_get_idx(t, 0));
pack->key_mappings[key_code].duration_ms = json_object_get_int(json_object_array_get_idx(t, 1));
}
}
if (pack->key_mappings[key_code].duration_ms > 0) {
uint32_t sr = pack->sf_info.samplerate;
uint32_t ch = pack->sf_info.channels;
uint32_t start_frame = (pack->key_mappings[key_code].start_ms * sr) / 1000;
uint32_t duration_frames = (pack->key_mappings[key_code].duration_ms * sr) / 1000;
SNDFILE *sf = sf_open(pack->sound_file, SFM_READ, &pack->sf_info);
if (sf) {
sf_seek(sf, start_frame, SEEK_SET);
pack->key_mappings[key_code].num_samples = duration_frames * ch;
pack->key_mappings[key_code].pcm_data = malloc(pack->key_mappings[key_code].num_samples * sizeof(short));
if (pack->key_mappings[key_code].pcm_data) {
sf_readf_short(sf, pack->key_mappings[key_code].pcm_data, duration_frames);
}
sf_close(sf);
}
}
}
}
+32 -48
View File
@@ -1,4 +1,5 @@
#define _XOPEN_SOURCE 500
#include "audio/config.h"
#include "audio/playback.h"
#include "audio/types.h"
#include "common/utils.h"
@@ -18,8 +19,6 @@ int g_mouse_enabled = 1;
int g_system_volume_following = 0;
float g_system_volume_multiplier = 1.0f;
int load_sound_config(const char *config_path);
// Generic function to read runtime state files
static int read_runtime_state(const char *filename_suffix, int default_value) {
char state_file[1024];
@@ -93,98 +92,86 @@ void *system_volume_poller_thread(void *arg) {
} else {
g_system_volume_multiplier = 1.0f;
}
usleep(500000); // 500ms
struct timespec ts = {0, 500000000L};
nanosleep(&ts, NULL); // 500ms
}
return NULL;
}
int main(int argc, char *argv[]) {
if (argc < 2 || argc > 12) {
safe_fprintf(stderr,
"Usage: %s <config.json> [volume] [verbose] [mute] [mouse_config] "
LOG_ERROR("Usage: %s <config.json> [volume] [verbose] [mute] [mouse_config] "
"[mouse_volume] [keyboard_mute] [mouse_mute] [keyboard_enabled] "
"[mouse_enabled] [system_volume_following]\n",
"[mouse_enabled] [system_volume_following]",
argv[0]);
safe_fprintf(stderr, " volume: 0-100 (default: 50)\n");
safe_fprintf(stderr, " verbose: 1 to enable verbose output (default: 0)\n");
safe_fprintf(stderr, " mute: 1 to mute all sound (default: 0)\n");
safe_fprintf(stderr, " mouse_config: path to mouse config.json (optional)\n");
safe_fprintf(stderr, " mouse_volume: 0-100 for mouse volume (optional)\n");
safe_fprintf(stderr, " keyboard_mute: 1 to mute keyboard sounds (optional)\n");
safe_fprintf(stderr, " mouse_mute: 1 to mute mouse sounds (optional)\n");
safe_fprintf(stderr,
" keyboard_enabled: 1 to enable keyboard sounds (optional)\n");
safe_fprintf(stderr, " mouse_enabled: 1 to enable mouse sounds (optional)\n");
LOG_ERROR(" volume: 0-100 (default: 50)");
LOG_ERROR(" verbose: 1 to enable verbose output (default: 0)");
LOG_ERROR(" mute: 1 to mute all sound (default: 0)");
LOG_ERROR(" mouse_config: path to mouse config.json (optional)");
LOG_ERROR(" mouse_volume: 0-100 for mouse volume (optional)");
LOG_ERROR(" keyboard_mute: 1 to mute keyboard sounds (optional)");
LOG_ERROR(" mouse_mute: 1 to mute mouse sounds (optional)");
LOG_ERROR(" keyboard_enabled: 1 to enable keyboard sounds (optional)");
LOG_ERROR(" mouse_enabled: 1 to enable mouse sounds (optional)");
return 1;
}
if (argc >= 3) {
int volume_percent = validate_volume(atoi(argv[2]));
g_volume = volume_percent / 100.0f;
if (g_verbose)
printf("Volume set to: %d%%\n", volume_percent);
LOG_DEBUG("Volume set to: %d%%", volume_percent);
} else {
if (g_verbose)
printf("Volume set to: 50%% (default)\n");
LOG_DEBUG("Volume set to: 50%% (default)");
}
if (argc >= 4) {
g_verbose = atoi(argv[3]);
if (g_verbose) {
printf("Verbose mode enabled\n");
LOG_INFO("Verbose mode enabled");
}
}
if (argc >= 5) {
g_mute = atoi(argv[4]);
if (g_mute) {
printf("Sound muted\n");
LOG_INFO("Sound muted");
}
}
if (argc >= 6) {
if (load_sound_config(argv[5]) != 0) {
safe_fprintf(stderr, "Failed to load mouse sound configuration\n");
if (load_sound_config(argv[5], &g_mouse_sound_pack) != 0) {
LOG_ERROR("Failed to load mouse sound configuration");
return 1;
}
g_mouse_sound_pack = g_sound_pack;
if (g_verbose) {
printf("Mouse sound pack loaded from: %s\n", argv[5]);
}
LOG_DEBUG("Mouse sound pack loaded from: %s", argv[5]);
}
if (argc >= 7) {
int mouse_volume_percent = validate_volume(atoi(argv[6]));
g_mouse_volume = mouse_volume_percent / 100.0f;
if (g_verbose)
printf("Mouse volume set to: %d%%\n", mouse_volume_percent);
LOG_DEBUG("Mouse volume set to: %d%%", mouse_volume_percent);
}
if (argc >= 8) {
g_keyboard_mute = atoi(argv[7]);
if (g_verbose)
printf("Keyboard mute: %s\n", g_keyboard_mute ? "enabled" : "disabled");
LOG_DEBUG("Keyboard mute: %s", g_keyboard_mute ? "enabled" : "disabled");
}
if (argc >= 9) {
g_mouse_mute = atoi(argv[8]);
if (g_verbose)
printf("Mouse mute: %s\n", g_mouse_mute ? "enabled" : "disabled");
LOG_DEBUG("Mouse mute: %s", g_mouse_mute ? "enabled" : "disabled");
}
if (argc >= 10) {
g_keyboard_enabled = atoi(argv[9]);
if (g_verbose)
printf("Keyboard enabled: %s\n", g_keyboard_enabled ? "yes" : "no");
LOG_DEBUG("Keyboard enabled: %s", g_keyboard_enabled ? "yes" : "no");
}
if (argc >= 11) {
g_mouse_enabled = atoi(argv[10]);
if (g_verbose)
printf("Mouse enabled: %s\n", g_mouse_enabled ? "yes" : "no");
LOG_DEBUG("Mouse enabled: %s", g_mouse_enabled ? "yes" : "no");
}
if (argc >= 12) {
g_system_volume_following = atoi(argv[11]);
if (g_verbose)
printf("System volume following: %s\n", g_system_volume_following ? "yes" : "no");
LOG_DEBUG("System volume following: %s", g_system_volume_following ? "yes" : "no");
}
if (load_sound_config(argv[1]) != 0) {
safe_fprintf(stderr, "Failed to load keyboard sound configuration\n");
if (load_sound_config(argv[1], &g_sound_pack) != 0) {
LOG_ERROR("Failed to load keyboard sound configuration");
return 1;
}
if (init_audio() != 0) {
safe_fprintf(stderr, "Failed to initialize audio\n");
LOG_ERROR("Failed to initialize audio");
return 1;
}
@@ -212,12 +199,9 @@ int main(int argc, char *argv[]) {
if (errno == EINTR) {
continue;
}
perror("select");
LOG_PERROR("select");
break;
} else if (ready == 0) {
if (g_verbose) {
printf("Waiting for input...\n");
}
continue;
}
if (FD_ISSET(STDIN_FILENO, &readfds)) {
@@ -226,7 +210,7 @@ int main(int argc, char *argv[]) {
if (feof(stdin)) {
LOG_DEBUG("EOF reached on stdin");
} else {
perror("fread");
LOG_PERROR("fread");
}
break;
}
+123 -210
View File
@@ -1,3 +1,4 @@
#define _DEFAULT_SOURCE
#include "audio/playback.h"
#include "audio/types.h"
#include "common/utils.h"
@@ -10,10 +11,13 @@
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <math.h>
pthread_t sound_threads[MAX_CONCURRENT_SOUNDS];
volatile int thread_active[MAX_CONCURRENT_SOUNDS] = {0};
pthread_mutex_t thread_mutex = PTHREAD_MUTEX_INITIALIZER;
static pa_simple *g_pa_handle = NULL;
static AudioVoice g_mixer_voices[MAX_MIXER_VOICES] = {0};
static pthread_mutex_t g_mixer_mutex = PTHREAD_MUTEX_INITIALIZER;
static pthread_t g_mixer_thread;
static volatile int g_mixer_running = 0;
static inline float get_boosted_system_volume(float vol_multiplier) {
if (vol_multiplier >= 1.0f) return vol_multiplier;
@@ -22,236 +26,145 @@ static inline float get_boosted_system_volume(float vol_multiplier) {
return 1.0f - (inv * inv * inv);
}
int init_audio() {
if (g_sound_pack.is_multi)
return 0;
if (strlen(g_sound_pack.sound_file) == 0) {
LOG_ERROR("No sound file specified in sound pack config");
LOG_ERROR("Check that your sound pack has a valid config.json file.");
return -1;
}
if (access(g_sound_pack.sound_file, R_OK) != 0) {
LOG_ERROR("Sound file not accessible: %s", g_sound_pack.sound_file);
perror("access");
return -1;
}
SNDFILE *test_sf =
sf_open(g_sound_pack.sound_file, SFM_READ, &g_sound_pack.sf_info);
if (!test_sf) {
LOG_ERROR("Could not open sound file: %s", g_sound_pack.sound_file);
LOG_ERROR("libsndfile error: %s", sf_strerror(NULL));
return -1;
}
sf_close(test_sf);
LOG_DEBUG("Sound file info: %ld frames, %d channels, %d Hz",
(long)g_sound_pack.sf_info.frames, g_sound_pack.sf_info.channels,
g_sound_pack.sf_info.samplerate);
return 0;
void *mixer_thread_func(void *arg) {
(void)arg;
const int chunk_frames = 512;
const int channels = g_sound_pack.sf_info.channels ? g_sound_pack.sf_info.channels : 2;
short *mix_buffer = malloc(chunk_frames * channels * sizeof(short));
while (g_mixer_running) {
memset(mix_buffer, 0, chunk_frames * channels * sizeof(short));
int any_active = 0;
pthread_mutex_lock(&g_mixer_mutex);
float sys_vol = get_boosted_system_volume(g_system_volume_multiplier);
for (int i = 0; i < MAX_MIXER_VOICES; i++) {
if (!g_mixer_voices[i].active) continue;
any_active = 1;
uint32_t samples_to_copy = chunk_frames * channels;
if (g_mixer_voices[i].current_pos + samples_to_copy > g_mixer_voices[i].total_samples) {
samples_to_copy = g_mixer_voices[i].total_samples - g_mixer_voices[i].current_pos;
}
void *play_sound_thread(void *arg) {
PlaybackData *data = (PlaybackData *)arg;
int key_code = data->key_code;
int thread_id = data->thread_id;
int is_pressed = data->is_pressed;
const char *file_to_play = NULL;
int is_mouse_event = (key_code == 272 || key_code == 273 || key_code == 274);
SoundPack *sound_pack = is_mouse_event ? &g_mouse_sound_pack : &g_sound_pack;
float volume = is_mouse_event ? g_mouse_volume : g_volume;
int mute_state = is_mouse_event ? g_mouse_mute : g_keyboard_mute;
LOG_DEBUG("Thread %d: Playing %s sound for key %d (%s)", thread_id,
is_mouse_event ? "mouse" : "keyboard", key_code,
is_pressed ? "press" : "release");
if (g_mute || mute_state) {
LOG_DEBUG("Thread %d: %s sound is muted", thread_id,
is_mouse_event ? "Mouse" : "Keyboard");
goto exit_cleanup;
float vol = g_mixer_voices[i].volume * sys_vol;
for (uint32_t s = 0; s < samples_to_copy; s++) {
int32_t mixed = mix_buffer[s] + (int32_t)(g_mixer_voices[i].data[g_mixer_voices[i].current_pos + s] * vol);
// Clip
if (mixed > 32767) mixed = 32767;
else if (mixed < -32768) mixed = -32768;
mix_buffer[s] = (short)mixed;
}
if (sound_pack->is_multi) {
if (is_pressed && sound_pack->multi_key_mappings[key_code].press)
file_to_play = sound_pack->multi_key_mappings[key_code].press;
else if (!is_pressed && sound_pack->multi_key_mappings[key_code].release)
file_to_play = sound_pack->multi_key_mappings[key_code].release;
else if (is_pressed && sound_pack->num_generic_press_files > 0) {
int idx = rand() % sound_pack->num_generic_press_files;
file_to_play = sound_pack->generic_press_files[idx];
} else if (!is_pressed && strlen(sound_pack->release_file) > 0)
file_to_play = sound_pack->release_file;
if (!file_to_play) {
LOG_DEBUG("Thread %d: No sound file found for key %d (%s)", thread_id,
key_code, is_pressed ? "press" : "release");
goto exit_cleanup;
g_mixer_voices[i].current_pos += samples_to_copy;
if (g_mixer_voices[i].current_pos >= g_mixer_voices[i].total_samples) {
g_mixer_voices[i].active = 0;
}
LOG_DEBUG("Thread %d: Using sound file: %s", thread_id, file_to_play);
SF_INFO sf_info = {0};
SNDFILE *sf = sf_open(file_to_play, SFM_READ, &sf_info);
if (!sf) {
LOG_ERROR("Could not open sound file: %s", file_to_play);
LOG_ERROR("Details: %s", sf_strerror(NULL));
LOG_ERROR("Check that the audio file exists and is readable.");
goto exit_cleanup;
}
pa_sample_spec ss = {.format = PA_SAMPLE_S16LE,
.rate = sf_info.samplerate,
.channels = sf_info.channels};
pthread_mutex_unlock(&g_mixer_mutex);
if (any_active) {
int pa_error;
pa_simple *pa_handle =
pa_simple_new(NULL, "KeyboardSounds", PA_STREAM_PLAYBACK, NULL,
"playback", &ss, NULL, NULL, &pa_error);
if (!pa_handle) {
LOG_ERROR("Could not initialize PulseAudio: %s", pa_strerror(pa_error));
sf_close(sf);
goto exit_cleanup;
if (pa_simple_write(g_pa_handle, mix_buffer, chunk_frames * channels * sizeof(short), &pa_error) < 0) {
LOG_ERROR("PulseAudio write error: %s", pa_strerror(pa_error));
}
int frames = 2048;
short *buffer = malloc(frames * sf_info.channels * sizeof(short));
if (!buffer) {
pa_simple_free(pa_handle);
sf_close(sf);
goto exit_cleanup;
}
sf_count_t read;
while ((read = sf_readf_short(sf, buffer, frames)) > 0) {
float vol_mult = volume * get_boosted_system_volume(g_system_volume_multiplier);
for (sf_count_t i = 0; i < read * sf_info.channels; i++) {
buffer[i] = (short)(buffer[i] * vol_mult);
}
int pa_write_error;
if (pa_simple_write(pa_handle, buffer,
read * sf_info.channels * sizeof(short),
&pa_write_error) < 0) {
LOG_ERROR("PulseAudio write error: %s", pa_strerror(pa_write_error));
break;
}
}
int pa_drain_error;
pa_simple_drain(pa_handle, &pa_drain_error);
pa_simple_free(pa_handle);
sf_close(sf);
free(buffer);
} else {
if (key_code >= 512 ||
sound_pack->key_mappings[key_code].duration_ms == 0) {
LOG_DEBUG("Thread %d: No mapping for key %d", thread_id, key_code);
goto exit_cleanup;
// Nothing playing, sleep a bit to avoid CPU spin
struct timespec ts = {0, 10000000L};
nanosleep(&ts, NULL); // 10ms
}
SoundMapping *mapping = &sound_pack->key_mappings[key_code];
SF_INFO sf_info = sound_pack->sf_info;
SNDFILE *sf = sf_open(sound_pack->sound_file, SFM_READ, &sf_info);
if (!sf) {
LOG_ERROR("Thread: Could not open sound file");
goto exit_cleanup;
}
pa_sample_spec ss = {.format = PA_SAMPLE_S16LE,
.rate = sf_info.samplerate,
.channels = sf_info.channels};
int pa_error;
pa_simple *pa_handle =
pa_simple_new(NULL, "KeyboardSounds", PA_STREAM_PLAYBACK, NULL,
"playback", &ss, NULL, NULL, &pa_error);
if (!pa_handle) {
sf_close(sf);
LOG_ERROR("Thread: Could not initialize PulseAudio: %s", pa_strerror(pa_error));
goto exit_cleanup;
}
sf_count_t start_frame = (mapping->start_ms * sf_info.samplerate) / 1000;
sf_count_t duration_frames =
(mapping->duration_ms * sf_info.samplerate) / 1000;
sf_seek(sf, start_frame, SEEK_SET);
short *buffer = malloc(duration_frames * sf_info.channels * sizeof(short));
if (!buffer) {
pa_simple_free(pa_handle);
sf_close(sf);
goto exit_cleanup;
}
sf_count_t frames_read = sf_readf_short(sf, buffer, duration_frames);
float vol_mult = volume * get_boosted_system_volume(g_system_volume_multiplier);
for (sf_count_t i = 0; i < frames_read * sf_info.channels; i++) {
buffer[i] = (short)(buffer[i] * vol_mult);
}
int pa_write_error, pa_drain_error;
pa_simple_write(pa_handle, buffer,
frames_read * sf_info.channels * sizeof(short),
&pa_write_error);
pa_simple_drain(pa_handle, &pa_drain_error);
free(buffer);
pa_simple_free(pa_handle);
sf_close(sf);
}
exit_cleanup:
pthread_mutex_lock(&thread_mutex);
thread_active[thread_id] = 0;
pthread_mutex_unlock(&thread_mutex);
free(data);
free(mix_buffer);
return NULL;
}
int find_available_thread_slot() {
pthread_mutex_lock(&thread_mutex);
for (int i = 0; i < MAX_CONCURRENT_SOUNDS; i++) {
if (!thread_active[i]) {
pthread_mutex_unlock(&thread_mutex);
return i;
}
}
pthread_mutex_unlock(&thread_mutex);
int init_audio() {
// Use keyboard pack as reference for samplerate/channels
pa_sample_spec ss = {
.format = PA_SAMPLE_S16LE,
.rate = g_sound_pack.sf_info.samplerate ? g_sound_pack.sf_info.samplerate : 44100,
.channels = g_sound_pack.sf_info.channels ? g_sound_pack.sf_info.channels : 2
};
int pa_error;
g_pa_handle = pa_simple_new(NULL, "vbx", PA_STREAM_PLAYBACK, NULL, "clicks", &ss, NULL, NULL, &pa_error);
if (!g_pa_handle) {
LOG_ERROR("Could not initialize PulseAudio stream: %s", pa_strerror(pa_error));
return -1;
}
g_mixer_running = 1;
if (pthread_create(&g_mixer_thread, NULL, mixer_thread_func, NULL) != 0) {
LOG_ERROR("Failed to create mixer thread");
return -1;
}
return 0;
}
void play_sound_segment(int key_code, int is_pressed) {
if (g_mute) {
LOG_DEBUG("Sound muted - ignoring key %d (%s)", key_code,
is_pressed ? "press" : "release");
return;
}
if (g_mute) return;
// Check if this is a mouse event and if mouse is disabled
int is_mouse_event = (key_code == 272 || key_code == 273 || key_code == 274);
if (is_mouse_event) {
// For mouse events, we need to check if mouse is enabled
// This will be handled by the main loop passing enabled state
// For now, we'll add a simple check here
extern int g_mouse_enabled;
if (!g_mouse_enabled) {
LOG_DEBUG("Mouse sounds disabled - ignoring mouse event %d", key_code);
return;
int is_mouse = (key_code == 272 || key_code == 273 || key_code == 274);
int device_enabled = is_mouse ? g_mouse_enabled : g_keyboard_enabled;
if (!device_enabled) return;
SoundPack *pack = is_mouse ? &g_mouse_sound_pack : &g_sound_pack;
float volume = is_mouse ? g_mouse_volume : g_volume;
int device_mute = is_mouse ? g_mouse_mute : g_keyboard_mute;
if (device_mute) return;
short *pcm_data = NULL;
uint32_t num_samples = 0;
if (pack->is_multi) {
if (is_pressed) {
if (pack->multi_key_mappings[key_code].press_data) {
pcm_data = pack->multi_key_mappings[key_code].press_data;
num_samples = pack->multi_key_mappings[key_code].press_samples;
} else if (pack->num_generic_press_files > 0) {
int idx = rand() % pack->num_generic_press_files;
pcm_data = pack->generic_press_data[idx];
num_samples = pack->generic_press_samples[idx];
}
} else {
// For keyboard events, check if keyboard is enabled
extern int g_keyboard_enabled;
if (!g_keyboard_enabled) {
LOG_DEBUG("Keyboard sounds disabled - ignoring key %d", key_code);
return;
if (pack->multi_key_mappings[key_code].release_data) {
pcm_data = pack->multi_key_mappings[key_code].release_data;
num_samples = pack->multi_key_mappings[key_code].release_samples;
} else if (pack->release_data) {
pcm_data = pack->release_data;
num_samples = pack->release_samples;
}
}
if (!g_sound_pack.is_multi && !is_pressed) {
LOG_DEBUG("Single mode: Ignoring key release for key %d", key_code);
return;
}
int slot = find_available_thread_slot();
if (slot == -1) {
LOG_WARN("No available thread slots");
return;
}
PlaybackData *data = malloc(sizeof(PlaybackData));
if (!data)
return;
data->key_code = key_code;
data->thread_id = slot;
data->is_pressed = is_pressed;
pthread_mutex_lock(&thread_mutex);
thread_active[slot] = 1;
if (pthread_create(&sound_threads[slot], NULL, play_sound_thread, data) !=
0) {
thread_active[slot] = 0;
free(data);
LOG_ERROR("Failed to create sound thread");
} else {
pthread_detach(sound_threads[slot]);
if (is_pressed && key_code < 512) {
pcm_data = pack->key_mappings[key_code].pcm_data;
num_samples = pack->key_mappings[key_code].num_samples;
}
pthread_mutex_unlock(&thread_mutex);
}
if (!pcm_data || num_samples == 0) return;
// Find a slot in the mixer
pthread_mutex_lock(&g_mixer_mutex);
int found = 0;
for (int i = 0; i < MAX_MIXER_VOICES; i++) {
if (!g_mixer_voices[i].active) {
g_mixer_voices[i].data = pcm_data;
g_mixer_voices[i].total_samples = num_samples;
g_mixer_voices[i].current_pos = 0;
g_mixer_voices[i].volume = volume;
g_mixer_voices[i].active = 1;
found = 1;
break;
}
}
pthread_mutex_unlock(&g_mixer_mutex);
if (!found) {
LOG_WARN("Mixer full, dropping sound");
}
}
+5 -5
View File
@@ -1,4 +1,5 @@
#include "app/cli.h"
#include "common/log.h"
#include "common/utils.h"
#include "config.h"
#include <getopt.h>
@@ -18,8 +19,8 @@ static int parse_device_argument(const char *optarg, const char *action,
} else if (strcmp(optarg, "mouse") == 0) {
*mouse_value = value;
} else {
safe_fprintf(stderr, "Error: Invalid device '%s' for %s\n", optarg, action);
safe_fprintf(stderr, "Valid options: keyboard, mouse, or both\n");
LOG_ERROR("Error: Invalid device '%s' for %s", optarg, action);
LOG_ERROR("Valid options: keyboard, mouse, or both");
return 1;
}
return 0;
@@ -138,9 +139,8 @@ int parse_cli(int argc, char **argv, CliOptions *out) {
(n == 7 && strncmp(a, "disable", 7) == 0) ||
(n == 7 && strncmp(a, "verbose", 7) == 0) ||
(n == 23 && strncmp(a, "system-volume-following", 23) == 0))) {
safe_fprintf(stderr, "Error: Unknown option '%s'\n", argv[i]);
safe_fprintf(stderr, "Run '%s --help' to see available options.\n",
argv[0]);
LOG_ERROR("Error: Unknown option '%s'", argv[i]);
LOG_ERROR("Run '%s --help' to see available options.", argv[0]);
return 1;
}
}
+21 -14
View File
@@ -1,6 +1,8 @@
#define _POSIX_C_SOURCE 200809L
#include "common/utils.h"
#include "common/ipc.h"
#include "common/log.h"
#include <time.h>
#include <errno.h>
#include <stdbool.h>
#include <stdint.h>
@@ -51,7 +53,7 @@ static int open_restricted(const char *path, int flags, void *user_data) {
(void)user_data;
int fd = open(path, flags);
if (fd < 0)
errorf("Failed to open %s because of %s.\n", path, strerror(errno));
LOG_PERROR("Failed to open path");
return fd < 0 ? -errno : fd;
}
@@ -121,15 +123,17 @@ static int run_mainloop(struct libinput *libinput) {
fd.fd = libinput_get_fd(libinput);
fd.events = POLLIN;
fd.revents = 0;
if (handle_events(libinput) != 0)
return errorf("Expected device added events on startup but got none. Maybe "
"you don't have the right permissions?\n");
if (handle_events(libinput) != 0) {
LOG_ERROR("Expected device added events on startup but got none. Maybe you don't have the right permissions?");
return -1;
}
while (1) {
int pr = poll(&fd, 1, -1);
int pr = poll(&fd, 1, 10);
if (pr < 0) {
if (errno == EINTR)
continue;
return errorf("poll failed: %s\n", strerror(errno));
LOG_PERROR("poll failed");
return -1;
}
handle_events(libinput);
}
@@ -148,7 +152,6 @@ void print_help(char *program_name) {
"will run this.\n");
}
int g_verbose = 0;
int is_daemon = 0;
int main(int argc, char *argv[]) {
@@ -172,19 +175,23 @@ int main(int argc, char *argv[]) {
case '?':
break;
default:
errorf("%s: Invalid option `-%c`.\n", argv[0], opt);
LOG_ERROR("Invalid option `-%c`.", opt);
break;
}
}
struct udev *udev = udev_new();
if (udev == NULL)
return errorf("Failed to initialize udev.\n");
if (udev == NULL) {
LOG_ERROR("Failed to initialize udev.");
return 1;
}
struct libinput *libinput =
libinput_udev_create_context(&interface, NULL, udev);
if (!libinput)
return errorf("Failed to initialize libinput from udev.\n");
if (!libinput) {
LOG_ERROR("Failed to initialize libinput from udev.");
return 1;
}
if (libinput_udev_assign_seat(libinput, "seat0") != 0) {
errorf("Failed to set seat.\n");
LOG_ERROR("Failed to set seat.");
libinput_unref(libinput);
udev_unref(udev);
return SEAT_FAILED;
@@ -193,7 +200,7 @@ int main(int argc, char *argv[]) {
struct input_handler_data input_handler_data = {udev, libinput};
if (pthread_create(&input_handler, NULL, handle_input, &input_handler_data) !=
0) {
errorf("Failed to create input handler thread.\n");
LOG_ERROR("Failed to create input handler thread.");
} else {
pthread_detach(input_handler);
}
+27 -31
View File
@@ -71,9 +71,7 @@ int main(int argc, char *argv[]) {
char user_cfg_path[MAX_PATH_LENGTH];
if (get_user_config_path(user_cfg_path, sizeof(user_cfg_path))) {
if (access(user_cfg_path, R_OK) == 0) {
if (verbose) {
LOG_INFO("Loading configuration from: %s", user_cfg_path);
}
LOG_DEBUG("Loading configuration from: %s", user_cfg_path);
char *cfg_keyboard_sound = NULL;
char *cfg_mouse_sound = NULL;
int cfg_keyboard_volume = current_keyboard_volume;
@@ -144,10 +142,9 @@ int main(int argc, char *argv[]) {
// Show welcome message for first-time users
if (access(user_cfg_path, F_OK) != 0 && !flag_daemon) {
printf("\nWelcome to VBX!\n");
printf("This appears to be your first time running VBX.\n");
printf("A default configuration will be created at: %s\n", user_cfg_path);
printf("\n");
LOG_INFO("Welcome to VBX!");
LOG_INFO("This appears to be your first time running VBX.");
LOG_INFO("A default configuration will be created at: %s", user_cfg_path);
}
build_pidfile_path(pidfile_path, 1024);
if (flag_stop) {
@@ -168,7 +165,8 @@ int main(int argc, char *argv[]) {
if (mouse_sound_name_owned) {
free(mouse_sound_name);
}
return (perror("kill"), 1);
LOG_PERROR("kill");
return 1;
}
for (int i = 0; i < 30; i++) {
if (!process_is_running(running_pid)) {
@@ -193,8 +191,8 @@ int main(int argc, char *argv[]) {
if (mouse_sound_name_owned) {
free(mouse_sound_name);
}
return errorf(
"VBX: daemon did not stop in time. Try running 'vbx --stop' again.\n");
LOG_ERROR("VBX: daemon did not stop in time. Try running 'vbx --stop' again.");
return 1;
}
current_mute = read_runtime_mute_file();
@@ -232,36 +230,36 @@ int main(int argc, char *argv[]) {
current_keyboard_mute = cli_opts.keyboard_mute;
config_updated = 1;
if (current_keyboard_mute) {
printf("Keyboard sounds muted.\n");
LOG_INFO("Keyboard sounds muted.");
} else {
printf("Keyboard sounds unmuted.\n");
LOG_INFO("Keyboard sounds unmuted.");
}
}
if (cli_opts.mouse_mute >= 0) {
current_mouse_mute = cli_opts.mouse_mute;
config_updated = 1;
if (current_mouse_mute) {
printf("Mouse sounds muted.\n");
LOG_INFO("Mouse sounds muted.");
} else {
printf("Mouse sounds unmuted.\n");
LOG_INFO("Mouse sounds unmuted.");
}
}
if (cli_opts.keyboard_enabled >= 0) {
current_keyboard_enabled = cli_opts.keyboard_enabled;
config_updated = 1;
if (current_keyboard_enabled) {
printf("Keyboard sounds enabled.\n");
LOG_INFO("Keyboard sounds enabled.");
} else {
printf("Keyboard sounds disabled.\n");
LOG_INFO("Keyboard sounds disabled.");
}
}
if (cli_opts.mouse_enabled >= 0) {
current_mouse_enabled = cli_opts.mouse_enabled;
config_updated = 1;
if (current_mouse_enabled) {
printf("Mouse sounds enabled.\n");
LOG_INFO("Mouse sounds enabled.");
} else {
printf("Mouse sounds disabled.\n");
LOG_INFO("Mouse sounds disabled.");
}
}
@@ -280,8 +278,8 @@ int main(int argc, char *argv[]) {
current_system_volume_following)) {
LOG_WARN("Failed to update config file %s", user_cfg_path);
LOG_WARN("Your settings will not be saved for next time.");
} else if (verbose) {
LOG_INFO("Updated config file %s", user_cfg_path);
} else {
LOG_DEBUG("Updated config file %s", user_cfg_path);
}
}
@@ -340,8 +338,8 @@ int main(int argc, char *argv[]) {
if (mouse_sound_name_owned) {
free(mouse_sound_name);
}
return errorf("Error: Cannot find or execute required binaries in %s\n",
VBX_BIN_DIR);
LOG_ERROR("Error: Cannot find or execute required binaries in %s", VBX_BIN_DIR);
return 1;
}
signal(SIGINT, cleanup_processes);
signal(SIGTERM, cleanup_processes);
@@ -405,17 +403,15 @@ int main(int argc, char *argv[]) {
LOG_INFO("Mouse config file: %s", mouse_config_path);
LOG_INFO("Keyboard working directory: %s", sound_dir);
LOG_INFO("Mouse working directory: %s", mouse_sound_dir);
LOG_INFO("Press Ctrl+C to exit.");
LOG_DEBUG("Press Ctrl+C to exit.");
} else {
if (!is_daemon) {
printf("VBX daemon started successfully!\n");
printf(" Keyboard: %s (volume: %d%%)\n", sound_name,
current_keyboard_volume);
printf(" Mouse: %s (volume: %d%%)\n", mouse_sound_name,
current_mouse_volume);
printf(" Config: ~/.vbx.json (auto-reload enabled)\n");
printf("\nUse 'vbx --stop' to stop the daemon.\n");
printf("Press Ctrl+C to exit.\n");
LOG_INFO("VBX daemon started successfully!");
LOG_INFO(" Keyboard: %s (volume: %d%%)", sound_name, current_keyboard_volume);
LOG_INFO(" Mouse: %s (volume: %d%%)", mouse_sound_name, current_mouse_volume);
LOG_INFO(" Config: ~/.vbx.json (auto-reload enabled)");
LOG_INFO("Use 'vbx --stop' to stop the daemon.");
LOG_INFO("Press Ctrl+C to exit.");
}
}
if (!start_children(sound_dir, config_path, current_keyboard_volume, verbose, current_mute,
+35 -36
View File
@@ -1,4 +1,5 @@
#include "soundpacks.h"
#include "common/log.h"
#include "common/utils.h"
#include "config.h"
#include <dirent.h>
@@ -129,24 +130,21 @@ int build_paths_for_mouse_sound(const char *sound_name, char *out_config_path,
int validate_keyboard_sound_pack(const char *sound_name) {
char basedir[1024];
if (!resolve_keyboard_sound_base_dir(sound_name, basedir, sizeof(basedir))) {
fprintf(
stderr,
"Error: Keyboard sound pack '%s' not found in user or system dirs.\n",
LOG_ERROR("Error: Keyboard sound pack '%s' not found in user or system dirs.",
sound_name);
fprintf(stderr, "Use --list to see available sound packs.\n");
LOG_ERROR("Use --list to see available sound packs.");
return 0;
}
char config_path[1024];
if ((size_t)snprintf(config_path, sizeof(config_path), "%s/%s/config.json",
basedir, sound_name) >= sizeof(config_path)) {
fprintf(stderr, "Error: Sound pack path too long: %s/%s\n", basedir,
sound_name);
fprintf(stderr, "Try using a shorter sound pack name.\n");
LOG_ERROR("Error: Sound pack path too long: %s/%s", basedir, sound_name);
LOG_ERROR("Try using a shorter sound pack name.");
return 0;
}
if (access(config_path, R_OK) != 0) {
fprintf(stderr, "Error: Sound pack config not found: %s\n", config_path);
fprintf(stderr, "Make sure the sound pack directory contains a valid config.json file.\n");
LOG_ERROR("Error: Sound pack config not found: %s", config_path);
LOG_ERROR("Make sure the sound pack directory contains a valid config.json file.");
return 0;
}
return 1;
@@ -155,23 +153,21 @@ int validate_keyboard_sound_pack(const char *sound_name) {
int validate_mouse_sound_pack(const char *sound_name) {
char basedir[1024];
if (!resolve_mouse_sound_base_dir(sound_name, basedir, sizeof(basedir))) {
fprintf(stderr,
"Error: Mouse sound pack '%s' not found in user or system dirs.\n",
LOG_ERROR("Error: Mouse sound pack '%s' not found in user or system dirs.",
sound_name);
fprintf(stderr, "Use --list to see available sound packs.\n");
LOG_ERROR("Use --list to see available sound packs.");
return 0;
}
char config_path[1024];
if ((size_t)snprintf(config_path, sizeof(config_path), "%s/%s/config.json",
basedir, sound_name) >= sizeof(config_path)) {
fprintf(stderr, "Error: Sound pack path too long: %s/%s\n", basedir,
sound_name);
fprintf(stderr, "Try using a shorter sound pack name.\n");
LOG_ERROR("Error: Sound pack path too long: %s/%s", basedir, sound_name);
LOG_ERROR("Try using a shorter sound pack name.");
return 0;
}
if (access(config_path, R_OK) != 0) {
fprintf(stderr, "Error: Sound pack config not found: %s\n", config_path);
fprintf(stderr, "Make sure the sound pack directory contains a valid config.json file.\n");
LOG_ERROR("Error: Sound pack config not found: %s", config_path);
LOG_ERROR("Make sure the sound pack directory contains a valid config.json file.");
return 0;
}
return 1;
@@ -198,24 +194,25 @@ int list_sound_packs(void) {
USER_MOUSE_AUDIO_SUBPATH);
}
printf("Available Sound Packs\n");
printf("=====================\n\n");
printf("\nKEYBOARD SOUND PACKS:\n");
printf("======================\n");
printf("%-30s %-15s\n", "Pack Name", "Source");
printf("%-30s %-15s\n", "---------", "------");
LOG_INFO("Available Sound Packs");
LOG_INFO("=====================");
LOG_INFO(" ");
LOG_INFO("KEYBOARD SOUND PACKS:");
LOG_INFO("======================");
LOG_INFO("%-30s %-15s", "Pack Name", "Source");
LOG_INFO("%-30s %-15s", "---------", "------");
if (user_keyboard_dir[0] && (dir = opendir(user_keyboard_dir)) != NULL) {
while ((entry = readdir(dir)) != NULL) {
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
continue;
if ((size_t)snprintf(path, sizeof(path), "%s/%s", user_keyboard_dir,
entry->d_name) >= sizeof(path)) {
fprintf(stderr, "Warning: Path too long, skipping: %s/%s\n",
LOG_WARN("Warning: Path too long, skipping: %s/%s",
user_keyboard_dir, entry->d_name);
continue;
}
if (stat(path, &st) == 0 && S_ISDIR(st.st_mode)) {
printf("%-30s %-15s\n", entry->d_name, "user");
LOG_INFO("%-30s %-15s", entry->d_name, "user");
}
}
closedir(dir);
@@ -227,32 +224,33 @@ int list_sound_packs(void) {
continue;
if ((size_t)snprintf(path, sizeof(path), "%s/%s", KEYBOARD_AUDIO_DIR,
entry->d_name) >= sizeof(path)) {
fprintf(stderr, "Warning: Path too long, skipping: %s/%s\n",
LOG_WARN("Warning: Path too long, skipping: %s/%s",
KEYBOARD_AUDIO_DIR, entry->d_name);
continue;
}
if (stat(path, &st) == 0 && S_ISDIR(st.st_mode)) {
printf("%-30s %-15s\n", entry->d_name, "system");
LOG_INFO("%-30s %-15s", entry->d_name, "system");
}
}
closedir(dir);
}
printf("\nMOUSE SOUND PACKS:\n");
printf("==================\n");
printf("%-30s %-15s\n", "Pack Name", "Source");
printf("%-30s %-15s\n", "---------", "------");
LOG_INFO(" ");
LOG_INFO("MOUSE SOUND PACKS:");
LOG_INFO("==================");
LOG_INFO("%-30s %-15s", "Pack Name", "Source");
LOG_INFO("%-30s %-15s", "---------", "------");
if (user_mouse_dir[0] && (dir = opendir(user_mouse_dir)) != NULL) {
while ((entry = readdir(dir)) != NULL) {
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
continue;
if ((size_t)snprintf(path, sizeof(path), "%s/%s", user_mouse_dir,
entry->d_name) >= sizeof(path)) {
fprintf(stderr, "Warning: Path too long, skipping: %s/%s\n",
LOG_WARN("Warning: Path too long, skipping: %s/%s",
user_mouse_dir, entry->d_name);
continue;
}
if (stat(path, &st) == 0 && S_ISDIR(st.st_mode)) {
printf("%-30s %-15s\n", entry->d_name, "user");
LOG_INFO("%-30s %-15s", entry->d_name, "user");
}
}
closedir(dir);
@@ -264,16 +262,17 @@ int list_sound_packs(void) {
continue;
if ((size_t)snprintf(path, sizeof(path), "%s/%s", MOUSE_AUDIO_DIR,
entry->d_name) >= sizeof(path)) {
fprintf(stderr, "Warning: Path too long, skipping: %s/%s\n",
LOG_WARN("Warning: Path too long, skipping: %s/%s",
MOUSE_AUDIO_DIR, entry->d_name);
continue;
}
if (stat(path, &st) == 0 && S_ISDIR(st.st_mode)) {
printf("%-30s %-15s\n", entry->d_name, "system");
LOG_INFO("%-30s %-15s", entry->d_name, "system");
}
}
closedir(dir);
}
printf("\nUsage: vbx -S <pack-name> for keyboard or vbx -M <pack-name> for mouse\n");
LOG_INFO(" ");
LOG_INFO("Usage: vbx -S <pack-name> for keyboard or vbx -M <pack-name> for mouse");
return 0;
}