1#ifdef MAYAFLUX_PLATFORM_WINDOWS
4#include "KeyMapping.hpp"
16static constexpr const wchar_t* k_class_name =
L"MayaFluxWindow";
18static void register_window_class(HINSTANCE hinstance)
20 static std::once_flag s_flag;
21 std::call_once(s_flag, [&]() {
23 wc.cbSize =
sizeof(wc);
24 wc.style = CS_HREDRAW | CS_VREDRAW;
25 wc.lpfnWndProc = Win32Window::wnd_proc;
26 wc.hInstance = hinstance;
27 wc.hCursor = LoadCursor(
nullptr, IDC_ARROW);
28 wc.lpszClassName = k_class_name;
29 RegisterClassExW(&wc);
37Win32Window::Win32Window(
const WindowCreateInfo& create_info,
38 const GlobalGraphicsConfig& graphics_config)
39 : m_create_info(create_info)
40 , m_key_repeat_config(graphics_config.key_repeat_config)
42 m_hinstance = GetModuleHandleW(
nullptr);
44 m_ui_thread = std::thread([
this]() { ui_thread_main(); });
46 m_hwnd_ready.wait(
false);
49Win32Window::~Win32Window()
58void Win32Window::ui_thread_main()
60 register_window_class(m_hinstance);
62 auto wtitle = std::wstring(m_create_info.title.begin(), m_create_info.title.end());
64 m_hwnd = CreateWindowExW(
69 CW_USEDEFAULT, CW_USEDEFAULT,
70 static_cast<int>(m_create_info.width),
71 static_cast<int>(m_create_info.height),
72 nullptr,
nullptr, m_hinstance,
nullptr);
74 SetWindowLongPtrW(m_hwnd, GWLP_USERDATA,
reinterpret_cast<LONG_PTR
>(
this));
76 m_hwnd_ready.store(
true);
77 m_hwnd_ready.notify_all();
80 while (GetMessageW(&msg,
nullptr, 0, 0)) {
81 TranslateMessage(&msg);
82 DispatchMessageW(&msg);
90LRESULT
CALLBACK Win32Window::wnd_proc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp)
92 auto* self =
reinterpret_cast<Win32Window*
>(GetWindowLongPtrW(hwnd, GWLP_USERDATA));
95 return DefWindowProcW(hwnd, msg, wp, lp);
99 self->m_should_close.store(
true);
102 ev.type = WindowEventType::WINDOW_CLOSED;
104 self->push_event(ev);
113 case WM_ENTERSIZEMOVE:
114 self->m_in_size_move.store(
true, std::memory_order_release);
117 case WM_EXITSIZEMOVE: {
118 self->m_in_size_move.store(
false, std::memory_order_release);
120 ev.type = WindowEventType::WINDOW_RESIZED;
122 ev.data = WindowEvent::ResizeData {
123 .width =
static_cast<uint32_t
>(self->m_state.current_width),
124 .
height =
static_cast<uint32_t
>(self->m_state.current_height)
126 self->push_event(ev);
131 auto w =
static_cast<uint32_t
>(LOWORD(lp));
132 auto h =
static_cast<uint32_t
>(HIWORD(lp));
133 self->m_state.current_width =
static_cast<int>(w);
134 self->m_state.current_height =
static_cast<int>(
h);
136 ev.type = WindowEventType::WINDOW_RESIZED;
138 ev.data = WindowEvent::ResizeData { .width = w, .height =
h };
139 self->push_event(ev);
144 self->m_state.is_focused =
true;
146 ev.type = WindowEventType::WINDOW_FOCUS_GAINED;
148 self->push_event(ev);
153 self->m_state.is_focused =
false;
155 ev.type = WindowEventType::WINDOW_FOCUS_LOST;
157 self->push_event(ev);
162 case WM_SYSKEYDOWN: {
166 const uint32_t scancode =
static_cast<uint32_t
>(HIWORD(lp) & 0x1FF);
167 IO::Keys key = from_win32_scancode(scancode);
168 if (key == IO::Keys::Unknown)
169 key = from_win32_key(wp);
171 const WindowEvent::KeyData kd {
172 .key =
static_cast<int16_t
>(key),
173 .scancode =
static_cast<int32_t
>(scancode),
177 self->m_held_keys_ui[kd.key] = kd;
178 self->m_keys_dirty.store(
true, std::memory_order_release);
180 ev.type = WindowEventType::KEY_PRESSED;
183 self->push_event(ev);
189 const uint32_t scancode =
static_cast<uint32_t
>(HIWORD(lp) & 0x1FF);
190 IO::Keys key = from_win32_scancode(scancode);
191 if (key == IO::Keys::Unknown)
192 key = from_win32_key(wp);
194 const WindowEvent::KeyData kd {
195 .key =
static_cast<int16_t
>(key),
196 .scancode =
static_cast<int32_t
>(scancode),
200 self->m_held_keys_ui.erase(kd.key);
201 self->m_keys_dirty.store(
true, std::memory_order_release);
203 ev.type = WindowEventType::KEY_RELEASED;
206 self->push_event(ev);
211 const auto unit =
static_cast<uint16_t
>(wp);
213 if (unit >= 0xD800 && unit <= 0xDBFF) {
214 self->m_pending_high_surrogate = unit;
218 uint32_t codepoint = unit;
219 if (unit >= 0xDC00 && unit <= 0xDFFF && self->m_pending_high_surrogate != 0) {
221 + (
static_cast<uint32_t
>(self->m_pending_high_surrogate - 0xD800) << 10)
222 + (
static_cast<uint32_t
>(unit - 0xDC00));
223 self->m_pending_high_surrogate = 0;
224 }
else if (unit >= 0xDC00 && unit <= 0xDFFF) {
225 self->m_pending_high_surrogate = 0;
228 self->m_pending_high_surrogate = 0;
236 ev.type = WindowEventType::TEXT_INPUT;
238 ev.data = WindowEvent::TextData { .codepoint = codepoint };
239 self->push_event(ev);
245 ev.type = WindowEventType::MOUSE_MOTION;
247 ev.data = WindowEvent::MousePosData {
248 .x =
static_cast<double>(GET_X_LPARAM(lp)),
249 .y =
static_cast<double>(GET_Y_LPARAM(lp))
251 self->push_event(ev);
257 case WM_MBUTTONDOWN: {
258 IO::MouseButtons btn = (msg == WM_LBUTTONDOWN) ? IO::MouseButtons::Left
262 ev.type = WindowEventType::MOUSE_BUTTON_PRESSED;
264 ev.data = WindowEvent::MouseButtonData {
265 .button =
static_cast<int8_t
>(btn),
268 self->push_event(ev);
275 IO::MouseButtons btn = (msg == WM_LBUTTONUP) ? IO::MouseButtons::Left
279 ev.type = WindowEventType::MOUSE_BUTTON_RELEASED;
281 ev.data = WindowEvent::MouseButtonData {
282 .button =
static_cast<int8_t
>(btn),
285 self->push_event(ev);
289 case WM_MOUSEWHEEL: {
290 double delta =
static_cast<double>(GET_WHEEL_DELTA_WPARAM(wp)) / WHEEL_DELTA;
292 ev.type = WindowEventType::MOUSE_SCROLLED;
294 ev.data = WindowEvent::ScrollData { .x_offset = 0.0, .y_offset = delta };
295 self->push_event(ev);
303 return DefWindowProcW(hwnd, msg, wp, lp);
310void Win32Window::push_event(WindowEvent ev)
312 (void)m_event_queue.push(ev);
319void Win32Window::poll()
321 if (m_keys_dirty.exchange(
false, std::memory_order_acq_rel)) {
322 const bool was_empty = m_held_keys.empty();
323 m_held_keys = m_held_keys_ui;
324 if (was_empty && !m_held_keys.empty())
325 m_repeat_next_tick = GetTickCount64() + m_key_repeat_config.initial_delay_ms;
328 while (
auto ev = m_event_queue.pop()) {
329 const bool suppress_signal = ev->type == WindowEventType::WINDOW_RESIZED
330 && m_in_size_move.load(std::memory_order_acquire);
331 if (!suppress_signal)
332 m_event_source.signal(*ev);
333 if (m_event_callback)
334 m_event_callback(*ev);
337 const ULONGLONG now = GetTickCount64();
338 if (!m_held_keys.empty() && now >= m_repeat_next_tick) {
339 m_repeat_next_tick = now + m_key_repeat_config.interval_ms;
340 for (
const auto& [k, kd] : m_held_keys) {
342 rev.type = WindowEventType::KEY_REPEAT;
345 m_event_source.signal(rev);
346 if (m_event_callback)
347 m_event_callback(rev);
356void Win32Window::show()
359 ShowWindow(m_hwnd, SW_SHOW);
362void Win32Window::hide()
365 ShowWindow(m_hwnd, SW_HIDE);
368void Win32Window::destroy()
371 PostMessageW(m_hwnd, WM_CLOSE, 0, 0);
374 if (m_ui_thread.joinable())
378bool Win32Window::should_close()
const
380 return m_should_close.load();
383void Win32Window::request_close()
385 if (!m_should_close.exchange(
true)) {
387 ev.type = WindowEventType::WINDOW_CLOSED;
393void Win32Window::set_event_callback(WindowEventCallback
callback)
395 m_event_callback = std::move(
callback);
398void Win32Window::set_title(
const std::string& title)
400 m_create_info.title = title;
402 auto wt = std::wstring(title.begin(), title.end());
403 SetWindowTextW(m_hwnd, wt.c_str());
407void Win32Window::set_size(uint32_t
width, uint32_t
height)
409 m_create_info.width =
width;
410 m_create_info.height =
height;
412 SetWindowPos(m_hwnd,
nullptr, 0, 0,
414 SWP_NOMOVE | SWP_NOZORDER);
417void Win32Window::set_position(uint32_t x, uint32_t y)
420 SetWindowPos(m_hwnd,
nullptr,
421 static_cast<int>(x),
static_cast<int>(y),
422 0, 0, SWP_NOSIZE | SWP_NOZORDER);
425void Win32Window::set_color(
const std::array<float, 4>&
color)
427 m_create_info.clear_color =
color;
434void Win32Window::register_rendering_buffer(std::shared_ptr<Buffers::VKBuffer>
buffer)
436 std::lock_guard lock(m_render_tracking_mutex);
437 m_rendering_buffers.push_back(
buffer);
440void Win32Window::unregister_rendering_buffer(std::shared_ptr<Buffers::VKBuffer>
buffer)
442 std::lock_guard lock(m_render_tracking_mutex);
443 std::erase_if(m_rendering_buffers,
444 [&
buffer](
const auto& wp) {
446 return !sp || sp ==
buffer;
450void Win32Window::track_frame_command(uint64_t cmd_id)
452 m_frame_commands.push_back(cmd_id);
455const std::vector<uint64_t>& Win32Window::get_frame_commands()
const
457 return m_frame_commands;
460void Win32Window::clear_frame_commands()
462 m_frame_commands.clear();
465std::vector<std::shared_ptr<Buffers::VKBuffer>> Win32Window::get_rendering_buffers()
const
467 std::lock_guard lock(m_render_tracking_mutex);
468 std::vector<std::shared_ptr<Buffers::VKBuffer>>
out;
469 out.reserve(m_rendering_buffers.size());
470 for (
auto& wp : m_rendering_buffers) {
471 if (
auto sp = wp.lock())
std::shared_ptr< NetworkGeometryBuffer > buffer
std::vector< float > * out
constexpr bool is_text_input_codepoint(uint32_t codepoint) noexcept
Check if a Unicode codepoint is valid for text input.
MouseButtons
Enumeration for mouse buttons.
@ IO
Networking, file handling, streaming.
@ CALLBACK
Use callback for custom transition.