@@ -70,7 +70,7 @@ bool EmulatorWindow::Initialize() {
|
|||||||
window_->on_key_down.AddListener([this](KeyEvent& e) {
|
window_->on_key_down.AddListener([this](KeyEvent& e) {
|
||||||
bool handled = true;
|
bool handled = true;
|
||||||
switch (e.key_code()) {
|
switch (e.key_code()) {
|
||||||
case 0x0D: { // numpad enter
|
case 0x6A: { // numpad *
|
||||||
CpuTimeScalarReset();
|
CpuTimeScalarReset();
|
||||||
} break;
|
} break;
|
||||||
case 0x6D: { // numpad minus
|
case 0x6D: { // numpad minus
|
||||||
@@ -94,6 +94,8 @@ bool EmulatorWindow::Initialize() {
|
|||||||
// Allow users to escape fullscreen (but not enter it).
|
// Allow users to escape fullscreen (but not enter it).
|
||||||
if (window_->is_fullscreen()) {
|
if (window_->is_fullscreen()) {
|
||||||
window_->ToggleFullscreen(false);
|
window_->ToggleFullscreen(false);
|
||||||
|
} else {
|
||||||
|
handled = false;
|
||||||
}
|
}
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
@@ -121,7 +123,7 @@ bool EmulatorWindow::Initialize() {
|
|||||||
auto cpu_menu = MenuItem::Create(MenuItem::Type::kPopup, L"&CPU");
|
auto cpu_menu = MenuItem::Create(MenuItem::Type::kPopup, L"&CPU");
|
||||||
{
|
{
|
||||||
cpu_menu->AddChild(MenuItem::Create(
|
cpu_menu->AddChild(MenuItem::Create(
|
||||||
MenuItem::Type::kString, L"&Reset Time Scalar", L"Numpad Enter",
|
MenuItem::Type::kString, L"&Reset Time Scalar", L"Numpad *",
|
||||||
std::bind(&EmulatorWindow::CpuTimeScalarReset, this)));
|
std::bind(&EmulatorWindow::CpuTimeScalarReset, this)));
|
||||||
cpu_menu->AddChild(MenuItem::Create(
|
cpu_menu->AddChild(MenuItem::Create(
|
||||||
MenuItem::Type::kString, L"Time Scalar /= 2", L"Numpad -",
|
MenuItem::Type::kString, L"Time Scalar /= 2", L"Numpad -",
|
||||||
|
|||||||
@@ -38,9 +38,9 @@ class StringBuffer {
|
|||||||
private:
|
private:
|
||||||
void Grow(size_t additional_length);
|
void Grow(size_t additional_length);
|
||||||
|
|
||||||
char* buffer_;
|
char* buffer_ = nullptr;
|
||||||
size_t buffer_offset_;
|
size_t buffer_offset_ = 0;
|
||||||
size_t buffer_capacity_;
|
size_t buffer_capacity_ = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace xe
|
} // namespace xe
|
||||||
|
|||||||
@@ -7,8 +7,8 @@
|
|||||||
******************************************************************************
|
******************************************************************************
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include "el/elemental_forms.h"
|
||||||
#include "xenia/base/logging.h"
|
#include "xenia/base/logging.h"
|
||||||
#include "xenia/base/platform_win.h"
|
|
||||||
#include "xenia/emulator.h"
|
#include "xenia/emulator.h"
|
||||||
#include "xenia/kernel/kernel_state.h"
|
#include "xenia/kernel/kernel_state.h"
|
||||||
#include "xenia/kernel/util/shim_utils.h"
|
#include "xenia/kernel/util/shim_utils.h"
|
||||||
@@ -16,17 +16,87 @@
|
|||||||
#include "xenia/ui/window.h"
|
#include "xenia/ui/window.h"
|
||||||
#include "xenia/xbox.h"
|
#include "xenia/xbox.h"
|
||||||
|
|
||||||
#include <commctrl.h>
|
|
||||||
|
|
||||||
namespace xe {
|
namespace xe {
|
||||||
namespace kernel {
|
namespace kernel {
|
||||||
|
|
||||||
SHIM_CALL XamIsUIActive_shim(PPCContext* ppc_context,
|
SHIM_CALL XamIsUIActive_shim(PPCContext* ppc_context,
|
||||||
KernelState* kernel_state) {
|
KernelState* kernel_state) {
|
||||||
XELOGD("XamIsUIActive()");
|
XELOGD("XamIsUIActive()");
|
||||||
SHIM_SET_RETURN_32(0);
|
SHIM_SET_RETURN_32(el::ModalWindow::is_any_visible());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class MessageBoxWindow : public el::ModalWindow {
|
||||||
|
public:
|
||||||
|
MessageBoxWindow(xe::threading::Fence* fence)
|
||||||
|
: ModalWindow([fence]() { fence->Signal(); }) {}
|
||||||
|
~MessageBoxWindow() override = default;
|
||||||
|
|
||||||
|
// TODO(benvanik): icon.
|
||||||
|
void Show(el::Element* root_element, std::wstring title, std::wstring message,
|
||||||
|
std::vector<std::wstring> buttons, uint32_t default_button,
|
||||||
|
uint32_t* out_chosen_button) {
|
||||||
|
title_ = std::move(title);
|
||||||
|
message_ = std::move(message);
|
||||||
|
buttons_ = std::move(buttons);
|
||||||
|
default_button_ = default_button;
|
||||||
|
out_chosen_button_ = out_chosen_button;
|
||||||
|
|
||||||
|
// In case we are cancelled, always set default button.
|
||||||
|
*out_chosen_button_ = default_button;
|
||||||
|
|
||||||
|
ModalWindow::Show(root_element);
|
||||||
|
|
||||||
|
EnsureFocus();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void BuildUI() override {
|
||||||
|
using namespace el::dsl;
|
||||||
|
|
||||||
|
set_text(xe::to_string(title_));
|
||||||
|
|
||||||
|
auto button_bar = LayoutBoxNode().distribution_position(
|
||||||
|
LayoutDistributionPosition::kRightBottom);
|
||||||
|
for (int32_t i = 0; i < buttons_.size(); ++i) {
|
||||||
|
button_bar.child(ButtonNode(xe::to_string(buttons_[i]).c_str())
|
||||||
|
.id(100 + i)
|
||||||
|
.data(100 + i));
|
||||||
|
}
|
||||||
|
|
||||||
|
LoadNodeTree(
|
||||||
|
LayoutBoxNode()
|
||||||
|
.axis(Axis::kY)
|
||||||
|
.gravity(Gravity::kAll)
|
||||||
|
.position(LayoutPosition::kLeftTop)
|
||||||
|
.distribution(LayoutDistribution::kAvailable)
|
||||||
|
.distribution_position(LayoutDistributionPosition::kLeftTop)
|
||||||
|
.child(LabelNode(xe::to_string(message_).c_str()))
|
||||||
|
.child(button_bar));
|
||||||
|
|
||||||
|
auto default_button = GetElementById<el::Button>(100 + default_button_);
|
||||||
|
el::Button::set_auto_focus_state(true);
|
||||||
|
default_button->set_focus(el::FocusReason::kNavigation);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool OnEvent(const el::Event& ev) override {
|
||||||
|
if (ev.target->IsOfType<el::Button>() && ev.type == el::EventType::kClick) {
|
||||||
|
int data_value = ev.target->data.as_integer();
|
||||||
|
if (data_value) {
|
||||||
|
*out_chosen_button_ = data_value - 100;
|
||||||
|
}
|
||||||
|
Die();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return ModalWindow::OnEvent(ev);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::wstring title_;
|
||||||
|
std::wstring message_;
|
||||||
|
std::vector<std::wstring> buttons_;
|
||||||
|
uint32_t default_button_ = 0;
|
||||||
|
uint32_t* out_chosen_button_ = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
// http://www.se7ensins.com/forums/threads/working-xshowmessageboxui.844116/?jdfwkey=sb0vm
|
// http://www.se7ensins.com/forums/threads/working-xshowmessageboxui.844116/?jdfwkey=sb0vm
|
||||||
SHIM_CALL XamShowMessageBoxUI_shim(PPCContext* ppc_context,
|
SHIM_CALL XamShowMessageBoxUI_shim(PPCContext* ppc_context,
|
||||||
KernelState* kernel_state) {
|
KernelState* kernel_state) {
|
||||||
@@ -66,49 +136,29 @@ SHIM_CALL XamShowMessageBoxUI_shim(PPCContext* ppc_context,
|
|||||||
// Auto-pick the focused button.
|
// Auto-pick the focused button.
|
||||||
chosen_button = active_button;
|
chosen_button = active_button;
|
||||||
} else {
|
} else {
|
||||||
TASKDIALOGCONFIG config = {0};
|
auto display_window = kernel_state->emulator()->display_window();
|
||||||
config.cbSize = sizeof(config);
|
xe::threading::Fence fence;
|
||||||
config.hInstance = GetModuleHandle(nullptr);
|
display_window->loop()->PostSynchronous([&]() {
|
||||||
config.hwndParent =
|
auto root_element = display_window->root_element();
|
||||||
HWND(kernel_state->emulator()->display_window()->native_handle());
|
auto window = new MessageBoxWindow(&fence);
|
||||||
config.dwFlags = TDF_ALLOW_DIALOG_CANCELLATION | // esc to exit
|
switch (flags & 0xF) {
|
||||||
TDF_POSITION_RELATIVE_TO_WINDOW; // center in window
|
case 0:
|
||||||
config.dwCommonButtons = 0;
|
// config.pszMainIcon = nullptr;
|
||||||
config.pszWindowTitle = title.c_str();
|
break;
|
||||||
switch (flags & 0xF) {
|
case 1:
|
||||||
case 0:
|
// config.pszMainIcon = TD_ERROR_ICON;
|
||||||
config.pszMainIcon = nullptr;
|
break;
|
||||||
break;
|
case 2:
|
||||||
case 1:
|
// config.pszMainIcon = TD_WARNING_ICON;
|
||||||
config.pszMainIcon = TD_ERROR_ICON;
|
break;
|
||||||
break;
|
case 3:
|
||||||
case 2:
|
// config.pszMainIcon = TD_INFORMATION_ICON;
|
||||||
config.pszMainIcon = TD_WARNING_ICON;
|
break;
|
||||||
break;
|
}
|
||||||
case 3:
|
window->Show(root_element, title, text, buttons, active_button,
|
||||||
config.pszMainIcon = TD_INFORMATION_ICON;
|
&chosen_button);
|
||||||
break;
|
});
|
||||||
}
|
fence.Wait();
|
||||||
config.pszMainInstruction = text.c_str();
|
|
||||||
config.pszContent = nullptr;
|
|
||||||
std::vector<TASKDIALOG_BUTTON> taskdialog_buttons;
|
|
||||||
for (uint32_t i = 0; i < button_count; ++i) {
|
|
||||||
taskdialog_buttons.push_back({1000 + int(i), buttons[i].c_str()});
|
|
||||||
}
|
|
||||||
config.pButtons = taskdialog_buttons.data();
|
|
||||||
config.cButtons = button_count;
|
|
||||||
config.nDefaultButton = active_button;
|
|
||||||
int button_pressed = 0;
|
|
||||||
TaskDialogIndirect(&config, &button_pressed, nullptr, nullptr);
|
|
||||||
switch (button_pressed) {
|
|
||||||
default:
|
|
||||||
chosen_button = button_pressed - 1000;
|
|
||||||
break;
|
|
||||||
case IDCANCEL:
|
|
||||||
// User cancelled, just pick default.
|
|
||||||
chosen_button = active_button;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
SHIM_SET_MEM_32(result_ptr, chosen_button);
|
SHIM_SET_MEM_32(result_ptr, chosen_button);
|
||||||
|
|
||||||
@@ -116,20 +166,71 @@ SHIM_CALL XamShowMessageBoxUI_shim(PPCContext* ppc_context,
|
|||||||
SHIM_SET_RETURN_32(X_ERROR_IO_PENDING);
|
SHIM_SET_RETURN_32(X_ERROR_IO_PENDING);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class DirtyDiscWindow : public el::ModalWindow {
|
||||||
|
public:
|
||||||
|
DirtyDiscWindow(xe::threading::Fence* fence)
|
||||||
|
: ModalWindow([fence]() { fence->Signal(); }) {}
|
||||||
|
~DirtyDiscWindow() override = default;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void BuildUI() override {
|
||||||
|
using namespace el::dsl;
|
||||||
|
|
||||||
|
set_text("Disc Read Error");
|
||||||
|
|
||||||
|
LoadNodeTree(
|
||||||
|
LayoutBoxNode()
|
||||||
|
.axis(Axis::kY)
|
||||||
|
.gravity(Gravity::kAll)
|
||||||
|
.position(LayoutPosition::kLeftTop)
|
||||||
|
.distribution(LayoutDistribution::kAvailable)
|
||||||
|
.distribution_position(LayoutDistributionPosition::kLeftTop)
|
||||||
|
.child(LabelNode(
|
||||||
|
"There's been an issue reading content from the game disc."))
|
||||||
|
.child(LabelNode(
|
||||||
|
"This is likely caused by bad or unimplemented file IO "
|
||||||
|
"calls."))
|
||||||
|
.child(LayoutBoxNode()
|
||||||
|
.distribution_position(
|
||||||
|
LayoutDistributionPosition::kRightBottom)
|
||||||
|
.child(ButtonNode("Exit").id("exit_button"))));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool OnEvent(const el::Event& ev) override {
|
||||||
|
if (ev.target->id() == TBIDC("exit_button") &&
|
||||||
|
ev.type == el::EventType::kClick) {
|
||||||
|
Die();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return ModalWindow::OnEvent(ev);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
SHIM_CALL XamShowDirtyDiscErrorUI_shim(PPCContext* ppc_context,
|
SHIM_CALL XamShowDirtyDiscErrorUI_shim(PPCContext* ppc_context,
|
||||||
KernelState* kernel_state) {
|
KernelState* kernel_state) {
|
||||||
uint32_t user_index = SHIM_GET_ARG_32(0);
|
uint32_t user_index = SHIM_GET_ARG_32(0);
|
||||||
|
|
||||||
XELOGD("XamShowDirtyDiscErrorUI(%d)", user_index);
|
XELOGD("XamShowDirtyDiscErrorUI(%d)", user_index);
|
||||||
|
|
||||||
int button_pressed = 0;
|
if (FLAGS_headless) {
|
||||||
TaskDialog(HWND(kernel_state->emulator()->display_window()->native_handle()),
|
assert_always();
|
||||||
GetModuleHandle(nullptr), L"Disc Read Error",
|
exit(1);
|
||||||
L"Game is claiming to be unable to read game data!", nullptr,
|
return;
|
||||||
TDCBF_CLOSE_BUTTON, TD_ERROR_ICON, &button_pressed);
|
}
|
||||||
|
|
||||||
|
auto display_window = kernel_state->emulator()->display_window();
|
||||||
|
xe::threading::Fence fence;
|
||||||
|
display_window->loop()->PostSynchronous([&]() {
|
||||||
|
auto root_element = display_window->root_element();
|
||||||
|
auto window = new DirtyDiscWindow(&fence);
|
||||||
|
window->Show(root_element);
|
||||||
|
});
|
||||||
|
fence.Wait();
|
||||||
|
|
||||||
// This is death, and should never return.
|
// This is death, and should never return.
|
||||||
|
// TODO(benvanik): cleaner exit.
|
||||||
assert_always();
|
assert_always();
|
||||||
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace kernel
|
} // namespace kernel
|
||||||
|
|||||||
@@ -171,6 +171,10 @@ int XboxkrnlModule::LaunchModule(const char* path) {
|
|||||||
// Create and register the module. We keep it local to this function and
|
// Create and register the module. We keep it local to this function and
|
||||||
// dispose it on exit.
|
// dispose it on exit.
|
||||||
auto module = kernel_state_->LoadUserModule(path);
|
auto module = kernel_state_->LoadUserModule(path);
|
||||||
|
if (!module) {
|
||||||
|
XELOGE("Failed to load user module %s.", path);
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
// Set as the main module, while running.
|
// Set as the main module, while running.
|
||||||
kernel_state_->SetExecutableModule(module);
|
kernel_state_->SetExecutableModule(module);
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ std::unique_ptr<ProfilerDisplay> Profiler::display_ = nullptr;
|
|||||||
|
|
||||||
bool Profiler::is_enabled() { return true; }
|
bool Profiler::is_enabled() { return true; }
|
||||||
|
|
||||||
bool Profiler::is_visible() { return MicroProfileIsDrawing(); }
|
bool Profiler::is_visible() { return is_enabled() && MicroProfileIsDrawing(); }
|
||||||
|
|
||||||
void Profiler::Initialize() {
|
void Profiler::Initialize() {
|
||||||
// Custom groups.
|
// Custom groups.
|
||||||
|
|||||||
@@ -223,6 +223,7 @@ void GL4ElementalRenderer::BeginPaint(int render_target_w,
|
|||||||
|
|
||||||
glUseProgram(program_);
|
glUseProgram(program_);
|
||||||
glBindVertexArray(vao_);
|
glBindVertexArray(vao_);
|
||||||
|
glProgramUniform1f(program_, 2, 0.0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GL4ElementalRenderer::EndPaint() {
|
void GL4ElementalRenderer::EndPaint() {
|
||||||
@@ -250,15 +251,6 @@ void GL4ElementalRenderer::Flush() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void GL4ElementalRenderer::RenderBatch(Batch* batch) {
|
void GL4ElementalRenderer::RenderBatch(Batch* batch) {
|
||||||
auto bitmap = static_cast<GL4Bitmap*>(batch->bitmap);
|
|
||||||
if (bitmap != current_bitmap_) {
|
|
||||||
current_bitmap_ = bitmap;
|
|
||||||
Flush();
|
|
||||||
glProgramUniformHandleui64ARB(program_, 1,
|
|
||||||
bitmap ? bitmap->gpu_handle_ : 0);
|
|
||||||
glProgramUniform1f(program_, 2, bitmap ? 1.0f : 0.0f);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (draw_command_count_ + 1 > kMaxCommands) {
|
if (draw_command_count_ + 1 > kMaxCommands) {
|
||||||
Flush();
|
Flush();
|
||||||
}
|
}
|
||||||
@@ -266,6 +258,16 @@ void GL4ElementalRenderer::RenderBatch(Batch* batch) {
|
|||||||
if (!vertex_buffer_.CanAcquire(total_length)) {
|
if (!vertex_buffer_.CanAcquire(total_length)) {
|
||||||
Flush();
|
Flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto bitmap = static_cast<GL4Bitmap*>(batch->bitmap);
|
||||||
|
if (bitmap != current_bitmap_) {
|
||||||
|
Flush();
|
||||||
|
current_bitmap_ = bitmap;
|
||||||
|
glProgramUniformHandleui64ARB(program_, 1,
|
||||||
|
bitmap ? bitmap->gpu_handle_ : 0);
|
||||||
|
glProgramUniform1f(program_, 2, bitmap ? 1.0f : 0.0f);
|
||||||
|
}
|
||||||
|
|
||||||
auto allocation = vertex_buffer_.Acquire(total_length);
|
auto allocation = vertex_buffer_.Acquire(total_length);
|
||||||
|
|
||||||
// TODO(benvanik): custom batcher that lets us use the ringbuffer memory
|
// TODO(benvanik): custom batcher that lets us use the ringbuffer memory
|
||||||
|
|||||||
@@ -144,43 +144,49 @@ GLProfilerDisplay::GLProfilerDisplay(xe::ui::Window* window)
|
|||||||
window_->on_painted.AddListener([this](UIEvent& e) { Profiler::Present(); });
|
window_->on_painted.AddListener([this](UIEvent& e) { Profiler::Present(); });
|
||||||
|
|
||||||
// Pass through mouse events.
|
// Pass through mouse events.
|
||||||
window_->on_mouse_down.AddListener([](xe::ui::MouseEvent& e) {
|
window_->on_mouse_down.AddListener([this](xe::ui::MouseEvent& e) {
|
||||||
if (Profiler::is_enabled()) {
|
if (Profiler::is_visible()) {
|
||||||
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);
|
||||||
|
window_->Invalidate();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
window_->on_mouse_up.AddListener([](xe::ui::MouseEvent& e) {
|
window_->on_mouse_up.AddListener([this](xe::ui::MouseEvent& e) {
|
||||||
if (Profiler::is_enabled()) {
|
if (Profiler::is_visible()) {
|
||||||
Profiler::OnMouseUp();
|
Profiler::OnMouseUp();
|
||||||
e.set_handled(true);
|
e.set_handled(true);
|
||||||
|
window_->Invalidate();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
window_->on_mouse_move.AddListener([](xe::ui::MouseEvent& e) {
|
window_->on_mouse_move.AddListener([this](xe::ui::MouseEvent& e) {
|
||||||
if (Profiler::is_enabled()) {
|
if (Profiler::is_visible()) {
|
||||||
Profiler::OnMouseMove(e.x(), e.y());
|
Profiler::OnMouseMove(e.x(), e.y());
|
||||||
e.set_handled(true);
|
e.set_handled(true);
|
||||||
|
window_->Invalidate();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
window_->on_mouse_wheel.AddListener([](xe::ui::MouseEvent& e) {
|
window_->on_mouse_wheel.AddListener([this](xe::ui::MouseEvent& e) {
|
||||||
if (Profiler::is_enabled()) {
|
if (Profiler::is_visible()) {
|
||||||
Profiler::OnMouseWheel(e.x(), e.y(), -e.dy());
|
Profiler::OnMouseWheel(e.x(), e.y(), -e.dy());
|
||||||
e.set_handled(true);
|
e.set_handled(true);
|
||||||
|
window_->Invalidate();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Watch for toggle/mode keys and such.
|
// Watch for toggle/mode keys and such.
|
||||||
window_->on_key_down.AddListener([](xe::ui::KeyEvent& e) {
|
window_->on_key_down.AddListener([this](xe::ui::KeyEvent& e) {
|
||||||
if (Profiler::is_enabled()) {
|
if (Profiler::is_visible()) {
|
||||||
Profiler::OnKeyDown(e.key_code());
|
Profiler::OnKeyDown(e.key_code());
|
||||||
e.set_handled(true);
|
e.set_handled(true);
|
||||||
|
window_->Invalidate();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
window_->on_key_up.AddListener([](xe::ui::KeyEvent& e) {
|
window_->on_key_up.AddListener([this](xe::ui::KeyEvent& e) {
|
||||||
if (Profiler::is_enabled()) {
|
if (Profiler::is_visible()) {
|
||||||
Profiler::OnKeyUp(e.key_code());
|
Profiler::OnKeyUp(e.key_code());
|
||||||
e.set_handled(true);
|
e.set_handled(true);
|
||||||
|
window_->Invalidate();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
2
third_party/elemental-forms
vendored
2
third_party/elemental-forms
vendored
Submodule third_party/elemental-forms updated: 187fb988e0...b0ecdb343c
Reference in New Issue
Block a user