summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.clang-tidy1
-rw-r--r--.gitignore7
-rw-r--r--NEWS15
-rw-r--r--PACKAGING.md12
-rw-r--r--doc/Doxyfile.in (renamed from doc/c/Doxyfile.in)0
-rw-r--r--doc/api/meson.build (renamed from doc/c/api/meson.build)6
-rw-r--r--doc/c/index.rst14
-rw-r--r--doc/c/meson.build51
-rw-r--r--doc/c/reference.rst15
-rw-r--r--doc/conf.py.in12
-rw-r--r--doc/html/meson.build32
-rw-r--r--doc/index.rst (renamed from doc/summary.rst)11
-rw-r--r--doc/meson.build69
-rw-r--r--doc/overview.rst (renamed from doc/c/overview.rst)0
-rw-r--r--doc/singlehtml/meson.build33
-rw-r--r--doc/xml/meson.build (renamed from doc/c/xml/meson.build)17
-rw-r--r--include/suil/suil.h91
-rw-r--r--meson.build114
-rw-r--r--meson/library/meson.build31
-rw-r--r--meson/suppressions/meson.build95
-rw-r--r--meson/warnings/meson.build245
-rw-r--r--meson_options.txt9
-rwxr-xr-xscripts/dox_to_sphinx.py663
-rw-r--r--src/dylib.h6
-rw-r--r--src/gtk2_in_qt5.cpp156
-rw-r--r--src/instance.c34
-rw-r--r--src/qt5_in_gtk.cpp239
-rw-r--r--src/suil_internal.h7
-rw-r--r--src/win_in_gtk2.cpp6
-rw-r--r--src/x11_in_qt5.cpp16
-rw-r--r--subprojects/sphinxygen.wrap14
31 files changed, 409 insertions, 1612 deletions
diff --git a/.clang-tidy b/.clang-tidy
index ab9957e..92d3c36 100644
--- a/.clang-tidy
+++ b/.clang-tidy
@@ -27,6 +27,7 @@ Checks: >
-hicpp-signed-bitwise,
-llvm-header-guard,
-llvmlibc-*,
+ -modernize-use-nodiscard,
-modernize-use-trailing-return-type,
-performance-no-int-to-ptr,
-readability-function-cognitive-complexity,
diff --git a/.gitignore b/.gitignore
index 72b54d3..8ac4349 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,6 +1,9 @@
# Copyright 2017-2022 David Robillard <d@drobilla.net>
# SPDX-License-Identifier: 0BSD OR ISC
-build/**
-__pycache__
.meson-subproject-wrap-hash.txt
+__pycache__
+build/**
+subprojects/packagecache/
+subprojects/sphinxygen-1.0.4/
+subprojects/sphinxygen/
diff --git a/NEWS b/NEWS
index 2677ef7..6425f79 100644
--- a/NEWS
+++ b/NEWS
@@ -1,9 +1,20 @@
-suil (0.10.19) unstable; urgency=medium
+suil (0.10.21) unstable; urgency=medium
+ * Fix library current_version on MacOS
+
+ -- David Robillard <d@drobilla.net> Thu, 14 Mar 2024 16:54:56 +0000
+
+suil (0.10.20) stable; urgency=medium
+
+ * Allow SUIL_API to be defined by the user
* Fix dependencies in pkg-config file
+ * Only check for Gtk Quartz support on MacOS
* Override pkg-config dependency within meson
+ * Remove Gtk in Qt and Qt in Gtk wrappers
+ * Remove junk files from documentation install
+ * Replace duplicated dox_to_sphinx script with sphinxygen dependency
- -- David Robillard <d@drobilla.net> Fri, 07 Oct 2022 17:34:53 +0000
+ -- David Robillard <d@drobilla.net> Sun, 22 Oct 2023 20:11:10 +0000
suil (0.10.18) stable; urgency=medium
diff --git a/PACKAGING.md b/PACKAGING.md
index 4702379..6b0debb 100644
--- a/PACKAGING.md
+++ b/PACKAGING.md
@@ -9,7 +9,7 @@ versions. To facilitate this, the shared library name, include directory, and
pkg-config file are suffixed with the major version number of the library.
Dependencies check for the pkg-config package `suil-0` and will build against a
-compatible version 0, regardless any other installed versions.
+compatible version 0, regardless any other installed major versions.
Packages should follow the same conventions as above, that is, include the
major version (and only the major version) in the name of the package so that
@@ -23,12 +23,12 @@ achieve this, Suil dynamically loads modules for the toolkits in use. The main
Suil library does NOT depend on any toolkit libraries, and its package
shouldn't either (otherwise, for example, every LV2 host in the distribution
would depend directly on Gtk and Qt). Individual modules (like
-`libsuil_gtk2_in_qt5.so`) should be packaged separately and themselves depend
-on the involved toolkits. These packages should also be versioned as described
-above to support parallel installation.
+`libsuil_x11_in_gtk3.so`) should be packaged separately and themselves depend
+on the involved libraries or toolkits. These packages should also be versioned
+as described above to support parallel installation.
Please do not make the main Suil package strongly depend on any toolkit
-package, this defeats the purpose of Suil and will irritate users who want to
-avoid a particular toolkit dependency for whatever reason. "Weak" or
+package, this defeats the purpose of Suil and will cause problems for users who
+need to avoid a particular toolkit dependency for whatever reason. "Weak" or
"recommended" dependencies are fine, the important thing is that users are able
to avoid particular toolkits if they choose.
diff --git a/doc/c/Doxyfile.in b/doc/Doxyfile.in
index 0d787bc..0d787bc 100644
--- a/doc/c/Doxyfile.in
+++ b/doc/Doxyfile.in
diff --git a/doc/c/api/meson.build b/doc/api/meson.build
index 6463f7c..a5cecf8 100644
--- a/doc/c/api/meson.build
+++ b/doc/api/meson.build
@@ -1,9 +1,9 @@
# Copyright 2021-2022 David Robillard <d@drobilla.net>
# SPDX-License-Identifier: 0BSD OR ISC
-c_suil_rst = custom_target(
+api_suil_rst = custom_target(
'suil.rst',
- command: [dox_to_sphinx, '-f', '@INPUT0@', '@OUTDIR@'],
- input: [c_index_xml] + c_rst_files,
+ command: [sphinxygen, '-f', '@INPUT0@', '@OUTDIR@'],
+ input: doxygen_xml,
output: 'suil.rst',
)
diff --git a/doc/c/index.rst b/doc/c/index.rst
deleted file mode 100644
index 7f8eb92..0000000
--- a/doc/c/index.rst
+++ /dev/null
@@ -1,14 +0,0 @@
-..
- Copyright 2020-2022 David Robillard <d@drobilla.net>
- SPDX-License-Identifier: ISC
-
-####
-Suil
-####
-
-.. include:: summary.rst
-
-.. toctree::
-
- overview
- api/suil
diff --git a/doc/c/meson.build b/doc/c/meson.build
deleted file mode 100644
index 2786139..0000000
--- a/doc/c/meson.build
+++ /dev/null
@@ -1,51 +0,0 @@
-# Copyright 2021-2022 David Robillard <d@drobilla.net>
-# SPDX-License-Identifier: 0BSD OR ISC
-
-config = configuration_data()
-config.set('SUIL_VERSION', meson.project_version())
-
-conf_py = configure_file(
- configuration: config,
- input: files('../conf.py.in'),
- output: 'conf.py',
-)
-
-configure_file(
- copy: true,
- input: files('../summary.rst'),
- output: 'summary.rst',
-)
-
-c_rst_files = files(
- 'index.rst',
- 'overview.rst',
-)
-
-foreach f : c_rst_files
- configure_file(copy: true, input: f, output: '@PLAINNAME@')
-endforeach
-
-subdir('xml')
-subdir('api')
-
-docs = custom_target(
- 'singlehtml',
- build_by_default: true,
- command: [sphinx_build, '-M', 'singlehtml', '@OUTDIR@', '@OUTDIR@',
- '-E', '-q', '-t', 'singlehtml'],
- input: [c_rst_files, c_suil_rst, c_index_xml],
- install: true,
- install_dir: docdir / 'suil-0',
- output: 'singlehtml',
-)
-
-docs = custom_target(
- 'html',
- build_by_default: true,
- command: [sphinx_build, '-M', 'html', '@OUTDIR@', '@OUTDIR@',
- '-E', '-q', '-t', 'html'],
- input: [c_rst_files, c_suil_rst, c_index_xml],
- install: true,
- install_dir: docdir / 'suil-0',
- output: 'html',
-)
diff --git a/doc/c/reference.rst b/doc/c/reference.rst
deleted file mode 100644
index 1512f42..0000000
--- a/doc/c/reference.rst
+++ /dev/null
@@ -1,15 +0,0 @@
-..
- Copyright 2020-2022 David Robillard <d@drobilla.net>
- SPDX-License-Identifier: ISC
-
-#############
-API Reference
-#############
-
-This section contains the generated documentation for all symbols in the public
-API.
-
-.. toctree::
-
- api/suil
-
diff --git a/doc/conf.py.in b/doc/conf.py.in
index 3193610..7b6a2db 100644
--- a/doc/conf.py.in
+++ b/doc/conf.py.in
@@ -1,12 +1,14 @@
-# Copyright 2020-2021 David Robillard <d@drobilla.net>
+# Copyright 2020-2023 David Robillard <d@drobilla.net>
# SPDX-License-Identifier: ISC
# Project information
-project = "Suil"
-copyright = "2020, David Robillard"
+project = "@SUIL_TITLE@"
+copyright = "2023, David Robillard"
author = "David Robillard"
release = "@SUIL_VERSION@"
+version = "@SUIL_VERSION@"
+desc = "A library for loading LV2 plugin UIs"
# General configuration
@@ -42,7 +44,7 @@ if tags.has("singlehtml"):
html_theme_options = {
"body_max_width": "48em",
"body_min_width": "48em",
- "description": "A library for loading LV2 plugin UIs",
+ "description": desc,
"globaltoc_collapse": False,
"globaltoc_maxdepth": 3,
"logo_name": True,
@@ -57,7 +59,7 @@ else:
html_theme_options = {
"body_max_width": "60em",
"body_min_width": "40em",
- "description": "A library for loading LV2 plugin UIs",
+ "description": desc,
"globaltoc_collapse": True,
"globaltoc_maxdepth": 1,
"logo_name": True,
diff --git a/doc/html/meson.build b/doc/html/meson.build
new file mode 100644
index 0000000..7a41dc5
--- /dev/null
+++ b/doc/html/meson.build
@@ -0,0 +1,32 @@
+# Copyright 2021-2023 David Robillard <d@drobilla.net>
+# SPDX-License-Identifier: 0BSD OR ISC
+
+html_dir = docdir / versioned_name / 'html'
+
+# TODO: Add install_tag: 'doc' after requiring meson 0.60.0
+
+custom_target(
+ 'html',
+ build_by_default: true,
+ command: sphinx_build_command + [
+ '-b', 'html',
+ '-t', 'html',
+ sphinx_in_dir,
+ '@OUTDIR@',
+ ],
+ input: [api_suil_rst, conf_py, sphinx_input],
+ install: true,
+ install_dir: html_dir,
+ output: [
+ 'index.html',
+
+ '_static',
+ 'api',
+ 'genindex.html',
+ 'overview.html',
+ ],
+)
+
+if not meson.is_subproject()
+ summary('HTML', get_option('prefix') / html_dir, section: 'Directories')
+endif
diff --git a/doc/summary.rst b/doc/index.rst
index 57c101e..7f81dbd 100644
--- a/doc/summary.rst
+++ b/doc/index.rst
@@ -1,7 +1,11 @@
..
- Copyright 2020-2022 David Robillard <d@drobilla.net>
+ Copyright 2020-2023 David Robillard <d@drobilla.net>
SPDX-License-Identifier: ISC
+####
+Suil
+####
+
Suil is a library for loading and wrapping LV2 plugin UIs.
With Suil,
@@ -12,3 +16,8 @@ a Gtk host can embed a Qt UI without linking against Qt at compile time.
Suil also handles the embedding of native platform UIs (which are recommended) in common toolkits,
for example embedding an X11 UI in a Gtk host.
+
+.. toctree::
+
+ overview
+ api/suil
diff --git a/doc/meson.build b/doc/meson.build
index 7e447d5..4db8c74 100644
--- a/doc/meson.build
+++ b/doc/meson.build
@@ -1,18 +1,77 @@
-# Copyright 2021-2022 David Robillard <d@drobilla.net>
+# Copyright 2021-2023 David Robillard <d@drobilla.net>
# SPDX-License-Identifier: 0BSD OR ISC
docdir = get_option('datadir') / 'doc'
+#############
+# Reference #
+#############
+
+# Find required programs
doxygen = find_program('doxygen', required: get_option('docs'))
-dox_to_sphinx = files('../scripts/dox_to_sphinx.py')
sphinx_build = find_program('sphinx-build', required: get_option('docs'))
-build_docs = doxygen.found() and sphinx_build.found()
+# Find sphinxygen or fall back to subproject
+sphinxygen = disabler()
+if doxygen.found() and sphinx_build.found()
+ sphinxygen = find_program('sphinxygen', required: false)
+ if not sphinxygen.found()
+ subproject('sphinxygen')
+ sphinxygen = find_program('sphinxygen', required: get_option('docs'))
+ endif
+endif
+# Build documentation if all required tools are found
+build_docs = doxygen.found() and sphinxygen.found() and sphinx_build.found()
if build_docs
- subdir('c')
+ # Warn if the "official" theme isn't present
+ pymod = import('python')
+ doc_modules = ['sphinx_lv2_theme']
+ py = pymod.find_installation('python3', modules: doc_modules, required: false)
+ if not py.found()
+ warning('Missing sphinx_lv2_theme module, falling back to alabaster')
+ endif
+
+ # Configure conf.py for Sphinx
+ conf_config = configuration_data()
+ conf_config.set('SUIL_SRCDIR', suil_src_root)
+ conf_config.set('SUIL_TITLE', get_option('title'))
+ conf_config.set('SUIL_VERSION', meson.project_version())
+ conf_py = configure_file(
+ configuration: conf_config,
+ input: files('conf.py.in'),
+ output: 'conf.py',
+ )
+
+ # Copy hand-written documentation files
+ rst_sources = files('index.rst', 'overview.rst')
+ sphinx_input = []
+ foreach f : rst_sources
+ sphinx_input += [
+ configure_file(copy: true, input: f, output: '@PLAINNAME@'),
+ ]
+ endforeach
+
+ # Generate reference documentation input with Doxygen and Sphinxygen
+ subdir('xml')
+ subdir('api')
+
+ # Build strict Sphinx flags, with termination on warnings if werror=true
+ sphinx_in_dir = meson.current_build_dir()
+ sphinx_flags = ['-E', '-a', '-q']
+ if get_option('werror')
+ sphinx_flags += ['-W']
+ endif
+
+ # Run Sphinx to generate final documentation for each format
+ sphinx_build_command = [sphinx_build] + sphinx_flags
+ foreach format : ['html', 'singlehtml']
+ if not get_option(format).disabled()
+ subdir(format)
+ endif
+ endforeach
endif
if not meson.is_subproject()
- summary('Documentation', build_docs, bool_yn: true)
+ summary('Documentation', build_docs, bool_yn: true, section: 'Components')
endif
diff --git a/doc/c/overview.rst b/doc/overview.rst
index 71937b2..71937b2 100644
--- a/doc/c/overview.rst
+++ b/doc/overview.rst
diff --git a/doc/singlehtml/meson.build b/doc/singlehtml/meson.build
new file mode 100644
index 0000000..0398b32
--- /dev/null
+++ b/doc/singlehtml/meson.build
@@ -0,0 +1,33 @@
+# Copyright 2021-2023 David Robillard <d@drobilla.net>
+# SPDX-License-Identifier: 0BSD OR ISC
+
+singlehtml_dir = docdir / versioned_name / 'singlehtml'
+
+# TODO: Add install_tag: 'doc' after requiring meson 0.60.0
+
+custom_target(
+ 'singlehtml',
+ build_by_default: true,
+ command: sphinx_build_command + [
+ '-b', 'singlehtml',
+ '-t', 'singlehtml',
+ sphinx_in_dir,
+ '@OUTDIR@',
+ ],
+ input: [api_suil_rst, conf_py, sphinx_input],
+ install: true,
+ install_dir: singlehtml_dir,
+ output: [
+ 'index.html',
+
+ '_static',
+ ],
+)
+
+if not meson.is_subproject()
+ summary(
+ 'Single HTML',
+ get_option('prefix') / singlehtml_dir,
+ section: 'Directories',
+ )
+endif
diff --git a/doc/c/xml/meson.build b/doc/xml/meson.build
index bbbf130..e49aeaf 100644
--- a/doc/c/xml/meson.build
+++ b/doc/xml/meson.build
@@ -7,15 +7,24 @@ config = configuration_data()
config.set('SUIL_SRCDIR', suil_src_root)
config.set('DOX_OUTPUT', meson.current_build_dir() / '..')
-c_doxyfile = configure_file(
+doxyfile = configure_file(
configuration: config,
input: files('../Doxyfile.in'),
output: 'Doxyfile',
)
-c_index_xml = custom_target(
+doxygen_xml = custom_target(
'index.xml',
command: [doxygen, '@INPUT0@'],
- input: [c_doxyfile] + c_headers,
- output: 'index.xml',
+ input: [doxyfile] + c_headers,
+ output: [
+ 'index.xml',
+
+ 'group__suil.xml',
+ 'group__suil__callbacks.xml',
+ 'group__suil__host.xml',
+ 'group__suil__instance.xml',
+ 'group__suil__library.xml',
+ 'suil_8h.xml',
+ ],
)
diff --git a/include/suil/suil.h b/include/suil/suil.h
index c821641..ba55a91 100644
--- a/include/suil/suil.h
+++ b/include/suil/suil.h
@@ -11,6 +11,7 @@
#include <stdbool.h>
#include <stdint.h>
+// SUIL_LIB_IMPORT and SUIL_LIB_EXPORT mark the entry points of shared libraries
#ifdef _WIN32
# define SUIL_LIB_IMPORT __declspec(dllimport)
# define SUIL_LIB_EXPORT __declspec(dllexport)
@@ -19,14 +20,15 @@
# define SUIL_LIB_EXPORT __attribute__((visibility("default")))
#endif
-#ifndef SUIL_STATIC
-# ifdef SUIL_INTERNAL
+// SUIL_API exposes symbols in the public API
+#ifndef SUIL_API
+# ifdef SUIL_STATIC
+# define SUIL_API
+# elif defined(SUIL_INTERNAL)
# define SUIL_API SUIL_LIB_EXPORT
# else
# define SUIL_API SUIL_LIB_IMPORT
# endif
-#else
-# define SUIL_API
#endif
#ifdef __cplusplus
@@ -34,27 +36,14 @@ extern "C" {
#endif
/**
- @defgroup suil Suil
+ @defgroup suil Suil C API
@{
*/
/**
- UI host descriptor.
-
- This contains the various functions that a plugin UI may use to communicate
- with the plugin. It is passed to suil_instance_new() to provide these
- functions to the UI.
+ @defgroup suil_callbacks Callbacks
+ @{
*/
-typedef struct SuilHostImpl SuilHost;
-
-/// An instance of an LV2 plugin UI
-typedef struct SuilInstanceImpl SuilInstance;
-
-/// Opaque pointer to a UI handle
-typedef void* SuilHandle;
-
-/// Opaque pointer to a UI widget
-typedef void* SuilWidget;
/**
UI controller.
@@ -99,6 +88,12 @@ typedef void (*SuilTouchFunc)( //
uint32_t port_index,
bool grabbed);
+/**
+ @}
+ @defgroup suil_library Library
+ @{
+*/
+
/// Initialization argument
typedef enum { SUIL_ARG_NONE } SuilArg;
@@ -115,6 +110,37 @@ void
suil_init(int* argc, char*** argv, SuilArg key, ...);
/**
+ Check if suil can wrap a UI type.
+
+ @param host_type_uri The URI of the desired widget type of the host,
+ corresponding to the `type_uri` parameter of suil_instance_new().
+
+ @param ui_type_uri The URI of the UI widget type.
+
+ @return 0 if wrapping is unsupported, otherwise the quality of the wrapping
+ where 1 is the highest quality (direct native embedding with no wrapping)
+ and increasing values are of a progressively lower quality and/or stability.
+*/
+SUIL_API
+unsigned
+suil_ui_supported(const char* host_type_uri, const char* ui_type_uri);
+
+/**
+ @}
+ @defgroup suil_host Host
+ @{
+*/
+
+/**
+ UI host descriptor.
+
+ This contains the various functions that a plugin UI may use to communicate
+ with the plugin. It is passed to suil_instance_new() to provide these
+ functions to the UI.
+*/
+typedef struct SuilHostImpl SuilHost;
+
+/**
Create a new UI host descriptor.
@param write_func Function to send a value to a plugin port.
@@ -146,20 +172,22 @@ void
suil_host_free(SuilHost* host);
/**
- Check if suil can wrap a UI type.
+ @}
+*/
- @param host_type_uri The URI of the desired widget type of the host,
- corresponding to the `type_uri` parameter of suil_instance_new().
+/**
+ @defgroup suil_instance Instance
+ @{
+*/
- @param ui_type_uri The URI of the UI widget type.
+/// An instance of an LV2 plugin UI
+typedef struct SuilInstanceImpl SuilInstance;
- @return 0 if wrapping is unsupported, otherwise the quality of the wrapping
- where 1 is the highest quality (direct native embedding with no wrapping)
- and increasing values are of a progressively lower quality and/or stability.
-*/
-SUIL_API
-unsigned
-suil_ui_supported(const char* host_type_uri, const char* ui_type_uri);
+/// Opaque pointer to a UI handle
+typedef void* SuilHandle;
+
+/// Opaque pointer to a UI widget
+typedef void* SuilWidget;
/**
Instantiate a UI for an LV2 plugin.
@@ -264,6 +292,7 @@ suil_instance_extension_data(SuilInstance* instance, const char* uri);
/**
@}
+ @}
*/
#ifdef __cplusplus
diff --git a/meson.build b/meson.build
index 433cbb4..3089ce8 100644
--- a/meson.build
+++ b/meson.build
@@ -1,16 +1,19 @@
# Copyright 2021-2022 David Robillard <d@drobilla.net>
# SPDX-License-Identifier: 0BSD OR ISC
-project('suil', ['c', 'cpp'],
- version: '0.10.19',
- license: 'ISC',
- meson_version: '>= 0.56.0',
- default_options: [
- 'b_ndebug=if-release',
- 'buildtype=release',
- 'c_std=c99',
- 'cpp_std=c++14',
- ])
+project(
+ 'suil',
+ ['c', 'cpp'],
+ default_options: [
+ 'b_ndebug=if-release',
+ 'buildtype=release',
+ 'c_std=c99',
+ 'cpp_std=c++14',
+ ],
+ license: 'ISC',
+ meson_version: '>= 0.56.0',
+ version: '0.10.21',
+)
suil_src_root = meson.current_source_dir()
major_version = meson.project_version().split('.')[0]
@@ -34,9 +37,6 @@ if host_machine.system() == 'darwin'
endif
# Set global warning flags
-if get_option('strict') and not meson.is_subproject()
- subdir('meson/warnings')
-endif
subdir('meson/suppressions')
##########################
@@ -49,6 +49,16 @@ platform_defines = ['-DSUIL_MODULE_DIR="@0@"'.format(suil_abs_module_dir)]
nodelete_c_link_args = cc.get_supported_link_arguments(['-Wl,-z,nodelete'])
nodelete_cpp_link_args = cpp.get_supported_link_arguments(['-Wl,-z,nodelete'])
+# Use versioned name everywhere to support parallel major version installations
+if host_machine.system() == 'windows'
+ if get_option('default_library') == 'both'
+ error('default_library=both is not supported on Windows')
+ endif
+ soversion = ''
+else
+ soversion = meson.project_version().split('.')[0]
+endif
+
################
# Dependencies #
################
@@ -76,11 +86,15 @@ gtk2_x11_dep = dependency(
required: get_option('gtk2').enabled() and get_option('x11').enabled(),
)
-gtk2_quartz_dep = dependency(
- 'gtk+-quartz-2.0',
- include_type: 'system',
- required: get_option('gtk2').enabled() and get_option('cocoa').enabled(),
-)
+if host_machine.system() == 'darwin'
+ gtk2_quartz_dep = dependency(
+ 'gtk+-quartz-2.0',
+ include_type: 'system',
+ required: get_option('gtk2').enabled() and get_option('cocoa').enabled(),
+ )
+else
+ gtk2_quartz_dep = disabler()
+endif
gtk3_dep = dependency(
'gtk+-3.0',
@@ -132,20 +146,21 @@ sources = files(
# Set appropriate arguments for building against the library type
extra_c_args = []
-subdir('meson/library')
if get_option('default_library') == 'static'
extra_c_args = ['-DSUIL_STATIC']
endif
# Build shared and/or static library
libsuil = library(
- meson.project_name() + library_suffix,
+ versioned_name,
sources,
c_args: c_suppressions + extra_c_args + platform_defines + ['-DSUIL_INTERNAL'],
+ darwin_versions: [major_version + '.0.0', meson.project_version()],
dependencies: [dl_dep, lv2_dep],
gnu_symbol_visibility: 'hidden',
include_directories: include_dirs,
install: true,
+ soversion: soversion,
version: meson.project_version(),
)
@@ -206,31 +221,6 @@ endif
gtk_c_args = cc.get_supported_arguments(gtk_args)
gtk_cpp_args = cpp.get_supported_arguments(gtk_args)
-if gtk2_dep.found() and qt5_dep.found()
- shared_module(
- 'suil_gtk2_in_qt5',
- files('src/gtk2_in_qt5.cpp'),
- cpp_args: cpp_suppressions + gtk_cpp_args + platform_defines,
- dependencies: [gtk2_dep, lv2_dep, qt5_dep],
- gnu_symbol_visibility: 'hidden',
- include_directories: include_dirs,
- install: true,
- install_dir: suil_module_dir,
- )
-
- shared_module(
- 'suil_qt5_in_gtk2',
- files('src/qt5_in_gtk.cpp'),
- cpp_args: cpp_suppressions + gtk_cpp_args + platform_defines,
- dependencies: [gtk2_dep, lv2_dep, qt5_dep],
- gnu_symbol_visibility: 'hidden',
- include_directories: include_dirs,
- install: true,
- install_dir: suil_module_dir,
- link_args: nodelete_cpp_link_args,
- )
-endif
-
if gtk2_dep.found() and gtk2_x11_dep.found() and x11_dep.found()
shared_module(
'suil_x11_in_gtk2',
@@ -259,25 +249,11 @@ if gtk3_dep.found() and gtk3_x11_dep.found() and x11_dep.found()
)
endif
-if gtk3_dep.found() and gtk3_x11_dep.found() and qt5_dep.found()
- shared_module(
- 'suil_qt5_in_gtk3',
- files('src/qt5_in_gtk.cpp'),
- cpp_args: cpp_suppressions + gtk_cpp_args + platform_defines,
- dependencies: [gtk3_dep, lv2_dep, qt5_dep],
- gnu_symbol_visibility: 'hidden',
- include_directories: include_dirs,
- install: true,
- install_dir: suil_module_dir,
- link_args: nodelete_cpp_link_args,
- )
-endif
-
if gtk2_dep.found() and gtk2_quartz_dep.found()
shared_module(
'suil_cocoa_in_gtk2',
files('src/cocoa_in_gtk2.mm'),
- dependencies: [gtk2_dep, lv2_dep, qt5_dep],
+ dependencies: [gtk2_dep, gtk2_quartz_dep, lv2_dep, qt5_dep],
gnu_symbol_visibility: 'hidden',
include_directories: include_dirs,
install: true,
@@ -336,7 +312,7 @@ endif
# Tests #
#########
-if get_option('strict')
+if get_option('warning_level') == 'everything'
# Check release metadata
if not meson.is_subproject()
autoship = find_program('autoship', required: false)
@@ -348,9 +324,7 @@ if get_option('strict')
# Check licensing metadata
reuse = find_program('reuse', required: false)
if reuse.found()
- test('REUSE', reuse,
- args: ['--root', suil_src_root, 'lint'],
- suite: 'data')
+ test('REUSE', reuse, args: ['--root', suil_src_root, 'lint'], suite: 'data')
endif
endif
@@ -362,8 +336,14 @@ if not get_option('docs').disabled()
subdir('doc')
endif
+# Display top-level summary (augmented in subdirectories)
if not meson.is_subproject()
- summary('Install prefix', get_option('prefix'))
- summary('Headers', get_option('prefix') / get_option('includedir'))
- summary('Libraries', get_option('prefix') / get_option('libdir'))
+ summary(
+ {
+ 'Install prefix': get_option('prefix'),
+ 'Headers': get_option('prefix') / get_option('includedir'),
+ 'Libraries': get_option('prefix') / get_option('libdir'),
+ },
+ section: 'Directories',
+ )
endif
diff --git a/meson/library/meson.build b/meson/library/meson.build
deleted file mode 100644
index 756a222..0000000
--- a/meson/library/meson.build
+++ /dev/null
@@ -1,31 +0,0 @@
-# Copyright 2020-2022 David Robillard <d@drobilla.net>
-# SPDX-License-Identifier: 0BSD OR ISC
-
-# General definitions for building libraries.
-#
-# These are essentially workarounds for Meson/Windows/MSVC. Unfortunately,
-# Meson's default_library option doesn't support shared and static builds very
-# well. In particular, it's often necessary to define different symbols for
-# static and shared builds of libraries so that symbols can be exported. To
-# work around this, default_library=both isn't supported on Windows. On other
-# platforms with GCC-like compilers, we can support both because symbols can
-# safely be exported in the same way (giving them default visibility) in both
-# static and shared builds.
-
-default_library = get_option('default_library')
-host_system = host_machine.system()
-
-# Abort on Windows with default_library=both
-if host_system == 'windows' and default_library == 'both'
- error('default_library=both is not supported on Windows')
-endif
-
-# Set library_suffix to the suffix for libraries
-if host_system == 'windows' and default_library == 'shared'
- # Meson appends a version to the name only for DLLs, which leads to
- # inconsistent library names, like `mylib-1-1`. So, provide no suffix to
- # ultimately get the same name as on other platforms, like `mylib-1`.
- library_suffix = ''
-else
- library_suffix = '-@0@'.format(meson.project_version().split('.')[0])
-endif
diff --git a/meson/suppressions/meson.build b/meson/suppressions/meson.build
index 0ee8230..87af4d6 100644
--- a/meson/suppressions/meson.build
+++ b/meson/suppressions/meson.build
@@ -1,11 +1,9 @@
-# Copyright 2020-2022 David Robillard <d@drobilla.net>
+# Copyright 2020-2023 David Robillard <d@drobilla.net>
# SPDX-License-Identifier: 0BSD OR ISC
-# Project-specific warning suppressions.
-#
-# This should be used in conjunction with the generic "warnings" sibling that
-# enables all reasonable warnings for the compiler. It lives here just to keep
-# the top-level meson.build more readable.
+# Project-specific warning suppressions
+
+warning_level = get_option('warning_level')
#####
# C #
@@ -14,36 +12,65 @@
if is_variable('cc')
c_suppressions = []
- if get_option('strict')
- if cc.get_id() == 'clang'
+ if cc.get_id() in ['clang', 'emscripten']
+ if warning_level == 'everything'
c_suppressions += [
'-Wno-atomic-implicit-seq-cst',
+ '-Wno-cast-function-type-strict',
'-Wno-cast-qual',
'-Wno-declaration-after-statement',
'-Wno-disabled-macro-expansion',
'-Wno-padded',
'-Wno-reserved-id-macro',
+ '-Wno-unsafe-buffer-usage',
'-Wno-variadic-macros',
]
- elif cc.get_id() == 'gcc'
+ if not meson.is_cross_build()
+ c_suppressions += [
+ '-Wno-poison-system-directories',
+ ]
+ endif
+ endif
+
+ if host_machine.system() == 'windows'
+ c_suppressions += [
+ '-Wno-deprecated-declarations',
+ '-Wno-nonportable-system-include-path',
+ ]
+ endif
+
+ elif cc.get_id() == 'gcc'
+ if warning_level == 'everything'
c_suppressions += [
'-Wno-padded',
'-Wno-suggest-attribute=const',
'-Wno-suggest-attribute=pure',
]
+ endif
- elif cc.get_id() == 'msvc'
+ elif cc.get_id() == 'msvc'
+ c_suppressions += [
+ '/experimental:external',
+ '/external:W0',
+ '/external:anglebrackets',
+ ]
+
+ if warning_level == 'everything'
c_suppressions += [
'/wd4191', # unsafe function conversion
'/wd4514', # unreferenced inline function has been removed
'/wd4710', # function not inlined
'/wd4820', # padding added after construct
- '/wd4996', # function or variable may be unsafe
'/wd5045', # will insert Spectre mitigation for memory load
]
endif
+ if warning_level in ['everything', '3', '2']
+ c_suppressions += [
+ '/wd4996', # function or variable may be unsafe
+ ]
+ endif
endif
c_suppressions = cc.get_supported_arguments(c_suppressions)
@@ -56,20 +83,25 @@ endif
if is_variable('cpp')
cpp_suppressions = []
- if get_option('strict')
- if cpp.get_id() == 'clang'
+ if cpp.get_id() in ['clang', 'emscripten']
+ if warning_level == 'everything'
cpp_suppressions += [
'-Wno-atomic-implicit-seq-cst',
+ '-Wno-c++98-compat',
+ '-Wno-c++98-compat-pedantic',
+ '-Wno-cast-function-type-strict',
'-Wno-cast-qual',
'-Wno-disabled-macro-expansion',
'-Wno-old-style-cast',
'-Wno-padded',
'-Wno-reserved-id-macro',
+ '-Wno-unsafe-buffer-usage',
'-Wno-variadic-macros',
'-Wno-zero-as-null-pointer-constant',
]
-
- elif cpp.get_id() == 'gcc'
+ endif
+ elif cpp.get_id() == 'gcc'
+ if warning_level == 'everything'
cpp_suppressions += [
'-Wno-arith-conversion',
'-Wno-cast-qual',
@@ -92,14 +124,33 @@ endif
if is_variable('objcpp')
objcpp_suppressions = []
- if objcpp.get_id() == 'clang'
- c_suppressions += [
- '-Wno-deprecated-declarations',
- ]
+ if objcpp.get_id() in ['clang', 'emscripten']
+ if warning_level == 'everything'
+ objcpp_suppressions += [
+ '-Wno-c++98-compat-pedantic',
+ '-Wno-deprecated-declarations',
+ '-Wno-old-style-cast',
+ '-Wno-padded',
+ '-Wno-reserved-id-macro',
+ '-Wno-weak-vtables',
+ '-Wno-zero-as-null-pointer-constant',
+ ]
+
+ if not meson.is_cross_build()
+ objcpp_suppressions += [
+ '-Wno-poison-system-directories',
+ ]
+ endif
+ endif
+
elif objcpp.get_id() == 'gcc'
- objcpp_suppressions = gcc_common_warnings + [
- '-Wno-direct-ivar-access',
- ]
+ if warning_level == 'everything'
+ objcpp_suppressions = (
+ gcc_common_warnings + [
+ '-Wno-direct-ivar-access',
+ ]
+ )
+ endif
endif
objcpp_suppressions = objcpp.get_supported_arguments(objcpp_suppressions)
diff --git a/meson/warnings/meson.build b/meson/warnings/meson.build
deleted file mode 100644
index 858dc7b..0000000
--- a/meson/warnings/meson.build
+++ /dev/null
@@ -1,245 +0,0 @@
-# Copyright 2020-2022 David Robillard <d@drobilla.net>
-# SPDX-License-Identifier: 0BSD OR ISC
-
-# General code to enable approximately all warnings in GCC 12, clang, and MSVC.
-#
-# This is trivial for clang and MSVC, but GCC doesn't have an "everything"
-# option, so we need to enable everything we want explicitly. Wall is assumed,
-# but Wextra is not, for stability.
-#
-# These are collected from common.opt and c.opt in the GCC source, and manually
-# curated with the help of the GCC documentation. Warnings that are
-# application-specific, historical, or about compatibility between specific
-# language revisions are omitted. The intent here is to have roughly the same
-# meaning as clang's Weverything: extremely strict, but general. Specifically
-# omitted are:
-#
-# General:
-#
-# Wabi=
-# Waggregate-return
-# Walloc-size-larger-than=BYTES
-# Walloca-larger-than=BYTES
-# Wframe-larger-than=BYTES
-# Wlarger-than=BYTES
-# Wstack-usage=BYTES
-# Wsystem-headers
-# Wtraditional
-# Wtraditional-conversion
-# Wtrampolines
-# Wvla-larger-than=BYTES
-#
-# Build specific:
-#
-# Wpoison-system-directories
-#
-# C Specific:
-#
-# Wc11-c2x-compat
-# Wc90-c99-compat
-# Wc99-c11-compat
-# Wdeclaration-after-statement
-# Wtraditional
-# Wtraditional-conversion
-#
-# C++ Specific:
-#
-# Wc++0x-compat
-# Wc++1z-compat
-# Wc++2a-compat
-# Wctad-maybe-unsupported
-# Wnamespaces
-# Wtemplates
-
-gcc_common_warnings = [
- '-Walloc-zero',
- '-Walloca',
- '-Wanalyzer-too-complex',
- '-Warith-conversion',
- '-Warray-bounds=2',
- '-Wattribute-alias=2',
- '-Wbidi-chars=ucn',
- '-Wcast-align=strict',
- '-Wcast-function-type',
- '-Wcast-qual',
- '-Wclobbered',
- '-Wconversion',
- '-Wdate-time',
- '-Wdisabled-optimization',
- '-Wdouble-promotion',
- '-Wduplicated-branches',
- '-Wduplicated-cond',
- '-Wempty-body',
- '-Wendif-labels',
- '-Wfloat-equal',
- '-Wformat-overflow=2',
- '-Wformat-signedness',
- '-Wformat-truncation=2',
- '-Wformat=2',
- '-Wignored-qualifiers',
- '-Wimplicit-fallthrough=3',
- '-Winit-self',
- '-Winline',
- '-Winvalid-pch',
- '-Wlogical-op',
- '-Wmissing-declarations',
- '-Wmissing-field-initializers',
- '-Wmissing-include-dirs',
- '-Wmultichar',
- '-Wnormalized=nfc',
- '-Wnull-dereference',
- '-Wopenacc-parallelism',
- '-Woverlength-strings',
- '-Wpacked',
- '-Wpacked-bitfield-compat',
- '-Wpadded',
- '-Wpointer-arith',
- '-Wredundant-decls',
- '-Wshadow',
- '-Wshift-negative-value',
- '-Wshift-overflow=2',
- '-Wstack-protector',
- '-Wstrict-aliasing=3',
- '-Wstrict-overflow=5',
- '-Wstring-compare',
- '-Wstringop-overflow=3',
- '-Wsuggest-attribute=cold',
- '-Wsuggest-attribute=const',
- '-Wsuggest-attribute=format',
- '-Wsuggest-attribute=malloc',
- '-Wsuggest-attribute=noreturn',
- '-Wsuggest-attribute=pure',
- '-Wswitch-default',
- '-Wswitch-enum',
- '-Wtrampolines',
- '-Wtrivial-auto-var-init',
- '-Wtype-limits',
- '-Wundef',
- '-Wuninitialized',
- '-Wunsafe-loop-optimizations',
- '-Wunused',
- '-Wunused-const-variable=2',
- '-Wunused-macros',
- '-Wvector-operation-performance',
- '-Wvla',
- '-Wwrite-strings',
-]
-
-#####
-# C #
-#####
-
-if is_variable('cc')
- all_c_warnings = []
-
- if cc.get_id() == 'clang'
- all_c_warnings += ['-Weverything']
-
- elif cc.get_id() == 'gcc'
- all_c_warnings += gcc_common_warnings + [
- '-Wabsolute-value',
- '-Wbad-function-cast',
- '-Wc++-compat',
- '-Wenum-conversion',
- '-Wjump-misses-init',
- '-Wmissing-parameter-type',
- '-Wmissing-prototypes',
- '-Wnested-externs',
- '-Wold-style-declaration',
- '-Wold-style-definition',
- '-Woverride-init',
- '-Wsign-compare',
- '-Wstrict-prototypes',
- '-Wunsuffixed-float-constants',
- ]
-
- elif cc.get_id() == 'msvc'
- all_c_warnings += ['/Wall']
- endif
-
- all_c_warnings = cc.get_supported_arguments(all_c_warnings)
- add_global_arguments(all_c_warnings, language: ['c'])
-endif
-
-#######
-# C++ #
-#######
-
-if is_variable('cpp')
- all_cpp_warnings = []
-
- if cpp.get_id() == 'clang'
- all_cpp_warnings += [
- '-Weverything',
- '-Wno-c++98-compat',
- '-Wno-c++98-compat-pedantic'
- ]
-
- elif cpp.get_id() == 'gcc'
- all_cpp_warnings += gcc_common_warnings + [
- '-Wabi-tag',
- '-Waligned-new=all',
- '-Wcatch-value=3',
- '-Wcomma-subscript',
- '-Wconditionally-supported',
- '-Wctor-dtor-privacy',
- '-Wdelete-non-virtual-dtor',
- '-Wdeprecated',
- '-Wdeprecated-copy',
- '-Wdeprecated-copy-dtor',
- '-Wdeprecated-enum-enum-conversion',
- '-Wdeprecated-enum-float-conversion',
- '-Weffc++',
- '-Wexpansion-to-defined',
- '-Wextra-semi',
- '-Wimport',
- '-Winvalid-imported-macros',
- '-Wmismatched-tags',
- '-Wmultiple-inheritance',
- '-Wnoexcept',
- '-Wnoexcept-type',
- '-Wnon-virtual-dtor',
- '-Wold-style-cast',
- '-Woverloaded-virtual',
- '-Wplacement-new=2',
- '-Wredundant-move',
- '-Wredundant-tags',
- '-Wregister',
- '-Wsign-compare',
- '-Wsign-promo',
- '-Wsized-deallocation',
- '-Wstrict-null-sentinel',
- '-Wsuggest-final-methods',
- '-Wsuggest-final-types',
- '-Wsuggest-override',
- '-Wuseless-cast',
- '-Wvirtual-inheritance',
- '-Wvolatile',
- '-Wzero-as-null-pointer-constant',
- ]
-
- elif cpp.get_id() == 'msvc'
- all_cpp_warnings += ['/Wall']
- endif
-
- all_cpp_warnings = cpp.get_supported_arguments(all_cpp_warnings)
- add_global_arguments(all_cpp_warnings, language: ['cpp'])
-endif
-
-#################
-# Objective C++ #
-#################
-
-if is_variable('objcpp')
- all_objcpp_warnings = []
-
- if objcpp.get_id() == 'clang'
- all_objcpp_warnings += ['-Weverything']
-
- elif objpp.get_id() == 'gcc'
- all_objcpp_warnings = gcc_common_warnings
- endif
-
- all_objcpp_warnings = objcpp.get_supported_arguments(all_objcpp_warnings)
- add_global_arguments(all_objcpp_warnings, language: ['objcpp'])
-endif
diff --git a/meson_options.txt b/meson_options.txt
index 620747b..b468923 100644
--- a/meson_options.txt
+++ b/meson_options.txt
@@ -1,4 +1,4 @@
-# Copyright 2021-2022 David Robillard <d@drobilla.net>
+# Copyright 2021-2023 David Robillard <d@drobilla.net>
# SPDX-License-Identifier: 0BSD OR ISC
option('cocoa', type: 'feature', value: 'auto', yield: true,
@@ -13,11 +13,14 @@ option('gtk2', type: 'feature', value: 'auto', yield: true,
option('gtk3', type: 'feature', value: 'auto', yield: true,
description : 'Build Gtk3 wrappers')
+option('html', type: 'feature', value: 'auto', yield: true,
+ description: 'Build paginated HTML documentation')
+
option('qt5', type: 'feature', value: 'auto', yield: true,
description : 'Build Qt5 wrappers')
-option('strict', type: 'boolean', value: false, yield: true,
- description: 'Enable ultra-strict warnings')
+option('singlehtml', type: 'feature', value: 'auto', yield: true,
+ description: 'Build single-page HTML documentation')
option('tests', type: 'feature', value: 'auto', yield: true,
description: 'Build tests')
diff --git a/scripts/dox_to_sphinx.py b/scripts/dox_to_sphinx.py
deleted file mode 100755
index 06cfbdb..0000000
--- a/scripts/dox_to_sphinx.py
+++ /dev/null
@@ -1,663 +0,0 @@
-#!/usr/bin/env python3
-
-# Copyright 2020 David Robillard <d@drobilla.net>
-# SPDX-License-Identifier: ISC
-
-"""
-Write Sphinx markup from Doxygen XML.
-
-Takes a path to a directory of XML generated by Doxygen, and emits a directory
-with a reStructuredText file for every documented symbol.
-"""
-
-import argparse
-import os
-import sys
-import textwrap
-import xml.etree.ElementTree
-
-__author__ = "David Robillard"
-__date__ = "2020-11-18"
-__email__ = "d@drobilla.net"
-__license__ = "ISC"
-__version__ = __date__.replace("-", ".")
-
-
-def load_index(index_path):
- """
- Load the index from XML.
-
- :returns: A dictionary from ID to skeleton records with basic information
- for every documented entity. Some records have an ``xml_filename`` key
- with the filename of a definition file. These files will be loaded later
- to flesh out the records in the index.
- """
-
- root = xml.etree.ElementTree.parse(index_path).getroot()
- index = {}
-
- for compound in root:
- compound_id = compound.get("refid")
- compound_kind = compound.get("kind")
- compound_name = compound.find("name").text
- if compound_kind in ["dir", "file", "page"]:
- continue
-
- # Add record for compound (compounds appear only once in the index)
- assert compound_id not in index
- index[compound_id] = {
- "kind": compound_kind,
- "name": compound_name,
- "xml_filename": compound_id + ".xml",
- "children": [],
- }
-
- name_prefix = (
- ("%s::" % compound_name) if compound_kind == "namespace" else ""
- )
-
- for child in compound.findall("member"):
- if child.get("refid") in index:
- assert compound_kind == "group"
- continue
-
- # Everything has a kind and a name
- child_record = {
- "kind": child.get("kind"),
- "name": name_prefix + child.find("name").text,
- }
-
- if child.get("kind") == "enum":
- # Enums are not compounds, but we want to resolve the parent of
- # their values so they are not written as top level documents
- child_record["children"] = []
-
- if child.get("kind") == "enumvalue":
- # Remove namespace prefix
- child_record["name"] = child.find("name").text
-
- index[child.get("refid")] = child_record
-
- return index
-
-
-def resolve_index(index, root):
- """
- Walk a definition document and extend the index for linking.
-
- This does two things: sets the "parent" and "children" fields of all
- applicable records, and sets the "strong" field of enums so that the
- correct Sphinx role can be used when referring to them.
- """
-
- def add_child(index, parent_id, child_id):
- parent = index[parent_id]
- child = index[child_id]
-
- if child["kind"] == "enumvalue":
- assert parent["kind"] == "enum"
- assert "parent" not in child or child["parent"] == parent_id
- child["parent"] = parent_id
-
- else:
- if parent["kind"] in ["class", "struct", "union"]:
- assert "parent" not in child or child["parent"] == parent_id
- child["parent"] = parent_id
-
- if child_id not in parent["children"]:
- parent["children"] += [child_id]
-
- compound = root.find("compounddef")
- compound_kind = compound.get("kind")
-
- if compound_kind == "group":
- for subgroup in compound.findall("innergroup"):
- add_child(index, compound.get("id"), subgroup.get("refid"))
-
- for klass in compound.findall("innerclass"):
- add_child(index, compound.get("id"), klass.get("refid"))
-
- for section in compound.findall("sectiondef"):
- if section.get("kind").startswith("private"):
- for member in section.findall("memberdef"):
- if member.get("id") in index:
- del index[member.get("id")]
- else:
- for member in section.findall("memberdef"):
- member_id = member.get("id")
- add_child(index, compound.get("id"), member_id)
-
- if member.get("kind") == "enum":
- index[member_id]["strong"] = member.get("strong") == "yes"
- for value in member.findall("enumvalue"):
- add_child(index, member_id, value.get("id"))
-
-
-def sphinx_role(record, lang):
- """
- Return the Sphinx role used for a record.
-
- This is used for the description directive like ".. c:function::", and
- links like ":c:func:`foo`.
- """
-
- kind = record["kind"]
-
- if kind in ["class", "function", "namespace", "struct", "union"]:
- return lang + ":" + kind
-
- if kind == "define":
- return "c:macro"
-
- if kind == "enum":
- return lang + (":enum-class" if record["strong"] else ":enum")
-
- if kind == "typedef":
- return lang + ":type"
-
- if kind == "enumvalue":
- return lang + ":enumerator"
-
- if kind == "variable":
- return lang + (":member" if "parent" in record else ":var")
-
- raise RuntimeError("No known role for kind '%s'" % kind)
-
-
-def child_identifier(lang, parent_name, child_name):
- """
- Return the identifier for an enum value or struct member.
-
- Sphinx, for some reason, uses a different syntax for this in C and C++.
- """
-
- separator = "::" if lang == "cpp" else "."
-
- return "%s%s%s" % (parent_name, separator, child_name)
-
-
-def link_markup(index, lang, refid):
- """Return a Sphinx link for a Doxygen reference."""
-
- record = index[refid]
- kind, name = record["kind"], record["name"]
- role = sphinx_role(record, lang)
-
- if kind in ["class", "enum", "struct", "typedef", "union"]:
- return ":%s:`%s`" % (role, name)
-
- if kind == "function":
- return ":%s:func:`%s`" % (lang, name)
-
- if kind == "enumvalue":
- parent_name = index[record["parent"]]["name"]
- return ":%s:`%s`" % (role, child_identifier(lang, parent_name, name))
-
- if kind == "variable":
- if "parent" not in record:
- return ":%s:var:`%s`" % (lang, name)
-
- parent_name = index[record["parent"]]["name"]
- return ":%s:`%s`" % (role, child_identifier(lang, parent_name, name))
-
- raise RuntimeError("Unknown link target kind: %s" % kind)
-
-
-def indent(markup, depth):
- """
- Indent markup to a depth level.
-
- Like textwrap.indent() but takes an integer and works in reST indentation
- levels for clarity."
- """
-
- return textwrap.indent(markup, " " * depth)
-
-
-def heading(text, level):
- """
- Return a ReST heading at a given level.
-
- Follows the style in the Python documentation guide, see
- <https://devguide.python.org/documenting/#sections>.
- """
-
- assert 1 <= level <= 6
-
- chars = ("#", "*", "=", "-", "^", '"')
- line = chars[level] * len(text)
-
- return "%s\n%s\n%s\n\n" % (line if level < 3 else "", text, line)
-
-
-def dox_to_rst(index, lang, node):
- """
- Convert documentation commands (docCmdGroup) to Sphinx markup.
-
- This is used to convert the content of descriptions in the documentation.
- It recursively parses all children tags and raises a RuntimeError if any
- unknown tag is encountered.
- """
-
- def field_value(markup):
- """Return a value for a field as a single line or indented block."""
- if "\n" in markup.strip():
- return "\n" + indent(markup, 1)
-
- return " " + markup.strip()
-
- if node.tag == "computeroutput":
- # assert len(node) == 0 FIXME
- return "``%s``" % node.text
-
- if node.tag == "itemizedlist":
- markup = ""
- for item in node.findall("listitem"):
- assert len(item) == 1
- markup += "\n- %s" % dox_to_rst(index, lang, item[0])
-
- return markup
-
- if node.tag == "para":
- markup = node.text if node.text is not None else ""
- for child in node:
- markup += dox_to_rst(index, lang, child)
- markup += child.tail if child.tail is not None else ""
-
- return markup.strip() + "\n\n"
-
- if node.tag == "parameterlist":
- markup = ""
- for item in node.findall("parameteritem"):
- name = item.find("parameternamelist/parametername")
- description = item.find("parameterdescription")
- assert len(description) == 1
- markup += "\n\n:param %s:%s" % (
- name.text,
- field_value(dox_to_rst(index, lang, description[0])),
- )
-
- return markup + "\n"
-
- if node.tag == "programlisting":
- return "\n.. code-block:: %s\n\n%s" % (
- lang,
- indent(plain_text(node), 1),
- )
-
- if node.tag == "ref":
- refid = node.get("refid")
- if refid not in index:
- sys.stderr.write("warning: Unresolved link: %s\n" % refid)
- return node.text
-
- assert len(node) == 0
- assert len(link_markup(index, lang, refid)) > 0
- return link_markup(index, lang, refid)
-
- if node.tag == "simplesect":
- assert len(node) == 1
-
- if node.get("kind") == "return":
- return "\n:returns:" + field_value(
- dox_to_rst(index, lang, node[0])
- )
-
- if node.get("kind") == "see":
- return dox_to_rst(index, lang, node[0])
-
- raise RuntimeError("Unknown simplesect kind: %s" % node.get("kind"))
-
- if node.tag == "ulink":
- return "`%s <%s>`_" % (node.text, node.get("url"))
-
- raise RuntimeError("Unknown documentation command: %s" % node.tag)
-
-
-def description_markup(index, lang, node):
- """Return the markup for a brief or detailed description."""
-
- assert node.tag == "briefdescription" or node.tag == "detaileddescription"
- assert not (node.tag == "briefdescription" and len(node) > 1)
- assert len(node.text.strip()) == 0
-
- return "".join([dox_to_rst(index, lang, child) for child in node])
-
-
-def set_descriptions(index, lang, definition, record):
- """Set a record's brief/detailed descriptions from the XML definition."""
-
- for tag in ["briefdescription", "detaileddescription"]:
- node = definition.find(tag)
- if node is not None:
- record[tag] = description_markup(index, lang, node)
-
-
-def set_template_params(node, record):
- """Set a record's template_params from the XML definition."""
-
- template_param_list = node.find("templateparamlist")
- if template_param_list is not None:
- params = []
- for param in template_param_list.findall("param"):
- if param.find("declname") is not None:
- # Value parameter
- type_text = plain_text(param.find("type"))
- name_text = plain_text(param.find("declname"))
-
- params += ["%s %s" % (type_text, name_text)]
- else:
- # Type parameter
- params += ["%s" % (plain_text(param.find("type")))]
-
- record["template_params"] = "%s" % ", ".join(params)
-
-
-def plain_text(node):
- """
- Return the plain text of a node with all tags ignored.
-
- This is needed where Doxygen may include refs but Sphinx needs plain text
- because it parses things itself to generate links.
- """
-
- if node.tag == "sp":
- markup = " "
- elif node.text is not None:
- markup = node.text
- else:
- markup = ""
-
- for child in node:
- markup += plain_text(child)
- markup += child.tail if child.tail is not None else ""
-
- return markup
-
-
-def local_name(name):
- """Return a name with all namespace prefixes stripped."""
-
- return name[name.rindex("::") + 2 :] if "::" in name else name
-
-
-def read_definition_doc(index, lang, root):
- """Walk a definition document and update described records in the index."""
-
- # Set descriptions for the compound itself
- compound = root.find("compounddef")
- compound_record = index[compound.get("id")]
- set_descriptions(index, lang, compound, compound_record)
- set_template_params(compound, compound_record)
-
- if compound.find("title") is not None:
- compound_record["title"] = compound.find("title").text.strip()
-
- # Set documentation for all children
- for section in compound.findall("sectiondef"):
- if section.get("kind").startswith("private"):
- continue
-
- for member in section.findall("memberdef"):
- kind = member.get("kind")
- record = index[member.get("id")]
- set_descriptions(index, lang, member, record)
- set_template_params(member, record)
-
- if compound.get("kind") in ["class", "struct", "union"]:
- assert kind in ["function", "typedef", "variable"]
- record["type"] = plain_text(member.find("type"))
-
- if kind == "enum":
- for value in member.findall("enumvalue"):
- set_descriptions(
- index, lang, value, index[value.get("id")]
- )
-
- elif kind == "function":
- record["prototype"] = "%s %s%s" % (
- plain_text(member.find("type")),
- member.find("name").text,
- member.find("argsstring").text,
- )
-
- elif kind == "typedef":
- name = local_name(record["name"])
- args_text = member.find("argsstring").text
- target_text = plain_text(member.find("type"))
- if args_text is not None: # Function pointer
- assert target_text[-2:] == "(*" and args_text[0] == ")"
- record["type"] = target_text + args_text
- record["definition"] = target_text + name + args_text
- else: # Normal named typedef
- assert target_text is not None
- record["type"] = target_text
- if member.find("definition").text.startswith("using"):
- record["definition"] = "%s = %s" % (
- name,
- target_text,
- )
- else:
- record["definition"] = "%s %s" % (
- target_text,
- name,
- )
-
- elif kind == "variable":
- record["definition"] = member.find("definition").text
-
-
-def declaration_string(record):
- """
- Return the string that describes a declaration.
-
- This is what follows the directive, and is in C/C++ syntax, except without
- keywords like "typedef" and "using" as expected by Sphinx. For example,
- "struct ThingImpl Thing" or "void run(int value)".
- """
-
- kind = record["kind"]
- result = ""
-
- if "template_params" in record:
- result = "template <%s> " % record["template_params"]
-
- if kind == "function":
- result += record["prototype"]
- elif kind == "typedef":
- result += record["definition"]
- elif kind == "variable":
- if "parent" in record:
- result += "%s %s" % (record["type"], local_name(record["name"]))
- else:
- result += record["definition"]
- elif "type" in record:
- result += "%s %s" % (record["type"], local_name(record["name"]))
- else:
- result += local_name(record["name"])
-
- return result
-
-
-def document_markup(index, lang, record):
- """Return the complete document that describes some documented entity."""
-
- kind = record["kind"]
- role = sphinx_role(record, lang)
- name = record["name"]
- markup = ""
-
- if name != local_name(name):
- markup += ".. cpp:namespace:: %s\n\n" % name[0 : name.rindex("::")]
-
- # Write top-level directive
- markup += ".. %s:: %s\n" % (role, declaration_string(record))
-
- # Write main description blurb
- markup += "\n"
- markup += indent(record["briefdescription"], 1)
- markup += indent(record["detaileddescription"], 1)
-
- assert (
- kind in ["class", "enum", "namespace", "struct", "union"]
- or "children" not in record
- )
-
- # Sphinx C++ namespaces work by setting a scope, they have no content
- child_indent = 0 if kind == "namespace" else 1
-
- # Write inline children if applicable
- markup += "\n"
- for child_id in record.get("children", []):
- child_record = index[child_id]
- child_role = sphinx_role(child_record, lang)
-
- child_header = ".. %s:: %s\n\n" % (
- child_role,
- declaration_string(child_record),
- )
-
- markup += "\n"
- markup += indent(child_header, child_indent)
- markup += indent(child_record["briefdescription"], child_indent + 1)
- markup += indent(child_record["detaileddescription"], child_indent + 1)
- markup += "\n"
-
- return markup
-
-
-def symbol_filename(name):
- """Adapt the name of a symbol to be suitable for use as a filename."""
-
- return name.replace("::", "__")
-
-
-def emit_symbols(index, lang, symbol_dir, force):
- """Write a description file for every symbol documented in the index."""
-
- for record in index.values():
- if (
- record["kind"] in ["group", "namespace"]
- or "parent" in record
- and index[record["parent"]]["kind"] != "group"
- ):
- continue
-
- name = record["name"]
- filename = os.path.join(symbol_dir, symbol_filename("%s.rst" % name))
- if not force and os.path.exists(filename):
- raise FileExistsError("File already exists: '%s'" % filename)
-
- with open(filename, "w") as rst:
- rst.write(heading(local_name(name), 3))
- rst.write(document_markup(index, lang, record))
-
-
-def emit_groups(index, output_dir, symbol_dir_name, force):
- """Write a description file for every group documented in the index."""
-
- for record in index.values():
- if record["kind"] != "group":
- continue
-
- name = record["name"]
- filename = os.path.join(output_dir, "%s.rst" % name)
- if not force and os.path.exists(filename):
- raise FileExistsError("File already exists: '%s'" % filename)
-
- with open(filename, "w") as rst:
- rst.write(heading(record["title"], 2))
-
- # Get all child group and symbol names
- group_names = []
- symbol_names = []
- for child_id in record["children"]:
- child = index[child_id]
- if child["kind"] == "group":
- group_names += [child["name"]]
- else:
- symbol_names += [child["name"]]
-
- # Emit description (document body)
- rst.write(record["briefdescription"] + "\n\n")
- rst.write(record["detaileddescription"] + "\n\n")
-
- # Emit TOC
- rst.write(".. toctree::\n")
-
- # Emit groups at the top of the TOC
- for group_name in group_names:
- rst.write("\n" + indent(group_name, 1))
-
- # Emit symbols in sorted order
- for symbol_name in sorted(symbol_names):
- path = "/".join(
- [symbol_dir_name, symbol_filename(symbol_name)]
- )
- rst.write("\n" + indent(path, 1))
-
- rst.write("\n")
-
-
-def run(index_xml_path, output_dir, symbol_dir_name, language, force):
- """Write a directory of Sphinx files from a Doxygen XML directory."""
-
- # Build skeleton index from index.xml
- xml_dir = os.path.dirname(index_xml_path)
- index = load_index(index_xml_path)
-
- # Load all definition documents
- definition_docs = []
- for record in index.values():
- if "xml_filename" in record:
- xml_path = os.path.join(xml_dir, record["xml_filename"])
- definition_docs += [xml.etree.ElementTree.parse(xml_path)]
-
- # Do an initial pass of the definition documents to resolve the index
- for root in definition_docs:
- resolve_index(index, root)
-
- # Finally read the documentation from definition documents
- for root in definition_docs:
- read_definition_doc(index, language, root)
-
- # Emit output files
- symbol_dir = os.path.join(output_dir, symbol_dir_name)
- os.makedirs(symbol_dir, exist_ok=True)
- emit_symbols(index, language, symbol_dir, force)
- emit_groups(index, output_dir, symbol_dir_name, force)
-
-
-if __name__ == "__main__":
- ap = argparse.ArgumentParser(
- usage="%(prog)s [OPTION]... XML_DIR OUTPUT_DIR",
- description=__doc__,
- formatter_class=argparse.RawDescriptionHelpFormatter,
- )
-
- ap.add_argument(
- "-f",
- "--force",
- action="store_true",
- help="overwrite files",
- )
-
- ap.add_argument(
- "-l",
- "--language",
- default="c",
- choices=["c", "cpp"],
- help="language domain for output",
- )
-
- ap.add_argument(
- "-s",
- "--symbol-dir-name",
- default="symbols",
- help="name for subdirectory of symbol documentation files",
- )
-
- ap.add_argument("index_xml_path", help="path index.xml from Doxygen")
- ap.add_argument("output_dir", help="output directory")
-
- run(**vars(ap.parse_args(sys.argv[1:])))
diff --git a/src/dylib.h b/src/dylib.h
index 65bbc1a..ac8bfbc 100644
--- a/src/dylib.h
+++ b/src/dylib.h
@@ -1,4 +1,4 @@
-// Copyright 2020-2022 David Robillard <d@drobilla.net>
+// Copyright 2020-2023 David Robillard <d@drobilla.net>
// SPDX-License-Identifier: ISC
#ifndef SUIL_DYLIB_H
@@ -11,7 +11,7 @@
enum DylibFlags {
DYLIB_GLOBAL = 0,
DYLIB_LAZY = 1,
- DYLIB_NOW = 2,
+ DYLIB_NOW = 2
};
static inline void*
@@ -40,7 +40,7 @@ dylib_error(void)
enum DylibFlags {
DYLIB_GLOBAL = RTLD_GLOBAL,
DYLIB_LAZY = RTLD_LAZY,
- DYLIB_NOW = RTLD_NOW,
+ DYLIB_NOW = RTLD_NOW
};
static inline void*
diff --git a/src/gtk2_in_qt5.cpp b/src/gtk2_in_qt5.cpp
deleted file mode 100644
index 705f119..0000000
--- a/src/gtk2_in_qt5.cpp
+++ /dev/null
@@ -1,156 +0,0 @@
-// Copyright 2011-2022 David Robillard <d@drobilla.net>
-// Copyright 2015 Rui Nuno Capela <rncbc@rncbc.org>
-// SPDX-License-Identifier: ISC
-
-#include "dylib.h"
-#include "suil_config.h" // IWYU pragma: keep
-#include "suil_internal.h"
-#include "warnings.h"
-
-#include "lv2/core/lv2.h"
-#include "suil/suil.h"
-
-SUIL_DISABLE_QT_WARNINGS
-#include <QVBoxLayout>
-#include <QWidget>
-#include <QWindow>
-#include <Qt>
-#include <QtGui>
-SUIL_RESTORE_WARNINGS
-
-#undef signals
-
-SUIL_DISABLE_GTK_WARNINGS
-#include <gdk/gdk.h>
-#include <glib-object.h>
-#include <glib.h>
-#include <gobject/gclosure.h>
-#include <gtk/gtk.h>
-SUIL_RESTORE_WARNINGS
-
-#include <cstdlib>
-
-extern "C" {
-
-struct SuilGtk2InQt5Wrapper {
- QWidget* host_widget;
- QWindow* window;
- GtkWidget* plug;
-};
-
-static void
-on_size_request(GtkWidget*, GtkRequisition* requisition, gpointer user_data)
-{
- auto* const wrap = static_cast<QWidget*>(user_data);
- wrap->setMinimumSize(requisition->width, requisition->height);
-}
-
-static void
-on_size_allocate(GtkWidget*, GdkRectangle* allocation, gpointer user_data)
-{
- auto* const wrap = static_cast<QWidget*>(user_data);
- wrap->resize(allocation->width, allocation->height);
-}
-
-static void
-wrapper_free(SuilWrapper* wrapper)
-{
- auto* impl = static_cast<SuilGtk2InQt5Wrapper*>(wrapper->impl);
-
- if (impl->window) {
- impl->window->setParent(nullptr);
- delete impl->window;
- }
-
- if (impl->plug) {
- gtk_widget_destroy(impl->plug);
- }
-
- delete impl->host_widget;
-
- free(impl);
-}
-
-static int
-wrapper_wrap(SuilWrapper* wrapper, SuilInstance* instance)
-{
- auto* const impl = static_cast<SuilGtk2InQt5Wrapper*>(wrapper->impl);
- auto* const wrap = new QWidget(nullptr, Qt::Window);
- GtkWidget* const plug = gtk_plug_new(0);
- auto* const widget = static_cast<GtkWidget*>(instance->ui_widget);
-
- gtk_container_add(GTK_CONTAINER(plug), widget);
- gtk_widget_show_all(plug);
-
- const WId wid = (WId)gtk_plug_get_id(GTK_PLUG(plug));
-
- QWindow* window = QWindow::fromWinId(wid);
- QWidget* container =
- QWidget::createWindowContainer(window, wrap, Qt::WindowFlags());
-
- auto* layout = new QVBoxLayout();
- layout->setMargin(0);
- layout->setSpacing(0);
- layout->addWidget(container, 0, Qt::Alignment());
- wrap->setLayout(layout);
-
-#ifdef SUIL_OLD_GTK
- wrap->resize(widget->allocation.width, widget->allocation.height);
-#else
- GtkAllocation alloc;
- gtk_widget_get_allocation(widget, &alloc);
- wrap->resize(alloc.width, alloc.height);
-#endif
-
- g_signal_connect(
- G_OBJECT(plug), "size-request", G_CALLBACK(on_size_request), wrap);
-
- g_signal_connect(
- G_OBJECT(plug), "size-allocate", G_CALLBACK(on_size_allocate), wrap);
-
- impl->host_widget = wrap;
- impl->window = window;
- impl->plug = plug;
- instance->host_widget = wrap;
-
- return 0;
-}
-
-SUIL_LIB_EXPORT
-SuilWrapper*
-suil_wrapper_new(SuilHost* host,
- const char*,
- const char*,
- LV2_Feature***,
- unsigned)
-{
- /* We have to open libgtk here, so Gtk type symbols are present and will be
- found by the introspection stuff. This is required at least to make
- GtkBuilder use in UIs work, otherwise they will cause "Invalid object
- type" errors.
- */
- if (!host->gtk_lib) {
- dylib_error();
- host->gtk_lib = dylib_open(SUIL_GTK2_LIB_NAME, DYLIB_LAZY | DYLIB_GLOBAL);
- if (!host->gtk_lib) {
- SUIL_ERRORF(
- "Failed to open %s (%s)\n", SUIL_GTK2_LIB_NAME, dylib_error());
- return nullptr;
- }
-
- gtk_init(nullptr, nullptr);
- }
-
- /* Create wrapper implementation. */
- auto* const impl =
- static_cast<SuilGtk2InQt5Wrapper*>(calloc(1, sizeof(SuilGtk2InQt5Wrapper)));
-
- auto* wrapper = static_cast<SuilWrapper*>(calloc(1, sizeof(SuilWrapper)));
- wrapper->wrap = wrapper_wrap;
- wrapper->free = wrapper_free;
- wrapper->impl = impl;
-
- return wrapper;
-}
-
-} // extern "C"
diff --git a/src/instance.c b/src/instance.c
index a2e478a..441176c 100644
--- a/src/instance.c
+++ b/src/instance.c
@@ -36,23 +36,14 @@ suil_ui_supported(const char* host_type_uri, const char* ui_type_uri)
}
if ((!strcmp(host_type_uri, GTK2_UI_URI) &&
- !strcmp(ui_type_uri, QT5_UI_URI)) ||
- (!strcmp(host_type_uri, QT5_UI_URI) &&
- !strcmp(ui_type_uri, GTK2_UI_URI)) ||
- (!strcmp(host_type_uri, GTK2_UI_URI) &&
- !strcmp(ui_type_uri, X11_UI_URI)) ||
+ (!strcmp(ui_type_uri, COCOA_UI_URI) ||
+ !strcmp(ui_type_uri, WIN_UI_URI) ||
+ !strcmp(ui_type_uri, X11_UI_URI))) ||
(!strcmp(host_type_uri, GTK3_UI_URI) &&
!strcmp(ui_type_uri, X11_UI_URI)) ||
- (!strcmp(host_type_uri, GTK3_UI_URI) &&
- !strcmp(ui_type_uri, QT5_UI_URI)) ||
- (!strcmp(host_type_uri, GTK2_UI_URI) &&
- !strcmp(ui_type_uri, WIN_UI_URI)) ||
- (!strcmp(host_type_uri, GTK2_UI_URI) &&
- !strcmp(ui_type_uri, COCOA_UI_URI)) ||
(!strcmp(host_type_uri, QT5_UI_URI) &&
- !strcmp(ui_type_uri, X11_UI_URI)) ||
- (!strcmp(host_type_uri, QT5_UI_URI) &&
- !strcmp(ui_type_uri, COCOA_UI_URI))) {
+ (!strcmp(ui_type_uri, COCOA_UI_URI) ||
+ !strcmp(ui_type_uri, X11_UI_URI)))) {
return SUIL_WRAPPING_EMBEDDED;
}
@@ -68,16 +59,6 @@ open_wrapper(SuilHost* host,
{
const char* module_name = NULL;
- if (!strcmp(container_type_uri, QT5_UI_URI) &&
- !strcmp(ui_type_uri, GTK2_UI_URI)) {
- module_name = "suil_gtk2_in_qt5";
- }
-
- if (!strcmp(container_type_uri, GTK2_UI_URI) &&
- !strcmp(ui_type_uri, QT5_UI_URI)) {
- module_name = "suil_qt5_in_gtk2";
- }
-
if (!strcmp(container_type_uri, GTK2_UI_URI) &&
!strcmp(ui_type_uri, X11_UI_URI)) {
module_name = "suil_x11_in_gtk2";
@@ -88,11 +69,6 @@ open_wrapper(SuilHost* host,
module_name = "suil_x11_in_gtk3";
}
- if (!strcmp(container_type_uri, GTK3_UI_URI) &&
- !strcmp(ui_type_uri, QT5_UI_URI)) {
- module_name = "suil_qt5_in_gtk3";
- }
-
if (!strcmp(container_type_uri, GTK2_UI_URI) &&
!strcmp(ui_type_uri, WIN_UI_URI)) {
module_name = "suil_win_in_gtk2";
diff --git a/src/qt5_in_gtk.cpp b/src/qt5_in_gtk.cpp
deleted file mode 100644
index 1b3f31d..0000000
--- a/src/qt5_in_gtk.cpp
+++ /dev/null
@@ -1,239 +0,0 @@
-// Copyright 2011-2022 David Robillard <d@drobilla.net>
-// Copyright 2018 Rui Nuno Capela <rncbc@rncbc.org>
-// SPDX-License-Identifier: ISC
-
-#include "suil_internal.h"
-#include "warnings.h"
-
-#include "lv2/core/lv2.h"
-#include "lv2/options/options.h"
-#include "lv2/ui/ui.h"
-#include "lv2/urid/urid.h"
-#include "suil/suil.h"
-
-SUIL_DISABLE_QT_WARNINGS
-#include <QVBoxLayout>
-#include <QWidget>
-#include <QWindow>
-#include <Qt>
-#include <QtGui>
-SUIL_RESTORE_WARNINGS
-
-#undef signals
-
-SUIL_DISABLE_GTK_WARNINGS
-
-#include <glib-object.h>
-#include <glib.h>
-#include <gobject/gclosure.h>
-#include <gtk/gtk.h>
-
-#if GTK_MAJOR_VERSION == 3
-# include <gtk/gtkx.h>
-#endif
-
-SUIL_RESTORE_WARNINGS
-
-#include <cstdlib>
-#include <cstring>
-
-extern "C" {
-
-struct SuilQtWrapper {
- GtkSocket socket;
- QWidget* qembed;
- SuilWrapper* wrapper;
- SuilInstance* instance;
- const LV2UI_Idle_Interface* idle_iface;
- guint idle_id;
- guint idle_ms;
-};
-
-struct SuilQtWrapperClass {
- GtkSocketClass parent_class;
-};
-
-GType
-suil_qt_wrapper_get_type(void); // Accessor for SUIL_TYPE_QT_WRAPPER
-
-#define SUIL_TYPE_QT_WRAPPER (suil_qt_wrapper_get_type())
-#define SUIL_QT_WRAPPER(obj) \
- (G_TYPE_CHECK_INSTANCE_CAST((obj), SUIL_TYPE_QT_WRAPPER, SuilQtWrapper))
-
-G_DEFINE_TYPE(SuilQtWrapper, suil_qt_wrapper, GTK_TYPE_SOCKET)
-
-static void
-suil_qt_wrapper_finalize(GObject* gobject)
-{
- SuilQtWrapper* const self = SUIL_QT_WRAPPER(gobject);
-
- if (self->idle_id) {
- g_source_remove(self->idle_id);
- self->idle_id = 0;
- }
-
- if (self->instance->handle) {
- self->instance->descriptor->cleanup(self->instance->handle);
- self->instance->handle = nullptr;
- }
-
- if (self->qembed) {
- self->qembed->deleteLater();
- }
-
- self->qembed = nullptr;
- self->idle_iface = nullptr;
- self->wrapper->impl = nullptr;
-
- G_OBJECT_CLASS(suil_qt_wrapper_parent_class)->finalize(gobject);
-}
-
-static void
-suil_qt_wrapper_class_init(SuilQtWrapperClass* klass)
-{
- GObjectClass* const gobject_class = G_OBJECT_CLASS(klass);
-
- gobject_class->finalize = suil_qt_wrapper_finalize;
-}
-
-static void
-suil_qt_wrapper_init(SuilQtWrapper* self)
-{
- self->qembed = nullptr;
- self->wrapper = nullptr;
- self->instance = nullptr;
- self->idle_iface = nullptr;
- self->idle_id = 0;
- self->idle_ms = 1000 / 30; // 30 Hz default
-}
-
-static void
-suil_qt_wrapper_realize(GtkWidget* w, gpointer)
-{
- SuilQtWrapper* const wrap = SUIL_QT_WRAPPER(w);
- GtkSocket* const s = GTK_SOCKET(w);
- const WId id = (WId)gtk_socket_get_id(s);
-
- wrap->qembed->winId();
- wrap->qembed->windowHandle()->setParent(QWindow::fromWinId(id));
- wrap->qembed->show();
-}
-
-static int
-suil_qt_wrapper_resize(LV2UI_Feature_Handle handle, int width, int height)
-{
- gtk_widget_set_size_request(GTK_WIDGET(handle), width, height);
-
- return 0;
-}
-
-static gboolean
-suil_qt_wrapper_idle(void* data)
-{
- SuilQtWrapper* const wrap = SUIL_QT_WRAPPER(data);
-
- if (wrap->idle_iface) {
- wrap->idle_iface->idle(wrap->instance->handle);
- return TRUE; // Continue calling
- }
-
- return FALSE;
-}
-
-static int
-wrapper_wrap(SuilWrapper* wrapper, SuilInstance* instance)
-{
- SuilQtWrapper* const wrap = SUIL_QT_WRAPPER(wrapper->impl);
-
- wrap->qembed = new QWidget(nullptr, Qt::WindowFlags());
- wrap->wrapper = wrapper;
- wrap->instance = instance;
-
- auto* qwidget = static_cast<QWidget*>(instance->ui_widget);
- auto* layout = new QVBoxLayout();
- layout->setMargin(0);
- layout->setSpacing(0);
- layout->addWidget(qwidget, 0, Qt::Alignment());
-
- wrap->qembed->setLayout(layout);
-
- g_signal_connect_after(
- G_OBJECT(wrap), "realize", G_CALLBACK(suil_qt_wrapper_realize), nullptr);
-
- instance->host_widget = GTK_WIDGET(wrap);
-
- const LV2UI_Idle_Interface* idle_iface = nullptr;
- if (instance->descriptor->extension_data) {
- idle_iface = static_cast<const LV2UI_Idle_Interface*>(
- instance->descriptor->extension_data(LV2_UI__idleInterface));
- }
-
- if (idle_iface) {
- wrap->idle_iface = idle_iface;
- wrap->idle_id = g_timeout_add(wrap->idle_ms, suil_qt_wrapper_idle, wrap);
- }
-
- return 0;
-}
-
-static void
-wrapper_free(SuilWrapper* wrapper)
-{
- if (wrapper->impl) {
- SuilQtWrapper* const wrap = SUIL_QT_WRAPPER(wrapper->impl);
- gtk_widget_destroy(GTK_WIDGET(wrap));
- }
-}
-
-SUIL_LIB_EXPORT
-SuilWrapper*
-suil_wrapper_new(SuilHost*,
- const char*,
- const char*,
- LV2_Feature*** features,
- unsigned n_features)
-{
- auto* wrapper = static_cast<SuilWrapper*>(calloc(1, sizeof(SuilWrapper)));
- wrapper->wrap = wrapper_wrap;
- wrapper->free = wrapper_free;
-
- SuilQtWrapper* const wrap =
- SUIL_QT_WRAPPER(g_object_new(SUIL_TYPE_QT_WRAPPER, nullptr));
-
- wrap->wrapper = nullptr;
- wrapper->impl = wrap;
-
- wrapper->resize.handle = wrap;
- wrapper->resize.ui_resize = suil_qt_wrapper_resize;
-
- suil_add_feature(features, &n_features, LV2_UI__resize, &wrapper->resize);
- suil_add_feature(features, &n_features, LV2_UI__idleInterface, nullptr);
-
- // Scan for URID map and options
- LV2_URID_Map* map = nullptr;
- LV2_Options_Option* options = nullptr;
- for (LV2_Feature** f = *features; *f && (!map || !options); ++f) {
- if (!strcmp((*f)->URI, LV2_OPTIONS__options)) {
- options = static_cast<LV2_Options_Option*>((*f)->data);
- } else if (!strcmp((*f)->URI, LV2_URID__map)) {
- map = static_cast<LV2_URID_Map*>((*f)->data);
- }
- }
-
- if (map && options) {
- // Set UI update rate if given
- LV2_URID ui_updateRate = map->map(map->handle, LV2_UI__updateRate);
- for (LV2_Options_Option* o = options; o->key; ++o) {
- if (o->key == ui_updateRate) {
- wrap->idle_ms =
- static_cast<guint>(1000.0f / *static_cast<const float*>(o->value));
-
- break;
- }
- }
- }
-
- return wrapper;
-}
-
-} // extern "C"
diff --git a/src/suil_internal.h b/src/suil_internal.h
index b6640c3..753bfb1 100644
--- a/src/suil_internal.h
+++ b/src/suil_internal.h
@@ -1,4 +1,4 @@
-// Copyright 2007-2022 David Robillard <d@drobilla.net>
+// Copyright 2007-2023 David Robillard <d@drobilla.net>
// SPDX-License-Identifier: ISC
#ifndef SUIL_INTERNAL_H
@@ -21,11 +21,6 @@
#ifdef __cplusplus
extern "C" {
-# if defined(__GNUC__)
-# pragma GCC diagnostic push
-# pragma GCC diagnostic ignored "-Wold-style-cast"
-# pragma GCC diagnostic ignored "-Wzero-as-null-pointer-constant"
-# endif
#endif
#define SUIL_ERRORF(fmt, ...) fprintf(stderr, "suil error: " fmt, __VA_ARGS__)
diff --git a/src/win_in_gtk2.cpp b/src/win_in_gtk2.cpp
index cd5ead9..9c295e2 100644
--- a/src/win_in_gtk2.cpp
+++ b/src/win_in_gtk2.cpp
@@ -27,9 +27,6 @@ extern "C" {
#define SUIL_WIN_WRAPPER(obj) \
(G_TYPE_CHECK_INSTANCE_CAST((obj), SUIL_TYPE_WIN_WRAPPER, SuilWinWrapper))
-using SuilWinWrapper = _SuilWinWrapper;
-using SuilWinWrapperClass = _SuilWinWrapperClass;
-
struct _SuilWinWrapper {
GtkDrawingArea area;
SuilWrapper* wrapper;
@@ -44,6 +41,9 @@ struct _SuilWinWrapperClass {
GtkDrawingAreaClass parent_class;
};
+using SuilWinWrapper = _SuilWinWrapper;
+using SuilWinWrapperClass = _SuilWinWrapperClass;
+
GType
suil_win_wrapper_get_type(void); // Accessor for SUIL_TYPE_WIN_WRAPPER
diff --git a/src/x11_in_qt5.cpp b/src/x11_in_qt5.cpp
index 89323d7..7e3d6ac 100644
--- a/src/x11_in_qt5.cpp
+++ b/src/x11_in_qt5.cpp
@@ -25,7 +25,7 @@ SUIL_RESTORE_WARNINGS
#undef signals
-extern "C" {
+namespace {
class SuilQX11Widget : public QWidget
{
@@ -34,10 +34,10 @@ public:
: QWidget(parent, wflags)
{}
- SuilQX11Widget(const SuilQX11Widget&) = delete;
+ SuilQX11Widget(const SuilQX11Widget&) = delete;
SuilQX11Widget& operator=(const SuilQX11Widget&) = delete;
- SuilQX11Widget(SuilQX11Widget&&) = delete;
+ SuilQX11Widget(SuilQX11Widget&&) = delete;
SuilQX11Widget& operator=(SuilQX11Widget&&) = delete;
~SuilQX11Widget() override;
@@ -125,7 +125,7 @@ struct SuilX11InQt5Wrapper {
SuilQX11Widget* parent;
};
-static void
+void
wrapper_free(SuilWrapper* wrapper)
{
auto* impl = static_cast<SuilX11InQt5Wrapper*>(wrapper->impl);
@@ -135,7 +135,7 @@ wrapper_free(SuilWrapper* wrapper)
free(impl);
}
-static int
+int
wrapper_wrap(SuilWrapper* wrapper, SuilInstance* instance)
{
auto* const impl = static_cast<SuilX11InQt5Wrapper*>(wrapper->impl);
@@ -178,7 +178,7 @@ wrapper_wrap(SuilWrapper* wrapper, SuilInstance* instance)
return 0;
}
-static int
+int
wrapper_resize(LV2UI_Feature_Handle handle, int width, int height)
{
auto* const ew = static_cast<QWidget*>(handle);
@@ -186,6 +186,10 @@ wrapper_resize(LV2UI_Feature_Handle handle, int width, int height)
return 0;
}
+} // namespace
+
+extern "C" {
+
SUIL_LIB_EXPORT
SuilWrapper*
suil_wrapper_new(SuilHost*,
diff --git a/subprojects/sphinxygen.wrap b/subprojects/sphinxygen.wrap
new file mode 100644
index 0000000..9707d1a
--- /dev/null
+++ b/subprojects/sphinxygen.wrap
@@ -0,0 +1,14 @@
+# Copyright 2022-2023 David Robillard <d@drobilla.net>
+# SPDX-License-Identifier: 0BSD OR ISC
+
+[wrap-file]
+directory = sphinxygen-1.0.4
+source_url = https://download.drobilla.net/sphinxygen-1.0.4.tar.gz
+source_filename = sphinxygen-1.0.4.tar.gz
+source_hash = 12fa9f18ed9fca608f272520072257ba61fd9eff25613f86d83d4fce14fc01f5
+
+# [wrap-git]
+# url = https://gitlab.com/drobilla/sphinxygen.git
+# push-url = ssh://git@gitlab.com:drobilla/sphinxygen.git
+# revision = v1.0.4
+# depth = 1