206 lines
7.9 KiB
C++
206 lines
7.9 KiB
C++
#include "Web_Event_Adapter.h"
|
|
#include "Gallery_Enum.h"
|
|
|
|
#include <algorithm>
|
|
#include <cmath>
|
|
#include <memory>
|
|
#include <string>
|
|
|
|
namespace renderive::web {
|
|
namespace {
|
|
|
|
std::optional<double> 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<double>(result) : std::nullopt;
|
|
}
|
|
|
|
std::optional<bool> boolean(const Json::Value& root, const char* name) {
|
|
const auto& value = root[name];
|
|
return value.isBool() ? std::optional<bool>(value.asBool()) : std::nullopt;
|
|
}
|
|
|
|
Keyboard_Modifier modifiers(const Json::Value& root) {
|
|
const int value = root["modifiers"].isInt() ? root["modifiers"].asInt() : 0;
|
|
return static_cast<Keyboard_Modifier>(std::clamp(value, 0, 15));
|
|
}
|
|
|
|
Mouse_Button mouse_button(const Json::Value& root) {
|
|
const std::string value = root["button"].isString() ? root["button"].asString() : "none";
|
|
return gallery_enum_cast<Mouse_Button>(value).value_or(Mouse_Button::None);
|
|
}
|
|
|
|
Key key(const Json::Value& root) {
|
|
const std::string value = root["key"].isString() ? root["key"].asString() : "";
|
|
if (const auto direct = magic_enum::enum_cast<Key>(value))
|
|
return *direct;
|
|
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<Render_2D_Web_Events> 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<Mouse_Button_Mask>(std::clamp(buttons, 0, 7));
|
|
event.modifiers = modifiers(root);
|
|
return event;
|
|
}
|
|
|
|
std::optional<Render_2D_Web_Events> 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<Mouse_Button_Mask>(std::clamp(buttons, 0, 7));
|
|
return event;
|
|
}
|
|
|
|
std::optional<Render_2D_Web_Events> 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;
|
|
}
|
|
|
|
} // namespace
|
|
|
|
std::optional<Json::Value> parse_web_event(std::string_view message) {
|
|
Json::CharReaderBuilder builder;
|
|
builder["collectComments"] = false;
|
|
std::unique_ptr<Json::CharReader> reader(builder.newCharReader());
|
|
Json::Value root;
|
|
std::string errors;
|
|
if (!reader->parse(message.data(), message.data() + message.size(), &root, &errors) ||
|
|
!root.isObject() || root["category"].asString() != "event" ||
|
|
!root["type"].isString()) {
|
|
return std::nullopt;
|
|
}
|
|
return root;
|
|
}
|
|
|
|
std::optional<Web_Transport_Events> Web_Transport_Event_Decoder::decode(
|
|
const Json::Value& root, std::string_view source) {
|
|
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<int>(std::clamp(*width, 240.0, 1920.0)),
|
|
static_cast<int>(std::clamp(*height, 180.0, 1200.0))}};
|
|
}
|
|
constexpr std::string_view gallery_prefix = "gallery_";
|
|
if (!std::string_view(type).starts_with(gallery_prefix))
|
|
return std::nullopt;
|
|
const auto kind = gallery_enum_cast<Gallery_Request_Kind>(
|
|
std::string_view(type).substr(gallery_prefix.size()));
|
|
return kind ? std::optional<Web_Transport_Events>(
|
|
Gallery_Request{*kind, std::string(source)})
|
|
: std::nullopt;
|
|
}
|
|
|
|
std::optional<Render_2D_Web_Events> Render_2D_Web_Event_Decoder::decode(
|
|
const Json::Value& root, std::string_view) {
|
|
const std::string type = root["type"].asString();
|
|
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);
|
|
return std::nullopt;
|
|
}
|
|
|
|
std::optional<Web_Demo_Control_Events> Web_Demo_Control_Decoder::decode(
|
|
const Json::Value& root, std::string_view) {
|
|
if (root["type"].asString() != "control" || !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();
|
|
const auto mode = magic_enum::enum_cast<Demo_Mode>(
|
|
value, magic_enum::case_insensitive);
|
|
return mode ? std::optional<Web_Demo_Control_Events>(Set_Demo_Mode{*mode})
|
|
: std::nullopt;
|
|
}
|
|
if (control == "center_frequency_mhz") {
|
|
const auto value = finite_number(root, "value");
|
|
return value && *value >= 1.0 && *value <= 6000.0
|
|
? std::optional<Web_Demo_Control_Events>(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<Web_Demo_Control_Events>(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<Web_Demo_Control_Events>(Set_Gain{*value})
|
|
: std::nullopt;
|
|
}
|
|
if (control == "max_hold") {
|
|
const auto value = boolean(root, "value");
|
|
return value ? std::optional<Web_Demo_Control_Events>(Set_Max_Hold{*value})
|
|
: std::nullopt;
|
|
}
|
|
if (control == "smoothing") {
|
|
const auto value = boolean(root, "value");
|
|
return value ? std::optional<Web_Demo_Control_Events>(Set_Smoothing{*value})
|
|
: std::nullopt;
|
|
}
|
|
if (control == "clear_selection")
|
|
return Clear_Selection{};
|
|
return std::nullopt;
|
|
}
|
|
|
|
} // namespace renderive::web
|