ui: Support controller peripherals and XMU devices (#1315)

* Added XMU Settings to the Input Screen

* Added Peripherals to config

* Prevent overwriting existing XMUs

* Added blockdev.h to try to fix the MacOS build

* Fixed some issues that antangelo pointed out

* Moved the peripheralType and param vars into the loop

* Moved fatx.h and fatx.c to ui\thirdparty\fatx

* Added Validation for Peripheral Settings

* Fixed some nits that were pointed out

* don't pass NULL into xemu_settings_set_string

* Changes following Matt's recommendations

* Changes to XMU FilePicker

* XMU image auto-bind logic refactor

* renamed peripheralType to peripheral_type

* removed unnecessary calls to g_strdup_printf and g_free

* Cleaned up some comments, removed an unnecessary variable

* handle overwrite prompt in Windows

* Fixed some code format and style inconsistencies

* More formatting fixes

* Fixed a few memory leaks

* qemu_access: check for Read and Write access

* Run clang-format

* Remove unused xemu_new_xmu declaration

* Fix use after free in rebind code
This commit is contained in:
Fred Hallock
2023-12-18 01:04:14 -05:00
committed by GitHub
parent 800eb468a4
commit 03f40b1d8e
13 changed files with 769 additions and 142 deletions
+21
View File
@@ -25,6 +25,27 @@ input:
port2: string
port3: string
port4: string
peripherals:
port1:
peripheral_type_0: integer
peripheral_param_0: string
peripheral_type_1: integer
peripheral_param_1: string
port2:
peripheral_type_0: integer
peripheral_param_0: string
peripheral_type_1: integer
peripheral_param_1: string
port3:
peripheral_type_0: integer
peripheral_param_0: string
peripheral_type_1: integer
peripheral_param_1: string
port4:
peripheral_type_0: integer
peripheral_param_0: string
peripheral_type_1: integer
peripheral_param_1: string
gamecontrollerdb_path: string
auto_bind:
type: bool
+1
View File
@@ -1,5 +1,6 @@
pfiles = [
'controller_mask.png',
'xmu_mask.png',
'logo_sdf.png',
'xemu_64x64.png',
'abxy.ttf',
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 9.7 KiB

+1 -1
View File
@@ -47,7 +47,7 @@ endif
xemu_ss.add(when: 'CONFIG_LINUX', if_true: [gtk, files('xemu-os-utils-linux.c')])
xemu_ss.add(when: 'CONFIG_WIN32', if_true: files('xemu-os-utils-windows.c'))
xemu_ss.add(when: 'CONFIG_DARWIN', if_true: files('xemu-os-utils-macos.m'))
xemu_ss.add(imgui, implot, stb_image, noc, sdl, opengl, openssl, fa, fpng, json, httplib)
xemu_ss.add(imgui, implot, stb_image, noc, sdl, opengl, openssl, fa, fpng, json, httplib, fatx)
softmmu_ss.add_all(xemu_ss)
+51
View File
@@ -0,0 +1,51 @@
#include "fatx.h"
#include "qemu/bswap.h"
#define FATX_SIGNATURE 0x58544146
// This is from libfatx
#pragma pack(1)
struct fatx_superblock {
uint32_t signature;
uint32_t volume_id;
uint32_t sectors_per_cluster;
uint32_t root_cluster;
uint16_t unknown1;
uint8_t padding[4078];
};
#pragma pack()
bool create_fatx_image(const char *filename, unsigned int size)
{
unsigned int empty_fat = cpu_to_le32(0xfffffff8);
unsigned char zero = 0;
FILE *fp = qemu_fopen(filename, "wb");
if (fp != NULL) {
struct fatx_superblock superblock;
memset(&superblock, 0xff, sizeof(struct fatx_superblock));
superblock.signature = cpu_to_le32(FATX_SIGNATURE);
superblock.sectors_per_cluster = cpu_to_le32(4);
superblock.volume_id = (uint32_t)rand();
superblock.root_cluster = cpu_to_le32(1);
superblock.unknown1 = 0;
// Write the fatx superblock.
fwrite(&superblock, sizeof(superblock), 1, fp);
// Write the FAT
fwrite(&empty_fat, sizeof(empty_fat), 1, fp);
fseek(fp, size - sizeof(unsigned char), SEEK_SET);
fwrite(&zero, sizeof(unsigned char), 1, fp);
fflush(fp);
fclose(fp);
return true;
}
return false;
}
+16
View File
@@ -0,0 +1,16 @@
#ifndef FATX_H
#define FATX_H
#include "qemu/osdep.h"
#ifdef __cplusplus
extern "C" {
#endif
bool create_fatx_image(const char *filename, unsigned int size);
#ifdef __cplusplus
}
#endif
#endif
+3
View File
@@ -62,3 +62,6 @@ fpng = declare_dependency(include_directories: 'fpng', link_with: libfpng)
json = declare_dependency(include_directories: 'json')
httplib = declare_dependency(include_directories: 'httplib')
libfatx = static_library('fatx', sources: 'fatx/fatx.c')
fatx = declare_dependency(include_directories: 'fatx', link_with: libfatx)
+3
View File
@@ -285,6 +285,9 @@ const char *noc_file_dialog_open(int flags,
ofn.lpstrInitialDir = initialDir;
ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST | OFN_NOCHANGEDIR;
if (flags & NOC_FILE_DIALOG_OVERWRITE_CONFIRMATION)
ofn.Flags |= OFN_OVERWRITEPROMPT;
if (flags & NOC_FILE_DIALOG_OPEN) {
ret = GetOpenFileNameW(&ofn);
} else {
+251 -7
View File
@@ -32,6 +32,8 @@
#include "xemu-notifications.h"
#include "xemu-settings.h"
#include "sysemu/blockdev.h"
// #define DEBUG_INPUT
#ifdef DEBUG_INPUT
@@ -93,8 +95,32 @@ static const char **port_index_to_settings_key_map[] = {
&g_config.input.bindings.port4,
};
static int *peripheral_types_settings_map[4][2] = {
{ &g_config.input.peripherals.port1.peripheral_type_0,
&g_config.input.peripherals.port1.peripheral_type_1 },
{ &g_config.input.peripherals.port2.peripheral_type_0,
&g_config.input.peripherals.port2.peripheral_type_1 },
{ &g_config.input.peripherals.port3.peripheral_type_0,
&g_config.input.peripherals.port3.peripheral_type_1 },
{ &g_config.input.peripherals.port4.peripheral_type_0,
&g_config.input.peripherals.port4.peripheral_type_1 }
};
static const char **peripheral_params_settings_map[4][2] = {
{ &g_config.input.peripherals.port1.peripheral_param_0,
&g_config.input.peripherals.port1.peripheral_param_1 },
{ &g_config.input.peripherals.port2.peripheral_param_0,
&g_config.input.peripherals.port2.peripheral_param_1 },
{ &g_config.input.peripherals.port3.peripheral_param_0,
&g_config.input.peripherals.port3.peripheral_param_1 },
{ &g_config.input.peripherals.port4.peripheral_param_0,
&g_config.input.peripherals.port4.peripheral_param_1 }
};
static int sdl_kbd_scancode_map[25];
static const int port_map[4] = { 3, 4, 1, 2 };
void xemu_input_init(void)
{
if (g_config.input.background_input_capture) {
@@ -112,6 +138,10 @@ void xemu_input_init(void)
new_con->type = INPUT_DEVICE_SDL_KEYBOARD;
new_con->name = "Keyboard";
new_con->bound = -1;
new_con->peripheral_types[0] = PERIPHERAL_NONE;
new_con->peripheral_types[1] = PERIPHERAL_NONE;
new_con->peripherals[0] = NULL;
new_con->peripherals[1] = NULL;
sdl_kbd_scancode_map[0] = g_config.input.keyboard_controller_scancode_map.a;
sdl_kbd_scancode_map[1] = g_config.input.keyboard_controller_scancode_map.b;
@@ -154,6 +184,7 @@ void xemu_input_init(void)
char buf[128];
snprintf(buf, sizeof(buf), "Connected '%s' to port %d", new_con->name, port+1);
xemu_queue_notification(buf);
xemu_input_rebind_xmu(port);
}
QTAILQ_INSERT_TAIL(&available_controllers, new_con, entry);
@@ -177,6 +208,24 @@ int xemu_input_get_controller_default_bind_port(ControllerState *state, int star
return -1;
}
void xemu_save_peripheral_settings(int player_index, int peripheral_index,
int peripheral_type,
const char *peripheral_parameter)
{
int *peripheral_type_ptr =
peripheral_types_settings_map[player_index][peripheral_index];
const char **peripheral_param_ptr =
peripheral_params_settings_map[player_index][peripheral_index];
assert(peripheral_type_ptr);
assert(peripheral_param_ptr);
*peripheral_type_ptr = peripheral_type;
xemu_settings_set_string(
peripheral_param_ptr,
peripheral_parameter == NULL ? "" : peripheral_parameter);
}
void xemu_input_process_sdl_events(const SDL_Event *event)
{
if (event->type == SDL_CONTROLLERDEVICEADDED) {
@@ -201,6 +250,10 @@ void xemu_input_process_sdl_events(const SDL_Event *event)
new_con->sdl_joystick_id = SDL_JoystickInstanceID(new_con->sdl_joystick);
new_con->sdl_joystick_guid = SDL_JoystickGetGUID(new_con->sdl_joystick);
new_con->bound = -1;
new_con->peripheral_types[0] = PERIPHERAL_NONE;
new_con->peripheral_types[1] = PERIPHERAL_NONE;
new_con->peripherals[0] = NULL;
new_con->peripherals[1] = NULL;
char guid_buf[35] = { 0 };
SDL_JoystickGetGUIDString(new_con->sdl_joystick_guid, guid_buf, sizeof(guid_buf));
@@ -254,6 +307,7 @@ void xemu_input_process_sdl_events(const SDL_Event *event)
char buf[128];
snprintf(buf, sizeof(buf), "Connected '%s' to port %d", new_con->name, port+1);
xemu_queue_notification(buf);
xemu_input_rebind_xmu(port);
}
} else if (event->type == SDL_CONTROLLERDEVICEREMOVED) {
DPRINTF("Controller Removed: %d\n", event->cdevice.which);
@@ -286,6 +340,11 @@ void xemu_input_process_sdl_events(const SDL_Event *event)
if (iter->sdl_gamecontroller) {
SDL_GameControllerClose(iter->sdl_gamecontroller);
}
for (int i = 0; i < 2; i++) {
if (iter->peripherals[i])
g_free(iter->peripherals[i]);
}
free(iter);
handled = 1;
@@ -380,12 +439,9 @@ void xemu_input_update_sdl_controller_state(ControllerState *state)
}
const SDL_GameControllerAxis sdl_axis_map[6] = {
SDL_CONTROLLER_AXIS_TRIGGERLEFT,
SDL_CONTROLLER_AXIS_TRIGGERRIGHT,
SDL_CONTROLLER_AXIS_LEFTX,
SDL_CONTROLLER_AXIS_LEFTY,
SDL_CONTROLLER_AXIS_RIGHTX,
SDL_CONTROLLER_AXIS_RIGHTY,
SDL_CONTROLLER_AXIS_TRIGGERLEFT, SDL_CONTROLLER_AXIS_TRIGGERRIGHT,
SDL_CONTROLLER_AXIS_LEFTX, SDL_CONTROLLER_AXIS_LEFTY,
SDL_CONTROLLER_AXIS_RIGHTX, SDL_CONTROLLER_AXIS_RIGHTY,
};
for (int i = 0; i < 6; i++) {
@@ -429,6 +485,22 @@ void xemu_input_bind(int index, ControllerState *state, int save)
if (bound_controllers[index]) {
assert(bound_controllers[index]->device != NULL);
Error *err = NULL;
// Unbind any XMUs
for (int i = 0; i < 2; i++) {
if (bound_controllers[index]->peripherals[i]) {
// If this was an XMU, unbind the XMU
if (bound_controllers[index]->peripheral_types[i] ==
PERIPHERAL_XMU)
xemu_input_unbind_xmu(index, i);
// Free up the XmuState and set the peripheral type to none
g_free(bound_controllers[index]->peripherals[i]);
bound_controllers[index]->peripherals[i] = NULL;
bound_controllers[index]->peripheral_types[i] = PERIPHERAL_NONE;
}
}
qdev_unplug((DeviceState *)bound_controllers[index]->device, &err);
assert(err == NULL);
@@ -460,7 +532,6 @@ void xemu_input_bind(int index, ControllerState *state, int save)
bound_controllers[index] = state;
bound_controllers[index]->bound = index;
const int port_map[4] = {3, 4, 1, 2};
char *tmp;
// Create controller's internal USB hub.
@@ -506,6 +577,179 @@ void xemu_input_bind(int index, ControllerState *state, int save)
}
}
bool xemu_input_bind_xmu(int player_index, int expansion_slot_index,
const char *filename, bool is_rebind)
{
assert(player_index >= 0 && player_index < 4);
assert(expansion_slot_index >= 0 && expansion_slot_index < 2);
ControllerState *player = bound_controllers[player_index];
enum peripheral_type peripheral_type =
player->peripheral_types[expansion_slot_index];
if (peripheral_type != PERIPHERAL_XMU)
return false;
XmuState *xmu = (XmuState *)player->peripherals[expansion_slot_index];
// Unbind existing XMU
if (xmu->dev != NULL) {
xemu_input_unbind_xmu(player_index, expansion_slot_index);
}
if (filename == NULL)
return false;
// Look for any other XMUs that are using this file, and unbind them
for (int player_i = 0; player_i < 4; player_i++) {
ControllerState *state = bound_controllers[player_i];
if (state != NULL) {
for (int peripheral_i = 0; peripheral_i < 2; peripheral_i++) {
if (state->peripheral_types[peripheral_i] == PERIPHERAL_XMU) {
XmuState *xmu_i =
(XmuState *)state->peripherals[peripheral_i];
assert(xmu_i);
if (xmu_i->filename != NULL &&
strcmp(xmu_i->filename, filename) == 0) {
char *buf =
g_strdup_printf("This XMU is already mounted on "
"player %d slot %c\r\n",
player_i + 1, 'A' + peripheral_i);
xemu_queue_notification(buf);
g_free(buf);
return false;
}
}
}
}
}
xmu->filename = g_strdup(filename);
const int xmu_map[2] = { 2, 3 };
char *tmp;
static int id_counter = 0;
tmp = g_strdup_printf("xmu_%d", id_counter++);
// Add the file as a drive
QDict *qdict1 = qdict_new();
qdict_put_str(qdict1, "id", tmp);
qdict_put_str(qdict1, "format", "raw");
qdict_put_str(qdict1, "file", filename);
QemuOpts *drvopts =
qemu_opts_from_qdict(qemu_find_opts("drive"), qdict1, &error_abort);
DriveInfo *dinfo = drive_new(drvopts, 0, &error_abort);
assert(dinfo);
// Create the usb-storage device
QDict *qdict2 = qdict_new();
// Specify device driver
qdict_put_str(qdict2, "driver", "usb-storage");
// Specify device identifier
qdict_put_str(qdict2, "drive", tmp);
g_free(tmp);
// Specify index/port
tmp = g_strdup_printf("1.%d.%d", port_map[player_index],
xmu_map[expansion_slot_index]);
qdict_put_str(qdict2, "port", tmp);
g_free(tmp);
// Create the device
QemuOpts *opts =
qemu_opts_from_qdict(qemu_find_opts("device"), qdict2, &error_abort);
DeviceState *dev = qdev_device_add(opts, &error_abort);
assert(dev);
xmu->dev = (void *)dev;
// Unref for eventual cleanup
qobject_unref(qdict1);
qobject_unref(qdict2);
if (!is_rebind) {
xemu_save_peripheral_settings(player_index, expansion_slot_index,
peripheral_type, xmu->filename);
}
return true;
}
void xemu_input_unbind_xmu(int player_index, int expansion_slot_index)
{
assert(player_index >= 0 && player_index < 4);
assert(expansion_slot_index >= 0 && expansion_slot_index < 2);
ControllerState *state = bound_controllers[player_index];
if (state->peripheral_types[expansion_slot_index] != PERIPHERAL_XMU)
return;
XmuState *xmu = (XmuState *)state->peripherals[expansion_slot_index];
if (xmu != NULL) {
if (xmu->dev != NULL) {
qdev_unplug((DeviceState *)xmu->dev, &error_abort);
object_unref(OBJECT(xmu->dev));
xmu->dev = NULL;
}
g_free((void *)xmu->filename);
xmu->filename = NULL;
}
}
void xemu_input_rebind_xmu(int port)
{
// Try to bind peripherals back to controller
for (int i = 0; i < 2; i++) {
enum peripheral_type peripheral_type =
(enum peripheral_type)(*peripheral_types_settings_map[port][i]);
// If peripheralType is out of range, change the settings for this
// controller and peripheral port to default
if (peripheral_type < PERIPHERAL_NONE ||
peripheral_type >= PERIPHERAL_TYPE_COUNT) {
xemu_save_peripheral_settings(port, i, PERIPHERAL_NONE, NULL);
peripheral_type = PERIPHERAL_NONE;
}
const char *param = *peripheral_params_settings_map[port][i];
if (peripheral_type == PERIPHERAL_XMU) {
if (param != NULL && strlen(param) > 0) {
// This is an XMU and needs to be bound to this controller
if (qemu_access(param, R_OK | W_OK) == 0) {
bound_controllers[port]->peripheral_types[i] =
peripheral_type;
bound_controllers[port]->peripherals[i] =
g_malloc(sizeof(XmuState));
memset(bound_controllers[port]->peripherals[i], 0,
sizeof(XmuState));
bool did_bind = xemu_input_bind_xmu(port, i, param, true);
if (did_bind) {
char *buf =
g_strdup_printf("Connected XMU %s to port %d%c",
param, port + 1, 'A' + i);
xemu_queue_notification(buf);
g_free(buf);
}
} else {
char *buf =
g_strdup_printf("Unable to bind XMU at %s to port %d%c",
param, port + 1, 'A' + i);
xemu_queue_error_message(buf);
g_free(buf);
}
}
}
}
}
void xemu_input_set_test_mode(int enabled)
{
test_mode = enabled;
+19
View File
@@ -26,6 +26,8 @@
#define XEMU_INPUT_H
#include <SDL2/SDL.h>
#include <stdbool.h>
#include "qemu/queue.h"
enum controller_state_buttons_mask {
@@ -63,6 +65,13 @@ enum controller_input_device_type {
INPUT_DEVICE_SDL_GAMECONTROLLER,
};
enum peripheral_type { PERIPHERAL_NONE, PERIPHERAL_XMU, PERIPHERAL_TYPE_COUNT };
typedef struct XmuState {
const char *filename;
void *dev;
} XmuState;
typedef struct ControllerState {
QTAILQ_ENTRY(ControllerState) entry;
@@ -88,6 +97,9 @@ typedef struct ControllerState {
SDL_JoystickID sdl_joystick_id;
SDL_JoystickGUID sdl_joystick_guid;
enum peripheral_type peripheral_types[2];
void *peripherals[2];
int bound; // Which port this input device is bound to
void *device; // DeviceState opaque
} ControllerState;
@@ -109,7 +121,14 @@ void xemu_input_update_sdl_controller_state(ControllerState *state);
void xemu_input_update_rumble(ControllerState *state);
ControllerState *xemu_input_get_bound(int index);
void xemu_input_bind(int index, ControllerState *state, int save);
bool xemu_input_bind_xmu(int player_index, int peripheral_port_index,
const char *filename, bool is_rebind);
void xemu_input_rebind_xmu(int port);
void xemu_input_unbind_xmu(int player_index, int peripheral_port_index);
int xemu_input_get_controller_default_bind_port(ControllerState *state, int start);
void xemu_save_peripheral_settings(int player_index, int peripheral_index,
int peripheral_type,
const char *peripheral_parameter);
void xemu_input_set_test_mode(int enabled);
int xemu_input_get_test_mode(void);
+62 -37
View File
@@ -16,25 +16,24 @@
// 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 <stdio.h>
#include <math.h>
#include <vector>
#include <fpng.h>
#include "ui/xemu-widescreen.h"
#include "gl-helpers.hh"
#include "stb_image.h"
#include "common.hh"
#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 "data/xmu_mask.png.h"
#include "notifications.hh"
#include "ui/xemu-widescreen.h"
#include "stb_image.h"
#include <fpng.h>
#include <math.h>
#include <stdio.h>
#include <vector>
Fbo *controller_fbo,
*logo_fbo;
GLuint g_controller_tex,
g_logo_tex,
g_icon_tex;
#include "ui/shader/xemu-logo-frag.h"
Fbo *controller_fbo, *xmu_fbo, *logo_fbo;
GLuint g_controller_tex, g_logo_tex, g_icon_tex, g_xmu_tex;
enum class ShaderType {
Blit,
@@ -186,9 +185,11 @@ static GLuint Shader(GLenum type, const char *src)
glGetShaderiv(shader, GL_COMPILE_STATUS, &status);
if (status != GL_TRUE) {
glGetShaderInfoLog(shader, sizeof(err_buf), NULL, err_buf);
fprintf(stderr, "Shader compilation failed: %s\n\n"
"[Shader Source]\n"
"%s\n", err_buf, src);
fprintf(stderr,
"Shader compilation failed: %s\n\n"
"[Shader Source]\n"
"%s\n",
err_buf, src);
assert(0);
}
@@ -222,15 +223,15 @@ void main() {
GLuint vert = Shader(GL_VERTEX_SHADER, vert_src);
assert(vert != 0);
// const char *image_frag_src = R"(
// #version 150 core
// uniform sampler2D tex;
// in vec2 Texcoord;
// out vec4 out_Color;
// void main() {
// out_Color.rgba = texture(tex, Texcoord);
// }
// )";
// const char *image_frag_src = R"(
// #version 150 core
// uniform sampler2D tex;
// in vec2 Texcoord;
// out vec4 out_Color;
// void main() {
// out_Color.rgba = texture(tex, Texcoord);
// }
// )";
const char *image_gamma_frag_src = R"(
#version 400 core
@@ -370,7 +371,7 @@ void RenderDecal(DecalShader *s, float x, float y, float w, float h,
glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &th_i);
float tw = tw_i, th = th_i;
#define COL(color, c) (float)(((color)>>((c)*8)) & 0xff)/255.0
#define COL(color, c) (float)(((color) >> ((c)*8)) & 0xff) / 255.0
if (s->flipy_loc >= 0) {
glUniform1i(s->flipy_loc, s->flip);
}
@@ -403,7 +404,7 @@ void RenderDecal(DecalShader *s, float x, float y, float w, float h,
if (s->scale_loc >= 0) {
glUniform1f(s->scale_loc, s->scale);
}
#undef COL
#undef COL
glDrawElements(GL_TRIANGLE_FAN, 4, GL_UNSIGNED_INT, NULL);
}
@@ -412,14 +413,15 @@ struct rect {
};
static const struct rect tex_items[] = {
{ 0, 148, 467, 364 }, // obj_controller
{ 0, 81, 67, 67 }, // obj_lstick
{ 0, 14, 67, 67 }, // obj_rstick
{ 67, 104, 68, 44 }, // obj_port_socket
{ 67, 76, 28, 28 }, // obj_port_lbl_1
{ 67, 48, 28, 28 }, // obj_port_lbl_2
{ 67, 20, 28, 28 }, // obj_port_lbl_3
{ 95, 76, 28, 28 }, // obj_port_lbl_4
{ 0, 148, 467, 364 }, // obj_controller
{ 0, 81, 67, 67 }, // obj_lstick
{ 0, 14, 67, 67 }, // obj_rstick
{ 67, 104, 68, 44 }, // obj_port_socket
{ 67, 76, 28, 28 }, // obj_port_lbl_1
{ 67, 48, 28, 28 }, // obj_port_lbl_2
{ 67, 20, 28, 28 }, // obj_port_lbl_3
{ 95, 76, 28, 28 }, // obj_port_lbl_4
{ 0, 0, 512, 512 } // obj_xmu
};
enum tex_item_names {
@@ -431,6 +433,7 @@ enum tex_item_names {
obj_port_lbl_2,
obj_port_lbl_3,
obj_port_lbl_4,
obj_xmu
};
void InitCustomRendering(void)
@@ -441,6 +444,9 @@ void InitCustomRendering(void)
g_decal_shader = NewDecalShader(ShaderType::Mask);
controller_fbo = new Fbo(512, 512);
g_xmu_tex = LoadTextureFromMemory(xmu_mask_data, xmu_mask_size);
xmu_fbo = new Fbo(512, 256);
g_logo_tex = LoadTextureFromMemory(logo_sdf_data, logo_sdf_size);
g_logo_shader = NewDecalShader(ShaderType::Logo);
logo_fbo = new Fbo(512, 512);
@@ -646,6 +652,26 @@ void RenderControllerPort(float frame_x, float frame_y, int i,
glUseProgram(0);
}
void RenderXmu(float frame_x, float frame_y, uint32_t primary_color,
uint32_t secondary_color)
{
glUseProgram(g_decal_shader->prog);
glBindVertexArray(g_decal_shader->vao);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, g_xmu_tex);
glBlendEquation(GL_FUNC_ADD);
glBlendFunc(GL_ONE, GL_ZERO);
// Render xmu
RenderDecal(g_decal_shader, frame_x, frame_y, 256, 256,
tex_items[obj_xmu].x, tex_items[obj_xmu].y,
tex_items[obj_xmu].w, tex_items[obj_xmu].h, primary_color,
secondary_color, 0);
glBindVertexArray(0);
glUseProgram(0);
}
void RenderLogo(uint32_t time)
{
uint32_t color = 0x62ca13ff;
@@ -801,8 +827,7 @@ void SaveScreenshot(GLuint tex, bool flip)
time_t t = time(NULL);
struct tm *tmp = localtime(&t);
if (tmp) {
strftime(fname, sizeof(fname), "xemu-%Y-%m-%d-%H-%M-%S.png",
tmp);
strftime(fname, sizeof(fname), "xemu-%Y-%m-%d-%H-%M-%S.png", tmp);
} else {
strcpy(fname, "xemu.png");
}
+3 -1
View File
@@ -38,7 +38,7 @@ public:
void Restore();
};
extern Fbo *controller_fbo, *logo_fbo;
extern Fbo *controller_fbo, *xmu_fbo, *logo_fbo;
extern GLuint g_icon_tex;
void InitCustomRendering(void);
@@ -47,6 +47,8 @@ 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 RenderXmu(float frame_x, float frame_y, uint32_t primary_color,
uint32_t secondary_color);
void RenderFramebuffer(GLint tex, int width, int height, bool flip);
void RenderFramebuffer(GLint tex, int width, int height, bool flip, float scale[2]);
bool RenderFramebufferToPng(GLuint tex, bool flip, std::vector<uint8_t> &png, int max_width = 0, int max_height = 0);
+338 -96
View File
File diff suppressed because it is too large Load Diff