From f736a6a7bb8ead368443194dd1889250627ba423 Mon Sep 17 00:00:00 2001 From: infinityabundance <255699974+infinityabundance@users.noreply.github.com> Date: Fri, 6 Feb 2026 19:27:02 +0000 Subject: [PATCH] Add display selection to settings and host defaults --- include/rootstream.h | 1 + src/config.c | 36 +++++++++++++++++++++++++++++++----- src/main.c | 6 +++++- src/tray.c | 37 +++++++++++++++++++++++++++++++++++++ 4 files changed, 74 insertions(+), 6 deletions(-) diff --git a/include/rootstream.h b/include/rootstream.h index 003defc..20017d9 100644 --- a/include/rootstream.h +++ b/include/rootstream.h @@ -322,6 +322,7 @@ typedef struct { uint32_t video_bitrate; /* Target bitrate (bits/sec) */ uint32_t video_framerate; /* Target framerate (fps) */ char video_codec[16]; /* Codec: "h264", "h265" */ + int display_index; /* Preferred display index */ /* Audio settings */ bool audio_enabled; /* Enable audio streaming */ diff --git a/src/config.c b/src/config.c index 0098acb..a7428dc 100644 --- a/src/config.c +++ b/src/config.c @@ -17,20 +17,30 @@ #include #include #include -#include -#include #include -#include + +#ifdef _WIN32 + #include + #include + #define getuid() 0 +#else + #include + #include + #include +#endif /* * Get configuration directory path * * @return Configuration directory path (never NULL) * - * Priority: + * Priority (Linux): * 1. $XDG_CONFIG_HOME/rootstream * 2. $HOME/.config/rootstream * 3. /tmp/rootstream (fallback if no home) + * + * Priority (Windows): + * 1. %APPDATA%\RootStream (via platform layer) */ const char* config_get_dir(void) { static char config_dir[512] = {0}; @@ -39,6 +49,17 @@ const char* config_get_dir(void) { return config_dir; /* Already computed */ } +#ifdef _WIN32 + /* Use platform layer for Windows */ + const char *platform_dir = rs_config_dir(); + if (platform_dir) { + strncpy(config_dir, platform_dir, sizeof(config_dir) - 1); + return config_dir; + } + /* Fallback */ + snprintf(config_dir, sizeof(config_dir), "C:\\RootStream"); + return config_dir; +#else /* Try XDG_CONFIG_HOME */ const char *xdg_config = getenv("XDG_CONFIG_HOME"); if (xdg_config && xdg_config[0] != '\0') { @@ -64,6 +85,7 @@ const char* config_get_dir(void) { /* Fallback to /tmp (not ideal, but works) */ snprintf(config_dir, sizeof(config_dir), "/tmp/rootstream-%d", getuid()); fprintf(stderr, "WARNING: Using fallback config directory: %s\n", config_dir); +#endif return config_dir; } @@ -76,6 +98,7 @@ static void config_init_defaults(settings_t *settings) { settings->video_bitrate = 10000000; /* 10 Mbps */ settings->video_framerate = 60; /* 60 fps */ strncpy(settings->video_codec, "h264", sizeof(settings->video_codec) - 1); + settings->display_index = 0; /* Audio defaults */ settings->audio_enabled = true; @@ -182,6 +205,8 @@ static int config_load_ini(settings_t *settings, const char *config_dir) { settings->video_framerate = (uint32_t)atoi(value); } else if (strcmp(key, "codec") == 0) { strncpy(settings->video_codec, value, sizeof(settings->video_codec) - 1); + } else if (strcmp(key, "display") == 0) { + settings->display_index = atoi(value); } } /* Audio settings */ @@ -247,7 +272,8 @@ static int config_save_ini(const settings_t *settings, const char *config_dir) { fprintf(fp, "[video]\n"); fprintf(fp, "bitrate = %u\n", settings->video_bitrate); fprintf(fp, "framerate = %u\n", settings->video_framerate); - fprintf(fp, "codec = %s\n\n", settings->video_codec); + fprintf(fp, "codec = %s\n", settings->video_codec); + fprintf(fp, "display = %d\n\n", settings->display_index); /* Audio settings */ fprintf(fp, "[audio]\n"); diff --git a/src/main.c b/src/main.c index a651f4b..5c98a2c 100644 --- a/src/main.c +++ b/src/main.c @@ -308,7 +308,7 @@ int main(int argc, char **argv) { bool service_mode = false; bool no_discovery = false; uint16_t port = 9876; - int display_idx = 0; + int display_idx = -1; int bitrate = 10000; const char *record_file = NULL; bool latency_log = false; @@ -390,6 +390,10 @@ int main(int argc, char **argv) { ctx.encoder.bitrate = (uint32_t)bitrate * 1000; ctx.is_host = false; + if (display_idx < 0) { + display_idx = ctx.settings.display_index; + } + /* Handle --list-displays flag */ if (list_displays) { display_info_t displays[MAX_DISPLAYS]; diff --git a/src/tray.c b/src/tray.c index a33caae..91614bf 100644 --- a/src/tray.c +++ b/src/tray.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include @@ -365,6 +366,38 @@ static void on_settings(GtkMenuItem *item, gpointer user_data) { GtkWidget *codec_label = gtk_label_new(codec_text); gtk_box_pack_start(GTK_BOX(video_box), codec_label, FALSE, FALSE, 0); + /* Display selection */ + GtkWidget *display_label = gtk_label_new("Display:"); + GtkWidget *display_combo = gtk_combo_box_text_new(); + display_info_t displays[MAX_DISPLAYS]; + int num_displays = rootstream_detect_displays(displays, MAX_DISPLAYS); + int active_index = 0; + if (num_displays > 0) { + for (int i = 0; i < num_displays; i++) { + char item[128]; + snprintf(item, sizeof(item), "%d: %s (%dx%d @ %d Hz)", + i, displays[i].name, displays[i].width, + displays[i].height, displays[i].refresh_rate); + gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(display_combo), item); + if (i == ctx->settings.display_index) { + active_index = i; + } + } + for (int i = 0; i < num_displays; i++) { + if (displays[i].fd >= 0) { + close(displays[i].fd); + } + } + gtk_combo_box_set_active(GTK_COMBO_BOX(display_combo), active_index); + } else { + gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(display_combo), + "No displays detected"); + gtk_combo_box_set_active(GTK_COMBO_BOX(display_combo), 0); + gtk_widget_set_sensitive(display_combo, FALSE); + } + gtk_box_pack_start(GTK_BOX(video_box), display_label, FALSE, FALSE, 0); + gtk_box_pack_start(GTK_BOX(video_box), display_combo, FALSE, FALSE, 0); + gtk_notebook_append_page(GTK_NOTEBOOK(notebook), video_box, gtk_label_new("Video")); @@ -422,6 +455,10 @@ static void on_settings(GtkMenuItem *item, gpointer user_data) { gtk_spin_button_get_value(GTK_SPIN_BUTTON(bitrate_spin)) * 1000000); ctx->settings.video_framerate = (uint32_t) gtk_spin_button_get_value(GTK_SPIN_BUTTON(fps_spin)); + if (num_displays > 0) { + ctx->settings.display_index = + gtk_combo_box_get_active(GTK_COMBO_BOX(display_combo)); + } ctx->settings.audio_enabled = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(audio_enabled)); ctx->settings.audio_bitrate = (uint32_t)(