#include "Input_Event.hpp" #include #include #include #include #include #include #include #include namespace aethera::web { namespace { [[nodiscard]] Event_Timeline_Time input_timeline_time(double milliseconds) { constexpr long double nanoseconds_per_millisecond{1'000'000.0L}; constexpr long double maximum_milliseconds = static_cast(std::numeric_limits::max()) / nanoseconds_per_millisecond; if (!std::isfinite(milliseconds) || milliseconds < 0.0 || static_cast(milliseconds) > maximum_milliseconds) throw std::invalid_argument( "input time_milliseconds must be finite, non-negative and representable"); return Event_Timeline_Time{static_cast( static_cast(milliseconds) * nanoseconds_per_millisecond)}; } void read_point(const nlohmann::json& input, std::string_view key, Input_Point& point, Input_Viewport viewport, bool viewport_relative) { const auto value = input.find(key); if (value == input.end() || !value->is_object()) return; const auto x = value->value("x", 0.0); const auto y = value->value("y", 0.0); point.x = viewport_relative && viewport.width != 0 ? std::clamp(x, 0.0, static_cast(viewport.width)) : x; point.y = viewport_relative && viewport.height != 0 ? std::clamp(y, 0.0, static_cast(viewport.height)) : y; } void read_pointer(const nlohmann::json& input, Basic_Pointer_Event& event, Input_Viewport viewport) { read_point(input, "position", event.position, viewport, true); read_point(input, "global_position", event.global_position, viewport, false); event.button = magic_enum::enum_cast( input.value("button", std::string{"none"})).value_or(Mouse_Button::none); event.buttons = static_cast( std::clamp(input.value("buttons", 0), 0, 255)); event.modifiers = static_cast( std::clamp(input.value("modifiers", 0), 0, 15)); } } Input_Resize_Event::Input_Resize_Event(Event_Timeline_Time occurred_at) : Event(Event_Type::resize, occurred_at) {} Input_Pointer_Event::Input_Pointer_Event( Event_Type type, Event_Timeline_Time occurred_at) : Basic_Pointer_Event(type, occurred_at) {} Input_Wheel_Event::Input_Wheel_Event(Event_Timeline_Time occurred_at) : Basic_Wheel_Event(occurred_at) {} Input_Key_Event::Input_Key_Event( Event_Type type, Event_Timeline_Time occurred_at) : Key_Event(type, occurred_at) {} std::unique_ptr make_input_event(const nlohmann::json& input, Input_Viewport viewport) { if (!input.is_object()) throw std::invalid_argument("input event must be an object"); const auto type = magic_enum::enum_cast( input.value("type", std::string{})); if (!type) throw std::invalid_argument("input event type is unknown"); const auto occurred_at = input_timeline_time( input.at("time_milliseconds").get()); switch (*type) { case Event_Type::resize: { auto event = std::make_unique(occurred_at); if (const auto size = input.find("old_size"); size != input.end() && size->is_object()) { event->old_size.width = size->value("width", 0U); event->old_size.height = size->value("height", 0U); } if (const auto size = input.find("new_size"); size != input.end() && size->is_object()) { event->new_size.width = size->value("width", 0U); event->new_size.height = size->value("height", 0U); } return event; } case Event_Type::pointer_move: case Event_Type::pointer_press: case Event_Type::pointer_release: { auto event = std::make_unique(*type, occurred_at); read_pointer(input, *event, viewport); return event; } case Event_Type::wheel: { auto event = std::make_unique(occurred_at); read_pointer(input, *event, viewport); event->pixel_delta_x = std::clamp( input.value("pixel_delta_x", 0.0), -4096.0, 4096.0); event->pixel_delta_y = std::clamp( input.value("pixel_delta_y", 0.0), -4096.0, 4096.0); event->angle_delta_x = std::clamp( input.value("angle_delta_x", 0.0), -120.0, 120.0); event->angle_delta_y = std::clamp( input.value("angle_delta_y", 0.0), -120.0, 120.0); return event; } case Event_Type::key_press: case Event_Type::key_release: { auto event = std::make_unique(*type, occurred_at); event->key = magic_enum::enum_cast( input.value("key", std::string{"unknown"})).value_or(Key::unknown); event->native_key = input.value("native_key", 0U); event->modifiers = static_cast( std::clamp(input.value("modifiers", 0), 0, 15)); event->auto_repeat = input.value("auto_repeat", false); return event; } case Event_Type::show: case Event_Type::hide: case Event_Type::leave: return std::make_unique(*type, occurred_at); } throw std::logic_error("unhandled input event type"); } }