You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
182 lines
6.8 KiB
182 lines
6.8 KiB
/* |
|
* chat_setting.c — chat-related settings: validation, storage, on-the-fly apply |
|
* |
|
* Используется и из парсера конфига (headless), и из GUI (через bridge). |
|
* Состояние per-instance (struct chat_setting_state). |
|
*/ |
|
|
|
#include "chat_setting.h" |
|
#include "../utun_instance.h" |
|
#include "../../lib/debug_config.h" |
|
|
|
#include <string.h> |
|
#include <stdio.h> |
|
#include <stdlib.h> |
|
|
|
#define CS_ID DEBUG_CATEGORY_CHAT_SYNC |
|
|
|
/* ─── типы настроек ─── */ |
|
|
|
#define CHAT_SETTING_INT 0 |
|
#define CHAT_SETTING_BOOL 1 |
|
#define CHAT_SETTING_STRING 2 |
|
|
|
struct chat_setting_def { |
|
const char* name; |
|
int type; |
|
int default_val; |
|
int min_val; |
|
int max_val; |
|
}; |
|
|
|
/* ─── реестр всех известных настроек ─── */ |
|
|
|
static const struct chat_setting_def g_setting_defs[] = { |
|
{"storage_autoload", CHAT_SETTING_BOOL, 1, 0, 1}, |
|
{"opus_codec_preset", CHAT_SETTING_INT, 1, 0, 4}, |
|
{"radio_opus_mode", CHAT_SETTING_INT, 0, 0, 1}, |
|
{"radio_opus_bitrate", CHAT_SETTING_INT, 32, 16, 128}, |
|
{"radio_capture_stereo", CHAT_SETTING_INT, 0, 0, 1}, |
|
{"compressor_enabled", CHAT_SETTING_BOOL, 1, 0, 1}, |
|
{"compressor_max_gain_db", CHAT_SETTING_INT, 15, 1, 100}, |
|
{"compressor_rise_rate", CHAT_SETTING_INT, 2, 1, 100}, |
|
{"radio_compressor_enabled", CHAT_SETTING_BOOL, 1, 0, 1}, |
|
{"radio_compressor_max_gain_db",CHAT_SETTING_INT, 15, 1, 100}, |
|
{"media_download_max_peers", CHAT_SETTING_INT, 3, 1, 16}, |
|
{"storage_backfill_days", CHAT_SETTING_INT, 7, 1, 365}, |
|
{"standby_active_sec", CHAT_SETTING_INT, 2, 1, 3600}, |
|
{"standby_sleep_sec", CHAT_SETTING_INT, 60, 1, 86400}, |
|
{"standby_min_sleep_sec", CHAT_SETTING_INT, 15, 1, 86400}, |
|
{"standby_keepalive_ms", CHAT_SETTING_INT, 30000, 1000, 600000}, |
|
{"join_policy", CHAT_SETTING_INT, 1, 0, 2}, /* 0=autojoin, 1=ask, 2=deny */ |
|
{"whisper_enabled", CHAT_SETTING_BOOL, 0, 0, 1}, |
|
{"whisper_model_path", CHAT_SETTING_STRING, 0, 0, 0}, |
|
{"group_autoconnect", CHAT_SETTING_BOOL, 1, 0, 1}, |
|
}; |
|
|
|
#define CHAT_SETTING_COUNT (sizeof(g_setting_defs) / sizeof(g_setting_defs[0])) |
|
|
|
/* ─── state helpers ─── */ |
|
|
|
static void chat_setting_reset_defaults(struct chat_setting_state* s) { |
|
for (size_t i = 0; i < CHAT_SETTING_COUNT; i++) |
|
s->values[i] = g_setting_defs[i].default_val; |
|
s->initialized = 1; |
|
} |
|
|
|
static int chat_setting_find(const char* name) { |
|
for (size_t i = 0; i < CHAT_SETTING_COUNT; i++) |
|
if (strcmp(g_setting_defs[i].name, name) == 0) return (int)i; |
|
return -1; |
|
} |
|
|
|
static void chat_setting_apply(struct chat_setting_state* s) { |
|
(void)s; |
|
/* будет заполняться по мере появления runtime-setter'ов в модулях: |
|
if (idx == ...) some_module_set_xxx(value); */ |
|
} |
|
|
|
/* ─── state-level API ─── */ |
|
|
|
int chat_setting_set_state(struct chat_setting_state* s, const char* name, const char* value) { |
|
if (!s || !name || !value || !*name || !*value) return -2; |
|
|
|
if (!s->initialized) chat_setting_reset_defaults(s); |
|
|
|
int idx = chat_setting_find(name); |
|
if (idx < 0) { |
|
DEBUG_ERROR(CS_ID, "chat_setting: unknown setting '%s'", name); |
|
return -1; |
|
} |
|
|
|
const struct chat_setting_def* def = &g_setting_defs[idx]; |
|
int v; |
|
|
|
if (def->type == CHAT_SETTING_STRING) { |
|
for (int i = 0; i < s->string_count; i++) { |
|
if (s->string_idx[i] == idx) { |
|
if (strcmp(s->string_val[i], value) == 0) return 0; |
|
snprintf(s->string_val[i], sizeof(s->string_val[i]), "%s", value); |
|
DEBUG_INFO(CS_ID, "chat_setting: %s = %s", name, value); |
|
chat_setting_apply(s); |
|
return 0; |
|
} |
|
} |
|
if (s->string_count >= CHAT_SETTING_STRING_MAX) { |
|
DEBUG_ERROR(CS_ID, "chat_setting: string storage full for '%s'", name); |
|
return -2; |
|
} |
|
s->string_idx[s->string_count] = idx; |
|
snprintf(s->string_val[s->string_count], sizeof(s->string_val[0]), "%s", value); |
|
s->string_count++; |
|
DEBUG_INFO(CS_ID, "chat_setting: %s = %s", name, value); |
|
chat_setting_apply(s); |
|
return 0; |
|
} |
|
|
|
if (def->type == CHAT_SETTING_BOOL) { |
|
if (strcmp(value, "0") == 0 || strcmp(value, "no") == 0 || strcmp(value, "false") == 0) v = 0; |
|
else if (strcmp(value, "1") == 0 || strcmp(value, "yes") == 0 || strcmp(value, "true") == 0) v = 1; |
|
else { DEBUG_ERROR(CS_ID, "chat_setting: '%s' invalid bool '%s' (expect 0/1)", name, value); return -2; } |
|
} else { |
|
char* end = NULL; |
|
long lv = strtol(value, &end, 10); |
|
if (!end || *end != '\0' || lv < def->min_val || lv > def->max_val) { |
|
DEBUG_ERROR(CS_ID, "chat_setting: '%s' invalid int '%s' (range %d..%d)", name, value, def->min_val, def->max_val); |
|
return -2; |
|
} |
|
v = (int)lv; |
|
} |
|
|
|
if (s->values[idx] == v) return 0; |
|
|
|
s->values[idx] = v; |
|
DEBUG_INFO(CS_ID, "chat_setting: %s = %s", name, value); |
|
chat_setting_apply(s); |
|
return 0; |
|
} |
|
|
|
const char* chat_setting_get_state(const struct chat_setting_state* s, const char* name) { |
|
if (!s || !s->initialized) return NULL; |
|
int idx = chat_setting_find(name); |
|
if (idx < 0) return NULL; |
|
|
|
const struct chat_setting_def* def = &g_setting_defs[idx]; |
|
if (def->type == CHAT_SETTING_STRING) { |
|
for (int i = 0; i < s->string_count; i++) { |
|
if (s->string_idx[i] == idx) return s->string_val[i]; |
|
} |
|
return ""; |
|
} |
|
|
|
snprintf((char*)s->int_buf, sizeof(s->int_buf), "%d", s->values[idx]); |
|
return s->int_buf; |
|
} |
|
|
|
int chat_setting_get_int_state(const struct chat_setting_state* s, const char* name, int def) { |
|
if (!s || !s->initialized) return def; |
|
int idx = chat_setting_find(name); |
|
return idx >= 0 ? s->values[idx] : def; |
|
} |
|
|
|
/* ─── instance-level API ─── */ |
|
|
|
static struct chat_setting_state* cs_state(struct UTUN_INSTANCE* inst) { |
|
return inst ? &inst->chat_settings : NULL; |
|
} |
|
|
|
int chat_setting_set(struct UTUN_INSTANCE* inst, const char* name, const char* value) { |
|
struct chat_setting_state* s = cs_state(inst); |
|
if (!s) return -2; |
|
return chat_setting_set_state(s, name, value); |
|
} |
|
|
|
const char* chat_setting_get(struct UTUN_INSTANCE* inst, const char* name) { |
|
struct chat_setting_state* s = cs_state(inst); |
|
return s ? chat_setting_get_state(s, name) : NULL; |
|
} |
|
|
|
int chat_setting_get_int(struct UTUN_INSTANCE* inst, const char* name, int def) { |
|
struct chat_setting_state* s = cs_state(inst); |
|
return s ? chat_setting_get_int_state(s, name, def) : def; |
|
}
|
|
|