ui: Add snapshot management UI

This commit is contained in:
Antonio Abbatangelo
2023-06-04 20:24:43 -07:00
committed by mborgerson
parent fe5160e859
commit 9c9f1e83eb
22 changed files with 1052 additions and 126 deletions
+6
View File
@@ -2599,6 +2599,12 @@ static void bdrv_default_perms_for_storage(BlockDriverState *bs, BdrvChild *c,
shared |= BLK_PERM_WRITE | BLK_PERM_RESIZE;
}
#ifdef XBOX
if (bs->open_flags & BDRV_O_RO_WRITE_SHARE) {
shared |= BLK_PERM_WRITE | BLK_PERM_RESIZE;
}
#endif
*nperm = perm;
*nshared = shared;
}
+16
View File
@@ -36,6 +36,7 @@
#include "qapi/qmp/qstring.h"
#include <windows.h>
#include <winioctl.h>
#include <assert.h>
#define FTYPE_FILE 0
#define FTYPE_CD 1
@@ -341,6 +342,9 @@ static int raw_open(BlockDriverState *bs, QDict *options, int flags,
bool use_aio;
OnOffAuto locking;
int ret;
#ifdef XBOX
int sharing_flags;
#endif
s->type = FTYPE_FILE;
@@ -396,9 +400,21 @@ static int raw_open(BlockDriverState *bs, QDict *options, int flags,
if (!filename) {
goto fail;
}
#ifdef XBOX
sharing_flags = FILE_SHARE_READ;
if (flags & BDRV_O_RO_WRITE_SHARE) {
assert(access_flags == GENERIC_READ);
sharing_flags = FILE_SHARE_READ | FILE_SHARE_WRITE;
}
s->hfile = CreateFileW(wfilename, access_flags,
sharing_flags, NULL,
OPEN_EXISTING, overlapped, NULL);
#else
s->hfile = CreateFileW(wfilename, access_flags,
FILE_SHARE_READ, NULL,
OPEN_EXISTING, overlapped, NULL);
#endif
g_free(wfilename);
if (s->hfile == INVALID_HANDLE_VALUE) {
int err = GetLastError();
+7
View File
@@ -11,6 +11,13 @@ general:
# throttle_io: bool
last_viewed_menu_index: integer
user_token: string
snapshots:
shortcuts:
f5: string
f6: string
f7: string
f8: string
filter_current_game: bool
input:
bindings:
+4
View File
@@ -123,6 +123,10 @@ typedef struct HDGeometry {
#define BDRV_O_AUTO_RDONLY 0x20000 /* degrade to read-only if opening read-write fails */
#define BDRV_O_IO_URING 0x40000 /* use io_uring instead of the thread pool */
#ifdef XBOX
#define BDRV_O_RO_WRITE_SHARE 0x80000 /* allow the file to open RO alongside an existing RW handle */
#endif
#define BDRV_O_CACHE_MASK (BDRV_O_NOCACHE | BDRV_O_NO_FLUSH)
+15
View File
@@ -67,6 +67,8 @@
#include "qemu/yank.h"
#include "yank_functions.h"
#include "ui/xemu-snapshots.h"
const unsigned int postcopy_ram_discard_version;
/* Subcommands for QEMU_VM_COMMAND */
@@ -1570,6 +1572,9 @@ static int qemu_savevm_state(QEMUFile *f, Error **errp)
ms->to_dst_file = f;
qemu_mutex_unlock_iothread();
#ifdef XBOX
xemu_snapshots_save_extra_data(f);
#endif
qemu_savevm_state_header(f);
qemu_savevm_state_setup(f);
qemu_mutex_lock_iothread();
@@ -2691,6 +2696,12 @@ int qemu_loadvm_state(QEMUFile *f)
return -EINVAL;
}
#ifdef XBOX
if (!xemu_snapshots_offset_extra_data(f)) {
return -EINVAL;
}
#endif
ret = qemu_loadvm_state_header(f);
if (ret) {
return ret;
@@ -3086,6 +3097,10 @@ bool delete_snapshot(const char *name, bool has_devices,
return false;
}
#ifdef XBOX
xemu_snapshots_mark_dirty();
#endif
return true;
}
+2
View File
@@ -26,6 +26,8 @@ xemu_ss.add(files(
'xemu.c',
'xemu-data.c',
'xemu-snapshots.c',
'xemu-thumbnail.cc',
))
subdir('xui')
+1
View File
@@ -5,6 +5,7 @@ imgui_files = files(
'imgui/imgui_widgets.cpp',
'imgui/backends/imgui_impl_sdl.cpp',
'imgui/backends/imgui_impl_opengl3.cpp',
'imgui/misc/cpp/imgui_stdlib.cpp',
#'imgui/imgui_demo.cpp',
)
+4 -2
View File
@@ -26,6 +26,7 @@
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#ifdef __cplusplus
extern "C" {
@@ -58,8 +59,9 @@ void xemu_settings_save(void);
static inline void xemu_settings_set_string(const char **str, const char *new_str)
{
free((char*)*str);
*str = strdup(new_str);
assert(new_str);
free((char*)*str);
*str = strdup(new_str);
}
void add_net_nat_forward_ports(int host, int guest, CONFIG_NET_NAT_FORWARD_PORTS_PROTOCOL protocol);
+319
View File
@@ -0,0 +1,319 @@
/*
* 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 "xemu-snapshots.h"
#include "xemu-settings.h"
#include "xemu-xbe.h"
#include <SDL2/SDL.h>
#include <epoxy/gl.h>
#include "block/aio.h"
#include "block/block_int.h"
#include "block/qapi.h"
#include "block/qdict.h"
#include "migration/qemu-file.h"
#include "migration/snapshot.h"
#include "qapi/error.h"
#include "sysemu/runstate.h"
#include "qemu-common.h"
#include "ui/console.h"
#include "ui/input.h"
static QEMUSnapshotInfo *xemu_snapshots_metadata = NULL;
static XemuSnapshotData *xemu_snapshots_extra_data = NULL;
static int xemu_snapshots_len = 0;
static bool xemu_snapshots_dirty = true;
const char **g_snapshot_shortcut_index_key_map[] = {
&g_config.general.snapshots.shortcuts.f5,
&g_config.general.snapshots.shortcuts.f6,
&g_config.general.snapshots.shortcuts.f7,
&g_config.general.snapshots.shortcuts.f8,
};
static bool xemu_snapshots_load_thumbnail(BlockDriverState *bs_ro,
XemuSnapshotData *data,
int64_t *offset)
{
int res = bdrv_load_vmstate(bs_ro, (uint8_t *)&data->thumbnail, *offset,
sizeof(TextureBuffer) -
sizeof(data->thumbnail.buffer));
if (res != sizeof(TextureBuffer) - sizeof(data->thumbnail.buffer))
return false;
*offset += res;
data->thumbnail.buffer = g_malloc(data->thumbnail.size);
res = bdrv_load_vmstate(bs_ro, (uint8_t *)data->thumbnail.buffer, *offset,
data->thumbnail.size);
if (res != data->thumbnail.size) {
return false;
}
*offset += res;
return true;
}
static void xemu_snapshots_load_data(BlockDriverState *bs_ro,
QEMUSnapshotInfo *info,
XemuSnapshotData *data, Error **err)
{
int res;
XemuSnapshotHeader header;
int64_t offset = 0;
data->xbe_title_present = false;
data->thumbnail_present = false;
res = bdrv_snapshot_load_tmp(bs_ro, info->id_str, info->name, err);
if (res < 0)
return;
res = bdrv_load_vmstate(bs_ro, (uint8_t *)&header, offset, sizeof(header));
if (res != sizeof(header))
goto error;
offset += res;
if (header.magic != XEMU_SNAPSHOT_DATA_MAGIC)
goto error;
res = bdrv_load_vmstate(bs_ro, (uint8_t *)&data->xbe_title_len, offset,
sizeof(data->xbe_title_len));
if (res != sizeof(data->xbe_title_len))
goto error;
offset += res;
data->xbe_title = (char *)g_malloc(data->xbe_title_len);
res = bdrv_load_vmstate(bs_ro, (uint8_t *)data->xbe_title, offset,
data->xbe_title_len);
if (res != data->xbe_title_len)
goto error;
offset += res;
data->xbe_title_present = (offset <= sizeof(header) + header.size);
if (offset == sizeof(header) + header.size)
return;
if (!xemu_snapshots_load_thumbnail(bs_ro, data, &offset)) {
goto error;
}
data->thumbnail_present = (offset <= sizeof(header) + header.size);
if (data->thumbnail_present) {
glGenTextures(1, &data->gl_thumbnail);
xemu_snapshots_render_thumbnail(data->gl_thumbnail, &data->thumbnail);
}
return;
error:
g_free(data->xbe_title);
data->xbe_title_present = false;
g_free(data->thumbnail.buffer);
data->thumbnail_present = false;
}
static void xemu_snapshots_all_load_data(QEMUSnapshotInfo **info,
XemuSnapshotData **data,
int snapshots_len, Error **err)
{
BlockDriverState *bs_ro;
QDict *opts = qdict_new();
assert(info && data);
if (*data) {
for (int i = 0; i < xemu_snapshots_len; ++i) {
if ((*data)[i].xbe_title_present) {
g_free((*data)[i].xbe_title);
}
if ((*data)[i].thumbnail_present) {
g_free((*data)[i].thumbnail.buffer);
glDeleteTextures(1, &((*data)[i].gl_thumbnail));
}
}
g_free(*data);
}
*data =
(XemuSnapshotData *)g_malloc(sizeof(XemuSnapshotData) * snapshots_len);
memset(*data, 0, sizeof(XemuSnapshotData) * snapshots_len);
qdict_put_bool(opts, BDRV_OPT_READ_ONLY, true);
bs_ro = bdrv_open(g_config.sys.files.hdd_path, NULL, opts,
BDRV_O_RO_WRITE_SHARE | BDRV_O_AUTO_RDONLY, err);
if (!bs_ro) {
return;
}
for (int i = 0; i < snapshots_len; ++i) {
xemu_snapshots_load_data(bs_ro, (*info) + i, (*data) + i, err);
if (*err) {
break;
}
}
bdrv_flush(bs_ro);
bdrv_drain(bs_ro);
bdrv_unref(bs_ro);
assert(bs_ro->refcnt == 0);
if (!(*err))
xemu_snapshots_dirty = false;
}
int xemu_snapshots_list(QEMUSnapshotInfo **info, XemuSnapshotData **extra_data,
Error **err)
{
BlockDriverState *bs;
AioContext *aio_context;
int snapshots_len;
assert(err);
if (!xemu_snapshots_dirty && xemu_snapshots_extra_data &&
xemu_snapshots_metadata) {
goto done;
}
if (xemu_snapshots_metadata)
g_free(xemu_snapshots_metadata);
bs = bdrv_all_find_vmstate_bs(NULL, false, NULL, err);
if (!bs) {
return -1;
}
aio_context = bdrv_get_aio_context(bs);
aio_context_acquire(aio_context);
snapshots_len = bdrv_snapshot_list(bs, &xemu_snapshots_metadata);
aio_context_release(aio_context);
xemu_snapshots_all_load_data(&xemu_snapshots_metadata,
&xemu_snapshots_extra_data, snapshots_len,
err);
if (*err) {
return -1;
}
xemu_snapshots_len = snapshots_len;
done:
if (info) {
*info = xemu_snapshots_metadata;
}
if (extra_data) {
*extra_data = xemu_snapshots_extra_data;
}
return xemu_snapshots_len;
}
void xemu_snapshots_load(const char *vm_name, Error **err)
{
bool vm_running = runstate_is_running();
vm_stop(RUN_STATE_RESTORE_VM);
if (load_snapshot(vm_name, NULL, false, NULL, err) && vm_running) {
vm_start();
}
}
void xemu_snapshots_save(const char *vm_name, Error **err)
{
save_snapshot(vm_name, true, NULL, false, NULL, err);
}
void xemu_snapshots_delete(const char *vm_name, Error **err)
{
delete_snapshot(vm_name, false, NULL, err);
}
void xemu_snapshots_save_extra_data(QEMUFile *f)
{
struct xbe *xbe_data = xemu_get_xbe_info();
int64_t xbe_title_len = 0;
char *xbe_title = g_utf16_to_utf8(xbe_data->cert->m_title_name, 40, NULL,
&xbe_title_len, NULL);
xbe_title_len++;
XemuSnapshotHeader header = { XEMU_SNAPSHOT_DATA_MAGIC, 0 };
header.size += sizeof(xbe_title_len);
header.size += xbe_title_len;
TextureBuffer *thumbnail = xemu_snapshots_extract_thumbnail();
if (thumbnail && thumbnail->buffer) {
header.size += sizeof(TextureBuffer) - sizeof(thumbnail->buffer);
header.size += thumbnail->size;
}
qemu_put_buffer(f, (const uint8_t *)&header, sizeof(header));
qemu_put_buffer(f, (const uint8_t *)&xbe_title_len, sizeof(xbe_title_len));
qemu_put_buffer(f, (const uint8_t *)xbe_title, xbe_title_len);
if (thumbnail && thumbnail->buffer) {
qemu_put_buffer(f, (const uint8_t *)thumbnail,
sizeof(TextureBuffer) - sizeof(thumbnail->buffer));
qemu_put_buffer(f, (const uint8_t *)thumbnail->buffer, thumbnail->size);
}
g_free(xbe_title);
if (thumbnail && thumbnail->buffer) {
g_free(thumbnail->buffer);
}
g_free(thumbnail);
xemu_snapshots_dirty = true;
}
bool xemu_snapshots_offset_extra_data(QEMUFile *f)
{
size_t ret;
XemuSnapshotHeader header;
ret = qemu_get_buffer(f, (uint8_t *)&header, sizeof(header));
if (ret != sizeof(header)) {
return false;
}
if (header.magic == XEMU_SNAPSHOT_DATA_MAGIC) {
/*
* qemu_file_skip only works if you aren't skipping past its buffer.
* Unfortunately, it's not usable here.
*/
void *buf = g_malloc(header.size);
qemu_get_buffer(f, buf, header.size);
g_free(buf);
} else {
qemu_file_skip(f, -((int)sizeof(header)));
}
return true;
}
void xemu_snapshots_mark_dirty(void)
{
xemu_snapshots_dirty = true;
}
+79
View File
@@ -0,0 +1,79 @@
/*
* 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/>.
*/
#ifndef XEMU_SNAPSHOTS_H
#define XEMU_SNAPSHOTS_H
#ifdef __cplusplus
extern "C" {
#endif
#include "qemu/osdep.h"
#include "block/snapshot.h"
#define XEMU_SNAPSHOT_DATA_MAGIC 0x78656d75
#define XEMU_SNAPSHOT_HEIGHT 120
#define XEMU_SNAPSHOT_WIDTH 160
extern const char **g_snapshot_shortcut_index_key_map[];
#pragma pack(1)
typedef struct TextureBuffer {
int channels;
unsigned long size;
void *buffer;
} TextureBuffer;
#pragma pack()
typedef struct XemuSnapshotHeader {
uint32_t magic;
uint32_t size;
} XemuSnapshotHeader;
typedef struct XemuSnapshotData {
int64_t xbe_title_len;
char *xbe_title;
bool xbe_title_present;
TextureBuffer thumbnail;
bool thumbnail_present;
unsigned int gl_thumbnail;
} XemuSnapshotData;
// Implemented in xemu-snapshots.c
int xemu_snapshots_list(QEMUSnapshotInfo **info, XemuSnapshotData **extra_data,
Error **err);
void xemu_snapshots_load(const char *vm_name, Error **err);
void xemu_snapshots_save(const char *vm_name, Error **err);
void xemu_snapshots_delete(const char *vm_name, Error **err);
void xemu_snapshots_save_extra_data(QEMUFile *f);
bool xemu_snapshots_offset_extra_data(QEMUFile *f);
void xemu_snapshots_mark_dirty(void);
// Implemented in xemu-thumbnail.cc
void xemu_snapshots_set_framebuffer_texture(unsigned int tex, bool flip);
void xemu_snapshots_render_thumbnail(unsigned int tex,
TextureBuffer *thumbnail);
TextureBuffer *xemu_snapshots_extract_thumbnail(void);
#ifdef __cplusplus
}
#endif
#endif
+92
View File
@@ -0,0 +1,92 @@
/*
* 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 <cstdint>
#include <fpng.h>
#include <vector>
#include "xemu-snapshots.h"
#include "xui/gl-helpers.hh"
static GLuint display_tex = 0;
static bool display_flip = false;
void xemu_snapshots_set_framebuffer_texture(GLuint tex, bool flip)
{
display_tex = tex;
display_flip = flip;
}
void xemu_snapshots_render_thumbnail(GLuint tex, TextureBuffer *thumbnail)
{
std::vector<uint8_t> pixels;
unsigned int width, height, channels;
if (fpng::fpng_decode_memory(
thumbnail->buffer, thumbnail->size, pixels, width, height, channels,
thumbnail->channels) != fpng::FPNG_DECODE_SUCCESS) {
return;
}
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, tex);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB,
GL_UNSIGNED_BYTE, pixels.data());
}
TextureBuffer *xemu_snapshots_extract_thumbnail()
{
/*
* Avoids crashing if a snapshot is made on a thread with no GL context
* Normally, this is not an issue, but it is better to fail safe than assert
* here.
* FIXME: Allow for dispatching a thumbnail request to the UI thread to
* remove this altogether.
*/
if (!SDL_GL_GetCurrentContext() || display_tex == 0) {
return NULL;
}
// Render at 2x the base size to account for potential UI scaling
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, display_tex);
int thumbnail_width = XEMU_SNAPSHOT_WIDTH * 2;
int tex_width;
int tex_height;
glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &tex_width);
glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &tex_height);
int thumbnail_height = (int)(((float)tex_height / (float)tex_width) * (float)thumbnail_width);
std::vector<uint8_t> png;
if (!ExtractFramebufferPixels(display_tex, display_flip, png, thumbnail_width, thumbnail_height)) {
return NULL;
}
TextureBuffer *thumbnail = (TextureBuffer *)g_malloc(sizeof(TextureBuffer));
thumbnail->buffer = g_malloc(png.size() * sizeof(uint8_t));
thumbnail->channels = 3;
thumbnail->size = png.size() * sizeof(uint8_t);
memcpy(thumbnail->buffer, png.data(), thumbnail->size);
return thumbnail;
}
+3
View File
@@ -47,6 +47,7 @@
#include "xemu-input.h"
#include "xemu-settings.h"
// #include "xemu-shaders.h"
#include "xemu-snapshots.h"
#include "xemu-version.h"
#include "xemu-os-utils.h"
@@ -1199,6 +1200,7 @@ void sdl2_gl_refresh(DisplayChangeListener *dcl)
glClearColor(0, 0, 0, 0);
glClear(GL_COLOR_BUFFER_BIT);
xemu_snapshots_set_framebuffer_texture(tex, flip_required);
xemu_hud_set_framebuffer_texture(tex, flip_required);
xemu_hud_render();
@@ -1548,6 +1550,7 @@ int main(int argc, char **argv)
while (1) {
sdl2_gl_refresh(&sdl2_console[0].dcl);
assert(glGetError() == GL_NO_ERROR);
}
// rcu_unregister_thread();
+27 -1
View File
@@ -19,6 +19,8 @@
#include "common.hh"
#include "misc.hh"
#include "xemu-hud.h"
#include "../xemu-snapshots.h"
#include "../xemu-notifications.h"
void ActionEjectDisc(void)
{
@@ -62,4 +64,28 @@ void ActionShutdown(void)
void ActionScreenshot(void)
{
g_screenshot_pending = true;
}
}
void ActionActivateBoundSnapshot(int slot, bool save)
{
assert(slot < 4 && slot >= 0);
const char *snapshot_name = *(g_snapshot_shortcut_index_key_map[slot]);
if (!snapshot_name || !(snapshot_name[0])) {
char *msg = g_strdup_printf("F%d is not bound to a snapshot", slot + 5);
xemu_queue_notification(msg);
g_free(msg);
return;
}
Error *err = NULL;
if (save) {
xemu_snapshots_save(snapshot_name, &err);
} else {
xemu_snapshots_load(snapshot_name, &err);
}
if (err) {
xemu_queue_error_message(error_get_pretty(err));
error_free(err);
}
}
+1
View File
@@ -24,3 +24,4 @@ void ActionTogglePause();
void ActionReset();
void ActionShutdown();
void ActionScreenshot();
void ActionActivateBoundSnapshot(int slot, bool save);
+1
View File
@@ -28,6 +28,7 @@
#include <imgui_impl_sdl.h>
#include <imgui_impl_opengl3.h>
#include <implot.h>
#include <misc/cpp/imgui_stdlib.h>
#include <stb_image.h>
extern "C" {
+37 -17
View File
@@ -26,12 +26,14 @@
#include "data/controller_mask.png.h"
#include "data/logo_sdf.png.h"
#include "ui/shader/xemu-logo-frag.h"
#include "data/xemu_64x64.png.h"
#include "notifications.hh"
Fbo *controller_fbo,
*logo_fbo;
GLuint g_controller_tex,
g_logo_tex;
g_logo_tex,
g_icon_tex;
enum class ShaderType {
Blit,
@@ -155,10 +157,10 @@ static GLuint InitTexture(unsigned char *data, int width, int height,
return tex;
}
static GLuint LoadTextureFromMemory(const unsigned char *buf, unsigned int size)
static GLuint LoadTextureFromMemory(const unsigned char *buf, unsigned int size, bool flip=true)
{
// Flip vertically so textures are loaded according to GL convention.
stbi_set_flip_vertically_on_load(1);
stbi_set_flip_vertically_on_load(flip);
int width, height, channels = 0;
unsigned char *data = stbi_load_from_memory(buf, size, &width, &height, &channels, 4);
@@ -442,6 +444,8 @@ void InitCustomRendering(void)
g_logo_shader = NewDecalShader(ShaderType::Logo);
logo_fbo = new Fbo(512, 512);
g_icon_tex = LoadTextureFromMemory(xemu_64x64_data, xemu_64x64_size, false);
g_framebuffer_shader = NewDecalShader(ShaderType::BlitGamma);
}
@@ -657,7 +661,7 @@ void RenderLogo(uint32_t time)
glUseProgram(0);
}
void RenderFramebuffer(GLint tex, int width, int height, bool flip)
void RenderFramebuffer(GLint tex, int width, int height, bool flip, bool apply_scaling_factor)
{
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, tex);
@@ -668,7 +672,10 @@ void RenderFramebuffer(GLint tex, int width, int height, bool flip)
// Calculate scaling factors
float scale[2];
if (g_config.display.ui.fit == CONFIG_DISPLAY_UI_FIT_STRETCH) {
if (!apply_scaling_factor) {
scale[0] = 1.0;
scale[1] = 1.0;
} else if (g_config.display.ui.fit == CONFIG_DISPLAY_UI_FIT_STRETCH) {
// Stretch to fit
scale[0] = 1.0;
scale[1] = 1.0;
@@ -720,19 +727,27 @@ void RenderFramebuffer(GLint tex, int width, int height, bool flip)
glDrawElements(GL_TRIANGLE_FAN, 4, GL_UNSIGNED_INT, NULL);
}
void SaveScreenshot(GLuint tex, bool flip)
bool ExtractFramebufferPixels(GLuint tex, bool flip, std::vector<uint8_t> &png, int width, int height)
{
int width, height;
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, tex);
glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &width);
glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &height);
glBindTexture(GL_TEXTURE_2D, 0);
assert((width == 0 && height == 0) || (width > 0 && height > 0));
if (g_config.display.ui.fit == CONFIG_DISPLAY_UI_FIT_SCALE_16_9) {
width = height * (16.0f / 9.0f);
} else if (g_config.display.ui.fit == CONFIG_DISPLAY_UI_FIT_SCALE_4_3) {
width = height * (4.0f / 3.0f);
bool params_from_tex = false;
if (width <= 0 && height <= 0) {
glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &width);
glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &height);
params_from_tex = true;
}
glBindTexture(GL_TEXTURE_2D, 0);
assert(width > 0 && height > 0);
if (params_from_tex) {
if (g_config.display.ui.fit == CONFIG_DISPLAY_UI_FIT_SCALE_16_9) {
width = height * (16.0f / 9.0f);
} else if (g_config.display.ui.fit == CONFIG_DISPLAY_UI_FIT_SCALE_4_3) {
width = height * (4.0f / 3.0f);
}
}
std::vector<uint8_t> pixels;
@@ -742,7 +757,7 @@ void SaveScreenshot(GLuint tex, bool flip)
fbo.Target();
bool blend = glIsEnabled(GL_BLEND);
if (blend) glDisable(GL_BLEND);
RenderFramebuffer(tex, width, height, !flip);
RenderFramebuffer(tex, width, height, !flip, params_from_tex);
if (blend) glEnable(GL_BLEND);
glPixelStorei(GL_PACK_ROW_LENGTH, width);
glPixelStorei(GL_PACK_IMAGE_HEIGHT, height);
@@ -750,10 +765,15 @@ void SaveScreenshot(GLuint tex, bool flip)
glReadPixels(0, 0, width, height, GL_RGB, GL_UNSIGNED_BYTE, pixels.data());
fbo.Restore();
char fname[128];
return fpng::fpng_encode_image_to_memory(pixels.data(), width, height, 3, png);
}
void SaveScreenshot(GLuint tex, bool flip)
{
Error *err = NULL;
char fname[128];
std::vector<uint8_t> png;
if (fpng::fpng_encode_image_to_memory(pixels.data(), width, height, 3, png)) {
if (ExtractFramebufferPixels(tex, flip, png)) {
time_t t = time(NULL);
struct tm *tmp = localtime(&t);
if (tmp) {
+4 -1
View File
@@ -17,6 +17,7 @@
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
#pragma once
#include <vector>
#include "common.hh"
#include "../xemu-input.h"
@@ -38,6 +39,7 @@ public:
};
extern Fbo *controller_fbo, *logo_fbo;
extern GLuint g_icon_tex;
void InitCustomRendering(void);
void RenderLogo(uint32_t time);
@@ -45,5 +47,6 @@ void RenderController(float frame_x, float frame_y, uint32_t primary_color,
uint32_t secondary_color, ControllerState *state);
void RenderControllerPort(float frame_x, float frame_y, int i,
uint32_t port_color);
void RenderFramebuffer(GLint tex, int width, int height, bool flip);
void RenderFramebuffer(GLint tex, int width, int height, bool flip, bool apply_scaling_factor = true);
bool ExtractFramebufferPixels(GLuint tex, bool flip, std::vector<uint8_t> &png, int width = 0, int height = 0);
void SaveScreenshot(GLuint tex, bool flip);
+346 -100
View File
File diff suppressed because it is too large Load Diff
+21 -4
View File
@@ -24,6 +24,7 @@
#include "widgets.hh"
#include "scene.hh"
#include "scene-components.hh"
#include "../xemu-snapshots.h"
extern "C" {
#include "net/pcap.h"
@@ -102,8 +103,24 @@ 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;
std::string m_create_buf;
bool m_load_failed;
private:
void Load();
public:
void SnapshotBigButton(const char *name, const char *title_name,
GRegex *m_search_regex;
MainMenuSnapshotsView();
~MainMenuSnapshotsView();
void SnapshotBigButton(QEMUSnapshotInfo *snapshot, const char *title_name,
GLuint screenshot);
void Draw() override;
};
@@ -153,7 +170,7 @@ protected:
m_display_button,
m_audio_button,
m_network_button,
// m_snapshots_button,
m_snapshots_button,
m_system_button,
m_about_button;
std::vector<MainMenuTabView*> m_views;
@@ -162,7 +179,7 @@ protected:
MainMenuDisplayView m_display_view;
MainMenuAudioView m_audio_view;
MainMenuNetworkView m_network_view;
// MainMenuSnapshotsView m_snapshots_view;
MainMenuSnapshotsView m_snapshots_view;
MainMenuSystemView m_system_view;
MainMenuAboutView m_about_view;
@@ -174,7 +191,7 @@ public:
void ShowDisplay();
void ShowAudio();
void ShowNetwork();
// void ShowSnapshots();
void ShowSnapshots();
void ShowSystem();
void ShowAbout();
void SetNextViewIndexWithFocus(int i);
+9
View File
@@ -31,6 +31,7 @@
#include <string>
#include <memory>
#include "actions.hh"
#include "common.hh"
#include "xemu-hud.h"
#include "misc.hh"
@@ -277,6 +278,14 @@ void xemu_hud_render(void)
!ImGui::IsAnyItemFocused() && !ImGui::IsAnyItemHovered())) {
g_scene_mgr.PushScene(g_popup_menu);
}
bool mod_key_down = ImGui::IsKeyDown(ImGuiKey_ModShift);
for (int f_key = 0; f_key < 4; ++f_key) {
if (ImGui::IsKeyPressed(f_key + ImGuiKey_F5)) {
ActionActivateBoundSnapshot(f_key, mod_key_down);
break;
}
}
}
first_boot_window.Draw();

Some files were not shown because too many files have changed in this diff Show More