From d839359b4a4fe9e293f7c52bf51a3368eae2c5cf Mon Sep 17 00:00:00 2001 From: Ben Vanik Date: Sat, 20 Dec 2014 13:54:55 -0800 Subject: [PATCH] UI hacking. --- src/poly/main_win.cc | 10 + src/poly/sources.gypi | 1 + src/poly/ui/control.cc | 75 ++++ src/poly/ui/control.h | 120 ++++++ src/{xenia/ui/menu_item.cc => poly/ui/loop.h} | 23 +- src/poly/ui/menu_item.cc | 49 +++ src/poly/ui/menu_item.h | 61 +++ src/poly/ui/sources.gypi | 20 + src/{xenia => poly}/ui/ui_event.h | 43 +-- src/{xenia => poly}/ui/win32/sources.gypi | 2 + src/poly/ui/win32/win32_control.cc | 314 ++++++++++++++++ src/poly/ui/win32/win32_control.h | 62 +++ src/poly/ui/win32/win32_loop.cc | 72 ++++ src/{xenia => poly}/ui/win32/win32_loop.h | 28 +- src/poly/ui/win32/win32_menu_item.cc | 37 ++ .../ui/win32/win32_menu_item.h | 24 +- src/poly/ui/win32/win32_window.cc | 189 ++++++++++ src/{xenia => poly}/ui/win32/win32_window.h | 35 +- src/poly/ui/window.h | 84 +++++ src/xenia/emulator.cc | 27 +- src/xenia/emulator.h | 8 +- .../win32_menu_item.cc => main_window.cc} | 27 +- src/xenia/ui/{menu_item.h => main_window.h} | 33 +- src/xenia/ui/sources.gypi | 15 +- src/xenia/ui/win32/win32_loop.cc | 44 --- src/xenia/ui/win32/win32_window.cc | 352 ------------------ src/xenia/ui/window.cc | 114 ------ src/xenia/ui/window.h | 80 ---- 28 files changed, 1235 insertions(+), 714 deletions(-) create mode 100644 src/poly/ui/control.cc create mode 100644 src/poly/ui/control.h rename src/{xenia/ui/menu_item.cc => poly/ui/loop.h} (64%) create mode 100644 src/poly/ui/menu_item.cc create mode 100644 src/poly/ui/menu_item.h create mode 100644 src/poly/ui/sources.gypi rename src/{xenia => poly}/ui/ui_event.h (63%) rename src/{xenia => poly}/ui/win32/sources.gypi (81%) create mode 100644 src/poly/ui/win32/win32_control.cc create mode 100644 src/poly/ui/win32/win32_control.h create mode 100644 src/poly/ui/win32/win32_loop.cc rename src/{xenia => poly}/ui/win32/win32_loop.h (60%) create mode 100644 src/poly/ui/win32/win32_menu_item.cc rename src/{xenia => poly}/ui/win32/win32_menu_item.h (62%) create mode 100644 src/poly/ui/win32/win32_window.cc rename src/{xenia => poly}/ui/win32/win32_window.h (51%) create mode 100644 src/poly/ui/window.h rename src/xenia/ui/{win32/win32_menu_item.cc => main_window.cc} (57%) rename src/xenia/ui/{menu_item.h => main_window.h} (53%) delete mode 100644 src/xenia/ui/win32/win32_loop.cc delete mode 100644 src/xenia/ui/win32/win32_window.cc delete mode 100644 src/xenia/ui/window.cc delete mode 100644 src/xenia/ui/window.h diff --git a/src/poly/main_win.cc b/src/poly/main_win.cc index c01705ba7..122fa5327 100644 --- a/src/poly/main_win.cc +++ b/src/poly/main_win.cc @@ -107,3 +107,13 @@ int WINAPI wWinMain(HINSTANCE, HINSTANCE, LPWSTR command_line, int) { LocalFree(argv); return result; } + +#if defined _M_IX86 +#pragma comment(linker, "/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='x86' publicKeyToken='6595b64144ccf1df' language='*'\"") +#elif defined _M_IA64 +#pragma comment(linker, "/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='ia64' publicKeyToken='6595b64144ccf1df' language='*'\"") +#elif defined _M_X64 +#pragma comment(linker, "/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='amd64' publicKeyToken='6595b64144ccf1df' language='*'\"") +#else +#pragma comment(linker, "/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"") +#endif diff --git a/src/poly/sources.gypi b/src/poly/sources.gypi index ec514df44..c17b8b463 100644 --- a/src/poly/sources.gypi +++ b/src/poly/sources.gypi @@ -52,5 +52,6 @@ ], 'includes': [ + 'ui/sources.gypi', ], } diff --git a/src/poly/ui/control.cc b/src/poly/ui/control.cc new file mode 100644 index 000000000..1d93690e5 --- /dev/null +++ b/src/poly/ui/control.cc @@ -0,0 +1,75 @@ +/** + ****************************************************************************** + * 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 + +namespace poly { +namespace ui { + +Control::Control(Control* parent, uint32_t flags) + : parent_(parent), + flags_(flags), + 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 child_control) { + AddChild(ControlPtr(child_control.release(), + [](Control* control) { delete control; })); +} + +void Control::AddChild(ControlPtr control) { + auto control_ptr = control.get(); + children_.emplace_back(std::move(control)); + OnChildAdded(control_ptr); +} + +void Control::RemoveChild(Control* child_control) { + for (auto& it = children_.begin(); it != children_.end(); ++it) { + if (it->get() == child_control) { + children_.erase(it); + OnChildRemoved(child_control); + break; + } + } +} + +void Control::OnResize(UIEvent& e) { on_resize(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); } + +void Control::OnKeyUp(KeyEvent& e) { on_key_up(e); } + +void Control::OnMouseDown(MouseEvent& e) { on_mouse_down(e); } + +void Control::OnMouseMove(MouseEvent& e) { on_mouse_move(e); } + +void Control::OnMouseUp(MouseEvent& e) { on_mouse_up(e); } + +void Control::OnMouseWheel(MouseEvent& e) { on_mouse_wheel(e); } + +} // namespace ui +} // namespace poly diff --git a/src/poly/ui/control.h b/src/poly/ui/control.h new file mode 100644 index 000000000..475bb2651 --- /dev/null +++ b/src/poly/ui/control.h @@ -0,0 +1,120 @@ +/** + ****************************************************************************** + * 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 POLY_UI_CONTROL_H_ +#define POLY_UI_CONTROL_H_ + +#include +#include + +#include +#include + +namespace poly { +namespace ui { + +class Control { + public: + typedef std::unique_ptr ControlPtr; + enum Flags { + // Control paints itself, so disable platform drawing. + kFlagOwnPaint = 1 << 1, + }; + + virtual ~Control(); + + Control* parent() const { return parent_; } + + void AddChild(Control* child_control); + void AddChild(std::unique_ptr child_control); + void AddChild(ControlPtr child_control); + void 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; + virtual void ResizeToFill(int32_t pad_left, int32_t pad_top, + int32_t pad_right, int32_t pad_bottom) = 0; + + // 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: + poly::Delegate on_resize; + + poly::Delegate on_visible; + poly::Delegate on_hidden; + + poly::Delegate on_got_focus; + poly::Delegate on_lost_focus; + + poly::Delegate on_key_down; + poly::Delegate on_key_up; + + poly::Delegate on_mouse_down; + poly::Delegate on_mouse_move; + poly::Delegate on_mouse_up; + poly::Delegate on_mouse_wheel; + + protected: + Control(Control* parent, uint32_t flags); + + 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 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 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 children_; + + int32_t width_; + int32_t height_; + + bool is_cursor_visible_; + bool is_enabled_; + bool is_visible_; + bool has_focus_; +}; + +} // namespace ui +} // namespace poly + +#endif // POLY_UI_CONTROL_H_ diff --git a/src/xenia/ui/menu_item.cc b/src/poly/ui/loop.h similarity index 64% rename from src/xenia/ui/menu_item.cc rename to src/poly/ui/loop.h index 89b050810..e5ae3cf72 100644 --- a/src/xenia/ui/menu_item.cc +++ b/src/poly/ui/loop.h @@ -7,18 +7,25 @@ ****************************************************************************** */ -#include +#ifndef POLY_UI_LOOP_H_ +#define POLY_UI_LOOP_H_ -namespace xe { +#include + +namespace poly { namespace ui { -MenuItem::MenuItem(Window* window) : window_(window), parent_item_(nullptr) {} +class Loop { + public: + Loop() = default; + virtual ~Loop() = default; -MenuItem::~MenuItem() {} + virtual void Post(std::function fn) = 0; -void MenuItem::AddChild(std::unique_ptr child_item) { - children_.emplace_back(std::move(child_item)); -} + virtual void Quit() = 0; +}; } // namespace ui -} // namespace xe +} // namespace poly + +#endif // POLY_UI_LOOP_H_ diff --git a/src/poly/ui/menu_item.cc b/src/poly/ui/menu_item.cc new file mode 100644 index 000000000..c75ab80ad --- /dev/null +++ b/src/poly/ui/menu_item.cc @@ -0,0 +1,49 @@ +/** + ****************************************************************************** + * 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 + +namespace poly { +namespace ui { + +MenuItem::MenuItem(Type type) : type_(type), parent_item_(nullptr) {} + +MenuItem::~MenuItem() = default; + +void MenuItem::AddChild(MenuItem* child_item) { + AddChild(MenuItemPtr(child_item, [](MenuItem* item) {})); +} + +void MenuItem::AddChild(std::unique_ptr child_item) { + AddChild( + MenuItemPtr(child_item.release(), [](MenuItem* item) { delete item; })); +} + +void MenuItem::AddChild(MenuItemPtr child_item) { + auto child_item_ptr = child_item.get(); + children_.emplace_back(std::move(child_item)); + OnChildAdded(child_item_ptr); +} + +void MenuItem::RemoveChild(MenuItem* child_item) { + for (auto& it = children_.begin(); it != children_.end(); ++it) { + if (it->get() == child_item) { + children_.erase(it); + OnChildRemoved(child_item); + break; + } + } +} + +void MenuItem::OnSelected(UIEvent& e) { + on_selected(e); +} + +} // namespace ui +} // namespace poly diff --git a/src/poly/ui/menu_item.h b/src/poly/ui/menu_item.h new file mode 100644 index 000000000..cdd285b55 --- /dev/null +++ b/src/poly/ui/menu_item.h @@ -0,0 +1,61 @@ +/** + ****************************************************************************** + * 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 POLY_UI_MENU_ITEM_H_ +#define POLY_UI_MENU_ITEM_H_ + +#include +#include + +#include +#include + +namespace poly { +namespace ui { + +class Window; + +class MenuItem { + public: + typedef std::unique_ptr MenuItemPtr; + + enum class Type { + kNormal, + kSeparator, + }; + + virtual ~MenuItem(); + + MenuItem* parent_item() const { return parent_item_; } + + void AddChild(MenuItem* child_item); + void AddChild(std::unique_ptr child_item); + void AddChild(MenuItemPtr child_item); + void RemoveChild(MenuItem* child_item); + + poly::Delegate on_selected; + + protected: + MenuItem(Type type); + + virtual void OnChildAdded(MenuItem* child_item) {} + virtual void OnChildRemoved(MenuItem* child_item) {} + + virtual void OnSelected(UIEvent& e); + + private: + Type type_; + MenuItem* parent_item_; + std::vector children_; +}; + +} // namespace ui +} // namespace poly + +#endif // POLY_UI_MENU_ITEM_H_ diff --git a/src/poly/ui/sources.gypi b/src/poly/ui/sources.gypi new file mode 100644 index 000000000..74d1b4d52 --- /dev/null +++ b/src/poly/ui/sources.gypi @@ -0,0 +1,20 @@ +# Copyright 2014 Ben Vanik. All Rights Reserved. +{ + 'sources': [ + 'control.cc', + 'control.h', + 'loop.h', + 'menu_item.cc', + 'menu_item.h', + 'ui_event.h', + 'window.h', + ], + + 'conditions': [ + ['OS == "win"', { + 'includes': [ + 'win32/sources.gypi', + ], + }], + ], +} diff --git a/src/xenia/ui/ui_event.h b/src/poly/ui/ui_event.h similarity index 63% rename from src/xenia/ui/ui_event.h rename to src/poly/ui/ui_event.h index 3f4fd8dd1..eb3dfe8bc 100644 --- a/src/xenia/ui/ui_event.h +++ b/src/poly/ui/ui_event.h @@ -7,32 +7,29 @@ ****************************************************************************** */ -#ifndef XENIA_UI_UI_EVENT_H_ -#define XENIA_UI_UI_EVENT_H_ +#ifndef POLY_UI_UI_EVENT_H_ +#define POLY_UI_UI_EVENT_H_ -#include - -namespace xe { +namespace poly { namespace ui { -class App; -class Window; +class Control; class UIEvent { public: - UIEvent(Window* window = NULL) : window_(window) {} + UIEvent(Control* control = nullptr) : control_(control) {} virtual ~UIEvent() = default; - Window* window() const { return window_; } + Control* control() const { return control_; } private: - Window* window_; + Control* control_; }; class KeyEvent : public UIEvent { public: - KeyEvent(Window* window, int key_code) - : UIEvent(window), key_code_(key_code) {} + KeyEvent(Control* control, int key_code) + : UIEvent(control), key_code_(key_code) {} ~KeyEvent() override = default; int key_code() const { return key_code_; } @@ -43,19 +40,19 @@ class KeyEvent : public UIEvent { class MouseEvent : public UIEvent { public: - enum Button { - MOUSE_BUTTON_NONE = 0, - MOUSE_BUTTON_LEFT, - MOUSE_BUTTON_RIGHT, - MOUSE_BUTTON_MIDDLE, - MOUSE_BUTTON_X1, - MOUSE_BUTTON_X2, + enum class Button { + kNone = 0, + kLeft, + kRight, + kMiddle, + kX1, + kX2, }; public: - MouseEvent(Window* window, Button button, int32_t x, int32_t y, + MouseEvent(Control* control, Button button, int32_t x, int32_t y, int32_t dx = 0, int32_t dy = 0) - : UIEvent(window), button_(button), x_(x), y_(y), dx_(dx), dy_(dy) {} + : UIEvent(control), button_(button), x_(x), y_(y), dx_(dx), dy_(dy) {} ~MouseEvent() override = default; Button button() const { return button_; } @@ -73,6 +70,6 @@ class MouseEvent : public UIEvent { }; } // namespace ui -} // namespace xe +} // namespace poly -#endif // XENIA_UI_UI_EVENT_H_ +#endif // POLY_UI_UI_EVENT_H_ diff --git a/src/xenia/ui/win32/sources.gypi b/src/poly/ui/win32/sources.gypi similarity index 81% rename from src/xenia/ui/win32/sources.gypi rename to src/poly/ui/win32/sources.gypi index ae78b203b..184e94c3f 100644 --- a/src/xenia/ui/win32/sources.gypi +++ b/src/poly/ui/win32/sources.gypi @@ -1,6 +1,8 @@ # Copyright 2014 Ben Vanik. All Rights Reserved. { 'sources': [ + 'win32_control.cc', + 'win32_control.h', 'win32_loop.cc', 'win32_loop.h', 'win32_menu_item.cc', diff --git a/src/poly/ui/win32/win32_control.cc b/src/poly/ui/win32/win32_control.cc new file mode 100644 index 000000000..070b00cbb --- /dev/null +++ b/src/poly/ui/win32/win32_control.cc @@ -0,0 +1,314 @@ +/** + ****************************************************************************** + * 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 + +namespace poly { +namespace ui { +namespace win32 { + +LRESULT CALLBACK +Win32Control::WndProcThunk(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { + Win32Control* control = 0; + if (message == WM_NCCREATE) { + auto create_struct = reinterpret_cast(lParam); + control = reinterpret_cast(create_struct->lpCreateParams); + SetWindowLongPtr(hWnd, GWLP_USERDATA, (__int3264)(LONG_PTR)control); + } else { + control = + reinterpret_cast(GetWindowLongPtr(hWnd, GWLP_USERDATA)); + } + if (control) { + return control->WndProc(hWnd, message, wParam, lParam); + } else { + return DefWindowProc(hWnd, message, wParam, lParam); + } +} + +Win32Control::Win32Control(Control* parent, uint32_t flags) + : Control(parent, flags), hwnd_(nullptr) {} + +Win32Control::~Win32Control() { + if (hwnd_) { + CloseWindow(hwnd_); + hwnd_ = nullptr; + } +} + +void Win32Control::OnCreate() { + Control::OnCreate(); + 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::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(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.right - pad_bottom - pad_top, TRUE); +} + +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 Win32Control::WndProc(HWND hWnd, UINT message, WPARAM wParam, + LPARAM lParam) { + if (message >= WM_MOUSEFIRST && message <= WM_MOUSELAST) { + if (HandleMouse(message, wParam, lParam)) { + return 0; + } else { + return DefWindowProc(hWnd, message, wParam, lParam); + } + } else if (message >= WM_KEYFIRST && message <= WM_KEYLAST) { + if (HandleKeyboard(message, wParam, lParam)) { + return 0; + } else { + return DefWindowProc(hWnd, message, wParam, lParam); + } + } + + switch (message) { + case WM_NCCREATE: + hwnd_ = hWnd; + break; + case WM_CREATE: + OnCreate(); + break; + case WM_DESTROY: + OnDestroy(); + break; + + case WM_MOVING: + break; + case WM_MOVE: + break; + case WM_SIZING: + break; + case WM_SIZE: { + 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; + auto e = UIEvent(this); + OnResize(e); + } + break; + } + + case WM_PAINT: + if (flags_ & kFlagOwnPaint) { + PAINTSTRUCT ps; + HDC hdc = BeginPaint(hWnd, &ps); + EndPaint(hWnd, &ps); + } + 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 true; +} + +bool Win32Control::HandleKeyboard(UINT message, WPARAM wParam, LPARAM lParam) { + auto e = KeyEvent(this, (int)wParam); + switch (message) { + case WM_KEYDOWN: + OnKeyDown(e); + return true; + case WM_KEYUP: + OnKeyUp(e); + return true; + default: + return false; + } +} + +} // namespace win32 +} // namespace ui +} // namespace poly diff --git a/src/poly/ui/win32/win32_control.h b/src/poly/ui/win32/win32_control.h new file mode 100644 index 000000000..d1d156808 --- /dev/null +++ b/src/poly/ui/win32/win32_control.h @@ -0,0 +1,62 @@ +/** + ****************************************************************************** + * 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 POLY_UI_WIN32_WIN32_CONTROL_H_ +#define POLY_UI_WIN32_WIN32_CONTROL_H_ + +#include +#include + +#include + +namespace poly { +namespace ui { +namespace win32 { + +class Win32Control : public Control { + public: + ~Win32Control() override; + + HWND hwnd() const { return hwnd_; } + + 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; + + void set_cursor_visible(bool value) override; + void set_enabled(bool value) override; + void set_visible(bool value) override; + void set_focus(bool value) override; + + protected: + Win32Control(Control* parent, uint32_t flags); + + virtual bool CreateHWND() = 0; + + void OnCreate() override; + + static LRESULT CALLBACK + WndProcThunk(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); + virtual LRESULT WndProc(HWND hWnd, UINT message, WPARAM wParam, + LPARAM lParam); + + HWND hwnd_; + + private: + bool HandleMouse(UINT message, WPARAM wParam, LPARAM lParam); + bool HandleKeyboard(UINT message, WPARAM wParam, LPARAM lParam); +}; + +} // namespace win32 +} // namespace ui +} // namespace poly + +#endif // POLY_UI_WIN32_WIN32_CONTROL_H_ diff --git a/src/poly/ui/win32/win32_loop.cc b/src/poly/ui/win32/win32_loop.cc new file mode 100644 index 000000000..4b9883733 --- /dev/null +++ b/src/poly/ui/win32/win32_loop.cc @@ -0,0 +1,72 @@ +/** + ****************************************************************************** + * 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 + +namespace poly { +namespace ui { +namespace win32 { + +const DWORD kWmWin32LoopPost = WM_APP + 0x100; +const DWORD kWmWin32LoopQuit = WM_APP + 0x101; + +class PostedFn { + public: + explicit PostedFn(std::function fn) : fn_(std::move(fn)) {} + void Call() { fn_(); } + + private: + std::function fn_; +}; + +Win32Loop::Win32Loop() : thread_id_(0) { + thread_ = std::thread([this]() { + thread_id_ = GetCurrentThreadId(); + ThreadMain(); + }); +} + +Win32Loop::~Win32Loop() = default; + +void Win32Loop::ThreadMain() { + MSG msg; + while (GetMessage(&msg, nullptr, 0, 0)) { + TranslateMessage(&msg); + DispatchMessage(&msg); + switch (msg.message) { + case kWmWin32LoopPost: + if (msg.wParam == reinterpret_cast(this)) { + auto posted_fn = reinterpret_cast(msg.lParam); + posted_fn->Call(); + delete posted_fn; + } + break; + case kWmWin32LoopQuit: + if (msg.wParam == reinterpret_cast(this)) { + return; + } + break; + } + } +} + +void Win32Loop::Post(std::function fn) { + PostThreadMessage(thread_id_, kWmWin32LoopPost, + reinterpret_cast(this), + reinterpret_cast(new PostedFn(std::move(fn)))); +} + +void Win32Loop::Quit() { + PostThreadMessage(thread_id_, kWmWin32LoopQuit, + reinterpret_cast(this), 0); +} + +} // namespace win32 +} // namespace ui +} // namespace poly diff --git a/src/xenia/ui/win32/win32_loop.h b/src/poly/ui/win32/win32_loop.h similarity index 60% rename from src/xenia/ui/win32/win32_loop.h rename to src/poly/ui/win32/win32_loop.h index 6888c4782..f750e625d 100644 --- a/src/xenia/ui/win32/win32_loop.h +++ b/src/poly/ui/win32/win32_loop.h @@ -7,30 +7,38 @@ ****************************************************************************** */ -#ifndef XENIA_UI_WIN32_WIN32_LOOP_H_ -#define XENIA_UI_WIN32_WIN32_LOOP_H_ +#ifndef POLY_UI_WIN32_WIN32_LOOP_H_ +#define POLY_UI_WIN32_WIN32_LOOP_H_ -#include +#include +#include -#include +#include -namespace xe { +#include + +namespace poly { namespace ui { namespace win32 { -class Win32Loop { +class Win32Loop : public Loop { public: Win32Loop(); ~Win32Loop(); - bool Run(); - void Quit(); + void Post(std::function fn) override; + + void Quit() override; private: + void ThreadMain(); + + std::thread thread_; + DWORD thread_id_; }; } // namespace win32 } // namespace ui -} // namespace xe +} // namespace poly -#endif // XENIA_UI_WIN32_WIN32_LOOP_H_ +#endif // POLY_UI_WIN32_WIN32_LOOP_H_ diff --git a/src/poly/ui/win32/win32_menu_item.cc b/src/poly/ui/win32/win32_menu_item.cc new file mode 100644 index 000000000..4824f989b --- /dev/null +++ b/src/poly/ui/win32/win32_menu_item.cc @@ -0,0 +1,37 @@ +/** + ****************************************************************************** + * 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 + +namespace poly { +namespace ui { +namespace win32 { + +Win32MenuItem::Win32MenuItem(Type type) + : MenuItem(type), handle_(CreateMenu()) {} + +Win32MenuItem::~Win32MenuItem() { + if (handle_) { + DestroyMenu(handle_); + } +} + +void Win32MenuItem::OnChildAdded(MenuItem* generic_child_item) { + auto child_item = static_cast(generic_child_item); + // +} + +void Win32MenuItem::OnChildRemoved(MenuItem* generic_child_item) { + auto child_item = static_cast(generic_child_item); + // +} + +} // namespace win32 +} // namespace ui +} // namespace poly diff --git a/src/xenia/ui/win32/win32_menu_item.h b/src/poly/ui/win32/win32_menu_item.h similarity index 62% rename from src/xenia/ui/win32/win32_menu_item.h rename to src/poly/ui/win32/win32_menu_item.h index adb5d261c..80f9a7150 100644 --- a/src/xenia/ui/win32/win32_menu_item.h +++ b/src/poly/ui/win32/win32_menu_item.h @@ -7,26 +7,34 @@ ****************************************************************************** */ -#ifndef XENIA_UI_WIN32_WIN32_MENU_ITEM_H_ -#define XENIA_UI_WIN32_WIN32_MENU_ITEM_H_ +#ifndef POLY_UI_WIN32_WIN32_MENU_ITEM_H_ +#define POLY_UI_WIN32_WIN32_MENU_ITEM_H_ -#include -#include +#include +#include -namespace xe { +#include + +namespace poly { namespace ui { namespace win32 { class Win32MenuItem : public MenuItem { public: - Win32MenuItem(Window* window); ~Win32MenuItem() override; + protected: + void OnChildAdded(MenuItem* child_item) override; + void OnChildRemoved(MenuItem* child_item) override; + private: + Win32MenuItem(Type type); + + HMENU handle_; }; } // namespace win32 } // namespace ui -} // namespace xe +} // namespace poly -#endif // XENIA_UI_WIN32_WIN32_MENU_ITEM_H_ +#endif // POLY_UI_WIN32_WIN32_MENU_ITEM_H_ diff --git a/src/poly/ui/win32/win32_window.cc b/src/poly/ui/win32/win32_window.cc new file mode 100644 index 000000000..bf62454d6 --- /dev/null +++ b/src/poly/ui/win32/win32_window.cc @@ -0,0 +1,189 @@ +/** + ****************************************************************************** + * 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 + +#include +#include +#include + +#include + +namespace poly { +namespace ui { +namespace win32 { + +Win32Window::Win32Window(const std::wstring& title) + : Window(title), main_menu_(nullptr), closing_(false) {} + +Win32Window::~Win32Window() {} + +bool Win32Window::CreateHWND() { + HINSTANCE hInstance = GetModuleHandle(nullptr); + + WNDCLASSEX wcex; + wcex.cbSize = sizeof(WNDCLASSEX); + wcex.style = 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)) { + PLOGE("RegisterClassEx failed"); + return false; + } + + // Setup initial size. + DWORD window_style = WS_OVERLAPPEDWINDOW; + DWORD window_ex_style = WS_EX_APPWINDOW; + RECT rc = {0, 0, width_, height_}; + AdjustWindowRect(&rc, WS_OVERLAPPEDWINDOW, FALSE); + + // Create window. + hwnd_ = CreateWindowEx(window_ex_style, L"XeniaWindowClass", L"Xenia", + window_style, CW_USEDEFAULT, CW_USEDEFAULT, + rc.right - rc.left, rc.bottom - rc.top, nullptr, + nullptr, hInstance, this); + if (!hwnd_) { + PLOGE("CreateWindow failed"); + return false; + } + + main_menu_ = CreateMenu(); + SetMenu(hwnd_, main_menu_); + + // 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(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; + 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; +} + +// bool Win32Window::SetSize(uint32_t width, uint32_t height) { +// RECT rc = {0, 0, static_cast(width), static_cast(height)}; +// AdjustWindowRect(&rc, WS_OVERLAPPEDWINDOW, FALSE); +// // TODO(benvanik): center? +// MoveWindow(handle_, 0, 0, rc.right - rc.left, rc.bottom - rc.top, TRUE); +// return true; +// } + +void Win32Window::OnClose() { + if (!closing_ && hwnd_) { + closing_ = true; + CloseWindow(hwnd_); + } +} + +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: + switch (LOWORD(wParam)) { + // TODO(benvanik): dispatch to menu. + } + break; + } + + return DefWindowProc(hWnd, message, wParam, lParam); +} + +} // namespace win32 +} // namespace ui +} // namespace poly diff --git a/src/xenia/ui/win32/win32_window.h b/src/poly/ui/win32/win32_window.h similarity index 51% rename from src/xenia/ui/win32/win32_window.h rename to src/poly/ui/win32/win32_window.h index 2dc05d3a2..63a15ddf9 100644 --- a/src/xenia/ui/win32/win32_window.h +++ b/src/poly/ui/win32/win32_window.h @@ -7,47 +7,42 @@ ****************************************************************************** */ -#ifndef XENIA_UI_WIN32_WIN32_WINDOW_H_ -#define XENIA_UI_WIN32_WIN32_WINDOW_H_ +#ifndef POLY_UI_WIN32_WIN32_WINDOW_H_ +#define POLY_UI_WIN32_WIN32_WINDOW_H_ #include -#include -#include +#include +#include -namespace xe { +namespace poly { namespace ui { namespace win32 { -class Win32Window : public Window { +class Win32Window : public Window { public: - Win32Window(); + Win32Window(const std::wstring& title); ~Win32Window() override; - int Initialize(const std::wstring& title, uint32_t width, - uint32_t height) override; - bool set_title(const std::wstring& title) override; - bool set_cursor_visible(bool value) override; - HWND handle() const { return handle_; } - - LRESULT WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); protected: - bool SetSize(uint32_t width, uint32_t height) override; + bool CreateHWND() override; + void OnClose() override; + LRESULT WndProc(HWND hWnd, UINT message, WPARAM wParam, + LPARAM lParam) override; + private: void EnableMMCSS(); - bool HandleMouse(UINT message, WPARAM wParam, LPARAM lParam); - bool HandleKeyboard(UINT message, WPARAM wParam, LPARAM lParam); - HWND handle_; + HMENU main_menu_; bool closing_; }; } // namespace win32 } // namespace ui -} // namespace xe +} // namespace poly -#endif // XENIA_UI_WIN32_WIN32_WINDOW_H_ +#endif // POLY_UI_WIN32_WIN32_WINDOW_H_ diff --git a/src/poly/ui/window.h b/src/poly/ui/window.h new file mode 100644 index 000000000..d8365096d --- /dev/null +++ b/src/poly/ui/window.h @@ -0,0 +1,84 @@ +/** + ****************************************************************************** + * 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 POLY_UI_WINDOW_H_ +#define POLY_UI_WINDOW_H_ + +#include + +#include +#include +#include + +namespace poly { +namespace ui { + +template +class Window : public T { + public: + ~Window() override = default; + + virtual bool Initialize() { return true; } + + const std::wstring& title() const { return title_; } + virtual bool set_title(const std::wstring& title) { + if (title == title_) { + return false; + } + title_ = title; + return true; + } + + void Close() { + auto e = UIEvent(this); + on_closing(e); + + OnClose(); + + e = UIEvent(this); + on_closed(e); + } + + public: + poly::Delegate on_shown; + poly::Delegate on_hidden; + poly::Delegate on_closing; + poly::Delegate on_closed; + + protected: + Window(const std::wstring& title) : T(nullptr, 0), title_(title) {} + + void OnShow() { + if (is_visible_) { + return; + } + is_visible_ = true; + auto e = UIEvent(this); + on_shown(e); + } + + void OnHide() { + if (!is_visible_) { + return; + } + is_visible_ = false; + auto e = UIEvent(this); + on_hidden(e); + } + + virtual void OnClose() {} + + private: + std::wstring title_; +}; + +} // namespace ui +} // namespace poly + +#endif // POLY_UI_WINDOW_H_ diff --git a/src/xenia/emulator.cc b/src/xenia/emulator.cc index ce63e8252..912099163 100644 --- a/src/xenia/emulator.cc +++ b/src/xenia/emulator.cc @@ -20,7 +20,7 @@ #include #include #include -#include +#include namespace xe { @@ -33,7 +33,7 @@ using namespace xe::kernel::fs; using namespace xe::ui; Emulator::Emulator(const std::wstring& command_line) - : command_line_(command_line), main_window_(nullptr) {} + : command_line_(command_line) {} Emulator::~Emulator() { // Note that we delete things in the reverse order they were initialized. @@ -43,10 +43,6 @@ Emulator::~Emulator() { ev->type = xdb::protocol::EventType::PROCESS_EXIT; } - if (main_window_) { - main_window_->Close(); - } - debug_agent_.reset(); xam_.reset(); @@ -66,11 +62,20 @@ Emulator::~Emulator() { processor_.reset(); export_resolver_.reset(); + + // Kill the window last, as until the graphics system/etc is dead it's needed. + main_window_.reset(); } X_STATUS Emulator::Setup() { X_STATUS result = X_STATUS_UNSUCCESSFUL; + // Create the main window. Other parts will hook into this. + main_window_ = std::make_unique(); + if (!main_window_->Initialize()) { + return result; + } + debug_agent_.reset(new DebugAgent(this)); result = debug_agent_->Initialize(); if (result) { @@ -141,16 +146,6 @@ X_STATUS Emulator::Setup() { return result; } -void Emulator::set_main_window(Window* window) { - assert_null(main_window_); - main_window_ = window; - - window->closed.AddListener([](UIEvent& e) { - // TODO(benvanik): call module API to kill? this is a bad shutdown. - exit(1); - }); -} - X_STATUS Emulator::LaunchXexFile(const std::wstring& path) { // We create a virtual filesystem pointing to its directory and symlink // that to the game filesystem. diff --git a/src/xenia/emulator.h b/src/xenia/emulator.h index 9a5891800..4a71c2971 100644 --- a/src/xenia/emulator.h +++ b/src/xenia/emulator.h @@ -37,7 +37,7 @@ class XamModule; class XboxkrnlModule; } // namespace kernel namespace ui { -class Window; +class MainWindow; } // namespace ui } // namespace xe @@ -52,8 +52,7 @@ class Emulator { const std::wstring& command_line() const { return command_line_; } - ui::Window* main_window() const { return main_window_; } - void set_main_window(ui::Window* window); + ui::MainWindow* main_window() const { return main_window_.get(); } Memory* memory() const { return memory_.get(); } @@ -85,8 +84,7 @@ class Emulator { std::wstring command_line_; - // TODO(benvanik): remove from here? - ui::Window* main_window_; + std::unique_ptr main_window_; std::unique_ptr memory_; diff --git a/src/xenia/ui/win32/win32_menu_item.cc b/src/xenia/ui/main_window.cc similarity index 57% rename from src/xenia/ui/win32/win32_menu_item.cc rename to src/xenia/ui/main_window.cc index 9ce673acc..235c4c557 100644 --- a/src/xenia/ui/win32/win32_menu_item.cc +++ b/src/xenia/ui/main_window.cc @@ -7,16 +7,33 @@ ****************************************************************************** */ -#include +#include + +#include namespace xe { namespace ui { -namespace win32 { -Win32MenuItem::Win32MenuItem(Window* window) : MenuItem(window) {} +MainWindow::MainWindow() : PlatformWindow(L"xenia") {} -Win32MenuItem::~Win32MenuItem() {} +MainWindow::~MainWindow() {} + +void MainWindow::Start() { + loop_.Post([this]() { + if (!Initialize()) { + PFATAL("Failed to initialize main window"); + exit(1); + } + }); +} + +bool MainWindow::Initialize() { + if (!Window::Initialize()) { + return false; + } + // + return true; +} -} // namespace win32 } // namespace ui } // namespace xe diff --git a/src/xenia/ui/menu_item.h b/src/xenia/ui/main_window.h similarity index 53% rename from src/xenia/ui/menu_item.h rename to src/xenia/ui/main_window.h index 06264c7b1..49b75a0f4 100644 --- a/src/xenia/ui/menu_item.h +++ b/src/xenia/ui/main_window.h @@ -7,36 +7,37 @@ ****************************************************************************** */ -#ifndef XENIA_UI_MENU_ITEM_H_ -#define XENIA_UI_MENU_ITEM_H_ +#ifndef XENIA_UI_MAIN_WINDOW_H_ +#define XENIA_UI_MAIN_WINDOW_H_ -#include -#include +#include -#include +// TODO(benvanik): only on windows. +#include +#include namespace xe { namespace ui { -class Window; +using PlatformLoop = poly::ui::win32::Win32Loop; +using PlatformWindow = poly::ui::win32::Win32Window; -class MenuItem { +class MainWindow : public PlatformWindow { public: - MenuItem(Window* window); - virtual ~MenuItem(); + MainWindow(); + ~MainWindow(); - Window* window() const { return window_; } - MenuItem* parent_item() const { return parent_item_; } + PlatformLoop* loop() { return &loop_; } - virtual void AddChild(std::unique_ptr child_item); + void Start(); private: - Window* window_; - MenuItem* parent_item_; - std::vector> children_; + bool Initialize(); + + PlatformLoop loop_; }; } // namespace ui } // namespace xe -#endif // XENIA_UI_MENU_ITEM_H_ +#endif // XENIA_UI_MAIN_WINDOW_H_ diff --git a/src/xenia/ui/sources.gypi b/src/xenia/ui/sources.gypi index b0e8905f6..f2c3a3d99 100644 --- a/src/xenia/ui/sources.gypi +++ b/src/xenia/ui/sources.gypi @@ -1,18 +1,7 @@ # Copyright 2014 Ben Vanik. All Rights Reserved. { 'sources': [ - 'menu_item.cc', - 'menu_item.h', - 'ui_event.h', - 'window.cc', - 'window.h', - ], - - 'conditions': [ - ['OS == "win"', { - 'includes': [ - 'win32/sources.gypi', - ], - }], + 'main_window.cc', + 'main_window.h', ], } diff --git a/src/xenia/ui/win32/win32_loop.cc b/src/xenia/ui/win32/win32_loop.cc deleted file mode 100644 index 907165efb..000000000 --- a/src/xenia/ui/win32/win32_loop.cc +++ /dev/null @@ -1,44 +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 - -namespace xe { -namespace ui { -namespace win32 { - -const DWORD kWmWin32LoopQuit = WM_APP + 0x100; - -Win32Loop::Win32Loop() {} - -Win32Loop::~Win32Loop() {} - -bool Win32Loop::Run() { - MSG msg; - while (GetMessage(&msg, nullptr, 0, 0)) { - TranslateMessage(&msg); - DispatchMessage(&msg); - switch (msg.message) { - case kWmWin32LoopQuit: - if (msg.wParam == reinterpret_cast(this)) { - return true; - } - break; - } - } - return true; -} - -void Win32Loop::Quit() { - PostMessage(nullptr, kWmWin32LoopQuit, reinterpret_cast(this), 0); -} - -} // namespace win32 -} // namespace ui -} // namespace xe diff --git a/src/xenia/ui/win32/win32_window.cc b/src/xenia/ui/win32/win32_window.cc deleted file mode 100644 index 9e084b679..000000000 --- a/src/xenia/ui/win32/win32_window.cc +++ /dev/null @@ -1,352 +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 - -#include -#include -#include - -namespace xe { -namespace ui { -namespace win32 { - -static LRESULT CALLBACK -Win32WindowWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { - Win32Window* window = 0; - if (message == WM_NCCREATE) { - LPCREATESTRUCT create_struct = (LPCREATESTRUCT)lParam; - window = (Win32Window*)create_struct->lpCreateParams; - SetWindowLongPtr(hWnd, GWLP_USERDATA, (__int3264)(LONG_PTR)window); - } else { - window = (Win32Window*)GetWindowLongPtr(hWnd, GWLP_USERDATA); - } - if (window) { - return window->WndProc(hWnd, message, wParam, lParam); - } else { - return DefWindowProc(hWnd, message, wParam, lParam); - } -} - -Win32Window::Win32Window() : handle_(0), closing_(false) {} - -Win32Window::~Win32Window() { - if (handle_) { - CloseWindow(handle_); - handle_ = nullptr; - } -} - -int Win32Window::Initialize(const std::wstring& title, uint32_t width, - uint32_t height) { - int result = Window::Initialize(title, width, height); - if (result) { - return result; - } - - HINSTANCE hInstance = GetModuleHandle(NULL); - - WNDCLASSEX wcex; - wcex.cbSize = sizeof(WNDCLASSEX); - wcex.style = CS_OWNDC; - wcex.lpfnWndProc = Win32WindowWndProc; - wcex.cbClsExtra = 0; - wcex.cbWndExtra = 0; - wcex.hInstance = hInstance; - wcex.hIcon = NULL; // LoadIcon(hInstance, (LPCTSTR)IDI_TUTORIAL1); - wcex.hIconSm = NULL; // LoadIcon(hInstance, (LPCTSTR)IDI_TUTORIAL1); - wcex.hCursor = LoadCursor(NULL, IDC_ARROW); - wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); - wcex.lpszMenuName = NULL; - wcex.lpszClassName = L"XeniaWindowClass"; - if (!RegisterClassEx(&wcex)) { - XELOGE("RegisterClassEx failed"); - return 1; - } - - // Setup initial size. - DWORD window_style = WS_OVERLAPPEDWINDOW; - DWORD window_ex_style = WS_EX_APPWINDOW; - RECT rc = {0, 0, static_cast(width), static_cast(height)}; - AdjustWindowRect(&rc, WS_OVERLAPPEDWINDOW, FALSE); - - // Create window. - handle_ = CreateWindowEx(window_ex_style, L"XeniaWindowClass", L"Xenia", - window_style, CW_USEDEFAULT, CW_USEDEFAULT, - rc.right - rc.left, rc.bottom - rc.top, NULL, NULL, - hInstance, this); - if (!handle_) { - XELOGE("CreateWindow failed"); - return 1; - } - - // 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(handle_, L"MicrosoftTabletPenServiceProperty", - reinterpret_cast(dwHwndTabletProperty)); - GlobalDeleteAtom(atom); - - // Enable DWM elevation. - EnableMMCSS(); - - ShowWindow(handle_, SW_SHOWNORMAL); - UpdateWindow(handle_); - - 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; - 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(handle_, &pp); - } - - FreeLibrary(hLibrary); -} - -bool Win32Window::set_title(const std::wstring& title) { - if (!Window::set_title(title)) { - return false; - } - SetWindowText(handle_, title.c_str()); - return true; -} - -bool Win32Window::set_cursor_visible(bool value) { - if (!Window::set_cursor_visible(value)) { - return false; - } - if (value) { - ShowCursor(TRUE); - SetCursor(NULL); - } else { - ShowCursor(FALSE); - } - return true; -} - -bool Win32Window::SetSize(uint32_t width, uint32_t height) { - RECT rc = {0, 0, static_cast(width), static_cast(height)}; - AdjustWindowRect(&rc, WS_OVERLAPPEDWINDOW, FALSE); - // TODO(benvanik): center? - MoveWindow(handle_, 0, 0, rc.right - rc.left, rc.bottom - rc.top, TRUE); - return true; -} - -void Win32Window::OnClose() { - if (!closing_ && handle_) { - closing_ = true; - CloseWindow(handle_); - handle_ = NULL; - } -} - -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(handle_, &pt); - x = pt.x; - y = pt.y; - } - - MouseEvent::Button button = MouseEvent::MOUSE_BUTTON_NONE; - int32_t dx = 0; - int32_t dy = 0; - switch (message) { - case WM_LBUTTONDOWN: - case WM_LBUTTONUP: - button = MouseEvent::MOUSE_BUTTON_LEFT; - break; - case WM_RBUTTONDOWN: - case WM_RBUTTONUP: - button = MouseEvent::MOUSE_BUTTON_RIGHT; - break; - case WM_MBUTTONDOWN: - case WM_MBUTTONUP: - button = MouseEvent::MOUSE_BUTTON_MIDDLE; - break; - case WM_XBUTTONDOWN: - case WM_XBUTTONUP: - switch (GET_XBUTTON_WPARAM(wParam)) { - case XBUTTON1: - button = MouseEvent::MOUSE_BUTTON_X1; - break; - case XBUTTON2: - button = MouseEvent::MOUSE_BUTTON_X2; - break; - default: - return false; - } - break; - case WM_MOUSEMOVE: - button = MouseEvent::MOUSE_BUTTON_NONE; - break; - case WM_MOUSEWHEEL: - button = MouseEvent::MOUSE_BUTTON_NONE; - 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: - mouse_down(e); - break; - case WM_LBUTTONUP: - case WM_RBUTTONUP: - case WM_MBUTTONUP: - case WM_XBUTTONUP: - mouse_up(e); - break; - case WM_MOUSEMOVE: - mouse_move(e); - break; - case WM_MOUSEWHEEL: - mouse_wheel(e); - break; - } - return true; -} - -bool Win32Window::HandleKeyboard(UINT message, WPARAM wParam, LPARAM lParam) { - auto e = KeyEvent(this, (int)wParam); - switch (message) { - case WM_KEYDOWN: - key_down(e); - return true; - case WM_KEYUP: - key_up(e); - return true; - default: - return false; - } -} - -LRESULT Win32Window::WndProc(HWND hWnd, UINT message, WPARAM wParam, - LPARAM lParam) { - if (message >= WM_MOUSEFIRST && message <= WM_MOUSELAST) { - if (HandleMouse(message, wParam, lParam)) { - return 0; - } else { - return DefWindowProc(hWnd, message, wParam, lParam); - } - } else if (message >= WM_KEYFIRST && message <= WM_KEYLAST) { - if (HandleKeyboard(message, wParam, lParam)) { - return 0; - } else { - return DefWindowProc(hWnd, message, wParam, lParam); - } - } - - switch (message) { - case WM_NCCREATE: - handle_ = hWnd; - return DefWindowProc(hWnd, message, wParam, lParam); - case WM_ACTIVATEAPP: - if (wParam) { - // Made active. - OnShow(); - } else { - // Made inactive. - OnHide(); - } - return DefWindowProc(hWnd, message, wParam, lParam); - case WM_CLOSE: - closing_ = true; - Close(); - return DefWindowProc(hWnd, message, wParam, lParam); - case WM_DESTROY: - return 0; - - case WM_PAINT: { - PAINTSTRUCT ps; - HDC hdc = BeginPaint(hWnd, &ps); - EndPaint(hWnd, &ps); - } - return 0; - case WM_ERASEBKGND: - return 0; // ignore - case WM_DISPLAYCHANGE: - return DefWindowProc(hWnd, message, wParam, lParam); - - case WM_MOVE: - return DefWindowProc(hWnd, message, wParam, lParam); - case WM_SIZING: - BeginResizing(); - return DefWindowProc(hWnd, message, wParam, lParam); - case WM_SIZE: { - RECT frame; - GetClientRect(handle_, &frame); - OnResize(frame.right - frame.left, frame.bottom - frame.top); - EndResizing(); - } - return DefWindowProc(hWnd, message, wParam, lParam); - - 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; - default: - return DefWindowProc(hWnd, message, wParam, lParam); - } -} - -} // namespace win32 -} // namespace ui -} // namespace xe diff --git a/src/xenia/ui/window.cc b/src/xenia/ui/window.cc deleted file mode 100644 index 568388f44..000000000 --- a/src/xenia/ui/window.cc +++ /dev/null @@ -1,114 +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 - -namespace xe { -namespace ui { - -Window::Window() - : title_(L"Window"), - is_visible_(true), - is_cursor_visible_(true), - width_(0), - height_(0) {} - -Window::~Window() {} - -int Window::Initialize(const std::wstring& title, uint32_t width, - uint32_t height) { - title_ = title; - width_ = width; - height_ = height; - return 0; -} - -bool Window::set_title(const std::wstring& title) { - if (title == title_) { - return false; - } - title_ = title; - return true; -} - -bool Window::set_cursor_visible(bool value) { - if (value == is_cursor_visible_) { - return false; - } - is_cursor_visible_ = value; - return true; -} - -void Window::OnShow() { - if (is_visible_) { - return; - } - is_visible_ = true; - auto e = UIEvent(this); - shown(e); -} - -void Window::OnHide() { - if (!is_visible_) { - return; - } - is_visible_ = false; - auto e = UIEvent(this); - hidden(e); -} - -void Window::Resize(uint32_t width, uint32_t height) { - BeginResizing(); - SetSize(width, height); - OnResize(width, height); // redundant? - EndResizing(); -} - -void Window::BeginResizing() { - if (resizing_) { - return; - } - resizing_ = true; - auto e = UIEvent(this); - resizing(e); -} - -bool Window::OnResize(uint32_t width, uint32_t height) { - if (!resizing_) { - BeginResizing(); - } - if (width == width_ && height == height_) { - return false; - } - width_ = width; - height_ = height; - return true; -} - -void Window::EndResizing() { - assert_true(resizing_); - resizing_ = false; - auto e = UIEvent(this); - resized(e); -} - -void Window::Close() { - auto e = UIEvent(this); - closing(e); - - OnClose(); - - e = UIEvent(this); - closed(e); -} - -void Window::OnClose() {} - -} // namespace ui -} // namespace xe diff --git a/src/xenia/ui/window.h b/src/xenia/ui/window.h deleted file mode 100644 index 9519f33d3..000000000 --- a/src/xenia/ui/window.h +++ /dev/null @@ -1,80 +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_WINDOW_H_ -#define XENIA_UI_WINDOW_H_ - -#include - -#include -#include -#include - -namespace xe { -namespace ui { - -class Window { - public: - Window(); - virtual ~Window(); - - virtual int Initialize(const std::wstring& title, uint32_t width, - uint32_t height); - - const std::wstring& title() const { return title_; } - virtual bool set_title(const std::wstring& title); - bool is_visible() const { return is_visible_; } - bool is_cursor_visible() const { return is_cursor_visible_; } - virtual bool set_cursor_visible(bool value); - uint32_t width() const { return width_; } - uint32_t height() const { return height_; } - void Resize(uint32_t width, uint32_t height); - - void Close(); - - public: - poly::Delegate shown; - poly::Delegate hidden; - poly::Delegate resizing; - poly::Delegate resized; - poly::Delegate closing; - poly::Delegate closed; - - poly::Delegate key_down; - poly::Delegate key_up; - - poly::Delegate mouse_down; - poly::Delegate mouse_move; - poly::Delegate mouse_up; - poly::Delegate mouse_wheel; - - protected: - void OnShow(); - void OnHide(); - - void BeginResizing(); - virtual bool OnResize(uint32_t width, uint32_t height); - void EndResizing(); - virtual bool SetSize(uint32_t width, uint32_t height) = 0; - - virtual void OnClose(); - - private: - std::wstring title_; - bool is_visible_; - bool is_cursor_visible_; - bool resizing_; - uint32_t width_; - uint32_t height_; -}; - -} // namespace ui -} // namespace xe - -#endif // XENIA_UI_WINDOW_H_