UI hacking.

This commit is contained in:
Ben Vanik
2014-12-20 13:54:55 -08:00
parent c1df273600
commit d839359b4a
28 changed files with 1235 additions and 714 deletions

View File

@@ -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

View File

@@ -52,5 +52,6 @@
],
'includes': [
'ui/sources.gypi',
],
}

75
src/poly/ui/control.cc Normal file
View File

@@ -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 <poly/ui/control.h>
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<Control> 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

120
src/poly/ui/control.h Normal file
View File

@@ -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 <memory>
#include <vector>
#include <poly/delegate.h>
#include <poly/ui/ui_event.h>
namespace poly {
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_; }
void AddChild(Control* child_control);
void AddChild(std::unique_ptr<Control> 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<UIEvent> on_resize;
poly::Delegate<UIEvent> on_visible;
poly::Delegate<UIEvent> on_hidden;
poly::Delegate<UIEvent> on_got_focus;
poly::Delegate<UIEvent> on_lost_focus;
poly::Delegate<KeyEvent> on_key_down;
poly::Delegate<KeyEvent> on_key_up;
poly::Delegate<MouseEvent> on_mouse_down;
poly::Delegate<MouseEvent> on_mouse_move;
poly::Delegate<MouseEvent> on_mouse_up;
poly::Delegate<MouseEvent> 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<ControlPtr> 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_

31
src/poly/ui/loop.h Normal file
View File

@@ -0,0 +1,31 @@
/**
******************************************************************************
* 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_LOOP_H_
#define POLY_UI_LOOP_H_
#include <functional>
namespace poly {
namespace ui {
class Loop {
public:
Loop() = default;
virtual ~Loop() = default;
virtual void Post(std::function<void()> fn) = 0;
virtual void Quit() = 0;
};
} // namespace ui
} // namespace poly
#endif // POLY_UI_LOOP_H_

49
src/poly/ui/menu_item.cc Normal file
View File

@@ -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 <poly/ui/menu_item.h>
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<MenuItem> 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

61
src/poly/ui/menu_item.h Normal file
View File

@@ -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 <memory>
#include <vector>
#include <poly/delegate.h>
#include <poly/ui/ui_event.h>
namespace poly {
namespace ui {
class Window;
class MenuItem {
public:
typedef std::unique_ptr<MenuItem, void (*)(MenuItem*)> 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<MenuItem> child_item);
void AddChild(MenuItemPtr child_item);
void RemoveChild(MenuItem* child_item);
poly::Delegate<UIEvent> 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<MenuItemPtr> children_;
};
} // namespace ui
} // namespace poly
#endif // POLY_UI_MENU_ITEM_H_

20
src/poly/ui/sources.gypi Normal file
View File

@@ -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',
],
}],
],
}

75
src/poly/ui/ui_event.h Normal file
View File

@@ -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. *
******************************************************************************
*/
#ifndef POLY_UI_UI_EVENT_H_
#define POLY_UI_UI_EVENT_H_
namespace poly {
namespace ui {
class Control;
class UIEvent {
public:
UIEvent(Control* control = nullptr) : control_(control) {}
virtual ~UIEvent() = default;
Control* control() const { return control_; }
private:
Control* control_;
};
class KeyEvent : public UIEvent {
public:
KeyEvent(Control* control, int key_code)
: UIEvent(control), key_code_(key_code) {}
~KeyEvent() override = default;
int key_code() const { return key_code_; }
private:
int key_code_;
};
class MouseEvent : public UIEvent {
public:
enum class Button {
kNone = 0,
kLeft,
kRight,
kMiddle,
kX1,
kX2,
};
public:
MouseEvent(Control* control, Button button, int32_t x, int32_t y,
int32_t dx = 0, int32_t dy = 0)
: UIEvent(control), button_(button), x_(x), y_(y), dx_(dx), dy_(dy) {}
~MouseEvent() override = default;
Button button() const { return button_; }
int32_t x() const { return x_; }
int32_t y() const { return y_; }
int32_t dx() const { return dx_; }
int32_t dy() const { return dy_; }
private:
Button button_;
int32_t x_;
int32_t y_;
int32_t dx_;
int32_t dy_;
};
} // namespace ui
} // namespace poly
#endif // POLY_UI_UI_EVENT_H_

View File

@@ -0,0 +1,13 @@
# Copyright 2014 Ben Vanik. All Rights Reserved.
{
'sources': [
'win32_control.cc',
'win32_control.h',
'win32_loop.cc',
'win32_loop.h',
'win32_menu_item.cc',
'win32_menu_item.h',
'win32_window.cc',
'win32_window.h',
],
}

View File

@@ -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 <poly/ui/win32/win32_control.h>
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<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);
}
}
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<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.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

View File

@@ -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 <windows.h>
#include <windowsx.h>
#include <poly/ui/control.h>
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_

View File

@@ -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 <poly/ui/win32/win32_loop.h>
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<void()> fn) : fn_(std::move(fn)) {}
void Call() { fn_(); }
private:
std::function<void()> 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<WPARAM>(this)) {
auto posted_fn = reinterpret_cast<PostedFn*>(msg.lParam);
posted_fn->Call();
delete posted_fn;
}
break;
case kWmWin32LoopQuit:
if (msg.wParam == reinterpret_cast<WPARAM>(this)) {
return;
}
break;
}
}
}
void Win32Loop::Post(std::function<void()> fn) {
PostThreadMessage(thread_id_, kWmWin32LoopPost,
reinterpret_cast<WPARAM>(this),
reinterpret_cast<LPARAM>(new PostedFn(std::move(fn))));
}
void Win32Loop::Quit() {
PostThreadMessage(thread_id_, kWmWin32LoopQuit,
reinterpret_cast<WPARAM>(this), 0);
}
} // namespace win32
} // namespace ui
} // namespace poly

View File

@@ -0,0 +1,44 @@
/**
******************************************************************************
* 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_LOOP_H_
#define POLY_UI_WIN32_WIN32_LOOP_H_
#include <windows.h>
#include <windowsx.h>
#include <thread>
#include <poly/ui/loop.h>
namespace poly {
namespace ui {
namespace win32 {
class Win32Loop : public Loop {
public:
Win32Loop();
~Win32Loop();
void Post(std::function<void()> fn) override;
void Quit() override;
private:
void ThreadMain();
std::thread thread_;
DWORD thread_id_;
};
} // namespace win32
} // namespace ui
} // namespace poly
#endif // POLY_UI_WIN32_WIN32_LOOP_H_

View File

@@ -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 <poly/ui/win32/win32_menu_item.h>
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<Win32MenuItem*>(generic_child_item);
//
}
void Win32MenuItem::OnChildRemoved(MenuItem* generic_child_item) {
auto child_item = static_cast<Win32MenuItem*>(generic_child_item);
//
}
} // namespace win32
} // namespace ui
} // namespace poly

View File

@@ -0,0 +1,40 @@
/**
******************************************************************************
* 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_MENU_ITEM_H_
#define POLY_UI_WIN32_WIN32_MENU_ITEM_H_
#include <windows.h>
#include <windowsx.h>
#include <poly/ui/menu_item.h>
namespace poly {
namespace ui {
namespace win32 {
class Win32MenuItem : public MenuItem {
public:
~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 poly
#endif // POLY_UI_WIN32_WIN32_MENU_ITEM_H_

View File

@@ -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 <poly/ui/win32/win32_window.h>
#include <dwmapi.h>
#include <tpcshrd.h>
#include <windowsx.h>
#include <poly/logging.h>
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<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;
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<LONG>(width), static_cast<LONG>(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

View File

@@ -0,0 +1,48 @@
/**
******************************************************************************
* 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_WINDOW_H_
#define POLY_UI_WIN32_WIN32_WINDOW_H_
#include <string>
#include <poly/ui/win32/win32_control.h>
#include <poly/ui/window.h>
namespace poly {
namespace ui {
namespace win32 {
class Win32Window : public Window<Win32Control> {
public:
Win32Window(const std::wstring& title);
~Win32Window() override;
bool set_title(const std::wstring& title) override;
protected:
bool CreateHWND() override;
void OnClose() override;
LRESULT WndProc(HWND hWnd, UINT message, WPARAM wParam,
LPARAM lParam) override;
private:
void EnableMMCSS();
HMENU main_menu_;
bool closing_;
};
} // namespace win32
} // namespace ui
} // namespace poly
#endif // POLY_UI_WIN32_WIN32_WINDOW_H_

84
src/poly/ui/window.h Normal file
View File

@@ -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 <string>
#include <poly/delegate.h>
#include <poly/ui/control.h>
#include <poly/ui/ui_event.h>
namespace poly {
namespace ui {
template <typename T>
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<UIEvent> on_shown;
poly::Delegate<UIEvent> on_hidden;
poly::Delegate<UIEvent> on_closing;
poly::Delegate<UIEvent> 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_