Browse Source

Replace custom message implementation by host context creation

Signed-off-by: falkTX <falktx@falktx.com>
pull/338/head
falkTX 4 years ago
parent
commit
b2109439bf
Signed by: falkTX <falktx@falktx.com> GPG Key ID: CDBAA37ABC74FBA0
7 changed files with 208 additions and 465 deletions
  1. +76
    -429
      distrho/src/DistrhoPluginVST3.cpp
  2. +39
    -16
      distrho/src/DistrhoUIVST3.cpp
  3. +5
    -5
      distrho/src/travesty/base.h
  4. +24
    -0
      distrho/src/travesty/component.h
  5. +1
    -1
      distrho/src/travesty/edit_controller.h
  6. +49
    -0
      distrho/src/travesty/host.h
  7. +14
    -14
      distrho/src/travesty/message.h

+ 76
- 429
distrho/src/DistrhoPluginVST3.cpp View File

@@ -35,7 +35,7 @@
#include "travesty/component.h"
#include "travesty/edit_controller.h"
#include "travesty/factory.h"
#include "travesty/message.h"
#include "travesty/host.h"

#include <atomic>
#include <map>
@@ -49,6 +49,7 @@
* - hide program parameter?
* - deal with parameter triggers
* - MIDI program changes
* - MIDI sysex
* - append MIDI input events in a sorted way
* - decode version number (0x010203 -> 1.2.3)
* - bus arrangements
@@ -60,7 +61,6 @@
* - set factory sub_categories
* - set factory email (needs new DPF API, useful for LV2 as well)
* - do something with get_controller_class_id and set_io_mode?
* - replace dpf_message_create with host-side message creation
*/

START_NAMESPACE_DISTRHO
@@ -161,6 +161,8 @@ const char* tuid2str(const v3_tuid iid)
return "{v3_event_list}";
if (v3_tuid_match(iid, v3_funknown_iid))
return "{v3_funknown}";
if (v3_tuid_match(iid, v3_host_application_iid))
return "{v3_host_application_iid}";
if (v3_tuid_match(iid, v3_message_iid))
return "{v3_message_iid}";
if (v3_tuid_match(iid, v3_midi_mapping_iid))
@@ -286,15 +288,10 @@ static constexpr void (*const snprintf_u32)(char*, uint32_t, size_t) = snprintf_
static constexpr void (*const snprintf_f32_utf16)(int16_t*, float, size_t) = snprintf_utf16_t<float, format_f32>;
static constexpr void (*const snprintf_u32_utf16)(int16_t*, uint32_t, size_t) = snprintf_utf16_t<uint32_t, format_u32>;

// --------------------------------------------------------------------------------------------------------------------
// create message object (implementation comes later)

v3_message** dpf_message_create(const char* id);

// --------------------------------------------------------------------------------------------------------------------
// dpf_plugin_view_create (implemented on UI side)

v3_plugin_view** dpf_plugin_view_create(void* instancePointer, double sampleRate);
v3_plugin_view** dpf_plugin_view_create(v3_host_application** host, void* instancePointer, double sampleRate);

// --------------------------------------------------------------------------------------------------------------------

@@ -324,12 +321,13 @@ class PluginVst3
} inputBuses, outputBuses;

public:
PluginVst3()
PluginVst3(v3_host_application** const context)
: fPlugin(this, writeMidiCallback, requestParameterValueChangeCallback),
fComponentHandler(nullptr),
#if DISTRHO_PLUGIN_HAS_UI
fConnection(nullptr),
#endif
fHostContext(context),
fParameterOffset(fPlugin.getParameterOffset()),
fRealParameterCount(fParameterOffset + fPlugin.getParameterCount()),
fParameterValues(nullptr),
@@ -1848,6 +1846,7 @@ private:
#if DISTRHO_PLUGIN_HAS_UI
v3_connection_point** fConnection;
#endif
v3_host_application** const fHostContext;

// Temporary data
const uint32_t fParameterOffset;
@@ -1941,9 +1940,24 @@ private:
// ----------------------------------------------------------------------------------------------------------------
// helper functions called during message passing, can block

void sendSampleRateToUI(const double sampleRate)
v3_message** createMessage(const char* const id) const
{
DISTRHO_SAFE_ASSERT_RETURN(fHostContext != nullptr, nullptr);

v3_tuid iid;
memcpy(iid, v3_message_iid, sizeof(v3_tuid));
v3_message** msg = nullptr;
const v3_result res = v3_cpp_obj(fHostContext)->create_instance(fHostContext, iid, iid, (void**)&msg);
DISTRHO_SAFE_ASSERT_INT_RETURN(res == V3_TRUE, res, nullptr);
DISTRHO_SAFE_ASSERT_RETURN(msg != nullptr, nullptr);

v3_cpp_obj(msg)->set_message_id(msg, id);
return msg;
}

void sendSampleRateToUI(const double sampleRate) const
{
v3_message** const message = dpf_message_create("sample-rate");
v3_message** const message = createMessage("sample-rate");
DISTRHO_SAFE_ASSERT_RETURN(message != nullptr,);

v3_attribute_list** const attrlist = v3_cpp_obj(message)->get_attributes(message);
@@ -1956,9 +1970,9 @@ private:
v3_cpp_obj_unref(message);
}

void sendParameterChangeToUI(const v3_param_id rindex, const double value)
void sendParameterChangeToUI(const v3_param_id rindex, const double value) const
{
v3_message** const message = dpf_message_create("parameter-set");
v3_message** const message = createMessage("parameter-set");
DISTRHO_SAFE_ASSERT_RETURN(message != nullptr,);

v3_attribute_list** const attrlist = v3_cpp_obj(message)->get_attributes(message);
@@ -1972,9 +1986,9 @@ private:
v3_cpp_obj_unref(message);
}

void sendStateChangeToUI(const char* const key, const char* const value)
void sendStateChangeToUI(const char* const key, const char* const value) const
{
v3_message** const message = dpf_message_create("state-set");
v3_message** const message = createMessage("state-set");
DISTRHO_SAFE_ASSERT_RETURN(message != nullptr,);

v3_attribute_list** const attrlist = v3_cpp_obj(message)->get_attributes(message);
@@ -1992,9 +2006,9 @@ private:
v3_cpp_obj_unref(message);
}

void sendReadyToUI()
void sendReadyToUI() const
{
v3_message** const message = dpf_message_create("ready");
v3_message** const message = createMessage("ready");
DISTRHO_SAFE_ASSERT_RETURN(message != nullptr,);

v3_attribute_list** const attrlist = v3_cpp_obj(message)->get_attributes(message);
@@ -2121,385 +2135,6 @@ private:
* VST3 low-level pointer thingies follow, proceed with care.
*/

// --------------------------------------------------------------------------------------------------------------------

struct dpf_attribute_value {
char type; // one of: i, f, s, b
union {
int64_t integer;
double v_float;
int16_t* string;
struct {
void* ptr;
uint32_t size;
} binary;
};
};

static void dpf_attribute_list_free(std::map<std::string, dpf_attribute_value>& attrs)
{
for (auto& it : attrs)
{
dpf_attribute_value& v(it.second);

switch (v.type)
{
case 's':
case 'b':
std::free(v.binary.ptr);
break;
}
}
}

// --------------------------------------------------------------------------------------------------------------------
// dpf_attribute_list_utf8 (the custom variant)

struct v3_attribute_list_utf8_cpp : v3_funknown {
v3_attribute_list_utf8 attr;
};

struct dpf_attribute_list_utf8 : v3_attribute_list_utf8_cpp {
std::map<std::string, dpf_attribute_value>& attrs;

dpf_attribute_list_utf8(std::map<std::string, dpf_attribute_value>& a)
: attrs(a)
{
static const uint8_t* kSupportedInterfaces[] = {
v3_funknown_iid,
v3_attribute_list_utf8_iid
};

// ------------------------------------------------------------------------------------------------------------
// v3_funknown

query_interface = []V3_API(void* self, const v3_tuid iid, void** iface) -> v3_result
{
d_stdout("dpf_attribute_list_utf8::query_interface => %p %s %p", self, tuid2str(iid), iface);
*iface = NULL;
DISTRHO_SAFE_ASSERT_RETURN(self != nullptr, V3_NO_INTERFACE);

for (const uint8_t* interface_iid : kSupportedInterfaces)
{
if (v3_tuid_match(interface_iid, iid))
{
*iface = self;
return V3_OK;
}
}

return V3_NO_INTERFACE;
};

// there is only a single instance of this, so we don't have to care here
ref = []V3_API(void*) -> uint32_t { return 1; };
unref = []V3_API(void*) -> uint32_t { return 0; };

// ------------------------------------------------------------------------------------------------------------
// v3_attribute_list_utf8

attr.set_string_utf8 = []V3_API(void* self, const char* id, const char* string) -> v3_result
{
dpf_attribute_list_utf8* const attr = *(dpf_attribute_list_utf8**)self;
DISTRHO_SAFE_ASSERT_RETURN(attr != nullptr, V3_NOT_INITIALISED);

const uint32_t size = std::strlen(string) + 1;
int16_t* const copy = (int16_t*)std::malloc(sizeof(int16_t) * size);
DISTRHO_SAFE_ASSERT_RETURN(copy != nullptr, V3_NOMEM);

DISTRHO_NAMESPACE::strncpy_utf16(copy, string, size);

dpf_attribute_value& attrval(attr->attrs[id]);
attrval.type = 's';
attrval.binary.ptr = copy;
attrval.binary.size = sizeof(int16_t) * size;
return V3_OK;
};

attr.get_string_utf8 = []V3_API(void* self, const char* id, char*, uint32_t) -> v3_result
{
dpf_attribute_list_utf8* const attr = *(dpf_attribute_list_utf8**)self;
DISTRHO_SAFE_ASSERT_RETURN(attr != nullptr, V3_NOT_INITIALISED);

if (attr->attrs.find(id) == attr->attrs.end())
return V3_INVALID_ARG;

return V3_NOT_IMPLEMENTED;
};
}
};

// --------------------------------------------------------------------------------------------------------------------
// dpf_attribute_list

struct dpf_attribute_list : v3_attribute_list_cpp {
ScopedPointer<dpf_attribute_list_utf8> attrutf8;
std::map<std::string, dpf_attribute_value> attrs;

dpf_attribute_list()
{
static const uint8_t* kSupportedInterfacesBase[] = {
v3_funknown_iid,
v3_attribute_list_iid
};

// ------------------------------------------------------------------------------------------------------------
// v3_funknown

query_interface = []V3_API(void* self, const v3_tuid iid, void** iface) -> v3_result
{
// d_stdout("dpf_attribute_list::query_interface => %p %s %p", self, tuid2str(iid), iface);
*iface = NULL;
DISTRHO_SAFE_ASSERT_RETURN(self != nullptr, V3_NO_INTERFACE);

for (const uint8_t* interface_iid : kSupportedInterfacesBase)
{
if (v3_tuid_match(interface_iid, iid))
{
*iface = self;
return V3_OK;
}
}

dpf_attribute_list* const attr = *(dpf_attribute_list**)self;
DISTRHO_SAFE_ASSERT_RETURN(attr != nullptr, V3_NO_INTERFACE);

if (v3_tuid_match(v3_attribute_list_utf8_iid, iid))
{
if (attr->attrutf8 == nullptr)
attr->attrutf8 = new dpf_attribute_list_utf8(attr->attrs);
*iface = &attr->attrutf8;
return V3_OK;
}

return V3_NO_INTERFACE;
};

// there is only a single instance of this, so we don't have to care here
ref = []V3_API(void*) -> uint32_t { return 1; };
unref = []V3_API(void*) -> uint32_t { return 0; };

// ------------------------------------------------------------------------------------------------------------
// v3_attribute_list

attrlist.set_int = []V3_API(void* self, const char* id, int64_t value) -> v3_result
{
dpf_attribute_list* const attr = *(dpf_attribute_list**)self;
DISTRHO_SAFE_ASSERT_RETURN(attr != nullptr, V3_NOT_INITIALISED);

dpf_attribute_value& attrval(attr->attrs[id]);
attrval.type = 'i';
attrval.integer = value;
return V3_OK;
};

attrlist.get_int = []V3_API(void* self, const char* id, int64_t* value) -> v3_result
{
dpf_attribute_list* const attr = *(dpf_attribute_list**)self;
DISTRHO_SAFE_ASSERT_RETURN(attr != nullptr, V3_NOT_INITIALISED);

if (attr->attrs.find(id) == attr->attrs.end())
return V3_INVALID_ARG;

const dpf_attribute_value& attrval(attr->attrs[id]);
DISTRHO_SAFE_ASSERT_RETURN(attrval.type == 'i', V3_INVALID_ARG);

*value = attrval.integer;
return V3_OK;
};

attrlist.set_float = []V3_API(void* self, const char* id, double value) -> v3_result
{
dpf_attribute_list* const attr = *(dpf_attribute_list**)self;
DISTRHO_SAFE_ASSERT_RETURN(attr != nullptr, V3_NOT_INITIALISED);

dpf_attribute_value& attrval(attr->attrs[id]);
attrval.type = 'f';
attrval.v_float = value;
return V3_OK;
};

attrlist.get_float = []V3_API(void* self, const char* id, double* value) -> v3_result
{
dpf_attribute_list* const attr = *(dpf_attribute_list**)self;
DISTRHO_SAFE_ASSERT_RETURN(attr != nullptr, V3_NOT_INITIALISED);

if (attr->attrs.find(id) == attr->attrs.end())
return V3_INVALID_ARG;

const dpf_attribute_value& attrval(attr->attrs[id]);
DISTRHO_SAFE_ASSERT_RETURN(attrval.type == 'f', V3_INVALID_ARG);

*value = attrval.v_float;
return V3_OK;
};

attrlist.set_string = []V3_API(void* self, const char* id, const int16_t* /*string*/) -> v3_result
{
dpf_attribute_list* const attr = *(dpf_attribute_list**)self;
DISTRHO_SAFE_ASSERT_RETURN(attr != nullptr, V3_NOT_INITIALISED);

dpf_attribute_value& attrval(attr->attrs[id]);
attrval.type = 's';
attrval.binary.ptr = nullptr;
attrval.binary.size = 0;
return V3_NOT_IMPLEMENTED;
};

attrlist.get_string = []V3_API(void* self, const char* id, int16_t* /*string*/, uint32_t /*size*/) -> v3_result
{
dpf_attribute_list* const attr = *(dpf_attribute_list**)self;
DISTRHO_SAFE_ASSERT_RETURN(attr != nullptr, V3_NOT_INITIALISED);

if (attr->attrs.find(id) == attr->attrs.end())
return V3_INVALID_ARG;

const dpf_attribute_value& attrval(attr->attrs[id]);
DISTRHO_SAFE_ASSERT_RETURN(attrval.type == 's', V3_INVALID_ARG);

return V3_NOT_IMPLEMENTED;
};

attrlist.set_binary = []V3_API(void* self, const char* id, const void* data, uint32_t size) -> v3_result
{
dpf_attribute_list* const attr = *(dpf_attribute_list**)self;
DISTRHO_SAFE_ASSERT_RETURN(attr != nullptr, V3_NOT_INITIALISED);

void* const copy = std::malloc(size);
DISTRHO_SAFE_ASSERT_RETURN(copy != nullptr, V3_NOMEM);

std::memcpy(copy, data, size);

dpf_attribute_value& attrval(attr->attrs[id]);
attrval.type = 'b';
attrval.binary.ptr = copy;
attrval.binary.size = size;
return V3_NOT_IMPLEMENTED;
};

attrlist.get_binary = []V3_API(void* self, const char* id, const void** data, uint32_t* size) -> v3_result
{
dpf_attribute_list* const attr = *(dpf_attribute_list**)self;
DISTRHO_SAFE_ASSERT_RETURN(attr != nullptr, V3_NOT_INITIALISED);

if (attr->attrs.find(id) == attr->attrs.end())
return V3_INVALID_ARG;

const dpf_attribute_value& attrval(attr->attrs[id]);
DISTRHO_SAFE_ASSERT_RETURN(attrval.type == 's' || attrval.type == 'b', V3_INVALID_ARG);

*data = attrval.binary.ptr;
*size = attrval.binary.size;
return V3_OK;
};
}
};

// --------------------------------------------------------------------------------------------------------------------
// dpf_message

struct dpf_message : v3_message_cpp {
std::atomic<int> refcounter;
ScopedPointer<dpf_message>* self;
ScopedPointer<dpf_attribute_list> attrlist;
String id;

dpf_message(ScopedPointer<dpf_message>* const s, const char* const id2)
: self(s),
id(id2)
{
static const uint8_t* kSupportedInterfaces[] = {
v3_funknown_iid,
v3_message_iid
};

// ------------------------------------------------------------------------------------------------------------
// v3_funknown

query_interface = []V3_API(void* self, const v3_tuid iid, void** iface) -> v3_result
{
d_stdout("dpf_plugin_view::query_interface => %p %s %p", self, tuid2str(iid), iface);
*iface = NULL;
DISTRHO_SAFE_ASSERT_RETURN(self != nullptr, V3_NO_INTERFACE);

for (const uint8_t* interface_iid : kSupportedInterfaces)
{
if (v3_tuid_match(interface_iid, iid))
{
*iface = self;
return V3_OK;
}
}

return V3_NO_INTERFACE;
};

ref = []V3_API(void* const self) -> uint32_t
{
d_stdout("dpf_message::ref => %p", self);
dpf_message** const messageptr = static_cast<dpf_message**>(self);
DISTRHO_SAFE_ASSERT_RETURN(messageptr != nullptr, 0);
dpf_message* const message = *messageptr;
DISTRHO_SAFE_ASSERT_RETURN(message != nullptr, 0);

return ++message->refcounter;
};

unref = []V3_API(void* const self) -> uint32_t
{
dpf_message** const messageptr = static_cast<dpf_message**>(self);
DISTRHO_SAFE_ASSERT_RETURN(messageptr != nullptr, 0);
dpf_message* const message = *messageptr;
DISTRHO_SAFE_ASSERT_RETURN(message != nullptr, 0);

if (const int refcounter = --message->refcounter)
return refcounter;

if (message->attrlist != nullptr)
dpf_attribute_list_free(message->attrlist->attrs);

*message->self = nullptr;
delete messageptr;
return 0;
};

msg.get_message_id = []V3_API(void* const self) -> const char*
{
dpf_message* const message = *(dpf_message**)self;
DISTRHO_SAFE_ASSERT_RETURN(message != nullptr, nullptr);

return message->id;
};

msg.set_message_id = []V3_API(void* const self, const char* const id) -> void
{
d_stdout("dpf_message::set_message_id => %p %s", self, id);
dpf_message* const message = *(dpf_message**)self;
DISTRHO_SAFE_ASSERT_RETURN(message != nullptr,);

message->id = id;
};

msg.get_attributes = []V3_API(void* const self) -> v3_attribute_list**
{
dpf_message* const message = *(dpf_message**)self;
DISTRHO_SAFE_ASSERT_RETURN(message != nullptr, nullptr);

if (message->attrlist == nullptr)
message->attrlist = new dpf_attribute_list();

return (v3_attribute_list**)&message->attrlist;
};
}
};

v3_message** dpf_message_create(const char* const id)
{
ScopedPointer<dpf_message>* const messageptr = new ScopedPointer<dpf_message>;
*messageptr = new dpf_message(messageptr, id);
return static_cast<v3_message**>(static_cast<void*>(messageptr));
}

#if DISTRHO_PLUGIN_HAS_UI
// --------------------------------------------------------------------------------------------------------------------
// dpf_dsp_connection_point
@@ -2561,7 +2196,7 @@ struct dpf_dsp_connection_point : v3_connection_point_cpp {
// ------------------------------------------------------------------------------------------------------------
// v3_connection_point

point.connect = []V3_API(void* self, struct v3_connection_point** other) -> v3_result
point.connect = []V3_API(void* self, v3_connection_point** other) -> v3_result
{
d_stdout("dpf_dsp_connection_point::connect => %p %p", self, other);
dpf_dsp_connection_point* const point = *(dpf_dsp_connection_point**)self;
@@ -2578,7 +2213,7 @@ struct dpf_dsp_connection_point : v3_connection_point_cpp {
return V3_OK;
};

point.disconnect = []V3_API(void* self, struct v3_connection_point** other) -> v3_result
point.disconnect = []V3_API(void* self, v3_connection_point** other) -> v3_result
{
d_stdout("dpf_dsp_connection_point::disconnect => %p %p", self, other);
dpf_dsp_connection_point* const point = *(dpf_dsp_connection_point**)self;
@@ -2595,7 +2230,7 @@ struct dpf_dsp_connection_point : v3_connection_point_cpp {
return V3_OK;
};

point.notify = []V3_API(void* self, struct v3_message** message) -> v3_result
point.notify = []V3_API(void* self, v3_message** message) -> v3_result
{
dpf_dsp_connection_point* const point = *(dpf_dsp_connection_point**)self;
DISTRHO_SAFE_ASSERT_RETURN(point != nullptr, V3_NOT_INITIALISED);
@@ -2751,11 +2386,13 @@ struct dpf_edit_controller : v3_edit_controller_cpp {
bool initialized;
// cached values
v3_component_handler** handler;
v3_plugin_base::v3_funknown** hostContext;

dpf_edit_controller(ScopedPointer<PluginVst3>& v)
: vst3(v),
initialized(false),
handler(nullptr)
handler(nullptr),
hostContext(nullptr)
{
static const uint8_t* kSupportedInterfacesBase[] = {
v3_funknown_iid,
@@ -2814,9 +2451,9 @@ struct dpf_edit_controller : v3_edit_controller_cpp {
// ------------------------------------------------------------------------------------------------------------
// v3_plugin_base

base.initialise = []V3_API(void* self, struct v3_plugin_base::v3_funknown *context) -> v3_result
base.initialize = []V3_API(void* self, v3_plugin_base::v3_funknown** context) -> v3_result
{
d_stdout("dpf_edit_controller::initialise => %p %p", self, context);
d_stdout("dpf_edit_controller::initialize => %p %p", self, context);
dpf_edit_controller* const controller = *(dpf_edit_controller**)self;
DISTRHO_SAFE_ASSERT_RETURN(controller != nullptr, V3_NOT_INITIALISED);

@@ -2824,6 +2461,7 @@ struct dpf_edit_controller : v3_edit_controller_cpp {
DISTRHO_SAFE_ASSERT_RETURN(! initialized, V3_INVALID_ARG);

controller->initialized = true;
controller->hostContext = context;
return V3_OK;
};

@@ -2837,13 +2475,14 @@ struct dpf_edit_controller : v3_edit_controller_cpp {
DISTRHO_SAFE_ASSERT_RETURN(initialized, V3_INVALID_ARG);

controller->initialized = false;
controller->hostContext = nullptr;
return V3_OK;
};

// ------------------------------------------------------------------------------------------------------------
// v3_edit_controller

controller.set_component_state = []V3_API(void* self, v3_bstream* stream) -> v3_result
ctrl.set_component_state = []V3_API(void* self, v3_bstream* stream) -> v3_result
{
d_stdout("dpf_edit_controller::set_component_state => %p %p", self, stream);
dpf_edit_controller* const controller = *(dpf_edit_controller**)self;
@@ -2860,7 +2499,7 @@ struct dpf_edit_controller : v3_edit_controller_cpp {
return V3_OK;
};

controller.set_state = []V3_API(void* self, v3_bstream* stream) -> v3_result
ctrl.set_state = []V3_API(void* self, v3_bstream* stream) -> v3_result
{
d_stdout("dpf_edit_controller::set_state => %p %p", self, stream);
dpf_edit_controller* const controller = *(dpf_edit_controller**)self;
@@ -2875,7 +2514,7 @@ struct dpf_edit_controller : v3_edit_controller_cpp {
return V3_NOT_IMPLEMENTED;
};

controller.get_state = []V3_API(void* self, v3_bstream* stream) -> v3_result
ctrl.get_state = []V3_API(void* self, v3_bstream* stream) -> v3_result
{
d_stdout("dpf_edit_controller::get_state => %p %p", self, stream);
dpf_edit_controller* const controller = *(dpf_edit_controller**)self;
@@ -2890,7 +2529,7 @@ struct dpf_edit_controller : v3_edit_controller_cpp {
return V3_NOT_IMPLEMENTED;
};

controller.get_parameter_count = []V3_API(void* self) -> int32_t
ctrl.get_parameter_count = []V3_API(void* self) -> int32_t
{
// d_stdout("dpf_edit_controller::get_parameter_count => %p", self);
dpf_edit_controller* const controller = *(dpf_edit_controller**)self;
@@ -2902,7 +2541,7 @@ struct dpf_edit_controller : v3_edit_controller_cpp {
return vst3->getParameterCount();
};

controller.get_parameter_info = []V3_API(void* self, int32_t param_idx, v3_param_info* param_info) -> v3_result
ctrl.get_parameter_info = []V3_API(void* self, int32_t param_idx, v3_param_info* param_info) -> v3_result
{
// d_stdout("dpf_edit_controller::get_parameter_info => %p %i", self, param_idx);
dpf_edit_controller* const controller = *(dpf_edit_controller**)self;
@@ -2914,7 +2553,7 @@ struct dpf_edit_controller : v3_edit_controller_cpp {
return vst3->getParameterInfo(param_idx, param_info);
};

controller.get_parameter_string_for_value = []V3_API(void* self, v3_param_id index, double normalised, v3_str_128 output) -> v3_result
ctrl.get_parameter_string_for_value = []V3_API(void* self, v3_param_id index, double normalised, v3_str_128 output) -> v3_result
{
// NOTE very noisy, called many times
// d_stdout("dpf_edit_controller::get_parameter_string_for_value => %p %u %f %p", self, index, normalised, output);
@@ -2927,7 +2566,7 @@ struct dpf_edit_controller : v3_edit_controller_cpp {
return vst3->getParameterStringForValue(index, normalised, output);
};

controller.get_parameter_value_for_string = []V3_API(void* self, v3_param_id index, int16_t* input, double* output) -> v3_result
ctrl.get_parameter_value_for_string = []V3_API(void* self, v3_param_id index, int16_t* input, double* output) -> v3_result
{
d_stdout("dpf_edit_controller::get_parameter_value_for_string => %p %u %p %p", self, index, input, output);
dpf_edit_controller* const controller = *(dpf_edit_controller**)self;
@@ -2939,7 +2578,7 @@ struct dpf_edit_controller : v3_edit_controller_cpp {
return vst3->getParameterValueForString(index, input, output);
};

controller.normalised_parameter_to_plain = []V3_API(void* self, v3_param_id index, double normalised) -> double
ctrl.normalised_parameter_to_plain = []V3_API(void* self, v3_param_id index, double normalised) -> double
{
d_stdout("dpf_edit_controller::normalised_parameter_to_plain => %p %u %f", self, index, normalised);
dpf_edit_controller* const controller = *(dpf_edit_controller**)self;
@@ -2951,7 +2590,7 @@ struct dpf_edit_controller : v3_edit_controller_cpp {
return vst3->normalisedParameterToPlain(index, normalised);
};

controller.plain_parameter_to_normalised = []V3_API(void* self, v3_param_id index, double plain) -> double
ctrl.plain_parameter_to_normalised = []V3_API(void* self, v3_param_id index, double plain) -> double
{
d_stdout("dpf_edit_controller::plain_parameter_to_normalised => %p %u %f", self, index, plain);
dpf_edit_controller* const controller = *(dpf_edit_controller**)self;
@@ -2963,7 +2602,7 @@ struct dpf_edit_controller : v3_edit_controller_cpp {
return vst3->plainParameterToNormalised(index, plain);
};

controller.get_parameter_normalised = []V3_API(void* self, v3_param_id index) -> double
ctrl.get_parameter_normalised = []V3_API(void* self, v3_param_id index) -> double
{
// NOTE very noisy, called many times
// d_stdout("dpf_edit_controller::get_parameter_normalised => %p %u", self, index);
@@ -2976,7 +2615,7 @@ struct dpf_edit_controller : v3_edit_controller_cpp {
return vst3->getParameterNormalized(index);
};

controller.set_parameter_normalised = []V3_API(void* self, v3_param_id index, double normalised) -> v3_result
ctrl.set_parameter_normalised = []V3_API(void* self, v3_param_id index, double normalised) -> v3_result
{
d_stdout("dpf_edit_controller::set_parameter_normalised => %p %u %f", self, index, normalised);
dpf_edit_controller* const controller = *(dpf_edit_controller**)self;
@@ -2988,7 +2627,7 @@ struct dpf_edit_controller : v3_edit_controller_cpp {
return vst3->setParameterNormalized(index, normalised);
};

controller.set_component_handler = []V3_API(void* self, v3_component_handler** handler) -> v3_result
ctrl.set_component_handler = []V3_API(void* self, v3_component_handler** handler) -> v3_result
{
d_stdout("dpf_edit_controller::set_component_handler => %p %p", self, handler);
dpf_edit_controller* const controller = *(dpf_edit_controller**)self;
@@ -3002,16 +2641,24 @@ struct dpf_edit_controller : v3_edit_controller_cpp {
return V3_NOT_INITIALISED;
};

controller.create_view = []V3_API(void* self, const char* name) -> v3_plugin_view**
ctrl.create_view = []V3_API(void* self, const char* name) -> v3_plugin_view**
{
d_stdout("dpf_edit_controller::create_view => %p %s", self, name);
dpf_edit_controller* const controller = *(dpf_edit_controller**)self;
DISTRHO_SAFE_ASSERT_RETURN(controller != nullptr, nullptr);

#if DISTRHO_PLUGIN_HAS_UI
// plugin must be initialized
PluginVst3* const vst3 = controller->vst3;
DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, nullptr);

// we require host context for message creation
DISTRHO_SAFE_ASSERT_RETURN(controller->hostContext != nullptr, nullptr);

v3_host_application** host = nullptr;
v3_cpp_obj_query_interface(controller->hostContext, v3_host_application_iid, &host);
DISTRHO_SAFE_ASSERT_RETURN(host != nullptr, nullptr);

// if there is a component connection, we require it to be active
if (controller->connectionComp != nullptr)
{
@@ -3025,7 +2672,8 @@ struct dpf_edit_controller : v3_edit_controller_cpp {
controller->connectionComp->shortcircuit = true;
}

v3_plugin_view** const view = dpf_plugin_view_create(vst3->getInstancePointer(),
v3_plugin_view** const view = dpf_plugin_view_create(host,
vst3->getInstancePointer(),
vst3->getSampleRate());
DISTRHO_SAFE_ASSERT_RETURN(view != nullptr, nullptr);

@@ -3285,11 +2933,6 @@ struct dpf_audio_processor : v3_audio_processor_cpp {
// --------------------------------------------------------------------------------------------------------------------
// dpf_component

struct v3_component_cpp : v3_funknown {
v3_plugin_base base;
v3_component comp;
};

struct dpf_component : v3_component_cpp {
std::atomic<int> refcounter;
ScopedPointer<dpf_component>* self;
@@ -3396,9 +3039,9 @@ struct dpf_component : v3_component_cpp {
// ------------------------------------------------------------------------------------------------------------
// v3_plugin_base

base.initialise = []V3_API(void* self, struct v3_plugin_base::v3_funknown* context) -> v3_result
base.initialize = []V3_API(void* self, v3_plugin_base::v3_funknown** context) -> v3_result
{
d_stdout("dpf_component::initialise => %p %p", self, context);
d_stdout("dpf_component::initialize => %p %p", self, context);
dpf_component* const component = *(dpf_component**)self;
DISTRHO_SAFE_ASSERT_RETURN(component != nullptr, V3_NOT_INITIALISED);

@@ -3413,7 +3056,11 @@ struct dpf_component : v3_component_cpp {
if (d_lastSampleRate <= 0.0)
d_lastSampleRate = 44100.0;

component->vst3 = new PluginVst3();
// query for host context
v3_host_application** host = nullptr;
v3_cpp_obj_query_interface(context, v3_host_application_iid, &host);

component->vst3 = new PluginVst3(host);
return V3_OK;
};

@@ -3612,7 +3259,7 @@ struct dpf_factory : v3_plugin_factory_cpp {
// ------------------------------------------------------------------------------------------------------------
// v3_plugin_factory

v1.get_factory_info = []V3_API(void*, struct v3_factory_info* const info) -> v3_result
v1.get_factory_info = []V3_API(void*, v3_factory_info* const info) -> v3_result
{
std::memset(info, 0, sizeof(*info));
DISTRHO_NAMESPACE::strncpy(info->vendor, gPluginInfo.getMaker(), sizeof(info->vendor));
@@ -3626,7 +3273,7 @@ struct dpf_factory : v3_plugin_factory_cpp {
return 1;
};

v1.get_class_info = []V3_API(void*, int32_t idx, struct v3_class_info* const info) -> v3_result
v1.get_class_info = []V3_API(void*, int32_t idx, v3_class_info* const info) -> v3_result
{
std::memset(info, 0, sizeof(*info));
DISTRHO_SAFE_ASSERT_RETURN(idx == 0, V3_NO_INTERFACE);
@@ -3657,7 +3304,7 @@ struct dpf_factory : v3_plugin_factory_cpp {
// ------------------------------------------------------------------------------------------------------------
// v3_plugin_factory_2

v2.get_class_info_2 = []V3_API(void*, int32_t idx, struct v3_class_info_2* info) -> v3_result
v2.get_class_info_2 = []V3_API(void*, int32_t idx, v3_class_info_2* info) -> v3_result
{
std::memset(info, 0, sizeof(*info));
DISTRHO_SAFE_ASSERT_RETURN(idx == 0, V3_NO_INTERFACE);
@@ -3678,7 +3325,7 @@ struct dpf_factory : v3_plugin_factory_cpp {
// ------------------------------------------------------------------------------------------------------------
// v3_plugin_factory_3

v3.get_class_info_utf16 = []V3_API(void*, int32_t idx, struct v3_class_info_3* info) -> v3_result
v3.get_class_info_utf16 = []V3_API(void*, int32_t idx, v3_class_info_3* info) -> v3_result
{
std::memset(info, 0, sizeof(*info));
DISTRHO_SAFE_ASSERT_RETURN(idx == 0, V3_NO_INTERFACE);
@@ -3696,7 +3343,7 @@ struct dpf_factory : v3_plugin_factory_cpp {
return V3_OK;
};

v3.set_host_context = []V3_API (void* self, struct v3_funknown* host) -> v3_result
v3.set_host_context = []V3_API (void* self, v3_funknown* host) -> v3_result
{
d_stdout("dpf_factory::set_host_context => %p %p", self, host);
return V3_NOT_IMPLEMENTED;


+ 39
- 16
distrho/src/DistrhoUIVST3.cpp View File

@@ -22,7 +22,7 @@
#include "../extra/Thread.hpp"

#include "travesty/edit_controller.h"
#include "travesty/message.h"
#include "travesty/host.h"
#include "travesty/view.h"

/* TODO items:
@@ -51,7 +51,6 @@ static constexpr const setStateFunc setStateCallback = nullptr;

const char* tuid2str(const v3_tuid iid);
void strncpy_utf16(int16_t* dst, const char* src, size_t length);
v3_message** dpf_message_create(const char* id);

// --------------------------------------------------------------------------------------------------------------------
// custom attribute list struct, used for sending utf8 strings
@@ -86,6 +85,7 @@ class UIVst3 : public Thread
{
public:
UIVst3(v3_plugin_view** const view,
v3_host_application** const host,
const intptr_t winId,
const float scaleFactor,
const double sampleRate,
@@ -101,6 +101,7 @@ public:
instancePointer,
scaleFactor),
fView(view),
fHostContext(host),
fConnection(nullptr),
fFrame(nullptr),
fReadyForPluginData(false),
@@ -224,7 +225,7 @@ public:

d_stdout("requesting current plugin state");

v3_message** const message = dpf_message_create("init");
v3_message** const message = createMessage("init");
DISTRHO_SAFE_ASSERT_RETURN(message != nullptr,);

v3_attribute_list** const attrlist = v3_cpp_obj(message)->get_attributes(message);
@@ -243,7 +244,7 @@ public:
d_stdout("reporting UI closed");
fReadyForPluginData = false;

v3_message** const message = dpf_message_create("close");
v3_message** const message = createMessage("close");
DISTRHO_SAFE_ASSERT_RETURN(message != nullptr,);

v3_attribute_list** const attrlist = v3_cpp_obj(message)->get_attributes(message);
@@ -357,6 +358,7 @@ private:

// VST3 stuff
v3_plugin_view** const fView;
v3_host_application** const fHostContext;
v3_connection_point** fConnection;
v3_plugin_frame** fFrame;

@@ -367,11 +369,26 @@ private:
// ----------------------------------------------------------------------------------------------------------------
// helper functions called during message passing

v3_message** createMessage(const char* const id) const
{
DISTRHO_SAFE_ASSERT_RETURN(fHostContext != nullptr, nullptr);

v3_tuid iid;
memcpy(iid, v3_message_iid, sizeof(v3_tuid));
v3_message** msg = nullptr;
const v3_result res = v3_cpp_obj(fHostContext)->create_instance(fHostContext, iid, iid, (void**)&msg);
DISTRHO_SAFE_ASSERT_INT_RETURN(res == V3_TRUE, res, nullptr);
DISTRHO_SAFE_ASSERT_RETURN(msg != nullptr, nullptr);

v3_cpp_obj(msg)->set_message_id(msg, id);
return msg;
}

void requestMorePluginData() const
{
DISTRHO_SAFE_ASSERT_RETURN(fConnection != nullptr,);

v3_message** const message = dpf_message_create("idle");
v3_message** const message = createMessage("idle");
DISTRHO_SAFE_ASSERT_RETURN(message != nullptr,);

v3_attribute_list** const attrlist = v3_cpp_obj(message)->get_attributes(message);
@@ -390,7 +407,7 @@ private:
{
DISTRHO_SAFE_ASSERT_RETURN(fConnection != nullptr,);

v3_message** const message = dpf_message_create("parameter-edit");
v3_message** const message = createMessage("parameter-edit");
DISTRHO_SAFE_ASSERT_RETURN(message != nullptr,);

v3_attribute_list** const attrlist = v3_cpp_obj(message)->get_attributes(message);
@@ -413,7 +430,7 @@ private:
{
DISTRHO_SAFE_ASSERT_RETURN(fConnection != nullptr,);

v3_message** const message = dpf_message_create("parameter-set");
v3_message** const message = createMessage("parameter-set");
DISTRHO_SAFE_ASSERT_RETURN(message != nullptr,);

v3_attribute_list** const attrlist = v3_cpp_obj(message)->get_attributes(message);
@@ -461,7 +478,7 @@ private:
{
DISTRHO_SAFE_ASSERT_RETURN(fConnection != nullptr,);

v3_message** const message = dpf_message_create("midi");
v3_message** const message = createMessage("midi");
DISTRHO_SAFE_ASSERT_RETURN(message != nullptr,);

v3_attribute_list** const attrlist = v3_cpp_obj(message)->get_attributes(message);
@@ -490,7 +507,7 @@ private:
{
DISTRHO_SAFE_ASSERT_RETURN(fConnection != nullptr,);

v3_message** const message = dpf_message_create("state-set");
v3_message** const message = createMessage("state-set");
DISTRHO_SAFE_ASSERT_RETURN(message != nullptr,);

v3_attribute_list** const attrlist = v3_cpp_obj(message)->get_attributes(message);
@@ -631,7 +648,7 @@ struct dpf_ui_connection_point : v3_connection_point_cpp {
// ------------------------------------------------------------------------------------------------------------
// v3_connection_point

point.connect = []V3_API(void* self, struct v3_connection_point** other) -> v3_result
point.connect = []V3_API(void* self, v3_connection_point** other) -> v3_result
{
d_stdout("dpf_ui_connection_point::connect => %p %p", self, other);
dpf_ui_connection_point* const point = *(dpf_ui_connection_point**)self;
@@ -646,7 +663,7 @@ struct dpf_ui_connection_point : v3_connection_point_cpp {
return V3_OK;
};

point.disconnect = []V3_API(void* self, struct v3_connection_point** other) -> v3_result
point.disconnect = []V3_API(void* self, v3_connection_point** other) -> v3_result
{
d_stdout("dpf_ui_connection_point::disconnect => %p %p", self, other);
dpf_ui_connection_point* const point = *(dpf_ui_connection_point**)self;
@@ -661,7 +678,7 @@ struct dpf_ui_connection_point : v3_connection_point_cpp {
return V3_OK;
};

point.notify = []V3_API(void* self, struct v3_message** message) -> v3_result
point.notify = []V3_API(void* self, v3_message** message) -> v3_result
{
dpf_ui_connection_point* const point = *(dpf_ui_connection_point**)self;
DISTRHO_SAFE_ASSERT_RETURN(point != nullptr, V3_NOT_INITIALISED);
@@ -684,13 +701,16 @@ struct dpf_plugin_view : v3_plugin_view_cpp {
ScopedPointer<dpf_ui_connection_point> connection;
ScopedPointer<UIVst3> uivst3;
// cached values
v3_host_application** const host;
void* const instancePointer;
double sampleRate;
v3_plugin_frame** frame = nullptr;

dpf_plugin_view(ScopedPointer<dpf_plugin_view>* selfptr, void* const instance, const double sr)
dpf_plugin_view(ScopedPointer<dpf_plugin_view>* selfptr,
v3_host_application** const h, void* const instance, const double sr)
: refcounter(1),
self(selfptr),
host(h),
instancePointer(instance),
sampleRate(sr),
frame(nullptr)
@@ -808,6 +828,7 @@ struct dpf_plugin_view : v3_plugin_view_cpp {
{
const float scaleFactor = view->scale != nullptr ? view->scale->scaleFactor : 0.0f;
view->uivst3 = new UIVst3((v3_plugin_view**)view->self,
view->host,
(uintptr_t)parent,
scaleFactor,
view->sampleRate,
@@ -956,12 +977,14 @@ struct dpf_plugin_view : v3_plugin_view_cpp {
// --------------------------------------------------------------------------------------------------------------------
// dpf_plugin_view_create (called from plugin side)

v3_plugin_view** dpf_plugin_view_create(void* instancePointer, double sampleRate);
v3_plugin_view** dpf_plugin_view_create(v3_host_application** host, void* instancePointer, double sampleRate);

v3_plugin_view** dpf_plugin_view_create(void* const instancePointer, const double sampleRate)
v3_plugin_view** dpf_plugin_view_create(v3_host_application** const host,
void* const instancePointer,
const double sampleRate)
{
ScopedPointer<dpf_plugin_view>* const viewptr = new ScopedPointer<dpf_plugin_view>;
*viewptr = new dpf_plugin_view(viewptr, instancePointer, sampleRate);
*viewptr = new dpf_plugin_view(viewptr, host, instancePointer, sampleRate);
return (v3_plugin_view**)static_cast<void*>(viewptr);
}



+ 5
- 5
distrho/src/travesty/base.h View File

@@ -160,9 +160,9 @@ enum {
*/

struct v3_funknown {
v3_result (V3_API *query_interface)(void* self, const v3_tuid iid, void** obj);
uint32_t (V3_API *ref)(void* self);
uint32_t (V3_API *unref)(void* self);
v3_result (V3_API* query_interface)(void* self, const v3_tuid iid, void** obj);
uint32_t (V3_API* ref)(void* self);
uint32_t (V3_API* unref)(void* self);
};

static constexpr const v3_tuid v3_funknown_iid =
@@ -175,8 +175,8 @@ static constexpr const v3_tuid v3_funknown_iid =
struct v3_plugin_base {
struct v3_funknown;

v3_result (V3_API *initialise)(void* self, struct v3_funknown* context);
v3_result (V3_API *terminate)(void* self);
v3_result (V3_API* initialize)(void* self, struct v3_funknown** context);
v3_result (V3_API* terminate)(void* self);
};

static constexpr const v3_tuid v3_plugin_base_iid =


+ 24
- 0
distrho/src/travesty/component.h View File

@@ -107,4 +107,28 @@ struct v3_component {
static constexpr const v3_tuid v3_component_iid =
V3_ID(0xE831FF31, 0xF2D54301, 0x928EBBEE, 0x25697802);

#ifdef __cplusplus

/**
* C++ variants
*/

struct v3_component_cpp : v3_funknown {
v3_plugin_base base;
v3_component comp;
};

template<> inline
constexpr v3_component* v3_cpp_obj(v3_component** obj)
{
/**
* this ugly piece of code is required due to C++ assuming `reinterpret_cast` by default,
* but we need everything to be `static_cast` for it to be `constexpr` compatible.
*/
return static_cast<v3_component*>(
static_cast<void*>(static_cast<uint8_t*>(static_cast<void*>(*obj)) + sizeof(void*)*5));
}

#endif

#include "align_pop.h"

+ 1
- 1
distrho/src/travesty/edit_controller.h View File

@@ -118,7 +118,7 @@ static constexpr const v3_tuid v3_midi_mapping_iid =

struct v3_edit_controller_cpp : v3_funknown {
v3_plugin_base base;
v3_edit_controller controller;
v3_edit_controller ctrl;
};

struct v3_midi_mapping_cpp : v3_funknown {


+ 49
- 0
distrho/src/travesty/host.h View File

@@ -0,0 +1,49 @@
/*
* travesty, pure C VST3-compatible interface
* Copyright (C) 2021 Filipe Coelho <falktx@falktx.com>
*
* Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this
* permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
* TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN
* NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/

#pragma once

#include "message.h"

#include "align_push.h"

/**
* connection point
*/

struct v3_host_application {
struct v3_funknown;

v3_result (V3_API* get_name)(void* self, v3_str_128 name); // wtf?
v3_result (V3_API* create_instance)(void* self, v3_tuid cid, v3_tuid iid, void** obj);
};

static constexpr const v3_tuid v3_host_application_iid =
V3_ID(0x58E595CC, 0xDB2D4969, 0x8B6AAF8C, 0x36A664E5);

#ifdef __cplusplus

/**
* C++ variants
*/

struct v3_host_application_cpp : v3_funknown {
v3_host_application app;
};

#endif

#include "align_pop.h"

+ 14
- 14
distrho/src/travesty/message.h View File

@@ -27,14 +27,14 @@
struct v3_attribute_list {
struct v3_funknown;

v3_result (V3_API *set_int)(void* self, const char* id, int64_t value);
v3_result (V3_API *get_int)(void* self, const char* id, int64_t* value);
v3_result (V3_API *set_float)(void* self, const char* id, double value);
v3_result (V3_API *get_float)(void* self, const char* id, double* value);
v3_result (V3_API *set_string)(void* self, const char* id, const int16_t* string);
v3_result (V3_API *get_string)(void* self, const char* id, int16_t* string, uint32_t size);
v3_result (V3_API *set_binary)(void* self, const char* id, const void* data, uint32_t size);
v3_result (V3_API *get_binary)(void* self, const char* id, const void** data, uint32_t* size);
v3_result (V3_API* set_int)(void* self, const char* id, int64_t value);
v3_result (V3_API* get_int)(void* self, const char* id, int64_t* value);
v3_result (V3_API* set_float)(void* self, const char* id, double value);
v3_result (V3_API* get_float)(void* self, const char* id, double* value);
v3_result (V3_API* set_string)(void* self, const char* id, const int16_t* string);
v3_result (V3_API* get_string)(void* self, const char* id, int16_t* string, uint32_t size);
v3_result (V3_API* set_binary)(void* self, const char* id, const void* data, uint32_t size);
v3_result (V3_API* get_binary)(void* self, const char* id, const void** data, uint32_t* size);
};

static constexpr const v3_tuid v3_attribute_list_iid =
@@ -47,9 +47,9 @@ static constexpr const v3_tuid v3_attribute_list_iid =
struct v3_message {
struct v3_funknown;

const char* (V3_API *get_message_id)(void* self);
void (V3_API *set_message_id)(void* self, const char* id);
v3_attribute_list** (V3_API *get_attributes)(void* self);
const char* (V3_API* get_message_id)(void* self);
void (V3_API* set_message_id)(void* self, const char* id);
v3_attribute_list** (V3_API* get_attributes)(void* self);
};

static constexpr const v3_tuid v3_message_iid =
@@ -62,9 +62,9 @@ static constexpr const v3_tuid v3_message_iid =
struct v3_connection_point {
struct v3_funknown;

v3_result (V3_API *connect)(void* self, struct v3_connection_point** other);
v3_result (V3_API *disconnect)(void* self, struct v3_connection_point** other);
v3_result (V3_API *notify)(void* self, struct v3_message** message);
v3_result (V3_API* connect)(void* self, struct v3_connection_point** other);
v3_result (V3_API* disconnect)(void* self, struct v3_connection_point** other);
v3_result (V3_API* notify)(void* self, struct v3_message** message);
};

static constexpr const v3_tuid v3_connection_point_iid =


Loading…
Cancel
Save