diff --git a/libxenia.vcxproj b/libxenia.vcxproj index 9334245b9..74515307c 100644 --- a/libxenia.vcxproj +++ b/libxenia.vcxproj @@ -403,6 +403,7 @@ + diff --git a/libxenia.vcxproj.filters b/libxenia.vcxproj.filters index 70041c6f3..9484f4917 100644 --- a/libxenia.vcxproj.filters +++ b/libxenia.vcxproj.filters @@ -1368,6 +1368,9 @@ src\xenia\ui\gl + + src\xenia\ui + diff --git a/src/xenia/emulator.cc b/src/xenia/emulator.cc index 34d94f5f9..93fdd4ee8 100644 --- a/src/xenia/emulator.cc +++ b/src/xenia/emulator.cc @@ -70,7 +70,7 @@ Emulator::~Emulator() { export_resolver_.reset(); // Kill the window last, as until the graphics system/etc is dead it's needed. - main_window_.reset(); + display_window_.reset(); } X_STATUS Emulator::Setup() { @@ -94,8 +94,7 @@ X_STATUS Emulator::Setup() { SetProcessAffinityMask(process_handle, system_affinity_mask); // Create the main window. Other parts will hook into this. - main_window_ = std::make_unique(this); - main_window_->Start(); + display_window_ = ui::MainWindow::Create(this); // Create memory system first, as it is required for other systems. memory_ = std::make_unique(); @@ -152,8 +151,8 @@ X_STATUS Emulator::Setup() { if (!processor_->Setup()) { return result; } - result = graphics_system_->Setup(processor_.get(), main_window_->loop(), - main_window_.get()); + result = graphics_system_->Setup(processor_.get(), display_window_->loop(), + display_window_.get()); if (result) { return result; } diff --git a/src/xenia/emulator.h b/src/xenia/emulator.h index ccddfc52b..93b39ae73 100644 --- a/src/xenia/emulator.h +++ b/src/xenia/emulator.h @@ -15,7 +15,7 @@ #include "xenia/debug/debugger.h" #include "xenia/kernel/kernel_state.h" #include "xenia/memory.h" -#include "xenia/ui/main_window.h" +#include "xenia/ui/platform.h" #include "xenia/vfs/virtual_file_system.h" #include "xenia/xbox.h" @@ -49,7 +49,7 @@ class Emulator { const std::wstring& command_line() const { return command_line_; } - ui::MainWindow* main_window() const { return main_window_.get(); } + ui::PlatformWindow* display_window() const { return display_window_.get(); } Memory* memory() const { return memory_.get(); } @@ -83,7 +83,7 @@ class Emulator { std::wstring command_line_; - std::unique_ptr main_window_; + std::unique_ptr display_window_; std::unique_ptr memory_; diff --git a/src/xenia/gpu/gl4/gl4_graphics_system.cc b/src/xenia/gpu/gl4/gl4_graphics_system.cc index 52641c9af..852dcc6fb 100644 --- a/src/xenia/gpu/gl4/gl4_graphics_system.cc +++ b/src/xenia/gpu/gl4/gl4_graphics_system.cc @@ -53,7 +53,7 @@ GL4GraphicsSystem::GL4GraphicsSystem(Emulator* emulator) GL4GraphicsSystem::~GL4GraphicsSystem() = default; X_STATUS GL4GraphicsSystem::Setup(cpu::Processor* processor, - ui::PlatformLoop* target_loop, + ui::Loop* target_loop, ui::PlatformWindow* target_window) { auto result = GraphicsSystem::Setup(processor, target_loop, target_window); if (result) { diff --git a/src/xenia/gpu/gl4/gl4_graphics_system.h b/src/xenia/gpu/gl4/gl4_graphics_system.h index 5baaa1f70..8137ea8c6 100644 --- a/src/xenia/gpu/gl4/gl4_graphics_system.h +++ b/src/xenia/gpu/gl4/gl4_graphics_system.h @@ -29,7 +29,7 @@ class GL4GraphicsSystem : public GraphicsSystem { static std::unique_ptr Create(Emulator* emulator); - X_STATUS Setup(cpu::Processor* processor, ui::PlatformLoop* target_loop, + X_STATUS Setup(cpu::Processor* processor, ui::Loop* target_loop, ui::PlatformWindow* target_window) override; void Shutdown() override; diff --git a/src/xenia/gpu/graphics_system.cc b/src/xenia/gpu/graphics_system.cc index 46a882100..d5233d250 100644 --- a/src/xenia/gpu/graphics_system.cc +++ b/src/xenia/gpu/graphics_system.cc @@ -38,19 +38,11 @@ std::unique_ptr GraphicsSystem::Create(Emulator* emulator) { } } -GraphicsSystem::GraphicsSystem(Emulator* emulator) - : emulator_(emulator), - memory_(nullptr), - processor_(nullptr), - target_loop_(nullptr), - target_window_(nullptr), - interrupt_callback_(0), - interrupt_callback_data_(0) {} +GraphicsSystem::GraphicsSystem(Emulator* emulator) : emulator_(emulator) {} GraphicsSystem::~GraphicsSystem() = default; -X_STATUS GraphicsSystem::Setup(cpu::Processor* processor, - ui::PlatformLoop* target_loop, +X_STATUS GraphicsSystem::Setup(cpu::Processor* processor, ui::Loop* target_loop, ui::PlatformWindow* target_window) { processor_ = processor; memory_ = processor->memory(); diff --git a/src/xenia/gpu/graphics_system.h b/src/xenia/gpu/graphics_system.h index e7a2c32d7..a77654c45 100644 --- a/src/xenia/gpu/graphics_system.h +++ b/src/xenia/gpu/graphics_system.h @@ -16,7 +16,8 @@ #include "xenia/cpu/processor.h" #include "xenia/memory.h" -#include "xenia/ui/main_window.h" +#include "xenia/ui/loop.h" +#include "xenia/ui/platform.h" #include "xenia/xbox.h" namespace xe { @@ -36,8 +37,7 @@ class GraphicsSystem { Memory* memory() const { return memory_; } cpu::Processor* processor() const { return processor_; } - virtual X_STATUS Setup(cpu::Processor* processor, - ui::PlatformLoop* target_loop, + virtual X_STATUS Setup(cpu::Processor* processor, ui::Loop* target_loop, ui::PlatformWindow* target_window); virtual void Shutdown(); @@ -64,14 +64,14 @@ class GraphicsSystem { protected: GraphicsSystem(Emulator* emulator); - Emulator* emulator_; - Memory* memory_; - cpu::Processor* processor_; - ui::PlatformLoop* target_loop_; - ui::PlatformWindow* target_window_; + Emulator* emulator_ = nullptr; + Memory* memory_ = nullptr; + cpu::Processor* processor_ = nullptr; + ui::Loop* target_loop_ = nullptr; + ui::PlatformWindow* target_window_ = nullptr; - uint32_t interrupt_callback_; - uint32_t interrupt_callback_data_; + uint32_t interrupt_callback_ = 0; + uint32_t interrupt_callback_data_ = 0; }; } // namespace gpu diff --git a/src/xenia/gpu/xe-gpu-trace-viewer.cc b/src/xenia/gpu/xe-gpu-trace-viewer.cc index 19569542b..98b9cf36b 100644 --- a/src/xenia/gpu/xe-gpu-trace-viewer.cc +++ b/src/xenia/gpu/xe-gpu-trace-viewer.cc @@ -24,7 +24,6 @@ #include "xenia/gpu/xenos.h" #include "xenia/profiling.h" #include "xenia/ui/gl/gl_context.h" -#include "xenia/ui/main_window.h" // HACK: until we have another impl, we just use gl4 directly. #include "xenia/gpu/gl4/command_processor.h" @@ -843,7 +842,7 @@ class TracePlayer : public TraceReader { int current_command_index_; }; -void DrawControllerUI(xe::ui::MainWindow* window, TracePlayer& player, +void DrawControllerUI(xe::ui::PlatformWindow* window, TracePlayer& player, Memory* memory) { ImGui::SetNextWindowPos(ImVec2(5, 5), ImGuiSetCond_FirstUseEver); if (!ImGui::Begin("Controller", nullptr, ImVec2(340, 60))) { @@ -884,7 +883,7 @@ void DrawControllerUI(xe::ui::MainWindow* window, TracePlayer& player, ImGui::End(); } -void DrawCommandListUI(xe::ui::MainWindow* window, TracePlayer& player, +void DrawCommandListUI(xe::ui::PlatformWindow* window, TracePlayer& player, Memory* memory) { ImGui::SetNextWindowPos(ImVec2(5, 70), ImGuiSetCond_FirstUseEver); if (!ImGui::Begin("Command List", nullptr, ImVec2(200, 640))) { @@ -1028,7 +1027,7 @@ ShaderDisplayType DrawShaderTypeUI() { return shader_display_type; } -void DrawShaderUI(xe::ui::MainWindow* window, TracePlayer& player, +void DrawShaderUI(xe::ui::PlatformWindow* window, TracePlayer& player, Memory* memory, gl4::GL4Shader* shader, ShaderDisplayType display_type) { // Must be prepared for advanced display modes. @@ -1394,7 +1393,7 @@ static const char* kEndiannessNames[] = { "unspecified endianness", "8-in-16", "8-in-32", "16-in-32", }; -void DrawStateUI(xe::ui::MainWindow* window, TracePlayer& player, +void DrawStateUI(xe::ui::PlatformWindow* window, TracePlayer& player, Memory* memory) { auto gs = static_cast(player.graphics_system()); auto cp = gs->command_processor(); @@ -2034,8 +2033,8 @@ void DrawStateUI(xe::ui::MainWindow* window, TracePlayer& player, ImGui::End(); } -void DrawPacketDisassemblerUI(xe::ui::MainWindow* window, TracePlayer& player, - Memory* memory) { +void DrawPacketDisassemblerUI(xe::ui::PlatformWindow* window, + TracePlayer& player, Memory* memory) { ImGui::SetNextWindowCollapsed(true, ImGuiSetCond_FirstUseEver); ImGui::SetNextWindowPos(ImVec2(float(window->width()) - 500 - 5, 5), ImGuiSetCond_FirstUseEver); @@ -2176,7 +2175,8 @@ void DrawPacketDisassemblerUI(xe::ui::MainWindow* window, TracePlayer& player, ImGui::End(); } -void DrawUI(xe::ui::MainWindow* window, TracePlayer& player, Memory* memory) { +void DrawUI(xe::ui::PlatformWindow* window, TracePlayer& player, + Memory* memory) { // ImGui::ShowTestWindow(); DrawControllerUI(window, player, memory); @@ -2212,7 +2212,7 @@ int trace_viewer_main(std::vector& args) { // Normalize the path and make absolute. auto abs_path = xe::to_absolute_path(path); - auto window = emulator->main_window(); + 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); @@ -2306,7 +2306,7 @@ int trace_viewer_main(std::vector& args) { graphics_system->RequestSwap(); // Wait until we are exited. - emulator->main_window()->loop()->AwaitQuit(); + emulator->display_window()->loop()->AwaitQuit(); ImImpl_Shutdown(); } diff --git a/src/xenia/kernel/xam_ui.cc b/src/xenia/kernel/xam_ui.cc index 6da922b07..81d2a9445 100644 --- a/src/xenia/kernel/xam_ui.cc +++ b/src/xenia/kernel/xam_ui.cc @@ -67,7 +67,7 @@ SHIM_CALL XamShowMessageBoxUI_shim(PPCContext* ppc_context, TASKDIALOGCONFIG config = {0}; config.cbSize = sizeof(config); config.hInstance = GetModuleHandle(nullptr); - config.hwndParent = kernel_state->emulator()->main_window()->hwnd(); + config.hwndParent = kernel_state->emulator()->display_window()->hwnd(); config.dwFlags = TDF_ALLOW_DIALOG_CANCELLATION | // esc to exit TDF_POSITION_RELATIVE_TO_WINDOW; // center in window config.dwCommonButtons = 0; @@ -120,7 +120,7 @@ SHIM_CALL XamShowDirtyDiscErrorUI_shim(PPCContext* ppc_context, XELOGD("XamShowDirtyDiscErrorUI(%d)", user_index); int button_pressed = 0; - TaskDialog(kernel_state->emulator()->main_window()->hwnd(), + TaskDialog(kernel_state->emulator()->display_window()->hwnd(), GetModuleHandle(nullptr), L"Disc Read Error", L"Game is claiming to be unable to read game data!", nullptr, TDCBF_CLOSE_BUTTON, TD_ERROR_ICON, &button_pressed); diff --git a/src/xenia/ui/main_window.cc b/src/xenia/ui/main_window.cc index f2b40276e..95be90a2a 100644 --- a/src/xenia/ui/main_window.cc +++ b/src/xenia/ui/main_window.cc @@ -42,21 +42,22 @@ enum Commands { const std::wstring kBaseTitle = L"xenia"; MainWindow::MainWindow(Emulator* emulator) - : PlatformWindow(kBaseTitle), + : PlatformWindow(&loop_, kBaseTitle), emulator_(emulator), - main_menu_(MenuItem::Type::kNormal), - fullscreen_(false) {} + main_menu_(MenuItem::Type::kNormal) {} MainWindow::~MainWindow() = default; -void MainWindow::Start() { +std::unique_ptr MainWindow::Create(Emulator* emulator) { + std::unique_ptr main_window(new MainWindow(emulator)); + xe::threading::Fence fence; - loop_.Post([&]() { + main_window->loop()->Post([&main_window, &fence]() { xe::threading::set_name("Win32 Loop"); xe::Profiler::ThreadEnter("Win32 Loop"); - if (!Initialize()) { + if (!main_window->Initialize()) { XEFATAL("Failed to initialize main window"); exit(1); } @@ -65,6 +66,8 @@ void MainWindow::Start() { }); fence.Wait(); + + return std::unique_ptr(main_window.release()); } bool MainWindow::Initialize() { @@ -98,8 +101,8 @@ bool MainWindow::Initialize() { } break; case 0x1B: { // VK_ESCAPE // Allow users to escape fullscreen (but not enter it). - if (fullscreen_) { - ToggleFullscreen(); + if (is_fullscreen()) { + ToggleFullscreen(false); } } break; @@ -187,7 +190,7 @@ bool MainWindow::Initialize() { } main_menu_.AddChild(std::move(help_menu)); - SetMenu(&main_menu_); + set_menu(&main_menu_); Resize(1280, 720); @@ -204,11 +207,6 @@ void MainWindow::UpdateTitle() { set_title(title); } -void MainWindow::ToggleFullscreen() { - fullscreen_ = !fullscreen_; - SetFullscreen(fullscreen_); -} - void MainWindow::OnClose() { loop_.Quit(); @@ -250,7 +248,7 @@ void MainWindow::OnCommand(int id) { } break; case IDC_WINDOW_FULLSCREEN: { - ToggleFullscreen(); + ToggleFullscreen(!is_fullscreen()); } break; case IDC_HELP_WEBSITE: { diff --git a/src/xenia/ui/main_window.h b/src/xenia/ui/main_window.h index 7db737180..3f5a9bbcc 100644 --- a/src/xenia/ui/main_window.h +++ b/src/xenia/ui/main_window.h @@ -10,15 +10,10 @@ #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" -// TODO(benvanik): only on windows. -#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 { class Emulator; } // namespace xe @@ -26,26 +21,20 @@ class Emulator; namespace xe { namespace ui { -using PlatformLoop = xe::ui::win32::Win32Loop; -using PlatformWindow = xe::ui::win32::Win32Window; -using PlatformMenu = xe::ui::win32::Win32MenuItem; -using PlatformFilePicker = xe::ui::win32::Win32FilePicker; - class MainWindow : public PlatformWindow { public: - explicit MainWindow(Emulator* emulator); ~MainWindow() override; - Emulator* emulator() const { return emulator_; } - PlatformLoop* loop() { return &loop_; } + static std::unique_ptr Create(Emulator* emulator); - void Start(); + Emulator* emulator() const { return emulator_; } private: + explicit MainWindow(Emulator* emulator); + bool Initialize(); void UpdateTitle(); - void ToggleFullscreen(); void OnClose() override; void OnCommand(int id) override; @@ -53,7 +42,6 @@ class MainWindow : public PlatformWindow { Emulator* emulator_; PlatformLoop loop_; PlatformMenu main_menu_; - bool fullscreen_; }; } // namespace ui diff --git a/src/xenia/ui/platform.h b/src/xenia/ui/platform.h new file mode 100644 index 000000000..6ba60300f --- /dev/null +++ b/src/xenia/ui/platform.h @@ -0,0 +1,31 @@ +#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_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 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_ diff --git a/src/xenia/ui/win32/win32_window.cc b/src/xenia/ui/win32/win32_window.cc index 97d37225a..d335956ee 100644 --- a/src/xenia/ui/win32/win32_window.cc +++ b/src/xenia/ui/win32/win32_window.cc @@ -20,12 +20,10 @@ namespace xe { namespace ui { namespace win32 { -Win32Window::Win32Window(const std::wstring& title) - : Window(title), closing_(false), fullscreen_(false) { - menu_ = nullptr; -} +Win32Window::Win32Window(Loop* loop, const std::wstring& title) + : Window(loop, title) {} -Win32Window::~Win32Window() {} +Win32Window::~Win32Window() = default; bool Win32Window::Initialize() { Create(); @@ -160,8 +158,15 @@ void Win32Window::ResizeToFill(int32_t pad_left, int32_t pad_top, // TODO(benvanik): fullscreen. } -void Win32Window::SetFullscreen(bool fullscreen) { - fullscreen_ = 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) { @@ -200,7 +205,7 @@ void Win32Window::OnClose() { void Win32Window::OnSetMenu(MenuItem* menu) { Win32MenuItem* win_menu = reinterpret_cast(menu); // Don't actually set the menu if we're fullscreen. We'll do that later. - if (win_menu && !fullscreen_) { + if (win_menu && !is_fullscreen()) { ::SetMenu(hwnd_, win_menu->handle()); } } diff --git a/src/xenia/ui/win32/win32_window.h b/src/xenia/ui/win32/win32_window.h index eddb2f744..b655e46c1 100644 --- a/src/xenia/ui/win32/win32_window.h +++ b/src/xenia/ui/win32/win32_window.h @@ -22,7 +22,7 @@ namespace win32 { class Win32Window : public Window { public: - Win32Window(const std::wstring& title); + Win32Window(Loop* loop, const std::wstring& title); ~Win32Window() override; bool Initialize() override; @@ -35,7 +35,8 @@ class Win32Window : public Window { void ResizeToFill(int32_t pad_left, int32_t pad_top, int32_t pad_right, int32_t pad_bottom) override; - void SetFullscreen(bool fullscreen) override; + bool is_fullscreen() const override; + void ToggleFullscreen(bool fullscreen) override; protected: bool Create() override; @@ -49,9 +50,8 @@ class Win32Window : public Window { private: void EnableMMCSS(); - bool closing_; + bool closing_ = false; - bool fullscreen_; WINDOWPLACEMENT windowed_pos_; }; diff --git a/src/xenia/ui/window.h b/src/xenia/ui/window.h index aa98f2175..691335250 100644 --- a/src/xenia/ui/window.h +++ b/src/xenia/ui/window.h @@ -14,6 +14,7 @@ #include "xenia/base/delegate.h" #include "xenia/ui/control.h" +#include "xenia/ui/loop.h" #include "xenia/ui/menu_item.h" #include "xenia/ui/ui_event.h" @@ -27,6 +28,8 @@ class Window : public T { virtual bool Initialize() { return true; } + Loop* loop() const { return loop_; } + const std::wstring& title() const { return title_; } virtual bool set_title(const std::wstring& title) { if (title == title_) { @@ -36,6 +39,15 @@ 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); @@ -46,13 +58,8 @@ class Window : public T { on_closed(e); } - virtual void SetMenu(MenuItem* menu) { - menu_ = menu; - - OnSetMenu(menu); - } - - virtual void SetFullscreen(bool fullscreen) {} + virtual bool is_fullscreen() const { return false; } + virtual void ToggleFullscreen(bool fullscreen) {} public: Delegate on_shown; @@ -61,7 +68,8 @@ class Window : public T { Delegate on_closed; protected: - Window(const std::wstring& title) : T(0), title_(title) {} + Window(Loop* loop, const std::wstring& title) + : T(0), loop_(loop), title_(title) {} void OnShow() { if (is_visible_) { @@ -87,8 +95,9 @@ class Window : public T { virtual void OnCommand(int id) {} - MenuItem* menu_; + Loop* loop_ = nullptr; std::wstring title_; + MenuItem* menu_ = nullptr; }; } // namespace ui diff --git a/src/xenia/xenia_main.cc b/src/xenia/xenia_main.cc index 22c6f8934..d32240b7c 100644 --- a/src/xenia/xenia_main.cc +++ b/src/xenia/xenia_main.cc @@ -15,7 +15,6 @@ #include "xenia/kernel/kernel.h" #include "xenia/profiling.h" #include "xenia/ui/file_picker.h" -#include "xenia/ui/main_window.h" DEFINE_string(target, "", "Specifies the target .xex or .iso to execute."); @@ -61,7 +60,7 @@ int xenia_main(std::vector& args) { //{ L"Content Package (*.xcp)", L"*.xcp" }, {L"All Files (*.*)", L"*.*"}, }); - if (file_picker.Show(emulator->main_window()->hwnd())) { + if (file_picker.Show(emulator->display_window()->hwnd())) { auto selected_files = file_picker.selected_files(); if (!selected_files.empty()) { path = selected_files[0]; @@ -80,7 +79,7 @@ int xenia_main(std::vector& args) { } // Wait until we are exited. - emulator->main_window()->loop()->AwaitQuit(); + emulator->display_window()->loop()->AwaitQuit(); } emulator.reset();