aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/cru/common/Event.hpp29
-rw-r--r--include/cru/platform/native/UiApplication.hpp10
-rw-r--r--include/cru/win/native/GodWindow.hpp9
-rw-r--r--include/cru/win/native/UiApplication.hpp2
-rw-r--r--src/ui/UiHost.cpp2
-rw-r--r--src/win/native/CMakeLists.txt5
-rw-r--r--src/win/native/GodWindow.cpp33
-rw-r--r--src/win/native/GodWindowMessage.hpp6
-rw-r--r--src/win/native/Timer.cpp28
-rw-r--r--src/win/native/Timer.hpp34
-rw-r--r--src/win/native/TimerManager.cpp100
-rw-r--r--src/win/native/TimerManager.hpp61
-rw-r--r--src/win/native/UiApplication.cpp28
13 files changed, 225 insertions, 122 deletions
diff --git a/include/cru/common/Event.hpp b/include/cru/common/Event.hpp
index 377ca7f3..6417bc78 100644
--- a/include/cru/common/Event.hpp
+++ b/include/cru/common/Event.hpp
@@ -5,6 +5,7 @@
#include <algorithm>
#include <functional>
+#include <initializer_list>
#include <memory>
#include <utility>
#include <vector>
@@ -183,6 +184,7 @@ struct EventRevokerDestroyer {
};
} // namespace details
+// A guard class for event revoker. Automatically revoke it when destroyed.
class EventRevokerGuard {
public:
EventRevokerGuard() = default;
@@ -201,7 +203,7 @@ class EventRevokerGuard {
return *revoker_;
}
- void Release() { revoker_.release(); }
+ EventRevoker Release() { return std::move(*revoker_.release()); }
void Reset(EventRevoker&& revoker) {
revoker_.reset(new EventRevoker(std::move(revoker)));
@@ -209,5 +211,28 @@ class EventRevokerGuard {
private:
std::unique_ptr<EventRevoker, details::EventRevokerDestroyer> revoker_;
-}; // namespace cru
+};
+
+class EventRevokerListGuard {
+ public:
+ EventRevokerListGuard() = default;
+ EventRevokerListGuard(const EventRevokerListGuard& other) = delete;
+ EventRevokerListGuard(EventRevokerListGuard&& other) = default;
+ EventRevokerListGuard& operator=(const EventRevokerListGuard& other) = delete;
+ EventRevokerListGuard& operator=(EventRevokerListGuard&& other) = default;
+ ~EventRevokerListGuard() = default;
+
+ public:
+ void Add(EventRevoker&& revoker) {
+ event_revoker_guard_list_.push_back(EventRevokerGuard(std::move(revoker)));
+ }
+
+ EventRevokerListGuard& operator+=(EventRevoker&& revoker) {
+ this->Add(std::move(revoker));
+ return *this;
+ }
+
+ private:
+ std::vector<EventRevokerGuard> event_revoker_guard_list_;
+};
} // namespace cru
diff --git a/include/cru/platform/native/UiApplication.hpp b/include/cru/platform/native/UiApplication.hpp
index 1aa4df57..135e95c3 100644
--- a/include/cru/platform/native/UiApplication.hpp
+++ b/include/cru/platform/native/UiApplication.hpp
@@ -31,16 +31,16 @@ struct IUiApplication : public virtual INativeResource {
virtual void AddOnQuitHandler(std::function<void()> handler) = 0;
- virtual void InvokeLater(std::function<void()> action) = 0;
- // Timer id should always be positive and never the same. So it's ok to use
- // negative value to represent no timer.
+ // Timer id should always be positive (not 0) and never the same. So it's ok
+ // to use negative value (or 0) to represent no timer.
+ virtual long long SetImmediate(std::function<void()> action) = 0;
virtual long long SetTimeout(std::chrono::milliseconds milliseconds,
std::function<void()> action) = 0;
virtual long long SetInterval(std::chrono::milliseconds milliseconds,
std::function<void()> action) = 0;
// Implementation should guarantee calls on timer id already canceled have no
- // effects and do not crash. Also canceling negative id should always result
- // in no-op.
+ // effects and do not crash. Also canceling negative id or 0 should always
+ // result in no-op.
virtual void CancelTimer(long long id) = 0;
virtual std::vector<INativeWindow*> GetAllWindow() = 0;
diff --git a/include/cru/win/native/GodWindow.hpp b/include/cru/win/native/GodWindow.hpp
index 8b20e01f..93d1acad 100644
--- a/include/cru/win/native/GodWindow.hpp
+++ b/include/cru/win/native/GodWindow.hpp
@@ -1,6 +1,9 @@
#pragma once
#include "Base.hpp"
+#include "WindowNativeMessageEventArgs.hpp"
+#include "cru/common/Event.hpp"
+
#include <memory>
namespace cru::platform::native::win {
@@ -20,10 +23,16 @@ class GodWindow : public Object {
bool HandleGodWindowMessage(HWND hwnd, UINT msg, WPARAM w_param,
LPARAM l_param, LRESULT* result);
+ IEvent<WindowNativeMessageEventArgs&>* MessageEvent() {
+ return &message_event_;
+ }
+
private:
WinUiApplication* application_;
std::unique_ptr<WindowClass> god_window_class_;
HWND hwnd_;
+
+ Event<WindowNativeMessageEventArgs&> message_event_;
};
} // namespace cru::platform::native::win
diff --git a/include/cru/win/native/UiApplication.hpp b/include/cru/win/native/UiApplication.hpp
index cbc08af7..328a6b84 100644
--- a/include/cru/win/native/UiApplication.hpp
+++ b/include/cru/win/native/UiApplication.hpp
@@ -32,7 +32,7 @@ class WinUiApplication : public WinNativeResource,
void AddOnQuitHandler(std::function<void()> handler) override;
- void InvokeLater(std::function<void()> action) override;
+ long long SetImmediate(std::function<void()> action) override;
long long SetTimeout(std::chrono::milliseconds milliseconds,
std::function<void()> action) override;
long long SetInterval(std::chrono::milliseconds milliseconds,
diff --git a/src/ui/UiHost.cpp b/src/ui/UiHost.cpp
index 5451ebce..0fdf2f53 100644
--- a/src/ui/UiHost.cpp
+++ b/src/ui/UiHost.cpp
@@ -156,7 +156,7 @@ void UiHost::InvalidatePaint() {
void UiHost::InvalidateLayout() {
log::TagDebug(log_tag, u"A relayout is requested.");
if (!need_layout_) {
- platform::native::IUiApplication::GetInstance()->InvokeLater(
+ platform::native::IUiApplication::GetInstance()->SetImmediate(
[resolver = this->CreateResolver()] {
if (const auto host = resolver.Resolve()) {
host->Relayout();
diff --git a/src/win/native/CMakeLists.txt b/src/win/native/CMakeLists.txt
index f1b167d2..0e8ae44f 100644
--- a/src/win/native/CMakeLists.txt
+++ b/src/win/native/CMakeLists.txt
@@ -2,8 +2,7 @@ set(CRU_WIN_NATIVE_INCLUDE_DIR ${CRU_INCLUDE_DIR}/cru/win/native)
add_library(cru_win_native STATIC
DpiUtil.hpp
- GodWindowMessage.hpp
- Timer.hpp
+ TimerManager.hpp
WindowD2DPainter.hpp
WindowManager.hpp
@@ -11,7 +10,7 @@ add_library(cru_win_native STATIC
GodWindow.cpp
InputMethod.cpp
Keyboard.cpp
- Timer.cpp
+ TimerManager.cpp
UiApplication.cpp
Window.cpp
WindowClass.cpp
diff --git a/src/win/native/GodWindow.cpp b/src/win/native/GodWindow.cpp
index b1e7275e..203542f7 100644
--- a/src/win/native/GodWindow.cpp
+++ b/src/win/native/GodWindow.cpp
@@ -1,7 +1,5 @@
#include "cru/win/native/GodWindow.hpp"
-#include "GodWindowMessage.hpp"
-#include "Timer.hpp"
#include "cru/common/Logger.hpp"
#include "cru/win/native/Exception.hpp"
#include "cru/win/native/UiApplication.hpp"
@@ -51,32 +49,15 @@ GodWindow::~GodWindow() {
bool GodWindow::HandleGodWindowMessage(HWND hwnd, UINT msg, WPARAM w_param,
LPARAM l_param, LRESULT* result) {
- CRU_UNUSED(hwnd)
- CRU_UNUSED(l_param)
+ WindowNativeMessageEventArgs args(
+ WindowNativeMessage{hwnd, msg, w_param, l_param});
+ message_event_.Raise(args);
- switch (msg) {
- case invoke_later_message_id: {
- const auto p_action = reinterpret_cast<std::function<void()>*>(w_param);
- (*p_action)();
- delete p_action;
- *result = 0;
- return true;
- }
- 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);
- result = 0;
- return true;
- }
- break;
- }
- default:
- return false;
+ if (args.IsHandled()) {
+ *result = args.GetResult();
+ return true;
}
+
return false;
}
} // namespace cru::platform::native::win
diff --git a/src/win/native/GodWindowMessage.hpp b/src/win/native/GodWindowMessage.hpp
deleted file mode 100644
index 9063cb4d..00000000
--- a/src/win/native/GodWindowMessage.hpp
+++ /dev/null
@@ -1,6 +0,0 @@
-#pragma once
-#include "cru/win/WinPreConfig.hpp"
-
-namespace cru::platform::native::win {
-constexpr int invoke_later_message_id = WM_USER + 2000;
-}
diff --git a/src/win/native/Timer.cpp b/src/win/native/Timer.cpp
deleted file mode 100644
index 662067fb..00000000
--- a/src/win/native/Timer.cpp
+++ /dev/null
@@ -1,28 +0,0 @@
-#include "Timer.hpp"
-
-namespace cru::platform::native::win {
-TimerManager::TimerManager(GodWindow* god_window) { god_window_ = god_window; }
-
-UINT_PTR TimerManager::CreateTimer(const UINT milliseconds, const bool loop,
- TimerAction action) {
- const auto id = current_count_++;
- ::SetTimer(god_window_->GetHandle(), id, milliseconds, nullptr);
- map_.emplace(id, std::make_pair(loop, std::move(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::native::win
diff --git a/src/win/native/Timer.hpp b/src/win/native/Timer.hpp
deleted file mode 100644
index 95f186a1..00000000
--- a/src/win/native/Timer.hpp
+++ /dev/null
@@ -1,34 +0,0 @@
-#pragma once
-#include "cru/win/WinPreConfig.hpp"
-
-#include "cru/common/Base.hpp"
-#include "cru/win/native/GodWindow.hpp"
-
-#include <chrono>
-#include <functional>
-#include <map>
-#include <optional>
-
-namespace cru::platform::native::win {
-using TimerAction = std::function<void()>;
-
-class TimerManager : public Object {
- public:
- TimerManager(GodWindow* god_window);
-
- CRU_DELETE_COPY(TimerManager)
- CRU_DELETE_MOVE(TimerManager)
-
- ~TimerManager() override = default;
-
- UINT_PTR CreateTimer(UINT milliseconds, bool loop, 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::native::win
diff --git a/src/win/native/TimerManager.cpp b/src/win/native/TimerManager.cpp
new file mode 100644
index 00000000..da129b52
--- /dev/null
+++ b/src/win/native/TimerManager.cpp
@@ -0,0 +1,100 @@
+#include "TimerManager.hpp"
+
+#include "cru/win/native/Base.hpp"
+#include "cru/win/native/Exception.hpp"
+#include "gsl/gsl_util"
+
+#include <functional>
+#include <type_traits>
+
+namespace cru::platform::native::win {
+constexpr int kSetImmediateWindowMessageId = WM_USER + 2000;
+
+TimerManager::TimerManager(GodWindow* god_window) {
+ god_window_ = god_window;
+ event_guard_ += god_window->MessageEvent()->AddHandler(std::bind(
+ &TimerManager::HandleGodWindowMessage, this, std::placeholders::_1));
+}
+
+long long TimerManager::SetTimer(TimerType type, int period,
+ std::function<void()> action) {
+ auto id = next_id_++;
+ TimerInfo timer_info{id, type, type == TimerType::Immediate ? 0 : period,
+ std::move(action)};
+ if (type == TimerType::Immediate) {
+ if (!::PostMessageW(god_window_->GetHandle(), kSetImmediateWindowMessageId,
+ gsl::narrow<UINT_PTR>(id), 0)) {
+ throw Win32Error(
+ ::GetLastError(),
+ "Failed to post window message to god window for set immediate.");
+ }
+ } else {
+ CreateNativeTimer(&timer_info);
+ }
+
+ info_map_.emplace(id, std::move(timer_info));
+ return id;
+}
+
+void TimerManager::CancelTimer(long long id) {
+ if (id <= 0) return;
+ auto find_result = this->info_map_.find(id);
+ if (find_result != info_map_.cend()) {
+ auto& info = find_result->second;
+ KillNativeTimer(&info);
+ this->info_map_.erase(find_result);
+ }
+}
+
+void TimerManager::CreateNativeTimer(TimerInfo* info) {
+ info->native_timer_id = gsl::narrow<UINT_PTR>(info->id);
+ ::SetTimer(god_window_->GetHandle(), info->native_timer_id, info->period,
+ nullptr);
+}
+
+void TimerManager::KillNativeTimer(TimerInfo* info) {
+ if (info->id == 0) return;
+ ::KillTimer(god_window_->GetHandle(), info->id);
+ info->native_timer_id = 0;
+}
+
+void TimerManager::HandleGodWindowMessage(WindowNativeMessageEventArgs& args) {
+ const auto& message = args.GetWindowMessage();
+
+ switch (message.msg) {
+ case kSetImmediateWindowMessageId: {
+ auto find_result =
+ this->info_map_.find(static_cast<long long>(message.w_param));
+ if (find_result != info_map_.cend()) {
+ auto& info = find_result->second;
+ info.action();
+ info_map_.erase(find_result);
+ }
+ args.SetResult(0);
+ args.SetHandled(true);
+ return;
+ }
+ case WM_TIMER: {
+ auto find_result =
+ this->info_map_.find(static_cast<long long>(message.w_param));
+ if (find_result != info_map_.cend()) {
+ auto& info = find_result->second;
+ if (info.type == TimerType::Interval) {
+ info.action();
+ args.SetResult(0);
+ args.SetHandled(true);
+ } else if (info.type == TimerType::Timeout) {
+ info.action();
+ KillNativeTimer(&info);
+ info_map_.erase(find_result);
+ args.SetResult(0);
+ args.SetHandled(true);
+ }
+ }
+ return;
+ }
+ default:
+ return;
+ }
+}
+} // namespace cru::platform::native::win
diff --git a/src/win/native/TimerManager.hpp b/src/win/native/TimerManager.hpp
new file mode 100644
index 00000000..f2731f60
--- /dev/null
+++ b/src/win/native/TimerManager.hpp
@@ -0,0 +1,61 @@
+#pragma once
+#include "cru/common/Event.hpp"
+#include "cru/win/WinPreConfig.hpp"
+
+#include "cru/common/Base.hpp"
+#include "cru/win/native/GodWindow.hpp"
+#include "cru/win/native/WindowNativeMessageEventArgs.hpp"
+
+#include <chrono>
+#include <functional>
+#include <optional>
+#include <unordered_map>
+
+namespace cru::platform::native::win {
+enum class TimerType { Immediate, Timeout, Interval };
+
+struct TimerInfo {
+ TimerInfo(long long id, TimerType type, int period,
+ std::function<void()> action, UINT_PTR native_timer_id = 0)
+ : id(id),
+ type(type),
+ period(period),
+ action(std::move(action)),
+ native_timer_id(native_timer_id) {}
+
+ long long id;
+ TimerType type;
+ int period; // in milliseconds
+ std::function<void()> action;
+ UINT_PTR native_timer_id;
+};
+
+class TimerManager : public Object {
+ public:
+ TimerManager(GodWindow* god_window);
+
+ CRU_DELETE_COPY(TimerManager)
+ CRU_DELETE_MOVE(TimerManager)
+
+ ~TimerManager() override = default;
+
+ // Period is in milliseconds. When type is immediate, it is not checked and
+ // used.
+ long long SetTimer(TimerType type, int period, std::function<void()> action);
+ void CancelTimer(long long id);
+
+ private:
+ void HandleGodWindowMessage(WindowNativeMessageEventArgs& args);
+
+ void CreateNativeTimer(TimerInfo* info);
+ void KillNativeTimer(TimerInfo* info);
+
+ private:
+ GodWindow* god_window_;
+
+ EventRevokerListGuard event_guard_;
+
+ long long next_id_ = 1;
+ std::unordered_map<long long, TimerInfo> info_map_;
+};
+} // namespace cru::platform::native::win
diff --git a/src/win/native/UiApplication.cpp b/src/win/native/UiApplication.cpp
index dbf52e05..a806db88 100644
--- a/src/win/native/UiApplication.cpp
+++ b/src/win/native/UiApplication.cpp
@@ -1,6 +1,8 @@
#include "cru/win/native/UiApplication.hpp"
#include "../DebugLogger.hpp"
+#include "TimerManager.hpp"
+#include "WindowManager.hpp"
#include "cru/common/Logger.hpp"
#include "cru/platform/Check.hpp"
#include "cru/win/graph/direct/Factory.hpp"
@@ -9,9 +11,6 @@
#include "cru/win/native/GodWindow.hpp"
#include "cru/win/native/InputMethod.hpp"
#include "cru/win/native/Window.hpp"
-#include "GodWindowMessage.hpp"
-#include "Timer.hpp"
-#include "WindowManager.hpp"
namespace cru::platform::native {
std::unique_ptr<IUiApplication> CreateUiApplication() {
@@ -64,30 +63,27 @@ void WinUiApplication::AddOnQuitHandler(std::function<void()> handler) {
quit_handlers_.push_back(std::move(handler));
}
-void WinUiApplication::InvokeLater(std::function<void()> action) {
- // copy the action to a safe place
- auto p_action_copy = new std::function<void()>(std::move(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.");
+long long WinUiApplication::SetImmediate(std::function<void()> action) {
+ return this->timer_manager_->SetTimer(TimerType::Immediate, 0,
+ std::move(action));
}
long long WinUiApplication::SetTimeout(std::chrono::milliseconds milliseconds,
std::function<void()> action) {
- return gsl::narrow<long long>(timer_manager_->CreateTimer(
- static_cast<UINT>(milliseconds.count()), false, std::move(action)));
+ return this->timer_manager_->SetTimer(TimerType::Timeout,
+ gsl::narrow<int>(milliseconds.count()),
+ std::move(action));
}
long long WinUiApplication::SetInterval(std::chrono::milliseconds milliseconds,
std::function<void()> action) {
- return gsl::narrow<long long>(timer_manager_->CreateTimer(
- static_cast<UINT>(milliseconds.count()), true, std::move(action)));
+ return this->timer_manager_->SetTimer(TimerType::Interval,
+ gsl::narrow<int>(milliseconds.count()),
+ std::move(action));
}
void WinUiApplication::CancelTimer(long long id) {
- if (id < 0) return;
- timer_manager_->KillTimer(static_cast<UINT_PTR>(id));
+ timer_manager_->CancelTimer(id);
}
std::vector<INativeWindow*> WinUiApplication::GetAllWindow() {