Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 12 additions & 2 deletions app/platforms/common/runtime.c
Original file line number Diff line number Diff line change
Expand Up @@ -328,8 +328,18 @@ static void configuring(IHS_Session *s, IHS_SessionConfig *c, void *ctx) {
c->maxWidth = 1280;
c->maxHeight = 720;
c->maxFps = 60;
const uint32_t rates[] = {6000, 4000, 10000};
c->maxBitrateKbps = rates[r->active.quality <= 2 ? r->active.quality : 0];
switch (r->active.quality) {
case 1:
c->quality = IHS_StreamQualityFast;
break;
case 2:
c->quality = IHS_StreamQualityBeautiful;
break;
default:
c->quality = IHS_StreamQualityBalanced;
break;
}
c->maxBitrateKbps = sl_bitrate_valid(r->active.bitrate_kbps) ? r->active.bitrate_kbps : 6000;
}
static void connected(IHS_Session *s, void *ctx) {
(void)s;
Expand Down
14 changes: 9 additions & 5 deletions app/platforms/common/ui_renderer.c
Original file line number Diff line number Diff line change
Expand Up @@ -484,6 +484,8 @@ static sl_gfx_color action_color(sl_action a) {
case SL_BACK:
return green;
case SL_OPEN_SETTINGS:
case SL_OPEN_BANDWIDTH:
case SL_SET_BANDWIDTH:
case SL_OPEN_QUALITY:
case SL_SET_QUALITY:
return violet;
Expand All @@ -503,7 +505,7 @@ static sl_gfx_color action_color(sl_action a) {
static void action_icon(sl_gfx *r, sl_action a, int x, int y, sl_gfx_color c) {
if (a == SL_BACK || a == SL_START || a == SL_RECENT) {
play_icon(r, x + 7, y + 14, 19, c);
} else if (a == SL_OPEN_SETTINGS || a == SL_SET_QUALITY) {
} else if (a == SL_OPEN_SETTINGS || a == SL_OPEN_BANDWIDTH || a == SL_SET_QUALITY) {
for (int i = 0; i < 3; ++i) {
int yy = y + 6 + i * 8, xx = x + (i == 1 ? 17 : 9);
line(r, x + 3, yy, x + 27, yy, c);
Expand Down Expand Up @@ -868,14 +870,16 @@ static void draw_scene(sl_ui_renderer *r, const sl_ui_model *m, const sl_debug_s
} else if (menu_row) {
if (focused)
rounded(r->renderer, box, 8, fg);
if (c->action == SL_SET_QUALITY || c->action == SL_SET_LANGUAGE) {
if (c->action == SL_SET_QUALITY || c->action == SL_SET_BANDWIDTH ||
c->action == SL_SET_LANGUAGE) {
int x = box.x + 23, y = box.y + (box.h - 26) / 2;
sl_gfx_color surface = focused ? fg : panel;
sl_gfx_color ring = focused ? bg : muted;
rounded(r->renderer, (sl_gfx_rect){x, y, 26, 26}, 13, ring);
rounded(r->renderer, (sl_gfx_rect){x + 2, y + 2, 22, 22}, 11, surface);
if (c->arg == (c->action == SL_SET_LANGUAGE ? (int)sl_i18n_language()
: (int)m->store.quality))
if (c->arg == (c->action == SL_SET_LANGUAGE ? (int)sl_i18n_language()
: c->action == SL_SET_BANDWIDTH ? (int)m->store.bitrate_kbps
: (int)m->store.quality))
rounded(r->renderer, (sl_gfx_rect){x + 6, y + 6, 14, 14}, 7,
focused ? bg : green);
} else {
Expand Down Expand Up @@ -954,7 +958,7 @@ static void draw_scene(sl_ui_renderer *r, const sl_ui_model *m, const sl_debug_s
rounded(r->renderer, (sl_gfx_rect){tx + (m->store.sound ? 25 : 3), ty + 3, 22, 22},
11, bg);
} else if (c->action != SL_BACK && c->action != SL_SET_QUALITY &&
c->action != SL_SET_LANGUAGE)
c->action != SL_SET_BANDWIDTH && c->action != SL_SET_LANGUAGE)
chevron(r->renderer, box.x + box.w - 35, box.y + box.h / 2, focused ? bg : muted);
}
sl_gfx_clip(r->renderer, card ? &clip : &box);
Expand Down
1 change: 1 addition & 0 deletions app/resources/i18n/en.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
{
"BANDWIDTH": "Bandwidth limit",
"LAST_ADDRESS": "Last IP: ",
"PAIRED": "Paired",
"UNPAIRED": "Not paired",
Expand Down
1 change: 1 addition & 0 deletions app/resources/i18n/zh-CN.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
{
"BANDWIDTH": "带宽限制",
"LAST_ADDRESS": "上次地址 ",
"PAIRED": "已配对",
"UNPAIRED": "未配对",
Expand Down
56 changes: 48 additions & 8 deletions app/src/services/auth_store.c
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,20 @@ typedef struct __attribute__((packed)) legacy_auth {
uint16_t port;
uint8_t reserved[30];
} legacy_auth;
/* Frozen v2 layout: do not use the growing runtime structure to read old identities. */
typedef struct auth_store_v2 {
uint64_t device_id;
uint8_t secret[32];
char device_name[64];
sl_host_registry registry;
uint32_t quality;
bool sound;
} auth_store_v2;
typedef struct disk_store_v2 {
char magic[8];
uint32_t version, size, checksum;
auth_store_v2 data;
} disk_store_v2;
typedef struct disk_store {
char magic[8];
uint32_t version, size, checksum;
Expand Down Expand Up @@ -66,10 +80,12 @@ bool sl_auth_save(const sl_auth_store *s, const char *dir) {
snprintf(tmp, sizeof(tmp), "%s/profile.tmp", dir);
snprintf(backup, sizeof(backup), "%s/profile.bak", dir);
disk_store d = {0};
memcpy(d.magic, "NSLUI02", 8);
d.version = 2;
memcpy(d.magic, "NSLUI03", 8);
d.version = 3;
d.size = sizeof(d);
d.data = *s;
if (!sl_bitrate_valid(d.data.bitrate_kbps))
d.data.bitrate_kbps = 6000;
for (int i = 0; i < d.data.registry.count; ++i) {
d.data.registry.hosts[i].observed = false;
d.data.registry.hosts[i].last_seen = 0;
Expand Down Expand Up @@ -97,6 +113,7 @@ int sl_auth_load(sl_auth_store *s, const char *dir) {
memset(s, 0, sizeof(*s));
sl_host_registry_init(&s->registry);
s->sound = true;
s->bitrate_kbps = 6000;
char path[512];
snprintf(path, sizeof(path), "%s/profile.bin", dir);
FILE *f = fopen(path, "rb");
Expand All @@ -108,13 +125,36 @@ int sl_auth_load(sl_auth_store *s, const char *dir) {
recovered = f != NULL;
}
if (f) {
disk_store d;
bool ok = fread(&d, 1, sizeof(d), f) == sizeof(d) && fgetc(f) == EOF;
disk_store d = {0};
char magic[8];
bool ok = fread(magic, 1, sizeof(magic), f) == sizeof(magic);
rewind(f);
if (ok && !memcmp(magic, "NSLUI02", 8)) {
disk_store_v2 old;
ok = fread(&old, 1, sizeof(old), f) == sizeof(old) && fgetc(f) == EOF;
ok = ok && old.version == 2 && old.size == sizeof(old) &&
old.checksum == checksum(&old.data, sizeof(old.data)) && old.data.quality <= 2;
if (ok) {
d.data.device_id = old.data.device_id;
memcpy(d.data.secret, old.data.secret, sizeof(d.data.secret));
memcpy(d.data.device_name, old.data.device_name, sizeof(d.data.device_name));
d.data.registry = old.data.registry;
d.data.quality = old.data.quality;
d.data.sound = old.data.sound;
const uint32_t old_rates[] = {6000, 4000, 10000};
d.data.bitrate_kbps = old_rates[old.data.quality];
}
} else if (ok && !memcmp(magic, "NSLUI03", 8)) {
ok = fread(&d, 1, sizeof(d), f) == sizeof(d) && fgetc(f) == EOF;
ok = ok && d.version == 3 && d.size == sizeof(d) &&
d.checksum == checksum(&d.data, sizeof(d.data));
} else {
ok = false;
}
fclose(f);
if (!ok || memcmp(d.magic, "NSLUI02", 8) || d.version != 2 || d.size != sizeof(d) ||
d.checksum != checksum(&d.data, sizeof(d.data)) || !d.data.device_id ||
d.data.registry.count < 0 || d.data.registry.count > SL_HOST_LIMIT ||
d.data.quality > 2)
if (!ok || !d.data.device_id || d.data.registry.count < 0 ||
d.data.registry.count > SL_HOST_LIMIT || d.data.quality > 2 ||
!sl_bitrate_valid(d.data.bitrate_kbps))
return -2;
if (recovered && rename(backup, path)) {
store_error("recover");
Expand Down
5 changes: 5 additions & 0 deletions app/src/services/auth_store.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,12 @@ typedef struct sl_auth_store {
sl_host_registry registry;
uint32_t quality;
bool sound;
uint32_t bitrate_kbps;
} sl_auth_store;
/* Explicit bandwidth choices; independent of the quality preference. */
static inline bool sl_bitrate_valid(uint32_t kbps) {
return kbps == 4000 || kbps == 6000 || kbps == 10000 || kbps == 20000;
}
/* 0 loaded, 1 created, negative: corrupt/unreadable; never replaces corrupt identity. */
int sl_auth_load(sl_auth_store *store, const char *directory);
bool sl_auth_save(const sl_auth_store *store, const char *directory);
Expand Down
1 change: 1 addition & 0 deletions app/src/ui/ui_events.c
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ void sl_ui_runtime_event(sl_ui_model *m, const sl_runtime_event *event) {
m->command.generation = m->generation;
m->command.type = SL_CMD_STREAM;
m->command.quality = m->store.quality;
m->command.bitrate_kbps = m->store.bitrate_kbps;
} else
sl_ui_error(m, sl_tr(SL_T_HOST_REMOVED));
break;
Expand Down
18 changes: 14 additions & 4 deletions app/src/ui/ui_layout.c
Original file line number Diff line number Diff line change
Expand Up @@ -128,17 +128,27 @@ void sl_ui_layout(sl_ui_model *m) {
case SL_SETTINGS:
strcpy(l->title, sl_tr(SL_T_SETTINGS));
row(l, 0, sl_tr(SL_T_QUALITY), SL_OPEN_QUALITY, 0);
row(l, 1, m->store.sound ? sl_tr(SL_T_SOUND_ON) : sl_tr(SL_T_SOUND_OFF), SL_SOUND, 0);
row(l, 2, sl_tr(SL_T_LANGUAGE), SL_OPEN_LANGUAGE, 0);
row(l, 1, sl_tr(SL_T_BANDWIDTH), SL_OPEN_BANDWIDTH, 0);
row(l, 2, m->store.sound ? sl_tr(SL_T_SOUND_ON) : sl_tr(SL_T_SOUND_OFF), SL_SOUND, 0);
row(l, 3, sl_tr(SL_T_LANGUAGE), SL_OPEN_LANGUAGE, 0);
if (!m->streaming)
row(l, 3, sl_tr(SL_T_MANUAL), SL_OPEN_MANUAL, 0);
row(l, 4, sl_tr(SL_T_MANUAL), SL_OPEN_MANUAL, 0);
break;
case SL_LANGUAGE:
strcpy(l->title, sl_tr(SL_T_LANGUAGE));
row(l, 0, sl_tr(SL_T_SYSTEM_LANGUAGE), SL_SET_LANGUAGE, SL_LANG_SYSTEM);
row(l, 1, sl_tr(SL_T_CHINESE), SL_SET_LANGUAGE, SL_LANG_ZH_CN);
row(l, 2, sl_tr(SL_T_ENGLISH), SL_SET_LANGUAGE, SL_LANG_EN);
break;
case SL_BANDWIDTH:
strcpy(l->title, sl_tr(SL_T_BANDWIDTH));
row(l, 0, "4 Mbps", SL_SET_BANDWIDTH, 4000);
row(l, 1, "6 Mbps", SL_SET_BANDWIDTH, 6000);
row(l, 2, "10 Mbps", SL_SET_BANDWIDTH, 10000);
row(l, 3, "20 Mbps", SL_SET_BANDWIDTH, 20000);
if (m->streaming)
text(l, 320, 548, 26, sl_tr(SL_T_NEXT_CONNECTION));
break;
case SL_QUALITY:
strcpy(l->title, sl_tr(SL_T_QUALITY));
row(l, 0, sl_tr(SL_T_BALANCED), SL_SET_QUALITY, 0);
Expand Down Expand Up @@ -227,7 +237,7 @@ void sl_ui_layout(sl_ui_model *m) {
}
if (l->dialog) {
l->drawer = m->page == SL_MENU || m->page == SL_OPTIONS || m->page == SL_SETTINGS ||
m->page == SL_QUALITY || m->page == SL_LANGUAGE;
m->page == SL_QUALITY || m->page == SL_BANDWIDTH || m->page == SL_LANGUAGE;
l->compact = m->page == SL_END_GAME || m->page == SL_DISCONNECT || m->page == SL_FORGET ||
m->page == SL_EXIT || m->page == SL_ERROR || m->page == SL_STOPPING ||
m->page == SL_INSTALLING;
Expand Down
21 changes: 20 additions & 1 deletion app/src/ui/ui_model.c
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,8 @@ void sl_ui_release_games(sl_ui_model *m, float velocity) {
void sl_ui_init(sl_ui_model *m, const sl_auth_store *s) {
memset(m, 0, sizeof(*m));
m->store = *s;
if (!sl_bitrate_valid(m->store.bitrate_kbps))
m->store.bitrate_kbps = 6000;
m->page = SL_HOME;
m->network_ok = true;
sl_ui_layout(m);
Expand Down Expand Up @@ -172,6 +174,7 @@ static void emit(sl_ui_model *m, sl_command_type type) {
m->command.type = type;
m->command.generation = m->generation;
m->command.quality = m->store.quality;
m->command.bitrate_kbps = m->store.bitrate_kbps;
m->command.language = sl_i18n_language();
}
static void start(sl_ui_model *m, int game) {
Expand Down Expand Up @@ -329,6 +332,15 @@ static void action(sl_ui_model *m, sl_action a, int arg) {
emit(m, SL_CMD_SAVE);
}
break;
case SL_OPEN_BANDWIDTH: {
push(m, SL_BANDWIDTH);
const uint32_t rates[] = {4000, 6000, 10000, 20000};
m->focus = 101;
for (int i = 0; i < 4; ++i)
if (m->store.bitrate_kbps == rates[i])
m->focus = 100 + i;
break;
}
case SL_OPEN_QUALITY:
push(m, SL_QUALITY);
m->focus = 100 + (m->store.quality <= 2 ? (int)m->store.quality : 0);
Expand Down Expand Up @@ -431,8 +443,15 @@ static void action(sl_ui_model *m, sl_action a, int arg) {
emit(m, SL_CMD_MANUAL);
}
break;
case SL_SET_BANDWIDTH:
if (m->page == SL_BANDWIDTH && sl_bitrate_valid((uint32_t)arg)) {
m->store.bitrate_kbps = (uint32_t)arg;
emit(m, SL_CMD_SAVE);
back(m);
}
break;
case SL_SET_QUALITY:
if (arg >= 0 && arg <= 2) {
if (m->page == SL_QUALITY && arg >= 0 && arg <= 2) {
m->store.quality = (uint32_t)arg;
emit(m, SL_CMD_SAVE);
back(m);
Expand Down
4 changes: 4 additions & 0 deletions app/src/ui/ui_model.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ typedef enum sl_page {
SL_SETTINGS,
SL_MANUAL,
SL_QUALITY,
SL_BANDWIDTH,
SL_LANGUAGE,
SL_FORGET,
SL_END_GAME,
Expand Down Expand Up @@ -48,6 +49,7 @@ typedef enum sl_action {
SL_CONFIRM_SHORTCUT,
SL_OPEN_MANUAL,
SL_OPEN_QUALITY,
SL_OPEN_BANDWIDTH,
SL_OPEN_LANGUAGE,
SL_SET_LANGUAGE,
SL_OPEN_FORGET,
Expand All @@ -61,6 +63,7 @@ typedef enum sl_action {
SL_ERASE,
SL_SUBMIT,
SL_SET_QUALITY,
SL_SET_BANDWIDTH,
SL_SOUND,
SL_RETRY
} sl_action;
Expand All @@ -82,6 +85,7 @@ typedef struct sl_command {
sl_host host;
uint64_t game_id;
uint32_t quality;
uint32_t bitrate_kbps;
sl_language language;
char text[64];
} sl_command;
Expand Down
2 changes: 1 addition & 1 deletion app/tests/test_native.c
Original file line number Diff line number Diff line change
Expand Up @@ -330,7 +330,7 @@ int main(int argc, char **argv) {
ui.page = p;
ui.depth = 0;
ui.leaving = false;
ui.streaming = p == SL_STREAM || p == SL_MENU;
ui.streaming = p == SL_STREAM || p == SL_MENU || p == SL_BANDWIDTH;
ui.ending_game = p == SL_STOPPING;
ui.debug = p == SL_STREAM;
ui.stream_started_at = ui.now;
Expand Down
Loading
Loading