Commit 411c057a authored by Hanspeter Portner's avatar Hanspeter Portner

Merge commit '4c94cfa2'

parents e75e417a 4c94cfa2
cmake_minimum_required(VERSION 2.8)
project(eo.lv2)
include_directories(${PROJECT_SOURCE_DIR})
include_directories(${PROJECT_BINARY_DIR})
set(CMAKE_C_FLAGS "-std=gnu99 -Wextra -Wno-unused-parameter -ffast-math -fvisibility=hidden ${CMAKE_C_FLAGS}")
set(CMAKE_C_FLAGS "-Wshadow -Wimplicit-function-declaration -Wredundant-decls -Wmissing-prototypes -Wstrict-prototypes ${CMAKE_C_FLAGS}")
set(CMAKE_MODULE_LINKER_FLAGS "-Wl,-z,nodelete ${CMAKE_MODULE_LINKER_FLAGS}")
add_definitions("-D_GNU_SOURCE=1") # asprintf
set(EO_MAJOR_VERSION 0)
set(EO_MINOR_VERSION 1)
set(EO_MICRO_VERSION 1)
set(DEST lib/lv2/eo.lv2)
find_package(PkgConfig) # ${PKG_CONFIG_FOUND}
# eo
add_library(eo MODULE
test/eo.c)
target_link_libraries(eo ${LIBS})
set_target_properties(eo PROPERTIES PREFIX "")
install(TARGETS eo DESTINATION ${DEST})
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/test/eo.ttl DESTINATION ${DEST})
# manifest
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/test/manifest.ttl.in ${PROJECT_BINARY_DIR}/manifest.ttl)
install(FILES ${PROJECT_BINARY_DIR}/manifest.ttl DESTINATION ${DEST})
pkg_search_module(ELM REQUIRED elementary>=1.8)
include_directories(${ELM_INCLUDE_DIRS})
set(LIBS_UI ${LIBS_UI} ${ELM_LDFLAGS})
pkg_search_module(ECORE_X OPTIONAL ecore-x)
if((${ELM_VERSION} VERSION_EQUAL "1.9.0") OR (${ELM_VERSION} VERSION_GREATER "1.9.0"))
add_definitions("-DELM_1_9")
endif()
if((DEFINED ECORE_X_FOUND) AND ((${ELM_VERSION} VERSION_EQUAL "1.13.0") OR (${ELM_VERSION} VERSION_GREATER "1.13.0")))
message(STATUS "X11 UI wrap enabled")
set(X11_UI_WRAP "")
add_definitions("-DX11_UI_WRAP")
else()
message(STATUS "X11 UI wrap disabled")
set(X11_UI_WRAP "#")
endif()
# eo_ui
add_library(eo_ui MODULE
test/eo_ui.c)
target_link_libraries(eo_ui ${LIBS_UI})
set_target_properties(eo_ui PROPERTIES PREFIX "")
install(TARGETS eo_ui DESTINATION ${DEST})
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/test/lv2_white.png DESTINATION ${DEST})
......@@ -25,6 +25,9 @@
#endif
#include <lv2/lv2plug.in/ns/extensions/ui/ui.h>
#include <lv2/lv2plug.in/ns/ext/options/options.h>
#include <lv2/lv2plug.in/ns/ext/urid/urid.h>
#include <lv2/lv2plug.in/ns/ext/atom/atom.h>
#include <lv2_external_ui.h> // kxstudio external-ui extension
typedef enum _eo_ui_driver_t eo_ui_driver_t;
......@@ -50,6 +53,10 @@ struct _eo_ui_t {
Evas_Object *content;
eo_ui_content_get content_get;
LV2_URID window_title;
LV2_URID atom_string;
char title [512];
union {
// eo iface
struct {
......@@ -118,7 +125,7 @@ _show_cb(LV2UI_Handle instance)
return -1;
// create main window
eoui->win = elm_win_add(NULL, "EoUI", ELM_WIN_BASIC);
eoui->win = elm_win_add(NULL, eoui->title, ELM_WIN_BASIC);
if(!eoui->win)
return -1;
evas_object_smart_callback_add(eoui->win, "delete,request",
......@@ -252,10 +259,7 @@ _kx_show(LV2_External_UI_Widget *widget)
return;
// create main window
const char *title = eoui->kx.host->plugin_human_id
? eoui->kx.host->plugin_human_id
: "EoUI";
eoui->win = elm_win_add(NULL, title, ELM_WIN_BASIC);
eoui->win = elm_win_add(NULL, eoui->title, ELM_WIN_BASIC);
if(!eoui->win)
return;
elm_win_autodel_set(eoui->win, EINA_TRUE);
......@@ -343,6 +347,19 @@ eoui_instantiate(eo_ui_t *eoui, const LV2UI_Descriptor *descriptor,
eoui->w = eoui->w > 0 ? eoui->w : 400; // fall-back if w == 0
eoui->h = eoui->h > 0 ? eoui->h : 400; // fall-back if h == 0
LV2_URID_Map *map = NULL;
for(unsigned i=0; features[i]; i++)
{
if(!strcmp(features[i]->URI, LV2_URID__map))
map = features[i]->data;
}
if(!map)
return -1;
eoui->atom_string = map->map(map->handle, LV2_ATOM__String);
eoui->window_title = map->map(map->handle, LV2_UI__windowTitle);
*widget = NULL;
switch(eoui->driver)
......@@ -356,7 +373,7 @@ eoui_instantiate(eo_ui_t *eoui, const LV2UI_Descriptor *descriptor,
if(!strcmp(features[i]->URI, LV2_UI__parent))
eoui->eo.parent = features[i]->data;
else if(!strcmp(features[i]->URI, LV2_UI__resize))
eoui->eo.resize = (LV2UI_Resize *)features[i]->data;
eoui->eo.resize = features[i]->data;
}
if(!eoui->eo.parent)
return -1;
......@@ -378,14 +395,29 @@ eoui_instantiate(eo_ui_t *eoui, const LV2UI_Descriptor *descriptor,
// according to the LV2 spec, the host MUST signal availability of
// idle interface via features, thus we test for it here
int host_provides_idle_iface = 0; // mandatory
LV2_Options_Option *opts = NULL; // optional
for(int i=0; features[i]; i++)
{
if(!strcmp(features[i]->URI, LV2_UI__idleInterface))
host_provides_idle_iface = 1;
else if(!strcmp(features[i]->URI, LV2_OPTIONS__options))
opts = features[i]->data;
}
if(!host_provides_idle_iface)
return -1;
snprintf(eoui->title, 512, "%s", descriptor->URI);
if(opts)
{
for(LV2_Options_Option *opt = opts;
(opt->key != 0) && (opt->value != NULL);
opt++)
{
if( (opt->key == eoui->window_title) && (opt->type == eoui->atom_string) )
snprintf(eoui->title, 512, "%s", opt->value);
}
}
// initialize elementary library
_elm_startup_time = ecore_time_unix_get();
elm_init(0, NULL);
......@@ -404,7 +436,7 @@ eoui_instantiate(eo_ui_t *eoui, const LV2UI_Descriptor *descriptor,
if(!strcmp(features[i]->URI, LV2_UI__parent))
eoui->x11.parent = (Ecore_X_Window)(uintptr_t)features[i]->data;
else if(!strcmp(features[i]->URI, LV2_UI__resize))
eoui->x11.resize = (LV2UI_Resize *)features[i]->data;
eoui->x11.resize = features[i]->data;
else if(!strcmp(features[i]->URI, LV2_UI__idleInterface))
host_provides_idle_iface = 1;
}
......@@ -468,14 +500,31 @@ eoui_instantiate(eo_ui_t *eoui, const LV2UI_Descriptor *descriptor,
case EO_UI_DRIVER_KX:
{
eoui->kx.host = NULL; // mandatory
LV2_Options_Option *opts = NULL; // optional
for(int i=0; features[i]; i++)
{
if(!strcmp(features[i]->URI, LV2_EXTERNAL_UI__Host))
eoui->kx.host = features[i]->data;
else if(!strcmp(features[i]->URI, LV2_OPTIONS__options))
opts = features[i]->data;
}
if(!eoui->kx.host)
return -1;
snprintf(eoui->title, 512, "%s", descriptor->URI);
if(opts)
{
for(LV2_Options_Option *opt = opts;
(opt->key != 0) && (opt->value != NULL);
opt++)
{
if( (opt->key == eoui->window_title) && (opt->type == eoui->atom_string) )
snprintf(eoui->title, 512, "%s", opt->value);
}
}
if(eoui->kx.host->plugin_human_id)
snprintf(eoui->title, 512, "%s", eoui->kx.host->plugin_human_id);
// initialize elementary library
_elm_startup_time = ecore_time_unix_get();
elm_init(0, NULL);
......@@ -558,12 +607,8 @@ eoui_cleanup(eo_ui_t *eoui)
memset(eoui, 0, sizeof(eo_ui_t));
}
// extension data callback for EoUI
static inline const void *
eoui_eo_extension_data(const char *uri)
{
return NULL;
}
#define eoui_eo_extension_data NULL
#define eoui_kx_extension_data NULL
// extension data callback for show interface UI
static inline const void *
......@@ -589,11 +634,4 @@ eoui_x11_extension_data(const char *uri)
return NULL;
}
// extension data callback for external-ui
static inline const void *
eoui_kx_extension_data(const char *uri)
{
return NULL;
}
#endif // _EO_UI_H
/*
* Copyright (c) 2016 Hanspeter Portner (dev@open-music-kontrollers.ch)
*
* This is free software: you can redistribute it and/or modify
* it under the terms of the Artistic License 2.0 as published by
* The Perl Foundation.
*
* This source is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the voiceied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* Artistic License 2.0 for more details.
*
* You should have received a copy of the Artistic License 2.0
* along the source as a COPYING file. If not, obtain it from
* http://www.perlfoundation.org/artistic_license_2_0.
*/
#include <stdlib.h>
#include <lv2/lv2plug.in/ns/lv2core/lv2.h>
#define EO_PREFIX "http://open-music-kontrollers.ch/lv2/eo#"
#define EO_TEST_URI EO_PREFIX"test"
typedef struct _plughandle_t plughandle_t;
struct _plughandle_t {
const float *x_in;
const float *y_in;
};
static LV2_Handle
instantiate(const LV2_Descriptor* descriptor, double rate,
const char *bundle_path, const LV2_Feature *const *features)
{
plughandle_t *handle = calloc(1, sizeof(plughandle_t));
if(!handle)
return NULL;
return handle;
}
static void
connect_port(LV2_Handle instance, uint32_t port, void *data)
{
plughandle_t *handle = (plughandle_t *)instance;
switch(port)
{
case 0:
handle->x_in = (const float *)data;
break;
case 1:
handle->y_in = (const float *)data;
break;
default:
break;
}
}
static void
run(LV2_Handle instance, uint32_t nsamples)
{
plughandle_t *handle = instance;
// do nothing
}
static void
cleanup(LV2_Handle instance)
{
plughandle_t *handle = instance;
free(handle);
}
const LV2_Descriptor eo_test = {
.URI = EO_TEST_URI,
.instantiate = instantiate,
.connect_port = connect_port,
.activate = NULL,
.run = run,
.deactivate = NULL,
.cleanup = cleanup,
.extension_data = NULL
};
#ifdef _WIN32
__declspec(dllexport)
#else
__attribute__((visibility("default")))
#endif
const LV2_Descriptor*
lv2_descriptor(uint32_t index)
{
switch(index)
{
case 0:
return &eo_test;
default:
return NULL;
}
}
# Copyright (c) 2016 Hanspeter Portner (dev@open-music-kontrollers.ch)
#
# This is free software: you can redistribute it and/or modify
# it under the terms of the Artistic License 2.0 as published by
# The Perl Foundation.
#
# This source is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# Artistic License 2.0 for more details.
#
# You should have received a copy of the Artistic License 2.0
# along the source as a COPYING file. If not, obtain it from
# http://www.perlfoundation.org/artistic_license_2_0.
@prefix owl: <http://www.w3.org/2002/07/owl#> .
@prefix foaf: <http://xmlns.com/foaf/0.1/> .
@prefix doap: <http://usefulinc.com/ns/doap#> .
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@prefix lv2: <http://lv2plug.in/ns/lv2core#> .
@prefix atom: <http://lv2plug.in/ns/ext/atom#> .
@prefix urid: <http://lv2plug.in/ns/ext/urid#> .
@prefix state: <http://lv2plug.in/ns/ext/state#> .
@prefix patch: <http://lv2plug.in/ns/ext/patch#> .
@prefix log: <http://lv2plug.in/ns/ext/log#> .
@prefix units: <http://lv2plug.in/ns/extensions/units#> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
@prefix ui: <http://lv2plug.in/ns/extensions/ui#> .
@prefix kx: <http://kxstudio.sf.net/ns/lv2ext/external-ui#> .
@prefix lic: <http://opensource.org/licenses/> .
@prefix omk: <http://open-music-kontrollers.ch/ventosus#> .
@prefix proj: <http://open-music-kontrollers.ch/lv2/> .
@prefix eo: <http://open-music-kontrollers.ch/lv2/eo#> .
ui:EoUI
a rdfs:Class, owl:Class ;
rdfs:subClassOf ui:UI .
kx:Widget
a rdfs:Class, owl:Class ;
rdfs:subClassOf ui:UI .
kx:Host
a lv2:Feature .
# Maintainer
omk:me
a foaf:Person ;
foaf:name "Hanspeter Portner" ;
foaf:mbox <mailto:dev@open-music-kontrollers.ch> ;
foaf:homepage <http://open-music-kontrollers.ch> .
# Project
proj:eo
a doap:Project ;
doap:maintainer omk:me ;
doap:name "EoUI Bundle" .
# Test Plugin
eo:test
a lv2:Plugin ,
lv2:ConverterPlugin ;
doap:name "EoUI Test" ;
doap:license lic:Artistic-2.0 ;
lv2:project proj:eo ;
lv2:optionalFeature lv2:isLive, lv2:hardRTCapable ;
lv2:port [
a lv2:InputPort ,
lv2:ControlPort ;
lv2:index 0 ;
lv2:symbol "x_in" ;
lv2:name "X In" ;
lv2:default 0.5 ;
lv2:minimum 0.0 ;
lv2:maximum 1.0 ;
] , [
a lv2:InputPort ,
lv2:ControlPort ;
lv2:index 1 ;
lv2:symbol "y_in" ;
lv2:name "Y In" ;
lv2:default 0.5 ;
lv2:minimum 0.0 ;
lv2:maximum 1.0 ;
] .
# Test UI
eo:ui
a ui:UI ;
lv2:requiredFeature ui:idleInterface, ui:portMap, urid:map ;
lv2:extensionData ui:idleInterface, ui:showInterface .
eo:kx
a kx:Widget ;
lv2:requiredFeature kx:Host, ui:portMap, urid:map .
eo:x11
a ui:X11UI ;
lv2:requiredFeature ui:idleInterface, ui:portMap, urid:map ;
lv2:optionalFeature ui:resize ;
lv2:extensionData ui:idleInterface, ui:resize .
eo:eo
a ui:EoUI ;
lv2:optionalFeature ui:resize ;
lv2:requiredFeature ui:idleInterface, ui:portMap, urid:map .
/*
* Copyright (c) 2015 Hanspeter Portner (dev@open-music-kontrollers.ch)
*
* This is free software: you can redistribute it and/or modify
* it under the terms of the Artistic License 2.0 as published by
* The Perl Foundation.
*
* This source is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* Artistic License 2.0 for more details.
*
* You should have received a copy of the Artistic License 2.0
* along the source as a COPYING file. If not, obtain it from
* http://www.perlfoundation.org/artistic_license_2_0.
*/
#include <string.h>
#include <Elementary.h>
#include <lv2_eo_ui.h>
#define EO_PREFIX "http://open-music-kontrollers.ch/lv2/eo#"
#define EO_TEST_URI EO_PREFIX"test"
#define EO_UI_URI EO_PREFIX"ui"
#define EO_KX_URI EO_PREFIX"kx"
#define EO_X11_URI EO_PREFIX"x11"
#define EO_EO_URI EO_PREFIX"eo"
typedef struct _UI UI;
struct _UI {
eo_ui_t eoui;
LV2_URID_Map *map;
LV2_URID float_protocol;
LV2UI_Write_Function write_function;
LV2UI_Controller controller;
LV2UI_Port_Map *port_map;
uint32_t x_in_port;
uint32_t y_in_port;
int w, h;
Evas_Object *widget;
char img_src [512];
};
const LV2UI_Descriptor eo_eo;
const LV2UI_Descriptor eo_ui;
const LV2UI_Descriptor eo_x11;
const LV2UI_Descriptor eo_kx;
static void
_mouse_move(void *data, Evas *e, Evas_Object *obj, void *event_info)
{
UI *ui = data;
Evas_Event_Mouse_Move *ev = event_info;
int w, h;
evas_object_geometry_get(obj, NULL, NULL, &w, &h);
Evas_Coord x = ev->cur.canvas.x;
Evas_Coord y = ev->cur.canvas.y;
const float X = (float)x / w;
const float Y = (float)y / h;
ui->write_function(ui->controller, ui->x_in_port, sizeof(float),
ui->float_protocol, &X);
ui->write_function(ui->controller, ui->y_in_port, sizeof(float),
ui->float_protocol, &Y);
}
static Evas_Object *
_content_get(eo_ui_t *eoui)
{
UI *ui = (void *)eoui - offsetof(UI, eoui);
ui->widget = elm_bg_add(eoui->win);
if(ui->widget)
{
elm_bg_file_set(ui->widget, ui->img_src, NULL);
evas_object_event_callback_add(ui->widget, EVAS_CALLBACK_MOUSE_MOVE, _mouse_move, ui);
}
return ui->widget;
}
static LV2UI_Handle
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)
{
if(strcmp(plugin_uri, EO_TEST_URI))
return NULL;
eo_ui_driver_t driver;
if(descriptor == &eo_eo)
driver = EO_UI_DRIVER_EO;
else if(descriptor == &eo_ui)
driver = EO_UI_DRIVER_UI;
else if(descriptor == &eo_x11)
driver = EO_UI_DRIVER_X11;
else if(descriptor == &eo_kx)
driver = EO_UI_DRIVER_KX;
else
return NULL;
UI *ui = calloc(1, sizeof(UI));
if(!ui)
return NULL;
for(int i=0; features[i]; i++)
{
if(!strcmp(features[i]->URI, LV2_URID__map))
ui->map = (LV2_URID_Map *)features[i]->data;
else if(!strcmp(features[i]->URI, LV2_UI__portMap))
ui->port_map = (LV2UI_Port_Map *)features[i]->data;
}
if(!ui->map)
{
fprintf(stderr, "%s: Host does not support urid:map\n", descriptor->URI);
free(ui);
return NULL;
}
if(!ui->port_map)
{
fprintf(stderr, "%s: Host does not support ui:portMap\n", descriptor->URI);
free(ui);
return NULL;
}
ui->float_protocol = ui->map->map(ui->map->handle, LV2_UI__floatProtocol);
// query port index of "control" port
ui->x_in_port = ui->port_map->port_index(ui->port_map->handle, "x_in");
ui->y_in_port = ui->port_map->port_index(ui->port_map->handle, "y_in");
eo_ui_t *eoui = &ui->eoui;
eoui->driver = driver;
eoui->content_get = _content_get;
eoui->w = 100,
eoui->h = 63;
ui->write_function = write_function;
ui->controller = controller;
snprintf(ui->img_src, 512, "%s/lv2_white.png", bundle_path);
if(eoui_instantiate(eoui, descriptor, plugin_uri, bundle_path, write_function,
controller, widget, features))
{
free(ui);
return NULL;
}
return ui;
}
static void
cleanup(LV2UI_Handle handle)
{
UI *ui = handle;
eoui_cleanup(&ui->eoui);
free(ui);
}
static void
port_event(LV2UI_Handle handle, uint32_t port_index, uint32_t buffer_size,
uint32_t format, const void *buffer)
{
UI *ui = handle;
if(port_index == ui->x_in_port)
{
//TODO
}
else if(port_index == ui->y_in_port)
{
//TODO
}
}
const LV2UI_Descriptor eo_eo = {
.URI = EO_EO_URI,
.instantiate = instantiate,
.cleanup = cleanup,
.port_event = port_event,
.extension_data = eoui_eo_extension_data
};
const LV2UI_Descriptor eo_ui = {
.URI = EO_UI_URI,
.instantiate = instantiate,
.cleanup = cleanup,
.port_event = port_event,
.extension_data = eoui_ui_extension_data
};
const LV2UI_Descriptor eo_x11 = {
.URI = EO_X11_URI,
.instantiate = instantiate,
.cleanup = cleanup,
.port_event = port_event,
.extension_data = eoui_x11_extension_data
};
const LV2UI_Descriptor eo_kx = {
.URI = EO_KX_URI,
.instantiate = instantiate,
.cleanup = cleanup,
.port_event = port_event,
.extension_data = eoui_kx_extension_data
};
#ifdef _WIN32
__declspec(dllexport)
#else
__attribute__((visibility("default")))
#endif
const LV2UI_Descriptor*
lv2ui_descriptor(uint32_t index)
{
switch(index)
{
case 0:
return &eo_eo;
case 1:
return &eo_ui;
case 2:
return &eo_x11;
case 3:
return &eo_kx;
default:
return NULL;
}
}
# Copyright (c) 2016 Hanspeter Portner (dev@open-music-kontrollers.ch)
#
# This is free software: you can redistribute it and/or modify
# it under the terms of the Artistic License 2.0 as published by
# The Perl Foundation.
#
# This source is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# Artistic License 2.0 for more details.
#
# You should have received a copy of the Artistic License 2.0
# along the source as a COPYING file. If not, obtain it from
# http://www.perlfoundation.org/artistic_license_2_0.
@prefix lv2: <http://lv2plug.in/ns/lv2core#> .
@prefix owl: <http://www.w3.org/2002/07/owl#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@prefix ui: <http://lv2plug.in/ns/extensions/ui#> .
@prefix kx: <http://kxstudio.sf.net/ns/lv2ext/external-ui#> .
@prefix eo: <http://open-music-kontrollers.ch/lv2/eo#> .
ui:EoUI
a rdfs:Class, owl:Class ;
rdfs:subClassOf ui:UI .
kx:Widget
a rdfs:Class, owl:Class ;
rdfs:subClassOf ui:UI .
kx:Host
a lv2:Feature .
# Test Plugin
eo:test
a lv2:Plugin ;
lv2:minorVersion @EO_MINOR_VERSION@ ;
lv2:microVersion @EO_MICRO_VERSION@ ;
lv2:binary <eo@CMAKE_SHARED_MODULE_SUFFIX@> ;
@UI_UI_WRAP@ui:ui eo:ui ;
@KX_UI_WRAP@ui:ui eo:kx ;
@X11_UI_WRAP@ui:ui eo:x11 ;
@EO_UI_WRAP@ui:ui eo:eo ;
rdfs:seeAlso <eo.ttl> .
# Test UI
eo:ui
a ui:UI ;
ui:binary <eo_ui@CMAKE_SHARED_MODULE_SUFFIX@> ;
rdfs:seeAlso <eo.ttl> .
eo:kx
a kx:Widget ;
ui:binary <eo_ui@CMAKE_SHARED_MODULE_SUFFIX@> ;
rdfs:seeAlso <eo.ttl> .
eo:x11
a ui:X11UI ;
ui:binary <eo_ui@CMAKE_SHARED_MODULE_SUFFIX@> ;
rdfs:seeAlso <eo.ttl> .
eo:eo
a ui:EoUI ;
ui:binary <eo_ui@CMAKE_SHARED_MODULE_SUFFIX@> ;
rdfs:seeAlso <eo.ttl> .