214 lines
7.6 KiB
C++
214 lines
7.6 KiB
C++
#include "Web_Event_Adapter.h"
|
|
|
|
#include <json/json.h>
|
|
|
|
#include <algorithm>
|
|
#include <cmath>
|
|
#include <memory>
|
|
#include <string>
|
|
|
|
namespace renderive::web {
|
|
namespace {
|
|
|
|
std::optional<Json::Value> parse_json(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()) {
|
|
return std::nullopt;
|
|
}
|
|
return root;
|
|
}
|
|
|
|
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<Web_Event> 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<Web_Event> 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<Web_Event> 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<Web_Event> 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<Web_Event>(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_Event>(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_Event>(Set_Gain{*value})
|
|
: std::nullopt;
|
|
}
|
|
if (control == "max_hold") {
|
|
const auto value = boolean(root, "value");
|
|
return value ? std::optional<Web_Event>(Set_Max_Hold{*value}) : std::nullopt;
|
|
}
|
|
if (control == "smoothing") {
|
|
const auto value = boolean(root, "value");
|
|
return value ? std::optional<Web_Event>(Set_Smoothing{*value}) : std::nullopt;
|
|
}
|
|
if (control == "clear_selection")
|
|
return Clear_Selection{};
|
|
return std::nullopt;
|
|
}
|
|
|
|
} // namespace
|
|
|
|
std::optional<Web_Event> 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{{std::clamp(static_cast<int>(*width), 240, 1920),
|
|
std::clamp(static_cast<int>(*height), 180, 1200)}};
|
|
}
|
|
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);
|
|
return std::nullopt;
|
|
}
|
|
|
|
} // namespace renderive::web
|