aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitlab-ci.yml72
-rw-r--r--COPYING214
-rw-r--r--README.md54
-rw-r--r--VERSION1
-rw-r--r--ardour.lv2/lv2_extensions.h174
-rw-r--r--canvas.c719
-rw-r--r--canvas_display.ttl110
-rw-r--r--canvas_display_ui.ttl30
-rw-r--r--canvas_ui.c575
-rw-r--r--manifest.ttl.in34
-rw-r--r--meson.build105
-rw-r--r--meson/arm-linux-gnueabihf18
-rw-r--r--meson/i686-linux-gnu19
-rw-r--r--meson/i686-w64-mingw3217
-rw-r--r--meson/universal-apple-darwin19
-rw-r--r--meson/x86_64-linux-gnu16
-rw-r--r--meson/x86_64-w64-mingw3217
-rw-r--r--pugl/.gitignore (renamed from .gitignore)0
-rw-r--r--pugl/AUTHORS (renamed from AUTHORS)0
-rw-r--r--pugl/COPYING13
-rw-r--r--pugl/Doxyfile.in (renamed from Doxyfile.in)0
-rw-r--r--pugl/README.md28
-rw-r--r--pugl/pugl.pc.in (renamed from pugl.pc.in)0
-rw-r--r--pugl/pugl/cairo_gl.h (renamed from pugl/cairo_gl.h)0
-rw-r--r--pugl/pugl/gl.h (renamed from pugl/gl.h)0
-rw-r--r--pugl/pugl/glew.h (renamed from pugl/glew.h)0
-rw-r--r--pugl/pugl/glu.h (renamed from pugl/glu.h)0
-rw-r--r--pugl/pugl/pugl.h (renamed from pugl/pugl.h)0
-rw-r--r--pugl/pugl/pugl.hpp (renamed from pugl/pugl.hpp)0
-rw-r--r--pugl/pugl/pugl_internal.h (renamed from pugl/pugl_internal.h)0
-rw-r--r--pugl/pugl/pugl_osx.m (renamed from pugl/pugl_osx.m)0
-rw-r--r--pugl/pugl/pugl_win.cpp (renamed from pugl/pugl_win.cpp)0
-rw-r--r--pugl/pugl/pugl_x11.c (renamed from pugl/pugl_x11.c)0
-rw-r--r--pugl/pugl_cairo_test.c (renamed from pugl_cairo_test.c)0
-rw-r--r--pugl/pugl_test.c (renamed from pugl_test.c)0
-rwxr-xr-xpugl/waf (renamed from waf)bin97489 -> 97489 bytes
-rw-r--r--pugl/wscript (renamed from wscript)0
37 files changed, 2203 insertions, 32 deletions
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
new file mode 100644
index 0000000..a824c76
--- /dev/null
+++ b/.gitlab-ci.yml
@@ -0,0 +1,72 @@
+stages:
+ - build
+ - deploy
+
+.variables_template: &variables_definition
+ variables:
+ BASE_NAME: "canvas.lv2"
+ PKG_CONFIG_PATH: "/opt/lv2/lib/pkgconfig:/opt/${CI_BUILD_NAME}/lib/pkgconfig:/usr/lib/${CI_BUILD_NAME}/pkgconfig"
+ TOOLCHAIN_FILE: "${CI_PROJECT_DIR}/cmake/${CI_BUILD_NAME}.cmake"
+
+.common_template: &common_definition
+ <<: *variables_definition
+ stage: build
+ artifacts:
+ name: "${BASE_NAME}-$(cat VERSION)-${CI_BUILD_NAME}"
+ paths:
+ - "${BASE_NAME}-$(cat VERSION)/"
+
+.build_template: &build_definition
+ <<: *common_definition
+ script:
+ - mkdir build
+ - pushd build
+ - cmake -DCAIRO_INCLUDE_DIRS="/opt/${CI_BUILD_NAME}/include" -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=${CI_PROJECT_DIR} -DPLUGIN_DEST="${BASE_NAME}-$(cat ../VERSION)/${CI_BUILD_NAME}/${BASE_NAME}" -DCMAKE_TOOLCHAIN_FILE=${TOOLCHAIN_FILE} ..
+ - cmake .. # needed for darwin
+ - make
+ - make install
+
+.universal_linux_template: &universal_linux_definition
+ image: ventosus/universal-linux-gnu
+ <<: *build_definition
+
+.arm_linux_template: &arm_linux_definition
+ image: ventosus/arm-linux-gnueabihf
+ <<: *build_definition
+
+.universal_w64_template: &universal_w64_definition
+ image: ventosus/universal-w64-mingw32
+ <<: *build_definition
+
+.universal_apple_template: &universal_apple_definition
+ image: ventosus/universal-apple-darwin
+ <<: *build_definition
+
+# building in docker
+x86_64-linux-gnu:
+ <<: *universal_linux_definition
+
+i686-linux-gnu:
+ <<: *universal_linux_definition
+
+arm-linux-gnueabihf:
+ <<: *arm_linux_definition
+
+x86_64-w64-mingw32:
+ <<: *universal_w64_definition
+
+i686-w64-mingw32:
+ <<: *universal_w64_definition
+
+universal-apple-darwin:
+ <<: *universal_apple_definition
+
+pack:
+ <<: *variables_definition
+ stage: deploy
+ script:
+ - echo 'packing up...'
+ artifacts:
+ name: "${BASE_NAME}-$(cat VERSION)"
+ paths:
+ - "${BASE_NAME}-$(cat VERSION)/"
diff --git a/COPYING b/COPYING
index e1e203d..ddb9a46 100644
--- a/COPYING
+++ b/COPYING
@@ -1,13 +1,201 @@
-Copyright 2011-2014 David Robillard <http://drobilla.net>
-
-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.
-
-THIS 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.
+ The Artistic License 2.0
+
+ Copyright (c) 2000-2006, The Perl Foundation.
+
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+Preamble
+
+This license establishes the terms under which a given free software
+Package may be copied, modified, distributed, and/or redistributed.
+The intent is that the Copyright Holder maintains some artistic
+control over the development of that Package while still keeping the
+Package available as open source and free software.
+
+You are always permitted to make arrangements wholly outside of this
+license directly with the Copyright Holder of a given Package. If the
+terms of this license do not permit the full use that you propose to
+make of the Package, you should contact the Copyright Holder and seek
+a different licensing arrangement.
+
+Definitions
+
+ "Copyright Holder" means the individual(s) or organization(s)
+ named in the copyright notice for the entire Package.
+
+ "Contributor" means any party that has contributed code or other
+ material to the Package, in accordance with the Copyright Holder's
+ procedures.
+
+ "You" and "your" means any person who would like to copy,
+ distribute, or modify the Package.
+
+ "Package" means the collection of files distributed by the
+ Copyright Holder, and derivatives of that collection and/or of
+ those files. A given Package may consist of either the Standard
+ Version, or a Modified Version.
+
+ "Distribute" means providing a copy of the Package or making it
+ accessible to anyone else, or in the case of a company or
+ organization, to others outside of your company or organization.
+
+ "Distributor Fee" means any fee that you charge for Distributing
+ this Package or providing support for this Package to another
+ party. It does not mean licensing fees.
+
+ "Standard Version" refers to the Package if it has not been
+ modified, or has been modified only in ways explicitly requested
+ by the Copyright Holder.
+
+ "Modified Version" means the Package, if it has been changed, and
+ such changes were not explicitly requested by the Copyright
+ Holder.
+
+ "Original License" means this Artistic License as Distributed with
+ the Standard Version of the Package, in its current version or as
+ it may be modified by The Perl Foundation in the future.
+
+ "Source" form means the source code, documentation source, and
+ configuration files for the Package.
+
+ "Compiled" form means the compiled bytecode, object code, binary,
+ or any other form resulting from mechanical transformation or
+ translation of the Source form.
+
+
+Permission for Use and Modification Without Distribution
+
+(1) You are permitted to use the Standard Version and create and use
+Modified Versions for any purpose without restriction, provided that
+you do not Distribute the Modified Version.
+
+
+Permissions for Redistribution of the Standard Version
+
+(2) You may Distribute verbatim copies of the Source form of the
+Standard Version of this Package in any medium without restriction,
+either gratis or for a Distributor Fee, provided that you duplicate
+all of the original copyright notices and associated disclaimers. At
+your discretion, such verbatim copies may or may not include a
+Compiled form of the Package.
+
+(3) You may apply any bug fixes, portability changes, and other
+modifications made available from the Copyright Holder. The resulting
+Package will still be considered the Standard Version, and as such
+will be subject to the Original License.
+
+
+Distribution of Modified Versions of the Package as Source
+
+(4) You may Distribute your Modified Version as Source (either gratis
+or for a Distributor Fee, and with or without a Compiled form of the
+Modified Version) provided that you clearly document how it differs
+from the Standard Version, including, but not limited to, documenting
+any non-standard features, executables, or modules, and provided that
+you do at least ONE of the following:
+
+ (a) make the Modified Version available to the Copyright Holder
+ of the Standard Version, under the Original License, so that the
+ Copyright Holder may include your modifications in the Standard
+ Version.
+
+ (b) ensure that installation of your Modified Version does not
+ prevent the user installing or running the Standard Version. In
+ addition, the Modified Version must bear a name that is different
+ from the name of the Standard Version.
+
+ (c) allow anyone who receives a copy of the Modified Version to
+ make the Source form of the Modified Version available to others
+ under
+
+ (i) the Original License or
+
+ (ii) a license that permits the licensee to freely copy,
+ modify and redistribute the Modified Version using the same
+ licensing terms that apply to the copy that the licensee
+ received, and requires that the Source form of the Modified
+ Version, and of any works derived from it, be made freely
+ available in that license fees are prohibited but Distributor
+ Fees are allowed.
+
+
+Distribution of Compiled Forms of the Standard Version
+or Modified Versions without the Source
+
+(5) You may Distribute Compiled forms of the Standard Version without
+the Source, provided that you include complete instructions on how to
+get the Source of the Standard Version. Such instructions must be
+valid at the time of your distribution. If these instructions, at any
+time while you are carrying out such distribution, become invalid, you
+must provide new instructions on demand or cease further distribution.
+If you provide valid instructions or cease distribution within thirty
+days after you become aware that the instructions are invalid, then
+you do not forfeit any of your rights under this license.
+
+(6) You may Distribute a Modified Version in Compiled form without
+the Source, provided that you comply with Section 4 with respect to
+the Source of the Modified Version.
+
+
+Aggregating or Linking the Package
+
+(7) You may aggregate the Package (either the Standard Version or
+Modified Version) with other packages and Distribute the resulting
+aggregation provided that you do not charge a licensing fee for the
+Package. Distributor Fees are permitted, and licensing fees for other
+components in the aggregation are permitted. The terms of this license
+apply to the use and Distribution of the Standard or Modified Versions
+as included in the aggregation.
+
+(8) You are permitted to link Modified and Standard Versions with
+other works, to embed the Package in a larger work of your own, or to
+build stand-alone binary or bytecode versions of applications that
+include the Package, and Distribute the result without restriction,
+provided the result does not expose a direct interface to the Package.
+
+
+Items That are Not Considered Part of a Modified Version
+
+(9) Works (including, but not limited to, modules and scripts) that
+merely extend or make use of the Package, do not, by themselves, cause
+the Package to be a Modified Version. In addition, such works are not
+considered parts of the Package itself, and are not subject to the
+terms of this license.
+
+
+General Provisions
+
+(10) Any use, modification, and distribution of the Standard or
+Modified Versions is governed by this Artistic License. By using,
+modifying or distributing the Package, you accept this license. Do not
+use, modify, or distribute the Package, if you do not accept this
+license.
+
+(11) If your Modified Version has been derived from a Modified
+Version made by someone other than you, you are nevertheless required
+to ensure that your Modified Version complies with the requirements of
+this license.
+
+(12) This license does not grant you the right to use any trademark,
+service mark, tradename, or logo of the Copyright Holder.
+
+(13) This license includes the non-exclusive, worldwide,
+free-of-charge patent license to make, have made, use, offer to sell,
+sell, import and otherwise transfer the Package with respect to any
+patent claims licensable by the Copyright Holder that are necessarily
+infringed by the Package. If you institute patent litigation
+(including a cross-claim or counterclaim) against any party alleging
+that the Package constitutes direct or contributory patent
+infringement, then this Artistic License to you shall terminate on the
+date that such litigation is filed.
+
+(14) Disclaimer of Warranty:
+THE PACKAGE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS "AS
+IS' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES. THE IMPLIED
+WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR
+NON-INFRINGEMENT ARE DISCLAIMED TO THE EXTENT PERMITTED BY YOUR LOCAL
+LAW. UNLESS REQUIRED BY LAW, NO COPYRIGHT HOLDER OR CONTRIBUTOR WILL
+BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
+DAMAGES ARISING IN ANY WAY OUT OF THE USE OF THE PACKAGE, EVEN IF
+ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/README.md b/README.md
index 77809d8..d55c1ca 100644
--- a/README.md
+++ b/README.md
@@ -1,28 +1,44 @@
-PUGL
-====
+# Canvas
-Pugl is a minimal portable API for GUIs which supports embedding and is
-suitable for use in plugins. It works on X11, Mac OS X, and Windows. GUIs can
-be drawn with OpenGL or Cairo.
+## Canvas LV2 plugin bundle
-Pugl is vaguely similar to GLUT, but with some significant distinctions:
+### Webpage
- * Minimal in scope, providing only what is necessary to draw and receive
- keyboard and mouse input.
+Get more information at: [http://open-music-kontrollers.ch/lv2/canvas](http://open-music-kontrollers.ch/lv2/canvas)
- * No reliance on static data whatsoever, so the API can be used in plugins or
- multiple independent parts of a program.
+### Build status
- * Single implementation, which is small, liberally licensed Free / Open Source
- Software, and suitable for direct inclusion in programs if avoiding a
- library dependency is desired.
+[![build status](https://gitlab.com/OpenMusicKontrollers/canvas.lv2/badges/master/build.svg)](https://gitlab.com/OpenMusicKontrollers/canvas.lv2/commits/master)
- * Support for embedding in other windows, so Pugl code can draw to a widget
- inside a larger GUI.
+### Dependencies
- * More complete support for keyboard input, including additional "special"
- keys, modifiers, and support for detecting individual modifier key presses.
+* [LV2](http://lv2plug.in) (LV2 Plugin Standard)
+* [pugl](http://drobilla.net/software/pugl) (Portable API for OpenGL GUIs)
+* [cairo](http://cairographics.org) (Cairo Vector Graphics Library)
-For more information, see <http://drobilla.net/software/pugl>.
+### Build / install
- -- David Robillard <d@drobilla.net>
+ git clone https://github.com/OpenMusicKontrollers/canvas.lv2.git
+ cd canvas.lv2
+ mkdir build
+ cd build
+ cmake ..
+ make
+ sudo make install
+
+### License
+
+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>.
diff --git a/VERSION b/VERSION
new file mode 100644
index 0000000..a6c8cf7
--- /dev/null
+++ b/VERSION
@@ -0,0 +1 @@
+0.3.29
diff --git a/ardour.lv2/lv2_extensions.h b/ardour.lv2/lv2_extensions.h
new file mode 100644
index 0000000..64fc3bc
--- /dev/null
+++ b/ardour.lv2/lv2_extensions.h
@@ -0,0 +1,174 @@
+/*
+ Copyright 2016 Robin Gareus <robin@gareus.org>
+
+ 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.
+
+ THIS 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.
+*/
+
+#ifndef _ardour_lv2_extensions_h_
+#define _ardour_lv2_extensions_h_
+
+#include "lv2/lv2plug.in/ns/lv2core/lv2.h"
+
+/**
+ @defgroup inlinedisplay Inline-Display
+
+ Support for displaying a miniaturized generic view
+ directly in the host's Mixer Window.
+
+ @{
+*/
+
+#define LV2_INLINEDISPLAY_URI "http://harrisonconsoles.com/lv2/inlinedisplay"
+#define LV2_INLINEDISPLAY_PREFIX LV2_INLINEDISPLAY_URI "#"
+#define LV2_INLINEDISPLAY__interface LV2_INLINEDISPLAY_PREFIX "interface"
+#define LV2_INLINEDISPLAY__queue_draw LV2_INLINEDISPLAY_PREFIX "queue_draw"
+
+/** Opaque handle for LV2_Inline_Display::queue_draw() */
+typedef void* LV2_Inline_Display_Handle;
+
+/** raw image pixmap format is ARGB32,
+ * the data pointer is owned by the plugin and must be valid
+ * from the first call to render until cleanup.
+ */
+typedef struct {
+ unsigned char *data;
+ int width;
+ int height;
+ int stride;
+} LV2_Inline_Display_Image_Surface;
+
+/** a LV2 Feature provided by the Host to the plugin */
+typedef struct {
+ /** Opaque host data */
+ LV2_Inline_Display_Handle handle;
+ /** Request from run() that the host should call render() at a later time
+ * to update the inline display */
+ void (*queue_draw)(LV2_Inline_Display_Handle handle);
+} LV2_Inline_Display;
+
+/**
+ * Plugin Inline-Display Interface.
+ */
+typedef struct {
+ /**
+ * The render method. This is called by the host in a non-realtime context,
+ * usually the main GUI thread.
+ * The data pointer is owned by the plugin and must be valid
+ * from the first call to render until cleanup.
+ *
+ * @param instance The LV2 instance
+ * @param w the max available width
+ * @param h the max available height
+ * @return pointer to a LV2_Inline_Display_Image_Surface or NULL
+ */
+ LV2_Inline_Display_Image_Surface* (*render)(LV2_Handle instance, uint32_t w, uint32_t h);
+} LV2_Inline_Display_Interface;
+
+/**
+ @}
+*/
+
+/**
+ @defgroup automate Self-Automation
+
+ Support for plugins to write automation data via Atom Events
+
+ @{
+*/
+
+#define LV2_AUTOMATE_URI "http://ardour.org/lv2/automate"
+#define LV2_AUTOMATE_URI_PREFIX LV2_AUTOMATE_URI "#"
+/** an lv2:optionalFeature */
+#define LV2_AUTOMATE_URI__can_write LV2_AUTOMATE_URI_PREFIX "canWriteAutomatation"
+/** atom:supports */
+#define LV2_AUTOMATE_URI__control LV2_AUTOMATE_URI_PREFIX "automationControl"
+/** lv2:portProperty */
+#define LV2_AUTOMATE_URI__controlled LV2_AUTOMATE_URI_PREFIX "automationControlled"
+#define LV2_AUTOMATE_URI__controller LV2_AUTOMATE_URI_PREFIX "automationController"
+
+/** atom messages */
+#define LV2_AUTOMATE_URI__event LV2_AUTOMATE_URI_PREFIX "event"
+#define LV2_AUTOMATE_URI__setup LV2_AUTOMATE_URI_PREFIX "setup"
+#define LV2_AUTOMATE_URI__finalize LV2_AUTOMATE_URI_PREFIX "finalize"
+#define LV2_AUTOMATE_URI__start LV2_AUTOMATE_URI_PREFIX "start"
+#define LV2_AUTOMATE_URI__end LV2_AUTOMATE_URI_PREFIX "end"
+#define LV2_AUTOMATE_URI__parameter LV2_AUTOMATE_URI_PREFIX "parameter"
+#define LV2_AUTOMATE_URI__value LV2_AUTOMATE_URI_PREFIX "value"
+
+/**
+ @}
+*/
+
+/**
+ @defgroup license License-Report
+
+ Allow for commercial LV2 to report their
+ licensing status.
+
+ @{
+*/
+
+#define LV2_PLUGINLICENSE_URI "http://harrisonconsoles.com/lv2/license"
+#define LV2_PLUGINLICENSE_PREFIX LV2_PLUGINLICENSE_URI "#"
+#define LV2_PLUGINLICENSE__interface LV2_PLUGINLICENSE_PREFIX "interface"
+
+typedef struct _LV2_License_Interface {
+ /* @return -1 if no license is needed; 0 if unlicensed, 1 if licensed */
+ int (*is_licensed)(LV2_Handle instance);
+ /* @return a string copy of the licensee name if licensed, or NULL, the caller needs to free this */
+ char* (*licensee)(LV2_Handle instance);
+ /* @return a URI identifying the plugin-bundle or plugin for which a given license is valid */
+ const char* (*product_uri)(LV2_Handle instance);
+ /* @return human readable product name for the URI */
+ const char* (*product_name)(LV2_Handle instance);
+ /* @return link to website or webstore */
+ const char* (*store_url)(LV2_Handle instance);
+} LV2_License_Interface;
+
+/**
+ @}
+*/
+
+/**
+ @defgroup plugin provided bypass
+
+ A port with the designation "processing#enable" must
+ control a plugin's internal bypass mode.
+
+ If the port value is larger than zero the plugin processes
+ normally.
+
+ If the port value is zero, the plugin is expected to bypass
+ all signals unmodified.
+
+ The plugin is responsible for providing a click-free transition
+ between the states.
+
+ (values less than zero are reserved for future use:
+ e.g click-free insert/removal of latent plugins.
+ Generally values <= 0 are to be treated as bypassed.)
+
+ lv2:designation <http://ardour.org/lv2/processing#enable> ;
+
+ @{
+*/
+
+#define LV2_PROCESSING_URI "http://ardour.org/lv2/processing"
+#define LV2_PROCESSING_URI_PREFIX LV2_PROCESSING_URI "#"
+#define LV2_PROCESSING_URI__enable LV2_PROCESSING_URI_PREFIX "enable"
+
+/**
+ @}
+*/
+
+#endif
diff --git a/canvas.c b/canvas.c
new file mode 100644
index 0000000..af2df7b
--- /dev/null
+++ b/canvas.c
@@ -0,0 +1,719 @@
+/*
+ * 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.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdatomic.h>
+#include <inttypes.h>
+
+#include <lv2/lv2plug.in/ns/ext/patch/patch.h>
+#include <lv2/lv2plug.in/ns/ext/log/log.h>
+#include <lv2/lv2plug.in/ns/ext/log/logger.h>
+
+#include <canvas.lv2/forge.h>
+#include <canvas.lv2/render.h>
+
+#include <lv2_extensions.h>
+
+#include <cairo/cairo.h>
+
+//#define DEBUG
+#define MAX_GRAPH_BUF 0x10000
+
+typedef struct _plughandle_t plughandle_t;
+
+struct _plughandle_t {
+ LV2_URID_Map *map;
+ LV2_Atom_Forge forge;
+ LV2_Atom_Forge forge_through;
+
+ LV2_Log_Log *log;
+ LV2_Log_Logger logger;
+
+ const LV2_Atom_Sequence *control;
+ LV2_Atom_Sequence *notify;
+
+ LV2_URID patch_Get;
+ LV2_URID patch_Set;
+ LV2_URID patch_Put;
+ LV2_URID patch_property;
+ LV2_URID patch_value;
+ LV2_URID patch_body;
+
+ atomic_flag lock;
+
+ LV2_Inline_Display *queue_draw;
+ LV2_Inline_Display_Image_Surface image_surface;
+ struct {
+ cairo_surface_t *surface;
+ cairo_t *ctx;
+ } cairo;
+
+ LV2_Canvas canvas;
+ bool dirty;
+
+ float aspect_ratio;
+ union {
+ LV2_Atom_Tuple graph;
+ uint8_t buf [MAX_GRAPH_BUF];
+ };
+};
+
+static inline void
+_spin_lock(atomic_flag *lock)
+{
+ while(atomic_flag_test_and_set_explicit(lock, memory_order_acquire))
+ {
+ // spin
+ }
+}
+
+static inline bool
+_try_lock(atomic_flag *lock)
+{
+ return atomic_flag_test_and_set_explicit(lock, memory_order_acquire);
+}
+
+static inline void
+_unlock(atomic_flag *lock)
+{
+ atomic_flag_clear_explicit(lock, memory_order_release);
+}
+
+static const float lv2_L [] = {
+ 0.05, 0.275,
+ 0.05, 0.73463521816969,
+ 0.39996786383766, 0.73463521816969,
+ 0.35805418792799, 0.61981755929103,
+ 0.16950515672412, 0.61981755929103,
+ 0.16950515672412, 0.275,
+ 0.05, 0.275
+};
+
+static const float lv2_V [] = {
+ 0.44035674587458, 0.73463521816969,
+ 0.27321237521861, 0.275,
+ 0.39612954205777, 0.275,
+ 0.5215250619933, 0.61980400005209,
+ 0.64678627651808, 0.275,
+ 0.76999411666921, 0.275,
+ 0.60269884777111, 0.73463521816969,
+ 0.44035674587458, 0.73463521816969
+};
+
+static inline LV2_Atom_Forge_Ref
+_lv2_logo_forge(LV2_Atom_Forge *forge, LV2_Canvas_URID *urid)
+{
+ const uint32_t fg = 0xbb6600ff;
+ LV2_Atom_Forge_Ref ref;
+
+ if( (ref = lv2_canvas_forge_beginPath(forge, urid))
+ && (ref = lv2_canvas_forge_polyLine(forge, urid, sizeof(lv2_L)/sizeof(float), lv2_L))
+ && (ref = lv2_canvas_forge_closePath(forge, urid))
+ && (ref = lv2_canvas_forge_style(forge, urid, fg))
+ && (ref = lv2_canvas_forge_stroke(forge, urid))
+
+ && (ref = lv2_canvas_forge_beginPath(forge, urid))
+ && (ref = lv2_canvas_forge_polyLine(forge, urid, sizeof(lv2_L)/sizeof(float), lv2_V))
+ && (ref = lv2_canvas_forge_closePath(forge, urid))
+ && (ref = lv2_canvas_forge_style(forge, urid, fg))
+ && (ref = lv2_canvas_forge_stroke(forge, urid))
+
+ && (ref = lv2_canvas_forge_beginPath(forge, urid))
+ && (ref = lv2_canvas_forge_moveTo(forge, urid, 0.92679577564592, 0.33745757758451))
+ && (ref = lv2_canvas_forge_curveTo(forge, urid, 0.95, 0.37544661222032, 0.9486097413556,
+ 0.42890103900541, 0.91866073788306, 0.46581025262318))
+ && (ref = lv2_canvas_forge_curveTo(forge, urid, 0.87662774067075, 0.51761178520021,
+ 0.84865149155459, 0.52351773004551, 0.8188709443895, 0.55088574387747))
+ && (ref = lv2_canvas_forge_lineTo(forge, urid, 0.93798338878322, 0.55088574387747))
+ && (ref = lv2_canvas_forge_lineTo(forge, urid, 0.93798338878322, 0.61972641362727))
+ && (ref = lv2_canvas_forge_lineTo(forge, urid, 0.68857649440815, 0.61972641362727))
+ && (ref = lv2_canvas_forge_curveTo(forge, urid, 0.70410821191941, 0.57897193773781,
+ 0.71568706655441, 0.55649255812279, 0.73505227967577, 0.53436493734023))
+ && (ref = lv2_canvas_forge_curveTo(forge, urid, 0.78431409785481, 0.47807598612821,
+ 0.88073913173375, 0.44149338929647, 0.87483180798279, 0.39074363998918))
+ && (ref = lv2_canvas_forge_curveTo(forge, urid, 0.8731729385169, 0.37649219041461,
+ 0.86900905711197, 0.34385128732334, 0.80655313421425, 0.34385128732334))
+ && (ref = lv2_canvas_forge_lineTo(forge, urid, 0.7834998081023, 0.34385128732334))
+ && (ref = lv2_canvas_forge_lineTo(forge, urid, 0.80849192152801, 0.275))
+ && (ref = lv2_canvas_forge_curveTo(forge, urid, 0.88098903540187, 0.275,
+ 0.90879494370618, 0.30798728419169, 0.92679577564592, 0.33745757758451))
+ && (ref = lv2_canvas_forge_style(forge, urid, fg))
+ && (ref = lv2_canvas_forge_stroke(forge, urid)) )
+ {
+ return ref;
+ }
+
+ return 0;
+}
+
+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;
+
+ for(unsigned i=0; features[i]; i++)
+ {
+ if(!strcmp(features[i]->URI, LV2_URID__map))
+ handle->map = features[i]->data;
+ else if(!strcmp(features[i]->URI, LV2_INLINEDISPLAY__queue_draw))
+ handle->queue_draw = features[i]->data;
+ else if(!strcmp(features[i]->URI, LV2_LOG__log))
+ handle->log = features[i]->data;
+ }
+
+ if(!handle->map)
+ {
+ fprintf(stderr,
+ "%s: Host does not support urid:map\n", descriptor->URI);
+ free(handle);
+ return NULL;
+ }
+
+ if(handle->log)
+ lv2_log_logger_init(&handle->logger, handle->map, handle->log);
+
+ handle->patch_Get = handle->map->map(handle->map->handle, LV2_PATCH__Get);
+ handle->patch_Set = handle->map->map(handle->map->handle, LV2_PATCH__Set);
+ handle->patch_Put = handle->map->map(handle->map->handle, LV2_PATCH__Put);
+ handle->patch_property = handle->map->map(handle->map->handle, LV2_PATCH__property);
+ handle->patch_value = handle->map->map(handle->map->handle, LV2_PATCH__value);
+ handle->patch_body = handle->map->map(handle->map->handle, LV2_PATCH__body);
+
+ lv2_atom_forge_init(&handle->forge, handle->map);
+
+ handle->lock = (atomic_flag)ATOMIC_FLAG_INIT;
+
+ lv2_canvas_init(&handle->canvas, handle->map);
+
+ handle->aspect_ratio = 1.f;
+
+ LV2_Atom_Forge_Frame frame;
+ lv2_atom_forge_set_buffer(&handle->forge, handle->buf, MAX_GRAPH_BUF);
+ LV2_Atom_Forge_Ref ref = lv2_atom_forge_tuple(&handle->forge, &frame);
+ if(ref)
+ ref = _lv2_logo_forge(&handle->forge, &handle->canvas.urid);
+ if(ref)
+ lv2_atom_forge_pop(&handle->forge, &frame);
+
+ handle->dirty = true;
+
+ return handle;
+}
+
+static void
+connect_port(LV2_Handle instance, uint32_t port, void *data)
+{
+ plughandle_t *handle = instance;
+
+ switch(port)
+ {
+ case 0:
+ handle->control = data;
+ break;
+ case 1:
+ handle->notify = data;
+ break;
+
+ default:
+ break;
+ }
+}
+
+static LV2_Atom_Forge_Ref
+_forge_graph(plughandle_t *handle, int64_t frames)
+{
+ const uint32_t szg = lv2_atom_total_size(&handle->graph.atom);
+
+ LV2_Atom_Forge_Frame obj_frame;
+ LV2_Atom_Forge_Ref ref = lv2_atom_forge_frame_time(&handle->forge, frames);
+ if(ref)
+ ref = lv2_atom_forge_object(&handle->forge, &obj_frame, 0, handle->patch_Set);
+ if(ref)
+ ref = lv2_atom_forge_key(&handle->forge, handle->patch_property);
+ if(ref)
+ ref = lv2_atom_forge_urid(&handle->forge, handle->canvas.urid.Canvas_graph);
+ if(ref)
+ ref = lv2_atom_forge_key(&handle->forge, handle->patch_value);
+ if(ref)
+ ref = lv2_atom_forge_write(&handle->forge, &handle->graph, szg);
+ if(ref)
+ lv2_atom_forge_pop(&handle->forge, &obj_frame);
+
+ return ref;
+}
+
+static LV2_Atom_Forge_Ref
+_forge_aspect(plughandle_t *handle, int64_t frames)
+{
+ LV2_Atom_Forge_Frame obj_frame;
+ LV2_Atom_Forge_Ref ref = lv2_atom_forge_frame_time(&handle->forge, frames);
+ if(ref)
+ ref = lv2_atom_forge_object(&handle->forge, &obj_frame, 0, handle->patch_Set);
+ if(ref)
+ ref = lv2_atom_forge_key(&handle->forge, handle->patch_property);
+ if(ref)
+ ref = lv2_atom_forge_urid(&handle->forge, handle->canvas.urid.Canvas_aspectRatio);
+ if(ref)
+ ref = lv2_atom_forge_key(&handle->forge, handle->patch_value);
+ if(ref)
+ ref = lv2_atom_forge_float(&handle->forge, handle->aspect_ratio);
+ if(ref)
+ lv2_atom_forge_pop(&handle->forge, &obj_frame);
+
+ return ref;
+}
+
+static void
+run(LV2_Handle instance, uint32_t nsamples)
+{
+ plughandle_t *handle = instance;
+
+ const uint32_t capacity = handle->notify->atom.size;
+ lv2_atom_forge_set_buffer(&handle->forge, (uint8_t *)handle->notify, capacity);
+ LV2_Atom_Forge_Frame frame;
+ LV2_Atom_Forge_Ref ref = lv2_atom_forge_sequence_head(&handle->forge, &frame, 0);
+
+ LV2_ATOM_SEQUENCE_FOREACH(handle->control, ev)
+ {
+ const LV2_Atom_Object *obj = (const LV2_Atom_Object *)&ev->body;
+ const uint32_t szo = lv2_atom_total_size(&obj->atom);
+ bool route = false;
+
+ if(lv2_atom_forge_is_object_type(&handle->forge, obj->atom.type))
+ {
+ if(obj->body.otype == handle->patch_Get)
+ {
+ const LV2_Atom_URID *property = NULL;
+
+ lv2_atom_object_get(obj, handle->patch_property, &property,
+ 0);
+
+ if(!property)
+ {
+ if(ref)
+ ref = _forge_aspect(handle, ev->time.frames);
+ if(ref)
+ ref = _forge_graph(handle, ev->time.frames);
+ }
+ if( property
+ && (property->atom.type == handle->forge.URID)
+ && (property->body == handle->canvas.urid.Canvas_graph) )
+ {
+ if(ref)
+ ref = _forge_graph(handle, ev->time.frames);
+ }
+ else if( property
+ && (property->atom.type == handle->forge.URID)
+ && (property->body == handle->canvas.urid.Canvas_aspectRatio) )
+ {
+ if(ref)
+ ref = _forge_aspect(handle, ev->time.frames);
+ }
+ }
+ else if(obj->body.otype == handle->patch_Set)
+ {
+ const LV2_Atom_URID *property = NULL;
+ const LV2_Atom *value = NULL;
+
+ lv2_atom_object_get(obj, handle->patch_property, &property,
+ handle->patch_value, &value,
+ 0);
+
+ if( property
+ && (property->atom.type == handle->forge.URID)
+ && value)
+ {
+ if( (property->body == handle->canvas.urid.Canvas_graph)
+ && (value->type == handle->forge.Tuple) )
+ {
+ const LV2_Atom_Tuple *graph = (const LV2_Atom_Tuple *)value;
+ const uint32_t szg = lv2_atom_total_size(&graph->atom);
+
+ if( (szg <= MAX_GRAPH_BUF)
+ && _try_lock(&handle->lock) )
+ {
+ memcpy(&handle->graph, graph, szg);
+
+ _unlock(&handle->lock);
+
+ if(handle->queue_draw)
+ handle->queue_draw->queue_draw(handle->queue_draw->handle);
+ }
+
+ route = true;
+ }
+ else if( (property->body == handle->canvas.urid.Canvas_aspectRatio)
+ && (value->type == handle->forge.Float) )
+ {
+ if( _try_lock(&handle->lock) )
+ {
+ handle->aspect_ratio = ((const LV2_Atom_Float *)value)->body;
+
+ _unlock(&handle->lock);
+
+ if(handle->queue_draw)
+ handle->queue_draw->queue_draw(handle->queue_draw->handle);
+ }
+
+ route = true;
+ }
+ else if( (property->body == handle->canvas.urid.Canvas_mouseButtonLeft)
+ && (value->type == handle->forge.Bool) )
+ {
+#ifdef DEBUG
+ if(handle->log)
+ {
+ lv2_log_trace(&handle->logger, "\tcanvas:mouseButtonLeft: %"PRIi32"\n",
+ ((const LV2_Atom_Bool *)value)->body);
+ }
+#endif
+
+ route = true;
+ }
+ else if( (property->body == handle->canvas.urid.Canvas_mouseButtonMiddle)
+ && (value->type == handle->forge.Bool) )
+ {
+#ifdef DEBUG
+ if(handle->log)
+ {
+ lv2_log_trace(&handle->logger, "\tcanvas:mouseButtonMiddle: %"PRIi32"\n",
+ ((const LV2_Atom_Bool *)value)->body);
+ }
+#endif
+
+ route = true;
+ }
+ else if( (property->body == handle->canvas.urid.Canvas_mouseButtonRight)
+ && (value->type == handle->forge.Bool) )
+ {
+#ifdef DEBUG
+ if(handle->log)
+ {
+ lv2_log_trace(&handle->logger, "\tcanvas:mouseButtonRight: %"PRIi32"\n",
+ ((const LV2_Atom_Bool *)value)->body);
+ }
+#endif
+
+ route = true;
+ }
+ else if( (property->body == handle->canvas.urid.Canvas_mousePositionX)
+ && (value->type == handle->forge.Double) )
+ {
+#ifdef DEBUG
+ if(handle->log)
+ {
+ lv2_log_trace(&handle->logger, "\tcanvas:mousePositionX: %lf\n",
+ ((const LV2_Atom_Double *)value)->body);
+ }
+#endif
+
+ route = true;
+ }
+ else if( (property->body == handle->canvas.urid.Canvas_mousePositionY)
+ && (value->type == handle->forge.Double) )
+ {
+#ifdef DEBUG
+ if(handle->log)
+ {
+ lv2_log_trace(&handle->logger, "\tcanvas:mousePositionY: %lf\n",
+ ((const LV2_Atom_Double *)value)->body);
+ }
+#endif
+
+ route = true;
+ }
+ else if( (property->body == handle->canvas.urid.Canvas_mouseWheelX)
+ && (value->type == handle->forge.Double) )
+ {
+#ifdef DEBUG
+ if(handle->log)
+ {
+ lv2_log_trace(&handle->logger, "\tcanvas:mouseWheelX: %lf\n",
+ ((const LV2_Atom_Double *)value)->body);
+ }
+#endif
+
+ route = true;
+ }
+ else if( (property->body == handle->canvas.urid.Canvas_mouseWheelY)
+ && (value->type == handle->forge.Double) )
+ {
+#ifdef DEBUG
+ if(handle->log)
+ {
+ lv2_log_trace(&handle->logger, "\tcanvas:mouseWheelY: %lf\n",
+ ((const LV2_Atom_Double *)value)->body);
+ }
+#endif
+
+ route = true;
+ }
+ else if( (property->body == handle->canvas.urid.Canvas_mouseFocus)
+ && (value->type == handle->forge.Bool) )
+ {
+#ifdef DEBUG
+ if(handle->log)
+ {
+ lv2_log_trace(&handle->logger, "\tcanvas:mouseFocus: %"PRIi32"\n",
+ ((const LV2_Atom_Bool *)value)->body);
+ }
+#endif
+
+ route = true;
+ }
+ }
+ }
+ else if(obj->body.otype == handle->patch_Put)
+ {
+ const LV2_Atom_Object *body = NULL;
+
+ lv2_atom_object_get(obj, handle->patch_body, &body,
+ 0);
+
+ if( body
+ && lv2_atom_forge_is_object_type(&handle->forge, body->atom.type) )
+ {
+ const LV2_Atom_Bool *l = NULL;
+ const LV2_Atom_Bool *m = NULL;
+ const LV2_Atom_Bool *r = NULL;
+ const LV2_Atom_Double *dx = NULL;
+ const LV2_Atom_Double *dy = NULL;
+ const LV2_Atom_Double *x = NULL;
+ const LV2_Atom_Double *y = NULL;
+ const LV2_Atom_Bool *f = NULL;
+
+ lv2_atom_object_get(body,
+ handle->canvas.urid.Canvas_mouseButtonLeft, &l,
+ handle->canvas.urid.Canvas_mouseButtonMiddle, &m,
+ handle->canvas.urid.Canvas_mouseButtonRight, &r,
+ handle->canvas.urid.Canvas_mouseWheelX, &dx,
+ handle->canvas.urid.Canvas_mouseWheelY, &dy,
+ handle->canvas.urid.Canvas_mousePositionX, &x,
+ handle->canvas.urid.Canvas_mousePositionY, &y,
+ handle->canvas.urid.Canvas_mouseFocus, &f,
+ 0);
+
+#ifdef DEBUG
+ if(handle->log)
+ {
+ lv2_log_trace(&handle->logger, "{\n");
+ if(l && (l->atom.type == handle->forge.Bool) )
+ lv2_log_trace(&handle->logger, "\tcanvas:mousebuttonLeft: %"PRIi32"\n", l->body);
+ if(m && (m->atom.type == handle->forge.Bool) )
+ lv2_log_trace(&handle->logger, "\tcanvas:mousebuttonMiddle: %"PRIi32"\n", m->body);
+ if(r && (r->atom.type == handle->forge.Bool) )
+ lv2_log_trace(&handle->logger, "\tcanvas:mousebuttonRight: %"PRIi32"\n", r->body);
+ if(x && (x->atom.type == handle->forge.Double) )
+ lv2_log_trace(&handle->logger, "\tcanvas:mousePositionX: %lf\n", x->body);
+ if(y && (y->atom.type == handle->forge.Double) )
+ lv2_log_trace(&handle->logger, "\tcanvas:mousePositionY: %lf\n", y->body);
+ if(dx && (dx->atom.type == handle->forge.Double) )
+ lv2_log_trace(&handle->logger, "\tcanvas:mouseWheelX: %lf\n", dx->body);
+ if(dy && (dy->atom.type == handle->forge.Double) )
+ lv2_log_trace(&handle->logger, "\tcanvas:mouseWheelY: %lf\n", dy->body);
+ if(f && (f->atom.type == handle->forge.Bool) )
+ lv2_log_trace(&handle->logger, "\tcanvas:mouseFocus: %"PRIi32"\n", f->body);
+ lv2_log_trace(&handle->logger, "}\n");
+ }
+#endif
+
+ if( x && (x->atom.type == handle->forge.Double)
+ && y && (y->atom.type == handle->forge.Double) )
+ route = true; // a valid input event at least sets x and y
+ }
+ }
+ }
+
+ if(route) // should we route to UI?
+ {
+ if(ref)
+ ref = lv2_atom_forge_frame_time(&handle->forge, ev->time.frames);
+ if(ref)
+ ref = lv2_atom_forge_write(&handle->forge, obj, szo);
+ }
+ }
+
+ if(handle->dirty)
+ {
+ if(handle->queue_draw)
+ handle->queue_draw->queue_draw(handle->queue_draw->handle);
+
+ if(ref)
+ ref = _forge_aspect(handle, nsamples-1);
+ if(ref)
+ ref = _forge_graph(handle, nsamples-1);
+
+ handle->dirty = false;
+ }
+
+ if(ref)
+ lv2_atom_forge_pop(&handle->forge, &frame);
+ else
+ lv2_atom_sequence_clear(handle->notify);
+}
+
+static inline LV2_Inline_Display_Image_Surface *
+_cairo_init(plughandle_t *handle, int w, int h)
+{
+ LV2_Inline_Display_Image_Surface *surf = &handle->image_surface;
+
+ surf->width = w;
+ surf->height = w > h ? h : w; // try to use 1:1 ratio
+ surf->stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, surf->width);
+ surf->data = realloc(surf->data, surf->stride * surf->height);
+ if(!surf->data)
+ return NULL;
+
+ handle->cairo.surface = cairo_image_surface_create_for_data(
+ surf->data, CAIRO_FORMAT_ARGB32, surf->width, surf->height, surf->stride);
+
+ if(handle->cairo.surface)
+ {
+ cairo_surface_set_device_scale(handle->cairo.surface, surf->width, surf->height);
+
+ handle->cairo.ctx = cairo_create(handle->cairo.surface);
+ if(handle->cairo.ctx)
+ {
+ cairo_select_font_face(handle->cairo.ctx, "cairo:monospace", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL);
+ }
+ }
+
+ return surf;
+}
+
+static inline void
+_cairo_deinit(plughandle_t *handle)
+{
+ LV2_Inline_Display_Image_Surface *surf = &handle->image_surface;
+
+ if(handle->cairo.ctx)
+ {
+ cairo_destroy(handle->cairo.ctx);
+ handle->cairo.ctx = NULL;
+ }
+
+ if(handle->cairo.surface)
+ {
+ cairo_surface_finish(handle->cairo.surface);
+ cairo_surface_destroy(handle->cairo.surface);
+ handle->cairo.surface = NULL;
+ }
+
+ if(surf->data)
+ {
+ free(surf->data);
+ surf->data = NULL;
+ }
+}
+
+// non-rt
+static LV2_Inline_Display_Image_Surface *
+_render(LV2_Handle instance, uint32_t w, uint32_t h)
+{
+ plughandle_t *handle = instance;
+ LV2_Inline_Display_Image_Surface *surf = &handle->image_surface;
+
+ int W;
+ int H;
+
+ if(handle->aspect_ratio == 0.f)
+ {
+ W = w;
+ H = h;
+ }
+ else if(handle->aspect_ratio <= 1.f)
+ {
+ W = h * handle->aspect_ratio;
+ H = h;
+ }
+ else if(handle->aspect_ratio > 1.f)
+ {
+ W = w;
+ H = w / handle->aspect_ratio;
+ }
+
+ if( (surf->width != W) || (surf->height != H) || !surf->data)
+ {
+ _cairo_deinit(handle);
+ surf = _cairo_init(handle, W, H);
+ }
+
+ if(!surf)
+ return NULL;
+
+ _spin_lock(&handle->lock);
+
+ lv2_canvas_render(&handle->canvas, handle->cairo.ctx, &handle->graph);
+
+ _unlock(&handle->lock);
+
+ return surf;
+}
+
+static const LV2_Inline_Display_Interface idisp_iface = {
+ .render = _render
+};
+
+static void
+cleanup(LV2_Handle instance)
+{
+ plughandle_t *handle = instance;
+
+ _cairo_deinit(handle);
+
+ if(handle->image_surface.data)
+ free(handle->image_surface.data);
+
+ free(handle);
+}
+
+static const void *
+extension_data(const char *uri)
+{
+ if(!strcmp(uri, LV2_INLINEDISPLAY__interface))
+ return &idisp_iface;
+
+ return NULL;
+}
+
+const LV2_Descriptor canvas_canvas = {
+ .URI = "http://open-music-kontrollers.ch/lv2/canvas_display#canvas",
+ .instantiate = instantiate,
+ .connect_port = connect_port,
+ .activate = NULL,
+ .run = run,
+ .deactivate = NULL,
+ .cleanup = cleanup,
+ .extension_data = extension_data
+};
+
+LV2_SYMBOL_EXPORT const LV2_Descriptor*
+lv2_descriptor(uint32_t index)
+{
+ switch(index)
+ {
+ case 0:
+ return &canvas_canvas;
+
+ default:
+ return NULL;
+ }
+}
diff --git a/canvas_display.ttl b/canvas_display.ttl
new file mode 100644
index 0000000..2c89758
--- /dev/null
+++ b/canvas_display.ttl
@@ -0,0 +1,110 @@
+# 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 foaf: <http://xmlns.com/foaf/0.1/> .
+@prefix doap: <http://usefulinc.com/ns/doap#> .
+@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
+@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
+@prefix lv2: <http://lv2plug.in/ns/lv2core#> .
+@prefix atom: <http://lv2plug.in/ns/ext/atom#> .
+@prefix urid: <http://lv2plug.in/ns/ext/urid#> .
+@prefix log: <http://lv2plug.in/ns/ext/log#> .
+@prefix patch: <http://lv2plug.in/ns/ext/patch#> .
+@prefix state: <http://lv2plug.in/ns/ext/state#> .
+@prefix idisp: <http://harrisonconsoles.com/lv2/inlinedisplay#> .
+
+@prefix lic: <http://opensource.org/licenses/> .
+@prefix omk: <http://open-music-kontrollers.ch/ventosus#> .
+@prefix proj: <http://open-music-kontrollers.ch/lv2/> .
+@prefix canvasdisp: <http://open-music-kontrollers.ch/lv2/canvas_display#> .
+@prefix canvas: <http://open-music-kontrollers.ch/lv2/canvas#> .
+
+# to please sord_validate
+idisp:queue_draw
+ a lv2:Feature .
+idisp:interface
+ a lv2:ExtensionData .
+
+# 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:canvas
+ a doap:Project ;
+ doap:maintainer omk:me ;
+ doap:name "Canvas Bundle" .
+
+# Parameters
+canvas:graph
+ a lv2:Parameter ;
+ rdfs:range atom:Tuple ;
+ rdfs:label "graph" ;
+ rdfs:comment "Canvas graph" .
+canvas:aspectRatio
+ a lv2:Parameter ;
+ rdfs:range atom:Float ;
+ rdfs:label "aspectRatio" ;
+ rdfs:comment "Aspect Ratio" ;
+ lv2:minimum 0.25 ;
+ lv2:maximum 4.0 ;
+ lv2:default 1.0 .
+
+# Canvas Plugin
+canvasdisp:canvas
+ a lv2:Plugin,
+ lv2:AnalyserPlugin ;
+ doap:name "Canvas" ;
+ doap:license lic:Artistic-2.0 ;
+ lv2:project proj:canvas ;
+ lv2:requiredFeature urid:map ;
+ lv2:optionalFeature lv2:isLive, lv2:hardRTCapable, idisp:queue_draw, log:log ;
+ lv2:extensionData idisp:interface ;
+
+ lv2:port [
+ a lv2:InputPort ,
+ atom:AtomPort ;
+ atom:bufferType atom:Sequence ;
+ atom:supports patch:Message ;
+ lv2:index 0 ;
+ lv2:symbol "control" ;
+ lv2:name "Control" ;
+ lv2:designation lv2:control ;
+ ] , [
+ a lv2:OutputPort ,
+ atom:AtomPort ;
+ atom:bufferType atom:Sequence ;
+ atom:supports patch:Message ;
+ lv2:index 1 ;
+ lv2:symbol "notify" ;
+ lv2:name "Notify" ;
+ lv2:designation lv2:control ;
+ ] ;
+
+ patch:writable
+ canvas:aspectRatio ,
+ canvas:graph ;
+
+ state:state [
+ canvas:aspectRatio 1.0 ;
+ canvas:graph [
+ a atom:Tuple ;
+ rdf:value (
+ )
+ ] ;
+ ] .
diff --git a/canvas_display_ui.ttl b/canvas_display_ui.ttl
new file mode 100644
index 0000000..ab185be
--- /dev/null
+++ b/canvas_display_ui.ttl
@@ -0,0 +1,30 @@
+# 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 ui: <http://lv2plug.in/ns/extensions/ui#> .
+@prefix atom: <http://lv2plug.in/ns/ext/atom#> .
+@prefix urid: <http://lv2plug.in/ns/ext/urid#> .
+
+@prefix canvasdisp: <http://open-music-kontrollers.ch/lv2/canvas_display#> .
+
+canvasdisp:canvas_ui
+ ui:portNotification [
+ ui:plugin canvasdisp:canvas ;
+ lv2:symbol "notify" ;
+ ui:protocol atom:eventTransfer
+ ] ;
+ lv2:requiredFeature ui:idleInterface, urid:map ;
+ lv2:extensionData ui:idleInterface .
diff --git a/canvas_ui.c b/canvas_ui.c
new file mode 100644
index 0000000..eef5d19
--- /dev/null
+++ b/canvas_ui.c
@@ -0,0 +1,575 @@
+/*
+ * 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.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <math.h>
+
+#include <lv2/lv2plug.in/ns/ext/patch/patch.h>
+#include <lv2/lv2plug.in/ns/extensions/ui/ui.h>
+
+#include <canvas.lv2/forge.h>
+#include <canvas.lv2/render.h>
+
+#include <pugl/pugl.h>
+
+#include <cairo/cairo.h>
+
+typedef struct _plughandle_t plughandle_t;
+
+struct _plughandle_t {
+ LV2_URID_Map *map;
+ LV2_Atom_Forge forge;
+ LV2UI_Resize *host_resize;
+
+ PuglView *view;
+ int done;
+
+ LV2_Canvas canvas;
+
+ LV2_URID patch_Get;
+ LV2_URID patch_Set;
+ LV2_URID patch_Put;
+ LV2_URID patch_property;
+ LV2_URID patch_value;
+ LV2_URID patch_body;
+ LV2_URID atom_eventTransfer;
+
+ float aspect_ratio;
+ LV2_Atom_Tuple *graph;
+
+ LV2UI_Write_Function writer;
+ LV2UI_Controller controller;
+
+ union {
+ LV2_Atom atom;
+ uint8_t buf [512];
+ } dst;
+};
+
+static inline void
+_event_request(plughandle_t *handle)
+{
+ lv2_atom_forge_set_buffer(&handle->forge, handle->dst.buf, 512);
+}
+
+static inline void
+_event_commit(plughandle_t *handle)
+{
+ const uint32_t sz = lv2_atom_total_size(&handle->dst.atom);
+ handle->writer(handle->controller, 0, sz, handle->atom_eventTransfer, &handle->dst.atom);
+}
+
+static inline void
+_refresh(plughandle_t *handle)
+{
+ LV2_Atom_Forge_Frame obj_frame;
+ LV2_Atom_Forge_Ref ref;
+
+ _event_request(handle);
+
+ ref = lv2_atom_forge_object(&handle->forge, &obj_frame, 0, handle->patch_Get);
+ if(ref)
+ ref = lv2_atom_forge_key(&handle->forge, handle->patch_property);
+ if(ref)
+ ref = lv2_atom_forge_urid(&handle->forge, handle->canvas.urid.Canvas_aspectRatio);
+ if(ref)
+ lv2_atom_forge_pop(&handle->forge, &obj_frame);
+
+ if(ref)
+ _event_commit(handle);
+
+ _event_request(handle);
+
+ ref = lv2_atom_forge_object(&handle->forge, &obj_frame, 0, handle->patch_Get);
+ if(ref)
+ ref = lv2_atom_forge_key(&handle->forge, handle->patch_property);
+ if(ref)
+ ref = lv2_atom_forge_urid(&handle->forge, handle->canvas.urid.Canvas_graph);
+ if(ref)
+ lv2_atom_forge_pop(&handle->forge, &obj_frame);
+
+ if(ref)
+ _event_commit(handle);
+}
+
+static inline LV2_Atom_Forge_Ref
+_input_request(plughandle_t *handle, LV2_Atom_Forge_Frame frame [2])
+{
+ LV2_Atom_Forge_Ref ref;
+
+ _event_request(handle);
+
+ ref = lv2_atom_forge_object(&handle->forge, &frame[0], 0, handle->patch_Put);
+ if(ref)
+ ref = lv2_atom_forge_key(&handle->forge, handle->patch_body);
+ if(ref)
+ ref = lv2_atom_forge_object(&handle->forge, &frame[1], 0, 0);
+
+ return ref;
+}
+
+static inline void
+_input_commit(plughandle_t *handle, LV2_Atom_Forge_Frame frame [2])
+{
+ lv2_atom_forge_pop(&handle->forge, &frame[1]);
+ lv2_atom_forge_pop(&handle->forge, &frame[0]);
+
+ _event_commit(handle);
+}
+
+static inline void
+_expose(plughandle_t *handle)
+{
+#ifndef _WIN32 //FIXME
+ cairo_t *ctx = puglGetContext(handle->view);
+
+ if(ctx)
+ {
+ lv2_canvas_render(&handle->canvas, ctx, handle->graph);
+ }
+#endif
+}
+
+static inline void
+_close(plughandle_t *handle)
+{
+ handle->done = 1;
+}
+
+static inline void
+_configure(plughandle_t *handle, const PuglEventConfigure *e)
+{
+#ifndef _WIN32 //FIXME
+ cairo_t *ctx = puglGetContext(handle->view);
+
+ if(ctx)
+ {
+ cairo_surface_t *surf = cairo_get_target(ctx);
+ cairo_surface_set_device_scale(surf, e->width, e->height);
+ }
+#endif
+}
+
+static void
+_relative_position(PuglView *view, double xa, double ya, double *xr, double *yr)
+{
+ int width;
+ int height;
+
+ puglGetSize(view, &width, &height);
+
+ *xr = (double)xa / width;
+ *yr = (double)ya / height;
+}
+
+static void
+_event_func(PuglView *view, const PuglEvent *e)
+{
+ plughandle_t *handle = puglGetHandle(view);
+ LV2_Atom_Forge_Ref ref;
+ LV2_Atom_Forge_Frame frame [2];
+
+ switch(e->type)
+ {
+ case PUGL_CONFIGURE:
+ {
+ _configure(handle, (const PuglEventConfigure *)e);
+
+ puglPostRedisplay(handle->view);
+ } break;
+ case PUGL_EXPOSE:
+ {
+ _expose(handle);
+ } break;
+ case PUGL_CLOSE:
+ {
+ _close(handle);
+ } break;
+
+ case PUGL_FOCUS_IN:
+ // fall-through
+ case PUGL_FOCUS_OUT:
+ {
+ puglPostRedisplay(handle->view);
+ } break;
+
+ case PUGL_ENTER_NOTIFY:
+ case PUGL_LEAVE_NOTIFY:
+ {
+ double x, y;
+ _relative_position(view, e->crossing.x, e->crossing.y, &x, &y);
+
+ ref = _input_request(handle, frame);
+ if(ref)
+ ref = lv2_atom_forge_key(&handle->forge, handle->canvas.urid.Canvas_mousePositionX);
+ if(ref)
+ ref = lv2_atom_forge_double(&handle->forge, x);
+ if(ref)
+ ref = lv2_atom_forge_key(&handle->forge, handle->canvas.urid.Canvas_mousePositionY);
+ if(ref)
+ ref = lv2_atom_forge_double(&handle->forge, y);
+ if(ref)
+ ref = lv2_atom_forge_key(&handle->forge, handle->canvas.urid.Canvas_mouseFocus);
+ if(ref)
+ ref = lv2_atom_forge_bool(&handle->forge, e->type == PUGL_ENTER_NOTIFY ? 1 : 0);
+ if(ref)
+ _input_commit(handle, frame);
+
+ puglPostRedisplay(handle->view);
+ } break;
+
+ case PUGL_BUTTON_PRESS:
+ case PUGL_BUTTON_RELEASE:
+ {
+ double x, y;
+ _relative_position(view, e->button.x, e->button.y, &x, &y);
+
+ ref = _input_request(handle, frame);
+ if(ref)
+ ref = lv2_atom_forge_key(&handle->forge, handle->canvas.urid.Canvas_mousePositionX);
+ if(ref)
+ ref = lv2_atom_forge_double(&handle->forge, x);
+ if(ref)
+ ref = lv2_atom_forge_key(&handle->forge, handle->canvas.urid.Canvas_mousePositionY);
+ if(ref)
+ ref = lv2_atom_forge_double(&handle->forge, y);
+ LV2_URID button_urid;
+ switch(e->button.button)
+ {
+ case 3:
+ button_urid = handle->canvas.urid.Canvas_mouseButtonRight;
+ break;
+ case 2:
+ button_urid = handle->canvas.urid.Canvas_mouseButtonMiddle;
+ break;
+ case 1:
+ default:
+ button_urid = handle->canvas.urid.Canvas_mouseButtonLeft;
+ break;
+ }
+ if(ref)
+ ref = lv2_atom_forge_key(&handle->forge, button_urid);
+ if(ref)
+ ref = lv2_atom_forge_bool(&handle->forge, e->type == PUGL_BUTTON_PRESS ? 1 : 0);
+ if(ref)
+ _input_commit(handle, frame);
+ } break;
+
+ case PUGL_MOTION_NOTIFY:
+ {
+ double x, y;
+ _relative_position(view, e->motion.x, e->motion.y, &x, &y);
+
+ ref = _input_request(handle, frame);
+ if(ref)
+ ref = lv2_atom_forge_key(&handle->forge, handle->canvas.urid.Canvas_mousePositionX);
+ if(ref)
+ ref = lv2_atom_forge_double(&handle->forge, x);
+ if(ref)
+ ref = lv2_atom_forge_key(&handle->forge, handle->canvas.urid.Canvas_mousePositionY);
+ if(ref)
+ ref = lv2_atom_forge_double(&handle->forge, y);
+ if(ref)
+ _input_commit(handle, frame);
+ } break;
+
+ case PUGL_SCROLL:
+ {
+ double x, y;
+ _relative_position(view, e->scroll.x, e->scroll.y, &x, &y);
+
+ ref = _input_request(handle, frame);
+ if(ref)
+ ref = lv2_atom_forge_key(&handle->forge, handle->canvas.urid.Canvas_mousePositionX);
+ if(ref)
+ ref = lv2_atom_forge_double(&handle->forge, x);
+ if(ref)
+ ref = lv2_atom_forge_key(&handle->forge, handle->canvas.urid.Canvas_mousePositionY);
+ if(ref)
+ ref = lv2_atom_forge_double(&handle->forge, y);
+ if(ref)
+ ref = lv2_atom_forge_key(&handle->forge, handle->canvas.urid.Canvas_mouseWheelX);
+ if(ref)
+ ref = lv2_atom_forge_double(&handle->forge, e->scroll.dx);
+ if(ref)
+ ref = lv2_atom_forge_key(&handle->forge, handle->canvas.urid.Canvas_mouseWheelY);
+ if(ref)
+ ref = lv2_atom_forge_double(&handle->forge, e->scroll.dy);
+ if(ref)
+ _input_commit(handle, frame);
+ } break;
+
+ case PUGL_KEY_PRESS:
+ // fall-through
+ case PUGL_KEY_RELEASE:
+ // fall-through
+ case PUGL_NOTHING:
+ // fall-through
+ {
+ // nothing
+ } break;
+ }
+}
+
+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)
+{
+ plughandle_t *handle= calloc(1, sizeof(plughandle_t));
+ if(!handle)
+ return NULL;
+
+ void *parent = NULL;
+ for(int i=0; features[i]; i++)
+ {
+ if(!strcmp(features[i]->URI, LV2_UI__parent))
+ parent = features[i]->data;
+ else if(!strcmp(features[i]->URI, LV2_UI__resize))
+ handle->host_resize = features[i]->data;
+ else if(!strcmp(features[i]->URI, LV2_URID__map))
+ handle->map = features[i]->data;
+ }
+
+ if(!parent)
+ {
+ fprintf(stderr,
+ "%s: Host does not support ui:parent\n", descriptor->URI);
+ free(handle);
+ return NULL;
+ }
+ if(!handle->map)
+ {
+ fprintf(stderr,
+ "%s: Host does not support urid:map\n", descriptor->URI);
+ free(handle);
+ return NULL;
+ }
+
+ lv2_atom_forge_init(&handle->forge, handle->map);
+
+ handle->patch_Get = handle->map->map(handle->map->handle, LV2_PATCH__Get);
+ handle->patch_Set = handle->map->map(handle->map->handle, LV2_PATCH__Set);
+ handle->patch_Put = handle->map->map(handle->map->handle, LV2_PATCH__Put);
+ handle->patch_property = handle->map->map(handle->map->handle, LV2_PATCH__property);
+ handle->patch_value = handle->map->map(handle->map->handle, LV2_PATCH__value);
+ handle->patch_body = handle->map->map(handle->map->handle, LV2_PATCH__body);
+ handle->atom_eventTransfer= handle->map->map(handle->map->handle, LV2_ATOM__eventTransfer);
+
+ handle->view = puglInit(NULL, NULL);
+ if(!handle->view)
+ {
+ free(handle);
+ return NULL;
+ }
+
+ const unsigned w = 640;
+ const unsigned h = 640;;
+ puglInitWindowClass(handle->view, "canvas");
+ puglInitWindowParent(handle->view, (intptr_t)parent);
+ puglInitWindowSize(handle->view, w, h);
+ puglInitWindowMinSize(handle->view, w/8, h/8);
+ //puglInitWindowAspectRatio(handle->view, 1, 1, 1, 1);
+ puglInitResizable(handle->view, true);
+ puglInitTransientFor(handle->view, (intptr_t)parent);
+ puglSetHandle(handle->view, handle);
+ puglSetEventFunc(handle->view, _event_func);
+
+ int stat;
+#ifdef PUGL_HAVE_GL
+ puglInitContextType(handle->view, PUGL_CAIRO_GL);
+ stat = puglCreateWindow(handle->view, "CanvasGL");
+ if(stat != 0)
+#endif
+ {
+ fprintf(stderr, "falling back to non-GL\n");
+ puglInitContextType(handle->view, PUGL_CAIRO);
+ stat = puglCreateWindow(handle->view, "Canvas");
+ }
+
+ if(stat != 0)
+ {
+ puglDestroy(handle->view);
+ free(handle);
+ return NULL;
+ }
+ puglShowWindow(handle->view);
+
+#ifndef _WIN32 //FIXME
+ cairo_t *ctx = puglGetContext(handle->view);
+ cairo_surface_t *surf = cairo_get_target(ctx);
+ cairo_surface_set_device_scale(surf, w, h);
+#endif
+
+ const intptr_t child = puglGetNativeWindow(handle->view);
+ *(intptr_t *)widget = child;
+
+ if(handle->host_resize)
+ handle->host_resize->ui_resize(handle->host_resize->handle, w, h);
+
+ lv2_canvas_init(&handle->canvas, handle->map);
+
+ handle->controller = controller;
+ handle->writer = write_function;
+ _refresh(handle);
+
+ return handle;
+}
+
+static void
+cleanup(LV2UI_Handle instance)
+{
+ plughandle_t *handle = instance;
+
+ if(handle->graph)
+ free(handle->graph);
+
+ if(handle->view)
+ {
+ if(puglGetVisible(handle->view))
+ puglHideWindow(handle->view);
+ puglDestroy(handle->view);
+ }
+
+ free(handle);
+}
+
+static void
+port_event(LV2UI_Handle instance, uint32_t index, uint32_t size,
+ uint32_t protocol, const void *buf)
+{
+ plughandle_t *handle = instance;
+
+ if(protocol == handle->atom_eventTransfer) // notify
+ {
+ const LV2_Atom_Object *obj = buf;
+
+ if(lv2_atom_forge_is_object_type(&handle->forge, obj->atom.type))
+ {
+ if(obj->body.otype == handle->patch_Set)
+ {
+ const LV2_Atom_URID *property = NULL;
+ const LV2_Atom *value = NULL;
+
+ lv2_atom_object_get(obj,
+ handle->patch_property, &property,
+ handle->patch_value, &value,
+ 0);
+
+ if( property
+ && (property->atom.type == handle->forge.URID)
+ && (property->body == handle->canvas.urid.Canvas_graph)
+ && value
+ && (value->type == handle->forge.Tuple) )
+ {
+ if(handle->graph)
+ free(handle->graph);
+
+ const size_t sz = lv2_atom_total_size(value);
+ handle->graph = malloc(sz);
+ if(handle->graph)
+ memcpy(handle->graph, value, sz);
+
+ puglPostRedisplay(handle->view);
+ }
+ else if( property
+ && (property->atom.type == handle->forge.URID)
+ && (property->body == handle->canvas.urid.Canvas_aspectRatio)
+ && value
+ && (value->type == handle->forge.Float) )
+ {
+ handle->aspect_ratio = ((const LV2_Atom_Float *)value)->body;
+
+ int w;
+ int h;
+
+ puglGetSize(handle->view, &w, &h);
+
+ int W;
+ int H;
+
+ if(handle->aspect_ratio == 0.f)
+ {
+ W = w;
+ H = h;
+ }
+ else if(handle->aspect_ratio <= 1.f)
+ {
+ W = h * handle->aspect_ratio;
+ H = h;
+ }
+ else if(handle->aspect_ratio > 1.f)
+ {
+ W = w;
+ H = w / handle->aspect_ratio;
+ }
+
+ if(handle->host_resize && ( (W != w) || (H != h) ) )
+ handle->host_resize->ui_resize(handle->host_resize->handle, W, H);
+
+ puglPostRedisplay(handle->view);
+ }
+ }
+ }
+ }
+}
+
+static int
+_idle(LV2UI_Handle instance)
+{
+ plughandle_t *handle = instance;
+
+ puglProcessEvents(handle->view);
+
+ return handle->done;
+}
+
+static const LV2UI_Idle_Interface idle_ext = {
+ .idle = _idle
+};
+
+static const void *
+extension_data(const char *uri)
+{
+ if(!strcmp(uri, LV2_UI__idleInterface))
+ return &idle_ext;
+
+ return NULL;
+}
+
+const LV2UI_Descriptor canvas_canvas_ui = {
+ .URI = "http://open-music-kontrollers.ch/lv2/canvas_display#canvas_ui",
+ .instantiate = instantiate,
+ .cleanup = cleanup,
+ .port_event = port_event,
+ .extension_data = extension_data
+};
+
+LV2_SYMBOL_EXPORT const LV2UI_Descriptor*
+lv2ui_descriptor(uint32_t index)
+{
+ switch(index)
+ {
+ case 0:
+ return &canvas_canvas_ui;
+
+ default:
+ return NULL;
+ }
+}
diff --git a/manifest.ttl.in b/manifest.ttl.in
new file mode 100644
index 0000000..a085c66
--- /dev/null
+++ b/manifest.ttl.in
@@ -0,0 +1,34 @@
+# 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 canvasdisp: <http://open-music-kontrollers.ch/lv2/canvas_display#> .
+
+canvasdisp:canvas
+ a lv2:Plugin ;
+ lv2:minorVersion @MINOR_VERSION@ ;
+ lv2:microVersion @MICRO_VERSION@ ;
+ lv2:binary <canvas_display@MODULE_SUFFIX@> ;
+ ui:ui canvasdisp:canvas_ui ;
+ rdfs:seeAlso <canvas_display.ttl> .
+
+canvasdisp:canvas_ui
+ a ui:@UI_TYPE@ ;
+ ui:binary <canvas_display_ui@MODULE_SUFFIX@> ;
+ rdfs:seeAlso <canvas_display_ui.ttl> .
diff --git a/meson.build b/meson.build
new file mode 100644
index 0000000..35776d4
--- /dev/null
+++ b/meson.build
@@ -0,0 +1,105 @@
+project('canvas_display.lv2', 'c', default_options : [
+ 'buildtype=release',
+ 'warning_level=1',
+ 'werror=false',
+ 'b_lto=true',
+ 'c_std=c11'])
+
+cc = meson.get_compiler('c')
+
+m_dep = cc.find_library('m')
+lv2_dep = dependency('lv2', version : '>=1.14.0')
+cairo_dep = dependency('cairo', version : '>=1.0.0',
+ static : meson.is_cross_build())
+
+dsp_deps = [m_dep, lv2_dep, cairo_dep]
+ui_deps = [m_dep, lv2_dep, cairo_dep]
+
+pugl_inc = include_directories('pugl')
+canvas_inc = include_directories('canvas.lv2')
+ardour_inc = include_directories('ardour.lv2')
+inc_dir = [pugl_inc, canvas_inc, ardour_inc]
+
+inst_dir = join_paths(get_option('libdir'), 'lv2', meson.project_name())
+
+rawvers = run_command('cat', 'VERSION').stdout().strip()
+version = rawvers.split('.')
+
+conf_data = configuration_data()
+conf_data.set('MAJOR_VERSION', version[0])
+conf_data.set('MINOR_VERSION', version[1])
+conf_data.set('MICRO_VERSION', version[2])
+
+add_project_arguments('-D_GNU_SOURCE', language : 'c')
+add_project_arguments('-DPUGL_HAVE_GL', language : 'c')
+add_project_arguments('-DPUGL_HAVE_CAIRO', language : 'c')
+
+cp = find_program('cp')
+clone = [cp, '@INPUT@', '@OUTPUT@']
+
+dsp_srcs = ['canvas.c']
+
+ui_srcs = ['canvas_ui.c']
+
+c_args = ['-fvisibility=hidden',
+ '-ffast-math']
+
+if host_machine.system() == 'linux'
+ conf_data.set('UI_TYPE', 'X11UI')
+ ui_deps += dependency('gl')
+ ui_deps += dependency('x11', version : '>=1.6.0')
+ ui_deps += dependency('xext', version : '>=1.3.0')
+ ui_srcs += 'pugl/pugl/pugl_x11.c'
+elif host_machine.system() == 'windows'
+ add_languages('cpp')
+ conf_data.set('UI_TYPE', 'WindowsUI')
+ ui_deps += cc.find_library('opengl32')
+ ui_deps += cc.find_library('gdi32')
+ ui_deps += cc.find_library('user32')
+ ui_srcs += 'pugl/pugl/pugl_win.cpp'
+elif host_machine.system() == 'darwin'
+ #add_languages('objc')
+ conf_data.set('UI_TYPE', 'CocoaUI')
+ #ui_deps += cc.find_library('Cocoa')
+ #ui_deps += cc.find_library('gl')
+ #ui_deps += dependency('appleframeworks', modules : 'cocoa')
+ #ui_srcs += 'pugl/pugl/pugl_osx.m'
+endif
+
+mod = shared_module('canvas_display', dsp_srcs,
+ c_args : c_args,
+ include_directories : inc_dir,
+ name_prefix : '',
+ dependencies : dsp_deps,
+ install : true,
+ install_dir : inst_dir)
+
+ui = shared_module('canvas_display_ui', ui_srcs,
+ c_args : c_args,
+ include_directories : inc_dir,
+ name_prefix : '',
+ dependencies : ui_deps,
+ install : true,
+ install_dir : inst_dir)
+
+suffix = mod.full_path().strip().split('.')[-1]
+conf_data.set('MODULE_SUFFIX', '.' + suffix)
+
+configure_file(input : 'manifest.ttl.in', output : 'manifest.ttl',
+ configuration : conf_data,
+ install : true,
+ install_dir : inst_dir)
+
+custom_target('dsp_ttl',
+ input : 'canvas_display.ttl',
+ output : 'canvas_display.ttl',
+ command : clone,
+ install : true,
+ install_dir : inst_dir)
+
+custom_target('ui_ttl',
+ input : 'canvas_display_ui.ttl',
+ output : 'canvas_display_ui.ttl',
+ command : clone,
+ install : true,
+ install_dir : inst_dir)
diff --git a/meson/arm-linux-gnueabihf b/meson/arm-linux-gnueabihf
new file mode 100644
index 0000000..241266e
--- /dev/null
+++ b/meson/arm-linux-gnueabihf
@@ -0,0 +1,18 @@
+[host_machine]
+system = 'linux'
+cpu_family = 'arm'
+cpu = 'armv7hl'
+endian = 'little'
+
+[binaries]
+c = '/usr/bin/arm-linux-gnueabihf-gcc'
+cpp = '/usr/bin/arm-linux-gnueabihf-g++'
+ar = '/usr/bin/arm-linux-gnueabihf-ar'
+strip = '/usr/bin/arm-linux-gnueabihf-strip'
+pkgconfig = '/usr/bin/pkg-config'
+exe_wrapper = '/usr/bin/qemu-arm'
+
+[properties]
+root = '/usr/arm-linux-gnueabihf'
+c_link_args = ['-Wl,-z,defs']
+needs_exe_wrapper = true
diff --git a/meson/i686-linux-gnu b/meson/i686-linux-gnu
new file mode 100644
index 0000000..17d5a41
--- /dev/null
+++ b/meson/i686-linux-gnu
@@ -0,0 +1,19 @@
+[host_machine]
+system = 'linux'
+cpu_family = 'x86'
+cpu = 'i686'
+endian = 'little'
+
+[binaries]
+c = '/usr/bin/x86_64-linux-gnu-gcc'
+cpp = '/usr/bin/x86_64-linux-gnu-g++'
+ar = '/usr/bin/x86_64-linux-gnu-ar'
+strip = '/usr/bin/x86_64-linux-gnu-strip'
+pkgconfig = '/usr/bin/pkg-config'
+
+[properties]
+c_args = ['-m32']
+cpp_args = ['-m32']
+c_link_args = ['-m32', '-Wl,-z,defs']
+cpp_link_args = ['-m32', '-Wl,-z,defs']
+needs_exe_wrapper = false
diff --git a/meson/i686-w64-mingw32 b/meson/i686-w64-mingw32
new file mode 100644
index 0000000..4966ba7
--- /dev/null
+++ b/meson/i686-w64-mingw32
@@ -0,0 +1,17 @@
+[host_machine]
+system = 'windows'
+cpu_family = 'i686'
+cpu = 'i686'
+endian = 'little'
+
+[binaries]
+c = '/usr/bin/i686-w64-mingw32-gcc'
+cpp = '/usr/bin/i686-w64-mingw32-g++'
+ar = '/usr/bin/i686-w64-mingw32-ar'
+strip = '/usr/bin/i686-w64-mingw32-strip'
+pkgconfig = '/usr/bin/pkg-config'
+exe_wrapper = '/usr/bin/wine'
+
+[properties]
+root = '/usr/i686-w64-mingw32'
+needs_exe_wrapper = true
diff --git a/meson/universal-apple-darwin b/meson/universal-apple-darwin
new file mode 100644
index 0000000..0ee2e02
--- /dev/null
+++ b/meson/universal-apple-darwin
@@ -0,0 +1,19 @@
+[host_machine]
+system = 'darwin'
+cpu_family = 'x86_64'
+cpu = 'x86_64'
+endian = 'little'
+
+[binaries]
+c = '/usr/universal-apple-darwin/bin/x86_64-apple-darwin15-clang'
+cpp = '/usr/universal-apple-darwin/bin/x86_64-apple-darwin15-clang++'
+objc = '/usr/universal-apple-darwin/bin/x86_64-apple-darwin15-clang'
+ar = '/usr/universal-apple-darwin/bin/x86_64-apple-darwin15-ar'
+strip = '/usr/universal-apple-darwin/bin/x86_64-apple-darwin15-strip'
+pkgconfig = '/usr/bin/pkg-config'
+
+[properties]
+root = '/usr/universal-apple-darwin/SDK/MacOSX10.11.sdk'
+c_args = ['-arch', 'i386', '-arch', 'x86_64']
+c_link_args = ['-arch', 'i386', '-arch', 'x86_64']
+needs_exe_wrapper = true
diff --git a/meson/x86_64-linux-gnu b/meson/x86_64-linux-gnu
new file mode 100644
index 0000000..b09321c
--- /dev/null
+++ b/meson/x86_64-linux-gnu
@@ -0,0 +1,16 @@
+[host_machine]
+system = 'linux'
+cpu_family = 'x86_64'
+cpu = 'x86_64'
+endian = 'little'
+
+[binaries]
+c = '/usr/bin/x86_64-linux-gnu-gcc'
+cpp = '/usr/bin/x86_64-linux-gnu-g++'
+ar = '/usr/bin/x86_64-linux-gnu-ar'
+strip = '/usr/bin/x86_64-linux-gnu-strip'
+pkgconfig = '/usr/bin/pkg-config'
+
+[properties]
+c_link_args = ['-Wl,-z,defs']
+needs_exe_wrapper = false
diff --git a/meson/x86_64-w64-mingw32 b/meson/x86_64-w64-mingw32
new file mode 100644
index 0000000..40c8a32
--- /dev/null
+++ b/meson/x86_64-w64-mingw32
@@ -0,0 +1,17 @@
+[host_machine]
+system = 'windows'
+cpu_family = 'x86_64'
+cpu = 'x86_64'
+endian = 'little'
+
+[binaries]
+c = '/usr/bin/x86_64-w64-mingw32-gcc'
+cpp = '/usr/bin/x86_64-w64-mingw32-g++'
+ar = '/usr/bin/x86_64-w64-mingw32-ar'
+strip = '/usr/bin/x86_64-w64-mingw32-strip'
+pkgconfig = '/usr/bin/pkg-config'
+exe_wrapper = '/usr/bin/wine64'
+
+[properties]
+root = '/usr/x86_64-w64-mingw32'
+needs_exe_wrapper = true
diff --git a/.gitignore b/pugl/.gitignore
index e511642..e511642 100644
--- a/.gitignore
+++ b/pugl/.gitignore
diff --git a/AUTHORS b/pugl/AUTHORS
index 5625baa..5625baa 100644
--- a/AUTHORS
+++ b/pugl/AUTHORS
diff --git a/pugl/COPYING b/pugl/COPYING
new file mode 100644
index 0000000..e1e203d
--- /dev/null
+++ b/pugl/COPYING
@@ -0,0 +1,13 @@
+Copyright 2011-2014 David Robillard <http://drobilla.net>
+
+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.
+
+THIS 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.
diff --git a/Doxyfile.in b/pugl/Doxyfile.in
index 59e3331..59e3331 100644
--- a/Doxyfile.in
+++ b/pugl/Doxyfile.in
diff --git a/pugl/README.md b/pugl/README.md
new file mode 100644
index 0000000..77809d8
--- /dev/null
+++ b/pugl/README.md
@@ -0,0 +1,28 @@
+PUGL
+====
+
+Pugl is a minimal portable API for GUIs which supports embedding and is
+suitable for use in plugins. It works on X11, Mac OS X, and Windows. GUIs can
+be drawn with OpenGL or Cairo.
+
+Pugl is vaguely similar to GLUT, but with some significant distinctions:
+
+ * Minimal in scope, providing only what is necessary to draw and receive
+ keyboard and mouse input.
+
+ * No reliance on static data whatsoever, so the API can be used in plugins or
+ multiple independent parts of a program.
+
+ * Single implementation, which is small, liberally licensed Free / Open Source
+ Software, and suitable for direct inclusion in programs if avoiding a
+ library dependency is desired.
+
+ * Support for embedding in other windows, so Pugl code can draw to a widget
+ inside a larger GUI.
+
+ * More complete support for keyboard input, including additional "special"
+ keys, modifiers, and support for detecting individual modifier key presses.
+
+For more information, see <http://drobilla.net/software/pugl>.
+
+ -- David Robillard <d@drobilla.net>
diff --git a/pugl.pc.in b/pugl/pugl.pc.in
index d3606cd..d3606cd 100644
--- a/pugl.pc.in
+++ b/pugl/pugl.pc.in
diff --git a/pugl/cairo_gl.h b/pugl/pugl/cairo_gl.h
index 5c0f1f9..5c0f1f9 100644
--- a/pugl/cairo_gl.h
+++ b/pugl/pugl/cairo_gl.h
diff --git a/pugl/gl.h b/pugl/pugl/gl.h
index 9a6aeef..9a6aeef 100644
--- a/pugl/gl.h
+++ b/pugl/pugl/gl.h
diff --git a/pugl/glew.h b/pugl/pugl/glew.h
index 5374406..5374406 100644
--- a/pugl/glew.h
+++ b/pugl/pugl/glew.h
diff --git a/pugl/glu.h b/pugl/pugl/glu.h
index 0d3e8e1..0d3e8e1 100644
--- a/pugl/glu.h
+++ b/pugl/pugl/glu.h
diff --git a/pugl/pugl.h b/pugl/pugl/pugl.h
index dbbad90..dbbad90 100644
--- a/pugl/pugl.h
+++ b/pugl/pugl/pugl.h
diff --git a/pugl/pugl.hpp b/pugl/pugl/pugl.hpp
index 7d3ea9e..7d3ea9e 100644
--- a/pugl/pugl.hpp
+++ b/pugl/pugl/pugl.hpp
diff --git a/pugl/pugl_internal.h b/pugl/pugl/pugl_internal.h
index 45084de..45084de 100644
--- a/pugl/pugl_internal.h
+++ b/pugl/pugl/pugl_internal.h
diff --git a/pugl/pugl_osx.m b/pugl/pugl/pugl_osx.m
index 5f57798..5f57798 100644
--- a/pugl/pugl_osx.m
+++ b/pugl/pugl/pugl_osx.m
diff --git a/pugl/pugl_win.cpp b/pugl/pugl/pugl_win.cpp
index 6468963..6468963 100644
--- a/pugl/pugl_win.cpp
+++ b/pugl/pugl/pugl_win.cpp
diff --git a/pugl/pugl_x11.c b/pugl/pugl/pugl_x11.c
index a8daac4..a8daac4 100644
--- a/pugl/pugl_x11.c
+++ b/pugl/pugl/pugl_x11.c
diff --git a/pugl_cairo_test.c b/pugl/pugl_cairo_test.c
index c04c785..c04c785 100644
--- a/pugl_cairo_test.c
+++ b/pugl/pugl_cairo_test.c
diff --git a/pugl_test.c b/pugl/pugl_test.c
index 367e7a4..367e7a4 100644
--- a/pugl_test.c
+++ b/pugl/pugl_test.c
diff --git a/waf b/pugl/waf
index 2dbb06b..2dbb06b 100755
--- a/waf
+++ b/pugl/waf
Binary files differ
diff --git a/wscript b/pugl/wscript
index 349ee14..349ee14 100644
--- a/wscript
+++ b/pugl/wscript