#include "Web_Event_Adapter.h" #include #include #include #include #include namespace renderive::web { namespace { std::optional parse_json(std::string_view message) { Json::CharReaderBuilder builder; builder["collectComments"] = false; std::unique_ptr reader(builder.newCharReader()); Json::Value root; std::string errors; if (!reader->parse(message.data(), message.data() + message.size(), &root, &errors) || !root.isObject()) { return std::nullopt; } return root; } std::optional finite_number(const Json::Value& root, const char* name) { const auto& value = root[name]; if (!value.isNumeric()) return std::nullopt; const double result = value.asDouble(); return std::isfinite(result) ? std::optional(result) : std::nullopt; } std::optional boolean(const Json::Value& root, const char* name) { const auto& value = root[name]; return value.isBool() ? std::optional(value.asBool()) : std::nullopt; } Keyboard_Modifier modifiers(const Json::Value& root) { const int value = root["modifiers"].isInt() ? root["modifiers"].asInt() : 0; return static_cast(std::clamp(value, 0, 15)); } Mouse_Button mouse_button(const Json::Value& root) { const std::string value = root["button"].isString() ? root["button"].asString() : "none"; if (value == "left") return Mouse_Button::Left; if (value == "right") return Mouse_Button::Right; if (value == "middle") return Mouse_Button::Middle; return Mouse_Button::None; } Key key(const Json::Value& root) { const std::string value = root["key"].isString() ? root["key"].asString() : ""; if (value == "Escape") return Key::Escape; if (value == "Enter") return Key::Enter; if (value == "Space") return Key::Space; if (value == "Delete") return Key::Delete; if (value == "Backspace") return Key::Backspace; if (value == "ArrowLeft") return Key::Left; if (value == "ArrowRight") return Key::Right; if (value == "ArrowUp") return Key::Up; if (value == "ArrowDown") return Key::Down; return Key::Unknown; } std::optional pointer_event(const Json::Value& root, Event_Type type) { const auto x = finite_number(root, "x"); const auto y = finite_number(root, "y"); if (!x || !y) return std::nullopt; Pointer_Event event(type); event.position = {*x, *y}; event.global_position = event.position; event.button = mouse_button(root); const int buttons = root["buttons"].isInt() ? root["buttons"].asInt() : 0; event.buttons = static_cast(std::clamp(buttons, 0, 7)); event.modifiers = modifiers(root); return event; } std::optional wheel_event(const Json::Value& root) { const auto x = finite_number(root, "x"); const auto y = finite_number(root, "y"); const auto pixel_x = finite_number(root, "pixelDeltaX"); const auto pixel_y = finite_number(root, "pixelDeltaY"); const auto angle_x = finite_number(root, "angleDeltaX"); const auto angle_y = finite_number(root, "angleDeltaY"); if (!x || !y || !pixel_x || !pixel_y || !angle_x || !angle_y) return std::nullopt; Wheel_Event event; event.position = {*x, *y}; event.global_position = event.position; event.pixel_delta_x = *pixel_x; event.pixel_delta_y = *pixel_y; event.angle_delta_x = *angle_x; event.angle_delta_y = *angle_y; event.modifiers = modifiers(root); const int buttons = root["buttons"].isInt() ? root["buttons"].asInt() : 0; event.buttons = static_cast(std::clamp(buttons, 0, 7)); return event; } std::optional key_event(const Json::Value& root, Event_Type type) { Key_Event event(type); event.key = key(root); event.native_key = root["nativeKey"].isUInt() ? root["nativeKey"].asUInt() : 0; event.modifiers = modifiers(root); event.auto_repeat = root["repeat"].isBool() && root["repeat"].asBool(); return event; } std::optional control_event(const Json::Value& root) { if (!root["control"].isString()) return std::nullopt; const std::string control = root["control"].asString(); if (control == "mode" && root["value"].isString()) { const std::string value = root["value"].asString(); if (value == "AM") return Set_Demo_Mode{Demo_Mode::Am}; if (value == "FM") return Set_Demo_Mode{Demo_Mode::Fm}; if (value == "USB") return Set_Demo_Mode{Demo_Mode::Usb}; if (value == "LSB") return Set_Demo_Mode{Demo_Mode::Lsb}; return std::nullopt; } if (control == "center_frequency_mhz") { const auto value = finite_number(root, "value"); return value && *value >= 1.0 && *value <= 6000.0 ? std::optional(Set_Center_Frequency{*value}) : std::nullopt; } if (control == "bandwidth_khz") { const auto value = finite_number(root, "value"); return value && *value >= 10.0 && *value <= 50000.0 ? std::optional(Set_Bandwidth{*value}) : std::nullopt; } if (control == "gain_db") { const auto value = finite_number(root, "value"); return value && *value >= -30.0 && *value <= 80.0 ? std::optional(Set_Gain{*value}) : std::nullopt; } if (control == "max_hold") { const auto value = boolean(root, "value"); return value ? std::optional(Set_Max_Hold{*value}) : std::nullopt; } if (control == "smoothing") { const auto value = boolean(root, "value"); return value ? std::optional(Set_Smoothing{*value}) : std::nullopt; } if (control == "clear_selection") return Clear_Selection{}; return std::nullopt; } } // namespace std::optional Web_Event_Adapter::decode(std::string_view message) { const auto parsed = parse_json(message); if (!parsed || (*parsed)["category"].asString() != "event" || !(*parsed)["type"].isString()) return std::nullopt; const Json::Value& root = *parsed; const std::string type = root["type"].asString(); if (type == "frame") return Frame_Request{}; if (type == "resize") { const auto width = finite_number(root, "width"); const auto height = finite_number(root, "height"); if (!width || !height) return std::nullopt; return Viewport_Resize{{static_cast(std::clamp(*width, 240.0, 1920.0)), static_cast(std::clamp(*height, 180.0, 1200.0))}}; } if (type == "show") return Event(Event_Type::Show); if (type == "hide") return Event(Event_Type::Hide); if (type == "leave") return Event(Event_Type::Leave); if (type == "pointer_move") return pointer_event(root, Event_Type::Pointer_Move); if (type == "pointer_press") return pointer_event(root, Event_Type::Pointer_Press); if (type == "pointer_release") return pointer_event(root, Event_Type::Pointer_Release); if (type == "wheel") return wheel_event(root); if (type == "key_press") return key_event(root, Event_Type::Key_Press); if (type == "key_release") return key_event(root, Event_Type::Key_Release); if (type == "control") return control_event(root); if (type == "gallery_catalog") return Gallery_Request{Gallery_Request_Kind::Catalog, std::string(message)}; if (type == "gallery_open") return Gallery_Request{Gallery_Request_Kind::Open, std::string(message)}; if (type == "gallery_patch") return Gallery_Request{Gallery_Request_Kind::Patch, std::string(message)}; if (type == "gallery_action") return Gallery_Request{Gallery_Request_Kind::Action, std::string(message)}; if (type == "gallery_observe") return Gallery_Request{Gallery_Request_Kind::Observe, std::string(message)}; return std::nullopt; } } // namespace renderive::web