Initial commit

This commit is contained in:
Patrick 2025-10-03 20:16:11 +02:00
commit 56f7cd1875
105 changed files with 22109 additions and 0 deletions

2
.gitignore vendored Normal file
View file

@ -0,0 +1,2 @@
build/
.cache/

16
.vscode/launch.json vendored Normal file
View 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
View 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
View 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

View 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
View 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

View 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
View 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

View 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

View 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

View 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

View 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

View 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
View 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
View 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
View 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
View 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

View 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

View 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

File diff suppressed because it is too large Load diff

5079
include/stb/stb_truetype.h Normal file

File diff suppressed because it is too large Load diff

64
meson.build Normal file
View 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

Binary file not shown.

125
src/gui/gui.c Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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
View 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

View 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
View 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
View 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
View 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

View 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

View 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

View 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
View 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

View 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

View 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

View 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
View 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

View 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
View 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
View 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
View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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
View 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
View 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
View 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;

View 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
View 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

View 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
View 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
View 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
View 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
View 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
View 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