diff --git a/libxenia.vcxproj b/libxenia.vcxproj index f1c28be46..adf121ff8 100644 --- a/libxenia.vcxproj +++ b/libxenia.vcxproj @@ -34,6 +34,7 @@ + diff --git a/libxenia.vcxproj.filters b/libxenia.vcxproj.filters index ef8268443..165bf7e35 100644 --- a/libxenia.vcxproj.filters +++ b/libxenia.vcxproj.filters @@ -712,9 +712,6 @@ src\xenia\cpu\backend\x64 - - src\xenia\apu - src\xenia\base @@ -778,6 +775,9 @@ src\xenia\apu + + src\xenia\base + diff --git a/src/xenia/base/platform.h b/src/xenia/base/platform.h index 184cd8388..f43fb4ff8 100644 --- a/src/xenia/base/platform.h +++ b/src/xenia/base/platform.h @@ -71,6 +71,8 @@ const char path_separator = '/'; const size_t max_path = 1024; // PATH_MAX #endif // XE_PLATFORM_WIN32 +void LaunchBrowser(const char* url); + } // namespace xe #endif // XENIA_BASE_PLATFORM_H_ diff --git a/src/xenia/base/platform_win.cc b/src/xenia/base/platform_win.cc new file mode 100644 index 000000000..bf12e2635 --- /dev/null +++ b/src/xenia/base/platform_win.cc @@ -0,0 +1,20 @@ +/** + ****************************************************************************** + * 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/base/platform.h" + +#include + +namespace xe { + +void LaunchBrowser(const char* url) { + ShellExecuteA(NULL, "open", url, NULL, NULL, SW_SHOWNORMAL); +} + +} // namespace xe diff --git a/src/xenia/profiling.cc b/src/xenia/profiling.cc index 3f2aaacbb..4e1589840 100644 --- a/src/xenia/profiling.cc +++ b/src/xenia/profiling.cc @@ -129,6 +129,10 @@ void Profiler::OnMouseWheel(int x, int y, int dy) { MicroProfileMousePosition(x, y, dy); } +void Profiler::ToggleDisplay() { MicroProfileToggleDisplayMode(); } + +void Profiler::TogglePause() { MicroProfileTogglePause(); } + #else void Profiler::OnMouseDown(bool left_button, bool right_button) {} @@ -139,6 +143,10 @@ void Profiler::OnMouseMove(int x, int y) {} void Profiler::OnMouseWheel(int x, int y, int dy) {} +void Profiler::ToggleDisplay() {} + +void Profiler::TogglePause() {} + #endif // XE_OPTION_PROFILING_UI void Profiler::set_display(std::unique_ptr display) { diff --git a/src/xenia/profiling.h b/src/xenia/profiling.h index 6c2253b35..ee02e5910 100644 --- a/src/xenia/profiling.h +++ b/src/xenia/profiling.h @@ -176,6 +176,8 @@ class Profiler { static void OnMouseUp(); static void OnMouseMove(int x, int y); static void OnMouseWheel(int x, int y, int dy); + static void ToggleDisplay(); + static void TogglePause(); // Gets the current display, if any. static ProfilerDisplay* display() { return display_.get(); } @@ -184,8 +186,6 @@ class Profiler { // Presents the profiler to the bound display, if any. static void Present(); - // TODO(benvanik): display mode/pause/etc? - private: static std::unique_ptr display_; }; diff --git a/src/xenia/ui/main_window.cc b/src/xenia/ui/main_window.cc index ec111f9cd..177c762bd 100644 --- a/src/xenia/ui/main_window.cc +++ b/src/xenia/ui/main_window.cc @@ -11,6 +11,7 @@ #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" @@ -19,6 +20,25 @@ 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) @@ -56,63 +76,116 @@ bool MainWindow::Initialize() { 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 - emulator()->graphics_system()->RequestFrameTrace(); - break; - } + OnCommand(Commands::IDC_GPU_TRACE_FRAME); + } break; case 0x74: { // VK_F5 - emulator()->graphics_system()->ClearCaches(); - break; - } + OnCommand(Commands::IDC_GPU_CLEAR_CACHES); + } break; + case 0x7A: { // VK_F11 - ToggleFullscreen(); - break; - } + OnCommand(Commands::IDC_WINDOW_FULLSCREEN); + } break; case 0x1B: { // VK_ESCAPE - // Allow users to escape fullscreen (but not enter it) + // Allow users to escape fullscreen (but not enter it). if (fullscreen_) { ToggleFullscreen(); } - } - case 0x6D: { // numpad minus - Clock::set_guest_time_scalar(Clock::guest_time_scalar() / 2.0); - UpdateTitle(); - break; - } - case 0x6B: { // numpad plus - Clock::set_guest_time_scalar(Clock::guest_time_scalar() * 2.0); - UpdateTitle(); - break; - } - case 0x0D: { // numpad enter - Clock::set_guest_time_scalar(1.0); - UpdateTitle(); - break; - } - default: { - handled = false; - break; - } + } break; + + case 0x70: { // VK_F1 + OnCommand(Commands::IDC_HELP_WEBSITE); + } break; + + default: { handled = false; } break; } e.set_handled(handled); }); - // Main menu + // Main menu. // FIXME: This code is really messy. - auto file = std::make_unique(MenuItem::Type::kPopup, L"&File"); - file->AddChild(std::make_unique( - MenuItem::Type::kString, Commands::IDC_FILE_OPEN, L"&Open")); + auto file_menu = + std::make_unique(MenuItem::Type::kPopup, L"&File"); + { + file_menu->AddChild(std::make_unique( + MenuItem::Type::kString, Commands::IDC_FILE_EXIT, L"E&xit", L"Alt+F4")); + } + main_menu_.AddChild(std::move(file_menu)); - main_menu_.AddChild(std::move(file)); + // CPU menu. + auto cpu_menu = + std::make_unique(MenuItem::Type::kPopup, L"&CPU"); + { + cpu_menu->AddChild(std::make_unique( + MenuItem::Type::kString, Commands::IDC_CPU_TIME_SCALAR_RESET, + L"&Reset Time Scalar", L"Numpad Enter")); + cpu_menu->AddChild(std::make_unique( + MenuItem::Type::kString, Commands::IDC_CPU_TIME_SCALAR_HALF, + L"Time Scalar /= 2", L"Numpad -")); + cpu_menu->AddChild(std::make_unique( + MenuItem::Type::kString, Commands::IDC_CPU_TIME_SCALAR_DOUBLE, + L"Time Scalar *= 2", L"Numpad +")); + } + cpu_menu->AddChild( + std::make_unique(MenuItem::Type::kSeparator)); + { + cpu_menu->AddChild(std::make_unique( + MenuItem::Type::kString, Commands::IDC_CPU_PROFILER_TOGGLE_DISPLAY, + L"Toggle Profiler &Display", L"Tab")); + cpu_menu->AddChild(std::make_unique( + MenuItem::Type::kString, Commands::IDC_CPU_PROFILER_TOGGLE_PAUSE, + L"&Pause/Resume Profiler", L"`")); + } + main_menu_.AddChild(std::move(cpu_menu)); - // Window submenu - auto window = + // GPU menu. + auto gpu_menu = + std::make_unique(MenuItem::Type::kPopup, L"&GPU"); + { + gpu_menu->AddChild(std::make_unique( + MenuItem::Type::kString, Commands::IDC_GPU_TRACE_FRAME, L"&Trace Frame", + L"F4")); + } + gpu_menu->AddChild( + std::make_unique(MenuItem::Type::kSeparator)); + { + gpu_menu->AddChild(std::make_unique( + MenuItem::Type::kString, Commands::IDC_GPU_CLEAR_CACHES, + L"&Clear Caches", L"F5")); + } + main_menu_.AddChild(std::move(gpu_menu)); + + // Window menu. + auto window_menu = std::make_unique(MenuItem::Type::kPopup, L"&Window"); - window->AddChild(std::make_unique( - MenuItem::Type::kString, Commands::IDC_WINDOW_FULLSCREEN, - L"Fullscreen\tF11")); + { + window_menu->AddChild(std::make_unique( + MenuItem::Type::kString, Commands::IDC_WINDOW_FULLSCREEN, + L"&Fullscreen", L"F11")); + } + main_menu_.AddChild(std::move(window_menu)); - main_menu_.AddChild(std::move(window)); + // Help menu. + auto help_menu = + std::make_unique(MenuItem::Type::kPopup, L"&Help"); + { + help_menu->AddChild(std::make_unique( + MenuItem::Type::kString, Commands::IDC_HELP_WEBSITE, L"&Website...", + L"F1")); + help_menu->AddChild(std::make_unique( + MenuItem::Type::kString, Commands::IDC_HELP_ABOUT, L"&About...")); + } + main_menu_.AddChild(std::move(help_menu)); SetMenu(&main_menu_); @@ -146,11 +219,46 @@ void MainWindow::OnClose() { void MainWindow::OnCommand(int id) { switch (id) { - // TODO: Setup delegates to MenuItems so we don't have to do this + 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(); - break; - } + } break; + + case IDC_HELP_WEBSITE: { + LaunchBrowser("http://xenia.jp"); + } break; + case IDC_HELP_ABOUT: { + LaunchBrowser("http://xenia.jp/about/"); + } break; } } diff --git a/src/xenia/ui/main_window.h b/src/xenia/ui/main_window.h index d5d6f8531..c4a4c0537 100644 --- a/src/xenia/ui/main_window.h +++ b/src/xenia/ui/main_window.h @@ -50,12 +50,6 @@ class MainWindow : public PlatformWindow { void OnClose() override; void OnCommand(int id) override; - enum Commands { - IDC_FILE_OPEN, - - IDC_WINDOW_FULLSCREEN, - }; - Emulator* emulator_; PlatformLoop loop_; PlatformMenu main_menu_; diff --git a/src/xenia/ui/menu_item.cc b/src/xenia/ui/menu_item.cc index 1b21ba5df..e77ebae61 100644 --- a/src/xenia/ui/menu_item.cc +++ b/src/xenia/ui/menu_item.cc @@ -14,8 +14,9 @@ namespace ui { MenuItem::MenuItem(Type type) : type_(type), parent_item_(nullptr) {} -MenuItem::MenuItem(Type type, const std::wstring& text) - : type_(type), parent_item_(nullptr), text_(text) {} +MenuItem::MenuItem(Type type, const std::wstring& text, + const std::wstring& hotkey) + : type_(type), parent_item_(nullptr), text_(text), hotkey_(hotkey) {} MenuItem::~MenuItem() = default; diff --git a/src/xenia/ui/menu_item.h b/src/xenia/ui/menu_item.h index 5d294779b..d21c70d9f 100644 --- a/src/xenia/ui/menu_item.h +++ b/src/xenia/ui/menu_item.h @@ -35,6 +35,7 @@ class MenuItem { MenuItem* parent_item() const { return parent_item_; } Type type() { return type_; } const std::wstring& text() { return text_; } + const std::wstring& hotkey() { return hotkey_; } void AddChild(MenuItem* child_item); void AddChild(std::unique_ptr child_item); @@ -45,7 +46,7 @@ class MenuItem { protected: MenuItem(Type type); - MenuItem(Type type, const std::wstring& text); + MenuItem(Type type, const std::wstring& text, const std::wstring& hotkey); virtual void OnChildAdded(MenuItem* child_item) {} virtual void OnChildRemoved(MenuItem* child_item) {} @@ -55,7 +56,8 @@ class MenuItem { Type type_; MenuItem* parent_item_; std::vector children_; - std::wstring text_; // Text associated with this item (typically the title) + std::wstring text_; + std::wstring hotkey_; }; } // namespace ui diff --git a/src/xenia/ui/win32/win32_menu_item.cc b/src/xenia/ui/win32/win32_menu_item.cc index 6116a2f7e..69993abc8 100644 --- a/src/xenia/ui/win32/win32_menu_item.cc +++ b/src/xenia/ui/win32/win32_menu_item.cc @@ -15,8 +15,9 @@ namespace xe { namespace ui { namespace win32 { -Win32MenuItem::Win32MenuItem(Type type, int id, const std::wstring& text) - : id_(id), MenuItem(type, text) { +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(); @@ -29,8 +30,9 @@ Win32MenuItem::Win32MenuItem(Type type, int id, const std::wstring& text) Win32MenuItem::Win32MenuItem(Type type) : Win32MenuItem(type, 0, L"") {} -Win32MenuItem::Win32MenuItem(Type type, const std::wstring& text) - : Win32MenuItem(type, 0, text) {} +Win32MenuItem::Win32MenuItem(Type type, const std::wstring& text, + const std::wstring& hotkey) + : Win32MenuItem(type, 0, text, hotkey) {} Win32MenuItem::~Win32MenuItem() { if (handle_) { @@ -51,8 +53,11 @@ void Win32MenuItem::OnChildAdded(MenuItem* generic_child_item) { AppendMenuW(handle_, MF_SEPARATOR, child_item->id(), 0); break; case MenuItem::Type::kString: - AppendMenuW(handle_, MF_STRING, child_item->id(), - child_item->text().c_str()); + 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; } } diff --git a/src/xenia/ui/win32/win32_menu_item.h b/src/xenia/ui/win32/win32_menu_item.h index 63cd7975c..392af2e55 100644 --- a/src/xenia/ui/win32/win32_menu_item.h +++ b/src/xenia/ui/win32/win32_menu_item.h @@ -22,8 +22,10 @@ namespace win32 { class Win32MenuItem : public MenuItem { public: Win32MenuItem(Type type); - Win32MenuItem(Type type, const std::wstring& text); - Win32MenuItem(Type type, int id, const std::wstring& text); + 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_; }