parent
5c7f42e9d1
commit
baa86fcd1b
|
@ -70,7 +70,7 @@ bool EmulatorWindow::Initialize() {
|
|||
window_->on_key_down.AddListener([this](KeyEvent& e) {
|
||||
bool handled = true;
|
||||
switch (e.key_code()) {
|
||||
case 0x0D: { // numpad enter
|
||||
case 0x6A: { // numpad *
|
||||
CpuTimeScalarReset();
|
||||
} break;
|
||||
case 0x6D: { // numpad minus
|
||||
|
@ -94,6 +94,8 @@ bool EmulatorWindow::Initialize() {
|
|||
// Allow users to escape fullscreen (but not enter it).
|
||||
if (window_->is_fullscreen()) {
|
||||
window_->ToggleFullscreen(false);
|
||||
} else {
|
||||
handled = false;
|
||||
}
|
||||
} break;
|
||||
|
||||
|
@ -121,7 +123,7 @@ bool EmulatorWindow::Initialize() {
|
|||
auto cpu_menu = MenuItem::Create(MenuItem::Type::kPopup, L"&CPU");
|
||||
{
|
||||
cpu_menu->AddChild(MenuItem::Create(
|
||||
MenuItem::Type::kString, L"&Reset Time Scalar", L"Numpad Enter",
|
||||
MenuItem::Type::kString, L"&Reset Time Scalar", L"Numpad *",
|
||||
std::bind(&EmulatorWindow::CpuTimeScalarReset, this)));
|
||||
cpu_menu->AddChild(MenuItem::Create(
|
||||
MenuItem::Type::kString, L"Time Scalar /= 2", L"Numpad -",
|
||||
|
|
|
@ -38,9 +38,9 @@ class StringBuffer {
|
|||
private:
|
||||
void Grow(size_t additional_length);
|
||||
|
||||
char* buffer_;
|
||||
size_t buffer_offset_;
|
||||
size_t buffer_capacity_;
|
||||
char* buffer_ = nullptr;
|
||||
size_t buffer_offset_ = 0;
|
||||
size_t buffer_capacity_ = 0;
|
||||
};
|
||||
|
||||
} // namespace xe
|
||||
|
|
|
@ -7,8 +7,8 @@
|
|||
******************************************************************************
|
||||
*/
|
||||
|
||||
#include "el/elemental_forms.h"
|
||||
#include "xenia/base/logging.h"
|
||||
#include "xenia/base/platform_win.h"
|
||||
#include "xenia/emulator.h"
|
||||
#include "xenia/kernel/kernel_state.h"
|
||||
#include "xenia/kernel/util/shim_utils.h"
|
||||
|
@ -16,17 +16,87 @@
|
|||
#include "xenia/ui/window.h"
|
||||
#include "xenia/xbox.h"
|
||||
|
||||
#include <commctrl.h>
|
||||
|
||||
namespace xe {
|
||||
namespace kernel {
|
||||
|
||||
SHIM_CALL XamIsUIActive_shim(PPCContext* ppc_context,
|
||||
KernelState* kernel_state) {
|
||||
XELOGD("XamIsUIActive()");
|
||||
SHIM_SET_RETURN_32(0);
|
||||
SHIM_SET_RETURN_32(el::ModalWindow::is_any_visible());
|
||||
}
|
||||
|
||||
class MessageBoxWindow : public el::ModalWindow {
|
||||
public:
|
||||
MessageBoxWindow(xe::threading::Fence* fence)
|
||||
: ModalWindow([fence]() { fence->Signal(); }) {}
|
||||
~MessageBoxWindow() override = default;
|
||||
|
||||
// TODO(benvanik): icon.
|
||||
void Show(el::Element* root_element, std::wstring title, std::wstring message,
|
||||
std::vector<std::wstring> buttons, uint32_t default_button,
|
||||
uint32_t* out_chosen_button) {
|
||||
title_ = std::move(title);
|
||||
message_ = std::move(message);
|
||||
buttons_ = std::move(buttons);
|
||||
default_button_ = default_button;
|
||||
out_chosen_button_ = out_chosen_button;
|
||||
|
||||
// In case we are cancelled, always set default button.
|
||||
*out_chosen_button_ = default_button;
|
||||
|
||||
ModalWindow::Show(root_element);
|
||||
|
||||
EnsureFocus();
|
||||
}
|
||||
|
||||
protected:
|
||||
void BuildUI() override {
|
||||
using namespace el::dsl;
|
||||
|
||||
set_text(xe::to_string(title_));
|
||||
|
||||
auto button_bar = LayoutBoxNode().distribution_position(
|
||||
LayoutDistributionPosition::kRightBottom);
|
||||
for (int32_t i = 0; i < buttons_.size(); ++i) {
|
||||
button_bar.child(ButtonNode(xe::to_string(buttons_[i]).c_str())
|
||||
.id(100 + i)
|
||||
.data(100 + i));
|
||||
}
|
||||
|
||||
LoadNodeTree(
|
||||
LayoutBoxNode()
|
||||
.axis(Axis::kY)
|
||||
.gravity(Gravity::kAll)
|
||||
.position(LayoutPosition::kLeftTop)
|
||||
.distribution(LayoutDistribution::kAvailable)
|
||||
.distribution_position(LayoutDistributionPosition::kLeftTop)
|
||||
.child(LabelNode(xe::to_string(message_).c_str()))
|
||||
.child(button_bar));
|
||||
|
||||
auto default_button = GetElementById<el::Button>(100 + default_button_);
|
||||
el::Button::set_auto_focus_state(true);
|
||||
default_button->set_focus(el::FocusReason::kNavigation);
|
||||
}
|
||||
|
||||
bool OnEvent(const el::Event& ev) override {
|
||||
if (ev.target->IsOfType<el::Button>() && ev.type == el::EventType::kClick) {
|
||||
int data_value = ev.target->data.as_integer();
|
||||
if (data_value) {
|
||||
*out_chosen_button_ = data_value - 100;
|
||||
}
|
||||
Die();
|
||||
return true;
|
||||
}
|
||||
return ModalWindow::OnEvent(ev);
|
||||
}
|
||||
|
||||
std::wstring title_;
|
||||
std::wstring message_;
|
||||
std::vector<std::wstring> buttons_;
|
||||
uint32_t default_button_ = 0;
|
||||
uint32_t* out_chosen_button_ = nullptr;
|
||||
};
|
||||
|
||||
// http://www.se7ensins.com/forums/threads/working-xshowmessageboxui.844116/?jdfwkey=sb0vm
|
||||
SHIM_CALL XamShowMessageBoxUI_shim(PPCContext* ppc_context,
|
||||
KernelState* kernel_state) {
|
||||
|
@ -66,49 +136,29 @@ SHIM_CALL XamShowMessageBoxUI_shim(PPCContext* ppc_context,
|
|||
// Auto-pick the focused button.
|
||||
chosen_button = active_button;
|
||||
} else {
|
||||
TASKDIALOGCONFIG config = {0};
|
||||
config.cbSize = sizeof(config);
|
||||
config.hInstance = GetModuleHandle(nullptr);
|
||||
config.hwndParent =
|
||||
HWND(kernel_state->emulator()->display_window()->native_handle());
|
||||
config.dwFlags = TDF_ALLOW_DIALOG_CANCELLATION | // esc to exit
|
||||
TDF_POSITION_RELATIVE_TO_WINDOW; // center in window
|
||||
config.dwCommonButtons = 0;
|
||||
config.pszWindowTitle = title.c_str();
|
||||
switch (flags & 0xF) {
|
||||
case 0:
|
||||
config.pszMainIcon = nullptr;
|
||||
break;
|
||||
case 1:
|
||||
config.pszMainIcon = TD_ERROR_ICON;
|
||||
break;
|
||||
case 2:
|
||||
config.pszMainIcon = TD_WARNING_ICON;
|
||||
break;
|
||||
case 3:
|
||||
config.pszMainIcon = TD_INFORMATION_ICON;
|
||||
break;
|
||||
}
|
||||
config.pszMainInstruction = text.c_str();
|
||||
config.pszContent = nullptr;
|
||||
std::vector<TASKDIALOG_BUTTON> taskdialog_buttons;
|
||||
for (uint32_t i = 0; i < button_count; ++i) {
|
||||
taskdialog_buttons.push_back({1000 + int(i), buttons[i].c_str()});
|
||||
}
|
||||
config.pButtons = taskdialog_buttons.data();
|
||||
config.cButtons = button_count;
|
||||
config.nDefaultButton = active_button;
|
||||
int button_pressed = 0;
|
||||
TaskDialogIndirect(&config, &button_pressed, nullptr, nullptr);
|
||||
switch (button_pressed) {
|
||||
default:
|
||||
chosen_button = button_pressed - 1000;
|
||||
break;
|
||||
case IDCANCEL:
|
||||
// User cancelled, just pick default.
|
||||
chosen_button = active_button;
|
||||
break;
|
||||
}
|
||||
auto display_window = kernel_state->emulator()->display_window();
|
||||
xe::threading::Fence fence;
|
||||
display_window->loop()->PostSynchronous([&]() {
|
||||
auto root_element = display_window->root_element();
|
||||
auto window = new MessageBoxWindow(&fence);
|
||||
switch (flags & 0xF) {
|
||||
case 0:
|
||||
// config.pszMainIcon = nullptr;
|
||||
break;
|
||||
case 1:
|
||||
// config.pszMainIcon = TD_ERROR_ICON;
|
||||
break;
|
||||
case 2:
|
||||
// config.pszMainIcon = TD_WARNING_ICON;
|
||||
break;
|
||||
case 3:
|
||||
// config.pszMainIcon = TD_INFORMATION_ICON;
|
||||
break;
|
||||
}
|
||||
window->Show(root_element, title, text, buttons, active_button,
|
||||
&chosen_button);
|
||||
});
|
||||
fence.Wait();
|
||||
}
|
||||
SHIM_SET_MEM_32(result_ptr, chosen_button);
|
||||
|
||||
|
@ -116,20 +166,71 @@ SHIM_CALL XamShowMessageBoxUI_shim(PPCContext* ppc_context,
|
|||
SHIM_SET_RETURN_32(X_ERROR_IO_PENDING);
|
||||
}
|
||||
|
||||
class DirtyDiscWindow : public el::ModalWindow {
|
||||
public:
|
||||
DirtyDiscWindow(xe::threading::Fence* fence)
|
||||
: ModalWindow([fence]() { fence->Signal(); }) {}
|
||||
~DirtyDiscWindow() override = default;
|
||||
|
||||
protected:
|
||||
void BuildUI() override {
|
||||
using namespace el::dsl;
|
||||
|
||||
set_text("Disc Read Error");
|
||||
|
||||
LoadNodeTree(
|
||||
LayoutBoxNode()
|
||||
.axis(Axis::kY)
|
||||
.gravity(Gravity::kAll)
|
||||
.position(LayoutPosition::kLeftTop)
|
||||
.distribution(LayoutDistribution::kAvailable)
|
||||
.distribution_position(LayoutDistributionPosition::kLeftTop)
|
||||
.child(LabelNode(
|
||||
"There's been an issue reading content from the game disc."))
|
||||
.child(LabelNode(
|
||||
"This is likely caused by bad or unimplemented file IO "
|
||||
"calls."))
|
||||
.child(LayoutBoxNode()
|
||||
.distribution_position(
|
||||
LayoutDistributionPosition::kRightBottom)
|
||||
.child(ButtonNode("Exit").id("exit_button"))));
|
||||
}
|
||||
|
||||
bool OnEvent(const el::Event& ev) override {
|
||||
if (ev.target->id() == TBIDC("exit_button") &&
|
||||
ev.type == el::EventType::kClick) {
|
||||
Die();
|
||||
return true;
|
||||
}
|
||||
return ModalWindow::OnEvent(ev);
|
||||
}
|
||||
};
|
||||
|
||||
SHIM_CALL XamShowDirtyDiscErrorUI_shim(PPCContext* ppc_context,
|
||||
KernelState* kernel_state) {
|
||||
uint32_t user_index = SHIM_GET_ARG_32(0);
|
||||
|
||||
XELOGD("XamShowDirtyDiscErrorUI(%d)", user_index);
|
||||
|
||||
int button_pressed = 0;
|
||||
TaskDialog(HWND(kernel_state->emulator()->display_window()->native_handle()),
|
||||
GetModuleHandle(nullptr), L"Disc Read Error",
|
||||
L"Game is claiming to be unable to read game data!", nullptr,
|
||||
TDCBF_CLOSE_BUTTON, TD_ERROR_ICON, &button_pressed);
|
||||
if (FLAGS_headless) {
|
||||
assert_always();
|
||||
exit(1);
|
||||
return;
|
||||
}
|
||||
|
||||
auto display_window = kernel_state->emulator()->display_window();
|
||||
xe::threading::Fence fence;
|
||||
display_window->loop()->PostSynchronous([&]() {
|
||||
auto root_element = display_window->root_element();
|
||||
auto window = new DirtyDiscWindow(&fence);
|
||||
window->Show(root_element);
|
||||
});
|
||||
fence.Wait();
|
||||
|
||||
// This is death, and should never return.
|
||||
// TODO(benvanik): cleaner exit.
|
||||
assert_always();
|
||||
exit(1);
|
||||
}
|
||||
|
||||
} // namespace kernel
|
||||
|
|
|
@ -171,6 +171,10 @@ int XboxkrnlModule::LaunchModule(const char* path) {
|
|||
// Create and register the module. We keep it local to this function and
|
||||
// dispose it on exit.
|
||||
auto module = kernel_state_->LoadUserModule(path);
|
||||
if (!module) {
|
||||
XELOGE("Failed to load user module %s.", path);
|
||||
return 2;
|
||||
}
|
||||
|
||||
// Set as the main module, while running.
|
||||
kernel_state_->SetExecutableModule(module);
|
||||
|
|
|
@ -35,7 +35,7 @@ std::unique_ptr<ProfilerDisplay> Profiler::display_ = nullptr;
|
|||
|
||||
bool Profiler::is_enabled() { return true; }
|
||||
|
||||
bool Profiler::is_visible() { return MicroProfileIsDrawing(); }
|
||||
bool Profiler::is_visible() { return is_enabled() && MicroProfileIsDrawing(); }
|
||||
|
||||
void Profiler::Initialize() {
|
||||
// Custom groups.
|
||||
|
|
|
@ -223,6 +223,7 @@ void GL4ElementalRenderer::BeginPaint(int render_target_w,
|
|||
|
||||
glUseProgram(program_);
|
||||
glBindVertexArray(vao_);
|
||||
glProgramUniform1f(program_, 2, 0.0f);
|
||||
}
|
||||
|
||||
void GL4ElementalRenderer::EndPaint() {
|
||||
|
@ -250,15 +251,6 @@ void GL4ElementalRenderer::Flush() {
|
|||
}
|
||||
|
||||
void GL4ElementalRenderer::RenderBatch(Batch* batch) {
|
||||
auto bitmap = static_cast<GL4Bitmap*>(batch->bitmap);
|
||||
if (bitmap != current_bitmap_) {
|
||||
current_bitmap_ = bitmap;
|
||||
Flush();
|
||||
glProgramUniformHandleui64ARB(program_, 1,
|
||||
bitmap ? bitmap->gpu_handle_ : 0);
|
||||
glProgramUniform1f(program_, 2, bitmap ? 1.0f : 0.0f);
|
||||
}
|
||||
|
||||
if (draw_command_count_ + 1 > kMaxCommands) {
|
||||
Flush();
|
||||
}
|
||||
|
@ -266,6 +258,16 @@ void GL4ElementalRenderer::RenderBatch(Batch* batch) {
|
|||
if (!vertex_buffer_.CanAcquire(total_length)) {
|
||||
Flush();
|
||||
}
|
||||
|
||||
auto bitmap = static_cast<GL4Bitmap*>(batch->bitmap);
|
||||
if (bitmap != current_bitmap_) {
|
||||
Flush();
|
||||
current_bitmap_ = bitmap;
|
||||
glProgramUniformHandleui64ARB(program_, 1,
|
||||
bitmap ? bitmap->gpu_handle_ : 0);
|
||||
glProgramUniform1f(program_, 2, bitmap ? 1.0f : 0.0f);
|
||||
}
|
||||
|
||||
auto allocation = vertex_buffer_.Acquire(total_length);
|
||||
|
||||
// TODO(benvanik): custom batcher that lets us use the ringbuffer memory
|
||||
|
|
|
@ -144,43 +144,49 @@ GLProfilerDisplay::GLProfilerDisplay(xe::ui::Window* window)
|
|||
window_->on_painted.AddListener([this](UIEvent& e) { Profiler::Present(); });
|
||||
|
||||
// Pass through mouse events.
|
||||
window_->on_mouse_down.AddListener([](xe::ui::MouseEvent& e) {
|
||||
if (Profiler::is_enabled()) {
|
||||
window_->on_mouse_down.AddListener([this](xe::ui::MouseEvent& e) {
|
||||
if (Profiler::is_visible()) {
|
||||
Profiler::OnMouseDown(e.button() == xe::ui::MouseEvent::Button::kLeft,
|
||||
e.button() == xe::ui::MouseEvent::Button::kRight);
|
||||
e.set_handled(true);
|
||||
window_->Invalidate();
|
||||
}
|
||||
});
|
||||
window_->on_mouse_up.AddListener([](xe::ui::MouseEvent& e) {
|
||||
if (Profiler::is_enabled()) {
|
||||
window_->on_mouse_up.AddListener([this](xe::ui::MouseEvent& e) {
|
||||
if (Profiler::is_visible()) {
|
||||
Profiler::OnMouseUp();
|
||||
e.set_handled(true);
|
||||
window_->Invalidate();
|
||||
}
|
||||
});
|
||||
window_->on_mouse_move.AddListener([](xe::ui::MouseEvent& e) {
|
||||
if (Profiler::is_enabled()) {
|
||||
window_->on_mouse_move.AddListener([this](xe::ui::MouseEvent& e) {
|
||||
if (Profiler::is_visible()) {
|
||||
Profiler::OnMouseMove(e.x(), e.y());
|
||||
e.set_handled(true);
|
||||
window_->Invalidate();
|
||||
}
|
||||
});
|
||||
window_->on_mouse_wheel.AddListener([](xe::ui::MouseEvent& e) {
|
||||
if (Profiler::is_enabled()) {
|
||||
window_->on_mouse_wheel.AddListener([this](xe::ui::MouseEvent& e) {
|
||||
if (Profiler::is_visible()) {
|
||||
Profiler::OnMouseWheel(e.x(), e.y(), -e.dy());
|
||||
e.set_handled(true);
|
||||
window_->Invalidate();
|
||||
}
|
||||
});
|
||||
|
||||
// Watch for toggle/mode keys and such.
|
||||
window_->on_key_down.AddListener([](xe::ui::KeyEvent& e) {
|
||||
if (Profiler::is_enabled()) {
|
||||
window_->on_key_down.AddListener([this](xe::ui::KeyEvent& e) {
|
||||
if (Profiler::is_visible()) {
|
||||
Profiler::OnKeyDown(e.key_code());
|
||||
e.set_handled(true);
|
||||
window_->Invalidate();
|
||||
}
|
||||
});
|
||||
window_->on_key_up.AddListener([](xe::ui::KeyEvent& e) {
|
||||
if (Profiler::is_enabled()) {
|
||||
window_->on_key_up.AddListener([this](xe::ui::KeyEvent& e) {
|
||||
if (Profiler::is_visible()) {
|
||||
Profiler::OnKeyUp(e.key_code());
|
||||
e.set_handled(true);
|
||||
window_->Invalidate();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit 187fb988e015576f840d47f6722b094d1d8cd7e8
|
||||
Subproject commit b0ecdb343c06c157c44acbcc8d140c88ab1568f3
|
Loading…
Reference in New Issue