From ca06426cee69b0f263d38e9542894a91cd75cdb4 Mon Sep 17 00:00:00 2001 From: damachine Date: Tue, 3 Feb 2026 23:45:30 +0100 Subject: [PATCH 1/6] VERSION: prepare to 2.1.4 --- .SRCINFO | 2 +- VERSION | 2 +- .../plugins/coolerdash/config.json | 10 +- .../plugins/coolerdash/ui/index.html | 163 +++++++- src/device/config.c | 166 +++++++- src/device/config.h | 9 + src/mods/circle.c | 348 ++++++++--------- src/mods/display.c | 129 ++++++- src/mods/display.h | 57 ++- src/mods/dual.c | 357 ++++++++++-------- 10 files changed, 882 insertions(+), 361 deletions(-) diff --git a/.SRCINFO b/.SRCINFO index 4ee8d2e..62976cb 100644 --- a/.SRCINFO +++ b/.SRCINFO @@ -1,6 +1,6 @@ pkgbase = coolerdash pkgdesc = Monitor telemetry data on an AIO liquid cooler with an integrated LCD display - pkgver = 2.1.3 + pkgver = 2.1.4 pkgrel = 1 url = https://github.com/damachine/coolerdash install = coolerdash.install diff --git a/VERSION b/VERSION index ac2cdeb..7d2ed7c 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -2.1.3 +2.1.4 diff --git a/etc/coolercontrol/plugins/coolerdash/config.json b/etc/coolercontrol/plugins/coolerdash/config.json index fd642fb..6d1923b 100644 --- a/etc/coolercontrol/plugins/coolerdash/config.json +++ b/etc/coolercontrol/plugins/coolerdash/config.json @@ -22,7 +22,10 @@ "height": 0, "shape": "auto", "content_scale_factor": 0.98, - "inscribe_factor": 0.70710678 + "inscribe_factor": 0.70710678, + "sensor_slot_up": "cpu", + "sensor_slot_mid": "liquid", + "sensor_slot_down": "gpu" }, "layout": { @@ -31,7 +34,10 @@ "bar_gap": 12, "bar_border": 2.0, "label_margin_left": 1, - "label_margin_bar": 1 + "label_margin_bar": 1, + "bar_height_up": 0, + "bar_height_mid": 0, + "bar_height_down": 0 }, "colors": { diff --git a/etc/coolercontrol/plugins/coolerdash/ui/index.html b/etc/coolercontrol/plugins/coolerdash/ui/index.html index 1716af2..8c30e81 100644 --- a/etc/coolercontrol/plugins/coolerdash/ui/index.html +++ b/etc/coolercontrol/plugins/coolerdash/ui/index.html @@ -349,11 +349,56 @@

+

Sensor Slots

+
+ Sensor Assignment + Assign sensors to display positions. Use "None" to disable a slot. At least one slot must be active. +
+ +
+
+ + Top position sensor + +
+ +
+ + Center position (Circle mode only) + +
+ +
+ + Bottom position sensor + +
+
+ +
- 0.5 - 60 seconds - + 0.2 - 60 seconds +
@@ -420,7 +465,8 @@

Layout
- + + Fallback for all slots
@@ -435,6 +481,27 @@

Layout

+

Individual Bar Heights

+
+
+ + 0 = use default + +
+ +
+ + 0 = use default (Circle only) + +
+ +
+ + 0 = use default + +
+
+
@@ -768,7 +835,10 @@

Labels height: 0, shape: "auto", content_scale_factor: 0.98, - inscribe_factor: 0.70710678 + inscribe_factor: 0.70710678, + sensor_slot_up: "cpu", + sensor_slot_mid: "liquid", + sensor_slot_down: "gpu" }, layout: { bar_height: 24, @@ -777,7 +847,10 @@

Labels bar_border: 2.0, bar_border_enabled: true, label_margin_left: 1, - label_margin_bar: 1 + label_margin_bar: 1, + bar_height_up: 0, + bar_height_mid: 0, + bar_height_down: 0 }, colors: { display_background: { r: 0, g: 0, b: 0 }, @@ -930,8 +1003,72 @@

Labels // Toggle circle interval visibility function toggleCircleInterval() { const mode = document.getElementById('display_mode').value; - const group = document.getElementById('circle_interval_group'); - group.style.display = mode === 'circle' ? 'block' : 'none'; + const circleGroup = document.getElementById('circle_interval_group'); + const midSlotGroup = document.getElementById('sensor_slot_mid_group'); + const midBarHeightGroup = document.getElementById('bar_height_mid_group'); + + // Show circle interval only in circle mode + circleGroup.style.display = mode === 'circle' ? 'block' : 'none'; + + // Show/hide middle slot fields based on mode + if (midSlotGroup) { + midSlotGroup.style.display = mode === 'circle' ? 'block' : 'none'; + } + if (midBarHeightGroup) { + midBarHeightGroup.style.display = mode === 'circle' ? 'block' : 'none'; + } + + // If switching to dual mode, set mid slot to "none" + if (mode === 'dual') { + const midSlot = document.getElementById('sensor_slot_mid'); + if (midSlot) midSlot.value = 'none'; + } + + // Revalidate slots + validateSensorSlots(); + } + + // Validate sensor slot assignments + function validateSensorSlots() { + const mode = document.getElementById('display_mode').value; + const slotUp = document.getElementById('sensor_slot_up').value; + const slotMid = document.getElementById('sensor_slot_mid').value; + const slotDown = document.getElementById('sensor_slot_down').value; + + const warningDiv = document.getElementById('sensor_slot_warning'); + const warningText = document.getElementById('sensor_slot_warning_text'); + let warnings = []; + + // Get active slots (excluding "none") + const activeSlots = []; + if (slotUp !== 'none') activeSlots.push({ name: 'Upper', value: slotUp }); + if (mode === 'circle' && slotMid !== 'none') activeSlots.push({ name: 'Middle', value: slotMid }); + if (slotDown !== 'none') activeSlots.push({ name: 'Lower', value: slotDown }); + + // Check minimum one active slot + if (activeSlots.length === 0) { + warnings.push('At least one sensor slot must be active.'); + } + + // Check for duplicates + const usedSensors = {}; + for (const slot of activeSlots) { + if (usedSensors[slot.value]) { + warnings.push(`Duplicate sensor: "${slot.value}" is used in both ${usedSensors[slot.value]} and ${slot.name} slot.`); + } else { + usedSensors[slot.value] = slot.name; + } + } + + // Show/hide warnings + if (warnings.length > 0) { + warningDiv.style.display = 'block'; + warningText.textContent = warnings.join(' '); + } else { + warningDiv.style.display = 'none'; + } + + return warnings.length === 0; } // Update range value display @@ -1060,8 +1197,6 @@

Labels // Update UI if (field === 'brightness') { updateRangeValue('brightness', value); - } else if (field === 'mode') { - toggleCircleInterval(); } } } @@ -1091,11 +1226,21 @@

Labels if (config.liquid.threshold_4_color) setupColorPicker('color_liquid_4', 'rgb_liquid_4', config.liquid.threshold_4_color); } + // Update mode-dependent UI elements after form is populated + toggleCircleInterval(); + validateSensorSlots(); + console.log("Form populated with config"); } // Save config async function saveConfig() { + // Validate sensor slots before saving + if (!validateSensorSlots()) { + alert("Configuration has validation errors.\n\nPlease fix the sensor slot warnings before saving."); + return; + } + const config = buildConfig(); console.log("Saving config:", config); diff --git a/src/device/config.c b/src/device/config.c index 18dbba9..ca873f1 100644 --- a/src/device/config.c +++ b/src/device/config.c @@ -142,6 +142,14 @@ static void set_display_defaults(Config *config) config->display_content_scale_factor = 0.98f; if (config->display_inscribe_factor < 0.0f) config->display_inscribe_factor = 0.70710678f; + + // Sensor slot defaults (flexible sensor assignment) + if (config->sensor_slot_up[0] == '\0') + cc_safe_strcpy(config->sensor_slot_up, sizeof(config->sensor_slot_up), "cpu"); + if (config->sensor_slot_mid[0] == '\0') + cc_safe_strcpy(config->sensor_slot_mid, sizeof(config->sensor_slot_mid), "liquid"); + if (config->sensor_slot_down[0] == '\0') + cc_safe_strcpy(config->sensor_slot_down, sizeof(config->sensor_slot_down), "gpu"); } /** @@ -165,6 +173,14 @@ static void set_layout_defaults(Config *config) // bar_border_enabled: -1 = auto (enabled), 0 = disabled, 1 = enabled if (config->layout_bar_border_enabled < 0) config->layout_bar_border_enabled = 1; // Default: enabled + + // Individual bar heights per slot (default to main bar_height) + if (config->layout_bar_height_up == 0) + config->layout_bar_height_up = config->layout_bar_height; + if (config->layout_bar_height_mid == 0) + config->layout_bar_height_mid = config->layout_bar_height; + if (config->layout_bar_height_down == 0) + config->layout_bar_height_down = config->layout_bar_height; } /** @@ -296,6 +312,103 @@ static void set_color_defaults(Config *config) } } +/** + * @brief Check if a sensor slot value is valid + */ +static int is_valid_sensor_slot(const char *slot) +{ + if (!slot || slot[0] == '\0') + return 0; + return (strcmp(slot, "cpu") == 0 || + strcmp(slot, "gpu") == 0 || + strcmp(slot, "liquid") == 0 || + strcmp(slot, "none") == 0); +} + +/** + * @brief Check if a sensor slot is active (not "none") + */ +static int slot_is_active_str(const char *slot) +{ + if (!slot || slot[0] == '\0') + return 0; + return strcmp(slot, "none") != 0; +} + +/** + * @brief Validate sensor slot configuration + * @details Checks for duplicates (excluding "none"), ensures at least one active slot, + * and validates slot values. Resets to defaults on critical errors. + */ +static void validate_sensor_slots(Config *config) +{ + if (!config) + return; + + int reset_needed = 0; + + // Validate slot values (must be cpu/gpu/liquid/none) + if (!is_valid_sensor_slot(config->sensor_slot_up)) + { + log_message(LOG_WARNING, "Invalid sensor_slot_up value, using 'cpu'"); + cc_safe_strcpy(config->sensor_slot_up, sizeof(config->sensor_slot_up), "cpu"); + } + if (!is_valid_sensor_slot(config->sensor_slot_mid)) + { + log_message(LOG_WARNING, "Invalid sensor_slot_mid value, using 'liquid'"); + cc_safe_strcpy(config->sensor_slot_mid, sizeof(config->sensor_slot_mid), "liquid"); + } + if (!is_valid_sensor_slot(config->sensor_slot_down)) + { + log_message(LOG_WARNING, "Invalid sensor_slot_down value, using 'gpu'"); + cc_safe_strcpy(config->sensor_slot_down, sizeof(config->sensor_slot_down), "gpu"); + } + + // Check for duplicates (only among active slots, "none" can appear multiple times) + const char *slots[] = {config->sensor_slot_up, config->sensor_slot_mid, config->sensor_slot_down}; + const char *slot_names[] = {"sensor_slot_up", "sensor_slot_mid", "sensor_slot_down"}; + + for (int i = 0; i < 3; i++) + { + if (!slot_is_active_str(slots[i])) + continue; // Skip "none" slots + + for (int j = i + 1; j < 3; j++) + { + if (!slot_is_active_str(slots[j])) + continue; // Skip "none" slots + + if (strcmp(slots[i], slots[j]) == 0) + { + log_message(LOG_WARNING, "Duplicate sensor in %s and %s: '%s'. Resetting to defaults.", + slot_names[i], slot_names[j], slots[i]); + reset_needed = 1; + break; + } + } + if (reset_needed) + break; + } + + // Check that at least one slot is active + if (!slot_is_active_str(config->sensor_slot_up) && + !slot_is_active_str(config->sensor_slot_mid) && + !slot_is_active_str(config->sensor_slot_down)) + { + log_message(LOG_ERROR, "All sensor slots are 'none'. At least one sensor must be active. Resetting to defaults."); + reset_needed = 1; + } + + // Reset to defaults if validation failed + if (reset_needed) + { + cc_safe_strcpy(config->sensor_slot_up, sizeof(config->sensor_slot_up), "cpu"); + cc_safe_strcpy(config->sensor_slot_mid, sizeof(config->sensor_slot_mid), "liquid"); + cc_safe_strcpy(config->sensor_slot_down, sizeof(config->sensor_slot_down), "gpu"); + log_message(LOG_STATUS, "Sensor slots reset to defaults: up=cpu, mid=liquid, down=gpu"); + } +} + /** * @brief Apply all system default values for missing fields */ @@ -311,6 +424,7 @@ static void apply_system_defaults(Config *config) set_font_defaults(config); set_temperature_defaults(config); set_color_defaults(config); + validate_sensor_slots(config); } // ============================================================================ @@ -483,7 +597,7 @@ static void load_display_from_json(json_t *root, Config *config) if (refresh && json_is_number(refresh)) { double val = json_number_value(refresh); - if (val >= 0.01 && val <= 60.0) + if (val >= 0.2 && val <= 60.0) config->display_refresh_interval = (float)val; } @@ -540,6 +654,31 @@ static void load_display_from_json(json_t *root, Config *config) if (val >= 0.0 && val <= 1.0) config->display_inscribe_factor = (float)val; } + + // Sensor slot configuration + json_t *slot_up = json_object_get(display, "sensor_slot_up"); + if (slot_up && json_is_string(slot_up)) + { + const char *value = json_string_value(slot_up); + if (value) + cc_safe_strcpy(config->sensor_slot_up, sizeof(config->sensor_slot_up), value); + } + + json_t *slot_mid = json_object_get(display, "sensor_slot_mid"); + if (slot_mid && json_is_string(slot_mid)) + { + const char *value = json_string_value(slot_mid); + if (value) + cc_safe_strcpy(config->sensor_slot_mid, sizeof(config->sensor_slot_mid), value); + } + + json_t *slot_down = json_object_get(display, "sensor_slot_down"); + if (slot_down && json_is_string(slot_down)) + { + const char *value = json_string_value(slot_down); + if (value) + cc_safe_strcpy(config->sensor_slot_down, sizeof(config->sensor_slot_down), value); + } } /** @@ -608,6 +747,31 @@ static void load_layout_from_json(json_t *root, Config *config) if (val >= 1 && val <= 20) config->layout_label_margin_bar = (uint8_t)val; } + + // Individual bar heights per slot + json_t *bar_height_up = json_object_get(layout, "bar_height_up"); + if (bar_height_up && json_is_integer(bar_height_up)) + { + int val = (int)json_integer_value(bar_height_up); + if (val > 0 && val <= 100) + config->layout_bar_height_up = (uint16_t)val; + } + + json_t *bar_height_mid = json_object_get(layout, "bar_height_mid"); + if (bar_height_mid && json_is_integer(bar_height_mid)) + { + int val = (int)json_integer_value(bar_height_mid); + if (val > 0 && val <= 100) + config->layout_bar_height_mid = (uint16_t)val; + } + + json_t *bar_height_down = json_object_get(layout, "bar_height_down"); + if (bar_height_down && json_is_integer(bar_height_down)) + { + int val = (int)json_integer_value(bar_height_down); + if (val > 0 && val <= 100) + config->layout_bar_height_down = (uint16_t)val; + } } /** diff --git a/src/device/config.h b/src/device/config.h index 4dd093f..375a872 100644 --- a/src/device/config.h +++ b/src/device/config.h @@ -26,6 +26,7 @@ #define CONFIG_MAX_PASSWORD_LEN 128 #define CONFIG_MAX_PATH_LEN 512 #define CONFIG_MAX_FONT_NAME_LEN 64 +#define CONFIG_MAX_SENSOR_SLOT_LEN 32 /** * @brief Simple color structure. @@ -82,8 +83,16 @@ typedef struct Config float display_content_scale_factor; float display_inscribe_factor; + // Sensor slot configuration (flexible sensor assignment) + char sensor_slot_up[CONFIG_MAX_SENSOR_SLOT_LEN]; // "cpu", "gpu", "liquid", "none" + char sensor_slot_mid[CONFIG_MAX_SENSOR_SLOT_LEN]; // "cpu", "gpu", "liquid", "none" + char sensor_slot_down[CONFIG_MAX_SENSOR_SLOT_LEN]; // "cpu", "gpu", "liquid", "none" + // Layout configuration uint16_t layout_bar_height; + uint16_t layout_bar_height_up; // Individual bar height for upper slot + uint16_t layout_bar_height_mid; // Individual bar height for middle slot + uint16_t layout_bar_height_down; // Individual bar height for lower slot uint16_t layout_bar_gap; float layout_bar_border; int layout_bar_border_enabled; // 1=enabled, 0=disabled, -1=auto (use default) diff --git a/src/mods/circle.c b/src/mods/circle.c index 0d64711..b59104d 100644 --- a/src/mods/circle.c +++ b/src/mods/circle.c @@ -34,6 +34,7 @@ #include "../srv/cc_conf.h" #include "../srv/cc_main.h" #include "../srv/cc_sensor.h" +#include "display.h" #include "circle.h" // Circle inscribe factor for circular displays (1/√2 ≈ 0.7071) @@ -42,18 +43,9 @@ #endif /** - * @brief Sensor display mode enumeration + * @brief Global state for sensor alternation (slot-based cycling) */ -typedef enum -{ - SENSOR_CPU = 0, - SENSOR_GPU = 1 -} SensorMode; - -/** - * @brief Global state for sensor alternation - */ -static SensorMode current_sensor = SENSOR_CPU; +static int current_slot_index = 0; // 0=up, 1=mid, 2=down static time_t last_switch_time = 0; /** @@ -209,6 +201,66 @@ static void calculate_scaling_params(const struct Config *config, params->safe_content_margin); } +/** + * @brief Get the slot value for a given slot index + * @param config Configuration + * @param slot_index 0=up, 1=mid, 2=down + * @return Slot value string ("cpu", "gpu", "liquid", "none") + */ +static const char *get_slot_value_by_index(const struct Config *config, int slot_index) +{ + if (!config) + return "none"; + + switch (slot_index) + { + case 0: + return config->sensor_slot_up; + case 1: + return config->sensor_slot_mid; + case 2: + return config->sensor_slot_down; + default: + return "none"; + } +} + +/** + * @brief Get slot name for a given slot index + */ +static const char *get_slot_name_by_index(int slot_index) +{ + switch (slot_index) + { + case 0: + return "up"; + case 1: + return "mid"; + case 2: + return "down"; + default: + return "up"; + } +} + +/** + * @brief Find next active slot index (wrapping around) + * @param config Configuration + * @param start_index Starting slot index + * @return Next active slot index, or -1 if none found + */ +static int find_next_active_slot(const struct Config *config, int start_index) +{ + for (int i = 0; i < 3; i++) + { + int idx = (start_index + i) % 3; + const char *slot_value = get_slot_value_by_index(config, idx); + if (slot_is_active(slot_value)) + return idx; + } + return -1; // No active slots +} + /** * @brief Check if sensor should switch based on configured interval */ @@ -218,6 +270,10 @@ static void update_sensor_mode(const struct Config *config) if (last_switch_time == 0) { + // Initialize to first active slot + current_slot_index = find_next_active_slot(config, 0); + if (current_slot_index < 0) + current_slot_index = 0; // Fallback last_switch_time = current_time; return; } @@ -229,54 +285,26 @@ static void update_sensor_mode(const struct Config *config) if (difftime(current_time, last_switch_time) >= interval) { - // Toggle sensor - current_sensor = (current_sensor == SENSOR_CPU) ? SENSOR_GPU : SENSOR_CPU; + // Find next active slot + int next_slot = find_next_active_slot(config, (current_slot_index + 1) % 3); + if (next_slot >= 0) + current_slot_index = next_slot; last_switch_time = current_time; // Verbose logging only if (verbose_logging) { + const char *slot_value = get_slot_value_by_index(config, current_slot_index); + const char *label = get_slot_label(slot_value); log_message(LOG_INFO, - "Circle mode: switched to %s display (interval: %.0fs)", - current_sensor == SENSOR_CPU ? "CPU" : "GPU", interval); + "Circle mode: switched to %s display (slot: %s, interval: %.0fs)", + label ? label : "unknown", + get_slot_name_by_index(current_slot_index), + interval); } } } -/** - * @brief Get temperature bar color based on thresholds - */ -static Color get_temperature_bar_color(const struct Config *config, float val, - SensorMode sensor) -{ - // Use unified temperature thresholds (same for CPU/GPU) - (void)sensor; // Unused parameter - keeping for API consistency - - if (val < config->temp_threshold_1) - return config->temp_threshold_1_bar; - else if (val < config->temp_threshold_2) - return config->temp_threshold_2_bar; - else if (val < config->temp_threshold_3) - return config->temp_threshold_3_bar; - else - return config->temp_threshold_4_bar; -} - -/** - * @brief Get liquid temperature bar color based on thresholds - */ -static Color get_liquid_bar_color(const struct Config *config, float val) -{ - if (val < config->temp_liquid_threshold_1) - return config->temp_liquid_threshold_1_bar; - else if (val < config->temp_liquid_threshold_2) - return config->temp_liquid_threshold_2_bar; - else if (val < config->temp_liquid_threshold_3) - return config->temp_liquid_threshold_3_bar; - else - return config->temp_liquid_threshold_4_bar; -} - /** * @brief Draw rounded rectangle path */ @@ -308,35 +336,51 @@ static void draw_degree_symbol(cairo_t *cr, double x, double y, } /** - * @brief Draw single sensor display (CPU or GPU) with optional liquid - * temperature + * @brief Draw single sensor display based on current slot + * @param cr Cairo context + * @param config Configuration + * @param params Scaling parameters + * @param data Sensor data + * @param slot_value Current slot sensor value ("cpu", "gpu", "liquid") */ static void draw_single_sensor(cairo_t *cr, const struct Config *config, - const ScalingParams *params, float temp_value, - float temp_liquid, SensorMode sensor) + const ScalingParams *params, + const monitor_sensor_data_t *data, + const char *slot_value) { - if (!cr || !config || !params) + if (!cr || !config || !params || !data || !slot_value) + return; + + // Skip if slot is not active + if (!slot_is_active(slot_value)) return; + // Get temperature and label for current slot + const float temp_value = get_slot_temperature(data, slot_value); + const char *label_text = get_slot_label(slot_value); + const float max_temp = get_slot_max_scale(config, slot_value); + const int effective_bar_width = params->safe_bar_width; - const int bar_height = config->layout_bar_height; - const int bar_gap = config->layout_bar_gap; - // Calculate vertical layout - BAR is centered (like original) - // For CPU: Extra liquid bar below CPU bar - // For GPU: Single bar (original behavior) - const int bar_y = - (config->display_height - bar_height) / 2; // Bar centered vertically - const int bar_x = (config->display_width - effective_bar_width) / - 2; // Bar centered horizontally + // Get bar height for current slot (use "up" slot as reference since circle shows one at a time) + const int bar_height = get_slot_bar_height(config, get_slot_name_by_index(current_slot_index)); + + // Calculate vertical layout - BAR is centered + const int bar_y = (config->display_height - bar_height) / 2; + const int bar_x = (config->display_width - effective_bar_width) / 2; // Temperature above the bar (10% of display height above bar) - const int temp_spacing = (int)(config->display_height * 0.10); // 10% spacing + const int temp_spacing = (int)(config->display_height * 0.10); const int temp_y = bar_y - temp_spacing; // Draw temperature value (centered horizontally INCLUDING degree symbol) char temp_str[16]; - snprintf(temp_str, sizeof(temp_str), "%d", (int)temp_value); + + // Use 1 decimal for liquid, integer for CPU/GPU + if (strcmp(slot_value, "liquid") == 0) + snprintf(temp_str, sizeof(temp_str), "%.1f", temp_value); + else + snprintf(temp_str, sizeof(temp_str), "%d", (int)temp_value); const Color *value_color = &config->font_color_temp; @@ -352,33 +396,40 @@ static void draw_single_sensor(cairo_t *cr, const struct Config *config, cairo_set_font_size(cr, config->font_size_temp / 1.66); cairo_text_extents_t degree_ext; cairo_text_extents(cr, "°", °ree_ext); - cairo_set_font_size(cr, config->font_size_temp); // Restore temp font size + cairo_set_font_size(cr, config->font_size_temp); // Center temperature + degree symbol as a unit const double total_width = temp_ext.width - 4 + degree_ext.width; double temp_x = (config->display_width - total_width) / 2.0; double final_temp_y = temp_y; - // Apply user-defined offsets for CPU/GPU - if (sensor == SENSOR_CPU) + // Apply user-defined offsets based on sensor type + if (strcmp(slot_value, "cpu") == 0) { if (config->display_temp_offset_x_cpu != -9999) temp_x += config->display_temp_offset_x_cpu; if (config->display_temp_offset_y_cpu != -9999) final_temp_y += config->display_temp_offset_y_cpu; } - else // SENSOR_GPU + else if (strcmp(slot_value, "gpu") == 0) { if (config->display_temp_offset_x_gpu != -9999) temp_x += config->display_temp_offset_x_gpu; if (config->display_temp_offset_y_gpu != -9999) final_temp_y += config->display_temp_offset_y_gpu; } + else if (strcmp(slot_value, "liquid") == 0) + { + if (config->display_temp_offset_x_liquid != -9999) + temp_x += config->display_temp_offset_x_liquid; + if (config->display_temp_offset_y_liquid != -9999) + final_temp_y += config->display_temp_offset_y_liquid; + } cairo_move_to(cr, temp_x, final_temp_y); cairo_show_text(cr, temp_str); - // Draw degree symbol (next to temperature, always bound to temp position) + // Draw degree symbol const int degree_spacing = (config->display_degree_spacing > 0) ? config->display_degree_spacing : 16; @@ -406,13 +457,11 @@ static void draw_single_sensor(cairo_t *cr, const struct Config *config, } // Bar fill (temperature-based) - const float max_temp = config->temp_max_scale; - const int fill_width = - calculate_temp_fill_width(temp_value, effective_bar_width, max_temp); + const int fill_width = calculate_temp_fill_width(temp_value, effective_bar_width, max_temp); if (fill_width > 0) { - Color bar_color = get_temperature_bar_color(config, temp_value, sensor); + Color bar_color = get_slot_bar_color(config, slot_value, temp_value); set_cairo_color(cr, &bar_color); cairo_save(cr); @@ -424,125 +473,34 @@ static void draw_single_sensor(cairo_t *cr, const struct Config *config, cairo_restore(cr); } - // --- Liquid Bar and Temperature (only for CPU mode) --- - if (sensor == SENSOR_CPU) + // Draw label (CPU, GPU, or LIQ) - centered horizontally, close to bottom + if (label_text) { - const int liquid_bar_y = bar_y + bar_height + bar_gap; - - // Liquid bar background - set_cairo_color(cr, &config->layout_bar_color_background); - draw_rounded_rectangle_path(cr, bar_x, liquid_bar_y, effective_bar_width, - bar_height, params->corner_radius); - cairo_fill(cr); - - // Liquid bar border (only if enabled and thickness > 0) - if (config->layout_bar_border_enabled && config->layout_bar_border > 0.0f) - { - set_cairo_color(cr, &config->layout_bar_color_border); - draw_rounded_rectangle_path(cr, bar_x, liquid_bar_y, effective_bar_width, - bar_height, params->corner_radius); - cairo_set_line_width(cr, config->layout_bar_border); - cairo_stroke(cr); - } - - // Liquid bar fill (0-40°C typical range for AIO coolers) - const float max_liquid_temp = config->temp_liquid_max_scale; - const int liquid_fill_width = calculate_temp_fill_width( - temp_liquid, effective_bar_width, max_liquid_temp); + const Color *label_color = &config->font_color_label; - if (liquid_fill_width > 0) - { - Color liquid_bar_color = get_liquid_bar_color(config, temp_liquid); - set_cairo_color(cr, &liquid_bar_color); - - cairo_save(cr); - draw_rounded_rectangle_path(cr, bar_x, liquid_bar_y, effective_bar_width, - bar_height, params->corner_radius); - cairo_clip(cr); - cairo_rectangle(cr, bar_x, liquid_bar_y, liquid_fill_width, bar_height); - cairo_fill(cr); - cairo_restore(cr); - } + cairo_select_font_face(cr, config->font_face, CAIRO_FONT_SLANT_NORMAL, + CAIRO_FONT_WEIGHT_NORMAL); + cairo_set_font_size(cr, config->font_size_labels); + set_cairo_color(cr, label_color); - // Liquid temperature text (under liquid bar with 1 decimal, half size, 10% - // spacing) - char liquid_temp_str[16]; - snprintf(liquid_temp_str, sizeof(liquid_temp_str), "%.1f", temp_liquid); + cairo_text_extents_t label_text_ext; + cairo_text_extents(cr, label_text, &label_text_ext); - const float liquid_font_size = - config->font_size_temp / 2.0f; // Half size of normal temp - cairo_set_font_size(cr, liquid_font_size); - set_cairo_color(cr, &config->font_color_temp); + // Center label horizontally + double label_x = (config->display_width - label_text_ext.width) / 2.0; - cairo_text_extents_t liquid_temp_ext; - cairo_text_extents(cr, liquid_temp_str, &liquid_temp_ext); + // Position label 2% from bottom + double final_label_y = config->display_height - (config->display_height * 0.02); - // Calculate degree symbol width for liquid temp - cairo_set_font_size(cr, liquid_font_size / 1.66); - cairo_text_extents_t liquid_degree_ext; - cairo_text_extents(cr, "°", &liquid_degree_ext); - cairo_set_font_size(cr, liquid_font_size); + // Apply user-defined offsets + if (config->display_label_offset_x != 0) + label_x += config->display_label_offset_x; + if (config->display_label_offset_y != 0) + final_label_y += config->display_label_offset_y; - const double liquid_total_width = - liquid_temp_ext.width - 4 + liquid_degree_ext.width; - double liquid_temp_x = (config->display_width - liquid_total_width) / 2.0; - - // Position directly under liquid bar (minimal spacing) - cairo_font_extents_t liquid_font_ext; - cairo_font_extents(cr, &liquid_font_ext); - int liquid_temp_y = liquid_bar_y + bar_height + (int)liquid_font_ext.ascent; - - // Apply user-defined offsets for liquid temperature - if (config->display_temp_offset_x_liquid != -9999) - liquid_temp_x += config->display_temp_offset_x_liquid; - if (config->display_temp_offset_y_liquid != -9999) - liquid_temp_y += config->display_temp_offset_y_liquid; - - cairo_move_to(cr, liquid_temp_x, liquid_temp_y); - cairo_show_text(cr, liquid_temp_str); - - // Liquid degree symbol (half size like liquid temp) - const double liquid_degree_x = - liquid_temp_x + liquid_temp_ext.width + degree_spacing / 2.0; - const double liquid_degree_y = liquid_temp_y - liquid_font_size * 0.25; - cairo_set_font_size(cr, - liquid_font_size / 1.66); // Match liquid temp sizing - cairo_move_to(cr, liquid_degree_x, liquid_degree_y); - cairo_show_text(cr, "°"); + cairo_move_to(cr, label_x, final_label_y); + cairo_show_text(cr, label_text); } - - // Draw label (CPU or GPU) - centered horizontally, close to bar - const char *label_text = (sensor == SENSOR_CPU) ? "CPU" : "GPU"; - const Color *label_color = &config->font_color_label; - - cairo_select_font_face(cr, config->font_face, CAIRO_FONT_SLANT_NORMAL, - CAIRO_FONT_WEIGHT_NORMAL); - cairo_set_font_size(cr, config->font_size_labels); - set_cairo_color(cr, label_color); - - // Get text extents for centering - cairo_text_extents_t label_text_ext; - cairo_text_extents(cr, label_text, &label_text_ext); - - // Get font extents for baseline positioning - cairo_font_extents_t label_font_ext; - cairo_font_extents(cr, &label_font_ext); - - // Center label horizontally - double label_x = (config->display_width - label_text_ext.width) / 2.0; - - // Position label 2% from bottom (baseline position, not adding ascent again) - double final_label_y = - config->display_height - (config->display_height * 0.02); - - // Apply user-defined offsets if set - if (config->display_label_offset_x != 0) - label_x += config->display_label_offset_x; - if (config->display_label_offset_y != 0) - final_label_y += config->display_label_offset_y; - - cairo_move_to(cr, label_x, final_label_y); - cairo_show_text(cr, label_text); } /** @@ -587,11 +545,9 @@ static void render_display_content(cairo_t *cr, const struct Config *config, // Update sensor mode (check if configured interval elapsed) update_sensor_mode(config); - // Draw current sensor - float temp_value = - (current_sensor == SENSOR_CPU) ? data->temp_cpu : data->temp_gpu; - draw_single_sensor(cr, config, params, temp_value, data->temp_liquid, - current_sensor); + // Get current slot value and draw sensor + const char *slot_value = get_slot_value_by_index(config, current_slot_index); + draw_single_sensor(cr, config, params, data, slot_value); } /** @@ -613,9 +569,11 @@ int render_circle_display(const struct Config *config, // Verbose logging only if (verbose_logging) { + const char *slot_value = get_slot_value_by_index(config, current_slot_index); + const char *label = get_slot_label(slot_value); + float temp = get_slot_temperature(data, slot_value); log_message(LOG_INFO, "Circle mode: rendering %s (%.1f°C)", - current_sensor == SENSOR_CPU ? "CPU" : "GPU", - current_sensor == SENSOR_CPU ? data->temp_cpu : data->temp_gpu); + label ? label : "unknown", temp); } cairo_surface_t *surface = NULL; @@ -661,9 +619,11 @@ int render_circle_display(const struct Config *config, // Verbose logging only if (verbose_logging) { + const char *slot_value = get_slot_value_by_index(config, current_slot_index); + const char *label = get_slot_label(slot_value); + float temp = get_slot_temperature(data, slot_value); log_message(LOG_STATUS, "Circle mode: %s display updated (%.1f°C)", - current_sensor == SENSOR_CPU ? "CPU" : "GPU", - current_sensor == SENSOR_CPU ? data->temp_cpu : data->temp_gpu); + label ? label : "unknown", temp); } return 1; diff --git a/src/mods/display.c b/src/mods/display.c index ebf28d2..5603c55 100644 --- a/src/mods/display.c +++ b/src/mods/display.c @@ -10,7 +10,7 @@ /** * @brief Display mode dispatcher implementation. * @details Routes rendering requests to appropriate mode-specific modules based - * on configuration. + * on configuration. Also provides shared helper functions for sensor slot handling. */ // Define POSIX constants @@ -23,10 +23,137 @@ // Include project headers #include "../device/config.h" +#include "../srv/cc_sensor.h" #include "circle.h" #include "display.h" #include "dual.h" +// ============================================================================ +// Sensor Slot Helper Functions +// ============================================================================ + +/** + * @brief Check if a sensor slot is active (not "none") + */ +int slot_is_active(const char *slot_value) +{ + if (!slot_value || slot_value[0] == '\0') + return 0; + return strcmp(slot_value, "none") != 0; +} + +/** + * @brief Get temperature value for a sensor slot + */ +float get_slot_temperature(const monitor_sensor_data_t *data, const char *slot_value) +{ + if (!data || !slot_value) + return 0.0f; + + if (strcmp(slot_value, "cpu") == 0) + return data->temp_cpu; + else if (strcmp(slot_value, "gpu") == 0) + return data->temp_gpu; + else if (strcmp(slot_value, "liquid") == 0) + return data->temp_liquid; + + return 0.0f; +} + +/** + * @brief Get display label for a sensor slot + */ +const char *get_slot_label(const char *slot_value) +{ + if (!slot_value || slot_value[0] == '\0') + return NULL; + + if (strcmp(slot_value, "cpu") == 0) + return "CPU"; + else if (strcmp(slot_value, "gpu") == 0) + return "GPU"; + else if (strcmp(slot_value, "liquid") == 0) + return "LIQ"; + else if (strcmp(slot_value, "none") == 0) + return NULL; + + return NULL; +} + +/** + * @brief Get bar color for a sensor slot based on temperature + */ +Color get_slot_bar_color(const struct Config *config, const char *slot_value, float temperature) +{ + if (!config || !slot_value) + { + // Return default green color + Color default_color = {0, 255, 0, 1}; + return default_color; + } + + // Liquid uses separate thresholds + if (strcmp(slot_value, "liquid") == 0) + { + if (temperature < config->temp_liquid_threshold_1) + return config->temp_liquid_threshold_1_bar; + else if (temperature < config->temp_liquid_threshold_2) + return config->temp_liquid_threshold_2_bar; + else if (temperature < config->temp_liquid_threshold_3) + return config->temp_liquid_threshold_3_bar; + else + return config->temp_liquid_threshold_4_bar; + } + + // CPU and GPU use global temperature thresholds + if (temperature < config->temp_threshold_1) + return config->temp_threshold_1_bar; + else if (temperature < config->temp_threshold_2) + return config->temp_threshold_2_bar; + else if (temperature < config->temp_threshold_3) + return config->temp_threshold_3_bar; + else + return config->temp_threshold_4_bar; +} + +/** + * @brief Get maximum scale for a sensor slot + */ +float get_slot_max_scale(const struct Config *config, const char *slot_value) +{ + if (!config) + return 115.0f; + + // Liquid has its own max scale (typically lower, e.g., 50°C) + if (slot_value && strcmp(slot_value, "liquid") == 0) + return config->temp_liquid_max_scale; + + // CPU and GPU use global max scale + return config->temp_max_scale; +} + +/** + * @brief Get bar height for a specific slot + */ +uint16_t get_slot_bar_height(const struct Config *config, const char *slot_name) +{ + if (!config || !slot_name) + return 24; // Default fallback + + if (strcmp(slot_name, "up") == 0) + return config->layout_bar_height_up; + else if (strcmp(slot_name, "mid") == 0) + return config->layout_bar_height_mid; + else if (strcmp(slot_name, "down") == 0) + return config->layout_bar_height_down; + + return config->layout_bar_height; // Fallback to global +} + +// ============================================================================ +// Display Dispatcher +// ============================================================================ + /** * @brief Main display dispatcher - routes to appropriate rendering mode. * @details Examines display_mode configuration and dispatches to either dual or diff --git a/src/mods/display.h b/src/mods/display.h index d549073..8e06050 100644 --- a/src/mods/display.h +++ b/src/mods/display.h @@ -16,8 +16,10 @@ #ifndef DISPLAY_DISPATCHER_H #define DISPLAY_DISPATCHER_H -// Forward declarations -struct Config; +// Include for Config and Color types +#include "../device/config.h" +// Include for monitor_sensor_data_t +#include "../srv/cc_sensor.h" /** * @brief Main display dispatcher - routes to appropriate rendering mode. @@ -28,4 +30,55 @@ struct Config; */ void draw_display_image(const struct Config *config); +// ============================================================================ +// Sensor Slot Helper Functions +// ============================================================================ + +/** + * @brief Check if a sensor slot is active (not "none") + * @param slot_value Slot configuration value ("cpu", "gpu", "liquid", "none") + * @return 1 if active, 0 if "none" or invalid + */ +int slot_is_active(const char *slot_value); + +/** + * @brief Get temperature value for a sensor slot + * @param data Sensor data structure with CPU, GPU, and liquid temperatures + * @param slot_value Slot configuration value ("cpu", "gpu", "liquid") + * @return Temperature in Celsius, or 0.0 if slot is "none" or invalid + */ +float get_slot_temperature(const monitor_sensor_data_t *data, const char *slot_value); + +/** + * @brief Get display label for a sensor slot + * @param slot_value Slot configuration value ("cpu", "gpu", "liquid", "none") + * @return Label string ("CPU", "GPU", "LIQ") or NULL if "none" + */ +const char *get_slot_label(const char *slot_value); + +/** + * @brief Get bar color for a sensor slot based on temperature + * @param config Configuration with threshold colors + * @param slot_value Slot configuration value (determines which thresholds to use) + * @param temperature Current temperature value + * @return Color based on temperature thresholds + */ +Color get_slot_bar_color(const struct Config *config, const char *slot_value, float temperature); + +/** + * @brief Get maximum scale for a sensor slot + * @param config Configuration with max scale values + * @param slot_value Slot configuration value + * @return Maximum temperature scale (liquid uses different max) + */ +float get_slot_max_scale(const struct Config *config, const char *slot_value); + +/** + * @brief Get bar height for a specific slot + * @param config Configuration with bar height values + * @param slot_name Slot name: "up", "mid", or "down" + * @return Bar height in pixels for the specified slot + */ +uint16_t get_slot_bar_height(const struct Config *config, const char *slot_name); + #endif // DISPLAY_DISPATCHER_H diff --git a/src/mods/dual.c b/src/mods/dual.c index 8eb344b..0004fe4 100644 --- a/src/mods/dual.c +++ b/src/mods/dual.c @@ -33,6 +33,7 @@ #include "../srv/cc_conf.h" #include "../srv/cc_main.h" #include "../srv/cc_sensor.h" +#include "display.h" #include "dual.h" // Circle inscribe factor for circular displays (1/√2 ≈ 0.7071) @@ -216,14 +217,15 @@ static void draw_temperature_bars(cairo_t *cr, const monitor_sensor_data_t *data, const struct Config *config, const ScalingParams *params); -static void draw_single_temperature_bar(cairo_t *cr, - const struct Config *config, - const ScalingParams *params, - float temp_value, int bar_x, int bar_y, - int bar_width); +static void draw_single_temperature_bar_slot(cairo_t *cr, + const struct Config *config, + const ScalingParams *params, + const char *slot_value, + float temp_value, int bar_x, int bar_y, + int bar_width, int bar_height); static void draw_labels(cairo_t *cr, const struct Config *config, + const monitor_sensor_data_t *data, const ScalingParams *params); -static Color get_temperature_bar_color(const struct Config *config, float val); static void draw_rounded_rectangle_path(cairo_t *cr, int x, int y, int width, int height, double radius); static cairo_t *create_cairo_context(const struct Config *config, @@ -261,30 +263,7 @@ static void draw_rounded_rectangle_path(cairo_t *cr, int x, int y, int width, } /** - * @brief Calculate color gradient for temperature bars (green → orange → red) - */ -static Color get_temperature_bar_color(const struct Config *config, float val) -{ - const struct - { - float threshold; - Color color; - } temp_ranges[] = {{config->temp_threshold_1, config->temp_threshold_1_bar}, - {config->temp_threshold_2, config->temp_threshold_2_bar}, - {config->temp_threshold_3, config->temp_threshold_3_bar}, - {INFINITY, config->temp_threshold_4_bar}}; - - for (size_t i = 0; i < sizeof(temp_ranges) / sizeof(temp_ranges[0]); i++) - { - if (val <= temp_ranges[i].threshold) - return temp_ranges[i].color; - } - - return config->temp_threshold_4_bar; -} - -/** - * @brief Draw temperature displays for CPU and GPU + * @brief Draw temperature displays for up and down slots */ static void draw_temperature_displays(cairo_t *cr, const monitor_sensor_data_t *data, @@ -297,21 +276,38 @@ static void draw_temperature_displays(cairo_t *cr, const int effective_bar_width = params->safe_bar_width; const int bar_x = (config->display_width - effective_bar_width) / 2; - // Calculate bar positions (same as in draw_temperature_bars) - const int total_height = - 2 * config->layout_bar_height + config->layout_bar_gap; - const int start_y = (config->display_height - total_height) / 2; - const int cpu_bar_y = start_y; - const int gpu_bar_y = - start_y + config->layout_bar_height + config->layout_bar_gap; + // Get slot configurations + const char *slot_up = config->sensor_slot_up; + const char *slot_down = config->sensor_slot_down; + const int up_active = slot_is_active(slot_up); + const int down_active = slot_is_active(slot_down); + + // Get temperatures from configured slots + float temp_up = get_slot_temperature(data, slot_up); + float temp_down = get_slot_temperature(data, slot_down); + + // Get bar heights + const uint16_t bar_height_up = get_slot_bar_height(config, "up"); + const uint16_t bar_height_down = get_slot_bar_height(config, "down"); + + // Calculate bar positions based on active slots + int total_height = 0; + if (up_active && down_active) + total_height = bar_height_up + config->layout_bar_gap + bar_height_down; + else if (up_active) + total_height = bar_height_up; + else if (down_active) + total_height = bar_height_down; + else + return; // No active slots - char cpu_num_str[16], gpu_num_str[16]; - snprintf(cpu_num_str, sizeof(cpu_num_str), "%d", (int)data->temp_cpu); - snprintf(gpu_num_str, sizeof(gpu_num_str), "%d", (int)data->temp_gpu); + const int start_y = (config->display_height - total_height) / 2; + int up_bar_y = start_y; + int down_bar_y = start_y + bar_height_up + config->layout_bar_gap; - cairo_text_extents_t cpu_num_ext, gpu_num_ext; - cairo_text_extents(cr, cpu_num_str, &cpu_num_ext); - cairo_text_extents(cr, gpu_num_str, &gpu_num_ext); + // If only down slot is active, center it + if (!up_active && down_active) + down_bar_y = start_y; cairo_font_extents_t font_ext; cairo_font_extents(cr, &font_ext); @@ -320,111 +316,109 @@ static void draw_temperature_displays(cairo_t *cr, cairo_text_extents_t ref_width_ext; cairo_text_extents(cr, "88", &ref_width_ext); - // Use actual text extents for positioning to handle 3-digit temperatures - // correctly For values ≥100, use actual width; for <100, use fixed width of - // "88" for stability - double cpu_width = - (data->temp_cpu >= 100.0f) ? cpu_num_ext.width : ref_width_ext.width; - double gpu_width = - (data->temp_gpu >= 100.0f) ? gpu_num_ext.width : ref_width_ext.width; - - // Use reference width for values <100 to keep position stable - if (data->temp_cpu < 100.0f) - cpu_width = ref_width_ext.width; - if (data->temp_gpu < 100.0f) - gpu_width = ref_width_ext.width; - - // Center-align based on actual or reference width - double cpu_temp_x = bar_x + (effective_bar_width - cpu_width) / 2.0; - double gpu_temp_x = bar_x + (effective_bar_width - gpu_width) / 2.0; - - // Default offset for small displays (240x240): +20px to the right - if (config->display_width == 240 && config->display_height == 240) + // Draw upper slot temperature + if (up_active) { - cpu_temp_x += 20; - gpu_temp_x += 20; + char up_num_str[16]; + snprintf(up_num_str, sizeof(up_num_str), "%d", (int)temp_up); + + cairo_text_extents_t up_num_ext; + cairo_text_extents(cr, up_num_str, &up_num_ext); + + double up_width = (temp_up >= 100.0f) ? up_num_ext.width : ref_width_ext.width; + double up_temp_x = bar_x + (effective_bar_width - up_width) / 2.0; + + if (config->display_width == 240 && config->display_height == 240) + up_temp_x += 20; + + if (config->display_temp_offset_x_cpu != 0) + up_temp_x += config->display_temp_offset_x_cpu; + + double up_temp_y = up_bar_y + 8 - font_ext.descent; + if (config->display_temp_offset_y_cpu != 0) + up_temp_y += config->display_temp_offset_y_cpu; + + cairo_move_to(cr, up_temp_x, up_temp_y); + cairo_show_text(cr, up_num_str); + + // Degree symbol + const int degree_spacing = (config->display_degree_spacing > 0) ? config->display_degree_spacing : 16; + double degree_up_x = up_temp_x + up_width + degree_spacing; + double degree_up_y = up_temp_y - up_num_ext.height * 0.40; + draw_degree_symbol(cr, degree_up_x, degree_up_y, config); } - // Apply user-defined X offsets if set (0 = automatic positioning) - if (config->display_temp_offset_x_cpu != 0) - cpu_temp_x += config->display_temp_offset_x_cpu; - if (config->display_temp_offset_x_gpu != 0) - gpu_temp_x += config->display_temp_offset_x_gpu; + // Draw lower slot temperature + if (down_active) + { + char down_num_str[16]; + snprintf(down_num_str, sizeof(down_num_str), "%d", (int)temp_down); - // Position temperatures 1px outside bars (same calculation as labels) - // CPU temperature - above bar (same as CPU label) - double cpu_temp_y = cpu_bar_y + 8 - font_ext.descent; + cairo_text_extents_t down_num_ext; + cairo_text_extents(cr, down_num_str, &down_num_ext); - // GPU temperature - below bar (same as GPU label) - double gpu_temp_y = - gpu_bar_y + config->layout_bar_height - 4 + font_ext.ascent; + double down_width = (temp_down >= 100.0f) ? down_num_ext.width : ref_width_ext.width; + double down_temp_x = bar_x + (effective_bar_width - down_width) / 2.0; - // Apply user-defined Y offsets if set - if (config->display_temp_offset_y_cpu != 0) - cpu_temp_y += config->display_temp_offset_y_cpu; - if (config->display_temp_offset_y_gpu != 0) - gpu_temp_y += config->display_temp_offset_y_gpu; + if (config->display_width == 240 && config->display_height == 240) + down_temp_x += 20; - // Draw CPU temperature number - cairo_move_to(cr, cpu_temp_x, cpu_temp_y); - cairo_show_text(cr, cpu_num_str); + if (config->display_temp_offset_x_gpu != 0) + down_temp_x += config->display_temp_offset_x_gpu; - // Draw GPU temperature number - cairo_move_to(cr, gpu_temp_x, gpu_temp_y); - cairo_show_text(cr, gpu_num_str); + // Use the actual bar height for positioning + uint16_t effective_down_height = down_active ? bar_height_down : 0; + double down_temp_y = down_bar_y + effective_down_height - 4 + font_ext.ascent; + if (config->display_temp_offset_y_gpu != 0) + down_temp_y += config->display_temp_offset_y_gpu; - // Draw degree symbols at configurable offset (default: 16px right, bound to - // temperature position) - cairo_set_font_size(cr, config->font_size_temp / 1.66); + cairo_move_to(cr, down_temp_x, down_temp_y); + cairo_show_text(cr, down_num_str); - // Position degree symbols using configured spacing (default: 16px) - const int degree_spacing = (config->display_degree_spacing > 0) - ? config->display_degree_spacing - : 16; - double degree_cpu_x = cpu_temp_x + cpu_width + degree_spacing; - double degree_gpu_x = gpu_temp_x + gpu_width + degree_spacing; - double degree_cpu_y = cpu_temp_y - cpu_num_ext.height * 0.40; - double degree_gpu_y = gpu_temp_y - gpu_num_ext.height * 0.40; - - draw_degree_symbol(cr, degree_cpu_x, degree_cpu_y, config); - draw_degree_symbol(cr, degree_gpu_x, degree_gpu_y, config); + // Degree symbol + const int degree_spacing = (config->display_degree_spacing > 0) ? config->display_degree_spacing : 16; + double degree_down_x = down_temp_x + down_width + degree_spacing; + double degree_down_y = down_temp_y - down_num_ext.height * 0.40; + draw_degree_symbol(cr, degree_down_x, degree_down_y, config); + } } /** * @brief Draw a single temperature bar with background, fill, and border */ -static void draw_single_temperature_bar(cairo_t *cr, - const struct Config *config, - const ScalingParams *params, - float temp_value, int bar_x, int bar_y, - int bar_width) +static void draw_single_temperature_bar_slot(cairo_t *cr, + const struct Config *config, + const ScalingParams *params, + const char *slot_value, + float temp_value, int bar_x, int bar_y, + int bar_width, int bar_height) { if (!cr || !config || !params) return; - // Use configured maximum temperature for bar scaling (default: 115°C) - const float max_temp = config->temp_max_scale; + // Use slot-specific max scale and color + const float max_temp = get_slot_max_scale(config, slot_value); const int fill_width = calculate_temp_fill_width(temp_value, bar_width, max_temp); // Background set_cairo_color(cr, &config->layout_bar_color_background); draw_rounded_rectangle_path(cr, bar_x, bar_y, bar_width, - config->layout_bar_height, params->corner_radius); + bar_height, params->corner_radius); cairo_fill(cr); // Fill if (fill_width > 0) { - Color fill_color = get_temperature_bar_color(config, temp_value); + Color fill_color = get_slot_bar_color(config, slot_value, temp_value); set_cairo_color(cr, &fill_color); if (fill_width >= 16) draw_rounded_rectangle_path(cr, bar_x, bar_y, fill_width, - config->layout_bar_height, + bar_height, params->corner_radius); else - cairo_rectangle(cr, bar_x, bar_y, fill_width, config->layout_bar_height); + cairo_rectangle(cr, bar_x, bar_y, fill_width, bar_height); cairo_fill(cr); } @@ -435,13 +429,13 @@ static void draw_single_temperature_bar(cairo_t *cr, cairo_set_line_width(cr, config->layout_bar_border); set_cairo_color(cr, &config->layout_bar_color_border); draw_rounded_rectangle_path(cr, bar_x, bar_y, bar_width, - config->layout_bar_height, params->corner_radius); + bar_height, params->corner_radius); cairo_stroke(cr); } } /** - * @brief Draw temperature bars for CPU and GPU + * @brief Draw temperature bars for up and down slots */ static void draw_temperature_bars(cairo_t *cr, const monitor_sensor_data_t *data, @@ -456,39 +450,96 @@ static void draw_temperature_bars(cairo_t *cr, params->safe_bar_width - (int)(2 * bar_side_margin); const int bar_x = (config->display_width - effective_bar_width) / 2; - const int total_height = - 2 * config->layout_bar_height + config->layout_bar_gap; + // Get slot configurations + const char *slot_up = config->sensor_slot_up; + const char *slot_down = config->sensor_slot_down; + const int up_active = slot_is_active(slot_up); + const int down_active = slot_is_active(slot_down); + + // Get bar heights + const uint16_t bar_height_up = get_slot_bar_height(config, "up"); + const uint16_t bar_height_down = get_slot_bar_height(config, "down"); + + // Calculate positions based on active slots + int total_height = 0; + if (up_active && down_active) + total_height = bar_height_up + config->layout_bar_gap + bar_height_down; + else if (up_active) + total_height = bar_height_up; + else if (down_active) + total_height = bar_height_down; + else + return; + const int start_y = (config->display_height - total_height) / 2; + int up_bar_y = start_y; + int down_bar_y = start_y + bar_height_up + config->layout_bar_gap; - const int cpu_bar_y = start_y; - const int gpu_bar_y = - start_y + config->layout_bar_height + config->layout_bar_gap; + // If only down slot is active, center it + if (!up_active && down_active) + down_bar_y = start_y; - draw_single_temperature_bar(cr, config, params, data->temp_cpu, bar_x, - cpu_bar_y, effective_bar_width); - draw_single_temperature_bar(cr, config, params, data->temp_gpu, bar_x, - gpu_bar_y, effective_bar_width); + // Draw upper slot bar + if (up_active) + { + float temp_up = get_slot_temperature(data, slot_up); + draw_single_temperature_bar_slot(cr, config, params, slot_up, temp_up, + bar_x, up_bar_y, effective_bar_width, bar_height_up); + } + + // Draw lower slot bar + if (down_active) + { + float temp_down = get_slot_temperature(data, slot_down); + draw_single_temperature_bar_slot(cr, config, params, slot_down, temp_down, + bar_x, down_bar_y, effective_bar_width, bar_height_down); + } } /** - * @brief Draw CPU/GPU labels (only for displays ≤240×240) + * @brief Draw labels for up and down slots */ static void draw_labels(cairo_t *cr, const struct Config *config, + const monitor_sensor_data_t *data, const ScalingParams *params) { + (void)data; // Reserved for future use (e.g., dynamic labels based on values) + if (!cr || !config || !params) return; - // Only show labels on small displays - // if (config->display_width > 240 || config->display_height > 240) - // return; + // Get slot configurations + const char *slot_up = config->sensor_slot_up; + const char *slot_down = config->sensor_slot_down; + const int up_active = slot_is_active(slot_up); + const int down_active = slot_is_active(slot_down); + + // Get labels from slots (NULL if "none") + const char *label_up = get_slot_label(slot_up); + const char *label_down = get_slot_label(slot_down); + + // Get bar heights + const uint16_t bar_height_up = get_slot_bar_height(config, "up"); + const uint16_t bar_height_down = get_slot_bar_height(config, "down"); + + // Calculate total height based on active slots + int total_height = 0; + if (up_active && down_active) + total_height = bar_height_up + config->layout_bar_gap + bar_height_down; + else if (up_active) + total_height = bar_height_up; + else if (down_active) + total_height = bar_height_down; + else + return; - const int total_height = - 2 * config->layout_bar_height + config->layout_bar_gap; const int start_y = (config->display_height - total_height) / 2; - const int cpu_bar_y = start_y; - const int gpu_bar_y = - start_y + config->layout_bar_height + config->layout_bar_gap; + int up_bar_y = start_y; + int down_bar_y = start_y + bar_height_up + config->layout_bar_gap; + + // If only down slot is active, center it + if (!up_active && down_active) + down_bar_y = start_y; // Labels: Configurable distance from left screen edge (default: 1%) const double left_margin_factor = @@ -513,25 +564,27 @@ static void draw_labels(cairo_t *cr, const struct Config *config, : 0.01; const double label_spacing = config->display_height * bar_margin_factor; - // CPU label - 1% above CPU bar - double cpu_label_y = cpu_bar_y - label_spacing - font_ext.descent; - - // GPU label - 1% below GPU bar - double gpu_label_y = - gpu_bar_y + config->layout_bar_height + label_spacing + font_ext.ascent; - - // Apply user-defined Y offset if set - if (config->display_label_offset_y != -9999) + // Draw upper slot label (if active and has label) + if (up_active && label_up) { - cpu_label_y += config->display_label_offset_y; - gpu_label_y += config->display_label_offset_y; + double up_label_y = up_bar_y - label_spacing - font_ext.descent; + if (config->display_label_offset_y != -9999) + up_label_y += config->display_label_offset_y; + + cairo_move_to(cr, label_x, up_label_y); + cairo_show_text(cr, label_up); } - cairo_move_to(cr, label_x, cpu_label_y); - cairo_show_text(cr, "CPU"); + // Draw lower slot label (if active and has label) + if (down_active && label_down) + { + double down_label_y = down_bar_y + bar_height_down + label_spacing + font_ext.ascent; + if (config->display_label_offset_y != -9999) + down_label_y += config->display_label_offset_y; - cairo_move_to(cr, label_x, gpu_label_y); - cairo_show_text(cr, "GPU"); + cairo_move_to(cr, label_x, down_label_y); + cairo_show_text(cr, label_down); + } } /** @@ -580,12 +633,16 @@ static void render_display_content(cairo_t *cr, const struct Config *config, draw_temperature_displays(cr, data, config, params); draw_temperature_bars(cr, data, config, params); - // Labels only if temp < 99°C - if (data->temp_cpu < 99.0 && data->temp_gpu < 99.0) + // Get temperatures from active slots for label visibility check + float temp_up = get_slot_temperature(data, config->sensor_slot_up); + float temp_down = get_slot_temperature(data, config->sensor_slot_down); + + // Labels only if both temps < 99°C (to avoid overlap with large numbers) + if (temp_up < 99.0f && temp_down < 99.0f) { cairo_set_font_size(cr, config->font_size_labels); set_cairo_color(cr, &config->font_color_label); - draw_labels(cr, config, params); + draw_labels(cr, config, data, params); } } From 69d97b54d24f92819d34693f3f6f4f654adadc8c Mon Sep 17 00:00:00 2001 From: damachine Date: Wed, 4 Feb 2026 00:06:11 +0100 Subject: [PATCH 2/6] GUI: improve configuration options --- .../plugins/coolerdash/config.json | 13 +- .../plugins/coolerdash/ui/index.html | 731 ++++++++---------- src/device/config.c | 127 ++- src/device/config.h | 30 +- src/mods/display.c | 37 +- 5 files changed, 468 insertions(+), 470 deletions(-) diff --git a/etc/coolercontrol/plugins/coolerdash/config.json b/etc/coolercontrol/plugins/coolerdash/config.json index 6d1923b..5b49916 100644 --- a/etc/coolercontrol/plugins/coolerdash/config.json +++ b/etc/coolercontrol/plugins/coolerdash/config.json @@ -53,7 +53,18 @@ "size_labels": 30.0 }, - "temperature": { + "cpu": { + "threshold_1": 55.0, + "threshold_2": 65.0, + "threshold_3": 75.0, + "max_scale": 115.0, + "threshold_1_color": { "r": 0, "g": 255, "b": 0 }, + "threshold_2_color": { "r": 255, "g": 140, "b": 0 }, + "threshold_3_color": { "r": 255, "g": 70, "b": 0 }, + "threshold_4_color": { "r": 255, "g": 0, "b": 0 } + }, + + "gpu": { "threshold_1": 55.0, "threshold_2": 65.0, "threshold_3": 75.0, diff --git a/etc/coolercontrol/plugins/coolerdash/ui/index.html b/etc/coolercontrol/plugins/coolerdash/ui/index.html index 8c30e81..9f70a98 100644 --- a/etc/coolercontrol/plugins/coolerdash/ui/index.html +++ b/etc/coolercontrol/plugins/coolerdash/ui/index.html @@ -61,25 +61,26 @@ /* Tabs */ .tabs { display: flex; - gap: 8px; + gap: 4px; margin-bottom: 20px; background: var(--bg-card); padding: 8px; border-radius: var(--radius); box-shadow: var(--shadow); overflow-x: auto; + flex-wrap: wrap; } .tab { flex: 1; - min-width: 100px; - padding: 12px 16px; + min-width: 80px; + padding: 10px 12px; background: transparent; border: none; border-radius: 6px; color: var(--text-dim); cursor: pointer; - font-size: 14px; + font-size: 13px; font-weight: 500; transition: all 0.2s; white-space: nowrap; @@ -114,6 +115,19 @@ to { opacity: 1; transform: translateY(0); } } + /* Section Title */ + .section-title { + margin: 24px 0 16px; + font-size: 16px; + color: var(--text-dim); + border-bottom: 1px solid var(--border); + padding-bottom: 8px; + } + + .section-title:first-child { + margin-top: 0; + } + /* Form Elements */ .form-group { margin-bottom: 20px; @@ -166,6 +180,7 @@ .grid-2 { grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); } .grid-3 { grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); } + .grid-4 { grid-template-columns: repeat(auto-fit, minmax(120px, 1fr)); } /* Range Slider */ .range-group { @@ -285,6 +300,11 @@ margin-bottom: 4px; } + .alert-warning { + background: rgba(255, 140, 0, 0.15); + border-left-color: #ff8c00; + } + /* Scrollbar */ ::-webkit-scrollbar { width: 8px; height: 8px; } ::-webkit-scrollbar-track { background: var(--bg-secondary); } @@ -296,9 +316,7 @@
-

- CoolerDash Configuration -

+

CoolerDash Configuration

Customize your LCD temperature dashboard • Changes require plugin restart

@@ -306,14 +324,17 @@

- - - - + + + + + + +
- +
CoolerControl API @@ -322,7 +343,7 @@

- Default: http://localhost:11987 + HTTP or HTTPS supported (Default: http://localhost:11987)
@@ -333,32 +354,35 @@

- +
-
- - -
+

Display Mode

+ +
+
+ + +
- -

Sensor Slots

+

Sensor Slots

Sensor Assignment - Assign sensors to display positions. Use "None" to disable a slot. At least one slot must be active. + Assign sensors to display positions. Use "None" to disable a slot.
- Top position sensor @@ -380,7 +404,6 @@

Sensor
- Bottom position sensor

- + +
+

Display Colors

- Main background color
@@ -561,7 +588,10 @@

Colors

+
+

Text Colors

+
@@ -578,121 +608,171 @@

Colors

+
-

Font

+ +
+

CPU Temperature Scale

+
+ + Maximum temperature for bar scale + +
+

CPU Thresholds

- - Default: Roboto Black - + + Green → Orange +
- - + + Orange → Dark Orange +
- - + + Dark Orange → Red + +
+
+ +

CPU Bar Colors

+
+
+ +
+ + +
+
+ +
+ +
+ + +
+
+ +
+ +
+ + +
+
+ +
+ +
+ + +
- +
+

GPU Temperature Scale

- Maximum temperature for scale - + Maximum temperature for bar scale +
-

Thresholds

- +

GPU Thresholds

- + Green → Orange +
- + Orange → Dark Orange +
- + Dark Orange → Red +
-

Colors

- +

GPU Bar Colors

- - + +
- - + +
- - + +
- - + +
- +
+

Liquid Temperature Scale

- Maximum liquid temperature for scale + Maximum temperature for bar scale (typically 50°C)
-

Thresholds

- +

Liquid Thresholds

+ Green → Orange
+ Orange → Dark Orange
+ Dark Orange → Red
-

Colors

- +

Liquid Bar Colors

@@ -728,14 +808,14 @@

Colors

- +
Advanced Positioning - Value 0 = Automatic positioning + Value 0 = Automatic positioning. Adjust offsets to fine-tune element placement.
-

CPU Temperature

+

Upper Slot Position

@@ -748,33 +828,33 @@

CPU Te

-

GPU Temperature

+

Middle Slot Position

- +
- +
-

Liquid Temperature

+

Lower Slot Position

- +
- +
-

Labels

+

Labels Position

@@ -792,16 +872,33 @@

Labels

+ + +
+

Image Paths

+
+ Custom Images + Specify custom paths for plugin images. Leave default for standard installation. +
+ +
+ + Base directory for plugin images + +
+ +
+ + Image displayed when daemon shuts down + +
+
- - + +
@@ -814,7 +911,6 @@

Labels diff --git a/src/device/config.c b/src/device/config.c index ca873f1..a1639fe 100644 --- a/src/device/config.c +++ b/src/device/config.c @@ -243,23 +243,35 @@ static void set_font_defaults(Config *config) */ static void set_temperature_defaults(Config *config) { - if (config->temp_threshold_1 == 0.0f) - config->temp_threshold_1 = 55.0f; - if (config->temp_threshold_2 == 0.0f) - config->temp_threshold_2 = 65.0f; - if (config->temp_threshold_3 == 0.0f) - config->temp_threshold_3 = 75.0f; - if (config->temp_max_scale == 0.0f) - config->temp_max_scale = 115.0f; - - if (config->temp_liquid_max_scale == 0.0f) - config->temp_liquid_max_scale = 50.0f; + // CPU temperature defaults + if (config->temp_cpu_threshold_1 == 0.0f) + config->temp_cpu_threshold_1 = 55.0f; + if (config->temp_cpu_threshold_2 == 0.0f) + config->temp_cpu_threshold_2 = 65.0f; + if (config->temp_cpu_threshold_3 == 0.0f) + config->temp_cpu_threshold_3 = 75.0f; + if (config->temp_cpu_max_scale == 0.0f) + config->temp_cpu_max_scale = 115.0f; + + // GPU temperature defaults (same as CPU) + if (config->temp_gpu_threshold_1 == 0.0f) + config->temp_gpu_threshold_1 = 55.0f; + if (config->temp_gpu_threshold_2 == 0.0f) + config->temp_gpu_threshold_2 = 65.0f; + if (config->temp_gpu_threshold_3 == 0.0f) + config->temp_gpu_threshold_3 = 75.0f; + if (config->temp_gpu_max_scale == 0.0f) + config->temp_gpu_max_scale = 115.0f; + + // Liquid temperature defaults if (config->temp_liquid_threshold_1 == 0.0f) config->temp_liquid_threshold_1 = 25.0f; if (config->temp_liquid_threshold_2 == 0.0f) config->temp_liquid_threshold_2 = 28.0f; if (config->temp_liquid_threshold_3 == 0.0f) config->temp_liquid_threshold_3 = 31.0f; + if (config->temp_liquid_max_scale == 0.0f) + config->temp_liquid_max_scale = 50.0f; } /** @@ -291,10 +303,17 @@ static void set_color_defaults(Config *config) {&config->layout_bar_color_border, 192, 192, 192}, {&config->font_color_temp, 255, 255, 255}, {&config->font_color_label, 200, 200, 200}, - {&config->temp_threshold_1_bar, 0, 255, 0}, - {&config->temp_threshold_2_bar, 255, 140, 0}, - {&config->temp_threshold_3_bar, 255, 70, 0}, - {&config->temp_threshold_4_bar, 255, 0, 0}, + // CPU temperature colors + {&config->temp_cpu_threshold_1_bar, 0, 255, 0}, + {&config->temp_cpu_threshold_2_bar, 255, 140, 0}, + {&config->temp_cpu_threshold_3_bar, 255, 70, 0}, + {&config->temp_cpu_threshold_4_bar, 255, 0, 0}, + // GPU temperature colors (same as CPU) + {&config->temp_gpu_threshold_1_bar, 0, 255, 0}, + {&config->temp_gpu_threshold_2_bar, 255, 140, 0}, + {&config->temp_gpu_threshold_3_bar, 255, 70, 0}, + {&config->temp_gpu_threshold_4_bar, 255, 0, 0}, + // Liquid temperature colors {&config->temp_liquid_threshold_1_bar, 0, 255, 0}, {&config->temp_liquid_threshold_2_bar, 255, 140, 0}, {&config->temp_liquid_threshold_3_bar, 255, 70, 0}, @@ -477,10 +496,8 @@ static const char *find_config_json(const char *custom_path) } static const char *possible_paths[] = { + "~/.config/coolerdash/config.json", "/etc/coolercontrol/plugins/coolerdash/config.json", - "/etc/coolercontrol/plugins/coolerdash/plugin-config.json", - "/etc/coolercontrol/config/plugins/coolerdash.json", - "./config.json", NULL}; for (int i = 0; possible_paths[i] != NULL; i++) @@ -823,42 +840,81 @@ static void load_font_from_json(json_t *root, Config *config) } /** - * @brief Load temperature settings from JSON + * @brief Load CPU temperature settings from JSON + */ +static void load_cpu_temperature_from_json(json_t *root, Config *config) +{ + json_t *cpu = json_object_get(root, "cpu"); + if (!cpu || !json_is_object(cpu)) + return; + + json_t *threshold_1 = json_object_get(cpu, "threshold_1"); + if (threshold_1 && json_is_number(threshold_1)) + { + config->temp_cpu_threshold_1 = (float)json_number_value(threshold_1); + } + + json_t *threshold_2 = json_object_get(cpu, "threshold_2"); + if (threshold_2 && json_is_number(threshold_2)) + { + config->temp_cpu_threshold_2 = (float)json_number_value(threshold_2); + } + + json_t *threshold_3 = json_object_get(cpu, "threshold_3"); + if (threshold_3 && json_is_number(threshold_3)) + { + config->temp_cpu_threshold_3 = (float)json_number_value(threshold_3); + } + + json_t *max_scale = json_object_get(cpu, "max_scale"); + if (max_scale && json_is_number(max_scale)) + { + config->temp_cpu_max_scale = (float)json_number_value(max_scale); + } + + read_color_from_json(json_object_get(cpu, "threshold_1_color"), &config->temp_cpu_threshold_1_bar); + read_color_from_json(json_object_get(cpu, "threshold_2_color"), &config->temp_cpu_threshold_2_bar); + read_color_from_json(json_object_get(cpu, "threshold_3_color"), &config->temp_cpu_threshold_3_bar); + read_color_from_json(json_object_get(cpu, "threshold_4_color"), &config->temp_cpu_threshold_4_bar); +} + +/** + * @brief Load GPU temperature settings from JSON */ -static void load_temperature_from_json(json_t *root, Config *config) +static void load_gpu_temperature_from_json(json_t *root, Config *config) { - json_t *temperature = json_object_get(root, "temperature"); - if (!temperature || !json_is_object(temperature)) + json_t *gpu = json_object_get(root, "gpu"); + if (!gpu || !json_is_object(gpu)) return; - json_t *threshold_1 = json_object_get(temperature, "threshold_1"); + json_t *threshold_1 = json_object_get(gpu, "threshold_1"); if (threshold_1 && json_is_number(threshold_1)) { - config->temp_threshold_1 = (float)json_number_value(threshold_1); + config->temp_gpu_threshold_1 = (float)json_number_value(threshold_1); } - json_t *threshold_2 = json_object_get(temperature, "threshold_2"); + json_t *threshold_2 = json_object_get(gpu, "threshold_2"); if (threshold_2 && json_is_number(threshold_2)) { - config->temp_threshold_2 = (float)json_number_value(threshold_2); + config->temp_gpu_threshold_2 = (float)json_number_value(threshold_2); } - json_t *threshold_3 = json_object_get(temperature, "threshold_3"); + json_t *threshold_3 = json_object_get(gpu, "threshold_3"); if (threshold_3 && json_is_number(threshold_3)) { - config->temp_threshold_3 = (float)json_number_value(threshold_3); + config->temp_gpu_threshold_3 = (float)json_number_value(threshold_3); } - json_t *max_scale = json_object_get(temperature, "max_scale"); + json_t *max_scale = json_object_get(gpu, "max_scale"); if (max_scale && json_is_number(max_scale)) { - config->temp_max_scale = (float)json_number_value(max_scale); + config->temp_gpu_max_scale = (float)json_number_value(max_scale); } - read_color_from_json(json_object_get(temperature, "threshold_1_color"), &config->temp_threshold_1_bar); - read_color_from_json(json_object_get(temperature, "threshold_2_color"), &config->temp_threshold_2_bar); - read_color_from_json(json_object_get(temperature, "threshold_3_color"), &config->temp_threshold_3_bar); - read_color_from_json(json_object_get(temperature, "threshold_4_color"), &config->temp_threshold_4_bar); + read_color_from_json(json_object_get(gpu, "threshold_1_color"), &config->temp_gpu_threshold_1_bar); + read_color_from_json(json_object_get(gpu, "threshold_2_color"), &config->temp_gpu_threshold_2_bar); + read_color_from_json(json_object_get(gpu, "threshold_3_color"), &config->temp_gpu_threshold_3_bar); + read_color_from_json(json_object_get(gpu, "threshold_4_color"), &config->temp_gpu_threshold_4_bar); } /** @@ -1004,7 +1060,8 @@ int load_plugin_config(Config *config, const char *config_path) load_layout_from_json(root, config); load_colors_from_json(root, config); load_font_from_json(root, config); - load_temperature_from_json(root, config); + load_cpu_temperature_from_json(root, config); + load_gpu_temperature_from_json(root, config); load_liquid_from_json(root, config); load_positioning_from_json(root, config); diff --git a/src/device/config.h b/src/device/config.h index 375a872..e54241e 100644 --- a/src/device/config.h +++ b/src/device/config.h @@ -121,21 +121,31 @@ typedef struct Config int display_label_offset_x; int display_label_offset_y; - // Temperature configuration - float temp_threshold_1; - float temp_threshold_2; - float temp_threshold_3; - float temp_max_scale; - Color temp_threshold_1_bar; - Color temp_threshold_2_bar; - Color temp_threshold_3_bar; - Color temp_threshold_4_bar; + // CPU temperature configuration + float temp_cpu_threshold_1; + float temp_cpu_threshold_2; + float temp_cpu_threshold_3; + float temp_cpu_max_scale; + Color temp_cpu_threshold_1_bar; + Color temp_cpu_threshold_2_bar; + Color temp_cpu_threshold_3_bar; + Color temp_cpu_threshold_4_bar; + + // GPU temperature configuration + float temp_gpu_threshold_1; + float temp_gpu_threshold_2; + float temp_gpu_threshold_3; + float temp_gpu_max_scale; + Color temp_gpu_threshold_1_bar; + Color temp_gpu_threshold_2_bar; + Color temp_gpu_threshold_3_bar; + Color temp_gpu_threshold_4_bar; // Liquid temperature configuration - float temp_liquid_max_scale; float temp_liquid_threshold_1; float temp_liquid_threshold_2; float temp_liquid_threshold_3; + float temp_liquid_max_scale; Color temp_liquid_threshold_1_bar; Color temp_liquid_threshold_2_bar; Color temp_liquid_threshold_3_bar; diff --git a/src/mods/display.c b/src/mods/display.c index 5603c55..4ce208d 100644 --- a/src/mods/display.c +++ b/src/mods/display.c @@ -105,15 +105,28 @@ Color get_slot_bar_color(const struct Config *config, const char *slot_value, fl return config->temp_liquid_threshold_4_bar; } - // CPU and GPU use global temperature thresholds - if (temperature < config->temp_threshold_1) - return config->temp_threshold_1_bar; - else if (temperature < config->temp_threshold_2) - return config->temp_threshold_2_bar; - else if (temperature < config->temp_threshold_3) - return config->temp_threshold_3_bar; + // GPU uses separate thresholds + if (strcmp(slot_value, "gpu") == 0) + { + if (temperature < config->temp_gpu_threshold_1) + return config->temp_gpu_threshold_1_bar; + else if (temperature < config->temp_gpu_threshold_2) + return config->temp_gpu_threshold_2_bar; + else if (temperature < config->temp_gpu_threshold_3) + return config->temp_gpu_threshold_3_bar; + else + return config->temp_gpu_threshold_4_bar; + } + + // CPU (default) uses separate thresholds + if (temperature < config->temp_cpu_threshold_1) + return config->temp_cpu_threshold_1_bar; + else if (temperature < config->temp_cpu_threshold_2) + return config->temp_cpu_threshold_2_bar; + else if (temperature < config->temp_cpu_threshold_3) + return config->temp_cpu_threshold_3_bar; else - return config->temp_threshold_4_bar; + return config->temp_cpu_threshold_4_bar; } /** @@ -128,8 +141,12 @@ float get_slot_max_scale(const struct Config *config, const char *slot_value) if (slot_value && strcmp(slot_value, "liquid") == 0) return config->temp_liquid_max_scale; - // CPU and GPU use global max scale - return config->temp_max_scale; + // GPU has its own max scale + if (slot_value && strcmp(slot_value, "gpu") == 0) + return config->temp_gpu_max_scale; + + // CPU (default) max scale + return config->temp_cpu_max_scale; } /** From 1e20748fb107cc2b7938600c1b806b834eb196e0 Mon Sep 17 00:00:00 2001 From: damachine Date: Wed, 4 Feb 2026 00:31:34 +0100 Subject: [PATCH 3/6] feat: Add all config options adjustable via GUI --- .../plugins/coolerdash/config.json | 1 + .../plugins/coolerdash/ui/index.html | 311 ++++++++++++------ 2 files changed, 206 insertions(+), 106 deletions(-) diff --git a/etc/coolercontrol/plugins/coolerdash/config.json b/etc/coolercontrol/plugins/coolerdash/config.json index 5b49916..898ac3b 100644 --- a/etc/coolercontrol/plugins/coolerdash/config.json +++ b/etc/coolercontrol/plugins/coolerdash/config.json @@ -33,6 +33,7 @@ "bar_width": 98, "bar_gap": 12, "bar_border": 2.0, + "bar_border_enabled": true, "label_margin_left": 1, "label_margin_bar": 1, "bar_height_up": 0, diff --git a/etc/coolercontrol/plugins/coolerdash/ui/index.html b/etc/coolercontrol/plugins/coolerdash/ui/index.html index 9f70a98..0ab76f3 100644 --- a/etc/coolercontrol/plugins/coolerdash/ui/index.html +++ b/etc/coolercontrol/plugins/coolerdash/ui/index.html @@ -12,26 +12,33 @@ --bg-primary: rgb(26, 26, 46); --bg-secondary: rgb(22, 33, 62); --bg-card: rgb(30, 35, 55); + --bg-input: rgb(20, 28, 48); --border: rgb(45, 66, 99); + --border-light: rgb(55, 76, 109); --text: rgb(234, 234, 234); - --text-dim: rgb(160, 160, 180); + --text-dim: rgb(140, 145, 165); + --text-muted: rgb(100, 105, 125); --accent: rgb(233, 69, 96); --accent-hover: rgb(255, 89, 116); + --accent-glow: rgba(233, 69, 96, 0.15); --success: rgb(34, 197, 94); - --radius: 8px; - --shadow: 0 2px 8px rgba(0, 0, 0, 0.3); + --radius: 10px; + --radius-sm: 6px; + --shadow: 0 4px 20px rgba(0, 0, 0, 0.25); + --shadow-sm: 0 2px 8px rgba(0, 0, 0, 0.2); } body { - font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; - background: var(--bg-secondary); + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Inter', sans-serif; + background: linear-gradient(135deg, var(--bg-secondary) 0%, var(--bg-primary) 100%); color: var(--text); line-height: 1.6; - padding: 20px; + padding: 24px; + min-height: 100vh; } .container { - max-width: 900px; + max-width: 920px; margin: 0 auto; } @@ -39,18 +46,23 @@ .header { background: var(--bg-card); border-radius: var(--radius); - padding: 24px; + padding: 28px 32px; margin-bottom: 20px; box-shadow: var(--shadow); + border: 1px solid var(--border); } .header h1 { - font-size: 24px; + font-size: 26px; font-weight: 700; - margin-bottom: 8px; + margin-bottom: 10px; display: flex; align-items: center; gap: 12px; + background: linear-gradient(135deg, var(--text) 0%, var(--text-dim) 100%); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + background-clip: text; } .header p { @@ -61,39 +73,41 @@ /* Tabs */ .tabs { display: flex; - gap: 4px; + gap: 6px; margin-bottom: 20px; background: var(--bg-card); - padding: 8px; + padding: 10px; border-radius: var(--radius); - box-shadow: var(--shadow); + box-shadow: var(--shadow-sm); overflow-x: auto; flex-wrap: wrap; + border: 1px solid var(--border); } .tab { flex: 1; - min-width: 80px; - padding: 10px 12px; + min-width: 85px; + padding: 11px 14px; background: transparent; border: none; - border-radius: 6px; + border-radius: var(--radius-sm); color: var(--text-dim); cursor: pointer; font-size: 13px; font-weight: 500; - transition: all 0.2s; + transition: all 0.2s ease; white-space: nowrap; } .tab:hover { - background: rgba(233, 69, 96, 0.1); + background: var(--accent-glow); color: var(--text); } .tab.active { background: var(--accent); color: white; + box-shadow: 0 2px 8px rgba(233, 69, 96, 0.4); } /* Panel */ @@ -101,9 +115,10 @@ display: none; background: var(--bg-card); border-radius: var(--radius); - padding: 24px; + padding: 28px 32px; box-shadow: var(--shadow); - animation: fadeIn 0.3s; + animation: fadeIn 0.3s ease; + border: 1px solid var(--border); } .panel.active { @@ -111,17 +126,20 @@ } @keyframes fadeIn { - from { opacity: 0; transform: translateY(10px); } + from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: translateY(0); } } /* Section Title */ .section-title { - margin: 24px 0 16px; - font-size: 16px; - color: var(--text-dim); + margin: 32px 0 20px; + font-size: 13px; + font-weight: 600; + color: var(--accent); border-bottom: 1px solid var(--border); - padding-bottom: 8px; + padding-bottom: 10px; + text-transform: uppercase; + letter-spacing: 1px; } .section-title:first-child { @@ -130,134 +148,163 @@ /* Form Elements */ .form-group { - margin-bottom: 20px; - } - - .form-group:last-child { margin-bottom: 0; } .form-label { display: block; font-weight: 500; - margin-bottom: 6px; - font-size: 14px; + margin-bottom: 8px; + font-size: 13px; + color: var(--text); } .form-hint { display: block; - font-size: 12px; - color: var(--text-dim); + font-size: 11px; + color: var(--text-muted); margin-bottom: 8px; } .form-control { width: 100%; - padding: 10px 12px; - background: var(--bg-secondary); + padding: 11px 14px; + background: var(--bg-input); border: 1px solid var(--border); - border-radius: 6px; + border-radius: var(--radius-sm); color: var(--text); font-size: 14px; - transition: all 0.2s; + transition: all 0.2s ease; + } + + .form-control:hover { + border-color: var(--border-light); } .form-control:focus { outline: none; border-color: var(--accent); - box-shadow: 0 0 0 3px rgba(233, 69, 96, 0.1); + box-shadow: 0 0 0 3px var(--accent-glow); + background: var(--bg-secondary); } .form-control[type="number"] { - max-width: 150px; + width: 100%; + font-variant-numeric: tabular-nums; + } + + select.form-control { + width: 100%; + cursor: pointer; } /* Grid Layout */ .grid { display: grid; - gap: 20px; + gap: 20px 28px; } - .grid-2 { grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); } - .grid-3 { grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); } - .grid-4 { grid-template-columns: repeat(auto-fit, minmax(120px, 1fr)); } + .grid-2 { grid-template-columns: repeat(2, 1fr); } + .grid-3 { grid-template-columns: repeat(3, 1fr); } + .grid-4 { grid-template-columns: repeat(4, 1fr); } + + @media (max-width: 768px) { + .grid-2, .grid-3, .grid-4 { grid-template-columns: 1fr; } + .panel { padding: 20px; } + .header { padding: 20px; } + } + + @media (min-width: 769px) and (max-width: 900px) { + .grid-3, .grid-4 { grid-template-columns: repeat(2, 1fr); } + } /* Range Slider */ .range-group { display: flex; align-items: center; - gap: 12px; + gap: 16px; } .range-input { flex: 1; height: 6px; - background: var(--bg-secondary); + background: var(--bg-input); border-radius: 3px; outline: none; -webkit-appearance: none; + border: 1px solid var(--border); } .range-input::-webkit-slider-thumb { -webkit-appearance: none; - width: 18px; - height: 18px; + width: 20px; + height: 20px; background: var(--accent); border-radius: 50%; cursor: pointer; + box-shadow: 0 2px 6px rgba(233, 69, 96, 0.4); + transition: transform 0.15s ease; + } + + .range-input::-webkit-slider-thumb:hover { + transform: scale(1.1); } .range-value { - min-width: 50px; + min-width: 55px; text-align: right; font-weight: 600; color: var(--accent); + font-size: 14px; + font-variant-numeric: tabular-nums; } /* Color Picker */ .color-group { display: flex; align-items: center; - gap: 12px; + gap: 14px; } .color-swatch { - width: 50px; - height: 40px; - border-radius: 6px; + width: 52px; + height: 42px; + border-radius: var(--radius-sm); border: 2px solid var(--border); cursor: pointer; - transition: transform 0.2s; + transition: all 0.2s ease; } .color-swatch:hover { transform: scale(1.05); + border-color: var(--border-light); } .color-rgb { - font-family: 'Courier New', monospace; - font-size: 13px; - color: var(--text-dim); + font-family: 'SF Mono', 'Fira Code', 'Courier New', monospace; + font-size: 12px; + color: var(--text-muted); + letter-spacing: 0.3px; } /* Buttons */ .btn-group { display: flex; - gap: 12px; - margin-top: 24px; - padding-top: 24px; + gap: 14px; + margin-top: 28px; + padding-top: 28px; border-top: 1px solid var(--border); } .btn { flex: 1; - padding: 12px 24px; + padding: 13px 28px; border: none; - border-radius: 6px; + border-radius: var(--radius-sm); font-size: 14px; font-weight: 600; cursor: pointer; - transition: all 0.2s; + transition: all 0.2s ease; display: flex; align-items: center; justify-content: center; @@ -265,49 +312,51 @@ } .btn-primary { - background: var(--accent); + background: linear-gradient(135deg, var(--accent) 0%, var(--accent-hover) 100%); color: white; + box-shadow: 0 4px 14px rgba(233, 69, 96, 0.35); } .btn-primary:hover { - background: var(--accent-hover); transform: translateY(-2px); - box-shadow: 0 4px 12px rgba(233, 69, 96, 0.3); + box-shadow: 0 6px 20px rgba(233, 69, 96, 0.45); } .btn-secondary { - background: var(--bg-secondary); + background: var(--bg-input); color: var(--text); border: 1px solid var(--border); } .btn-secondary:hover { - background: var(--bg-primary); + background: var(--bg-secondary); + border-color: var(--border-light); } /* Alert */ .alert { - background: rgba(233, 69, 96, 0.1); + background: var(--accent-glow); border-left: 4px solid var(--accent); - padding: 12px 16px; - border-radius: 6px; - margin-bottom: 20px; + padding: 14px 18px; + border-radius: var(--radius-sm); + margin-bottom: 24px; font-size: 13px; } .alert strong { display: block; - margin-bottom: 4px; + margin-bottom: 6px; + color: var(--text); } .alert-warning { - background: rgba(255, 140, 0, 0.15); + background: rgba(255, 140, 0, 0.12); border-left-color: #ff8c00; } /* Scrollbar */ ::-webkit-scrollbar { width: 8px; height: 8px; } - ::-webkit-scrollbar-track { background: var(--bg-secondary); } + ::-webkit-scrollbar-track { background: var(--bg-primary); border-radius: 4px; } ::-webkit-scrollbar-thumb { background: var(--border); border-radius: 4px; } ::-webkit-scrollbar-thumb:hover { background: var(--accent); } @@ -318,6 +367,9 @@

CoolerDash Configuration

Customize your LCD temperature dashboard • Changes require plugin restart

+

+ 💡 Tip: After an update, click Reset to apply new default values added by the developer. +

@@ -349,7 +401,7 @@

CoolerDash Configuration

- Leave empty if authentication is disabled + Leave empty if authentication is disabled (Default: coolAdmin)

@@ -369,7 +421,7 @@

Display Mode

@@ -421,13 +473,13 @@

Timing & Brightness

- 0.2 - 60 seconds + 0.2 - 60 seconds (Default: 2.5)
- 80% recommended + Default: 80%
80% @@ -472,13 +524,13 @@

Display Geometry

- 0.90 - 1.0 + 0.90 - 1.0 (Default: 0.98)
- For circular displays + For circular displays (Default: 0.707)
@@ -490,16 +542,19 @@

Bar Dimensions

+ Default: 24
+ Default: 98
+ Default: 12
@@ -509,34 +564,49 @@

Individual Bar Heights

+ Default: 0
+ Default: 0
+ Default: 0

Border & Margins

-
+
+
+ + Default: On + +
+
+ Default: 2.0
+ Default: 1
+ Default: 1
@@ -551,11 +621,13 @@

Font

+ Default: 100
+ Default: 30
@@ -615,7 +687,7 @@

Text Colors

CPU Temperature Scale

- Maximum temperature for bar scale + Maximum temperature for bar scale (Default: 115)
@@ -623,19 +695,19 @@

CPU Thresholds

- Green → Orange + Green → Orange (Default: 55)
- Orange → Dark Orange + Orange → Dark Orange (Default: 65)
- Dark Orange → Red + Dark Orange → Red (Default: 75)
@@ -681,7 +753,7 @@

CPU Bar Colors

GPU Temperature Scale

- Maximum temperature for bar scale + Maximum temperature for bar scale (Default: 115)
@@ -689,19 +761,19 @@

GPU Thresholds

- Green → Orange + Green → Orange (Default: 55)
- Orange → Dark Orange + Orange → Dark Orange (Default: 65)
- Dark Orange → Red + Dark Orange → Red (Default: 75)
@@ -747,7 +819,7 @@

GPU Bar Colors

Liquid Temperature Scale

- Maximum temperature for bar scale (typically 50°C) + Maximum temperature for bar scale (Default: 50)
@@ -755,19 +827,19 @@

Liquid Thresholds

- Green → Orange + Green → Orange (Default: 25)
- Orange → Dark Orange + Orange → Dark Orange (Default: 28)
- Dark Orange → Red + Dark Orange → Red (Default: 31)
@@ -819,11 +891,13 @@

Upper Slot Position

+ Default: 0
+ Default: 0
@@ -832,11 +906,13 @@

Middle Slot Position

+ Default: 0
+ Default: 0
@@ -845,11 +921,13 @@

Lower Slot Position

+ Default: 0
+ Default: 0
@@ -858,16 +936,19 @@

Labels Position

+ Default: 0
+ Default: 0
+ Default: 16
@@ -876,22 +957,21 @@

Labels Position

Image Paths

-
- Custom Images - Specify custom paths for plugin images. Leave default for standard installation. -
- -
- - Base directory for plugin images - -
- Image displayed when daemon shuts down + Image displayed when daemon stops (Default: /etc/coolercontrol/plugins/coolerdash/shutdown.png)
+ +

Backup & Restore

+
+ Configuration Backup + Export your current settings as a JSON file for backup or migration. +
+
@@ -917,8 +997,6 @@

Image Paths

password: "coolAdmin" }, paths: { - images: "/etc/coolercontrol/plugins/coolerdash", - image_coolerdash: "/etc/coolercontrol/plugins/coolerdash/coolerdash.png", image_shutdown: "/etc/coolercontrol/plugins/coolerdash/shutdown.png" }, display: { @@ -941,6 +1019,7 @@

Image Paths

bar_width: 98, bar_gap: 12, bar_border: 2.0, + bar_border_enabled: true, label_margin_left: 1, label_margin_bar: 1, bar_height_up: 0, @@ -1274,6 +1353,26 @@

Image Paths

} } + function exportConfig() { + try { + const config = buildConfig(); + const jsonStr = JSON.stringify(config, null, 2); + const blob = new Blob([jsonStr], { type: 'application/json' }); + const url = URL.createObjectURL(blob); + const a = document.createElement('a'); + a.href = url; + const date = new Date().toISOString().slice(0, 10); + a.download = `coolerdash-config-backup-${date}.json`; + document.body.appendChild(a); + a.click(); + document.body.removeChild(a); + URL.revokeObjectURL(url); + } catch (error) { + console.error("Export failed:", error); + alert("Export failed: " + error.message); + } + } + async function restartPluginDaemon(config) { try { const apiAddress = config.daemon?.address || 'http://localhost:11987'; From 157b6d7916fb9445e92396cc981719e084a08bf0 Mon Sep 17 00:00:00 2001 From: damachine Date: Wed, 4 Feb 2026 00:53:14 +0100 Subject: [PATCH 4/6] GUI: improve design and readability --- .../plugins/coolerdash/config.json | 2 +- .../plugins/coolerdash/ui/index.html | 112 +++++++++--------- 2 files changed, 60 insertions(+), 54 deletions(-) diff --git a/etc/coolercontrol/plugins/coolerdash/config.json b/etc/coolercontrol/plugins/coolerdash/config.json index 898ac3b..2ea0395 100644 --- a/etc/coolercontrol/plugins/coolerdash/config.json +++ b/etc/coolercontrol/plugins/coolerdash/config.json @@ -33,7 +33,7 @@ "bar_width": 98, "bar_gap": 12, "bar_border": 2.0, - "bar_border_enabled": true, + "bar_border_enabled": 1, "label_margin_left": 1, "label_margin_bar": 1, "bar_height_up": 0, diff --git a/etc/coolercontrol/plugins/coolerdash/ui/index.html b/etc/coolercontrol/plugins/coolerdash/ui/index.html index 0ab76f3..41f026d 100644 --- a/etc/coolercontrol/plugins/coolerdash/ui/index.html +++ b/etc/coolercontrol/plugins/coolerdash/ui/index.html @@ -202,6 +202,11 @@ .grid { display: grid; gap: 20px 28px; + margin-bottom: 20px; + } + + .grid:last-child { + margin-bottom: 0; } .grid-2 { grid-template-columns: repeat(2, 1fr); } @@ -398,7 +403,7 @@

CoolerDash Configuration

HTTP or HTTPS supported (Default: http://localhost:11987)
- +
Leave empty if authentication is disabled (Default: coolAdmin) @@ -488,9 +493,10 @@

Timing & Brightness

Display Geometry

-
+
+ Default: 0°
+
+ + Default: Automatic + +
+
+ +
0 = automatic @@ -512,25 +530,16 @@

Display Geometry

-
-
- - -
- +
- 0.90 - 1.0 (Default: 0.98) + Default: 0.98
- For circular displays (Default: 0.707) + Default: 0.707
@@ -587,8 +596,8 @@

Border & Margins

Default: On
@@ -684,36 +693,35 @@

Text Colors

-

CPU Temperature Scale

-
- - Maximum temperature for bar scale (Default: 115) - -
-

CPU Thresholds

-
+
- Green → Orange (Default: 55) + Default: 55
- Orange → Dark Orange (Default: 65) + Default: 65
- Dark Orange → Red (Default: 75) + Default: 75
+ +
+ + Default: 115 + +

CPU Bar Colors

-
+
@@ -750,36 +758,35 @@

CPU Bar Colors

-

GPU Temperature Scale

-
- - Maximum temperature for bar scale (Default: 115) - -
-

GPU Thresholds

-
+
- Green → Orange (Default: 55) + Default: 55
- Orange → Dark Orange (Default: 65) + Default: 65
- Dark Orange → Red (Default: 75) + Default: 75
+ +
+ + Default: 115 + +

GPU Bar Colors

-
+
@@ -816,36 +823,35 @@

GPU Bar Colors

-

Liquid Temperature Scale

-
- - Maximum temperature for bar scale (Default: 50) - -
-

Liquid Thresholds

-
+
- Green → Orange (Default: 25) + Default: 25
- Orange → Dark Orange (Default: 28) + Default: 28
- Dark Orange → Red (Default: 31) + Default: 31
+ +
+ + Default: 50 + +

Liquid Bar Colors

-
+
@@ -1019,7 +1025,7 @@

Backup & Restore

bar_width: 98, bar_gap: 12, bar_border: 2.0, - bar_border_enabled: true, + bar_border_enabled: 1, label_margin_left: 1, label_margin_bar: 1, bar_height_up: 0, From a1db4edd5269169e9336ce497c2dda9361555323 Mon Sep 17 00:00:00 2001 From: damachine Date: Wed, 4 Feb 2026 01:08:25 +0100 Subject: [PATCH 5/6] fix: GUI alignment and typo --- etc/coolercontrol/plugins/coolerdash/ui/index.html | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/etc/coolercontrol/plugins/coolerdash/ui/index.html b/etc/coolercontrol/plugins/coolerdash/ui/index.html index 41f026d..05c9cdb 100644 --- a/etc/coolercontrol/plugins/coolerdash/ui/index.html +++ b/etc/coolercontrol/plugins/coolerdash/ui/index.html @@ -440,6 +440,7 @@

Sensor Slots

+ Default: CPU @@ -461,6 +462,7 @@

Sensor Slots

+ Default: GPU
@@ -1026,7 +1026,7 @@

Backup & Restore

bar_height: 24, bar_width: 98, bar_gap: 12, - bar_border: 2.0, + bar_border: 1.0, bar_border_enabled: 1, label_margin_left: 1, label_margin_bar: 1, diff --git a/src/device/config.c b/src/device/config.c index a1639fe..f77da3c 100644 --- a/src/device/config.c +++ b/src/device/config.c @@ -167,9 +167,9 @@ static void set_layout_defaults(Config *config) config->layout_bar_height = 24; if (config->layout_bar_gap == 0) config->layout_bar_gap = 12.0f; - // bar_border: -1 = use default (2.0), 0 = explicitly disabled, >0 = custom value + // bar_border: -1 = use default (1.0), 0 = explicitly disabled, >0 = custom value if (config->layout_bar_border < 0.0f) - config->layout_bar_border = 2.0f; + config->layout_bar_border = 1.0f; // bar_border_enabled: -1 = auto (enabled), 0 = disabled, 1 = enabled if (config->layout_bar_border_enabled < 0) config->layout_bar_border_enabled = 1; // Default: enabled