ui: Create SnapshotManager, check snapshot disc image path

This commit is contained in:
Matt Borgerson 2023-06-04 16:23:34 -07:00 committed by mborgerson
parent 756e423eac
commit 0155721cfe
12 changed files with 440 additions and 175 deletions

View File

@ -31,6 +31,7 @@
#include "migration/qemu-file.h"
#include "migration/snapshot.h"
#include "qapi/error.h"
#include "qapi/qapi-commands-block.h"
#include "sysemu/runstate.h"
#include "qemu-common.h"
@ -53,6 +54,7 @@ static void xemu_snapshots_load_data(BlockDriverState *bs_ro,
QEMUSnapshotInfo *info,
XemuSnapshotData *data, Error **err)
{
data->disc_path = NULL;
data->xbe_title_name = NULL;
data->gl_thumbnail = 0;
@ -82,11 +84,28 @@ static void xemu_snapshots_load_data(BlockDriverState *bs_ro,
return;
}
const size_t xbe_title_name_size = buf[0];
offset = 1;
assert(size >= 9);
offset = 0;
const size_t disc_path_size = be32_to_cpu(*(uint32_t *)&buf[offset]);
offset += 4;
if (disc_path_size) {
data->disc_path = (char *)g_malloc(disc_path_size + 1);
assert(size >= (offset + disc_path_size));
memcpy(data->disc_path, &buf[offset], disc_path_size);
data->disc_path[disc_path_size] = 0;
offset += disc_path_size;
}
assert(size >= (offset + 4));
const size_t xbe_title_name_size = buf[offset];
offset += 1;
if (xbe_title_name_size) {
data->xbe_title_name = (char *)g_malloc(xbe_title_name_size + 1);
assert(size >= (offset + xbe_title_name_size));
memcpy(data->xbe_title_name, &buf[offset], xbe_title_name_size);
data->xbe_title_name[xbe_title_name_size] = 0;
offset += xbe_title_name_size;
@ -98,6 +117,7 @@ static void xemu_snapshots_load_data(BlockDriverState *bs_ro,
if (thumbnail_size) {
GLuint thumbnail;
glGenTextures(1, &thumbnail);
assert(size >= (offset + thumbnail_size));
if (xemu_snapshots_load_png_to_texture(thumbnail, &buf[offset],
thumbnail_size)) {
data->gl_thumbnail = thumbnail;
@ -202,6 +222,30 @@ done:
return xemu_snapshots_len;
}
char *xemu_get_currently_loaded_disc_path(void)
{
char *file = NULL;
BlockInfoList *block_list, *info;
block_list = qmp_query_block(NULL);
for (info = block_list; info; info = info->next) {
if (strcmp("ide0-cd1", info->value->device)) {
continue;
}
if (info->value->has_inserted) {
BlockDeviceInfo *inserted = info->value->inserted;
if (inserted->has_node_name) {
file = g_strdup(inserted->file);
}
}
}
qapi_free_BlockInfoList(block_list);
return file;
}
void xemu_snapshots_load(const char *vm_name, Error **err)
{
bool vm_running = runstate_is_running();
@ -223,6 +267,9 @@ void xemu_snapshots_delete(const char *vm_name, Error **err)
void xemu_snapshots_save_extra_data(QEMUFile *f)
{
char *path = xemu_get_currently_loaded_disc_path();
size_t path_size = path ? strlen(path) : 0;
size_t xbe_title_name_size = 0;
char *xbe_title_name = NULL;
struct xbe *xbe_data = xemu_get_xbe_info();
@ -239,7 +286,13 @@ void xemu_snapshots_save_extra_data(QEMUFile *f)
qemu_put_be32(f, XEMU_SNAPSHOT_DATA_MAGIC);
qemu_put_be32(f, XEMU_SNAPSHOT_DATA_VERSION);
qemu_put_be32(f, 1 + xbe_title_name_size + 4 + thumbnail_size);
qemu_put_be32(f, 4 + path_size + 1 + xbe_title_name_size + 4 + thumbnail_size);
qemu_put_be32(f, path_size);
if (path_size) {
qemu_put_buffer(f, (const uint8_t *)path, path_size);
g_free(path);
}
qemu_put_byte(f, xbe_title_name_size);
if (xbe_title_name_size) {

View File

@ -37,11 +37,13 @@ extern "C" {
extern const char **g_snapshot_shortcut_index_key_map[];
typedef struct XemuSnapshotData {
char *disc_path;
char *xbe_title_name;
GLuint gl_thumbnail;
} XemuSnapshotData;
// Implemented in xemu-snapshots.c
char *xemu_get_currently_loaded_disc_path(void);
int xemu_snapshots_list(QEMUSnapshotInfo **info, XemuSnapshotData **extra_data,
Error **err);
void xemu_snapshots_load(const char *vm_name, Error **err);

View File

@ -55,6 +55,7 @@
#include "hw/xbox/smbus.h" // For eject, drive tray
#include "hw/xbox/nv2a/nv2a.h"
#include "ui/xemu-notifications.h"
#include <stb_image.h>
@ -1556,26 +1557,34 @@ int main(int argc, char **argv)
// rcu_unregister_thread();
}
void xemu_eject_disc(void)
void xemu_eject_disc(Error **errp)
{
Error *error = NULL;
xbox_smc_eject_button();
// Xbox software may request that the drive open, but do it now anyway
Error *err = NULL;
qmp_eject(true, "ide0-cd1", false, NULL, true, false, &err);
qmp_eject(true, "ide0-cd1", false, NULL, true, false, &error);
if (error) {
error_propagate(errp, error);
}
xbox_smc_update_tray_state();
}
void xemu_load_disc(const char *path)
void xemu_load_disc(const char *path, Error **errp)
{
Error *error = NULL;
// Ensure an eject sequence is always triggered so Xbox software reloads
xbox_smc_eject_button();
Error *err = NULL;
qmp_blockdev_change_medium(true, "ide0-cd1", false, NULL, path,
false, "", false, 0,
&err);
&error);
if (error) {
error_propagate(errp, error);
}
xbox_smc_update_tray_state();
}

View File

@ -17,19 +17,29 @@
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
#include "common.hh"
#include "actions.hh"
#include "misc.hh"
#include "xemu-hud.h"
#include "../xemu-snapshots.h"
#include "../xemu-notifications.h"
#include "snapshot-manager.hh"
void ActionEjectDisc(void)
{
xemu_settings_set_string(&g_config.sys.files.dvd_path, "");
xemu_eject_disc();
Error *err = NULL;
xemu_eject_disc(&err);
if (err) {
xemu_queue_error_message(error_get_pretty(err));
error_free(err);
}
}
void ActionLoadDisc(void)
{
Error *err = NULL;
const char *iso_file_filters = ".iso Files\0*.iso\0All Files\0*.*\0";
const char *new_disc_path =
PausedFileOpen(NOC_FILE_DIALOG_OPEN, iso_file_filters,
@ -39,7 +49,12 @@ void ActionLoadDisc(void)
return;
}
xemu_settings_set_string(&g_config.sys.files.dvd_path, new_disc_path);
xemu_load_disc(new_disc_path);
xemu_load_disc(new_disc_path, &err);
if (err) {
xemu_queue_error_message(error_get_pretty(err));
error_free(err);
}
}
void ActionTogglePause(void)
@ -81,7 +96,7 @@ void ActionActivateBoundSnapshot(int slot, bool save)
if (save) {
xemu_snapshots_save(snapshot_name, &err);
} else {
xemu_snapshots_load(snapshot_name, &err);
ActionLoadSnapshotChecked(snapshot_name);
}
if (err) {
@ -89,3 +104,8 @@ void ActionActivateBoundSnapshot(int slot, bool save)
error_free(err);
}
}
void ActionLoadSnapshotChecked(const char *name)
{
g_snapshot_mgr.LoadSnapshotChecked(name);
}

View File

@ -25,3 +25,4 @@ void ActionReset();
void ActionShutdown();
void ActionScreenshot();
void ActionActivateBoundSnapshot(int slot, bool save);
void ActionLoadSnapshotChecked(const char *name);

View File

@ -22,12 +22,14 @@
#include "main-menu.hh"
#include "font-manager.hh"
#include "input-manager.hh"
#include "snapshot-manager.hh"
#include "viewport-manager.hh"
#include "xemu-hud.h"
#include "misc.hh"
#include "gl-helpers.hh"
#include "reporting.hh"
#include "qapi/error.h"
#include "actions.hh"
#include "../xemu-input.h"
#include "../xemu-notifications.h"
@ -35,7 +37,6 @@
#include "../xemu-monitor.h"
#include "../xemu-version.h"
#include "../xemu-net.h"
#include "../xemu-snapshots.h"
#include "../xemu-os-utils.h"
#include "../xemu-xbe.h"
@ -641,63 +642,24 @@ void MainMenuNetworkView::DrawUdpOptions(bool appearing)
ImGui::PopFont();
}
void MainMenuSnapshotsView::Load()
{
Error *err = NULL;
if (!m_load_failed) {
m_snapshots_len = xemu_snapshots_list(&m_snapshots, &m_extra_data, &err);
}
if (err) {
m_load_failed = true;
xemu_queue_error_message(error_get_pretty(err));
error_free(err);
m_snapshots_len = 0;
}
struct xbe *xbe = xemu_get_xbe_info();
if (xbe && xbe->cert->m_titleid != m_current_title_id) {
g_free(m_current_title_name);
m_current_title_name = g_utf16_to_utf8(xbe->cert->m_title_name, 40, NULL, NULL, NULL);
m_current_title_id = xbe->cert->m_titleid;
}
}
MainMenuSnapshotsView::MainMenuSnapshotsView(): MainMenuTabView()
MainMenuSnapshotsView::MainMenuSnapshotsView() : MainMenuTabView()
{
xemu_snapshots_mark_dirty();
m_load_failed = false;
m_search_regex = NULL;
m_current_title_name = NULL;
m_current_title_id = 0;
}
MainMenuSnapshotsView::~MainMenuSnapshotsView()
{
g_free(m_snapshots);
g_free(m_extra_data);
xemu_snapshots_mark_dirty();
g_free(m_current_title_name);
g_free(m_search_regex);
}
void MainMenuSnapshotsView::SnapshotBigButton(QEMUSnapshotInfo *snapshot, const char *title_name, GLuint thumbnail)
bool MainMenuSnapshotsView::BigSnapshotButton(QEMUSnapshotInfo *snapshot, XemuSnapshotData *data, int current_snapshot_binding)
{
Error *err = NULL;
int current_snapshot_binding = -1;
for (int i = 0; i < 4; ++i) {
if (g_strcmp0(*(g_snapshot_shortcut_index_key_map[i]), snapshot->name) == 0) {
assert(current_snapshot_binding == -1);
current_snapshot_binding = i;
}
}
ImGuiStyle &style = ImGui::GetStyle();
ImVec2 pos = ImGui::GetCursorPos();
ImDrawList *draw_list = ImGui::GetWindowDrawList();
ImGui::PushFont(g_font_mgr.m_menu_font_small);
@ -706,6 +668,7 @@ void MainMenuSnapshotsView::SnapshotBigButton(QEMUSnapshotInfo *snapshot, const
ImGui::PushStyleVar(ImGuiStyleVar_ButtonTextAlign, ImVec2(0, 0));
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, g_viewport_mgr.Scale(ImVec2(5, 5)));
ImGui::PushFont(g_font_mgr.m_menu_font_medium);
ImVec2 ts_title = ImGui::CalcTextSize(snapshot->name);
@ -715,54 +678,36 @@ void MainMenuSnapshotsView::SnapshotBigButton(QEMUSnapshotInfo *snapshot, const
ImVec2 title_pos(name_pos.x, name_pos.y + ts_title.y + style.FramePadding.x);
ImVec2 date_pos(name_pos.x, title_pos.y + ts_title.y + style.FramePadding.x);
ImVec2 binding_pos(name_pos.x, date_pos.y + ts_title.y + style.FramePadding.x);
ImVec2 button_size(-FLT_MIN, fmax(thumbnail_size.y + style.FramePadding.y * 2, ts_title.y + ts_sub.y + style.FramePadding.y * 3));
bool load = ImGui::Button("###button", ImVec2(-FLT_MIN, fmax(thumbnail_size.y + style.FramePadding.y * 2, ts_title.y + ts_sub.y + style.FramePadding.y * 3)));
if (load) {
xemu_snapshots_load(snapshot->name, &err);
if (err) {
xemu_queue_error_message(error_get_pretty(err));
error_free(err);
}
}
bool options_key_pressed = ImGui::IsKeyPressed(ImGuiKey_GamepadFaceLeft);
bool show_snapshot_context_menu = ImGui::IsItemHovered() &&
(ImGui::IsMouseReleased(ImGuiMouseButton_Right) ||
options_key_pressed);
bool load = ImGui::Button("###button", button_size);
ImVec2 next_pos = ImGui::GetCursorPos();
const ImVec2 p0 = ImGui::GetItemRectMin();
const ImVec2 p1 = ImGui::GetItemRectMax();
ImGui::PopFont();
const ImVec2 p0 = ImGui::GetItemRectMin();
const ImVec2 p1 = ImGui::GetItemRectMax();
draw_list->PushClipRect(p0, p1, true);
// Snapshot thumbnail
ImGui::SetItemAllowOverlap();
ImGui::SetCursorPos(ImVec2(pos.x + thumbnail_pos.x, pos.y + thumbnail_pos.y));
if (!thumbnail) {
thumbnail = g_icon_tex;
}
GLuint thumbnail = data->gl_thumbnail ? data->gl_thumbnail : g_icon_tex;
int thumbnail_width, thumbnail_height;
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, thumbnail);
int thumbnail_width, thumbnail_height;
glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &thumbnail_width);
glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &thumbnail_height);
// Draw black background behind thumbnail
draw_list->AddRectFilled(ImVec2(p0.x + thumbnail_pos.x, p0.y + thumbnail_pos.y),
ImVec2(p0.x + thumbnail_pos.x + thumbnail_size.x, p0.y + thumbnail_pos.y + thumbnail_size.y),
IM_COL32_BLACK);
ImVec2 thumbnail_min(p0.x + thumbnail_pos.x, p0.y + thumbnail_pos.y);
ImVec2 thumbnail_max(thumbnail_min.x + thumbnail_size.x, thumbnail_min.y + thumbnail_size.y);
draw_list->AddRectFilled(thumbnail_min, thumbnail_max, IM_COL32_BLACK);
// Draw centered thumbnail
// Draw centered thumbnail image
int scaled_width, scaled_height;
ScaleDimensions(thumbnail_width, thumbnail_height, thumbnail_size.x, thumbnail_size.y, &scaled_width, &scaled_height);
ImVec2 img_pos = ImGui::GetCursorPos();
img_pos.x += (thumbnail_size.x - scaled_width) / 2;
img_pos.y += (thumbnail_size.y - scaled_height) / 2;
ImGui::SetCursorPos(img_pos);
ImGui::Image((ImTextureID)(uint64_t)thumbnail, ImVec2(scaled_width, scaled_height));
ImVec2 img_min = ImVec2(thumbnail_min.x + (thumbnail_size.x - scaled_width) / 2,
thumbnail_min.y + (thumbnail_size.y - scaled_height) / 2);
ImVec2 img_max = ImVec2(img_min.x + scaled_width, img_min.y + scaled_height);
draw_list->AddImage((ImTextureID)(uint64_t)thumbnail, img_min, img_max);
// Snapshot title
ImGui::PushFont(g_font_mgr.m_menu_font_medium);
@ -771,6 +716,7 @@ void MainMenuSnapshotsView::SnapshotBigButton(QEMUSnapshotInfo *snapshot, const
// Snapshot XBE title name
ImGui::PushFont(g_font_mgr.m_menu_font_small);
const char *title_name = data->xbe_title_name ? data->xbe_title_name : "(Unknown XBE Title Name)";
draw_list->AddText(ImVec2(p0.x + title_pos.x, p0.y + title_pos.y), IM_COL32(255, 255, 255, 200), title_name);
// Snapshot date
@ -788,71 +734,9 @@ void MainMenuSnapshotsView::SnapshotBigButton(QEMUSnapshotInfo *snapshot, const
ImGui::PopFont();
draw_list->PopClipRect();
if (show_snapshot_context_menu) {
if (options_key_pressed) {
ImGui::SetNextWindowPos(p0);
}
ImGui::OpenPopup("Snapshot Options");
}
if (ImGui::BeginPopupContextItem("Snapshot Options")) {
if (ImGui::MenuItem("Load")) {
xemu_snapshots_load(snapshot->name, &err);
if (err) {
xemu_queue_error_message(error_get_pretty(err));
error_free(err);
}
}
if (ImGui::BeginMenu("Keybinding")) {
for (int i = 0; i < 4; ++i) {
char *item_name = g_strdup_printf("Bind to F%d", i + 5);
if (ImGui::MenuItem(item_name)) {
if (current_snapshot_binding >= 0) {
xemu_settings_set_string(g_snapshot_shortcut_index_key_map[current_snapshot_binding], "");
}
xemu_settings_set_string(g_snapshot_shortcut_index_key_map[i], snapshot->name);
current_snapshot_binding = i;
ImGui::CloseCurrentPopup();
}
g_free(item_name);
}
if (current_snapshot_binding >= 0) {
if (ImGui::MenuItem("Unbind")) {
xemu_settings_set_string(g_snapshot_shortcut_index_key_map[current_snapshot_binding], "");
current_snapshot_binding = -1;
}
}
ImGui::EndMenu();
}
ImGui::Separator();
if (ImGui::MenuItem("Replace")) {
xemu_snapshots_save(snapshot->name, &err);
if (err) {
xemu_queue_error_message(error_get_pretty(err));
error_free(err);
}
}
if (ImGui::MenuItem("Delete")) {
xemu_snapshots_delete(snapshot->name, &err);
if (err) {
xemu_queue_error_message(error_get_pretty(err));
error_free(err);
}
}
ImGui::EndPopup();
}
ImGui::PopStyleVar(2);
ImGui::SetCursorPos(next_pos);
return load;
}
void MainMenuSnapshotsView::ClearSearch()
@ -892,18 +776,26 @@ static int MainMenuSnapshotsViewUpdateSearchBox(ImGuiInputTextCallbackData *data
void MainMenuSnapshotsView::Draw()
{
Load();
SectionTitle("Snapshots");
g_snapshot_mgr.Refresh();
Toggle("Filter by current title", &g_config.general.snapshots.filter_current_game);
struct xbe *xbe = xemu_get_xbe_info();
if (xbe && xbe->cert->m_titleid != m_current_title_id) {
g_free(m_current_title_name);
m_current_title_name = g_utf16_to_utf8(xbe->cert->m_title_name, 40, NULL, NULL, NULL);
m_current_title_id = xbe->cert->m_titleid;
}
SectionTitle("Snapshots");
Toggle("Filter by current title", &g_config.general.snapshots.filter_current_game,
"Only display snapshots created while running the currently running XBE");
ImGui::SetNextItemWidth(ImGui::GetColumnWidth() * 0.8);
ImGui::InputTextWithHint("##search", "Search...", &m_search_buf, ImGuiInputTextFlags_CallbackEdit,
&MainMenuSnapshotsViewUpdateSearchBox, this);
bool snapshot_with_create_name_exists = false;
for (int i = 0; i < m_snapshots_len; ++i) {
if (g_strcmp0(m_search_buf.c_str(), m_snapshots[i].name) == 0) {
for (int i = 0; i < g_snapshot_mgr.m_snapshots_len; ++i) {
if (g_strcmp0(m_search_buf.c_str(), g_snapshot_mgr.m_snapshots[i].name) == 0) {
snapshot_with_create_name_exists = true;
break;
}
@ -919,9 +811,9 @@ void MainMenuSnapshotsView::Draw()
ImGui::SetTooltip("A snapshot with the name \"%s\" already exists. This button will overwrite the existing snapshot.", m_search_buf.c_str());
}
for (int i = m_snapshots_len - 1; i >= 0; i--) {
if (g_config.general.snapshots.filter_current_game && m_extra_data[i].xbe_title_name &&
(strcmp(m_current_title_name, m_extra_data[i].xbe_title_name) != 0)) {
for (int i = g_snapshot_mgr.m_snapshots_len - 1; i >= 0; i--) {
if (g_config.general.snapshots.filter_current_game && g_snapshot_mgr.m_extra_data[i].xbe_title_name &&
(strcmp(m_current_title_name, g_snapshot_mgr.m_extra_data[i].xbe_title_name) != 0)) {
continue;
}
@ -929,12 +821,12 @@ void MainMenuSnapshotsView::Draw()
GMatchInfo *match;
bool keep_entry = false;
g_regex_match(m_search_regex, m_snapshots[i].name, (GRegexMatchFlags)0, &match);
g_regex_match(m_search_regex, g_snapshot_mgr.m_snapshots[i].name, (GRegexMatchFlags)0, &match);
keep_entry |= g_match_info_matches(match);
g_match_info_free(match);
if (m_extra_data[i].xbe_title_name) {
g_regex_match(m_search_regex, m_extra_data[i].xbe_title_name, (GRegexMatchFlags)0, &match);
if (g_snapshot_mgr.m_extra_data[i].xbe_title_name) {
g_regex_match(m_search_regex, g_snapshot_mgr.m_extra_data[i].xbe_title_name, (GRegexMatchFlags)0, &match);
keep_entry |= g_match_info_matches(match);
g_free(match);
}
@ -944,16 +836,94 @@ void MainMenuSnapshotsView::Draw()
}
}
QEMUSnapshotInfo *snapshot = &g_snapshot_mgr.m_snapshots[i];
XemuSnapshotData *data = &g_snapshot_mgr.m_extra_data[i];
int current_snapshot_binding = -1;
for (int i = 0; i < 4; ++i) {
if (g_strcmp0(*(g_snapshot_shortcut_index_key_map[i]), snapshot->name) == 0) {
assert(current_snapshot_binding == -1);
current_snapshot_binding = i;
}
}
ImGui::PushID(i);
SnapshotBigButton(
m_snapshots + i,
m_extra_data[i].xbe_title_name ? m_extra_data[i].xbe_title_name : "Unknown",
m_extra_data[i].gl_thumbnail
);
ImVec2 pos = ImGui::GetCursorScreenPos();
bool load = BigSnapshotButton(snapshot, data, current_snapshot_binding);
// FIXME: Provide context menu control annotation
if (ImGui::IsItemHovered() && ImGui::IsKeyPressed(ImGuiKey_GamepadFaceLeft)) {
ImGui::SetNextWindowPos(pos);
ImGui::OpenPopup("Snapshot Options");
}
DrawSnapshotContextMenu(snapshot, data, current_snapshot_binding);
ImGui::PopID();
if (load) {
ActionLoadSnapshotChecked(snapshot->name);
}
}
}
void MainMenuSnapshotsView::DrawSnapshotContextMenu(QEMUSnapshotInfo *snapshot, XemuSnapshotData *data, int current_snapshot_binding)
{
if (!ImGui::BeginPopupContextItem("Snapshot Options")) {
return;
}
if (ImGui::MenuItem("Load")) {
ActionLoadSnapshotChecked(snapshot->name);
}
if (ImGui::BeginMenu("Keybinding")) {
for (int i = 0; i < 4; ++i) {
char *item_name = g_strdup_printf("Bind to F%d", i + 5);
if (ImGui::MenuItem(item_name)) {
if (current_snapshot_binding >= 0) {
xemu_settings_set_string(g_snapshot_shortcut_index_key_map[current_snapshot_binding], "");
}
xemu_settings_set_string(g_snapshot_shortcut_index_key_map[i], snapshot->name);
current_snapshot_binding = i;
ImGui::CloseCurrentPopup();
}
g_free(item_name);
}
if (current_snapshot_binding >= 0) {
if (ImGui::MenuItem("Unbind")) {
xemu_settings_set_string(g_snapshot_shortcut_index_key_map[current_snapshot_binding], "");
current_snapshot_binding = -1;
}
}
ImGui::EndMenu();
}
ImGui::Separator();
Error *err = NULL;
if (ImGui::MenuItem("Replace")) {
xemu_snapshots_save(snapshot->name, &err);
}
if (ImGui::MenuItem("Delete")) {
xemu_snapshots_delete(snapshot->name, &err);
}
if (err) {
xemu_queue_error_message(error_get_pretty(err));
error_free(err);
}
ImGui::EndPopup();
}
MainMenuSystemView::MainMenuSystemView() : m_dirty(false)
{
}

View File

@ -104,24 +104,19 @@ public:
class MainMenuSnapshotsView : public virtual MainMenuTabView
{
protected:
QEMUSnapshotInfo *m_snapshots;
XemuSnapshotData *m_extra_data;
int m_snapshots_len;
uint32_t m_current_title_id;
char *m_current_title_name;
std::string m_search_buf;
bool m_load_failed;
private:
void Load();
void ClearSearch();
void DrawSnapshotContextMenu(QEMUSnapshotInfo *snapshot, XemuSnapshotData *data, int current_snapshot_binding);
public:
GRegex *m_search_regex;
MainMenuSnapshotsView();
~MainMenuSnapshotsView();
void SnapshotBigButton(QEMUSnapshotInfo *snapshot, const char *title_name,
GLuint screenshot);
bool BigSnapshotButton(QEMUSnapshotInfo *snapshot, XemuSnapshotData *data, int current_snapshot_binding);
void Draw() override;
};

View File

@ -37,6 +37,7 @@
#include "misc.hh"
#include "gl-helpers.hh"
#include "input-manager.hh"
#include "snapshot-manager.hh"
#include "viewport-manager.hh"
#include "font-manager.hh"
#include "scene.hh"
@ -54,6 +55,8 @@
#endif
bool g_screenshot_pending;
const char *g_snapshot_pending_load_name;
float g_main_menu_height;
static ImGuiStyle g_base_style;
@ -298,6 +301,7 @@ void xemu_hud_render(void)
#endif
g_scene_mgr.Draw();
if (!first_boot_window.is_open) notification_manager.Draw();
g_snapshot_mgr.Draw();
// static bool show_demo = true;
// if (show_demo) ImGui::ShowDemoWindow(&show_demo);

View File

@ -16,6 +16,7 @@ xemu_ss.add(files(
'scene-components.cc',
'scene-manager.cc',
'scene.cc',
'snapshot-manager.cc',
'viewport-manager.cc',
'welcome.cc',
'widgets.cc',

163
ui/xui/snapshot-manager.cc Normal file
View File

@ -0,0 +1,163 @@
//
// xemu User Interface
//
// Copyright (C) 2020-2022 Matt Borgerson
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
#include "common.hh"
#include "notifications.hh"
#include "snapshot-manager.hh"
#include "xemu-hud.h"
SnapshotManager g_snapshot_mgr;
SnapshotManager::SnapshotManager()
{
m_snapshots = NULL;
m_extra_data = NULL;
m_load_failed = false;
m_open_pending = false;
m_snapshots_len = 0;
}
SnapshotManager::~SnapshotManager()
{
g_free(m_snapshots);
g_free(m_extra_data);
xemu_snapshots_mark_dirty();
}
void SnapshotManager::Refresh()
{
Error *err = NULL;
if (!m_load_failed) {
m_snapshots_len = xemu_snapshots_list(&m_snapshots, &m_extra_data, &err);
}
if (err) {
m_load_failed = true;
xemu_queue_error_message(error_get_pretty(err));
error_free(err);
m_snapshots_len = 0;
}
}
void SnapshotManager::LoadSnapshotChecked(const char *name)
{
Refresh();
XemuSnapshotData *data = NULL;
for (int i = 0; i < m_snapshots_len; i++) {
if (!strcmp(m_snapshots[i].name, name)) {
data = &m_extra_data[i];
break;
}
}
if (data == NULL) {
return;
}
char *current_disc_path = xemu_get_currently_loaded_disc_path();
if (data->disc_path && (!current_disc_path || strcmp(current_disc_path, data->disc_path))) {
if (current_disc_path) {
m_current_disc_path = current_disc_path;
} else {
m_current_disc_path.clear();
}
m_target_disc_path = data->disc_path;
m_pending_load_name = name;
m_open_pending = true;
} else {
if (!data->disc_path) {
xemu_eject_disc(NULL);
}
LoadSnapshot(name);
}
if (current_disc_path) {
g_free(current_disc_path);
}
}
void SnapshotManager::LoadSnapshot(const char *name)
{
Error *err = NULL;
xemu_snapshots_load(name, &err);
if (err) {
xemu_queue_error_message(error_get_pretty(err));
error_free(err);
}
}
void SnapshotManager::Draw()
{
DrawSnapshotDiscLoadDialog();
}
void SnapshotManager::DrawSnapshotDiscLoadDialog()
{
if (m_open_pending) {
ImGui::OpenPopup("DVD Drive Image");
m_open_pending = false;
}
if (!ImGui::BeginPopupModal("DVD Drive Image", NULL, ImGuiWindowFlags_AlwaysAutoResize)) {
return;
}
ImGui::Text("The DVD drive disc image mounted when the snapshot was created does not appear to be loaded:");
ImGui::Spacing();
ImGui::Indent();
ImGui::Text("Current Image: %s", m_current_disc_path.length() ? m_current_disc_path.c_str() : "(None)");
ImGui::Text("Expected Image: %s", m_target_disc_path.length() ? m_target_disc_path.c_str() : "(None)");
ImGui::Unindent();
ImGui::Spacing();
ImGui::Text("Would you like to load it now?");
ImGui::Dummy(ImVec2(0,16));
if (ImGui::Button("Yes", ImVec2(120, 0))) {
xemu_eject_disc(NULL);
Error *err = NULL;
xemu_load_disc(m_target_disc_path.c_str(), &err);
if (err) {
xemu_queue_error_message(error_get_pretty(err));
error_free(err);
} else {
LoadSnapshot(m_pending_load_name.c_str());
}
ImGui::CloseCurrentPopup();
}
ImGui::SameLine();
if (ImGui::Button("No", ImVec2(120, 0))) {
LoadSnapshot(m_pending_load_name.c_str());
ImGui::CloseCurrentPopup();
}
ImGui::SameLine();
if (ImGui::Button("Cancel", ImVec2(120, 0))) {
ImGui::CloseCurrentPopup();
}
ImGui::EndPopup();
}

View File

@ -0,0 +1,47 @@
//
// xemu User Interface
//
// Copyright (C) 2020-2022 Matt Borgerson
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
#pragma once
#include <string>
#include "../xemu-snapshots.h"
class SnapshotManager
{
public:
QEMUSnapshotInfo *m_snapshots;
XemuSnapshotData *m_extra_data;
bool m_load_failed;
bool m_open_pending;
int m_snapshots_len;
std::string m_pending_load_name;
std::string m_current_disc_path;
std::string m_target_disc_path;
SnapshotManager();
~SnapshotManager();
void Refresh();
void LoadSnapshot(const char *name);
void LoadSnapshotChecked(const char *name);
void Draw();
void DrawSnapshotDiscLoadDialog();
};
extern SnapshotManager g_snapshot_mgr;

View File

@ -33,8 +33,8 @@ extern "C" {
int xemu_is_fullscreen(void);
void xemu_monitor_init(void);
void xemu_toggle_fullscreen(void);
void xemu_eject_disc(void);
void xemu_load_disc(const char *path);
void xemu_eject_disc(Error **errp);
void xemu_load_disc(const char *path, Error **errp);
// Implemented in xemu_hud.cc
void xemu_hud_init(SDL_Window *window, void *sdl_gl_context);