Qt: Exit And Save Log - toolbar action (#14212)

Fixup main_window::IsValidFile
This commit is contained in:
Elad Ashkenazi 2023-07-18 05:11:53 +03:00 committed by GitHub
parent db029ed29f
commit ba988f1d3f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 230 additions and 1 deletions

View file

@ -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);

View file

@ -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);
@ -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;
}
@ -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);
}
@ -3248,6 +3373,11 @@ 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
@ -3255,6 +3385,14 @@ main_window::drop_type main_window::IsValidFile(const QMimeData& md, QStringList
// 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;
}

View file

@ -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;
@ -95,6 +96,7 @@ Q_SIGNALS:
void RequestGlobalStylesheetChange();
void RequestTrophyManagerRepaint();
void NotifyEmuSettingsChange();
void NotifyWindowCloseEvent(bool closed);
public Q_SLOTS:
void OnEmuStop();

View file

@ -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">
@ -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>

View file

@ -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
@ -121,6 +124,11 @@ namespace logs
{
file_writer::sync();
}
void close_prematurely() override
{
file_writer::close_prematurely();
}
};
struct root_listener final : public listener
@ -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)
@ -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);
@ -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));
@ -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()

View file

@ -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*);
@ -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