aboutsummaryrefslogtreecommitdiff
path: root/src/platform_win
diff options
context:
space:
mode:
Diffstat (limited to 'src/platform_win')
-rw-r--r--src/platform_win/CMakeLists.txt10
-rw-r--r--src/platform_win/debug.cpp10
-rw-r--r--src/platform_win/exception.cpp50
-rw-r--r--src/platform_win/god_window.cpp70
-rw-r--r--src/platform_win/god_window_message.hpp6
-rw-r--r--src/platform_win/timer.cpp28
-rw-r--r--src/platform_win/timer.hpp34
-rw-r--r--src/platform_win/win_application.cpp70
-rw-r--r--src/platform_win/window_class.cpp28
9 files changed, 306 insertions, 0 deletions
diff --git a/src/platform_win/CMakeLists.txt b/src/platform_win/CMakeLists.txt
new file mode 100644
index 00000000..fbcf1c00
--- /dev/null
+++ b/src/platform_win/CMakeLists.txt
@@ -0,0 +1,10 @@
+add_library(cru_platform_win STATIC
+ debug.cpp
+ exception.cpp
+ god_window.cpp
+ timer.cpp
+ win_application.cpp
+ window_class.cpp)
+target_include_directories(cru_platform_win PUBLIC ${PROJECT_SOURCE_DIR}/include .)
+target_link_libraries(cru_platform_win PRIVATE D3D11 D2d1 DWrite)
+target_compile_definitions(cru_platform_win PUBLIC UNICODE _UNICODE) # use unicode
diff --git a/src/platform_win/debug.cpp b/src/platform_win/debug.cpp
new file mode 100644
index 00000000..cdff7963
--- /dev/null
+++ b/src/platform_win/debug.cpp
@@ -0,0 +1,10 @@
+#include "cru/platform/win/win_pre_config.hpp"
+
+#include "cru/platform/debug.hpp"
+
+
+namespace cru::debug {
+void DebugMessage(const std::wstring_view& message) {
+ ::OutputDebugStringW(message.data());
+}
+} // namespace cru::debug
diff --git a/src/platform_win/exception.cpp b/src/platform_win/exception.cpp
new file mode 100644
index 00000000..3db88b8b
--- /dev/null
+++ b/src/platform_win/exception.cpp
@@ -0,0 +1,50 @@
+#include "cru/platform/win/exception.hpp"
+
+#include "cru/common/format.hpp"
+
+namespace cru::platform::win {
+using util::Format;
+
+inline std::string HResultMakeMessage(HRESULT h_result,
+ const std::string_view* message) {
+ char buffer[10];
+ sprintf_s(buffer, "%#08x", h_result);
+
+ if (message)
+ return Format(
+ "An HResultError is thrown. HRESULT: {}.\nAdditional message: {}\n",
+ buffer, *message);
+ else
+ return Format("An HResultError is thrown. HRESULT: {}.\n", buffer);
+}
+
+HResultError::HResultError(HRESULT h_result)
+ : runtime_error(HResultMakeMessage(h_result, nullptr)),
+ h_result_(h_result) {}
+
+HResultError::HResultError(HRESULT h_result,
+ const std::string_view& additional_message)
+ : runtime_error(HResultMakeMessage(h_result, &additional_message)),
+ h_result_(h_result) {}
+
+inline std::string Win32MakeMessage(DWORD error_code,
+ const std::string_view* message) {
+ char buffer[10];
+ sprintf_s(buffer, "%#04x", error_code);
+
+ if (message)
+ return Format("Last error code: {}.\nAdditional message: {}\n", buffer,
+ *message);
+ else
+ return Format("Last error code: {}.\n", buffer);
+}
+
+Win32Error::Win32Error(DWORD error_code)
+ : runtime_error(Win32MakeMessage(error_code, nullptr)),
+ error_code_(error_code) {}
+
+Win32Error::Win32Error(DWORD error_code,
+ const std::string_view& additional_message)
+ : runtime_error(Win32MakeMessage(error_code, &additional_message)),
+ error_code_(error_code) {}
+} // namespace cru::platform::win
diff --git a/src/platform_win/god_window.cpp b/src/platform_win/god_window.cpp
new file mode 100644
index 00000000..2b4fbe48
--- /dev/null
+++ b/src/platform_win/god_window.cpp
@@ -0,0 +1,70 @@
+#include "cru/platform/win/god_window.hpp"
+
+#include "cru/platform/win/exception.hpp"
+#include "cru/platform/win/win_application.hpp"
+#include "cru/platform/win/window_class.hpp"
+#include "god_window_message.hpp"
+#include "timer.hpp"
+
+namespace cru::platform::win {
+constexpr auto god_window_class_name = L"GodWindowClass";
+
+LRESULT CALLBACK GodWndProc(HWND hWnd, UINT uMsg, WPARAM wParam,
+ LPARAM lParam) {
+ const auto app = WinApplication::GetInstance();
+
+ if (app) {
+ const auto result =
+ app->GetGodWindow()->HandleGodWindowMessage(hWnd, uMsg, wParam, lParam);
+ if (result.has_value())
+ return result.value();
+ else
+ return DefWindowProcW(hWnd, uMsg, wParam, lParam);
+ } else
+ return DefWindowProcW(hWnd, uMsg, wParam, lParam);
+}
+
+GodWindow::GodWindow(WinApplication* application) {
+ application_ = application;
+
+ const auto h_instance = application->GetInstanceHandle();
+
+ god_window_class_ = std::make_shared<WindowClass>(god_window_class_name,
+ GodWndProc, h_instance);
+
+ hwnd_ = CreateWindowEx(0, god_window_class_name, L"", 0, CW_USEDEFAULT,
+ CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
+ HWND_MESSAGE, nullptr, h_instance, nullptr);
+
+ if (hwnd_ == nullptr) throw Win32Error(::GetLastError(), "Failed to create god window.");
+}
+
+GodWindow::~GodWindow() { ::DestroyWindow(hwnd_); }
+
+std::optional<LRESULT> GodWindow::HandleGodWindowMessage(HWND hwnd, int msg,
+ WPARAM w_param,
+ LPARAM l_param) {
+ switch (msg) {
+ case invoke_later_message_id: {
+ const auto p_action = reinterpret_cast<std::function<void()>*>(w_param);
+ (*p_action)();
+ delete p_action;
+ return 0;
+ }
+ case WM_TIMER: {
+ const auto id = static_cast<UINT_PTR>(w_param);
+ const auto action = application_->GetTimerManager()->GetAction(id);
+ if (action.has_value()) {
+ (action.value().second)();
+ if (!action.value().first)
+ application_->GetTimerManager()->KillTimer(id);
+ return 0;
+ }
+ break;
+ }
+ default:
+ return std::nullopt;
+ }
+ return std::nullopt;
+}
+} // namespace cru::platform::win \ No newline at end of file
diff --git a/src/platform_win/god_window_message.hpp b/src/platform_win/god_window_message.hpp
new file mode 100644
index 00000000..a906a3b7
--- /dev/null
+++ b/src/platform_win/god_window_message.hpp
@@ -0,0 +1,6 @@
+#pragma once
+#include "cru/platform/win/win_pre_config.hpp"
+
+namespace cru::platform::win {
+constexpr int invoke_later_message_id = WM_USER + 2000;
+}
diff --git a/src/platform_win/timer.cpp b/src/platform_win/timer.cpp
new file mode 100644
index 00000000..280d1aed
--- /dev/null
+++ b/src/platform_win/timer.cpp
@@ -0,0 +1,28 @@
+#include "timer.hpp"
+
+namespace cru::platform::win {
+TimerManager::TimerManager(GodWindow* god_window) { god_window_ = god_window; }
+
+UINT_PTR TimerManager::CreateTimer(const UINT milliseconds, const bool loop,
+ const TimerAction& action) {
+ const auto id = current_count_++;
+ ::SetTimer(god_window_->GetHandle(), id, milliseconds, nullptr);
+ map_.emplace(id, std::make_pair(loop, action));
+ return id;
+}
+
+void TimerManager::KillTimer(const UINT_PTR id) {
+ const auto find_result = map_.find(id);
+ if (find_result != map_.cend()) {
+ ::KillTimer(god_window_->GetHandle(), id);
+ map_.erase(find_result);
+ }
+}
+
+std::optional<std::pair<bool, TimerAction>> TimerManager::GetAction(
+ const UINT_PTR id) {
+ const auto find_result = map_.find(id);
+ if (find_result == map_.cend()) return std::nullopt;
+ return find_result->second;
+}
+} // namespace cru::platform::win
diff --git a/src/platform_win/timer.hpp b/src/platform_win/timer.hpp
new file mode 100644
index 00000000..95468b8d
--- /dev/null
+++ b/src/platform_win/timer.hpp
@@ -0,0 +1,34 @@
+#pragma once
+#include "cru/platform/win/win_pre_config.hpp"
+
+#include <chrono>
+#include <functional>
+#include <map>
+#include <optional>
+
+#include "cru/common/base.hpp"
+#include "cru/platform/win/god_window.hpp"
+
+namespace cru::platform::win {
+using TimerAction = std::function<void()>;
+
+class TimerManager : public Object {
+ public:
+ TimerManager(GodWindow* god_window);
+ TimerManager(const TimerManager& other) = delete;
+ TimerManager(TimerManager&& other) = delete;
+ TimerManager& operator=(const TimerManager& other) = delete;
+ TimerManager& operator=(TimerManager&& other) = delete;
+ ~TimerManager() override = default;
+
+ UINT_PTR CreateTimer(UINT milliseconds, bool loop, const TimerAction& action);
+ void KillTimer(UINT_PTR id);
+ std::optional<std::pair<bool, TimerAction>> GetAction(UINT_PTR id);
+
+ private:
+ GodWindow* god_window_;
+
+ std::map<UINT_PTR, std::pair<bool, TimerAction>> map_{};
+ UINT_PTR current_count_ = 0;
+};
+} // namespace cru::platform::win
diff --git a/src/platform_win/win_application.cpp b/src/platform_win/win_application.cpp
new file mode 100644
index 00000000..a90f509c
--- /dev/null
+++ b/src/platform_win/win_application.cpp
@@ -0,0 +1,70 @@
+#include "cru/platform/win/win_application.hpp"
+
+#include <VersionHelpers.h>
+
+#include "cru/platform/win/exception.hpp"
+#include "cru/platform/win/god_window.hpp"
+#include "god_window_message.hpp"
+#include "timer.hpp"
+
+namespace cru::platform::win {
+WinApplication* WinApplication::instance_ = nullptr;
+
+WinApplication* WinApplication::GetInstance() {
+ if (instance_ == nullptr)
+ instance_ = new WinApplication(::GetModuleHandleW(nullptr));
+ return instance_;
+}
+
+WinApplication::WinApplication(HINSTANCE h_instance) : h_instance_(h_instance) {
+ if (instance_)
+ throw std::runtime_error("A application instance already exists.");
+
+ instance_ = this;
+
+ if (!::IsWindows8OrGreater())
+ throw std::runtime_error("Must run on Windows 8 or later.");
+
+ god_window_ = std::make_shared<GodWindow>(this);
+ timer_manager_ = std::make_shared<TimerManager>(god_window_.get());
+}
+
+WinApplication::~WinApplication() { instance_ = nullptr; }
+
+int WinApplication::Run() {
+ MSG msg;
+ while (GetMessageW(&msg, nullptr, 0, 0)) {
+ TranslateMessage(&msg);
+ DispatchMessageW(&msg);
+ }
+ return static_cast<int>(msg.wParam);
+}
+
+void WinApplication::Quit(const int quit_code) { ::PostQuitMessage(quit_code); }
+
+void WinApplication::InvokeLater(const std::function<void()>& action) {
+ // copy the action to a safe place
+ auto p_action_copy = new std::function<void()>(action);
+
+ if (PostMessageW(GetGodWindow()->GetHandle(), invoke_later_message_id,
+ reinterpret_cast<WPARAM>(p_action_copy), 0) == 0)
+ throw Win32Error(::GetLastError(), "InvokeLater failed to post message.");
+}
+
+unsigned long WinApplication::SetTimeout(std::chrono::milliseconds milliseconds,
+ const std::function<void()>& action) {
+ return static_cast<unsigned long>(timer_manager_->CreateTimer(
+ static_cast<UINT>(milliseconds.count()), false, action));
+}
+
+unsigned long WinApplication::SetInterval(
+ std::chrono::milliseconds milliseconds,
+ const std::function<void()>& action) {
+ return static_cast<unsigned long>(timer_manager_->CreateTimer(
+ static_cast<UINT>(milliseconds.count()), true, action));
+}
+
+void WinApplication::CancelTimer(unsigned long id) {
+ timer_manager_->KillTimer(static_cast<UINT_PTR>(id));
+}
+} // namespace cru::platform::win
diff --git a/src/platform_win/window_class.cpp b/src/platform_win/window_class.cpp
new file mode 100644
index 00000000..b58f53b2
--- /dev/null
+++ b/src/platform_win/window_class.cpp
@@ -0,0 +1,28 @@
+#include "cru/platform/win/window_class.hpp"
+
+#include "cru/platform/win/exception.hpp"
+
+namespace cru::platform::win {
+WindowClass::WindowClass(const std::wstring& name, WNDPROC window_proc,
+ HINSTANCE h_instance)
+ : name_(name) {
+ WNDCLASSEX window_class;
+ window_class.cbSize = sizeof(WNDCLASSEX);
+
+ window_class.style = CS_HREDRAW | CS_VREDRAW;
+ window_class.lpfnWndProc = window_proc;
+ window_class.cbClsExtra = 0;
+ window_class.cbWndExtra = 0;
+ window_class.hInstance = h_instance;
+ window_class.hIcon = LoadIcon(NULL, IDI_APPLICATION);
+ window_class.hCursor = LoadCursor(NULL, IDC_ARROW);
+ window_class.hbrBackground = GetSysColorBrush(COLOR_BTNFACE);
+ window_class.lpszMenuName = NULL;
+ window_class.lpszClassName = name.c_str();
+ window_class.hIconSm = NULL;
+
+ atom_ = ::RegisterClassExW(&window_class);
+ if (atom_ == 0)
+ throw Win32Error(::GetLastError(), "Failed to create window class.");
+}
+} // namespace cru::ui