Refactor LV2UI support:

Move all LV2UI initialisation to lv2pui class.
Register UIs via factory pattern.

Plugin UI code is now just widget initialisation, the rest is handled by PUI.
master
Peter Nelson 2013-02-09 20:27:18 +00:00
parent 0fc08cf88f
commit 7f92ee1565
5 changed files with 267 additions and 153 deletions

View File

@ -13,58 +13,23 @@
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#define _BSD_SOURCE 1
#include <unistd.h> /* for usleep */
#include <pthread.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <lv2.h>
#include <lv2/lv2plug.in/ns/extensions/ui/ui.h>
#include <pugl/pugl.h>
#include <limits.h>
#include "../pui/pui.h"
#include "../pui/lv2pui.h"
#include "ptap.h"
/*struct PTapUI : PTextures {
PuglView *view;
pthread_t thread;
LV2UI_Write_Function write;
LV2UI_Controller controller;
struct PTapUI : LV2PUi {
int width;
int height;
bool exit;
int mx;
int my;
int mb;
int mm;
Widget *active;
Container *widget;
void InitWidgets();
};*/
struct PTapUI : PUi {
pthread_t thread;
LV2UI_Write_Function write;
LV2UI_Controller controller;
int width;
int height;
bool exit;
virtual ~PTapUI() {}
void InitWidgets();
/* virtual */ void ParameterChanged(const Widget *w);
/* virtual */ void InitWidgets();
};
void PTapUI::InitWidgets()
{
this->width = 250 + 50 * (NUM_TAPS + NUM_CHANNELS + 1);
this->height = 660;
char tmp[128];
scale = 1.0f;
@ -179,118 +144,10 @@ void PTapUI::InitWidgets()
this->widget->Pack(tooltip);
}
static void *ui_thread(void *ptr)
{
PTapUI *pui = (PTapUI *)ptr;
while (!pui->exit) {
usleep(1000000 / 25);
puglProcessEvents(pui->view);
}
return NULL;
}
struct FLV2PUi_PTapUI : LV2PUiFactory<FLV2PUi_PTapUI> {
/* virtual */ const char *GetUri() { return PTAP_URI "#X11UI"; }
void PTapUI::ParameterChanged(const Widget *w)
{
if (w->port == UINT_MAX) return;
this->write(this->controller, w->port, sizeof w->value, 0, &w->value);
char tmp[64];
snprintf(tmp, sizeof tmp, "(%s) %s: %0.4f", w->group, w->label, w->value);
tooltip->SetLabel(tmp);
}
static LV2UI_Handle ptapui_instantiate(
const LV2UI_Descriptor *descriptor,
const char *plugin_uri,
const char *bundle_path,
LV2UI_Write_Function write_function,
LV2UI_Controller controller,
LV2UI_Widget *widget,
const LV2_Feature *const *host_features)
{
PuglNativeWindow parent = 0;
LV2UI_Resize *resize = NULL;
for (; host_features && *host_features; host_features++) {
if (!strcmp((*host_features)->URI, LV2_UI__parent)) {
parent = (PuglNativeWindow)(*host_features)->data;
} else if (!strcmp((*host_features)->URI, LV2_UI__resize)) {
resize = (LV2UI_Resize *)(*host_features)->data;
}
}
if (!parent) {
fprintf(stderr, "error: ptapui: No parent window provided.\n");
return NULL;
}
PTapUI *pui = new PTapUI();
pui->write = write_function;
pui->controller = controller;
pui->width = 250 + 50 * (NUM_TAPS + NUM_CHANNELS + 1);
pui->height = 660;
pui->exit = false;
pui->InitWidgets();
pui->view = puglCreate(parent, "PTap", pui->width, pui->height, true);
puglSetHandle(pui->view, pui);
pui->SetFunc();
if (resize) {
resize->ui_resize(resize->handle, pui->width, pui->height);
}
pthread_create(&pui->thread, NULL, ui_thread, pui);
*widget = (void*)puglGetNativeWindow(pui->view);
return pui;
}
static void ptapui_cleanup(LV2UI_Handle ui)
{
PTapUI *pui = (PTapUI *)ui;
pui->exit = true;
pthread_join(pui->thread, NULL);
puglDestroy(pui->view);
delete pui;
}
static void ptapui_port_event(
LV2UI_Handle ui,
uint32_t port_index,
uint32_t buffer_size,
uint32_t format,
const void *buffer)
{
PUi *pui = (PUi *)ui;
Widget *w = pui->widget->GetWidget(port_index);
if (!w) return;
w->SetValue(*((float *)buffer));
pui->Repaint();
}
static const void *ptapui_extension_data(const char *uri)
{
return NULL;
}
static const LV2UI_Descriptor s_lv2uidescriptor =
{
PTAP_URI "#X11UI",
&ptapui_instantiate,
&ptapui_cleanup,
&ptapui_port_event,
&ptapui_extension_data,
/* virtual */ LV2PUi *CreateInstance() { return new PTapUI(); }
};
const LV2UI_Descriptor *lv2ui_descriptor(uint32_t index)
{
if (index == 0) {
return &s_lv2uidescriptor;
}
return NULL;
}
static FLV2PUi_PTapUI iFLV2PUi_PTapUI;

View File

@ -7,6 +7,7 @@ LDFLAGS += `pkg-config ftgl pugl-0 --libs`
PUISRC := button.cpp
PUISRC += container.cpp
PUISRC += label.cpp
PUISRC += lv2pui.cpp
PUISRC += knob.cpp
PUISRC += pui.cpp
PUISRC += slider.cpp

141
pui/lv2pui.cpp 100644
View File

@ -0,0 +1,141 @@
#include <unistd.h>
#include <pthread.h>
#include <pugl/pugl.h>
#include "pui.h"
#include "lv2pui.h"
static void *lv2pui_thread(void *ptr)
{
LV2PUi *lv2pui = (LV2PUi *)ptr;
while (!lv2pui->exit) {
usleep(1000000 / 25);
puglProcessEvents(lv2pui->view);
}
return NULL;
}
void LV2PUi::Instantiate(
const LV2UI_Descriptor *descriptor,
const char *bundle_path,
LV2UI_Write_Function write_function,
LV2UI_Controller controller,
LV2UI_Widget *widget,
const LV2_Feature *const *features)
{
PuglNativeWindow parent = 0;
for (; features && *features; ++features) {
if (!strcmp((*features)->URI, LV2_UI__parent)) {
parent = (PuglNativeWindow)(*features)->data;
} else if (!strcmp((*features)->URI, LV2_UI__resize)) {
this->resize = (LV2UI_Resize *)(*features)->data;
}
}
this->write = write_function;
this->controller = controller;
this->exit = false;
this->InitWidgets();
int w = this->widget->w;
int h = this->widget->h;
this->view = puglCreate(parent, "Test", w, h, true);
puglSetHandle(this->view, this);
this->SetFunc();
if (this->resize) {
this->resize->ui_resize(this->resize->handle, w, h);
}
pthread_create(&this->thread, NULL, &lv2pui_thread, this);
*widget = (void *)puglGetNativeWindow(this->view);
}
static LV2UI_Handle lv2pui_instantiate(
const LV2UI_Descriptor *descriptor,
const char *plugin_uri,
const char *bundle_path,
LV2UI_Write_Function write_function,
LV2UI_Controller controller,
LV2UI_Widget *widget,
const LV2_Feature *const *features)
{
char uri[128];
snprintf(uri, sizeof uri, "%s#X11UI", plugin_uri);
LV2PUi *lv2pui = LV2PUiFactoryBase::CreateInstance(uri);
if (!lv2pui) return NULL;
lv2pui->Instantiate(descriptor, bundle_path, write_function, controller, widget, features);
return lv2pui;
}
LV2PUi::~LV2PUi()
{
this->exit = true;
pthread_join(this->thread, NULL);
puglDestroy(this->view);
}
static void lv2pui_cleanup(LV2UI_Handle handle)
{
LV2PUi *lv2pui = (LV2PUi *)handle;
delete lv2pui;
}
void LV2PUi::ParameterChanged(const Widget *w)
{
if (w->port == UINT_MAX) return;
this->write(this->controller, w->port, sizeof w->value, 0, &w->value);
if (!tooltip) return;
char tmp[128];
snprintf(tmp, sizeof tmp, "(%s): %s: %0.4f", w->group, w->label, w->value);
tooltip->SetLabel(tmp);
}
void LV2PUi::PortEvent(uint32_t port_index, uint32_t buffer_size, uint32_t format, const void *buffer)
{
Widget *w = this->widget->GetWidget(port_index);
if (!w) return;
w->SetValue(*((float *)buffer));
this->Repaint();
}
static void lv2pui_port_event(
LV2UI_Handle handle,
uint32_t port_index,
uint32_t buffer_size,
uint32_t format,
const void *buffer)
{
LV2PUi *lv2pui = (LV2PUi *)handle;
lv2pui->PortEvent(port_index, buffer_size, format, buffer);
}
static const void *lv2pui_extension_data(const char *uri)
{
return NULL;
}
static LV2UI_Descriptor s_lv2ui_descriptor = {
NULL,
&lv2pui_instantiate,
&lv2pui_cleanup,
&lv2pui_port_event,
&lv2pui_extension_data,
};
const LV2UI_Descriptor *lv2ui_descriptor(uint32_t index)
{
const char *uri = LV2PUiFactoryBase::GetUri(index);
if (!uri) return NULL;
s_lv2ui_descriptor.URI = uri;
return &s_lv2ui_descriptor;
}

113
pui/lv2pui.h 100644
View File

@ -0,0 +1,113 @@
#ifndef LV2PUI_H
#define LV2PUI_H
#include <lv2.h>
#include <lv2/lv2plug.in/ns/extensions/ui/ui.h>
#include <map>
struct LV2PUi : PUi {
pthread_t thread;
bool exit;
LV2UI_Write_Function write;
LV2UI_Controller controller;
LV2UI_Resize *resize;
virtual ~LV2PUi();
virtual void Instantiate(
const LV2UI_Descriptor *descriptor,
const char *bundle_path,
LV2UI_Write_Function write_function,
LV2UI_Controller controller,
LV2UI_Widget *widget,
const LV2_Feature *const *features
);
virtual void PortEvent(uint32_t port_index, uint32_t buffer_size, uint32_t format, const void *buffer);
/* virtual */ void ParameterChanged(const Widget *w);
};
struct StringCompare {
bool operator () (const char *a, const char *b) const
{
return strcmp(a, b) < 0;
}
};
struct LV2PUiFactoryBase {
private:
const char *uri;
typedef std::map<const char *, LV2PUiFactoryBase *, StringCompare> LV2PUis;
static LV2PUis &GetLV2PUis()
{
static LV2PUis &s_lv2puis = *new LV2PUis();
return s_lv2puis;
}
protected:
void RegisterLV2PUi(const char *uri)
{
if (!uri) return;
this->uri = uri;
//std::pair<LV2PUis::iterator, bool> P = ;
GetLV2PUis().insert(LV2PUis::value_type(uri, this));
}
public:
LV2PUiFactoryBase() : uri(NULL) {}
virtual ~LV2PUiFactoryBase()
{
if (!this->uri) return;
GetLV2PUis().erase(this->uri);
if (GetLV2PUis().empty()) delete &GetLV2PUis();
}
static LV2PUi *CreateInstance(const char *uri)
{
if (!uri) return NULL;
if (GetLV2PUis().size() == 0) return NULL;
LV2PUis::iterator it = GetLV2PUis().begin();
for (; it != GetLV2PUis().end(); ++it) {
LV2PUiFactoryBase *b = (*it).second;
if (!strcmp(uri, b->uri)) {
return b->CreateInstance();
}
}
return NULL;
}
static const char *GetUri(uint32_t index)
{
if (GetLV2PUis().size() <= index) return NULL;
LV2PUis::iterator it = GetLV2PUis().begin();
for (; it != GetLV2PUis().end(); ++it) {
if (!index--) {
LV2PUiFactoryBase *b = (*it).second;
return b->uri;
}
}
return NULL;
}
virtual LV2PUi *CreateInstance() = 0;
};
template <class T>
struct LV2PUiFactory : LV2PUiFactoryBase {
LV2PUiFactory() { this->RegisterLV2PUi(((T *)this)->GetUri()); }
const char *GetUri();
};
#endif /* LV2PUI_H */

View File

@ -39,6 +39,8 @@ struct PUi : PTextures {
virtual ~PUi();
virtual void InitWidgets() = 0;
inline void Repaint() { puglPostRedisplay(this->view); }
void OnReshape(int w, int h);