aboutsummaryrefslogtreecommitdiff
path: root/src/win/gui/TimerManager.cpp
blob: f3da1f091501b4c893e9ef63868a98975fd02f1c (plain)
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(),
          u"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