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