230 lines
8.4 KiB
C++
230 lines
8.4 KiB
C++
#include "Web_Event_Adapter.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";
|
|
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<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))}};
|
|
}
|
|
Gallery_Request_Kind kind;
|
|
if (type == "gallery_catalog")
|
|
kind = Gallery_Request_Kind::Catalog;
|
|
else if (type == "gallery_open")
|
|
kind = Gallery_Request_Kind::Open;
|
|
else if (type == "gallery_patch")
|
|
kind = Gallery_Request_Kind::Patch;
|
|
else if (type == "gallery_action")
|
|
kind = Gallery_Request_Kind::Action;
|
|
else if (type == "gallery_observe")
|
|
kind = Gallery_Request_Kind::Observe;
|
|
else
|
|
return std::nullopt;
|
|
return Gallery_Request{kind, std::string(source)};
|
|
}
|
|
|
|
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();
|
|
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<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
|