Program Listing for File CPDJE_Input.cpp
↰ Return to documentation for file (include\input\CPDJE_Input.cpp)
#include "CPDJE_Input.h"
#include "PDJE_CAbi_Input_Private.hpp"
#include "PDJE_Input.hpp"
#include "PDJE_Input_Log.hpp"
#include "PDJE_Input_StateLogic.hpp"
#include "PDJE_LOG_SETTER.hpp"
#include <algorithm>
#include <cstddef>
#include <cstdint>
#include <exception>
#include <string>
#include <vector>
struct PDJE_InputSnapshotHandleV1 {
bool has_input_stream = false;
bool has_midi_stream = false;
std::vector<PDJE_Input_Log> input_events;
std::vector<PDJE_MIDI::MIDI_EV> midi_events;
};
namespace {
PDJE_Input *
GetInput(PDJE_InputHandleV1 *handle) noexcept
{
return handle == nullptr ? nullptr : static_cast<PDJE_Input *>(handle->input);
}
const PDJE_Input *
GetInput(const PDJE_InputHandleV1 *handle) noexcept
{
return handle == nullptr ? nullptr : static_cast<const PDJE_Input *>(handle->input);
}
void
RefreshInputDataLineCache(PDJE_InputHandleV1 *handle) noexcept
{
if (handle == nullptr) {
return;
}
auto *input = GetInput(handle);
if (input == nullptr) {
handle->input_arena = nullptr;
handle->midi_datas = nullptr;
return;
}
const auto line = input->PullOutDataLine();
handle->input_arena = line.input_arena;
handle->midi_datas = line.midi_datas;
}
template <typename Fn> PDJE_InputResultV1
GuardInputAbi(const char *context, Fn &&fn) noexcept
{
try {
return fn();
} catch (const std::exception &e) {
critlog(context);
critlog(e.what());
return PDJE_INPUT_RESULT_INTERNAL_ERROR_V1;
} catch (...) {
critlog(context);
return PDJE_INPUT_RESULT_INTERNAL_ERROR_V1;
}
}
template <typename T> bool
StructIsCompatible(const T *value) noexcept
{
if (value == nullptr) {
return false;
}
return value->struct_size == 0 || value->struct_size >= sizeof(T);
}
PDJE_InputStringViewV1
MakeStringView(const std::string &value) noexcept
{
if (value.empty()) {
return {};
}
return PDJE_InputStringViewV1 { value.c_str(), value.size() };
}
PDJE_InputStringViewV1
MakeCountedStringView(const char *value, std::size_t value_size) noexcept
{
if (value == nullptr || value_size == 0) {
return {};
}
return PDJE_InputStringViewV1 { value, value_size };
}
PDJE_InputBytesViewV1
MakeBytesView(const uint8_t *value, std::size_t value_size) noexcept
{
if (value == nullptr || value_size == 0) {
return {};
}
return PDJE_InputBytesViewV1 { value, value_size };
}
PDJE_InputDeviceTypeV1
ToCDeviceType(const PDJE_Dev_Type type) noexcept
{
switch (type) {
case PDJE_Dev_Type::MOUSE:
return PDJE_INPUT_DEVICE_MOUSE_V1;
case PDJE_Dev_Type::KEYBOARD:
return PDJE_INPUT_DEVICE_KEYBOARD_V1;
default:
return PDJE_INPUT_DEVICE_UNKNOWN_V1;
}
}
PDJE_InputStateV1
ToCState(const PDJE_INPUT_STATE state) noexcept
{
switch (state) {
case PDJE_INPUT_STATE::DEVICE_CONFIG_STATE:
return PDJE_INPUT_STATE_DEVICE_CONFIG_V1;
case PDJE_INPUT_STATE::INPUT_LOOP_READY:
return PDJE_INPUT_STATE_LOOP_READY_V1;
case PDJE_INPUT_STATE::INPUT_LOOP_RUNNING:
return PDJE_INPUT_STATE_LOOP_RUNNING_V1;
default:
return PDJE_INPUT_STATE_DEAD_V1;
}
}
bool
CanEnumerate(const PDJE_InputHandleV1 *input) noexcept
{
auto *input_obj = GetInput(const_cast<PDJE_InputHandleV1 *>(input));
return input_obj != nullptr &&
const_cast<PDJE_Input &>(*input_obj).GetState() != PDJE_INPUT_STATE::DEAD;
}
void
ResetDeviceView(PDJE_InputDeviceViewV1 *out_device) noexcept
{
if (out_device == nullptr) {
return;
}
const auto struct_size = out_device->struct_size;
*out_device = {};
out_device->struct_size =
struct_size != 0 ? struct_size : sizeof(*out_device);
}
void
ResetMidiDeviceView(PDJE_MidiDeviceViewV1 *out_device) noexcept
{
if (out_device == nullptr) {
return;
}
const auto struct_size = out_device->struct_size;
*out_device = {};
out_device->struct_size =
struct_size != 0 ? struct_size : sizeof(*out_device);
}
void
ResetInputEventView(PDJE_InputEventViewV1 *out_event) noexcept
{
if (out_event == nullptr) {
return;
}
const auto struct_size = out_event->struct_size;
*out_event = {};
out_event->struct_size =
struct_size != 0 ? struct_size : sizeof(*out_event);
}
void
ResetMidiEventView(PDJE_MidiEventViewV1 *out_event) noexcept
{
if (out_event == nullptr) {
return;
}
const auto struct_size = out_event->struct_size;
*out_event = {};
out_event->struct_size =
struct_size != 0 ? struct_size : sizeof(*out_event);
}
void
ResetSnapshotInfo(PDJE_InputSnapshotInfoV1 *out_info) noexcept
{
if (out_info == nullptr) {
return;
}
const auto struct_size = out_info->struct_size;
*out_info = {};
out_info->struct_size =
struct_size != 0 ? struct_size : sizeof(*out_info);
}
template <typename HandleT, typename ItemT> PDJE_InputResultV1
GatherSelectedItems(const HandleT *list,
const std::size_t *indices,
const std::size_t index_count,
std::vector<ItemT> &out_items) noexcept
{
out_items.clear();
if (index_count == 0) {
return PDJE_INPUT_RESULT_OK_V1;
}
if (list == nullptr || indices == nullptr) {
return PDJE_INPUT_RESULT_INVALID_ARGUMENT_V1;
}
std::vector<std::size_t> seen;
seen.reserve(index_count);
out_items.reserve(index_count);
for (std::size_t i = 0; i < index_count; ++i) {
const auto idx = indices[i];
if (idx >= list->items.size()) {
return PDJE_INPUT_RESULT_OUT_OF_RANGE_V1;
}
if (std::find(seen.begin(), seen.end(), idx) != seen.end()) {
continue;
}
seen.push_back(idx);
out_items.push_back(list->items[idx]);
}
return PDJE_INPUT_RESULT_OK_V1;
}
} // namespace
PDJE_InputResultV1 PDJE_CALL
pdje_input_create_v1(PDJE_InputHandleV1 **out_input)
{
return GuardInputAbi("pdje_input_create_v1 failed", [&]() {
if (out_input == nullptr) {
return PDJE_INPUT_RESULT_INVALID_ARGUMENT_V1;
}
auto *handle = new PDJE_InputHandleV1();
handle->input = new PDJE_Input();
RefreshInputDataLineCache(handle);
*out_input = handle;
return PDJE_INPUT_RESULT_OK_V1;
});
}
void PDJE_CALL
pdje_input_destroy_v1(PDJE_InputHandleV1 *input)
{
delete GetInput(input);
delete input;
}
PDJE_InputResultV1 PDJE_CALL
pdje_input_init_v1(PDJE_InputHandleV1 *input,
void *platform_ctx0,
void *platform_ctx1,
int use_internal_window)
{
return GuardInputAbi("pdje_input_init_v1 failed", [&]() {
if (input == nullptr) {
return PDJE_INPUT_RESULT_INVALID_ARGUMENT_V1;
}
auto *input_obj = GetInput(input);
if (input_obj == nullptr) {
return PDJE_INPUT_RESULT_INTERNAL_ERROR_V1;
}
if (!PDJE_INPUT_STATE_LOGIC::CanInit(input_obj->GetState())) {
return PDJE_INPUT_RESULT_INVALID_STATE_V1;
}
if (!input_obj->Init(platform_ctx0, platform_ctx1, use_internal_window != 0)) {
return PDJE_INPUT_RESULT_OPERATION_FAILED_V1;
}
RefreshInputDataLineCache(input);
return PDJE_INPUT_RESULT_OK_V1;
});
}
PDJE_InputResultV1 PDJE_CALL
pdje_input_kill_v1(PDJE_InputHandleV1 *input)
{
return GuardInputAbi("pdje_input_kill_v1 failed", [&]() {
if (input == nullptr) {
return PDJE_INPUT_RESULT_INVALID_ARGUMENT_V1;
}
auto *input_obj = GetInput(input);
if (input_obj == nullptr) {
return PDJE_INPUT_RESULT_INTERNAL_ERROR_V1;
}
const auto ok = input_obj->Kill();
RefreshInputDataLineCache(input);
return ok ? PDJE_INPUT_RESULT_OK_V1 : PDJE_INPUT_RESULT_OPERATION_FAILED_V1;
});
}
PDJE_InputResultV1 PDJE_CALL
pdje_input_get_state_v1(const PDJE_InputHandleV1 *input,
PDJE_InputStateV1 *out_state)
{
return GuardInputAbi("pdje_input_get_state_v1 failed", [&]() {
if (input == nullptr || out_state == nullptr) {
return PDJE_INPUT_RESULT_INVALID_ARGUMENT_V1;
}
auto *input_obj = GetInput(const_cast<PDJE_InputHandleV1 *>(input));
if (input_obj == nullptr) {
return PDJE_INPUT_RESULT_INTERNAL_ERROR_V1;
}
*out_state = ToCState(const_cast<PDJE_Input &>(*input_obj).GetState());
return PDJE_INPUT_RESULT_OK_V1;
});
}
PDJE_InputResultV1 PDJE_CALL
pdje_input_get_backend_name_v1(PDJE_InputHandleV1 *input,
PDJE_InputStringViewV1 *out_backend)
{
return GuardInputAbi("pdje_input_get_backend_name_v1 failed", [&]() {
if (input == nullptr || out_backend == nullptr) {
return PDJE_INPUT_RESULT_INVALID_ARGUMENT_V1;
}
*out_backend = {};
auto *input_obj = GetInput(input);
if (input_obj == nullptr) {
return PDJE_INPUT_RESULT_INTERNAL_ERROR_V1;
}
input->backend_name_cache = input_obj->GetCurrentInputBackend();
*out_backend = MakeStringView(input->backend_name_cache);
return PDJE_INPUT_RESULT_OK_V1;
});
}
PDJE_InputResultV1 PDJE_CALL
pdje_input_list_devices_v1(PDJE_InputHandleV1 *input,
PDJE_InputDeviceListHandleV1 **out_list)
{
return GuardInputAbi("pdje_input_list_devices_v1 failed", [&]() {
if (input == nullptr || out_list == nullptr) {
return PDJE_INPUT_RESULT_INVALID_ARGUMENT_V1;
}
if (!CanEnumerate(input)) {
return PDJE_INPUT_RESULT_INVALID_STATE_V1;
}
auto *input_obj = GetInput(input);
if (input_obj == nullptr) {
return PDJE_INPUT_RESULT_INTERNAL_ERROR_V1;
}
auto *list = new PDJE_InputDeviceListHandleV1();
list->items = input_obj->GetDevs();
*out_list = list;
return PDJE_INPUT_RESULT_OK_V1;
});
}
size_t PDJE_CALL
pdje_input_device_list_size_v1(const PDJE_InputDeviceListHandleV1 *list)
{
return list != nullptr ? list->items.size() : 0;
}
PDJE_InputResultV1 PDJE_CALL
pdje_input_device_list_get_v1(const PDJE_InputDeviceListHandleV1 *list,
size_t index,
PDJE_InputDeviceViewV1 *out_device)
{
return GuardInputAbi("pdje_input_device_list_get_v1 failed", [&]() {
if (list == nullptr || !StructIsCompatible(out_device)) {
return PDJE_INPUT_RESULT_INVALID_ARGUMENT_V1;
}
if (index >= list->items.size()) {
return PDJE_INPUT_RESULT_OUT_OF_RANGE_V1;
}
ResetDeviceView(out_device);
const auto &item = list->items[index];
out_device->type = ToCDeviceType(item.Type);
out_device->name = MakeStringView(item.Name);
out_device->device_specific_id = MakeStringView(item.device_specific_id);
out_device->struct_size = sizeof(*out_device);
return PDJE_INPUT_RESULT_OK_V1;
});
}
void PDJE_CALL
pdje_input_device_list_destroy_v1(PDJE_InputDeviceListHandleV1 *list)
{
delete list;
}
PDJE_InputResultV1 PDJE_CALL
pdje_input_list_midi_devices_v1(PDJE_InputHandleV1 *input,
PDJE_MidiDeviceListHandleV1 **out_list)
{
return GuardInputAbi("pdje_input_list_midi_devices_v1 failed", [&]() {
if (input == nullptr || out_list == nullptr) {
return PDJE_INPUT_RESULT_INVALID_ARGUMENT_V1;
}
if (!CanEnumerate(input)) {
return PDJE_INPUT_RESULT_INVALID_STATE_V1;
}
auto *input_obj = GetInput(input);
if (input_obj == nullptr) {
return PDJE_INPUT_RESULT_INTERNAL_ERROR_V1;
}
auto *list = new PDJE_MidiDeviceListHandleV1();
list->items = input_obj->GetMIDIDevs();
*out_list = list;
return PDJE_INPUT_RESULT_OK_V1;
});
}
size_t PDJE_CALL
pdje_input_midi_device_list_size_v1(const PDJE_MidiDeviceListHandleV1 *list)
{
return list != nullptr ? list->items.size() : 0;
}
PDJE_InputResultV1 PDJE_CALL
pdje_input_midi_device_list_get_v1(const PDJE_MidiDeviceListHandleV1 *list,
size_t index,
PDJE_MidiDeviceViewV1 *out_device)
{
return GuardInputAbi("pdje_input_midi_device_list_get_v1 failed", [&]() {
if (list == nullptr || !StructIsCompatible(out_device)) {
return PDJE_INPUT_RESULT_INVALID_ARGUMENT_V1;
}
if (index >= list->items.size()) {
return PDJE_INPUT_RESULT_OUT_OF_RANGE_V1;
}
ResetMidiDeviceView(out_device);
const auto &item = list->items[index];
out_device->manufacturer = MakeStringView(item.manufacturer);
out_device->device_name = MakeStringView(item.device_name);
out_device->port_name = MakeStringView(item.port_name);
out_device->display_name = MakeStringView(item.display_name);
out_device->client_handle = item.client;
out_device->port_handle = item.port;
out_device->port_type = static_cast<uint8_t>(item.type);
out_device->struct_size = sizeof(*out_device);
return PDJE_INPUT_RESULT_OK_V1;
});
}
void PDJE_CALL
pdje_input_midi_device_list_destroy_v1(PDJE_MidiDeviceListHandleV1 *list)
{
delete list;
}
PDJE_InputResultV1 PDJE_CALL
pdje_input_config_v1(PDJE_InputHandleV1 *input,
const PDJE_InputDeviceListHandleV1 *devices,
const std::size_t *device_indices,
std::size_t device_index_count,
const PDJE_MidiDeviceListHandleV1 *midi_devices,
const std::size_t *midi_indices,
std::size_t midi_index_count)
{
return GuardInputAbi("pdje_input_config_v1 failed", [&]() {
if (input == nullptr) {
return PDJE_INPUT_RESULT_INVALID_ARGUMENT_V1;
}
auto *input_obj = GetInput(input);
if (input_obj == nullptr) {
return PDJE_INPUT_RESULT_INTERNAL_ERROR_V1;
}
if (!PDJE_INPUT_STATE_LOGIC::CanConfig(input_obj->GetState())) {
return PDJE_INPUT_RESULT_INVALID_STATE_V1;
}
std::vector<DeviceData> selected_devices;
const auto device_result = GatherSelectedItems(
devices, device_indices, device_index_count, selected_devices);
if (device_result != PDJE_INPUT_RESULT_OK_V1) {
return device_result;
}
std::vector<libremidi::input_port> selected_midi_devices;
const auto midi_result = GatherSelectedItems(
midi_devices, midi_indices, midi_index_count, selected_midi_devices);
if (midi_result != PDJE_INPUT_RESULT_OK_V1) {
return midi_result;
}
const auto ok = input_obj->Config(selected_devices, selected_midi_devices);
RefreshInputDataLineCache(input);
return ok
? PDJE_INPUT_RESULT_OK_V1
: PDJE_INPUT_RESULT_OPERATION_FAILED_V1;
});
}
PDJE_InputResultV1 PDJE_CALL
pdje_input_run_v1(PDJE_InputHandleV1 *input)
{
return GuardInputAbi("pdje_input_run_v1 failed", [&]() {
if (input == nullptr) {
return PDJE_INPUT_RESULT_INVALID_ARGUMENT_V1;
}
auto *input_obj = GetInput(input);
if (input_obj == nullptr) {
return PDJE_INPUT_RESULT_INTERNAL_ERROR_V1;
}
if (!PDJE_INPUT_STATE_LOGIC::CanRun(input_obj->GetState())) {
return PDJE_INPUT_RESULT_INVALID_STATE_V1;
}
const auto ok = input_obj->Run();
RefreshInputDataLineCache(input);
return ok ? PDJE_INPUT_RESULT_OK_V1 : PDJE_INPUT_RESULT_OPERATION_FAILED_V1;
});
}
PDJE_InputResultV1 PDJE_CALL
pdje_input_poll_snapshot_v1(PDJE_InputHandleV1 *input,
PDJE_InputSnapshotHandleV1 **out_snapshot)
{
return GuardInputAbi("pdje_input_poll_snapshot_v1 failed", [&]() {
if (input == nullptr || out_snapshot == nullptr) {
return PDJE_INPUT_RESULT_INVALID_ARGUMENT_V1;
}
auto *snapshot = new PDJE_InputSnapshotHandleV1();
auto *input_obj = GetInput(input);
if (input_obj == nullptr) {
return PDJE_INPUT_RESULT_INTERNAL_ERROR_V1;
}
RefreshInputDataLineCache(input);
snapshot->has_input_stream = input->input_arena != nullptr;
snapshot->has_midi_stream = input->midi_datas != nullptr;
if (input->input_arena != nullptr) {
auto *arena = static_cast<PDJE_IPC::PDJE_Input_Transfer *>(input->input_arena);
arena->Receive();
snapshot->input_events = arena->datas;
}
if (input->midi_datas != nullptr) {
auto *midi_buffer = static_cast<Atomic_Double_Buffer<PDJE_MIDI::MIDI_EV> *>(input->midi_datas);
const auto *midi_events = midi_buffer->Get();
if (midi_events != nullptr) {
snapshot->midi_events = *midi_events;
}
}
*out_snapshot = snapshot;
return PDJE_INPUT_RESULT_OK_V1;
});
}
PDJE_InputResultV1 PDJE_CALL
pdje_input_snapshot_describe_v1(const PDJE_InputSnapshotHandleV1 *snapshot,
PDJE_InputSnapshotInfoV1 *out_info)
{
return GuardInputAbi("pdje_input_snapshot_describe_v1 failed", [&]() {
if (snapshot == nullptr || !StructIsCompatible(out_info)) {
return PDJE_INPUT_RESULT_INVALID_ARGUMENT_V1;
}
ResetSnapshotInfo(out_info);
out_info->has_input_stream = snapshot->has_input_stream ? 1 : 0;
out_info->has_midi_stream = snapshot->has_midi_stream ? 1 : 0;
out_info->input_event_count = snapshot->input_events.size();
out_info->midi_event_count = snapshot->midi_events.size();
out_info->struct_size = sizeof(*out_info);
return PDJE_INPUT_RESULT_OK_V1;
});
}
size_t PDJE_CALL
pdje_input_snapshot_input_size_v1(const PDJE_InputSnapshotHandleV1 *snapshot)
{
return snapshot != nullptr ? snapshot->input_events.size() : 0;
}
PDJE_InputResultV1 PDJE_CALL
pdje_input_snapshot_input_get_v1(const PDJE_InputSnapshotHandleV1 *snapshot,
size_t index,
PDJE_InputEventViewV1 *out_event)
{
return GuardInputAbi("pdje_input_snapshot_input_get_v1 failed", [&]() {
if (snapshot == nullptr || !StructIsCompatible(out_event)) {
return PDJE_INPUT_RESULT_INVALID_ARGUMENT_V1;
}
if (index >= snapshot->input_events.size()) {
return PDJE_INPUT_RESULT_OUT_OF_RANGE_V1;
}
ResetInputEventView(out_event);
const auto &item = snapshot->input_events[index];
out_event->type = ToCDeviceType(item.type);
out_event->id = MakeCountedStringView(
item.id, std::min<std::size_t>(item.id_len, sizeof(item.id)));
out_event->name = MakeCountedStringView(
item.name, std::min<std::size_t>(item.name_len, sizeof(item.name)));
out_event->microsecond = item.microSecond;
out_event->keyboard.key_code =
static_cast<uint32_t>(item.event.keyboard.k);
out_event->keyboard.pressed = item.event.keyboard.pressed ? 1 : 0;
out_event->mouse.button_type = item.event.mouse.button_type;
out_event->mouse.wheel_move = item.event.mouse.wheel_move;
out_event->mouse.axis_type =
static_cast<uint32_t>(item.event.mouse.axis_type);
out_event->mouse.x = item.event.mouse.x;
out_event->mouse.y = item.event.mouse.y;
out_event->hid_report = MakeBytesView(
item.hid_event.hid_buffer,
std::min<std::size_t>(
static_cast<std::size_t>(item.hid_event.hid_byte_size),
sizeof(item.hid_event.hid_buffer)));
out_event->struct_size = sizeof(*out_event);
return PDJE_INPUT_RESULT_OK_V1;
});
}
size_t PDJE_CALL
pdje_input_snapshot_midi_size_v1(const PDJE_InputSnapshotHandleV1 *snapshot)
{
return snapshot != nullptr ? snapshot->midi_events.size() : 0;
}
PDJE_InputResultV1 PDJE_CALL
pdje_input_snapshot_midi_get_v1(const PDJE_InputSnapshotHandleV1 *snapshot,
size_t index,
PDJE_MidiEventViewV1 *out_event)
{
return GuardInputAbi("pdje_input_snapshot_midi_get_v1 failed", [&]() {
if (snapshot == nullptr || !StructIsCompatible(out_event)) {
return PDJE_INPUT_RESULT_INVALID_ARGUMENT_V1;
}
if (index >= snapshot->midi_events.size()) {
return PDJE_INPUT_RESULT_OUT_OF_RANGE_V1;
}
ResetMidiEventView(out_event);
const auto &item = snapshot->midi_events[index];
out_event->type = item.type;
out_event->channel = item.ch;
out_event->position = item.pos;
out_event->value = item.value;
out_event->highres_time = item.highres_time;
out_event->port_name = MakeCountedStringView(
item.port_name,
std::min<std::size_t>(item.port_name_len, sizeof(item.port_name)));
out_event->struct_size = sizeof(*out_event);
return PDJE_INPUT_RESULT_OK_V1;
});
}
void PDJE_CALL
pdje_input_snapshot_destroy_v1(PDJE_InputSnapshotHandleV1 *snapshot)
{
delete snapshot;
}