Add configurable game/initial screen width and distance, with a bundled first-run config
build / build (push) Successful in 2m41s
build / build (push) Successful in 2m41s
- Video Options tab offers width and distance presets independently for the Initial Screen (splash/intro/menu) and Game Screen (gameplay) profiles, with their preset step/offset configurable via questshock.config.json. - Ships res/questshock.config.default.json as the initial config on first run (or whenever questshock.config.json doesn't exist yet), refreshed from the APK on every launch without touching a user's own saved config.
This commit is contained in:
@@ -28,6 +28,10 @@
|
||||
#include "tools.h"
|
||||
|
||||
#define QS_CONFIG_FILENAME "questshock.config.json"
|
||||
// Bundled with the app (QuestShockActivity.java extracts it fresh on every
|
||||
// launch) and read only at startup, only if QS_CONFIG_FILENAME doesn't
|
||||
// exist yet - never touched by Save/Load Configuration.
|
||||
#define QS_CONFIG_DEFAULT_FILENAME "questshock.config.default.json"
|
||||
|
||||
// ===========================================================================
|
||||
// Minimal JSON parser
|
||||
@@ -330,6 +334,27 @@ static void qs_json_take_number_field(QsJsonParser *p, float *out, bool *has) {
|
||||
typedef struct {
|
||||
char renderer[16]; // "" = not present in the file
|
||||
char resolution[16]; // "" = not present in the file
|
||||
// The virtual screen's physical width (meters) for each of the two
|
||||
// screen profiles - see qs_screen_profile_notify_loop_mode()'s own
|
||||
// comment. Like QsParsedPlayerPosition below (and unlike renderer/
|
||||
// resolution above), these are plain numbers, not string enums, and
|
||||
// need their own has_* bools since 0.0f is a valid value.
|
||||
float initial_screen_width_m, initial_screen_distance_m;
|
||||
float game_screen_width_m, game_screen_distance_m;
|
||||
bool has_initial_screen_width_m, has_initial_screen_distance_m;
|
||||
bool has_game_screen_width_m, has_game_screen_distance_m;
|
||||
// Each of the two options (width/distance) is offered as 5 preset
|
||||
// buttons in the Video Options tab, generated as offset + step*index
|
||||
// (index 0..4) rather than a fixed table - see qs_preset_value()'s
|
||||
// own comment. Shared by both "Initial Screen" and "Game Screen"
|
||||
// (both columns' preset buttons show the same 5 values per row) -
|
||||
// config-file-only (no UI control): the buttons are for adjusting
|
||||
// each profile's own current *value*, this offset/step pair is for
|
||||
// reshaping what those buttons themselves offer.
|
||||
float screen_width_offset_m, screen_width_step_m;
|
||||
float screen_distance_offset_m, screen_distance_step_m;
|
||||
bool has_screen_width_offset_m, has_screen_width_step_m;
|
||||
bool has_screen_distance_offset_m, has_screen_distance_step_m;
|
||||
} QsParsedVideoOptions;
|
||||
|
||||
typedef struct {
|
||||
@@ -359,6 +384,26 @@ static void qs_parse_video_options_field(const char *key, QsJsonParser *p, void
|
||||
qs_json_take_string_field(p, out->renderer, sizeof(out->renderer));
|
||||
else if (strcmp(key, "resolution") == 0)
|
||||
qs_json_take_string_field(p, out->resolution, sizeof(out->resolution));
|
||||
else if (strcmp(key, "initial_screen_width_m") == 0)
|
||||
qs_json_take_number_field(p, &out->initial_screen_width_m, &out->has_initial_screen_width_m);
|
||||
else if (strcmp(key, "game_screen_width_m") == 0)
|
||||
qs_json_take_number_field(p, &out->game_screen_width_m, &out->has_game_screen_width_m);
|
||||
else if (strcmp(key, "initial_screen_distance_m") == 0)
|
||||
qs_json_take_number_field(p, &out->initial_screen_distance_m,
|
||||
&out->has_initial_screen_distance_m);
|
||||
else if (strcmp(key, "game_screen_distance_m") == 0)
|
||||
qs_json_take_number_field(p, &out->game_screen_distance_m,
|
||||
&out->has_game_screen_distance_m);
|
||||
else if (strcmp(key, "screen_width_offset_m") == 0)
|
||||
qs_json_take_number_field(p, &out->screen_width_offset_m, &out->has_screen_width_offset_m);
|
||||
else if (strcmp(key, "screen_width_step_m") == 0)
|
||||
qs_json_take_number_field(p, &out->screen_width_step_m, &out->has_screen_width_step_m);
|
||||
else if (strcmp(key, "screen_distance_offset_m") == 0)
|
||||
qs_json_take_number_field(p, &out->screen_distance_offset_m,
|
||||
&out->has_screen_distance_offset_m);
|
||||
else if (strcmp(key, "screen_distance_step_m") == 0)
|
||||
qs_json_take_number_field(p, &out->screen_distance_step_m,
|
||||
&out->has_screen_distance_step_m);
|
||||
else
|
||||
qs_json_skip_value(p);
|
||||
}
|
||||
@@ -396,13 +441,13 @@ static void qs_parse_top_level_field(const char *key, QsJsonParser *p, void *ctx
|
||||
qs_json_skip_value(p);
|
||||
}
|
||||
|
||||
// Reads and parses QS_CONFIG_FILENAME into *out. Returns false (with *out
|
||||
// zeroed) if the file doesn't exist, is empty, or fails to parse as valid
|
||||
// JSON - logging a WARN only for the last case, since a missing file is the
|
||||
// Reads and parses `filename` into *out. Returns false (with *out zeroed)
|
||||
// if the file doesn't exist, is empty, or fails to parse as valid JSON -
|
||||
// logging a WARN only for the last case, since a missing file is the
|
||||
// normal/expected state, not an error.
|
||||
static bool qs_config_read_file(QsParsedConfig *out) {
|
||||
static bool qs_config_read_file_named(const char *filename, QsParsedConfig *out) {
|
||||
memset(out, 0, sizeof(*out));
|
||||
FILE *f = fopen(QS_CONFIG_FILENAME, "rb");
|
||||
FILE *f = fopen(filename, "rb");
|
||||
if (!f)
|
||||
return false;
|
||||
fseek(f, 0, SEEK_END);
|
||||
@@ -425,13 +470,17 @@ static bool qs_config_read_file(QsParsedConfig *out) {
|
||||
bool ok = qs_json_parse_object(&parser, qs_parse_top_level_field, out) && !parser.error;
|
||||
free(buf);
|
||||
if (!ok) {
|
||||
WARN("%s: malformed JSON - ignoring file", QS_CONFIG_FILENAME);
|
||||
WARN("%s: malformed JSON - ignoring file", filename);
|
||||
memset(out, 0, sizeof(*out));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool qs_config_read_file(QsParsedConfig *out) {
|
||||
return qs_config_read_file_named(QS_CONFIG_FILENAME, out);
|
||||
}
|
||||
|
||||
// ===========================================================================
|
||||
// Video Options: renderer
|
||||
// ===========================================================================
|
||||
@@ -531,6 +580,149 @@ static const char *qs_current_resolution(void) {
|
||||
return "640x480"; // shouldn't happen - safe fallback for Save
|
||||
}
|
||||
|
||||
// ===========================================================================
|
||||
// Video Options: screen profile (initial screen / game screen)
|
||||
// ===========================================================================
|
||||
|
||||
// Live-appliable: xr_session_set_game_screen_shape() (xr_session.c) is a
|
||||
// plain state setter with no XR calls, same category as
|
||||
// xr_session_set_player_transform() - safe to call live or at startup.
|
||||
#define QS_SCREEN_PROFILE_REQUIRES_RESTART false
|
||||
|
||||
// mainloop.h's own loop-mode #defines - declared locally rather than
|
||||
// #include "mainloop.h", which pulls in an `extern LGRegion *_current_view;`
|
||||
// this file can't resolve (LGRegion isn't visible via any header this file
|
||||
// already includes) - same "avoid a header with declarations this file has
|
||||
// no other reason to need" reasoning as screen_draw()/language_change()
|
||||
// above. These two values must stay in sync with mainloop.h's own
|
||||
// SETUP_LOOP/CUTSCENE_LOOP.
|
||||
#define QS_SETUP_LOOP 4
|
||||
#define QS_CUTSCENE_LOOP 6
|
||||
|
||||
// Each of the two options (width/distance) offers 5 preset buttons in
|
||||
// the Video Options tab, generated as offset+step*index (index 0..4)
|
||||
// rather than a fixed table - see qs_preset_value() below. Shared by
|
||||
// both "Initial Screen" and "Game Screen" (both columns' preset buttons
|
||||
// show the same 5 values per row), config-file-only (no UI control):
|
||||
// unlike the four *_screen_width_m-style current-value statics below
|
||||
// (button-driven, live-appliable, one pair per profile), these only
|
||||
// reshape what the buttons themselves offer.
|
||||
#define QS_NUM_SCREEN_PRESETS 5
|
||||
static float s_screen_width_offset_m = 1.0f;
|
||||
static float s_screen_width_step_m = 0.6f;
|
||||
static float s_screen_distance_offset_m = 1.0f;
|
||||
static float s_screen_distance_step_m = 0.5f;
|
||||
|
||||
// Generates preset index `index`'s value (index 0..QS_NUM_SCREEN_PRESETS-1)
|
||||
// for a given offset/step pair - e.g. offset 1.0/step 0.6 generates
|
||||
// 1.0, 1.6, 2.2, 2.8, 3.4 (this feature's original width presets).
|
||||
static float qs_preset_value(float offset, float step, int index) {
|
||||
return offset + step * (float)index;
|
||||
}
|
||||
|
||||
// Inverse of qs_preset_value() - returns -1 (a legitimate result here,
|
||||
// unlike kResolutions'-style "shouldn't happen" fallbacks - a hand-edited
|
||||
// config file can set any value in between presets) if the current value
|
||||
// doesn't exactly match one of the 5 presets this offset/step pair
|
||||
// generates.
|
||||
static int qs_preset_index(float offset, float step, float value) {
|
||||
for (int i = 0; i < QS_NUM_SCREEN_PRESETS; i++)
|
||||
if (qs_preset_value(offset, step, i) == value)
|
||||
return i;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// 1.6m/2.0m (qs_preset_value() index 1/2 of the shared offset/step
|
||||
// defaults above) for both profiles - the game screen's own original,
|
||||
// pre-this-feature hardcoded shape (QUAD_DISTANCE_METERS in xr_session.c),
|
||||
// so a config file/build that never touches these renders identically to
|
||||
// before until the player opts in.
|
||||
static float s_initial_screen_width_m = 1.6f;
|
||||
static float s_initial_screen_distance_m = 2.0f;
|
||||
static float s_game_screen_width_m = 1.6f;
|
||||
static float s_game_screen_distance_m = 2.0f;
|
||||
// Which profile xr_session.c's game-screen shape was last set to - so
|
||||
// qs_screen_profile_notify_loop_mode() can skip re-applying anything on a
|
||||
// same-profile transition (e.g. game -> automap -> game), and so a preset
|
||||
// button tap while the *other* profile is active knows to just update the
|
||||
// stored value without touching the live shape (see
|
||||
// qs_config_handle_event()'s own screen-profile branches).
|
||||
static bool s_screen_profile_is_game = false;
|
||||
|
||||
static float qs_clamp_screen_width(float width_m) {
|
||||
if (width_m < 0.2f)
|
||||
return 0.2f;
|
||||
if (width_m > 10.0f)
|
||||
return 10.0f;
|
||||
return width_m;
|
||||
}
|
||||
|
||||
static float qs_clamp_screen_distance(float distance_m) {
|
||||
if (distance_m < 0.5f)
|
||||
return 0.5f;
|
||||
if (distance_m > 10.0f)
|
||||
return 10.0f;
|
||||
return distance_m;
|
||||
}
|
||||
|
||||
static void qs_screen_profile_reapply(void) {
|
||||
if (s_screen_profile_is_game)
|
||||
xr_session_set_game_screen_shape(s_game_screen_width_m, 0.0f, s_game_screen_distance_m);
|
||||
else
|
||||
xr_session_set_game_screen_shape(s_initial_screen_width_m, 0.0f,
|
||||
s_initial_screen_distance_m);
|
||||
}
|
||||
|
||||
// Called directly from the engine (see android/engine-patches/
|
||||
// 22-android-screen-profile.patch's hook into mainloop.c's
|
||||
// loopmode_switch(), and qs_config_load_startup() below for the initial
|
||||
// call) - runs on the engine's own thread already, so unlike every other
|
||||
// action in this file (all UI-thread-button-triggered), no cross-thread
|
||||
// SDL-event dispatch is needed here.
|
||||
void qs_screen_profile_notify_loop_mode(short new_mode) {
|
||||
bool isGame = new_mode != QS_SETUP_LOOP && new_mode != QS_CUTSCENE_LOOP;
|
||||
if (isGame == s_screen_profile_is_game)
|
||||
return;
|
||||
s_screen_profile_is_game = isGame;
|
||||
qs_screen_profile_reapply();
|
||||
}
|
||||
|
||||
static bool qs_apply_initial_screen_width(float width_m) {
|
||||
width_m = qs_clamp_screen_width(width_m);
|
||||
bool changed = s_initial_screen_width_m != width_m;
|
||||
s_initial_screen_width_m = width_m;
|
||||
if (changed && !s_screen_profile_is_game)
|
||||
qs_screen_profile_reapply();
|
||||
return changed;
|
||||
}
|
||||
|
||||
static bool qs_apply_game_screen_width(float width_m) {
|
||||
width_m = qs_clamp_screen_width(width_m);
|
||||
bool changed = s_game_screen_width_m != width_m;
|
||||
s_game_screen_width_m = width_m;
|
||||
if (changed && s_screen_profile_is_game)
|
||||
qs_screen_profile_reapply();
|
||||
return changed;
|
||||
}
|
||||
|
||||
static bool qs_apply_initial_screen_distance(float distance_m) {
|
||||
distance_m = qs_clamp_screen_distance(distance_m);
|
||||
bool changed = s_initial_screen_distance_m != distance_m;
|
||||
s_initial_screen_distance_m = distance_m;
|
||||
if (changed && !s_screen_profile_is_game)
|
||||
qs_screen_profile_reapply();
|
||||
return changed;
|
||||
}
|
||||
|
||||
static bool qs_apply_game_screen_distance(float distance_m) {
|
||||
distance_m = qs_clamp_screen_distance(distance_m);
|
||||
bool changed = s_game_screen_distance_m != distance_m;
|
||||
s_game_screen_distance_m = distance_m;
|
||||
if (changed && s_screen_profile_is_game)
|
||||
qs_screen_profile_reapply();
|
||||
return changed;
|
||||
}
|
||||
|
||||
// ===========================================================================
|
||||
// General: language
|
||||
// ===========================================================================
|
||||
@@ -697,6 +889,45 @@ int qs_config_current_resolution_index(void) {
|
||||
|
||||
int qs_config_current_language(void) { return gShockPrefs.goLanguage; }
|
||||
|
||||
// -1 (no exact preset match) is a legitimate result for all four of these -
|
||||
// see qs_preset_index()'s own comment.
|
||||
int qs_config_current_initial_screen_width_index(void) {
|
||||
return qs_preset_index(s_screen_width_offset_m, s_screen_width_step_m, s_initial_screen_width_m);
|
||||
}
|
||||
int qs_config_current_initial_screen_distance_index(void) {
|
||||
return qs_preset_index(s_screen_distance_offset_m, s_screen_distance_step_m,
|
||||
s_initial_screen_distance_m);
|
||||
}
|
||||
int qs_config_current_game_screen_width_index(void) {
|
||||
return qs_preset_index(s_screen_width_offset_m, s_screen_width_step_m, s_game_screen_width_m);
|
||||
}
|
||||
int qs_config_current_game_screen_distance_index(void) {
|
||||
return qs_preset_index(s_screen_distance_offset_m, s_screen_distance_step_m,
|
||||
s_game_screen_distance_m);
|
||||
}
|
||||
|
||||
// Returns the value preset button `index` (0..QS_NUM_SCREEN_PRESETS-1)
|
||||
// currently shows for a given option - shared by both profiles' columns
|
||||
// (see the offset/step statics' own comment) - so MenuOverlay.java can
|
||||
// label its preset buttons correctly even after a config-file offset/step
|
||||
// change. Out-of-range index clamps into range rather than returning a
|
||||
// sentinel: unlike the index getters above, there's no "not present"
|
||||
// concept here - every index 0..4 always has a value.
|
||||
static int qs_clamp_preset_index(int index) {
|
||||
if (index < 0)
|
||||
return 0;
|
||||
if (index >= QS_NUM_SCREEN_PRESETS)
|
||||
return QS_NUM_SCREEN_PRESETS - 1;
|
||||
return index;
|
||||
}
|
||||
float qs_config_screen_width_preset_value(int index) {
|
||||
return qs_preset_value(s_screen_width_offset_m, s_screen_width_step_m, qs_clamp_preset_index(index));
|
||||
}
|
||||
float qs_config_screen_distance_preset_value(int index) {
|
||||
return qs_preset_value(s_screen_distance_offset_m, s_screen_distance_step_m,
|
||||
qs_clamp_preset_index(index));
|
||||
}
|
||||
|
||||
// ===========================================================================
|
||||
// Apply / Save orchestration
|
||||
// ===========================================================================
|
||||
@@ -734,6 +965,41 @@ static void qs_config_apply_parsed(const QsParsedConfig *parsed, bool live, char
|
||||
if (changed && QS_RESOLUTION_REQUIRES_RESTART)
|
||||
qs_append_restart_reason(restart_needed, restart_needed_size, "resolution");
|
||||
}
|
||||
if (parsed->video_options.has_initial_screen_width_m) {
|
||||
bool changed = qs_apply_initial_screen_width(parsed->video_options.initial_screen_width_m);
|
||||
if (changed && QS_SCREEN_PROFILE_REQUIRES_RESTART)
|
||||
qs_append_restart_reason(restart_needed, restart_needed_size, "initial_screen_width_m");
|
||||
}
|
||||
if (parsed->video_options.has_game_screen_width_m) {
|
||||
bool changed = qs_apply_game_screen_width(parsed->video_options.game_screen_width_m);
|
||||
if (changed && QS_SCREEN_PROFILE_REQUIRES_RESTART)
|
||||
qs_append_restart_reason(restart_needed, restart_needed_size, "game_screen_width_m");
|
||||
}
|
||||
if (parsed->video_options.has_initial_screen_distance_m) {
|
||||
bool changed =
|
||||
qs_apply_initial_screen_distance(parsed->video_options.initial_screen_distance_m);
|
||||
if (changed && QS_SCREEN_PROFILE_REQUIRES_RESTART)
|
||||
qs_append_restart_reason(restart_needed, restart_needed_size,
|
||||
"initial_screen_distance_m");
|
||||
}
|
||||
if (parsed->video_options.has_game_screen_distance_m) {
|
||||
bool changed =
|
||||
qs_apply_game_screen_distance(parsed->video_options.game_screen_distance_m);
|
||||
if (changed && QS_SCREEN_PROFILE_REQUIRES_RESTART)
|
||||
qs_append_restart_reason(restart_needed, restart_needed_size,
|
||||
"game_screen_distance_m");
|
||||
}
|
||||
// The four offset/step fields below only reshape what the preset
|
||||
// buttons offer (see their own statics' comment) - no live-reapply
|
||||
// or restart consideration needed, just a direct assignment.
|
||||
if (parsed->video_options.has_screen_width_offset_m)
|
||||
s_screen_width_offset_m = parsed->video_options.screen_width_offset_m;
|
||||
if (parsed->video_options.has_screen_width_step_m)
|
||||
s_screen_width_step_m = parsed->video_options.screen_width_step_m;
|
||||
if (parsed->video_options.has_screen_distance_offset_m)
|
||||
s_screen_distance_offset_m = parsed->video_options.screen_distance_offset_m;
|
||||
if (parsed->video_options.has_screen_distance_step_m)
|
||||
s_screen_distance_step_m = parsed->video_options.screen_distance_step_m;
|
||||
}
|
||||
if (parsed->has_general) {
|
||||
if (parsed->general.language[0] != '\0') {
|
||||
@@ -758,7 +1024,15 @@ static bool qs_config_write_file(void) {
|
||||
fprintf(f, "{\n");
|
||||
fprintf(f, " \"video_options\": {\n");
|
||||
fprintf(f, " \"renderer\": \"%s\",\n", qs_current_renderer());
|
||||
fprintf(f, " \"resolution\": \"%s\"\n", qs_current_resolution());
|
||||
fprintf(f, " \"resolution\": \"%s\",\n", qs_current_resolution());
|
||||
fprintf(f, " \"initial_screen_width_m\": %f,\n", s_initial_screen_width_m);
|
||||
fprintf(f, " \"initial_screen_distance_m\": %f,\n", s_initial_screen_distance_m);
|
||||
fprintf(f, " \"game_screen_width_m\": %f,\n", s_game_screen_width_m);
|
||||
fprintf(f, " \"game_screen_distance_m\": %f,\n", s_game_screen_distance_m);
|
||||
fprintf(f, " \"screen_width_offset_m\": %f,\n", s_screen_width_offset_m);
|
||||
fprintf(f, " \"screen_width_step_m\": %f,\n", s_screen_width_step_m);
|
||||
fprintf(f, " \"screen_distance_offset_m\": %f,\n", s_screen_distance_offset_m);
|
||||
fprintf(f, " \"screen_distance_step_m\": %f\n", s_screen_distance_step_m);
|
||||
fprintf(f, " },\n");
|
||||
fprintf(f, " \"general\": {\n");
|
||||
fprintf(f, " \"language\": \"%s\"\n", qs_current_language_name());
|
||||
@@ -853,24 +1127,36 @@ static Uint32 s_set_renderer_event_type;
|
||||
static Uint32 s_set_resolution_event_type;
|
||||
static Uint32 s_set_language_event_type;
|
||||
static Uint32 s_reset_position_event_type;
|
||||
static Uint32 s_set_initial_screen_width_event_type;
|
||||
static Uint32 s_set_initial_screen_distance_event_type;
|
||||
static Uint32 s_set_game_screen_width_event_type;
|
||||
static Uint32 s_set_game_screen_distance_event_type;
|
||||
|
||||
void qs_config_load_startup(void) {
|
||||
// Registers the custom SDL event types qs_config_request_save()/_load()/
|
||||
// _set_renderer()/_set_resolution()/_set_language()/_reset_position()
|
||||
// enqueue - done here, once, on the engine's own thread and before the
|
||||
// menu/its buttons could possibly exist yet, rather than lazily from
|
||||
// those functions themselves (which run on Android's UI thread and
|
||||
// could otherwise race each other doing this lazily).
|
||||
Uint32 base = SDL_RegisterEvents(6);
|
||||
// _set_renderer()/_set_resolution()/_set_language()/_reset_position()/
|
||||
// the four _set_*_screen_*() screen-profile setters enqueue - done
|
||||
// here, once, on the engine's own thread and before the menu/its
|
||||
// buttons could possibly exist yet, rather than lazily from those
|
||||
// functions themselves (which run on Android's UI thread and could
|
||||
// otherwise race each other doing this lazily).
|
||||
Uint32 base = SDL_RegisterEvents(10);
|
||||
s_save_event_type = base;
|
||||
s_load_event_type = base + 1;
|
||||
s_set_renderer_event_type = base + 2;
|
||||
s_set_resolution_event_type = base + 3;
|
||||
s_set_language_event_type = base + 4;
|
||||
s_reset_position_event_type = base + 5;
|
||||
s_set_initial_screen_width_event_type = base + 6;
|
||||
s_set_game_screen_width_event_type = base + 7;
|
||||
s_set_initial_screen_distance_event_type = base + 8;
|
||||
s_set_game_screen_distance_event_type = base + 9;
|
||||
|
||||
QsParsedConfig parsed;
|
||||
if (qs_config_read_file(&parsed))
|
||||
bool haveConfig = qs_config_read_file(&parsed);
|
||||
if (!haveConfig)
|
||||
haveConfig = qs_config_read_file_named(QS_CONFIG_DEFAULT_FILENAME, &parsed);
|
||||
if (haveConfig)
|
||||
qs_config_apply_parsed(&parsed, /*live=*/false, NULL, 0);
|
||||
|
||||
// A saved player_position only means anything if all three fields are
|
||||
@@ -884,6 +1170,19 @@ void qs_config_load_startup(void) {
|
||||
if (parsed.has_player_position && parsed.player_position.has_x &&
|
||||
parsed.player_position.has_z && parsed.player_position.has_yaw_degrees)
|
||||
xr_input_skip_startup_recenter();
|
||||
|
||||
// The engine's own startup state is always QS_SETUP_LOOP (mainloop.c:
|
||||
// `short _current_loop = SETUP_LOOP;`, Shock.c's main() never changes
|
||||
// it before entering mainloop()), matching s_screen_profile_is_game's
|
||||
// own default (false/"initial") - so calling
|
||||
// qs_screen_profile_notify_loop_mode() here would see "no change" and
|
||||
// skip actually applying anything (its whole point is to skip
|
||||
// redundant re-applies on a same-profile transition, but there's no
|
||||
// "previous" profile yet this early). Call the apply step directly
|
||||
// instead, unconditionally, so whichever profile's shape was just
|
||||
// loaded/defaulted above actually reaches xr_session.c before the
|
||||
// first frame renders.
|
||||
qs_screen_profile_reapply();
|
||||
}
|
||||
|
||||
void qs_config_request_save(void) {
|
||||
@@ -931,6 +1230,42 @@ void qs_config_request_reset_position(void) {
|
||||
SDL_PushEvent(&event);
|
||||
}
|
||||
|
||||
// Screen profile preset buttons (Video Options tab) - index into the
|
||||
// shared width/distance offset+step-generated presets (see
|
||||
// qs_preset_value()'s own comment), same enqueue-only reasoning as every
|
||||
// other button in this file.
|
||||
void qs_config_request_set_initial_screen_width(int preset_index) {
|
||||
SDL_Event event;
|
||||
SDL_zero(event);
|
||||
event.type = s_set_initial_screen_width_event_type;
|
||||
event.user.code = preset_index;
|
||||
SDL_PushEvent(&event);
|
||||
}
|
||||
|
||||
void qs_config_request_set_game_screen_width(int preset_index) {
|
||||
SDL_Event event;
|
||||
SDL_zero(event);
|
||||
event.type = s_set_game_screen_width_event_type;
|
||||
event.user.code = preset_index;
|
||||
SDL_PushEvent(&event);
|
||||
}
|
||||
|
||||
void qs_config_request_set_initial_screen_distance(int preset_index) {
|
||||
SDL_Event event;
|
||||
SDL_zero(event);
|
||||
event.type = s_set_initial_screen_distance_event_type;
|
||||
event.user.code = preset_index;
|
||||
SDL_PushEvent(&event);
|
||||
}
|
||||
|
||||
void qs_config_request_set_game_screen_distance(int preset_index) {
|
||||
SDL_Event event;
|
||||
SDL_zero(event);
|
||||
event.type = s_set_game_screen_distance_event_type;
|
||||
event.user.code = preset_index;
|
||||
SDL_PushEvent(&event);
|
||||
}
|
||||
|
||||
bool qs_config_handle_event(const SDL_Event *event) {
|
||||
if (event->type == s_save_event_type) {
|
||||
if (qs_config_write_file())
|
||||
@@ -982,5 +1317,33 @@ bool qs_config_handle_event(const SDL_Event *event) {
|
||||
message_info("Recentered");
|
||||
return true;
|
||||
}
|
||||
if (event->type == s_set_initial_screen_width_event_type) {
|
||||
int index = event->user.code;
|
||||
if (index >= 0 && index < QS_NUM_SCREEN_PRESETS)
|
||||
qs_apply_initial_screen_width(
|
||||
qs_preset_value(s_screen_width_offset_m, s_screen_width_step_m, index));
|
||||
return true;
|
||||
}
|
||||
if (event->type == s_set_game_screen_width_event_type) {
|
||||
int index = event->user.code;
|
||||
if (index >= 0 && index < QS_NUM_SCREEN_PRESETS)
|
||||
qs_apply_game_screen_width(
|
||||
qs_preset_value(s_screen_width_offset_m, s_screen_width_step_m, index));
|
||||
return true;
|
||||
}
|
||||
if (event->type == s_set_initial_screen_distance_event_type) {
|
||||
int index = event->user.code;
|
||||
if (index >= 0 && index < QS_NUM_SCREEN_PRESETS)
|
||||
qs_apply_initial_screen_distance(
|
||||
qs_preset_value(s_screen_distance_offset_m, s_screen_distance_step_m, index));
|
||||
return true;
|
||||
}
|
||||
if (event->type == s_set_game_screen_distance_event_type) {
|
||||
int index = event->user.code;
|
||||
if (index >= 0 && index < QS_NUM_SCREEN_PRESETS)
|
||||
qs_apply_game_screen_distance(
|
||||
qs_preset_value(s_screen_distance_offset_m, s_screen_distance_step_m, index));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user