mirror of
https://github.com/izzy2lost/xemu.git
synced 2026-07-06 00:20:22 -07:00
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:
@@ -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,5 +1,6 @@
|
||||
pfiles = [
|
||||
'controller_mask.png',
|
||||
'xmu_mask.png',
|
||||
'logo_sdf.png',
|
||||
'xemu_64x64.png',
|
||||
'abxy.ttf',
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 9.7 KiB |
+1
-1
@@ -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)
|
||||
|
||||
|
||||
Vendored
+51
@@ -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;
|
||||
}
|
||||
Vendored
+16
@@ -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
|
||||
Vendored
+3
@@ -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)
|
||||
|
||||
@@ -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
@@ -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;
|
||||
|
||||
@@ -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
@@ -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");
|
||||
}
|
||||
|
||||
@@ -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
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user