Initial commit
This commit is contained in:
commit
56f7cd1875
105 changed files with 22109 additions and 0 deletions
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
build/
|
||||
.cache/
|
||||
16
.vscode/launch.json
vendored
Normal file
16
.vscode/launch.json
vendored
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
{
|
||||
// Use IntelliSense to learn about possible attributes.
|
||||
// Hover to view descriptions of existing attributes.
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"type": "lldb",
|
||||
"request": "launch",
|
||||
"name": "Launch",
|
||||
"program": "${workspaceFolder}/build/pgpl_test",
|
||||
"args": [],
|
||||
"cwd": "${workspaceFolder}/build"
|
||||
}
|
||||
]
|
||||
}
|
||||
27
include/pgpl.h
Normal file
27
include/pgpl.h
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
#ifndef PGPL_H
|
||||
#define PGPL_H
|
||||
|
||||
#include <pgpl/gui.h>
|
||||
#include <pgpl/render.h>
|
||||
#include <pgpl/thread.h>
|
||||
#include <pgpl/timer.h>
|
||||
#include <pgpl/vector.h>
|
||||
#include <pgpl/window.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* This initalizes certain values inside of the pgpl library, call this before
|
||||
* you call any other pgpl function. */
|
||||
void pgpl_init(void);
|
||||
|
||||
/* This frees any of the resources allocated by pgpl_init. You may call
|
||||
* pgpl_init again after calling this function. */
|
||||
void pgpl_deinit(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
45
include/pgpl/gui.h
Normal file
45
include/pgpl/gui.h
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
#ifndef PGPL_GUI_H
|
||||
#define PGPL_GUI_H
|
||||
|
||||
#include <pgpl/gui/helpers.h>
|
||||
#include <pgpl/gui/predef.h>
|
||||
#include <pgpl/gui/widgets.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* This creates a new GUI instance, the parameters are mostly the same as
|
||||
* creating a window, however you can also specify a theme which will theme the
|
||||
* widgets respectively, and app_data, which can be used in callbacks of
|
||||
* widgets. */
|
||||
PGPL_Gui *pgpl_gui_create(const char *title, uint32_t width, uint32_t height,
|
||||
int32_t x, int32_t y, PGPL_GuiTheme *theme,
|
||||
void *app_data);
|
||||
|
||||
/* Destroys the GUI object, including all of its widgets by calling their
|
||||
* destroy method. The internal boolean must be set to true if this is being
|
||||
* called from inside of the gui event loop.*/
|
||||
void pgpl_gui_destroy(PGPL_Gui *gui, bool internal);
|
||||
|
||||
/* This shows the GUI window. If wait is true, this will block until the window
|
||||
* is closed. */
|
||||
void pgpl_gui_run_and_show(PGPL_Gui *gui, bool wait);
|
||||
|
||||
/* Adds the specified widget to be rendered by the GUI. */
|
||||
void pgpl_gui_widget_add(PGPL_Gui *gui, PGPL_GuiWidget *widget);
|
||||
|
||||
/* Removes the widget at the specified index. */
|
||||
void pgpl_gui_widget_remove(PGPL_Gui *gui, uint32_t index);
|
||||
|
||||
/* Get amount of widgets that the gui contains. */
|
||||
uint32_t pgpl_gui_widget_amount(PGPL_Gui *gui);
|
||||
|
||||
/* Gets the widget at the specified index. */
|
||||
PGPL_GuiWidget *pgpl_gui_widget_get(PGPL_Gui *gui, uint32_t index);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
55
include/pgpl/gui/helpers.h
Normal file
55
include/pgpl/gui/helpers.h
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
#ifndef PGPL_GUI_HELPERS_H
|
||||
#define PGPL_GUI_HELPERS_H
|
||||
|
||||
#include <pgpl/gui/predef.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* This function basically subtracts margin, border and padding from the
|
||||
* max_width and max_height, and then return that through the width and height
|
||||
* pointers. */
|
||||
void pgpl_gui_widget_max_content_size(PGPL_GuiWidget *widget,
|
||||
PGPL_GuiTheme *theme, double *width,
|
||||
double *height, double max_width,
|
||||
double max_height);
|
||||
|
||||
/* This function lets you configure a theme with just a base_color, which in
|
||||
* this case is the brightest color that should be used (text, active), a font
|
||||
* size for the content and a font. This will calculate the other colors,
|
||||
* margin, border, padding and other font sizes based on those values. */
|
||||
void pgpl_gui_theme_configure(PGPL_GuiTheme *theme, PGPL_Color base_color,
|
||||
double content_font_size, PGPL_Font *font);
|
||||
|
||||
/* This will do a full render on a widget, it will account for it's offset, if
|
||||
* it has a border or background, it's margin, border and padding sizes and also
|
||||
* if it expands on the x and y axis. It then calls the widgets render function
|
||||
* at the correct x and y offset. */
|
||||
void pgpl_gui_widget_render_full(PGPL_GuiWidget *widget,
|
||||
PGPL_Renderer *renderer, PGPL_GuiTheme *theme,
|
||||
double x, double y, double max_width,
|
||||
double max_height);
|
||||
|
||||
/* Checks if x and y lie within the visible bounds of a widget, accounting for
|
||||
* margin, border and padding and returns true or false. */
|
||||
bool pgpl_gui_widget_within_bounds(PGPL_GuiWidget *widget, PGPL_GuiTheme *theme,
|
||||
double widget_x, double widget_y,
|
||||
double max_width, double max_height,
|
||||
double x, double y);
|
||||
|
||||
/* This configures all of the widget properties that should be configurable by
|
||||
* the user. Check the PGPL_GuiWidget struct comment for more info. */
|
||||
void pgpl_gui_widget_configure(PGPL_GuiWidget *widget, double offset_x,
|
||||
double offset_y, bool expand_x, bool expand_y,
|
||||
bool border, bool background,
|
||||
PGPL_GuiFontSize font_size,
|
||||
PGPL_GuiTheme *theme_override,
|
||||
bool use_theme_override, bool ignore_margin,
|
||||
bool ignore_border, bool ignore_padding);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
131
include/pgpl/gui/predef.h
Normal file
131
include/pgpl/gui/predef.h
Normal file
|
|
@ -0,0 +1,131 @@
|
|||
#ifndef PGPL_GUI_PREDEF_H
|
||||
#define PGPL_GUI_PREDEF_H
|
||||
|
||||
#include <pgpl/render/predef.h>
|
||||
#include <pgpl/thread.h>
|
||||
#include <pgpl/vector.h>
|
||||
#include <pgpl/window/predef.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Predefinition of some structs. */
|
||||
typedef struct PGPL_GuiWidget PGPL_GuiWidget;
|
||||
typedef struct PGPL_Gui PGPL_Gui;
|
||||
|
||||
/* These are the enum values for the GUI colors. This helps select a different
|
||||
* color from the theme if a widget is currently active, hovered over, or in a
|
||||
* default state. */
|
||||
typedef enum PGPL_GuiStatusColor {
|
||||
PGPL_GUI_STATUS_COLOR_NORMAL,
|
||||
PGPL_GUI_STATUS_COLOR_HOVER,
|
||||
PGPL_GUI_STATUS_COLOR_ACTIVE
|
||||
} PGPL_GuiStatusColor;
|
||||
|
||||
/* This controls what font size from the theme should be used. There are three
|
||||
* different types. */
|
||||
typedef enum PGPL_GuiFontSize {
|
||||
PGPL_GUI_FONT_SIZE_TITLE,
|
||||
PGPL_GUI_FONT_SIZE_HEADING,
|
||||
PGPL_GUI_FONT_SIZE_CONTENT
|
||||
} PGPL_GuiFontSize;
|
||||
|
||||
/* This is the struct for the themes. This controls the colors, font sizes, font
|
||||
* and many more settings that widgets in a GUI render with. You can use the
|
||||
* helper function pgpl_gui_theme_configure to easily configure such a theme
|
||||
* struct. */
|
||||
typedef struct PGPL_GuiTheme {
|
||||
/* Color for the text and borders, array of 3 colors to support all values
|
||||
* from PGPL_GuiStatusColor. */
|
||||
PGPL_Color text_color[3];
|
||||
/* Color for the background of widgets, array of 3 colors to support all
|
||||
* values from PGPL_GuiStatusColor. */
|
||||
PGPL_Color widget_background_color[3];
|
||||
/* Color for the background of a GUI. */
|
||||
PGPL_Color background_color;
|
||||
/* Controls the margin of the widgets. */
|
||||
PGPL_Rectangle margin;
|
||||
/* Controls the border of the widgets. */
|
||||
PGPL_Rectangle border;
|
||||
/* Controls the padding of the widgets. */
|
||||
PGPL_Rectangle padding;
|
||||
/* Controls the font size of the widgets, this is an array of three so that it
|
||||
* can support all values from PGPL_GuiFontSize. */
|
||||
double font_size[3];
|
||||
/* The font of the theme. */
|
||||
PGPL_Font *font;
|
||||
} PGPL_GuiTheme;
|
||||
|
||||
/* This is the struct for the individual widgets. This can be used to create
|
||||
* custom widgets as well. Do note that if you create a custom struct for your
|
||||
* widget, you need to ensure that the first attribute of said struct is of type
|
||||
* PGPL_GuiWidget, so that other containers and the gui itself can call its
|
||||
* methods and read its attributes. */
|
||||
struct PGPL_GuiWidget {
|
||||
/* This should be a unique string, often the name of the struct, so that the
|
||||
* widget type can be identified. */
|
||||
const char *id;
|
||||
/* This is the rendering offset of the widget. */
|
||||
double offset_x, offset_y;
|
||||
/* expand_x and expand_y determine if the widget should expand in the x and y
|
||||
* direction when being rendered. */
|
||||
bool expand_x, expand_y;
|
||||
/* This determines if the border and background of the widget should be
|
||||
* rendered or not. */
|
||||
bool border, background;
|
||||
/* This picks out which font size from the theme to use. */
|
||||
PGPL_GuiFontSize font_size;
|
||||
/* This picks out which color from the theme to use. */
|
||||
PGPL_GuiStatusColor gui_color;
|
||||
/* This is the theme that should be used instead of the main theme. Note that
|
||||
* this theme should propagate to any possible child widgets as well. */
|
||||
PGPL_GuiTheme theme_override;
|
||||
/* This determines if the override theme should even be used. If this is
|
||||
* false, the theme_override is simply ignored. */
|
||||
bool use_theme_override;
|
||||
/* These attributes determine whether or not margin, border and padding sizes
|
||||
* respectively should be ignored when rendering. */
|
||||
bool ignore_margin, ignore_border, ignore_padding;
|
||||
/* This returns the size of the content of a widget. This should NOT include
|
||||
* any margin, padding or border size. */
|
||||
void (*get_content_size)(PGPL_GuiWidget *widget, PGPL_GuiTheme *theme,
|
||||
double *width, double *height, double max_width,
|
||||
double max_height);
|
||||
/* This renders the content of the widget. Note that the margin, border,
|
||||
* padding, background and offset of the widget must be accounted for BEFORE
|
||||
* calling this function. For ease, you can use the
|
||||
* pgpl_gui_widget_render_full function to automatically take care of all of
|
||||
* those things for you. */
|
||||
void (*render_content)(PGPL_GuiWidget *widget, PGPL_Renderer *renderer,
|
||||
PGPL_GuiTheme *theme, double x, double y,
|
||||
double max_width, double max_height);
|
||||
/* This should get called for every event that is not a render or close event.
|
||||
* Note that the x and y values should be called with the widgets offset
|
||||
* pre-included. */
|
||||
void (*event)(PGPL_GuiWidget *widget, PGPL_GuiTheme *theme, PGPL_Gui *gui,
|
||||
PGPL_WindowEventType event_type,
|
||||
PGPL_WindowEventData *event_data, double x, double y,
|
||||
double max_width, double max_height);
|
||||
/* This should destroy the widget, freeing all of its resources and also call
|
||||
* the destroy method on all of its potential child widgets. */
|
||||
void (*destroy)(PGPL_GuiWidget *widget);
|
||||
};
|
||||
|
||||
/* This is the struct for the gui itself. Do not modify it's mutex and the
|
||||
* widgets vector. The app_data value is used for event callbacks. */
|
||||
struct PGPL_Gui {
|
||||
PGPL_WindowThread *window_thread;
|
||||
PGPL_GuiTheme theme;
|
||||
PGPL_Vector *widgets;
|
||||
void *app_data;
|
||||
PGPL_Mutex *mutex;
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
96
include/pgpl/gui/widgets.h
Normal file
96
include/pgpl/gui/widgets.h
Normal file
|
|
@ -0,0 +1,96 @@
|
|||
#ifndef PGPL_GUI_WIDGETS_H
|
||||
#define PGPL_GUI_WIDGETS_H
|
||||
|
||||
#include <pgpl/gui/predef.h>
|
||||
#include <pgpl/thread.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* These are the two directions a PGPL_GuiContainerLayout can place widgets in.
|
||||
* You can combine multiple of these to recreate a grid layout. */
|
||||
typedef enum PGPL_GuiContainerLayoutDirection {
|
||||
PGPL_GUI_CONTAINER_LAYOUT_DIRECTION_VERTICAL,
|
||||
PGPL_GUI_CONTAINER_LAYOUT_DIRECTION_HORIZONTAL
|
||||
} PGPL_GuiContainerLayoutDirection;
|
||||
|
||||
/* This is the PGPL_GuiContainerLayout, it places it's widgets along a single
|
||||
* direction, see PGPL_GuiContainerLayoutDirection for more details on that. Do
|
||||
* not modify the mutex and widgets vector in this struct. */
|
||||
typedef struct PGPL_GuiContainerLayout {
|
||||
PGPL_GuiWidget parent;
|
||||
PGPL_Vector *widgets;
|
||||
PGPL_GuiContainerLayoutDirection direction;
|
||||
PGPL_Mutex *mutex;
|
||||
} PGPL_GuiContainerLayout;
|
||||
|
||||
/* This creates a new container layout instance with the specified direction. */
|
||||
PGPL_GuiContainerLayout *
|
||||
pgpl_gui_container_layout_create(PGPL_GuiContainerLayoutDirection direction);
|
||||
|
||||
/* This adds a widget to a container layout. */
|
||||
void pgpl_gui_container_layout_widget_add(
|
||||
PGPL_GuiContainerLayout *container_layout, PGPL_GuiWidget *widget);
|
||||
|
||||
/* This returns a writeable pointer to the widget at the specified index in the
|
||||
* container_layout. */
|
||||
PGPL_GuiWidget *
|
||||
pgpl_gui_container_layout_widget_get(PGPL_GuiContainerLayout *container_layout,
|
||||
uint32_t index);
|
||||
|
||||
/* This deletes the widget at the specified index from the container layout. */
|
||||
void pgpl_gui_container_layout_widget_delete(
|
||||
PGPL_GuiContainerLayout *container_layout, uint32_t index);
|
||||
|
||||
/* This returns the amount of widgets in a container layout. */
|
||||
uint32_t pgpl_gui_container_layout_widget_amount(
|
||||
PGPL_GuiContainerLayout *container_layout);
|
||||
|
||||
/* This destroys a container layout, and calls the destroy method on all of it's
|
||||
* child widgets. */
|
||||
void pgpl_gui_container_layout_destroy(
|
||||
PGPL_GuiContainerLayout *container_layout);
|
||||
|
||||
/* The struct for a PGPL_GuiTextWidget, the text variable is the string to be
|
||||
* displayed. */
|
||||
typedef struct PGPL_GuiTextWidget {
|
||||
PGPL_GuiWidget parent;
|
||||
const char *text;
|
||||
} PGPL_GuiTextWidget;
|
||||
|
||||
/* Creates a new text widget. */
|
||||
PGPL_GuiTextWidget *pgpl_gui_text_widget_create(const char *text);
|
||||
|
||||
/* Destroys a text widget. */
|
||||
void pgpl_gui_text_widget_destroy(PGPL_GuiTextWidget *text_widget);
|
||||
|
||||
/* The struct for a PGPL_GuiButtonWidget, the text variable is the string to be
|
||||
* displayed. The click_callback variable gets called with the gui instances
|
||||
* app_data parameter. */
|
||||
typedef struct PGPL_GuiButtonWidget {
|
||||
PGPL_GuiWidget parent;
|
||||
const char *text;
|
||||
void (*click_callback)(void *app_data);
|
||||
} PGPL_GuiButtonWidget;
|
||||
|
||||
/* Creates a new button widget. */
|
||||
PGPL_GuiButtonWidget *
|
||||
pgpl_gui_button_widget_create(const char *text,
|
||||
void (*click_callback)(void *app_data));
|
||||
|
||||
/* Destroys a button widget. */
|
||||
void pgpl_gui_button_widget_destroy(PGPL_GuiButtonWidget *button_widget);
|
||||
|
||||
/* Creates a new empty widget. This is very useful to create spacers for
|
||||
* container layouts. */
|
||||
PGPL_GuiWidget *pgpl_gui_empty_widget_create(void);
|
||||
|
||||
/* Destroys a empty widget. */
|
||||
void pgpl_gui_empty_widget_destroy(PGPL_GuiWidget *empty_widget);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
8
include/pgpl/render.h
Normal file
8
include/pgpl/render.h
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
#ifndef PGPL_RENDER_H
|
||||
#define PGPL_RENDER_H
|
||||
|
||||
#include <pgpl/render/font.h>
|
||||
#include <pgpl/render/shapes.h>
|
||||
#include <pgpl/render/texture.h>
|
||||
|
||||
#endif
|
||||
56
include/pgpl/render/color.h
Normal file
56
include/pgpl/render/color.h
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
#ifndef PGPL_RENDER_COLOR_H
|
||||
#define PGPL_RENDER_COLOR_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* PGPL_Color type. This is simply a uint32_t. It contains an RGBA value:
|
||||
* 0xFF000000 -> Red
|
||||
* 0x00FF0000 -> Green
|
||||
* 0x0000FF00 -> Blue
|
||||
* 0x000000FF -> Alpha */
|
||||
typedef uint32_t PGPL_Color;
|
||||
|
||||
/* Creates a new PGPL_Color with the given parameters. */
|
||||
static inline PGPL_Color pgpl_color_create(uint8_t red, uint8_t green,
|
||||
uint8_t blue, uint8_t alpha) {
|
||||
return ((uint32_t)red << 24) + ((uint32_t)green << 16) +
|
||||
((uint32_t)blue << 8) + (uint32_t)alpha;
|
||||
}
|
||||
|
||||
/* Gets the red value of the PGPL_Color type. */
|
||||
static inline uint8_t pgpl_color_get_red(PGPL_Color color) {
|
||||
return (color >> 24) & 0xFF;
|
||||
}
|
||||
|
||||
/* Gets the green value of the PGPL_Color type. */
|
||||
static inline uint8_t pgpl_color_get_green(PGPL_Color color) {
|
||||
return (color >> 16) & 0xFF;
|
||||
}
|
||||
|
||||
/* Gets the blue value of the PGPL_Color type. */
|
||||
static inline uint8_t pgpl_color_get_blue(PGPL_Color color) {
|
||||
return (color >> 8) & 0xFF;
|
||||
}
|
||||
|
||||
/* Gets the alpha value of the PGPL_Color type. */
|
||||
static inline uint8_t pgpl_color_get_alpha(PGPL_Color color) {
|
||||
return color & 0xFF;
|
||||
}
|
||||
|
||||
/* Divides all color values by the given amount. */
|
||||
static inline PGPL_Color pgpl_color_divide(PGPL_Color color, double divisor) {
|
||||
return pgpl_color_create(pgpl_color_get_red(color) / divisor,
|
||||
pgpl_color_get_green(color) / divisor,
|
||||
pgpl_color_get_blue(color) / divisor,
|
||||
pgpl_color_get_alpha(color));
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
69
include/pgpl/render/font.h
Normal file
69
include/pgpl/render/font.h
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
#ifndef PGPL_RENDER_FONT_H
|
||||
#define PGPL_RENDER_FONT_H
|
||||
|
||||
#include <pgpl/render/predef.h>
|
||||
#include <wchar.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* This creates a new font from a .ttf file. See pgpl_font_create for more
|
||||
* info. */
|
||||
PGPL_Font *pgpl_font_create_from_file(const char *filename,
|
||||
uint32_t glyph_size);
|
||||
|
||||
/* This bakes a font from a .ttf file which is located somewhere in memory. The
|
||||
* glyph_size parameter lets you modify the resolution of the baked glyphs. It
|
||||
* can still be rendered at any size. Note that the data passed here will be
|
||||
* freed automatically when the font gets destroyed, so do not attempt to free
|
||||
* it yourself. */
|
||||
PGPL_Font *pgpl_font_create(uint8_t *data, uint32_t glyph_size);
|
||||
|
||||
/* This frees all the resources associated with a font. It can also be with the
|
||||
* renderer being NULL, this however does not delete the OpenGL texture that was
|
||||
* created (it still gets deleted upon window destruction). Only use this if the
|
||||
* window that the font was baked with no longer exists. */
|
||||
void pgpl_font_destroy(PGPL_Renderer *renderer, PGPL_Font *font);
|
||||
|
||||
/* This renders a single glyph on the screen. The c parameter can hold any
|
||||
* Unicode character value. The values in the xoff and yoff parameters will be
|
||||
* offset to where the next character should be rendered. The color modulates
|
||||
* the color of the font texture. */
|
||||
void pgpl_font_render_glyph(PGPL_Renderer *renderer, PGPL_Font *font, wchar_t c,
|
||||
PGPL_Color color, double x, double y, double size,
|
||||
double *xoff, double *yoff);
|
||||
|
||||
/* This renders a whole UTF-8 string at a given position. Its parameters work
|
||||
* similarily to pgpl_render_glyph. */
|
||||
void pgpl_font_render_string(PGPL_Renderer *renderer, PGPL_Font *font,
|
||||
const char *str, PGPL_Color color, double x,
|
||||
double y, double size);
|
||||
|
||||
/* Works like pgpl_font_render_string, but takes a wchar_t string instead. */
|
||||
void pgpl_font_render_wstring(PGPL_Renderer *renderer, PGPL_Font *font,
|
||||
wchar_t *str, PGPL_Color color, double x,
|
||||
double y, double size);
|
||||
|
||||
/* This calculates the dimensions of a glyph. in the top and left parameters it
|
||||
* puts the coordinates of the top-left corner, and into the bottom and right
|
||||
* coordinates those of the bottom-left corner respectively. */
|
||||
void pgpl_font_glyph_dimensions(PGPL_Font *font, wchar_t c, double size,
|
||||
double *xoff, double *yoff,
|
||||
PGPL_Rectangle *rectangle);
|
||||
|
||||
/* Works similarily to pgpl_font_glyph_dimensions, however this calculates those
|
||||
* values for an entire UTF-8 string. */
|
||||
void pgpl_font_string_dimensions(PGPL_Font *font, const char *str, double size,
|
||||
PGPL_Rectangle *rectangle);
|
||||
|
||||
/* Works like pgpl_font_string_dimensions, but takes a wchar_t string instead.
|
||||
*/
|
||||
void pgpl_font_wstring_dimensions(PGPL_Font *font, wchar_t *str, double size,
|
||||
PGPL_Rectangle *rectangle);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
37
include/pgpl/render/predef.h
Normal file
37
include/pgpl/render/predef.h
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
#ifndef PGPL_RENDER_PREDEF_H
|
||||
#define PGPL_RENDER_PREDEF_H
|
||||
|
||||
#include <pgpl/render/color.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* PGPL_Font type. Contains all the data needed to render glyphs on the window.
|
||||
* None of its members should be accessed directly. */
|
||||
typedef struct PGPL_Font PGPL_Font;
|
||||
|
||||
/* PGPL_Rectangle type. */
|
||||
typedef struct PGPL_Rectangle {
|
||||
double top;
|
||||
double left;
|
||||
double bottom;
|
||||
double right;
|
||||
} PGPL_Rectangle;
|
||||
|
||||
/* PGPL_Renderer type. Contains all necessary information for rendering. None of
|
||||
* its members should be accessed directly. */
|
||||
typedef struct PGPL_Renderer {
|
||||
uint32_t width, height;
|
||||
} PGPL_Renderer;
|
||||
|
||||
/* PGPL_Texture type. Contains a texture that can be rendered. None of its
|
||||
* members should be accessed directly. */
|
||||
typedef struct PGPL_Texture PGPL_Texture;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
39
include/pgpl/render/shapes.h
Normal file
39
include/pgpl/render/shapes.h
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
#ifndef PGPL_RENDER_SHAPES_H
|
||||
#define PGPL_RENDER_SHAPES_H
|
||||
|
||||
#include <pgpl/render/predef.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Renders a basic rectangle with the given parameters. */
|
||||
void pgpl_render_rectangle(PGPL_Renderer *renderer, PGPL_Color color, double x,
|
||||
double y, double width, double height);
|
||||
|
||||
/* Renders a basic line with the given parameters. Note that this doesn't work
|
||||
* that well if the scale is not at 1.0. */
|
||||
void pgpl_render_line(PGPL_Renderer *renderer, PGPL_Color color, double x0,
|
||||
double y0, double x1, double y1);
|
||||
|
||||
/* Renders a basic circle at the given coordinates with the given radius. The
|
||||
* triangle count defines how many triangles should be rendered, the higher, the
|
||||
* more accurate the circle will look. */
|
||||
void pgpl_render_circle(PGPL_Renderer *renderer, PGPL_Color color, double x,
|
||||
double y, double radius, double triangle_amount);
|
||||
|
||||
/* Renders a single dot at the given coordinates. Note that this doesn't work
|
||||
* that well if the scale is not at 1.0. */
|
||||
void pgpl_render_point(PGPL_Renderer *renderer, PGPL_Color color, double x,
|
||||
double y);
|
||||
|
||||
/* Renders a basic triangle with the given parameters. */
|
||||
void pgpl_render_triangle(PGPL_Renderer *renderer, PGPL_Color color, double ax,
|
||||
double ay, double bx, double by, double cx,
|
||||
double cy);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
73
include/pgpl/render/texture.h
Normal file
73
include/pgpl/render/texture.h
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
#ifndef PGPL_RENDER_TEXTURE_H
|
||||
#define PGPL_RENDER_TEXTURE_H
|
||||
|
||||
#include <pgpl/render/predef.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* This enum contains all possible texture filtering methods that textures can
|
||||
* be created with. */
|
||||
typedef enum PGPL_TextureFilter {
|
||||
PGPL_TEXTURE_FILTER_LINEAR,
|
||||
PGPL_TEXTURE_FILTER_NEAREST
|
||||
} PGPL_TextureFilter;
|
||||
|
||||
/* This enum contains all possible texture formats that textures can be created
|
||||
* from. */
|
||||
typedef enum PGPL_TextureFormat {
|
||||
PGPL_TEXTURE_FORMAT_RGBA,
|
||||
PGPL_TEXTURE_FORMAT_RGB,
|
||||
PGPL_TEXTURE_FORMAT_BW
|
||||
} PGPL_TextureFormat;
|
||||
|
||||
/* This creates a texture from an image file stored in memory. The file_data is
|
||||
* a pointer to it, and len is the lenght of the file_data. See
|
||||
* pgpl_render_create_texture_file for more info. */
|
||||
PGPL_Texture *pgpl_render_create_texture_file_memory(PGPL_Renderer *renderer,
|
||||
const uint8_t *file_data,
|
||||
uint32_t len,
|
||||
PGPL_TextureFilter filter);
|
||||
|
||||
/* This creates a texture from a file. This supports anything that stb_image
|
||||
* supports. You can also choose the filtering method to be used. */
|
||||
PGPL_Texture *pgpl_render_create_texture_file(PGPL_Renderer *renderer,
|
||||
const char *filename,
|
||||
PGPL_TextureFilter filter);
|
||||
|
||||
/* This creates a texture from raw pixel data. The width and height are used to
|
||||
* determine the size of the data. Note that RGB images are padded to 4 bytes
|
||||
* per pixel. */
|
||||
PGPL_Texture *pgpl_render_create_texture(PGPL_Renderer *renderer,
|
||||
const uint8_t *data, uint32_t width,
|
||||
uint32_t height,
|
||||
PGPL_TextureFilter filter,
|
||||
PGPL_TextureFormat format);
|
||||
|
||||
/* This frees any resources associated with a texture. Note that you can call
|
||||
* this with renderer being NULL, this however doesn't get rid of any OpenGL
|
||||
* data (it still gets deleted upon window destruction). Only call this with the
|
||||
* renderer as NULL if the window it was created on is already destroyed. */
|
||||
void pgpl_render_destroy_texture(PGPL_Renderer *renderer,
|
||||
PGPL_Texture *texture);
|
||||
|
||||
/* This renders a texture at the given position and with the given size. Note
|
||||
* that you can ONLY render images on the window they were created on. */
|
||||
void pgpl_render_texture(PGPL_Renderer *renderer, PGPL_Texture *texture,
|
||||
double x, double y, double width, double height);
|
||||
|
||||
/* This renders a texture with extended settings. The color parameter allows you
|
||||
* to modulate the color of the texture, the s0, s1, t0 and t1 parameters let
|
||||
* you specify coordinates on a given texture, so that you only render a part of
|
||||
* the texture. See pgpl_render_texture for more info. */
|
||||
void pgpl_render_texture_extended(PGPL_Renderer *renderer,
|
||||
PGPL_Texture *texture, double x, double y,
|
||||
double width, double height, PGPL_Color color,
|
||||
double s0, double t0, double s1, double t1);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
46
include/pgpl/thread.h
Normal file
46
include/pgpl/thread.h
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
#ifndef PGPL_THREAD_H
|
||||
#define PGPL_THREAD_H
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* These are the thread and mutex abstraction structs, do not try to modify
|
||||
* their contents. */
|
||||
typedef struct PGPL_Thread PGPL_Thread;
|
||||
typedef struct PGPL_Mutex PGPL_Mutex;
|
||||
|
||||
/* This creates a new thread, which executes the given function with the given
|
||||
* argument. */
|
||||
PGPL_Thread *pgpl_thread_create(void *(*function)(void *arg), void *arg);
|
||||
|
||||
/* This destroys a thread. This doesn't actually stop the execution of the
|
||||
* thread however, it simply just frees the data. */
|
||||
void pgpl_thread_destroy(PGPL_Thread *thread);
|
||||
|
||||
/* This blocks until the thread finishes executing. */
|
||||
void pgpl_thread_join(PGPL_Thread *thread);
|
||||
|
||||
/* This creates a new mutex. */
|
||||
PGPL_Mutex *pgpl_mutex_create(void);
|
||||
|
||||
/* This destroys a mutex. */
|
||||
void pgpl_mutex_destroy(PGPL_Mutex *mutex);
|
||||
|
||||
/* This locks a mutex, and blocks until said mutex can be locked. */
|
||||
void pgpl_mutex_lock(PGPL_Mutex *mutex);
|
||||
|
||||
/* This attempts to lock a mutex. If it fails, this will return false, else it
|
||||
* will return true. */
|
||||
bool pgpl_mutex_try_lock(PGPL_Mutex *mutex);
|
||||
|
||||
/* This will unlock a mutex. */
|
||||
void pgpl_mutex_unlock(PGPL_Mutex *mutex);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
33
include/pgpl/timer.h
Normal file
33
include/pgpl/timer.h
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
#ifndef PGPL_TIMER_H
|
||||
#define PGPL_TIMER_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* PGPL_Timer was never designed with accuracy in mind. It just serves as an
|
||||
* easy way to prevent busy wait loops in a cross-platform manner. */
|
||||
|
||||
/* The PGPL_Timer type contains all info needed by the pgpl_timer functions. */
|
||||
typedef struct PGPL_Timer PGPL_Timer;
|
||||
|
||||
/* This creates a new PGPL_Timer. */
|
||||
PGPL_Timer *pgpl_timer_create(void);
|
||||
|
||||
/* This gets the time in milliseconds since the last time it was measured. */
|
||||
uint32_t pgpl_timer_get_delta(PGPL_Timer *timer);
|
||||
|
||||
/* This destroys a PGPL_Timer */
|
||||
void pgpl_timer_destroy(PGPL_Timer *timer);
|
||||
|
||||
/* This sleeps for the given amount of milliseconds. This function is not
|
||||
* guaranteed to be 100% accurate. */
|
||||
void pgpl_timer_sleep(uint32_t milliseconds);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
68
include/pgpl/vector.h
Normal file
68
include/pgpl/vector.h
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
#ifndef PGPL_VECTOR_H
|
||||
#define PGPL_VECTOR_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* PGPL_Vector is NOT thread safe. It can be used from multiple threads, but the
|
||||
* synchronization is up to the user. */
|
||||
|
||||
/* Contents of the PGPL_Vector struct should not be accessed directly. */
|
||||
typedef struct PGPL_Vector PGPL_Vector;
|
||||
|
||||
/* Creates a new Vector which can then store data with the given data_size. */
|
||||
PGPL_Vector *pgpl_vector_create(uint32_t data_size);
|
||||
|
||||
/* This destroys a vector created by pgpl_vector_create. */
|
||||
void pgpl_vector_destroy(PGPL_Vector *vector);
|
||||
|
||||
/* Pushes data of the vectors data_size onto the back of the vector. */
|
||||
void pgpl_vector_push_back(PGPL_Vector *vector, void *data);
|
||||
|
||||
/* Pushes data of the vectors data_size onto the front of the vector. This is
|
||||
* slower than pushing it onto the back. */
|
||||
void pgpl_vector_push_front(PGPL_Vector *vector, void *data);
|
||||
|
||||
/* Pops data of the vectors data_size from the back of the vector. Do not free
|
||||
* the returned pointer. The memory becomes invalid after running another pop or
|
||||
* delete on the array. */
|
||||
void *pgpl_vector_pop_back(PGPL_Vector *vector);
|
||||
|
||||
/* Pops data of the vectors data_size from the back of the vector. Do not free
|
||||
* the returned pointer. Slower than poping from the back. The memory becomes
|
||||
* invalid after running another pop or delete on the array. */
|
||||
void *pgpl_vector_pop_front(PGPL_Vector *vector);
|
||||
|
||||
/* Returns the top entry on the back of the vector. The returned pointer points
|
||||
* to data inside of the vector, and may be used to modify the entry's content.
|
||||
* Do not free the returned pointer */
|
||||
void *pgpl_vector_peek_back(PGPL_Vector *vector);
|
||||
|
||||
/* Returns the top entry on the front of the vector. The returned pointer points
|
||||
* to data inside of the vector, and may be used to modify the entry's content.
|
||||
* Do not free the returned pointer */
|
||||
void *pgpl_vector_peek_front(PGPL_Vector *vector);
|
||||
|
||||
/* Deletes an index from the vector. The returned pointer should not be freed.
|
||||
* The memory is invalid after running another pop or delete on the array. */
|
||||
void *pgpl_vector_delete_index(PGPL_Vector *vector, uint32_t index);
|
||||
|
||||
/* Inserts data of the vectors data_size after the given index. */
|
||||
void pgpl_vector_insert_after_index(PGPL_Vector *vector, uint32_t index,
|
||||
void *data);
|
||||
|
||||
/* Returns a pointer to the data at the given index. You may modify the contents
|
||||
* of this memory, and this will reflect in the vector itself. */
|
||||
void *pgpl_vector_get_index(PGPL_Vector *vector, uint32_t index);
|
||||
|
||||
/* Returns the length of the vector. */
|
||||
uint32_t pgpl_vector_get_length(PGPL_Vector *vector);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
114
include/pgpl/window.h
Normal file
114
include/pgpl/window.h
Normal file
|
|
@ -0,0 +1,114 @@
|
|||
#ifndef PGPL_WINDOW_H
|
||||
#define PGPL_WINDOW_H
|
||||
|
||||
#include <pgpl/render.h>
|
||||
#include <pgpl/window/predef.h>
|
||||
#include <pgpl/window/thread.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Mouse button event enum. Goes from 1-5. */
|
||||
typedef enum PGPL_WindowMouseButton {
|
||||
PGPL_WINDOW_MOUSE_BUTTON_LEFT = 1,
|
||||
PGPL_WINDOW_MOUSE_BUTTON_MIDDLE,
|
||||
PGPL_WINDOW_MOUSE_BUTTON_RIGHT,
|
||||
PGPL_WINDOW_MOUSE_BUTTON_SCROLL_UP,
|
||||
PGPL_WINDOW_MOUSE_BUTTON_SCROLL_DOWN
|
||||
} PGPL_WindowMouseButton;
|
||||
|
||||
/* Creates a new Window with the given parameters. Do not call free on the
|
||||
* returned object manually. */
|
||||
PGPL_Window *pgpl_window_create(const char *title, uint32_t width,
|
||||
uint32_t height, int32_t x, int32_t y);
|
||||
|
||||
/* Destroys the window and frees all its resources. Does not free rendering
|
||||
* objects. */
|
||||
void pgpl_window_destroy(PGPL_Window *window);
|
||||
|
||||
/* Makes the window visible. */
|
||||
void pgpl_window_show(PGPL_Window *window);
|
||||
|
||||
/* Makes the window invisible. */
|
||||
void pgpl_window_hide(PGPL_Window *window);
|
||||
|
||||
/* Sets a new title for the window. */
|
||||
void pgpl_window_set_title(PGPL_Window *window, const char *title);
|
||||
|
||||
/* Returns the current title of the window. Note that the returned pointer is
|
||||
* only valid during the windows existance. Changing the title may also change
|
||||
* what the returned string points to. Do not call free on the
|
||||
* returned object manually. */
|
||||
const char *pgpl_window_get_title(PGPL_Window *window);
|
||||
|
||||
/* Returns the windows size in the passed pointers. The width and height
|
||||
* pointers are allowed to be NULL. */
|
||||
void pgpl_window_get_size(PGPL_Window *window, uint32_t *width,
|
||||
uint32_t *height);
|
||||
|
||||
/* Changes the windows size to the passed width and height */
|
||||
void pgpl_window_set_size(PGPL_Window *window, uint32_t width, uint32_t height);
|
||||
|
||||
/* Returns the windows position in the passed pointers. The x and y pointers are
|
||||
* allowed to be NULL. */
|
||||
void pgpl_window_get_position(PGPL_Window *window, int32_t *x, int32_t *y);
|
||||
|
||||
/* Changes the windows position to the passed x and y */
|
||||
void pgpl_window_set_position(PGPL_Window *window, int32_t x, int32_t y);
|
||||
|
||||
/* Sets the rendering scale of the window. */
|
||||
void pgpl_window_set_scale(PGPL_Window *window, double scale);
|
||||
|
||||
/* Gets the rendering scale of the window. */
|
||||
double pgpl_window_get_scale(PGPL_Window *window);
|
||||
|
||||
/* This makes the window always display above the parent window. On Windows this
|
||||
* reparents the window without removing it's borders, so that it still looks
|
||||
* like a window. On X11 this works just like the respective X11 function. The
|
||||
* parent pointer must either be of type HWND (on Windows) or of type Window *
|
||||
* (on X11). */
|
||||
void pgpl_window_set_transient(PGPL_Window *window, void *parent);
|
||||
|
||||
/* This reparents the window to the parent window. On Windows this also removes
|
||||
* the childs windows borders. The parent pointer must either be of type HWND
|
||||
* (on Windows) or of type Window * (on X11). */
|
||||
void pgpl_window_reparent_to(PGPL_Window *window, void *parent);
|
||||
|
||||
/* This gets you the raw Window handle. This will be of type HWND (on Windows)
|
||||
* or of type Window * (on X11). */
|
||||
void *pgpl_window_get_raw_window(PGPL_Window *window);
|
||||
|
||||
/* This fetches all events from the window into an internal queue, and returns
|
||||
* back data of the first event found. */
|
||||
PGPL_WindowEventType pgpl_window_fetch_event(PGPL_Window *window,
|
||||
PGPL_WindowEventData *event_data);
|
||||
|
||||
/* This creates a new window event and puts it into the internal queue. */
|
||||
void pgpl_window_create_event(PGPL_Window *window,
|
||||
PGPL_WindowEventType event_type,
|
||||
PGPL_WindowEventData *event_data);
|
||||
|
||||
/* This starts a render on a window, and clears it with the given color. Within
|
||||
* any given program, you can only have one renderer at a time because of
|
||||
* OpenGL. Any new calls to this function will block until the rendering is
|
||||
* stopped.
|
||||
*
|
||||
* It is recommended to only call this at the beginning of a render event
|
||||
* on a window, and then finish it at the end of a render event. It is however
|
||||
* allowed to start a render at any time.
|
||||
*
|
||||
* If you need to clean up any rendering resources upon program exit, you are
|
||||
* allowed to do so without starting a new render. All OpenGL objects are freed
|
||||
* upon window destruction. */
|
||||
PGPL_Renderer *pgpl_window_start_render(PGPL_Window *window, PGPL_Color color);
|
||||
|
||||
/* This finishes a render, and displays the rendered buffer on the window. See
|
||||
* the pgpl_window_start_render function for more information. */
|
||||
void pgpl_window_finish_render(PGPL_Window *window, PGPL_Renderer *renderer);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
50
include/pgpl/window/predef.h
Normal file
50
include/pgpl/window/predef.h
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
#ifndef PGPL_WINDOW_PREDEF_H
|
||||
#define PGPL_WINDOW_PREDEF_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* PGPL_WindowThread type. Contains all the data that is needed by the thread
|
||||
* loop and a PGPL_Window. None of its members should be accessed directly. */
|
||||
typedef struct PGPL_WindowThread PGPL_WindowThread;
|
||||
|
||||
/* PGPL_Window type. This contains all of the data associated with a window.
|
||||
* None of its members should be accessed directly. */
|
||||
typedef struct PGPL_Window PGPL_Window;
|
||||
|
||||
/* This is an input event which is returned on PGPL_WINDOW_EVENT_KEY_* and
|
||||
* PGPL_WINDOW_EVENT_MOUSE_* events. */
|
||||
typedef struct PGPL_WindowInputEvent {
|
||||
uint32_t key;
|
||||
int32_t x;
|
||||
int32_t y;
|
||||
} PGPL_WindowInputEvent;
|
||||
|
||||
/* This is the event data type, it allows you to access extended information for
|
||||
* certain window events. */
|
||||
typedef union PGPL_WindowEventData {
|
||||
PGPL_WindowInputEvent input_event;
|
||||
} PGPL_WindowEventData;
|
||||
|
||||
/* This is an enum containing all kinds of window events. Further details can be
|
||||
* read from the PGPL_WindowEventData type. */
|
||||
typedef enum PGPL_WindowEventType {
|
||||
PGPL_WINDOW_EVENT_NONE,
|
||||
PGPL_WINDOW_EVENT_RENDER_REQUEST,
|
||||
PGPL_WINDOW_EVENT_KEY_PRESS,
|
||||
PGPL_WINDOW_EVENT_KEY_RELEASE,
|
||||
PGPL_WINDOW_EVENT_MOUSE_PRESS,
|
||||
PGPL_WINDOW_EVENT_MOUSE_RELEASE,
|
||||
PGPL_WINDOW_EVENT_MOUSE_MOVE,
|
||||
PGPL_WINDOW_EVENT_CLOSE,
|
||||
PGPL_WINDOW_EVENT_ERROR
|
||||
} PGPL_WindowEventType;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
104
include/pgpl/window/thread.h
Normal file
104
include/pgpl/window/thread.h
Normal file
|
|
@ -0,0 +1,104 @@
|
|||
#ifndef PGPL_WINDOW_THREAD_H
|
||||
#define PGPL_WINDOW_THREAD_H
|
||||
|
||||
#include <pgpl/window/predef.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* This creates a new thread loop, in which a window is also created. The first
|
||||
* five parameters are shared with the non threaded counterpart. The event_loop
|
||||
* function is called whenever there is a window event. The user_data parameter
|
||||
* can be any custom pointer to anything that might be needed inside of the
|
||||
* event_loop. If the user_data is not needed it can also be NULL.
|
||||
*
|
||||
* Returning false from the event_loop will close the window.
|
||||
*/
|
||||
PGPL_WindowThread *pgpl_window_thread_create(
|
||||
const char *title, uint32_t width, uint32_t height, int32_t x, int32_t y,
|
||||
bool (*event_loop)(PGPL_Window *window, PGPL_WindowEventType event,
|
||||
PGPL_WindowEventData *event_data, void *user_data),
|
||||
void *user_data);
|
||||
|
||||
/* This exits the thread loop that is running in the background. It also frees
|
||||
* all resources that were created for this thread, including the Window that
|
||||
* was created in the thread. */
|
||||
void pgpl_window_thread_destroy(PGPL_WindowThread *window_thread);
|
||||
|
||||
/* Threaded version of pgpl_window_show, see pgpl_window_show for more
|
||||
* information. */
|
||||
void pgpl_window_thread_show(PGPL_WindowThread *window_thread);
|
||||
|
||||
/* Threaded version of pgpl_window_hide, see pgpl_window_hide for more
|
||||
* information. */
|
||||
void pgpl_window_thread_hide(PGPL_WindowThread *window_thread);
|
||||
|
||||
/* Threaded version of pgpl_window_set_title, see pgpl_window_set_title for more
|
||||
* information. */
|
||||
void pgpl_window_thread_set_title(PGPL_WindowThread *window_thread,
|
||||
const char *title);
|
||||
|
||||
/* Threaded version of pgpl_window_get_title, see pgpl_window_get_title for more
|
||||
* information. */
|
||||
const char *pgpl_window_thread_get_title(PGPL_WindowThread *window_thread);
|
||||
|
||||
/* Threaded version of pgpl_window_get_size, see pgpl_window_get_size for more
|
||||
* information. */
|
||||
void pgpl_window_thread_get_size(PGPL_WindowThread *window_thread,
|
||||
uint32_t *width, uint32_t *height);
|
||||
|
||||
/* Threaded version of pgpl_window_set_size, see pgpl_window_set_size for more
|
||||
* information. */
|
||||
void pgpl_window_thread_set_size(PGPL_WindowThread *window_thread,
|
||||
uint32_t width, uint32_t height);
|
||||
|
||||
/* Threaded version of pgpl_window_get_position, see pgpl_window_get_position
|
||||
* for more information. */
|
||||
void pgpl_window_thread_get_position(PGPL_WindowThread *window_thread,
|
||||
int32_t *x, int32_t *y);
|
||||
|
||||
/* Threaded version of pgpl_window_set_position, see pgpl_window_set_position
|
||||
* for more information. */
|
||||
void pgpl_window_thread_set_position(PGPL_WindowThread *window_thread,
|
||||
int32_t x, int32_t y);
|
||||
|
||||
/* Threaded version of pgpl_window_get_scale, see pgpl_window_get_scale for more
|
||||
* information. */
|
||||
double pgpl_window_thread_get_scale(PGPL_WindowThread *window_thread);
|
||||
|
||||
/* Threaded version of pgpl_window_set_scale, see pgpl_window_set_scale for more
|
||||
* information. */
|
||||
void pgpl_window_thread_set_scale(PGPL_WindowThread *window_thread,
|
||||
double scale);
|
||||
|
||||
/* Threaded version of pgpl_window_create_event, see pgpl_window_create_event
|
||||
* for more information. */
|
||||
void pgpl_window_thread_create_event(PGPL_WindowThread *window_thread,
|
||||
PGPL_WindowEventType event_type,
|
||||
PGPL_WindowEventData *event_data);
|
||||
|
||||
/* Threaded version of pgpl_window_set_transient, see pgpl_window_set_transient
|
||||
* for more information. */
|
||||
void pgpl_window_thread_set_transient(PGPL_WindowThread *window_thread,
|
||||
void *parent);
|
||||
|
||||
/* Threaded version of pgpl_window_reparent_to, see pgpl_window_reparent_to for
|
||||
* more information. */
|
||||
void pgpl_window_thread_reparent_to(PGPL_WindowThread *window_thread,
|
||||
void *parent);
|
||||
|
||||
/* Threaded version of pgpl_window_get_raw_window, see
|
||||
* pgpl_window_get_raw_window for more information. */
|
||||
void *pgpl_window_thread_get_raw_window(PGPL_WindowThread *window_thread);
|
||||
|
||||
/* This function halts until the window_thread is destroyed. This function only
|
||||
* works once per window_thread. */
|
||||
void pgpl_window_thread_await_destruction(PGPL_WindowThread *window_thread);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
7988
include/stb/stb_image.h
Normal file
7988
include/stb/stb_image.h
Normal file
File diff suppressed because it is too large
Load diff
5079
include/stb/stb_truetype.h
Normal file
5079
include/stb/stb_truetype.h
Normal file
File diff suppressed because it is too large
Load diff
64
meson.build
Normal file
64
meson.build
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
project(
|
||||
'pgpl',
|
||||
'c',
|
||||
'cpp',
|
||||
version: '0.1',
|
||||
default_options: ['warning_level=3', 'c_std=c99', 'cpp_std=c++11'],
|
||||
)
|
||||
|
||||
include = include_directories('include')
|
||||
|
||||
cc = meson.get_compiler('c')
|
||||
|
||||
link_args = []
|
||||
|
||||
if target_machine.system() == 'windows' or target_machine.system() == 'cygwin'
|
||||
link_args = ['-static']
|
||||
endif
|
||||
|
||||
lib = both_libraries(
|
||||
'pgpl',
|
||||
'src/pgpl.c',
|
||||
'src/thread.c',
|
||||
'src/timer.c',
|
||||
'src/vector.c',
|
||||
'src/window/window.c',
|
||||
'src/window/window-x11.c',
|
||||
'src/window/window-win32.c',
|
||||
'src/window/thread.c',
|
||||
'src/render/shapes.c',
|
||||
'src/render/texture.c',
|
||||
'src/render/font.c',
|
||||
'src/gui/gui.c',
|
||||
'src/gui/helpers.c',
|
||||
'src/gui/widgets/text.c',
|
||||
'src/gui/widgets/button.c',
|
||||
'src/gui/widgets/container.c',
|
||||
include_directories: include,
|
||||
dependencies: [
|
||||
dependency('gl'),
|
||||
dependency('threads'),
|
||||
dependency('x11', required: false),
|
||||
cc.find_library('m', required: false),
|
||||
],
|
||||
install: true,
|
||||
link_args: link_args,
|
||||
)
|
||||
|
||||
test = executable(
|
||||
'pgpl_test',
|
||||
'tests/program.c',
|
||||
include_directories: include,
|
||||
link_with: lib.get_static_lib(),
|
||||
link_args: link_args,
|
||||
)
|
||||
|
||||
shared_library(
|
||||
'pgpl_plugin_test',
|
||||
'tests/plugin.cpp',
|
||||
include_directories: include,
|
||||
link_with: lib.get_static_lib(),
|
||||
link_args: link_args,
|
||||
)
|
||||
|
||||
test('pgpl_test', test)
|
||||
BIN
roboto.ttf
Normal file
BIN
roboto.ttf
Normal file
Binary file not shown.
125
src/gui/gui.c
Normal file
125
src/gui/gui.c
Normal file
|
|
@ -0,0 +1,125 @@
|
|||
|
||||
#include <pgpl.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
static bool pgpl_gui_internal_event_loop(PGPL_Window *window,
|
||||
PGPL_WindowEventType event_type,
|
||||
PGPL_WindowEventData *event_data,
|
||||
void *user_data) {
|
||||
PGPL_Gui *gui = user_data;
|
||||
|
||||
pgpl_mutex_lock(gui->mutex);
|
||||
|
||||
if (event_type == PGPL_WINDOW_EVENT_NONE) {
|
||||
} else if (event_type == PGPL_WINDOW_EVENT_RENDER_REQUEST) {
|
||||
PGPL_Renderer *renderer =
|
||||
pgpl_window_start_render(window, gui->theme.background_color);
|
||||
uint32_t width, height;
|
||||
uint32_t length = pgpl_vector_get_length(gui->widgets);
|
||||
|
||||
pgpl_window_get_size(window, &width, &height);
|
||||
|
||||
for (uint32_t i = 0; i < length; i++) {
|
||||
PGPL_GuiWidget *widget =
|
||||
*(PGPL_GuiWidget **)pgpl_vector_get_index(gui->widgets, i);
|
||||
PGPL_GuiTheme *theme = &gui->theme;
|
||||
|
||||
if (widget->use_theme_override) {
|
||||
theme = &widget->theme_override;
|
||||
}
|
||||
|
||||
pgpl_gui_widget_render_full(widget, renderer, theme, 0, 0, width, height);
|
||||
}
|
||||
|
||||
pgpl_window_finish_render(window, renderer);
|
||||
} else if (event_type == PGPL_WINDOW_EVENT_CLOSE) {
|
||||
pgpl_gui_destroy(gui, true);
|
||||
return false;
|
||||
} else if (event_type == PGPL_WINDOW_EVENT_ERROR) {
|
||||
printf("PGPL_Window has experienced an unexpected error!\nExiting...");
|
||||
} else {
|
||||
uint32_t width, height;
|
||||
uint32_t length = pgpl_vector_get_length(gui->widgets);
|
||||
|
||||
pgpl_window_get_size(window, &width, &height);
|
||||
|
||||
for (uint32_t i = 0; i < length; i++) {
|
||||
PGPL_GuiWidget *widget =
|
||||
*(PGPL_GuiWidget **)pgpl_vector_get_index(gui->widgets, i);
|
||||
widget->event(widget, &gui->theme, gui, event_type, event_data,
|
||||
widget->offset_x, widget->offset_y, width, height);
|
||||
}
|
||||
}
|
||||
|
||||
pgpl_mutex_unlock(gui->mutex);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
PGPL_Gui *pgpl_gui_create(const char *title, uint32_t width, uint32_t height,
|
||||
int32_t x, int32_t y, PGPL_GuiTheme *theme,
|
||||
void *app_data) {
|
||||
PGPL_Gui *gui = malloc(sizeof(*gui));
|
||||
gui->app_data = app_data;
|
||||
gui->widgets = pgpl_vector_create(sizeof(PGPL_GuiWidget *));
|
||||
gui->theme = *theme;
|
||||
gui->window_thread = pgpl_window_thread_create(
|
||||
title, width, height, x, y, pgpl_gui_internal_event_loop, gui);
|
||||
gui->mutex = pgpl_mutex_create();
|
||||
return gui;
|
||||
}
|
||||
|
||||
void pgpl_gui_destroy(PGPL_Gui *gui, bool internal) {
|
||||
uint32_t length = pgpl_vector_get_length(gui->widgets);
|
||||
|
||||
if (!internal) {
|
||||
pgpl_mutex_lock(gui->mutex);
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < length; i++) {
|
||||
PGPL_GuiWidget *widget =
|
||||
*(PGPL_GuiWidget **)pgpl_vector_get_index(gui->widgets, i);
|
||||
widget->destroy(widget);
|
||||
}
|
||||
|
||||
pgpl_mutex_destroy(gui->mutex);
|
||||
|
||||
if (!internal) {
|
||||
pgpl_window_thread_destroy(gui->window_thread);
|
||||
}
|
||||
|
||||
pgpl_vector_destroy(gui->widgets);
|
||||
free(gui);
|
||||
}
|
||||
|
||||
void pgpl_gui_run_and_show(PGPL_Gui *gui, bool wait) {
|
||||
pgpl_window_thread_show(gui->window_thread);
|
||||
if (wait) {
|
||||
pgpl_window_thread_await_destruction(gui->window_thread);
|
||||
}
|
||||
}
|
||||
|
||||
void pgpl_gui_widget_add(PGPL_Gui *gui, PGPL_GuiWidget *widget) {
|
||||
pgpl_mutex_lock(gui->mutex);
|
||||
pgpl_vector_push_back(gui->widgets, &widget);
|
||||
pgpl_mutex_unlock(gui->mutex);
|
||||
}
|
||||
|
||||
uint32_t pgpl_gui_widget_amount(PGPL_Gui *gui) {
|
||||
return pgpl_vector_get_length(gui->widgets);
|
||||
}
|
||||
|
||||
void pgpl_gui_widget_remove(PGPL_Gui *gui, uint32_t index) {
|
||||
pgpl_mutex_lock(gui->mutex);
|
||||
PGPL_GuiWidget *widget =
|
||||
*(PGPL_GuiWidget **)pgpl_vector_delete_index(gui->widgets, index);
|
||||
widget->destroy(widget);
|
||||
pgpl_mutex_unlock(gui->mutex);
|
||||
}
|
||||
|
||||
PGPL_GuiWidget *pgpl_gui_widget_get(PGPL_Gui *gui, uint32_t index) {
|
||||
pgpl_mutex_lock(gui->mutex);
|
||||
return *(PGPL_GuiWidget **)pgpl_vector_get_index(gui->widgets, index);
|
||||
pgpl_mutex_unlock(gui->mutex);
|
||||
}
|
||||
187
src/gui/helpers.c
Normal file
187
src/gui/helpers.c
Normal file
|
|
@ -0,0 +1,187 @@
|
|||
#include <pgpl.h>
|
||||
|
||||
void pgpl_gui_theme_configure(PGPL_GuiTheme *theme, PGPL_Color base_color,
|
||||
double content_font_size, PGPL_Font *font) {
|
||||
theme->background_color = pgpl_color_divide(base_color, 8);
|
||||
|
||||
theme->widget_background_color[0] = pgpl_color_divide(base_color, 5);
|
||||
theme->widget_background_color[1] = pgpl_color_divide(base_color, 4);
|
||||
theme->widget_background_color[2] = pgpl_color_divide(base_color, 3);
|
||||
|
||||
theme->text_color[0] = pgpl_color_divide(base_color, 1.5);
|
||||
theme->text_color[1] = pgpl_color_divide(base_color, 1.25);
|
||||
theme->text_color[2] = base_color;
|
||||
|
||||
theme->font_size[0] = content_font_size * 3;
|
||||
theme->font_size[1] = content_font_size * 2;
|
||||
theme->font_size[2] = content_font_size;
|
||||
|
||||
theme->margin.top = content_font_size / 6;
|
||||
theme->margin.left = theme->margin.top;
|
||||
theme->margin.bottom = theme->margin.top;
|
||||
theme->margin.right = theme->margin.top;
|
||||
|
||||
theme->border.top = content_font_size / 12;
|
||||
theme->border.left = theme->border.top;
|
||||
theme->border.bottom = theme->border.top;
|
||||
theme->border.right = theme->border.top;
|
||||
|
||||
theme->padding.top = content_font_size / 6;
|
||||
theme->padding.left = theme->padding.top;
|
||||
theme->padding.bottom = theme->padding.top;
|
||||
theme->padding.right = theme->padding.top;
|
||||
|
||||
theme->font = font;
|
||||
}
|
||||
|
||||
void pgpl_gui_widget_max_content_size(PGPL_GuiWidget *widget,
|
||||
PGPL_GuiTheme *theme, double *width,
|
||||
double *height, double max_width,
|
||||
double max_height) {
|
||||
*width = max_width;
|
||||
*height = max_height;
|
||||
|
||||
if (!widget->ignore_margin) {
|
||||
*width -= theme->margin.left + theme->margin.right;
|
||||
*height -= theme->margin.top + theme->margin.bottom;
|
||||
}
|
||||
|
||||
if (!widget->ignore_border) {
|
||||
*width -= theme->border.left + theme->border.right;
|
||||
*height -= theme->border.top + theme->border.bottom;
|
||||
}
|
||||
|
||||
if (!widget->ignore_padding) {
|
||||
*width -= theme->padding.left + theme->padding.right;
|
||||
*height -= theme->padding.top + theme->padding.bottom;
|
||||
}
|
||||
}
|
||||
|
||||
void pgpl_gui_widget_render_full(PGPL_GuiWidget *widget,
|
||||
PGPL_Renderer *renderer, PGPL_GuiTheme *theme,
|
||||
double x, double y, double max_width,
|
||||
double max_height) {
|
||||
double width, height;
|
||||
|
||||
if (widget->use_theme_override) {
|
||||
theme = &widget->theme_override;
|
||||
}
|
||||
|
||||
x += +widget->offset_x;
|
||||
y += +widget->offset_y;
|
||||
|
||||
widget->get_content_size(widget, theme, &width, &height, max_width,
|
||||
max_height);
|
||||
|
||||
if (!widget->expand_x) {
|
||||
max_width = width;
|
||||
if (!widget->ignore_margin) {
|
||||
max_width += theme->margin.left + theme->margin.right;
|
||||
}
|
||||
if (!widget->ignore_border) {
|
||||
max_width += theme->border.left + theme->border.right;
|
||||
}
|
||||
if (!widget->ignore_padding) {
|
||||
max_width += theme->padding.left + theme->padding.right;
|
||||
}
|
||||
}
|
||||
|
||||
if (!widget->expand_y) {
|
||||
max_height = height;
|
||||
if (!widget->ignore_margin) {
|
||||
max_height += theme->margin.top + theme->margin.bottom;
|
||||
}
|
||||
if (!widget->ignore_border) {
|
||||
max_height += theme->border.top + theme->border.bottom;
|
||||
}
|
||||
if (!widget->ignore_padding) {
|
||||
max_height += theme->padding.top + theme->padding.bottom;
|
||||
}
|
||||
}
|
||||
|
||||
if (!widget->ignore_margin) {
|
||||
x += theme->margin.left;
|
||||
y += theme->margin.top;
|
||||
|
||||
max_width -= theme->margin.left + theme->margin.right;
|
||||
max_height -= theme->margin.top + theme->margin.bottom;
|
||||
}
|
||||
|
||||
if (widget->border) {
|
||||
pgpl_render_rectangle(renderer, theme->text_color[widget->gui_color], x, y,
|
||||
max_width, max_height);
|
||||
}
|
||||
|
||||
if (!widget->ignore_border) {
|
||||
x += theme->border.left;
|
||||
y += theme->border.top;
|
||||
|
||||
max_width -= theme->border.left + theme->border.right;
|
||||
max_height -= theme->border.top + theme->border.bottom;
|
||||
}
|
||||
|
||||
if (widget->background) {
|
||||
pgpl_render_rectangle(renderer,
|
||||
theme->widget_background_color[widget->gui_color], x,
|
||||
y, max_width, max_height);
|
||||
} else {
|
||||
pgpl_render_rectangle(renderer, theme->background_color, x, y, max_width,
|
||||
max_height);
|
||||
}
|
||||
|
||||
if (!widget->ignore_padding) {
|
||||
x += theme->padding.left;
|
||||
y += theme->padding.top;
|
||||
|
||||
max_width -= theme->padding.left + theme->padding.right;
|
||||
max_height -= theme->padding.top + theme->padding.bottom;
|
||||
}
|
||||
|
||||
x += (max_width - width) / 2;
|
||||
y += (max_height - height) / 2;
|
||||
|
||||
widget->render_content(widget, renderer, theme, x, y, max_width, max_height);
|
||||
}
|
||||
|
||||
bool pgpl_gui_widget_within_bounds(PGPL_GuiWidget *widget, PGPL_GuiTheme *theme,
|
||||
double widget_x, double widget_y,
|
||||
double max_width, double max_height,
|
||||
double x, double y) {
|
||||
(void)widget;
|
||||
|
||||
widget_x += theme->margin.left;
|
||||
widget_y += theme->margin.top;
|
||||
|
||||
max_width -= theme->margin.left + theme->margin.right;
|
||||
max_height -= theme->margin.top + theme->margin.bottom;
|
||||
|
||||
if ((x >= widget_x && x <= (widget_x + max_width)) &&
|
||||
(y >= widget_y && y <= (widget_y + max_height))) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void pgpl_gui_widget_configure(PGPL_GuiWidget *widget, double offset_x,
|
||||
double offset_y, bool expand_x, bool expand_y,
|
||||
bool border, bool background,
|
||||
PGPL_GuiFontSize font_size,
|
||||
PGPL_GuiTheme *theme_override,
|
||||
bool use_theme_override, bool ignore_margin,
|
||||
bool ignore_border, bool ignore_padding) {
|
||||
widget->offset_x = offset_x;
|
||||
widget->offset_y = offset_y;
|
||||
widget->expand_x = expand_x;
|
||||
widget->expand_y = expand_y;
|
||||
widget->border = border;
|
||||
widget->background = background;
|
||||
widget->font_size = font_size;
|
||||
if (theme_override != NULL) {
|
||||
widget->theme_override = *theme_override;
|
||||
}
|
||||
widget->use_theme_override = use_theme_override;
|
||||
widget->ignore_margin = ignore_margin;
|
||||
widget->ignore_border = ignore_border;
|
||||
widget->ignore_padding = ignore_padding;
|
||||
}
|
||||
88
src/gui/widgets/button.c
Normal file
88
src/gui/widgets/button.c
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
#include <pgpl.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
static void get_content_size(PGPL_GuiWidget *widget, PGPL_GuiTheme *theme,
|
||||
double *width, double *height, double max_width,
|
||||
double max_height) {
|
||||
PGPL_GuiButtonWidget *button_widget = (PGPL_GuiButtonWidget *)widget;
|
||||
PGPL_Rectangle rect;
|
||||
|
||||
(void)max_width;
|
||||
(void)max_height;
|
||||
|
||||
pgpl_font_string_dimensions(theme->font, button_widget->text,
|
||||
theme->font_size[widget->font_size], &rect);
|
||||
|
||||
*width = rect.right - rect.left;
|
||||
*height = rect.bottom - rect.top;
|
||||
}
|
||||
|
||||
static void render_content(PGPL_GuiWidget *widget, PGPL_Renderer *renderer,
|
||||
PGPL_GuiTheme *theme, double x, double y,
|
||||
double max_width, double max_height) {
|
||||
PGPL_GuiButtonWidget *button_widget = (PGPL_GuiButtonWidget *)widget;
|
||||
|
||||
(void)max_width;
|
||||
(void)max_height;
|
||||
|
||||
pgpl_font_render_string(renderer, theme->font, button_widget->text,
|
||||
theme->text_color[widget->gui_color], x, y,
|
||||
theme->font_size[widget->font_size]);
|
||||
}
|
||||
|
||||
static void event(PGPL_GuiWidget *widget, PGPL_GuiTheme *theme, PGPL_Gui *gui,
|
||||
PGPL_WindowEventType event_type,
|
||||
PGPL_WindowEventData *event_data, double x, double y,
|
||||
double max_width, double max_height) {
|
||||
PGPL_GuiButtonWidget *button_widget = (PGPL_GuiButtonWidget *)widget;
|
||||
|
||||
if (event_type == PGPL_WINDOW_EVENT_MOUSE_PRESS ||
|
||||
event_type == PGPL_WINDOW_EVENT_MOUSE_RELEASE ||
|
||||
event_type == PGPL_WINDOW_EVENT_MOUSE_MOVE) {
|
||||
if (pgpl_gui_widget_within_bounds(widget, theme, x, y, max_width,
|
||||
max_height, event_data->input_event.x,
|
||||
event_data->input_event.y)) {
|
||||
if (event_type == PGPL_WINDOW_EVENT_MOUSE_PRESS &&
|
||||
event_data->input_event.key == PGPL_WINDOW_MOUSE_BUTTON_LEFT) {
|
||||
widget->gui_color = PGPL_GUI_STATUS_COLOR_ACTIVE;
|
||||
return;
|
||||
} else if (event_type == PGPL_WINDOW_EVENT_MOUSE_RELEASE &&
|
||||
event_data->input_event.key == PGPL_WINDOW_MOUSE_BUTTON_LEFT) {
|
||||
if (widget->gui_color == PGPL_GUI_STATUS_COLOR_ACTIVE) {
|
||||
button_widget->click_callback(gui->app_data);
|
||||
}
|
||||
widget->gui_color = PGPL_GUI_STATUS_COLOR_HOVER;
|
||||
return;
|
||||
} else {
|
||||
if (widget->gui_color != PGPL_GUI_STATUS_COLOR_ACTIVE) {
|
||||
widget->gui_color = PGPL_GUI_STATUS_COLOR_HOVER;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
widget->gui_color = PGPL_GUI_STATUS_COLOR_NORMAL;
|
||||
}
|
||||
|
||||
static void destroy(PGPL_GuiWidget *widget) {
|
||||
pgpl_gui_button_widget_destroy((PGPL_GuiButtonWidget *)widget);
|
||||
}
|
||||
|
||||
PGPL_GuiButtonWidget *
|
||||
pgpl_gui_button_widget_create(const char *text,
|
||||
void (*click_callback)(void *app_data)) {
|
||||
PGPL_GuiButtonWidget *button_widget = calloc(1, sizeof(*button_widget));
|
||||
button_widget->parent.id = "PGPL_GuiButtonWidget";
|
||||
button_widget->parent.get_content_size = get_content_size;
|
||||
button_widget->parent.render_content = render_content;
|
||||
button_widget->parent.event = event;
|
||||
button_widget->parent.destroy = destroy;
|
||||
button_widget->text = text;
|
||||
button_widget->click_callback = click_callback;
|
||||
return button_widget;
|
||||
}
|
||||
|
||||
void pgpl_gui_button_widget_destroy(PGPL_GuiButtonWidget *button_widget) {
|
||||
free(button_widget);
|
||||
}
|
||||
169
src/gui/widgets/container.c
Normal file
169
src/gui/widgets/container.c
Normal file
|
|
@ -0,0 +1,169 @@
|
|||
#include <pgpl.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
static void get_content_size(PGPL_GuiWidget *widget, PGPL_GuiTheme *theme,
|
||||
double *width, double *height, double max_width,
|
||||
double max_height) {
|
||||
(void)widget;
|
||||
pgpl_gui_widget_max_content_size(widget, theme, width, height, max_width,
|
||||
max_height);
|
||||
}
|
||||
|
||||
static void render_content(PGPL_GuiWidget *widget, PGPL_Renderer *renderer,
|
||||
PGPL_GuiTheme *theme, double x, double y,
|
||||
double max_width, double max_height) {
|
||||
PGPL_GuiContainerLayout *container_layout = (PGPL_GuiContainerLayout *)widget;
|
||||
uint32_t length = pgpl_vector_get_length(container_layout->widgets);
|
||||
|
||||
switch (container_layout->direction) {
|
||||
case PGPL_GUI_CONTAINER_LAYOUT_DIRECTION_VERTICAL:
|
||||
max_height /= length;
|
||||
break;
|
||||
case PGPL_GUI_CONTAINER_LAYOUT_DIRECTION_HORIZONTAL:
|
||||
max_width /= length;
|
||||
break;
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < length; i++) {
|
||||
PGPL_GuiWidget *child_widget =
|
||||
*(PGPL_GuiWidget **)pgpl_vector_get_index(container_layout->widgets, i);
|
||||
|
||||
pgpl_gui_widget_render_full(child_widget, renderer, theme, x, y, max_width,
|
||||
max_height);
|
||||
|
||||
switch (container_layout->direction) {
|
||||
case PGPL_GUI_CONTAINER_LAYOUT_DIRECTION_VERTICAL:
|
||||
y += max_height;
|
||||
break;
|
||||
case PGPL_GUI_CONTAINER_LAYOUT_DIRECTION_HORIZONTAL:
|
||||
x += max_width;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void event(PGPL_GuiWidget *widget, PGPL_GuiTheme *theme, PGPL_Gui *gui,
|
||||
PGPL_WindowEventType event_type,
|
||||
PGPL_WindowEventData *event_data, double x, double y,
|
||||
double max_width, double max_height) {
|
||||
PGPL_GuiContainerLayout *container_layout = (PGPL_GuiContainerLayout *)widget;
|
||||
uint32_t length = pgpl_vector_get_length(container_layout->widgets);
|
||||
|
||||
pgpl_gui_widget_max_content_size(widget, theme, &max_width, &max_height,
|
||||
max_width, max_height);
|
||||
|
||||
switch (container_layout->direction) {
|
||||
case PGPL_GUI_CONTAINER_LAYOUT_DIRECTION_VERTICAL:
|
||||
max_height /= length;
|
||||
break;
|
||||
case PGPL_GUI_CONTAINER_LAYOUT_DIRECTION_HORIZONTAL:
|
||||
max_width /= length;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!widget->ignore_margin) {
|
||||
x += theme->margin.left;
|
||||
y += theme->margin.top;
|
||||
}
|
||||
|
||||
if (!widget->ignore_border) {
|
||||
x += theme->border.left;
|
||||
y += theme->border.top;
|
||||
}
|
||||
|
||||
if (!widget->ignore_padding) {
|
||||
x += theme->padding.left;
|
||||
y += theme->padding.top;
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < length; i++) {
|
||||
PGPL_GuiWidget *child_widget =
|
||||
*(PGPL_GuiWidget **)pgpl_vector_get_index(container_layout->widgets, i);
|
||||
|
||||
if (child_widget->use_theme_override) {
|
||||
theme = &child_widget->theme_override;
|
||||
}
|
||||
|
||||
child_widget->event(child_widget, theme, gui, event_type, event_data,
|
||||
x + child_widget->offset_x, y + child_widget->offset_y,
|
||||
max_width, max_height);
|
||||
|
||||
switch (container_layout->direction) {
|
||||
case PGPL_GUI_CONTAINER_LAYOUT_DIRECTION_VERTICAL:
|
||||
y += max_height;
|
||||
break;
|
||||
case PGPL_GUI_CONTAINER_LAYOUT_DIRECTION_HORIZONTAL:
|
||||
x += max_width;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void destroy(PGPL_GuiWidget *widget) {
|
||||
pgpl_gui_container_layout_destroy((PGPL_GuiContainerLayout *)widget);
|
||||
}
|
||||
|
||||
PGPL_GuiContainerLayout *
|
||||
pgpl_gui_container_layout_create(PGPL_GuiContainerLayoutDirection direction) {
|
||||
PGPL_GuiContainerLayout *container_layout =
|
||||
calloc(1, sizeof(*container_layout));
|
||||
container_layout->parent.id = "PGPL_GuiContainerLayout";
|
||||
container_layout->parent.get_content_size = get_content_size;
|
||||
container_layout->parent.render_content = render_content;
|
||||
container_layout->parent.event = event;
|
||||
container_layout->parent.destroy = destroy;
|
||||
container_layout->widgets = pgpl_vector_create(sizeof(PGPL_GuiWidget *));
|
||||
container_layout->direction = direction;
|
||||
container_layout->mutex = pgpl_mutex_create();
|
||||
return container_layout;
|
||||
}
|
||||
|
||||
void pgpl_gui_container_layout_widget_add(
|
||||
PGPL_GuiContainerLayout *container_layout, PGPL_GuiWidget *widget) {
|
||||
pgpl_mutex_lock(container_layout->mutex);
|
||||
pgpl_vector_push_back(container_layout->widgets, &widget);
|
||||
pgpl_mutex_unlock(container_layout->mutex);
|
||||
}
|
||||
|
||||
PGPL_GuiWidget *
|
||||
pgpl_gui_container_layout_widget_get(PGPL_GuiContainerLayout *container_layout,
|
||||
uint32_t index) {
|
||||
PGPL_GuiWidget *widget;
|
||||
pgpl_mutex_lock(container_layout->mutex);
|
||||
widget = *(PGPL_GuiWidget **)pgpl_vector_delete_index(
|
||||
container_layout->widgets, index);
|
||||
pgpl_mutex_unlock(container_layout->mutex);
|
||||
return widget;
|
||||
}
|
||||
|
||||
void pgpl_gui_container_layout_widget_delete(
|
||||
PGPL_GuiContainerLayout *container_layout, uint32_t index) {
|
||||
PGPL_GuiWidget *widget;
|
||||
pgpl_mutex_lock(container_layout->mutex);
|
||||
widget = *(PGPL_GuiWidget **)pgpl_vector_delete_index(
|
||||
container_layout->widgets, index);
|
||||
widget->destroy(widget);
|
||||
pgpl_mutex_unlock(container_layout->mutex);
|
||||
}
|
||||
|
||||
uint32_t pgpl_gui_container_layout_widget_amount(
|
||||
PGPL_GuiContainerLayout *container_layout) {
|
||||
return pgpl_vector_get_length(container_layout->widgets);
|
||||
}
|
||||
|
||||
void pgpl_gui_container_layout_destroy(
|
||||
PGPL_GuiContainerLayout *container_layout) {
|
||||
uint32_t length = pgpl_vector_get_length(container_layout->widgets);
|
||||
pgpl_mutex_lock(container_layout->mutex);
|
||||
|
||||
for (uint32_t i = 0; i < length; i++) {
|
||||
PGPL_GuiWidget *widget =
|
||||
*(PGPL_GuiWidget **)pgpl_vector_get_index(container_layout->widgets, i);
|
||||
widget->destroy(widget);
|
||||
}
|
||||
|
||||
pgpl_mutex_destroy(container_layout->mutex);
|
||||
|
||||
pgpl_vector_destroy(container_layout->widgets);
|
||||
free(container_layout);
|
||||
}
|
||||
59
src/gui/widgets/empty.c
Normal file
59
src/gui/widgets/empty.c
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
#include <pgpl.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
static void get_content_size(PGPL_GuiWidget *widget, PGPL_GuiTheme *theme,
|
||||
double *width, double *height, double max_width,
|
||||
double max_height) {
|
||||
(void)widget;
|
||||
(void)theme;
|
||||
(void)max_width;
|
||||
(void)max_height;
|
||||
|
||||
*width = 0;
|
||||
*height = 0;
|
||||
}
|
||||
|
||||
static void render_content(PGPL_GuiWidget *widget, PGPL_Renderer *renderer,
|
||||
PGPL_GuiTheme *theme, double x, double y,
|
||||
double max_width, double max_height) {
|
||||
(void)widget;
|
||||
(void)theme;
|
||||
(void)renderer;
|
||||
(void)x;
|
||||
(void)y;
|
||||
(void)max_width;
|
||||
(void)max_height;
|
||||
}
|
||||
|
||||
static void event(PGPL_GuiWidget *widget, PGPL_GuiTheme *theme, PGPL_Gui *gui,
|
||||
PGPL_WindowEventType event_type,
|
||||
PGPL_WindowEventData *event_data, double x, double y,
|
||||
double max_width, double max_height) {
|
||||
(void)widget;
|
||||
(void)theme;
|
||||
(void)gui;
|
||||
(void)event_type;
|
||||
(void)event_data;
|
||||
(void)x;
|
||||
(void)y;
|
||||
(void)max_width;
|
||||
(void)max_height;
|
||||
}
|
||||
|
||||
static void destroy(PGPL_GuiWidget *widget) {
|
||||
pgpl_gui_empty_widget_destroy(widget);
|
||||
}
|
||||
|
||||
PGPL_GuiWidget *pgpl_gui_empty_widget_create(void) {
|
||||
PGPL_GuiWidget *empty_widget = calloc(1, sizeof(*empty_widget));
|
||||
empty_widget->id = "PGPL_GuiWidget";
|
||||
empty_widget->get_content_size = get_content_size;
|
||||
empty_widget->render_content = render_content;
|
||||
empty_widget->event = event;
|
||||
empty_widget->destroy = destroy;
|
||||
return empty_widget;
|
||||
}
|
||||
|
||||
void pgpl_gui_empty_widget_destroy(PGPL_GuiWidget *empty_widget) {
|
||||
free(empty_widget);
|
||||
}
|
||||
65
src/gui/widgets/text.c
Normal file
65
src/gui/widgets/text.c
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
#include <pgpl.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
static void get_content_size(PGPL_GuiWidget *widget, PGPL_GuiTheme *theme,
|
||||
double *width, double *height, double max_width,
|
||||
double max_height) {
|
||||
PGPL_GuiTextWidget *text_widget = (PGPL_GuiTextWidget *)widget;
|
||||
PGPL_Rectangle rect;
|
||||
|
||||
(void)max_width;
|
||||
(void)max_height;
|
||||
|
||||
pgpl_font_string_dimensions(theme->font, text_widget->text,
|
||||
theme->font_size[widget->font_size], &rect);
|
||||
|
||||
*width = rect.right - rect.left;
|
||||
*height = rect.bottom - rect.top;
|
||||
}
|
||||
|
||||
static void render_content(PGPL_GuiWidget *widget, PGPL_Renderer *renderer,
|
||||
PGPL_GuiTheme *theme, double x, double y,
|
||||
double max_width, double max_height) {
|
||||
PGPL_GuiTextWidget *text_widget = (PGPL_GuiTextWidget *)widget;
|
||||
|
||||
(void)max_width;
|
||||
(void)max_height;
|
||||
|
||||
pgpl_font_render_string(renderer, theme->font, text_widget->text,
|
||||
theme->text_color[widget->gui_color], x, y,
|
||||
theme->font_size[widget->font_size]);
|
||||
}
|
||||
|
||||
static void event(PGPL_GuiWidget *widget, PGPL_GuiTheme *theme, PGPL_Gui *gui,
|
||||
PGPL_WindowEventType event_type,
|
||||
PGPL_WindowEventData *event_data, double x, double y,
|
||||
double max_width, double max_height) {
|
||||
(void)widget;
|
||||
(void)theme;
|
||||
(void)gui;
|
||||
(void)event_type;
|
||||
(void)event_data;
|
||||
(void)x;
|
||||
(void)y;
|
||||
(void)max_width;
|
||||
(void)max_height;
|
||||
}
|
||||
|
||||
static void destroy(PGPL_GuiWidget *widget) {
|
||||
pgpl_gui_text_widget_destroy((PGPL_GuiTextWidget *)widget);
|
||||
}
|
||||
|
||||
PGPL_GuiTextWidget *pgpl_gui_text_widget_create(const char *text) {
|
||||
PGPL_GuiTextWidget *text_widget = calloc(1, sizeof(*text_widget));
|
||||
text_widget->parent.id = "PGPL_GuiTextWidget";
|
||||
text_widget->parent.get_content_size = get_content_size;
|
||||
text_widget->parent.render_content = render_content;
|
||||
text_widget->parent.event = event;
|
||||
text_widget->parent.destroy = destroy;
|
||||
text_widget->text = text;
|
||||
return text_widget;
|
||||
}
|
||||
|
||||
void pgpl_gui_text_widget_destroy(PGPL_GuiTextWidget *text_widget) {
|
||||
free(text_widget);
|
||||
}
|
||||
7
src/pgpl.c
Normal file
7
src/pgpl.c
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
#include <pgpl.h>
|
||||
|
||||
PGPL_Mutex *render_lock;
|
||||
|
||||
void pgpl_init(void) { render_lock = pgpl_mutex_create(); }
|
||||
|
||||
void pgpl_deinit(void) { pgpl_mutex_destroy(render_lock); }
|
||||
261
src/render/font.c
Normal file
261
src/render/font.c
Normal file
|
|
@ -0,0 +1,261 @@
|
|||
#include "internal.h"
|
||||
#include <float.h>
|
||||
#include <locale.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#define STB_TRUETYPE_IMPLEMENTATION
|
||||
#define STBTT_STATIC
|
||||
#include <stb/stb_truetype.h>
|
||||
|
||||
#ifdef __WIN32__
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
#define CHAR_AMOUNT 256
|
||||
#define BANK_AMOUNT 256
|
||||
|
||||
typedef struct PGPL_FontBank {
|
||||
stbtt_bakedchar baked[CHAR_AMOUNT];
|
||||
uint8_t *bitmap;
|
||||
PGPL_Texture *texture;
|
||||
} PGPL_FontBank;
|
||||
|
||||
struct PGPL_Font {
|
||||
PGPL_FontBank *banks[BANK_AMOUNT];
|
||||
uint32_t glyph_size;
|
||||
uint32_t chars_horiontal;
|
||||
uint32_t chars_vertical;
|
||||
uint8_t *data;
|
||||
};
|
||||
|
||||
/* Converts a string to a wide string, don't forget to call free() on the
|
||||
* returned pointer. */
|
||||
static wchar_t *str_to_wstr(const char *str) {
|
||||
size_t length = strlen(str);
|
||||
wchar_t *wstr = malloc((length + 1) * sizeof(*wstr));
|
||||
|
||||
setlocale(LC_ALL, "en_US.UTF-8");
|
||||
|
||||
/* mbstowcs works just fine with UTF-8 strings on most platforms, except on
|
||||
* Windows, where we have to use MultiByteToWideChar to do the exact same
|
||||
* thing. */
|
||||
#if defined(__WIN32__)
|
||||
MultiByteToWideChar(CP_UTF8, MB_PRECOMPOSED, str, -1, wstr, length + 1);
|
||||
#else
|
||||
mbstowcs(wstr, str, length + 1);
|
||||
#endif
|
||||
|
||||
return wstr;
|
||||
}
|
||||
|
||||
PGPL_Font *pgpl_font_create_from_file(const char *filename,
|
||||
uint32_t glyph_size) {
|
||||
PGPL_Font *font;
|
||||
FILE *font_file = fopen(filename, "r");
|
||||
uint32_t size;
|
||||
uint8_t *font_data;
|
||||
|
||||
if (font_file == NULL) {
|
||||
perror("Failed to open font");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
fseek(font_file, 0, SEEK_END);
|
||||
size = ftell(font_file);
|
||||
font_data = malloc(size);
|
||||
rewind(font_file);
|
||||
fread(font_data, size, 1, font_file);
|
||||
font = pgpl_font_create(font_data, glyph_size);
|
||||
|
||||
fclose(font_file);
|
||||
|
||||
return font;
|
||||
}
|
||||
|
||||
void pgpl_font_internal_bake(PGPL_Font *font, uint32_t index) {
|
||||
font->banks[index] = malloc(sizeof(*font->banks[index]));
|
||||
|
||||
font->banks[index]->bitmap =
|
||||
malloc(CHAR_AMOUNT * font->glyph_size * font->glyph_size);
|
||||
|
||||
stbtt_BakeFontBitmap(
|
||||
font->data, 0, font->glyph_size, font->banks[index]->bitmap,
|
||||
font->glyph_size * font->chars_horiontal,
|
||||
font->glyph_size * font->chars_vertical, CHAR_AMOUNT * index, CHAR_AMOUNT,
|
||||
font->banks[index]->baked);
|
||||
}
|
||||
|
||||
PGPL_Font *pgpl_font_create(uint8_t *data, uint32_t glyph_size) {
|
||||
PGPL_Font *font = malloc(sizeof(*font));
|
||||
|
||||
for (uint32_t i = 0; i < BANK_AMOUNT; i++) {
|
||||
font->banks[i] = NULL;
|
||||
}
|
||||
|
||||
font->glyph_size = glyph_size;
|
||||
font->chars_vertical = 8;
|
||||
font->chars_horiontal = CHAR_AMOUNT / font->chars_vertical;
|
||||
font->data = data;
|
||||
|
||||
return font;
|
||||
}
|
||||
|
||||
void pgpl_font_destroy(PGPL_Renderer *renderer, PGPL_Font *font) {
|
||||
for (uint32_t i = 0; i < BANK_AMOUNT; i++) {
|
||||
if (font->banks[i] != NULL) {
|
||||
pgpl_render_destroy_texture(renderer, font->banks[i]->texture);
|
||||
free(font->banks[i]);
|
||||
}
|
||||
}
|
||||
free(font->data);
|
||||
free(font);
|
||||
}
|
||||
|
||||
void pgpl_font_render_glyph(PGPL_Renderer *renderer, PGPL_Font *font, wchar_t c,
|
||||
PGPL_Color color, double x, double y, double size,
|
||||
double *xoff, double *yoff) {
|
||||
stbtt_aligned_quad q;
|
||||
uint32_t bank_index = c / 256;
|
||||
uint32_t bank_offset = c % 256;
|
||||
double factor = size / font->glyph_size;
|
||||
float xoff_stb = *xoff;
|
||||
float yoff_stb = *yoff;
|
||||
|
||||
if (font->banks[bank_index] == NULL) {
|
||||
pgpl_font_internal_bake(font, bank_index);
|
||||
}
|
||||
|
||||
if (font->banks[bank_index]->bitmap != NULL) {
|
||||
font->banks[bank_index]->texture = pgpl_render_create_texture(
|
||||
renderer, font->banks[bank_index]->bitmap,
|
||||
font->glyph_size * font->chars_horiontal,
|
||||
font->glyph_size * font->chars_vertical, PGPL_TEXTURE_FILTER_LINEAR,
|
||||
PGPL_TEXTURE_FORMAT_BW);
|
||||
|
||||
free(font->banks[bank_index]->bitmap);
|
||||
font->banks[bank_index]->bitmap = NULL;
|
||||
}
|
||||
|
||||
stbtt_GetBakedQuad(font->banks[bank_index]->baked,
|
||||
font->glyph_size * font->chars_horiontal,
|
||||
font->glyph_size * font->chars_vertical, bank_offset,
|
||||
&xoff_stb, &yoff_stb, &q, 0);
|
||||
|
||||
pgpl_render_texture_extended(
|
||||
renderer, font->banks[bank_index]->texture, (x + q.x0 * factor),
|
||||
(y + q.y0 * factor + size / 1.75), (q.x1 - q.x0) * factor,
|
||||
(q.y1 - q.y0) * factor, color, q.s0, q.t0, q.s1, q.t1);
|
||||
|
||||
*xoff = xoff_stb;
|
||||
*yoff = yoff_stb;
|
||||
}
|
||||
|
||||
/* This renders some text, also converts it to a wide string so that it
|
||||
* supports Unicode characters when rendering.
|
||||
*/
|
||||
void pgpl_font_render_string(PGPL_Renderer *renderer, PGPL_Font *font,
|
||||
const char *str, PGPL_Color color, double x,
|
||||
double y, double size) {
|
||||
wchar_t *wstr = str_to_wstr(str);
|
||||
|
||||
pgpl_font_render_wstring(renderer, font, wstr, color, x, y, size);
|
||||
|
||||
free(wstr);
|
||||
}
|
||||
|
||||
void pgpl_font_render_wstring(PGPL_Renderer *renderer, PGPL_Font *font,
|
||||
wchar_t *str, PGPL_Color color, double x,
|
||||
double y, double size) {
|
||||
double xoff = 0;
|
||||
double yoff = 0;
|
||||
|
||||
for (uint32_t i = 0; str[i] != '\0'; i++) {
|
||||
if (str[i] == '\n') {
|
||||
xoff = 0;
|
||||
y += size;
|
||||
continue;
|
||||
}
|
||||
|
||||
pgpl_font_render_glyph(renderer, font, str[i], color, x, y, size, &xoff,
|
||||
&yoff);
|
||||
}
|
||||
}
|
||||
|
||||
/* Calculates the glyph coordinates, and returns them in the passed pointers. */
|
||||
void pgpl_font_glyph_dimensions(PGPL_Font *font, wchar_t c, double size,
|
||||
double *xoff, double *yoff,
|
||||
PGPL_Rectangle *rectangle) {
|
||||
stbtt_aligned_quad q;
|
||||
uint32_t bank_index = c / 256;
|
||||
uint32_t bank_offset = c % 256;
|
||||
double factor = size / font->glyph_size;
|
||||
float xoff_stb = *xoff;
|
||||
float yoff_stb = *yoff;
|
||||
|
||||
if (font->banks[bank_index] == NULL) {
|
||||
pgpl_font_internal_bake(font, bank_index);
|
||||
}
|
||||
|
||||
stbtt_GetBakedQuad(font->banks[bank_index]->baked,
|
||||
font->glyph_size * font->chars_horiontal,
|
||||
font->glyph_size * font->chars_vertical, bank_offset,
|
||||
&xoff_stb, &yoff_stb, &q, 0);
|
||||
|
||||
rectangle->top = q.y0 * factor + size / 1.5;
|
||||
rectangle->left = q.x0 * factor;
|
||||
rectangle->bottom = q.y1 * factor + size / 1.5;
|
||||
rectangle->right = q.x1 * factor;
|
||||
|
||||
*xoff = xoff_stb;
|
||||
*yoff = yoff_stb;
|
||||
}
|
||||
|
||||
/* Calculates the coordinates of an entire string. Also returns them in the
|
||||
* passed pointers.
|
||||
*/
|
||||
void pgpl_font_string_dimensions(PGPL_Font *font, const char *str, double size,
|
||||
PGPL_Rectangle *rectangle) {
|
||||
wchar_t *wstr = str_to_wstr(str);
|
||||
|
||||
pgpl_font_wstring_dimensions(font, wstr, size, rectangle);
|
||||
|
||||
free(wstr);
|
||||
}
|
||||
|
||||
void pgpl_font_wstring_dimensions(PGPL_Font *font, wchar_t *str, double size,
|
||||
PGPL_Rectangle *rectangle) {
|
||||
double xoff = 0;
|
||||
double yoff = 0;
|
||||
|
||||
rectangle->top = DBL_MAX;
|
||||
rectangle->left = DBL_MAX;
|
||||
rectangle->bottom = DBL_MIN;
|
||||
rectangle->right = DBL_MIN;
|
||||
|
||||
for (uint32_t i = 0; str[i] != '\0'; i++) {
|
||||
PGPL_Rectangle glyph_rectangle = {0, 0, 0, 0};
|
||||
|
||||
if (str[i] == '\n') {
|
||||
xoff = 0;
|
||||
yoff += size;
|
||||
continue;
|
||||
}
|
||||
|
||||
pgpl_font_glyph_dimensions(font, str[i], size, &xoff, &yoff, rectangle);
|
||||
|
||||
if (glyph_rectangle.top < rectangle->top) {
|
||||
rectangle->top = glyph_rectangle.top;
|
||||
}
|
||||
if (glyph_rectangle.bottom > rectangle->bottom) {
|
||||
rectangle->bottom = glyph_rectangle.bottom;
|
||||
}
|
||||
if (glyph_rectangle.left < rectangle->left) {
|
||||
rectangle->left = glyph_rectangle.left;
|
||||
}
|
||||
if (glyph_rectangle.right > rectangle->right) {
|
||||
rectangle->right = glyph_rectangle.right;
|
||||
}
|
||||
}
|
||||
}
|
||||
15
src/render/internal.h
Normal file
15
src/render/internal.h
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
#include <GL/gl.h>
|
||||
#include <pgpl.h>
|
||||
|
||||
/* General Notes: All of the gl_* variables are translated from pixels to the
|
||||
* range [-1.0, 1.0] so that they can be rendered with OpenGL.
|
||||
*
|
||||
* It's also important to note that this file should be platform independent,
|
||||
* this code should be able to run on all OpenGL supporting devices.
|
||||
*/
|
||||
|
||||
#define PI 3.1415926535897932384626433
|
||||
|
||||
struct PGPL_Texture {
|
||||
GLuint id;
|
||||
};
|
||||
96
src/render/shapes.c
Normal file
96
src/render/shapes.c
Normal file
|
|
@ -0,0 +1,96 @@
|
|||
#include "internal.h"
|
||||
#include <math.h>
|
||||
|
||||
void pgpl_render_rectangle(PGPL_Renderer *renderer, PGPL_Color color, double x,
|
||||
double y, double width, double height) {
|
||||
double gl_x = x / renderer->width;
|
||||
double gl_y = y / renderer->height;
|
||||
double gl_width = width / renderer->width;
|
||||
double gl_height = height / renderer->height;
|
||||
|
||||
glColor4ub(pgpl_color_get_red(color), pgpl_color_get_green(color),
|
||||
pgpl_color_get_blue(color), pgpl_color_get_alpha(color));
|
||||
|
||||
glBegin(GL_QUADS);
|
||||
glVertex2d(gl_x, gl_y);
|
||||
glVertex2d(gl_x + gl_width, gl_y);
|
||||
glVertex2d(gl_x + gl_width, gl_y + gl_height);
|
||||
glVertex2d(gl_x, gl_y + gl_height);
|
||||
glEnd();
|
||||
}
|
||||
|
||||
void pgpl_render_line(PGPL_Renderer *renderer, PGPL_Color color, double x0,
|
||||
double y0, double x1, double y1) {
|
||||
double gl_x0 = x0 / renderer->width;
|
||||
double gl_y0 = y0 / renderer->height;
|
||||
double gl_x1 = x1 / renderer->width;
|
||||
double gl_y1 = y1 / renderer->height;
|
||||
|
||||
glColor4ub(pgpl_color_get_red(color), pgpl_color_get_green(color),
|
||||
pgpl_color_get_blue(color), pgpl_color_get_alpha(color));
|
||||
|
||||
glBegin(GL_LINES);
|
||||
glVertex2d(gl_x0, gl_y0);
|
||||
glVertex2d(gl_x1, gl_y1);
|
||||
glEnd();
|
||||
}
|
||||
|
||||
void pgpl_render_circle(PGPL_Renderer *renderer, PGPL_Color color, double x,
|
||||
double y, double radius, double triangle_amount) {
|
||||
GLfloat two_pi = 2 * PI;
|
||||
double gl_x, gl_y;
|
||||
|
||||
x += radius;
|
||||
y += radius;
|
||||
|
||||
gl_x = x / renderer->width;
|
||||
gl_y = y / renderer->height;
|
||||
|
||||
glColor4ub(pgpl_color_get_red(color), pgpl_color_get_green(color),
|
||||
pgpl_color_get_blue(color), pgpl_color_get_alpha(color));
|
||||
|
||||
glBegin(GL_TRIANGLE_FAN);
|
||||
glVertex2d(gl_x, gl_y);
|
||||
for (uint32_t i = 0; i <= triangle_amount; i++) {
|
||||
gl_x = (x + (radius * cos(i * two_pi / triangle_amount))) / renderer->width;
|
||||
|
||||
gl_y =
|
||||
(y + (radius * sin(i * two_pi / triangle_amount))) / renderer->height;
|
||||
|
||||
glVertex2d(gl_x, gl_y);
|
||||
}
|
||||
glEnd();
|
||||
}
|
||||
|
||||
void pgpl_render_point(PGPL_Renderer *renderer, PGPL_Color color, double x,
|
||||
double y) {
|
||||
double gl_x = x / renderer->width;
|
||||
double gl_y = y / renderer->height;
|
||||
|
||||
glColor4ub(pgpl_color_get_red(color), pgpl_color_get_green(color),
|
||||
pgpl_color_get_blue(color), pgpl_color_get_alpha(color));
|
||||
|
||||
glBegin(GL_POINTS);
|
||||
glVertex2d(gl_x, gl_y);
|
||||
glEnd();
|
||||
}
|
||||
|
||||
void pgpl_render_triangle(PGPL_Renderer *renderer, PGPL_Color color, double ax,
|
||||
double ay, double bx, double by, double cx,
|
||||
double cy) {
|
||||
double gl_ax = ax / renderer->width;
|
||||
double gl_ay = ay / renderer->height;
|
||||
double gl_bx = bx / renderer->width;
|
||||
double gl_by = by / renderer->height;
|
||||
double gl_cx = cx / renderer->width;
|
||||
double gl_cy = cy / renderer->height;
|
||||
|
||||
glColor4ub(pgpl_color_get_red(color), pgpl_color_get_green(color),
|
||||
pgpl_color_get_blue(color), pgpl_color_get_alpha(color));
|
||||
|
||||
glBegin(GL_TRIANGLES);
|
||||
glVertex2d(gl_ax, gl_ay);
|
||||
glVertex2d(gl_bx, gl_by);
|
||||
glVertex2d(gl_cx, gl_cy);
|
||||
glEnd();
|
||||
}
|
||||
143
src/render/texture.c
Normal file
143
src/render/texture.c
Normal file
|
|
@ -0,0 +1,143 @@
|
|||
#include "internal.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#define STB_IMAGE_IMPLEMENTATION
|
||||
#define STB_IMAGE_STATIC
|
||||
#include <stb/stb_image.h>
|
||||
|
||||
PGPL_Texture *
|
||||
pgpl_render_create_texture_file_memory(PGPL_Renderer *renderer,
|
||||
const uint8_t *file_data, uint32_t len,
|
||||
PGPL_TextureFilter filter) {
|
||||
int32_t x, y, comp;
|
||||
stbi_uc *data =
|
||||
stbi_load_from_memory(file_data, len, &x, &y, &comp, STBI_rgb_alpha);
|
||||
PGPL_Texture *texture;
|
||||
|
||||
if (data != NULL) {
|
||||
texture = pgpl_render_create_texture(renderer, data, x, y, filter,
|
||||
PGPL_TEXTURE_FORMAT_RGBA);
|
||||
} else {
|
||||
fprintf(stderr, "Failed to open image: %s\n", stbi_failure_reason());
|
||||
return NULL;
|
||||
}
|
||||
|
||||
stbi_image_free(data);
|
||||
return texture;
|
||||
}
|
||||
|
||||
PGPL_Texture *pgpl_render_create_texture_file(PGPL_Renderer *renderer,
|
||||
const char *filename,
|
||||
PGPL_TextureFilter filter) {
|
||||
int32_t x, y, comp;
|
||||
stbi_uc *data = stbi_load(filename, &x, &y, &comp, STBI_rgb_alpha);
|
||||
PGPL_Texture *texture;
|
||||
|
||||
if (data != NULL) {
|
||||
texture = pgpl_render_create_texture(renderer, data, x, y, filter,
|
||||
PGPL_TEXTURE_FORMAT_RGBA);
|
||||
} else {
|
||||
fprintf(stderr, "Failed to open image: %s\n", stbi_failure_reason());
|
||||
return NULL;
|
||||
}
|
||||
|
||||
stbi_image_free(data);
|
||||
return texture;
|
||||
}
|
||||
|
||||
/* Creates an OpenGL Texture. The filterers and format are specified with a
|
||||
* switch case. */
|
||||
PGPL_Texture *pgpl_render_create_texture(PGPL_Renderer *renderer,
|
||||
const uint8_t *data, uint32_t width,
|
||||
uint32_t height,
|
||||
PGPL_TextureFilter filter,
|
||||
PGPL_TextureFormat format) {
|
||||
PGPL_Texture *texture = malloc(sizeof(*texture));
|
||||
int32_t gl_filter = 0;
|
||||
int32_t gl_format = 0;
|
||||
|
||||
(void)renderer;
|
||||
|
||||
switch (filter) {
|
||||
case PGPL_TEXTURE_FILTER_LINEAR:
|
||||
gl_filter = GL_LINEAR;
|
||||
break;
|
||||
case PGPL_TEXTURE_FILTER_NEAREST:
|
||||
gl_filter = GL_NEAREST;
|
||||
break;
|
||||
}
|
||||
|
||||
switch (format) {
|
||||
case PGPL_TEXTURE_FORMAT_RGBA:
|
||||
gl_format = GL_RGBA;
|
||||
break;
|
||||
case PGPL_TEXTURE_FORMAT_RGB:
|
||||
gl_format = GL_RGB;
|
||||
break;
|
||||
case PGPL_TEXTURE_FORMAT_BW:
|
||||
gl_format = GL_ALPHA;
|
||||
break;
|
||||
}
|
||||
|
||||
glGenTextures(1, &texture->id);
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, texture->id);
|
||||
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter);
|
||||
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, gl_format, width, height, 0, gl_format,
|
||||
GL_UNSIGNED_BYTE, data);
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
|
||||
return texture;
|
||||
}
|
||||
|
||||
/* Null check so that it can be freed in the case of it being impossible to get
|
||||
* another renderer
|
||||
*/
|
||||
void pgpl_render_destroy_texture(PGPL_Renderer *renderer,
|
||||
PGPL_Texture *texture) {
|
||||
if (renderer != NULL) {
|
||||
glDeleteTextures(1, &texture->id);
|
||||
}
|
||||
free(texture);
|
||||
}
|
||||
|
||||
void pgpl_render_texture(PGPL_Renderer *renderer, PGPL_Texture *texture,
|
||||
double x, double y, double width, double height) {
|
||||
pgpl_render_texture_extended(renderer, texture, x, y, width, height, 0xFFFFFF,
|
||||
0.0f, 0.0f, 1.0f, 1.0f);
|
||||
}
|
||||
|
||||
void pgpl_render_texture_extended(PGPL_Renderer *renderer,
|
||||
PGPL_Texture *texture, double x, double y,
|
||||
double width, double height, PGPL_Color color,
|
||||
double s0, double t0, double s1, double t1) {
|
||||
double gl_x = x / renderer->width;
|
||||
double gl_y = y / renderer->height;
|
||||
double gl_width = width / renderer->width;
|
||||
double gl_height = height / renderer->height;
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, texture->id);
|
||||
|
||||
glColor4ub(pgpl_color_get_red(color), pgpl_color_get_green(color),
|
||||
pgpl_color_get_blue(color), pgpl_color_get_alpha(color));
|
||||
|
||||
glBegin(GL_QUADS);
|
||||
glTexCoord2d(s0, t0);
|
||||
glVertex2d(gl_x, gl_y);
|
||||
glTexCoord2d(s1, t0);
|
||||
glVertex2d(gl_x + gl_width, gl_y);
|
||||
glTexCoord2d(s1, t1);
|
||||
glVertex2d(gl_x + gl_width, gl_y + gl_height);
|
||||
glTexCoord2d(s0, t1);
|
||||
glVertex2d(gl_x, gl_y + gl_height);
|
||||
glEnd();
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
}
|
||||
44
src/thread.c
Normal file
44
src/thread.c
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
#include <pgpl.h>
|
||||
#include <pthread.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
struct PGPL_Thread {
|
||||
pthread_t thread;
|
||||
};
|
||||
|
||||
struct PGPL_Mutex {
|
||||
pthread_mutex_t mutex;
|
||||
};
|
||||
|
||||
PGPL_Thread *pgpl_thread_create(void *(*function)(void *arg), void *arg) {
|
||||
PGPL_Thread *thread = malloc(sizeof(*thread));
|
||||
pthread_create(&thread->thread, NULL, function, arg);
|
||||
return thread;
|
||||
}
|
||||
|
||||
void pgpl_thread_destroy(PGPL_Thread *thread) { free(thread); }
|
||||
|
||||
void pgpl_thread_join(PGPL_Thread *thread) {
|
||||
pthread_join(thread->thread, NULL);
|
||||
}
|
||||
|
||||
PGPL_Mutex *pgpl_mutex_create(void) {
|
||||
PGPL_Mutex *mutex = malloc(sizeof(*mutex));
|
||||
pthread_mutex_init(&mutex->mutex, NULL);
|
||||
return mutex;
|
||||
}
|
||||
|
||||
void pgpl_mutex_destroy(PGPL_Mutex *mutex) {
|
||||
pthread_mutex_destroy(&mutex->mutex);
|
||||
free(mutex);
|
||||
}
|
||||
|
||||
void pgpl_mutex_lock(PGPL_Mutex *mutex) { pthread_mutex_lock(&mutex->mutex); }
|
||||
|
||||
bool pgpl_mutex_try_lock(PGPL_Mutex *mutex) {
|
||||
return pthread_mutex_trylock(&mutex->mutex) == 0;
|
||||
}
|
||||
|
||||
void pgpl_mutex_unlock(PGPL_Mutex *mutex) {
|
||||
pthread_mutex_unlock(&mutex->mutex);
|
||||
}
|
||||
48
src/timer.c
Normal file
48
src/timer.c
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
/* _DEFAULT_SOURCE must be defined for usleep to be declared on C99 (glibc). */
|
||||
#define _DEFAULT_SOURCE
|
||||
/* _XPG4_2 must be defined on OpenIndiana for usleep to be defined. */
|
||||
#define _XPG4_2
|
||||
|
||||
#include <pgpl.h>
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
|
||||
#ifdef __WIN32__
|
||||
#include <windows.h>
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
struct PGPL_Timer {
|
||||
clock_t time;
|
||||
};
|
||||
|
||||
PGPL_Timer *pgpl_timer_create(void) {
|
||||
PGPL_Timer *timer = malloc(sizeof(PGPL_Timer));
|
||||
timer->time = clock() / (CLOCKS_PER_SEC / 1000.0);
|
||||
return timer;
|
||||
}
|
||||
|
||||
void pgpl_timer_destroy(PGPL_Timer *timer) { free(timer); }
|
||||
|
||||
uint32_t pgpl_timer_get_delta(PGPL_Timer *timer) {
|
||||
clock_t new_time = clock() / (CLOCKS_PER_SEC / 1000.0);
|
||||
clock_t delta;
|
||||
if (timer->time != 0) {
|
||||
delta = new_time / timer->time;
|
||||
} else {
|
||||
delta = 0;
|
||||
}
|
||||
timer->time = new_time;
|
||||
return delta;
|
||||
}
|
||||
|
||||
void pgpl_timer_sleep(uint32_t milliseconds) {
|
||||
if (milliseconds > 0) {
|
||||
#ifdef __WIN32__
|
||||
Sleep(milliseconds);
|
||||
#else
|
||||
usleep(1000 * milliseconds);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
105
src/vector.c
Normal file
105
src/vector.c
Normal file
|
|
@ -0,0 +1,105 @@
|
|||
#include <pgpl.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
/* TODO: Further test this file. */
|
||||
|
||||
struct PGPL_Vector {
|
||||
void *data;
|
||||
uint32_t data_size;
|
||||
uint32_t length;
|
||||
uint32_t allocated;
|
||||
void *last_delete;
|
||||
};
|
||||
|
||||
#define pgpl_vector_internal_conditional_resize(vector) \
|
||||
if (vector->length > vector->allocated) { \
|
||||
vector->allocated *= 2; \
|
||||
vector->data = \
|
||||
realloc(vector->data, vector->allocated * vector->data_size); \
|
||||
}
|
||||
|
||||
#define pgpl_vector_internal_memcpy_offset(vector, i) \
|
||||
(char *)vector->data + (vector->data_size * (i))
|
||||
|
||||
PGPL_Vector *pgpl_vector_create(uint32_t data_size) {
|
||||
PGPL_Vector *vector = malloc(sizeof(*vector));
|
||||
vector->data_size = data_size;
|
||||
vector->allocated = 1;
|
||||
vector->length = 0;
|
||||
vector->last_delete = malloc(vector->data_size);
|
||||
vector->data = malloc(vector->allocated * vector->data_size);
|
||||
return vector;
|
||||
}
|
||||
|
||||
void pgpl_vector_destroy(PGPL_Vector *vector) {
|
||||
free(vector->data);
|
||||
free(vector->last_delete);
|
||||
free(vector);
|
||||
}
|
||||
|
||||
void pgpl_vector_push_back(PGPL_Vector *vector, void *data) {
|
||||
vector->length++;
|
||||
pgpl_vector_internal_conditional_resize(vector);
|
||||
memcpy(pgpl_vector_internal_memcpy_offset(vector, vector->length - 1), data,
|
||||
vector->data_size);
|
||||
}
|
||||
|
||||
void pgpl_vector_push_front(PGPL_Vector *vector, void *data) {
|
||||
vector->length++;
|
||||
pgpl_vector_internal_conditional_resize(vector);
|
||||
memmove(pgpl_vector_internal_memcpy_offset(vector, 1), vector->data,
|
||||
vector->data_size * vector->length);
|
||||
memcpy(vector->data, data, vector->data_size);
|
||||
}
|
||||
|
||||
void *pgpl_vector_pop_back(PGPL_Vector *vector) {
|
||||
memcpy(vector->last_delete,
|
||||
pgpl_vector_internal_memcpy_offset(vector, vector->length - 1),
|
||||
vector->data_size);
|
||||
|
||||
vector->length--;
|
||||
|
||||
return vector->last_delete;
|
||||
}
|
||||
|
||||
void *pgpl_vector_pop_front(PGPL_Vector *vector) {
|
||||
memcpy(vector->last_delete, vector->data, vector->data_size);
|
||||
vector->length--;
|
||||
memmove(vector->data, pgpl_vector_internal_memcpy_offset(vector, 1),
|
||||
vector->data_size * vector->length);
|
||||
return vector->last_delete;
|
||||
}
|
||||
|
||||
void *pgpl_vector_peek_back(PGPL_Vector *vector) {
|
||||
return pgpl_vector_internal_memcpy_offset(vector, vector->length - 1);
|
||||
}
|
||||
|
||||
void *pgpl_vector_peek_front(PGPL_Vector *vector) { return vector->data; }
|
||||
|
||||
void *pgpl_vector_delete_index(PGPL_Vector *vector, uint32_t index) {
|
||||
memcpy(vector->last_delete, pgpl_vector_internal_memcpy_offset(vector, index),
|
||||
vector->data_size);
|
||||
memmove(pgpl_vector_internal_memcpy_offset(vector, index),
|
||||
pgpl_vector_internal_memcpy_offset(vector, index + 1),
|
||||
vector->data_size * (vector->length - index - 1));
|
||||
vector->length--;
|
||||
return vector->last_delete;
|
||||
}
|
||||
|
||||
void pgpl_vector_insert_after_index(PGPL_Vector *vector, uint32_t index,
|
||||
void *data) {
|
||||
vector->length++;
|
||||
pgpl_vector_internal_conditional_resize(vector);
|
||||
memmove(pgpl_vector_internal_memcpy_offset(vector, index + 2),
|
||||
pgpl_vector_internal_memcpy_offset(vector, index + 1),
|
||||
vector->data_size * (vector->length - index - 2));
|
||||
memcpy(pgpl_vector_internal_memcpy_offset(vector, index + 1), data,
|
||||
vector->data_size);
|
||||
}
|
||||
|
||||
void *pgpl_vector_get_index(PGPL_Vector *vector, uint32_t index) {
|
||||
return pgpl_vector_internal_memcpy_offset(vector, index);
|
||||
}
|
||||
|
||||
uint32_t pgpl_vector_get_length(PGPL_Vector *vector) { return vector->length; }
|
||||
24
src/window/internal.h
Normal file
24
src/window/internal.h
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
#ifndef PGPL_WINDOW_INTERNAL_H
|
||||
#define PGPL_WINDOW_INTERNAL_H
|
||||
|
||||
#include <pgpl.h>
|
||||
|
||||
/* This is needed if there are multiple windows open. Created by pgpl_init,
|
||||
* destroyed by pgpl_deinit. */
|
||||
extern PGPL_Mutex *render_lock;
|
||||
|
||||
typedef struct PGPL_WindowShared {
|
||||
double scale;
|
||||
PGPL_Vector *event_types;
|
||||
PGPL_Vector *event_data;
|
||||
} PGPL_WindowShared;
|
||||
|
||||
PGPL_WindowEventType
|
||||
pgpl_window_internal_fetch_window_event(PGPL_Window *window,
|
||||
PGPL_WindowEventData *event_data);
|
||||
|
||||
void pgpl_window_internal_start_render(PGPL_Window *window);
|
||||
|
||||
void pgpl_window_internal_finish_render(PGPL_Window *window);
|
||||
|
||||
#endif
|
||||
363
src/window/thread.c
Normal file
363
src/window/thread.c
Normal file
|
|
@ -0,0 +1,363 @@
|
|||
#include "internal.h"
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
||||
typedef enum PGPL_WindowThreadAction {
|
||||
PGPL_WINDOW_THREAD_NONE,
|
||||
PGPL_WINDOW_THREAD_DESTROY,
|
||||
PGPL_WINDOW_THREAD_SHOW,
|
||||
PGPL_WINDOW_THREAD_HIDE,
|
||||
PGPL_WINDOW_THREAD_SET_TITLE,
|
||||
PGPL_WINDOW_THREAD_GET_TITLE,
|
||||
PGPL_WINDOW_THREAD_SET_SIZE,
|
||||
PGPL_WINDOW_THREAD_GET_SIZE,
|
||||
PGPL_WINDOW_THREAD_SET_POSITION,
|
||||
PGPL_WINDOW_THREAD_GET_POSITION,
|
||||
PGPL_WINDOW_THREAD_SET_SCALE,
|
||||
PGPL_WINDOW_THREAD_GET_SCALE,
|
||||
PGPL_WINDOW_THREAD_SET_TRANSIENT,
|
||||
PGPL_WINDOW_THREAD_REPARENT,
|
||||
PGPL_WINDOW_THREAD_GET_RAW_WINDOW,
|
||||
PGPL_WINDOW_THREAD_CREATE_EVENT
|
||||
} PGPL_WindowThreadAction;
|
||||
|
||||
typedef struct PGPL_WindowThreadCreateParameters {
|
||||
PGPL_WindowThread *window_thread;
|
||||
const char *title;
|
||||
uint32_t width, height;
|
||||
int32_t x, y;
|
||||
} PGPL_WindowThreadCreateParameters;
|
||||
|
||||
typedef union PGPL_WindowThreadActionData {
|
||||
struct {
|
||||
uint32_t width;
|
||||
uint32_t height;
|
||||
} size_data;
|
||||
struct {
|
||||
int32_t x;
|
||||
int32_t y;
|
||||
} position_data;
|
||||
const char *title;
|
||||
double scale;
|
||||
void *raw_window;
|
||||
struct {
|
||||
PGPL_WindowEventType type;
|
||||
PGPL_WindowEventData *data;
|
||||
} event;
|
||||
} PGPL_WindowThreadActionData;
|
||||
|
||||
struct PGPL_WindowThread {
|
||||
PGPL_Window *window;
|
||||
PGPL_Thread *thread;
|
||||
PGPL_Mutex *action_lock;
|
||||
PGPL_Mutex *process_ready_lock;
|
||||
PGPL_Mutex *result_ready_lock;
|
||||
PGPL_WindowThreadAction action;
|
||||
PGPL_WindowThreadActionData data;
|
||||
bool (*event_loop)(PGPL_Window *window, PGPL_WindowEventType event,
|
||||
PGPL_WindowEventData *event_data, void *user_data);
|
||||
void *user_data;
|
||||
};
|
||||
|
||||
/* This is the main event_loop for the threads. It fetches the Window events and
|
||||
* calls the event loop callback. It also receives other events from the
|
||||
* functions later in this file.
|
||||
*
|
||||
* TODO: Allow choosing the framerate.
|
||||
*/
|
||||
static void *pgpl_window_event_loop(void *arg) {
|
||||
PGPL_WindowThreadCreateParameters *args = arg;
|
||||
PGPL_WindowThread *window_thread = args->window_thread;
|
||||
PGPL_Timer *timer;
|
||||
|
||||
bool running = true;
|
||||
|
||||
window_thread->window = pgpl_window_create(args->title, args->width,
|
||||
args->height, args->x, args->y);
|
||||
|
||||
free(arg);
|
||||
|
||||
pgpl_mutex_unlock(window_thread->action_lock);
|
||||
|
||||
timer = pgpl_timer_create();
|
||||
while (running) {
|
||||
PGPL_WindowEventData event_data;
|
||||
|
||||
while (running) {
|
||||
PGPL_WindowEventType event_type =
|
||||
pgpl_window_fetch_event(window_thread->window, &event_data);
|
||||
|
||||
bool lock_status = pgpl_mutex_try_lock(window_thread->process_ready_lock);
|
||||
|
||||
if (lock_status != true && event_type == PGPL_WINDOW_EVENT_NONE) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (event_type != PGPL_WINDOW_EVENT_NONE) {
|
||||
if (!window_thread->event_loop(window_thread->window, event_type,
|
||||
&event_data, window_thread->user_data)) {
|
||||
running = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (lock_status == true) {
|
||||
switch (window_thread->action) {
|
||||
case PGPL_WINDOW_THREAD_NONE:
|
||||
break;
|
||||
case PGPL_WINDOW_THREAD_DESTROY:
|
||||
window_thread->event_loop(window_thread->window,
|
||||
PGPL_WINDOW_EVENT_CLOSE, &event_data,
|
||||
window_thread->user_data);
|
||||
running = 0;
|
||||
break;
|
||||
case PGPL_WINDOW_THREAD_SHOW:
|
||||
pgpl_window_show(window_thread->window);
|
||||
break;
|
||||
case PGPL_WINDOW_THREAD_HIDE:
|
||||
pgpl_window_hide(window_thread->window);
|
||||
break;
|
||||
case PGPL_WINDOW_THREAD_SET_TITLE:
|
||||
pgpl_window_set_title(window_thread->window,
|
||||
window_thread->data.title);
|
||||
break;
|
||||
case PGPL_WINDOW_THREAD_GET_TITLE:
|
||||
window_thread->data.title =
|
||||
pgpl_window_get_title(window_thread->window);
|
||||
break;
|
||||
case PGPL_WINDOW_THREAD_SET_SIZE:
|
||||
pgpl_window_set_size(window_thread->window,
|
||||
window_thread->data.size_data.width,
|
||||
window_thread->data.size_data.height);
|
||||
break;
|
||||
case PGPL_WINDOW_THREAD_GET_SIZE:
|
||||
pgpl_window_get_size(window_thread->window,
|
||||
&window_thread->data.size_data.width,
|
||||
&window_thread->data.size_data.height);
|
||||
break;
|
||||
case PGPL_WINDOW_THREAD_SET_POSITION:
|
||||
pgpl_window_set_position(window_thread->window,
|
||||
window_thread->data.position_data.x,
|
||||
window_thread->data.position_data.y);
|
||||
break;
|
||||
case PGPL_WINDOW_THREAD_GET_POSITION:
|
||||
pgpl_window_get_position(window_thread->window,
|
||||
&window_thread->data.position_data.x,
|
||||
&window_thread->data.position_data.y);
|
||||
break;
|
||||
case PGPL_WINDOW_THREAD_SET_SCALE:
|
||||
pgpl_window_set_scale(window_thread->window,
|
||||
window_thread->data.scale);
|
||||
break;
|
||||
case PGPL_WINDOW_THREAD_GET_SCALE:
|
||||
window_thread->data.scale =
|
||||
pgpl_window_get_scale(window_thread->window);
|
||||
break;
|
||||
case PGPL_WINDOW_THREAD_SET_TRANSIENT:
|
||||
pgpl_window_set_transient(window_thread->window,
|
||||
window_thread->data.raw_window);
|
||||
break;
|
||||
case PGPL_WINDOW_THREAD_REPARENT:
|
||||
pgpl_window_reparent_to(window_thread->window,
|
||||
window_thread->data.raw_window);
|
||||
break;
|
||||
case PGPL_WINDOW_THREAD_GET_RAW_WINDOW:
|
||||
window_thread->data.raw_window =
|
||||
pgpl_window_get_raw_window(window_thread->window);
|
||||
break;
|
||||
case PGPL_WINDOW_THREAD_CREATE_EVENT:
|
||||
pgpl_window_create_event(window_thread->window,
|
||||
window_thread->data.event.type,
|
||||
window_thread->data.event.data);
|
||||
break;
|
||||
}
|
||||
pgpl_mutex_unlock(window_thread->result_ready_lock);
|
||||
}
|
||||
}
|
||||
|
||||
pgpl_window_create_event(window_thread->window,
|
||||
PGPL_WINDOW_EVENT_RENDER_REQUEST, NULL);
|
||||
pgpl_timer_sleep(20 - pgpl_timer_get_delta(timer));
|
||||
}
|
||||
|
||||
pgpl_timer_destroy(timer);
|
||||
pgpl_window_destroy(window_thread->window);
|
||||
|
||||
pgpl_mutex_destroy(window_thread->action_lock);
|
||||
pgpl_mutex_destroy(window_thread->process_ready_lock);
|
||||
pgpl_mutex_destroy(window_thread->result_ready_lock);
|
||||
|
||||
pgpl_thread_destroy(window_thread->thread);
|
||||
|
||||
free(window_thread);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Creates all needed mutexes. */
|
||||
PGPL_WindowThread *pgpl_window_thread_create(
|
||||
const char *title, uint32_t width, uint32_t height, int32_t x, int32_t y,
|
||||
bool (*event_loop)(PGPL_Window *window, PGPL_WindowEventType event,
|
||||
PGPL_WindowEventData *event_data, void *user_data),
|
||||
void *user_data) {
|
||||
PGPL_WindowThreadCreateParameters *args = malloc(sizeof(*args));
|
||||
|
||||
args->window_thread = malloc(sizeof(*args->window_thread));
|
||||
args->window_thread->user_data = user_data;
|
||||
args->window_thread->event_loop = event_loop;
|
||||
args->window_thread->action = PGPL_WINDOW_THREAD_NONE;
|
||||
args->title = title;
|
||||
args->width = width;
|
||||
args->height = height;
|
||||
args->x = x;
|
||||
args->y = y;
|
||||
|
||||
args->window_thread->action_lock = pgpl_mutex_create();
|
||||
args->window_thread->process_ready_lock = pgpl_mutex_create();
|
||||
args->window_thread->result_ready_lock = pgpl_mutex_create();
|
||||
|
||||
pgpl_mutex_lock(args->window_thread->action_lock);
|
||||
pgpl_mutex_lock(args->window_thread->process_ready_lock);
|
||||
pgpl_mutex_lock(args->window_thread->result_ready_lock);
|
||||
|
||||
args->window_thread->thread =
|
||||
pgpl_thread_create(pgpl_window_event_loop, args);
|
||||
|
||||
return args->window_thread;
|
||||
}
|
||||
|
||||
PGPL_WindowThreadActionData *
|
||||
pgpl_window_thread_internal_send_message(PGPL_WindowThread *window_thread,
|
||||
PGPL_WindowThreadAction action,
|
||||
PGPL_WindowThreadActionData *data) {
|
||||
pgpl_mutex_lock(window_thread->action_lock);
|
||||
window_thread->action = action;
|
||||
if (data != NULL) {
|
||||
window_thread->data = *data;
|
||||
}
|
||||
pgpl_mutex_unlock(window_thread->process_ready_lock);
|
||||
pgpl_mutex_lock(window_thread->result_ready_lock);
|
||||
if (data != NULL) {
|
||||
*data = window_thread->data;
|
||||
}
|
||||
pgpl_mutex_unlock(window_thread->action_lock);
|
||||
return data;
|
||||
}
|
||||
|
||||
void pgpl_window_thread_destroy(PGPL_WindowThread *window_thread) {
|
||||
pgpl_window_thread_internal_send_message(window_thread,
|
||||
PGPL_WINDOW_THREAD_DESTROY, NULL);
|
||||
}
|
||||
|
||||
void pgpl_window_thread_show(PGPL_WindowThread *window_thread) {
|
||||
pgpl_window_thread_internal_send_message(window_thread,
|
||||
PGPL_WINDOW_THREAD_SHOW, NULL);
|
||||
}
|
||||
|
||||
void pgpl_window_thread_hide(PGPL_WindowThread *window_thread) {
|
||||
pgpl_window_thread_internal_send_message(window_thread,
|
||||
PGPL_WINDOW_THREAD_HIDE, NULL);
|
||||
}
|
||||
|
||||
void pgpl_window_thread_set_title(PGPL_WindowThread *window_thread,
|
||||
const char *title) {
|
||||
PGPL_WindowThreadActionData data;
|
||||
data.title = title;
|
||||
pgpl_window_thread_internal_send_message(window_thread,
|
||||
PGPL_WINDOW_THREAD_SET_TITLE, &data);
|
||||
}
|
||||
|
||||
const char *pgpl_window_thread_get_title(PGPL_WindowThread *window_thread) {
|
||||
PGPL_WindowThreadActionData data;
|
||||
return pgpl_window_thread_internal_send_message(
|
||||
window_thread, PGPL_WINDOW_THREAD_GET_TITLE, &data)
|
||||
->title;
|
||||
}
|
||||
|
||||
void pgpl_window_thread_get_size(PGPL_WindowThread *window_thread,
|
||||
uint32_t *width, uint32_t *height) {
|
||||
PGPL_WindowThreadActionData data;
|
||||
pgpl_window_thread_internal_send_message(window_thread,
|
||||
PGPL_WINDOW_THREAD_GET_SIZE, &data);
|
||||
*width = data.size_data.width;
|
||||
*height = data.size_data.height;
|
||||
}
|
||||
|
||||
void pgpl_window_thread_set_size(PGPL_WindowThread *window_thread,
|
||||
uint32_t width, uint32_t height) {
|
||||
PGPL_WindowThreadActionData data;
|
||||
data.size_data.width = width;
|
||||
data.size_data.height = height;
|
||||
pgpl_window_thread_internal_send_message(window_thread,
|
||||
PGPL_WINDOW_THREAD_SET_SIZE, &data);
|
||||
}
|
||||
|
||||
void pgpl_window_thread_get_position(PGPL_WindowThread *window_thread,
|
||||
int32_t *x, int32_t *y) {
|
||||
PGPL_WindowThreadActionData data;
|
||||
pgpl_window_thread_internal_send_message(
|
||||
window_thread, PGPL_WINDOW_THREAD_GET_POSITION, &data);
|
||||
*x = data.position_data.x;
|
||||
*y = data.position_data.y;
|
||||
}
|
||||
|
||||
void pgpl_window_thread_set_position(PGPL_WindowThread *window_thread,
|
||||
int32_t x, int32_t y) {
|
||||
PGPL_WindowThreadActionData data;
|
||||
data.position_data.x = x;
|
||||
data.position_data.y = y;
|
||||
pgpl_window_thread_internal_send_message(
|
||||
window_thread, PGPL_WINDOW_THREAD_SET_POSITION, &data);
|
||||
}
|
||||
|
||||
double pgpl_window_thread_get_scale(PGPL_WindowThread *window_thread) {
|
||||
PGPL_WindowThreadActionData data;
|
||||
return pgpl_window_thread_internal_send_message(
|
||||
window_thread, PGPL_WINDOW_THREAD_GET_SCALE, &data)
|
||||
->scale;
|
||||
}
|
||||
|
||||
void pgpl_window_thread_set_scale(PGPL_WindowThread *window_thread,
|
||||
double scale) {
|
||||
PGPL_WindowThreadActionData data;
|
||||
data.scale = scale;
|
||||
pgpl_window_thread_internal_send_message(window_thread,
|
||||
PGPL_WINDOW_THREAD_SET_SCALE, &data);
|
||||
}
|
||||
|
||||
void pgpl_window_thread_set_transient(PGPL_WindowThread *window_thread,
|
||||
void *parent) {
|
||||
PGPL_WindowThreadActionData data;
|
||||
data.raw_window = parent;
|
||||
pgpl_window_thread_internal_send_message(
|
||||
window_thread, PGPL_WINDOW_THREAD_SET_TRANSIENT, &data);
|
||||
}
|
||||
|
||||
void pgpl_window_thread_reparent_to(PGPL_WindowThread *window_thread,
|
||||
void *parent) {
|
||||
PGPL_WindowThreadActionData data;
|
||||
data.raw_window = parent;
|
||||
pgpl_window_thread_internal_send_message(window_thread,
|
||||
PGPL_WINDOW_THREAD_REPARENT, &data);
|
||||
}
|
||||
|
||||
void *pgpl_window_thread_get_raw_window(PGPL_WindowThread *window_thread) {
|
||||
PGPL_WindowThreadActionData data;
|
||||
return pgpl_window_thread_internal_send_message(
|
||||
window_thread, PGPL_WINDOW_THREAD_GET_RAW_WINDOW, &data)
|
||||
->raw_window;
|
||||
}
|
||||
|
||||
void pgpl_window_thread_create_event(PGPL_WindowThread *window_thread,
|
||||
PGPL_WindowEventType event_type,
|
||||
PGPL_WindowEventData *event_data) {
|
||||
PGPL_WindowThreadActionData data;
|
||||
data.event.type = event_type;
|
||||
data.event.data = event_data;
|
||||
pgpl_window_thread_internal_send_message(
|
||||
window_thread, PGPL_WINDOW_THREAD_CREATE_EVENT, &data);
|
||||
}
|
||||
|
||||
void pgpl_window_thread_await_destruction(PGPL_WindowThread *window_thread) {
|
||||
pgpl_thread_join(window_thread->thread);
|
||||
}
|
||||
331
src/window/window-win32.c
Normal file
331
src/window/window-win32.c
Normal file
|
|
@ -0,0 +1,331 @@
|
|||
#ifdef __WIN32__
|
||||
#include "internal.h"
|
||||
#include <GL/gl.h>
|
||||
#include <string.h>
|
||||
#include <windows.h>
|
||||
|
||||
struct PGPL_Window {
|
||||
PGPL_WindowShared shared;
|
||||
HWND hwnd;
|
||||
WNDCLASS wc;
|
||||
HDC hdc;
|
||||
HGLRC wgl_context;
|
||||
PGPL_Mutex *window_proc_vector_lock;
|
||||
PGPL_Vector *window_proc_event_type;
|
||||
PGPL_Vector *window_proc_event_data;
|
||||
int32_t last_mouse_x, last_mouse_y;
|
||||
char title[128];
|
||||
};
|
||||
|
||||
/* Our WindowProc function, this needs to be overhauled a bit, and certain
|
||||
* things in here could be solved with more macros, to clean up and shorten the
|
||||
* code a bit.
|
||||
*/
|
||||
static LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam,
|
||||
LPARAM lParam) {
|
||||
PGPL_Window *window = (PGPL_Window *)GetWindowLongPtr(hwnd, 0);
|
||||
PGPL_WindowEventType event_type;
|
||||
PGPL_WindowEventData event_data;
|
||||
|
||||
if (window == NULL) {
|
||||
return DefWindowProc(hwnd, uMsg, wParam, lParam);
|
||||
}
|
||||
|
||||
pgpl_mutex_lock(window->window_proc_vector_lock);
|
||||
|
||||
switch (uMsg) {
|
||||
case WM_SETCURSOR:
|
||||
if (LOWORD(lParam) == HTCLIENT) {
|
||||
SetCursor(LoadCursor(NULL, IDC_ARROW));
|
||||
pgpl_mutex_unlock(window->window_proc_vector_lock);
|
||||
return TRUE;
|
||||
} else {
|
||||
pgpl_mutex_unlock(window->window_proc_vector_lock);
|
||||
return DefWindowProc(hwnd, uMsg, wParam, lParam);
|
||||
}
|
||||
break;
|
||||
case WM_KEYDOWN:
|
||||
event_type = PGPL_WINDOW_EVENT_KEY_PRESS;
|
||||
event_data.input_event.key = wParam;
|
||||
event_data.input_event.x = window->last_mouse_x;
|
||||
event_data.input_event.y = window->last_mouse_y;
|
||||
break;
|
||||
case WM_KEYUP:
|
||||
event_type = PGPL_WINDOW_EVENT_KEY_RELEASE;
|
||||
event_data.input_event.key = wParam;
|
||||
event_data.input_event.x = window->last_mouse_x;
|
||||
event_data.input_event.y = window->last_mouse_y;
|
||||
break;
|
||||
case WM_LBUTTONDOWN: {
|
||||
POINTS points = MAKEPOINTS(lParam);
|
||||
event_type = PGPL_WINDOW_EVENT_MOUSE_PRESS;
|
||||
event_data.input_event.key = 1;
|
||||
event_data.input_event.x = points.x;
|
||||
event_data.input_event.y = points.y;
|
||||
break;
|
||||
}
|
||||
case WM_LBUTTONUP: {
|
||||
POINTS points = MAKEPOINTS(lParam);
|
||||
event_type = PGPL_WINDOW_EVENT_MOUSE_RELEASE;
|
||||
event_data.input_event.key = 1;
|
||||
event_data.input_event.x = points.x;
|
||||
event_data.input_event.y = points.y;
|
||||
break;
|
||||
}
|
||||
case WM_MBUTTONDOWN: {
|
||||
POINTS points = MAKEPOINTS(lParam);
|
||||
event_type = PGPL_WINDOW_EVENT_MOUSE_PRESS;
|
||||
event_data.input_event.key = 2;
|
||||
event_data.input_event.x = points.x;
|
||||
event_data.input_event.y = points.y;
|
||||
break;
|
||||
}
|
||||
case WM_MBUTTONUP: {
|
||||
POINTS points = MAKEPOINTS(lParam);
|
||||
event_type = PGPL_WINDOW_EVENT_MOUSE_RELEASE;
|
||||
event_data.input_event.key = 2;
|
||||
event_data.input_event.x = points.x;
|
||||
event_data.input_event.y = points.y;
|
||||
break;
|
||||
}
|
||||
case WM_RBUTTONDOWN: {
|
||||
POINTS points = MAKEPOINTS(lParam);
|
||||
event_type = PGPL_WINDOW_EVENT_MOUSE_PRESS;
|
||||
event_data.input_event.key = 3;
|
||||
event_data.input_event.x = points.x;
|
||||
event_data.input_event.y = points.y;
|
||||
break;
|
||||
}
|
||||
case WM_RBUTTONUP: {
|
||||
POINTS points = MAKEPOINTS(lParam);
|
||||
event_type = PGPL_WINDOW_EVENT_MOUSE_RELEASE;
|
||||
event_data.input_event.key = 3;
|
||||
event_data.input_event.x = points.x;
|
||||
event_data.input_event.y = points.y;
|
||||
break;
|
||||
}
|
||||
case WM_MOUSEWHEEL: {
|
||||
/* TODO: This code probably doesn't work that well. Unlike X11, this only
|
||||
* returns a MOUSE_PRESS event, and if the scrolls are more granular, this
|
||||
* will scroll really fast.
|
||||
*/
|
||||
POINTS points = MAKEPOINTS(lParam);
|
||||
event_type = PGPL_WINDOW_EVENT_MOUSE_PRESS;
|
||||
event_data.input_event.x = points.x;
|
||||
event_data.input_event.y = points.y;
|
||||
event_data.input_event.key = GET_WHEEL_DELTA_WPARAM(wParam) > 0 ? 4 : 5;
|
||||
break;
|
||||
}
|
||||
case WM_MOUSEMOVE: {
|
||||
POINTS points = MAKEPOINTS(lParam);
|
||||
event_type = PGPL_WINDOW_EVENT_MOUSE_MOVE;
|
||||
window->last_mouse_x = points.x;
|
||||
window->last_mouse_y = points.y;
|
||||
event_data.input_event.key = -1;
|
||||
event_data.input_event.x = points.x;
|
||||
event_data.input_event.y = points.y;
|
||||
break;
|
||||
}
|
||||
case WM_CLOSE:
|
||||
event_type = PGPL_WINDOW_EVENT_CLOSE;
|
||||
break;
|
||||
default:
|
||||
event_type = PGPL_WINDOW_EVENT_NONE;
|
||||
pgpl_mutex_unlock(window->window_proc_vector_lock);
|
||||
return DefWindowProc(hwnd, uMsg, wParam, lParam);
|
||||
}
|
||||
|
||||
pgpl_vector_push_back(window->window_proc_event_type, &event_type);
|
||||
pgpl_vector_push_back(window->window_proc_event_data, &event_data);
|
||||
pgpl_mutex_unlock(window->window_proc_vector_lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
PGPL_Window *pgpl_window_create(const char *title, uint32_t width,
|
||||
uint32_t height, int32_t x, int32_t y) {
|
||||
PGPL_Window *window = malloc(sizeof(*window));
|
||||
int32_t pixel_format;
|
||||
|
||||
PIXELFORMATDESCRIPTOR pfd = {sizeof(PIXELFORMATDESCRIPTOR),
|
||||
1,
|
||||
PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL |
|
||||
PFD_DOUBLEBUFFER,
|
||||
PFD_TYPE_RGBA,
|
||||
32,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
24,
|
||||
8,
|
||||
0,
|
||||
PFD_MAIN_PLANE,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0};
|
||||
|
||||
/* TODO: Move into shared function? */
|
||||
window->shared.scale = 1.0;
|
||||
window->shared.event_data = pgpl_vector_create(sizeof(PGPL_WindowEventData));
|
||||
window->shared.event_types = pgpl_vector_create(sizeof(PGPL_WindowEventType));
|
||||
|
||||
window->window_proc_event_data =
|
||||
pgpl_vector_create(sizeof(PGPL_WindowEventData));
|
||||
window->window_proc_event_type =
|
||||
pgpl_vector_create(sizeof(PGPL_WindowEventType));
|
||||
|
||||
memset(&window->wc, 0, sizeof(window->wc));
|
||||
|
||||
window->window_proc_vector_lock = pgpl_mutex_create();
|
||||
|
||||
window->wc.lpfnWndProc = WindowProc;
|
||||
window->wc.cbWndExtra = sizeof(PGPL_Window *);
|
||||
window->wc.lpszClassName = "PGPL_Window Window";
|
||||
window->wc.style = CS_OWNDC;
|
||||
|
||||
RegisterClass(&window->wc);
|
||||
|
||||
window->hwnd =
|
||||
CreateWindowEx(0, window->wc.lpszClassName, title, WS_OVERLAPPEDWINDOW, x,
|
||||
y, width, height, NULL, NULL, window->wc.hInstance, NULL);
|
||||
|
||||
if (window->hwnd == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
SetWindowLongPtr(window->hwnd, 0, (LONG_PTR)window);
|
||||
SetWindowPos(window->hwnd, NULL, x, y, width, height, 0);
|
||||
|
||||
window->hdc = GetDC(window->hwnd);
|
||||
pixel_format = ChoosePixelFormat(window->hdc, &pfd);
|
||||
SetPixelFormat(window->hdc, pixel_format, &pfd);
|
||||
|
||||
window->wgl_context = wglCreateContext(window->hdc);
|
||||
|
||||
return window;
|
||||
}
|
||||
|
||||
void pgpl_window_destroy(PGPL_Window *window) {
|
||||
/* TODO: Also turn this into a generic function perhaps. */
|
||||
pgpl_vector_destroy(window->shared.event_data);
|
||||
pgpl_vector_destroy(window->shared.event_types);
|
||||
pgpl_mutex_destroy(window->window_proc_vector_lock);
|
||||
wglMakeCurrent(NULL, NULL);
|
||||
wglDeleteContext(window->wgl_context);
|
||||
ReleaseDC(window->hwnd, window->hdc);
|
||||
UnregisterClass(window->wc.lpszClassName, window->wc.hInstance);
|
||||
DestroyWindow(window->hwnd);
|
||||
}
|
||||
|
||||
void pgpl_window_show(PGPL_Window *window) {
|
||||
ShowWindow(window->hwnd, SW_SHOW);
|
||||
}
|
||||
|
||||
void pgpl_window_hide(PGPL_Window *window) {
|
||||
ShowWindow(window->hwnd, SW_HIDE);
|
||||
}
|
||||
|
||||
void pgpl_window_set_title(PGPL_Window *window, const char *title) {
|
||||
SetWindowText(window->hwnd, title);
|
||||
}
|
||||
|
||||
/* 128 is allowed as the max count, because it includes the null terminator */
|
||||
const char *pgpl_window_get_title(PGPL_Window *window) {
|
||||
GetWindowText(window->hwnd, window->title, 128);
|
||||
return window->title;
|
||||
}
|
||||
|
||||
void pgpl_window_get_size(PGPL_Window *window, uint32_t *width,
|
||||
uint32_t *height) {
|
||||
RECT rect;
|
||||
GetClientRect(window->hwnd, &rect);
|
||||
*width = rect.right - rect.left;
|
||||
*height = rect.bottom - rect.top;
|
||||
}
|
||||
|
||||
void pgpl_window_set_size(PGPL_Window *window, uint32_t width,
|
||||
uint32_t height) {
|
||||
SetWindowPos(window->hwnd, NULL, 0, 0, width, height, SWP_NOMOVE);
|
||||
}
|
||||
|
||||
void pgpl_window_get_position(PGPL_Window *window, int32_t *x, int32_t *y) {
|
||||
WINDOWPLACEMENT placement;
|
||||
GetWindowPlacement(window->hwnd, &placement);
|
||||
*x = placement.ptMinPosition.x;
|
||||
*y = placement.ptMinPosition.y;
|
||||
}
|
||||
|
||||
void pgpl_window_set_position(PGPL_Window *window, int32_t x, int32_t y) {
|
||||
SetWindowPos(window->hwnd, NULL, x, y, 0, 0, SWP_NOSIZE);
|
||||
}
|
||||
|
||||
/* Fetches an event, the mutex might be useless entirely. This basically calls
|
||||
* the WindowProc, and takes it's data back out to be returned. */
|
||||
PGPL_WindowEventType
|
||||
pgpl_window_internal_fetch_window_event(PGPL_Window *window,
|
||||
PGPL_WindowEventData *event_data) {
|
||||
MSG msg;
|
||||
PGPL_WindowEventType event_type = PGPL_WINDOW_EVENT_NONE;
|
||||
|
||||
if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) > 0) {
|
||||
TranslateMessage(&msg);
|
||||
DispatchMessage(&msg);
|
||||
}
|
||||
|
||||
pgpl_mutex_lock(window->window_proc_vector_lock);
|
||||
if (pgpl_vector_get_length(window->window_proc_event_type) > 0) {
|
||||
event_type = *(PGPL_WindowEventType *)pgpl_vector_pop_front(
|
||||
window->window_proc_event_type);
|
||||
*event_data = *(PGPL_WindowEventData *)pgpl_vector_pop_front(
|
||||
window->window_proc_event_data);
|
||||
}
|
||||
|
||||
pgpl_mutex_unlock(window->window_proc_vector_lock);
|
||||
|
||||
return event_type;
|
||||
}
|
||||
|
||||
/* Here we reparent the window, this is because under Windows, this creates a
|
||||
* subwindow of sorts on the other window, so this behaves "similarily" to the
|
||||
* X11 function. */
|
||||
void pgpl_window_set_transient(PGPL_Window *window, void *parent) {
|
||||
SetParent(window->hwnd, parent);
|
||||
}
|
||||
|
||||
/* These style changes are done to remove the borders around the window, which
|
||||
* are usually undesireable in reparenting contexts, especially when working
|
||||
* with audio plugins. */
|
||||
void pgpl_window_reparent_to(PGPL_Window *window, void *parent) {
|
||||
LONG style = GetWindowLongPtr(window->hwnd, GWL_STYLE);
|
||||
LONG ex_style = GetWindowLongPtr(window->hwnd, GWL_EXSTYLE);
|
||||
style &= ~(WS_CAPTION | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX |
|
||||
WS_SYSMENU);
|
||||
ex_style &= ~(WS_EX_DLGMODALFRAME | WS_EX_CLIENTEDGE | WS_EX_STATICEDGE);
|
||||
SetWindowLongPtr(window->hwnd, GWL_STYLE, style);
|
||||
SetWindowLongPtr(window->hwnd, GWL_EXSTYLE, ex_style);
|
||||
pgpl_window_set_transient(window, parent);
|
||||
}
|
||||
|
||||
void *pgpl_window_get_raw_window(PGPL_Window *window) { return window->hwnd; }
|
||||
|
||||
void pgpl_window_internal_start_render(PGPL_Window *window) {
|
||||
pgpl_mutex_lock(render_lock);
|
||||
wglMakeCurrent(window->hdc, window->wgl_context);
|
||||
}
|
||||
|
||||
void pgpl_window_internal_finish_render(PGPL_Window *window) {
|
||||
wglSwapLayerBuffers(window->hdc, WGL_SWAP_MAIN_PLANE);
|
||||
pgpl_mutex_unlock(render_lock);
|
||||
}
|
||||
|
||||
#endif
|
||||
232
src/window/window-x11.c
Normal file
232
src/window/window-x11.c
Normal file
|
|
@ -0,0 +1,232 @@
|
|||
#ifdef __unix__
|
||||
#include "internal.h"
|
||||
#include <GL/glx.h>
|
||||
#include <X11/Xlib.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
struct PGPL_Window {
|
||||
PGPL_WindowShared shared;
|
||||
Display *display;
|
||||
Window window;
|
||||
Atom wm_delete;
|
||||
GLXContext glx_context;
|
||||
char *title;
|
||||
bool reparented;
|
||||
};
|
||||
|
||||
/* Creates a basic X11 window with GLX. We select all the events that we handle
|
||||
* in here, and create the most basic GLX context that we need. */
|
||||
PGPL_Window *pgpl_window_create(const char *title, uint32_t width,
|
||||
uint32_t height, int32_t x, int32_t y) {
|
||||
PGPL_Window *window = malloc(sizeof(*window));
|
||||
|
||||
int32_t screen;
|
||||
Window root;
|
||||
XSetWindowAttributes window_attributes;
|
||||
|
||||
GLint attributes[5] = {GLX_RGBA, GLX_DOUBLEBUFFER, None};
|
||||
XVisualInfo *visual;
|
||||
|
||||
window->title = NULL;
|
||||
|
||||
/* TODO: Move into shared function? */
|
||||
window->shared.scale = 1.0;
|
||||
window->shared.event_data = pgpl_vector_create(sizeof(PGPL_WindowEventData));
|
||||
window->shared.event_types = pgpl_vector_create(sizeof(PGPL_WindowEventType));
|
||||
|
||||
window->reparented = 0;
|
||||
|
||||
window->display = XOpenDisplay(NULL);
|
||||
|
||||
screen = DefaultScreen(window->display);
|
||||
root = DefaultRootWindow(window->display);
|
||||
|
||||
visual = glXChooseVisual(window->display, screen, attributes);
|
||||
|
||||
window_attributes.colormap =
|
||||
XCreateColormap(window->display, root, visual->visual, AllocNone);
|
||||
window_attributes.event_mask = KeyPressMask | KeyReleaseMask |
|
||||
ButtonPressMask | ButtonReleaseMask |
|
||||
PointerMotionMask;
|
||||
|
||||
window->window = XCreateWindow(window->display, root, x, y, width, height, 0,
|
||||
visual->depth, InputOutput, visual->visual,
|
||||
CWColormap | CWEventMask, &window_attributes);
|
||||
|
||||
XStoreName(window->display, window->window, title);
|
||||
|
||||
/* If the window gets closed by the WM, this let's us catch that and
|
||||
* gracefully exit. */
|
||||
window->wm_delete = XInternAtom(window->display, "WM_DELETE_WINDOW", False);
|
||||
XSetWMProtocols(window->display, window->window, &window->wm_delete, 1);
|
||||
|
||||
window->glx_context =
|
||||
glXCreateContext(window->display, visual, NULL, GL_TRUE);
|
||||
|
||||
XFlush(window->display);
|
||||
|
||||
free(visual);
|
||||
|
||||
return window;
|
||||
}
|
||||
|
||||
void pgpl_window_destroy(PGPL_Window *window) {
|
||||
/* TODO: Also turn this into a generic function perhaps. */
|
||||
pgpl_vector_destroy(window->shared.event_data);
|
||||
pgpl_vector_destroy(window->shared.event_types);
|
||||
if (window->title != NULL) {
|
||||
XFree(window->title);
|
||||
}
|
||||
glXDestroyContext(window->display, window->glx_context);
|
||||
/* Can cause problems when reparented, so we just don't destroy the window if
|
||||
* we have been reparented. */
|
||||
if (!window->reparented) {
|
||||
XDestroyWindow(window->display, window->window);
|
||||
}
|
||||
XCloseDisplay(window->display);
|
||||
free(window);
|
||||
}
|
||||
|
||||
void pgpl_window_show(PGPL_Window *window) {
|
||||
XMapWindow(window->display, window->window);
|
||||
XFlush(window->display);
|
||||
}
|
||||
|
||||
void pgpl_window_hide(PGPL_Window *window) {
|
||||
XUnmapWindow(window->display, window->window);
|
||||
XFlush(window->display);
|
||||
}
|
||||
|
||||
void pgpl_window_set_title(PGPL_Window *window, const char *title) {
|
||||
XStoreName(window->display, window->window, title);
|
||||
XFlush(window->display);
|
||||
}
|
||||
|
||||
const char *pgpl_window_get_title(PGPL_Window *window) {
|
||||
if (window->title != NULL) {
|
||||
XFree(window->title);
|
||||
}
|
||||
XFetchName(window->display, window->window, &window->title);
|
||||
return window->title;
|
||||
}
|
||||
|
||||
void pgpl_window_get_size(PGPL_Window *window, uint32_t *width,
|
||||
uint32_t *height) {
|
||||
XWindowAttributes attributes;
|
||||
XGetWindowAttributes(window->display, window->window, &attributes);
|
||||
if (width != NULL) {
|
||||
*width = attributes.width;
|
||||
}
|
||||
if (height != NULL) {
|
||||
*height = attributes.height;
|
||||
}
|
||||
}
|
||||
|
||||
void pgpl_window_set_size(PGPL_Window *window, uint32_t width,
|
||||
uint32_t height) {
|
||||
XResizeWindow(window->display, window->window, width, height);
|
||||
XFlush(window->display);
|
||||
}
|
||||
|
||||
void pgpl_window_get_position(PGPL_Window *window, int32_t *x, int32_t *y) {
|
||||
XWindowAttributes attributes;
|
||||
XGetWindowAttributes(window->display, window->window, &attributes);
|
||||
if (x != NULL) {
|
||||
*x = attributes.x;
|
||||
}
|
||||
if (y != NULL) {
|
||||
*y = attributes.y;
|
||||
}
|
||||
}
|
||||
|
||||
void pgpl_window_set_position(PGPL_Window *window, int32_t x, int32_t y) {
|
||||
XMoveWindow(window->display, window->window, x, y);
|
||||
XFlush(window->display);
|
||||
}
|
||||
|
||||
/* This fetches the event from X11 and translates it to our own format, returns
|
||||
* an Error if the Event was unknown.
|
||||
*
|
||||
* TODO: Translate the keymaps */
|
||||
PGPL_WindowEventType
|
||||
pgpl_window_internal_fetch_window_event(PGPL_Window *window,
|
||||
PGPL_WindowEventData *event_data) {
|
||||
if (XPending(window->display)) {
|
||||
XEvent event;
|
||||
XNextEvent(window->display, &event);
|
||||
switch (event.type) {
|
||||
case KeyPress:
|
||||
if (event_data != NULL) {
|
||||
event_data->input_event.x = event.xkey.x;
|
||||
event_data->input_event.y = event.xkey.y;
|
||||
event_data->input_event.key = event.xkey.keycode;
|
||||
}
|
||||
return PGPL_WINDOW_EVENT_KEY_PRESS;
|
||||
break;
|
||||
case KeyRelease:
|
||||
if (event_data != NULL) {
|
||||
event_data->input_event.x = event.xkey.x;
|
||||
event_data->input_event.y = event.xkey.y;
|
||||
event_data->input_event.key = event.xkey.keycode;
|
||||
}
|
||||
return PGPL_WINDOW_EVENT_KEY_RELEASE;
|
||||
break;
|
||||
case ButtonPress:
|
||||
if (event_data != NULL) {
|
||||
event_data->input_event.x = event.xbutton.x;
|
||||
event_data->input_event.y = event.xbutton.y;
|
||||
event_data->input_event.key = event.xbutton.button;
|
||||
}
|
||||
return PGPL_WINDOW_EVENT_MOUSE_PRESS;
|
||||
break;
|
||||
case ButtonRelease:
|
||||
if (event_data != NULL) {
|
||||
event_data->input_event.x = event.xbutton.x;
|
||||
event_data->input_event.y = event.xbutton.y;
|
||||
event_data->input_event.key = event.xbutton.button;
|
||||
}
|
||||
return PGPL_WINDOW_EVENT_MOUSE_RELEASE;
|
||||
break;
|
||||
case MotionNotify:
|
||||
if (event_data != NULL) {
|
||||
event_data->input_event.x = event.xbutton.x;
|
||||
event_data->input_event.y = event.xbutton.y;
|
||||
event_data->input_event.key = 0;
|
||||
}
|
||||
return PGPL_WINDOW_EVENT_MOUSE_MOVE;
|
||||
break;
|
||||
case ClientMessage:
|
||||
if (event.xclient.data.l[0] == (long)window->wm_delete)
|
||||
return PGPL_WINDOW_EVENT_CLOSE;
|
||||
return PGPL_WINDOW_EVENT_ERROR;
|
||||
break;
|
||||
default:
|
||||
return PGPL_WINDOW_EVENT_ERROR;
|
||||
}
|
||||
}
|
||||
return PGPL_WINDOW_EVENT_NONE;
|
||||
}
|
||||
|
||||
void pgpl_window_set_transient(PGPL_Window *window, void *parent) {
|
||||
XSetTransientForHint(window->display, window->window, *(Window *)parent);
|
||||
}
|
||||
|
||||
void pgpl_window_reparent_to(PGPL_Window *window, void *parent) {
|
||||
XReparentWindow(window->display, window->window, *(Window *)parent, 0, 0);
|
||||
window->reparented = true;
|
||||
}
|
||||
|
||||
void *pgpl_window_get_raw_window(PGPL_Window *window) {
|
||||
return &window->window;
|
||||
}
|
||||
|
||||
void pgpl_window_internal_start_render(PGPL_Window *window) {
|
||||
pgpl_mutex_lock(render_lock);
|
||||
glXMakeCurrent(window->display, window->window, window->glx_context);
|
||||
}
|
||||
|
||||
void pgpl_window_internal_finish_render(PGPL_Window *window) {
|
||||
glXSwapBuffers(window->display, window->window);
|
||||
pgpl_mutex_unlock(render_lock);
|
||||
}
|
||||
#endif
|
||||
93
src/window/window.c
Normal file
93
src/window/window.c
Normal file
|
|
@ -0,0 +1,93 @@
|
|||
#include "internal.h"
|
||||
#include <GL/gl.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
/* Platform agnostic functions for our PGPL_Window "class" */
|
||||
|
||||
void pgpl_window_set_scale(PGPL_Window *window, double scale) {
|
||||
PGPL_WindowShared *window_shared = (PGPL_WindowShared *)window;
|
||||
window_shared->scale = scale;
|
||||
}
|
||||
|
||||
double pgpl_window_get_scale(PGPL_Window *window) {
|
||||
PGPL_WindowShared *window_shared = (PGPL_WindowShared *)window;
|
||||
return window_shared->scale;
|
||||
}
|
||||
|
||||
PGPL_WindowEventType pgpl_window_fetch_event(PGPL_Window *window,
|
||||
PGPL_WindowEventData *event_data) {
|
||||
PGPL_WindowShared *window_shared = (PGPL_WindowShared *)window;
|
||||
PGPL_WindowEventType event_type;
|
||||
|
||||
while ((event_type = pgpl_window_internal_fetch_window_event(
|
||||
window, event_data)) != PGPL_WINDOW_EVENT_NONE) {
|
||||
if (event_type == PGPL_WINDOW_EVENT_MOUSE_PRESS ||
|
||||
event_type == PGPL_WINDOW_EVENT_MOUSE_RELEASE ||
|
||||
event_type == PGPL_WINDOW_EVENT_KEY_PRESS ||
|
||||
event_type == PGPL_WINDOW_EVENT_KEY_RELEASE ||
|
||||
event_type == PGPL_WINDOW_EVENT_MOUSE_MOVE) {
|
||||
event_data->input_event.x /= window_shared->scale;
|
||||
event_data->input_event.y /= window_shared->scale;
|
||||
}
|
||||
pgpl_vector_push_back(window_shared->event_types, &event_type);
|
||||
pgpl_vector_push_back(window_shared->event_data, event_data);
|
||||
}
|
||||
|
||||
if (pgpl_vector_get_length(window_shared->event_types) > 0) {
|
||||
*event_data = *(PGPL_WindowEventData *)pgpl_vector_pop_front(
|
||||
window_shared->event_data);
|
||||
event_type = *(PGPL_WindowEventType *)pgpl_vector_pop_front(
|
||||
window_shared->event_types);
|
||||
return event_type;
|
||||
} else {
|
||||
return PGPL_WINDOW_EVENT_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
void pgpl_window_create_event(PGPL_Window *window,
|
||||
PGPL_WindowEventType event_type,
|
||||
PGPL_WindowEventData *event_data) {
|
||||
PGPL_WindowShared *window_shared = (PGPL_WindowShared *)window;
|
||||
pgpl_vector_push_back(window_shared->event_types, &event_type);
|
||||
if (event_data == NULL) {
|
||||
PGPL_WindowEventData data;
|
||||
pgpl_vector_push_back(window_shared->event_data, &data);
|
||||
} else {
|
||||
pgpl_vector_push_back(window_shared->event_data, &event_data);
|
||||
}
|
||||
}
|
||||
|
||||
/* Enables important OpenGL settings, like transparency support, and sets up our
|
||||
* projection matrix to be easier to work with. */
|
||||
PGPL_Renderer *pgpl_window_start_render(PGPL_Window *window, PGPL_Color color) {
|
||||
PGPL_WindowShared *window_shared = (PGPL_WindowShared *)window;
|
||||
PGPL_Renderer *renderer = malloc(sizeof(*renderer));
|
||||
pgpl_window_internal_start_render(window);
|
||||
|
||||
glEnable(GL_TEXTURE_2D);
|
||||
glEnable(GL_BLEND);
|
||||
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
|
||||
|
||||
pgpl_window_get_size(window, &renderer->width, &renderer->height);
|
||||
glViewport(0, 0, renderer->width, renderer->height);
|
||||
|
||||
glPushMatrix();
|
||||
glOrtho(0.0, 1.0, 1.0, 0.0, 0.0, 1.0);
|
||||
glScalef(window_shared->scale, window_shared->scale, window_shared->scale);
|
||||
|
||||
glClearColor(
|
||||
pgpl_color_get_red(color) / 255.0, pgpl_color_get_green(color) / 255.0,
|
||||
pgpl_color_get_blue(color) / 255.0, pgpl_color_get_alpha(color) / 255.0);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
|
||||
return renderer;
|
||||
}
|
||||
|
||||
void pgpl_window_finish_render(PGPL_Window *window, PGPL_Renderer *renderer) {
|
||||
glPopMatrix();
|
||||
pgpl_window_internal_finish_render(window);
|
||||
free(renderer);
|
||||
}
|
||||
17
tests/clap/all.h
Normal file
17
tests/clap/all.h
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
#pragma once
|
||||
|
||||
#include "clap.h"
|
||||
|
||||
#include "factory/draft/plugin-invalidation.h"
|
||||
#include "factory/draft/plugin-state-converter.h"
|
||||
|
||||
#include "ext/draft/extensible-audio-ports.h"
|
||||
#include "ext/draft/gain-adjustment-metering.h"
|
||||
#include "ext/draft/mini-curve-display.h"
|
||||
#include "ext/draft/project-location.h"
|
||||
#include "ext/draft/resource-directory.h"
|
||||
#include "ext/draft/scratch-memory.h"
|
||||
#include "ext/draft/transport-control.h"
|
||||
#include "ext/draft/triggers.h"
|
||||
#include "ext/draft/tuning.h"
|
||||
#include "ext/draft/undo.h"
|
||||
37
tests/clap/audio-buffer.h
Normal file
37
tests/clap/audio-buffer.h
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
#pragma once
|
||||
|
||||
#include "private/std.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// Sample code for reading a stereo buffer:
|
||||
//
|
||||
// bool isLeftConstant = (buffer->constant_mask & (1 << 0)) != 0;
|
||||
// bool isRightConstant = (buffer->constant_mask & (1 << 1)) != 0;
|
||||
//
|
||||
// for (int i = 0; i < N; ++i) {
|
||||
// float l = data32[0][isLeftConstant ? 0 : i];
|
||||
// float r = data32[1][isRightConstant ? 0 : i];
|
||||
// }
|
||||
//
|
||||
// Note: checking the constant mask is optional, and this implies that
|
||||
// the buffer must be filled with the constant value.
|
||||
// Rationale: if a buffer reader doesn't check the constant mask, then it may
|
||||
// process garbage samples and in result, garbage samples may be transmitted
|
||||
// to the audio interface with all the bad consequences it can have.
|
||||
//
|
||||
// The constant mask is a hint.
|
||||
typedef struct clap_audio_buffer {
|
||||
// Either data32 or data64 pointer will be set.
|
||||
float **data32;
|
||||
double **data64;
|
||||
uint32_t channel_count;
|
||||
uint32_t latency; // latency from/to the audio interface
|
||||
uint64_t constant_mask;
|
||||
} clap_audio_buffer_t;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
64
tests/clap/clap.h
Normal file
64
tests/clap/clap.h
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
/*
|
||||
* CLAP - CLever Audio Plugin
|
||||
* ~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
*
|
||||
* Copyright (c) 2014...2022 Alexandre BIQUE <bique.alexandre@gmail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "entry.h"
|
||||
|
||||
#include "factory/plugin-factory.h"
|
||||
#include "factory/preset-discovery.h"
|
||||
|
||||
#include "plugin.h"
|
||||
#include "plugin-features.h"
|
||||
#include "host.h"
|
||||
#include "universal-plugin-id.h"
|
||||
|
||||
#include "ext/ambisonic.h"
|
||||
#include "ext/audio-ports-activation.h"
|
||||
#include "ext/audio-ports-config.h"
|
||||
#include "ext/audio-ports.h"
|
||||
#include "ext/configurable-audio-ports.h"
|
||||
#include "ext/context-menu.h"
|
||||
#include "ext/event-registry.h"
|
||||
#include "ext/gui.h"
|
||||
#include "ext/latency.h"
|
||||
#include "ext/log.h"
|
||||
#include "ext/note-name.h"
|
||||
#include "ext/note-ports.h"
|
||||
#include "ext/param-indication.h"
|
||||
#include "ext/params.h"
|
||||
#include "ext/posix-fd-support.h"
|
||||
#include "ext/preset-load.h"
|
||||
#include "ext/remote-controls.h"
|
||||
#include "ext/render.h"
|
||||
#include "ext/state-context.h"
|
||||
#include "ext/state.h"
|
||||
#include "ext/surround.h"
|
||||
#include "ext/tail.h"
|
||||
#include "ext/thread-check.h"
|
||||
#include "ext/thread-pool.h"
|
||||
#include "ext/timer-support.h"
|
||||
#include "ext/track-info.h"
|
||||
#include "ext/voice-info.h"
|
||||
20
tests/clap/color.h
Normal file
20
tests/clap/color.h
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
#pragma once
|
||||
|
||||
#include "private/std.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct clap_color {
|
||||
uint8_t alpha;
|
||||
uint8_t red;
|
||||
uint8_t green;
|
||||
uint8_t blue;
|
||||
} clap_color_t;
|
||||
|
||||
static const CLAP_CONSTEXPR clap_color_t CLAP_COLOR_TRANSPARENT = { 0, 0, 0, 0 };
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
136
tests/clap/entry.h
Normal file
136
tests/clap/entry.h
Normal file
|
|
@ -0,0 +1,136 @@
|
|||
#pragma once
|
||||
|
||||
#include "version.h"
|
||||
#include "private/macros.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// This interface is the entry point of the dynamic library.
|
||||
//
|
||||
// CLAP plugins standard search path:
|
||||
//
|
||||
// Linux
|
||||
// - ~/.clap
|
||||
// - /usr/lib/clap
|
||||
//
|
||||
// Windows
|
||||
// - %COMMONPROGRAMFILES%\CLAP
|
||||
// - %LOCALAPPDATA%\Programs\Common\CLAP
|
||||
//
|
||||
// MacOS
|
||||
// - /Library/Audio/Plug-Ins/CLAP
|
||||
// - ~/Library/Audio/Plug-Ins/CLAP
|
||||
//
|
||||
// In addition to the OS-specific default locations above, a CLAP host must query the environment
|
||||
// for a CLAP_PATH variable, which is a list of directories formatted in the same manner as the host
|
||||
// OS binary search path (PATH on Unix, separated by `:` and Path on Windows, separated by ';', as
|
||||
// of this writing).
|
||||
//
|
||||
// Each directory should be recursively searched for files and/or bundles as appropriate in your OS
|
||||
// ending with the extension `.clap`.
|
||||
//
|
||||
// init and deinit in most cases are called once, in a matched pair, when the dso is loaded / unloaded.
|
||||
// In some rare situations it may be called multiple times in a process, so the functions must be defensive,
|
||||
// mutex locking and counting calls if undertaking non trivial non idempotent actions.
|
||||
//
|
||||
// Rationale:
|
||||
//
|
||||
// The intent of the init() and deinit() functions is to provide a "normal" initialization patterh
|
||||
// which occurs when the shared object is loaded or unloaded. As such, hosts will call each once and
|
||||
// in matched pairs. In CLAP specifications prior to 1.2.0, this single-call was documented as a
|
||||
// requirement.
|
||||
//
|
||||
// We realized, though, that this is not a requirement hosts can meet. If hosts load a plugin
|
||||
// which itself wraps another CLAP for instance, while also loading that same clap in its memory
|
||||
// space, both the host and the wrapper will call init() and deinit() and have no means to communicate
|
||||
// the state.
|
||||
//
|
||||
// With CLAP 1.2.0 and beyond we are changing the spec to indicate that a host should make an
|
||||
// absolute best effort to call init() and deinit() once, and always in matched pairs (for every
|
||||
// init() which returns true, one deinit() should be called).
|
||||
//
|
||||
// This takes the de-facto burden on plugin writers to deal with multiple calls into a hard requirement.
|
||||
//
|
||||
// Most init() / deinit() pairs we have seen are the relatively trivial {return true;} and {}. But
|
||||
// if your init() function does non-trivial one time work, the plugin author must maintain a counter
|
||||
// and must manage a mutex lock. The most obvious implementation will maintain a static counter and a
|
||||
// global mutex, increment the counter on each init, decrement it on each deinit, and only undertake
|
||||
// the init or deinit action when the counter is zero.
|
||||
typedef struct clap_plugin_entry {
|
||||
clap_version_t clap_version; // initialized to CLAP_VERSION
|
||||
|
||||
// Initializes the DSO.
|
||||
//
|
||||
// This function must be called first, before any-other CLAP-related function or symbol from this
|
||||
// DSO.
|
||||
//
|
||||
// It also must only be called once, until a later call to deinit() is made, after which init()
|
||||
// can be called once more to re-initialize the DSO.
|
||||
// This enables hosts to e.g. quickly load and unload a DSO for scanning its plugins, and then
|
||||
// load it again later to actually use the plugins if needed.
|
||||
//
|
||||
// As stated above, even though hosts are forbidden to do so directly, multiple calls before any
|
||||
// deinit() call may still happen. Implementations *should* take this into account, and *must*
|
||||
// do so as of CLAP 1.2.0.
|
||||
//
|
||||
// It should be as fast as possible, in order to perform a very quick scan of the plugin
|
||||
// descriptors.
|
||||
//
|
||||
// It is forbidden to display graphical user interfaces in this call.
|
||||
// It is forbidden to perform any user interaction in this call.
|
||||
//
|
||||
// If the initialization depends upon expensive computation, maybe try to do them ahead of time
|
||||
// and cache the result.
|
||||
//
|
||||
// Returns true on success. If init() returns false, then the DSO must be considered
|
||||
// uninitialized, and the host must not call deinit() nor any other CLAP-related symbols from the
|
||||
// DSO.
|
||||
// This function also returns true in the case where the DSO is already initialized, and no
|
||||
// actual initialization work is done in this call, as explain above.
|
||||
//
|
||||
// plugin_path is the path to the DSO (Linux, Windows), or the bundle (macOS).
|
||||
//
|
||||
// This function may be called on any thread, including a different one from the one a later call
|
||||
// to deinit() (or a later init()) can be made.
|
||||
// However, it is forbidden to call this function simultaneously from multiple threads.
|
||||
// It is also forbidden to call it simultaneously with *any* other CLAP-related symbols from the
|
||||
// DSO, including (but not limited to) deinit().
|
||||
bool(CLAP_ABI *init)(const char *plugin_path);
|
||||
|
||||
// De-initializes the DSO, freeing any resources allocated or initialized by init().
|
||||
//
|
||||
// After this function is called, no more calls into the DSO must be made, except calling init()
|
||||
// again to re-initialize the DSO.
|
||||
// This means that after deinit() is called, the DSO can be considered to be in the same state
|
||||
// as if init() was never called at all yet, enabling it to be re-initialized as needed.
|
||||
//
|
||||
// As stated above, even though hosts are forbidden to do so directly, multiple calls before any
|
||||
// new init() call may still happen. Implementations *should* take this into account, and *must*
|
||||
// do so as of CLAP 1.2.0.
|
||||
//
|
||||
// Just like init(), this function may be called on any thread, including a different one from
|
||||
// the one init() was called from, or from the one a later init() call can be made.
|
||||
// However, it is forbidden to call this function simultaneously from multiple threads.
|
||||
// It is also forbidden to call it simultaneously with *any* other CLAP-related symbols from the
|
||||
// DSO, including (but not limited to) deinit().
|
||||
void(CLAP_ABI *deinit)(void);
|
||||
|
||||
// Get the pointer to a factory. See factory/plugin-factory.h for an example.
|
||||
//
|
||||
// Returns null if the factory is not provided.
|
||||
// The returned pointer must *not* be freed by the caller.
|
||||
//
|
||||
// Unlike init() and deinit(), this function can be called simultaneously by multiple threads.
|
||||
//
|
||||
// [thread-safe]
|
||||
const void *(CLAP_ABI *get_factory)(const char *factory_id);
|
||||
} clap_plugin_entry_t;
|
||||
|
||||
/* Entry point */
|
||||
CLAP_EXPORT extern const clap_plugin_entry_t clap_entry;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
367
tests/clap/events.h
Normal file
367
tests/clap/events.h
Normal file
|
|
@ -0,0 +1,367 @@
|
|||
#pragma once
|
||||
|
||||
#include "private/std.h"
|
||||
#include "fixedpoint.h"
|
||||
#include "id.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// event header
|
||||
// All clap events start with an event header to determine the overall
|
||||
// size of the event and its type and space (a namespacing for types).
|
||||
// clap_event objects are contiguous regions of memory which can be copied
|
||||
// with a memcpy of `size` bytes starting at the top of the header. As
|
||||
// such, be very careful when designing clap events with internal pointers
|
||||
// and other non-value-types to consider the lifetime of those members.
|
||||
typedef struct clap_event_header {
|
||||
uint32_t size; // event size including this header, eg: sizeof (clap_event_note)
|
||||
uint32_t time; // sample offset within the buffer for this event
|
||||
uint16_t space_id; // event space, see clap_host_event_registry
|
||||
uint16_t type; // event type
|
||||
uint32_t flags; // see clap_event_flags
|
||||
} clap_event_header_t;
|
||||
|
||||
// The clap core event space
|
||||
static const CLAP_CONSTEXPR uint16_t CLAP_CORE_EVENT_SPACE_ID = 0;
|
||||
|
||||
enum clap_event_flags {
|
||||
// Indicate a live user event, for example a user turning a physical knob
|
||||
// or playing a physical key.
|
||||
CLAP_EVENT_IS_LIVE = 1 << 0,
|
||||
|
||||
// Indicate that the event should not be recorded.
|
||||
// For example this is useful when a parameter changes because of a MIDI CC,
|
||||
// because if the host records both the MIDI CC automation and the parameter
|
||||
// automation there will be a conflict.
|
||||
CLAP_EVENT_DONT_RECORD = 1 << 1,
|
||||
};
|
||||
|
||||
// Some of the following events overlap, a note on can be expressed with:
|
||||
// - CLAP_EVENT_NOTE_ON
|
||||
// - CLAP_EVENT_MIDI
|
||||
// - CLAP_EVENT_MIDI2
|
||||
//
|
||||
// The preferred way of sending a note event is to use CLAP_EVENT_NOTE_*.
|
||||
//
|
||||
// The same event must not be sent twice: it is forbidden to send a the same note on
|
||||
// encoded with both CLAP_EVENT_NOTE_ON and CLAP_EVENT_MIDI.
|
||||
//
|
||||
// The plugins are encouraged to be able to handle note events encoded as raw midi or midi2,
|
||||
// or implement clap_plugin_event_filter and reject raw midi and midi2 events.
|
||||
enum {
|
||||
// NOTE_ON and NOTE_OFF represent a key pressed and key released event, respectively.
|
||||
// A NOTE_ON with a velocity of 0 is valid and should not be interpreted as a NOTE_OFF.
|
||||
//
|
||||
// NOTE_CHOKE is meant to choke the voice(s), like in a drum machine when a closed hihat
|
||||
// chokes an open hihat. This event can be sent by the host to the plugin. Here are two use
|
||||
// cases:
|
||||
// - a plugin is inside a drum pad in Bitwig Studio's drum machine, and this pad is choked by
|
||||
// another one
|
||||
// - the user double-clicks the DAW's stop button in the transport which then stops the sound on
|
||||
// every track
|
||||
//
|
||||
// NOTE_END is sent by the plugin to the host. The port, channel, key and note_id are those given
|
||||
// by the host in the NOTE_ON event. In other words, this event is matched against the
|
||||
// plugin's note input port.
|
||||
// NOTE_END is useful to help the host to match the plugin's voice life time.
|
||||
//
|
||||
// When using polyphonic modulations, the host has to allocate and release voices for its
|
||||
// polyphonic modulator. Yet only the plugin effectively knows when the host should terminate
|
||||
// a voice. NOTE_END solves that issue in a non-intrusive and cooperative way.
|
||||
//
|
||||
// CLAP assumes that the host will allocate a unique voice on NOTE_ON event for a given port,
|
||||
// channel and key. This voice will run until the plugin will instruct the host to terminate
|
||||
// it by sending a NOTE_END event.
|
||||
//
|
||||
// Consider the following sequence:
|
||||
// - process()
|
||||
// Host->Plugin NoteOn(port:0, channel:0, key:16, time:t0)
|
||||
// Host->Plugin NoteOn(port:0, channel:0, key:64, time:t0)
|
||||
// Host->Plugin NoteOff(port:0, channel:0, key:16, t1)
|
||||
// Host->Plugin NoteOff(port:0, channel:0, key:64, t1)
|
||||
// # on t2, both notes did terminate
|
||||
// Host->Plugin NoteOn(port:0, channel:0, key:64, t3)
|
||||
// # Here the plugin finished processing all the frames and will tell the host
|
||||
// # to terminate the voice on key 16 but not 64, because a note has been started at t3
|
||||
// Plugin->Host NoteEnd(port:0, channel:0, key:16, time:ignored)
|
||||
//
|
||||
// These four events use clap_event_note.
|
||||
CLAP_EVENT_NOTE_ON = 0,
|
||||
CLAP_EVENT_NOTE_OFF = 1,
|
||||
CLAP_EVENT_NOTE_CHOKE = 2,
|
||||
CLAP_EVENT_NOTE_END = 3,
|
||||
|
||||
// Represents a note expression.
|
||||
// Uses clap_event_note_expression.
|
||||
CLAP_EVENT_NOTE_EXPRESSION = 4,
|
||||
|
||||
// PARAM_VALUE sets the parameter's value; uses clap_event_param_value.
|
||||
// PARAM_MOD sets the parameter's modulation amount; uses clap_event_param_mod.
|
||||
//
|
||||
// The value heard is: param_value + param_mod.
|
||||
//
|
||||
// In case of a concurrent global value/modulation versus a polyphonic one,
|
||||
// the voice should only use the polyphonic one and the polyphonic modulation
|
||||
// amount will already include the monophonic signal.
|
||||
CLAP_EVENT_PARAM_VALUE = 5,
|
||||
CLAP_EVENT_PARAM_MOD = 6,
|
||||
|
||||
// Indicates that the user started or finished adjusting a knob.
|
||||
// This is not mandatory to wrap parameter changes with gesture events, but this improves
|
||||
// the user experience a lot when recording automation or overriding automation playback.
|
||||
// Uses clap_event_param_gesture.
|
||||
CLAP_EVENT_PARAM_GESTURE_BEGIN = 7,
|
||||
CLAP_EVENT_PARAM_GESTURE_END = 8,
|
||||
|
||||
CLAP_EVENT_TRANSPORT = 9, // update the transport info; clap_event_transport
|
||||
CLAP_EVENT_MIDI = 10, // raw midi event; clap_event_midi
|
||||
CLAP_EVENT_MIDI_SYSEX = 11, // raw midi sysex event; clap_event_midi_sysex
|
||||
CLAP_EVENT_MIDI2 = 12, // raw midi 2 event; clap_event_midi2
|
||||
};
|
||||
|
||||
// Note on, off, end and choke events.
|
||||
//
|
||||
// Clap addresses notes and voices using the 4-value tuple
|
||||
// (port, channel, key, note_id). Note on/off/end/choke
|
||||
// events and parameter modulation messages are delivered with
|
||||
// these values populated.
|
||||
//
|
||||
// Values in a note and voice address are either >= 0 if they
|
||||
// are specified, or -1 to indicate a wildcard. A wildcard
|
||||
// means a voice with any value in that part of the tuple
|
||||
// matches the message.
|
||||
//
|
||||
// For instance, a (PCKN) of (0, 3, -1, -1) will match all voices
|
||||
// on channel 3 of port 0. And a PCKN of (-1, 0, 60, -1) will match
|
||||
// all channel 0 key 60 voices, independent of port or note id.
|
||||
//
|
||||
// Especially in the case of note-on note-off pairs, and in the
|
||||
// absence of voice stacking or polyphonic modulation, a host may
|
||||
// choose to issue a note id only at note on. So you may see a
|
||||
// message stream like
|
||||
//
|
||||
// CLAP_EVENT_NOTE_ON [0,0,60,184]
|
||||
// CLAP_EVENT_NOTE_OFF [0,0,60,-1]
|
||||
//
|
||||
// and the host will expect the first voice to be released.
|
||||
// Well constructed plugins will search for voices and notes using
|
||||
// the entire tuple.
|
||||
//
|
||||
// In the case of note on events:
|
||||
// - The port, channel and key must be specified with a value >= 0
|
||||
// - A note-on event with a '-1' for port, channel or key is invalid and
|
||||
// can be rejected or ignored by a plugin or host.
|
||||
// - A host which does not support note ids should set the note id to -1.
|
||||
//
|
||||
// In the case of note choke or end events:
|
||||
// - the velocity is ignored.
|
||||
// - key and channel are used to match active notes
|
||||
// - note_id is optionally provided by the host
|
||||
typedef struct clap_event_note {
|
||||
clap_event_header_t header;
|
||||
|
||||
int32_t note_id; // host provided note id >= 0, or -1 if unspecified or wildcard
|
||||
int16_t port_index; // port index from ext/note-ports; -1 for wildcard
|
||||
int16_t channel; // 0..15, same as MIDI1 Channel Number, -1 for wildcard
|
||||
int16_t key; // 0..127, same as MIDI1 Key Number (60==Middle C), -1 for wildcard
|
||||
double velocity; // 0..1
|
||||
} clap_event_note_t;
|
||||
|
||||
// Note Expressions are well named modifications of a voice targeted to
|
||||
// voices using the same wildcard rules described above. Note Expressions are delivered
|
||||
// as sample accurate events and should be applied at the sample when received.
|
||||
//
|
||||
// Note expressions are a statement of value, not cumulative. A PAN event of 0 followed by 1
|
||||
// followed by 0.5 would pan hard left, hard right, and center. They are intended as
|
||||
// an offset from the non-note-expression voice default. A voice which had a volume of
|
||||
// -20db absent note expressions which received a +4db note expression would move the
|
||||
// voice to -16db.
|
||||
//
|
||||
// A plugin which receives a note expression at the same sample as a NOTE_ON event
|
||||
// should apply that expression to all generated samples. A plugin which receives
|
||||
// a note expression after a NOTE_ON event should initiate the voice with default
|
||||
// values and then apply the note expression when received. A plugin may make a choice
|
||||
// to smooth note expression streams.
|
||||
enum {
|
||||
// with 0 < x <= 4, plain = 20 * log(x)
|
||||
CLAP_NOTE_EXPRESSION_VOLUME = 0,
|
||||
|
||||
// pan, 0 left, 0.5 center, 1 right
|
||||
CLAP_NOTE_EXPRESSION_PAN = 1,
|
||||
|
||||
// Relative tuning in semitones, from -120 to +120. Semitones are in
|
||||
// equal temperament and are doubles; the resulting note would be
|
||||
// retuned by `100 * evt->value` cents.
|
||||
CLAP_NOTE_EXPRESSION_TUNING = 2,
|
||||
|
||||
// 0..1
|
||||
CLAP_NOTE_EXPRESSION_VIBRATO = 3,
|
||||
CLAP_NOTE_EXPRESSION_EXPRESSION = 4,
|
||||
CLAP_NOTE_EXPRESSION_BRIGHTNESS = 5,
|
||||
CLAP_NOTE_EXPRESSION_PRESSURE = 6,
|
||||
};
|
||||
typedef int32_t clap_note_expression;
|
||||
|
||||
typedef struct clap_event_note_expression {
|
||||
clap_event_header_t header;
|
||||
|
||||
clap_note_expression expression_id;
|
||||
|
||||
// target a specific note_id, port, key and channel, with
|
||||
// -1 meaning wildcard, per the wildcard discussion above
|
||||
int32_t note_id;
|
||||
int16_t port_index;
|
||||
int16_t channel;
|
||||
int16_t key;
|
||||
|
||||
double value; // see expression for the range
|
||||
} clap_event_note_expression_t;
|
||||
|
||||
typedef struct clap_event_param_value {
|
||||
clap_event_header_t header;
|
||||
|
||||
// target parameter
|
||||
clap_id param_id; // @ref clap_param_info.id
|
||||
void *cookie; // @ref clap_param_info.cookie
|
||||
|
||||
// target a specific note_id, port, key and channel, with
|
||||
// -1 meaning wildcard, per the wildcard discussion above
|
||||
int32_t note_id;
|
||||
int16_t port_index;
|
||||
int16_t channel;
|
||||
int16_t key;
|
||||
|
||||
double value;
|
||||
} clap_event_param_value_t;
|
||||
|
||||
typedef struct clap_event_param_mod {
|
||||
clap_event_header_t header;
|
||||
|
||||
// target parameter
|
||||
clap_id param_id; // @ref clap_param_info.id
|
||||
void *cookie; // @ref clap_param_info.cookie
|
||||
|
||||
// target a specific note_id, port, key and channel, with
|
||||
// -1 meaning wildcard, per the wildcard discussion above
|
||||
int32_t note_id;
|
||||
int16_t port_index;
|
||||
int16_t channel;
|
||||
int16_t key;
|
||||
|
||||
double amount; // modulation amount
|
||||
} clap_event_param_mod_t;
|
||||
|
||||
typedef struct clap_event_param_gesture {
|
||||
clap_event_header_t header;
|
||||
|
||||
// target parameter
|
||||
clap_id param_id; // @ref clap_param_info.id
|
||||
} clap_event_param_gesture_t;
|
||||
|
||||
enum clap_transport_flags {
|
||||
CLAP_TRANSPORT_HAS_TEMPO = 1 << 0,
|
||||
CLAP_TRANSPORT_HAS_BEATS_TIMELINE = 1 << 1,
|
||||
CLAP_TRANSPORT_HAS_SECONDS_TIMELINE = 1 << 2,
|
||||
CLAP_TRANSPORT_HAS_TIME_SIGNATURE = 1 << 3,
|
||||
CLAP_TRANSPORT_IS_PLAYING = 1 << 4,
|
||||
CLAP_TRANSPORT_IS_RECORDING = 1 << 5,
|
||||
CLAP_TRANSPORT_IS_LOOP_ACTIVE = 1 << 6,
|
||||
CLAP_TRANSPORT_IS_WITHIN_PRE_ROLL = 1 << 7,
|
||||
};
|
||||
|
||||
// clap_event_transport provides song position, tempo, and similar information
|
||||
// from the host to the plugin. There are two ways a host communicates these values.
|
||||
// In the `clap_process` structure sent to each processing block, the host may
|
||||
// provide a transport structure which indicates the available information at the
|
||||
// start of the block. If the host provides sample-accurate tempo or transport changes,
|
||||
// it can also provide subsequent inter-block transport updates by delivering a new event.
|
||||
typedef struct clap_event_transport {
|
||||
clap_event_header_t header;
|
||||
|
||||
uint32_t flags; // see clap_transport_flags
|
||||
|
||||
clap_beattime song_pos_beats; // position in beats
|
||||
clap_sectime song_pos_seconds; // position in seconds
|
||||
|
||||
double tempo; // in bpm
|
||||
double tempo_inc; // tempo increment for each sample and until the next
|
||||
// time info event
|
||||
|
||||
clap_beattime loop_start_beats;
|
||||
clap_beattime loop_end_beats;
|
||||
clap_sectime loop_start_seconds;
|
||||
clap_sectime loop_end_seconds;
|
||||
|
||||
clap_beattime bar_start; // start pos of the current bar
|
||||
int32_t bar_number; // bar at song pos 0 has the number 0
|
||||
|
||||
uint16_t tsig_num; // time signature numerator
|
||||
uint16_t tsig_denom; // time signature denominator
|
||||
} clap_event_transport_t;
|
||||
|
||||
typedef struct clap_event_midi {
|
||||
clap_event_header_t header;
|
||||
|
||||
uint16_t port_index;
|
||||
uint8_t data[3];
|
||||
} clap_event_midi_t;
|
||||
|
||||
// clap_event_midi_sysex contains a pointer to a sysex contents buffer.
|
||||
// The lifetime of this buffer is (from host->plugin) only the process
|
||||
// call in which the event is delivered or (from plugin->host) only the
|
||||
// duration of a try_push call.
|
||||
//
|
||||
// Since `clap_output_events.try_push` requires hosts to make a copy of
|
||||
// an event, host implementers receiving sysex messages from plugins need
|
||||
// to take care to both copy the event (so header, size, etc...) but
|
||||
// also memcpy the contents of the sysex pointer to host-owned memory, and
|
||||
// not just copy the data pointer.
|
||||
//
|
||||
// Similarly plugins retaining the sysex outside the lifetime of a single
|
||||
// process call must copy the sysex buffer to plugin-owned memory.
|
||||
//
|
||||
// As a consequence, the data structure pointed to by the sysex buffer
|
||||
// must be contiguous and copyable with `memcpy` of `size` bytes.
|
||||
typedef struct clap_event_midi_sysex {
|
||||
clap_event_header_t header;
|
||||
|
||||
uint16_t port_index;
|
||||
const uint8_t *buffer; // midi buffer. See lifetime comment above.
|
||||
uint32_t size;
|
||||
} clap_event_midi_sysex_t;
|
||||
|
||||
// While it is possible to use a series of midi2 event to send a sysex,
|
||||
// prefer clap_event_midi_sysex if possible for efficiency.
|
||||
typedef struct clap_event_midi2 {
|
||||
clap_event_header_t header;
|
||||
|
||||
uint16_t port_index;
|
||||
uint32_t data[4];
|
||||
} clap_event_midi2_t;
|
||||
|
||||
// Input event list. The host will deliver these sorted in sample order.
|
||||
typedef struct clap_input_events {
|
||||
void *ctx; // reserved pointer for the list
|
||||
|
||||
// returns the number of events in the list
|
||||
uint32_t(CLAP_ABI *size)(const struct clap_input_events *list);
|
||||
|
||||
// Don't free the returned event, it belongs to the list
|
||||
const clap_event_header_t *(CLAP_ABI *get)(const struct clap_input_events *list, uint32_t index);
|
||||
} clap_input_events_t;
|
||||
|
||||
// Output event list. The plugin must insert events in sample sorted order when inserting events
|
||||
typedef struct clap_output_events {
|
||||
void *ctx; // reserved pointer for the list
|
||||
|
||||
// Pushes a copy of the event
|
||||
// returns false if the event could not be pushed to the queue (out of memory?)
|
||||
bool(CLAP_ABI *try_push)(const struct clap_output_events *list,
|
||||
const clap_event_header_t *event);
|
||||
} clap_output_events_t;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
63
tests/clap/ext/ambisonic.h
Normal file
63
tests/clap/ext/ambisonic.h
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
#pragma once
|
||||
|
||||
#include "../plugin.h"
|
||||
|
||||
// This extension can be used to specify the channel mapping used by the plugin.
|
||||
static CLAP_CONSTEXPR const char CLAP_EXT_AMBISONIC[] = "clap.ambisonic/3";
|
||||
|
||||
// The latest draft is 100% compatible.
|
||||
// This compat ID may be removed in 2026.
|
||||
static CLAP_CONSTEXPR const char CLAP_EXT_AMBISONIC_COMPAT[] = "clap.ambisonic.draft/3";
|
||||
|
||||
static CLAP_CONSTEXPR const char CLAP_PORT_AMBISONIC[] = "ambisonic";
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
enum clap_ambisonic_ordering {
|
||||
// FuMa channel ordering
|
||||
CLAP_AMBISONIC_ORDERING_FUMA = 0,
|
||||
|
||||
// ACN channel ordering
|
||||
CLAP_AMBISONIC_ORDERING_ACN = 1,
|
||||
};
|
||||
|
||||
enum clap_ambisonic_normalization {
|
||||
CLAP_AMBISONIC_NORMALIZATION_MAXN = 0,
|
||||
CLAP_AMBISONIC_NORMALIZATION_SN3D = 1,
|
||||
CLAP_AMBISONIC_NORMALIZATION_N3D = 2,
|
||||
CLAP_AMBISONIC_NORMALIZATION_SN2D = 3,
|
||||
CLAP_AMBISONIC_NORMALIZATION_N2D = 4,
|
||||
};
|
||||
|
||||
typedef struct clap_ambisonic_config {
|
||||
uint32_t ordering; // see clap_ambisonic_ordering
|
||||
uint32_t normalization; // see clap_ambisonic_normalization
|
||||
} clap_ambisonic_config_t;
|
||||
|
||||
typedef struct clap_plugin_ambisonic {
|
||||
// Returns true if the given configuration is supported.
|
||||
// [main-thread]
|
||||
bool(CLAP_ABI *is_config_supported)(const clap_plugin_t *plugin,
|
||||
const clap_ambisonic_config_t *config);
|
||||
|
||||
// Returns true on success
|
||||
// [main-thread]
|
||||
bool(CLAP_ABI *get_config)(const clap_plugin_t *plugin,
|
||||
bool is_input,
|
||||
uint32_t port_index,
|
||||
clap_ambisonic_config_t *config);
|
||||
|
||||
} clap_plugin_ambisonic_t;
|
||||
|
||||
typedef struct clap_host_ambisonic {
|
||||
// Informs the host that the info has changed.
|
||||
// The info can only change when the plugin is de-activated.
|
||||
// [main-thread]
|
||||
void(CLAP_ABI *changed)(const clap_host_t *host);
|
||||
} clap_host_ambisonic_t;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
64
tests/clap/ext/audio-ports-activation.h
Normal file
64
tests/clap/ext/audio-ports-activation.h
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
#pragma once
|
||||
|
||||
#include "../plugin.h"
|
||||
|
||||
/// @page Audio Ports Activation
|
||||
///
|
||||
/// This extension provides a way for the host to activate and de-activate audio ports.
|
||||
/// Deactivating a port provides the following benefits:
|
||||
/// - the plugin knows ahead of time that a given input is not present and can choose
|
||||
/// an optimized computation path,
|
||||
/// - the plugin knows that an output is not consumed by the host, and doesn't need to
|
||||
/// compute it.
|
||||
///
|
||||
/// Audio ports can only be activated or deactivated when the plugin is deactivated, unless
|
||||
/// can_activate_while_processing() returns true.
|
||||
///
|
||||
/// Audio buffers must still be provided if the audio port is deactivated.
|
||||
/// In such case, they shall be filled with 0 (or whatever is the neutral value in your context)
|
||||
/// and the constant_mask shall be set.
|
||||
///
|
||||
/// Audio ports are initially in the active state after creating the plugin instance.
|
||||
/// Audio ports state are not saved in the plugin state, so the host must restore the
|
||||
/// audio ports state after creating the plugin instance.
|
||||
///
|
||||
/// Audio ports state is invalidated by clap_plugin_audio_ports_config.select() and
|
||||
/// clap_host_audio_ports.rescan(CLAP_AUDIO_PORTS_RESCAN_LIST).
|
||||
|
||||
static CLAP_CONSTEXPR const char CLAP_EXT_AUDIO_PORTS_ACTIVATION[] =
|
||||
"clap.audio-ports-activation/2";
|
||||
|
||||
// The latest draft is 100% compatible.
|
||||
// This compat ID may be removed in 2026.
|
||||
static CLAP_CONSTEXPR const char CLAP_EXT_AUDIO_PORTS_ACTIVATION_COMPAT[] =
|
||||
"clap.audio-ports-activation/draft-2";
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct clap_plugin_audio_ports_activation {
|
||||
// Returns true if the plugin supports activation/deactivation while processing.
|
||||
// [main-thread]
|
||||
bool(CLAP_ABI *can_activate_while_processing)(const clap_plugin_t *plugin);
|
||||
|
||||
// Activate the given port.
|
||||
//
|
||||
// It is only possible to activate and de-activate on the audio-thread if
|
||||
// can_activate_while_processing() returns true.
|
||||
//
|
||||
// sample_size indicate if the host will provide 32 bit audio buffers or 64 bits one.
|
||||
// Possible values are: 32, 64 or 0 if unspecified.
|
||||
//
|
||||
// returns false if failed, or invalid parameters
|
||||
// [active ? audio-thread : main-thread]
|
||||
bool(CLAP_ABI *set_active)(const clap_plugin_t *plugin,
|
||||
bool is_input,
|
||||
uint32_t port_index,
|
||||
bool is_active,
|
||||
uint32_t sample_size);
|
||||
} clap_plugin_audio_ports_activation_t;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
109
tests/clap/ext/audio-ports-config.h
Normal file
109
tests/clap/ext/audio-ports-config.h
Normal file
|
|
@ -0,0 +1,109 @@
|
|||
#pragma once
|
||||
|
||||
#include "../string-sizes.h"
|
||||
#include "../plugin.h"
|
||||
#include "audio-ports.h"
|
||||
|
||||
/// @page Audio Ports Config
|
||||
///
|
||||
/// This extension let the plugin provide port configurations presets.
|
||||
/// For example mono, stereo, surround, ambisonic, ...
|
||||
///
|
||||
/// After the plugin initialization, the host may scan the list of configurations and eventually
|
||||
/// select one that fits the plugin context. The host can only select a configuration if the plugin
|
||||
/// is deactivated.
|
||||
///
|
||||
/// A configuration is a very simple description of the audio ports:
|
||||
/// - it describes the main input and output ports
|
||||
/// - it has a name that can be displayed to the user
|
||||
///
|
||||
/// The idea behind the configurations, is to let the user choose one via a menu.
|
||||
///
|
||||
/// Plugins with very complex configuration possibilities should let the user configure the ports
|
||||
/// from the plugin GUI, and call @ref clap_host_audio_ports.rescan(CLAP_AUDIO_PORTS_RESCAN_ALL).
|
||||
///
|
||||
/// To inquire the exact bus layout, the plugin implements the clap_plugin_audio_ports_config_info_t
|
||||
/// extension where all busses can be retrieved in the same way as in the audio-port extension.
|
||||
|
||||
static CLAP_CONSTEXPR const char CLAP_EXT_AUDIO_PORTS_CONFIG[] = "clap.audio-ports-config";
|
||||
|
||||
static CLAP_CONSTEXPR const char CLAP_EXT_AUDIO_PORTS_CONFIG_INFO[] =
|
||||
"clap.audio-ports-config-info/1";
|
||||
|
||||
// The latest draft is 100% compatible.
|
||||
// This compat ID may be removed in 2026.
|
||||
static CLAP_CONSTEXPR const char CLAP_EXT_AUDIO_PORTS_CONFIG_INFO_COMPAT[] =
|
||||
"clap.audio-ports-config-info/draft-0";
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// Minimalistic description of ports configuration
|
||||
typedef struct clap_audio_ports_config {
|
||||
clap_id id;
|
||||
char name[CLAP_NAME_SIZE];
|
||||
|
||||
uint32_t input_port_count;
|
||||
uint32_t output_port_count;
|
||||
|
||||
// main input info
|
||||
bool has_main_input;
|
||||
uint32_t main_input_channel_count;
|
||||
const char *main_input_port_type;
|
||||
|
||||
// main output info
|
||||
bool has_main_output;
|
||||
uint32_t main_output_channel_count;
|
||||
const char *main_output_port_type;
|
||||
} clap_audio_ports_config_t;
|
||||
|
||||
// The audio ports config scan has to be done while the plugin is deactivated.
|
||||
typedef struct clap_plugin_audio_ports_config {
|
||||
// Gets the number of available configurations
|
||||
// [main-thread]
|
||||
uint32_t(CLAP_ABI *count)(const clap_plugin_t *plugin);
|
||||
|
||||
// Gets information about a configuration
|
||||
// Returns true on success and stores the result into config.
|
||||
// [main-thread]
|
||||
bool(CLAP_ABI *get)(const clap_plugin_t *plugin,
|
||||
uint32_t index,
|
||||
clap_audio_ports_config_t *config);
|
||||
|
||||
// Selects the configuration designated by id
|
||||
// Returns true if the configuration could be applied.
|
||||
// Once applied the host should scan again the audio ports.
|
||||
// [main-thread & plugin-deactivated]
|
||||
bool(CLAP_ABI *select)(const clap_plugin_t *plugin, clap_id config_id);
|
||||
} clap_plugin_audio_ports_config_t;
|
||||
|
||||
// Extended config info
|
||||
typedef struct clap_plugin_audio_ports_config_info {
|
||||
|
||||
// Gets the id of the currently selected config, or CLAP_INVALID_ID if the current port
|
||||
// layout isn't part of the config list.
|
||||
//
|
||||
// [main-thread]
|
||||
clap_id(CLAP_ABI *current_config)(const clap_plugin_t *plugin);
|
||||
|
||||
// Get info about an audio port, for a given config_id.
|
||||
// This is analogous to clap_plugin_audio_ports.get().
|
||||
// Returns true on success and stores the result into info.
|
||||
// [main-thread]
|
||||
bool(CLAP_ABI *get)(const clap_plugin_t *plugin,
|
||||
clap_id config_id,
|
||||
uint32_t port_index,
|
||||
bool is_input,
|
||||
clap_audio_port_info_t *info);
|
||||
} clap_plugin_audio_ports_config_info_t;
|
||||
|
||||
typedef struct clap_host_audio_ports_config {
|
||||
// Rescan the full list of configs.
|
||||
// [main-thread]
|
||||
void(CLAP_ABI *rescan)(const clap_host_t *host);
|
||||
} clap_host_audio_ports_config_t;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
116
tests/clap/ext/audio-ports.h
Normal file
116
tests/clap/ext/audio-ports.h
Normal file
|
|
@ -0,0 +1,116 @@
|
|||
#pragma once
|
||||
|
||||
#include "../plugin.h"
|
||||
#include "../string-sizes.h"
|
||||
|
||||
/// @page Audio Ports
|
||||
///
|
||||
/// This extension provides a way for the plugin to describe its current audio ports.
|
||||
///
|
||||
/// If the plugin does not implement this extension, it won't have audio ports.
|
||||
///
|
||||
/// 32 bits support is required for both host and plugins. 64 bits audio is optional.
|
||||
///
|
||||
/// The plugin is only allowed to change its ports configuration while it is deactivated.
|
||||
|
||||
static CLAP_CONSTEXPR const char CLAP_EXT_AUDIO_PORTS[] = "clap.audio-ports";
|
||||
static CLAP_CONSTEXPR const char CLAP_PORT_MONO[] = "mono";
|
||||
static CLAP_CONSTEXPR const char CLAP_PORT_STEREO[] = "stereo";
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
enum {
|
||||
// This port is the main audio input or output.
|
||||
// There can be only one main input and main output.
|
||||
// Main port must be at index 0.
|
||||
CLAP_AUDIO_PORT_IS_MAIN = 1 << 0,
|
||||
|
||||
// This port can be used with 64 bits audio
|
||||
CLAP_AUDIO_PORT_SUPPORTS_64BITS = 1 << 1,
|
||||
|
||||
// 64 bits audio is preferred with this port
|
||||
CLAP_AUDIO_PORT_PREFERS_64BITS = 1 << 2,
|
||||
|
||||
// This port must be used with the same sample size as all the other ports which have this flag.
|
||||
// In other words if all ports have this flag then the plugin may either be used entirely with
|
||||
// 64 bits audio or 32 bits audio, but it can't be mixed.
|
||||
CLAP_AUDIO_PORT_REQUIRES_COMMON_SAMPLE_SIZE = 1 << 3,
|
||||
};
|
||||
|
||||
typedef struct clap_audio_port_info {
|
||||
// id identifies a port and must be stable.
|
||||
// id may overlap between input and output ports.
|
||||
clap_id id;
|
||||
char name[CLAP_NAME_SIZE]; // displayable name
|
||||
|
||||
uint32_t flags;
|
||||
uint32_t channel_count;
|
||||
|
||||
// If null or empty then it is unspecified (arbitrary audio).
|
||||
// This field can be compared against:
|
||||
// - CLAP_PORT_MONO
|
||||
// - CLAP_PORT_STEREO
|
||||
// - CLAP_PORT_SURROUND (defined in the surround extension)
|
||||
// - CLAP_PORT_AMBISONIC (defined in the ambisonic extension)
|
||||
//
|
||||
// An extension can provide its own port type and way to inspect the channels.
|
||||
const char *port_type;
|
||||
|
||||
// in-place processing: allow the host to use the same buffer for input and output
|
||||
// if supported set the pair port id.
|
||||
// if not supported set to CLAP_INVALID_ID
|
||||
clap_id in_place_pair;
|
||||
} clap_audio_port_info_t;
|
||||
|
||||
// The audio ports scan has to be done while the plugin is deactivated.
|
||||
typedef struct clap_plugin_audio_ports {
|
||||
// Number of ports, for either input or output
|
||||
// [main-thread]
|
||||
uint32_t(CLAP_ABI *count)(const clap_plugin_t *plugin, bool is_input);
|
||||
|
||||
// Get info about an audio port.
|
||||
// Returns true on success and stores the result into info.
|
||||
// [main-thread]
|
||||
bool(CLAP_ABI *get)(const clap_plugin_t *plugin,
|
||||
uint32_t index,
|
||||
bool is_input,
|
||||
clap_audio_port_info_t *info);
|
||||
} clap_plugin_audio_ports_t;
|
||||
|
||||
enum {
|
||||
// The ports name did change, the host can scan them right away.
|
||||
CLAP_AUDIO_PORTS_RESCAN_NAMES = 1 << 0,
|
||||
|
||||
// [!active] The flags did change
|
||||
CLAP_AUDIO_PORTS_RESCAN_FLAGS = 1 << 1,
|
||||
|
||||
// [!active] The channel_count did change
|
||||
CLAP_AUDIO_PORTS_RESCAN_CHANNEL_COUNT = 1 << 2,
|
||||
|
||||
// [!active] The port type did change
|
||||
CLAP_AUDIO_PORTS_RESCAN_PORT_TYPE = 1 << 3,
|
||||
|
||||
// [!active] The in-place pair did change, this requires.
|
||||
CLAP_AUDIO_PORTS_RESCAN_IN_PLACE_PAIR = 1 << 4,
|
||||
|
||||
// [!active] The list of ports have changed: entries have been removed/added.
|
||||
CLAP_AUDIO_PORTS_RESCAN_LIST = 1 << 5,
|
||||
};
|
||||
|
||||
typedef struct clap_host_audio_ports {
|
||||
// Checks if the host allows a plugin to change a given aspect of the audio ports definition.
|
||||
// [main-thread]
|
||||
bool(CLAP_ABI *is_rescan_flag_supported)(const clap_host_t *host, uint32_t flag);
|
||||
|
||||
// Rescan the full list of audio ports according to the flags.
|
||||
// It is illegal to ask the host to rescan with a flag that is not supported.
|
||||
// Certain flags require the plugin to be de-activated.
|
||||
// [main-thread]
|
||||
void(CLAP_ABI *rescan)(const clap_host_t *host, uint32_t flags);
|
||||
} clap_host_audio_ports_t;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
63
tests/clap/ext/configurable-audio-ports.h
Normal file
63
tests/clap/ext/configurable-audio-ports.h
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
#pragma once
|
||||
|
||||
#include "audio-ports.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// This extension lets the host configure the plugin's input and output audio ports.
|
||||
// This is a "push" approach to audio ports configuration.
|
||||
|
||||
static CLAP_CONSTEXPR const char CLAP_EXT_CONFIGURABLE_AUDIO_PORTS[] =
|
||||
"clap.configurable-audio-ports/1";
|
||||
|
||||
// The latest draft is 100% compatible.
|
||||
// This compat ID may be removed in 2026.
|
||||
static CLAP_CONSTEXPR const char CLAP_EXT_CONFIGURABLE_AUDIO_PORTS_COMPAT[] =
|
||||
"clap.configurable-audio-ports.draft1";
|
||||
|
||||
typedef struct clap_audio_port_configuration_request {
|
||||
// Identifies the port by is_input and port_index
|
||||
bool is_input;
|
||||
uint32_t port_index;
|
||||
|
||||
// The requested number of channels.
|
||||
uint32_t channel_count;
|
||||
|
||||
// The port type, see audio-ports.h, clap_audio_port_info.port_type for interpretation.
|
||||
const char *port_type;
|
||||
|
||||
// cast port_details according to port_type:
|
||||
// - CLAP_PORT_MONO: (discard)
|
||||
// - CLAP_PORT_STEREO: (discard)
|
||||
// - CLAP_PORT_SURROUND: const uint8_t *channel_map
|
||||
// - CLAP_PORT_AMBISONIC: const clap_ambisonic_config_t *info
|
||||
const void *port_details;
|
||||
} clap_audio_port_configuration_request_t;
|
||||
|
||||
typedef struct clap_plugin_configurable_audio_ports {
|
||||
// Returns true if the given configurations can be applied using apply_configuration().
|
||||
// [main-thread && !active]
|
||||
bool(CLAP_ABI *can_apply_configuration)(
|
||||
const clap_plugin_t *plugin,
|
||||
const struct clap_audio_port_configuration_request *requests,
|
||||
uint32_t request_count);
|
||||
|
||||
// Submit a bunch of configuration requests which will atomically be applied together,
|
||||
// or discarded together.
|
||||
//
|
||||
// Once the configuration is successfully applied, it isn't necessary for the plugin to call
|
||||
// clap_host_audio_ports->changed(); and it isn't necessary for the host to scan the
|
||||
// audio ports.
|
||||
//
|
||||
// Returns true if applied.
|
||||
// [main-thread && !active]
|
||||
bool(CLAP_ABI *apply_configuration)(const clap_plugin_t *plugin,
|
||||
const struct clap_audio_port_configuration_request *requests,
|
||||
uint32_t request_count);
|
||||
} clap_plugin_configurable_audio_ports_t;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
167
tests/clap/ext/context-menu.h
Normal file
167
tests/clap/ext/context-menu.h
Normal file
|
|
@ -0,0 +1,167 @@
|
|||
#pragma once
|
||||
|
||||
#include "../plugin.h"
|
||||
|
||||
// This extension lets the host and plugin exchange menu items and let the plugin ask the host to
|
||||
// show its context menu.
|
||||
|
||||
static CLAP_CONSTEXPR const char CLAP_EXT_CONTEXT_MENU[] = "clap.context-menu/1";
|
||||
|
||||
// The latest draft is 100% compatible.
|
||||
// This compat ID may be removed in 2026.
|
||||
static CLAP_CONSTEXPR const char CLAP_EXT_CONTEXT_MENU_COMPAT[] = "clap.context-menu.draft/0";
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// There can be different target kind for a context menu
|
||||
enum {
|
||||
CLAP_CONTEXT_MENU_TARGET_KIND_GLOBAL = 0,
|
||||
CLAP_CONTEXT_MENU_TARGET_KIND_PARAM = 1,
|
||||
};
|
||||
|
||||
// Describes the context menu target
|
||||
typedef struct clap_context_menu_target {
|
||||
uint32_t kind;
|
||||
clap_id id;
|
||||
} clap_context_menu_target_t;
|
||||
|
||||
enum {
|
||||
// Adds a clickable menu entry.
|
||||
// data: const clap_context_menu_item_entry_t*
|
||||
CLAP_CONTEXT_MENU_ITEM_ENTRY,
|
||||
|
||||
// Adds a clickable menu entry which will feature both a checkmark and a label.
|
||||
// data: const clap_context_menu_item_check_entry_t*
|
||||
CLAP_CONTEXT_MENU_ITEM_CHECK_ENTRY,
|
||||
|
||||
// Adds a separator line.
|
||||
// data: NULL
|
||||
CLAP_CONTEXT_MENU_ITEM_SEPARATOR,
|
||||
|
||||
// Starts a sub menu with the given label.
|
||||
// data: const clap_context_menu_item_begin_submenu_t*
|
||||
CLAP_CONTEXT_MENU_ITEM_BEGIN_SUBMENU,
|
||||
|
||||
// Ends the current sub menu.
|
||||
// data: NULL
|
||||
CLAP_CONTEXT_MENU_ITEM_END_SUBMENU,
|
||||
|
||||
// Adds a title entry
|
||||
// data: const clap_context_menu_item_title_t *
|
||||
CLAP_CONTEXT_MENU_ITEM_TITLE,
|
||||
};
|
||||
typedef uint32_t clap_context_menu_item_kind_t;
|
||||
|
||||
typedef struct clap_context_menu_entry {
|
||||
// text to be displayed
|
||||
const char *label;
|
||||
|
||||
// if false, then the menu entry is greyed out and not clickable
|
||||
bool is_enabled;
|
||||
clap_id action_id;
|
||||
} clap_context_menu_entry_t;
|
||||
|
||||
typedef struct clap_context_menu_check_entry {
|
||||
// text to be displayed
|
||||
const char *label;
|
||||
|
||||
// if false, then the menu entry is greyed out and not clickable
|
||||
bool is_enabled;
|
||||
|
||||
// if true, then the menu entry will be displayed as checked
|
||||
bool is_checked;
|
||||
clap_id action_id;
|
||||
} clap_context_menu_check_entry_t;
|
||||
|
||||
typedef struct clap_context_menu_item_title {
|
||||
// text to be displayed
|
||||
const char *title;
|
||||
|
||||
// if false, then the menu entry is greyed out
|
||||
bool is_enabled;
|
||||
} clap_context_menu_item_title_t;
|
||||
|
||||
typedef struct clap_context_menu_submenu {
|
||||
// text to be displayed
|
||||
const char *label;
|
||||
|
||||
// if false, then the menu entry is greyed out and won't show submenu
|
||||
bool is_enabled;
|
||||
} clap_context_menu_submenu_t;
|
||||
|
||||
// Context menu builder.
|
||||
// This object isn't thread-safe and must be used on the same thread as it was provided.
|
||||
typedef struct clap_context_menu_builder {
|
||||
void *ctx;
|
||||
|
||||
// Adds an entry to the menu.
|
||||
// item_data type is determined by item_kind.
|
||||
// Returns true on success.
|
||||
bool(CLAP_ABI *add_item)(const struct clap_context_menu_builder *builder,
|
||||
clap_context_menu_item_kind_t item_kind,
|
||||
const void *item_data);
|
||||
|
||||
// Returns true if the menu builder supports the given item kind
|
||||
bool(CLAP_ABI *supports)(const struct clap_context_menu_builder *builder,
|
||||
clap_context_menu_item_kind_t item_kind);
|
||||
} clap_context_menu_builder_t;
|
||||
|
||||
typedef struct clap_plugin_context_menu {
|
||||
// Insert plugin's menu items into the menu builder.
|
||||
// If target is null, assume global context.
|
||||
// Returns true on success.
|
||||
// [main-thread]
|
||||
bool(CLAP_ABI *populate)(const clap_plugin_t *plugin,
|
||||
const clap_context_menu_target_t *target,
|
||||
const clap_context_menu_builder_t *builder);
|
||||
|
||||
// Performs the given action, which was previously provided to the host via populate().
|
||||
// If target is null, assume global context.
|
||||
// Returns true on success.
|
||||
// [main-thread]
|
||||
bool(CLAP_ABI *perform)(const clap_plugin_t *plugin,
|
||||
const clap_context_menu_target_t *target,
|
||||
clap_id action_id);
|
||||
} clap_plugin_context_menu_t;
|
||||
|
||||
typedef struct clap_host_context_menu {
|
||||
// Insert host's menu items into the menu builder.
|
||||
// If target is null, assume global context.
|
||||
// Returns true on success.
|
||||
// [main-thread]
|
||||
bool(CLAP_ABI *populate)(const clap_host_t *host,
|
||||
const clap_context_menu_target_t *target,
|
||||
const clap_context_menu_builder_t *builder);
|
||||
|
||||
// Performs the given action, which was previously provided to the plugin via populate().
|
||||
// If target is null, assume global context.
|
||||
// Returns true on success.
|
||||
// [main-thread]
|
||||
bool(CLAP_ABI *perform)(const clap_host_t *host,
|
||||
const clap_context_menu_target_t *target,
|
||||
clap_id action_id);
|
||||
|
||||
// Returns true if the host can display a popup menu for the plugin.
|
||||
// This may depend upon the current windowing system used to display the plugin, so the
|
||||
// return value is invalidated after creating the plugin window.
|
||||
// [main-thread]
|
||||
bool(CLAP_ABI *can_popup)(const clap_host_t *host);
|
||||
|
||||
// Shows the host popup menu for a given parameter.
|
||||
// If the plugin is using embedded GUI, then x and y are relative to the plugin's window,
|
||||
// otherwise they're absolute coordinate, and screen index might be set accordingly.
|
||||
// If target is null, assume global context.
|
||||
// Returns true on success.
|
||||
// [main-thread]
|
||||
bool(CLAP_ABI *popup)(const clap_host_t *host,
|
||||
const clap_context_menu_target_t *target,
|
||||
int32_t screen_index,
|
||||
int32_t x,
|
||||
int32_t y);
|
||||
} clap_host_context_menu_t;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
33
tests/clap/ext/draft/extensible-audio-ports.h
Normal file
33
tests/clap/ext/draft/extensible-audio-ports.h
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
#pragma once
|
||||
|
||||
#include "../audio-ports.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// This extension lets the host add and remove audio ports to the plugin.
|
||||
static CLAP_CONSTEXPR const char CLAP_EXT_EXTENSIBLE_AUDIO_PORTS[] =
|
||||
"clap.extensible-audio-ports/1";
|
||||
|
||||
typedef struct clap_plugin_extensible_audio_ports {
|
||||
// Asks the plugin to add a new port (at the end of the list), with the following settings.
|
||||
// port_type: see clap_audio_port_info.port_type for interpretation.
|
||||
// port_details: see clap_audio_port_configuration_request.port_details for interpretation.
|
||||
// Returns true on success.
|
||||
// [main-thread && !is_active]
|
||||
bool(CLAP_ABI *add_port)(const clap_plugin_t *plugin,
|
||||
bool is_input,
|
||||
uint32_t channel_count,
|
||||
const char *port_type,
|
||||
const void *port_details);
|
||||
|
||||
// Asks the plugin to remove a port.
|
||||
// Returns true on success.
|
||||
// [main-thread && !is_active]
|
||||
bool(CLAP_ABI *remove_port)(const clap_plugin_t *plugin, bool is_input, uint32_t index);
|
||||
} clap_plugin_extensible_audio_ports_t;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
34
tests/clap/ext/draft/gain-adjustment-metering.h
Normal file
34
tests/clap/ext/draft/gain-adjustment-metering.h
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
#pragma once
|
||||
|
||||
#include "../../plugin.h"
|
||||
|
||||
// This extension lets the plugin report the current gain adjustment
|
||||
// (typically, gain reduction) to the host.
|
||||
|
||||
static CLAP_CONSTEXPR const char CLAP_EXT_GAIN_ADJUSTMENT_METERING[] = "clap.gain-adjustment-metering/0";
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct clap_plugin_gain_adjustment_metering {
|
||||
// Returns the current gain adjustment in dB. The value is intended
|
||||
// for informational display, for example in a host meter or tooltip.
|
||||
// The returned value represents the gain adjustment that the plugin
|
||||
// applied to the last sample in the most recently processed block.
|
||||
//
|
||||
// The returned value is in dB. Zero means the plugin is applying no gain
|
||||
// reduction, or is not processing. A negative value means the plugin is
|
||||
// applying gain reduction, as with a compressor or limiter. A positive
|
||||
// value means the plugin is adding gain, as with an expander. The value
|
||||
// represents the dynamic gain reduction or expansion applied by the
|
||||
// plugin, before any make-up gain or other adjustment. A single value is
|
||||
// returned for all audio channels.
|
||||
//
|
||||
// [audio-thread]
|
||||
double(CLAP_ABI *get)(const clap_plugin_t *plugin);
|
||||
} clap_plugin_gain_adjustment_metering_t;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
153
tests/clap/ext/draft/mini-curve-display.h
Normal file
153
tests/clap/ext/draft/mini-curve-display.h
Normal file
|
|
@ -0,0 +1,153 @@
|
|||
#pragma once
|
||||
|
||||
#include "../../plugin.h"
|
||||
|
||||
// This extension allows a host to render a small curve provided by the plugin.
|
||||
// A useful application is to render an EQ frequency response in the DAW mixer view.
|
||||
|
||||
static CLAP_CONSTEXPR const char CLAP_EXT_MINI_CURVE_DISPLAY[] = "clap.mini-curve-display/3";
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
enum clap_mini_curve_display_curve_kind {
|
||||
// If the curve's kind doesn't fit in any proposed kind, use this one
|
||||
// and perhaps, make a pull request to extend the list.
|
||||
CLAP_MINI_CURVE_DISPLAY_CURVE_KIND_UNSPECIFIED = 0,
|
||||
|
||||
// The mini curve is intended to draw the total gain response of the plugin.
|
||||
// In this case the y values are in dB and the x values are in Hz (logarithmic).
|
||||
// This would be useful in for example an equalizer.
|
||||
CLAP_MINI_CURVE_DISPLAY_CURVE_KIND_GAIN_RESPONSE = 1,
|
||||
|
||||
// The mini curve is intended to draw the total phase response of the plugin.
|
||||
// In this case the y values are in radians and the x values are in Hz (logarithmic).
|
||||
// This would be useful in for example an equalizer.
|
||||
CLAP_MINI_CURVE_DISPLAY_CURVE_KIND_PHASE_RESPONSE = 2,
|
||||
|
||||
// The mini curve is intended to draw the transfer curve of the plugin.
|
||||
// In this case the both x and y values are in dB.
|
||||
// This would be useful in for example a compressor or distortion plugin.
|
||||
CLAP_MINI_CURVE_DISPLAY_CURVE_KIND_TRANSFER_CURVE = 3,
|
||||
|
||||
// This mini curve is intended to draw gain reduction over time. In this case
|
||||
// x refers to the window in seconds and y refers to level in dB, x_min is
|
||||
// always 0, and x_max would be the duration of the window.
|
||||
// This would be useful in for example a compressor or limiter.
|
||||
CLAP_MINI_CURVE_DISPLAY_CURVE_KIND_GAIN_REDUCTION = 4,
|
||||
|
||||
// This curve is intended as a generic time series plot. In this case
|
||||
// x refers to the window in seconds. x_min is always 0, and x_max would be the duration of the
|
||||
// window.
|
||||
// Y is not specified and up to the plugin.
|
||||
CLAP_MINI_CURVE_DISPLAY_CURVE_KIND_TIME_SERIES = 5,
|
||||
|
||||
// Note: more entries could be added here in the future
|
||||
};
|
||||
|
||||
typedef struct clap_mini_curve_display_curve_hints {
|
||||
|
||||
// Range for the x axis.
|
||||
double x_min;
|
||||
double x_max;
|
||||
|
||||
// Range for the y axis.
|
||||
double y_min;
|
||||
double y_max;
|
||||
|
||||
} clap_mini_curve_display_curve_hints_t;
|
||||
|
||||
// A set of points representing the curve to be painted.
|
||||
typedef struct clap_mini_curve_display_curve_data {
|
||||
// Indicates the kind of curve those values represent, the host can use this
|
||||
// information to paint the curve using a meaningful color.
|
||||
int32_t curve_kind;
|
||||
|
||||
// values[0] will be the leftmost value and values[data_size -1] will be the rightmost
|
||||
// value.
|
||||
//
|
||||
// The value 0 and UINT16_MAX won't be painted.
|
||||
// The value 1 will be at the bottom of the curve and UINT16_MAX - 1 will be at the top.
|
||||
uint16_t *values;
|
||||
uint32_t values_count;
|
||||
} clap_mini_curve_display_curve_data_t;
|
||||
|
||||
typedef struct clap_plugin_mini_curve_display {
|
||||
// Returns the number of curves the plugin wants to paint.
|
||||
// Be aware that the space to display those curves will be small, and too much data will make
|
||||
// the output hard to read.
|
||||
uint32_t(CLAP_ABI *get_curve_count)(const clap_plugin_t *plugin);
|
||||
|
||||
// Renders the curve into each the curves buffer.
|
||||
//
|
||||
// curves is an array, and each entries (up to curves_size) contains pre-allocated
|
||||
// values buffer that must be filled by the plugin.
|
||||
//
|
||||
// The host will "stack" the curves, from the first one to the last one.
|
||||
// curves[0] is the first curve to be painted.
|
||||
// curves[n + 1] will be painted over curves[n].
|
||||
//
|
||||
// Returns the number of curves rendered.
|
||||
// [main-thread]
|
||||
uint32_t(CLAP_ABI *render)(const clap_plugin_t *plugin,
|
||||
clap_mini_curve_display_curve_data_t *curves,
|
||||
uint32_t curves_size);
|
||||
|
||||
// Tells the plugin if the curve is currently observed or not.
|
||||
// When it isn't observed render() can't be called.
|
||||
//
|
||||
// When is_obseverd becomes true, the curve content and axis name are implicitly invalidated. So
|
||||
// the plugin don't need to call host->changed.
|
||||
//
|
||||
// [main-thread]
|
||||
void(CLAP_ABI *set_observed)(const clap_plugin_t *plugin, bool is_observed);
|
||||
|
||||
// Retrives the axis name.
|
||||
// x_name and y_name must not to be null.
|
||||
// Returns true on success, if the name capacity was sufficient.
|
||||
// [main-thread]
|
||||
bool(CLAP_ABI *get_axis_name)(const clap_plugin_t *plugin,
|
||||
uint32_t curve_index,
|
||||
char *x_name,
|
||||
char *y_name,
|
||||
uint32_t name_capacity);
|
||||
} clap_plugin_mini_curve_display_t;
|
||||
|
||||
enum clap_mini_curve_display_change_flags {
|
||||
// Informs the host that the curve content changed.
|
||||
// Can only be called if the curve is observed and is static.
|
||||
CLAP_MINI_CURVE_DISPLAY_CURVE_CHANGED = 1 << 0,
|
||||
|
||||
// Informs the host that the curve axis name changed.
|
||||
// Can only be called if the curve is observed.
|
||||
CLAP_MINI_CURVE_DISPLAY_AXIS_NAME_CHANGED = 1 << 1,
|
||||
};
|
||||
|
||||
typedef struct clap_host_mini_curve_display {
|
||||
// Fills in the given clap_mini_display_curve_hints_t structure and returns
|
||||
// true if successful. If not, return false.
|
||||
// [main-thread]
|
||||
bool(CLAP_ABI *get_hints)(const clap_host_t *host,
|
||||
uint32_t kind,
|
||||
clap_mini_curve_display_curve_hints_t *hints);
|
||||
|
||||
// Mark the curve as being static or dynamic.
|
||||
// The curve is initially considered as static, though the plugin should explicitely
|
||||
// initialize this state.
|
||||
//
|
||||
// When static, the curve changes will be notified by calling host->changed().
|
||||
// When dynamic, the curve is constantly changing and the host is expected to
|
||||
// periodically re-render.
|
||||
//
|
||||
// [main-thread]
|
||||
void(CLAP_ABI *set_dynamic)(const clap_host_t *host, bool is_dynamic);
|
||||
|
||||
// See clap_mini_curve_display_change_flags
|
||||
// [main-thread]
|
||||
void(CLAP_ABI *changed)(const clap_host_t *host, uint32_t flags);
|
||||
} clap_host_mini_curve_display_t;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
108
tests/clap/ext/draft/project-location.h
Normal file
108
tests/clap/ext/draft/project-location.h
Normal file
|
|
@ -0,0 +1,108 @@
|
|||
#pragma once
|
||||
|
||||
#include "../../color.h"
|
||||
#include "../../plugin.h"
|
||||
#include "../../string-sizes.h"
|
||||
|
||||
// This extension allows a host to tell the plugin more about its position
|
||||
// within a project or session.
|
||||
|
||||
static CLAP_CONSTEXPR const char CLAP_EXT_PROJECT_LOCATION[] = "clap.project-location/2";
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
enum clap_project_location_kind {
|
||||
// Represents a document/project/session.
|
||||
CLAP_PROJECT_LOCATION_PROJECT = 1,
|
||||
|
||||
// Represents a group of tracks.
|
||||
// It can contain track groups, tracks, and devices (post processing).
|
||||
// The first device within a track group has the index of
|
||||
// the last track or track group within this group + 1.
|
||||
CLAP_PROJECT_LOCATION_TRACK_GROUP = 2,
|
||||
|
||||
// Represents a single track.
|
||||
// It contains devices (serial).
|
||||
CLAP_PROJECT_LOCATION_TRACK = 3,
|
||||
|
||||
// Represents a single device.
|
||||
// It can contain other nested device chains.
|
||||
CLAP_PROJECT_LOCATION_DEVICE = 4,
|
||||
|
||||
// Represents a nested device chain (serial).
|
||||
// Its parent must be a device.
|
||||
// It contains other devices.
|
||||
CLAP_PROJECT_LOCATION_NESTED_DEVICE_CHAIN = 5,
|
||||
};
|
||||
|
||||
enum clap_project_location_track_kind {
|
||||
// This track is an instrument track.
|
||||
CLAP_PROJECT_LOCATION_INSTUMENT_TRACK = 1,
|
||||
|
||||
// This track is an audio track.
|
||||
CLAP_PROJECT_LOCATION_AUDIO_TRACK = 2,
|
||||
|
||||
// This track is both an instrument and audio track.
|
||||
CLAP_PROJECT_LOCATION_HYBRID_TRACK = 3,
|
||||
|
||||
// This track is a return track.
|
||||
CLAP_PROJECT_LOCATION_RETURN_TRACK = 4,
|
||||
|
||||
// This track is a master track.
|
||||
// Each group have a master track for processing the sum of all its children tracks.
|
||||
CLAP_PROJECT_LOCATION_MASTER_TRACK = 5,
|
||||
};
|
||||
|
||||
enum clap_project_location_flags {
|
||||
CLAP_PROJECT_LOCATION_HAS_INDEX = 1 << 0,
|
||||
CLAP_PROJECT_LOCATION_HAS_COLOR = 1 << 1,
|
||||
};
|
||||
|
||||
typedef struct clap_project_location_element {
|
||||
// A bit-mask, see clap_project_location_flags.
|
||||
uint64_t flags;
|
||||
|
||||
// Kind of the element, must be one of the CLAP_PROJECT_LOCATION_* values.
|
||||
uint32_t kind;
|
||||
|
||||
// Only relevant if kind is CLAP_PLUGIN_LOCATION_TRACK.
|
||||
// see enum CLAP_PROJECT_LOCATION_track_kind.
|
||||
uint32_t track_kind;
|
||||
|
||||
// Index within the parent element.
|
||||
// Only usable if CLAP_PROJECT_LOCATION_HAS_INDEX is set in flags.
|
||||
uint32_t index;
|
||||
|
||||
// Internal ID of the element.
|
||||
// This is not intended for display to the user,
|
||||
// but rather to give the host a potential quick way for lookups.
|
||||
char id[CLAP_PATH_SIZE];
|
||||
|
||||
// User friendly name of the element.
|
||||
char name[CLAP_NAME_SIZE];
|
||||
|
||||
// Color for this element.
|
||||
// Only usable if CLAP_PROJECT_LOCATION_HAS_COLOR is set in flags.
|
||||
clap_color_t color;
|
||||
} clap_project_location_element_t;
|
||||
|
||||
typedef struct clap_plugin_project_location {
|
||||
// Called by the host when the location of the plugin instance changes.
|
||||
//
|
||||
// The last item in this array always refers to the device itself, and as
|
||||
// such is expected to be of kind CLAP_PLUGIN_LOCATION_DEVICE.
|
||||
// The first item in this array always refers to the project this device is in and must be of
|
||||
// kind CLAP_PROJECT_LOCATION_PROJECT. The path is expected to be something like: PROJECT >
|
||||
// TRACK_GROUP+ > TRACK > (DEVICE > NESTED_DEVICE_CHAIN)* > DEVICE
|
||||
//
|
||||
// [main-thread]
|
||||
void(CLAP_ABI *set)(const clap_plugin_t *plugin,
|
||||
const clap_project_location_element_t *path,
|
||||
uint32_t num_elements);
|
||||
} clap_plugin_project_location_t;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
88
tests/clap/ext/draft/resource-directory.h
Normal file
88
tests/clap/ext/draft/resource-directory.h
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
#pragma once
|
||||
|
||||
#include "../../plugin.h"
|
||||
|
||||
static CLAP_CONSTEXPR const char CLAP_EXT_RESOURCE_DIRECTORY[] = "clap.resource-directory/1";
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/// @page Resource Directory
|
||||
///
|
||||
/// This extension provides a way for the plugin to store its resources as file in a directory
|
||||
/// provided by the host and recover them later on.
|
||||
///
|
||||
/// The plugin **must** store relative path in its state toward resource directories.
|
||||
///
|
||||
/// Resource sharing:
|
||||
/// - shared directory is shared among all plugin instances, hence mostly appropriate for read-only
|
||||
/// content
|
||||
/// -> suitable for read-only content
|
||||
/// - exclusive directory is exclusive to the plugin instance
|
||||
/// -> if the plugin, then its exclusive directory must be duplicated too
|
||||
/// -> suitable for read-write content
|
||||
///
|
||||
/// Keeping the shared directory clean:
|
||||
/// - to avoid clashes in the shared directory, plugins are encouraged to organize their files in
|
||||
/// sub-folders, for example create one subdirectory using the vendor name
|
||||
/// - don't use symbolic links or hard links which points outside of the directory
|
||||
///
|
||||
/// Resource life-time:
|
||||
/// - exclusive folder content is managed by the plugin instance
|
||||
/// - exclusive folder content is deleted when the plugin instance is removed from the project
|
||||
/// - shared folder content isn't managed by the host, until all plugins using the shared directory
|
||||
/// are removed from the project
|
||||
///
|
||||
/// Note for the host
|
||||
/// - try to use the filesystem's copy-on-write feature when possible for reducing exclusive folder
|
||||
/// space usage on duplication
|
||||
/// - host can "garbage collect" the files in the shared folder using:
|
||||
/// clap_plugin_resource_directory.get_files_count()
|
||||
/// clap_plugin_resource_directory.get_file_path()
|
||||
/// but be **very** careful before deleting any resources
|
||||
|
||||
typedef struct clap_plugin_resource_directory {
|
||||
// Sets the directory in which the plugin can save its resources.
|
||||
// The directory remains valid until it is overridden or the plugin is destroyed.
|
||||
// If path is null or blank, it clears the directory location.
|
||||
// path must be absolute.
|
||||
// [main-thread]
|
||||
void(CLAP_ABI *set_directory)(const clap_plugin_t *plugin, const char *path, bool is_shared);
|
||||
|
||||
// Asks the plugin to put its resources into the resource directory.
|
||||
// It is not necessary to collect files which belongs to the plugin's
|
||||
// factory content unless the param all is true.
|
||||
// [main-thread]
|
||||
void(CLAP_ABI *collect)(const clap_plugin_t *plugin, bool all);
|
||||
|
||||
// Returns the number of files used by the plugin in the shared resource folder.
|
||||
// [main-thread]
|
||||
uint32_t(CLAP_ABI *get_files_count)(const clap_plugin_t *plugin);
|
||||
|
||||
// Retrieves relative file path to the resource directory.
|
||||
// @param path writable memory to store the path
|
||||
// @param path_size number of available bytes in path
|
||||
// Returns the number of bytes in the path, or -1 on error
|
||||
// [main-thread]
|
||||
int32_t(CLAP_ABI *get_file_path)(const clap_plugin_t *plugin,
|
||||
uint32_t index,
|
||||
char *path,
|
||||
uint32_t path_size);
|
||||
} clap_plugin_resource_directory_t;
|
||||
|
||||
typedef struct clap_host_resource_directory {
|
||||
// Request the host to setup a resource directory with the specified sharing.
|
||||
// Returns true if the host will perform the request.
|
||||
// [main-thread]
|
||||
bool(CLAP_ABI *request_directory)(const clap_host_t *host, bool is_shared);
|
||||
|
||||
// Tell the host that the resource directory of the specified sharing is no longer required.
|
||||
// If is_shared = false, then the host may delete the directory content.
|
||||
// [main-thread]
|
||||
void(CLAP_ABI *release_directory)(const clap_host_t *host, bool is_shared);
|
||||
} clap_host_resource_directory_t;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
90
tests/clap/ext/draft/scratch-memory.h
Normal file
90
tests/clap/ext/draft/scratch-memory.h
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
#pragma once
|
||||
|
||||
#include "../../plugin.h"
|
||||
|
||||
// This extension lets the plugin request "scratch" memory from the host.
|
||||
//
|
||||
// The scratch memory is thread-local, and can be accessed during
|
||||
// `clap_plugin->process()` and `clap_plugin_thread_pool->exec()`;
|
||||
// its content is not persistent between callbacks.
|
||||
//
|
||||
// The motivation for this extension is to allow the plugin host
|
||||
// to "share" a single scratch buffer across multiple plugin
|
||||
// instances.
|
||||
//
|
||||
// For example, imagine the host needs to process N plugins
|
||||
// in sequence, and each plugin requires 10K of scratch memory.
|
||||
// If each plugin pre-allocates its own scratch memory, then N * 10K
|
||||
// of memory is being allocated in total. However, if each plugin
|
||||
// requests 10K of scratch memory from the host, then the host can
|
||||
// allocate a single 10K scratch buffer, and make it available to all
|
||||
// plugins.
|
||||
//
|
||||
// This optimization may allow for reduced memory usage and improved
|
||||
// CPU cache usage.
|
||||
|
||||
static CLAP_CONSTEXPR const char CLAP_EXT_SCRATCH_MEMORY[] = "clap.scratch-memory/1";
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct clap_host_scratch_memory {
|
||||
// Asks the host to reserve scratch memory.
|
||||
//
|
||||
// The plugin may call this method multiple times (for
|
||||
// example, gradually decreasing the amount of scratch
|
||||
// being asked for until the host returns true), however,
|
||||
// the plugin should avoid calling this method un-neccesarily
|
||||
// since the host implementation may be relatively expensive.
|
||||
// If the plugin calls `reserve()` multiple times, then the
|
||||
// last call invalidates all previous calls.
|
||||
//
|
||||
// De-activating the plugin releases the scratch memory.
|
||||
//
|
||||
// `max_concurrency_hint` is an optional hint which indicates
|
||||
// the maximum number of threads concurrently accessing the scratch memory.
|
||||
// Set to 0 if unspecified.
|
||||
//
|
||||
// Returns true on success.
|
||||
//
|
||||
// [main-thread & being-activated]
|
||||
bool(CLAP_ABI *reserve)(const clap_host_t *host,
|
||||
uint32_t scratch_size_bytes,
|
||||
uint32_t max_concurrency_hint);
|
||||
|
||||
// Returns a pointer to the "thread-local" scratch memory.
|
||||
//
|
||||
// If the scratch memory wasn't successfully reserved, returns NULL.
|
||||
//
|
||||
// If the plugin crosses `max_concurrency_hint`, then the return value
|
||||
// is either NULL or a valid scratch memory pointer.
|
||||
//
|
||||
// This method may only be called by the plugin from the audio thread,
|
||||
// (i.e. during the process() or thread_pool.exec() callback), and
|
||||
// the provided memory is only valid until the plugin returns from
|
||||
// that callback. The plugin must not hold any references to data
|
||||
// that lives in the scratch memory after returning from the callback,
|
||||
// as that data will likely be over-written by another plugin using
|
||||
// the same scratch memory.
|
||||
//
|
||||
// The provided memory is not initialized, and may have been used
|
||||
// by other plugin instances, so the plugin must correctly initialize
|
||||
// the memory when using it.
|
||||
//
|
||||
// The provided memory is owned by the host, so the plugin must not
|
||||
// free the memory.
|
||||
//
|
||||
// If the plugin wants to share the same scratch memory pointer with
|
||||
// many threads, it must access the the scratch at the beginning of the
|
||||
// `process()` callback, cache the returned pointer before calling
|
||||
// `clap_host_thread_pool->request_exec()` and clear the cached pointer
|
||||
// before returning from `process()`.
|
||||
//
|
||||
// [audio-thread]
|
||||
void *(CLAP_ABI *access)(const clap_host_t *host);
|
||||
} clap_host_scratch_memory_t;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
66
tests/clap/ext/draft/transport-control.h
Normal file
66
tests/clap/ext/draft/transport-control.h
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
#pragma once
|
||||
|
||||
#include "../../plugin.h"
|
||||
|
||||
// This extension lets the plugin submit transport requests to the host.
|
||||
// The host has no obligation to execute these requests, so the interface may be
|
||||
// partially working.
|
||||
|
||||
static CLAP_CONSTEXPR const char CLAP_EXT_TRANSPORT_CONTROL[] = "clap.transport-control/1";
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct clap_host_transport_control {
|
||||
// Jumps back to the start point and starts the transport
|
||||
// [main-thread]
|
||||
void(CLAP_ABI *request_start)(const clap_host_t *host);
|
||||
|
||||
// Stops the transport, and jumps to the start point
|
||||
// [main-thread]
|
||||
void(CLAP_ABI *request_stop)(const clap_host_t *host);
|
||||
|
||||
// If not playing, starts the transport from its current position
|
||||
// [main-thread]
|
||||
void(CLAP_ABI *request_continue)(const clap_host_t *host);
|
||||
|
||||
// If playing, stops the transport at the current position
|
||||
// [main-thread]
|
||||
void(CLAP_ABI *request_pause)(const clap_host_t *host);
|
||||
|
||||
// Equivalent to what "space bar" does with most DAWs
|
||||
// [main-thread]
|
||||
void(CLAP_ABI *request_toggle_play)(const clap_host_t *host);
|
||||
|
||||
// Jumps the transport to the given position.
|
||||
// Does not start the transport.
|
||||
// [main-thread]
|
||||
void(CLAP_ABI *request_jump)(const clap_host_t *host, clap_beattime position);
|
||||
|
||||
// Sets the loop region
|
||||
// [main-thread]
|
||||
void(CLAP_ABI *request_loop_region)(const clap_host_t *host,
|
||||
clap_beattime start,
|
||||
clap_beattime duration);
|
||||
|
||||
// Toggles looping
|
||||
// [main-thread]
|
||||
void(CLAP_ABI *request_toggle_loop)(const clap_host_t *host);
|
||||
|
||||
// Enables/Disables looping
|
||||
// [main-thread]
|
||||
void(CLAP_ABI *request_enable_loop)(const clap_host_t *host, bool is_enabled);
|
||||
|
||||
// Enables/Disables recording
|
||||
// [main-thread]
|
||||
void(CLAP_ABI *request_record)(const clap_host_t *host, bool is_recording);
|
||||
|
||||
// Toggles recording
|
||||
// [main-thread]
|
||||
void(CLAP_ABI *request_toggle_record)(const clap_host_t *host);
|
||||
} clap_host_transport_control_t;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
144
tests/clap/ext/draft/triggers.h
Normal file
144
tests/clap/ext/draft/triggers.h
Normal file
|
|
@ -0,0 +1,144 @@
|
|||
#pragma once
|
||||
|
||||
#include "../../plugin.h"
|
||||
#include "../../events.h"
|
||||
#include "../../string-sizes.h"
|
||||
|
||||
static CLAP_CONSTEXPR const char CLAP_EXT_TRIGGERS[] = "clap.triggers/1";
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/// @page Trigger events
|
||||
///
|
||||
/// This extension enables the plugin to expose a set of triggers to the host.
|
||||
///
|
||||
/// Some examples for triggers:
|
||||
/// - trigger an envelope which is independent of the notes
|
||||
/// - trigger a sample-and-hold unit (maybe even per-voice)
|
||||
|
||||
enum {
|
||||
// Does this trigger support per note automations?
|
||||
CLAP_TRIGGER_IS_AUTOMATABLE_PER_NOTE_ID = 1 << 0,
|
||||
|
||||
// Does this trigger support per key automations?
|
||||
CLAP_TRIGGER_IS_AUTOMATABLE_PER_KEY = 1 << 1,
|
||||
|
||||
// Does this trigger support per channel automations?
|
||||
CLAP_TRIGGER_IS_AUTOMATABLE_PER_CHANNEL = 1 << 2,
|
||||
|
||||
// Does this trigger support per port automations?
|
||||
CLAP_TRIGGER_IS_AUTOMATABLE_PER_PORT = 1 << 3,
|
||||
};
|
||||
typedef uint32_t clap_trigger_info_flags;
|
||||
|
||||
// Given that this extension is still draft, it'll use the event-registry and its own event
|
||||
// namespace until we stabilize it.
|
||||
//
|
||||
// #include <clap/ext/event-registry.h>
|
||||
//
|
||||
// uint16_t CLAP_EXT_TRIGGER_EVENT_SPACE_ID = UINT16_MAX;
|
||||
// if (host_event_registry->query(host, CLAP_EXT_TRIGGERS, &CLAP_EXT_TRIGGER_EVENT_SPACE_ID)) {
|
||||
// /* we can use trigger events */
|
||||
// }
|
||||
//
|
||||
// /* later on */
|
||||
// clap_event_trigger ev;
|
||||
// ev.header.space_id = CLAP_EXT_TRIGGER_EVENT_SPACE_ID;
|
||||
// ev.header.type = CLAP_EVENT_TRIGGER;
|
||||
|
||||
enum { CLAP_EVENT_TRIGGER = 0 };
|
||||
|
||||
typedef struct clap_event_trigger {
|
||||
clap_event_header_t header;
|
||||
|
||||
// target trigger
|
||||
clap_id trigger_id; // @ref clap_trigger_info.id
|
||||
void *cookie; // @ref clap_trigger_info.cookie
|
||||
|
||||
// target a specific note_id, port, key and channel, -1 for global
|
||||
int32_t note_id;
|
||||
int16_t port_index;
|
||||
int16_t channel;
|
||||
int16_t key;
|
||||
} clap_event_trigger_t;
|
||||
|
||||
/* This describes a trigger */
|
||||
typedef struct clap_trigger_info {
|
||||
// stable trigger identifier, it must never change.
|
||||
clap_id id;
|
||||
|
||||
clap_trigger_info_flags flags;
|
||||
|
||||
// in analogy to clap_param_info.cookie
|
||||
void *cookie;
|
||||
|
||||
// displayable name
|
||||
char name[CLAP_NAME_SIZE];
|
||||
|
||||
// the module path containing the trigger, eg:"sequencers/seq1"
|
||||
// '/' will be used as a separator to show a tree like structure.
|
||||
char module[CLAP_PATH_SIZE];
|
||||
} clap_trigger_info_t;
|
||||
|
||||
typedef struct clap_plugin_triggers {
|
||||
// Returns the number of triggers.
|
||||
// [main-thread]
|
||||
uint32_t(CLAP_ABI *count)(const clap_plugin_t *plugin);
|
||||
|
||||
// Copies the trigger's info to trigger_info and returns true on success.
|
||||
// [main-thread]
|
||||
bool(CLAP_ABI *get_info)(const clap_plugin_t *plugin,
|
||||
uint32_t index,
|
||||
clap_trigger_info_t *trigger_info);
|
||||
} clap_plugin_triggers_t;
|
||||
|
||||
enum {
|
||||
// The trigger info did change, use this flag for:
|
||||
// - name change
|
||||
// - module change
|
||||
// New info takes effect immediately.
|
||||
CLAP_TRIGGER_RESCAN_INFO = 1 << 0,
|
||||
|
||||
// Invalidates everything the host knows about triggers.
|
||||
// It can only be used while the plugin is deactivated.
|
||||
// If the plugin is activated use clap_host->restart() and delay any change until the host calls
|
||||
// clap_plugin->deactivate().
|
||||
//
|
||||
// You must use this flag if:
|
||||
// - some triggers were added or removed.
|
||||
// - some triggers had critical changes:
|
||||
// - is_per_note (flag)
|
||||
// - is_per_key (flag)
|
||||
// - is_per_channel (flag)
|
||||
// - is_per_port (flag)
|
||||
// - cookie
|
||||
CLAP_TRIGGER_RESCAN_ALL = 1 << 1,
|
||||
};
|
||||
typedef uint32_t clap_trigger_rescan_flags;
|
||||
|
||||
enum {
|
||||
// Clears all possible references to a trigger
|
||||
CLAP_TRIGGER_CLEAR_ALL = 1 << 0,
|
||||
|
||||
// Clears all automations to a trigger
|
||||
CLAP_TRIGGER_CLEAR_AUTOMATIONS = 1 << 1,
|
||||
};
|
||||
typedef uint32_t clap_trigger_clear_flags;
|
||||
|
||||
typedef struct clap_host_triggers {
|
||||
// Rescan the full list of triggers according to the flags.
|
||||
// [main-thread]
|
||||
void(CLAP_ABI *rescan)(const clap_host_t *host, clap_trigger_rescan_flags flags);
|
||||
|
||||
// Clears references to a trigger.
|
||||
// [main-thread]
|
||||
void(CLAP_ABI *clear)(const clap_host_t *host,
|
||||
clap_id trigger_id,
|
||||
clap_trigger_clear_flags flags);
|
||||
} clap_host_triggers_t;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
76
tests/clap/ext/draft/tuning.h
Normal file
76
tests/clap/ext/draft/tuning.h
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
#pragma once
|
||||
|
||||
#include "../../plugin.h"
|
||||
#include "../../events.h"
|
||||
#include "../../string-sizes.h"
|
||||
|
||||
static CLAP_CONSTEXPR const char CLAP_EXT_TUNING[] = "clap.tuning/2";
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// Use clap_host_event_registry->query(host, CLAP_EXT_TUNING, &space_id) to know the event space.
|
||||
//
|
||||
// This event defines the tuning to be used on the given port/channel.
|
||||
typedef struct clap_event_tuning {
|
||||
clap_event_header_t header;
|
||||
|
||||
int16_t port_index; // -1 global
|
||||
int16_t channel; // 0..15, -1 global
|
||||
clap_id tunning_id;
|
||||
} clap_event_tuning_t;
|
||||
|
||||
typedef struct clap_tuning_info {
|
||||
clap_id tuning_id;
|
||||
char name[CLAP_NAME_SIZE];
|
||||
bool is_dynamic; // true if the values may vary with time
|
||||
} clap_tuning_info_t;
|
||||
|
||||
typedef struct clap_plugin_tuning {
|
||||
// Called when a tuning is added or removed from the pool.
|
||||
// [main-thread]
|
||||
void(CLAP_ABI *changed)(const clap_plugin_t *plugin);
|
||||
} clap_plugin_tuning_t;
|
||||
|
||||
// This extension provides a dynamic tuning table to the plugin.
|
||||
typedef struct clap_host_tuning {
|
||||
// Gets the relative tuning in semitones against equal temperament with A4=440Hz.
|
||||
// The plugin may query the tuning at a rate that makes sense for *low* frequency modulations.
|
||||
//
|
||||
// If the tuning_id is not found or equals to CLAP_INVALID_ID,
|
||||
// then the function shall gracefully return a sensible value.
|
||||
//
|
||||
// sample_offset is the sample offset from the beginning of the current process block.
|
||||
//
|
||||
// should_play(...) should be checked before calling this function.
|
||||
//
|
||||
// [audio-thread & in-process]
|
||||
double(CLAP_ABI *get_relative)(const clap_host_t *host,
|
||||
clap_id tuning_id,
|
||||
int32_t channel,
|
||||
int32_t key,
|
||||
uint32_t sample_offset);
|
||||
|
||||
// Returns true if the note should be played.
|
||||
// [audio-thread & in-process]
|
||||
bool(CLAP_ABI *should_play)(const clap_host_t *host,
|
||||
clap_id tuning_id,
|
||||
int32_t channel,
|
||||
int32_t key);
|
||||
|
||||
// Returns the number of tunings in the pool.
|
||||
// [main-thread]
|
||||
uint32_t(CLAP_ABI *get_tuning_count)(const clap_host_t *host);
|
||||
|
||||
// Gets info about a tuning
|
||||
// Returns true on success and stores the result into info.
|
||||
// [main-thread]
|
||||
bool(CLAP_ABI *get_info)(const clap_host_t *host,
|
||||
uint32_t tuning_index,
|
||||
clap_tuning_info_t *info);
|
||||
} clap_host_tuning_t;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
201
tests/clap/ext/draft/undo.h
Normal file
201
tests/clap/ext/draft/undo.h
Normal file
|
|
@ -0,0 +1,201 @@
|
|||
#pragma once
|
||||
|
||||
#include "../../plugin.h"
|
||||
#include "../../stream.h"
|
||||
|
||||
static CLAP_CONSTEXPR const char CLAP_EXT_UNDO[] = "clap.undo/4";
|
||||
static CLAP_CONSTEXPR const char CLAP_EXT_UNDO_CONTEXT[] = "clap.undo_context/4";
|
||||
static CLAP_CONSTEXPR const char CLAP_EXT_UNDO_DELTA[] = "clap.undo_delta/4";
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/// @page Undo
|
||||
///
|
||||
/// This extension enables the plugin to merge its undo history with the host.
|
||||
/// This leads to a single undo history shared by the host and many plugins.
|
||||
///
|
||||
/// Calling host->undo() or host->redo() is equivalent to clicking undo/redo within the host's GUI.
|
||||
///
|
||||
/// If the plugin uses this interface then its undo and redo should be entirely delegated to
|
||||
/// the host; clicking in the plugin's UI undo or redo is equivalent to clicking undo or redo in the
|
||||
/// host's UI.
|
||||
///
|
||||
/// Some changes are long running changes, for example a mouse interaction will begin editing some
|
||||
/// complex data and it may take multiple events and a long duration to complete the change.
|
||||
/// In such case the plugin will call host->begin_change() to indicate the beginning of a long
|
||||
/// running change and complete the change by calling host->change_made().
|
||||
///
|
||||
/// The host may group changes together:
|
||||
/// [---------------------------------]
|
||||
/// ^-T0 ^-T1 ^-T2 ^-T3
|
||||
/// Here a long running change C0 begin at T0.
|
||||
/// A instantaneous change C1 at T1, and another one C2 at T2.
|
||||
/// Then at T3 the long running change is completed.
|
||||
/// The host will then create a single undo step that will merge all the changes into C0.
|
||||
///
|
||||
/// This leads to another important consideration: starting a long running change without
|
||||
/// terminating is **VERY BAD**, because while a change is running it is impossible to call undo or
|
||||
/// redo.
|
||||
///
|
||||
/// Rationale: multiple designs were considered and this one has the benefit of having a single undo
|
||||
/// history. This simplifies the host implementation, leading to less bugs, a more robust design
|
||||
/// and maybe an easier experience for the user because there's a single undo context versus one
|
||||
/// for the host and one for each plugin instance.
|
||||
///
|
||||
/// This extension tries to make it as easy as possible for the plugin to hook into the host undo
|
||||
/// and make it efficient when possible by using deltas. The plugin interfaces are all optional, and
|
||||
/// the plugin can for a minimal implementation, just use the host interface and call
|
||||
/// host->change_made() without providing a delta. This is enough for the host to know that it can
|
||||
/// capture a plugin state for the undo step.
|
||||
|
||||
typedef struct clap_undo_delta_properties {
|
||||
// If true, then the plugin will provide deltas in host->change_made().
|
||||
// If false, then all clap_undo_delta_properties's attributes become irrelevant.
|
||||
bool has_delta;
|
||||
|
||||
// If true, then the deltas can be stored on disk and re-used in the future as long as the plugin
|
||||
// is compatible with the given format_version.
|
||||
//
|
||||
// If false, then format_version must be set to CLAP_INVALID_ID.
|
||||
bool are_deltas_persistent;
|
||||
|
||||
// This represents the delta format version that the plugin is currently using.
|
||||
// Use CLAP_INVALID_ID for invalid value.
|
||||
clap_id format_version;
|
||||
} clap_undo_delta_properties_t;
|
||||
|
||||
// Use CLAP_EXT_UNDO_DELTA.
|
||||
// This is an optional interface, using deltas is an optimization versus making a state snapshot.
|
||||
typedef struct clap_plugin_undo_delta {
|
||||
// Asks the plugin the delta properties.
|
||||
// [main-thread]
|
||||
void(CLAP_ABI *get_delta_properties)(const clap_plugin_t *plugin,
|
||||
clap_undo_delta_properties_t *properties);
|
||||
|
||||
// Asks the plugin if it can apply a delta using the given format version.
|
||||
// Returns true if it is possible.
|
||||
// [main-thread]
|
||||
bool(CLAP_ABI *can_use_delta_format_version)(const clap_plugin_t *plugin,
|
||||
clap_id format_version);
|
||||
|
||||
// Undo using the delta.
|
||||
// Returns true on success.
|
||||
//
|
||||
// [main-thread]
|
||||
bool(CLAP_ABI *undo)(const clap_plugin_t *plugin,
|
||||
clap_id format_version,
|
||||
const void *delta,
|
||||
size_t delta_size);
|
||||
|
||||
// Redo using the delta.
|
||||
// Returns true on success.
|
||||
//
|
||||
// [main-thread]
|
||||
bool(CLAP_ABI *redo)(const clap_plugin_t *plugin,
|
||||
clap_id format_version,
|
||||
const void *delta,
|
||||
size_t delta_size);
|
||||
} clap_plugin_undo_delta_t;
|
||||
|
||||
// Use CLAP_EXT_UNDO_CONTEXT.
|
||||
// This is an optional interface, that the plugin can implement in order to know about
|
||||
// the current undo context.
|
||||
typedef struct clap_plugin_undo_context {
|
||||
// Indicate if it is currently possible to perform an undo or redo operation.
|
||||
// [main-thread & plugin-subscribed-to-undo-context]
|
||||
void(CLAP_ABI *set_can_undo)(const clap_plugin_t *plugin, bool can_undo);
|
||||
void(CLAP_ABI *set_can_redo)(const clap_plugin_t *plugin, bool can_redo);
|
||||
|
||||
// Sets the name of the next undo or redo step.
|
||||
// name: null terminated string.
|
||||
// [main-thread & plugin-subscribed-to-undo-context]
|
||||
void(CLAP_ABI *set_undo_name)(const clap_plugin_t *plugin, const char *name);
|
||||
void(CLAP_ABI *set_redo_name)(const clap_plugin_t *plugin, const char *name);
|
||||
} clap_plugin_undo_context_t;
|
||||
|
||||
// Use CLAP_EXT_UNDO.
|
||||
typedef struct clap_host_undo {
|
||||
// Begins a long running change.
|
||||
// The plugin must not call this twice: there must be either a call to cancel_change() or
|
||||
// change_made() before calling begin_change() again.
|
||||
// [main-thread]
|
||||
void(CLAP_ABI *begin_change)(const clap_host_t *host);
|
||||
|
||||
// Cancels a long running change.
|
||||
// cancel_change() must not be called without a preceding begin_change().
|
||||
// [main-thread]
|
||||
void(CLAP_ABI *cancel_change)(const clap_host_t *host);
|
||||
|
||||
// Completes an undoable change.
|
||||
// At the moment of this function call, plugin_state->save() would include the current change.
|
||||
//
|
||||
// name: mandatory null terminated string describing the change, this is displayed to the user
|
||||
//
|
||||
// delta: optional, it is a binary blobs used to perform the undo and redo. When not available
|
||||
// the host will save the plugin state and use state->load() to perform undo and redo.
|
||||
// The plugin must be able to perform a redo operation using the delta, though the undo operation
|
||||
// is only possible if delta_can_undo is true.
|
||||
//
|
||||
// Note: the provided delta may be used for incremental state saving and crash recovery. The
|
||||
// plugin can indicate a format version id and the validity lifetime for the binary blobs.
|
||||
// The host can use these to verify the compatibility before applying the delta.
|
||||
// If the plugin is unable to use a delta, a notification should be provided to the user and
|
||||
// the crash recovery should perform a best effort job, at least restoring the latest saved
|
||||
// state.
|
||||
//
|
||||
// Special case: for objects with shared and synchronized state, changes shouldn't be reported
|
||||
// as the host already knows about it.
|
||||
// For example, plugin parameter changes shouldn't produce a call to change_made().
|
||||
//
|
||||
// Note: if the plugin asked for this interface, then host_state->mark_dirty() will not create an
|
||||
// implicit undo step.
|
||||
//
|
||||
// Note: if the plugin did load a preset or did something that leads to a large delta,
|
||||
// it may consider not producing a delta (pass null) and let the host make a state snapshot
|
||||
// instead.
|
||||
//
|
||||
// Note: if a plugin is producing a lot of changes within a small amount of time, the host
|
||||
// may merge them into a single undo step.
|
||||
//
|
||||
// [main-thread]
|
||||
void(CLAP_ABI *change_made)(const clap_host_t *host,
|
||||
const char *name,
|
||||
const void *delta,
|
||||
size_t delta_size,
|
||||
bool delta_can_undo);
|
||||
|
||||
// Asks the host to perform the next undo or redo step.
|
||||
//
|
||||
// Note: this maybe a complex and asynchronous operation, which may complete after
|
||||
// this function returns.
|
||||
//
|
||||
// Note: the host may ignore this request if there is no undo/redo step to perform,
|
||||
// or if the host is unable to perform undo/redo at the time (eg: a long running
|
||||
// change is going on).
|
||||
//
|
||||
// [main-thread]
|
||||
void(CLAP_ABI *request_undo)(const clap_host_t *host);
|
||||
void(CLAP_ABI *request_redo)(const clap_host_t *host);
|
||||
|
||||
// Subscribes to or unsubscribes from undo context info.
|
||||
//
|
||||
// This method helps reducing the number of calls the host has to perform when updating
|
||||
// the undo context info. Consider a large project with 1000+ plugins, we don't want to
|
||||
// call 1000+ times update, while the plugin may only need the context info if its GUI
|
||||
// is shown and it wants to display undo/redo info.
|
||||
//
|
||||
// Initial state is unsubscribed.
|
||||
//
|
||||
// is_subscribed: set to true to receive context info
|
||||
//
|
||||
// It is mandatory for the plugin to implement CLAP_EXT_UNDO_CONTEXT when using this method.
|
||||
//
|
||||
// [main-thread]
|
||||
void(CLAP_ABI *set_wants_context_updates)(const clap_host_t *host, bool is_subscribed);
|
||||
} clap_host_undo_t;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
22
tests/clap/ext/event-registry.h
Normal file
22
tests/clap/ext/event-registry.h
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
#pragma once
|
||||
|
||||
#include "../plugin.h"
|
||||
|
||||
static CLAP_CONSTEXPR const char CLAP_EXT_EVENT_REGISTRY[] = "clap.event-registry";
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct clap_host_event_registry {
|
||||
// Queries an event space id.
|
||||
// The space id 0 is reserved for CLAP's core events. See CLAP_CORE_EVENT_SPACE.
|
||||
//
|
||||
// Return false and sets *space_id to UINT16_MAX if the space name is unknown to the host.
|
||||
// [main-thread]
|
||||
bool(CLAP_ABI *query)(const clap_host_t *host, const char *space_name, uint16_t *space_id);
|
||||
} clap_host_event_registry_t;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
244
tests/clap/ext/gui.h
Normal file
244
tests/clap/ext/gui.h
Normal file
|
|
@ -0,0 +1,244 @@
|
|||
#pragma once
|
||||
|
||||
#include "../plugin.h"
|
||||
|
||||
/// @page GUI
|
||||
///
|
||||
/// This extension defines how the plugin will present its GUI.
|
||||
///
|
||||
/// There are two approaches:
|
||||
/// 1. the plugin creates a window and embeds it into the host's window
|
||||
/// 2. the plugin creates a floating window
|
||||
///
|
||||
/// Embedding the window gives more control to the host, and feels more integrated.
|
||||
/// Floating window are sometimes the only option due to technical limitations.
|
||||
///
|
||||
/// The Embedding protocol is by far the most common, supported by all hosts to date,
|
||||
/// and a plugin author should support at least that case.
|
||||
///
|
||||
/// Showing the GUI works as follow:
|
||||
/// 1. clap_plugin_gui->is_api_supported(), check what can work
|
||||
/// 2. clap_plugin_gui->create(), allocates gui resources
|
||||
/// 3. if the plugin window is floating
|
||||
/// 4. -> clap_plugin_gui->set_transient()
|
||||
/// 5. -> clap_plugin_gui->suggest_title()
|
||||
/// 6. else
|
||||
/// 7. -> clap_plugin_gui->set_scale()
|
||||
/// 8. -> clap_plugin_gui->can_resize()
|
||||
/// 9. -> if resizable and has known size from previous session, clap_plugin_gui->set_size()
|
||||
/// 10. -> else clap_plugin_gui->get_size(), gets initial size
|
||||
/// 11. -> clap_plugin_gui->set_parent()
|
||||
/// 12. clap_plugin_gui->show()
|
||||
/// 13. clap_plugin_gui->hide()/show() ...
|
||||
/// 14. clap_plugin_gui->destroy() when done with the gui
|
||||
///
|
||||
/// Resizing the window (initiated by the plugin, if embedded):
|
||||
/// 1. Plugins calls clap_host_gui->request_resize()
|
||||
/// 2. If the host returns true the new size is accepted,
|
||||
/// the host doesn't have to call clap_plugin_gui->set_size().
|
||||
/// If the host returns false, the new size is rejected.
|
||||
///
|
||||
/// Resizing the window (drag, if embedded)):
|
||||
/// 1. Only possible if clap_plugin_gui->can_resize() returns true
|
||||
/// 2. Mouse drag -> new_size
|
||||
/// 3. clap_plugin_gui->adjust_size(new_size) -> working_size
|
||||
/// 4. clap_plugin_gui->set_size(working_size)
|
||||
|
||||
static CLAP_CONSTEXPR const char CLAP_EXT_GUI[] = "clap.gui";
|
||||
|
||||
// If your windowing API is not listed here, please open an issue and we'll figure it out.
|
||||
// https://github.com/free-audio/clap/issues/new
|
||||
|
||||
// uses physical size
|
||||
// embed using https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-setparent
|
||||
static const CLAP_CONSTEXPR char CLAP_WINDOW_API_WIN32[] = "win32";
|
||||
|
||||
// uses logical size, don't call clap_plugin_gui->set_scale()
|
||||
static const CLAP_CONSTEXPR char CLAP_WINDOW_API_COCOA[] = "cocoa";
|
||||
|
||||
// uses physical size
|
||||
// embed using https://specifications.freedesktop.org/xembed-spec/xembed-spec-latest.html
|
||||
static const CLAP_CONSTEXPR char CLAP_WINDOW_API_X11[] = "x11";
|
||||
|
||||
// uses physical size
|
||||
// embed is currently not supported, use floating windows
|
||||
static const CLAP_CONSTEXPR char CLAP_WINDOW_API_WAYLAND[] = "wayland";
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef void *clap_hwnd;
|
||||
typedef void *clap_nsview;
|
||||
typedef unsigned long clap_xwnd;
|
||||
|
||||
// Represent a window reference.
|
||||
typedef struct clap_window {
|
||||
const char *api; // one of CLAP_WINDOW_API_XXX
|
||||
union {
|
||||
clap_nsview cocoa;
|
||||
clap_xwnd x11;
|
||||
clap_hwnd win32;
|
||||
void *ptr; // for anything defined outside of clap
|
||||
};
|
||||
} clap_window_t;
|
||||
|
||||
// Information to improve window resizing when initiated by the host or window manager.
|
||||
typedef struct clap_gui_resize_hints {
|
||||
bool can_resize_horizontally;
|
||||
bool can_resize_vertically;
|
||||
|
||||
// if both horizontal and vertical resize are available, do we preserve the
|
||||
// aspect ratio, and if so, what is the width x height aspect ratio to preserve.
|
||||
// These flags are unused if can_resize_horizontally or vertically are false,
|
||||
// and ratios are unused if preserve is false.
|
||||
bool preserve_aspect_ratio;
|
||||
uint32_t aspect_ratio_width;
|
||||
uint32_t aspect_ratio_height;
|
||||
} clap_gui_resize_hints_t;
|
||||
|
||||
// Size (width, height) is in pixels; the corresponding windowing system extension is
|
||||
// responsible for defining if it is physical pixels or logical pixels.
|
||||
typedef struct clap_plugin_gui {
|
||||
// Returns true if the requested gui api is supported, either in floating (plugin-created)
|
||||
// or non-floating (embedded) mode.
|
||||
// [main-thread]
|
||||
bool(CLAP_ABI *is_api_supported)(const clap_plugin_t *plugin, const char *api, bool is_floating);
|
||||
|
||||
// Returns true if the plugin has a preferred api.
|
||||
// The host has no obligation to honor the plugin preference, this is just a hint.
|
||||
// The const char **api variable should be explicitly assigned as a pointer to
|
||||
// one of the CLAP_WINDOW_API_ constants defined above, not strcopied.
|
||||
// [main-thread]
|
||||
bool(CLAP_ABI *get_preferred_api)(const clap_plugin_t *plugin,
|
||||
const char **api,
|
||||
bool *is_floating);
|
||||
|
||||
// Create and allocate all resources necessary for the gui.
|
||||
//
|
||||
// If is_floating is true, then the window will not be managed by the host. The plugin
|
||||
// can set its window to stays above the parent window, see set_transient().
|
||||
// api may be null or blank for floating window.
|
||||
//
|
||||
// If is_floating is false, then the plugin has to embed its window into the parent window, see
|
||||
// set_parent().
|
||||
//
|
||||
// After this call, the GUI may not be visible yet; don't forget to call show().
|
||||
//
|
||||
// Returns true if the GUI is successfully created.
|
||||
// [main-thread]
|
||||
bool(CLAP_ABI *create)(const clap_plugin_t *plugin, const char *api, bool is_floating);
|
||||
|
||||
// Free all resources associated with the gui.
|
||||
// [main-thread]
|
||||
void(CLAP_ABI *destroy)(const clap_plugin_t *plugin);
|
||||
|
||||
// Set the absolute GUI scaling factor, and override any OS info.
|
||||
// Should not be used if the windowing api relies upon logical pixels.
|
||||
//
|
||||
// If the plugin prefers to work out the scaling factor itself by querying the OS directly,
|
||||
// then ignore the call.
|
||||
//
|
||||
// scale = 2 means 200% scaling.
|
||||
//
|
||||
// Returns true if the scaling could be applied
|
||||
// Returns false if the call was ignored, or the scaling could not be applied.
|
||||
// [main-thread]
|
||||
bool(CLAP_ABI *set_scale)(const clap_plugin_t *plugin, double scale);
|
||||
|
||||
// Get the current size of the plugin UI.
|
||||
// clap_plugin_gui->create() must have been called prior to asking the size.
|
||||
//
|
||||
// Returns true if the plugin could get the size.
|
||||
// [main-thread]
|
||||
bool(CLAP_ABI *get_size)(const clap_plugin_t *plugin, uint32_t *width, uint32_t *height);
|
||||
|
||||
// Returns true if the window is resizeable (mouse drag).
|
||||
// [main-thread & !floating]
|
||||
bool(CLAP_ABI *can_resize)(const clap_plugin_t *plugin);
|
||||
|
||||
// Returns true if the plugin can provide hints on how to resize the window.
|
||||
// [main-thread & !floating]
|
||||
bool(CLAP_ABI *get_resize_hints)(const clap_plugin_t *plugin, clap_gui_resize_hints_t *hints);
|
||||
|
||||
// If the plugin gui is resizable, then the plugin will calculate the closest
|
||||
// usable size which fits in the given size.
|
||||
// This method does not change the size.
|
||||
//
|
||||
// Returns true if the plugin could adjust the given size.
|
||||
// [main-thread & !floating]
|
||||
bool(CLAP_ABI *adjust_size)(const clap_plugin_t *plugin, uint32_t *width, uint32_t *height);
|
||||
|
||||
// Sets the window size.
|
||||
//
|
||||
// Returns true if the plugin could resize its window to the given size.
|
||||
// [main-thread & !floating]
|
||||
bool(CLAP_ABI *set_size)(const clap_plugin_t *plugin, uint32_t width, uint32_t height);
|
||||
|
||||
// Embeds the plugin window into the given window.
|
||||
//
|
||||
// Returns true on success.
|
||||
// [main-thread & !floating]
|
||||
bool(CLAP_ABI *set_parent)(const clap_plugin_t *plugin, const clap_window_t *window);
|
||||
|
||||
// Set the plugin floating window to stay above the given window.
|
||||
//
|
||||
// Returns true on success.
|
||||
// [main-thread & floating]
|
||||
bool(CLAP_ABI *set_transient)(const clap_plugin_t *plugin, const clap_window_t *window);
|
||||
|
||||
// Suggests a window title. Only for floating windows.
|
||||
//
|
||||
// [main-thread & floating]
|
||||
void(CLAP_ABI *suggest_title)(const clap_plugin_t *plugin, const char *title);
|
||||
|
||||
// Show the window.
|
||||
//
|
||||
// Returns true on success.
|
||||
// [main-thread]
|
||||
bool(CLAP_ABI *show)(const clap_plugin_t *plugin);
|
||||
|
||||
// Hide the window, this method does not free the resources, it just hides
|
||||
// the window content. Yet it may be a good idea to stop painting timers.
|
||||
//
|
||||
// Returns true on success.
|
||||
// [main-thread]
|
||||
bool(CLAP_ABI *hide)(const clap_plugin_t *plugin);
|
||||
} clap_plugin_gui_t;
|
||||
|
||||
typedef struct clap_host_gui {
|
||||
// The host should call get_resize_hints() again.
|
||||
// [thread-safe & !floating]
|
||||
void(CLAP_ABI *resize_hints_changed)(const clap_host_t *host);
|
||||
|
||||
// Request the host to resize the client area to width, height.
|
||||
// Return true if the new size is accepted, false otherwise.
|
||||
// The host doesn't have to call set_size().
|
||||
//
|
||||
// Note: if not called from the main thread, then a return value simply means that the host
|
||||
// acknowledged the request and will process it asynchronously. If the request then can't be
|
||||
// satisfied then the host will call set_size() to revert the operation.
|
||||
// [thread-safe & !floating]
|
||||
bool(CLAP_ABI *request_resize)(const clap_host_t *host, uint32_t width, uint32_t height);
|
||||
|
||||
// Request the host to show the plugin gui.
|
||||
// Return true on success, false otherwise.
|
||||
// [thread-safe]
|
||||
bool(CLAP_ABI *request_show)(const clap_host_t *host);
|
||||
|
||||
// Request the host to hide the plugin gui.
|
||||
// Return true on success, false otherwise.
|
||||
// [thread-safe]
|
||||
bool(CLAP_ABI *request_hide)(const clap_host_t *host);
|
||||
|
||||
// The floating window has been closed, or the connection to the gui has been lost.
|
||||
//
|
||||
// If was_destroyed is true, then the host must call clap_plugin_gui->destroy() to acknowledge
|
||||
// the gui destruction.
|
||||
// [thread-safe]
|
||||
void(CLAP_ABI *closed)(const clap_host_t *host, bool was_destroyed);
|
||||
} clap_host_gui_t;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
27
tests/clap/ext/latency.h
Normal file
27
tests/clap/ext/latency.h
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
#pragma once
|
||||
|
||||
#include "../plugin.h"
|
||||
|
||||
static CLAP_CONSTEXPR const char CLAP_EXT_LATENCY[] = "clap.latency";
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct clap_plugin_latency {
|
||||
// Returns the plugin latency in samples.
|
||||
// [main-thread & (being-activated | active)]
|
||||
uint32_t(CLAP_ABI *get)(const clap_plugin_t *plugin);
|
||||
} clap_plugin_latency_t;
|
||||
|
||||
typedef struct clap_host_latency {
|
||||
// Tell the host that the latency changed.
|
||||
// The latency is only allowed to change during plugin->activate.
|
||||
// If the plugin is activated, call host->request_restart()
|
||||
// [main-thread & being-activated]
|
||||
void(CLAP_ABI *changed)(const clap_host_t *host);
|
||||
} clap_host_latency_t;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
33
tests/clap/ext/log.h
Normal file
33
tests/clap/ext/log.h
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
#pragma once
|
||||
|
||||
#include "../plugin.h"
|
||||
|
||||
static CLAP_CONSTEXPR const char CLAP_EXT_LOG[] = "clap.log";
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
enum {
|
||||
CLAP_LOG_DEBUG = 0,
|
||||
CLAP_LOG_INFO = 1,
|
||||
CLAP_LOG_WARNING = 2,
|
||||
CLAP_LOG_ERROR = 3,
|
||||
CLAP_LOG_FATAL = 4,
|
||||
|
||||
// These severities should be used to report misbehaviour.
|
||||
// The plugin one can be used by a layer between the plugin and the host.
|
||||
CLAP_LOG_HOST_MISBEHAVING = 5,
|
||||
CLAP_LOG_PLUGIN_MISBEHAVING = 6,
|
||||
};
|
||||
typedef int32_t clap_log_severity;
|
||||
|
||||
typedef struct clap_host_log {
|
||||
// Log a message through the host.
|
||||
// [thread-safe]
|
||||
void(CLAP_ABI *log)(const clap_host_t *host, clap_log_severity severity, const char *msg);
|
||||
} clap_host_log_t;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
37
tests/clap/ext/note-name.h
Normal file
37
tests/clap/ext/note-name.h
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
#pragma once
|
||||
|
||||
#include "../plugin.h"
|
||||
#include "../string-sizes.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
static CLAP_CONSTEXPR const char CLAP_EXT_NOTE_NAME[] = "clap.note-name";
|
||||
|
||||
typedef struct clap_note_name {
|
||||
char name[CLAP_NAME_SIZE];
|
||||
int16_t port; // -1 for every port
|
||||
int16_t key; // -1 for every key
|
||||
int16_t channel; // -1 for every channel
|
||||
} clap_note_name_t;
|
||||
|
||||
typedef struct clap_plugin_note_name {
|
||||
// Return the number of note names
|
||||
// [main-thread]
|
||||
uint32_t(CLAP_ABI *count)(const clap_plugin_t *plugin);
|
||||
|
||||
// Returns true on success and stores the result into note_name
|
||||
// [main-thread]
|
||||
bool(CLAP_ABI *get)(const clap_plugin_t *plugin, uint32_t index, clap_note_name_t *note_name);
|
||||
} clap_plugin_note_name_t;
|
||||
|
||||
typedef struct clap_host_note_name {
|
||||
// Informs the host that the note names have changed.
|
||||
// [main-thread]
|
||||
void(CLAP_ABI *changed)(const clap_host_t *host);
|
||||
} clap_host_note_name_t;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
79
tests/clap/ext/note-ports.h
Normal file
79
tests/clap/ext/note-ports.h
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
#pragma once
|
||||
|
||||
#include "../plugin.h"
|
||||
#include "../string-sizes.h"
|
||||
|
||||
/// @page Note Ports
|
||||
///
|
||||
/// This extension provides a way for the plugin to describe its current note ports.
|
||||
/// If the plugin does not implement this extension, it won't have note input or output.
|
||||
/// The plugin is only allowed to change its note ports configuration while it is deactivated.
|
||||
|
||||
static CLAP_CONSTEXPR const char CLAP_EXT_NOTE_PORTS[] = "clap.note-ports";
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
enum clap_note_dialect {
|
||||
// Uses clap_event_note and clap_event_note_expression.
|
||||
CLAP_NOTE_DIALECT_CLAP = 1 << 0,
|
||||
|
||||
// Uses clap_event_midi, no polyphonic expression
|
||||
CLAP_NOTE_DIALECT_MIDI = 1 << 1,
|
||||
|
||||
// Uses clap_event_midi, with polyphonic expression (MPE)
|
||||
CLAP_NOTE_DIALECT_MIDI_MPE = 1 << 2,
|
||||
|
||||
// Uses clap_event_midi2
|
||||
CLAP_NOTE_DIALECT_MIDI2 = 1 << 3,
|
||||
};
|
||||
|
||||
typedef struct clap_note_port_info {
|
||||
// id identifies a port and must be stable.
|
||||
// id may overlap between input and output ports.
|
||||
clap_id id;
|
||||
uint32_t supported_dialects; // bitfield, see clap_note_dialect
|
||||
uint32_t preferred_dialect; // one value of clap_note_dialect
|
||||
char name[CLAP_NAME_SIZE]; // displayable name, i18n?
|
||||
} clap_note_port_info_t;
|
||||
|
||||
// The note ports scan has to be done while the plugin is deactivated.
|
||||
typedef struct clap_plugin_note_ports {
|
||||
// Number of ports, for either input or output.
|
||||
// [main-thread]
|
||||
uint32_t(CLAP_ABI *count)(const clap_plugin_t *plugin, bool is_input);
|
||||
|
||||
// Get info about a note port.
|
||||
// Returns true on success and stores the result into info.
|
||||
// [main-thread]
|
||||
bool(CLAP_ABI *get)(const clap_plugin_t *plugin,
|
||||
uint32_t index,
|
||||
bool is_input,
|
||||
clap_note_port_info_t *info);
|
||||
} clap_plugin_note_ports_t;
|
||||
|
||||
enum {
|
||||
// The ports have changed, the host shall perform a full scan of the ports.
|
||||
// This flag can only be used if the plugin is not active.
|
||||
// If the plugin active, call host->request_restart() and then call rescan()
|
||||
// when the host calls deactivate()
|
||||
CLAP_NOTE_PORTS_RESCAN_ALL = 1 << 0,
|
||||
|
||||
// The ports name did change, the host can scan them right away.
|
||||
CLAP_NOTE_PORTS_RESCAN_NAMES = 1 << 1,
|
||||
};
|
||||
|
||||
typedef struct clap_host_note_ports {
|
||||
// Query which dialects the host supports
|
||||
// [main-thread]
|
||||
uint32_t(CLAP_ABI *supported_dialects)(const clap_host_t *host);
|
||||
|
||||
// Rescan the full list of note ports according to the flags.
|
||||
// [main-thread]
|
||||
void(CLAP_ABI *rescan)(const clap_host_t *host, uint32_t flags);
|
||||
} clap_host_note_ports_t;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
77
tests/clap/ext/param-indication.h
Normal file
77
tests/clap/ext/param-indication.h
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
#pragma once
|
||||
|
||||
#include "params.h"
|
||||
#include "../color.h"
|
||||
|
||||
// This extension lets the host tell the plugin to display a little color based indication on the
|
||||
// parameter. This can be used to indicate:
|
||||
// - a physical controller is mapped to a parameter
|
||||
// - the parameter is current playing an automation
|
||||
// - the parameter is overriding the automation
|
||||
// - etc...
|
||||
//
|
||||
// The color semantic depends upon the host here and the goal is to have a consistent experience
|
||||
// across all plugins.
|
||||
|
||||
static CLAP_CONSTEXPR const char CLAP_EXT_PARAM_INDICATION[] = "clap.param-indication/4";
|
||||
|
||||
// The latest draft is 100% compatible.
|
||||
// This compat ID may be removed in 2026.
|
||||
static CLAP_CONSTEXPR const char CLAP_EXT_PARAM_INDICATION_COMPAT[] = "clap.param-indication.draft/4";
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
enum {
|
||||
// The host doesn't have an automation for this parameter
|
||||
CLAP_PARAM_INDICATION_AUTOMATION_NONE = 0,
|
||||
|
||||
// The host has an automation for this parameter, but it isn't playing it
|
||||
CLAP_PARAM_INDICATION_AUTOMATION_PRESENT = 1,
|
||||
|
||||
// The host is playing an automation for this parameter
|
||||
CLAP_PARAM_INDICATION_AUTOMATION_PLAYING = 2,
|
||||
|
||||
// The host is recording an automation on this parameter
|
||||
CLAP_PARAM_INDICATION_AUTOMATION_RECORDING = 3,
|
||||
|
||||
// The host should play an automation for this parameter, but the user has started to adjust this
|
||||
// parameter and is overriding the automation playback
|
||||
CLAP_PARAM_INDICATION_AUTOMATION_OVERRIDING = 4,
|
||||
};
|
||||
|
||||
typedef struct clap_plugin_param_indication {
|
||||
// Sets or clears a mapping indication.
|
||||
//
|
||||
// has_mapping: does the parameter currently has a mapping?
|
||||
// color: if set, the color to use to highlight the control in the plugin GUI
|
||||
// label: if set, a small string to display on top of the knob which identifies the hardware
|
||||
// controller description: if set, a string which can be used in a tooltip, which describes the
|
||||
// current mapping
|
||||
//
|
||||
// Parameter indications should not be saved in the plugin context, and are off by default.
|
||||
// [main-thread]
|
||||
void(CLAP_ABI *set_mapping)(const clap_plugin_t *plugin,
|
||||
clap_id param_id,
|
||||
bool has_mapping,
|
||||
const clap_color_t *color,
|
||||
const char *label,
|
||||
const char *description);
|
||||
|
||||
// Sets or clears an automation indication.
|
||||
//
|
||||
// automation_state: current automation state for the given parameter
|
||||
// color: if set, the color to use to display the automation indication in the plugin GUI
|
||||
//
|
||||
// Parameter indications should not be saved in the plugin context, and are off by default.
|
||||
// [main-thread]
|
||||
void(CLAP_ABI *set_automation)(const clap_plugin_t *plugin,
|
||||
clap_id param_id,
|
||||
uint32_t automation_state,
|
||||
const clap_color_t *color);
|
||||
} clap_plugin_param_indication_t;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
382
tests/clap/ext/params.h
Normal file
382
tests/clap/ext/params.h
Normal file
|
|
@ -0,0 +1,382 @@
|
|||
#pragma once
|
||||
|
||||
#include "../plugin.h"
|
||||
#include "../string-sizes.h"
|
||||
|
||||
/// @page Parameters
|
||||
/// @brief parameters management
|
||||
///
|
||||
/// Main idea:
|
||||
///
|
||||
/// The host sees the plugin as an atomic entity; and acts as a controller on top of its parameters.
|
||||
/// The plugin is responsible for keeping its audio processor and its GUI in sync.
|
||||
///
|
||||
/// The host can at any time read parameters' value on the [main-thread] using
|
||||
/// @ref clap_plugin_params.get_value().
|
||||
///
|
||||
/// There are two options to communicate parameter value changes, and they are not concurrent.
|
||||
/// - send automation points during clap_plugin.process()
|
||||
/// - send automation points during clap_plugin_params.flush(), for parameter changes
|
||||
/// without processing audio
|
||||
///
|
||||
/// When the plugin changes a parameter value, it must inform the host.
|
||||
/// It will send @ref CLAP_EVENT_PARAM_VALUE event during process() or flush().
|
||||
/// If the user is adjusting the value, don't forget to mark the beginning and end
|
||||
/// of the gesture by sending CLAP_EVENT_PARAM_GESTURE_BEGIN and CLAP_EVENT_PARAM_GESTURE_END
|
||||
/// events.
|
||||
///
|
||||
/// @note MIDI CCs are tricky because you may not know when the parameter adjustment ends.
|
||||
/// Also if the host records incoming MIDI CC and parameter change automation at the same time,
|
||||
/// there will be a conflict at playback: MIDI CC vs Automation.
|
||||
/// The parameter automation will always target the same parameter because the param_id is stable.
|
||||
/// The MIDI CC may have a different mapping in the future and may result in a different playback.
|
||||
///
|
||||
/// When a MIDI CC changes a parameter's value, set the flag CLAP_EVENT_DONT_RECORD in
|
||||
/// clap_event_param.header.flags. That way the host may record the MIDI CC automation, but not the
|
||||
/// parameter change and there won't be conflict at playback.
|
||||
///
|
||||
/// Scenarios:
|
||||
///
|
||||
/// I. Loading a preset
|
||||
/// - load the preset in a temporary state
|
||||
/// - call @ref clap_host_params.rescan() if anything changed
|
||||
/// - call @ref clap_host_latency.changed() if latency changed
|
||||
/// - invalidate any other info that may be cached by the host
|
||||
/// - if the plugin is activated and the preset will introduce breaking changes
|
||||
/// (latency, audio ports, new parameters, ...) be sure to wait for the host
|
||||
/// to deactivate the plugin to apply those changes.
|
||||
/// If there are no breaking changes, the plugin can apply them them right away.
|
||||
/// The plugin is responsible for updating both its audio processor and its gui.
|
||||
///
|
||||
/// II. Turning a knob on the DAW interface
|
||||
/// - the host will send an automation event to the plugin via a process() or flush()
|
||||
///
|
||||
/// III. Turning a knob on the Plugin interface
|
||||
/// - the plugin is responsible for sending the parameter value to its audio processor
|
||||
/// - call clap_host_params->request_flush() or clap_host->request_process().
|
||||
/// - when the host calls either clap_plugin->process() or clap_plugin_params->flush(),
|
||||
/// send an automation event and don't forget to wrap the parameter change(s)
|
||||
/// with CLAP_EVENT_PARAM_GESTURE_BEGIN and CLAP_EVENT_PARAM_GESTURE_END to define the
|
||||
/// beginning and end of the gesture.
|
||||
///
|
||||
/// IV. Turning a knob via automation
|
||||
/// - host sends an automation point during clap_plugin->process() or clap_plugin_params->flush().
|
||||
/// - the plugin is responsible for updating its GUI
|
||||
///
|
||||
/// V. Turning a knob via plugin's internal MIDI mapping
|
||||
/// - the plugin sends a CLAP_EVENT_PARAM_VALUE output event, set should_record to false
|
||||
/// - the plugin is responsible for updating its GUI
|
||||
///
|
||||
/// VI. Adding or removing parameters
|
||||
/// - if the plugin is activated call clap_host->restart()
|
||||
/// - once the plugin isn't active:
|
||||
/// - apply the new state
|
||||
/// - if a parameter is gone or is created with an id that may have been used before,
|
||||
/// call clap_host_params.clear(host, param_id, CLAP_PARAM_CLEAR_ALL)
|
||||
/// - call clap_host_params->rescan(CLAP_PARAM_RESCAN_ALL)
|
||||
///
|
||||
/// CLAP allows the plugin to change the parameter range, yet the plugin developer
|
||||
/// should be aware that doing so isn't without risk, especially if you made the
|
||||
/// promise to never change the sound. If you want to be 100% certain that the
|
||||
/// sound will not change with all host, then simply never change the range.
|
||||
///
|
||||
/// There are two approaches to automations, either you automate the plain value,
|
||||
/// or you automate the knob position. The first option will be robust to a range
|
||||
/// increase, while the second won't be.
|
||||
///
|
||||
/// If the host goes with the second approach (automating the knob position), it means
|
||||
/// that the plugin is hosted in a relaxed environment regarding sound changes (they are
|
||||
/// accepted, and not a concern as long as they are reasonable). Though, stepped parameters
|
||||
/// should be stored as plain value in the document.
|
||||
///
|
||||
/// If the host goes with the first approach, there will still be situation where the
|
||||
/// sound may inevitably change. For example, if the plugin increase the range, there
|
||||
/// is an automation playing at the max value and on top of that an LFO is applied.
|
||||
/// See the following curve:
|
||||
/// .
|
||||
/// . .
|
||||
/// ..... . .
|
||||
/// before: . . and after: . .
|
||||
///
|
||||
/// Persisting parameter values:
|
||||
///
|
||||
/// Plugins are responsible for persisting their parameter's values between
|
||||
/// sessions by implementing the state extension. Otherwise parameter value will
|
||||
/// not be recalled when reloading a project. Hosts should _not_ try to save and
|
||||
/// restore parameter values for plugins that don't implement the state
|
||||
/// extension.
|
||||
///
|
||||
/// Advice for the host:
|
||||
///
|
||||
/// - store plain values in the document (automation)
|
||||
/// - store modulation amount in plain value delta, not in percentage
|
||||
/// - when you apply a CC mapping, remember the min/max plain values so you can adjust
|
||||
/// - do not implement a parameter saving fall back for plugins that don't
|
||||
/// implement the state extension
|
||||
///
|
||||
/// Advice for the plugin:
|
||||
///
|
||||
/// - think carefully about your parameter range when designing your DSP
|
||||
/// - avoid shrinking parameter ranges, they are very likely to change the sound
|
||||
/// - consider changing the parameter range as a tradeoff: what you improve vs what you break
|
||||
/// - make sure to implement saving and loading the parameter values using the
|
||||
/// state extension
|
||||
/// - if you plan to use adapters for other plugin formats, then you need to pay extra
|
||||
/// attention to the adapter requirements
|
||||
|
||||
static CLAP_CONSTEXPR const char CLAP_EXT_PARAMS[] = "clap.params";
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
enum {
|
||||
// Is this param stepped? (integer values only)
|
||||
// if so the double value is converted to integer using a cast (equivalent to trunc).
|
||||
CLAP_PARAM_IS_STEPPED = 1 << 0,
|
||||
|
||||
// Useful for periodic parameters like a phase
|
||||
CLAP_PARAM_IS_PERIODIC = 1 << 1,
|
||||
|
||||
// The parameter should not be shown to the user, because it is currently not used.
|
||||
// It is not necessary to process automation for this parameter.
|
||||
CLAP_PARAM_IS_HIDDEN = 1 << 2,
|
||||
|
||||
// The parameter can't be changed by the host.
|
||||
CLAP_PARAM_IS_READONLY = 1 << 3,
|
||||
|
||||
// This parameter is used to merge the plugin and host bypass button.
|
||||
// It implies that the parameter is stepped.
|
||||
// min: 0 -> bypass off
|
||||
// max: 1 -> bypass on
|
||||
CLAP_PARAM_IS_BYPASS = 1 << 4,
|
||||
|
||||
// When set:
|
||||
// - automation can be recorded
|
||||
// - automation can be played back
|
||||
//
|
||||
// The host can send live user changes for this parameter regardless of this flag.
|
||||
//
|
||||
// If this parameter affects the internal processing structure of the plugin, ie: max delay, fft
|
||||
// size, ... and the plugins needs to re-allocate its working buffers, then it should call
|
||||
// host->request_restart(), and perform the change once the plugin is re-activated.
|
||||
CLAP_PARAM_IS_AUTOMATABLE = 1 << 5,
|
||||
|
||||
// Does this parameter support per note automations?
|
||||
CLAP_PARAM_IS_AUTOMATABLE_PER_NOTE_ID = 1 << 6,
|
||||
|
||||
// Does this parameter support per key automations?
|
||||
CLAP_PARAM_IS_AUTOMATABLE_PER_KEY = 1 << 7,
|
||||
|
||||
// Does this parameter support per channel automations?
|
||||
CLAP_PARAM_IS_AUTOMATABLE_PER_CHANNEL = 1 << 8,
|
||||
|
||||
// Does this parameter support per port automations?
|
||||
CLAP_PARAM_IS_AUTOMATABLE_PER_PORT = 1 << 9,
|
||||
|
||||
// Does this parameter support the modulation signal?
|
||||
CLAP_PARAM_IS_MODULATABLE = 1 << 10,
|
||||
|
||||
// Does this parameter support per note modulations?
|
||||
CLAP_PARAM_IS_MODULATABLE_PER_NOTE_ID = 1 << 11,
|
||||
|
||||
// Does this parameter support per key modulations?
|
||||
CLAP_PARAM_IS_MODULATABLE_PER_KEY = 1 << 12,
|
||||
|
||||
// Does this parameter support per channel modulations?
|
||||
CLAP_PARAM_IS_MODULATABLE_PER_CHANNEL = 1 << 13,
|
||||
|
||||
// Does this parameter support per port modulations?
|
||||
CLAP_PARAM_IS_MODULATABLE_PER_PORT = 1 << 14,
|
||||
|
||||
// Any change to this parameter will affect the plugin output and requires to be done via
|
||||
// process() if the plugin is active.
|
||||
//
|
||||
// A simple example would be a DC Offset, changing it will change the output signal and must be
|
||||
// processed.
|
||||
CLAP_PARAM_REQUIRES_PROCESS = 1 << 15,
|
||||
|
||||
// This parameter represents an enumerated value.
|
||||
// If you set this flag, then you must set CLAP_PARAM_IS_STEPPED too.
|
||||
// All values from min to max must not have a blank value_to_text().
|
||||
CLAP_PARAM_IS_ENUM = 1 << 16,
|
||||
};
|
||||
typedef uint32_t clap_param_info_flags;
|
||||
|
||||
/* This describes a parameter */
|
||||
typedef struct clap_param_info {
|
||||
// Stable parameter identifier, it must never change.
|
||||
clap_id id;
|
||||
|
||||
clap_param_info_flags flags;
|
||||
|
||||
// This value is optional and set by the plugin.
|
||||
// Its purpose is to provide fast access to the plugin parameter object by caching its pointer.
|
||||
// For instance:
|
||||
//
|
||||
// in clap_plugin_params.get_info():
|
||||
// Parameter *p = findParameter(param_id);
|
||||
// param_info->cookie = p;
|
||||
//
|
||||
// later, in clap_plugin.process():
|
||||
//
|
||||
// Parameter *p = (Parameter *)event->cookie;
|
||||
// if (!p) [[unlikely]]
|
||||
// p = findParameter(event->param_id);
|
||||
//
|
||||
// where findParameter() is a function the plugin implements to map parameter ids to internal
|
||||
// objects.
|
||||
//
|
||||
// Important:
|
||||
// - The cookie is invalidated by a call to clap_host_params->rescan(CLAP_PARAM_RESCAN_ALL) or
|
||||
// when the plugin is destroyed.
|
||||
// - The host will either provide the cookie as issued or nullptr in events addressing
|
||||
// parameters.
|
||||
// - The plugin must gracefully handle the case of a cookie which is nullptr.
|
||||
// - Many plugins will process the parameter events more quickly if the host can provide the
|
||||
// cookie in a faster time than a hashmap lookup per param per event.
|
||||
void *cookie;
|
||||
|
||||
// The display name. eg: "Volume". This does not need to be unique. Do not include the module
|
||||
// text in this. The host should concatenate/format the module + name in the case where showing
|
||||
// the name alone would be too vague.
|
||||
char name[CLAP_NAME_SIZE];
|
||||
|
||||
// The module path containing the param, eg: "Oscillators/Wavetable 1".
|
||||
// '/' will be used as a separator to show a tree-like structure.
|
||||
char module[CLAP_PATH_SIZE];
|
||||
|
||||
double min_value; // Minimum plain value. Must be finite (`std::isfinite` true)
|
||||
double max_value; // Maximum plain value. Must be finite
|
||||
double default_value; // Default plain value. Must be in [min, max] range.
|
||||
} clap_param_info_t;
|
||||
|
||||
typedef struct clap_plugin_params {
|
||||
// Returns the number of parameters.
|
||||
// [main-thread]
|
||||
uint32_t(CLAP_ABI *count)(const clap_plugin_t *plugin);
|
||||
|
||||
// Copies the parameter's info to param_info.
|
||||
// Returns true on success.
|
||||
// [main-thread]
|
||||
bool(CLAP_ABI *get_info)(const clap_plugin_t *plugin,
|
||||
uint32_t param_index,
|
||||
clap_param_info_t *param_info);
|
||||
|
||||
// Writes the parameter's current value to out_value.
|
||||
// Returns true on success.
|
||||
// [main-thread]
|
||||
bool(CLAP_ABI *get_value)(const clap_plugin_t *plugin, clap_id param_id, double *out_value);
|
||||
|
||||
// Fills out_buffer with a null-terminated UTF-8 string that represents the parameter at the
|
||||
// given 'value' argument. eg: "2.3 kHz". The host should always use this to format parameter
|
||||
// values before displaying it to the user.
|
||||
// Returns true on success.
|
||||
// [main-thread]
|
||||
bool(CLAP_ABI *value_to_text)(const clap_plugin_t *plugin,
|
||||
clap_id param_id,
|
||||
double value,
|
||||
char *out_buffer,
|
||||
uint32_t out_buffer_capacity);
|
||||
|
||||
// Converts the null-terminated UTF-8 param_value_text into a double and writes it to out_value.
|
||||
// The host can use this to convert user input into a parameter value.
|
||||
// Returns true on success.
|
||||
// [main-thread]
|
||||
bool(CLAP_ABI *text_to_value)(const clap_plugin_t *plugin,
|
||||
clap_id param_id,
|
||||
const char *param_value_text,
|
||||
double *out_value);
|
||||
|
||||
// Flushes a set of parameter changes.
|
||||
// This method must not be called concurrently to clap_plugin->process().
|
||||
//
|
||||
// Note: if the plugin is processing, then the process() call will already achieve the
|
||||
// parameter update (bi-directional), so a call to flush isn't required, also be aware
|
||||
// that the plugin may use the sample offset in process(), while this information would be
|
||||
// lost within flush().
|
||||
//
|
||||
// [active ? audio-thread : main-thread]
|
||||
void(CLAP_ABI *flush)(const clap_plugin_t *plugin,
|
||||
const clap_input_events_t *in,
|
||||
const clap_output_events_t *out);
|
||||
} clap_plugin_params_t;
|
||||
|
||||
enum {
|
||||
// The parameter values did change, eg. after loading a preset.
|
||||
// The host will scan all the parameters value.
|
||||
// The host will not record those changes as automation points.
|
||||
// New values takes effect immediately.
|
||||
CLAP_PARAM_RESCAN_VALUES = 1 << 0,
|
||||
|
||||
// The value to text conversion changed, and the text needs to be rendered again.
|
||||
CLAP_PARAM_RESCAN_TEXT = 1 << 1,
|
||||
|
||||
// The parameter info did change, use this flag for:
|
||||
// - name change
|
||||
// - module change
|
||||
// - is_periodic (flag)
|
||||
// - is_hidden (flag)
|
||||
// New info takes effect immediately.
|
||||
CLAP_PARAM_RESCAN_INFO = 1 << 2,
|
||||
|
||||
// Invalidates everything the host knows about parameters.
|
||||
// It can only be used while the plugin is deactivated.
|
||||
// If the plugin is activated use clap_host->restart() and delay any change until the host calls
|
||||
// clap_plugin->deactivate().
|
||||
//
|
||||
// You must use this flag if:
|
||||
// - some parameters were added or removed.
|
||||
// - some parameters had critical changes:
|
||||
// - is_per_note (flag)
|
||||
// - is_per_key (flag)
|
||||
// - is_per_channel (flag)
|
||||
// - is_per_port (flag)
|
||||
// - is_readonly (flag)
|
||||
// - is_bypass (flag)
|
||||
// - is_stepped (flag)
|
||||
// - is_modulatable (flag)
|
||||
// - min_value
|
||||
// - max_value
|
||||
// - cookie
|
||||
CLAP_PARAM_RESCAN_ALL = 1 << 3,
|
||||
};
|
||||
typedef uint32_t clap_param_rescan_flags;
|
||||
|
||||
enum {
|
||||
// Clears all possible references to a parameter
|
||||
CLAP_PARAM_CLEAR_ALL = 1 << 0,
|
||||
|
||||
// Clears all automations to a parameter
|
||||
CLAP_PARAM_CLEAR_AUTOMATIONS = 1 << 1,
|
||||
|
||||
// Clears all modulations to a parameter
|
||||
CLAP_PARAM_CLEAR_MODULATIONS = 1 << 2,
|
||||
};
|
||||
typedef uint32_t clap_param_clear_flags;
|
||||
|
||||
typedef struct clap_host_params {
|
||||
// Rescan the full list of parameters according to the flags.
|
||||
// [main-thread]
|
||||
void(CLAP_ABI *rescan)(const clap_host_t *host, clap_param_rescan_flags flags);
|
||||
|
||||
// Clears references to a parameter.
|
||||
// [main-thread]
|
||||
void(CLAP_ABI *clear)(const clap_host_t *host, clap_id param_id, clap_param_clear_flags flags);
|
||||
|
||||
// Request a parameter flush.
|
||||
//
|
||||
// The host will then schedule a call to either:
|
||||
// - clap_plugin.process()
|
||||
// - clap_plugin_params.flush()
|
||||
//
|
||||
// This function is always safe to use and should not be called from an [audio-thread] as the
|
||||
// plugin would already be within process() or flush().
|
||||
//
|
||||
// [thread-safe,!audio-thread]
|
||||
void(CLAP_ABI *request_flush)(const clap_host_t *host);
|
||||
} clap_host_params_t;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
49
tests/clap/ext/posix-fd-support.h
Normal file
49
tests/clap/ext/posix-fd-support.h
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
#pragma once
|
||||
|
||||
#include "../plugin.h"
|
||||
|
||||
// This extension let your plugin hook itself into the host select/poll/epoll/kqueue reactor.
|
||||
// This is useful to handle asynchronous I/O on the main thread.
|
||||
static CLAP_CONSTEXPR const char CLAP_EXT_POSIX_FD_SUPPORT[] = "clap.posix-fd-support";
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
enum {
|
||||
// IO events flags, they can be used to form a mask which describes:
|
||||
// - which events you are interested in (register_fd/modify_fd)
|
||||
// - which events happened (on_fd)
|
||||
CLAP_POSIX_FD_READ = 1 << 0,
|
||||
CLAP_POSIX_FD_WRITE = 1 << 1,
|
||||
CLAP_POSIX_FD_ERROR = 1 << 2,
|
||||
};
|
||||
typedef uint32_t clap_posix_fd_flags_t;
|
||||
|
||||
typedef struct clap_plugin_posix_fd_support {
|
||||
// This callback is "level-triggered".
|
||||
// It means that a writable fd will continuously produce "on_fd()" events;
|
||||
// don't forget using modify_fd() to remove the write notification once you're
|
||||
// done writing.
|
||||
//
|
||||
// [main-thread]
|
||||
void(CLAP_ABI *on_fd)(const clap_plugin_t *plugin, int fd, clap_posix_fd_flags_t flags);
|
||||
} clap_plugin_posix_fd_support_t;
|
||||
|
||||
typedef struct clap_host_posix_fd_support {
|
||||
// Returns true on success.
|
||||
// [main-thread]
|
||||
bool(CLAP_ABI *register_fd)(const clap_host_t *host, int fd, clap_posix_fd_flags_t flags);
|
||||
|
||||
// Returns true on success.
|
||||
// [main-thread]
|
||||
bool(CLAP_ABI *modify_fd)(const clap_host_t *host, int fd, clap_posix_fd_flags_t flags);
|
||||
|
||||
// Returns true on success.
|
||||
// [main-thread]
|
||||
bool(CLAP_ABI *unregister_fd)(const clap_host_t *host, int fd);
|
||||
} clap_host_posix_fd_support_t;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
53
tests/clap/ext/preset-load.h
Normal file
53
tests/clap/ext/preset-load.h
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
#pragma once
|
||||
|
||||
#include "../plugin.h"
|
||||
|
||||
static CLAP_CONSTEXPR const char CLAP_EXT_PRESET_LOAD[] = "clap.preset-load/2";
|
||||
|
||||
// The latest draft is 100% compatible.
|
||||
// This compat ID may be removed in 2026.
|
||||
static CLAP_CONSTEXPR const char CLAP_EXT_PRESET_LOAD_COMPAT[] = "clap.preset-load.draft/2";
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct clap_plugin_preset_load {
|
||||
// Loads a preset in the plugin native preset file format from a location.
|
||||
// The preset discovery provider defines the location and load_key to be passed to this function.
|
||||
// Returns true on success.
|
||||
// [main-thread]
|
||||
bool(CLAP_ABI *from_location)(const clap_plugin_t *plugin,
|
||||
uint32_t location_kind,
|
||||
const char *location,
|
||||
const char *load_key);
|
||||
} clap_plugin_preset_load_t;
|
||||
|
||||
typedef struct clap_host_preset_load {
|
||||
// Called if clap_plugin_preset_load.load() failed.
|
||||
// os_error: the operating system error, if applicable. If not applicable set it to a non-error
|
||||
// value, eg: 0 on unix and Windows.
|
||||
//
|
||||
// [main-thread]
|
||||
void(CLAP_ABI *on_error)(const clap_host_t *host,
|
||||
uint32_t location_kind,
|
||||
const char *location,
|
||||
const char *load_key,
|
||||
int32_t os_error,
|
||||
const char *msg);
|
||||
|
||||
// Informs the host that the following preset has been loaded.
|
||||
// This contributes to keep in sync the host preset browser and plugin preset browser.
|
||||
// If the preset was loaded from a container file, then the load_key must be set, otherwise it
|
||||
// must be null.
|
||||
//
|
||||
// [main-thread]
|
||||
void(CLAP_ABI *loaded)(const clap_host_t *host,
|
||||
uint32_t location_kind,
|
||||
const char *location,
|
||||
const char *load_key);
|
||||
} clap_host_preset_load_t;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
83
tests/clap/ext/remote-controls.h
Normal file
83
tests/clap/ext/remote-controls.h
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
#pragma once
|
||||
|
||||
#include "../plugin.h"
|
||||
#include "../string-sizes.h"
|
||||
|
||||
// This extension let the plugin provide a structured way of mapping parameters to an hardware
|
||||
// controller.
|
||||
//
|
||||
// This is done by providing a set of remote control pages organized by section.
|
||||
// A page contains up to 8 controls, which references parameters using param_id.
|
||||
//
|
||||
// |`- [section:main]
|
||||
// | `- [name:main] performance controls
|
||||
// |`- [section:osc]
|
||||
// | |`- [name:osc1] osc1 page
|
||||
// | |`- [name:osc2] osc2 page
|
||||
// | |`- [name:osc-sync] osc sync page
|
||||
// | `- [name:osc-noise] osc noise page
|
||||
// |`- [section:filter]
|
||||
// | |`- [name:flt1] filter 1 page
|
||||
// | `- [name:flt2] filter 2 page
|
||||
// |`- [section:env]
|
||||
// | |`- [name:env1] env1 page
|
||||
// | `- [name:env2] env2 page
|
||||
// |`- [section:lfo]
|
||||
// | |`- [name:lfo1] env1 page
|
||||
// | `- [name:lfo2] env2 page
|
||||
// `- etc...
|
||||
//
|
||||
// One possible workflow is to have a set of buttons, which correspond to a section.
|
||||
// Pressing that button once gets you to the first page of the section.
|
||||
// Press it again to cycle through the section's pages.
|
||||
|
||||
static CLAP_CONSTEXPR const char CLAP_EXT_REMOTE_CONTROLS[] = "clap.remote-controls/2";
|
||||
|
||||
// The latest draft is 100% compatible
|
||||
// This compat ID may be removed in 2026.
|
||||
static CLAP_CONSTEXPR const char CLAP_EXT_REMOTE_CONTROLS_COMPAT[] = "clap.remote-controls.draft/2";
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
enum { CLAP_REMOTE_CONTROLS_COUNT = 8 };
|
||||
|
||||
typedef struct clap_remote_controls_page {
|
||||
char section_name[CLAP_NAME_SIZE];
|
||||
clap_id page_id;
|
||||
char page_name[CLAP_NAME_SIZE];
|
||||
clap_id param_ids[CLAP_REMOTE_CONTROLS_COUNT];
|
||||
|
||||
// This is used to separate device pages versus preset pages.
|
||||
// If true, then this page is specific to this preset.
|
||||
bool is_for_preset;
|
||||
} clap_remote_controls_page_t;
|
||||
|
||||
typedef struct clap_plugin_remote_controls {
|
||||
// Returns the number of pages.
|
||||
// [main-thread]
|
||||
uint32_t(CLAP_ABI *count)(const clap_plugin_t *plugin);
|
||||
|
||||
// Get a page by index.
|
||||
// Returns true on success and stores the result into page.
|
||||
// [main-thread]
|
||||
bool(CLAP_ABI *get)(const clap_plugin_t *plugin,
|
||||
uint32_t page_index,
|
||||
clap_remote_controls_page_t *page);
|
||||
} clap_plugin_remote_controls_t;
|
||||
|
||||
typedef struct clap_host_remote_controls {
|
||||
// Informs the host that the remote controls have changed.
|
||||
// [main-thread]
|
||||
void(CLAP_ABI *changed)(const clap_host_t *host);
|
||||
|
||||
// Suggest a page to the host because it corresponds to what the user is currently editing in the
|
||||
// plugin's GUI.
|
||||
// [main-thread]
|
||||
void(CLAP_ABI *suggest_page)(const clap_host_t *host, clap_id page_id);
|
||||
} clap_host_remote_controls_t;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
39
tests/clap/ext/render.h
Normal file
39
tests/clap/ext/render.h
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
#pragma once
|
||||
|
||||
#include "../plugin.h"
|
||||
|
||||
static CLAP_CONSTEXPR const char CLAP_EXT_RENDER[] = "clap.render";
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
enum {
|
||||
// Default setting, for "realtime" processing
|
||||
CLAP_RENDER_REALTIME = 0,
|
||||
|
||||
// For processing without realtime pressure
|
||||
// The plugin may use more expensive algorithms for higher sound quality.
|
||||
CLAP_RENDER_OFFLINE = 1,
|
||||
};
|
||||
typedef int32_t clap_plugin_render_mode;
|
||||
|
||||
// The render extension is used to let the plugin know if it has "realtime"
|
||||
// pressure to process.
|
||||
//
|
||||
// If this information does not influence your rendering code, then don't
|
||||
// implement this extension.
|
||||
typedef struct clap_plugin_render {
|
||||
// Returns true if the plugin has a hard requirement to process in real-time.
|
||||
// This is especially useful for plugin acting as a proxy to an hardware device.
|
||||
// [main-thread]
|
||||
bool(CLAP_ABI *has_hard_realtime_requirement)(const clap_plugin_t *plugin);
|
||||
|
||||
// Returns true if the rendering mode could be applied.
|
||||
// [main-thread]
|
||||
bool(CLAP_ABI *set)(const clap_plugin_t *plugin, clap_plugin_render_mode mode);
|
||||
} clap_plugin_render_t;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
72
tests/clap/ext/state-context.h
Normal file
72
tests/clap/ext/state-context.h
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
#pragma once
|
||||
|
||||
#include "../plugin.h"
|
||||
#include "../stream.h"
|
||||
|
||||
/// @page state-context extension
|
||||
/// @brief extended state handling
|
||||
///
|
||||
/// This extension lets the host save and load the plugin state with different semantics depending
|
||||
/// on the context.
|
||||
///
|
||||
/// Briefly, when loading a preset or duplicating a device, the plugin may want to partially load
|
||||
/// the state and initialize certain things differently, like handling limited resources or fixed
|
||||
/// connections to external hardware resources.
|
||||
///
|
||||
/// Save and Load operations may have a different context.
|
||||
/// All three operations should be equivalent:
|
||||
/// 1. clap_plugin_state_context.load(clap_plugin_state.save(), CLAP_STATE_CONTEXT_FOR_PRESET)
|
||||
/// 2. clap_plugin_state.load(clap_plugin_state_context.save(CLAP_STATE_CONTEXT_FOR_PRESET))
|
||||
/// 3. clap_plugin_state_context.load(
|
||||
/// clap_plugin_state_context.save(CLAP_STATE_CONTEXT_FOR_PRESET),
|
||||
/// CLAP_STATE_CONTEXT_FOR_PRESET)
|
||||
///
|
||||
/// If in doubt, fallback to clap_plugin_state.
|
||||
///
|
||||
/// If the plugin implements CLAP_EXT_STATE_CONTEXT then it is mandatory to also implement
|
||||
/// CLAP_EXT_STATE.
|
||||
///
|
||||
/// It is unspecified which context is equivalent to clap_plugin_state.{save,load}()
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
static CLAP_CONSTEXPR const char CLAP_EXT_STATE_CONTEXT[] = "clap.state-context/2";
|
||||
|
||||
enum clap_plugin_state_context_type {
|
||||
// suitable for storing and loading a state as a preset
|
||||
CLAP_STATE_CONTEXT_FOR_PRESET = 1,
|
||||
|
||||
// suitable for duplicating a plugin instance
|
||||
CLAP_STATE_CONTEXT_FOR_DUPLICATE = 2,
|
||||
|
||||
// suitable for storing and loading a state within a project/song
|
||||
CLAP_STATE_CONTEXT_FOR_PROJECT = 3,
|
||||
};
|
||||
|
||||
typedef struct clap_plugin_state_context {
|
||||
// Saves the plugin state into stream, according to context_type.
|
||||
// Returns true if the state was correctly saved.
|
||||
//
|
||||
// Note that the result may be loaded by both clap_plugin_state.load() and
|
||||
// clap_plugin_state_context.load().
|
||||
// [main-thread]
|
||||
bool(CLAP_ABI *save)(const clap_plugin_t *plugin,
|
||||
const clap_ostream_t *stream,
|
||||
uint32_t context_type);
|
||||
|
||||
// Loads the plugin state from stream, according to context_type.
|
||||
// Returns true if the state was correctly restored.
|
||||
//
|
||||
// Note that the state may have been saved by clap_plugin_state.save() or
|
||||
// clap_plugin_state_context.save() with a different context_type.
|
||||
// [main-thread]
|
||||
bool(CLAP_ABI *load)(const clap_plugin_t *plugin,
|
||||
const clap_istream_t *stream,
|
||||
uint32_t context_type);
|
||||
} clap_plugin_state_context_t;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
45
tests/clap/ext/state.h
Normal file
45
tests/clap/ext/state.h
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
#pragma once
|
||||
|
||||
#include "../plugin.h"
|
||||
#include "../stream.h"
|
||||
|
||||
/// @page State
|
||||
/// @brief state management
|
||||
///
|
||||
/// Plugins can implement this extension to save and restore both parameter
|
||||
/// values and non-parameter state. This is used to persist a plugin's state
|
||||
/// between project reloads, when duplicating and copying plugin instances, and
|
||||
/// for host-side preset management.
|
||||
///
|
||||
/// If you need to know if the save/load operation is meant for duplicating a plugin
|
||||
/// instance, for saving/loading a plugin preset or while saving/loading the project
|
||||
/// then consider implementing CLAP_EXT_STATE_CONTEXT in addition to CLAP_EXT_STATE.
|
||||
|
||||
static CLAP_CONSTEXPR const char CLAP_EXT_STATE[] = "clap.state";
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct clap_plugin_state {
|
||||
// Saves the plugin state into stream.
|
||||
// Returns true if the state was correctly saved.
|
||||
// [main-thread]
|
||||
bool(CLAP_ABI *save)(const clap_plugin_t *plugin, const clap_ostream_t *stream);
|
||||
|
||||
// Loads the plugin state from stream.
|
||||
// Returns true if the state was correctly restored.
|
||||
// [main-thread]
|
||||
bool(CLAP_ABI *load)(const clap_plugin_t *plugin, const clap_istream_t *stream);
|
||||
} clap_plugin_state_t;
|
||||
|
||||
typedef struct clap_host_state {
|
||||
// Tell the host that the plugin state has changed and should be saved again.
|
||||
// If a parameter value changes, then it is implicit that the state is dirty.
|
||||
// [main-thread]
|
||||
void(CLAP_ABI *mark_dirty)(const clap_host_t *host);
|
||||
} clap_host_state_t;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
89
tests/clap/ext/surround.h
Normal file
89
tests/clap/ext/surround.h
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
#pragma once
|
||||
|
||||
#include "../plugin.h"
|
||||
|
||||
// This extension can be used to specify the channel mapping used by the plugin.
|
||||
//
|
||||
// To have consistent surround features across all the plugin instances,
|
||||
// here is the proposed workflow:
|
||||
// 1. the plugin queries the host preferred channel mapping and
|
||||
// adjusts its configuration to match it.
|
||||
// 2. the host checks how the plugin is effectively configured and honors it.
|
||||
//
|
||||
// If the host decides to change the project's surround setup:
|
||||
// 1. deactivate the plugin
|
||||
// 2. host calls clap_plugin_surround->changed()
|
||||
// 3. plugin calls clap_host_surround->get_preferred_channel_map()
|
||||
// 4. plugin eventually calls clap_host_surround->changed()
|
||||
// 5. host calls clap_plugin_surround->get_channel_map() if changed
|
||||
// 6. host activates the plugin and can start processing audio
|
||||
//
|
||||
// If the plugin wants to change its surround setup:
|
||||
// 1. call host->request_restart() if the plugin is active
|
||||
// 2. once deactivated plugin calls clap_host_surround->changed()
|
||||
// 3. host calls clap_plugin_surround->get_channel_map()
|
||||
// 4. host activates the plugin and can start processing audio
|
||||
|
||||
static CLAP_CONSTEXPR const char CLAP_EXT_SURROUND[] = "clap.surround/4";
|
||||
|
||||
// The latest draft is 100% compatible.
|
||||
// This compat ID may be removed in 2026.
|
||||
static CLAP_CONSTEXPR const char CLAP_EXT_SURROUND_COMPAT[] = "clap.surround.draft/4";
|
||||
|
||||
static CLAP_CONSTEXPR const char CLAP_PORT_SURROUND[] = "surround";
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
enum {
|
||||
CLAP_SURROUND_FL = 0, // Front Left
|
||||
CLAP_SURROUND_FR = 1, // Front Right
|
||||
CLAP_SURROUND_FC = 2, // Front Center
|
||||
CLAP_SURROUND_LFE = 3, // Low Frequency
|
||||
CLAP_SURROUND_BL = 4, // Back (Rear) Left
|
||||
CLAP_SURROUND_BR = 5, // Back (Rear) Right
|
||||
CLAP_SURROUND_FLC = 6, // Front Left of Center
|
||||
CLAP_SURROUND_FRC = 7, // Front Right of Center
|
||||
CLAP_SURROUND_BC = 8, // Back (Rear) Center
|
||||
CLAP_SURROUND_SL = 9, // Side Left
|
||||
CLAP_SURROUND_SR = 10, // Side Right
|
||||
CLAP_SURROUND_TC = 11, // Top (Height) Center
|
||||
CLAP_SURROUND_TFL = 12, // Top (Height) Front Left
|
||||
CLAP_SURROUND_TFC = 13, // Top (Height) Front Center
|
||||
CLAP_SURROUND_TFR = 14, // Top (Height) Front Right
|
||||
CLAP_SURROUND_TBL = 15, // Top (Height) Back (Rear) Left
|
||||
CLAP_SURROUND_TBC = 16, // Top (Height) Back (Rear) Center
|
||||
CLAP_SURROUND_TBR = 17, // Top (Height) Back (Rear) Right
|
||||
CLAP_SURROUND_TSL = 18, // Top (Height) Side Left
|
||||
CLAP_SURROUND_TSR = 19, // Top (Height) Side Right
|
||||
};
|
||||
|
||||
typedef struct clap_plugin_surround {
|
||||
// Checks if a given channel mask is supported.
|
||||
// The channel mask is a bitmask, for example:
|
||||
// (1 << CLAP_SURROUND_FL) | (1 << CLAP_SURROUND_FR) | ...
|
||||
// [main-thread]
|
||||
bool(CLAP_ABI *is_channel_mask_supported)(const clap_plugin_t *plugin, uint64_t channel_mask);
|
||||
|
||||
// Stores the surround identifier of each channel into the channel_map array.
|
||||
// Returns the number of elements stored in channel_map.
|
||||
// channel_map_capacity must be greater or equal to the channel count of the given port.
|
||||
// [main-thread]
|
||||
uint32_t(CLAP_ABI *get_channel_map)(const clap_plugin_t *plugin,
|
||||
bool is_input,
|
||||
uint32_t port_index,
|
||||
uint8_t *channel_map,
|
||||
uint32_t channel_map_capacity);
|
||||
} clap_plugin_surround_t;
|
||||
|
||||
typedef struct clap_host_surround {
|
||||
// Informs the host that the channel map has changed.
|
||||
// The channel map can only change when the plugin is de-activated.
|
||||
// [main-thread]
|
||||
void(CLAP_ABI *changed)(const clap_host_t *host);
|
||||
} clap_host_surround_t;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
26
tests/clap/ext/tail.h
Normal file
26
tests/clap/ext/tail.h
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
#pragma once
|
||||
|
||||
#include "../plugin.h"
|
||||
|
||||
static CLAP_CONSTEXPR const char CLAP_EXT_TAIL[] = "clap.tail";
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct clap_plugin_tail {
|
||||
// Returns tail length in samples.
|
||||
// Any value greater or equal to INT32_MAX implies infinite tail.
|
||||
// [main-thread,audio-thread]
|
||||
uint32_t(CLAP_ABI *get)(const clap_plugin_t *plugin);
|
||||
} clap_plugin_tail_t;
|
||||
|
||||
typedef struct clap_host_tail {
|
||||
// Tell the host that the tail has changed.
|
||||
// [audio-thread]
|
||||
void(CLAP_ABI *changed)(const clap_host_t *host);
|
||||
} clap_host_tail_t;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
72
tests/clap/ext/thread-check.h
Normal file
72
tests/clap/ext/thread-check.h
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
#pragma once
|
||||
|
||||
#include "../plugin.h"
|
||||
|
||||
static CLAP_CONSTEXPR const char CLAP_EXT_THREAD_CHECK[] = "clap.thread-check";
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/// @page thread-check
|
||||
///
|
||||
/// CLAP defines two symbolic threads:
|
||||
///
|
||||
/// main-thread:
|
||||
/// This is the thread in which most of the interaction between the plugin and host happens.
|
||||
/// This will be the same OS thread throughout the lifetime of the plug-in.
|
||||
/// On macOS and Windows, this must be the thread on which gui and timer events are received
|
||||
/// (i.e., the main thread of the program).
|
||||
/// It isn't a realtime thread, yet this thread needs to respond fast enough to allow responsive
|
||||
/// user interaction, so it is strongly recommended plugins run long,and expensive or blocking
|
||||
/// tasks such as preset indexing or asset loading in dedicated background threads started by the
|
||||
/// plugin.
|
||||
///
|
||||
/// audio-thread:
|
||||
/// This thread can be used for realtime audio processing. Its execution should be as
|
||||
/// deterministic as possible to meet the audio interface's deadline (can be <1ms). There are a
|
||||
/// known set of operations that should be avoided: malloc() and free(), contended locks and
|
||||
/// mutexes, I/O, waiting, and so forth.
|
||||
///
|
||||
/// The audio-thread is symbolic, there isn't one OS thread that remains the
|
||||
/// audio-thread for the plugin lifetime. A host is may opt to have a
|
||||
/// thread pool and the plugin.process() call may be scheduled on different OS threads over time.
|
||||
/// However, the host must guarantee that single plugin instance will not be two audio-threads
|
||||
/// at the same time.
|
||||
///
|
||||
/// Functions marked with [audio-thread] **ARE NOT CONCURRENT**. The host may mark any OS thread,
|
||||
/// including the main-thread as the audio-thread, as long as it can guarantee that only one OS
|
||||
/// thread is the audio-thread at a time in a plugin instance. The audio-thread can be seen as a
|
||||
/// concurrency guard for all functions marked with [audio-thread].
|
||||
///
|
||||
/// The real-time constraint on the [audio-thread] interacts closely with the render extension.
|
||||
/// If a plugin doesn't implement render, then that plugin must have all [audio-thread] functions
|
||||
/// meet the real time standard. If the plugin does implement render, and returns true when
|
||||
/// render mode is set to real-time or if the plugin advertises a hard realtime requirement, it
|
||||
/// must implement realtime constraints. Hosts also provide functions marked [audio-thread].
|
||||
/// These can be safely called by a plugin in the audio thread. Therefore hosts must either (1)
|
||||
/// implement those functions meeting the real-time constraints or (2) not process plugins which
|
||||
/// advertise a hard realtime constraint or don't implement the render extension. Hosts which
|
||||
/// provide [audio-thread] functions outside these conditions may experience inconsistent or
|
||||
/// inaccurate rendering.
|
||||
///
|
||||
/// Clap also tags some functions as [thread-safe]. Functions tagged as [thread-safe] can be called
|
||||
/// from any thread unless explicitly counter-indicated (for instance [thread-safe, !audio-thread])
|
||||
/// and may be called concurrently.
|
||||
|
||||
// This interface is useful to do runtime checks and make
|
||||
// sure that the functions are called on the correct threads.
|
||||
// It is highly recommended that hosts implement this extension.
|
||||
typedef struct clap_host_thread_check {
|
||||
// Returns true if "this" thread is the main thread.
|
||||
// [thread-safe]
|
||||
bool(CLAP_ABI *is_main_thread)(const clap_host_t *host);
|
||||
|
||||
// Returns true if "this" thread is one of the audio threads.
|
||||
// [thread-safe]
|
||||
bool(CLAP_ABI *is_audio_thread)(const clap_host_t *host);
|
||||
} clap_host_thread_check_t;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
66
tests/clap/ext/thread-pool.h
Normal file
66
tests/clap/ext/thread-pool.h
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
#pragma once
|
||||
|
||||
#include "../plugin.h"
|
||||
|
||||
/// @page
|
||||
///
|
||||
/// This extension lets the plugin use the host's thread pool.
|
||||
///
|
||||
/// The plugin must provide @ref clap_plugin_thread_pool, and the host may provide @ref
|
||||
/// clap_host_thread_pool. If it doesn't, the plugin should process its data by its own means. In
|
||||
/// the worst case, a single threaded for-loop.
|
||||
///
|
||||
/// Simple example with N voices to process
|
||||
///
|
||||
/// @code
|
||||
/// void myplug_thread_pool_exec(const clap_plugin *plugin, uint32_t voice_index)
|
||||
/// {
|
||||
/// compute_voice(plugin, voice_index);
|
||||
/// }
|
||||
///
|
||||
/// void myplug_process(const clap_plugin *plugin, const clap_process *process)
|
||||
/// {
|
||||
/// ...
|
||||
/// bool didComputeVoices = false;
|
||||
/// if (host_thread_pool && host_thread_pool.exec)
|
||||
/// didComputeVoices = host_thread_pool.request_exec(host, plugin, N);
|
||||
///
|
||||
/// if (!didComputeVoices)
|
||||
/// for (uint32_t i = 0; i < N; ++i)
|
||||
/// myplug_thread_pool_exec(plugin, i);
|
||||
/// ...
|
||||
/// }
|
||||
/// @endcode
|
||||
///
|
||||
/// Be aware that using a thread pool may break hard real-time rules due to the thread
|
||||
/// synchronization involved.
|
||||
///
|
||||
/// If the host knows that it is running under hard real-time pressure it may decide to not
|
||||
/// provide this interface.
|
||||
|
||||
static CLAP_CONSTEXPR const char CLAP_EXT_THREAD_POOL[] = "clap.thread-pool";
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct clap_plugin_thread_pool {
|
||||
// Called by the thread pool
|
||||
void(CLAP_ABI *exec)(const clap_plugin_t *plugin, uint32_t task_index);
|
||||
} clap_plugin_thread_pool_t;
|
||||
|
||||
typedef struct clap_host_thread_pool {
|
||||
// Schedule num_tasks jobs in the host thread pool.
|
||||
// It can't be called concurrently or from the thread pool.
|
||||
// Will block until all the tasks are processed.
|
||||
// This must be used exclusively for realtime processing within the process call.
|
||||
// Returns true if the host did execute all the tasks, false if it rejected the request.
|
||||
// The host should check that the plugin is within the process call, and if not, reject the exec
|
||||
// request.
|
||||
// [audio-thread]
|
||||
bool(CLAP_ABI *request_exec)(const clap_host_t *host, uint32_t num_tasks);
|
||||
} clap_host_thread_pool_t;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
31
tests/clap/ext/timer-support.h
Normal file
31
tests/clap/ext/timer-support.h
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
#pragma once
|
||||
|
||||
#include "../plugin.h"
|
||||
|
||||
static CLAP_CONSTEXPR const char CLAP_EXT_TIMER_SUPPORT[] = "clap.timer-support";
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct clap_plugin_timer_support {
|
||||
// [main-thread]
|
||||
void(CLAP_ABI *on_timer)(const clap_plugin_t *plugin, clap_id timer_id);
|
||||
} clap_plugin_timer_support_t;
|
||||
|
||||
typedef struct clap_host_timer_support {
|
||||
// Registers a periodic timer.
|
||||
// The host may adjust the period if it is under a certain threshold.
|
||||
// 30 Hz should be allowed.
|
||||
// Returns true on success.
|
||||
// [main-thread]
|
||||
bool(CLAP_ABI *register_timer)(const clap_host_t *host, uint32_t period_ms, clap_id *timer_id);
|
||||
|
||||
// Returns true on success.
|
||||
// [main-thread]
|
||||
bool(CLAP_ABI *unregister_timer)(const clap_host_t *host, clap_id timer_id);
|
||||
} clap_host_timer_support_t;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
66
tests/clap/ext/track-info.h
Normal file
66
tests/clap/ext/track-info.h
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
#pragma once
|
||||
|
||||
#include "../plugin.h"
|
||||
#include "../color.h"
|
||||
#include "../string-sizes.h"
|
||||
|
||||
// This extension let the plugin query info about the track it's in.
|
||||
// It is useful when the plugin is created, to initialize some parameters (mix, dry, wet)
|
||||
// and pick a suitable configuration regarding audio port type and channel count.
|
||||
|
||||
static CLAP_CONSTEXPR const char CLAP_EXT_TRACK_INFO[] = "clap.track-info/1";
|
||||
|
||||
// The latest draft is 100% compatible.
|
||||
// This compat ID may be removed in 2026.
|
||||
static CLAP_CONSTEXPR const char CLAP_EXT_TRACK_INFO_COMPAT[] = "clap.track-info.draft/1";
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
enum {
|
||||
CLAP_TRACK_INFO_HAS_TRACK_NAME = (1 << 0),
|
||||
CLAP_TRACK_INFO_HAS_TRACK_COLOR = (1 << 1),
|
||||
CLAP_TRACK_INFO_HAS_AUDIO_CHANNEL = (1 << 2),
|
||||
|
||||
// This plugin is on a return track, initialize with wet 100%
|
||||
CLAP_TRACK_INFO_IS_FOR_RETURN_TRACK = (1 << 3),
|
||||
|
||||
// This plugin is on a bus track, initialize with appropriate settings for bus processing
|
||||
CLAP_TRACK_INFO_IS_FOR_BUS = (1 << 4),
|
||||
|
||||
// This plugin is on the master, initialize with appropriate settings for channel processing
|
||||
CLAP_TRACK_INFO_IS_FOR_MASTER = (1 << 5),
|
||||
};
|
||||
|
||||
typedef struct clap_track_info {
|
||||
uint64_t flags; // see the flags above
|
||||
|
||||
// track name, available if flags contain CLAP_TRACK_INFO_HAS_TRACK_NAME
|
||||
char name[CLAP_NAME_SIZE];
|
||||
|
||||
// track color, available if flags contain CLAP_TRACK_INFO_HAS_TRACK_COLOR
|
||||
clap_color_t color;
|
||||
|
||||
// available if flags contain CLAP_TRACK_INFO_HAS_AUDIO_CHANNEL
|
||||
// see audio-ports.h, struct clap_audio_port_info to learn how to use channel count and port type
|
||||
int32_t audio_channel_count;
|
||||
const char *audio_port_type;
|
||||
} clap_track_info_t;
|
||||
|
||||
typedef struct clap_plugin_track_info {
|
||||
// Called when the info changes.
|
||||
// [main-thread]
|
||||
void(CLAP_ABI *changed)(const clap_plugin_t *plugin);
|
||||
} clap_plugin_track_info_t;
|
||||
|
||||
typedef struct clap_host_track_info {
|
||||
// Get info about the track the plugin belongs to.
|
||||
// Returns true on success and stores the result into info.
|
||||
// [main-thread]
|
||||
bool(CLAP_ABI *get)(const clap_host_t *host, clap_track_info_t *info);
|
||||
} clap_host_track_info_t;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
56
tests/clap/ext/voice-info.h
Normal file
56
tests/clap/ext/voice-info.h
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
#pragma once
|
||||
|
||||
#include "../plugin.h"
|
||||
|
||||
// This extension indicates the number of voices the synthesizer has.
|
||||
// It is useful for the host when performing polyphonic modulations,
|
||||
// because the host needs its own voice management and should try to follow
|
||||
// what the plugin is doing:
|
||||
// - make the host's voice pool coherent with what the plugin has
|
||||
// - turn the host's voice management to mono when the plugin is mono
|
||||
|
||||
static CLAP_CONSTEXPR const char CLAP_EXT_VOICE_INFO[] = "clap.voice-info";
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
enum {
|
||||
// Allows the host to send overlapping NOTE_ON events.
|
||||
// The plugin will then rely upon the note_id to distinguish between them.
|
||||
CLAP_VOICE_INFO_SUPPORTS_OVERLAPPING_NOTES = 1 << 0,
|
||||
};
|
||||
|
||||
typedef struct clap_voice_info {
|
||||
// voice_count is the current number of voices that the patch can use
|
||||
// voice_capacity is the number of voices allocated voices
|
||||
// voice_count should not be confused with the number of active voices.
|
||||
//
|
||||
// 1 <= voice_count <= voice_capacity
|
||||
//
|
||||
// For example, a synth can have a capacity of 8 voices, but be configured
|
||||
// to only use 4 voices: {count: 4, capacity: 8}.
|
||||
//
|
||||
// If the voice_count is 1, then the synth is working in mono and the host
|
||||
// can decide to only use global modulation mapping.
|
||||
uint32_t voice_count;
|
||||
uint32_t voice_capacity;
|
||||
|
||||
uint64_t flags;
|
||||
} clap_voice_info_t;
|
||||
|
||||
typedef struct clap_plugin_voice_info {
|
||||
// gets the voice info, returns true on success
|
||||
// [main-thread && active]
|
||||
bool(CLAP_ABI *get)(const clap_plugin_t *plugin, clap_voice_info_t *info);
|
||||
} clap_plugin_voice_info_t;
|
||||
|
||||
typedef struct clap_host_voice_info {
|
||||
// informs the host that the voice info has changed
|
||||
// [main-thread]
|
||||
void(CLAP_ABI *changed)(const clap_host_t *host);
|
||||
} clap_host_voice_info_t;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
47
tests/clap/factory/draft/plugin-invalidation.h
Normal file
47
tests/clap/factory/draft/plugin-invalidation.h
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
#pragma once
|
||||
|
||||
#include "../../private/std.h"
|
||||
#include "../../private/macros.h"
|
||||
|
||||
// Use it to retrieve const clap_plugin_invalidation_factory_t* from
|
||||
// clap_plugin_entry.get_factory()
|
||||
static const CLAP_CONSTEXPR char CLAP_PLUGIN_INVALIDATION_FACTORY_ID[] =
|
||||
"clap.plugin-invalidation-factory/1";
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct clap_plugin_invalidation_source {
|
||||
// Directory containing the file(s) to scan, must be absolute
|
||||
const char *directory;
|
||||
|
||||
// globing pattern, in the form *.dll
|
||||
const char *filename_glob;
|
||||
|
||||
// should the directory be scanned recursively?
|
||||
bool recursive_scan;
|
||||
} clap_plugin_invalidation_source_t;
|
||||
|
||||
// Used to figure out when a plugin needs to be scanned again.
|
||||
// Imagine a situation with a single entry point: my-plugin.clap which then scans itself
|
||||
// a set of "sub-plugins". New plugin may be available even if my-plugin.clap file doesn't change.
|
||||
// This interfaces solves this issue and gives a way to the host to monitor additional files.
|
||||
typedef struct clap_plugin_invalidation_factory {
|
||||
// Get the number of invalidation source.
|
||||
uint32_t(CLAP_ABI *count)(const struct clap_plugin_invalidation_factory *factory);
|
||||
|
||||
// Get the invalidation source by its index.
|
||||
// [thread-safe]
|
||||
const clap_plugin_invalidation_source_t *(CLAP_ABI *get)(
|
||||
const struct clap_plugin_invalidation_factory *factory, uint32_t index);
|
||||
|
||||
// In case the host detected a invalidation event, it can call refresh() to let the
|
||||
// plugin_entry update the set of plugins available.
|
||||
// If the function returned false, then the plugin needs to be reloaded.
|
||||
bool(CLAP_ABI *refresh)(const struct clap_plugin_invalidation_factory *factory);
|
||||
} clap_plugin_invalidation_factory_t;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
99
tests/clap/factory/draft/plugin-state-converter.h
Normal file
99
tests/clap/factory/draft/plugin-state-converter.h
Normal file
|
|
@ -0,0 +1,99 @@
|
|||
#pragma once
|
||||
|
||||
#include "../../id.h"
|
||||
#include "../../universal-plugin-id.h"
|
||||
#include "../../stream.h"
|
||||
#include "../../version.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct clap_plugin_state_converter_descriptor {
|
||||
clap_version_t clap_version;
|
||||
|
||||
clap_universal_plugin_id_t src_plugin_id;
|
||||
clap_universal_plugin_id_t dst_plugin_id;
|
||||
|
||||
const char *id; // eg: "com.u-he.diva-converter", mandatory
|
||||
const char *name; // eg: "Diva Converter", mandatory
|
||||
const char *vendor; // eg: "u-he"
|
||||
const char *version; // eg: 1.1.5
|
||||
const char *description; // eg: "Official state converter for u-he Diva."
|
||||
} clap_plugin_state_converter_descriptor_t;
|
||||
|
||||
// This interface provides a mechanism for the host to convert a plugin state and its automation
|
||||
// points to a new plugin.
|
||||
//
|
||||
// This is useful to convert from one plugin ABI to another one.
|
||||
// This is also useful to offer an upgrade path: from EQ version 1 to EQ version 2.
|
||||
// This can also be used to convert the state of a plugin that isn't maintained anymore into
|
||||
// another plugin that would be similar.
|
||||
typedef struct clap_plugin_state_converter {
|
||||
const clap_plugin_state_converter_descriptor_t *desc;
|
||||
|
||||
void *converter_data;
|
||||
|
||||
// Destroy the converter.
|
||||
void (*destroy)(struct clap_plugin_state_converter *converter);
|
||||
|
||||
// Converts the input state to a state usable by the destination plugin.
|
||||
//
|
||||
// error_buffer is a place holder of error_buffer_size bytes for storing a null-terminated
|
||||
// error message in case of failure, which can be displayed to the user.
|
||||
//
|
||||
// Returns true on success.
|
||||
// [thread-safe]
|
||||
bool (*convert_state)(struct clap_plugin_state_converter *converter,
|
||||
const clap_istream_t *src,
|
||||
const clap_ostream_t *dst,
|
||||
char *error_buffer,
|
||||
size_t error_buffer_size);
|
||||
|
||||
// Converts a normalized value.
|
||||
// Returns true on success.
|
||||
// [thread-safe]
|
||||
bool (*convert_normalized_value)(struct clap_plugin_state_converter *converter,
|
||||
clap_id src_param_id,
|
||||
double src_normalized_value,
|
||||
clap_id *dst_param_id,
|
||||
double *dst_normalized_value);
|
||||
|
||||
// Converts a plain value.
|
||||
// Returns true on success.
|
||||
// [thread-safe]
|
||||
bool (*convert_plain_value)(struct clap_plugin_state_converter *converter,
|
||||
clap_id src_param_id,
|
||||
double src_plain_value,
|
||||
clap_id *dst_param_id,
|
||||
double *dst_plain_value);
|
||||
} clap_plugin_state_converter_t;
|
||||
|
||||
// Factory identifier
|
||||
static CLAP_CONSTEXPR const char CLAP_PLUGIN_STATE_CONVERTER_FACTORY_ID[] =
|
||||
"clap.plugin-state-converter-factory/1";
|
||||
|
||||
// List all the plugin state converters available in the current DSO.
|
||||
typedef struct clap_plugin_state_converter_factory {
|
||||
// Get the number of converters.
|
||||
// [thread-safe]
|
||||
uint32_t (*count)(const struct clap_plugin_state_converter_factory *factory);
|
||||
|
||||
// Retrieves a plugin state converter descriptor by its index.
|
||||
// Returns null in case of error.
|
||||
// The descriptor must not be freed.
|
||||
// [thread-safe]
|
||||
const clap_plugin_state_converter_descriptor_t *(*get_descriptor)(
|
||||
const struct clap_plugin_state_converter_factory *factory, uint32_t index);
|
||||
|
||||
// Create a plugin state converter by its converter_id.
|
||||
// The returned pointer must be freed by calling converter->destroy(converter);
|
||||
// Returns null in case of error.
|
||||
// [thread-safe]
|
||||
clap_plugin_state_converter_t *(*create)(
|
||||
const struct clap_plugin_state_converter_factory *factory, const char *converter_id);
|
||||
} clap_plugin_state_converter_factory_t;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
42
tests/clap/factory/plugin-factory.h
Normal file
42
tests/clap/factory/plugin-factory.h
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
#pragma once
|
||||
|
||||
#include "../plugin.h"
|
||||
|
||||
// Use it to retrieve const clap_plugin_factory_t* from
|
||||
// clap_plugin_entry.get_factory()
|
||||
static const CLAP_CONSTEXPR char CLAP_PLUGIN_FACTORY_ID[] = "clap.plugin-factory";
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// Every method must be thread-safe.
|
||||
// It is very important to be able to scan the plugin as quickly as possible.
|
||||
//
|
||||
// The host may use clap_plugin_invalidation_factory to detect filesystem changes
|
||||
// which may change the factory's content.
|
||||
typedef struct clap_plugin_factory {
|
||||
// Get the number of plugins available.
|
||||
// [thread-safe]
|
||||
uint32_t(CLAP_ABI *get_plugin_count)(const struct clap_plugin_factory *factory);
|
||||
|
||||
// Retrieves a plugin descriptor by its index.
|
||||
// Returns null in case of error.
|
||||
// The descriptor must not be freed.
|
||||
// [thread-safe]
|
||||
const clap_plugin_descriptor_t *(CLAP_ABI *get_plugin_descriptor)(
|
||||
const struct clap_plugin_factory *factory, uint32_t index);
|
||||
|
||||
// Create a clap_plugin by its plugin_id.
|
||||
// The returned pointer must be freed by calling plugin->destroy(plugin);
|
||||
// The plugin is not allowed to use the host callbacks in the create method.
|
||||
// Returns null in case of error.
|
||||
// [thread-safe]
|
||||
const clap_plugin_t *(CLAP_ABI *create_plugin)(const struct clap_plugin_factory *factory,
|
||||
const clap_host_t *host,
|
||||
const char *plugin_id);
|
||||
} clap_plugin_factory_t;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
313
tests/clap/factory/preset-discovery.h
Normal file
313
tests/clap/factory/preset-discovery.h
Normal file
|
|
@ -0,0 +1,313 @@
|
|||
/*
|
||||
Preset Discovery API.
|
||||
|
||||
Preset Discovery enables a plug-in host to identify where presets are found, what
|
||||
extensions they have, which plug-ins they apply to, and other metadata associated with the
|
||||
presets so that they can be indexed and searched for quickly within the plug-in host's browser.
|
||||
|
||||
This has a number of advantages for the user:
|
||||
- it allows them to browse for presets from one central location in a consistent way
|
||||
- the user can browse for presets without having to commit to a particular plug-in first
|
||||
|
||||
The API works as follow to index presets and presets metadata:
|
||||
1. clap_plugin_entry.get_factory(CLAP_PRESET_DISCOVERY_FACTORY_ID)
|
||||
2. clap_preset_discovery_factory_t.create(...)
|
||||
3. clap_preset_discovery_provider.init() (only necessary the first time, declarations
|
||||
can be cached)
|
||||
`-> clap_preset_discovery_indexer.declare_filetype()
|
||||
`-> clap_preset_discovery_indexer.declare_location()
|
||||
`-> clap_preset_discovery_indexer.declare_soundpack() (optional)
|
||||
`-> clap_preset_discovery_indexer.set_invalidation_watch_file() (optional)
|
||||
4. crawl the given locations and monitor file system changes
|
||||
`-> clap_preset_discovery_indexer.get_metadata() for each presets files
|
||||
|
||||
Then to load a preset, use ext/draft/preset-load.h.
|
||||
TODO: create a dedicated repo for other plugin abi preset-load extension.
|
||||
|
||||
The design of this API deliberately does not define a fixed set tags or categories. It is the
|
||||
plug-in host's job to try to intelligently map the raw list of features that are found for a
|
||||
preset and to process this list to generate something that makes sense for the host's tagging and
|
||||
categorization system. The reason for this is to reduce the work for a plug-in developer to add
|
||||
Preset Discovery support for their existing preset file format and not have to be concerned with
|
||||
all the different hosts and how they want to receive the metadata.
|
||||
|
||||
VERY IMPORTANT:
|
||||
- the whole indexing process has to be **fast**
|
||||
- clap_preset_provider->get_metadata() has to be fast and avoid unnecessary operations
|
||||
- the whole indexing process must not be interactive
|
||||
- don't show dialogs, windows, ...
|
||||
- don't ask for user input
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../private/std.h"
|
||||
#include "../private/macros.h"
|
||||
#include "../timestamp.h"
|
||||
#include "../version.h"
|
||||
#include "../universal-plugin-id.h"
|
||||
|
||||
// Use it to retrieve const clap_preset_discovery_factory_t* from
|
||||
// clap_plugin_entry.get_factory()
|
||||
static const CLAP_CONSTEXPR char CLAP_PRESET_DISCOVERY_FACTORY_ID[] =
|
||||
"clap.preset-discovery-factory/2";
|
||||
|
||||
// The latest draft is 100% compatible.
|
||||
// This compat ID may be removed in 2026.
|
||||
static const CLAP_CONSTEXPR char CLAP_PRESET_DISCOVERY_FACTORY_ID_COMPAT[] =
|
||||
"clap.preset-discovery-factory/draft-2";
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
enum clap_preset_discovery_location_kind {
|
||||
// The preset are located in a file on the OS filesystem.
|
||||
// The location is then a path which works with the OS file system functions (open, stat, ...)
|
||||
// So both '/' and '\' shall work on Windows as a separator.
|
||||
CLAP_PRESET_DISCOVERY_LOCATION_FILE = 0,
|
||||
|
||||
// The preset is bundled within the plugin DSO itself.
|
||||
// The location must then be null, as the preset are within the plugin itself and then the plugin
|
||||
// will act as a preset container.
|
||||
CLAP_PRESET_DISCOVERY_LOCATION_PLUGIN = 1,
|
||||
};
|
||||
|
||||
enum clap_preset_discovery_flags {
|
||||
// This is for factory or sound-pack presets.
|
||||
CLAP_PRESET_DISCOVERY_IS_FACTORY_CONTENT = 1 << 0,
|
||||
|
||||
// This is for user presets.
|
||||
CLAP_PRESET_DISCOVERY_IS_USER_CONTENT = 1 << 1,
|
||||
|
||||
// This location is meant for demo presets, those are preset which may trigger
|
||||
// some limitation in the plugin because they require additional features which the user
|
||||
// needs to purchase or the content itself needs to be bought and is only available in
|
||||
// demo mode.
|
||||
CLAP_PRESET_DISCOVERY_IS_DEMO_CONTENT = 1 << 2,
|
||||
|
||||
// This preset is a user's favorite
|
||||
CLAP_PRESET_DISCOVERY_IS_FAVORITE = 1 << 3,
|
||||
};
|
||||
|
||||
// Receiver that receives the metadata for a single preset file.
|
||||
// The host would define the various callbacks in this interface and the preset parser function
|
||||
// would then call them.
|
||||
//
|
||||
// This interface isn't thread-safe.
|
||||
typedef struct clap_preset_discovery_metadata_receiver {
|
||||
void *receiver_data; // reserved pointer for the metadata receiver
|
||||
|
||||
// If there is an error reading metadata from a file this should be called with an error
|
||||
// message.
|
||||
// os_error: the operating system error, if applicable. If not applicable set it to a non-error
|
||||
// value, eg: 0 on unix and Windows.
|
||||
void(CLAP_ABI *on_error)(const struct clap_preset_discovery_metadata_receiver *receiver,
|
||||
int32_t os_error,
|
||||
const char *error_message);
|
||||
|
||||
// This must be called for every preset in the file and before any preset metadata is
|
||||
// sent with the calls below.
|
||||
//
|
||||
// If the preset file is a preset container then name and load_key are mandatory, otherwise
|
||||
// they are optional.
|
||||
//
|
||||
// The load_key is a machine friendly string used to load the preset inside the container via a
|
||||
// the preset-load plug-in extension. The load_key can also just be the subpath if that's what
|
||||
// the plugin wants but it could also be some other unique id like a database primary key or a
|
||||
// binary offset. It's use is entirely up to the plug-in.
|
||||
//
|
||||
// If the function returns false, then the provider must stop calling back into the receiver.
|
||||
bool(CLAP_ABI *begin_preset)(const struct clap_preset_discovery_metadata_receiver *receiver,
|
||||
const char *name,
|
||||
const char *load_key);
|
||||
|
||||
// Adds a plug-in id that this preset can be used with.
|
||||
void(CLAP_ABI *add_plugin_id)(const struct clap_preset_discovery_metadata_receiver *receiver,
|
||||
const clap_universal_plugin_id_t *plugin_id);
|
||||
|
||||
// Sets the sound pack to which the preset belongs to.
|
||||
void(CLAP_ABI *set_soundpack_id)(const struct clap_preset_discovery_metadata_receiver *receiver,
|
||||
const char *soundpack_id);
|
||||
|
||||
// Sets the flags, see clap_preset_discovery_flags.
|
||||
// If unset, they are then inherited from the location.
|
||||
void(CLAP_ABI *set_flags)(const struct clap_preset_discovery_metadata_receiver *receiver,
|
||||
uint32_t flags);
|
||||
|
||||
// Adds a creator name for the preset.
|
||||
void(CLAP_ABI *add_creator)(const struct clap_preset_discovery_metadata_receiver *receiver,
|
||||
const char *creator);
|
||||
|
||||
// Sets a description of the preset.
|
||||
void(CLAP_ABI *set_description)(const struct clap_preset_discovery_metadata_receiver *receiver,
|
||||
const char *description);
|
||||
|
||||
// Sets the creation time and last modification time of the preset.
|
||||
// If one of the times isn't known, set it to CLAP_TIMESTAMP_UNKNOWN.
|
||||
// If this function is not called, then the indexer may look at the file's creation and
|
||||
// modification time.
|
||||
void(CLAP_ABI *set_timestamps)(const struct clap_preset_discovery_metadata_receiver *receiver,
|
||||
clap_timestamp creation_time,
|
||||
clap_timestamp modification_time);
|
||||
|
||||
// Adds a feature to the preset.
|
||||
//
|
||||
// The feature string is arbitrary, it is the indexer's job to understand it and remap it to its
|
||||
// internal categorization and tagging system.
|
||||
//
|
||||
// However, the strings from plugin-features.h should be understood by the indexer and one of the
|
||||
// plugin category could be provided to determine if the preset will result into an audio-effect,
|
||||
// instrument, ...
|
||||
//
|
||||
// Examples:
|
||||
// kick, drum, tom, snare, clap, cymbal, bass, lead, metalic, hardsync, crossmod, acid,
|
||||
// distorted, drone, pad, dirty, etc...
|
||||
void(CLAP_ABI *add_feature)(const struct clap_preset_discovery_metadata_receiver *receiver,
|
||||
const char *feature);
|
||||
|
||||
// Adds extra information to the metadata.
|
||||
void(CLAP_ABI *add_extra_info)(const struct clap_preset_discovery_metadata_receiver *receiver,
|
||||
const char *key,
|
||||
const char *value);
|
||||
} clap_preset_discovery_metadata_receiver_t;
|
||||
|
||||
typedef struct clap_preset_discovery_filetype {
|
||||
const char *name;
|
||||
const char *description; // optional
|
||||
|
||||
// `.' isn't included in the string.
|
||||
// If empty or NULL then every file should be matched.
|
||||
const char *file_extension;
|
||||
} clap_preset_discovery_filetype_t;
|
||||
|
||||
// Defines a place in which to search for presets
|
||||
typedef struct clap_preset_discovery_location {
|
||||
uint32_t flags; // see enum clap_preset_discovery_flags
|
||||
const char *name; // name of this location
|
||||
uint32_t kind; // See clap_preset_discovery_location_kind
|
||||
|
||||
// Actual location in which to crawl presets.
|
||||
// For FILE kind, the location can be either a path to a directory or a file.
|
||||
// For PLUGIN kind, the location must be null.
|
||||
const char *location;
|
||||
} clap_preset_discovery_location_t;
|
||||
|
||||
// Describes an installed sound pack.
|
||||
typedef struct clap_preset_discovery_soundpack {
|
||||
uint32_t flags; // see enum clap_preset_discovery_flags
|
||||
const char *id; // sound pack identifier
|
||||
const char *name; // name of this sound pack
|
||||
const char *description; // optional, reasonably short description of the sound pack
|
||||
const char *homepage_url; // optional, url to the pack's homepage
|
||||
const char *vendor; // optional, sound pack's vendor
|
||||
const char *image_path; // optional, an image on disk
|
||||
clap_timestamp release_timestamp; // release date, CLAP_TIMESTAMP_UNKNOWN if unavailable
|
||||
} clap_preset_discovery_soundpack_t;
|
||||
|
||||
// Describes a preset provider
|
||||
typedef struct clap_preset_discovery_provider_descriptor {
|
||||
clap_version_t clap_version; // initialized to CLAP_VERSION
|
||||
const char *id; // see plugin.h for advice on how to choose a good identifier
|
||||
const char *name; // eg: "Diva's preset provider"
|
||||
const char *vendor; // optional, eg: u-he
|
||||
} clap_preset_discovery_provider_descriptor_t;
|
||||
|
||||
// This interface isn't thread-safe.
|
||||
typedef struct clap_preset_discovery_provider {
|
||||
const clap_preset_discovery_provider_descriptor_t *desc;
|
||||
|
||||
void *provider_data; // reserved pointer for the provider
|
||||
|
||||
// Initialize the preset provider.
|
||||
// It should declare all its locations, filetypes and sound packs.
|
||||
// Returns false if initialization failed.
|
||||
bool(CLAP_ABI *init)(const struct clap_preset_discovery_provider *provider);
|
||||
|
||||
// Destroys the preset provider
|
||||
void(CLAP_ABI *destroy)(const struct clap_preset_discovery_provider *provider);
|
||||
|
||||
// reads metadata from the given file and passes them to the metadata receiver
|
||||
// Returns true on success.
|
||||
bool(CLAP_ABI *get_metadata)(const struct clap_preset_discovery_provider *provider,
|
||||
uint32_t location_kind,
|
||||
const char *location,
|
||||
const clap_preset_discovery_metadata_receiver_t *metadata_receiver);
|
||||
|
||||
// Query an extension.
|
||||
// The returned pointer is owned by the provider.
|
||||
// It is forbidden to call it before provider->init().
|
||||
// You can call it within provider->init() call, and after.
|
||||
const void *(CLAP_ABI *get_extension)(const struct clap_preset_discovery_provider *provider,
|
||||
const char *extension_id);
|
||||
} clap_preset_discovery_provider_t;
|
||||
|
||||
// This interface isn't thread-safe
|
||||
typedef struct clap_preset_discovery_indexer {
|
||||
clap_version_t clap_version; // initialized to CLAP_VERSION
|
||||
const char *name; // eg: "Bitwig Studio"
|
||||
const char *vendor; // optional, eg: "Bitwig GmbH"
|
||||
const char *url; // optional, eg: "https://bitwig.com"
|
||||
const char *version; // optional, eg: "4.3", see plugin.h for advice on how to format the version
|
||||
|
||||
void *indexer_data; // reserved pointer for the indexer
|
||||
|
||||
// Declares a preset filetype.
|
||||
// Don't callback into the provider during this call.
|
||||
// Returns false if the filetype is invalid.
|
||||
bool(CLAP_ABI *declare_filetype)(const struct clap_preset_discovery_indexer *indexer,
|
||||
const clap_preset_discovery_filetype_t *filetype);
|
||||
|
||||
// Declares a preset location.
|
||||
// Don't callback into the provider during this call.
|
||||
// Returns false if the location is invalid.
|
||||
bool(CLAP_ABI *declare_location)(const struct clap_preset_discovery_indexer *indexer,
|
||||
const clap_preset_discovery_location_t *location);
|
||||
|
||||
// Declares a sound pack.
|
||||
// Don't callback into the provider during this call.
|
||||
// Returns false if the sound pack is invalid.
|
||||
bool(CLAP_ABI *declare_soundpack)(const struct clap_preset_discovery_indexer *indexer,
|
||||
const clap_preset_discovery_soundpack_t *soundpack);
|
||||
|
||||
// Query an extension.
|
||||
// The returned pointer is owned by the indexer.
|
||||
// It is forbidden to call it before provider->init().
|
||||
// You can call it within provider->init() call, and after.
|
||||
const void *(CLAP_ABI *get_extension)(const struct clap_preset_discovery_indexer *indexer,
|
||||
const char *extension_id);
|
||||
} clap_preset_discovery_indexer_t;
|
||||
|
||||
// Every methods in this factory must be thread-safe.
|
||||
// It is encouraged to perform preset indexing in background threads, maybe even in background
|
||||
// process.
|
||||
//
|
||||
// The host may use clap_plugin_invalidation_factory to detect filesystem changes
|
||||
// which may change the factory's content.
|
||||
typedef struct clap_preset_discovery_factory {
|
||||
// Get the number of preset providers available.
|
||||
// [thread-safe]
|
||||
uint32_t(CLAP_ABI *count)(const struct clap_preset_discovery_factory *factory);
|
||||
|
||||
// Retrieves a preset provider descriptor by its index.
|
||||
// Returns null in case of error.
|
||||
// The descriptor must not be freed.
|
||||
// [thread-safe]
|
||||
const clap_preset_discovery_provider_descriptor_t *(CLAP_ABI *get_descriptor)(
|
||||
const struct clap_preset_discovery_factory *factory, uint32_t index);
|
||||
|
||||
// Create a preset provider by its id.
|
||||
// The returned pointer must be freed by calling preset_provider->destroy(preset_provider);
|
||||
// The preset provider is not allowed to use the indexer callbacks in the create method.
|
||||
// It is forbidden to call back into the indexer before the indexer calls provider->init().
|
||||
// Returns null in case of error.
|
||||
// [thread-safe]
|
||||
const clap_preset_discovery_provider_t *(CLAP_ABI *create)(
|
||||
const struct clap_preset_discovery_factory *factory,
|
||||
const clap_preset_discovery_indexer_t *indexer,
|
||||
const char *provider_id);
|
||||
} clap_preset_discovery_factory_t;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
16
tests/clap/fixedpoint.h
Normal file
16
tests/clap/fixedpoint.h
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
#pragma once
|
||||
|
||||
#include "private/std.h"
|
||||
#include "private/macros.h"
|
||||
|
||||
/// We use fixed point representation of beat time and seconds time
|
||||
/// Usage:
|
||||
/// double x = ...; // in beats
|
||||
/// clap_beattime y = round(CLAP_BEATTIME_FACTOR * x);
|
||||
|
||||
// This will never change
|
||||
static const CLAP_CONSTEXPR int64_t CLAP_BEATTIME_FACTOR = 1LL << 31;
|
||||
static const CLAP_CONSTEXPR int64_t CLAP_SECTIME_FACTOR = 1LL << 31;
|
||||
|
||||
typedef int64_t clap_beattime;
|
||||
typedef int64_t clap_sectime;
|
||||
51
tests/clap/host.h
Normal file
51
tests/clap/host.h
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
#pragma once
|
||||
|
||||
#include "version.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct clap_host {
|
||||
clap_version_t clap_version; // initialized to CLAP_VERSION
|
||||
|
||||
void *host_data; // reserved pointer for the host
|
||||
|
||||
// name and version are mandatory.
|
||||
const char *name; // eg: "Bitwig Studio"
|
||||
const char *vendor; // eg: "Bitwig GmbH"
|
||||
const char *url; // eg: "https://bitwig.com"
|
||||
const char *version; // eg: "4.3", see plugin.h for advice on how to format the version
|
||||
|
||||
// Query an extension.
|
||||
// The returned pointer is owned by the host.
|
||||
// It is forbidden to call it before plugin->init().
|
||||
// You can call it within plugin->init() call, and after.
|
||||
// [thread-safe]
|
||||
const void *(CLAP_ABI *get_extension)(const struct clap_host *host, const char *extension_id);
|
||||
|
||||
// Request the host to deactivate and then reactivate the plugin.
|
||||
// The operation may be delayed by the host.
|
||||
// [thread-safe]
|
||||
void(CLAP_ABI *request_restart)(const struct clap_host *host);
|
||||
|
||||
// Request the host to activate and start processing the plugin.
|
||||
// This is useful if you have external IO and need to wake up the plugin from "sleep".
|
||||
// [thread-safe]
|
||||
void(CLAP_ABI *request_process)(const struct clap_host *host);
|
||||
|
||||
// Request the host to schedule a call to plugin->on_main_thread(plugin) on the main thread.
|
||||
// This callback should be called as soon as practicable, usually in the host application's next
|
||||
// available main thread time slice. Typically callbacks occur within 33ms / 30hz.
|
||||
// Despite this guidance, plugins should not make assumptions about the exactness of timing for
|
||||
// a main thread callback, but hosts should endeavour to be prompt. For example, in high load
|
||||
// situations the environment may starve the gui/main thread in favor of audio processing,
|
||||
// leading to substantially longer latencies for the callback than the indicative times given
|
||||
// here.
|
||||
// [thread-safe]
|
||||
void(CLAP_ABI *request_callback)(const struct clap_host *host);
|
||||
} clap_host_t;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
8
tests/clap/id.h
Normal file
8
tests/clap/id.h
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
#pragma once
|
||||
|
||||
#include "private/std.h"
|
||||
#include "private/macros.h"
|
||||
|
||||
typedef uint32_t clap_id;
|
||||
|
||||
static const CLAP_CONSTEXPR clap_id CLAP_INVALID_ID = UINT32_MAX;
|
||||
79
tests/clap/plugin-features.h
Normal file
79
tests/clap/plugin-features.h
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
#pragma once
|
||||
|
||||
// This file provides a set of standard plugin features meant to be used
|
||||
// within clap_plugin_descriptor.features.
|
||||
//
|
||||
// For practical reasons we'll avoid spaces and use `-` instead to facilitate
|
||||
// scripts that generate the feature array.
|
||||
//
|
||||
// Non-standard features should be formatted as follow: "$namespace:$feature"
|
||||
|
||||
/////////////////////
|
||||
// Plugin category //
|
||||
/////////////////////
|
||||
|
||||
// Add this feature if your plugin can process note events and then produce audio
|
||||
#define CLAP_PLUGIN_FEATURE_INSTRUMENT "instrument"
|
||||
|
||||
// Add this feature if your plugin is an audio effect
|
||||
#define CLAP_PLUGIN_FEATURE_AUDIO_EFFECT "audio-effect"
|
||||
|
||||
// Add this feature if your plugin is a note effect or a note generator/sequencer
|
||||
#define CLAP_PLUGIN_FEATURE_NOTE_EFFECT "note-effect"
|
||||
|
||||
// Add this feature if your plugin converts audio to notes
|
||||
#define CLAP_PLUGIN_FEATURE_NOTE_DETECTOR "note-detector"
|
||||
|
||||
// Add this feature if your plugin is an analyzer
|
||||
#define CLAP_PLUGIN_FEATURE_ANALYZER "analyzer"
|
||||
|
||||
/////////////////////////
|
||||
// Plugin sub-category //
|
||||
/////////////////////////
|
||||
|
||||
#define CLAP_PLUGIN_FEATURE_SYNTHESIZER "synthesizer"
|
||||
#define CLAP_PLUGIN_FEATURE_SAMPLER "sampler"
|
||||
#define CLAP_PLUGIN_FEATURE_DRUM "drum" // For single drum
|
||||
#define CLAP_PLUGIN_FEATURE_DRUM_MACHINE "drum-machine"
|
||||
|
||||
#define CLAP_PLUGIN_FEATURE_FILTER "filter"
|
||||
#define CLAP_PLUGIN_FEATURE_PHASER "phaser"
|
||||
#define CLAP_PLUGIN_FEATURE_EQUALIZER "equalizer"
|
||||
#define CLAP_PLUGIN_FEATURE_DEESSER "de-esser"
|
||||
#define CLAP_PLUGIN_FEATURE_PHASE_VOCODER "phase-vocoder"
|
||||
#define CLAP_PLUGIN_FEATURE_GRANULAR "granular"
|
||||
#define CLAP_PLUGIN_FEATURE_FREQUENCY_SHIFTER "frequency-shifter"
|
||||
#define CLAP_PLUGIN_FEATURE_PITCH_SHIFTER "pitch-shifter"
|
||||
|
||||
#define CLAP_PLUGIN_FEATURE_DISTORTION "distortion"
|
||||
#define CLAP_PLUGIN_FEATURE_TRANSIENT_SHAPER "transient-shaper"
|
||||
#define CLAP_PLUGIN_FEATURE_COMPRESSOR "compressor"
|
||||
#define CLAP_PLUGIN_FEATURE_EXPANDER "expander"
|
||||
#define CLAP_PLUGIN_FEATURE_GATE "gate"
|
||||
#define CLAP_PLUGIN_FEATURE_LIMITER "limiter"
|
||||
|
||||
#define CLAP_PLUGIN_FEATURE_FLANGER "flanger"
|
||||
#define CLAP_PLUGIN_FEATURE_CHORUS "chorus"
|
||||
#define CLAP_PLUGIN_FEATURE_DELAY "delay"
|
||||
#define CLAP_PLUGIN_FEATURE_REVERB "reverb"
|
||||
|
||||
#define CLAP_PLUGIN_FEATURE_TREMOLO "tremolo"
|
||||
#define CLAP_PLUGIN_FEATURE_GLITCH "glitch"
|
||||
|
||||
#define CLAP_PLUGIN_FEATURE_UTILITY "utility"
|
||||
#define CLAP_PLUGIN_FEATURE_PITCH_CORRECTION "pitch-correction"
|
||||
#define CLAP_PLUGIN_FEATURE_RESTORATION "restoration" // repair the sound
|
||||
|
||||
#define CLAP_PLUGIN_FEATURE_MULTI_EFFECTS "multi-effects"
|
||||
|
||||
#define CLAP_PLUGIN_FEATURE_MIXING "mixing"
|
||||
#define CLAP_PLUGIN_FEATURE_MASTERING "mastering"
|
||||
|
||||
////////////////////////
|
||||
// Audio Capabilities //
|
||||
////////////////////////
|
||||
|
||||
#define CLAP_PLUGIN_FEATURE_MONO "mono"
|
||||
#define CLAP_PLUGIN_FEATURE_STEREO "stereo"
|
||||
#define CLAP_PLUGIN_FEATURE_SURROUND "surround"
|
||||
#define CLAP_PLUGIN_FEATURE_AMBISONIC "ambisonic"
|
||||
114
tests/clap/plugin.h
Normal file
114
tests/clap/plugin.h
Normal file
|
|
@ -0,0 +1,114 @@
|
|||
#pragma once
|
||||
|
||||
#include "private/macros.h"
|
||||
#include "host.h"
|
||||
#include "process.h"
|
||||
#include "plugin-features.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct clap_plugin_descriptor {
|
||||
clap_version_t clap_version; // initialized to CLAP_VERSION
|
||||
|
||||
// Mandatory fields must be set and must not be blank.
|
||||
// Otherwise the fields can be null or blank, though it is safer to make them blank.
|
||||
//
|
||||
// Some indications regarding id and version
|
||||
// - id is an arbitrary string which should be unique to your plugin,
|
||||
// we encourage you to use a reverse URI eg: "com.u-he.diva"
|
||||
// - version is an arbitrary string which describes a plugin,
|
||||
// it is useful for the host to understand and be able to compare two different
|
||||
// version strings, so here is a regex like expression which is likely to be
|
||||
// understood by most hosts: MAJOR(.MINOR(.REVISION)?)?( (Alpha|Beta) XREV)?
|
||||
const char *id; // eg: "com.u-he.diva", mandatory
|
||||
const char *name; // eg: "Diva", mandatory
|
||||
const char *vendor; // eg: "u-he"
|
||||
const char *url; // eg: "https://u-he.com/products/diva/"
|
||||
const char *manual_url; // eg: "https://dl.u-he.com/manuals/plugins/diva/Diva-user-guide.pdf"
|
||||
const char *support_url; // eg: "https://u-he.com/support/"
|
||||
const char *version; // eg: "1.4.4"
|
||||
const char *description; // eg: "The spirit of analogue"
|
||||
|
||||
// Arbitrary list of keywords.
|
||||
// They can be matched by the host indexer and used to classify the plugin.
|
||||
// The array of pointers must be null terminated.
|
||||
// For some standard features see plugin-features.h
|
||||
const char *const *features;
|
||||
} clap_plugin_descriptor_t;
|
||||
|
||||
typedef struct clap_plugin {
|
||||
const clap_plugin_descriptor_t *desc;
|
||||
|
||||
void *plugin_data; // reserved pointer for the plugin
|
||||
|
||||
// Must be called after creating the plugin.
|
||||
// If init returns false, the host must destroy the plugin instance.
|
||||
// If init returns true, then the plugin is initialized and in the deactivated state.
|
||||
// Unlike in `plugin-factory::create_plugin`, in init you have complete access to the host
|
||||
// and host extensions, so clap related setup activities should be done here rather than in
|
||||
// create_plugin.
|
||||
// [main-thread]
|
||||
bool(CLAP_ABI *init)(const struct clap_plugin *plugin);
|
||||
|
||||
// Free the plugin and its resources.
|
||||
// It is required to deactivate the plugin prior to this call.
|
||||
// [main-thread & !active]
|
||||
void(CLAP_ABI *destroy)(const struct clap_plugin *plugin);
|
||||
|
||||
// Activate and deactivate the plugin.
|
||||
// In this call the plugin may allocate memory and prepare everything needed for the process
|
||||
// call. The process's sample rate will be constant and process's frame count will included in
|
||||
// the [min, max] range, which is bounded by [1, INT32_MAX].
|
||||
// In this call the plugin may call host-provided methods marked [being-activated].
|
||||
// Once activated the latency and port configuration must remain constant, until deactivation.
|
||||
// Returns true on success.
|
||||
// [main-thread & !active]
|
||||
bool(CLAP_ABI *activate)(const struct clap_plugin *plugin,
|
||||
double sample_rate,
|
||||
uint32_t min_frames_count,
|
||||
uint32_t max_frames_count);
|
||||
// [main-thread & active]
|
||||
void(CLAP_ABI *deactivate)(const struct clap_plugin *plugin);
|
||||
|
||||
// Call start processing before processing.
|
||||
// Returns true on success.
|
||||
// [audio-thread & active & !processing]
|
||||
bool(CLAP_ABI *start_processing)(const struct clap_plugin *plugin);
|
||||
|
||||
// Call stop processing before sending the plugin to sleep.
|
||||
// [audio-thread & active & processing]
|
||||
void(CLAP_ABI *stop_processing)(const struct clap_plugin *plugin);
|
||||
|
||||
// - Clears all buffers, performs a full reset of the processing state (filters, oscillators,
|
||||
// envelopes, lfo, ...) and kills all voices.
|
||||
// - The parameter's value remain unchanged.
|
||||
// - clap_process.steady_time may jump backward.
|
||||
//
|
||||
// [audio-thread & active]
|
||||
void(CLAP_ABI *reset)(const struct clap_plugin *plugin);
|
||||
|
||||
// process audio, events, ...
|
||||
// All the pointers coming from clap_process_t and its nested attributes,
|
||||
// are valid until process() returns.
|
||||
// [audio-thread & active & processing]
|
||||
clap_process_status(CLAP_ABI *process)(const struct clap_plugin *plugin,
|
||||
const clap_process_t *process);
|
||||
|
||||
// Query an extension.
|
||||
// The returned pointer is owned by the plugin.
|
||||
// It is forbidden to call it before plugin->init().
|
||||
// You can call it within plugin->init() call, and after.
|
||||
// [thread-safe]
|
||||
const void *(CLAP_ABI *get_extension)(const struct clap_plugin *plugin, const char *id);
|
||||
|
||||
// Called by the host on the main thread in response to a previous call to:
|
||||
// host->request_callback(host);
|
||||
// [main-thread]
|
||||
void(CLAP_ABI *on_main_thread)(const struct clap_plugin *plugin);
|
||||
} clap_plugin_t;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
50
tests/clap/private/macros.h
Normal file
50
tests/clap/private/macros.h
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
#pragma once
|
||||
|
||||
// Define CLAP_EXPORT
|
||||
#if !defined(CLAP_EXPORT)
|
||||
# if defined _WIN32 || defined __CYGWIN__
|
||||
# ifdef __GNUC__
|
||||
# define CLAP_EXPORT __attribute__((dllexport))
|
||||
# else
|
||||
# define CLAP_EXPORT __declspec(dllexport)
|
||||
# endif
|
||||
# else
|
||||
# if __GNUC__ >= 4 || defined(__clang__)
|
||||
# define CLAP_EXPORT __attribute__((visibility("default")))
|
||||
# else
|
||||
# define CLAP_EXPORT
|
||||
# endif
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#if !defined(CLAP_ABI)
|
||||
# if defined _WIN32 || defined __CYGWIN__
|
||||
# define CLAP_ABI __cdecl
|
||||
# else
|
||||
# define CLAP_ABI
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#if defined(_MSVC_LANG)
|
||||
# define CLAP_CPLUSPLUS _MSVC_LANG
|
||||
#elif defined(__cplusplus)
|
||||
# define CLAP_CPLUSPLUS __cplusplus
|
||||
#endif
|
||||
|
||||
#if defined(CLAP_CPLUSPLUS) && CLAP_CPLUSPLUS >= 201103L
|
||||
# define CLAP_HAS_CXX11
|
||||
# define CLAP_CONSTEXPR constexpr
|
||||
#else
|
||||
# define CLAP_CONSTEXPR
|
||||
#endif
|
||||
|
||||
#if defined(CLAP_CPLUSPLUS) && CLAP_CPLUSPLUS >= 201703L
|
||||
# define CLAP_HAS_CXX17
|
||||
# define CLAP_NODISCARD [[nodiscard]]
|
||||
#else
|
||||
# define CLAP_NODISCARD
|
||||
#endif
|
||||
|
||||
#if defined(CLAP_CPLUSPLUS) && CLAP_CPLUSPLUS >= 202002L
|
||||
# define CLAP_HAS_CXX20
|
||||
#endif
|
||||
16
tests/clap/private/std.h
Normal file
16
tests/clap/private/std.h
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
#pragma once
|
||||
|
||||
#include "macros.h"
|
||||
|
||||
#ifdef CLAP_HAS_CXX11
|
||||
# include <cstdint>
|
||||
#else
|
||||
# include <stdint.h>
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
# include <cstddef>
|
||||
#else
|
||||
# include <stddef.h>
|
||||
# include <stdbool.h>
|
||||
#endif
|
||||
66
tests/clap/process.h
Normal file
66
tests/clap/process.h
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
#pragma once
|
||||
|
||||
#include "events.h"
|
||||
#include "audio-buffer.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
enum {
|
||||
// Processing failed. The output buffer must be discarded.
|
||||
CLAP_PROCESS_ERROR = 0,
|
||||
|
||||
// Processing succeeded, keep processing.
|
||||
CLAP_PROCESS_CONTINUE = 1,
|
||||
|
||||
// Processing succeeded, keep processing if the output is not quiet.
|
||||
CLAP_PROCESS_CONTINUE_IF_NOT_QUIET = 2,
|
||||
|
||||
// Rely upon the plugin's tail to determine if the plugin should continue to process.
|
||||
// see clap_plugin_tail
|
||||
CLAP_PROCESS_TAIL = 3,
|
||||
|
||||
// Processing succeeded, but no more processing is required,
|
||||
// until the next event or variation in audio input.
|
||||
CLAP_PROCESS_SLEEP = 4,
|
||||
};
|
||||
typedef int32_t clap_process_status;
|
||||
|
||||
typedef struct clap_process {
|
||||
// A steady sample time counter.
|
||||
// This field can be used to calculate the sleep duration between two process calls.
|
||||
// This value may be specific to this plugin instance and have no relation to what
|
||||
// other plugin instances may receive.
|
||||
//
|
||||
// Set to -1 if not available, otherwise the value must be greater or equal to 0,
|
||||
// and must be increased by at least `frames_count` for the next call to process.
|
||||
int64_t steady_time;
|
||||
|
||||
// Number of frames to process
|
||||
uint32_t frames_count;
|
||||
|
||||
// time info at sample 0
|
||||
// If null, then this is a free running host, no transport events will be provided
|
||||
const clap_event_transport_t *transport;
|
||||
|
||||
// Audio buffers, they must have the same count as specified
|
||||
// by clap_plugin_audio_ports->count().
|
||||
// The index maps to clap_plugin_audio_ports->get().
|
||||
// Input buffer and its contents are read-only.
|
||||
const clap_audio_buffer_t *audio_inputs;
|
||||
clap_audio_buffer_t *audio_outputs;
|
||||
uint32_t audio_inputs_count;
|
||||
uint32_t audio_outputs_count;
|
||||
|
||||
// The input event list can't be modified.
|
||||
// Input read-only event list. The host will deliver these sorted in sample order.
|
||||
const clap_input_events_t *in_events;
|
||||
|
||||
// Output event list. The plugin must insert events in sample sorted order when inserting events
|
||||
const clap_output_events_t *out_events;
|
||||
} clap_process_t;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
38
tests/clap/stream.h
Normal file
38
tests/clap/stream.h
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
#pragma once
|
||||
|
||||
#include "private/std.h"
|
||||
#include "private/macros.h"
|
||||
|
||||
/// @page Streams
|
||||
///
|
||||
/// ## Notes on using streams
|
||||
///
|
||||
/// When working with `clap_istream` and `clap_ostream` objects to load and save
|
||||
/// state, it is important to keep in mind that the host may limit the number of
|
||||
/// bytes that can be read or written at a time. The return values for the
|
||||
/// stream read and write functions indicate how many bytes were actually read
|
||||
/// or written. You need to use a loop to ensure that you read or write the
|
||||
/// entirety of your state. Don't forget to also consider the negative return
|
||||
/// values for the end of file and IO error codes.
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct clap_istream {
|
||||
void *ctx; // reserved pointer for the stream
|
||||
|
||||
// returns the number of bytes read; 0 indicates end of file and -1 a read error
|
||||
int64_t(CLAP_ABI *read)(const struct clap_istream *stream, void *buffer, uint64_t size);
|
||||
} clap_istream_t;
|
||||
|
||||
typedef struct clap_ostream {
|
||||
void *ctx; // reserved pointer for the stream
|
||||
|
||||
// returns the number of bytes written; -1 on write error
|
||||
int64_t(CLAP_ABI *write)(const struct clap_ostream *stream, const void *buffer, uint64_t size);
|
||||
} clap_ostream_t;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
21
tests/clap/string-sizes.h
Normal file
21
tests/clap/string-sizes.h
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
enum {
|
||||
// String capacity for names that can be displayed to the user.
|
||||
CLAP_NAME_SIZE = 256,
|
||||
|
||||
// String capacity for describing a path, like a parameter in a module hierarchy or path within a
|
||||
// set of nested track groups.
|
||||
//
|
||||
// This is not suited for describing a file path on the disk, as NTFS allows up to 32K long
|
||||
// paths.
|
||||
CLAP_PATH_SIZE = 1024,
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
11
tests/clap/timestamp.h
Normal file
11
tests/clap/timestamp.h
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
#pragma once
|
||||
|
||||
#include "private/std.h"
|
||||
#include "private/macros.h"
|
||||
|
||||
// This type defines a timestamp: the number of seconds since UNIX EPOCH.
|
||||
// See C's time_t time(time_t *).
|
||||
typedef uint64_t clap_timestamp;
|
||||
|
||||
// Value for unknown timestamp.
|
||||
static const CLAP_CONSTEXPR clap_timestamp CLAP_TIMESTAMP_UNKNOWN = 0;
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue