diff --git a/Makefile b/Makefile index ba124e8..391f659 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,3 @@ - CC = gcc CFLAGS = -Wall -Wextra -std=c99 -Iinclude PREFIX ?= /usr @@ -55,7 +54,7 @@ install: install -Dm755 $(SOUND_TARGET) $(DESTDIR)$(BINDIR)/$(SOUND_TARGET) install -Dm755 $(KEYBOARD_TARGET) $(DESTDIR)$(BINDIR)/$(KEYBOARD_TARGET) install -d $(DESTDIR)$(SHAREDIR) - cp -r audio $(DESTDIR)$(SHAREDIR)/ + cp -r soundpacks $(DESTDIR)$(SHAREDIR)/ @echo "Installing udev rule for non-root keyboard access..." @echo '# Allow non-root access to input event devices for active seat users and input group' | sudo tee $(UDEV_RULE) >/dev/null @echo 'SUBSYSTEM=="input", KERNEL=="event*", TAG+="uaccess", GROUP="input", MODE="0660"' | sudo tee -a $(UDEV_RULE) >/dev/null diff --git a/include/config.h b/include/config.h index 8b2bbe4..9c2c9ff 100644 --- a/include/config.h +++ b/include/config.h @@ -1,7 +1,7 @@ #ifndef __CONFIG_H__ #define __CONFIG_H__ -#define PROJECT_VERSION "0.1.0" +#define PROJECT_VERSION "1.1.0" #define GETTEXT_PACKAGE "keyvibe" #define PKEXEC_PATH "/usr/bin/pkexec" @@ -18,3 +18,5 @@ #define KeyVibe_BIN_DIR PACKAGE_PREFIX "/bin" #endif + + diff --git a/include/utils.h b/include/utils.h index 03cd5bf..38a3b07 100644 --- a/include/utils.h +++ b/include/utils.h @@ -1,4 +1,3 @@ - #ifndef KEYVIBE_UTILS_H #define KEYVIBE_UTILS_H @@ -8,8 +7,7 @@ // 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. +// fprintf to stderr with printf-style formatting. Returns 1 for convenient `return errorf(...)` usage. int errorf(const char *fmt, ...); // Common runtime helpers @@ -20,3 +18,4 @@ int write_pidfile(const char *path, pid_t pid); int process_is_running(pid_t pid); #endif // KEYVIBE_UTILS_H + diff --git a/audio/banana-split-lubed/LICENSE.txt b/soundpacks/keyboard/banana-split-lubed/LICENSE.txt similarity index 100% rename from audio/banana-split-lubed/LICENSE.txt rename to soundpacks/keyboard/banana-split-lubed/LICENSE.txt diff --git a/audio/banana-split-lubed/banana-l-1.wav b/soundpacks/keyboard/banana-split-lubed/banana-l-1.wav similarity index 100% rename from audio/banana-split-lubed/banana-l-1.wav rename to soundpacks/keyboard/banana-split-lubed/banana-l-1.wav diff --git a/audio/banana-split-lubed/banana-l-2.wav b/soundpacks/keyboard/banana-split-lubed/banana-l-2.wav similarity index 100% rename from audio/banana-split-lubed/banana-l-2.wav rename to soundpacks/keyboard/banana-split-lubed/banana-l-2.wav diff --git a/audio/banana-split-lubed/banana-l-3.wav b/soundpacks/keyboard/banana-split-lubed/banana-l-3.wav similarity index 100% rename from audio/banana-split-lubed/banana-l-3.wav rename to soundpacks/keyboard/banana-split-lubed/banana-l-3.wav diff --git a/audio/banana-split-lubed/banana-l-4.wav b/soundpacks/keyboard/banana-split-lubed/banana-l-4.wav similarity index 100% rename from audio/banana-split-lubed/banana-l-4.wav rename to soundpacks/keyboard/banana-split-lubed/banana-l-4.wav diff --git a/audio/banana-split-lubed/banana-l-5.wav b/soundpacks/keyboard/banana-split-lubed/banana-l-5.wav similarity index 100% rename from audio/banana-split-lubed/banana-l-5.wav rename to soundpacks/keyboard/banana-split-lubed/banana-l-5.wav diff --git a/audio/banana-split-lubed/banana-l-6.wav b/soundpacks/keyboard/banana-split-lubed/banana-l-6.wav similarity index 100% rename from audio/banana-split-lubed/banana-l-6.wav rename to soundpacks/keyboard/banana-split-lubed/banana-l-6.wav diff --git a/audio/banana-split-lubed/banana-l-7.wav b/soundpacks/keyboard/banana-split-lubed/banana-l-7.wav similarity index 100% rename from audio/banana-split-lubed/banana-l-7.wav rename to soundpacks/keyboard/banana-split-lubed/banana-l-7.wav diff --git a/audio/banana-split-lubed/config.json b/soundpacks/keyboard/banana-split-lubed/config.json similarity index 100% rename from audio/banana-split-lubed/config.json rename to soundpacks/keyboard/banana-split-lubed/config.json diff --git a/audio/cherrymx-black-abs/config.json b/soundpacks/keyboard/cherrymx-black-abs/config.json similarity index 100% rename from audio/cherrymx-black-abs/config.json rename to soundpacks/keyboard/cherrymx-black-abs/config.json diff --git a/audio/cherrymx-black-abs/sound.ogg b/soundpacks/keyboard/cherrymx-black-abs/sound.ogg similarity index 100% rename from audio/cherrymx-black-abs/sound.ogg rename to soundpacks/keyboard/cherrymx-black-abs/sound.ogg diff --git a/audio/cherrymx-black-pbt/config.json b/soundpacks/keyboard/cherrymx-black-pbt/config.json similarity index 100% rename from audio/cherrymx-black-pbt/config.json rename to soundpacks/keyboard/cherrymx-black-pbt/config.json diff --git a/audio/cherrymx-black-pbt/sound.ogg b/soundpacks/keyboard/cherrymx-black-pbt/sound.ogg similarity index 100% rename from audio/cherrymx-black-pbt/sound.ogg rename to soundpacks/keyboard/cherrymx-black-pbt/sound.ogg diff --git a/audio/cherrymx-blue-abs/config.json b/soundpacks/keyboard/cherrymx-blue-abs/config.json similarity index 100% rename from audio/cherrymx-blue-abs/config.json rename to soundpacks/keyboard/cherrymx-blue-abs/config.json diff --git a/audio/cherrymx-blue-abs/sound.ogg b/soundpacks/keyboard/cherrymx-blue-abs/sound.ogg similarity index 100% rename from audio/cherrymx-blue-abs/sound.ogg rename to soundpacks/keyboard/cherrymx-blue-abs/sound.ogg diff --git a/audio/cherrymx-blue-pbt/config.json b/soundpacks/keyboard/cherrymx-blue-pbt/config.json similarity index 100% rename from audio/cherrymx-blue-pbt/config.json rename to soundpacks/keyboard/cherrymx-blue-pbt/config.json diff --git a/audio/cherrymx-blue-pbt/sound.ogg b/soundpacks/keyboard/cherrymx-blue-pbt/sound.ogg similarity index 100% rename from audio/cherrymx-blue-pbt/sound.ogg rename to soundpacks/keyboard/cherrymx-blue-pbt/sound.ogg diff --git a/audio/cherrymx-brown-abs/config.json b/soundpacks/keyboard/cherrymx-brown-abs/config.json similarity index 100% rename from audio/cherrymx-brown-abs/config.json rename to soundpacks/keyboard/cherrymx-brown-abs/config.json diff --git a/audio/cherrymx-brown-abs/sound.ogg b/soundpacks/keyboard/cherrymx-brown-abs/sound.ogg similarity index 100% rename from audio/cherrymx-brown-abs/sound.ogg rename to soundpacks/keyboard/cherrymx-brown-abs/sound.ogg diff --git a/audio/cherrymx-brown-pbt/config.json b/soundpacks/keyboard/cherrymx-brown-pbt/config.json similarity index 100% rename from audio/cherrymx-brown-pbt/config.json rename to soundpacks/keyboard/cherrymx-brown-pbt/config.json diff --git a/audio/cherrymx-brown-pbt/sound.ogg b/soundpacks/keyboard/cherrymx-brown-pbt/sound.ogg similarity index 100% rename from audio/cherrymx-brown-pbt/sound.ogg rename to soundpacks/keyboard/cherrymx-brown-pbt/sound.ogg diff --git a/audio/cherrymx-red-abs/config.json b/soundpacks/keyboard/cherrymx-red-abs/config.json similarity index 100% rename from audio/cherrymx-red-abs/config.json rename to soundpacks/keyboard/cherrymx-red-abs/config.json diff --git a/audio/cherrymx-red-abs/sound.ogg b/soundpacks/keyboard/cherrymx-red-abs/sound.ogg similarity index 100% rename from audio/cherrymx-red-abs/sound.ogg rename to soundpacks/keyboard/cherrymx-red-abs/sound.ogg diff --git a/audio/cherrymx-red-pbt/config.json b/soundpacks/keyboard/cherrymx-red-pbt/config.json similarity index 100% rename from audio/cherrymx-red-pbt/config.json rename to soundpacks/keyboard/cherrymx-red-pbt/config.json diff --git a/audio/cherrymx-red-pbt/sound.ogg b/soundpacks/keyboard/cherrymx-red-pbt/sound.ogg similarity index 100% rename from audio/cherrymx-red-pbt/sound.ogg rename to soundpacks/keyboard/cherrymx-red-pbt/sound.ogg diff --git a/audio/cream-travel/config.json b/soundpacks/keyboard/cream-travel/config.json similarity index 100% rename from audio/cream-travel/config.json rename to soundpacks/keyboard/cream-travel/config.json diff --git a/audio/cream-travel/press/BACKSPACE.mp3 b/soundpacks/keyboard/cream-travel/press/BACKSPACE.mp3 similarity index 100% rename from audio/cream-travel/press/BACKSPACE.mp3 rename to soundpacks/keyboard/cream-travel/press/BACKSPACE.mp3 diff --git a/audio/cream-travel/press/ENTER.mp3 b/soundpacks/keyboard/cream-travel/press/ENTER.mp3 similarity index 100% rename from audio/cream-travel/press/ENTER.mp3 rename to soundpacks/keyboard/cream-travel/press/ENTER.mp3 diff --git a/audio/cream-travel/press/GENERIC_R0.mp3 b/soundpacks/keyboard/cream-travel/press/GENERIC_R0.mp3 similarity index 100% rename from audio/cream-travel/press/GENERIC_R0.mp3 rename to soundpacks/keyboard/cream-travel/press/GENERIC_R0.mp3 diff --git a/audio/cream-travel/press/GENERIC_R1.mp3 b/soundpacks/keyboard/cream-travel/press/GENERIC_R1.mp3 similarity index 100% rename from audio/cream-travel/press/GENERIC_R1.mp3 rename to soundpacks/keyboard/cream-travel/press/GENERIC_R1.mp3 diff --git a/audio/cream-travel/press/GENERIC_R2.mp3 b/soundpacks/keyboard/cream-travel/press/GENERIC_R2.mp3 similarity index 100% rename from audio/cream-travel/press/GENERIC_R2.mp3 rename to soundpacks/keyboard/cream-travel/press/GENERIC_R2.mp3 diff --git a/audio/cream-travel/press/GENERIC_R3.mp3 b/soundpacks/keyboard/cream-travel/press/GENERIC_R3.mp3 similarity index 100% rename from audio/cream-travel/press/GENERIC_R3.mp3 rename to soundpacks/keyboard/cream-travel/press/GENERIC_R3.mp3 diff --git a/audio/cream-travel/press/GENERIC_R4.mp3 b/soundpacks/keyboard/cream-travel/press/GENERIC_R4.mp3 similarity index 100% rename from audio/cream-travel/press/GENERIC_R4.mp3 rename to soundpacks/keyboard/cream-travel/press/GENERIC_R4.mp3 diff --git a/audio/cream-travel/press/SPACE.mp3 b/soundpacks/keyboard/cream-travel/press/SPACE.mp3 similarity index 100% rename from audio/cream-travel/press/SPACE.mp3 rename to soundpacks/keyboard/cream-travel/press/SPACE.mp3 diff --git a/audio/cream-travel/release/BACKSPACE.mp3 b/soundpacks/keyboard/cream-travel/release/BACKSPACE.mp3 similarity index 100% rename from audio/cream-travel/release/BACKSPACE.mp3 rename to soundpacks/keyboard/cream-travel/release/BACKSPACE.mp3 diff --git a/audio/cream-travel/release/ENTER.mp3 b/soundpacks/keyboard/cream-travel/release/ENTER.mp3 similarity index 100% rename from audio/cream-travel/release/ENTER.mp3 rename to soundpacks/keyboard/cream-travel/release/ENTER.mp3 diff --git a/audio/cream-travel/release/GENERIC.mp3 b/soundpacks/keyboard/cream-travel/release/GENERIC.mp3 similarity index 100% rename from audio/cream-travel/release/GENERIC.mp3 rename to soundpacks/keyboard/cream-travel/release/GENERIC.mp3 diff --git a/audio/cream-travel/release/SPACE.mp3 b/soundpacks/keyboard/cream-travel/release/SPACE.mp3 similarity index 100% rename from audio/cream-travel/release/SPACE.mp3 rename to soundpacks/keyboard/cream-travel/release/SPACE.mp3 diff --git a/audio/eg-crystal-purple/config.json b/soundpacks/keyboard/eg-crystal-purple/config.json similarity index 100% rename from audio/eg-crystal-purple/config.json rename to soundpacks/keyboard/eg-crystal-purple/config.json diff --git a/audio/eg-crystal-purple/purple.ogg b/soundpacks/keyboard/eg-crystal-purple/purple.ogg similarity index 100% rename from audio/eg-crystal-purple/purple.ogg rename to soundpacks/keyboard/eg-crystal-purple/purple.ogg diff --git a/audio/eg-oreo/config.json b/soundpacks/keyboard/eg-oreo/config.json similarity index 97% rename from audio/eg-oreo/config.json rename to soundpacks/keyboard/eg-oreo/config.json index ddaebb1..5aac47f 100644 --- a/audio/eg-oreo/config.json +++ b/soundpacks/keyboard/eg-oreo/config.json @@ -464,6 +464,18 @@ "61011": [ 13964, 113 + ], + "272": [ + 971, + 142 + ], + "273": [ + 1276, + 115 + ], + "274": [ + 1604, + 122 ] } } diff --git a/audio/eg-oreo/oreo.ogg b/soundpacks/keyboard/eg-oreo/oreo.ogg similarity index 100% rename from audio/eg-oreo/oreo.ogg rename to soundpacks/keyboard/eg-oreo/oreo.ogg diff --git a/audio/holy-pandas/BACKSPACE.mp3 b/soundpacks/keyboard/holy-pandas/BACKSPACE.mp3 similarity index 100% rename from audio/holy-pandas/BACKSPACE.mp3 rename to soundpacks/keyboard/holy-pandas/BACKSPACE.mp3 diff --git a/audio/holy-pandas/ENTER.mp3 b/soundpacks/keyboard/holy-pandas/ENTER.mp3 similarity index 100% rename from audio/holy-pandas/ENTER.mp3 rename to soundpacks/keyboard/holy-pandas/ENTER.mp3 diff --git a/audio/holy-pandas/GENERIC_R0.mp3 b/soundpacks/keyboard/holy-pandas/GENERIC_R0.mp3 similarity index 100% rename from audio/holy-pandas/GENERIC_R0.mp3 rename to soundpacks/keyboard/holy-pandas/GENERIC_R0.mp3 diff --git a/audio/holy-pandas/GENERIC_R1.mp3 b/soundpacks/keyboard/holy-pandas/GENERIC_R1.mp3 similarity index 100% rename from audio/holy-pandas/GENERIC_R1.mp3 rename to soundpacks/keyboard/holy-pandas/GENERIC_R1.mp3 diff --git a/audio/holy-pandas/GENERIC_R2.mp3 b/soundpacks/keyboard/holy-pandas/GENERIC_R2.mp3 similarity index 100% rename from audio/holy-pandas/GENERIC_R2.mp3 rename to soundpacks/keyboard/holy-pandas/GENERIC_R2.mp3 diff --git a/audio/holy-pandas/GENERIC_R3.mp3 b/soundpacks/keyboard/holy-pandas/GENERIC_R3.mp3 similarity index 100% rename from audio/holy-pandas/GENERIC_R3.mp3 rename to soundpacks/keyboard/holy-pandas/GENERIC_R3.mp3 diff --git a/audio/holy-pandas/GENERIC_R4.mp3 b/soundpacks/keyboard/holy-pandas/GENERIC_R4.mp3 similarity index 100% rename from audio/holy-pandas/GENERIC_R4.mp3 rename to soundpacks/keyboard/holy-pandas/GENERIC_R4.mp3 diff --git a/audio/holy-pandas/LICENSE b/soundpacks/keyboard/holy-pandas/LICENSE similarity index 100% rename from audio/holy-pandas/LICENSE rename to soundpacks/keyboard/holy-pandas/LICENSE diff --git a/audio/holy-pandas/README.md b/soundpacks/keyboard/holy-pandas/README.md similarity index 100% rename from audio/holy-pandas/README.md rename to soundpacks/keyboard/holy-pandas/README.md diff --git a/audio/holy-pandas/SPACE.mp3 b/soundpacks/keyboard/holy-pandas/SPACE.mp3 similarity index 100% rename from audio/holy-pandas/SPACE.mp3 rename to soundpacks/keyboard/holy-pandas/SPACE.mp3 diff --git a/audio/holy-pandas/config.json b/soundpacks/keyboard/holy-pandas/config.json similarity index 100% rename from audio/holy-pandas/config.json rename to soundpacks/keyboard/holy-pandas/config.json diff --git a/audio/holy-pandas/release/BACKSPACE.mp3 b/soundpacks/keyboard/holy-pandas/release/BACKSPACE.mp3 similarity index 100% rename from audio/holy-pandas/release/BACKSPACE.mp3 rename to soundpacks/keyboard/holy-pandas/release/BACKSPACE.mp3 diff --git a/audio/holy-pandas/release/ENTER.mp3 b/soundpacks/keyboard/holy-pandas/release/ENTER.mp3 similarity index 100% rename from audio/holy-pandas/release/ENTER.mp3 rename to soundpacks/keyboard/holy-pandas/release/ENTER.mp3 diff --git a/audio/holy-pandas/release/GENERIC.mp3 b/soundpacks/keyboard/holy-pandas/release/GENERIC.mp3 similarity index 100% rename from audio/holy-pandas/release/GENERIC.mp3 rename to soundpacks/keyboard/holy-pandas/release/GENERIC.mp3 diff --git a/audio/holy-pandas/release/SPACE.mp3 b/soundpacks/keyboard/holy-pandas/release/SPACE.mp3 similarity index 100% rename from audio/holy-pandas/release/SPACE.mp3 rename to soundpacks/keyboard/holy-pandas/release/SPACE.mp3 diff --git a/soundpacks/keyboard/kalih-box-white/Untitled Clip(3).mp3 b/soundpacks/keyboard/kalih-box-white/Untitled Clip(3).mp3 new file mode 100644 index 0000000..9bbdc66 Binary files /dev/null and b/soundpacks/keyboard/kalih-box-white/Untitled Clip(3).mp3 differ diff --git a/soundpacks/keyboard/kalih-box-white/Untitled Clip(4).mp3 b/soundpacks/keyboard/kalih-box-white/Untitled Clip(4).mp3 new file mode 100644 index 0000000..9e03f93 Binary files /dev/null and b/soundpacks/keyboard/kalih-box-white/Untitled Clip(4).mp3 differ diff --git a/soundpacks/keyboard/kalih-box-white/Untitled Clip(5).mp3 b/soundpacks/keyboard/kalih-box-white/Untitled Clip(5).mp3 new file mode 100644 index 0000000..2789cfe Binary files /dev/null and b/soundpacks/keyboard/kalih-box-white/Untitled Clip(5).mp3 differ diff --git a/soundpacks/keyboard/kalih-box-white/Untitled Clip(6).mp3 b/soundpacks/keyboard/kalih-box-white/Untitled Clip(6).mp3 new file mode 100644 index 0000000..f3ac560 Binary files /dev/null and b/soundpacks/keyboard/kalih-box-white/Untitled Clip(6).mp3 differ diff --git a/soundpacks/keyboard/kalih-box-white/Untitled Clip(7).mp3 b/soundpacks/keyboard/kalih-box-white/Untitled Clip(7).mp3 new file mode 100644 index 0000000..4a4b64b Binary files /dev/null and b/soundpacks/keyboard/kalih-box-white/Untitled Clip(7).mp3 differ diff --git a/soundpacks/keyboard/kalih-box-white/Untitled Clip.mp3 b/soundpacks/keyboard/kalih-box-white/Untitled Clip.mp3 new file mode 100644 index 0000000..dda4af7 Binary files /dev/null and b/soundpacks/keyboard/kalih-box-white/Untitled Clip.mp3 differ diff --git a/soundpacks/keyboard/kalih-box-white/alt.mp3 b/soundpacks/keyboard/kalih-box-white/alt.mp3 new file mode 100644 index 0000000..7ba5dc1 Binary files /dev/null and b/soundpacks/keyboard/kalih-box-white/alt.mp3 differ diff --git a/soundpacks/keyboard/kalih-box-white/config.json b/soundpacks/keyboard/kalih-box-white/config.json new file mode 100644 index 0000000..d590dd6 --- /dev/null +++ b/soundpacks/keyboard/kalih-box-white/config.json @@ -0,0 +1,135 @@ +{ + "id": "custom-sound-pack-1203000000053", + "name": "Kalih Box White", + "key_define_type": "multi", + "includes_numpad": false, + "sound": "sound.ogg", + "defines": { + "1": "kboxwh - 08.mp3", + "2": "kboxwh - 10.mp3", + "3": "kboxwh - 06.mp3", + "4": "kboxwh - 03.mp3", + "5": "kboxwh - 02.mp3", + "6": "kboxwh - 08.mp3", + "7": "kboxwh - 07.mp3", + "8": "kboxwh - 04.mp3", + "9": "kboxwh - 01.mp3", + "10": "kboxwh - 04.mp3", + "11": "kboxwh - 02.mp3", + "12": "Untitled Clip(6).mp3", + "13": "Untitled Clip(5).mp3", + "14": "Untitled Clip(3).mp3", + "15": "Untitled Clip(4).mp3", + "16": "kboxwh - 05.mp3", + "17": "kboxwh - 01.mp3", + "18": "kboxwh - 06.mp3", + "19": "kboxwh - 07.mp3", + "20": "kboxwh - 05.mp3", + "21": "kboxwh - 03.mp3", + "22": "kboxwh - 10.mp3", + "23": "kboxwh - 01.mp3", + "24": "kboxwh - 03.mp3", + "25": "kboxwh - 06.mp3", + "26": "Untitled Clip(5).mp3", + "27": "Untitled Clip(6).mp3", + "28": "enter.mp3", + "29": "Untitled Clip(3).mp3", + "30": "kboxwh - 02.mp3", + "31": "kboxwh - 03.mp3", + "32": "kboxwh - 04.mp3", + "33": "kboxwh - 08.mp3", + "34": "kboxwh - 06.mp3", + "35": "kboxwh - 07.mp3", + "36": "kboxwh - 01.mp3", + "37": "Untitled Clip(3).mp3", + "38": "kboxwh - 06.mp3", + "39": "Untitled Clip(5).mp3", + "40": "Untitled Clip(6).mp3", + "41": "kboxwh - 09.mp3", + "42": "Untitled Clip(5).mp3", + "43": "enter.mp3", + "44": "kboxwh - 06.mp3", + "45": "kboxwh - 04.mp3", + "46": "kboxwh - 09.mp3", + "47": "kboxwh - 10.mp3", + "48": "kboxwh - 02.mp3", + "49": "kboxwh - 09.mp3", + "50": "kboxwh - 08.mp3", + "51": "Untitled Clip(7).mp3", + "52": "Untitled Clip(7).mp3", + "53": "kboxwh - 04.mp3", + "54": "Untitled Clip(5).mp3", + "55": "Untitled Clip(6).mp3", + "56": "alt.mp3", + "57": "Untitled Clip.mp3", + "58": "Untitled Clip(6).mp3", + "59": "kboxwh - 10.mp3", + "60": "kboxwh - 09.mp3", + "61": "kboxwh - 08.mp3", + "62": "kboxwh - 07.mp3", + "63": "kboxwh - 06.mp3", + "64": "kboxwh - 05.mp3", + "65": "kboxwh - 04.mp3", + "66": "kboxwh - 03.mp3", + "67": "kboxwh - 01.mp3", + "68": "kboxwh - 02.mp3", + "69": "Untitled Clip(5).mp3", + "70": null, + "71": "kboxwh - 08.mp3", + "72": "kboxwh - 07.mp3", + "73": "kboxwh - 03.mp3", + "74": "Untitled Clip(6).mp3", + "75": "kboxwh - 09.mp3", + "76": "kboxwh - 01.mp3", + "77": "kboxwh - 09.mp3", + "78": "Untitled Clip(6).mp3", + "79": "kboxwh - 09.mp3", + "80": "kboxwh - 03.mp3", + "81": "kboxwh - 04.mp3", + "82": "kboxwh - 02.mp3", + "83": "Untitled Clip(7).mp3", + "87": "kboxwh - 06.mp3", + "88": "kboxwh - 04.mp3", + "91": null, + "92": null, + "93": null, + "3597": null, + "3612": "enter.mp3", + "3613": "Untitled Clip(3).mp3", + "3637": "Untitled Clip(6).mp3", + "3639": null, + "3640": "alt.mp3", + "3653": null, + "3655": null, + "3657": null, + "3663": null, + "3665": null, + "3666": null, + "3667": null, + "3675": "Untitled Clip(7).mp3", + "3676": "Untitled Clip(7).mp3", + "3677": "Untitled Clip(3).mp3", + "57416": "kboxwh - 01.mp3", + "57419": "kboxwh - 02.mp3", + "57421": "kboxwh - 04.mp3", + "57424": "kboxwh - 03.mp3", + "60999": null, + "61000": "kboxwh - 01.mp3", + "61001": null, + "61003": "kboxwh - 02.mp3", + "61005": "kboxwh - 04.mp3", + "61007": null, + "61008": "kboxwh - 03.mp3", + "61009": null, + "61010": null, + "61011": null + }, + "_zip": "kailh-box-white.zip", + "group": "1fMFAp - sOcaAT - MuxCD - GlHmN3", + "description": "Kailh Box White switches with individual sounds per keypress and no background noises.", + "m_author": "edy.", + "tags": [ + "keyboard", + "clicky" + ] +} \ No newline at end of file diff --git a/soundpacks/keyboard/kalih-box-white/enter.mp3 b/soundpacks/keyboard/kalih-box-white/enter.mp3 new file mode 100644 index 0000000..7a54bdb Binary files /dev/null and b/soundpacks/keyboard/kalih-box-white/enter.mp3 differ diff --git a/soundpacks/keyboard/kalih-box-white/kboxwh - 01.mp3 b/soundpacks/keyboard/kalih-box-white/kboxwh - 01.mp3 new file mode 100644 index 0000000..2a3efea Binary files /dev/null and b/soundpacks/keyboard/kalih-box-white/kboxwh - 01.mp3 differ diff --git a/soundpacks/keyboard/kalih-box-white/kboxwh - 02.mp3 b/soundpacks/keyboard/kalih-box-white/kboxwh - 02.mp3 new file mode 100644 index 0000000..94e7daf Binary files /dev/null and b/soundpacks/keyboard/kalih-box-white/kboxwh - 02.mp3 differ diff --git a/soundpacks/keyboard/kalih-box-white/kboxwh - 03.mp3 b/soundpacks/keyboard/kalih-box-white/kboxwh - 03.mp3 new file mode 100644 index 0000000..770b929 Binary files /dev/null and b/soundpacks/keyboard/kalih-box-white/kboxwh - 03.mp3 differ diff --git a/soundpacks/keyboard/kalih-box-white/kboxwh - 04.mp3 b/soundpacks/keyboard/kalih-box-white/kboxwh - 04.mp3 new file mode 100644 index 0000000..2490ac3 Binary files /dev/null and b/soundpacks/keyboard/kalih-box-white/kboxwh - 04.mp3 differ diff --git a/soundpacks/keyboard/kalih-box-white/kboxwh - 05.mp3 b/soundpacks/keyboard/kalih-box-white/kboxwh - 05.mp3 new file mode 100644 index 0000000..daac682 Binary files /dev/null and b/soundpacks/keyboard/kalih-box-white/kboxwh - 05.mp3 differ diff --git a/soundpacks/keyboard/kalih-box-white/kboxwh - 06.mp3 b/soundpacks/keyboard/kalih-box-white/kboxwh - 06.mp3 new file mode 100644 index 0000000..c3d3444 Binary files /dev/null and b/soundpacks/keyboard/kalih-box-white/kboxwh - 06.mp3 differ diff --git a/soundpacks/keyboard/kalih-box-white/kboxwh - 07.mp3 b/soundpacks/keyboard/kalih-box-white/kboxwh - 07.mp3 new file mode 100644 index 0000000..d05eebf Binary files /dev/null and b/soundpacks/keyboard/kalih-box-white/kboxwh - 07.mp3 differ diff --git a/soundpacks/keyboard/kalih-box-white/kboxwh - 08.mp3 b/soundpacks/keyboard/kalih-box-white/kboxwh - 08.mp3 new file mode 100644 index 0000000..16b4115 Binary files /dev/null and b/soundpacks/keyboard/kalih-box-white/kboxwh - 08.mp3 differ diff --git a/soundpacks/keyboard/kalih-box-white/kboxwh - 09.mp3 b/soundpacks/keyboard/kalih-box-white/kboxwh - 09.mp3 new file mode 100644 index 0000000..485ed83 Binary files /dev/null and b/soundpacks/keyboard/kalih-box-white/kboxwh - 09.mp3 differ diff --git a/soundpacks/keyboard/kalih-box-white/kboxwh - 10.mp3 b/soundpacks/keyboard/kalih-box-white/kboxwh - 10.mp3 new file mode 100644 index 0000000..18a064d Binary files /dev/null and b/soundpacks/keyboard/kalih-box-white/kboxwh - 10.mp3 differ diff --git a/audio/mxblack-travel/config.json b/soundpacks/keyboard/mxblack-travel/config.json similarity index 100% rename from audio/mxblack-travel/config.json rename to soundpacks/keyboard/mxblack-travel/config.json diff --git a/audio/mxblack-travel/press/BACKSPACE.mp3 b/soundpacks/keyboard/mxblack-travel/press/BACKSPACE.mp3 similarity index 100% rename from audio/mxblack-travel/press/BACKSPACE.mp3 rename to soundpacks/keyboard/mxblack-travel/press/BACKSPACE.mp3 diff --git a/audio/mxblack-travel/press/ENTER.mp3 b/soundpacks/keyboard/mxblack-travel/press/ENTER.mp3 similarity index 100% rename from audio/mxblack-travel/press/ENTER.mp3 rename to soundpacks/keyboard/mxblack-travel/press/ENTER.mp3 diff --git a/audio/mxblack-travel/press/GENERIC_R0.mp3 b/soundpacks/keyboard/mxblack-travel/press/GENERIC_R0.mp3 similarity index 100% rename from audio/mxblack-travel/press/GENERIC_R0.mp3 rename to soundpacks/keyboard/mxblack-travel/press/GENERIC_R0.mp3 diff --git a/audio/mxblack-travel/press/GENERIC_R1.mp3 b/soundpacks/keyboard/mxblack-travel/press/GENERIC_R1.mp3 similarity index 100% rename from audio/mxblack-travel/press/GENERIC_R1.mp3 rename to soundpacks/keyboard/mxblack-travel/press/GENERIC_R1.mp3 diff --git a/audio/mxblack-travel/press/GENERIC_R2.mp3 b/soundpacks/keyboard/mxblack-travel/press/GENERIC_R2.mp3 similarity index 100% rename from audio/mxblack-travel/press/GENERIC_R2.mp3 rename to soundpacks/keyboard/mxblack-travel/press/GENERIC_R2.mp3 diff --git a/audio/mxblack-travel/press/GENERIC_R3.mp3 b/soundpacks/keyboard/mxblack-travel/press/GENERIC_R3.mp3 similarity index 100% rename from audio/mxblack-travel/press/GENERIC_R3.mp3 rename to soundpacks/keyboard/mxblack-travel/press/GENERIC_R3.mp3 diff --git a/audio/mxblack-travel/press/GENERIC_R4.mp3 b/soundpacks/keyboard/mxblack-travel/press/GENERIC_R4.mp3 similarity index 100% rename from audio/mxblack-travel/press/GENERIC_R4.mp3 rename to soundpacks/keyboard/mxblack-travel/press/GENERIC_R4.mp3 diff --git a/audio/mxblack-travel/press/SPACE.mp3 b/soundpacks/keyboard/mxblack-travel/press/SPACE.mp3 similarity index 100% rename from audio/mxblack-travel/press/SPACE.mp3 rename to soundpacks/keyboard/mxblack-travel/press/SPACE.mp3 diff --git a/audio/mxblack-travel/release/BACKSPACE.mp3 b/soundpacks/keyboard/mxblack-travel/release/BACKSPACE.mp3 similarity index 100% rename from audio/mxblack-travel/release/BACKSPACE.mp3 rename to soundpacks/keyboard/mxblack-travel/release/BACKSPACE.mp3 diff --git a/audio/mxblack-travel/release/ENTER.mp3 b/soundpacks/keyboard/mxblack-travel/release/ENTER.mp3 similarity index 100% rename from audio/mxblack-travel/release/ENTER.mp3 rename to soundpacks/keyboard/mxblack-travel/release/ENTER.mp3 diff --git a/audio/mxblack-travel/release/GENERIC.mp3 b/soundpacks/keyboard/mxblack-travel/release/GENERIC.mp3 similarity index 100% rename from audio/mxblack-travel/release/GENERIC.mp3 rename to soundpacks/keyboard/mxblack-travel/release/GENERIC.mp3 diff --git a/audio/mxblack-travel/release/SPACE.mp3 b/soundpacks/keyboard/mxblack-travel/release/SPACE.mp3 similarity index 100% rename from audio/mxblack-travel/release/SPACE.mp3 rename to soundpacks/keyboard/mxblack-travel/release/SPACE.mp3 diff --git a/audio/mxblue-travel/config.json b/soundpacks/keyboard/mxblue-travel/config.json similarity index 100% rename from audio/mxblue-travel/config.json rename to soundpacks/keyboard/mxblue-travel/config.json diff --git a/audio/mxblue-travel/press/GENERIC_R0.mp3 b/soundpacks/keyboard/mxblue-travel/press/GENERIC_R0.mp3 similarity index 100% rename from audio/mxblue-travel/press/GENERIC_R0.mp3 rename to soundpacks/keyboard/mxblue-travel/press/GENERIC_R0.mp3 diff --git a/audio/mxblue-travel/press/GENERIC_R1.mp3 b/soundpacks/keyboard/mxblue-travel/press/GENERIC_R1.mp3 similarity index 100% rename from audio/mxblue-travel/press/GENERIC_R1.mp3 rename to soundpacks/keyboard/mxblue-travel/press/GENERIC_R1.mp3 diff --git a/audio/mxblue-travel/press/GENERIC_R2.mp3 b/soundpacks/keyboard/mxblue-travel/press/GENERIC_R2.mp3 similarity index 100% rename from audio/mxblue-travel/press/GENERIC_R2.mp3 rename to soundpacks/keyboard/mxblue-travel/press/GENERIC_R2.mp3 diff --git a/audio/mxblue-travel/press/GENERIC_R3.mp3 b/soundpacks/keyboard/mxblue-travel/press/GENERIC_R3.mp3 similarity index 100% rename from audio/mxblue-travel/press/GENERIC_R3.mp3 rename to soundpacks/keyboard/mxblue-travel/press/GENERIC_R3.mp3 diff --git a/audio/mxblue-travel/press/GENERIC_R4.mp3 b/soundpacks/keyboard/mxblue-travel/press/GENERIC_R4.mp3 similarity index 100% rename from audio/mxblue-travel/press/GENERIC_R4.mp3 rename to soundpacks/keyboard/mxblue-travel/press/GENERIC_R4.mp3 diff --git a/audio/mxblue-travel/release/GENERIC.mp3 b/soundpacks/keyboard/mxblue-travel/release/GENERIC.mp3 similarity index 100% rename from audio/mxblue-travel/release/GENERIC.mp3 rename to soundpacks/keyboard/mxblue-travel/release/GENERIC.mp3 diff --git a/audio/mxbrown-travel/config.json b/soundpacks/keyboard/mxbrown-travel/config.json similarity index 100% rename from audio/mxbrown-travel/config.json rename to soundpacks/keyboard/mxbrown-travel/config.json diff --git a/audio/mxbrown-travel/press/BACKSPACE.mp3 b/soundpacks/keyboard/mxbrown-travel/press/BACKSPACE.mp3 similarity index 100% rename from audio/mxbrown-travel/press/BACKSPACE.mp3 rename to soundpacks/keyboard/mxbrown-travel/press/BACKSPACE.mp3 diff --git a/audio/mxbrown-travel/press/ENTER.mp3 b/soundpacks/keyboard/mxbrown-travel/press/ENTER.mp3 similarity index 100% rename from audio/mxbrown-travel/press/ENTER.mp3 rename to soundpacks/keyboard/mxbrown-travel/press/ENTER.mp3 diff --git a/audio/mxbrown-travel/press/GENERIC_R0.mp3 b/soundpacks/keyboard/mxbrown-travel/press/GENERIC_R0.mp3 similarity index 100% rename from audio/mxbrown-travel/press/GENERIC_R0.mp3 rename to soundpacks/keyboard/mxbrown-travel/press/GENERIC_R0.mp3 diff --git a/audio/mxbrown-travel/press/GENERIC_R1.mp3 b/soundpacks/keyboard/mxbrown-travel/press/GENERIC_R1.mp3 similarity index 100% rename from audio/mxbrown-travel/press/GENERIC_R1.mp3 rename to soundpacks/keyboard/mxbrown-travel/press/GENERIC_R1.mp3 diff --git a/audio/mxbrown-travel/press/GENERIC_R2.mp3 b/soundpacks/keyboard/mxbrown-travel/press/GENERIC_R2.mp3 similarity index 100% rename from audio/mxbrown-travel/press/GENERIC_R2.mp3 rename to soundpacks/keyboard/mxbrown-travel/press/GENERIC_R2.mp3 diff --git a/audio/mxbrown-travel/press/GENERIC_R3.mp3 b/soundpacks/keyboard/mxbrown-travel/press/GENERIC_R3.mp3 similarity index 100% rename from audio/mxbrown-travel/press/GENERIC_R3.mp3 rename to soundpacks/keyboard/mxbrown-travel/press/GENERIC_R3.mp3 diff --git a/audio/mxbrown-travel/press/GENERIC_R4.mp3 b/soundpacks/keyboard/mxbrown-travel/press/GENERIC_R4.mp3 similarity index 100% rename from audio/mxbrown-travel/press/GENERIC_R4.mp3 rename to soundpacks/keyboard/mxbrown-travel/press/GENERIC_R4.mp3 diff --git a/audio/mxbrown-travel/press/SPACE.mp3 b/soundpacks/keyboard/mxbrown-travel/press/SPACE.mp3 similarity index 100% rename from audio/mxbrown-travel/press/SPACE.mp3 rename to soundpacks/keyboard/mxbrown-travel/press/SPACE.mp3 diff --git a/audio/mxbrown-travel/release/BACKSPACE.mp3 b/soundpacks/keyboard/mxbrown-travel/release/BACKSPACE.mp3 similarity index 100% rename from audio/mxbrown-travel/release/BACKSPACE.mp3 rename to soundpacks/keyboard/mxbrown-travel/release/BACKSPACE.mp3 diff --git a/audio/mxbrown-travel/release/ENTER.mp3 b/soundpacks/keyboard/mxbrown-travel/release/ENTER.mp3 similarity index 100% rename from audio/mxbrown-travel/release/ENTER.mp3 rename to soundpacks/keyboard/mxbrown-travel/release/ENTER.mp3 diff --git a/audio/mxbrown-travel/release/GENERIC.mp3 b/soundpacks/keyboard/mxbrown-travel/release/GENERIC.mp3 similarity index 100% rename from audio/mxbrown-travel/release/GENERIC.mp3 rename to soundpacks/keyboard/mxbrown-travel/release/GENERIC.mp3 diff --git a/audio/mxbrown-travel/release/SPACE.mp3 b/soundpacks/keyboard/mxbrown-travel/release/SPACE.mp3 similarity index 100% rename from audio/mxbrown-travel/release/SPACE.mp3 rename to soundpacks/keyboard/mxbrown-travel/release/SPACE.mp3 diff --git a/audio/nk-cream/[.wav b/soundpacks/keyboard/nk-cream/[.wav similarity index 100% rename from audio/nk-cream/[.wav rename to soundpacks/keyboard/nk-cream/[.wav diff --git a/audio/nk-cream/].wav b/soundpacks/keyboard/nk-cream/].wav similarity index 100% rename from audio/nk-cream/].wav rename to soundpacks/keyboard/nk-cream/].wav diff --git a/audio/nk-cream/a.wav b/soundpacks/keyboard/nk-cream/a.wav similarity index 100% rename from audio/nk-cream/a.wav rename to soundpacks/keyboard/nk-cream/a.wav diff --git a/audio/nk-cream/b.wav b/soundpacks/keyboard/nk-cream/b.wav similarity index 100% rename from audio/nk-cream/b.wav rename to soundpacks/keyboard/nk-cream/b.wav diff --git a/audio/nk-cream/backspace.wav b/soundpacks/keyboard/nk-cream/backspace.wav similarity index 100% rename from audio/nk-cream/backspace.wav rename to soundpacks/keyboard/nk-cream/backspace.wav diff --git a/audio/nk-cream/c.wav b/soundpacks/keyboard/nk-cream/c.wav similarity index 100% rename from audio/nk-cream/c.wav rename to soundpacks/keyboard/nk-cream/c.wav diff --git a/audio/nk-cream/caps lock.wav b/soundpacks/keyboard/nk-cream/caps lock.wav similarity index 100% rename from audio/nk-cream/caps lock.wav rename to soundpacks/keyboard/nk-cream/caps lock.wav diff --git a/audio/nk-cream/config.json b/soundpacks/keyboard/nk-cream/config.json similarity index 100% rename from audio/nk-cream/config.json rename to soundpacks/keyboard/nk-cream/config.json diff --git a/audio/nk-cream/d.wav b/soundpacks/keyboard/nk-cream/d.wav similarity index 100% rename from audio/nk-cream/d.wav rename to soundpacks/keyboard/nk-cream/d.wav diff --git a/audio/nk-cream/e.wav b/soundpacks/keyboard/nk-cream/e.wav similarity index 100% rename from audio/nk-cream/e.wav rename to soundpacks/keyboard/nk-cream/e.wav diff --git a/audio/nk-cream/enter.wav b/soundpacks/keyboard/nk-cream/enter.wav similarity index 100% rename from audio/nk-cream/enter.wav rename to soundpacks/keyboard/nk-cream/enter.wav diff --git a/audio/nk-cream/f.wav b/soundpacks/keyboard/nk-cream/f.wav similarity index 100% rename from audio/nk-cream/f.wav rename to soundpacks/keyboard/nk-cream/f.wav diff --git a/audio/nk-cream/g.wav b/soundpacks/keyboard/nk-cream/g.wav similarity index 100% rename from audio/nk-cream/g.wav rename to soundpacks/keyboard/nk-cream/g.wav diff --git a/audio/nk-cream/h.wav b/soundpacks/keyboard/nk-cream/h.wav similarity index 100% rename from audio/nk-cream/h.wav rename to soundpacks/keyboard/nk-cream/h.wav diff --git a/audio/nk-cream/i.wav b/soundpacks/keyboard/nk-cream/i.wav similarity index 100% rename from audio/nk-cream/i.wav rename to soundpacks/keyboard/nk-cream/i.wav diff --git a/audio/nk-cream/j.wav b/soundpacks/keyboard/nk-cream/j.wav similarity index 100% rename from audio/nk-cream/j.wav rename to soundpacks/keyboard/nk-cream/j.wav diff --git a/audio/nk-cream/k.wav b/soundpacks/keyboard/nk-cream/k.wav similarity index 100% rename from audio/nk-cream/k.wav rename to soundpacks/keyboard/nk-cream/k.wav diff --git a/audio/nk-cream/l.wav b/soundpacks/keyboard/nk-cream/l.wav similarity index 100% rename from audio/nk-cream/l.wav rename to soundpacks/keyboard/nk-cream/l.wav diff --git a/audio/nk-cream/m.wav b/soundpacks/keyboard/nk-cream/m.wav similarity index 100% rename from audio/nk-cream/m.wav rename to soundpacks/keyboard/nk-cream/m.wav diff --git a/audio/nk-cream/n.wav b/soundpacks/keyboard/nk-cream/n.wav similarity index 100% rename from audio/nk-cream/n.wav rename to soundpacks/keyboard/nk-cream/n.wav diff --git a/audio/nk-cream/o.wav b/soundpacks/keyboard/nk-cream/o.wav similarity index 100% rename from audio/nk-cream/o.wav rename to soundpacks/keyboard/nk-cream/o.wav diff --git a/audio/nk-cream/p.wav b/soundpacks/keyboard/nk-cream/p.wav similarity index 100% rename from audio/nk-cream/p.wav rename to soundpacks/keyboard/nk-cream/p.wav diff --git a/audio/nk-cream/q.wav b/soundpacks/keyboard/nk-cream/q.wav similarity index 100% rename from audio/nk-cream/q.wav rename to soundpacks/keyboard/nk-cream/q.wav diff --git a/audio/nk-cream/r.wav b/soundpacks/keyboard/nk-cream/r.wav similarity index 100% rename from audio/nk-cream/r.wav rename to soundpacks/keyboard/nk-cream/r.wav diff --git a/audio/nk-cream/s.wav b/soundpacks/keyboard/nk-cream/s.wav similarity index 100% rename from audio/nk-cream/s.wav rename to soundpacks/keyboard/nk-cream/s.wav diff --git a/audio/nk-cream/shift.wav b/soundpacks/keyboard/nk-cream/shift.wav similarity index 100% rename from audio/nk-cream/shift.wav rename to soundpacks/keyboard/nk-cream/shift.wav diff --git a/audio/nk-cream/space.wav b/soundpacks/keyboard/nk-cream/space.wav similarity index 100% rename from audio/nk-cream/space.wav rename to soundpacks/keyboard/nk-cream/space.wav diff --git a/audio/nk-cream/t.wav b/soundpacks/keyboard/nk-cream/t.wav similarity index 100% rename from audio/nk-cream/t.wav rename to soundpacks/keyboard/nk-cream/t.wav diff --git a/audio/nk-cream/tab.wav b/soundpacks/keyboard/nk-cream/tab.wav similarity index 100% rename from audio/nk-cream/tab.wav rename to soundpacks/keyboard/nk-cream/tab.wav diff --git a/audio/nk-cream/u.wav b/soundpacks/keyboard/nk-cream/u.wav similarity index 100% rename from audio/nk-cream/u.wav rename to soundpacks/keyboard/nk-cream/u.wav diff --git a/audio/nk-cream/v.wav b/soundpacks/keyboard/nk-cream/v.wav similarity index 100% rename from audio/nk-cream/v.wav rename to soundpacks/keyboard/nk-cream/v.wav diff --git a/audio/nk-cream/w.wav b/soundpacks/keyboard/nk-cream/w.wav similarity index 100% rename from audio/nk-cream/w.wav rename to soundpacks/keyboard/nk-cream/w.wav diff --git a/audio/nk-cream/x.wav b/soundpacks/keyboard/nk-cream/x.wav similarity index 100% rename from audio/nk-cream/x.wav rename to soundpacks/keyboard/nk-cream/x.wav diff --git a/audio/nk-cream/y.wav b/soundpacks/keyboard/nk-cream/y.wav similarity index 100% rename from audio/nk-cream/y.wav rename to soundpacks/keyboard/nk-cream/y.wav diff --git a/audio/nk-cream/z.wav b/soundpacks/keyboard/nk-cream/z.wav similarity index 100% rename from audio/nk-cream/z.wav rename to soundpacks/keyboard/nk-cream/z.wav diff --git a/audio/topre-purple-hybrid-pbt/config.json b/soundpacks/keyboard/topre-purple-hybrid-pbt/config.json similarity index 100% rename from audio/topre-purple-hybrid-pbt/config.json rename to soundpacks/keyboard/topre-purple-hybrid-pbt/config.json diff --git a/audio/topre-purple-hybrid-pbt/sound.ogg b/soundpacks/keyboard/topre-purple-hybrid-pbt/sound.ogg similarity index 100% rename from audio/topre-purple-hybrid-pbt/sound.ogg rename to soundpacks/keyboard/topre-purple-hybrid-pbt/sound.ogg diff --git a/audio/turquoise/config.json b/soundpacks/keyboard/turquoise/config.json similarity index 100% rename from audio/turquoise/config.json rename to soundpacks/keyboard/turquoise/config.json diff --git a/audio/turquoise/press/BACKSPACE.mp3 b/soundpacks/keyboard/turquoise/press/BACKSPACE.mp3 similarity index 100% rename from audio/turquoise/press/BACKSPACE.mp3 rename to soundpacks/keyboard/turquoise/press/BACKSPACE.mp3 diff --git a/audio/turquoise/press/ENTER.mp3 b/soundpacks/keyboard/turquoise/press/ENTER.mp3 similarity index 100% rename from audio/turquoise/press/ENTER.mp3 rename to soundpacks/keyboard/turquoise/press/ENTER.mp3 diff --git a/audio/turquoise/press/GENERIC_R0.mp3 b/soundpacks/keyboard/turquoise/press/GENERIC_R0.mp3 similarity index 100% rename from audio/turquoise/press/GENERIC_R0.mp3 rename to soundpacks/keyboard/turquoise/press/GENERIC_R0.mp3 diff --git a/audio/turquoise/press/GENERIC_R1.mp3 b/soundpacks/keyboard/turquoise/press/GENERIC_R1.mp3 similarity index 100% rename from audio/turquoise/press/GENERIC_R1.mp3 rename to soundpacks/keyboard/turquoise/press/GENERIC_R1.mp3 diff --git a/audio/turquoise/press/GENERIC_R2.mp3 b/soundpacks/keyboard/turquoise/press/GENERIC_R2.mp3 similarity index 100% rename from audio/turquoise/press/GENERIC_R2.mp3 rename to soundpacks/keyboard/turquoise/press/GENERIC_R2.mp3 diff --git a/audio/turquoise/press/GENERIC_R3.mp3 b/soundpacks/keyboard/turquoise/press/GENERIC_R3.mp3 similarity index 100% rename from audio/turquoise/press/GENERIC_R3.mp3 rename to soundpacks/keyboard/turquoise/press/GENERIC_R3.mp3 diff --git a/audio/turquoise/press/GENERIC_R4.mp3 b/soundpacks/keyboard/turquoise/press/GENERIC_R4.mp3 similarity index 100% rename from audio/turquoise/press/GENERIC_R4.mp3 rename to soundpacks/keyboard/turquoise/press/GENERIC_R4.mp3 diff --git a/audio/turquoise/press/SPACE.mp3 b/soundpacks/keyboard/turquoise/press/SPACE.mp3 similarity index 100% rename from audio/turquoise/press/SPACE.mp3 rename to soundpacks/keyboard/turquoise/press/SPACE.mp3 diff --git a/audio/turquoise/release/BACKSPACE.mp3 b/soundpacks/keyboard/turquoise/release/BACKSPACE.mp3 similarity index 100% rename from audio/turquoise/release/BACKSPACE.mp3 rename to soundpacks/keyboard/turquoise/release/BACKSPACE.mp3 diff --git a/audio/turquoise/release/ENTER.mp3 b/soundpacks/keyboard/turquoise/release/ENTER.mp3 similarity index 100% rename from audio/turquoise/release/ENTER.mp3 rename to soundpacks/keyboard/turquoise/release/ENTER.mp3 diff --git a/audio/turquoise/release/GENERIC.mp3 b/soundpacks/keyboard/turquoise/release/GENERIC.mp3 similarity index 100% rename from audio/turquoise/release/GENERIC.mp3 rename to soundpacks/keyboard/turquoise/release/GENERIC.mp3 diff --git a/audio/turquoise/release/SPACE.mp3 b/soundpacks/keyboard/turquoise/release/SPACE.mp3 similarity index 100% rename from audio/turquoise/release/SPACE.mp3 rename to soundpacks/keyboard/turquoise/release/SPACE.mp3 diff --git a/soundpacks/mouse/chat/chat.mp3 b/soundpacks/mouse/chat/chat.mp3 new file mode 100644 index 0000000..883b63a Binary files /dev/null and b/soundpacks/mouse/chat/chat.mp3 differ diff --git a/soundpacks/mouse/chat/config.json b/soundpacks/mouse/chat/config.json new file mode 100644 index 0000000..47627a8 --- /dev/null +++ b/soundpacks/mouse/chat/config.json @@ -0,0 +1,28 @@ +{ + "audio_file": "chat.mp3", + "config_version": "2", + "created_at": "2025-06-17T12:23:39.537516300+00:00", + "definition_method": "single", + "author": "Mechvibes", + "definitions": { + "MouseLeft": { + "timing": [ + [26.0, 156.0], + [176.0, 545.0] + ] + }, + "MouseRight": { + "timing": [ + [26.0, 156.0], + [176.0, 545.0] + ] + } + }, + "id": "mouse-chat", + "name": "Chat", + "options": { + "random_pitch": false, + "recommended_volume": 1.0 + }, + "tags": [] +} diff --git a/soundpacks/mouse/ping/config.json b/soundpacks/mouse/ping/config.json new file mode 100644 index 0000000..9d13ff4 --- /dev/null +++ b/soundpacks/mouse/ping/config.json @@ -0,0 +1,28 @@ +{ + "audio_file": "ping.mp3", + "config_version": "2", + "created_at": "2025-06-17T12:23:39.537516300+00:00", + "definition_method": "single", + "author": "Mechvibes", + "definitions": { + "MouseLeft": { + "timing": [ + [47.0, 84.0], + [105.0, 177.0] + ] + }, + "MouseRight": { + "timing": [ + [261.0, 295.0], + [373.0, 402.0] + ] + } + }, + "id": "mouse-ping", + "name": "Ping", + "options": { + "random_pitch": false, + "recommended_volume": 1.0 + }, + "tags": [] +} diff --git a/soundpacks/mouse/ping/ping.mp3 b/soundpacks/mouse/ping/ping.mp3 new file mode 100644 index 0000000..945eac5 Binary files /dev/null and b/soundpacks/mouse/ping/ping.mp3 differ diff --git a/soundpacks/mouse/vibrate/config.json b/soundpacks/mouse/vibrate/config.json new file mode 100644 index 0000000..103c375 --- /dev/null +++ b/soundpacks/mouse/vibrate/config.json @@ -0,0 +1,28 @@ +{ + "audio_file": "vibrate.mp3", + "config_version": "2", + "created_at": "2025-06-17T12:23:39.537516300+00:00", + "definition_method": "single", + "author": "Mechvibes", + "definitions": { + "MouseLeft": { + "timing": [ + [99.0, 321.0], + [368.0, 633.0] + ] + }, + "MouseRight": { + "timing": [ + [99.0, 321.0], + [368.0, 633.0] + ] + } + }, + "id": "mouse-vibrate", + "name": "Vibrate", + "options": { + "random_pitch": false, + "recommended_volume": 1.0 + }, + "tags": [] +} diff --git a/soundpacks/mouse/vibrate/vibrate.mp3 b/soundpacks/mouse/vibrate/vibrate.mp3 new file mode 100644 index 0000000..4bd2435 Binary files /dev/null and b/soundpacks/mouse/vibrate/vibrate.mp3 differ diff --git a/soundpacks/mouse/wooden/config.json b/soundpacks/mouse/wooden/config.json new file mode 100644 index 0000000..a1f10f3 --- /dev/null +++ b/soundpacks/mouse/wooden/config.json @@ -0,0 +1,28 @@ +{ + "audio_file": "wooden.mp3", + "config_version": "2", + "created_at": "2025-06-17T12:23:39.537516300+00:00", + "definition_method": "single", + "author": "Mechvibes", + "definitions": { + "MouseLeft": { + "timing": [ + [528.0, 570.0], + [782.0, 806.0] + ] + }, + "MouseRight": { + "timing": [ + [528.0, 570.0], + [782.0, 806.0] + ] + } + }, + "id": "mouse-wooden", + "name": "Wooden", + "options": { + "random_pitch": false, + "recommended_volume": 1.0 + }, + "tags": [] +} diff --git a/soundpacks/mouse/wooden/wooden.mp3 b/soundpacks/mouse/wooden/wooden.mp3 new file mode 100644 index 0000000..1c648e9 Binary files /dev/null and b/soundpacks/mouse/wooden/wooden.mp3 differ diff --git a/src/keyvibe_audio.c b/src/keyvibe_audio.c index 6683a60..e16dc96 100644 --- a/src/keyvibe_audio.c +++ b/src/keyvibe_audio.c @@ -1,8 +1,6 @@ - #define _GNU_SOURCE -#include "utils.h" -#include #include +#include "utils.h" #include #include #include @@ -14,6 +12,7 @@ #include #include #include +#include #define MAX_LINE_LENGTH 1024 #define MAX_CONCURRENT_SOUNDS 10 @@ -28,11 +27,11 @@ typedef struct { char generic_press_files[5][256]; int num_generic_press_files; char sound_file[256]; - SoundMapping key_mappings[256]; + SoundMapping key_mappings[512]; struct { char *press; char *release; - } multi_key_mappings[256]; + } multi_key_mappings[512]; int is_multi; SF_INFO sf_info; } SoundPack; @@ -43,13 +42,16 @@ typedef struct { } PlaybackData; 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; pthread_t sound_threads[MAX_CONCURRENT_SOUNDS]; volatile int thread_active[MAX_CONCURRENT_SOUNDS] = {0}; pthread_mutex_t thread_mutex = PTHREAD_MUTEX_INITIALIZER; + static void get_full_path(char *buffer, size_t buffer_size, const char *base_dir, const char *filename) { if (!filename || !base_dir) { @@ -119,8 +121,7 @@ int load_sound_config(const char *config_path) { g_sound_pack.num_generic_press_files = 0; 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 (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]; @@ -175,24 +176,35 @@ int load_sound_config(const char *config_path) { 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); + if (g_verbose) printf("Release sound file: %s\n", g_sound_pack.release_file); } if (json_object_object_get_ex(root, "defines", &obj)) { json_object_object_foreach(obj, key, val) { int key_code; int is_release = 0; - char *dash = strstr(key, "-up"); - if (dash) { - char key_copy[16]; - strncpy(key_copy, key, dash - key); - key_copy[dash - key] = '\0'; - key_code = atoi(key_copy); - is_release = 1; + + // Handle mouse key names first + 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); + // Handle regular key codes with -up suffix + char *dash = strstr(key, "-up"); + if (dash) { + char key_copy[16]; + strncpy(key_copy, key, dash - key); + key_copy[dash - key] = '\0'; + key_code = atoi(key_copy); + is_release = 1; + } else { + key_code = atoi(key); + } } - if (key_code >= 0 && key_code < 256) { + + if (key_code >= 0 && key_code < 512) { const char *filename_relative = json_object_get_string(val); char full_filename[MAX_LINE_LENGTH]; get_full_path(full_filename, sizeof(full_filename), config_dir, @@ -212,26 +224,62 @@ int load_sound_config(const char *config_path) { } } } else { - if (json_object_object_get_ex(root, "sound", &obj)) { + // Try "sound" first, then "audio_file" for mouse configs + if (json_object_object_get_ex(root, "sound", &obj) || + json_object_object_get_ex(root, "audio_file", &obj)) { char temp_sound_file[256]; strncpy(temp_sound_file, json_object_get_string(obj), sizeof(temp_sound_file) - 1); temp_sound_file[sizeof(temp_sound_file) - 1] = '\0'; 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 (g_verbose) printf("Single mode sound file: %s\n", g_sound_pack.sound_file); } - if (json_object_object_get_ex(root, "defines", &obj)) { - json_object_object_foreach(obj, key, val) { - int key_code = atoi(key); - if (key_code >= 0 && key_code < 256 && - json_object_is_type(val, json_type_array) && - 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)); + // Try "defines" first, then "definitions" for mouse configs + json_object *defines_obj = NULL; + 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; + + // Handle mouse key names + 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) { + // Handle different config formats + if (json_object_is_type(val, json_type_array)) { + // Standard format: [start_ms, duration_ms] + 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)); + } + } else if (json_object_is_type(val, json_type_object)) { + // Mouse format: {"timing": [[start1, duration1], [start2, duration2]]} + 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) { + // Use the first timing entry + 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)); + } + } + } } } } @@ -274,20 +322,27 @@ void *play_sound_thread(void *arg) { int thread_id = data->thread_id; int is_pressed = data->is_pressed; const char *file_to_play = NULL; + + // Determine if this is a mouse event (key codes 272, 273, 274) + 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; + if (g_verbose) { - printf("Thread %d: Playing sound for key %d (%s)\n", thread_id, key_code, + printf("Thread %d: Playing %s sound for key %d (%s)\n", thread_id, + is_mouse_event ? "mouse" : "keyboard", key_code, is_pressed ? "press" : "release"); } - if (g_sound_pack.is_multi) { - if (is_pressed && g_sound_pack.multi_key_mappings[key_code].press) - file_to_play = g_sound_pack.multi_key_mappings[key_code].press; - else if (!is_pressed && g_sound_pack.multi_key_mappings[key_code].release) - file_to_play = g_sound_pack.multi_key_mappings[key_code].release; - else if (is_pressed && g_sound_pack.num_generic_press_files > 0) { - int idx = rand() % g_sound_pack.num_generic_press_files; - file_to_play = g_sound_pack.generic_press_files[idx]; - } else if (!is_pressed && strlen(g_sound_pack.release_file) > 0) - file_to_play = g_sound_pack.release_file; + 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, @@ -328,7 +383,7 @@ void *play_sound_thread(void *arg) { 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] * g_volume); + buffer[i] = (short)(buffer[i] * volume); } int pa_write_error; if (pa_simple_write(pa_handle, buffer, @@ -345,16 +400,16 @@ void *play_sound_thread(void *arg) { sf_close(sf); free(buffer); } else { - if (key_code >= 256 || - g_sound_pack.key_mappings[key_code].duration_ms == 0) { + 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 = &g_sound_pack.key_mappings[key_code]; - SF_INFO sf_info = g_sound_pack.sf_info; - SNDFILE *sf = sf_open(g_sound_pack.sound_file, SFM_READ, &sf_info); + 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; @@ -384,7 +439,7 @@ void *play_sound_thread(void *arg) { } 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] * g_volume); + buffer[i] = (short)(buffer[i] * volume); } int pa_write_error, pa_drain_error; pa_simple_write(pa_handle, buffer, @@ -489,10 +544,9 @@ int parse_keyboard_event(const char *json_line, int *key_code, } void cleanup() { - if (g_verbose) - printf("Cleaning up...\n"); + if (g_verbose) printf("Cleaning up...\n"); usleep(500000); - for (int i = 0; i < 256; i++) { + for (int i = 0; i < 512; i++) { if (g_sound_pack.multi_key_mappings[i].press) { free(g_sound_pack.multi_key_mappings[i].press); g_sound_pack.multi_key_mappings[i].press = NULL; @@ -515,16 +569,15 @@ static int read_mute_state() { if (!rd || strlen(rd) == 0) { rd = "/tmp"; } - if (!safe_snprintf(mute_file, sizeof(mute_file), "%s/keyvibe-mute-%d", rd, - (int)getuid())) { + if (!safe_snprintf(mute_file, sizeof(mute_file), "%s/keyvibe-mute-%d", rd, (int)getuid())) { return 0; } - + FILE *f = fopen(mute_file, "r"); if (!f) { return 0; // Default to unmuted if file doesn't exist } - + int mute = 0; if (fscanf(f, "%d", &mute) == 1) { fclose(f); @@ -534,13 +587,15 @@ static int read_mute_state() { return 0; } + int main(int argc, char *argv[]) { - if (argc < 2 || argc > 5) { - fprintf(stderr, "Usage: %s [volume] [verbose] [mute]\n", - argv[0]); + if (argc < 2 || argc > 7) { + fprintf(stderr, "Usage: %s [volume] [verbose] [mute] [mouse_config] [mouse_volume]\n", argv[0]); fprintf(stderr, " volume: 0-100 (default: 50)\n"); fprintf(stderr, " verbose: 1 to enable verbose output (default: 0)\n"); fprintf(stderr, " mute: 1 to mute sound (default: 0)\n"); + fprintf(stderr, " mouse_config: path to mouse config.json (optional)\n"); + fprintf(stderr, " mouse_volume: 0-100 for mouse volume (optional)\n"); return 1; } if (argc >= 3) { @@ -550,11 +605,9 @@ int main(int argc, char *argv[]) { if (volume_percent > 100) volume_percent = 100; g_volume = volume_percent / 100.0f; - if (g_verbose) - printf("Volume set to: %d%%\n", volume_percent); + if (g_verbose) printf("Volume set to: %d%%\n", volume_percent); } else { - if (g_verbose) - printf("Volume set to: 50%% (default)\n"); + if (g_verbose) printf("Volume set to: 50%% (default)\n"); } if (argc >= 4) { g_verbose = atoi(argv[3]); @@ -568,8 +621,32 @@ int main(int argc, char *argv[]) { printf("Sound muted\n"); } } + + // Parse mouse config and volume if provided + if (argc >= 6) { + if (load_sound_config(argv[5]) != 0) { + fprintf(stderr, "Failed to load mouse sound configuration\n"); + return 1; + } + // Copy the loaded config to mouse sound pack + g_mouse_sound_pack = g_sound_pack; + if (g_verbose) { + printf("Mouse sound pack loaded from: %s\n", argv[5]); + } + } + + if (argc >= 7) { + int mouse_volume_percent = atoi(argv[6]); + if (mouse_volume_percent < 0) + mouse_volume_percent = 0; + if (mouse_volume_percent > 100) + mouse_volume_percent = 100; + g_mouse_volume = mouse_volume_percent / 100.0f; + if (g_verbose) printf("Mouse volume set to: %d%%\n", mouse_volume_percent); + } + if (load_sound_config(argv[1]) != 0) { - fprintf(stderr, "Failed to load sound configuration\n"); + fprintf(stderr, "Failed to load keyboard sound configuration\n"); return 1; } if (init_audio() != 0) { @@ -583,7 +660,7 @@ int main(int argc, char *argv[]) { while (1) { // Check mute state from file periodically g_mute = read_mute_state(); - + FD_ZERO(&readfds); FD_SET(STDIN_FILENO, &readfds); timeout.tv_sec = 1; @@ -605,8 +682,7 @@ int main(int argc, char *argv[]) { if (FD_ISSET(STDIN_FILENO, &readfds)) { if (fgets(line, sizeof(line), stdin) == NULL) { if (feof(stdin)) { - if (g_verbose) - printf("EOF reached on stdin\n"); + if (g_verbose) printf("EOF reached on stdin\n"); } else { perror("fgets"); } diff --git a/src/keyvibe_input.c b/src/keyvibe_input.c index e70fdbf..90f04d0 100644 --- a/src/keyvibe_input.c +++ b/src/keyvibe_input.c @@ -1,18 +1,19 @@ -#include "utils.h" #include -#include -#include -#include -#include -#include -#include -#include #include #include #include +#include "utils.h" #include #include + +#include +#include +#include +#include #include +#include +#include +#include #include "config.h" @@ -126,13 +127,11 @@ static int run_mainloop(struct libinput *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"); + return errorf("Expected device added events on startup but got none. Maybe you don't have the right permissions?\n"); while (1) { int pr = poll(&fd, 1, -1); if (pr < 0) { - if (errno == EINTR) - continue; + if (errno == EINTR) continue; return errorf("poll failed: %s\n", strerror(errno)); } handle_events(libinput); @@ -192,8 +191,7 @@ int main(int argc, char *argv[]) { } pthread_t input_handler; struct input_handler_data input_handler_data = {udev, libinput}; - if (pthread_create(&input_handler, NULL, handle_input, &input_handler_data) != - 0) { + if (pthread_create(&input_handler, NULL, handle_input, &input_handler_data) != 0) { errorf("Failed to create input handler thread.\n"); } else { pthread_detach(input_handler); diff --git a/src/keyvibe_main.c b/src/keyvibe_main.c index b782cd9..5b88cfd 100644 --- a/src/keyvibe_main.c +++ b/src/keyvibe_main.c @@ -10,8 +10,8 @@ #include #include #include -#include #include +#include #include #include #include @@ -22,8 +22,11 @@ #include #define MAX_PATH_LENGTH 1024 -#define AUDIO_BASE_DIR KeyVibe_DATA_DIR "/audio" -#define USER_AUDIO_SUBPATH "/.local/share/keyvibe/audio" +#define AUDIO_BASE_DIR KeyVibe_DATA_DIR "/soundpacks" +#define KEYBOARD_AUDIO_DIR AUDIO_BASE_DIR "/keyboard" +#define MOUSE_AUDIO_DIR AUDIO_BASE_DIR "/mouse" +#define USER_KEYBOARD_AUDIO_SUBPATH "/.local/share/keyvibe/soundpacks/keyboard" +#define USER_MOUSE_AUDIO_SUBPATH "/.local/share/keyvibe/soundpacks/mouse" #define USER_CONFIG_FILENAME ".keyvibe.json" // Global variables for cleanup @@ -32,19 +35,29 @@ pid_t sound_pid = 0; static char pidfile_path[MAX_PATH_LENGTH] = {0}; static int is_daemon = 0; static volatile sig_atomic_t reload_requested = 0; -static int current_volume = 50; +static volatile sig_atomic_t keyboard_reload_requested = 0; +static volatile sig_atomic_t mouse_reload_requested = 0; +static volatile sig_atomic_t volume_reload_requested = 0; +static int current_keyboard_volume = 50; +static int current_mouse_volume = 50; static char current_sound_name[MAX_PATH_LENGTH] = {0}; +static char current_mouse_sound_name[MAX_PATH_LENGTH] = {0}; static int current_verbose = 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}; +static char current_mouse_sound_dir[MAX_PATH_LENGTH] = {0}; static int current_mute = 0; void print_usage(const char *program_name) { printf("KeyVibe - Mechanical Keyboard Sound Simulator\n\n"); printf("Usage: %s [OPTIONS]\n\n", program_name); printf("Options:\n"); - printf(" -S, --sound SOUND_NAME Select sound pack (default: eg-oreo)\n"); - printf(" -V, --volume VOLUME Set volume [0-100] (default: 50)\n"); + printf(" -S, --sound SOUND_NAME Select keyboard sound pack (default: eg-oreo)\n"); + printf(" -M, --mouse SOUND_NAME Select mouse sound pack (default: ping)\n"); + printf(" -V, --volume VOLUME Set volume [0-100] for both keyboard and mouse (default: 50)\n"); + printf(" -K, --keyboard-volume VOLUME Set keyboard volume [0-100] (default: 50)\n"); + printf(" -O, --mouse-volume VOLUME Set mouse volume [0-100] (default: 50)\n"); printf(" -l, --list List available sound packs\n"); printf(" -d, --daemon Run in background (write PID file)\n"); printf(" -s, --stop Stop background daemon\n"); @@ -61,31 +74,31 @@ int list_sound_packs() { char path[MAX_PATH_LENGTH]; struct stat st; - // Determine user audio dir - char user_audio_dir[MAX_PATH_LENGTH] = {0}; + // Determine user audio dirs + char user_keyboard_dir[MAX_PATH_LENGTH] = {0}; + char user_mouse_dir[MAX_PATH_LENGTH] = {0}; const char *home = getenv("HOME"); if (!home || strlen(home) == 0) { struct passwd *pw = getpwuid(getuid()); - if (pw && pw->pw_dir) - home = pw->pw_dir; + if (pw && pw->pw_dir) home = pw->pw_dir; } if (home) { - snprintf(user_audio_dir, sizeof(user_audio_dir), "%s%s", home, - USER_AUDIO_SUBPATH); + snprintf(user_keyboard_dir, sizeof(user_keyboard_dir), "%s%s", home, USER_KEYBOARD_AUDIO_SUBPATH); + snprintf(user_mouse_dir, sizeof(user_mouse_dir), "%s%s", home, USER_MOUSE_AUDIO_SUBPATH); } printf("Available sound packs:\n"); - printf("======================\n"); + printf("======================\n\n"); - // List user packs first (if available) - if (user_audio_dir[0] && (dir = opendir(user_audio_dir)) != NULL) { + printf("Keyboard Sound Packs:\n"); + printf("---------------------\n"); + + // List user keyboard packs first (if available) + 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_audio_dir, - entry->d_name) >= sizeof(path)) { - fprintf(stderr, "Warning: Path too long, skipping: %s/%s\n", - user_audio_dir, entry->d_name); + 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", user_keyboard_dir, entry->d_name); continue; } if (stat(path, &st) == 0 && S_ISDIR(st.st_mode)) { @@ -95,16 +108,47 @@ int list_sound_packs() { closedir(dir); } - // Then list system packs - dir = opendir(AUDIO_BASE_DIR); + // Then list system keyboard packs + dir = opendir(KEYBOARD_AUDIO_DIR); if (dir != NULL) { while ((entry = readdir(dir)) != NULL) { - if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) + if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) 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", KEYBOARD_AUDIO_DIR, entry->d_name); continue; - if ((size_t)snprintf(path, sizeof(path), "%s/%s", AUDIO_BASE_DIR, - entry->d_name) >= sizeof(path)) { - fprintf(stderr, "Warning: Path too long, skipping: %s/%s\n", - AUDIO_BASE_DIR, entry->d_name); + } + if (stat(path, &st) == 0 && S_ISDIR(st.st_mode)) { + printf(" %s (system)\n", entry->d_name); + } + } + closedir(dir); + } + + printf("\nMouse Sound Packs:\n"); + printf("------------------\n"); + + // List user mouse packs first (if available) + 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", user_mouse_dir, entry->d_name); + continue; + } + if (stat(path, &st) == 0 && S_ISDIR(st.st_mode)) { + printf(" %s (user)\n", entry->d_name); + } + } + closedir(dir); + } + + // Then list system mouse packs + dir = opendir(MOUSE_AUDIO_DIR); + if (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", MOUSE_AUDIO_DIR, entry->d_name) >= sizeof(path)) { + fprintf(stderr, "Warning: Path too long, skipping: %s/%s\n", MOUSE_AUDIO_DIR, entry->d_name); continue; } if (stat(path, &st) == 0 && S_ISDIR(st.st_mode)) { @@ -117,60 +161,101 @@ int list_sound_packs() { return 0; } -static void get_user_audio_dir(char *buffer, size_t buflen) { +static void get_user_keyboard_audio_dir(char *buffer, size_t buflen) { buffer[0] = '\0'; const char *home = getenv("HOME"); if (!home || strlen(home) == 0) { struct passwd *pw = getpwuid(getuid()); - if (pw && pw->pw_dir) - home = pw->pw_dir; + if (pw && pw->pw_dir) home = pw->pw_dir; } - if (home) - snprintf(buffer, buflen, "%s%s", home, USER_AUDIO_SUBPATH); + if (home) snprintf(buffer, buflen, "%s%s", home, USER_KEYBOARD_AUDIO_SUBPATH); } -static int resolve_sound_base_dir(const char *sound_name, char *out_basedir, - size_t out_sz) { +static void get_user_mouse_audio_dir(char *buffer, size_t buflen) { + buffer[0] = '\0'; + const char *home = getenv("HOME"); + if (!home || strlen(home) == 0) { + struct passwd *pw = getpwuid(getuid()); + if (pw && pw->pw_dir) home = pw->pw_dir; + } + if (home) snprintf(buffer, buflen, "%s%s", home, USER_MOUSE_AUDIO_SUBPATH); +} + +static int resolve_keyboard_sound_base_dir(const char *sound_name, char *out_basedir, size_t out_sz) { char user_audio_dir[MAX_PATH_LENGTH]; - get_user_audio_dir(user_audio_dir, sizeof(user_audio_dir)); + get_user_keyboard_audio_dir(user_audio_dir, sizeof(user_audio_dir)); if (user_audio_dir[0]) { char p[MAX_PATH_LENGTH]; - if ((size_t)snprintf(p, sizeof(p), "%s/%s/config.json", user_audio_dir, - sound_name) < sizeof(p) && - access(p, R_OK) == 0) { - strncpy(out_basedir, user_audio_dir, out_sz - 1); - out_basedir[out_sz - 1] = '\0'; + if ((size_t)snprintf(p, sizeof(p), "%s/%s/config.json", user_audio_dir, sound_name) < sizeof(p) + && access(p, R_OK) == 0) { + strncpy(out_basedir, user_audio_dir, out_sz - 1); out_basedir[out_sz-1] = '\0'; return 1; } } // Fallback to system dir char p2[MAX_PATH_LENGTH]; - if ((size_t)snprintf(p2, sizeof(p2), "%s/%s/config.json", AUDIO_BASE_DIR, - sound_name) >= sizeof(p2)) { + if ((size_t)snprintf(p2, sizeof(p2), "%s/%s/config.json", KEYBOARD_AUDIO_DIR, sound_name) >= sizeof(p2)) { return 0; // Path too long } if (access(p2, R_OK) == 0) { - strncpy(out_basedir, AUDIO_BASE_DIR, out_sz - 1); - out_basedir[out_sz - 1] = '\0'; + strncpy(out_basedir, KEYBOARD_AUDIO_DIR, out_sz - 1); out_basedir[out_sz-1] = '\0'; return 1; } return 0; } -int validate_sound_pack(const char *sound_name) { +static int resolve_mouse_sound_base_dir(const char *sound_name, char *out_basedir, size_t out_sz) { + char user_audio_dir[MAX_PATH_LENGTH]; + get_user_mouse_audio_dir(user_audio_dir, sizeof(user_audio_dir)); + if (user_audio_dir[0]) { + char p[MAX_PATH_LENGTH]; + if ((size_t)snprintf(p, sizeof(p), "%s/%s/config.json", user_audio_dir, sound_name) < sizeof(p) + && access(p, R_OK) == 0) { + strncpy(out_basedir, user_audio_dir, out_sz - 1); out_basedir[out_sz-1] = '\0'; + return 1; + } + } + // Fallback to system dir + char p2[MAX_PATH_LENGTH]; + if ((size_t)snprintf(p2, sizeof(p2), "%s/%s/config.json", MOUSE_AUDIO_DIR, sound_name) >= sizeof(p2)) { + return 0; // Path too long + } + if (access(p2, R_OK) == 0) { + strncpy(out_basedir, MOUSE_AUDIO_DIR, out_sz - 1); out_basedir[out_sz-1] = '\0'; + return 1; + } + return 0; +} + +int validate_keyboard_sound_pack(const char *sound_name) { char basedir[MAX_PATH_LENGTH]; - if (!resolve_sound_base_dir(sound_name, basedir, sizeof(basedir))) { - fprintf(stderr, - "Error: Sound pack '%s' not found in user or system dirs.\n", - sound_name); + 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"); return 0; } char config_path[MAX_PATH_LENGTH]; - 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); + 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); + return 0; + } + if (access(config_path, R_OK) != 0) { + fprintf(stderr, "Error: Config file not found: %s\n", config_path); + return 0; + } + return 1; +} + +int validate_mouse_sound_pack(const char *sound_name) { + char basedir[MAX_PATH_LENGTH]; + 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"); + return 0; + } + char config_path[MAX_PATH_LENGTH]; + 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); return 0; } if (access(config_path, R_OK) != 0) { @@ -213,11 +298,12 @@ static char *get_user_config_path(char *buffer, size_t buflen) { return buffer; } -static int build_paths_for_sound(const char *sound_name, char *out_config_path, + +static int build_paths_for_keyboard_sound(const char *sound_name, char *out_config_path, size_t out_config_sz, char *out_sound_dir, size_t out_sound_sz) { char basedir[MAX_PATH_LENGTH]; - if (!resolve_sound_base_dir(sound_name, basedir, sizeof(basedir))) { + if (!resolve_keyboard_sound_base_dir(sound_name, basedir, sizeof(basedir))) { return 0; } size_t base_len = strlen(basedir); @@ -232,20 +318,45 @@ static int build_paths_for_sound(const char *sound_name, char *out_config_path, fprintf(stderr, "Error: sound pack name too long for directory buffer.\n"); return 0; } - if (!safe_snprintf(out_config_path, out_config_sz, "%s/%s%s", basedir, - sound_name, cfg_suffix)) + if (!safe_snprintf(out_config_path, out_config_sz, "%s/%s%s", basedir, sound_name, cfg_suffix)) return 0; if (!safe_snprintf(out_sound_dir, out_sound_sz, "%s/%s", basedir, sound_name)) return 0; return 1; } +static int build_paths_for_mouse_sound(const char *sound_name, char *out_config_path, + size_t out_config_sz, char *out_sound_dir, + size_t out_sound_sz) { + char basedir[MAX_PATH_LENGTH]; + if (!resolve_mouse_sound_base_dir(sound_name, basedir, sizeof(basedir))) { + return 0; + } + size_t base_len = strlen(basedir); + size_t name_len = strlen(sound_name); + const char *cfg_suffix = "/config.json"; + size_t cfg_suffix_len = strlen(cfg_suffix); + if (base_len + 1 + name_len + cfg_suffix_len + 1 > out_config_sz) { + fprintf(stderr, "Error: sound pack name too long for path buffer.\n"); + return 0; + } + if (base_len + 1 + name_len + 1 > out_sound_sz) { + fprintf(stderr, "Error: sound pack name too long for directory buffer.\n"); + return 0; + } + if (!safe_snprintf(out_config_path, out_config_sz, "%s/%s%s", basedir, sound_name, cfg_suffix)) + return 0; + if (!safe_snprintf(out_sound_dir, out_sound_sz, "%s/%s", basedir, sound_name)) + return 0; + return 1; +} + + // Helper: ensure daemon is running, return its pid or print a consistent error static int require_running_pid(pid_t *out_pid) { build_pidfile_path(pidfile_path, sizeof(pidfile_path)); pid_t running_pid = 0; - if (!read_pidfile(pidfile_path, &running_pid) || - !process_is_running(running_pid)) { + if (!read_pidfile(pidfile_path, &running_pid) || !process_is_running(running_pid)) { fprintf(stderr, "KeyVibe: not running.\n"); return 0; } @@ -292,8 +403,11 @@ static void stop_children() { } static int start_children(const char *sound_dir, const char *config_path, - int volume, int verbose, int mute) { + int volume, int verbose, int mute, + const char *mouse_sound_dir, const char *mouse_config_path, + int mouse_volume) { (void)config_path; + (void)mouse_sound_dir; int pipefd[2]; if (pipe(pipefd) == -1) { perror("pipe"); @@ -319,12 +433,14 @@ static int start_children(const char *sound_dir, const char *config_path, } char volume_str[32]; snprintf(volume_str, sizeof(volume_str), "%d", volume); + char mouse_volume_str[32]; + snprintf(mouse_volume_str, sizeof(mouse_volume_str), "%d", mouse_volume); char verbose_str[8]; snprintf(verbose_str, sizeof(verbose_str), "%d", verbose); char mute_str[8]; snprintf(mute_str, sizeof(mute_str), "%d", mute); execl(sound_player_path, "keyvibe-audio", "config.json", volume_str, - verbose_str, mute_str, (char *)NULL); + verbose_str, mute_str, mouse_config_path, mouse_volume_str, (char *)NULL); perror("execl keyvibe-audio"); exit(1); } @@ -360,9 +476,8 @@ static void handle_sighup(int sig) { static void write_mute_state(int mute) { char mute_file[MAX_PATH_LENGTH]; const char *rd = get_runtime_dir(); - snprintf(mute_file, sizeof(mute_file), "%s/keyvibe-mute-%d", rd, - (int)getuid()); - + snprintf(mute_file, sizeof(mute_file), "%s/keyvibe-mute-%d", rd, (int)getuid()); + FILE *f = fopen(mute_file, "w"); if (f) { fprintf(f, "%d\n", mute); @@ -370,6 +485,7 @@ static void write_mute_state(int mute) { } } + struct inotify_thread_args { char path[MAX_PATH_LENGTH]; }; @@ -377,24 +493,33 @@ struct inotify_thread_args { 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) + if (fd < 0) { + printf("Failed to initialize inotify: %s\n", strerror(errno)); return NULL; - int wd = - inotify_add_watch(fd, a->path, IN_CLOSE_WRITE | IN_MOVED_TO | IN_ATTRIB); + } + 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)); close(fd); return NULL; } + printf("Watching config file: %s\n", a->path); + char buf[4096] __attribute__((aligned(__alignof__(struct inotify_event)))); while (1) { ssize_t len = read(fd, buf, sizeof(buf)); if (len <= 0) { + if (errno == EAGAIN || errno == EWOULDBLOCK) { struct timespec ts; ts.tv_sec = 0; - ts.tv_nsec = 200000000L; + ts.tv_nsec = 100000000L; // 100ms nanosleep(&ts, NULL); continue; } + printf("inotify read error: %s\n", strerror(errno)); + break; + } + printf("Config file changed, requesting reload...\n"); reload_requested = 1; } inotify_rm_watch(fd, wd); @@ -402,12 +527,23 @@ static void *inotify_thread_fn(void *arg) { return NULL; } -static int write_user_config(const char *path, const char *sound, int volume) { +static int write_user_config(const char *path, const char *keyboard_sound, const char *mouse_sound, int keyboard_volume, int mouse_volume) { json_object *root = json_object_new_object(); if (!root) return 0; - json_object_object_add(root, "sound", json_object_new_string(sound)); - json_object_object_add(root, "volume", json_object_new_int(volume)); + + // Create keyboard section + json_object *keyboard = json_object_new_object(); + json_object_object_add(keyboard, "keyboard_sound", json_object_new_string(keyboard_sound)); + json_object_object_add(keyboard, "volume", json_object_new_int(keyboard_volume)); + json_object_object_add(root, "keyboard", keyboard); + + // Create mouse section + json_object *mouse = json_object_new_object(); + json_object_object_add(mouse, "mouse_sound", json_object_new_string(mouse_sound)); + json_object_object_add(mouse, "volume", json_object_new_int(mouse_volume)); + json_object_object_add(root, "mouse", mouse); + const char *json_str = json_object_to_json_string_ext(root, JSON_C_TO_STRING_PRETTY); FILE *f = fopen(path, "w"); @@ -421,8 +557,8 @@ static int write_user_config(const char *path, const char *sound, int volume) { return 1; } -static int read_user_config(const char *path, char **out_sound, - int *out_volume) { +static int read_user_config(const char *path, char **out_keyboard_sound, char **out_mouse_sound, + int *out_keyboard_volume, int *out_mouse_volume) { FILE *f = fopen(path, "r"); if (!f) return 0; @@ -450,74 +586,110 @@ static int read_user_config(const char *path, char **out_sound, free(buf); if (!root) return 0; + + // Try new format first (keyboard/mouse sections) + json_object *keyboard_obj, *mouse_obj; + if (json_object_object_get_ex(root, "keyboard", &keyboard_obj) && + json_object_object_get_ex(root, "mouse", &mouse_obj)) { + // New format + json_object *o; + if (json_object_object_get_ex(keyboard_obj, "keyboard_sound", &o)) { + const char *s = json_object_get_string(o); + if (s) + *out_keyboard_sound = strdup(s); + } + if (json_object_object_get_ex(mouse_obj, "mouse_sound", &o)) { + const char *s = json_object_get_string(o); + if (s) + *out_mouse_sound = strdup(s); + } + + // Read separate volumes for keyboard and mouse + if (json_object_object_get_ex(keyboard_obj, "volume", &o)) { + *out_keyboard_volume = json_object_get_int(o); + } + if (json_object_object_get_ex(mouse_obj, "volume", &o)) { + *out_mouse_volume = json_object_get_int(o); + } + } else { + // Legacy format - try old single sound/volume format json_object *o; if (json_object_object_get_ex(root, "sound", &o)) { const char *s = json_object_get_string(o); - if (s) - *out_sound = strdup(s); + if (s) { + *out_keyboard_sound = strdup(s); + *out_mouse_sound = strdup("ping"); // default mouse sound + } } if (json_object_object_get_ex(root, "volume", &o)) { - *out_volume = json_object_get_int(o); + int legacy_volume = json_object_get_int(o); + *out_keyboard_volume = legacy_volume; + *out_mouse_volume = legacy_volume; + } } json_object_put(root); return 1; } int main(int argc, char *argv[]) { - char *sound_name = "eg-oreo"; // may be replaced by strdup; track for free - int sound_name_owned = 0; + char *sound_name = strdup("eg-oreo"); // may be replaced by strdup; track for free + char *mouse_sound_name = strdup("ping"); // may be replaced by strdup; track for free + int sound_name_owned = 1; + int mouse_sound_name_owned = 1; int verbose = 0; int list_sounds = 0; int flag_daemon = 0; int flag_stop = 0; - static struct option long_options[] = {{"sound", required_argument, 0, 'S'}, - {"volume", required_argument, 0, 'V'}, - {"list", no_argument, 0, 'l'}, - {"daemon", no_argument, 0, 'd'}, - {"stop", no_argument, 0, 's'}, - {"mute", no_argument, 0, 'm'}, - {"unmute", no_argument, 0, 'u'}, - {"help", no_argument, 0, 'h'}, - {"verbose", no_argument, 0, 'v'}, - {0, 0, 0, 0}}; + static struct option long_options[] = { + {"sound", required_argument, 0, 'S'}, + {"mouse", required_argument, 0, 'M'}, + {"volume", required_argument, 0, 'V'}, + {"keyboard-volume", required_argument, 0, 'K'}, + {"mouse-volume", required_argument, 0, 'O'}, + {"list", no_argument, 0, 'l'}, + {"daemon", no_argument, 0, 'd'}, + {"stop", no_argument, 0, 's'}, + {"mute", no_argument, 0, 'm'}, + {"unmute", no_argument, 0, 'u'}, + {"help", no_argument, 0, 'h'}, + {"verbose", no_argument, 0, 'v'}, + {0, 0, 0, 0}}; int volume = 50; char *cli_sound = NULL; + char *cli_mouse_sound = NULL; int cli_volume = -1; + int cli_keyboard_volume = -1; + int cli_mouse_volume = -1; 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) { - char *cfg_sound = NULL; - int cfg_volume = volume; - if (read_user_config(user_cfg_path, &cfg_sound, &cfg_volume)) { - if (cfg_sound) { - sound_name = cfg_sound; // now owned, must free on exit paths + char *cfg_keyboard_sound = NULL; + char *cfg_mouse_sound = NULL; + int cfg_keyboard_volume = volume; + int cfg_mouse_volume = volume; + if (read_user_config(user_cfg_path, &cfg_keyboard_sound, &cfg_mouse_sound, &cfg_keyboard_volume, &cfg_mouse_volume)) { + if (cfg_keyboard_sound) { + if (sound_name_owned) { free(sound_name); } + sound_name = cfg_keyboard_sound; // now owned, must free on exit paths sound_name_owned = 1; } - volume = cfg_volume; + if (cfg_mouse_sound) { + if (mouse_sound_name_owned) { free(mouse_sound_name); } + mouse_sound_name = cfg_mouse_sound; // now owned, must free on exit paths + mouse_sound_name_owned = 1; + } + volume = cfg_keyboard_volume; // Use keyboard volume as main volume for CLI compatibility } } } for (int i = 1; i < argc; i++) { if (argv[i][0] == '-' && argv[i][1] == '-') { - const char *a = argv[i] + 2; - const char *e = strchr(a, '='); - size_t n = e ? (size_t)(e - a) : strlen(a); - if (!((n == 5 && strncmp(a, "sound", 5) == 0) || - (n == 6 && strncmp(a, "volume", 6) == 0) || - (n == 4 && strncmp(a, "list", 4) == 0) || - (n == 6 && strncmp(a, "daemon", 6) == 0) || - (n == 4 && strncmp(a, "stop", 4) == 0) || - (n == 4 && strncmp(a, "mute", 4) == 0) || - (n == 6 && strncmp(a, "unmute", 6) == 0) || - (n == 4 && strncmp(a, "help", 4) == 0) || - (n == 7 && strncmp(a, "verbose", 7) == 0))) { - fprintf(stderr, "Unknown option: %s (use full option name)\n", argv[i]); - return 1; - } + const char *a = argv[i] + 2; const char *e = strchr(a, '='); size_t n = e ? (size_t)(e - a) : strlen(a); + if (!((n==5 && strncmp(a, "sound", 5)==0) || (n==5 && strncmp(a, "mouse", 5)==0) || (n==6 && strncmp(a, "volume", 6)==0) || (n==4 && strncmp(a, "list", 4)==0) || (n==6 && strncmp(a, "daemon", 6)==0) || (n==4 && strncmp(a, "stop", 4)==0) || (n==4 && strncmp(a, "mute", 4)==0) || (n==6 && strncmp(a, "unmute", 6)==0) || (n==4 && strncmp(a, "help", 4)==0) || (n==7 && strncmp(a, "verbose", 7)==0))) { fprintf(stderr, "Unknown option: %s (use full option name)\n", argv[i]); return 1; } } } int opt; - while ((opt = getopt_long(argc, argv, "S:V:lhdsmuv", long_options, NULL)) != + while ((opt = getopt_long(argc, argv, "S:M:V:K:O:lhdsmuv", long_options, NULL)) != -1) { switch (opt) { case 'd': @@ -528,37 +700,26 @@ int main(int argc, char *argv[]) { break; case 'm': { pid_t running_pid = 0; - if (!require_running_pid(&running_pid)) { - if (sound_name_owned) { - free(sound_name); - } - return 1; - } + if (!require_running_pid(&running_pid)) { if (sound_name_owned) { free(sound_name); } return 1; } write_mute_state(1); printf("KeyVibe muted.\n"); - if (sound_name_owned) { - free(sound_name); - } + if (sound_name_owned) { free(sound_name); } return 0; } case 'u': { pid_t running_pid = 0; - if (!require_running_pid(&running_pid)) { - if (sound_name_owned) { - free(sound_name); - } - return 1; - } + if (!require_running_pid(&running_pid)) { if (sound_name_owned) { free(sound_name); } return 1; } write_mute_state(0); printf("KeyVibe unmuted.\n"); - if (sound_name_owned) { - free(sound_name); - } + if (sound_name_owned) { free(sound_name); } return 0; } case 'S': cli_sound = optarg; break; + case 'M': + cli_mouse_sound = optarg; + break; case 'V': cli_volume = atoi(optarg); if (cli_volume < 0) @@ -566,41 +727,47 @@ int main(int argc, char *argv[]) { if (cli_volume > 100) cli_volume = 100; break; + case 'K': + cli_keyboard_volume = atoi(optarg); + if (cli_keyboard_volume < 0) + cli_keyboard_volume = 0; + if (cli_keyboard_volume > 100) + cli_keyboard_volume = 100; + break; + case 'O': + cli_mouse_volume = atoi(optarg); + if (cli_mouse_volume < 0) + cli_mouse_volume = 0; + if (cli_mouse_volume > 100) + cli_mouse_volume = 100; + break; case 'l': list_sounds = 1; break; /* removed legacy long mute/unmute cases; use -m/-u or --mute/--unmute */ case 'h': print_usage(argv[0]); - if (sound_name_owned) { - free(sound_name); - } + if (sound_name_owned) { free(sound_name); } return 0; case 'v': verbose = 1; break; default: print_usage(argv[0]); - if (sound_name_owned) { - free(sound_name); - } + if (sound_name_owned) { free(sound_name); } return 1; } } if (list_sounds) { int rc = list_sound_packs(); - if (sound_name_owned) { - free(sound_name); - } + if (sound_name_owned) { free(sound_name); } return rc; } build_pidfile_path(pidfile_path, sizeof(pidfile_path)); if (flag_stop) { pid_t running_pid = 0; - if (!require_running_pid(&running_pid)) - return 1; - if (kill(running_pid, SIGTERM) != 0) - return (perror("kill"), 1); + if (!require_running_pid(&running_pid)) return 1; + if (kill(running_pid, SIGTERM) != 0) return (perror("kill"), 1); for (int i = 0; i < 30; i++) { if (!process_is_running(running_pid)) { unlink(pidfile_path); @@ -620,40 +787,57 @@ int main(int argc, char *argv[]) { if (sound_name_owned) { free(sound_name); } - sound_name = cli_sound; + sound_name = cli_sound; sound_name_owned = 0; // cli_sound is not owned by us config_updated = 1; } - if (cli_volume >= 0) { - volume = cli_volume; + if (cli_mouse_sound != NULL) { + if (mouse_sound_name_owned) { + free(mouse_sound_name); + } + mouse_sound_name = cli_mouse_sound; + mouse_sound_name_owned = 0; // cli_mouse_sound is not owned by us config_updated = 1; } - + if (cli_volume >= 0) { + volume = cli_volume; + config_updated = 1; + } + if (cli_keyboard_volume >= 0) { + current_keyboard_volume = cli_keyboard_volume; + config_updated = 1; + } + if (cli_mouse_volume >= 0) { + current_mouse_volume = cli_mouse_volume; + config_updated = 1; + } + // Update config file if CLI options were used - if (config_updated && - get_user_config_path(user_cfg_path, sizeof(user_cfg_path))) { - if (!write_user_config(user_cfg_path, sound_name, volume)) { - fprintf(stderr, "Warning: Failed to update config file %s\n", - user_cfg_path); + 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)) { + fprintf(stderr, "Warning: Failed to update config file %s\n", user_cfg_path); } else if (verbose) { fprintf(stderr, "Updated config file %s\n", user_cfg_path); } } // Create default config file if it doesn't exist and no CLI options were used - if (!config_updated && - get_user_config_path(user_cfg_path, sizeof(user_cfg_path))) { + if (!config_updated && get_user_config_path(user_cfg_path, sizeof(user_cfg_path))) { if (access(user_cfg_path, F_OK) != 0) { - if (!write_user_config(user_cfg_path, sound_name, volume)) { + if (!write_user_config(user_cfg_path, sound_name, mouse_sound_name, current_keyboard_volume, current_mouse_volume)) { fprintf(stderr, "Warning: Failed to write %s\n", user_cfg_path); } else if (verbose) { fprintf(stderr, "Created default config %s\n", user_cfg_path); } } } - if (!validate_sound_pack(sound_name)) { - if (sound_name_owned) { - free(sound_name); - } + if (!validate_keyboard_sound_pack(sound_name)) { + if (sound_name_owned) { free(sound_name); } + if (mouse_sound_name_owned) { free(mouse_sound_name); } + return 1; + } + if (!validate_mouse_sound_pack(mouse_sound_name)) { + if (sound_name_owned) { free(sound_name); } + if (mouse_sound_name_owned) { free(mouse_sound_name); } return 1; } char get_key_presses_path[MAX_PATH_LENGTH]; @@ -662,10 +846,8 @@ int main(int argc, char *argv[]) { "%s/keyvibe-input", KeyVibe_BIN_DIR); snprintf(sound_player_path, sizeof(sound_player_path), "%s/keyvibe-audio", KeyVibe_BIN_DIR); - if (access(get_key_presses_path, X_OK) != 0 || - access(sound_player_path, X_OK) != 0) - return errorf("Error: Cannot find or execute required binaries in %s\n", - KeyVibe_BIN_DIR); + if (access(get_key_presses_path, X_OK) != 0 || access(sound_player_path, X_OK) != 0) + return errorf("Error: Cannot find or execute required binaries in %s\n", KeyVibe_BIN_DIR); signal(SIGINT, cleanup_processes); signal(SIGTERM, cleanup_processes); if (flag_daemon) { @@ -682,34 +864,53 @@ int main(int argc, char *argv[]) { } char config_path[MAX_PATH_LENGTH]; char sound_dir[MAX_PATH_LENGTH]; - if (!build_paths_for_sound(sound_name, config_path, sizeof(config_path), + char mouse_config_path[MAX_PATH_LENGTH]; + char mouse_sound_dir[MAX_PATH_LENGTH]; + if (!build_paths_for_keyboard_sound(sound_name, config_path, sizeof(config_path), sound_dir, sizeof(sound_dir))) { + if (sound_name_owned) { free(sound_name); } + if (mouse_sound_name_owned) { free(mouse_sound_name); } return 1; } - current_volume = volume; + if (!build_paths_for_mouse_sound(mouse_sound_name, mouse_config_path, sizeof(mouse_config_path), + mouse_sound_dir, sizeof(mouse_sound_dir))) { + if (sound_name_owned) { free(sound_name); } + if (mouse_sound_name_owned) { free(mouse_sound_name); } + return 1; + } + current_keyboard_volume = volume; + current_mouse_volume = volume; current_verbose = verbose; strncpy(current_sound_name, sound_name, sizeof(current_sound_name) - 1); current_sound_name[sizeof(current_sound_name) - 1] = '\0'; + strncpy(current_mouse_sound_name, mouse_sound_name, sizeof(current_mouse_sound_name) - 1); + current_mouse_sound_name[sizeof(current_mouse_sound_name) - 1] = '\0'; strncpy(current_config_path, config_path, sizeof(current_config_path) - 1); current_config_path[sizeof(current_config_path) - 1] = '\0'; strncpy(current_sound_dir, sound_dir, sizeof(current_sound_dir) - 1); current_sound_dir[sizeof(current_sound_dir) - 1] = '\0'; + strncpy(current_mouse_config_path, mouse_config_path, sizeof(current_mouse_config_path) - 1); + current_mouse_config_path[sizeof(current_mouse_config_path) - 1] = '\0'; + strncpy(current_mouse_sound_dir, mouse_sound_dir, sizeof(current_mouse_sound_dir) - 1); + current_mouse_sound_dir[sizeof(current_mouse_sound_dir) - 1] = '\0'; if (verbose && !is_daemon) { printf("KeyVibe starting...\n"); - printf("Sound pack: %s\n", sound_name); - printf("Config file: %s\n", config_path); - printf("Working directory: %s\n", sound_dir); + 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"); } else { if (!is_daemon) { - printf("KeyVibe started with sound pack: %s\n", sound_name); + printf("KeyVibe started with keyboard sound pack: %s, mouse sound pack: %s\n", sound_name, mouse_sound_name); printf("Press Ctrl+C to exit.\n"); } } - if (!start_children(sound_dir, config_path, volume, verbose, current_mute)) { - if (sound_name_owned) { - free(sound_name); - } + if (!start_children(sound_dir, config_path, volume, verbose, current_mute, + mouse_sound_dir, mouse_config_path, current_mouse_volume)) { + if (sound_name_owned) { free(sound_name); } return 1; } int status; @@ -717,43 +918,119 @@ int main(int argc, char *argv[]) { signal(SIGHUP, handle_sighup); pthread_t inotify_thread; struct inotify_thread_args in_args; - if (is_daemon) { + // Enable live reloading for both daemon and non-daemon modes char user_cfg_path2[MAX_PATH_LENGTH]; if (get_user_config_path(user_cfg_path2, sizeof(user_cfg_path2))) { strncpy(in_args.path, user_cfg_path2, sizeof(in_args.path) - 1); in_args.path[sizeof(in_args.path) - 1] = '\0'; + printf("Setting up file watcher for: %s\n", in_args.path); + + // Check if the config file exists + if (access(in_args.path, F_OK) == 0) { pthread_create(&inotify_thread, NULL, inotify_thread_fn, &in_args); pthread_detach(inotify_thread); + } else { + printf("Warning: Config file %s does not exist, file watching disabled\n", in_args.path); } + } else { + printf("Warning: Could not get user config path for file watching\n"); } while (1) { if (reload_requested) { + printf("Reload requested, processing config changes...\n"); reload_requested = 0; + char user_cfg_path3[MAX_PATH_LENGTH]; if (get_user_config_path(user_cfg_path3, sizeof(user_cfg_path3))) { - char *cfg_sound = NULL; - int cfg_volume = current_volume; - if (read_user_config(user_cfg_path3, &cfg_sound, &cfg_volume)) { - if (cfg_sound) { - strncpy(current_sound_name, cfg_sound, - sizeof(current_sound_name) - 1); - free(cfg_sound); + char *cfg_keyboard_sound = NULL; + char *cfg_mouse_sound = NULL; + int cfg_keyboard_volume = current_keyboard_volume; + int cfg_mouse_volume = current_mouse_volume; + + if (read_user_config(user_cfg_path3, &cfg_keyboard_sound, &cfg_mouse_sound, &cfg_keyboard_volume, &cfg_mouse_volume)) { + printf("Read config - keyboard:%s mouse:%s kvol:%d mvol:%d (current kvol:%d mvol:%d)\n", + cfg_keyboard_sound ? cfg_keyboard_sound : "NULL", + cfg_mouse_sound ? cfg_mouse_sound : "NULL", + cfg_keyboard_volume, cfg_mouse_volume, current_keyboard_volume, current_mouse_volume); + + // Check what changed + int keyboard_changed = 0; + int mouse_changed = 0; + int keyboard_volume_changed = 0; + int mouse_volume_changed = 0; + + if (cfg_keyboard_sound && strcmp(current_sound_name, cfg_keyboard_sound) != 0) { + keyboard_changed = 1; + strncpy(current_sound_name, cfg_keyboard_sound, sizeof(current_sound_name) - 1); + current_sound_name[sizeof(current_sound_name) - 1] = '\0'; } - current_volume = cfg_volume; - } - } - if (validate_sound_pack(current_sound_name)) { - if (!build_paths_for_sound(current_sound_name, current_config_path, + free(cfg_keyboard_sound); + + if (cfg_mouse_sound && strcmp(current_mouse_sound_name, cfg_mouse_sound) != 0) { + mouse_changed = 1; + strncpy(current_mouse_sound_name, cfg_mouse_sound, sizeof(current_mouse_sound_name) - 1); + current_mouse_sound_name[sizeof(current_mouse_sound_name) - 1] = '\0'; + } + free(cfg_mouse_sound); + + if (cfg_keyboard_volume != current_keyboard_volume) { + keyboard_volume_changed = 1; + current_keyboard_volume = cfg_keyboard_volume; + } + + if (cfg_mouse_volume != current_mouse_volume) { + mouse_volume_changed = 1; + current_mouse_volume = cfg_mouse_volume; + } + + printf("Config changed - keyboard:%d mouse:%d kvol:%d mvol:%d\n", + keyboard_changed, mouse_changed, keyboard_volume_changed, mouse_volume_changed); + + // Handle the changes immediately + if (keyboard_changed || mouse_changed || keyboard_volume_changed || mouse_volume_changed) { + // Validate both sound packs + 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) { + // Update keyboard paths + if (build_paths_for_keyboard_sound(current_sound_name, current_config_path, sizeof(current_config_path), current_sound_dir, sizeof(current_sound_dir))) { - continue; - } + // Update mouse paths + if (build_paths_for_mouse_sound(current_mouse_sound_name, current_mouse_config_path, + sizeof(current_mouse_config_path), + current_mouse_sound_dir, + sizeof(current_mouse_sound_dir))) { + // Restart children with new config (including volume changes) stop_children(); - start_children(current_sound_dir, current_config_path, current_volume, - current_verbose, current_mute); + 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); + printf("Reloaded successfully: keyboard=%s, mouse=%s, kvol=%d, mvol=%d\n", + current_sound_name, current_mouse_sound_name, current_keyboard_volume, current_mouse_volume); + } else { + printf("Failed to build mouse sound paths\n"); + } + } else { + printf("Failed to build keyboard sound paths\n"); + } + } else { + if (!keyboard_valid) { + printf("Reload failed: invalid keyboard sound pack '%s'\n", current_sound_name); + } + if (!mouse_valid) { + printf("Reload failed: invalid mouse sound pack '%s'\n", current_mouse_sound_name); + } + } + } + } else { + printf("Failed to read user config\n"); + } + } else { + printf("Failed to get user config path\n"); } - continue; } finished_pid = waitpid(-1, &status, WNOHANG); if (finished_pid == 0) { @@ -786,8 +1063,7 @@ int main(int argc, char *argv[]) { if (!is_daemon) { printf("KeyVibe exited.\n"); } - if (sound_name_owned) { - free(sound_name); - } + if (sound_name_owned) { free(sound_name); } + if (mouse_sound_name_owned) { free(mouse_sound_name); } return 0; } diff --git a/src/utils.c b/src/utils.c index 39a25a5..387c4d2 100644 --- a/src/utils.c +++ b/src/utils.c @@ -1,17 +1,16 @@ #define _POSIX_C_SOURCE 200809L -#include "utils.h" -#include "config.h" -#include -#include #include #include -#include -#include #include +#include +#include +#include +#include +#include "utils.h" +#include "config.h" int safe_snprintf(char *dst, size_t dst_sz, const char *fmt, ...) { - if (!dst || dst_sz == 0) - return 0; + if (!dst || dst_sz == 0) return 0; va_list ap; va_start(ap, fmt); int n = vsnprintf(dst, dst_sz, fmt, ap); @@ -67,3 +66,5 @@ int process_is_running(pid_t pid) { return 0; return kill(pid, 0) == 0; } + +