1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
|
#include "TimerManager.hpp"
#include "cru/win/gui/Base.hpp"
#include "cru/win/gui/Exception.hpp"
#include "gsl/gsl_util"
#include <functional>
#include <type_traits>
namespace cru::platform::gui::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->native_timer_id == 0) return;
::KillTimer(god_window_->GetHandle(), info->native_timer_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::gui::win
|