Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Qt: Exit And Save Log - toolbar action #14212

Merged
merged 5 commits into from Jul 18, 2023
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions rpcs3/rpcs3qt/gui_settings.h
Expand Up @@ -135,6 +135,7 @@ namespace gui
const gui_save fd_insert_disc = gui_save(main_window, "lastExplorePathDISC", "");
const gui_save fd_cfg_check = gui_save(main_window, "lastExplorePathCfgChk", "");
const gui_save fd_save_elf = gui_save(main_window, "lastExplorePathSaveElf", "");
const gui_save fd_save_log = gui_save(main_window, "lastExplorePathSaveLog", "");

const gui_save mw_debugger = gui_save(main_window, "debuggerVisible", false);
const gui_save mw_logger = gui_save(main_window, "loggerVisible", true);
Expand Down
138 changes: 138 additions & 0 deletions rpcs3/rpcs3qt/main_window.cpp
Expand Up @@ -2347,6 +2347,124 @@ void main_window::CreateConnects()

connect(ui->bootInstallPkgAct, &QAction::triggered, this, [this] {InstallPackages(); });
connect(ui->bootInstallPupAct, &QAction::triggered, this, [this] {InstallPup(); });

connect(this, &main_window::NotifyWindowCloseEvent, this, [this](bool closed)
{
if (!closed)
{
// Cancel the request
m_requested_show_logs_on_exit = false;
return;
}

if (!m_requested_show_logs_on_exit)
{
// Not requested
return;
}

const std::string archived_path = fs::get_cache_dir() + "RPCS3.log.gz";
const std::string raw_file_path = fs::get_cache_dir() + "RPCS3.log";

fs::stat_t raw_stat{};
fs::stat_t archived_stat{};

if ((!fs::stat(raw_file_path, raw_stat) || raw_stat.is_directory) || (!fs::stat(archived_path, archived_stat) || archived_stat.is_directory) || (raw_stat.size == 0 && archived_stat.size == 0))
{
QMessageBox::warning(this, tr("Failed to locate log"), tr("Failed to locate log files.\nMake sure that RPCS3.log and RPCS3.log.gz are writable and can be created without permission issues."));
return;
}

// Get new filename from title and title ID but simplified
std::string log_filename = Emu.GetTitleID().empty() ? "RPCS3" : Emu.GetTitleAndTitleID();
log_filename.erase(std::remove_if(log_filename.begin(), log_filename.end(), [](u8 c){ return !std::isalnum(c) && c != ' ' && c != '[' && ']'; }), log_filename.end());
fmt::trim_back(log_filename);

QString path_last_log = m_gui_settings->GetValue(gui::fd_save_log).toString();

auto move_log = [](const std::string& from, const std::string& to)
{
if (from == to)
{
return false;
}

// Test writablity here to avoid closing the log with no *chance* of success
if (fs::file test_writable{to, fs::write + fs::create}; !test_writable)
{
return false;
}

// Close and flush log file handle (!)
// Cannot rename the file due to file management design
logs::listener::close_all_prematurely();

// Try to move it
if (fs::rename(from, to, true))
{
return true;
}

// Try to copy it if fails
if (fs::copy_file(from, to, true))
{
fs::remove_file(from);
return true;
}

return false;
};

if (archived_stat.size)
{
const QString dir_path = QFileDialog::getExistingDirectory(this, tr("Select RPCS3's log saving location (saving %0)").arg(qstr(log_filename + ".log.gz")), path_last_log, QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks);

if (dir_path.isEmpty())
{
// Aborted - view the current location
gui::utils::open_dir(archived_path);
return;
}

const std::string dest_archived_path = dir_path.toStdString() + "/" + log_filename + ".log.gz";

if (!Emu.GetTitleID().empty() && !dest_archived_path.empty() && move_log(archived_path, dest_archived_path))
{
m_gui_settings->SetValue(gui::fd_save_log, dir_path);
gui_log.success("Moved log file to '%s'!", dest_archived_path);
return;
}

gui::utils::open_dir(archived_path);
return;
}

const QString dir_path = QFileDialog::getExistingDirectory(this, tr("Select RPCS3's log saving location (saving %0)").arg(qstr(log_filename + ".log")), path_last_log, QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks);

if (dir_path.isEmpty())
{
// Aborted - view the current location
gui::utils::open_dir(raw_file_path);
return;
}

const std::string dest_raw_file_path = dir_path.toStdString() + "/" + log_filename + ".log";

if (!Emu.GetTitleID().empty() && !dest_raw_file_path.empty() && move_log(raw_file_path, dest_raw_file_path))
{
m_gui_settings->SetValue(gui::fd_save_log, dir_path);
gui_log.success("Moved log file to '%s'!", dest_raw_file_path);
return;
}

gui::utils::open_dir(raw_file_path);
});

connect(ui->exitAndSaveLogAct, &QAction::triggered, this, [this]()
{
m_requested_show_logs_on_exit = true;
close();
});
connect(ui->exitAct, &QAction::triggered, this, &QWidget::close);

connect(ui->batchCreatePPUCachesAct, &QAction::triggered, m_game_list_frame, &game_list_frame::BatchCreatePPUCaches);
Expand Down Expand Up @@ -3212,6 +3330,7 @@ void main_window::closeEvent(QCloseEvent* closeEvent)
{
if (!m_gui_settings->GetBootConfirmation(this, gui::ib_confirm_exit))
{
Q_EMIT NotifyWindowCloseEvent(false);
closeEvent->ignore();
return;
}
Expand All @@ -3223,6 +3342,12 @@ void main_window::closeEvent(QCloseEvent* closeEvent)
}

SaveWindowState();

// Flush logs here as well
logs::listener::sync_all();

Q_EMIT NotifyWindowCloseEvent(true);

Emu.Quit(true);
}

Expand All @@ -3248,13 +3373,26 @@ Check data for valid file types and cache their paths if necessary
*/
main_window::drop_type main_window::IsValidFile(const QMimeData& md, QStringList* drop_paths)
{
if (drop_paths)
{
drop_paths->clear();
}

drop_type type = drop_type::drop_error;

QList<QUrl> list = md.urls(); // get list of all the dropped file urls

// Try to cache the data for half a second
if (m_drop_file_timestamp != umax && m_drop_file_url_list == list && get_system_time() - m_drop_file_timestamp < 500'000)
{
if (drop_paths)
{
for (auto&& url : m_drop_file_url_list)
{
drop_paths->append(url.toLocalFile());
}
}

return m_drop_file_cached_drop_type;
}

Expand Down
2 changes: 2 additions & 0 deletions rpcs3/rpcs3qt/main_window.h
Expand Up @@ -50,6 +50,7 @@ class main_window : public QMainWindow

bool m_is_list_mode = true;
bool m_save_slider_pos = false;
bool m_requested_show_logs_on_exit = false;
int m_other_slider_pos = 0;

QIcon m_app_icon;
Expand Down Expand Up @@ -95,6 +96,7 @@ class main_window : public QMainWindow
void RequestGlobalStylesheetChange();
void RequestTrophyManagerRepaint();
void NotifyEmuSettingsChange();
void NotifyWindowCloseEvent(bool closed);

public Q_SLOTS:
void OnEmuStop();
Expand Down
12 changes: 12 additions & 0 deletions rpcs3/rpcs3qt/main_window.ui
Expand Up @@ -213,6 +213,7 @@
<addaction name="menuBatch"/>
<addaction name="menuFirmware"/>
<addaction name="separator"/>
<addaction name="exitAndSaveLogAct"/>
<addaction name="exitAct"/>
</widget>
<widget class="QMenu" name="menuEmulation">
Expand Down Expand Up @@ -588,6 +589,17 @@
<string>Configure Auto Pause</string>
</property>
</action>
<action name="exitAndSaveLogAct">
<property name="text">
<string>Exit And Save Log</string>
</property>
<property name="toolTip">
<string>Exit RPCS3, move the log file to a custom location</string>
</property>
<property name="statusTip">
<string>Exit the application and save the log to a user-defined location</string>
</property>
</action>
<action name="exitAct">
<property name="text">
<string>Exit</string>
Expand Down
72 changes: 71 additions & 1 deletion rpcs3/util/logs.cpp
Expand Up @@ -107,6 +107,9 @@ namespace logs

// Ensure written to disk
void sync();

// Close file handle after flushing to disk
void close_prematurely();
};

struct file_listener final : file_writer, public listener
Expand All @@ -121,6 +124,11 @@ namespace logs
{
file_writer::sync();
}

void close_prematurely() override
{
file_writer::close_prematurely();
}
};

struct root_listener final : public listener
Expand Down Expand Up @@ -353,6 +361,10 @@ void logs::listener::sync()
{
}

void logs::listener::close_prematurely()
{
}

void logs::listener::sync_all()
{
for (listener* lis = get_logger(); lis; lis = lis->m_next)
Expand All @@ -361,6 +373,14 @@ void logs::listener::sync_all()
}
}

void logs::listener::close_all_prematurely()
{
for (listener* lis = get_logger(); lis; lis = lis->m_next)
{
lis->close_prematurely();
}
}

logs::registerer::registerer(channel& _ch)
{
std::lock_guard lock(g_mutex);
Expand Down Expand Up @@ -535,7 +555,7 @@ logs::file_writer::~file_writer()
}

#ifdef _WIN32
// Cancel compressed log file autodeletion
// Cancel compressed log file auto-deletion
FILE_DISPOSITION_INFO disp;
disp.DeleteFileW = false;
SetFileInformationByHandle(m_fout2.get_handle(), FileDispositionInfo, &disp, sizeof(disp));
Expand Down Expand Up @@ -691,6 +711,56 @@ void logs::file_writer::sync()
}
}

void logs::file_writer::close_prematurely()
{
if (!m_fptr)
{
return;
}

// Ensure written to disk
sync();

std::lock_guard lock(m_m);

if (m_fout2)
{
m_zs.avail_in = 0;
m_zs.next_in = nullptr;

do
{
m_zs.avail_out = sizeof(m_zout);
m_zs.next_out = m_zout;

if (deflate(&m_zs, Z_FINISH) == Z_STREAM_ERROR || m_fout2.write(m_zout, sizeof(m_zout) - m_zs.avail_out) != sizeof(m_zout) - m_zs.avail_out)
{
break;
}
}
while (m_zs.avail_out == 0);

deflateEnd(&m_zs);

#ifdef _WIN32
// Cancel compressed log file auto-deletion
FILE_DISPOSITION_INFO disp;
disp.DeleteFileW = false;
SetFileInformationByHandle(m_fout2.get_handle(), FileDispositionInfo, &disp, sizeof(disp));
#else
// Restore compressed log file permissions
::fchmod(m_fout2.get_handle(), S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
#endif

m_fout2.close();
}

if (m_fout)
{
m_fout.close();
}
}

logs::file_listener::file_listener(const std::string& path, u64 max_size)
: file_writer(path, max_size)
, listener()
Expand Down
6 changes: 6 additions & 0 deletions rpcs3/util/logs.hpp
Expand Up @@ -84,6 +84,9 @@ namespace logs
// Flush contents (file writer)
virtual void sync();

// Close file handle after flushing to disk (hazardous)
virtual void close_prematurely();

// Add new listener
static void add(listener*);

Expand All @@ -92,6 +95,9 @@ namespace logs

// Flush log to disk
static void sync_all();

// Close file handle after flushing to disk (hazardous)
static void close_all_prematurely();
};

struct alignas(16) channel : private message
Expand Down