feat(surface): windows api
Some checks reported errors
continuous-integration/drone/push Build was killed
Some checks reported errors
continuous-integration/drone/push Build was killed
This commit is contained in:
parent
90d9226e28
commit
9cfacb02cd
11 changed files with 457 additions and 246 deletions
|
|
@ -128,6 +128,11 @@ if(WIN32)
|
||||||
memory
|
memory
|
||||||
input_codes
|
input_codes
|
||||||
PRIVATE_DEPENDENCIES
|
PRIVATE_DEPENDENCIES
|
||||||
|
user32
|
||||||
|
gdi32
|
||||||
|
kernel32
|
||||||
|
dwmapi
|
||||||
|
Shcore
|
||||||
logger
|
logger
|
||||||
time
|
time
|
||||||
TESTS
|
TESTS
|
||||||
|
|
|
||||||
|
|
@ -36,18 +36,12 @@ private:
|
||||||
|
|
||||||
void on_key_release(const lt::surface::KeyReleasedEvent &event);
|
void on_key_release(const lt::surface::KeyReleasedEvent &event);
|
||||||
|
|
||||||
void on_pointer_move(const lt::surface::MouseMovedEvent &event);
|
void on_pointer(const lt::surface::PointerEvent &event);
|
||||||
|
|
||||||
void on_button_press(const lt::surface::ButtonPressedEvent &event);
|
|
||||||
|
|
||||||
void on_button_release(const lt::surface::ButtonReleasedEvent &event);
|
|
||||||
|
|
||||||
memory::Ref<ecs::Registry> m_registry;
|
memory::Ref<ecs::Registry> m_registry;
|
||||||
|
|
||||||
std::array<bool, 512> m_keys {};
|
std::array<bool, 512> m_keys {};
|
||||||
|
|
||||||
std::array<bool, 512> m_buttons {};
|
|
||||||
|
|
||||||
math::vec2 m_pointer_position;
|
math::vec2 m_pointer_position;
|
||||||
|
|
||||||
app::TickResult m_last_tick_result {};
|
app::TickResult m_last_tick_result {};
|
||||||
|
|
@ -131,9 +125,7 @@ void System::handle_event(const surface::SurfaceComponent::Event &event)
|
||||||
[this](const surface::LostFocusEvent &) { on_surface_lost_focus(); },
|
[this](const surface::LostFocusEvent &) { on_surface_lost_focus(); },
|
||||||
[this](const surface::KeyPressedEvent &event) { on_key_press(event); },
|
[this](const surface::KeyPressedEvent &event) { on_key_press(event); },
|
||||||
[this](const surface::KeyReleasedEvent &event) { on_key_release(event); },
|
[this](const surface::KeyReleasedEvent &event) { on_key_release(event); },
|
||||||
[this](const surface::MouseMovedEvent &event) { on_pointer_move(event); },
|
[this](const surface::PointerEvent &event) { on_pointer(event); },
|
||||||
[this](const surface::ButtonPressedEvent &event) { on_button_press(event); },
|
|
||||||
[this](const surface::ButtonReleasedEvent &event) { on_button_release(event); },
|
|
||||||
[this](auto) {},
|
[this](auto) {},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -146,11 +138,6 @@ void System::on_surface_lost_focus()
|
||||||
{
|
{
|
||||||
key = false;
|
key = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto &button : m_buttons)
|
|
||||||
{
|
|
||||||
button = false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void System::on_key_press(const lt::surface::KeyPressedEvent &event)
|
void System::on_key_press(const lt::surface::KeyPressedEvent &event)
|
||||||
|
|
@ -183,19 +170,9 @@ void System::on_key_release(const lt::surface::KeyReleasedEvent &event)
|
||||||
m_keys[std::to_underlying(event.get_key())] = false;
|
m_keys[std::to_underlying(event.get_key())] = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void System::on_pointer_move(const lt::surface::MouseMovedEvent &event)
|
void System::on_pointer(const lt::surface::PointerEvent &event)
|
||||||
{
|
{
|
||||||
m_pointer_position = event.get_position();
|
m_pointer_position = event.get_position();
|
||||||
}
|
}
|
||||||
|
|
||||||
void System::on_button_press(const lt::surface::ButtonPressedEvent &event)
|
|
||||||
{
|
|
||||||
m_buttons[std::to_underlying(event.get_button())] = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void System::on_button_release(const lt::surface::ButtonReleasedEvent &event)
|
|
||||||
{
|
|
||||||
m_buttons[std::to_underlying(event.get_button())] = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace lt::input
|
} // namespace lt::input
|
||||||
|
|
|
||||||
|
|
@ -147,7 +147,7 @@ Suite tick = "tick"_suite = [] {
|
||||||
auto action_key = input.add_action(
|
auto action_key = input.add_action(
|
||||||
{
|
{
|
||||||
.name { "test" },
|
.name { "test" },
|
||||||
.trigger = { .mapped_keycode = Key::a },
|
.trigger = { .mapped_keycode = lt::Key::a },
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -156,7 +156,7 @@ Suite tick = "tick"_suite = [] {
|
||||||
system.tick(tick_info());
|
system.tick(tick_info());
|
||||||
expect_eq(input.get_action(action_key).state, inactive);
|
expect_eq(input.get_action(action_key).state, inactive);
|
||||||
|
|
||||||
surface.push_event(lt::surface::KeyPressedEvent(Key::a));
|
surface.push_event(lt::surface::KeyPressedEvent(lt::Key::a));
|
||||||
system.tick(tick_info());
|
system.tick(tick_info());
|
||||||
expect_eq(input.get_action(action_key).state, triggered);
|
expect_eq(input.get_action(action_key).state, triggered);
|
||||||
|
|
||||||
|
|
@ -168,7 +168,7 @@ Suite tick = "tick"_suite = [] {
|
||||||
system.tick(tick_info());
|
system.tick(tick_info());
|
||||||
expect_eq(input.get_action(action_key).state, active);
|
expect_eq(input.get_action(action_key).state, active);
|
||||||
|
|
||||||
surface.push_event(lt::surface::KeyReleasedEvent(Key::a));
|
surface.push_event(lt::surface::KeyReleasedEvent(lt::Key::a));
|
||||||
system.tick(tick_info());
|
system.tick(tick_info());
|
||||||
expect_eq(input.get_action(action_key).state, inactive);
|
expect_eq(input.get_action(action_key).state, inactive);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,10 @@ export module input.codes;
|
||||||
|
|
||||||
import preliminary;
|
import preliminary;
|
||||||
|
|
||||||
export enum class Key: u16 {
|
export namespace lt {
|
||||||
|
|
||||||
|
enum class Key : u16
|
||||||
|
{
|
||||||
none = 0,
|
none = 0,
|
||||||
|
|
||||||
left_button,
|
left_button,
|
||||||
|
|
@ -27,6 +30,12 @@ export enum class Key: u16 {
|
||||||
x_button_1,
|
x_button_1,
|
||||||
x_button_2,
|
x_button_2,
|
||||||
|
|
||||||
|
// Mouse-wheel movement is treated like a key, deal with it.
|
||||||
|
wheel_down,
|
||||||
|
wheel_up,
|
||||||
|
|
||||||
|
escape,
|
||||||
|
escp = escape,
|
||||||
backspace,
|
backspace,
|
||||||
tab,
|
tab,
|
||||||
capslock,
|
capslock,
|
||||||
|
|
@ -167,7 +176,7 @@ export enum class Key: u16 {
|
||||||
unknown,
|
unknown,
|
||||||
};
|
};
|
||||||
|
|
||||||
export [[nodiscard]] constexpr auto to_string(Key key) -> std::string
|
[[nodiscard]] constexpr auto to_string(Key key) -> std::string
|
||||||
{
|
{
|
||||||
using enum Key;
|
using enum Key;
|
||||||
switch (key)
|
switch (key)
|
||||||
|
|
@ -181,6 +190,10 @@ export [[nodiscard]] constexpr auto to_string(Key key) -> std::string
|
||||||
case x_button_1: return "x_button_1";
|
case x_button_1: return "x_button_1";
|
||||||
case x_button_2: return "x_button_2";
|
case x_button_2: return "x_button_2";
|
||||||
|
|
||||||
|
case wheel_down: return "wheel_down";
|
||||||
|
case wheel_up: return "wheel_up";
|
||||||
|
|
||||||
|
case escape: return "escape";
|
||||||
case backspace: return "backspace";
|
case backspace: return "backspace";
|
||||||
case tab: return "tab";
|
case tab: return "tab";
|
||||||
case capslock: return "capslock";
|
case capslock: return "capslock";
|
||||||
|
|
@ -290,3 +303,15 @@ export [[nodiscard]] constexpr auto to_string(Key key) -> std::string
|
||||||
|
|
||||||
return "<invalid>";
|
return "<invalid>";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} // namespace lt
|
||||||
|
|
||||||
|
template<>
|
||||||
|
struct std::formatter<lt::Key>: std::formatter<std::string_view>
|
||||||
|
{
|
||||||
|
template<typename FormatContext>
|
||||||
|
auto format(lt::Key key, FormatContext &ctx) const
|
||||||
|
{
|
||||||
|
return std::formatter<std::string_view>::format(lt::to_string(key), ctx);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
export module renderer.vk.renderer;
|
export module renderer.vk.renderer;
|
||||||
|
|
||||||
import preliminary;
|
import preliminary;
|
||||||
|
import time;
|
||||||
import logger;
|
import logger;
|
||||||
import assets.shader;
|
import assets.shader;
|
||||||
import renderer.vk.api_wrapper;
|
import renderer.vk.api_wrapper;
|
||||||
|
|
@ -290,22 +291,27 @@ void Renderer::record_cmd(vk::CommandBuffer &cmd, u32 image_idx)
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
static lt::time::Timer timer;
|
||||||
|
|
||||||
using Attachment = vk::CommandBuffer::RenderingInfo::AttachmentInfo;
|
using Attachment = vk::CommandBuffer::RenderingInfo::AttachmentInfo;
|
||||||
cmd.begin_rendering(
|
cmd.begin_rendering({
|
||||||
{
|
|
||||||
.area_offset = {0u, 0u,},
|
.area_offset = {0u, 0u,},
|
||||||
.area_extent = m_resolution,
|
.area_extent = m_resolution,
|
||||||
.color_attachments = std::vector<Attachment> {
|
.color_attachments = std::vector<Attachment> {
|
||||||
Attachment{
|
Attachment{
|
||||||
.view= &m_swapchain->get_image_view(image_idx),
|
.view= &m_swapchain->get_image_view(image_idx),
|
||||||
.layout = vk::Image::Layout::color_attachment_optimal,
|
.layout = vk::Image::Layout::color_attachment_optimal,
|
||||||
.load_operation = Attachment::LoadOperation::clear,
|
.load_operation = Attachment::LoadOperation::clear,
|
||||||
.store_operation = Attachment::StoreOperation::store,
|
.store_operation = Attachment::StoreOperation::store,
|
||||||
.color_clear_values = {.5f, .5f, .5f, 1.f}
|
.color_clear_values = {
|
||||||
|
static_cast<float>(std::sin(timer.elapsed_time().count())),
|
||||||
|
static_cast<float>(std::cos(timer.elapsed_time().count() * 2.0)),
|
||||||
|
static_cast<float>(std::sin(timer.elapsed_time().count() / 2.0)),
|
||||||
|
1.f
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
}
|
|
||||||
);
|
|
||||||
// cmd.bind_pipeline(m_pass->get_pipeline(), vk::Pipeline::BindPoint::graphics);
|
// cmd.bind_pipeline(m_pass->get_pipeline(), vk::Pipeline::BindPoint::graphics);
|
||||||
// cmd.set_viewport(
|
// cmd.set_viewport(
|
||||||
// {
|
// {
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
import preliminary;
|
import preliminary;
|
||||||
import time;
|
import time;
|
||||||
import logger;
|
import logger;
|
||||||
|
import renderer.system;
|
||||||
|
import renderer.frontend;
|
||||||
import surface.system;
|
import surface.system;
|
||||||
import surface.events;
|
import surface.events;
|
||||||
import surface.requests;
|
import surface.requests;
|
||||||
|
|
@ -15,37 +17,114 @@ constexpr auto title = "TestWindow";
|
||||||
constexpr auto width = 800u;
|
constexpr auto width = 800u;
|
||||||
constexpr auto height = 600u;
|
constexpr auto height = 600u;
|
||||||
constexpr auto vsync = true;
|
constexpr auto vsync = true;
|
||||||
constexpr auto visible = false;
|
constexpr auto visible = true;
|
||||||
|
|
||||||
|
template<class... Ts>
|
||||||
|
struct overloads: Ts...
|
||||||
|
{
|
||||||
|
using Ts::operator()...;
|
||||||
|
};
|
||||||
|
|
||||||
|
void renderer_debug_callback(
|
||||||
|
lt::renderer::IDebugger::MessageSeverity message_severity,
|
||||||
|
lt::renderer::IDebugger::MessageType message_type,
|
||||||
|
lt::renderer::IDebugger::MessageData data,
|
||||||
|
std::any &user_data
|
||||||
|
)
|
||||||
|
{
|
||||||
|
ignore = message_severity;
|
||||||
|
ignore = message_type;
|
||||||
|
ignore = user_data;
|
||||||
|
|
||||||
|
lt::log::trace("< Renderer > ==> {}", std::string { data.message });
|
||||||
|
}
|
||||||
|
|
||||||
auto main() -> i32
|
auto main() -> i32
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
for (auto idx = 0; idx < 100; ++idx)
|
|
||||||
{
|
|
||||||
std::println("{}: \033[1;{}m| color |\033[0m", idx, idx);
|
|
||||||
}
|
|
||||||
auto registry = lt::memory::create_ref<lt::ecs::Registry>();
|
auto registry = lt::memory::create_ref<lt::ecs::Registry>();
|
||||||
auto system = lt::surface::System { registry };
|
auto surface_system = lt::surface::System { registry };
|
||||||
|
|
||||||
auto entity = registry->create_entity();
|
auto entity = registry->create_entity();
|
||||||
|
|
||||||
system.create_surface_component(
|
lt::log::trace("Creating surface component");
|
||||||
|
surface_system.create_surface_component(
|
||||||
entity,
|
entity,
|
||||||
lt::surface::SurfaceComponent::CreateInfo {
|
lt::surface::SurfaceComponent::CreateInfo {
|
||||||
.title = title,
|
.title = title,
|
||||||
|
.position = { 500, 500 },
|
||||||
|
|
||||||
.resolution = { width, height },
|
.resolution = { width, height },
|
||||||
.vsync = vsync,
|
.vsync = vsync,
|
||||||
.visible = visible,
|
.visible = visible,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
auto timer = lt::time::Timer {};
|
auto &window = registry->get<lt::surface::SurfaceComponent>(entity);
|
||||||
lt::log::trace("Ticking for 3 seconds...");
|
|
||||||
while (timer.elapsed_time() < std::chrono::seconds { 30 })
|
const auto config = lt::renderer::System::Configuration {
|
||||||
|
.target_api = lt::renderer::Api::vulkan,
|
||||||
|
.max_frames_in_flight = 3u,
|
||||||
|
};
|
||||||
|
|
||||||
|
const auto debug_callback_info = lt::renderer::IDebugger::CreateInfo {
|
||||||
|
.severities = lt::renderer::IDebugger::MessageSeverity::all,
|
||||||
|
.types = lt::renderer::IDebugger::MessageType::all,
|
||||||
|
.callback = &renderer_debug_callback,
|
||||||
|
.user_data = nullptr,
|
||||||
|
};
|
||||||
|
|
||||||
|
const auto surface_entity = lt::ecs::Entity { registry, entity };
|
||||||
|
|
||||||
|
auto renderer_system = lt::renderer::System { lt::renderer::System::CreateInfo {
|
||||||
|
config,
|
||||||
|
registry,
|
||||||
|
surface_entity,
|
||||||
|
debug_callback_info,
|
||||||
|
} };
|
||||||
|
|
||||||
|
registry.add_
|
||||||
|
|
||||||
|
auto should_close = false;
|
||||||
|
const auto visitor = overloads {
|
||||||
|
[&](const lt::surface::ClosedEvent &) {
|
||||||
|
lt::log::info("Closing due to: Window X button pressed");
|
||||||
|
should_close = true;
|
||||||
|
},
|
||||||
|
[&](const lt::surface::MovedEvent &event) {
|
||||||
|
lt::log::info("Moved: {}", event.get_position());
|
||||||
|
},
|
||||||
|
[&](const lt::surface::ResizedEvent &event) {
|
||||||
|
lt::log::info("Resized: {}", event.get_size());
|
||||||
|
},
|
||||||
|
[&](const lt::surface::LostFocusEvent &) { lt::log::info("Lost focus"); },
|
||||||
|
[&](const lt::surface::GainFocusEvent &) { lt::log::info("Gain focus"); },
|
||||||
|
[&](const lt::surface::KeyPressedEvent &event) {
|
||||||
|
if (event.get_key() == lt::Key::escape)
|
||||||
|
{
|
||||||
|
lt::log::info("Closing due to: Escape key pressed");
|
||||||
|
should_close = true;
|
||||||
|
}
|
||||||
|
lt::log::info("Key pressed: {}", event.get_key());
|
||||||
|
},
|
||||||
|
[&](const lt::surface::KeyReleasedEvent &event) {
|
||||||
|
lt::log::info("Key released: {}", event.get_key());
|
||||||
|
},
|
||||||
|
[&](const lt::surface::PointerEvent &event) {
|
||||||
|
lt::log::info("Pointer: {}", event.get_position());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
while (!should_close)
|
||||||
{
|
{
|
||||||
system.tick({});
|
surface_system.tick({});
|
||||||
|
renderer_system.tick({});
|
||||||
|
|
||||||
|
for (const auto &event : window.peek_events())
|
||||||
|
{
|
||||||
|
std::visit(visitor, event);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
lt::log::trace("Three seconds passed, quitting...");
|
|
||||||
}
|
}
|
||||||
catch (const std::exception &exp)
|
catch (const std::exception &exp)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -72,6 +72,10 @@ private:
|
||||||
class PointerEvent
|
class PointerEvent
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
PointerEvent(math::vec2 position): m_position(position)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
PointerEvent(f32 x, f32 y): m_position(x, y)
|
PointerEvent(f32 x, f32 y): m_position(x, y)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
@ -135,6 +139,10 @@ public:
|
||||||
class MovedEvent
|
class MovedEvent
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
MovedEvent(math::vec2_i32 position): m_position(position)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
MovedEvent(i32 x, i32 y): m_position(x, y)
|
MovedEvent(i32 x, i32 y): m_position(x, y)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
@ -156,6 +164,10 @@ private:
|
||||||
class ResizedEvent
|
class ResizedEvent
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
ResizedEvent(math::vec2_u32 size): m_size(size)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
ResizedEvent(u32 width, u32 height): m_size(width, height)
|
ResizedEvent(u32 width, u32 height): m_size(width, height)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,8 @@ module;
|
||||||
#include <ShellScalingApi.h>
|
#include <ShellScalingApi.h>
|
||||||
#include <Windows.h>
|
#include <Windows.h>
|
||||||
#include <comdef.h>
|
#include <comdef.h>
|
||||||
|
#include <dwmapi.h> // For DWM functions
|
||||||
|
#include <windowsx.h>
|
||||||
|
|
||||||
#if defined(UNICODE) || defined(_UNICODE)
|
#if defined(UNICODE) || defined(_UNICODE)
|
||||||
#error "Unicode is not turned off"
|
#error "Unicode is not turned off"
|
||||||
|
|
@ -165,11 +167,7 @@ private:
|
||||||
|
|
||||||
void modify_position(SurfaceComponent &surface, const ModifyPositionRequest &request);
|
void modify_position(SurfaceComponent &surface, const ModifyPositionRequest &request);
|
||||||
|
|
||||||
void modify_visiblity(SurfaceComponent &surface, const ModifyVisibilityRequest &request);
|
void modify_visibility(SurfaceComponent &surface, const ModifyVisibilityRequest &request);
|
||||||
|
|
||||||
void modify_position(ecs::EntityId surface_entity, const math::vec2_i32 &new_size);
|
|
||||||
|
|
||||||
void modify_position(ecs::EntityId surface_entity, const math::vec2_u32 &new_size);
|
|
||||||
|
|
||||||
void set_visibility(ecs::EntityId surface_entity, bool visible);
|
void set_visibility(ecs::EntityId surface_entity, bool visible);
|
||||||
|
|
||||||
|
|
@ -636,31 +634,26 @@ try
|
||||||
auto &surface = m_registry->add<SurfaceComponent>(entity, info);
|
auto &surface = m_registry->add<SurfaceComponent>(entity, info);
|
||||||
ensure_component_sanity(surface);
|
ensure_component_sanity(surface);
|
||||||
|
|
||||||
const auto style = WS_OVERLAPPEDWINDOW;
|
// auto style = WS_THICKFRAME // Required for a standard resizeable window
|
||||||
const auto ex_style = ::DWORD {};
|
// | WS_SYSMENU // Support snapping via Win + ← / Win + →
|
||||||
const auto dpi = get_dpi_from_point(info.position);
|
// | WS_MAXIMIZEBOX // Support maximizing via mouse dragging to the top of the
|
||||||
|
// screen | WS_MINIMIZEBOX // Support minimizing by clicking on the taskbar icon
|
||||||
|
// ;
|
||||||
|
|
||||||
auto client_area_rectangle = ::RECT {
|
// if (info.visible)
|
||||||
.left = info.position.x,
|
// {
|
||||||
.top = info.position.y,
|
// style |= WS_VISIBLE;
|
||||||
.right = static_cast<::LONG>(info.position.x + info.resolution.x),
|
// }
|
||||||
.bottom = static_cast<::LONG>(info.position.y + info.resolution.y),
|
|
||||||
};
|
|
||||||
ensure(
|
|
||||||
::AdjustWindowRectExForDpi(&client_area_rectangle, style, false, ex_style, dpi),
|
|
||||||
"Failed ::AdjustWindowRectExForDpi: {}",
|
|
||||||
get_error_message()
|
|
||||||
);
|
|
||||||
|
|
||||||
auto hwnd = ::CreateWindowEx(
|
auto hwnd = ::CreateWindowEx(
|
||||||
0,
|
0,
|
||||||
constants::class_name,
|
constants::class_name,
|
||||||
info.title.data(),
|
info.title.data(),
|
||||||
WS_OVERLAPPEDWINDOW,
|
WS_OVERLAPPEDWINDOW | WS_VISIBLE,
|
||||||
client_area_rectangle.left,
|
info.position.x,
|
||||||
client_area_rectangle.top,
|
info.position.y,
|
||||||
client_area_rectangle.right - client_area_rectangle.left,
|
info.resolution.x,
|
||||||
client_area_rectangle.bottom - client_area_rectangle.top,
|
info.resolution.y,
|
||||||
nullptr,
|
nullptr,
|
||||||
nullptr,
|
nullptr,
|
||||||
::GetModuleHandle(nullptr),
|
::GetModuleHandle(nullptr),
|
||||||
|
|
@ -669,10 +662,35 @@ try
|
||||||
std::bit_cast<LPVOID>(&surface)
|
std::bit_cast<LPVOID>(&surface)
|
||||||
);
|
);
|
||||||
ensure(hwnd, "Failed ::CreateWindowEx: {}", get_error_message());
|
ensure(hwnd, "Failed ::CreateWindowEx: {}", get_error_message());
|
||||||
|
|
||||||
surface.m_native_data.window = hwnd;
|
surface.m_native_data.window = hwnd;
|
||||||
|
|
||||||
::ShowWindow(surface.m_native_data.window, SW_SHOW);
|
auto margins = MARGINS { 0, 0, 1, 0 }; // Extend 1px on right for shadow rendering
|
||||||
|
DwmExtendFrameIntoClientArea(hwnd, &margins);
|
||||||
|
|
||||||
|
BOOL useDarkMode = TRUE;
|
||||||
|
DwmSetWindowAttribute(hwnd, DWMWA_USE_IMMERSIVE_DARK_MODE, &useDarkMode, sizeof(useDarkMode));
|
||||||
|
|
||||||
|
ShowWindow(hwnd, SW_SHOW);
|
||||||
|
SetWindowPos(
|
||||||
|
hwnd,
|
||||||
|
NULL,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE
|
||||||
|
);
|
||||||
|
UpdateWindow(hwnd);
|
||||||
|
surface.m_visible = info.visible;
|
||||||
|
|
||||||
|
if (info.visible)
|
||||||
|
{
|
||||||
|
ShowWindow(hwnd, SW_SHOW);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ShowWindow(hwnd, SW_HIDE);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch (const std::exception &exp)
|
catch (const std::exception &exp)
|
||||||
{
|
{
|
||||||
|
|
@ -714,7 +732,7 @@ void System::handle_requests(SurfaceComponent &surface)
|
||||||
[&](const ModifyResolutionRequest &request) { modify_resolution(surface, request); },
|
[&](const ModifyResolutionRequest &request) { modify_resolution(surface, request); },
|
||||||
[&](const ModifyPositionRequest &request) { modify_position(surface, request); },
|
[&](const ModifyPositionRequest &request) { modify_position(surface, request); },
|
||||||
[&](const ModifyVisibilityRequest &request) {
|
[&](const ModifyVisibilityRequest &request) {
|
||||||
modify_visiblity(surface, request);
|
modify_visibility(surface, request);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -728,126 +746,41 @@ void System::handle_requests(SurfaceComponent &surface)
|
||||||
|
|
||||||
void System::modify_title(SurfaceComponent &surface, const ModifyTitleRequest &request)
|
void System::modify_title(SurfaceComponent &surface, const ModifyTitleRequest &request)
|
||||||
{
|
{
|
||||||
// WIP(Light):
|
auto hwnd = surface.m_native_data.window;
|
||||||
ignore = surface;
|
|
||||||
ignore = request;
|
|
||||||
|
|
||||||
surface.m_title = request.title;
|
surface.m_title = request.title;
|
||||||
|
|
||||||
// const auto &[display, window, _] = surface.get_native_data();
|
::SetWindowTextA(hwnd, request.title.c_str());
|
||||||
// XStoreName(display, window, request.title.c_str());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void System::modify_resolution(SurfaceComponent &surface, const ModifyResolutionRequest &request)
|
void System::modify_resolution(SurfaceComponent &surface, const ModifyResolutionRequest &request)
|
||||||
{
|
{
|
||||||
// WIP(Light):
|
::SetWindowPos(
|
||||||
ignore = surface;
|
surface.m_native_data.window,
|
||||||
ignore = request;
|
{},
|
||||||
|
surface.get_position().x,
|
||||||
// surface.m_resolution = request.resolution;
|
surface.get_position().y,
|
||||||
|
request.resolution.x,
|
||||||
// auto &[display, window, _] = surface.m_native_data;
|
request.resolution.y,
|
||||||
// const auto &[width, height] = request.resolution;
|
SWP_NOZORDER | SWP_NOACTIVATE
|
||||||
// // XResizeWindow(display, window, width, height);
|
);
|
||||||
//
|
|
||||||
// // get baseline serial number for X requests generated from XResizeWindow
|
|
||||||
// auto serial = NextRequest(display);
|
|
||||||
//
|
|
||||||
// // request a new window size from the X server
|
|
||||||
// XResizeWindow(
|
|
||||||
// display,
|
|
||||||
// window,
|
|
||||||
// static_cast<u32>(width),
|
|
||||||
// static_cast<u32>(height)
|
|
||||||
// );
|
|
||||||
//
|
|
||||||
// // flush output queue and wait for X server to processes the request
|
|
||||||
// XSync(display, False);
|
|
||||||
// // The documentation for XResizeWindow includes this important note:
|
|
||||||
// //
|
|
||||||
// // If the override-redirect flag of the window is False and some
|
|
||||||
// // other client has selected SubstructureRedirectMask on the parent,
|
|
||||||
// // the X server generates a ConfigureRequest event, and no further
|
|
||||||
// // processing is performed.
|
|
||||||
// //
|
|
||||||
// // What this means, essentially, is that if this window is a top-level
|
|
||||||
// // window, then it's the window manager (the "other client") that is
|
|
||||||
// // responsible for changing this window's size. So when we call
|
|
||||||
// // XResizeWindow() on a top-level window, then instead of resizing
|
|
||||||
// // the window immediately, the X server informs the window manager,
|
|
||||||
// // and then the window manager sets our new size (usually it will be
|
|
||||||
// // the size we asked for). We receive a ConfigureNotify event when
|
|
||||||
// // our new size has been set.
|
|
||||||
// constexpr auto lifespan = std::chrono::milliseconds { 10 };
|
|
||||||
// auto timer = time::Timer {};
|
|
||||||
// auto event = XEvent {};
|
|
||||||
// while (!XCheckIfEvent(
|
|
||||||
// display,
|
|
||||||
// &event,
|
|
||||||
// XEventTypeEquals<ConfigureNotify>,
|
|
||||||
// reinterpret_cast<XPointer>(&window) // NOLINT
|
|
||||||
// )
|
|
||||||
// || event.xconfigure.serial < serial)
|
|
||||||
// {
|
|
||||||
// std::this_thread::sleep_for(std::chrono::microseconds { 100 });
|
|
||||||
// if (timer.elapsed_time() > lifespan)
|
|
||||||
// {
|
|
||||||
// log::error("Timed out waiting for XResizeWindow's event");
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// // We don't need to update the component's state and handle the event in this funcion.
|
|
||||||
// // Since handle_requests is called before handle_events.
|
|
||||||
// // So we just put the event back into the queue and move on.
|
|
||||||
// XPutBackEvent(display, &event);
|
|
||||||
// XSync(display, False);
|
|
||||||
// XFlush(display);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void System::modify_position(SurfaceComponent &surface, const ModifyPositionRequest &request)
|
void System::modify_position(SurfaceComponent &surface, const ModifyPositionRequest &request)
|
||||||
{
|
{
|
||||||
auto hwnd = surface.m_native_data.window;
|
::SetWindowPos(
|
||||||
|
|
||||||
const auto style = ::GetWindowLong(hwnd, GWL_STYLE);
|
|
||||||
const auto ex_style = ::GetWindowLong(hwnd, GWL_EXSTYLE);
|
|
||||||
const auto dpi = get_dpi_from_point(request.position);
|
|
||||||
|
|
||||||
auto rectangle = ::RECT {
|
|
||||||
.left = {},
|
|
||||||
.top = {},
|
|
||||||
.right = static_cast<::LONG>(surface.get_resolution().x),
|
|
||||||
.bottom = static_cast<::LONG>(surface.get_resolution().y),
|
|
||||||
};
|
|
||||||
AdjustWindowRectExForDpi(&rectangle, style, false, ex_style, dpi);
|
|
||||||
|
|
||||||
SetWindowPos(
|
|
||||||
surface.m_native_data.window,
|
surface.m_native_data.window,
|
||||||
{},
|
{},
|
||||||
request.position.x + rectangle.left,
|
request.position.x,
|
||||||
request.position.y + rectangle.top,
|
request.position.y,
|
||||||
surface.get_resolution().x,
|
surface.get_resolution().x,
|
||||||
surface.get_resolution().y,
|
surface.get_resolution().y,
|
||||||
SWP_NOZORDER | SWP_NOACTIVATE
|
SWP_NOZORDER | SWP_NOACTIVATE
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void System::modify_visiblity(SurfaceComponent &surface, const ModifyVisibilityRequest &request)
|
void System::modify_visibility(SurfaceComponent &surface, const ModifyVisibilityRequest &request)
|
||||||
{
|
{
|
||||||
// WIP(Light): Use ignored local-variables
|
::ShowWindow(surface.m_native_data.window, request.visible ? SW_SHOW : SW_HIDE);
|
||||||
ignore = surface;
|
|
||||||
ignore = request;
|
|
||||||
|
|
||||||
// const auto &[display, window, _] = surface.get_native_data();
|
|
||||||
// surface.m_visible = request.visible;
|
|
||||||
|
|
||||||
// if (request.visible)
|
|
||||||
// {
|
|
||||||
// XMapWindow(display, window);
|
|
||||||
// }
|
|
||||||
// else
|
|
||||||
// {
|
|
||||||
// XUnmapWindow(display, window);
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void System::tick(app::TickInfo tick)
|
void System::tick(app::TickInfo tick)
|
||||||
|
|
@ -1120,6 +1053,74 @@ void System::tick(app::TickInfo tick)
|
||||||
|
|
||||||
switch (uMsg)
|
switch (uMsg)
|
||||||
{
|
{
|
||||||
|
case WM_NCCALCSIZE:
|
||||||
|
{
|
||||||
|
if (wParam == TRUE)
|
||||||
|
{
|
||||||
|
// Get border thicknesses
|
||||||
|
// int borderPadding = GetSystemMetrics(SM_CXPADDEDBORDER);
|
||||||
|
// int borderLR = GetSystemMetrics(SM_CXFRAME) + borderPadding;
|
||||||
|
// int borderTB = GetSystemMetrics(SM_CYFRAME) + borderPadding;
|
||||||
|
//
|
||||||
|
// NCCALCSIZE_PARAMS *params = (NCCALCSIZE_PARAMS *)lParam;
|
||||||
|
// RECT *rect = ¶ms->rgrc[0];
|
||||||
|
|
||||||
|
// Adjust client rect: Subtract borders from left/right/bottom
|
||||||
|
// rect->left += borderLR;
|
||||||
|
// rect->right -= borderLR;
|
||||||
|
// rect->bottom -= borderTB;
|
||||||
|
|
||||||
|
// For maximized windows, also subtract top border
|
||||||
|
// if (IsZoomed(hwnd))
|
||||||
|
// {
|
||||||
|
// rect->top += borderTB;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// Return 0 to indicate we handled it (prevents default title bar)
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case WM_NCHITTEST:
|
||||||
|
{
|
||||||
|
// Get mouse position in client coordinates
|
||||||
|
POINT pt;
|
||||||
|
pt.x = GET_X_LPARAM(lParam);
|
||||||
|
pt.y = GET_Y_LPARAM(lParam);
|
||||||
|
ScreenToClient(hwnd, &pt);
|
||||||
|
|
||||||
|
RECT clientRect;
|
||||||
|
GetClientRect(hwnd, &clientRect);
|
||||||
|
|
||||||
|
// Define a drag area (e.g., top 30px of client area acts as "caption" for moving)
|
||||||
|
const int dragHeight = 30; // Adjust as needed; set to 0 if no dragging desired
|
||||||
|
if (pt.y < dragHeight && pt.y >= 0)
|
||||||
|
{
|
||||||
|
// Check corners for diagonal resize
|
||||||
|
const int resizeBorder = 8; // Pixels for corner hit detection
|
||||||
|
if (pt.x < resizeBorder)
|
||||||
|
return HTTOPLEFT;
|
||||||
|
if (pt.x > clientRect.right - resizeBorder)
|
||||||
|
return HTTOPRIGHT;
|
||||||
|
// Middle top: vertical resize
|
||||||
|
return HTTOP;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback to default handling for side/bottom borders and other areas
|
||||||
|
return DefWindowProc(hwnd, uMsg, wParam, lParam);
|
||||||
|
}
|
||||||
|
|
||||||
|
case WM_PAINT:
|
||||||
|
{
|
||||||
|
PAINTSTRUCT ps;
|
||||||
|
HDC hdc = BeginPaint(hwnd, &ps);
|
||||||
|
RECT rect;
|
||||||
|
GetClientRect(hwnd, &rect);
|
||||||
|
FillRect(hdc, &rect, (HBRUSH)(COLOR_WINDOW + 5));
|
||||||
|
EndPaint(hwnd, &ps);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
case WM_NCCREATE:
|
case WM_NCCREATE:
|
||||||
[[unlikely]]
|
[[unlikely]]
|
||||||
{
|
{
|
||||||
|
|
@ -1130,42 +1131,36 @@ void System::tick(app::TickInfo tick)
|
||||||
return !!surface_ptr;
|
return !!surface_ptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
case WM_SETFOCUS: get_userdata().m_event_queue.emplace_back(GainFocusEvent {}); break;
|
case WM_SETTEXT:
|
||||||
|
[[unlikely]]
|
||||||
|
{
|
||||||
|
get_userdata().m_title = std::string { std::bit_cast<char *>(lParam) };
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case WM_KILLFOCUS: get_userdata().m_event_queue.emplace_back(LostFocusEvent {}); break;
|
case WM_SHOWWINDOW: get_userdata().m_visible = static_cast<bool>(wParam); return 0;
|
||||||
|
|
||||||
|
case WM_SETFOCUS: get_userdata().m_event_queue.emplace_back(GainFocusEvent {}); return 0;
|
||||||
|
|
||||||
|
case WM_KILLFOCUS: get_userdata().m_event_queue.emplace_back(LostFocusEvent {}); return 0;
|
||||||
|
|
||||||
case WM_SIZE:
|
case WM_SIZE:
|
||||||
{
|
{
|
||||||
const auto width = LOWORD(lParam);
|
|
||||||
const auto height = HIWORD(lParam);
|
|
||||||
|
|
||||||
lt::log::test("Received WM_SIZE: {}, {}", width, height);
|
|
||||||
auto &surface = get_userdata();
|
auto &surface = get_userdata();
|
||||||
surface.m_resolution = math::vec2_u32 { width, height };
|
|
||||||
surface.m_event_queue.emplace_back(ResizedEvent { width, height });
|
|
||||||
|
surface.m_resolution = math::vec2_u32 { LOWORD(lParam), HIWORD(lParam) };
|
||||||
|
surface.m_event_queue.emplace_back(surface.m_resolution);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
case WM_MOVE:
|
case WM_MOVE:
|
||||||
{
|
{
|
||||||
auto client_top_left = ::POINT {};
|
|
||||||
ensure(
|
|
||||||
::ClientToScreen(hwnd, &client_top_left),
|
|
||||||
"Failed ::ClientToScreen (hwnd: {:x})",
|
|
||||||
std::bit_cast<size_t>(hwnd)
|
|
||||||
);
|
|
||||||
|
|
||||||
auto &surface = get_userdata();
|
auto &surface = get_userdata();
|
||||||
|
|
||||||
surface.m_position = {
|
surface.m_position = math::vec2_i32 { LOWORD(lParam), HIWORD(lParam) };
|
||||||
client_top_left.x,
|
surface.m_event_queue.emplace_back(MovedEvent { surface.m_position });
|
||||||
client_top_left.y,
|
|
||||||
};
|
|
||||||
|
|
||||||
surface.m_event_queue.emplace_back(
|
|
||||||
MovedEvent { static_cast<i32>(client_top_left.x), static_cast<i32>(client_top_left.y) }
|
|
||||||
);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
@ -1222,7 +1217,7 @@ void System::tick(app::TickInfo tick)
|
||||||
const auto key = static_cast<Key>(
|
const auto key = static_cast<Key>(
|
||||||
std::to_underlying(Key::x_button_1) + GET_XBUTTON_WPARAM(wParam) - 1
|
std::to_underlying(Key::x_button_1) + GET_XBUTTON_WPARAM(wParam) - 1
|
||||||
);
|
);
|
||||||
get_userdata().m_event_queue.emplace_back<KeyReleasedEvent>(key);
|
get_userdata().m_event_queue.emplace_back<KeyPressedEvent>(key);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case WM_XBUTTONUP:
|
case WM_XBUTTONUP:
|
||||||
|
|
@ -1230,7 +1225,7 @@ void System::tick(app::TickInfo tick)
|
||||||
const auto key = static_cast<Key>(
|
const auto key = static_cast<Key>(
|
||||||
std::to_underlying(Key::x_button_1) + GET_XBUTTON_WPARAM(wParam) - 1
|
std::to_underlying(Key::x_button_1) + GET_XBUTTON_WPARAM(wParam) - 1
|
||||||
);
|
);
|
||||||
get_userdata().m_event_queue.emplace_back<KeyPressedEvent>(key);
|
get_userdata().m_event_queue.emplace_back<KeyReleasedEvent>(key);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case WM_KEYDOWN:
|
case WM_KEYDOWN:
|
||||||
|
|
@ -1255,11 +1250,11 @@ void System::tick(app::TickInfo tick)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
case WM_DESTROY:
|
// case WM_DESTROY:
|
||||||
{
|
// {
|
||||||
PostQuitMessage(0);
|
// PostQuitMessage(0);
|
||||||
return 0;
|
// return 0;
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
return DefWindowProcA(hwnd, uMsg, wParam, lParam);
|
return DefWindowProcA(hwnd, uMsg, wParam, lParam);
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@ import memory.reference;
|
||||||
import math.vec2;
|
import math.vec2;
|
||||||
import app.system;
|
import app.system;
|
||||||
|
|
||||||
|
using ::lt::Key;
|
||||||
using ::lt::surface::SurfaceComponent;
|
using ::lt::surface::SurfaceComponent;
|
||||||
using ::lt::surface::System;
|
using ::lt::surface::System;
|
||||||
|
|
||||||
|
|
@ -32,21 +33,6 @@ using ::lt::surface::System;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// void simulate_key_down(lt::surface::SurfaceComponent &surface, lt::Key key)
|
|
||||||
// {
|
|
||||||
// #if defined(LIGHT_PLATFORM_LINUX)
|
|
||||||
// #elif defined(LIGHT_PLATFORM_WINDOWS)
|
|
||||||
// #endif
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// void simulate_key_up(lt::Key key)
|
|
||||||
// {
|
|
||||||
// #if defined(LIGHT_PLATFORM_LINUX)
|
|
||||||
// #elif defined(LIGHT_PLATFORM_WINDOWS)
|
|
||||||
// #endif
|
|
||||||
// }
|
|
||||||
|
|
||||||
constexpr auto title = "TestWindow";
|
constexpr auto title = "TestWindow";
|
||||||
constexpr auto width = 800u;
|
constexpr auto width = 800u;
|
||||||
constexpr auto height = 600u;
|
constexpr auto height = 600u;
|
||||||
|
|
@ -164,9 +150,19 @@ Suite system_events = "system_events"_suite = [] {
|
||||||
Suite registry_events = "registry_events"_suite = [] {
|
Suite registry_events = "registry_events"_suite = [] {
|
||||||
Case { "on_construct initializes component" } = [] {
|
Case { "on_construct initializes component" } = [] {
|
||||||
auto fixture = Fixture {};
|
auto fixture = Fixture {};
|
||||||
|
auto system = System { fixture.registry() };
|
||||||
|
|
||||||
|
system.tick({});
|
||||||
|
system.tick({});
|
||||||
const auto &component = fixture.create_component();
|
const auto &component = fixture.create_component();
|
||||||
|
system.tick({});
|
||||||
|
system.tick({});
|
||||||
|
system.tick({});
|
||||||
expect_eq(fixture.registry()->view<SurfaceComponent>().get_size(), 1);
|
expect_eq(fixture.registry()->view<SurfaceComponent>().get_size(), 1);
|
||||||
|
|
||||||
|
system.tick({});
|
||||||
|
system.tick({});
|
||||||
|
system.tick({});
|
||||||
fixture.check_values(*component);
|
fixture.check_values(*component);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -256,8 +252,9 @@ Suite tick = "ticking"_suite = [] {
|
||||||
auto system = System { fixture.registry() };
|
auto system = System { fixture.registry() };
|
||||||
auto &surface = **fixture.create_component();
|
auto &surface = **fixture.create_component();
|
||||||
|
|
||||||
constexpr auto position = lt::math::vec2_i32 { 50, 50 };
|
const auto new_title = std::string { title } + std::string { "_" };
|
||||||
constexpr auto resolution = lt::math::vec2_u32 { width, height };
|
constexpr auto new_position = lt::math::vec2_i32 { position_x + 50, position_y + 50 };
|
||||||
|
constexpr auto new_resolution = lt::math::vec2_u32 { width + 50, height + 50 };
|
||||||
|
|
||||||
expect_eq(surface.peek_requests().size(), 0);
|
expect_eq(surface.peek_requests().size(), 0);
|
||||||
|
|
||||||
|
|
@ -266,11 +263,11 @@ Suite tick = "ticking"_suite = [] {
|
||||||
system.tick(tick_info());
|
system.tick(tick_info());
|
||||||
expect_eq(surface.peek_requests().size(), 0);
|
expect_eq(surface.peek_requests().size(), 0);
|
||||||
|
|
||||||
surface.push_request(lt::surface::ModifyTitleRequest(title));
|
surface.push_request(lt::surface::ModifyTitleRequest(new_title));
|
||||||
expect_eq(surface.peek_requests().size(), 1);
|
expect_eq(surface.peek_requests().size(), 1);
|
||||||
|
|
||||||
surface.push_request(lt::surface::ModifyResolutionRequest(resolution));
|
surface.push_request(lt::surface::ModifyResolutionRequest(new_resolution));
|
||||||
surface.push_request(lt::surface::ModifyPositionRequest(position));
|
surface.push_request(lt::surface::ModifyPositionRequest(new_position));
|
||||||
expect_eq(surface.peek_requests().size(), 1 + 2);
|
expect_eq(surface.peek_requests().size(), 1 + 2);
|
||||||
|
|
||||||
surface.push_request(lt::surface::ModifyVisibilityRequest(false));
|
surface.push_request(lt::surface::ModifyVisibilityRequest(false));
|
||||||
|
|
@ -280,10 +277,51 @@ Suite tick = "ticking"_suite = [] {
|
||||||
|
|
||||||
system.tick(tick_info());
|
system.tick(tick_info());
|
||||||
expect_eq(surface.peek_requests().size(), 0);
|
expect_eq(surface.peek_requests().size(), 0);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
expect_eq(surface.get_title(), title);
|
Suite requests = "requests"_suite = [] {
|
||||||
expect_eq(surface.get_position(), position);
|
using ::lt::surface::ModifyTitleRequest;
|
||||||
expect_eq(surface.get_resolution(), resolution);
|
using ::lt::surface::ModifyResolutionRequest;
|
||||||
|
using ::lt::surface::ModifyPositionRequest;
|
||||||
|
using ::lt::surface::ModifyVisibilityRequest;
|
||||||
|
|
||||||
|
auto fixture = Fixture {};
|
||||||
|
auto system = System { fixture.registry() };
|
||||||
|
auto &surface = **fixture.create_component();
|
||||||
|
|
||||||
|
Case { "ModifyTitleRequest" } = [&] {
|
||||||
|
const auto new_title = std::string { title } + std::string { "_" };
|
||||||
|
surface.push_request({ ModifyTitleRequest { new_title } });
|
||||||
|
|
||||||
|
system.tick({});
|
||||||
|
expect_eq(surface.get_title(), new_title);
|
||||||
|
};
|
||||||
|
|
||||||
|
Case { "ModifyResolutionRequest" } = [&] {
|
||||||
|
constexpr auto new_resolution = lt::math::vec2_u32 { width + 50, height + 50 };
|
||||||
|
surface.push_request({ ModifyResolutionRequest { new_resolution } });
|
||||||
|
|
||||||
|
system.tick({});
|
||||||
|
expect_eq(surface.get_resolution(), new_resolution);
|
||||||
|
};
|
||||||
|
|
||||||
|
Case { "ModifyPositionRequest" } = [&] {
|
||||||
|
constexpr auto new_position = lt::math::vec2_i32 { position_x + 50, position_y + 50 };
|
||||||
|
surface.push_request({ ModifyPositionRequest { new_position } });
|
||||||
|
|
||||||
|
system.tick({});
|
||||||
|
expect_eq(surface.get_position(), new_position);
|
||||||
|
};
|
||||||
|
|
||||||
|
Case { "ModifyVisibilityRequest" } = [&] {
|
||||||
|
surface.push_request({ ModifyVisibilityRequest { .visible = false } });
|
||||||
|
system.tick({});
|
||||||
|
expect_eq(surface.is_visible(), false);
|
||||||
|
|
||||||
|
surface.push_request({ ModifyVisibilityRequest { .visible = true } });
|
||||||
|
system.tick({});
|
||||||
|
expect_eq(surface.is_visible(), true);
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -302,7 +340,8 @@ Suite windows_window_proc = "windows_window_proc"_suite = [] {
|
||||||
::SendMessage(hwnd, WM_SETFOCUS, {}, {});
|
::SendMessage(hwnd, WM_SETFOCUS, {}, {});
|
||||||
expect_eq(events.size(), 1u);
|
expect_eq(events.size(), 1u);
|
||||||
|
|
||||||
ignore = std::get<lt::surface::GainFocusEvent>(events.front());
|
auto event = std::get<lt::surface::GainFocusEvent>(events.front());
|
||||||
|
::lt::log::trace("{}", event.to_string()); // make sure it's not optimized away?
|
||||||
};
|
};
|
||||||
|
|
||||||
system.tick({});
|
system.tick({});
|
||||||
|
|
@ -311,7 +350,8 @@ Suite windows_window_proc = "windows_window_proc"_suite = [] {
|
||||||
::SendMessage(hwnd, WM_KILLFOCUS, {}, {});
|
::SendMessage(hwnd, WM_KILLFOCUS, {}, {});
|
||||||
expect_eq(events.size(), 1u);
|
expect_eq(events.size(), 1u);
|
||||||
|
|
||||||
ignore = std::get<lt::surface::LostFocusEvent>(events.front());
|
auto event = std::get<lt::surface::LostFocusEvent>(events.front());
|
||||||
|
::lt::log::trace("{}", event.to_string()); // make sure it's not optimized away?
|
||||||
};
|
};
|
||||||
|
|
||||||
system.tick({});
|
system.tick({});
|
||||||
|
|
@ -350,60 +390,133 @@ Suite windows_window_proc = "windows_window_proc"_suite = [] {
|
||||||
|
|
||||||
system.tick({});
|
system.tick({});
|
||||||
Case { "WM_MOUSEWHEEL" } = [&] {
|
Case { "WM_MOUSEWHEEL" } = [&] {
|
||||||
|
expect_eq(events.size(), 0u);
|
||||||
|
::SendMessage(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(0, WHEEL_DELTA), {});
|
||||||
|
::SendMessage(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(0, -WHEEL_DELTA), {});
|
||||||
|
|
||||||
|
// Mouse wheel is treated like key presses,
|
||||||
|
// but since there is no "release" action for it...
|
||||||
|
// Every movement causes two key press events together:
|
||||||
|
// Press + Release of wheel_up/down.
|
||||||
|
expect_eq(events.size(), 4u);
|
||||||
|
expect_eq(std::get<lt::surface::KeyPressedEvent>(events[0]).get_key(), Key::wheel_up);
|
||||||
|
expect_eq(std::get<lt::surface::KeyReleasedEvent>(events[1]).get_key(), Key::wheel_up);
|
||||||
|
expect_eq(std::get<lt::surface::KeyPressedEvent>(events[2]).get_key(), Key::wheel_down);
|
||||||
|
expect_eq(std::get<lt::surface::KeyReleasedEvent>(events[3]).get_key(), Key::wheel_down);
|
||||||
};
|
};
|
||||||
|
|
||||||
system.tick({});
|
system.tick({});
|
||||||
Case { "WM_LBUTTONDOWN" } = [&] {
|
Case { "WM_LBUTTONDOWN" } = [&] {
|
||||||
|
expect_eq(events.size(), 0u);
|
||||||
|
::SendMessage(hwnd, WM_LBUTTONDOWN, {}, {});
|
||||||
|
expect_eq(events.size(), 1u);
|
||||||
|
|
||||||
|
// Mouse buttons are treated like key presses.
|
||||||
|
expect_eq(std::get<lt::surface::KeyPressedEvent>(events[0]).get_key(), Key::left_button);
|
||||||
};
|
};
|
||||||
|
|
||||||
system.tick({});
|
system.tick({});
|
||||||
Case { "WM_LBUTTONUP" } = [&] {
|
Case { "WM_LBUTTONUP" } = [&] {
|
||||||
|
expect_eq(events.size(), 0u);
|
||||||
|
::SendMessage(hwnd, WM_LBUTTONUP, {}, {});
|
||||||
|
expect_eq(events.size(), 1u);
|
||||||
|
|
||||||
|
// Mouse buttons are treated like key presses.
|
||||||
|
expect_eq(std::get<lt::surface::KeyReleasedEvent>(events[0]).get_key(), Key::left_button);
|
||||||
};
|
};
|
||||||
|
|
||||||
system.tick({});
|
system.tick({});
|
||||||
Case { "WM_RBUTTONDOWN" } = [&] {
|
Case { "WM_RBUTTONDOWN" } = [&] {
|
||||||
|
expect_eq(events.size(), 0u);
|
||||||
|
::SendMessage(hwnd, WM_RBUTTONDOWN, {}, {});
|
||||||
|
expect_eq(events.size(), 1u);
|
||||||
|
|
||||||
|
// Mouse buttons are treated like key presses.
|
||||||
|
expect_eq(std::get<lt::surface::KeyPressedEvent>(events[0]).get_key(), Key::right_button);
|
||||||
};
|
};
|
||||||
|
|
||||||
system.tick({});
|
system.tick({});
|
||||||
Case { "WM_RBUTTONUP" } = [&] {
|
Case { "WM_RBUTTONUP" } = [&] {
|
||||||
|
expect_eq(events.size(), 0u);
|
||||||
|
::SendMessage(hwnd, WM_RBUTTONUP, {}, {});
|
||||||
|
expect_eq(events.size(), 1u);
|
||||||
|
|
||||||
|
// Mouse buttons are treated like key presses.
|
||||||
|
expect_eq(std::get<lt::surface::KeyReleasedEvent>(events[0]).get_key(), Key::right_button);
|
||||||
};
|
};
|
||||||
|
|
||||||
system.tick({});
|
system.tick({});
|
||||||
Case { "WM_MBUTTONDOWN" } = [&] {
|
Case { "WM_MBUTTONDOWN" } = [&] {
|
||||||
|
expect_eq(events.size(), 0u);
|
||||||
|
::SendMessage(hwnd, WM_MBUTTONDOWN, {}, {});
|
||||||
|
expect_eq(events.size(), 1u);
|
||||||
|
|
||||||
|
// Mouse buttons are treated like key presses.
|
||||||
|
expect_eq(std::get<lt::surface::KeyPressedEvent>(events[0]).get_key(), Key::middle_button);
|
||||||
};
|
};
|
||||||
|
|
||||||
system.tick({});
|
system.tick({});
|
||||||
Case { "WM_MBUTTONUP" } = [&] {
|
Case { "WM_MBUTTONUP" } = [&] {
|
||||||
|
expect_eq(events.size(), 0u);
|
||||||
|
::SendMessage(hwnd, WM_MBUTTONUP, {}, {});
|
||||||
|
expect_eq(events.size(), 1u);
|
||||||
|
|
||||||
|
// Mouse buttons are treated like key presses.
|
||||||
|
expect_eq(std::get<lt::surface::KeyReleasedEvent>(events[0]).get_key(), Key::middle_button);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
system.tick({});
|
system.tick({});
|
||||||
Case { "WM_XBUTTONDOWN" } = [&] {
|
Case { "WM_XBUTTONDOWN" } = [&] {
|
||||||
|
expect_eq(events.size(), 0u);
|
||||||
|
::SendMessage(hwnd, WM_XBUTTONDOWN, MAKEWPARAM(0, XBUTTON1), {});
|
||||||
|
::SendMessage(hwnd, WM_XBUTTONDOWN, MAKEWPARAM(0, XBUTTON2), {});
|
||||||
|
expect_eq(events.size(), 2u);
|
||||||
|
|
||||||
|
// Mouse buttons are treated like key presses.
|
||||||
|
expect_eq(std::get<lt::surface::KeyPressedEvent>(events[0]).get_key(), Key::x_button_1);
|
||||||
|
expect_eq(std::get<lt::surface::KeyPressedEvent>(events[1]).get_key(), Key::x_button_2);
|
||||||
};
|
};
|
||||||
|
|
||||||
system.tick({});
|
system.tick({});
|
||||||
Case { "WM_XBUTTONUP" } = [&] {
|
Case { "WM_XBUTTONUP" } = [&] {
|
||||||
|
expect_eq(events.size(), 0u);
|
||||||
|
::SendMessage(hwnd, WM_XBUTTONUP, MAKEWPARAM(0, XBUTTON1), {});
|
||||||
|
::SendMessage(hwnd, WM_XBUTTONUP, MAKEWPARAM(0, XBUTTON2), {});
|
||||||
|
expect_eq(events.size(), 2u);
|
||||||
|
|
||||||
|
// Mouse buttons are treated like key presses.
|
||||||
|
expect_eq(std::get<lt::surface::KeyReleasedEvent>(events[0]).get_key(), Key::x_button_1);
|
||||||
|
expect_eq(std::get<lt::surface::KeyReleasedEvent>(events[1]).get_key(), Key::x_button_2);
|
||||||
};
|
};
|
||||||
|
|
||||||
system.tick({});
|
system.tick({});
|
||||||
Case { "WM_KEYDOWN" } = [&] {
|
Case { "WM_KEYDOWN" } = [&] {
|
||||||
expect_eq(events.size(), 0u);
|
expect_eq(events.size(), 0u);
|
||||||
::SendMessage(hwnd, WM_KEYDOWN, System::to_native_key(lt::Key::escape), {});
|
::SendMessage(hwnd, WM_KEYDOWN, System::to_native_key(Key::escape), {});
|
||||||
expect_eq(events.size(), 1u);
|
expect_eq(events.size(), 1u);
|
||||||
|
|
||||||
const auto &event = std::get<lt::surface::KeyPressedEvent>(events.front());
|
expect_eq(std::get<lt::surface::KeyPressedEvent>(events[0]).get_key(), Key::escape);
|
||||||
expect_eq(event.get_key(), lt::Key::escape);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
system.tick({});
|
system.tick({});
|
||||||
Case { "WM_KEYUP" } = [&] {
|
Case { "WM_KEYUP" } = [&] {
|
||||||
|
expect_eq(events.size(), 0u);
|
||||||
|
::SendMessage(hwnd, WM_KEYUP, System::to_native_key(Key::escape), {});
|
||||||
|
expect_eq(events.size(), 1u);
|
||||||
|
|
||||||
|
expect_eq(std::get<lt::surface::KeyReleasedEvent>(events[0]).get_key(), Key::escape);
|
||||||
};
|
};
|
||||||
|
|
||||||
system.tick({});
|
system.tick({});
|
||||||
Case { "WM_CLOSE" } = [&] {
|
Case { "WM_CLOSE" } = [&] {
|
||||||
};
|
expect_eq(events.size(), 0u);
|
||||||
|
::SendMessage(hwnd, WM_CLOSE, {}, {});
|
||||||
|
expect_eq(events.size(), 1u);
|
||||||
|
|
||||||
system.tick({});
|
// would throw if type is incorrect
|
||||||
Case { "WM_DESTROY" } = [&] {
|
auto event = std::get<lt::surface::ClosedEvent>(events[0]);
|
||||||
|
::lt::log::trace("{}", event.to_string()); // make sure it's not optimized away?
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,6 @@ Suite expects = "expects"_suite = []() {
|
||||||
};
|
};
|
||||||
|
|
||||||
Case { "this emptiness machine" } = [] {
|
Case { "this emptiness machine" } = [] {
|
||||||
expect_le(9, 6);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Case { "expect_unreachable" } = [] {
|
Case { "expect_unreachable" } = [] {
|
||||||
|
|
|
||||||
|
|
@ -19,11 +19,11 @@ cmake `
|
||||||
-B build `
|
-B build `
|
||||||
-G Ninja `
|
-G Ninja `
|
||||||
-D ENABLE_UNIT_TESTS=ON `
|
-D ENABLE_UNIT_TESTS=ON `
|
||||||
-D CMAKE_BUILD_TYPE=Debug `
|
-D CMAKE_BUILD_TYPE=Release `
|
||||||
-D CMAKE_EXPORT_COMPILE_COMMANDS=True `
|
-D CMAKE_EXPORT_COMPILE_COMMANDS=True `
|
||||||
-D CMAKE_CXX_FLAGS="/std:c++latest /EHsc /Zi /Oy- /WX /W4"
|
-D CMAKE_CXX_FLAGS="/std:c++latest /EHsc /Zi /Oy- /WX /W4"
|
||||||
|
|
||||||
cmake --build ./build
|
cmake --build ./build || $(exit $LASTEXITCODE)
|
||||||
|
|
||||||
$tests = Get-ChildItem -Path "./build" -Recurse -File | Where-Object {
|
$tests = Get-ChildItem -Path "./build" -Recurse -File | Where-Object {
|
||||||
$_.Name -like "*_tests.exe"
|
$_.Name -like "*_tests.exe"
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue