citra_qt: Add indicator in status bar

Since we do not have an overlay yet, it can be confusing whether movie is being recorded or played. This makes it clear.

Status messages (e.g. system archive missing) will be overriden, but that shouldn't be too important when recording movies.

Doubled the status bar updating frequency to provide a better experience. It now updates every second.
This commit is contained in:
zhupengfei 2020-07-07 00:15:26 +08:00
parent 113e0c7331
commit fb14bd956a
No known key found for this signature in database
GPG Key ID: DD129E108BD09378
4 changed files with 61 additions and 17 deletions

View File

@ -1055,7 +1055,7 @@ void GMainWindow::BootGame(const QString& filename) {
game_list->hide(); game_list->hide();
game_list_placeholder->hide(); game_list_placeholder->hide();
} }
status_bar_update_timer.start(2000); status_bar_update_timer.start(1000);
if (UISettings::values.hide_mouse) { if (UISettings::values.hide_mouse) {
mouse_hide_timer.start(); mouse_hide_timer.start();
@ -1165,6 +1165,7 @@ void GMainWindow::ShutdownGame() {
// Disable status bar updates // Disable status bar updates
status_bar_update_timer.stop(); status_bar_update_timer.stop();
message_label->setVisible(false); message_label->setVisible(false);
message_label_used_for_movie = false;
emu_speed_label->setVisible(false); emu_speed_label->setVisible(false);
game_fps_label->setVisible(false); game_fps_label->setVisible(false);
emu_frametime_label->setVisible(false); emu_frametime_label->setVisible(false);
@ -1982,6 +1983,23 @@ void GMainWindow::UpdateStatusBar() {
return; return;
} }
// Update movie status
const u64 current = Core::Movie::GetInstance().GetCurrentInputIndex();
const u64 total = Core::Movie::GetInstance().GetTotalInputCount();
if (Core::Movie::GetInstance().IsRecordingInput()) {
message_label->setText(tr("Recording %1").arg(current));
message_label->setVisible(true);
message_label_used_for_movie = true;
} else if (Core::Movie::GetInstance().IsPlayingInput()) {
message_label->setText(tr("Playing %1 / %2").arg(current).arg(total));
message_label->setVisible(true);
message_label_used_for_movie = true;
} else if (message_label_used_for_movie) { // Clear the label if movie was just closed
message_label->setText(QString{});
message_label->setVisible(false);
message_label_used_for_movie = false;
}
auto results = Core::System::GetInstance().GetAndResetPerfStats(); auto results = Core::System::GetInstance().GetAndResetPerfStats();
if (Settings::values.use_frame_limit_alternate) { if (Settings::values.use_frame_limit_alternate) {
@ -2093,6 +2111,7 @@ void GMainWindow::OnCoreError(Core::System::ResultStatus result, std::string det
emu_thread->SetRunning(true); emu_thread->SetRunning(true);
message_label->setText(status_message); message_label->setText(status_message);
message_label->setVisible(true); message_label->setVisible(true);
message_label_used_for_movie = false;
} }
} }
} }

View File

@ -248,6 +248,7 @@ private:
QLabel* game_fps_label = nullptr; QLabel* game_fps_label = nullptr;
QLabel* emu_frametime_label = nullptr; QLabel* emu_frametime_label = nullptr;
QTimer status_bar_update_timer; QTimer status_bar_update_timer;
bool message_label_used_for_movie = false;
MultiplayerState* multiplayer_state = nullptr; MultiplayerState* multiplayer_state = nullptr;
std::unique_ptr<Config> config; std::unique_ptr<Config> config;

View File

@ -129,6 +129,22 @@ struct CTMHeader {
static_assert(sizeof(CTMHeader) == 256, "CTMHeader should be 256 bytes"); static_assert(sizeof(CTMHeader) == 256, "CTMHeader should be 256 bytes");
#pragma pack(pop) #pragma pack(pop)
static u64 GetInputCount(const std::vector<u8>& input) {
u64 input_count = 0;
for (std::size_t pos = 0; pos < input.size(); pos += sizeof(ControllerState)) {
if (input.size() < pos + sizeof(ControllerState)) {
break;
}
ControllerState state;
std::memcpy(&state, input.data() + pos, sizeof(ControllerState));
if (state.type == ControllerStateType::PadAndCircle) {
input_count++;
}
}
return input_count;
}
template <class Archive> template <class Archive>
void Movie::serialize(Archive& ar, const unsigned int file_version) { void Movie::serialize(Archive& ar, const unsigned int file_version) {
// Only serialize what's needed to make savestates useful for TAS: // Only serialize what's needed to make savestates useful for TAS:
@ -136,6 +152,10 @@ void Movie::serialize(Archive& ar, const unsigned int file_version) {
ar& _current_byte; ar& _current_byte;
current_byte = static_cast<std::size_t>(_current_byte); current_byte = static_cast<std::size_t>(_current_byte);
if (file_version > 0) {
ar& current_input;
}
std::vector<u8> recorded_input_ = recorded_input; std::vector<u8> recorded_input_ = recorded_input;
ar& recorded_input_; ar& recorded_input_;
@ -167,6 +187,7 @@ void Movie::serialize(Archive& ar, const unsigned int file_version) {
"This savestate was created at a later point and must be loaded in R+W mode"); "This savestate was created at a later point and must be loaded in R+W mode");
} }
play_mode = PlayMode::Playing; play_mode = PlayMode::Playing;
total_input = GetInputCount(recorded_input);
} else { } else {
recorded_input = std::move(recorded_input_); recorded_input = std::move(recorded_input_);
play_mode = PlayMode::Recording; play_mode = PlayMode::Recording;
@ -184,6 +205,13 @@ bool Movie::IsRecordingInput() const {
return play_mode == PlayMode::Recording; return play_mode == PlayMode::Recording;
} }
u64 Movie::GetCurrentInputIndex() const {
return current_input;
}
u64 Movie::GetTotalInputCount() const {
return total_input;
}
void Movie::CheckInputEnd() { void Movie::CheckInputEnd() {
if (current_byte + sizeof(ControllerState) > recorded_input.size()) { if (current_byte + sizeof(ControllerState) > recorded_input.size()) {
LOG_INFO(Movie, "Playback finished"); LOG_INFO(Movie, "Playback finished");
@ -198,6 +226,7 @@ void Movie::Play(Service::HID::PadState& pad_state, s16& circle_pad_x, s16& circ
ControllerState s; ControllerState s;
std::memcpy(&s, &recorded_input[current_byte], sizeof(ControllerState)); std::memcpy(&s, &recorded_input[current_byte], sizeof(ControllerState));
current_byte += sizeof(ControllerState); current_byte += sizeof(ControllerState);
current_input++;
if (s.type != ControllerStateType::PadAndCircle) { if (s.type != ControllerStateType::PadAndCircle) {
LOG_ERROR(Movie, LOG_ERROR(Movie,
@ -325,6 +354,8 @@ void Movie::Record(const ControllerState& controller_state) {
void Movie::Record(const Service::HID::PadState& pad_state, const s16& circle_pad_x, void Movie::Record(const Service::HID::PadState& pad_state, const s16& circle_pad_x,
const s16& circle_pad_y) { const s16& circle_pad_y) {
current_input++;
ControllerState s; ControllerState s;
s.type = ControllerStateType::PadAndCircle; s.type = ControllerStateType::PadAndCircle;
@ -429,22 +460,6 @@ Movie::ValidationResult Movie::ValidateHeader(const CTMHeader& header) const {
return ValidationResult::OK; return ValidationResult::OK;
} }
static u64 GetInputCount(const std::vector<u8>& input) {
u64 input_count = 0;
for (std::size_t pos = 0; pos < input.size(); pos += sizeof(ControllerState)) {
if (input.size() < pos + sizeof(ControllerState)) {
break;
}
ControllerState state;
std::memcpy(&state, input.data() + pos, sizeof(ControllerState));
if (state.type == ControllerStateType::PadAndCircle) {
input_count++;
}
}
return input_count;
}
Movie::ValidationResult Movie::ValidateInput(const std::vector<u8>& input, Movie::ValidationResult Movie::ValidateInput(const std::vector<u8>& input,
u64 expected_count) const { u64 expected_count) const {
return GetInputCount(input) == expected_count ? ValidationResult::OK return GetInputCount(input) == expected_count ? ValidationResult::OK
@ -507,11 +522,13 @@ void Movie::StartPlayback(const std::string& movie_file) {
record_movie_author = author.data(); record_movie_author = author.data();
rerecord_count = header.rerecord_count; rerecord_count = header.rerecord_count;
total_input = header.input_count;
recorded_input.resize(size - sizeof(CTMHeader)); recorded_input.resize(size - sizeof(CTMHeader));
save_record.ReadArray(recorded_input.data(), recorded_input.size()); save_record.ReadArray(recorded_input.data(), recorded_input.size());
current_byte = 0; current_byte = 0;
current_input = 0;
id = header.id; id = header.id;
LOG_INFO(Movie, "Loaded Movie, ID: {:016X}", id); LOG_INFO(Movie, "Loaded Movie, ID: {:016X}", id);
@ -622,6 +639,7 @@ void Movie::Shutdown() {
recorded_input.resize(0); recorded_input.resize(0);
record_movie_file.clear(); record_movie_file.clear();
current_byte = 0; current_byte = 0;
current_input = 0;
init_time = 0; init_time = 0;
id = 0; id = 0;
} }

View File

@ -123,6 +123,9 @@ public:
bool IsPlayingInput() const; bool IsPlayingInput() const;
bool IsRecordingInput() const; bool IsRecordingInput() const;
u64 GetCurrentInputIndex() const;
u64 GetTotalInputCount() const;
private: private:
static Movie s_instance; static Movie s_instance;
@ -159,6 +162,9 @@ private:
std::vector<u8> recorded_input; std::vector<u8> recorded_input;
std::size_t current_byte = 0; std::size_t current_byte = 0;
u64 current_input = 0;
// Total input count of the current movie being played. Not used for recording.
u64 total_input = 0;
u64 id = 0; // ID of the current movie loaded u64 id = 0; // ID of the current movie loaded
u64 init_time; u64 init_time;