diff --git a/CHANGELOG.md b/CHANGELOG.md index e76b844..1dfee7b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,11 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [Unreleased] +### Added +* Demonstration of plugin GUI using raw XCB calls. + The GUI is currently totally non-functional. + ## [0.1.3] - 2024-01-24 ### Changed * Plugin version is now source from V mod file. diff --git a/Makefile b/Makefile index e8e1ef9..4f63531 100644 --- a/Makefile +++ b/Makefile @@ -1,18 +1,17 @@ -SOURCEDIR = src +SOURCEDIR = . BUILDDIR = build TARGET = $(BUILDDIR)/hello_world.clap V_CMD = v -cc gcc -shared -enable-globals - -# List all V source files -V_SRC = $(wildcard $(SOURCEDIR)/*.v) - +V_SRC := $(shell find $(SOURCEDIR) -name '*.v' -o -name '*.c') # Debug and Release build options DEBUG ?= 0 RELEASE ?= 0 ifeq ($(DEBUG),1) - V_FLAGS = -cg -show-c-output + V_FLAGS = -cg else ifeq ($(DEBUG),2) + V_FLAGS = -cg -show-c-output +else ifeq ($(DEBUG),3) # Even more debug! Very noisy. V_FLAGS = -cg -show-c-output -trace-calls else ifeq ($(RELEASE),1) diff --git a/main.v b/main.v new file mode 100644 index 0000000..6048746 --- /dev/null +++ b/main.v @@ -0,0 +1,14 @@ +import vclap +// Exposes the plugin to the host (DAW). + +@[markused] +__global clap_entry = vclap.plugin_entry +// This requires modification to `clap/entry.h`. +// Remove "const" so you get: +// CLAP_EXPORT extern clap_plugin_entry_t clap_entry; + +fn init() { + $if debug { + eprintln('VCLAP in debug mode') + } +} diff --git a/src/main.v b/src/main.v deleted file mode 100644 index 140aa25..0000000 --- a/src/main.v +++ /dev/null @@ -1,22 +0,0 @@ -import log -import v.vmod - -// Only need to change the mod file to update the version. -const manifest = vmod.decode(@VMOD_FILE) or { panic(err) } -const current_version = manifest.version - -// Exposes the plugin to the host (DAW). - -@[markused] -__global clap_entry = plugin_entry -// This requires modification to `clap/entry.h`. -// Remove "const" so you get: -// CLAP_EXPORT extern clap_plugin_entry_t clap_entry; - -fn init() { - $if debug { - log.set_level(log.Level.debug) - } $else { - log.set_level(log.Level.info) - } -} diff --git a/vclap/.clang-format b/vclap/.clang-format new file mode 100644 index 0000000..4e5fd17 --- /dev/null +++ b/vclap/.clang-format @@ -0,0 +1,2 @@ +--- +IndentWidth: 4 diff --git a/src/c_abi.v b/vclap/c_abi.v similarity index 98% rename from src/c_abi.v rename to vclap/c_abi.v index 8cd34f4..cdc4e0f 100644 --- a/src/c_abi.v +++ b/vclap/c_abi.v @@ -1,4 +1,6 @@ -#flag -I./include +module vclap + +#flag -I @VMODROOT/include #include "clap/clap.h" enum ClapProcessStatus { diff --git a/src/ext_audio_ports.v b/vclap/ext_audio_ports.v similarity index 98% rename from src/ext_audio_ports.v rename to vclap/ext_audio_ports.v index c455974..f7dbef6 100644 --- a/src/ext_audio_ports.v +++ b/vclap/ext_audio_ports.v @@ -1,3 +1,5 @@ +module vclap + const clap_ext_audio_ports = unsafe { (&char(C.CLAP_EXT_AUDIO_PORTS)).vstring() } const clap_port_stereo = unsafe { (&char(C.CLAP_PORT_STEREO)).vstring() } const clap_port_mono = unsafe { (&char(C.CLAP_PORT_MONO)).vstring() } diff --git a/vclap/ext_gui.v b/vclap/ext_gui.v new file mode 100644 index 0000000..c698ce7 --- /dev/null +++ b/vclap/ext_gui.v @@ -0,0 +1,49 @@ +module vclap + +const clap_ext_gui = unsafe { (&char(C.CLAP_EXT_GUI)).vstring() } +const clap_window_api_x11 = unsafe { (&char(C.CLAP_WINDOW_API_X11)).vstring() } +const clap_window_api_wayland = unsafe { (&char(C.CLAP_WINDOW_API_WAYLAND)).vstring() } + +type C.clap_hwnd = voidptr +type C.clap_nsview = voidptr +type C.clap_xwnd = u32 + +@[typedef] +struct C.clap_window_t { + api &char + // union { + cocoa C.clap_nsview + x11 C.clap_xwnd + win32 C.clap_hwnd + ptr voidptr + // } +} + +@[typedef] +struct C.clap_gui_resize_hints_t { + can_resize_horizontally bool + can_resize_vertically bool + preserve_aspect_ratio bool + aspect_ratio_width u32 + aspect_ratio_height u32 +} + +@[typedef] +struct C.clap_plugin_gui_t { + is_api_supported fn (&C.clap_plugin_t, &char, bool) bool + // voidptr is &&char in original but used in a weird way. + get_preferred_api fn (&C.clap_plugin_t, voidptr, &bool) bool + create fn (&C.clap_plugin_t, &char, bool) bool + destroy fn (&C.clap_plugin_t) + set_scale fn (&C.clap_plugin_t, f64) bool + get_size fn (&C.clap_plugin_t, &u32, &u32) bool + can_resize fn (&C.clap_plugin_t) bool + get_resize_hints fn (&C.clap_plugin_t, &C.clap_gui_resize_hints_t) bool + adjust_size fn (&C.clap_plugin_t, &u32, &u32) bool + set_size fn (&C.clap_plugin_t, &u32, &u32) bool + set_parent fn (&C.clap_plugin_t, &C.clap_window_t) bool + set_transient fn (&C.clap_plugin_t, &C.clap_window_t) bool + suggest_title fn (&C.clap_plugin_t, &char) + show fn (&C.clap_plugin_t) bool + hide fn (&C.clap_plugin_t) bool +} diff --git a/src/ext_latency.v b/vclap/ext_latency.v similarity index 91% rename from src/ext_latency.v rename to vclap/ext_latency.v index c199b6f..556a1e5 100644 --- a/src/ext_latency.v +++ b/vclap/ext_latency.v @@ -1,3 +1,5 @@ +module vclap + const clap_ext_latency = unsafe { (&char(C.CLAP_EXT_LATENCY)).vstring() } @[typedef] diff --git a/src/ext_note_ports.v b/vclap/ext_note_ports.v similarity index 97% rename from src/ext_note_ports.v rename to vclap/ext_note_ports.v index ae6b337..3df3bac 100644 --- a/src/ext_note_ports.v +++ b/vclap/ext_note_ports.v @@ -1,3 +1,5 @@ +module vclap + const clap_ext_note_ports = unsafe { (&char(C.CLAP_EXT_NOTE_PORTS)).vstring() } @[typedef] diff --git a/vclap/ext_posix_fd.v b/vclap/ext_posix_fd.v new file mode 100644 index 0000000..4e106d5 --- /dev/null +++ b/vclap/ext_posix_fd.v @@ -0,0 +1,21 @@ +module vclap + +const clap_ext_posix_fd_support = unsafe { (&char(C.CLAP_EXT_POSIX_FD_SUPPORT)).vstring() } + +enum ClapPosixFDFlags as u32 { + read = 1 << 0 + write = 1 << 1 + error = 1 << 2 +} + +@[typedef] +struct C.clap_host_posix_fd_support_t { + register_fd fn (&C.clap_host_t, int, ClapPosixFDFlags) bool + modify_fd fn (&C.clap_host_t, int, ClapPosixFDFlags) bool + unregister_fd fn (&C.clap_host_t, int) bool +} + +@[typedef] +struct C.clap_plugin_posix_fd_support_t { + on_fd fn (&C.clap_plugin_t, int, ClapPosixFDFlags) +} diff --git a/vclap/gui/gui.v b/vclap/gui/gui.v new file mode 100644 index 0000000..1529307 --- /dev/null +++ b/vclap/gui/gui.v @@ -0,0 +1,107 @@ +module gui + +@[heap] +pub struct GUI { + width u32 + height u32 + connection &C.xcb_connection_t + window C.xcb_window_t + gc C.xcb_gcontext_t +pub: + fd int +} + +pub fn GUI.create(width u32, height u32, title string) &GUI { + // Open the connection to the X server. + conn := unsafe { C.xcb_connect(nil, nil) } + if isnil(conn) { + panic('Unable to open XCB connection.') + } + + // Get the first screen. + screen := C.xcb_setup_roots_iterator(C.xcb_get_setup(conn)).data + // Create a window. + window := C.xcb_generate_id(conn) + C.xcb_create_window(conn, C.XCB_COPY_FROM_PARENT, window, screen.root, 0, 0, width, + height, 1, C.XCB_WINDOW_CLASS_INPUT_OUTPUT, screen.root_visual, C.XCB_CW_BACK_PIXEL, + &screen.white_pixel) + + // Select the events the window will receive. + event_mask := C.XCB_EVENT_MASK_EXPOSURE | C.XCB_EVENT_MASK_POINTER_MOTION | C.XCB_EVENT_MASK_BUTTON_PRESS | C.XCB_EVENT_MASK_BUTTON_RELEASE | C.XCB_EVENT_MASK_KEY_PRESS | C.XCB_EVENT_MASK_KEY_RELEASE | C.XCB_EVENT_MASK_ENTER_WINDOW | C.XCB_EVENT_MASK_LEAVE_WINDOW | C.XCB_EVENT_MASK_BUTTON_MOTION | C.XCB_EVENT_MASK_KEYMAP_STATE | C.XCB_EVENT_MASK_FOCUS_CHANGE + C.xcb_change_window_attributes(conn, window, C.XCB_CW_EVENT_MASK, &event_mask) + + // Inform a window manager not to tamper with the window + // Note: doesn't work in standalone. + C.xcb_change_window_attributes(conn, window, C.XCB_CW_OVERRIDE_REDIRECT, &[1]!) + + // Create a graphic context to paint on. + gc := C.xcb_generate_id(conn) + gc_values := [screen.black_pixel, 0]! + C.xcb_create_gc(conn, gc, window, C.XCB_GC_FOREGROUND | C.XCB_GC_GRAPHICS_EXPOSURES, + &gc_values) + + C.xcb_flush(conn) + + return &GUI{ + width: width + height: height + connection: conn + window: window + gc: gc + fd: C.xcb_get_file_descriptor(conn) + } +} + +pub fn (g &GUI) destroy() { + C.xcb_free_gc(g.connection, g.gc) + C.xcb_destroy_window(g.connection, g.window) + C.xcb_disconnect(g.connection) +} + +pub fn (g &GUI) set_parent(parent u32) { + C.xcb_reparent_window(g.connection, g.window, parent, 0, 0) + C.xcb_flush(g.connection) +} + +pub fn (g &GUI) set_visible(visible bool) { + if visible { + C.xcb_configure_window(g.connection, g.window, C.XCB_CONFIG_WINDOW_STACK_MODE, + &[C.XCB_STACK_MODE_ABOVE]!) + C.xcb_map_window(g.connection, g.window) + } else { + C.xcb_unmap_window(g.connection, g.window) + } + C.xcb_flush(g.connection) +} + +fn (g &GUI) paint() { + C.xcb_clear_area(g.connection, 0, g.window, 0, 0, g.width, g.height) + // Parameter "slider". + C.xcb_change_gc(g.connection, g.gc, C.XCB_GC_FOREGROUND, &[0]!) + C.xcb_poly_rectangle(g.connection, g.window, g.gc, 1, &C.xcb_rectangle_t{50, 50, 25, 100}) + C.xcb_poly_fill_rectangle(g.connection, g.window, g.gc, 1, &C.xcb_rectangle_t{50, 50, 25, 40}) +} + +pub fn (g &GUI) on_posix_fd() { + C.xcb_flush(g.connection) + + for { + event := C.xcb_poll_for_event(g.connection) + if isnil(event) { + break + } + t := event.response_type & ~0x80 + match t { + u8(C.XCB_EXPOSE) { + expose_ev := &C.xcb_expose_event_t(event) + if expose_ev.window == g.window { + g.paint() + } + } + else {} + } + + C.xcb_flush(g.connection) + C.free(event) + } +} diff --git a/vclap/gui/xcb.v b/vclap/gui/xcb.v new file mode 100644 index 0000000..24b3e5e --- /dev/null +++ b/vclap/gui/xcb.v @@ -0,0 +1,79 @@ +module gui + +#pkgconfig --libs xcb + +#include + +// XCB library +type C.xcb_window_t = u32 +type C.xcb_gcontext_t = u32 +type C.xcb_visualid_t = u32 +type C.xcb_atom_t = u32 + +@[typedef] +struct C.xcb_connection_t {} + +@[typedef] +struct C.xcb_setup_t {} + +@[typedef] +struct C.xcb_screen_t { + root C.xcb_window_t + root_visual C.xcb_visualid_t + white_pixel u32 + black_pixel u32 +} + +@[typedef] +struct C.xcb_screen_iterator_t { + data &C.xcb_screen_t +} + +@[typedef] +struct C.xcb_gcontext_t {} + +@[typedef] +struct C.xcb_rectangle_t { + x i16 + y i16 + width u16 + height u16 +} + +@[typedef] +struct C.xcb_generic_event_t { + response_type u8 +} + +@[typedef] +struct C.xcb_expose_event_t { + window C.xcb_window_t +} + +fn C.xcb_connect(&char, &int) &C.xcb_connection_t +fn C.xcb_disconnect(&C.xcb_connection_t) +fn C.xcb_flush(&C.xcb_connection_t) + +fn C.xcb_get_setup(&C.xcb_connection_t) &C.xcb_setup_t +fn C.xcb_setup_roots_iterator(&C.xcb_setup_t) C.xcb_screen_iterator_t +fn C.xcb_generate_id(&C.xcb_connection_t) u32 +fn C.xcb_get_file_descriptor(&C.xcb_connection_t) int + +fn C.xcb_create_window(&C.xcb_connection_t, u8, C.xcb_window_t, C.xcb_window_t, i16, i16, u16, u16, u16, u16, C.xcb_visualid_t, u32, voidptr) +fn C.xcb_destroy_window(&C.xcb_connection_t, C.xcb_window_t) +fn C.xcb_configure_window(&C.xcb_connection_t, C.xcb_window_t, u16, voidptr) +fn C.xcb_change_window_attributes(&C.xcb_connection_t, C.xcb_window_t, u32, voidptr) +fn C.xcb_reparent_window(&C.xcb_connection_t, C.xcb_window_t, C.xcb_window_t, i16, i16) +fn C.xcb_map_window(&C.xcb_connection_t, C.xcb_window_t) +fn C.xcb_unmap_window(&C.xcb_connection_t, C.xcb_window_t) + +fn C.xcb_create_gc(&C.xcb_connection_t, C.xcb_gcontext_t, C.xcb_window_t, u32, voidptr) +fn C.xcb_free_gc(&C.xcb_connection_t, C.xcb_gcontext_t) +fn C.xcb_change_gc(&C.xcb_connection_t, C.xcb_gcontext_t, u32, voidptr) + +fn C.xcb_clear_area(&C.xcb_connection_t, u8, C.xcb_window_t, i16, i16, u16, u16) +fn C.xcb_poly_rectangle(&C.xcb_connection_t, C.xcb_window_t, C.xcb_gcontext_t, u32, &C.xcb_rectangle_t) +fn C.xcb_poly_fill_rectangle(&C.xcb_connection_t, C.xcb_window_t, C.xcb_gcontext_t, u32, &C.xcb_rectangle_t) + +fn C.xcb_poll_for_event(&C.xcb_connection_t) &C.xcb_generic_event_t + diff --git a/src/plugin.v b/vclap/plugin.v similarity index 55% rename from src/plugin.v rename to vclap/plugin.v index fd7e320..948d64d 100644 --- a/src/plugin.v +++ b/vclap/plugin.v @@ -1,4 +1,9 @@ -import log +module vclap + +import vclap.gui { GUI } + +const gui_width = 640 +const gui_height = 480 // This should be the actual implementation of the plugin with // all the interesting logic like DSP, UI, etc. @@ -6,8 +11,10 @@ import log struct MinimalPlugin { host &C.clap_host_t mut: - sample_rate f64 - latency u32 + sample_rate f64 + latency u32 + host_posix_fd &C.clap_host_posix_fd_support_t = unsafe { nil } + gui &gui.GUI = unsafe { nil } } // Extract our actual pluging from CLAP plugin wrapper. @@ -15,7 +22,8 @@ fn from_clap(clap_plugin &C.clap_plugin_t) &MinimalPlugin { return unsafe { &MinimalPlugin(clap_plugin.plugin_data) } } -fn (mp MinimalPlugin) init(clap_plugin &C.clap_plugin_t) bool { +fn (mut mp MinimalPlugin) init(clap_plugin &C.clap_plugin_t) bool { + mp.host_posix_fd = mp.host.get_extension(mp.host, clap_ext_posix_fd_support.str) return true } @@ -51,17 +59,17 @@ fn (mp MinimalPlugin) process_event(header &C.clap_event_header_t) { u16(ClapEventType.note_on) { // Handle note playing. event := &C.clap_event_note_t(header) - log.debug('Note ON: ${event.note_id}') + debug('Note ON: ${event.note_id}') } u16(ClapEventType.note_off) { // Handle note stop playing. event := &C.clap_event_note_t(header) - log.debug('Note OFF: ${event.note_id}') + debug('Note OFF: ${event.note_id}') } // And so on... else { t := unsafe { ClapEventType(header.@type) } - log.debug('Unsupported event type: ${t}') + debug('Unsupported event type: ${t}') } } } @@ -119,7 +127,7 @@ fn (mp MinimalPlugin) process(clap_plugin &C.clap_plugin_t, mut process C.clap_p return ClapProcessStatus.@continue } -fn (mp MinimalPlugin) get_extension(clap_plugin &C.clap_plugin_t, id &char) voidptr { +fn (mut mp MinimalPlugin) get_extension(clap_plugin &C.clap_plugin_t, id &char) voidptr { v_id := unsafe { cstring_to_vstring(id) } match v_id { @@ -188,6 +196,100 @@ fn (mp MinimalPlugin) get_extension(clap_plugin &C.clap_plugin_t, id &char) void } } } + clap_ext_gui { + return &C.clap_plugin_gui_t{ + is_api_supported: fn (clap_plugin &C.clap_plugin_t, api &char, is_floating bool) bool { + if is_floating { + return false + } + v_api := unsafe { cstring_to_vstring(api) } + // Only X11 for now. + return v_api == clap_window_api_x11 + } + get_preferred_api: fn (clap_plugin &C.clap_plugin_t, mut api voidptr, mut is_floating &bool) bool { + is_floating = false + api = unsafe { &clap_window_api_x11 } + return true + } + create: fn [mut mp] (clap_plugin &C.clap_plugin_t, api &char, is_floating bool) bool { + v_api := unsafe { cstring_to_vstring(api) } + if v_api != clap_window_api_x11 || is_floating { + return false + } + if !isnil(mp.gui) { + panic('GUI already initialised!') + } + mp.gui = GUI.create(vclap.gui_width, vclap.gui_height, plugin_name) + // Register the file descriptor we'll receive events from. + if !isnil(mp.host_posix_fd) && !isnil(mp.host_posix_fd.register_fd) { + mp.host_posix_fd.register_fd(mp.host, mp.gui.fd, ClapPosixFDFlags.read) + } + + return true + } + destroy: fn [mut mp] (clap_plugin &C.clap_plugin_t) { + if !isnil(mp.host_posix_fd) && !isnil(mp.host_posix_fd.unregister_fd) { + mp.host_posix_fd.unregister_fd(mp.host, mp.gui.fd) + } + if isnil(mp.gui) { + panic('GUI not initialised!') + } + mp.gui.destroy() + + unsafe { + mp.gui = nil + } + } + set_scale: fn (clap_plugin &C.clap_plugin_t, scale f64) bool { + return false + } + get_size: fn (clap_plugin &C.clap_plugin_t, mut width &u32, mut height &u32) bool { + width = vclap.gui_width + height = vclap.gui_height + return true + } + can_resize: fn (clap_plugin &C.clap_plugin_t) bool { + return false + } + get_resize_hints: fn (clap_plugin &C.clap_plugin_t, hints &C.clap_gui_resize_hints_t) bool { + return false + } + adjust_size: fn (clap_plugin &C.clap_plugin_t, mut width &u32, mut height &u32) bool { + width = vclap.gui_width + height = vclap.gui_height + return true + } + set_size: fn (clap_plugin &C.clap_plugin_t, width &u32, height &u32) bool { + return true + } + set_parent: fn [mut mp] (clap_plugin &C.clap_plugin_t, window &C.clap_window_t) bool { + v_wapi := unsafe { (&char(window.api)).vstring() } + assert v_wapi == clap_window_api_x11, 'Bad GUI API' + + mp.gui.set_parent(window.x11) + return true + } + set_transient: fn (clap_plugin &C.clap_plugin_t, window &C.clap_window_t) bool { + return false + } + suggest_title: fn (clap_plugin &C.clap_plugin_t, title &char) {} + show: fn [mp] (clap_plugin &C.clap_plugin_t) bool { + mp.gui.set_visible(true) + return true + } + hide: fn [mp] (clap_plugin &C.clap_plugin_t) bool { + mp.gui.set_visible(false) + return true + } + } + } + clap_ext_posix_fd_support { + return &C.clap_plugin_posix_fd_support_t{ + on_fd: fn [mp] (clap_plugin &C.clap_plugin_t, fd int, flags ClapPosixFDFlags) { + mp.gui.on_posix_fd() + } + } + } else { return unsafe { nil } } diff --git a/src/setup.v b/vclap/setup.v similarity index 73% rename from src/setup.v rename to vclap/setup.v index 9fd3436..048992f 100644 --- a/src/setup.v +++ b/vclap/setup.v @@ -1,34 +1,42 @@ +module vclap + +import v.vmod + +// Only need to change the mod file to update the version. +const manifest = vmod.decode(@VMOD_FILE) or { panic(err) } +const current_version = manifest.version +const plugin_name = 'CLAP V Hello World' + // Features of the CLAP plugin. // Have to be defined separately here, otherwise wrong C is generated. const _plugin_features = [ - c'audio-effect', - c'note-effect', - c'stereo', - unsafe { nil }, + c'audio-effect', + c'note-effect', + c'stereo', + unsafe { nil }, ]! // Plugin information, extracted at load time. const _plugin_id = 'example.hello.world' const _plugin_descriptor = C.clap_plugin_descriptor_t{ - id: _plugin_id.str - name: c'CLAP V Hello World' - vendor: c'MOFOSS' - version: current_version.str - description: c'MVP of a CLAP plugin in V.' - // voidptr is to fix warning about const char**. - features: voidptr(unsafe { - &&char(&_plugin_features[0]) - }) + id: _plugin_id.str + name: plugin_name.str + vendor: c'MOFOSS' + version: current_version.str + description: c'MVP of a CLAP plugin in V.' + // voidptr is to fix warning about const char**. + features: voidptr(unsafe { + &&char(&_plugin_features[0]) + }) } - fn create_plugin(factory &C.clap_plugin_factory_t, host &C.clap_host_t, plugin_id &char) &C.clap_plugin_t { // Sanity checks for lib version and correct plugin expected. if !C.clap_version_is_compatible(host.clap_version) { return unsafe { nil } } v_plugin_id := unsafe { cstring_to_vstring(plugin_id) } - if v_plugin_id != _plugin_id { + if v_plugin_id != vclap._plugin_id { return unsafe { nil } } @@ -38,7 +46,7 @@ fn create_plugin(factory &C.clap_plugin_factory_t, host &C.clap_host_t, plugin_i } // This is the "official" plugin. clap_plugin := &C.clap_plugin_t{ - desc: &_plugin_descriptor + desc: &vclap._plugin_descriptor // It always carries a pointer to our custom structure. plugin_data: main_plugin init: main_plugin.init @@ -66,7 +74,7 @@ fn entry_get_factory(factory_id &char) voidptr { } get_plugin_descriptor: fn (factory &C.clap_plugin_factory_t, index u32) &C.clap_plugin_descriptor_t { if index == 0 { - return &_plugin_descriptor + return &vclap._plugin_descriptor } else { return unsafe { nil } } @@ -79,13 +87,11 @@ fn entry_get_factory(factory_id &char) voidptr { return unsafe { nil } } - -const plugin_entry = C.clap_plugin_entry_t{ +pub const plugin_entry = C.clap_plugin_entry_t{ clap_version: C.clap_version_t{} init: fn (plugin_path &char) bool { return true } deinit: fn () {} - get_factory: entry_get_factory } diff --git a/vclap/utils.v b/vclap/utils.v new file mode 100644 index 0000000..3b71aad --- /dev/null +++ b/vclap/utils.v @@ -0,0 +1,6 @@ +module vclap + +@[if debug] +fn debug(s string) { + eprintln(s) +}