From bae912ee92b180c3c8f30b291e4fdedd21c64d78 Mon Sep 17 00:00:00 2001 From: Don Freiday Date: Thu, 12 Dec 2024 19:59:16 -0500 Subject: [PATCH 1/3] Implement wireplumber library that shells out to pw-dump and wpctl --- common/wireplumber.c | 257 +++++++++++++++++++++++++++++++++++++++++++ common/wireplumber.h | 13 +++ 2 files changed, 270 insertions(+) create mode 100644 common/wireplumber.c create mode 100644 common/wireplumber.h diff --git a/common/wireplumber.c b/common/wireplumber.c new file mode 100644 index 00000000..32edd78f --- /dev/null +++ b/common/wireplumber.c @@ -0,0 +1,257 @@ +#include +#include +#include +#include +#include +#include "json/json.h" +#include "wireplumber.h" + +#ifdef DEBUG +#define DEBUG_PRINT(fmt, ...) printf("[DEBUG] %s:%d: " fmt "\n", __FILE__, __LINE__, ##__VA_ARGS__) +#else +#define DEBUG_PRINT(fmt, ...) +#endif + +static char *execute_pw_dump(void); +static int parse_json_to_sinks(const char *json_output, Sink **sinks, int *count); +static Sink create_sink_from_json(struct json item); + +int get_sinks(Sink **sinks, int *count) { + printf("Executing pw-dump command to retrieve sinks...\n"); + + FILE *fp = popen("pw-dump", "r"); + if (!fp) { + perror("Failed to run pw-dump"); + return -1; + } + + char *json_output = NULL; + size_t json_size = 0; + char buffer[512]; + + while (fgets(buffer, sizeof(buffer), fp)) { + size_t line_length = strlen(buffer); + char *new_output = realloc(json_output, json_size + line_length + 1); + if (!new_output) { + perror("Failed to allocate memory for JSON buffer"); + free(json_output); + pclose(fp); + return -1; + } + json_output = new_output; + memcpy(json_output + json_size, buffer, line_length); + json_size += line_length; + json_output[json_size] = '\0'; + } + pclose(fp); + + if (!json_output) { + fprintf(stderr, "Failed to retrieve JSON output from pw-dump\n"); + return -1; + } + + int result = parse_json_to_sinks(json_output, sinks, count); + free(json_output); + + return result; +} + +static char *execute_pw_dump(void) { + FILE *fp = popen("pw-dump", "r"); + if (!fp) { + perror("Failed to run pw-dump"); + return NULL; + } + + size_t buffer_size = 4096; + char *json_output = malloc(buffer_size); + if (!json_output) { + perror("Failed to allocate memory for JSON buffer"); + pclose(fp); + return NULL; + } + + size_t json_size = 0; + char line[512]; + + while (fgets(line, sizeof(line), fp)) { + size_t line_length = strlen(line); + if (json_size + line_length + 1 > buffer_size) { + buffer_size *= 2; + char *new_output = realloc(json_output, buffer_size); + if (!new_output) { + perror("Failed to reallocate memory for JSON buffer"); + free(json_output); + pclose(fp); + return NULL; + } + json_output = new_output; + } + memcpy(json_output + json_size, line, line_length); + json_size += line_length; + json_output[json_size] = '\0'; + } + pclose(fp); + + DEBUG_PRINT("JSON output size: %zu bytes\n", json_size); + return json_output; +} + +static int parse_json_to_sinks(const char *json_output, Sink **sinks, int *count) { + struct json root = json_parse(json_output); + if (!json_exists(root) || json_type(root) != JSON_ARRAY) { + fprintf(stderr, "Failed to parse JSON or JSON is not an array\n"); + return -1; + } + + *sinks = NULL; + *count = 0; + + size_t total_items = json_array_count(root); + for (size_t i = 0; i < total_items; i++) { + struct json item = json_array_get(root, i); + if (json_type(item) != JSON_OBJECT) { + continue; + } + + struct json info = json_object_get(item, "info"); + if (!json_exists(info) || json_type(info) != JSON_OBJECT) { + continue; + } + + struct json props = json_object_get(info, "props"); + if (!json_exists(props) || json_type(props) != JSON_OBJECT) { + continue; + } + + struct json media_class = json_object_get(props, "media.class"); + if (!json_exists(media_class) || json_type(media_class) != JSON_STRING || + json_string_compare(media_class, "Audio/Sink") != 0) { + continue; + } + + struct json description = json_object_get(props, "node.description"); + struct json nick = json_object_get(props, "node.nick"); + struct json name = json_object_get(props, "node.name"); + struct json object_id = json_object_get(item, "id"); + + if (!json_exists(description) || !json_exists(object_id) || json_type(object_id) != JSON_NUMBER) { + continue; + } + + Sink sink = {0}; + sink.id = json_int(object_id); + json_string_copy(description, sink.description, sizeof(sink.description)); + if (json_exists(nick)) { + json_string_copy(nick, sink.nick, sizeof(sink.nick)); + } else { + strncpy(sink.nick, "Unknown", sizeof(sink.nick)); + } + if (json_exists(name)) { + json_string_copy(name, sink.name, sizeof(sink.name)); + } else { + strncpy(sink.name, "Unknown", sizeof(sink.name)); + } + + // Allocate memory for the new sink + Sink *new_sinks = realloc(*sinks, (*count + 1) * sizeof(Sink)); + if (!new_sinks) { + perror("Failed to allocate memory for sinks array"); + free(*sinks); + return -1; + } + + *sinks = new_sinks; + (*sinks)[*count] = sink; + (*count)++; + } + + return 0; +} + +static Sink create_sink_from_json(struct json item) { + Sink sink; + sink.id = -1; + struct json info = json_object_get(item, "info"); + if (!json_exists(info)) { + return sink; + } + + struct json props = json_object_get(info, "props"); + if (!json_exists(props)) { + return sink; + } + + struct json media_class = json_object_get(props, "media.class"); + struct json description = json_object_get(props, "node.description"); + struct json object_id = json_object_get(item, "id"); + + if (!json_exists(media_class) || json_string_compare(media_class, "Audio/Sink") != 0) { + return sink; + } + + if (!json_exists(description) || !json_exists(object_id)) { + return sink; + } + + json_string_copy(description, sink.description, sizeof(sink.description)); + + struct json nick = json_object_get(props, "node.nick"); + struct json name = json_object_get(props, "node.name"); + if (json_exists(nick)) { + json_string_copy(nick, sink.nick, sizeof(sink.nick)); + } else { + strcpy(sink.nick, "Unknown"); + } + + if (json_exists(name)) { + json_string_copy(name, sink.name, sizeof(sink.name)); + } else { + strcpy(sink.name, "Unknown"); + } + + sink.id = json_int(object_id); + + const char *raw_json = json_raw(item); + size_t raw_length = json_raw_length(item); + if (raw_json && raw_length > 0) { + DEBUG_PRINT("Full JSON for sink: %.*s\n", (int)raw_length, raw_json); + } + + DEBUG_PRINT("Found sink: ID=%d, Description=%s, Nick=%s, Name=%s\n", sink.id, sink.description, sink.nick, sink.name); + return sink; +} + +int get_default_sink_id(int *sink_id) { + const char* command = "wpctl status | grep -A 5 Sinks | grep '\\*' | sed 's/ \\|.*\\* //' | cut -f1 -d'.'"; + FILE *fp = popen(command, "r"); + if (fp == NULL) { + perror("Failed to run command"); + return -1; + } + + char buffer[128]; + if (fgets(buffer, sizeof(buffer), fp) != NULL) { + buffer[strcspn(buffer, "\n")] = '\0'; + *sink_id = atoi(buffer); + } else { + perror("Failed to read sink ID"); + pclose(fp); + return -1; + } + + pclose(fp); + return 0; +} + +int set_default_sink(int sink_id) { + char command[256]; + snprintf(command, sizeof(command), "wpctl set-default %d", sink_id); + DEBUG_PRINT("Executing command: %s\n", command); + int result = system(command); + if (result != 0) { + perror("Failed to execute command"); + } + DEBUG_PRINT("Command result: %d\n", result); + return result; +} \ No newline at end of file diff --git a/common/wireplumber.h b/common/wireplumber.h new file mode 100644 index 00000000..f9a17010 --- /dev/null +++ b/common/wireplumber.h @@ -0,0 +1,13 @@ +#pragma once + +typedef struct +{ + int id; + char description[256]; + char nick[256]; + char name[256]; +} Sink; + +int get_sinks(Sink** sinks, int* count); +int get_default_sink_id(int *sink_id); +int set_default_sink(int sink_id); From 5b5fff22582486a416bb57bc9074ed525ab92c2d Mon Sep 17 00:00:00 2001 From: Don Freiday Date: Thu, 12 Dec 2024 20:04:32 -0500 Subject: [PATCH 2/3] Add audio sink selection dropdown to general tweaks --- common/wireplumber.c | 17 +++---- module/muxtweakgen.c | 102 +++++++++++++++++++++++++++++++++---- module/ui/ui_muxtweakgen.c | 8 +++ module/ui/ui_muxtweakgen.h | 4 ++ 4 files changed, 112 insertions(+), 19 deletions(-) diff --git a/common/wireplumber.c b/common/wireplumber.c index 32edd78f..fe32744d 100644 --- a/common/wireplumber.c +++ b/common/wireplumber.c @@ -6,11 +6,8 @@ #include "json/json.h" #include "wireplumber.h" -#ifdef DEBUG -#define DEBUG_PRINT(fmt, ...) printf("[DEBUG] %s:%d: " fmt "\n", __FILE__, __LINE__, ##__VA_ARGS__) -#else -#define DEBUG_PRINT(fmt, ...) -#endif +#include "common.h" +#include "log.h" static char *execute_pw_dump(void); static int parse_json_to_sinks(const char *json_output, Sink **sinks, int *count); @@ -93,7 +90,7 @@ static char *execute_pw_dump(void) { } pclose(fp); - DEBUG_PRINT("JSON output size: %zu bytes\n", json_size); + LOG_DEBUG(mux_module, "JSON output size: %zu bytes\n", json_size); return json_output; } @@ -215,10 +212,10 @@ static Sink create_sink_from_json(struct json item) { const char *raw_json = json_raw(item); size_t raw_length = json_raw_length(item); if (raw_json && raw_length > 0) { - DEBUG_PRINT("Full JSON for sink: %.*s\n", (int)raw_length, raw_json); + LOG_DEBUG(mux_module, "Full JSON for sink: %.*s\n", (int)raw_length, raw_json); } - DEBUG_PRINT("Found sink: ID=%d, Description=%s, Nick=%s, Name=%s\n", sink.id, sink.description, sink.nick, sink.name); + LOG_INFO(mux_module, "Found sink: ID=%d, Description=%s, Nick=%s, Name=%s\n", sink.id, sink.description, sink.nick, sink.name); return sink; } @@ -247,11 +244,11 @@ int get_default_sink_id(int *sink_id) { int set_default_sink(int sink_id) { char command[256]; snprintf(command, sizeof(command), "wpctl set-default %d", sink_id); - DEBUG_PRINT("Executing command: %s\n", command); + LOG_INFO(mux_module, "Executing command: %s\n", command); int result = system(command); if (result != 0) { perror("Failed to execute command"); } - DEBUG_PRINT("Command result: %d\n", result); + LOG_INFO(mux_module, "Command result: %d\n", result); return result; } \ No newline at end of file diff --git a/module/muxtweakgen.c b/module/muxtweakgen.c index ab31208f..cc58771d 100755 --- a/module/muxtweakgen.c +++ b/module/muxtweakgen.c @@ -8,6 +8,8 @@ #include #include #include +#include + #include "../common/common.h" #include "../common/options.h" #include "../common/theme.h" @@ -17,6 +19,7 @@ #include "../common/kiosk.h" #include "../common/input.h" #include "../common/input/list_nav.h" +#include "../common/wireplumber.h" char *mux_module; static int js_fd; @@ -45,14 +48,14 @@ lv_obj_t *kiosk_image = NULL; int progress_onscreen = -1; -int hidden_original, bgm_original, sound_original, startup_original, colour_original, brightness_original; +int hidden_original, bgm_original, sound_original, startup_original, colour_original, brightness_original, audio_sink_original; lv_group_t *ui_group; lv_group_t *ui_group_value; lv_group_t *ui_group_glyph; lv_group_t *ui_group_panel; -#define UI_COUNT 10 +#define UI_COUNT 11 lv_obj_t *ui_objects[UI_COUNT]; lv_obj_t *ui_mux_panels[5]; @@ -68,6 +71,7 @@ void show_help(lv_obj_t *element_focused) { "at the start of a file or folder name to hide it")}, {ui_lblBGM, TS("Toggle the background music of the frontend - This will stop if content is launched")}, {ui_lblSound, TS("Toggle the navigation sound of the frontend if the current theme supports it")}, + {ui_lblAudioSink, TS("Change the audio output sink")}, {ui_lblStartup, TS("Change where the device will start up into")}, {ui_lblColour, TS("Change the colour temperature of the display if the device supports it")}, {ui_lblBrightness, TS("Change the brightness of the device to a specific level")}, @@ -93,6 +97,69 @@ void show_help(lv_obj_t *element_focused) { TS(lv_label_get_text(element_focused)), message); } +void init_audio_sink_dropdown() { + LOG_DEBUG(mux_module, "Initializing audio sink dropdown...\n"); + + Sink *sinks = NULL; + int count = 0; + + if (get_sinks(&sinks, &count) != 0) { + LOG_ERROR(mux_module, "Failed to retrieve audio sinks.\n"); + return; + } + + lv_dropdown_clear_options(ui_droAudioSink); + + for (int i = 0; i < count; i++) { + char dropdown_option[128]; + snprintf(dropdown_option, sizeof(dropdown_option), "%d: %s", sinks[i].id, sinks[i].description); + LOG_DEBUG(mux_module, "Adding sink to dropdown: %s\n", dropdown_option); + lv_dropdown_add_option(ui_droAudioSink, dropdown_option, LV_DROPDOWN_POS_LAST); + } + + int default_sink_id = -1; + if (get_default_sink_id(&default_sink_id) == 0) { + LOG_DEBUG(mux_module, "Default sink ID: %d\n", default_sink_id); + for (int i = 0; i < count; i++) { + if (sinks[i].id == default_sink_id) { + audio_sink_original = i; + LOG_DEBUG(mux_module, "Setting default sink to index: %d\n", i); + lv_dropdown_set_selected(ui_droAudioSink, i); + break; + } + } + } else { + LOG_ERROR(mux_module, "Failed to retrieve default sink ID.\n"); + } + + free(sinks); +} + +static void update_default_sink(int selected_index) { + Sink *sinks = NULL; + int count = 0; + + if (get_sinks(&sinks, &count) != 0) { + fprintf(stderr, "Failed to retrieve sinks.\n"); + return; + } + + if (selected_index >= 0 && selected_index < count) { + int sink_id = sinks[selected_index].id; + LOG_DEBUG(mux_module, "Setting default sink ID: %d\n", sink_id); + + if (set_default_sink(sink_id) == 0) { + LOG_DEBUG(mux_module, "Successfully set default sink.\n"); + } else { + LOG_ERROR(mux_module, "Failed to set default sink.\n"); + } + } else { + LOG_ERROR(mux_module, "Invalid selected audio sink index: %d\n", selected_index); + } + + free(sinks); +} + static void dropdown_event_handler(lv_event_t *e) { lv_event_code_t code = lv_event_get_code(e); lv_obj_t * obj = lv_event_get_target(e); @@ -108,6 +175,7 @@ void elements_events_init() { ui_droHidden, ui_droBGM, ui_droSound, + ui_droAudioSink, ui_droStartup, ui_droColour, ui_droBrightness @@ -125,6 +193,7 @@ void init_dropdown_settings() { startup_original = lv_dropdown_get_selected(ui_droStartup); colour_original = lv_dropdown_get_selected(ui_droColour); brightness_original = lv_dropdown_get_selected(ui_droBrightness); + init_audio_sink_dropdown(); } void restore_tweak_options() { @@ -199,6 +268,12 @@ void save_tweak_options() { write_text_to_file("/run/muos/global/settings/general/sound", "w", INT, idx_sound); } + int idx_audio_sink = lv_dropdown_get_selected(ui_droAudioSink); + if (idx_audio_sink != audio_sink_original) { + is_modified++; + update_default_sink(idx_audio_sink); + } + if (lv_dropdown_get_selected(ui_droStartup) != startup_original) { is_modified++; write_text_to_file("/run/muos/global/settings/general/startup", "w", CHAR, idx_startup); @@ -226,6 +301,7 @@ void init_navigation_groups() { ui_pnlHidden, ui_pnlBGM, ui_pnlSound, + ui_pnlAudioSink, ui_pnlStartup, ui_pnlColour, ui_pnlBrightness, @@ -238,18 +314,20 @@ void init_navigation_groups() { ui_objects[0] = ui_lblHidden; ui_objects[1] = ui_lblBGM; ui_objects[2] = ui_lblSound; - ui_objects[3] = ui_lblStartup; - ui_objects[4] = ui_lblColour; - ui_objects[5] = ui_lblBrightness; - ui_objects[6] = ui_lblHDMI; - ui_objects[7] = ui_lblPower; - ui_objects[8] = ui_lblInterface; - ui_objects[9] = ui_lblAdvanced; + ui_objects[3] = ui_lblAudioSink; + ui_objects[4] = ui_lblStartup; + ui_objects[5] = ui_lblColour; + ui_objects[6] = ui_lblBrightness; + ui_objects[7] = ui_lblHDMI; + ui_objects[8] = ui_lblPower; + ui_objects[9] = ui_lblInterface; + ui_objects[10] = ui_lblAdvanced; lv_obj_t *ui_objects_value[] = { ui_droHidden, ui_droBGM, ui_droSound, + ui_droAudioSink, ui_droStartup, ui_droColour, ui_droBrightness, @@ -263,6 +341,7 @@ void init_navigation_groups() { ui_icoHidden, ui_icoBGM, ui_icoSound, + ui_icoAudioSink, ui_icoStartup, ui_icoColour, ui_icoBrightness, @@ -275,6 +354,7 @@ void init_navigation_groups() { apply_theme_list_panel(&theme, &device, ui_pnlHidden); apply_theme_list_panel(&theme, &device, ui_pnlBGM); apply_theme_list_panel(&theme, &device, ui_pnlSound); + apply_theme_list_panel(&theme, &device, ui_pnlAudioSink); apply_theme_list_panel(&theme, &device, ui_pnlStartup); apply_theme_list_panel(&theme, &device, ui_pnlColour); apply_theme_list_panel(&theme, &device, ui_pnlBrightness); @@ -286,6 +366,7 @@ void init_navigation_groups() { apply_theme_list_item(&theme, ui_lblHidden, TS("Show Hidden Content"), false, true); apply_theme_list_item(&theme, ui_lblBGM, TS("Background Music"), false, true); apply_theme_list_item(&theme, ui_lblSound, TS("Navigation Sound"), false, true); + apply_theme_list_item(&theme, ui_lblAudioSink, TS("Audio Sink"), false, true); apply_theme_list_item(&theme, ui_lblStartup, TS("Device Startup"), false, true); apply_theme_list_item(&theme, ui_lblColour, TS("Colour Temperature"), false, true); apply_theme_list_item(&theme, ui_lblBrightness, TS("Brightness"), false, true); @@ -297,6 +378,7 @@ void init_navigation_groups() { apply_theme_list_glyph(&theme, ui_icoHidden, mux_module, "hidden"); apply_theme_list_glyph(&theme, ui_icoBGM, mux_module, "bgm"); apply_theme_list_glyph(&theme, ui_icoSound, mux_module, "sound"); + apply_theme_list_glyph(&theme, ui_icoAudioSink, mux_module, "audiosink"); apply_theme_list_glyph(&theme, ui_icoStartup, mux_module, "startup"); apply_theme_list_glyph(&theme, ui_icoColour, mux_module, "colour"); apply_theme_list_glyph(&theme, ui_icoBrightness, mux_module, "brightness"); @@ -308,6 +390,7 @@ void init_navigation_groups() { apply_theme_list_drop_down(&theme, ui_droHidden, NULL); apply_theme_list_drop_down(&theme, ui_droBGM, NULL); apply_theme_list_drop_down(&theme, ui_droSound, NULL); + apply_theme_list_drop_down(&theme, ui_droAudioSink, NULL); apply_theme_list_drop_down(&theme, ui_droStartup, NULL); apply_theme_list_drop_down(&theme, ui_droColour, NULL); @@ -500,6 +583,7 @@ void init_elements() { lv_obj_set_user_data(ui_lblHidden, "hidden"); lv_obj_set_user_data(ui_lblBGM, "bgm"); lv_obj_set_user_data(ui_lblSound, "sound"); + lv_obj_set_user_data(ui_lblAudioSink, "audiosink"); lv_obj_set_user_data(ui_lblStartup, "startup"); lv_obj_set_user_data(ui_lblColour, "colour"); lv_obj_set_user_data(ui_lblBrightness, "brightness"); diff --git a/module/ui/ui_muxtweakgen.c b/module/ui/ui_muxtweakgen.c index 7c60e421..75bbe7e6 100755 --- a/module/ui/ui_muxtweakgen.c +++ b/module/ui/ui_muxtweakgen.c @@ -3,6 +3,7 @@ lv_obj_t *ui_pnlHidden; lv_obj_t *ui_pnlBGM; lv_obj_t *ui_pnlSound; +lv_obj_t *ui_pnlAudioSink; lv_obj_t *ui_pnlStartup; lv_obj_t *ui_pnlColour; lv_obj_t *ui_pnlBrightness; @@ -14,6 +15,7 @@ lv_obj_t *ui_pnlAdvanced; lv_obj_t *ui_lblHidden; lv_obj_t *ui_lblBGM; lv_obj_t *ui_lblSound; +lv_obj_t *ui_lblAudioSink; lv_obj_t *ui_lblStartup; lv_obj_t *ui_lblColour; lv_obj_t *ui_lblBrightness; @@ -25,6 +27,7 @@ lv_obj_t *ui_lblAdvanced; lv_obj_t *ui_icoHidden; lv_obj_t *ui_icoBGM; lv_obj_t *ui_icoSound; +lv_obj_t *ui_icoAudioSink; lv_obj_t *ui_icoStartup; lv_obj_t *ui_icoColour; lv_obj_t *ui_icoBrightness; @@ -36,6 +39,7 @@ lv_obj_t *ui_icoAdvanced; lv_obj_t *ui_droHidden; lv_obj_t *ui_droBGM; lv_obj_t *ui_droSound; +lv_obj_t *ui_droAudioSink; lv_obj_t *ui_droStartup; lv_obj_t *ui_droColour; lv_obj_t *ui_droBrightness; @@ -48,6 +52,7 @@ void ui_init(lv_obj_t *ui_pnlContent) { ui_pnlHidden = lv_obj_create(ui_pnlContent); ui_pnlBGM = lv_obj_create(ui_pnlContent); ui_pnlSound = lv_obj_create(ui_pnlContent); + ui_pnlAudioSink = lv_obj_create(ui_pnlContent); ui_pnlStartup = lv_obj_create(ui_pnlContent); ui_pnlColour = lv_obj_create(ui_pnlContent); ui_pnlBrightness = lv_obj_create(ui_pnlContent); @@ -59,6 +64,7 @@ void ui_init(lv_obj_t *ui_pnlContent) { ui_lblHidden = lv_label_create(ui_pnlHidden); ui_lblBGM = lv_label_create(ui_pnlBGM); ui_lblSound = lv_label_create(ui_pnlSound); + ui_lblAudioSink = lv_label_create(ui_pnlAudioSink); ui_lblStartup = lv_label_create(ui_pnlStartup); ui_lblColour = lv_label_create(ui_pnlColour); ui_lblBrightness = lv_label_create(ui_pnlBrightness); @@ -70,6 +76,7 @@ void ui_init(lv_obj_t *ui_pnlContent) { ui_icoHidden = lv_img_create(ui_pnlHidden); ui_icoBGM = lv_img_create(ui_pnlBGM); ui_icoSound = lv_img_create(ui_pnlSound); + ui_icoAudioSink = lv_img_create(ui_pnlAudioSink); ui_icoStartup = lv_img_create(ui_pnlStartup); ui_icoColour = lv_img_create(ui_pnlColour); ui_icoBrightness = lv_img_create(ui_pnlBrightness); @@ -81,6 +88,7 @@ void ui_init(lv_obj_t *ui_pnlContent) { ui_droHidden = lv_dropdown_create(ui_pnlHidden); ui_droBGM = lv_dropdown_create(ui_pnlBGM); ui_droSound = lv_dropdown_create(ui_pnlSound); + ui_droAudioSink = lv_dropdown_create(ui_pnlAudioSink); ui_droStartup = lv_dropdown_create(ui_pnlStartup); ui_droColour = lv_dropdown_create(ui_pnlColour); ui_droBrightness = lv_dropdown_create(ui_pnlBrightness); diff --git a/module/ui/ui_muxtweakgen.h b/module/ui/ui_muxtweakgen.h index 90650f64..4ef291d8 100755 --- a/module/ui/ui_muxtweakgen.h +++ b/module/ui/ui_muxtweakgen.h @@ -7,6 +7,7 @@ void ui_init(lv_obj_t *ui_pnlContent); extern lv_obj_t *ui_pnlHidden; extern lv_obj_t *ui_pnlBGM; extern lv_obj_t *ui_pnlSound; +extern lv_obj_t *ui_pnlAudioSink; extern lv_obj_t *ui_pnlStartup; extern lv_obj_t *ui_pnlColour; extern lv_obj_t *ui_pnlBrightness; @@ -18,6 +19,7 @@ extern lv_obj_t *ui_pnlAdvanced; extern lv_obj_t *ui_lblHidden; extern lv_obj_t *ui_lblBGM; extern lv_obj_t *ui_lblSound; +extern lv_obj_t *ui_lblAudioSink; extern lv_obj_t *ui_lblStartup; extern lv_obj_t *ui_lblColour; extern lv_obj_t *ui_lblBrightness; @@ -29,6 +31,7 @@ extern lv_obj_t *ui_lblAdvanced; extern lv_obj_t *ui_icoHidden; extern lv_obj_t *ui_icoBGM; extern lv_obj_t *ui_icoSound; +extern lv_obj_t *ui_icoAudioSink; extern lv_obj_t *ui_icoStartup; extern lv_obj_t *ui_icoColour; extern lv_obj_t *ui_icoBrightness; @@ -40,6 +43,7 @@ extern lv_obj_t *ui_icoAdvanced; extern lv_obj_t *ui_droHidden; extern lv_obj_t *ui_droBGM; extern lv_obj_t *ui_droSound; +extern lv_obj_t *ui_droAudioSink; extern lv_obj_t *ui_droStartup; extern lv_obj_t *ui_droColour; extern lv_obj_t *ui_droBrightness; From 366609ccb943463249eb288f349f24bb1e74a423 Mon Sep 17 00:00:00 2001 From: Don Freiday Date: Sun, 15 Dec 2024 13:20:18 -0500 Subject: [PATCH 3/3] Cleanup and apply PR feedback --- common/wireplumber.c | 114 ++++--------------------------------------- module/muxtweakgen.c | 24 ++++----- 2 files changed, 22 insertions(+), 116 deletions(-) diff --git a/common/wireplumber.c b/common/wireplumber.c index fe32744d..41fae769 100644 --- a/common/wireplumber.c +++ b/common/wireplumber.c @@ -6,19 +6,19 @@ #include "json/json.h" #include "wireplumber.h" +#include + #include "common.h" #include "log.h" -static char *execute_pw_dump(void); static int parse_json_to_sinks(const char *json_output, Sink **sinks, int *count); -static Sink create_sink_from_json(struct json item); int get_sinks(Sink **sinks, int *count) { printf("Executing pw-dump command to retrieve sinks...\n"); FILE *fp = popen("pw-dump", "r"); if (!fp) { - perror("Failed to run pw-dump"); + LOG_ERROR(mux_module, "Failed to run pw-dump: %s", strerror(errno)); return -1; } @@ -30,7 +30,7 @@ int get_sinks(Sink **sinks, int *count) { size_t line_length = strlen(buffer); char *new_output = realloc(json_output, json_size + line_length + 1); if (!new_output) { - perror("Failed to allocate memory for JSON buffer"); + LOG_ERROR(mux_module, "Failed to allocate memory for JSON buffer: %s", strerror(errno)); free(json_output); pclose(fp); return -1; @@ -53,47 +53,6 @@ int get_sinks(Sink **sinks, int *count) { return result; } -static char *execute_pw_dump(void) { - FILE *fp = popen("pw-dump", "r"); - if (!fp) { - perror("Failed to run pw-dump"); - return NULL; - } - - size_t buffer_size = 4096; - char *json_output = malloc(buffer_size); - if (!json_output) { - perror("Failed to allocate memory for JSON buffer"); - pclose(fp); - return NULL; - } - - size_t json_size = 0; - char line[512]; - - while (fgets(line, sizeof(line), fp)) { - size_t line_length = strlen(line); - if (json_size + line_length + 1 > buffer_size) { - buffer_size *= 2; - char *new_output = realloc(json_output, buffer_size); - if (!new_output) { - perror("Failed to reallocate memory for JSON buffer"); - free(json_output); - pclose(fp); - return NULL; - } - json_output = new_output; - } - memcpy(json_output + json_size, line, line_length); - json_size += line_length; - json_output[json_size] = '\0'; - } - pclose(fp); - - LOG_DEBUG(mux_module, "JSON output size: %zu bytes\n", json_size); - return json_output; -} - static int parse_json_to_sinks(const char *json_output, Sink **sinks, int *count) { struct json root = json_parse(json_output); if (!json_exists(root) || json_type(root) != JSON_ARRAY) { @@ -153,7 +112,7 @@ static int parse_json_to_sinks(const char *json_output, Sink **sinks, int *count // Allocate memory for the new sink Sink *new_sinks = realloc(*sinks, (*count + 1) * sizeof(Sink)); if (!new_sinks) { - perror("Failed to allocate memory for sinks array"); + LOG_ERROR(mux_module, "Failed to allocate memory for sinks array: %s", strerror(errno)); free(*sinks); return -1; } @@ -166,64 +125,11 @@ static int parse_json_to_sinks(const char *json_output, Sink **sinks, int *count return 0; } -static Sink create_sink_from_json(struct json item) { - Sink sink; - sink.id = -1; - struct json info = json_object_get(item, "info"); - if (!json_exists(info)) { - return sink; - } - - struct json props = json_object_get(info, "props"); - if (!json_exists(props)) { - return sink; - } - - struct json media_class = json_object_get(props, "media.class"); - struct json description = json_object_get(props, "node.description"); - struct json object_id = json_object_get(item, "id"); - - if (!json_exists(media_class) || json_string_compare(media_class, "Audio/Sink") != 0) { - return sink; - } - - if (!json_exists(description) || !json_exists(object_id)) { - return sink; - } - - json_string_copy(description, sink.description, sizeof(sink.description)); - - struct json nick = json_object_get(props, "node.nick"); - struct json name = json_object_get(props, "node.name"); - if (json_exists(nick)) { - json_string_copy(nick, sink.nick, sizeof(sink.nick)); - } else { - strcpy(sink.nick, "Unknown"); - } - - if (json_exists(name)) { - json_string_copy(name, sink.name, sizeof(sink.name)); - } else { - strcpy(sink.name, "Unknown"); - } - - sink.id = json_int(object_id); - - const char *raw_json = json_raw(item); - size_t raw_length = json_raw_length(item); - if (raw_json && raw_length > 0) { - LOG_DEBUG(mux_module, "Full JSON for sink: %.*s\n", (int)raw_length, raw_json); - } - - LOG_INFO(mux_module, "Found sink: ID=%d, Description=%s, Nick=%s, Name=%s\n", sink.id, sink.description, sink.nick, sink.name); - return sink; -} - int get_default_sink_id(int *sink_id) { const char* command = "wpctl status | grep -A 5 Sinks | grep '\\*' | sed 's/ \\|.*\\* //' | cut -f1 -d'.'"; FILE *fp = popen(command, "r"); if (fp == NULL) { - perror("Failed to run command"); + LOG_ERROR(mux_module, "Failed to run command: %s", strerror(errno)); return -1; } @@ -232,7 +138,7 @@ int get_default_sink_id(int *sink_id) { buffer[strcspn(buffer, "\n")] = '\0'; *sink_id = atoi(buffer); } else { - perror("Failed to read sink ID"); + LOG_ERROR(mux_module, "Failed to read sink ID: %s", strerror(errno)); pclose(fp); return -1; } @@ -244,11 +150,11 @@ int get_default_sink_id(int *sink_id) { int set_default_sink(int sink_id) { char command[256]; snprintf(command, sizeof(command), "wpctl set-default %d", sink_id); - LOG_INFO(mux_module, "Executing command: %s\n", command); + LOG_INFO(mux_module, "Executing command: %s", command); int result = system(command); if (result != 0) { - perror("Failed to execute command"); + LOG_ERROR(mux_module, "Failed to execute command: %s", strerror(errno)); } - LOG_INFO(mux_module, "Command result: %d\n", result); + LOG_INFO(mux_module, "Command result: %d", result); return result; } \ No newline at end of file diff --git a/module/muxtweakgen.c b/module/muxtweakgen.c index cc58771d..a7612754 100755 --- a/module/muxtweakgen.c +++ b/module/muxtweakgen.c @@ -98,13 +98,13 @@ void show_help(lv_obj_t *element_focused) { } void init_audio_sink_dropdown() { - LOG_DEBUG(mux_module, "Initializing audio sink dropdown...\n"); + LOG_DEBUG(mux_module, "Initializing audio sink dropdown..."); Sink *sinks = NULL; int count = 0; if (get_sinks(&sinks, &count) != 0) { - LOG_ERROR(mux_module, "Failed to retrieve audio sinks.\n"); + LOG_ERROR(mux_module, "Failed to retrieve audio sinks"); return; } @@ -113,23 +113,23 @@ void init_audio_sink_dropdown() { for (int i = 0; i < count; i++) { char dropdown_option[128]; snprintf(dropdown_option, sizeof(dropdown_option), "%d: %s", sinks[i].id, sinks[i].description); - LOG_DEBUG(mux_module, "Adding sink to dropdown: %s\n", dropdown_option); + LOG_DEBUG(mux_module, "Adding sink to dropdown: %s", dropdown_option); lv_dropdown_add_option(ui_droAudioSink, dropdown_option, LV_DROPDOWN_POS_LAST); } int default_sink_id = -1; if (get_default_sink_id(&default_sink_id) == 0) { - LOG_DEBUG(mux_module, "Default sink ID: %d\n", default_sink_id); + LOG_DEBUG(mux_module, "Default sink ID: %d", default_sink_id); for (int i = 0; i < count; i++) { if (sinks[i].id == default_sink_id) { audio_sink_original = i; - LOG_DEBUG(mux_module, "Setting default sink to index: %d\n", i); + LOG_DEBUG(mux_module, "Setting default sink to index: %d", i); lv_dropdown_set_selected(ui_droAudioSink, i); break; } } } else { - LOG_ERROR(mux_module, "Failed to retrieve default sink ID.\n"); + LOG_ERROR(mux_module, "Failed to retrieve default sink ID."); } free(sinks); @@ -140,21 +140,21 @@ static void update_default_sink(int selected_index) { int count = 0; if (get_sinks(&sinks, &count) != 0) { - fprintf(stderr, "Failed to retrieve sinks.\n"); + LOG_ERROR(mux_module, "Failed to retrieve sinks."); return; } if (selected_index >= 0 && selected_index < count) { int sink_id = sinks[selected_index].id; - LOG_DEBUG(mux_module, "Setting default sink ID: %d\n", sink_id); + LOG_DEBUG(mux_module, "Setting default sink ID: %d", sink_id); if (set_default_sink(sink_id) == 0) { - LOG_DEBUG(mux_module, "Successfully set default sink.\n"); + LOG_DEBUG(mux_module, "Successfully set default sink."); } else { - LOG_ERROR(mux_module, "Failed to set default sink.\n"); + LOG_ERROR(mux_module, "Failed to set default sink."); } } else { - LOG_ERROR(mux_module, "Invalid selected audio sink index: %d\n", selected_index); + LOG_ERROR(mux_module, "Invalid selected audio sink index: %d", selected_index); } free(sinks); @@ -378,7 +378,7 @@ void init_navigation_groups() { apply_theme_list_glyph(&theme, ui_icoHidden, mux_module, "hidden"); apply_theme_list_glyph(&theme, ui_icoBGM, mux_module, "bgm"); apply_theme_list_glyph(&theme, ui_icoSound, mux_module, "sound"); - apply_theme_list_glyph(&theme, ui_icoAudioSink, mux_module, "audiosink"); + apply_theme_list_glyph(&theme, ui_icoAudioSink, mux_module, "sound"); apply_theme_list_glyph(&theme, ui_icoStartup, mux_module, "startup"); apply_theme_list_glyph(&theme, ui_icoColour, mux_module, "colour"); apply_theme_list_glyph(&theme, ui_icoBrightness, mux_module, "brightness");