mirror of
https://github.com/xemu-project/xemu.git
synced 2024-11-23 03:29:43 +00: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:
parent
800eb468a4
commit
03f40b1d8e
@ -25,6 +25,27 @@ input:
|
|||||||
port2: string
|
port2: string
|
||||||
port3: string
|
port3: string
|
||||||
port4: 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
|
gamecontrollerdb_path: string
|
||||||
auto_bind:
|
auto_bind:
|
||||||
type: bool
|
type: bool
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
pfiles = [
|
pfiles = [
|
||||||
'controller_mask.png',
|
'controller_mask.png',
|
||||||
|
'xmu_mask.png',
|
||||||
'logo_sdf.png',
|
'logo_sdf.png',
|
||||||
'xemu_64x64.png',
|
'xemu_64x64.png',
|
||||||
'abxy.ttf',
|
'abxy.ttf',
|
||||||
|
BIN
data/xmu_mask.png
Normal file
BIN
data/xmu_mask.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 9.7 KiB |
@ -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_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_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(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)
|
softmmu_ss.add_all(xemu_ss)
|
||||||
|
|
||||||
|
51
ui/thirdparty/fatx/fatx.c
vendored
Normal file
51
ui/thirdparty/fatx/fatx.c
vendored
Normal 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
ui/thirdparty/fatx/fatx.h
vendored
Normal file
16
ui/thirdparty/fatx/fatx.h
vendored
Normal 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
ui/thirdparty/meson.build
vendored
3
ui/thirdparty/meson.build
vendored
@ -62,3 +62,6 @@ fpng = declare_dependency(include_directories: 'fpng', link_with: libfpng)
|
|||||||
|
|
||||||
json = declare_dependency(include_directories: 'json')
|
json = declare_dependency(include_directories: 'json')
|
||||||
httplib = declare_dependency(include_directories: 'httplib')
|
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.lpstrInitialDir = initialDir;
|
||||||
ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST | OFN_NOCHANGEDIR;
|
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) {
|
if (flags & NOC_FILE_DIALOG_OPEN) {
|
||||||
ret = GetOpenFileNameW(&ofn);
|
ret = GetOpenFileNameW(&ofn);
|
||||||
} else {
|
} else {
|
||||||
|
258
ui/xemu-input.c
258
ui/xemu-input.c
@ -32,6 +32,8 @@
|
|||||||
#include "xemu-notifications.h"
|
#include "xemu-notifications.h"
|
||||||
#include "xemu-settings.h"
|
#include "xemu-settings.h"
|
||||||
|
|
||||||
|
#include "sysemu/blockdev.h"
|
||||||
|
|
||||||
// #define DEBUG_INPUT
|
// #define DEBUG_INPUT
|
||||||
|
|
||||||
#ifdef DEBUG_INPUT
|
#ifdef DEBUG_INPUT
|
||||||
@ -93,8 +95,32 @@ static const char **port_index_to_settings_key_map[] = {
|
|||||||
&g_config.input.bindings.port4,
|
&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 int sdl_kbd_scancode_map[25];
|
||||||
|
|
||||||
|
static const int port_map[4] = { 3, 4, 1, 2 };
|
||||||
|
|
||||||
void xemu_input_init(void)
|
void xemu_input_init(void)
|
||||||
{
|
{
|
||||||
if (g_config.input.background_input_capture) {
|
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->type = INPUT_DEVICE_SDL_KEYBOARD;
|
||||||
new_con->name = "Keyboard";
|
new_con->name = "Keyboard";
|
||||||
new_con->bound = -1;
|
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[0] = g_config.input.keyboard_controller_scancode_map.a;
|
||||||
sdl_kbd_scancode_map[1] = g_config.input.keyboard_controller_scancode_map.b;
|
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];
|
char buf[128];
|
||||||
snprintf(buf, sizeof(buf), "Connected '%s' to port %d", new_con->name, port+1);
|
snprintf(buf, sizeof(buf), "Connected '%s' to port %d", new_con->name, port+1);
|
||||||
xemu_queue_notification(buf);
|
xemu_queue_notification(buf);
|
||||||
|
xemu_input_rebind_xmu(port);
|
||||||
}
|
}
|
||||||
|
|
||||||
QTAILQ_INSERT_TAIL(&available_controllers, new_con, entry);
|
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;
|
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)
|
void xemu_input_process_sdl_events(const SDL_Event *event)
|
||||||
{
|
{
|
||||||
if (event->type == SDL_CONTROLLERDEVICEADDED) {
|
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_id = SDL_JoystickInstanceID(new_con->sdl_joystick);
|
||||||
new_con->sdl_joystick_guid = SDL_JoystickGetGUID(new_con->sdl_joystick);
|
new_con->sdl_joystick_guid = SDL_JoystickGetGUID(new_con->sdl_joystick);
|
||||||
new_con->bound = -1;
|
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 };
|
char guid_buf[35] = { 0 };
|
||||||
SDL_JoystickGetGUIDString(new_con->sdl_joystick_guid, guid_buf, sizeof(guid_buf));
|
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];
|
char buf[128];
|
||||||
snprintf(buf, sizeof(buf), "Connected '%s' to port %d", new_con->name, port+1);
|
snprintf(buf, sizeof(buf), "Connected '%s' to port %d", new_con->name, port+1);
|
||||||
xemu_queue_notification(buf);
|
xemu_queue_notification(buf);
|
||||||
|
xemu_input_rebind_xmu(port);
|
||||||
}
|
}
|
||||||
} else if (event->type == SDL_CONTROLLERDEVICEREMOVED) {
|
} else if (event->type == SDL_CONTROLLERDEVICEREMOVED) {
|
||||||
DPRINTF("Controller Removed: %d\n", event->cdevice.which);
|
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) {
|
if (iter->sdl_gamecontroller) {
|
||||||
SDL_GameControllerClose(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);
|
free(iter);
|
||||||
|
|
||||||
handled = 1;
|
handled = 1;
|
||||||
@ -380,12 +439,9 @@ void xemu_input_update_sdl_controller_state(ControllerState *state)
|
|||||||
}
|
}
|
||||||
|
|
||||||
const SDL_GameControllerAxis sdl_axis_map[6] = {
|
const SDL_GameControllerAxis sdl_axis_map[6] = {
|
||||||
SDL_CONTROLLER_AXIS_TRIGGERLEFT,
|
SDL_CONTROLLER_AXIS_TRIGGERLEFT, SDL_CONTROLLER_AXIS_TRIGGERRIGHT,
|
||||||
SDL_CONTROLLER_AXIS_TRIGGERRIGHT,
|
SDL_CONTROLLER_AXIS_LEFTX, SDL_CONTROLLER_AXIS_LEFTY,
|
||||||
SDL_CONTROLLER_AXIS_LEFTX,
|
SDL_CONTROLLER_AXIS_RIGHTX, SDL_CONTROLLER_AXIS_RIGHTY,
|
||||||
SDL_CONTROLLER_AXIS_LEFTY,
|
|
||||||
SDL_CONTROLLER_AXIS_RIGHTX,
|
|
||||||
SDL_CONTROLLER_AXIS_RIGHTY,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
for (int i = 0; i < 6; i++) {
|
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]) {
|
if (bound_controllers[index]) {
|
||||||
assert(bound_controllers[index]->device != NULL);
|
assert(bound_controllers[index]->device != NULL);
|
||||||
Error *err = 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);
|
qdev_unplug((DeviceState *)bound_controllers[index]->device, &err);
|
||||||
assert(err == NULL);
|
assert(err == NULL);
|
||||||
|
|
||||||
@ -460,7 +532,6 @@ void xemu_input_bind(int index, ControllerState *state, int save)
|
|||||||
bound_controllers[index] = state;
|
bound_controllers[index] = state;
|
||||||
bound_controllers[index]->bound = index;
|
bound_controllers[index]->bound = index;
|
||||||
|
|
||||||
const int port_map[4] = {3, 4, 1, 2};
|
|
||||||
char *tmp;
|
char *tmp;
|
||||||
|
|
||||||
// Create controller's internal USB hub.
|
// 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)
|
void xemu_input_set_test_mode(int enabled)
|
||||||
{
|
{
|
||||||
test_mode = enabled;
|
test_mode = enabled;
|
||||||
|
@ -26,6 +26,8 @@
|
|||||||
#define XEMU_INPUT_H
|
#define XEMU_INPUT_H
|
||||||
|
|
||||||
#include <SDL2/SDL.h>
|
#include <SDL2/SDL.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
#include "qemu/queue.h"
|
#include "qemu/queue.h"
|
||||||
|
|
||||||
enum controller_state_buttons_mask {
|
enum controller_state_buttons_mask {
|
||||||
@ -63,6 +65,13 @@ enum controller_input_device_type {
|
|||||||
INPUT_DEVICE_SDL_GAMECONTROLLER,
|
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 {
|
typedef struct ControllerState {
|
||||||
QTAILQ_ENTRY(ControllerState) entry;
|
QTAILQ_ENTRY(ControllerState) entry;
|
||||||
|
|
||||||
@ -88,6 +97,9 @@ typedef struct ControllerState {
|
|||||||
SDL_JoystickID sdl_joystick_id;
|
SDL_JoystickID sdl_joystick_id;
|
||||||
SDL_JoystickGUID sdl_joystick_guid;
|
SDL_JoystickGUID sdl_joystick_guid;
|
||||||
|
|
||||||
|
enum peripheral_type peripheral_types[2];
|
||||||
|
void *peripherals[2];
|
||||||
|
|
||||||
int bound; // Which port this input device is bound to
|
int bound; // Which port this input device is bound to
|
||||||
void *device; // DeviceState opaque
|
void *device; // DeviceState opaque
|
||||||
} ControllerState;
|
} ControllerState;
|
||||||
@ -109,7 +121,14 @@ void xemu_input_update_sdl_controller_state(ControllerState *state);
|
|||||||
void xemu_input_update_rumble(ControllerState *state);
|
void xemu_input_update_rumble(ControllerState *state);
|
||||||
ControllerState *xemu_input_get_bound(int index);
|
ControllerState *xemu_input_get_bound(int index);
|
||||||
void xemu_input_bind(int index, ControllerState *state, int save);
|
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);
|
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);
|
void xemu_input_set_test_mode(int enabled);
|
||||||
int xemu_input_get_test_mode(void);
|
int xemu_input_get_test_mode(void);
|
||||||
|
@ -16,25 +16,24 @@
|
|||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
//
|
//
|
||||||
#include "common.hh"
|
#include "ui/xemu-widescreen.h"
|
||||||
#include <stdio.h>
|
|
||||||
#include <math.h>
|
|
||||||
#include <vector>
|
|
||||||
#include <fpng.h>
|
|
||||||
#include "gl-helpers.hh"
|
#include "gl-helpers.hh"
|
||||||
#include "stb_image.h"
|
#include "common.hh"
|
||||||
#include "data/controller_mask.png.h"
|
#include "data/controller_mask.png.h"
|
||||||
#include "data/logo_sdf.png.h"
|
#include "data/logo_sdf.png.h"
|
||||||
#include "ui/shader/xemu-logo-frag.h"
|
|
||||||
#include "data/xemu_64x64.png.h"
|
#include "data/xemu_64x64.png.h"
|
||||||
|
#include "data/xmu_mask.png.h"
|
||||||
#include "notifications.hh"
|
#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,
|
#include "ui/shader/xemu-logo-frag.h"
|
||||||
*logo_fbo;
|
|
||||||
GLuint g_controller_tex,
|
Fbo *controller_fbo, *xmu_fbo, *logo_fbo;
|
||||||
g_logo_tex,
|
GLuint g_controller_tex, g_logo_tex, g_icon_tex, g_xmu_tex;
|
||||||
g_icon_tex;
|
|
||||||
|
|
||||||
enum class ShaderType {
|
enum class ShaderType {
|
||||||
Blit,
|
Blit,
|
||||||
@ -186,9 +185,11 @@ static GLuint Shader(GLenum type, const char *src)
|
|||||||
glGetShaderiv(shader, GL_COMPILE_STATUS, &status);
|
glGetShaderiv(shader, GL_COMPILE_STATUS, &status);
|
||||||
if (status != GL_TRUE) {
|
if (status != GL_TRUE) {
|
||||||
glGetShaderInfoLog(shader, sizeof(err_buf), NULL, err_buf);
|
glGetShaderInfoLog(shader, sizeof(err_buf), NULL, err_buf);
|
||||||
fprintf(stderr, "Shader compilation failed: %s\n\n"
|
fprintf(stderr,
|
||||||
"[Shader Source]\n"
|
"Shader compilation failed: %s\n\n"
|
||||||
"%s\n", err_buf, src);
|
"[Shader Source]\n"
|
||||||
|
"%s\n",
|
||||||
|
err_buf, src);
|
||||||
assert(0);
|
assert(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -222,15 +223,15 @@ void main() {
|
|||||||
GLuint vert = Shader(GL_VERTEX_SHADER, vert_src);
|
GLuint vert = Shader(GL_VERTEX_SHADER, vert_src);
|
||||||
assert(vert != 0);
|
assert(vert != 0);
|
||||||
|
|
||||||
// const char *image_frag_src = R"(
|
// const char *image_frag_src = R"(
|
||||||
// #version 150 core
|
// #version 150 core
|
||||||
// uniform sampler2D tex;
|
// uniform sampler2D tex;
|
||||||
// in vec2 Texcoord;
|
// in vec2 Texcoord;
|
||||||
// out vec4 out_Color;
|
// out vec4 out_Color;
|
||||||
// void main() {
|
// void main() {
|
||||||
// out_Color.rgba = texture(tex, Texcoord);
|
// out_Color.rgba = texture(tex, Texcoord);
|
||||||
// }
|
// }
|
||||||
// )";
|
// )";
|
||||||
|
|
||||||
const char *image_gamma_frag_src = R"(
|
const char *image_gamma_frag_src = R"(
|
||||||
#version 400 core
|
#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);
|
glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &th_i);
|
||||||
float tw = tw_i, th = 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) {
|
if (s->flipy_loc >= 0) {
|
||||||
glUniform1i(s->flipy_loc, s->flip);
|
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) {
|
if (s->scale_loc >= 0) {
|
||||||
glUniform1f(s->scale_loc, s->scale);
|
glUniform1f(s->scale_loc, s->scale);
|
||||||
}
|
}
|
||||||
#undef COL
|
#undef COL
|
||||||
glDrawElements(GL_TRIANGLE_FAN, 4, GL_UNSIGNED_INT, NULL);
|
glDrawElements(GL_TRIANGLE_FAN, 4, GL_UNSIGNED_INT, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -412,14 +413,15 @@ struct rect {
|
|||||||
};
|
};
|
||||||
|
|
||||||
static const struct rect tex_items[] = {
|
static const struct rect tex_items[] = {
|
||||||
{ 0, 148, 467, 364 }, // obj_controller
|
{ 0, 148, 467, 364 }, // obj_controller
|
||||||
{ 0, 81, 67, 67 }, // obj_lstick
|
{ 0, 81, 67, 67 }, // obj_lstick
|
||||||
{ 0, 14, 67, 67 }, // obj_rstick
|
{ 0, 14, 67, 67 }, // obj_rstick
|
||||||
{ 67, 104, 68, 44 }, // obj_port_socket
|
{ 67, 104, 68, 44 }, // obj_port_socket
|
||||||
{ 67, 76, 28, 28 }, // obj_port_lbl_1
|
{ 67, 76, 28, 28 }, // obj_port_lbl_1
|
||||||
{ 67, 48, 28, 28 }, // obj_port_lbl_2
|
{ 67, 48, 28, 28 }, // obj_port_lbl_2
|
||||||
{ 67, 20, 28, 28 }, // obj_port_lbl_3
|
{ 67, 20, 28, 28 }, // obj_port_lbl_3
|
||||||
{ 95, 76, 28, 28 }, // obj_port_lbl_4
|
{ 95, 76, 28, 28 }, // obj_port_lbl_4
|
||||||
|
{ 0, 0, 512, 512 } // obj_xmu
|
||||||
};
|
};
|
||||||
|
|
||||||
enum tex_item_names {
|
enum tex_item_names {
|
||||||
@ -431,6 +433,7 @@ enum tex_item_names {
|
|||||||
obj_port_lbl_2,
|
obj_port_lbl_2,
|
||||||
obj_port_lbl_3,
|
obj_port_lbl_3,
|
||||||
obj_port_lbl_4,
|
obj_port_lbl_4,
|
||||||
|
obj_xmu
|
||||||
};
|
};
|
||||||
|
|
||||||
void InitCustomRendering(void)
|
void InitCustomRendering(void)
|
||||||
@ -441,6 +444,9 @@ void InitCustomRendering(void)
|
|||||||
g_decal_shader = NewDecalShader(ShaderType::Mask);
|
g_decal_shader = NewDecalShader(ShaderType::Mask);
|
||||||
controller_fbo = new Fbo(512, 512);
|
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_tex = LoadTextureFromMemory(logo_sdf_data, logo_sdf_size);
|
||||||
g_logo_shader = NewDecalShader(ShaderType::Logo);
|
g_logo_shader = NewDecalShader(ShaderType::Logo);
|
||||||
logo_fbo = new Fbo(512, 512);
|
logo_fbo = new Fbo(512, 512);
|
||||||
@ -646,6 +652,26 @@ void RenderControllerPort(float frame_x, float frame_y, int i,
|
|||||||
glUseProgram(0);
|
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)
|
void RenderLogo(uint32_t time)
|
||||||
{
|
{
|
||||||
uint32_t color = 0x62ca13ff;
|
uint32_t color = 0x62ca13ff;
|
||||||
@ -801,8 +827,7 @@ void SaveScreenshot(GLuint tex, bool flip)
|
|||||||
time_t t = time(NULL);
|
time_t t = time(NULL);
|
||||||
struct tm *tmp = localtime(&t);
|
struct tm *tmp = localtime(&t);
|
||||||
if (tmp) {
|
if (tmp) {
|
||||||
strftime(fname, sizeof(fname), "xemu-%Y-%m-%d-%H-%M-%S.png",
|
strftime(fname, sizeof(fname), "xemu-%Y-%m-%d-%H-%M-%S.png", tmp);
|
||||||
tmp);
|
|
||||||
} else {
|
} else {
|
||||||
strcpy(fname, "xemu.png");
|
strcpy(fname, "xemu.png");
|
||||||
}
|
}
|
||||||
|
@ -38,7 +38,7 @@ public:
|
|||||||
void Restore();
|
void Restore();
|
||||||
};
|
};
|
||||||
|
|
||||||
extern Fbo *controller_fbo, *logo_fbo;
|
extern Fbo *controller_fbo, *xmu_fbo, *logo_fbo;
|
||||||
extern GLuint g_icon_tex;
|
extern GLuint g_icon_tex;
|
||||||
|
|
||||||
void InitCustomRendering(void);
|
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);
|
uint32_t secondary_color, ControllerState *state);
|
||||||
void RenderControllerPort(float frame_x, float frame_y, int i,
|
void RenderControllerPort(float frame_x, float frame_y, int i,
|
||||||
uint32_t port_color);
|
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);
|
||||||
void RenderFramebuffer(GLint tex, int width, int height, bool flip, float scale[2]);
|
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);
|
bool RenderFramebufferToPng(GLuint tex, bool flip, std::vector<uint8_t> &png, int max_width = 0, int max_height = 0);
|
||||||
|
@ -40,6 +40,10 @@
|
|||||||
#include "../xemu-os-utils.h"
|
#include "../xemu-os-utils.h"
|
||||||
#include "../xemu-xbe.h"
|
#include "../xemu-xbe.h"
|
||||||
|
|
||||||
|
#include "../thirdparty/fatx/fatx.h"
|
||||||
|
|
||||||
|
#define DEFAULT_XMU_SIZE 8388608
|
||||||
|
|
||||||
MainMenuScene g_main_menu;
|
MainMenuScene g_main_menu;
|
||||||
|
|
||||||
MainMenuTabView::~MainMenuTabView() {}
|
MainMenuTabView::~MainMenuTabView() {}
|
||||||
@ -86,6 +90,9 @@ void MainMenuInputView::Draw()
|
|||||||
// Dimensions of controller (rendered at origin)
|
// Dimensions of controller (rendered at origin)
|
||||||
float controller_width = 477.0f;
|
float controller_width = 477.0f;
|
||||||
float controller_height = 395.0f;
|
float controller_height = 395.0f;
|
||||||
|
// Dimensions of XMU
|
||||||
|
float xmu_x = 0, xmu_x_stride = 256, xmu_y = 0;
|
||||||
|
float xmu_w = 256, xmu_h = 256;
|
||||||
|
|
||||||
// Setup rendering to fbo for controller and port images
|
// Setup rendering to fbo for controller and port images
|
||||||
controller_fbo->Target();
|
controller_fbo->Target();
|
||||||
@ -120,14 +127,14 @@ void MainMenuInputView::Draw()
|
|||||||
// uses the texture as a unique ID. Push a new ID now to resolve
|
// uses the texture as a unique ID. Push a new ID now to resolve
|
||||||
// the conflict.
|
// the conflict.
|
||||||
ImGui::PushID(i);
|
ImGui::PushID(i);
|
||||||
float x = b_x+i*b_x_stride;
|
float x = b_x + i * b_x_stride;
|
||||||
ImGui::PushStyleColor(ImGuiCol_Button, is_selected ?
|
ImGui::PushStyleColor(ImGuiCol_Button,
|
||||||
color_active :
|
is_selected ? color_active : color_inactive);
|
||||||
color_inactive);
|
bool activated = ImGui::ImageButton(
|
||||||
bool activated = ImGui::ImageButton(id,
|
id,
|
||||||
ImVec2(b_w*g_viewport_mgr.m_scale,b_h*g_viewport_mgr.m_scale),
|
ImVec2(b_w * g_viewport_mgr.m_scale, b_h * g_viewport_mgr.m_scale),
|
||||||
ImVec2(x/t_w, (b_y+b_h)/t_h),
|
ImVec2(x / t_w, (b_y + b_h) / t_h),
|
||||||
ImVec2((x+b_w)/t_w, b_y/t_h),
|
ImVec2((x + b_w) / t_w, b_y / t_h),
|
||||||
port_padding * g_viewport_mgr.m_scale);
|
port_padding * g_viewport_mgr.m_scale);
|
||||||
ImGui::PopStyleColor();
|
ImGui::PopStyleColor();
|
||||||
|
|
||||||
@ -193,6 +200,16 @@ void MainMenuInputView::Draw()
|
|||||||
}
|
}
|
||||||
if (ImGui::Selectable(selectable_label, is_selected)) {
|
if (ImGui::Selectable(selectable_label, is_selected)) {
|
||||||
xemu_input_bind(active, iter, 1);
|
xemu_input_bind(active, iter, 1);
|
||||||
|
|
||||||
|
// FIXME: We want to bind the XMU here, but we can't because we
|
||||||
|
// just unbound it and we need to wait for Qemu to release the
|
||||||
|
// file
|
||||||
|
|
||||||
|
// If we previously had no controller connected, we can rebind
|
||||||
|
// the XMU
|
||||||
|
if (bound_state == NULL)
|
||||||
|
xemu_input_rebind_xmu(active);
|
||||||
|
|
||||||
bound_state = iter;
|
bound_state = iter;
|
||||||
}
|
}
|
||||||
if (is_selected) {
|
if (is_selected) {
|
||||||
@ -260,6 +277,168 @@ void MainMenuInputView::Draw()
|
|||||||
ImGui::PopFont();
|
ImGui::PopFont();
|
||||||
ImGui::SetCursorPos(pos);
|
ImGui::SetCursorPos(pos);
|
||||||
|
|
||||||
|
if (bound_state) {
|
||||||
|
SectionTitle("Expansion Slots");
|
||||||
|
// Begin a 2-column layout to render the expansion slots
|
||||||
|
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing,
|
||||||
|
g_viewport_mgr.Scale(ImVec2(0, 12)));
|
||||||
|
ImGui::Columns(2, "mixed", false);
|
||||||
|
|
||||||
|
xmu_fbo->Target();
|
||||||
|
id = (ImTextureID)(intptr_t)xmu_fbo->Texture();
|
||||||
|
|
||||||
|
const char *img_file_filters = ".img Files\0*.img\0All Files\0*.*\0";
|
||||||
|
const char *comboLabels[2] = { "###ExpansionSlotA",
|
||||||
|
"###ExpansionSlotB" };
|
||||||
|
for (int i = 0; i < 2; i++) {
|
||||||
|
// Display a combo box to allow the user to choose the type of
|
||||||
|
// peripheral they want to use
|
||||||
|
enum peripheral_type selected_type =
|
||||||
|
bound_state->peripheral_types[i];
|
||||||
|
const char *peripheral_type_names[2] = { "None", "Memory Unit" };
|
||||||
|
const char *selected_peripheral_type =
|
||||||
|
peripheral_type_names[selected_type];
|
||||||
|
ImGui::SetNextItemWidth(-FLT_MIN);
|
||||||
|
if (ImGui::BeginCombo(comboLabels[i], selected_peripheral_type,
|
||||||
|
ImGuiComboFlags_NoArrowButton)) {
|
||||||
|
// Handle all available peripheral types
|
||||||
|
for (int j = 0; j < 2; j++) {
|
||||||
|
bool is_selected = selected_type == j;
|
||||||
|
ImGui::PushID(j);
|
||||||
|
const char *selectable_label = peripheral_type_names[j];
|
||||||
|
|
||||||
|
if (ImGui::Selectable(selectable_label, is_selected)) {
|
||||||
|
// Free any existing peripheral
|
||||||
|
if (bound_state->peripherals[i] != NULL) {
|
||||||
|
if (bound_state->peripheral_types[i] ==
|
||||||
|
PERIPHERAL_XMU) {
|
||||||
|
// Another peripheral was already bound.
|
||||||
|
// Unplugging
|
||||||
|
xemu_input_unbind_xmu(active, i);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Free the existing state
|
||||||
|
g_free((void *)bound_state->peripherals[i]);
|
||||||
|
bound_state->peripherals[i] = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Change the peripheral type to the newly selected type
|
||||||
|
bound_state->peripheral_types[i] =
|
||||||
|
(enum peripheral_type)j;
|
||||||
|
|
||||||
|
// Allocate state for the new peripheral
|
||||||
|
if (j == PERIPHERAL_XMU) {
|
||||||
|
bound_state->peripherals[i] =
|
||||||
|
g_malloc(sizeof(XmuState));
|
||||||
|
memset(bound_state->peripherals[i], 0,
|
||||||
|
sizeof(XmuState));
|
||||||
|
}
|
||||||
|
|
||||||
|
xemu_save_peripheral_settings(
|
||||||
|
active, i, bound_state->peripheral_types[i], NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_selected) {
|
||||||
|
ImGui::SetItemDefaultFocus();
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::PopID();
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::EndCombo();
|
||||||
|
}
|
||||||
|
DrawComboChevron();
|
||||||
|
|
||||||
|
// Set an X offset to center the image button within the column
|
||||||
|
ImGui::SetCursorPosX(
|
||||||
|
ImGui::GetCursorPosX() +
|
||||||
|
(int)((ImGui::GetColumnWidth() -
|
||||||
|
xmu_w * g_viewport_mgr.m_scale -
|
||||||
|
2 * port_padding * g_viewport_mgr.m_scale) /
|
||||||
|
2));
|
||||||
|
|
||||||
|
selected_type = bound_state->peripheral_types[i];
|
||||||
|
if (selected_type == PERIPHERAL_XMU) {
|
||||||
|
float x = xmu_x + i * xmu_x_stride;
|
||||||
|
float y = xmu_y;
|
||||||
|
|
||||||
|
XmuState *xmu = (XmuState *)bound_state->peripherals[i];
|
||||||
|
if (xmu->filename != NULL && strlen(xmu->filename) > 0) {
|
||||||
|
RenderXmu(x, y, 0x81dc8a00, 0x0f0f0f00);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
RenderXmu(x, y, 0x1f1f1f00, 0x0f0f0f00);
|
||||||
|
}
|
||||||
|
|
||||||
|
ImVec2 xmu_display_size;
|
||||||
|
if (ImGui::GetContentRegionMax().x <
|
||||||
|
xmu_h * g_viewport_mgr.m_scale) {
|
||||||
|
xmu_display_size.x = ImGui::GetContentRegionMax().x / 2;
|
||||||
|
xmu_display_size.y = xmu_display_size.x * xmu_h / xmu_w;
|
||||||
|
} else {
|
||||||
|
xmu_display_size = ImVec2(xmu_w * g_viewport_mgr.m_scale,
|
||||||
|
xmu_h * g_viewport_mgr.m_scale);
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::SetCursorPosX(
|
||||||
|
ImGui::GetCursorPosX() +
|
||||||
|
(int)((ImGui::GetColumnWidth() - xmu_display_size.x) /
|
||||||
|
2.0));
|
||||||
|
|
||||||
|
ImGui::Image(id, xmu_display_size, ImVec2(0.5f * i, 1),
|
||||||
|
ImVec2(0.5f * (i + 1), 0));
|
||||||
|
ImVec2 pos = ImGui::GetCursorPos();
|
||||||
|
|
||||||
|
ImGui::SetCursorPos(pos);
|
||||||
|
|
||||||
|
// Button to generate a new XMU
|
||||||
|
ImGui::PushID(i);
|
||||||
|
if (ImGui::Button("New Image", ImVec2(250, 0))) {
|
||||||
|
int flags = NOC_FILE_DIALOG_SAVE |
|
||||||
|
NOC_FILE_DIALOG_OVERWRITE_CONFIRMATION;
|
||||||
|
const char *new_path = PausedFileOpen(
|
||||||
|
flags, img_file_filters, NULL, "xmu.img");
|
||||||
|
|
||||||
|
if (new_path) {
|
||||||
|
if (create_fatx_image(new_path, DEFAULT_XMU_SIZE)) {
|
||||||
|
// XMU was created successfully. Bind it
|
||||||
|
xemu_input_bind_xmu(active, i, new_path, false);
|
||||||
|
} else {
|
||||||
|
// Show alert message
|
||||||
|
char *msg = g_strdup_printf(
|
||||||
|
"Unable to create XMU image at %s", new_path);
|
||||||
|
xemu_queue_error_message(msg);
|
||||||
|
g_free(msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *xmu_port_path = NULL;
|
||||||
|
if (xmu->filename == NULL)
|
||||||
|
xmu_port_path = g_strdup("");
|
||||||
|
else
|
||||||
|
xmu_port_path = g_strdup(xmu->filename);
|
||||||
|
if (FilePicker("Image", &xmu_port_path, img_file_filters)) {
|
||||||
|
if (strlen(xmu_port_path) == 0) {
|
||||||
|
xemu_input_unbind_xmu(active, i);
|
||||||
|
} else {
|
||||||
|
xemu_input_bind_xmu(active, i, xmu_port_path, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
g_free((void *)xmu_port_path);
|
||||||
|
|
||||||
|
ImGui::PopID();
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::NextColumn();
|
||||||
|
}
|
||||||
|
|
||||||
|
xmu_fbo->Restore();
|
||||||
|
|
||||||
|
ImGui::PopStyleVar(); // ItemSpacing
|
||||||
|
ImGui::Columns(1);
|
||||||
|
}
|
||||||
|
|
||||||
SectionTitle("Options");
|
SectionTitle("Options");
|
||||||
Toggle("Auto-bind controllers", &g_config.input.auto_bind,
|
Toggle("Auto-bind controllers", &g_config.input.auto_bind,
|
||||||
"Bind newly connected controllers to any open port");
|
"Bind newly connected controllers to any open port");
|
||||||
@ -625,8 +804,9 @@ void MainMenuNetworkView::DrawNatOptions(bool appearing)
|
|||||||
void MainMenuNetworkView::DrawUdpOptions(bool appearing)
|
void MainMenuNetworkView::DrawUdpOptions(bool appearing)
|
||||||
{
|
{
|
||||||
if (appearing) {
|
if (appearing) {
|
||||||
strncpy(remote_addr, g_config.net.udp.remote_addr, sizeof(remote_addr)-1);
|
strncpy(remote_addr, g_config.net.udp.remote_addr,
|
||||||
strncpy(local_addr, g_config.net.udp.bind_addr, sizeof(local_addr)-1);
|
sizeof(remote_addr) - 1);
|
||||||
|
strncpy(local_addr, g_config.net.udp.bind_addr, sizeof(local_addr) - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
float size_ratio = 0.5;
|
float size_ratio = 0.5;
|
||||||
@ -663,7 +843,9 @@ MainMenuSnapshotsView::~MainMenuSnapshotsView()
|
|||||||
g_free(m_search_regex);
|
g_free(m_search_regex);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MainMenuSnapshotsView::BigSnapshotButton(QEMUSnapshotInfo *snapshot, XemuSnapshotData *data, int current_snapshot_binding)
|
bool MainMenuSnapshotsView::BigSnapshotButton(QEMUSnapshotInfo *snapshot,
|
||||||
|
XemuSnapshotData *data,
|
||||||
|
int current_snapshot_binding)
|
||||||
{
|
{
|
||||||
ImGuiStyle &style = ImGui::GetStyle();
|
ImGuiStyle &style = ImGui::GetStyle();
|
||||||
ImDrawList *draw_list = ImGui::GetWindowDrawList();
|
ImDrawList *draw_list = ImGui::GetWindowDrawList();
|
||||||
@ -673,18 +855,27 @@ bool MainMenuSnapshotsView::BigSnapshotButton(QEMUSnapshotInfo *snapshot, XemuSn
|
|||||||
ImGui::PopFont();
|
ImGui::PopFont();
|
||||||
|
|
||||||
ImGui::PushStyleVar(ImGuiStyleVar_ButtonTextAlign, ImVec2(0, 0));
|
ImGui::PushStyleVar(ImGuiStyleVar_ButtonTextAlign, ImVec2(0, 0));
|
||||||
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, g_viewport_mgr.Scale(ImVec2(5, 5)));
|
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding,
|
||||||
|
g_viewport_mgr.Scale(ImVec2(5, 5)));
|
||||||
|
|
||||||
ImGui::PushFont(g_font_mgr.m_menu_font_medium);
|
ImGui::PushFont(g_font_mgr.m_menu_font_medium);
|
||||||
|
|
||||||
ImVec2 ts_title = ImGui::CalcTextSize(snapshot->name);
|
ImVec2 ts_title = ImGui::CalcTextSize(snapshot->name);
|
||||||
ImVec2 thumbnail_size = g_viewport_mgr.Scale(ImVec2(XEMU_SNAPSHOT_THUMBNAIL_WIDTH, XEMU_SNAPSHOT_THUMBNAIL_HEIGHT));
|
ImVec2 thumbnail_size = g_viewport_mgr.Scale(
|
||||||
|
ImVec2(XEMU_SNAPSHOT_THUMBNAIL_WIDTH, XEMU_SNAPSHOT_THUMBNAIL_HEIGHT));
|
||||||
ImVec2 thumbnail_pos(style.FramePadding.x, style.FramePadding.y);
|
ImVec2 thumbnail_pos(style.FramePadding.x, style.FramePadding.y);
|
||||||
ImVec2 name_pos(thumbnail_pos.x + thumbnail_size.x + style.FramePadding.x * 2, thumbnail_pos.y);
|
ImVec2 name_pos(thumbnail_pos.x + thumbnail_size.x +
|
||||||
ImVec2 title_pos(name_pos.x, name_pos.y + ts_title.y + style.FramePadding.x);
|
style.FramePadding.x * 2,
|
||||||
ImVec2 date_pos(name_pos.x, title_pos.y + ts_title.y + style.FramePadding.x);
|
thumbnail_pos.y);
|
||||||
ImVec2 binding_pos(name_pos.x, date_pos.y + ts_title.y + style.FramePadding.x);
|
ImVec2 title_pos(name_pos.x,
|
||||||
ImVec2 button_size(-FLT_MIN, fmax(thumbnail_size.y + style.FramePadding.y * 2, ts_title.y + ts_sub.y + style.FramePadding.y * 3));
|
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", button_size);
|
bool load = ImGui::Button("###button", button_size);
|
||||||
|
|
||||||
@ -699,42 +890,55 @@ bool MainMenuSnapshotsView::BigSnapshotButton(QEMUSnapshotInfo *snapshot, XemuSn
|
|||||||
int thumbnail_width, thumbnail_height;
|
int thumbnail_width, thumbnail_height;
|
||||||
glActiveTexture(GL_TEXTURE0);
|
glActiveTexture(GL_TEXTURE0);
|
||||||
glBindTexture(GL_TEXTURE_2D, thumbnail);
|
glBindTexture(GL_TEXTURE_2D, thumbnail);
|
||||||
glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &thumbnail_width);
|
glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH,
|
||||||
glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &thumbnail_height);
|
&thumbnail_width);
|
||||||
|
glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT,
|
||||||
|
&thumbnail_height);
|
||||||
|
|
||||||
// Draw black background behind thumbnail
|
// Draw black background behind thumbnail
|
||||||
ImVec2 thumbnail_min(p0.x + thumbnail_pos.x, p0.y + thumbnail_pos.y);
|
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);
|
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_list->AddRectFilled(thumbnail_min, thumbnail_max, IM_COL32_BLACK);
|
||||||
|
|
||||||
// Draw centered thumbnail image
|
// Draw centered thumbnail image
|
||||||
int scaled_width, scaled_height;
|
int scaled_width, scaled_height;
|
||||||
ScaleDimensions(thumbnail_width, thumbnail_height, thumbnail_size.x, thumbnail_size.y, &scaled_width, &scaled_height);
|
ScaleDimensions(thumbnail_width, thumbnail_height, thumbnail_size.x,
|
||||||
ImVec2 img_min = ImVec2(thumbnail_min.x + (thumbnail_size.x - scaled_width) / 2,
|
thumbnail_size.y, &scaled_width, &scaled_height);
|
||||||
thumbnail_min.y + (thumbnail_size.y - scaled_height) / 2);
|
ImVec2 img_min =
|
||||||
ImVec2 img_max = ImVec2(img_min.x + scaled_width, img_min.y + scaled_height);
|
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);
|
draw_list->AddImage((ImTextureID)(uint64_t)thumbnail, img_min, img_max);
|
||||||
|
|
||||||
// Snapshot title
|
// Snapshot title
|
||||||
ImGui::PushFont(g_font_mgr.m_menu_font_medium);
|
ImGui::PushFont(g_font_mgr.m_menu_font_medium);
|
||||||
draw_list->AddText(ImVec2(p0.x + name_pos.x, p0.y + name_pos.y), IM_COL32(255, 255, 255, 255), snapshot->name);
|
draw_list->AddText(ImVec2(p0.x + name_pos.x, p0.y + name_pos.y),
|
||||||
|
IM_COL32(255, 255, 255, 255), snapshot->name);
|
||||||
ImGui::PopFont();
|
ImGui::PopFont();
|
||||||
|
|
||||||
// Snapshot XBE title name
|
// Snapshot XBE title name
|
||||||
ImGui::PushFont(g_font_mgr.m_menu_font_small);
|
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)";
|
const char *title_name = data->xbe_title_name ? data->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);
|
"(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
|
// Snapshot date
|
||||||
g_autoptr(GDateTime) date = g_date_time_new_from_unix_local(snapshot->date_sec);
|
g_autoptr(GDateTime) date =
|
||||||
|
g_date_time_new_from_unix_local(snapshot->date_sec);
|
||||||
char *date_buf = g_date_time_format(date, "%Y-%m-%d %H:%M:%S");
|
char *date_buf = g_date_time_format(date, "%Y-%m-%d %H:%M:%S");
|
||||||
draw_list->AddText(ImVec2(p0.x + date_pos.x, p0.y + date_pos.y), IM_COL32(255, 255, 255, 200), date_buf);
|
draw_list->AddText(ImVec2(p0.x + date_pos.x, p0.y + date_pos.y),
|
||||||
|
IM_COL32(255, 255, 255, 200), date_buf);
|
||||||
g_free(date_buf);
|
g_free(date_buf);
|
||||||
|
|
||||||
// Snapshot keyboard binding
|
// Snapshot keyboard binding
|
||||||
if (current_snapshot_binding != -1) {
|
if (current_snapshot_binding != -1) {
|
||||||
char *binding_text = g_strdup_printf("Bound to F%d", current_snapshot_binding + 5);
|
char *binding_text =
|
||||||
draw_list->AddText(ImVec2(p0.x + binding_pos.x, p0.y + binding_pos.y), IM_COL32(255, 255, 255, 200), binding_text);
|
g_strdup_printf("Bound to F%d", current_snapshot_binding + 5);
|
||||||
|
draw_list->AddText(ImVec2(p0.x + binding_pos.x, p0.y + binding_pos.y),
|
||||||
|
IM_COL32(255, 255, 255, 200), binding_text);
|
||||||
g_free(binding_text);
|
g_free(binding_text);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -758,7 +962,7 @@ void MainMenuSnapshotsView::ClearSearch()
|
|||||||
int MainMenuSnapshotsView::OnSearchTextUpdate(ImGuiInputTextCallbackData *data)
|
int MainMenuSnapshotsView::OnSearchTextUpdate(ImGuiInputTextCallbackData *data)
|
||||||
{
|
{
|
||||||
GError *gerr = NULL;
|
GError *gerr = NULL;
|
||||||
MainMenuSnapshotsView *win = (MainMenuSnapshotsView*)data->UserData;
|
MainMenuSnapshotsView *win = (MainMenuSnapshotsView *)data->UserData;
|
||||||
|
|
||||||
if (win->m_search_regex) {
|
if (win->m_search_regex) {
|
||||||
g_free(win->m_search_regex);
|
g_free(win->m_search_regex);
|
||||||
@ -770,7 +974,8 @@ int MainMenuSnapshotsView::OnSearchTextUpdate(ImGuiInputTextCallbackData *data)
|
|||||||
}
|
}
|
||||||
|
|
||||||
char *buf = g_strdup_printf("(.*)%s(.*)", data->Buf);
|
char *buf = g_strdup_printf("(.*)%s(.*)", data->Buf);
|
||||||
win->m_search_regex = g_regex_new(buf, (GRegexCompileFlags)0, (GRegexMatchFlags)0, &gerr);
|
win->m_search_regex =
|
||||||
|
g_regex_new(buf, (GRegexCompileFlags)0, (GRegexMatchFlags)0, &gerr);
|
||||||
g_free(buf);
|
g_free(buf);
|
||||||
if (gerr) {
|
if (gerr) {
|
||||||
win->m_search_regex = NULL;
|
win->m_search_regex = NULL;
|
||||||
@ -785,14 +990,17 @@ void MainMenuSnapshotsView::Draw()
|
|||||||
g_snapshot_mgr.Refresh();
|
g_snapshot_mgr.Refresh();
|
||||||
|
|
||||||
SectionTitle("Snapshots");
|
SectionTitle("Snapshots");
|
||||||
Toggle("Filter by current title", &g_config.general.snapshots.filter_current_game,
|
Toggle("Filter by current title",
|
||||||
"Only display snapshots created while running the currently running XBE");
|
&g_config.general.snapshots.filter_current_game,
|
||||||
|
"Only display snapshots created while running the currently running "
|
||||||
|
"XBE");
|
||||||
|
|
||||||
if (g_config.general.snapshots.filter_current_game) {
|
if (g_config.general.snapshots.filter_current_game) {
|
||||||
struct xbe *xbe = xemu_get_xbe_info();
|
struct xbe *xbe = xemu_get_xbe_info();
|
||||||
if (xbe && xbe->cert) {
|
if (xbe && xbe->cert) {
|
||||||
if (xbe->cert->m_titleid != m_current_title_id) {
|
if (xbe->cert->m_titleid != m_current_title_id) {
|
||||||
char *title_name = g_utf16_to_utf8(xbe->cert->m_title_name, 40, NULL, NULL, NULL);
|
char *title_name = g_utf16_to_utf8(xbe->cert->m_title_name, 40,
|
||||||
|
NULL, NULL, NULL);
|
||||||
if (title_name) {
|
if (title_name) {
|
||||||
m_current_title_name = title_name;
|
m_current_title_name = title_name;
|
||||||
g_free(title_name);
|
g_free(title_name);
|
||||||
@ -816,7 +1024,8 @@ void MainMenuSnapshotsView::Draw()
|
|||||||
|
|
||||||
bool snapshot_with_create_name_exists = false;
|
bool snapshot_with_create_name_exists = false;
|
||||||
for (int i = 0; i < g_snapshot_mgr.m_snapshots_len; ++i) {
|
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) {
|
if (g_strcmp0(m_search_buf.c_str(),
|
||||||
|
g_snapshot_mgr.m_snapshots[i].name) == 0) {
|
||||||
snapshot_with_create_name_exists = true;
|
snapshot_with_create_name_exists = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -828,8 +1037,10 @@ void MainMenuSnapshotsView::Draw()
|
|||||||
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(1, 0, 0, 1));
|
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(1, 0, 0, 1));
|
||||||
ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(1, 0, 0, 1));
|
ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(1, 0, 0, 1));
|
||||||
}
|
}
|
||||||
if (ImGui::Button(snapshot_with_create_name_exists ? "Replace" : "Create", ImVec2(-FLT_MIN, 0))) {
|
if (ImGui::Button(snapshot_with_create_name_exists ? "Replace" : "Create",
|
||||||
xemu_snapshots_save(m_search_buf.empty() ? NULL : m_search_buf.c_str(), NULL);
|
ImVec2(-FLT_MIN, 0))) {
|
||||||
|
xemu_snapshots_save(m_search_buf.empty() ? NULL : m_search_buf.c_str(),
|
||||||
|
NULL);
|
||||||
ClearSearch();
|
ClearSearch();
|
||||||
}
|
}
|
||||||
if (snapshot_with_create_name_exists) {
|
if (snapshot_with_create_name_exists) {
|
||||||
@ -837,28 +1048,36 @@ void MainMenuSnapshotsView::Draw()
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (snapshot_with_create_name_exists && ImGui::IsItemHovered()) {
|
if (snapshot_with_create_name_exists && ImGui::IsItemHovered()) {
|
||||||
ImGui::SetTooltip("A snapshot with the name \"%s\" already exists. This button will overwrite the existing snapshot.", m_search_buf.c_str());
|
ImGui::SetTooltip("A snapshot with the name \"%s\" already exists. "
|
||||||
|
"This button will overwrite the existing snapshot.",
|
||||||
|
m_search_buf.c_str());
|
||||||
}
|
}
|
||||||
ImGui::PopFont();
|
ImGui::PopFont();
|
||||||
|
|
||||||
bool at_least_one_snapshot_displayed = false;
|
bool at_least_one_snapshot_displayed = false;
|
||||||
|
|
||||||
for (int i = g_snapshot_mgr.m_snapshots_len - 1; i >= 0; i--) {
|
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 &&
|
if (g_config.general.snapshots.filter_current_game &&
|
||||||
m_current_title_name.size() && strcmp(m_current_title_name.c_str(), g_snapshot_mgr.m_extra_data[i].xbe_title_name)) {
|
g_snapshot_mgr.m_extra_data[i].xbe_title_name &&
|
||||||
|
m_current_title_name.size() &&
|
||||||
|
strcmp(m_current_title_name.c_str(),
|
||||||
|
g_snapshot_mgr.m_extra_data[i].xbe_title_name)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_search_regex) {
|
if (m_search_regex) {
|
||||||
GMatchInfo *match;
|
GMatchInfo *match;
|
||||||
bool keep_entry = false;
|
bool keep_entry = false;
|
||||||
|
|
||||||
g_regex_match(m_search_regex, g_snapshot_mgr.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);
|
keep_entry |= g_match_info_matches(match);
|
||||||
g_match_info_free(match);
|
g_match_info_free(match);
|
||||||
|
|
||||||
if (g_snapshot_mgr.m_extra_data[i].xbe_title_name) {
|
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);
|
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);
|
keep_entry |= g_match_info_matches(match);
|
||||||
g_free(match);
|
g_free(match);
|
||||||
}
|
}
|
||||||
@ -873,7 +1092,8 @@ void MainMenuSnapshotsView::Draw()
|
|||||||
|
|
||||||
int current_snapshot_binding = -1;
|
int current_snapshot_binding = -1;
|
||||||
for (int i = 0; i < 4; ++i) {
|
for (int i = 0; i < 4; ++i) {
|
||||||
if (g_strcmp0(*(g_snapshot_shortcut_index_key_map[i]), snapshot->name) == 0) {
|
if (g_strcmp0(*(g_snapshot_shortcut_index_key_map[i]),
|
||||||
|
snapshot->name) == 0) {
|
||||||
assert(current_snapshot_binding == -1);
|
assert(current_snapshot_binding == -1);
|
||||||
current_snapshot_binding = i;
|
current_snapshot_binding = i;
|
||||||
}
|
}
|
||||||
@ -885,13 +1105,14 @@ void MainMenuSnapshotsView::Draw()
|
|||||||
bool load = BigSnapshotButton(snapshot, data, current_snapshot_binding);
|
bool load = BigSnapshotButton(snapshot, data, current_snapshot_binding);
|
||||||
|
|
||||||
// FIXME: Provide context menu control annotation
|
// FIXME: Provide context menu control annotation
|
||||||
if (ImGui::IsItemHovered() && ImGui::IsKeyPressed(ImGuiKey_GamepadFaceLeft)) {
|
if (ImGui::IsItemHovered() &&
|
||||||
|
ImGui::IsKeyPressed(ImGuiKey_GamepadFaceLeft)) {
|
||||||
ImGui::SetNextWindowPos(pos);
|
ImGui::SetNextWindowPos(pos);
|
||||||
ImGui::OpenPopup("Snapshot Options");
|
ImGui::OpenPopup("Snapshot Options");
|
||||||
}
|
}
|
||||||
|
|
||||||
DrawSnapshotContextMenu(snapshot, data, current_snapshot_binding);
|
DrawSnapshotContextMenu(snapshot, data, current_snapshot_binding);
|
||||||
|
|
||||||
ImGui::PopID();
|
ImGui::PopID();
|
||||||
|
|
||||||
if (load) {
|
if (load) {
|
||||||
@ -915,12 +1136,14 @@ void MainMenuSnapshotsView::Draw()
|
|||||||
}
|
}
|
||||||
ImVec2 dim = ImGui::CalcTextSize(msg);
|
ImVec2 dim = ImGui::CalcTextSize(msg);
|
||||||
ImVec2 cur = ImGui::GetCursorPos();
|
ImVec2 cur = ImGui::GetCursorPos();
|
||||||
ImGui::SetCursorPosX(cur.x + (ImGui::GetColumnWidth()-dim.x)/2);
|
ImGui::SetCursorPosX(cur.x + (ImGui::GetColumnWidth() - dim.x) / 2);
|
||||||
ImGui::TextColored(ImVec4(0.94f, 0.94f, 0.94f, 0.70f), "%s", msg);
|
ImGui::TextColored(ImVec4(0.94f, 0.94f, 0.94f, 0.70f), "%s", msg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainMenuSnapshotsView::DrawSnapshotContextMenu(QEMUSnapshotInfo *snapshot, XemuSnapshotData *data, int current_snapshot_binding)
|
void MainMenuSnapshotsView::DrawSnapshotContextMenu(
|
||||||
|
QEMUSnapshotInfo *snapshot, XemuSnapshotData *data,
|
||||||
|
int current_snapshot_binding)
|
||||||
{
|
{
|
||||||
if (!ImGui::BeginPopupContextItem("Snapshot Options")) {
|
if (!ImGui::BeginPopupContextItem("Snapshot Options")) {
|
||||||
return;
|
return;
|
||||||
@ -936,9 +1159,12 @@ void MainMenuSnapshotsView::DrawSnapshotContextMenu(QEMUSnapshotInfo *snapshot,
|
|||||||
|
|
||||||
if (ImGui::MenuItem(item_name)) {
|
if (ImGui::MenuItem(item_name)) {
|
||||||
if (current_snapshot_binding >= 0) {
|
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
|
||||||
|
[current_snapshot_binding],
|
||||||
|
"");
|
||||||
}
|
}
|
||||||
xemu_settings_set_string(g_snapshot_shortcut_index_key_map[i], snapshot->name);
|
xemu_settings_set_string(g_snapshot_shortcut_index_key_map[i],
|
||||||
|
snapshot->name);
|
||||||
current_snapshot_binding = i;
|
current_snapshot_binding = i;
|
||||||
|
|
||||||
ImGui::CloseCurrentPopup();
|
ImGui::CloseCurrentPopup();
|
||||||
@ -949,13 +1175,15 @@ void MainMenuSnapshotsView::DrawSnapshotContextMenu(QEMUSnapshotInfo *snapshot,
|
|||||||
|
|
||||||
if (current_snapshot_binding >= 0) {
|
if (current_snapshot_binding >= 0) {
|
||||||
if (ImGui::MenuItem("Unbind")) {
|
if (ImGui::MenuItem("Unbind")) {
|
||||||
xemu_settings_set_string(g_snapshot_shortcut_index_key_map[current_snapshot_binding], "");
|
xemu_settings_set_string(
|
||||||
|
g_snapshot_shortcut_index_key_map[current_snapshot_binding],
|
||||||
|
"");
|
||||||
current_snapshot_binding = -1;
|
current_snapshot_binding = -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ImGui::EndMenu();
|
ImGui::EndMenu();
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui::Separator();
|
ImGui::Separator();
|
||||||
|
|
||||||
Error *err = NULL;
|
Error *err = NULL;
|
||||||
@ -982,11 +1210,13 @@ MainMenuSystemView::MainMenuSystemView() : m_dirty(false)
|
|||||||
|
|
||||||
void MainMenuSystemView::Draw()
|
void MainMenuSystemView::Draw()
|
||||||
{
|
{
|
||||||
const char *rom_file_filters = ".bin Files\0*.bin\0.rom Files\0*.rom\0All Files\0*.*\0";
|
const char *rom_file_filters =
|
||||||
|
".bin Files\0*.bin\0.rom Files\0*.rom\0All Files\0*.*\0";
|
||||||
const char *qcow_file_filters = ".qcow2 Files\0*.qcow2\0All Files\0*.*\0";
|
const char *qcow_file_filters = ".qcow2 Files\0*.qcow2\0All Files\0*.*\0";
|
||||||
|
|
||||||
if (m_dirty) {
|
if (m_dirty) {
|
||||||
ImGui::TextColored(ImVec4(1,0,0,1), "Application restart required to apply settings");
|
ImGui::TextColored(ImVec4(1, 0, 0, 1),
|
||||||
|
"Application restart required to apply settings");
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((int)g_config.sys.avpack == CONFIG_SYS_AVPACK_NONE) {
|
if ((int)g_config.sys.avpack == CONFIG_SYS_AVPACK_NONE) {
|
||||||
@ -997,7 +1227,8 @@ void MainMenuSystemView::Draw()
|
|||||||
|
|
||||||
if (ChevronCombo(
|
if (ChevronCombo(
|
||||||
"System Memory", &g_config.sys.mem_limit,
|
"System Memory", &g_config.sys.mem_limit,
|
||||||
"64 MiB (Default)\0""128 MiB\0",
|
"64 MiB (Default)\0"
|
||||||
|
"128 MiB\0",
|
||||||
"Increase to 128 MiB for debug or homebrew applications")) {
|
"Increase to 128 MiB for debug or homebrew applications")) {
|
||||||
m_dirty = true;
|
m_dirty = true;
|
||||||
}
|
}
|
||||||
@ -1030,8 +1261,9 @@ void MainMenuSystemView::Draw()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MainMenuAboutView::MainMenuAboutView(): m_config_info_text{NULL}
|
MainMenuAboutView::MainMenuAboutView() : m_config_info_text{ NULL }
|
||||||
{}
|
{
|
||||||
|
}
|
||||||
|
|
||||||
void MainMenuAboutView::UpdateConfigInfoText()
|
void MainMenuAboutView::UpdateConfigInfoText()
|
||||||
{
|
{
|
||||||
@ -1039,20 +1271,21 @@ void MainMenuAboutView::UpdateConfigInfoText()
|
|||||||
g_free(m_config_info_text);
|
g_free(m_config_info_text);
|
||||||
}
|
}
|
||||||
|
|
||||||
gchar *bootrom_checksum = GetFileMD5Checksum(g_config.sys.files.bootrom_path);
|
gchar *bootrom_checksum =
|
||||||
|
GetFileMD5Checksum(g_config.sys.files.bootrom_path);
|
||||||
if (!bootrom_checksum) {
|
if (!bootrom_checksum) {
|
||||||
bootrom_checksum = g_strdup("None");
|
bootrom_checksum = g_strdup("None");
|
||||||
}
|
}
|
||||||
|
|
||||||
gchar *flash_rom_checksum = GetFileMD5Checksum(g_config.sys.files.flashrom_path);
|
gchar *flash_rom_checksum =
|
||||||
|
GetFileMD5Checksum(g_config.sys.files.flashrom_path);
|
||||||
if (!flash_rom_checksum) {
|
if (!flash_rom_checksum) {
|
||||||
flash_rom_checksum = g_strdup("None");
|
flash_rom_checksum = g_strdup("None");
|
||||||
}
|
}
|
||||||
|
|
||||||
m_config_info_text = g_strdup_printf(
|
m_config_info_text = g_strdup_printf("MCPX Boot ROM MD5 Hash: %s\n"
|
||||||
"MCPX Boot ROM MD5 Hash: %s\n"
|
"Flash ROM (BIOS) MD5 Hash: %s",
|
||||||
"Flash ROM (BIOS) MD5 Hash: %s",
|
bootrom_checksum, flash_rom_checksum);
|
||||||
bootrom_checksum, flash_rom_checksum);
|
|
||||||
g_free(bootrom_checksum);
|
g_free(bootrom_checksum);
|
||||||
g_free(flash_rom_checksum);
|
g_free(flash_rom_checksum);
|
||||||
}
|
}
|
||||||
@ -1061,22 +1294,25 @@ void MainMenuAboutView::Draw()
|
|||||||
{
|
{
|
||||||
static const char *build_info_text = NULL;
|
static const char *build_info_text = NULL;
|
||||||
if (build_info_text == NULL) {
|
if (build_info_text == NULL) {
|
||||||
build_info_text = g_strdup_printf(
|
build_info_text =
|
||||||
"Version: %s\nBranch: %s\nCommit: %s\nDate: %s",
|
g_strdup_printf("Version: %s\nBranch: %s\nCommit: "
|
||||||
xemu_version, xemu_branch, xemu_commit, xemu_date);
|
"%s\nDate: %s",
|
||||||
|
xemu_version, xemu_branch, xemu_commit, xemu_date);
|
||||||
}
|
}
|
||||||
|
|
||||||
static const char *sys_info_text = NULL;
|
static const char *sys_info_text = NULL;
|
||||||
if (sys_info_text == NULL) {
|
if (sys_info_text == NULL) {
|
||||||
const char *gl_shader_version = (const char*)glGetString(GL_SHADING_LANGUAGE_VERSION);
|
const char *gl_shader_version =
|
||||||
const char *gl_version = (const char*)glGetString(GL_VERSION);
|
(const char *)glGetString(GL_SHADING_LANGUAGE_VERSION);
|
||||||
const char *gl_renderer = (const char*)glGetString(GL_RENDERER);
|
const char *gl_version = (const char *)glGetString(GL_VERSION);
|
||||||
const char *gl_vendor = (const char*)glGetString(GL_VENDOR);
|
const char *gl_renderer = (const char *)glGetString(GL_RENDERER);
|
||||||
|
const char *gl_vendor = (const char *)glGetString(GL_VENDOR);
|
||||||
sys_info_text = g_strdup_printf(
|
sys_info_text = g_strdup_printf(
|
||||||
"CPU: %s\nOS Platform: %s\nOS Version: %s\nManufacturer: %s\n"
|
"CPU: %s\nOS Platform: %s\nOS Version: "
|
||||||
|
"%s\nManufacturer: %s\n"
|
||||||
"GPU Model: %s\nDriver: %s\nShader: %s",
|
"GPU Model: %s\nDriver: %s\nShader: %s",
|
||||||
xemu_get_cpu_info(), xemu_get_os_platform(), xemu_get_os_info(), gl_vendor,
|
xemu_get_cpu_info(), xemu_get_os_platform(), xemu_get_os_info(),
|
||||||
gl_renderer, gl_version, gl_shader_version);
|
gl_vendor, gl_renderer, gl_version, gl_shader_version);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_config_info_text == NULL) {
|
if (m_config_info_text == NULL) {
|
||||||
@ -1121,7 +1357,7 @@ void MainMenuAboutView::Draw()
|
|||||||
}
|
}
|
||||||
|
|
||||||
MainMenuTabButton::MainMenuTabButton(std::string text, std::string icon)
|
MainMenuTabButton::MainMenuTabButton(std::string text, std::string icon)
|
||||||
: m_icon(icon), m_text(text)
|
: m_icon(icon), m_text(text)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1134,8 +1370,10 @@ bool MainMenuTabButton::Draw(bool selected)
|
|||||||
IM_COL32(0, 0, 0, 0);
|
IM_COL32(0, 0, 0, 0);
|
||||||
|
|
||||||
ImGui::PushStyleColor(ImGuiCol_Button, col);
|
ImGui::PushStyleColor(ImGuiCol_Button, col);
|
||||||
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, selected ? col : IM_COL32(32, 32, 32, 255));
|
ImGui::PushStyleColor(ImGuiCol_ButtonHovered,
|
||||||
ImGui::PushStyleColor(ImGuiCol_ButtonActive, selected ? col : IM_COL32(32, 32, 32, 255));
|
selected ? col : IM_COL32(32, 32, 32, 255));
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_ButtonActive,
|
||||||
|
selected ? col : IM_COL32(32, 32, 32, 255));
|
||||||
int p = ImGui::GetTextLineHeight() * 0.5;
|
int p = ImGui::GetTextLineHeight() * 0.5;
|
||||||
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(p, p));
|
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(p, p));
|
||||||
ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 0);
|
ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 0);
|
||||||
@ -1154,15 +1392,14 @@ bool MainMenuTabButton::Draw(bool selected)
|
|||||||
}
|
}
|
||||||
|
|
||||||
MainMenuScene::MainMenuScene()
|
MainMenuScene::MainMenuScene()
|
||||||
: m_animation(0.12, 0.12),
|
: m_animation(0.12, 0.12), m_general_button("General", ICON_FA_GEARS),
|
||||||
m_general_button("General", ICON_FA_GEARS),
|
m_input_button("Input", ICON_FA_GAMEPAD),
|
||||||
m_input_button("Input", ICON_FA_GAMEPAD),
|
m_display_button("Display", ICON_FA_TV),
|
||||||
m_display_button("Display", ICON_FA_TV),
|
m_audio_button("Audio", ICON_FA_VOLUME_HIGH),
|
||||||
m_audio_button("Audio", ICON_FA_VOLUME_HIGH),
|
m_network_button("Network", ICON_FA_NETWORK_WIRED),
|
||||||
m_network_button("Network", ICON_FA_NETWORK_WIRED),
|
m_snapshots_button("Snapshots", ICON_FA_CLOCK_ROTATE_LEFT),
|
||||||
m_snapshots_button("Snapshots", ICON_FA_CLOCK_ROTATE_LEFT),
|
m_system_button("System", ICON_FA_MICROCHIP),
|
||||||
m_system_button("System", ICON_FA_MICROCHIP),
|
m_about_button("About", ICON_FA_CIRCLE_INFO)
|
||||||
m_about_button("About", ICON_FA_CIRCLE_INFO)
|
|
||||||
{
|
{
|
||||||
m_had_focus_last_frame = false;
|
m_had_focus_last_frame = false;
|
||||||
m_focus_view = false;
|
m_focus_view = false;
|
||||||
@ -1249,11 +1486,12 @@ void MainMenuScene::HandleInput()
|
|||||||
bool focus = ImGui::IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows |
|
bool focus = ImGui::IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows |
|
||||||
ImGuiFocusedFlags_NoPopupHierarchy);
|
ImGuiFocusedFlags_NoPopupHierarchy);
|
||||||
|
|
||||||
// XXX: Ensure we have focus for two frames. If a user cancels a popup window, we do not want to cancel main
|
// XXX: Ensure we have focus for two frames. If a user cancels a popup
|
||||||
|
// window, we do not want to cancel main
|
||||||
// window as well.
|
// window as well.
|
||||||
if (nofocus || (focus && m_had_focus_last_frame &&
|
if (nofocus || (focus && m_had_focus_last_frame &&
|
||||||
(ImGui::IsKeyDown(ImGuiKey_GamepadFaceRight)
|
(ImGui::IsKeyDown(ImGuiKey_GamepadFaceRight) ||
|
||||||
|| ImGui::IsKeyDown(ImGuiKey_Escape)))) {
|
ImGui::IsKeyDown(ImGuiKey_Escape)))) {
|
||||||
Hide();
|
Hide();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -1313,9 +1551,10 @@ bool MainMenuScene::Draw()
|
|||||||
float nav_width = width * 0.3;
|
float nav_width = width * 0.3;
|
||||||
float content_width = width - nav_width;
|
float content_width = width - nav_width;
|
||||||
|
|
||||||
ImGui::PushStyleColor(ImGuiCol_ChildBg, IM_COL32(26,26,26,255));
|
ImGui::PushStyleColor(ImGuiCol_ChildBg, IM_COL32(26, 26, 26, 255));
|
||||||
|
|
||||||
ImGui::BeginChild("###MainWindowNav", ImVec2(nav_width, -1), true, ImGuiWindowFlags_NavFlattened);
|
ImGui::BeginChild("###MainWindowNav", ImVec2(nav_width, -1), true,
|
||||||
|
ImGuiWindowFlags_NavFlattened);
|
||||||
|
|
||||||
bool move_focus_to_tab = false;
|
bool move_focus_to_tab = false;
|
||||||
if (m_current_view_index != m_next_view_index) {
|
if (m_current_view_index != m_next_view_index) {
|
||||||
@ -1349,7 +1588,8 @@ bool MainMenuScene::Draw()
|
|||||||
int s = ImGui::GetTextLineHeight() * 0.75;
|
int s = ImGui::GetTextLineHeight() * 0.75;
|
||||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(s, s));
|
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(s, s));
|
||||||
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(s, s));
|
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(s, s));
|
||||||
ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 6*g_viewport_mgr.m_scale);
|
ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding,
|
||||||
|
6 * g_viewport_mgr.m_scale);
|
||||||
|
|
||||||
ImGui::PushID(m_current_view_index);
|
ImGui::PushID(m_current_view_index);
|
||||||
ImGui::BeginChild("###MainWindowContent", ImVec2(content_width, -1),
|
ImGui::BeginChild("###MainWindowContent", ImVec2(content_width, -1),
|
||||||
@ -1364,7 +1604,9 @@ bool MainMenuScene::Draw()
|
|||||||
ImGui::PushStyleColor(ImGuiCol_Text, IM_COL32(255, 255, 255, 128));
|
ImGui::PushStyleColor(ImGuiCol_Text, IM_COL32(255, 255, 255, 128));
|
||||||
ImGui::PushStyleColor(ImGuiCol_Button, IM_COL32_BLACK_TRANS);
|
ImGui::PushStyleColor(ImGuiCol_Button, IM_COL32_BLACK_TRANS);
|
||||||
ImVec2 pos = ImGui::GetCursorPos();
|
ImVec2 pos = ImGui::GetCursorPos();
|
||||||
ImGui::SetCursorPosX(ImGui::GetContentRegionMax().x - style.FramePadding.x * 2.0f - ImGui::GetTextLineHeight());
|
ImGui::SetCursorPosX(ImGui::GetContentRegionMax().x -
|
||||||
|
style.FramePadding.x * 2.0f -
|
||||||
|
ImGui::GetTextLineHeight());
|
||||||
if (ImGui::Button(ICON_FA_XMARK)) {
|
if (ImGui::Button(ICON_FA_XMARK)) {
|
||||||
Hide();
|
Hide();
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user