Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
69b6ecd2d5 | ||
|
|
373e64fcad | ||
|
|
21cada4969 | ||
|
|
563ebdbbab | ||
|
|
a776be0bf3 | ||
|
|
f7a2a7270b | ||
|
|
95255580b1 | ||
|
|
f70e13dd70 | ||
|
|
c44ae01226 | ||
|
|
ffef8d7eaf | ||
|
|
6658d6561b | ||
|
|
93491af2e2 |
No files matched your search
@@ -3,6 +3,9 @@
|
||||
/keyvibe
|
||||
/keyvibe-audio
|
||||
/keyvibe-input
|
||||
/audio
|
||||
/input
|
||||
/vbx
|
||||
|
||||
# Object and static/shared library files
|
||||
*.o
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
CC = gcc
|
||||
# Updated include paths for modular headers
|
||||
CFLAGS = -Wall -Wextra -std=c99 -Iinclude -Iinclude/app -Iinclude/audio -Iinclude/common -Iinclude/sound -Iinclude/config
|
||||
CFLAGS = -Wall -Wextra -std=c99 -D_POSIX_C_SOURCE=200809L -Iinclude -Iinclude/app -Iinclude/audio -Iinclude/common -Iinclude/sound -Iinclude/config
|
||||
PREFIX ?= /usr
|
||||
|
||||
# Pass PACKAGE_PREFIX macro for config.h
|
||||
|
||||
@@ -72,14 +72,28 @@ sudo make install
|
||||
-u --unmute Unmute sound
|
||||
-- enbale/disable DEVICE_NAME Enable/disable a device
|
||||
-l, --list List available sound packs
|
||||
-f, --system-volume-following=0/1 Enable/disable dynamic tracking of system volume (default: 0)
|
||||
-h, --help Show this help message
|
||||
-v, --verbose Enable verbose output
|
||||
|
||||
Notes:
|
||||
|
||||
- First run creates `~/.vbx.json`. Subsequent runs use it unless `-c` is supplied.
|
||||
- First run creates `~/.vbx.json`. Subsequent runs use it unless `--config` is supplied.
|
||||
- In daemon mode, editing `~/.vbx.json` will automatically reload.
|
||||
|
||||
## 🔊 Volume Mechanics
|
||||
|
||||
vbx lets you control your volume in three ways:
|
||||
1. **Keyboard Volume** (`--volume`): Sets the base volume for your keyboard sounds (from 0 to 100).
|
||||
2. **Mouse Volume** (`--mouse-volume`): Sets the base volume for your mouse clicks (from 0 to 100).
|
||||
3. **System Volume Multiplexer** (`--system-volume-following=1`): When turned on, `vbx` automatically scales the app's sound to match your computer's main volume slider.
|
||||
|
||||
**How the multiplexer works:**
|
||||
Instead of just cutting the volume linearly (which makes sounds too quiet at 50% OS volume), the multiplexer uses a gentle boost curve. This means if you lower your computer's volume, your typing sounds will still be punchy and easy to hear without ever being too loud!
|
||||
|
||||
**Example:**
|
||||
If you set your keyboard volume to `50%` (`vbx --volume 50 --system-volume-following 1`), and your computer's master volume is currently at `80%`, `vbx` will intelligently scale its own output down to securely match your OS levels, without you having to change your `vbx` settings every time you watch a quiet video!
|
||||
|
||||
## 🎵 Sound Packs
|
||||
|
||||
System packs are installed under: `/usr/share/vbx/soundpacks/`
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 1.4 MiB |
@@ -15,6 +15,7 @@ typedef struct {
|
||||
int mouse_mute; // -1 unchanged, 0 unmute, 1 mute
|
||||
int keyboard_enabled; // -1 unchanged, 0 disabled, 1 enabled
|
||||
int mouse_enabled; // -1 unchanged, 0 disabled, 1 enabled
|
||||
int system_volume_following; // -1 unchanged, 0 disabled, 1 enabled
|
||||
} CliOptions;
|
||||
|
||||
void print_usage(const char *program_name);
|
||||
|
||||
@@ -17,7 +17,8 @@ void stop_children(void);
|
||||
int start_children(const char *sound_dir, const char *config_path, int volume,
|
||||
int verbose, int mute, const char *mouse_sound_dir,
|
||||
const char *mouse_config_path, int mouse_volume,
|
||||
int keyboard_mute, int mouse_mute, int keyboard_enabled, int mouse_enabled);
|
||||
int keyboard_mute, int mouse_mute, int keyboard_enabled,
|
||||
int mouse_enabled, int system_volume_following);
|
||||
void handle_sighup(int sig);
|
||||
|
||||
#endif // VBX_PROCESS_H
|
||||
@@ -7,8 +7,9 @@ int handle_reload(const char *user_cfg_path,
|
||||
char *current_config_path, char *current_sound_dir,
|
||||
char *current_mouse_config_path, char *current_mouse_sound_dir,
|
||||
int current_mute, int current_verbose,
|
||||
int current_mouse_volume_arg, int current_keyboard_mute,
|
||||
int current_mouse_mute, int *current_keyboard_enabled, int *current_mouse_enabled);
|
||||
int current_keyboard_mute, int current_mouse_mute,
|
||||
int *current_keyboard_enabled, int *current_mouse_enabled,
|
||||
int *current_system_volume_following);
|
||||
|
||||
#endif // VBX_RELOAD_H
|
||||
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
#ifndef VBX_AUDIO_CONFIG_H
|
||||
#define VBX_AUDIO_CONFIG_H
|
||||
|
||||
#include "audio/types.h"
|
||||
|
||||
int load_sound_config(const char *config_path, SoundPack *pack);
|
||||
|
||||
#endif // VBX_AUDIO_CONFIG_H
|
||||
@@ -2,7 +2,6 @@
|
||||
#define VBX_AUDIO_PLAYBACK_H
|
||||
|
||||
int init_audio(void);
|
||||
int parse_keyboard_event(const char *json_line, int *key_code, int *is_pressed);
|
||||
void play_sound_segment(int key_code, int is_pressed);
|
||||
|
||||
#endif // VBX_AUDIO_PLAYBACK_H
|
||||
|
||||
@@ -8,23 +8,43 @@
|
||||
typedef struct {
|
||||
int start_ms;
|
||||
int duration_ms;
|
||||
short *pcm_data;
|
||||
uint32_t num_samples;
|
||||
} SoundMapping;
|
||||
|
||||
typedef struct {
|
||||
char press_file[256];
|
||||
char release_file[256];
|
||||
char generic_press_files[5][256];
|
||||
short *generic_press_data[5];
|
||||
uint32_t generic_press_samples[5];
|
||||
int num_generic_press_files;
|
||||
char sound_file[256];
|
||||
short *release_data;
|
||||
uint32_t release_samples;
|
||||
SoundMapping key_mappings[512];
|
||||
struct {
|
||||
char *press;
|
||||
char *release;
|
||||
short *press_data;
|
||||
uint32_t press_samples;
|
||||
short *release_data;
|
||||
uint32_t release_samples;
|
||||
} multi_key_mappings[512];
|
||||
int is_multi;
|
||||
SF_INFO sf_info;
|
||||
} SoundPack;
|
||||
|
||||
typedef struct {
|
||||
short *data;
|
||||
uint32_t total_samples;
|
||||
uint32_t current_pos;
|
||||
float volume;
|
||||
int active;
|
||||
} AudioVoice;
|
||||
|
||||
#define MAX_MIXER_VOICES 32
|
||||
|
||||
typedef struct {
|
||||
int key_code;
|
||||
int thread_id;
|
||||
@@ -41,5 +61,7 @@ extern int g_keyboard_mute;
|
||||
extern int g_mouse_mute;
|
||||
extern int g_keyboard_enabled;
|
||||
extern int g_mouse_enabled;
|
||||
extern int g_system_volume_following;
|
||||
extern float g_system_volume_multiplier;
|
||||
|
||||
#endif // VBX_AUDIO_TYPES_H
|
||||
@@ -0,0 +1,13 @@
|
||||
#ifndef VBX_IPC_H
|
||||
#define VBX_IPC_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
// Lightweight binary structure for IPC between vbx-input and vbx-audio
|
||||
// Replaces the heavy JSON parsing
|
||||
typedef struct {
|
||||
uint32_t key_code;
|
||||
uint8_t is_pressed;
|
||||
} VbxEvent;
|
||||
|
||||
#endif // VBX_IPC_H
|
||||
@@ -0,0 +1,34 @@
|
||||
#ifndef VBX_LOG_H
|
||||
#define VBX_LOG_H
|
||||
|
||||
#include <stdio.h>
|
||||
#include <time.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
|
||||
// Assuming g_verbose is defined globally or we pass it
|
||||
extern int g_verbose;
|
||||
extern int is_daemon;
|
||||
|
||||
#define LOG_LEVEL_DEBUG 0
|
||||
#define LOG_LEVEL_INFO 1
|
||||
#define LOG_LEVEL_WARN 2
|
||||
#define LOG_LEVEL_ERROR 3
|
||||
|
||||
#define LOG_MACRO(level, prefix, out_stream, ...) do { \
|
||||
if (level == LOG_LEVEL_DEBUG && !g_verbose) break; \
|
||||
flockfile(out_stream); \
|
||||
fprintf(out_stream, "[VBX-%s] ", prefix); \
|
||||
fprintf(out_stream, __VA_ARGS__); \
|
||||
fprintf(out_stream, "\n"); \
|
||||
fflush(out_stream); \
|
||||
funlockfile(out_stream); \
|
||||
} while(0)
|
||||
|
||||
#define LOG_DEBUG(...) LOG_MACRO(LOG_LEVEL_DEBUG, "DEBUG", stdout, __VA_ARGS__)
|
||||
#define LOG_INFO(...) LOG_MACRO(LOG_LEVEL_INFO, "INFO", stdout, __VA_ARGS__)
|
||||
#define LOG_WARN(...) LOG_MACRO(LOG_LEVEL_WARN, "WARN", stderr, __VA_ARGS__)
|
||||
#define LOG_ERROR(...) LOG_MACRO(LOG_LEVEL_ERROR, "ERROR", stderr, __VA_ARGS__)
|
||||
#define LOG_PERROR(msg) LOG_MACRO(LOG_LEVEL_ERROR, "ERROR", stderr, "%s: %s", msg, strerror(errno))
|
||||
|
||||
#endif // VBX_LOG_H
|
||||
@@ -8,10 +8,6 @@
|
||||
// Returns 1 on success, 0 on truncation/error
|
||||
int safe_snprintf(char *dst, size_t dst_sz, const char *fmt, ...);
|
||||
|
||||
// fprintf to stderr with printf-style formatting. Returns 1 for convenient
|
||||
// `return errorf(...)` usage.
|
||||
int errorf(const char *fmt, ...);
|
||||
|
||||
// Common runtime helpers
|
||||
const char *get_runtime_dir(void);
|
||||
void build_pidfile_path(char *buffer, size_t buflen);
|
||||
@@ -29,6 +25,9 @@ void write_runtime_mouse_mute_file(int mute);
|
||||
int write_runtime_keyboard_enabled_file(int enabled);
|
||||
int write_runtime_mouse_enabled_file(int enabled);
|
||||
|
||||
// System volume following helper
|
||||
int write_runtime_system_volume_following_file(int enabled);
|
||||
|
||||
// Helper functions
|
||||
void int_to_str(char *buffer, size_t size, int value);
|
||||
char *xstrdup(const char *s);
|
||||
|
||||
@@ -8,11 +8,13 @@ char *get_user_config_path(char *buffer, size_t buflen);
|
||||
// Write ~/.vbx.json in new format
|
||||
int write_user_config(const char *path, const char *keyboard_sound,
|
||||
const char *mouse_sound, int keyboard_volume,
|
||||
int mouse_volume, int keyboard_enabled, int mouse_enabled);
|
||||
int mouse_volume, int keyboard_enabled, int mouse_enabled,
|
||||
int system_volume_following);
|
||||
|
||||
// Read ~/.vbx.json supporting new and legacy formats
|
||||
int read_user_config(const char *path, char **out_keyboard_sound,
|
||||
char **out_mouse_sound, int *out_keyboard_volume,
|
||||
int *out_mouse_volume, int *out_keyboard_enabled, int *out_mouse_enabled);
|
||||
int *out_mouse_volume, int *out_keyboard_enabled, int *out_mouse_enabled,
|
||||
int *out_system_volume_following);
|
||||
|
||||
#endif // VBX_CONFIG_IO_H
|
||||
+16
-10
@@ -1,11 +1,14 @@
|
||||
#define _POSIX_C_SOURCE 200809L
|
||||
#include "app/process.h"
|
||||
#include "common/log.h"
|
||||
#include "common/utils.h"
|
||||
#include "config.h"
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
@@ -21,7 +24,7 @@ volatile sig_atomic_t reload_requested = 0;
|
||||
void cleanup_processes(int sig) {
|
||||
(void)sig;
|
||||
if (!is_daemon) {
|
||||
printf("\nShutting down VBX daemon...\n");
|
||||
LOG_INFO("Shutting down VBX daemon...");
|
||||
}
|
||||
if (sound_pid > 0) {
|
||||
kill(sound_pid, SIGTERM);
|
||||
@@ -42,7 +45,7 @@ int require_running_pid(pid_t *out_pid) {
|
||||
pid_t running_pid = 0;
|
||||
if (!read_pidfile(pidfile_path, &running_pid) ||
|
||||
!process_is_running(running_pid)) {
|
||||
fprintf(stderr, "VBX daemon is not running.\n");
|
||||
LOG_ERROR("VBX daemon is not running.");
|
||||
return 0;
|
||||
}
|
||||
*out_pid = running_pid;
|
||||
@@ -92,17 +95,17 @@ int start_children(const char *sound_dir, const char *config_path, int volume,
|
||||
int verbose, int mute, const char *mouse_sound_dir,
|
||||
const char *mouse_config_path, int mouse_volume,
|
||||
int keyboard_mute, int mouse_mute, int keyboard_enabled,
|
||||
int mouse_enabled) {
|
||||
int mouse_enabled, int system_volume_following) {
|
||||
(void)config_path;
|
||||
(void)mouse_sound_dir;
|
||||
int pipefd[2];
|
||||
if (pipe(pipefd) == -1) {
|
||||
perror("pipe");
|
||||
LOG_PERROR("pipe");
|
||||
return 0;
|
||||
}
|
||||
sound_pid = fork();
|
||||
if (sound_pid == -1) {
|
||||
perror("fork");
|
||||
LOG_PERROR("fork");
|
||||
close(pipefd[0]);
|
||||
close(pipefd[1]);
|
||||
return 0;
|
||||
@@ -115,12 +118,13 @@ int start_children(const char *sound_dir, const char *config_path, int volume,
|
||||
dup2(pipefd[0], STDIN_FILENO);
|
||||
close(pipefd[0]);
|
||||
if (chdir(sound_dir) != 0) {
|
||||
perror("chdir");
|
||||
LOG_ERROR("chdir: %s", strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
char volume_str[32], mouse_volume_str[32], verbose_str[8], mute_str[8];
|
||||
char keyboard_mute_str[8], mouse_mute_str[8];
|
||||
char keyboard_enabled_str[8], mouse_enabled_str[8];
|
||||
char system_volume_following_str[8];
|
||||
|
||||
int_to_str(volume_str, sizeof(volume_str), volume);
|
||||
int_to_str(mouse_volume_str, sizeof(mouse_volume_str), mouse_volume);
|
||||
@@ -130,17 +134,19 @@ int start_children(const char *sound_dir, const char *config_path, int volume,
|
||||
int_to_str(mouse_mute_str, sizeof(mouse_mute_str), mouse_mute);
|
||||
int_to_str(keyboard_enabled_str, sizeof(keyboard_enabled_str), keyboard_enabled);
|
||||
int_to_str(mouse_enabled_str, sizeof(mouse_enabled_str), mouse_enabled);
|
||||
int_to_str(system_volume_following_str, sizeof(system_volume_following_str),
|
||||
system_volume_following);
|
||||
|
||||
execl(sound_player_path, "vbx-audio", "config.json", volume_str,
|
||||
verbose_str, mute_str, mouse_config_path, mouse_volume_str,
|
||||
keyboard_mute_str, mouse_mute_str, keyboard_enabled_str,
|
||||
mouse_enabled_str, (char *)NULL);
|
||||
perror("execl vbx-audio");
|
||||
mouse_enabled_str, system_volume_following_str, (char *)NULL);
|
||||
LOG_PERROR("execl vbx-audio");
|
||||
exit(1);
|
||||
}
|
||||
keyboard_pid = fork();
|
||||
if (keyboard_pid == -1) {
|
||||
perror("fork");
|
||||
LOG_PERROR("fork");
|
||||
kill(sound_pid, SIGTERM);
|
||||
close(pipefd[0]);
|
||||
close(pipefd[1]);
|
||||
@@ -154,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);
|
||||
perror("execl vbx-input");
|
||||
LOG_PERROR("execl vbx-input");
|
||||
exit(1);
|
||||
}
|
||||
close(pipefd[0]);
|
||||
|
||||
+28
-19
@@ -1,5 +1,6 @@
|
||||
#include "app/reload.h"
|
||||
#include "app/process.h"
|
||||
#include "common/log.h"
|
||||
#include "common/utils.h"
|
||||
#include "soundpacks.h"
|
||||
#include "user_config.h"
|
||||
@@ -12,26 +13,28 @@ int handle_reload(const char *user_cfg_path, char *current_sound_name,
|
||||
int *current_mouse_volume, char *current_config_path,
|
||||
char *current_sound_dir, char *current_mouse_config_path,
|
||||
char *current_mouse_sound_dir, int current_mute,
|
||||
int current_verbose, int current_mouse_volume_arg,
|
||||
int current_keyboard_mute, int current_mouse_mute,
|
||||
int *current_keyboard_enabled, int *current_mouse_enabled) {
|
||||
int current_verbose, int current_keyboard_mute,
|
||||
int current_mouse_mute, int *current_keyboard_enabled,
|
||||
int *current_mouse_enabled, int *current_system_volume_following) {
|
||||
int g_verbose = current_verbose;
|
||||
char *cfg_keyboard_sound = NULL;
|
||||
char *cfg_mouse_sound = NULL;
|
||||
int cfg_keyboard_volume = *current_keyboard_volume;
|
||||
int cfg_mouse_volume = *current_mouse_volume;
|
||||
int cfg_keyboard_enabled = *current_keyboard_enabled;
|
||||
int cfg_mouse_enabled = *current_mouse_enabled;
|
||||
int cfg_system_volume_following = *current_system_volume_following;
|
||||
if (read_user_config(user_cfg_path, &cfg_keyboard_sound, &cfg_mouse_sound,
|
||||
&cfg_keyboard_volume, &cfg_mouse_volume,
|
||||
&cfg_keyboard_enabled, &cfg_mouse_enabled)) {
|
||||
printf("Read config - keyboard:%s mouse:%s kvol:%d mvol:%d kenabled:%d "
|
||||
"menabled:%d\n",
|
||||
&cfg_keyboard_enabled, &cfg_mouse_enabled, &cfg_system_volume_following)) {
|
||||
LOG_DEBUG("Read config - keyboard:%s mouse:%s kvol:%d mvol:%d kenabled:%d "
|
||||
"menabled:%d",
|
||||
cfg_keyboard_sound ? cfg_keyboard_sound : "NULL",
|
||||
cfg_mouse_sound ? cfg_mouse_sound : "NULL", cfg_keyboard_volume,
|
||||
cfg_mouse_volume, cfg_keyboard_enabled, cfg_mouse_enabled);
|
||||
int keyboard_changed = 0, mouse_changed = 0, keyboard_volume_changed = 0,
|
||||
mouse_volume_changed = 0, keyboard_enabled_changed = 0,
|
||||
mouse_enabled_changed = 0;
|
||||
mouse_enabled_changed = 0, sysvol_following_changed = 0;
|
||||
if (cfg_keyboard_sound &&
|
||||
strcmp(current_sound_name, cfg_keyboard_sound) != 0) {
|
||||
keyboard_changed = 1;
|
||||
@@ -62,18 +65,23 @@ int handle_reload(const char *user_cfg_path, char *current_sound_name,
|
||||
mouse_enabled_changed = 1;
|
||||
*current_mouse_enabled = cfg_mouse_enabled;
|
||||
}
|
||||
printf("Config changed - keyboard:%d mouse:%d kvol:%d mvol:%d kenabled:%d "
|
||||
"menabled:%d\n",
|
||||
if (cfg_system_volume_following != *current_system_volume_following) {
|
||||
sysvol_following_changed = 1;
|
||||
*current_system_volume_following = cfg_system_volume_following;
|
||||
}
|
||||
LOG_DEBUG("Config changed - keyboard:%d mouse:%d kvol:%d mvol:%d kenabled:%d "
|
||||
"menabled:%d",
|
||||
keyboard_changed, mouse_changed, keyboard_volume_changed,
|
||||
mouse_volume_changed, keyboard_enabled_changed,
|
||||
mouse_enabled_changed);
|
||||
if (keyboard_changed || mouse_changed || keyboard_volume_changed ||
|
||||
mouse_volume_changed || keyboard_enabled_changed ||
|
||||
mouse_enabled_changed) {
|
||||
mouse_enabled_changed || sysvol_following_changed) {
|
||||
// Persist enabled state to runtime files so the audio process picks it up
|
||||
// immediately
|
||||
write_runtime_keyboard_enabled_file(*current_keyboard_enabled);
|
||||
write_runtime_mouse_enabled_file(*current_mouse_enabled);
|
||||
write_runtime_system_volume_following_file(*current_system_volume_following);
|
||||
int keyboard_valid = validate_keyboard_sound_pack(current_sound_name);
|
||||
int mouse_valid = validate_mouse_sound_pack(current_mouse_sound_name);
|
||||
if (keyboard_valid && mouse_valid) {
|
||||
@@ -87,31 +95,32 @@ int handle_reload(const char *user_cfg_path, char *current_sound_name,
|
||||
start_children(current_sound_dir, current_config_path,
|
||||
*current_keyboard_volume, current_verbose,
|
||||
current_mute, current_mouse_sound_dir,
|
||||
current_mouse_config_path, current_mouse_volume_arg,
|
||||
current_mouse_config_path, *current_mouse_volume,
|
||||
current_keyboard_mute, current_mouse_mute,
|
||||
*current_keyboard_enabled, *current_mouse_enabled);
|
||||
printf("Reloaded successfully: keyboard=%s, mouse=%s, kvol=%d, "
|
||||
"mvol=%d\n",
|
||||
*current_keyboard_enabled, *current_mouse_enabled,
|
||||
*current_system_volume_following);
|
||||
LOG_INFO("Reloaded successfully: keyboard=%s, mouse=%s, kvol=%d, "
|
||||
"mvol=%d",
|
||||
current_sound_name, current_mouse_sound_name,
|
||||
*current_keyboard_volume, *current_mouse_volume);
|
||||
return 1;
|
||||
} else {
|
||||
printf("Failed to build mouse sound paths\n");
|
||||
LOG_ERROR("Failed to build mouse sound paths");
|
||||
}
|
||||
} else {
|
||||
printf("Failed to build keyboard sound paths\n");
|
||||
LOG_ERROR("Failed to build keyboard sound paths");
|
||||
}
|
||||
} else {
|
||||
if (!keyboard_valid)
|
||||
printf("Reload failed: invalid keyboard sound pack '%s'\n",
|
||||
LOG_ERROR("Reload failed: invalid keyboard sound pack '%s'",
|
||||
current_sound_name);
|
||||
if (!mouse_valid)
|
||||
printf("Reload failed: invalid mouse sound pack '%s'\n",
|
||||
LOG_ERROR("Reload failed: invalid mouse sound pack '%s'",
|
||||
current_mouse_sound_name);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
printf("Failed to read user config\n");
|
||||
LOG_ERROR("Failed to read user config");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
+8
-8
@@ -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;
|
||||
}
|
||||
|
||||
+110
-131
@@ -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;
|
||||
|
||||
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) {
|
||||
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,170 +94,124 @@ 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);
|
||||
// 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 {
|
||||
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;
|
||||
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(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]);
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+94
-64
@@ -1,19 +1,23 @@
|
||||
#define _XOPEN_SOURCE 500
|
||||
#include "audio/config.h"
|
||||
#include "audio/playback.h"
|
||||
#include "audio/types.h"
|
||||
#include "common/utils.h"
|
||||
#include "common/ipc.h"
|
||||
#include "common/log.h"
|
||||
#include <errno.h>
|
||||
#include <json-c/json.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/select.h>
|
||||
#include <sys/time.h>
|
||||
#include <unistd.h>
|
||||
#include <pthread.h>
|
||||
|
||||
int g_keyboard_enabled = 1;
|
||||
int g_mouse_enabled = 1;
|
||||
|
||||
int load_sound_config(const char *config_path);
|
||||
int g_system_volume_following = 0;
|
||||
float g_system_volume_multiplier = 1.0f;
|
||||
|
||||
// Generic function to read runtime state files
|
||||
static int read_runtime_state(const char *filename_suffix, int default_value) {
|
||||
@@ -59,101 +63,133 @@ static int read_mouse_enabled_state() {
|
||||
return read_runtime_state("mouse-enabled", 1);
|
||||
}
|
||||
|
||||
static int read_system_volume_following_state() {
|
||||
return read_runtime_state("sysvol-following", 0);
|
||||
}
|
||||
|
||||
void *system_volume_poller_thread(void *arg) {
|
||||
(void)arg;
|
||||
while (1) {
|
||||
g_mute = read_mute_state();
|
||||
g_keyboard_mute = read_keyboard_mute_state();
|
||||
g_mouse_mute = read_mouse_mute_state();
|
||||
g_keyboard_enabled = read_keyboard_enabled_state();
|
||||
g_mouse_enabled = read_mouse_enabled_state();
|
||||
g_system_volume_following = read_system_volume_following_state();
|
||||
|
||||
if (g_system_volume_following) {
|
||||
char buffer[128];
|
||||
FILE *fp = popen("wpctl get-volume @DEFAULT_AUDIO_SINK@", "r");
|
||||
if (fp != NULL) {
|
||||
if (fgets(buffer, sizeof(buffer), fp) != NULL) {
|
||||
float vol = 1.0f;
|
||||
if (sscanf(buffer, "Volume: %f", &vol) == 1) {
|
||||
g_system_volume_multiplier = vol;
|
||||
}
|
||||
}
|
||||
pclose(fp);
|
||||
}
|
||||
} else {
|
||||
g_system_volume_multiplier = 1.0f;
|
||||
}
|
||||
struct timespec ts = {0, 500000000L};
|
||||
nanosleep(&ts, NULL); // 500ms
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
if (argc < 2 || argc > 11) {
|
||||
safe_fprintf(stderr,
|
||||
"Usage: %s <config.json> [volume] [verbose] [mute] [mouse_config] "
|
||||
"[mouse_volume] [keyboard_mute] [mouse_mute] [keyboard_enabled] "
|
||||
"[mouse_enabled]\n",
|
||||
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");
|
||||
if (argc < 2 || argc > 12) {
|
||||
LOG_ERROR("Usage: %s <config.json> [volume] [verbose] [mute] [mouse_config] "
|
||||
"[mouse_volume] [keyboard_mute] [mouse_mute] [keyboard_enabled] "
|
||||
"[mouse_enabled] [system_volume_following]",
|
||||
argv[0]);
|
||||
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 (load_sound_config(argv[1]) != 0) {
|
||||
safe_fprintf(stderr, "Failed to load keyboard sound configuration\n");
|
||||
if (argc >= 12) {
|
||||
g_system_volume_following = atoi(argv[11]);
|
||||
LOG_DEBUG("System volume following: %s", g_system_volume_following ? "yes" : "no");
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
// Read states initially before starting threads and loops
|
||||
g_mute = read_mute_state();
|
||||
g_keyboard_mute = read_keyboard_mute_state();
|
||||
g_mouse_mute = read_mouse_mute_state();
|
||||
g_keyboard_enabled = read_keyboard_enabled_state();
|
||||
g_mouse_enabled = read_mouse_enabled_state();
|
||||
g_system_volume_following = read_system_volume_following_state();
|
||||
|
||||
pthread_t poller_thread;
|
||||
pthread_create(&poller_thread, NULL, system_volume_poller_thread, NULL);
|
||||
pthread_detach(poller_thread);
|
||||
|
||||
fd_set readfds;
|
||||
struct timeval timeout;
|
||||
char line[1024];
|
||||
while (1) {
|
||||
g_mute = read_mute_state();
|
||||
g_keyboard_mute = read_keyboard_mute_state();
|
||||
g_mouse_mute = read_mouse_mute_state();
|
||||
|
||||
g_keyboard_enabled = read_keyboard_enabled_state();
|
||||
g_mouse_enabled = read_mouse_enabled_state();
|
||||
|
||||
FD_ZERO(&readfds);
|
||||
FD_SET(STDIN_FILENO, &readfds);
|
||||
timeout.tv_sec = 1;
|
||||
@@ -163,28 +199,22 @@ 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)) {
|
||||
if (fgets(line, sizeof(line), stdin) == NULL) {
|
||||
VbxEvent ev;
|
||||
if (fread(&ev, sizeof(VbxEvent), 1, stdin) != 1) {
|
||||
if (feof(stdin)) {
|
||||
if (g_verbose)
|
||||
printf("EOF reached on stdin\n");
|
||||
LOG_DEBUG("EOF reached on stdin");
|
||||
} else {
|
||||
perror("fgets");
|
||||
LOG_PERROR("fread");
|
||||
}
|
||||
break;
|
||||
}
|
||||
int key_code, is_pressed;
|
||||
if (parse_keyboard_event(line, &key_code, &is_pressed) == 0) {
|
||||
play_sound_segment(key_code, is_pressed);
|
||||
}
|
||||
play_sound_segment(ev.key_code, ev.is_pressed);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
|
||||
+137
-273
@@ -1,7 +1,8 @@
|
||||
#define _DEFAULT_SOURCE
|
||||
#include "audio/playback.h"
|
||||
#include "audio/types.h"
|
||||
#include "common/utils.h"
|
||||
#include <json-c/json.h>
|
||||
#include "common/log.h"
|
||||
#include <pthread.h>
|
||||
#include <pulse/error.h>
|
||||
#include <pulse/simple.h>
|
||||
@@ -10,297 +11,160 @@
|
||||
#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;
|
||||
|
||||
int init_audio() {
|
||||
if (g_sound_pack.is_multi)
|
||||
return 0;
|
||||
if (strlen(g_sound_pack.sound_file) == 0) {
|
||||
fprintf(stderr, "Error: No sound file specified in sound pack config\n");
|
||||
fprintf(stderr, "Check that your sound pack has a valid config.json file.\n");
|
||||
return -1;
|
||||
}
|
||||
if (access(g_sound_pack.sound_file, R_OK) != 0) {
|
||||
fprintf(stderr, "Sound file not accessible: %s\n", 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) {
|
||||
fprintf(stderr, "Could not open sound file: %s\n", g_sound_pack.sound_file);
|
||||
fprintf(stderr, "libsndfile error: %s\n", sf_strerror(NULL));
|
||||
return -1;
|
||||
}
|
||||
sf_close(test_sf);
|
||||
if (g_verbose) {
|
||||
printf("Sound file info: %ld frames, %d channels, %d Hz\n",
|
||||
g_sound_pack.sf_info.frames, g_sound_pack.sf_info.channels,
|
||||
g_sound_pack.sf_info.samplerate);
|
||||
}
|
||||
return 0;
|
||||
static inline float get_boosted_system_volume(float vol_multiplier) {
|
||||
if (vol_multiplier >= 1.0f) return vol_multiplier;
|
||||
if (vol_multiplier <= 0.0f) return 0.0f;
|
||||
float inv = 1.0f - vol_multiplier;
|
||||
return 1.0f - (inv * inv * inv);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
if (g_verbose) {
|
||||
printf("Thread %d: Playing %s sound for key %d (%s)\n", thread_id,
|
||||
is_mouse_event ? "mouse" : "keyboard", key_code,
|
||||
is_pressed ? "press" : "release");
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
pthread_mutex_unlock(&g_mixer_mutex);
|
||||
|
||||
if (any_active) {
|
||||
int pa_error;
|
||||
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));
|
||||
}
|
||||
} else {
|
||||
// Nothing playing, sleep a bit to avoid CPU spin
|
||||
struct timespec ts = {0, 10000000L};
|
||||
nanosleep(&ts, NULL); // 10ms
|
||||
}
|
||||
}
|
||||
|
||||
if (g_mute || mute_state) {
|
||||
if (g_verbose) {
|
||||
printf("Thread %d: %s sound is muted\n", thread_id,
|
||||
is_mouse_event ? "Mouse" : "Keyboard");
|
||||
}
|
||||
goto exit_cleanup;
|
||||
}
|
||||
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) {
|
||||
if (g_verbose) {
|
||||
printf("Thread %d: No sound file found for key %d (%s)\n", thread_id,
|
||||
key_code, is_pressed ? "press" : "release");
|
||||
}
|
||||
goto exit_cleanup;
|
||||
}
|
||||
if (g_verbose) {
|
||||
printf("Thread %d: Using sound file: %s\n", thread_id, file_to_play);
|
||||
}
|
||||
SF_INFO sf_info = {0};
|
||||
SNDFILE *sf = sf_open(file_to_play, SFM_READ, &sf_info);
|
||||
if (!sf) {
|
||||
fprintf(stderr, "Error: Could not open sound file: %s\n", file_to_play);
|
||||
fprintf(stderr, "Details: %s\n", sf_strerror(NULL));
|
||||
fprintf(stderr, "Check that the audio file exists and is readable.\n");
|
||||
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) {
|
||||
fprintf(stderr, "Could not initialize PulseAudio: %s\n",
|
||||
pa_strerror(pa_error));
|
||||
sf_close(sf);
|
||||
goto exit_cleanup;
|
||||
}
|
||||
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) {
|
||||
for (sf_count_t i = 0; i < read * sf_info.channels; i++) {
|
||||
buffer[i] = (short)(buffer[i] * volume);
|
||||
}
|
||||
int pa_write_error;
|
||||
if (pa_simple_write(pa_handle, buffer,
|
||||
read * sf_info.channels * sizeof(short),
|
||||
&pa_write_error) < 0) {
|
||||
fprintf(stderr, "PulseAudio write error: %s\n",
|
||||
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) {
|
||||
if (g_verbose) {
|
||||
printf("Thread %d: No mapping for key %d\n", thread_id, key_code);
|
||||
}
|
||||
goto exit_cleanup;
|
||||
}
|
||||
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) {
|
||||
fprintf(stderr, "Thread: Could not open sound file\n");
|
||||
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);
|
||||
fprintf(stderr, "Thread: Could not initialize PulseAudio: %s\n",
|
||||
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);
|
||||
for (sf_count_t i = 0; i < frames_read * sf_info.channels; i++) {
|
||||
buffer[i] = (short)(buffer[i] * volume);
|
||||
}
|
||||
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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
pthread_mutex_unlock(&thread_mutex);
|
||||
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) {
|
||||
if (g_verbose) {
|
||||
printf("Sound muted - ignoring key %d (%s)\n", key_code,
|
||||
is_pressed ? "press" : "release");
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (g_mute) 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;
|
||||
|
||||
// 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) {
|
||||
if (g_verbose) {
|
||||
printf("Mouse sounds disabled - ignoring mouse event %d\n", key_code);
|
||||
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 {
|
||||
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;
|
||||
}
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
// For keyboard events, check if keyboard is enabled
|
||||
extern int g_keyboard_enabled;
|
||||
if (!g_keyboard_enabled) {
|
||||
if (g_verbose) {
|
||||
printf("Keyboard sounds disabled - ignoring key %d\n", key_code);
|
||||
}
|
||||
return;
|
||||
if (is_pressed && key_code < 512) {
|
||||
pcm_data = pack->key_mappings[key_code].pcm_data;
|
||||
num_samples = pack->key_mappings[key_code].num_samples;
|
||||
}
|
||||
}
|
||||
if (!g_sound_pack.is_multi && !is_pressed) {
|
||||
if (g_verbose) {
|
||||
printf("Single mode: Ignoring key release for key %d\n", key_code);
|
||||
|
||||
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;
|
||||
}
|
||||
return;
|
||||
}
|
||||
int slot = find_available_thread_slot();
|
||||
if (slot == -1) {
|
||||
if (g_verbose) {
|
||||
printf("Warning: No available thread slots\n");
|
||||
}
|
||||
return;
|
||||
pthread_mutex_unlock(&g_mixer_mutex);
|
||||
|
||||
if (!found) {
|
||||
LOG_WARN("Mixer full, dropping sound");
|
||||
}
|
||||
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);
|
||||
fprintf(stderr, "Failed to create sound thread\n");
|
||||
} else {
|
||||
pthread_detach(sound_threads[slot]);
|
||||
}
|
||||
pthread_mutex_unlock(&thread_mutex);
|
||||
}
|
||||
|
||||
|
||||
int parse_keyboard_event(const char *json_line, int *key_code,
|
||||
int *is_pressed) {
|
||||
char *line_copy = xstrdup(json_line);
|
||||
if (!line_copy)
|
||||
return -1;
|
||||
char *newline = strchr(line_copy, '\n');
|
||||
if (newline)
|
||||
*newline = '\0';
|
||||
if (g_verbose) {
|
||||
printf("Parsing JSON: %s\n", line_copy);
|
||||
}
|
||||
json_object *root = json_tokener_parse(line_copy);
|
||||
free(line_copy);
|
||||
if (!root) {
|
||||
fprintf(stderr, "Failed to parse JSON: %s\n", json_line);
|
||||
return -1;
|
||||
}
|
||||
json_object *key_code_obj, *state_code_obj;
|
||||
if (json_object_object_get_ex(root, "key_code", &key_code_obj) &&
|
||||
json_object_object_get_ex(root, "state_code", &state_code_obj)) {
|
||||
*key_code = json_object_get_int(key_code_obj);
|
||||
*is_pressed = json_object_get_int(state_code_obj);
|
||||
if (g_verbose) {
|
||||
printf("Parsed key event: key_code=%d, is_pressed=%d\n", *key_code,
|
||||
*is_pressed);
|
||||
}
|
||||
json_object_put(root);
|
||||
return 0;
|
||||
}
|
||||
json_object_put(root);
|
||||
return -1;
|
||||
}
|
||||
@@ -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;
|
||||
@@ -59,7 +60,8 @@ void print_usage(const char *program_name) {
|
||||
printf(" --enable[=DEVICE] Enable device sounds "
|
||||
"(keyboard|mouse|both)\n");
|
||||
printf(" --disable[=DEVICE] Disable device sounds "
|
||||
"(keyboard|mouse|both)\n\n");
|
||||
"(keyboard|mouse|both)\n");
|
||||
printf(" --system-volume-following=0/1 Enable or disable tracking Linux system volume dynamically\n\n");
|
||||
|
||||
printf("DAEMON MODE:\n");
|
||||
printf(" -d, --daemon Run in background with auto-reload\n");
|
||||
@@ -102,6 +104,7 @@ int parse_cli(int argc, char **argv, CliOptions *out) {
|
||||
out->mouse_mute = -1;
|
||||
out->keyboard_enabled = -1;
|
||||
out->mouse_enabled = -1;
|
||||
out->system_volume_following = -1;
|
||||
static struct option long_options[] = {
|
||||
{"sound", required_argument, 0, 'S'},
|
||||
{"mouse", required_argument, 0, 'M'},
|
||||
@@ -117,6 +120,7 @@ int parse_cli(int argc, char **argv, CliOptions *out) {
|
||||
{"stop", no_argument, 0, 's'},
|
||||
{"help", no_argument, 0, 'h'},
|
||||
{"verbose", no_argument, 0, 'v'},
|
||||
{"system-volume-following", required_argument, 0, 'f'},
|
||||
{0, 0, 0, 0}};
|
||||
for (int i = 1; i < argc; i++) {
|
||||
if (argv[i][0] == '-' && argv[i][1] == '-') {
|
||||
@@ -133,18 +137,17 @@ int parse_cli(int argc, char **argv, CliOptions *out) {
|
||||
(n == 6 && strncmp(a, "unmute", 6) == 0) ||
|
||||
(n == 6 && strncmp(a, "enable", 6) == 0) ||
|
||||
(n == 7 && strncmp(a, "disable", 7) == 0) ||
|
||||
(n == 4 && strncmp(a, "help", 4) == 0) ||
|
||||
(n == 7 && strncmp(a, "verbose", 7) == 0))) {
|
||||
safe_fprintf(stderr, "Error: Unknown option '%s'\n", argv[i]);
|
||||
safe_fprintf(stderr, "Run '%s --help' to see available options.\n",
|
||||
argv[0]);
|
||||
(n == 7 && strncmp(a, "verbose", 7) == 0) ||
|
||||
(n == 23 && strncmp(a, "system-volume-following", 23) == 0))) {
|
||||
LOG_ERROR("Error: Unknown option '%s'", argv[i]);
|
||||
LOG_ERROR("Run '%s --help' to see available options.", argv[0]);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
optind = 1;
|
||||
int opt;
|
||||
while ((opt = getopt_long(argc, argv, "S:M:V:K:O:lhdsm::u::v", long_options,
|
||||
while ((opt = getopt_long(argc, argv, "S:M:V:K:O:f:lhdsm::u::v", long_options,
|
||||
NULL)) != -1) {
|
||||
switch (opt) {
|
||||
case 'd':
|
||||
@@ -180,6 +183,9 @@ int parse_cli(int argc, char **argv, CliOptions *out) {
|
||||
return 1;
|
||||
}
|
||||
break;
|
||||
case 'f':
|
||||
out->system_volume_following = atoi(optarg);
|
||||
break;
|
||||
case 1000: // --enable
|
||||
case 1001: // --disable
|
||||
{
|
||||
|
||||
+9
-9
@@ -1,5 +1,6 @@
|
||||
#define _POSIX_C_SOURCE 200809L
|
||||
#include "common/utils.h"
|
||||
#include "common/log.h"
|
||||
#include <pwd.h>
|
||||
#include <signal.h>
|
||||
#include <stdarg.h>
|
||||
@@ -8,6 +9,8 @@
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
int g_verbose = 0;
|
||||
|
||||
int read_runtime_mute_file() {
|
||||
char mute_file[1024];
|
||||
const char *rd = getenv("XDG_RUNTIME_DIR");
|
||||
@@ -66,14 +69,6 @@ int safe_snprintf(char *dst, size_t dst_sz, const char *fmt, ...) {
|
||||
return (n >= 0 && (size_t)n < dst_sz);
|
||||
}
|
||||
|
||||
int errorf(const char *fmt, ...) {
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
vfprintf(stderr, fmt, ap);
|
||||
va_end(ap);
|
||||
return 1;
|
||||
}
|
||||
|
||||
const char *get_runtime_dir(void) {
|
||||
const char *rd = getenv("XDG_RUNTIME_DIR");
|
||||
if (rd && strlen(rd) > 0)
|
||||
@@ -125,6 +120,11 @@ int write_runtime_mouse_enabled_file(int enabled) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
int write_runtime_system_volume_following_file(int enabled) {
|
||||
write_runtime_state_file("sysvol-following", enabled);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
void int_to_str(char *buffer, size_t size, int value) {
|
||||
safe_snprintf_wrapper(buffer, size, "%d", value);
|
||||
@@ -171,7 +171,7 @@ void safe_strncpy(char *dest, const char *src, size_t dest_size) {
|
||||
// Helper to print error and close file
|
||||
void print_error_and_close_file(FILE *file, const char *error_msg, const char *filename) {
|
||||
if (file) fclose(file);
|
||||
safe_fprintf(stderr, "Error: %s %s\n", error_msg, filename ? filename : "");
|
||||
LOG_ERROR("Error: %s %s", error_msg, filename ? filename : "");
|
||||
}
|
||||
|
||||
// Safe wrapper for fscanf with bounds checking
|
||||
|
||||
+30
-3
@@ -21,8 +21,31 @@ char *get_user_config_path(char *buffer, size_t buflen) {
|
||||
int write_user_config(const char *path, const char *keyboard_sound,
|
||||
const char *mouse_sound, int keyboard_volume,
|
||||
int mouse_volume, int keyboard_enabled,
|
||||
int mouse_enabled) {
|
||||
json_object *root = json_object_new_object();
|
||||
int mouse_enabled, int system_volume_following) {
|
||||
json_object *root = NULL;
|
||||
|
||||
FILE *in_f = fopen(path, "r");
|
||||
if (in_f) {
|
||||
fseek(in_f, 0, SEEK_END);
|
||||
long sz = ftell(in_f);
|
||||
if (sz > 0) {
|
||||
rewind(in_f);
|
||||
char *buf = malloc((size_t)sz + 1);
|
||||
if (buf) {
|
||||
if (fread(buf, 1, sz, in_f) == (size_t)sz) {
|
||||
buf[sz] = '\0';
|
||||
root = json_tokener_parse(buf);
|
||||
}
|
||||
free(buf);
|
||||
}
|
||||
}
|
||||
fclose(in_f);
|
||||
}
|
||||
|
||||
if (!root || !json_object_is_type(root, json_type_object)) {
|
||||
if (root) json_object_put(root);
|
||||
root = json_object_new_object();
|
||||
}
|
||||
if (!root)
|
||||
return 0;
|
||||
|
||||
@@ -43,6 +66,9 @@ int write_user_config(const char *path, const char *keyboard_sound,
|
||||
json_object_object_add(mouse, "volume", json_object_new_int(mouse_volume));
|
||||
json_object_object_add(root, "mouse", mouse);
|
||||
|
||||
json_object_object_add(root, "system_volume_following",
|
||||
json_object_new_boolean(system_volume_following));
|
||||
|
||||
const char *json_str =
|
||||
json_object_to_json_string_ext(root, JSON_C_TO_STRING_PRETTY);
|
||||
FILE *f = fopen(path, "w");
|
||||
@@ -59,7 +85,7 @@ int write_user_config(const char *path, const char *keyboard_sound,
|
||||
int read_user_config(const char *path, char **out_keyboard_sound,
|
||||
char **out_mouse_sound, int *out_keyboard_volume,
|
||||
int *out_mouse_volume, int *out_keyboard_enabled,
|
||||
int *out_mouse_enabled) {
|
||||
int *out_mouse_enabled, int *out_system_volume_following) {
|
||||
FILE *f = fopen(path, "r");
|
||||
if (!f)
|
||||
return 0;
|
||||
@@ -135,6 +161,7 @@ int read_user_config(const char *path, char **out_keyboard_sound,
|
||||
*out_keyboard_volume = legacy_volume;
|
||||
*out_mouse_volume = legacy_volume;
|
||||
}
|
||||
*out_system_volume_following = 0;
|
||||
}
|
||||
json_object_put(root);
|
||||
return 1;
|
||||
|
||||
+49
-44
@@ -1,4 +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>
|
||||
@@ -49,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;
|
||||
}
|
||||
|
||||
@@ -61,41 +65,27 @@ static void close_restricted(int fd, void *user_data) {
|
||||
static const struct libinput_interface interface = {
|
||||
.open_restricted = open_restricted, .close_restricted = close_restricted};
|
||||
|
||||
static int print_key_event(struct libinput_event *event) {
|
||||
struct libinput_event_keyboard *keyboard =
|
||||
libinput_event_get_keyboard_event(event);
|
||||
enum libinput_event_type event_type = libinput_event_get_type(event);
|
||||
uint32_t time_stamp = libinput_event_keyboard_get_time(keyboard);
|
||||
uint32_t key_code = libinput_event_keyboard_get_key(keyboard);
|
||||
const char *key_name = libevdev_event_code_get_name(EV_KEY, key_code);
|
||||
key_name = key_name ? key_name : "null";
|
||||
enum libinput_key_state state_code =
|
||||
libinput_event_keyboard_get_key_state(keyboard);
|
||||
const char *state_name =
|
||||
state_code == LIBINPUT_KEY_STATE_PRESSED ? "PRESSED" : "RELEASED";
|
||||
return printf("{\"event_name\": \"KEYBOARD_KEY\", \"event_type\": %d, "
|
||||
"\"time_stamp\": %d, \"key_name\": \"%s\", \"key_code\": %d, "
|
||||
"\"state_name\": \"%s\", \"state_code\": %d}\n",
|
||||
event_type, time_stamp, key_name, key_code, state_name,
|
||||
state_code);
|
||||
static void send_event(uint32_t key_code, uint8_t is_pressed) {
|
||||
VbxEvent ev = { .key_code = key_code, .is_pressed = is_pressed };
|
||||
fwrite(&ev, sizeof(VbxEvent), 1, stdout);
|
||||
}
|
||||
|
||||
static int print_button_event(struct libinput_event *event) {
|
||||
static void send_key_event(struct libinput_event *event) {
|
||||
struct libinput_event_keyboard *keyboard =
|
||||
libinput_event_get_keyboard_event(event);
|
||||
uint32_t key_code = libinput_event_keyboard_get_key(keyboard);
|
||||
enum libinput_key_state state_code =
|
||||
libinput_event_keyboard_get_key_state(keyboard);
|
||||
send_event(key_code, state_code == LIBINPUT_KEY_STATE_PRESSED ? 1 : 0);
|
||||
}
|
||||
|
||||
static void send_button_event(struct libinput_event *event) {
|
||||
struct libinput_event_pointer *pointer =
|
||||
libinput_event_get_pointer_event(event);
|
||||
enum libinput_event_type event_type = libinput_event_get_type(event);
|
||||
uint32_t time_stamp = libinput_event_pointer_get_time(pointer);
|
||||
uint32_t button_code = libinput_event_pointer_get_button(pointer);
|
||||
const char *button_name = libevdev_event_code_get_name(EV_KEY, button_code);
|
||||
enum libinput_button_state state_code =
|
||||
libinput_event_pointer_get_button_state(pointer);
|
||||
const char *state_name =
|
||||
state_code == LIBINPUT_BUTTON_STATE_PRESSED ? "PRESSED" : "RELEASED";
|
||||
return printf("{\"event_name\": \"POINTER_BUTTON\", \"event_type\": %d, "
|
||||
"\"time_stamp\": %d, \"key_name\": \"%s\", \"key_code\": %d, "
|
||||
"\"state_name\": \"%s\", \"state_code\": %d}\n",
|
||||
event_type, time_stamp, button_name, button_code, state_name,
|
||||
state_code);
|
||||
send_event(button_code, state_code == LIBINPUT_BUTTON_STATE_PRESSED ? 1 : 0);
|
||||
}
|
||||
|
||||
static int handle_events(struct libinput *libinput) {
|
||||
@@ -103,21 +93,28 @@ static int handle_events(struct libinput *libinput) {
|
||||
struct libinput_event *event;
|
||||
if (libinput_dispatch(libinput) < 0)
|
||||
return result;
|
||||
|
||||
bool flushed = false;
|
||||
while ((event = libinput_get_event(libinput)) != NULL) {
|
||||
switch (libinput_event_get_type(event)) {
|
||||
case LIBINPUT_EVENT_KEYBOARD_KEY:
|
||||
print_key_event(event);
|
||||
send_key_event(event);
|
||||
flushed = true;
|
||||
break;
|
||||
case LIBINPUT_EVENT_POINTER_BUTTON:
|
||||
print_button_event(event);
|
||||
send_button_event(event);
|
||||
flushed = true;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
fflush(stdout);
|
||||
libinput_event_destroy(event);
|
||||
result = 0;
|
||||
}
|
||||
|
||||
if (flushed) {
|
||||
fflush(stdout);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -126,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);
|
||||
}
|
||||
@@ -153,6 +152,8 @@ void print_help(char *program_name) {
|
||||
"will run this.\n");
|
||||
}
|
||||
|
||||
int is_daemon = 0;
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
setvbuf(stdout, NULL, _IOLBF, 0);
|
||||
const struct option long_options[] = {{"version", no_argument, 0, 'v'},
|
||||
@@ -174,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;
|
||||
@@ -195,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);
|
||||
}
|
||||
|
||||
+70
-70
@@ -4,6 +4,7 @@
|
||||
#include "app/process.h"
|
||||
#include "app/reload.h"
|
||||
#include "app/watch.h"
|
||||
#include "common/log.h"
|
||||
#include "common/mute.h"
|
||||
#include "common/utils.h"
|
||||
#include "config.h"
|
||||
@@ -50,6 +51,7 @@ static int current_keyboard_mute = 0;
|
||||
static int current_mouse_mute = 0;
|
||||
static int current_keyboard_enabled = 1;
|
||||
static int current_mouse_enabled = 1;
|
||||
static int current_system_volume_following = 0;
|
||||
static char current_config_path[MAX_PATH_LENGTH] = {0};
|
||||
static char current_sound_dir[MAX_PATH_LENGTH] = {0};
|
||||
static char current_mouse_config_path[MAX_PATH_LENGTH] = {0};
|
||||
@@ -65,23 +67,22 @@ int main(int argc, char *argv[]) {
|
||||
int list_sounds = 0;
|
||||
int flag_daemon = 0;
|
||||
int flag_stop = 0;
|
||||
int volume = 50;
|
||||
CliOptions cli_opts;
|
||||
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) {
|
||||
printf("Loading configuration from: %s\n", 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 = volume;
|
||||
int cfg_mouse_volume = volume;
|
||||
int cfg_keyboard_volume = current_keyboard_volume;
|
||||
int cfg_mouse_volume = current_mouse_volume;
|
||||
int cfg_keyboard_enabled = 1;
|
||||
int cfg_mouse_enabled = 1;
|
||||
int cfg_system_volume_following = 0;
|
||||
if (read_user_config(user_cfg_path, &cfg_keyboard_sound, &cfg_mouse_sound,
|
||||
&cfg_keyboard_volume, &cfg_mouse_volume,
|
||||
&cfg_keyboard_enabled, &cfg_mouse_enabled)) {
|
||||
&cfg_keyboard_enabled, &cfg_mouse_enabled,
|
||||
&cfg_system_volume_following)) {
|
||||
if (cfg_keyboard_sound) {
|
||||
if (sound_name_owned) {
|
||||
free(sound_name);
|
||||
@@ -96,9 +97,11 @@ int main(int argc, char *argv[]) {
|
||||
mouse_sound_name = cfg_mouse_sound;
|
||||
mouse_sound_name_owned = 1;
|
||||
}
|
||||
volume = cfg_keyboard_volume;
|
||||
current_keyboard_volume = cfg_keyboard_volume;
|
||||
current_mouse_volume = cfg_mouse_volume;
|
||||
current_keyboard_enabled = cfg_keyboard_enabled;
|
||||
current_mouse_enabled = cfg_mouse_enabled;
|
||||
current_system_volume_following = cfg_system_volume_following;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -139,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) {
|
||||
@@ -163,12 +165,13 @@ 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)) {
|
||||
unlink(pidfile_path);
|
||||
printf("VBX daemon stopped successfully.\n");
|
||||
LOG_INFO("VBX daemon stopped successfully.");
|
||||
if (sound_name_owned) {
|
||||
free(sound_name);
|
||||
}
|
||||
@@ -188,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();
|
||||
|
||||
@@ -211,7 +214,8 @@ int main(int argc, char *argv[]) {
|
||||
config_updated = 1;
|
||||
}
|
||||
if (cli_opts.volume >= 0) {
|
||||
volume = cli_opts.volume;
|
||||
current_keyboard_volume = cli_opts.volume;
|
||||
current_mouse_volume = cli_opts.volume;
|
||||
config_updated = 1;
|
||||
}
|
||||
if (cli_opts.keyboard_volume >= 0) {
|
||||
@@ -226,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.");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -264,17 +268,18 @@ int main(int argc, char *argv[]) {
|
||||
write_runtime_mouse_mute_file(current_mouse_mute);
|
||||
write_runtime_keyboard_enabled_file(current_keyboard_enabled);
|
||||
write_runtime_mouse_enabled_file(current_mouse_enabled);
|
||||
write_runtime_system_volume_following_file(current_system_volume_following);
|
||||
|
||||
if (config_updated &&
|
||||
get_user_config_path(user_cfg_path, sizeof(user_cfg_path))) {
|
||||
if (!write_user_config(user_cfg_path, sound_name, mouse_sound_name,
|
||||
current_keyboard_volume, current_mouse_volume,
|
||||
current_keyboard_enabled, current_mouse_enabled)) {
|
||||
safe_fprintf(stderr, "Warning: Failed to update config file %s\n",
|
||||
user_cfg_path);
|
||||
safe_fprintf(stderr, "Your settings will not be saved for next time.\n");
|
||||
} else if (verbose) {
|
||||
safe_fprintf(stderr, "Updated config file %s\n", user_cfg_path);
|
||||
current_keyboard_enabled, current_mouse_enabled,
|
||||
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 {
|
||||
LOG_DEBUG("Updated config file %s", user_cfg_path);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -293,10 +298,11 @@ int main(int argc, char *argv[]) {
|
||||
if (access(user_cfg_path, F_OK) != 0) {
|
||||
if (!write_user_config(user_cfg_path, sound_name, mouse_sound_name,
|
||||
current_keyboard_volume, current_mouse_volume,
|
||||
current_keyboard_enabled, current_mouse_enabled)) {
|
||||
safe_fprintf(stderr, "Warning: Failed to write %s\n", user_cfg_path);
|
||||
current_keyboard_enabled, current_mouse_enabled,
|
||||
current_system_volume_following)) {
|
||||
LOG_WARN("Failed to write %s", user_cfg_path);
|
||||
} else if (verbose) {
|
||||
printf("Created default configuration: %s\n", user_cfg_path);
|
||||
LOG_INFO("Created default configuration: %s", user_cfg_path);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -332,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);
|
||||
@@ -346,10 +352,7 @@ int main(int argc, char *argv[]) {
|
||||
if (mouse_sound_name_owned) {
|
||||
free(mouse_sound_name);
|
||||
}
|
||||
safe_fprintf(stderr,
|
||||
"VBX daemon is already running (PID: %ld). Use 'vbx --stop' "
|
||||
"to stop it.\n",
|
||||
(long)existing);
|
||||
LOG_WARN("VBX daemon is already running (PID: %ld). Use 'vbx --stop' to stop it.", (long)existing);
|
||||
return 1;
|
||||
}
|
||||
daemonize_self();
|
||||
@@ -382,8 +385,6 @@ int main(int argc, char *argv[]) {
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
current_keyboard_volume = volume;
|
||||
current_mouse_volume = volume;
|
||||
current_verbose = verbose;
|
||||
safe_strncpy(current_sound_name, sound_name, sizeof(current_sound_name));
|
||||
safe_strncpy(current_mouse_sound_name, mouse_sound_name,
|
||||
@@ -395,30 +396,29 @@ int main(int argc, char *argv[]) {
|
||||
safe_strncpy(current_mouse_sound_dir, mouse_sound_dir,
|
||||
sizeof(current_mouse_sound_dir));
|
||||
if (verbose && !is_daemon) {
|
||||
printf("Starting VBX daemon...\n");
|
||||
printf("Keyboard sound pack: %s\n", sound_name);
|
||||
printf("Mouse sound pack: %s\n", mouse_sound_name);
|
||||
printf("Keyboard config file: %s\n", config_path);
|
||||
printf("Mouse config file: %s\n", mouse_config_path);
|
||||
printf("Keyboard working directory: %s\n", sound_dir);
|
||||
printf("Mouse working directory: %s\n", mouse_sound_dir);
|
||||
printf("Press Ctrl+C to exit.\n\n");
|
||||
LOG_INFO("Starting VBX daemon...");
|
||||
LOG_INFO("Keyboard sound pack: %s", sound_name);
|
||||
LOG_INFO("Mouse sound pack: %s", mouse_sound_name);
|
||||
LOG_INFO("Keyboard config file: %s", config_path);
|
||||
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_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, volume, verbose, current_mute,
|
||||
if (!start_children(sound_dir, config_path, current_keyboard_volume, verbose, current_mute,
|
||||
mouse_sound_dir, mouse_config_path, current_mouse_volume,
|
||||
current_keyboard_mute, current_mouse_mute,
|
||||
current_keyboard_enabled, current_mouse_enabled)) {
|
||||
current_keyboard_enabled, current_mouse_enabled,
|
||||
current_system_volume_following)) {
|
||||
if (sound_name_owned) {
|
||||
free(sound_name);
|
||||
}
|
||||
@@ -433,14 +433,14 @@ int main(int argc, char *argv[]) {
|
||||
|
||||
char user_cfg_path2[MAX_PATH_LENGTH];
|
||||
if (get_user_config_path(user_cfg_path2, sizeof(user_cfg_path2))) {
|
||||
printf("Setting up file watcher for: %s\n", user_cfg_path2);
|
||||
LOG_INFO("Setting up file watcher for: %s", user_cfg_path2);
|
||||
start_config_watcher(user_cfg_path2);
|
||||
} else {
|
||||
printf("Warning: Could not get user config path for file watching\n");
|
||||
LOG_WARN("Could not get user config path for file watching");
|
||||
}
|
||||
while (1) {
|
||||
if (reload_requested) {
|
||||
printf("Reload requested, processing config changes...\n");
|
||||
LOG_INFO("Reload requested, processing config changes...");
|
||||
reload_requested = 0;
|
||||
char user_cfg_path3[MAX_PATH_LENGTH];
|
||||
if (get_user_config_path(user_cfg_path3, sizeof(user_cfg_path3))) {
|
||||
@@ -449,10 +449,11 @@ int main(int argc, char *argv[]) {
|
||||
¤t_keyboard_volume, ¤t_mouse_volume,
|
||||
current_config_path, current_sound_dir, current_mouse_config_path,
|
||||
current_mouse_sound_dir, current_mute, current_verbose,
|
||||
current_mouse_volume, current_keyboard_mute, current_mouse_mute,
|
||||
¤t_keyboard_enabled, ¤t_mouse_enabled);
|
||||
current_keyboard_mute, current_mouse_mute,
|
||||
¤t_keyboard_enabled, ¤t_mouse_enabled,
|
||||
¤t_system_volume_following);
|
||||
} else {
|
||||
printf("Failed to get user config path\n");
|
||||
LOG_ERROR("Failed to get user config path");
|
||||
}
|
||||
}
|
||||
finished_pid = waitpid(-1, &status, WNOHANG);
|
||||
@@ -465,14 +466,13 @@ int main(int argc, char *argv[]) {
|
||||
} else if (finished_pid > 0) {
|
||||
if (finished_pid == keyboard_pid) {
|
||||
if (!is_daemon)
|
||||
printf("Keyboard listener exited with status %d\n",
|
||||
WEXITSTATUS(status));
|
||||
LOG_WARN("Keyboard listener exited with status %d", WEXITSTATUS(status));
|
||||
keyboard_pid = 0;
|
||||
if (sound_pid > 0)
|
||||
kill(sound_pid, SIGTERM);
|
||||
} else if (finished_pid == sound_pid) {
|
||||
if (!is_daemon)
|
||||
printf("Sound player exited with status %d\n", WEXITSTATUS(status));
|
||||
LOG_WARN("Sound player exited with status %d", WEXITSTATUS(status));
|
||||
sound_pid = 0;
|
||||
if (keyboard_pid > 0)
|
||||
kill(keyboard_pid, SIGTERM);
|
||||
@@ -484,7 +484,7 @@ int main(int argc, char *argv[]) {
|
||||
}
|
||||
}
|
||||
if (!is_daemon) {
|
||||
printf("VBX daemon exited.\n");
|
||||
LOG_INFO("VBX daemon exited.");
|
||||
}
|
||||
if (sound_name_owned) {
|
||||
free(sound_name);
|
||||
|
||||
+37
-38
@@ -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",
|
||||
sound_name);
|
||||
fprintf(stderr, "Use --list to see available sound packs.\n");
|
||||
LOG_ERROR("Error: Keyboard sound pack '%s' not found in user or system dirs.",
|
||||
sound_name);
|
||||
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",
|
||||
sound_name);
|
||||
fprintf(stderr, "Use --list to see available sound packs.\n");
|
||||
LOG_ERROR("Error: Mouse sound pack '%s' not found in user or system dirs.",
|
||||
sound_name);
|
||||
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;
|
||||
}
|
||||
Reference in new issue
Block a user