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\kernel\xobject.cc" />
|
||||||
<ClCompile Include="src\xenia\memory.cc" />
|
<ClCompile Include="src\xenia\memory.cc" />
|
||||||
<ClCompile Include="src\xenia\profiling.cc" />
|
<ClCompile Include="src\xenia\profiling.cc" />
|
||||||
<ClCompile Include="src\xenia\ui\control.cc" />
|
<ClCompile Include="src\xenia\ui\file_picker_win.cc" />
|
||||||
<ClCompile Include="src\xenia\ui\elemental_control.cc" />
|
|
||||||
<ClCompile Include="src\xenia\ui\gl\blitter.cc" />
|
<ClCompile Include="src\xenia\ui\gl\blitter.cc" />
|
||||||
<ClCompile Include="src\xenia\ui\gl\circular_buffer.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\gl4_elemental_renderer.cc" />
|
||||||
<ClCompile Include="src\xenia\ui\gl\gl_context.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\gl_profiler_display.cc" />
|
||||||
<ClCompile Include="src\xenia\ui\gl\wgl_control.cc" />
|
<ClCompile Include="src\xenia\ui\graphics_context.cc" />
|
||||||
<ClCompile Include="src\xenia\ui\gl\wgl_elemental_control.cc" />
|
<ClCompile Include="src\xenia\ui\loop.cc" />
|
||||||
<ClCompile Include="src\xenia\ui\main_window.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\menu_item.cc" />
|
||||||
<ClCompile Include="src\xenia\ui\win32\win32_control.cc" />
|
<ClCompile Include="src\xenia\ui\window_win.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\vfs\device.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_device.cc" />
|
||||||
<ClCompile Include="src\xenia\vfs\devices\disc_image_entry.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\kernel\xobject.h" />
|
||||||
<ClInclude Include="src\xenia\memory.h" />
|
<ClInclude Include="src\xenia\memory.h" />
|
||||||
<ClInclude Include="src\xenia\profiling.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\file_picker.h" />
|
||||||
<ClInclude Include="src\xenia\ui\gl\blitter.h" />
|
<ClInclude Include="src\xenia\ui\gl\blitter.h" />
|
||||||
<ClInclude Include="src\xenia\ui\gl\circular_buffer.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\gl4_elemental_renderer.h" />
|
||||||
<ClInclude Include="src\xenia\ui\gl\gl_context.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\gl_profiler_display.h" />
|
||||||
<ClInclude Include="src\xenia\ui\gl\wgl_control.h" />
|
<ClInclude Include="src\xenia\ui\graphics_context.h" />
|
||||||
<ClInclude Include="src\xenia\ui\gl\wgl_elemental_control.h" />
|
|
||||||
<ClInclude Include="src\xenia\ui\loop.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\menu_item.h" />
|
||||||
<ClInclude Include="src\xenia\ui\platform.h" />
|
|
||||||
<ClInclude Include="src\xenia\ui\ui_event.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.h" />
|
||||||
|
<ClInclude Include="src\xenia\ui\window_win.h" />
|
||||||
<ClInclude Include="src\xenia\vfs\device.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_device.h" />
|
||||||
<ClInclude Include="src\xenia\vfs\devices\disc_image_entry.h" />
|
<ClInclude Include="src\xenia\vfs\devices\disc_image_entry.h" />
|
||||||
|
|
|
@ -68,9 +68,6 @@
|
||||||
<Filter Include="src\xenia\ui">
|
<Filter Include="src\xenia\ui">
|
||||||
<UniqueIdentifier>{42d47a43-1af4-4e1a-9ed7-afa7f7d18e9f}</UniqueIdentifier>
|
<UniqueIdentifier>{42d47a43-1af4-4e1a-9ed7-afa7f7d18e9f}</UniqueIdentifier>
|
||||||
</Filter>
|
</Filter>
|
||||||
<Filter Include="src\xenia\ui\win32">
|
|
||||||
<UniqueIdentifier>{268545c9-fbdf-46d2-96f6-35188cec09d6}</UniqueIdentifier>
|
|
||||||
</Filter>
|
|
||||||
<Filter Include="src\xenia\kernel">
|
<Filter Include="src\xenia\kernel">
|
||||||
<UniqueIdentifier>{c1ac0db1-2f4b-4376-b1dc-e6355c99b395}</UniqueIdentifier>
|
<UniqueIdentifier>{c1ac0db1-2f4b-4376-b1dc-e6355c99b395}</UniqueIdentifier>
|
||||||
</Filter>
|
</Filter>
|
||||||
|
@ -159,7 +156,7 @@
|
||||||
<UniqueIdentifier>{82795389-e855-4cd6-a3b6-9580030cebf2}</UniqueIdentifier>
|
<UniqueIdentifier>{82795389-e855-4cd6-a3b6-9580030cebf2}</UniqueIdentifier>
|
||||||
</Filter>
|
</Filter>
|
||||||
<Filter Include="src\xenia\ui\gl">
|
<Filter Include="src\xenia\ui\gl">
|
||||||
<UniqueIdentifier>{c38dacd1-1e4c-4cd1-847e-19b394e8313d}</UniqueIdentifier>
|
<UniqueIdentifier>{f598eed5-11dc-4ef8-a7d4-28ec5e43009b}</UniqueIdentifier>
|
||||||
</Filter>
|
</Filter>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
@ -409,24 +406,6 @@
|
||||||
<ClCompile Include="src\xenia\hid\xinput\xinput_input_driver.cc">
|
<ClCompile Include="src\xenia\hid\xinput\xinput_input_driver.cc">
|
||||||
<Filter>src\xenia\hid\xinput</Filter>
|
<Filter>src\xenia\hid\xinput</Filter>
|
||||||
</ClCompile>
|
</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">
|
<ClCompile Include="src\xenia\ui\menu_item.cc">
|
||||||
<Filter>src\xenia\ui</Filter>
|
<Filter>src\xenia\ui</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
@ -655,9 +634,6 @@
|
||||||
<ClCompile Include="src\xenia\apu\xma_context.cc">
|
<ClCompile Include="src\xenia\apu\xma_context.cc">
|
||||||
<Filter>src\xenia\apu</Filter>
|
<Filter>src\xenia\apu</Filter>
|
||||||
</ClCompile>
|
</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">
|
<ClCompile Include="src\xenia\hid\hid_flags.cc">
|
||||||
<Filter>src\xenia\hid</Filter>
|
<Filter>src\xenia\hid</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
@ -715,30 +691,39 @@
|
||||||
<ClCompile Include="src\xenia\vfs\virtual_file_system.cc">
|
<ClCompile Include="src\xenia\vfs\virtual_file_system.cc">
|
||||||
<Filter>src\xenia\vfs</Filter>
|
<Filter>src\xenia\vfs</Filter>
|
||||||
</ClCompile>
|
</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">
|
<ClCompile Include="src\xenia\ui\gl\blitter.cc">
|
||||||
<Filter>src\xenia\ui\gl</Filter>
|
<Filter>src\xenia\ui\gl</Filter>
|
||||||
</ClCompile>
|
</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">
|
<ClCompile Include="src\xenia\ui\gl\circular_buffer.cc">
|
||||||
<Filter>src\xenia\ui\gl</Filter>
|
<Filter>src\xenia\ui\gl</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<ClCompile Include="src\xenia\ui\elemental_control.cc">
|
<ClCompile Include="src\xenia\ui\gl\gl_context.cc">
|
||||||
<Filter>src\xenia\ui</Filter>
|
<Filter>src\xenia\ui\gl</Filter>
|
||||||
</ClCompile>
|
</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>
|
<Filter>src\xenia\ui\gl</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<ClCompile Include="src\xenia\ui\gl\gl4_elemental_renderer.cc">
|
<ClCompile Include="src\xenia\ui\gl\gl4_elemental_renderer.cc">
|
||||||
<Filter>src\xenia\ui\gl</Filter>
|
<Filter>src\xenia\ui\gl</Filter>
|
||||||
</ClCompile>
|
</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>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClInclude Include="src\xenia\emulator.h">
|
<ClInclude Include="src\xenia\emulator.h">
|
||||||
|
@ -1008,27 +993,9 @@
|
||||||
<ClInclude Include="src\xenia\hid\xinput\xinput_input_driver.h">
|
<ClInclude Include="src\xenia\hid\xinput\xinput_input_driver.h">
|
||||||
<Filter>src\xenia\hid\xinput</Filter>
|
<Filter>src\xenia\hid\xinput</Filter>
|
||||||
</ClInclude>
|
</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">
|
<ClInclude Include="src\xenia\ui\loop.h">
|
||||||
<Filter>src\xenia\ui</Filter>
|
<Filter>src\xenia\ui</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
<ClInclude Include="src\xenia\ui\main_window.h">
|
|
||||||
<Filter>src\xenia\ui</Filter>
|
|
||||||
</ClInclude>
|
|
||||||
<ClInclude Include="src\xenia\ui\menu_item.h">
|
<ClInclude Include="src\xenia\ui\menu_item.h">
|
||||||
<Filter>src\xenia\ui</Filter>
|
<Filter>src\xenia\ui</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
@ -1296,9 +1263,6 @@
|
||||||
<ClInclude Include="src\xenia\ui\file_picker.h">
|
<ClInclude Include="src\xenia\ui\file_picker.h">
|
||||||
<Filter>src\xenia\ui</Filter>
|
<Filter>src\xenia\ui</Filter>
|
||||||
</ClInclude>
|
</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">
|
<ClInclude Include="src\xenia\hid\hid_flags.h">
|
||||||
<Filter>src\xenia\hid</Filter>
|
<Filter>src\xenia\hid</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
@ -1362,36 +1326,33 @@
|
||||||
<ClInclude Include="src\xenia\cpu\backend\x64\x64_stack_layout.h">
|
<ClInclude Include="src\xenia\cpu\backend\x64\x64_stack_layout.h">
|
||||||
<Filter>src\xenia\cpu\backend\x64</Filter>
|
<Filter>src\xenia\cpu\backend\x64</Filter>
|
||||||
</ClInclude>
|
</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">
|
<ClInclude Include="src\xenia\ui\gl\blitter.h">
|
||||||
<Filter>src\xenia\ui\gl</Filter>
|
<Filter>src\xenia\ui\gl</Filter>
|
||||||
</ClInclude>
|
</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">
|
<ClInclude Include="src\xenia\ui\gl\circular_buffer.h">
|
||||||
<Filter>src\xenia\ui\gl</Filter>
|
<Filter>src\xenia\ui\gl</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
<ClInclude Include="src\xenia\ui\platform.h">
|
|
||||||
<Filter>src\xenia\ui</Filter>
|
|
||||||
</ClInclude>
|
|
||||||
<ClInclude Include="src\xenia\ui\gl\gl.h">
|
<ClInclude Include="src\xenia\ui\gl\gl.h">
|
||||||
<Filter>src\xenia\ui\gl</Filter>
|
<Filter>src\xenia\ui\gl</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
<ClInclude Include="src\xenia\ui\elemental_control.h">
|
<ClInclude Include="src\xenia\ui\gl\gl_context.h">
|
||||||
<Filter>src\xenia\ui</Filter>
|
<Filter>src\xenia\ui\gl</Filter>
|
||||||
</ClInclude>
|
</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>
|
<Filter>src\xenia\ui\gl</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
<ClInclude Include="src\xenia\ui\gl\gl4_elemental_renderer.h">
|
<ClInclude Include="src\xenia\ui\gl\gl4_elemental_renderer.h">
|
||||||
<Filter>src\xenia\ui\gl</Filter>
|
<Filter>src\xenia\ui\gl</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
<ClInclude Include="src\xenia\ui\graphics_context.h">
|
||||||
|
<Filter>src\xenia\ui</Filter>
|
||||||
|
</ClInclude>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Include="src\xenia\cpu\hir\opcodes.inl">
|
<None Include="src\xenia\cpu\hir\opcodes.inl">
|
||||||
|
|
|
@ -27,7 +27,10 @@ Application* Application::current() {
|
||||||
return current_application_;
|
return current_application_;
|
||||||
}
|
}
|
||||||
|
|
||||||
Application::Application() { current_application_ = this; }
|
Application::Application() {
|
||||||
|
current_application_ = this;
|
||||||
|
loop_ = xe::ui::Loop::Create();
|
||||||
|
}
|
||||||
|
|
||||||
Application::~Application() {
|
Application::~Application() {
|
||||||
assert_true(current_application_ == this);
|
assert_true(current_application_ == this);
|
||||||
|
@ -65,7 +68,7 @@ bool Application::Initialize() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Application::Quit() {
|
void Application::Quit() {
|
||||||
loop_.Quit();
|
loop_->Quit();
|
||||||
|
|
||||||
// TODO(benvanik): proper exit.
|
// TODO(benvanik): proper exit.
|
||||||
XELOGI("User-initiated death!");
|
XELOGI("User-initiated death!");
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
#include "xenia/ui/platform.h"
|
#include "xenia/ui/loop.h"
|
||||||
|
|
||||||
namespace xe {
|
namespace xe {
|
||||||
namespace debug {
|
namespace debug {
|
||||||
|
@ -27,7 +27,7 @@ class Application {
|
||||||
static std::unique_ptr<Application> Create();
|
static std::unique_ptr<Application> Create();
|
||||||
static Application* current();
|
static Application* current();
|
||||||
|
|
||||||
xe::ui::Loop* loop() { return &loop_; }
|
xe::ui::Loop* loop() { return loop_.get(); }
|
||||||
MainWindow* main_window() const { return main_window_.get(); }
|
MainWindow* main_window() const { return main_window_.get(); }
|
||||||
|
|
||||||
void Quit();
|
void Quit();
|
||||||
|
@ -37,7 +37,7 @@ class Application {
|
||||||
|
|
||||||
bool Initialize();
|
bool Initialize();
|
||||||
|
|
||||||
xe::ui::PlatformLoop loop_;
|
std::unique_ptr<xe::ui::Loop> loop_;
|
||||||
std::unique_ptr<MainWindow> main_window_;
|
std::unique_ptr<MainWindow> main_window_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -15,51 +15,46 @@
|
||||||
#include "xenia/base/logging.h"
|
#include "xenia/base/logging.h"
|
||||||
#include "xenia/base/platform.h"
|
#include "xenia/base/platform.h"
|
||||||
#include "xenia/base/threading.h"
|
#include "xenia/base/threading.h"
|
||||||
|
#include "xenia/ui/gl/gl_context.h"
|
||||||
// TODO(benvanik): platform based.
|
|
||||||
#include "xenia/ui/gl/wgl_elemental_control.h"
|
|
||||||
|
|
||||||
namespace xe {
|
namespace xe {
|
||||||
namespace debug {
|
namespace debug {
|
||||||
namespace ui {
|
namespace ui {
|
||||||
|
|
||||||
using xe::ui::MenuItem;
|
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";
|
const std::wstring kBaseTitle = L"xenia debugger";
|
||||||
|
|
||||||
MainWindow::MainWindow(Application* app)
|
MainWindow::MainWindow(Application* app) : app_(app) {}
|
||||||
: PlatformWindow(app->loop(), kBaseTitle),
|
// main_menu_(MenuItem::Type::kNormal) {}
|
||||||
app_(app),
|
|
||||||
main_menu_(MenuItem::Type::kNormal) {}
|
|
||||||
|
|
||||||
MainWindow::~MainWindow() = default;
|
MainWindow::~MainWindow() = default;
|
||||||
|
|
||||||
bool MainWindow::Initialize() {
|
bool MainWindow::Initialize() {
|
||||||
if (!PlatformWindow::Initialize()) {
|
platform_window_ = xe::ui::Window::Create(app()->loop(), kBaseTitle);
|
||||||
|
if (!platform_window_) {
|
||||||
return false;
|
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;
|
bool handled = true;
|
||||||
switch (e.key_code()) {
|
switch (e.key_code()) {
|
||||||
case 0x1B: { // VK_ESCAPE
|
case 0x1B: { // VK_ESCAPE
|
||||||
// Allow users to escape fullscreen (but not enter it).
|
// Allow users to escape fullscreen (but not enter it).
|
||||||
if (is_fullscreen()) {
|
if (platform_window_->is_fullscreen()) {
|
||||||
ToggleFullscreen(false);
|
platform_window_->ToggleFullscreen(false);
|
||||||
}
|
}
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
case 0x70: { // VK_F1
|
case 0x70: { // VK_F1
|
||||||
OnCommand(Commands::IDC_HELP_WEBSITE);
|
LaunchBrowser("http://xenia.jp");
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
default: { handled = false; } break;
|
default: { handled = false; } break;
|
||||||
|
@ -68,35 +63,30 @@ bool MainWindow::Initialize() {
|
||||||
});
|
});
|
||||||
|
|
||||||
// Main menu.
|
// Main menu.
|
||||||
auto file_menu =
|
auto main_menu = MenuItem::Create(MenuItem::Type::kNormal);
|
||||||
std::make_unique<PlatformMenu>(MenuItem::Type::kPopup, L"&File");
|
auto file_menu = MenuItem::Create(MenuItem::Type::kPopup, L"&File");
|
||||||
{
|
{
|
||||||
file_menu->AddChild(std::make_unique<PlatformMenu>(
|
file_menu->AddChild(
|
||||||
MenuItem::Type::kString, Commands::IDC_FILE_EXIT, L"E&xit", L"Alt+F4"));
|
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.
|
// Help menu.
|
||||||
auto help_menu =
|
auto help_menu = MenuItem::Create(MenuItem::Type::kPopup, L"&Help");
|
||||||
std::make_unique<PlatformMenu>(MenuItem::Type::kPopup, L"&Help");
|
|
||||||
{
|
{
|
||||||
help_menu->AddChild(std::make_unique<PlatformMenu>(
|
help_menu->AddChild(
|
||||||
MenuItem::Type::kString, Commands::IDC_HELP_WEBSITE, L"&Website...",
|
MenuItem::Create(MenuItem::Type::kString, L"&Website...", L"F1",
|
||||||
L"F1"));
|
[]() { LaunchBrowser("http://xenia.jp"); }));
|
||||||
help_menu->AddChild(std::make_unique<PlatformMenu>(
|
help_menu->AddChild(
|
||||||
MenuItem::Type::kString, Commands::IDC_HELP_ABOUT, L"&About..."));
|
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.
|
platform_window_->Resize(1440, 1200);
|
||||||
// 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);
|
|
||||||
|
|
||||||
BuildUI();
|
BuildUI();
|
||||||
|
|
||||||
|
@ -107,7 +97,7 @@ void MainWindow::BuildUI() {
|
||||||
using namespace el::dsl;
|
using namespace el::dsl;
|
||||||
el::AnimationBlocker animation_blocker;
|
el::AnimationBlocker animation_blocker;
|
||||||
|
|
||||||
auto root_element = control_->root_element();
|
auto root_element = platform_window_->root_element();
|
||||||
window_ = std::make_unique<el::Window>();
|
window_ = std::make_unique<el::Window>();
|
||||||
window_->set_settings(el::WindowSettings::kFullScreen);
|
window_->set_settings(el::WindowSettings::kFullScreen);
|
||||||
root_element->AddChild(window_.get());
|
root_element->AddChild(window_.get());
|
||||||
|
@ -154,21 +144,6 @@ void MainWindow::BuildUI() {
|
||||||
|
|
||||||
void MainWindow::OnClose() { app_->Quit(); }
|
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 ui
|
||||||
} // namespace debug
|
} // namespace debug
|
||||||
} // namespace xe
|
} // namespace xe
|
||||||
|
|
|
@ -16,18 +16,16 @@
|
||||||
#include "xenia/debug/ui/application.h"
|
#include "xenia/debug/ui/application.h"
|
||||||
#include "xenia/debug/ui/views/cpu/cpu_view.h"
|
#include "xenia/debug/ui/views/cpu/cpu_view.h"
|
||||||
#include "xenia/debug/ui/views/gpu/gpu_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"
|
#include "xenia/ui/window.h"
|
||||||
|
|
||||||
namespace xe {
|
namespace xe {
|
||||||
namespace debug {
|
namespace debug {
|
||||||
namespace ui {
|
namespace ui {
|
||||||
|
|
||||||
class MainWindow : public xe::ui::PlatformWindow {
|
class MainWindow {
|
||||||
public:
|
public:
|
||||||
MainWindow(Application* app);
|
MainWindow(Application* app);
|
||||||
~MainWindow() override;
|
~MainWindow();
|
||||||
|
|
||||||
Application* app() const { return app_; }
|
Application* app() const { return app_; }
|
||||||
|
|
||||||
|
@ -38,13 +36,11 @@ class MainWindow : public xe::ui::PlatformWindow {
|
||||||
private:
|
private:
|
||||||
void BuildUI();
|
void BuildUI();
|
||||||
|
|
||||||
void OnClose() override;
|
void OnClose();
|
||||||
void OnCommand(int id) override;
|
|
||||||
|
|
||||||
Application* app_ = nullptr;
|
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_;
|
std::unique_ptr<el::Window> window_;
|
||||||
struct {
|
struct {
|
||||||
el::SplitContainer* split_container;
|
el::SplitContainer* split_container;
|
||||||
|
|
|
@ -23,7 +23,6 @@
|
||||||
#include "xenia/kernel/kernel_state.h"
|
#include "xenia/kernel/kernel_state.h"
|
||||||
#include "xenia/kernel/modules.h"
|
#include "xenia/kernel/modules.h"
|
||||||
#include "xenia/memory.h"
|
#include "xenia/memory.h"
|
||||||
#include "xenia/ui/main_window.h"
|
|
||||||
#include "xenia/vfs/virtual_file_system.h"
|
#include "xenia/vfs/virtual_file_system.h"
|
||||||
#include "xenia/vfs/devices/disc_image_device.h"
|
#include "xenia/vfs/devices/disc_image_device.h"
|
||||||
#include "xenia/vfs/devices/host_path_device.h"
|
#include "xenia/vfs/devices/host_path_device.h"
|
||||||
|
@ -68,14 +67,13 @@ Emulator::~Emulator() {
|
||||||
debugger_.reset();
|
debugger_.reset();
|
||||||
|
|
||||||
export_resolver_.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;
|
X_STATUS result = X_STATUS_UNSUCCESSFUL;
|
||||||
|
|
||||||
|
display_window_ = display_window;
|
||||||
|
|
||||||
// Initialize clock.
|
// Initialize clock.
|
||||||
// 360 uses a 50MHz clock.
|
// 360 uses a 50MHz clock.
|
||||||
Clock::set_guest_tick_frequency(50000000);
|
Clock::set_guest_tick_frequency(50000000);
|
||||||
|
@ -93,9 +91,6 @@ X_STATUS Emulator::Setup() {
|
||||||
&system_affinity_mask);
|
&system_affinity_mask);
|
||||||
SetProcessAffinityMask(process_handle, 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.
|
// Create memory system first, as it is required for other systems.
|
||||||
memory_ = std::make_unique<Memory>();
|
memory_ = std::make_unique<Memory>();
|
||||||
result = memory_->Initialize();
|
result = memory_->Initialize();
|
||||||
|
@ -129,6 +124,10 @@ X_STATUS Emulator::Setup() {
|
||||||
if (!graphics_system_) {
|
if (!graphics_system_) {
|
||||||
return X_STATUS_NOT_IMPLEMENTED;
|
return X_STATUS_NOT_IMPLEMENTED;
|
||||||
}
|
}
|
||||||
|
display_window_->loop()->PostSynchronous([this]() {
|
||||||
|
display_window_->set_context(
|
||||||
|
graphics_system_->CreateContext(display_window_));
|
||||||
|
});
|
||||||
|
|
||||||
// Initialize the HID.
|
// Initialize the HID.
|
||||||
input_system_ = std::move(xe::hid::InputSystem::Create(this));
|
input_system_ = std::move(xe::hid::InputSystem::Create(this));
|
||||||
|
@ -152,7 +151,7 @@ X_STATUS Emulator::Setup() {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
result = graphics_system_->Setup(processor_.get(), display_window_->loop(),
|
result = graphics_system_->Setup(processor_.get(), display_window_->loop(),
|
||||||
display_window_.get());
|
display_window_);
|
||||||
if (result) {
|
if (result) {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -171,6 +170,17 @@ X_STATUS Emulator::Setup() {
|
||||||
kernel_state_->LoadKernelModule<kernel::XboxkrnlModule>();
|
kernel_state_->LoadKernelModule<kernel::XboxkrnlModule>();
|
||||||
kernel_state_->LoadKernelModule<kernel::XamModule>();
|
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;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
#include "xenia/debug/debugger.h"
|
#include "xenia/debug/debugger.h"
|
||||||
#include "xenia/kernel/kernel_state.h"
|
#include "xenia/kernel/kernel_state.h"
|
||||||
#include "xenia/memory.h"
|
#include "xenia/memory.h"
|
||||||
#include "xenia/ui/platform.h"
|
#include "xenia/ui/window.h"
|
||||||
#include "xenia/vfs/virtual_file_system.h"
|
#include "xenia/vfs/virtual_file_system.h"
|
||||||
#include "xenia/xbox.h"
|
#include "xenia/xbox.h"
|
||||||
|
|
||||||
|
@ -35,9 +35,6 @@ class GraphicsSystem;
|
||||||
namespace hid {
|
namespace hid {
|
||||||
class InputSystem;
|
class InputSystem;
|
||||||
} // namespace hid
|
} // namespace hid
|
||||||
namespace ui {
|
|
||||||
class MainWindow;
|
|
||||||
} // namespace ui
|
|
||||||
} // namespace xe
|
} // namespace xe
|
||||||
|
|
||||||
namespace xe {
|
namespace xe {
|
||||||
|
@ -49,7 +46,7 @@ class Emulator {
|
||||||
|
|
||||||
const std::wstring& command_line() const { return command_line_; }
|
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(); }
|
Memory* memory() const { return memory_.get(); }
|
||||||
|
|
||||||
|
@ -70,7 +67,7 @@ class Emulator {
|
||||||
|
|
||||||
kernel::KernelState* kernel_state() const { return kernel_state_.get(); }
|
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 LaunchPath(std::wstring path);
|
||||||
X_STATUS LaunchXexFile(std::wstring path);
|
X_STATUS LaunchXexFile(std::wstring path);
|
||||||
|
@ -83,7 +80,7 @@ class Emulator {
|
||||||
|
|
||||||
std::wstring command_line_;
|
std::wstring command_line_;
|
||||||
|
|
||||||
std::unique_ptr<ui::PlatformWindow> display_window_;
|
ui::Window* display_window_;
|
||||||
|
|
||||||
std::unique_ptr<Memory> memory_;
|
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_pixel_shader_(nullptr),
|
||||||
active_framebuffer_(nullptr),
|
active_framebuffer_(nullptr),
|
||||||
last_framebuffer_texture_(0),
|
last_framebuffer_texture_(0),
|
||||||
last_swap_width_(0),
|
|
||||||
last_swap_height_(0),
|
|
||||||
point_list_geometry_program_(0),
|
point_list_geometry_program_(0),
|
||||||
rect_list_geometry_program_(0),
|
rect_list_geometry_program_(0),
|
||||||
quad_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_); }
|
CommandProcessor::~CommandProcessor() { CloseHandle(write_ptr_index_event_); }
|
||||||
|
|
||||||
bool CommandProcessor::Initialize(
|
bool CommandProcessor::Initialize(
|
||||||
std::unique_ptr<xe::ui::gl::GLContext> context) {
|
std::unique_ptr<xe::ui::GraphicsContext> context) {
|
||||||
context_ = std::move(context);
|
context_ = std::move(context);
|
||||||
|
|
||||||
worker_running_ = true;
|
worker_running_ = true;
|
||||||
|
@ -197,7 +195,7 @@ void CommandProcessor::WorkerThreadMain() {
|
||||||
// We've run out of commands to execute.
|
// We've run out of commands to execute.
|
||||||
// We spin here waiting for new ones, as the overhead of waiting on our
|
// We spin here waiting for new ones, as the overhead of waiting on our
|
||||||
// event is too high.
|
// event is too high.
|
||||||
// PrepareForWait();
|
PrepareForWait();
|
||||||
do {
|
do {
|
||||||
// TODO(benvanik): if we go longer than Nms, switch to waiting?
|
// TODO(benvanik): if we go longer than Nms, switch to waiting?
|
||||||
// It'll keep us from burning power.
|
// It'll keep us from burning power.
|
||||||
|
@ -209,7 +207,7 @@ void CommandProcessor::WorkerThreadMain() {
|
||||||
} while (worker_running_ && pending_fns_.empty() &&
|
} while (worker_running_ && pending_fns_.empty() &&
|
||||||
(write_ptr_index == 0xBAADF00D ||
|
(write_ptr_index == 0xBAADF00D ||
|
||||||
read_ptr_index_ == write_ptr_index));
|
read_ptr_index_ == write_ptr_index));
|
||||||
// ReturnFromWait();
|
ReturnFromWait();
|
||||||
if (!worker_running_ || !pending_fns_.empty()) {
|
if (!worker_running_ || !pending_fns_.empty()) {
|
||||||
continue;
|
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,
|
void CommandProcessor::IssueSwap(uint32_t frontbuffer_width,
|
||||||
uint32_t frontbuffer_height) {
|
uint32_t frontbuffer_height) {
|
||||||
if (!swap_handler_) {
|
SCOPE_profile_cpu_f("gpu");
|
||||||
|
if (!swap_request_handler_) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto& regs = *register_file_;
|
// If there was a swap pending we drop it on the floor.
|
||||||
SwapParameters swap_params;
|
// 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.
|
// Lookup the framebuffer in the recently-resolved list.
|
||||||
// TODO(benvanik): make this much more sophisticated.
|
// 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).
|
// TODO(benvanik): handle dirty cases (resolved to sysmem, touched).
|
||||||
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||||
// HACK: just use whatever our current framebuffer is.
|
// HACK: just use whatever our current framebuffer is.
|
||||||
swap_params.framebuffer_texture = last_framebuffer_texture_;
|
GLuint framebuffer_texture = last_framebuffer_texture_;
|
||||||
/*swap_params.framebuffer_texture = active_framebuffer_
|
/*GLuint framebuffer_texture = active_framebuffer_
|
||||||
? active_framebuffer_->color_targets[0]
|
? active_framebuffer_->color_targets[0]
|
||||||
: last_framebuffer_texture_;*/
|
: last_framebuffer_texture_;*/
|
||||||
|
|
||||||
// Frontbuffer dimensions, if valid.
|
// Copy the the given framebuffer to the current backbuffer.
|
||||||
swap_params.x = 0;
|
Rect2D src_rect(0, 0, frontbuffer_width ? frontbuffer_width : 1280,
|
||||||
swap_params.y = 0;
|
frontbuffer_height ? frontbuffer_height : 720);
|
||||||
swap_params.width = frontbuffer_width ? frontbuffer_width : 1280;
|
Rect2D dest_rect(0, 0, swap_state_.width, swap_state_.height);
|
||||||
swap_params.height = frontbuffer_height ? frontbuffer_height : 720;
|
reinterpret_cast<xe::ui::gl::GLContext*>(context_.get())
|
||||||
|
->blitter()
|
||||||
|
->CopyColorTexture2D(framebuffer_texture, src_rect,
|
||||||
|
swap_state_.back_buffer_texture, dest_rect,
|
||||||
|
GL_LINEAR);
|
||||||
|
|
||||||
PrepareForWait();
|
// Need to finish to be sure the other context sees the right data.
|
||||||
swap_handler_(swap_params);
|
// TODO(benvanik): prevent this? fences?
|
||||||
ReturnFromWait();
|
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.
|
// Remove any dead textures, etc.
|
||||||
texture_cache_.Scavenge();
|
texture_cache_.Scavenge();
|
||||||
|
@ -964,8 +1008,6 @@ bool CommandProcessor::ExecutePacketType3_XE_SWAP(RingbufferReader* reader,
|
||||||
uint32_t frontbuffer_width = reader->Read();
|
uint32_t frontbuffer_width = reader->Read();
|
||||||
uint32_t frontbuffer_height = reader->Read();
|
uint32_t frontbuffer_height = reader->Read();
|
||||||
reader->Advance(count - 4);
|
reader->Advance(count - 4);
|
||||||
last_swap_width_ = frontbuffer_width;
|
|
||||||
last_swap_height_ = frontbuffer_height;
|
|
||||||
|
|
||||||
// Ensure we issue any pending draws.
|
// Ensure we issue any pending draws.
|
||||||
draw_batcher_.Flush(DrawBatcher::FlushMode::kMakeCoherent);
|
draw_batcher_.Flush(DrawBatcher::FlushMode::kMakeCoherent);
|
||||||
|
@ -2757,6 +2799,8 @@ bool CommandProcessor::IssueCopy() {
|
||||||
// TODO(benvanik): copy to staging texture then PBO back?
|
// TODO(benvanik): copy to staging texture then PBO back?
|
||||||
void* ptr = memory_->TranslatePhysical(copy_dest_base);
|
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.
|
// Make active so glReadPixels reads from us.
|
||||||
switch (copy_command) {
|
switch (copy_command) {
|
||||||
case CopyCommand::kRaw: {
|
case CopyCommand::kRaw: {
|
||||||
|
@ -2766,8 +2810,8 @@ bool CommandProcessor::IssueCopy() {
|
||||||
// Source from a bound render target.
|
// Source from a bound render target.
|
||||||
// TODO(benvanik): RAW copy.
|
// TODO(benvanik): RAW copy.
|
||||||
last_framebuffer_texture_ = texture_cache_.CopyTexture(
|
last_framebuffer_texture_ = texture_cache_.CopyTexture(
|
||||||
context_->blitter(), copy_dest_base, dest_logical_width,
|
blitter, copy_dest_base, dest_logical_width, dest_logical_height,
|
||||||
dest_logical_height, dest_block_width, dest_block_height,
|
dest_block_width, dest_block_height,
|
||||||
ColorFormatToTextureFormat(copy_dest_format),
|
ColorFormatToTextureFormat(copy_dest_format),
|
||||||
copy_dest_swap ? true : false, color_targets[copy_src_select],
|
copy_dest_swap ? true : false, color_targets[copy_src_select],
|
||||||
src_rect, dest_rect);
|
src_rect, dest_rect);
|
||||||
|
@ -2777,11 +2821,10 @@ bool CommandProcessor::IssueCopy() {
|
||||||
} else {
|
} else {
|
||||||
// Source from the bound depth/stencil target.
|
// Source from the bound depth/stencil target.
|
||||||
// TODO(benvanik): RAW copy.
|
// TODO(benvanik): RAW copy.
|
||||||
texture_cache_.CopyTexture(context_->blitter(), copy_dest_base,
|
texture_cache_.CopyTexture(
|
||||||
dest_logical_width, dest_logical_height,
|
blitter, copy_dest_base, dest_logical_width, dest_logical_height,
|
||||||
dest_block_width, dest_block_height,
|
dest_block_width, dest_block_height, src_format,
|
||||||
src_format, copy_dest_swap ? true : false,
|
copy_dest_swap ? true : false, depth_target, src_rect, dest_rect);
|
||||||
depth_target, src_rect, dest_rect);
|
|
||||||
if (!FLAGS_disable_framebuffer_readback) {
|
if (!FLAGS_disable_framebuffer_readback) {
|
||||||
// glReadPixels(x, y, w, h, GL_DEPTH_STENCIL, read_type, ptr);
|
// 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
|
// Either copy the readbuffer into an existing texture or create a new
|
||||||
// one in the cache so we can service future upload requests.
|
// one in the cache so we can service future upload requests.
|
||||||
last_framebuffer_texture_ = texture_cache_.ConvertTexture(
|
last_framebuffer_texture_ = texture_cache_.ConvertTexture(
|
||||||
context_->blitter(), copy_dest_base, dest_logical_width,
|
blitter, copy_dest_base, dest_logical_width, dest_logical_height,
|
||||||
dest_logical_height, dest_block_width, dest_block_height,
|
dest_block_width, dest_block_height,
|
||||||
ColorFormatToTextureFormat(copy_dest_format),
|
ColorFormatToTextureFormat(copy_dest_format),
|
||||||
copy_dest_swap ? true : false, color_targets[copy_src_select],
|
copy_dest_swap ? true : false, color_targets[copy_src_select],
|
||||||
src_rect, dest_rect);
|
src_rect, dest_rect);
|
||||||
|
@ -2804,11 +2847,10 @@ bool CommandProcessor::IssueCopy() {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Source from the bound depth/stencil target.
|
// Source from the bound depth/stencil target.
|
||||||
texture_cache_.ConvertTexture(context_->blitter(), copy_dest_base,
|
texture_cache_.ConvertTexture(
|
||||||
dest_logical_width, dest_logical_height,
|
blitter, copy_dest_base, dest_logical_width, dest_logical_height,
|
||||||
dest_block_width, dest_block_height,
|
dest_block_width, dest_block_height, src_format,
|
||||||
src_format, copy_dest_swap ? true : false,
|
copy_dest_swap ? true : false, depth_target, src_rect, dest_rect);
|
||||||
depth_target, src_rect, dest_rect);
|
|
||||||
if (!FLAGS_disable_framebuffer_readback) {
|
if (!FLAGS_disable_framebuffer_readback) {
|
||||||
// glReadPixels(x, y, w, h, GL_DEPTH_STENCIL, read_type, ptr);
|
// glReadPixels(x, y, w, h, GL_DEPTH_STENCIL, read_type, ptr);
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,13 +42,18 @@ namespace gl4 {
|
||||||
|
|
||||||
class GL4GraphicsSystem;
|
class GL4GraphicsSystem;
|
||||||
|
|
||||||
struct SwapParameters {
|
struct SwapState {
|
||||||
uint32_t x;
|
// Lock must be held when changing data in this structure.
|
||||||
uint32_t y;
|
xe::mutex mutex;
|
||||||
uint32_t width;
|
// Dimensions of the framebuffer textures. Should match window size.
|
||||||
uint32_t height;
|
uint32_t width = 0;
|
||||||
|
uint32_t height = 0;
|
||||||
GLuint framebuffer_texture;
|
// 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 {
|
enum class SwapMode {
|
||||||
|
@ -61,22 +66,23 @@ class CommandProcessor {
|
||||||
CommandProcessor(GL4GraphicsSystem* graphics_system);
|
CommandProcessor(GL4GraphicsSystem* graphics_system);
|
||||||
~CommandProcessor();
|
~CommandProcessor();
|
||||||
|
|
||||||
typedef std::function<void(const SwapParameters& params)> SwapHandler;
|
|
||||||
void set_swap_handler(SwapHandler fn) { swap_handler_ = fn; }
|
|
||||||
|
|
||||||
uint32_t counter() const { return counter_; }
|
uint32_t counter() const { return counter_; }
|
||||||
void increment_counter() { 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 Shutdown();
|
||||||
void CallInThread(std::function<void()> fn);
|
void CallInThread(std::function<void()> fn);
|
||||||
|
|
||||||
void ClearCaches();
|
void ClearCaches();
|
||||||
|
|
||||||
|
SwapState& swap_state() { return swap_state_; }
|
||||||
void set_swap_mode(SwapMode swap_mode) { swap_mode_ = swap_mode; }
|
void set_swap_mode(SwapMode swap_mode) { swap_mode_ = swap_mode; }
|
||||||
void IssueSwap();
|
|
||||||
void IssueSwap(uint32_t frontbuffer_width, uint32_t frontbuffer_height);
|
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 RequestFrameTrace(const std::wstring& root_path);
|
||||||
void BeginTracing(const std::wstring& root_path);
|
void BeginTracing(const std::wstring& root_path);
|
||||||
void EndTracing();
|
void EndTracing();
|
||||||
|
@ -238,11 +244,11 @@ class CommandProcessor {
|
||||||
std::atomic<bool> worker_running_;
|
std::atomic<bool> worker_running_;
|
||||||
kernel::object_ref<kernel::XHostThread> worker_thread_;
|
kernel::object_ref<kernel::XHostThread> worker_thread_;
|
||||||
|
|
||||||
std::unique_ptr<xe::ui::gl::GLContext> context_;
|
std::unique_ptr<xe::ui::GraphicsContext> context_;
|
||||||
SwapHandler swap_handler_;
|
|
||||||
std::queue<std::function<void()>> pending_fns_;
|
|
||||||
|
|
||||||
SwapMode swap_mode_;
|
SwapMode swap_mode_;
|
||||||
|
SwapState swap_state_;
|
||||||
|
std::function<void()> swap_request_handler_;
|
||||||
|
std::queue<std::function<void()>> pending_fns_;
|
||||||
|
|
||||||
uint32_t counter_;
|
uint32_t counter_;
|
||||||
|
|
||||||
|
@ -266,8 +272,6 @@ class CommandProcessor {
|
||||||
GL4Shader* active_pixel_shader_;
|
GL4Shader* active_pixel_shader_;
|
||||||
CachedFramebuffer* active_framebuffer_;
|
CachedFramebuffer* active_framebuffer_;
|
||||||
GLuint last_framebuffer_texture_;
|
GLuint last_framebuffer_texture_;
|
||||||
uint32_t last_swap_width_;
|
|
||||||
uint32_t last_swap_height_;
|
|
||||||
|
|
||||||
std::vector<CachedFramebuffer> cached_framebuffers_;
|
std::vector<CachedFramebuffer> cached_framebuffers_;
|
||||||
std::vector<CachedColorRenderTarget> cached_color_render_targets_;
|
std::vector<CachedColorRenderTarget> cached_color_render_targets_;
|
||||||
|
|
|
@ -19,7 +19,8 @@
|
||||||
#include "xenia/gpu/gl4/gl4_gpu_flags.h"
|
#include "xenia/gpu/gl4/gl4_gpu_flags.h"
|
||||||
#include "xenia/gpu/gpu_flags.h"
|
#include "xenia/gpu/gpu_flags.h"
|
||||||
#include "xenia/gpu/tracing.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 xe {
|
||||||
namespace gpu {
|
namespace gpu {
|
||||||
|
@ -47,6 +48,14 @@ std::unique_ptr<GraphicsSystem> GL4GraphicsSystem::Create(Emulator* emulator) {
|
||||||
return std::make_unique<GL4GraphicsSystem>(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)
|
GL4GraphicsSystem::GL4GraphicsSystem(Emulator* emulator)
|
||||||
: GraphicsSystem(emulator), worker_running_(false) {}
|
: GraphicsSystem(emulator), worker_running_(false) {}
|
||||||
|
|
||||||
|
@ -54,38 +63,36 @@ GL4GraphicsSystem::~GL4GraphicsSystem() = default;
|
||||||
|
|
||||||
X_STATUS GL4GraphicsSystem::Setup(cpu::Processor* processor,
|
X_STATUS GL4GraphicsSystem::Setup(cpu::Processor* processor,
|
||||||
ui::Loop* target_loop,
|
ui::Loop* target_loop,
|
||||||
ui::PlatformWindow* target_window) {
|
ui::Window* target_window) {
|
||||||
auto result = GraphicsSystem::Setup(processor, target_loop, target_window);
|
auto result = GraphicsSystem::Setup(processor, target_loop, target_window);
|
||||||
if (result) {
|
if (result) {
|
||||||
return 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.
|
// Create rendering control.
|
||||||
// This must happen on the UI thread.
|
// This must happen on the UI thread.
|
||||||
xe::threading::Fence control_ready_fence;
|
std::unique_ptr<xe::ui::GraphicsContext> processor_context;
|
||||||
std::unique_ptr<xe::ui::gl::GLContext> processor_context;
|
target_loop_->PostSynchronous([&]() {
|
||||||
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());
|
|
||||||
|
|
||||||
// Setup the GL context the command processor will do all its drawing in.
|
// 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.
|
// from it.
|
||||||
processor_context = control_->context()->CreateShared();
|
processor_context = display_context_->CreateShared();
|
||||||
|
processor_context->ClearCurrent();
|
||||||
{
|
|
||||||
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();
|
|
||||||
});
|
});
|
||||||
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
|
// Create command processor. This will spin up a thread to process all
|
||||||
// incoming ringbuffer packets.
|
// incoming ringbuffer packets.
|
||||||
|
@ -94,8 +101,8 @@ X_STATUS GL4GraphicsSystem::Setup(cpu::Processor* processor,
|
||||||
XELOGE("Unable to initialize command processor");
|
XELOGE("Unable to initialize command processor");
|
||||||
return X_STATUS_UNSUCCESSFUL;
|
return X_STATUS_UNSUCCESSFUL;
|
||||||
}
|
}
|
||||||
command_processor_->set_swap_handler(
|
command_processor_->set_swap_request_handler(
|
||||||
[this](const SwapParameters& swap_params) { SwapHandler(swap_params); });
|
[this]() { target_window_->Invalidate(); });
|
||||||
|
|
||||||
// Let the processor know we want register access callbacks.
|
// Let the processor know we want register access callbacks.
|
||||||
memory_->AddVirtualMappedRange(
|
memory_->AddVirtualMappedRange(
|
||||||
|
@ -144,7 +151,6 @@ void GL4GraphicsSystem::Shutdown() {
|
||||||
// TODO(benvanik): remove mapped range.
|
// TODO(benvanik): remove mapped range.
|
||||||
|
|
||||||
command_processor_.reset();
|
command_processor_.reset();
|
||||||
control_.reset();
|
|
||||||
|
|
||||||
GraphicsSystem::Shutdown();
|
GraphicsSystem::Shutdown();
|
||||||
}
|
}
|
||||||
|
@ -159,10 +165,6 @@ void GL4GraphicsSystem::EnableReadPointerWriteBack(uint32_t ptr,
|
||||||
command_processor_->EnableReadPointerWriteBack(ptr, block_size);
|
command_processor_->EnableReadPointerWriteBack(ptr, block_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GL4GraphicsSystem::RequestSwap() {
|
|
||||||
command_processor_->CallInThread([&]() { command_processor_->IssueSwap(); });
|
|
||||||
}
|
|
||||||
|
|
||||||
void GL4GraphicsSystem::RequestFrameTrace() {
|
void GL4GraphicsSystem::RequestFrameTrace() {
|
||||||
command_processor_->RequestFrameTrace(xe::to_wstring(FLAGS_trace_gpu_prefix));
|
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_->set_swap_mode(SwapMode::kNormal);
|
||||||
|
command_processor_->IssueSwap(1280, 720);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -288,22 +291,29 @@ void GL4GraphicsSystem::MarkVblank() {
|
||||||
DispatchInterruptCallback(0, 2);
|
DispatchInterruptCallback(0, 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GL4GraphicsSystem::SwapHandler(const SwapParameters& swap_params) {
|
void GL4GraphicsSystem::Swap(xe::ui::UIEvent& e) {
|
||||||
SCOPE_profile_cpu_f("gpu");
|
// 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Swap requested. Synchronously post a request to the loop so that
|
if (!swap_state.front_buffer_texture) {
|
||||||
// we do the swap in the right thread.
|
// Not yet ready.
|
||||||
control_->SynchronousRepaint([this, swap_params]() {
|
|
||||||
if (!swap_params.framebuffer_texture) {
|
|
||||||
// no-op.
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Rect2D src_rect(swap_params.x, swap_params.y, swap_params.width,
|
|
||||||
swap_params.height);
|
// Blit the frontbuffer.
|
||||||
Rect2D dest_rect(0, 0, control_->width(), control_->height());
|
display_context_->blitter()->BlitTexture2D(
|
||||||
control_->context()->blitter()->BlitTexture2D(
|
swap_state.front_buffer_texture,
|
||||||
swap_params.framebuffer_texture, src_rect, dest_rect, GL_LINEAR);
|
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) {
|
uint64_t GL4GraphicsSystem::ReadRegister(uint32_t addr) {
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
#include "xenia/gpu/graphics_system.h"
|
#include "xenia/gpu/graphics_system.h"
|
||||||
#include "xenia/gpu/register_file.h"
|
#include "xenia/gpu/register_file.h"
|
||||||
#include "xenia/kernel/objects/xthread.h"
|
#include "xenia/kernel/objects/xthread.h"
|
||||||
#include "xenia/ui/gl/wgl_control.h"
|
#include "xenia/ui/gl/gl_context.h"
|
||||||
|
|
||||||
namespace xe {
|
namespace xe {
|
||||||
namespace gpu {
|
namespace gpu {
|
||||||
|
@ -28,9 +28,11 @@ class GL4GraphicsSystem : public GraphicsSystem {
|
||||||
~GL4GraphicsSystem() override;
|
~GL4GraphicsSystem() override;
|
||||||
|
|
||||||
static std::unique_ptr<GraphicsSystem> Create(Emulator* emulator);
|
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,
|
X_STATUS Setup(cpu::Processor* processor, ui::Loop* target_loop,
|
||||||
ui::PlatformWindow* target_window) override;
|
ui::Window* target_window) override;
|
||||||
void Shutdown() override;
|
void Shutdown() override;
|
||||||
|
|
||||||
RegisterFile* register_file() { return ®ister_file_; }
|
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 InitializeRingBuffer(uint32_t ptr, uint32_t page_count) override;
|
||||||
void EnableReadPointerWriteBack(uint32_t ptr, uint32_t block_size) override;
|
void EnableReadPointerWriteBack(uint32_t ptr, uint32_t block_size) override;
|
||||||
|
|
||||||
void RequestSwap() override;
|
|
||||||
|
|
||||||
void RequestFrameTrace() override;
|
void RequestFrameTrace() override;
|
||||||
void BeginTracing() override;
|
void BeginTracing() override;
|
||||||
void EndTracing() override;
|
void EndTracing() override;
|
||||||
|
@ -52,7 +52,7 @@ class GL4GraphicsSystem : public GraphicsSystem {
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void MarkVblank();
|
void MarkVblank();
|
||||||
void SwapHandler(const SwapParameters& swap_params);
|
void Swap(xe::ui::UIEvent& e);
|
||||||
uint64_t ReadRegister(uint32_t addr);
|
uint64_t ReadRegister(uint32_t addr);
|
||||||
void WriteRegister(uint32_t addr, uint64_t value);
|
void WriteRegister(uint32_t addr, uint64_t value);
|
||||||
|
|
||||||
|
@ -67,7 +67,8 @@ class GL4GraphicsSystem : public GraphicsSystem {
|
||||||
|
|
||||||
RegisterFile register_file_;
|
RegisterFile register_file_;
|
||||||
std::unique_ptr<CommandProcessor> command_processor_;
|
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_;
|
std::atomic<bool> worker_running_;
|
||||||
kernel::object_ref<kernel::XHostThread> worker_thread_;
|
kernel::object_ref<kernel::XHostThread> worker_thread_;
|
||||||
|
|
|
@ -43,7 +43,7 @@ GraphicsSystem::GraphicsSystem(Emulator* emulator) : emulator_(emulator) {}
|
||||||
GraphicsSystem::~GraphicsSystem() = default;
|
GraphicsSystem::~GraphicsSystem() = default;
|
||||||
|
|
||||||
X_STATUS GraphicsSystem::Setup(cpu::Processor* processor, ui::Loop* target_loop,
|
X_STATUS GraphicsSystem::Setup(cpu::Processor* processor, ui::Loop* target_loop,
|
||||||
ui::PlatformWindow* target_window) {
|
ui::Window* target_window) {
|
||||||
processor_ = processor;
|
processor_ = processor;
|
||||||
memory_ = processor->memory();
|
memory_ = processor->memory();
|
||||||
target_loop_ = target_loop;
|
target_loop_ = target_loop;
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
#include "xenia/cpu/processor.h"
|
#include "xenia/cpu/processor.h"
|
||||||
#include "xenia/memory.h"
|
#include "xenia/memory.h"
|
||||||
#include "xenia/ui/loop.h"
|
#include "xenia/ui/loop.h"
|
||||||
#include "xenia/ui/platform.h"
|
#include "xenia/ui/window.h"
|
||||||
#include "xenia/xbox.h"
|
#include "xenia/xbox.h"
|
||||||
|
|
||||||
namespace xe {
|
namespace xe {
|
||||||
|
@ -32,13 +32,15 @@ class GraphicsSystem {
|
||||||
virtual ~GraphicsSystem();
|
virtual ~GraphicsSystem();
|
||||||
|
|
||||||
static std::unique_ptr<GraphicsSystem> Create(Emulator* emulator);
|
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_; }
|
Emulator* emulator() const { return emulator_; }
|
||||||
Memory* memory() const { return memory_; }
|
Memory* memory() const { return memory_; }
|
||||||
cpu::Processor* processor() const { return processor_; }
|
cpu::Processor* processor() const { return processor_; }
|
||||||
|
|
||||||
virtual X_STATUS Setup(cpu::Processor* processor, ui::Loop* target_loop,
|
virtual X_STATUS Setup(cpu::Processor* processor, ui::Loop* target_loop,
|
||||||
ui::PlatformWindow* target_window);
|
ui::Window* target_window);
|
||||||
virtual void Shutdown();
|
virtual void Shutdown();
|
||||||
|
|
||||||
void SetInterruptCallback(uint32_t callback, uint32_t user_data);
|
void SetInterruptCallback(uint32_t callback, uint32_t user_data);
|
||||||
|
@ -46,8 +48,6 @@ class GraphicsSystem {
|
||||||
virtual void EnableReadPointerWriteBack(uint32_t ptr,
|
virtual void EnableReadPointerWriteBack(uint32_t ptr,
|
||||||
uint32_t block_size) = 0;
|
uint32_t block_size) = 0;
|
||||||
|
|
||||||
virtual void RequestSwap() = 0;
|
|
||||||
|
|
||||||
void DispatchInterruptCallback(uint32_t source, uint32_t cpu);
|
void DispatchInterruptCallback(uint32_t source, uint32_t cpu);
|
||||||
|
|
||||||
virtual void RequestFrameTrace() {}
|
virtual void RequestFrameTrace() {}
|
||||||
|
@ -68,7 +68,7 @@ class GraphicsSystem {
|
||||||
Memory* memory_ = nullptr;
|
Memory* memory_ = nullptr;
|
||||||
cpu::Processor* processor_ = nullptr;
|
cpu::Processor* processor_ = nullptr;
|
||||||
ui::Loop* target_loop_ = 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_ = 0;
|
||||||
uint32_t interrupt_callback_data_ = 0;
|
uint32_t interrupt_callback_data_ = 0;
|
||||||
|
|
|
@ -24,6 +24,7 @@
|
||||||
#include "xenia/gpu/xenos.h"
|
#include "xenia/gpu/xenos.h"
|
||||||
#include "xenia/profiling.h"
|
#include "xenia/profiling.h"
|
||||||
#include "xenia/ui/gl/gl_context.h"
|
#include "xenia/ui/gl/gl_context.h"
|
||||||
|
#include "xenia/ui/window.h"
|
||||||
|
|
||||||
// HACK: until we have another impl, we just use gl4 directly.
|
// HACK: until we have another impl, we just use gl4 directly.
|
||||||
#include "xenia/gpu/gl4/command_processor.h"
|
#include "xenia/gpu/gl4/command_processor.h"
|
||||||
|
@ -842,7 +843,7 @@ class TracePlayer : public TraceReader {
|
||||||
int current_command_index_;
|
int current_command_index_;
|
||||||
};
|
};
|
||||||
|
|
||||||
void DrawControllerUI(xe::ui::PlatformWindow* window, TracePlayer& player,
|
void DrawControllerUI(xe::ui::Window* window, TracePlayer& player,
|
||||||
Memory* memory) {
|
Memory* memory) {
|
||||||
ImGui::SetNextWindowPos(ImVec2(5, 5), ImGuiSetCond_FirstUseEver);
|
ImGui::SetNextWindowPos(ImVec2(5, 5), ImGuiSetCond_FirstUseEver);
|
||||||
if (!ImGui::Begin("Controller", nullptr, ImVec2(340, 60))) {
|
if (!ImGui::Begin("Controller", nullptr, ImVec2(340, 60))) {
|
||||||
|
@ -883,7 +884,7 @@ void DrawControllerUI(xe::ui::PlatformWindow* window, TracePlayer& player,
|
||||||
ImGui::End();
|
ImGui::End();
|
||||||
}
|
}
|
||||||
|
|
||||||
void DrawCommandListUI(xe::ui::PlatformWindow* window, TracePlayer& player,
|
void DrawCommandListUI(xe::ui::Window* window, TracePlayer& player,
|
||||||
Memory* memory) {
|
Memory* memory) {
|
||||||
ImGui::SetNextWindowPos(ImVec2(5, 70), ImGuiSetCond_FirstUseEver);
|
ImGui::SetNextWindowPos(ImVec2(5, 70), ImGuiSetCond_FirstUseEver);
|
||||||
if (!ImGui::Begin("Command List", nullptr, ImVec2(200, 640))) {
|
if (!ImGui::Begin("Command List", nullptr, ImVec2(200, 640))) {
|
||||||
|
@ -1027,9 +1028,8 @@ ShaderDisplayType DrawShaderTypeUI() {
|
||||||
return shader_display_type;
|
return shader_display_type;
|
||||||
}
|
}
|
||||||
|
|
||||||
void DrawShaderUI(xe::ui::PlatformWindow* window, TracePlayer& player,
|
void DrawShaderUI(xe::ui::Window* window, TracePlayer& player, Memory* memory,
|
||||||
Memory* memory, gl4::GL4Shader* shader,
|
gl4::GL4Shader* shader, ShaderDisplayType display_type) {
|
||||||
ShaderDisplayType display_type) {
|
|
||||||
// Must be prepared for advanced display modes.
|
// Must be prepared for advanced display modes.
|
||||||
if (display_type != ShaderDisplayType::kUcode) {
|
if (display_type != ShaderDisplayType::kUcode) {
|
||||||
if (!shader->has_prepared()) {
|
if (!shader->has_prepared()) {
|
||||||
|
@ -1393,8 +1393,7 @@ static const char* kEndiannessNames[] = {
|
||||||
"unspecified endianness", "8-in-16", "8-in-32", "16-in-32",
|
"unspecified endianness", "8-in-16", "8-in-32", "16-in-32",
|
||||||
};
|
};
|
||||||
|
|
||||||
void DrawStateUI(xe::ui::PlatformWindow* window, TracePlayer& player,
|
void DrawStateUI(xe::ui::Window* window, TracePlayer& player, Memory* memory) {
|
||||||
Memory* memory) {
|
|
||||||
auto gs = static_cast<gl4::GL4GraphicsSystem*>(player.graphics_system());
|
auto gs = static_cast<gl4::GL4GraphicsSystem*>(player.graphics_system());
|
||||||
auto cp = gs->command_processor();
|
auto cp = gs->command_processor();
|
||||||
auto& regs = *gs->register_file();
|
auto& regs = *gs->register_file();
|
||||||
|
@ -2033,8 +2032,8 @@ void DrawStateUI(xe::ui::PlatformWindow* window, TracePlayer& player,
|
||||||
ImGui::End();
|
ImGui::End();
|
||||||
}
|
}
|
||||||
|
|
||||||
void DrawPacketDisassemblerUI(xe::ui::PlatformWindow* window,
|
void DrawPacketDisassemblerUI(xe::ui::Window* window, TracePlayer& player,
|
||||||
TracePlayer& player, Memory* memory) {
|
Memory* memory) {
|
||||||
ImGui::SetNextWindowCollapsed(true, ImGuiSetCond_FirstUseEver);
|
ImGui::SetNextWindowCollapsed(true, ImGuiSetCond_FirstUseEver);
|
||||||
ImGui::SetNextWindowPos(ImVec2(float(window->width()) - 500 - 5, 5),
|
ImGui::SetNextWindowPos(ImVec2(float(window->width()) - 500 - 5, 5),
|
||||||
ImGuiSetCond_FirstUseEver);
|
ImGuiSetCond_FirstUseEver);
|
||||||
|
@ -2175,8 +2174,7 @@ void DrawPacketDisassemblerUI(xe::ui::PlatformWindow* window,
|
||||||
ImGui::End();
|
ImGui::End();
|
||||||
}
|
}
|
||||||
|
|
||||||
void DrawUI(xe::ui::PlatformWindow* window, TracePlayer& player,
|
void DrawUI(xe::ui::Window* window, TracePlayer& player, Memory* memory) {
|
||||||
Memory* memory) {
|
|
||||||
// ImGui::ShowTestWindow();
|
// ImGui::ShowTestWindow();
|
||||||
|
|
||||||
DrawControllerUI(window, player, memory);
|
DrawControllerUI(window, player, memory);
|
||||||
|
@ -2189,16 +2187,39 @@ void ImImpl_Setup();
|
||||||
void ImImpl_Shutdown();
|
void ImImpl_Shutdown();
|
||||||
|
|
||||||
int trace_viewer_main(std::vector<std::wstring>& args) {
|
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"");
|
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)) {
|
if (XFAILED(result)) {
|
||||||
XELOGE("Failed to setup emulator: %.8X", result);
|
XELOGE("Failed to setup emulator: %.8X", result);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Grab path from the flag or unnamed argument.
|
// Grab path from the flag or unnamed argument.
|
||||||
if (!FLAGS_target_trace_file.empty() || args.size() >= 2) {
|
if (FLAGS_target_trace_file.empty() && args.size() < 2) {
|
||||||
|
XELOGE("No trace file specified");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
std::wstring path;
|
std::wstring path;
|
||||||
if (!FLAGS_target_trace_file.empty()) {
|
if (!FLAGS_target_trace_file.empty()) {
|
||||||
// Passed as a named argument.
|
// Passed as a named argument.
|
||||||
|
@ -2212,22 +2233,19 @@ int trace_viewer_main(std::vector<std::wstring>& args) {
|
||||||
// Normalize the path and make absolute.
|
// Normalize the path and make absolute.
|
||||||
auto abs_path = xe::to_absolute_path(path);
|
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);
|
auto file_name = xe::find_name_from_path(path);
|
||||||
window->set_title(std::wstring(L"Xenia GPU Trace Viewer: ") + file_name);
|
window->set_title(std::wstring(L"Xenia GPU Trace Viewer: ") + file_name);
|
||||||
|
|
||||||
auto graphics_system = emulator->graphics_system();
|
auto graphics_system = emulator->graphics_system();
|
||||||
Profiler::set_display(nullptr);
|
Profiler::set_display(nullptr);
|
||||||
|
|
||||||
TracePlayer player(loop, emulator->graphics_system());
|
TracePlayer player(loop.get(), emulator->graphics_system());
|
||||||
if (!player.Open(abs_path)) {
|
if (!player.Open(abs_path)) {
|
||||||
XELOGE("Could not load trace file");
|
XELOGE("Could not load trace file");
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto control = window->child(0);
|
window->on_key_char.AddListener([graphics_system](xe::ui::KeyEvent& e) {
|
||||||
control->on_key_char.AddListener([graphics_system](xe::ui::KeyEvent& e) {
|
|
||||||
auto& io = ImGui::GetIO();
|
auto& io = ImGui::GetIO();
|
||||||
if (e.key_code() > 0 && e.key_code() < 0x10000) {
|
if (e.key_code() > 0 && e.key_code() < 0x10000) {
|
||||||
if (e.key_code() == 0x74 /* VK_F5 */) {
|
if (e.key_code() == 0x74 /* VK_F5 */) {
|
||||||
|
@ -2238,7 +2256,7 @@ int trace_viewer_main(std::vector<std::wstring>& args) {
|
||||||
}
|
}
|
||||||
e.set_handled(true);
|
e.set_handled(true);
|
||||||
});
|
});
|
||||||
control->on_mouse_down.AddListener([](xe::ui::MouseEvent& e) {
|
window->on_mouse_down.AddListener([](xe::ui::MouseEvent& e) {
|
||||||
auto& io = ImGui::GetIO();
|
auto& io = ImGui::GetIO();
|
||||||
io.MousePos = ImVec2(float(e.x()), float(e.y()));
|
io.MousePos = ImVec2(float(e.x()), float(e.y()));
|
||||||
switch (e.button()) {
|
switch (e.button()) {
|
||||||
|
@ -2250,11 +2268,11 @@ int trace_viewer_main(std::vector<std::wstring>& args) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
control->on_mouse_move.AddListener([](xe::ui::MouseEvent& e) {
|
window->on_mouse_move.AddListener([](xe::ui::MouseEvent& e) {
|
||||||
auto& io = ImGui::GetIO();
|
auto& io = ImGui::GetIO();
|
||||||
io.MousePos = ImVec2(float(e.x()), float(e.y()));
|
io.MousePos = ImVec2(float(e.x()), float(e.y()));
|
||||||
});
|
});
|
||||||
control->on_mouse_up.AddListener([](xe::ui::MouseEvent& e) {
|
window->on_mouse_up.AddListener([](xe::ui::MouseEvent& e) {
|
||||||
auto& io = ImGui::GetIO();
|
auto& io = ImGui::GetIO();
|
||||||
io.MousePos = ImVec2(float(e.x()), float(e.y()));
|
io.MousePos = ImVec2(float(e.x()), float(e.y()));
|
||||||
switch (e.button()) {
|
switch (e.button()) {
|
||||||
|
@ -2266,13 +2284,13 @@ int trace_viewer_main(std::vector<std::wstring>& args) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
control->on_mouse_wheel.AddListener([](xe::ui::MouseEvent& e) {
|
window->on_mouse_wheel.AddListener([](xe::ui::MouseEvent& e) {
|
||||||
auto& io = ImGui::GetIO();
|
auto& io = ImGui::GetIO();
|
||||||
io.MousePos = ImVec2(float(e.x()), float(e.y()));
|
io.MousePos = ImVec2(float(e.x()), float(e.y()));
|
||||||
io.MouseWheel += float(e.dy() / 120.0f);
|
io.MouseWheel += float(e.dy() / 120.0f);
|
||||||
});
|
});
|
||||||
|
|
||||||
control->on_paint.AddListener([&](xe::ui::UIEvent& e) {
|
window->on_painting.AddListener([&](xe::ui::UIEvent& e) {
|
||||||
static bool imgui_setup = false;
|
static bool imgui_setup = false;
|
||||||
if (!imgui_setup) {
|
if (!imgui_setup) {
|
||||||
ImImpl_Setup();
|
ImImpl_Setup();
|
||||||
|
@ -2286,7 +2304,7 @@ int trace_viewer_main(std::vector<std::wstring>& args) {
|
||||||
last_ticks = current_ticks;
|
last_ticks = current_ticks;
|
||||||
|
|
||||||
io.DisplaySize =
|
io.DisplaySize =
|
||||||
ImVec2(float(e.control()->width()), float(e.control()->height()));
|
ImVec2(float(e.target()->width()), float(e.target()->height()));
|
||||||
|
|
||||||
BYTE keystate[256];
|
BYTE keystate[256];
|
||||||
GetKeyboardState(keystate);
|
GetKeyboardState(keystate);
|
||||||
|
@ -2296,22 +2314,24 @@ int trace_viewer_main(std::vector<std::wstring>& args) {
|
||||||
|
|
||||||
ImGui::NewFrame();
|
ImGui::NewFrame();
|
||||||
|
|
||||||
DrawUI(window, player, emulator->memory());
|
DrawUI(window.get(), player, emulator->memory());
|
||||||
|
|
||||||
glViewport(0, 0, (int)io.DisplaySize.x, (int)io.DisplaySize.y);
|
glViewport(0, 0, (int)io.DisplaySize.x, (int)io.DisplaySize.y);
|
||||||
ImGui::Render();
|
ImGui::Render();
|
||||||
|
|
||||||
graphics_system->RequestSwap();
|
// Continuous paint.
|
||||||
|
window->Invalidate();
|
||||||
});
|
});
|
||||||
graphics_system->RequestSwap();
|
window->Invalidate();
|
||||||
|
|
||||||
// Wait until we are exited.
|
// Wait until we are exited.
|
||||||
emulator->display_window()->loop()->AwaitQuit();
|
loop->AwaitQuit();
|
||||||
|
|
||||||
ImImpl_Shutdown();
|
ImImpl_Shutdown();
|
||||||
}
|
|
||||||
|
|
||||||
emulator.reset();
|
emulator.reset();
|
||||||
|
window.reset();
|
||||||
|
loop.reset();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -67,7 +67,8 @@ SHIM_CALL XamShowMessageBoxUI_shim(PPCContext* ppc_context,
|
||||||
TASKDIALOGCONFIG config = {0};
|
TASKDIALOGCONFIG config = {0};
|
||||||
config.cbSize = sizeof(config);
|
config.cbSize = sizeof(config);
|
||||||
config.hInstance = GetModuleHandle(nullptr);
|
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
|
config.dwFlags = TDF_ALLOW_DIALOG_CANCELLATION | // esc to exit
|
||||||
TDF_POSITION_RELATIVE_TO_WINDOW; // center in window
|
TDF_POSITION_RELATIVE_TO_WINDOW; // center in window
|
||||||
config.dwCommonButtons = 0;
|
config.dwCommonButtons = 0;
|
||||||
|
@ -120,7 +121,7 @@ SHIM_CALL XamShowDirtyDiscErrorUI_shim(PPCContext* ppc_context,
|
||||||
XELOGD("XamShowDirtyDiscErrorUI(%d)", user_index);
|
XELOGD("XamShowDirtyDiscErrorUI(%d)", user_index);
|
||||||
|
|
||||||
int button_pressed = 0;
|
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",
|
GetModuleHandle(nullptr), L"Disc Read Error",
|
||||||
L"Game is claiming to be unable to read game data!", nullptr,
|
L"Game is claiming to be unable to read game data!", nullptr,
|
||||||
TDCBF_CLOSE_BUTTON, TD_ERROR_ICON, &button_pressed);
|
TDCBF_CLOSE_BUTTON, TD_ERROR_ICON, &button_pressed);
|
||||||
|
|
|
@ -33,6 +33,10 @@ std::unique_ptr<ProfilerDisplay> Profiler::display_ = nullptr;
|
||||||
|
|
||||||
#if XE_OPTION_PROFILING
|
#if XE_OPTION_PROFILING
|
||||||
|
|
||||||
|
bool Profiler::is_enabled() { return true; }
|
||||||
|
|
||||||
|
bool Profiler::is_visible() { return MicroProfileIsDrawing(); }
|
||||||
|
|
||||||
void Profiler::Initialize() {
|
void Profiler::Initialize() {
|
||||||
// Custom groups.
|
// Custom groups.
|
||||||
MicroProfileSetEnableAllGroups(false);
|
MicroProfileSetEnableAllGroups(false);
|
||||||
|
@ -168,6 +172,8 @@ void Profiler::Present() {
|
||||||
|
|
||||||
#else
|
#else
|
||||||
|
|
||||||
|
bool Profiler::is_enabled() { return false; }
|
||||||
|
bool Profiler::is_visible() { return false; }
|
||||||
void Profiler::Initialize() {}
|
void Profiler::Initialize() {}
|
||||||
void Profiler::Dump() {}
|
void Profiler::Dump() {}
|
||||||
void Profiler::Shutdown() {}
|
void Profiler::Shutdown() {}
|
||||||
|
|
|
@ -148,11 +148,8 @@ class ProfilerDisplay {
|
||||||
|
|
||||||
class Profiler {
|
class Profiler {
|
||||||
public:
|
public:
|
||||||
#if XE_OPTION_PROFILING
|
static bool is_enabled();
|
||||||
static bool is_enabled() { return true; }
|
static bool is_visible();
|
||||||
#else
|
|
||||||
static bool is_enabled() { return false; }
|
|
||||||
#endif // XE_OPTION_PROFILING
|
|
||||||
|
|
||||||
// Initializes the profiler. Call at startup.
|
// Initializes the profiler. Call at startup.
|
||||||
static void Initialize();
|
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_
|
#ifndef XENIA_UI_FILE_PICKER_H_
|
||||||
#define XENIA_UI_FILE_PICKER_H_
|
#define XENIA_UI_FILE_PICKER_H_
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
@ -27,6 +28,8 @@ class FilePicker {
|
||||||
kDirectory = 1,
|
kDirectory = 1,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static std::unique_ptr<FilePicker> Create();
|
||||||
|
|
||||||
FilePicker()
|
FilePicker()
|
||||||
: mode_(Mode::kOpen),
|
: mode_(Mode::kOpen),
|
||||||
type_(Type::kFile),
|
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/platform.h"
|
||||||
#include "xenia/base/assert.h"
|
#include "xenia/base/assert.h"
|
||||||
|
|
||||||
namespace xe {
|
namespace xe {
|
||||||
namespace ui {
|
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,
|
class CDialogEventHandler : public IFileDialogEvents,
|
||||||
public IFileDialogControlEvents {
|
public IFileDialogControlEvents {
|
||||||
|
@ -201,6 +214,5 @@ bool Win32FilePicker::Show(void* parent_window_handle) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace win32
|
|
||||||
} // namespace ui
|
} // namespace ui
|
||||||
} // namespace xe
|
} // namespace xe
|
|
@ -11,7 +11,6 @@
|
||||||
|
|
||||||
#include "xenia/base/assert.h"
|
#include "xenia/base/assert.h"
|
||||||
#include "xenia/base/math.h"
|
#include "xenia/base/math.h"
|
||||||
#include "xenia/gpu/gl4/gl4_gpu_flags.h"
|
|
||||||
|
|
||||||
namespace xe {
|
namespace xe {
|
||||||
namespace ui {
|
namespace ui {
|
||||||
|
|
|
@ -28,7 +28,7 @@ GL4ElementalRenderer::GL4Bitmap::GL4Bitmap(GLContext* context,
|
||||||
: context_(context), renderer_(renderer) {}
|
: context_(context), renderer_(renderer) {}
|
||||||
|
|
||||||
GL4ElementalRenderer::GL4Bitmap::~GL4Bitmap() {
|
GL4ElementalRenderer::GL4Bitmap::~GL4Bitmap() {
|
||||||
GLContextLock lock(context_);
|
GraphicsContextLock lock(context_);
|
||||||
|
|
||||||
// Must flush and unbind before we delete the texture.
|
// Must flush and unbind before we delete the texture.
|
||||||
renderer_->FlushBitmap(this);
|
renderer_->FlushBitmap(this);
|
||||||
|
@ -69,7 +69,7 @@ GL4ElementalRenderer::GL4ElementalRenderer(GLContext* context)
|
||||||
vertex_buffer_(max_vertex_batch_size() * sizeof(Vertex)) {}
|
vertex_buffer_(max_vertex_batch_size() * sizeof(Vertex)) {}
|
||||||
|
|
||||||
GL4ElementalRenderer::~GL4ElementalRenderer() {
|
GL4ElementalRenderer::~GL4ElementalRenderer() {
|
||||||
GLContextLock lock(context_);
|
GraphicsContextLock lock(context_);
|
||||||
vertex_buffer_.Shutdown();
|
vertex_buffer_.Shutdown();
|
||||||
glDeleteVertexArrays(1, &vao_);
|
glDeleteVertexArrays(1, &vao_);
|
||||||
glDeleteProgram(program_);
|
glDeleteProgram(program_);
|
||||||
|
@ -77,6 +77,7 @@ GL4ElementalRenderer::~GL4ElementalRenderer() {
|
||||||
|
|
||||||
std::unique_ptr<GL4ElementalRenderer> GL4ElementalRenderer::Create(
|
std::unique_ptr<GL4ElementalRenderer> GL4ElementalRenderer::Create(
|
||||||
GLContext* context) {
|
GLContext* context) {
|
||||||
|
GraphicsContextLock lock(context);
|
||||||
auto renderer = std::make_unique<GL4ElementalRenderer>(context);
|
auto renderer = std::make_unique<GL4ElementalRenderer>(context);
|
||||||
if (!renderer->Initialize()) {
|
if (!renderer->Initialize()) {
|
||||||
XELOGE("Failed to initialize TurboBadger GL4 renderer");
|
XELOGE("Failed to initialize TurboBadger GL4 renderer");
|
||||||
|
|
|
@ -16,6 +16,9 @@
|
||||||
#include "xenia/base/logging.h"
|
#include "xenia/base/logging.h"
|
||||||
#include "xenia/base/math.h"
|
#include "xenia/base/math.h"
|
||||||
#include "xenia/profiling.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,
|
DEFINE_bool(thread_safe_gl, false,
|
||||||
"Only allow one GL context to be active at a time.");
|
"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" GLEWContext* glewGetContext() { return tls_glew_context_; }
|
||||||
extern "C" WGLEWContext* wglewGetContext() { return tls_wglew_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)
|
GLContext::GLContext(Window* target_window) : GraphicsContext(target_window) {}
|
||||||
: hwnd_(hwnd), dc_(nullptr), glrc_(glrc) {
|
|
||||||
dc_ = GetDC(hwnd);
|
GLContext::GLContext(Window* target_window, HGLRC glrc)
|
||||||
|
: GraphicsContext(target_window), glrc_(glrc) {
|
||||||
|
dc_ = GetDC(HWND(target_window_->native_handle()));
|
||||||
}
|
}
|
||||||
|
|
||||||
GLContext::~GLContext() {
|
GLContext::~GLContext() {
|
||||||
|
@ -55,13 +67,13 @@ GLContext::~GLContext() {
|
||||||
wglDeleteContext(glrc_);
|
wglDeleteContext(glrc_);
|
||||||
}
|
}
|
||||||
if (dc_) {
|
if (dc_) {
|
||||||
ReleaseDC(hwnd_, dc_);
|
ReleaseDC(HWND(target_window_->native_handle()), dc_);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GLContext::Initialize(HWND hwnd) {
|
bool GLContext::Initialize(Window* target_window) {
|
||||||
hwnd_ = hwnd;
|
target_window_ = target_window;
|
||||||
dc_ = GetDC(hwnd);
|
dc_ = GetDC(HWND(target_window_->native_handle()));
|
||||||
|
|
||||||
PIXELFORMATDESCRIPTOR pfd = {0};
|
PIXELFORMATDESCRIPTOR pfd = {0};
|
||||||
pfd.nSize = sizeof(pfd);
|
pfd.nSize = sizeof(pfd);
|
||||||
|
@ -152,12 +164,12 @@ bool GLContext::Initialize(HWND hwnd) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<GLContext> GLContext::CreateShared() {
|
std::unique_ptr<GraphicsContext> GLContext::CreateShared() {
|
||||||
assert_not_null(glrc_);
|
assert_not_null(glrc_);
|
||||||
|
|
||||||
HGLRC new_glrc = nullptr;
|
HGLRC new_glrc = nullptr;
|
||||||
{
|
{
|
||||||
GLContextLock context_lock(this);
|
GraphicsContextLock context_lock(this);
|
||||||
|
|
||||||
int context_flags = 0;
|
int context_flags = 0;
|
||||||
// int profile = WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB;
|
// 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()) {
|
if (!new_context->MakeCurrent()) {
|
||||||
XELOGE("Could not make new GL context current");
|
XELOGE("Could not make new GL context current");
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
@ -205,7 +218,7 @@ std::unique_ptr<GLContext> GLContext::CreateShared() {
|
||||||
|
|
||||||
new_context->ClearCurrent();
|
new_context->ClearCurrent();
|
||||||
|
|
||||||
return new_context;
|
return std::unique_ptr<GraphicsContext>(new_context.release());
|
||||||
}
|
}
|
||||||
|
|
||||||
void FatalGLError(std::string error) {
|
void FatalGLError(std::string error) {
|
||||||
|
@ -369,6 +382,14 @@ void GLContext::SetupDebugging() {
|
||||||
this);
|
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() {
|
bool GLContext::MakeCurrent() {
|
||||||
SCOPE_profile_cpu_f("gpu");
|
SCOPE_profile_cpu_f("gpu");
|
||||||
if (FLAGS_thread_safe_gl) {
|
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 gl
|
||||||
} // namespace ui
|
} // namespace ui
|
||||||
} // namespace xe
|
} // namespace xe
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
|
|
||||||
#include "xenia/ui/gl/blitter.h"
|
#include "xenia/ui/gl/blitter.h"
|
||||||
#include "xenia/ui/gl/gl.h"
|
#include "xenia/ui/gl/gl.h"
|
||||||
|
#include "xenia/ui/graphics_context.h"
|
||||||
|
|
||||||
DECLARE_bool(thread_safe_gl);
|
DECLARE_bool(thread_safe_gl);
|
||||||
|
|
||||||
|
@ -23,25 +24,33 @@ namespace xe {
|
||||||
namespace ui {
|
namespace ui {
|
||||||
namespace gl {
|
namespace gl {
|
||||||
|
|
||||||
class GLContext {
|
class GLContext : public GraphicsContext {
|
||||||
public:
|
public:
|
||||||
GLContext();
|
static std::unique_ptr<GLContext> Create(Window* target_window);
|
||||||
GLContext(HWND hwnd, HGLRC glrc);
|
|
||||||
~GLContext();
|
|
||||||
|
|
||||||
bool Initialize(HWND hwnd);
|
~GLContext() override;
|
||||||
void AssertExtensionsPresent();
|
|
||||||
|
|
||||||
HDC dc() const { return dc_; }
|
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();
|
bool MakeCurrent() override;
|
||||||
void ClearCurrent();
|
void ClearCurrent() override;
|
||||||
|
|
||||||
|
void BeginSwap() override;
|
||||||
|
void EndSwap() override;
|
||||||
|
|
||||||
Blitter* blitter() { return &blitter_; }
|
Blitter* blitter() { return &blitter_; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
GLContext(Window* target_window);
|
||||||
|
GLContext(Window* target_window, HGLRC glrc);
|
||||||
|
|
||||||
|
bool Initialize(Window* target_window);
|
||||||
|
void AssertExtensionsPresent();
|
||||||
|
|
||||||
void SetupDebugging();
|
void SetupDebugging();
|
||||||
void DebugMessage(GLenum source, GLenum type, GLuint id, GLenum severity,
|
void DebugMessage(GLenum source, GLenum type, GLuint id, GLenum severity,
|
||||||
GLsizei length, const GLchar* message);
|
GLsizei length, const GLchar* message);
|
||||||
|
@ -49,9 +58,8 @@ class GLContext {
|
||||||
DebugMessageThunk(GLenum source, GLenum type, GLuint id, GLenum severity,
|
DebugMessageThunk(GLenum source, GLenum type, GLuint id, GLenum severity,
|
||||||
GLsizei length, const GLchar* message, GLvoid* user_param);
|
GLsizei length, const GLchar* message, GLvoid* user_param);
|
||||||
|
|
||||||
HWND hwnd_;
|
HDC dc_ = nullptr;
|
||||||
HDC dc_;
|
HGLRC glrc_ = nullptr;
|
||||||
HGLRC glrc_;
|
|
||||||
|
|
||||||
GLEWContext glew_context_;
|
GLEWContext glew_context_;
|
||||||
WGLEWContext wglew_context_;
|
WGLEWContext wglew_context_;
|
||||||
|
@ -59,16 +67,6 @@ class GLContext {
|
||||||
Blitter blitter_;
|
Blitter blitter_;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct GLContextLock {
|
|
||||||
GLContextLock(GLContext* context) : context_(context) {
|
|
||||||
context_->MakeCurrent();
|
|
||||||
}
|
|
||||||
~GLContextLock() { context_->ClearCurrent(); }
|
|
||||||
|
|
||||||
private:
|
|
||||||
GLContext* context_;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace gl
|
} // namespace gl
|
||||||
} // namespace ui
|
} // namespace ui
|
||||||
} // namespace xe
|
} // namespace xe
|
||||||
|
|
|
@ -132,47 +132,56 @@ const uint8_t profiler_font[] = {
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
};
|
};
|
||||||
|
|
||||||
GLProfilerDisplay::GLProfilerDisplay(xe::ui::gl::WGLControl* control)
|
GLProfilerDisplay::GLProfilerDisplay(xe::ui::Window* window)
|
||||||
: control_(control),
|
: window_(window),
|
||||||
program_(0),
|
|
||||||
vao_(0),
|
|
||||||
font_texture_(0),
|
|
||||||
font_handle_(0),
|
|
||||||
vertex_buffer_(MICROPROFILE_MAX_VERTICES * sizeof(Vertex) * 10,
|
vertex_buffer_(MICROPROFILE_MAX_VERTICES * sizeof(Vertex) * 10,
|
||||||
sizeof(Vertex)),
|
sizeof(Vertex)) {
|
||||||
draw_command_count_(0) {
|
|
||||||
if (!SetupFont() || !SetupState() || !SetupShaders()) {
|
if (!SetupFont() || !SetupState() || !SetupShaders()) {
|
||||||
// Hrm.
|
// Hrm.
|
||||||
assert_always();
|
assert_always();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
window_->on_painted.AddListener([this](UIEvent& e) { Profiler::Present(); });
|
||||||
|
|
||||||
// Pass through mouse events.
|
// Pass through mouse events.
|
||||||
control->on_mouse_down.AddListener([](xe::ui::MouseEvent& e) {
|
window_->on_mouse_down.AddListener([](xe::ui::MouseEvent& e) {
|
||||||
|
if (Profiler::is_enabled()) {
|
||||||
Profiler::OnMouseDown(e.button() == xe::ui::MouseEvent::Button::kLeft,
|
Profiler::OnMouseDown(e.button() == xe::ui::MouseEvent::Button::kLeft,
|
||||||
e.button() == xe::ui::MouseEvent::Button::kRight);
|
e.button() == xe::ui::MouseEvent::Button::kRight);
|
||||||
e.set_handled(true);
|
e.set_handled(true);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
control->on_mouse_up.AddListener([](xe::ui::MouseEvent& e) {
|
window_->on_mouse_up.AddListener([](xe::ui::MouseEvent& e) {
|
||||||
|
if (Profiler::is_enabled()) {
|
||||||
Profiler::OnMouseUp();
|
Profiler::OnMouseUp();
|
||||||
e.set_handled(true);
|
e.set_handled(true);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
control->on_mouse_move.AddListener([](xe::ui::MouseEvent& e) {
|
window_->on_mouse_move.AddListener([](xe::ui::MouseEvent& e) {
|
||||||
|
if (Profiler::is_enabled()) {
|
||||||
Profiler::OnMouseMove(e.x(), e.y());
|
Profiler::OnMouseMove(e.x(), e.y());
|
||||||
e.set_handled(true);
|
e.set_handled(true);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
control->on_mouse_wheel.AddListener([](xe::ui::MouseEvent& e) {
|
window_->on_mouse_wheel.AddListener([](xe::ui::MouseEvent& e) {
|
||||||
|
if (Profiler::is_enabled()) {
|
||||||
Profiler::OnMouseWheel(e.x(), e.y(), -e.dy());
|
Profiler::OnMouseWheel(e.x(), e.y(), -e.dy());
|
||||||
e.set_handled(true);
|
e.set_handled(true);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Watch for toggle/mode keys and such.
|
// Watch for toggle/mode keys and such.
|
||||||
control->on_key_down.AddListener([](xe::ui::KeyEvent& e) {
|
window_->on_key_down.AddListener([](xe::ui::KeyEvent& e) {
|
||||||
|
if (Profiler::is_enabled()) {
|
||||||
Profiler::OnKeyDown(e.key_code());
|
Profiler::OnKeyDown(e.key_code());
|
||||||
// e.set_handled(true);
|
e.set_handled(true);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
control->on_key_up.AddListener([](xe::ui::KeyEvent& e) {
|
window_->on_key_up.AddListener([](xe::ui::KeyEvent& e) {
|
||||||
|
if (Profiler::is_enabled()) {
|
||||||
Profiler::OnKeyUp(e.key_code());
|
Profiler::OnKeyUp(e.key_code());
|
||||||
// e.set_handled(true);
|
e.set_handled(true);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -336,9 +345,9 @@ GLProfilerDisplay::~GLProfilerDisplay() {
|
||||||
glDeleteProgram(program_);
|
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() {
|
void GLProfilerDisplay::Begin() {
|
||||||
glEnablei(GL_BLEND, 0);
|
glEnablei(GL_BLEND, 0);
|
||||||
|
@ -347,7 +356,7 @@ void GLProfilerDisplay::Begin() {
|
||||||
glDisable(GL_STENCIL_TEST);
|
glDisable(GL_STENCIL_TEST);
|
||||||
glDisable(GL_SCISSOR_TEST);
|
glDisable(GL_SCISSOR_TEST);
|
||||||
|
|
||||||
glViewport(0, 0, control_->width(), control_->height());
|
glViewport(0, 0, width(), height());
|
||||||
|
|
||||||
float left = 0.0f;
|
float left = 0.0f;
|
||||||
float right = float(width());
|
float right = float(width());
|
||||||
|
|
|
@ -12,8 +12,7 @@
|
||||||
|
|
||||||
#include "xenia/profiling.h"
|
#include "xenia/profiling.h"
|
||||||
#include "xenia/ui/gl/circular_buffer.h"
|
#include "xenia/ui/gl/circular_buffer.h"
|
||||||
#include "xenia/ui/gl/gl_context.h"
|
#include "xenia/ui/window.h"
|
||||||
#include "xenia/ui/gl/wgl_control.h"
|
|
||||||
|
|
||||||
namespace xe {
|
namespace xe {
|
||||||
namespace ui {
|
namespace ui {
|
||||||
|
@ -21,7 +20,7 @@ namespace gl {
|
||||||
|
|
||||||
class GLProfilerDisplay : public ProfilerDisplay {
|
class GLProfilerDisplay : public ProfilerDisplay {
|
||||||
public:
|
public:
|
||||||
GLProfilerDisplay(xe::ui::gl::WGLControl* control);
|
GLProfilerDisplay(xe::ui::Window* window);
|
||||||
virtual ~GLProfilerDisplay();
|
virtual ~GLProfilerDisplay();
|
||||||
|
|
||||||
uint32_t width() const override;
|
uint32_t width() const override;
|
||||||
|
@ -54,11 +53,11 @@ class GLProfilerDisplay : public ProfilerDisplay {
|
||||||
void EndVertices(GLenum prim_type);
|
void EndVertices(GLenum prim_type);
|
||||||
void Flush();
|
void Flush();
|
||||||
|
|
||||||
xe::ui::gl::WGLControl* control_;
|
xe::ui::Window* window_ = nullptr;
|
||||||
GLuint program_;
|
GLuint program_ = 0;
|
||||||
GLuint vao_;
|
GLuint vao_ = 0;
|
||||||
GLuint font_texture_;
|
GLuint font_texture_ = 0;
|
||||||
GLuint64 font_handle_;
|
GLuint64 font_handle_ = 0;
|
||||||
CircularBuffer vertex_buffer_;
|
CircularBuffer vertex_buffer_;
|
||||||
|
|
||||||
static const size_t kMaxCommands = 32;
|
static const size_t kMaxCommands = 32;
|
||||||
|
@ -67,13 +66,13 @@ class GLProfilerDisplay : public ProfilerDisplay {
|
||||||
size_t vertex_offset;
|
size_t vertex_offset;
|
||||||
size_t vertex_count;
|
size_t vertex_count;
|
||||||
} draw_commands_[kMaxCommands];
|
} draw_commands_[kMaxCommands];
|
||||||
uint32_t draw_command_count_;
|
uint32_t draw_command_count_ = 0;
|
||||||
|
|
||||||
CircularBuffer::Allocation current_allocation_;
|
CircularBuffer::Allocation current_allocation_;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
uint16_t char_offsets[256];
|
uint16_t char_offsets[256];
|
||||||
} font_description_;
|
} font_description_ = {0};
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace gl
|
} // 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_
|
#include "xenia/ui/graphics_context.h"
|
||||||
#define XENIA_UI_WIN32_WIN32_FILE_PICKER_H_
|
|
||||||
|
|
||||||
#include "xenia/ui/file_picker.h"
|
|
||||||
|
|
||||||
namespace xe {
|
namespace xe {
|
||||||
namespace ui {
|
namespace ui {
|
||||||
namespace win32 {
|
|
||||||
|
|
||||||
class Win32FilePicker : public FilePicker {
|
GraphicsContext::GraphicsContext(Window* target_window)
|
||||||
public:
|
: target_window_(target_window) {}
|
||||||
Win32FilePicker();
|
|
||||||
~Win32FilePicker() override;
|
|
||||||
|
|
||||||
bool Show(void* parent_window_handle) override;
|
GraphicsContext::~GraphicsContext() = default;
|
||||||
|
|
||||||
private:
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace win32
|
|
||||||
} // namespace ui
|
} // namespace ui
|
||||||
} // namespace xe
|
} // 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_
|
#define XENIA_UI_LOOP_H_
|
||||||
|
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
#include "xenia/base/delegate.h"
|
||||||
|
#include "xenia/ui/ui_event.h"
|
||||||
|
|
||||||
namespace xe {
|
namespace xe {
|
||||||
namespace ui {
|
namespace ui {
|
||||||
|
|
||||||
class Loop {
|
class Loop {
|
||||||
public:
|
public:
|
||||||
Loop() = default;
|
static std::unique_ptr<Loop> Create();
|
||||||
virtual ~Loop() = default;
|
|
||||||
|
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 Post(std::function<void()> fn) = 0;
|
||||||
virtual void PostDelayed(std::function<void()> fn, uint64_t delay_millis) = 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 Quit() = 0;
|
||||||
virtual void AwaitQuit() = 0;
|
virtual void AwaitQuit() = 0;
|
||||||
|
|
||||||
|
Delegate<UIEvent> on_quit;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace ui
|
} // namespace ui
|
||||||
|
|
|
@ -7,13 +7,12 @@
|
||||||
******************************************************************************
|
******************************************************************************
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "xenia/ui/win32/win32_loop.h"
|
#include "xenia/ui/loop_win.h"
|
||||||
|
|
||||||
#include "xenia/base/assert.h"
|
#include "xenia/base/assert.h"
|
||||||
|
|
||||||
namespace xe {
|
namespace xe {
|
||||||
namespace ui {
|
namespace ui {
|
||||||
namespace win32 {
|
|
||||||
|
|
||||||
const DWORD kWmWin32LoopPost = WM_APP + 0x100;
|
const DWORD kWmWin32LoopPost = WM_APP + 0x100;
|
||||||
const DWORD kWmWin32LoopQuit = WM_APP + 0x101;
|
const DWORD kWmWin32LoopQuit = WM_APP + 0x101;
|
||||||
|
@ -27,6 +26,8 @@ class PostedFn {
|
||||||
std::function<void()> fn_;
|
std::function<void()> fn_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
std::unique_ptr<Loop> Loop::Create() { return std::make_unique<Win32Loop>(); }
|
||||||
|
|
||||||
Win32Loop::Win32Loop() : thread_id_(0) {
|
Win32Loop::Win32Loop() : thread_id_(0) {
|
||||||
timer_queue_ = CreateTimerQueue();
|
timer_queue_ = CreateTimerQueue();
|
||||||
|
|
||||||
|
@ -81,6 +82,9 @@ void Win32Loop::ThreadMain() {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
UIEvent e(nullptr);
|
||||||
|
on_quit(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Win32Loop::Post(std::function<void()> fn) {
|
void Win32Loop::Post(std::function<void()> fn) {
|
||||||
|
@ -132,6 +136,5 @@ void Win32Loop::Quit() {
|
||||||
|
|
||||||
void Win32Loop::AwaitQuit() { quit_fence_.Wait(); }
|
void Win32Loop::AwaitQuit() { quit_fence_.Wait(); }
|
||||||
|
|
||||||
} // namespace win32
|
|
||||||
} // namespace ui
|
} // namespace ui
|
||||||
} // namespace xe
|
} // namespace xe
|
|
@ -7,8 +7,8 @@
|
||||||
******************************************************************************
|
******************************************************************************
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef XENIA_UI_WIN32_WIN32_LOOP_H_
|
#ifndef XENIA_UI_LOOP_WIN_H_
|
||||||
#define XENIA_UI_WIN32_WIN32_LOOP_H_
|
#define XENIA_UI_LOOP_WIN_H_
|
||||||
|
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <list>
|
#include <list>
|
||||||
|
@ -21,7 +21,6 @@
|
||||||
|
|
||||||
namespace xe {
|
namespace xe {
|
||||||
namespace ui {
|
namespace ui {
|
||||||
namespace win32 {
|
|
||||||
|
|
||||||
class Win32Loop : public Loop {
|
class Win32Loop : public Loop {
|
||||||
public:
|
public:
|
||||||
|
@ -55,8 +54,7 @@ class Win32Loop : public Loop {
|
||||||
std::list<PendingTimer*> pending_timers_;
|
std::list<PendingTimer*> pending_timers_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace win32
|
|
||||||
} // namespace ui
|
} // namespace ui
|
||||||
} // namespace xe
|
} // 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 xe {
|
||||||
namespace ui {
|
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,
|
MenuItem::MenuItem(Type type, const std::wstring& text,
|
||||||
const std::wstring& hotkey)
|
const std::wstring& hotkey, std::function<void()> callback)
|
||||||
: type_(type), parent_item_(nullptr), text_(text), hotkey_(hotkey) {}
|
: type_(type),
|
||||||
|
parent_item_(nullptr),
|
||||||
|
text_(text),
|
||||||
|
hotkey_(hotkey),
|
||||||
|
callback_(std::move(callback)) {}
|
||||||
|
|
||||||
MenuItem::~MenuItem() = default;
|
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 ui
|
||||||
} // namespace xe
|
} // namespace xe
|
||||||
|
|
|
@ -10,10 +10,10 @@
|
||||||
#ifndef XENIA_UI_MENU_ITEM_H_
|
#ifndef XENIA_UI_MENU_ITEM_H_
|
||||||
#define XENIA_UI_MENU_ITEM_H_
|
#define XENIA_UI_MENU_ITEM_H_
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "xenia/base/delegate.h"
|
|
||||||
#include "xenia/ui/ui_event.h"
|
#include "xenia/ui/ui_event.h"
|
||||||
|
|
||||||
namespace xe {
|
namespace xe {
|
||||||
|
@ -30,6 +30,14 @@ class MenuItem {
|
||||||
kString, // Menu is just a string
|
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();
|
virtual ~MenuItem();
|
||||||
|
|
||||||
MenuItem* parent_item() const { return parent_item_; }
|
MenuItem* parent_item() const { return parent_item_; }
|
||||||
|
@ -41,12 +49,11 @@ class MenuItem {
|
||||||
void AddChild(std::unique_ptr<MenuItem> child_item);
|
void AddChild(std::unique_ptr<MenuItem> child_item);
|
||||||
void AddChild(MenuItemPtr child_item);
|
void AddChild(MenuItemPtr child_item);
|
||||||
void RemoveChild(MenuItem* child_item);
|
void RemoveChild(MenuItem* child_item);
|
||||||
|
MenuItem* child(size_t index);
|
||||||
Delegate<UIEvent> on_selected;
|
|
||||||
|
|
||||||
protected:
|
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 OnChildAdded(MenuItem* child_item) {}
|
||||||
virtual void OnChildRemoved(MenuItem* child_item) {}
|
virtual void OnChildRemoved(MenuItem* child_item) {}
|
||||||
|
@ -58,6 +65,7 @@ class MenuItem {
|
||||||
std::vector<MenuItemPtr> children_;
|
std::vector<MenuItemPtr> children_;
|
||||||
std::wstring text_;
|
std::wstring text_;
|
||||||
std::wstring hotkey_;
|
std::wstring hotkey_;
|
||||||
|
std::function<void()> callback_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace ui
|
} // 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 xe {
|
||||||
namespace ui {
|
namespace ui {
|
||||||
|
|
||||||
class Control;
|
class Window;
|
||||||
|
|
||||||
class UIEvent {
|
class UIEvent {
|
||||||
public:
|
public:
|
||||||
UIEvent(Control* control = nullptr) : control_(control) {}
|
UIEvent(Window* target = nullptr) : target_(target) {}
|
||||||
virtual ~UIEvent() = default;
|
virtual ~UIEvent() = default;
|
||||||
|
|
||||||
Control* control() const { return control_; }
|
Window* target() const { return target_; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Control* control_;
|
Window* target_ = nullptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
class KeyEvent : public UIEvent {
|
class KeyEvent : public UIEvent {
|
||||||
public:
|
public:
|
||||||
KeyEvent(Control* control, int key_code)
|
KeyEvent(Window* target, int key_code)
|
||||||
: UIEvent(control), handled_(false), key_code_(key_code) {}
|
: UIEvent(target), key_code_(key_code) {}
|
||||||
~KeyEvent() override = default;
|
~KeyEvent() override = default;
|
||||||
|
|
||||||
bool is_handled() const { return handled_; }
|
bool is_handled() const { return handled_; }
|
||||||
|
@ -38,8 +38,8 @@ class KeyEvent : public UIEvent {
|
||||||
int key_code() const { return key_code_; }
|
int key_code() const { return key_code_; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool handled_;
|
bool handled_ = false;
|
||||||
int key_code_;
|
int key_code_ = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
class MouseEvent : public UIEvent {
|
class MouseEvent : public UIEvent {
|
||||||
|
@ -54,15 +54,9 @@ class MouseEvent : public UIEvent {
|
||||||
};
|
};
|
||||||
|
|
||||||
public:
|
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)
|
int32_t dx = 0, int32_t dy = 0)
|
||||||
: UIEvent(control),
|
: UIEvent(target), button_(button), x_(x), y_(y), dx_(dx), dy_(dy) {}
|
||||||
handled_(false),
|
|
||||||
button_(button),
|
|
||||||
x_(x),
|
|
||||||
y_(y),
|
|
||||||
dx_(dx),
|
|
||||||
dy_(dy) {}
|
|
||||||
~MouseEvent() override = default;
|
~MouseEvent() override = default;
|
||||||
|
|
||||||
bool is_handled() const { return handled_; }
|
bool is_handled() const { return handled_; }
|
||||||
|
@ -75,12 +69,12 @@ class MouseEvent : public UIEvent {
|
||||||
int32_t dy() const { return dy_; }
|
int32_t dy() const { return dy_; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool handled_;
|
bool handled_ = false;
|
||||||
Button button_;
|
Button button_;
|
||||||
int32_t x_;
|
int32_t x_ = 0;
|
||||||
int32_t y_;
|
int32_t y_ = 0;
|
||||||
int32_t dx_;
|
int32_t dx_ = 0;
|
||||||
int32_t dy_;
|
int32_t dy_ = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace ui
|
} // 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 *
|
* 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. *
|
* 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/animation_manager.h"
|
||||||
#include "el/util/debug.h"
|
#include "el/util/debug.h"
|
||||||
|
@ -28,6 +28,7 @@ namespace xe {
|
||||||
namespace ui {
|
namespace ui {
|
||||||
|
|
||||||
constexpr bool kContinuousRepaint = false;
|
constexpr bool kContinuousRepaint = false;
|
||||||
|
constexpr bool kShowPresentFps = kContinuousRepaint;
|
||||||
|
|
||||||
// Enables long press behaviors (context menu, etc).
|
// Enables long press behaviors (context menu, etc).
|
||||||
constexpr bool kTouch = false;
|
constexpr bool kTouch = false;
|
||||||
|
@ -37,19 +38,26 @@ constexpr double kDoubleClickDistance = 5;
|
||||||
|
|
||||||
constexpr int32_t kMouseWheelDetent = 120;
|
constexpr int32_t kMouseWheelDetent = 120;
|
||||||
|
|
||||||
Loop* elemental_loop_ = nullptr;
|
|
||||||
|
|
||||||
class RootElement : public el::Element {
|
class RootElement : public el::Element {
|
||||||
public:
|
public:
|
||||||
RootElement(ElementalControl* owner) : owner_(owner) {}
|
RootElement(Window* owner) : owner_(owner) {}
|
||||||
void OnInvalid() override { owner_->Invalidate(); }
|
void OnInvalid() override { owner_->Invalidate(); }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
ElementalControl* owner_ = nullptr;
|
Window* owner_ = nullptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
bool ElementalControl::InitializeElemental(Loop* loop,
|
Window::Window(Loop* loop, const std::wstring& title)
|
||||||
el::graphics::Renderer* renderer) {
|
: 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;
|
static bool has_initialized = false;
|
||||||
if (has_initialized) {
|
if (has_initialized) {
|
||||||
return true;
|
return true;
|
||||||
|
@ -58,7 +66,7 @@ bool ElementalControl::InitializeElemental(Loop* loop,
|
||||||
|
|
||||||
// Need to pass off the Loop we want Elemental to use.
|
// Need to pass off the Loop we want Elemental to use.
|
||||||
// TODO(benvanik): give the callback to elemental instead.
|
// TODO(benvanik): give the callback to elemental instead.
|
||||||
elemental_loop_ = loop;
|
Loop::set_elemental_loop(loop);
|
||||||
|
|
||||||
if (!el::Initialize(renderer)) {
|
if (!el::Initialize(renderer)) {
|
||||||
XELOGE("Failed to initialize elemental core");
|
XELOGE("Failed to initialize elemental core");
|
||||||
|
@ -116,18 +124,10 @@ bool ElementalControl::InitializeElemental(Loop* loop,
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
ElementalControl::ElementalControl(Loop* loop, uint32_t flags)
|
bool Window::OnCreate() { return true; }
|
||||||
: super(flags), loop_(loop) {}
|
|
||||||
|
|
||||||
ElementalControl::~ElementalControl() = default;
|
bool Window::MakeReady() {
|
||||||
|
renderer_ = context_->CreateElementalRenderer();
|
||||||
bool ElementalControl::Create() {
|
|
||||||
if (!super::Create()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create subclass renderer (GL, etc).
|
|
||||||
renderer_ = CreateRenderer();
|
|
||||||
|
|
||||||
// Initialize elemental.
|
// Initialize elemental.
|
||||||
// TODO(benvanik): once? Do we care about multiple controls?
|
// TODO(benvanik): once? Do we care about multiple controls?
|
||||||
|
@ -139,26 +139,49 @@ bool ElementalControl::Create() {
|
||||||
// TODO(benvanik): setup elements.
|
// TODO(benvanik): setup elements.
|
||||||
root_element_ = std::make_unique<RootElement>(this);
|
root_element_ = std::make_unique<RootElement>(this);
|
||||||
root_element_->set_background_skin(TBIDC("background"));
|
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;
|
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());
|
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());
|
return el::Skin::get()->Load(filename.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
void ElementalControl::Destroy() {
|
void Window::Layout() {
|
||||||
el::Shutdown();
|
auto e = UIEvent(this);
|
||||||
super::Destroy();
|
OnLayout(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ElementalControl::OnLayout(UIEvent& e) {
|
void Window::Invalidate() {}
|
||||||
super::OnLayout(e);
|
|
||||||
|
void Window::OnResize(UIEvent& e) { on_resize(e); }
|
||||||
|
|
||||||
|
void Window::OnLayout(UIEvent& e) {
|
||||||
|
on_layout(e);
|
||||||
if (!root_element()) {
|
if (!root_element()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -166,9 +189,8 @@ void ElementalControl::OnLayout(UIEvent& e) {
|
||||||
root_element()->set_rect({0, 0, width(), height()});
|
root_element()->set_rect({0, 0, width(), height()});
|
||||||
}
|
}
|
||||||
|
|
||||||
void ElementalControl::OnPaint(UIEvent& e) {
|
void Window::OnPaint(UIEvent& e) {
|
||||||
super::OnPaint(e);
|
if (!renderer()) {
|
||||||
if (!root_element()) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -184,26 +206,44 @@ void ElementalControl::OnPaint(UIEvent& e) {
|
||||||
|
|
||||||
// Update TB (run animations, handle deferred input, etc).
|
// Update TB (run animations, handle deferred input, etc).
|
||||||
el::AnimationManager::Update();
|
el::AnimationManager::Update();
|
||||||
|
if (root_element()) {
|
||||||
root_element()->InvokeProcessStates();
|
root_element()->InvokeProcessStates();
|
||||||
root_element()->InvokeProcess();
|
root_element()->InvokeProcess();
|
||||||
|
}
|
||||||
|
|
||||||
|
GraphicsContextLock context_lock(context_.get());
|
||||||
|
|
||||||
|
context_->BeginSwap();
|
||||||
|
|
||||||
|
on_painting(e);
|
||||||
|
|
||||||
renderer()->BeginPaint(width(), height());
|
renderer()->BeginPaint(width(), height());
|
||||||
|
|
||||||
// Render entire control hierarchy.
|
// Render entire control hierarchy.
|
||||||
|
if (root_element()) {
|
||||||
root_element()->InvokePaint(el::Element::PaintProps());
|
root_element()->InvokePaint(el::Element::PaintProps());
|
||||||
|
}
|
||||||
|
|
||||||
|
on_paint(e);
|
||||||
|
|
||||||
|
if (root_element() && kShowPresentFps) {
|
||||||
// Render debug overlay.
|
// Render debug overlay.
|
||||||
root_element()->computed_font()->DrawString(
|
root_element()->computed_font()->DrawString(
|
||||||
5, 5, el::Color(255, 0, 0),
|
5, 5, el::Color(255, 0, 0),
|
||||||
el::format_string("Frame %lld", frame_count_));
|
el::format_string("Frame %lld", frame_count_));
|
||||||
if (kContinuousRepaint) {
|
|
||||||
root_element()->computed_font()->DrawString(
|
root_element()->computed_font()->DrawString(
|
||||||
5, 20, el::Color(255, 0, 0), el::format_string("FPS: %d", fps_));
|
5, 20, el::Color(255, 0, 0),
|
||||||
|
el::format_string("Present FPS: %d", fps_));
|
||||||
}
|
}
|
||||||
|
|
||||||
renderer()->EndPaint();
|
renderer()->EndPaint();
|
||||||
|
|
||||||
|
on_painted(e);
|
||||||
|
|
||||||
|
context_->EndSwap();
|
||||||
|
|
||||||
// If animations are running, reinvalidate immediately.
|
// If animations are running, reinvalidate immediately.
|
||||||
|
if (root_element()) {
|
||||||
if (el::AnimationManager::has_running_animations()) {
|
if (el::AnimationManager::has_running_animations()) {
|
||||||
root_element()->Invalidate();
|
root_element()->Invalidate();
|
||||||
}
|
}
|
||||||
|
@ -211,20 +251,29 @@ void ElementalControl::OnPaint(UIEvent& e) {
|
||||||
// Force an immediate repaint, always.
|
// Force an immediate repaint, always.
|
||||||
root_element()->Invalidate();
|
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) {
|
void Window::OnHidden(UIEvent& e) { on_hidden(e); }
|
||||||
super::OnLostFocus(e);
|
|
||||||
|
void Window::OnGotFocus(UIEvent& e) { on_got_focus(e); }
|
||||||
|
|
||||||
|
void Window::OnLostFocus(UIEvent& e) {
|
||||||
modifier_shift_pressed_ = false;
|
modifier_shift_pressed_ = false;
|
||||||
modifier_cntrl_pressed_ = false;
|
modifier_cntrl_pressed_ = false;
|
||||||
modifier_alt_pressed_ = false;
|
modifier_alt_pressed_ = false;
|
||||||
modifier_super_pressed_ = false;
|
modifier_super_pressed_ = false;
|
||||||
last_click_time_ = 0;
|
last_click_time_ = 0;
|
||||||
|
on_lost_focus(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
el::ModifierKeys ElementalControl::GetModifierKeys() {
|
el::ModifierKeys Window::GetModifierKeys() {
|
||||||
auto modifiers = el::ModifierKeys::kNone;
|
auto modifiers = el::ModifierKeys::kNone;
|
||||||
if (modifier_shift_pressed_) {
|
if (modifier_shift_pressed_) {
|
||||||
modifiers |= el::ModifierKeys::kShift;
|
modifiers |= el::ModifierKeys::kShift;
|
||||||
|
@ -241,7 +290,7 @@ el::ModifierKeys ElementalControl::GetModifierKeys() {
|
||||||
return modifiers;
|
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()) {
|
if (!root_element()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -364,7 +413,7 @@ void ElementalControl::OnKeyPress(KeyEvent& e, bool is_down, bool is_char) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ElementalControl::CheckShortcutKey(KeyEvent& e, el::SpecialKey special_key,
|
bool Window::CheckShortcutKey(KeyEvent& e, el::SpecialKey special_key,
|
||||||
bool is_down) {
|
bool is_down) {
|
||||||
bool shortcut_key = modifier_cntrl_pressed_;
|
bool shortcut_key = modifier_cntrl_pressed_;
|
||||||
if (!el::Element::focused_element || !is_down || !shortcut_key) {
|
if (!el::Element::focused_element || !is_down || !shortcut_key) {
|
||||||
|
@ -417,24 +466,32 @@ bool ElementalControl::CheckShortcutKey(KeyEvent& e, el::SpecialKey special_key,
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ElementalControl::OnKeyDown(KeyEvent& e) {
|
void Window::OnKeyDown(KeyEvent& e) {
|
||||||
super::OnKeyDown(e);
|
on_key_down(e);
|
||||||
|
if (e.is_handled()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
OnKeyPress(e, true, false);
|
OnKeyPress(e, true, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ElementalControl::OnKeyUp(KeyEvent& e) {
|
void Window::OnKeyUp(KeyEvent& e) {
|
||||||
super::OnKeyUp(e);
|
on_key_up(e);
|
||||||
|
if (e.is_handled()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
OnKeyPress(e, false, false);
|
OnKeyPress(e, false, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ElementalControl::OnKeyChar(KeyEvent& e) {
|
void Window::OnKeyChar(KeyEvent& e) {
|
||||||
super::OnKeyChar(e);
|
|
||||||
OnKeyPress(e, true, true);
|
OnKeyPress(e, true, true);
|
||||||
OnKeyPress(e, false, true);
|
OnKeyPress(e, false, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ElementalControl::OnMouseDown(MouseEvent& e) {
|
void Window::OnMouseDown(MouseEvent& e) {
|
||||||
super::OnMouseDown(e);
|
on_mouse_down(e);
|
||||||
|
if (e.is_handled()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (!root_element()) {
|
if (!root_element()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -463,8 +520,11 @@ void ElementalControl::OnMouseDown(MouseEvent& e) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ElementalControl::OnMouseMove(MouseEvent& e) {
|
void Window::OnMouseMove(MouseEvent& e) {
|
||||||
super::OnMouseMove(e);
|
on_mouse_move(e);
|
||||||
|
if (e.is_handled()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (!root_element()) {
|
if (!root_element()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -472,8 +532,11 @@ void ElementalControl::OnMouseMove(MouseEvent& e) {
|
||||||
e.set_handled(true);
|
e.set_handled(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ElementalControl::OnMouseUp(MouseEvent& e) {
|
void Window::OnMouseUp(MouseEvent& e) {
|
||||||
super::OnMouseUp(e);
|
on_mouse_up(e);
|
||||||
|
if (e.is_handled()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (!root_element()) {
|
if (!root_element()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -494,8 +557,11 @@ void ElementalControl::OnMouseUp(MouseEvent& e) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ElementalControl::OnMouseWheel(MouseEvent& e) {
|
void Window::OnMouseWheel(MouseEvent& e) {
|
||||||
super::OnMouseWheel(e);
|
on_mouse_wheel(e);
|
||||||
|
if (e.is_handled()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (!root_element()) {
|
if (!root_element()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -505,32 +571,3 @@ void ElementalControl::OnMouseWheel(MouseEvent& e) {
|
||||||
|
|
||||||
} // namespace ui
|
} // namespace ui
|
||||||
} // namespace xe
|
} // 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_
|
#ifndef XENIA_UI_WINDOW_H_
|
||||||
#define XENIA_UI_WINDOW_H_
|
#define XENIA_UI_WINDOW_H_
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
|
#include "el/element.h"
|
||||||
|
#include "el/graphics/renderer.h"
|
||||||
#include "xenia/base/delegate.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/loop.h"
|
||||||
#include "xenia/ui/menu_item.h"
|
#include "xenia/ui/menu_item.h"
|
||||||
#include "xenia/ui/ui_event.h"
|
#include "xenia/ui/ui_event.h"
|
||||||
|
@ -21,14 +24,22 @@
|
||||||
namespace xe {
|
namespace xe {
|
||||||
namespace ui {
|
namespace ui {
|
||||||
|
|
||||||
template <typename T>
|
typedef void* NativeWindowHandle;
|
||||||
class Window : public T {
|
|
||||||
public:
|
|
||||||
~Window() override = default;
|
|
||||||
|
|
||||||
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_; }
|
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_; }
|
const std::wstring& title() const { return title_; }
|
||||||
virtual bool set_title(const std::wstring& title) {
|
virtual bool set_title(const std::wstring& title) {
|
||||||
|
@ -39,65 +50,124 @@ class Window : public T {
|
||||||
return true;
|
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 bool is_fullscreen() const { return false; }
|
||||||
virtual void ToggleFullscreen(bool fullscreen) {}
|
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:
|
public:
|
||||||
Delegate<UIEvent> on_shown;
|
|
||||||
Delegate<UIEvent> on_hidden;
|
|
||||||
Delegate<UIEvent> on_closing;
|
Delegate<UIEvent> on_closing;
|
||||||
Delegate<UIEvent> on_closed;
|
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:
|
protected:
|
||||||
Window(Loop* loop, const std::wstring& title)
|
Window(Loop* loop, const std::wstring& title);
|
||||||
: T(0), loop_(loop), title_(title) {}
|
|
||||||
|
|
||||||
void OnShow() {
|
bool InitializeElemental(Loop* loop, el::graphics::Renderer* renderer);
|
||||||
if (is_visible_) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
is_visible_ = true;
|
|
||||||
auto e = UIEvent(this);
|
|
||||||
on_shown(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
void OnHide() {
|
virtual bool OnCreate();
|
||||||
if (!is_visible_) {
|
virtual void OnMainMenuChange();
|
||||||
return;
|
virtual void OnClose();
|
||||||
}
|
virtual void OnDestroy();
|
||||||
is_visible_ = false;
|
|
||||||
auto e = UIEvent(this);
|
|
||||||
on_hidden(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
Loop* loop_ = nullptr;
|
||||||
|
std::unique_ptr<MenuItem> main_menu_;
|
||||||
std::wstring title_;
|
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
|
} // 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_
|
#ifndef XENIA_UI_WINDOW_WIN_H_
|
||||||
#define XENIA_UI_WIN32_WIN32_CONTROL_H_
|
#define XENIA_UI_WINDOW_WIN_H_
|
||||||
|
|
||||||
#include "xenia/base/platform.h"
|
#include <memory>
|
||||||
#include "xenia/ui/control.h"
|
#include <string>
|
||||||
|
|
||||||
|
#include "xenia/ui/menu_item.h"
|
||||||
|
#include "xenia/ui/window.h"
|
||||||
|
|
||||||
namespace xe {
|
namespace xe {
|
||||||
namespace ui {
|
namespace ui {
|
||||||
namespace win32 {
|
|
||||||
|
|
||||||
class Win32Control : public Control {
|
class Win32Window : public Window {
|
||||||
|
using super = Window;
|
||||||
|
|
||||||
public:
|
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 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 width, int32_t height) override;
|
||||||
void Resize(int32_t left, int32_t top, int32_t right,
|
void Resize(int32_t left, int32_t top, int32_t right,
|
||||||
int32_t bottom) override;
|
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;
|
bool Initialize() override;
|
||||||
void set_enabled(bool value) override;
|
void Invalidate() override;
|
||||||
void set_visible(bool value) override;
|
void Close() override;
|
||||||
void set_focus(bool value) override;
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
explicit Win32Control(uint32_t flags);
|
bool OnCreate() override;
|
||||||
|
void OnMainMenuChange() override;
|
||||||
void OnCreate() override;
|
|
||||||
void OnDestroy() override;
|
void OnDestroy() override;
|
||||||
|
void OnClose() override;
|
||||||
void OnChildAdded(Control* child_control) override;
|
|
||||||
void OnChildRemoved(Control* child_control) override;
|
|
||||||
|
|
||||||
void OnResize(UIEvent& e) override;
|
void OnResize(UIEvent& e) override;
|
||||||
|
|
||||||
|
@ -54,16 +58,37 @@ class Win32Control : public Control {
|
||||||
virtual LRESULT WndProc(HWND hWnd, UINT message, WPARAM wParam,
|
virtual LRESULT WndProc(HWND hWnd, UINT message, WPARAM wParam,
|
||||||
LPARAM lParam);
|
LPARAM lParam);
|
||||||
|
|
||||||
HWND hwnd_ = nullptr;
|
|
||||||
bool invalidated_ = true;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void EnableMMCSS();
|
||||||
bool HandleMouse(UINT message, WPARAM wParam, LPARAM lParam);
|
bool HandleMouse(UINT message, WPARAM wParam, LPARAM lParam);
|
||||||
bool HandleKeyboard(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 ui
|
||||||
} // namespace xe
|
} // 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/logging.h"
|
||||||
#include "xenia/base/main.h"
|
#include "xenia/base/main.h"
|
||||||
#include "xenia/emulator.h"
|
#include "xenia/emulator.h"
|
||||||
|
#include "xenia/emulator_window.h"
|
||||||
#include "xenia/kernel/kernel.h"
|
#include "xenia/kernel/kernel.h"
|
||||||
#include "xenia/profiling.h"
|
#include "xenia/profiling.h"
|
||||||
#include "xenia/ui/file_picker.h"
|
#include "xenia/ui/file_picker.h"
|
||||||
|
@ -24,9 +25,15 @@ int xenia_main(std::vector<std::wstring>& args) {
|
||||||
Profiler::Initialize();
|
Profiler::Initialize();
|
||||||
Profiler::ThreadEnter("main");
|
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"");
|
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)) {
|
if (XFAILED(result)) {
|
||||||
XELOGE("Failed to setup emulator: %.8X", result);
|
XELOGE("Failed to setup emulator: %.8X", result);
|
||||||
return 1;
|
return 1;
|
||||||
|
@ -48,20 +55,20 @@ int xenia_main(std::vector<std::wstring>& args) {
|
||||||
|
|
||||||
// If no path passed, ask the user.
|
// If no path passed, ask the user.
|
||||||
if (path.empty()) {
|
if (path.empty()) {
|
||||||
ui::PlatformFilePicker file_picker;
|
auto file_picker = xe::ui::FilePicker::Create();
|
||||||
file_picker.set_mode(ui::FilePicker::Mode::kOpen);
|
file_picker->set_mode(ui::FilePicker::Mode::kOpen);
|
||||||
file_picker.set_type(ui::FilePicker::Type::kFile);
|
file_picker->set_type(ui::FilePicker::Type::kFile);
|
||||||
file_picker.set_multi_selection(false);
|
file_picker->set_multi_selection(false);
|
||||||
file_picker.set_title(L"Select Content Package");
|
file_picker->set_title(L"Select Content Package");
|
||||||
file_picker.set_extensions({
|
file_picker->set_extensions({
|
||||||
{L"Supported Files", L"*.iso;*.xex;*.xcp;*.*"},
|
{L"Supported Files", L"*.iso;*.xex;*.xcp;*.*"},
|
||||||
{L"Disc Image (*.iso)", L"*.iso"},
|
{L"Disc Image (*.iso)", L"*.iso"},
|
||||||
{L"Xbox Executable (*.xex)", L"*.xex"},
|
{L"Xbox Executable (*.xex)", L"*.xex"},
|
||||||
//{ L"Content Package (*.xcp)", L"*.xcp" },
|
//{ L"Content Package (*.xcp)", L"*.xcp" },
|
||||||
{L"All Files (*.*)", L"*.*"},
|
{L"All Files (*.*)", L"*.*"},
|
||||||
});
|
});
|
||||||
if (file_picker.Show(emulator->display_window()->hwnd())) {
|
if (file_picker->Show(emulator->display_window()->native_handle())) {
|
||||||
auto selected_files = file_picker.selected_files();
|
auto selected_files = file_picker->selected_files();
|
||||||
if (!selected_files.empty()) {
|
if (!selected_files.empty()) {
|
||||||
path = selected_files[0];
|
path = selected_files[0];
|
||||||
}
|
}
|
||||||
|
@ -83,6 +90,8 @@ int xenia_main(std::vector<std::wstring>& args) {
|
||||||
}
|
}
|
||||||
|
|
||||||
emulator.reset();
|
emulator.reset();
|
||||||
|
emulator_window.reset();
|
||||||
|
|
||||||
Profiler::Dump();
|
Profiler::Dump();
|
||||||
Profiler::Shutdown();
|
Profiler::Shutdown();
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Subproject commit a95e9a1984029382ae8e35260725d5697a702f8b
|
Subproject commit 879a034ddf323744654bb810da5fe8e15143f6ea
|
|
@ -106,10 +106,19 @@
|
||||||
</ItemDefinitionGroup>
|
</ItemDefinitionGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClCompile Include="src\xenia\base\main_win.cc" />
|
<ClCompile Include="src\xenia\base\main_win.cc" />
|
||||||
|
<ClCompile Include="src\xenia\emulator_window.cc" />
|
||||||
<ClCompile Include="src\xenia\xenia_main.cc" />
|
<ClCompile Include="src\xenia\xenia_main.cc" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClInclude Include="src\xenia\base\main.h" />
|
<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>
|
</ItemGroup>
|
||||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||||
<ImportGroup Label="ExtensionTargets">
|
<ImportGroup Label="ExtensionTargets">
|
||||||
|
|
|
@ -14,6 +14,9 @@
|
||||||
<Filter Include="src\xenia\base">
|
<Filter Include="src\xenia\base">
|
||||||
<UniqueIdentifier>{a3d918ab-c42b-469d-9950-ec4656b77b32}</UniqueIdentifier>
|
<UniqueIdentifier>{a3d918ab-c42b-469d-9950-ec4656b77b32}</UniqueIdentifier>
|
||||||
</Filter>
|
</Filter>
|
||||||
|
<Filter Include="src\xenia\ui">
|
||||||
|
<UniqueIdentifier>{81255c06-3cf7-48ad-900a-f7c614d134ac}</UniqueIdentifier>
|
||||||
|
</Filter>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClCompile Include="src\xenia\base\main_win.cc">
|
<ClCompile Include="src\xenia\base\main_win.cc">
|
||||||
|
@ -22,10 +25,21 @@
|
||||||
<ClCompile Include="src\xenia\xenia_main.cc">
|
<ClCompile Include="src\xenia\xenia_main.cc">
|
||||||
<Filter>src\xenia</Filter>
|
<Filter>src\xenia</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
<ClCompile Include="src\xenia\emulator_window.cc">
|
||||||
|
<Filter>src\xenia</Filter>
|
||||||
|
</ClCompile>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClInclude Include="src\xenia\base\main.h">
|
<ClInclude Include="src\xenia\base\main.h">
|
||||||
<Filter>src\xenia\base</Filter>
|
<Filter>src\xenia\base</Filter>
|
||||||
</ClInclude>
|
</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>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
Loading…
Reference in New Issue