aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitlab-ci.yml23
-rw-r--r--README.md10
-rw-r--r--VERSION2
-rw-r--r--app/synthpod_app.c31
-rw-r--r--app/synthpod_app_mod.c29
-rw-r--r--app/synthpod_app_private.h8
-rw-r--r--app/synthpod_app_state.c42
-rw-r--r--app/synthpod_app_ui.c22
-rw-r--r--app/synthpod_app_worker.c41
-rw-r--r--bin/meson.build13
-rw-r--r--bin/synthpod_alsa.122
-rw-r--r--bin/synthpod_alsa.c95
-rw-r--r--bin/synthpod_bin.c348
-rw-r--r--bin/synthpod_bin.h17
-rw-r--r--bin/synthpod_dummy.115
-rw-r--r--bin/synthpod_dummy.c87
-rw-r--r--bin/synthpod_jack.115
-rw-r--r--bin/synthpod_jack.c122
-rw-r--r--bin/synthpod_sandbox_gtk.c27
-rw-r--r--bin/synthpod_sandbox_kx.c30
-rw-r--r--bin/synthpod_sandbox_qt.cpp24
-rw-r--r--bin/synthpod_sandbox_show.c30
-rw-r--r--bin/synthpod_sandbox_x11.c283
-rw-r--r--bin/synthpod_sandbox_x11_driver.c366
-rw-r--r--bin/synthpod_sandbox_x11_driver.h23
-rw-r--r--bundle/meson.build25
-rw-r--r--bundle/synthpod_bundle.ttl25
-rw-r--r--include/synthpod_app.h9
-rw-r--r--include/synthpod_private.h6
-rw-r--r--meson.build21
-rw-r--r--meson_options.txt32
-rw-r--r--nsmc/nsmc.h988
-rw-r--r--osc.lv2/.gitlab-ci.yml3
-rw-r--r--osc.lv2/VERSION2
-rw-r--r--osc.lv2/osc.lv2/reader.h55
-rw-r--r--osc.lv2/osc.lv2/stream.h35
-rw-r--r--osc.lv2/osc.lv2/util.h126
-rw-r--r--osc.lv2/test/osc_test.c411
-rw-r--r--plugins/manifest.ttl.in8
-rw-r--r--plugins/meson.build31
-rw-r--r--plugins/synthpod_common_d2tk.c70
-rw-r--r--plugins/synthpod_common_nk.c555
-rw-r--r--plugins/synthpod_ui.ttl11
-rw-r--r--pugl/AUTHORS11
-rw-r--r--pugl/README.md28
-rw-r--r--pugl/pugl/cairo_gl.h106
-rw-r--r--pugl/pugl/pugl.h634
-rw-r--r--pugl/pugl/pugl_internal.h273
-rw-r--r--pugl/pugl/pugl_osx.m746
-rw-r--r--pugl/pugl/pugl_win.cpp704
-rw-r--r--pugl/pugl/pugl_x11.c810
-rw-r--r--pugl/pugl_cairo_test.c205
-rw-r--r--pugl/pugl_test.c261
-rwxr-xr-xpugl/wafbin97489 -> 0 bytes
-rw-r--r--pugl/wscript203
-rw-r--r--sandbox_ui.lv2/sandbox_io.h6
-rw-r--r--sandbox_ui.lv2/sandbox_slave.c377
-rw-r--r--sandbox_ui.lv2/sandbox_slave.h13
-rw-r--r--subprojects/d2tk/.gitlab-ci.yml30
-rw-r--r--subprojects/d2tk/README.md16
-rw-r--r--subprojects/d2tk/VERSION2
-rwxr-xr-xsubprojects/d2tk/check_for_font3
-rw-r--r--subprojects/d2tk/d2tk/base.h230
-rw-r--r--subprojects/d2tk/d2tk/config.h.in6
-rw-r--r--subprojects/d2tk/d2tk/core.h8
-rw-r--r--subprojects/d2tk/d2tk/frontend.h63
-rw-r--r--subprojects/d2tk/d2tk/frontend_fbdev.h20
-rw-r--r--subprojects/d2tk/d2tk/frontend_pugl.h29
-rw-r--r--subprojects/d2tk/d2tk/hash.h6
-rw-r--r--subprojects/d2tk/example/d2tk_fbdev.c31
-rw-r--r--subprojects/d2tk/example/d2tk_pugl.c12
-rw-r--r--subprojects/d2tk/example/example.c824
-rw-r--r--subprojects/d2tk/meson.build412
-rw-r--r--subprojects/d2tk/meson_options.txt44
-rw-r--r--subprojects/d2tk/pugl/.clang-format127
-rw-r--r--subprojects/d2tk/pugl/.clang-tidy16
-rw-r--r--subprojects/d2tk/pugl/.gitattributes1
-rw-r--r--subprojects/d2tk/pugl/.gitignore2
-rw-r--r--subprojects/d2tk/pugl/.gitlab-ci.yml118
-rw-r--r--subprojects/d2tk/pugl/.gitmodules3
-rw-r--r--subprojects/d2tk/pugl/AUTHORS20
-rw-r--r--subprojects/d2tk/pugl/COPYING2
-rw-r--r--subprojects/d2tk/pugl/README.md114
-rw-r--r--subprojects/d2tk/pugl/doc/footer.html20
-rw-r--r--subprojects/d2tk/pugl/doc/header.html49
-rw-r--r--subprojects/d2tk/pugl/doc/layout.xml193
-rw-r--r--subprojects/d2tk/pugl/doc/mainpage.md73
-rw-r--r--subprojects/d2tk/pugl/doc/reference.doxygen.in (renamed from pugl/Doxyfile.in)233
-rw-r--r--subprojects/d2tk/pugl/doc/style.css808
-rw-r--r--subprojects/d2tk/pugl/examples/cube_view.h82
-rw-r--r--subprojects/d2tk/pugl/examples/demo_utils.h175
-rw-r--r--subprojects/d2tk/pugl/examples/glad/glad.c1138
-rw-r--r--subprojects/d2tk/pugl/examples/glad/glad.h2129
-rw-r--r--subprojects/d2tk/pugl/examples/glad/khrplatform.h290
-rw-r--r--subprojects/d2tk/pugl/examples/pugl_cairo_demo.c270
-rw-r--r--subprojects/d2tk/pugl/examples/pugl_embed_demo.c366
-rw-r--r--subprojects/d2tk/pugl/examples/pugl_gl3_demo.c432
-rw-r--r--subprojects/d2tk/pugl/examples/pugl_print_events.c79
-rw-r--r--subprojects/d2tk/pugl/examples/pugl_window_demo.c252
-rw-r--r--subprojects/d2tk/pugl/examples/shader_utils.h97
-rw-r--r--subprojects/d2tk/pugl/pugl.pc.in7
-rw-r--r--subprojects/d2tk/pugl/pugl/cairo_gl.h106
-rw-r--r--subprojects/d2tk/pugl/pugl/detail/implementation.c467
-rw-r--r--subprojects/d2tk/pugl/pugl/detail/implementation.h76
-rw-r--r--subprojects/d2tk/pugl/pugl/detail/mac.h65
-rw-r--r--subprojects/d2tk/pugl/pugl/detail/mac.m1200
-rw-r--r--subprojects/d2tk/pugl/pugl/detail/mac_cairo.m155
-rw-r--r--subprojects/d2tk/pugl/pugl/detail/mac_gl.m177
-rw-r--r--subprojects/d2tk/pugl/pugl/detail/mac_stub.m95
-rw-r--r--subprojects/d2tk/pugl/pugl/detail/types.h113
-rw-r--r--subprojects/d2tk/pugl/pugl/detail/win.c1084
-rw-r--r--subprojects/d2tk/pugl/pugl/detail/win.h141
-rw-r--r--subprojects/d2tk/pugl/pugl/detail/win_cairo.c178
-rw-r--r--subprojects/d2tk/pugl/pugl/detail/win_gl.c308
-rw-r--r--subprojects/d2tk/pugl/pugl/detail/x11.c1218
-rw-r--r--subprojects/d2tk/pugl/pugl/detail/x11.h83
-rw-r--r--subprojects/d2tk/pugl/pugl/detail/x11_cairo.c169
-rw-r--r--subprojects/d2tk/pugl/pugl/detail/x11_gl.c216
-rw-r--r--subprojects/d2tk/pugl/pugl/gl.h1
-rw-r--r--subprojects/d2tk/pugl/pugl/glu.h3
-rw-r--r--subprojects/d2tk/pugl/pugl/pugl.h1475
-rw-r--r--subprojects/d2tk/pugl/pugl/pugl.hpp34
-rw-r--r--subprojects/d2tk/pugl/pugl/pugl_cairo.h49
-rw-r--r--subprojects/d2tk/pugl/pugl/pugl_cairo_backend.h (renamed from pugl/pugl/glew.h)21
-rw-r--r--subprojects/d2tk/pugl/pugl/pugl_gl.h60
-rw-r--r--subprojects/d2tk/pugl/pugl/pugl_gl_backend.h (renamed from subprojects/d2tk/pugl/pugl/glew.h)21
-rw-r--r--subprojects/d2tk/pugl/pugl/pugl_internal.h273
-rw-r--r--subprojects/d2tk/pugl/pugl/pugl_osx.m746
-rw-r--r--subprojects/d2tk/pugl/pugl/pugl_stub.h103
-rw-r--r--subprojects/d2tk/pugl/pugl/pugl_stub_backend.h23
-rw-r--r--subprojects/d2tk/pugl/pugl/pugl_win.cpp704
-rw-r--r--subprojects/d2tk/pugl/pugl/pugl_x11.c810
-rw-r--r--subprojects/d2tk/pugl/pugl_cairo_test.c205
-rw-r--r--subprojects/d2tk/pugl/pugl_test.c261
-rw-r--r--subprojects/d2tk/pugl/resources/Info.plist.in20
-rw-r--r--subprojects/d2tk/pugl/resources/pugl.ipe293
-rw-r--r--subprojects/d2tk/pugl/resources/pugl.pngbin0 -> 2641 bytes
-rw-r--r--subprojects/d2tk/pugl/resources/pugl.svg83
-rw-r--r--subprojects/d2tk/pugl/shaders/rect.frag35
-rw-r--r--subprojects/d2tk/pugl/shaders/rect.vert34
-rw-r--r--subprojects/d2tk/pugl/test/test_redisplay.c132
-rw-r--r--subprojects/d2tk/pugl/test/test_show_hide.c147
-rw-r--r--subprojects/d2tk/pugl/test/test_timer.c160
-rw-r--r--subprojects/d2tk/pugl/test/test_update.c124
-rw-r--r--subprojects/d2tk/pugl/test/test_utils.h256
-rwxr-xr-xsubprojects/d2tk/pugl/wafbin97489 -> 709 bytes
-rw-r--r--subprojects/d2tk/pugl/wscript499
-rw-r--r--subprojects/d2tk/src/backend_cairo.c138
-rw-r--r--subprojects/d2tk/src/backend_nanovg.c64
-rw-r--r--subprojects/d2tk/src/base.c3710
-rw-r--r--subprojects/d2tk/src/base_bar.c209
-rw-r--r--subprojects/d2tk/src/base_bitmap.c45
-rw-r--r--subprojects/d2tk/src/base_button.c216
-rw-r--r--subprojects/d2tk/src/base_combo.c251
-rw-r--r--subprojects/d2tk/src/base_cursor.c66
-rw-r--r--subprojects/d2tk/src/base_custom.c41
-rw-r--r--subprojects/d2tk/src/base_dial.c499
-rw-r--r--subprojects/d2tk/src/base_flowmatrix.c806
-rw-r--r--subprojects/d2tk/src/base_frame.c118
-rw-r--r--subprojects/d2tk/src/base_image.c53
-rw-r--r--subprojects/d2tk/src/base_internal.h136
-rw-r--r--subprojects/d2tk/src/base_label.c80
-rw-r--r--subprojects/d2tk/src/base_layout.c138
-rw-r--r--subprojects/d2tk/src/base_link.c122
-rw-r--r--subprojects/d2tk/src/base_meter.c233
-rw-r--r--subprojects/d2tk/src/base_pane.c246
-rw-r--r--subprojects/d2tk/src/base_pty.c1127
-rw-r--r--subprojects/d2tk/src/base_scrollbar.c331
-rw-r--r--subprojects/d2tk/src/base_spinner.c503
-rw-r--r--subprojects/d2tk/src/base_table.c123
-rw-r--r--subprojects/d2tk/src/base_textfield.c212
-rw-r--r--subprojects/d2tk/src/base_vkb.c621
-rw-r--r--subprojects/d2tk/src/base_wave.c146
-rw-r--r--subprojects/d2tk/src/core.c225
-rw-r--r--subprojects/d2tk/src/core_internal.h40
-rw-r--r--subprojects/d2tk/src/frontend_fbdev.c164
-rw-r--r--subprojects/d2tk/src/frontend_pugl.c642
-rw-r--r--subprojects/d2tk/src/hash.c (renamed from subprojects/d2tk/src/mum.c)31
-rw-r--r--subprojects/d2tk/test/base.c679
-rw-r--r--subprojects/d2tk/test/core.c54
-rw-r--r--subprojects/d2tk/test/mock.c28
-rw-r--r--subprojects/d2tk/ttf/FiraCode-Bold.ttfbin0 -> 315784 bytes
-rw-r--r--subprojects/d2tk/ttf/FiraCode-Light.ttfbin0 -> 276684 bytes
-rw-r--r--subprojects/d2tk/ttf/FiraCode-Medium.ttfbin0 -> 286232 bytes
-rw-r--r--subprojects/d2tk/ttf/FiraCode-Regular.ttfbin0 -> 290360 bytes
-rw-r--r--subprojects/d2tk/ttf/FiraSans-Bold.ttfbin0 -> 521312 bytes
-rw-r--r--subprojects/d2tk/ttf/LICENSE93
-rw-r--r--subprojects/d2tk/utf8.h/.clang-format2
-rw-r--r--subprojects/d2tk/utf8.h/.gitignore1
-rw-r--r--subprojects/d2tk/utf8.h/.travis.yml16
-rw-r--r--subprojects/d2tk/utf8.h/LICENSE24
-rw-r--r--subprojects/d2tk/utf8.h/README.md293
-rw-r--r--subprojects/d2tk/utf8.h/appveyor.yml33
-rw-r--r--subprojects/d2tk/utf8.h/test/CMakeLists.txt51
-rw-r--r--subprojects/d2tk/utf8.h/test/main.c1040
-rw-r--r--subprojects/d2tk/utf8.h/test/test.c26
-rw-r--r--subprojects/d2tk/utf8.h/test/test.cpp26
-rw-r--r--subprojects/d2tk/utf8.h/test/utest.h835
-rw-r--r--subprojects/d2tk/utf8.h/utf8.h1262
-rw-r--r--subprojects/nk_pugl/.gitlab-ci.yml94
-rw-r--r--subprojects/nk_pugl/COPYING (renamed from nk_pugl/COPYING)0
-rw-r--r--subprojects/nk_pugl/VERSION1
-rw-r--r--subprojects/nk_pugl/example/example.c117
-rw-r--r--subprojects/nk_pugl/glew-2.1.0/GL/eglew.h2618
-rw-r--r--subprojects/nk_pugl/glew-2.1.0/GL/glew.h23686
-rw-r--r--subprojects/nk_pugl/glew-2.1.0/GL/glxew.h1775
-rw-r--r--subprojects/nk_pugl/glew-2.1.0/GL/wglew.h1447
-rw-r--r--subprojects/nk_pugl/glew-2.1.0/glew.c28581
-rw-r--r--subprojects/nk_pugl/meson.build86
-rw-r--r--subprojects/nk_pugl/meson_options.txt4
-rw-r--r--subprojects/nk_pugl/nk_pugl/nk_pugl.h (renamed from nk_pugl/nk_pugl.h)648
-rw-r--r--subprojects/nk_pugl/nuklear/.gitattributes (renamed from nuklear/.gitattributes)0
-rw-r--r--subprojects/nk_pugl/nuklear/.gitignore (renamed from nuklear/.gitignore)0
-rw-r--r--subprojects/nk_pugl/nuklear/.gitmodules (renamed from nuklear/.gitmodules)0
-rw-r--r--subprojects/nk_pugl/nuklear/.travis.yml (renamed from nuklear/.travis.yml)0
-rw-r--r--subprojects/nk_pugl/nuklear/Readme.md (renamed from nuklear/Readme.md)0
-rw-r--r--subprojects/nk_pugl/nuklear/demo/allegro5/KeyboardHandleriOS.h (renamed from nuklear/demo/allegro5/KeyboardHandleriOS.h)0
-rw-r--r--subprojects/nk_pugl/nuklear/demo/allegro5/KeyboardHandleriOS.m (renamed from nuklear/demo/allegro5/KeyboardHandleriOS.m)0
-rw-r--r--subprojects/nk_pugl/nuklear/demo/allegro5/Makefile (renamed from nuklear/demo/allegro5/Makefile)0
-rw-r--r--subprojects/nk_pugl/nuklear/demo/allegro5/Readme.md (renamed from nuklear/demo/allegro5/Readme.md)0
-rw-r--r--subprojects/nk_pugl/nuklear/demo/allegro5/main.c (renamed from nuklear/demo/allegro5/main.c)0
-rw-r--r--subprojects/nk_pugl/nuklear/demo/allegro5/nuklear_allegro5.h (renamed from nuklear/demo/allegro5/nuklear_allegro5.h)0
-rw-r--r--subprojects/nk_pugl/nuklear/demo/calculator.c (renamed from nuklear/demo/calculator.c)0
-rw-r--r--subprojects/nk_pugl/nuklear/demo/d3d11/build.bat (renamed from nuklear/demo/d3d11/build.bat)0
-rw-r--r--subprojects/nk_pugl/nuklear/demo/d3d11/main.c (renamed from nuklear/demo/d3d11/main.c)0
-rw-r--r--subprojects/nk_pugl/nuklear/demo/d3d11/nuklear_d3d11.h (renamed from nuklear/demo/d3d11/nuklear_d3d11.h)0
-rw-r--r--subprojects/nk_pugl/nuklear/demo/d3d11/nuklear_d3d11.hlsl (renamed from nuklear/demo/d3d11/nuklear_d3d11.hlsl)0
-rw-r--r--subprojects/nk_pugl/nuklear/demo/d3d11/nuklear_d3d11_pixel_shader.h (renamed from nuklear/demo/d3d11/nuklear_d3d11_pixel_shader.h)0
-rw-r--r--subprojects/nk_pugl/nuklear/demo/d3d11/nuklear_d3d11_vertex_shader.h (renamed from nuklear/demo/d3d11/nuklear_d3d11_vertex_shader.h)0
-rw-r--r--subprojects/nk_pugl/nuklear/demo/d3d9/build.bat (renamed from nuklear/demo/d3d9/build.bat)0
-rw-r--r--subprojects/nk_pugl/nuklear/demo/d3d9/main.c (renamed from nuklear/demo/d3d9/main.c)0
-rw-r--r--subprojects/nk_pugl/nuklear/demo/d3d9/nuklear_d3d9.h (renamed from nuklear/demo/d3d9/nuklear_d3d9.h)0
-rw-r--r--subprojects/nk_pugl/nuklear/demo/gdi/build.bat (renamed from nuklear/demo/gdi/build.bat)0
-rw-r--r--subprojects/nk_pugl/nuklear/demo/gdi/main.c (renamed from nuklear/demo/gdi/main.c)0
-rw-r--r--subprojects/nk_pugl/nuklear/demo/gdi/nuklear_gdi.h (renamed from nuklear/demo/gdi/nuklear_gdi.h)0
-rw-r--r--subprojects/nk_pugl/nuklear/demo/gdip/build.bat (renamed from nuklear/demo/gdip/build.bat)0
-rw-r--r--subprojects/nk_pugl/nuklear/demo/gdip/main.c (renamed from nuklear/demo/gdip/main.c)0
-rw-r--r--subprojects/nk_pugl/nuklear/demo/gdip/nuklear_gdip.h (renamed from nuklear/demo/gdip/nuklear_gdip.h)0
-rw-r--r--subprojects/nk_pugl/nuklear/demo/glfw_opengl2/Makefile (renamed from nuklear/demo/glfw_opengl2/Makefile)0
-rw-r--r--subprojects/nk_pugl/nuklear/demo/glfw_opengl2/main.c (renamed from nuklear/demo/glfw_opengl2/main.c)0
-rw-r--r--subprojects/nk_pugl/nuklear/demo/glfw_opengl2/nuklear_glfw_gl2.h (renamed from nuklear/demo/glfw_opengl2/nuklear_glfw_gl2.h)0
-rw-r--r--subprojects/nk_pugl/nuklear/demo/glfw_opengl3/Makefile (renamed from nuklear/demo/glfw_opengl3/Makefile)0
-rw-r--r--subprojects/nk_pugl/nuklear/demo/glfw_opengl3/main.c (renamed from nuklear/demo/glfw_opengl3/main.c)0
-rw-r--r--subprojects/nk_pugl/nuklear/demo/glfw_opengl3/nuklear_glfw_gl3.h (renamed from nuklear/demo/glfw_opengl3/nuklear_glfw_gl3.h)0
-rw-r--r--subprojects/nk_pugl/nuklear/demo/node_editor.c (renamed from nuklear/demo/node_editor.c)0
-rw-r--r--subprojects/nk_pugl/nuklear/demo/overview.c (renamed from nuklear/demo/overview.c)0
-rw-r--r--subprojects/nk_pugl/nuklear/demo/sdl_opengl2/Makefile (renamed from nuklear/demo/sdl_opengl2/Makefile)0
-rw-r--r--subprojects/nk_pugl/nuklear/demo/sdl_opengl2/main.c (renamed from nuklear/demo/sdl_opengl2/main.c)0
-rw-r--r--subprojects/nk_pugl/nuklear/demo/sdl_opengl2/nuklear_sdl_gl2.h (renamed from nuklear/demo/sdl_opengl2/nuklear_sdl_gl2.h)0
-rw-r--r--subprojects/nk_pugl/nuklear/demo/sdl_opengl3/Makefile (renamed from nuklear/demo/sdl_opengl3/Makefile)0
-rw-r--r--subprojects/nk_pugl/nuklear/demo/sdl_opengl3/main.c (renamed from nuklear/demo/sdl_opengl3/main.c)0
-rw-r--r--subprojects/nk_pugl/nuklear/demo/sdl_opengl3/nuklear_sdl_gl3.h (renamed from nuklear/demo/sdl_opengl3/nuklear_sdl_gl3.h)0
-rw-r--r--subprojects/nk_pugl/nuklear/demo/sdl_opengles2/Makefile (renamed from nuklear/demo/sdl_opengles2/Makefile)0
-rw-r--r--subprojects/nk_pugl/nuklear/demo/sdl_opengles2/main.c (renamed from nuklear/demo/sdl_opengles2/main.c)0
-rw-r--r--subprojects/nk_pugl/nuklear/demo/sdl_opengles2/nuklear_sdl_gles2.h (renamed from nuklear/demo/sdl_opengles2/nuklear_sdl_gles2.h)0
-rw-r--r--subprojects/nk_pugl/nuklear/demo/sfml_opengl2/Makefile (renamed from nuklear/demo/sfml_opengl2/Makefile)0
-rw-r--r--subprojects/nk_pugl/nuklear/demo/sfml_opengl2/Readme.md (renamed from nuklear/demo/sfml_opengl2/Readme.md)0
-rw-r--r--subprojects/nk_pugl/nuklear/demo/sfml_opengl2/main.cpp (renamed from nuklear/demo/sfml_opengl2/main.cpp)0
-rw-r--r--subprojects/nk_pugl/nuklear/demo/sfml_opengl2/nuklear_sfml_gl2.h (renamed from nuklear/demo/sfml_opengl2/nuklear_sfml_gl2.h)0
-rw-r--r--subprojects/nk_pugl/nuklear/demo/sfml_opengl3/Makefile (renamed from nuklear/demo/sfml_opengl3/Makefile)0
-rw-r--r--subprojects/nk_pugl/nuklear/demo/sfml_opengl3/Readme.md (renamed from nuklear/demo/sfml_opengl3/Readme.md)0
-rw-r--r--subprojects/nk_pugl/nuklear/demo/sfml_opengl3/main.cpp (renamed from nuklear/demo/sfml_opengl3/main.cpp)0
-rw-r--r--subprojects/nk_pugl/nuklear/demo/sfml_opengl3/nuklear_sfml_gl3.h (renamed from nuklear/demo/sfml_opengl3/nuklear_sfml_gl3.h)0
-rw-r--r--subprojects/nk_pugl/nuklear/demo/style.c (renamed from nuklear/demo/style.c)0
-rw-r--r--subprojects/nk_pugl/nuklear/demo/x11/Makefile (renamed from nuklear/demo/x11/Makefile)0
-rw-r--r--subprojects/nk_pugl/nuklear/demo/x11/main.c (renamed from nuklear/demo/x11/main.c)0
-rw-r--r--subprojects/nk_pugl/nuklear/demo/x11/nuklear_xlib.h (renamed from nuklear/demo/x11/nuklear_xlib.h)0
-rw-r--r--subprojects/nk_pugl/nuklear/demo/x11_opengl2/Makefile (renamed from nuklear/demo/x11_opengl2/Makefile)0
-rw-r--r--subprojects/nk_pugl/nuklear/demo/x11_opengl2/main.c (renamed from nuklear/demo/x11_opengl2/main.c)0
-rw-r--r--subprojects/nk_pugl/nuklear/demo/x11_opengl2/nuklear_xlib_gl2.h (renamed from nuklear/demo/x11_opengl2/nuklear_xlib_gl2.h)0
-rw-r--r--subprojects/nk_pugl/nuklear/demo/x11_opengl3/Makefile (renamed from nuklear/demo/x11_opengl3/Makefile)0
-rw-r--r--subprojects/nk_pugl/nuklear/demo/x11_opengl3/main.c (renamed from nuklear/demo/x11_opengl3/main.c)0
-rw-r--r--subprojects/nk_pugl/nuklear/demo/x11_opengl3/nuklear_xlib_gl3.h (renamed from nuklear/demo/x11_opengl3/nuklear_xlib_gl3.h)0
-rw-r--r--subprojects/nk_pugl/nuklear/demo/x11_rawfb/Makefile (renamed from nuklear/demo/x11_rawfb/Makefile)0
-rw-r--r--subprojects/nk_pugl/nuklear/demo/x11_rawfb/main.c (renamed from nuklear/demo/x11_rawfb/main.c)0
-rw-r--r--subprojects/nk_pugl/nuklear/demo/x11_rawfb/nuklear_rawfb.h (renamed from nuklear/demo/x11_rawfb/nuklear_rawfb.h)0
-rw-r--r--subprojects/nk_pugl/nuklear/demo/x11_rawfb/nuklear_xlib.h (renamed from nuklear/demo/x11_rawfb/nuklear_xlib.h)0
-rw-r--r--subprojects/nk_pugl/nuklear/doc/Makefile (renamed from nuklear/doc/Makefile)0
-rwxr-xr-xsubprojects/nk_pugl/nuklear/doc/build.sh (renamed from nuklear/doc/build.sh)0
-rw-r--r--subprojects/nk_pugl/nuklear/doc/nuklear.html (renamed from nuklear/doc/nuklear.html)0
-rw-r--r--subprojects/nk_pugl/nuklear/doc/stddoc.c (renamed from nuklear/doc/stddoc.c)0
-rw-r--r--subprojects/nk_pugl/nuklear/example/Makefile (renamed from nuklear/example/Makefile)0
-rw-r--r--subprojects/nk_pugl/nuklear/example/canvas.c (renamed from nuklear/example/canvas.c)0
-rw-r--r--subprojects/nk_pugl/nuklear/example/extended.c (renamed from nuklear/example/extended.c)0
-rw-r--r--subprojects/nk_pugl/nuklear/example/file_browser.c (renamed from nuklear/example/file_browser.c)0
-rw-r--r--subprojects/nk_pugl/nuklear/example/icon/checked.png (renamed from nuklear/example/icon/checked.png)bin1813 -> 1813 bytes
-rw-r--r--subprojects/nk_pugl/nuklear/example/icon/cloud.png (renamed from nuklear/example/icon/cloud.png)bin7509 -> 7509 bytes
-rw-r--r--subprojects/nk_pugl/nuklear/example/icon/computer.png (renamed from nuklear/example/icon/computer.png)bin620 -> 620 bytes
-rw-r--r--subprojects/nk_pugl/nuklear/example/icon/copy.png (renamed from nuklear/example/icon/copy.png)bin655 -> 655 bytes
-rw-r--r--subprojects/nk_pugl/nuklear/example/icon/default.png (renamed from nuklear/example/icon/default.png)bin460 -> 460 bytes
-rw-r--r--subprojects/nk_pugl/nuklear/example/icon/delete.png (renamed from nuklear/example/icon/delete.png)bin11040 -> 11040 bytes
-rw-r--r--subprojects/nk_pugl/nuklear/example/icon/desktop.png (renamed from nuklear/example/icon/desktop.png)bin583 -> 583 bytes
-rw-r--r--subprojects/nk_pugl/nuklear/example/icon/directory.png (renamed from nuklear/example/icon/directory.png)bin533 -> 533 bytes
-rw-r--r--subprojects/nk_pugl/nuklear/example/icon/edit.png (renamed from nuklear/example/icon/edit.png)bin14998 -> 14998 bytes
-rw-r--r--subprojects/nk_pugl/nuklear/example/icon/export.png (renamed from nuklear/example/icon/export.png)bin13336 -> 13336 bytes
-rw-r--r--subprojects/nk_pugl/nuklear/example/icon/font.png (renamed from nuklear/example/icon/font.png)bin561 -> 561 bytes
-rw-r--r--subprojects/nk_pugl/nuklear/example/icon/home.png (renamed from nuklear/example/icon/home.png)bin819 -> 819 bytes
-rw-r--r--subprojects/nk_pugl/nuklear/example/icon/img.png (renamed from nuklear/example/icon/img.png)bin648 -> 648 bytes
-rw-r--r--subprojects/nk_pugl/nuklear/example/icon/movie.png (renamed from nuklear/example/icon/movie.png)bin626 -> 626 bytes
-rw-r--r--subprojects/nk_pugl/nuklear/example/icon/music.png (renamed from nuklear/example/icon/music.png)bin610 -> 610 bytes
-rw-r--r--subprojects/nk_pugl/nuklear/example/icon/next.png (renamed from nuklear/example/icon/next.png)bin703 -> 703 bytes
-rw-r--r--subprojects/nk_pugl/nuklear/example/icon/pause.png (renamed from nuklear/example/icon/pause.png)bin1338 -> 1338 bytes
-rw-r--r--subprojects/nk_pugl/nuklear/example/icon/pen.png (renamed from nuklear/example/icon/pen.png)bin5949 -> 5949 bytes
-rw-r--r--subprojects/nk_pugl/nuklear/example/icon/phone.png (renamed from nuklear/example/icon/phone.png)bin15778 -> 15778 bytes
-rw-r--r--subprojects/nk_pugl/nuklear/example/icon/plane.png (renamed from nuklear/example/icon/plane.png)bin13546 -> 13546 bytes
-rw-r--r--subprojects/nk_pugl/nuklear/example/icon/play.png (renamed from nuklear/example/icon/play.png)bin566 -> 566 bytes
-rw-r--r--subprojects/nk_pugl/nuklear/example/icon/prev.png (renamed from nuklear/example/icon/prev.png)bin701 -> 701 bytes
-rw-r--r--subprojects/nk_pugl/nuklear/example/icon/rocket.png (renamed from nuklear/example/icon/rocket.png)bin1121 -> 1121 bytes
-rw-r--r--subprojects/nk_pugl/nuklear/example/icon/settings.png (renamed from nuklear/example/icon/settings.png)bin15671 -> 15671 bytes
-rw-r--r--subprojects/nk_pugl/nuklear/example/icon/stop.png (renamed from nuklear/example/icon/stop.png)bin520 -> 520 bytes
-rw-r--r--subprojects/nk_pugl/nuklear/example/icon/text.png (renamed from nuklear/example/icon/text.png)bin601 -> 601 bytes
-rw-r--r--subprojects/nk_pugl/nuklear/example/icon/tools.png (renamed from nuklear/example/icon/tools.png)bin24483 -> 24483 bytes
-rw-r--r--subprojects/nk_pugl/nuklear/example/icon/unchecked.png (renamed from nuklear/example/icon/unchecked.png)bin1044 -> 1044 bytes
-rw-r--r--subprojects/nk_pugl/nuklear/example/icon/volume.png (renamed from nuklear/example/icon/volume.png)bin25438 -> 25438 bytes
-rw-r--r--subprojects/nk_pugl/nuklear/example/icon/wifi.png (renamed from nuklear/example/icon/wifi.png)bin18857 -> 18857 bytes
-rw-r--r--subprojects/nk_pugl/nuklear/example/images/image1.png (renamed from nuklear/example/images/image1.png)bin42882 -> 42882 bytes
-rw-r--r--subprojects/nk_pugl/nuklear/example/images/image2.png (renamed from nuklear/example/images/image2.png)bin5671 -> 5671 bytes
-rw-r--r--subprojects/nk_pugl/nuklear/example/images/image3.png (renamed from nuklear/example/images/image3.png)bin131502 -> 131502 bytes
-rw-r--r--subprojects/nk_pugl/nuklear/example/images/image4.png (renamed from nuklear/example/images/image4.png)bin185821 -> 185821 bytes
-rw-r--r--subprojects/nk_pugl/nuklear/example/images/image5.png (renamed from nuklear/example/images/image5.png)bin98475 -> 98475 bytes
-rw-r--r--subprojects/nk_pugl/nuklear/example/images/image6.png (renamed from nuklear/example/images/image6.png)bin35633 -> 35633 bytes
-rw-r--r--subprojects/nk_pugl/nuklear/example/images/image7.png (renamed from nuklear/example/images/image7.png)bin13960 -> 13960 bytes
-rw-r--r--subprojects/nk_pugl/nuklear/example/images/image8.png (renamed from nuklear/example/images/image8.png)bin45987 -> 45987 bytes
-rw-r--r--subprojects/nk_pugl/nuklear/example/images/image9.png (renamed from nuklear/example/images/image9.png)bin30759 -> 30759 bytes
-rw-r--r--subprojects/nk_pugl/nuklear/example/skinning.c (renamed from nuklear/example/skinning.c)0
-rw-r--r--subprojects/nk_pugl/nuklear/example/skins/gwen.png (renamed from nuklear/example/skins/gwen.png)bin24565 -> 24565 bytes
-rw-r--r--subprojects/nk_pugl/nuklear/example/stb_image.h (renamed from nuklear/example/stb_image.h)0
-rw-r--r--subprojects/nk_pugl/nuklear/extra_font/Cousine-Regular.ttf (renamed from nuklear/extra_font/Cousine-Regular.ttf)bin43912 -> 43912 bytes
-rw-r--r--subprojects/nk_pugl/nuklear/extra_font/DroidSans.ttf (renamed from nuklear/extra_font/DroidSans.ttf)bin190044 -> 190044 bytes
-rw-r--r--subprojects/nk_pugl/nuklear/extra_font/Karla-Regular.ttf (renamed from nuklear/extra_font/Karla-Regular.ttf)bin16848 -> 16848 bytes
-rw-r--r--subprojects/nk_pugl/nuklear/extra_font/ProggyClean.ttf (renamed from nuklear/extra_font/ProggyClean.ttf)bin41208 -> 41208 bytes
-rw-r--r--subprojects/nk_pugl/nuklear/extra_font/ProggyTiny.ttf (renamed from nuklear/extra_font/ProggyTiny.ttf)bin35656 -> 35656 bytes
-rw-r--r--subprojects/nk_pugl/nuklear/extra_font/Raleway-Bold.ttf (renamed from nuklear/extra_font/Raleway-Bold.ttf)bin176280 -> 176280 bytes
-rw-r--r--subprojects/nk_pugl/nuklear/extra_font/Roboto-Bold.ttf (renamed from nuklear/extra_font/Roboto-Bold.ttf)bin135820 -> 135820 bytes
-rw-r--r--subprojects/nk_pugl/nuklear/extra_font/Roboto-Light.ttf (renamed from nuklear/extra_font/Roboto-Light.ttf)bin140276 -> 140276 bytes
-rw-r--r--subprojects/nk_pugl/nuklear/extra_font/Roboto-Regular.ttf (renamed from nuklear/extra_font/Roboto-Regular.ttf)bin145348 -> 145348 bytes
-rw-r--r--subprojects/nk_pugl/nuklear/extra_font/kenvector_future.ttf (renamed from nuklear/extra_font/kenvector_future.ttf)bin34136 -> 34136 bytes
-rw-r--r--subprojects/nk_pugl/nuklear/extra_font/kenvector_future_thin.ttf (renamed from nuklear/extra_font/kenvector_future_thin.ttf)bin34100 -> 34100 bytes
-rw-r--r--subprojects/nk_pugl/nuklear/nuklear.h (renamed from nuklear/nuklear.h)0
-rw-r--r--subprojects/nk_pugl/nuklear/package.json (renamed from nuklear/package.json)0
-rw-r--r--subprojects/nk_pugl/pugl/.clang-format127
-rw-r--r--subprojects/nk_pugl/pugl/.clang-tidy16
-rw-r--r--subprojects/nk_pugl/pugl/.gitattributes1
-rw-r--r--subprojects/nk_pugl/pugl/.gitignore (renamed from pugl/.gitignore)2
-rw-r--r--subprojects/nk_pugl/pugl/.gitlab-ci.yml118
-rw-r--r--subprojects/nk_pugl/pugl/.gitmodules3
-rw-r--r--subprojects/nk_pugl/pugl/AUTHORS11
-rw-r--r--subprojects/nk_pugl/pugl/COPYING (renamed from pugl/COPYING)2
-rw-r--r--subprojects/nk_pugl/pugl/README.md108
-rw-r--r--subprojects/nk_pugl/pugl/doc/footer.html20
-rw-r--r--subprojects/nk_pugl/pugl/doc/header.html49
-rw-r--r--subprojects/nk_pugl/pugl/doc/layout.xml193
-rw-r--r--subprojects/nk_pugl/pugl/doc/mainpage.md73
-rw-r--r--subprojects/nk_pugl/pugl/doc/reference.doxygen.in (renamed from subprojects/d2tk/pugl/Doxyfile.in)233
-rw-r--r--subprojects/nk_pugl/pugl/doc/style.css808
-rw-r--r--subprojects/nk_pugl/pugl/examples/cube_view.h82
-rw-r--r--subprojects/nk_pugl/pugl/examples/demo_utils.h175
-rw-r--r--subprojects/nk_pugl/pugl/examples/glad/glad.c1138
-rw-r--r--subprojects/nk_pugl/pugl/examples/glad/glad.h2129
-rw-r--r--subprojects/nk_pugl/pugl/examples/glad/khrplatform.h290
-rw-r--r--subprojects/nk_pugl/pugl/examples/pugl_cairo_demo.c270
-rw-r--r--subprojects/nk_pugl/pugl/examples/pugl_embed_demo.c366
-rw-r--r--subprojects/nk_pugl/pugl/examples/pugl_gl3_demo.c432
-rw-r--r--subprojects/nk_pugl/pugl/examples/pugl_print_events.c79
-rw-r--r--subprojects/nk_pugl/pugl/examples/pugl_window_demo.c252
-rw-r--r--subprojects/nk_pugl/pugl/examples/shader_utils.h97
-rw-r--r--subprojects/nk_pugl/pugl/pugl.pc.in (renamed from pugl/pugl.pc.in)7
-rw-r--r--subprojects/nk_pugl/pugl/pugl/detail/implementation.c467
-rw-r--r--subprojects/nk_pugl/pugl/pugl/detail/implementation.h76
-rw-r--r--subprojects/nk_pugl/pugl/pugl/detail/mac.h65
-rw-r--r--subprojects/nk_pugl/pugl/pugl/detail/mac.m1200
-rw-r--r--subprojects/nk_pugl/pugl/pugl/detail/mac_cairo.m155
-rw-r--r--subprojects/nk_pugl/pugl/pugl/detail/mac_gl.m177
-rw-r--r--subprojects/nk_pugl/pugl/pugl/detail/mac_stub.m95
-rw-r--r--subprojects/nk_pugl/pugl/pugl/detail/types.h113
-rw-r--r--subprojects/nk_pugl/pugl/pugl/detail/win.c1084
-rw-r--r--subprojects/nk_pugl/pugl/pugl/detail/win.h141
-rw-r--r--subprojects/nk_pugl/pugl/pugl/detail/win_cairo.c178
-rw-r--r--subprojects/nk_pugl/pugl/pugl/detail/win_gl.c308
-rw-r--r--subprojects/nk_pugl/pugl/pugl/detail/x11.c1218
-rw-r--r--subprojects/nk_pugl/pugl/pugl/detail/x11.h83
-rw-r--r--subprojects/nk_pugl/pugl/pugl/detail/x11_cairo.c169
-rw-r--r--subprojects/nk_pugl/pugl/pugl/detail/x11_gl.c216
-rw-r--r--subprojects/nk_pugl/pugl/pugl/gl.h (renamed from pugl/pugl/gl.h)1
-rw-r--r--subprojects/nk_pugl/pugl/pugl/glu.h (renamed from pugl/pugl/glu.h)3
-rw-r--r--subprojects/nk_pugl/pugl/pugl/pugl.h1503
-rw-r--r--subprojects/nk_pugl/pugl/pugl/pugl.hpp (renamed from pugl/pugl/pugl.hpp)34
-rw-r--r--subprojects/nk_pugl/pugl/pugl/pugl_cairo.h49
-rw-r--r--subprojects/nk_pugl/pugl/pugl/pugl_cairo_backend.h23
-rw-r--r--subprojects/nk_pugl/pugl/pugl/pugl_gl.h60
-rw-r--r--subprojects/nk_pugl/pugl/pugl/pugl_gl_backend.h23
-rw-r--r--subprojects/nk_pugl/pugl/pugl/pugl_stub.h103
-rw-r--r--subprojects/nk_pugl/pugl/pugl/pugl_stub_backend.h23
-rw-r--r--subprojects/nk_pugl/pugl/resources/Info.plist.in20
-rw-r--r--subprojects/nk_pugl/pugl/resources/pugl.ipe293
-rw-r--r--subprojects/nk_pugl/pugl/resources/pugl.pngbin0 -> 2641 bytes
-rw-r--r--subprojects/nk_pugl/pugl/resources/pugl.svg83
-rw-r--r--subprojects/nk_pugl/pugl/shaders/rect.frag35
-rw-r--r--subprojects/nk_pugl/pugl/shaders/rect.vert34
-rw-r--r--subprojects/nk_pugl/pugl/test/test_redisplay.c132
-rw-r--r--subprojects/nk_pugl/pugl/test/test_show_hide.c147
-rw-r--r--subprojects/nk_pugl/pugl/test/test_timer.c160
-rw-r--r--subprojects/nk_pugl/pugl/test/test_update.c124
-rw-r--r--subprojects/nk_pugl/pugl/test/test_utils.h256
-rwxr-xr-xsubprojects/nk_pugl/pugl/waf27
-rw-r--r--subprojects/nk_pugl/pugl/wscript390
406 files changed, 106933 insertions, 14042 deletions
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 59fe6505..1253d16c 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -4,23 +4,24 @@ stages:
.variables_template: &variables_definition
variables:
- BASE_NAME: "synthpod"
PKG_CONFIG_PATH: "/opt/lv2/lib/pkgconfig:/opt/${CI_BUILD_NAME}/lib/pkgconfig:/usr/lib/${CI_BUILD_NAME}/pkgconfig"
+ BUILD_OPTS: "-Duse-fontconfig=disabled -Duse-qt4=false -Duse-qt5=false -Duse-gtk2=false -Duse-gtk3=false"
.common_template: &common_definition
<<: *variables_definition
stage: build
artifacts:
- name: "${BASE_NAME}-$(cat VERSION)-${CI_BUILD_NAME}"
+ name: "${CI_PROJECT_NAME}-$(cat VERSION)-${CI_BUILD_NAME}"
paths:
- - "${BASE_NAME}-$(cat VERSION)/"
+ - "${CI_PROJECT_NAME}-$(cat VERSION)/${CI_BUILD_NAME}/"
.build_template: &build_definition
<<: *common_definition
script:
- - meson --prefix="/" --libdir="lib" --cross-file "${CI_BUILD_NAME}" -Duse-qt4=false -Duse-qt5=false -Duse-gtk2=false -Duse-gtk3=false build
+ - 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
- - DESTDIR="${CI_PROJECT_DIR}/${BASE_NAME}-$(cat VERSION)/${CI_BUILD_NAME}" ninja -C build install
+ - ninja -C build test
+ - ninja -C build install
.universal_linux_template: &universal_linux_definition
image: ventosus/universal-linux-gnu
@@ -41,22 +42,22 @@ stages:
# building in docker
x86_64-linux-gnu:
before_script:
- - apt-get install -y libjack-dev libglu1-mesa-dev libevdev-dev
+ - apt-get install -y libjack-dev libglu1-mesa-dev libevdev-dev libxcb-xrm-dev
<<: *universal_linux_definition
i686-linux-gnu:
before_script:
- - apt-get install -y libjack-dev:i386 libglu1-mesa-dev:i386 libevdev-dev:i386
+ - apt-get install -y libjack-dev:i386 libglu1-mesa-dev:i386 libevdev-dev:i386 libxcb-xrm-dev:i386
<<: *universal_linux_definition
arm-linux-gnueabihf:
before_script:
- - apt-get install -y libjack-dev:armhf libglu1-mesa-dev:armhf libevdev-dev:armhf
+ - apt-get install -y libjack-dev:armhf libglu1-mesa-dev:armhf libevdev-dev:armhf libxcb-xrm-dev:armhf
<<: *arm_linux_definition
aarch64-linux-gnu:
before_script:
- - apt-get install -y libjack-dev:arm64 libglu1-mesa-dev:arm64 libevdev-dev:arm64
+ - apt-get install -y libjack-dev:arm64 libglu1-mesa-dev:arm64 libevdev-dev:arm64 libxcb-xrm-dev:arm64
<<: *arm_linux_definition
#x86_64-w64-mingw32:
@@ -74,6 +75,6 @@ pack:
script:
- echo 'packing up...'
artifacts:
- name: "${BASE_NAME}-$(cat VERSION)"
+ name: "${CI_PROJECT_NAME}-$(cat VERSION)"
paths:
- - "${BASE_NAME}-$(cat VERSION)/"
+ - "${CI_PROJECT_NAME}-$(cat VERSION)/"
diff --git a/README.md b/README.md
index 69d7f5f5..28aee903 100644
--- a/README.md
+++ b/README.md
@@ -86,7 +86,7 @@ When paired with realtime scripting via [Moony](/lv2/moony/#),
it turns Synthpod into a versatile realtime programmable, remote controllable
LV2 host framework.
-![Synthpod screenshot](https://git.open-music-kontrollers.ch/lv2/synthpod/plain/screenshots/screenshot_1.png)
+![Synthpod screenshot](/screenshots/screenshot_1.png)
### LV2 specifications support status
@@ -120,8 +120,8 @@ The full LV2 specification is located at <http://lv2plug.in/ns/>.
<td>Data Access</td>
<td>data-access</td>
<td>Provides access to LV2_Descriptor::extension_data().</td>
- <td style="color:#b00;">No</td>
- <td>won't, ever</td>
+ <td style="color:#0b0;">Yes</td>
+ <td>X11UI only</td>
</tr><tr>
<td>Dynamic Manifest</td>
<td>dynmanifest</td>
@@ -138,8 +138,8 @@ The full LV2 specification is located at <http://lv2plug.in/ns/>.
<td>Instance Access</td>
<td>instance-access</td>
<td>Provides access to the LV2_Handle of a plugin.</td>
- <td style="color:#b00;">No</td>
- <td>won't, ever</td>
+ <td style="color:#0b0;">Yes</td>
+ <td>X11UI only</td>
</tr><tr>
<td>Log</td>
<td>log</td>
diff --git a/VERSION b/VERSION
index 1d07d21b..874b1ea6 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-0.1.6137
+0.1.6275
diff --git a/app/synthpod_app.c b/app/synthpod_app.c
index 2301d790..092a960d 100644
--- a/app/synthpod_app.c
+++ b/app/synthpod_app.c
@@ -771,7 +771,7 @@ _sp_app_populate(sp_app_t *app)
// inject source mod
uri_str = SYNTHPOD_PREFIX"source";
- mod = _sp_app_mod_add(app, uri_str, 0);
+ mod = _sp_app_mod_add(app, uri_str, 0, 0, NULL);
if(mod)
{
app->mods[app->num_mods] = mod;
@@ -784,7 +784,7 @@ _sp_app_populate(sp_app_t *app)
// inject sink mod
uri_str = SYNTHPOD_PREFIX"sink";
- mod = _sp_app_mod_add(app, uri_str, 0);
+ mod = _sp_app_mod_add(app, uri_str, 0, 0, NULL);
if(mod)
{
app->mods[app->num_mods] = mod;
@@ -808,6 +808,8 @@ sp_app_new(const LilvWorld *world, sp_app_driver_t *driver, void *data)
if(!app)
return NULL;
+ atomic_init(&app->visibility, false);
+
atomic_init(&app->dirty, false);
app->dir.home = getenv("HOME");
@@ -1341,6 +1343,9 @@ _sp_app_reinitialize(sp_app_t *app)
}
lilv_instance_activate(mod->inst);
+
+ // some plugins need to run before they can be configured
+ lilv_instance_run(mod->inst, app->driver->min_block_size);
}
}
@@ -1553,3 +1558,25 @@ sp_app_xrun_report(sp_app_t *app)
atomic_store(&dsp_master->xrun_report, true);
}
+
+void
+sp_app_visibility_set(sp_app_t *app, bool visibility)
+{
+ if(!app)
+ {
+ return;
+ }
+
+ atomic_store(&app->visibility, visibility);
+}
+
+bool
+sp_app_visibility_get(sp_app_t *app)
+{
+ if(!app)
+ {
+ return false;
+ }
+
+ return atomic_load(&app->visibility);
+}
diff --git a/app/synthpod_app_mod.c b/app/synthpod_app_mod.c
index 6e44e0db..e6c066e7 100644
--- a/app/synthpod_app_mod.c
+++ b/app/synthpod_app_mod.c
@@ -587,7 +587,8 @@ _mod_queue_draw(void *data)
}
mod_t *
-_sp_app_mod_add(sp_app_t *app, const char *uri, LV2_URID urn)
+_sp_app_mod_add(sp_app_t *app, const char *uri, LV2_URID urn, uint32_t created,
+ const char *alias)
{
const LilvPlugin *plug;
@@ -597,6 +598,11 @@ _sp_app_mod_add(sp_app_t *app, const char *uri, LV2_URID urn)
return NULL;
}
+ if(created == 0)
+ {
+ created = ++app->created;
+ }
+
mod_t *mod = calloc(1, sizeof(mod_t));
if(!mod)
{
@@ -604,6 +610,13 @@ _sp_app_mod_add(sp_app_t *app, const char *uri, LV2_URID urn)
return NULL;
}
+ mod->created = created;
+
+ if(alias != NULL)
+ {
+ strncpy(mod->alias, alias, ALIAS_MAX - 1);
+ }
+
mod->needs_bypassing = false; // plugins with control ports only need no bypassing upon preset load
mod->bypassed = false;
atomic_init(&mod->dsp_client.ref_count, 0);
@@ -789,7 +802,7 @@ _sp_app_mod_add(sp_app_t *app, const char *uri, LV2_URID urn)
asprintf(&pretty_name, "#%"PRIu32" - %s",
mod->urn, lilv_node_as_string(port_name_node));
designation = port_designation ? lilv_node_as_string(port_designation) : NULL;
- const uint32_t order = (mod->urn << 16) | tar->index;
+ const uint32_t order = (mod->created << 16) | tar->index;
tar->sys.data = app->driver->system_port_add(app->data, tar->sys.type,
short_name, pretty_name, designation,
@@ -800,6 +813,12 @@ _sp_app_mod_add(sp_app_t *app, const char *uri, LV2_URID urn)
free(short_name);
free(pretty_name);
}
+
+ if(strlen(mod->alias) && app->driver->system_port_set)
+ {
+ app->driver->system_port_set(app->data, tar->sys.data,
+ SYNTHPOD_PREFIX"#moduleAlias", mod->alias);
+ }
}
else
{
@@ -996,6 +1015,9 @@ _sp_app_mod_add(sp_app_t *app, const char *uri, LV2_URID urn)
// activate
lilv_instance_activate(mod->inst);
+ // some plugins need to run before they can be configured
+ lilv_instance_run(mod->inst, app->driver->min_block_size);
+
// load default state
if(load_default_state && _sp_app_state_preset_load(app, mod, uri, false))
sp_app_log_error(app, "%s: default state loading failed\n", __func__);
@@ -1178,6 +1200,9 @@ _sp_app_mod_reinstantiate(sp_app_t *app, mod_t *mod)
lilv_instance_activate(mod->inst);
+ // some plugins need to run before they can be configured
+ lilv_instance_run(mod->inst, app->driver->min_block_size);
+
_sp_app_state_preset_restore(app, mod, state, false);
lilv_state_free(state);
diff --git a/app/synthpod_app_private.h b/app/synthpod_app_private.h
index 514b8a2e..3177ad7a 100644
--- a/app/synthpod_app_private.h
+++ b/app/synthpod_app_private.h
@@ -121,6 +121,7 @@ enum _job_type_request_t {
JOB_TYPE_REQUEST_MODULE_ADD,
JOB_TYPE_REQUEST_MODULE_DEL,
JOB_TYPE_REQUEST_MODULE_REINSTANTIATE,
+ JOB_TYPE_REQUEST_MODULE_SYSTEM_PORTS_UPDATE,
JOB_TYPE_REQUEST_PRESET_LOAD,
JOB_TYPE_REQUEST_PRESET_SAVE,
JOB_TYPE_REQUEST_BUNDLE_LOAD,
@@ -263,6 +264,7 @@ struct _mod_t {
LV2_URID urn;
LV2_URID visible;
bool disabled;
+ uint32_t created;
bool delete_request;
bool needs_bypassing;
@@ -445,6 +447,8 @@ struct _sp_app_t {
sp_app_driver_t *driver;
void *data;
+ atomic_bool visibility;
+
atomic_bool dirty;
unsigned skip_reweighting;
@@ -511,6 +515,7 @@ struct _sp_app_t {
int32_t column_enabled;
int32_t row_enabled;
+ uint32_t created;
};
extern const port_driver_t control_port_driver;
@@ -671,7 +676,8 @@ _sp_app_state_bundle_load(sp_app_t *app, const char *bundle_path);
* Mod
*/
mod_t *
-_sp_app_mod_add(sp_app_t *app, const char *uri, LV2_URID urn);
+_sp_app_mod_add(sp_app_t *app, const char *uri, LV2_URID urn, uint32_t created,
+ const char *alias);
void
_sp_app_mod_eject(sp_app_t *app, mod_t *mod);
diff --git a/app/synthpod_app_state.c b/app/synthpod_app_state.c
index 224e7829..f4e27eda 100644
--- a/app/synthpod_app_state.c
+++ b/app/synthpod_app_state.c
@@ -906,6 +906,12 @@ sp_app_save(sp_app_t *app, LV2_State_Store_Function store,
}
*/
+ // store visibility
+ const int32_t visibility = sp_app_visibility_get(app);
+ store(hndl, app->regs.synthpod.visibility.urid,
+ &visibility, sizeof(int32_t), app->forge.Bool,
+ LV2_STATE_IS_POD | LV2_STATE_IS_PORTABLE);
+
// store minor version
const int32_t minor_version = SYNTHPOD_MINOR_VERSION;
store(hndl, app->regs.core.minor_version.urid,
@@ -1006,6 +1012,12 @@ sp_app_save(sp_app_t *app, LV2_State_Store_Function store,
}
if(ref)
+ {
+ ref = lv2_atom_forge_key(forge, app->regs.synthpod.module_created.urid)
+ && lv2_atom_forge_int(forge, mod->created);
+ }
+
+ if(ref)
lv2_atom_forge_pop(forge, &mod_frame);
}
else
@@ -1378,6 +1390,7 @@ _mod_inject(sp_app_t *app, int32_t mod_uid, LV2_URID mod_urn, const LV2_Atom_Obj
const LV2_Atom_URID *mod_ui = NULL;
const LV2_Atom_Bool *mod_visible = NULL;
const LV2_Atom_Bool *mod_disabled = NULL;
+ const LV2_Atom_Int *mod_created = NULL;
lv2_atom_object_get(mod_obj,
app->regs.synthpod.module_position_x.urid, &mod_pos_x,
app->regs.synthpod.module_position_y.urid, &mod_pos_y,
@@ -1385,10 +1398,16 @@ _mod_inject(sp_app_t *app, int32_t mod_uid, LV2_URID mod_urn, const LV2_Atom_Obj
app->regs.ui.ui.urid, &mod_ui,
app->regs.synthpod.module_visible.urid, &mod_visible,
app->regs.synthpod.module_disabled.urid, &mod_disabled,
+ app->regs.synthpod.module_created.urid, &mod_created,
0);
+ const uint32_t created = mod_created && (mod_created->atom.type == app->forge.Int)
+ ? mod_created->body : 0;
+ const char *alias = mod_alias && (mod_alias->atom.type == app->forge.String)
+ ? (const char *)LV2_ATOM_BODY_CONST(mod_alias) : NULL;
+
const char *mod_uri_str = app->driver->unmap->unmap(app->driver->unmap->handle, mod_obj->body.otype);
- mod_t *mod = _sp_app_mod_add(app, mod_uri_str, mod_urn);
+ mod_t *mod = _sp_app_mod_add(app, mod_uri_str, mod_urn, created, alias);
if(!mod)
{
sp_app_log_error(app, "%s: _sp_app_mod_add fialed\n", __func__);
@@ -1407,8 +1426,6 @@ _mod_inject(sp_app_t *app, int32_t mod_uid, LV2_URID mod_urn, const LV2_Atom_Obj
? mod_visible->body : 0;
mod->disabled = mod_disabled && (mod_disabled->atom.type == app->forge.Bool)
? mod_disabled->body : false;
- if(mod_alias)
- strncpy(mod->alias, LV2_ATOM_BODY_CONST(&mod_alias->atom), ALIAS_MAX - 1);
mod->ui = mod_ui && (mod_ui->atom.type == app->forge.URID)
? mod_ui->body : 0;
@@ -1520,10 +1537,21 @@ sp_app_restore(sp_app_t *app, LV2_State_Retrieve_Function retrieve,
return LV2_STATE_ERR_UNKNOWN;
}
+ // reset created counter
+ app->created = 0;
+
size_t size;
uint32_t _flags;
uint32_t type;
+ // retrieve visibility
+ const int32_t *visibility = retrieve(hndl, app->regs.synthpod.visibility.urid,
+ &size, &type, &_flags);
+ if(visibility && (type == app->forge.Bool) && (size == sizeof(int32_t)) )
+ {
+ sp_app_visibility_set(app, *visibility);
+ }
+
// retrieve minor version
const int32_t *minor_version = retrieve(hndl, app->regs.core.minor_version.urid,
&size, &type, &_flags);
@@ -1560,11 +1588,17 @@ sp_app_restore(sp_app_t *app, LV2_State_Retrieve_Function retrieve,
{
mod_obj->body.otype = app->regs.synthpod.placeholder.urid;
mod = _mod_inject(app, mod_index, mod_urn, mod_obj, map_path);
- if(mod)
+ if(!mod)
{
snprintf(mod->alias, sizeof(mod->alias), "%s", "!!! Failed to load !!!");
+ continue;
}
}
+
+ if(mod->created > app->created)
+ {
+ app->created = mod->created;
+ }
}
_sp_app_order(app);
diff --git a/app/synthpod_app_ui.c b/app/synthpod_app_ui.c
index ccd11121..9be3bb5a 100644
--- a/app/synthpod_app_ui.c
+++ b/app/synthpod_app_ui.c
@@ -794,6 +794,11 @@ _sp_app_from_ui_patch_get(sp_app_t *app, const LV2_Atom *atom)
if(ref)
ref = lv2_atom_forge_urid(&app->forge, mod->ui);
}
+
+ if(ref)
+ ref = lv2_atom_forge_key(&app->forge, app->regs.ui.instance_access.urid);
+ if(ref)
+ ref = lv2_atom_forge_long(&app->forge, (intptr_t)mod->inst);
}
if(ref)
{
@@ -865,6 +870,23 @@ _sp_app_from_ui_patch_set(sp_app_t *app, const LV2_Atom *atom)
&& (value->type == app->forge.String) )
{
strncpy(mod->alias, LV2_ATOM_BODY_CONST(value), ALIAS_MAX - 1);
+
+ if(mod->system_ports)
+ {
+ // send request to worker thread
+ size_t size = sizeof(job_t);
+ job_t *job = _sp_app_to_worker_request(app, size);
+ if(job)
+ {
+ job->request = JOB_TYPE_REQUEST_MODULE_SYSTEM_PORTS_UPDATE;
+ job->mod = mod;
+ _sp_app_to_worker_advance(app, size);
+ }
+ else
+ {
+ sp_app_log_trace(app, "%s: buffer request failed\n", __func__);
+ }
+ }
}
else if( (prop == app->regs.ui.ui.urid)
&& (value->type == app->forge.URID) )
diff --git a/app/synthpod_app_worker.c b/app/synthpod_app_worker.c
index 3656d492..c5751b10 100644
--- a/app/synthpod_app_worker.c
+++ b/app/synthpod_app_worker.c
@@ -161,7 +161,27 @@ sp_app_from_worker(sp_app_t *app, uint32_t len, const void *data)
}
}
- //FIXME signal to ui
+ // signal to ui
+ LV2_Atom *answer = _sp_app_to_ui_request_atom(app);
+ if(answer)
+ {
+ const int64_t dsp_instance = (intptr_t )mod->inst;
+ LV2_Atom_Forge_Ref ref = synthpod_patcher_set(
+ &app->regs, &app->forge, mod->urn, 0, app->regs.ui.instance_access.urid, //FIXME seqnum
+ sizeof(int64_t), app->forge.Long, &dsp_instance);
+ if(ref)
+ {
+ _sp_app_to_ui_advance_atom(app, answer);
+ }
+ else
+ {
+ _sp_app_to_ui_overflow(app);
+ }
+ }
+ else
+ {
+ _sp_app_to_ui_overflow(app);
+ }
break;
}
@@ -351,7 +371,7 @@ sp_worker_from_app(sp_app_t *app, uint32_t len, const void *data)
case JOB_TYPE_REQUEST_MODULE_ADD:
{
const char *uri = app->driver->unmap->unmap(app->driver->unmap->handle, job->urn);
- mod_t *mod = uri ? _sp_app_mod_add(app, uri, 0) : NULL;
+ mod_t *mod = uri ? _sp_app_mod_add(app, uri, 0, 0, NULL) : NULL;
if(!mod)
break; //TODO report
@@ -413,6 +433,23 @@ sp_worker_from_app(sp_app_t *app, uint32_t len, const void *data)
break;
}
+ case JOB_TYPE_REQUEST_MODULE_SYSTEM_PORTS_UPDATE:
+ {
+ mod_t *mod = job->mod;
+
+ if(mod->system_ports && strlen(mod->alias) && app->driver->system_port_set)
+ {
+ for(unsigned i=0; i<mod->num_ports - 2; i++) // - automation ports
+ {
+ port_t *tar = &mod->ports[i];
+
+ app->driver->system_port_set(app->data, tar->sys.data,
+ SYNTHPOD_PREFIX"#moduleAlias", mod->alias);
+ }
+ }
+
+ break;
+ }
case JOB_TYPE_REQUEST_PRESET_LOAD:
{
const char *uri = app->driver->unmap->unmap(app->driver->unmap->handle, job->urn);
diff --git a/bin/meson.build b/bin/meson.build
index d901cf86..7d78d9d0 100644
--- a/bin/meson.build
+++ b/bin/meson.build
@@ -1,9 +1,11 @@
-bin_srcs = ['synthpod_bin.c']
+bin_srcs = ['synthpod_bin.c',
+ join_paths('..', 'sandbox_ui.lv2', 'sandbox_slave.c'),
+ 'synthpod_sandbox_x11_driver.c']
bin = static_library('synthpod_bin', bin_srcs,
include_directories : bin_incs,
c_args : c_args,
- dependencies : bin_deps)
+ dependencies : [bin_deps, xcb_dep, xcbicccm_dep, xcbxrm_dep])
if use_dummy
dummy_srcs = ['synthpod_dummy.c']
@@ -50,13 +52,14 @@ if use_jack and jack_dep.found()
install_man('synthpod_jack.1')
endif
-if use_x11 and xcb_dep.found() and xcbicccm_dep.found()
- x11_srcs = ['synthpod_sandbox_x11.c']
+if use_x11 and xcb_dep.found() and xcbicccm_dep.found() and xcbxrm_dep.found()
+ x11_srcs = ['synthpod_sandbox_x11.c',
+ 'synthpod_sandbox_x11_driver.c']
x11 = executable('synthpod_sandbox_x11', x11_srcs,
include_directories : bin_incs,
c_args : c_args,
- dependencies : [lv2_dep, rt_dep, thread_dep, xcb_dep, xcbicccm_dep],
+ dependencies : [lv2_dep, rt_dep, thread_dep, xcb_dep, xcbicccm_dep, xcbxrm_dep],
link_with : [sbox_slave],
install : true)
endif
diff --git a/bin/synthpod_alsa.1 b/bin/synthpod_alsa.1
index a30aa738..24f02ee4 100644
--- a/bin/synthpod_alsa.1
+++ b/bin/synthpod_alsa.1
@@ -46,6 +46,16 @@ Kill DSP with GUI
Do NOT kill DSP with GUI (default)
.HP
+\fB\-t\fR
+.IP
+Run GUI in threaded mode
+
+.HP
+\fB\-T\fR
+.IP
+Run GUI in separate process (default)
+
+.HP
\fB\-b\fR
.IP
Enable bad plugins
@@ -76,16 +86,11 @@ Disable playback
Disable capture
.HP
-\fB\-t\fR
+\fB\-2\fR
.IP
Force 2 channel stereo mode
.HP
-\fB\-T\fR
-.IP
-Do NOT force 2 channel stereo mode
-
-.HP
\fB\-x\fR
.IP
Notify about XRuns
@@ -116,6 +121,11 @@ Worker thread realtime priority (60)
Disable worker thread realtime priority
.HP
+\fB\-u\fR
+.IP
+Show alternate UI
+
+.HP
\fB\-l\fR
.IP
Socket link path (shm:///synthpod), e.g. tcp://*:9090
diff --git a/bin/synthpod_alsa.c b/bin/synthpod_alsa.c
index 63997039..3f0d45bf 100644
--- a/bin/synthpod_alsa.c
+++ b/bin/synthpod_alsa.c
@@ -779,9 +779,8 @@ _alsa_deinit(prog_t *handle)
}
__non_realtime static int
-_open(const char *path, const char *name, const char *id, void *data)
+_open(const char *path, const char *name, const char *id, bin_t *bin)
{
- bin_t *bin = data;
prog_t *handle = (void *)bin - offsetof(prog_t, bin);
(void)name;
@@ -817,46 +816,51 @@ _open(const char *path, const char *name, const char *id, void *data)
bin_log_error(bin, "%s: creation of realtime thread failed\n", __func__);
}
- bin_bundle_load(bin, bin->path);
-
- return 0; // success
+ return bin_bundle_load(bin, bin->path);
}
__non_realtime static int
-_save(void *data)
+_nsm_callback(void *data, const nsmc_event_t *ev)
{
bin_t *bin = data;
- prog_t *handle = (void *)bin - offsetof(prog_t, bin);
-
- bin_bundle_save(bin, bin->path);
- return 0; // success
-}
-
-__non_realtime static int
-_show(void *data)
-{
- bin_t *bin = data;
-
- return bin_show(bin);
-}
-
-__non_realtime static int
-_hide(void *data)
-{
- bin_t *bin = data;
+ switch(ev->type)
+ {
+ case NSMC_EVENT_TYPE_OPEN:
+ return _open(ev->open.path, ev->open.name, ev->open.id, bin);
+ case NSMC_EVENT_TYPE_SAVE:
+ return bin_bundle_save(bin, bin->path);
+ case NSMC_EVENT_TYPE_SHOW:
+ return bin_show(bin);
+ case NSMC_EVENT_TYPE_HIDE:
+ return bin_hide(bin);
+ case NSMC_EVENT_TYPE_SESSION_IS_LOADED:
+ return 0;
+
+ case NSMC_EVENT_TYPE_VISIBILITY:
+ return bin_visibility(bin);
+ case NSMC_EVENT_TYPE_CAPABILITY:
+ return NSMC_CAPABILITY_MESSAGE
+ | NSMC_CAPABILITY_SWITCH
+ | NSMC_CAPABILITY_OPTIONAL_GUI;
+
+ case NSMC_EVENT_TYPE_ERROR:
+ return bin_log_error(bin, "%s: (%i) %s", ev->error.request,
+ ev->error.code, ev->error.message);
+ case NSMC_EVENT_TYPE_REPLY:
+ return bin_log_note(bin, "%s", ev->reply.request);
+
+ case NSMC_EVENT_TYPE_NONE:
+ // fall-through
+ case NSMC_EVENT_TYPE_MAX:
+ // fall-through
+ default:
+ return 1;
+ }
- return bin_hide(bin);
+ return 0;
}
-static const nsmc_driver_t nsm_driver = {
- .open = _open,
- .save = _save,
- .show = _show,
- .hide = _hide,
- .supports_switch = true
-};
-
// rt
__realtime static double
_osc_schedule_osc2frames(LV2_OSC_Schedule_Handle instance, uint64_t timestamp)
@@ -932,6 +936,7 @@ main(int argc, char **argv)
bin->bad_plugins = false;
bin->has_gui = false;
bin->kill_gui = false;
+ bin->threaded_gui = false;
snprintf(bin->socket_path, sizeof(bin->socket_path), "shm:///synthpod-%i", getpid());
bin->update_rate = 25;
bin->cpu_affinity = false;
@@ -947,7 +952,7 @@ main(int argc, char **argv)
*/
int c;
- while((c = getopt(argc, argv, "vhgGbkKBaAIOtTxXy:Yw:Wl:d:i:o:r:p:n:s:c:f:")) != -1)
+ while((c = getopt(argc, argv, "vhgGbkKtTBaAIO2xXy:Yw:Wul:d:i:o:r:p:n:s:c:f:")) != -1)
{
switch(c)
{
@@ -980,20 +985,22 @@ main(int argc, char **argv)
" [-G] do NOT load GUI (default)\n"
" [-k] kill DSP with GUI\n"
" [-K] do NOT kill DSP with GUI (default)\n"
+ " [-t] run GUI in threaded mode\n"
+ " [-T] run GUI in separate process (default)\n"
" [-b] enable bad plugins\n"
" [-B] disable bad plugins (default)\n"
" [-a] enable CPU affinity\n"
" [-A] disable CPU affinity (default)\n"
" [-I] disable capture\n"
" [-O] disable playback\n"
- " [-t] force 2 channel mode\n"
- " [-T] do NOT force 2 channel mode (default)\n"
+ " [-2] force 2 channel mode\n"
" [-x] notify about XRuns\n"
" [-X] do NOT notify about XRuns (default)\n"
" [-y] audio-priority audio thread realtime priority (70)\n"
" [-Y] do NOT use audio thread realtime priority\n"
" [-w] worker-priority worker thread realtime priority (60)\n"
" [-W] do NOT use worker thread realtime priority\n"
+ " [-u] show alternate UI\n"
" [-l] link-path socket link path (shm:///synthpod)\n"
" [-d] device capture/playback device (\"hw:0\")\n"
" [-i] capture-device capture device (\"hw:0\")\n"
@@ -1018,6 +1025,12 @@ main(int argc, char **argv)
case 'K':
bin->kill_gui = false;
break;
+ case 't':
+ bin->threaded_gui = true;
+ break;
+ case 'T':
+ bin->threaded_gui = false;
+ break;
case 'b':
bin->bad_plugins = true;
break;
@@ -1036,12 +1049,9 @@ main(int argc, char **argv)
case 'O':
handle.do_play = false;
break;
- case 't':
+ case '2':
handle.twochan = true;
break;
- case 'T':
- handle.twochan = false;
- break;
case 'x':
handle.debug = true;
break;
@@ -1060,6 +1070,9 @@ main(int argc, char **argv)
case 'W':
bin->worker_prio = 0;
break;
+ case 'u':
+ bin->d2tk_gui = true;
+ break;
case 'l':
snprintf(bin->socket_path, sizeof(bin->socket_path), "%s", optarg);
break;
@@ -1135,7 +1148,7 @@ main(int argc, char **argv)
bin->app_driver.features |= SP_APP_FEATURE_POWER_OF_2_BLOCK_LENGTH;
// run
- bin_run(bin, "Synthpod-ALSA", argv, &nsm_driver);
+ bin_run(bin, "Synthpod-ALSA", argv, _nsm_callback);
// stop
bin_stop(bin);
diff --git a/bin/synthpod_bin.c b/bin/synthpod_bin.c
index 1f7fdf60..bc04ccc7 100644
--- a/bin/synthpod_bin.c
+++ b/bin/synthpod_bin.c
@@ -23,6 +23,8 @@
#include <signal.h>
#include <synthpod_bin.h>
+#include <sandbox_slave.h>
+#include <synthpod_sandbox_x11_driver.h>
#define ANSI_COLOR_RED "\x1b[31m"
#define ANSI_COLOR_GREEN "\x1b[32m"
@@ -410,6 +412,8 @@ bin_init(bin_t *bin, uint32_t sample_rate)
bin->worker_thread = pthread_self(); // thread ID of UI thread
bin->first = true;
+ atomic_init(&bin->gui_done, true);
+
bin->sb_driver.socket_path = bin->socket_path;
bin->sb_driver.map = bin->map;
bin->sb_driver.unmap = bin->unmap;
@@ -434,8 +438,60 @@ bin_init(bin_t *bin, uint32_t sample_rate)
cross_clock_init(&bin->clk_real, CROSS_CLOCK_REALTIME);
}
+static bool
+_gui_rolling_threaded(bin_t *bin)
+{
+ const bool done = atomic_load(&bin->gui_done);
+
+ return !done;
+}
+
+static bool
+_gui_rolling_ipc(bin_t *bin)
+{
+ bool rolling = true;
+
+ if(bin->child > 0)
+ {
+ int status;
+ const int res = waitpid(bin->child, &status, WUNTRACED | WNOHANG);
+ if(res < 0)
+ {
+ if(errno == ECHILD) // child not existing
+ {
+ rolling = false;
+ }
+ }
+ else if(res == bin->child)
+ {
+ if(!WIFSTOPPED(status) && !WIFCONTINUED(status)) // child exited/crashed
+ {
+ rolling = false;
+ }
+ }
+ }
+
+ if(!rolling)
+ {
+ bin->child = 0; // invalidate
+ }
+
+ return rolling;
+}
+
+static bool
+_gui_rolling(bin_t *bin)
+{
+ if(bin->threaded_gui)
+ {
+ return _gui_rolling_threaded(bin);
+ }
+
+ return _gui_rolling_ipc(bin);
+}
+
__realtime void
-bin_run(bin_t *bin, const char *name, char **argv, const nsmc_driver_t *nsm_driver)
+bin_run(bin_t *bin, const char *name, char **argv, nsmc_callback_t callback)
{
char *fallback_path = NULL;
@@ -456,7 +512,7 @@ bin_run(bin_t *bin, const char *name, char **argv, const nsmc_driver_t *nsm_driv
const char *exe = strrchr(argv[0], '/');
exe = exe ? exe + 1 : argv[0]; // we only want the program name without path
bin->nsm = nsmc_new(name, exe, fallback_path ? fallback_path : argv[optind],
- nsm_driver, bin); //TODO check
+ callback, bin); //TODO check
if(fallback_path)
{
@@ -497,35 +553,24 @@ bin_run(bin_t *bin, const char *name, char **argv, const nsmc_driver_t *nsm_driv
if(timedout)
{
// check if GUI still running
- if(bin->child > 0)
- {
- bool rolling = true;
+ const bool rolling = _gui_rolling(bin);
- int status;
- const int res = waitpid(bin->child, &status, WUNTRACED | WNOHANG);
- if(res < 0)
- {
- if(errno == ECHILD) // child not existing
- rolling = false;
- }
- else if(res == bin->child)
+ if(bin->last_rolling && !rolling)
+ {
+ if(nsmc_managed())
{
- if(!WIFSTOPPED(status) && !WIFCONTINUED(status)) // child exited/crashed
- rolling = false;
+ sp_app_visibility_set(bin->app, false);
+ nsmc_hidden(bin->nsm);
}
- if(!rolling)
+ if(bin->kill_gui || bin->threaded_gui)
{
- bin->child = 0; // invalidate
-
- if(nsmc_managed())
- nsmc_hidden(bin->nsm);
-
- if(bin->kill_gui)
- atomic_store_explicit(&done, true, memory_order_relaxed);
+ atomic_store_explicit(&done, true, memory_order_relaxed);
}
}
+ bin->last_rolling = rolling;
+
// schedule next timeout
uint64_t nanos = to.tv_nsec + nstep;
while(nanos >= nsecs)
@@ -577,13 +622,175 @@ bin_run(bin_t *bin, const char *name, char **argv, const nsmc_driver_t *nsm_driv
}
}
+#define ARGC 19
+static void *
+_gui_thread(void *data)
+{
+ bin_t *bin = data;
+
+ char srate [32];
+ char urate [32];
+ char wname [384];
+ char minimum [32];
+ snprintf(srate, sizeof(srate), "%"PRIu32, bin->sample_rate);
+ snprintf(urate, sizeof(urate), "%"PRIu32, bin->update_rate);
+ snprintf(wname, sizeof(wname), "Synthpod - %s", bin->socket_path);
+ snprintf(minimum, sizeof(minimum), "%zu", SBOX_BUF_SIZE);
+ char *ui_uri = bin->d2tk_gui
+ ? SYNTHPOD_ROOT_D2TK_URI
+ : SYNTHPOD_ROOT_NK_URI;
+
+ char *argv [ARGC + 1] = {
+ "synthpod_sandbox_x11",
+ "-p", SYNTHPOD_STEREO_URI,
+ "-P", SYNTHPOD_PLUGIN_DIR,
+ "-u", ui_uri,
+ "-U", SYNTHPOD_PLUGIN_DIR,
+ "-s", (char *)bin->socket_path,
+ "-w", wname,
+ "-m", minimum,
+ "-r", srate,
+ "-f", urate,
+ NULL
+ };
+
+ const intptr_t dummy = 1;
+ atomic_store(&bin->gui_done, false);
+ x11_app_run(ARGC, argv, (void *)dummy, &bin->gui_done);
+
+ return NULL;
+}
+#undef ARGC
+
+static int
+_bin_show_threaded(bin_t *bin)
+{
+ if(pthread_create(&bin->gui_thread, NULL, _gui_thread, bin) != 0)
+ {
+ return -1;
+ };
+
+ sp_app_visibility_set(bin->app, true);
+
+ return 0;
+}
+
+static int
+_bin_show_ipc(bin_t *bin)
+{
+ char srate [32];
+ char urate [32];
+ char wname [384];
+ char minimum [32];
+ snprintf(srate, sizeof(srate), "%"PRIu32, bin->sample_rate);
+ snprintf(urate, sizeof(urate), "%"PRIu32, bin->update_rate);
+ snprintf(wname, sizeof(wname), "Synthpod - %s", bin->socket_path);
+ snprintf(minimum, sizeof(minimum), "%zu", SBOX_BUF_SIZE);
+ char *ui_uri = bin->d2tk_gui
+ ? SYNTHPOD_ROOT_D2TK_URI
+ : SYNTHPOD_ROOT_NK_URI;
+
+ bin->child = vfork();
+ if(bin->child == 0) // child
+ {
+ char *const args [] = {
+ "synthpod_sandbox_x11",
+ "-p", SYNTHPOD_STEREO_URI,
+ "-P", SYNTHPOD_PLUGIN_DIR,
+ "-u", ui_uri,
+ "-U", SYNTHPOD_PLUGIN_DIR,
+ "-s", (char *)bin->socket_path,
+ "-w", wname,
+ "-m", minimum,
+ "-r", srate,
+ "-f", urate,
+ NULL
+ };
+
+ execvp(args[0], args);
+ }
+ else if(bin->child == -1)
+ {
+ return -1;
+ }
+
+ sp_app_visibility_set(bin->app, true);
+
+ return 0;
+}
+
+int
+bin_show(bin_t *bin)
+{
+ if(sp_app_visibility_get(bin->app))
+ {
+ return 0;
+ }
+
+ if(bin->threaded_gui)
+ {
+ return _bin_show_threaded(bin);
+ }
+
+ return _bin_show_ipc(bin);
+}
+
+static int
+_bin_hide_threaded(bin_t *bin)
+{
+ atomic_store(&bin->gui_done, true);
+ pthread_join(bin->gui_thread, NULL);
+
+ return 0;
+}
+
+static int
+_bin_hide_ipc(bin_t *bin)
+{
+ if(bin->child > 0)
+ {
+ int status;
+
+ kill(bin->child, SIGINT);
+ waitpid(bin->child, &status, WUNTRACED); // blocking waitpid
+ bin->child = 0;
+ }
+
+ return 0;
+}
+
+static int
+_bin_hide(bin_t *bin)
+{
+ if(bin->threaded_gui)
+ {
+ return _bin_hide_threaded(bin);
+ }
+
+ return _bin_hide_ipc(bin);
+}
+
+int
+bin_hide(bin_t *bin)
+{
+ sp_app_visibility_set(bin->app, false);
+
+ return _bin_hide(bin);
+}
+
+bool
+bin_visibility(bin_t *bin)
+{
+ return sp_app_visibility_get(bin->app);
+}
+
__non_realtime void
bin_stop(bin_t *bin)
{
// NSM deinit
nsmc_free(bin->nsm);
- bin_hide(bin);
+ _bin_hide(bin);
if(bin->path)
free(bin->path);
@@ -680,24 +887,44 @@ bin_process_post(bin_t *bin)
// nothing
}
-__non_realtime void
+__non_realtime int
bin_bundle_new(bin_t *bin)
{
// simply load the default state
- bin_bundle_load(bin, SYNTHPOD_PREFIX"stereo");
+ return bin_bundle_load(bin, SYNTHPOD_PREFIX"stereo");
}
-__non_realtime void
+__non_realtime int
bin_bundle_load(bin_t *bin, const char *bundle_path)
{
const LV2_URID urn = bin->map->map(bin->map->handle, bundle_path);
if(!urn)
{
bin_log_error(bin, "%s: invalid path: %s\n", __func__, bundle_path);
- return;
+ return 1;
}
+ const bool visibility_old = sp_app_visibility_get(bin->app);
+
sp_app_bundle_load(bin->app, urn, false);
+
+ const bool visibility_new = sp_app_visibility_get(bin->app);
+
+ if(visibility_old != visibility_new)
+ {
+ sp_app_visibility_set(bin->app, visibility_old);
+
+ if(visibility_new)
+ {
+ bin_show(bin);
+ }
+ else
+ {
+ bin_hide(bin);
+ }
+ }
+
+ return 0;
}
__non_realtime void
@@ -706,17 +933,18 @@ bin_bundle_reset(bin_t *bin)
sp_app_bundle_reset(bin->app);
}
-__non_realtime void
+__non_realtime int
bin_bundle_save(bin_t *bin, const char *bundle_path)
{
const LV2_URID urn = bin->map->map(bin->map->handle, bundle_path);
if(!urn)
{
bin_log_error(bin, "%s: invalid path: %s\n", __func__, bundle_path);
- return;
+ return 1;
}
- sp_app_bundle_save(bin->app, urn, false);
+ sp_app_bundle_save(bin->app, urn, false); //FIXME check
+ return 0;
}
__realtime void
@@ -776,61 +1004,3 @@ bin_log_trace(bin_t *bin, const char *fmt, ...)
return ret;
}
-
-int
-bin_show(bin_t *bin)
-{
- char srate [32];
- char urate [32];
- char wname [384];
- char minimum [32];
- snprintf(srate, sizeof(srate), "%"PRIu32, bin->sample_rate);
- snprintf(urate, sizeof(urate), "%"PRIu32, bin->update_rate);
- snprintf(wname, sizeof(wname), "Synthpod - %s", bin->socket_path);
- snprintf(minimum, sizeof(minimum), "%zu", SBOX_BUF_SIZE);
-
- bin->child = vfork();
- if(bin->child == 0) // child
- {
- char *const args [] = {
- "synthpod_sandbox_x11",
- "-p", SYNTHPOD_STEREO_URI,
- "-P", SYNTHPOD_PLUGIN_DIR,
-#if 0
- "-u", SYNTHPOD_ROOT_D2TK_URI,
-#else
- "-u", SYNTHPOD_ROOT_NK_URI,
-#endif
- "-U", SYNTHPOD_PLUGIN_DIR,
- "-s", (char *)bin->socket_path,
- "-w", wname,
- "-m", minimum,
- "-r", srate,
- "-f", urate,
- NULL
- };
-
- execvp(args[0], args);
- }
- else if(bin->child == -1)
- {
- return -1;
- }
-
- return 0;
-}
-
-int
-bin_hide(bin_t *bin)
-{
- if(bin->child > 0)
- {
- int status;
-
- kill(bin->child, SIGINT);
- waitpid(bin->child, &status, WUNTRACED); // blocking waitpid
- bin->child = 0;
- }
-
- return 0;
-}
diff --git a/bin/synthpod_bin.h b/bin/synthpod_bin.h
index 78ad8a50..9792e2e3 100644
--- a/bin/synthpod_bin.h
+++ b/bin/synthpod_bin.h
@@ -88,12 +88,18 @@ struct _bin_t {
LV2_Log_Log log;
+ bool last_rolling;
+ atomic_bool gui_done;
+
+ pthread_t gui_thread;
pthread_t worker_thread;
pthread_t dsp_thread;
atomic_flag trace_lock;
+ bool d2tk_gui;
bool has_gui;
bool kill_gui;
+ bool threaded_gui;
int audio_prio;
int worker_prio;
int num_slaves;
@@ -122,7 +128,7 @@ void
bin_init(bin_t *bin, uint32_t sample_rate);
void
-bin_run(bin_t *bin, const char *name, char **argv, const nsmc_driver_t *nsm_driver);
+bin_run(bin_t *bin, const char *name, char **argv, nsmc_callback_t callback);
void
bin_stop(bin_t *bin);
@@ -136,16 +142,16 @@ bin_process_pre(bin_t *bin, uint32_t nsamples, bool bypassed);
void
bin_process_post(bin_t *bin);
-void
+int
bin_bundle_new(bin_t *bin);
void
bin_bundle_reset(bin_t *bin);
-void
+int
bin_bundle_load(bin_t *bin, const char *bundle_path);
-void
+int
bin_bundle_save(bin_t *bin, const char *bundle_path);
void
@@ -169,4 +175,7 @@ bin_show(bin_t *bin);
int
bin_hide(bin_t *bin);
+bool
+bin_visibility(bin_t *bin);
+
#endif // _SYNTHPOD_BIN_H
diff --git a/bin/synthpod_dummy.1 b/bin/synthpod_dummy.1
index 4880b901..b8a1d886 100644
--- a/bin/synthpod_dummy.1
+++ b/bin/synthpod_dummy.1
@@ -46,6 +46,16 @@ Kill DSP with GUI
Do NOT kill DSP with GUI (default)
.HP
+\fB\-t\fR
+.IP
+Run GUI in threaded mode
+
+.HP
+\fB\-T\fR
+.IP
+Run GUI in separate process (default)
+
+.HP
\fB\-b\fR
.IP
Enable bad plugins
@@ -86,6 +96,11 @@ Worker thread realtime priority (60)
Disable worker thread realtime priority
.HP
+\fB\-u\fR
+.IP
+Show alternate UI
+
+.HP
\fB\-l\fR
.IP
Socket link path (shm:///synthpod), e.g. tcp://*:9090
diff --git a/bin/synthpod_dummy.c b/bin/synthpod_dummy.c
index 4179e8e5..99cf8d99 100644
--- a/bin/synthpod_dummy.c
+++ b/bin/synthpod_dummy.c
@@ -331,9 +331,8 @@ _system_port_del(void *data, void *sys_port)
}
__non_realtime static int
-_open(const char *path, const char *name, const char *id, void *data)
+_open(const char *path, const char *name, const char *id, bin_t *bin)
{
- bin_t *bin = data;
prog_t *handle = (void *)bin - offsetof(prog_t, bin);
(void)name;
@@ -362,46 +361,51 @@ _open(const char *path, const char *name, const char *id, void *data)
bin_log_error(bin, "%s: creation of realtime thread failed\n", __func__);
}
- bin_bundle_load(bin, bin->path);
-
- return 0; // success
-}
-
-__non_realtime static int
-_save(void *data)
-{
- bin_t *bin = data;
- prog_t *handle = (void *)bin - offsetof(prog_t, bin);
-
- bin_bundle_save(bin, bin->path);
-
- return 0; // success
+ return bin_bundle_load(bin, bin->path);
}
__non_realtime static int
-_show(void *data)
+_nsm_callback(void *data, const nsmc_event_t *ev)
{
bin_t *bin = data;
- return bin_show(bin);
-}
-
-__non_realtime static int
-_hide(void *data)
-{
- bin_t *bin = data;
+ switch(ev->type)
+ {
+ case NSMC_EVENT_TYPE_OPEN:
+ return _open(ev->open.path, ev->open.name, ev->open.id, bin);
+ case NSMC_EVENT_TYPE_SAVE:
+ return bin_bundle_save(bin, bin->path);
+ case NSMC_EVENT_TYPE_SHOW:
+ return bin_show(bin);
+ case NSMC_EVENT_TYPE_HIDE:
+ return bin_hide(bin);
+ case NSMC_EVENT_TYPE_SESSION_IS_LOADED:
+ return 0;
+
+ case NSMC_EVENT_TYPE_VISIBILITY:
+ return bin_visibility(bin);
+ case NSMC_EVENT_TYPE_CAPABILITY:
+ return NSMC_CAPABILITY_MESSAGE
+ | NSMC_CAPABILITY_SWITCH
+ | NSMC_CAPABILITY_OPTIONAL_GUI;
+
+ case NSMC_EVENT_TYPE_ERROR:
+ return bin_log_error(bin, "%s: (%i) %s", ev->error.request,
+ ev->error.code, ev->error.message);
+ case NSMC_EVENT_TYPE_REPLY:
+ return bin_log_note(bin, "%s", ev->reply.request);
+
+ case NSMC_EVENT_TYPE_NONE:
+ // fall-through
+ case NSMC_EVENT_TYPE_MAX:
+ // fall-through
+ default:
+ return 1;
+ }
- return bin_hide(bin);
+ return 0;
}
-static const nsmc_driver_t nsm_driver = {
- .open = _open,
- .save = _save,
- .show = _show,
- .hide = _hide,
- .supports_switch = true
-};
-
// rt
__realtime static double
_osc_schedule_osc2frames(LV2_OSC_Schedule_Handle instance, uint64_t timestamp)
@@ -468,6 +472,7 @@ main(int argc, char **argv)
bin->bad_plugins = false;
bin->has_gui = false;
bin->kill_gui = false;
+ bin->threaded_gui = false;
snprintf(bin->socket_path, sizeof(bin->socket_path), "shm:///synthpod-%i", getpid());
bin->update_rate = 25;
bin->cpu_affinity = false;
@@ -478,7 +483,7 @@ main(int argc, char **argv)
"Released under Artistic License 2.0 by Open Music Kontrollers\n");
int c;
- while((c = getopt(argc, argv, "vhgGkKbBaAy:Yw:Wl:r:p:s:c:f:")) != -1)
+ while((c = getopt(argc, argv, "vhgGkKtTbBaAy:Yw:Wul:r:p:s:c:f:")) != -1)
{
switch(c)
{
@@ -511,6 +516,8 @@ main(int argc, char **argv)
" [-G] do NOT load GUI (default)\n"
" [-k] kill DSP with GUI\n"
" [-K] do NOT kill DSP with GUI (default)\n"
+ " [-t] run GUI in threaded mode\n"
+ " [-T] run GUI in separate process (default)\n"
" [-b] enable bad plugins\n"
" [-B] disable bad plugins (default)\n"
" [-a] enable CPU affinity\n"
@@ -519,6 +526,7 @@ main(int argc, char **argv)
" [-Y] do NOT use audio thread realtime priority\n"
" [-w] worker-priority worker thread realtime priority (60)\n"
" [-W] do NOT use worker thread realtime priority\n"
+ " [-u] show alternate UI\n"
" [-l] link-path socket link path (shm:///synthpod)\n"
" [-r] sample-rate sample rate (48000)\n"
" [-p] sample-period frames per period (1024)\n"
@@ -539,6 +547,12 @@ main(int argc, char **argv)
case 'K':
bin->kill_gui = false;
break;
+ case 't':
+ bin->threaded_gui = true;
+ break;
+ case 'T':
+ bin->threaded_gui = false;
+ break;
case 'b':
bin->bad_plugins = true;
break;
@@ -563,6 +577,9 @@ main(int argc, char **argv)
case 'W':
bin->worker_prio = 0;
break;
+ case 'u':
+ bin->d2tk_gui = true;
+ break;
case 'l':
snprintf(bin->socket_path, sizeof(bin->socket_path), "%s", optarg);
break;
@@ -614,7 +631,7 @@ main(int argc, char **argv)
bin->app_driver.features |= SP_APP_FEATURE_POWER_OF_2_BLOCK_LENGTH;
// run
- bin_run(bin, "Synthpod-DUMMY", argv, &nsm_driver);
+ bin_run(bin, "Synthpod-DUMMY", argv, _nsm_callback);
// stop
bin_stop(bin);
diff --git a/bin/synthpod_jack.1 b/bin/synthpod_jack.1
index 905cbdc2..864c7759 100644
--- a/bin/synthpod_jack.1
+++ b/bin/synthpod_jack.1
@@ -46,6 +46,16 @@ Kill DSP with GUI
Do NOT kill DSP with GUI (default)
.HP
+\fB\-t\fR
+.IP
+Run GUI in threaded mode
+
+.HP
+\fB\-T\fR
+.IP
+Run GUI in separate process (default)
+
+.HP
\fB\-b\fR
.IP
Enable bad plugins
@@ -66,6 +76,11 @@ Enable CPU affinity
Disable CPU affinity (default)
.HP
+\fB\-u\fR
+.IP
+Show alternate UI
+
+.HP
\fB\-l\fR link-path
.IP
Socket link path (shm:///synthpod), e.g. tcp://*:9090
diff --git a/bin/synthpod_jack.c b/bin/synthpod_jack.c
index 7f93a9c5..1f27ce81 100644
--- a/bin/synthpod_jack.c
+++ b/bin/synthpod_jack.c
@@ -721,6 +721,39 @@ _system_port_del(void *data, void *sys_port)
}
__non_realtime static void
+_system_port_set(void *data, void *sys_port, const char *key, const void *body)
+{
+ bin_t *bin = data;
+ sp_app_t *app = bin->app;
+ prog_t *handle = (void *)bin - offsetof(prog_t, bin);
+
+ jack_port_t *jack_port = sys_port;
+
+ if(!jack_port || !handle->client)
+ return;
+
+ if(!strcmp(key, SYNTHPOD_PREFIX"#moduleAlias"))
+ {
+#if defined(JACK_HAS_METADATA_API)
+ jack_uuid_t uuid = jack_port_uuid(jack_port);
+ if(!jack_uuid_empty(uuid))
+ {
+ char pretty_name [128];
+ const char *alias = body;
+
+ const char *port_name = jack_port_name(jack_port);
+ uint32_t idx = port_name[strlen(port_name) - 1] - '0';
+
+ snprintf(pretty_name, sizeof(pretty_name), "%s - %"PRIu32, alias, idx);
+
+ jack_set_property(handle->client, uuid,
+ JACK_METADATA_PRETTY_NAME, pretty_name, "text/plain");
+ }
+#endif
+ }
+}
+
+__non_realtime static void
_shutdown(void *data)
{
prog_t *handle = data;
@@ -850,9 +883,8 @@ _jack_deinit(prog_t *handle)
}
__non_realtime static int
-_open(const char *path, const char *name, const char *id, void *data)
+_open(const char *path, const char *name, const char *id, bin_t *bin)
{
- bin_t *bin = data;
prog_t *handle = (void *)bin - offsetof(prog_t, bin);
(void)name;
@@ -902,46 +934,52 @@ _open(const char *path, const char *name, const char *id, void *data)
jack_activate(handle->client); //TODO check
- bin_bundle_load(bin, bin->path);
-
- return 0; // success
-}
-
-__non_realtime static int
-_save(void *data)
-{
- bin_t *bin = data;
- prog_t *handle = (void *)bin - offsetof(prog_t, bin);
-
- bin_bundle_save(bin, bin->path);
-
- return 0; // success
+ return bin_bundle_load(bin, bin->path);
}
__non_realtime static int
-_show(void *data)
+_nsm_callback(void *data, const nsmc_event_t *ev)
{
bin_t *bin = data;
- return bin_show(bin);
-}
-
-__non_realtime static int
-_hide(void *data)
-{
- bin_t *bin = data;
+ switch(ev->type)
+ {
+ case NSMC_EVENT_TYPE_OPEN:
+ return _open(ev->open.path, ev->open.name, ev->open.id, bin);
+ case NSMC_EVENT_TYPE_SAVE:
+ return bin_bundle_save(bin, bin->path);
+ case NSMC_EVENT_TYPE_SHOW:
+ return bin_show(bin);
+ case NSMC_EVENT_TYPE_HIDE:
+ return bin_hide(bin);
+ case NSMC_EVENT_TYPE_SESSION_IS_LOADED:
+ return 0;
+
+ case NSMC_EVENT_TYPE_VISIBILITY:
+ return bin_visibility(bin);
+ case NSMC_EVENT_TYPE_CAPABILITY:
+ return NSMC_CAPABILITY_MESSAGE
+ | NSMC_CAPABILITY_SWITCH
+ | NSMC_CAPABILITY_OPTIONAL_GUI;
+
+ case NSMC_EVENT_TYPE_ERROR:
+ return bin_log_error(bin, "%s: (%i) %s", ev->error.request,
+ ev->error.code, ev->error.message);
+ case NSMC_EVENT_TYPE_REPLY:
+ return bin_log_note(bin, "%s", ev->reply.request);
+
+ // fall-through
+ case NSMC_EVENT_TYPE_NONE:
+ // fall-through
+ case NSMC_EVENT_TYPE_MAX:
+ // fall-through
+ default:
+ return 1;
+ }
- return bin_hide(bin);
+ return 0;
}
-static const nsmc_driver_t nsm_driver = {
- .open = _open,
- .save = _save,
- .show = _show,
- .hide = _hide,
- .supports_switch = true
-};
-
// rt
__realtime static double
_osc_schedule_osc2frames(LV2_OSC_Schedule_Handle instance, uint64_t timestamp)
@@ -1006,6 +1044,7 @@ main(int argc, char **argv)
bin->bad_plugins = false;
bin->has_gui = false;
bin->kill_gui = false;
+ bin->threaded_gui = false;
snprintf(bin->socket_path, sizeof(bin->socket_path), "shm:///synthpod-%i", getpid());
bin->update_rate = 25;
bin->cpu_affinity = false;
@@ -1016,7 +1055,7 @@ main(int argc, char **argv)
"Released under Artistic License 2.0 by Open Music Kontrollers\n");
int c;
- while((c = getopt(argc, argv, "vhgGkKbBaAl:n:s:c:f:")) != -1)
+ while((c = getopt(argc, argv, "vhgGkKtTbBaAul:n:s:c:f:")) != -1)
{
switch(c)
{
@@ -1049,10 +1088,13 @@ main(int argc, char **argv)
" [-G] do NOT load GUI (default)\n"
" [-k] kill DSP with GUI\n"
" [-K] do NOT kill DSP with GUI (default)\n"
+ " [-t] run GUI in threaded mode\n"
+ " [-T] run GUI in separate process (default)\n"
" [-b] enable bad plugins\n"
" [-B] disable bad plugins (default)\n"
" [-a] enable CPU affinity\n"
" [-A] disable CPU affinity (default)\n"
+ " [-u] show alternate UI\n"
" [-l] link-path socket link path (shm:///synthpod)\n"
" [-n] server-name connect to named JACK daemon\n"
" [-s] sequence-size minimum sequence size (8192)\n"
@@ -1072,6 +1114,12 @@ main(int argc, char **argv)
case 'K':
bin->kill_gui = false;
break;
+ case 't':
+ bin->threaded_gui = true;
+ break;
+ case 'T':
+ bin->threaded_gui = false;
+ break;
case 'b':
bin->bad_plugins = true;
break;
@@ -1084,6 +1132,9 @@ main(int argc, char **argv)
case 'A':
bin->cpu_affinity = false;
break;
+ case 'u':
+ bin->d2tk_gui = true;
+ break;
case 'l':
snprintf(bin->socket_path, sizeof(bin->socket_path), "%s", optarg);
break;
@@ -1141,6 +1192,7 @@ main(int argc, char **argv)
bin->app_driver.system_port_add = _system_port_add;
bin->app_driver.system_port_del = _system_port_del;
+ bin->app_driver.system_port_set = _system_port_set;
handle.osc_sched.osc2frames = _osc_schedule_osc2frames;
handle.osc_sched.frames2osc = _osc_schedule_frames2osc;
@@ -1150,7 +1202,7 @@ main(int argc, char **argv)
bin->app_driver.features = SP_APP_FEATURE_POWER_OF_2_BLOCK_LENGTH; // always true for JACK
// run
- bin_run(bin, "Synthpod-JACK", argv, &nsm_driver);
+ bin_run(bin, "Synthpod-JACK", argv, _nsm_callback);
// stop
bin_stop(bin);
diff --git a/bin/synthpod_sandbox_gtk.c b/bin/synthpod_sandbox_gtk.c
index d8932031..b61a7a4a 100644
--- a/bin/synthpod_sandbox_gtk.c
+++ b/bin/synthpod_sandbox_gtk.c
@@ -23,7 +23,10 @@
#include <sandbox_slave.h>
#include <lv2/lv2plug.in/ns/extensions/ui/ui.h>
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
#include <gtk/gtk.h>
+#pragma GCC diagnostic pop
#include <glib-unix.h>
typedef struct _wrap_t wrap_t;
@@ -100,7 +103,7 @@ _init(sandbox_slave_t *sb, void *data)
.data = app->win
};
- if(!sandbox_slave_instantiate(sb, &parent_feature, &app->widget))
+ if(!sandbox_slave_instantiate(sb, &parent_feature, NULL, &app->widget))
goto fail;
if(!app->widget)
goto fail;
@@ -156,11 +159,31 @@ _deinit(void *data)
}
}
+static inline int
+_request(void *data, LV2_URID key, size_t path_len, char *path)
+{
+ app_t *app = data;
+ (void)app;
+
+ FILE *fin = popen("zenity --file-selection", "r");
+ const size_t len = fread(path, sizeof(char), path_len, fin);
+ pclose(fin);
+
+ if(len)
+ {
+ path[len] = '\0';
+ return 0;
+ }
+
+ return 1;
+}
+
static const sandbox_slave_driver_t driver = {
.init_cb = _init,
.run_cb = _run,
.deinit_cb = _deinit,
- .resize_cb = NULL
+ .resize_cb = NULL,
+ .request_cb = _request
};
int
diff --git a/bin/synthpod_sandbox_kx.c b/bin/synthpod_sandbox_kx.c
index cbec82db..2bbec535 100644
--- a/bin/synthpod_sandbox_kx.c
+++ b/bin/synthpod_sandbox_kx.c
@@ -34,6 +34,7 @@ typedef struct _app_t app_t;
struct _app_t {
sandbox_slave_t *sb;
+ void *dsp_instance; //FIXME use this
LV2_External_UI_Host host;
LV2_External_UI_Widget *widget;
@@ -72,10 +73,15 @@ _init(sandbox_slave_t *sb, void *data)
.data = &app->host
};
- if(!sandbox_slave_instantiate(sb, &parent_feature, &app->widget))
+ if(!sandbox_slave_instantiate(sb, &parent_feature, app->dsp_instance,
+ &app->widget))
+ {
return -1;
+ }
if(!app->widget)
+ {
return -1;
+ }
LV2_EXTERNAL_UI_SHOW(app->widget);
@@ -119,11 +125,31 @@ _deinit(void *data)
cross_clock_deinit(&app->clk_mono);
}
+static inline int
+_request(void *data, LV2_URID key, size_t path_len, char *path)
+{
+ app_t *app = data;
+ (void)app;
+
+ FILE *fin = popen("zenity --file-selection", "r");
+ const size_t len = fread(path, sizeof(char), path_len, fin);
+ pclose(fin);
+
+ if(len)
+ {
+ path[len] = '\0';
+ return 0;
+ }
+
+ return 1;
+}
+
static const sandbox_slave_driver_t driver = {
.init_cb = _init,
.run_cb = _run,
.deinit_cb = _deinit,
- .resize_cb = NULL
+ .resize_cb = NULL,
+ .request_cb = _request
};
int
diff --git a/bin/synthpod_sandbox_qt.cpp b/bin/synthpod_sandbox_qt.cpp
index 9bc12da6..950c0a86 100644
--- a/bin/synthpod_sandbox_qt.cpp
+++ b/bin/synthpod_sandbox_qt.cpp
@@ -123,7 +123,7 @@ _init(sandbox_slave_t *sb, void *data)
.data = (void *)app->win
};
- if(!sandbox_slave_instantiate(sb, &parent_feature, (void *)&app->widget))
+ if(!sandbox_slave_instantiate(sb, &parent_feature, NULL, (void *)&app->widget))
return -1;
if(!app->widget)
return -1;
@@ -158,11 +158,31 @@ _deinit(void *data)
delete a;
}
+static inline int
+_request(void *data, LV2_URID key, size_t path_len, char *path)
+{
+ app_t *app = (app_t *)data;
+ (void)app;
+
+ FILE *fin = popen("zenity --file-selection", "r");
+ const size_t len = fread(path, sizeof(char), path_len, fin);
+ pclose(fin);
+
+ if(len)
+ {
+ path[len] = '\0';
+ return 0;
+ }
+
+ return 1;
+}
+
static const sandbox_slave_driver_t driver = {
.init_cb = _init,
.run_cb = _run,
.deinit_cb = _deinit,
- .resize_cb = NULL
+ .resize_cb = NULL,
+ .request_cb = _request
};
int
diff --git a/bin/synthpod_sandbox_show.c b/bin/synthpod_sandbox_show.c
index 50902dde..ff42aea1 100644
--- a/bin/synthpod_sandbox_show.c
+++ b/bin/synthpod_sandbox_show.c
@@ -33,6 +33,7 @@ typedef struct _app_t app_t;
struct _app_t {
sandbox_slave_t *sb;
+ void *dsp_instance; //FIXME use this
LV2UI_Handle *handle;
const LV2UI_Idle_Interface *idle_iface;
const LV2UI_Show_Interface *show_iface;
@@ -55,14 +56,19 @@ _init(sandbox_slave_t *sb, void *data)
signal(SIGINT, _sig);
void *widget = NULL;
- if(!(app->handle = sandbox_slave_instantiate(sb, NULL, &widget)))
+ if(!(app->handle = sandbox_slave_instantiate(sb, NULL, app->dsp_instance,
+ &widget)))
+ {
return -1;
+ }
app->idle_iface = sandbox_slave_extension_data(sb, LV2_UI__idleInterface);
app->show_iface = sandbox_slave_extension_data(sb, LV2_UI__showInterface);
if(app->show_iface)
+ {
app->show_iface->show(app->handle);
+ }
cross_clock_init(&app->clk_mono, CROSS_CLOCK_MONOTONIC);
@@ -109,11 +115,31 @@ _deinit(void *data)
cross_clock_deinit(&app->clk_mono);
}
+static inline int
+_request(void *data, LV2_URID key, size_t path_len, char *path)
+{
+ app_t *app = data;
+ (void)app;
+
+ FILE *fin = popen("zenity --file-selection", "r");
+ const size_t len = fread(path, sizeof(char), path_len, fin);
+ pclose(fin);
+
+ if(len)
+ {
+ path[len] = '\0';
+ return 0;
+ }
+
+ return 1;
+}
+
static const sandbox_slave_driver_t driver = {
.init_cb = _init,
.run_cb = _run,
.deinit_cb = _deinit,
- .resize_cb = NULL
+ .resize_cb = NULL,
+ .request_cb = _request
};
int
diff --git a/bin/synthpod_sandbox_x11.c b/bin/synthpod_sandbox_x11.c
index 3075ee50..ea3910b8 100644
--- a/bin/synthpod_sandbox_x11.c
+++ b/bin/synthpod_sandbox_x11.c
@@ -15,289 +15,12 @@
* http://www.perlfoundation.org/artistic_license_2_0.
*/
-#include <stdatomic.h>
-#include <stdbool.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <string.h>
-
-#define CROSS_CLOCK_IMPLEMENTATION
-#include <cross_clock/cross_clock.h>
-
-#include <sandbox_slave.h>
-#include <lv2/lv2plug.in/ns/extensions/ui/ui.h>
-
-#include <xcb/xcb.h>
-#include <xcb/xcb_icccm.h>
-#include <signal.h>
-
-typedef struct _app_t app_t;
-
-struct _app_t {
- sandbox_slave_t *sb;
- LV2UI_Handle *handle;
- const LV2UI_Idle_Interface *idle_iface;
- const LV2UI_Resize *resize_iface;
-
- xcb_connection_t *conn;
- xcb_screen_t *screen;
- xcb_drawable_t win;
- xcb_drawable_t widget;
- xcb_intern_atom_cookie_t cookie;
- xcb_intern_atom_reply_t* reply;
- xcb_intern_atom_cookie_t cookie2;
- xcb_intern_atom_reply_t* reply2;
- int w;
- int h;
- cross_clock_t clk_real;
-};
-
-static atomic_bool done = ATOMIC_VAR_INIT(false);
-
-static inline void
-_sig(int signum)
-{
- atomic_store_explicit(&done, true, memory_order_relaxed);
-}
-
-static inline int
-_resize(void *data, int w, int h)
-{
- app_t *app= data;
-
- app->w = w;
- app->h = h;
-
- const uint32_t values [2] = {app->w, app->h};
- xcb_configure_window(app->conn, app->win,
- XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT, values);
- xcb_flush(app->conn);
-
- return 0;
-}
-
-static inline void
-_clone_size_hints(app_t *app)
-{
- // clone size hints from widget to parent window
- xcb_get_property_cookie_t reply = xcb_icccm_get_wm_size_hints(app->conn,
- app->widget, XCB_ATOM_WM_NORMAL_HINTS);
- xcb_size_hints_t size_hints;
- memset(&size_hints, 0, sizeof(size_hints));
- xcb_icccm_get_wm_size_hints_reply(app->conn, reply, &size_hints, NULL);
-
-#if 0
- fprintf(stdout, "%u, (%i, %i), (%i, %i), (%i, %i), (%i, %i), (%i, %i), (%i, %i), (%i, %i), (%i, %i), %u\n",
- size_hints.flags,
- size_hints.x, size_hints.y,
- size_hints.width, size_hints.height,
- size_hints.min_width, size_hints.min_height,
- size_hints.max_width, size_hints.max_height,
- size_hints.width_inc, size_hints.height_inc,
- size_hints.min_aspect_num, size_hints.min_aspect_den,
- size_hints.max_aspect_num, size_hints.max_aspect_den,
- size_hints.base_width, size_hints.base_height,
- size_hints.win_gravity);
-#endif
-
- // quirk for invalid min/max size hints reported by e.g. zyn
- if( (size_hints.flags & (XCB_ICCCM_SIZE_HINT_P_MIN_SIZE | XCB_ICCCM_SIZE_HINT_P_MAX_SIZE) )
- && (size_hints.min_width == 1) && (size_hints.min_height == 1)
- && (size_hints.max_width == 1) && (size_hints.max_height == 1) )
- {
- return;
- }
-
- xcb_icccm_set_wm_size_hints(app->conn, app->win, XCB_ATOM_WM_NORMAL_HINTS, &size_hints);
- xcb_flush(app->conn);
-}
-
-static inline int
-_init(sandbox_slave_t *sb, void *data)
-{
- app_t *app= data;
-
- signal(SIGINT, _sig);
-
- app->conn = xcb_connect(NULL, NULL);
- app->screen = xcb_setup_roots_iterator(xcb_get_setup(app->conn)).data;
- app->win = xcb_generate_id(app->conn);
- const uint32_t mask = XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK;
- const uint32_t values [2] = {
- app->screen->white_pixel,
- XCB_EVENT_MASK_STRUCTURE_NOTIFY
- };
-
- app->w = 640;
- app->h = 360;
- xcb_create_window(app->conn, XCB_COPY_FROM_PARENT, app->win, app->screen->root,
- 0, 0, app->w, app->h, 0,
- XCB_WINDOW_CLASS_INPUT_OUTPUT, app->screen->root_visual, mask, values);
-
- const char *title = sandbox_slave_title_get(sb);
- if(title)
- xcb_change_property(app->conn, XCB_PROP_MODE_REPLACE, app->win,
- XCB_ATOM_WM_NAME, XCB_ATOM_STRING, 8,
- strlen(title), title);
-
- app->cookie = xcb_intern_atom(app->conn, 1, 12, "WM_PROTOCOLS");
- app->reply = xcb_intern_atom_reply(app->conn, app->cookie, 0);
-
- app->cookie2 = xcb_intern_atom(app->conn, 0, 16, "WM_DELETE_WINDOW");
- app->reply2 = xcb_intern_atom_reply(app->conn, app->cookie2, 0);
-
- xcb_change_property(app->conn,
- XCB_PROP_MODE_REPLACE, app->win, (*app->reply).atom, 4, 32, 1, &(*app->reply2).atom);
-
- xcb_map_window(app->conn, app->win);
- xcb_flush(app->conn);
-
- const LV2_Feature parent_feature = {
- .URI = LV2_UI__parent,
- .data = (void *)(uintptr_t)app->win
- };
-
- if(!(app->handle = sandbox_slave_instantiate(sb, &parent_feature, (uintptr_t *)&app->widget)))
- return -1;
- if(!app->widget)
- return -1;
-
- _clone_size_hints(app);
-
- app->idle_iface = sandbox_slave_extension_data(sb, LV2_UI__idleInterface);
- app->resize_iface = sandbox_slave_extension_data(sb, LV2_UI__resize);
-
- // work-around for broken lsp-plugins
- if((uintptr_t)app->resize_iface == (uintptr_t)app->idle_iface)
- {
- app->resize_iface = NULL;
- }
-
- cross_clock_init(&app->clk_real, CROSS_CLOCK_REALTIME);
-
- return 0;
-}
-
-static inline void
-_run(sandbox_slave_t *sb, float update_rate, void *data)
-{
- app_t *app = data;
- const unsigned ns = 1000000000 / update_rate;
- struct timespec to;
- cross_clock_gettime(&app->clk_real, &to);
-
- while(!atomic_load_explicit(&done, memory_order_relaxed))
- {
- xcb_generic_event_t *e;
- while((e = xcb_poll_for_event(app->conn)))
- {
- switch(e->response_type & ~0x80)
- {
- case XCB_CONFIGURE_NOTIFY:
- {
- const xcb_configure_notify_event_t *ev = (const xcb_configure_notify_event_t *)e;
- if( (app->w != ev->width) || (app->h != ev->height) )
- {
- app->w = ev->width;
- app->h = ev->height;
-
- const uint32_t values [2] = {app->w, app->h};
- xcb_configure_window(app->conn, app->widget,
- XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT,
- &values);
- xcb_flush(app->conn);
-
- if(app->resize_iface)
- {
- app->resize_iface->ui_resize(app->handle, app->w, app->h);
- }
- }
- break;
- }
- case XCB_REPARENT_NOTIFY:
- {
- _clone_size_hints(app);
- } break;
- case XCB_CLIENT_MESSAGE:
- {
- const xcb_client_message_event_t *ev = (const xcb_client_message_event_t *)e;
- if(ev->data.data32[0] == (*app->reply2).atom)
- atomic_store_explicit(&done, true, memory_order_relaxed);
- break;
- }
- }
- free(e);
- }
-
- if(sandbox_slave_timedwait(sb, &to)) // timedout
- {
- struct timespec tf;
-
- cross_clock_gettime(&app->clk_real, &tf);
- const uint64_t dd = (tf.tv_sec - to.tv_sec) * 1000000000
- + (tf.tv_nsec - to.tv_nsec);
-
-#if 0
- printf(":: %i, %lums\n", getpid(), dd/1000000);
-#endif
-
- if(dd <= ns)
- {
- if(app->idle_iface)
- {
- if(app->idle_iface->idle(app->handle))
- atomic_store_explicit(&done, true, memory_order_relaxed);
- }
- }
-
- to.tv_nsec += ns;
- while(to.tv_nsec >= 1000000000)
- {
- to.tv_nsec -= 1000000000;
- to.tv_sec += 1;
- }
- }
- else
- {
- if(sandbox_slave_recv(sb))
- atomic_store_explicit(&done, true, memory_order_relaxed);
- }
- }
-}
-
-static inline void
-_deinit(void *data)
-{
- app_t *app = data;
-
- xcb_destroy_subwindows(app->conn, app->win);
- xcb_destroy_window(app->conn, app->win);
- xcb_disconnect(app->conn);
-
- cross_clock_deinit(&app->clk_real);
-}
-
-static const sandbox_slave_driver_t driver = {
- .init_cb = _init,
- .run_cb = _run,
- .deinit_cb = _deinit,
- .resize_cb = _resize
-};
+#include <synthpod_sandbox_x11_driver.h>
int
main(int argc, char **argv)
{
- static app_t app;
- int res;
-
- app.sb = sandbox_slave_new(argc, argv, &driver, &app, &res);
- if(app.sb)
- {
- sandbox_slave_run(app.sb);
- sandbox_slave_free(app.sb);
- return res;
- }
+ atomic_bool done = ATOMIC_VAR_INIT(false);
- return res;
+ return x11_app_run(argc, argv, NULL, &done);
}
diff --git a/bin/synthpod_sandbox_x11_driver.c b/bin/synthpod_sandbox_x11_driver.c
new file mode 100644
index 00000000..696cd760
--- /dev/null
+++ b/bin/synthpod_sandbox_x11_driver.c
@@ -0,0 +1,366 @@
+/*
+ * Copyright (c) 2015-2016 Hanspeter Portner (dev@open-music-kontrollers.ch)
+ *
+ * This is free software: you can redistribute it and/or modify
+ * it under the terms of the Artistic License 2.0 as published by
+ * The Perl Foundation.
+ *
+ * This source is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * Artistic License 2.0 for more details.
+ *
+ * You should have received a copy of the Artistic License 2.0
+ * along the source as a COPYING file. If not, obtain it from
+ * http://www.perlfoundation.org/artistic_license_2_0.
+ */
+
+#include <stdatomic.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+
+#define CROSS_CLOCK_IMPLEMENTATION
+#include <cross_clock/cross_clock.h>
+
+#include <sandbox_slave.h>
+#include <lv2/lv2plug.in/ns/extensions/ui/ui.h>
+
+#include <xcb/xcb.h>
+#include <xcb/xcb_icccm.h>
+#include <xcb/xcb_xrm.h>
+#include <signal.h>
+
+typedef struct _app_t app_t;
+
+struct _app_t {
+ sandbox_slave_t *sb;
+ void *dsp_instance;
+ LV2UI_Handle *handle;
+ const LV2UI_Idle_Interface *idle_iface;
+ const LV2UI_Resize *resize_iface;
+
+ xcb_connection_t *conn;
+ xcb_screen_t *screen;
+ xcb_drawable_t win;
+ xcb_drawable_t widget;
+ xcb_intern_atom_cookie_t cookie;
+ xcb_intern_atom_reply_t* reply;
+ xcb_intern_atom_cookie_t cookie2;
+ xcb_intern_atom_reply_t* reply2;
+ int w;
+ int h;
+ cross_clock_t clk_real;
+ atomic_bool *done;
+};
+
+static atomic_bool *_done;
+
+static inline void
+_sig(int signum)
+{
+ atomic_store_explicit(_done, true, memory_order_relaxed);
+}
+
+static inline int
+_resize(void *data, int w, int h)
+{
+ app_t *app= data;
+
+ app->w = w;
+ app->h = h;
+
+ const uint32_t values [2] = {app->w, app->h};
+ xcb_configure_window(app->conn, app->win,
+ XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT, values);
+ xcb_flush(app->conn);
+
+ return 0;
+}
+
+static inline void
+_clone_size_hints(app_t *app)
+{
+ // clone size hints from widget to parent window
+ xcb_get_property_cookie_t reply = xcb_icccm_get_wm_size_hints(app->conn,
+ app->widget, XCB_ATOM_WM_NORMAL_HINTS);
+ xcb_size_hints_t size_hints;
+ memset(&size_hints, 0, sizeof(size_hints));
+ xcb_icccm_get_wm_size_hints_reply(app->conn, reply, &size_hints, NULL);
+
+#if 0
+ fprintf(stdout, "%u, (%i, %i), (%i, %i), (%i, %i), (%i, %i), (%i, %i), (%i, %i), (%i, %i), (%i, %i), %u\n",
+ size_hints.flags,
+ size_hints.x, size_hints.y,
+ size_hints.width, size_hints.height,
+ size_hints.min_width, size_hints.min_height,
+ size_hints.max_width, size_hints.max_height,
+ size_hints.width_inc, size_hints.height_inc,
+ size_hints.min_aspect_num, size_hints.min_aspect_den,
+ size_hints.max_aspect_num, size_hints.max_aspect_den,
+ size_hints.base_width, size_hints.base_height,
+ size_hints.win_gravity);
+#endif
+
+ // quirk for invalid min/max size hints reported by e.g. zyn
+ if( (size_hints.flags & (XCB_ICCCM_SIZE_HINT_P_MIN_SIZE | XCB_ICCCM_SIZE_HINT_P_MAX_SIZE) )
+ && (size_hints.min_width == 1) && (size_hints.min_height == 1)
+ && (size_hints.max_width == 1) && (size_hints.max_height == 1) )
+ {
+ return;
+ }
+
+ xcb_icccm_set_wm_size_hints(app->conn, app->win, XCB_ATOM_WM_NORMAL_HINTS, &size_hints);
+ xcb_flush(app->conn);
+}
+
+static inline int
+_init(sandbox_slave_t *sb, void *data)
+{
+ app_t *app= data;
+
+ signal(SIGINT, _sig);
+
+ app->conn = xcb_connect(NULL, NULL);
+ app->screen = xcb_setup_roots_iterator(xcb_get_setup(app->conn)).data;
+ app->win = xcb_generate_id(app->conn);
+ const uint32_t mask = XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK;
+ const uint32_t values [2] = {
+ app->screen->white_pixel,
+ XCB_EVENT_MASK_STRUCTURE_NOTIFY
+ };
+
+ app->w = 640;
+ app->h = 360;
+ xcb_create_window(app->conn, XCB_COPY_FROM_PARENT, app->win, app->screen->root,
+ 0, 0, app->w, app->h, 0,
+ XCB_WINDOW_CLASS_INPUT_OUTPUT, app->screen->root_visual, mask, values);
+
+ const float dpi0 = 96.f;
+ float dpi1 = dpi0;
+
+ // read DPI from users's ~/.Xresources
+ xcb_xrm_database_t *database = xcb_xrm_database_from_default(app->conn);
+ if(database != NULL)
+ {
+ char *value = NULL;
+
+ if(xcb_xrm_resource_get_string(database, "Xft.dpi", NULL, &value) >= 0)
+ {
+ dpi1 = atof(value);
+ free(value);
+ }
+
+ xcb_xrm_database_free(database);
+ }
+
+ const float scale_factor = dpi1 / dpi0;
+ sandbox_slave_scale_factor_set(sb, scale_factor);
+
+ const char *title = sandbox_slave_title_get(sb);
+ if(title)
+ {
+ xcb_change_property(app->conn, XCB_PROP_MODE_REPLACE, app->win,
+ XCB_ATOM_WM_NAME, XCB_ATOM_STRING, 8,
+ strlen(title), title);
+ }
+
+ app->cookie = xcb_intern_atom(app->conn, 1, 12, "WM_PROTOCOLS");
+ app->reply = xcb_intern_atom_reply(app->conn, app->cookie, 0);
+
+ app->cookie2 = xcb_intern_atom(app->conn, 0, 16, "WM_DELETE_WINDOW");
+ app->reply2 = xcb_intern_atom_reply(app->conn, app->cookie2, 0);
+
+ xcb_change_property(app->conn,
+ XCB_PROP_MODE_REPLACE, app->win, (*app->reply).atom, 4, 32, 1, &(*app->reply2).atom);
+
+ xcb_map_window(app->conn, app->win);
+ xcb_flush(app->conn);
+
+ const LV2_Feature parent_feature = {
+ .URI = LV2_UI__parent,
+ .data = (void *)(uintptr_t)app->win
+ };
+
+ if(!(app->handle = sandbox_slave_instantiate(sb, &parent_feature,
+ app->dsp_instance, (uintptr_t *)&app->widget)))
+ {
+ return -1;
+ }
+ if(!app->widget)
+ {
+ return -1;
+ }
+
+ _clone_size_hints(app);
+
+ app->idle_iface = sandbox_slave_extension_data(sb, LV2_UI__idleInterface);
+ app->resize_iface = sandbox_slave_extension_data(sb, LV2_UI__resize);
+
+ // work-around for broken lsp-plugins
+ if((uintptr_t)app->resize_iface == (uintptr_t)app->idle_iface)
+ {
+ app->resize_iface = NULL;
+ }
+
+ cross_clock_init(&app->clk_real, CROSS_CLOCK_REALTIME);
+
+ return 0;
+}
+
+static inline void
+_run(sandbox_slave_t *sb, float update_rate, void *data)
+{
+ app_t *app = data;
+ const unsigned ns = 1000000000 / update_rate;
+ struct timespec to;
+ cross_clock_gettime(&app->clk_real, &to);
+
+ while(!atomic_load_explicit(app->done, memory_order_relaxed))
+ {
+ xcb_generic_event_t *e;
+ while((e = xcb_poll_for_event(app->conn)))
+ {
+ switch(e->response_type & ~0x80)
+ {
+ case XCB_CONFIGURE_NOTIFY:
+ {
+ const xcb_configure_notify_event_t *ev = (const xcb_configure_notify_event_t *)e;
+ if( (app->w != ev->width) || (app->h != ev->height) )
+ {
+ app->w = ev->width;
+ app->h = ev->height;
+
+ const uint32_t values [2] = {app->w, app->h};
+ xcb_configure_window(app->conn, app->widget,
+ XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT,
+ &values);
+ xcb_flush(app->conn);
+
+ if(app->resize_iface)
+ {
+ app->resize_iface->ui_resize(app->handle, app->w, app->h);
+ }
+ }
+ break;
+ }
+ case XCB_REPARENT_NOTIFY:
+ {
+ _clone_size_hints(app);
+ } break;
+ case XCB_CLIENT_MESSAGE:
+ {
+ const xcb_client_message_event_t *ev = (const xcb_client_message_event_t *)e;
+ if(ev->data.data32[0] == (*app->reply2).atom)
+ {
+ atomic_store_explicit(app->done, true, memory_order_relaxed);
+ }
+ break;
+ }
+ }
+ free(e);
+ }
+
+ if(sandbox_slave_timedwait(sb, &to)) // timedout
+ {
+ struct timespec tf;
+
+ cross_clock_gettime(&app->clk_real, &tf);
+ const uint64_t dd = (tf.tv_sec - to.tv_sec) * 1000000000
+ + (tf.tv_nsec - to.tv_nsec);
+
+#if 0
+ printf(":: %i, %lums\n", getpid(), dd/1000000);
+#endif
+
+ if(dd <= ns)
+ {
+ if(app->idle_iface)
+ {
+ if(app->idle_iface->idle(app->handle))
+ {
+ atomic_store_explicit(app->done, true, memory_order_relaxed);
+ }
+ }
+ }
+
+ to.tv_nsec += ns;
+ while(to.tv_nsec >= 1000000000)
+ {
+ to.tv_nsec -= 1000000000;
+ to.tv_sec += 1;
+ }
+ }
+ else
+ {
+ if(sandbox_slave_recv(sb))
+ {
+ atomic_store_explicit(app->done, true, memory_order_relaxed);
+ }
+ }
+ }
+}
+
+static inline void
+_deinit(void *data)
+{
+ app_t *app = data;
+
+ xcb_destroy_subwindows(app->conn, app->win);
+ xcb_destroy_window(app->conn, app->win);
+ xcb_disconnect(app->conn);
+
+ cross_clock_deinit(&app->clk_real);
+}
+
+static inline int
+_request(void *data, LV2_URID key, size_t path_len, char *path)
+{
+ app_t *app = data;
+ (void)app;
+
+ FILE *fin = popen("zenity --file-selection", "r");
+ const size_t len = fread(path, sizeof(char), path_len, fin);
+ pclose(fin);
+
+ if(len)
+ {
+ path[len] = '\0';
+ return 0;
+ }
+
+ return 1;
+}
+
+static const sandbox_slave_driver_t x11_driver = {
+ .init_cb = _init,
+ .run_cb = _run,
+ .deinit_cb = _deinit,
+ .resize_cb = _resize,
+ .request_cb = _request
+};
+
+int
+x11_app_run(int argc, char **argv, void *dsp_instance, atomic_bool *done)
+{
+ app_t app;
+ int res;
+
+ memset(&app, 0x0, sizeof(app_t));
+
+ app.done = done;
+ _done = done;
+
+ app.dsp_instance = dsp_instance;
+ app.sb = sandbox_slave_new(argc, argv, &x11_driver, &app, &res);
+ if(app.sb)
+ {
+ sandbox_slave_run(app.sb);
+ sandbox_slave_free(app.sb);
+ return res;
+ }
+
+ return res;
+}
diff --git a/bin/synthpod_sandbox_x11_driver.h b/bin/synthpod_sandbox_x11_driver.h
new file mode 100644
index 00000000..8500a772
--- /dev/null
+++ b/bin/synthpod_sandbox_x11_driver.h
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2015-2016 Hanspeter Portner (dev@open-music-kontrollers.ch)
+ *
+ * This is free software: you can redistribute it and/or modify
+ * it under the terms of the Artistic License 2.0 as published by
+ * The Perl Foundation.
+ *
+ * This source is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * Artistic License 2.0 for more details.
+ *
+ * You should have received a copy of the Artistic License 2.0
+ * along the source as a COPYING file. If not, obtain it from
+ * http://www.perlfoundation.org/artistic_license_2_0.
+ */
+
+#include <stddef.h>
+#include <stdbool.h>
+#include <stdatomic.h>
+
+int
+x11_app_run(int argc, char **argv, void *dsp_instance, atomic_bool *done);
diff --git a/bundle/meson.build b/bundle/meson.build
index 2b44fb01..ea6caa86 100644
--- a/bundle/meson.build
+++ b/bundle/meson.build
@@ -7,16 +7,37 @@ bndl = shared_module('synthpod_bundle', bndl_srcs,
install : true,
install_dir : bndl_dir)
-configure_file(
+bndl_manifest_ttl = configure_file(
input : 'manifest.ttl.in',
output : 'manifest.ttl',
configuration : conf_data,
install : true,
install_dir : bndl_dir)
-configure_file(
+bndl_dsp_ttl = configure_file(
input : 'synthpod_bundle.ttl',
output : 'synthpod_bundle.ttl',
copy : true,
install : true,
install_dir : bndl_dir)
+
+if lv2_validate.found() and sord_validate.found()
+ test('LV2 validate', lv2_validate,
+ args : [bndl_manifest_ttl, bndl_dsp_ttl])
+endif
+
+if lv2lint.found()
+ test('LV2 lint', lv2lint,
+ args : ['-I', join_paths(build_root, 'bundle', ''),
+ '-Ewarn',
+ 'http://open-music-kontrollers.ch/lv2/synthpod#source',
+ 'http://open-music-kontrollers.ch/lv2/synthpod#sink',
+ 'http://open-music-kontrollers.ch/lv2/synthpod#osc_source',
+ 'http://open-music-kontrollers.ch/lv2/synthpod#osc_sink',
+ 'http://open-music-kontrollers.ch/lv2/synthpod#cv_source',
+ 'http://open-music-kontrollers.ch/lv2/synthpod#cv_sink',
+ 'http://open-music-kontrollers.ch/lv2/synthpod#audio_source',
+ 'http://open-music-kontrollers.ch/lv2/synthpod#audio_sink',
+ 'http://open-music-kontrollers.ch/lv2/synthpod#midi_source',
+ 'http://open-music-kontrollers.ch/lv2/synthpod#midi_sink'])
+endif
diff --git a/bundle/synthpod_bundle.ttl b/bundle/synthpod_bundle.ttl
index 3946bbac..0067130c 100644
--- a/bundle/synthpod_bundle.ttl
+++ b/bundle/synthpod_bundle.ttl
@@ -23,6 +23,7 @@
@prefix bufsz: <http://lv2plug.in/ns/ext/buf-size#> .
@prefix pg: <http://lv2plug.in/ns/ext/port-groups#> .
@prefix patch: <http://lv2plug.in/ns/ext/patch#> .
+@prefix urid: <http://lv2plug.in/ns/ext/urid#> .
@prefix lic: <http://opensource.org/licenses/> .
@prefix omk: <http://open-music-kontrollers.ch/ventosus#> .
@@ -108,7 +109,7 @@ synthpod:sink
"Synthpod Syschtem Ziiu"@gsw ;
doap:license lic:Artistic-2.0 ;
lv2:project proj:synthpod ;
- lv2:requiredFeature bufsz:boundedBlockLength ;
+ lv2:requiredFeature bufsz:boundedBlockLength, urid:map ;
lv2:optionalFeature lv2:isLive, lv2:hardRTCapable, synthpod:systemPorts ;
lv2:port [
@@ -221,7 +222,7 @@ synthpod:source
"Synthpod System Quelle"@de ;
doap:license lic:Artistic-2.0 ;
lv2:project proj:synthpod ;
- lv2:requiredFeature bufsz:boundedBlockLength ;
+ lv2:requiredFeature bufsz:boundedBlockLength, urid:map ;
lv2:optionalFeature lv2:isLive, lv2:hardRTCapable, synthpod:systemPorts ;
lv2:port [
@@ -322,7 +323,7 @@ synthpod:midi_sink
"Synthpod MIDI Ziel"@de ;
doap:license lic:Artistic-2.0 ;
lv2:project proj:synthpod ;
- lv2:requiredFeature bufsz:boundedBlockLength, synthpod:systemPorts ;
+ lv2:requiredFeature bufsz:boundedBlockLength, synthpod:systemPorts, urid:map ;
lv2:optionalFeature lv2:isLive, lv2:hardRTCapable ;
lv2:port [
@@ -346,7 +347,7 @@ synthpod:midi_source
"Synthpod MIDI Quelle"@de ;
doap:license lic:Artistic-2.0 ;
lv2:project proj:synthpod ;
- lv2:requiredFeature bufsz:boundedBlockLength, synthpod:systemPorts ;
+ lv2:requiredFeature bufsz:boundedBlockLength, synthpod:systemPorts, urid:map ;
lv2:optionalFeature lv2:isLive, lv2:hardRTCapable ;
lv2:port [
@@ -370,7 +371,7 @@ synthpod:osc_sink
"Synthpod OSC Ziel"@de ;
doap:license lic:Artistic-2.0 ;
lv2:project proj:synthpod ;
- lv2:requiredFeature bufsz:boundedBlockLength, synthpod:systemPorts ;
+ lv2:requiredFeature bufsz:boundedBlockLength, synthpod:systemPorts, urid:map ;
lv2:optionalFeature lv2:isLive, lv2:hardRTCapable ;
lv2:port [
@@ -395,7 +396,7 @@ synthpod:osc_source
"Synthpod OSC Quelle"@de ;
doap:license lic:Artistic-2.0 ;
lv2:project proj:synthpod ;
- lv2:requiredFeature bufsz:boundedBlockLength, synthpod:systemPorts ;
+ lv2:requiredFeature bufsz:boundedBlockLength, synthpod:systemPorts, urid:map ;
lv2:optionalFeature lv2:isLive, lv2:hardRTCapable ;
lv2:port [
@@ -430,6 +431,9 @@ synthpod:cv_sink
lv2:index 0 ;
lv2:symbol "cv_sink_1" ;
lv2:name "CV Sink 1" ;
+ lv2:default 0.0 ;
+ lv2:minimum 0.0 ;
+ lv2:maximum 1.0 ;
pg:group synthpod:group_sink ;
] , [
a lv2:InputPort ,
@@ -438,6 +442,9 @@ synthpod:cv_sink
lv2:index 1 ;
lv2:symbol "cv_sink_2" ;
lv2:name "CV Sink 2" ;
+ lv2:default 0.0 ;
+ lv2:minimum 0.0 ;
+ lv2:maximum 1.0 ;
pg:group synthpod:group_sink ;
] , [
a lv2:InputPort ,
@@ -446,6 +453,9 @@ synthpod:cv_sink
lv2:index 2 ;
lv2:symbol "cv_sink_3" ;
lv2:name "CV Sink 3" ;
+ lv2:default 0.0 ;
+ lv2:minimum 0.0 ;
+ lv2:maximum 1.0 ;
pg:group synthpod:group_sink ;
] , [
a lv2:InputPort ,
@@ -454,6 +464,9 @@ synthpod:cv_sink
lv2:index 3 ;
lv2:symbol "cv_sink_4" ;
lv2:name "CV Sink 4" ;
+ lv2:default 0.0 ;
+ lv2:minimum 0.0 ;
+ lv2:maximum 1.0 ;
pg:group synthpod:group_sink ;
] .
diff --git a/include/synthpod_app.h b/include/synthpod_app.h
index 563d5398..aa37b900 100644
--- a/include/synthpod_app.h
+++ b/include/synthpod_app.h
@@ -48,6 +48,8 @@ typedef void *(*sp_system_port_add)(void *data, system_port_t type,
const char *short_name, const char *pretty_name, const char *designation,
bool input, uint32_t order);
typedef void (*sp_system_port_del)(void *data, void *sys_port);
+typedef void (*sp_system_port_set)(void *data, void *sys_port, const char *key,
+ const void *body);
typedef void (*sp_close_request_t)(void *data);
@@ -110,6 +112,7 @@ struct _sp_app_driver_t {
// system_port
sp_system_port_add system_port_add;
sp_system_port_del system_port_del;
+ sp_system_port_set system_port_set;
// clock_sync
LV2_OSC_Schedule *osc_sched;
@@ -207,6 +210,12 @@ void
sp_app_bundle_save(sp_app_t *app, LV2_URID urn, bool via_app);
void
+sp_app_visibility_set(sp_app_t *app, bool visibility);
+
+bool
+sp_app_visibility_get(sp_app_t *app);
+
+void
sp_app_xrun_report(sp_app_t *app);
#endif // _SYNTHPOD_APP_H
diff --git a/include/synthpod_private.h b/include/synthpod_private.h
index 7f7744e6..f7d4cc28 100644
--- a/include/synthpod_private.h
+++ b/include/synthpod_private.h
@@ -362,6 +362,7 @@ struct _reg_t {
reg_item_t module_position_y;
reg_item_t module_alias;
reg_item_t module_reinstantiate;
+ reg_item_t module_created;
reg_item_t node_position_x;
reg_item_t node_position_y;
reg_item_t graph_position_x;
@@ -408,6 +409,7 @@ struct _reg_t {
reg_item_t learning;
reg_item_t placeholder;
+ reg_item_t visibility;
} synthpod;
struct {
@@ -656,6 +658,7 @@ sp_regs_init(reg_t *regs, LilvWorld *world, LV2_URID_Map *map)
_register(&regs->synthpod.module_position_y, world, map, SYNTHPOD_PREFIX"modulePositionY");
_register(&regs->synthpod.module_alias, world, map, SYNTHPOD_PREFIX"moduleAlias");
_register(&regs->synthpod.module_reinstantiate, world, map, SYNTHPOD_PREFIX"moduleReinstantiate");
+ _register(&regs->synthpod.module_created, world, map, SYNTHPOD_PREFIX"moduleCreated");
_register(&regs->synthpod.node_position_x, world, map, SYNTHPOD_PREFIX"nodePositionX");
_register(&regs->synthpod.node_position_y, world, map, SYNTHPOD_PREFIX"nodePositionY");
_register(&regs->synthpod.graph_position_x, world, map, SYNTHPOD_PREFIX"graphPositionX");
@@ -702,6 +705,7 @@ sp_regs_init(reg_t *regs, LilvWorld *world, LV2_URID_Map *map)
_register(&regs->synthpod.learning, world, map, SYNTHPOD_PREFIX"learning");
_register(&regs->synthpod.placeholder, world, map, SYNTHPOD_PREFIX"placeholder");
+ _register(&regs->synthpod.visibility, world, map, SYNTHPOD_PREFIX"visibility");
_register(&regs->midi.Controller, world, map, LV2_MIDI__Controller);
_register(&regs->midi.channel, world, map, LV2_MIDI__channel);
@@ -915,6 +919,7 @@ sp_regs_deinit(reg_t *regs)
_unregister(&regs->synthpod.module_position_y);
_unregister(&regs->synthpod.module_alias);
_unregister(&regs->synthpod.module_reinstantiate);
+ _unregister(&regs->synthpod.module_created);
_unregister(&regs->synthpod.node_position_x);
_unregister(&regs->synthpod.node_position_y);
_unregister(&regs->synthpod.graph_position_x);
@@ -961,6 +966,7 @@ sp_regs_deinit(reg_t *regs)
_unregister(&regs->synthpod.learning);
_unregister(&regs->synthpod.placeholder);
+ _unregister(&regs->synthpod.visibility);
_unregister(&regs->midi.Controller);
_unregister(&regs->midi.channel);
diff --git a/meson.build b/meson.build
index 36800c4c..712ed017 100644
--- a/meson.build
+++ b/meson.build
@@ -5,16 +5,20 @@ project('synthpod', ['c', 'cpp'], default_options : [
'b_lto=false',
'c_std=gnu11'])
+build_root = meson.build_root()
+
d2tk = subproject('d2tk')
+nk_pugl = subproject('nk_pugl')
-use_backend = get_option('ui-backend')
-if use_backend == 'nanovg'
+if get_option('use-backend-nanovg').enabled()
d2tk_dep = d2tk.get_variable('d2tk_nanovg')
-elif use_backend == 'cairo'
+elif get_option('use-backend-cairo').enabled()
d2tk_dep = d2tk.get_variable('d2tk_cairo')
else
error('no valid UI backend given')
endif
+
+nk_pugl_dep = nk_pugl.get_variable('nk_pugl_gl')
cc = meson.get_compiler('c')
lv2_validate = find_program('lv2_validate', native : true, required : false)
@@ -22,6 +26,7 @@ sord_validate = find_program('sord_validate', native : true, required : false)
lv2lint = find_program('lv2lint', required : false)
inc_incs = include_directories('include')
+binbin_incs = include_directories('bin')
app_incs = include_directories('app')
xpress_incs = include_directories('xpress.lv2')
osc_incs = include_directories('osc.lv2')
@@ -35,13 +40,13 @@ mapper_incs = include_directories('mapper.lv2')
sbox_incs = include_directories('sandbox_ui.lv2')
netatom_incs = include_directories('netatom.lv2')
props_incs = include_directories('props.lv2')
-pugl_incs = include_directories('pugl')
jackey_incs = include_directories('jackey')
nsmc_incs = include_directories('nsmc')
self_incs = include_directories('')
d2tk_incs = include_directories(join_paths('subprojects', 'd2tk'))
+nk_pugl_incs = include_directories(join_paths('subprojects', 'nk_pugl'))
-bin_incs = [inc_incs, app_incs, xpress_incs, osc_incs, canvas_incs, extui_incs, ardour_incs, varchunk_incs, crossclock_incs, lfrtm_incs, mapper_incs, sbox_incs, props_incs, pugl_incs, jackey_incs, nsmc_incs, self_incs, d2tk_incs]
+bin_incs = [inc_incs, binbin_incs, app_incs, xpress_incs, osc_incs, canvas_incs, extui_incs, ardour_incs, varchunk_incs, crossclock_incs, lfrtm_incs, mapper_incs, sbox_incs, props_incs, jackey_incs, nsmc_incs, self_incs, d2tk_incs, nk_pugl_incs, netatom_incs]
static_link = meson.is_cross_build() and false #FIXME
@@ -56,7 +61,7 @@ use_qt5 = get_option('use-qt5')
m_dep = cc.find_library('m')
rt_dep = cc.find_library('rt')
-lv2_dep = dependency('lv2', version : '>=1.14.0')
+lv2_dep = dependency('lv2', version : '>=1.18.0')
thread_dep = dependency('threads')
lilv_dep = dependency('lilv-0', version : '>=0.24.0', static : static_link)
alsa_dep = dependency('alsa', version : '>=1.1.0', required : use_alsa)
@@ -67,6 +72,7 @@ x11_dep = dependency('x11', version : '>=1.6.0', required : use_x11)
xext_dep = dependency('xext', version : '>=1.3.0', required : use_x11)
xcb_dep = dependency('xcb', version : '>=1.12', required : use_x11)
xcbicccm_dep = dependency('xcb-icccm', version : '>=0.4.0', required : use_x11)
+xcbxrm_dep = dependency('xcb-xrm', version : '>=1.0', required : use_x11)
gtk2_dep = dependency('gtk+-2.0', version : '>=2.24', required : use_gtk2)
gtk3_dep = dependency('gtk+-3.0', version : '>=3.22', required : use_gtk3)
qt4_dep = dependency('qt4', modules : ['Core', 'Gui'], required : use_qt4)
@@ -96,7 +102,7 @@ if use_x11 and gl_dep.found()
add_project_arguments('-DPUGL_HAVE_GL', language : 'c')
endif
-if use_x11 and xcb_dep.found() and xcbicccm_dep.found()
+if use_x11 and xcb_dep.found() and xcbicccm_dep.found() and xcbxrm_dep.found()
message('building X11 sandbox')
endif
@@ -148,7 +154,6 @@ if cc.compiles(builtin_assume_aligned, name : 'builtin_assume_aligned')
endif
c_args = ['-fvisibility=hidden',
- '-ffast-math',
'-Wno-attributes',
'-Wno-unused-function',
'-Wno-unused-variable']
diff --git a/meson_options.txt b/meson_options.txt
index 3edc68f9..a2bf775c 100644
--- a/meson_options.txt
+++ b/meson_options.txt
@@ -1,10 +1,40 @@
option('dynamic-parallelizer', type : 'boolean', value : true)
+
option('use-jack', type : 'boolean', value : true)
option('use-alsa', type : 'boolean', value : true)
option('use-dummy', type : 'boolean', value : true)
+
option('use-x11', type : 'boolean', value : true)
option('use-qt4', type : 'boolean', value : false)
option('use-qt5', type : 'boolean', value : false)
option('use-gtk2', type : 'boolean', value : false)
option('use-gtk3', type : 'boolean', value : false)
-option('ui-backend', type : 'string', value : 'nanovg')
+
+option('build-debug-overlay',
+ type : 'boolean',
+ value : false)
+option('build-tests',
+ type : 'boolean',
+ value : true)
+
+option('use-backend-cairo',
+ type : 'feature',
+ value : 'disabled')
+option('use-backend-nanovg',
+ type : 'feature',
+ value : 'enabled')
+
+option('use-frontend-pugl',
+ type : 'feature',
+ value : 'enabled')
+
+option('use-vterm',
+ type : 'feature',
+ value : 'disabled')
+option('use-fontconfig',
+ type : 'feature',
+ value : 'enabled')
+
+option('lv2libdir',
+ type : 'string',
+ value : 'lib/lv2')
diff --git a/nsmc/nsmc.h b/nsmc/nsmc.h
index 93e9b4ee..7a24c7c1 100644
--- a/nsmc/nsmc.h
+++ b/nsmc/nsmc.h
@@ -27,49 +27,165 @@ extern "C" {
#endif
#include <stdbool.h>
+#include <stdarg.h>
+
+typedef enum _nsmc_err_t {
+ NSMC_ERR_GENERAL = -1,
+ NSMC_ERR_INCOMPATIBLE_API = -2,
+ NSMC_ERR_BLACKLISTED = -3,
+ NSMC_ERR_LAUNCH_FAILED = -4,
+ NSMC_ERR_NO_SUCH_FILE = -5,
+ NSMC_ERR_NO_SESSION_OPEN = -6,
+ NSMC_ERR_UNSAVED_CHANGES = -7,
+ NSMC_ERR_NOT_NOW = -8,
+ NSMC_ERR_BAD_PROJECT = -9,
+ NSMC_ERR_CREATE_FAILED = -10
+} nsmc_err_t;
+
+typedef enum _nsmc_capability_t {
+ NSMC_CAPABILITY_NONE = 0,
+
+ // client only
+ NSMC_CAPABILITY_SWITCH = (1 << 0),
+ NSMC_CAPABILITY_DIRTY = (1 << 1),
+ NSMC_CAPABILITY_PROGRESS = (1 << 2),
+ NSMC_CAPABILITY_MESSAGE = (1 << 3),
+
+ // client + server
+ NSMC_CAPABILITY_OPTIONAL_GUI = (1 << 4),
+
+ // server only
+ NSMC_CAPABILITY_SERVER_CONTROL = (1 << 5),
+ NSMC_CAPABILITY_BROADCAST = (1 << 6),
+
+ NSMC_CAPABILITY_MAX
+} nsmc_capability_t;
+
+typedef enum _nsmc_event_type_t {
+ NSMC_EVENT_TYPE_NONE = 0,
+
+ NSMC_EVENT_TYPE_OPEN,
+ NSMC_EVENT_TYPE_SAVE,
+ NSMC_EVENT_TYPE_SHOW,
+ NSMC_EVENT_TYPE_HIDE,
+ NSMC_EVENT_TYPE_SESSION_IS_LOADED,
+
+ NSMC_EVENT_TYPE_VISIBILITY,
+ NSMC_EVENT_TYPE_CAPABILITY,
+
+ NSMC_EVENT_TYPE_REPLY,
+ NSMC_EVENT_TYPE_ERROR,
+
+ NSMC_EVENT_TYPE_MAX
+} nsmc_event_type_t;
typedef struct _nsmc_t nsmc_t;
-typedef struct _nsmc_driver_t nsmc_driver_t;
+typedef struct _nsmc_event_open_t nsmc_event_open_t;
+typedef struct _nsmc_event_error_t nsmc_event_error_t;
+typedef struct _nsmc_event_reply_t nsmc_event_reply_t;
+typedef struct _nsmc_event_t nsmc_event_t;
-typedef int (*nsmc_open_t)(const char *path, const char *name,
- const char *id, void *data);
-typedef int (*nsmc_save_t)(void *data);
-typedef int (*nsmc_show_t)(void *data);
-typedef int (*nsmc_hide_t)(void *data);
-
-struct _nsmc_driver_t {
- nsmc_open_t open;
- nsmc_save_t save;
- nsmc_show_t show;
- nsmc_hide_t hide;
- bool supports_switch;
+typedef int (*nsmc_callback_t)(void *data, const nsmc_event_t *ev);
+
+struct _nsmc_event_open_t {
+ const char *path;
+ const char *name;
+ const char *id;
+};
+
+struct _nsmc_event_error_t {
+ const char *request;
+ nsmc_err_t code;
+ const char *message;
+};
+
+struct _nsmc_event_reply_t {
+ const char *request;
+ const char *message;
+};
+
+struct _nsmc_event_t {
+ nsmc_event_type_t type;
+ union {
+ nsmc_event_open_t open;
+ nsmc_event_error_t error;
+ nsmc_event_reply_t reply;
+ };
};
NSMC_API nsmc_t *
nsmc_new(const char *call, const char *exe, const char *fallback_path,
- const nsmc_driver_t *driver, void *data);
+ nsmc_callback_t callback, void *data);
NSMC_API void
nsmc_free(nsmc_t *nsm);
NSMC_API void
-nsmc_run(nsmc_t *nsm);
+nsmc_pollin(nsmc_t *nsm, int timeout_ms);
NSMC_API void
+nsmc_run(nsmc_t *nsm);
+
+NSMC_API int
nsmc_opened(nsmc_t *nsm, int status);
-NSMC_API void
+NSMC_API int
nsmc_shown(nsmc_t *nsm);
-NSMC_API void
+NSMC_API int
nsmc_hidden(nsmc_t *nsm);
-NSMC_API void
+NSMC_API int
nsmc_saved(nsmc_t *nsm, int status);
NSMC_API bool
nsmc_managed();
+NSMC_API int
+nsmc_progress(nsmc_t *nsm, float progress);
+
+NSMC_API int
+nsmc_dirty(nsmc_t *nsm);
+
+NSMC_API int
+nsmc_clean(nsmc_t *nsm);
+
+NSMC_API int
+nsmc_message(nsmc_t *nsm, int priority, const char *message);
+
+NSMC_API int
+nsmc_server_add(nsmc_t *nsm, const char *exe);
+
+NSMC_API int
+nsmc_server_save(nsmc_t *nsm);
+
+NSMC_API int
+nsmc_server_load(nsmc_t *nsm, const char *porj_name);
+
+NSMC_API int
+nsmc_server_new(nsmc_t *nsm, const char *porj_name);
+
+NSMC_API int
+nsmc_server_duplicate(nsmc_t *nsm, const char *porj_name);
+
+NSMC_API int
+nsmc_server_close(nsmc_t *nsm);
+
+NSMC_API int
+nsmc_server_abort(nsmc_t *nsm);
+
+NSMC_API int
+nsmc_server_quit(nsmc_t *nsm);
+
+NSMC_API int
+nsmc_server_list(nsmc_t *nsm);
+
+NSMC_API int
+nsmc_server_broadcast_varlist(nsmc_t *nsm, const char *fmt, va_list args);
+
+NSMC_API int
+nsmc_server_broadcast_vararg(nsmc_t *nsm, const char *fmt, ...);
+
#ifdef NSMC_IMPLEMENTATION
#include <stdio.h>
@@ -83,14 +199,6 @@ nsmc_managed();
#include <varchunk.h>
-typedef void (*osc_cb_t)(LV2_OSC_Reader *reader, nsmc_t *nsm);
-typedef struct _osc_msg_t osc_msg_t;
-
-struct _osc_msg_t {
- const char *path;
- osc_cb_t cb;
-};
-
struct _nsmc_t {
bool connected;
bool connectionless;
@@ -99,211 +207,398 @@ struct _nsmc_t {
char *call;
char *exe;
- const nsmc_driver_t *driver;
+ nsmc_capability_t client_capability;
+ nsmc_callback_t callback;
void *data;
LV2_OSC_Stream stream;
varchunk_t *tx;
varchunk_t *rx;
+
+ nsmc_capability_t host_capability;
};
-static void
-_reply(LV2_OSC_Reader *reader, nsmc_t *nsm)
+static const char *nsmc_capability_labels [NSMC_CAPABILITY_MAX] = {
+ [NSMC_CAPABILITY_SWITCH] = "switch",
+ [NSMC_CAPABILITY_DIRTY] = "dirty",
+ [NSMC_CAPABILITY_PROGRESS] = "progress",
+ [NSMC_CAPABILITY_MESSAGE] = "message",
+ [NSMC_CAPABILITY_OPTIONAL_GUI] = "optional-gui",
+ [NSMC_CAPABILITY_SERVER_CONTROL] = "server-control",
+ [NSMC_CAPABILITY_BROADCAST] = "broadcast"
+};
+
+static int
+_nsmc_message_varlist(nsmc_t *nsm, const char *path, const char *fmt, va_list args)
{
- const char *target = NULL;
- lv2_osc_reader_get_string(reader, &target);
+ if(!nsm)
+ {
+ return 1;
+ }
- //fprintf(stdout, "nsmc reply: %s\n", target);
+ if(!nsmc_managed())
+ {
+ return 0;
+ }
- if(target && !strcmp(target, "/nsm/server/announce"))
+ size_t max = 0;
+ uint8_t *tx = varchunk_write_request_max(nsm->tx, 1024, &max);
+ if(!tx)
{
- const char *message = NULL;
- const char *manager = NULL;
- const char *capabilities = NULL;
+ return 1;
+ }
- lv2_osc_reader_get_string(reader, &message);
- lv2_osc_reader_get_string(reader, &manager);
- lv2_osc_reader_get_string(reader, &capabilities);
+ LV2_OSC_Writer writer;
+ lv2_osc_writer_initialize(&writer, tx, max);
- //TODO, e.g. toggle SM LED, check capabilities
+ if(lv2_osc_writer_message_varlist(&writer, path, fmt, args) == false)
+ {
+ return 1;
+ }
+
+ size_t written;
+ if(lv2_osc_writer_finalize(&writer, &written) == NULL)
+ {
+ return 1;
}
+
+ varchunk_write_advance(nsm->tx, written);
+
+ return 0;
}
-static void
-_error(LV2_OSC_Reader *reader, nsmc_t *nsm)
+static int
+_nsmc_message_vararg(nsmc_t *nsm, const char *path, const char *fmt, ...)
{
- const char *msg = NULL;
- int32_t code = 0;
- const char *err = NULL;
+ va_list args;
+ va_start(args, fmt);
- lv2_osc_reader_get_string(reader, &msg);
- lv2_osc_reader_get_int32(reader, &code);
- lv2_osc_reader_get_string(reader, &err);
+ const int ret = _nsmc_message_varlist(nsm, path, fmt, args);
- fprintf(stderr, "nsmc error: #%i in %s: %s\n", code, msg, err);
+ va_end(args);
+
+ return ret;
}
-static void
-_client_open(LV2_OSC_Reader *reader, nsmc_t *nsm)
+static const char *
+_arg_to_string(LV2_OSC_Reader *reader, LV2_OSC_Arg **arg)
{
- const char *dir = NULL;
- const char *name = NULL;
- const char *id = NULL;
+ if(lv2_osc_reader_arg_is_end(reader, *arg))
+ {
+ return NULL;
+ }
- lv2_osc_reader_get_string(reader, &dir);
- lv2_osc_reader_get_string(reader, &name);
- lv2_osc_reader_get_string(reader, &id);
+ if((*arg)->type[0] != LV2_OSC_STRING)
+ {
+ return NULL;
+ }
- char tmp [PATH_MAX];
- const char *resolvedpath = realpath(dir, tmp);
- if(!resolvedpath)
- resolvedpath = dir;
+ const char *s = (*arg)->s;
- if(nsm->driver->open && nsm->driver->open(resolvedpath, name, id, nsm->data))
- fprintf(stderr, "NSM load failed: '%s'\n", dir);
+ *arg = lv2_osc_reader_arg_next(reader, *arg);
+
+ return s;
+}
+
+static int32_t
+_arg_to_int32(LV2_OSC_Reader *reader, LV2_OSC_Arg **arg)
+{
+ if(lv2_osc_reader_arg_is_end(reader, *arg))
+ {
+ return 0;
+ }
+
+ if((*arg)->type[0] != LV2_OSC_INT32)
+ {
+ return 0;
+ }
+
+ const int32_t i = (*arg)->i;
+
+ *arg = lv2_osc_reader_arg_next(reader, *arg);
+
+ return i;
}
static void
-_client_save(LV2_OSC_Reader *reader, nsmc_t *nsm)
+_reply_server_announce(LV2_OSC_Reader *reader, LV2_OSC_Arg *arg,
+ const LV2_OSC_Tree *tree, nsmc_t *nsm)
{
- // save app
- if(nsm->driver->save && nsm->driver->save(nsm->data))
- fprintf(stderr, "NSM save failed:\n");
+ const char *message = _arg_to_string(reader, &arg);
+ const char *manager = _arg_to_string(reader, &arg);
+ const char *capabilities = _arg_to_string(reader, &arg);
+
+ (void)message; //FIXME
+ (void)manager; //FIXME
+
+ char *caps = alloca(strlen(capabilities) + 1);
+ strcpy(caps, capabilities);
+
+ char *tok;
+ while( (tok = strsep(&caps, ":")) )
+ {
+ for(nsmc_capability_t cap = NSMC_CAPABILITY_NONE;
+ cap < NSMC_CAPABILITY_MAX;
+ cap++)
+ {
+ if( nsmc_capability_labels[cap]
+ && !strcasecmp(nsmc_capability_labels[cap], tok) )
+ {
+ nsm->host_capability |= cap;
+ }
+ }
+ }
}
static void
-_client_show_optional_gui(LV2_OSC_Reader *reader, nsmc_t *nsm)
+_reply(LV2_OSC_Reader *reader, LV2_OSC_Arg *arg, const LV2_OSC_Tree *tree,
+ void *data)
{
- // show gui
- if(nsm->driver->show && nsm->driver->show(nsm->data))
+ nsmc_t *nsm = data;
+ const char *target = _arg_to_string(reader, &arg);
+
+ if(!target)
{
- fprintf(stderr, "NSM show GUI failed\n");
return;
}
- nsmc_shown(nsm);
+ if(!strcasecmp(target, "/nsm/server/announce"))
+ {
+ _reply_server_announce(reader, arg, tree, nsm);
+ }
+
+ const nsmc_event_t ev_reply = {
+ .type = NSMC_EVENT_TYPE_REPLY,
+ .reply = {
+ .request = target
+ }
+ };
+
+ nsm->callback(nsm->data, &ev_reply);
}
static void
-_client_hide_optional_gui(LV2_OSC_Reader *reader, nsmc_t *nsm)
+_error(LV2_OSC_Reader *reader, LV2_OSC_Arg *arg, const LV2_OSC_Tree *tree,
+ void *data)
{
- // hide gui
- if(nsm->driver->hide && nsm->driver->hide(nsm->data))
+ nsmc_t *nsm = data;
+
+ const char *target = _arg_to_string(reader, &arg);
+ if(!target)
+ {
+ return;
+ }
+
+ const nsmc_err_t code = _arg_to_int32(reader, &arg);
+ if(!code)
+ {
+ return;
+ }
+
+ const char *err = _arg_to_string(reader, &arg);
+ if(!err)
{
- fprintf(stderr, "NSM hide GUI failed\n");
return;
}
- nsmc_hidden(nsm);
+ const nsmc_event_t ev_error = {
+ .type = NSMC_EVENT_TYPE_ERROR,
+ .error = {
+ .request = target,
+ .code = code,
+ .message = err
+ }
+ };
+
+ nsm->callback(nsm->data, &ev_error);
}
static void
-_announce(nsmc_t *nsm)
+_client_open(LV2_OSC_Reader *reader, LV2_OSC_Arg *arg, const LV2_OSC_Tree *tree,
+ void *data)
{
- char capabilities [64] = ":message:";
+ nsmc_t *nsm = data;
+ const char *dir = _arg_to_string(reader, &arg);
+ const char *name = _arg_to_string(reader, &arg);
+ const char *id = _arg_to_string(reader, &arg);
- // send announce message
- pid_t pid = getpid();
+ char tmp [PATH_MAX];
+ const char *resolvedpath = realpath(dir, tmp);
+ if(!resolvedpath)
+ {
+ resolvedpath = dir;
+ }
- const bool has_gui = nsm->driver->show && nsm->driver->hide;
+ const nsmc_event_t ev_open = {
+ .type = NSMC_EVENT_TYPE_OPEN,
+ .open = {
+ .path = resolvedpath,
+ .name = name,
+ .id = id
+ }
+ };
- if(has_gui)
+ if(nsm->callback(nsm->data, &ev_open) != 0)
{
- strcat(capabilities, "optional-gui:");
+ fprintf(stderr, "NSM load failed: '%s'\n", dir);
}
- if(nsm->driver->supports_switch)
+ // return if client does not support optional gui
+ if(!(nsm->client_capability & NSMC_CAPABILITY_OPTIONAL_GUI))
{
- strcat(capabilities, "switch:");
+ return;
}
- uint8_t *tx;
- size_t max;
- if( (tx = varchunk_write_request_max(nsm->tx, 1024, &max)) )
- {
- LV2_OSC_Writer writer;
- lv2_osc_writer_initialize(&writer, tx, max);
+ const nsmc_event_t ev_show = {
+ .type = NSMC_EVENT_TYPE_SHOW
+ };
- LV2_OSC_Writer_Frame bndl = {0}, itm = {0};
- lv2_osc_writer_push_bundle(&writer, &bndl, LV2_OSC_IMMEDIATE);
- {
- lv2_osc_writer_push_item(&writer, &itm);
- lv2_osc_writer_message_vararg(&writer, "/nsm/server/announce", "sssiii",
- nsm->call, capabilities, nsm->exe, 1, 2, pid);
- lv2_osc_writer_pop_item(&writer, &itm);
- }
- if(has_gui)
+ // always show gui if server does not support optional gui
+ if(!(nsm->host_capability & NSMC_CAPABILITY_OPTIONAL_GUI))
+ {
+ if(nsm->callback(nsm->data, &ev_show) != 0)
{
- // report initial gui visibility //FIXME should be saved in state somewhere
- if(nsm->driver->show(nsm->data) == 0)
- {
- lv2_osc_writer_push_item(&writer, &itm);
- lv2_osc_writer_message_vararg(&writer, "/nsm/client/gui_is_shown", "");
- lv2_osc_writer_pop_item(&writer, &itm);
- }
- else
- {
- lv2_osc_writer_push_item(&writer, &itm);
- lv2_osc_writer_message_vararg(&writer, "/nsm/client/gui_is_hidden", "");
- lv2_osc_writer_pop_item(&writer, &itm);
- }
+ //FIXME report error
}
- lv2_osc_writer_pop_bundle(&writer, &bndl);
- size_t written;
- if(lv2_osc_writer_finalize(&writer, &written))
- {
- varchunk_write_advance(nsm->tx, written);
- }
- else
- {
- fprintf(stderr, "OSC sending failed\n");
- }
+ return;
+ }
+
+ const nsmc_event_t ev_visibility = {
+ .type = NSMC_EVENT_TYPE_VISIBILITY
+ };
+
+ // put gui visibility into last known state
+ const int visibility = nsm->callback(nsm->data, &ev_visibility);
+
+ if(visibility && (nsm->callback(nsm->data, &ev_show) == 0) )
+ {
+ _nsmc_message_vararg(nsm, "/nsm/client/gui_is_shown", "");
+ }
+ else
+ {
+ _nsmc_message_vararg(nsm, "/nsm/client/gui_is_hidden", "");
+ }
+}
+
+static void
+_client_save(LV2_OSC_Reader *reader, LV2_OSC_Arg *arg, const LV2_OSC_Tree *tree,
+ void *data)
+{
+ nsmc_t *nsm = data;
+ const nsmc_event_t ev_save = {
+ .type = NSMC_EVENT_TYPE_SAVE
+ };
+
+ // save app
+ if(nsm->callback(nsm->data, &ev_save) != 0)
+ {
+ fprintf(stderr, "NSM save failed:\n");
}
}
-static const osc_msg_t messages [] = {
- {"/reply", _reply},
- {"/error", _error},
+static void
+_client_session_is_loaded(LV2_OSC_Reader *reader, LV2_OSC_Arg *arg, const LV2_OSC_Tree *tree,
+ void *data)
+{
+ nsmc_t *nsm = data;
+ const nsmc_event_t ev_session_is_loaded = {
+ .type = NSMC_EVENT_TYPE_SESSION_IS_LOADED
+ };
- {"/nsm/client/open", _client_open},
- {"/nsm/client/save", _client_save},
- {"/nsm/client/show_optional_gui", _client_show_optional_gui},
- {"/nsm/client/hide_optional_gui", _client_hide_optional_gui},
+ // save app
+ if(nsm->callback(nsm->data, &ev_session_is_loaded) != 0)
+ {
+ fprintf(stderr, "NSM session_is_loaded failed:\n");
+ }
+}
- {NULL, NULL}
-};
+static void
+_client_show_optional_gui(LV2_OSC_Reader *reader, LV2_OSC_Arg *arg,
+ const LV2_OSC_Tree *tree, void *data)
+{
+ nsmc_t *nsm = data;
+ const nsmc_event_t ev_show = {
+ .type = NSMC_EVENT_TYPE_SHOW
+ };
+
+ // show gui
+ if( (nsm->client_capability & NSMC_CAPABILITY_OPTIONAL_GUI)
+ && (nsm->callback(nsm->data, &ev_show) == 0) )
+ {
+ nsmc_shown(nsm);
+ }
+}
static void
-_unpack_messages(LV2_OSC_Reader *reader, size_t len, nsmc_t *nsm)
+_client_hide_optional_gui(LV2_OSC_Reader *reader, LV2_OSC_Arg *arg,
+ const LV2_OSC_Tree *tree, void *data)
{
- if(lv2_osc_reader_is_message(reader))
+ nsmc_t *nsm = data;
+ const nsmc_event_t ev_hide = {
+ .type = NSMC_EVENT_TYPE_HIDE
+ };
+
+ // hide gui
+ if( (nsm->client_capability & NSMC_CAPABILITY_OPTIONAL_GUI)
+ && (nsm->callback(nsm->data, &ev_hide) == 0) )
{
- const char *path = NULL;
- const char *fmt = NULL;
- lv2_osc_reader_get_string(reader, &path);
- lv2_osc_reader_get_string(reader, &fmt);
- for(const osc_msg_t *msg = messages; msg->cb; msg++)
- {
- if(!strcmp(msg->path, path))
- {
- msg->cb(reader, nsm);
- break;
- }
- }
+ nsmc_hidden(nsm);
}
- else if(lv2_osc_reader_is_bundle(reader))
+}
+
+static int
+_announce(nsmc_t *nsm)
+{
+ char capabilities [128] = "";
+
+ for(nsmc_capability_t cap = NSMC_CAPABILITY_NONE;
+ cap < NSMC_CAPABILITY_MAX;
+ cap++)
{
- OSC_READER_BUNDLE_FOREACH(reader, itm, len)
+ if( (nsm->client_capability & cap)
+ && nsmc_capability_labels[cap] )
{
- LV2_OSC_Reader reader2;
- lv2_osc_reader_initialize(&reader2, itm->body, itm->size);
- _unpack_messages(&reader2, itm->size, nsm);
+ strcat(capabilities, ":");
+ strcat(capabilities, nsmc_capability_labels[cap]);
}
}
+
+ if(strlen(capabilities) > 0)
+ {
+ strcat(capabilities, ":");
+ }
+
+ // send announce message
+ pid_t pid = getpid();
+
+ return _nsmc_message_vararg(nsm, "/nsm/server/announce", "sssiii",
+ nsm->call, capabilities, nsm->exe, 1, 2, pid);
}
+static const LV2_OSC_Tree tree_client [] = {
+ { .name = "open", .trees = NULL, .branch = _client_open },
+ { .name = "save", .trees = NULL, .branch = _client_save },
+ { .name = "session_is_loaded", .trees = NULL, .branch = _client_session_is_loaded},
+ { .name = "show_optional_gui", .trees = NULL, .branch = _client_show_optional_gui },
+ { .name = "hide_optional_gui", .trees = NULL, .branch = _client_hide_optional_gui },
+ { .name = NULL, .trees = NULL, .branch = NULL } // sentinel
+};
+
+static const LV2_OSC_Tree tree_nsm [] = {
+ { .name = "client", .trees = tree_client, .branch = NULL },
+ { .name = NULL, .trees = NULL, .branch = NULL } // sentinel
+};
+
+static const LV2_OSC_Tree tree_root [] = {
+ { .name = "reply", .trees = NULL, .branch = _reply },
+ { .name = "error", .trees = NULL, .branch = _error },
+ { .name = "nsm", .trees = tree_nsm, .branch = NULL },
+
+ { .name = NULL, .trees = NULL, .branch = NULL } // sentinel
+};
+
static void *
_recv_req(void *data, size_t size, size_t *max)
{
@@ -345,57 +640,96 @@ static const LV2_OSC_Driver driver = {
NSMC_API nsmc_t *
nsmc_new(const char *call, const char *exe, const char *fallback_path,
- const nsmc_driver_t *nsm_driver, void *data)
+ nsmc_callback_t callback, void *data)
{
- if(!nsm_driver)
+ if(!callback)
+ {
return NULL;
+ }
nsmc_t *nsm = calloc(1, sizeof(nsmc_t));
if(!nsm)
+ {
return NULL;
+ }
- nsm->driver = nsm_driver;
+ nsm->callback = callback;
nsm->data = data;
+ const nsmc_event_t ev_capability = {
+ .type = NSMC_EVENT_TYPE_CAPABILITY,
+ };
+
+ nsm->client_capability = nsm->callback(nsm->data, &ev_capability);
+
nsm->call = call ? strdup(call) : NULL;
nsm->exe = exe ? strdup(exe) : NULL;
- nsm->url = getenv("NSM_URL");
- if(nsm->url)
+ char *nsm_url = getenv("NSM_URL");
+ if(nsm_url)
{
- nsm->connectionless = !strncmp(nsm->url, "osc.udp", 7) ? true : false;
+ nsm->connectionless = !strncmp(nsm_url, "osc.udp", 7) ? true : false;
- nsm->url = strdup(nsm->url); //FIXME
+ nsm->url = strdup(nsm_url);
if(!nsm->url)
- return NULL;
+ {
+ goto fail;
+ }
// remove trailing slash
if(!isdigit(nsm->url[strlen(nsm->url)-1]))
+ {
nsm->url[strlen(nsm->url)-1] = '\0';
+ }
nsm->tx = varchunk_new(8192, false);
if(!nsm->tx)
- return NULL;
+ {
+ goto fail;
+ }
nsm->rx = varchunk_new(8192, false);
if(!nsm->rx)
- return NULL;
+ {
+ goto fail;
+ }
if(lv2_osc_stream_init(&nsm->stream, nsm->url, &driver, nsm) != 0)
- return NULL;
+ {
+ goto fail;
+ }
}
else if(fallback_path)
{
char tmp [PATH_MAX];
const char *resolvedfallback_path = realpath(fallback_path, tmp);
if(!resolvedfallback_path)
+ {
resolvedfallback_path = fallback_path;
+ }
+
+ const nsmc_event_t ev_open = {
+ .type = NSMC_EVENT_TYPE_OPEN,
+ .open = {
+ .path = resolvedfallback_path,
+ .name = "unmanaged",
+ .id = nsm->call
+ }
+ };
- if(nsm->driver->open && nsm->driver->open(resolvedfallback_path, "unmanaged", nsm->call, nsm->data))
+ if(nsm->callback(nsm->data, &ev_open) != 0)
+ {
fprintf(stderr, "NSM load failed: '%s'\n", fallback_path);
+ goto fail;
+ }
}
return nsm;
+
+fail:
+ nsmc_free(nsm);
+
+ return NULL;
}
NSMC_API void
@@ -404,17 +738,26 @@ nsmc_free(nsmc_t *nsm)
if(nsm)
{
if(nsm->call)
+ {
free(nsm->call);
+ }
+
if(nsm->exe)
+ {
free(nsm->exe);
+ }
if(nsm->url)
{
if(nsm->rx)
+ {
varchunk_free(nsm->rx);
+ }
if(nsm->tx)
+ {
varchunk_free(nsm->tx);
+ }
lv2_osc_stream_deinit(&nsm->stream);
@@ -426,16 +769,17 @@ nsmc_free(nsmc_t *nsm)
}
NSMC_API void
-nsmc_run(nsmc_t *nsm)
+nsmc_pollin(nsmc_t *nsm, int timeout_ms)
{
- if(!nsm || !nsm->tx)
+ if(!nsm || !nsm->rx)
+ {
return;
+ }
- const LV2_OSC_Enum ev = lv2_osc_stream_run(&nsm->stream);
+ const LV2_OSC_Enum ev = lv2_osc_stream_pollin(&nsm->stream, timeout_ms);
if(ev & LV2_OSC_ERR)
{
- fprintf(stderr, "%s: %s\n", __func__, strerror(ev & LV2_OSC_ERR));
nsm->connected = false;
}
@@ -457,7 +801,7 @@ nsmc_run(nsmc_t *nsm)
LV2_OSC_Reader reader;
lv2_osc_reader_initialize(&reader, rx, size);
- _unpack_messages(&reader, size, nsm);
+ lv2_osc_reader_match(&reader, size, tree_root, nsm);
varchunk_read_advance(nsm->rx);
}
@@ -465,140 +809,248 @@ nsmc_run(nsmc_t *nsm)
}
NSMC_API void
-nsmc_opened(nsmc_t *nsm, int status)
+nsmc_run(nsmc_t *nsm)
{
- if(!nsm || !nsm->tx)
- return;
+ return nsmc_pollin(nsm, 0);
+}
- uint8_t *tx;
- size_t max;
- if( (tx = varchunk_write_request_max(nsm->tx, 1024, &max)) )
+NSMC_API int
+nsmc_opened(nsmc_t *nsm, int status)
+{
+ if(!nsm)
{
- LV2_OSC_Writer writer;
- lv2_osc_writer_initialize(&writer, tx, max);
-
- if(status == 0)
- {
- lv2_osc_writer_message_vararg(&writer, "/reply", "ss",
- "/nsm/client/open", "opened");
- }
- else
- {
- lv2_osc_writer_message_vararg(&writer, "/error", "sis",
- "/nsm/client/open", 2, "opening failed");
- }
+ return 1;
+ }
- size_t written;
- if(lv2_osc_writer_finalize(&writer, &written))
- {
- varchunk_write_advance(nsm->tx, written);
- }
- else
- {
- fprintf(stderr, "OSC sending failed\n");
- }
+ if(status == 0)
+ {
+ return _nsmc_message_vararg(nsm, "/reply", "ss",
+ "/nsm/client/open", "opened");
}
+
+ return _nsmc_message_vararg(nsm, "/error", "sis",
+ "/nsm/client/open", NSMC_ERR_GENERAL, "opening failed");
}
-NSMC_API void
+NSMC_API int
nsmc_shown(nsmc_t *nsm)
{
- if(!nsm || !nsm->tx)
- return;
-
- uint8_t *tx;
- size_t max;
- if( (tx = varchunk_write_request_max(nsm->tx, 1024, &max)) )
+ if(!nsm)
{
- // reply
- LV2_OSC_Writer writer;
- lv2_osc_writer_initialize(&writer, tx, max);
- lv2_osc_writer_message_vararg(&writer, "/nsm/client/gui_is_shown", "");
-
- size_t written;
- if(lv2_osc_writer_finalize(&writer, &written))
- {
- varchunk_write_advance(nsm->tx, written);
- }
- else
- {
- fprintf(stderr, "OSC sending failed\n");
- }
+ return 1;
}
+
+ return _nsmc_message_vararg(nsm, "/nsm/client/gui_is_shown", "");
}
-NSMC_API void
+NSMC_API int
nsmc_hidden(nsmc_t *nsm)
{
- if(!nsm || !nsm->tx)
- return;
-
- uint8_t *tx;
- size_t max;
- if( (tx = varchunk_write_request_max(nsm->tx, 1024, &max)) )
+ if(!nsm)
{
- // reply
- LV2_OSC_Writer writer;
- lv2_osc_writer_initialize(&writer, tx, max);
- lv2_osc_writer_message_vararg(&writer, "/nsm/client/gui_is_hidden", "");
-
- size_t written;
- if(lv2_osc_writer_finalize(&writer, &written))
- {
- varchunk_write_advance(nsm->tx, written);
- }
- else
- {
- fprintf(stderr, "OSC sending failed\n");
- }
+ return 1;
}
+
+ return _nsmc_message_vararg(nsm, "/nsm/client/gui_is_hidden", "");
}
-NSMC_API void
+NSMC_API int
nsmc_saved(nsmc_t *nsm, int status)
{
- if(!nsm || !nsm->tx)
- return;
-
- uint8_t *tx;
- size_t max;
- if( (tx = varchunk_write_request_max(nsm->tx, 1024, &max)) )
+ if(!nsm)
{
- LV2_OSC_Writer writer;
- lv2_osc_writer_initialize(&writer, tx, max);
-
- if(status == 0)
- {
- lv2_osc_writer_message_vararg(&writer, "/reply", "ss",
- "/nsm/client/save", "saved");
- }
- else
- {
- lv2_osc_writer_message_vararg(&writer, "/error", "sis",
- "/nsm/client/save", 1, "save failed");
- }
+ return 1;
+ }
- size_t written;
- if(lv2_osc_writer_finalize(&writer, &written))
- {
- varchunk_write_advance(nsm->tx, written);
- }
- else
- {
- fprintf(stderr, "OSC sending failed\n");
- }
+ if(status == 0)
+ {
+ return _nsmc_message_vararg(nsm, "/reply", "ss",
+ "/nsm/client/save", "saved");
}
+
+ return _nsmc_message_vararg(nsm, "/error", "sis",
+ "/nsm/client/save", NSMC_ERR_GENERAL, "save failed");
}
NSMC_API bool
nsmc_managed()
{
if(getenv("NSM_URL"))
+ {
return true;
+ }
return false;
}
+NSMC_API int
+nsmc_progress(nsmc_t *nsm, float progress)
+{
+ if(!nsm || !(nsm->client_capability & NSMC_CAPABILITY_PROGRESS))
+ {
+ return 1;
+ }
+
+ return _nsmc_message_vararg(nsm, "/nsm/client/progress", "f", progress);
+}
+
+NSMC_API int
+nsmc_dirty(nsmc_t *nsm)
+{
+ if(!nsm || !(nsm->client_capability & NSMC_CAPABILITY_DIRTY))
+ {
+ return 1;
+ }
+
+ return _nsmc_message_vararg(nsm, "/nsm/client/is_dirty", "");
+}
+
+NSMC_API int
+nsmc_clean(nsmc_t *nsm)
+{
+ if(!nsm || !(nsm->client_capability & NSMC_CAPABILITY_DIRTY))
+ {
+ return 1;
+ }
+
+ return _nsmc_message_vararg(nsm, "/nsm/client/is_clean", "");
+}
+
+NSMC_API int
+nsmc_message(nsmc_t *nsm, int priority, const char *message)
+{
+ if(!nsm || !(nsm->client_capability & NSMC_CAPABILITY_MESSAGE))
+ {
+ return 1;
+ }
+
+ return _nsmc_message_vararg(nsm, "/nsm/client/message", "is",
+ priority, message);
+}
+
+NSMC_API int
+nsmc_server_add(nsmc_t *nsm, const char *exe)
+{
+ if(!nsm || !(nsm->host_capability & NSMC_CAPABILITY_SERVER_CONTROL))
+ {
+ return 1;
+ }
+
+ return _nsmc_message_vararg(nsm, "/nsm/server/add", "s", exe);
+}
+
+NSMC_API int
+nsmc_server_save(nsmc_t *nsm)
+{
+ if(!nsm || !(nsm->host_capability & NSMC_CAPABILITY_SERVER_CONTROL))
+ {
+ return 1;
+ }
+
+ return _nsmc_message_vararg(nsm, "/nsm/server/save", "");
+}
+
+NSMC_API int
+nsmc_server_load(nsmc_t *nsm, const char *proj_name)
+{
+ if(!nsm || !(nsm->host_capability & NSMC_CAPABILITY_SERVER_CONTROL))
+ {
+ return 1;
+ }
+
+ return _nsmc_message_vararg(nsm, "/nsm/server/load", "s", proj_name);
+}
+
+NSMC_API int
+nsmc_server_new(nsmc_t *nsm, const char *proj_name)
+{
+ if(!nsm || !(nsm->host_capability & NSMC_CAPABILITY_SERVER_CONTROL))
+ {
+ return 1;
+ }
+
+ return _nsmc_message_vararg(nsm, "/nsm/server/new", "s", proj_name);
+}
+
+NSMC_API int
+nsmc_server_duplicate(nsmc_t *nsm, const char *proj_name)
+{
+ if(!nsm || !(nsm->host_capability & NSMC_CAPABILITY_SERVER_CONTROL))
+ {
+ return 1;
+ }
+
+ return _nsmc_message_vararg(nsm, "/nsm/server/duplicate", "s", proj_name);
+}
+
+NSMC_API int
+nsmc_server_close(nsmc_t *nsm)
+{
+ if(!nsm || !(nsm->host_capability & NSMC_CAPABILITY_SERVER_CONTROL))
+ {
+ return 1;
+ }
+
+ return _nsmc_message_vararg(nsm, "/nsm/server/close", "");
+}
+
+NSMC_API int
+nsmc_server_abort(nsmc_t *nsm)
+{
+ if(!nsm || !(nsm->host_capability & NSMC_CAPABILITY_SERVER_CONTROL))
+ {
+ return 1;
+ }
+
+ return _nsmc_message_vararg(nsm, "/nsm/server/abort", "");
+}
+
+NSMC_API int
+nsmc_server_quit(nsmc_t *nsm)
+{
+ if(!nsm || !(nsm->host_capability & NSMC_CAPABILITY_SERVER_CONTROL))
+ {
+ return 1;
+ }
+
+ return _nsmc_message_vararg(nsm, "/nsm/server/quit", "");
+}
+
+NSMC_API int
+nsmc_server_list(nsmc_t *nsm)
+{
+ if(!nsm || !(nsm->host_capability & NSMC_CAPABILITY_SERVER_CONTROL))
+ {
+ return 1;
+ }
+
+ return _nsmc_message_vararg(nsm, "/nsm/server/list", "");
+}
+
+NSMC_API int
+nsmc_server_broadcast_varlist(nsmc_t *nsm, const char *fmt, va_list args)
+{
+ if(!nsm || !(nsm->host_capability & NSMC_CAPABILITY_BROADCAST))
+ {
+ return 1;
+ }
+
+ return _nsmc_message_varlist(nsm, "/nsm/server/broadcast", fmt, args);
+}
+
+NSMC_API int
+nsmc_server_broadcast_vararg(nsmc_t *nsm, const char *fmt, ...)
+{
+ va_list args;
+ va_start(args, fmt);
+
+ const int ret = nsmc_server_broadcast_varlist(nsm, fmt, args);
+
+ va_end(args);
+
+ return ret;
+}
+
#endif /* NSMC_IMPLEMENTATION */
#ifdef __cplusplus
diff --git a/osc.lv2/.gitlab-ci.yml b/osc.lv2/.gitlab-ci.yml
index ebc4676b..f0e54dd0 100644
--- a/osc.lv2/.gitlab-ci.yml
+++ b/osc.lv2/.gitlab-ci.yml
@@ -52,6 +52,9 @@ i686-linux-gnu:
arm-linux-gnueabihf:
<<: *arm_linux_definition
+aarch64-linux-gnu:
+ <<: *arm_linux_definition
+
x86_64-w64-mingw32:
<<: *universal_w64_definition
diff --git a/osc.lv2/VERSION b/osc.lv2/VERSION
index 23175873..e670da30 100644
--- a/osc.lv2/VERSION
+++ b/osc.lv2/VERSION
@@ -1 +1 @@
-0.1.105
+0.1.137
diff --git a/osc.lv2/osc.lv2/reader.h b/osc.lv2/osc.lv2/reader.h
index 8e0ae459..7a8456b5 100644
--- a/osc.lv2/osc.lv2/reader.h
+++ b/osc.lv2/osc.lv2/reader.h
@@ -24,14 +24,25 @@
#include <osc.lv2/osc.h>
#include <osc.lv2/endian.h>
+#include <osc.lv2/util.h>
#ifdef __cplusplus
extern "C" {
#endif
+
+typedef struct _LV2_OSC_Tree LV2_OSC_Tree;
typedef struct _LV2_OSC_Reader LV2_OSC_Reader;
typedef struct _LV2_OSC_Item LV2_OSC_Item;
typedef struct _LV2_OSC_Arg LV2_OSC_Arg;
+typedef void (*LV2_OSC_Branch)(LV2_OSC_Reader *reader, LV2_OSC_Arg *arg,
+ const LV2_OSC_Tree *tree, void *data);
+
+struct _LV2_OSC_Tree {
+ const char *name;
+ const LV2_OSC_Tree *trees;
+ LV2_OSC_Branch branch;
+};
struct _LV2_OSC_Reader {
const uint8_t *buf;
@@ -564,6 +575,50 @@ lv2_osc_reader_is_message(LV2_OSC_Reader *reader)
return reader->ptr[0] == '/'; //FIXME check path
}
+static inline void
+_lv2_osc_trees_internal(LV2_OSC_Reader *reader, const char *path, const char *from,
+ LV2_OSC_Arg *arg, const LV2_OSC_Tree *trees, void *data)
+{
+ const char *ptr = strchr(from, '/');
+
+ const size_t len = ptr
+ ? (size_t)(ptr - from)
+ : strlen(from);
+
+ for(const LV2_OSC_Tree *tree = trees; tree && tree->name; tree++)
+ {
+ if(lv2_osc_pattern_match(from, tree->name, len))
+ {
+ if(tree->trees && ptr)
+ {
+ if(tree->branch)
+ {
+ LV2_OSC_Reader reader_clone = *reader;
+ tree->branch(&reader_clone, arg, tree, data);
+ }
+
+ _lv2_osc_trees_internal(reader, path, &ptr[1], arg, tree->trees, data);
+ }
+ else if(tree->branch && !ptr)
+ {
+ LV2_OSC_Reader reader_clone = *reader;
+ tree->branch(&reader_clone, arg, tree, data);
+ }
+ }
+ }
+}
+
+static inline void
+lv2_osc_reader_match(LV2_OSC_Reader *reader, size_t len,
+ const LV2_OSC_Tree *trees, void *data)
+{
+ LV2_OSC_Arg *arg = OSC_READER_MESSAGE_BEGIN(reader, len);
+ const char *path = arg->path;
+ const char *from = &path[1];
+
+ _lv2_osc_trees_internal(reader, path, from, arg, trees, data);
+}
+
#ifdef __cplusplus
} // extern "C"
#endif
diff --git a/osc.lv2/osc.lv2/stream.h b/osc.lv2/osc.lv2/stream.h
index a86c2309..c1339c0d 100644
--- a/osc.lv2/osc.lv2/stream.h
+++ b/osc.lv2/osc.lv2/stream.h
@@ -19,6 +19,7 @@
#define LV2_OSC_STREAM_H
#include <stdbool.h>
+#include <stdlib.h>
#include <string.h>
#if !defined(_WIN32)
# include <arpa/inet.h>
@@ -34,6 +35,7 @@
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
+#include <poll.h>
#include <osc.lv2/osc.h>
@@ -665,7 +667,7 @@ lv2_osc_stream_init(LV2_OSC_Stream *stream, const char *url,
{
memset(stream, 0x0, sizeof(LV2_OSC_Stream));
- strncpy(stream->url, url, sizeof(stream->url));
+ strncpy(stream->url, url, sizeof(stream->url) - 1);
stream->driv = driv;
stream->data = data;
stream->sock = -1;
@@ -1372,6 +1374,37 @@ lv2_osc_stream_run(LV2_OSC_Stream *stream)
return ev;
}
+static LV2_OSC_Enum
+lv2_osc_stream_pollin(LV2_OSC_Stream *stream, int timeout_ms)
+{
+ struct pollfd fds [2] = {
+ [0] = {
+ .fd = stream->sock,
+ .events = POLLIN,
+ .revents = 0
+ },
+ [1] = {
+ .fd = stream->fd,
+ .events = POLLIN,
+ .revents = 0
+ }
+ };
+
+ const int res = poll(fds, 2, timeout_ms);
+ if(res < 0)
+ {
+ return LV2_OSC_STREAM_ERRNO(LV2_OSC_NONE, errno);
+ }
+
+#if 0
+ fprintf(stderr, "++ %i: %i %i %i %i\n", res,
+ fds[0].fd, (int)fds[0].revents,
+ fds[1].fd, (int)fds[1].revents);
+#endif
+
+ return lv2_osc_stream_run(stream);
+}
+
#ifdef __cplusplus
} // extern "C"
#endif
diff --git a/osc.lv2/osc.lv2/util.h b/osc.lv2/osc.lv2/util.h
index 195bb867..35176181 100644
--- a/osc.lv2/osc.lv2/util.h
+++ b/osc.lv2/osc.lv2/util.h
@@ -23,6 +23,9 @@
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
+#if !defined(_WIN32)
+# include <fnmatch.h>
+#endif
#include <osc.lv2/osc.h>
@@ -45,6 +48,15 @@ extern "C" {
typedef void (*LV2_OSC_Method)(const char *path,
const LV2_Atom_Tuple *arguments, void *data);
+typedef struct _LV2_OSC_Hook LV2_OSC_Hook;
+
+struct _LV2_OSC_Hook {
+ const char *name;
+ const LV2_OSC_Hook *hooks;
+ LV2_OSC_Method method;
+ void *data;
+};
+
// characters not allowed in OSC path string
static const char invalid_path_chars [] = {
' ', '#',
@@ -60,6 +72,120 @@ static const char valid_format_chars [] = {
'\0'
};
+static bool
+lv2_osc_pattern_match(const char *from, const char *name, size_t len)
+{
+#if !defined(_WIN32)
+ size_t nbrace = 0;
+
+# if defined(FNM_EXTMATCH)
+ // count opening curly braces
+ for(size_t i = 0; i < len; i++)
+ {
+ if(from[i] == '{')
+ {
+ nbrace++;
+ }
+ }
+# endif
+
+ // allocate temporary pattern buffer
+ char *pattern = alloca(len + nbrace + 1);
+
+ if(!pattern)
+ {
+ return false;
+ }
+
+# if defined(FNM_EXTMATCH)
+ // convert {x,y} to @(x|y) for extended fnmatch
+ if(nbrace)
+ {
+ char *ptr = pattern;
+
+ for(size_t i = 0; i < len; i++)
+ {
+ switch(from[i])
+ {
+ case '{':
+ {
+ *ptr++ = '@';
+ *ptr++ = '(';
+ } break;
+ case ',':
+ {
+ *ptr++ = '|';
+ } break;
+ case '}':
+ {
+ *ptr++ = ')';
+ } break;
+ default:
+ {
+ *ptr++ = from[i];
+ } break;
+ }
+ }
+ }
+ else
+# endif
+ {
+ memcpy(pattern, from, len);
+ }
+
+ // terminate pattern string with null terminator
+ pattern[len + nbrace] = '\0';
+
+# if defined(FNM_EXTMATCH)
+ return fnmatch(pattern, name, FNM_NOESCAPE | FNM_EXTMATCH) == 0 ? true : false;
+# else
+ return fnmatch(pattern, name, FNM_NOESCAPE) == 0 ? true : false;
+# endif
+#else
+ return strncmp(from, name, len) == 0 ? true : false;
+#endif
+}
+
+static void
+_lv2_osc_hooks_internal(const char *path, const char *from,
+ const LV2_Atom_Tuple *arguments, const LV2_OSC_Hook *hooks)
+{
+ const char *ptr = strchr(from, '/');
+
+ const size_t len = ptr
+ ? (size_t)(ptr - from)
+ : strlen(from);
+
+ for(const LV2_OSC_Hook *hook = hooks; hook && hook->name; hook++)
+ {
+ if(lv2_osc_pattern_match(from, hook->name, len))
+ {
+ if(hook->hooks && ptr)
+ {
+ from = &ptr[1];
+
+ _lv2_osc_hooks_internal(path, from, arguments, hook->hooks);
+ }
+ else if(hook->method && !ptr)
+ {
+ hook->method(path, arguments, hook->data);
+ }
+ }
+ }
+}
+
+/**
+ TODO
+*/
+static void
+lv2_osc_hooks(const char *path, const LV2_Atom_Tuple *arguments, void *data)
+{
+ const LV2_OSC_Hook *hooks = data;
+ const char *from = &path[1];
+
+ _lv2_osc_hooks_internal(path, from, arguments, hooks);
+}
+
/**
TODO
*/
diff --git a/osc.lv2/test/osc_test.c b/osc.lv2/test/osc_test.c
index a5352777..2c6a7101 100644
--- a/osc.lv2/test/osc_test.c
+++ b/osc.lv2/test/osc_test.c
@@ -931,16 +931,421 @@ static const pair_t pairs [] = {
};
#endif
+#if !defined(_WIN32)
+static unsigned foo_sub_one = 0;
+static unsigned foo_sub_two [2] = { 0, 0 };
+static unsigned foo = 0;
+static unsigned bar = 0;
+
+static void
+_one(const char *path, unsigned *flag)
+{
+ *flag += 1;
+
+ if(!path)
+ {
+ return;
+ }
+
+ assert(!strcmp(path, "/sub/one")
+ || !strcmp(path, "/*/one")
+ || !strcmp(path, "/s*/one")
+ || !strcmp(path, "/su*/one")
+ || !strcmp(path, "/sub*/one")
+ || !strcmp(path, "/sub/*")
+ || !strcmp(path, "/*sub/one")
+ || !strcmp(path, "/*s*u*b*/one")
+ || !strcmp(path, "/su[ab]/one")
+ || !strcmp(path, "/su[a-b]/[!a-np-z]ne")
+ || !strcmp(path, "/su[a-b]/one")
+ || !strcmp(path, "/s?b/?ne")
+ || !strcmp(path, "/s?*/?ne")
+ || !strcmp(path, "/s?*/*?e")
+ || !strcmp(path, "/sub/{one,two}"));
+}
+
+static void
+_two(const char *path, unsigned *flag)
+{
+ *flag += 1;
+
+ if(!path)
+ {
+ return;
+ }
+
+ assert(!strcmp(path, "/sub/two")
+ || !strcmp(path, "/sub/*")
+ || !strcmp(path, "/sub/{one,two}"));
+}
+
+static void
+_foo(const char *path, unsigned *flag)
+{
+ *flag += 1;
+
+ if(!path)
+ {
+ return;
+ }
+
+ assert(!strcmp(path, "/foo")
+ || !strcmp(path, "/{foo,bar}"));
+}
+
+static void
+_bar(const char *path, unsigned *flag)
+{
+ *flag += 1;
+
+ if(!path)
+ {
+ return;
+ }
+
+ assert(!strcmp(path, "/bar")
+ || !strcmp(path, "/{foo,bar}"));
+}
+
+static void
+_hook_one(const char *path, const LV2_Atom_Tuple *arguments __attribute__((unused)),
+ void *data)
+{
+ _one(path, data);
+}
+
+static void
+_hook_two(const char *path, const LV2_Atom_Tuple *arguments __attribute__((unused)),
+ void *data)
+{
+ _two(path, data);
+}
+
+static void
+_hook_foo(const char *path, const LV2_Atom_Tuple *arguments __attribute__((unused)),
+ void *data)
+{
+ _foo(path, data);
+}
+
+static void
+_hook_bar(const char *path, const LV2_Atom_Tuple *arguments __attribute__((unused)),
+ void *data)
+{
+ _bar(path, data);
+}
+
+static LV2_OSC_Hook hook_sub [] = {
+ { .name = "one", .method = _hook_one, .data = &foo_sub_one },
+ { .name = "two", .method = _hook_two, .data = &foo_sub_two[0] },
+ { .name = "two", .method = _hook_two, .data = &foo_sub_two[1] },
+ { .name = NULL }
+};
+
+static LV2_OSC_Hook hook_root [] = {
+ { .name = "foo", .method = _hook_foo, .data = &foo },
+ { .name = "bar", .method = _hook_bar, .data = &bar },
+ { .name = "sub", .hooks = hook_sub },
+ { .name = NULL }
+};
+
+static LV2_OSC_Tree tree_sub [4];
+
+static void
+_branch_one(LV2_OSC_Reader *reader __attribute__((unused)),
+ LV2_OSC_Arg *arg __attribute__((unused)),
+ const LV2_OSC_Tree *tree __attribute__((unused)),
+ void *data __attribute__((unused)))
+{
+ _one(NULL, &foo_sub_one);
+}
+
+static void
+_branch_two(LV2_OSC_Reader *reader __attribute__((unused)),
+ LV2_OSC_Arg *arg __attribute__((unused)),
+ const LV2_OSC_Tree *tree __attribute__((unused)),
+ void *data __attribute__((unused)))
+{
+ const size_t idx = tree - &tree_sub[1];
+
+ _two(NULL, &foo_sub_two[idx]);
+}
+
+static void
+_branch_foo(LV2_OSC_Reader *reader __attribute__((unused)),
+ LV2_OSC_Arg *arg __attribute__((unused)),
+ const LV2_OSC_Tree *tree __attribute__((unused)),
+ void *data __attribute__((unused)))
+{
+ _foo(NULL, &foo);
+}
+
+static void
+_branch_bar(LV2_OSC_Reader *reader __attribute__((unused)),
+ LV2_OSC_Arg *arg __attribute__((unused)),
+ const LV2_OSC_Tree *tree __attribute__((unused)),
+ void *data __attribute__((unused)))
+{
+ _bar(NULL, &bar);
+}
+
+static LV2_OSC_Tree tree_sub [] = {
+ { .name = "one", .branch = _branch_one },
+ { .name = "two", .branch = _branch_two },
+ { .name = "two", .branch = _branch_two },
+ { .name = NULL }
+};
+
+static LV2_OSC_Tree tree_root [] = {
+ { .name = "foo", .branch = _branch_foo },
+ { .name = "bar", .branch = _branch_bar },
+ { .name = "sub", .trees = tree_sub },
+ { .name = NULL }
+};
+
+static bool
+_run_test_hooks_internal(const char *path)
+{
+ foo_sub_one = foo_sub_two[0] = foo_sub_two[1] = foo = bar = false;
+
+ {
+ LV2_OSC_URID osc_urid;
+ LV2_Atom_Forge forge;
+
+ lv2_osc_urid_init(&osc_urid, &map);
+ lv2_atom_forge_init(&forge, &map);
+
+ lv2_atom_forge_set_buffer(&forge, buf0, BUF_SIZE);
+ assert(lv2_osc_forge_message_vararg(&forge, &osc_urid, path, ""));
+
+ const LV2_Atom_Object *obj = (const LV2_Atom_Object *)buf0;;
+ assert(lv2_osc_unroll(&osc_urid, obj, lv2_osc_hooks, hook_root) == true);
+ }
+
+ {
+ LV2_OSC_Writer writer;
+ LV2_OSC_Reader reader;
+
+ lv2_osc_writer_initialize(&writer, buf1, BUF_SIZE);
+ assert(lv2_osc_writer_message_vararg(&writer, path, "") == true);
+
+ size_t len;
+ const uint8_t *buf = lv2_osc_writer_finalize(&writer, &len);
+ assert(buf);
+ assert(len);
+
+ lv2_osc_reader_initialize(&reader, buf, len);
+ lv2_osc_reader_match(&reader, len, tree_root, NULL);
+ }
+
+ return true;
+}
+
+static int
+_run_test_hooks()
+{
+ {
+ assert(_run_test_hooks_internal("/nil") == true);
+ assert(foo == 0);
+ assert(bar == 0);
+ assert(foo_sub_one == 0);
+ assert(foo_sub_two[0] == 0);
+ assert(foo_sub_two[1] == 0);
+ }
+
+ {
+ assert(_run_test_hooks_internal("/foo") == true);
+ assert(foo == 2);
+ assert(bar == 0);
+ assert(foo_sub_one == 0);
+ assert(foo_sub_two[0] == 0);
+ assert(foo_sub_two[1] == 0);
+ }
+
+ {
+ assert(_run_test_hooks_internal("/bar") == true);
+ assert(foo == 0);
+ assert(bar == 2);
+ assert(foo_sub_one == 0);
+ assert(foo_sub_two[0] == 0);
+ assert(foo_sub_two[1] == 0);
+ }
+
+ {
+ assert(_run_test_hooks_internal("/sub/nil") == true);
+ assert(foo == 0);
+ assert(bar == 0);
+ assert(foo_sub_one == 0);
+ assert(foo_sub_two[0] == 0);
+ assert(foo_sub_two[1] == 0);
+ }
+
+ {
+ assert(_run_test_hooks_internal("/sub/one") == true);
+ assert(foo == 0);
+ assert(bar == 0);
+ assert(foo_sub_one == 2);
+ assert(foo_sub_two[0] == 0);
+ assert(foo_sub_two[1] == 0);
+ }
+
+ {
+ assert(_run_test_hooks_internal("/sub/two") == true);
+ assert(foo == 0);
+ assert(bar == 0);
+ assert(foo_sub_one == 0);
+ assert(foo_sub_two[0] == 2);
+ assert(foo_sub_two[1] == 2);
+ }
+
+ {
+ assert(_run_test_hooks_internal("/sub/*") == true);
+ assert(foo == 0);
+ assert(bar == 0);
+ assert(foo_sub_one == 2);
+ assert(foo_sub_two[0] == 2);
+ assert(foo_sub_two[1] == 2);
+ }
+
+ {
+ assert(_run_test_hooks_internal("/*/one") == true);
+ assert(foo == 0);
+ assert(bar == 0);
+ assert(foo_sub_one == 2);
+ assert(foo_sub_two[0] == 0);
+ assert(foo_sub_two[1] == 0);
+ }
+
+ {
+ assert(_run_test_hooks_internal("/s*/one") == true);
+ assert(foo == 0);
+ assert(bar == 0);
+ assert(foo_sub_one == 2);
+ assert(foo_sub_two[0] == 0);
+ assert(foo_sub_two[1] == 0);
+ }
+
+ {
+ assert(_run_test_hooks_internal("/su*/one") == true);
+ assert(foo == 0);
+ assert(bar == 0);
+ assert(foo_sub_one == 2);
+ assert(foo_sub_two[0] == 0);
+ assert(foo_sub_two[1] == 0);
+ }
+
+ {
+ assert(_run_test_hooks_internal("/sub*/one") == true);
+ assert(foo == 0);
+ assert(bar == 0);
+ assert(foo_sub_one == 2);
+ assert(foo_sub_two[0] == 0);
+ assert(foo_sub_two[1] == 0);
+ }
+
+ {
+ assert(_run_test_hooks_internal("/*sub/one") == true);
+ assert(foo == 0);
+ assert(bar == 0);
+ assert(foo_sub_one == 2);
+ assert(foo_sub_two[0] == 0);
+ assert(foo_sub_two[1] == 0);
+ }
+
+ {
+ assert(_run_test_hooks_internal("/*s*u*b*/one") == true);
+ assert(foo == 0);
+ assert(bar == 0);
+ assert(foo_sub_one == 2);
+ assert(foo_sub_two[0] == 0);
+ assert(foo_sub_two[1] == 0);
+ }
+
+ {
+ assert(_run_test_hooks_internal("/su[ab]/one") == true);
+ assert(foo == 0);
+ assert(bar == 0);
+ assert(foo_sub_one == 2);
+ assert(foo_sub_two[0] == 0);
+ assert(foo_sub_two[1] == 0);
+ }
+
+ {
+ assert(_run_test_hooks_internal("/su[a-b]/[!a-np-z]ne") == true);
+ assert(foo == 0);
+ assert(bar == 0);
+ assert(foo_sub_one == 2);
+ assert(foo_sub_two[0] == 0);
+ assert(foo_sub_two[1] == 0);
+ }
+
+ {
+ assert(_run_test_hooks_internal("/su[!a-b]/one") == true);
+ assert(foo == 0);
+ assert(bar == 0);
+ assert(foo_sub_one == 0);
+ assert(foo_sub_two[0] == 0);
+ assert(foo_sub_two[1] == 0);
+ }
+
+ {
+ assert(_run_test_hooks_internal("/s?b/?ne") == true);
+ assert(foo == 0);
+ assert(bar == 0);
+ assert(foo_sub_one == 2);
+ assert(foo_sub_two[0] == 0);
+ assert(foo_sub_two[1] == 0);
+ }
+
+ {
+ assert(_run_test_hooks_internal("/s?*/*?e") == true);
+ assert(foo == 0);
+ assert(bar == 0);
+ assert(foo_sub_one == 2);
+ assert(foo_sub_two[0] == 0);
+ assert(foo_sub_two[1] == 0);
+ }
+
+ {
+ assert(_run_test_hooks_internal("/{foo,bar}") == true);
+ assert(foo == 2);
+ assert(bar == 2);
+ assert(foo_sub_one == 0);
+ assert(foo_sub_two[0] == 0);
+ assert(foo_sub_two[1] == 0);
+ }
+
+ {
+ assert(_run_test_hooks_internal("/sub/{one,two}") == true);
+ assert(foo == 0);
+ assert(bar == 0);
+ assert(foo_sub_one == 2);
+ assert(foo_sub_two[0] == 2);
+ assert(foo_sub_two[1] == 2);
+ }
+
+ return 0;
+}
+#endif
+
int
-main(int argc, char **argv)
+main(int argc __attribute__((unused)), char **argv __attribute__((unused)))
{
- (void)argc;
- (void)argv;
+#if !defined(_WIN32)
+ (void)lv2_osc_stream_pollin; //FIXME
+#endif
fprintf(stdout, "running main tests:\n");
assert(_run_tests() == 0);
#if !defined(_WIN32)
+ fprintf(stdout, "running hook tests:\n");
+ assert(_run_test_hooks() == 0);
+#else
+ (void)lv2_osc_hooks; //FIXME
+#endif
+
+#if !defined(_WIN32)
for(const pair_t *pair = pairs; pair->server; pair++)
{
pthread_t thread_1;
diff --git a/plugins/manifest.ttl.in b/plugins/manifest.ttl.in
index 62ac671f..1444edcd 100644
--- a/plugins/manifest.ttl.in
+++ b/plugins/manifest.ttl.in
@@ -17,17 +17,9 @@
@prefix owl: <http://www.w3.org/2002/07/owl#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@prefix ui: <http://lv2plug.in/ns/extensions/ui#> .
-@prefix kx: <http://kxstudio.sf.net/ns/lv2ext/external-ui#> .
@prefix synthpod: <http://open-music-kontrollers.ch/lv2/synthpod#> .
-# to please sord_validate
-kx:Widget
- a rdfs:Class, owl:Class ;
- rdfs:subClassOf ui:UI .
-kx:Host
- a lv2:Feature .
-
# Synthpod Stereo
synthpod:stereo
a lv2:Plugin ;
diff --git a/plugins/meson.build b/plugins/meson.build
index a6452723..ac87460b 100644
--- a/plugins/meson.build
+++ b/plugins/meson.build
@@ -8,14 +8,18 @@ dsp_srcs = ['synthpod_lv2.c',
'synthpod_placeholder.c',
'synthpod_stereo.c']
-nk_deps = [m_dep, rt_dep, thread_dep, lv2_dep, lilv_dep, cairo_dep]
+nk_deps = [m_dep, rt_dep, thread_dep, lv2_dep, lilv_dep, cairo_dep, nk_pugl_dep, xcb_dep, xcbicccm_dep, xcbxrm_dep]
nk_srcs = ['synthpod_lv2_nk.c',
'synthpod_keyboard_nk.c',
- 'synthpod_common_nk.c']
+ 'synthpod_common_nk.c',
+ join_paths('..', 'sandbox_ui.lv2', 'sandbox_slave.c'),
+ join_paths('..', 'bin', 'synthpod_sandbox_x11_driver.c')]
-ui_deps = [m_dep, rt_dep, thread_dep, lv2_dep, lilv_dep, d2tk_dep]
+ui_deps = [m_dep, rt_dep, thread_dep, lv2_dep, lilv_dep, d2tk_dep, xcb_dep, xcbicccm_dep, xcbxrm_dep]
ui_srcs = ['synthpod_lv2_d2tk.c',
- 'synthpod_common_d2tk.c']
+ 'synthpod_common_d2tk.c',
+ join_paths('..', 'sandbox_ui.lv2', 'sandbox_slave.c'),
+ join_paths('..', 'bin', 'synthpod_sandbox_x11_driver.c')]
dsp = shared_module('synthpod', dsp_srcs,
c_args : c_args,
@@ -31,24 +35,10 @@ conf_data.set('MODULE_SUFFIX', '.' + suffix)
if host_machine.system() == 'linux'
conf_data.set('UI_TYPE', 'X11UI')
- nk_deps += gl_dep
- nk_deps += x11_dep
- nk_deps += xext_dep
- nk_srcs += join_paths('..', 'pugl', 'pugl', 'pugl_x11.c')
elif host_machine.system() == 'windows'
- add_languages('cpp')
conf_data.set('UI_TYPE', 'WindowsUI')
- nk_deps += cc.find_library('opengl32')
- nk_deps += cc.find_library('gdi32')
- nk_deps += cc.find_library('user32')
- nk_srcs += join_paths('..', 'pugl', 'pugl', 'pugl_win.cpp')
elif host_machine.system() == 'darwin'
- #add_languages('objc')
conf_data.set('UI_TYPE', 'CocoaUI')
- #nk_deps += cc.find_library('Cocoa')
- #nk_deps += cc.find_library('gl')
- #nk_deps += dependency('appleframeworks', modules : 'cocoa')
- #nk_srcs += join_paths('..', 'pugl', 'pugl', 'pugl_osx.m')
endif
ttls = []
@@ -74,7 +64,7 @@ if use_x11
configure_file(
input : join_paths('..', 'data', 'font', 'Abel-Regular.ttf'),
- output : 'Roboto-Bold.ttf',
+ output : 'Abel-Regular.ttf',
copy : true,
install : true,
install_dir : plug_dir)
@@ -136,7 +126,8 @@ endif
if lv2lint.found()
test('LV2 lint', lv2lint,
- args : ['-Ewarn',
+ args : ['-I', join_paths(build_root, 'plugins', ''),
+ '-Ewarn',
'http://open-music-kontrollers.ch/lv2/synthpod#control2cv',
'http://open-music-kontrollers.ch/lv2/synthpod#cv2control',
'http://open-music-kontrollers.ch/lv2/synthpod#heavyload',
diff --git a/plugins/synthpod_common_d2tk.c b/plugins/synthpod_common_d2tk.c
index c5ea09b7..b9bea814 100644
--- a/plugins/synthpod_common_d2tk.c
+++ b/plugins/synthpod_common_d2tk.c
@@ -137,7 +137,7 @@ struct _plughandle_t {
LV2_Log_Logger logger;
d2tk_pugl_config_t config;
- d2tk_pugl_t *dpugl;
+ d2tk_frontend_t *dpugl;
LV2UI_Controller *controller;
LV2UI_Write_Function writer;
@@ -283,7 +283,7 @@ _plug_populate(plughandle_t *handle, const char *pattern)
}
else
{
- d2tk_pugl_redisplay(handle->dpugl); // schedule redisplay until done
+ d2tk_frontend_redisplay(handle->dpugl); // schedule redisplay until done
}
}
else // normal operation
@@ -321,8 +321,8 @@ static inline void
_expose_plugin_list_header(plughandle_t *handle, const d2tk_rect_t *rect)
{
DBG;
- d2tk_pugl_t *dpugl = handle->dpugl;
- d2tk_base_t *base = d2tk_pugl_get_base(dpugl);
+ d2tk_frontend_t *dpugl = handle->dpugl;
+ d2tk_base_t *base = d2tk_frontend_get_base(dpugl);
if(_initializing(handle) || _lazy_loading(handle)) // still loading ?
{
@@ -341,16 +341,17 @@ static inline void
_expose_plugin_list_body(plughandle_t *handle, const d2tk_rect_t *rect)
{
DBG;
- d2tk_pugl_t *dpugl = handle->dpugl;
- d2tk_base_t *base = d2tk_pugl_get_base(dpugl);
+ d2tk_frontend_t *dpugl = handle->dpugl;
+ d2tk_base_t *base = d2tk_frontend_get_base(dpugl);
const unsigned dd = 24;
const unsigned dn = rect->h / dd;
handle->plug_info = NULL;
- D2TK_BASE_SCROLLBAR(base, rect, D2TK_ID, D2TK_FLAG_SCROLL_Y,
- 0, handle->nplugs, 0, dn, vscroll)
+ const uint32_t max [2] = { 0, handle->nplugs };
+ const uint32_t num [2] = { 0, dn };
+ D2TK_BASE_SCROLLBAR(base, rect, D2TK_ID, D2TK_FLAG_SCROLL_Y, max, num, vscroll)
{
const float voffset = d2tk_scrollbar_get_offset_y(vscroll);
const d2tk_rect_t *col = d2tk_scrollbar_get_rect(vscroll);
@@ -387,8 +388,8 @@ static inline void
_expose_plugin_list(plughandle_t *handle, const d2tk_rect_t *rect)
{
DBG;
- d2tk_pugl_t *dpugl = handle->dpugl;
- d2tk_base_t *base = d2tk_pugl_get_base(dpugl);
+ d2tk_frontend_t *dpugl = handle->dpugl;
+ d2tk_base_t *base = d2tk_frontend_get_base(dpugl);
static const d2tk_coord_t vfrac [3] = { 24, 0 };
D2TK_BASE_LAYOUT(rect, 2, vfrac, D2TK_FLAG_LAYOUT_Y_ABS, vlay)
@@ -481,8 +482,8 @@ static inline void
_expose_sidebar_bottom(plughandle_t *handle, const d2tk_rect_t *rect)
{
DBG;
- d2tk_pugl_t *dpugl = handle->dpugl;
- d2tk_base_t *base = d2tk_pugl_get_base(dpugl);
+ d2tk_frontend_t *dpugl = handle->dpugl;
+ d2tk_base_t *base = d2tk_frontend_get_base(dpugl);
if(!handle->plug_info)
{
@@ -493,8 +494,9 @@ _expose_sidebar_bottom(plughandle_t *handle, const d2tk_rect_t *rect)
const unsigned dn = rect->h / dd;
const unsigned en = 9;
- D2TK_BASE_SCROLLBAR(base, rect, D2TK_ID, D2TK_FLAG_SCROLL_Y,
- 0, en, 0, dn, vscroll)
+ const uint32_t max [2] = { 0, en };
+ const uint32_t num [2] = { 0, dn };
+ D2TK_BASE_SCROLLBAR(base, rect, D2TK_ID, D2TK_FLAG_SCROLL_Y, max, num, vscroll)
{
const float voffset = d2tk_scrollbar_get_offset_y(vscroll);
const d2tk_rect_t *col = d2tk_scrollbar_get_rect(vscroll);
@@ -589,8 +591,8 @@ static inline void
_expose_sidebar(plughandle_t *handle, const d2tk_rect_t *rect)
{
DBG;
- d2tk_pugl_t *dpugl = handle->dpugl;
- d2tk_base_t *base = d2tk_pugl_get_base(dpugl);
+ d2tk_frontend_t *dpugl = handle->dpugl;
+ d2tk_base_t *base = d2tk_frontend_get_base(dpugl);
D2TK_BASE_PANE(base, rect, D2TK_ID, D2TK_FLAG_PANE_Y,
0.6f, 1.f, 0.05f, vpane)
@@ -616,7 +618,7 @@ static inline void
_expose_patchmatrix_mod(plughandle_t *handle, mod_t *mod,
const d2tk_rect_t *rect)
{
- d2tk_base_t *base = d2tk_pugl_get_base(handle->dpugl);
+ d2tk_base_t *base = d2tk_frontend_get_base(handle->dpugl);
const stat_label_t *label = mod->alias.len
? &mod->alias
: &mod->name;
@@ -633,7 +635,7 @@ static inline void
_expose_patchmatrix_connection(plughandle_t *handle, unsigned o,
mod_t *mod_src, mod_t *mod_snk, const d2tk_rect_t *rect)
{
- d2tk_base_t *base = d2tk_pugl_get_base(handle->dpugl);
+ d2tk_base_t *base = d2tk_frontend_get_base(handle->dpugl);
D2TK_BASE_FRAME(base, rect, 0, NULL, frm)
{
@@ -662,7 +664,7 @@ _expose_patchmatrix_connection(plughandle_t *handle, unsigned o,
static inline void
_expose_patchmatrix(plughandle_t *handle, const d2tk_rect_t *rect)
{
- d2tk_base_t *base = d2tk_pugl_get_base(handle->dpugl);
+ d2tk_base_t *base = d2tk_frontend_get_base(handle->dpugl);
const unsigned dd = 128;
const unsigned N = handle->nmods;
@@ -674,8 +676,10 @@ _expose_patchmatrix(plughandle_t *handle, const d2tk_rect_t *rect)
const unsigned dw = rect->w / dd;
const unsigned dh = rect->h / dd;
+ const uint32_t max [2] = { N, N };
+ const uint32_t num [2] = { dw, dh };
D2TK_BASE_SCROLLBAR(base, rect, D2TK_ID,
- D2TK_FLAG_SCROLL_X | D2TK_FLAG_SCROLL_Y, N, N, dw, dh, vscroll)
+ D2TK_FLAG_SCROLL_X | D2TK_FLAG_SCROLL_Y, max, num, vscroll)
{
const float hoffset = d2tk_scrollbar_get_offset_x(vscroll);
const float voffset = d2tk_scrollbar_get_offset_y(vscroll);
@@ -720,8 +724,8 @@ static inline void
_expose_patchbay(plughandle_t *handle, const d2tk_rect_t *rect)
{
DBG;
- d2tk_pugl_t *dpugl = handle->dpugl;
- d2tk_base_t *base = d2tk_pugl_get_base(dpugl);
+ d2tk_frontend_t *dpugl = handle->dpugl;
+ d2tk_base_t *base = d2tk_frontend_get_base(dpugl);
D2TK_BASE_FLOWMATRIX(base, rect, D2TK_ID, flowm)
{
@@ -746,7 +750,7 @@ _expose_patchbay(plughandle_t *handle, const d2tk_rect_t *rect)
state = d2tk_base_toggle_label(base, id, label->len, label->buf,
D2TK_ALIGN_CENTERED, bnd, &mod->selected);
- if(d2tk_base_get_ctrl(base))
+ if(d2tk_base_get_modmask(base, D2TK_MODMASK_CTRL, false))
{
continue;
}
@@ -778,8 +782,8 @@ _expose_patchbay(plughandle_t *handle, const d2tk_rect_t *rect)
static inline void
_expose_status_bar(plughandle_t *handle, const d2tk_rect_t *rect)
{
- d2tk_pugl_t *dpugl = handle->dpugl;
- d2tk_base_t *base = d2tk_pugl_get_base(dpugl);
+ d2tk_frontend_t *dpugl = handle->dpugl;
+ d2tk_base_t *base = d2tk_frontend_get_base(dpugl);
static const d2tk_coord_t hfrac [5] = { 4, 1, 1, 1, 1 };
D2TK_BASE_LAYOUT(rect, 5, hfrac, D2TK_FLAG_LAYOUT_X_REL, hlay)
@@ -823,8 +827,8 @@ _expose_status_bar(plughandle_t *handle, const d2tk_rect_t *rect)
static inline void
_expose_main_area(plughandle_t *handle, const d2tk_rect_t *rect)
{
- d2tk_pugl_t *dpugl = handle->dpugl;
- d2tk_base_t *base = d2tk_pugl_get_base(dpugl);
+ d2tk_frontend_t *dpugl = handle->dpugl;
+ d2tk_base_t *base = d2tk_frontend_get_base(dpugl);
D2TK_BASE_PANE(base, rect, D2TK_ID, D2TK_FLAG_PANE_X,
0.0f, 0.2f, 0.05f, hpane)
@@ -858,8 +862,8 @@ _expose(void *data, d2tk_coord_t w, d2tk_coord_t h)
{
DBG;
plughandle_t *handle = data;
- d2tk_pugl_t *dpugl = handle->dpugl;
- d2tk_base_t *base = d2tk_pugl_get_base(dpugl);
+ d2tk_frontend_t *dpugl = handle->dpugl;
+ d2tk_base_t *base = d2tk_frontend_get_base(dpugl);
const d2tk_rect_t rect = D2TK_RECT(0, 0, w, h);
if(_lazy_loading(handle))
@@ -1016,7 +1020,7 @@ cleanup(LV2UI_Handle instance)
DBG;
plughandle_t *handle = instance;
- d2tk_pugl_free(handle->dpugl);
+ d2tk_frontend_free(handle->dpugl);
sp_regs_deinit(&handle->regs);
@@ -1651,7 +1655,7 @@ port_event(LV2UI_Handle instance, uint32_t port_index, uint32_t size,
return;
}
- d2tk_pugl_redisplay(handle->dpugl); //FIXME only do when needed
+ d2tk_frontend_redisplay(handle->dpugl); //FIXME only do when needed
}
static void
@@ -1761,7 +1765,7 @@ _idle(LV2UI_Handle instance)
DBG;
plughandle_t *handle = instance;
- const int res = d2tk_pugl_step(handle->dpugl);
+ const int res = d2tk_frontend_step(handle->dpugl);
if(_initializing(handle))
{
@@ -1781,7 +1785,7 @@ _resize(LV2UI_Handle instance, int width, int height)
DBG;
plughandle_t *handle = instance;
- return d2tk_pugl_resize(handle->dpugl, width, height);
+ return d2tk_frontend_set_size(handle->dpugl, width, height);
}
static const LV2UI_Resize resize_ext = {
diff --git a/plugins/synthpod_common_nk.c b/plugins/synthpod_common_nk.c
index aee98934..8624acf8 100644
--- a/plugins/synthpod_common_nk.c
+++ b/plugins/synthpod_common_nk.c
@@ -28,11 +28,13 @@
#include "lv2/lv2plug.in/ns/ext/port-groups/port-groups.h"
#include "lv2/lv2plug.in/ns/ext/presets/presets.h"
#include "lv2/lv2plug.in/ns/ext/patch/patch.h"
+#include "lv2/lv2plug.in/ns/ext/instance-access/instance-access.h"
#include <osc.lv2/osc.h>
#include <xpress.lv2/xpress.h>
#include <sandbox_master.h>
+#include <synthpod_sandbox_x11_driver.h>
#include <math.h>
#include <unistd.h> // vfork
@@ -40,6 +42,9 @@
#include <errno.h> // waitpid
#include <time.h>
#include <signal.h> // kill
+#include <inttypes.h> // kill
+#include <pthread.h> // kill
+#include <stdatomic.h> // kill
#define NK_PUGL_API
#include <nk_pugl/nk_pugl.h>
@@ -64,7 +69,6 @@
#endif
typedef enum _property_type_t property_type_t;
-typedef enum _bundle_selector_search_t bundle_selector_search_t;
typedef enum _plugin_selector_search_t plugin_selector_search_t;
typedef enum _preset_selector_search_t preset_selector_search_t;
typedef enum _property_selector_search_t property_selector_search_t;
@@ -110,12 +114,6 @@ enum _property_type_t {
PROPERTY_TYPE_MAX
};
-enum _bundle_selector_search_t {
- BUNDLE_SELECTOR_SEARCH_NAME = 0,
-
- BUNDLE_SELECTOR_SEARCH_MAX
-};
-
enum _plugin_selector_search_t {
PLUGIN_SELECTOR_SEARCH_NAME = 0,
PLUGIN_SELECTOR_SEARCH_COMMENT,
@@ -267,6 +265,8 @@ struct _prof_t {
struct _mod_t {
plughandle_t *handle;
+ LilvInstance *dsp_instance;
+
LV2_URID urn;
LV2_URID subj;
const LilvPlugin *plug;
@@ -342,6 +342,8 @@ struct _mod_ui_t {
const char *uri;
LV2_URID urn;
+ bool threaded;
+
pid_t pid;
struct {
sandbox_master_driver_t driver;
@@ -354,6 +356,10 @@ struct _mod_ui_t {
char *sample_rate;
char *update_rate;
} sbox;
+
+ pthread_t gui_thread;
+ bool last_rolling;
+ atomic_bool gui_done;
};
struct _pset_group_t {
@@ -371,6 +377,8 @@ struct _plughandle_t {
LilvWorld *world;
LilvNodes *bundles;
+ void *dsp_instance;
+
LV2_Atom_Forge forge;
LV2_Log_Log *log;
@@ -384,6 +392,7 @@ struct _plughandle_t {
LV2_URID_Unmap *unmap;
LV2UI_Write_Function writer;
LV2UI_Controller controller;
+ LV2UI_Request_Value *reqval;
nk_pugl_window_t win;
@@ -436,7 +445,6 @@ struct _plughandle_t {
enum nk_collapse_states plugin_info_collapse_states;
enum nk_collapse_states preset_info_collapse_states;
- bundle_selector_search_t bundle_search_selector;
plugin_selector_search_t plugin_search_selector;
preset_selector_search_t preset_search_selector;
property_selector_search_t property_search_selector;
@@ -448,13 +456,11 @@ struct _plughandle_t {
hash_t param_matches;
hash_t dynam_matches;
- char bundle_search_buf [SEARCH_BUF_MAX];
char plugin_search_buf [SEARCH_BUF_MAX];
char preset_search_buf [SEARCH_BUF_MAX];
char port_search_buf [SEARCH_BUF_MAX];
char mod_alias_buf [ALIAS_MAX];
- struct nk_text_edit bundle_search_edit;
struct nk_text_edit plugin_search_edit;
struct nk_text_edit preset_search_edit;
struct nk_text_edit port_search_edit;
@@ -549,10 +555,6 @@ struct _plughandle_t {
bool supports_show;
};
-static const char *bundle_search_labels [BUNDLE_SELECTOR_SEARCH_MAX] = {
- [BUNDLE_SELECTOR_SEARCH_NAME] = "Name"
-};
-
static const char *plugin_search_labels [PLUGIN_SELECTOR_SEARCH_MAX] = {
[PLUGIN_SELECTOR_SEARCH_NAME] = "Name",
[PLUGIN_SELECTOR_SEARCH_COMMENT] = "Comment",
@@ -698,7 +700,10 @@ _image_new(plughandle_t *handle, unsigned w, unsigned h, const void *data)
}
#endif
- puglEnterContext(handle->win.view);
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+ puglEnterContext(handle->win.view, false);
+#pragma GCC diagnostic pop
{
glGenTextures(1, &tex);
glBindTexture(GL_TEXTURE_2D, tex);
@@ -712,7 +717,10 @@ _image_new(plughandle_t *handle, unsigned w, unsigned h, const void *data)
if(handle->win.glGenerateMipmap)
handle->win.glGenerateMipmap(GL_TEXTURE_2D);
}
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
puglLeaveContext(handle->win.view, false);
+#pragma GCC diagnostic pop
return nk_image_id(tex);
}
@@ -723,12 +731,18 @@ _image_free(plughandle_t *handle, struct nk_image *img)
DBG;
if(img->handle.id)
{
- puglEnterContext(handle->win.view);
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+ puglEnterContext(handle->win.view, false);
+#pragma GCC diagnostic pop
{
glDeleteTextures(1, (const GLuint *)&img->handle.id);
img->handle.id = 0;
}
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
puglLeaveContext(handle->win.view, false);
+#pragma GCC diagnostic pop
}
}
@@ -1799,10 +1813,60 @@ _mod_find_by_urn(plughandle_t *handle, LV2_URID urn)
}
static bool
-_mod_ui_is_running(mod_ui_t *mod_ui)
+_mod_ui_is_rolling_threaded(mod_ui_t *mod_ui)
+{
+ DBG;
+ const bool done = atomic_load(&mod_ui->gui_done);
+ const bool rolling = !done && mod_ui->sbox.sb;
+
+ return rolling;
+}
+
+static bool
+_mod_ui_is_rolling_ipc(mod_ui_t *mod_ui)
+{
+ DBG;
+
+ bool rolling = true;
+
+ if( (mod_ui->pid > 0) && mod_ui->sbox.sb)
+ {
+ int status;
+ const int res = waitpid(mod_ui->pid, &status, WUNTRACED | WNOHANG);
+ if(res < 0)
+ {
+ if(errno == ECHILD) // child not existing
+ {
+ rolling = false;
+ }
+ }
+ else if(res == mod_ui->pid)
+ {
+ if(!WIFSTOPPED(status) && !WIFCONTINUED(status)) // child exited/crashed
+ {
+ rolling = false;
+ }
+ }
+ }
+ else
+ {
+ rolling = false;
+ }
+
+ return rolling ;
+}
+
+static bool
+_mod_ui_is_rolling(mod_ui_t *mod_ui)
{
DBG;
- return (mod_ui->pid != 0) && mod_ui->sbox.sb;
+
+ if(mod_ui->threaded)
+ {
+ return _mod_ui_is_rolling_threaded(mod_ui);
+ }
+
+ return _mod_ui_is_rolling_ipc(mod_ui);
}
static void
@@ -1816,11 +1880,15 @@ _mod_uis_send(mod_t *mod, uint32_t index, uint32_t size, uint32_t format,
{
mod_ui_t *mod_ui = *mod_ui_itr;
- if(!_mod_ui_is_running(mod_ui))
+ if(!_mod_ui_is_rolling(mod_ui))
+ {
continue;
+ }
if(sandbox_master_send(mod_ui->sbox.sb, index, size, format, buf) == -1)
+ {
_log_error(handle, "%s: buffer overflow\n", __func__);
+ }
sandbox_master_signal_tx(mod_ui->sbox.sb);
}
}
@@ -2885,7 +2953,7 @@ _mod_ui_subscribe_function(LV2UI_Controller controller, uint32_t index,
}
static mod_ui_t *
-_mod_ui_add(plughandle_t *handle, mod_t *mod, const LilvUI *ui)
+_mod_ui_add(plughandle_t *handle, mod_t *mod, const LilvUI *ui, bool threaded)
{
DBG;
const LilvNode *ui_node = lilv_ui_get_uri(ui);
@@ -2922,11 +2990,14 @@ _mod_ui_add(plughandle_t *handle, mod_t *mod, const LilvUI *ui)
mod_ui_t *mod_ui = calloc(1, sizeof(mod_ui_t));
if(mod_ui)
{
+ mod_ui->threaded = threaded;
mod_ui->mod = mod;
mod_ui->ui = ui;
mod_ui->uri = lilv_node_as_uri(ui_node);
mod_ui->urn = handle->map->map(handle->map->handle, mod_ui->uri);
+ atomic_init(&mod_ui->gui_done, true);
+
const char *plugin_bundle_uri = plugin_bundle_node
? lilv_node_as_uri(plugin_bundle_node)
: NULL;
@@ -2979,6 +3050,43 @@ _check_support_for_ui(const char *exec_uri)
return (res == 0);
}
+#define ARGC 21
+static void *
+_gui_thread(void *data)
+{
+ mod_ui_t *mod_ui = data;
+ mod_t *mod = mod_ui->mod;
+ plughandle_t *handle = mod->handle;
+
+ const LilvNode *plugin_node = lilv_plugin_get_uri(mod->plug);
+ const char *plugin_uri = plugin_node ? lilv_node_as_uri(plugin_node) : NULL;
+ const char *plugin_urn = handle->unmap->unmap(handle->unmap->handle, mod_ui->mod->urn);
+
+ char *argv [ARGC + 1] = {
+ "synthpod_sandbox_x11",
+ "-n", (char *)plugin_urn,
+ "-p", (char *)plugin_uri,
+ "-P", mod_ui->sbox.plugin_bundle_path,
+ "-u", (char *)mod_ui->uri,
+ "-U", mod_ui->sbox.ui_bundle_path,
+ "-s", mod_ui->sbox.socket_uri,
+ "-w", mod_ui->sbox.window_name,
+ "-m", mod_ui->sbox.minimum,
+ "-r", mod_ui->sbox.sample_rate,
+ "-f", mod_ui->sbox.update_rate,
+ NULL
+ };
+
+ if(mod->dsp_instance)
+ {
+ atomic_store(&mod_ui->gui_done, false);
+ x11_app_run(ARGC, argv, mod->dsp_instance, &mod_ui->gui_done);
+ }
+
+ return NULL;
+}
+#undef ARGC
+
static void
_mod_ui_run(mod_ui_t *mod_ui, bool sync)
{
@@ -2994,19 +3102,33 @@ _mod_ui_run(mod_ui_t *mod_ui, bool sync)
const char *exec_uri = NULL;
if(lilv_ui_is_a(ui, handle->regs.ui.x11.node))
+ {
exec_uri = "synthpod_sandbox_x11";
+ }
else if(lilv_ui_is_a(ui, handle->regs.ui.gtk2.node))
+ {
exec_uri = "synthpod_sandbox_gtk2";
+ }
else if(lilv_ui_is_a(ui, handle->regs.ui.gtk3.node))
+ {
exec_uri = "synthpod_sandbox_gtk3";
+ }
else if(lilv_ui_is_a(ui, handle->regs.ui.qt4.node))
+ {
exec_uri = "synthpod_sandbox_qt4";
+ }
else if(lilv_ui_is_a(ui, handle->regs.ui.qt5.node))
+ {
exec_uri = "synthpod_sandbox_qt5";
+ }
else if(lilv_ui_is_a(ui, handle->regs.ui.kx_widget.node))
+ {
exec_uri = "synthpod_sandbox_kx";
+ }
else if(lilv_world_ask(handle->world, ui_node, handle->regs.core.extension_data.node, handle->regs.ui.show_interface.node))
+ {
exec_uri = "synthpod_sandbox_show";
+ }
mod_ui->sbox.sb = sandbox_master_new(&mod_ui->sbox.driver, mod_ui, mod->minimum);
@@ -3017,48 +3139,58 @@ _mod_ui_run(mod_ui_t *mod_ui, bool sync)
&& mod_ui->sbox.socket_uri && mod_ui->sbox.window_name && mod_ui->sbox.minimum
&& mod_ui->sbox.sample_rate && mod_ui->sbox.update_rate && mod_ui->sbox.sb)
{
- const pid_t pid = vfork();
- if(pid == 0) // child
- {
- char *const args [] = {
#if 0
- "gdb", "--args",
+ fprintf(stderr, "%s \\\n%s %s \\\n%s %s \\\n%s %s \\\n%s %s \\\n%s %s \\\n%s %s \\\n%s %s \\\n%s %s \\\n%s %s \\\n%s %s\n",
+ (char *)exec_uri,
+ "-n", (char *)plugin_urn,
+ "-p", (char *)plugin_uri,
+ "-P", mod_ui->sbox.plugin_bundle_path,
+ "-u", (char *)mod_ui->uri,
+ "-U", mod_ui->sbox.ui_bundle_path,
+ "-s", mod_ui->sbox.socket_uri,
+ "-w", mod_ui->sbox.window_name,
+ "-m", mod_ui->sbox.minimum,
+ "-r", mod_ui->sbox.sample_rate,
+ "-f", mod_ui->sbox.update_rate);
#endif
- (char *)exec_uri,
- "-n", (char *)plugin_urn,
- "-p", (char *)plugin_uri,
- "-P", mod_ui->sbox.plugin_bundle_path,
- "-u", (char *)mod_ui->uri,
- "-U", mod_ui->sbox.ui_bundle_path,
- "-s", mod_ui->sbox.socket_uri,
- "-w", mod_ui->sbox.window_name,
- "-m", mod_ui->sbox.minimum,
- "-r", mod_ui->sbox.sample_rate,
- "-f", mod_ui->sbox.update_rate,
- NULL
- };
+ if(mod_ui->threaded)
+ {
+ if(pthread_create(&mod_ui->gui_thread, NULL, _gui_thread, mod_ui) != 0)
+ {
+ //FIXME
+ };
+ }
+ else
+ {
+ const pid_t pid = vfork();
+ if(pid == 0) // child
+ {
+ char *const args [] = {
#if 0
- fprintf(stderr, "%s \\\n%s %s \\\n%s %s \\\n%s %s \\\n%s %s \\\n%s %s \\\n%s %s \\\n%s %s \\\n%s %s \\\n%s %s \\\n%s %s\n",
- (char *)exec_uri,
- "-n", (char *)plugin_urn,
- "-p", (char *)plugin_uri,
- "-P", mod_ui->sbox.plugin_bundle_path,
- "-u", (char *)mod_ui->uri,
- "-U", mod_ui->sbox.ui_bundle_path,
- "-s", mod_ui->sbox.socket_uri,
- "-w", mod_ui->sbox.window_name,
- "-m", mod_ui->sbox.minimum,
- "-r", mod_ui->sbox.sample_rate,
- "-f", mod_ui->sbox.update_rate);
+ "gdb", "--args",
#endif
+ (char *)exec_uri,
+ "-n", (char *)plugin_urn,
+ "-p", (char *)plugin_uri,
+ "-P", mod_ui->sbox.plugin_bundle_path,
+ "-u", (char *)mod_ui->uri,
+ "-U", mod_ui->sbox.ui_bundle_path,
+ "-s", mod_ui->sbox.socket_uri,
+ "-w", mod_ui->sbox.window_name,
+ "-m", mod_ui->sbox.minimum,
+ "-r", mod_ui->sbox.sample_rate,
+ "-f", mod_ui->sbox.update_rate,
+ NULL
+ };
+
+ execvp(args[0], args);
+ }
- execvp(args[0], args);
+ // parent
+ mod_ui->pid = pid;
}
- // parent
- mod_ui->pid = pid;
-
bool connected = false;
for(unsigned i = 0; i < 10; i++)
@@ -3098,12 +3230,15 @@ _mod_ui_run(mod_ui_t *mod_ui, bool sync)
}
static void
-_mod_ui_stop(mod_ui_t *mod_ui, bool sync)
+_mod_ui_stop_threaded(mod_ui_t *mod_ui)
{
- DBG;
- mod_t *mod = mod_ui->mod;
- plughandle_t *handle = mod->handle;
+ atomic_store(&mod_ui->gui_done, true);
+ pthread_join(mod_ui->gui_thread, NULL);
+}
+static void
+_mod_ui_stop_ipc(mod_ui_t *mod_ui)
+{
if(mod_ui->pid)
{
int status;
@@ -3112,6 +3247,23 @@ _mod_ui_stop(mod_ui_t *mod_ui, bool sync)
waitpid(mod_ui->pid, &status, WUNTRACED); // blocking waitpid
mod_ui->pid = 0;
}
+}
+
+static void
+_mod_ui_stop(mod_ui_t *mod_ui, bool sync)
+{
+ DBG;
+ mod_t *mod = mod_ui->mod;
+ plughandle_t *handle = mod->handle;
+
+ if(mod_ui->threaded)
+ {
+ _mod_ui_stop_threaded(mod_ui);
+ }
+ else
+ {
+ _mod_ui_stop_ipc(mod_ui);
+ }
if(mod_ui->sbox.sb)
{
@@ -3143,8 +3295,10 @@ _mod_ui_free(mod_ui_t *mod_ui)
const LilvNode *ui_node = lilv_ui_get_uri(mod_ui->ui);
const LilvNode *bundle_node = lilv_plugin_get_bundle_uri(mod->plug);
- if(_mod_ui_is_running(mod_ui))
+ if(_mod_ui_is_rolling(mod_ui))
+ {
_mod_ui_stop(mod_ui, false);
+ }
lilv_world_unload_resource(handle->world, ui_node);
//lilv_world_unload_bundle(handle->world, (LilvNode *)bundle_node);
@@ -3347,6 +3501,18 @@ _patch_mod_reinstantiate_set(plughandle_t *handle, mod_t *mod, int32_t state)
{
DBG;
+ HASH_FOREACH(&mod->uis, mod_ui_itr)
+ {
+ mod_ui_t *mod_ui = *mod_ui_itr;
+
+ if(_mod_ui_is_rolling(mod_ui))
+ {
+ _mod_ui_stop(mod_ui, true); // stop existing UI
+ }
+ }
+
+ mod->dsp_instance = NULL;
+
if( _message_request(handle)
&& synthpod_patcher_set(&handle->regs, &handle->forge,
mod->urn, 0, handle->regs.synthpod.module_reinstantiate.urid,
@@ -3890,28 +4056,52 @@ _mod_init(plughandle_t *handle, mod_t *mod, const LilvPlugin *plug)
{
const LilvUI *ui = lilv_uis_get(mod->ui_nodes, itr);
const LilvNode *ui_uri = lilv_ui_get_uri(ui);
+ bool threaded = false;
+ const bool x11ui = lilv_ui_is_a(ui, handle->regs.ui.x11.node);
- const bool needs_instance_access = lilv_world_ask(handle->world, ui_uri,
- handle->regs.core.required_feature.node, handle->regs.ui.instance_access.node);
- if(needs_instance_access)
+ const bool wants_instance_access = lilv_world_ask(handle->world, ui_uri,
+ handle->regs.core.required_feature.node, handle->regs.ui.instance_access.node)
+ || lilv_world_ask(handle->world, ui_uri,
+ handle->regs.core.optional_feature.node, handle->regs.ui.instance_access.node);
+ if(wants_instance_access)
{
- if(handle->log)
- _log_warning(handle, "<%s> instance-access extension not supported\n", lilv_node_as_uri(ui_uri));
- continue;
+ if(x11ui && handle->dsp_instance)
+ {
+ threaded = true;
+ }
+ else
+ {
+ if(handle->log)
+ {
+ _log_warning(handle, "<%s> instance-access extension not supported\n", lilv_node_as_uri(ui_uri));
+ }
+ continue;
+ }
}
- const bool needs_data_access = lilv_world_ask(handle->world, ui_uri,
- handle->regs.core.required_feature.node, handle->regs.ui.data_access.node);
- if(needs_data_access)
+ const bool wants_data_access = lilv_world_ask(handle->world, ui_uri,
+ handle->regs.core.required_feature.node, handle->regs.ui.data_access.node)
+ || lilv_world_ask(handle->world, ui_uri,
+ handle->regs.core.optional_feature.node, handle->regs.ui.data_access.node);
+ if(wants_data_access)
{
- if(handle->log)
- _log_warning(handle, "<%s> data-access extension not supported\n", lilv_node_as_uri(ui_uri));
- continue;
+ if(x11ui && handle->dsp_instance)
+ {
+ threaded = true;
+ }
+ else
+ {
+ if(handle->log)
+ {
+ _log_warning(handle, "<%s> data-access extension not supported\n", lilv_node_as_uri(ui_uri));
+ }
+ continue;
+ }
}
//FIXME check for more unsupported features
- _mod_ui_add(handle, mod, ui);
+ _mod_ui_add(handle, mod, ui, threaded);
}
_set_module_idisp_subscription(handle, mod, 1);
@@ -4346,100 +4536,6 @@ _undiscover_bundles(plughandle_t *handle)
}
static void
-_refresh_main_bundle_list(plughandle_t *handle)
-{
- DBG;
- _hash_free(&handle->bundle_matches);
-
- bool search = _textedit_len(&handle->bundle_search_edit) != 0;
-
- LILV_FOREACH(nodes, itr, handle->bundles)
- {
- const LilvNode *bundle = lilv_nodes_get(handle->bundles, itr);
-
- //FIXME support other search criteria
- LilvNode *label_node = lilv_world_get(handle->world, bundle, handle->node.rdfs_label, NULL);
- if(!label_node)
- label_node = lilv_world_get(handle->world, bundle, handle->node.lv2_name, NULL);
- if(label_node)
- {
- const char *label_str = lilv_node_as_string(label_node);
-
- if(!search || strcasestr(label_str, _textedit_const(&handle->bundle_search_edit)))
- {
- _hash_add(&handle->bundle_matches, (void *)bundle);
- }
-
- lilv_node_free(label_node);
- }
- }
-
- _hash_sort_r(&handle->bundle_matches, _sort_rdfs_label, handle);
-}
-
-static void
-_expose_main_bundle_list(plughandle_t *handle, struct nk_context *ctx,
- bool find_matches)
-{
- DBG;
- if(_hash_empty(&handle->bundle_matches) || find_matches)
- _refresh_main_bundle_list(handle);
-
- int count = 0;
- HASH_FOREACH(&handle->bundle_matches, itr)
- {
- const LilvNode *bundle = *itr;
- if(bundle)
- {
- LilvNode *label_node = lilv_world_get(handle->world, bundle, handle->node.rdfs_label, NULL);
- if(!label_node)
- label_node = lilv_world_get(handle->world, bundle, handle->node.lv2_name, NULL);
- if(label_node)
- {
- const char *label_str = lilv_node_as_string(label_node);
- const char *offset = NULL;
- if( (offset = strstr(label_str, ".lv2/")))
- label_str = offset + 5; // skip path prefix
-
- nk_style_push_style_item(ctx, &ctx->style.selectable.normal, (count++ % 2)
- ? nk_style_item_color(nk_rgb(40, 40, 40))
- : nk_style_item_color(nk_rgb(45, 45, 45))); // NK_COLOR_WINDOW
-
- if(nk_select_label(ctx, label_str, NK_TEXT_LEFT, nk_false))
- {
- char *bundle_path = lilv_node_get_path(bundle, NULL);
- if(bundle_path)
- {
- char *tmp = strdup(bundle_path);
- if(tmp)
- {
- char *end = strrchr(tmp, '/');
- if(end)
- *end = '\0';
-
- const LV2_URID bundle_urid = handle->map->map(handle->map->handle, tmp);
- if( _message_request(handle)
- && synthpod_patcher_copy(&handle->regs, &handle->forge,
- bundle_urid, 0, 0) )
- {
- _message_write(handle);
- }
-
- free(tmp);
- }
- lilv_free(bundle_path);
- }
- }
-
- nk_style_pop_style_item(ctx);
-
- lilv_node_free(label_node);
- }
- }
- }
-}
-
-static void
_refresh_main_plugin_list(plughandle_t *handle)
{
DBG;
@@ -4457,6 +4553,13 @@ _refresh_main_plugin_list(plughandle_t *handle)
LILV_FOREACH(plugins, i, plugs)
{
const LilvPlugin *plug = lilv_plugins_get(plugs, i);
+ const char *plug_uri = lilv_node_as_uri(lilv_plugin_get_uri(plug));
+
+ if( !strcmp(plug_uri, SYNTHPOD_PREFIX"sink")
+ || !strcmp(plug_uri, SYNTHPOD_PREFIX"source") )
+ {
+ continue;
+ }
LilvNode *name_node = lilv_plugin_get_name(plug);
if(name_node)
@@ -6421,10 +6524,14 @@ _show_selected_nodes(plughandle_t *handle)
{
mod_ui_t *mod_ui = *mod_ui_itr;
- if(_mod_ui_is_running(mod_ui))
+ if(_mod_ui_is_rolling(mod_ui))
+ {
_mod_ui_stop(mod_ui, true); // stop existing UI
+ }
else
+ {
_mod_ui_run(mod_ui, true); // run UI
+ }
break; //FIXME only consider first UI
}
@@ -7548,43 +7655,7 @@ _expose_main_body(plughandle_t *handle, struct nk_context *ctx, float dh, float
if(handle->show_bottombar)
{
- nk_layout_row_dynamic(ctx, lower_h, 4);
-
- if(_group_begin(ctx, "Bundles", NK_WINDOW_TITLE, &bb))
- {
- nk_menubar_begin(ctx);
- {
- const float dim [2] = {0.6, 0.4};
- nk_layout_row(ctx, NK_DYNAMIC, dy, 2, dim);
-
- const size_t old_len = _textedit_len(&handle->bundle_search_edit);
- const nk_flags args = NK_EDIT_FIELD | NK_EDIT_SIG_ENTER | NK_EDIT_AUTO_SELECT;
- if(!handle->has_initial_focus)
- {
- nk_edit_focus(ctx, args);
- handle->has_initial_focus = true;
- }
- const nk_flags flags = nk_edit_buffer(ctx, args, &handle->bundle_search_edit, nk_filter_default);
- _textedit_zero_terminate(&handle->bundle_search_edit);
- if( (flags & NK_EDIT_COMMITED) || (old_len != _textedit_len(&handle->bundle_search_edit)) )
- handle->bundle_find_matches = true;
- if( (flags & NK_EDIT_ACTIVE) && handle->has_control_a)
- nk_textedit_select_all(&handle->bundle_search_edit);
-
- const bundle_selector_search_t old_sel = handle->bundle_search_selector;
- handle->bundle_search_selector = nk_combo(ctx, bundle_search_labels, BUNDLE_SELECTOR_SEARCH_MAX,
- handle->bundle_search_selector, dy, nk_vec2(nk_widget_width(ctx), 7*dy));
- if(old_sel != handle->bundle_search_selector)
- handle->bundle_find_matches = true;
- }
- nk_menubar_end(ctx);
-
- nk_layout_row_dynamic(ctx, handle->dy2, 1);
- nk_spacing(ctx, 1); // fixes mouse-over bug
- _expose_main_bundle_list(handle, ctx, handle->bundle_find_matches);
-
- _group_end(ctx, &bb);
- }
+ nk_layout_row_dynamic(ctx, lower_h, 3);
if(_group_begin(ctx, "Plugins", NK_WINDOW_TITLE, &bb))
{
@@ -7916,7 +7987,7 @@ _expose_main_body(plughandle_t *handle, struct nk_context *ctx, float dh, float
const LilvUI *ui = mod_ui->ui;
const LilvNode *ui_node = lilv_ui_get_uri(ui);
- const bool is_running = _mod_ui_is_running(mod_ui);
+ const bool is_rolling = _mod_ui_is_rolling(mod_ui);
const char *label = "Show plugin GUI";
if(!single_ui)
@@ -7937,13 +8008,17 @@ _expose_main_body(plughandle_t *handle, struct nk_context *ctx, float dh, float
label = "Show";
}
- const bool is_still_running = _toolbar_label(ctx, is_running, 0x0, label);
- if(is_still_running != is_running)
+ const bool is_still_running = _toolbar_label(ctx, is_rolling, 0x0, label);
+ if(is_still_running != is_rolling)
{
- if(is_running)
+ if(is_rolling)
+ {
_mod_ui_stop(mod_ui, true); // stop existing UI
+ }
else
+ {
_mod_ui_run(mod_ui, true); // run UI
+ }
}
}
}
@@ -8290,6 +8365,10 @@ instantiate(const LV2UI_Descriptor *descriptor, const char *plugin_uri,
opts = features[i]->data;
else if(!strcmp(features[i]->URI, LV2_LOG__log))
handle->log = features[i]->data;
+ else if(!strcmp(features[i]->URI, LV2_UI__requestValue))
+ handle->reqval= features[i]->data;
+ else if(!strcmp(features[i]->URI, LV2_INSTANCE_ACCESS_URI))
+ handle->dsp_instance = features[i]->data;
}
if(!parent)
@@ -8400,7 +8479,6 @@ instantiate(const LV2UI_Descriptor *descriptor, const char *plugin_uri,
handle->plugin_info_collapse_states = NK_MINIMIZED;
handle->preset_info_collapse_states = NK_MINIMIZED;
- nk_textedit_init_fixed(&handle->bundle_search_edit, handle->bundle_search_buf, SEARCH_BUF_MAX);
nk_textedit_init_fixed(&handle->plugin_search_edit, handle->plugin_search_buf, SEARCH_BUF_MAX);
nk_textedit_init_fixed(&handle->preset_search_edit, handle->preset_search_buf, SEARCH_BUF_MAX);
nk_textedit_init_fixed(&handle->port_search_edit, handle->port_search_buf, SEARCH_BUF_MAX);
@@ -8475,11 +8553,6 @@ cleanup(LV2UI_Handle instance)
_icon_unload(handle, handle->icon.settings);
_icon_unload(handle, handle->icon.menu);
- if(handle->win.cfg.font.face)
- free(handle->win.cfg.font.face);
- nk_pugl_hide(&handle->win);
- nk_pugl_shutdown(&handle->win);
-
_set_module_selector(handle, NULL);
HASH_FREE(&handle->mods, ptr)
@@ -8488,6 +8561,11 @@ cleanup(LV2UI_Handle instance)
_mod_free(handle, mod);
}
+ if(handle->win.cfg.font.face)
+ free(handle->win.cfg.font.face);
+ nk_pugl_hide(&handle->win);
+ nk_pugl_shutdown(&handle->win);
+
HASH_FREE(&handle->conns, ptr)
{
mod_conn_t *mod_conn = ptr;
@@ -8990,6 +9068,18 @@ port_event(LV2UI_Handle instance, uint32_t port_index, uint32_t size,
nk_pugl_post_redisplay(&handle->win);
}
}
+ else if( (prop == handle->regs.ui.instance_access.urid)
+ && (value->type == handle->forge.Long)
+ && subj )
+ {
+ const LV2_Atom_Long *ptr = (const LV2_Atom_Long *)value;
+
+ mod_t *mod = _mod_find_by_urn(handle, subj);
+ if(mod)
+ {
+ mod->dsp_instance = (LilvInstance *)ptr->body;
+ }
+ }
else if( (prop == handle->regs.synthpod.graph_position_x.urid)
&& (value->type == handle->forge.Float) )
{
@@ -9117,6 +9207,7 @@ port_event(LV2UI_Handle instance, uint32_t port_index, uint32_t size,
const LV2_Atom_Float *mod_pos_y = NULL;
const LV2_Atom_String *mod_alias = NULL;
const LV2_Atom_URID *ui_uri = NULL;
+ const LV2_Atom_Long *instance_access = NULL;
lv2_atom_object_get(body,
handle->regs.core.plugin.urid, &plugin,
@@ -9124,6 +9215,7 @@ port_event(LV2UI_Handle instance, uint32_t port_index, uint32_t size,
handle->regs.synthpod.module_position_y.urid, &mod_pos_y,
handle->regs.synthpod.module_alias.urid, &mod_alias,
handle->regs.ui.ui.urid, &ui_uri, //FIXME use this
+ handle->regs.ui.instance_access.urid, &instance_access,
0); //FIXME query more
const LV2_URID urid = plugin
@@ -9186,6 +9278,11 @@ port_event(LV2UI_Handle instance, uint32_t port_index, uint32_t size,
strncpy(mod->alias, LV2_ATOM_BODY_CONST(&mod_alias->atom), ALIAS_MAX-1);
}
+ if(instance_access && (instance_access->atom.type == handle->forge.Long) )
+ {
+ mod->dsp_instance = (LilvInstance *)instance_access->body;
+ }
+
if(ui_urn)
{
// look for ui, and run it
@@ -9193,11 +9290,15 @@ port_event(LV2UI_Handle instance, uint32_t port_index, uint32_t size,
{
mod_ui_t *mod_ui = *mod_ui_itr;
- if(_mod_ui_is_running(mod_ui))
+ if(_mod_ui_is_rolling(mod_ui))
+ {
_mod_ui_stop(mod_ui, false);
+ }
if(mod_ui->urn == ui_urn)
+ {
_mod_ui_run(mod_ui, false);
+ }
}
}
}
@@ -9341,33 +9442,27 @@ _idle(LV2UI_Handle instance)
{
mod_ui_t *mod_ui = *mod_ui_itr;
- if(!_mod_ui_is_running(mod_ui))
- continue;
-
- bool rolling = true;
+ bool rolling = _mod_ui_is_rolling(mod_ui);
- int status;
- const int res = waitpid(mod_ui->pid, &status, WUNTRACED | WNOHANG);
- if(res < 0)
- {
- if(errno == ECHILD) // child not existing
- rolling = false;
- }
- else if(res == mod_ui->pid)
+ if(!mod_ui->last_rolling && !rolling)
{
- if(!WIFSTOPPED(status) && !WIFCONTINUED(status)) // child exited/crashed
- rolling = false;
+ continue;
}
- if(!rolling || sandbox_master_recv(mod_ui->sbox.sb))
+ if( (mod_ui->last_rolling && !rolling) || sandbox_master_recv(mod_ui->sbox.sb))
{
_mod_ui_stop(mod_ui, true);
+ rolling = false;
}
+
+ mod_ui->last_rolling = rolling;
}
}
if(nk_pugl_process_events(&handle->win) || handle->done)
+ {
return 1;
+ }
return 0;
}
diff --git a/plugins/synthpod_ui.ttl b/plugins/synthpod_ui.ttl
index 6a34373d..2e56611a 100644
--- a/plugins/synthpod_ui.ttl
+++ b/plugins/synthpod_ui.ttl
@@ -21,6 +21,7 @@
@prefix urid: <http://lv2plug.in/ns/ext/urid#> .
@prefix opts: <http://lv2plug.in/ns/ext/options#> .
@prefix param: <http://lv2plug.in/ns/ext/parameters#> .
+@prefix instacc: <http://lv2plug.in/ns/ext/instance-access> .
@prefix synthpod: <http://open-music-kontrollers.ch/lv2/synthpod#> .
@@ -31,7 +32,7 @@ synthpod:common_4_nk
lv2:symbol "notify" ;
ui:protocol atom:eventTransfer ;
] ;
- lv2:optionalFeature ui:resize, log:log, ui:portSubscribe, opts:options ;
+ lv2:optionalFeature ui:resize, log:log, ui:portSubscribe, opts:options, ui:requestValue, instacc: ;
lv2:requiredFeature ui:portMap, urid:map, urid:unmap, ui:idleInterface ;
lv2:extensionData ui:idleInterface, ui:resize ;
opts:supportedOption param:sampleRate, ui:updateRate .
@@ -41,7 +42,7 @@ synthpod:root_4_nk
lv2:symbol "notify" ;
ui:protocol atom:eventTransfer ;
] ;
- lv2:optionalFeature ui:resize, log:log, ui:portSubscribe, opts:options ;
+ lv2:optionalFeature ui:resize, log:log, ui:portSubscribe, opts:options, ui:requestValue, instacc: ;
lv2:requiredFeature ui:portMap, urid:map, urid:unmap, ui:idleInterface ;
lv2:extensionData ui:idleInterface, ui:resize ;
opts:supportedOption param:sampleRate, ui:updateRate .
@@ -53,7 +54,7 @@ synthpod:common_5_d2tk
lv2:symbol "notify" ;
ui:protocol atom:eventTransfer ;
] ;
- lv2:optionalFeature ui:resize, log:log, ui:portSubscribe, opts:options ;
+ lv2:optionalFeature ui:resize, log:log, ui:portSubscribe, opts:options, ui:requestValue, instacc: ;
lv2:requiredFeature ui:portMap, urid:map, urid:unmap, ui:idleInterface ;
lv2:extensionData ui:idleInterface, ui:resize ;
opts:supportedOption param:sampleRate, ui:updateRate .
@@ -63,12 +64,12 @@ synthpod:root_5_d2tk
lv2:symbol "notify" ;
ui:protocol atom:eventTransfer ;
] ;
- lv2:optionalFeature ui:resize, log:log, ui:portSubscribe, opts:options ;
+ lv2:optionalFeature ui:resize, log:log, ui:portSubscribe, opts:options, ui:requestValue, instacc: ;
lv2:requiredFeature ui:portMap, urid:map, urid:unmap, ui:idleInterface ;
lv2:extensionData ui:idleInterface, ui:resize ;
opts:supportedOption param:sampleRate, ui:updateRate .
-# Keybaord UIs
+# Keyboard UIs
synthpod:keyboard_4_nk
lv2:optionalFeature ui:resize ;
lv2:requiredFeature ui:idleInterface, urid:map ;
diff --git a/pugl/AUTHORS b/pugl/AUTHORS
deleted file mode 100644
index 5625baa4..00000000
--- a/pugl/AUTHORS
+++ /dev/null
@@ -1,11 +0,0 @@
-Author:
- David Robillard <d@drobilla.net>
-
-Original GLX inspiration, portability fixes:
- Ben Loftis
-
-Various fixes and improvements:
- Robin Gareus <robin@gareus.org>
-
-UTF-8 and Windows event work:
- Erik Åldstedt Sund <erikalds@gmail.com>
diff --git a/pugl/README.md b/pugl/README.md
deleted file mode 100644
index 77809d80..00000000
--- a/pugl/README.md
+++ /dev/null
@@ -1,28 +0,0 @@
-PUGL
-====
-
-Pugl is a minimal portable API for GUIs which supports embedding and is
-suitable for use in plugins. It works on X11, Mac OS X, and Windows. GUIs can
-be drawn with OpenGL or Cairo.
-
-Pugl is vaguely similar to GLUT, but with some significant distinctions:
-
- * Minimal in scope, providing only what is necessary to draw and receive
- keyboard and mouse input.
-
- * No reliance on static data whatsoever, so the API can be used in plugins or
- multiple independent parts of a program.
-
- * Single implementation, which is small, liberally licensed Free / Open Source
- Software, and suitable for direct inclusion in programs if avoiding a
- library dependency is desired.
-
- * Support for embedding in other windows, so Pugl code can draw to a widget
- inside a larger GUI.
-
- * More complete support for keyboard input, including additional "special"
- keys, modifiers, and support for detecting individual modifier key presses.
-
-For more information, see <http://drobilla.net/software/pugl>.
-
- -- David Robillard <d@drobilla.net>
diff --git a/pugl/pugl/cairo_gl.h b/pugl/pugl/cairo_gl.h
deleted file mode 100644
index fb4cb2a4..00000000
--- a/pugl/pugl/cairo_gl.h
+++ /dev/null
@@ -1,106 +0,0 @@
-/*
- Copyright 2016 David Robillard <http://drobilla.net>
-
- Permission to use, copy, modify, and/or distribute this software for any
- purpose with or without fee is hereby granted, provided that the above
- copyright notice and this permission notice appear in all copies.
-
- THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-*/
-
-#if defined(PUGL_HAVE_GL) && defined(PUGL_HAVE_CAIRO)
-
-#include <cairo/cairo.h>
-#include <stdint.h>
-
-#include "pugl/gl.h"
-
-typedef struct {
- unsigned texture_id;
- uint8_t* buffer;
-} PuglCairoGL;
-
-static cairo_surface_t*
-pugl_cairo_gl_create(PuglCairoGL* ctx, int width, int height, int bpp)
-{
- free(ctx->buffer);
- ctx->buffer = (uint8_t*)calloc(bpp * width * height, sizeof(uint8_t));
- if (!ctx->buffer) {
- fprintf(stderr, "failed to allocate surface buffer\n");
- return NULL;
- }
-
- return cairo_image_surface_create_for_data(
- ctx->buffer, CAIRO_FORMAT_ARGB32, width, height, bpp * width);
-}
-
-static void
-pugl_cairo_gl_free(PuglCairoGL* ctx)
-{
- free(ctx->buffer);
- ctx->buffer = NULL;
-}
-
-static void
-pugl_cairo_gl_configure(PuglCairoGL* ctx,
- int width __attribute__((unused)), int height __attribute__((unused)))
-{
- glDisable(GL_DEPTH_TEST);
- glEnable(GL_BLEND);
- glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
- glEnable(GL_TEXTURE_RECTANGLE_ARB);
-
- glMatrixMode(GL_PROJECTION);
- glLoadIdentity();
- glOrtho(-1.0f, 1.0f, -1.0f, 1.0f, -1.0f, 1.0f);
-
- glClear(GL_COLOR_BUFFER_BIT);
-
- glDeleteTextures(1, &ctx->texture_id);
- glGenTextures(1, &ctx->texture_id);
- glBindTexture(GL_TEXTURE_RECTANGLE_ARB, ctx->texture_id);
- glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);
-}
-
-static void
-pugl_cairo_gl_draw(PuglCairoGL* ctx, int width, int height)
-{
- glMatrixMode(GL_MODELVIEW);
- glLoadIdentity();
- glViewport(0, 0, width, height);
- glClear(GL_COLOR_BUFFER_BIT);
-
- glPushMatrix();
- glEnable(GL_TEXTURE_RECTANGLE_ARB);
- glEnable(GL_TEXTURE_2D);
-
- glTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, GL_RGBA8,
- width, height, 0,
- GL_BGRA, GL_UNSIGNED_BYTE, ctx->buffer);
-
- glBegin(GL_QUADS);
- glTexCoord2f(0.0f, (GLfloat)height);
- glVertex2f(-1.0f, -1.0f);
-
- glTexCoord2f((GLfloat)width, (GLfloat)height);
- glVertex2f(1.0f, -1.0f);
-
- glTexCoord2f((GLfloat)width, 0.0f);
- glVertex2f(1.0f, 1.0f);
-
- glTexCoord2f(0.0f, 0.0f);
- glVertex2f(-1.0f, 1.0f);
- glEnd();
-
- glDisable(GL_TEXTURE_2D);
- glDisable(GL_TEXTURE_RECTANGLE_ARB);
- glPopMatrix();
-}
-
-#endif
diff --git a/pugl/pugl/pugl.h b/pugl/pugl/pugl.h
deleted file mode 100644
index dbbad901..00000000
--- a/pugl/pugl/pugl.h
+++ /dev/null
@@ -1,634 +0,0 @@
-/*
- Copyright 2012-2016 David Robillard <http://drobilla.net>
-
- Permission to use, copy, modify, and/or distribute this software for any
- purpose with or without fee is hereby granted, provided that the above
- copyright notice and this permission notice appear in all copies.
-
- THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-*/
-
-/**
- @file pugl.h API for Pugl, a minimal portable API for OpenGL.
-*/
-
-#ifndef PUGL_H_INCLUDED
-#define PUGL_H_INCLUDED
-
-#include <stdint.h>
-
-#ifdef PUGL_SHARED
-# ifdef _WIN32
-# define PUGL_LIB_IMPORT __declspec(dllimport)
-# define PUGL_LIB_EXPORT __declspec(dllexport)
-# else
-# define PUGL_LIB_IMPORT __attribute__((visibility("default")))
-# define PUGL_LIB_EXPORT __attribute__((visibility("default")))
-# endif
-# ifdef PUGL_INTERNAL
-# define PUGL_API PUGL_LIB_EXPORT
-# else
-# define PUGL_API PUGL_LIB_IMPORT
-# endif
-#else
-# define PUGL_API
-#endif
-
-#ifdef __cplusplus
-extern "C" {
-#else
-# include <stdbool.h>
-#endif
-
-/**
- @defgroup pugl Pugl
- A minimal portable API for OpenGL.
- @{
-*/
-
-/**
- A Pugl view.
-*/
-typedef struct PuglViewImpl PuglView;
-
-/**
- A native window handle.
-
- On X11, this is a Window.
- On OSX, this is an NSView*.
- On Windows, this is a HWND.
-*/
-typedef intptr_t PuglNativeWindow;
-
-/**
- Handle for opaque user data.
-*/
-typedef void* PuglHandle;
-
-/**
- Return status code.
-*/
-typedef enum {
- PUGL_SUCCESS = 0
-} PuglStatus;
-
-/**
- Drawing context type.
-*/
-typedef enum {
- PUGL_GL = 0x1,
- PUGL_CAIRO = 0x2,
- PUGL_CAIRO_GL = 0x3
-} PuglContextType;
-
-/**
- Convenience symbols for ASCII control characters.
-*/
-typedef enum {
- PUGL_CHAR_BACKSPACE = 0x08,
- PUGL_CHAR_ESCAPE = 0x1B,
- PUGL_CHAR_DELETE = 0x7F
-} PuglChar;
-
-/**
- Keyboard modifier flags.
-*/
-typedef enum {
- PUGL_MOD_SHIFT = 1, /**< Shift key */
- PUGL_MOD_CTRL = 1 << 1, /**< Control key */
- PUGL_MOD_ALT = 1 << 2, /**< Alt/Option key */
- PUGL_MOD_SUPER = 1 << 3 /**< Mod4/Command/Windows key */
-} PuglMod;
-
-/**
- Special (non-Unicode) keyboard keys.
-
- The numerical values of these symbols occupy a reserved range of Unicode
- points, so it is possible to express either a PuglKey value or a Unicode
- character in the same variable. This is sometimes useful for interfacing
- with APIs that do not make this distinction.
-*/
-typedef enum {
- PUGL_KEY_F1 = 0xE000,
- PUGL_KEY_F2,
- PUGL_KEY_F3,
- PUGL_KEY_F4,
- PUGL_KEY_F5,
- PUGL_KEY_F6,
- PUGL_KEY_F7,
- PUGL_KEY_F8,
- PUGL_KEY_F9,
- PUGL_KEY_F10,
- PUGL_KEY_F11,
- PUGL_KEY_F12,
- PUGL_KEY_LEFT,
- PUGL_KEY_UP,
- PUGL_KEY_RIGHT,
- PUGL_KEY_DOWN,
- PUGL_KEY_PAGE_UP,
- PUGL_KEY_PAGE_DOWN,
- PUGL_KEY_HOME,
- PUGL_KEY_END,
- PUGL_KEY_INSERT,
- PUGL_KEY_SHIFT,
- PUGL_KEY_CTRL,
- PUGL_KEY_ALT,
- PUGL_KEY_SUPER
-} PuglKey;
-
-/**
- The type of a PuglEvent.
-*/
-typedef enum {
- PUGL_NOTHING, /**< No event */
- PUGL_BUTTON_PRESS, /**< Mouse button press */
- PUGL_BUTTON_RELEASE, /**< Mouse button release */
- PUGL_CONFIGURE, /**< View moved and/or resized */
- PUGL_EXPOSE, /**< View exposed, redraw required */
- PUGL_CLOSE, /**< Close view */
- PUGL_KEY_PRESS, /**< Key press */
- PUGL_KEY_RELEASE, /**< Key release */
- PUGL_ENTER_NOTIFY, /**< Pointer entered view */
- PUGL_LEAVE_NOTIFY, /**< Pointer left view */
- PUGL_MOTION_NOTIFY, /**< Pointer motion */
- PUGL_SCROLL, /**< Scroll */
- PUGL_FOCUS_IN, /**< Keyboard focus entered view */
- PUGL_FOCUS_OUT /**< Keyboard focus left view */
-} PuglEventType;
-
-typedef enum {
- PUGL_IS_SEND_EVENT = 1
-} PuglEventFlag;
-
-/**
- Reason for a PuglEventCrossing.
-*/
-typedef enum {
- PUGL_CROSSING_NORMAL, /**< Crossing due to pointer motion. */
- PUGL_CROSSING_GRAB, /**< Crossing due to a grab. */
- PUGL_CROSSING_UNGRAB /**< Crossing due to a grab release. */
-} PuglCrossingMode;
-
-/**
- Common header for all event structs.
-*/
-typedef struct {
- PuglEventType type; /**< Event type. */
- PuglView* view; /**< View that received this event. */
- uint32_t flags; /**< Bitwise OR of PuglEventFlag values. */
-} PuglEventAny;
-
-/**
- Button press or release event.
-
- For event types PUGL_BUTTON_PRESS and PUGL_BUTTON_RELEASE.
-*/
-typedef struct {
- PuglEventType type; /**< PUGL_BUTTON_PRESS or PUGL_BUTTON_RELEASE. */
- PuglView* view; /**< View that received this event. */
- uint32_t flags; /**< Bitwise OR of PuglEventFlag values. */
- uint32_t time; /**< Time in milliseconds. */
- double x; /**< View-relative X coordinate. */
- double y; /**< View-relative Y coordinate. */
- double x_root; /**< Root-relative X coordinate. */
- double y_root; /**< Root-relative Y coordinate. */
- unsigned state; /**< Bitwise OR of PuglMod flags. */
- unsigned button; /**< 1-relative button number. */
-} PuglEventButton;
-
-/**
- Configure event for when window size or position has changed.
-*/
-typedef struct {
- PuglEventType type; /**< PUGL_CONFIGURE. */
- PuglView* view; /**< View that received this event. */
- uint32_t flags; /**< Bitwise OR of PuglEventFlag values. */
- double x; /**< New parent-relative X coordinate. */
- double y; /**< New parent-relative Y coordinate. */
- double width; /**< New width. */
- double height; /**< New height. */
-} PuglEventConfigure;
-
-/**
- Expose event for when a region must be redrawn.
-*/
-typedef struct {
- PuglEventType type; /**< PUGL_EXPOSE. */
- PuglView* view; /**< View that received this event. */
- uint32_t flags; /**< Bitwise OR of PuglEventFlag values. */
- double x; /**< View-relative X coordinate. */
- double y; /**< View-relative Y coordinate. */
- double width; /**< Width of exposed region. */
- double height; /**< Height of exposed region. */
- int count; /**< Number of expose events to follow. */
-} PuglEventExpose;
-
-/**
- Window close event.
-*/
-typedef struct {
- PuglEventType type; /**< PUGL_CLOSE. */
- PuglView* view; /**< View that received this event. */
- uint32_t flags; /**< Bitwise OR of PuglEventFlag values. */
-} PuglEventClose;
-
-/**
- Key press/release event.
-
- Keys that correspond to a Unicode character have `character` and `utf8` set.
- Other keys will have `character` 0, but `special` may be set if this is a
- known special key.
-
- A key press may be part of a multi-key sequence to generate a wide
- character. If `filter` is set, this event is part of a multi-key sequence
- and should be ignored if the application is reading textual input.
- Following the series of filtered press events, a press event with
- `character` and `utf8` (but `keycode` 0) will be sent. This event will have
- no corresponding release event.
-
- Generally, an application should either work with raw keyboard press/release
- events based on `keycode` (ignoring events with `keycode` 0), or
- read textual input based on `character` or `utf8` (ignoring releases and
- events with `filter` 1). Note that blindly appending `utf8` will yield
- incorrect text, since press events are sent for both individually composed
- keys and the resulting synthetic multi-byte press.
-*/
-typedef struct {
- PuglEventType type; /**< PUGL_KEY_PRESS or PUGL_KEY_RELEASE. */
- PuglView* view; /**< View that received this event. */
- uint32_t flags; /**< Bitwise OR of PuglEventFlag values. */
- uint32_t time; /**< Time in milliseconds. */
- double x; /**< View-relative X coordinate. */
- double y; /**< View-relative Y coordinate. */
- double x_root; /**< Root-relative X coordinate. */
- double y_root; /**< Root-relative Y coordinate. */
- unsigned state; /**< Bitwise OR of PuglMod flags. */
- unsigned keycode; /**< Raw key code. */
- uint32_t character; /**< Unicode character code, or 0. */
- PuglKey special; /**< Special key, or 0. */
- uint8_t utf8[8]; /**< UTF-8 string. */
- bool filter; /**< True if part of a multi-key sequence. */
-} PuglEventKey;
-
-/**
- Pointer crossing event (enter and leave).
-*/
-typedef struct {
- PuglEventType type; /**< PUGL_ENTER_NOTIFY or PUGL_LEAVE_NOTIFY. */
- PuglView* view; /**< View that received this event. */
- uint32_t flags; /**< Bitwise OR of PuglEventFlag values. */
- uint32_t time; /**< Time in milliseconds. */
- double x; /**< View-relative X coordinate. */
- double y; /**< View-relative Y coordinate. */
- double x_root; /**< Root-relative X coordinate. */
- double y_root; /**< Root-relative Y coordinate. */
- unsigned state; /**< Bitwise OR of PuglMod flags. */
- PuglCrossingMode mode; /**< Reason for crossing. */
-} PuglEventCrossing;
-
-/**
- Pointer motion event.
-*/
-typedef struct {
- PuglEventType type; /**< PUGL_MOTION_NOTIFY. */
- PuglView* view; /**< View that received this event. */
- uint32_t flags; /**< Bitwise OR of PuglEventFlag values. */
- uint32_t time; /**< Time in milliseconds. */
- double x; /**< View-relative X coordinate. */
- double y; /**< View-relative Y coordinate. */
- double x_root; /**< Root-relative X coordinate. */
- double y_root; /**< Root-relative Y coordinate. */
- unsigned state; /**< Bitwise OR of PuglMod flags. */
- bool is_hint; /**< True iff this event is a motion hint. */
- bool focus; /**< True iff this is the focused window. */
-} PuglEventMotion;
-
-/**
- Scroll event.
-
- The scroll distance is expressed in "lines", an arbitrary unit that
- corresponds to a single tick of a detented mouse wheel. For example, `dy` =
- 1.0 scrolls 1 line up. Some systems and devices support finer resolution
- and/or higher values for fast scrolls, so programs should handle any value
- gracefully.
- */
-typedef struct {
- PuglEventType type; /**< PUGL_SCROLL. */
- PuglView* view; /**< View that received this event. */
- uint32_t flags; /**< Bitwise OR of PuglEventFlag values. */
- uint32_t time; /**< Time in milliseconds. */
- double x; /**< View-relative X coordinate. */
- double y; /**< View-relative Y coordinate. */
- double x_root; /**< Root-relative X coordinate. */
- double y_root; /**< Root-relative Y coordinate. */
- unsigned state; /**< Bitwise OR of PuglMod flags. */
- double dx; /**< Scroll X distance in lines. */
- double dy; /**< Scroll Y distance in lines. */
-} PuglEventScroll;
-
-/**
- Keyboard focus event.
-*/
-typedef struct {
- PuglEventType type; /**< PUGL_FOCUS_IN or PUGL_FOCUS_OUT. */
- PuglView* view; /**< View that received this event. */
- uint32_t flags; /**< Bitwise OR of PuglEventFlag values. */
- bool grab; /**< True iff this is a grab/ungrab event. */
-} PuglEventFocus;
-
-/**
- Interface event.
-
- This is a union of all event structs. The `type` must be checked to
- determine which fields are safe to access. A pointer to PuglEvent can
- either be cast to the appropriate type, or the union members used.
-*/
-typedef union {
- PuglEventType type; /**< Event type. */
- PuglEventAny any; /**< Valid for all event types. */
- PuglEventButton button; /**< PUGL_BUTTON_PRESS, PUGL_BUTTON_RELEASE. */
- PuglEventConfigure configure; /**< PUGL_CONFIGURE. */
- PuglEventExpose expose; /**< PUGL_EXPOSE. */
- PuglEventClose close; /**< PUGL_CLOSE. */
- PuglEventKey key; /**< PUGL_KEY_PRESS, PUGL_KEY_RELEASE. */
- PuglEventCrossing crossing; /**< PUGL_ENTER_NOTIFY, PUGL_LEAVE_NOTIFY. */
- PuglEventMotion motion; /**< PUGL_MOTION_NOTIFY. */
- PuglEventScroll scroll; /**< PUGL_SCROLL. */
- PuglEventFocus focus; /**< PUGL_FOCUS_IN, PUGL_FOCUS_OUT. */
-} PuglEvent;
-
-/**
- @name Initialization
- Configuration functions which must be called before creating a window.
- @{
-*/
-
-/**
- Create a Pugl view.
-
- To create a window, call the various puglInit* functions as necessary, then
- call puglCreateWindow().
-
- @param pargc Pointer to argument count (currently unused).
- @param argv Arguments (currently unused).
- @return A newly created view.
-*/
-PUGL_API PuglView*
-puglInit(int* pargc, char** argv);
-
-/**
- Set the window class name before creating a window.
-*/
-PUGL_API void
-puglInitWindowClass(PuglView* view, const char* name);
-
-/**
- Set the parent window before creating a window (for embedding).
-*/
-PUGL_API void
-puglInitWindowParent(PuglView* view, PuglNativeWindow parent);
-
-/**
- Set the window size before creating a window.
-*/
-PUGL_API void
-puglInitWindowSize(PuglView* view, int width, int height);
-
-/**
- Set the minimum window size before creating a window.
-*/
-PUGL_API void
-puglInitWindowMinSize(PuglView* view, int width, int height);
-
-/**
- Set the window aspect ratio range before creating a window.
-
- The x and y values here represent a ratio of width to height. To set a
- fixed aspect ratio, set the minimum and maximum values to the same ratio.
-*/
-PUGL_API void
-puglInitWindowAspectRatio(PuglView* view,
- int min_x,
- int min_y,
- int max_x,
- int max_y);
-
-/**
- Enable or disable resizing before creating a window.
-*/
-PUGL_API void
-puglInitResizable(PuglView* view, bool resizable);
-
-/**
- Set transient parent before creating a window.
-
- On X11, parent must be a Window.
- On OSX, parent must be an NSView*.
-*/
-PUGL_API void
-puglInitTransientFor(PuglView* view, uintptr_t parent);
-
-/**
- Set the context type before creating a window.
-*/
-PUGL_API void
-puglInitContextType(PuglView* view, PuglContextType type);
-
-/**
- @}
-*/
-
-/**
- @name Windows
- Functions for creating and managing a visible window for a view.
- @{
-*/
-
-/**
- Create a window with the settings given by the various puglInit functions.
-
- @return 1 (pugl does not currently support multiple windows).
-*/
-PUGL_API int
-puglCreateWindow(PuglView* view, const char* title);
-
-/**
- Show the current window.
-*/
-PUGL_API void
-puglShowWindow(PuglView* view);
-
-/**
- Hide the current window.
-*/
-PUGL_API void
-puglHideWindow(PuglView* view);
-
-/**
- Return the native window handle.
-*/
-PUGL_API PuglNativeWindow
-puglGetNativeWindow(PuglView* view);
-
-/**
- @}
-*/
-
-/**
- Set the handle to be passed to all callbacks.
-
- This is generally a pointer to a struct which contains all necessary state.
- Everything needed in callbacks should be here, not in static variables.
-*/
-PUGL_API void
-puglSetHandle(PuglView* view, PuglHandle handle);
-
-/**
- Get the handle to be passed to all callbacks.
-*/
-PUGL_API PuglHandle
-puglGetHandle(PuglView* view);
-
-/**
- Return true iff the view is currently visible.
-*/
-PUGL_API bool
-puglGetVisible(PuglView* view);
-
-/**
- Get the current size of the view.
-*/
-PUGL_API void
-puglGetSize(PuglView* view, int* width, int* height);
-
-/**
- @name Context
- Functions for accessing the drawing context.
- @{
-*/
-
-/**
- Get the drawing context.
-
- For PUGL_GL contexts, this is unused and returns NULL.
- For PUGL_CAIRO contexts, this returns a pointer to a cairo_t.
-*/
-PUGL_API void*
-puglGetContext(PuglView* view);
-
-
-/**
- Enter the drawing context.
-
- This must be called before any code that accesses the drawing context,
- including any GL functions. This is only necessary for code that does so
- outside the usual draw callback or handling of an expose event.
-*/
-PUGL_API void
-puglEnterContext(PuglView* view);
-
-/**
- Leave the drawing context.
-
- This must be called after puglEnterContext and applies the results of the
- drawing code (for example, by swapping buffers).
-*/
-PUGL_API void
-puglLeaveContext(PuglView* view, bool flush);
-
-/**
- @}
-*/
-
-/**
- @name Event Handling
- @{
-*/
-
-/**
- A function called when an event occurs.
-*/
-typedef void (*PuglEventFunc)(PuglView* view, const PuglEvent* event);
-
-/**
- Set the function to call when an event occurs.
-*/
-PUGL_API void
-puglSetEventFunc(PuglView* view, PuglEventFunc eventFunc);
-
-/**
- Ignore synthetic repeated key events.
-*/
-PUGL_API void
-puglIgnoreKeyRepeat(PuglView* view, bool ignore);
-
-/**
- Copy selection to clipboard.
-*/
-PUGL_API void
-puglCopyToClipboard(PuglView* view, const char* selection, size_t len);
-
-/**
- Paste selection from clipboard.
-*/
-PUGL_API const char*
-puglPasteFromClipboard(PuglView* view, size_t* len);
-
-/**
- Grab the input focus.
-*/
-PUGL_API void
-puglGrabFocus(PuglView* view);
-
-/**
- Block and wait for an event to be ready.
-
- This can be used in a loop to only process events via puglProcessEvents when
- necessary. This function will block indefinitely if no events are
- available, so is not appropriate for use in programs that need to perform
- regular updates (e.g. animation).
-*/
-PUGL_API PuglStatus
-puglWaitForEvent(PuglView* view);
-
-/**
- Process all pending window events.
-
- This handles input events as well as rendering, so it should be called
- regularly and rapidly enough to keep the UI responsive. This function does
- not block if no events are pending.
-*/
-PUGL_API PuglStatus
-puglProcessEvents(PuglView* view);
-
-/**
- @}
-*/
-
-/**
- Request a redisplay on the next call to puglProcessEvents().
-*/
-PUGL_API void
-puglPostRedisplay(PuglView* view);
-
-/**
- Destroy a GL window.
-*/
-PUGL_API void
-puglDestroy(PuglView* view);
-
-/**
- @}
-*/
-
-#ifdef __cplusplus
-} /* extern "C" */
-#endif
-
-#endif /* PUGL_H_INCLUDED */
diff --git a/pugl/pugl/pugl_internal.h b/pugl/pugl/pugl_internal.h
deleted file mode 100644
index 5976cd76..00000000
--- a/pugl/pugl/pugl_internal.h
+++ /dev/null
@@ -1,273 +0,0 @@
-/*
- Copyright 2012-2016 David Robillard <http://drobilla.net>
-
- Permission to use, copy, modify, and/or distribute this software for any
- purpose with or without fee is hereby granted, provided that the above
- copyright notice and this permission notice appear in all copies.
-
- THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-*/
-
-/**
- @file pugl_internal.h Private platform-independent definitions.
-
- Note this file contains function definitions, so it must be compiled into
- the final binary exactly once. Each platform specific implementation file
- including it once should achieve this.
-
- If you are copying the pugl code into your source tree, the following
- symbols can be defined to tweak pugl behaviour:
-
- PUGL_HAVE_CAIRO: Include Cairo support code.
- PUGL_HAVE_GL: Include OpenGL support code.
-*/
-
-#include <stdlib.h>
-#include <string.h>
-
-#include "pugl/pugl.h"
-
-typedef struct PuglInternalsImpl PuglInternals;
-
-struct PuglViewImpl {
- PuglHandle handle;
- PuglEventFunc eventFunc;
-
- PuglInternals* impl;
- char* selection;
-
- char* windowClass;
- PuglNativeWindow parent;
- PuglContextType ctx_type;
- uintptr_t transient_parent;
-
- int width;
- int height;
- int min_width;
- int min_height;
- int min_aspect_x;
- int min_aspect_y;
- int max_aspect_x;
- int max_aspect_y;
- bool ignoreKeyRepeat;
- bool redisplay;
- bool resizable;
- bool visible;
-};
-
-PuglInternals* puglInitInternals(void);
-
-PuglView*
-puglInit(int* pargc __attribute__((unused)), char** argv __attribute__((unused)))
-{
- PuglView* view = (PuglView*)calloc(1, sizeof(PuglView));
- if (!view) {
- return NULL;
- }
-
- PuglInternals* impl = puglInitInternals();
- if (!impl) {
- return NULL;
- }
-
- view->ctx_type = PUGL_GL;
- view->impl = impl;
- view->width = 640;
- view->height = 480;
-
- return view;
-}
-
-void
-puglInitWindowSize(PuglView* view, int width, int height)
-{
- view->width = width;
- view->height = height;
-}
-
-void
-puglInitWindowMinSize(PuglView* view, int width, int height)
-{
- view->min_width = width;
- view->min_height = height;
-}
-
-void
-puglInitWindowAspectRatio(PuglView* view,
- int min_x,
- int min_y,
- int max_x,
- int max_y)
-{
- view->min_aspect_x = min_x;
- view->min_aspect_y = min_y;
- view->max_aspect_x = max_x;
- view->max_aspect_y = max_y;
-}
-
-void
-puglInitWindowClass(PuglView* view, const char* name)
-{
- const size_t len = strlen(name);
-
- free(view->windowClass);
- view->windowClass = (char*)calloc(1, len + 1);
- memcpy(view->windowClass, name, len);
-}
-
-void
-puglInitWindowParent(PuglView* view, PuglNativeWindow parent)
-{
- view->parent = parent;
-}
-
-void
-puglInitResizable(PuglView* view, bool resizable)
-{
- view->resizable = resizable;
-}
-
-void
-puglInitTransientFor(PuglView* view, uintptr_t parent)
-{
- view->transient_parent = parent;
-}
-
-void
-puglInitContextType(PuglView* view, PuglContextType type)
-{
- view->ctx_type = type;
-}
-
-void
-puglSetHandle(PuglView* view, PuglHandle handle)
-{
- view->handle = handle;
-}
-
-PuglHandle
-puglGetHandle(PuglView* view)
-{
- return view->handle;
-}
-
-bool
-puglGetVisible(PuglView* view)
-{
- return view->visible;
-}
-
-void
-puglGetSize(PuglView* view, int* width, int* height)
-{
- *width = view->width;
- *height = view->height;
-}
-
-void
-puglIgnoreKeyRepeat(PuglView* view, bool ignore)
-{
- view->ignoreKeyRepeat = ignore;
-}
-
-void
-puglSetEventFunc(PuglView* view, PuglEventFunc eventFunc)
-{
- view->eventFunc = eventFunc;
-}
-
-/** Return the code point for buf, or the replacement character on error. */
-static uint32_t
-puglDecodeUTF8(const uint8_t* buf)
-{
-#define FAIL_IF(cond) { if (cond) return 0xFFFD; }
-
- // http://en.wikipedia.org/wiki/UTF-8
-
- if (buf[0] < 0x80) {
- return buf[0];
- } else if (buf[0] < 0xC2) {
- return 0xFFFD;
- } else if (buf[0] < 0xE0) {
- FAIL_IF((buf[1] & 0xC0) != 0x80);
- return (buf[0] << 6) + buf[1] - 0x3080;
- } else if (buf[0] < 0xF0) {
- FAIL_IF((buf[1] & 0xC0) != 0x80);
- FAIL_IF(buf[0] == 0xE0 && buf[1] < 0xA0);
- FAIL_IF((buf[2] & 0xC0) != 0x80);
- return (buf[0] << 12) + (buf[1] << 6) + buf[2] - 0xE2080;
- } else if (buf[0] < 0xF5) {
- FAIL_IF((buf[1] & 0xC0) != 0x80);
- FAIL_IF(buf[0] == 0xF0 && buf[1] < 0x90);
- FAIL_IF(buf[0] == 0xF4 && buf[1] >= 0x90);
- FAIL_IF((buf[2] & 0xC0) != 0x80);
- FAIL_IF((buf[3] & 0xC0) != 0x80);
- return ((buf[0] << 18) +
- (buf[1] << 12) +
- (buf[2] << 6) +
- buf[3] - 0x3C82080);
- }
- return 0xFFFD;
-}
-
-static void
-puglDispatchEvent(PuglView* view, const PuglEvent* event)
-{
- switch (event->type) {
- case PUGL_NOTHING:
- break;
- case PUGL_CONFIGURE:
- view->width = event->configure.width;
- view->height = event->configure.height;
- puglEnterContext(view);
- view->eventFunc(view, event);
- puglLeaveContext(view, false);
- break;
- case PUGL_EXPOSE:
- if (event->expose.count == 0) {
- puglEnterContext(view);
- view->eventFunc(view, event);
- puglLeaveContext(view, true);
- }
- break;
- default:
- view->eventFunc(view, event);
- }
-}
-
-static void
-puglClearSelection(PuglView* view)
-{
- if(view->selection) {
- free(view->selection);
- view->selection = NULL;
- }
-}
-
-static void
-puglSetSelection(PuglView* view, const char *selection, size_t len)
-{
- puglClearSelection(view);
-
- if(selection) {
- view->selection = (char*)malloc(len + 1);
- if(view->selection) {
- memcpy(view->selection, selection, len);
- view->selection[len] = 0;
- }
- }
-}
-
-static const char*
-puglGetSelection(PuglView* view, size_t* len)
-{
- if(len)
- *len = view->selection ? strlen(view->selection) : 0;
- return view->selection;
-}
diff --git a/pugl/pugl/pugl_osx.m b/pugl/pugl/pugl_osx.m
deleted file mode 100644
index 014d553a..00000000
--- a/pugl/pugl/pugl_osx.m
+++ /dev/null
@@ -1,746 +0,0 @@
-/*
- Copyright 2012-2016 David Robillard <http://drobilla.net>
-
- Permission to use, copy, modify, and/or distribute this software for any
- purpose with or without fee is hereby granted, provided that the above
- copyright notice and this permission notice appear in all copies.
-
- THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-*/
-
-/**
- @file pugl_osx.m OSX/Cocoa Pugl Implementation.
-*/
-
-#include <stdlib.h>
-
-#import <Cocoa/Cocoa.h>
-
-#include "pugl/cairo_gl.h"
-#include "pugl/gl.h"
-#include "pugl/pugl_internal.h"
-
-@class PuglOpenGLView;
-
-struct PuglInternalsImpl {
- NSApplication* app;
- PuglOpenGLView* glview;
- id window;
- NSEvent* nextEvent;
- unsigned mods;
-#ifdef PUGL_HAVE_CAIRO
- cairo_surface_t* surface;
- cairo_t* cr;
- PuglCairoGL cairo_gl;
-#endif
-};
-
-@interface PuglWindow : NSWindow
-{
-@public
- PuglView* puglview;
-}
-
-- (id) initWithContentRect:(NSRect)contentRect
- styleMask:(unsigned int)aStyle
- backing:(NSBackingStoreType)bufferingType
- defer:(BOOL)flag;
-- (void) setPuglview:(PuglView*)view;
-- (BOOL) windowShouldClose:(id)sender;
-- (BOOL) canBecomeKeyWindow:(id)sender;
-@end
-
-@implementation PuglWindow
-
-- (id)initWithContentRect:(NSRect)contentRect
- styleMask:(unsigned int)aStyle
- backing:(NSBackingStoreType)bufferingType
- defer:(BOOL)flag
-{
- if (![super initWithContentRect:contentRect
- styleMask:(NSClosableWindowMask |
- NSTitledWindowMask |
- NSResizableWindowMask)
- backing:NSBackingStoreBuffered defer:NO]) {
- return nil;
- }
-
- [self setAcceptsMouseMovedEvents:YES];
- return (PuglWindow*)self;
-}
-
-- (void)setPuglview:(PuglView*)view
-{
- puglview = view;
- [self setContentSize:NSMakeSize(view->width, view->height)];
-}
-
-- (BOOL)windowShouldClose:(id)sender
-{
- const PuglEventClose ev = {
- PUGL_CLOSE,
- puglview,
- 0
- };
- puglDispatchEvent(puglview, (PuglEvent*)&ev);
-
- return YES;
-}
-
-- (BOOL) canBecomeKeyWindow
-{
- return YES;
-}
-
-- (BOOL) canBecomeMainWindow
-{
- return YES;
-}
-
-- (BOOL) canBecomeKeyWindow:(id)sender
-{
- return NO;
-}
-
-@end
-
-@interface PuglOpenGLView : NSOpenGLView
-{
-@public
- PuglView* puglview;
-
- NSTrackingArea* trackingArea;
-}
-
-- (id) initWithFrame:(NSRect)frame;
-- (void) reshape;
-- (void) drawRect:(NSRect)rect;
-- (NSPoint) eventLocation:(NSEvent*)event;
-- (void) mouseEntered:(NSEvent*)event;
-- (void) mouseExited:(NSEvent*)event;
-- (void) mouseMoved:(NSEvent*)event;
-- (void) mouseDragged:(NSEvent*)event;
-- (void) rightMouseDragged:(NSEvent*)event;
-- (void) mouseDown:(NSEvent*)event;
-- (void) mouseUp:(NSEvent*)event;
-- (void) rightMouseDragged:(NSEvent*)event;
-- (void) rightMouseDown:(NSEvent*)event;
-- (void) rightMouseUp:(NSEvent*)event;
-- (void) otherMouseDragged:(NSEvent*)event;
-- (void) otherMouseDown:(NSEvent*)event;
-- (void) otherMouseUp:(NSEvent*)event;
-- (void) scrollWheel:(NSEvent*)event;
-- (void) keyDown:(NSEvent*)event;
-- (void) keyUp:(NSEvent*)event;
-- (void) flagsChanged:(NSEvent*)event;
-
-@end
-
-@implementation PuglOpenGLView
-
-- (id) initWithFrame:(NSRect)frame
-{
- NSOpenGLPixelFormatAttribute pixelAttribs[16] = {
- NSOpenGLPFADoubleBuffer,
- NSOpenGLPFAAccelerated,
- NSOpenGLPFAColorSize, 32,
- NSOpenGLPFADepthSize, 32,
- NSOpenGLPFAMultisample,
- NSOpenGLPFASampleBuffers, 1,
- NSOpenGLPFASamples, 4,
- 0
- };
-
- NSOpenGLPixelFormat* pixelFormat = [
- [NSOpenGLPixelFormat alloc] initWithAttributes:pixelAttribs];
-
- if (pixelFormat) {
- self = [super initWithFrame:frame pixelFormat:pixelFormat];
- [pixelFormat release];
- } else {
- self = [super initWithFrame:frame];
- }
-
- if (self) {
- [[self openGLContext] makeCurrentContext];
- [self reshape];
- [NSOpenGLContext clearCurrentContext];
- }
- return self;
-}
-
-- (void) reshape
-{
- [[self openGLContext] update];
-
- if (!puglview) {
- return;
- }
-
- const NSRect bounds = [self bounds];
- const PuglEventConfigure ev = {
- PUGL_CONFIGURE,
- puglview,
- 0,
- bounds.origin.x,
- bounds.origin.y,
- bounds.size.width,
- bounds.size.height,
- };
-
-#ifdef PUGL_HAVE_CAIRO
- PuglInternals* impl = puglview->impl;
- if (puglview->ctx_type & PUGL_CAIRO) {
- cairo_surface_destroy(impl->surface);
- cairo_destroy(impl->cr);
- impl->surface = pugl_cairo_gl_create(
- &impl->cairo_gl, ev.width, ev.height, 4);
- impl->cr = cairo_create(impl->surface);
- pugl_cairo_gl_configure(&impl->cairo_gl, ev.width, ev.height);
- }
-#endif
-
- puglDispatchEvent(puglview, (PuglEvent*)&ev);
-}
-
-- (void) drawRect:(NSRect)rect
-{
- const PuglEventExpose ev = {
- PUGL_EXPOSE,
- puglview,
- 0,
- rect.origin.x,
- rect.origin.y,
- rect.size.width,
- rect.size.height,
- 0
- };
-
- puglDispatchEvent(puglview, (const PuglEvent*)&ev);
-
-#ifdef PUGL_HAVE_CAIRO
- if (puglview->ctx_type & PUGL_CAIRO) {
- pugl_cairo_gl_draw(
- &puglview->impl->cairo_gl, puglview->width, puglview->height);
- }
-#endif
-}
-
-- (BOOL) acceptsFirstResponder
-{
- return YES;
-}
-
-static unsigned
-getModifiers(PuglView* view, NSEvent* ev)
-{
- const unsigned modifierFlags = [ev modifierFlags];
-
- unsigned mods = 0;
- mods |= (modifierFlags & NSShiftKeyMask) ? PUGL_MOD_SHIFT : 0;
- mods |= (modifierFlags & NSControlKeyMask) ? PUGL_MOD_CTRL : 0;
- mods |= (modifierFlags & NSAlternateKeyMask) ? PUGL_MOD_ALT : 0;
- mods |= (modifierFlags & NSCommandKeyMask) ? PUGL_MOD_SUPER : 0;
- return mods;
-}
-
-static PuglKey
-keySymToSpecial(PuglView* view, NSEvent* ev)
-{
- NSString* chars = [ev charactersIgnoringModifiers];
- if([chars length] == 1) {
- switch ([chars characterAtIndex:0]) {
- case NSF1FunctionKey: return PUGL_KEY_F1;
- case NSF2FunctionKey: return PUGL_KEY_F2;
- case NSF3FunctionKey: return PUGL_KEY_F3;
- case NSF4FunctionKey: return PUGL_KEY_F4;
- case NSF5FunctionKey: return PUGL_KEY_F5;
- case NSF6FunctionKey: return PUGL_KEY_F6;
- case NSF7FunctionKey: return PUGL_KEY_F7;
- case NSF8FunctionKey: return PUGL_KEY_F8;
- case NSF9FunctionKey: return PUGL_KEY_F9;
- case NSF10FunctionKey: return PUGL_KEY_F10;
- case NSF11FunctionKey: return PUGL_KEY_F11;
- case NSF12FunctionKey: return PUGL_KEY_F12;
- case NSLeftArrowFunctionKey: return PUGL_KEY_LEFT;
- case NSUpArrowFunctionKey: return PUGL_KEY_UP;
- case NSRightArrowFunctionKey: return PUGL_KEY_RIGHT;
- case NSDownArrowFunctionKey: return PUGL_KEY_DOWN;
- case NSPageUpFunctionKey: return PUGL_KEY_PAGE_UP;
- case NSPageDownFunctionKey: return PUGL_KEY_PAGE_DOWN;
- case NSHomeFunctionKey: return PUGL_KEY_HOME;
- case NSEndFunctionKey: return PUGL_KEY_END;
- case NSInsertFunctionKey: return PUGL_KEY_INSERT;
- /* special keys (SHIFT, CTRL, ALT, SUPER) are handled in [flagsChanged] */
- }
- }
- return (PuglKey)0;
-}
-
--(void)updateTrackingAreas
-{
- if (trackingArea != nil) {
- [self removeTrackingArea:trackingArea];
- [trackingArea release];
- }
-
- const int opts = (NSTrackingMouseEnteredAndExited |
- NSTrackingMouseMoved |
- NSTrackingActiveAlways);
- trackingArea = [ [NSTrackingArea alloc] initWithRect:[self bounds]
- options:opts
- owner:self
- userInfo:nil];
- [self addTrackingArea:trackingArea];
-}
-
-- (NSPoint) eventLocation:(NSEvent*)event
-{
- return [self convertPoint:[event locationInWindow] fromView:nil];
-}
-
-- (void)mouseEntered:(NSEvent*)theEvent
-{
- [self updateTrackingAreas];
-}
-
-- (void)mouseExited:(NSEvent*)theEvent
-{
-}
-
-- (void) mouseMoved:(NSEvent*)event
-{
- const NSPoint wloc = [self eventLocation:event];
- const NSPoint rloc = [NSEvent mouseLocation];
- const PuglEventMotion ev = {
- PUGL_MOTION_NOTIFY,
- puglview,
- 0,
- [event timestamp],
- wloc.x,
- puglview->height - wloc.y,
- rloc.x,
- [[NSScreen mainScreen] frame].size.height - rloc.y,
- getModifiers(puglview, event),
- 0,
- 1
- };
- puglDispatchEvent(puglview, (PuglEvent*)&ev);
-}
-
-- (void) mouseDragged:(NSEvent*)event
-{
- [self mouseMoved: event];
-}
-
-- (void) rightMouseDragged:(NSEvent*)event
-{
- [self mouseMoved: event];
-}
-
-- (void) otherMouseDragged:(NSEvent*)event
-{
- [self mouseMoved: event];
-}
-
-- (void) mouseDown:(NSEvent*)event
-{
- const NSPoint wloc = [self eventLocation:event];
- const NSPoint rloc = [NSEvent mouseLocation];
- const PuglEventButton ev = {
- PUGL_BUTTON_PRESS,
- puglview,
- 0,
- [event timestamp],
- wloc.x,
- puglview->height - wloc.y,
- rloc.x,
- [[NSScreen mainScreen] frame].size.height - rloc.y,
- getModifiers(puglview, event),
- [event buttonNumber] + 1
- };
- puglDispatchEvent(puglview, (PuglEvent*)&ev);
-}
-
-- (void) mouseUp:(NSEvent*)event
-{
- const NSPoint wloc = [self eventLocation:event];
- const NSPoint rloc = [NSEvent mouseLocation];
- const PuglEventButton ev = {
- PUGL_BUTTON_RELEASE,
- puglview,
- 0,
- [event timestamp],
- wloc.x,
- puglview->height - wloc.y,
- rloc.x,
- [[NSScreen mainScreen] frame].size.height - rloc.y,
- getModifiers(puglview, event),
- [event buttonNumber] + 1
- };
- puglDispatchEvent(puglview, (PuglEvent*)&ev);
- [self updateTrackingAreas];
-}
-
-- (void) rightMouseDown:(NSEvent*)event
-{
- [self mouseDown: event];
-}
-
-- (void) rightMouseUp:(NSEvent*)event
-{
- [self mouseUp: event];
-}
-
-- (void) otherMouseDown:(NSEvent*)event
-{
- [self mouseDown: event];
-}
-
-- (void) otherMouseUp:(NSEvent*)event
-{
- [self mouseUp: event];
-}
-
-- (void) scrollWheel:(NSEvent*)event
-{
- [self updateTrackingAreas];
-
- const NSPoint wloc = [self eventLocation:event];
- const NSPoint rloc = [NSEvent mouseLocation];
- const PuglEventScroll ev = {
- PUGL_SCROLL,
- puglview,
- 0,
- [event timestamp],
- wloc.x,
- puglview->height - wloc.y,
- rloc.x,
- [[NSScreen mainScreen] frame].size.height - rloc.y,
- getModifiers(puglview, event),
- [event deltaX],
- [event deltaY]
- };
- puglDispatchEvent(puglview, (PuglEvent*)&ev);
- [self updateTrackingAreas];
-}
-
-- (void) keyDown:(NSEvent*)event
-{
- if (puglview->ignoreKeyRepeat && [event isARepeat]) {
- return;
- }
-
- const NSPoint wloc = [self eventLocation:event];
- const NSPoint rloc = [NSEvent mouseLocation];
- const NSString* chars = [event characters];
- const char* str = [chars UTF8String];
- PuglEventKey ev = {
- PUGL_KEY_PRESS,
- puglview,
- 0,
- [event timestamp],
- wloc.x,
- puglview->height - wloc.y,
- rloc.x,
- [[NSScreen mainScreen] frame].size.height - rloc.y,
- getModifiers(puglview, event),
- [event keyCode],
- puglDecodeUTF8((const uint8_t*)str),
- keySymToSpecial(puglview, event),
- { 0, 0, 0, 0, 0, 0, 0, 0 },
- false
- };
- strncpy((char*)ev.utf8, str, 8);
- puglDispatchEvent(puglview, (PuglEvent*)&ev);
-}
-
-- (void) keyUp:(NSEvent*)event
-{
- const NSPoint wloc = [self eventLocation:event];
- const NSPoint rloc = [NSEvent mouseLocation];
- const NSString* chars = [event characters];
- const char* str = [chars UTF8String];
- const PuglEventKey ev = {
- PUGL_KEY_RELEASE,
- puglview,
- 0,
- [event timestamp],
- wloc.x,
- puglview->height - wloc.y,
- rloc.x,
- [[NSScreen mainScreen] frame].size.height - rloc.y,
- getModifiers(puglview, event),
- [event keyCode],
- puglDecodeUTF8((const uint8_t*)str),
- keySymToSpecial(puglview, event),
- { 0, 0, 0, 0, 0, 0, 0, 0 },
- false,
- };
- strncpy((char*)ev.utf8, str, 8);
- puglDispatchEvent(puglview, (PuglEvent*)&ev);
-}
-
-- (void) flagsChanged:(NSEvent*)event
-{
- const unsigned mods = getModifiers(puglview, event);
- PuglEventType type = PUGL_NOTHING;
- PuglKey special = 0;
-
- if ((mods & PUGL_MOD_SHIFT) != (puglview->impl->mods & PUGL_MOD_SHIFT)) {
- type = mods & PUGL_MOD_SHIFT ? PUGL_KEY_PRESS : PUGL_KEY_RELEASE;
- special = PUGL_KEY_SHIFT;
- } else if ((mods & PUGL_MOD_CTRL) != (puglview->impl->mods & PUGL_MOD_CTRL)) {
- type = mods & PUGL_MOD_CTRL ? PUGL_KEY_PRESS : PUGL_KEY_RELEASE;
- special = PUGL_KEY_CTRL;
- } else if ((mods & PUGL_MOD_ALT) != (puglview->impl->mods & PUGL_MOD_ALT)) {
- type = mods & PUGL_MOD_ALT ? PUGL_KEY_PRESS : PUGL_KEY_RELEASE;
- special = PUGL_KEY_ALT;
- } else if ((mods & PUGL_MOD_SUPER) != (puglview->impl->mods & PUGL_MOD_SUPER)) {
- type = mods & PUGL_MOD_SUPER ? PUGL_KEY_PRESS : PUGL_KEY_RELEASE;
- special = PUGL_KEY_SUPER;
- }
-
- if (special != 0) {
- const NSPoint wloc = [self eventLocation:event];
- const NSPoint rloc = [NSEvent mouseLocation];
- PuglEventKey ev = {
- type,
- puglview,
- 0,
- [event timestamp],
- wloc.x,
- puglview->height - wloc.y,
- rloc.x,
- [[NSScreen mainScreen] frame].size.height - rloc.y,
- mods,
- [event keyCode],
- 0,
- special,
- { 0, 0, 0, 0, 0, 0, 0, 0 },
- false
- };
- puglDispatchEvent(puglview, (PuglEvent*)&ev);
- }
-
- puglview->impl->mods = mods;
-}
-
-@end
-
-PuglInternals*
-puglInitInternals(void)
-{
- return (PuglInternals*)calloc(1, sizeof(PuglInternals));
-}
-
-void
-puglEnterContext(PuglView* view)
-{
- [[view->impl->glview openGLContext] makeCurrentContext];
-}
-
-void
-puglLeaveContext(PuglView* view, bool flush)
-{
-#ifdef PUGL_HAVE_CAIRO
- if (view->ctx_type & PUGL_CAIRO) {
- pugl_cairo_gl_draw(&view->impl->cairo_gl, view->width, view->height);
- }
-#endif
-
- if (flush) {
- //[[view->impl->glview openGLContext] flushBuffer];
- glFlush();
- glSwapAPPLE();
- }
- [NSOpenGLContext clearCurrentContext];
-}
-
-int
-puglCreateWindow(PuglView* view, const char* title)
-{
- PuglInternals* impl = view->impl;
-
- [NSAutoreleasePool new];
- impl->app = [NSApplication sharedApplication];
-
- impl->glview = [PuglOpenGLView new];
- impl->glview->puglview = view;
-
- if (view->transient_parent) {
- NSView* pview = (NSView*)view->transient_parent;
- [pview addSubview:impl->glview];
- [impl->glview setHidden:NO];
- } else {
- NSString* titleString = [[NSString alloc]
- initWithBytes:title
- length:strlen(title)
- encoding:NSUTF8StringEncoding];
-
- id window = [[PuglWindow new] retain];
- [window setPuglview:view];
- [window setTitle:titleString];
- if (view->min_width || view->min_height) {
- [window setContentMinSize:NSMakeSize(view->min_width,
- view->min_height)];
- }
- impl->window = window;
-
- [window setContentView:impl->glview];
- [impl->app activateIgnoringOtherApps:YES];
- [window makeFirstResponder:impl->glview];
- [window makeKeyAndOrderFront:window];
-#if 0
- if (resizable) {
- [impl->glview setAutoresizingMask:NSViewWidthSizable|NSViewHeightSizable];
- }
-#endif
- }
-
- return 0;
-}
-
-void
-puglShowWindow(PuglView* view)
-{
- [view->impl->window setIsVisible:YES];
- view->visible = true;
-}
-
-void
-puglHideWindow(PuglView* view)
-{
- [view->impl->window setIsVisible:NO];
- view->visible = false;
-}
-
-void
-puglDestroy(PuglView* view)
-{
-#ifdef PUGL_HAVE_CAIRO
- pugl_cairo_gl_free(&view->impl->cairo_gl);
-#endif
- view->impl->glview->puglview = NULL;
- [view->impl->glview removeFromSuperview];
- if (view->impl->window) {
- [view->impl->window close];
- }
- [view->impl->glview release];
- if (view->impl->window) {
- [view->impl->window release];
- }
- puglClearSelection(view);
- free(view->windowClass);
- free(view->impl);
- free(view);
-}
-
-void
-puglCopyToClipboard(PuglView* view, const char* selection, size_t len)
-{
- PuglInternals* const impl = view->impl;
-
- puglSetSelection(view, selection, len);
-
- NSPasteboard* pasteboard = [NSPasteboard generalPasteboard];
- if(pasteboard) {
- [pasteboard
- declareTypes:[NSArray arrayWithObjects:NSStringPboardType, nil]
- owner:nil];
- [pasteboard
- setString:[NSString stringWithUTF8String:puglGetSelection(view, NULL)]
- forType:NSStringPboardType];
- }
-}
-
-const char*
-puglPasteFromClipboard(PuglView* view, size_t* len)
-{
- PuglInternals* const impl = view->impl;
-
- NSPasteboard* pasteboard = [NSPasteboard generalPasteboard];
- if(pasteboard && [ [pasteboard types] containsObject:NSStringPboardType] ) {
- const NSString* mem = [pasteboard stringForType:NSStringPboardType];
- if(mem) {
- const char *utf8 = [mem UTF8String];
- if(utf8)
- puglSetSelection(view, utf8, strlen(utf8));
- }
- }
-
- return puglGetSelection(view, len);
-}
-
-void
-puglGrabFocus(PuglView* view)
-{
- // TODO
-}
-
-PuglStatus
-puglWaitForEvent(PuglView* view)
-{
- /* OSX supposedly has queue: and untilDate: selectors that can be used for
- a blocking non-queueing event check, but if used here cause an
- unsupported selector error at runtime. I have no idea why, so just get
- the event and keep it around until the call to puglProcessEvents. */
- if (!view->impl->nextEvent) {
- view->impl->nextEvent = [view->impl->window
- nextEventMatchingMask: NSAnyEventMask];
- }
-
- return PUGL_SUCCESS;
-}
-
-PuglStatus
-puglProcessEvents(PuglView* view)
-{
- while (true) {
- // Get the next event, or use the cached one from puglWaitForEvent
- if (!view->impl->nextEvent) {
- view->impl->nextEvent = [view->impl->window
- nextEventMatchingMask: NSAnyEventMask];
- }
-
- if (!view->impl->nextEvent) {
- break; // No events to process, done
- }
-
- // Dispatch event
- [view->impl->app sendEvent: view->impl->nextEvent];
- view->impl->nextEvent = NULL;
- }
-
- return PUGL_SUCCESS;
-}
-
-void
-puglPostRedisplay(PuglView* view)
-{
- //view->redisplay = true; // unused
- [view->impl->glview setNeedsDisplay: YES];
-}
-
-PuglNativeWindow
-puglGetNativeWindow(PuglView* view)
-{
- return (PuglNativeWindow)view->impl->glview;
-}
-
-void*
-puglGetContext(PuglView* view)
-{
-#ifdef PUGL_HAVE_CAIRO
- if (view->ctx_type & PUGL_CAIRO) {
- return view->impl->cr;
- }
-#endif
- return NULL;
-}
diff --git a/pugl/pugl/pugl_win.cpp b/pugl/pugl/pugl_win.cpp
deleted file mode 100644
index 6468963f..00000000
--- a/pugl/pugl/pugl_win.cpp
+++ /dev/null
@@ -1,704 +0,0 @@
-/*
- Copyright 2012-2015 David Robillard <http://drobilla.net>
-
- Permission to use, copy, modify, and/or distribute this software for any
- purpose with or without fee is hereby granted, provided that the above
- copyright notice and this permission notice appear in all copies.
-
- THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-*/
-
-/**
- @file pugl_win.cpp Windows/WGL Pugl Implementation.
-*/
-
-#include <windows.h>
-#include <windowsx.h>
-#include <GL/gl.h>
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <wctype.h>
-
-#include "pugl/pugl_internal.h"
-
-#ifndef WM_MOUSEWHEEL
-# define WM_MOUSEWHEEL 0x020A
-#endif
-#ifndef WM_MOUSEHWHEEL
-# define WM_MOUSEHWHEEL 0x020E
-#endif
-#ifndef WHEEL_DELTA
-# define WHEEL_DELTA 120
-#endif
-#ifdef _WIN64
-# ifndef GWLP_USERDATA
-# define GWLP_USERDATA (-21)
-# endif
-#else
-# ifndef GWL_USERDATA
-# define GWL_USERDATA (-21)
-# endif
-#endif
-
-#define PUGL_LOCAL_CLOSE_MSG (WM_USER + 50)
-
-struct PuglInternalsImpl {
- HWND hwnd;
- HDC hdc;
- HGLRC hglrc;
- WNDCLASS wc;
-};
-
-LRESULT CALLBACK
-wndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
-
-PuglView*
-puglInit()
-{
- PuglView* view = (PuglView*)calloc(1, sizeof(PuglView));
- PuglInternals* impl = (PuglInternals*)calloc(1, sizeof(PuglInternals));
- if (!view || !impl) {
- return NULL;
- }
-
- view->impl = impl;
- view->width = 640;
- view->height = 480;
-
- return view;
-}
-
-PuglInternals*
-puglInitInternals(void)
-{
- return (PuglInternals*)calloc(1, sizeof(PuglInternals));
-}
-
-void
-puglEnterContext(PuglView* view)
-{
- PAINTSTRUCT ps;
- BeginPaint(view->impl->hwnd, &ps);
-
-#ifdef PUGL_HAVE_GL
- if (view->ctx_type == PUGL_GL) {
- wglMakeCurrent(view->impl->hdc, view->impl->hglrc);
- }
-#endif
-}
-
-void
-puglLeaveContext(PuglView* view, bool flush)
-{
-#ifdef PUGL_HAVE_GL
- if (view->ctx_type == PUGL_GL && flush) {
- glFlush();
- SwapBuffers(view->impl->hdc);
- }
-#endif
-
- PAINTSTRUCT ps;
- EndPaint(view->impl->hwnd, &ps);
-}
-
-int
-puglCreateWindow(PuglView* view, const char* title)
-{
- static const TCHAR* DEFAULT_CLASSNAME = "Pugl";
-
- PuglInternals* impl = view->impl;
-
- if (!title) {
- title = "Window";
- }
-
- WNDCLASSEX wc;
- memset(&wc, 0, sizeof(wc));
- wc.cbSize = sizeof(wc);
- wc.style = CS_OWNDC;
- wc.lpfnWndProc = wndProc;
- wc.hInstance = GetModuleHandle(NULL);
- wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); // TODO: user-specified icon
- wc.hCursor = LoadCursor(NULL, IDC_ARROW);
- wc.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
- wc.lpszClassName = view->windowClass ? view->windowClass : DEFAULT_CLASSNAME;
- if (!RegisterClassEx(&wc)) {
- free((void*)impl->wc.lpszClassName);
- free(impl);
- free(view);
- return NULL;
- }
-
- int winFlags = WS_POPUPWINDOW | WS_CAPTION;
- if (view->resizable) {
- winFlags |= WS_SIZEBOX;
- if (view->min_width || view->min_height) {
- // Adjust the minimum window size to accomodate requested view size
- RECT mr = { 0, 0, view->min_width, view->min_height };
- AdjustWindowRectEx(&mr, winFlags, FALSE, WS_EX_TOPMOST);
- view->min_width = mr.right - mr.left;
- view->min_height = mr.bottom - mr.top;
- }
- }
-
- // Adjust the window size to accomodate requested view size
- RECT wr = { 0, 0, view->width, view->height };
- AdjustWindowRectEx(&wr, winFlags, FALSE, WS_EX_TOPMOST);
-
- impl->hwnd = CreateWindowEx(
- WS_EX_TOPMOST,
- wc.lpszClassName, title,
- (view->parent ? WS_CHILD : winFlags),
- CW_USEDEFAULT, CW_USEDEFAULT, wr.right-wr.left, wr.bottom-wr.top,
- (HWND)view->parent, NULL, NULL, NULL);
-
- if (!impl->hwnd) {
- free((void*)impl->wc.lpszClassName);
- free(impl);
- free(view);
- return 1;
- }
-
-#ifdef _WIN64
- SetWindowLongPtr(impl->hwnd, GWLP_USERDATA, (LONG_PTR)view);
-#else
- SetWindowLongPtr(impl->hwnd, GWL_USERDATA, (LONG)view);
-#endif
-
- impl->hdc = GetDC(impl->hwnd);
-
- PIXELFORMATDESCRIPTOR pfd;
- ZeroMemory(&pfd, sizeof(pfd));
- pfd.nSize = sizeof(pfd);
- pfd.nVersion = 1;
- pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
- pfd.iPixelType = PFD_TYPE_RGBA;
- pfd.cColorBits = 24;
- pfd.cDepthBits = 16;
- pfd.iLayerType = PFD_MAIN_PLANE;
-
- int format = ChoosePixelFormat(impl->hdc, &pfd);
- SetPixelFormat(impl->hdc, format, &pfd);
-
- impl->hglrc = wglCreateContext(impl->hdc);
- if (!impl->hglrc) {
- ReleaseDC(impl->hwnd, impl->hdc);
- DestroyWindow(impl->hwnd);
- UnregisterClass(impl->wc.lpszClassName, NULL);
- free((void*)impl->wc.lpszClassName);
- free(impl);
- free(view);
- return NULL;
- }
- wglMakeCurrent(impl->hdc, impl->hglrc);
-
- return 0;
-}
-
-void
-puglShowWindow(PuglView* view)
-{
- PuglInternals* impl = view->impl;
-
- ShowWindow(impl->hwnd, SW_SHOWNORMAL);
- view->visible = true;
-}
-
-void
-puglHideWindow(PuglView* view)
-{
- PuglInternals* impl = view->impl;
-
- ShowWindow(impl->hwnd, SW_HIDE);
- view->visible = false;
-}
-
-void
-puglDestroy(PuglView* view)
-{
- wglMakeCurrent(NULL, NULL);
- wglDeleteContext(view->impl->hglrc);
- ReleaseDC(view->impl->hwnd, view->impl->hdc);
- DestroyWindow(view->impl->hwnd);
- UnregisterClass(view->impl->wc.lpszClassName, NULL);
- puglClearSelection(view);
- free(view->windowClass);
- free(view->impl);
- free(view);
-}
-
-static PuglKey
-keySymToSpecial(int sym)
-{
- switch (sym) {
- case VK_F1: return PUGL_KEY_F1;
- case VK_F2: return PUGL_KEY_F2;
- case VK_F3: return PUGL_KEY_F3;
- case VK_F4: return PUGL_KEY_F4;
- case VK_F5: return PUGL_KEY_F5;
- case VK_F6: return PUGL_KEY_F6;
- case VK_F7: return PUGL_KEY_F7;
- case VK_F8: return PUGL_KEY_F8;
- case VK_F9: return PUGL_KEY_F9;
- case VK_F10: return PUGL_KEY_F10;
- case VK_F11: return PUGL_KEY_F11;
- case VK_F12: return PUGL_KEY_F12;
- case VK_LEFT: return PUGL_KEY_LEFT;
- case VK_UP: return PUGL_KEY_UP;
- case VK_RIGHT: return PUGL_KEY_RIGHT;
- case VK_DOWN: return PUGL_KEY_DOWN;
- case VK_PRIOR: return PUGL_KEY_PAGE_UP;
- case VK_NEXT: return PUGL_KEY_PAGE_DOWN;
- case VK_HOME: return PUGL_KEY_HOME;
- case VK_END: return PUGL_KEY_END;
- case VK_INSERT: return PUGL_KEY_INSERT;
- case VK_SHIFT: return PUGL_KEY_SHIFT;
- case VK_CONTROL: return PUGL_KEY_CTRL;
- case VK_MENU: return PUGL_KEY_ALT;
- case VK_LWIN: return PUGL_KEY_SUPER;
- case VK_RWIN: return PUGL_KEY_SUPER;
- }
- return (PuglKey)0;
-}
-
-static unsigned int
-getModifiers()
-{
- unsigned int mods = 0;
- mods |= (GetKeyState(VK_SHIFT) < 0) ? PUGL_MOD_SHIFT : 0;
- mods |= (GetKeyState(VK_CONTROL) < 0) ? PUGL_MOD_CTRL : 0;
- mods |= (GetKeyState(VK_MENU) < 0) ? PUGL_MOD_ALT : 0;
- mods |= (GetKeyState(VK_LWIN) < 0) ? PUGL_MOD_SUPER : 0;
- mods |= (GetKeyState(VK_RWIN) < 0) ? PUGL_MOD_SUPER : 0;
- return mods;
-}
-
-static void
-initMouseEvent(PuglEvent* event,
- PuglView* view,
- int button,
- bool press,
- LPARAM lParam)
-{
- POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
- ClientToScreen(view->impl->hwnd, &pt);
-
- if (press) {
- SetCapture(view->impl->hwnd);
- } else {
- ReleaseCapture();
- }
-
- event->button.time = GetMessageTime();
- event->button.type = press ? PUGL_BUTTON_PRESS : PUGL_BUTTON_RELEASE;
- event->button.x = GET_X_LPARAM(lParam);
- event->button.y = GET_Y_LPARAM(lParam);
- event->button.x_root = pt.x;
- event->button.y_root = pt.y;
- event->button.state = getModifiers();
- event->button.button = button;
-}
-
-static void
-initScrollEvent(PuglEvent* event, PuglView* view, LPARAM lParam, WPARAM wParam)
-{
- POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
- ScreenToClient(view->impl->hwnd, &pt);
-
- event->scroll.time = GetMessageTime();
- event->scroll.type = PUGL_SCROLL;
- event->scroll.x = pt.x;
- event->scroll.y = pt.y;
- event->scroll.x_root = GET_X_LPARAM(lParam);
- event->scroll.y_root = GET_Y_LPARAM(lParam);
- event->scroll.state = getModifiers();
- event->scroll.dx = 0;
- event->scroll.dy = 0;
-}
-
-static unsigned int
-utf16_to_code_point(const wchar_t* input, size_t input_size)
-{
- unsigned int code_unit = *input;
- // Equiv. range check between 0xD800 to 0xDBFF inclusive
- if ((code_unit & 0xFC00) == 0xD800) {
- if (input_size < 2) {
- // "Error: is surrogate but input_size too small"
- return 0xFFFD; // replacement character
- }
-
- unsigned int code_unit_2 = *++input;
- // Equiv. range check between 0xDC00 to 0xDFFF inclusive
- if ((code_unit_2 & 0xFC00) == 0xDC00) {
- return (code_unit << 10) + code_unit_2 - 0x35FDC00;
- }
-
- // TODO: push_back(code_unit_2);
- // "Error: Unpaired surrogates."
- return 0xFFFD; // replacement character
- }
- return code_unit;
-}
-
-static void
-initKeyEvent(PuglEvent* event, PuglView* view, bool press, LPARAM lParam)
-{
- POINT rpos = { 0, 0 };
- GetCursorPos(&rpos);
-
- POINT cpos = { rpos.x, rpos.y };
- ScreenToClient(view->impl->hwnd, &rpos);
-
- event->key.type = press ? PUGL_KEY_PRESS : PUGL_KEY_RELEASE;
- event->key.time = GetMessageTime();
- event->key.state = getModifiers();
- event->key.x_root = rpos.x;
- event->key.y_root = rpos.y;
- event->key.x = cpos.x;
- event->key.y = cpos.y;
- event->key.keycode = (lParam & 0xFF0000) >> 16;
- event->key.character = 0;
- event->key.special = static_cast<PuglKey>(0);
- event->key.filter = 0;
-}
-
-static void
-wcharBufToEvent(wchar_t* buf, int n, PuglEvent* event)
-{
- if (n > 0) {
- char* charp = reinterpret_cast<char*>(event->key.utf8);
- if (!WideCharToMultiByte(CP_UTF8, 0, buf, n,
- charp, 8, NULL, NULL)) {
- /* error: could not convert to utf-8,
- GetLastError has details */
- memset(event->key.utf8, 0, 8);
- // replacement character
- event->key.utf8[0] = 0xEF;
- event->key.utf8[1] = 0xBF;
- event->key.utf8[2] = 0xBD;
- }
-
- event->key.character = utf16_to_code_point(buf, n);
- } else {
- // replacement character
- event->key.utf8[0] = 0xEF;
- event->key.utf8[1] = 0xBF;
- event->key.utf8[2] = 0xBD;
- event->key.character = 0xFFFD;
- }
-}
-
-static void
-translateMessageParamsToEvent(LPARAM lParam, WPARAM wParam, PuglEvent* event)
-{
- /* TODO: This is a kludge. Would be nice to use ToUnicode here, but this
- breaks composed keys because it messes with the keyboard state. Not
- sure how to correctly handle this on Windows. */
-
- // This is how I really want to do this, but it breaks composed keys (é,
- // è, ü, ö, and so on) because ToUnicode messes with the keyboard state.
-
- //wchar_t buf[5];
- //BYTE keyboard_state[256];
- //int wcharCount = 0;
- //GetKeyboardState(keyboard_state);
- //wcharCount = ToUnicode(wParam, MapVirtualKey(wParam, MAPVK_VK_TO_VSC),
- // keyboard_state, buf, 4, 0);
- //wcharBufToEvent(buf, wcharCount, event);
-
- // So, since Google refuses to give me a better solution, and if no one
- // else has a better solution, I will make a hack...
- wchar_t buf[5] = { 0, 0, 0, 0, 0 };
- UINT c = MapVirtualKey(wParam, MAPVK_VK_TO_CHAR);
- buf[0] = c & 0xffff;
- // TODO: This does not take caps lock into account
- // TODO: Dead keys should affect key releases as well
- if (!(event->key.state && PUGL_MOD_SHIFT))
- buf[0] = towlower(buf[0]);
- wcharBufToEvent(buf, 1, event);
- event->key.filter = ((c >> 31) & 0x1);
-}
-
-static void
-translateCharEventToEvent(WPARAM wParam, PuglEvent* event)
-{
- wchar_t buf[2];
- int wcharCount;
- if (wParam & 0xFFFF0000) {
- wcharCount = 2;
- buf[0] = (wParam & 0xFFFF);
- buf[1] = ((wParam >> 16) & 0xFFFF);
- } else {
- wcharCount = 1;
- buf[0] = (wParam & 0xFFFF);
- }
- wcharBufToEvent(buf, wcharCount, event);
-}
-
-static bool
-ignoreKeyEvent(PuglView* view, LPARAM lParam)
-{
- return view->ignoreKeyRepeat && (lParam & (1 << 30));
-}
-
-static LRESULT
-handleMessage(PuglView* view, UINT message, WPARAM wParam, LPARAM lParam)
-{
- PuglEvent event;
- void* dummy_ptr = NULL;
- RECT rect;
- MINMAXINFO* mmi;
- POINT pt;
- bool dispatchThisEvent = true;
-
- memset(&event, 0, sizeof(event));
-
- event.any.type = PUGL_NOTHING;
- event.any.view = view;
- if (InSendMessageEx(dummy_ptr)) {
- event.any.flags |= PUGL_IS_SEND_EVENT;
- }
-
- switch (message) {
- case WM_CREATE:
- case WM_SHOWWINDOW:
- case WM_SIZE:
- GetWindowRect(view->impl->hwnd, &rect);
- event.configure.type = PUGL_CONFIGURE;
- event.configure.x = rect.left;
- event.configure.y = rect.top;
- view->width = rect.right - rect.left;
- view->height = rect.bottom - rect.top;
- event.configure.width = view->width;
- event.configure.height = view->height;
- break;
- case WM_GETMINMAXINFO:
- mmi = (MINMAXINFO*)lParam;
- mmi->ptMinTrackSize.x = view->min_width;
- mmi->ptMinTrackSize.y = view->min_height;
- break;
- case WM_PAINT:
- GetUpdateRect(view->impl->hwnd, &rect, false);
- event.expose.type = PUGL_EXPOSE;
- event.expose.x = rect.left;
- event.expose.y = rect.top;
- event.expose.width = rect.right - rect.left;
- event.expose.height = rect.bottom - rect.top;
- event.expose.count = 0;
- break;
- case WM_MOUSEMOVE:
- pt.x = GET_X_LPARAM(lParam);
- pt.y = GET_Y_LPARAM(lParam);
- ClientToScreen(view->impl->hwnd, &pt);
-
- event.motion.type = PUGL_MOTION_NOTIFY;
- event.motion.time = GetMessageTime();
- event.motion.x = GET_X_LPARAM(lParam);
- event.motion.y = GET_Y_LPARAM(lParam);
- event.motion.x_root = pt.x;
- event.motion.y_root = pt.y;
- event.motion.state = getModifiers();
- event.motion.is_hint = false;
- break;
- case WM_LBUTTONDOWN:
- initMouseEvent(&event, view, 1, true, lParam);
- break;
- case WM_MBUTTONDOWN:
- initMouseEvent(&event, view, 2, true, lParam);
- break;
- case WM_RBUTTONDOWN:
- initMouseEvent(&event, view, 3, true, lParam);
- break;
- case WM_LBUTTONUP:
- initMouseEvent(&event, view, 1, false, lParam);
- break;
- case WM_MBUTTONUP:
- initMouseEvent(&event, view, 2, false, lParam);
- break;
- case WM_RBUTTONUP:
- initMouseEvent(&event, view, 3, false, lParam);
- break;
- case WM_MOUSEWHEEL:
- initScrollEvent(&event, view, lParam, wParam);
- event.scroll.dy = GET_WHEEL_DELTA_WPARAM(wParam) / (float)WHEEL_DELTA;
- break;
- case WM_MOUSEHWHEEL:
- initScrollEvent(&event, view, lParam, wParam);
- event.scroll.dx = GET_WHEEL_DELTA_WPARAM(wParam) / (float)WHEEL_DELTA;
- break;
- case WM_KEYDOWN:
- if (!ignoreKeyEvent(view, lParam)) {
- initKeyEvent(&event, view, true, lParam);
- if (!(event.key.special = keySymToSpecial(wParam))) {
- event.key.type = PUGL_NOTHING;
- }
- }
- break;
- case WM_CHAR:
- if (!ignoreKeyEvent(view, lParam)) {
- initKeyEvent(&event, view, true, lParam);
- translateCharEventToEvent(wParam, &event);
- }
- break;
- case WM_DEADCHAR:
- if (!ignoreKeyEvent(view, lParam)) {
- initKeyEvent(&event, view, true, lParam);
- translateCharEventToEvent(wParam, &event);
- event.key.filter = 1;
- }
- break;
- case WM_KEYUP:
- initKeyEvent(&event, view, false, lParam);
- if (!(event.key.special = keySymToSpecial(wParam))) {
- translateMessageParamsToEvent(lParam, wParam, &event);
- }
- break;
- case WM_QUIT:
- case PUGL_LOCAL_CLOSE_MSG:
- event.close.type = PUGL_CLOSE;
- break;
- default:
- return DefWindowProc(
- view->impl->hwnd, message, wParam, lParam);
- }
-
- puglDispatchEvent(view, &event);
-
- return 0;
-}
-
-void
-puglCopyToClipboard(PuglView* view, const char* selection, size_t len)
-{
- PuglInternals* const impl = view->impl;
-
- puglSetSelection(view, selection, len);
-
- if(OpenClipboard(impl->hwnd)) {
- const int wsize = MultiByteToWideChar(CP_UTF8, 0, selection, len, NULL, 0);
- if(wsize) {
- HGLOBAL mem = GlobalAlloc(GMEM_MOVEABLE, (wsize + 1) * sizeof(wchar_t));
- if(mem) {
- wchar_t* wstr = (wchar_t*)GlobalLock(mem);
- if(wstr) {
- MultiByteToWideChar(CP_UTF8, 0, selection, len, wstr, wsize);
- wstr[wsize] = 0;
- GlobalUnlock(mem);
- SetClipboardData(CF_UNICODETEXT, mem);
- }
- else {
- GlobalFree(mem);
- }
- }
- }
- CloseClipboard();
- }
-}
-
-const char*
-puglPasteFromClipboard(PuglView* view, size_t* len)
-{
- PuglInternals* const impl = view->impl;
-
- if(IsClipboardFormatAvailable(CF_UNICODETEXT) && OpenClipboard(impl->hwnd)) {
- HGLOBAL mem = GetClipboardData(CF_UNICODETEXT);
- if(mem) {
- const int wsize = GlobalSize(mem) / sizeof(wchar_t) - 1;
- if(wsize) {
- wchar_t* wstr = (wchar_t*)GlobalLock(mem);
- if(wstr) {
- const int utf8size = WideCharToMultiByte(CP_UTF8, 0, wstr, wsize, NULL, 0, NULL, NULL);
- if(utf8size) {
- char* utf8 = (char*)malloc(utf8size);
- if(utf8) {
- WideCharToMultiByte(CP_UTF8, 0, wstr, wsize, utf8, utf8size, NULL, NULL);
- puglSetSelection(view, utf8, utf8size);
- free(utf8);
- }
- }
- GlobalUnlock(mem);
- }
- }
- }
- CloseClipboard();
- }
-
- return puglGetSelection(view, len);
-}
-
-void
-puglGrabFocus(PuglView* view)
-{
- // TODO
-}
-
-PuglStatus
-puglWaitForEvent(PuglView* view)
-{
- WaitMessage();
- return PUGL_SUCCESS;
-}
-
-PuglStatus
-puglProcessEvents(PuglView* view)
-{
- MSG msg;
- while (PeekMessage(&msg, view->impl->hwnd, 0, 0, PM_REMOVE)) {
- TranslateMessage(&msg);
- handleMessage(view, msg.message, msg.wParam, msg.lParam);
- }
-
- if (view->redisplay) {
- InvalidateRect(view->impl->hwnd, NULL, FALSE);
- view->redisplay = false;
- }
-
- return PUGL_SUCCESS;
-}
-
-LRESULT CALLBACK
-wndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
-{
-#ifdef _WIN64
- PuglView* view = (PuglView*)GetWindowLongPtr(hwnd, GWLP_USERDATA);
-#else
- PuglView* view = (PuglView*)GetWindowLongPtr(hwnd, GWL_USERDATA);
-#endif
-
- switch (message) {
- case WM_CREATE:
- PostMessage(hwnd, WM_SHOWWINDOW, TRUE, 0);
- return 0;
- case WM_CLOSE:
- PostMessage(hwnd, PUGL_LOCAL_CLOSE_MSG, wParam, lParam);
- return 0;
- case WM_DESTROY:
- return 0;
- default:
- if (view && hwnd == view->impl->hwnd) {
- return handleMessage(view, message, wParam, lParam);
- } else {
- return DefWindowProc(hwnd, message, wParam, lParam);
- }
- }
-}
-
-void
-puglPostRedisplay(PuglView* view)
-{
- view->redisplay = true;
-}
-
-PuglNativeWindow
-puglGetNativeWindow(PuglView* view)
-{
- return (PuglNativeWindow)view->impl->hwnd;
-}
diff --git a/pugl/pugl/pugl_x11.c b/pugl/pugl/pugl_x11.c
deleted file mode 100644
index 5afc880a..00000000
--- a/pugl/pugl/pugl_x11.c
+++ /dev/null
@@ -1,810 +0,0 @@
-/*
- Copyright 2012-2016 David Robillard <http://drobilla.net>
- Copyright 2013 Robin Gareus <robin@gareus.org>
- Copyright 2011-2012 Ben Loftis, Harrison Consoles
-
- Permission to use, copy, modify, and/or distribute this software for any
- purpose with or without fee is hereby granted, provided that the above
- copyright notice and this permission notice appear in all copies.
-
- THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-*/
-
-/**
- @file pugl_x11.c X11 Pugl Implementation.
-*/
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <limits.h>
-
-#include <X11/Xatom.h>
-#include <X11/Xlib.h>
-#include <X11/Xutil.h>
-#include <X11/keysym.h>
-
-#ifdef PUGL_HAVE_GL
-#include <GL/gl.h>
-#include <GL/glx.h>
-#endif
-
-#ifdef PUGL_HAVE_CAIRO
-#include <cairo/cairo.h>
-#include <cairo/cairo-xlib.h>
-#endif
-
-#include "pugl/cairo_gl.h"
-#include "pugl/pugl_internal.h"
-
-#ifndef MIN
-# define MIN(a, b) (((a) < (b)) ? (a) : (b))
-#endif
-
-#ifndef MAX
-# define MAX(a, b) (((a) > (b)) ? (a) : (b))
-#endif
-
-#ifdef PUGL_HAVE_GL
-
-/** Attributes for double-buffered RGBA. */
-static int attrListDbl[] = {
- GLX_RGBA,
- GLX_DOUBLEBUFFER , True,
- GLX_RED_SIZE , 4,
- GLX_GREEN_SIZE , 4,
- GLX_BLUE_SIZE , 4,
- GLX_DEPTH_SIZE , 8,
- GLX_STENCIL_SIZE , 8,
- /* GLX_SAMPLE_BUFFERS , 1, */
- /* GLX_SAMPLES , 4, */
- None
-};
-
-/** Attributes for single-buffered RGBA. */
-static int attrListSgl[] = {
- GLX_RGBA,
- GLX_DOUBLEBUFFER , False,
- GLX_RED_SIZE , 4,
- GLX_GREEN_SIZE , 4,
- GLX_BLUE_SIZE , 4,
- GLX_DEPTH_SIZE , 8,
- GLX_STENCIL_SIZE , 8,
- /* GLX_SAMPLE_BUFFERS , 1, */
- /* GLX_SAMPLES , 4, */
- None
-};
-
-/** Null-terminated list of attributes in order of preference. */
-static int* attrLists[] = { attrListDbl, attrListSgl, NULL };
-
-#endif // PUGL_HAVE_GL
-
-struct PuglInternalsImpl {
- Display* display;
- int screen;
- Window win;
- XIM xim;
- XIC xic;
-#ifdef PUGL_HAVE_CAIRO
- cairo_surface_t* surface;
- cairo_t* cr;
-#endif
-#ifdef PUGL_HAVE_GL
- GLXContext ctx;
- int doubleBuffered;
-#endif
-#if defined(PUGL_HAVE_CAIRO) && defined(PUGL_HAVE_GL)
- PuglCairoGL cairo_gl;
-#endif
- Atom clipboard;
- Atom utf8_string;
-};
-
-PuglInternals*
-puglInitInternals(void)
-{
- return (PuglInternals*)calloc(1, sizeof(PuglInternals));
-}
-
-static XVisualInfo*
-getVisual(PuglView* view)
-{
- PuglInternals* const impl = view->impl;
- XVisualInfo* vi = NULL;
-
-#ifdef PUGL_HAVE_GL
- if (view->ctx_type & PUGL_GL) {
- for (int* attr = *attrLists; !vi && *attr; ++attr) {
- vi = glXChooseVisual(impl->display, impl->screen, attr);
- }
- }
-#endif
-#ifdef PUGL_HAVE_CAIRO
- if (view->ctx_type == PUGL_CAIRO) {
- XVisualInfo pat;
- int n;
- pat.screen = impl->screen;
- vi = XGetVisualInfo(impl->display, VisualScreenMask, &pat, &n);
- }
-#endif
-
- return vi;
-}
-
-#ifdef PUGL_HAVE_CAIRO
-static int
-createCairoContext(PuglView* view)
-{
- PuglInternals* const impl = view->impl;
-
- if (impl->cr) {
- cairo_destroy(impl->cr);
- }
-
- impl->cr = cairo_create(impl->surface);
- return cairo_status(impl->cr);
-}
-#endif
-
-static bool
-createContext(PuglView* view, XVisualInfo* vi)
-{
- PuglInternals* const impl = view->impl;
-
-#ifdef PUGL_HAVE_GL
- if (view->ctx_type & PUGL_GL) {
- impl->ctx = glXCreateContext(impl->display, vi, 0, GL_TRUE);
- glXGetConfig(impl->display, vi, GLX_DOUBLEBUFFER, &impl->doubleBuffered);
- }
-#endif
-#ifdef PUGL_HAVE_CAIRO
- if (view->ctx_type == PUGL_CAIRO) {
- impl->surface = cairo_xlib_surface_create(
- impl->display, impl->win, vi->visual, view->width, view->height);
- }
-#endif
-#if defined(PUGL_HAVE_GL) && defined(PUGL_HAVE_CAIRO)
- if (view->ctx_type == PUGL_CAIRO_GL) {
- impl->surface = pugl_cairo_gl_create(
- &impl->cairo_gl, view->width, view->height, 4);
- }
-#endif
-
-#ifdef PUGL_HAVE_CAIRO
- if (view->ctx_type & PUGL_CAIRO) {
- if (cairo_surface_status(impl->surface) != CAIRO_STATUS_SUCCESS) {
- fprintf(stderr, "error: failed to create cairo surface\n");
- return false;
- }
-
- if (createCairoContext(view) != CAIRO_STATUS_SUCCESS) {
- cairo_surface_destroy(impl->surface);
- fprintf(stderr, "error: failed to create cairo context\n");
- return false;
- }
- }
-#endif
-
- return true;
-}
-
-static void
-destroyContext(PuglView* view)
-{
-#if defined(PUGL_HAVE_CAIRO) && defined(PUGL_HAVE_GL)
- if (view->ctx_type == PUGL_CAIRO_GL) {
- pugl_cairo_gl_free(&view->impl->cairo_gl);
- }
-#endif
-#ifdef PUGL_HAVE_GL
- if (view->ctx_type & PUGL_GL) {
- glXDestroyContext(view->impl->display, view->impl->ctx);
- }
-#endif
-#ifdef PUGL_HAVE_CAIRO
- if (view->ctx_type & PUGL_CAIRO) {
- cairo_destroy(view->impl->cr);
- cairo_surface_destroy(view->impl->surface);
- }
-#endif
-}
-
-void
-puglEnterContext(PuglView* view)
-{
-#ifdef PUGL_HAVE_GL
- if (view->ctx_type & PUGL_GL) {
- glXMakeCurrent(view->impl->display, view->impl->win, view->impl->ctx);
- }
-#endif
-}
-
-void
-puglLeaveContext(PuglView* view, bool flush)
-{
-#ifdef PUGL_HAVE_GL
- if (flush && view->ctx_type & PUGL_GL) {
-#ifdef PUGL_HAVE_CAIRO
- if (view->ctx_type == PUGL_CAIRO_GL) {
- pugl_cairo_gl_draw(&view->impl->cairo_gl, view->width, view->height);
- }
-#endif
-
- glFlush();
- if (view->impl->doubleBuffered) {
- glXSwapBuffers(view->impl->display, view->impl->win);
- }
- }
-
- glXMakeCurrent(view->impl->display, None, NULL);
-#endif
-}
-
-int
-puglCreateWindow(PuglView* view, const char* title)
-{
- PuglInternals* const impl = view->impl;
-
- impl->display = XOpenDisplay(0);
- impl->screen = DefaultScreen(impl->display);
-
- XVisualInfo* const vi = getVisual(view);
- if (!vi) {
- return 1;
- }
-
- Window xParent = view->parent
- ? (Window)view->parent
- : RootWindow(impl->display, impl->screen);
-
- Colormap cmap = XCreateColormap(
- impl->display, xParent, vi->visual, AllocNone);
-
- impl->clipboard = XInternAtom(impl->display, "CLIPBOARD", 0);
- impl->utf8_string = XInternAtom(impl->display, "UTF8_STRING", 0);
-
- XSetWindowAttributes attr;
- memset(&attr, 0, sizeof(XSetWindowAttributes));
- attr.colormap = cmap;
- attr.event_mask = (ExposureMask | StructureNotifyMask |
- EnterWindowMask | LeaveWindowMask |
- KeyPressMask | KeyReleaseMask |
- ButtonPressMask | ButtonReleaseMask |
- PointerMotionMask | FocusChangeMask);
-
- impl->win = XCreateWindow(
- impl->display, xParent,
- 0, 0, view->width, view->height, 0, vi->depth, InputOutput, vi->visual,
- CWColormap | CWEventMask, &attr);
-
- if (!createContext(view, vi)) {
- XFree(vi);
- return 2;
- }
-
- XSizeHints sizeHints;
- memset(&sizeHints, 0, sizeof(sizeHints));
- if (!view->resizable) {
- sizeHints.flags = PMinSize|PMaxSize;
- sizeHints.min_width = view->width;
- sizeHints.min_height = view->height;
- sizeHints.max_width = view->width;
- sizeHints.max_height = view->height;
- XSetNormalHints(impl->display, impl->win, &sizeHints);
- } else {
- if (view->min_width || view->min_height) {
- sizeHints.flags = PMinSize;
- sizeHints.min_width = view->min_width;
- sizeHints.min_height = view->min_height;
- }
- if (view->min_aspect_x) {
- sizeHints.flags |= PAspect;
- sizeHints.min_aspect.x = view->min_aspect_x;
- sizeHints.min_aspect.y = view->min_aspect_y;
- sizeHints.max_aspect.x = view->max_aspect_x;
- sizeHints.max_aspect.y = view->max_aspect_y;
- }
-
- XSetNormalHints(impl->display, impl->win, &sizeHints);
- }
-
- if (title) {
- XStoreName(impl->display, impl->win, title);
- }
-
- if (!view->parent) {
- Atom wmDelete = XInternAtom(impl->display, "WM_DELETE_WINDOW", True);
- XSetWMProtocols(impl->display, impl->win, &wmDelete, 1);
- }
-
- if (view->transient_parent) {
- XSetTransientForHint(impl->display, impl->win,
- (Window)(view->transient_parent));
- }
-
- XSetLocaleModifiers("");
- if (!(impl->xim = XOpenIM(impl->display, NULL, NULL, NULL))) {
- XSetLocaleModifiers("@im=");
- if (!(impl->xim = XOpenIM(impl->display, NULL, NULL, NULL))) {
- fprintf(stderr, "warning: XOpenIM failed\n");
- }
- }
-
- const XIMStyle im_style = XIMPreeditNothing | XIMStatusNothing;
- if (!(impl->xic = XCreateIC(impl->xim,
- XNInputStyle, im_style,
- XNClientWindow, impl->win,
- XNFocusWindow, impl->win,
- NULL))) {
- fprintf(stderr, "warning: XCreateIC failed\n");
- }
-
- XFree(vi);
-
- return 0;
-}
-
-void
-puglShowWindow(PuglView* view)
-{
- XMapRaised(view->impl->display, view->impl->win);
- view->visible = true;
-}
-
-void
-puglHideWindow(PuglView* view)
-{
- XUnmapWindow(view->impl->display, view->impl->win);
- view->visible = false;
-}
-
-void
-puglDestroy(PuglView* view)
-{
- if (view) {
- destroyContext(view);
- XDestroyWindow(view->impl->display, view->impl->win);
- XCloseDisplay(view->impl->display);
- puglClearSelection(view);
- free(view->windowClass);
- free(view->impl);
- free(view);
- }
-}
-
-static PuglKey
-keySymToSpecial(KeySym sym)
-{
- switch (sym) {
- case XK_F1: return PUGL_KEY_F1;
- case XK_F2: return PUGL_KEY_F2;
- case XK_F3: return PUGL_KEY_F3;
- case XK_F4: return PUGL_KEY_F4;
- case XK_F5: return PUGL_KEY_F5;
- case XK_F6: return PUGL_KEY_F6;
- case XK_F7: return PUGL_KEY_F7;
- case XK_F8: return PUGL_KEY_F8;
- case XK_F9: return PUGL_KEY_F9;
- case XK_F10: return PUGL_KEY_F10;
- case XK_F11: return PUGL_KEY_F11;
- case XK_F12: return PUGL_KEY_F12;
- case XK_Left: return PUGL_KEY_LEFT;
- case XK_Up: return PUGL_KEY_UP;
- case XK_Right: return PUGL_KEY_RIGHT;
- case XK_Down: return PUGL_KEY_DOWN;
- case XK_Page_Up: return PUGL_KEY_PAGE_UP;
- case XK_Page_Down: return PUGL_KEY_PAGE_DOWN;
- case XK_Home: return PUGL_KEY_HOME;
- case XK_End: return PUGL_KEY_END;
- case XK_Insert: return PUGL_KEY_INSERT;
- case XK_Shift_L: return PUGL_KEY_SHIFT;
- case XK_Shift_R: return PUGL_KEY_SHIFT;
- case XK_Control_L: return PUGL_KEY_CTRL;
- case XK_Control_R: return PUGL_KEY_CTRL;
- case XK_Alt_L: return PUGL_KEY_ALT;
- case XK_Alt_R: return PUGL_KEY_ALT;
- case XK_Super_L: return PUGL_KEY_SUPER;
- case XK_Super_R: return PUGL_KEY_SUPER;
- }
- return (PuglKey)0;
-}
-
-static void
-translateKey(PuglView* view, XEvent* xevent, PuglEvent* event)
-{
- KeySym sym = 0;
- char* str = (char*)event->key.utf8;
- memset(str, 0, 8);
- event->key.filter = XFilterEvent(xevent, None);
- if (xevent->type == KeyRelease || event->key.filter || !view->impl->xic) {
- if (XLookupString(&xevent->xkey, str, 7, &sym, NULL) == 1) {
- event->key.character = str[0];
- }
- } else {
- /* TODO: Not sure about this. On my system, some characters work with
- Xutf8LookupString but not with XmbLookupString, and some are the
- opposite. */
- Status status = 0;
-#ifdef X_HAVE_UTF8_STRING
- const int n = Xutf8LookupString(
- view->impl->xic, &xevent->xkey, str, 7, &sym, &status);
-#else
- const int n = XmbLookupString(
- view->impl->xic, &xevent->xkey, str, 7, &sym, &status);
-#endif
- if (n > 0) {
- event->key.character = puglDecodeUTF8((const uint8_t*)str);
- }
- }
- event->key.special = keySymToSpecial(sym);
- event->key.keycode = xevent->xkey.keycode;
-}
-
-static unsigned
-translateModifiers(unsigned xstate)
-{
- unsigned state = 0;
- state |= (xstate & ShiftMask) ? PUGL_MOD_SHIFT : 0;
- state |= (xstate & ControlMask) ? PUGL_MOD_CTRL : 0;
- state |= (xstate & Mod1Mask) ? PUGL_MOD_ALT : 0;
- state |= (xstate & Mod4Mask) ? PUGL_MOD_SUPER : 0;
- return state;
-}
-
-static PuglEvent
-translateEvent(PuglView* view, XEvent xevent)
-{
- PuglEvent event;
- memset(&event, 0, sizeof(event));
-
- event.any.view = view;
- if (xevent.xany.send_event) {
- event.any.flags |= PUGL_IS_SEND_EVENT;
- }
-
- switch (xevent.type) {
- case ClientMessage: {
- char* type = XGetAtomName(view->impl->display,
- xevent.xclient.message_type);
- if (!strcmp(type, "WM_PROTOCOLS")) {
- event.type = PUGL_CLOSE;
- }
- XFree(type);
- break;
- }
- case ConfigureNotify:
- event.type = PUGL_CONFIGURE;
- event.configure.x = xevent.xconfigure.x;
- event.configure.y = xevent.xconfigure.y;
- event.configure.width = xevent.xconfigure.width;
- event.configure.height = xevent.xconfigure.height;
- break;
- case Expose:
- event.type = PUGL_EXPOSE;
- event.expose.x = xevent.xexpose.x;
- event.expose.y = xevent.xexpose.y;
- event.expose.width = xevent.xexpose.width;
- event.expose.height = xevent.xexpose.height;
- event.expose.count = xevent.xexpose.count;
- break;
- case MotionNotify:
- event.type = PUGL_MOTION_NOTIFY;
- event.motion.time = xevent.xmotion.time;
- event.motion.x = xevent.xmotion.x;
- event.motion.y = xevent.xmotion.y;
- event.motion.x_root = xevent.xmotion.x_root;
- event.motion.y_root = xevent.xmotion.y_root;
- event.motion.state = translateModifiers(xevent.xmotion.state);
- event.motion.is_hint = (xevent.xmotion.is_hint == NotifyHint);
- break;
- case ButtonPress:
- if (xevent.xbutton.button >= 4 && xevent.xbutton.button <= 7) {
- event.type = PUGL_SCROLL;
- event.scroll.time = xevent.xbutton.time;
- event.scroll.x = xevent.xbutton.x;
- event.scroll.y = xevent.xbutton.y;
- event.scroll.x_root = xevent.xbutton.x_root;
- event.scroll.y_root = xevent.xbutton.y_root;
- event.scroll.state = translateModifiers(xevent.xbutton.state);
- event.scroll.dx = 0.0;
- event.scroll.dy = 0.0;
- switch (xevent.xbutton.button) {
- case 4: event.scroll.dy = 1.0f; break;
- case 5: event.scroll.dy = -1.0f; break;
- case 6: event.scroll.dx = -1.0f; break;
- case 7: event.scroll.dx = 1.0f; break;
- }
- }
- /* fall through */
- case ButtonRelease:
- if (xevent.xbutton.button < 4 || xevent.xbutton.button > 7) {
- event.button.type = ((xevent.type == ButtonPress)
- ? PUGL_BUTTON_PRESS
- : PUGL_BUTTON_RELEASE);
- event.button.time = xevent.xbutton.time;
- event.button.x = xevent.xbutton.x;
- event.button.y = xevent.xbutton.y;
- event.button.x_root = xevent.xbutton.x_root;
- event.button.y_root = xevent.xbutton.y_root;
- event.button.state = translateModifiers(xevent.xbutton.state);
- event.button.button = xevent.xbutton.button;
- }
- break;
- case KeyPress:
- case KeyRelease:
- event.type = ((xevent.type == KeyPress)
- ? PUGL_KEY_PRESS
- : PUGL_KEY_RELEASE);
- event.key.time = xevent.xkey.time;
- event.key.x = xevent.xkey.x;
- event.key.y = xevent.xkey.y;
- event.key.x_root = xevent.xkey.x_root;
- event.key.y_root = xevent.xkey.y_root;
- event.key.state = translateModifiers(xevent.xkey.state);
- translateKey(view, &xevent, &event);
- break;
- case EnterNotify:
- case LeaveNotify:
- event.type = ((xevent.type == EnterNotify)
- ? PUGL_ENTER_NOTIFY
- : PUGL_LEAVE_NOTIFY);
- event.crossing.time = xevent.xcrossing.time;
- event.crossing.x = xevent.xcrossing.x;
- event.crossing.y = xevent.xcrossing.y;
- event.crossing.x_root = xevent.xcrossing.x_root;
- event.crossing.y_root = xevent.xcrossing.y_root;
- event.crossing.state = translateModifiers(xevent.xcrossing.state);
- event.crossing.mode = PUGL_CROSSING_NORMAL;
- if (xevent.xcrossing.mode == NotifyGrab) {
- event.crossing.mode = PUGL_CROSSING_GRAB;
- } else if (xevent.xcrossing.mode == NotifyUngrab) {
- event.crossing.mode = PUGL_CROSSING_UNGRAB;
- }
- break;
-
- case FocusIn:
- case FocusOut:
- event.type = ((xevent.type == FocusIn)
- ? PUGL_FOCUS_IN
- : PUGL_FOCUS_OUT);
- event.focus.grab = (xevent.xfocus.mode != NotifyNormal);
- break;
-
- case SelectionClear:
- puglClearSelection(view);
- break;
- case SelectionRequest:
- {
- XSelectionEvent xev = {
- .type = SelectionNotify,
- .requestor = xevent.xselectionrequest.requestor,
- .selection = xevent.xselectionrequest.selection,
- .target = xevent.xselectionrequest.target,
- .time = xevent.xselectionrequest.time,
- };
-
- size_t len;
- const char *selection = puglGetSelection(view, &len);
- if(selection
- && (xevent.xselectionrequest.selection == view->impl->clipboard)
- && (xevent.xselectionrequest.target == view->impl->utf8_string) )
- {
- xev.property = xevent.xselectionrequest.property;
- XChangeProperty(view->impl->display, xev.requestor,
- xev.property, xev.target, 8, PropModeReplace,
- (const uint8_t*)selection, len);
- }
- else // conversion failed
- {
- xev.property = None;
- }
- XSendEvent(view->impl->display, xev.requestor, True, 0, (XEvent*)&xev);
-
- break;
- }
-
- default:
- break;
- }
-
- return event;
-}
-
-void
-puglCopyToClipboard(PuglView* view, const char* selection, size_t len)
-{
- PuglInternals* const impl = view->impl;
-
- puglSetSelection(view, selection, len);
-
- XSetSelectionOwner(impl->display, impl->clipboard, impl->win, CurrentTime);
-}
-
-const char*
-puglPasteFromClipboard(PuglView* view, size_t* len)
-{
- PuglInternals* const impl = view->impl;
-
- if(XGetSelectionOwner(impl->display, impl->clipboard) != impl->win) {
- XConvertSelection(impl->display, impl->clipboard, impl->utf8_string,
- XA_PRIMARY, impl->win, CurrentTime);
-
- XEvent xevent;
- while(!XCheckTypedWindowEvent(impl->display, impl->win, SelectionNotify,
- &xevent)) {
- // wait for answer
- }
-
- if( (xevent.xselection.selection == impl->clipboard)
- && (xevent.xselection.target == impl->utf8_string)
- && (xevent.xselection.property == XA_PRIMARY) ) {
- unsigned long nitems, rem;
- int format;
- uint8_t* data;
- Atom type;
-
- XGetWindowProperty(impl->display, impl->win, XA_PRIMARY,
- 0, LONG_MAX/4, False, AnyPropertyType, // request 32*32=1024 bytes
- &type, &format, &nitems, &rem, &data);
- if(data) {
- if( (format == 8) && (type == impl->utf8_string) && (rem == 0) ) {
- puglSetSelection(view, (const char*)data, nitems);
- }
-
- XFree(data);
- }
- }
- }
-
- return puglGetSelection(view, len);
-}
-
-void
-puglGrabFocus(PuglView* view)
-{
- XSetInputFocus(
- view->impl->display, view->impl->win, RevertToPointerRoot, CurrentTime);
-}
-
-PuglStatus
-puglWaitForEvent(PuglView* view)
-{
- XEvent xevent;
- XPeekEvent(view->impl->display, &xevent);
- return PUGL_SUCCESS;
-}
-
-static void
-merge_draw_events(PuglEvent* dst, const PuglEvent* src)
-{
- if (!dst->type) {
- *dst = *src;
- } else {
- dst->expose.x = MIN(dst->expose.x, src->expose.x);
- dst->expose.y = MIN(dst->expose.y, src->expose.y);
- dst->expose.width = MAX(dst->expose.width, src->expose.width);
- dst->expose.height = MAX(dst->expose.height, src->expose.height);
- }
-}
-
-PuglStatus
-puglProcessEvents(PuglView* view)
-{
- /* Maintain a single expose/configure event to execute after all pending
- events. This avoids redundant drawing/configuration which prevents a
- series of window resizes in the same loop from being laggy. */
- PuglEvent expose_event = { 0 };
- PuglEvent config_event = { 0 };
- XEvent xevent;
- while (XPending(view->impl->display) > 0) {
- XNextEvent(view->impl->display, &xevent);
- if (xevent.type == KeyRelease) {
- // Ignore key repeat if necessary
- if (view->ignoreKeyRepeat &&
- XEventsQueued(view->impl->display, QueuedAfterReading)) {
- XEvent next;
- XPeekEvent(view->impl->display, &next);
- if (next.type == KeyPress &&
- next.xkey.time == xevent.xkey.time &&
- next.xkey.keycode == xevent.xkey.keycode) {
- XNextEvent(view->impl->display, &xevent);
- continue;
- }
- }
- } else if (xevent.type == FocusIn) {
- XSetICFocus(view->impl->xic);
- } else if (xevent.type == FocusOut) {
- XUnsetICFocus(view->impl->xic);
- }
-
- // Translate X11 event to Pugl event
- const PuglEvent event = translateEvent(view, xevent);
-
- if (event.type == PUGL_EXPOSE) {
- // Expand expose event to be dispatched after loop
- merge_draw_events(&expose_event, &event);
- } else if (event.type == PUGL_CONFIGURE) {
- // Expand configure event to be dispatched after loop
- merge_draw_events(&config_event, &event);
- } else {
- // Dispatch event to application immediately
- puglDispatchEvent(view, &event);
- }
- }
-
- if (config_event.type
- && ( (view->width != config_event.configure.width)
- || (view->height != config_event.configure.height) )) {
-#ifdef PUGL_HAVE_CAIRO
- if (view->ctx_type == PUGL_CAIRO) {
- // Resize surfaces/contexts before dispatching
- view->redisplay = true;
- cairo_xlib_surface_set_size(view->impl->surface,
- config_event.configure.width,
- config_event.configure.height);
- }
-#ifdef PUGL_HAVE_GL
- if (view->ctx_type == PUGL_CAIRO_GL) {
- view->redisplay = true;
- cairo_surface_destroy(view->impl->surface);
- view->impl->surface = pugl_cairo_gl_create(
- &view->impl->cairo_gl,
- config_event.configure.width,
- config_event.configure.height,
- 4);
- pugl_cairo_gl_configure(&view->impl->cairo_gl,
- config_event.configure.width,
- config_event.configure.height);
- createCairoContext(view);
- }
-#endif
-#endif
- puglDispatchEvent(view, (const PuglEvent*)&config_event);
- }
-
- if (view->redisplay) {
- expose_event.expose.type = PUGL_EXPOSE;
- expose_event.expose.view = view;
- expose_event.expose.x = 0;
- expose_event.expose.y = 0;
- expose_event.expose.width = view->width;
- expose_event.expose.height = view->height;
- view->redisplay = false;
- }
-
- if (expose_event.type) {
- puglDispatchEvent(view, (const PuglEvent*)&expose_event);
- }
-
- return PUGL_SUCCESS;
-}
-
-void
-puglPostRedisplay(PuglView* view)
-{
- view->redisplay = true;
-}
-
-PuglNativeWindow
-puglGetNativeWindow(PuglView* view)
-{
- return view->impl->win;
-}
-
-void*
-puglGetContext(PuglView* view __attribute__((unused)))
-{
-#ifdef PUGL_HAVE_CAIRO
- if (view->ctx_type & PUGL_CAIRO) {
- return view->impl->cr;
- }
-#endif
- return NULL;
-}
diff --git a/pugl/pugl_cairo_test.c b/pugl/pugl_cairo_test.c
deleted file mode 100644
index c04c785b..00000000
--- a/pugl/pugl_cairo_test.c
+++ /dev/null
@@ -1,205 +0,0 @@
-/*
- Copyright 2012-2014 David Robillard <http://drobilla.net>
-
- Permission to use, copy, modify, and/or distribute this software for any
- purpose with or without fee is hereby granted, provided that the above
- copyright notice and this permission notice appear in all copies.
-
- THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-*/
-
-/**
- @file pugl_cairo_test.c A simple Pugl test that creates a top-level window.
-*/
-
-#include <stdint.h>
-#include <stdio.h>
-#include <string.h>
-
-#include <cairo/cairo.h>
-
-#include "pugl/pugl.h"
-
-static int quit = 0;
-static bool entered = false;
-
-typedef struct {
- int x;
- int y;
- int w;
- int h;
- bool pressed;
- const char* label;
-} Button;
-
-static Button toggle_button = { 16, 16, 128, 64, false, "Test" };
-
-static void
-roundedBox(cairo_t* cr, double x, double y, double w, double h)
-{
- static const double radius = 10;
- static const double degrees = 3.14159265 / 180.0;
-
- cairo_new_sub_path(cr);
- cairo_arc(cr,
- x + w - radius,
- y + radius,
- radius, -90 * degrees, 0 * degrees);
- cairo_arc(cr,
- x + w - radius, y + h - radius,
- radius, 0 * degrees, 90 * degrees);
- cairo_arc(cr,
- x + radius, y + h - radius,
- radius, 90 * degrees, 180 * degrees);
- cairo_arc(cr,
- x + radius, y + radius,
- radius, 180 * degrees, 270 * degrees);
- cairo_close_path(cr);
-}
-
-static void
-buttonDraw(cairo_t* cr, const Button* but)
-{
- // Draw base
- if (but->pressed) {
- cairo_set_source_rgba(cr, 0.4, 0.9, 0.1, 1);
- } else {
- cairo_set_source_rgba(cr, 0.3, 0.5, 0.1, 1);
- }
- roundedBox(cr, but->x, but->y, but->w, but->h);
- cairo_fill_preserve(cr);
-
- // Draw border
- cairo_set_source_rgba(cr, 0.4, 0.9, 0.1, 1);
- cairo_set_line_width(cr, 4.0);
- cairo_stroke(cr);
-
- // Draw label
- cairo_text_extents_t extents;
- cairo_set_font_size(cr, 32.0);
- cairo_text_extents(cr, but->label, &extents);
- cairo_move_to(cr,
- (but->x + but->w / 2) - extents.width / 2,
- (but->y + but->h / 2) + extents.height / 2);
- cairo_set_source_rgba(cr, 0, 0, 0, 1);
- cairo_show_text(cr, but->label);
-}
-
-static bool
-buttonTouches(const Button* but, double x, double y)
-{
- return (x >= toggle_button.x && x <= toggle_button.x + toggle_button.w &&
- y >= toggle_button.y && y <= toggle_button.y + toggle_button.h);
-}
-
-static void
-onDisplay(PuglView* view)
-{
- cairo_t* cr = puglGetContext(view);
-
- // Draw background
- int width, height;
- puglGetSize(view, &width, &height);
- if (entered) {
- cairo_set_source_rgb(cr, 0.1, 0.1, 0.1);
- } else {
- cairo_set_source_rgb(cr, 0, 0, 0);
- }
- cairo_rectangle(cr, 0, 0, width, height);
- cairo_fill(cr);
-
- // Draw button
- buttonDraw(cr, &toggle_button);
-}
-
-static void
-onClose(PuglView* view)
-{
- quit = 1;
-}
-
-static void
-onEvent(PuglView* view, const PuglEvent* event)
-{
- switch (event->type) {
- case PUGL_KEY_PRESS:
- if (event->key.character == 'q' ||
- event->key.character == 'Q' ||
- event->key.character == PUGL_CHAR_ESCAPE) {
- quit = 1;
- }
- break;
- case PUGL_BUTTON_PRESS:
- if (buttonTouches(&toggle_button, event->button.x, event->button.y)) {
- toggle_button.pressed = !toggle_button.pressed;
- puglPostRedisplay(view);
- }
- break;
- case PUGL_ENTER_NOTIFY:
- entered = true;
- puglPostRedisplay(view);
- break;
- case PUGL_LEAVE_NOTIFY:
- entered = false;
- puglPostRedisplay(view);
- break;
- case PUGL_EXPOSE:
- onDisplay(view);
- break;
- case PUGL_CLOSE:
- onClose(view);
- break;
- default: break;
- }
-}
-
-int
-main(int argc, char** argv)
-{
- bool useGL = false;
- bool ignoreKeyRepeat = false;
- bool resizable = false;
- for (int i = 1; i < argc; ++i) {
- if (!strcmp(argv[i], "-h")) {
- printf("USAGE: %s [OPTIONS]...\n\n"
- " -g Use OpenGL\n"
- " -h Display this help\n"
- " -i Ignore key repeat\n"
- " -r Resizable window\n", argv[0]);
- return 0;
- } else if (!strcmp(argv[i], "-g")) {
- useGL = true;
- } else if (!strcmp(argv[i], "-i")) {
- ignoreKeyRepeat = true;
- } else if (!strcmp(argv[i], "-r")) {
- resizable = true;
- } else {
- fprintf(stderr, "Unknown option: %s\n", argv[i]);
- }
- }
-
- PuglView* view = puglInit(NULL, NULL);
- puglInitWindowSize(view, 512, 512);
- puglInitResizable(view, resizable);
- puglInitContextType(view, useGL ? PUGL_CAIRO_GL : PUGL_CAIRO);
-
- puglIgnoreKeyRepeat(view, ignoreKeyRepeat);
- puglSetEventFunc(view, onEvent);
-
- puglCreateWindow(view, "Pugl Test");
- puglShowWindow(view);
-
- while (!quit) {
- puglWaitForEvent(view);
- puglProcessEvents(view);
- }
-
- puglDestroy(view);
- return 0;
-}
diff --git a/pugl/pugl_test.c b/pugl/pugl_test.c
deleted file mode 100644
index 367e7a4d..00000000
--- a/pugl/pugl_test.c
+++ /dev/null
@@ -1,261 +0,0 @@
-/*
- Copyright 2012-2016 David Robillard <http://drobilla.net>
-
- Permission to use, copy, modify, and/or distribute this software for any
- purpose with or without fee is hereby granted, provided that the above
- copyright notice and this permission notice appear in all copies.
-
- THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-*/
-
-/**
- @file pugl_test.c A simple Pugl test that creates a top-level window.
-*/
-
-#include <locale.h>
-#include <math.h>
-#include <stdio.h>
-#include <string.h>
-
-#include "pugl/gl.h"
-#include "pugl/pugl.h"
-
-static int quit = 0;
-static float xAngle = 0.0f;
-static float yAngle = 0.0f;
-static float dist = 10.0f;
-
-static const float cubeVertices[] = {
- -1.0f, -1.0f, -1.0f,
- -1.0f, -1.0f, 1.0f,
- -1.0f, 1.0f, 1.0f,
-
- 1.0f, 1.0f, -1.0f,
- -1.0f, -1.0f, -1.0f,
- -1.0f, 1.0f, -1.0f,
-
- 1.0f, -1.0f, 1.0f,
- -1.0f, -1.0f, -1.0f,
- 1.0f, -1.0f, -1.0f,
-
- 1.0f, 1.0f, -1.0f,
- 1.0f, -1.0f, -1.0f,
- -1.0f, -1.0f, -1.0f,
-
- -1.0f, -1.0f, -1.0f,
- -1.0f, 1.0f, 1.0f,
- -1.0f, 1.0f, -1.0f,
-
- 1.0f, -1.0f, 1.0f,
- -1.0f, -1.0f, 1.0f,
- -1.0f, -1.0f, -1.0f,
-
- -1.0f, 1.0f, 1.0f,
- -1.0f, -1.0f, 1.0f,
- 1.0f, -1.0f, 1.0f,
-
- 1.0f, 1.0f, 1.0f,
- 1.0f, -1.0f, -1.0f,
- 1.0f, 1.0f, -1.0f,
-
- 1.0f, -1.0f, -1.0f,
- 1.0f, 1.0f, 1.0f,
- 1.0f, -1.0f, 1.0f,
-
- 1.0f, 1.0f, 1.0f,
- 1.0f, 1.0f, -1.0f,
- -1.0f, 1.0f, -1.0f,
-
- 1.0f, 1.0f, 1.0f,
- -1.0f, 1.0f, -1.0f,
- -1.0f, 1.0f, 1.0f,
-
- 1.0f, 1.0f, 1.0f,
- -1.0f, 1.0f, 1.0f,
- 1.0f, -1.0f, 1.0f
-};
-
-/** Calculate a projection matrix for a given perspective. */
-static void
-perspective(float* m, float fov, float aspect, float zNear, float zFar)
-{
- const float h = tan(fov);
- const float w = h / aspect;
- const float depth = zNear - zFar;
- const float q = (zFar + zNear) / depth;
- const float qn = 2 * zFar * zNear / depth;
-
- m[0] = w; m[1] = 0; m[2] = 0; m[3] = 0;
- m[4] = 0; m[5] = h; m[6] = 0; m[7] = 0;
- m[8] = 0; m[9] = 0; m[10] = q; m[11] = -1;
- m[12] = 0; m[13] = 0; m[14] = qn; m[15] = 0;
-}
-
-static void
-onReshape(PuglView* view, int width, int height)
-{
- glMatrixMode(GL_PROJECTION);
- glLoadIdentity();
- glViewport(0, 0, width, height);
-
- float projection[16];
- perspective(projection, 1.8f, width / (float)height, 1.0, 100.0f);
- glLoadMatrixf(projection);
-}
-
-static void
-onDisplay(PuglView* view)
-{
- glMatrixMode(GL_MODELVIEW);
- glLoadIdentity();
- glTranslatef(0.0f, 0.0f, dist * -1);
- glRotatef(xAngle, 0.0f, 1.0f, 0.0f);
- glRotatef(yAngle, 1.0f, 0.0f, 0.0f);
-
- glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
- glEnableClientState(GL_VERTEX_ARRAY);
- glEnableClientState(GL_COLOR_ARRAY);
-
- glVertexPointer(3, GL_FLOAT, 0, cubeVertices);
- glColorPointer(3, GL_FLOAT, 0, cubeVertices);
- glDrawArrays(GL_TRIANGLES, 0, 12 * 3);
-
- glDisableClientState(GL_VERTEX_ARRAY);
- glDisableClientState(GL_COLOR_ARRAY);
-}
-
-static void
-printModifiers(PuglView* view, unsigned mods)
-{
- fprintf(stderr, "Modifiers:%s%s%s%s\n",
- (mods & PUGL_MOD_SHIFT) ? " Shift" : "",
- (mods & PUGL_MOD_CTRL) ? " Ctrl" : "",
- (mods & PUGL_MOD_ALT) ? " Alt" : "",
- (mods & PUGL_MOD_SUPER) ? " Super" : "");
-}
-
-static void
-onEvent(PuglView* view, const PuglEvent* event)
-{
- switch (event->type) {
- case PUGL_NOTHING:
- break;
- case PUGL_CONFIGURE:
- onReshape(view, event->configure.width, event->configure.height);
- break;
- case PUGL_EXPOSE:
- onDisplay(view);
- break;
- case PUGL_CLOSE:
- quit = 1;
- break;
- case PUGL_KEY_PRESS:
- fprintf(stderr, "Key %u (char %u) press (%s)%s\n",
- event->key.keycode, event->key.character, event->key.utf8,
- event->key.filter ? " (filtered)" : "");
- if (event->key.character == 'q' ||
- event->key.character == 'Q' ||
- event->key.character == PUGL_CHAR_ESCAPE) {
- quit = 1;
- }
- break;
- case PUGL_KEY_RELEASE:
- fprintf(stderr, "Key %u (char %u) release (%s)%s\n",
- event->key.keycode, event->key.character, event->key.utf8,
- event->key.filter ? " (filtered)" : "");
- break;
- case PUGL_MOTION_NOTIFY:
- xAngle = -(int)event->motion.x % 360;
- yAngle = (int)event->motion.y % 360;
- puglPostRedisplay(view);
- break;
- case PUGL_BUTTON_PRESS:
- case PUGL_BUTTON_RELEASE:
- fprintf(stderr, "Mouse %d %s at %f,%f ",
- event->button.button,
- (event->type == PUGL_BUTTON_PRESS) ? "down" : "up",
- event->button.x,
- event->button.y);
- printModifiers(view, event->scroll.state);
- break;
- case PUGL_SCROLL:
- fprintf(stderr, "Scroll %f %f %f %f ",
- event->scroll.x, event->scroll.y, event->scroll.dx, event->scroll.dy);
- printModifiers(view, event->scroll.state);
- dist += event->scroll.dy;
- if (dist < 10.0f) {
- dist = 10.0f;
- }
- puglPostRedisplay(view);
- break;
- case PUGL_ENTER_NOTIFY:
- fprintf(stderr, "Entered\n");
- break;
- case PUGL_LEAVE_NOTIFY:
- fprintf(stderr, "Exited\n");
- break;
- case PUGL_FOCUS_IN:
- fprintf(stderr, "Focus in\n");
- break;
- case PUGL_FOCUS_OUT:
- fprintf(stderr, "Focus out\n");
- break;
- }
-}
-
-int
-main(int argc, char** argv)
-{
- bool ignoreKeyRepeat = false;
- bool resizable = false;
- for (int i = 1; i < argc; ++i) {
- if (!strcmp(argv[i], "-h")) {
- printf("USAGE: %s [OPTIONS]...\n\n"
- " -h Display this help\n"
- " -i Ignore key repeat\n"
- " -r Resizable window\n", argv[0]);
- return 0;
- } else if (!strcmp(argv[i], "-i")) {
- ignoreKeyRepeat = true;
- } else if (!strcmp(argv[i], "-r")) {
- resizable = true;
- } else {
- fprintf(stderr, "Unknown option: %s\n", argv[i]);
- }
- }
-
- setlocale(LC_CTYPE, "");
-
- PuglView* view = puglInit(NULL, NULL);
- puglInitWindowClass(view, "PuglTest");
- puglInitWindowSize(view, 512, 512);
- puglInitWindowMinSize(view, 256, 256);
- puglInitResizable(view, resizable);
-
- puglIgnoreKeyRepeat(view, ignoreKeyRepeat);
- puglSetEventFunc(view, onEvent);
-
- puglCreateWindow(view, "Pugl Test");
-
- puglEnterContext(view);
- glEnable(GL_DEPTH_TEST);
- glDepthFunc(GL_LESS);
- glClearColor(0.2f, 0.2f, 0.2f, 1.0f);
- puglLeaveContext(view, false);
-
- puglShowWindow(view);
-
- while (!quit) {
- puglWaitForEvent(view);
- puglProcessEvents(view);
- }
-
- puglDestroy(view);
- return 0;
-}
diff --git a/pugl/waf b/pugl/waf
deleted file mode 100755
index 2dbb06bd..00000000
--- a/pugl/waf
+++ /dev/null
Binary files differ
diff --git a/pugl/wscript b/pugl/wscript
deleted file mode 100644
index 349ee140..00000000
--- a/pugl/wscript
+++ /dev/null
@@ -1,203 +0,0 @@
-#!/usr/bin/env python
-import glob
-import os
-import subprocess
-import sys
-import waflib.Logs as Logs
-import waflib.Options as Options
-import waflib.extras.autowaf as autowaf
-
-# Library and package version (UNIX style major, minor, micro)
-# major increment <=> incompatible changes
-# minor increment <=> compatible changes (additions)
-# micro increment <=> no interface changes
-PUGL_VERSION = '0.2.0'
-PUGL_MAJOR_VERSION = '0'
-
-# Mandatory waf variables
-APPNAME = 'pugl' # Package name for waf dist
-VERSION = PUGL_VERSION # Package version for waf dist
-top = '.' # Source directory
-out = 'build' # Build directory
-
-def options(opt):
- opt.load('compiler_c')
- opt.load('compiler_cxx')
- autowaf.set_options(opt)
- opt.add_option('--no-gl', action='store_true', default=False, dest='no_gl',
- help='Do not build OpenGL support')
- opt.add_option('--no-cairo', action='store_true', default=False, dest='no_cairo',
- help='Do not build Cairo support')
- opt.add_option('--test', action='store_true', default=False, dest='build_tests',
- help='Build unit tests')
- opt.add_option('--static', action='store_true', default=False, dest='static',
- help='Build static library')
- opt.add_option('--shared', action='store_true', default=False, dest='shared',
- help='Build shared library')
- opt.add_option('--target', default=None, dest='target',
- help='Target platform (e.g. "win32" or "darwin")')
- opt.add_option('--log', action='store_true', default=False, dest='log',
- help='Print GL information to console')
- opt.add_option('--grab-focus', action='store_true', default=False, dest='grab_focus',
- help='Work around reparent keyboard issues by grabbing focus')
-
-def configure(conf):
- conf.env.TARGET_PLATFORM = Options.options.target or Options.platform
- conf.load('compiler_c')
- if conf.env.TARGET_PLATFORM == 'win32':
- conf.load('compiler_cxx')
-
- autowaf.configure(conf)
- autowaf.set_c99_mode(conf)
- autowaf.display_header('Pugl Configuration')
-
- if not Options.options.no_gl:
- # TODO: Portable check for OpenGL
- conf.define('HAVE_GL', 1)
- autowaf.define(conf, 'PUGL_HAVE_GL', 1)
-
- if not Options.options.no_cairo:
- autowaf.check_pkg(conf, 'cairo',
- uselib_store = 'CAIRO',
- atleast_version = '1.0.0',
- mandatory = False)
- if conf.is_defined('HAVE_CAIRO'):
- autowaf.define(conf, 'PUGL_HAVE_CAIRO', 1)
-
- if Options.options.log:
- autowaf.define(conf, 'PUGL_VERBOSE', 1)
-
- # Shared library building is broken on win32 for some reason
- conf.env['BUILD_TESTS'] = Options.options.build_tests
- conf.env['BUILD_SHARED'] = (conf.env.TARGET_PLATFORM != 'win32' or
- Options.options.shared)
- conf.env['BUILD_STATIC'] = (Options.options.build_tests or
- Options.options.static)
-
- autowaf.define(conf, 'PUGL_VERSION', PUGL_VERSION)
- conf.write_config_header('pugl_config.h', remove=False)
-
- conf.env['INCLUDES_PUGL'] = ['%s/pugl-%s' % (conf.env['INCLUDEDIR'],
- PUGL_MAJOR_VERSION)]
- conf.env['LIBPATH_PUGL'] = [conf.env['LIBDIR']]
- conf.env['LIB_PUGL'] = ['pugl-%s' % PUGL_MAJOR_VERSION];
-
- autowaf.display_msg(conf, "OpenGL support", conf.is_defined('HAVE_GL'))
- autowaf.display_msg(conf, "Cairo support", conf.is_defined('HAVE_CAIRO'))
- autowaf.display_msg(conf, "Verbose console output", conf.is_defined('PUGL_VERBOSE'))
- autowaf.display_msg(conf, "Static library", str(conf.env['BUILD_STATIC']))
- autowaf.display_msg(conf, "Unit tests", str(conf.env['BUILD_TESTS']))
- print('')
-
-def build(bld):
- # C Headers
- includedir = '${INCLUDEDIR}/pugl-%s/pugl' % PUGL_MAJOR_VERSION
- bld.install_files(includedir, bld.path.ant_glob('pugl/*.h'))
- bld.install_files(includedir, bld.path.ant_glob('pugl/*.hpp'))
-
- # Pkgconfig file
- autowaf.build_pc(bld, 'PUGL', PUGL_VERSION, PUGL_MAJOR_VERSION, [],
- {'PUGL_MAJOR_VERSION' : PUGL_MAJOR_VERSION})
-
- libflags = [ '-fvisibility=hidden' ]
- framework = []
- libs = []
- if bld.env.TARGET_PLATFORM == 'win32':
- lang = 'cxx'
- lib_source = ['pugl/pugl_win.cpp']
- libs = ['opengl32', 'gdi32', 'user32']
- defines = []
- elif bld.env.TARGET_PLATFORM == 'darwin':
- lang = 'c' # Objective C, actually
- lib_source = ['pugl/pugl_osx.m']
- framework = ['Cocoa', 'OpenGL']
- defines = []
- else:
- lang = 'c'
- lib_source = ['pugl/pugl_x11.c']
- libs = ['X11']
- defines = []
- if bld.is_defined('HAVE_GL'):
- libs += ['GL']
- if bld.env['MSVC_COMPILER']:
- libflags = []
- else:
- libs += ['m']
-
- # Shared Library
- if bld.env['BUILD_SHARED']:
- obj = bld(features = '%s %sshlib' % (lang, lang),
- export_includes = ['.'],
- source = lib_source,
- includes = ['.', './src'],
- lib = libs,
- uselib = ['CAIRO'],
- framework = framework,
- name = 'libpugl',
- target = 'pugl-%s' % PUGL_MAJOR_VERSION,
- vnum = PUGL_VERSION,
- install_path = '${LIBDIR}',
- defines = defines,
- cflags = libflags + [ '-DPUGL_SHARED',
- '-DPUGL_INTERNAL' ])
-
- # Static library
- if bld.env['BUILD_STATIC']:
- obj = bld(features = '%s %sstlib' % (lang, lang),
- export_includes = ['.'],
- source = lib_source,
- includes = ['.', './src'],
- lib = libs,
- uselib = ['CAIRO'],
- framework = framework,
- name = 'libpugl_static',
- target = 'pugl-%s' % PUGL_MAJOR_VERSION,
- vnum = PUGL_VERSION,
- install_path = '${LIBDIR}',
- defines = defines,
- cflags = ['-DPUGL_INTERNAL'])
-
- if bld.env['BUILD_TESTS']:
- test_libs = libs
- test_cflags = ['']
-
- # Test programs
- progs = []
- if bld.is_defined('HAVE_GL'):
- progs += ['pugl_test']
- if bld.is_defined('HAVE_CAIRO'):
- progs += ['pugl_cairo_test']
-
- for prog in progs:
- obj = bld(features = 'c cprogram',
- source = '%s.c' % prog,
- includes = ['.', './src'],
- use = 'libpugl_static',
- lib = test_libs,
- uselib = ['CAIRO'],
- framework = framework,
- target = prog,
- install_path = '',
- defines = defines,
- cflags = test_cflags)
-
- if bld.env['DOCS']:
- bld(features = 'subst',
- source = 'Doxyfile.in',
- target = 'Doxyfile',
- install_path = '',
- name = 'Doxyfile',
- PUGL_VERSION = PUGL_VERSION)
-
- bld(features = 'doxygen',
- doxyfile = 'Doxyfile')
-
-def lint(ctx):
- "checks code for style issues"
- subprocess.call('cpplint.py --filter=+whitespace/comments,-whitespace/tab,-whitespace/braces,-whitespace/labels,-build/header_guard,-readability/casting,-readability/todo,-build/include src/* pugl/*', shell=True)
-
-# Alias .m files to be compiled the same as .c files, gcc will do the right thing.
-from waflib import TaskGen
-@TaskGen.extension('.m')
-def m_hook(self, node):
- return self.create_compiled_task('c', node)
diff --git a/sandbox_ui.lv2/sandbox_io.h b/sandbox_ui.lv2/sandbox_io.h
index e9b5e574..a43e195a 100644
--- a/sandbox_ui.lv2/sandbox_io.h
+++ b/sandbox_ui.lv2/sandbox_io.h
@@ -93,6 +93,9 @@ struct _sandbox_io_t {
LV2_URID ui_window_title;
LV2_URID ui_port_subscribe;
LV2_URID ui_update_rate;
+ LV2_URID ui_scale_factor;
+ LV2_URID ui_background_color;
+ LV2_URID ui_foreground_color;
LV2_URID params_sample_rate;
char *name;
@@ -523,6 +526,9 @@ _sandbox_io_init(sandbox_io_t *io, LV2_URID_Map *map, LV2_URID_Unmap *unmap,
io->ui_window_title = map->map(map->handle, LV2_UI__windowTitle);
io->ui_port_subscribe = map->map(map->handle, LV2_UI__portSubscribe);
io->ui_update_rate = map->map(map->handle, LV2_UI__updateRate);
+ io->ui_scale_factor = map->map(map->handle, LV2_UI__scaleFactor);
+ io->ui_background_color = map->map(map->handle, LV2_UI__backgroundColor);
+ io->ui_foreground_color = map->map(map->handle, LV2_UI__foregroundColor);
io->params_sample_rate = map->map(map->handle, LV2_PARAMETERS__sampleRate);
return 0;
diff --git a/sandbox_ui.lv2/sandbox_slave.c b/sandbox_ui.lv2/sandbox_slave.c
index 4b0e601f..75095a47 100644
--- a/sandbox_ui.lv2/sandbox_slave.c
+++ b/sandbox_ui.lv2/sandbox_slave.c
@@ -31,7 +31,11 @@
#include <lv2/lv2plug.in/ns/ext/log/log.h>
#include <lv2/lv2plug.in/ns/ext/options/options.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/uri-map/uri-map.h>
+#include <lv2/lv2plug.in/ns/ext/instance-access/instance-access.h>
+#include <lv2/lv2plug.in/ns/ext/data-access/data-access.h>
#include <lv2/lv2plug.in/ns/extensions/ui/ui.h>
#include <lilv/lilv.h>
@@ -49,6 +53,13 @@ struct _sandbox_slave_t {
LV2_URI_Map_Feature uri_id;
#pragma GCC diagnostic pop
+ LV2_Atom_Forge forge;
+
+ LV2_URID atom_eventTransfer;
+ LV2_URID patch_Set;
+ LV2_URID patch_property;
+ LV2_URID patch_value;
+
LV2_URID log_trace;
LV2_URID log_error;
LV2_URID log_warning;
@@ -59,6 +70,8 @@ struct _sandbox_slave_t {
LV2UI_Port_Map port_map;
LV2UI_Port_Subscribe port_subscribe;
LV2UI_Touch touch;
+ LV2UI_Request_Value request_value;
+ LV2_Extension_Data_Feature data_access;
xpress_map_t xmap;
xpress_t xpress;
@@ -96,6 +109,9 @@ struct _sandbox_slave_t {
uint32_t minimum;
float sample_rate;
float update_rate;
+ float scale_factor;
+ uint32_t background_color;
+ uint32_t foreground_color;
};
#define ANSI_COLOR_BOLD "\x1b[1m"
@@ -151,17 +167,27 @@ _log_vprintf(LV2_Log_Handle handle, LV2_URID type, const char *fmt, va_list args
int idx = COLOR_LOG;
if(type == sb->log_trace)
+ {
idx = COLOR_TRACE;
+ }
else if(type == sb->log_error)
+ {
idx = COLOR_ERROR;
+ }
else if(type == sb->log_note)
+ {
idx = COLOR_NOTE;
+ }
else if(type == sb->log_warning)
+ {
idx = COLOR_WARNING;
+ }
char *buf;
if(vasprintf(&buf, fmt, args) == -1)
+ {
buf = NULL;
+ }
if(buf)
{
@@ -175,7 +201,9 @@ _log_vprintf(LV2_Log_Handle handle, LV2_URID type, const char *fmt, va_list args
{
fprintf(stderr, "%s %s ", prefix[istty][COLOR_UI], prefix[istty][idx]);
if(sb->plugin_urn)
+ {
fprintf(stderr, "%s%s%s ", prefix[istty][COLOR_URN1], sb->plugin_urn, prefix[istty][COLOR_URN2]);
+ }
fprintf(stderr, "%s\n", pch);
}
}
@@ -210,7 +238,9 @@ _port_index(LV2UI_Feature_Handle handle, const char *symbol)
const LilvPort *port = lilv_plugin_get_port_by_symbol(sb->plug, symbol_uri);
if(port)
+ {
index = lilv_port_get_index(sb->plug, port);
+ }
lilv_node_free(symbol_uri);
}
@@ -272,6 +302,198 @@ _touch(LV2UI_Feature_Handle handle, uint32_t index, bool grabbed)
return;
}
+static uint8_t *
+_file_read(const char *path, size_t *len)
+{
+ const int fd = open(path, O_RDONLY);
+
+ if(fd == -1)
+ {
+ fprintf(stderr, "open: %s '%s'\n", path, strerror(errno));
+ return NULL;
+ }
+
+ lseek(fd, 0, SEEK_SET);
+ *len = lseek(fd, 0, SEEK_END);
+
+ uint8_t *buf = malloc(*len);
+ memset(buf, 0x0, *len);
+
+ lseek(fd, 0, SEEK_SET);
+ read(fd, buf, *len);
+
+ close(fd);
+
+ return buf;
+}
+
+static inline LV2UI_Request_Value_Status
+_request_value(LV2UI_Feature_Handle handle, LV2_URID key, LV2_URID type,
+ const LV2_Feature* const* features __attribute__((unused)))
+{
+ sandbox_slave_t *sb = handle;
+ char path [PATH_MAX];
+
+ if(type == 0)
+ {
+ LilvNode *subj = lilv_new_uri(sb->world, sb->unmap->unmap(sb->unmap->handle, key));
+ LilvNode *pred = lilv_new_uri(sb->world, LILV_NS_RDFS"range");
+ LilvNode *obj = lilv_world_get(sb->world, subj, pred, NULL);
+
+ type = sb->map->map(sb->map->handle, lilv_node_as_uri(obj));
+
+ lilv_node_free(subj);
+ lilv_node_free(pred);
+ lilv_node_free(obj);
+ }
+
+ if(!sb->driver || !sb->driver->request_cb)
+ {
+ return LV2UI_REQUEST_VALUE_ERR_UNKNOWN;
+ }
+
+ memset(path, 0x0, sizeof(path));
+
+ if(sb->driver->request_cb(sb->data, key, sizeof(path), path) != 0)
+ {
+ return LV2UI_REQUEST_VALUE_ERR_UNKNOWN;
+ }
+
+ if(strlen(path) == 0)
+ {
+ return LV2UI_REQUEST_VALUE_ERR_UNKNOWN;
+ }
+
+ // replace NL/CR with zero byte
+ char *endl = strpbrk(path, "\n\r");
+ if(endl)
+ {
+ *endl = '\0';
+ }
+
+ if(type == sb->forge.Path)
+ {
+ const uint32_t nports = lilv_plugin_get_num_ports(sb->plug);
+ LilvNode *patch_message_node = lilv_new_uri(sb->world, LV2_PATCH__Message);
+ LilvNode *core_inputport_node = lilv_new_uri(sb->world, LV2_CORE__InputPort);
+ LilvNode *atom_atomport_node = lilv_new_uri(sb->world, LV2_ATOM__AtomPort);
+
+ for(uint32_t index= 0; index < nports; index++)
+ {
+ const LilvPort *port = lilv_plugin_get_port_by_index(sb->plug, index);
+
+ if(!lilv_port_is_a(sb->plug, port, core_inputport_node))
+ {
+ continue;
+ }
+
+ if(!lilv_port_is_a(sb->plug, port, atom_atomport_node))
+ {
+ continue;
+ }
+
+ if(!lilv_port_supports_event(sb->plug, port, patch_message_node))
+ {
+ continue;
+ }
+
+ uint8_t buf [PATH_MAX]; //FIXME use ser_atom
+ const LV2_Atom *atom = (const LV2_Atom *)buf;
+ LV2_Atom_Forge_Frame frame;
+
+ lv2_atom_forge_set_buffer(&sb->forge, buf, sizeof(buf));
+ lv2_atom_forge_object(&sb->forge, &frame, 0, sb->patch_Set);
+ lv2_atom_forge_key(&sb->forge, sb->patch_property);
+ lv2_atom_forge_urid(&sb->forge, key);
+ lv2_atom_forge_key(&sb->forge, sb->patch_value);
+ lv2_atom_forge_path(&sb->forge, path, strlen(path));
+ lv2_atom_forge_pop(&sb->forge, &frame);
+
+ _write_function(handle, index, lv2_atom_total_size(atom),
+ sb->atom_eventTransfer, buf);
+ }
+
+ lilv_node_free(patch_message_node);
+ lilv_node_free(core_inputport_node);
+ lilv_node_free(atom_atomport_node);
+
+ return LV2UI_REQUEST_VALUE_SUCCESS;
+ }
+ else if( (type == sb->forge.String)
+ || (type == sb->forge.Chunk) )
+ {
+ const uint32_t nports = lilv_plugin_get_num_ports(sb->plug);
+ LilvNode *patch_message_node = lilv_new_uri(sb->world, LV2_PATCH__Message);
+ LilvNode *core_inputport_node = lilv_new_uri(sb->world, LV2_CORE__InputPort);
+ LilvNode *atom_atomport_node = lilv_new_uri(sb->world, LV2_ATOM__AtomPort);
+
+ for(uint32_t index= 0; index < nports; index++)
+ {
+ const LilvPort *port = lilv_plugin_get_port_by_index(sb->plug, index);
+
+ if(!lilv_port_is_a(sb->plug, port, core_inputport_node))
+ {
+ continue;
+ }
+
+ if(!lilv_port_is_a(sb->plug, port, atom_atomport_node))
+ {
+ continue;
+ }
+
+ if(!lilv_port_supports_event(sb->plug, port, patch_message_node))
+ {
+ continue;
+ }
+
+ size_t body_len= 0;
+ uint8_t *body = _file_read(path, &body_len);
+
+ if(!body)
+ {
+ continue;
+ }
+
+ const size_t buf_len = 1024 + body_len; //FIXME use ser_atom
+ uint8_t *buf = malloc(buf_len);
+
+ if(!buf)
+ {
+ free(body);
+ continue;
+ }
+
+ memset(buf, 0x0, buf_len);
+
+ const LV2_Atom *atom = (const LV2_Atom *)buf;
+ LV2_Atom_Forge_Frame frame;
+
+ lv2_atom_forge_set_buffer(&sb->forge, buf, buf_len);
+ lv2_atom_forge_object(&sb->forge, &frame, 0, sb->patch_Set);
+ lv2_atom_forge_key(&sb->forge, sb->patch_property);
+ lv2_atom_forge_urid(&sb->forge, key);
+ lv2_atom_forge_key(&sb->forge, sb->patch_value);
+ lv2_atom_forge_atom(&sb->forge, body_len, type);
+ lv2_atom_forge_write(&sb->forge, body, body_len);
+ lv2_atom_forge_pop(&sb->forge, &frame);
+
+ _write_function(handle, index, lv2_atom_total_size(atom),
+ sb->atom_eventTransfer, buf);
+
+ free(body);
+ free(buf);
+ }
+
+ lilv_node_free(core_inputport_node);
+ lilv_node_free(atom_atomport_node);
+ lilv_node_free(patch_message_node);
+
+ return LV2UI_REQUEST_VALUE_SUCCESS;
+ }
+
+ return LV2UI_REQUEST_VALUE_ERR_UNKNOWN;
+}
+
static inline bool
_sandbox_recv_cb(LV2UI_Handle handle, uint32_t index, uint32_t size,
uint32_t protocol, const void *buf)
@@ -279,7 +501,9 @@ _sandbox_recv_cb(LV2UI_Handle handle, uint32_t index, uint32_t size,
sandbox_slave_t *sb = handle;
if(sb->desc && sb->desc->port_event)
+ {
sb->desc->port_event(sb->handle, index, size, protocol, buf);
+ }
return true; // continue handling messages
}
@@ -321,12 +545,17 @@ sandbox_slave_new(int argc, char **argv, const sandbox_slave_driver_t *driver,
sb->minimum = 0x100000; // fall-back
sb->sample_rate = 44100.f; // fall-back
sb->update_rate = 25.f; // fall-back
+ sb->scale_factor = 1.f; // fall-back
+ sb->background_color = 0x222222ff; // fall-back
+ sb->foreground_color = 0xccccccff; // fall-back
fprintf(stderr,
"Synthpod "SYNTHPOD_VERSION"\n"
"Copyright (c) 2015-2016 Hanspeter Portner (dev@open-music-kontrollers.ch)\n"
"Released under Artistic License 2.0 by Open Music Kontrollers\n");
+ optind = 1; // needed when called from thread that already ran getopt
+
int c;
while((c = getopt(argc, argv, "vhtn:p:P:u:U:s:w:m:r:f:")) != -1)
{
@@ -407,11 +636,17 @@ sandbox_slave_new(int argc, char **argv, const sandbox_slave_driver_t *driver,
break;
case '?':
if( (optopt == 'n') || (optopt == 'p') || (optopt == 'P') || (optopt == 'u') || (optopt == 'U') || (optopt == 's') || (optopt == 'w') || (optopt == 'm') || (optopt == 'r') || (optopt == 'f') )
+ {
fprintf(stderr, "Option `-%c' requires an argument.\n", optopt);
+ }
else if(isprint(optopt))
+ {
fprintf(stderr, "Unknown option `-%c'.\n", optopt);
+ }
else
+ {
fprintf(stderr, "Unknown option character `\\x%x'.\n", optopt);
+ }
goto fail;
default:
goto fail;
@@ -456,6 +691,9 @@ sandbox_slave_new(int argc, char **argv, const sandbox_slave_driver_t *driver,
sb->touch.handle = sb;
sb->touch.touch = _touch;
+ sb->request_value.handle = sb;
+ sb->request_value.request= _request_value;
+
sb->host_resize.handle = data;
sb->host_resize.ui_resize = driver->resize_cb;
@@ -470,6 +708,13 @@ sandbox_slave_new(int argc, char **argv, const sandbox_slave_driver_t *driver,
sb->uri_id.callback_data = sb;
sb->uri_id.uri_to_id = _sb_uri_to_id;
+ lv2_atom_forge_init(&sb->forge, sb->map);
+
+ sb->atom_eventTransfer = sb->map->map(sb->map->handle, LV2_ATOM__eventTransfer);
+ sb->patch_Set = sb->map->map(sb->map->handle, LV2_PATCH__Set);
+ sb->patch_property = sb->map->map(sb->map->handle, LV2_PATCH__property);
+ sb->patch_value = sb->map->map(sb->map->handle, LV2_PATCH__value);
+
sb->log_trace = sb->map->map(sb->map->handle, LV2_LOG__Trace);
sb->log_error = sb->map->map(sb->map->handle, LV2_LOG__Error);
sb->log_warning = sb->map->map(sb->map->handle, LV2_LOG__Warning);
@@ -576,7 +821,9 @@ sandbox_slave_new(int argc, char **argv, const sandbox_slave_driver_t *driver,
{
const LV2UI_Descriptor *desc = desc_func(i);
if(!desc) // sentinel
+ {
break;
+ }
if(!strcmp(desc->URI, sb->ui_uri))
{
@@ -679,8 +926,33 @@ sandbox_slave_free(sandbox_slave_t *sb)
}
void *
-sandbox_slave_instantiate(sandbox_slave_t *sb, const LV2_Feature *parent_feature, void *widget)
+sandbox_slave_instantiate(sandbox_slave_t *sb, const LV2_Feature *parent_feature,
+ void *_dsp_instance, void *widget)
{
+ const intptr_t dummy = 1;
+ LilvInstance *dsp_instance = _dsp_instance;
+
+ void *dsp_handle = NULL;
+ const LV2_Descriptor *desc = NULL;
+
+ if(dsp_instance)
+ {
+ if((intptr_t)dsp_instance == dummy)
+ {
+ dsp_handle = dsp_instance;
+ }
+ else
+ {
+ dsp_handle = lilv_instance_get_handle(dsp_instance);
+ desc = lilv_instance_get_descriptor(dsp_instance);
+
+ if(desc && desc->extension_data)
+ {
+ sb->data_access.data_access = desc->extension_data;
+ }
+ }
+ }
+
LV2_Options_Option options [] = {
[0] = {
.context = LV2_OPTIONS_INSTANCE,
@@ -707,6 +979,30 @@ sandbox_slave_instantiate(sandbox_slave_t *sb, const LV2_Feature *parent_feature
.value = &sb->update_rate
},
[3] = {
+ .context = LV2_OPTIONS_INSTANCE,
+ .subject = 0,
+ .key = sb->io.ui_scale_factor,
+ .size = sizeof(float),
+ .type = sb->io.forge.Float,
+ .value = &sb->scale_factor
+ },
+ [4] = {
+ .context = LV2_OPTIONS_INSTANCE,
+ .subject = 0,
+ .key = sb->io.ui_background_color,
+ .size = sizeof(int32_t),
+ .type = sb->io.forge.Int,
+ .value = &sb->background_color
+ },
+ [5] = {
+ .context = LV2_OPTIONS_INSTANCE,
+ .subject = 0,
+ .key = sb->io.ui_foreground_color,
+ .size = sizeof(int32_t),
+ .type = sb->io.forge.Int,
+ .value = &sb->foreground_color
+ },
+ [6] = {
.key = 0,
.value = NULL
}
@@ -740,6 +1036,10 @@ sandbox_slave_instantiate(sandbox_slave_t *sb, const LV2_Feature *parent_feature
.URI = LV2_UI__touch,
.data = &sb->touch
};
+ const LV2_Feature request_value_feature = {
+ .URI = LV2_UI__requestValue,
+ .data = &sb->request_value
+ };
const LV2_Feature options_feature = {
.URI = LV2_OPTIONS__options,
.data = options
@@ -752,22 +1052,48 @@ sandbox_slave_instantiate(sandbox_slave_t *sb, const LV2_Feature *parent_feature
.URI = LV2_UI__resize,
.data = &sb->host_resize
};
-
- const LV2_Feature *const features [] = {
- &map_feature,
- &unmap_feature,
- &uri_id_feature,
- &log_feature,
- &port_map_feature,
- &port_subscribe_feature,
- &touch_feature,
- &options_feature,
- &voice_map_feature,
- sb->host_resize.ui_resize ? &resize_feature : parent_feature,
- sb->host_resize.ui_resize && parent_feature ? parent_feature : NULL,
- NULL
+ const LV2_Feature instance_access_feature = {
+ .URI = LV2_INSTANCE_ACCESS_URI,
+ .data = dsp_handle
+ };
+ const LV2_Feature data_access_feature = {
+ .URI = LV2_DATA_ACCESS_URI,
+ .data = &sb->data_access
};
+ unsigned i = 0;
+ const LV2_Feature *features [16];
+ features[i++] = &map_feature;
+ features[i++] = &unmap_feature;
+ features[i++] = &uri_id_feature;
+ features[i++] = &log_feature;
+ features[i++] = &port_map_feature;
+ features[i++] = &port_subscribe_feature;
+ features[i++] = &touch_feature;
+ features[i++] = &request_value_feature;
+ features[i++] = &options_feature;
+ features[i++] = &voice_map_feature;
+ if(sb->host_resize.ui_resize)
+ {
+ features[i++] = &resize_feature;
+
+ if(parent_feature)
+ {
+ features[i++] = parent_feature;
+ }
+ }
+ if(dsp_instance)
+ {
+ features[i++] = &instance_access_feature;
+ }
+ if(sb->data_access.data_access)
+ {
+ features[i++] = &data_access_feature;
+ }
+
+ features[i] = NULL;
+ assert(i <= 16);
+
//FIXME check features
const LilvNode *ui_bundle_uri = lilv_ui_get_bundle_uri(sb->ui);
@@ -788,7 +1114,9 @@ sandbox_slave_instantiate(sandbox_slave_t *sb, const LV2_Feature *parent_feature
#endif
if(sb->handle)
+ {
return sb->handle; // success
+ }
return NULL;
}
@@ -797,7 +1125,9 @@ int
sandbox_slave_recv(sandbox_slave_t *sb)
{
if(sb)
+ {
return _sandbox_io_recv(&sb->io, _sandbox_recv_cb, NULL, sb);
+ }
return -1;
}
@@ -818,7 +1148,9 @@ const void *
sandbox_slave_extension_data(sandbox_slave_t *sb, const char *URI)
{
if(sb && sb->desc && sb->desc->extension_data)
+ {
return sb->desc->extension_data(URI);
+ }
return NULL;
}
@@ -827,14 +1159,18 @@ void
sandbox_slave_run(sandbox_slave_t *sb)
{
if(sb && sb->driver && sb->driver->run_cb)
+ {
sb->driver->run_cb(sb, sb->update_rate, sb->data);
+ }
}
const char *
sandbox_slave_title_get(sandbox_slave_t *sb)
{
if(sb)
+ {
return sb->window_title;
+ }
return NULL;
}
@@ -843,7 +1179,18 @@ bool
sandbox_slave_no_user_resize_get(sandbox_slave_t *sb)
{
if(sb)
+ {
return sb->no_user_resize;
+ }
return false;
}
+
+void
+sandbox_slave_scale_factor_set(sandbox_slave_t *sb, float scale_factor)
+{
+ if(sb)
+ {
+ sb->scale_factor = scale_factor;
+ }
+}
diff --git a/sandbox_ui.lv2/sandbox_slave.h b/sandbox_ui.lv2/sandbox_slave.h
index 0a60badb..2cf5a4fb 100644
--- a/sandbox_ui.lv2/sandbox_slave.h
+++ b/sandbox_ui.lv2/sandbox_slave.h
@@ -18,7 +18,11 @@
#ifndef _SANDBOX_SLAVE_H
#define _SANDBOX_SLAVE_H
+#include <stddef.h>
+#include <stdbool.h>
+
#include <lv2/lv2plug.in/ns/lv2core/lv2.h>
+#include <lv2/lv2plug.in/ns/ext/urid/urid.h>
#ifdef __cplusplus
extern "C" {
@@ -31,12 +35,15 @@ typedef int (*sandbox_slave_driver_init_t)(sandbox_slave_t *sb, void *data);
typedef void (*sandbox_slave_driver_run_t)(sandbox_slave_t *sb, float update_rate, void *data);
typedef void (*sandbox_slave_driver_deinit_t)(void *data);
typedef int (*sandbox_slave_driver_resize_t)(void *data, int width, int height);
+typedef int (*sandbox_slave_driver_request_t)(void *data, LV2_URID key,
+ size_t len, char *path);
struct _sandbox_slave_driver_t {
sandbox_slave_driver_init_t init_cb;
sandbox_slave_driver_run_t run_cb;
sandbox_slave_driver_deinit_t deinit_cb;
sandbox_slave_driver_resize_t resize_cb;
+ sandbox_slave_driver_request_t request_cb;
};
sandbox_slave_t *
@@ -47,7 +54,8 @@ void
sandbox_slave_free(sandbox_slave_t *sb);
void *
-sandbox_slave_instantiate(sandbox_slave_t *sb, const LV2_Feature *parent_feature, void *widget);
+sandbox_slave_instantiate(sandbox_slave_t *sb, const LV2_Feature *parent_feature,
+ void *dsp_instance, void *widget);
int
sandbox_slave_recv(sandbox_slave_t *sb);
@@ -70,6 +78,9 @@ sandbox_slave_title_get(sandbox_slave_t *sb);
bool
sandbox_slave_no_user_resize_get(sandbox_slave_t *sb);
+void
+sandbox_slave_scale_factor_set(sandbox_slave_t *sb, float scale_factor);
+
#ifdef __cplusplus
}
#endif
diff --git a/subprojects/d2tk/.gitlab-ci.yml b/subprojects/d2tk/.gitlab-ci.yml
index ec929aac..86273442 100644
--- a/subprojects/d2tk/.gitlab-ci.yml
+++ b/subprojects/d2tk/.gitlab-ci.yml
@@ -2,6 +2,7 @@ stages:
- build
- deploy
+# templates
.variables_template: &variables_definition
variables:
BASE_NAME: "d2tk"
@@ -17,6 +18,7 @@ stages:
.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
@@ -25,21 +27,26 @@ stages:
.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
- - ninja -C build test
+
+ - 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
- - ninja -C build test
+
+ - 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
@@ -50,40 +57,40 @@ stages:
- scan-build --status-bugs ninja -C scanbuild test
.universal_linux_template: &universal_linux_definition
- image: ventosus/universal-linux-gnu
+ image: ventosus/universal-linux-gnu:buster
<<: *analyze_definition
.arm_linux_template: &arm_linux_definition
- image: ventosus/arm-linux-gnueabihf
+ image: ventosus/arm-linux-gnueabihf:buster
<<: *test_definition
.universal_w64_template: &universal_w64_definition
image: ventosus/universal-w64-mingw32
- <<: *build_definition
+ <<: *test_definition
.universal_apple_template: &universal_apple_definition
image: ventosus/universal-apple-darwin
- <<: *test_definition
+ <<: *build_definition
-# building in docker
+# targets
x86_64-linux-gnu:
before_script:
- - apt-get install -y libglu1-mesa-dev libevdev-dev
+ - 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
+ - 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
+ - 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
+ - apt-get install -y libglu1-mesa-dev:arm64 libevdev-dev:arm64 libvterm-dev:arm64
<<: *arm_linux_definition
x86_64-w64-mingw32:
@@ -106,7 +113,6 @@ pack:
- "${BASE_NAME}-$(cat VERSION)/"
pages:
- <<: *variables_definition
stage: deploy
before_script:
- apt-get update -y
diff --git a/subprojects/d2tk/README.md b/subprojects/d2tk/README.md
index 63e12bc0..51c9529b 100644
--- a/subprojects/d2tk/README.md
+++ b/subprojects/d2tk/README.md
@@ -28,21 +28,21 @@ and on-demand rendered sprites.
### Screenshots
-![Screenshot 1](https://git.open-music-kontrollers.ch/lad/d2tk/plain/screenshots/screenshot_1.png)
+![Screenshot 1](/screenshots/screenshot_1.png)
-![Screenshot 2](https://git.open-music-kontrollers.ch/lad/d2tk/plain/screenshots/screenshot_2.png)
+![Screenshot 2](/screenshots/screenshot_2.png)
-![Screenshot 3](https://git.open-music-kontrollers.ch/lad/d2tk/plain/screenshots/screenshot_3.png)
+![Screenshot 3](/screenshots/screenshot_3.png)
-![Screenshot 4](https://git.open-music-kontrollers.ch/lad/d2tk/plain/screenshots/screenshot_4.png)
+![Screenshot 4](/screenshots/screenshot_4.png)
-![Screenshot 5](https://git.open-music-kontrollers.ch/lad/d2tk/plain/screenshots/screenshot_5.png)
+![Screenshot 5](/screenshots/screenshot_5.png)
-![Screenshot 6](https://git.open-music-kontrollers.ch/lad/d2tk/plain/screenshots/screenshot_6.png)
+![Screenshot 6](/screenshots/screenshot_6.png)
-![Screenshot 7](https://git.open-music-kontrollers.ch/lad/d2tk/plain/screenshots/screenshot_7.png)
+![Screenshot 7](/screenshots/screenshot_7.png)
-![Screenshot 8](https://git.open-music-kontrollers.ch/lad/d2tk/plain/screenshots/screenshot_8.png)
+![Screenshot 8](/screenshots/screenshot_8.png)
### License
diff --git a/subprojects/d2tk/VERSION b/subprojects/d2tk/VERSION
index 33a73c49..735baec8 100644
--- a/subprojects/d2tk/VERSION
+++ b/subprojects/d2tk/VERSION
@@ -1 +1 @@
-0.1.783
+0.1.1089
diff --git a/subprojects/d2tk/check_for_font b/subprojects/d2tk/check_for_font
new file mode 100755
index 00000000..befcbf40
--- /dev/null
+++ b/subprojects/d2tk/check_for_font
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+fc-list | grep "$1"
diff --git a/subprojects/d2tk/d2tk/base.h b/subprojects/d2tk/d2tk/base.h
index 048b91df..0926b6d1 100644
--- a/subprojects/d2tk/d2tk/base.h
+++ b/subprojects/d2tk/d2tk/base.h
@@ -19,9 +19,13 @@
#define _D2TK_BASE_H
#include <stdlib.h>
+#include <sys/types.h>
+#include <stdio.h>
#include <d2tk/core.h>
+#include <utf8.h/utf8.h>
+
#ifdef __cplusplus
extern "C" {
#endif
@@ -37,6 +41,7 @@ typedef struct _d2tk_flowmatrix_t d2tk_flowmatrix_t;
typedef struct _d2tk_flowmatrix_node_t d2tk_flowmatrix_node_t;
typedef struct _d2tk_flowmatrix_arc_t d2tk_flowmatrix_arc_t;
typedef struct _d2tk_pane_t d2tk_pane_t;
+typedef struct _d2tk_pty_t d2tk_pty_t;
typedef struct _d2tk_base_t d2tk_base_t;
struct _d2tk_pos_t {
@@ -60,6 +65,43 @@ typedef enum _d2tk_triple_t {
D2TK_TRIPLE_MAX
} d2tk_triple_t;
+typedef enum _d2tk_keymask_t {
+ D2TK_KEYMASK_NONE = 0,
+
+ D2TK_KEYMASK_ENTER = (1 << 0),
+ D2TK_KEYMASK_TAB = (1 << 1),
+ D2TK_KEYMASK_BACKSPACE = (1 << 2),
+ D2TK_KEYMASK_ESCAPE = (1 << 3),
+
+ D2TK_KEYMASK_UP = (1 << 4),
+ D2TK_KEYMASK_DOWN = (1 << 5),
+ D2TK_KEYMASK_LEFT = (1 << 6),
+ D2TK_KEYMASK_RIGHT = (1 << 7),
+
+ D2TK_KEYMASK_INS = (1 << 8),
+ D2TK_KEYMASK_DEL = (1 << 9),
+ D2TK_KEYMASK_HOME = (1 << 10),
+ D2TK_KEYMASK_END = (1 << 11),
+ D2TK_KEYMASK_PAGEUP = (1 << 12),
+ D2TK_KEYMASK_PAGEDOWN = (1 << 13)
+} d2tk_keymask_t;
+
+typedef enum _d2tk_modmask_t {
+ D2TK_MODMASK_NONE = 0,
+
+ D2TK_MODMASK_SHIFT = (1 << 0),
+ D2TK_MODMASK_ALT = (1 << 1),
+ D2TK_MODMASK_CTRL = (1 << 2)
+} d2tk_modmask_t;
+
+typedef enum _d2tk_butmask_t {
+ D2TK_BUTMASK_NONE = 0,
+
+ D2TK_BUTMASK_LEFT = (1 << 0),
+ D2TK_BUTMASK_MIDDLE = (1 << 1),
+ D2TK_BUTMASK_RIGHT = (1 << 2)
+} d2tk_butmask_t;
+
struct _d2tk_style_t {
const char *font_face;
uint32_t border_width;
@@ -68,7 +110,8 @@ struct _d2tk_style_t {
uint32_t bg_color;
uint32_t fill_color [D2TK_TRIPLE_MAX];
uint32_t stroke_color [D2TK_TRIPLE_MAX];
- uint32_t text_color [D2TK_TRIPLE_MAX];
+ uint32_t text_fill_color [D2TK_TRIPLE_MAX];
+ uint32_t text_stroke_color [D2TK_TRIPLE_MAX];
};
typedef enum _d2tk_state_t {
@@ -87,7 +130,9 @@ typedef enum _d2tk_state_t {
D2TK_STATE_MOTION = (1 << 11),
D2TK_STATE_CHANGED = (1 << 12),
D2TK_STATE_ENTER = (1 << 13),
- D2TK_STATE_OVER = (1 << 14)
+ D2TK_STATE_OVER = (1 << 14),
+ D2TK_STATE_CLOSE = (1 << 15),
+ D2TK_STATE_BELL = (1 << 16)
} d2tk_state_t;
typedef enum _d2tk_flag_t {
@@ -109,9 +154,9 @@ typedef enum _d2tk_flag_t {
D2TK_FLAG_TABLE_REL = (1 << 9),
} d2tk_flag_t;
-#define D2TK_ID_IDX(IDX) ((d2tk_id_t)__LINE__ << 16) | (IDX)
+#define D2TK_ID_IDX(IDX) ( ((d2tk_id_t)__LINE__ << 16) | (IDX) )
#define D2TK_ID_FILE_IDX(IDX) \
- ((d2tk_id_t)d2tk_hash(__FILE__, -1) << 32) | D2TK_ID_IDX((IDX))
+ ( ((d2tk_id_t)d2tk_hash(__FILE__, -1) << 32) | D2TK_ID_IDX((IDX)) )
#define D2TK_ID D2TK_ID_IDX(0)
#define D2TK_ID_FILE D2TK_ID_FILE_IDX(0)
@@ -123,6 +168,7 @@ extern const size_t d2tk_flowmatrix_sz;
extern const size_t d2tk_flowmatrix_node_sz;
extern const size_t d2tk_flowmatrix_arc_sz;
extern const size_t d2tk_pane_sz;
+extern const size_t d2tk_pty_sz;
D2TK_API d2tk_table_t *
d2tk_table_begin(const d2tk_rect_t *rect, unsigned N, unsigned M,
@@ -195,7 +241,7 @@ d2tk_layout_get_rect(d2tk_layout_t *lay);
D2TK_API d2tk_scrollbar_t *
d2tk_scrollbar_begin(d2tk_base_t *base, const d2tk_rect_t *rect, d2tk_id_t id,
- d2tk_flag_t flags, int32_t hmax, int32_t vmax, int32_t hnum, int32_t vnum,
+ d2tk_flag_t flags, const uint32_t max [2], const uint32_t num [2],
d2tk_scrollbar_t *scrollbar);
D2TK_API bool
@@ -213,11 +259,9 @@ d2tk_scrollbar_get_offset_x(d2tk_scrollbar_t *scrollbar);
D2TK_API const d2tk_rect_t *
d2tk_scrollbar_get_rect(d2tk_scrollbar_t *scrollbar);
-#define D2TK_BASE_SCROLLBAR(BASE, RECT, ID, FLAGS, HMAX, VMAX, HNUM, VNUM, \
- SCROLLBAR) \
+#define D2TK_BASE_SCROLLBAR(BASE, RECT, ID, FLAGS, MAX, NUM, SCROLLBAR) \
for(d2tk_scrollbar_t *(SCROLLBAR) = d2tk_scrollbar_begin((BASE), (RECT), \
- (ID), (FLAGS), (HMAX), (VMAX), (HNUM), (VNUM), \
- alloca(d2tk_scrollbar_sz)); \
+ (ID), (FLAGS), (MAX), (NUM), alloca(d2tk_scrollbar_sz)); \
d2tk_scrollbar_not_end((SCROLLBAR)); \
(SCROLLBAR) = d2tk_scrollbar_next((BASE), (SCROLLBAR)))
@@ -259,15 +303,6 @@ D2TK_API void
d2tk_clip_double(double min, double *value, double max);
D2TK_API bool
-d2tk_base_get_shift(d2tk_base_t *base);
-
-D2TK_API bool
-d2tk_base_get_ctrl(d2tk_base_t *base);
-
-D2TK_API bool
-d2tk_base_get_alt(d2tk_base_t *base);
-
-D2TK_API bool
d2tk_base_get_mod(d2tk_base_t *base);
D2TK_API const char *
@@ -319,10 +354,19 @@ D2TK_API bool
d2tk_state_is_over(d2tk_state_t state);
D2TK_API bool
+d2tk_state_is_close(d2tk_state_t state);
+
+D2TK_API bool
+d2tk_state_is_bell(d2tk_state_t state);
+
+D2TK_API bool
d2tk_base_is_hit(d2tk_base_t *base, const d2tk_rect_t *rect);
D2TK_API void
-d2tk_base_append_char(d2tk_base_t *base, int ch);
+d2tk_base_append_utf8(d2tk_base_t *base, utf8_int32_t utf8);
+
+D2TK_API void
+d2tk_base_get_utf8(d2tk_base_t *base, ssize_t *len, const utf8_int32_t **utf8);
D2TK_API d2tk_state_t
d2tk_base_is_active_hot(d2tk_base_t *base, d2tk_id_t id,
@@ -372,6 +416,14 @@ d2tk_base_button(d2tk_base_t *base, d2tk_id_t id, const d2tk_rect_t *rect);
d2tk_state_is_changed(d2tk_base_button(__VA_ARGS__))
D2TK_API d2tk_state_t
+d2tk_base_toggle_label_image(d2tk_base_t *base, d2tk_id_t id, ssize_t lbl_len,
+ const char *lbl, d2tk_align_t align, ssize_t path_len, const char *path,
+ const d2tk_rect_t *rect, bool *value);
+
+#define d2tk_base_toggle_label_image_is_changed(...) \
+ d2tk_state_is_changed(d2tk_base_toggle_label_image(__VA_ARGS__))
+
+D2TK_API d2tk_state_t
d2tk_base_toggle_label(d2tk_base_t *base, d2tk_id_t id, ssize_t lbl_len,
const char *lbl, d2tk_align_t align, const d2tk_rect_t *rect, bool *value);
@@ -430,6 +482,41 @@ d2tk_base_link(d2tk_base_t *base, d2tk_id_t id, ssize_t lbl_len, const char *lbl
#define d2tk_base_link_is_changed(...) \
d2tk_state_is_changed(d2tk_base_link(__VA_ARGS__))
+#if D2TK_PTY
+D2TK_API d2tk_pty_t *
+d2tk_pty_begin(d2tk_base_t *base, d2tk_id_t id, char **argv,
+ d2tk_coord_t height, const d2tk_rect_t *rect, bool reinit, d2tk_pty_t *pty);
+
+D2TK_API bool
+d2tk_pty_not_end(d2tk_pty_t *pty);
+
+D2TK_API d2tk_pty_t *
+d2tk_pty_next(d2tk_pty_t *pty);
+
+D2TK_API d2tk_state_t
+d2tk_pty_get_state(d2tk_pty_t *pty);
+
+D2TK_API uint32_t
+d2tk_pty_get_max_red(d2tk_pty_t *pty);
+
+D2TK_API uint32_t
+d2tk_pty_get_max_green(d2tk_pty_t *pty);
+
+D2TK_API uint32_t
+d2tk_pty_get_max_blue(d2tk_pty_t *pty);
+
+#define D2TK_BASE_PTY(BASE, ID, ARGV, HEIGHT, RECT, REINIT, PTY) \
+ for(d2tk_pty_t *(PTY) = d2tk_pty_begin((BASE), (ID), (ARGV), (HEIGHT), \
+ (RECT), (REINIT), alloca(d2tk_pty_sz)); \
+ d2tk_pty_not_end((PTY)); \
+ (PTY) = d2tk_pty_next((PTY)))
+#endif
+
+#if D2TK_EVDEV
+D2TK_API d2tk_state_t
+d2tk_base_vkb(d2tk_base_t *base, d2tk_id_t id, const d2tk_rect_t *rect);
+#endif
+
D2TK_API d2tk_state_t
d2tk_base_dial_bool(d2tk_base_t *base, d2tk_id_t id, const d2tk_rect_t *rect,
bool *value);
@@ -479,6 +566,55 @@ d2tk_base_dial_double(d2tk_base_t *base, d2tk_id_t id, const d2tk_rect_t *rect,
#define d2tk_base_dial_double_is_changed(...) \
d2tk_state_is_changed(d2tk_base_dial_double(__VA_ARGS__))
+D2TK_API d2tk_state_t
+d2tk_base_spinner_bool(d2tk_base_t *base, d2tk_id_t id, const d2tk_rect_t *rect,
+ ssize_t lbl_len, const char *lbl, bool *value);
+
+#define d2tk_base_spinner_bool_is_changed(...) \
+ d2tk_state_is_changed(d2tk_base_spinner_bool(__VA_ARGS__))
+
+D2TK_API d2tk_state_t
+d2tk_base_spinner_int32(d2tk_base_t *base, d2tk_id_t id, const d2tk_rect_t *rect,
+ ssize_t lbl_len, const char *lbl, int32_t min, int32_t *value, int32_t max);
+
+#define d2tk_base_spinner_int32_is_changed(...) \
+ d2tk_state_is_changed(d2tk_base_spinner_int32(__VA_ARGS__))
+
+D2TK_API d2tk_state_t
+d2tk_base_bar_int32(d2tk_base_t *base, d2tk_id_t id, const d2tk_rect_t *rect,
+ int32_t min, int32_t *value, int32_t max);
+
+#define d2tk_base_bar_int32_is_changed(...) \
+ d2tk_state_is_changed(d2tk_base_bar_int32(__VA_ARGS__))
+
+D2TK_API d2tk_state_t
+d2tk_base_spinner_float(d2tk_base_t *base, d2tk_id_t id, const d2tk_rect_t *rect,
+ ssize_t lbl_len, const char *lbl, float min, float *value, float max);
+
+#define d2tk_base_spinner_float_is_changed(...) \
+ d2tk_state_is_changed(d2tk_base_spinner_float(__VA_ARGS__))
+
+D2TK_API d2tk_state_t
+d2tk_base_spinner_wave_float(d2tk_base_t *base, d2tk_id_t id, const d2tk_rect_t *rect,
+ ssize_t lbl_len, const char *lbl, float min, const float *value, int32_t nelem, float max);
+
+#define d2tk_base_spinner_wave_float_is_changed(...) \
+ d2tk_state_is_changed(d2tk_base_spinner_wave_float(__VA_ARGS__))
+
+D2TK_API d2tk_state_t
+d2tk_base_bar_float(d2tk_base_t *base, d2tk_id_t id, const d2tk_rect_t *rect,
+ float min, float *value, float max);
+
+#define d2tk_base_bar_float_is_changed(...) \
+ d2tk_state_is_changed(d2tk_base_bar_float(__VA_ARGS__))
+
+D2TK_API d2tk_state_t
+d2tk_base_wave_float(d2tk_base_t *base, d2tk_id_t id, const d2tk_rect_t *rect,
+ float min, const float *value, int32_t nelem, float max);
+
+#define d2tk_base_wave_float_is_changed(...) \
+ d2tk_state_is_changed(d2tk_base_wave_float(__VA_ARGS__))
+
D2TK_API d2tk_flowmatrix_t *
d2tk_flowmatrix_begin(d2tk_base_t *base, const d2tk_rect_t *rect, d2tk_id_t id,
d2tk_flowmatrix_t *flowmatrix);
@@ -568,13 +704,16 @@ d2tk_base_set_ttls(d2tk_base_t *base, uint32_t sprites, uint32_t memcaches);
D2TK_API void
d2tk_base_free(d2tk_base_t *base);
-D2TK_API void
-d2tk_base_pre(d2tk_base_t *base);
+D2TK_API int
+d2tk_base_pre(d2tk_base_t *base, void *pctx);
D2TK_API void
d2tk_base_post(d2tk_base_t *base);
D2TK_API void
+d2tk_base_probe(d2tk_base_t *base);
+
+D2TK_API bool
d2tk_base_set_again(d2tk_base_t *base);
D2TK_API void
@@ -583,14 +722,17 @@ d2tk_base_clear_focus(d2tk_base_t *base);
D2TK_API bool
d2tk_base_get_again(d2tk_base_t *base);
-D2TK_API void
-d2tk_base_set_mouse_l(d2tk_base_t *base, bool down);
+D2TK_API bool
+d2tk_base_set_butmask(d2tk_base_t *base, d2tk_butmask_t mask, bool down);
-D2TK_API void
-d2tk_base_set_mouse_m(d2tk_base_t *base, bool down);
+D2TK_API bool
+d2tk_base_get_butmask(d2tk_base_t *base, d2tk_butmask_t mask, bool clear);
-D2TK_API void
-d2tk_base_set_mouse_r(d2tk_base_t *base, bool down);
+D2TK_API bool
+d2tk_base_get_butmask_down(d2tk_base_t *base, d2tk_butmask_t mask);
+
+D2TK_API bool
+d2tk_base_get_butmask_up(d2tk_base_t *base, d2tk_butmask_t mask);
D2TK_API void
d2tk_base_set_mouse_pos(d2tk_base_t *base, d2tk_coord_t x, d2tk_coord_t y);
@@ -602,37 +744,26 @@ D2TK_API void
d2tk_base_add_mouse_scroll(d2tk_base_t *base, int32_t dx, int32_t dy);
D2TK_API void
-d2tk_base_set_shift(d2tk_base_t *base, bool down);
-
-D2TK_API void
-d2tk_base_set_ctrl(d2tk_base_t *base, bool down);
-
-D2TK_API void
-d2tk_base_set_alt(d2tk_base_t *base, bool down);
+d2tk_base_get_mouse_scroll(d2tk_base_t *base, int32_t *dx, int32_t *dy,
+ bool clear);
-D2TK_API void
-d2tk_base_set_left(d2tk_base_t *base, bool down);
-
-D2TK_API void
-d2tk_base_set_right(d2tk_base_t *base, bool down);
-
-D2TK_API void
-d2tk_base_set_up(d2tk_base_t *base, bool down);
+D2TK_API bool
+d2tk_base_set_modmask(d2tk_base_t *base, d2tk_modmask_t mask, bool down);
-D2TK_API void
-d2tk_base_set_down(d2tk_base_t *base, bool down);
+D2TK_API bool
+d2tk_base_get_modmask(d2tk_base_t *base, d2tk_modmask_t mask, bool clear);
D2TK_API bool
-d2tk_base_get_left(d2tk_base_t *base);
+d2tk_base_set_keymask(d2tk_base_t *base, d2tk_keymask_t mask, bool down);
D2TK_API bool
-d2tk_base_get_right(d2tk_base_t *base);
+d2tk_base_get_keymask(d2tk_base_t *base, d2tk_keymask_t mask, bool clear);
D2TK_API bool
-d2tk_base_get_up(d2tk_base_t *base);
+d2tk_base_get_keymask_down(d2tk_base_t *base, d2tk_keymask_t mask);
D2TK_API bool
-d2tk_base_get_down(d2tk_base_t *base);
+d2tk_base_get_keymask_up(d2tk_base_t *base, d2tk_keymask_t mask);
D2TK_API void
d2tk_base_set_dimensions(d2tk_base_t *base, d2tk_coord_t w, d2tk_coord_t h);
@@ -640,6 +771,9 @@ d2tk_base_set_dimensions(d2tk_base_t *base, d2tk_coord_t w, d2tk_coord_t h);
D2TK_API void
d2tk_base_get_dimensions(d2tk_base_t *base, d2tk_coord_t *w, d2tk_coord_t *h);
+D2TK_API void
+d2tk_base_set_full_refresh(d2tk_base_t *base);
+
#ifdef __cplusplus
}
#endif
diff --git a/subprojects/d2tk/d2tk/config.h.in b/subprojects/d2tk/d2tk/config.h.in
new file mode 100644
index 00000000..c1d746ec
--- /dev/null
+++ b/subprojects/d2tk/d2tk/config.h.in
@@ -0,0 +1,6 @@
+#define D2TK_PTY @D2TK_PTY@
+#define D2TK_EVDEV @D2TK_EVDEV@
+#define D2TK_INPUT_1_15 @D2TK_INPUT_1_15@
+#define D2TK_FONTCONFIG @D2TK_FONTCONFIG@
+#define D2TK_VFORK @D2TK_VFORK@
+#define D2TK_CLONE @D2TK_CLONE@
diff --git a/subprojects/d2tk/d2tk/core.h b/subprojects/d2tk/d2tk/core.h
index 903118d0..0b8049b0 100644
--- a/subprojects/d2tk/d2tk/core.h
+++ b/subprojects/d2tk/d2tk/core.h
@@ -21,6 +21,7 @@
#include <stdint.h>
#include <stdbool.h>
+#include "config.h"
#include <d2tk/d2tk.h>
#ifdef __cplusplus
@@ -185,8 +186,8 @@ d2tk_core_custom(d2tk_core_t *core, const d2tk_rect_t *rect, uint32_t size,
D2TK_API void
d2tk_core_stroke_width(d2tk_core_t *core, d2tk_coord_t width);
-D2TK_API void
-d2tk_core_pre(d2tk_core_t *core);
+D2TK_API int
+d2tk_core_pre(d2tk_core_t *core, void *pctx);
D2TK_API void
d2tk_core_post(d2tk_core_t *core);
@@ -206,6 +207,9 @@ d2tk_core_set_dimensions(d2tk_core_t *core, d2tk_coord_t w, d2tk_coord_t h);
D2TK_API void
d2tk_core_get_dimensions(d2tk_core_t *core, d2tk_coord_t *w, d2tk_coord_t *h);
+D2TK_API void
+d2tk_core_set_full_refresh(d2tk_core_t *core);
+
#ifdef __cplusplus
}
#endif
diff --git a/subprojects/d2tk/d2tk/frontend.h b/subprojects/d2tk/d2tk/frontend.h
new file mode 100644
index 00000000..eaa2b4a5
--- /dev/null
+++ b/subprojects/d2tk/d2tk/frontend.h
@@ -0,0 +1,63 @@
+/*
+ * 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.
+ */
+
+#ifndef _D2TK_FRONTEND_H
+#define _D2TK_FRONTEND_H
+
+#include <signal.h>
+
+#include <d2tk/base.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef int (*d2tk_frontend_expose_t)(void *data, d2tk_coord_t w, d2tk_coord_t h);
+typedef struct _d2tk_frontend_t d2tk_frontend_t;
+
+D2TK_API void
+d2tk_frontend_free(d2tk_frontend_t *dpugl);
+
+D2TK_API int
+d2tk_frontend_step(d2tk_frontend_t *dpugl);
+
+D2TK_API int
+d2tk_frontend_poll(d2tk_frontend_t *dpugl, double timeout);
+
+D2TK_API void
+d2tk_frontend_run(d2tk_frontend_t *dpugl, const sig_atomic_t *done);
+
+D2TK_API void
+d2tk_frontend_redisplay(d2tk_frontend_t *dpugl);
+
+D2TK_API int
+d2tk_frontend_set_size(d2tk_frontend_t *dpugl, d2tk_coord_t w, d2tk_coord_t h);
+
+D2TK_API int
+d2tk_frontend_get_size(d2tk_frontend_t *dpugl, d2tk_coord_t *w, d2tk_coord_t *h);
+
+D2TK_API d2tk_base_t *
+d2tk_frontend_get_base(d2tk_frontend_t *dpugl);
+
+D2TK_API float
+d2tk_frontend_get_scale();
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // _D2TK_FRONTEND_H
diff --git a/subprojects/d2tk/d2tk/frontend_fbdev.h b/subprojects/d2tk/d2tk/frontend_fbdev.h
index 06a29073..969c6446 100644
--- a/subprojects/d2tk/d2tk/frontend_fbdev.h
+++ b/subprojects/d2tk/d2tk/frontend_fbdev.h
@@ -21,38 +21,24 @@
#include <signal.h>
#include <d2tk/base.h>
+#include <d2tk/frontend.h>
#ifdef __cplusplus
extern "C" {
#endif
-typedef int (*d2tk_fbdev_expose_t)(void *data, d2tk_coord_t w, d2tk_coord_t h);
-
-typedef struct _d2tk_fbdev_t d2tk_fbdev_t;
typedef struct _d2tk_fbdev_config_t d2tk_fbdev_config_t;
struct _d2tk_fbdev_config_t {
const char *fb_device;
const char *bundle_path;
- d2tk_fbdev_expose_t expose;
+ d2tk_frontend_expose_t expose;
void *data;
};
-D2TK_API d2tk_fbdev_t *
+D2TK_API d2tk_frontend_t *
d2tk_fbdev_new(const d2tk_fbdev_config_t *config);
-D2TK_API void
-d2tk_fbdev_free(d2tk_fbdev_t *fbdev);
-
-D2TK_API int
-d2tk_fbdev_step(d2tk_fbdev_t *fbdev);
-
-D2TK_API void
-d2tk_fbdev_run(d2tk_fbdev_t *fbdev, const sig_atomic_t *done);
-
-D2TK_API d2tk_base_t *
-d2tk_fbdev_get_base(d2tk_fbdev_t *fbdev);
-
#ifdef __cplusplus
}
#endif
diff --git a/subprojects/d2tk/d2tk/frontend_pugl.h b/subprojects/d2tk/d2tk/frontend_pugl.h
index bb61980b..4be0b863 100644
--- a/subprojects/d2tk/d2tk/frontend_pugl.h
+++ b/subprojects/d2tk/d2tk/frontend_pugl.h
@@ -21,14 +21,12 @@
#include <signal.h>
#include <d2tk/base.h>
+#include <d2tk/frontend.h>
#ifdef __cplusplus
extern "C" {
#endif
-typedef int (*d2tk_pugl_expose_t)(void *data, d2tk_coord_t w, d2tk_coord_t h);
-
-typedef struct _d2tk_pugl_t d2tk_pugl_t;
typedef struct _d2tk_pugl_config_t d2tk_pugl_config_t;
struct _d2tk_pugl_config_t {
@@ -40,34 +38,13 @@ struct _d2tk_pugl_config_t {
d2tk_coord_t h;
bool fixed_size;
bool fixed_aspect;
- d2tk_pugl_expose_t expose;
+ d2tk_frontend_expose_t expose;
void *data;
};
-D2TK_API d2tk_pugl_t *
+D2TK_API d2tk_frontend_t *
d2tk_pugl_new(const d2tk_pugl_config_t *config, uintptr_t *widget);
-D2TK_API void
-d2tk_pugl_free(d2tk_pugl_t *dpugl);
-
-D2TK_API int
-d2tk_pugl_step(d2tk_pugl_t *dpugl);
-
-D2TK_API void
-d2tk_pugl_run(d2tk_pugl_t *dpugl, const sig_atomic_t *done);
-
-D2TK_API void
-d2tk_pugl_redisplay(d2tk_pugl_t *dpugl);
-
-D2TK_API int
-d2tk_pugl_resize(d2tk_pugl_t *dpugl, d2tk_coord_t w, d2tk_coord_t h);
-
-D2TK_API d2tk_base_t *
-d2tk_pugl_get_base(d2tk_pugl_t *dpugl);
-
-D2TK_API float
-d2tk_pugl_get_scale();
-
#ifdef __cplusplus
}
#endif
diff --git a/subprojects/d2tk/d2tk/hash.h b/subprojects/d2tk/d2tk/hash.h
index 52cf33e2..a6cf6147 100644
--- a/subprojects/d2tk/d2tk/hash.h
+++ b/subprojects/d2tk/d2tk/hash.h
@@ -15,8 +15,8 @@
* http://www.perlfoundation.org/artistic_license_2_0.
*/
-#ifndef _D2TK_MURMUR32_H
-#define _D2TK_MURMUR32_H
+#ifndef _D2TK_HASH_H
+#define _D2TK_HASH_H
#include <stdint.h>
#include <unistd.h>
@@ -47,4 +47,4 @@ d2tk_hash_dict(const d2tk_hash_dict_t *dict);
}
#endif
-#endif // _D2TK_MURMUR32_H
+#endif // _D2TK_HASH_H
diff --git a/subprojects/d2tk/example/d2tk_fbdev.c b/subprojects/d2tk/example/d2tk_fbdev.c
index a6c9c260..f44e47d5 100644
--- a/subprojects/d2tk/example/d2tk_fbdev.c
+++ b/subprojects/d2tk/example/d2tk_fbdev.c
@@ -32,7 +32,8 @@ typedef struct _app_t app_t;
typedef void (*foreach_t)(const char *path, const char *d_name);
struct _app_t {
- d2tk_fbdev_t *fbdev;
+ d2tk_frontend_t *fbdev;
+ bool show_cursor;
};
static sig_atomic_t done = false;
@@ -47,16 +48,19 @@ static inline int
_expose(void *data, d2tk_coord_t w, d2tk_coord_t h)
{
app_t *app = data;
- d2tk_fbdev_t *fbdev = app->fbdev;
- d2tk_base_t *base = d2tk_fbdev_get_base(fbdev);
+ d2tk_frontend_t *fbdev = app->fbdev;
+ d2tk_base_t *base = d2tk_frontend_get_base(fbdev);
d2tk_example_run(base, w, h);
- d2tk_coord_t x = 0;
- d2tk_coord_t y = 0;
+ if(app->show_cursor)
+ {
+ d2tk_coord_t x = 0;
+ d2tk_coord_t y = 0;
- d2tk_base_get_mouse_pos(base, &x, &y);
- d2tk_base_cursor(base, &D2TK_RECT(x, y, 24, 24));
+ d2tk_base_get_mouse_pos(base, &x, &y);
+ d2tk_base_cursor(base, &D2TK_RECT(x, y, 24, 24));
+ }
return EXIT_SUCCESS;
}
@@ -153,7 +157,7 @@ main(int argc, char **argv)
static char fb_device [PATH_MAX] = AUTO;
int c;
- while( (c = getopt(argc, argv, "f:")) != -1)
+ while( (c = getopt(argc, argv, "f:c")) != -1)
{
switch(c)
{
@@ -161,11 +165,16 @@ main(int argc, char **argv)
{
strncpy(fb_device, optarg, PATH_MAX-1);
} break;
+ case 'c':
+ {
+ app.show_cursor = true;
+ } break;
default:
{
fprintf(stderr, "Usage: %s\n"
- " -f fb_device (auto)\n\n",
+ " -f fb_device (auto)\n"
+ " -c show cursor\n\n",
argv[0]);
} return EXIT_FAILURE;
}
@@ -209,9 +218,9 @@ main(int argc, char **argv)
d2tk_example_init();
- d2tk_fbdev_run(app.fbdev, &done);
+ d2tk_frontend_run(app.fbdev, &done);
- d2tk_fbdev_free(app.fbdev);
+ d2tk_frontend_free(app.fbdev);
d2tk_example_deinit();
diff --git a/subprojects/d2tk/example/d2tk_pugl.c b/subprojects/d2tk/example/d2tk_pugl.c
index 4c9bc2e8..70ab9673 100644
--- a/subprojects/d2tk/example/d2tk_pugl.c
+++ b/subprojects/d2tk/example/d2tk_pugl.c
@@ -27,7 +27,7 @@
typedef struct _app_t app_t;
struct _app_t {
- d2tk_pugl_t *dpugl;
+ d2tk_frontend_t *dpugl;
};
static sig_atomic_t done = false;
@@ -42,8 +42,8 @@ static int
_expose(void *data, d2tk_coord_t w, d2tk_coord_t h)
{
app_t *app = data;
- d2tk_pugl_t *dpugl = app->dpugl;
- d2tk_base_t *base = d2tk_pugl_get_base(dpugl);
+ d2tk_frontend_t *dpugl = app->dpugl;
+ d2tk_base_t *base = d2tk_frontend_get_base(dpugl);
d2tk_example_run(base, w, h);
@@ -55,7 +55,7 @@ main(int argc __attribute__((unused)), char **argv __attribute__((unused)))
{
static app_t app;
- const float scale = d2tk_pugl_get_scale();
+ const float scale = d2tk_frontend_get_scale();
d2tk_coord_t w = scale * 1280;
d2tk_coord_t h = scale * 720;
@@ -108,9 +108,9 @@ main(int argc __attribute__((unused)), char **argv __attribute__((unused)))
{
d2tk_example_init();
- d2tk_pugl_run(app.dpugl, &done);
+ d2tk_frontend_run(app.dpugl, &done);
- d2tk_pugl_free(app.dpugl);
+ d2tk_frontend_free(app.dpugl);
d2tk_example_deinit();
diff --git a/subprojects/d2tk/example/example.c b/subprojects/d2tk/example/example.c
index a5bd63c7..a0294696 100644
--- a/subprojects/d2tk/example/example.c
+++ b/subprojects/d2tk/example/example.c
@@ -17,6 +17,7 @@
#include <stdio.h>
#include <stdlib.h>
+#include <unistd.h>
#include <math.h>
#include <inttypes.h>
#include <dirent.h>
@@ -25,23 +26,6 @@
#include <d2tk/frontend_pugl.h>
#include "example/example.h"
-#if !defined(_WIN32) && !defined(__APPLE__)
-# include <libevdev/libevdev.h>
-# include <libevdev/libevdev-uinput.h>
-
-typedef struct _fake_t fake_t;
-
-struct _fake_t {
- struct libevdev *dev;
- struct libevdev_uinput *uidev;
-};
-
-static fake_t fake = {
- .dev = NULL,
- .uidev = NULL
-};
-#endif
-
typedef union _val_t val_t;
union _val_t {
@@ -55,6 +39,8 @@ union _val_t {
typedef enum _bar_t {
BAR_MIX,
+ BAR_SPINNER,
+ BAR_WAVE,
BAR_SEQ,
BAR_TABLE_ABS,
BAR_SCROLL,
@@ -63,8 +49,14 @@ typedef enum _bar_t {
BAR_FLOWMATRIX,
BAR_METER,
BAR_FRAME,
+ BAR_UTF8,
+#if D2TK_PTY
+ BAR_PTY,
+#endif
#if !defined(_WIN32) && !defined(__APPLE__)
BAR_BROWSER,
+#endif
+#if D2TK_EVDEV
BAR_KEYBOARD,
#endif
@@ -74,6 +66,8 @@ typedef enum _bar_t {
static bar_t bar = BAR_MIX;
static const char *bar_lbl [BAR_MAX] = {
[BAR_MIX] = "Mix of many",
+ [BAR_SPINNER] = "Spinner",
+ [BAR_WAVE] = "Wave",
[BAR_SEQ] = "Sequencer",
[BAR_TABLE_ABS] = "Table Abs",
[BAR_SCROLL] = "Scrollbar",
@@ -82,8 +76,14 @@ static const char *bar_lbl [BAR_MAX] = {
[BAR_FLOWMATRIX] = "Flowmatrix",
[BAR_METER] = "Meter",
[BAR_FRAME] = "Frame",
+ [BAR_UTF8] = "UTF-8",
+#if D2TK_PTY
+ [BAR_PTY] = "PTY",
+#endif
#if !defined(_WIN32) && !defined(__APPLE__)
[BAR_BROWSER] = "Browser",
+#endif
+#if D2TK_EVDEV
[BAR_KEYBOARD] = "Keyboard"
#endif
};
@@ -252,6 +252,134 @@ _render_c_mix(d2tk_base_t *base, const d2tk_rect_t *rect)
}
static inline void
+_render_c_spinner(d2tk_base_t *base, const d2tk_rect_t *rect)
+{
+#define N 6
+#define M 24
+ static union {
+ bool b32;
+ int32_t i32;
+ float f32;
+ } val [N*M];
+
+ D2TK_BASE_TABLE(rect, N, M, D2TK_FLAG_TABLE_REL, tab)
+ {
+ const d2tk_rect_t *trect = d2tk_table_get_rect(tab);
+ const unsigned k = d2tk_table_get_index(tab);
+ const d2tk_id_t id = D2TK_ID_IDX(k);
+
+ char lbl [32];
+ const size_t lbl_len = snprintf(lbl, sizeof(lbl), "Spinner/%02X", k);
+
+ switch(k % N)
+ {
+ case 0:
+ {
+ if(d2tk_base_spinner_int32_is_changed(base, id, trect, lbl_len, lbl,
+ 0, &val[k].i32, 99))
+ {
+ fprintf(stdout, "spinner %016"PRIx64" %"PRIi32"\n", id, val[k].i32);
+ }
+ } break;
+ case 1:
+ {
+ if(d2tk_base_spinner_float_is_changed(base, id, trect, lbl_len, lbl,
+ -1.f, &val[k].f32, 0.f))
+ {
+ fprintf(stdout, "spinner %016"PRIx64" %f\n", id, val[k].f32);
+ }
+ } break;
+ case 2:
+ {
+ if(d2tk_base_spinner_int32_is_changed(base, id, trect, lbl_len, lbl,
+ -99, &val[k].i32, 33))
+ {
+ fprintf(stdout, "spinner %016"PRIx64" %"PRIi32"\n", id, val[k].i32);
+ }
+ } break;
+ case 3:
+ {
+ if(d2tk_base_spinner_float_is_changed(base, id, trect, 0, NULL,
+ -0.5f, &val[k].f32, 1.f))
+ {
+ fprintf(stdout, "spinner %016"PRIx64" %f\n", id, val[k].f32);
+ }
+ } break;
+ case 4:
+ {
+ if(d2tk_base_spinner_wave_float_is_changed(base, id, trect, lbl_len, lbl,
+ -0.5f, (const float *)val, N*M, 1.f))
+ {
+ fprintf(stdout, "spinner %016"PRIx64" %f\n", id, val[k].f32);
+ }
+ } break;
+ case 5:
+ {
+ if(d2tk_base_spinner_bool_is_changed(base, id, trect, lbl_len, lbl,
+ &val[k].b32))
+ {
+ fprintf(stdout, "spinner %016"PRIx64" %s\n", id, val[k].b32 ? "true" : "false");
+ }
+ } break;
+ }
+ }
+#undef N
+#undef M
+}
+
+static inline void
+_render_c_wave(d2tk_base_t *base, const d2tk_rect_t *rect)
+{
+#define N 18
+#define M 8192
+ static const float value1 [N] = {
+ 0.2f, 0.1f,
+ 0.3f, 0.2f,
+ 0.4f, 0.3f,
+ 0.5f, 0.4f,
+ 0.6f, 0.5f,
+ 0.7f, 0.6f,
+ 0.8f, 0.7f,
+ 0.9f, 0.8f,
+ 1.0f, 0.9f
+ };
+ static float value2 [M];
+ static bool init2 = false;
+
+ if(!init2)
+ {
+ for(unsigned i = 0; i < M; i++)
+ {
+ value2[i] = sinf(2*M_PI/M*i);
+ }
+
+ init2 = true;
+ }
+
+ const d2tk_coord_t vfrac [2] = { 1, 1 };
+ D2TK_BASE_LAYOUT(rect, 2, vfrac, D2TK_FLAG_LAYOUT_Y_REL, vlay)
+ {
+ const d2tk_rect_t *vrect = d2tk_layout_get_rect(vlay);
+ const unsigned k = d2tk_layout_get_index(vlay);
+
+ switch(k)
+ {
+ case 0:
+ {
+ d2tk_base_wave_float(base, D2TK_ID, vrect, 0.f, value1, N, 1.f);
+ } break;
+ case 1:
+ {
+ d2tk_base_wave_float(base, D2TK_ID, vrect, -1.f, value2, M, 1.f);
+ } break;
+ }
+ }
+
+#undef M
+#undef N
+}
+
+static inline void
_render_c_seq(d2tk_base_t *base, const d2tk_rect_t *rect)
{
#define N (8*12)
@@ -393,13 +521,13 @@ _render_c_table_abs(d2tk_base_t *base, const d2tk_rect_t *rect)
{
static int32_t ntile = 64;
- if(d2tk_base_get_ctrl(base))
+ if(d2tk_base_get_modmask(base, D2TK_MODMASK_CTRL, false))
{
- if(d2tk_base_get_down(base))
+ if(d2tk_base_get_keymask(base, D2TK_KEYMASK_LEFT, true))
{
ntile >>= 1;
}
- else if(d2tk_base_get_up(base))
+ if(d2tk_base_get_keymask(base, D2TK_KEYMASK_UP, true))
{
ntile <<= 1;
}
@@ -426,8 +554,10 @@ _render_c_scroll(d2tk_base_t *base, const d2tk_rect_t *rect)
d2tk_style_t style = *d2tk_base_get_default_style();
+ const uint32_t hmax [2] = { N, 0 };
+ const uint32_t hnum [2] = { N_2, 0 };
D2TK_BASE_SCROLLBAR(base, rect, D2TK_ID, D2TK_FLAG_SCROLL_X,
- N, 0, N_2, 0, hscroll)
+ hmax, hnum, hscroll)
{
const float hoffset = d2tk_scrollbar_get_offset_x(hscroll);
const d2tk_rect_t *row = d2tk_scrollbar_get_rect(hscroll);
@@ -437,8 +567,10 @@ _render_c_scroll(d2tk_base_t *base, const d2tk_rect_t *rect)
const unsigned j = d2tk_table_get_index_x(tcol) + hoffset;
const d2tk_rect_t *col = d2tk_table_get_rect(tcol);
+ const uint32_t vmax [2] = { 0, M/(j+1) };
+ const uint32_t vnum [2] = { 0, O };
D2TK_BASE_SCROLLBAR(base, col, D2TK_ID_IDX(j), D2TK_FLAG_SCROLL_Y,
- 0, M/(j+1), 0, O, vscroll)
+ vmax, vnum, vscroll)
{
const float voffset = d2tk_scrollbar_get_offset_y(vscroll);
const d2tk_rect_t *sub = d2tk_scrollbar_get_rect(vscroll);
@@ -758,7 +890,96 @@ _render_c_frame(d2tk_base_t *base, const d2tk_rect_t *rect)
}
}
}
+#undef N
+}
+
+static inline void
+_render_c_utf8(d2tk_base_t *base, const d2tk_rect_t *rect)
+{
+#define N 3
+ static const char *lbls [N] = {
+ "I can eat glass and it doesn't hurt me", // English
+ "Я могу есть стекло, оно мне не вредит", // Russian
+ "Μπορῶ νὰ φάω σπασμένα γυαλιὰ χωρὶς νὰ πάθω τίποτα", // Greek
+ };
+
+ D2TK_BASE_TABLE(rect, 1, 20, D2TK_FLAG_TABLE_REL, tab)
+ {
+ const d2tk_rect_t *trect = d2tk_table_get_rect(tab);
+ const unsigned k = d2tk_table_get_index(tab);
+
+ if(k >= N)
+ {
+ break;
+ }
+
+ d2tk_base_label(base, -1, lbls[k], 0.5f, trect,
+ D2TK_ALIGN_MIDDLE | D2TK_ALIGN_LEFT);
+ }
+#undef N
+}
+
+#if D2TK_PTY
+static inline void
+_render_c_pty(d2tk_base_t *base, const d2tk_rect_t *rect)
+{
+#define HEIGHT 16
+ static char *argv [] = {
+ "bash",
+ NULL
+ };
+
+ static uint32_t last_red = 0x0;;
+ static uint32_t last_green = 0x0;;
+ static uint32_t last_blue = 0x0;;
+
+ static d2tk_coord_t hfrac [2] = { 1, 1 };
+
+ D2TK_BASE_LAYOUT(rect, 2, hfrac, D2TK_FLAG_LAYOUT_X_REL, hlay)
+ {
+ const d2tk_rect_t *hrect = d2tk_layout_get_rect(hlay);
+ const d2tk_coord_t x = d2tk_layout_get_index(hlay);
+
+ switch(x)
+ {
+ case 0:
+ {
+ D2TK_BASE_PTY(base, D2TK_ID, argv, HEIGHT, hrect, false, pty)
+ {
+ const uint32_t max_red = d2tk_pty_get_max_red(pty);
+ const uint32_t max_green = d2tk_pty_get_max_green(pty);
+ const uint32_t max_blue = d2tk_pty_get_max_blue(pty);
+
+ if(max_red != last_red)
+ {
+ fprintf(stderr, "max_red: 0x%08x\n", max_red);
+ last_red = max_red;
+ }
+ if(max_green != last_green)
+ {
+ fprintf(stderr, "max_green: 0x%08x\n", max_green);
+ last_green = max_green;
+ }
+ if(max_blue != last_blue)
+ {
+ fprintf(stderr, "max_blue: 0x%08x\n", max_blue);
+ last_blue = max_blue;
+ }
+ }
+ } break;
+ case 1:
+ {
+ D2TK_BASE_PTY(base, D2TK_ID, argv, HEIGHT, hrect, false, pty)
+ {
+ // nothing to do
+ }
+ } break;
+ }
+ }
+
+#undef HEIGHT
}
+#endif
#if !defined(_WIN32) && !defined(__APPLE__)
static int
@@ -902,8 +1123,10 @@ _render_c_browser(d2tk_base_t *base, const d2tk_rect_t *rect)
ndir++;
}
+ const uint32_t max [2] = { 0, ndir };
+ const uint32_t num [2] = { 0, M };
D2TK_BASE_SCROLLBAR(base, col, D2TK_ID, D2TK_FLAG_SCROLL_Y,
- 0, ndir, 0, M, vscroll)
+ max, num, vscroll)
{
const float voffset = d2tk_scrollbar_get_offset_y(vscroll);
const d2tk_rect_t *row = d2tk_scrollbar_get_rect(vscroll);
@@ -950,8 +1173,10 @@ _render_c_browser(d2tk_base_t *base, const d2tk_rect_t *rect)
case 1:
{
+ const uint32_t max [2] = { 0, nlist };
+ const uint32_t num [2] = { 0, M };
D2TK_BASE_SCROLLBAR(base, col, D2TK_ID, D2TK_FLAG_SCROLL_Y,
- 0, nlist, 0, M, vscroll)
+ max, num, vscroll)
{
const float voffset = d2tk_scrollbar_get_offset_y(vscroll);
const d2tk_rect_t *row = d2tk_scrollbar_get_rect(vscroll);
@@ -1002,548 +1227,25 @@ _render_c_browser(d2tk_base_t *base, const d2tk_rect_t *rect)
_file_list_free(list);
#undef M
}
+#endif
-static void
-_fake_event(unsigned type, unsigned code, int value)
-{
- if(fake.uidev)
- {
- libevdev_uinput_write_event(fake.uidev, type, code, value);
- }
-}
-
-static void
-_fake_key_down(unsigned keycode)
-{
- _fake_event(EV_KEY, keycode, 1);
- _fake_event(EV_SYN, SYN_REPORT, 0);
-}
-
-static void
-_fake_key_up(unsigned keycode)
-{
- _fake_event(EV_KEY, keycode, 0);
- _fake_event(EV_SYN, SYN_REPORT, 0);
-}
-
-typedef struct _keybtn_t keybtn_t;
-
-struct _keybtn_t {
- const char *name;
- const char *altn;
- unsigned code;
- float rect [4];
-};
-
-#define W (1.f / 15.f)
-#define W_2 (W / 2)
-#define H (1.f / 6.f)
-
-static const keybtn_t keybtns [] = {
- // row 1
- {
- .name = "Esc",
- .code = KEY_ESC,
- .rect = { 0*W, 0*H, W + W_2, H }
- },
- {
- .name = "F1",
- .code = KEY_F1,
- .rect = { 1*W + W_2, 0*H, W, H }
- },
- {
- .name = "F2",
- .code = KEY_F2,
- .rect = { 2*W + W_2, 0*H, W, H }
- },
- {
- .name = "F3",
- .code = KEY_F3,
- .rect = { 3*W + W_2, 0*H, W, H }
- },
- {
- .name = "F4",
- .code = KEY_F4,
- .rect = { 4*W + W_2, 0*H, W, H }
- },
- {
- .name = "F5",
- .code = KEY_F5,
- .rect = { 5*W + W_2, 0*H, W, H }
- },
- {
- .name = "F6",
- .code = KEY_F6,
- .rect = { 6*W + W_2, 0*H, W, H }
- },
- {
- .name = "F7",
- .code = KEY_F7,
- .rect = { 7*W + W_2, 0*H, W, H }
- },
- {
- .name = "F8",
- .code = KEY_F8,
- .rect = { 8*W + W_2, 0*H, W, H }
- },
- {
- .name = "F9",
- .code = KEY_F9,
- .rect = { 9*W + W_2, 0*H, W, H }
- },
- {
- .name = "F10",
- .code = KEY_F10,
- .rect = { 10*W + W_2, 0*H, W, H }
- },
- {
- .name = "F11",
- .code = KEY_F11,
- .rect = { 11*W + W_2, 0*H, W, H }
- },
- {
- .name = "F12",
- .code = KEY_F12,
- .rect = { 12*W + W_2, 0*H, W, H }
- },
- {
- .name = "Delete",
- .code = KEY_DELETE,
- .rect = { 13*W + W_2, 0*H, W + W_2, H }
- },
-
- // row 2
- {
- .name = "`",
- .altn = "~",
- .code = KEY_GRAVE,
- .rect = { 0*W, 1*H, W, H }
- },
- {
- .name = "1",
- .altn = "!",
- .code = KEY_1,
- .rect = { 1*W, 1*H, W, H }
- },
- {
- .name = "2",
- .altn = "@",
- .code = KEY_2,
- .rect = { 2*W, 1*H, W, H }
- },
- {
- .name = "3",
- .altn = "#",
- .code = KEY_3,
- .rect = { 3*W, 1*H, W, H }
- },
- {
- .name = "4",
- .altn = "$",
- .code = KEY_4,
- .rect = { 4*W, 1*H, W, H }
- },
- {
- .name = "5",
- .altn = "%",
- .code = KEY_5,
- .rect = { 5*W, 1*H, W, H }
- },
- {
- .name = "6",
- .altn = "^",
- .code = KEY_6,
- .rect = { 6*W, 1*H, W, H }
- },
- {
- .name = "7",
- .altn = "&",
- .code = KEY_7,
- .rect = { 7*W, 1*H, W, H }
- },
- {
- .name = "8",
- .altn = "*",
- .code = KEY_8,
- .rect = { 8*W, 1*H, W, H }
- },
- {
- .name = "9",
- .altn = "(",
- .code = KEY_9,
- .rect = { 9*W, 1*H, W, H }
- },
- {
- .name = "0",
- .altn = ")",
- .code = KEY_0,
- .rect = { 10*W, 1*H, W, H }
- },
- {
- .name = "-",
- .altn = "_",
- .code = KEY_MINUS,
- .rect = { 11*W, 1*H, W, H }
- },
- {
- .name = "=",
- .altn = "+",
- .code = KEY_EQUAL,
- .rect = { 12*W, 1*H, W, H }
- },
- {
- .name = "Back",
- .code = KEY_BACKSPACE,
- .rect = { 13*W, 1*H, 2*W, H }
- },
-
- // row 3
- {
- .name = "Tab",
- .code = KEY_TAB,
- .rect = { 0*W, 2*H, W + W_2, H }
- },
- {
- .name = "Q",
- .code = KEY_Q,
- .rect = { 1*W + W_2, 2*H, W, H }
- },
- {
- .name = "W",
- .code = KEY_W,
- .rect = { 2*W + W_2, 2*H, W, H }
- },
- {
- .name = "E",
- .code = KEY_E,
- .rect = { 3*W + W_2, 2*H, W, H }
- },
- {
- .name = "R",
- .code = KEY_R,
- .rect = { 4*W + W_2, 2*H, W, H }
- },
- {
- .name = "T",
- .code = KEY_T,
- .rect = { 5*W + W_2, 2*H, W, H }
- },
- {
- .name = "Y",
- .code = KEY_Y,
- .rect = { 6*W + W_2, 2*H, W, H }
- },
- {
- .name = "U",
- .code = KEY_U,
- .rect = { 7*W + W_2, 2*H, W, H }
- },
- {
- .name = "I",
- .code = KEY_I,
- .rect = { 8*W + W_2, 2*H, W, H }
- },
- {
- .name = "O",
- .code = KEY_O,
- .rect = { 9*W + W_2, 2*H, W, H }
- },
- {
- .name = "P",
- .code = KEY_P,
- .rect = { 10*W + W_2, 2*H, W, H }
- },
- {
- .name = "[",
- .altn = "{",
- .code = KEY_LEFTBRACE,
- .rect = { 11*W + W_2, 2*H, W, H }
- },
- {
- .name = "]",
- .altn = "}",
- .code = KEY_RIGHTBRACE,
- .rect = { 12*W + W_2, 2*H, W, H }
- },
- {
- .name = "\\",
- .altn = "|",
- .code = KEY_BACKSLASH,
- .rect = { 13*W + W_2, 2*H, 2*W - W_2, H }
- },
-
- // row 4
- {
- .name = "Caps",
- .code = KEY_CAPSLOCK,
- .rect = { 0*W, 3*H, W*2, H }
- },
- {
- .name = "A",
- .code = KEY_A,
- .rect = { 2*W, 3*H, W, H }
- },
- {
- .name = "S",
- .code = KEY_S,
- .rect = { 3*W, 3*H, W, H }
- },
- {
- .name = "D",
- .code = KEY_D,
- .rect = { 4*W, 3*H, W, H }
- },
- {
- .name = "F",
- .code = KEY_F,
- .rect = { 5*W, 3*H, W, H }
- },
- {
- .name = "G",
- .code = KEY_G,
- .rect = { 6*W, 3*H, W, H }
- },
- {
- .name = "H",
- .code = KEY_H,
- .rect = { 7*W, 3*H, W, H }
- },
- {
- .name = "J",
- .code = KEY_J,
- .rect = { 8*W, 3*H, W, H }
- },
- {
- .name = "K",
- .code = KEY_K,
- .rect = { 9*W, 3*H, W, H }
- },
- {
- .name = "L",
- .code = KEY_L,
- .rect = { 10*W, 3*H, W, H }
- },
- {
- .name = ";",
- .altn = ":",
- .code = KEY_SEMICOLON,
- .rect = { 11*W, 3*H, W, H }
- },
- {
- .name = "'",
- .altn = "\"",
- .code = KEY_APOSTROPHE,
- .rect = { 12*W, 3*H, W, H }
- },
- {
- .name = "Enter",
- .code = KEY_ENTER,
- .rect = { 13*W, 3*H, W*2, H }
- },
-
- // row 5
- {
- .name = "Shift",
- .code = KEY_LEFTSHIFT,
- .rect = { 0*W, 4*H, W*2 + W_2, H }
- },
- {
- .name = "Z",
- .code = KEY_Z,
- .rect = { 2*W + W_2, 4*H, W, H }
- },
- {
- .name = "X",
- .code = KEY_X,
- .rect = { 3*W + W_2, 4*H, W, H }
- },
- {
- .name = "C",
- .code = KEY_C,
- .rect = { 4*W + W_2, 4*H, W, H }
- },
- {
- .name = "V",
- .code = KEY_V,
- .rect = { 5*W + W_2, 4*H, W, H }
- },
- {
- .name = "B",
- .code = KEY_B,
- .rect = { 6*W + W_2, 4*H, W, H }
- },
- {
- .name = "N",
- .code = KEY_N,
- .rect = { 7*W + W_2, 4*H, W, H }
- },
- {
- .name = "M",
- .code = KEY_M,
- .rect = { 8*W + W_2, 4*H, W, H }
- },
- {
- .name = ",",
- .altn = "<",
- .code = KEY_COMMA,
- .rect = { 9*W + W_2, 4*H, W, H }
- },
- {
- .name = ".",
- .altn = ">",
- .code = KEY_DOT,
- .rect = { 10*W + W_2, 4*H, W, H }
- },
- {
- .name = "/",
- .altn = "?",
- .code = KEY_SLASH,
- .rect = { 11*W + W_2, 4*H, W, H }
- },
- {
- .name = "Shift",
- .code = KEY_RIGHTSHIFT,
- .rect = { 12*W + W_2, 4*H, 3*W - W_2, H }
- },
-
- // row 6
- {
- .name = "Fn",
- .code = KEY_FN,
- .rect = { 0*W, 5*H, W, H }
- },
- {
- .name = "Ctrl",
- .code = KEY_LEFTCTRL,
- .rect = { 1*W, 5*H, W, H }
- },
- {
- .name = "Mta",
- .code = KEY_LEFTMETA,
- .rect = { 2*W, 5*H, W, H }
- },
- {
- .name = "Alt",
- .code = KEY_LEFTALT,
- .rect = { 3*W, 5*H, W, H }
- },
- {
- .name = "Space",
- .code = KEY_SPACE,
- .rect = { 4*W, 5*H, 5*W, H }
- },
- {
- .name = "Alt",
- .code = KEY_RIGHTALT,
- .rect = { 9*W, 5*H, W, H }
- },
- {
- .name = "Mta",
- .code = KEY_RIGHTMETA,
- .rect = { 10*W, 5*H, W, H }
- },
- {
- .name = "Ctrl",
- .code = KEY_RIGHTCTRL,
- .rect = { 11*W, 5*H, W, H }
- },
- {
- .name = "Hom",
- .code = KEY_HOME,
- .rect = { 12*W, 5*H, W, H }
- },
- {
- .name = "End",
- .code = KEY_END,
- .rect = { 13*W, 5*H, W, H }
- },
- {
- .name = "Ins",
- .code = KEY_INSERT,
- .rect = { 14*W, 5*H, W, H }
- },
-
- { // sentinel
- .name = NULL
- }
-};
-
+#if D2TK_EVDEV
static inline void
_render_c_keyboard(d2tk_base_t *base, const d2tk_rect_t *rect)
{
- for(const keybtn_t *keybtn = keybtns; keybtn->name; keybtn++)
- {
- const d2tk_rect_t bnd = {
- .x = rect->x + keybtn->rect[0]*rect->w,
- .y = rect->y + keybtn->rect[1]*rect->h,
- .w = keybtn->rect[2]*rect->w,
- .h = keybtn->rect[3]*rect->h
- };
-
- const char *lbl = d2tk_base_get_shift(base) && keybtn->altn
- ? keybtn->altn
- : keybtn->name;
-
- const d2tk_state_t state = d2tk_base_button_label(base,
- D2TK_ID_IDX(keybtn-keybtns), -1, lbl, D2TK_ALIGN_CENTERED, &bnd);
-
- if(d2tk_state_is_down(state))
- {
- _fake_key_down(keybtn->code);
- }
- else if(d2tk_state_is_up(state))
- {
- _fake_key_up(keybtn->code);
- }
- }
+ d2tk_base_vkb(base, D2TK_ID, rect);
}
#endif
D2TK_API int
d2tk_example_init(void)
-{
-#if !defined(_WIN32) && !defined(__APPLE__)
- fake.dev = libevdev_new();
- if(!fake.dev)
- {
- fprintf(stderr, "Error: libevdev_new\n");
- return EXIT_FAILURE;;
- }
- libevdev_set_name(fake.dev, "Fake keyboard");
- libevdev_enable_event_type(fake.dev, EV_SYN);
- libevdev_enable_event_code(fake.dev, EV_SYN, SYN_REPORT, NULL);
- libevdev_enable_event_type(fake.dev, EV_KEY);
- for(const keybtn_t *keybtn = keybtns; keybtn->name; keybtn++)
- {
- libevdev_enable_event_code(fake.dev, EV_KEY, keybtn->code, NULL);
- }
-
- fake.uidev = NULL;
- libevdev_uinput_create_from_device(fake.dev, LIBEVDEV_UINPUT_OPEN_MANAGED,
- &fake.uidev);
- if(!fake.uidev)
- {
- fprintf(stderr, "Warning: libevdev_uinput_create_from_device\n");
- return EXIT_FAILURE;
- }
-#endif
-
+{
return EXIT_SUCCESS;
}
D2TK_API void
d2tk_example_deinit(void)
{
-#if !defined(_WIN32) && !defined(__APPLE__)
- if(fake.uidev)
- {
- libevdev_uinput_destroy(fake.uidev);
- }
- if(fake.dev)
- {
- libevdev_free(fake.dev);
- }
-#endif
}
D2TK_API void
@@ -1584,6 +1286,14 @@ d2tk_example_run(d2tk_base_t *base, d2tk_coord_t w, d2tk_coord_t h)
{
_render_c_mix(base, vrect);
} break;
+ case BAR_SPINNER:
+ {
+ _render_c_spinner(base, vrect);
+ } break;
+ case BAR_WAVE:
+ {
+ _render_c_wave(base, vrect);
+ } break;
case BAR_SEQ:
{
_render_c_seq(base, vrect);
@@ -1616,11 +1326,23 @@ d2tk_example_run(d2tk_base_t *base, d2tk_coord_t w, d2tk_coord_t h)
{
_render_c_frame(base, vrect);
} break;
+ case BAR_UTF8:
+ {
+ _render_c_utf8(base, vrect);
+ } break;
+#if D2TK_PTY
+ case BAR_PTY:
+ {
+ _render_c_pty(base, vrect);
+ } break;
+#endif
#if !defined(_WIN32) && !defined(__APPLE__)
case BAR_BROWSER:
{
_render_c_browser(base, vrect);
} break;
+#endif
+#if D2TK_EVDEV
case BAR_KEYBOARD:
{
_render_c_keyboard(base, vrect);
diff --git a/subprojects/d2tk/meson.build b/subprojects/d2tk/meson.build
index 5160e870..33e49c56 100644
--- a/subprojects/d2tk/meson.build
+++ b/subprojects/d2tk/meson.build
@@ -6,40 +6,106 @@ project('d2tk', 'c', default_options : [
'c_std=gnu11'])
static_link = false #meson.is_cross_build()
-build_debug = get_option('build-debug')
-prefix = get_option('prefix')
-datadir = get_option('datadir')
-bindir = get_option('bindir')
-
-pdatadir = join_paths(prefix, datadir, 'd2tk', '')
-
-add_project_arguments('-DD2TK_DATA_DIR="'+pdatadir+'"', language : 'c')
+build_debug_overlay = get_option('build-debug-overlay')
+build_examples = get_option('build-examples')
+build_tests = get_option('build-tests')
+
+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_vterm = get_option('use-vterm')
+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)
cc = meson.get_compiler('c')
+# mandatory dependencies
m_dep = cc.find_library('m')
-thread_dep = dependency('threads')
-freetype_dep = dependency('freetype2', version : '>=18.0.0',
- static : static_link, required: false)
-pixman_dep = dependency('pixman-1', version : '>=0.34.0',
- static : static_link, required: false)
-cairo_dep = dependency('cairo', version : '>=1.14.0',
- static : static_link, required: false)
-evdev_dep = dependency('libevdev', version : '>=1.5.0',
- static : static_link, required: false)
-input_dep = dependency('libinput', version : '>=1.6.0',
- static : static_link, required: false)
-udev_dep = dependency('libudev', version : '>=232',
- static : static_link, required: false)
-
-deps = [m_dep, evdev_dep]
+
+# 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
+glew_dep = dependency('glew',
+ version : '>=2.1.0',
+ static : static_link,
+ required : false)
+if use_backend_nanovg.enabled() and not glew_dep.found()
+ # use embedded glew
+ glew_dep = declare_dependency(
+ compile_args : '-DGLEW_STATIC',
+ include_directories : include_directories('glew-2.1.0'),
+ sources : join_paths('glew-2.1.0', 'glew.c'))
+endif
+
+# optional dependencies
+util_dep = cc.find_library('util',
+ required : use_vterm)
+vterm_dep = dependency('vterm',
+ version : '>=0.1',
+ static : static_link,
+ required : use_vterm)
+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, evdev_dep, vterm_dep, fontconfig_dep]
links = []
pugl_inc = include_directories('pugl')
nanovg_inc = include_directories('nanovg/src')
-glew_inc = include_directories('glew-2.1.0')
-inc_dir = [pugl_inc, nanovg_inc, glew_inc]
+inc_dir = [pugl_inc, nanovg_inc]
rawvers = run_command('cat', 'VERSION').stdout().strip()
version = rawvers.split('.')
@@ -50,34 +116,100 @@ conf_data.set('MINOR_VERSION', version[1])
conf_data.set('MICRO_VERSION', version[2])
add_project_arguments('-D_GNU_SOURCE', language : 'c')
-add_project_arguments('-DPUGL_HAVE_GL', language : 'c')
-if build_debug
+if build_debug_overlay
add_project_arguments('-DD2TK_DEBUG', language : 'c')
endif
lib_srcs = [
- join_paths('src', 'mum.c'),
+ join_paths('src', 'hash.c'),
join_paths('src', 'core.c'),
- join_paths('src', 'base.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_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')
]
-bin_srcs = [
+if use_vterm.enabled()
+ conf_data.set('D2TK_PTY', 1)
+ lib_srcs += join_paths('src', 'base_pty.c')
+ deps += util_dep
+else
+ conf_data.set('D2TK_PTY', 0)
+endif
+
+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
+
+if cc.has_function('vfork')
+ conf_data.set('D2TK_VFORK', 1)
+else
+ conf_data.set('D2TK_VFORK', 0)
+endif
+
+if cc.has_function('clone')
+ conf_data.set('D2TK_CLONE', 1)
+else
+ conf_data.set('D2TK_CLONE', 0)
+endif
+
+example_srcs = [
join_paths('example', 'example.c')
]
-pugl_srcs = [
- join_paths('src', 'frontend_pugl.c')
+example_pugl_srcs = [
+ join_paths('example', 'd2tk_pugl.c')
]
-pugl_bin_srcs = [
- join_paths('example', 'd2tk_pugl.c')
+example_fbdev_srcs = [
+ join_paths('example', 'd2tk_fbdev.c')
]
+pugl_srcs = [
+ join_paths('src', 'frontend_pugl.c'),
+ join_paths('pugl', 'pugl', 'detail', 'implementation.c')
+]
+
+pugl_gl_srcs = []
+
+pugl_cairo_srcs = []
+
nanovg_srcs = [
join_paths('nanovg', 'src', 'nanovg.c'),
- join_paths('src', 'backend_nanovg.c'),
- join_paths('glew-2.1.0', 'glew.c')
+ join_paths('src', 'backend_nanovg.c')
]
cairo_srcs = [
@@ -88,10 +220,6 @@ fbdev_srcs = [
join_paths('src', 'frontend_fbdev.c')
]
-fbdev_bin_srcs = [
- join_paths('example', 'd2tk_fbdev.c')
-]
-
test_core_srcs = [
join_paths('test', 'core.c'),
join_paths('test', 'mock.c')
@@ -106,100 +234,160 @@ c_args = ['-fvisibility=hidden',
'-ffast-math']
if host_machine.system() == 'windows'
- add_languages('cpp')
- add_project_arguments('-DGLEW_STATIC', language : 'c')
- deps += cc.find_library('opengl32')
- deps += cc.find_library('gdi32')
- deps += cc.find_library('ws2_32')
- pugl_srcs += 'pugl/pugl/pugl_win.cpp'
+ 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 += 'pugl/pugl/detail/win.c'
+ pugl_gl_srcs += 'pugl/pugl/detail/win_gl.c'
+ pugl_cairo_srcs += 'pugl/pugl/detail/win_cairo.c'
elif host_machine.system() == 'darwin'
add_languages('objc')
links += ['-framework', 'OpenGL']
links += ['-framework', 'Cocoa']
- pugl_srcs += 'pugl/pugl/pugl_osx.m'
+ pugl_srcs += 'pugl/pugl/detail/mac.m'
+ pugl_gl_srcs += 'pugl/pugl/detail/mac_gl.m'
+ pugl_cairo_srcs += 'pugl/pugl/detail/mac_cairo.m'
else
- deps += dependency('gl')
- deps += dependency('x11', version : '>=1.6.0')
- deps += dependency('xext', version : '>=1.3.0')
- pugl_srcs += 'pugl/pugl/pugl_x11.c'
+ 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 += 'pugl/pugl/detail/x11.c'
+ pugl_gl_srcs += 'pugl/pugl/detail/x11_gl.c'
+ pugl_cairo_srcs += 'pugl/pugl/detail/x11_cairo.c'
endif
-if freetype_dep.found() and pixman_dep.found() and cairo_dep.found() and (host_machine.system() == 'linux')
- d2tk_cairo = declare_dependency(
- compile_args : '-DPUGL_HAVE_CAIRO',
- include_directories : inc_dir,
- dependencies : [deps, freetype_dep, pixman_dep, cairo_dep],
- link_args : links,
- sources : [lib_srcs, cairo_srcs, pugl_srcs])
-
- executable('d2tk.cairo', [bin_srcs, pugl_bin_srcs],
- c_args : c_args,
- include_directories : inc_dir,
- dependencies: d2tk_cairo,
- install : false)
+if use_backend_cairo.enabled()
+ if use_frontend_pugl.enabled()
+ d2tk_cairo = declare_dependency(
+ compile_args : '-DPUGL_HAVE_CAIRO',
+ 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],
+ c_args : c_args,
+ include_directories : inc_dir,
+ dependencies: d2tk_cairo,
+ install : false)
+ endif
+ endif
- if input_dep.found() and udev_dep.found()
+ if use_frontend_fbdev.enabled()
d2tk_fbdev = declare_dependency(
include_directories : inc_dir,
- dependencies : [deps, freetype_dep, pixman_dep, cairo_dep, input_dep, udev_dep],
+ dependencies : [deps, cairo_deps, input_dep, udev_dep, evdev_dep],
link_args : links,
sources : [lib_srcs, cairo_srcs, fbdev_srcs])
- executable('d2tk.fbdev', [bin_srcs, fbdev_bin_srcs],
- c_args : c_args,
- include_directories : inc_dir,
- dependencies: d2tk_fbdev,
- install : false)
+ if build_examples
+ executable('d2tk.fbdev', [example_srcs, example_fbdev_srcs],
+ c_args : c_args,
+ include_directories : inc_dir,
+ dependencies: d2tk_fbdev,
+ install : false)
+ endif
endif
endif
-d2tk_nanovg = declare_dependency(
- include_directories : inc_dir,
- dependencies : deps,
- link_args : links,
- sources : [lib_srcs, nanovg_srcs, pugl_srcs])
+if use_backend_nanovg.enabled()
+ if use_frontend_pugl.enabled()
+ d2tk_nanovg = declare_dependency(
+ include_directories : inc_dir,
+ dependencies : [deps, 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],
+ c_args : c_args,
+ include_directories : inc_dir,
+ dependencies: d2tk_nanovg,
+ install : false)
+ endif
+ endif
+endif
-executable('d2tk.nanovg', [bin_srcs, pugl_bin_srcs],
- c_args : c_args,
- include_directories : inc_dir,
- dependencies: d2tk_nanovg,
+config_h = configure_file(
+ input : join_paths('d2tk', 'config.h.in'),
+ output : 'config.h',
+ configuration : conf_data,
install : false)
-configure_file(
- input : join_paths('nanovg', 'example', 'Roboto-Bold.ttf'),
- output : 'Roboto-Bold.ttf',
- copy : true,
- 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)
-configure_file(
- input : join_paths('example', 'libre-arrow-circle-right.png'),
- output : 'libre-arrow-circle-right.png',
- 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)
-configure_file(
- input : join_paths('example', 'libre-gui-folder.png'),
- output : 'libre-gui-folder.png',
- 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)
-configure_file(
- input : join_paths('example', 'libre-gui-file.png'),
- output : 'libre-gui-file.png',
- 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)
-test_core = executable('test.core', [test_core_srcs, lib_srcs],
- c_args : c_args,
- dependencies : deps,
- include_directories : inc_dir,
- install : false)
+ fira_code_regular_ttf = configure_file(
+ input : join_paths('ttf', 'FiraCode-Regular.ttf'),
+ output : 'FiraCode:regular.ttf',
+ copy : true,
+ install : false)
+endif
-test_base = executable('test.base', [test_base_srcs, lib_srcs],
- c_args : c_args,
- dependencies : deps,
- include_directories : inc_dir,
- install : false)
+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)
-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
diff --git a/subprojects/d2tk/meson_options.txt b/subprojects/d2tk/meson_options.txt
index a6584831..63965032 100644
--- a/subprojects/d2tk/meson_options.txt
+++ b/subprojects/d2tk/meson_options.txt
@@ -1 +1,43 @@
-option('build-debug', type : 'boolean', value : false)
+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('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-vterm',
+ 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/subprojects/d2tk/pugl/.clang-format b/subprojects/d2tk/pugl/.clang-format
new file mode 100644
index 00000000..b7886761
--- /dev/null
+++ b/subprojects/d2tk/pugl/.clang-format
@@ -0,0 +1,127 @@
+---
+# Language: Cpp
+# BasedOnStyle: Mozilla
+AccessModifierOffset: -4
+AlignAfterOpenBracket: Align
+AlignConsecutiveAssignments: true
+AlignConsecutiveDeclarations: true
+AlignEscapedNewlines: Left
+AlignOperands: true
+AlignTrailingComments: true
+AllowAllArgumentsOnNextLine: true
+AllowAllConstructorInitializersOnNextLine: false
+AllowAllParametersOfDeclarationOnNextLine: false
+AllowShortBlocksOnASingleLine: false
+AllowShortCaseLabelsOnASingleLine: false
+AllowShortFunctionsOnASingleLine: Inline
+AllowShortLambdasOnASingleLine: All
+AllowShortIfStatementsOnASingleLine: Never
+AllowShortLoopsOnASingleLine: false
+AlwaysBreakAfterDefinitionReturnType: TopLevel
+AlwaysBreakAfterReturnType: TopLevel
+AlwaysBreakBeforeMultilineStrings: false
+AlwaysBreakTemplateDeclarations: Yes
+BinPackArguments: false
+BinPackParameters: false
+BraceWrapping:
+ AfterCaseLabel: false
+ AfterClass: true
+ AfterControlStatement: false
+ AfterEnum: false
+ AfterFunction: true
+ AfterNamespace: false
+ AfterObjCDeclaration: true
+ AfterStruct: false
+ AfterUnion: false
+ AfterExternBlock: true
+ BeforeCatch: false
+ BeforeElse: false
+ IndentBraces: false
+ SplitEmptyFunction: true
+ SplitEmptyRecord: false
+ SplitEmptyNamespace: true
+BreakBeforeBinaryOperators: None
+BreakBeforeBraces: Custom
+BreakBeforeInheritanceComma: false
+BreakInheritanceList: BeforeComma
+BreakBeforeTernaryOperators: true
+BreakConstructorInitializersBeforeComma: false
+BreakConstructorInitializers: BeforeComma
+BreakAfterJavaFieldAnnotations: false
+BreakStringLiterals: true
+ColumnLimit: 80
+CommentPragmas: '^ IWYU pragma:'
+CompactNamespaces: false
+ConstructorInitializerAllOnOneLineOrOnePerLine: false
+ConstructorInitializerIndentWidth: 4
+ContinuationIndentWidth: 4
+Cpp11BracedListStyle: true
+DerivePointerAlignment: false
+DisableFormat: false
+ExperimentalAutoDetectBinPacking: false
+FixNamespaceComments: false
+ForEachMacros:
+ - foreach
+ - Q_FOREACH
+ - BOOST_FOREACH
+IncludeBlocks: Preserve
+IncludeCategories:
+ - Regex: '^"(llvm|llvm-c|clang|clang-c)/'
+ Priority: 2
+ - Regex: '^(<|"(gtest|gmock|isl|json)/)'
+ Priority: 3
+ - Regex: '.*'
+ Priority: 1
+IncludeIsMainRegex: '(Test)?$'
+IndentCaseLabels: false
+IndentPPDirectives: AfterHash
+IndentWidth: 4
+IndentWrappedFunctionNames: false
+JavaScriptQuotes: Leave
+JavaScriptWrapImports: true
+KeepEmptyLinesAtTheStartOfBlocks: true
+MacroBlockBegin: ''
+MacroBlockEnd: ''
+MaxEmptyLinesToKeep: 1
+NamespaceIndentation: None
+ObjCBinPackProtocolList: Auto
+ObjCBlockIndentWidth: 4
+ObjCSpaceAfterProperty: true
+ObjCSpaceBeforeProtocolList: false
+PenaltyBreakAssignment: 100
+PenaltyBreakBeforeFirstCallParameter: 100
+PenaltyBreakComment: 300
+PenaltyBreakFirstLessLess: 120
+PenaltyBreakString: 1000
+PenaltyBreakTemplateDeclaration: 10
+PenaltyExcessCharacter: 1000000
+PenaltyReturnTypeOnItsOwnLine: 0
+PointerAlignment: Left
+ReflowComments: true
+SortIncludes: true
+SortUsingDeclarations: true
+SpaceAfterCStyleCast: false
+SpaceAfterLogicalNot: false
+SpaceAfterTemplateKeyword: false
+SpaceBeforeAssignmentOperators: true
+SpaceBeforeCpp11BracedList: true
+SpaceBeforeCtorInitializerColon: true
+SpaceBeforeInheritanceColon: true
+SpaceBeforeParens: ControlStatements
+SpaceBeforeRangeBasedForLoopColon: true
+SpaceInEmptyParentheses: false
+SpacesBeforeTrailingComments: 1
+SpacesInAngles: false
+SpacesInContainerLiterals: true
+SpacesInCStyleCastParentheses: false
+SpacesInParentheses: false
+SpacesInSquareBrackets: false
+Standard: Cpp11
+StatementMacros:
+ - PUGL_API
+ - PUGL_DEPRECATED_BY
+ - PUGL_UNUSED
+TabWidth: 4
+UseTab: ForIndentation
+...
+
diff --git a/subprojects/d2tk/pugl/.clang-tidy b/subprojects/d2tk/pugl/.clang-tidy
new file mode 100644
index 00000000..055d63b3
--- /dev/null
+++ b/subprojects/d2tk/pugl/.clang-tidy
@@ -0,0 +1,16 @@
+Checks: >
+ *,
+ -*magic-numbers,
+ -*uppercase-literal-suffix,
+ -android-cloexec-fopen,
+ -bugprone-suspicious-string-compare,
+ -cert-flp30-c,
+ -clang-analyzer-alpha.*,
+ -clang-analyzer-security.FloatLoopCounter,
+ -hicpp-multiway-paths-covered,
+ -hicpp-signed-bitwise,
+ -llvm-header-guard,
+ -readability-else-after-return
+WarningsAsErrors: ''
+HeaderFilterRegex: 'pugl/.*|test/.*'
+FormatStyle: file
diff --git a/subprojects/d2tk/pugl/.gitattributes b/subprojects/d2tk/pugl/.gitattributes
new file mode 100644
index 00000000..32967c10
--- /dev/null
+++ b/subprojects/d2tk/pugl/.gitattributes
@@ -0,0 +1 @@
+waflib/* linguist-vendored
diff --git a/subprojects/d2tk/pugl/.gitignore b/subprojects/d2tk/pugl/.gitignore
index e5116427..dad71c32 100644
--- a/subprojects/d2tk/pugl/.gitignore
+++ b/subprojects/d2tk/pugl/.gitignore
@@ -1,3 +1,5 @@
.waf*/
build/
.lock-waf*
+__pycache__
+*.pyc
diff --git a/subprojects/d2tk/pugl/.gitlab-ci.yml b/subprojects/d2tk/pugl/.gitlab-ci.yml
new file mode 100644
index 00000000..29dc8bea
--- /dev/null
+++ b/subprojects/d2tk/pugl/.gitlab-ci.yml
@@ -0,0 +1,118 @@
+stages:
+ - build
+ - deploy
+
+variables:
+ GIT_SUBMODULE_STRATEGY: normal
+
+.build_template: &build_definition
+ stage: build
+
+arm32_dbg:
+ <<: *build_definition
+ image: lv2plugin/debian-arm32
+ script: python ./waf configure build -dsT --no-coverage
+ variables:
+ CC: "arm-linux-gnueabihf-gcc"
+ CXX: "arm-linux-gnueabihf-g++"
+
+arm32_rel:
+ <<: *build_definition
+ image: lv2plugin/debian-arm32
+ script: python ./waf configure build -sT --no-coverage
+ variables:
+ CC: "arm-linux-gnueabihf-gcc"
+ CXX: "arm-linux-gnueabihf-g++"
+
+arm64_dbg:
+ <<: *build_definition
+ image: lv2plugin/debian-arm64
+ script: python ./waf configure build -dsT --no-coverage
+ variables:
+ CC: "aarch64-linux-gnu-gcc"
+ CXX: "aarch64-linux-gnu-g++"
+
+arm64_rel:
+ <<: *build_definition
+ image: lv2plugin/debian-arm64
+ script: python ./waf configure build -sT --no-coverage
+ variables:
+ CC: "aarch64-linux-gnu-gcc"
+ CXX: "aarch64-linux-gnu-g++"
+
+x64_dbg:
+ <<: *build_definition
+ image: lv2plugin/debian-x64
+ script: python ./waf configure build -dsT --no-coverage --docs
+ artifacts:
+ paths:
+ - build/doc
+
+x64_rel:
+ <<: *build_definition
+ image: lv2plugin/debian-x64
+ script: python ./waf configure build -sT --no-coverage
+
+mingw32_dbg:
+ <<: *build_definition
+ image: lv2plugin/debian-mingw32
+ script: python ./waf configure build -dsT --no-coverage --target=win32
+ variables:
+ CC: "i686-w64-mingw32-gcc"
+ CXX: "i686-w64-mingw32-g++"
+
+mingw32_rel:
+ <<: *build_definition
+ image: lv2plugin/debian-mingw32
+ script: python ./waf configure build -sT --no-coverage --target=win32
+ variables:
+ CC: "i686-w64-mingw32-gcc"
+ CXX: "i686-w64-mingw32-g++"
+
+mingw64_dbg:
+ <<: *build_definition
+ image: lv2plugin/debian-mingw64
+ script: python ./waf configure build -dsT --no-coverage --target=win32
+ variables:
+ CC: "x86_64-w64-mingw32-gcc"
+ CXX: "x86_64-w64-mingw32-g++"
+
+mingw64_rel:
+ <<: *build_definition
+ image: lv2plugin/debian-mingw64
+ script: python ./waf configure build -sT --no-coverage --target=win32
+ variables:
+ CC: "x86_64-w64-mingw32-gcc"
+ CXX: "x86_64-w64-mingw32-g++"
+
+mac_dbg:
+ <<: *build_definition
+ script: python ./waf configure build -dsT --no-coverage
+ tags: [macos]
+
+mac_rel:
+ <<: *build_definition
+ script: python ./waf configure build -sT --no-coverage
+ tags: [macos]
+
+win_dbg:
+ <<: *build_definition
+ script:
+ - python ./waf configure build -dT --no-coverage
+ tags: [windows,msvc,python]
+
+win_rel:
+ <<: *build_definition
+ script: python ./waf configure build -T --no-coverage
+ tags: [windows,msvc,python]
+
+pages:
+ stage: deploy
+ script: mv build/doc/html/ public/
+ dependencies:
+ - x64_dbg
+ artifacts:
+ paths:
+ - public
+ only:
+ - master
diff --git a/subprojects/d2tk/pugl/.gitmodules b/subprojects/d2tk/pugl/.gitmodules
new file mode 100644
index 00000000..cc8b569f
--- /dev/null
+++ b/subprojects/d2tk/pugl/.gitmodules
@@ -0,0 +1,3 @@
+[submodule "waflib"]
+ path = waflib
+ url = ../../drobilla/autowaf.git
diff --git a/subprojects/d2tk/pugl/AUTHORS b/subprojects/d2tk/pugl/AUTHORS
index 5625baa4..1470491c 100644
--- a/subprojects/d2tk/pugl/AUTHORS
+++ b/subprojects/d2tk/pugl/AUTHORS
@@ -1,11 +1,11 @@
-Author:
- David Robillard <d@drobilla.net>
+Pugl is primarily written and maintained by David Robillard <d@drobilla.net>
+with contributions from (in increasing chronological order):
-Original GLX inspiration, portability fixes:
- Ben Loftis
-
-Various fixes and improvements:
- Robin Gareus <robin@gareus.org>
-
-UTF-8 and Windows event work:
- Erik Åldstedt Sund <erikalds@gmail.com>
+Ben Loftis <ben@harrisonconsoles.com>
+Robin Gareus <robin@gareus.org>
+Erik Åldstedt Sund <erikalds@gmail.com>
+Hanspeter Portner <dev@open-music-kontrollers.ch>
+Stefan Westerfeld <stefan@space.twc.de>
+Jordan Halase <jordan@halase.me>
+Oliver Schmidt <oliver@luced.de>
+Zoë Sparks <zoe@milky.flowers> \ No newline at end of file
diff --git a/subprojects/d2tk/pugl/COPYING b/subprojects/d2tk/pugl/COPYING
index e1e203dd..4a287b92 100644
--- a/subprojects/d2tk/pugl/COPYING
+++ b/subprojects/d2tk/pugl/COPYING
@@ -1,4 +1,4 @@
-Copyright 2011-2014 David Robillard <http://drobilla.net>
+Copyright 2011-2020 David Robillard <http://drobilla.net>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
diff --git a/subprojects/d2tk/pugl/README.md b/subprojects/d2tk/pugl/README.md
index 77809d80..ae9c420e 100644
--- a/subprojects/d2tk/pugl/README.md
+++ b/subprojects/d2tk/pugl/README.md
@@ -1,28 +1,108 @@
-PUGL
+Pugl
====
-Pugl is a minimal portable API for GUIs which supports embedding and is
-suitable for use in plugins. It works on X11, Mac OS X, and Windows. GUIs can
-be drawn with OpenGL or Cairo.
+Pugl (PlUgin Graphics Library) is a minimal portable API for GUIs which is
+suitable for use in plugins. It works on X11, MacOS, and Windows, and
+optionally supports OpenGL and Cairo graphics contexts.
-Pugl is vaguely similar to GLUT, but with some significant distinctions:
+Pugl is vaguely similar to libraries like GLUT and GLFW, but with some
+distinguishing features:
- * Minimal in scope, providing only what is necessary to draw and receive
- keyboard and mouse input.
+ * Minimal in scope, providing only a thin interface to isolate
+ platform-specific details from applications.
- * No reliance on static data whatsoever, so the API can be used in plugins or
- multiple independent parts of a program.
+ * Zero dependencies, aside from standard system libraries.
- * Single implementation, which is small, liberally licensed Free / Open Source
- Software, and suitable for direct inclusion in programs if avoiding a
- library dependency is desired.
+ * Support for embedding in native windows, for example as a plugin or
+ component within a larger application that is not based on Pugl.
- * Support for embedding in other windows, so Pugl code can draw to a widget
- inside a larger GUI.
+ * Simple and extensible event-based API that makes dispatching in application
+ or toolkit code easy with minimal boilerplate.
- * More complete support for keyboard input, including additional "special"
- keys, modifiers, and support for detecting individual modifier key presses.
+ * Suitable not only for continuously rendering applications like games, but
+ also event-driven applications that only draw when necessary.
-For more information, see <http://drobilla.net/software/pugl>.
+ * Explicit context and no static data whatsoever, so that several instances
+ can be used within a single program at once.
+
+ * Small, liberally licensed Free Software implementation that is suitable for
+ vendoring and/or static linking. Pugl can be installed as a library, or
+ used by simply copying the headers into a project.
+
+Stability
+---------
+
+Pugl is currently being developed towards a long-term stable API. For the time
+being, however, the API may break occasionally. Please report any relevant
+feedback, or file feature requests, so that we can ensure that the released API
+is stable for as long as possible.
+
+Distribution
+------------
+
+Pugl is designed for flexible distribution. It can be used by simply including
+the source code, or installed and linked against as a static or shared library.
+Static linking or direct inclusion is a good idea for plugins that will be
+distributed as binaries to avoid dependency problems.
+
+If you are including the code, please use a submodule so that suitable changes
+can be merged upstream to keep fragmentation to a minimum.
+
+When installed, Pugl is split into different libraries to keep dependencies
+minimal. The core implementation is separate from graphics backends:
+
+ * The core implementation for a particular platform is in one library:
+ `pugl_x11`, `pugl_mac`, or `pugl_win`. This does not depend on backends or
+ their dependencies.
+
+ * Backends for platforms are in separate libraries, which depend on the core:
+ `pugl_x11_cairo`, `pugl_x11_gl`, `pugl_mac_cairo`, and so on.
+
+Applications must link against the core and at least one backend. Normally,
+this can be achieved by simply depending on the package `pugl-gl-0` or
+`pugl-cairo-0`. Though it is possible to compile everything into a monolithic
+library, distributions should retain this separation so that GL applications
+don't depend on Cairo and its dependencies, or vice-versa.
+
+Distributions are encouraged to include static libraries if possible so that
+developers can build portable plugin binaries.
+
+Testing
+-------
+
+There are a few unit tests included which can be run with `python waf test
+--gui-tests`, but unfortunately manual testing is still required.
+
+Several example programs are included that serve as both manual tests and
+demonstrations:
+
+ * `pugl_embed_demo` shows a view embedded in another, and also tests
+ requesting attention (which happens after 5 seconds), keyboard focus
+ (switched by pressing tab), view moving (with the arrow keys), and view
+ resizing (with the arrow keys while shift is held). This program uses only
+ very old OpenGL and should work on any system.
+
+ * `pugl_window_demo` demonstrates multiple top-level windows.
+
+ * `pugl_gl3_demo` demonstrates using more modern OpenGL where dynamic loading
+ and shaders are required. It can also be used to test performance by
+ passing the number of rectangles to draw on the command line.
+
+ * `pugl_cairo_demo` demonstrates using Cairo on top of the native windowing
+ system (without OpenGL), and partial redrawing.
+
+ * `pugl_print_events` is a utility that prints all received events to the
+ console in a human readable format.
+
+All example programs support several command line options to control various
+behaviours, see the output of `--help` for details. Please file an issue if
+any of these programs do not work as expected on your system.
+
+Documentation
+-------------
+
+The [API reference](https://lv2.gitlab.io/pugl/) for the latest master is
+available online, and can also be built from the source code by configuring
+with `--docs`.
-- David Robillard <d@drobilla.net>
diff --git a/subprojects/d2tk/pugl/doc/footer.html b/subprojects/d2tk/pugl/doc/footer.html
new file mode 100644
index 00000000..0dc69197
--- /dev/null
+++ b/subprojects/d2tk/pugl/doc/footer.html
@@ -0,0 +1,20 @@
+<!-- HTML footer for doxygen 1.8.15-->
+<!-- start footer part -->
+<!--BEGIN GENERATE_TREEVIEW-->
+<div id="nav-path" class="navpath"><!-- id is needed for treeview function! -->
+ <ul>
+ $navpath
+ <li class="footer">$generatedby
+ <a href="http://www.doxygen.org/index.html">Doxygen $doxygenversion</li>
+ </ul>
+</div>
+<!--END GENERATE_TREEVIEW-->
+<!--BEGIN !GENERATE_TREEVIEW-->
+<div id="footer">
+ <address class="footer">$generatedby
+ <a href="http://www.doxygen.org/">Doxygen</a> $doxygenversion
+ </address>
+</div>
+<!--END !GENERATE_TREEVIEW-->
+</body>
+</html>
diff --git a/subprojects/d2tk/pugl/doc/header.html b/subprojects/d2tk/pugl/doc/header.html
new file mode 100644
index 00000000..54c25b06
--- /dev/null
+++ b/subprojects/d2tk/pugl/doc/header.html
@@ -0,0 +1,49 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+ <meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
+ <!--BEGIN PROJECT_NAME--><title>$projectname: $title</title><!--END PROJECT_NAME-->
+ <!--BEGIN !PROJECT_NAME--><title>$title</title><!--END !PROJECT_NAME-->
+ <link href="$relpath^$stylesheet" rel="stylesheet" type="text/css" />
+ $extrastylesheet
+ </head>
+ <body>
+ <div id="top"><!-- do not remove this div, it is closed by doxygen! -->
+
+ <!--BEGIN TITLEAREA-->
+ <div id="titlearea">
+ <div id="header">
+ <div id="titlebox">
+ <!--BEGIN PROJECT_LOGO-->
+ <div id="projectlogo"><img alt="Logo" src="$relpath^$projectlogo"/></div>
+ <!--END PROJECT_LOGO-->
+ <!--BEGIN PROJECT_NAME-->
+ <h1 id="title">$projectname</h1>
+ <!--END PROJECT_NAME-->
+ <!--BEGIN PROJECT_BRIEF-->
+ <div id="shortdesc">$projectbrief</div>
+ <!--END PROJECT_BRIEF-->
+ </div>
+ <div id="metabox">
+ <table id="meta">
+ <!--BEGIN PROJECT_NUMBER-->
+ <tr><th>Version</th><td>$projectnumber</td></tr>
+ <!--END PROJECT_NUMBER-->
+ </table>
+ </div>
+ </div>
+ </div>
+ <!--END TITLEAREA-->
+ <!-- end header part -->
+
+ <!-- Fake static menu from Doxygen 1.8.15 -->
+ <div id="staticnavrow" class="tabs">
+ <ul class="tablist">
+ <li><a href="index.html"><span>Main&#160;Page</span></a></li>
+ <li><a href="modules.html"><span>Modules</span></a></li>
+ <li><a href="namespaces.html"><span>Namespaces</span></a></li>
+ <li><a href="annotated.html"><span>Data&#160;Structures</span></a></li>
+ <li><a href="functions.html"><span>Symbols</span></a></li>
+ <li><a href="files.html"><span>Files</span></a></li>
+ </ul>
+ </div>
diff --git a/subprojects/d2tk/pugl/doc/layout.xml b/subprojects/d2tk/pugl/doc/layout.xml
new file mode 100644
index 00000000..18893027
--- /dev/null
+++ b/subprojects/d2tk/pugl/doc/layout.xml
@@ -0,0 +1,193 @@
+<doxygenlayout version="1.0">
+ <!-- Generated by doxygen 1.8.13 -->
+ <!-- Navigation index tabs for HTML output -->
+ <navindex>
+ <tab type="mainpage" visible="yes" title=""/>
+ <tab type="pages" visible="yes" title="" intro=""/>
+ <tab type="modules" visible="yes" title="" intro=""/>
+ <tab type="namespaces" visible="yes" title="">
+ <tab type="namespacelist" visible="yes" title="" intro=""/>
+ <tab type="namespacemembers" visible="yes" title="" intro=""/>
+ </tab>
+ <tab type="classes" visible="yes" title="">
+ <tab type="classlist" visible="yes" title="" intro=""/>
+ <tab type="classindex" visible="$ALPHABETICAL_INDEX" title=""/>
+ <tab type="hierarchy" visible="yes" title="" intro=""/>
+ <tab type="classmembers" visible="yes" title="" intro=""/>
+ </tab>
+ <tab type="files" visible="yes" title="">
+ <tab type="filelist" visible="yes" title="" intro=""/>
+ <tab type="globals" visible="yes" title="" intro=""/>
+ </tab>
+ <tab type="examples" visible="yes" title="" intro=""/>
+ </navindex>
+
+ <!-- Layout definition for a class page -->
+ <class>
+ <briefdescription visible="yes"/>
+ <includes visible="$SHOW_INCLUDE_FILES"/>
+ <inheritancegraph visible="$CLASS_GRAPH"/>
+ <collaborationgraph visible="$COLLABORATION_GRAPH"/>
+ <memberdecl>
+ <nestedclasses visible="yes" title=""/>
+ <publictypes title=""/>
+ <services title=""/>
+ <interfaces title=""/>
+ <publicslots title=""/>
+ <signals title=""/>
+ <publicmethods title=""/>
+ <publicstaticmethods title=""/>
+ <publicattributes title=""/>
+ <publicstaticattributes title=""/>
+ <protectedtypes title=""/>
+ <protectedslots title=""/>
+ <protectedmethods title=""/>
+ <protectedstaticmethods title=""/>
+ <protectedattributes title=""/>
+ <protectedstaticattributes title=""/>
+ <packagetypes title=""/>
+ <packagemethods title=""/>
+ <packagestaticmethods title=""/>
+ <packageattributes title=""/>
+ <packagestaticattributes title=""/>
+ <properties title=""/>
+ <events title=""/>
+ <privatetypes title=""/>
+ <privateslots title=""/>
+ <privatemethods title=""/>
+ <privatestaticmethods title=""/>
+ <privateattributes title=""/>
+ <privatestaticattributes title=""/>
+ <friends title=""/>
+ <related title="" subtitle=""/>
+ <membergroups visible="yes"/>
+ </memberdecl>
+ <memberdef>
+ <inlineclasses title=""/>
+ <typedefs title=""/>
+ <enums title=""/>
+ <services title=""/>
+ <interfaces title=""/>
+ <constructors title=""/>
+ <functions title=""/>
+ <related title=""/>
+ <variables title=""/>
+ <properties title=""/>
+ <events title=""/>
+ </memberdef>
+ <allmemberslink visible="yes"/>
+ <usedfiles visible="$SHOW_USED_FILES"/>
+ <authorsection visible="yes"/>
+ </class>
+
+ <!-- Layout definition for a namespace page -->
+ <namespace>
+ <briefdescription visible="yes"/>
+ <detaileddescription title=""/>
+ <memberdecl>
+ <nestednamespaces visible="yes" title=""/>
+ <constantgroups visible="yes" title=""/>
+ <classes visible="yes" title=""/>
+ <typedefs title=""/>
+ <enums title=""/>
+ <functions title=""/>
+ <variables title=""/>
+ <membergroups visible="yes"/>
+ </memberdecl>
+ <memberdef>
+ <inlineclasses title=""/>
+ <typedefs title=""/>
+ <enums title=""/>
+ <functions title=""/>
+ <variables title=""/>
+ </memberdef>
+ <authorsection visible="yes"/>
+ </namespace>
+
+ <!-- Layout definition for a file page -->
+ <file>
+ <briefdescription visible="yes"/>
+ <includes visible="$SHOW_INCLUDE_FILES"/>
+ <includegraph visible="$INCLUDE_GRAPH"/>
+ <includedbygraph visible="$INCLUDED_BY_GRAPH"/>
+ <sourcelink visible="yes"/>
+ <memberdecl>
+ <classes visible="yes" title=""/>
+ <namespaces visible="yes" title=""/>
+ <constantgroups visible="yes" title=""/>
+ <defines title=""/>
+ <typedefs title=""/>
+ <enums title=""/>
+ <functions title=""/>
+ <variables title=""/>
+ <membergroups visible="yes"/>
+ </memberdecl>
+ <detaileddescription title=""/>
+ <memberdef>
+ <inlineclasses title=""/>
+ <defines title=""/>
+ <typedefs title=""/>
+ <enums title=""/>
+ <functions title=""/>
+ <variables title=""/>
+ </memberdef>
+ <authorsection/>
+ </file>
+
+ <!-- Layout definition for a group page -->
+ <group>
+ <briefdescription visible="yes"/>
+ <detaileddescription title=""/>
+ <groupgraph visible="$GROUP_GRAPHS"/>
+ <memberdecl>
+ <nestedgroups visible="yes" title=""/>
+ <dirs visible="yes" title=""/>
+ <files visible="yes" title=""/>
+ <namespaces visible="yes" title=""/>
+ <classes visible="yes" title=""/>
+ <defines title=""/>
+ <typedefs title=""/>
+ <enums title=""/>
+ <enumvalues title=""/>
+ <functions title=""/>
+ <variables title=""/>
+ <signals title=""/>
+ <publicslots title=""/>
+ <protectedslots title=""/>
+ <privateslots title=""/>
+ <events title=""/>
+ <properties title=""/>
+ <friends title=""/>
+ <membergroups visible="yes"/>
+ </memberdecl>
+ <memberdef>
+ <pagedocs/>
+ <inlineclasses title=""/>
+ <defines title=""/>
+ <typedefs title=""/>
+ <enums title=""/>
+ <enumvalues title=""/>
+ <functions title=""/>
+ <variables title=""/>
+ <signals title=""/>
+ <publicslots title=""/>
+ <protectedslots title=""/>
+ <privateslots title=""/>
+ <events title=""/>
+ <properties title=""/>
+ <friends title=""/>
+ </memberdef>
+ <authorsection visible="yes"/>
+ </group>
+
+ <!-- Layout definition for a directory page -->
+ <directory>
+ <briefdescription visible="yes"/>
+ <directorygraph visible="yes"/>
+ <memberdecl>
+ <dirs visible="yes"/>
+ <files visible="yes"/>
+ </memberdecl>
+ <detaileddescription title=""/>
+ </directory>
+</doxygenlayout>
diff --git a/subprojects/d2tk/pugl/doc/mainpage.md b/subprojects/d2tk/pugl/doc/mainpage.md
new file mode 100644
index 00000000..aa6f925a
--- /dev/null
+++ b/subprojects/d2tk/pugl/doc/mainpage.md
@@ -0,0 +1,73 @@
+This is the API documentation for Pugl.
+This documentation is based around the [C API](@ref pugl_api),
+there is also a [C++ API](@ref pugl) in the `pugl` namespace.
+
+The Pugl API revolves around two main objects:
+the [World](@ref world) and the [View](@ref view).
+An application creates a single world to manage top-level state,
+then creates one or more views to display.
+
+## World
+
+The [World](@ref world) contains all top-level state,
+and manages views and the event loop.
+
+A world must be [created](@ref puglNewWorld) before any views,
+and it must outlive all views.
+
+## View
+
+A [View](@ref view) is a drawable region that receives events.
+
+Creating a visible view is a multi-step process.
+When a new view is [created](@ref puglNewView),
+it does not yet represent a real system view or window.
+To display, it must first have a [backend](@ref puglSetBackend)
+and [event handler](@ref puglSetEventFunc) set,
+and be configured by [setting hints](@ref puglSetViewHint)
+and optionally [adjusting the frame](@ref frame).
+
+The [Backend](@ref PuglBackend) controls drawing for a view.
+Pugl includes [Cairo](@ref cairo) and [OpenGL](@ref gl) backends,
+as well as a [stub](@ref stub) backend that creates a native window with no drawing context.
+
+
+Once the view is configured,
+it can be [realized](@ref puglRealize) and [shown](@ref puglShowWindow).
+By default a view will correspond to a top-level system window.
+To create a view within another window,
+it must have a [parent window set](@ref puglSetParentWindow) before being created.
+
+
+## Events
+
+[Events](@ref events) are sent to a view when it has received user input or must be drawn.
+
+Events are handled by the [event handler](@ref PuglEventFunc) set during initialisation.
+This function is called whenever something happens that the view must respond to.
+This includes user interaction like mouse and keyboard input,
+and system events like window resizing and exposure.
+
+## Event Loop
+
+The event loop is driven by repeatedly calling #puglUpdate which processes events from the window system,
+and dispatches them to views when necessary.
+
+Typically, a plugin calls #puglUpdate with timeout 0 in some callback driven by the host.
+A program can use whatever timeout is appropriate:
+event-driven applications may wait forever,
+or for continuous animation,
+use a timeout that is a significant fraction of the frame period
+(with enough time left over to render).
+
+Redrawing can be requested by calling #puglPostRedisplay or #puglPostRedisplayRect,
+which post expose events to the queue.
+Note, however, that this will not wake up a blocked #puglUpdate call on MacOS
+(which does not handle drawing via events).
+For continuous redrawing, call #puglPostRedisplay when a #PUGL_UPDATE event is received.
+This event is sent before views are redrawn,
+so can be used as a hook to expand the update region right before the view is exposed.
+
+## Error Handling
+
+Most functions return a [Status](@ref status) which should be checked to detect failure.
diff --git a/pugl/Doxyfile.in b/subprojects/d2tk/pugl/doc/reference.doxygen.in
index 59e33311..1357fe4a 100644
--- a/pugl/Doxyfile.in
+++ b/subprojects/d2tk/pugl/doc/reference.doxygen.in
@@ -1,4 +1,4 @@
-# Doxyfile 1.8.12
+# Doxyfile 1.8.15
# This file describes the settings to be used by the documentation system
# doxygen (www.doxygen.org) for a project.
@@ -17,11 +17,11 @@
# Project related configuration options
#---------------------------------------------------------------------------
-# This tag specifies the encoding used for all characters in the config file
-# that follow. The default is UTF-8 which is also the encoding used for all text
-# before the first occurrence of this tag. Doxygen uses libiconv (or the iconv
-# built into libc) for the transcoding. See http://www.gnu.org/software/libiconv
-# for the list of possible encodings.
+# This tag specifies the encoding used for all characters in the configuration
+# file that follow. The default is UTF-8 which is also the encoding used for all
+# text before the first occurrence of this tag. Doxygen uses libiconv (or the
+# iconv built into libc) for the transcoding. See
+# https://www.gnu.org/software/libiconv/ for the list of possible encodings.
# The default value is: UTF-8.
DOXYFILE_ENCODING = UTF-8
@@ -44,7 +44,7 @@ PROJECT_NUMBER = @PUGL_VERSION@
# for a project that appears at the top of each page and should give viewer a
# quick idea about the purpose of the project. Keep the description short.
-PROJECT_BRIEF =
+PROJECT_BRIEF = "A minimal portable API for embeddable GUIs"
# With the PROJECT_LOGO tag one can specify a logo or an icon that is included
# in the documentation. The maximum height of the logo should not exceed 55
@@ -58,7 +58,7 @@ PROJECT_LOGO =
# entered, it will be relative to the location where doxygen was started. If
# left blank the current directory will be used.
-OUTPUT_DIRECTORY = doc
+OUTPUT_DIRECTORY = .
# If the CREATE_SUBDIRS tag is set to YES then doxygen will create 4096 sub-
# directories (in 2 levels) under the output directory of each output format and
@@ -93,6 +93,14 @@ ALLOW_UNICODE_NAMES = NO
OUTPUT_LANGUAGE = English
+# The OUTPUT_TEXT_DIRECTION tag is used to specify the direction in which all
+# documentation generated by doxygen is written. Doxygen will use this
+# information to generate all generated output in the proper direction.
+# Possible values are: None, LTR, RTL and Context.
+# The default value is: None.
+
+OUTPUT_TEXT_DIRECTION = None
+
# If the BRIEF_MEMBER_DESC tag is set to YES, doxygen will include brief member
# descriptions after the members that are listed in the file and class
# documentation (similar to Javadoc). Set to NO to disable this.
@@ -226,7 +234,12 @@ TAB_SIZE = 4
# will allow you to put the command \sideeffect (or @sideeffect) in the
# documentation, which will result in a user-defined paragraph with heading
# "Side Effects:". You can put \n's in the value part of an alias to insert
-# newlines.
+# newlines (in the resulting output). You can put ^^ in the value part of an
+# alias to insert a newline as if a physical newline was in the original file.
+# When you need a literal { or } or , in the value part of an alias you have to
+# escape them by means of a backslash (\), this can lead to conflicts with the
+# commands \{ and \} for these it is advised to use the version @{ and @} or use
+# a double escape (\\{ and \\})
ALIASES =
@@ -264,17 +277,26 @@ OPTIMIZE_FOR_FORTRAN = NO
OPTIMIZE_OUTPUT_VHDL = NO
+# Set the OPTIMIZE_OUTPUT_SLICE tag to YES if your project consists of Slice
+# sources only. Doxygen will then generate output that is more tailored for that
+# language. For instance, namespaces will be presented as modules, types will be
+# separated into more groups, etc.
+# The default value is: NO.
+
+OPTIMIZE_OUTPUT_SLICE = NO
+
# Doxygen selects the parser to use depending on the extension of the files it
# parses. With this tag you can assign which parser to use for a given
# extension. Doxygen has a built-in mapping, but you can override or extend it
# using this tag. The format is ext=language, where ext is a file extension, and
# language is one of the parsers supported by doxygen: IDL, Java, Javascript,
-# C#, C, C++, D, PHP, Objective-C, Python, Fortran (fixed format Fortran:
-# FortranFixed, free formatted Fortran: FortranFree, unknown formatted Fortran:
-# Fortran. In the later case the parser tries to guess whether the code is fixed
-# or free formatted code, this is the default for Fortran type files), VHDL. For
-# instance to make doxygen treat .inc files as Fortran files (default is PHP),
-# and .f files as C (default is Fortran), use: inc=Fortran f=C.
+# Csharp (C#), C, C++, D, PHP, md (Markdown), Objective-C, Python, Slice,
+# Fortran (fixed format Fortran: FortranFixed, free formatted Fortran:
+# FortranFree, unknown formatted Fortran: Fortran. In the later case the parser
+# tries to guess whether the code is fixed or free formatted code, this is the
+# default for Fortran type files), VHDL, tcl. For instance to make doxygen treat
+# .inc files as Fortran files (default is PHP), and .f files as C (default is
+# Fortran), use: inc=Fortran f=C.
#
# Note: For files without extension you can use no_extension as a placeholder.
#
@@ -285,7 +307,7 @@ EXTENSION_MAPPING =
# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments
# according to the Markdown format, which allows for more readable
-# documentation. See http://daringfireball.net/projects/markdown/ for details.
+# documentation. See https://daringfireball.net/projects/markdown/ for details.
# The output of markdown processing is further processed by doxygen, so you can
# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in
# case of backward compatibilities issues.
@@ -327,7 +349,7 @@ BUILTIN_STL_SUPPORT = NO
CPP_CLI_SUPPORT = NO
# Set the SIP_SUPPORT tag to YES if your project consists of sip (see:
-# http://www.riverbankcomputing.co.uk/software/sip/intro) sources only. Doxygen
+# https://www.riverbankcomputing.com/software/sip/intro) sources only. Doxygen
# will parse them like normal C++ but will assume all classes use public instead
# of private inheritance when no explicit protection keyword is present.
# The default value is: NO.
@@ -357,7 +379,7 @@ DISTRIBUTE_GROUP_DOC = NO
# is disabled and one has to add nested compounds explicitly via \ingroup.
# The default value is: NO.
-GROUP_NESTED_COMPOUNDS = YES
+GROUP_NESTED_COMPOUNDS = NO
# Set the SUBGROUPING tag to YES to allow class member groups of the same type
# (for instance a group of public functions) to be put as a subgroup of that
@@ -663,14 +685,14 @@ SHOW_USED_FILES = YES
# (if specified).
# The default value is: YES.
-SHOW_FILES = NO
+SHOW_FILES = YES
# Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces
# page. This will remove the Namespaces entry from the Quick Index and from the
# Folder Tree View (if specified).
# The default value is: YES.
-SHOW_NAMESPACES = NO
+SHOW_NAMESPACES = YES
# The FILE_VERSION_FILTER tag can be used to specify a program or script that
# doxygen should invoke to get the current version for each file (typically from
@@ -693,12 +715,12 @@ FILE_VERSION_FILTER =
# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE
# tag is left empty.
-LAYOUT_FILE =
+LAYOUT_FILE = @PUGL_SRCDIR@/doc/layout.xml
# The CITE_BIB_FILES tag can be used to specify one or more bib files containing
# the reference definitions. This must be a list of .bib files. The .bib
# extension is automatically appended if omitted. This requires the bibtex tool
-# to be installed. See also http://en.wikipedia.org/wiki/BibTeX for more info.
+# to be installed. See also https://en.wikipedia.org/wiki/BibTeX for more info.
# For LaTeX the style of the bibliography can be controlled using
# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the
# search path. See also \cite for info how to create references.
@@ -743,7 +765,8 @@ WARN_IF_DOC_ERROR = YES
# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that
# are documented, but have no documentation for their parameters or return
# value. If set to NO, doxygen will only warn about wrong or incomplete
-# parameter documentation, but not about the absence of documentation.
+# parameter documentation, but not about the absence of documentation. If
+# EXTRACT_ALL is set to YES then this flag will automatically be disabled.
# The default value is: NO.
WARN_NO_PARAMDOC = YES
@@ -780,12 +803,13 @@ WARN_LOGFILE =
# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING
# Note: If this tag is empty the current directory is searched.
-INPUT = pugl
+INPUT = @PUGL_SRCDIR@/pugl/ \
+ @PUGL_SRCDIR@/doc/mainpage.md
# This tag can be used to specify the character encoding of the source files
# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
# libiconv (or the iconv built into libc) for the transcoding. See the libiconv
-# documentation (see: http://www.gnu.org/software/libiconv) for the list of
+# documentation (see: https://www.gnu.org/software/libiconv/) for the list of
# possible encodings.
# The default value is: UTF-8.
@@ -803,9 +827,9 @@ INPUT_ENCODING = UTF-8
# *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h,
# *.hh, *.hxx, *.hpp, *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc,
# *.m, *.markdown, *.md, *.mm, *.dox, *.py, *.pyw, *.f90, *.f95, *.f03, *.f08,
-# *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf and *.qsf.
+# *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf, *.qsf and *.ice.
-FILE_PATTERNS =
+FILE_PATTERNS = *.h *.hpp
# The RECURSIVE tag can be used to specify whether or not subdirectories should
# be searched for input files as well.
@@ -929,7 +953,7 @@ FILTER_SOURCE_PATTERNS =
# (index.html). This can be useful if you have a project on for instance GitHub
# and want to reuse the introduction page also for the doxygen output.
-USE_MDFILE_AS_MAINPAGE =
+USE_MDFILE_AS_MAINPAGE = @PUGL_SRCDIR@/doc/mainpage.md
#---------------------------------------------------------------------------
# Configuration options related to source browsing
@@ -958,7 +982,7 @@ INLINE_SOURCES = NO
STRIP_CODE_COMMENTS = YES
# If the REFERENCED_BY_RELATION tag is set to YES then for each documented
-# function all documented functions referencing it will be listed.
+# entity all documented functions referencing it will be listed.
# The default value is: NO.
REFERENCED_BY_RELATION = NO
@@ -967,7 +991,7 @@ REFERENCED_BY_RELATION = NO
# all documented entities called/used by that function will be listed.
# The default value is: NO.
-REFERENCES_RELATION = NO
+REFERENCES_RELATION = YES
# If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set
# to YES then the hyperlinks from functions in REFERENCES_RELATION and
@@ -985,17 +1009,17 @@ REFERENCES_LINK_SOURCE = YES
# The default value is: YES.
# This tag requires that the tag SOURCE_BROWSER is set to YES.
-SOURCE_TOOLTIPS = YES
+SOURCE_TOOLTIPS = NO
# If the USE_HTAGS tag is set to YES then the references to source code will
# point to the HTML generated by the htags(1) tool instead of doxygen built-in
# source browser. The htags tool is part of GNU's global source tagging system
-# (see http://www.gnu.org/software/global/global.html). You will need version
+# (see https://www.gnu.org/software/global/global.html). You will need version
# 4.8.6 or higher.
#
# To use it do the following:
# - Install the latest version of global
-# - Enable SOURCE_BROWSER and USE_HTAGS in the config file
+# - Enable SOURCE_BROWSER and USE_HTAGS in the configuration file
# - Make sure the INPUT points to the root of the source tree
# - Run doxygen as normal
#
@@ -1085,7 +1109,7 @@ HTML_FILE_EXTENSION = .html
# of the possible markers and block names see the documentation.
# This tag requires that the tag GENERATE_HTML is set to YES.
-HTML_HEADER =
+HTML_HEADER = @PUGL_SRCDIR@/doc/header.html
# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each
# generated HTML page. If the tag is left blank doxygen will generate a standard
@@ -1095,7 +1119,7 @@ HTML_HEADER =
# that doxygen normally uses.
# This tag requires that the tag GENERATE_HTML is set to YES.
-HTML_FOOTER =
+HTML_FOOTER = @PUGL_SRCDIR@/doc/footer.html
# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style
# sheet that is used by each HTML page. It can be used to fine-tune the look of
@@ -1107,7 +1131,7 @@ HTML_FOOTER =
# obsolete.
# This tag requires that the tag GENERATE_HTML is set to YES.
-HTML_STYLESHEET =
+HTML_STYLESHEET = @PUGL_SRCDIR@/doc/style.css
# The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined
# cascading style sheets that are included after the standard style sheets
@@ -1135,13 +1159,13 @@ HTML_EXTRA_FILES =
# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen
# will adjust the colors in the style sheet and background images according to
# this color. Hue is specified as an angle on a colorwheel, see
-# http://en.wikipedia.org/wiki/Hue for more information. For instance the value
+# https://en.wikipedia.org/wiki/Hue for more information. For instance the value
# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300
# purple, and 360 is red again.
# Minimum value: 0, maximum value: 359, default value: 220.
# This tag requires that the tag GENERATE_HTML is set to YES.
-HTML_COLORSTYLE_HUE = 130
+HTML_COLORSTYLE_HUE = 160
# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors
# in the HTML output. For a value of 0 the output will use grayscales only. A
@@ -1149,7 +1173,7 @@ HTML_COLORSTYLE_HUE = 130
# Minimum value: 0, maximum value: 255, default value: 100.
# This tag requires that the tag GENERATE_HTML is set to YES.
-HTML_COLORSTYLE_SAT = 30
+HTML_COLORSTYLE_SAT = 100
# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the
# luminance component of the colors in the HTML output. Values below 100
@@ -1160,7 +1184,7 @@ HTML_COLORSTYLE_SAT = 30
# Minimum value: 40, maximum value: 240, default value: 80.
# This tag requires that the tag GENERATE_HTML is set to YES.
-HTML_COLORSTYLE_GAMMA = 100
+HTML_COLORSTYLE_GAMMA = 80
# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML
# page will contain the date and time when the page was generated. Setting this
@@ -1171,13 +1195,24 @@ HTML_COLORSTYLE_GAMMA = 100
HTML_TIMESTAMP = NO
+# If the HTML_DYNAMIC_MENUS tag is set to YES then the generated HTML
+# documentation will contain a main index with vertical navigation menus that
+# are dynamically created via Javascript. If disabled, the navigation index will
+# consists of multiple levels of tabs that are statically embedded in every HTML
+# page. Disable this option to support browsers that do not have Javascript,
+# like the Qt help browser.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_DYNAMIC_MENUS = NO
+
# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML
# documentation will contain sections that can be hidden and shown after the
# page has loaded.
# The default value is: NO.
# This tag requires that the tag GENERATE_HTML is set to YES.
-HTML_DYNAMIC_SECTIONS = YES
+HTML_DYNAMIC_SECTIONS = NO
# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries
# shown in the various tree structured indices initially; the user can expand
@@ -1194,13 +1229,13 @@ HTML_INDEX_NUM_ENTRIES = 100
# If the GENERATE_DOCSET tag is set to YES, additional index files will be
# generated that can be used as input for Apple's Xcode 3 integrated development
-# environment (see: http://developer.apple.com/tools/xcode/), introduced with
-# OSX 10.5 (Leopard). To create a documentation set, doxygen will generate a
+# environment (see: https://developer.apple.com/xcode/), introduced with OSX
+# 10.5 (Leopard). To create a documentation set, doxygen will generate a
# Makefile in the HTML output directory. Running make will produce the docset in
# that directory and running make install will install the docset in
# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at
-# startup. See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html
-# for more information.
+# startup. See https://developer.apple.com/library/archive/featuredarticles/Doxy
+# genXcode/_index.html for more information.
# The default value is: NO.
# This tag requires that the tag GENERATE_HTML is set to YES.
@@ -1239,7 +1274,7 @@ DOCSET_PUBLISHER_NAME = Publisher
# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three
# additional HTML index files: index.hhp, index.hhc, and index.hhk. The
# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop
-# (see: http://www.microsoft.com/en-us/download/details.aspx?id=21138) on
+# (see: https://www.microsoft.com/en-us/download/details.aspx?id=21138) on
# Windows.
#
# The HTML Help Workshop contains a compiler that can convert all HTML output
@@ -1315,7 +1350,7 @@ QCH_FILE =
# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help
# Project output. For more information please see Qt Help Project / Namespace
-# (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#namespace).
+# (see: http://doc.qt.io/archives/qt-4.8/qthelpproject.html#namespace).
# The default value is: org.doxygen.Project.
# This tag requires that the tag GENERATE_QHP is set to YES.
@@ -1323,7 +1358,7 @@ QHP_NAMESPACE =
# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt
# Help Project output. For more information please see Qt Help Project / Virtual
-# Folders (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#virtual-
+# Folders (see: http://doc.qt.io/archives/qt-4.8/qthelpproject.html#virtual-
# folders).
# The default value is: doc.
# This tag requires that the tag GENERATE_QHP is set to YES.
@@ -1332,7 +1367,7 @@ QHP_VIRTUAL_FOLDER = doc
# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom
# filter to add. For more information please see Qt Help Project / Custom
-# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom-
+# Filters (see: http://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-
# filters).
# This tag requires that the tag GENERATE_QHP is set to YES.
@@ -1340,7 +1375,7 @@ QHP_CUST_FILTER_NAME =
# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the
# custom filter to add. For more information please see Qt Help Project / Custom
-# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom-
+# Filters (see: http://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-
# filters).
# This tag requires that the tag GENERATE_QHP is set to YES.
@@ -1348,7 +1383,7 @@ QHP_CUST_FILTER_ATTRS =
# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this
# project's filter section matches. Qt Help Project / Filter Attributes (see:
-# http://qt-project.org/doc/qt-4.8/qthelpproject.html#filter-attributes).
+# http://doc.qt.io/archives/qt-4.8/qthelpproject.html#filter-attributes).
# This tag requires that the tag GENERATE_QHP is set to YES.
QHP_SECT_FILTER_ATTRS =
@@ -1389,7 +1424,7 @@ ECLIPSE_DOC_ID = org.doxygen.Project
# The default value is: NO.
# This tag requires that the tag GENERATE_HTML is set to YES.
-DISABLE_INDEX = YES
+DISABLE_INDEX = NO
# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index
# structure should be generated to display hierarchical information. If the tag
@@ -1416,7 +1451,7 @@ GENERATE_TREEVIEW = NO
# Minimum value: 0, maximum value: 20, default value: 4.
# This tag requires that the tag GENERATE_HTML is set to YES.
-ENUM_VALUES_PER_LINE = 1
+ENUM_VALUES_PER_LINE = 0
# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used
# to set the initial width (in pixels) of the frame in which the tree is shown.
@@ -1441,7 +1476,7 @@ EXT_LINKS_IN_WINDOW = NO
FORMULA_FONTSIZE = 10
-# Use the FORMULA_TRANPARENT tag to determine whether or not the images
+# Use the FORMULA_TRANSPARENT tag to determine whether or not the images
# generated for formulas are transparent PNGs. Transparent PNGs are not
# supported properly for IE 6.0, but are supported on all modern browsers.
#
@@ -1453,7 +1488,7 @@ FORMULA_FONTSIZE = 10
FORMULA_TRANSPARENT = YES
# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see
-# http://www.mathjax.org) which uses client side Javascript for the rendering
+# https://www.mathjax.org) which uses client side Javascript for the rendering
# instead of using pre-rendered bitmaps. Use this if you do not have LaTeX
# installed or if you want to formulas look prettier in the HTML output. When
# enabled you may also need to install MathJax separately and configure the path
@@ -1480,8 +1515,8 @@ MATHJAX_FORMAT = HTML-CSS
# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax
# Content Delivery Network so you can quickly see the result without installing
# MathJax. However, it is strongly recommended to install a local copy of
-# MathJax from http://www.mathjax.org before deployment.
-# The default value is: http://cdn.mathjax.org/mathjax/latest.
+# MathJax from https://www.mathjax.org before deployment.
+# The default value is: https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/.
# This tag requires that the tag USE_MATHJAX is set to YES.
MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest
@@ -1542,7 +1577,7 @@ SERVER_BASED_SEARCH = NO
#
# Doxygen ships with an example indexer (doxyindexer) and search engine
# (doxysearch.cgi) which are based on the open source search engine library
-# Xapian (see: http://xapian.org/).
+# Xapian (see: https://xapian.org/).
#
# See the section "External Indexing and Searching" for details.
# The default value is: NO.
@@ -1555,7 +1590,7 @@ EXTERNAL_SEARCH = NO
#
# Doxygen ships with an example indexer (doxyindexer) and search engine
# (doxysearch.cgi) which are based on the open source search engine library
-# Xapian (see: http://xapian.org/). See the section "External Indexing and
+# Xapian (see: https://xapian.org/). See the section "External Indexing and
# Searching" for details.
# This tag requires that the tag SEARCHENGINE is set to YES.
@@ -1607,21 +1642,34 @@ LATEX_OUTPUT = latex
# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
# invoked.
#
-# Note that when enabling USE_PDFLATEX this option is only used for generating
-# bitmaps for formulas in the HTML output, but not in the Makefile that is
-# written to the output directory.
-# The default file is: latex.
+# Note that when not enabling USE_PDFLATEX the default is latex when enabling
+# USE_PDFLATEX the default is pdflatex and when in the later case latex is
+# chosen this is overwritten by pdflatex. For specific output languages the
+# default can have been set differently, this depends on the implementation of
+# the output language.
# This tag requires that the tag GENERATE_LATEX is set to YES.
LATEX_CMD_NAME = latex
# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to generate
# index for LaTeX.
+# Note: This tag is used in the Makefile / make.bat.
+# See also: LATEX_MAKEINDEX_CMD for the part in the generated output file
+# (.tex).
# The default file is: makeindex.
# This tag requires that the tag GENERATE_LATEX is set to YES.
MAKEINDEX_CMD_NAME = makeindex
+# The LATEX_MAKEINDEX_CMD tag can be used to specify the command name to
+# generate index for LaTeX.
+# Note: This tag is used in the generated output file (.tex).
+# See also: MAKEINDEX_CMD_NAME for the part in the Makefile / make.bat.
+# The default value is: \makeindex.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_MAKEINDEX_CMD = \makeindex
+
# If the COMPACT_LATEX tag is set to YES, doxygen generates more compact LaTeX
# documents. This may be useful for small projects and may help to save some
# trees in general.
@@ -1742,7 +1790,7 @@ LATEX_SOURCE_CODE = NO
# The LATEX_BIB_STYLE tag can be used to specify the style to use for the
# bibliography, e.g. plainnat, or ieeetr. See
-# http://en.wikipedia.org/wiki/BibTeX and \cite for more info.
+# https://en.wikipedia.org/wiki/BibTeX and \cite for more info.
# The default value is: plain.
# This tag requires that the tag GENERATE_LATEX is set to YES.
@@ -1756,6 +1804,14 @@ LATEX_BIB_STYLE = plain
LATEX_TIMESTAMP = NO
+# The LATEX_EMOJI_DIRECTORY tag is used to specify the (relative or absolute)
+# path from which the emoji images will be read. If a relative path is entered,
+# it will be relative to the LATEX_OUTPUT directory. If left blank the
+# LATEX_OUTPUT directory will be used.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_EMOJI_DIRECTORY =
+
#---------------------------------------------------------------------------
# Configuration options related to the RTF output
#---------------------------------------------------------------------------
@@ -1795,9 +1851,9 @@ COMPACT_RTF = NO
RTF_HYPERLINKS = NO
-# Load stylesheet definitions from file. Syntax is similar to doxygen's config
-# file, i.e. a series of assignments. You only have to provide replacements,
-# missing definitions are set to their default value.
+# Load stylesheet definitions from file. Syntax is similar to doxygen's
+# configuration file, i.e. a series of assignments. You only have to provide
+# replacements, missing definitions are set to their default value.
#
# See also section "Doxygen usage" for information on how to generate the
# default style sheet that doxygen normally uses.
@@ -1806,8 +1862,8 @@ RTF_HYPERLINKS = NO
RTF_STYLESHEET_FILE =
# Set optional variables used in the generation of an RTF document. Syntax is
-# similar to doxygen's config file. A template extensions file can be generated
-# using doxygen -e rtf extensionFile.
+# similar to doxygen's configuration file. A template extensions file can be
+# generated using doxygen -e rtf extensionFile.
# This tag requires that the tag GENERATE_RTF is set to YES.
RTF_EXTENSIONS_FILE =
@@ -1893,6 +1949,13 @@ XML_OUTPUT = xml
XML_PROGRAMLISTING = YES
+# If the XML_NS_MEMB_FILE_SCOPE tag is set to YES, doxygen will include
+# namespace members in file scope as well, matching the HTML output.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_XML is set to YES.
+
+XML_NS_MEMB_FILE_SCOPE = NO
+
#---------------------------------------------------------------------------
# Configuration options related to the DOCBOOK output
#---------------------------------------------------------------------------
@@ -1925,9 +1988,9 @@ DOCBOOK_PROGRAMLISTING = NO
#---------------------------------------------------------------------------
# If the GENERATE_AUTOGEN_DEF tag is set to YES, doxygen will generate an
-# AutoGen Definitions (see http://autogen.sf.net) file that captures the
-# structure of the code including all documentation. Note that this feature is
-# still experimental and incomplete at the moment.
+# AutoGen Definitions (see http://autogen.sourceforge.net/) file that captures
+# the structure of the code including all documentation. Note that this feature
+# is still experimental and incomplete at the moment.
# The default value is: NO.
GENERATE_AUTOGEN_DEF = NO
@@ -1987,7 +2050,7 @@ ENABLE_PREPROCESSING = YES
# The default value is: NO.
# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
-MACRO_EXPANSION = NO
+MACRO_EXPANSION = YES
# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES then
# the macro expansion is limited to the macros specified with the PREDEFINED and
@@ -1995,7 +2058,7 @@ MACRO_EXPANSION = NO
# The default value is: NO.
# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
-EXPAND_ONLY_PREDEF = NO
+EXPAND_ONLY_PREDEF = YES
# If the SEARCH_INCLUDES tag is set to YES, the include files in the
# INCLUDE_PATH will be searched if a #include is found.
@@ -2027,7 +2090,7 @@ INCLUDE_FILE_PATTERNS =
# recursively expanded use the := operator instead of the = operator.
# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
-PREDEFINED =
+PREDEFINED = PUGL_API PUGL_DISABLE_DEPRECATED
# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this
# tag can be used to specify a list of macro names that should be expanded. The
@@ -2094,12 +2157,6 @@ EXTERNAL_GROUPS = YES
EXTERNAL_PAGES = YES
-# The PERL_PATH should be the absolute path and name of the perl script
-# interpreter (i.e. the result of 'which perl').
-# The default file (with absolute path) is: /usr/bin/perl.
-
-PERL_PATH = /usr/bin/perl
-
#---------------------------------------------------------------------------
# Configuration options related to the dot tool
#---------------------------------------------------------------------------
@@ -2113,15 +2170,6 @@ PERL_PATH = /usr/bin/perl
CLASS_DIAGRAMS = NO
-# You can define message sequence charts within doxygen comments using the \msc
-# command. Doxygen will then run the mscgen tool (see:
-# http://www.mcternan.me.uk/mscgen/)) to produce the chart and insert it in the
-# documentation. The MSCGEN_PATH tag allows you to specify the directory where
-# the mscgen tool resides. If left empty the tool is assumed to be found in the
-# default search path.
-
-MSCGEN_PATH =
-
# You can include diagrams made with dia in doxygen documentation. Doxygen will
# then run dia to produce the diagram and insert it in the documentation. The
# DIA_PATH tag allows you to specify the directory where the dia binary resides.
@@ -2349,6 +2397,11 @@ DIAFILE_DIRS =
PLANTUML_JAR_PATH =
+# When using plantuml, the PLANTUML_CFG_FILE tag can be used to specify a
+# configuration file for plantuml.
+
+PLANTUML_CFG_FILE =
+
# When using plantuml, the specified paths are searched for files specified by
# the !include statement in a plantuml block.
diff --git a/subprojects/d2tk/pugl/doc/style.css b/subprojects/d2tk/pugl/doc/style.css
new file mode 100644
index 00000000..28f0519d
--- /dev/null
+++ b/subprojects/d2tk/pugl/doc/style.css
@@ -0,0 +1,808 @@
+body {
+ background: #FFF;
+ color: #222;
+ font-style: normal;
+ line-height: 1.6em;
+ margin-left: auto;
+ margin-right: auto;
+ padding: 1em;
+ max-width: 60em;
+ font-family: "SF Pro Text", Verdana, "DejaVu Sans", sans-serif;
+ text-rendering: optimizeLegibility;
+}
+
+h1 {
+ font-size: 1.68em;
+ font-weight: 500;
+ font-family: Helvetica, Arial, "DejaVu Sans Condensed", Verdana, sans-serif;
+ line-height: 2em;
+ margin: 0 0 0.25em 0;
+}
+
+h2 {
+ line-height: 1.68em;
+ font-size: 1.41em;
+ font-weight: 600;
+ font-family: Helvetica, Arial, "DejaVu Sans Condensed", Verdana, sans-serif;
+ margin: 1.25em 0 0.5em 0;
+}
+
+h3 {
+ line-height: 1.41em;
+ font-size: 1.18em;
+ font-weight: 600;
+ font-family: Helvetica, Arial, "DejaVu Sans Condensed", Verdana, sans-serif;
+ margin: 1.25em 0 0.5em 0;
+}
+
+h4 {
+ line-height: 1.18em;
+ font-size: 1em;
+ font-weight: 600;
+ font-family: Helvetica, Arial, "DejaVu Sans Condensed", Verdana, sans-serif;
+ margin: 1.25em 0 0.5em 0;
+}
+
+h5, h6 {
+ font-size: 0.7em;
+ font-weight: 600;
+ font-family: Helvetica, Arial, "DejaVu Sans Condensed", Verdana, sans-serif;
+ margin: 1.25em 0 0.5em 0;
+}
+
+a {
+ color: #546E00;
+ text-decoration: none;
+}
+
+h1 a, h2 a, h3 a, h4 a, h5 a, h6 a {
+ color: #444;
+}
+
+a:hover {
+ text-decoration: underline;
+}
+
+h1 a:link, h2 a:link, h3 a:link, h4 a:link, h5 a:link, h6 a:link {
+ color: #444;
+}
+
+h1 a:visited, h2 a:visited, h3 a:visited, h4 a:visited, h5 a:visited, h6 a:visited {
+ color: #444;
+}
+
+p {
+ margin: 0.5em 0 0.5em 0;
+}
+
+dt {
+ font-weight: 600;
+}
+
+dd {
+ margin-left: 2em;
+}
+
+caption {
+ font-weight: 700;
+}
+
+.title, #projectname {
+ line-height: 1.0125em;
+ margin: 0.75em 0 0 0;
+}
+
+.titlearea .header .titlebox, #projectname {
+ font-size: 1.68em;
+ font-weight: 400;
+ margin-bottom: 0.25em;
+ margin-top: 0;
+}
+
+#header {
+ padding: 0 0 0.5em 0;
+ border-bottom: 1px solid #EEE;
+}
+
+.header .headertitle .title {
+ line-height: 1.68em;
+ font-size: 1.68em;
+ font-weight: 600;
+ font-family: Helvetica, Arial, "DejaVu Sans Condensed", Verdana, sans-serif;
+}
+
+.ingroups {
+ display: none;
+}
+
+.title .ingroups a {
+ font-size: small;
+ margin-left: 1em;
+}
+
+#titlebox, #metabox {
+ display: inline-block;
+}
+
+#titlebox {
+ display: inline-block;
+ width: 75%;
+ left: 0;
+ top: 0;
+}
+
+#title {
+ margin-bottom: 0.25em;
+ line-height: 1.25em;
+ font-size: 2.5em;
+ color: #333;
+ font-weight: 600;
+}
+
+.PageDoc {
+ margin-top: 1.5em;
+}
+
+.PageDoc .header .headertitle .title {
+ display: none;
+}
+
+#shortdesc {
+ margin: 0;
+ color: #666;
+ display: inline-block;
+ font-style: italic;
+ font-family: Helvetica, Arial, "DejaVu Sans Condensed", Verdana, sans-serif;
+ padding: 0;
+}
+
+#titlearea {
+ margin: 0.25em auto 0 auto;
+ padding: 0;
+ position: relative;
+ clear: both;
+ line-height: 1em;
+}
+
+.legend {
+ font-size: small;
+ text-align: center;
+}
+
+.version {
+ font-size: small;
+ text-align: center;
+}
+
+div.qindex,div.navtab {
+ background-color: #EBEFF6;
+ border: 1px solid #A3B4D7;
+ text-align: center;
+ margin: 2px;
+ padding: 2px;
+}
+
+div.navtab {
+ margin-right: 15px;
+}
+
+.contents a:visited {
+ color: #344E00;
+}
+
+a.qindexHL {
+ background-color: #9CAFD4;
+ color: #FFF;
+ border: 1px double #869DCA;
+}
+
+code {
+ color: #444;
+ font-family: "SF Mono", Menlo, Consolas, "DejaVu Sans Mono", monospace, fixed;
+}
+
+dl.el {
+ margin-left: -1cm;
+}
+
+.fragment {
+ font-family: "SF Mono", Menlo, Consolas, "DejaVu Sans Mono", monospace, fixed;
+}
+
+pre.fragment {
+ border: 1px solid #C4C4C4;
+ background-color: #F9F9F9;
+ padding: 0.5em;
+ overflow: auto;
+}
+
+div.ah {
+ background-color: #000;
+ font-weight: 700;
+ color: #FFF;
+ margin-bottom: 3px;
+ margin-top: 3px;
+ padding: 0.2em;
+ border: thin solid #333;
+}
+
+div.groupHeader {
+ margin-left: 16px;
+ margin-top: 12px;
+ margin-bottom: 6px;
+ font-weight: 700;
+}
+
+a + h2.groupheader {
+ display: none;
+}
+
+div.groupText {
+ margin-left: 16px;
+ font-style: italic;
+}
+
+div.contents, #content {
+ max-width: 60em;
+ margin-left: auto;
+ margin-right: auto;
+}
+
+.groupheader + p {
+ font-style: italic;
+ color: #666;
+ margin: 0 0 1em 0;
+}
+
+td.indexkey {
+ background-color: #EBEFF6;
+ font-weight: 700;
+ border: 1px solid #C4CFE5;
+ margin: 2px 0;
+ padding: 2px 10px;
+}
+
+td.indexvalue {
+ background-color: #EBEFF6;
+ border: 1px solid #C4CFE5;
+ padding: 2px 10px;
+ margin: 2px 0;
+}
+
+table.memname {
+ font-family: "SF Mono", Menlo, Consolas, "DejaVu Sans Mono", monospace, fixed;
+ border-spacing: 0;
+}
+
+table.memname tbody tr:last-child {
+ display: none;
+}
+
+table.memname tbody tr:only-child {
+ display: table-cell;
+}
+
+table.memname tbody tr:nth-last-child(2)::after {
+ content: ")";
+}
+
+tr.memlist {
+ background-color: #EEF1F7;
+}
+
+p.formulaDsp {
+ text-align: center;
+}
+
+img.formulaInl {
+ vertical-align: middle;
+}
+
+div.center {
+ text-align: center;
+ margin-top: 0;
+ margin-bottom: 0;
+ padding: 0;
+}
+
+div.center img {
+ border: 0;
+}
+
+address.footer {
+ text-align: right;
+}
+
+img.footer {
+ border: 0;
+ vertical-align: middle;
+}
+
+span.keyword {
+ color: #586E75;
+}
+
+span.keywordtype {
+ color: #546E00;
+}
+
+span.keywordflow {
+ color: #586E75;
+}
+
+span.comment {
+ color: #6C71C4;
+}
+
+span.preprocessor {
+ color: #D33682;
+}
+
+span.stringliteral {
+ color: #CB4B16;
+}
+
+span.charliteral {
+ color: #CB4B16;
+}
+
+td.tiny {
+ font-size: x-small;
+}
+
+.dirtab {
+ padding: 4px;
+ border-collapse: collapse;
+ border: 1px solid #A3B4D7;
+}
+
+th.dirtab {
+ background: #EBEFF6;
+ font-weight: 700;
+}
+
+hr {
+ height: 0;
+ border: none;
+ border-top: 1px solid #DDD;
+ margin: 2em 0;
+}
+
+#footer {
+ bottom: 0;
+ clear: both;
+ font-size: x-small;
+ margin: 2em 0 0;
+ padding: 0 1em 1em 1em;
+ vertical-align: top;
+ color: #888;
+}
+
+td.ititle {
+ padding-bottom: 0.75em;
+}
+
+table.memberdecls {
+ border-spacing: 0.125em;
+ line-height: 1.3em;
+}
+
+.mdescLeft,.mdescRight,.memItemLeft,.memItemRight,.memTemplItemLeft,.memTemplItemRight,.memTemplParams {
+ margin: 0;
+ padding: 0;
+}
+
+.mdescLeft,.mdescRight {
+ color: #555;
+}
+
+.memItemLeft,.memItemRight,.memTemplParams {
+ border: 0;
+ font-family: "SF Mono", Menlo, Consolas, "DejaVu Sans Mono", monospace, fixed;
+}
+
+.memItemLeft,.memTemplItemLeft {
+ white-space: nowrap;
+ padding-left: 2em;
+}
+
+.memItemLeft a.el {
+ font-weight: bold;
+}
+
+.memTemplParams {
+ color: #464646;
+ white-space: nowrap;
+}
+
+td.memSeparator {
+ display: none;
+}
+
+td.mlabels-right {
+ vertical-align: top;
+ padding-top: 4px;
+ color: #B4C342;
+}
+
+.memtitle {
+ display: none;
+}
+
+.memtemplate {
+ color: #888;
+ font-style: italic;
+ font-family: "SF Mono", Menlo, Consolas, "DejaVu Sans Mono", monospace, fixed;
+ font-size: small;
+}
+
+.memnav {
+ background-color: #EEE;
+ border: 1px solid #B4C342;
+ text-align: center;
+ margin: 2px;
+ margin-right: 15px;
+ padding: 2px;
+}
+
+.memitem {
+ padding: 0.5em 0.5em 0.25em 0.5em;
+ margin: 1em 0 2em 0;
+}
+
+.memproto {
+ border-bottom: 1px solid #EEE;
+ font-family: "SF Mono", Menlo, Consolas, "DejaVu Sans Mono", monospace, fixed;
+ font-size: 1.09em;
+ font-weight: 600;
+ line-height: 1.41em;
+ margin-bottom: 0.25em;
+ padding-bottom: 0.125em;
+}
+
+.memproto .paramname {
+ font-style: normal;
+ padding-right: 0.25em;
+}
+
+.memdoc {
+ padding: 0;
+}
+
+.memdoc > p:first-child, .memdoc .textblock > p:first-child {
+ font-style: italic;
+ color: #444;
+ margin-bottom: 0.75em;
+}
+
+.paramkey {
+ text-align: right;
+}
+
+.paramtype {
+ color: #666;
+ padding: 0 0.25em 0 0.25em;
+ white-space: nowrap;
+}
+
+.params .paramname {
+ color: #111;
+ white-space: nowrap;
+ font-family: "SF Mono", Menlo, Consolas, "DejaVu Sans Mono", monospace, fixed;
+ font-style: italic;
+ padding-right: 0.5em;
+ vertical-align: top;
+}
+
+.fieldname {
+ color: #000;
+}
+
+.fieldtable {
+ margin-top: 1em;
+ border-collapse: collapse;
+}
+
+.fieldtable tbody tr:first-child {
+ display: none;
+}
+
+td.fieldname {
+ vertical-align: top;
+ font-family: "SF Mono", Menlo, Consolas, "DejaVu Sans Mono", monospace, fixed;
+}
+
+td.fielddoc {
+ padding: 0.125em 0.5em 0 0.25em;
+ vertical-align: top;
+}
+
+.fieldtable tbody tr td {
+ border-top: 1px dashed #DDD;
+ border-bottom: 1px dashed #DDD;
+}
+
+td.fieldtype {
+ color: #666;
+ padding: 0 0.5em 0 0;
+ vertical-align: top;
+ font-family: "SF Mono", Menlo, Consolas, "DejaVu Sans Mono", monospace, fixed;
+}
+
+td.fielddoc p {
+ margin: 0;
+ padding: 0 0.5em 0 0;
+}
+
+p.reference {
+ font-size: x-small;
+ font-style: italic;
+}
+
+.ftvtree {
+ font-family: "DejaVu Sans", Verdana, Helvetica, Arial, sans-serif;
+ margin: 0;
+}
+
+.directory {
+ margin: 0.5em;
+}
+
+.directory h3 {
+ margin: 0;
+ margin-top: 1em;
+ font-size: 11pt;
+}
+
+.directory > h3 {
+ margin-top: 0;
+}
+
+.directory p {
+ margin: 0;
+ white-space: nowrap;
+}
+
+.directory div {
+ display: none;
+ margin: 0;
+}
+
+.directory img {
+ vertical-align: -30%;
+}
+
+td.entry {
+ font-family: "DejaVu Sans", Verdana, Helvetica, Arial, sans-serif;
+ font-weight: 400;
+ padding-right: 1em;
+}
+
+.arrow {
+ color: #CCC;
+ user-select: none;
+ font-size: 80%;
+ display: inline-block;
+ width: 16px;
+ height: 22px;
+ vertical-align: top;
+}
+
+td.entry b {
+ font-family: "DejaVu Sans", Verdana, Helvetica, Arial, sans-serif;
+ font-weight: 400;
+ font-size: 130%;
+}
+
+.directory-alt {
+ font-size: 100%;
+ font-weight: bold;
+}
+
+.directory-alt h3 {
+ margin: 0;
+ margin-top: 1em;
+ font-size: 11pt;
+}
+
+.directory-alt > h3 {
+ margin-top: 0;
+}
+
+.directory-alt p {
+ margin: 0;
+ white-space: nowrap;
+}
+
+.directory-alt div {
+ display: none;
+ margin: 0;
+}
+
+.directory-alt img {
+ vertical-align: -30%;
+}
+
+div.dynheader {
+ margin-top: 8px;
+}
+
+address {
+ font-style: normal;
+ color: #444;
+}
+
+table.doxtable {
+ border-collapse: collapse;
+ margin: 0.5em;
+}
+
+table.doxtable td,table.doxtable th {
+ border: 1px solid #DDD;
+ padding: 3px 7px 2px;
+}
+
+table.doxtable th {
+ background-color: #F3F3F3;
+ color: #000;
+ padding-bottom: 4px;
+ padding-top: 5px;
+ text-align: left;
+ font-weight: bold;
+}
+
+.tabsearch {
+ top: 0;
+ left: 10px;
+ height: 36px;
+ z-index: 101;
+ overflow: hidden;
+ font-size: 13px;
+}
+
+div.navpath {
+ color: #DDD;
+}
+
+.navpath ul {
+ overflow: hidden;
+ margin: 0;
+ padding: 0;
+}
+
+.navpath li {
+ float: left;
+ padding-left: 0;
+ margin-left: 0.5em;
+ padding-right: 1em;
+}
+
+.navpath a {
+ display: block;
+ text-decoration: none;
+ outline: none;
+}
+
+div.summary {
+ font-size: small;
+ font-family: "DejaVu Sans", Verdana, Helvetica, Arial, sans-serif;
+ margin: 0;
+ padding: 0.25em 0;
+ display: none;
+}
+
+div.summary a {
+ white-space: nowrap;
+}
+
+#metabox {
+ display: inline-block;
+ font-size: x-small;
+ font-family: Helvetica, Arial, "DejaVu Sans Condensed", Verdana, sans-serif;
+ position: absolute;
+ right: 0;
+ bottom: 0.25em;
+ color: #666;
+ font-style: italic;
+}
+
+#meta {
+ border-style: hidden;
+ margin-right: 0.25em;
+}
+
+#meta tr, #meta th, #meta td {
+ background-color: transparent;
+ border: 0;
+ margin: 0;
+ font-weight: normal;
+}
+
+#meta th {
+ text-align: right;
+}
+
+#meta th::after {
+ content: ":";
+}
+
+div.line {
+ font-family: "SF Mono", Menlo, Consolas, "DejaVu Sans Mono", monospace, fixed;
+ line-height: 1.4em;
+ white-space: pre-wrap;
+}
+
+.glow {
+ background-color: #2AA198;
+ box-shadow: 0 0 10px #2AA198;
+}
+
+span.lineno {
+ padding-right: 4px;
+ text-align: right;
+ border-right: 2px solid #546E00;
+ background-color: #E8E8E8;
+ white-space: pre;
+}
+
+span.lineno a {
+ background-color: #D8D8D8;
+}
+
+span.lineno a:hover {
+ background-color: #C8C8C8;
+}
+
+.tabs, .tabs2, .navpath {
+ padding: 0.25em 0;
+ font-size: small;
+ font-family: Helvetica, Arial, "DejaVu Sans Condensed", Verdana, sans-serif;
+ margin: 0;
+}
+
+th {
+ text-align: left;
+ font-size: 110%;
+ font-weight: 500;
+}
+
+.mlabel {
+ padding: 0.125em;
+}
+
+#navrow1, #navrow2 {
+ /* Disable menu from Doxygen 1.8.15, it is faked in the template */
+ display: none;
+}
+
+.tablist {
+ margin: 0;
+ padding: 0;
+ display: table;
+}
+
+.tablist li {
+ display: table-cell;
+ line-height: 2em;
+ list-style: none;
+ border-bottom: 0;
+}
+
+.tablist a {
+ display: block;
+ padding: 0 1em 0 0;
+ text-decoration: none;
+ outline: none;
+}
+
+.tabs3 .tablist a {
+ padding: 0 10px;
+}
+
+.tablist li.current a {
+ color: #222;
+}
+
+span.icon {
+ display: none;
+}
diff --git a/subprojects/d2tk/pugl/examples/cube_view.h b/subprojects/d2tk/pugl/examples/cube_view.h
new file mode 100644
index 00000000..9fd23495
--- /dev/null
+++ b/subprojects/d2tk/pugl/examples/cube_view.h
@@ -0,0 +1,82 @@
+/*
+ Copyright 2012-2020 David Robillard <http://drobilla.net>
+
+ Permission to use, copy, modify, and/or distribute this software for any
+ purpose with or without fee is hereby granted, provided that the above
+ copyright notice and this permission notice appear in all copies.
+
+ THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+
+#define GL_SILENCE_DEPRECATION 1
+
+#include "demo_utils.h"
+
+#include "pugl/gl.h"
+
+static inline void
+reshapeCube(const float width, const float height)
+{
+ const float aspect = width / height;
+
+ glEnable(GL_DEPTH_TEST);
+ glDepthFunc(GL_LESS);
+ glClearColor(0.2f, 0.2f, 0.2f, 1.0f);
+
+ glMatrixMode(GL_PROJECTION);
+ glLoadIdentity();
+ glViewport(0, 0, (int)width, (int)height);
+
+ float projection[16];
+ perspective(projection, 1.8f, aspect, 1.0f, 100.0f);
+ glLoadMatrixf(projection);
+}
+
+static inline void
+displayCube(PuglView* const view,
+ const float distance,
+ const float xAngle,
+ const float yAngle,
+ const bool entered)
+{
+ glMatrixMode(GL_MODELVIEW);
+ glLoadIdentity();
+ glTranslatef(0.0f, 0.0f, distance * -1.0f);
+ glRotatef(xAngle, 0.0f, 1.0f, 0.0f);
+ glRotatef(yAngle, 1.0f, 0.0f, 0.0f);
+
+ const float bg = entered ? 0.2f : 0.0f;
+ glClearColor(bg, bg, bg, 1.0f);
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+
+ if (puglHasFocus(view)) {
+ // Draw cube surfaces
+ glEnableClientState(GL_VERTEX_ARRAY);
+ glEnableClientState(GL_COLOR_ARRAY);
+ glVertexPointer(3, GL_FLOAT, 0, cubeStripVertices);
+ glColorPointer(3, GL_FLOAT, 0, cubeStripVertices);
+ glDrawArrays(GL_TRIANGLE_STRIP, 0, 14);
+ glDisableClientState(GL_COLOR_ARRAY);
+ glDisableClientState(GL_VERTEX_ARRAY);
+
+ glColor3f(0.0f, 0.0f, 0.0f);
+ } else {
+ glColor3f(1.0f, 1.0f, 1.0f);
+ }
+
+ // Draw cube wireframe
+ glEnableClientState(GL_VERTEX_ARRAY);
+ glVertexPointer(3, GL_FLOAT, 0, cubeFrontLineLoop);
+ glDrawArrays(GL_LINE_LOOP, 0, 4);
+ glVertexPointer(3, GL_FLOAT, 0, cubeBackLineLoop);
+ glDrawArrays(GL_LINE_LOOP, 0, 4);
+ glVertexPointer(3, GL_FLOAT, 0, cubeSideLines);
+ glDrawArrays(GL_LINES, 0, 8);
+ glDisableClientState(GL_VERTEX_ARRAY);
+}
diff --git a/subprojects/d2tk/pugl/examples/demo_utils.h b/subprojects/d2tk/pugl/examples/demo_utils.h
new file mode 100644
index 00000000..9a1cb7a1
--- /dev/null
+++ b/subprojects/d2tk/pugl/examples/demo_utils.h
@@ -0,0 +1,175 @@
+/*
+ Copyright 2012-2019 David Robillard <http://drobilla.net>
+
+ Permission to use, copy, modify, and/or distribute this software for any
+ purpose with or without fee is hereby granted, provided that the above
+ copyright notice and this permission notice appear in all copies.
+
+ THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+
+#ifndef PUGL_DEMO_UTILS_H
+#define PUGL_DEMO_UTILS_H
+
+#include "pugl/pugl.h"
+
+#include <math.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+
+typedef struct {
+ double lastReportTime;
+} PuglFpsPrinter;
+
+typedef float vec4[4];
+typedef vec4 mat4[4];
+
+// clang-format off
+
+static const float cubeStripVertices[] = {
+ -1.0f, 1.0f, 1.0f, // Front top left
+ 1.0f, 1.0f, 1.0f, // Front top right
+ -1.0f, -1.0f, 1.0f, // Front bottom left
+ 1.0f, -1.0f, 1.0f, // Front bottom right
+ 1.0f, -1.0f, -1.0f, // Back bottom right
+ 1.0f, 1.0f, 1.0f, // Front top right
+ 1.0f, 1.0f, -1.0f, // Back top right
+ -1.0f, 1.0f, 1.0f, // Front top left
+ -1.0f, 1.0f, -1.0f, // Back top left
+ -1.0f, -1.0f, 1.0f, // Front bottom left
+ -1.0f, -1.0f, -1.0f, // Back bottom left
+ 1.0f, -1.0f, -1.0f, // Back bottom right
+ -1.0f, 1.0f, -1.0f, // Back top left
+ 1.0f, 1.0f, -1.0f // Back top right
+};
+
+static const float cubeFrontLineLoop[] = {
+ -1.0f, 1.0f, 1.0f, // Front top left
+ 1.0f, 1.0f, 1.0f, // Front top right
+ 1.0f, -1.0f, 1.0f, // Front bottom right
+ -1.0f, -1.0f, 1.0f, // Front bottom left
+};
+
+static const float cubeBackLineLoop[] = {
+ -1.0f, 1.0f, -1.0f, // Back top left
+ 1.0f, 1.0f, -1.0f, // Back top right
+ 1.0f, -1.0f, -1.0f, // Back bottom right
+ -1.0f, -1.0f, -1.0f, // Back bottom left
+};
+
+static const float cubeSideLines[] = {
+ -1.0f, 1.0f, 1.0f, // Front top left
+ -1.0f, 1.0f, -1.0f, // Back top left
+
+ -1.0f, -1.0f, 1.0f, // Front bottom left
+ -1.0f, -1.0f, -1.0f, // Back bottom left
+
+ 1.0f, 1.0f, 1.0f, // Front top right
+ 1.0f, 1.0f, -1.0f, // Back top right
+
+ 1.0f, -1.0f, 1.0f, // Front bottom right
+ 1.0f, -1.0f, -1.0f, // Back bottom right
+};
+
+// clang-format on
+
+static inline void
+mat4Identity(mat4 m)
+{
+ for (int c = 0; c < 4; ++c) {
+ for (int r = 0; r < 4; ++r) {
+ m[c][r] = c == r ? 1.0f : 0.0f;
+ }
+ }
+}
+
+static inline void
+mat4Translate(mat4 m, const float x, const float y, const float z)
+{
+ m[3][0] = x;
+ m[3][1] = y;
+ m[3][2] = z;
+}
+
+static inline void
+mat4Mul(mat4 m, mat4 a, mat4 b)
+{
+ for (int c = 0; c < 4; ++c) {
+ for (int r = 0; r < 4; ++r) {
+ m[c][r] = 0.0f;
+ for (int k = 0; k < 4; ++k) {
+ m[c][r] += a[k][r] * b[c][k];
+ }
+ }
+ }
+}
+
+static inline void
+mat4Ortho(mat4 m,
+ const float l,
+ const float r,
+ const float b,
+ const float t,
+ const float n,
+ const float f)
+{
+ m[0][0] = 2.0f / (r - l);
+ m[0][1] = m[0][2] = m[0][3] = 0.0f;
+
+ m[1][1] = 2.0f / (t - b);
+ m[1][0] = m[1][2] = m[1][3] = 0.0f;
+
+ m[2][2] = -2.0f / (f - n);
+ m[2][0] = m[2][1] = m[2][3] = 0.0f;
+
+ m[3][0] = -(r + l) / (r - l);
+ m[3][1] = -(t + b) / (t - b);
+ m[3][2] = -(f + n) / (f - n);
+ m[3][3] = 1.0f;
+}
+
+/** Calculate a projection matrix for a given perspective. */
+static inline void
+perspective(float* m, float fov, float aspect, float zNear, float zFar)
+{
+ const float h = tanf(fov);
+ const float w = h / aspect;
+ const float depth = zNear - zFar;
+ const float q = (zFar + zNear) / depth;
+ const float qn = 2 * zFar * zNear / depth;
+
+ // clang-format off
+ m[0] = w; m[1] = 0; m[2] = 0; m[3] = 0;
+ m[4] = 0; m[5] = h; m[6] = 0; m[7] = 0;
+ m[8] = 0; m[9] = 0; m[10] = q; m[11] = -1;
+ m[12] = 0; m[13] = 0; m[14] = qn; m[15] = 0;
+ // clang-format on
+}
+
+static inline void
+puglPrintFps(const PuglWorld* world,
+ PuglFpsPrinter* printer,
+ unsigned* const framesDrawn)
+{
+ const double thisTime = puglGetTime(world);
+ if (thisTime > printer->lastReportTime + 5) {
+ const double fps = *framesDrawn / (thisTime - printer->lastReportTime);
+ fprintf(stderr,
+ "%u frames in %.0f seconds = %.3f FPS\n",
+ *framesDrawn,
+ thisTime - printer->lastReportTime,
+ fps);
+
+ printer->lastReportTime = thisTime;
+ *framesDrawn = 0;
+ }
+}
+
+#endif // PUGL_DEMO_UTILS_H
diff --git a/subprojects/d2tk/pugl/examples/glad/glad.c b/subprojects/d2tk/pugl/examples/glad/glad.c
new file mode 100644
index 00000000..38f442c8
--- /dev/null
+++ b/subprojects/d2tk/pugl/examples/glad/glad.c
@@ -0,0 +1,1138 @@
+/*
+
+ OpenGL loader generated by glad 0.1.32 on -.
+
+ Language/Generator: C/C++
+ Specification: gl
+ APIs: gl=3.3
+ Profile: core
+ Extensions:
+
+ Loader: True
+ Local files: True
+ Omit khrplatform: False
+ Reproducible: True
+
+ Commandline:
+ --profile="core" --api="gl=3.3" --generator="c" --spec="gl" --local-files --extensions=""
+ Online:
+ https://glad.dav1d.de/#profile=core&language=c&specification=gl&loader=on&api=gl%3D3.3
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "glad.h"
+
+static void* get_proc(const char *namez);
+
+#if defined(_WIN32) || defined(__CYGWIN__)
+#undef APIENTRY
+#include <windows.h>
+static HMODULE libGL;
+
+typedef void* (APIENTRYP PFNWGLGETPROCADDRESSPROC_PRIVATE)(const char*);
+static PFNWGLGETPROCADDRESSPROC_PRIVATE gladGetProcAddressPtr;
+
+#ifdef _MSC_VER
+#ifdef __has_include
+ #if __has_include(<winapifamily.h>)
+ #define HAVE_WINAPIFAMILY 1
+ #endif
+#elif _MSC_VER >= 1700 && !_USING_V110_SDK71_
+ #define HAVE_WINAPIFAMILY 1
+#endif
+#endif
+
+#ifdef HAVE_WINAPIFAMILY
+ #include <winapifamily.h>
+ #if !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) && WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP)
+ #define IS_UWP 1
+ #endif
+#endif
+
+static
+int open_gl(void) {
+#ifndef IS_UWP
+ libGL = LoadLibraryW(L"opengl32.dll");
+ if(libGL != NULL) {
+ void (* tmp)(void);
+ tmp = (void(*)(void)) GetProcAddress(libGL, "wglGetProcAddress");
+ gladGetProcAddressPtr = (PFNWGLGETPROCADDRESSPROC_PRIVATE) tmp;
+ return gladGetProcAddressPtr != NULL;
+ }
+#endif
+
+ return 0;
+}
+
+static
+void close_gl(void) {
+ if(libGL != NULL) {
+ FreeLibrary((HMODULE) libGL);
+ libGL = NULL;
+ }
+}
+#else
+#include <dlfcn.h>
+static void* libGL;
+
+#if !defined(__APPLE__) && !defined(__HAIKU__)
+typedef void* (APIENTRYP PFNGLXGETPROCADDRESSPROC_PRIVATE)(const char*);
+static PFNGLXGETPROCADDRESSPROC_PRIVATE gladGetProcAddressPtr;
+#endif
+
+static
+int open_gl(void) {
+#ifdef __APPLE__
+ static const char *NAMES[] = {
+ "../Frameworks/OpenGL.framework/OpenGL",
+ "/Library/Frameworks/OpenGL.framework/OpenGL",
+ "/System/Library/Frameworks/OpenGL.framework/OpenGL",
+ "/System/Library/Frameworks/OpenGL.framework/Versions/Current/OpenGL"
+ };
+#else
+ static const char *NAMES[] = {"libGL.so.1", "libGL.so"};
+#endif
+
+ unsigned int index = 0;
+ for(index = 0; index < (sizeof(NAMES) / sizeof(NAMES[0])); index++) {
+ libGL = dlopen(NAMES[index], RTLD_NOW | RTLD_GLOBAL);
+
+ if(libGL != NULL) {
+#if defined(__APPLE__) || defined(__HAIKU__)
+ return 1;
+#else
+ gladGetProcAddressPtr = (PFNGLXGETPROCADDRESSPROC_PRIVATE)dlsym(libGL,
+ "glXGetProcAddressARB");
+ return gladGetProcAddressPtr != NULL;
+#endif
+ }
+ }
+
+ return 0;
+}
+
+static
+void close_gl(void) {
+ if(libGL != NULL) {
+ dlclose(libGL);
+ libGL = NULL;
+ }
+}
+#endif
+
+static
+void* get_proc(const char *namez) {
+ void* result = NULL;
+ if(libGL == NULL) return NULL;
+
+#if !defined(__APPLE__) && !defined(__HAIKU__)
+ if(gladGetProcAddressPtr != NULL) {
+ result = gladGetProcAddressPtr(namez);
+ }
+#endif
+ if(result == NULL) {
+#if defined(_WIN32) || defined(__CYGWIN__)
+ result = (void*)GetProcAddress((HMODULE) libGL, namez);
+#else
+ result = dlsym(libGL, namez);
+#endif
+ }
+
+ return result;
+}
+
+int gladLoadGL(void) {
+ int status = 0;
+
+ if(open_gl()) {
+ status = gladLoadGLLoader(&get_proc);
+ close_gl();
+ }
+
+ return status;
+}
+
+struct gladGLversionStruct GLVersion = { 0, 0 };
+
+#if defined(GL_ES_VERSION_3_0) || defined(GL_VERSION_3_0)
+#define _GLAD_IS_SOME_NEW_VERSION 1
+#endif
+
+static int max_loaded_major;
+static int max_loaded_minor;
+
+static const char *exts = NULL;
+static int num_exts_i = 0;
+static char **exts_i = NULL;
+
+static int get_exts(void) {
+#ifdef _GLAD_IS_SOME_NEW_VERSION
+ if(max_loaded_major < 3) {
+#endif
+ exts = (const char *)glGetString(GL_EXTENSIONS);
+#ifdef _GLAD_IS_SOME_NEW_VERSION
+ } else {
+ unsigned int index;
+
+ num_exts_i = 0;
+ glGetIntegerv(GL_NUM_EXTENSIONS, &num_exts_i);
+ if (num_exts_i > 0) {
+ exts_i = (char **)malloc((size_t)num_exts_i * (sizeof *exts_i));
+ }
+
+ if (exts_i == NULL) {
+ return 0;
+ }
+
+ for(index = 0; index < (unsigned)num_exts_i; index++) {
+ const char *gl_str_tmp = (const char*)glGetStringi(GL_EXTENSIONS, index);
+ size_t len = strlen(gl_str_tmp);
+
+ char *local_str = (char*)malloc((len+1) * sizeof(char));
+ if(local_str != NULL) {
+ memcpy(local_str, gl_str_tmp, (len+1) * sizeof(char));
+ }
+ exts_i[index] = local_str;
+ }
+ }
+#endif
+ return 1;
+}
+
+static void free_exts(void) {
+ if (exts_i != NULL) {
+ int index;
+ for(index = 0; index < num_exts_i; index++) {
+ free((char *)exts_i[index]);
+ }
+ free((void *)exts_i);
+ exts_i = NULL;
+ }
+}
+
+static int has_ext(const char *ext) {
+#ifdef _GLAD_IS_SOME_NEW_VERSION
+ if(max_loaded_major < 3) {
+#endif
+ const char *extensions;
+ const char *loc;
+ const char *terminator;
+ extensions = exts;
+ if(extensions == NULL || ext == NULL) {
+ return 0;
+ }
+
+ while(1) {
+ loc = strstr(extensions, ext);
+ if(loc == NULL) {
+ return 0;
+ }
+
+ terminator = loc + strlen(ext);
+ if((loc == extensions || *(loc - 1) == ' ') &&
+ (*terminator == ' ' || *terminator == '\0')) {
+ return 1;
+ }
+ extensions = terminator;
+ }
+#ifdef _GLAD_IS_SOME_NEW_VERSION
+ } else {
+ int index;
+ if(exts_i == NULL) return 0;
+ for(index = 0; index < num_exts_i; index++) {
+ const char *e = exts_i[index];
+
+ if(exts_i[index] != NULL && strcmp(e, ext) == 0) {
+ return 1;
+ }
+ }
+ }
+#endif
+
+ return 0;
+}
+int GLAD_GL_VERSION_1_0 = 0;
+int GLAD_GL_VERSION_1_1 = 0;
+int GLAD_GL_VERSION_1_2 = 0;
+int GLAD_GL_VERSION_1_3 = 0;
+int GLAD_GL_VERSION_1_4 = 0;
+int GLAD_GL_VERSION_1_5 = 0;
+int GLAD_GL_VERSION_2_0 = 0;
+int GLAD_GL_VERSION_2_1 = 0;
+int GLAD_GL_VERSION_3_0 = 0;
+int GLAD_GL_VERSION_3_1 = 0;
+int GLAD_GL_VERSION_3_2 = 0;
+int GLAD_GL_VERSION_3_3 = 0;
+PFNGLACTIVETEXTUREPROC glad_glActiveTexture = NULL;
+PFNGLATTACHSHADERPROC glad_glAttachShader = NULL;
+PFNGLBEGINCONDITIONALRENDERPROC glad_glBeginConditionalRender = NULL;
+PFNGLBEGINQUERYPROC glad_glBeginQuery = NULL;
+PFNGLBEGINTRANSFORMFEEDBACKPROC glad_glBeginTransformFeedback = NULL;
+PFNGLBINDATTRIBLOCATIONPROC glad_glBindAttribLocation = NULL;
+PFNGLBINDBUFFERPROC glad_glBindBuffer = NULL;
+PFNGLBINDBUFFERBASEPROC glad_glBindBufferBase = NULL;
+PFNGLBINDBUFFERRANGEPROC glad_glBindBufferRange = NULL;
+PFNGLBINDFRAGDATALOCATIONPROC glad_glBindFragDataLocation = NULL;
+PFNGLBINDFRAGDATALOCATIONINDEXEDPROC glad_glBindFragDataLocationIndexed = NULL;
+PFNGLBINDFRAMEBUFFERPROC glad_glBindFramebuffer = NULL;
+PFNGLBINDRENDERBUFFERPROC glad_glBindRenderbuffer = NULL;
+PFNGLBINDSAMPLERPROC glad_glBindSampler = NULL;
+PFNGLBINDTEXTUREPROC glad_glBindTexture = NULL;
+PFNGLBINDVERTEXARRAYPROC glad_glBindVertexArray = NULL;
+PFNGLBLENDCOLORPROC glad_glBlendColor = NULL;
+PFNGLBLENDEQUATIONPROC glad_glBlendEquation = NULL;
+PFNGLBLENDEQUATIONSEPARATEPROC glad_glBlendEquationSeparate = NULL;
+PFNGLBLENDFUNCPROC glad_glBlendFunc = NULL;
+PFNGLBLENDFUNCSEPARATEPROC glad_glBlendFuncSeparate = NULL;
+PFNGLBLITFRAMEBUFFERPROC glad_glBlitFramebuffer = NULL;
+PFNGLBUFFERDATAPROC glad_glBufferData = NULL;
+PFNGLBUFFERSUBDATAPROC glad_glBufferSubData = NULL;
+PFNGLCHECKFRAMEBUFFERSTATUSPROC glad_glCheckFramebufferStatus = NULL;
+PFNGLCLAMPCOLORPROC glad_glClampColor = NULL;
+PFNGLCLEARPROC glad_glClear = NULL;
+PFNGLCLEARBUFFERFIPROC glad_glClearBufferfi = NULL;
+PFNGLCLEARBUFFERFVPROC glad_glClearBufferfv = NULL;
+PFNGLCLEARBUFFERIVPROC glad_glClearBufferiv = NULL;
+PFNGLCLEARBUFFERUIVPROC glad_glClearBufferuiv = NULL;
+PFNGLCLEARCOLORPROC glad_glClearColor = NULL;
+PFNGLCLEARDEPTHPROC glad_glClearDepth = NULL;
+PFNGLCLEARSTENCILPROC glad_glClearStencil = NULL;
+PFNGLCLIENTWAITSYNCPROC glad_glClientWaitSync = NULL;
+PFNGLCOLORMASKPROC glad_glColorMask = NULL;
+PFNGLCOLORMASKIPROC glad_glColorMaski = NULL;
+PFNGLCOLORP3UIPROC glad_glColorP3ui = NULL;
+PFNGLCOLORP3UIVPROC glad_glColorP3uiv = NULL;
+PFNGLCOLORP4UIPROC glad_glColorP4ui = NULL;
+PFNGLCOLORP4UIVPROC glad_glColorP4uiv = NULL;
+PFNGLCOMPILESHADERPROC glad_glCompileShader = NULL;
+PFNGLCOMPRESSEDTEXIMAGE1DPROC glad_glCompressedTexImage1D = NULL;
+PFNGLCOMPRESSEDTEXIMAGE2DPROC glad_glCompressedTexImage2D = NULL;
+PFNGLCOMPRESSEDTEXIMAGE3DPROC glad_glCompressedTexImage3D = NULL;
+PFNGLCOMPRESSEDTEXSUBIMAGE1DPROC glad_glCompressedTexSubImage1D = NULL;
+PFNGLCOMPRESSEDTEXSUBIMAGE2DPROC glad_glCompressedTexSubImage2D = NULL;
+PFNGLCOMPRESSEDTEXSUBIMAGE3DPROC glad_glCompressedTexSubImage3D = NULL;
+PFNGLCOPYBUFFERSUBDATAPROC glad_glCopyBufferSubData = NULL;
+PFNGLCOPYTEXIMAGE1DPROC glad_glCopyTexImage1D = NULL;
+PFNGLCOPYTEXIMAGE2DPROC glad_glCopyTexImage2D = NULL;
+PFNGLCOPYTEXSUBIMAGE1DPROC glad_glCopyTexSubImage1D = NULL;
+PFNGLCOPYTEXSUBIMAGE2DPROC glad_glCopyTexSubImage2D = NULL;
+PFNGLCOPYTEXSUBIMAGE3DPROC glad_glCopyTexSubImage3D = NULL;
+PFNGLCREATEPROGRAMPROC glad_glCreateProgram = NULL;
+PFNGLCREATESHADERPROC glad_glCreateShader = NULL;
+PFNGLCULLFACEPROC glad_glCullFace = NULL;
+PFNGLDELETEBUFFERSPROC glad_glDeleteBuffers = NULL;
+PFNGLDELETEFRAMEBUFFERSPROC glad_glDeleteFramebuffers = NULL;
+PFNGLDELETEPROGRAMPROC glad_glDeleteProgram = NULL;
+PFNGLDELETEQUERIESPROC glad_glDeleteQueries = NULL;
+PFNGLDELETERENDERBUFFERSPROC glad_glDeleteRenderbuffers = NULL;
+PFNGLDELETESAMPLERSPROC glad_glDeleteSamplers = NULL;
+PFNGLDELETESHADERPROC glad_glDeleteShader = NULL;
+PFNGLDELETESYNCPROC glad_glDeleteSync = NULL;
+PFNGLDELETETEXTURESPROC glad_glDeleteTextures = NULL;
+PFNGLDELETEVERTEXARRAYSPROC glad_glDeleteVertexArrays = NULL;
+PFNGLDEPTHFUNCPROC glad_glDepthFunc = NULL;
+PFNGLDEPTHMASKPROC glad_glDepthMask = NULL;
+PFNGLDEPTHRANGEPROC glad_glDepthRange = NULL;
+PFNGLDETACHSHADERPROC glad_glDetachShader = NULL;
+PFNGLDISABLEPROC glad_glDisable = NULL;
+PFNGLDISABLEVERTEXATTRIBARRAYPROC glad_glDisableVertexAttribArray = NULL;
+PFNGLDISABLEIPROC glad_glDisablei = NULL;
+PFNGLDRAWARRAYSPROC glad_glDrawArrays = NULL;
+PFNGLDRAWARRAYSINSTANCEDPROC glad_glDrawArraysInstanced = NULL;
+PFNGLDRAWBUFFERPROC glad_glDrawBuffer = NULL;
+PFNGLDRAWBUFFERSPROC glad_glDrawBuffers = NULL;
+PFNGLDRAWELEMENTSPROC glad_glDrawElements = NULL;
+PFNGLDRAWELEMENTSBASEVERTEXPROC glad_glDrawElementsBaseVertex = NULL;
+PFNGLDRAWELEMENTSINSTANCEDPROC glad_glDrawElementsInstanced = NULL;
+PFNGLDRAWELEMENTSINSTANCEDBASEVERTEXPROC glad_glDrawElementsInstancedBaseVertex = NULL;
+PFNGLDRAWRANGEELEMENTSPROC glad_glDrawRangeElements = NULL;
+PFNGLDRAWRANGEELEMENTSBASEVERTEXPROC glad_glDrawRangeElementsBaseVertex = NULL;
+PFNGLENABLEPROC glad_glEnable = NULL;
+PFNGLENABLEVERTEXATTRIBARRAYPROC glad_glEnableVertexAttribArray = NULL;
+PFNGLENABLEIPROC glad_glEnablei = NULL;
+PFNGLENDCONDITIONALRENDERPROC glad_glEndConditionalRender = NULL;
+PFNGLENDQUERYPROC glad_glEndQuery = NULL;
+PFNGLENDTRANSFORMFEEDBACKPROC glad_glEndTransformFeedback = NULL;
+PFNGLFENCESYNCPROC glad_glFenceSync = NULL;
+PFNGLFINISHPROC glad_glFinish = NULL;
+PFNGLFLUSHPROC glad_glFlush = NULL;
+PFNGLFLUSHMAPPEDBUFFERRANGEPROC glad_glFlushMappedBufferRange = NULL;
+PFNGLFRAMEBUFFERRENDERBUFFERPROC glad_glFramebufferRenderbuffer = NULL;
+PFNGLFRAMEBUFFERTEXTUREPROC glad_glFramebufferTexture = NULL;
+PFNGLFRAMEBUFFERTEXTURE1DPROC glad_glFramebufferTexture1D = NULL;
+PFNGLFRAMEBUFFERTEXTURE2DPROC glad_glFramebufferTexture2D = NULL;
+PFNGLFRAMEBUFFERTEXTURE3DPROC glad_glFramebufferTexture3D = NULL;
+PFNGLFRAMEBUFFERTEXTURELAYERPROC glad_glFramebufferTextureLayer = NULL;
+PFNGLFRONTFACEPROC glad_glFrontFace = NULL;
+PFNGLGENBUFFERSPROC glad_glGenBuffers = NULL;
+PFNGLGENFRAMEBUFFERSPROC glad_glGenFramebuffers = NULL;
+PFNGLGENQUERIESPROC glad_glGenQueries = NULL;
+PFNGLGENRENDERBUFFERSPROC glad_glGenRenderbuffers = NULL;
+PFNGLGENSAMPLERSPROC glad_glGenSamplers = NULL;
+PFNGLGENTEXTURESPROC glad_glGenTextures = NULL;
+PFNGLGENVERTEXARRAYSPROC glad_glGenVertexArrays = NULL;
+PFNGLGENERATEMIPMAPPROC glad_glGenerateMipmap = NULL;
+PFNGLGETACTIVEATTRIBPROC glad_glGetActiveAttrib = NULL;
+PFNGLGETACTIVEUNIFORMPROC glad_glGetActiveUniform = NULL;
+PFNGLGETACTIVEUNIFORMBLOCKNAMEPROC glad_glGetActiveUniformBlockName = NULL;
+PFNGLGETACTIVEUNIFORMBLOCKIVPROC glad_glGetActiveUniformBlockiv = NULL;
+PFNGLGETACTIVEUNIFORMNAMEPROC glad_glGetActiveUniformName = NULL;
+PFNGLGETACTIVEUNIFORMSIVPROC glad_glGetActiveUniformsiv = NULL;
+PFNGLGETATTACHEDSHADERSPROC glad_glGetAttachedShaders = NULL;
+PFNGLGETATTRIBLOCATIONPROC glad_glGetAttribLocation = NULL;
+PFNGLGETBOOLEANI_VPROC glad_glGetBooleani_v = NULL;
+PFNGLGETBOOLEANVPROC glad_glGetBooleanv = NULL;
+PFNGLGETBUFFERPARAMETERI64VPROC glad_glGetBufferParameteri64v = NULL;
+PFNGLGETBUFFERPARAMETERIVPROC glad_glGetBufferParameteriv = NULL;
+PFNGLGETBUFFERPOINTERVPROC glad_glGetBufferPointerv = NULL;
+PFNGLGETBUFFERSUBDATAPROC glad_glGetBufferSubData = NULL;
+PFNGLGETCOMPRESSEDTEXIMAGEPROC glad_glGetCompressedTexImage = NULL;
+PFNGLGETDOUBLEVPROC glad_glGetDoublev = NULL;
+PFNGLGETERRORPROC glad_glGetError = NULL;
+PFNGLGETFLOATVPROC glad_glGetFloatv = NULL;
+PFNGLGETFRAGDATAINDEXPROC glad_glGetFragDataIndex = NULL;
+PFNGLGETFRAGDATALOCATIONPROC glad_glGetFragDataLocation = NULL;
+PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVPROC glad_glGetFramebufferAttachmentParameteriv = NULL;
+PFNGLGETINTEGER64I_VPROC glad_glGetInteger64i_v = NULL;
+PFNGLGETINTEGER64VPROC glad_glGetInteger64v = NULL;
+PFNGLGETINTEGERI_VPROC glad_glGetIntegeri_v = NULL;
+PFNGLGETINTEGERVPROC glad_glGetIntegerv = NULL;
+PFNGLGETMULTISAMPLEFVPROC glad_glGetMultisamplefv = NULL;
+PFNGLGETPROGRAMINFOLOGPROC glad_glGetProgramInfoLog = NULL;
+PFNGLGETPROGRAMIVPROC glad_glGetProgramiv = NULL;
+PFNGLGETQUERYOBJECTI64VPROC glad_glGetQueryObjecti64v = NULL;
+PFNGLGETQUERYOBJECTIVPROC glad_glGetQueryObjectiv = NULL;
+PFNGLGETQUERYOBJECTUI64VPROC glad_glGetQueryObjectui64v = NULL;
+PFNGLGETQUERYOBJECTUIVPROC glad_glGetQueryObjectuiv = NULL;
+PFNGLGETQUERYIVPROC glad_glGetQueryiv = NULL;
+PFNGLGETRENDERBUFFERPARAMETERIVPROC glad_glGetRenderbufferParameteriv = NULL;
+PFNGLGETSAMPLERPARAMETERIIVPROC glad_glGetSamplerParameterIiv = NULL;
+PFNGLGETSAMPLERPARAMETERIUIVPROC glad_glGetSamplerParameterIuiv = NULL;
+PFNGLGETSAMPLERPARAMETERFVPROC glad_glGetSamplerParameterfv = NULL;
+PFNGLGETSAMPLERPARAMETERIVPROC glad_glGetSamplerParameteriv = NULL;
+PFNGLGETSHADERINFOLOGPROC glad_glGetShaderInfoLog = NULL;
+PFNGLGETSHADERSOURCEPROC glad_glGetShaderSource = NULL;
+PFNGLGETSHADERIVPROC glad_glGetShaderiv = NULL;
+PFNGLGETSTRINGPROC glad_glGetString = NULL;
+PFNGLGETSTRINGIPROC glad_glGetStringi = NULL;
+PFNGLGETSYNCIVPROC glad_glGetSynciv = NULL;
+PFNGLGETTEXIMAGEPROC glad_glGetTexImage = NULL;
+PFNGLGETTEXLEVELPARAMETERFVPROC glad_glGetTexLevelParameterfv = NULL;
+PFNGLGETTEXLEVELPARAMETERIVPROC glad_glGetTexLevelParameteriv = NULL;
+PFNGLGETTEXPARAMETERIIVPROC glad_glGetTexParameterIiv = NULL;
+PFNGLGETTEXPARAMETERIUIVPROC glad_glGetTexParameterIuiv = NULL;
+PFNGLGETTEXPARAMETERFVPROC glad_glGetTexParameterfv = NULL;
+PFNGLGETTEXPARAMETERIVPROC glad_glGetTexParameteriv = NULL;
+PFNGLGETTRANSFORMFEEDBACKVARYINGPROC glad_glGetTransformFeedbackVarying = NULL;
+PFNGLGETUNIFORMBLOCKINDEXPROC glad_glGetUniformBlockIndex = NULL;
+PFNGLGETUNIFORMINDICESPROC glad_glGetUniformIndices = NULL;
+PFNGLGETUNIFORMLOCATIONPROC glad_glGetUniformLocation = NULL;
+PFNGLGETUNIFORMFVPROC glad_glGetUniformfv = NULL;
+PFNGLGETUNIFORMIVPROC glad_glGetUniformiv = NULL;
+PFNGLGETUNIFORMUIVPROC glad_glGetUniformuiv = NULL;
+PFNGLGETVERTEXATTRIBIIVPROC glad_glGetVertexAttribIiv = NULL;
+PFNGLGETVERTEXATTRIBIUIVPROC glad_glGetVertexAttribIuiv = NULL;
+PFNGLGETVERTEXATTRIBPOINTERVPROC glad_glGetVertexAttribPointerv = NULL;
+PFNGLGETVERTEXATTRIBDVPROC glad_glGetVertexAttribdv = NULL;
+PFNGLGETVERTEXATTRIBFVPROC glad_glGetVertexAttribfv = NULL;
+PFNGLGETVERTEXATTRIBIVPROC glad_glGetVertexAttribiv = NULL;
+PFNGLHINTPROC glad_glHint = NULL;
+PFNGLISBUFFERPROC glad_glIsBuffer = NULL;
+PFNGLISENABLEDPROC glad_glIsEnabled = NULL;
+PFNGLISENABLEDIPROC glad_glIsEnabledi = NULL;
+PFNGLISFRAMEBUFFERPROC glad_glIsFramebuffer = NULL;
+PFNGLISPROGRAMPROC glad_glIsProgram = NULL;
+PFNGLISQUERYPROC glad_glIsQuery = NULL;
+PFNGLISRENDERBUFFERPROC glad_glIsRenderbuffer = NULL;
+PFNGLISSAMPLERPROC glad_glIsSampler = NULL;
+PFNGLISSHADERPROC glad_glIsShader = NULL;
+PFNGLISSYNCPROC glad_glIsSync = NULL;
+PFNGLISTEXTUREPROC glad_glIsTexture = NULL;
+PFNGLISVERTEXARRAYPROC glad_glIsVertexArray = NULL;
+PFNGLLINEWIDTHPROC glad_glLineWidth = NULL;
+PFNGLLINKPROGRAMPROC glad_glLinkProgram = NULL;
+PFNGLLOGICOPPROC glad_glLogicOp = NULL;
+PFNGLMAPBUFFERPROC glad_glMapBuffer = NULL;
+PFNGLMAPBUFFERRANGEPROC glad_glMapBufferRange = NULL;
+PFNGLMULTIDRAWARRAYSPROC glad_glMultiDrawArrays = NULL;
+PFNGLMULTIDRAWELEMENTSPROC glad_glMultiDrawElements = NULL;
+PFNGLMULTIDRAWELEMENTSBASEVERTEXPROC glad_glMultiDrawElementsBaseVertex = NULL;
+PFNGLMULTITEXCOORDP1UIPROC glad_glMultiTexCoordP1ui = NULL;
+PFNGLMULTITEXCOORDP1UIVPROC glad_glMultiTexCoordP1uiv = NULL;
+PFNGLMULTITEXCOORDP2UIPROC glad_glMultiTexCoordP2ui = NULL;
+PFNGLMULTITEXCOORDP2UIVPROC glad_glMultiTexCoordP2uiv = NULL;
+PFNGLMULTITEXCOORDP3UIPROC glad_glMultiTexCoordP3ui = NULL;
+PFNGLMULTITEXCOORDP3UIVPROC glad_glMultiTexCoordP3uiv = NULL;
+PFNGLMULTITEXCOORDP4UIPROC glad_glMultiTexCoordP4ui = NULL;
+PFNGLMULTITEXCOORDP4UIVPROC glad_glMultiTexCoordP4uiv = NULL;
+PFNGLNORMALP3UIPROC glad_glNormalP3ui = NULL;
+PFNGLNORMALP3UIVPROC glad_glNormalP3uiv = NULL;
+PFNGLPIXELSTOREFPROC glad_glPixelStoref = NULL;
+PFNGLPIXELSTOREIPROC glad_glPixelStorei = NULL;
+PFNGLPOINTPARAMETERFPROC glad_glPointParameterf = NULL;
+PFNGLPOINTPARAMETERFVPROC glad_glPointParameterfv = NULL;
+PFNGLPOINTPARAMETERIPROC glad_glPointParameteri = NULL;
+PFNGLPOINTPARAMETERIVPROC glad_glPointParameteriv = NULL;
+PFNGLPOINTSIZEPROC glad_glPointSize = NULL;
+PFNGLPOLYGONMODEPROC glad_glPolygonMode = NULL;
+PFNGLPOLYGONOFFSETPROC glad_glPolygonOffset = NULL;
+PFNGLPRIMITIVERESTARTINDEXPROC glad_glPrimitiveRestartIndex = NULL;
+PFNGLPROVOKINGVERTEXPROC glad_glProvokingVertex = NULL;
+PFNGLQUERYCOUNTERPROC glad_glQueryCounter = NULL;
+PFNGLREADBUFFERPROC glad_glReadBuffer = NULL;
+PFNGLREADPIXELSPROC glad_glReadPixels = NULL;
+PFNGLRENDERBUFFERSTORAGEPROC glad_glRenderbufferStorage = NULL;
+PFNGLRENDERBUFFERSTORAGEMULTISAMPLEPROC glad_glRenderbufferStorageMultisample = NULL;
+PFNGLSAMPLECOVERAGEPROC glad_glSampleCoverage = NULL;
+PFNGLSAMPLEMASKIPROC glad_glSampleMaski = NULL;
+PFNGLSAMPLERPARAMETERIIVPROC glad_glSamplerParameterIiv = NULL;
+PFNGLSAMPLERPARAMETERIUIVPROC glad_glSamplerParameterIuiv = NULL;
+PFNGLSAMPLERPARAMETERFPROC glad_glSamplerParameterf = NULL;
+PFNGLSAMPLERPARAMETERFVPROC glad_glSamplerParameterfv = NULL;
+PFNGLSAMPLERPARAMETERIPROC glad_glSamplerParameteri = NULL;
+PFNGLSAMPLERPARAMETERIVPROC glad_glSamplerParameteriv = NULL;
+PFNGLSCISSORPROC glad_glScissor = NULL;
+PFNGLSECONDARYCOLORP3UIPROC glad_glSecondaryColorP3ui = NULL;
+PFNGLSECONDARYCOLORP3UIVPROC glad_glSecondaryColorP3uiv = NULL;
+PFNGLSHADERSOURCEPROC glad_glShaderSource = NULL;
+PFNGLSTENCILFUNCPROC glad_glStencilFunc = NULL;
+PFNGLSTENCILFUNCSEPARATEPROC glad_glStencilFuncSeparate = NULL;
+PFNGLSTENCILMASKPROC glad_glStencilMask = NULL;
+PFNGLSTENCILMASKSEPARATEPROC glad_glStencilMaskSeparate = NULL;
+PFNGLSTENCILOPPROC glad_glStencilOp = NULL;
+PFNGLSTENCILOPSEPARATEPROC glad_glStencilOpSeparate = NULL;
+PFNGLTEXBUFFERPROC glad_glTexBuffer = NULL;
+PFNGLTEXCOORDP1UIPROC glad_glTexCoordP1ui = NULL;
+PFNGLTEXCOORDP1UIVPROC glad_glTexCoordP1uiv = NULL;
+PFNGLTEXCOORDP2UIPROC glad_glTexCoordP2ui = NULL;
+PFNGLTEXCOORDP2UIVPROC glad_glTexCoordP2uiv = NULL;
+PFNGLTEXCOORDP3UIPROC glad_glTexCoordP3ui = NULL;
+PFNGLTEXCOORDP3UIVPROC glad_glTexCoordP3uiv = NULL;
+PFNGLTEXCOORDP4UIPROC glad_glTexCoordP4ui = NULL;
+PFNGLTEXCOORDP4UIVPROC glad_glTexCoordP4uiv = NULL;
+PFNGLTEXIMAGE1DPROC glad_glTexImage1D = NULL;
+PFNGLTEXIMAGE2DPROC glad_glTexImage2D = NULL;
+PFNGLTEXIMAGE2DMULTISAMPLEPROC glad_glTexImage2DMultisample = NULL;
+PFNGLTEXIMAGE3DPROC glad_glTexImage3D = NULL;
+PFNGLTEXIMAGE3DMULTISAMPLEPROC glad_glTexImage3DMultisample = NULL;
+PFNGLTEXPARAMETERIIVPROC glad_glTexParameterIiv = NULL;
+PFNGLTEXPARAMETERIUIVPROC glad_glTexParameterIuiv = NULL;
+PFNGLTEXPARAMETERFPROC glad_glTexParameterf = NULL;
+PFNGLTEXPARAMETERFVPROC glad_glTexParameterfv = NULL;
+PFNGLTEXPARAMETERIPROC glad_glTexParameteri = NULL;
+PFNGLTEXPARAMETERIVPROC glad_glTexParameteriv = NULL;
+PFNGLTEXSUBIMAGE1DPROC glad_glTexSubImage1D = NULL;
+PFNGLTEXSUBIMAGE2DPROC glad_glTexSubImage2D = NULL;
+PFNGLTEXSUBIMAGE3DPROC glad_glTexSubImage3D = NULL;
+PFNGLTRANSFORMFEEDBACKVARYINGSPROC glad_glTransformFeedbackVaryings = NULL;
+PFNGLUNIFORM1FPROC glad_glUniform1f = NULL;
+PFNGLUNIFORM1FVPROC glad_glUniform1fv = NULL;
+PFNGLUNIFORM1IPROC glad_glUniform1i = NULL;
+PFNGLUNIFORM1IVPROC glad_glUniform1iv = NULL;
+PFNGLUNIFORM1UIPROC glad_glUniform1ui = NULL;
+PFNGLUNIFORM1UIVPROC glad_glUniform1uiv = NULL;
+PFNGLUNIFORM2FPROC glad_glUniform2f = NULL;
+PFNGLUNIFORM2FVPROC glad_glUniform2fv = NULL;
+PFNGLUNIFORM2IPROC glad_glUniform2i = NULL;
+PFNGLUNIFORM2IVPROC glad_glUniform2iv = NULL;
+PFNGLUNIFORM2UIPROC glad_glUniform2ui = NULL;
+PFNGLUNIFORM2UIVPROC glad_glUniform2uiv = NULL;
+PFNGLUNIFORM3FPROC glad_glUniform3f = NULL;
+PFNGLUNIFORM3FVPROC glad_glUniform3fv = NULL;
+PFNGLUNIFORM3IPROC glad_glUniform3i = NULL;
+PFNGLUNIFORM3IVPROC glad_glUniform3iv = NULL;
+PFNGLUNIFORM3UIPROC glad_glUniform3ui = NULL;
+PFNGLUNIFORM3UIVPROC glad_glUniform3uiv = NULL;
+PFNGLUNIFORM4FPROC glad_glUniform4f = NULL;
+PFNGLUNIFORM4FVPROC glad_glUniform4fv = NULL;
+PFNGLUNIFORM4IPROC glad_glUniform4i = NULL;
+PFNGLUNIFORM4IVPROC glad_glUniform4iv = NULL;
+PFNGLUNIFORM4UIPROC glad_glUniform4ui = NULL;
+PFNGLUNIFORM4UIVPROC glad_glUniform4uiv = NULL;
+PFNGLUNIFORMBLOCKBINDINGPROC glad_glUniformBlockBinding = NULL;
+PFNGLUNIFORMMATRIX2FVPROC glad_glUniformMatrix2fv = NULL;
+PFNGLUNIFORMMATRIX2X3FVPROC glad_glUniformMatrix2x3fv = NULL;
+PFNGLUNIFORMMATRIX2X4FVPROC glad_glUniformMatrix2x4fv = NULL;
+PFNGLUNIFORMMATRIX3FVPROC glad_glUniformMatrix3fv = NULL;
+PFNGLUNIFORMMATRIX3X2FVPROC glad_glUniformMatrix3x2fv = NULL;
+PFNGLUNIFORMMATRIX3X4FVPROC glad_glUniformMatrix3x4fv = NULL;
+PFNGLUNIFORMMATRIX4FVPROC glad_glUniformMatrix4fv = NULL;
+PFNGLUNIFORMMATRIX4X2FVPROC glad_glUniformMatrix4x2fv = NULL;
+PFNGLUNIFORMMATRIX4X3FVPROC glad_glUniformMatrix4x3fv = NULL;
+PFNGLUNMAPBUFFERPROC glad_glUnmapBuffer = NULL;
+PFNGLUSEPROGRAMPROC glad_glUseProgram = NULL;
+PFNGLVALIDATEPROGRAMPROC glad_glValidateProgram = NULL;
+PFNGLVERTEXATTRIB1DPROC glad_glVertexAttrib1d = NULL;
+PFNGLVERTEXATTRIB1DVPROC glad_glVertexAttrib1dv = NULL;
+PFNGLVERTEXATTRIB1FPROC glad_glVertexAttrib1f = NULL;
+PFNGLVERTEXATTRIB1FVPROC glad_glVertexAttrib1fv = NULL;
+PFNGLVERTEXATTRIB1SPROC glad_glVertexAttrib1s = NULL;
+PFNGLVERTEXATTRIB1SVPROC glad_glVertexAttrib1sv = NULL;
+PFNGLVERTEXATTRIB2DPROC glad_glVertexAttrib2d = NULL;
+PFNGLVERTEXATTRIB2DVPROC glad_glVertexAttrib2dv = NULL;
+PFNGLVERTEXATTRIB2FPROC glad_glVertexAttrib2f = NULL;
+PFNGLVERTEXATTRIB2FVPROC glad_glVertexAttrib2fv = NULL;
+PFNGLVERTEXATTRIB2SPROC glad_glVertexAttrib2s = NULL;
+PFNGLVERTEXATTRIB2SVPROC glad_glVertexAttrib2sv = NULL;
+PFNGLVERTEXATTRIB3DPROC glad_glVertexAttrib3d = NULL;
+PFNGLVERTEXATTRIB3DVPROC glad_glVertexAttrib3dv = NULL;
+PFNGLVERTEXATTRIB3FPROC glad_glVertexAttrib3f = NULL;
+PFNGLVERTEXATTRIB3FVPROC glad_glVertexAttrib3fv = NULL;
+PFNGLVERTEXATTRIB3SPROC glad_glVertexAttrib3s = NULL;
+PFNGLVERTEXATTRIB3SVPROC glad_glVertexAttrib3sv = NULL;
+PFNGLVERTEXATTRIB4NBVPROC glad_glVertexAttrib4Nbv = NULL;
+PFNGLVERTEXATTRIB4NIVPROC glad_glVertexAttrib4Niv = NULL;
+PFNGLVERTEXATTRIB4NSVPROC glad_glVertexAttrib4Nsv = NULL;
+PFNGLVERTEXATTRIB4NUBPROC glad_glVertexAttrib4Nub = NULL;
+PFNGLVERTEXATTRIB4NUBVPROC glad_glVertexAttrib4Nubv = NULL;
+PFNGLVERTEXATTRIB4NUIVPROC glad_glVertexAttrib4Nuiv = NULL;
+PFNGLVERTEXATTRIB4NUSVPROC glad_glVertexAttrib4Nusv = NULL;
+PFNGLVERTEXATTRIB4BVPROC glad_glVertexAttrib4bv = NULL;
+PFNGLVERTEXATTRIB4DPROC glad_glVertexAttrib4d = NULL;
+PFNGLVERTEXATTRIB4DVPROC glad_glVertexAttrib4dv = NULL;
+PFNGLVERTEXATTRIB4FPROC glad_glVertexAttrib4f = NULL;
+PFNGLVERTEXATTRIB4FVPROC glad_glVertexAttrib4fv = NULL;
+PFNGLVERTEXATTRIB4IVPROC glad_glVertexAttrib4iv = NULL;
+PFNGLVERTEXATTRIB4SPROC glad_glVertexAttrib4s = NULL;
+PFNGLVERTEXATTRIB4SVPROC glad_glVertexAttrib4sv = NULL;
+PFNGLVERTEXATTRIB4UBVPROC glad_glVertexAttrib4ubv = NULL;
+PFNGLVERTEXATTRIB4UIVPROC glad_glVertexAttrib4uiv = NULL;
+PFNGLVERTEXATTRIB4USVPROC glad_glVertexAttrib4usv = NULL;
+PFNGLVERTEXATTRIBDIVISORPROC glad_glVertexAttribDivisor = NULL;
+PFNGLVERTEXATTRIBI1IPROC glad_glVertexAttribI1i = NULL;
+PFNGLVERTEXATTRIBI1IVPROC glad_glVertexAttribI1iv = NULL;
+PFNGLVERTEXATTRIBI1UIPROC glad_glVertexAttribI1ui = NULL;
+PFNGLVERTEXATTRIBI1UIVPROC glad_glVertexAttribI1uiv = NULL;
+PFNGLVERTEXATTRIBI2IPROC glad_glVertexAttribI2i = NULL;
+PFNGLVERTEXATTRIBI2IVPROC glad_glVertexAttribI2iv = NULL;
+PFNGLVERTEXATTRIBI2UIPROC glad_glVertexAttribI2ui = NULL;
+PFNGLVERTEXATTRIBI2UIVPROC glad_glVertexAttribI2uiv = NULL;
+PFNGLVERTEXATTRIBI3IPROC glad_glVertexAttribI3i = NULL;
+PFNGLVERTEXATTRIBI3IVPROC glad_glVertexAttribI3iv = NULL;
+PFNGLVERTEXATTRIBI3UIPROC glad_glVertexAttribI3ui = NULL;
+PFNGLVERTEXATTRIBI3UIVPROC glad_glVertexAttribI3uiv = NULL;
+PFNGLVERTEXATTRIBI4BVPROC glad_glVertexAttribI4bv = NULL;
+PFNGLVERTEXATTRIBI4IPROC glad_glVertexAttribI4i = NULL;
+PFNGLVERTEXATTRIBI4IVPROC glad_glVertexAttribI4iv = NULL;
+PFNGLVERTEXATTRIBI4SVPROC glad_glVertexAttribI4sv = NULL;
+PFNGLVERTEXATTRIBI4UBVPROC glad_glVertexAttribI4ubv = NULL;
+PFNGLVERTEXATTRIBI4UIPROC glad_glVertexAttribI4ui = NULL;
+PFNGLVERTEXATTRIBI4UIVPROC glad_glVertexAttribI4uiv = NULL;
+PFNGLVERTEXATTRIBI4USVPROC glad_glVertexAttribI4usv = NULL;
+PFNGLVERTEXATTRIBIPOINTERPROC glad_glVertexAttribIPointer = NULL;
+PFNGLVERTEXATTRIBP1UIPROC glad_glVertexAttribP1ui = NULL;
+PFNGLVERTEXATTRIBP1UIVPROC glad_glVertexAttribP1uiv = NULL;
+PFNGLVERTEXATTRIBP2UIPROC glad_glVertexAttribP2ui = NULL;
+PFNGLVERTEXATTRIBP2UIVPROC glad_glVertexAttribP2uiv = NULL;
+PFNGLVERTEXATTRIBP3UIPROC glad_glVertexAttribP3ui = NULL;
+PFNGLVERTEXATTRIBP3UIVPROC glad_glVertexAttribP3uiv = NULL;
+PFNGLVERTEXATTRIBP4UIPROC glad_glVertexAttribP4ui = NULL;
+PFNGLVERTEXATTRIBP4UIVPROC glad_glVertexAttribP4uiv = NULL;
+PFNGLVERTEXATTRIBPOINTERPROC glad_glVertexAttribPointer = NULL;
+PFNGLVERTEXP2UIPROC glad_glVertexP2ui = NULL;
+PFNGLVERTEXP2UIVPROC glad_glVertexP2uiv = NULL;
+PFNGLVERTEXP3UIPROC glad_glVertexP3ui = NULL;
+PFNGLVERTEXP3UIVPROC glad_glVertexP3uiv = NULL;
+PFNGLVERTEXP4UIPROC glad_glVertexP4ui = NULL;
+PFNGLVERTEXP4UIVPROC glad_glVertexP4uiv = NULL;
+PFNGLVIEWPORTPROC glad_glViewport = NULL;
+PFNGLWAITSYNCPROC glad_glWaitSync = NULL;
+static void load_GL_VERSION_1_0(GLADloadproc load) {
+ if(!GLAD_GL_VERSION_1_0) return;
+ glad_glCullFace = (PFNGLCULLFACEPROC)load("glCullFace");
+ glad_glFrontFace = (PFNGLFRONTFACEPROC)load("glFrontFace");
+ glad_glHint = (PFNGLHINTPROC)load("glHint");
+ glad_glLineWidth = (PFNGLLINEWIDTHPROC)load("glLineWidth");
+ glad_glPointSize = (PFNGLPOINTSIZEPROC)load("glPointSize");
+ glad_glPolygonMode = (PFNGLPOLYGONMODEPROC)load("glPolygonMode");
+ glad_glScissor = (PFNGLSCISSORPROC)load("glScissor");
+ glad_glTexParameterf = (PFNGLTEXPARAMETERFPROC)load("glTexParameterf");
+ glad_glTexParameterfv = (PFNGLTEXPARAMETERFVPROC)load("glTexParameterfv");
+ glad_glTexParameteri = (PFNGLTEXPARAMETERIPROC)load("glTexParameteri");
+ glad_glTexParameteriv = (PFNGLTEXPARAMETERIVPROC)load("glTexParameteriv");
+ glad_glTexImage1D = (PFNGLTEXIMAGE1DPROC)load("glTexImage1D");
+ glad_glTexImage2D = (PFNGLTEXIMAGE2DPROC)load("glTexImage2D");
+ glad_glDrawBuffer = (PFNGLDRAWBUFFERPROC)load("glDrawBuffer");
+ glad_glClear = (PFNGLCLEARPROC)load("glClear");
+ glad_glClearColor = (PFNGLCLEARCOLORPROC)load("glClearColor");
+ glad_glClearStencil = (PFNGLCLEARSTENCILPROC)load("glClearStencil");
+ glad_glClearDepth = (PFNGLCLEARDEPTHPROC)load("glClearDepth");
+ glad_glStencilMask = (PFNGLSTENCILMASKPROC)load("glStencilMask");
+ glad_glColorMask = (PFNGLCOLORMASKPROC)load("glColorMask");
+ glad_glDepthMask = (PFNGLDEPTHMASKPROC)load("glDepthMask");
+ glad_glDisable = (PFNGLDISABLEPROC)load("glDisable");
+ glad_glEnable = (PFNGLENABLEPROC)load("glEnable");
+ glad_glFinish = (PFNGLFINISHPROC)load("glFinish");
+ glad_glFlush = (PFNGLFLUSHPROC)load("glFlush");
+ glad_glBlendFunc = (PFNGLBLENDFUNCPROC)load("glBlendFunc");
+ glad_glLogicOp = (PFNGLLOGICOPPROC)load("glLogicOp");
+ glad_glStencilFunc = (PFNGLSTENCILFUNCPROC)load("glStencilFunc");
+ glad_glStencilOp = (PFNGLSTENCILOPPROC)load("glStencilOp");
+ glad_glDepthFunc = (PFNGLDEPTHFUNCPROC)load("glDepthFunc");
+ glad_glPixelStoref = (PFNGLPIXELSTOREFPROC)load("glPi