[Kernel, UI]
threaded UI notifications from emoose Issue 1296 added fixups for hdd detection in more games (wheelman for example) changed filter that stops notification spam
This commit is contained in:
parent
1139ccc30d
commit
fadf52381a
|
@ -14,6 +14,7 @@
|
||||||
#include "xenia/kernel/kernel_state.h"
|
#include "xenia/kernel/kernel_state.h"
|
||||||
#include "xenia/kernel/util/shim_utils.h"
|
#include "xenia/kernel/util/shim_utils.h"
|
||||||
#include "xenia/kernel/xam/xam_private.h"
|
#include "xenia/kernel/xam/xam_private.h"
|
||||||
|
#include "xenia/kernel/xthread.h"
|
||||||
#include "xenia/ui/imgui_dialog.h"
|
#include "xenia/ui/imgui_dialog.h"
|
||||||
#include "xenia/ui/window.h"
|
#include "xenia/ui/window.h"
|
||||||
#include "xenia/xbox.h"
|
#include "xenia/xbox.h"
|
||||||
|
@ -111,16 +112,27 @@ dword_result_t XamShowMessageBoxUI(dword_t user_index, lpwstring_t title_ptr,
|
||||||
buttons.push_back(button);
|
buttons.push_back(button);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
XELOGI(
|
||||||
|
"XamShowMessageBoxUI(%d, %.8X(%S), %.8X(%S), %d, %.8X(%S), %d, %X, %.8X, "
|
||||||
|
"%.8X)",
|
||||||
|
user_index, title_ptr, title.c_str(), text_ptr, text.c_str(),
|
||||||
|
button_count, button_ptrs, all_buttons.c_str(), active_button, flags,
|
||||||
|
result_ptr, overlapped);
|
||||||
|
|
||||||
|
// Set overlapped result to X_ERROR_IO_PENDING
|
||||||
|
if (overlapped) {
|
||||||
|
XOverlappedSetResult((void*)overlapped.host_address(), X_ERROR_IO_PENDING);
|
||||||
|
}
|
||||||
|
|
||||||
// Broadcast XN_SYS_UI = true
|
// Broadcast XN_SYS_UI = true
|
||||||
kernel_state()->BroadcastNotification(0x9, true);
|
kernel_state()->BroadcastNotification(0x9, true);
|
||||||
|
|
||||||
uint32_t chosen_button;
|
|
||||||
if (cvars::headless) {
|
|
||||||
// Auto-pick the focused button.
|
// Auto-pick the focused button.
|
||||||
chosen_button = active_button;
|
//uint32_t chosen_button = active_button;
|
||||||
} else {
|
|
||||||
|
if (!cvars::headless) {
|
||||||
auto display_window = kernel_state()->emulator()->display_window();
|
auto display_window = kernel_state()->emulator()->display_window();
|
||||||
xe::threading::Fence fence;
|
++xam_dialogs_shown_;
|
||||||
display_window->loop()->PostSynchronous([&]() {
|
display_window->loop()->PostSynchronous([&]() {
|
||||||
// TODO(benvanik): setup icon states.
|
// TODO(benvanik): setup icon states.
|
||||||
switch (flags & 0xF) {
|
switch (flags & 0xF) {
|
||||||
|
@ -137,25 +149,57 @@ dword_result_t XamShowMessageBoxUI(dword_t user_index, lpwstring_t title_ptr,
|
||||||
// config.pszMainIcon = TD_INFORMATION_ICON;
|
// config.pszMainIcon = TD_INFORMATION_ICON;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto chosen_button = new uint32_t();
|
||||||
|
auto fence = new xe::threading::Fence();
|
||||||
(new MessageBoxDialog(display_window, title, text, buttons, active_button,
|
(new MessageBoxDialog(display_window, title, text, buttons, active_button,
|
||||||
&chosen_button))
|
chosen_button))
|
||||||
->Then(&fence);
|
->Then(fence);
|
||||||
});
|
|
||||||
++xam_dialogs_shown_;
|
// The function to be run once dialog has finished
|
||||||
fence.Wait();
|
auto ui_fn = [fence, result_ptr, chosen_button, overlapped]() {
|
||||||
|
fence->Wait();
|
||||||
|
delete fence;
|
||||||
--xam_dialogs_shown_;
|
--xam_dialogs_shown_;
|
||||||
|
|
||||||
|
*result_ptr = *chosen_button;
|
||||||
|
delete chosen_button;
|
||||||
|
|
||||||
|
if (overlapped) {
|
||||||
|
// TODO: this will set overlapped's context to ui_threads thread
|
||||||
|
// ID, is that a good idea?
|
||||||
|
kernel_state()->CompleteOverlappedImmediate(overlapped,
|
||||||
|
X_ERROR_SUCCESS);
|
||||||
}
|
}
|
||||||
*result_ptr = chosen_button;
|
|
||||||
|
|
||||||
// Broadcast XN_SYS_UI = false
|
// Broadcast XN_SYS_UI = false
|
||||||
kernel_state()->BroadcastNotification(0x9, false);
|
kernel_state()->BroadcastNotification(0x9, false);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Create a host thread to run the function above
|
||||||
|
auto ui_thread = kernel::object_ref<kernel::XHostThread>(
|
||||||
|
new kernel::XHostThread(kernel_state(), 128 * 1024, 0, ui_fn));
|
||||||
|
ui_thread->set_name("XamShowMessageBoxUI Thread");
|
||||||
|
ui_thread->Create();
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// Auto-pick the focused button.
|
||||||
|
*result_ptr = (uint32_t)active_button;
|
||||||
|
|
||||||
if (overlapped) {
|
if (overlapped) {
|
||||||
kernel_state()->CompleteOverlappedImmediate(overlapped, X_ERROR_SUCCESS);
|
kernel_state()->CompleteOverlappedImmediate(overlapped, X_ERROR_SUCCESS);
|
||||||
return X_ERROR_IO_PENDING;
|
|
||||||
} else {
|
|
||||||
return X_ERROR_SUCCESS;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Broadcast XN_SYS_UI = false
|
||||||
|
kernel_state()->BroadcastNotification(0x9, false);
|
||||||
|
}
|
||||||
|
uint32_t result = X_ERROR_SUCCESS;
|
||||||
|
if (overlapped) {
|
||||||
|
return X_ERROR_IO_PENDING;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
DECLARE_XAM_EXPORT1(XamShowMessageBoxUI, kUI, kImplemented);
|
DECLARE_XAM_EXPORT1(XamShowMessageBoxUI, kUI, kImplemented);
|
||||||
|
|
||||||
|
@ -234,8 +278,13 @@ dword_result_t XamShowKeyboardUI(dword_t user_index, dword_t flags,
|
||||||
lpwstring_t description, lpwstring_t buffer,
|
lpwstring_t description, lpwstring_t buffer,
|
||||||
dword_t buffer_length,
|
dword_t buffer_length,
|
||||||
pointer_t<XAM_OVERLAPPED> overlapped) {
|
pointer_t<XAM_OVERLAPPED> overlapped) {
|
||||||
if (!buffer) {
|
// overlapped should always be set, xam seems to check for this specifically
|
||||||
|
if (!overlapped) {
|
||||||
|
assert_always();
|
||||||
return X_ERROR_INVALID_PARAMETER;
|
return X_ERROR_INVALID_PARAMETER;
|
||||||
|
} else {
|
||||||
|
// Set overlapped result to X_ERROR_IO_PENDING
|
||||||
|
XOverlappedSetResult((void*)overlapped.host_address(), X_ERROR_IO_PENDING);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Broadcast XN_SYS_UI = true
|
// Broadcast XN_SYS_UI = true
|
||||||
|
@ -248,48 +297,77 @@ dword_result_t XamShowKeyboardUI(dword_t user_index, dword_t flags,
|
||||||
xe::store_and_swap<std::wstring>(buffer, default_text.value());
|
xe::store_and_swap<std::wstring>(buffer, default_text.value());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: we should probably setup a thread to complete the overlapped a few
|
||||||
|
// seconds after this returns, to simulate the user taking a few seconds to
|
||||||
|
// enter text
|
||||||
|
if (overlapped) {
|
||||||
|
kernel_state()->CompleteOverlappedImmediate(overlapped, X_ERROR_SUCCESS);
|
||||||
|
}
|
||||||
|
|
||||||
// Broadcast XN_SYS_UI = false
|
// Broadcast XN_SYS_UI = false
|
||||||
kernel_state()->BroadcastNotification(0x9, false);
|
kernel_state()->BroadcastNotification(0x9, false);
|
||||||
|
|
||||||
if (overlapped) {
|
|
||||||
kernel_state()->CompleteOverlappedImmediate(overlapped, X_ERROR_SUCCESS);
|
|
||||||
return X_ERROR_IO_PENDING;
|
return X_ERROR_IO_PENDING;
|
||||||
} else {
|
|
||||||
return X_ERROR_SUCCESS;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::wstring out_text;
|
// Instead of waiting for the keyboard dialog to finish before returning,
|
||||||
|
// we'll create a thread that'll wait for it instead, and return immediately.
|
||||||
|
// This way we can let the game run any "code-to-run-while-UI-is-active" code
|
||||||
|
// that it might need to.
|
||||||
|
|
||||||
|
++xam_dialogs_shown_;
|
||||||
|
|
||||||
auto display_window = kernel_state()->emulator()->display_window();
|
auto display_window = kernel_state()->emulator()->display_window();
|
||||||
xe::threading::Fence fence;
|
|
||||||
display_window->loop()->PostSynchronous([&]() {
|
display_window->loop()->PostSynchronous([&]() {
|
||||||
|
auto out_text = new std::wstring();
|
||||||
|
auto fence = new xe::threading::Fence();
|
||||||
|
|
||||||
|
// Create the dialog
|
||||||
(new KeyboardInputDialog(display_window, title ? title.value() : L"",
|
(new KeyboardInputDialog(display_window, title ? title.value() : L"",
|
||||||
description ? description.value() : L"",
|
description ? description.value() : L"",
|
||||||
default_text ? default_text.value() : L"",
|
default_text ? default_text.value() : L"",
|
||||||
&out_text, buffer_length))
|
out_text, buffer_length))
|
||||||
->Then(&fence);
|
->Then(fence);
|
||||||
});
|
|
||||||
++xam_dialogs_shown_;
|
// The function to be run once dialog has finished
|
||||||
fence.Wait();
|
auto ui_fn = [fence, out_text, buffer, buffer_length, overlapped]() {
|
||||||
|
fence->Wait();
|
||||||
|
delete fence;
|
||||||
--xam_dialogs_shown_;
|
--xam_dialogs_shown_;
|
||||||
|
|
||||||
// Zero the output buffer.
|
// Zero the output buffer.
|
||||||
std::memset(buffer, 0, buffer_length * 2);
|
std::memset(buffer, 0, buffer_length * 2);
|
||||||
|
|
||||||
// Truncate the string.
|
// Copy the string.
|
||||||
out_text = out_text.substr(0, buffer_length - 1);
|
size_t size = buffer_length;
|
||||||
xe::store_and_swap<std::wstring>(buffer, out_text);
|
if (size > out_text->size()) {
|
||||||
|
size = out_text->size();
|
||||||
|
}
|
||||||
|
|
||||||
|
xe::copy_and_swap((wchar_t*)buffer.host_address(), out_text->c_str(),
|
||||||
|
size);
|
||||||
|
delete out_text;
|
||||||
|
|
||||||
|
// TODO: this will set overlapped's context to ui_threads thread ID
|
||||||
|
// is that a good idea?
|
||||||
|
if (overlapped) {
|
||||||
|
kernel_state()->CompleteOverlappedImmediate(overlapped, X_ERROR_SUCCESS);
|
||||||
|
}
|
||||||
|
|
||||||
// Broadcast XN_SYS_UI = false
|
// Broadcast XN_SYS_UI = false
|
||||||
kernel_state()->BroadcastNotification(0x9, false);
|
kernel_state()->BroadcastNotification(0x9, false);
|
||||||
|
|
||||||
if (overlapped) {
|
return 0;
|
||||||
kernel_state()->CompleteOverlappedImmediate(overlapped, X_ERROR_SUCCESS);
|
};
|
||||||
|
|
||||||
|
// Create a host thread to run the function above
|
||||||
|
auto ui_thread = kernel::object_ref<kernel::XHostThread>(
|
||||||
|
new kernel::XHostThread(kernel_state(), 128 * 1024, 0, ui_fn));
|
||||||
|
ui_thread->set_name("XamShowKeyboardUI Thread");
|
||||||
|
ui_thread->Create();
|
||||||
|
});
|
||||||
|
|
||||||
return X_ERROR_IO_PENDING;
|
return X_ERROR_IO_PENDING;
|
||||||
} else {
|
|
||||||
return X_ERROR_SUCCESS;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
DECLARE_XAM_EXPORT1(XamShowKeyboardUI, kUI, kImplemented);
|
DECLARE_XAM_EXPORT1(XamShowKeyboardUI, kUI, kImplemented);
|
||||||
|
|
||||||
|
@ -298,6 +376,19 @@ dword_result_t XamShowDeviceSelectorUI(dword_t user_index, dword_t content_type,
|
||||||
qword_t total_requested,
|
qword_t total_requested,
|
||||||
lpdword_t device_id_ptr,
|
lpdword_t device_id_ptr,
|
||||||
pointer_t<XAM_OVERLAPPED> overlapped) {
|
pointer_t<XAM_OVERLAPPED> overlapped) {
|
||||||
|
|
||||||
|
// Set overlapped to X_ERROR_IO_PENDING
|
||||||
|
if (overlapped) {
|
||||||
|
XOverlappedSetResult((void*)overlapped.host_address(), X_ERROR_IO_PENDING);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Broadcast XN_SYS_UI = true
|
||||||
|
kernel_state()->BroadcastNotification(0x9, true);
|
||||||
|
|
||||||
|
auto ui_fn = [content_type, device_id_ptr, overlapped]() {
|
||||||
|
XELOGI("XamShowDeviceSelectorUI Content_type:(%X) device_id_ptr: %.8X overlapped:(%X)",
|
||||||
|
content_type, device_id_ptr, (bool)overlapped);
|
||||||
|
|
||||||
// NOTE: 0xF00D0000 magic from xam_content.cc
|
// NOTE: 0xF00D0000 magic from xam_content.cc
|
||||||
switch (content_type) {
|
switch (content_type) {
|
||||||
case 1: // save game
|
case 1: // save game
|
||||||
|
@ -315,14 +406,33 @@ dword_result_t XamShowDeviceSelectorUI(dword_t user_index, dword_t content_type,
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (overlapped) {
|
||||||
|
kernel_state()->CompleteOverlappedImmediate(overlapped, X_ERROR_SUCCESS);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sleep for 1 second, act like user is making a choice
|
||||||
|
xe::threading::Sleep(std::chrono::milliseconds(500));
|
||||||
|
|
||||||
// Broadcast XN_SYS_UI = true followed by XN_SYS_UI = false
|
// Broadcast XN_SYS_UI = true followed by XN_SYS_UI = false
|
||||||
kernel_state()->BroadcastNotification(0x9, true);
|
kernel_state()->BroadcastNotification(0x9, true);
|
||||||
kernel_state()->BroadcastNotification(0x9, false);
|
kernel_state()->BroadcastNotification(0x9, false);
|
||||||
|
|
||||||
|
// return 0;
|
||||||
|
return X_ERROR_SUCCESS;
|
||||||
|
};
|
||||||
|
|
||||||
if (overlapped) {
|
if (overlapped) {
|
||||||
kernel_state()->CompleteOverlappedImmediate(overlapped, X_ERROR_SUCCESS);
|
// Create a host thread to run the function above
|
||||||
|
auto ui_thread = kernel::object_ref<kernel::XHostThread>(
|
||||||
|
new kernel::XHostThread(kernel_state(), 128 * 1024, 0, ui_fn));
|
||||||
|
ui_thread->set_name("XamShowDeviceSelectorUI Thread");
|
||||||
|
ui_thread->Create();
|
||||||
|
while (ui_thread->last_error() != X_ERROR_SUCCESS) {
|
||||||
|
xe::threading::Sleep(std::chrono::milliseconds(101));
|
||||||
|
}
|
||||||
return X_ERROR_IO_PENDING;
|
return X_ERROR_IO_PENDING;
|
||||||
} else {
|
} else {
|
||||||
|
ui_fn();
|
||||||
return X_ERROR_SUCCESS;
|
return X_ERROR_SUCCESS;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
|
|
||||||
#include "xenia/base/byte_stream.h"
|
#include "xenia/base/byte_stream.h"
|
||||||
#include "xenia/kernel/kernel_state.h"
|
#include "xenia/kernel/kernel_state.h"
|
||||||
|
#include "xenia/base/logging.h"
|
||||||
|
|
||||||
namespace xe {
|
namespace xe {
|
||||||
namespace kernel {
|
namespace kernel {
|
||||||
|
@ -32,12 +33,17 @@ void XNotifyListener::Initialize(uint64_t mask) {
|
||||||
void XNotifyListener::EnqueueNotification(XNotificationID id, uint32_t data) {
|
void XNotifyListener::EnqueueNotification(XNotificationID id, uint32_t data) {
|
||||||
// Ignore if the notification doesn't match our mask.
|
// Ignore if the notification doesn't match our mask.
|
||||||
// TODO(Gliniak): (confirm) mask 0x01 means accept all
|
// TODO(Gliniak): (confirm) mask 0x01 means accept all
|
||||||
if ((mask_ & ((id >> 25) & 0x3F)) == 0 && mask_ != 0x01) {
|
XELOGI("XnotifyListener::EnqueueNotification( Mask: %.8X ID: %.8X Drop?:(%X) )",
|
||||||
|
mask_, id,
|
||||||
|
((mask_ & ((id >> 25) & 0x3F)) == 0 && (mask_ != 0x01) && ((id >> 5) != 0)));
|
||||||
|
if ((mask_ & ((id >> 25) & 0x3F)) == 0 && (mask_ != 0x01) && ((id >> 5) != 0)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto global_lock = global_critical_region_.Acquire();
|
auto global_lock = global_critical_region_.Acquire();
|
||||||
notifications_.push_back(std::pair<XNotificationID, uint32_t>(id, data));
|
// notifications_.push_back(std::pair<XNotificationID, uint32_t>(id, data));
|
||||||
|
// TEST
|
||||||
|
notifications_.emplace_back(id, data);
|
||||||
wait_handle_->Set();
|
wait_handle_->Set();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue