#include "application.hpp" #include #include "exception.hpp" #include "timer.hpp" #include "ui/window_class.hpp" namespace cru { constexpr auto god_window_class_name = L"GodWindowClass"; constexpr int invoke_later_message_id = WM_USER + 2000; LRESULT CALLBACK GodWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { const auto app = Application::GetInstance(); if (app) { const auto result = app->GetGodWindow()->HandleGodWindowMessage(hWnd, uMsg, wParam, lParam); if (result.has_value()) return result.value(); else return DefWindowProc(hWnd, uMsg, wParam, lParam); } else return DefWindowProc(hWnd, uMsg, wParam, lParam); } GodWindow::GodWindow(Application* application) { const auto h_instance = application->GetInstanceHandle(); god_window_class_ = std::make_unique(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 std::runtime_error("Failed to create window."); } GodWindow::~GodWindow() { ::DestroyWindow(hwnd_); } std::optional 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*>(w_param); (*p_action)(); delete p_action; return 0; } case WM_TIMER: { const auto id = static_cast(w_param); const auto action = TimerManager::GetInstance()->GetAction(id); if (action.has_value()) { (action.value().second)(); if (!action.value().first) TimerManager::GetInstance()->KillTimer(id); return 0; } break; } default: return std::nullopt; } return std::nullopt; } Application* Application::instance_ = nullptr; Application* Application::GetInstance() { return instance_; } Application::Application(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_unique(this); } Application::~Application() { for (auto i = singleton_list_.crbegin(); i != singleton_list_.crend(); ++i) delete *i; instance_ = nullptr; } int Application::Run() { MSG msg; while (GetMessage(&msg, nullptr, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return static_cast(msg.wParam); } void Application::Quit(const int quit_code) { ::PostQuitMessage(quit_code); } void InvokeLater(const std::function& action) { // copy the action to a safe place auto p_action_copy = new std::function(action); if (PostMessageW(Application::GetInstance()->GetGodWindow()->GetHandle(), invoke_later_message_id, reinterpret_cast(p_action_copy), 0) == 0) throw Win32Error(::GetLastError(), "InvokeLater failed to post message."); } } // namespace cru