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:
parent
e69c7b00a8
commit
15c17459be
|
@ -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" />
|
||||
|
|
|
@ -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">
|
||||
|
|
|
@ -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!");
|
||||
|
|
|
@ -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_;
|
||||
};
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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_;
|
||||
|
||||
|
|
|
@ -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
|
|
@ -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_
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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_;
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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 ®ister_file_; }
|
||||
|
@ -41,8 +43,6 @@ class GL4GraphicsSystem : public GraphicsSystem {
|
|||
void InitializeRingBuffer(uint32_t ptr, uint32_t page_count) override;
|
||||
void EnableReadPointerWriteBack(uint32_t ptr, uint32_t block_size) override;
|
||||
|
||||
void RequestSwap() override;
|
||||
|
||||
void RequestFrameTrace() override;
|
||||
void BeginTracing() override;
|
||||
void EndTracing() override;
|
||||
|
@ -52,7 +52,7 @@ class GL4GraphicsSystem : public GraphicsSystem {
|
|||
|
||||
private:
|
||||
void MarkVblank();
|
||||
void SwapHandler(const SwapParameters& swap_params);
|
||||
void Swap(xe::ui::UIEvent& e);
|
||||
uint64_t ReadRegister(uint32_t addr);
|
||||
void WriteRegister(uint32_t addr, uint64_t value);
|
||||
|
||||
|
@ -67,7 +67,8 @@ class GL4GraphicsSystem : public GraphicsSystem {
|
|||
|
||||
RegisterFile register_file_;
|
||||
std::unique_ptr<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_;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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() {}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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
|
|
@ -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_
|
|
@ -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_
|
|
@ -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),
|
||||
|
|
|
@ -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
|
|
@ -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 {
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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_
|
|
@ -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
|
|
@ -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_
|
|
@ -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_
|
|
@ -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_
|
|
@ -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);
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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_
|
|
@ -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"
|
|
@ -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
|
|
@ -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_
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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_
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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_
|
|
@ -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
|
|
@ -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_
|
|
@ -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);
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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_
|
|
@ -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
|
|
@ -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">
|
||||
|
|
|
@ -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>
|
Loading…
Reference in New Issue