diff --git a/libxenia.vcxproj b/libxenia.vcxproj
index e591b3cee..8ba588138 100644
--- a/libxenia.vcxproj
+++ b/libxenia.vcxproj
@@ -165,22 +165,18 @@
-
-
+
-
-
-
+
+
+
+
-
-
-
-
-
+
@@ -396,8 +392,6 @@
-
-
@@ -405,19 +399,13 @@
-
-
+
-
+
-
-
-
-
-
-
+
diff --git a/libxenia.vcxproj.filters b/libxenia.vcxproj.filters
index bfb38c16a..314a1eb63 100644
--- a/libxenia.vcxproj.filters
+++ b/libxenia.vcxproj.filters
@@ -68,9 +68,6 @@
{42d47a43-1af4-4e1a-9ed7-afa7f7d18e9f}
-
- {268545c9-fbdf-46d2-96f6-35188cec09d6}
-
{c1ac0db1-2f4b-4376-b1dc-e6355c99b395}
@@ -159,7 +156,7 @@
{82795389-e855-4cd6-a3b6-9580030cebf2}
- {c38dacd1-1e4c-4cd1-847e-19b394e8313d}
+ {f598eed5-11dc-4ef8-a7d4-28ec5e43009b}
@@ -409,24 +406,6 @@
src\xenia\hid\xinput
-
- src\xenia\ui\win32
-
-
- src\xenia\ui\win32
-
-
- src\xenia\ui\win32
-
-
- src\xenia\ui\win32
-
-
- src\xenia\ui
-
-
- src\xenia\ui
-
src\xenia\ui
@@ -655,9 +634,6 @@
src\xenia\apu
-
- src\xenia\ui\win32
-
src\xenia\hid
@@ -715,30 +691,39 @@
src\xenia\vfs
+
+ src\xenia\ui
+
+
+ src\xenia\ui
+
+
+ src\xenia\ui
+
+
+ src\xenia\ui
+
src\xenia\ui\gl
-
- src\xenia\ui\gl
-
-
- src\xenia\ui\gl
-
-
- src\xenia\ui\gl
-
src\xenia\ui\gl
-
- src\xenia\ui
+
+ src\xenia\ui\gl
-
+
src\xenia\ui\gl
src\xenia\ui\gl
+
+ src\xenia\ui
+
+
+ src\xenia\ui
+
@@ -1008,27 +993,9 @@
src\xenia\hid\xinput
-
- src\xenia\ui\win32
-
-
- src\xenia\ui\win32
-
-
- src\xenia\ui\win32
-
-
- src\xenia\ui\win32
-
-
- src\xenia\ui
-
src\xenia\ui
-
- src\xenia\ui
-
src\xenia\ui
@@ -1296,9 +1263,6 @@
src\xenia\ui
-
- src\xenia\ui\win32
-
src\xenia\hid
@@ -1362,36 +1326,33 @@
src\xenia\cpu\backend\x64
+
+ src\xenia\ui
+
+
+ src\xenia\ui
+
src\xenia\ui\gl
-
- src\xenia\ui\gl
-
-
- src\xenia\ui\gl
-
-
- src\xenia\ui\gl
-
src\xenia\ui\gl
-
- src\xenia\ui
-
src\xenia\ui\gl
-
- src\xenia\ui
+
+ src\xenia\ui\gl
-
+
src\xenia\ui\gl
src\xenia\ui\gl
+
+ src\xenia\ui
+
diff --git a/src/xenia/debug/ui/application.cc b/src/xenia/debug/ui/application.cc
index 62bf87e17..15ccf21aa 100644
--- a/src/xenia/debug/ui/application.cc
+++ b/src/xenia/debug/ui/application.cc
@@ -27,7 +27,10 @@ Application* Application::current() {
return current_application_;
}
-Application::Application() { current_application_ = this; }
+Application::Application() {
+ current_application_ = this;
+ loop_ = xe::ui::Loop::Create();
+}
Application::~Application() {
assert_true(current_application_ == this);
@@ -65,7 +68,7 @@ bool Application::Initialize() {
}
void Application::Quit() {
- loop_.Quit();
+ loop_->Quit();
// TODO(benvanik): proper exit.
XELOGI("User-initiated death!");
diff --git a/src/xenia/debug/ui/application.h b/src/xenia/debug/ui/application.h
index 6ff3cd70b..72b4a3634 100644
--- a/src/xenia/debug/ui/application.h
+++ b/src/xenia/debug/ui/application.h
@@ -12,7 +12,7 @@
#include
-#include "xenia/ui/platform.h"
+#include "xenia/ui/loop.h"
namespace xe {
namespace debug {
@@ -27,7 +27,7 @@ class Application {
static std::unique_ptr Create();
static Application* current();
- xe::ui::Loop* loop() { return &loop_; }
+ xe::ui::Loop* loop() { return loop_.get(); }
MainWindow* main_window() const { return main_window_.get(); }
void Quit();
@@ -37,7 +37,7 @@ class Application {
bool Initialize();
- xe::ui::PlatformLoop loop_;
+ std::unique_ptr loop_;
std::unique_ptr main_window_;
};
diff --git a/src/xenia/debug/ui/main_window.cc b/src/xenia/debug/ui/main_window.cc
index 85e571f2a..3177f9f94 100644
--- a/src/xenia/debug/ui/main_window.cc
+++ b/src/xenia/debug/ui/main_window.cc
@@ -15,51 +15,46 @@
#include "xenia/base/logging.h"
#include "xenia/base/platform.h"
#include "xenia/base/threading.h"
-
-// TODO(benvanik): platform based.
-#include "xenia/ui/gl/wgl_elemental_control.h"
+#include "xenia/ui/gl/gl_context.h"
namespace xe {
namespace debug {
namespace ui {
using xe::ui::MenuItem;
-using xe::ui::PlatformMenu;
-using xe::ui::PlatformWindow;
-
-enum Commands {
- IDC_FILE_EXIT,
-
- IDC_HELP_WEBSITE,
- IDC_HELP_ABOUT,
-};
const std::wstring kBaseTitle = L"xenia debugger";
-MainWindow::MainWindow(Application* app)
- : PlatformWindow(app->loop(), kBaseTitle),
- app_(app),
- main_menu_(MenuItem::Type::kNormal) {}
+MainWindow::MainWindow(Application* app) : app_(app) {}
+// main_menu_(MenuItem::Type::kNormal) {}
MainWindow::~MainWindow() = default;
bool MainWindow::Initialize() {
- if (!PlatformWindow::Initialize()) {
+ platform_window_ = xe::ui::Window::Create(app()->loop(), kBaseTitle);
+ if (!platform_window_) {
return false;
}
+ platform_window_->Initialize();
+ platform_window_->set_context(
+ xe::ui::gl::GLContext::Create(platform_window_.get()));
+ platform_window_->MakeReady();
- on_key_down.AddListener([this](xe::ui::KeyEvent& e) {
+ platform_window_->on_closed.AddListener(
+ std::bind(&MainWindow::OnClose, this));
+
+ platform_window_->on_key_down.AddListener([this](xe::ui::KeyEvent& e) {
bool handled = true;
switch (e.key_code()) {
case 0x1B: { // VK_ESCAPE
// Allow users to escape fullscreen (but not enter it).
- if (is_fullscreen()) {
- ToggleFullscreen(false);
+ if (platform_window_->is_fullscreen()) {
+ platform_window_->ToggleFullscreen(false);
}
} break;
case 0x70: { // VK_F1
- OnCommand(Commands::IDC_HELP_WEBSITE);
+ LaunchBrowser("http://xenia.jp");
} break;
default: { handled = false; } break;
@@ -68,35 +63,30 @@ bool MainWindow::Initialize() {
});
// Main menu.
- auto file_menu =
- std::make_unique(MenuItem::Type::kPopup, L"&File");
+ auto main_menu = MenuItem::Create(MenuItem::Type::kNormal);
+ auto file_menu = MenuItem::Create(MenuItem::Type::kPopup, L"&File");
{
- file_menu->AddChild(std::make_unique(
- MenuItem::Type::kString, Commands::IDC_FILE_EXIT, L"E&xit", L"Alt+F4"));
+ file_menu->AddChild(
+ MenuItem::Create(MenuItem::Type::kString, L"E&xit", L"Alt+F4",
+ [this]() { platform_window_->Close(); }));
}
- main_menu_.AddChild(std::move(file_menu));
+ main_menu->AddChild(std::move(file_menu));
// Help menu.
- auto help_menu =
- std::make_unique(MenuItem::Type::kPopup, L"&Help");
+ auto help_menu = MenuItem::Create(MenuItem::Type::kPopup, L"&Help");
{
- help_menu->AddChild(std::make_unique(
- MenuItem::Type::kString, Commands::IDC_HELP_WEBSITE, L"&Website...",
- L"F1"));
- help_menu->AddChild(std::make_unique(
- MenuItem::Type::kString, Commands::IDC_HELP_ABOUT, L"&About..."));
+ help_menu->AddChild(
+ MenuItem::Create(MenuItem::Type::kString, L"&Website...", L"F1",
+ []() { LaunchBrowser("http://xenia.jp"); }));
+ help_menu->AddChild(
+ MenuItem::Create(MenuItem::Type::kString, L"&About...",
+ []() { LaunchBrowser("http://xenia.jp/about/"); }));
}
- main_menu_.AddChild(std::move(help_menu));
+ main_menu->AddChild(std::move(help_menu));
- set_menu(&main_menu_);
+ platform_window_->set_main_menu(std::move(main_menu));
- // Setup the GL control that actually does the drawing.
- // We run here in the loop and only touch it (and its context) on this
- // thread. That means some sync-fu when we want to swap.
- control_ = std::make_unique(loop());
- AddChild(control_.get());
-
- Resize(1440, 1200);
+ platform_window_->Resize(1440, 1200);
BuildUI();
@@ -107,7 +97,7 @@ void MainWindow::BuildUI() {
using namespace el::dsl;
el::AnimationBlocker animation_blocker;
- auto root_element = control_->root_element();
+ auto root_element = platform_window_->root_element();
window_ = std::make_unique();
window_->set_settings(el::WindowSettings::kFullScreen);
root_element->AddChild(window_.get());
@@ -154,21 +144,6 @@ void MainWindow::BuildUI() {
void MainWindow::OnClose() { app_->Quit(); }
-void MainWindow::OnCommand(int id) {
- switch (id) {
- case IDC_FILE_EXIT: {
- Close();
- } break;
-
- case IDC_HELP_WEBSITE: {
- LaunchBrowser("http://xenia.jp");
- } break;
- case IDC_HELP_ABOUT: {
- LaunchBrowser("http://xenia.jp/about/");
- } break;
- }
-}
-
} // namespace ui
} // namespace debug
} // namespace xe
diff --git a/src/xenia/debug/ui/main_window.h b/src/xenia/debug/ui/main_window.h
index 5c17c168d..c51117a42 100644
--- a/src/xenia/debug/ui/main_window.h
+++ b/src/xenia/debug/ui/main_window.h
@@ -16,18 +16,16 @@
#include "xenia/debug/ui/application.h"
#include "xenia/debug/ui/views/cpu/cpu_view.h"
#include "xenia/debug/ui/views/gpu/gpu_view.h"
-#include "xenia/ui/elemental_control.h"
-#include "xenia/ui/platform.h"
#include "xenia/ui/window.h"
namespace xe {
namespace debug {
namespace ui {
-class MainWindow : public xe::ui::PlatformWindow {
+class MainWindow {
public:
MainWindow(Application* app);
- ~MainWindow() override;
+ ~MainWindow();
Application* app() const { return app_; }
@@ -38,13 +36,11 @@ class MainWindow : public xe::ui::PlatformWindow {
private:
void BuildUI();
- void OnClose() override;
- void OnCommand(int id) override;
+ void OnClose();
Application* app_ = nullptr;
- xe::ui::PlatformMenu main_menu_;
- std::unique_ptr control_;
+ std::unique_ptr platform_window_;
std::unique_ptr window_;
struct {
el::SplitContainer* split_container;
diff --git a/src/xenia/emulator.cc b/src/xenia/emulator.cc
index 2ce75b66e..b643c98a4 100644
--- a/src/xenia/emulator.cc
+++ b/src/xenia/emulator.cc
@@ -23,7 +23,6 @@
#include "xenia/kernel/kernel_state.h"
#include "xenia/kernel/modules.h"
#include "xenia/memory.h"
-#include "xenia/ui/main_window.h"
#include "xenia/vfs/virtual_file_system.h"
#include "xenia/vfs/devices/disc_image_device.h"
#include "xenia/vfs/devices/host_path_device.h"
@@ -68,14 +67,13 @@ Emulator::~Emulator() {
debugger_.reset();
export_resolver_.reset();
-
- // Kill the window last, as until the graphics system/etc is dead it's needed.
- display_window_.reset();
}
-X_STATUS Emulator::Setup() {
+X_STATUS Emulator::Setup(ui::Window* display_window) {
X_STATUS result = X_STATUS_UNSUCCESSFUL;
+ display_window_ = display_window;
+
// Initialize clock.
// 360 uses a 50MHz clock.
Clock::set_guest_tick_frequency(50000000);
@@ -93,9 +91,6 @@ X_STATUS Emulator::Setup() {
&system_affinity_mask);
SetProcessAffinityMask(process_handle, system_affinity_mask);
- // Create the main window. Other parts will hook into this.
- display_window_ = ui::MainWindow::Create(this);
-
// Create memory system first, as it is required for other systems.
memory_ = std::make_unique();
result = memory_->Initialize();
@@ -129,6 +124,10 @@ X_STATUS Emulator::Setup() {
if (!graphics_system_) {
return X_STATUS_NOT_IMPLEMENTED;
}
+ display_window_->loop()->PostSynchronous([this]() {
+ display_window_->set_context(
+ graphics_system_->CreateContext(display_window_));
+ });
// Initialize the HID.
input_system_ = std::move(xe::hid::InputSystem::Create(this));
@@ -152,7 +151,7 @@ X_STATUS Emulator::Setup() {
return result;
}
result = graphics_system_->Setup(processor_.get(), display_window_->loop(),
- display_window_.get());
+ display_window_);
if (result) {
return result;
}
@@ -171,6 +170,17 @@ X_STATUS Emulator::Setup() {
kernel_state_->LoadKernelModule();
kernel_state_->LoadKernelModule();
+ // Finish initializing the display.
+ display_window_->loop()->PostSynchronous([this]() {
+ {
+ xe::ui::GraphicsContextLock context_lock(display_window_->context());
+ auto profiler_display =
+ display_window_->context()->CreateProfilerDisplay();
+ Profiler::set_display(std::move(profiler_display));
+ }
+ display_window_->MakeReady();
+ });
+
return result;
}
diff --git a/src/xenia/emulator.h b/src/xenia/emulator.h
index 93b39ae73..44d6e19f1 100644
--- a/src/xenia/emulator.h
+++ b/src/xenia/emulator.h
@@ -15,7 +15,7 @@
#include "xenia/debug/debugger.h"
#include "xenia/kernel/kernel_state.h"
#include "xenia/memory.h"
-#include "xenia/ui/platform.h"
+#include "xenia/ui/window.h"
#include "xenia/vfs/virtual_file_system.h"
#include "xenia/xbox.h"
@@ -35,9 +35,6 @@ class GraphicsSystem;
namespace hid {
class InputSystem;
} // namespace hid
-namespace ui {
-class MainWindow;
-} // namespace ui
} // namespace xe
namespace xe {
@@ -49,7 +46,7 @@ class Emulator {
const std::wstring& command_line() const { return command_line_; }
- ui::PlatformWindow* display_window() const { return display_window_.get(); }
+ ui::Window* display_window() const { return display_window_; }
Memory* memory() const { return memory_.get(); }
@@ -70,7 +67,7 @@ class Emulator {
kernel::KernelState* kernel_state() const { return kernel_state_.get(); }
- X_STATUS Setup();
+ X_STATUS Setup(ui::Window* display_window);
X_STATUS LaunchPath(std::wstring path);
X_STATUS LaunchXexFile(std::wstring path);
@@ -83,7 +80,7 @@ class Emulator {
std::wstring command_line_;
- std::unique_ptr display_window_;
+ ui::Window* display_window_;
std::unique_ptr memory_;
diff --git a/src/xenia/emulator_window.cc b/src/xenia/emulator_window.cc
new file mode 100644
index 000000000..37853981c
--- /dev/null
+++ b/src/xenia/emulator_window.cc
@@ -0,0 +1,225 @@
+/**
+ ******************************************************************************
+ * Xenia : Xbox 360 Emulator Research Project *
+ ******************************************************************************
+ * Copyright 2015 Ben Vanik. All rights reserved. *
+ * Released under the BSD license - see LICENSE in the root for more details. *
+ ******************************************************************************
+ */
+
+#include "xenia/emulator_window.h"
+
+#include "xenia/base/clock.h"
+#include "xenia/base/logging.h"
+#include "xenia/base/platform.h"
+#include "xenia/base/threading.h"
+#include "xenia/gpu/graphics_system.h"
+#include "xenia/emulator.h"
+#include "xenia/profiling.h"
+
+namespace xe {
+
+using xe::ui::KeyEvent;
+using xe::ui::MenuItem;
+using xe::ui::MouseEvent;
+using xe::ui::UIEvent;
+
+const std::wstring kBaseTitle = L"xenia";
+
+EmulatorWindow::EmulatorWindow(Emulator* emulator)
+ : emulator_(emulator),
+ loop_(ui::Loop::Create()),
+ window_(ui::Window::Create(loop_.get(), kBaseTitle)) {}
+
+EmulatorWindow::~EmulatorWindow() = default;
+
+std::unique_ptr EmulatorWindow::Create(Emulator* emulator) {
+ std::unique_ptr emulator_window(new EmulatorWindow(emulator));
+
+ emulator_window->loop()->PostSynchronous([&emulator_window]() {
+ xe::threading::set_name("Win32 Loop");
+ xe::Profiler::ThreadEnter("Win32 Loop");
+
+ if (!emulator_window->Initialize()) {
+ XEFATAL("Failed to initialize main window");
+ exit(1);
+ }
+ });
+
+ return emulator_window;
+}
+
+bool EmulatorWindow::Initialize() {
+ if (!window_->Initialize()) {
+ XELOGE("Failed to initialize platform window");
+ return false;
+ }
+
+ UpdateTitle();
+
+ window_->on_closed.AddListener([this](UIEvent& e) {
+ loop_->Quit();
+
+ // TODO(benvanik): proper exit.
+ XELOGI("User-initiated death!");
+ exit(1);
+ });
+ loop_->on_quit.AddListener([this](UIEvent& e) { window_.reset(); });
+
+ window_->on_key_down.AddListener([this](KeyEvent& e) {
+ bool handled = true;
+ switch (e.key_code()) {
+ case 0x0D: { // numpad enter
+ CpuTimeScalarReset();
+ } break;
+ case 0x6D: { // numpad minus
+ CpuTimeScalarSetHalf();
+ } break;
+ case 0x6B: { // numpad plus
+ CpuTimeScalarSetDouble();
+ } break;
+
+ case 0x73: { // VK_F4
+ GpuTraceFrame();
+ } break;
+ case 0x74: { // VK_F5
+ GpuClearCaches();
+ } break;
+
+ case 0x7A: { // VK_F11
+ ToggleFullscreen();
+ } break;
+ case 0x1B: { // VK_ESCAPE
+ // Allow users to escape fullscreen (but not enter it).
+ if (window_->is_fullscreen()) {
+ window_->ToggleFullscreen(false);
+ }
+ } break;
+
+ case 0x70: { // VK_F1
+ ShowHelpWebsite();
+ } break;
+
+ default: { handled = false; } break;
+ }
+ e.set_handled(handled);
+ });
+
+ // Main menu.
+ // FIXME: This code is really messy.
+ auto main_menu = MenuItem::Create(MenuItem::Type::kNormal);
+ auto file_menu = MenuItem::Create(MenuItem::Type::kPopup, L"&File");
+ {
+ file_menu->AddChild(MenuItem::Create(MenuItem::Type::kString, L"E&xit",
+ L"Alt+F4",
+ [this]() { window_->Close(); }));
+ }
+ main_menu->AddChild(std::move(file_menu));
+
+ // CPU menu.
+ 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",
+ std::bind(&EmulatorWindow::CpuTimeScalarReset, this)));
+ cpu_menu->AddChild(MenuItem::Create(
+ MenuItem::Type::kString, L"Time Scalar /= 2", L"Numpad -",
+ std::bind(&EmulatorWindow::CpuTimeScalarSetHalf, this)));
+ cpu_menu->AddChild(MenuItem::Create(
+ MenuItem::Type::kString, L"Time Scalar *= 2", L"Numpad +",
+ std::bind(&EmulatorWindow::CpuTimeScalarSetDouble, this)));
+ }
+ cpu_menu->AddChild(MenuItem::Create(MenuItem::Type::kSeparator));
+ {
+ cpu_menu->AddChild(MenuItem::Create(MenuItem::Type::kString,
+ L"Toggle Profiler &Display", L"Tab",
+ []() { Profiler::ToggleDisplay(); }));
+ cpu_menu->AddChild(MenuItem::Create(MenuItem::Type::kString,
+ L"&Pause/Resume Profiler", L"`",
+ []() { Profiler::TogglePause(); }));
+ }
+ main_menu->AddChild(std::move(cpu_menu));
+
+ // GPU menu.
+ auto gpu_menu = MenuItem::Create(MenuItem::Type::kPopup, L"&GPU");
+ {
+ gpu_menu->AddChild(
+ MenuItem::Create(MenuItem::Type::kString, L"&Trace Frame", L"F4",
+ std::bind(&EmulatorWindow::GpuTraceFrame, this)));
+ }
+ gpu_menu->AddChild(MenuItem::Create(MenuItem::Type::kSeparator));
+ {
+ gpu_menu->AddChild(
+ MenuItem::Create(MenuItem::Type::kString, L"&Clear Caches", L"F5",
+ std::bind(&EmulatorWindow::GpuClearCaches, this)));
+ }
+ main_menu->AddChild(std::move(gpu_menu));
+
+ // Window menu.
+ auto window_menu = MenuItem::Create(MenuItem::Type::kPopup, L"&Window");
+ {
+ window_menu->AddChild(
+ MenuItem::Create(MenuItem::Type::kString, L"&Fullscreen", L"F11",
+ std::bind(&EmulatorWindow::ToggleFullscreen, this)));
+ }
+ main_menu->AddChild(std::move(window_menu));
+
+ // Help menu.
+ auto help_menu = MenuItem::Create(MenuItem::Type::kPopup, L"&Help");
+ {
+ help_menu->AddChild(
+ MenuItem::Create(MenuItem::Type::kString, L"&Website...", L"F1",
+ std::bind(&EmulatorWindow::ShowHelpWebsite, this)));
+ help_menu->AddChild(MenuItem::Create(
+ MenuItem::Type::kString, L"&About...",
+ [this]() { LaunchBrowser("http://xenia.jp/about/"); }));
+ }
+ main_menu->AddChild(std::move(help_menu));
+
+ window_->set_main_menu(std::move(main_menu));
+
+ window_->Resize(1280, 720);
+
+ return true;
+}
+
+void EmulatorWindow::CpuTimeScalarReset() {
+ Clock::set_guest_time_scalar(1.0);
+ UpdateTitle();
+}
+
+void EmulatorWindow::CpuTimeScalarSetHalf() {
+ Clock::set_guest_time_scalar(Clock::guest_time_scalar() / 2.0);
+ UpdateTitle();
+}
+
+void EmulatorWindow::CpuTimeScalarSetDouble() {
+ Clock::set_guest_time_scalar(Clock::guest_time_scalar() * 2.0);
+ UpdateTitle();
+}
+
+void EmulatorWindow::GpuTraceFrame() {
+ emulator()->graphics_system()->RequestFrameTrace();
+}
+
+void EmulatorWindow::GpuClearCaches() {
+ emulator()->graphics_system()->ClearCaches();
+}
+
+void EmulatorWindow::ToggleFullscreen() {
+ window_->ToggleFullscreen(!window_->is_fullscreen());
+}
+
+void EmulatorWindow::ShowHelpWebsite() { LaunchBrowser("http://xenia.jp"); }
+
+void EmulatorWindow::UpdateTitle() {
+ std::wstring title(kBaseTitle);
+ if (Clock::guest_time_scalar() != 1.0) {
+ title += L" (@";
+ title += xe::to_wstring(std::to_string(Clock::guest_time_scalar()));
+ title += L"x)";
+ }
+ window_->set_title(title);
+}
+
+} // namespace xe
diff --git a/src/xenia/emulator_window.h b/src/xenia/emulator_window.h
new file mode 100644
index 000000000..4db17764a
--- /dev/null
+++ b/src/xenia/emulator_window.h
@@ -0,0 +1,55 @@
+/**
+ ******************************************************************************
+ * Xenia : Xbox 360 Emulator Research Project *
+ ******************************************************************************
+ * Copyright 2015 Ben Vanik. All rights reserved. *
+ * Released under the BSD license - see LICENSE in the root for more details. *
+ ******************************************************************************
+ */
+
+#ifndef XENIA_EMULATOR_WINDOW_H_
+#define XENIA_EMULATOR_WINDOW_H_
+
+#include
+
+#include "xenia/ui/loop.h"
+#include "xenia/ui/menu_item.h"
+#include "xenia/ui/window.h"
+#include "xenia/xbox.h"
+
+namespace xe {
+
+class Emulator;
+
+class EmulatorWindow {
+ public:
+ virtual ~EmulatorWindow();
+
+ static std::unique_ptr Create(Emulator* emulator);
+
+ Emulator* emulator() const { return emulator_; }
+ ui::Loop* loop() const { return loop_.get(); }
+ ui::Window* window() const { return window_.get(); }
+
+ private:
+ explicit EmulatorWindow(Emulator* emulator);
+
+ bool Initialize();
+ void UpdateTitle();
+
+ void CpuTimeScalarReset();
+ void CpuTimeScalarSetHalf();
+ void CpuTimeScalarSetDouble();
+ void GpuTraceFrame();
+ void GpuClearCaches();
+ void ToggleFullscreen();
+ void ShowHelpWebsite();
+
+ Emulator* emulator_;
+ std::unique_ptr loop_;
+ std::unique_ptr window_;
+};
+
+} // namespace xe
+
+#endif // XENIA_EMULATOR_WINDOW_H_
diff --git a/src/xenia/gpu/gl4/command_processor.cc b/src/xenia/gpu/gl4/command_processor.cc
index 7ebc0245a..c75d2ede4 100644
--- a/src/xenia/gpu/gl4/command_processor.cc
+++ b/src/xenia/gpu/gl4/command_processor.cc
@@ -71,8 +71,6 @@ CommandProcessor::CommandProcessor(GL4GraphicsSystem* graphics_system)
active_pixel_shader_(nullptr),
active_framebuffer_(nullptr),
last_framebuffer_texture_(0),
- last_swap_width_(0),
- last_swap_height_(0),
point_list_geometry_program_(0),
rect_list_geometry_program_(0),
quad_list_geometry_program_(0),
@@ -83,7 +81,7 @@ CommandProcessor::CommandProcessor(GL4GraphicsSystem* graphics_system)
CommandProcessor::~CommandProcessor() { CloseHandle(write_ptr_index_event_); }
bool CommandProcessor::Initialize(
- std::unique_ptr context) {
+ std::unique_ptr context) {
context_ = std::move(context);
worker_running_ = true;
@@ -197,7 +195,7 @@ void CommandProcessor::WorkerThreadMain() {
// We've run out of commands to execute.
// We spin here waiting for new ones, as the overhead of waiting on our
// event is too high.
- // PrepareForWait();
+ PrepareForWait();
do {
// TODO(benvanik): if we go longer than Nms, switch to waiting?
// It'll keep us from burning power.
@@ -209,7 +207,7 @@ void CommandProcessor::WorkerThreadMain() {
} while (worker_running_ && pending_fns_.empty() &&
(write_ptr_index == 0xBAADF00D ||
read_ptr_index_ == write_ptr_index));
- // ReturnFromWait();
+ ReturnFromWait();
if (!worker_running_ || !pending_fns_.empty()) {
continue;
}
@@ -576,18 +574,50 @@ void CommandProcessor::ReturnFromWait() {
}
}
-void CommandProcessor::IssueSwap() {
- IssueSwap(last_swap_width_, last_swap_height_);
-}
-
void CommandProcessor::IssueSwap(uint32_t frontbuffer_width,
uint32_t frontbuffer_height) {
- if (!swap_handler_) {
+ SCOPE_profile_cpu_f("gpu");
+ if (!swap_request_handler_) {
return;
}
- auto& regs = *register_file_;
- SwapParameters swap_params;
+ // If there was a swap pending we drop it on the floor.
+ // This prevents the display from pulling the backbuffer out from under us.
+ // If we skip a lot then we may need to buffer more, but as the display
+ // thread should be fairly idle that shouldn't happen.
+ if (!FLAGS_vsync) {
+ std::lock_guard lock(swap_state_.mutex);
+ if (swap_state_.pending) {
+ swap_state_.pending = false;
+ // TODO(benvanik): frame skip counter.
+ XELOGW("Skipped frame!");
+ }
+ } else {
+ // Spin until no more pending swap.
+ while (true) {
+ {
+ std::lock_guard lock(swap_state_.mutex);
+ if (!swap_state_.pending) {
+ break;
+ }
+ }
+ xe::threading::MaybeYield();
+ }
+ }
+
+ // One-time initialization.
+ // TODO(benvanik): move someplace more sane?
+ if (!swap_state_.front_buffer_texture) {
+ std::lock_guard lock(swap_state_.mutex);
+ swap_state_.width = frontbuffer_width;
+ swap_state_.height = frontbuffer_height;
+ glCreateTextures(GL_TEXTURE_2D, 1, &swap_state_.front_buffer_texture);
+ glCreateTextures(GL_TEXTURE_2D, 1, &swap_state_.back_buffer_texture);
+ glTextureStorage2D(swap_state_.front_buffer_texture, 1, GL_RGBA8,
+ swap_state_.width, swap_state_.height);
+ glTextureStorage2D(swap_state_.back_buffer_texture, 1, GL_RGBA8,
+ swap_state_.width, swap_state_.height);
+ }
// Lookup the framebuffer in the recently-resolved list.
// TODO(benvanik): make this much more sophisticated.
@@ -595,20 +625,34 @@ void CommandProcessor::IssueSwap(uint32_t frontbuffer_width,
// TODO(benvanik): handle dirty cases (resolved to sysmem, touched).
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
// HACK: just use whatever our current framebuffer is.
- swap_params.framebuffer_texture = last_framebuffer_texture_;
- /*swap_params.framebuffer_texture = active_framebuffer_
+ GLuint framebuffer_texture = last_framebuffer_texture_;
+ /*GLuint framebuffer_texture = active_framebuffer_
? active_framebuffer_->color_targets[0]
: last_framebuffer_texture_;*/
- // Frontbuffer dimensions, if valid.
- swap_params.x = 0;
- swap_params.y = 0;
- swap_params.width = frontbuffer_width ? frontbuffer_width : 1280;
- swap_params.height = frontbuffer_height ? frontbuffer_height : 720;
+ // Copy the the given framebuffer to the current backbuffer.
+ Rect2D src_rect(0, 0, frontbuffer_width ? frontbuffer_width : 1280,
+ frontbuffer_height ? frontbuffer_height : 720);
+ Rect2D dest_rect(0, 0, swap_state_.width, swap_state_.height);
+ reinterpret_cast(context_.get())
+ ->blitter()
+ ->CopyColorTexture2D(framebuffer_texture, src_rect,
+ swap_state_.back_buffer_texture, dest_rect,
+ GL_LINEAR);
- PrepareForWait();
- swap_handler_(swap_params);
- ReturnFromWait();
+ // Need to finish to be sure the other context sees the right data.
+ // TODO(benvanik): prevent this? fences?
+ glFinish();
+
+ {
+ // Set pending so that the display will swap the next time it can.
+ std::lock_guard lock(swap_state_.mutex);
+ swap_state_.pending = true;
+ }
+
+ // Notify the display a swap is pending so that our changes are picked up.
+ // It does the actual front/back buffer swap.
+ swap_request_handler_();
// Remove any dead textures, etc.
texture_cache_.Scavenge();
@@ -964,8 +1008,6 @@ bool CommandProcessor::ExecutePacketType3_XE_SWAP(RingbufferReader* reader,
uint32_t frontbuffer_width = reader->Read();
uint32_t frontbuffer_height = reader->Read();
reader->Advance(count - 4);
- last_swap_width_ = frontbuffer_width;
- last_swap_height_ = frontbuffer_height;
// Ensure we issue any pending draws.
draw_batcher_.Flush(DrawBatcher::FlushMode::kMakeCoherent);
@@ -2757,6 +2799,8 @@ bool CommandProcessor::IssueCopy() {
// TODO(benvanik): copy to staging texture then PBO back?
void* ptr = memory_->TranslatePhysical(copy_dest_base);
+ auto blitter = static_cast(context_.get())->blitter();
+
// Make active so glReadPixels reads from us.
switch (copy_command) {
case CopyCommand::kRaw: {
@@ -2766,8 +2810,8 @@ bool CommandProcessor::IssueCopy() {
// Source from a bound render target.
// TODO(benvanik): RAW copy.
last_framebuffer_texture_ = texture_cache_.CopyTexture(
- context_->blitter(), copy_dest_base, dest_logical_width,
- dest_logical_height, dest_block_width, dest_block_height,
+ blitter, copy_dest_base, dest_logical_width, dest_logical_height,
+ dest_block_width, dest_block_height,
ColorFormatToTextureFormat(copy_dest_format),
copy_dest_swap ? true : false, color_targets[copy_src_select],
src_rect, dest_rect);
@@ -2777,11 +2821,10 @@ bool CommandProcessor::IssueCopy() {
} else {
// Source from the bound depth/stencil target.
// TODO(benvanik): RAW copy.
- texture_cache_.CopyTexture(context_->blitter(), copy_dest_base,
- dest_logical_width, dest_logical_height,
- dest_block_width, dest_block_height,
- src_format, copy_dest_swap ? true : false,
- depth_target, src_rect, dest_rect);
+ texture_cache_.CopyTexture(
+ blitter, copy_dest_base, dest_logical_width, dest_logical_height,
+ dest_block_width, dest_block_height, src_format,
+ copy_dest_swap ? true : false, depth_target, src_rect, dest_rect);
if (!FLAGS_disable_framebuffer_readback) {
// glReadPixels(x, y, w, h, GL_DEPTH_STENCIL, read_type, ptr);
}
@@ -2794,8 +2837,8 @@ bool CommandProcessor::IssueCopy() {
// Either copy the readbuffer into an existing texture or create a new
// one in the cache so we can service future upload requests.
last_framebuffer_texture_ = texture_cache_.ConvertTexture(
- context_->blitter(), copy_dest_base, dest_logical_width,
- dest_logical_height, dest_block_width, dest_block_height,
+ blitter, copy_dest_base, dest_logical_width, dest_logical_height,
+ dest_block_width, dest_block_height,
ColorFormatToTextureFormat(copy_dest_format),
copy_dest_swap ? true : false, color_targets[copy_src_select],
src_rect, dest_rect);
@@ -2804,11 +2847,10 @@ bool CommandProcessor::IssueCopy() {
}
} else {
// Source from the bound depth/stencil target.
- texture_cache_.ConvertTexture(context_->blitter(), copy_dest_base,
- dest_logical_width, dest_logical_height,
- dest_block_width, dest_block_height,
- src_format, copy_dest_swap ? true : false,
- depth_target, src_rect, dest_rect);
+ texture_cache_.ConvertTexture(
+ blitter, copy_dest_base, dest_logical_width, dest_logical_height,
+ dest_block_width, dest_block_height, src_format,
+ copy_dest_swap ? true : false, depth_target, src_rect, dest_rect);
if (!FLAGS_disable_framebuffer_readback) {
// glReadPixels(x, y, w, h, GL_DEPTH_STENCIL, read_type, ptr);
}
diff --git a/src/xenia/gpu/gl4/command_processor.h b/src/xenia/gpu/gl4/command_processor.h
index ef0f93dae..f096ed142 100644
--- a/src/xenia/gpu/gl4/command_processor.h
+++ b/src/xenia/gpu/gl4/command_processor.h
@@ -42,13 +42,18 @@ namespace gl4 {
class GL4GraphicsSystem;
-struct SwapParameters {
- uint32_t x;
- uint32_t y;
- uint32_t width;
- uint32_t height;
-
- GLuint framebuffer_texture;
+struct SwapState {
+ // Lock must be held when changing data in this structure.
+ xe::mutex mutex;
+ // Dimensions of the framebuffer textures. Should match window size.
+ uint32_t width = 0;
+ uint32_t height = 0;
+ // Current front buffer, being drawn to the screen.
+ GLuint front_buffer_texture = 0;
+ // Current back buffer, being updated by the CP.
+ GLuint back_buffer_texture = 0;
+ // Whether the back buffer is dirty and a swap is pending.
+ bool pending = false;
};
enum class SwapMode {
@@ -61,22 +66,23 @@ class CommandProcessor {
CommandProcessor(GL4GraphicsSystem* graphics_system);
~CommandProcessor();
- typedef std::function SwapHandler;
- void set_swap_handler(SwapHandler fn) { swap_handler_ = fn; }
-
uint32_t counter() const { return counter_; }
void increment_counter() { counter_++; }
- bool Initialize(std::unique_ptr context);
+ bool Initialize(std::unique_ptr context);
void Shutdown();
void CallInThread(std::function fn);
void ClearCaches();
+ SwapState& swap_state() { return swap_state_; }
void set_swap_mode(SwapMode swap_mode) { swap_mode_ = swap_mode; }
- void IssueSwap();
void IssueSwap(uint32_t frontbuffer_width, uint32_t frontbuffer_height);
+ void set_swap_request_handler(std::function fn) {
+ swap_request_handler_ = fn;
+ }
+
void RequestFrameTrace(const std::wstring& root_path);
void BeginTracing(const std::wstring& root_path);
void EndTracing();
@@ -238,11 +244,11 @@ class CommandProcessor {
std::atomic worker_running_;
kernel::object_ref worker_thread_;
- std::unique_ptr context_;
- SwapHandler swap_handler_;
- std::queue> pending_fns_;
-
+ std::unique_ptr context_;
SwapMode swap_mode_;
+ SwapState swap_state_;
+ std::function swap_request_handler_;
+ std::queue> pending_fns_;
uint32_t counter_;
@@ -266,8 +272,6 @@ class CommandProcessor {
GL4Shader* active_pixel_shader_;
CachedFramebuffer* active_framebuffer_;
GLuint last_framebuffer_texture_;
- uint32_t last_swap_width_;
- uint32_t last_swap_height_;
std::vector cached_framebuffers_;
std::vector cached_color_render_targets_;
diff --git a/src/xenia/gpu/gl4/gl4_graphics_system.cc b/src/xenia/gpu/gl4/gl4_graphics_system.cc
index 852dcc6fb..37955a96a 100644
--- a/src/xenia/gpu/gl4/gl4_graphics_system.cc
+++ b/src/xenia/gpu/gl4/gl4_graphics_system.cc
@@ -19,7 +19,8 @@
#include "xenia/gpu/gl4/gl4_gpu_flags.h"
#include "xenia/gpu/gpu_flags.h"
#include "xenia/gpu/tracing.h"
-#include "xenia/ui/gl/gl_profiler_display.h"
+#include "xenia/profiling.h"
+#include "xenia/ui/window.h"
namespace xe {
namespace gpu {
@@ -47,6 +48,14 @@ std::unique_ptr GL4GraphicsSystem::Create(Emulator* emulator) {
return std::make_unique(emulator);
}
+std::unique_ptr GL4GraphicsSystem::CreateContext(
+ ui::Window* target_window) {
+ // Setup the GL control that actually does the drawing.
+ // We run here in the loop and only touch it (and its context) on this
+ // thread. That means some sync-fu when we want to swap.
+ return xe::ui::gl::GLContext::Create(target_window);
+}
+
GL4GraphicsSystem::GL4GraphicsSystem(Emulator* emulator)
: GraphicsSystem(emulator), worker_running_(false) {}
@@ -54,38 +63,36 @@ GL4GraphicsSystem::~GL4GraphicsSystem() = default;
X_STATUS GL4GraphicsSystem::Setup(cpu::Processor* processor,
ui::Loop* target_loop,
- ui::PlatformWindow* target_window) {
+ ui::Window* target_window) {
auto result = GraphicsSystem::Setup(processor, target_loop, target_window);
if (result) {
return result;
}
+ display_context_ =
+ reinterpret_cast(target_window->context());
+
+ // Watch for paint requests to do our swap.
+ target_window->on_painting.AddListener(
+ [this](xe::ui::UIEvent& e) { Swap(e); });
+
// Create rendering control.
// This must happen on the UI thread.
- xe::threading::Fence control_ready_fence;
- std::unique_ptr processor_context;
- target_loop_->Post([&]() {
- // Setup the GL control that actually does the drawing.
- // We run here in the loop and only touch it (and its context) on this
- // thread. That means some sync-fu when we want to swap.
- control_ = std::make_unique(target_loop_);
- target_window_->AddChild(control_.get());
-
+ std::unique_ptr processor_context;
+ target_loop_->PostSynchronous([&]() {
// Setup the GL context the command processor will do all its drawing in.
- // It's shared with the control context so that we can resolve framebuffers
+ // It's shared with the display context so that we can resolve framebuffers
// from it.
- processor_context = control_->context()->CreateShared();
-
- {
- xe::ui::gl::GLContextLock context_lock(control_->context());
- auto profiler_display =
- std::make_unique(control_.get());
- Profiler::set_display(std::move(profiler_display));
- }
-
- control_ready_fence.Signal();
+ processor_context = display_context_->CreateShared();
+ processor_context->ClearCurrent();
});
- control_ready_fence.Wait();
+ if (!processor_context) {
+ XEFATAL(
+ "Unable to initialize GL context. Xenia requires OpenGL 4.5. Ensure "
+ "you have the latest drivers for your GPU and that it supports OpenGL "
+ "4.5. See http://xenia.jp/faq/ for more information.");
+ return X_STATUS_UNSUCCESSFUL;
+ }
// Create command processor. This will spin up a thread to process all
// incoming ringbuffer packets.
@@ -94,8 +101,8 @@ X_STATUS GL4GraphicsSystem::Setup(cpu::Processor* processor,
XELOGE("Unable to initialize command processor");
return X_STATUS_UNSUCCESSFUL;
}
- command_processor_->set_swap_handler(
- [this](const SwapParameters& swap_params) { SwapHandler(swap_params); });
+ command_processor_->set_swap_request_handler(
+ [this]() { target_window_->Invalidate(); });
// Let the processor know we want register access callbacks.
memory_->AddVirtualMappedRange(
@@ -144,7 +151,6 @@ void GL4GraphicsSystem::Shutdown() {
// TODO(benvanik): remove mapped range.
command_processor_.reset();
- control_.reset();
GraphicsSystem::Shutdown();
}
@@ -159,10 +165,6 @@ void GL4GraphicsSystem::EnableReadPointerWriteBack(uint32_t ptr,
command_processor_->EnableReadPointerWriteBack(ptr, block_size);
}
-void GL4GraphicsSystem::RequestSwap() {
- command_processor_->CallInThread([&]() { command_processor_->IssueSwap(); });
-}
-
void GL4GraphicsSystem::RequestFrameTrace() {
command_processor_->RequestFrameTrace(xe::to_wstring(FLAGS_trace_gpu_prefix));
}
@@ -268,6 +270,7 @@ void GL4GraphicsSystem::PlayTrace(const uint8_t* trace_data, size_t trace_size,
}
command_processor_->set_swap_mode(SwapMode::kNormal);
+ command_processor_->IssueSwap(1280, 720);
});
}
@@ -288,22 +291,29 @@ void GL4GraphicsSystem::MarkVblank() {
DispatchInterruptCallback(0, 2);
}
-void GL4GraphicsSystem::SwapHandler(const SwapParameters& swap_params) {
- SCOPE_profile_cpu_f("gpu");
-
- // Swap requested. Synchronously post a request to the loop so that
- // we do the swap in the right thread.
- control_->SynchronousRepaint([this, swap_params]() {
- if (!swap_params.framebuffer_texture) {
- // no-op.
- return;
+void GL4GraphicsSystem::Swap(xe::ui::UIEvent& e) {
+ // Check for pending swap.
+ auto& swap_state = command_processor_->swap_state();
+ {
+ std::lock_guard lock(swap_state.mutex);
+ if (swap_state.pending) {
+ swap_state.pending = false;
+ std::swap(swap_state.front_buffer_texture,
+ swap_state.back_buffer_texture);
}
- Rect2D src_rect(swap_params.x, swap_params.y, swap_params.width,
- swap_params.height);
- Rect2D dest_rect(0, 0, control_->width(), control_->height());
- control_->context()->blitter()->BlitTexture2D(
- swap_params.framebuffer_texture, src_rect, dest_rect, GL_LINEAR);
- });
+ }
+
+ if (!swap_state.front_buffer_texture) {
+ // Not yet ready.
+ return;
+ }
+
+ // Blit the frontbuffer.
+ display_context_->blitter()->BlitTexture2D(
+ swap_state.front_buffer_texture,
+ Rect2D(0, 0, swap_state.width, swap_state.height),
+ Rect2D(0, 0, target_window_->width(), target_window_->height()),
+ GL_LINEAR);
}
uint64_t GL4GraphicsSystem::ReadRegister(uint32_t addr) {
diff --git a/src/xenia/gpu/gl4/gl4_graphics_system.h b/src/xenia/gpu/gl4/gl4_graphics_system.h
index 8137ea8c6..206651cd2 100644
--- a/src/xenia/gpu/gl4/gl4_graphics_system.h
+++ b/src/xenia/gpu/gl4/gl4_graphics_system.h
@@ -16,7 +16,7 @@
#include "xenia/gpu/graphics_system.h"
#include "xenia/gpu/register_file.h"
#include "xenia/kernel/objects/xthread.h"
-#include "xenia/ui/gl/wgl_control.h"
+#include "xenia/ui/gl/gl_context.h"
namespace xe {
namespace gpu {
@@ -28,9 +28,11 @@ class GL4GraphicsSystem : public GraphicsSystem {
~GL4GraphicsSystem() override;
static std::unique_ptr Create(Emulator* emulator);
+ std::unique_ptr CreateContext(
+ ui::Window* target_window) override;
X_STATUS Setup(cpu::Processor* processor, ui::Loop* target_loop,
- ui::PlatformWindow* target_window) override;
+ ui::Window* target_window) override;
void Shutdown() override;
RegisterFile* register_file() { return ®ister_file_; }
@@ -41,8 +43,6 @@ class GL4GraphicsSystem : public GraphicsSystem {
void InitializeRingBuffer(uint32_t ptr, uint32_t page_count) override;
void EnableReadPointerWriteBack(uint32_t ptr, uint32_t block_size) override;
- void RequestSwap() override;
-
void RequestFrameTrace() override;
void BeginTracing() override;
void EndTracing() override;
@@ -52,7 +52,7 @@ class GL4GraphicsSystem : public GraphicsSystem {
private:
void MarkVblank();
- void SwapHandler(const SwapParameters& swap_params);
+ void Swap(xe::ui::UIEvent& e);
uint64_t ReadRegister(uint32_t addr);
void WriteRegister(uint32_t addr, uint64_t value);
@@ -67,7 +67,8 @@ class GL4GraphicsSystem : public GraphicsSystem {
RegisterFile register_file_;
std::unique_ptr command_processor_;
- std::unique_ptr control_;
+
+ xe::ui::gl::GLContext* display_context_ = nullptr;
std::atomic worker_running_;
kernel::object_ref worker_thread_;
diff --git a/src/xenia/gpu/graphics_system.cc b/src/xenia/gpu/graphics_system.cc
index d5233d250..167d109e8 100644
--- a/src/xenia/gpu/graphics_system.cc
+++ b/src/xenia/gpu/graphics_system.cc
@@ -43,7 +43,7 @@ GraphicsSystem::GraphicsSystem(Emulator* emulator) : emulator_(emulator) {}
GraphicsSystem::~GraphicsSystem() = default;
X_STATUS GraphicsSystem::Setup(cpu::Processor* processor, ui::Loop* target_loop,
- ui::PlatformWindow* target_window) {
+ ui::Window* target_window) {
processor_ = processor;
memory_ = processor->memory();
target_loop_ = target_loop;
diff --git a/src/xenia/gpu/graphics_system.h b/src/xenia/gpu/graphics_system.h
index a77654c45..5556da33c 100644
--- a/src/xenia/gpu/graphics_system.h
+++ b/src/xenia/gpu/graphics_system.h
@@ -17,7 +17,7 @@
#include "xenia/cpu/processor.h"
#include "xenia/memory.h"
#include "xenia/ui/loop.h"
-#include "xenia/ui/platform.h"
+#include "xenia/ui/window.h"
#include "xenia/xbox.h"
namespace xe {
@@ -32,13 +32,15 @@ class GraphicsSystem {
virtual ~GraphicsSystem();
static std::unique_ptr Create(Emulator* emulator);
+ virtual std::unique_ptr CreateContext(
+ ui::Window* target_window) = 0;
Emulator* emulator() const { return emulator_; }
Memory* memory() const { return memory_; }
cpu::Processor* processor() const { return processor_; }
virtual X_STATUS Setup(cpu::Processor* processor, ui::Loop* target_loop,
- ui::PlatformWindow* target_window);
+ ui::Window* target_window);
virtual void Shutdown();
void SetInterruptCallback(uint32_t callback, uint32_t user_data);
@@ -46,8 +48,6 @@ class GraphicsSystem {
virtual void EnableReadPointerWriteBack(uint32_t ptr,
uint32_t block_size) = 0;
- virtual void RequestSwap() = 0;
-
void DispatchInterruptCallback(uint32_t source, uint32_t cpu);
virtual void RequestFrameTrace() {}
@@ -68,7 +68,7 @@ class GraphicsSystem {
Memory* memory_ = nullptr;
cpu::Processor* processor_ = nullptr;
ui::Loop* target_loop_ = nullptr;
- ui::PlatformWindow* target_window_ = nullptr;
+ ui::Window* target_window_ = nullptr;
uint32_t interrupt_callback_ = 0;
uint32_t interrupt_callback_data_ = 0;
diff --git a/src/xenia/gpu/xe-gpu-trace-viewer.cc b/src/xenia/gpu/xe-gpu-trace-viewer.cc
index 98b9cf36b..2fdb35773 100644
--- a/src/xenia/gpu/xe-gpu-trace-viewer.cc
+++ b/src/xenia/gpu/xe-gpu-trace-viewer.cc
@@ -24,6 +24,7 @@
#include "xenia/gpu/xenos.h"
#include "xenia/profiling.h"
#include "xenia/ui/gl/gl_context.h"
+#include "xenia/ui/window.h"
// HACK: until we have another impl, we just use gl4 directly.
#include "xenia/gpu/gl4/command_processor.h"
@@ -842,7 +843,7 @@ class TracePlayer : public TraceReader {
int current_command_index_;
};
-void DrawControllerUI(xe::ui::PlatformWindow* window, TracePlayer& player,
+void DrawControllerUI(xe::ui::Window* window, TracePlayer& player,
Memory* memory) {
ImGui::SetNextWindowPos(ImVec2(5, 5), ImGuiSetCond_FirstUseEver);
if (!ImGui::Begin("Controller", nullptr, ImVec2(340, 60))) {
@@ -883,7 +884,7 @@ void DrawControllerUI(xe::ui::PlatformWindow* window, TracePlayer& player,
ImGui::End();
}
-void DrawCommandListUI(xe::ui::PlatformWindow* window, TracePlayer& player,
+void DrawCommandListUI(xe::ui::Window* window, TracePlayer& player,
Memory* memory) {
ImGui::SetNextWindowPos(ImVec2(5, 70), ImGuiSetCond_FirstUseEver);
if (!ImGui::Begin("Command List", nullptr, ImVec2(200, 640))) {
@@ -1027,9 +1028,8 @@ ShaderDisplayType DrawShaderTypeUI() {
return shader_display_type;
}
-void DrawShaderUI(xe::ui::PlatformWindow* window, TracePlayer& player,
- Memory* memory, gl4::GL4Shader* shader,
- ShaderDisplayType display_type) {
+void DrawShaderUI(xe::ui::Window* window, TracePlayer& player, Memory* memory,
+ gl4::GL4Shader* shader, ShaderDisplayType display_type) {
// Must be prepared for advanced display modes.
if (display_type != ShaderDisplayType::kUcode) {
if (!shader->has_prepared()) {
@@ -1393,8 +1393,7 @@ static const char* kEndiannessNames[] = {
"unspecified endianness", "8-in-16", "8-in-32", "16-in-32",
};
-void DrawStateUI(xe::ui::PlatformWindow* window, TracePlayer& player,
- Memory* memory) {
+void DrawStateUI(xe::ui::Window* window, TracePlayer& player, Memory* memory) {
auto gs = static_cast(player.graphics_system());
auto cp = gs->command_processor();
auto& regs = *gs->register_file();
@@ -2033,8 +2032,8 @@ void DrawStateUI(xe::ui::PlatformWindow* window, TracePlayer& player,
ImGui::End();
}
-void DrawPacketDisassemblerUI(xe::ui::PlatformWindow* window,
- TracePlayer& player, Memory* memory) {
+void DrawPacketDisassemblerUI(xe::ui::Window* window, TracePlayer& player,
+ Memory* memory) {
ImGui::SetNextWindowCollapsed(true, ImGuiSetCond_FirstUseEver);
ImGui::SetNextWindowPos(ImVec2(float(window->width()) - 500 - 5, 5),
ImGuiSetCond_FirstUseEver);
@@ -2175,8 +2174,7 @@ void DrawPacketDisassemblerUI(xe::ui::PlatformWindow* window,
ImGui::End();
}
-void DrawUI(xe::ui::PlatformWindow* window, TracePlayer& player,
- Memory* memory) {
+void DrawUI(xe::ui::Window* window, TracePlayer& player, Memory* memory) {
// ImGui::ShowTestWindow();
DrawControllerUI(window, player, memory);
@@ -2189,129 +2187,151 @@ void ImImpl_Setup();
void ImImpl_Shutdown();
int trace_viewer_main(std::vector& args) {
- // Create the emulator.
+ // Create the emulator but don't initialize so we can setup the window.
auto emulator = std::make_unique(L"");
- X_STATUS result = emulator->Setup();
+
+ // Main emulator display window.
+ auto loop = ui::Loop::Create();
+ auto window = xe::ui::Window::Create(loop.get(), L"xe-gpu-trace-viewer");
+ loop->PostSynchronous([&window]() {
+ xe::threading::set_name("Win32 Loop");
+ if (!window->Initialize()) {
+ XEFATAL("Failed to initialize main window");
+ exit(1);
+ }
+ });
+ window->on_closed.AddListener([&loop](xe::ui::UIEvent& e) {
+ loop->Quit();
+ XELOGI("User-initiated death!");
+ exit(1);
+ });
+ loop->on_quit.AddListener([&window](xe::ui::UIEvent& e) { window.reset(); });
+ window->Resize(1920, 1200);
+
+ X_STATUS result = emulator->Setup(window.get());
if (XFAILED(result)) {
XELOGE("Failed to setup emulator: %.8X", result);
return 1;
}
// Grab path from the flag or unnamed argument.
- if (!FLAGS_target_trace_file.empty() || args.size() >= 2) {
- std::wstring path;
- if (!FLAGS_target_trace_file.empty()) {
- // Passed as a named argument.
- // TODO(benvanik): find something better than gflags that supports
- // unicode.
- path = xe::to_wstring(FLAGS_target_trace_file);
- } else {
- // Passed as an unnamed argument.
- path = args[1];
- }
- // Normalize the path and make absolute.
- auto abs_path = xe::to_absolute_path(path);
-
- auto window = emulator->display_window();
- auto loop = window->loop();
- auto file_name = xe::find_name_from_path(path);
- window->set_title(std::wstring(L"Xenia GPU Trace Viewer: ") + file_name);
-
- auto graphics_system = emulator->graphics_system();
- Profiler::set_display(nullptr);
-
- TracePlayer player(loop, emulator->graphics_system());
- if (!player.Open(abs_path)) {
- XELOGE("Could not load trace file");
- return 1;
- }
-
- auto control = window->child(0);
- control->on_key_char.AddListener([graphics_system](xe::ui::KeyEvent& e) {
- auto& io = ImGui::GetIO();
- if (e.key_code() > 0 && e.key_code() < 0x10000) {
- if (e.key_code() == 0x74 /* VK_F5 */) {
- graphics_system->ClearCaches();
- } else {
- io.AddInputCharacter(e.key_code());
- }
- }
- e.set_handled(true);
- });
- control->on_mouse_down.AddListener([](xe::ui::MouseEvent& e) {
- auto& io = ImGui::GetIO();
- io.MousePos = ImVec2(float(e.x()), float(e.y()));
- switch (e.button()) {
- case xe::ui::MouseEvent::Button::kLeft:
- io.MouseDown[0] = true;
- break;
- case xe::ui::MouseEvent::Button::kRight:
- io.MouseDown[1] = true;
- break;
- }
- });
- control->on_mouse_move.AddListener([](xe::ui::MouseEvent& e) {
- auto& io = ImGui::GetIO();
- io.MousePos = ImVec2(float(e.x()), float(e.y()));
- });
- control->on_mouse_up.AddListener([](xe::ui::MouseEvent& e) {
- auto& io = ImGui::GetIO();
- io.MousePos = ImVec2(float(e.x()), float(e.y()));
- switch (e.button()) {
- case xe::ui::MouseEvent::Button::kLeft:
- io.MouseDown[0] = false;
- break;
- case xe::ui::MouseEvent::Button::kRight:
- io.MouseDown[1] = false;
- break;
- }
- });
- control->on_mouse_wheel.AddListener([](xe::ui::MouseEvent& e) {
- auto& io = ImGui::GetIO();
- io.MousePos = ImVec2(float(e.x()), float(e.y()));
- io.MouseWheel += float(e.dy() / 120.0f);
- });
-
- control->on_paint.AddListener([&](xe::ui::UIEvent& e) {
- static bool imgui_setup = false;
- if (!imgui_setup) {
- ImImpl_Setup();
- imgui_setup = true;
- }
- auto& io = ImGui::GetIO();
- auto current_ticks = Clock::QueryHostTickCount();
- static uint64_t last_ticks = 0;
- io.DeltaTime =
- (current_ticks - last_ticks) / float(Clock::host_tick_frequency());
- last_ticks = current_ticks;
-
- io.DisplaySize =
- ImVec2(float(e.control()->width()), float(e.control()->height()));
-
- BYTE keystate[256];
- GetKeyboardState(keystate);
- for (int i = 0; i < 256; i++) io.KeysDown[i] = (keystate[i] & 0x80) != 0;
- io.KeyCtrl = (keystate[VK_CONTROL] & 0x80) != 0;
- io.KeyShift = (keystate[VK_SHIFT] & 0x80) != 0;
-
- ImGui::NewFrame();
-
- DrawUI(window, player, emulator->memory());
-
- glViewport(0, 0, (int)io.DisplaySize.x, (int)io.DisplaySize.y);
- ImGui::Render();
-
- graphics_system->RequestSwap();
- });
- graphics_system->RequestSwap();
-
- // Wait until we are exited.
- emulator->display_window()->loop()->AwaitQuit();
-
- ImImpl_Shutdown();
+ if (FLAGS_target_trace_file.empty() && args.size() < 2) {
+ XELOGE("No trace file specified");
+ return 1;
}
+ std::wstring path;
+ if (!FLAGS_target_trace_file.empty()) {
+ // Passed as a named argument.
+ // TODO(benvanik): find something better than gflags that supports
+ // unicode.
+ path = xe::to_wstring(FLAGS_target_trace_file);
+ } else {
+ // Passed as an unnamed argument.
+ path = args[1];
+ }
+ // Normalize the path and make absolute.
+ auto abs_path = xe::to_absolute_path(path);
+
+ auto file_name = xe::find_name_from_path(path);
+ window->set_title(std::wstring(L"Xenia GPU Trace Viewer: ") + file_name);
+
+ auto graphics_system = emulator->graphics_system();
+ Profiler::set_display(nullptr);
+
+ TracePlayer player(loop.get(), emulator->graphics_system());
+ if (!player.Open(abs_path)) {
+ XELOGE("Could not load trace file");
+ return 1;
+ }
+
+ window->on_key_char.AddListener([graphics_system](xe::ui::KeyEvent& e) {
+ auto& io = ImGui::GetIO();
+ if (e.key_code() > 0 && e.key_code() < 0x10000) {
+ if (e.key_code() == 0x74 /* VK_F5 */) {
+ graphics_system->ClearCaches();
+ } else {
+ io.AddInputCharacter(e.key_code());
+ }
+ }
+ e.set_handled(true);
+ });
+ window->on_mouse_down.AddListener([](xe::ui::MouseEvent& e) {
+ auto& io = ImGui::GetIO();
+ io.MousePos = ImVec2(float(e.x()), float(e.y()));
+ switch (e.button()) {
+ case xe::ui::MouseEvent::Button::kLeft:
+ io.MouseDown[0] = true;
+ break;
+ case xe::ui::MouseEvent::Button::kRight:
+ io.MouseDown[1] = true;
+ break;
+ }
+ });
+ window->on_mouse_move.AddListener([](xe::ui::MouseEvent& e) {
+ auto& io = ImGui::GetIO();
+ io.MousePos = ImVec2(float(e.x()), float(e.y()));
+ });
+ window->on_mouse_up.AddListener([](xe::ui::MouseEvent& e) {
+ auto& io = ImGui::GetIO();
+ io.MousePos = ImVec2(float(e.x()), float(e.y()));
+ switch (e.button()) {
+ case xe::ui::MouseEvent::Button::kLeft:
+ io.MouseDown[0] = false;
+ break;
+ case xe::ui::MouseEvent::Button::kRight:
+ io.MouseDown[1] = false;
+ break;
+ }
+ });
+ window->on_mouse_wheel.AddListener([](xe::ui::MouseEvent& e) {
+ auto& io = ImGui::GetIO();
+ io.MousePos = ImVec2(float(e.x()), float(e.y()));
+ io.MouseWheel += float(e.dy() / 120.0f);
+ });
+
+ window->on_painting.AddListener([&](xe::ui::UIEvent& e) {
+ static bool imgui_setup = false;
+ if (!imgui_setup) {
+ ImImpl_Setup();
+ imgui_setup = true;
+ }
+ auto& io = ImGui::GetIO();
+ auto current_ticks = Clock::QueryHostTickCount();
+ static uint64_t last_ticks = 0;
+ io.DeltaTime =
+ (current_ticks - last_ticks) / float(Clock::host_tick_frequency());
+ last_ticks = current_ticks;
+
+ io.DisplaySize =
+ ImVec2(float(e.target()->width()), float(e.target()->height()));
+
+ BYTE keystate[256];
+ GetKeyboardState(keystate);
+ for (int i = 0; i < 256; i++) io.KeysDown[i] = (keystate[i] & 0x80) != 0;
+ io.KeyCtrl = (keystate[VK_CONTROL] & 0x80) != 0;
+ io.KeyShift = (keystate[VK_SHIFT] & 0x80) != 0;
+
+ ImGui::NewFrame();
+
+ DrawUI(window.get(), player, emulator->memory());
+
+ glViewport(0, 0, (int)io.DisplaySize.x, (int)io.DisplaySize.y);
+ ImGui::Render();
+
+ // Continuous paint.
+ window->Invalidate();
+ });
+ window->Invalidate();
+
+ // Wait until we are exited.
+ loop->AwaitQuit();
+
+ ImImpl_Shutdown();
+
emulator.reset();
+ window.reset();
+ loop.reset();
return 0;
}
diff --git a/src/xenia/kernel/xam_ui.cc b/src/xenia/kernel/xam_ui.cc
index 81d2a9445..46e049a5f 100644
--- a/src/xenia/kernel/xam_ui.cc
+++ b/src/xenia/kernel/xam_ui.cc
@@ -67,7 +67,8 @@ SHIM_CALL XamShowMessageBoxUI_shim(PPCContext* ppc_context,
TASKDIALOGCONFIG config = {0};
config.cbSize = sizeof(config);
config.hInstance = GetModuleHandle(nullptr);
- config.hwndParent = kernel_state->emulator()->display_window()->hwnd();
+ 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;
@@ -120,7 +121,7 @@ SHIM_CALL XamShowDirtyDiscErrorUI_shim(PPCContext* ppc_context,
XELOGD("XamShowDirtyDiscErrorUI(%d)", user_index);
int button_pressed = 0;
- TaskDialog(kernel_state->emulator()->display_window()->hwnd(),
+ 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);
diff --git a/src/xenia/profiling.cc b/src/xenia/profiling.cc
index 4e1589840..1fc186158 100644
--- a/src/xenia/profiling.cc
+++ b/src/xenia/profiling.cc
@@ -33,6 +33,10 @@ std::unique_ptr Profiler::display_ = nullptr;
#if XE_OPTION_PROFILING
+bool Profiler::is_enabled() { return true; }
+
+bool Profiler::is_visible() { return MicroProfileIsDrawing(); }
+
void Profiler::Initialize() {
// Custom groups.
MicroProfileSetEnableAllGroups(false);
@@ -168,6 +172,8 @@ void Profiler::Present() {
#else
+bool Profiler::is_enabled() { return false; }
+bool Profiler::is_visible() { return false; }
void Profiler::Initialize() {}
void Profiler::Dump() {}
void Profiler::Shutdown() {}
diff --git a/src/xenia/profiling.h b/src/xenia/profiling.h
index ee02e5910..6d8da30f3 100644
--- a/src/xenia/profiling.h
+++ b/src/xenia/profiling.h
@@ -148,11 +148,8 @@ class ProfilerDisplay {
class Profiler {
public:
-#if XE_OPTION_PROFILING
- static bool is_enabled() { return true; }
-#else
- static bool is_enabled() { return false; }
-#endif // XE_OPTION_PROFILING
+ static bool is_enabled();
+ static bool is_visible();
// Initializes the profiler. Call at startup.
static void Initialize();
diff --git a/src/xenia/ui/control.cc b/src/xenia/ui/control.cc
deleted file mode 100644
index eb9601b68..000000000
--- a/src/xenia/ui/control.cc
+++ /dev/null
@@ -1,132 +0,0 @@
-/**
- ******************************************************************************
- * Xenia : Xbox 360 Emulator Research Project *
- ******************************************************************************
- * Copyright 2014 Ben Vanik. All rights reserved. *
- * Released under the BSD license - see LICENSE in the root for more details. *
- ******************************************************************************
- */
-
-#include "xenia/ui/control.h"
-
-#include "xenia/base/assert.h"
-
-namespace xe {
-namespace ui {
-
-Control::Control(uint32_t flags)
- : flags_(flags),
- parent_(nullptr),
- width_(0),
- height_(0),
- is_cursor_visible_(true),
- is_enabled_(true),
- is_visible_(true),
- has_focus_(false) {}
-
-Control::~Control() { children_.clear(); }
-
-void Control::AddChild(Control* child_control) {
- AddChild(ControlPtr(child_control, [](Control* control) {}));
-}
-
-void Control::AddChild(std::unique_ptr child_control) {
- AddChild(ControlPtr(child_control.release(),
- [](Control* control) { delete control; }));
-}
-
-void Control::AddChild(ControlPtr control) {
- assert_null(control->parent());
- control->parent_ = this;
- auto control_ptr = control.get();
- children_.emplace_back(std::move(control));
- OnChildAdded(control_ptr);
-}
-
-Control::ControlPtr Control::RemoveChild(Control* child_control) {
- assert_true(child_control->parent() == this);
- for (auto& it = children_.begin(); it != children_.end(); ++it) {
- if (it->get() == child_control) {
- auto control_ptr = std::move(*it);
- child_control->parent_ = nullptr;
- children_.erase(it);
- OnChildRemoved(child_control);
- return control_ptr;
- }
- }
- return ControlPtr(nullptr, [](Control*) {});
-}
-
-void Control::Layout() {
- auto e = UIEvent(this);
- OnLayout(e);
- for (auto& child_control : children_) {
- child_control->OnLayout(e);
- }
-}
-
-void Control::OnResize(UIEvent& e) { on_resize(e); }
-
-void Control::OnLayout(UIEvent& e) { on_layout(e); }
-
-void Control::OnPaint(UIEvent& e) { on_paint(e); }
-
-void Control::OnVisible(UIEvent& e) { on_visible(e); }
-
-void Control::OnHidden(UIEvent& e) { on_hidden(e); }
-
-void Control::OnGotFocus(UIEvent& e) { on_got_focus(e); }
-
-void Control::OnLostFocus(UIEvent& e) { on_lost_focus(e); }
-
-void Control::OnKeyDown(KeyEvent& e) {
- on_key_down(e);
- if (parent_ && !e.is_handled()) {
- parent_->OnKeyDown(e);
- }
-}
-
-void Control::OnKeyUp(KeyEvent& e) {
- on_key_up(e);
- if (parent_ && !e.is_handled()) {
- parent_->OnKeyUp(e);
- }
-}
-
-void Control::OnKeyChar(KeyEvent& e) {
- on_key_char(e);
- if (parent_ && !e.is_handled()) {
- parent_->OnKeyChar(e);
- }
-}
-
-void Control::OnMouseDown(MouseEvent& e) {
- on_mouse_down(e);
- if (parent_ && !e.is_handled()) {
- parent_->OnMouseDown(e);
- }
-}
-
-void Control::OnMouseMove(MouseEvent& e) {
- on_mouse_move(e);
- if (parent_ && !e.is_handled()) {
- parent_->OnMouseMove(e);
- }
-}
-
-void Control::OnMouseUp(MouseEvent& e) {
- on_mouse_up(e);
- if (parent_ && !e.is_handled()) {
- parent_->OnMouseUp(e);
- }
-}
-
-void Control::OnMouseWheel(MouseEvent& e) {
- on_mouse_wheel(e);
- if (parent_ && !e.is_handled()) {
- parent_->OnMouseWheel(e);
- }
-}
-
-} // namespace ui
-} // namespace xe
diff --git a/src/xenia/ui/control.h b/src/xenia/ui/control.h
deleted file mode 100644
index becdc17b6..000000000
--- a/src/xenia/ui/control.h
+++ /dev/null
@@ -1,134 +0,0 @@
-/**
- ******************************************************************************
- * Xenia : Xbox 360 Emulator Research Project *
- ******************************************************************************
- * Copyright 2014 Ben Vanik. All rights reserved. *
- * Released under the BSD license - see LICENSE in the root for more details. *
- ******************************************************************************
- */
-
-#ifndef XENIA_UI_CONTROL_H_
-#define XENIA_UI_CONTROL_H_
-
-#include
-#include
-
-#include "xenia/base/delegate.h"
-#include "xenia/ui/ui_event.h"
-
-namespace xe {
-namespace ui {
-
-class Control {
- public:
- typedef std::unique_ptr ControlPtr;
- enum Flags {
- // Control paints itself, so disable platform drawing.
- kFlagOwnPaint = 1 << 1,
- };
-
- virtual ~Control();
-
- Control* parent() const { return parent_; }
-
- size_t child_count() const { return children_.size(); }
- Control* child(size_t i) const { return children_[i].get(); }
- void AddChild(Control* child_control);
- void AddChild(std::unique_ptr child_control);
- void AddChild(ControlPtr child_control);
- ControlPtr RemoveChild(Control* child_control);
-
- int32_t width() const { return width_; }
- int32_t height() const { return height_; }
- virtual void Resize(int32_t width, int32_t height) = 0;
- virtual void Resize(int32_t left, int32_t top, int32_t right,
- int32_t bottom) = 0;
- void ResizeToFill() { ResizeToFill(0, 0, 0, 0); }
- virtual void ResizeToFill(int32_t pad_left, int32_t pad_top,
- int32_t pad_right, int32_t pad_bottom) = 0;
- void Layout();
- virtual void Invalidate() {}
-
- // TODO(benvanik): colors/brushes/etc.
- // TODO(benvanik): fonts.
-
- bool is_cursor_visible() const { return is_cursor_visible_; }
- virtual void set_cursor_visible(bool value) { is_cursor_visible_ = value; }
-
- bool is_enabled() const { return is_enabled_; }
- virtual void set_enabled(bool value) { is_enabled_ = value; }
-
- bool is_visible() const { return is_visible_; }
- virtual void set_visible(bool value) { is_visible_ = value; }
-
- bool has_focus() const { return has_focus_; }
- virtual void set_focus(bool value) { has_focus_ = value; }
-
- public:
- Delegate on_resize;
- Delegate on_layout;
- Delegate on_paint;
-
- Delegate on_visible;
- Delegate on_hidden;
-
- Delegate on_got_focus;
- Delegate on_lost_focus;
-
- Delegate on_key_down;
- Delegate on_key_up;
- Delegate on_key_char;
-
- Delegate on_mouse_down;
- Delegate on_mouse_move;
- Delegate on_mouse_up;
- Delegate on_mouse_wheel;
-
- protected:
- explicit Control(uint32_t flags);
-
- virtual bool Create() { return true; }
- virtual void Destroy() {}
-
- virtual void OnCreate() {}
- virtual void OnDestroy() {}
-
- virtual void OnChildAdded(Control* child_control) {}
- virtual void OnChildRemoved(Control* child_control) {}
-
- virtual void OnResize(UIEvent& e);
- virtual void OnLayout(UIEvent& e);
- virtual void OnPaint(UIEvent& e);
-
- virtual void OnVisible(UIEvent& e);
- virtual void OnHidden(UIEvent& e);
-
- virtual void OnGotFocus(UIEvent& e);
- virtual void OnLostFocus(UIEvent& e);
-
- virtual void OnKeyDown(KeyEvent& e);
- virtual void OnKeyUp(KeyEvent& e);
- virtual void OnKeyChar(KeyEvent& e);
-
- virtual void OnMouseDown(MouseEvent& e);
- virtual void OnMouseMove(MouseEvent& e);
- virtual void OnMouseUp(MouseEvent& e);
- virtual void OnMouseWheel(MouseEvent& e);
-
- uint32_t flags_;
- Control* parent_;
- std::vector children_;
-
- int32_t width_;
- int32_t height_;
-
- bool is_cursor_visible_;
- bool is_enabled_;
- bool is_visible_;
- bool has_focus_;
-};
-
-} // namespace ui
-} // namespace xe
-
-#endif // XENIA_UI_CONTROL_H_
diff --git a/src/xenia/ui/elemental_control.h b/src/xenia/ui/elemental_control.h
deleted file mode 100644
index 93c43fd76..000000000
--- a/src/xenia/ui/elemental_control.h
+++ /dev/null
@@ -1,85 +0,0 @@
-/**
- ******************************************************************************
- * Xenia : Xbox 360 Emulator Research Project *
- ******************************************************************************
- * Copyright 2015 Ben Vanik. All rights reserved. *
- * Released under the BSD license - see LICENSE in the root for more details. *
- ******************************************************************************
- */
-
-#ifndef XENIA_UI_ELEMENTAL_CONTROL_H_
-#define XENIA_UI_ELEMENTAL_CONTROL_H_
-
-#include
-
-#include "el/element.h"
-#include "el/graphics/renderer.h"
-#include "xenia/ui/control.h"
-#include "xenia/ui/loop.h"
-#include "xenia/ui/platform.h"
-
-namespace xe {
-namespace ui {
-
-class ElementalControl : public PlatformControl {
- public:
- ElementalControl(Loop* loop, uint32_t flags);
- ~ElementalControl() override;
-
- el::graphics::Renderer* renderer() const { return renderer_.get(); }
- el::Element* root_element() const { return root_element_.get(); }
-
- bool LoadLanguage(std::string filename);
- bool LoadSkin(std::string filename);
-
- protected:
- using super = PlatformControl;
-
- bool InitializeElemental(Loop* loop, el::graphics::Renderer* renderer);
- virtual std::unique_ptr CreateRenderer() = 0;
-
- bool Create() override;
- void Destroy() override;
-
- void OnLayout(UIEvent& e) override;
- void OnPaint(UIEvent& e) override;
-
- void OnGotFocus(UIEvent& e) override;
- void OnLostFocus(UIEvent& e) override;
-
- el::ModifierKeys GetModifierKeys();
-
- void OnKeyPress(KeyEvent& e, bool is_down, bool is_char);
- bool CheckShortcutKey(KeyEvent& e, el::SpecialKey special_key, bool is_down);
- void OnKeyDown(KeyEvent& e) override;
- void OnKeyUp(KeyEvent& e) override;
- void OnKeyChar(KeyEvent& e) override;
-
- void OnMouseDown(MouseEvent& e) override;
- void OnMouseMove(MouseEvent& e) override;
- void OnMouseUp(MouseEvent& e) override;
- void OnMouseWheel(MouseEvent& e) override;
-
- Loop* loop_ = nullptr;
- std::unique_ptr renderer_;
- std::unique_ptr root_element_;
-
- uint32_t frame_count_ = 0;
- uint32_t fps_ = 0;
- uint64_t fps_update_time_ = 0;
- uint64_t fps_frame_count_ = 0;
-
- bool modifier_shift_pressed_ = false;
- bool modifier_cntrl_pressed_ = false;
- bool modifier_alt_pressed_ = false;
- bool modifier_super_pressed_ = false;
- uint64_t last_click_time_ = 0;
- int last_click_x_ = 0;
- int last_click_y_ = 0;
- int last_click_counter_ = 0;
-};
-
-} // namespace ui
-} // namespace xe
-
-#endif // XENIA_UI_ELEMENTAL_CONTROL_H_
diff --git a/src/xenia/ui/file_picker.h b/src/xenia/ui/file_picker.h
index 1cd7553bb..c6884f23e 100644
--- a/src/xenia/ui/file_picker.h
+++ b/src/xenia/ui/file_picker.h
@@ -10,6 +10,7 @@
#ifndef XENIA_UI_FILE_PICKER_H_
#define XENIA_UI_FILE_PICKER_H_
+#include
#include
#include
@@ -27,6 +28,8 @@ class FilePicker {
kDirectory = 1,
};
+ static std::unique_ptr Create();
+
FilePicker()
: mode_(Mode::kOpen),
type_(Type::kFile),
diff --git a/src/xenia/ui/win32/win32_file_picker.cc b/src/xenia/ui/file_picker_win.cc
similarity index 95%
rename from src/xenia/ui/win32/win32_file_picker.cc
rename to src/xenia/ui/file_picker_win.cc
index ded97de33..eab1c0e92 100644
--- a/src/xenia/ui/win32/win32_file_picker.cc
+++ b/src/xenia/ui/file_picker_win.cc
@@ -7,14 +7,27 @@
******************************************************************************
*/
-#include "xenia/ui/win32/win32_file_picker.h"
+#include "xenia/ui/file_picker.h"
#include "xenia/base/platform.h"
#include "xenia/base/assert.h"
namespace xe {
namespace ui {
-namespace win32 {
+
+class Win32FilePicker : public FilePicker {
+ public:
+ Win32FilePicker();
+ ~Win32FilePicker() override;
+
+ bool Show(void* parent_window_handle) override;
+
+ private:
+};
+
+std::unique_ptr FilePicker::Create() {
+ return std::make_unique();
+}
class CDialogEventHandler : public IFileDialogEvents,
public IFileDialogControlEvents {
@@ -201,6 +214,5 @@ bool Win32FilePicker::Show(void* parent_window_handle) {
return true;
}
-} // namespace win32
} // namespace ui
} // namespace xe
diff --git a/src/xenia/ui/gl/circular_buffer.cc b/src/xenia/ui/gl/circular_buffer.cc
index 6039e1519..575a37b9e 100644
--- a/src/xenia/ui/gl/circular_buffer.cc
+++ b/src/xenia/ui/gl/circular_buffer.cc
@@ -11,7 +11,6 @@
#include "xenia/base/assert.h"
#include "xenia/base/math.h"
-#include "xenia/gpu/gl4/gl4_gpu_flags.h"
namespace xe {
namespace ui {
diff --git a/src/xenia/ui/gl/gl4_elemental_renderer.cc b/src/xenia/ui/gl/gl4_elemental_renderer.cc
index 1aa675c3f..7eba88d45 100644
--- a/src/xenia/ui/gl/gl4_elemental_renderer.cc
+++ b/src/xenia/ui/gl/gl4_elemental_renderer.cc
@@ -28,7 +28,7 @@ GL4ElementalRenderer::GL4Bitmap::GL4Bitmap(GLContext* context,
: context_(context), renderer_(renderer) {}
GL4ElementalRenderer::GL4Bitmap::~GL4Bitmap() {
- GLContextLock lock(context_);
+ GraphicsContextLock lock(context_);
// Must flush and unbind before we delete the texture.
renderer_->FlushBitmap(this);
@@ -69,7 +69,7 @@ GL4ElementalRenderer::GL4ElementalRenderer(GLContext* context)
vertex_buffer_(max_vertex_batch_size() * sizeof(Vertex)) {}
GL4ElementalRenderer::~GL4ElementalRenderer() {
- GLContextLock lock(context_);
+ GraphicsContextLock lock(context_);
vertex_buffer_.Shutdown();
glDeleteVertexArrays(1, &vao_);
glDeleteProgram(program_);
@@ -77,6 +77,7 @@ GL4ElementalRenderer::~GL4ElementalRenderer() {
std::unique_ptr GL4ElementalRenderer::Create(
GLContext* context) {
+ GraphicsContextLock lock(context);
auto renderer = std::make_unique(context);
if (!renderer->Initialize()) {
XELOGE("Failed to initialize TurboBadger GL4 renderer");
diff --git a/src/xenia/ui/gl/gl_context.cc b/src/xenia/ui/gl/gl_context.cc
index 2fb4f0329..02979439f 100644
--- a/src/xenia/ui/gl/gl_context.cc
+++ b/src/xenia/ui/gl/gl_context.cc
@@ -16,6 +16,9 @@
#include "xenia/base/logging.h"
#include "xenia/base/math.h"
#include "xenia/profiling.h"
+#include "xenia/ui/gl/gl_profiler_display.h"
+#include "xenia/ui/gl/gl4_elemental_renderer.h"
+#include "xenia/ui/window.h"
DEFINE_bool(thread_safe_gl, false,
"Only allow one GL context to be active at a time.");
@@ -40,11 +43,20 @@ thread_local WGLEWContext* tls_wglew_context_ = nullptr;
extern "C" GLEWContext* glewGetContext() { return tls_glew_context_; }
extern "C" WGLEWContext* wglewGetContext() { return tls_wglew_context_; }
-GLContext::GLContext() : hwnd_(nullptr), dc_(nullptr), glrc_(nullptr) {}
+std::unique_ptr GLContext::Create(Window* target_window) {
+ auto context = std::unique_ptr(new GLContext(target_window));
+ if (!context->Initialize(target_window)) {
+ return nullptr;
+ }
+ context->AssertExtensionsPresent();
+ return context;
+}
-GLContext::GLContext(HWND hwnd, HGLRC glrc)
- : hwnd_(hwnd), dc_(nullptr), glrc_(glrc) {
- dc_ = GetDC(hwnd);
+GLContext::GLContext(Window* target_window) : GraphicsContext(target_window) {}
+
+GLContext::GLContext(Window* target_window, HGLRC glrc)
+ : GraphicsContext(target_window), glrc_(glrc) {
+ dc_ = GetDC(HWND(target_window_->native_handle()));
}
GLContext::~GLContext() {
@@ -55,13 +67,13 @@ GLContext::~GLContext() {
wglDeleteContext(glrc_);
}
if (dc_) {
- ReleaseDC(hwnd_, dc_);
+ ReleaseDC(HWND(target_window_->native_handle()), dc_);
}
}
-bool GLContext::Initialize(HWND hwnd) {
- hwnd_ = hwnd;
- dc_ = GetDC(hwnd);
+bool GLContext::Initialize(Window* target_window) {
+ target_window_ = target_window;
+ dc_ = GetDC(HWND(target_window_->native_handle()));
PIXELFORMATDESCRIPTOR pfd = {0};
pfd.nSize = sizeof(pfd);
@@ -152,12 +164,12 @@ bool GLContext::Initialize(HWND hwnd) {
return true;
}
-std::unique_ptr GLContext::CreateShared() {
+std::unique_ptr GLContext::CreateShared() {
assert_not_null(glrc_);
HGLRC new_glrc = nullptr;
{
- GLContextLock context_lock(this);
+ GraphicsContextLock context_lock(this);
int context_flags = 0;
// int profile = WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB;
@@ -178,7 +190,8 @@ std::unique_ptr GLContext::CreateShared() {
}
}
- auto new_context = std::make_unique(hwnd_, new_glrc);
+ auto new_context =
+ std::unique_ptr(new GLContext(target_window_, new_glrc));
if (!new_context->MakeCurrent()) {
XELOGE("Could not make new GL context current");
return nullptr;
@@ -205,7 +218,7 @@ std::unique_ptr GLContext::CreateShared() {
new_context->ClearCurrent();
- return new_context;
+ return std::unique_ptr(new_context.release());
}
void FatalGLError(std::string error) {
@@ -369,6 +382,14 @@ void GLContext::SetupDebugging() {
this);
}
+std::unique_ptr GLContext::CreateProfilerDisplay() {
+ return std::make_unique(target_window_);
+}
+
+std::unique_ptr GLContext::CreateElementalRenderer() {
+ return GL4ElementalRenderer::Create(this);
+}
+
bool GLContext::MakeCurrent() {
SCOPE_profile_cpu_f("gpu");
if (FLAGS_thread_safe_gl) {
@@ -399,6 +420,17 @@ void GLContext::ClearCurrent() {
}
}
+void GLContext::BeginSwap() {
+ SCOPE_profile_cpu_i("gpu", "xe::ui::gl::GLContext::BeginSwap");
+ float clear_color[] = {rand() / (float)RAND_MAX, 1.0f, 0, 1.0f};
+ glClearNamedFramebufferfv(0, GL_COLOR, 0, clear_color);
+}
+
+void GLContext::EndSwap() {
+ SCOPE_profile_cpu_i("gpu", "xe::ui::gl::GLContext::EndSwap");
+ SwapBuffers(dc());
+}
+
} // namespace gl
} // namespace ui
} // namespace xe
diff --git a/src/xenia/ui/gl/gl_context.h b/src/xenia/ui/gl/gl_context.h
index d8ae4f766..17b3b275b 100644
--- a/src/xenia/ui/gl/gl_context.h
+++ b/src/xenia/ui/gl/gl_context.h
@@ -16,6 +16,7 @@
#include "xenia/ui/gl/blitter.h"
#include "xenia/ui/gl/gl.h"
+#include "xenia/ui/graphics_context.h"
DECLARE_bool(thread_safe_gl);
@@ -23,25 +24,33 @@ namespace xe {
namespace ui {
namespace gl {
-class GLContext {
+class GLContext : public GraphicsContext {
public:
- GLContext();
- GLContext(HWND hwnd, HGLRC glrc);
- ~GLContext();
+ static std::unique_ptr Create(Window* target_window);
- bool Initialize(HWND hwnd);
- void AssertExtensionsPresent();
+ ~GLContext() override;
HDC dc() const { return dc_; }
- std::unique_ptr CreateShared();
+ std::unique_ptr CreateShared() override;
+ std::unique_ptr CreateProfilerDisplay() override;
+ std::unique_ptr CreateElementalRenderer() override;
- bool MakeCurrent();
- void ClearCurrent();
+ bool MakeCurrent() override;
+ void ClearCurrent() override;
+
+ void BeginSwap() override;
+ void EndSwap() override;
Blitter* blitter() { return &blitter_; }
private:
+ GLContext(Window* target_window);
+ GLContext(Window* target_window, HGLRC glrc);
+
+ bool Initialize(Window* target_window);
+ void AssertExtensionsPresent();
+
void SetupDebugging();
void DebugMessage(GLenum source, GLenum type, GLuint id, GLenum severity,
GLsizei length, const GLchar* message);
@@ -49,9 +58,8 @@ class GLContext {
DebugMessageThunk(GLenum source, GLenum type, GLuint id, GLenum severity,
GLsizei length, const GLchar* message, GLvoid* user_param);
- HWND hwnd_;
- HDC dc_;
- HGLRC glrc_;
+ HDC dc_ = nullptr;
+ HGLRC glrc_ = nullptr;
GLEWContext glew_context_;
WGLEWContext wglew_context_;
@@ -59,16 +67,6 @@ class GLContext {
Blitter blitter_;
};
-struct GLContextLock {
- GLContextLock(GLContext* context) : context_(context) {
- context_->MakeCurrent();
- }
- ~GLContextLock() { context_->ClearCurrent(); }
-
- private:
- GLContext* context_;
-};
-
} // namespace gl
} // namespace ui
} // namespace xe
diff --git a/src/xenia/ui/gl/gl_profiler_display.cc b/src/xenia/ui/gl/gl_profiler_display.cc
index 3ea38cdd0..5a3f070ff 100644
--- a/src/xenia/ui/gl/gl_profiler_display.cc
+++ b/src/xenia/ui/gl/gl_profiler_display.cc
@@ -132,47 +132,56 @@ const uint8_t profiler_font[] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};
-GLProfilerDisplay::GLProfilerDisplay(xe::ui::gl::WGLControl* control)
- : control_(control),
- program_(0),
- vao_(0),
- font_texture_(0),
- font_handle_(0),
+GLProfilerDisplay::GLProfilerDisplay(xe::ui::Window* window)
+ : window_(window),
vertex_buffer_(MICROPROFILE_MAX_VERTICES * sizeof(Vertex) * 10,
- sizeof(Vertex)),
- draw_command_count_(0) {
+ sizeof(Vertex)) {
if (!SetupFont() || !SetupState() || !SetupShaders()) {
// Hrm.
assert_always();
}
+ window_->on_painted.AddListener([this](UIEvent& e) { Profiler::Present(); });
+
// Pass through mouse events.
- control->on_mouse_down.AddListener([](xe::ui::MouseEvent& e) {
- Profiler::OnMouseDown(e.button() == xe::ui::MouseEvent::Button::kLeft,
- e.button() == xe::ui::MouseEvent::Button::kRight);
- e.set_handled(true);
+ window_->on_mouse_down.AddListener([](xe::ui::MouseEvent& e) {
+ if (Profiler::is_enabled()) {
+ Profiler::OnMouseDown(e.button() == xe::ui::MouseEvent::Button::kLeft,
+ e.button() == xe::ui::MouseEvent::Button::kRight);
+ e.set_handled(true);
+ }
});
- control->on_mouse_up.AddListener([](xe::ui::MouseEvent& e) {
- Profiler::OnMouseUp();
- e.set_handled(true);
+ window_->on_mouse_up.AddListener([](xe::ui::MouseEvent& e) {
+ if (Profiler::is_enabled()) {
+ Profiler::OnMouseUp();
+ e.set_handled(true);
+ }
});
- control->on_mouse_move.AddListener([](xe::ui::MouseEvent& e) {
- Profiler::OnMouseMove(e.x(), e.y());
- e.set_handled(true);
+ window_->on_mouse_move.AddListener([](xe::ui::MouseEvent& e) {
+ if (Profiler::is_enabled()) {
+ Profiler::OnMouseMove(e.x(), e.y());
+ e.set_handled(true);
+ }
});
- control->on_mouse_wheel.AddListener([](xe::ui::MouseEvent& e) {
- Profiler::OnMouseWheel(e.x(), e.y(), -e.dy());
- e.set_handled(true);
+ window_->on_mouse_wheel.AddListener([](xe::ui::MouseEvent& e) {
+ if (Profiler::is_enabled()) {
+ Profiler::OnMouseWheel(e.x(), e.y(), -e.dy());
+ e.set_handled(true);
+ }
});
// Watch for toggle/mode keys and such.
- control->on_key_down.AddListener([](xe::ui::KeyEvent& e) {
- Profiler::OnKeyDown(e.key_code());
- // e.set_handled(true);
+ window_->on_key_down.AddListener([](xe::ui::KeyEvent& e) {
+ if (Profiler::is_enabled()) {
+ Profiler::OnKeyDown(e.key_code());
+ e.set_handled(true);
+ }
});
- control->on_key_up.AddListener([](xe::ui::KeyEvent& e) {
- Profiler::OnKeyUp(e.key_code());
- // e.set_handled(true);
+ window_->on_key_up.AddListener([](xe::ui::KeyEvent& e) {
+ if (Profiler::is_enabled()) {
+ Profiler::OnKeyUp(e.key_code());
+ e.set_handled(true);
+ }
});
}
@@ -336,9 +345,9 @@ GLProfilerDisplay::~GLProfilerDisplay() {
glDeleteProgram(program_);
}
-uint32_t GLProfilerDisplay::width() const { return control_->width(); }
+uint32_t GLProfilerDisplay::width() const { return window_->width(); }
-uint32_t GLProfilerDisplay::height() const { return control_->height(); }
+uint32_t GLProfilerDisplay::height() const { return window_->height(); }
void GLProfilerDisplay::Begin() {
glEnablei(GL_BLEND, 0);
@@ -347,7 +356,7 @@ void GLProfilerDisplay::Begin() {
glDisable(GL_STENCIL_TEST);
glDisable(GL_SCISSOR_TEST);
- glViewport(0, 0, control_->width(), control_->height());
+ glViewport(0, 0, width(), height());
float left = 0.0f;
float right = float(width());
diff --git a/src/xenia/ui/gl/gl_profiler_display.h b/src/xenia/ui/gl/gl_profiler_display.h
index ca029115b..b98dd35c3 100644
--- a/src/xenia/ui/gl/gl_profiler_display.h
+++ b/src/xenia/ui/gl/gl_profiler_display.h
@@ -12,8 +12,7 @@
#include "xenia/profiling.h"
#include "xenia/ui/gl/circular_buffer.h"
-#include "xenia/ui/gl/gl_context.h"
-#include "xenia/ui/gl/wgl_control.h"
+#include "xenia/ui/window.h"
namespace xe {
namespace ui {
@@ -21,7 +20,7 @@ namespace gl {
class GLProfilerDisplay : public ProfilerDisplay {
public:
- GLProfilerDisplay(xe::ui::gl::WGLControl* control);
+ GLProfilerDisplay(xe::ui::Window* window);
virtual ~GLProfilerDisplay();
uint32_t width() const override;
@@ -54,11 +53,11 @@ class GLProfilerDisplay : public ProfilerDisplay {
void EndVertices(GLenum prim_type);
void Flush();
- xe::ui::gl::WGLControl* control_;
- GLuint program_;
- GLuint vao_;
- GLuint font_texture_;
- GLuint64 font_handle_;
+ xe::ui::Window* window_ = nullptr;
+ GLuint program_ = 0;
+ GLuint vao_ = 0;
+ GLuint font_texture_ = 0;
+ GLuint64 font_handle_ = 0;
CircularBuffer vertex_buffer_;
static const size_t kMaxCommands = 32;
@@ -67,13 +66,13 @@ class GLProfilerDisplay : public ProfilerDisplay {
size_t vertex_offset;
size_t vertex_count;
} draw_commands_[kMaxCommands];
- uint32_t draw_command_count_;
+ uint32_t draw_command_count_ = 0;
CircularBuffer::Allocation current_allocation_;
struct {
uint16_t char_offsets[256];
- } font_description_;
+ } font_description_ = {0};
};
} // namespace gl
diff --git a/src/xenia/ui/gl/wgl_control.cc b/src/xenia/ui/gl/wgl_control.cc
deleted file mode 100644
index 86abb78e4..000000000
--- a/src/xenia/ui/gl/wgl_control.cc
+++ /dev/null
@@ -1,125 +0,0 @@
-/**
- ******************************************************************************
- * Xenia : Xbox 360 Emulator Research Project *
- ******************************************************************************
- * Copyright 2014 Ben Vanik. All rights reserved. *
- * Released under the BSD license - see LICENSE in the root for more details. *
- ******************************************************************************
- */
-
-#include "xenia/ui/gl/wgl_control.h"
-
-#include "xenia/base/assert.h"
-#include "xenia/base/logging.h"
-#include "xenia/profiling.h"
-
-namespace xe {
-namespace ui {
-namespace gl {
-
-WGLControl::WGLControl(Loop* loop)
- : xe::ui::win32::Win32Control(Flags::kFlagOwnPaint), loop_(loop) {}
-
-WGLControl::~WGLControl() = default;
-
-bool WGLControl::Create() {
- HINSTANCE hInstance = GetModuleHandle(nullptr);
-
- WNDCLASSEX wcex;
- wcex.cbSize = sizeof(WNDCLASSEX);
- wcex.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
- wcex.lpfnWndProc = Win32Control::WndProcThunk;
- wcex.cbClsExtra = 0;
- wcex.cbWndExtra = 0;
- wcex.hInstance = hInstance;
- wcex.hIcon = nullptr;
- wcex.hIconSm = nullptr;
- wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
- wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
- wcex.lpszMenuName = nullptr;
- wcex.lpszClassName = L"XeniaWglClass";
- if (!RegisterClassEx(&wcex)) {
- XELOGE("WGL RegisterClassEx failed");
- return false;
- }
-
- // Create window.
- DWORD window_style = WS_CHILD | WS_VISIBLE | SS_NOTIFY;
- DWORD window_ex_style = 0;
- hwnd_ =
- CreateWindowEx(window_ex_style, L"XeniaWglClass", L"Xenia", window_style,
- CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
- parent_hwnd(), nullptr, hInstance, this);
- if (!hwnd_) {
- XELOGE("WGL CreateWindow failed");
- return false;
- }
-
- if (!context_.Initialize(hwnd_)) {
- XEFATAL(
- "Unable to initialize GL context. Xenia requires OpenGL 4.5. Ensure "
- "you have the latest drivers for your GPU and that it supports OpenGL "
- "4.5. See http://xenia.jp/faq/ for more information.");
- return false;
- }
-
- context_.AssertExtensionsPresent();
-
- SetFocus(hwnd_);
-
- OnCreate();
- return true;
-}
-
-void WGLControl::OnLayout(UIEvent& e) { Control::ResizeToFill(); }
-
-LRESULT WGLControl::WndProc(HWND hWnd, UINT message, WPARAM wParam,
- LPARAM lParam) {
- switch (message) {
- case WM_PAINT: {
- invalidated_ = false;
- ValidateRect(hWnd, nullptr);
- SCOPE_profile_cpu_i("gpu", "xe::gpu::gl4::WGLControl::WM_PAINT");
- {
- GLContextLock context_lock(&context_);
-
- float clear_color[] = {rand() / (float)RAND_MAX, 1.0f, 0, 1.0f};
- glClearNamedFramebufferfv(0, GL_COLOR, 0, clear_color);
-
- if (current_paint_callback_) {
- current_paint_callback_();
- current_paint_callback_ = nullptr;
- }
-
- UIEvent e(this);
- OnPaint(e);
-
- // TODO(benvanik): profiler present.
- Profiler::Present();
- }
- {
- SCOPE_profile_cpu_i("gpu", "xe::gpu::gl4::WGLControl::SwapBuffers");
- SwapBuffers(context_.dc());
- }
- return 0;
- } break;
- }
- return Win32Control::WndProc(hWnd, message, wParam, lParam);
-}
-
-void WGLControl::SynchronousRepaint(std::function paint_callback) {
- SCOPE_profile_cpu_f("gpu");
-
- // We may already have a pending paint from a previous request when we
- // were minimized. We just overwrite it.
- current_paint_callback_ = std::move(paint_callback);
-
- // This will not return until the WM_PAINT has completed.
- // Note, if we are minimized this won't do anything.
- RedrawWindow(hwnd(), nullptr, nullptr,
- RDW_INTERNALPAINT | RDW_UPDATENOW | RDW_ALLCHILDREN);
-}
-
-} // namespace gl
-} // namespace ui
-} // namespace xe
diff --git a/src/xenia/ui/gl/wgl_control.h b/src/xenia/ui/gl/wgl_control.h
deleted file mode 100644
index 322e85541..000000000
--- a/src/xenia/ui/gl/wgl_control.h
+++ /dev/null
@@ -1,51 +0,0 @@
-/**
- ******************************************************************************
- * Xenia : Xbox 360 Emulator Research Project *
- ******************************************************************************
- * Copyright 2014 Ben Vanik. All rights reserved. *
- * Released under the BSD license - see LICENSE in the root for more details. *
- ******************************************************************************
- */
-
-#ifndef XENIA_UI_GL_WGL_CONTROL_H_
-#define XENIA_UI_GL_WGL_CONTROL_H_
-
-#include
-
-#include "xenia/base/threading.h"
-#include "xenia/ui/gl/gl_context.h"
-#include "xenia/ui/loop.h"
-#include "xenia/ui/win32/win32_control.h"
-
-namespace xe {
-namespace ui {
-namespace gl {
-
-class WGLControl : public xe::ui::win32::Win32Control {
- public:
- WGLControl(Loop* loop);
- ~WGLControl() override;
-
- GLContext* context() { return &context_; }
-
- void SynchronousRepaint(std::function paint_callback);
-
- protected:
- bool Create() override;
-
- void OnLayout(UIEvent& e) override;
-
- LRESULT WndProc(HWND hWnd, UINT message, WPARAM wParam,
- LPARAM lParam) override;
-
- private:
- Loop* loop_;
- GLContext context_;
- std::function current_paint_callback_;
-};
-
-} // namespace gl
-} // namespace ui
-} // namespace xe
-
-#endif // XENIA_UI_GL_WGL_CONTROL_H_
diff --git a/src/xenia/ui/gl/wgl_elemental_control.cc b/src/xenia/ui/gl/wgl_elemental_control.cc
deleted file mode 100644
index 0ff7470a8..000000000
--- a/src/xenia/ui/gl/wgl_elemental_control.cc
+++ /dev/null
@@ -1,140 +0,0 @@
-/**
- ******************************************************************************
- * Xenia : Xbox 360 Emulator Research Project *
- ******************************************************************************
- * Copyright 2015 Ben Vanik. All rights reserved. *
- * Released under the BSD license - see LICENSE in the root for more details. *
- ******************************************************************************
- */
-
-#include "xenia/ui/gl/wgl_elemental_control.h"
-
-#include
-
-#include "el/util/math.h"
-#include "xenia/base/assert.h"
-#include "xenia/base/logging.h"
-#include "xenia/profiling.h"
-#include "xenia/ui/gl/circular_buffer.h"
-#include "xenia/ui/gl/gl_context.h"
-#include "xenia/ui/gl/gl.h"
-#include "xenia/ui/gl/gl4_elemental_renderer.h"
-
-namespace xe {
-namespace ui {
-namespace gl {
-
-WGLElementalControl::WGLElementalControl(Loop* loop)
- : ElementalControl(loop, Flags::kFlagOwnPaint), loop_(loop) {}
-
-WGLElementalControl::~WGLElementalControl() = default;
-
-bool WGLElementalControl::Create() {
- HINSTANCE hInstance = GetModuleHandle(nullptr);
-
- WNDCLASSEX wcex;
- wcex.cbSize = sizeof(WNDCLASSEX);
- wcex.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
- wcex.lpfnWndProc = Win32Control::WndProcThunk;
- wcex.cbClsExtra = 0;
- wcex.cbWndExtra = 0;
- wcex.hInstance = hInstance;
- wcex.hIcon = nullptr;
- wcex.hIconSm = nullptr;
- wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
- wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
- wcex.lpszMenuName = nullptr;
- wcex.lpszClassName = L"XeniaWglElementalClass";
- if (!RegisterClassEx(&wcex)) {
- XELOGE("WGL RegisterClassEx failed");
- return false;
- }
-
- // Create window.
- DWORD window_style = WS_CHILD | WS_VISIBLE | SS_NOTIFY;
- DWORD window_ex_style = 0;
- hwnd_ =
- CreateWindowEx(window_ex_style, L"XeniaWglElementalClass", L"Xenia",
- window_style, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
- CW_USEDEFAULT, parent_hwnd(), nullptr, hInstance, this);
- if (!hwnd_) {
- XELOGE("WGL CreateWindow failed");
- return false;
- }
-
- if (!context_.Initialize(hwnd_)) {
- XEFATAL(
- "Unable to initialize GL context. Xenia requires OpenGL 4.5. Ensure "
- "you have the latest drivers for your GPU and that it supports OpenGL "
- "4.5. See http://xenia.jp/faq/ for more information.");
- return false;
- }
-
- context_.AssertExtensionsPresent();
-
- SetFocus(hwnd_);
-
- return super::Create();
-}
-
-std::unique_ptr WGLElementalControl::CreateRenderer() {
- return GL4ElementalRenderer::Create(&context_);
-}
-
-void WGLElementalControl::OnLayout(UIEvent& e) {
- Control::ResizeToFill();
- super::OnLayout(e);
-}
-
-LRESULT WGLElementalControl::WndProc(HWND hWnd, UINT message, WPARAM wParam,
- LPARAM lParam) {
- switch (message) {
- case WM_PAINT: {
- invalidated_ = false;
- ValidateRect(hWnd, nullptr);
- SCOPE_profile_cpu_i("gpu", "xe::ui::gl::WGLElementalControl::WM_PAINT");
- {
- GLContextLock context_lock(&context_);
-
- float clear_color[] = {rand() / (float)RAND_MAX, 1.0f, 0, 1.0f};
- glClearNamedFramebufferfv(0, GL_COLOR, 0, clear_color);
-
- if (current_paint_callback_) {
- current_paint_callback_();
- current_paint_callback_ = nullptr;
- }
-
- UIEvent e(this);
- OnPaint(e);
-
- // TODO(benvanik): profiler present.
- Profiler::Present();
- }
- {
- SCOPE_profile_cpu_i("gpu",
- "xe::ui::gl::WGLElementalControl::SwapBuffers");
- SwapBuffers(context_.dc());
- }
- return 0;
- } break;
- }
- return Win32Control::WndProc(hWnd, message, wParam, lParam);
-}
-
-void WGLElementalControl::SynchronousRepaint(
- std::function paint_callback) {
- SCOPE_profile_cpu_f("gpu");
-
- // We may already have a pending paint from a previous request when we
- // were minimized. We just overwrite it.
- current_paint_callback_ = std::move(paint_callback);
-
- // This will not return until the WM_PAINT has completed.
- // Note, if we are minimized this won't do anything.
- RedrawWindow(hwnd(), nullptr, nullptr,
- RDW_INTERNALPAINT | RDW_UPDATENOW | RDW_ALLCHILDREN);
-}
-
-} // namespace gl
-} // namespace ui
-} // namespace xe
diff --git a/src/xenia/ui/gl/wgl_elemental_control.h b/src/xenia/ui/gl/wgl_elemental_control.h
deleted file mode 100644
index deb0627e7..000000000
--- a/src/xenia/ui/gl/wgl_elemental_control.h
+++ /dev/null
@@ -1,55 +0,0 @@
-/**
- ******************************************************************************
- * Xenia : Xbox 360 Emulator Research Project *
- ******************************************************************************
- * Copyright 2014 Ben Vanik. All rights reserved. *
- * Released under the BSD license - see LICENSE in the root for more details. *
- ******************************************************************************
- */
-
-#ifndef XENIA_UI_GL_WGL_ELEMENTAL_CONTROL_H_
-#define XENIA_UI_GL_WGL_ELEMENTAL_CONTROL_H_
-
-#include
-
-#include "xenia/base/threading.h"
-#include "xenia/ui/elemental_control.h"
-#include "xenia/ui/gl/gl_context.h"
-#include "xenia/ui/loop.h"
-
-namespace xe {
-namespace ui {
-namespace gl {
-
-class WGLElementalControl : public ElementalControl {
- public:
- WGLElementalControl(Loop* loop);
- ~WGLElementalControl() override;
-
- GLContext* context() { return &context_; }
-
- void SynchronousRepaint(std::function paint_callback);
-
- protected:
- using super = ElementalControl;
-
- std::unique_ptr CreateRenderer() override;
-
- bool Create() override;
-
- void OnLayout(UIEvent& e) override;
-
- LRESULT WndProc(HWND hWnd, UINT message, WPARAM wParam,
- LPARAM lParam) override;
-
- private:
- Loop* loop_;
- GLContext context_;
- std::function current_paint_callback_;
-};
-
-} // namespace gl
-} // namespace ui
-} // namespace xe
-
-#endif // XENIA_UI_GL_WGL_ELEMENTAL_CONTROL_H_
diff --git a/src/xenia/ui/win32/win32_file_picker.h b/src/xenia/ui/graphics_context.cc
similarity index 59%
rename from src/xenia/ui/win32/win32_file_picker.h
rename to src/xenia/ui/graphics_context.cc
index ade3c2ba2..99fe9d91f 100644
--- a/src/xenia/ui/win32/win32_file_picker.h
+++ b/src/xenia/ui/graphics_context.cc
@@ -7,27 +7,15 @@
******************************************************************************
*/
-#ifndef XENIA_UI_WIN32_WIN32_FILE_PICKER_H_
-#define XENIA_UI_WIN32_WIN32_FILE_PICKER_H_
-
-#include "xenia/ui/file_picker.h"
+#include "xenia/ui/graphics_context.h"
namespace xe {
namespace ui {
-namespace win32 {
-class Win32FilePicker : public FilePicker {
- public:
- Win32FilePicker();
- ~Win32FilePicker() override;
+GraphicsContext::GraphicsContext(Window* target_window)
+ : target_window_(target_window) {}
- bool Show(void* parent_window_handle) override;
+GraphicsContext::~GraphicsContext() = default;
- private:
-};
-
-} // namespace win32
} // namespace ui
} // namespace xe
-
-#endif // XENIA_UI_WIN32_WIN32_FILE_PICKER_H_
diff --git a/src/xenia/ui/graphics_context.h b/src/xenia/ui/graphics_context.h
new file mode 100644
index 000000000..5f2cfc919
--- /dev/null
+++ b/src/xenia/ui/graphics_context.h
@@ -0,0 +1,58 @@
+/**
+ ******************************************************************************
+ * Xenia : Xbox 360 Emulator Research Project *
+ ******************************************************************************
+ * Copyright 2015 Ben Vanik. All rights reserved. *
+ * Released under the BSD license - see LICENSE in the root for more details. *
+ ******************************************************************************
+ */
+
+#ifndef XENIA_UI_GRAPHICS_CONTEXT_H_
+#define XENIA_UI_GRAPHICS_CONTEXT_H_
+
+#include
+
+#include "el/graphics/renderer.h"
+#include "xenia/profiling.h"
+
+namespace xe {
+namespace ui {
+
+class Window;
+
+class GraphicsContext {
+ public:
+ virtual ~GraphicsContext();
+
+ Window* target_window() const { return target_window_; }
+
+ virtual std::unique_ptr CreateShared() = 0;
+ virtual std::unique_ptr CreateProfilerDisplay() = 0;
+ virtual std::unique_ptr CreateElementalRenderer() = 0;
+
+ virtual bool MakeCurrent() = 0;
+ virtual void ClearCurrent() = 0;
+
+ virtual void BeginSwap() = 0;
+ virtual void EndSwap() = 0;
+
+ protected:
+ explicit GraphicsContext(Window* target_window);
+
+ Window* target_window_ = nullptr;
+};
+
+struct GraphicsContextLock {
+ GraphicsContextLock(GraphicsContext* context) : context_(context) {
+ context_->MakeCurrent();
+ }
+ ~GraphicsContextLock() { context_->ClearCurrent(); }
+
+ private:
+ GraphicsContext* context_;
+};
+
+} // namespace ui
+} // namespace xe
+
+#endif // XENIA_UI_GRAPHICS_CONTEXT_H_
diff --git a/src/xenia/ui/loop.cc b/src/xenia/ui/loop.cc
new file mode 100644
index 000000000..aacb98955
--- /dev/null
+++ b/src/xenia/ui/loop.cc
@@ -0,0 +1,82 @@
+/**
+ ******************************************************************************
+ * Xenia : Xbox 360 Emulator Research Project *
+ ******************************************************************************
+ * Copyright 2014 Ben Vanik. All rights reserved. *
+ * Released under the BSD license - see LICENSE in the root for more details. *
+ ******************************************************************************
+ */
+
+#include "xenia/ui/loop.h"
+
+#include "el/message_handler.h"
+#include "el/util/metrics.h"
+#include "el/util/timer.h"
+#include "xenia/base/assert.h"
+#include "xenia/base/threading.h"
+
+namespace xe {
+namespace ui {
+
+// The loop designated as being the main loop elemental-forms will use for all
+// activity.
+// TODO(benvanik): refactor elemental-forms to now need a global.
+Loop* elemental_loop_ = nullptr;
+
+Loop* Loop::elemental_loop() { return elemental_loop_; }
+
+void Loop::set_elemental_loop(Loop* loop) {
+ assert_null(elemental_loop_);
+ elemental_loop_ = loop;
+}
+
+Loop::Loop() = default;
+
+Loop::~Loop() {
+ if (this == elemental_loop_) {
+ elemental_loop_ = nullptr;
+ }
+}
+
+void Loop::PostSynchronous(std::function fn) {
+ xe::threading::Fence fence;
+ Post([&fn, &fence]() {
+ fn();
+ fence.Signal();
+ });
+ fence.Wait();
+}
+
+} // namespace ui
+} // namespace xe
+
+void el::util::RescheduleTimer(uint64_t fire_time) {
+ assert_not_null(xe::ui::elemental_loop_);
+ if (fire_time == el::MessageHandler::kNotSoon) {
+ return;
+ }
+
+ uint64_t now = el::util::GetTimeMS();
+ uint64_t delay_millis = fire_time >= now ? fire_time - now : 0;
+ xe::ui::elemental_loop_->PostDelayed([]() {
+ uint64_t next_fire_time = el::MessageHandler::GetNextMessageFireTime();
+ uint64_t now = el::util::GetTimeMS();
+ if (now < next_fire_time) {
+ // We timed out *before* we were supposed to (the OS is not playing
+ // nice).
+ // Calling ProcessMessages now won't achieve a thing so force a
+ // reschedule
+ // of the platform timer again with the same time.
+ // ReschedulePlatformTimer(next_fire_time, true);
+ return;
+ }
+
+ el::MessageHandler::ProcessMessages();
+
+ // If we still have things to do (because we didn't process all
+ // messages,
+ // or because there are new messages), we need to rescedule, so call
+ // RescheduleTimer.
+ el::util::RescheduleTimer(el::MessageHandler::GetNextMessageFireTime());
+ }, delay_millis);
+}
diff --git a/src/xenia/ui/loop.h b/src/xenia/ui/loop.h
index 272202c34..4310e1330 100644
--- a/src/xenia/ui/loop.h
+++ b/src/xenia/ui/loop.h
@@ -11,20 +11,34 @@
#define XENIA_UI_LOOP_H_
#include
+#include
+
+#include "xenia/base/delegate.h"
+#include "xenia/ui/ui_event.h"
namespace xe {
namespace ui {
class Loop {
public:
- Loop() = default;
- virtual ~Loop() = default;
+ static std::unique_ptr Create();
+
+ static Loop* elemental_loop();
+ // Sets the loop designated as being the main loop elemental-forms will use
+ // for all activity.
+ static void set_elemental_loop(Loop* loop);
+
+ Loop();
+ virtual ~Loop();
virtual void Post(std::function fn) = 0;
virtual void PostDelayed(std::function fn, uint64_t delay_millis) = 0;
+ void PostSynchronous(std::function fn);
virtual void Quit() = 0;
virtual void AwaitQuit() = 0;
+
+ Delegate on_quit;
};
} // namespace ui
diff --git a/src/xenia/ui/win32/win32_loop.cc b/src/xenia/ui/loop_win.cc
similarity index 96%
rename from src/xenia/ui/win32/win32_loop.cc
rename to src/xenia/ui/loop_win.cc
index 6ae28b368..0ce1a59d2 100644
--- a/src/xenia/ui/win32/win32_loop.cc
+++ b/src/xenia/ui/loop_win.cc
@@ -7,13 +7,12 @@
******************************************************************************
*/
-#include "xenia/ui/win32/win32_loop.h"
+#include "xenia/ui/loop_win.h"
#include "xenia/base/assert.h"
namespace xe {
namespace ui {
-namespace win32 {
const DWORD kWmWin32LoopPost = WM_APP + 0x100;
const DWORD kWmWin32LoopQuit = WM_APP + 0x101;
@@ -27,6 +26,8 @@ class PostedFn {
std::function fn_;
};
+std::unique_ptr Loop::Create() { return std::make_unique(); }
+
Win32Loop::Win32Loop() : thread_id_(0) {
timer_queue_ = CreateTimerQueue();
@@ -81,6 +82,9 @@ void Win32Loop::ThreadMain() {
break;
}
}
+
+ UIEvent e(nullptr);
+ on_quit(e);
}
void Win32Loop::Post(std::function fn) {
@@ -132,6 +136,5 @@ void Win32Loop::Quit() {
void Win32Loop::AwaitQuit() { quit_fence_.Wait(); }
-} // namespace win32
} // namespace ui
} // namespace xe
diff --git a/src/xenia/ui/win32/win32_loop.h b/src/xenia/ui/loop_win.h
similarity index 90%
rename from src/xenia/ui/win32/win32_loop.h
rename to src/xenia/ui/loop_win.h
index e0abb5913..c9732378f 100644
--- a/src/xenia/ui/win32/win32_loop.h
+++ b/src/xenia/ui/loop_win.h
@@ -7,8 +7,8 @@
******************************************************************************
*/
-#ifndef XENIA_UI_WIN32_WIN32_LOOP_H_
-#define XENIA_UI_WIN32_WIN32_LOOP_H_
+#ifndef XENIA_UI_LOOP_WIN_H_
+#define XENIA_UI_LOOP_WIN_H_
#include
#include
@@ -21,7 +21,6 @@
namespace xe {
namespace ui {
-namespace win32 {
class Win32Loop : public Loop {
public:
@@ -55,8 +54,7 @@ class Win32Loop : public Loop {
std::list pending_timers_;
};
-} // namespace win32
} // namespace ui
} // namespace xe
-#endif // XENIA_UI_WIN32_WIN32_LOOP_H_
+#endif // XENIA_UI_LOOP_WIN_H_
diff --git a/src/xenia/ui/main_resources.rc b/src/xenia/ui/main_resources.rc
new file mode 100644
index 000000000..5db04ad85
--- /dev/null
+++ b/src/xenia/ui/main_resources.rc
@@ -0,0 +1,5 @@
+//{{NO_DEPENDENCIES}}
+
+#include "third_party\\elemental-forms\\resources.rc"
+
+//IDR_xe_ui_main_resources_skin_bg_tile_png RCDATA ".\\resources\\skin\\bg_tile.png"
diff --git a/src/xenia/ui/main_window.cc b/src/xenia/ui/main_window.cc
deleted file mode 100644
index 95be90a2a..000000000
--- a/src/xenia/ui/main_window.cc
+++ /dev/null
@@ -1,264 +0,0 @@
-/**
- ******************************************************************************
- * Xenia : Xbox 360 Emulator Research Project *
- ******************************************************************************
- * Copyright 2014 Ben Vanik. All rights reserved. *
- * Released under the BSD license - see LICENSE in the root for more details. *
- ******************************************************************************
- */
-
-#include "xenia/ui/main_window.h"
-
-#include "xenia/base/clock.h"
-#include "xenia/base/logging.h"
-#include "xenia/base/platform.h"
-#include "xenia/base/threading.h"
-#include "xenia/gpu/graphics_system.h"
-#include "xenia/emulator.h"
-#include "xenia/profiling.h"
-
-namespace xe {
-namespace ui {
-
-enum Commands {
- IDC_FILE_EXIT,
-
- IDC_CPU_TIME_SCALAR_RESET,
- IDC_CPU_TIME_SCALAR_HALF,
- IDC_CPU_TIME_SCALAR_DOUBLE,
-
- IDC_CPU_PROFILER_TOGGLE_DISPLAY,
- IDC_CPU_PROFILER_TOGGLE_PAUSE,
-
- IDC_GPU_TRACE_FRAME,
- IDC_GPU_CLEAR_CACHES,
-
- IDC_WINDOW_FULLSCREEN,
-
- IDC_HELP_WEBSITE,
- IDC_HELP_ABOUT,
-};
-
-const std::wstring kBaseTitle = L"xenia";
-
-MainWindow::MainWindow(Emulator* emulator)
- : PlatformWindow(&loop_, kBaseTitle),
- emulator_(emulator),
- main_menu_(MenuItem::Type::kNormal) {}
-
-MainWindow::~MainWindow() = default;
-
-std::unique_ptr MainWindow::Create(Emulator* emulator) {
- std::unique_ptr main_window(new MainWindow(emulator));
-
- xe::threading::Fence fence;
-
- main_window->loop()->Post([&main_window, &fence]() {
- xe::threading::set_name("Win32 Loop");
- xe::Profiler::ThreadEnter("Win32 Loop");
-
- if (!main_window->Initialize()) {
- XEFATAL("Failed to initialize main window");
- exit(1);
- }
-
- fence.Signal();
- });
-
- fence.Wait();
-
- return std::unique_ptr(main_window.release());
-}
-
-bool MainWindow::Initialize() {
- if (!PlatformWindow::Initialize()) {
- return false;
- }
-
- UpdateTitle();
- on_key_down.AddListener([this](KeyEvent& e) {
- bool handled = true;
- switch (e.key_code()) {
- case 0x0D: { // numpad enter
- OnCommand(Commands::IDC_CPU_TIME_SCALAR_RESET);
- } break;
- case 0x6D: { // numpad minus
- OnCommand(Commands::IDC_CPU_TIME_SCALAR_HALF);
- } break;
- case 0x6B: { // numpad plus
- OnCommand(Commands::IDC_CPU_TIME_SCALAR_DOUBLE);
- } break;
-
- case 0x73: { // VK_F4
- OnCommand(Commands::IDC_GPU_TRACE_FRAME);
- } break;
- case 0x74: { // VK_F5
- OnCommand(Commands::IDC_GPU_CLEAR_CACHES);
- } break;
-
- case 0x7A: { // VK_F11
- OnCommand(Commands::IDC_WINDOW_FULLSCREEN);
- } break;
- case 0x1B: { // VK_ESCAPE
- // Allow users to escape fullscreen (but not enter it).
- if (is_fullscreen()) {
- ToggleFullscreen(false);
- }
- } break;
-
- case 0x70: { // VK_F1
- OnCommand(Commands::IDC_HELP_WEBSITE);
- } break;
-
- default: { handled = false; } break;
- }
- e.set_handled(handled);
- });
-
- // Main menu.
- // FIXME: This code is really messy.
- auto file_menu =
- std::make_unique(MenuItem::Type::kPopup, L"&File");
- {
- file_menu->AddChild(std::make_unique(
- MenuItem::Type::kString, Commands::IDC_FILE_EXIT, L"E&xit", L"Alt+F4"));
- }
- main_menu_.AddChild(std::move(file_menu));
-
- // CPU menu.
- auto cpu_menu =
- std::make_unique(MenuItem::Type::kPopup, L"&CPU");
- {
- cpu_menu->AddChild(std::make_unique(
- MenuItem::Type::kString, Commands::IDC_CPU_TIME_SCALAR_RESET,
- L"&Reset Time Scalar", L"Numpad Enter"));
- cpu_menu->AddChild(std::make_unique(
- MenuItem::Type::kString, Commands::IDC_CPU_TIME_SCALAR_HALF,
- L"Time Scalar /= 2", L"Numpad -"));
- cpu_menu->AddChild(std::make_unique(
- MenuItem::Type::kString, Commands::IDC_CPU_TIME_SCALAR_DOUBLE,
- L"Time Scalar *= 2", L"Numpad +"));
- }
- cpu_menu->AddChild(
- std::make_unique(MenuItem::Type::kSeparator));
- {
- cpu_menu->AddChild(std::make_unique(
- MenuItem::Type::kString, Commands::IDC_CPU_PROFILER_TOGGLE_DISPLAY,
- L"Toggle Profiler &Display", L"Tab"));
- cpu_menu->AddChild(std::make_unique(
- MenuItem::Type::kString, Commands::IDC_CPU_PROFILER_TOGGLE_PAUSE,
- L"&Pause/Resume Profiler", L"`"));
- }
- main_menu_.AddChild(std::move(cpu_menu));
-
- // GPU menu.
- auto gpu_menu =
- std::make_unique(MenuItem::Type::kPopup, L"&GPU");
- {
- gpu_menu->AddChild(std::make_unique(
- MenuItem::Type::kString, Commands::IDC_GPU_TRACE_FRAME, L"&Trace Frame",
- L"F4"));
- }
- gpu_menu->AddChild(
- std::make_unique(MenuItem::Type::kSeparator));
- {
- gpu_menu->AddChild(std::make_unique(
- MenuItem::Type::kString, Commands::IDC_GPU_CLEAR_CACHES,
- L"&Clear Caches", L"F5"));
- }
- main_menu_.AddChild(std::move(gpu_menu));
-
- // Window menu.
- auto window_menu =
- std::make_unique(MenuItem::Type::kPopup, L"&Window");
- {
- window_menu->AddChild(std::make_unique(
- MenuItem::Type::kString, Commands::IDC_WINDOW_FULLSCREEN,
- L"&Fullscreen", L"F11"));
- }
- main_menu_.AddChild(std::move(window_menu));
-
- // Help menu.
- auto help_menu =
- std::make_unique(MenuItem::Type::kPopup, L"&Help");
- {
- help_menu->AddChild(std::make_unique(
- MenuItem::Type::kString, Commands::IDC_HELP_WEBSITE, L"&Website...",
- L"F1"));
- help_menu->AddChild(std::make_unique(
- MenuItem::Type::kString, Commands::IDC_HELP_ABOUT, L"&About..."));
- }
- main_menu_.AddChild(std::move(help_menu));
-
- set_menu(&main_menu_);
-
- Resize(1280, 720);
-
- return true;
-}
-
-void MainWindow::UpdateTitle() {
- std::wstring title(kBaseTitle);
- if (Clock::guest_time_scalar() != 1.0) {
- title += L" (@";
- title += xe::to_wstring(std::to_string(Clock::guest_time_scalar()));
- title += L"x)";
- }
- set_title(title);
-}
-
-void MainWindow::OnClose() {
- loop_.Quit();
-
- // TODO(benvanik): proper exit.
- XELOGI("User-initiated death!");
- exit(1);
-}
-
-void MainWindow::OnCommand(int id) {
- switch (id) {
- case IDC_FILE_EXIT: {
- Close();
- } break;
-
- case IDC_CPU_TIME_SCALAR_RESET: {
- Clock::set_guest_time_scalar(1.0);
- UpdateTitle();
- } break;
- case IDC_CPU_TIME_SCALAR_HALF: {
- Clock::set_guest_time_scalar(Clock::guest_time_scalar() / 2.0);
- UpdateTitle();
- } break;
- case IDC_CPU_TIME_SCALAR_DOUBLE: {
- Clock::set_guest_time_scalar(Clock::guest_time_scalar() * 2.0);
- UpdateTitle();
- } break;
- case IDC_CPU_PROFILER_TOGGLE_DISPLAY: {
- Profiler::ToggleDisplay();
- } break;
- case IDC_CPU_PROFILER_TOGGLE_PAUSE: {
- Profiler::TogglePause();
- } break;
-
- case IDC_GPU_TRACE_FRAME: {
- emulator()->graphics_system()->RequestFrameTrace();
- } break;
- case IDC_GPU_CLEAR_CACHES: {
- emulator()->graphics_system()->ClearCaches();
- } break;
-
- case IDC_WINDOW_FULLSCREEN: {
- ToggleFullscreen(!is_fullscreen());
- } break;
-
- case IDC_HELP_WEBSITE: {
- LaunchBrowser("http://xenia.jp");
- } break;
- case IDC_HELP_ABOUT: {
- LaunchBrowser("http://xenia.jp/about/");
- } break;
- }
-}
-
-} // namespace ui
-} // namespace xe
diff --git a/src/xenia/ui/main_window.h b/src/xenia/ui/main_window.h
deleted file mode 100644
index 3f5a9bbcc..000000000
--- a/src/xenia/ui/main_window.h
+++ /dev/null
@@ -1,50 +0,0 @@
-/**
- ******************************************************************************
- * Xenia : Xbox 360 Emulator Research Project *
- ******************************************************************************
- * Copyright 2014 Ben Vanik. All rights reserved. *
- * Released under the BSD license - see LICENSE in the root for more details. *
- ******************************************************************************
- */
-
-#ifndef XENIA_UI_MAIN_WINDOW_H_
-#define XENIA_UI_MAIN_WINDOW_H_
-
-#include "xenia/ui/platform.h"
-#include "xenia/ui/window.h"
-#include "xenia/xbox.h"
-
-namespace xe {
-class Emulator;
-} // namespace xe
-
-namespace xe {
-namespace ui {
-
-class MainWindow : public PlatformWindow {
- public:
- ~MainWindow() override;
-
- static std::unique_ptr Create(Emulator* emulator);
-
- Emulator* emulator() const { return emulator_; }
-
- private:
- explicit MainWindow(Emulator* emulator);
-
- bool Initialize();
-
- void UpdateTitle();
-
- void OnClose() override;
- void OnCommand(int id) override;
-
- Emulator* emulator_;
- PlatformLoop loop_;
- PlatformMenu main_menu_;
-};
-
-} // namespace ui
-} // namespace xe
-
-#endif // XENIA_UI_MAIN_WINDOW_H_
diff --git a/src/xenia/ui/menu_item.cc b/src/xenia/ui/menu_item.cc
index e77ebae61..06f8c9774 100644
--- a/src/xenia/ui/menu_item.cc
+++ b/src/xenia/ui/menu_item.cc
@@ -12,11 +12,27 @@
namespace xe {
namespace ui {
-MenuItem::MenuItem(Type type) : type_(type), parent_item_(nullptr) {}
+std::unique_ptr