Migrating xam UI to elemental-forms.

Fixes #345.
This commit is contained in:
Ben Vanik 2015-07-19 14:43:27 -07:00
parent 5c7f42e9d1
commit baa86fcd1b
8 changed files with 195 additions and 80 deletions

View File

@ -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 -",

View File

@ -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

View File

@ -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

View File

@ -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);

View File

@ -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.

View File

@ -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

View File

@ -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