aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitlab-ci.yml111
-rw-r--r--ChangeLog46
-rw-r--r--README.md96
-rw-r--r--VERSION2
-rw-r--r--gitlab-ci/generic.yml106
-rw-r--r--manifest.ttl.in37
-rw-r--r--meson.build496
-rw-r--r--meson_options.txt42
-rw-r--r--props.lv2/.gitlab-ci.yml2
-rw-r--r--props.lv2/COPYING201
-rw-r--r--props.lv2/README.md20
-rw-r--r--props.lv2/VERSION1
-rw-r--r--props.lv2/gitlab-ci/generic.yml106
-rw-r--r--props.lv2/meson.build82
-rw-r--r--props.lv2/props.h1226
-rw-r--r--props.lv2/test/chunk.binbin0 -> 16 bytes
-rw-r--r--props.lv2/test/manifest.ttl.in28
-rw-r--r--props.lv2/test/props.c323
-rw-r--r--props.lv2/test/props.ttl151
-rw-r--r--props.lv2/test/props_test.c647
-rw-r--r--screenshots/screenshot_1.pngbin149120 -> 201055 bytes
-rw-r--r--ser_atom.lv2/.gitlab-ci.yml63
-rw-r--r--ser_atom.lv2/COPYING201
-rw-r--r--ser_atom.lv2/README.md33
-rw-r--r--ser_atom.lv2/VERSION1
-rw-r--r--ser_atom.lv2/meson.build26
-rw-r--r--ser_atom.lv2/ser_atom.lv2/ser_atom.h215
-rw-r--r--ser_atom.lv2/test/ser_atom_test.c136
-rw-r--r--shells_bells.c356
-rw-r--r--shells_bells.h72
-rw-r--r--shells_bells.ttl.in140
-rw-r--r--shells_bells_ui.c754
-rw-r--r--shells_bells_ui.ttl35
-rw-r--r--subprojects/d2tk/.gitignore (renamed from .gitignore)0
-rw-r--r--subprojects/d2tk/.gitlab-ci.yml108
-rw-r--r--subprojects/d2tk/COPYING201
-rw-r--r--subprojects/d2tk/Doxyfile (renamed from Doxyfile)0
-rw-r--r--subprojects/d2tk/README.md62
-rw-r--r--subprojects/d2tk/VERSION1
-rwxr-xr-xsubprojects/d2tk/check_for_font (renamed from check_for_font)0
-rw-r--r--subprojects/d2tk/d2tk/backend.h (renamed from d2tk/backend.h)0
-rw-r--r--subprojects/d2tk/d2tk/base.h (renamed from d2tk/base.h)0
-rw-r--r--subprojects/d2tk/d2tk/config.h.in (renamed from d2tk/config.h.in)0
-rw-r--r--subprojects/d2tk/d2tk/core.h (renamed from d2tk/core.h)0
-rw-r--r--subprojects/d2tk/d2tk/d2tk.h (renamed from d2tk/d2tk.h)0
-rw-r--r--subprojects/d2tk/d2tk/frontend.h (renamed from d2tk/frontend.h)0
-rw-r--r--subprojects/d2tk/d2tk/frontend_fbdev.h (renamed from d2tk/frontend_fbdev.h)0
-rw-r--r--subprojects/d2tk/d2tk/frontend_glfw.h (renamed from d2tk/frontend_glfw.h)0
-rw-r--r--subprojects/d2tk/d2tk/frontend_pugl.h (renamed from d2tk/frontend_pugl.h)0
-rw-r--r--subprojects/d2tk/d2tk/hash.h (renamed from d2tk/hash.h)0
-rw-r--r--subprojects/d2tk/d2tk/util.h (renamed from d2tk/util.h)0
-rw-r--r--subprojects/d2tk/example/custom_cairo.c (renamed from example/custom_cairo.c)0
-rw-r--r--subprojects/d2tk/example/custom_nanovg.c (renamed from example/custom_nanovg.c)0
-rw-r--r--subprojects/d2tk/example/d2tk_fbdev.c (renamed from example/d2tk_fbdev.c)0
-rw-r--r--subprojects/d2tk/example/d2tk_glfw.c (renamed from example/d2tk_glfw.c)0
-rw-r--r--subprojects/d2tk/example/d2tk_pugl.c (renamed from example/d2tk_pugl.c)0
-rw-r--r--subprojects/d2tk/example/example.c (renamed from example/example.c)0
-rw-r--r--subprojects/d2tk/example/example.h (renamed from example/example.h)0
-rw-r--r--subprojects/d2tk/example/libre-arrow-circle-right.png (renamed from example/libre-arrow-circle-right.png)bin2133 -> 2133 bytes
-rw-r--r--subprojects/d2tk/example/libre-gui-file.png (renamed from example/libre-gui-file.png)bin1034 -> 1034 bytes
-rw-r--r--subprojects/d2tk/example/libre-gui-folder.png (renamed from example/libre-gui-folder.png)bin710 -> 710 bytes
-rw-r--r--subprojects/d2tk/linenoise/.gitignore (renamed from linenoise/.gitignore)0
-rw-r--r--subprojects/d2tk/linenoise/LICENSE (renamed from linenoise/LICENSE)0
-rw-r--r--subprojects/d2tk/linenoise/Makefile (renamed from linenoise/Makefile)0
-rw-r--r--subprojects/d2tk/linenoise/README.markdown (renamed from linenoise/README.markdown)0
-rw-r--r--subprojects/d2tk/linenoise/encodings/tools/EastAsianWidth.txt (renamed from linenoise/encodings/tools/EastAsianWidth.txt)0
-rw-r--r--subprojects/d2tk/linenoise/encodings/tools/UnicodeData.txt (renamed from linenoise/encodings/tools/UnicodeData.txt)0
-rw-r--r--subprojects/d2tk/linenoise/encodings/tools/generate_combining_char_table.rb (renamed from linenoise/encodings/tools/generate_combining_char_table.rb)0
-rw-r--r--subprojects/d2tk/linenoise/encodings/tools/generate_wide_char_table.rb (renamed from linenoise/encodings/tools/generate_wide_char_table.rb)0
-rwxr-xr-xsubprojects/d2tk/linenoise/encodings/utf8.c (renamed from linenoise/encodings/utf8.c)0
-rwxr-xr-xsubprojects/d2tk/linenoise/encodings/utf8.h (renamed from linenoise/encodings/utf8.h)0
-rwxr-xr-xsubprojects/d2tk/linenoise/example.c (renamed from linenoise/example.c)0
-rwxr-xr-xsubprojects/d2tk/linenoise/linenoise.c (renamed from linenoise/linenoise.c)0
-rwxr-xr-xsubprojects/d2tk/linenoise/linenoise.h (renamed from linenoise/linenoise.h)0
-rw-r--r--subprojects/d2tk/meson.build434
-rw-r--r--subprojects/d2tk/meson_options.txt47
-rw-r--r--subprojects/d2tk/nanovg/.gitignore (renamed from nanovg/.gitignore)0
-rw-r--r--subprojects/d2tk/nanovg/LICENSE.txt (renamed from nanovg/LICENSE.txt)0
-rw-r--r--subprojects/d2tk/nanovg/README.md (renamed from nanovg/README.md)0
-rw-r--r--subprojects/d2tk/nanovg/example/LICENSE_OFL.txt (renamed from nanovg/example/LICENSE_OFL.txt)0
-rw-r--r--subprojects/d2tk/nanovg/example/NotoEmoji-Regular.ttf (renamed from nanovg/example/NotoEmoji-Regular.ttf)bin418804 -> 418804 bytes
-rwxr-xr-xsubprojects/d2tk/nanovg/example/Roboto-Bold.ttf (renamed from nanovg/example/Roboto-Bold.ttf)bin135820 -> 135820 bytes
-rwxr-xr-xsubprojects/d2tk/nanovg/example/Roboto-Light.ttf (renamed from nanovg/example/Roboto-Light.ttf)bin140276 -> 140276 bytes
-rwxr-xr-xsubprojects/d2tk/nanovg/example/Roboto-Regular.ttf (renamed from nanovg/example/Roboto-Regular.ttf)bin145348 -> 145348 bytes
-rw-r--r--subprojects/d2tk/nanovg/example/demo.c (renamed from nanovg/example/demo.c)0
-rw-r--r--subprojects/d2tk/nanovg/example/demo.h (renamed from nanovg/example/demo.h)0
-rw-r--r--subprojects/d2tk/nanovg/example/entypo.ttf (renamed from nanovg/example/entypo.ttf)bin35392 -> 35392 bytes
-rw-r--r--subprojects/d2tk/nanovg/example/example_fbo.c (renamed from nanovg/example/example_fbo.c)0
-rw-r--r--subprojects/d2tk/nanovg/example/example_gl2.c (renamed from nanovg/example/example_gl2.c)0
-rw-r--r--subprojects/d2tk/nanovg/example/example_gl3.c (renamed from nanovg/example/example_gl3.c)0
-rw-r--r--subprojects/d2tk/nanovg/example/example_gles2.c (renamed from nanovg/example/example_gles2.c)0
-rw-r--r--subprojects/d2tk/nanovg/example/example_gles3.c (renamed from nanovg/example/example_gles3.c)0
-rw-r--r--subprojects/d2tk/nanovg/example/images.txt (renamed from nanovg/example/images.txt)0
-rw-r--r--subprojects/d2tk/nanovg/example/images/image1.jpg (renamed from nanovg/example/images/image1.jpg)bin25760 -> 25760 bytes
-rw-r--r--subprojects/d2tk/nanovg/example/images/image10.jpg (renamed from nanovg/example/images/image10.jpg)bin3439 -> 3439 bytes
-rw-r--r--subprojects/d2tk/nanovg/example/images/image11.jpg (renamed from nanovg/example/images/image11.jpg)bin3818 -> 3818 bytes
-rw-r--r--subprojects/d2tk/nanovg/example/images/image12.jpg (renamed from nanovg/example/images/image12.jpg)bin5452 -> 5452 bytes
-rw-r--r--subprojects/d2tk/nanovg/example/images/image2.jpg (renamed from nanovg/example/images/image2.jpg)bin24091 -> 24091 bytes
-rw-r--r--subprojects/d2tk/nanovg/example/images/image3.jpg (renamed from nanovg/example/images/image3.jpg)bin29282 -> 29282 bytes
-rw-r--r--subprojects/d2tk/nanovg/example/images/image4.jpg (renamed from nanovg/example/images/image4.jpg)bin23830 -> 23830 bytes
-rw-r--r--subprojects/d2tk/nanovg/example/images/image5.jpg (renamed from nanovg/example/images/image5.jpg)bin27131 -> 27131 bytes
-rw-r--r--subprojects/d2tk/nanovg/example/images/image6.jpg (renamed from nanovg/example/images/image6.jpg)bin25116 -> 25116 bytes
-rw-r--r--subprojects/d2tk/nanovg/example/images/image7.jpg (renamed from nanovg/example/images/image7.jpg)bin25590 -> 25590 bytes
-rw-r--r--subprojects/d2tk/nanovg/example/images/image8.jpg (renamed from nanovg/example/images/image8.jpg)bin24607 -> 24607 bytes
-rw-r--r--subprojects/d2tk/nanovg/example/images/image9.jpg (renamed from nanovg/example/images/image9.jpg)bin4035 -> 4035 bytes
-rw-r--r--subprojects/d2tk/nanovg/example/perf.c (renamed from nanovg/example/perf.c)0
-rw-r--r--subprojects/d2tk/nanovg/example/perf.h (renamed from nanovg/example/perf.h)0
-rw-r--r--subprojects/d2tk/nanovg/example/screenshot-01.png (renamed from nanovg/example/screenshot-01.png)bin989424 -> 989424 bytes
-rw-r--r--subprojects/d2tk/nanovg/example/screenshot-02.png (renamed from nanovg/example/screenshot-02.png)bin229443 -> 229443 bytes
-rw-r--r--subprojects/d2tk/nanovg/example/stb_image_write.h (renamed from nanovg/example/stb_image_write.h)0
-rw-r--r--subprojects/d2tk/nanovg/obsolete/nanovg_gl2.h (renamed from nanovg/obsolete/nanovg_gl2.h)0
-rw-r--r--subprojects/d2tk/nanovg/obsolete/nanovg_gl3.h (renamed from nanovg/obsolete/nanovg_gl3.h)0
-rw-r--r--subprojects/d2tk/nanovg/obsolete/obsolete.md (renamed from nanovg/obsolete/obsolete.md)0
-rw-r--r--subprojects/d2tk/nanovg/premake4.lua (renamed from nanovg/premake4.lua)0
-rw-r--r--subprojects/d2tk/nanovg/src/fontstash.h (renamed from nanovg/src/fontstash.h)0
-rw-r--r--subprojects/d2tk/nanovg/src/nanovg.c (renamed from nanovg/src/nanovg.c)0
-rw-r--r--subprojects/d2tk/nanovg/src/nanovg.h (renamed from nanovg/src/nanovg.h)0
-rw-r--r--subprojects/d2tk/nanovg/src/nanovg_gl.h (renamed from nanovg/src/nanovg_gl.h)0
-rw-r--r--subprojects/d2tk/nanovg/src/nanovg_gl_utils.h (renamed from nanovg/src/nanovg_gl_utils.h)0
-rw-r--r--subprojects/d2tk/nanovg/src/stb_image.h (renamed from nanovg/src/stb_image.h)0
-rw-r--r--subprojects/d2tk/nanovg/src/stb_truetype.h (renamed from nanovg/src/stb_truetype.h)0
-rw-r--r--subprojects/d2tk/pugl/.clang-format (renamed from pugl/.clang-format)0
-rw-r--r--subprojects/d2tk/pugl/.clang-tidy (renamed from pugl/.clang-tidy)0
-rw-r--r--subprojects/d2tk/pugl/.clant.json (renamed from pugl/.clant.json)0
-rw-r--r--subprojects/d2tk/pugl/.editorconfig (renamed from pugl/.editorconfig)0
-rw-r--r--subprojects/d2tk/pugl/.gitattributes (renamed from pugl/.gitattributes)0
-rw-r--r--subprojects/d2tk/pugl/.gitignore (renamed from pugl/.gitignore)0
-rw-r--r--subprojects/d2tk/pugl/.gitlab-ci.yml (renamed from pugl/.gitlab-ci.yml)0
-rw-r--r--subprojects/d2tk/pugl/.gitmodules (renamed from pugl/.gitmodules)0
-rw-r--r--subprojects/d2tk/pugl/.includes.imp (renamed from pugl/.includes.imp)0
-rw-r--r--subprojects/d2tk/pugl/AUTHORS (renamed from pugl/AUTHORS)0
-rw-r--r--subprojects/d2tk/pugl/COPYING (renamed from pugl/COPYING)0
-rw-r--r--subprojects/d2tk/pugl/README.md (renamed from pugl/README.md)0
-rw-r--r--subprojects/d2tk/pugl/bindings/cxx/include/.clang-tidy (renamed from pugl/bindings/cxx/include/.clang-tidy)0
-rw-r--r--subprojects/d2tk/pugl/bindings/cxx/include/pugl/cairo.hpp (renamed from pugl/bindings/cxx/include/pugl/cairo.hpp)0
-rw-r--r--subprojects/d2tk/pugl/bindings/cxx/include/pugl/gl.hpp (renamed from pugl/bindings/cxx/include/pugl/gl.hpp)0
-rw-r--r--subprojects/d2tk/pugl/bindings/cxx/include/pugl/pugl.hpp (renamed from pugl/bindings/cxx/include/pugl/pugl.hpp)0
-rw-r--r--subprojects/d2tk/pugl/bindings/cxx/include/pugl/stub.hpp (renamed from pugl/bindings/cxx/include/pugl/stub.hpp)0
-rw-r--r--subprojects/d2tk/pugl/bindings/cxx/include/pugl/vulkan.hpp (renamed from pugl/bindings/cxx/include/pugl/vulkan.hpp)0
-rw-r--r--subprojects/d2tk/pugl/doc/_static/meson.build (renamed from pugl/doc/_static/meson.build)0
-rw-r--r--subprojects/d2tk/pugl/doc/c/Doxyfile.in (renamed from pugl/doc/c/Doxyfile.in)0
-rw-r--r--subprojects/d2tk/pugl/doc/c/api/meson.build (renamed from pugl/doc/c/api/meson.build)0
-rw-r--r--subprojects/d2tk/pugl/doc/c/event-loop.rst (renamed from pugl/doc/c/event-loop.rst)0
-rw-r--r--subprojects/d2tk/pugl/doc/c/events.rst (renamed from pugl/doc/c/events.rst)0
-rw-r--r--subprojects/d2tk/pugl/doc/c/index.rst (renamed from pugl/doc/c/index.rst)0
-rw-r--r--subprojects/d2tk/pugl/doc/c/meson.build (renamed from pugl/doc/c/meson.build)0
-rw-r--r--subprojects/d2tk/pugl/doc/c/overview.rst (renamed from pugl/doc/c/overview.rst)0
-rw-r--r--subprojects/d2tk/pugl/doc/c/shutting-down.rst (renamed from pugl/doc/c/shutting-down.rst)0
-rw-r--r--subprojects/d2tk/pugl/doc/c/view.rst (renamed from pugl/doc/c/view.rst)0
-rw-r--r--subprojects/d2tk/pugl/doc/c/world.rst (renamed from pugl/doc/c/world.rst)0
-rw-r--r--subprojects/d2tk/pugl/doc/c/xml/meson.build (renamed from pugl/doc/c/xml/meson.build)0
-rw-r--r--subprojects/d2tk/pugl/doc/conf.py.in (renamed from pugl/doc/conf.py.in)0
-rw-r--r--subprojects/d2tk/pugl/doc/cpp/Doxyfile.in (renamed from pugl/doc/cpp/Doxyfile.in)0
-rw-r--r--subprojects/d2tk/pugl/doc/cpp/api/meson.build (renamed from pugl/doc/cpp/api/meson.build)0
-rw-r--r--subprojects/d2tk/pugl/doc/cpp/event-loop.rst (renamed from pugl/doc/cpp/event-loop.rst)0
-rw-r--r--subprojects/d2tk/pugl/doc/cpp/events.rst (renamed from pugl/doc/cpp/events.rst)0
-rw-r--r--subprojects/d2tk/pugl/doc/cpp/index.rst (renamed from pugl/doc/cpp/index.rst)0
-rw-r--r--subprojects/d2tk/pugl/doc/cpp/meson.build (renamed from pugl/doc/cpp/meson.build)0
-rw-r--r--subprojects/d2tk/pugl/doc/cpp/overview.rst (renamed from pugl/doc/cpp/overview.rst)0
-rw-r--r--subprojects/d2tk/pugl/doc/cpp/view.rst (renamed from pugl/doc/cpp/view.rst)0
-rw-r--r--subprojects/d2tk/pugl/doc/cpp/world.rst (renamed from pugl/doc/cpp/world.rst)0
-rw-r--r--subprojects/d2tk/pugl/doc/cpp/xml/meson.build (renamed from pugl/doc/cpp/xml/meson.build)0
-rw-r--r--subprojects/d2tk/pugl/doc/deployment.rst (renamed from pugl/doc/deployment.rst)0
-rw-r--r--subprojects/d2tk/pugl/doc/meson.build (renamed from pugl/doc/meson.build)0
-rw-r--r--subprojects/d2tk/pugl/doc/summary.rst (renamed from pugl/doc/summary.rst)0
-rw-r--r--subprojects/d2tk/pugl/examples/.clang-tidy (renamed from pugl/examples/.clang-tidy)0
-rw-r--r--subprojects/d2tk/pugl/examples/cube_view.h (renamed from pugl/examples/cube_view.h)0
-rw-r--r--subprojects/d2tk/pugl/examples/demo_utils.h (renamed from pugl/examples/demo_utils.h)0
-rw-r--r--subprojects/d2tk/pugl/examples/file_utils.c (renamed from pugl/examples/file_utils.c)0
-rw-r--r--subprojects/d2tk/pugl/examples/file_utils.h (renamed from pugl/examples/file_utils.h)0
-rw-r--r--subprojects/d2tk/pugl/examples/glad/glad.c (renamed from pugl/examples/glad/glad.c)0
-rw-r--r--subprojects/d2tk/pugl/examples/glad/glad.h (renamed from pugl/examples/glad/glad.h)0
-rw-r--r--subprojects/d2tk/pugl/examples/glad/khrplatform.h (renamed from pugl/examples/glad/khrplatform.h)0
-rw-r--r--subprojects/d2tk/pugl/examples/meson.build (renamed from pugl/examples/meson.build)0
-rw-r--r--subprojects/d2tk/pugl/examples/pugl_cairo_demo.c (renamed from pugl/examples/pugl_cairo_demo.c)0
-rw-r--r--subprojects/d2tk/pugl/examples/pugl_cursor_demo.c (renamed from pugl/examples/pugl_cursor_demo.c)0
-rw-r--r--subprojects/d2tk/pugl/examples/pugl_cxx_demo.cpp (renamed from pugl/examples/pugl_cxx_demo.cpp)0
-rw-r--r--subprojects/d2tk/pugl/examples/pugl_embed_demo.c (renamed from pugl/examples/pugl_embed_demo.c)0
-rw-r--r--subprojects/d2tk/pugl/examples/pugl_print_events.c (renamed from pugl/examples/pugl_print_events.c)0
-rw-r--r--subprojects/d2tk/pugl/examples/pugl_shader_demo.c (renamed from pugl/examples/pugl_shader_demo.c)0
-rw-r--r--subprojects/d2tk/pugl/examples/pugl_vulkan_cxx_demo.cpp (renamed from pugl/examples/pugl_vulkan_cxx_demo.cpp)0
-rw-r--r--subprojects/d2tk/pugl/examples/pugl_vulkan_demo.c (renamed from pugl/examples/pugl_vulkan_demo.c)0
-rw-r--r--subprojects/d2tk/pugl/examples/pugl_window_demo.c (renamed from pugl/examples/pugl_window_demo.c)0
-rw-r--r--subprojects/d2tk/pugl/examples/rects.h (renamed from pugl/examples/rects.h)0
-rw-r--r--subprojects/d2tk/pugl/examples/shader_utils.h (renamed from pugl/examples/shader_utils.h)0
-rw-r--r--subprojects/d2tk/pugl/examples/shaders/header_330.glsl (renamed from pugl/examples/shaders/header_330.glsl)0
-rw-r--r--subprojects/d2tk/pugl/examples/shaders/header_420.glsl (renamed from pugl/examples/shaders/header_420.glsl)0
-rw-r--r--subprojects/d2tk/pugl/examples/shaders/meson.build (renamed from pugl/examples/shaders/meson.build)0
-rw-r--r--subprojects/d2tk/pugl/examples/shaders/rect.frag (renamed from pugl/examples/shaders/rect.frag)0
-rw-r--r--subprojects/d2tk/pugl/examples/shaders/rect.vert (renamed from pugl/examples/shaders/rect.vert)0
-rw-r--r--subprojects/d2tk/pugl/examples/sybok.hpp (renamed from pugl/examples/sybok.hpp)0
-rw-r--r--subprojects/d2tk/pugl/include/.clang-tidy (renamed from pugl/include/.clang-tidy)0
-rw-r--r--subprojects/d2tk/pugl/include/pugl/cairo.h (renamed from pugl/include/pugl/cairo.h)0
-rw-r--r--subprojects/d2tk/pugl/include/pugl/gl.h (renamed from pugl/include/pugl/gl.h)0
-rw-r--r--subprojects/d2tk/pugl/include/pugl/pugl.h (renamed from pugl/include/pugl/pugl.h)0
-rw-r--r--subprojects/d2tk/pugl/include/pugl/stub.h (renamed from pugl/include/pugl/stub.h)0
-rw-r--r--subprojects/d2tk/pugl/include/pugl/vulkan.h (renamed from pugl/include/pugl/vulkan.h)0
-rw-r--r--subprojects/d2tk/pugl/meson.build (renamed from pugl/meson.build)0
-rw-r--r--subprojects/d2tk/pugl/meson/meson.build (renamed from pugl/meson/meson.build)0
-rw-r--r--subprojects/d2tk/pugl/meson_options.txt (renamed from pugl/meson_options.txt)0
-rw-r--r--subprojects/d2tk/pugl/pugl.pc.in (renamed from pugl/pugl.pc.in)0
-rw-r--r--subprojects/d2tk/pugl/resources/Info.plist.in (renamed from pugl/resources/Info.plist.in)0
-rw-r--r--subprojects/d2tk/pugl/resources/pugl.ipe (renamed from pugl/resources/pugl.ipe)0
-rw-r--r--subprojects/d2tk/pugl/resources/pugl.png (renamed from pugl/resources/pugl.png)bin2641 -> 2641 bytes
-rw-r--r--subprojects/d2tk/pugl/resources/pugl.svg (renamed from pugl/resources/pugl.svg)0
-rwxr-xr-xsubprojects/d2tk/pugl/scripts/cat.py (renamed from pugl/scripts/cat.py)0
-rwxr-xr-xsubprojects/d2tk/pugl/scripts/dox_to_sphinx.py (renamed from pugl/scripts/dox_to_sphinx.py)0
-rw-r--r--subprojects/d2tk/pugl/src/.clang-tidy (renamed from pugl/src/.clang-tidy)0
-rw-r--r--subprojects/d2tk/pugl/src/implementation.c (renamed from pugl/src/implementation.c)0
-rw-r--r--subprojects/d2tk/pugl/src/implementation.h (renamed from pugl/src/implementation.h)0
-rw-r--r--subprojects/d2tk/pugl/src/mac.h (renamed from pugl/src/mac.h)0
-rw-r--r--subprojects/d2tk/pugl/src/mac.m (renamed from pugl/src/mac.m)0
-rw-r--r--subprojects/d2tk/pugl/src/mac_cairo.m (renamed from pugl/src/mac_cairo.m)0
-rw-r--r--subprojects/d2tk/pugl/src/mac_gl.m (renamed from pugl/src/mac_gl.m)0
-rw-r--r--subprojects/d2tk/pugl/src/mac_stub.m (renamed from pugl/src/mac_stub.m)0
-rw-r--r--subprojects/d2tk/pugl/src/mac_vulkan.m (renamed from pugl/src/mac_vulkan.m)0
-rw-r--r--subprojects/d2tk/pugl/src/stub.h (renamed from pugl/src/stub.h)0
-rw-r--r--subprojects/d2tk/pugl/src/types.h (renamed from pugl/src/types.h)0
-rw-r--r--subprojects/d2tk/pugl/src/win.c (renamed from pugl/src/win.c)0
-rw-r--r--subprojects/d2tk/pugl/src/win.h (renamed from pugl/src/win.h)0
-rw-r--r--subprojects/d2tk/pugl/src/win_cairo.c (renamed from pugl/src/win_cairo.c)0
-rw-r--r--subprojects/d2tk/pugl/src/win_gl.c (renamed from pugl/src/win_gl.c)0
-rw-r--r--subprojects/d2tk/pugl/src/win_stub.c (renamed from pugl/src/win_stub.c)0
-rw-r--r--subprojects/d2tk/pugl/src/win_vulkan.c (renamed from pugl/src/win_vulkan.c)0
-rw-r--r--subprojects/d2tk/pugl/src/x11.c (renamed from pugl/src/x11.c)0
-rw-r--r--subprojects/d2tk/pugl/src/x11.h (renamed from pugl/src/x11.h)0
-rw-r--r--subprojects/d2tk/pugl/src/x11_cairo.c (renamed from pugl/src/x11_cairo.c)0
-rw-r--r--subprojects/d2tk/pugl/src/x11_gl.c (renamed from pugl/src/x11_gl.c)0
-rw-r--r--subprojects/d2tk/pugl/src/x11_stub.c (renamed from pugl/src/x11_stub.c)0
-rw-r--r--subprojects/d2tk/pugl/src/x11_vulkan.c (renamed from pugl/src/x11_vulkan.c)0
-rw-r--r--subprojects/d2tk/pugl/test/.clang-tidy (renamed from pugl/test/.clang-tidy)0
-rw-r--r--subprojects/d2tk/pugl/test/meson.build (renamed from pugl/test/meson.build)0
-rw-r--r--subprojects/d2tk/pugl/test/test_build.c (renamed from pugl/test/test_build.c)0
-rw-r--r--subprojects/d2tk/pugl/test/test_build.cpp (renamed from pugl/test/test_build.cpp)0
-rw-r--r--subprojects/d2tk/pugl/test/test_clipboard.c (renamed from pugl/test/test_clipboard.c)0
-rw-r--r--subprojects/d2tk/pugl/test/test_gl_hints.c (renamed from pugl/test/test_gl_hints.c)0
-rw-r--r--subprojects/d2tk/pugl/test/test_realize.c (renamed from pugl/test/test_realize.c)0
-rw-r--r--subprojects/d2tk/pugl/test/test_redisplay.c (renamed from pugl/test/test_redisplay.c)0
-rw-r--r--subprojects/d2tk/pugl/test/test_show_hide.c (renamed from pugl/test/test_show_hide.c)0
-rw-r--r--subprojects/d2tk/pugl/test/test_stub_hints.c (renamed from pugl/test/test_stub_hints.c)0
-rw-r--r--subprojects/d2tk/pugl/test/test_timer.c (renamed from pugl/test/test_timer.c)0
-rw-r--r--subprojects/d2tk/pugl/test/test_update.c (renamed from pugl/test/test_update.c)0
-rw-r--r--subprojects/d2tk/pugl/test/test_utils.h (renamed from pugl/test/test_utils.h)0
-rw-r--r--subprojects/d2tk/screenshots/screenshot_1.pngbin0 -> 149120 bytes
-rw-r--r--subprojects/d2tk/screenshots/screenshot_2.png (renamed from screenshots/screenshot_2.png)bin37532 -> 37532 bytes
-rw-r--r--subprojects/d2tk/screenshots/screenshot_3.png (renamed from screenshots/screenshot_3.png)bin128486 -> 128486 bytes
-rw-r--r--subprojects/d2tk/screenshots/screenshot_4.png (renamed from screenshots/screenshot_4.png)bin35888 -> 35888 bytes
-rw-r--r--subprojects/d2tk/screenshots/screenshot_5.png (renamed from screenshots/screenshot_5.png)bin51888 -> 51888 bytes
-rw-r--r--subprojects/d2tk/screenshots/screenshot_6.png (renamed from screenshots/screenshot_6.png)bin84697 -> 84697 bytes
-rw-r--r--subprojects/d2tk/screenshots/screenshot_7.png (renamed from screenshots/screenshot_7.png)bin42476 -> 42476 bytes
-rw-r--r--subprojects/d2tk/screenshots/screenshot_8.png (renamed from screenshots/screenshot_8.png)bin73108 -> 73108 bytes
-rw-r--r--subprojects/d2tk/src/backend_cairo.c (renamed from src/backend_cairo.c)0
-rw-r--r--subprojects/d2tk/src/backend_nanovg.c (renamed from src/backend_nanovg.c)0
-rw-r--r--subprojects/d2tk/src/base.c (renamed from src/base.c)0
-rw-r--r--subprojects/d2tk/src/base_bar.c (renamed from src/base_bar.c)0
-rw-r--r--subprojects/d2tk/src/base_bitmap.c (renamed from src/base_bitmap.c)0
-rw-r--r--subprojects/d2tk/src/base_button.c (renamed from src/base_button.c)0
-rw-r--r--subprojects/d2tk/src/base_combo.c (renamed from src/base_combo.c)0
-rw-r--r--subprojects/d2tk/src/base_cursor.c (renamed from src/base_cursor.c)0
-rw-r--r--subprojects/d2tk/src/base_custom.c (renamed from src/base_custom.c)0
-rw-r--r--subprojects/d2tk/src/base_dial.c (renamed from src/base_dial.c)0
-rw-r--r--subprojects/d2tk/src/base_flowmatrix.c (renamed from src/base_flowmatrix.c)0
-rw-r--r--subprojects/d2tk/src/base_frame.c (renamed from src/base_frame.c)0
-rw-r--r--subprojects/d2tk/src/base_image.c (renamed from src/base_image.c)0
-rw-r--r--subprojects/d2tk/src/base_internal.h (renamed from src/base_internal.h)0
-rw-r--r--subprojects/d2tk/src/base_label.c (renamed from src/base_label.c)0
-rw-r--r--subprojects/d2tk/src/base_layout.c (renamed from src/base_layout.c)0
-rw-r--r--subprojects/d2tk/src/base_lineedit.c (renamed from src/base_lineedit.c)0
-rw-r--r--subprojects/d2tk/src/base_link.c (renamed from src/base_link.c)0
-rw-r--r--subprojects/d2tk/src/base_meter.c (renamed from src/base_meter.c)0
-rw-r--r--subprojects/d2tk/src/base_pane.c (renamed from src/base_pane.c)0
-rw-r--r--subprojects/d2tk/src/base_pty.c (renamed from src/base_pty.c)0
-rw-r--r--subprojects/d2tk/src/base_scrollbar.c (renamed from src/base_scrollbar.c)0
-rw-r--r--subprojects/d2tk/src/base_separator.c (renamed from src/base_separator.c)0
-rw-r--r--subprojects/d2tk/src/base_spinner.c (renamed from src/base_spinner.c)0
-rw-r--r--subprojects/d2tk/src/base_table.c (renamed from src/base_table.c)0
-rw-r--r--subprojects/d2tk/src/base_textfield.c (renamed from src/base_textfield.c)0
-rw-r--r--subprojects/d2tk/src/base_tooltip.c (renamed from src/base_tooltip.c)0
-rw-r--r--subprojects/d2tk/src/base_vkb.c (renamed from src/base_vkb.c)0
-rw-r--r--subprojects/d2tk/src/base_wave.c (renamed from src/base_wave.c)0
-rw-r--r--subprojects/d2tk/src/core.c (renamed from src/core.c)0
-rw-r--r--subprojects/d2tk/src/core_internal.h (renamed from src/core_internal.h)0
-rw-r--r--subprojects/d2tk/src/frontend_fbdev.c (renamed from src/frontend_fbdev.c)0
-rw-r--r--subprojects/d2tk/src/frontend_glfw.c (renamed from src/frontend_glfw.c)0
-rw-r--r--subprojects/d2tk/src/frontend_pugl.c (renamed from src/frontend_pugl.c)0
-rw-r--r--subprojects/d2tk/src/hash.c (renamed from src/hash.c)0
-rw-r--r--subprojects/d2tk/src/mum.h (renamed from src/mum.h)0
-rw-r--r--subprojects/d2tk/src/util_spawn.c (renamed from src/util_spawn.c)0
-rw-r--r--subprojects/d2tk/test/base.c (renamed from test/base.c)0
-rw-r--r--subprojects/d2tk/test/core.c (renamed from test/core.c)0
-rw-r--r--subprojects/d2tk/test/mock.c (renamed from test/mock.c)0
-rw-r--r--subprojects/d2tk/test/mock.h (renamed from test/mock.h)0
-rw-r--r--subprojects/d2tk/ttf/FiraCode-Bold.ttf (renamed from ttf/FiraCode-Bold.ttf)bin315784 -> 315784 bytes
-rw-r--r--subprojects/d2tk/ttf/FiraCode-Light.ttf (renamed from ttf/FiraCode-Light.ttf)bin276684 -> 276684 bytes
-rw-r--r--subprojects/d2tk/ttf/FiraCode-Medium.ttf (renamed from ttf/FiraCode-Medium.ttf)bin286232 -> 286232 bytes
-rw-r--r--subprojects/d2tk/ttf/FiraCode-Regular.ttf (renamed from ttf/FiraCode-Regular.ttf)bin290360 -> 290360 bytes
-rw-r--r--subprojects/d2tk/ttf/FiraSans-Bold.ttf (renamed from ttf/FiraSans-Bold.ttf)bin521312 -> 521312 bytes
-rw-r--r--subprojects/d2tk/ttf/LICENSE (renamed from ttf/LICENSE)0
-rw-r--r--subprojects/d2tk/utf8.h/.clang-format (renamed from utf8.h/.clang-format)0
-rw-r--r--subprojects/d2tk/utf8.h/.gitignore (renamed from utf8.h/.gitignore)0
-rw-r--r--subprojects/d2tk/utf8.h/.travis.yml (renamed from utf8.h/.travis.yml)0
-rw-r--r--subprojects/d2tk/utf8.h/LICENSE (renamed from utf8.h/LICENSE)0
-rw-r--r--subprojects/d2tk/utf8.h/README.md (renamed from utf8.h/README.md)0
-rw-r--r--subprojects/d2tk/utf8.h/appveyor.yml (renamed from utf8.h/appveyor.yml)0
-rw-r--r--subprojects/d2tk/utf8.h/test/CMakeLists.txt (renamed from utf8.h/test/CMakeLists.txt)0
-rw-r--r--subprojects/d2tk/utf8.h/test/main.c (renamed from utf8.h/test/main.c)0
-rw-r--r--subprojects/d2tk/utf8.h/test/test.c (renamed from utf8.h/test/test.c)0
-rw-r--r--subprojects/d2tk/utf8.h/test/test.cpp (renamed from utf8.h/test/test.cpp)0
-rw-r--r--subprojects/d2tk/utf8.h/test/utest.h (renamed from utf8.h/test/utest.h)0
-rw-r--r--subprojects/d2tk/utf8.h/utf8.h (renamed from utf8.h/utf8.h)0
310 files changed, 6051 insertions, 557 deletions
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 4847758..b91d04a 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -1,108 +1,5 @@
-stages:
- - build
- - deploy
+include:
+ - local: 'gitlab-ci/generic.yml'
-# templates
-.variables_template: &variables_definition
- variables:
- BASE_NAME: "d2tk"
- PKG_CONFIG_PATH: "/opt/lv2/lib/pkgconfig:/opt/${CI_BUILD_NAME}/lib/pkgconfig:/usr/lib/${CI_BUILD_NAME}/pkgconfig"
-
-.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
- stage: build
- script:
- - meson --prefix="/" --libdir="lib" --cross-file "${CI_BUILD_NAME}" build
- - sed -i -e '/framework/s/-Wl,-O1//g' -e '/framework/s/-Wl,--start-group//g' -e '/framework/s/-Wl,--end-group//g' build/build.ninja
- - ninja -C build
- - DESTDIR="${CI_PROJECT_DIR}/${BASE_NAME}-$(cat VERSION)/${CI_BUILD_NAME}" ninja -C build install
-
-.test_template: &test_definition
- <<: *common_definition
- stage: build
- script:
- - meson --prefix="/" --libdir="lib" --cross-file "${CI_BUILD_NAME}" build
- - sed -i -e '/framework/s/-Wl,-O1//g' -e '/framework/s/-Wl,--start-group//g' -e '/framework/s/-Wl,--end-group//g' build/build.ninja
- - ninja -C build
- - DESTDIR="${CI_PROJECT_DIR}/${BASE_NAME}-$(cat VERSION)/${CI_BUILD_NAME}" ninja -C build install
-
- - meson test -C build
-
-.analyze_template: &analyze_definition
- <<: *common_definition
- stage: build
- script:
- - meson --prefix="/" --libdir="lib" --cross-file "${CI_BUILD_NAME}" build
- - sed -i -e '/framework/s/-Wl,-O1//g' -e '/framework/s/-Wl,--start-group//g' -e '/framework/s/-Wl,--end-group//g' build/build.ninja
- - ninja -C build
- - DESTDIR="${CI_PROJECT_DIR}/${BASE_NAME}-$(cat VERSION)/${CI_BUILD_NAME}" ninja -C build install
-
- - meson test -C build
- - meson test -C build --wrap=valgrind
-
- - CC=clang CXX=clang++ meson --prefix="/" --libdir="lib" --cross-file "${CI_BUILD_NAME}" clang
- - ninja -C clang
- - ninja -C clang test
-
- - scan-build --status-bugs meson --prefix="/" --libdir="lib" --cross-file "${CI_BUILD_NAME}" scanbuild
- - scan-build --status-bugs ninja -C scanbuild
- - scan-build --status-bugs ninja -C scanbuild test
-
-.universal_linux_template: &universal_linux_definition
- image: ventosus/universal-linux-gnu:buster
- <<: *analyze_definition
-
-.arm_linux_template: &arm_linux_definition
- image: ventosus/arm-linux-gnueabihf:buster
- <<: *test_definition
-
-# targets
-x86_64-linux-gnu:
- before_script:
- - apt-get install -y libglu1-mesa-dev libevdev-dev libvterm-dev
- <<: *universal_linux_definition
-
-i686-linux-gnu:
- before_script:
- - apt-get install -y libglu1-mesa-dev:i386 libevdev-dev:i386 libvterm-dev:i386
- <<: *universal_linux_definition
-
-arm-linux-gnueabihf:
- before_script:
- - apt-get install -y libglu1-mesa-dev:armhf libevdev-dev:armhf libvterm-dev:armhf
- <<: *arm_linux_definition
-
-aarch64-linux-gnu:
- before_script:
- - apt-get install -y libglu1-mesa-dev:arm64 libevdev-dev:arm64 libvterm-dev:arm64
- <<: *arm_linux_definition
-
-pack:
- <<: *variables_definition
- stage: deploy
- script:
- - echo 'packing up...'
- artifacts:
- name: "${BASE_NAME}-$(cat VERSION)"
- paths:
- - "${BASE_NAME}-$(cat VERSION)/"
-
-pages:
- stage: deploy
- before_script:
- - apt-get update -y
- - apt-get install -y doxygen
- script:
- - doxygen
- - cp -r doc/html public
- artifacts:
- paths:
- - public/
+variables:
+ BUILD_OPTS: "-Duse-fontconfig=disabled"
diff --git a/ChangeLog b/ChangeLog
new file mode 100644
index 0000000..a2fba10
--- /dev/null
+++ b/ChangeLog
@@ -0,0 +1,46 @@
+# Changelog
+
+## [0.10.0] - 14 Apr 2021
+
+### Fixed
+
+* license URI in turtle manifest
+* pseudo terminal mouse support discovery
+* SHELL environment variable parsing
+* termination of spawned processes
+
+### Added
+
+* missing ui:parent feature in turtle manifest
+* cloning of parent environment
+* missing signal header include
+
+### Updated
+
+* to latest pugl
+
+## [0.8.0] - 15 Jan 2021
+
+### Added
+
+* support for host provided scaling factor
+* support for notifying host about changed parameters
+
+## [0.6.0] - 15 Jul 2020
+
+### Added
+
+* forkpty wrapper which uses clone or vfork instead of fork
+* support for GL double buffering
+
+## [0.4.0] - 13 Apr 2020
+
+### Changed
+
+* to build with pugl master
+* to use spinners instead of dials
+* to automagically adapt color theme to terminal colors
+
+## [0.2.0] - 15 Jan 2020
+
+### Initial release
diff --git a/README.md b/README.md
index 51c9529..fd010ce 100644
--- a/README.md
+++ b/README.md
@@ -1,52 +1,92 @@
-# d2tk
+## Shells Bells
-## Data Driven Tool Kit
+### A just-for-fun LV2 plugin bundle
-A performant, dyamic, immediate-mode GUI tool kit in C which partially renders
-on-change only by massively hashing-and-cashing of vector drawing instructions
-and on-demand rendered sprites.
+The most limited, useless, hackable and fun plugin bundle ever. Sound the bell in the shell
+(or from any program forked from the latter) to send a MIDI note.
-### Build / test
+#### Build status
- git clone https://git.open-music-kontrollers.ch/lad/d2tk
- cd d2tk
- meson build
- cd build
- ninja -j4
+[![build status](https://gitlab.com/OpenMusicKontrollers/shells_bells.lv2/badges/master/build.svg)](https://gitlab.com/OpenMusicKontrollers/shells_bells.lv2/commits/master)
+
+### Binaries
+
+For GNU/Linux (84-bit, 32-bit).
+
+To install the plugin bundle on your system, simply copy the __shells_bells.lv2__
+folder out of the platform folder of the downloaded package into your
+[LV2 path](http://lv2plug.in/pages/filesystem-hierarchy-standard.html).
+
+#### Stable release
+
+* [shells_bells.lv2-0.10.0.zip](https://dl.open-music-kontrollers.ch/shells_bells.lv2/stable/shells_bells.lv2-0.10.0.zip) ([sig](https://dl.open-music-kontrollers.ch/shells_bells.lv2/stable/shells_bells.lv2-0.10.0.zip.sig))
+
+#### Unstable (nightly) release
+
+* [shells_bells.lv2-latest-unstable.zip](https://dl.open-music-kontrollers.ch/shells_bells.lv2/unstable/shells_bells.lv2-latest-unstable.zip) ([sig](https://dl.open-music-kontrollers.ch/shells_bells.lv2/unstable/shells_bells.lv2-latest-unstable.zip.sig))
-#### Pugl/NanoVG backend
+### Sources
- ./d2tk.nanovg
+#### Stable release
-#### Pugl/Cairo backend
+* [shells_bells.lv2-0.10.0.tar.xz](https://git.open-music-kontrollers.ch/lv2/shells_bells.lv2/snapshot/shells_bells.lv2-0.10.0.tar.xz)
+([sig](https://git.open-music-kontrollers.ch/lv2/shells_bells.lv2/snapshot/shells_bells.lv2-0.10.0.tar.xz.asc))
- ./d2tk.cairo
+#### Git repository
-#### FBdev/Cairo backend
+* <https://git.open-music-kontrollers.ch/lv2/shells_bells.lv2>
- ./d2tk.fbdev
+<!--
+### Packages
-### Screenshots
+* [ArchLinux](https://www.archlinux.org/packages/community/x86_64/shells_bells.lv2/)
+-->
-![Screenshot 1](/screenshots/screenshot_1.png)
+### Bugs and feature requests
-![Screenshot 2](/screenshots/screenshot_2.png)
+* [Gitlab](https://gitlab.com/OpenMusicKontrollers/shells_bells.lv2)
+* [Github](https://github.com/OpenMusicKontrollers/shells_bells.lv2)
-![Screenshot 3](/screenshots/screenshot_3.png)
+#### Plugins
+
+![Screenshot](/screenshots/screenshot_1.png)
+
+##### Bells
+
+Its UI drops you into a shell and whenever you sound the bell, a MIDI note
+is played back on its DSP side.
+
+#### Dependencies
+
+* [LV2](http://lv2plug.in) (LV2 Plugin Standard)
+* [OpenGl](https://www.opengl.org) (OpenGl)
+* [GLEW](http://glew.sourceforge.net) (GLEW)
+* [VTERM](http://www.leonerd.org.uk/code/libvterm) (Virtual terminal emulator)
+
+#### Build / install
+
+ git clone https://git.open-music-kontrollers.ch/lv2/shells_bells.lv2
+ cd shells_bells.lv2
+ meson build
+ cd build
+ ninja -j4
+ ninja test
+ sudo ninja install
-![Screenshot 4](/screenshots/screenshot_4.png)
+#### UI
-![Screenshot 5](/screenshots/screenshot_5.png)
+On hi-DPI displays, the UI scales automatically if you have set the correct DPI
+in your ~/.Xresources.
-![Screenshot 6](/screenshots/screenshot_6.png)
+ Xft.dpi: 200
-![Screenshot 7](/screenshots/screenshot_7.png)
+If not, you can manually set your DPI via environmental variable *D2TK_SCALE*:
-![Screenshot 8](/screenshots/screenshot_8.png)
+ export D2TK_SCALE=200
-### License
+#### License
-Copyright (c) 2018-2019 Hanspeter Portner (dev@open-music-kontrollers.ch)
+Copyright (c) 2019-2021 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
diff --git a/VERSION b/VERSION
index e5e81a2..78bc1ab 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-0.1.1259
+0.10.0
diff --git a/gitlab-ci/generic.yml b/gitlab-ci/generic.yml
new file mode 100644
index 0000000..5cd2abc
--- /dev/null
+++ b/gitlab-ci/generic.yml
@@ -0,0 +1,106 @@
+stages:
+ - build
+ - deploy
+
+variables:
+ PKG_CONFIG_PATH: "/opt/lv2/lib/pkgconfig:/opt/${CI_BUILD_NAME}/lib/pkgconfig:/usr/lib/${CI_BUILD_NAME}/pkgconfig"
+ BUILD_OPTS : ""
+
+.native_template: &native_definition
+ stage: build
+ script:
+ - meson --prefix="${CI_PROJECT_DIR}/${CI_PROJECT_NAME}-$(cat VERSION)/${CI_BUILD_NAME}" -Dlv2libdir="" --cross-file "${CI_BUILD_NAME}" ${BUILD_OPTS} build
+ - ninja -C build
+ - ninja -C build test
+ - ninja -C build install
+
+ - scan-build --status-bugs meson --prefix="${CI_PROJECT_DIR}/${CI_PROJECT_NAME}-$(cat VERSION)/${CI_BUILD_NAME}" -Dlv2libdir="" --cross-file "${CI_BUILD_NAME}" ${BUILD_OPTS} scanbuild
+ - scan-build --status-bugs ninja -C scanbuild
+ - scan-build --status-bugs ninja -C scanbuild test
+ artifacts:
+ name: "${CI_PROJECT_NAME}-$(cat VERSION)-${CI_BUILD_NAME}"
+ paths:
+ - "${CI_PROJECT_NAME}-$(cat VERSION)/${CI_BUILD_NAME}/"
+
+.cross_template: &cross_definition
+ stage: build
+ script:
+ - meson --prefix="${CI_PROJECT_DIR}/${CI_PROJECT_NAME}-$(cat VERSION)/${CI_BUILD_NAME}" -Dlv2libdir="" --cross-file "${CI_BUILD_NAME}" ${BUILD_OPTS} build
+ - ninja -C build
+ - ninja -C build test
+ - ninja -C build install
+ artifacts:
+ name: "${CI_PROJECT_NAME}-$(cat VERSION)-${CI_BUILD_NAME}"
+ paths:
+ - "${CI_PROJECT_NAME}-$(cat VERSION)/${CI_BUILD_NAME}/"
+
+# build
+.universal_linux_template_stretch: &universal_linux_definition_stretch
+ image: ventosus/universal-linux-gnu:stretch
+ <<: *cross_definition
+
+.universal_linux_template_buster: &universal_linux_definition_buster
+ image: ventosus/universal-linux-gnu:buster
+ <<: *native_definition
+
+.universal_linux_template_bullseye: &universal_linux_definition_bullseye
+ image: ventosus/universal-linux-gnu:bullseye
+ <<: *native_definition
+
+.arm_linux_template_stretch: &arm_linux_definition_stretch
+ image: ventosus/arm-linux-gnueabihf:stretch
+ <<: *cross_definition
+
+.arm_linux_template_buster: &arm_linux_definition_buster
+ image: ventosus/arm-linux-gnueabihf:buster
+ <<: *cross_definition
+
+.arm_linux_template_bullseye: &arm_linux_definition_bullseye
+ image: ventosus/arm-linux-gnueabihf:bullseye
+ <<: *cross_definition
+
+# build
+x86_64-linux-gnu-stretch:
+ <<: *universal_linux_definition_stretch
+
+x86_64-linux-gnu-buster:
+ <<: *universal_linux_definition_buster
+
+x86_64-linux-gnu-bullseye:
+ <<: *universal_linux_definition_bullseye
+
+i686-linux-gnu-stretch:
+ <<: *universal_linux_definition_stretch
+
+i686-linux-gnu-buster:
+ <<: *universal_linux_definition_buster
+
+i686-linux-gnu-bullseye:
+ <<: *universal_linux_definition_bullseye
+
+arm-linux-gnueabihf-stretch:
+ <<: *arm_linux_definition_stretch
+
+arm-linux-gnueabihf-buster:
+ <<: *arm_linux_definition_buster
+
+arm-linux-gnueabihf-bullseye:
+ <<: *arm_linux_definition_bullseye
+
+aarch64-linux-gnu-stretch:
+ <<: *arm_linux_definition_stretch
+
+aarch64-linux-gnu-buster:
+ <<: *arm_linux_definition_buster
+
+aarch64-linux-gnu-bullseye:
+ <<: *arm_linux_definition_bullseye
+
+pack:
+ stage: deploy
+ script:
+ - echo 'packing up'
+ artifacts:
+ name: "${CI_PROJECT_NAME}-$(cat VERSION)"
+ paths:
+ - "${CI_PROJECT_NAME}-$(cat VERSION)/"
diff --git a/manifest.ttl.in b/manifest.ttl.in
new file mode 100644
index 0000000..5ea91f5
--- /dev/null
+++ b/manifest.ttl.in
@@ -0,0 +1,37 @@
+# Copyright (c) 2019-2021 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 pset: <http://lv2plug.in/ns/ext/presets#> .
+
+@prefix shells_bells: <http://open-music-kontrollers.ch/lv2/shells_bells#> .
+
+# Mephisto Audio 1x1
+shells_bells:bells
+ a lv2:Plugin ;
+ lv2:minorVersion @MINOR_VERSION@ ;
+ lv2:microVersion @MICRO_VERSION@ ;
+ ui:ui shells_bells:ui ;
+ lv2:binary <shells_bells@MODULE_SUFFIX@> ;
+ rdfs:seeAlso <shells_bells.ttl> .
+
+# UIs
+shells_bells:ui
+ a ui:X11UI ;
+ ui:binary <shells_bells_ui@MODULE_SUFFIX@> ;
+ rdfs:seeAlso <shells_bells_ui.ttl> .
diff --git a/meson.build b/meson.build
index 8e8e9ed..c9a6021 100644
--- a/meson.build
+++ b/meson.build
@@ -1,434 +1,152 @@
-project('d2tk', 'c', default_options : [
+project('shells_bells.lv2', 'c', default_options : [
'buildtype=release',
'warning_level=3',
'werror=false',
'b_lto=false',
'c_std=gnu11'])
-static_link = false #meson.is_cross_build()
+d2tk = subproject('d2tk')
-build_debug_overlay = get_option('build-debug-overlay')
-build_examples = get_option('build-examples')
+lv2libdir = get_option('lv2libdir')
build_tests = get_option('build-tests')
-build_doc = get_option('build-doc')
-
-use_backend_cairo = get_option('use-backend-cairo')
-use_backend_nanovg = get_option('use-backend-nanovg')
-use_frontend_fbdev = get_option('use-frontend-fbdev')
-use_frontend_pugl = get_option('use-frontend-pugl')
-use_frontend_glfw = get_option('use-frontend-glfw')
-
-use_evdev = get_option('use-evdev')
use_fontconfig = get_option('use-fontconfig')
-grep = find_program('grep',
- native : true,
- required : use_fontconfig)
-fc_list = find_program('fc-list',
- native : true,
- required : use_fontconfig)
-check_for_font = find_program('check_for_font',
- native : true,
- required : use_fontconfig)
-robodoc = find_program('robodoc',
- native : true,
- required : build_doc)
-
-cc = meson.get_compiler('c')
-
-# mandatory dependencies
-m_dep = cc.find_library('m')
-thread_dep = dependency('threads')
-
-# dependencies for backend_cairo/frontend_pugl
-freetype_dep = dependency('freetype2',
- version : '>=18.0.0',
- static : static_link,
- required : use_backend_cairo)
-pixman_dep = dependency('pixman-1',
- version : '>=0.34.0',
- static : static_link,
- required : use_backend_cairo)
-cairo_dep = dependency('cairo',
- version : '>=1.14.0',
- static : static_link,
- required : use_backend_cairo)
-cairo_deps = [freetype_dep, pixman_dep, cairo_dep]
-if use_frontend_pugl.enabled()
- cairo_xlib_dep = dependency('cairo-xlib',
- version : '>=1.14.0',
- static : static_link,
- required : use_backend_cairo)
-endif
-
-# dependencies for frontend_fbdev
-input_dep = dependency('libinput',
- version : '>=1.6.0',
- static : static_link,
- required : use_frontend_fbdev)
-udev_dep = dependency('libudev',
- static : static_link,
- required : use_frontend_fbdev)
-evdev_dep = dependency('libevdev',
- version : '>=1.5.0',
- static : static_link,
- required : use_frontend_fbdev)
-
-# dependencies for backend_nanovg
-glu_dep = dependency('glu',
- version : '>=9.0.0',
- static : static_link,
- required : use_backend_nanovg)
-glew_dep = dependency('glew',
- version : '>=2.0.0',
- static : static_link,
- required : use_backend_nanovg)
-if use_frontend_glfw.enabled()
- glfw_dep = dependency('glfw3',
- version : '>=3.3.0',
- static : static_link,
- required : use_backend_nanovg)
-endif
-
-# optional dependencies
-util_dep = cc.find_library('util')
-vterm_dep = dependency('vterm',
- version : '>=0.1',
- static : static_link)
-if not use_frontend_fbdev.enabled()
- evdev_dep = dependency('libevdev',
- version : '>=1.5.0',
- static : static_link,
- required : use_evdev)
-endif
-fontconfig_dep = dependency('fontconfig',
- version : '>=2.0.0',
- static : static_link,
- required : use_fontconfig)
-
-deps = [m_dep, thread_dep, evdev_dep, util_dep, vterm_dep, fontconfig_dep]
-links = []
-
-d2tk_inc = include_directories('')
-pugl_inc = include_directories(join_paths('pugl', 'include'))
-nanovg_inc = include_directories(join_paths('nanovg', 'src'))
-linenoise_inc = include_directories('linenoise')
-inc_dir = [d2tk_inc, pugl_inc, nanovg_inc, linenoise_inc]
-
-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')
-
-if build_debug_overlay
- conf_data.set('D2TK_DEBUG', 1)
-else
- conf_data.set('D2TK_DEBUG', 0)
-endif
-
-lib_srcs = [
- join_paths('src', 'hash.c'),
- join_paths('src', 'core.c'),
- join_paths('src', 'base.c'),
- join_paths('src', 'base_table.c'),
- join_paths('src', 'base_frame.c'),
- join_paths('src', 'base_layout.c'),
- join_paths('src', 'base_scrollbar.c'),
- join_paths('src', 'base_pane.c'),
- join_paths('src', 'base_cursor.c'),
- join_paths('src', 'base_button.c'),
- join_paths('src', 'base_image.c'),
- join_paths('src', 'base_bitmap.c'),
- join_paths('src', 'base_custom.c'),
- join_paths('src', 'base_meter.c'),
- join_paths('src', 'base_combo.c'),
- join_paths('src', 'base_textfield.c'),
- join_paths('src', 'base_label.c'),
- join_paths('src', 'base_separator.c'),
- join_paths('src', 'base_tooltip.c'),
- join_paths('src', 'base_link.c'),
- join_paths('src', 'base_dial.c'),
- join_paths('src', 'base_spinner.c'),
- join_paths('src', 'base_bar.c'),
- join_paths('src', 'base_wave.c'),
- join_paths('src', 'base_flowmatrix.c'),
- join_paths('src', 'base_pty.c'),
- join_paths('src', 'base_lineedit.c'),
- join_paths('src', 'util_spawn.c'),
- join_paths('linenoise', 'linenoise.c'),
- join_paths('linenoise', 'encodings', 'utf8.c')
-]
+inst_dir = join_paths(lv2libdir, meson.project_name())
-if use_evdev.enabled()
- conf_data.set('D2TK_EVDEV', 1)
- lib_srcs += join_paths('src', 'base_vkb.c')
+if get_option('use-backend-nanovg').enabled()
+ d2tk_dep = d2tk.get_variable('d2tk_nanovg')
+elif get_option('use-backend-cairo').enabled()
+ d2tk_dep = d2tk.get_variable('d2tk_cairo')
else
- conf_data.set('D2TK_EVDEV', 0)
+ error('no valid UI backend given')
endif
-if input_dep.found() and input_dep.version().version_compare('>=1.15.0')
- conf_data.set('D2TK_INPUT_1_15', 1)
-else
- conf_data.set('D2TK_INPUT_1_15', 0)
-endif
-
-if use_fontconfig.enabled()
- conf_data.set('D2TK_FONTCONFIG', 1)
-else
- conf_data.set('D2TK_FONTCONFIG', 0)
-endif
-
-example_srcs = [
- join_paths('example', 'example.c')
-]
-
-example_cairo_srcs = [
- join_paths('example', 'custom_cairo.c')
-]
-
-example_nanovg_srcs = [
- join_paths('example', 'custom_nanovg.c')
-]
-
-example_pugl_srcs = [
- join_paths('example', 'd2tk_pugl.c')
-]
-
-example_fbdev_srcs = [
- join_paths('example', 'd2tk_fbdev.c')
-]
-
-example_glfw_srcs = [
- join_paths('example', 'd2tk_glfw.c')
-]
-
-pugl_srcs = [
- join_paths('src', 'frontend_pugl.c'),
- join_paths('pugl', 'src', 'implementation.c')
-]
-
-pugl_gl_srcs = []
-
-pugl_cairo_srcs = []
-
-nanovg_srcs = [
- join_paths('nanovg', 'src', 'nanovg.c'),
- join_paths('src', 'backend_nanovg.c')
-]
-
-cairo_srcs = [
- join_paths('src', 'backend_cairo.c')
-]
-
-fbdev_srcs = [
- join_paths('src', 'frontend_fbdev.c')
-]
-
-glfw_srcs = [
- join_paths('src', 'frontend_glfw.c')
-]
+source_root = meson.source_root()
+build_root = meson.build_root()
-test_core_srcs = [
- join_paths('test', 'core.c'),
- join_paths('test', 'mock.c')
-]
+add_project_arguments('-D_GNU_SOURCE', language : 'c')
-test_base_srcs = [
- join_paths('test', 'base.c'),
- join_paths('test', 'mock.c')
-]
+conf_data = configuration_data()
+cc = meson.get_compiler('c')
-c_args = ['-fvisibility=hidden',
- '-ffast-math']
+lv2_validate = find_program('lv2_validate', native : true, required : false)
+sord_validate = find_program('sord_validate', native : true, required : false)
+lv2lint = find_program('lv2lint', required : false)
-if host_machine.system() == 'windows'
- deps += cc.find_library('opengl32', required : use_frontend_pugl)
- deps += cc.find_library('gdi32', required : use_frontend_pugl)
- deps += cc.find_library('ws2_32', required : true)
- pugl_srcs += join_paths('pugl', 'src', 'win.c')
- pugl_gl_srcs += join_paths('pugl', 'src', 'win_gl.c')
- pugl_cairo_srcs += join_paths('pugl', 'src', 'win_cairo.c')
-elif host_machine.system() == 'darwin'
- add_languages('objc')
- links += ['-framework', 'OpenGL']
- links += ['-framework', 'Cocoa']
- pugl_srcs += join_paths('pugl','src', 'mac.m')
- pugl_gl_srcs += join_paths('pugl', 'src', 'mac_gl.m')
- pugl_cairo_srcs += join_paths('pugl', 'src', 'mac_cairo.m')
-else
- deps += dependency('gl', required : use_frontend_pugl)
- deps += dependency('x11', version : '>=1.6.0', required : use_frontend_pugl)
- deps += dependency('xext', version : '>=1.3.0', required : use_frontend_pugl)
- pugl_srcs += join_paths('pugl', 'src', 'x11.c')
- pugl_srcs += join_paths('pugl', 'src', 'x11_stub.c')
- pugl_gl_srcs += join_paths('pugl', 'src', 'x11_gl.c')
- pugl_cairo_srcs += join_paths('pugl', 'src', 'x11_cairo.c')
-endif
+lv2_dep = dependency('lv2', version : '>=1.14.0')
-if use_backend_cairo.enabled()
- if use_frontend_pugl.enabled()
- d2tk_cairo = declare_dependency(
- compile_args : ['-DPUGL_HAVE_CAIRO', '-DPUGL_STATIC'],
- include_directories : inc_dir,
- dependencies : [deps, cairo_deps, cairo_xlib_dep],
- link_args : links,
- sources : [lib_srcs, cairo_srcs, pugl_srcs, pugl_cairo_srcs])
+dsp_deps = [lv2_dep]
+ui_deps = [lv2_dep, d2tk_dep]
- if build_examples
- executable('d2tk.cairo', [example_srcs, example_pugl_srcs, example_cairo_srcs],
- c_args : c_args,
- include_directories : inc_dir,
- dependencies: d2tk_cairo,
- install : false)
- endif
- endif
+props_inc = include_directories('props.lv2')
+ser_inc = include_directories('ser_atom.lv2')
+d2tk_inc = include_directories(join_paths('subprojects', 'd2tk'))
+inc_dir = [props_inc, ser_inc, d2tk_inc]
- if use_frontend_fbdev.enabled()
- d2tk_fbdev = declare_dependency(
- include_directories : inc_dir,
- dependencies : [deps, cairo_deps, input_dep, udev_dep, evdev_dep],
- link_args : links,
- sources : [lib_srcs, cairo_srcs, fbdev_srcs])
+dsp_srcs = ['shells_bells.c']
- if build_examples
- executable('d2tk.fbdev', [example_srcs, example_fbdev_srcs, example_cairo_srcs],
- c_args : c_args,
- include_directories : inc_dir,
- dependencies: d2tk_fbdev,
- install : false)
- endif
- endif
-endif
+ui_srcs = ['shells_bells_ui.c']
-if use_backend_nanovg.enabled()
- if use_frontend_pugl.enabled()
- d2tk_nanovg = declare_dependency(
- compile_args : ['-DPUGL_STATIC'],
- include_directories : inc_dir,
- dependencies : [deps, glu_dep, glew_dep],
- link_args : links,
- sources : [lib_srcs, nanovg_srcs, pugl_srcs, pugl_gl_srcs])
+c_args = ['-fvisibility=hidden']
- if build_examples
- executable('d2tk.nanovg', [example_srcs, example_pugl_srcs, example_nanovg_srcs],
- c_args : c_args,
- include_directories : inc_dir,
- dependencies: d2tk_nanovg,
- install : false)
- endif
- endif
-
- if use_frontend_glfw.enabled()
- d2tk_glfw = declare_dependency(
- include_directories : inc_dir,
- dependencies : [deps, glfw_dep, glew_dep],
- link_args : links,
- sources : [lib_srcs, nanovg_srcs, glfw_srcs])
+version = run_command('cat', 'VERSION').stdout().strip().split('.')
+add_project_arguments('-DSHELLS_BELLS_VERSION="'
+ + version[0] + '.' + version[1] + '.' + version[2] + '"', language : 'c')
+conf_data.set('MAJOR_VERSION', version[0])
+conf_data.set('MINOR_VERSION', version[1])
+conf_data.set('MICRO_VERSION', version[2])
- if build_examples
- executable('d2tk.glfw', [example_srcs, example_glfw_srcs, example_nanovg_srcs],
- c_args : c_args,
- include_directories : inc_dir,
- dependencies: d2tk_glfw,
- install : false)
- endif
- endif
-endif
+mod = shared_module('shells_bells', dsp_srcs,
+ c_args : c_args,
+ include_directories : inc_dir,
+ name_prefix : '',
+ dependencies : dsp_deps,
+ install : true,
+ install_dir : inst_dir)
+
+ui = shared_module('shells_bells_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)
+
+manifest_ttl = configure_file(
+ input : 'manifest.ttl.in',
+ output : 'manifest.ttl',
+ configuration : conf_data,
+ install : true,
+ install_dir : inst_dir)
-config_h = configure_file(
- input : join_paths('d2tk', 'config.h.in'),
- output : 'config.h',
+dsp_ttl = configure_file(
+ input : 'shells_bells.ttl.in',
+ output : 'shells_bells.ttl',
configuration : conf_data,
- install : false)
+ install : true,
+ install_dir : inst_dir)
+
+ui_ttl = configure_file(
+ input : 'shells_bells_ui.ttl',
+ output : 'shells_bells_ui.ttl',
+ copy: true,
+ install : true,
+ install_dir : inst_dir)
if not use_fontconfig.enabled()
- fira_sans_bold_ttf = configure_file(
- input : join_paths('ttf', 'FiraSans-Bold.ttf'),
+ fira_sans_bold_ttf = d2tk.get_variable('fira_sans_bold_ttf')
+ fira_code_bold_ttf = d2tk.get_variable('fira_code_bold_ttf')
+ fira_code_light_ttf = d2tk.get_variable('fira_code_light_ttf')
+ fira_code_medium_ttf = d2tk.get_variable('fira_code_medium_ttf')
+ fira_code_regular_ttf = d2tk.get_variable('fira_code_regular_ttf')
+
+ configure_file(
+ input : fira_sans_bold_ttf,
output : 'FiraSans:bold.ttf',
copy : true,
- install : false)
-
- fira_code_bold_ttf = configure_file(
- input : join_paths('ttf', 'FiraCode-Bold.ttf'),
+ install : true,
+ install_dir : inst_dir)
+ configure_file(
+ input : fira_code_bold_ttf,
output : 'FiraCode:bold.ttf',
copy : true,
- install : false)
-
- fira_code_light_ttf = configure_file(
- input : join_paths('ttf', 'FiraCode-Light.ttf'),
+ install : true,
+ install_dir : inst_dir)
+ configure_file(
+ input : fira_code_light_ttf,
output : 'FiraCode:light.ttf',
copy : true,
- install : false)
-
- fira_code_medium_ttf = configure_file(
- input : join_paths('ttf', 'FiraCode-Medium.ttf'),
- output : 'FiraCode:medium.ttf',
- copy : true,
- install : false)
-
- fira_code_regular_ttf = configure_file(
- input : join_paths('ttf', 'FiraCode-Regular.ttf'),
- output : 'FiraCode:regular.ttf',
- copy : true,
- install : false)
-endif
-
-if build_examples
+ install : true,
+ install_dir : inst_dir)
configure_file(
- input : join_paths('example', 'libre-arrow-circle-right.png'),
- output : 'libre-arrow-circle-right.png',
- copy : true,
- install : false)
-
- configure_file(
- input : join_paths('example', 'libre-gui-folder.png'),
- output : 'libre-gui-folder.png',
+ input : fira_code_medium_ttf,
+ output : 'FiraCode:medium.ttf',
copy : true,
- install : false)
-
+ install : true,
+ install_dir : inst_dir)
configure_file(
- input : join_paths('example', 'libre-gui-file.png'),
- output : 'libre-gui-file.png',
+ input : fira_code_regular_ttf,
+ output : 'FiraCode:regular.ttf',
copy : true,
- install : false)
+ install : true,
+ install_dir : inst_dir)
endif
if build_tests
- test_core = executable('test.core', [test_core_srcs, lib_srcs],
- c_args : c_args,
- dependencies : deps,
- include_directories : inc_dir,
- install : false)
-
- test_base = executable('test.base', [test_base_srcs, lib_srcs],
- c_args : c_args,
- dependencies : deps,
- include_directories : inc_dir,
- install : false)
-
- test('Test core', test_core)
- test('Test base', test_base)
-
- if fc_list.found() and grep.found() and check_for_font.found()
- test('FiraSans-Bold.ttf', check_for_font, args : ['FiraSans-Bold.ttf'])
- test('FiraCode-Light.ttf', check_for_font, args : ['FiraCode-Light.tt'])
- test('FiraCode-Regular.ttf', check_for_font, args : ['FiraCode-Regular.ttf'])
- test('FiraCode-Medium.ttf', check_for_font, args : ['FiraCode-Medium.ttf'])
- test('FiraCode-Bold.ttf', check_for_font, args : ['FiraCode-Bold.ttf'])
+ if lv2_validate.found() and sord_validate.found()
+ test('LV2 validate', lv2_validate,
+ args : [manifest_ttl, dsp_ttl, ui_ttl])
endif
-endif
-if build_doc
- run_command('rm', '-rf', 'doc')
-
- run_command(robodoc,
- '--src', './d2tk',
- '--doc', 'doc',
- '--multidoc', '--troff', '--nosort', '--nodesc', '--cmode',
- '--compress', 'gzip')
+ if lv2lint.found()
+ test('LV2 lint', lv2lint,
+ args : ['-M', 'pack',
+ '-E', 'warn',
+ '-I', join_paths(build_root, ''),
+ 'http://open-music-kontrollers.ch/lv2/shells_bells#bells'
+ ])
+ endif
endif
diff --git a/meson_options.txt b/meson_options.txt
index 242cbe0..016e54b 100644
--- a/meson_options.txt
+++ b/meson_options.txt
@@ -1,47 +1,25 @@
option('build-debug-overlay',
type : 'boolean',
- value : false,
- yield : true)
-option('build-examples',
- type : 'boolean',
- value : false,
- yield : true)
+ value : false)
option('build-tests',
type : 'boolean',
- value : true,
- yield : true)
-option('build-doc',
- type : 'boolean',
- value : false,
- yield : true)
+ value : true)
option('use-backend-cairo',
type : 'feature',
- value : 'disabled',
- yield : true)
+ value : 'disabled')
option('use-backend-nanovg',
type : 'feature',
- value : 'disabled',
- yield : true)
+ value : 'enabled')
-option('use-frontend-fbdev',
- type : 'feature',
- value : 'disabled',
- yield : true)
option('use-frontend-pugl',
type : 'feature',
- value : 'disabled',
- yield : true)
-option('use-frontend-glfw',
- type : 'feature',
- value : 'disabled',
- yield : true)
+ value : 'enabled')
-option('use-evdev',
- type : 'feature',
- value : 'disabled',
- yield : true)
option('use-fontconfig',
type : 'feature',
- value : 'disabled',
- yield : true)
+ value : 'enabled')
+
+option('lv2libdir',
+ type : 'string',
+ value : 'lib/lv2')
diff --git a/props.lv2/.gitlab-ci.yml b/props.lv2/.gitlab-ci.yml
new file mode 100644
index 0000000..979769c
--- /dev/null
+++ b/props.lv2/.gitlab-ci.yml
@@ -0,0 +1,2 @@
+include:
+ - local: 'gitlab-ci/generic.yml'
diff --git a/props.lv2/COPYING b/props.lv2/COPYING
new file mode 100644
index 0000000..ddb9a46
--- /dev/null
+++ b/props.lv2/COPYING
@@ -0,0 +1,201 @@
+ 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/props.lv2/README.md b/props.lv2/README.md
new file mode 100644
index 0000000..08466d2
--- /dev/null
+++ b/props.lv2/README.md
@@ -0,0 +1,20 @@
+# Props.lv2
+
+## Utility header for property based LV2 plugins
+
+### License
+
+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>.
diff --git a/props.lv2/VERSION b/props.lv2/VERSION
new file mode 100644
index 0000000..5130959
--- /dev/null
+++ b/props.lv2/VERSION
@@ -0,0 +1 @@
+0.1.165
diff --git a/props.lv2/gitlab-ci/generic.yml b/props.lv2/gitlab-ci/generic.yml
new file mode 100644
index 0000000..5cd2abc
--- /dev/null
+++ b/props.lv2/gitlab-ci/generic.yml
@@ -0,0 +1,106 @@
+stages:
+ - build
+ - deploy
+
+variables:
+ PKG_CONFIG_PATH: "/opt/lv2/lib/pkgconfig:/opt/${CI_BUILD_NAME}/lib/pkgconfig:/usr/lib/${CI_BUILD_NAME}/pkgconfig"
+ BUILD_OPTS : ""
+
+.native_template: &native_definition
+ stage: build
+ script:
+ - meson --prefix="${CI_PROJECT_DIR}/${CI_PROJECT_NAME}-$(cat VERSION)/${CI_BUILD_NAME}" -Dlv2libdir="" --cross-file "${CI_BUILD_NAME}" ${BUILD_OPTS} build
+ - ninja -C build
+ - ninja -C build test
+ - ninja -C build install
+
+ - scan-build --status-bugs meson --prefix="${CI_PROJECT_DIR}/${CI_PROJECT_NAME}-$(cat VERSION)/${CI_BUILD_NAME}" -Dlv2libdir="" --cross-file "${CI_BUILD_NAME}" ${BUILD_OPTS} scanbuild
+ - scan-build --status-bugs ninja -C scanbuild
+ - scan-build --status-bugs ninja -C scanbuild test
+ artifacts:
+ name: "${CI_PROJECT_NAME}-$(cat VERSION)-${CI_BUILD_NAME}"
+ paths:
+ - "${CI_PROJECT_NAME}-$(cat VERSION)/${CI_BUILD_NAME}/"
+
+.cross_template: &cross_definition
+ stage: build
+ script:
+ - meson --prefix="${CI_PROJECT_DIR}/${CI_PROJECT_NAME}-$(cat VERSION)/${CI_BUILD_NAME}" -Dlv2libdir="" --cross-file "${CI_BUILD_NAME}" ${BUILD_OPTS} build
+ - ninja -C build
+ - ninja -C build test
+ - ninja -C build install
+ artifacts:
+ name: "${CI_PROJECT_NAME}-$(cat VERSION)-${CI_BUILD_NAME}"
+ paths:
+ - "${CI_PROJECT_NAME}-$(cat VERSION)/${CI_BUILD_NAME}/"
+
+# build
+.universal_linux_template_stretch: &universal_linux_definition_stretch
+ image: ventosus/universal-linux-gnu:stretch
+ <<: *cross_definition
+
+.universal_linux_template_buster: &universal_linux_definition_buster
+ image: ventosus/universal-linux-gnu:buster
+ <<: *native_definition
+
+.universal_linux_template_bullseye: &universal_linux_definition_bullseye
+ image: ventosus/universal-linux-gnu:bullseye
+ <<: *native_definition
+
+.arm_linux_template_stretch: &arm_linux_definition_stretch
+ image: ventosus/arm-linux-gnueabihf:stretch
+ <<: *cross_definition
+
+.arm_linux_template_buster: &arm_linux_definition_buster
+ image: ventosus/arm-linux-gnueabihf:buster
+ <<: *cross_definition
+
+.arm_linux_template_bullseye: &arm_linux_definition_bullseye
+ image: ventosus/arm-linux-gnueabihf:bullseye
+ <<: *cross_definition
+
+# build
+x86_64-linux-gnu-stretch:
+ <<: *universal_linux_definition_stretch
+
+x86_64-linux-gnu-buster:
+ <<: *universal_linux_definition_buster
+
+x86_64-linux-gnu-bullseye:
+ <<: *universal_linux_definition_bullseye
+
+i686-linux-gnu-stretch:
+ <<: *universal_linux_definition_stretch
+
+i686-linux-gnu-buster:
+ <<: *universal_linux_definition_buster
+
+i686-linux-gnu-bullseye:
+ <<: *universal_linux_definition_bullseye
+
+arm-linux-gnueabihf-stretch:
+ <<: *arm_linux_definition_stretch
+
+arm-linux-gnueabihf-buster:
+ <<: *arm_linux_definition_buster
+
+arm-linux-gnueabihf-bullseye:
+ <<: *arm_linux_definition_bullseye
+
+aarch64-linux-gnu-stretch:
+ <<: *arm_linux_definition_stretch
+
+aarch64-linux-gnu-buster:
+ <<: *arm_linux_definition_buster
+
+aarch64-linux-gnu-bullseye:
+ <<: *arm_linux_definition_bullseye
+
+pack:
+ stage: deploy
+ script:
+ - echo 'packing up'
+ artifacts:
+ name: "${CI_PROJECT_NAME}-$(cat VERSION)"
+ paths:
+ - "${CI_PROJECT_NAME}-$(cat VERSION)/"
diff --git a/props.lv2/meson.build b/props.lv2/meson.build
new file mode 100644
index 0000000..d354d89
--- /dev/null
+++ b/props.lv2/meson.build
@@ -0,0 +1,82 @@
+project('props.lv2', 'c', default_options : [
+ 'buildtype=release',
+ 'warning_level=3',
+ 'werror=false',
+ 'b_lto=false',
+ 'c_std=c11'])
+
+add_project_arguments('-D_GNU_SOURCE', language : 'c')
+
+conf_data = configuration_data()
+cc = meson.get_compiler('c')
+
+cp = find_program('cp')
+lv2_validate = find_program('lv2_validate', native : true, required : false)
+sord_validate = find_program('sord_validate', native : true, required : false)
+lv2lint = find_program('lv2lint', required : false)
+clone = [cp, '@INPUT@', '@OUTPUT@']
+
+m_dep = cc.find_library('m')
+lv2_dep = dependency('lv2', version : '>=1.14.0')
+
+inc_dir = []
+
+inst_dir = join_paths(get_option('libdir'), 'lv2', meson.project_name())
+
+dsp_srcs = [join_paths('test', 'props.c')]
+
+c_args = ['-fvisibility=hidden',
+ '-ffast-math']
+
+mod = shared_module('props', dsp_srcs,
+ c_args : c_args,
+ include_directories : inc_dir,
+ name_prefix : '',
+ dependencies : [m_dep, lv2_dep],
+ install : true,
+ install_dir : inst_dir)
+
+version = run_command('cat', 'VERSION').stdout().strip().split('.')
+conf_data.set('MAJOR_VERSION', version[0])
+conf_data.set('MINOR_VERSION', version[1])
+conf_data.set('MICRO_VERSION', version[2])
+
+suffix = mod.full_path().strip().split('.')[-1]
+conf_data.set('MODULE_SUFFIX', '.' + suffix)
+
+manifest_ttl = configure_file(
+ input : join_paths('test', 'manifest.ttl.in'), output : 'manifest.ttl',
+ configuration : conf_data,
+ install : true,
+ install_dir : inst_dir)
+dsp_ttl = custom_target('props_ttl',
+ input : join_paths('test', 'props.ttl'),
+ output : 'props.ttl',
+ command : clone,
+ install : true,
+ install_dir : inst_dir)
+custom_target('chunk_bin',
+ input : join_paths('test', 'chunk.bin'),
+ output : 'chunk.bin',
+ command : clone,
+ install : true,
+ install_dir : inst_dir)
+
+props_test = executable('props_test',
+ join_paths('test', 'props_test.c'),
+ c_args : c_args,
+ install : false)
+
+test('Test', props_test,
+ timeout : 240)
+
+if lv2_validate.found() and sord_validate.found()
+ test('LV2 validate', lv2_validate,
+ args : [manifest_ttl, dsp_ttl])
+endif
+
+if lv2lint.found()
+ test('LV2 lint', lv2lint,
+ args : ['-Ewarn',
+ 'http://open-music-kontrollers.ch/lv2/props#test'])
+endif
diff --git a/props.lv2/props.h b/props.lv2/props.h
new file mode 100644
index 0000000..b3905ab
--- /dev/null
+++ b/props.lv2/props.h
@@ -0,0 +1,1226 @@
+/*
+ * 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.
+ */
+
+#ifndef _LV2_PROPS_H_
+#define _LV2_PROPS_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdlib.h>
+#include <stdatomic.h>
+#include <stdio.h>
+
+#include <lv2/lv2plug.in/ns/lv2core/lv2.h>
+#include <lv2/lv2plug.in/ns/ext/urid/urid.h>
+#include <lv2/lv2plug.in/ns/ext/atom/atom.h>
+#include <lv2/lv2plug.in/ns/ext/atom/forge.h>
+#include <lv2/lv2plug.in/ns/ext/patch/patch.h>
+#include <lv2/lv2plug.in/ns/ext/state/state.h>
+
+/*****************************************************************************
+ * API START
+ *****************************************************************************/
+
+// structures
+typedef struct _props_def_t props_def_t;
+typedef struct _props_impl_t props_impl_t;
+typedef struct _props_dyn_t props_dyn_t;
+typedef struct _props_t props_t;
+
+typedef enum _props_dyn_ev_t {
+ PROPS_DYN_EV_ADD,
+ PROPS_DYN_EV_REM,
+ PROPS_DYN_EV_SET
+} props_dyn_ev_t;
+
+// function callbacks
+typedef void (*props_event_cb_t)(
+ void *data,
+ int64_t frames,
+ props_impl_t *impl);
+
+typedef void (*props_dyn_prop_cb_t)(
+ void *data,
+ props_dyn_ev_t ev,
+ LV2_URID subj,
+ LV2_URID prop,
+ const LV2_Atom *body);
+
+struct _props_def_t {
+ const char *property;
+ const char *type;
+ const char *access;
+ size_t offset;
+ bool hidden;
+
+ uint32_t max_size;
+ props_event_cb_t event_cb;
+};
+
+struct _props_impl_t {
+ LV2_URID property;
+ LV2_URID type;
+ LV2_URID access;
+
+ struct {
+ uint32_t size;
+ void *body;
+ } value;
+ struct {
+ uint32_t size;
+ void *body;
+ } stash;
+
+ const props_def_t *def;
+
+ atomic_int state;
+ bool stashing;
+};
+
+struct _props_dyn_t {
+ props_dyn_prop_cb_t prop;
+};
+
+struct _props_t {
+ struct {
+ LV2_URID subject;
+
+ LV2_URID patch_get;
+ LV2_URID patch_set;
+ LV2_URID patch_put;
+ LV2_URID patch_patch;
+ LV2_URID patch_wildcard;
+ LV2_URID patch_add;
+ LV2_URID patch_remove;
+ LV2_URID patch_subject;
+ LV2_URID patch_body;
+ LV2_URID patch_property;
+ LV2_URID patch_value;
+ LV2_URID patch_writable;
+ LV2_URID patch_readable;
+ LV2_URID patch_sequence;
+ LV2_URID patch_error;
+ LV2_URID patch_ack;
+
+ LV2_URID atom_int;
+ LV2_URID atom_long;
+ LV2_URID atom_float;
+ LV2_URID atom_double;
+ LV2_URID atom_bool;
+ LV2_URID atom_urid;
+ LV2_URID atom_path;
+ LV2_URID atom_literal;
+ LV2_URID atom_vector;
+ LV2_URID atom_object;
+ LV2_URID atom_sequence;
+
+ LV2_URID state_StateChanged;
+ } urid;
+
+ void *data;
+
+ bool stashing;
+ atomic_bool restoring;
+
+ uint32_t max_size;
+
+ const props_dyn_t *dyn;
+
+ unsigned nimpls;
+ props_impl_t impls [1];
+};
+
+#define PROPS_T(PROPS, MAX_NIMPLS) \
+ props_t (PROPS); \
+ props_impl_t _impls [MAX_NIMPLS]
+
+// rt-safe
+static inline int
+props_init(props_t *props, const char *subject,
+ const props_def_t *defs, int nimpls,
+ void *value_base, void *stash_base,
+ LV2_URID_Map *map, void *data);
+
+// rt-safe
+static inline void
+props_dyn(props_t *props, const props_dyn_t *dyn);
+
+// rt-safe
+static inline void
+props_idle(props_t *props, LV2_Atom_Forge *forge, uint32_t frames,
+ LV2_Atom_Forge_Ref *ref);
+
+// rt-safe
+static inline int
+props_advance(props_t *props, LV2_Atom_Forge *forge, uint32_t frames,
+ const LV2_Atom_Object *obj, LV2_Atom_Forge_Ref *ref);
+
+// rt-safe
+static inline void
+props_set(props_t *props, LV2_Atom_Forge *forge, uint32_t frames,
+ LV2_URID property, LV2_Atom_Forge_Ref *ref);
+
+// rt-safe
+static inline void
+props_get(props_t *props, LV2_Atom_Forge *forge, uint32_t frames,
+ LV2_URID property, LV2_Atom_Forge_Ref *ref);
+
+// rt-safe
+static inline void
+props_stash(props_t *props, LV2_URID property);
+
+// rt-safe
+static inline LV2_URID
+props_map(props_t *props, const char *property);
+
+// rt-safe
+static inline const char *
+props_unmap(props_t *props, LV2_URID property);
+
+// non-rt
+static inline LV2_State_Status
+props_save(props_t *props, LV2_State_Store_Function store,
+ LV2_State_Handle state, uint32_t flags, const LV2_Feature *const *features);
+
+// non-rt
+static inline LV2_State_Status
+props_restore(props_t *props, LV2_State_Retrieve_Function retrieve,
+ LV2_State_Handle state, uint32_t flags, const LV2_Feature *const *features);
+
+/*****************************************************************************
+ * API END
+ *****************************************************************************/
+
+// enumerations
+typedef enum _props_state_t {
+ PROP_STATE_NONE = 0,
+ PROP_STATE_LOCK = 1,
+ PROP_STATE_RESTORE = 2
+} props_state_t;
+
+static inline void
+_props_impl_spin_lock(props_impl_t *impl, int from, int to)
+{
+ int expected = from;
+ const int desired = to;
+
+ while(!atomic_compare_exchange_strong_explicit(&impl->state, &expected, desired,
+ memory_order_acquire, memory_order_acquire))
+ {
+ // spin
+ }
+}
+
+static inline bool
+_props_impl_try_lock(props_impl_t *impl, int from, int to)
+{
+ int expected = from;
+ const int desired = to;
+
+ return atomic_compare_exchange_strong_explicit(&impl->state, &expected, desired,
+ memory_order_acquire, memory_order_acquire);
+}
+
+static inline void
+_props_impl_unlock(props_impl_t *impl, int to)
+{
+ atomic_store_explicit(&impl->state, to, memory_order_release);
+}
+
+static inline bool
+_props_restoring_get(props_t *props)
+{
+ return atomic_exchange_explicit(&props->restoring, false, memory_order_acquire);
+}
+
+static inline void
+_props_restoring_set(props_t *props)
+{
+ atomic_store_explicit(&props->restoring, true, memory_order_release);
+}
+
+static inline void
+_props_qsort(props_impl_t *A, int n)
+{
+ if(n < 2)
+ return;
+
+ const props_impl_t *p = A;
+
+ int i = -1;
+ int j = n;
+
+ while(true)
+ {
+ do {
+ i += 1;
+ } while(A[i].property < p->property);
+
+ do {
+ j -= 1;
+ } while(A[j].property > p->property);
+
+ if(i >= j)
+ break;
+
+ const props_impl_t tmp = A[i];
+ A[i] = A[j];
+ A[j] = tmp;
+ }
+
+ _props_qsort(A, j + 1);
+ _props_qsort(A + j + 1, n - j - 1);
+}
+
+static inline props_impl_t *
+_props_impl_get(props_t *props, LV2_URID property)
+{
+ props_impl_t *base = props->impls;
+
+ for(int N = props->nimpls, half; N > 1; N -= half)
+ {
+ half = N/2;
+ props_impl_t *dst = &base[half];
+ base = (dst->property > property) ? base : dst;
+ }
+
+ return (base->property == property) ? base : NULL;
+}
+
+static inline LV2_Atom_Forge_Ref
+_props_patch_set(props_t *props, LV2_Atom_Forge *forge, uint32_t frames,
+ props_impl_t *impl, int32_t sequence_num)
+{
+ LV2_Atom_Forge_Frame obj_frame;
+
+ LV2_Atom_Forge_Ref ref = lv2_atom_forge_frame_time(forge, frames);
+
+ if(ref)
+ ref = lv2_atom_forge_object(forge, &obj_frame, 0, props->urid.patch_set);
+ {
+ if(props->urid.subject) // is optional
+ {
+ if(ref)
+ ref = lv2_atom_forge_key(forge, props->urid.patch_subject);
+ if(ref)
+ ref = lv2_atom_forge_urid(forge, props->urid.subject);
+ }
+
+ if(sequence_num) // is optional
+ {
+ if(ref)
+ ref = lv2_atom_forge_key(forge, props->urid.patch_sequence);
+ if(ref)
+ ref = lv2_atom_forge_int(forge, sequence_num);
+ }
+
+ if(ref)
+ ref = lv2_atom_forge_key(forge, props->urid.patch_property);
+ if(ref)
+ ref = lv2_atom_forge_urid(forge, impl->property);
+
+ if(ref)
+ lv2_atom_forge_key(forge, props->urid.patch_value);
+ if(ref)
+ ref = lv2_atom_forge_atom(forge, impl->value.size, impl->type);
+ if(ref)
+ ref = lv2_atom_forge_write(forge, impl->value.body, impl->value.size);
+ }
+ if(ref)
+ lv2_atom_forge_pop(forge, &obj_frame);
+
+ if(ref)
+ ref = lv2_atom_forge_frame_time(forge, frames);
+ if(ref)
+ ref = lv2_atom_forge_object(forge, &obj_frame, 0, props->urid.state_StateChanged);
+ if(ref)
+ lv2_atom_forge_pop(forge, &obj_frame);
+
+ return ref;
+}
+
+static inline LV2_Atom_Forge_Ref
+_props_patch_get(props_t *props, LV2_Atom_Forge *forge, uint32_t frames,
+ props_impl_t *impl, int32_t sequence_num)
+{
+ LV2_Atom_Forge_Frame obj_frame;
+
+ LV2_Atom_Forge_Ref ref = lv2_atom_forge_frame_time(forge, frames);
+
+ if(ref)
+ ref = lv2_atom_forge_object(forge, &obj_frame, 0, props->urid.patch_get);
+ {
+ if(props->urid.subject) // is optional
+ {
+ if(ref)
+ ref = lv2_atom_forge_key(forge, props->urid.patch_subject);
+ if(ref)
+ ref = lv2_atom_forge_urid(forge, props->urid.subject);
+ }
+
+ if(sequence_num) // is optional
+ {
+ if(ref)
+ ref = lv2_atom_forge_key(forge, props->urid.patch_sequence);
+ if(ref)
+ ref = lv2_atom_forge_int(forge, sequence_num);
+ }
+
+ if(ref)
+ ref = lv2_atom_forge_key(forge, props->urid.patch_property);
+ if(ref)
+ ref = lv2_atom_forge_urid(forge, impl->property);
+ }
+ if(ref)
+ lv2_atom_forge_pop(forge, &obj_frame);
+
+ return ref;
+}
+
+static inline LV2_Atom_Forge_Ref
+_props_patch_error(props_t *props, LV2_Atom_Forge *forge, uint32_t frames,
+ int32_t sequence_num)
+{
+ LV2_Atom_Forge_Frame obj_frame;
+
+ LV2_Atom_Forge_Ref ref = lv2_atom_forge_frame_time(forge, frames);
+
+ if(ref)
+ ref = lv2_atom_forge_object(forge, &obj_frame, 0, props->urid.patch_error);
+ {
+ if(ref)
+ ref = lv2_atom_forge_key(forge, props->urid.patch_sequence);
+ if(ref)
+ ref = lv2_atom_forge_int(forge, sequence_num);
+ }
+ if(ref)
+ lv2_atom_forge_pop(forge, &obj_frame);
+
+ return ref;
+}
+
+static inline LV2_Atom_Forge_Ref
+_props_patch_ack(props_t *props, LV2_Atom_Forge *forge, uint32_t frames,
+ int32_t sequence_num)
+{
+ LV2_Atom_Forge_Frame obj_frame;
+
+ LV2_Atom_Forge_Ref ref = lv2_atom_forge_frame_time(forge, frames);
+
+ if(ref)
+ ref = lv2_atom_forge_object(forge, &obj_frame, 0, props->urid.patch_ack);
+ {
+ if(ref)
+ ref = lv2_atom_forge_key(forge, props->urid.patch_sequence);
+ if(ref)
+ ref = lv2_atom_forge_int(forge, sequence_num);
+ }
+ if(ref)
+ lv2_atom_forge_pop(forge, &obj_frame);
+
+ return ref;
+}
+
+static inline void
+_props_impl_stash(props_t *props, props_impl_t *impl)
+{
+ if(_props_impl_try_lock(impl, PROP_STATE_NONE, PROP_STATE_LOCK))
+ {
+ impl->stashing = false;
+ impl->stash.size = impl->value.size;
+ memcpy(impl->stash.body, impl->value.body, impl->value.size);
+
+ _props_impl_unlock(impl, PROP_STATE_NONE);
+ }
+ else
+ {
+ impl->stashing = true; // try again later
+ props->stashing = true;
+ }
+}
+
+static inline void
+_props_impl_restore(props_t *props, LV2_Atom_Forge *forge, uint32_t frames,
+ props_impl_t *impl, LV2_Atom_Forge_Ref *ref)
+{
+ if(_props_impl_try_lock(impl, PROP_STATE_RESTORE, PROP_STATE_LOCK))
+ {
+ impl->stashing = false; // makes no sense to stash a recently restored value
+ impl->value.size = impl->stash.size;
+ memcpy(impl->value.body, impl->stash.body, impl->stash.size);
+
+ _props_impl_unlock(impl, PROP_STATE_NONE);
+
+ if(*ref && !impl->def->hidden)
+ *ref = _props_patch_set(props, forge, frames, impl, 0);
+
+ const props_def_t *def = impl->def;
+ if(def->event_cb)
+ def->event_cb(props->data, 0, impl);
+ }
+}
+
+static inline void
+_props_impl_set(props_t *props, props_impl_t *impl, LV2_URID type,
+ uint32_t size, const void *body)
+{
+ if( (impl->type == type)
+ && ( (impl->def->max_size == 0) || (size <= impl->def->max_size)) )
+ {
+ impl->value.size = size;
+ memcpy(impl->value.body, body, size);
+
+ _props_impl_stash(props, impl);
+ }
+}
+
+static inline int
+_props_impl_init(props_t *props, props_impl_t *impl, const props_def_t *def,
+ void *value_base, void *stash_base, LV2_URID_Map *map)
+{
+ if(!def->property || !def->type)
+ return 0;
+
+ const LV2_URID type = map->map(map->handle, def->type);
+ const LV2_URID property = map->map(map->handle, def->property);
+ const LV2_URID access = def->access
+ ? map->map(map->handle, def->access)
+ : map->map(map->handle, LV2_PATCH__writable);
+
+ if(!type || !property || !access)
+ return 0;
+
+ impl->property = property;
+ impl->access = access;
+ impl->def = def;
+ impl->value.body = (uint8_t *)value_base + def->offset;
+ impl->stash.body = (uint8_t *)stash_base + def->offset;
+
+ uint32_t size;
+ if( (type == props->urid.atom_int)
+ || (type == props->urid.atom_float)
+ || (type == props->urid.atom_bool)
+ || (type == props->urid.atom_urid) )
+ {
+ size = 4;
+ }
+ else if((type == props->urid.atom_long)
+ || (type == props->urid.atom_double) )
+ {
+ size = 8;
+ }
+ else if(type == props->urid.atom_literal)
+ {
+ size = sizeof(LV2_Atom_Literal_Body);
+ }
+ else if(type == props->urid.atom_vector)
+ {
+ size = sizeof(LV2_Atom_Vector_Body);
+ }
+ else if(type == props->urid.atom_object)
+ {
+ size = sizeof(LV2_Atom_Object_Body);
+ }
+ else if(type == props->urid.atom_sequence)
+ {
+ size = sizeof(LV2_Atom_Sequence_Body);
+ }
+ else
+ {
+ size = 0; // assume everything else as having size 0
+ }
+
+ impl->type = type;
+ impl->value.size = size;
+ impl->stash.size = size;
+
+ atomic_init(&impl->state, PROP_STATE_NONE);
+
+ // update maximal value size
+ const uint32_t max_size = def->max_size
+ ? def->max_size
+ : size;
+
+ if(max_size > props->max_size)
+ {
+ props->max_size = max_size;
+ }
+
+ return 1;
+}
+
+static inline int
+props_init(props_t *props, const char *subject,
+ const props_def_t *defs, int nimpls,
+ void *value_base, void *stash_base,
+ LV2_URID_Map *map, void *data)
+{
+ if(!props || !defs || !value_base || !stash_base || !map)
+ return 0;
+
+ props->nimpls = nimpls;
+ props->data = data;
+
+ props->urid.subject = subject ? map->map(map->handle, subject) : 0;
+
+ props->urid.patch_get = map->map(map->handle, LV2_PATCH__Get);
+ props->urid.patch_set = map->map(map->handle, LV2_PATCH__Set);
+ props->urid.patch_put = map->map(map->handle, LV2_PATCH__Put);
+ props->urid.patch_patch = map->map(map->handle, LV2_PATCH__Patch);
+ props->urid.patch_wildcard = map->map(map->handle, LV2_PATCH__wildcard);
+ props->urid.patch_add = map->map(map->handle, LV2_PATCH__add);
+ props->urid.patch_remove = map->map(map->handle, LV2_PATCH__remove);
+ props->urid.patch_subject = map->map(map->handle, LV2_PATCH__subject);
+ props->urid.patch_body = map->map(map->handle, LV2_PATCH__body);
+ props->urid.patch_property = map->map(map->handle, LV2_PATCH__property);
+ props->urid.patch_value = map->map(map->handle, LV2_PATCH__value);
+ props->urid.patch_writable = map->map(map->handle, LV2_PATCH__writable);
+ props->urid.patch_readable = map->map(map->handle, LV2_PATCH__readable);
+ props->urid.patch_sequence = map->map(map->handle, LV2_PATCH__sequenceNumber);
+ props->urid.patch_ack = map->map(map->handle, LV2_PATCH__Ack);
+ props->urid.patch_error = map->map(map->handle, LV2_PATCH__Error);
+
+ props->urid.atom_int = map->map(map->handle, LV2_ATOM__Int);
+ props->urid.atom_long = map->map(map->handle, LV2_ATOM__Long);
+ props->urid.atom_float = map->map(map->handle, LV2_ATOM__Float);
+ props->urid.atom_double = map->map(map->handle, LV2_ATOM__Double);
+ props->urid.atom_bool = map->map(map->handle, LV2_ATOM__Bool);
+ props->urid.atom_urid = map->map(map->handle, LV2_ATOM__URID);
+ props->urid.atom_path = map->map(map->handle, LV2_ATOM__Path);
+ props->urid.atom_literal = map->map(map->handle, LV2_ATOM__Literal);
+ props->urid.atom_vector = map->map(map->handle, LV2_ATOM__Vector);
+ props->urid.atom_object = map->map(map->handle, LV2_ATOM__Object);
+ props->urid.atom_sequence = map->map(map->handle, LV2_ATOM__Sequence);
+
+ props->urid.state_StateChanged = map->map(map->handle, LV2_STATE__StateChanged);
+
+ atomic_init(&props->restoring, false);
+
+ int status = 1;
+ for(unsigned i = 0; i < props->nimpls; i++)
+ {
+ props_impl_t *impl = &props->impls[i];
+
+ status = status
+ && _props_impl_init(props, impl, &defs[i], value_base, stash_base, map);
+ }
+
+ _props_qsort(props->impls, props->nimpls);
+
+ return status;
+}
+
+static inline void
+props_dyn(props_t *props, const props_dyn_t *dyn)
+{
+ props->dyn = dyn;
+}
+
+static inline void
+props_idle(props_t *props, LV2_Atom_Forge *forge, uint32_t frames,
+ LV2_Atom_Forge_Ref *ref)
+{
+ if(_props_restoring_get(props))
+ {
+ for(unsigned i = 0; i < props->nimpls; i++)
+ {
+ props_impl_t *impl = &props->impls[i];
+
+ _props_impl_restore(props, forge, frames, impl, ref);
+ }
+ }
+
+ if(props->stashing)
+ {
+ props->stashing = false;
+
+ for(unsigned i = 0; i < props->nimpls; i++)
+ {
+ props_impl_t *impl = &props->impls[i];
+
+ if(impl->stashing)
+ _props_impl_stash(props, impl);
+ }
+ }
+}
+
+static inline int
+props_advance(props_t *props, LV2_Atom_Forge *forge, uint32_t frames,
+ const LV2_Atom_Object *obj, LV2_Atom_Forge_Ref *ref)
+{
+ if(!lv2_atom_forge_is_object_type(forge, obj->atom.type))
+ {
+ return 0;
+ }
+
+ if(obj->body.otype == props->urid.patch_get)
+ {
+ const LV2_Atom_URID *subject = NULL;
+ const LV2_Atom_URID *property = NULL;
+ const LV2_Atom_Int *sequence = NULL;
+
+ lv2_atom_object_get(obj,
+ props->urid.patch_subject, &subject,
+ props->urid.patch_property, &property,
+ props->urid.patch_sequence, &sequence,
+ 0);
+
+ // check for a matching optional subject
+ if( (subject && props->urid.subject)
+ && ( (subject->atom.type != props->urid.atom_urid)
+ || (subject->body != props->urid.subject) ) )
+ {
+ return 0;
+ }
+
+ int32_t sequence_num = 0;
+ if(sequence && (sequence->atom.type == props->urid.atom_int))
+ {
+ sequence_num = sequence->body;
+ }
+
+ if(!property)
+ {
+ for(unsigned i = 0; i < props->nimpls; i++)
+ {
+ props_impl_t *impl = &props->impls[i];
+
+ if(*ref && !impl->def->hidden)
+ *ref = _props_patch_set(props, forge, frames, impl, sequence_num);
+ }
+
+ return 1;
+ }
+ else if(property->atom.type == props->urid.atom_urid)
+ {
+ props_impl_t *impl = _props_impl_get(props, property->body);
+
+ if(impl)
+ {
+ if(*ref && !impl->def->hidden)
+ *ref = _props_patch_set(props, forge, frames, impl, sequence_num);
+
+ return 1;
+ }
+ else if(sequence_num)
+ {
+ if(*ref)
+ *ref = _props_patch_error(props, forge, frames, sequence_num);
+ }
+ }
+ else if(sequence_num)
+ {
+ if(*ref)
+ *ref = _props_patch_error(props, forge, frames, sequence_num);
+ }
+ }
+ else if(obj->body.otype == props->urid.patch_set)
+ {
+ const LV2_Atom_URID *subject = NULL;
+ const LV2_Atom_URID *property = NULL;
+ const LV2_Atom_Int *sequence = NULL;
+ const LV2_Atom *value = NULL;
+
+ lv2_atom_object_get(obj,
+ props->urid.patch_subject, &subject,
+ props->urid.patch_property, &property,
+ props->urid.patch_sequence, &sequence,
+ props->urid.patch_value, &value,
+ 0);
+
+ // check for a matching optional subject
+ if( (subject && props->urid.subject)
+ && ( (subject->atom.type != props->urid.atom_urid)
+ || (subject->body != props->urid.subject) ) )
+ {
+ return 0;
+ }
+
+ int32_t sequence_num = 0;
+ if(sequence && (sequence->atom.type == props->urid.atom_int))
+ {
+ sequence_num = sequence->body;
+ }
+
+ if(!property || (property->atom.type != props->urid.atom_urid) || !value)
+ {
+ if(sequence_num)
+ {
+ if(ref)
+ *ref = _props_patch_error(props, forge, frames, sequence_num);
+ }
+
+ return 0;
+ }
+
+ props_impl_t *impl = _props_impl_get(props, property->body);
+ if(impl)
+ {
+ _props_impl_set(props, impl, value->type, value->size,
+ LV2_ATOM_BODY_CONST(value));
+
+ // send on (e.g. to UI)
+ if(*ref && !impl->def->hidden)
+ *ref = _props_patch_set(props, forge, frames, impl, sequence_num);
+
+ const props_def_t *def = impl->def;
+ if(def->event_cb)
+ def->event_cb(props->data, frames, impl);
+
+ if(sequence_num)
+ {
+ if(*ref)
+ *ref = _props_patch_ack(props, forge, frames, sequence_num);
+ }
+
+ return 1;
+ }
+ else if(props->dyn && props->dyn->prop)
+ {
+ const LV2_URID subj = subject ? subject->body : 0;
+
+ props->dyn->prop(props->data, PROPS_DYN_EV_SET, subj, property->body, value);
+
+ //TODO send ack
+ }
+ else if(sequence_num)
+ {
+ if(*ref)
+ *ref = _props_patch_error(props, forge, frames, sequence_num);
+ }
+ }
+ else if(obj->body.otype == props->urid.patch_put)
+ {
+ const LV2_Atom_URID *subject = NULL;
+ const LV2_Atom_Int *sequence = NULL;
+ const LV2_Atom_Object *body = NULL;
+
+ lv2_atom_object_get(obj,
+ props->urid.patch_subject, &subject,
+ props->urid.patch_sequence, &sequence,
+ props->urid.patch_body, &body,
+ 0);
+
+ // check for a matching optional subject
+ if( (subject && props->urid.subject)
+ && ( (subject->atom.type != props->urid.atom_urid)
+ || (subject->body != props->urid.subject) ) )
+ {
+ return 0;
+ }
+
+ int32_t sequence_num = 0;
+ if(sequence && (sequence->atom.type == props->urid.atom_int))
+ {
+ sequence_num = sequence->body;
+ }
+
+ if(!body || !lv2_atom_forge_is_object_type(forge, body->atom.type))
+ {
+ if(sequence_num)
+ {
+ if(*ref)
+ *ref = _props_patch_error(props, forge, frames, sequence_num);
+ }
+
+ return 0;
+ }
+
+ LV2_ATOM_OBJECT_FOREACH(body, prop)
+ {
+ const LV2_URID property = prop->key;
+ const LV2_Atom *value = &prop->value;
+
+ props_impl_t *impl = _props_impl_get(props, property);
+ if(impl)
+ {
+ _props_impl_set(props, impl, value->type, value->size,
+ LV2_ATOM_BODY_CONST(value));
+
+ // send on (e.g. to UI)
+ if(*ref && !impl->def->hidden)
+ *ref = _props_patch_set(props, forge, frames, impl, sequence_num);
+
+ const props_def_t *def = impl->def;
+ if(def->event_cb)
+ def->event_cb(props->data, frames, impl);
+ }
+ else if(props->dyn && props->dyn->prop)
+ {
+ const LV2_URID subj = subject ? subject->body : 0;
+
+ props->dyn->prop(props->data, PROPS_DYN_EV_SET, subj, property, value);
+
+ //TODO send ack
+ }
+ }
+
+ if(sequence_num)
+ {
+ if(*ref)
+ *ref = _props_patch_ack(props, forge, frames, sequence_num);
+ }
+
+ return 1;
+ }
+ else if(obj->body.otype == props->urid.patch_patch)
+ {
+ const LV2_Atom_URID *subject = NULL;
+ const LV2_Atom_Int *sequence = NULL;
+ const LV2_Atom_Object *add = NULL;
+ const LV2_Atom_Object *rem = NULL;
+
+ lv2_atom_object_get(obj,
+ props->urid.patch_subject, &subject,
+ props->urid.patch_sequence, &sequence,
+ props->urid.patch_add, &add,
+ props->urid.patch_remove, &rem,
+ 0);
+
+ LV2_URID subj = 0;
+ if(subject && (subject->atom.type == props->urid.atom_urid))
+ {
+ subj = subject->body;
+ }
+
+ int32_t sequence_num = 0;
+ if(sequence && (sequence->atom.type == props->urid.atom_int))
+ {
+ sequence_num = sequence->body;
+ }
+
+ if(rem && lv2_atom_forge_is_object_type(forge, rem->atom.type))
+ {
+ LV2_ATOM_OBJECT_FOREACH(rem, prop)
+ {
+ const LV2_URID property = prop->key;
+ const LV2_Atom *value = &prop->value;
+
+ if(props->dyn && props->dyn->prop)
+ {
+ props->dyn->prop(props->data, PROPS_DYN_EV_REM, subj, property, value);
+ }
+ }
+ }
+
+ if(add && lv2_atom_forge_is_object_type(forge, add->atom.type))
+ {
+ LV2_ATOM_OBJECT_FOREACH(add, prop)
+ {
+ const LV2_URID property = prop->key;
+ const LV2_Atom *value = &prop->value;
+
+ if(props->dyn && props->dyn->prop)
+ {
+ props->dyn->prop(props->data, PROPS_DYN_EV_ADD, subj, property, value);
+ }
+ }
+ }
+
+ if(sequence_num && *ref)
+ {
+ *ref = _props_patch_ack(props, forge, frames, sequence_num);
+ }
+
+ return 1;
+ }
+
+ return 0; // did not handle a patch event
+}
+
+static inline void
+props_set(props_t *props, LV2_Atom_Forge *forge, uint32_t frames,
+ LV2_URID property, LV2_Atom_Forge_Ref *ref)
+{
+ props_impl_t *impl = _props_impl_get(props, property);
+
+ if(impl)
+ {
+ _props_impl_stash(props, impl);
+
+ if(*ref && !impl->def->hidden) //TODO use patch:sequenceNumber
+ *ref = _props_patch_set(props, forge, frames, impl, 0);
+ }
+}
+
+static inline void
+props_get(props_t *props, LV2_Atom_Forge *forge, uint32_t frames,
+ LV2_URID property, LV2_Atom_Forge_Ref *ref)
+{
+ props_impl_t *impl = _props_impl_get(props, property);
+
+ if(impl)
+ {
+ if(*ref && !impl->def->hidden) //TODO use patch:sequenceNumber
+ *ref = _props_patch_get(props, forge, frames, impl, 0);
+ }
+}
+
+static inline void
+props_stash(props_t *props, LV2_URID property)
+{
+ props_impl_t *impl = _props_impl_get(props, property);
+
+ if(impl)
+ _props_impl_stash(props, impl);
+}
+
+static inline LV2_URID
+props_map(props_t *props, const char *uri)
+{
+ for(unsigned i = 0; i < props->nimpls; i++)
+ {
+ props_impl_t *impl = &props->impls[i];
+
+ if(!strcmp(impl->def->property, uri))
+ return impl->property;
+ }
+
+ return 0;
+}
+
+static inline const char *
+props_unmap(props_t *props, LV2_URID property)
+{
+ props_impl_t *impl = _props_impl_get(props, property);
+
+ if(impl)
+ return impl->def->property;
+
+ return NULL;
+}
+
+static inline int
+_copy_file(const char *to, const char *from)
+{
+ FILE *dst = NULL;
+ FILE *src = NULL;
+ int ch;
+
+ dst = fopen(to, "wb");
+ if(!dst)
+ {
+ return 1;
+ }
+
+ src = fopen(from, "rb");
+ if(!src)
+ {
+ fclose(dst);
+
+ return 1;
+ }
+
+ while( (ch = fgetc(src)) != EOF)
+ {
+ fputc(ch, dst);
+ }
+
+ fclose(dst);
+ fclose(src);
+
+ return 0;
+}
+
+static inline void
+_free_path(const LV2_State_Free_Path *free_path, char *path)
+{
+ if(free_path && free_path->free_path)
+ {
+ free_path->free_path(free_path->handle, path);
+ }
+ else
+ {
+ free(path);
+ }
+}
+
+static inline LV2_State_Status
+props_save(props_t *props, LV2_State_Store_Function store,
+ LV2_State_Handle state, uint32_t flags, const LV2_Feature *const *features)
+{
+ const LV2_State_Map_Path *map_path = NULL;
+ const LV2_State_Make_Path *make_path = NULL;
+ const LV2_State_Free_Path *free_path = NULL;
+
+ // set POD flag if not already set by host
+ flags |= LV2_STATE_IS_POD;
+
+ for(unsigned i = 0; features[i]; i++)
+ {
+ if(!strcmp(features[i]->URI, LV2_STATE__mapPath))
+ {
+ map_path = features[i]->data;
+ }
+ else if(!strcmp(features[i]->URI, LV2_STATE__makePath))
+ {
+ make_path = features[i]->data;
+ }
+ else if(!strcmp(features[i]->URI, LV2_STATE__freePath))
+ {
+ free_path = features[i]->data;
+ }
+ }
+
+ void *body = malloc(props->max_size); // create memory to store widest value
+ if(body)
+ {
+ for(unsigned i = 0; i < props->nimpls; i++)
+ {
+ props_impl_t *impl = &props->impls[i];
+
+ if(impl->access == props->urid.patch_readable)
+ continue; // skip read-only, as it makes no sense to restore them
+
+ // always clear memory
+ memset(body, 0x0, props->max_size);
+
+ _props_impl_spin_lock(impl, PROP_STATE_NONE, PROP_STATE_LOCK);
+
+ // create temporary copy of value, store() may well be blocking
+ const uint32_t size = impl->stash.size;
+ memcpy(body, impl->stash.body, size);
+
+ _props_impl_unlock(impl, PROP_STATE_NONE);
+
+ if( map_path && map_path->abstract_path
+ && (impl->type == props->urid.atom_path) )
+ {
+ const char *path = strstr(body, "file://") == body
+ ? (char *)body + 7 // skip "file://"
+ : (char *)body;
+
+ char *abstract = NULL;
+
+ if( make_path && make_path->path
+ && (strstr(path, "/tmp") == path) )
+ {
+ char *absolute = make_path->path(make_path->handle, basename(path));
+
+ if(absolute)
+ {
+ if(_copy_file(absolute, path) == 0)
+ {
+ abstract = map_path->abstract_path(map_path->handle, absolute);
+ }
+
+ _free_path(free_path, absolute);
+ }
+ }
+ else
+ {
+ abstract = map_path->abstract_path(map_path->handle, path);
+ }
+
+ if(abstract)
+ {
+ const uint32_t sz = strlen(abstract) + 1;
+ store(state, impl->property, abstract, sz, impl->type, flags);
+
+ _free_path(free_path, abstract);
+ }
+ }
+ else // !Path
+ {
+ store(state, impl->property, body, size, impl->type, flags);
+ }
+ }
+
+ free(body);
+ }
+
+ return LV2_STATE_SUCCESS;
+}
+
+static inline LV2_State_Status
+props_restore(props_t *props, LV2_State_Retrieve_Function retrieve,
+ LV2_State_Handle state, uint32_t flags __attribute__((unused)),
+ const LV2_Feature *const *features)
+{
+ const LV2_State_Map_Path *map_path = NULL;
+ const LV2_State_Free_Path *free_path = NULL;
+
+ for(unsigned i = 0; features[i]; i++)
+ {
+ if(!strcmp(features[i]->URI, LV2_STATE__mapPath))
+ {
+ map_path = features[i]->data;
+ }
+ if(!strcmp(features[i]->URI, LV2_STATE__freePath))
+ {
+ free_path = features[i]->data;
+ }
+ }
+
+ for(unsigned i = 0; i < props->nimpls; i++)
+ {
+ props_impl_t *impl = &props->impls[i];
+
+ if(impl->access == props->urid.patch_readable)
+ continue; // skip read-only, as it makes no sense to restore them
+
+ size_t size;
+ uint32_t type;
+ uint32_t _flags;
+ const void *body = retrieve(state, impl->property, &size, &type, &_flags);
+
+ if( body
+ && (type == impl->type)
+ && ( (impl->def->max_size == 0) || (size <= impl->def->max_size) ) )
+ {
+ if( map_path && map_path->absolute_path
+ && (type == props->urid.atom_path) )
+ {
+ char *absolute = map_path->absolute_path(map_path->handle, body);
+ if(absolute)
+ {
+ const uint32_t sz = strlen(absolute) + 1;
+
+ _props_impl_spin_lock(impl, PROP_STATE_NONE, PROP_STATE_LOCK);
+
+ impl->stash.size = sz;
+ memcpy(impl->stash.body, absolute, sz);
+
+ _props_impl_unlock(impl, PROP_STATE_RESTORE);
+
+ _free_path(free_path, absolute);
+ }
+ }
+ else // !Path
+ {
+ _props_impl_spin_lock(impl, PROP_STATE_NONE, PROP_STATE_LOCK);
+
+ impl->stash.size = size;
+ memcpy(impl->stash.body, body, size);
+
+ _props_impl_unlock(impl, PROP_STATE_RESTORE);
+ }
+ }
+ }
+
+ _props_restoring_set(props);
+
+ return LV2_STATE_SUCCESS;
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // _LV2_PROPS_H_
diff --git a/props.lv2/test/chunk.bin b/props.lv2/test/chunk.bin
new file mode 100644
index 0000000..b66efb8
--- /dev/null
+++ b/props.lv2/test/chunk.bin
Binary files differ
diff --git a/props.lv2/test/manifest.ttl.in b/props.lv2/test/manifest.ttl.in
new file mode 100644
index 0000000..0ecc313
--- /dev/null
+++ b/props.lv2/test/manifest.ttl.in
@@ -0,0 +1,28 @@
+# 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.
+
+@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 props: <http://open-music-kontrollers.ch/lv2/props#> .
+
+# Orbit Looper
+props:test
+ a lv2:Plugin ;
+ lv2:minorVersion @MINOR_VERSION@ ;
+ lv2:microVersion @MICRO_VERSION@ ;
+ lv2:binary <props@MODULE_SUFFIX@> ;
+ rdfs:seeAlso <props.ttl> .
diff --git a/props.lv2/test/props.c b/props.lv2/test/props.c
new file mode 100644
index 0000000..590c519
--- /dev/null
+++ b/props.lv2/test/props.c
@@ -0,0 +1,323 @@
+/*
+ * 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 <stdio.h>
+
+#include <props.h>
+
+#include <lv2/lv2plug.in/ns/ext/log/log.h>
+#include <lv2/lv2plug.in/ns/ext/log/logger.h>
+
+#define PROPS_PREFIX "http://open-music-kontrollers.ch/lv2/props#"
+#define PROPS_TEST_URI PROPS_PREFIX"test"
+
+#define MAX_NPROPS 7
+#define MAX_STRLEN 256
+
+typedef struct _plugstate_t plugstate_t;
+typedef struct _plughandle_t plughandle_t;
+
+struct _plugstate_t {
+ int32_t val1;
+ int64_t val2;
+ float val3;
+ double val4;
+ char val5 [MAX_STRLEN];
+ char val6 [MAX_STRLEN];
+ uint8_t val7 [MAX_STRLEN];
+};
+
+struct _plughandle_t {
+ LV2_URID_Map *map;
+ LV2_Log_Log *log;
+ LV2_Log_Logger logger;
+ LV2_Atom_Forge forge;
+ LV2_Atom_Forge_Ref ref;
+
+ PROPS_T(props, MAX_NPROPS);
+ plugstate_t state;
+ plugstate_t stash;
+
+ struct {
+ LV2_URID val2;
+ LV2_URID val4;
+ } urid;
+
+ const LV2_Atom_Sequence *event_in;
+ LV2_Atom_Sequence *event_out;
+};
+
+static void
+_intercept(void *data, int64_t frames __attribute__((unused)), props_impl_t *impl)
+{
+ plughandle_t *handle = data;
+
+ lv2_log_trace(&handle->logger, "SET : %s\n", impl->def->property);
+}
+
+static void
+_intercept_stat1(void *data, int64_t frames, props_impl_t *impl)
+{
+ plughandle_t *handle = data;
+
+ _intercept(data, frames, impl);
+
+ handle->state.val2 = handle->state.val1 * 2;
+
+ props_set(&handle->props, &handle->forge, frames, handle->urid.val2, &handle->ref);
+}
+
+static void
+_intercept_stat3(void *data, int64_t frames, props_impl_t *impl)
+{
+ plughandle_t *handle = data;
+
+ _intercept(data, frames, impl);
+
+ handle->state.val4 = handle->state.val3 * 2;
+
+ props_set(&handle->props, &handle->forge, frames, handle->urid.val4, &handle->ref);
+}
+
+static void
+_intercept_stat6(void *data, int64_t frames, props_impl_t *impl)
+{
+ plughandle_t *handle = data;
+
+ _intercept(data, frames, impl);
+
+ const char *path = strstr(handle->state.val6, "file://")
+ ? handle->state.val6 + 7 // skip "file://"
+ : handle->state.val6;
+ FILE *f = fopen(path, "wb"); // create empty file
+ if(f)
+ fclose(f);
+}
+
+static const props_def_t defs [MAX_NPROPS] = {
+ {
+ .property = PROPS_PREFIX"statInt",
+ .offset = offsetof(plugstate_t, val1),
+ .type = LV2_ATOM__Int,
+ .event_cb = _intercept_stat1,
+ },
+ {
+ .property = PROPS_PREFIX"statLong",
+ .access = LV2_PATCH__readable,
+ .offset = offsetof(plugstate_t, val2),
+ .type = LV2_ATOM__Long,
+ .event_cb = _intercept,
+ },
+ {
+ .property = PROPS_PREFIX"statFloat",
+ .offset = offsetof(plugstate_t, val3),
+ .type = LV2_ATOM__Float,
+ .event_cb = _intercept_stat3,
+ },
+ {
+ .property = PROPS_PREFIX"statDouble",
+ .access = LV2_PATCH__readable,
+ .offset = offsetof(plugstate_t, val4),
+ .type = LV2_ATOM__Double,
+ .event_cb = _intercept,
+ },
+ {
+ .property = PROPS_PREFIX"statString",
+ .offset = offsetof(plugstate_t, val5),
+ .type = LV2_ATOM__String,
+ .event_cb = _intercept,
+ .max_size = MAX_STRLEN // strlen
+ },
+ {
+ .property = PROPS_PREFIX"statPath",
+ .offset = offsetof(plugstate_t, val6),
+ .type = LV2_ATOM__Path,
+ .event_cb = _intercept_stat6,
+ .max_size = MAX_STRLEN // strlen
+ },
+ {
+ .property = PROPS_PREFIX"statChunk",
+ .offset = offsetof(plugstate_t, val7),
+ .type = LV2_ATOM__Chunk,
+ .event_cb = _intercept,
+ .max_size = MAX_STRLEN // strlen
+ }
+};
+
+static LV2_Handle
+instantiate(const LV2_Descriptor* descriptor,
+ double rate __attribute__((unused)),
+ const char *bundle_path __attribute__((unused)),
+ 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_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)
+ {
+ fprintf(stderr,
+ "%s: Host does not support log:log\n", descriptor->URI);
+ free(handle);
+ return NULL;
+ }
+
+ lv2_log_logger_init(&handle->logger, handle->map, handle->log);
+ lv2_atom_forge_init(&handle->forge, handle->map);
+
+ if(!props_init(&handle->props, descriptor->URI,
+ defs, MAX_NPROPS, &handle->state, &handle->stash,
+ handle->map, handle))
+ {
+ lv2_log_error(&handle->logger, "failed to initialize property structure\n");
+ free(handle);
+ return NULL;
+ }
+
+ handle->urid.val2 = props_map(&handle->props, PROPS_PREFIX"statLong");
+ handle->urid.val4 = props_map(&handle->props, PROPS_PREFIX"statDouble");
+
+ 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->event_in = (const LV2_Atom_Sequence *)data;
+ break;
+ case 1:
+ handle->event_out = (LV2_Atom_Sequence *)data;
+ break;
+ default:
+ break;
+ }
+}
+
+static void
+run(LV2_Handle instance, uint32_t nsamples __attribute__((unused)))
+{
+ plughandle_t *handle = instance;
+
+ uint32_t capacity = handle->event_out->atom.size;
+ LV2_Atom_Forge_Frame frame;
+ lv2_atom_forge_set_buffer(&handle->forge, (uint8_t *)handle->event_out, capacity);
+ handle->ref = lv2_atom_forge_sequence_head(&handle->forge, &frame, 0);
+
+ props_idle(&handle->props, &handle->forge, 0, &handle->ref);
+
+ LV2_ATOM_SEQUENCE_FOREACH(handle->event_in, ev)
+ {
+ const LV2_Atom_Object *obj = (const LV2_Atom_Object *)&ev->body;
+
+ if(handle->ref)
+ props_advance(&handle->props, &handle->forge, ev->time.frames, obj, &handle->ref);
+ }
+
+ if(handle->ref)
+ lv2_atom_forge_pop(&handle->forge, &frame);
+ else
+ lv2_atom_sequence_clear(handle->event_out);
+}
+
+static void
+cleanup(LV2_Handle instance)
+{
+ plughandle_t *handle = instance;
+
+ free(handle);
+}
+
+static LV2_State_Status
+_state_save(LV2_Handle instance, LV2_State_Store_Function store,
+ LV2_State_Handle state, uint32_t flags,
+ const LV2_Feature *const *features)
+{
+ plughandle_t *handle = (plughandle_t *)instance;
+
+ return props_save(&handle->props, store, state, flags, features);
+}
+
+static LV2_State_Status
+_state_restore(LV2_Handle instance, LV2_State_Retrieve_Function retrieve,
+ LV2_State_Handle state, uint32_t flags,
+ const LV2_Feature *const *features)
+{
+ plughandle_t *handle = (plughandle_t *)instance;
+
+ return props_restore(&handle->props, retrieve, state, flags, features);
+}
+
+LV2_State_Interface state_iface = {
+ .save = _state_save,
+ .restore = _state_restore
+};
+
+static const void *
+extension_data(const char *uri)
+{
+ if(!strcmp(uri, LV2_STATE__interface))
+ return &state_iface;
+ return NULL;
+}
+
+const LV2_Descriptor props_test = {
+ .URI = PROPS_TEST_URI,
+ .instantiate = instantiate,
+ .connect_port = connect_port,
+ .activate = NULL,
+ .run = run,
+ .deactivate = NULL,
+ .cleanup = cleanup,
+ .extension_data = extension_data
+};
+
+#ifdef _WIN32
+__declspec(dllexport)
+#else
+__attribute__((visibility("default")))
+#endif
+const LV2_Descriptor*
+lv2_descriptor(uint32_t index)
+{
+ switch(index)
+ {
+ case 0:
+ return &props_test;
+ default:
+ return NULL;
+ }
+}
diff --git a/props.lv2/test/props.ttl b/props.lv2/test/props.ttl
new file mode 100644
index 0000000..f2ce779
--- /dev/null
+++ b/props.lv2/test/props.ttl
@@ -0,0 +1,151 @@
+# 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.
+
+@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 omk: <http://open-music-kontrollers.ch/ventosus#> .
+@prefix proj: <http://open-music-kontrollers.ch/lv2/> .
+@prefix props: <http://open-music-kontrollers.ch/lv2/props#> .
+
+# 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:props
+ a doap:Project ;
+ doap:maintainer omk:me ;
+ doap:name "Props Bundle" .
+
+props:statInt
+ a lv2:Parameter ;
+ rdfs:range atom:Int ;
+ rdfs:label "statInt" ;
+ rdfs:comment "This is a 32-bit integer" ;
+ units:unit units:hz ;
+ lv2:minimum 0 ;
+ lv2:maximum 10 .
+
+props:statLong
+ a lv2:Parameter ;
+ rdfs:range atom:Long ;
+ rdfs:label "statLong" ;
+ rdfs:comment "This is a 64-bit integer" ;
+ units:unit units:khz ;
+ lv2:minimum 0 ;
+ lv2:maximum 20 .
+
+props:statFloat
+ a lv2:Parameter ;
+ rdfs:range atom:Float ;
+ rdfs:label "statFloat" ;
+ rdfs:comment "This is a 32-bit float" ;
+ units:unit units:mhz ;
+ lv2:minimum -0.5 ;
+ lv2:maximum 0.5 .
+
+props:statDouble
+ a lv2:Parameter ;
+ rdfs:range atom:Double ;
+ rdfs:label "statDouble" ;
+ rdfs:comment "This is a 64-bit double" ;
+ units:unit units:db ;
+ lv2:minimum -1.0 ;
+ lv2:maximum 1.0 .
+
+props:statString
+ a lv2:Parameter ;
+ rdfs:range atom:String ;
+ rdfs:label "statString" ;
+ rdfs:comment "This is a string" .
+
+props:statPath
+ a lv2:Parameter ;
+ rdfs:range atom:Path ;
+ rdfs:label "statPath" ;
+ rdfs:comment "This is a path" .
+
+props:statChunk
+ a lv2:Parameter ;
+ rdfs:range atom:Chunk;
+ rdfs:label "statChunk" ;
+ rdfs:comment "This is a chunk" .
+
+# Looper Test
+props:test
+ a lv2:Plugin ,
+ lv2:ConverterPlugin ;
+ doap:name "Props Test" ;
+ doap:license <https://spdx.org/licenses/Artistic-2.0> ;
+ lv2:project proj:props ;
+ lv2:requiredFeature urid:map, log:log, state:loadDefaultState ;
+ lv2:optionalFeature lv2:isLive, lv2:hardRTCapable, state:threadSafeRestore ;
+ lv2:extensionData state:interface ;
+
+ lv2:port [
+ # sink event port
+ a lv2:InputPort ,
+ atom:AtomPort ;
+ atom:bufferType atom:Sequence ;
+ atom:supports patch:Message ;
+ lv2:index 0 ;
+ lv2:symbol "event_in" ;
+ lv2:name "Event Input" ;
+ lv2:designation lv2:control ;
+ ] , [
+ # source event port
+ a lv2:OutputPort ,
+ atom:AtomPort ;
+ atom:bufferType atom:Sequence ;
+ atom:supports patch:Message ;
+ lv2:index 1 ;
+ lv2:symbol "event_out" ;
+ lv2:name "Event Output" ;
+ lv2:designation lv2:control ;
+ ] ;
+
+ patch:writable
+ props:statInt ,
+ props:statFloat ,
+ props:statString ,
+ props:statPath ,
+ props:statChunk ;
+
+ patch:readable
+ props:statLong ,
+ props:statDouble ;
+
+ state:state [
+ props:statInt 4 ;
+ props:statFloat "0.4"^^xsd:float ;
+ props:statString "Hello world" ;
+ props:statPath <> ;
+ props:statChunk "AQIDBAUGBw=="^^xsd:base64Binary ;
+ ] .
diff --git a/props.lv2/test/props_test.c b/props.lv2/test/props_test.c
new file mode 100644
index 0000000..69f3b3f
--- /dev/null
+++ b/props.lv2/test/props_test.c
@@ -0,0 +1,647 @@
+/*
+ * 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 <assert.h>
+
+#include <props.h>
+
+#define MAX_URIDS 512
+#define STR_SIZE 32
+#define CHUNK_SIZE 16
+#define VEC_SIZE 13
+
+#define PROPS_PREFIX "http://open-music-kontrollers.ch/lv2/props#"
+#define PROPS_TEST_URI PROPS_PREFIX"test"
+
+typedef struct _plugstate_t plugstate_t;
+typedef struct _urid_t urid_t;
+typedef struct _handle_t handle_t;
+typedef void (*test_t)(handle_t *handle);
+typedef void *(*ser_atom_realloc_t)(void *data, void *buf, size_t size);
+typedef void (*ser_atom_free_t)(void *data, void *buf);
+
+typedef struct _ser_atom_t ser_atom_t;
+
+struct _plugstate_t {
+ int32_t b32;
+ int32_t i32;
+ int64_t i64;
+ float f32;
+ double f64;
+ uint32_t urid;
+ char str [STR_SIZE];
+ char uri [STR_SIZE];
+ char path [STR_SIZE];
+ uint8_t chunk [CHUNK_SIZE];
+ LV2_Atom_Literal_Body lit;
+ char lit_body [STR_SIZE];
+ LV2_Atom_Vector_Body vec;
+ int32_t vec_body [VEC_SIZE];
+ LV2_Atom_Object_Body obj; //FIXME
+ LV2_Atom_Sequence_Body seq; //FIXME
+};
+
+struct _urid_t {
+ LV2_URID urid;
+ char *uri;
+};
+
+enum {
+ PROP_b32 = 0,
+ PROP_i32,
+ PROP_i64,
+ PROP_f32,
+ PROP_f64,
+ PROP_urid,
+ PROP_str,
+ PROP_uri,
+ PROP_path,
+ PROP_chunk,
+ PROP_lit,
+ PROP_vec,
+ PROP_obj,
+ PROP_seq,
+
+ MAX_NPROPS
+};
+
+struct _handle_t {
+ PROPS_T(props, MAX_NPROPS);
+ plugstate_t state;
+ plugstate_t stash;
+
+ LV2_URID_Map map;
+
+ urid_t urids [MAX_URIDS];
+ LV2_URID urid;
+};
+
+struct _ser_atom_t {
+ ser_atom_realloc_t realloc;
+ ser_atom_free_t free;
+ void *data;
+
+ size_t size;
+ size_t offset;
+ union {
+ uint8_t *buf;
+ LV2_Atom *atom;
+ };
+};
+
+static LV2_Atom_Forge_Ref
+_ser_atom_sink(LV2_Atom_Forge_Sink_Handle handle, const void *buf,
+ uint32_t size)
+{
+ ser_atom_t *ser = handle;
+ const size_t needed = ser->offset + size;
+
+ while(needed > ser->size)
+ {
+ const size_t augmented = ser->size
+ ? ser->size << 1
+ : 1024;
+ uint8_t *grown = ser->realloc(ser->data, ser->buf, augmented);
+ if(!grown) // out-of-memory
+ {
+ return 0;
+ }
+
+ ser->buf = grown;
+ ser->size = augmented;
+ }
+
+ const LV2_Atom_Forge_Ref ref = ser->offset + 1;
+ memcpy(&ser->buf[ser->offset], buf, size);
+ ser->offset += size;
+
+ return ref;
+}
+
+static LV2_Atom *
+_ser_atom_deref(LV2_Atom_Forge_Sink_Handle handle, LV2_Atom_Forge_Ref ref)
+{
+ ser_atom_t *ser = handle;
+
+ if(!ref) // invalid reference
+ {
+ return NULL;
+ }
+
+ const size_t offset = ref - 1;
+ return (LV2_Atom *)&ser->buf[offset];
+}
+
+static void *
+_ser_atom_realloc(void *data, void *buf, size_t size)
+{
+ (void)data;
+
+ return realloc(buf, size);
+}
+
+static void
+_ser_atom_free(void *data, void *buf)
+{
+ (void)data;
+
+ free(buf);
+}
+
+static int
+ser_atom_deinit(ser_atom_t *ser)
+{
+ if(!ser)
+ {
+ return -1;
+ }
+
+ if(ser->buf)
+ {
+ ser->free(ser->data, ser->buf);
+ }
+
+ ser->size = 0;
+ ser->offset = 0;
+ ser->buf = NULL;
+
+ return 0;
+}
+
+static int
+ser_atom_funcs(ser_atom_t *ser, ser_atom_realloc_t realloc,
+ ser_atom_free_t free, void *data)
+{
+ if(!ser || !realloc || !free || ser_atom_deinit(ser))
+ {
+ return -1;
+ }
+
+ ser->realloc = realloc;
+ ser->free = free;
+ ser->data = data;
+
+ return 0;
+}
+
+static int
+ser_atom_init(ser_atom_t *ser)
+{
+ if(!ser)
+ {
+ return -1;
+ }
+
+ ser->size = 0;
+ ser->offset = 0;
+ ser->buf = NULL;
+
+ return ser_atom_funcs(ser, _ser_atom_realloc, _ser_atom_free, NULL);
+}
+
+#if 0
+static int
+ser_atom_reset(ser_atom_t *ser, LV2_Atom_Forge *forge)
+{
+ if(!ser || !forge)
+ {
+ return -1;
+ }
+
+ lv2_atom_forge_set_sink(forge, _ser_atom_sink, _ser_atom_deref, ser);
+
+ ser->offset = 0;
+
+ return 0;
+}
+#endif
+
+static LV2_Atom *
+ser_atom_get(ser_atom_t *ser)
+{
+ if(!ser)
+ {
+ return NULL;
+ }
+
+ return ser->atom;
+}
+
+static LV2_URID
+_map(LV2_URID_Map_Handle instance, const char *uri)
+{
+ handle_t *handle = instance;
+
+ urid_t *itm;
+ for(itm=handle->urids; itm->urid; itm++)
+ {
+ if(!strcmp(itm->uri, uri))
+ return itm->urid;
+ }
+
+ assert(handle->urid + 1 < MAX_URIDS);
+
+ // create new
+ itm->urid = ++handle->urid;
+ itm->uri = strdup(uri);
+
+ return itm->urid;
+}
+
+static const props_def_t defs [MAX_NPROPS] = {
+ [PROP_b32] = {
+ .property = PROPS_PREFIX"b32",
+ .offset = offsetof(plugstate_t, b32),
+ .type = LV2_ATOM__Bool
+ },
+ [PROP_i32] = {
+ .property = PROPS_PREFIX"i32",
+ .offset = offsetof(plugstate_t, i32),
+ .type = LV2_ATOM__Int
+ },
+ [PROP_i64] = {
+ .property = PROPS_PREFIX"i64",
+ .offset = offsetof(plugstate_t, i64),
+ .type = LV2_ATOM__Long
+ },
+ [PROP_f32] = {
+ .property = PROPS_PREFIX"f32",
+ .offset = offsetof(plugstate_t, f32),
+ .type = LV2_ATOM__Float
+ },
+ [PROP_f64] = {
+ .property = PROPS_PREFIX"f64",
+ .offset = offsetof(plugstate_t, f64),
+ .type = LV2_ATOM__Double
+ },
+ [PROP_urid] = {
+ .property = PROPS_PREFIX"urid",
+ .offset = offsetof(plugstate_t, urid),
+ .type = LV2_ATOM__URID
+ },
+ [PROP_str] = {
+ .property = PROPS_PREFIX"str",
+ .offset = offsetof(plugstate_t, str),
+ .type = LV2_ATOM__String,
+ .max_size = STR_SIZE
+ },
+ [PROP_uri] = {
+ .property = PROPS_PREFIX"uri",
+ .offset = offsetof(plugstate_t, uri),
+ .type = LV2_ATOM__URI,
+ .max_size = STR_SIZE
+ },
+ [PROP_path] = {
+ .property = PROPS_PREFIX"path",
+ .offset = offsetof(plugstate_t, path),
+ .type = LV2_ATOM__Path,
+ .max_size = STR_SIZE
+ },
+ [PROP_chunk] = {
+ .property = PROPS_PREFIX"chunk",
+ .offset = offsetof(plugstate_t, chunk),
+ .type = LV2_ATOM__Chunk,
+ .max_size = CHUNK_SIZE
+ },
+ [PROP_lit] = {
+ .property = PROPS_PREFIX"lit",
+ .offset = offsetof(plugstate_t, lit),
+ .type = LV2_ATOM__Literal,
+ .max_size = sizeof(LV2_Atom_Literal_Body) + STR_SIZE
+ },
+ [PROP_vec] = {
+ .property = PROPS_PREFIX"vec",
+ .offset = offsetof(plugstate_t, vec),
+ .type = LV2_ATOM__Literal,
+ .max_size = sizeof(LV2_Atom_Vector_Body) + VEC_SIZE*sizeof(int32_t)
+ },
+ [PROP_obj] = {
+ .property = PROPS_PREFIX"obj",
+ .offset = offsetof(plugstate_t, obj),
+ .type = LV2_ATOM__Object,
+ .max_size = sizeof(LV2_Atom_Object_Body) + 0 //FIXME
+ },
+ [PROP_seq] = {
+ .property = PROPS_PREFIX"seq",
+ .offset = offsetof(plugstate_t, seq),
+ .type = LV2_ATOM__Sequence,
+ .max_size = sizeof(LV2_Atom_Sequence_Body) + 0 //FIXME
+ }
+};
+
+static void
+_test_1(handle_t *handle)
+{
+ assert(handle);
+
+ props_t *props = &handle->props;
+ plugstate_t *state = &handle->state;
+ plugstate_t *stash = &handle->stash;
+ LV2_URID_Map *map = &handle->map;
+
+ for(unsigned i = 0; i < MAX_NPROPS; i++)
+ {
+ const props_def_t *def = &defs[i];
+
+ const LV2_URID property = props_map(props, def->property);
+ assert(property != 0);
+ assert(property == map->map(map->handle, def->property));
+
+ assert(strcmp(props_unmap(props, property), def->property) == 0);
+
+ props_impl_t *impl = _props_impl_get(props, property);
+ assert(impl);
+
+ const LV2_URID type = map->map(map->handle, def->type);
+ const LV2_URID access = map->map(map->handle, def->access
+ ? def->access : LV2_PATCH__writable);
+
+ assert(impl->property == property);
+ assert(impl->type == type);
+ assert(impl->access == access);
+
+ assert(impl->def == def);
+
+ assert(atomic_load(&impl->state) == PROP_STATE_NONE);
+ assert(impl->stashing == false);
+
+ switch(i)
+ {
+ case PROP_b32:
+ {
+ assert(impl->value.size == sizeof(state->b32));
+ assert(impl->value.body == &state->b32);
+
+ assert(impl->stash.size == sizeof(stash->b32));
+ assert(impl->stash.body == &stash->b32);
+ } break;
+ case PROP_i32:
+ {
+ assert(impl->value.size == sizeof(state->i32));
+ assert(impl->value.body == &state->i32);
+
+ assert(impl->stash.size == sizeof(stash->i32));
+ assert(impl->stash.body == &stash->i32);
+ } break;
+ case PROP_i64:
+ {
+ assert(impl->value.size == sizeof(state->i64));
+ assert(impl->value.body == &state->i64);
+
+ assert(impl->stash.size == sizeof(stash->i64));
+ assert(impl->stash.body == &stash->i64);
+ } break;
+ case PROP_f32:
+ {
+ assert(impl->value.size == sizeof(state->f32));
+ assert(impl->value.body == &state->f32);
+
+ assert(impl->stash.size == sizeof(stash->f32));
+ assert(impl->stash.body == &stash->f32);
+ } break;
+ case PROP_f64:
+ {
+ assert(impl->value.size == sizeof(state->f64));
+ assert(impl->value.body == &state->f64);
+
+ assert(impl->stash.size == sizeof(stash->f64));
+ assert(impl->stash.body == &stash->f64);
+ } break;
+ case PROP_urid:
+ {
+ assert(impl->value.size == sizeof(state->urid));
+ assert(impl->value.body == &state->urid);
+
+ assert(impl->stash.size == sizeof(stash->urid));
+ assert(impl->stash.body == &stash->urid);
+ } break;
+ case PROP_str:
+ {
+ assert(impl->value.size == 0);
+ assert(impl->value.body == &state->str);
+
+ assert(impl->stash.size == 0);
+ assert(impl->stash.body == &stash->str);
+ } break;
+ case PROP_uri:
+ {
+ assert(impl->value.size == 0);
+ assert(impl->value.body == &state->uri);
+
+ assert(impl->stash.size == 0);
+ assert(impl->stash.body == &stash->uri);
+ } break;
+ case PROP_path:
+ {
+ assert(impl->value.size == 0);
+ assert(impl->value.body == &state->path);
+
+ assert(impl->stash.size == 0);
+ assert(impl->stash.body == &stash->path);
+ } break;
+ case PROP_chunk:
+ {
+ assert(impl->value.size == 0);
+ assert(impl->value.body == &state->chunk);
+
+ assert(impl->stash.size == 0);
+ assert(impl->stash.body == &stash->chunk);
+ } break;
+ case PROP_lit:
+ {
+ assert(impl->value.size == sizeof(state->lit));
+ assert(impl->value.body == &state->lit);
+
+ assert(impl->stash.size == sizeof(stash->lit));
+ assert(impl->stash.body == &stash->lit);
+ } break;
+ case PROP_vec:
+ {
+ assert(impl->value.size == sizeof(state->vec));
+ assert(impl->value.body == &state->vec);
+
+ assert(impl->stash.size == sizeof(stash->vec));
+ assert(impl->stash.body == &stash->vec);
+ } break;
+ case PROP_obj:
+ {
+ assert(impl->value.size == sizeof(state->obj));
+ assert(impl->value.body == &state->obj);
+
+ assert(impl->stash.size == sizeof(stash->obj));
+ assert(impl->stash.body == &stash->obj);
+ } break;
+ case PROP_seq:
+ {
+ assert(impl->value.size == sizeof(state->seq));
+ assert(impl->value.body == &state->seq);
+
+ assert(impl->stash.size == sizeof(stash->seq));
+ assert(impl->stash.body == &stash->seq);
+ } break;
+ default:
+ {
+ assert(false);
+ } break;
+ }
+ }
+}
+
+static void
+_test_2(handle_t *handle)
+{
+ assert(handle);
+
+ props_t *props = &handle->props;
+ plugstate_t *state = &handle->state;
+ plugstate_t *stash = &handle->stash;
+ LV2_URID_Map *map = &handle->map;
+
+ LV2_Atom_Forge forge;
+ LV2_Atom_Forge_Frame frame;
+ LV2_Atom_Forge_Ref ref;
+ ser_atom_t ser;
+
+ lv2_atom_forge_init(&forge, map);
+ assert(ser_atom_init(&ser) == 0);
+
+ lv2_atom_forge_set_sink(&forge, _ser_atom_sink, _ser_atom_deref, &ser);
+
+ ref = lv2_atom_forge_sequence_head(&forge, &frame, 0);
+ assert(ref);
+
+ props_idle(props, &forge, 0, &ref);
+ assert(ref);
+
+ const LV2_URID property = props_map(props, defs[0].property);
+ assert(property);
+
+ state->b32 = true;
+
+ props_set(props, &forge, 1, property, &ref);
+ assert(ref);
+
+ state->b32 = false;
+
+ lv2_atom_forge_pop(&forge, &frame);
+
+ const LV2_Atom_Sequence *seq = (const LV2_Atom_Sequence *)ser_atom_get(&ser);
+ assert(seq);
+
+ unsigned nevs = 0;
+ LV2_ATOM_SEQUENCE_FOREACH(seq, ev)
+ {
+ const LV2_Atom *atom = &ev->body;
+
+ assert(ev->time.frames == 1);
+ assert(atom->type == forge.Object);
+
+ const LV2_Atom_Object *obj = (const LV2_Atom_Object *)atom;
+ assert(obj->body.id == 0);
+
+ if(obj->body.otype == props->urid.state_StateChanged)
+ {
+ continue;
+ }
+
+ assert(obj->body.otype == props->urid.patch_set);
+
+ unsigned nprops = 0;
+ LV2_ATOM_OBJECT_FOREACH(obj, prop)
+ {
+ assert(prop->context == 0);
+
+ if(prop->key == props->urid.patch_subject)
+ {
+ const LV2_Atom_URID *val = (const LV2_Atom_URID *)&prop->value;
+
+ assert(val->atom.type == forge.URID);
+ assert(val->atom.size == sizeof(uint32_t));
+ assert(val->body == props->urid.subject);
+
+ nprops |= 0x1;
+ }
+ else if(prop->key == props->urid.patch_property)
+ {
+ const LV2_Atom_URID *val = (const LV2_Atom_URID *)&prop->value;
+
+ assert(val->atom.type == forge.URID);
+ assert(val->atom.size == sizeof(uint32_t));
+ assert(val->body == property);
+
+ nprops |= 0x2;
+ }
+ else if(prop->key == props->urid.patch_value)
+ {
+ const LV2_Atom_Bool *val = (const LV2_Atom_Bool *)&prop->value;
+
+ assert(val->atom.type == forge.Bool);
+ assert(val->atom.size == sizeof(int32_t));
+ assert(val->body == true);
+
+ nprops |= 0x4;
+ }
+ else
+ {
+ assert(false);
+ }
+ }
+ assert(nprops == 0x7);
+
+ assert(props_advance(props, &forge, ev->time.frames, obj, &ref) == 1);
+
+ assert(state->b32 == true);
+ assert(stash->b32 == true);
+
+ nevs |= 0x1;
+ }
+ assert(nevs == 0x1);
+
+ assert(ser_atom_deinit(&ser) == 0);
+}
+
+static const test_t tests [] = {
+ _test_1,
+ _test_2,
+ NULL
+};
+
+int
+main(int argc __attribute__((unused)), char **argv __attribute__((unused)))
+{
+ static handle_t handle;
+
+ for(const test_t *test = tests; *test; test++)
+ {
+ memset(&handle, 0, sizeof(handle));
+
+ handle.map.handle = &handle;
+ handle.map.map = _map;
+
+ assert(props_init(&handle.props, PROPS_PREFIX"subj", defs, MAX_NPROPS,
+ &handle.state, &handle.stash, &handle.map, NULL) == 1);
+
+ (*test)(&handle);
+ }
+
+ for(urid_t *itm=handle.urids; itm->urid; itm++)
+ {
+ free(itm->uri);
+ }
+
+ return 0;
+}
diff --git a/screenshots/screenshot_1.png b/screenshots/screenshot_1.png
index 461d52a..898baf6 100644
--- a/screenshots/screenshot_1.png
+++ b/screenshots/screenshot_1.png
Binary files differ
diff --git a/ser_atom.lv2/.gitlab-ci.yml b/ser_atom.lv2/.gitlab-ci.yml
new file mode 100644
index 0000000..4f9e16f
--- /dev/null
+++ b/ser_atom.lv2/.gitlab-ci.yml
@@ -0,0 +1,63 @@
+stages:
+ - test
+
+.variables_template: &variables_definition
+ variables:
+ BASE_NAME: "ser_atom.lv2"
+ PKG_CONFIG_PATH: "/opt/lv2/lib/pkgconfig:/opt/${CI_BUILD_NAME}/lib/pkgconfig:/usr/lib/${CI_BUILD_NAME}/pkgconfig"
+
+.common_template: &common_definition
+ <<: *variables_definition
+ stage: test
+
+.build_template: &build_definition
+ <<: *common_definition
+ script:
+ - meson --cross-file "${CI_BUILD_NAME}" build
+ - ninja -C build
+
+.test_template: &test_definition
+ <<: *common_definition
+ script:
+ - meson --cross-file "${CI_BUILD_NAME}" build
+ - ninja -C build
+ - cd build
+ - meson test --verbose --wrap "${CI_BUILD_NAME}.wrap"
+
+.universal_linux_template: &universal_linux_definition
+ image: ventosus/universal-linux-gnu
+ <<: *test_definition
+
+.arm_linux_template: &arm_linux_definition
+ image: ventosus/arm-linux-gnueabihf
+ <<: *test_definition
+
+.universal_w64_template: &universal_w64_definition
+ image: ventosus/universal-w64-mingw32
+ <<: *test_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
+
+aarch64-linux-gnu:
+ <<: *arm_linux_definition
+
+x86_64-w64-mingw32:
+ <<: *universal_w64_definition
+
+i686-w64-mingw32:
+ <<: *universal_w64_definition
+
+universal-apple-darwin:
+ <<: *universal_apple_definition
diff --git a/ser_atom.lv2/COPYING b/ser_atom.lv2/COPYING
new file mode 100644
index 0000000..ddb9a46
--- /dev/null
+++ b/ser_atom.lv2/COPYING
@@ -0,0 +1,201 @@
+ 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/ser_atom.lv2/README.md b/ser_atom.lv2/README.md
new file mode 100644
index 0000000..00e0e03
--- /dev/null
+++ b/ser_atom.lv2/README.md
@@ -0,0 +1,33 @@
+# ser_atom.lv2
+
+## General purpose LV2 atom forge serializer
+
+### Build Status
+
+[![build status](https://gitlab.com/OpenMusicKontrollers/ser_atom.lv2/badges/master/build.svg)](https://gitlab.com/OpenMusicKontrollers/ser_atom.lv2/commits/master)
+
+### Build / test
+
+ git clone https://git.open-music-kontrollers.ch/lv2/ser_atom.lv2
+ cd ser_atom.lv2
+ meson build
+ cd build
+ ninja -j4
+ ninja test
+
+### License
+
+Copyright (c) 2018 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/ser_atom.lv2/VERSION b/ser_atom.lv2/VERSION
new file mode 100644
index 0000000..1a03094
--- /dev/null
+++ b/ser_atom.lv2/VERSION
@@ -0,0 +1 @@
+0.1.9
diff --git a/ser_atom.lv2/meson.build b/ser_atom.lv2/meson.build
new file mode 100644
index 0000000..4360ca5
--- /dev/null
+++ b/ser_atom.lv2/meson.build
@@ -0,0 +1,26 @@
+project('ser_atom.lv2', 'c', default_options : [
+ 'buildtype=release',
+ 'warning_level=3',
+ 'werror=true',
+ 'b_lto=false',
+ 'c_std=c11'])
+
+version = run_command('cat', 'VERSION').stdout().strip()
+
+add_project_arguments('-D_GNU_SOURCE', language : 'c')
+
+conf_data = configuration_data()
+cc = meson.get_compiler('c')
+
+lv2_dep = dependency('lv2')
+deps = [lv2_dep]
+
+c_args = []
+
+ser_atom_test = executable('ser_atom_test',
+ join_paths('test', 'ser_atom_test.c'),
+ c_args : c_args,
+ dependencies : deps,
+ install : false)
+
+test('Test', ser_atom_test)
diff --git a/ser_atom.lv2/ser_atom.lv2/ser_atom.h b/ser_atom.lv2/ser_atom.lv2/ser_atom.h
new file mode 100644
index 0000000..7357e65
--- /dev/null
+++ b/ser_atom.lv2/ser_atom.lv2/ser_atom.h
@@ -0,0 +1,215 @@
+/*
+ * Copyright (c) 2018 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.
+ */
+
+#ifndef _SER_ATOM_H
+#define _SER_ATOM_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdatomic.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <lv2/lv2plug.in/ns/ext/atom/forge.h>
+
+#ifndef SER_ATOM_API
+# define SER_ATOM_API static
+#endif
+
+typedef void *(*ser_atom_realloc_t)(void *data, void *buf, size_t size);
+typedef void (*ser_atom_free_t)(void *data, void *buf);
+
+typedef struct _ser_atom_t ser_atom_t;
+
+SER_ATOM_API int
+ser_atom_init(ser_atom_t *ser);
+
+SER_ATOM_API int
+ser_atom_funcs(ser_atom_t *ser, ser_atom_realloc_t realloc,
+ ser_atom_free_t free, void *data);
+
+SER_ATOM_API int
+ser_atom_reset(ser_atom_t *ser, LV2_Atom_Forge *forge);
+
+SER_ATOM_API LV2_Atom *
+ser_atom_get(ser_atom_t *ser);
+
+SER_ATOM_API int
+ser_atom_deinit(ser_atom_t *ser);
+
+#ifdef SER_ATOM_IMPLEMENTATION
+
+struct _ser_atom_t {
+ ser_atom_realloc_t realloc;
+ ser_atom_free_t free;
+ void *data;
+
+ size_t size;
+ size_t offset;
+ union {
+ uint8_t *buf;
+ LV2_Atom *atom;
+ };
+};
+
+static LV2_Atom_Forge_Ref
+_ser_atom_sink(LV2_Atom_Forge_Sink_Handle handle, const void *buf,
+ uint32_t size)
+{
+ ser_atom_t *ser = handle;
+ const size_t needed = ser->offset + size;
+
+ while(needed > ser->size)
+ {
+ const size_t augmented = ser->size
+ ? ser->size << 1
+ : 1024;
+ uint8_t *grown = ser->realloc(ser->data, ser->buf, augmented);
+ if(!grown) // out-of-memory
+ {
+ return 0;
+ }
+
+ ser->buf = grown;
+ ser->size = augmented;
+ }
+
+ const LV2_Atom_Forge_Ref ref = ser->offset + 1;
+ memcpy(&ser->buf[ser->offset], buf, size);
+ ser->offset += size;
+
+ return ref;
+}
+
+static LV2_Atom *
+_ser_atom_deref(LV2_Atom_Forge_Sink_Handle handle, LV2_Atom_Forge_Ref ref)
+{
+ ser_atom_t *ser = handle;
+
+ if(!ref) // invalid reference
+ {
+ return NULL;
+ }
+
+ const size_t offset = ref - 1;
+ return (LV2_Atom *)&ser->buf[offset];
+}
+
+static void *
+_ser_atom_realloc(void *data, void *buf, size_t size)
+{
+ (void)data;
+
+ return realloc(buf, size);
+}
+
+static void
+_ser_atom_free(void *data, void *buf)
+{
+ (void)data;
+
+ free(buf);
+}
+
+SER_ATOM_API int
+ser_atom_funcs(ser_atom_t *ser, ser_atom_realloc_t realloc,
+ ser_atom_free_t free, void *data)
+{
+ if(!ser || !realloc || !free || ser_atom_deinit(ser))
+ {
+ return -1;
+ }
+
+ ser->realloc = realloc;
+ ser->free = free;
+ ser->data = data;
+
+ return 0;
+}
+
+SER_ATOM_API int
+ser_atom_init(ser_atom_t *ser)
+{
+ if(!ser)
+ {
+ return -1;
+ }
+
+ ser->size = 0;
+ ser->offset = 0;
+ ser->buf = NULL;
+
+ return ser_atom_funcs(ser, _ser_atom_realloc, _ser_atom_free, NULL);
+}
+
+SER_ATOM_API int
+ser_atom_reset(ser_atom_t *ser, LV2_Atom_Forge *forge)
+{
+ if(!ser || !forge)
+ {
+ return -1;
+ }
+
+ lv2_atom_forge_set_sink(forge, _ser_atom_sink, _ser_atom_deref, ser);
+
+ ser->offset = 0;
+
+ return 0;
+}
+
+SER_ATOM_API LV2_Atom *
+ser_atom_get(ser_atom_t *ser)
+{
+ if(!ser)
+ {
+ return NULL;
+ }
+
+ return ser->atom;
+}
+
+SER_ATOM_API int
+ser_atom_deinit(ser_atom_t *ser)
+{
+ if(!ser)
+ {
+ return -1;
+ }
+
+ if(ser->buf)
+ {
+ ser->free(ser->data, ser->buf);
+ }
+
+ ser->size = 0;
+ ser->offset = 0;
+ ser->buf = NULL;
+
+ return 0;
+}
+
+#endif // SER_ATOM_IMPLEMENTATION
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif //_SER_ATOM_H
diff --git a/ser_atom.lv2/test/ser_atom_test.c b/ser_atom.lv2/test/ser_atom_test.c
new file mode 100644
index 0000000..00898f0
--- /dev/null
+++ b/ser_atom.lv2/test/ser_atom_test.c
@@ -0,0 +1,136 @@
+/*
+ * Copyright (c) 2018 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 <assert.h>
+#include <inttypes.h>
+#include <stdio.h>
+
+#define SER_ATOM_IMPLEMENTATION
+#include <ser_atom.lv2/ser_atom.h>
+
+#define MAX_ITEMS 0x100000
+
+static uint32_t
+_map(void *data, const char *uri)
+{
+ (void)uri;
+
+ uint32_t *urid = data;
+
+ return ++(*urid);
+}
+
+static void *
+_realloc(void *data, void *buf, size_t size)
+{
+ (void)data;
+ (void)buf;
+ (void)size;
+
+ return NULL;
+}
+
+static void
+_free(void *data, void *buf)
+{
+ (void)data;
+ (void)buf;
+}
+
+int
+main(int argc, char **argv)
+{
+ (void)argc;
+ (void)argv;
+
+ ser_atom_t ser;
+ LV2_Atom_Forge forge;
+
+ uint32_t urid = 0;
+ LV2_URID_Map map = {
+ .handle = &urid,
+ .map = _map
+ };
+
+ lv2_atom_forge_init(&forge, &map);
+
+ assert(ser_atom_init(NULL) != 0);
+ assert(ser_atom_init(&ser) == 0);
+
+ assert(ser.size == 0);
+ assert(ser.offset == 0);
+ assert(ser.buf == NULL);
+
+ assert(ser_atom_funcs(NULL, NULL, NULL, NULL) != 0);
+ assert(ser_atom_funcs(&ser, _realloc, _free, NULL) == 0);
+ assert(ser_atom_reset(&ser, &forge) == 0);
+ assert(lv2_atom_forge_bool(&forge, true) == 0);
+ _free(NULL, NULL);
+
+ assert(ser_atom_init(&ser) == 0);
+
+ assert(ser.realloc == _ser_atom_realloc);
+ assert(ser.free == _ser_atom_free);
+ assert(ser.data == NULL);
+
+ assert(ser.size == 0);
+ assert(ser.offset == 0);
+ assert(ser.buf == NULL);
+
+ assert(ser_atom_reset(NULL, &forge) != 0);
+ assert(ser_atom_reset(&ser, NULL) != 0);
+ assert(ser_atom_reset(NULL, NULL) != 0);
+ assert(ser_atom_reset(&ser, &forge) == 0);
+
+ LV2_Atom_Forge_Frame frame;
+ int64_t i;
+
+ assert(lv2_atom_forge_tuple(&forge, &frame) != 0);
+ for(i = 0; i < MAX_ITEMS; i++)
+ {
+ assert(lv2_atom_forge_long(&forge, i) != 0);
+ }
+ lv2_atom_forge_pop(&forge, &frame);
+
+ i = 0;
+ LV2_ATOM_TUPLE_FOREACH((const LV2_Atom_Tuple *)ser.buf, atom)
+ {
+ assert(atom->size == sizeof(int64_t));
+ assert(atom->type == forge.Long);
+
+ const int64_t *i64 = LV2_ATOM_BODY_CONST(atom);
+ assert(*i64 == i++);
+ }
+
+ const size_t tot_size = MAX_ITEMS*sizeof(LV2_Atom_Long) + sizeof(LV2_Atom_Tuple);
+ assert(ser.offset == tot_size);
+ assert(ser.size >= tot_size);
+
+ assert(lv2_atom_forge_deref(&forge, 0) == NULL);
+
+ assert(ser_atom_get(NULL) == NULL);
+ assert(ser_atom_get(&ser) == ser.atom);
+
+ assert(ser_atom_deinit(NULL) != 0);
+ assert(ser_atom_deinit(&ser) == 0);
+
+ assert(ser.size == 0);
+ assert(ser.offset == 0);
+ assert(ser.buf == NULL);
+
+ return 0;
+}
diff --git a/shells_bells.c b/shells_bells.c
new file mode 100644
index 0000000..6643981
--- /dev/null
+++ b/shells_bells.c
@@ -0,0 +1,356 @@
+/*
+ * Copyright (c) 2019-2021 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 <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <math.h>
+#include <inttypes.h>
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+#include <limits.h>
+#include <pthread.h>
+#include <inttypes.h>
+
+#include <shells_bells.h>
+
+typedef struct _plughandle_t plughandle_t;
+
+struct _plughandle_t {
+ LV2_URID_Map *map;
+ LV2_Atom_Forge forge;
+ LV2_Atom_Forge_Ref ref;
+
+ LV2_Log_Log *log;
+ LV2_Log_Logger logger;
+
+ plugstate_t state;
+ plugstate_t stash;
+
+ const LV2_Atom_Sequence *control;
+ LV2_Atom_Sequence *notify;
+
+ PROPS_T(props, MAX_NPROPS);
+
+ LV2_URID midi_MidiEvent;
+
+ int64_t rate;
+ int64_t last_remaining;
+ uint8_t last_channel;
+ uint8_t last_note;
+};
+
+static void
+_send_midi(plughandle_t *handle, int64_t frames, uint32_t len, const uint8_t *msg)
+{
+ if(handle->ref)
+ {
+ handle->ref = lv2_atom_forge_frame_time(&handle->forge, frames);
+ }
+
+ if(handle->ref)
+ {
+ handle->ref = lv2_atom_forge_atom(&handle->forge, len, handle->midi_MidiEvent);
+ }
+
+ if(handle->ref)
+ {
+ handle->ref = lv2_atom_forge_write(&handle->forge, msg, len);
+ }
+}
+
+static void
+_disable_bell(plughandle_t *handle, int64_t frames)
+{
+ if(handle->last_remaining <= 0)
+ {
+ return;
+ }
+
+ const uint8_t msg [3] = {
+ LV2_MIDI_MSG_NOTE_OFF | handle->last_channel,
+ handle->last_note,
+ 0x0
+ };
+
+ _send_midi(handle, frames, sizeof(msg), msg);
+
+ handle->last_channel = 0;
+ handle->last_note = 0;
+ handle->last_remaining = 0;
+}
+
+static void
+_enable_bell(plughandle_t *handle, int64_t frames)
+{
+ _disable_bell(handle, frames);
+
+ const uint8_t msg [3] = {
+ LV2_MIDI_MSG_NOTE_ON | handle->state.channel,
+ handle->state.note,
+ handle->state.velocity
+ };
+
+ _send_midi(handle, frames, sizeof(msg), msg);
+
+ handle->last_channel = handle->state.channel;
+ handle->last_note = handle->state.note;
+ handle->last_remaining = handle->rate / 1000 * handle->state.duration;
+}
+
+static void
+_intercept_trigger(void *data, int64_t frames,
+ props_impl_t *impl __attribute__((unused)))
+{
+ plughandle_t *handle = data;
+
+ if(handle->state.trigger)
+ {
+ _enable_bell(handle, frames);
+ }
+ else
+ {
+ _disable_bell(handle, frames);
+ }
+
+ handle->state.trigger = false;
+}
+
+static const props_def_t defs [MAX_NPROPS] = {
+ {
+ .property = SHELLS_BELLS__channel,
+ .offset = offsetof(plugstate_t, channel),
+ .type = LV2_ATOM__Int
+ },
+ {
+ .property = SHELLS_BELLS__note,
+ .offset = offsetof(plugstate_t, note),
+ .type = LV2_ATOM__Int
+ },
+ {
+ .property = SHELLS_BELLS__velocity,
+ .offset = offsetof(plugstate_t, velocity),
+ .type = LV2_ATOM__Int
+ },
+ {
+ .property = SHELLS_BELLS__duration,
+ .offset = offsetof(plugstate_t, duration),
+ .type = LV2_ATOM__Int
+ },
+ {
+ .property = SHELLS_BELLS__trigger,
+ .offset = offsetof(plugstate_t, trigger),
+ .type = LV2_ATOM__Bool,
+ .event_cb = _intercept_trigger,
+ },
+ {
+ .property = SHELLS_BELLS__fontHeight,
+ .offset = offsetof(plugstate_t, font_height),
+ .type = LV2_ATOM__Int
+ }
+};
+
+static LV2_Handle
+instantiate(const LV2_Descriptor* descriptor, double rate,
+ const char *bundle_path __attribute__((unused)),
+ const LV2_Feature *const *features)
+{
+ plughandle_t *handle = calloc(1, sizeof(plughandle_t));
+ if(!handle)
+ {
+ return NULL;
+ }
+ mlock(handle, sizeof(plughandle_t));
+
+ handle->rate = rate;
+
+ 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_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);
+ }
+
+ lv2_atom_forge_init(&handle->forge, handle->map);
+
+ handle->midi_MidiEvent = handle->map->map(handle->map->handle,
+ LV2_MIDI__MidiEvent);
+
+ if(!props_init(&handle->props, descriptor->URI,
+ defs, MAX_NPROPS, &handle->state, &handle->stash,
+ handle->map, handle))
+ {
+ fprintf(stderr, "failed to initialize property structure\n");
+ free(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->control = (const LV2_Atom_Sequence *)data;
+ break;
+ case 1:
+ handle->notify = (LV2_Atom_Sequence *)data;
+ break;
+
+ default:
+ break;
+ }
+}
+
+static void
+run(LV2_Handle instance, uint32_t nsamples)
+{
+ plughandle_t *handle = instance;
+
+ const uint32_t capacity = handle->notify->atom.size;
+ LV2_Atom_Forge_Frame frame;
+ lv2_atom_forge_set_buffer(&handle->forge, (uint8_t *)handle->notify, capacity);
+ handle->ref = lv2_atom_forge_sequence_head(&handle->forge, &frame, 0);
+
+ props_idle(&handle->props, &handle->forge, 0, &handle->ref);
+
+ LV2_ATOM_SEQUENCE_FOREACH(handle->control, ev)
+ {
+ const LV2_Atom_Object *obj = (const LV2_Atom_Object *)&ev->body;
+
+ props_advance(&handle->props, &handle->forge, 0, obj, &handle->ref);
+ }
+
+ if(handle->last_remaining > 0)
+ {
+ if(handle->last_remaining <= nsamples)
+ {
+ _disable_bell(handle, nsamples - 1);
+ }
+ else
+ {
+ handle->last_remaining -= nsamples;
+ }
+ }
+
+ if(handle->ref)
+ {
+ lv2_atom_forge_pop(&handle->forge, &frame);
+ }
+ else
+ {
+ lv2_atom_sequence_clear(handle->notify);
+
+ if(handle->log)
+ {
+ lv2_log_trace(&handle->logger, "forge buffer overflow\n");
+ }
+ }
+}
+
+static void
+cleanup(LV2_Handle instance)
+{
+ plughandle_t *handle = instance;
+
+ free(handle);
+}
+
+static LV2_State_Status
+_state_save(LV2_Handle instance, LV2_State_Store_Function store,
+ LV2_State_Handle state, uint32_t flags,
+ const LV2_Feature *const *features)
+{
+ plughandle_t *handle = instance;
+
+ return props_save(&handle->props, store, state, flags, features);
+}
+
+static LV2_State_Status
+_state_restore(LV2_Handle instance, LV2_State_Retrieve_Function retrieve,
+ LV2_State_Handle state, uint32_t flags,
+ const LV2_Feature *const *features)
+{
+ plughandle_t *handle = instance;
+
+ return props_restore(&handle->props, retrieve, state, flags, features);
+}
+
+static const LV2_State_Interface state_iface = {
+ .save = _state_save,
+ .restore = _state_restore
+};
+
+
+static const void*
+extension_data(const char* uri)
+{
+ if(!strcmp(uri, LV2_STATE__interface))
+ {
+ return &state_iface;
+ }
+
+ return NULL;
+}
+
+static const LV2_Descriptor shells_bells_bells= {
+ .URI = SHELLS_BELLS__bells,
+ .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 &shells_bells_bells;
+
+ default:
+ return NULL;
+ }
+}
diff --git a/shells_bells.h b/shells_bells.h
new file mode 100644
index 0000000..1a81da6
--- /dev/null
+++ b/shells_bells.h
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2019-2021 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.
+ */
+
+#ifndef _SHELLS_BELLS_LV2_H
+#define _SHELLS_BELLS_LV2_H
+
+#include <stdint.h>
+#if !defined(_WIN32)
+# include <sys/mman.h>
+#else
+# define mlock(...)
+# define munlock(...)
+#endif
+
+#include <lv2/lv2plug.in/ns/ext/atom/atom.h>
+#include <lv2/lv2plug.in/ns/ext/atom/forge.h>
+#include <lv2/lv2plug.in/ns/ext/urid/urid.h>
+#include <lv2/lv2plug.in/ns/ext/midi/midi.h>
+#include <lv2/lv2plug.in/ns/ext/state/state.h>
+#include <lv2/lv2plug.in/ns/ext/log/log.h>
+#include <lv2/lv2plug.in/ns/ext/log/logger.h>
+#include <lv2/lv2plug.in/ns/ext/options/options.h>
+#include <lv2/lv2plug.in/ns/extensions/ui/ui.h>
+#include <lv2/lv2plug.in/ns/lv2core/lv2.h>
+
+#include <props.h>
+
+#define SHELLS_BELLS_URI "http://open-music-kontrollers.ch/lv2/shells_bells"
+#define SHELLS_BELLS_PREFIX SHELLS_BELLS_URI "#"
+
+// plugin uris
+#define SHELLS_BELLS__bells SHELLS_BELLS_PREFIX "bells"
+
+// plugin UI uris
+#define SHELLS_BELLS__ui SHELLS_BELLS_PREFIX "ui"
+
+// plugin properties
+#define SHELLS_BELLS__channel SHELLS_BELLS_PREFIX "channel"
+#define SHELLS_BELLS__note SHELLS_BELLS_PREFIX "note"
+#define SHELLS_BELLS__velocity SHELLS_BELLS_PREFIX "velocity"
+#define SHELLS_BELLS__duration SHELLS_BELLS_PREFIX "duration"
+#define SHELLS_BELLS__trigger SHELLS_BELLS_PREFIX "trigger"
+#define SHELLS_BELLS__fontHeight SHELLS_BELLS_PREFIX "fontHeight"
+
+#define MAX_NPROPS 6
+
+typedef struct _plugstate_t plugstate_t;
+
+struct _plugstate_t {
+ int32_t channel;
+ int32_t note;
+ int32_t velocity;
+ int32_t duration;
+ int32_t trigger;
+ int32_t font_height;
+};
+
+#endif // _SHELLS_BELLS_LV2_H
diff --git a/shells_bells.ttl.in b/shells_bells.ttl.in
new file mode 100644
index 0000000..0b4be30
--- /dev/null
+++ b/shells_bells.ttl.in
@@ -0,0 +1,140 @@
+# Copyright (c) 2019-2021 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 xsd: <http://www.w3.org/2001/XMLSchema#> .
+@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 midi: <http://lv2plug.in/ns/ext/midi#> .
+@prefix urid: <http://lv2plug.in/ns/ext/urid#> .
+@prefix units: <http://lv2plug.in/ns/extensions/units#> .
+@prefix state: <http://lv2plug.in/ns/ext/state#> .
+@prefix rsz: <http://lv2plug.in/ns/ext/resize-port#> .
+@prefix bufsz: <http://lv2plug.in/ns/ext/buf-size#> .
+@prefix patch: <http://lv2plug.in/ns/ext/patch#> .
+@prefix log: <http://lv2plug.in/ns/ext/log#> .
+
+@prefix omk: <http://open-music-kontrollers.ch/ventosus#> .
+@prefix proj: <http://open-music-kontrollers.ch/lv2/> .
+@prefix shells_bells: <http://open-music-kontrollers.ch/lv2/shells_bells#> .
+
+# 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:shells_bells
+ a doap:Project ;
+ doap:maintainer omk:me ;
+ doap:name "Mephisto Bundle" .
+
+# Units
+shells_bells:px
+ a units:Unit ;
+ rdfs:label "pixels" ;
+ units:render "%f px" ;
+ units:symbol "px" .
+
+shells_bells:channel
+ a lv2:Parameter ;
+ rdfs:range atom:Int ;
+ rdfs:label "Channel" ;
+ rdfs:comment "get/set MIDI channel" ;
+ lv2:minimum 0 ;
+ lv2:maximum 15 .
+shells_bells:note
+ a lv2:Parameter ;
+ rdfs:range atom:Int ;
+ rdfs:label "Note" ;
+ rdfs:comment "get/set MIDI note" ;
+ lv2:minimum 0 ;
+ lv2:maximum 127 .
+shells_bells:velocity
+ a lv2:Parameter ;
+ rdfs:range atom:Int ;
+ rdfs:label "Velocity" ;
+ rdfs:comment "get/set MIDI velocity" ;
+ lv2:minimum 0 ;
+ lv2:maximum 127 .
+shells_bells:duration
+ a lv2:Parameter ;
+ rdfs:range atom:Int ;
+ rdfs:label "Duration" ;
+ rdfs:comment "get/set MIDI duration" ;
+ lv2:minimum 0 ;
+ lv2:maximum 10000 ;
+ units:unit units:ms .
+shells_bells:fontHeight
+ a lv2:Parameter ;
+ rdfs:range atom:Int ;
+ rdfs:label "Font height" ;
+ rdfs:comment "get/set font height in px" ;
+ lv2:minimum 10 ;
+ lv2:maximum 25 ;
+ units:unit shells_bells:px .
+
+shells_bells:bells
+ a lv2:Plugin ,
+ lv2:GeneratorPlugin ;
+ doap:name "SHells Bells" ;
+ doap:license <https://spdx.org/licenses/Artistic-2.0> ;
+ lv2:project proj:shells_bells ;
+ lv2:requiredFeature urid:map, state:loadDefaultState ;
+ lv2:optionalFeature lv2:isLive, lv2:hardRTCapable, state:threadSafeRestore, log:log ;
+ lv2:extensionData state:interface ;
+
+ lv2:port [
+ a lv2:InputPort ,
+ atom:AtomPort ;
+ atom:bufferType atom:Sequence ;
+ atom:supports midi:MidiEvent ,
+ 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 midi:MidiEvent ,
+ patch:Message ;
+ lv2:index 1 ;
+ lv2:symbol "notify" ;
+ lv2:name "Notify" ;
+ lv2:designation lv2:control ;
+ ] ;
+
+ patch:writable
+ shells_bells:channel ,
+ shells_bells:note ,
+ shells_bells:velocity ,
+ shells_bells:fontHeight ,
+ shells_bells:duration ;
+
+ state:state [
+ shells_bells:channel "0"^^xsd:int ;
+ shells_bells:note "60"^^xsd:int ;
+ shells_bells:velocity "127"^^xsd:int ;
+ shells_bells:fontHeight "16"^^xsd:int ;
+ shells_bells:duration "1000"^^xsd:int ;
+ ] .
diff --git a/shells_bells_ui.c b/shells_bells_ui.c
new file mode 100644
index 0000000..f87da10
--- /dev/null
+++ b/shells_bells_ui.c
@@ -0,0 +1,754 @@
+/*
+ * Copyright (c) 2019-2021 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 <inttypes.h>
+#include <stdio.h>
+#include <math.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <time.h>
+#include <utime.h>
+#include <limits.h>
+#include <wordexp.h>
+
+#include <shells_bells.h>
+#include <props.h>
+
+#define SER_ATOM_IMPLEMENTATION
+#include <ser_atom.lv2/ser_atom.h>
+
+#include <d2tk/hash.h>
+#include <d2tk/frontend_pugl.h>
+
+#define GLYPH_W 7
+#define GLYPH_H (GLYPH_W * 2)
+
+#define FPS 25
+
+#define DEFAULT_FG 0xddddddff
+#define DEFAULT_BG 0x222222ff
+
+#define NROWS_MAX 512
+#define NCOLS_MAX 512
+
+#define MAX(x, y) (x > y ? y : x)
+
+typedef struct _plughandle_t plughandle_t;
+
+struct _plughandle_t {
+ LV2_URID_Map *map;
+ LV2_Atom_Forge forge;
+
+ LV2_Log_Log *log;
+ LV2_Log_Logger logger;
+
+ d2tk_pugl_config_t config;
+ d2tk_frontend_t *dpugl;
+
+ LV2UI_Controller *controller;
+ LV2UI_Write_Function writer;
+
+ PROPS_T(props, MAX_NPROPS);
+
+ plugstate_t state;
+ plugstate_t stash;
+
+ LV2_URID atom_eventTransfer;
+ LV2_URID midi_MidiEvent;
+ LV2_URID urid_channel;
+ LV2_URID urid_note;
+ LV2_URID urid_velocity;
+ LV2_URID urid_duration;
+ LV2_URID urid_trigger;
+ LV2_URID urid_fontHeight;
+
+ bool reinit;
+
+ float scale;
+ d2tk_coord_t header_height;
+ d2tk_coord_t footer_height;
+ d2tk_coord_t sidebar_width;
+ d2tk_coord_t font_height;
+
+ uint32_t max_red;
+
+ int done;
+
+ wordexp_t wordexp;
+};
+
+static inline void
+_update_font_height(plughandle_t *handle)
+{
+ handle->font_height = handle->state.font_height * handle->scale;
+}
+
+static void
+_intercept_font_height(void *data, int64_t frames __attribute__((unused)),
+ props_impl_t *impl __attribute__((unused)))
+{
+ plughandle_t *handle = data;
+
+ _update_font_height(handle);
+}
+
+static const props_def_t defs [MAX_NPROPS] = {
+ {
+ .property = SHELLS_BELLS__channel,
+ .offset = offsetof(plugstate_t, channel),
+ .type = LV2_ATOM__Int
+ },
+ {
+ .property = SHELLS_BELLS__note,
+ .offset = offsetof(plugstate_t, note),
+ .type = LV2_ATOM__Int
+ },
+ {
+ .property = SHELLS_BELLS__velocity,
+ .offset = offsetof(plugstate_t, velocity),
+ .type = LV2_ATOM__Int
+ },
+ {
+ .property = SHELLS_BELLS__duration,
+ .offset = offsetof(plugstate_t, duration),
+ .type = LV2_ATOM__Int
+ },
+ {
+ .property = SHELLS_BELLS__trigger,
+ .offset = offsetof(plugstate_t, trigger),
+ .type = LV2_ATOM__Bool
+ },
+ {
+ .property = SHELLS_BELLS__fontHeight,
+ .offset = offsetof(plugstate_t, font_height),
+ .type = LV2_ATOM__Int,
+ .event_cb = _intercept_font_height
+ }
+};
+
+static void
+_message_set_key(plughandle_t *handle, LV2_URID key)
+{
+ ser_atom_t ser;
+ props_impl_t *impl = _props_impl_get(&handle->props, key);
+ if(!impl)
+ {
+ return;
+ }
+
+ ser_atom_init(&ser);
+ ser_atom_reset(&ser, &handle->forge);
+
+ LV2_Atom_Forge_Ref ref = 1;
+
+ props_set(&handle->props, &handle->forge, 0, key, &ref);
+
+ const LV2_Atom_Event *ev = (const LV2_Atom_Event *)ser_atom_get(&ser);
+ const LV2_Atom *atom = &ev->body;
+ handle->writer(handle->controller, 0, lv2_atom_total_size(atom),
+ handle->atom_eventTransfer, atom);
+
+ ser_atom_deinit(&ser);
+}
+
+static void
+_message_get(plughandle_t *handle, LV2_URID key)
+{
+ ser_atom_t ser;
+ props_impl_t *impl = _props_impl_get(&handle->props, key);
+ if(!impl)
+ {
+ return;
+ }
+
+ ser_atom_init(&ser);
+ ser_atom_reset(&ser, &handle->forge);
+
+ LV2_Atom_Forge_Ref ref = 1;
+
+ props_get(&handle->props, &handle->forge, 0, key, &ref);
+
+ const LV2_Atom_Event *ev = (const LV2_Atom_Event *)ser_atom_get(&ser);
+ const LV2_Atom *atom = &ev->body;
+ handle->writer(handle->controller, 0, lv2_atom_total_size(atom),
+ handle->atom_eventTransfer, atom);
+
+ ser_atom_deinit(&ser);
+}
+
+static inline void
+_expose_header(plughandle_t *handle, const d2tk_rect_t *rect)
+{
+ d2tk_frontend_t *dpugl = handle->dpugl;
+ d2tk_base_t *base = d2tk_frontend_get_base(dpugl);
+
+ const d2tk_coord_t frac [3] = { 1, 1, 1 };
+ D2TK_BASE_LAYOUT(rect, 3, frac, D2TK_FLAG_LAYOUT_X_REL, lay)
+ {
+ const unsigned k = d2tk_layout_get_index(lay);
+ const d2tk_rect_t *lrect = d2tk_layout_get_rect(lay);
+
+ switch(k)
+ {
+ case 0:
+ {
+ d2tk_base_label(base, -1, "Open•Music•Kontrollers", 0.5f, lrect,
+ D2TK_ALIGN_LEFT | D2TK_ALIGN_TOP);
+ } break;
+ case 1:
+ {
+ d2tk_base_label(base, -1, "SHells•Bells", 1.f, lrect,
+ D2TK_ALIGN_CENTER | D2TK_ALIGN_TOP);
+ } break;
+ case 2:
+ {
+ d2tk_base_label(base, -1, "Version "SHELLS_BELLS_VERSION, 0.5f, lrect,
+ D2TK_ALIGN_RIGHT | D2TK_ALIGN_TOP);
+ } break;
+ }
+ }
+}
+
+static inline void
+_expose_channel(plughandle_t *handle, const d2tk_rect_t *rect)
+{
+ d2tk_frontend_t *dpugl = handle->dpugl;
+ d2tk_base_t *base = d2tk_frontend_get_base(dpugl);
+
+ static const char lbl [] = "channel";
+
+ if(d2tk_base_spinner_int32_is_changed(base, D2TK_ID, rect,
+ sizeof(lbl), lbl, 0x0, &handle->state.channel, 0xf, D2TK_FLAG_NONE))
+ {
+ _message_set_key(handle, handle->urid_channel);
+ }
+}
+
+static inline void
+_expose_note(plughandle_t *handle, const d2tk_rect_t *rect)
+{
+ d2tk_frontend_t *dpugl = handle->dpugl;
+ d2tk_base_t *base = d2tk_frontend_get_base(dpugl);
+
+ static const char lbl [] = "note";
+
+ if(d2tk_base_spinner_int32_is_changed(base, D2TK_ID, rect,
+ sizeof(lbl), lbl, 0x0, &handle->state.note, 0x7f, D2TK_FLAG_NONE))
+ {
+ _message_set_key(handle, handle->urid_note);
+ }
+}
+
+static inline void
+_expose_velocity(plughandle_t *handle, const d2tk_rect_t *rect)
+{
+ d2tk_frontend_t *dpugl = handle->dpugl;
+ d2tk_base_t *base = d2tk_frontend_get_base(dpugl);
+
+ static const char lbl [] = "velocity";
+
+ if(d2tk_base_spinner_int32_is_changed(base, D2TK_ID, rect,
+ sizeof(lbl), lbl, 0x0, &handle->state.velocity, 0x7f, D2TK_FLAG_NONE))
+ {
+ _message_set_key(handle, handle->urid_velocity);
+ }
+}
+
+static inline void
+_expose_font_height(plughandle_t *handle, const d2tk_rect_t *rect)
+{
+ d2tk_frontend_t *dpugl = handle->dpugl;
+ d2tk_base_t *base = d2tk_frontend_get_base(dpugl);
+
+ static const char lbl [] = "font-height•px";
+
+ if(d2tk_base_spinner_int32_is_changed(base, D2TK_ID, rect,
+ sizeof(lbl), lbl, 10, &handle->state.font_height, 25, D2TK_FLAG_NONE))
+ {
+ _message_set_key(handle, handle->urid_fontHeight);
+ _update_font_height(handle);
+ }
+}
+
+static inline void
+_expose_duration(plughandle_t *handle, const d2tk_rect_t *rect)
+{
+ d2tk_frontend_t *dpugl = handle->dpugl;
+ d2tk_base_t *base = d2tk_frontend_get_base(dpugl);
+
+ static const char lbl [] = "duration•ms";
+
+ if(d2tk_base_spinner_int32_is_changed(base, D2TK_ID, rect,
+ sizeof(lbl), lbl, 0, &handle->state.duration, 10000, D2TK_FLAG_NONE))
+ {
+ _message_set_key(handle, handle->urid_duration);
+ }
+}
+
+static inline void
+_expose_footer(plughandle_t *handle, const d2tk_rect_t *rect)
+{
+ D2TK_BASE_TABLE(rect, 5, 1, D2TK_FLAG_TABLE_REL, tab)
+ {
+ const unsigned x = d2tk_table_get_index_x(tab);
+ const d2tk_rect_t *trect = d2tk_table_get_rect(tab);
+
+ switch(x)
+ {
+ case 0:
+ {
+ _expose_channel(handle, trect);
+ } break;
+ case 1:
+ {
+ _expose_note(handle, trect);
+ } break;
+ case 2:
+ {
+ _expose_velocity(handle, trect);
+ } break;
+ case 3:
+ {
+ _expose_duration(handle, trect);
+ } break;
+ case 4:
+ {
+ _expose_font_height(handle, trect);
+ } break;
+ }
+ }
+}
+
+static inline void
+_expose_term(plughandle_t *handle, const d2tk_rect_t *rect)
+{
+ d2tk_frontend_t *dpugl = handle->dpugl;
+ d2tk_base_t *base = d2tk_frontend_get_base(dpugl);
+
+ char **args = handle->wordexp.we_wordv;
+
+ d2tk_flag_t flag = D2TK_FLAG_PTY_NOMOUSE;
+ if(handle->reinit)
+ {
+ flag |= D2TK_FLAG_PTY_REINIT;
+ }
+
+ D2TK_BASE_PTY(base, D2TK_ID, NULL, args, handle->font_height, rect, flag, pty)
+ {
+ const d2tk_state_t state = d2tk_pty_get_state(pty);
+ const uint32_t max_red = d2tk_pty_get_max_green(pty);
+
+ if(max_red != handle->max_red)
+ {
+ handle->max_red = max_red;
+ d2tk_frontend_redisplay(handle->dpugl);
+ }
+
+ if(d2tk_state_is_close(state))
+ {
+ handle->done = 1;
+ }
+
+ if(d2tk_state_is_bell(state))
+ {
+ handle->state.trigger = true;
+
+ _message_set_key(handle, handle->urid_trigger);
+ }
+
+ handle->reinit = false;
+ }
+}
+
+static inline void
+_expose_editor(plughandle_t *handle, const d2tk_rect_t *rect)
+{
+ const size_t err_len = 0;
+ const unsigned n = err_len > 0 ? 2 : 1;
+ const d2tk_coord_t frac [2] = { 2, 1 };
+ D2TK_BASE_LAYOUT(rect, n, frac, D2TK_FLAG_LAYOUT_Y_REL, lay)
+ {
+ const unsigned k = d2tk_layout_get_index(lay);
+ const d2tk_rect_t *lrect = d2tk_layout_get_rect(lay);
+
+ switch(k)
+ {
+ case 0:
+ {
+ _expose_term(handle, lrect);
+ } break;
+ case 1:
+ {
+ // nothing to do
+ } break;
+ }
+ }
+}
+
+static inline void
+_expose_sidebar_left(plughandle_t *handle, const d2tk_rect_t *rect)
+{
+ const d2tk_coord_t frac [2] = { 0, handle->footer_height };
+ D2TK_BASE_LAYOUT(rect, 2, frac, D2TK_FLAG_LAYOUT_Y_ABS, lay)
+ {
+ const unsigned k = d2tk_layout_get_index(lay);
+ const d2tk_rect_t *lrect = d2tk_layout_get_rect(lay);
+
+ switch(k)
+ {
+ case 0:
+ {
+ _expose_editor(handle, lrect);
+ } break;
+ case 1:
+ {
+ _expose_footer(handle, lrect);
+ } break;
+ }
+ }
+}
+
+static inline void
+_expose_body(plughandle_t *handle, const d2tk_rect_t *rect)
+{
+ const d2tk_coord_t frac [2] = { 0, handle->sidebar_width };
+ D2TK_BASE_LAYOUT(rect, 2, frac, D2TK_FLAG_LAYOUT_X_ABS, lay)
+ {
+ const unsigned k = d2tk_layout_get_index(lay);
+ const d2tk_rect_t *lrect = d2tk_layout_get_rect(lay);
+
+ switch(k)
+ {
+ case 0:
+ {
+ _expose_sidebar_left(handle, lrect);
+ } break;
+ case 1:
+ {
+ // nothing to do
+ } break;
+ }
+ }
+}
+
+static int
+_expose(void *data, d2tk_coord_t w, d2tk_coord_t h)
+{
+ plughandle_t *handle = data;
+ d2tk_base_t *base = d2tk_frontend_get_base(handle->dpugl);
+ const d2tk_rect_t rect = D2TK_RECT(0, 0, w, h);
+
+ const d2tk_style_t *old_style = d2tk_base_get_style(base);
+ d2tk_style_t style = *old_style;
+ const uint32_t light = handle->max_red;
+ const uint32_t dark = (light & ~0xff) | 0x7f;
+ style.fill_color[D2TK_TRIPLE_ACTIVE] = dark;
+ style.fill_color[D2TK_TRIPLE_ACTIVE_HOT] = light;
+ style.fill_color[D2TK_TRIPLE_ACTIVE_FOCUS] = dark;
+ style.fill_color[D2TK_TRIPLE_ACTIVE_HOT_FOCUS] = light;
+ style.text_stroke_color[D2TK_TRIPLE_HOT] = light;
+ style.text_stroke_color[D2TK_TRIPLE_HOT_FOCUS] = light;
+
+ d2tk_base_set_style(base, &style);
+
+ const d2tk_coord_t frac [2] = { handle->header_height, 0 };
+ D2TK_BASE_LAYOUT(&rect, 2, frac, D2TK_FLAG_LAYOUT_Y_ABS, lay)
+ {
+ const unsigned k = d2tk_layout_get_index(lay);
+ const d2tk_rect_t *lrect = d2tk_layout_get_rect(lay);
+
+ switch(k)
+ {
+ case 0:
+ {
+ _expose_header(handle, lrect);
+ } break;
+ case 1:
+ {
+ _expose_body(handle, lrect);
+ } break;
+ }
+ }
+
+ d2tk_base_set_style(base, old_style);
+
+ return 0;
+}
+
+static LV2UI_Handle
+instantiate(const LV2UI_Descriptor *descriptor,
+ const char *plugin_uri,
+ const char *bundle_path __attribute__((unused)),
+ 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;
+ LV2UI_Resize *host_resize = NULL;
+ LV2_Options_Option *opts = 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))
+ {
+ host_resize = features[i]->data;
+ }
+ else if(!strcmp(features[i]->URI, LV2_URID__map))
+ {
+ handle->map = features[i]->data;
+ }
+ else if(!strcmp(features[i]->URI, LV2_LOG__log))
+ {
+ handle->log = features[i]->data;
+ }
+ else if(!strcmp(features[i]->URI, LV2_OPTIONS__options))
+ {
+ opts = 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;
+ }
+
+ if(handle->log)
+ {
+ lv2_log_logger_init(&handle->logger, handle->map, handle->log);
+ }
+
+ lv2_atom_forge_init(&handle->forge, handle->map);
+
+ handle->atom_eventTransfer = handle->map->map(handle->map->handle,
+ LV2_ATOM__eventTransfer);
+ handle->midi_MidiEvent = handle->map->map(handle->map->handle,
+ LV2_MIDI__MidiEvent);
+ handle->urid_channel = handle->map->map(handle->map->handle,
+ SHELLS_BELLS__channel);
+ handle->urid_note = handle->map->map(handle->map->handle,
+ SHELLS_BELLS__note);
+ handle->urid_velocity = handle->map->map(handle->map->handle,
+ SHELLS_BELLS__velocity);
+ handle->urid_duration = handle->map->map(handle->map->handle,
+ SHELLS_BELLS__duration);
+ handle->urid_trigger = handle->map->map(handle->map->handle,
+ SHELLS_BELLS__trigger);
+ handle->urid_fontHeight = handle->map->map(handle->map->handle,
+ SHELLS_BELLS__fontHeight);
+
+ if(!props_init(&handle->props, plugin_uri,
+ defs, MAX_NPROPS, &handle->state, &handle->stash,
+ handle->map, handle))
+ {
+ fprintf(stderr, "failed to initialize property structure\n");
+ free(handle);
+ return NULL;
+ }
+
+ static const char *fallback= "sh";
+ const char *shell = getenv("SHELL");
+ char cmdline [PATH_MAX];
+ snprintf(cmdline, sizeof(cmdline), "%s",
+ shell ? shell : fallback);
+ if(wordexp(cmdline, &handle->wordexp, WRDE_NOCMD) != 0)
+ {
+ fprintf(stderr, "failed to parse EDITOR");
+ free(handle);
+ return NULL;
+ }
+
+ handle->controller = controller;
+ handle->writer = write_function;
+
+ const d2tk_coord_t w = 800;
+ const d2tk_coord_t h = 800;
+
+ d2tk_pugl_config_t *config = &handle->config;
+ config->parent = (uintptr_t)parent;
+ config->bundle_path = bundle_path;
+ config->min_w = w/2;
+ config->min_h = h/2;
+ config->w = w;
+ config->h = h;
+ config->fixed_size = false;
+ config->fixed_aspect = false;
+ config->expose = _expose;
+ config->data = handle;
+
+ handle->dpugl = d2tk_pugl_new(config, (uintptr_t *)widget);
+ if(!handle->dpugl)
+ {
+ free(handle);
+ return NULL;
+ }
+
+ const LV2_URID ui_scaleFactor = handle->map->map(handle->map->handle,
+ LV2_UI__scaleFactor);
+
+ // fall-back
+ for(LV2_Options_Option *opt = opts;
+ opt && (opt->key != 0) && (opt->value != NULL);
+ opt++)
+ {
+ if( (opt->key == ui_scaleFactor) && (opt->type == handle->forge.Float) )
+ {
+ handle->scale = *(float*)opt->value;
+ }
+ }
+
+ if(handle->scale == 0.f)
+ {
+ handle->scale = d2tk_frontend_get_scale(handle->dpugl);
+ }
+
+ handle->header_height = 32 * handle->scale;
+ handle->footer_height = 32 * handle->scale;
+ handle->sidebar_width = 1 * handle->scale;
+
+ handle->state.font_height = 16;
+ _update_font_height(handle);
+
+ if(host_resize)
+ {
+ host_resize->ui_resize(host_resize->handle, w, h);
+ }
+
+ _message_get(handle, handle->urid_channel);
+ _message_get(handle, handle->urid_note);
+ _message_get(handle, handle->urid_velocity);
+ _message_get(handle, handle->urid_duration);
+ _message_get(handle, handle->urid_trigger);
+ _message_get(handle, handle->urid_fontHeight);
+
+ return handle;
+}
+
+static void
+cleanup(LV2UI_Handle instance)
+{
+ plughandle_t *handle = instance;
+
+ d2tk_frontend_free(handle->dpugl);
+
+ wordfree(&handle->wordexp);
+
+ free(handle);
+}
+
+static void
+port_event(LV2UI_Handle instance, uint32_t index __attribute__((unused)),
+ uint32_t size __attribute__((unused)), uint32_t protocol, const void *buf)
+{
+ plughandle_t *handle = instance;
+
+ if(protocol != handle->atom_eventTransfer)
+ {
+ return;
+ }
+
+ const LV2_Atom_Object *obj = buf;
+
+ ser_atom_t ser;
+ ser_atom_init(&ser);
+ ser_atom_reset(&ser, &handle->forge);
+
+ LV2_Atom_Forge_Ref ref = 0;
+ props_advance(&handle->props, &handle->forge, 0, obj, &ref);
+
+ ser_atom_deinit(&ser);
+
+ d2tk_frontend_redisplay(handle->dpugl);
+}
+
+static int
+_idle(LV2UI_Handle instance)
+{
+ plughandle_t *handle = instance;
+
+ if(d2tk_frontend_step(handle->dpugl))
+ {
+ handle->done = 1;
+ }
+
+ return handle->done;
+}
+
+static const LV2UI_Idle_Interface idle_ext = {
+ .idle = _idle
+};
+
+static int
+_resize(LV2UI_Handle instance, int width, int height)
+{
+ plughandle_t *handle = instance;
+
+ return d2tk_frontend_set_size(handle->dpugl, width, height);
+}
+
+static const LV2UI_Resize resize_ext = {
+ .ui_resize = _resize
+};
+
+static const void *
+extension_data(const char *uri)
+{
+ if(!strcmp(uri, LV2_UI__idleInterface))
+ return &idle_ext;
+ else if(!strcmp(uri, LV2_UI__resize))
+ return &resize_ext;
+
+ return NULL;
+}
+
+static const LV2UI_Descriptor shells_bells_ui= {
+ .URI = SHELLS_BELLS__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 &shells_bells_ui;
+ default:
+ return NULL;
+ }
+}
diff --git a/shells_bells_ui.ttl b/shells_bells_ui.ttl
new file mode 100644
index 0000000..396a9ad
--- /dev/null
+++ b/shells_bells_ui.ttl
@@ -0,0 +1,35 @@
+# Copyright (c) 2019-2021 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 ui: <http://lv2plug.in/ns/extensions/ui#> .
+@prefix urid: <http://lv2plug.in/ns/ext/urid#> .
+@prefix log: <http://lv2plug.in/ns/ext/log#> .
+@prefix opts: <http://lv2plug.in/ns/ext/options#> .
+
+@prefix shells_bells: <http://open-music-kontrollers.ch/lv2/shells_bells#> .
+
+shells_bells:ui
+ ui:portNotification [
+ ui:plugin shells_bells:bells ;
+ lv2:symbol "notify" ;
+ ui:protocol atom:eventTransfer ;
+ ] ;
+ lv2:requiredFeature ui:idleInterface, urid:map, ui:parent ;
+ lv2:optionalFeature ui:resize, opts:options, log:log ;
+ opts:supportedOption ui:scaleFactor ;
+ lv2:extensionData ui:idleInterface, ui:resize .
diff --git a/.gitignore b/subprojects/d2tk/.gitignore
index 651404e..651404e 100644
--- a/.gitignore
+++ b/subprojects/d2tk/.gitignore
diff --git a/subprojects/d2tk/.gitlab-ci.yml b/subprojects/d2tk/.gitlab-ci.yml
new file mode 100644
index 0000000..4847758
--- /dev/null
+++ b/subprojects/d2tk/.gitlab-ci.yml
@@ -0,0 +1,108 @@
+stages:
+ - build
+ - deploy
+
+# templates
+.variables_template: &variables_definition
+ variables:
+ BASE_NAME: "d2tk"
+ PKG_CONFIG_PATH: "/opt/lv2/lib/pkgconfig:/opt/${CI_BUILD_NAME}/lib/pkgconfig:/usr/lib/${CI_BUILD_NAME}/pkgconfig"
+
+.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
+ stage: build
+ script:
+ - meson --prefix="/" --libdir="lib" --cross-file "${CI_BUILD_NAME}" build
+ - sed -i -e '/framework/s/-Wl,-O1//g' -e '/framework/s/-Wl,--start-group//g' -e '/framework/s/-Wl,--end-group//g' build/build.ninja
+ - ninja -C build
+ - DESTDIR="${CI_PROJECT_DIR}/${BASE_NAME}-$(cat VERSION)/${CI_BUILD_NAME}" ninja -C build install
+
+.test_template: &test_definition
+ <<: *common_definition
+ stage: build
+ script:
+ - meson --prefix="/" --libdir="lib" --cross-file "${CI_BUILD_NAME}" build
+ - sed -i -e '/framework/s/-Wl,-O1//g' -e '/framework/s/-Wl,--start-group//g' -e '/framework/s/-Wl,--end-group//g' build/build.ninja
+ - ninja -C build
+ - DESTDIR="${CI_PROJECT_DIR}/${BASE_NAME}-$(cat VERSION)/${CI_BUILD_NAME}" ninja -C build install
+
+ - meson test -C build
+
+.analyze_template: &analyze_definition
+ <<: *common_definition
+ stage: build
+ script:
+ - meson --prefix="/" --libdir="lib" --cross-file "${CI_BUILD_NAME}" build
+ - sed -i -e '/framework/s/-Wl,-O1//g' -e '/framework/s/-Wl,--start-group//g' -e '/framework/s/-Wl,--end-group//g' build/build.ninja
+ - ninja -C build
+ - DESTDIR="${CI_PROJECT_DIR}/${BASE_NAME}-$(cat VERSION)/${CI_BUILD_NAME}" ninja -C build install
+
+ - meson test -C build
+ - meson test -C build --wrap=valgrind
+
+ - CC=clang CXX=clang++ meson --prefix="/" --libdir="lib" --cross-file "${CI_BUILD_NAME}" clang
+ - ninja -C clang
+ - ninja -C clang test
+
+ - scan-build --status-bugs meson --prefix="/" --libdir="lib" --cross-file "${CI_BUILD_NAME}" scanbuild
+ - scan-build --status-bugs ninja -C scanbuild
+ - scan-build --status-bugs ninja -C scanbuild test
+
+.universal_linux_template: &universal_linux_definition
+ image: ventosus/universal-linux-gnu:buster
+ <<: *analyze_definition
+
+.arm_linux_template: &arm_linux_definition
+ image: ventosus/arm-linux-gnueabihf:buster
+ <<: *test_definition
+
+# targets
+x86_64-linux-gnu:
+ before_script:
+ - apt-get install -y libglu1-mesa-dev libevdev-dev libvterm-dev
+ <<: *universal_linux_definition
+
+i686-linux-gnu:
+ before_script:
+ - apt-get install -y libglu1-mesa-dev:i386 libevdev-dev:i386 libvterm-dev:i386
+ <<: *universal_linux_definition
+
+arm-linux-gnueabihf:
+ before_script:
+ - apt-get install -y libglu1-mesa-dev:armhf libevdev-dev:armhf libvterm-dev:armhf
+ <<: *arm_linux_definition
+
+aarch64-linux-gnu:
+ before_script:
+ - apt-get install -y libglu1-mesa-dev:arm64 libevdev-dev:arm64 libvterm-dev:arm64
+ <<: *arm_linux_definition
+
+pack:
+ <<: *variables_definition
+ stage: deploy
+ script:
+ - echo 'packing up...'
+ artifacts:
+ name: "${BASE_NAME}-$(cat VERSION)"
+ paths:
+ - "${BASE_NAME}-$(cat VERSION)/"
+
+pages:
+ stage: deploy
+ before_script:
+ - apt-get update -y
+ - apt-get install -y doxygen
+ script:
+ - doxygen
+ - cp -r doc/html public
+ artifacts:
+ paths:
+ - public/
diff --git a/subprojects/d2tk/COPYING b/subprojects/d2tk/COPYING
new file mode 100644
index 0000000..ddb9a46
--- /dev/null
+++ b/subprojects/d2tk/COPYING
@@ -0,0 +1,201 @@
+ 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/Doxyfile b/subprojects/d2tk/Doxyfile
index 294d19d..294d19d 100644
--- a/Doxyfile
+++ b/subprojects/d2tk/Doxyfile
diff --git a/subprojects/d2tk/README.md b/subprojects/d2tk/README.md
new file mode 100644
index 0000000..51c9529
--- /dev/null
+++ b/subprojects/d2tk/README.md
@@ -0,0 +1,62 @@
+# d2tk
+
+## Data Driven Tool Kit
+
+A performant, dyamic, immediate-mode GUI tool kit in C which partially renders
+on-change only by massively hashing-and-cashing of vector drawing instructions
+and on-demand rendered sprites.
+
+### Build / test
+
+ git clone https://git.open-music-kontrollers.ch/lad/d2tk
+ cd d2tk
+ meson build
+ cd build
+ ninja -j4
+
+#### Pugl/NanoVG backend
+
+ ./d2tk.nanovg
+
+#### Pugl/Cairo backend
+
+ ./d2tk.cairo
+
+#### FBdev/Cairo backend
+
+ ./d2tk.fbdev
+
+### Screenshots
+
+![Screenshot 1](/screenshots/screenshot_1.png)
+
+![Screenshot 2](/screenshots/screenshot_2.png)
+
+![Screenshot 3](/screenshots/screenshot_3.png)
+
+![Screenshot 4](/screenshots/screenshot_4.png)
+
+![Screenshot 5](/screenshots/screenshot_5.png)
+
+![Screenshot 6](/screenshots/screenshot_6.png)
+
+![Screenshot 7](/screenshots/screenshot_7.png)
+
+![Screenshot 8](/screenshots/screenshot_8.png)
+
+### License
+
+Copyright (c) 2018-2019 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/subprojects/d2tk/VERSION b/subprojects/d2tk/VERSION
new file mode 100644
index 0000000..e5e81a2
--- /dev/null
+++ b/subprojects/d2tk/VERSION
@@ -0,0 +1 @@
+0.1.1259
diff --git a/check_for_font b/subprojects/d2tk/check_for_font
index befcbf4..befcbf4 100755
--- a/check_for_font
+++ b/subprojects/d2tk/check_for_font
diff --git a/d2tk/backend.h b/subprojects/d2tk/d2tk/backend.h
index 862f6d2..862f6d2 100644
--- a/d2tk/backend.h
+++ b/subprojects/d2tk/d2tk/backend.h
diff --git a/d2tk/base.h b/subprojects/d2tk/d2tk/base.h
index c37582c..c37582c 100644
--- a/d2tk/base.h
+++ b/subprojects/d2tk/d2tk/base.h
diff --git a/d2tk/config.h.in b/subprojects/d2tk/d2tk/config.h.in
index e8be923..e8be923 100644
--- a/d2tk/config.h.in
+++ b/subprojects/d2tk/d2tk/config.h.in
diff --git a/d2tk/core.h b/subprojects/d2tk/d2tk/core.h
index 265a06c..265a06c 100644
--- a/d2tk/core.h
+++ b/subprojects/d2tk/d2tk/core.h
diff --git a/d2tk/d2tk.h b/subprojects/d2tk/d2tk/d2tk.h
index 2d12a82..2d12a82 100644
--- a/d2tk/d2tk.h
+++ b/subprojects/d2tk/d2tk/d2tk.h
diff --git a/d2tk/frontend.h b/subprojects/d2tk/d2tk/frontend.h
index 716bad5..716bad5 100644
--- a/d2tk/frontend.h
+++ b/subprojects/d2tk/d2tk/frontend.h
diff --git a/d2tk/frontend_fbdev.h b/subprojects/d2tk/d2tk/frontend_fbdev.h
index 969c644..969c644 100644
--- a/d2tk/frontend_fbdev.h
+++ b/subprojects/d2tk/d2tk/frontend_fbdev.h
diff --git a/d2tk/frontend_glfw.h b/subprojects/d2tk/d2tk/frontend_glfw.h
index b7b934f..b7b934f 100644
--- a/d2tk/frontend_glfw.h
+++ b/subprojects/d2tk/d2tk/frontend_glfw.h
diff --git a/d2tk/frontend_pugl.h b/subprojects/d2tk/d2tk/frontend_pugl.h
index 4be0b86..4be0b86 100644
--- a/d2tk/frontend_pugl.h
+++ b/subprojects/d2tk/d2tk/frontend_pugl.h
diff --git a/d2tk/hash.h b/subprojects/d2tk/d2tk/hash.h
index a6cf614..a6cf614 100644
--- a/d2tk/hash.h
+++ b/subprojects/d2tk/d2tk/hash.h
diff --git a/d2tk/util.h b/subprojects/d2tk/d2tk/util.h
index f8eebf8..f8eebf8 100644
--- a/d2tk/util.h
+++ b/subprojects/d2tk/d2tk/util.h
diff --git a/example/custom_cairo.c b/subprojects/d2tk/example/custom_cairo.c
index 56904ed..56904ed 100644
--- a/example/custom_cairo.c
+++ b/subprojects/d2tk/example/custom_cairo.c
diff --git a/example/custom_nanovg.c b/subprojects/d2tk/example/custom_nanovg.c
index 7bd2db3..7bd2db3 100644
--- a/example/custom_nanovg.c
+++ b/subprojects/d2tk/example/custom_nanovg.c
diff --git a/example/d2tk_fbdev.c b/subprojects/d2tk/example/d2tk_fbdev.c
index 47baed8..47baed8 100644
--- a/example/d2tk_fbdev.c
+++ b/subprojects/d2tk/example/d2tk_fbdev.c
diff --git a/example/d2tk_glfw.c b/subprojects/d2tk/example/d2tk_glfw.c
index 31ce905..31ce905 100644
--- a/example/d2tk_glfw.c
+++ b/subprojects/d2tk/example/d2tk_glfw.c
diff --git a/example/d2tk_pugl.c b/subprojects/d2tk/example/d2tk_pugl.c
index 4132547..4132547 100644
--- a/example/d2tk_pugl.c
+++ b/subprojects/d2tk/example/d2tk_pugl.c
diff --git a/example/example.c b/subprojects/d2tk/example/example.c
index be8ed79..be8ed79 100644
--- a/example/example.c
+++ b/subprojects/d2tk/example/example.c
diff --git a/example/example.h b/subprojects/d2tk/example/example.h
index bbb013b..bbb013b 100644
--- a/example/example.h
+++ b/subprojects/d2tk/example/example.h
diff --git a/example/libre-arrow-circle-right.png b/subprojects/d2tk/example/libre-arrow-circle-right.png
index 21c8013..21c8013 100644
--- a/example/libre-arrow-circle-right.png
+++ b/subprojects/d2tk/example/libre-arrow-circle-right.png
Binary files differ
diff --git a/example/libre-gui-file.png b/subprojects/d2tk/example/libre-gui-file.png
index 1c6d984..1c6d984 100644
--- a/example/libre-gui-file.png
+++ b/subprojects/d2tk/example/libre-gui-file.png
Binary files differ
diff --git a/example/libre-gui-folder.png b/subprojects/d2tk/example/libre-gui-folder.png
index 6960606..6960606 100644
--- a/example/libre-gui-folder.png
+++ b/subprojects/d2tk/example/libre-gui-folder.png
Binary files differ
diff --git a/linenoise/.gitignore b/subprojects/d2tk/linenoise/.gitignore
index 7ab7825..7ab7825 100644
--- a/linenoise/.gitignore
+++ b/subprojects/d2tk/linenoise/.gitignore
diff --git a/linenoise/LICENSE b/subprojects/d2tk/linenoise/LICENSE
index 18e8148..18e8148 100644
--- a/linenoise/LICENSE
+++ b/subprojects/d2tk/linenoise/LICENSE
diff --git a/linenoise/Makefile b/subprojects/d2tk/linenoise/Makefile
index 76bcc4e..76bcc4e 100644
--- a/linenoise/Makefile
+++ b/subprojects/d2tk/linenoise/Makefile
diff --git a/linenoise/README.markdown b/subprojects/d2tk/linenoise/README.markdown
index 1afea2a..1afea2a 100644
--- a/linenoise/README.markdown
+++ b/subprojects/d2tk/linenoise/README.markdown
diff --git a/linenoise/encodings/tools/EastAsianWidth.txt b/subprojects/d2tk/linenoise/encodings/tools/EastAsianWidth.txt
index b43aec9..b43aec9 100644
--- a/linenoise/encodings/tools/EastAsianWidth.txt
+++ b/subprojects/d2tk/linenoise/encodings/tools/EastAsianWidth.txt
diff --git a/linenoise/encodings/tools/UnicodeData.txt b/subprojects/d2tk/linenoise/encodings/tools/UnicodeData.txt
index e22f967..e22f967 100644
--- a/linenoise/encodings/tools/UnicodeData.txt
+++ b/subprojects/d2tk/linenoise/encodings/tools/UnicodeData.txt
diff --git a/linenoise/encodings/tools/generate_combining_char_table.rb b/subprojects/d2tk/linenoise/encodings/tools/generate_combining_char_table.rb
index f454051..f454051 100644
--- a/linenoise/encodings/tools/generate_combining_char_table.rb
+++ b/subprojects/d2tk/linenoise/encodings/tools/generate_combining_char_table.rb
diff --git a/linenoise/encodings/tools/generate_wide_char_table.rb b/subprojects/d2tk/linenoise/encodings/tools/generate_wide_char_table.rb
index 8337ca6..8337ca6 100644
--- a/linenoise/encodings/tools/generate_wide_char_table.rb
+++ b/subprojects/d2tk/linenoise/encodings/tools/generate_wide_char_table.rb
diff --git a/linenoise/encodings/utf8.c b/subprojects/d2tk/linenoise/encodings/utf8.c
index 5cd7767..5cd7767 100755
--- a/linenoise/encodings/utf8.c
+++ b/subprojects/d2tk/linenoise/encodings/utf8.c
diff --git a/linenoise/encodings/utf8.h b/subprojects/d2tk/linenoise/encodings/utf8.h
index d401bc8..d401bc8 100755
--- a/linenoise/encodings/utf8.h
+++ b/subprojects/d2tk/linenoise/encodings/utf8.h
diff --git a/linenoise/example.c b/subprojects/d2tk/linenoise/example.c
index adbf1bd..adbf1bd 100755
--- a/linenoise/example.c
+++ b/subprojects/d2tk/linenoise/example.c
diff --git a/linenoise/linenoise.c b/subprojects/d2tk/linenoise/linenoise.c
index aac26fa..aac26fa 100755
--- a/linenoise/linenoise.c
+++ b/subprojects/d2tk/linenoise/linenoise.c
diff --git a/linenoise/linenoise.h b/subprojects/d2tk/linenoise/linenoise.h
index 643ba10..643ba10 100755
--- a/linenoise/linenoise.h
+++ b/subprojects/d2tk/linenoise/linenoise.h
diff --git a/subprojects/d2tk/meson.build b/subprojects/d2tk/meson.build
new file mode 100644
index 0000000..8e8e9ed
--- /dev/null
+++ b/subprojects/d2tk/meson.build
@@ -0,0 +1,434 @@
+project('d2tk', 'c', default_options : [
+ 'buildtype=release',
+ 'warning_level=3',
+ 'werror=false',
+ 'b_lto=false',
+ 'c_std=gnu11'])
+
+static_link = false #meson.is_cross_build()
+
+build_debug_overlay = get_option('build-debug-overlay')
+build_examples = get_option('build-examples')
+build_tests = get_option('build-tests')
+build_doc = get_option('build-doc')
+
+use_backend_cairo = get_option('use-backend-cairo')
+use_backend_nanovg = get_option('use-backend-nanovg')
+use_frontend_fbdev = get_option('use-frontend-fbdev')
+use_frontend_pugl = get_option('use-frontend-pugl')
+use_frontend_glfw = get_option('use-frontend-glfw')
+
+use_evdev = get_option('use-evdev')
+use_fontconfig = get_option('use-fontconfig')
+
+grep = find_program('grep',
+ native : true,
+ required : use_fontconfig)
+fc_list = find_program('fc-list',
+ native : true,
+ required : use_fontconfig)
+check_for_font = find_program('check_for_font',
+ native : true,
+ required : use_fontconfig)
+robodoc = find_program('robodoc',
+ native : true,
+ required : build_doc)
+
+cc = meson.get_compiler('c')
+
+# mandatory dependencies
+m_dep = cc.find_library('m')
+thread_dep = dependency('threads')
+
+# dependencies for backend_cairo/frontend_pugl
+freetype_dep = dependency('freetype2',
+ version : '>=18.0.0',
+ static : static_link,
+ required : use_backend_cairo)
+pixman_dep = dependency('pixman-1',
+ version : '>=0.34.0',
+ static : static_link,
+ required : use_backend_cairo)
+cairo_dep = dependency('cairo',
+ version : '>=1.14.0',
+ static : static_link,
+ required : use_backend_cairo)
+cairo_deps = [freetype_dep, pixman_dep, cairo_dep]
+if use_frontend_pugl.enabled()
+ cairo_xlib_dep = dependency('cairo-xlib',
+ version : '>=1.14.0',
+ static : static_link,
+ required : use_backend_cairo)
+endif
+
+# dependencies for frontend_fbdev
+input_dep = dependency('libinput',
+ version : '>=1.6.0',
+ static : static_link,
+ required : use_frontend_fbdev)
+udev_dep = dependency('libudev',
+ static : static_link,
+ required : use_frontend_fbdev)
+evdev_dep = dependency('libevdev',
+ version : '>=1.5.0',
+ static : static_link,
+ required : use_frontend_fbdev)
+
+# dependencies for backend_nanovg
+glu_dep = dependency('glu',
+ version : '>=9.0.0',
+ static : static_link,
+ required : use_backend_nanovg)
+glew_dep = dependency('glew',
+ version : '>=2.0.0',
+ static : static_link,
+ required : use_backend_nanovg)
+if use_frontend_glfw.enabled()
+ glfw_dep = dependency('glfw3',
+ version : '>=3.3.0',
+ static : static_link,
+ required : use_backend_nanovg)
+endif
+
+# optional dependencies
+util_dep = cc.find_library('util')
+vterm_dep = dependency('vterm',
+ version : '>=0.1',
+ static : static_link)
+if not use_frontend_fbdev.enabled()
+ evdev_dep = dependency('libevdev',
+ version : '>=1.5.0',
+ static : static_link,
+ required : use_evdev)
+endif
+fontconfig_dep = dependency('fontconfig',
+ version : '>=2.0.0',
+ static : static_link,
+ required : use_fontconfig)
+
+deps = [m_dep, thread_dep, evdev_dep, util_dep, vterm_dep, fontconfig_dep]
+links = []
+
+d2tk_inc = include_directories('')
+pugl_inc = include_directories(join_paths('pugl', 'include'))
+nanovg_inc = include_directories(join_paths('nanovg', 'src'))
+linenoise_inc = include_directories('linenoise')
+inc_dir = [d2tk_inc, pugl_inc, nanovg_inc, linenoise_inc]
+
+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')
+
+if build_debug_overlay
+ conf_data.set('D2TK_DEBUG', 1)
+else
+ conf_data.set('D2TK_DEBUG', 0)
+endif
+
+lib_srcs = [
+ join_paths('src', 'hash.c'),
+ join_paths('src', 'core.c'),
+ join_paths('src', 'base.c'),
+ join_paths('src', 'base_table.c'),
+ join_paths('src', 'base_frame.c'),
+ join_paths('src', 'base_layout.c'),
+ join_paths('src', 'base_scrollbar.c'),
+ join_paths('src', 'base_pane.c'),
+ join_paths('src', 'base_cursor.c'),
+ join_paths('src', 'base_button.c'),
+ join_paths('src', 'base_image.c'),
+ join_paths('src', 'base_bitmap.c'),
+ join_paths('src', 'base_custom.c'),
+ join_paths('src', 'base_meter.c'),
+ join_paths('src', 'base_combo.c'),
+ join_paths('src', 'base_textfield.c'),
+ join_paths('src', 'base_label.c'),
+ join_paths('src', 'base_separator.c'),
+ join_paths('src', 'base_tooltip.c'),
+ join_paths('src', 'base_link.c'),
+ join_paths('src', 'base_dial.c'),
+ join_paths('src', 'base_spinner.c'),
+ join_paths('src', 'base_bar.c'),
+ join_paths('src', 'base_wave.c'),
+ join_paths('src', 'base_flowmatrix.c'),
+ join_paths('src', 'base_pty.c'),
+ join_paths('src', 'base_lineedit.c'),
+ join_paths('src', 'util_spawn.c'),
+ join_paths('linenoise', 'linenoise.c'),
+ join_paths('linenoise', 'encodings', 'utf8.c')
+]
+
+if use_evdev.enabled()
+ conf_data.set('D2TK_EVDEV', 1)
+ lib_srcs += join_paths('src', 'base_vkb.c')
+else
+ conf_data.set('D2TK_EVDEV', 0)
+endif
+
+if input_dep.found() and input_dep.version().version_compare('>=1.15.0')
+ conf_data.set('D2TK_INPUT_1_15', 1)
+else
+ conf_data.set('D2TK_INPUT_1_15', 0)
+endif
+
+if use_fontconfig.enabled()
+ conf_data.set('D2TK_FONTCONFIG', 1)
+else
+ conf_data.set('D2TK_FONTCONFIG', 0)
+endif
+
+example_srcs = [
+ join_paths('example', 'example.c')
+]
+
+example_cairo_srcs = [
+ join_paths('example', 'custom_cairo.c')
+]
+
+example_nanovg_srcs = [
+ join_paths('example', 'custom_nanovg.c')
+]
+
+example_pugl_srcs = [
+ join_paths('example', 'd2tk_pugl.c')
+]
+
+example_fbdev_srcs = [
+ join_paths('example', 'd2tk_fbdev.c')
+]
+
+example_glfw_srcs = [
+ join_paths('example', 'd2tk_glfw.c')
+]
+
+pugl_srcs = [
+ join_paths('src', 'frontend_pugl.c'),
+ join_paths('pugl', 'src', 'implementation.c')
+]
+
+pugl_gl_srcs = []
+
+pugl_cairo_srcs = []
+
+nanovg_srcs = [
+ join_paths('nanovg', 'src', 'nanovg.c'),
+ join_paths('src', 'backend_nanovg.c')
+]
+
+cairo_srcs = [
+ join_paths('src', 'backend_cairo.c')
+]
+
+fbdev_srcs = [
+ join_paths('src', 'frontend_fbdev.c')
+]
+
+glfw_srcs = [
+ join_paths('src', 'frontend_glfw.c')
+]
+
+test_core_srcs = [
+ join_paths('test', 'core.c'),
+ join_paths('test', 'mock.c')
+]
+
+test_base_srcs = [
+ join_paths('test', 'base.c'),
+ join_paths('test', 'mock.c')
+]
+
+c_args = ['-fvisibility=hidden',
+ '-ffast-math']
+
+if host_machine.system() == 'windows'
+ deps += cc.find_library('opengl32', required : use_frontend_pugl)
+ deps += cc.find_library('gdi32', required : use_frontend_pugl)
+ deps += cc.find_library('ws2_32', required : true)
+ pugl_srcs += join_paths('pugl', 'src', 'win.c')
+ pugl_gl_srcs += join_paths('pugl', 'src', 'win_gl.c')
+ pugl_cairo_srcs += join_paths('pugl', 'src', 'win_cairo.c')
+elif host_machine.system() == 'darwin'
+ add_languages('objc')
+ links += ['-framework', 'OpenGL']
+ links += ['-framework', 'Cocoa']
+ pugl_srcs += join_paths('pugl','src', 'mac.m')
+ pugl_gl_srcs += join_paths('pugl', 'src', 'mac_gl.m')
+ pugl_cairo_srcs += join_paths('pugl', 'src', 'mac_cairo.m')
+else
+ deps += dependency('gl', required : use_frontend_pugl)
+ deps += dependency('x11', version : '>=1.6.0', required : use_frontend_pugl)
+ deps += dependency('xext', version : '>=1.3.0', required : use_frontend_pugl)
+ pugl_srcs += join_paths('pugl', 'src', 'x11.c')
+ pugl_srcs += join_paths('pugl', 'src', 'x11_stub.c')
+ pugl_gl_srcs += join_paths('pugl', 'src', 'x11_gl.c')
+ pugl_cairo_srcs += join_paths('pugl', 'src', 'x11_cairo.c')
+endif
+
+if use_backend_cairo.enabled()
+ if use_frontend_pugl.enabled()
+ d2tk_cairo = declare_dependency(
+ compile_args : ['-DPUGL_HAVE_CAIRO', '-DPUGL_STATIC'],
+ include_directories : inc_dir,
+ dependencies : [deps, cairo_deps, cairo_xlib_dep],
+ link_args : links,
+ sources : [lib_srcs, cairo_srcs, pugl_srcs, pugl_cairo_srcs])
+
+ if build_examples
+ executable('d2tk.cairo', [example_srcs, example_pugl_srcs, example_cairo_srcs],
+ c_args : c_args,
+ include_directories : inc_dir,
+ dependencies: d2tk_cairo,
+ install : false)
+ endif
+ endif
+
+ if use_frontend_fbdev.enabled()
+ d2tk_fbdev = declare_dependency(
+ include_directories : inc_dir,
+ dependencies : [deps, cairo_deps, input_dep, udev_dep, evdev_dep],
+ link_args : links,
+ sources : [lib_srcs, cairo_srcs, fbdev_srcs])
+
+ if build_examples
+ executable('d2tk.fbdev', [example_srcs, example_fbdev_srcs, example_cairo_srcs],
+ c_args : c_args,
+ include_directories : inc_dir,
+ dependencies: d2tk_fbdev,
+ install : false)
+ endif
+ endif
+endif
+
+if use_backend_nanovg.enabled()
+ if use_frontend_pugl.enabled()
+ d2tk_nanovg = declare_dependency(
+ compile_args : ['-DPUGL_STATIC'],
+ include_directories : inc_dir,
+ dependencies : [deps, glu_dep, glew_dep],
+ link_args : links,
+ sources : [lib_srcs, nanovg_srcs, pugl_srcs, pugl_gl_srcs])
+
+ if build_examples
+ executable('d2tk.nanovg', [example_srcs, example_pugl_srcs, example_nanovg_srcs],
+ c_args : c_args,
+ include_directories : inc_dir,
+ dependencies: d2tk_nanovg,
+ install : false)
+ endif
+ endif
+
+ if use_frontend_glfw.enabled()
+ d2tk_glfw = declare_dependency(
+ include_directories : inc_dir,
+ dependencies : [deps, glfw_dep, glew_dep],
+ link_args : links,
+ sources : [lib_srcs, nanovg_srcs, glfw_srcs])
+
+ if build_examples
+ executable('d2tk.glfw', [example_srcs, example_glfw_srcs, example_nanovg_srcs],
+ c_args : c_args,
+ include_directories : inc_dir,
+ dependencies: d2tk_glfw,
+ install : false)
+ endif
+ endif
+endif
+
+config_h = configure_file(
+ input : join_paths('d2tk', 'config.h.in'),
+ output : 'config.h',
+ configuration : conf_data,
+ install : false)
+
+if not use_fontconfig.enabled()
+ fira_sans_bold_ttf = configure_file(
+ input : join_paths('ttf', 'FiraSans-Bold.ttf'),
+ output : 'FiraSans:bold.ttf',
+ copy : true,
+ install : false)
+
+ fira_code_bold_ttf = configure_file(
+ input : join_paths('ttf', 'FiraCode-Bold.ttf'),
+ output : 'FiraCode:bold.ttf',
+ copy : true,
+ install : false)
+
+ fira_code_light_ttf = configure_file(
+ input : join_paths('ttf', 'FiraCode-Light.ttf'),
+ output : 'FiraCode:light.ttf',
+ copy : true,
+ install : false)
+
+ fira_code_medium_ttf = configure_file(
+ input : join_paths('ttf', 'FiraCode-Medium.ttf'),
+ output : 'FiraCode:medium.ttf',
+ copy : true,
+ install : false)
+
+ fira_code_regular_ttf = configure_file(
+ input : join_paths('ttf', 'FiraCode-Regular.ttf'),
+ output : 'FiraCode:regular.ttf',
+ copy : true,
+ install : false)
+endif
+
+if build_examples
+ configure_file(
+ input : join_paths('example', 'libre-arrow-circle-right.png'),
+ output : 'libre-arrow-circle-right.png',
+ copy : true,
+ install : false)
+
+ configure_file(
+ input : join_paths('example', 'libre-gui-folder.png'),
+ output : 'libre-gui-folder.png',
+ copy : true,
+ install : false)
+
+ configure_file(
+ input : join_paths('example', 'libre-gui-file.png'),
+ output : 'libre-gui-file.png',
+ copy : true,
+ install : false)
+endif
+
+if build_tests
+ test_core = executable('test.core', [test_core_srcs, lib_srcs],
+ c_args : c_args,
+ dependencies : deps,
+ include_directories : inc_dir,
+ install : false)
+
+ test_base = executable('test.base', [test_base_srcs, lib_srcs],
+ c_args : c_args,
+ dependencies : deps,
+ include_directories : inc_dir,
+ install : false)
+
+ test('Test core', test_core)
+ test('Test base', test_base)
+
+ if fc_list.found() and grep.found() and check_for_font.found()
+ test('FiraSans-Bold.ttf', check_for_font, args : ['FiraSans-Bold.ttf'])
+ test('FiraCode-Light.ttf', check_for_font, args : ['FiraCode-Light.tt'])
+ test('FiraCode-Regular.ttf', check_for_font, args : ['FiraCode-Regular.ttf'])
+ test('FiraCode-Medium.ttf', check_for_font, args : ['FiraCode-Medium.ttf'])
+ test('FiraCode-Bold.ttf', check_for_font, args : ['FiraCode-Bold.ttf'])
+ endif
+endif
+
+if build_doc
+ run_command('rm', '-rf', 'doc')
+
+ run_command(robodoc,
+ '--src', './d2tk',
+ '--doc', 'doc',
+ '--multidoc', '--troff', '--nosort', '--nodesc', '--cmode',
+ '--compress', 'gzip')
+endif
diff --git a/subprojects/d2tk/meson_options.txt b/subprojects/d2tk/meson_options.txt
new file mode 100644
index 0000000..242cbe0
--- /dev/null
+++ b/subprojects/d2tk/meson_options.txt
@@ -0,0 +1,47 @@
+option('build-debug-overlay',
+ type : 'boolean',
+ value : false,
+ yield : true)
+option('build-examples',
+ type : 'boolean',
+ value : false,
+ yield : true)
+option('build-tests',
+ type : 'boolean',
+ value : true,
+ yield : true)
+option('build-doc',
+ type : 'boolean',
+ value : false,
+ yield : true)
+
+option('use-backend-cairo',
+ type : 'feature',
+ value : 'disabled',
+ yield : true)
+option('use-backend-nanovg',
+ type : 'feature',
+ value : 'disabled',
+ yield : true)
+
+option('use-frontend-fbdev',
+ type : 'feature',
+ value : 'disabled',
+ yield : true)
+option('use-frontend-pugl',
+ type : 'feature',
+ value : 'disabled',
+ yield : true)
+option('use-frontend-glfw',
+ type : 'feature',
+ value : 'disabled',
+ yield : true)
+
+option('use-evdev',
+ type : 'feature',
+ value : 'disabled',
+ yield : true)
+option('use-fontconfig',
+ type : 'feature',
+ value : 'disabled',
+ yield : true)
diff --git a/nanovg/.gitignore b/subprojects/d2tk/nanovg/.gitignore
index 70f534e..70f534e 100644
--- a/nanovg/.gitignore
+++ b/subprojects/d2tk/nanovg/.gitignore
diff --git a/nanovg/LICENSE.txt b/subprojects/d2tk/nanovg/LICENSE.txt
index 2a03a1a..2a03a1a 100644
--- a/nanovg/LICENSE.txt
+++ b/subprojects/d2tk/nanovg/LICENSE.txt
diff --git a/nanovg/README.md b/subprojects/d2tk/nanovg/README.md
index 4f9709b..4f9709b 100644
--- a/nanovg/README.md
+++ b/subprojects/d2tk/nanovg/README.md
diff --git a/nanovg/example/LICENSE_OFL.txt b/subprojects/d2tk/nanovg/example/LICENSE_OFL.txt
index d952d62..d952d62 100644
--- a/nanovg/example/LICENSE_OFL.txt
+++ b/subprojects/d2tk/nanovg/example/LICENSE_OFL.txt
diff --git a/nanovg/example/NotoEmoji-Regular.ttf b/subprojects/d2tk/nanovg/example/NotoEmoji-Regular.ttf
index 19b7bad..19b7bad 100644
--- a/nanovg/example/NotoEmoji-Regular.ttf
+++ b/subprojects/d2tk/nanovg/example/NotoEmoji-Regular.ttf
Binary files differ
diff --git a/nanovg/example/Roboto-Bold.ttf b/subprojects/d2tk/nanovg/example/Roboto-Bold.ttf
index aaf374d..aaf374d 100755
--- a/nanovg/example/Roboto-Bold.ttf
+++ b/subprojects/d2tk/nanovg/example/Roboto-Bold.ttf
Binary files differ
diff --git a/nanovg/example/Roboto-Light.ttf b/subprojects/d2tk/nanovg/example/Roboto-Light.ttf
index 664e1b2..664e1b2 100755
--- a/nanovg/example/Roboto-Light.ttf
+++ b/subprojects/d2tk/nanovg/example/Roboto-Light.ttf
Binary files differ
diff --git a/nanovg/example/Roboto-Regular.ttf b/subprojects/d2tk/nanovg/example/Roboto-Regular.ttf
index 3e6e2e7..3e6e2e7 100755
--- a/nanovg/example/Roboto-Regular.ttf
+++ b/subprojects/d2tk/nanovg/example/Roboto-Regular.ttf
Binary files differ
diff --git a/nanovg/example/demo.c b/subprojects/d2tk/nanovg/example/demo.c
index cfad99e..cfad99e 100644
--- a/nanovg/example/demo.c
+++ b/subprojects/d2tk/nanovg/example/demo.c
diff --git a/nanovg/example/demo.h b/subprojects/d2tk/nanovg/example/demo.h
index aace449..aace449 100644
--- a/nanovg/example/demo.h
+++ b/subprojects/d2tk/nanovg/example/demo.h
diff --git a/nanovg/example/entypo.ttf b/subprojects/d2tk/nanovg/example/entypo.ttf
index fc305d2..fc305d2 100644
--- a/nanovg/example/entypo.ttf
+++ b/subprojects/d2tk/nanovg/example/entypo.ttf
Binary files differ
diff --git a/nanovg/example/example_fbo.c b/subprojects/d2tk/nanovg/example/example_fbo.c
index cff4ed2..cff4ed2 100644
--- a/nanovg/example/example_fbo.c
+++ b/subprojects/d2tk/nanovg/example/example_fbo.c
diff --git a/nanovg/example/example_gl2.c b/subprojects/d2tk/nanovg/example/example_gl2.c
index 7fd5621..7fd5621 100644
--- a/nanovg/example/example_gl2.c
+++ b/subprojects/d2tk/nanovg/example/example_gl2.c
diff --git a/nanovg/example/example_gl3.c b/subprojects/d2tk/nanovg/example/example_gl3.c
index 409a145..409a145 100644
--- a/nanovg/example/example_gl3.c
+++ b/subprojects/d2tk/nanovg/example/example_gl3.c
diff --git a/nanovg/example/example_gles2.c b/subprojects/d2tk/nanovg/example/example_gles2.c
index ed78838..ed78838 100644
--- a/nanovg/example/example_gles2.c
+++ b/subprojects/d2tk/nanovg/example/example_gles2.c
diff --git a/nanovg/example/example_gles3.c b/subprojects/d2tk/nanovg/example/example_gles3.c
index 4a6084c..4a6084c 100644
--- a/nanovg/example/example_gles3.c
+++ b/subprojects/d2tk/nanovg/example/example_gles3.c
diff --git a/nanovg/example/images.txt b/subprojects/d2tk/nanovg/example/images.txt
index 96ad626..96ad626 100644
--- a/nanovg/example/images.txt
+++ b/subprojects/d2tk/nanovg/example/images.txt
diff --git a/nanovg/example/images/image1.jpg b/subprojects/d2tk/nanovg/example/images/image1.jpg
index c2ce39b..c2ce39b 100644
--- a/nanovg/example/images/image1.jpg
+++ b/subprojects/d2tk/nanovg/example/images/image1.jpg
Binary files differ
diff --git a/nanovg/example/images/image10.jpg b/subprojects/d2tk/nanovg/example/images/image10.jpg
index d443fb0..d443fb0 100644
--- a/nanovg/example/images/image10.jpg
+++ b/subprojects/d2tk/nanovg/example/images/image10.jpg
Binary files differ
diff --git a/nanovg/example/images/image11.jpg b/subprojects/d2tk/nanovg/example/images/image11.jpg
index 7429fe5..7429fe5 100644
--- a/nanovg/example/images/image11.jpg
+++ b/subprojects/d2tk/nanovg/example/images/image11.jpg
Binary files differ
diff --git a/nanovg/example/images/image12.jpg b/subprojects/d2tk/nanovg/example/images/image12.jpg
index eb0369d..eb0369d 100644
--- a/nanovg/example/images/image12.jpg
+++ b/subprojects/d2tk/nanovg/example/images/image12.jpg
Binary files differ
diff --git a/nanovg/example/images/image2.jpg b/subprojects/d2tk/nanovg/example/images/image2.jpg
index 1db15ab..1db15ab 100644
--- a/nanovg/example/images/image2.jpg
+++ b/subprojects/d2tk/nanovg/example/images/image2.jpg
Binary files differ
diff --git a/nanovg/example/images/image3.jpg b/subprojects/d2tk/nanovg/example/images/image3.jpg
index 884f9f2..884f9f2 100644
--- a/nanovg/example/images/image3.jpg
+++ b/subprojects/d2tk/nanovg/example/images/image3.jpg
Binary files differ
diff --git a/nanovg/example/images/image4.jpg b/subprojects/d2tk/nanovg/example/images/image4.jpg
index f6e1039..f6e1039 100644
--- a/nanovg/example/images/image4.jpg
+++ b/subprojects/d2tk/nanovg/example/images/image4.jpg
Binary files differ
diff --git a/nanovg/example/images/image5.jpg b/subprojects/d2tk/nanovg/example/images/image5.jpg
index d952d16..d952d16 100644
--- a/nanovg/example/images/image5.jpg
+++ b/subprojects/d2tk/nanovg/example/images/image5.jpg
Binary files differ
diff --git a/nanovg/example/images/image6.jpg b/subprojects/d2tk/nanovg/example/images/image6.jpg
index f098087..f098087 100644
--- a/nanovg/example/images/image6.jpg
+++ b/subprojects/d2tk/nanovg/example/images/image6.jpg
Binary files differ
diff --git a/nanovg/example/images/image7.jpg b/subprojects/d2tk/nanovg/example/images/image7.jpg
index 623b4cb..623b4cb 100644
--- a/nanovg/example/images/image7.jpg
+++ b/subprojects/d2tk/nanovg/example/images/image7.jpg
Binary files differ
diff --git a/nanovg/example/images/image8.jpg b/subprojects/d2tk/nanovg/example/images/image8.jpg
index 123b6da..123b6da 100644
--- a/nanovg/example/images/image8.jpg
+++ b/subprojects/d2tk/nanovg/example/images/image8.jpg
Binary files differ
diff --git a/nanovg/example/images/image9.jpg b/subprojects/d2tk/nanovg/example/images/image9.jpg
index 045fadb..045fadb 100644
--- a/nanovg/example/images/image9.jpg
+++ b/subprojects/d2tk/nanovg/example/images/image9.jpg
Binary files differ
diff --git a/nanovg/example/perf.c b/subprojects/d2tk/nanovg/example/perf.c
index a74dc3c..a74dc3c 100644
--- a/nanovg/example/perf.c
+++ b/subprojects/d2tk/nanovg/example/perf.c
diff --git a/nanovg/example/perf.h b/subprojects/d2tk/nanovg/example/perf.h
index 3ca67b2..3ca67b2 100644
--- a/nanovg/example/perf.h
+++ b/subprojects/d2tk/nanovg/example/perf.h
diff --git a/nanovg/example/screenshot-01.png b/subprojects/d2tk/nanovg/example/screenshot-01.png
index d8febe9..d8febe9 100644
--- a/nanovg/example/screenshot-01.png
+++ b/subprojects/d2tk/nanovg/example/screenshot-01.png
Binary files differ
diff --git a/nanovg/example/screenshot-02.png b/subprojects/d2tk/nanovg/example/screenshot-02.png
index 7cfa4bc..7cfa4bc 100644
--- a/nanovg/example/screenshot-02.png
+++ b/subprojects/d2tk/nanovg/example/screenshot-02.png
Binary files differ
diff --git a/nanovg/example/stb_image_write.h b/subprojects/d2tk/nanovg/example/stb_image_write.h
index 5de3159..5de3159 100644
--- a/nanovg/example/stb_image_write.h
+++ b/subprojects/d2tk/nanovg/example/stb_image_write.h
diff --git a/nanovg/obsolete/nanovg_gl2.h b/subprojects/d2tk/nanovg/obsolete/nanovg_gl2.h
index ad8883a..ad8883a 100644
--- a/nanovg/obsolete/nanovg_gl2.h
+++ b/subprojects/d2tk/nanovg/obsolete/nanovg_gl2.h
diff --git a/nanovg/obsolete/nanovg_gl3.h b/subprojects/d2tk/nanovg/obsolete/nanovg_gl3.h
index 971fb6b..971fb6b 100644
--- a/nanovg/obsolete/nanovg_gl3.h
+++ b/subprojects/d2tk/nanovg/obsolete/nanovg_gl3.h
diff --git a/nanovg/obsolete/obsolete.md b/subprojects/d2tk/nanovg/obsolete/obsolete.md
index 1dee5d1..1dee5d1 100644
--- a/nanovg/obsolete/obsolete.md
+++ b/subprojects/d2tk/nanovg/obsolete/obsolete.md
diff --git a/nanovg/premake4.lua b/subprojects/d2tk/nanovg/premake4.lua
index 0f86168..0f86168 100644
--- a/nanovg/premake4.lua
+++ b/subprojects/d2tk/nanovg/premake4.lua
diff --git a/nanovg/src/fontstash.h b/subprojects/d2tk/nanovg/src/fontstash.h
index 9df68b3..9df68b3 100644
--- a/nanovg/src/fontstash.h
+++ b/subprojects/d2tk/nanovg/src/fontstash.h
diff --git a/nanovg/src/nanovg.c b/subprojects/d2tk/nanovg/src/nanovg.c
index f642788..f642788 100644
--- a/nanovg/src/nanovg.c
+++ b/subprojects/d2tk/nanovg/src/nanovg.c
diff --git a/nanovg/src/nanovg.h b/subprojects/d2tk/nanovg/src/nanovg.h
index 8aaec30..8aaec30 100644
--- a/nanovg/src/nanovg.h
+++ b/subprojects/d2tk/nanovg/src/nanovg.h
diff --git a/nanovg/src/nanovg_gl.h b/subprojects/d2tk/nanovg/src/nanovg_gl.h
index 75d51e9..75d51e9 100644
--- a/nanovg/src/nanovg_gl.h
+++ b/subprojects/d2tk/nanovg/src/nanovg_gl.h
diff --git a/nanovg/src/nanovg_gl_utils.h b/subprojects/d2tk/nanovg/src/nanovg_gl_utils.h
index f7384d8..f7384d8 100644
--- a/nanovg/src/nanovg_gl_utils.h
+++ b/subprojects/d2tk/nanovg/src/nanovg_gl_utils.h
diff --git a/nanovg/src/stb_image.h b/subprojects/d2tk/nanovg/src/stb_image.h
index e06f7a1..e06f7a1 100644
--- a/nanovg/src/stb_image.h
+++ b/subprojects/d2tk/nanovg/src/stb_image.h
diff --git a/nanovg/src/stb_truetype.h b/subprojects/d2tk/nanovg/src/stb_truetype.h
index bfb1841..bfb1841 100644
--- a/nanovg/src/stb_truetype.h
+++ b/subprojects/d2tk/nanovg/src/stb_truetype.h
diff --git a/pugl/.clang-format b/subprojects/d2tk/pugl/.clang-format
index 7b30bd2..7b30bd2 100644
--- a/pugl/.clang-format
+++ b/subprojects/d2tk/pugl/.clang-format
diff --git a/pugl/.clang-tidy b/subprojects/d2tk/pugl/.clang-tidy
index 1e40901..1e40901 100644
--- a/pugl/.clang-tidy
+++ b/subprojects/d2tk/pugl/.clang-tidy
diff --git a/pugl/.clant.json b/subprojects/d2tk/pugl/.clant.json
index 6f48901..6f48901 100644
--- a/pugl/.clant.json
+++ b/subprojects/d2tk/pugl/.clant.json
diff --git a/pugl/.editorconfig b/subprojects/d2tk/pugl/.editorconfig
index c2d35dd..c2d35dd 100644
--- a/pugl/.editorconfig
+++ b/subprojects/d2tk/pugl/.editorconfig
diff --git a/pugl/.gitattributes b/subprojects/d2tk/pugl/.gitattributes
index 32967c1..32967c1 100644
--- a/pugl/.gitattributes
+++ b/subprojects/d2tk/pugl/.gitattributes
diff --git a/pugl/.gitignore b/subprojects/d2tk/pugl/.gitignore
index 41c45d2..41c45d2 100644
--- a/pugl/.gitignore
+++ b/subprojects/d2tk/pugl/.gitignore
diff --git a/pugl/.gitlab-ci.yml b/subprojects/d2tk/pugl/.gitlab-ci.yml
index dc82cf8..dc82cf8 100644
--- a/pugl/.gitlab-ci.yml
+++ b/subprojects/d2tk/pugl/.gitlab-ci.yml
diff --git a/pugl/.gitmodules b/subprojects/d2tk/pugl/.gitmodules
index e69de29..e69de29 100644
--- a/pugl/.gitmodules
+++ b/subprojects/d2tk/pugl/.gitmodules
diff --git a/pugl/.includes.imp b/subprojects/d2tk/pugl/.includes.imp
index 74a3105..74a3105 100644
--- a/pugl/.includes.imp
+++ b/subprojects/d2tk/pugl/.includes.imp
diff --git a/pugl/AUTHORS b/subprojects/d2tk/pugl/AUTHORS
index 99f6dac..99f6dac 100644
--- a/pugl/AUTHORS
+++ b/subprojects/d2tk/pugl/AUTHORS
diff --git a/pugl/COPYING b/subprojects/d2tk/pugl/COPYING
index 63e6829..63e6829 100644
--- a/pugl/COPYING
+++ b/subprojects/d2tk/pugl/COPYING
diff --git a/pugl/README.md b/subprojects/d2tk/pugl/README.md
index 5b4f82f..5b4f82f 100644
--- a/pugl/README.md
+++ b/subprojects/d2tk/pugl/README.md
diff --git a/pugl/bindings/cxx/include/.clang-tidy b/subprojects/d2tk/pugl/bindings/cxx/include/.clang-tidy
index 816223d..816223d 100644
--- a/pugl/bindings/cxx/include/.clang-tidy
+++ b/subprojects/d2tk/pugl/bindings/cxx/include/.clang-tidy
diff --git a/pugl/bindings/cxx/include/pugl/cairo.hpp b/subprojects/d2tk/pugl/bindings/cxx/include/pugl/cairo.hpp
index 15dc5de..15dc5de 100644
--- a/pugl/bindings/cxx/include/pugl/cairo.hpp
+++ b/subprojects/d2tk/pugl/bindings/cxx/include/pugl/cairo.hpp
diff --git a/pugl/bindings/cxx/include/pugl/gl.hpp b/subprojects/d2tk/pugl/bindings/cxx/include/pugl/gl.hpp
index 023dd45..023dd45 100644
--- a/pugl/bindings/cxx/include/pugl/gl.hpp
+++ b/subprojects/d2tk/pugl/bindings/cxx/include/pugl/gl.hpp
diff --git a/pugl/bindings/cxx/include/pugl/pugl.hpp b/subprojects/d2tk/pugl/bindings/cxx/include/pugl/pugl.hpp
index fc3bb03..fc3bb03 100644
--- a/pugl/bindings/cxx/include/pugl/pugl.hpp
+++ b/subprojects/d2tk/pugl/bindings/cxx/include/pugl/pugl.hpp
diff --git a/pugl/bindings/cxx/include/pugl/stub.hpp b/subprojects/d2tk/pugl/bindings/cxx/include/pugl/stub.hpp
index fbafcee..fbafcee 100644
--- a/pugl/bindings/cxx/include/pugl/stub.hpp
+++ b/subprojects/d2tk/pugl/bindings/cxx/include/pugl/stub.hpp
diff --git a/pugl/bindings/cxx/include/pugl/vulkan.hpp b/subprojects/d2tk/pugl/bindings/cxx/include/pugl/vulkan.hpp
index f3dbcad..f3dbcad 100644
--- a/pugl/bindings/cxx/include/pugl/vulkan.hpp
+++ b/subprojects/d2tk/pugl/bindings/cxx/include/pugl/vulkan.hpp
diff --git a/pugl/doc/_static/meson.build b/subprojects/d2tk/pugl/doc/_static/meson.build
index fc7792c..fc7792c 100644
--- a/pugl/doc/_static/meson.build
+++ b/subprojects/d2tk/pugl/doc/_static/meson.build
diff --git a/pugl/doc/c/Doxyfile.in b/subprojects/d2tk/pugl/doc/c/Doxyfile.in
index 96bbf63..96bbf63 100644
--- a/pugl/doc/c/Doxyfile.in
+++ b/subprojects/d2tk/pugl/doc/c/Doxyfile.in
diff --git a/pugl/doc/c/api/meson.build b/subprojects/d2tk/pugl/doc/c/api/meson.build
index 5c1e30e..5c1e30e 100644
--- a/pugl/doc/c/api/meson.build
+++ b/subprojects/d2tk/pugl/doc/c/api/meson.build
diff --git a/pugl/doc/c/event-loop.rst b/subprojects/d2tk/pugl/doc/c/event-loop.rst
index 3b9915f..3b9915f 100644
--- a/pugl/doc/c/event-loop.rst
+++ b/subprojects/d2tk/pugl/doc/c/event-loop.rst
diff --git a/pugl/doc/c/events.rst b/subprojects/d2tk/pugl/doc/c/events.rst
index bf964db..bf964db 100644
--- a/pugl/doc/c/events.rst
+++ b/subprojects/d2tk/pugl/doc/c/events.rst
diff --git a/pugl/doc/c/index.rst b/subprojects/d2tk/pugl/doc/c/index.rst
index 020cf32..020cf32 100644
--- a/pugl/doc/c/index.rst
+++ b/subprojects/d2tk/pugl/doc/c/index.rst
diff --git a/pugl/doc/c/meson.build b/subprojects/d2tk/pugl/doc/c/meson.build
index df9363e..df9363e 100644
--- a/pugl/doc/c/meson.build
+++ b/subprojects/d2tk/pugl/doc/c/meson.build
diff --git a/pugl/doc/c/overview.rst b/subprojects/d2tk/pugl/doc/c/overview.rst
index 4bd024d..4bd024d 100644
--- a/pugl/doc/c/overview.rst
+++ b/subprojects/d2tk/pugl/doc/c/overview.rst
diff --git a/pugl/doc/c/shutting-down.rst b/subprojects/d2tk/pugl/doc/c/shutting-down.rst
index dfb56cd..dfb56cd 100644
--- a/pugl/doc/c/shutting-down.rst
+++ b/subprojects/d2tk/pugl/doc/c/shutting-down.rst
diff --git a/pugl/doc/c/view.rst b/subprojects/d2tk/pugl/doc/c/view.rst
index 12f146d..12f146d 100644
--- a/pugl/doc/c/view.rst
+++ b/subprojects/d2tk/pugl/doc/c/view.rst
diff --git a/pugl/doc/c/world.rst b/subprojects/d2tk/pugl/doc/c/world.rst
index 83d9dbd..83d9dbd 100644
--- a/pugl/doc/c/world.rst
+++ b/subprojects/d2tk/pugl/doc/c/world.rst
diff --git a/pugl/doc/c/xml/meson.build b/subprojects/d2tk/pugl/doc/c/xml/meson.build
index d79d59a..d79d59a 100644
--- a/pugl/doc/c/xml/meson.build
+++ b/subprojects/d2tk/pugl/doc/c/xml/meson.build
diff --git a/pugl/doc/conf.py.in b/subprojects/d2tk/pugl/doc/conf.py.in
index 3fa8ea2..3fa8ea2 100644
--- a/pugl/doc/conf.py.in
+++ b/subprojects/d2tk/pugl/doc/conf.py.in
diff --git a/pugl/doc/cpp/Doxyfile.in b/subprojects/d2tk/pugl/doc/cpp/Doxyfile.in
index 889ac0b..889ac0b 100644
--- a/pugl/doc/cpp/Doxyfile.in
+++ b/subprojects/d2tk/pugl/doc/cpp/Doxyfile.in
diff --git a/pugl/doc/cpp/api/meson.build b/subprojects/d2tk/pugl/doc/cpp/api/meson.build
index 4bbbec2..4bbbec2 100644
--- a/pugl/doc/cpp/api/meson.build
+++ b/subprojects/d2tk/pugl/doc/cpp/api/meson.build
diff --git a/pugl/doc/cpp/event-loop.rst b/subprojects/d2tk/pugl/doc/cpp/event-loop.rst
index 1d2ac41..1d2ac41 100644
--- a/pugl/doc/cpp/event-loop.rst
+++ b/subprojects/d2tk/pugl/doc/cpp/event-loop.rst
diff --git a/pugl/doc/cpp/events.rst b/subprojects/d2tk/pugl/doc/cpp/events.rst
index 72c396c..72c396c 100644
--- a/pugl/doc/cpp/events.rst
+++ b/subprojects/d2tk/pugl/doc/cpp/events.rst
diff --git a/pugl/doc/cpp/index.rst b/subprojects/d2tk/pugl/doc/cpp/index.rst
index b11d028..b11d028 100644
--- a/pugl/doc/cpp/index.rst
+++ b/subprojects/d2tk/pugl/doc/cpp/index.rst
diff --git a/pugl/doc/cpp/meson.build b/subprojects/d2tk/pugl/doc/cpp/meson.build
index d8bae11..d8bae11 100644
--- a/pugl/doc/cpp/meson.build
+++ b/subprojects/d2tk/pugl/doc/cpp/meson.build
diff --git a/pugl/doc/cpp/overview.rst b/subprojects/d2tk/pugl/doc/cpp/overview.rst
index 1928fba..1928fba 100644
--- a/pugl/doc/cpp/overview.rst
+++ b/subprojects/d2tk/pugl/doc/cpp/overview.rst
diff --git a/pugl/doc/cpp/view.rst b/subprojects/d2tk/pugl/doc/cpp/view.rst
index 3f5aee8..3f5aee8 100644
--- a/pugl/doc/cpp/view.rst
+++ b/subprojects/d2tk/pugl/doc/cpp/view.rst
diff --git a/pugl/doc/cpp/world.rst b/subprojects/d2tk/pugl/doc/cpp/world.rst
index 1a3b432..1a3b432 100644
--- a/pugl/doc/cpp/world.rst
+++ b/subprojects/d2tk/pugl/doc/cpp/world.rst
diff --git a/pugl/doc/cpp/xml/meson.build b/subprojects/d2tk/pugl/doc/cpp/xml/meson.build
index 3f87f2a..3f87f2a 100644
--- a/pugl/doc/cpp/xml/meson.build
+++ b/subprojects/d2tk/pugl/doc/cpp/xml/meson.build
diff --git a/pugl/doc/deployment.rst b/subprojects/d2tk/pugl/doc/deployment.rst
index 4afc51a..4afc51a 100644
--- a/pugl/doc/deployment.rst
+++ b/subprojects/d2tk/pugl/doc/deployment.rst
diff --git a/pugl/doc/meson.build b/subprojects/d2tk/pugl/doc/meson.build
index 583f09d..583f09d 100644
--- a/pugl/doc/meson.build
+++ b/subprojects/d2tk/pugl/doc/meson.build
diff --git a/pugl/doc/summary.rst b/subprojects/d2tk/pugl/doc/summary.rst
index f05515f..f05515f 100644
--- a/pugl/doc/summary.rst
+++ b/subprojects/d2tk/pugl/doc/summary.rst
diff --git a/pugl/examples/.clang-tidy b/subprojects/d2tk/pugl/examples/.clang-tidy
index fdfa4ea..fdfa4ea 100644
--- a/pugl/examples/.clang-tidy
+++ b/subprojects/d2tk/pugl/examples/.clang-tidy
diff --git a/pugl/examples/cube_view.h b/subprojects/d2tk/pugl/examples/cube_view.h
index 71ae88d..71ae88d 100644
--- a/pugl/examples/cube_view.h
+++ b/subprojects/d2tk/pugl/examples/cube_view.h
diff --git a/pugl/examples/demo_utils.h b/subprojects/d2tk/pugl/examples/demo_utils.h
index 2dda756..2dda756 100644
--- a/pugl/examples/demo_utils.h
+++ b/subprojects/d2tk/pugl/examples/demo_utils.h
diff --git a/pugl/examples/file_utils.c b/subprojects/d2tk/pugl/examples/file_utils.c
index 8ecbca4..8ecbca4 100644
--- a/pugl/examples/file_utils.c
+++ b/subprojects/d2tk/pugl/examples/file_utils.c
diff --git a/pugl/examples/file_utils.h b/subprojects/d2tk/pugl/examples/file_utils.h
index 1530157..1530157 100644
--- a/pugl/examples/file_utils.h
+++ b/subprojects/d2tk/pugl/examples/file_utils.h
diff --git a/pugl/examples/glad/glad.c b/subprojects/d2tk/pugl/examples/glad/glad.c
index 38f442c..38f442c 100644
--- a/pugl/examples/glad/glad.c
+++ b/subprojects/d2tk/pugl/examples/glad/glad.c
diff --git a/pugl/examples/glad/glad.h b/subprojects/d2tk/pugl/examples/glad/glad.h
index 9efb229..9efb229 100644
--- a/pugl/examples/glad/glad.h
+++ b/subprojects/d2tk/pugl/examples/glad/glad.h
diff --git a/pugl/examples/glad/khrplatform.h b/subprojects/d2tk/pugl/examples/glad/khrplatform.h
index 5b55ea2..5b55ea2 100644
--- a/pugl/examples/glad/khrplatform.h
+++ b/subprojects/d2tk/pugl/examples/glad/khrplatform.h
diff --git a/pugl/examples/meson.build b/subprojects/d2tk/pugl/examples/meson.build
index d455faf..d455faf 100644
--- a/pugl/examples/meson.build
+++ b/subprojects/d2tk/pugl/examples/meson.build
diff --git a/pugl/examples/pugl_cairo_demo.c b/subprojects/d2tk/pugl/examples/pugl_cairo_demo.c
index 67bc13c..67bc13c 100644
--- a/pugl/examples/pugl_cairo_demo.c
+++ b/subprojects/d2tk/pugl/examples/pugl_cairo_demo.c
diff --git a/pugl/examples/pugl_cursor_demo.c b/subprojects/d2tk/pugl/examples/pugl_cursor_demo.c
index 97e3b9f..97e3b9f 100644
--- a/pugl/examples/pugl_cursor_demo.c
+++ b/subprojects/d2tk/pugl/examples/pugl_cursor_demo.c
diff --git a/pugl/examples/pugl_cxx_demo.cpp b/subprojects/d2tk/pugl/examples/pugl_cxx_demo.cpp
index d663a3f..d663a3f 100644
--- a/pugl/examples/pugl_cxx_demo.cpp
+++ b/subprojects/d2tk/pugl/examples/pugl_cxx_demo.cpp
diff --git a/pugl/examples/pugl_embed_demo.c b/subprojects/d2tk/pugl/examples/pugl_embed_demo.c
index 0e12ddb..0e12ddb 100644
--- a/pugl/examples/pugl_embed_demo.c
+++ b/subprojects/d2tk/pugl/examples/pugl_embed_demo.c
diff --git a/pugl/examples/pugl_print_events.c b/subprojects/d2tk/pugl/examples/pugl_print_events.c
index dfa217e..dfa217e 100644
--- a/pugl/examples/pugl_print_events.c
+++ b/subprojects/d2tk/pugl/examples/pugl_print_events.c
diff --git a/pugl/examples/pugl_shader_demo.c b/subprojects/d2tk/pugl/examples/pugl_shader_demo.c
index aa5c38e..aa5c38e 100644
--- a/pugl/examples/pugl_shader_demo.c
+++ b/subprojects/d2tk/pugl/examples/pugl_shader_demo.c
diff --git a/pugl/examples/pugl_vulkan_cxx_demo.cpp b/subprojects/d2tk/pugl/examples/pugl_vulkan_cxx_demo.cpp
index d92e652..d92e652 100644
--- a/pugl/examples/pugl_vulkan_cxx_demo.cpp
+++ b/subprojects/d2tk/pugl/examples/pugl_vulkan_cxx_demo.cpp
diff --git a/pugl/examples/pugl_vulkan_demo.c b/subprojects/d2tk/pugl/examples/pugl_vulkan_demo.c
index 0dfbadd..0dfbadd 100644
--- a/pugl/examples/pugl_vulkan_demo.c
+++ b/subprojects/d2tk/pugl/examples/pugl_vulkan_demo.c
diff --git a/pugl/examples/pugl_window_demo.c b/subprojects/d2tk/pugl/examples/pugl_window_demo.c
index f7d5b2c..f7d5b2c 100644
--- a/pugl/examples/pugl_window_demo.c
+++ b/subprojects/d2tk/pugl/examples/pugl_window_demo.c
diff --git a/pugl/examples/rects.h b/subprojects/d2tk/pugl/examples/rects.h
index b99d9f0..b99d9f0 100644
--- a/pugl/examples/rects.h
+++ b/subprojects/d2tk/pugl/examples/rects.h
diff --git a/pugl/examples/shader_utils.h b/subprojects/d2tk/pugl/examples/shader_utils.h
index 2575f47..2575f47 100644
--- a/pugl/examples/shader_utils.h
+++ b/subprojects/d2tk/pugl/examples/shader_utils.h
diff --git a/pugl/examples/shaders/header_330.glsl b/subprojects/d2tk/pugl/examples/shaders/header_330.glsl
index 59d5f6f..59d5f6f 100644
--- a/pugl/examples/shaders/header_330.glsl
+++ b/subprojects/d2tk/pugl/examples/shaders/header_330.glsl
diff --git a/pugl/examples/shaders/header_420.glsl b/subprojects/d2tk/pugl/examples/shaders/header_420.glsl
index 2beaad0..2beaad0 100644
--- a/pugl/examples/shaders/header_420.glsl
+++ b/subprojects/d2tk/pugl/examples/shaders/header_420.glsl
diff --git a/pugl/examples/shaders/meson.build b/subprojects/d2tk/pugl/examples/shaders/meson.build
index e47be9d..e47be9d 100644
--- a/pugl/examples/shaders/meson.build
+++ b/subprojects/d2tk/pugl/examples/shaders/meson.build
diff --git a/pugl/examples/shaders/rect.frag b/subprojects/d2tk/pugl/examples/shaders/rect.frag
index 33bfbb2..33bfbb2 100644
--- a/pugl/examples/shaders/rect.frag
+++ b/subprojects/d2tk/pugl/examples/shaders/rect.frag
diff --git a/pugl/examples/shaders/rect.vert b/subprojects/d2tk/pugl/examples/shaders/rect.vert
index 2c7b5f1..2c7b5f1 100644
--- a/pugl/examples/shaders/rect.vert
+++ b/subprojects/d2tk/pugl/examples/shaders/rect.vert
diff --git a/pugl/examples/sybok.hpp b/subprojects/d2tk/pugl/examples/sybok.hpp
index 7740824..7740824 100644
--- a/pugl/examples/sybok.hpp
+++ b/subprojects/d2tk/pugl/examples/sybok.hpp
diff --git a/pugl/include/.clang-tidy b/subprojects/d2tk/pugl/include/.clang-tidy
index dd2fd47..dd2fd47 100644
--- a/pugl/include/.clang-tidy
+++ b/subprojects/d2tk/pugl/include/.clang-tidy
diff --git a/pugl/include/pugl/cairo.h b/subprojects/d2tk/pugl/include/pugl/cairo.h
index 48e868e..48e868e 100644
--- a/pugl/include/pugl/cairo.h
+++ b/subprojects/d2tk/pugl/include/pugl/cairo.h
diff --git a/pugl/include/pugl/gl.h b/subprojects/d2tk/pugl/include/pugl/gl.h
index 51c4a7d..51c4a7d 100644
--- a/pugl/include/pugl/gl.h
+++ b/subprojects/d2tk/pugl/include/pugl/gl.h
diff --git a/pugl/include/pugl/pugl.h b/subprojects/d2tk/pugl/include/pugl/pugl.h
index cd77334..cd77334 100644
--- a/pugl/include/pugl/pugl.h
+++ b/subprojects/d2tk/pugl/include/pugl/pugl.h
diff --git a/pugl/include/pugl/stub.h b/subprojects/d2tk/pugl/include/pugl/stub.h
index d1a699a..d1a699a 100644
--- a/pugl/include/pugl/stub.h
+++ b/subprojects/d2tk/pugl/include/pugl/stub.h
diff --git a/pugl/include/pugl/vulkan.h b/subprojects/d2tk/pugl/include/pugl/vulkan.h
index f12ad97..f12ad97 100644
--- a/pugl/include/pugl/vulkan.h
+++ b/subprojects/d2tk/pugl/include/pugl/vulkan.h
diff --git a/pugl/meson.build b/subprojects/d2tk/pugl/meson.build
index 02fae17..02fae17 100644
--- a/pugl/meson.build
+++ b/subprojects/d2tk/pugl/meson.build
diff --git a/pugl/meson/meson.build b/subprojects/d2tk/pugl/meson/meson.build
index 20e0522..20e0522 100644
--- a/pugl/meson/meson.build
+++ b/subprojects/d2tk/pugl/meson/meson.build
diff --git a/pugl/meson_options.txt b/subprojects/d2tk/pugl/meson_options.txt
index dd6ea8c..dd6ea8c 100644
--- a/pugl/meson_options.txt
+++ b/subprojects/d2tk/pugl/meson_options.txt
diff --git a/pugl/pugl.pc.in b/subprojects/d2tk/pugl/pugl.pc.in
index 2cfa644..2cfa644 100644
--- a/pugl/pugl.pc.in
+++ b/subprojects/d2tk/pugl/pugl.pc.in
diff --git a/pugl/resources/Info.plist.in b/subprojects/d2tk/pugl/resources/Info.plist.in
index a08dbd0..a08dbd0 100644
--- a/pugl/resources/Info.plist.in
+++ b/subprojects/d2tk/pugl/resources/Info.plist.in
diff --git a/pugl/resources/pugl.ipe b/subprojects/d2tk/pugl/resources/pugl.ipe
index 238c09c..238c09c 100644
--- a/pugl/resources/pugl.ipe
+++ b/subprojects/d2tk/pugl/resources/pugl.ipe
diff --git a/pugl/resources/pugl.png b/subprojects/d2tk/pugl/resources/pugl.png
index 4641660..4641660 100644
--- a/pugl/resources/pugl.png
+++ b/subprojects/d2tk/pugl/resources/pugl.png
Binary files differ
diff --git a/pugl/resources/pugl.svg b/subprojects/d2tk/pugl/resources/pugl.svg
index 93189f4..93189f4 100644
--- a/pugl/resources/pugl.svg
+++ b/subprojects/d2tk/pugl/resources/pugl.svg
diff --git a/pugl/scripts/cat.py b/subprojects/d2tk/pugl/scripts/cat.py
index 5f628b6..5f628b6 100755
--- a/pugl/scripts/cat.py
+++ b/subprojects/d2tk/pugl/scripts/cat.py
diff --git a/pugl/scripts/dox_to_sphinx.py b/subprojects/d2tk/pugl/scripts/dox_to_sphinx.py
index c3f7c44..c3f7c44 100755
--- a/pugl/scripts/dox_to_sphinx.py
+++ b/subprojects/d2tk/pugl/scripts/dox_to_sphinx.py
diff --git a/pugl/src/.clang-tidy b/subprojects/d2tk/pugl/src/.clang-tidy
index 11b620e..11b620e 100644
--- a/pugl/src/.clang-tidy
+++ b/subprojects/d2tk/pugl/src/.clang-tidy
diff --git a/pugl/src/implementation.c b/subprojects/d2tk/pugl/src/implementation.c
index 47b52b8..47b52b8 100644
--- a/pugl/src/implementation.c
+++ b/subprojects/d2tk/pugl/src/implementation.c
diff --git a/pugl/src/implementation.h b/subprojects/d2tk/pugl/src/implementation.h
index 8c5398c..8c5398c 100644
--- a/pugl/src/implementation.h
+++ b/subprojects/d2tk/pugl/src/implementation.h
diff --git a/pugl/src/mac.h b/subprojects/d2tk/pugl/src/mac.h
index 35e6e0d..35e6e0d 100644
--- a/pugl/src/mac.h
+++ b/subprojects/d2tk/pugl/src/mac.h
diff --git a/pugl/src/mac.m b/subprojects/d2tk/pugl/src/mac.m
index 58f16c7..58f16c7 100644
--- a/pugl/src/mac.m
+++ b/subprojects/d2tk/pugl/src/mac.m
diff --git a/pugl/src/mac_cairo.m b/subprojects/d2tk/pugl/src/mac_cairo.m
index 1c564a0..1c564a0 100644
--- a/pugl/src/mac_cairo.m
+++ b/subprojects/d2tk/pugl/src/mac_cairo.m
diff --git a/pugl/src/mac_gl.m b/subprojects/d2tk/pugl/src/mac_gl.m
index dd06cc0..dd06cc0 100644
--- a/pugl/src/mac_gl.m
+++ b/subprojects/d2tk/pugl/src/mac_gl.m
diff --git a/pugl/src/mac_stub.m b/subprojects/d2tk/pugl/src/mac_stub.m
index ac7bfcc..ac7bfcc 100644
--- a/pugl/src/mac_stub.m
+++ b/subprojects/d2tk/pugl/src/mac_stub.m
diff --git a/pugl/src/mac_vulkan.m b/subprojects/d2tk/pugl/src/mac_vulkan.m
index 22fff10..22fff10 100644
--- a/pugl/src/mac_vulkan.m
+++ b/subprojects/d2tk/pugl/src/mac_vulkan.m
diff --git a/pugl/src/stub.h b/subprojects/d2tk/pugl/src/stub.h
index c816679..c816679 100644
--- a/pugl/src/stub.h
+++ b/subprojects/d2tk/pugl/src/stub.h
diff --git a/pugl/src/types.h b/subprojects/d2tk/pugl/src/types.h
index 6fa658f..6fa658f 100644
--- a/pugl/src/types.h
+++ b/subprojects/d2tk/pugl/src/types.h
diff --git a/pugl/src/win.c b/subprojects/d2tk/pugl/src/win.c
index 1e11520..1e11520 100644
--- a/pugl/src/win.c
+++ b/subprojects/d2tk/pugl/src/win.c
diff --git a/pugl/src/win.h b/subprojects/d2tk/pugl/src/win.h
index ccab36a..ccab36a 100644
--- a/pugl/src/win.h
+++ b/subprojects/d2tk/pugl/src/win.h
diff --git a/pugl/src/win_cairo.c b/subprojects/d2tk/pugl/src/win_cairo.c
index 9dc5ce0..9dc5ce0 100644
--- a/pugl/src/win_cairo.c
+++ b/subprojects/d2tk/pugl/src/win_cairo.c
diff --git a/pugl/src/win_gl.c b/subprojects/d2tk/pugl/src/win_gl.c
index 4abd5ab..4abd5ab 100644
--- a/pugl/src/win_gl.c
+++ b/subprojects/d2tk/pugl/src/win_gl.c
diff --git a/pugl/src/win_stub.c b/subprojects/d2tk/pugl/src/win_stub.c
index cf86390..cf86390 100644
--- a/pugl/src/win_stub.c
+++ b/subprojects/d2tk/pugl/src/win_stub.c
diff --git a/pugl/src/win_vulkan.c b/subprojects/d2tk/pugl/src/win_vulkan.c
index a892a16..a892a16 100644
--- a/pugl/src/win_vulkan.c
+++ b/subprojects/d2tk/pugl/src/win_vulkan.c
diff --git a/pugl/src/x11.c b/subprojects/d2tk/pugl/src/x11.c
index 2cdb0f3..2cdb0f3 100644
--- a/pugl/src/x11.c
+++ b/subprojects/d2tk/pugl/src/x11.c
diff --git a/pugl/src/x11.h b/subprojects/d2tk/pugl/src/x11.h
index 778e5ec..778e5ec 100644
--- a/pugl/src/x11.h
+++ b/subprojects/d2tk/pugl/src/x11.h
diff --git a/pugl/src/x11_cairo.c b/subprojects/d2tk/pugl/src/x11_cairo.c
index a0e7d08..a0e7d08 100644
--- a/pugl/src/x11_cairo.c
+++ b/subprojects/d2tk/pugl/src/x11_cairo.c
diff --git a/pugl/src/x11_gl.c b/subprojects/d2tk/pugl/src/x11_gl.c
index 34152de..34152de 100644
--- a/pugl/src/x11_gl.c
+++ b/subprojects/d2tk/pugl/src/x11_gl.c
diff --git a/pugl/src/x11_stub.c b/subprojects/d2tk/pugl/src/x11_stub.c
index de89a86..de89a86 100644
--- a/pugl/src/x11_stub.c
+++ b/subprojects/d2tk/pugl/src/x11_stub.c
diff --git a/pugl/src/x11_vulkan.c b/subprojects/d2tk/pugl/src/x11_vulkan.c
index 1ff5759..1ff5759 100644
--- a/pugl/src/x11_vulkan.c
+++ b/subprojects/d2tk/pugl/src/x11_vulkan.c
diff --git a/pugl/test/.clang-tidy b/subprojects/d2tk/pugl/test/.clang-tidy
index 8014e6a..8014e6a 100644
--- a/pugl/test/.clang-tidy
+++ b/subprojects/d2tk/pugl/test/.clang-tidy
diff --git a/pugl/test/meson.build b/subprojects/d2tk/pugl/test/meson.build
index 340a7dd..340a7dd 100644
--- a/pugl/test/meson.build
+++ b/subprojects/d2tk/pugl/test/meson.build
diff --git a/pugl/test/test_build.c b/subprojects/d2tk/pugl/test/test_build.c
index 957e0bd..957e0bd 100644
--- a/pugl/test/test_build.c
+++ b/subprojects/d2tk/pugl/test/test_build.c
diff --git a/pugl/test/test_build.cpp b/subprojects/d2tk/pugl/test/test_build.cpp
index 20235e8..20235e8 100644
--- a/pugl/test/test_build.cpp
+++ b/subprojects/d2tk/pugl/test/test_build.cpp
diff --git a/pugl/test/test_clipboard.c b/subprojects/d2tk/pugl/test/test_clipboard.c
index a458d00..a458d00 100644
--- a/pugl/test/test_clipboard.c
+++ b/subprojects/d2tk/pugl/test/test_clipboard.c
diff --git a/pugl/test/test_gl_hints.c b/subprojects/d2tk/pugl/test/test_gl_hints.c
index 3be3651..3be3651 100644
--- a/pugl/test/test_gl_hints.c
+++ b/subprojects/d2tk/pugl/test/test_gl_hints.c
diff --git a/pugl/test/test_realize.c b/subprojects/d2tk/pugl/test/test_realize.c
index 3c4e09a..3c4e09a 100644
--- a/pugl/test/test_realize.c
+++ b/subprojects/d2tk/pugl/test/test_realize.c
diff --git a/pugl/test/test_redisplay.c b/subprojects/d2tk/pugl/test/test_redisplay.c
index c5b9887..c5b9887 100644
--- a/pugl/test/test_redisplay.c
+++ b/subprojects/d2tk/pugl/test/test_redisplay.c
diff --git a/pugl/test/test_show_hide.c b/subprojects/d2tk/pugl/test/test_show_hide.c
index 584448c..584448c 100644
--- a/pugl/test/test_show_hide.c
+++ b/subprojects/d2tk/pugl/test/test_show_hide.c
diff --git a/pugl/test/test_stub_hints.c b/subprojects/d2tk/pugl/test/test_stub_hints.c
index 1cc1180..1cc1180 100644
--- a/pugl/test/test_stub_hints.c
+++ b/subprojects/d2tk/pugl/test/test_stub_hints.c
diff --git a/pugl/test/test_timer.c b/subprojects/d2tk/pugl/test/test_timer.c
index 200ddc2..200ddc2 100644
--- a/pugl/test/test_timer.c
+++ b/subprojects/d2tk/pugl/test/test_timer.c
diff --git a/pugl/test/test_update.c b/subprojects/d2tk/pugl/test/test_update.c
index 89de0b6..89de0b6 100644
--- a/pugl/test/test_update.c
+++ b/subprojects/d2tk/pugl/test/test_update.c
diff --git a/pugl/test/test_utils.h b/subprojects/d2tk/pugl/test/test_utils.h
index 2464737..2464737 100644
--- a/pugl/test/test_utils.h
+++ b/subprojects/d2tk/pugl/test/test_utils.h
diff --git a/subprojects/d2tk/screenshots/screenshot_1.png b/subprojects/d2tk/screenshots/screenshot_1.png
new file mode 100644
index 0000000..461d52a
--- /dev/null
+++ b/subprojects/d2tk/screenshots/screenshot_1.png
Binary files differ
diff --git a/screenshots/screenshot_2.png b/subprojects/d2tk/screenshots/screenshot_2.png
index 5b40bb8..5b40bb8 100644
--- a/screenshots/screenshot_2.png
+++ b/subprojects/d2tk/screenshots/screenshot_2.png
Binary files differ
diff --git a/screenshots/screenshot_3.png b/subprojects/d2tk/screenshots/screenshot_3.png
index 13b9843..13b9843 100644
--- a/screenshots/screenshot_3.png
+++ b/subprojects/d2tk/screenshots/screenshot_3.png
Binary files differ
diff --git a/screenshots/screenshot_4.png b/subprojects/d2tk/screenshots/screenshot_4.png
index 8cd8329..8cd8329 100644
--- a/screenshots/screenshot_4.png
+++ b/subprojects/d2tk/screenshots/screenshot_4.png
Binary files differ
diff --git a/screenshots/screenshot_5.png b/subprojects/d2tk/screenshots/screenshot_5.png
index 202ab61..202ab61 100644
--- a/screenshots/screenshot_5.png
+++ b/subprojects/d2tk/screenshots/screenshot_5.png
Binary files differ
diff --git a/screenshots/screenshot_6.png b/subprojects/d2tk/screenshots/screenshot_6.png
index 10e5235..10e5235 100644
--- a/screenshots/screenshot_6.png
+++ b/subprojects/d2tk/screenshots/screenshot_6.png
Binary files differ
diff --git a/screenshots/screenshot_7.png b/subprojects/d2tk/screenshots/screenshot_7.png
index 4850e2c..4850e2c 100644
--- a/screenshots/screenshot_7.png
+++ b/subprojects/d2tk/screenshots/screenshot_7.png
Binary files differ
diff --git a/screenshots/screenshot_8.png b/subprojects/d2tk/screenshots/screenshot_8.png
index d474786..d474786 100644
--- a/screenshots/screenshot_8.png
+++ b/subprojects/d2tk/screenshots/screenshot_8.png
Binary files differ
diff --git a/src/backend_cairo.c b/subprojects/d2tk/src/backend_cairo.c
index 4e17769..4e17769 100644
--- a/src/backend_cairo.c
+++ b/subprojects/d2tk/src/backend_cairo.c
diff --git a/src/backend_nanovg.c b/subprojects/d2tk/src/backend_nanovg.c
index 4787164..4787164 100644
--- a/src/backend_nanovg.c
+++ b/subprojects/d2tk/src/backend_nanovg.c
diff --git a/src/base.c b/subprojects/d2tk/src/base.c
index ebd59c6..ebd59c6 100644
--- a/src/base.c
+++ b/subprojects/d2tk/src/base.c
diff --git a/src/base_bar.c b/subprojects/d2tk/src/base_bar.c
index 67cbfb4..67cbfb4 100644
--- a/src/base_bar.c
+++ b/subprojects/d2tk/src/base_bar.c
diff --git a/src/base_bitmap.c b/subprojects/d2tk/src/base_bitmap.c
index a542169..a542169 100644
--- a/src/base_bitmap.c
+++ b/subprojects/d2tk/src/base_bitmap.c
diff --git a/src/base_button.c b/subprojects/d2tk/src/base_button.c
index 244e7d1..244e7d1 100644
--- a/src/base_button.c
+++ b/subprojects/d2tk/src/base_button.c
diff --git a/src/base_combo.c b/subprojects/d2tk/src/base_combo.c
index 3ab073c..3ab073c 100644
--- a/src/base_combo.c
+++ b/subprojects/d2tk/src/base_combo.c
diff --git a/src/base_cursor.c b/subprojects/d2tk/src/base_cursor.c
index e9be4ed..e9be4ed 100644
--- a/src/base_cursor.c
+++ b/subprojects/d2tk/src/base_cursor.c
diff --git a/src/base_custom.c b/subprojects/d2tk/src/base_custom.c
index 2396530..2396530 100644
--- a/src/base_custom.c
+++ b/subprojects/d2tk/src/base_custom.c
diff --git a/src/base_dial.c b/subprojects/d2tk/src/base_dial.c
index ae3c97a..ae3c97a 100644
--- a/src/base_dial.c
+++ b/subprojects/d2tk/src/base_dial.c
diff --git a/src/base_flowmatrix.c b/subprojects/d2tk/src/base_flowmatrix.c
index 0edc1c5..0edc1c5 100644
--- a/src/base_flowmatrix.c
+++ b/subprojects/d2tk/src/base_flowmatrix.c
diff --git a/src/base_frame.c b/subprojects/d2tk/src/base_frame.c
index d78bdf0..d78bdf0 100644
--- a/src/base_frame.c
+++ b/subprojects/d2tk/src/base_frame.c
diff --git a/src/base_image.c b/subprojects/d2tk/src/base_image.c
index cfed2d1..cfed2d1 100644
--- a/src/base_image.c
+++ b/subprojects/d2tk/src/base_image.c
diff --git a/src/base_internal.h b/subprojects/d2tk/src/base_internal.h
index f801418..f801418 100644
--- a/src/base_internal.h
+++ b/subprojects/d2tk/src/base_internal.h
diff --git a/src/base_label.c b/subprojects/d2tk/src/base_label.c
index 64cbd4b..64cbd4b 100644
--- a/src/base_label.c
+++ b/subprojects/d2tk/src/base_label.c
diff --git a/src/base_layout.c b/subprojects/d2tk/src/base_layout.c
index 770e5df..770e5df 100644
--- a/src/base_layout.c
+++ b/subprojects/d2tk/src/base_layout.c
diff --git a/src/base_lineedit.c b/subprojects/d2tk/src/base_lineedit.c
index dc741a4..dc741a4 100644
--- a/src/base_lineedit.c
+++ b/subprojects/d2tk/src/base_lineedit.c
diff --git a/src/base_link.c b/subprojects/d2tk/src/base_link.c
index 355d394..355d394 100644
--- a/src/base_link.c
+++ b/subprojects/d2tk/src/base_link.c
diff --git a/src/base_meter.c b/subprojects/d2tk/src/base_meter.c
index 6d8d080..6d8d080 100644
--- a/src/base_meter.c
+++ b/subprojects/d2tk/src/base_meter.c
diff --git a/src/base_pane.c b/subprojects/d2tk/src/base_pane.c
index 07b6b5d..07b6b5d 100644
--- a/src/base_pane.c
+++ b/subprojects/d2tk/src/base_pane.c
diff --git a/src/base_pty.c b/subprojects/d2tk/src/base_pty.c
index b380a4c..b380a4c 100644
--- a/src/base_pty.c
+++ b/subprojects/d2tk/src/base_pty.c
diff --git a/src/base_scrollbar.c b/subprojects/d2tk/src/base_scrollbar.c
index c98b038..c98b038 100644
--- a/src/base_scrollbar.c
+++ b/subprojects/d2tk/src/base_scrollbar.c
diff --git a/src/base_separator.c b/subprojects/d2tk/src/base_separator.c
index f45afe3..f45afe3 100644
--- a/src/base_separator.c
+++ b/subprojects/d2tk/src/base_separator.c
diff --git a/src/base_spinner.c b/subprojects/d2tk/src/base_spinner.c
index e2c821c..e2c821c 100644
--- a/src/base_spinner.c
+++ b/subprojects/d2tk/src/base_spinner.c
diff --git a/src/base_table.c b/subprojects/d2tk/src/base_table.c
index eefa6e1..eefa6e1 100644
--- a/src/base_table.c
+++ b/subprojects/d2tk/src/base_table.c
diff --git a/src/base_textfield.c b/subprojects/d2tk/src/base_textfield.c
index 9d0fbc1..9d0fbc1 100644
--- a/src/base_textfield.c
+++ b/subprojects/d2tk/src/base_textfield.c
diff --git a/src/base_tooltip.c b/subprojects/d2tk/src/base_tooltip.c
index d105470..d105470 100644
--- a/src/base_tooltip.c
+++ b/subprojects/d2tk/src/base_tooltip.c
diff --git a/src/base_vkb.c b/subprojects/d2tk/src/base_vkb.c
index bbe65c6..bbe65c6 100644
--- a/src/base_vkb.c
+++ b/subprojects/d2tk/src/base_vkb.c
diff --git a/src/base_wave.c b/subprojects/d2tk/src/base_wave.c
index b9dddc6..b9dddc6 100644
--- a/src/base_wave.c
+++ b/subprojects/d2tk/src/base_wave.c
diff --git a/src/core.c b/subprojects/d2tk/src/core.c
index 5383776..5383776 100644
--- a/src/core.c
+++ b/subprojects/d2tk/src/core.c
diff --git a/src/core_internal.h b/subprojects/d2tk/src/core_internal.h
index d07252b..d07252b 100644
--- a/src/core_internal.h
+++ b/subprojects/d2tk/src/core_internal.h
diff --git a/src/frontend_fbdev.c b/subprojects/d2tk/src/frontend_fbdev.c
index 3ae87ed..3ae87ed 100644
--- a/src/frontend_fbdev.c
+++ b/subprojects/d2tk/src/frontend_fbdev.c
diff --git a/src/frontend_glfw.c b/subprojects/d2tk/src/frontend_glfw.c
index 6950c00..6950c00 100644
--- a/src/frontend_glfw.c
+++ b/subprojects/d2tk/src/frontend_glfw.c
diff --git a/src/frontend_pugl.c b/subprojects/d2tk/src/frontend_pugl.c
index 62ade95..62ade95 100644
--- a/src/frontend_pugl.c
+++ b/subprojects/d2tk/src/frontend_pugl.c
diff --git a/src/hash.c b/subprojects/d2tk/src/hash.c
index f02b7f2..f02b7f2 100644
--- a/src/hash.c
+++ b/subprojects/d2tk/src/hash.c
diff --git a/src/mum.h b/subprojects/d2tk/src/mum.h
index e42e2d7..e42e2d7 100644
--- a/src/mum.h
+++ b/subprojects/d2tk/src/mum.h
diff --git a/src/util_spawn.c b/subprojects/d2tk/src/util_spawn.c
index 28aff32..28aff32 100644
--- a/src/util_spawn.c
+++ b/subprojects/d2tk/src/util_spawn.c
diff --git a/test/base.c b/subprojects/d2tk/test/base.c
index cfb31a2..cfb31a2 100644
--- a/test/base.c
+++ b/subprojects/d2tk/test/base.c
diff --git a/test/core.c b/subprojects/d2tk/test/core.c
index 5389ed3..5389ed3 100644
--- a/test/core.c
+++ b/subprojects/d2tk/test/core.c
diff --git a/test/mock.c b/subprojects/d2tk/test/mock.c
index 5986109..5986109 100644
--- a/test/mock.c
+++ b/subprojects/d2tk/test/mock.c
diff --git a/test/mock.h b/subprojects/d2tk/test/mock.h
index 8b18ddb..8b18ddb 100644
--- a/test/mock.h
+++ b/subprojects/d2tk/test/mock.h
diff --git a/ttf/FiraCode-Bold.ttf b/subprojects/d2tk/ttf/FiraCode-Bold.ttf
index 5030383..5030383 100644
--- a/ttf/FiraCode-Bold.ttf
+++ b/subprojects/d2tk/ttf/FiraCode-Bold.ttf
Binary files differ
diff --git a/ttf/FiraCode-Light.ttf b/subprojects/d2tk/ttf/FiraCode-Light.ttf
index 95913af..95913af 100644
--- a/ttf/FiraCode-Light.ttf
+++ b/subprojects/d2tk/ttf/FiraCode-Light.ttf
Binary files differ
diff --git a/ttf/FiraCode-Medium.ttf b/subprojects/d2tk/ttf/FiraCode-Medium.ttf
index eca9e18..eca9e18 100644
--- a/ttf/FiraCode-Medium.ttf
+++ b/subprojects/d2tk/ttf/FiraCode-Medium.ttf
Binary files differ
diff --git a/ttf/FiraCode-Regular.ttf b/subprojects/d2tk/ttf/FiraCode-Regular.ttf
index 97c1159..97c1159 100644
--- a/ttf/FiraCode-Regular.ttf
+++ b/subprojects/d2tk/ttf/FiraCode-Regular.ttf
Binary files differ
diff --git a/ttf/FiraSans-Bold.ttf b/subprojects/d2tk/ttf/FiraSans-Bold.ttf
index f03425a..f03425a 100644
--- a/ttf/FiraSans-Bold.ttf
+++ b/subprojects/d2tk/ttf/FiraSans-Bold.ttf
Binary files differ
diff --git a/ttf/LICENSE b/subprojects/d2tk/ttf/LICENSE
index 805e0b3..805e0b3 100644
--- a/ttf/LICENSE
+++ b/subprojects/d2tk/ttf/LICENSE
diff --git a/utf8.h/.clang-format b/subprojects/d2tk/utf8.h/.clang-format
index be1e9c4..be1e9c4 100644
--- a/utf8.h/.clang-format
+++ b/subprojects/d2tk/utf8.h/.clang-format
diff --git a/utf8.h/.gitignore b/subprojects/d2tk/utf8.h/.gitignore
index 378eac2..378eac2 100644
--- a/utf8.h/.gitignore
+++ b/subprojects/d2tk/utf8.h/.gitignore
diff --git a/utf8.h/.travis.yml b/subprojects/d2tk/utf8.h/.travis.yml
index b6711ea..b6711ea 100644
--- a/utf8.h/.travis.yml
+++ b/subprojects/d2tk/utf8.h/.travis.yml
diff --git a/utf8.h/LICENSE b/subprojects/d2tk/utf8.h/LICENSE
index 68a49da..68a49da 100644
--- a/utf8.h/LICENSE
+++ b/subprojects/d2tk/utf8.h/LICENSE
diff --git a/utf8.h/README.md b/subprojects/d2tk/utf8.h/README.md
index 80e806b..80e806b 100644
--- a/utf8.h/README.md
+++ b/subprojects/d2tk/utf8.h/README.md
diff --git a/utf8.h/appveyor.yml b/subprojects/d2tk/utf8.h/appveyor.yml
index 8458679..8458679 100644
--- a/utf8.h/appveyor.yml
+++ b/subprojects/d2tk/utf8.h/appveyor.yml
diff --git a/utf8.h/test/CMakeLists.txt b/subprojects/d2tk/utf8.h/test/CMakeLists.txt
index b00114b..b00114b 100644
--- a/utf8.h/test/CMakeLists.txt
+++ b/subprojects/d2tk/utf8.h/test/CMakeLists.txt
diff --git a/utf8.h/test/main.c b/subprojects/d2tk/utf8.h/test/main.c
index bc28b3c..bc28b3c 100644
--- a/utf8.h/test/main.c
+++ b/subprojects/d2tk/utf8.h/test/main.c
diff --git a/utf8.h/test/test.c b/subprojects/d2tk/utf8.h/test/test.c
index f8cd1d6..f8cd1d6 100644
--- a/utf8.h/test/test.c
+++ b/subprojects/d2tk/utf8.h/test/test.c
diff --git a/utf8.h/test/test.cpp b/subprojects/d2tk/utf8.h/test/test.cpp
index f8cd1d6..f8cd1d6 100644
--- a/utf8.h/test/test.cpp
+++ b/subprojects/d2tk/utf8.h/test/test.cpp
diff --git a/utf8.h/test/utest.h b/subprojects/d2tk/utf8.h/test/utest.h
index 2a58af6..2a58af6 100644
--- a/utf8.h/test/utest.h
+++ b/subprojects/d2tk/utf8.h/test/utest.h
diff --git a/utf8.h/utf8.h b/subprojects/d2tk/utf8.h/utf8.h
index f4c62a0..f4c62a0 100644
--- a/utf8.h/utf8.h
+++ b/subprojects/d2tk/utf8.h/utf8.h