-
-
Notifications
You must be signed in to change notification settings - Fork 300
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
UI: XMU Settings #1315
UI: XMU Settings #1315
Conversation
Merged recent changes from xemu-project to master
Merged recent changes from remote
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A few more comments, mostly nits
ui/xui/main-menu.cc
Outdated
selectedType = bound_state->PeripheralTypes[i]; | ||
if(selectedType == PERIPHERAL_XMU) { | ||
// We are using the same texture for all buttons, but ImageButton | ||
// uses the texture as a unique ID. Push a new ID now to resolve |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this referring to the ID that is pushed later in this block (as opposed to now)?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes. I've moved this comment to the relevant code.
Updating my master branch to match xemu master
Merged the recent changes from master to xmu-settings2
superblock.unknown1 = 0; | ||
|
||
// Write the fatx superblock. | ||
fwrite(&superblock, sizeof(superblock), 1, fp); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This implementation depends on host byte order being little endian. Can you rework it so it will also work on BE hosts?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have changed the code to use cpu_to_le32(u32) on the unsigned integers that were being written to the superblock. I think that will work but I don't have a BE machine to test on. It doesn't break this functionality on my machine.
ui/thirdparty/fatx/fatx.c
Outdated
|
||
// Fill the rest of the space with zeros | ||
for (unsigned int i = ftell(fp); i < size; i++) | ||
fwrite(&zero, 1, 1, fp); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can avoid this loop and writing a bunch of zeros with seek/truncate, which will create a sparse file (logically, but not physically allocating space for all the zeros)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I changed this to fseek to size - sizeof(unsigned byte), and I just write one byte of 0x00 at the end. I initially tried ftruncate(size) but that didn't do the job.
ui/xemu-input.c
Outdated
xmu->dev = (void*)dev; | ||
|
||
// Unref for eventual cleanup | ||
//g_free(dinfo); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
dinfo is allocated in drive_new by a call to g_malloc0. I don't use it again, but I'm not sure what to do with it. Calling g_free here on dinfo causes a segfault when unmounting the XMU image. I didn't mean to leave this commented.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's tied to the blockdev and freed when the blockdev is deleted. You shouldn't have to do anything
config_spec.yml
Outdated
@@ -25,6 +25,51 @@ input: | |||
port2: string | |||
port3: string | |||
port4: string | |||
peripherals: | |||
port1: | |||
peripheral_type_0: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: The schema could be a little smaller
Some thoughts: genconfig is the config system used in xemu
- genconfig does support arrays, which could simplify this schema a bit, but might complicate the code a little too
- genconfig system has an enum type which would be preferable for config checking, or a string type could be used to identify the device type if necessary, enums in gen-config aren't easily re-used
or if you want to keep it fixed as-is, it could be simplified like:
port2:
peripheral_type_0: integer
peripheral_param_0: string
peripheral_type_1: integer
peripheral_param_1: string
nit: The manual refers to these peripheral slots as "Expansion Slot A" (top) and "Expansion Slot B" (bottom, nearest the cable)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I simplified it as you suggested. I used the word peripheral as it refers to the device plugged into the expansion slot rather than the slot itself, but I can change this if you'd like
ui/xemu-input.c
Outdated
|
||
xmu->filename = strdup(filename); | ||
|
||
const int port_map[4] = {3, 4, 1, 2}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is duplicated with xemu_input_bind
, could you refactor this mapping out so it can be shared by both functions in this file
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I pulled this out into a static const in[] and referenced that in both places
ui/xui/main-menu.cc
Outdated
g_free(msg); | ||
} else { | ||
// Create an 8MB formatted XMU image. We can create smaller or larger ones, but 8 MB is the default | ||
if (create_fatx_image(new_path, 8*1024*1024)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's move this number out to a constant definition and drop the above comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I moved this to a preprocessor definition called DEFAULT_XMU_SIZE
ui/xui/main-menu.cc
Outdated
ImGui::GetCursorPosX() + | ||
(int)((ImGui::GetColumnWidth() - xmu_display_size.x) / 2.0)); | ||
|
||
ImGui::Text("Peripheral %c", 'A' + i); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"Expansion Slot" is the manual terminology
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I changed the labels in the UI to follow the Expansion Slot wording, but I left the variables saying peripheral as they refer to the device rather than the slot in which the device is installed
ui/xui/main-menu.cc
Outdated
if (qemu_access(new_path, F_OK) == 0) { | ||
// The file already exists | ||
char *msg = g_strdup_printf("%s already exists", new_path); | ||
xemu_queue_notification(msg); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's xemu_queue_error_message
for errors
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, I changed over the xemu_queue_notification calls to xemu_queue_error_message where I was using it for error messages.
ui/xui/main-menu.cc
Outdated
g_free((void*)id); | ||
g_free((void*)xmu_port_path); | ||
|
||
ImGui::PushID(i+2); // Add 2 because ID value of i was already used and the for loop iterating on i stops at 2 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should be able to have just the one PushID at the start and PopID at the end of the loop, dropping the middle Pop/Push
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I removed those lines and it doesn't appear to have broken anything
if (qemu_access(new_path, F_OK) == 0) { | ||
// The file already exists | ||
char *msg = g_strdup_printf("%s already exists", new_path); | ||
xemu_queue_error_message(msg); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As an alternative to showing an error message in this way, we can request that the native file dialog prompt users for overwrite by adding the flag NOC_FILE_DIALOG_OVERWRITE_CONFIRMATION
with a bitwise OR above on line 383
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This doesn't appear to work on Windows. I changed it to work this way, but when testing it just overwrites the file without a confirmation and, after looking through the implementation of that function, it doesn't appear that NOC_FILE_DIALOG_OVERWRITE_CONFIRMATION is used in the Windows implementation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh wow, you are right! It looks like it's also missing on macOS, or maybe its the default.
On Windows we will need to support NOC_FILE_DIALOG_OVERWRITE_CONFIRMATION
and if set pass the OFN_OVERWRITEPROMPT
flag in when creating the save prompt. Can you try it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That worked, I committed the change
ui/xui/main-menu.cc
Outdated
xmu_port_path = g_strdup(""); | ||
else | ||
xmu_port_path = g_strdup(xmu->filename); | ||
if (FilePicker(id, &xmu_port_path, img_file_filters)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If the user presses the "X" button on the file picker widget, this will cause xemu to crash because file will be unspecified when binding the xmu. In this case, we can unbind the xmu and eliminate the need for the "Clear Selection" button below
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I made the X button unbind the XMU and clear the filename, and I removed the "Clear Selection" button
ui/xemu-input.c
Outdated
@@ -255,6 +293,39 @@ 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); | |||
|
|||
// Try to bind peripherals back to controller | |||
for(int i = 0; i < 2; i++) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unfortunately binding here wont detect the keyboard binds. We can probably just refactor this code out and add a call in xemu_input_bind
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I refactored the code into a new function called xemu_input_rebind_xmu(int port) but calling it from xemu_input_bind causes xemu to crash when switching between Keyboard and Xbox Controller. This is because unbinding the controller unplugs the XMUs, and trying to bind the XMUs again right after unplugging them causes an error because the file is still in use.
Instead I call this function where the code was originally and also on line 180 just after it auto-binds the keyboard.
ui/xemu-input.c
Outdated
|
||
// Try to bind peripherals back to controller | ||
for(int i = 0; i < 2; i++) { | ||
enum peripheral_type peripheralType = (enum peripheral_type)(*peripheral_types_settings_map[port][i]); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: style
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I renamed this variable to peripheral_type, though that might be confusing because it's the same as its type.
ui/xui/main-menu.cc
Outdated
RenderXmu(x, y, 0x1f1f1f00, 0x0f0f0f00); | ||
} | ||
|
||
ImVec2 cur = ImGui::GetCursorPos(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Set but unused
ui/xui/main-menu.cc
Outdated
ImGui::GetCursorPosX() + | ||
(int)((ImGui::GetColumnWidth() - xmu_display_size.x) / 2.0)); | ||
|
||
ImGui::Text("Expansion Slot %c", 'A' + i); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's drop this extra text
ui/xui/main-menu.cc
Outdated
} | ||
} | ||
|
||
const char *id = g_strdup_printf("MU_%d%c File Path", active + 1, 'A' + i); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's change this to something simpler, like "XMU" or "Image" or something. The FilePicker icon already tells the user that it's a file path
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I changed it from "MU_1A File Path" to "Image"
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please run clang-format
on the code (see https://xemu.app/docs/dev/#contributing for details on how to do that).
There are also a few spots where camelCase
variable naming conventions are used, can you update these to follow xemu's lower_snake_case
convention?
@@ -254,6 +293,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) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need to unbind the XMU and free the peripherals in this path?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure I understand. Are you asking in relation to the SDL_CONTROLLERDEVICEREMOVED event or the xemu_input_rebind_xmu call?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is in relation to the device removed event. When the device is removed, the controller state is freed, so any allocated peripherals will be leaked.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It will unbind the XMU/peripherals when we call xemu_input_bind(iter->bind, NULL, 0)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, I think I see what you mean. It's not the QEMU device that's being leaked, it's the memory for the peripheral state that's being leaked.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unless I'm missing it though, the XmuState
structs themselves are not freed by xemu_input_unbind_xmu
(they are only freed in the UI if the user selects None under the peripheral).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm beginning to think that maybe I should change that. I'm going through and fixing these memory leaks and running out of places where I'm not freeing the XmuState immediately after unbinding it
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I decided to leave the freeing of the XmuState
structs out of the xemu_input_unbind_xmu
function because there are 2 places where I want to unbind the XMU but I don't want to free it: When deselecting the image, and when changing the image. Another option would be to add a boolean argument to the function that lets the caller specify whether or not to free the XmuState
and set the peripheral type to PERIPHERAL_NONE
ui/xemu-input.c
Outdated
|
||
// Unbind any XMUs | ||
for(int i = 0; i < 2; i++) | ||
xemu_input_unbind_xmu(index, i); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need to free the XMU/peripherals in this path?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure if I understand what you're asking.
When qdev_unplug is called in xemu_input_bind to free up the underlying QDEV device, it implicitly frees any peripherals connected to the hub. So, we could just set the peripherals[0]->dev and peripherals[1]->dev to NULL and peripheral_types[0] and peripheral_types[1] to PERIPHERAL_NONE. We can't bind them back again when we plug the new controller in though because qdev_unplug doesn't release the XMU image files immediately.
This gave me an idea though.
It might be useful if, instead of freeing the entire QEMU device when we swap controllers, we just reuse the same unerlying QEMU device and move it to the new ControllerState.
This is what it would look like:
If bound_controllers[index] is not null and state is not null, then we're going to be replacing an existing device with a new device. So, instead of freeing the existing device and creating a new one, we call a swap function that does the following:
- If state is already bound to another port then call xemu_input_bind(state->bound, NULL, 1) to unbind it from the other port.
- Set state->device to bound_controllers[index]->device
- Set state->bound to bound_controllers[index]->bound
- Set bound_controllers[index]->device to NULL
- Iterate over bound_controllers[index]->periperals and bound_controllers[index]->peripheral_types
4a. for each expansion slot, copy the peripheral and peripheral_type from bound_controllers[index] to state, then set bound_controllers[index]->peripherals[expansion_slot] to NULL and set bound_controllers[index]->peripheral_types[expansion_slot] to PERIPHERAL_NONE - Set bound_controllers[index]->bound to -1
- Set bound_controllers[index] to state
- Save the changes. In the config, only the controller should have changed.
Concerns:
In this instance, if we call xemu_input_bind(index, bound_controllers[index], 1) in the current implementation, then it would free the exiting XID device from QEMU and create a new one. If we do that with this proposed implementation without any changes then we'll corrupt the ControllerState for this port. So, a check for bound_controllers[index] == state will need to be done and we'll need to decide to either leave the device alone (which is not what we currently do but which would preserve the state of perpherals), or do what we currently do which is free up the device (and the connected peripherals), and then reconnect the device, losing the peripherals in the process.
Any thoughts on this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Initially I thought this might be another leak like the above case, but on second inspection it might not be since the underlying state isn't freed.
From a technical standpoint I don't see an issue with your idea. From a UX standpoint, the current behavior when switching controllers is similar to switching on real hardware (i.e. unplugging one controller and plugging in another one). I don't suspect most users would care about preserving this, but it does add an extra step if this is wanted behavior.
If you did want to attempt it, I would suggest sending it as a separate patch and getting Matt's opinion on the UX (leaving out the peripheral part of it and adjusting one or the other patch appropriately depending on which lands first).
ui/xemu-input.c
Outdated
// This is an XMU and needs to be bound to this controller | ||
if(qemu_access(param, F_OK) == 0) { | ||
bound_controllers[port]->peripheral_types[i] = peripheral_type; | ||
bound_controllers[port]->peripherals[i] = malloc(sizeof(XmuState)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
malloc
is used to allocate peripherals here, but g_malloc
is used in the UI. They must be consistent.
@@ -460,7 +505,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. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In my testing if you disconnect and reconnect a controller in the input UI the XMU is not rebound. Do we need to rebind peripherals here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is how it behaves when we unplug a device and plug it back in with auto-bind enabled so we should probably do that here as well.
If I remember correctly, this will cause Xemu to crash if the user selects a different controller with one already connected though because QEMU will not have finished releasing the XMU image after freeing the existing controller when it goes to rebind the XMU to the hub created for the new controller. If we want to do this then we might want to consider reusing the QEMU device that's already bound when changing from one controller to another instead freeing it and creating a new one.
// Another peripheral was already bound. | ||
// Unplugging | ||
xemu_input_unbind_xmu(active, i); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need to free bound_state->peripherals[i]
here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, otherwise the existing XmuState will be lost when a new one is allocated
I've created #1572 to track the detach/reattach issue as a followup |
…ect#1315)" This reverts commit 03f40b1. Revert "ui: Warn user about no output when AV Pack set to "None"" This reverts commit 800eb46. Revert "ui: Use only one option for settings window (xemu-project#1122)" This reverts commit b605381.
…ect#1315)" This reverts commit 03f40b1. Revert "ui: Warn user about no output when AV Pack set to "None"" This reverts commit 800eb46. Revert "ui: Use only one option for settings window (xemu-project#1122)" This reverts commit b605381.
Added support for creating and mounting/unmounting XMU images in the Input settings menu.