Massive refactoring of xenia::ui and GL swap behavior.

This seems to dramatically improve most games (especially with
--vsync=false), though it may cause swap issues with others.
New code should be easier to port, and enables elemental-forms to be
drawn for any emulator UI.
This commit is contained in:
Ben Vanik 2015-07-12 22:03:47 -07:00
parent e69c7b00a8
commit 15c17459be
61 changed files with 1994 additions and 2623 deletions

View File

@ -165,22 +165,18 @@
<ClCompile Include="src\xenia\kernel\xobject.cc" />
<ClCompile Include="src\xenia\memory.cc" />
<ClCompile Include="src\xenia\profiling.cc" />
<ClCompile Include="src\xenia\ui\control.cc" />
<ClCompile Include="src\xenia\ui\elemental_control.cc" />
<ClCompile Include="src\xenia\ui\file_picker_win.cc" />
<ClCompile Include="src\xenia\ui\gl\blitter.cc" />
<ClCompile Include="src\xenia\ui\gl\circular_buffer.cc" />
<ClCompile Include="src\xenia\ui\gl\gl4_elemental_renderer.cc" />
<ClCompile Include="src\xenia\ui\gl\gl_context.cc" />
<ClCompile Include="src\xenia\ui\gl\gl_profiler_display.cc" />
<ClCompile Include="src\xenia\ui\gl\wgl_control.cc" />
<ClCompile Include="src\xenia\ui\gl\wgl_elemental_control.cc" />
<ClCompile Include="src\xenia\ui\main_window.cc" />
<ClCompile Include="src\xenia\ui\graphics_context.cc" />
<ClCompile Include="src\xenia\ui\loop.cc" />
<ClCompile Include="src\xenia\ui\loop_win.cc" />
<ClCompile Include="src\xenia\ui\window.cc" />
<ClCompile Include="src\xenia\ui\menu_item.cc" />
<ClCompile Include="src\xenia\ui\win32\win32_control.cc" />
<ClCompile Include="src\xenia\ui\win32\win32_file_picker.cc" />
<ClCompile Include="src\xenia\ui\win32\win32_loop.cc" />
<ClCompile Include="src\xenia\ui\win32\win32_menu_item.cc" />
<ClCompile Include="src\xenia\ui\win32\win32_window.cc" />
<ClCompile Include="src\xenia\ui\window_win.cc" />
<ClCompile Include="src\xenia\vfs\device.cc" />
<ClCompile Include="src\xenia\vfs\devices\disc_image_device.cc" />
<ClCompile Include="src\xenia\vfs\devices\disc_image_entry.cc" />
@ -396,8 +392,6 @@
<ClInclude Include="src\xenia\kernel\xobject.h" />
<ClInclude Include="src\xenia\memory.h" />
<ClInclude Include="src\xenia\profiling.h" />
<ClInclude Include="src\xenia\ui\control.h" />
<ClInclude Include="src\xenia\ui\elemental_control.h" />
<ClInclude Include="src\xenia\ui\file_picker.h" />
<ClInclude Include="src\xenia\ui\gl\blitter.h" />
<ClInclude Include="src\xenia\ui\gl\circular_buffer.h" />
@ -405,19 +399,13 @@
<ClInclude Include="src\xenia\ui\gl\gl4_elemental_renderer.h" />
<ClInclude Include="src\xenia\ui\gl\gl_context.h" />
<ClInclude Include="src\xenia\ui\gl\gl_profiler_display.h" />
<ClInclude Include="src\xenia\ui\gl\wgl_control.h" />
<ClInclude Include="src\xenia\ui\gl\wgl_elemental_control.h" />
<ClInclude Include="src\xenia\ui\graphics_context.h" />
<ClInclude Include="src\xenia\ui\loop.h" />
<ClInclude Include="src\xenia\ui\main_window.h" />
<ClInclude Include="src\xenia\ui\loop_win.h" />
<ClInclude Include="src\xenia\ui\menu_item.h" />
<ClInclude Include="src\xenia\ui\platform.h" />
<ClInclude Include="src\xenia\ui\ui_event.h" />
<ClInclude Include="src\xenia\ui\win32\win32_control.h" />
<ClInclude Include="src\xenia\ui\win32\win32_file_picker.h" />
<ClInclude Include="src\xenia\ui\win32\win32_loop.h" />
<ClInclude Include="src\xenia\ui\win32\win32_menu_item.h" />
<ClInclude Include="src\xenia\ui\win32\win32_window.h" />
<ClInclude Include="src\xenia\ui\window.h" />
<ClInclude Include="src\xenia\ui\window_win.h" />
<ClInclude Include="src\xenia\vfs\device.h" />
<ClInclude Include="src\xenia\vfs\devices\disc_image_device.h" />
<ClInclude Include="src\xenia\vfs\devices\disc_image_entry.h" />

View File

@ -68,9 +68,6 @@
<Filter Include="src\xenia\ui">
<UniqueIdentifier>{42d47a43-1af4-4e1a-9ed7-afa7f7d18e9f}</UniqueIdentifier>
</Filter>
<Filter Include="src\xenia\ui\win32">
<UniqueIdentifier>{268545c9-fbdf-46d2-96f6-35188cec09d6}</UniqueIdentifier>
</Filter>
<Filter Include="src\xenia\kernel">
<UniqueIdentifier>{c1ac0db1-2f4b-4376-b1dc-e6355c99b395}</UniqueIdentifier>
</Filter>
@ -159,7 +156,7 @@
<UniqueIdentifier>{82795389-e855-4cd6-a3b6-9580030cebf2}</UniqueIdentifier>
</Filter>
<Filter Include="src\xenia\ui\gl">
<UniqueIdentifier>{c38dacd1-1e4c-4cd1-847e-19b394e8313d}</UniqueIdentifier>
<UniqueIdentifier>{f598eed5-11dc-4ef8-a7d4-28ec5e43009b}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
@ -409,24 +406,6 @@
<ClCompile Include="src\xenia\hid\xinput\xinput_input_driver.cc">
<Filter>src\xenia\hid\xinput</Filter>
</ClCompile>
<ClCompile Include="src\xenia\ui\win32\win32_control.cc">
<Filter>src\xenia\ui\win32</Filter>
</ClCompile>
<ClCompile Include="src\xenia\ui\win32\win32_loop.cc">
<Filter>src\xenia\ui\win32</Filter>
</ClCompile>
<ClCompile Include="src\xenia\ui\win32\win32_menu_item.cc">
<Filter>src\xenia\ui\win32</Filter>
</ClCompile>
<ClCompile Include="src\xenia\ui\win32\win32_window.cc">
<Filter>src\xenia\ui\win32</Filter>
</ClCompile>
<ClCompile Include="src\xenia\ui\control.cc">
<Filter>src\xenia\ui</Filter>
</ClCompile>
<ClCompile Include="src\xenia\ui\main_window.cc">
<Filter>src\xenia\ui</Filter>
</ClCompile>
<ClCompile Include="src\xenia\ui\menu_item.cc">
<Filter>src\xenia\ui</Filter>
</ClCompile>
@ -655,9 +634,6 @@
<ClCompile Include="src\xenia\apu\xma_context.cc">
<Filter>src\xenia\apu</Filter>
</ClCompile>
<ClCompile Include="src\xenia\ui\win32\win32_file_picker.cc">
<Filter>src\xenia\ui\win32</Filter>
</ClCompile>
<ClCompile Include="src\xenia\hid\hid_flags.cc">
<Filter>src\xenia\hid</Filter>
</ClCompile>
@ -715,30 +691,39 @@
<ClCompile Include="src\xenia\vfs\virtual_file_system.cc">
<Filter>src\xenia\vfs</Filter>
</ClCompile>
<ClCompile Include="src\xenia\ui\window.cc">
<Filter>src\xenia\ui</Filter>
</ClCompile>
<ClCompile Include="src\xenia\ui\window_win.cc">
<Filter>src\xenia\ui</Filter>
</ClCompile>
<ClCompile Include="src\xenia\ui\loop_win.cc">
<Filter>src\xenia\ui</Filter>
</ClCompile>
<ClCompile Include="src\xenia\ui\loop.cc">
<Filter>src\xenia\ui</Filter>
</ClCompile>
<ClCompile Include="src\xenia\ui\gl\blitter.cc">
<Filter>src\xenia\ui\gl</Filter>
</ClCompile>
<ClCompile Include="src\xenia\ui\gl\gl_context.cc">
<Filter>src\xenia\ui\gl</Filter>
</ClCompile>
<ClCompile Include="src\xenia\ui\gl\wgl_control.cc">
<Filter>src\xenia\ui\gl</Filter>
</ClCompile>
<ClCompile Include="src\xenia\ui\gl\gl_profiler_display.cc">
<Filter>src\xenia\ui\gl</Filter>
</ClCompile>
<ClCompile Include="src\xenia\ui\gl\circular_buffer.cc">
<Filter>src\xenia\ui\gl</Filter>
</ClCompile>
<ClCompile Include="src\xenia\ui\elemental_control.cc">
<Filter>src\xenia\ui</Filter>
<ClCompile Include="src\xenia\ui\gl\gl_context.cc">
<Filter>src\xenia\ui\gl</Filter>
</ClCompile>
<ClCompile Include="src\xenia\ui\gl\wgl_elemental_control.cc">
<ClCompile Include="src\xenia\ui\gl\gl_profiler_display.cc">
<Filter>src\xenia\ui\gl</Filter>
</ClCompile>
<ClCompile Include="src\xenia\ui\gl\gl4_elemental_renderer.cc">
<Filter>src\xenia\ui\gl</Filter>
</ClCompile>
<ClCompile Include="src\xenia\ui\graphics_context.cc">
<Filter>src\xenia\ui</Filter>
</ClCompile>
<ClCompile Include="src\xenia\ui\file_picker_win.cc">
<Filter>src\xenia\ui</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="src\xenia\emulator.h">
@ -1008,27 +993,9 @@
<ClInclude Include="src\xenia\hid\xinput\xinput_input_driver.h">
<Filter>src\xenia\hid\xinput</Filter>
</ClInclude>
<ClInclude Include="src\xenia\ui\win32\win32_window.h">
<Filter>src\xenia\ui\win32</Filter>
</ClInclude>
<ClInclude Include="src\xenia\ui\win32\win32_control.h">
<Filter>src\xenia\ui\win32</Filter>
</ClInclude>
<ClInclude Include="src\xenia\ui\win32\win32_loop.h">
<Filter>src\xenia\ui\win32</Filter>
</ClInclude>
<ClInclude Include="src\xenia\ui\win32\win32_menu_item.h">
<Filter>src\xenia\ui\win32</Filter>
</ClInclude>
<ClInclude Include="src\xenia\ui\control.h">
<Filter>src\xenia\ui</Filter>
</ClInclude>
<ClInclude Include="src\xenia\ui\loop.h">
<Filter>src\xenia\ui</Filter>
</ClInclude>
<ClInclude Include="src\xenia\ui\main_window.h">
<Filter>src\xenia\ui</Filter>
</ClInclude>
<ClInclude Include="src\xenia\ui\menu_item.h">
<Filter>src\xenia\ui</Filter>
</ClInclude>
@ -1296,9 +1263,6 @@
<ClInclude Include="src\xenia\ui\file_picker.h">
<Filter>src\xenia\ui</Filter>
</ClInclude>
<ClInclude Include="src\xenia\ui\win32\win32_file_picker.h">
<Filter>src\xenia\ui\win32</Filter>
</ClInclude>
<ClInclude Include="src\xenia\hid\hid_flags.h">
<Filter>src\xenia\hid</Filter>
</ClInclude>
@ -1362,36 +1326,33 @@
<ClInclude Include="src\xenia\cpu\backend\x64\x64_stack_layout.h">
<Filter>src\xenia\cpu\backend\x64</Filter>
</ClInclude>
<ClInclude Include="src\xenia\ui\loop_win.h">
<Filter>src\xenia\ui</Filter>
</ClInclude>
<ClInclude Include="src\xenia\ui\window_win.h">
<Filter>src\xenia\ui</Filter>
</ClInclude>
<ClInclude Include="src\xenia\ui\gl\blitter.h">
<Filter>src\xenia\ui\gl</Filter>
</ClInclude>
<ClInclude Include="src\xenia\ui\gl\gl_context.h">
<Filter>src\xenia\ui\gl</Filter>
</ClInclude>
<ClInclude Include="src\xenia\ui\gl\wgl_control.h">
<Filter>src\xenia\ui\gl</Filter>
</ClInclude>
<ClInclude Include="src\xenia\ui\gl\gl_profiler_display.h">
<Filter>src\xenia\ui\gl</Filter>
</ClInclude>
<ClInclude Include="src\xenia\ui\gl\circular_buffer.h">
<Filter>src\xenia\ui\gl</Filter>
</ClInclude>
<ClInclude Include="src\xenia\ui\platform.h">
<Filter>src\xenia\ui</Filter>
</ClInclude>
<ClInclude Include="src\xenia\ui\gl\gl.h">
<Filter>src\xenia\ui\gl</Filter>
</ClInclude>
<ClInclude Include="src\xenia\ui\elemental_control.h">
<Filter>src\xenia\ui</Filter>
<ClInclude Include="src\xenia\ui\gl\gl_context.h">
<Filter>src\xenia\ui\gl</Filter>
</ClInclude>
<ClInclude Include="src\xenia\ui\gl\wgl_elemental_control.h">
<ClInclude Include="src\xenia\ui\gl\gl_profiler_display.h">
<Filter>src\xenia\ui\gl</Filter>
</ClInclude>
<ClInclude Include="src\xenia\ui\gl\gl4_elemental_renderer.h">
<Filter>src\xenia\ui\gl</Filter>
</ClInclude>
<ClInclude Include="src\xenia\ui\graphics_context.h">
<Filter>src\xenia\ui</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<None Include="src\xenia\cpu\hir\opcodes.inl">

View File

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

View File

@ -12,7 +12,7 @@
#include <memory>
#include "xenia/ui/platform.h"
#include "xenia/ui/loop.h"
namespace xe {
namespace debug {
@ -27,7 +27,7 @@ class Application {
static std::unique_ptr<Application> 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<xe::ui::Loop> loop_;
std::unique_ptr<MainWindow> main_window_;
};

View File

@ -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<PlatformMenu>(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<PlatformMenu>(
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<PlatformMenu>(MenuItem::Type::kPopup, L"&Help");
auto help_menu = MenuItem::Create(MenuItem::Type::kPopup, L"&Help");
{
help_menu->AddChild(std::make_unique<PlatformMenu>(
MenuItem::Type::kString, Commands::IDC_HELP_WEBSITE, L"&Website...",
L"F1"));
help_menu->AddChild(std::make_unique<PlatformMenu>(
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<xe::ui::gl::WGLElementalControl>(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<el::Window>();
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

View File

@ -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<xe::ui::ElementalControl> control_;
std::unique_ptr<xe::ui::Window> platform_window_;
std::unique_ptr<el::Window> window_;
struct {
el::SplitContainer* split_container;

View File

@ -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<Memory>();
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::XboxkrnlModule>();
kernel_state_->LoadKernelModule<kernel::XamModule>();
// 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;
}

View File

@ -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<ui::PlatformWindow> display_window_;
ui::Window* display_window_;
std::unique_ptr<Memory> memory_;

View File

@ -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> EmulatorWindow::Create(Emulator* emulator) {
std::unique_ptr<EmulatorWindow> 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

View File

@ -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 <memory>
#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<EmulatorWindow> 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<ui::Loop> loop_;
std::unique_ptr<ui::Window> window_;
};
} // namespace xe
#endif // XENIA_EMULATOR_WINDOW_H_

View File

@ -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<xe::ui::gl::GLContext> context) {
std::unique_ptr<xe::ui::GraphicsContext> 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<xe::mutex> 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<xe::mutex> 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<xe::mutex> 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<xe::ui::gl::GLContext*>(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<xe::mutex> 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<xe::ui::gl::GLContext*>(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);
}

View File

@ -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<void(const SwapParameters& params)> 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<xe::ui::gl::GLContext> context);
bool Initialize(std::unique_ptr<xe::ui::GraphicsContext> context);
void Shutdown();
void CallInThread(std::function<void()> 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<void()> 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<bool> worker_running_;
kernel::object_ref<kernel::XHostThread> worker_thread_;
std::unique_ptr<xe::ui::gl::GLContext> context_;
SwapHandler swap_handler_;
std::queue<std::function<void()>> pending_fns_;
std::unique_ptr<xe::ui::GraphicsContext> context_;
SwapMode swap_mode_;
SwapState swap_state_;
std::function<void()> swap_request_handler_;
std::queue<std::function<void()>> 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<CachedFramebuffer> cached_framebuffers_;
std::vector<CachedColorRenderTarget> cached_color_render_targets_;

View File

@ -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<GraphicsSystem> GL4GraphicsSystem::Create(Emulator* emulator) {
return std::make_unique<GL4GraphicsSystem>(emulator);
}
std::unique_ptr<ui::GraphicsContext> 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<xe::ui::gl::GLContext*>(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<xe::ui::gl::GLContext> 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<xe::ui::gl::WGLControl>(target_loop_);
target_window_->AddChild(control_.get());
std::unique_ptr<xe::ui::GraphicsContext> 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<xe::ui::gl::GLProfilerDisplay>(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<xe::mutex> 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) {

View File

@ -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<GraphicsSystem> Create(Emulator* emulator);
std::unique_ptr<ui::GraphicsContext> 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 &register_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<CommandProcessor> command_processor_;
std::unique_ptr<xe::ui::gl::WGLControl> control_;
xe::ui::gl::GLContext* display_context_ = nullptr;
std::atomic<bool> worker_running_;
kernel::object_ref<kernel::XHostThread> worker_thread_;

View File

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

View File

@ -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<GraphicsSystem> Create(Emulator* emulator);
virtual std::unique_ptr<ui::GraphicsContext> 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;

View File

@ -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<gl4::GL4GraphicsSystem*>(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<std::wstring>& args) {
// Create the emulator.
// Create the emulator but don't initialize so we can setup the window.
auto emulator = std::make_unique<Emulator>(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;
}

View File

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

View File

@ -33,6 +33,10 @@ std::unique_ptr<ProfilerDisplay> 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() {}

View File

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

View File

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

View File

@ -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 <memory>
#include <vector>
#include "xenia/base/delegate.h"
#include "xenia/ui/ui_event.h"
namespace xe {
namespace ui {
class Control {
public:
typedef std::unique_ptr<Control, void (*)(Control*)> 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<Control> 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<UIEvent> on_resize;
Delegate<UIEvent> on_layout;
Delegate<UIEvent> on_paint;
Delegate<UIEvent> on_visible;
Delegate<UIEvent> on_hidden;
Delegate<UIEvent> on_got_focus;
Delegate<UIEvent> on_lost_focus;
Delegate<KeyEvent> on_key_down;
Delegate<KeyEvent> on_key_up;
Delegate<KeyEvent> on_key_char;
Delegate<MouseEvent> on_mouse_down;
Delegate<MouseEvent> on_mouse_move;
Delegate<MouseEvent> on_mouse_up;
Delegate<MouseEvent> 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<ControlPtr> 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_

View File

@ -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 <memory>
#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<el::graphics::Renderer> 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<el::graphics::Renderer> renderer_;
std::unique_ptr<el::Element> 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_

View File

@ -10,6 +10,7 @@
#ifndef XENIA_UI_FILE_PICKER_H_
#define XENIA_UI_FILE_PICKER_H_
#include <memory>
#include <string>
#include <vector>
@ -27,6 +28,8 @@ class FilePicker {
kDirectory = 1,
};
static std::unique_ptr<FilePicker> Create();
FilePicker()
: mode_(Mode::kOpen),
type_(Type::kFile),

View File

@ -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> FilePicker::Create() {
return std::make_unique<Win32FilePicker>();
}
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

View File

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

View File

@ -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> GL4ElementalRenderer::Create(
GLContext* context) {
GraphicsContextLock lock(context);
auto renderer = std::make_unique<GL4ElementalRenderer>(context);
if (!renderer->Initialize()) {
XELOGE("Failed to initialize TurboBadger GL4 renderer");

View File

@ -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> GLContext::Create(Window* target_window) {
auto context = std::unique_ptr<GLContext>(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> GLContext::CreateShared() {
std::unique_ptr<GraphicsContext> 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> GLContext::CreateShared() {
}
}
auto new_context = std::make_unique<GLContext>(hwnd_, new_glrc);
auto new_context =
std::unique_ptr<GLContext>(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> GLContext::CreateShared() {
new_context->ClearCurrent();
return new_context;
return std::unique_ptr<GraphicsContext>(new_context.release());
}
void FatalGLError(std::string error) {
@ -369,6 +382,14 @@ void GLContext::SetupDebugging() {
this);
}
std::unique_ptr<ProfilerDisplay> GLContext::CreateProfilerDisplay() {
return std::make_unique<GLProfilerDisplay>(target_window_);
}
std::unique_ptr<el::graphics::Renderer> 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

View File

@ -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<GLContext> Create(Window* target_window);
bool Initialize(HWND hwnd);
void AssertExtensionsPresent();
~GLContext() override;
HDC dc() const { return dc_; }
std::unique_ptr<GLContext> CreateShared();
std::unique_ptr<GraphicsContext> CreateShared() override;
std::unique_ptr<ProfilerDisplay> CreateProfilerDisplay() override;
std::unique_ptr<el::graphics::Renderer> 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

View File

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

View File

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

View File

@ -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<void()> 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

View File

@ -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 <functional>
#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<void()> 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<void()> current_paint_callback_;
};
} // namespace gl
} // namespace ui
} // namespace xe
#endif // XENIA_UI_GL_WGL_CONTROL_H_

View File

@ -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 <memory>
#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<el::graphics::Renderer> 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<void()> 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

View File

@ -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 <functional>
#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<void()> paint_callback);
protected:
using super = ElementalControl;
std::unique_ptr<el::graphics::Renderer> 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<void()> current_paint_callback_;
};
} // namespace gl
} // namespace ui
} // namespace xe
#endif // XENIA_UI_GL_WGL_ELEMENTAL_CONTROL_H_

View File

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

View File

@ -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 <memory>
#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<GraphicsContext> CreateShared() = 0;
virtual std::unique_ptr<ProfilerDisplay> CreateProfilerDisplay() = 0;
virtual std::unique_ptr<el::graphics::Renderer> 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_

82
src/xenia/ui/loop.cc Normal file
View File

@ -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<void()> 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);
}

View File

@ -11,20 +11,34 @@
#define XENIA_UI_LOOP_H_
#include <functional>
#include <memory>
#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<Loop> 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<void()> fn) = 0;
virtual void PostDelayed(std::function<void()> fn, uint64_t delay_millis) = 0;
void PostSynchronous(std::function<void()> fn);
virtual void Quit() = 0;
virtual void AwaitQuit() = 0;
Delegate<UIEvent> on_quit;
};
} // namespace ui

View File

@ -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<void()> fn_;
};
std::unique_ptr<Loop> Loop::Create() { return std::make_unique<Win32Loop>(); }
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<void()> fn) {
@ -132,6 +136,5 @@ void Win32Loop::Quit() {
void Win32Loop::AwaitQuit() { quit_fence_.Wait(); }
} // namespace win32
} // namespace ui
} // namespace xe

View File

@ -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 <mutex>
#include <list>
@ -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<PendingTimer*> pending_timers_;
};
} // namespace win32
} // namespace ui
} // namespace xe
#endif // XENIA_UI_WIN32_WIN32_LOOP_H_
#endif // XENIA_UI_LOOP_WIN_H_

View File

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

View File

@ -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<PlatformWindow> MainWindow::Create(Emulator* emulator) {
std::unique_ptr<MainWindow> 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<PlatformWindow>(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<PlatformMenu>(MenuItem::Type::kPopup, L"&File");
{
file_menu->AddChild(std::make_unique<PlatformMenu>(
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<PlatformMenu>(MenuItem::Type::kPopup, L"&CPU");
{
cpu_menu->AddChild(std::make_unique<PlatformMenu>(
MenuItem::Type::kString, Commands::IDC_CPU_TIME_SCALAR_RESET,
L"&Reset Time Scalar", L"Numpad Enter"));
cpu_menu->AddChild(std::make_unique<PlatformMenu>(
MenuItem::Type::kString, Commands::IDC_CPU_TIME_SCALAR_HALF,
L"Time Scalar /= 2", L"Numpad -"));
cpu_menu->AddChild(std::make_unique<PlatformMenu>(
MenuItem::Type::kString, Commands::IDC_CPU_TIME_SCALAR_DOUBLE,
L"Time Scalar *= 2", L"Numpad +"));
}
cpu_menu->AddChild(
std::make_unique<PlatformMenu>(MenuItem::Type::kSeparator));
{
cpu_menu->AddChild(std::make_unique<PlatformMenu>(
MenuItem::Type::kString, Commands::IDC_CPU_PROFILER_TOGGLE_DISPLAY,
L"Toggle Profiler &Display", L"Tab"));
cpu_menu->AddChild(std::make_unique<PlatformMenu>(
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<PlatformMenu>(MenuItem::Type::kPopup, L"&GPU");
{
gpu_menu->AddChild(std::make_unique<PlatformMenu>(
MenuItem::Type::kString, Commands::IDC_GPU_TRACE_FRAME, L"&Trace Frame",
L"F4"));
}
gpu_menu->AddChild(
std::make_unique<PlatformMenu>(MenuItem::Type::kSeparator));
{
gpu_menu->AddChild(std::make_unique<PlatformMenu>(
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<PlatformMenu>(MenuItem::Type::kPopup, L"&Window");
{
window_menu->AddChild(std::make_unique<PlatformMenu>(
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<PlatformMenu>(MenuItem::Type::kPopup, L"&Help");
{
help_menu->AddChild(std::make_unique<PlatformMenu>(
MenuItem::Type::kString, Commands::IDC_HELP_WEBSITE, L"&Website...",
L"F1"));
help_menu->AddChild(std::make_unique<PlatformMenu>(
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

View File

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

View File

@ -12,11 +12,27 @@
namespace xe {
namespace ui {
MenuItem::MenuItem(Type type) : type_(type), parent_item_(nullptr) {}
std::unique_ptr<MenuItem> MenuItem::Create(Type type) {
return MenuItem::Create(type, L"", L"", nullptr);
}
std::unique_ptr<MenuItem> MenuItem::Create(Type type,
const std::wstring& text) {
return MenuItem::Create(type, text, L"", nullptr);
}
std::unique_ptr<MenuItem> MenuItem::Create(Type type, const std::wstring& text,
std::function<void()> callback) {
return MenuItem::Create(type, text, L"", std::move(callback));
}
MenuItem::MenuItem(Type type, const std::wstring& text,
const std::wstring& hotkey)
: type_(type), parent_item_(nullptr), text_(text), hotkey_(hotkey) {}
const std::wstring& hotkey, std::function<void()> callback)
: type_(type),
parent_item_(nullptr),
text_(text),
hotkey_(hotkey),
callback_(std::move(callback)) {}
MenuItem::~MenuItem() = default;
@ -45,7 +61,13 @@ void MenuItem::RemoveChild(MenuItem* child_item) {
}
}
void MenuItem::OnSelected(UIEvent& e) { on_selected(e); }
MenuItem* MenuItem::child(size_t index) { return children_[index].get(); }
void MenuItem::OnSelected(UIEvent& e) {
if (callback_) {
callback_();
}
}
} // namespace ui
} // namespace xe

View File

@ -10,10 +10,10 @@
#ifndef XENIA_UI_MENU_ITEM_H_
#define XENIA_UI_MENU_ITEM_H_
#include <functional>
#include <memory>
#include <vector>
#include "xenia/base/delegate.h"
#include "xenia/ui/ui_event.h"
namespace xe {
@ -30,6 +30,14 @@ class MenuItem {
kString, // Menu is just a string
};
static std::unique_ptr<MenuItem> Create(Type type);
static std::unique_ptr<MenuItem> Create(Type type, const std::wstring& text);
static std::unique_ptr<MenuItem> Create(Type type, const std::wstring& text,
std::function<void()> callback);
static std::unique_ptr<MenuItem> Create(Type type, const std::wstring& text,
const std::wstring& hotkey,
std::function<void()> callback);
virtual ~MenuItem();
MenuItem* parent_item() const { return parent_item_; }
@ -41,12 +49,11 @@ class MenuItem {
void AddChild(std::unique_ptr<MenuItem> child_item);
void AddChild(MenuItemPtr child_item);
void RemoveChild(MenuItem* child_item);
Delegate<UIEvent> on_selected;
MenuItem* child(size_t index);
protected:
MenuItem(Type type);
MenuItem(Type type, const std::wstring& text, const std::wstring& hotkey);
MenuItem(Type type, const std::wstring& text, const std::wstring& hotkey,
std::function<void()> callback);
virtual void OnChildAdded(MenuItem* child_item) {}
virtual void OnChildRemoved(MenuItem* child_item) {}
@ -58,6 +65,7 @@ class MenuItem {
std::vector<MenuItemPtr> children_;
std::wstring text_;
std::wstring hotkey_;
std::function<void()> callback_;
};
} // namespace ui

View File

@ -1,33 +0,0 @@
#pragma once
/**
******************************************************************************
* 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_PLATFORM_H_
#define XENIA_UI_PLATFORM_H_
// TODO(benvanik): only on windows.
#include "xenia/ui/win32/win32_control.h"
#include "xenia/ui/win32/win32_file_picker.h"
#include "xenia/ui/win32/win32_loop.h"
#include "xenia/ui/win32/win32_menu_item.h"
#include "xenia/ui/win32/win32_window.h"
namespace xe {
namespace ui {
using PlatformControl = xe::ui::win32::Win32Control;
using PlatformFilePicker = xe::ui::win32::Win32FilePicker;
using PlatformLoop = xe::ui::win32::Win32Loop;
using PlatformMenu = xe::ui::win32::Win32MenuItem;
using PlatformWindow = xe::ui::win32::Win32Window;
} // namespace ui
} // namespace xe
#endif // XENIA_UI_PLATFORM_H_

View File

@ -13,23 +13,23 @@
namespace xe {
namespace ui {
class Control;
class Window;
class UIEvent {
public:
UIEvent(Control* control = nullptr) : control_(control) {}
UIEvent(Window* target = nullptr) : target_(target) {}
virtual ~UIEvent() = default;
Control* control() const { return control_; }
Window* target() const { return target_; }
private:
Control* control_;
Window* target_ = nullptr;
};
class KeyEvent : public UIEvent {
public:
KeyEvent(Control* control, int key_code)
: UIEvent(control), handled_(false), key_code_(key_code) {}
KeyEvent(Window* target, int key_code)
: UIEvent(target), key_code_(key_code) {}
~KeyEvent() override = default;
bool is_handled() const { return handled_; }
@ -38,8 +38,8 @@ class KeyEvent : public UIEvent {
int key_code() const { return key_code_; }
private:
bool handled_;
int key_code_;
bool handled_ = false;
int key_code_ = 0;
};
class MouseEvent : public UIEvent {
@ -54,15 +54,9 @@ class MouseEvent : public UIEvent {
};
public:
MouseEvent(Control* control, Button button, int32_t x, int32_t y,
MouseEvent(Window* target, Button button, int32_t x, int32_t y,
int32_t dx = 0, int32_t dy = 0)
: UIEvent(control),
handled_(false),
button_(button),
x_(x),
y_(y),
dx_(dx),
dy_(dy) {}
: UIEvent(target), button_(button), x_(x), y_(y), dx_(dx), dy_(dy) {}
~MouseEvent() override = default;
bool is_handled() const { return handled_; }
@ -75,12 +69,12 @@ class MouseEvent : public UIEvent {
int32_t dy() const { return dy_; }
private:
bool handled_;
bool handled_ = false;
Button button_;
int32_t x_;
int32_t y_;
int32_t dx_;
int32_t dy_;
int32_t x_ = 0;
int32_t y_ = 0;
int32_t dx_ = 0;
int32_t dy_ = 0;
};
} // namespace ui

View File

@ -1,379 +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/win32/win32_control.h"
namespace xe {
namespace ui {
namespace win32 {
Win32Control::Win32Control(uint32_t flags) : Control(flags), hwnd_(nullptr) {}
Win32Control::~Win32Control() {
if (hwnd_) {
SetWindowLongPtr(hwnd_, GWLP_USERDATA, 0);
CloseWindow(hwnd_);
hwnd_ = nullptr;
}
}
void Win32Control::OnCreate() {
Control::OnCreate();
// Create all children, if needed.
for (auto& child_control : children_) {
auto win32_control = static_cast<Win32Control*>(child_control.get());
if (!win32_control->hwnd()) {
win32_control->Create();
} else {
SetParent(win32_control->hwnd(), hwnd());
}
win32_control->Layout();
}
if (!is_cursor_visible_) {
ShowCursor(FALSE);
}
if (!is_enabled_) {
EnableWindow(hwnd_, false);
}
if (!is_visible_) {
ShowWindow(hwnd_, SW_HIDE);
}
if (has_focus_) {
SetFocus(hwnd_);
}
}
void Win32Control::OnDestroy() {
// Destroy all children, if needed.
for (auto& child_control : children_) {
auto win32_control = static_cast<Win32Control*>(child_control.get());
if (!win32_control->hwnd()) {
win32_control->Destroy();
}
}
}
void Win32Control::OnChildAdded(Control* child_control) {
auto win32_control = static_cast<Win32Control*>(child_control);
if (hwnd_) {
if (!win32_control->hwnd()) {
win32_control->Create();
} else {
SetParent(win32_control->hwnd(), hwnd());
}
child_control->Layout();
}
}
void Win32Control::OnChildRemoved(Control* child_control) {
auto win32_control = static_cast<Win32Control*>(child_control);
if (win32_control->hwnd()) {
SetParent(win32_control->hwnd(), nullptr);
}
}
void Win32Control::Resize(int32_t width, int32_t height) {
MoveWindow(hwnd_, 0, 0, width, height, TRUE);
}
void Win32Control::Resize(int32_t left, int32_t top, int32_t right,
int32_t bottom) {
MoveWindow(hwnd_, left, top, right - left, bottom - top, TRUE);
}
void Win32Control::ResizeToFill(int32_t pad_left, int32_t pad_top,
int32_t pad_right, int32_t pad_bottom) {
if (!parent_) {
// TODO(benvanik): screen?
return;
}
RECT parent_rect;
auto parent_control = static_cast<Win32Control*>(parent_);
GetClientRect(parent_control->hwnd(), &parent_rect);
MoveWindow(hwnd_, parent_rect.left + pad_left, parent_rect.top + pad_top,
parent_rect.right - pad_right - pad_left,
parent_rect.bottom - pad_bottom - pad_top, TRUE);
}
void Win32Control::OnResize(UIEvent& e) {
RECT client_rect;
GetClientRect(hwnd_, &client_rect);
int32_t width = client_rect.right - client_rect.left;
int32_t height = client_rect.bottom - client_rect.top;
if (width != width_ || height != height_) {
width_ = width;
height_ = height;
Layout();
for (auto& child_control : children_) {
auto win32_control = static_cast<Win32Control*>(child_control.get());
win32_control->OnResize(e);
win32_control->Invalidate();
}
}
}
void Win32Control::Invalidate() {
if (invalidated_) {
return;
}
invalidated_ = true;
InvalidateRect(hwnd_, nullptr, FALSE);
for (auto& child_control : children_) {
child_control->Invalidate();
}
}
void Win32Control::set_cursor_visible(bool value) {
if (is_cursor_visible_ == value) {
return;
}
if (value) {
ShowCursor(TRUE);
SetCursor(nullptr);
} else {
ShowCursor(FALSE);
}
}
void Win32Control::set_enabled(bool value) {
if (is_enabled_ == value) {
return;
}
if (hwnd_) {
EnableWindow(hwnd_, value);
} else {
is_enabled_ = value;
}
}
void Win32Control::set_visible(bool value) {
if (is_visible_ == value) {
return;
}
if (hwnd_) {
ShowWindow(hwnd_, value ? SW_SHOWNOACTIVATE : SW_HIDE);
} else {
is_visible_ = value;
}
}
void Win32Control::set_focus(bool value) {
if (has_focus_ == value) {
return;
}
if (hwnd_) {
if (value) {
SetFocus(hwnd_);
} else {
SetFocus(nullptr);
}
} else {
has_focus_ = value;
}
}
LRESULT CALLBACK Win32Control::WndProcThunk(HWND hWnd, UINT message,
WPARAM wParam, LPARAM lParam) {
Win32Control* control = nullptr;
if (message == WM_NCCREATE) {
auto create_struct = reinterpret_cast<LPCREATESTRUCT>(lParam);
control = reinterpret_cast<Win32Control*>(create_struct->lpCreateParams);
SetWindowLongPtr(hWnd, GWLP_USERDATA, (__int3264)(LONG_PTR)control);
} else {
control =
reinterpret_cast<Win32Control*>(GetWindowLongPtr(hWnd, GWLP_USERDATA));
}
if (control) {
return control->WndProc(hWnd, message, wParam, lParam);
} else {
return DefWindowProc(hWnd, message, wParam, lParam);
}
}
LRESULT Win32Control::WndProc(HWND hWnd, UINT message, WPARAM wParam,
LPARAM lParam) {
if (message >= WM_MOUSEFIRST && message <= WM_MOUSELAST) {
if (HandleMouse(message, wParam, lParam)) {
SetFocus(hwnd_);
return 0;
} else {
return DefWindowProc(hWnd, message, wParam, lParam);
}
} else if (message >= WM_KEYFIRST && message <= WM_KEYLAST) {
if (HandleKeyboard(message, wParam, lParam)) {
SetFocus(hwnd_);
return 0;
} else {
return DefWindowProc(hWnd, message, wParam, lParam);
}
}
switch (message) {
case WM_NCCREATE:
break;
case WM_CREATE:
break;
case WM_DESTROY:
OnDestroy();
break;
case WM_MOVING:
break;
case WM_MOVE:
break;
case WM_SIZING:
break;
case WM_SIZE: {
auto e = UIEvent(this);
OnResize(e);
break;
}
case WM_PAINT: {
if (flags_ & kFlagOwnPaint) {
return 0; // ignore
}
invalidated_ = false;
auto e = UIEvent(this);
OnPaint(e);
break;
}
case WM_ERASEBKGND:
if (flags_ & kFlagOwnPaint) {
return 0; // ignore
}
break;
case WM_DISPLAYCHANGE:
break;
case WM_SHOWWINDOW: {
if (wParam == TRUE) {
auto e = UIEvent(this);
OnVisible(e);
} else {
auto e = UIEvent(this);
OnHidden(e);
}
break;
}
case WM_KILLFOCUS: {
has_focus_ = false;
auto e = UIEvent(this);
OnLostFocus(e);
break;
}
case WM_SETFOCUS: {
has_focus_ = true;
auto e = UIEvent(this);
OnGotFocus(e);
break;
}
}
return DefWindowProc(hWnd, message, wParam, lParam);
}
bool Win32Control::HandleMouse(UINT message, WPARAM wParam, LPARAM lParam) {
int32_t x = GET_X_LPARAM(lParam);
int32_t y = GET_Y_LPARAM(lParam);
if (message == WM_MOUSEWHEEL) {
POINT pt = {x, y};
ScreenToClient(hwnd_, &pt);
x = pt.x;
y = pt.y;
}
MouseEvent::Button button = MouseEvent::Button::kNone;
int32_t dx = 0;
int32_t dy = 0;
switch (message) {
case WM_LBUTTONDOWN:
case WM_LBUTTONUP:
button = MouseEvent::Button::kLeft;
break;
case WM_RBUTTONDOWN:
case WM_RBUTTONUP:
button = MouseEvent::Button::kRight;
break;
case WM_MBUTTONDOWN:
case WM_MBUTTONUP:
button = MouseEvent::Button::kMiddle;
break;
case WM_XBUTTONDOWN:
case WM_XBUTTONUP:
switch (GET_XBUTTON_WPARAM(wParam)) {
case XBUTTON1:
button = MouseEvent::Button::kX1;
break;
case XBUTTON2:
button = MouseEvent::Button::kX2;
break;
default:
return false;
}
break;
case WM_MOUSEMOVE:
button = MouseEvent::Button::kNone;
break;
case WM_MOUSEWHEEL:
button = MouseEvent::Button::kNone;
dx = 0; // ?
dy = GET_WHEEL_DELTA_WPARAM(wParam);
break;
default:
// Double click/etc?
return true;
}
auto e = MouseEvent(this, button, x, y, dx, dy);
switch (message) {
case WM_LBUTTONDOWN:
case WM_RBUTTONDOWN:
case WM_MBUTTONDOWN:
case WM_XBUTTONDOWN:
OnMouseDown(e);
break;
case WM_LBUTTONUP:
case WM_RBUTTONUP:
case WM_MBUTTONUP:
case WM_XBUTTONUP:
OnMouseUp(e);
break;
case WM_MOUSEMOVE:
OnMouseMove(e);
break;
case WM_MOUSEWHEEL:
OnMouseWheel(e);
break;
}
return e.is_handled();
}
bool Win32Control::HandleKeyboard(UINT message, WPARAM wParam, LPARAM lParam) {
auto e = KeyEvent(this, (int)wParam);
switch (message) {
case WM_KEYDOWN:
OnKeyDown(e);
break;
case WM_KEYUP:
OnKeyUp(e);
break;
case WM_CHAR:
OnKeyChar(e);
break;
}
return e.is_handled();
}
} // namespace win32
} // namespace ui
} // namespace xe

View File

@ -1,72 +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/win32/win32_menu_item.h"
#include "xenia/base/assert.h"
namespace xe {
namespace ui {
namespace win32 {
Win32MenuItem::Win32MenuItem(Type type, int id, const std::wstring& text,
const std::wstring& hotkey)
: id_(id), MenuItem(type, text, hotkey) {
switch (type) {
case MenuItem::Type::kNormal:
handle_ = CreateMenu();
break;
case MenuItem::Type::kPopup:
handle_ = CreatePopupMenu();
break;
}
}
Win32MenuItem::Win32MenuItem(Type type) : Win32MenuItem(type, 0, L"") {}
Win32MenuItem::Win32MenuItem(Type type, const std::wstring& text,
const std::wstring& hotkey)
: Win32MenuItem(type, 0, text, hotkey) {}
Win32MenuItem::~Win32MenuItem() {
if (handle_) {
DestroyMenu(handle_);
}
}
void Win32MenuItem::OnChildAdded(MenuItem* generic_child_item) {
auto child_item = static_cast<Win32MenuItem*>(generic_child_item);
switch (child_item->type()) {
case MenuItem::Type::kPopup:
AppendMenuW(handle_, MF_POPUP,
reinterpret_cast<UINT_PTR>(child_item->handle()),
child_item->text().c_str());
break;
case MenuItem::Type::kSeparator:
AppendMenuW(handle_, MF_SEPARATOR, child_item->id(), 0);
break;
case MenuItem::Type::kString:
auto full_name = child_item->text();
if (!child_item->hotkey().empty()) {
full_name += L"\t" + child_item->hotkey();
}
AppendMenuW(handle_, MF_STRING, child_item->id(), full_name.c_str());
break;
}
}
void Win32MenuItem::OnChildRemoved(MenuItem* generic_child_item) {
auto child_item = static_cast<Win32MenuItem*>(generic_child_item);
//
}
} // namespace win32
} // namespace ui
} // namespace xe

View File

@ -1,46 +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_WIN32_WIN32_MENU_ITEM_H_
#define XENIA_UI_WIN32_WIN32_MENU_ITEM_H_
#include "xenia/base/platform.h"
#include "xenia/ui/menu_item.h"
namespace xe {
namespace ui {
namespace win32 {
class Win32MenuItem : public MenuItem {
public:
Win32MenuItem(Type type);
Win32MenuItem(Type type, const std::wstring& text,
const std::wstring& hotkey = L"");
Win32MenuItem(Type type, int id, const std::wstring& text,
const std::wstring& hotkey = L"");
~Win32MenuItem() override;
HMENU handle() { return handle_; }
int id() { return id_; }
protected:
void OnChildAdded(MenuItem* child_item) override;
void OnChildRemoved(MenuItem* child_item) override;
private:
HMENU handle_;
uint32_t position_; // Position within parent, if any
int id_;
};
} // namespace win32
} // namespace ui
} // namespace xe
#endif // XENIA_UI_WIN32_WIN32_MENU_ITEM_H_

View File

@ -1,250 +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/win32/win32_window.h"
#include <cstring>
#include "xenia/base/logging.h"
namespace xe {
namespace ui {
namespace win32 {
Win32Window::Win32Window(Loop* loop, const std::wstring& title)
: Window(loop, title) {}
Win32Window::~Win32Window() = default;
bool Win32Window::Initialize() {
Create();
return true;
}
bool Win32Window::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; // LoadIcon(hInstance, (LPCTSTR)IDI_TUTORIAL1);
wcex.hIconSm = nullptr; // LoadIcon(hInstance, (LPCTSTR)IDI_TUTORIAL1);
wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wcex.lpszMenuName = nullptr;
wcex.lpszClassName = L"XeniaWindowClass";
if (!RegisterClassEx(&wcex)) {
XELOGE("RegisterClassEx failed");
return false;
}
// Setup initial size.
DWORD window_style = WS_OVERLAPPEDWINDOW;
DWORD window_ex_style = WS_EX_APPWINDOW | WS_EX_CONTROLPARENT;
RECT rc = {0, 0, width_, height_};
AdjustWindowRect(&rc, WS_OVERLAPPEDWINDOW, FALSE);
// Create window.
hwnd_ = CreateWindowEx(window_ex_style, L"XeniaWindowClass", title_.c_str(),
window_style, rc.left, rc.top, rc.right - rc.left,
rc.bottom - rc.top, nullptr, nullptr, hInstance, this);
if (!hwnd_) {
XELOGE("CreateWindow failed");
return false;
}
// Disable flicks.
ATOM atom = GlobalAddAtom(L"MicrosoftTabletPenServiceProperty");
const DWORD_PTR dwHwndTabletProperty =
TABLET_DISABLE_PRESSANDHOLD | // disables press and hold (right-click)
// gesture
TABLET_DISABLE_PENTAPFEEDBACK | // disables UI feedback on pen up (waves)
TABLET_DISABLE_PENBARRELFEEDBACK | // disables UI feedback on pen button
// down (circle)
TABLET_DISABLE_FLICKS | // disables pen flicks (back, forward, drag down,
// drag up)
TABLET_DISABLE_TOUCHSWITCH | TABLET_DISABLE_SMOOTHSCROLLING |
TABLET_DISABLE_TOUCHUIFORCEON | TABLET_ENABLE_MULTITOUCHDATA;
SetProp(hwnd_, L"MicrosoftTabletPenServiceProperty",
reinterpret_cast<HANDLE>(dwHwndTabletProperty));
GlobalDeleteAtom(atom);
// Enable DWM elevation.
EnableMMCSS();
ShowWindow(hwnd_, SW_SHOWNORMAL);
UpdateWindow(hwnd_);
return 0;
}
void Win32Window::EnableMMCSS() {
HMODULE hLibrary = LoadLibrary(L"DWMAPI.DLL");
if (!hLibrary) {
return;
}
typedef HRESULT(__stdcall * PDwmEnableMMCSS)(BOOL);
PDwmEnableMMCSS pDwmEnableMMCSS =
(PDwmEnableMMCSS)GetProcAddress(hLibrary, "DwmEnableMMCSS");
if (pDwmEnableMMCSS) {
pDwmEnableMMCSS(TRUE);
}
typedef HRESULT(__stdcall * PDwmSetPresentParameters)(
HWND, DWM_PRESENT_PARAMETERS*);
PDwmSetPresentParameters pDwmSetPresentParameters =
(PDwmSetPresentParameters)GetProcAddress(hLibrary,
"DwmSetPresentParameters");
if (pDwmSetPresentParameters) {
DWM_PRESENT_PARAMETERS pp;
std::memset(&pp, 0, sizeof(DWM_PRESENT_PARAMETERS));
pp.cbSize = sizeof(DWM_PRESENT_PARAMETERS);
pp.fQueue = FALSE;
pp.cBuffer = 2;
pp.fUseSourceRate = FALSE;
pp.cRefreshesPerFrame = 1;
pp.eSampling = DWM_SOURCE_FRAME_SAMPLING_POINT;
pDwmSetPresentParameters(hwnd_, &pp);
}
FreeLibrary(hLibrary);
}
bool Win32Window::set_title(const std::wstring& title) {
if (!Window::set_title(title)) {
return false;
}
SetWindowText(hwnd_, title.c_str());
return true;
}
void Win32Window::Resize(int32_t width, int32_t height) {
RECT rc = {0, 0, width, height};
bool has_menu = menu_ ? true : false;
AdjustWindowRect(&rc, WS_OVERLAPPEDWINDOW, has_menu);
if (true) {
rc.right += 100 - rc.left;
rc.left = 100;
rc.bottom += 100 - rc.top;
rc.top = 100;
}
Window::Resize(rc.left, rc.top, rc.right, rc.bottom);
}
void Win32Window::Resize(int32_t left, int32_t top, int32_t right,
int32_t bottom) {
RECT rc = {left, top, right, bottom};
bool has_menu = menu_ ? true : false;
AdjustWindowRect(&rc, WS_OVERLAPPEDWINDOW, has_menu);
Window::Resize(rc.left, rc.top, rc.right, rc.bottom);
}
void Win32Window::ResizeToFill(int32_t pad_left, int32_t pad_top,
int32_t pad_right, int32_t pad_bottom) {
// TODO(benvanik): fullscreen.
}
bool Win32Window::is_fullscreen() const {
DWORD style = GetWindowLong(hwnd_, GWL_STYLE);
return (style & WS_OVERLAPPEDWINDOW) != WS_OVERLAPPEDWINDOW;
}
void Win32Window::ToggleFullscreen(bool fullscreen) {
if (fullscreen == is_fullscreen()) {
return;
}
DWORD style = GetWindowLong(hwnd_, GWL_STYLE);
if (fullscreen) {
// Kill our borders and resize to take up entire primary monitor
// http://blogs.msdn.com/b/oldnewthing/archive/2010/04/12/9994016.aspx
MONITORINFO mi = {sizeof(mi)};
if (GetWindowPlacement(hwnd_, &windowed_pos_) &&
GetMonitorInfo(MonitorFromWindow(hwnd_, MONITOR_DEFAULTTOPRIMARY),
&mi)) {
SetWindowLong(hwnd_, GWL_STYLE, style & ~WS_OVERLAPPEDWINDOW);
::SetMenu(hwnd_, NULL);
// Call into parent class to get around menu resizing code
Window::Resize(mi.rcMonitor.left, mi.rcMonitor.top, mi.rcMonitor.right,
mi.rcMonitor.bottom);
}
} else {
// Reinstate borders, resize to 1280x720
SetWindowLong(hwnd_, GWL_STYLE, style | WS_OVERLAPPEDWINDOW);
SetWindowPlacement(hwnd_, &windowed_pos_);
Win32MenuItem* win_menu = reinterpret_cast<Win32MenuItem*>(menu_);
if (win_menu) {
::SetMenu(hwnd_, win_menu->handle());
}
}
}
void Win32Window::OnClose() {
if (!closing_ && hwnd_) {
closing_ = true;
CloseWindow(hwnd_);
}
}
void Win32Window::OnSetMenu(MenuItem* menu) {
Win32MenuItem* win_menu = reinterpret_cast<Win32MenuItem*>(menu);
// Don't actually set the menu if we're fullscreen. We'll do that later.
if (win_menu && !is_fullscreen()) {
::SetMenu(hwnd_, win_menu->handle());
}
}
LRESULT Win32Window::WndProc(HWND hWnd, UINT message, WPARAM wParam,
LPARAM lParam) {
switch (message) {
case WM_ACTIVATEAPP:
if (wParam) {
// Made active.
OnShow();
} else {
// Made inactive.
OnHide();
}
break;
case WM_CLOSE:
closing_ = true;
Close();
break;
case WM_TABLET_QUERYSYSTEMGESTURESTATUS:
return TABLET_DISABLE_PRESSANDHOLD | // disables press and hold
// (right-click) gesture
TABLET_DISABLE_PENTAPFEEDBACK | // disables UI feedback on pen up
// (waves)
TABLET_DISABLE_PENBARRELFEEDBACK | // disables UI feedback on pen
// button down (circle)
TABLET_DISABLE_FLICKS | // disables pen flicks (back, forward,
// drag down, drag up)
TABLET_DISABLE_TOUCHSWITCH | TABLET_DISABLE_SMOOTHSCROLLING |
TABLET_DISABLE_TOUCHUIFORCEON | TABLET_ENABLE_MULTITOUCHDATA;
case WM_COMMAND: {
// TODO: Redirect this to MenuItem's on_selected delegate.
OnCommand(LOWORD(wParam));
} break;
}
return Win32Control::WndProc(hWnd, message, wParam, lParam);
}
} // namespace win32
} // namespace ui
} // namespace xe

View File

@ -1,62 +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_WIN32_WIN32_WINDOW_H_
#define XENIA_UI_WIN32_WIN32_WINDOW_H_
#include <string>
#include "xenia/ui/win32/win32_control.h"
#include "xenia/ui/win32/win32_menu_item.h"
#include "xenia/ui/window.h"
namespace xe {
namespace ui {
namespace win32 {
class Win32Window : public Window<Win32Control> {
public:
Win32Window(Loop* loop, const std::wstring& title);
~Win32Window() override;
bool Initialize() override;
bool set_title(const std::wstring& title) override;
void Resize(int32_t width, int32_t height) override;
void Resize(int32_t left, int32_t top, int32_t right,
int32_t bottom) override;
void ResizeToFill(int32_t pad_left, int32_t pad_top, int32_t pad_right,
int32_t pad_bottom) override;
bool is_fullscreen() const override;
void ToggleFullscreen(bool fullscreen) override;
protected:
bool Create() override;
void OnClose() override;
void OnSetMenu(MenuItem*) override;
LRESULT WndProc(HWND hWnd, UINT message, WPARAM wParam,
LPARAM lParam) override;
private:
void EnableMMCSS();
bool closing_ = false;
WINDOWPLACEMENT windowed_pos_;
};
} // namespace win32
} // namespace ui
} // namespace xe
#endif // XENIA_UI_WIN32_WIN32_WINDOW_H_

View File

@ -2,12 +2,12 @@
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2015 Ben Vanik. All rights reserved. *
* Copyright 2014 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#include "xenia/ui/elemental_control.h"
#include "xenia/ui/window.h"
#include "el/animation_manager.h"
#include "el/util/debug.h"
@ -28,6 +28,7 @@ namespace xe {
namespace ui {
constexpr bool kContinuousRepaint = false;
constexpr bool kShowPresentFps = kContinuousRepaint;
// Enables long press behaviors (context menu, etc).
constexpr bool kTouch = false;
@ -37,19 +38,26 @@ constexpr double kDoubleClickDistance = 5;
constexpr int32_t kMouseWheelDetent = 120;
Loop* elemental_loop_ = nullptr;
class RootElement : public el::Element {
public:
RootElement(ElementalControl* owner) : owner_(owner) {}
RootElement(Window* owner) : owner_(owner) {}
void OnInvalid() override { owner_->Invalidate(); }
private:
ElementalControl* owner_ = nullptr;
Window* owner_ = nullptr;
};
bool ElementalControl::InitializeElemental(Loop* loop,
el::graphics::Renderer* renderer) {
Window::Window(Loop* loop, const std::wstring& title)
: loop_(loop), title_(title) {}
Window::~Window() {
// Context must have been cleaned up already in OnDestroy.
assert_null(context_.get());
}
bool Window::InitializeElemental(Loop* loop, el::graphics::Renderer* renderer) {
GraphicsContextLock context_lock(context_.get());
static bool has_initialized = false;
if (has_initialized) {
return true;
@ -58,7 +66,7 @@ bool ElementalControl::InitializeElemental(Loop* loop,
// Need to pass off the Loop we want Elemental to use.
// TODO(benvanik): give the callback to elemental instead.
elemental_loop_ = loop;
Loop::set_elemental_loop(loop);
if (!el::Initialize(renderer)) {
XELOGE("Failed to initialize elemental core");
@ -116,18 +124,10 @@ bool ElementalControl::InitializeElemental(Loop* loop,
return true;
}
ElementalControl::ElementalControl(Loop* loop, uint32_t flags)
: super(flags), loop_(loop) {}
bool Window::OnCreate() { return true; }
ElementalControl::~ElementalControl() = default;
bool ElementalControl::Create() {
if (!super::Create()) {
return false;
}
// Create subclass renderer (GL, etc).
renderer_ = CreateRenderer();
bool Window::MakeReady() {
renderer_ = context_->CreateElementalRenderer();
// Initialize elemental.
// TODO(benvanik): once? Do we care about multiple controls?
@ -139,26 +139,49 @@ bool ElementalControl::Create() {
// TODO(benvanik): setup elements.
root_element_ = std::make_unique<RootElement>(this);
root_element_->set_background_skin(TBIDC("background"));
root_element_->set_rect({0, 0, 1000, 1000});
root_element_->set_rect({0, 0, width(), height()});
// el::util::ShowDebugInfoSettingsWindow(root_element_.get());
return true;
}
bool ElementalControl::LoadLanguage(std::string filename) {
void Window::OnMainMenuChange() {}
void Window::OnClose() {
auto e = UIEvent(this);
on_closing(e);
on_closed(e);
}
void Window::OnDestroy() {
el::Shutdown();
// Context must go last.
root_element_.reset();
renderer_.reset();
context_.reset();
}
bool Window::LoadLanguage(std::string filename) {
return el::util::StringTable::get()->Load(filename.c_str());
}
bool ElementalControl::LoadSkin(std::string filename) {
bool Window::LoadSkin(std::string filename) {
return el::Skin::get()->Load(filename.c_str());
}
void ElementalControl::Destroy() {
el::Shutdown();
super::Destroy();
void Window::Layout() {
auto e = UIEvent(this);
OnLayout(e);
}
void ElementalControl::OnLayout(UIEvent& e) {
super::OnLayout(e);
void Window::Invalidate() {}
void Window::OnResize(UIEvent& e) { on_resize(e); }
void Window::OnLayout(UIEvent& e) {
on_layout(e);
if (!root_element()) {
return;
}
@ -166,9 +189,8 @@ void ElementalControl::OnLayout(UIEvent& e) {
root_element()->set_rect({0, 0, width(), height()});
}
void ElementalControl::OnPaint(UIEvent& e) {
super::OnPaint(e);
if (!root_element()) {
void Window::OnPaint(UIEvent& e) {
if (!renderer()) {
return;
}
@ -184,47 +206,74 @@ void ElementalControl::OnPaint(UIEvent& e) {
// Update TB (run animations, handle deferred input, etc).
el::AnimationManager::Update();
root_element()->InvokeProcessStates();
root_element()->InvokeProcess();
if (root_element()) {
root_element()->InvokeProcessStates();
root_element()->InvokeProcess();
}
GraphicsContextLock context_lock(context_.get());
context_->BeginSwap();
on_painting(e);
renderer()->BeginPaint(width(), height());
// Render entire control hierarchy.
root_element()->InvokePaint(el::Element::PaintProps());
if (root_element()) {
root_element()->InvokePaint(el::Element::PaintProps());
}
// Render debug overlay.
root_element()->computed_font()->DrawString(
5, 5, el::Color(255, 0, 0),
el::format_string("Frame %lld", frame_count_));
if (kContinuousRepaint) {
on_paint(e);
if (root_element() && kShowPresentFps) {
// Render debug overlay.
root_element()->computed_font()->DrawString(
5, 20, el::Color(255, 0, 0), el::format_string("FPS: %d", fps_));
5, 5, el::Color(255, 0, 0),
el::format_string("Frame %lld", frame_count_));
root_element()->computed_font()->DrawString(
5, 20, el::Color(255, 0, 0),
el::format_string("Present FPS: %d", fps_));
}
renderer()->EndPaint();
on_painted(e);
context_->EndSwap();
// If animations are running, reinvalidate immediately.
if (el::AnimationManager::has_running_animations()) {
root_element()->Invalidate();
}
if (kContinuousRepaint) {
// Force an immediate repaint, always.
root_element()->Invalidate();
if (root_element()) {
if (el::AnimationManager::has_running_animations()) {
root_element()->Invalidate();
}
if (kContinuousRepaint) {
// Force an immediate repaint, always.
root_element()->Invalidate();
}
} else {
if (kContinuousRepaint) {
Invalidate();
}
}
}
void ElementalControl::OnGotFocus(UIEvent& e) { super::OnGotFocus(e); }
void Window::OnVisible(UIEvent& e) { on_visible(e); }
void ElementalControl::OnLostFocus(UIEvent& e) {
super::OnLostFocus(e);
void Window::OnHidden(UIEvent& e) { on_hidden(e); }
void Window::OnGotFocus(UIEvent& e) { on_got_focus(e); }
void Window::OnLostFocus(UIEvent& e) {
modifier_shift_pressed_ = false;
modifier_cntrl_pressed_ = false;
modifier_alt_pressed_ = false;
modifier_super_pressed_ = false;
last_click_time_ = 0;
on_lost_focus(e);
}
el::ModifierKeys ElementalControl::GetModifierKeys() {
el::ModifierKeys Window::GetModifierKeys() {
auto modifiers = el::ModifierKeys::kNone;
if (modifier_shift_pressed_) {
modifiers |= el::ModifierKeys::kShift;
@ -241,7 +290,7 @@ el::ModifierKeys ElementalControl::GetModifierKeys() {
return modifiers;
}
void ElementalControl::OnKeyPress(KeyEvent& e, bool is_down, bool is_char) {
void Window::OnKeyPress(KeyEvent& e, bool is_down, bool is_char) {
if (!root_element()) {
return;
}
@ -364,8 +413,8 @@ void ElementalControl::OnKeyPress(KeyEvent& e, bool is_down, bool is_char) {
}
}
bool ElementalControl::CheckShortcutKey(KeyEvent& e, el::SpecialKey special_key,
bool is_down) {
bool Window::CheckShortcutKey(KeyEvent& e, el::SpecialKey special_key,
bool is_down) {
bool shortcut_key = modifier_cntrl_pressed_;
if (!el::Element::focused_element || !is_down || !shortcut_key) {
return false;
@ -417,24 +466,32 @@ bool ElementalControl::CheckShortcutKey(KeyEvent& e, el::SpecialKey special_key,
return true;
}
void ElementalControl::OnKeyDown(KeyEvent& e) {
super::OnKeyDown(e);
void Window::OnKeyDown(KeyEvent& e) {
on_key_down(e);
if (e.is_handled()) {
return;
}
OnKeyPress(e, true, false);
}
void ElementalControl::OnKeyUp(KeyEvent& e) {
super::OnKeyUp(e);
void Window::OnKeyUp(KeyEvent& e) {
on_key_up(e);
if (e.is_handled()) {
return;
}
OnKeyPress(e, false, false);
}
void ElementalControl::OnKeyChar(KeyEvent& e) {
super::OnKeyChar(e);
void Window::OnKeyChar(KeyEvent& e) {
OnKeyPress(e, true, true);
OnKeyPress(e, false, true);
}
void ElementalControl::OnMouseDown(MouseEvent& e) {
super::OnMouseDown(e);
void Window::OnMouseDown(MouseEvent& e) {
on_mouse_down(e);
if (e.is_handled()) {
return;
}
if (!root_element()) {
return;
}
@ -463,8 +520,11 @@ void ElementalControl::OnMouseDown(MouseEvent& e) {
}
}
void ElementalControl::OnMouseMove(MouseEvent& e) {
super::OnMouseMove(e);
void Window::OnMouseMove(MouseEvent& e) {
on_mouse_move(e);
if (e.is_handled()) {
return;
}
if (!root_element()) {
return;
}
@ -472,8 +532,11 @@ void ElementalControl::OnMouseMove(MouseEvent& e) {
e.set_handled(true);
}
void ElementalControl::OnMouseUp(MouseEvent& e) {
super::OnMouseUp(e);
void Window::OnMouseUp(MouseEvent& e) {
on_mouse_up(e);
if (e.is_handled()) {
return;
}
if (!root_element()) {
return;
}
@ -494,8 +557,11 @@ void ElementalControl::OnMouseUp(MouseEvent& e) {
}
}
void ElementalControl::OnMouseWheel(MouseEvent& e) {
super::OnMouseWheel(e);
void Window::OnMouseWheel(MouseEvent& e) {
on_mouse_wheel(e);
if (e.is_handled()) {
return;
}
if (!root_element()) {
return;
}
@ -505,32 +571,3 @@ void ElementalControl::OnMouseWheel(MouseEvent& e) {
} // namespace ui
} // namespace xe
// This doesn't really belong here (it belongs in tb_system_[linux/windows].cpp.
// This is here since the proper implementations has not yet been done.
void el::util::RescheduleTimer(uint64_t fire_time) {
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);
}

View File

@ -10,10 +10,13 @@
#ifndef XENIA_UI_WINDOW_H_
#define XENIA_UI_WINDOW_H_
#include <memory>
#include <string>
#include "el/element.h"
#include "el/graphics/renderer.h"
#include "xenia/base/delegate.h"
#include "xenia/ui/control.h"
#include "xenia/ui/graphics_context.h"
#include "xenia/ui/loop.h"
#include "xenia/ui/menu_item.h"
#include "xenia/ui/ui_event.h"
@ -21,14 +24,22 @@
namespace xe {
namespace ui {
template <typename T>
class Window : public T {
public:
~Window() override = default;
typedef void* NativeWindowHandle;
virtual bool Initialize() { return true; }
class Window {
public:
static std::unique_ptr<Window> Create(Loop* loop, const std::wstring& title);
virtual ~Window();
Loop* loop() const { return loop_; }
virtual NativeWindowHandle native_handle() const = 0;
MenuItem* main_menu() const { return main_menu_.get(); }
void set_main_menu(std::unique_ptr<MenuItem> main_menu) {
main_menu_ = std::move(main_menu);
OnMainMenuChange();
}
const std::wstring& title() const { return title_; }
virtual bool set_title(const std::wstring& title) {
@ -39,65 +50,124 @@ class Window : public T {
return true;
}
MenuItem* menu() const { return menu_; }
void set_menu(MenuItem* menu) {
if (menu == menu_) {
return;
}
menu_ = menu;
OnSetMenu(menu);
}
void Close() {
auto e = UIEvent(this);
on_closing(e);
OnClose();
e = UIEvent(this);
on_closed(e);
}
virtual bool is_fullscreen() const { return false; }
virtual void ToggleFullscreen(bool fullscreen) {}
bool has_focus() const { return has_focus_; }
virtual void set_focus(bool value) { has_focus_ = value; }
bool is_cursor_visible() const { return is_cursor_visible_; }
virtual void set_cursor_visible(bool value) { is_cursor_visible_ = value; }
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;
GraphicsContext* context() const { return context_.get(); }
el::graphics::Renderer* renderer() const { return renderer_.get(); }
el::Element* root_element() const { return root_element_.get(); }
virtual bool Initialize() { return true; }
void set_context(std::unique_ptr<GraphicsContext> context) {
context_ = std::move(context);
}
virtual bool MakeReady();
bool LoadLanguage(std::string filename);
bool LoadSkin(std::string filename);
void Layout();
virtual void Invalidate();
virtual void Close() = 0;
public:
Delegate<UIEvent> on_shown;
Delegate<UIEvent> on_hidden;
Delegate<UIEvent> on_closing;
Delegate<UIEvent> on_closed;
Delegate<int> on_command;
Delegate<UIEvent> on_resize;
Delegate<UIEvent> on_layout;
Delegate<UIEvent> on_painting;
Delegate<UIEvent> on_paint;
Delegate<UIEvent> on_painted;
Delegate<UIEvent> on_visible;
Delegate<UIEvent> on_hidden;
Delegate<UIEvent> on_got_focus;
Delegate<UIEvent> on_lost_focus;
Delegate<KeyEvent> on_key_down;
Delegate<KeyEvent> on_key_up;
Delegate<KeyEvent> on_key_char;
Delegate<MouseEvent> on_mouse_down;
Delegate<MouseEvent> on_mouse_move;
Delegate<MouseEvent> on_mouse_up;
Delegate<MouseEvent> on_mouse_wheel;
protected:
Window(Loop* loop, const std::wstring& title)
: T(0), loop_(loop), title_(title) {}
Window(Loop* loop, const std::wstring& title);
void OnShow() {
if (is_visible_) {
return;
}
is_visible_ = true;
auto e = UIEvent(this);
on_shown(e);
}
bool InitializeElemental(Loop* loop, el::graphics::Renderer* renderer);
void OnHide() {
if (!is_visible_) {
return;
}
is_visible_ = false;
auto e = UIEvent(this);
on_hidden(e);
}
virtual bool OnCreate();
virtual void OnMainMenuChange();
virtual void OnClose();
virtual void OnDestroy();
virtual void OnClose() {}
virtual void OnResize(UIEvent& e);
virtual void OnLayout(UIEvent& e);
virtual void OnPaint(UIEvent& e);
virtual void OnSetMenu(MenuItem* menu) {}
virtual void OnVisible(UIEvent& e);
virtual void OnHidden(UIEvent& e);
virtual void OnCommand(int id) {}
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);
el::ModifierKeys GetModifierKeys();
void OnKeyPress(KeyEvent& e, bool is_down, bool is_char);
bool CheckShortcutKey(KeyEvent& e, el::SpecialKey special_key, bool is_down);
Loop* loop_ = nullptr;
std::unique_ptr<MenuItem> main_menu_;
std::wstring title_;
MenuItem* menu_ = nullptr;
int32_t width_ = 0;
int32_t height_ = 0;
bool has_focus_ = true;
bool is_cursor_visible_ = true;
std::unique_ptr<GraphicsContext> context_;
std::unique_ptr<el::graphics::Renderer> renderer_;
std::unique_ptr<el::Element> 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

569
src/xenia/ui/window_win.cc Normal file
View File

@ -0,0 +1,569 @@
/**
******************************************************************************
* 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 <string>
#include "xenia/base/assert.h"
#include "xenia/base/logging.h"
#include "xenia/ui/window_win.h"
namespace xe {
namespace ui {
std::unique_ptr<Window> Window::Create(Loop* loop, const std::wstring& title) {
return std::make_unique<Win32Window>(loop, title);
}
Win32Window::Win32Window(Loop* loop, const std::wstring& title)
: Window(loop, title) {}
Win32Window::~Win32Window() {
OnDestroy();
if (hwnd_) {
SetWindowLongPtr(hwnd_, GWLP_USERDATA, 0);
CloseWindow(hwnd_);
hwnd_ = nullptr;
}
}
bool Win32Window::Initialize() { return OnCreate(); }
bool Win32Window::OnCreate() {
HINSTANCE hInstance = GetModuleHandle(nullptr);
WNDCLASSEX wcex;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
wcex.lpfnWndProc = Win32Window::WndProcThunk;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = nullptr; // LoadIcon(hInstance, (LPCTSTR)IDI_TUTORIAL1);
wcex.hIconSm = nullptr; // LoadIcon(hInstance, (LPCTSTR)IDI_TUTORIAL1);
wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wcex.lpszMenuName = nullptr;
wcex.lpszClassName = L"XeniaWindowClass";
if (!RegisterClassEx(&wcex)) {
XELOGE("RegisterClassEx failed");
return false;
}
// Setup initial size.
DWORD window_style = WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN | WS_CLIPSIBLINGS;
DWORD window_ex_style = WS_EX_APPWINDOW | WS_EX_CONTROLPARENT;
RECT rc = {0, 0, width_, height_};
AdjustWindowRect(&rc, WS_OVERLAPPEDWINDOW, FALSE);
// Create window.
hwnd_ = CreateWindowEx(window_ex_style, L"XeniaWindowClass", title_.c_str(),
window_style, rc.left, rc.top, rc.right - rc.left,
rc.bottom - rc.top, nullptr, nullptr, hInstance, this);
if (!hwnd_) {
XELOGE("CreateWindow failed");
return false;
}
// Disable flicks.
ATOM atom = GlobalAddAtom(L"MicrosoftTabletPenServiceProperty");
const DWORD_PTR dwHwndTabletProperty =
TABLET_DISABLE_PRESSANDHOLD | // disables press and hold (right-click)
// gesture
TABLET_DISABLE_PENTAPFEEDBACK | // disables UI feedback on pen up (waves)
TABLET_DISABLE_PENBARRELFEEDBACK | // disables UI feedback on pen button
// down (circle)
TABLET_DISABLE_FLICKS | // disables pen flicks (back, forward, drag down,
// drag up)
TABLET_DISABLE_TOUCHSWITCH | TABLET_DISABLE_SMOOTHSCROLLING |
TABLET_DISABLE_TOUCHUIFORCEON | TABLET_ENABLE_MULTITOUCHDATA;
SetProp(hwnd_, L"MicrosoftTabletPenServiceProperty",
reinterpret_cast<HANDLE>(dwHwndTabletProperty));
GlobalDeleteAtom(atom);
// Enable DWM elevation.
EnableMMCSS();
ShowWindow(hwnd_, SW_SHOWNORMAL);
UpdateWindow(hwnd_);
// Initial state.
if (!is_cursor_visible_) {
ShowCursor(FALSE);
}
if (has_focus_) {
SetFocus(hwnd_);
}
return super::OnCreate();
}
void Win32Window::EnableMMCSS() {
HMODULE hLibrary = LoadLibrary(L"DWMAPI.DLL");
if (!hLibrary) {
return;
}
typedef HRESULT(__stdcall * PDwmEnableMMCSS)(BOOL);
PDwmEnableMMCSS pDwmEnableMMCSS =
(PDwmEnableMMCSS)GetProcAddress(hLibrary, "DwmEnableMMCSS");
if (pDwmEnableMMCSS) {
pDwmEnableMMCSS(TRUE);
}
typedef HRESULT(__stdcall * PDwmSetPresentParameters)(
HWND, DWM_PRESENT_PARAMETERS*);
PDwmSetPresentParameters pDwmSetPresentParameters =
(PDwmSetPresentParameters)GetProcAddress(hLibrary,
"DwmSetPresentParameters");
if (pDwmSetPresentParameters) {
DWM_PRESENT_PARAMETERS pp;
std::memset(&pp, 0, sizeof(DWM_PRESENT_PARAMETERS));
pp.cbSize = sizeof(DWM_PRESENT_PARAMETERS);
pp.fQueue = FALSE;
pp.cBuffer = 2;
pp.fUseSourceRate = FALSE;
pp.cRefreshesPerFrame = 1;
pp.eSampling = DWM_SOURCE_FRAME_SAMPLING_POINT;
pDwmSetPresentParameters(hwnd_, &pp);
}
FreeLibrary(hLibrary);
}
void Win32Window::OnDestroy() {
super::OnDestroy();
hwnd_ = nullptr;
}
void Win32Window::OnClose() {
if (!closing_ && hwnd_) {
closing_ = true;
CloseWindow(hwnd_);
}
super::OnClose();
}
bool Win32Window::set_title(const std::wstring& title) {
if (!super::set_title(title)) {
return false;
}
SetWindowText(hwnd_, title.c_str());
return true;
}
bool Win32Window::is_fullscreen() const {
DWORD style = GetWindowLong(hwnd_, GWL_STYLE);
return (style & WS_OVERLAPPEDWINDOW) != WS_OVERLAPPEDWINDOW;
}
void Win32Window::ToggleFullscreen(bool fullscreen) {
if (fullscreen == is_fullscreen()) {
return;
}
DWORD style = GetWindowLong(hwnd_, GWL_STYLE);
if (fullscreen) {
// Kill our borders and resize to take up entire primary monitor.
// http://blogs.msdn.com/b/oldnewthing/archive/2010/04/12/9994016.aspx
MONITORINFO mi = {sizeof(mi)};
if (GetWindowPlacement(hwnd_, &windowed_pos_) &&
GetMonitorInfo(MonitorFromWindow(hwnd_, MONITOR_DEFAULTTOPRIMARY),
&mi)) {
SetWindowLong(hwnd_, GWL_STYLE, style & ~WS_OVERLAPPEDWINDOW);
::SetMenu(hwnd_, NULL);
// Call into parent class to get around menu resizing code.
Resize(mi.rcMonitor.left, mi.rcMonitor.top, mi.rcMonitor.right,
mi.rcMonitor.bottom);
}
} else {
// Reinstate borders, resize to 1280x720
SetWindowLong(hwnd_, GWL_STYLE, style | WS_OVERLAPPEDWINDOW);
SetWindowPlacement(hwnd_, &windowed_pos_);
auto main_menu = reinterpret_cast<Win32MenuItem*>(main_menu_.get());
if (main_menu) {
::SetMenu(hwnd_, main_menu->handle());
}
}
}
void Win32Window::set_cursor_visible(bool value) {
if (is_cursor_visible_ == value) {
return;
}
if (value) {
ShowCursor(TRUE);
SetCursor(nullptr);
} else {
ShowCursor(FALSE);
}
}
void Win32Window::set_focus(bool value) {
if (has_focus_ == value) {
return;
}
if (hwnd_) {
if (value) {
SetFocus(hwnd_);
} else {
SetFocus(nullptr);
}
} else {
has_focus_ = value;
}
}
void Win32Window::Resize(int32_t width, int32_t height) {
RECT rc = {0, 0, width, height};
bool has_menu = main_menu_ ? true : false;
AdjustWindowRect(&rc, WS_OVERLAPPEDWINDOW, has_menu);
if (true) {
rc.right += 100 - rc.left;
rc.left = 100;
rc.bottom += 100 - rc.top;
rc.top = 100;
}
MoveWindow(hwnd_, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top,
TRUE);
}
void Win32Window::Resize(int32_t left, int32_t top, int32_t right,
int32_t bottom) {
RECT rc = {left, top, right, bottom};
bool has_menu = main_menu_ ? true : false;
AdjustWindowRect(&rc, WS_OVERLAPPEDWINDOW, has_menu);
MoveWindow(hwnd_, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top,
TRUE);
}
void Win32Window::OnResize(UIEvent& e) {
RECT client_rect;
GetClientRect(hwnd_, &client_rect);
int32_t width = client_rect.right - client_rect.left;
int32_t height = client_rect.bottom - client_rect.top;
if (width != width_ || height != height_) {
width_ = width;
height_ = height;
Layout();
}
super::OnResize(e);
}
void Win32Window::Invalidate() {
super::Invalidate();
InvalidateRect(hwnd_, nullptr, FALSE);
}
void Win32Window::Close() {
if (closing_) {
return;
}
closing_ = true;
Close();
OnClose();
DestroyWindow(hwnd_);
}
void Win32Window::OnMainMenuChange() {
auto main_menu = reinterpret_cast<Win32MenuItem*>(main_menu_.get());
// Don't actually set the menu if we're fullscreen. We'll do that later.
if (main_menu && !is_fullscreen()) {
::SetMenu(hwnd_, main_menu->handle());
}
}
LRESULT CALLBACK Win32Window::WndProcThunk(HWND hWnd, UINT message,
WPARAM wParam, LPARAM lParam) {
Win32Window* window = nullptr;
if (message == WM_NCCREATE) {
auto create_struct = reinterpret_cast<LPCREATESTRUCT>(lParam);
window = reinterpret_cast<Win32Window*>(create_struct->lpCreateParams);
SetWindowLongPtr(hWnd, GWLP_USERDATA, (__int3264)(LONG_PTR)window);
} else {
window =
reinterpret_cast<Win32Window*>(GetWindowLongPtr(hWnd, GWLP_USERDATA));
}
if (window) {
return window->WndProc(hWnd, message, wParam, lParam);
} else {
return DefWindowProc(hWnd, message, wParam, lParam);
}
}
LRESULT Win32Window::WndProc(HWND hWnd, UINT message, WPARAM wParam,
LPARAM lParam) {
if (message >= WM_MOUSEFIRST && message <= WM_MOUSELAST) {
if (HandleMouse(message, wParam, lParam)) {
SetFocus(hwnd_);
return 0;
} else {
return DefWindowProc(hWnd, message, wParam, lParam);
}
} else if (message >= WM_KEYFIRST && message <= WM_KEYLAST) {
if (HandleKeyboard(message, wParam, lParam)) {
SetFocus(hwnd_);
return 0;
} else {
return DefWindowProc(hWnd, message, wParam, lParam);
}
}
switch (message) {
case WM_NCCREATE:
break;
case WM_CREATE:
break;
case WM_DESTROY:
OnDestroy();
break;
case WM_CLOSE:
closing_ = true;
Close();
OnClose();
break;
case WM_MOVING:
break;
case WM_MOVE:
break;
case WM_SIZING:
break;
case WM_SIZE: {
auto e = UIEvent(this);
OnResize(e);
break;
}
case WM_PAINT: {
ValidateRect(hwnd_, nullptr);
auto e = UIEvent(this);
OnPaint(e);
return 0; // Ignored because of custom paint.
break;
}
case WM_ERASEBKGND:
return 0; // Ignored because of custom paint.
case WM_DISPLAYCHANGE:
break;
case WM_ACTIVATEAPP:
case WM_SHOWWINDOW: {
if (wParam == TRUE) {
auto e = UIEvent(this);
OnVisible(e);
} else {
auto e = UIEvent(this);
OnHidden(e);
}
break;
}
case WM_KILLFOCUS: {
has_focus_ = false;
auto e = UIEvent(this);
OnLostFocus(e);
break;
}
case WM_SETFOCUS: {
has_focus_ = true;
auto e = UIEvent(this);
OnGotFocus(e);
break;
}
case WM_TABLET_QUERYSYSTEMGESTURESTATUS:
return
// disables press and hold (right-click) gesture
TABLET_DISABLE_PRESSANDHOLD |
// disables UI feedback on pen up (waves)
TABLET_DISABLE_PENTAPFEEDBACK |
// disables UI feedback on pen button down (circle)
TABLET_DISABLE_PENBARRELFEEDBACK |
// disables pen flicks (back, forward, drag down, drag up)
TABLET_DISABLE_FLICKS | TABLET_DISABLE_TOUCHSWITCH |
TABLET_DISABLE_SMOOTHSCROLLING | TABLET_DISABLE_TOUCHUIFORCEON |
TABLET_ENABLE_MULTITOUCHDATA;
case WM_MENUCOMMAND: {
// TODO: Redirect this to MenuItem's on_selected delegate.
MENUINFO menu_info = {0};
menu_info.cbSize = sizeof(menu_info);
menu_info.fMask = MIM_MENUDATA;
GetMenuInfo(HMENU(lParam), &menu_info);
auto parent_item = reinterpret_cast<Win32MenuItem*>(menu_info.dwMenuData);
auto child_item =
reinterpret_cast<Win32MenuItem*>(parent_item->child(wParam));
assert_not_null(child_item);
UIEvent e(this);
child_item->OnSelected(e);
} break;
}
return DefWindowProc(hWnd, message, wParam, lParam);
}
bool Win32Window::HandleMouse(UINT message, WPARAM wParam, LPARAM lParam) {
int32_t x = GET_X_LPARAM(lParam);
int32_t y = GET_Y_LPARAM(lParam);
if (message == WM_MOUSEWHEEL) {
POINT pt = {x, y};
ScreenToClient(hwnd_, &pt);
x = pt.x;
y = pt.y;
}
MouseEvent::Button button = MouseEvent::Button::kNone;
int32_t dx = 0;
int32_t dy = 0;
switch (message) {
case WM_LBUTTONDOWN:
case WM_LBUTTONUP:
button = MouseEvent::Button::kLeft;
break;
case WM_RBUTTONDOWN:
case WM_RBUTTONUP:
button = MouseEvent::Button::kRight;
break;
case WM_MBUTTONDOWN:
case WM_MBUTTONUP:
button = MouseEvent::Button::kMiddle;
break;
case WM_XBUTTONDOWN:
case WM_XBUTTONUP:
switch (GET_XBUTTON_WPARAM(wParam)) {
case XBUTTON1:
button = MouseEvent::Button::kX1;
break;
case XBUTTON2:
button = MouseEvent::Button::kX2;
break;
default:
return false;
}
break;
case WM_MOUSEMOVE:
button = MouseEvent::Button::kNone;
break;
case WM_MOUSEWHEEL:
button = MouseEvent::Button::kNone;
dx = 0; // ?
dy = GET_WHEEL_DELTA_WPARAM(wParam);
break;
default:
// Double click/etc?
return true;
}
auto e = MouseEvent(this, button, x, y, dx, dy);
switch (message) {
case WM_LBUTTONDOWN:
case WM_RBUTTONDOWN:
case WM_MBUTTONDOWN:
case WM_XBUTTONDOWN:
OnMouseDown(e);
break;
case WM_LBUTTONUP:
case WM_RBUTTONUP:
case WM_MBUTTONUP:
case WM_XBUTTONUP:
OnMouseUp(e);
break;
case WM_MOUSEMOVE:
OnMouseMove(e);
break;
case WM_MOUSEWHEEL:
OnMouseWheel(e);
break;
}
return e.is_handled();
}
bool Win32Window::HandleKeyboard(UINT message, WPARAM wParam, LPARAM lParam) {
auto e = KeyEvent(this, (int)wParam);
switch (message) {
case WM_KEYDOWN:
OnKeyDown(e);
break;
case WM_KEYUP:
OnKeyUp(e);
break;
case WM_CHAR:
OnKeyChar(e);
break;
}
return e.is_handled();
}
std::unique_ptr<ui::MenuItem> MenuItem::Create(Type type,
const std::wstring& text,
const std::wstring& hotkey,
std::function<void()> callback) {
return std::make_unique<Win32MenuItem>(type, text, hotkey, callback);
}
Win32MenuItem::Win32MenuItem(Type type, const std::wstring& text,
const std::wstring& hotkey,
std::function<void()> callback)
: MenuItem(type, text, hotkey, std::move(callback)) {
switch (type) {
case MenuItem::Type::kNormal:
handle_ = CreateMenu();
break;
case MenuItem::Type::kPopup:
handle_ = CreatePopupMenu();
break;
}
if (handle_) {
MENUINFO menu_info = {0};
menu_info.cbSize = sizeof(menu_info);
menu_info.fMask = MIM_MENUDATA | MIM_STYLE;
menu_info.dwMenuData = ULONG_PTR(this);
menu_info.dwStyle = MNS_NOTIFYBYPOS;
SetMenuInfo(handle_, &menu_info);
}
}
Win32MenuItem::~Win32MenuItem() {
if (handle_) {
DestroyMenu(handle_);
}
}
void Win32MenuItem::OnChildAdded(MenuItem* generic_child_item) {
auto child_item = static_cast<Win32MenuItem*>(generic_child_item);
switch (child_item->type()) {
case MenuItem::Type::kPopup:
AppendMenuW(handle_, MF_POPUP,
reinterpret_cast<UINT_PTR>(child_item->handle()),
child_item->text().c_str());
break;
case MenuItem::Type::kSeparator:
AppendMenuW(handle_, MF_SEPARATOR, UINT_PTR(child_item->handle_), 0);
break;
case MenuItem::Type::kString:
auto full_name = child_item->text();
if (!child_item->hotkey().empty()) {
full_name += L"\t" + child_item->hotkey();
}
AppendMenuW(handle_, MF_STRING, UINT_PTR(child_item->handle_),
full_name.c_str());
break;
}
}
void Win32MenuItem::OnChildRemoved(MenuItem* generic_child_item) {
auto child_item = static_cast<Win32MenuItem*>(generic_child_item);
//
}
} // namespace ui
} // namespace xe

View File

@ -7,45 +7,49 @@
******************************************************************************
*/
#ifndef XENIA_UI_WIN32_WIN32_CONTROL_H_
#define XENIA_UI_WIN32_WIN32_CONTROL_H_
#ifndef XENIA_UI_WINDOW_WIN_H_
#define XENIA_UI_WINDOW_WIN_H_
#include "xenia/base/platform.h"
#include "xenia/ui/control.h"
#include <memory>
#include <string>
#include "xenia/ui/menu_item.h"
#include "xenia/ui/window.h"
namespace xe {
namespace ui {
namespace win32 {
class Win32Control : public Control {
class Win32Window : public Window {
using super = Window;
public:
~Win32Control() override;
Win32Window(Loop* loop, const std::wstring& title);
~Win32Window() override;
NativeWindowHandle native_handle() const override { return hwnd_; }
HWND hwnd() const { return hwnd_; }
HWND parent_hwnd() const {
return parent_ ? static_cast<Win32Control*>(parent_)->hwnd() : nullptr;
}
bool set_title(const std::wstring& title) override;
bool is_fullscreen() const override;
void ToggleFullscreen(bool fullscreen) override;
void set_cursor_visible(bool value) override;
void set_focus(bool value) override;
void Resize(int32_t width, int32_t height) override;
void Resize(int32_t left, int32_t top, int32_t right,
int32_t bottom) override;
void ResizeToFill(int32_t pad_left, int32_t pad_top, int32_t pad_right,
int32_t pad_bottom) override;
void Invalidate() override;
void set_cursor_visible(bool value) override;
void set_enabled(bool value) override;
void set_visible(bool value) override;
void set_focus(bool value) override;
bool Initialize() override;
void Invalidate() override;
void Close() override;
protected:
explicit Win32Control(uint32_t flags);
void OnCreate() override;
bool OnCreate() override;
void OnMainMenuChange() override;
void OnDestroy() override;
void OnChildAdded(Control* child_control) override;
void OnChildRemoved(Control* child_control) override;
void OnClose() override;
void OnResize(UIEvent& e) override;
@ -54,16 +58,37 @@ class Win32Control : public Control {
virtual LRESULT WndProc(HWND hWnd, UINT message, WPARAM wParam,
LPARAM lParam);
HWND hwnd_ = nullptr;
bool invalidated_ = true;
private:
void EnableMMCSS();
bool HandleMouse(UINT message, WPARAM wParam, LPARAM lParam);
bool HandleKeyboard(UINT message, WPARAM wParam, LPARAM lParam);
HWND hwnd_ = nullptr;
bool invalidated_ = true;
bool closing_ = false;
WINDOWPLACEMENT windowed_pos_;
};
class Win32MenuItem : public MenuItem {
public:
Win32MenuItem(Type type, const std::wstring& text, const std::wstring& hotkey,
std::function<void()> callback);
~Win32MenuItem() override;
HMENU handle() { return handle_; }
using MenuItem::OnSelected;
protected:
void OnChildAdded(MenuItem* child_item) override;
void OnChildRemoved(MenuItem* child_item) override;
private:
HMENU handle_ = nullptr;
};
} // namespace win32
} // namespace ui
} // namespace xe
#endif // XENIA_UI_WIN32_WIN32_CONTROL_H_
#endif // XENIA_UI_WINDOW_WIN_H_

View File

@ -12,6 +12,7 @@
#include "xenia/base/logging.h"
#include "xenia/base/main.h"
#include "xenia/emulator.h"
#include "xenia/emulator_window.h"
#include "xenia/kernel/kernel.h"
#include "xenia/profiling.h"
#include "xenia/ui/file_picker.h"
@ -24,9 +25,15 @@ int xenia_main(std::vector<std::wstring>& args) {
Profiler::Initialize();
Profiler::ThreadEnter("main");
// Create the emulator.
// Create the emulator but don't initialize so we can setup the window.
auto emulator = std::make_unique<Emulator>(L"");
X_STATUS result = emulator->Setup();
// Main emulator display window.
auto emulator_window = EmulatorWindow::Create(emulator.get());
// Setup and initialize all subsystems. If we can't do something
// (unsupported system, memory issues, etc) this will fail early.
X_STATUS result = emulator->Setup(emulator_window->window());
if (XFAILED(result)) {
XELOGE("Failed to setup emulator: %.8X", result);
return 1;
@ -48,20 +55,20 @@ int xenia_main(std::vector<std::wstring>& args) {
// If no path passed, ask the user.
if (path.empty()) {
ui::PlatformFilePicker file_picker;
file_picker.set_mode(ui::FilePicker::Mode::kOpen);
file_picker.set_type(ui::FilePicker::Type::kFile);
file_picker.set_multi_selection(false);
file_picker.set_title(L"Select Content Package");
file_picker.set_extensions({
auto file_picker = xe::ui::FilePicker::Create();
file_picker->set_mode(ui::FilePicker::Mode::kOpen);
file_picker->set_type(ui::FilePicker::Type::kFile);
file_picker->set_multi_selection(false);
file_picker->set_title(L"Select Content Package");
file_picker->set_extensions({
{L"Supported Files", L"*.iso;*.xex;*.xcp;*.*"},
{L"Disc Image (*.iso)", L"*.iso"},
{L"Xbox Executable (*.xex)", L"*.xex"},
//{ L"Content Package (*.xcp)", L"*.xcp" },
{L"All Files (*.*)", L"*.*"},
});
if (file_picker.Show(emulator->display_window()->hwnd())) {
auto selected_files = file_picker.selected_files();
if (file_picker->Show(emulator->display_window()->native_handle())) {
auto selected_files = file_picker->selected_files();
if (!selected_files.empty()) {
path = selected_files[0];
}
@ -83,6 +90,8 @@ int xenia_main(std::vector<std::wstring>& args) {
}
emulator.reset();
emulator_window.reset();
Profiler::Dump();
Profiler::Shutdown();
return 0;

@ -1 +1 @@
Subproject commit a95e9a1984029382ae8e35260725d5697a702f8b
Subproject commit 879a034ddf323744654bb810da5fe8e15143f6ea

View File

@ -106,10 +106,19 @@
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="src\xenia\base\main_win.cc" />
<ClCompile Include="src\xenia\emulator_window.cc" />
<ClCompile Include="src\xenia\xenia_main.cc" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="src\xenia\base\main.h" />
<ClInclude Include="src\xenia\emulator_window.h" />
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="src\xenia\ui\main_resources.rc">
<AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(SolutionDir)third_party/elemental-forms;$(SolutionDir);.</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|x64'">$(SolutionDir)third_party/elemental-forms;$(SolutionDir);.</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Checked|x64'">$(SolutionDir)third_party/elemental-forms;$(SolutionDir);.</AdditionalIncludeDirectories>
</ResourceCompile>
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">

View File

@ -14,6 +14,9 @@
<Filter Include="src\xenia\base">
<UniqueIdentifier>{a3d918ab-c42b-469d-9950-ec4656b77b32}</UniqueIdentifier>
</Filter>
<Filter Include="src\xenia\ui">
<UniqueIdentifier>{81255c06-3cf7-48ad-900a-f7c614d134ac}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="src\xenia\base\main_win.cc">
@ -22,10 +25,21 @@
<ClCompile Include="src\xenia\xenia_main.cc">
<Filter>src\xenia</Filter>
</ClCompile>
<ClCompile Include="src\xenia\emulator_window.cc">
<Filter>src\xenia</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="src\xenia\base\main.h">
<Filter>src\xenia\base</Filter>
</ClInclude>
<ClInclude Include="src\xenia\emulator_window.h">
<Filter>src\xenia</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="src\xenia\ui\main_resources.rc">
<Filter>src\xenia\ui</Filter>
</ResourceCompile>
</ItemGroup>
</Project>