2026-10-26 23:40:28 +01:00
# include "../../src/gui/main_window.hpp"
2026-10-26 23:55:35 +01:00
# include "../../src/gui/graph_widget.hpp"
2026-10-27 00:10:45 +01:00
# include "../../src/gui/sequencer_widget.hpp"
2026-10-27 00:53:41 +01:00
# include "../../src/gui/piano_roll.hpp"
2026-10-27 01:06:57 +01:00
# include "../../src/gui/mixer_widget.hpp"
2026-10-27 16:24:21 +01:00
# include "../../src/gui/plugin_dialog.hpp"
2026-10-28 10:37:01 +01:00
# include "../../src/gui/preferences_dialog.hpp"
2026-10-28 17:16:01 +01:00
# include "../../src/gui/command_palette.hpp"
# include <FL/Fl_Value_Input.H>
2026-10-28 10:37:01 +01:00
# include "../../src/gui/plugin_library.hpp"
2026-10-27 16:24:21 +01:00
# include <FL/Fl_Hold_Browser.H>
2026-10-27 16:37:59 +01:00
# include <FL/Fl_Choice.H>
2026-10-28 10:08:13 +01:00
# include <FL/Fl_Value_Slider.H>
2026-10-28 11:23:25 +01:00
# include <FL/Fl_Dial.H>
# include <FL/Fl_Check_Button.H>
# include <FL/Fl_Scroll.H>
2026-10-27 00:10:45 +01:00
# include <unistd.h>
2026-10-26 23:40:28 +01:00
# include <FL/Fl.H>
# include <FL/Fl_Box.H>
# include <FL/Fl_Button.H>
# include <FL/Fl_Group.H>
# include <FL/Fl_Menu_Bar.H>
# include <FL/Fl_Tabs.H>
# include <chrono>
# include <cstdio>
# include <cstring>
2026-10-26 23:55:35 +01:00
# include <cmath>
2026-10-26 23:40:28 +01:00
# include <initializer_list>
2026-10-27 18:04:39 +01:00
# include <utility>
2026-10-27 04:00:27 +01:00
# include <cstdlib>
2026-10-26 23:40:28 +01:00
/* Fake only the public C backend boundary. Real FLTK widgets, callbacks and
* timers run under Xvfb ; no server / device is required or touched . */
2026-10-27 00:53:41 +01:00
struct xtc_backend { int running = 0 ; xtc_plan * plan = nullptr ; xtc_audio_plan * audio = nullptr ; xtc_midi_plan * midi = nullptr ; xtc_recorder * recorder = nullptr ; } ;
2026-10-26 23:40:28 +01:00
namespace {
xtc_backend handle ;
2026-10-27 03:25:04 +01:00
xtc_result seek_result = XTC_OK ;
2026-10-26 23:40:28 +01:00
xtc_result open_result = XTC_OK , start_result = XTC_OK , stop_result = XTC_OK , close_result = XTC_OK ;
2026-10-27 16:49:30 +01:00
xtc_result status_result = XTC_OK , submit_result = XTC_OK , reserve_result = XTC_OK ;
2026-10-26 23:55:35 +01:00
unsigned int submissions = 0U ;
2026-10-26 23:40:28 +01:00
xtc_backend_status engine_status = { } ;
bool partial_open = false ;
unsigned int opens = 0 , starts = 0 , stops = 0 , closes = 0 , collects = 0 , alive = 0 ;
int failures = 0 ;
# define CHECK(c) do { if (!(c)) { std::fprintf(stderr,"FAIL %s:%d: %s\n",__FILE__,__LINE__,#c); ++failures; } } while (0)
void pump ( )
{
const auto end = std : : chrono : : steady_clock : : now ( ) + std : : chrono : : milliseconds ( 180 ) ;
while ( std : : chrono : : steady_clock : : now ( ) < end ) Fl : : wait ( 0.02 ) ;
}
Fl_Widget * find ( Fl_Group * group , const char * label )
{
for ( int i = 0 ; i < group - > children ( ) ; + + i ) {
Fl_Widget * child = group - > child ( i ) ;
if ( child - > label ( ) ! = nullptr & & std : : strcmp ( child - > label ( ) , label ) = = 0 ) return child ;
auto * nested = dynamic_cast < Fl_Group * > ( child ) ;
if ( nested ! = nullptr ) if ( auto * match = find ( nested , label ) ) return match ;
}
return nullptr ;
}
bool contains ( Fl_Group * group , const char * text )
{
for ( int i = 0 ; i < group - > children ( ) ; + + i ) {
Fl_Widget * child = group - > child ( i ) ;
if ( child - > label ( ) ! = nullptr & & std : : strstr ( child - > label ( ) , text ) ! = nullptr ) return true ;
if ( auto * nested = dynamic_cast < Fl_Group * > ( child ) ) if ( contains ( nested , text ) ) return true ;
}
return false ;
}
2026-10-27 00:10:45 +01:00
SequencerWidget * timeline ( Fl_Group * group )
{
for ( int i = 0 ; i < group - > children ( ) ; + + i ) {
if ( auto * s = dynamic_cast < SequencerWidget * > ( group - > child ( i ) ) ) return s ;
if ( auto * g = dynamic_cast < Fl_Group * > ( group - > child ( i ) ) ) if ( auto * s = timeline ( g ) ) return s ;
}
return nullptr ;
}
2026-10-27 16:00:29 +01:00
GraphWidget * graph_canvas ( Fl_Group * group )
{
for ( int i = 0 ; i < group - > children ( ) ; + + i ) {
if ( auto * canvas = dynamic_cast < GraphWidget * > ( group - > child ( i ) ) ) return canvas ;
if ( auto * nested = dynamic_cast < Fl_Group * > ( group - > child ( i ) ) )
if ( auto * canvas = graph_canvas ( nested ) ) return canvas ;
}
return nullptr ;
}
2026-10-27 00:53:41 +01:00
void gesture ( Fl_Widget * s , int left , int top , int dx , int dy )
2026-10-27 00:10:45 +01:00
{
int old_x = Fl : : e_x , old_y = Fl : : e_y , old_key = Fl : : e_keysym ;
Fl : : e_x = s - > x ( ) + left ; Fl : : e_y = s - > y ( ) + top ; Fl : : e_keysym = FL_Button + FL_LEFT_MOUSE ;
CHECK ( s - > handle ( FL_PUSH ) = = 1 ) ;
Fl : : e_x + = dx ; Fl : : e_y + = dy ;
CHECK ( s - > handle ( FL_DRAG ) = = 1 ) ;
CHECK ( s - > handle ( FL_RELEASE ) = = 1 ) ;
Fl : : e_x = old_x ; Fl : : e_y = old_y ; Fl : : e_keysym = old_key ;
}
2026-10-26 23:40:28 +01:00
void press ( Fl_Widget * widget )
{
CHECK ( widget ! = nullptr ) ;
if ( widget ! = nullptr ) widget - > do_callback ( ) ;
}
void reset ( )
{
CHECK ( alive = = 0U ) ;
open_result = start_result = stop_result = close_result = status_result = XTC_OK ;
2026-10-27 16:49:30 +01:00
submit_result = seek_result = reserve_result = XTC_OK ;
2026-10-26 23:40:28 +01:00
engine_status = { } ;
engine_status . sample_rate = 48000U ;
engine_status . buffer_frames = 1024U ;
partial_open = false ;
}
}
extern " C " {
2026-10-27 16:49:30 +01:00
xtc_result __real_xtc_history_reserve ( xtc_history * history ) ;
xtc_result __wrap_xtc_history_reserve ( xtc_history * history ) {
return reserve_result = = XTC_OK ? __real_xtc_history_reserve ( history ) : reserve_result ;
}
2026-10-26 23:40:28 +01:00
xtc_result xtc_backend_open ( const xtc_backend_config * config , xtc_backend * * out )
{
2026-10-27 01:06:57 +01:00
CHECK ( config - > kind = = XTC_BACKEND_JACK & & config - > channel_count = = 2U ) ;
2026-10-26 23:40:28 +01:00
+ + opens ;
* out = open_result = = XTC_OK | | partial_open ? & handle : nullptr ;
2026-10-26 23:55:35 +01:00
if ( * out ! = nullptr ) { + + alive ; handle . running = 0 ; CHECK ( handle . plan = = nullptr ) ; engine_status . active_revision = engine_status . submitted_revision = 0U ; }
2026-10-26 23:40:28 +01:00
return open_result ;
}
xtc_result xtc_backend_close ( xtc_backend * backend )
{
if ( backend = = nullptr ) return XTC_OK ;
+ + closes ;
2026-10-27 00:53:41 +01:00
if ( close_result = = XTC_OK ) { CHECK ( alive = = 1U ) ; - - alive ; handle . running = 0 ; xtc_plan_destroy ( handle . plan ) ; handle . plan = nullptr ; xtc_audio_plan_destroy ( handle . audio ) ; handle . audio = nullptr ; xtc_midi_plan_destroy ( handle . midi ) ; handle . midi = nullptr ; }
2026-10-26 23:40:28 +01:00
return close_result ;
}
xtc_result xtc_backend_start ( xtc_backend * backend )
2026-10-27 00:31:03 +01:00
{ CHECK ( backend = = & handle ) ; + + starts ; if ( start_result = = XTC_OK ) { handle . running = 1 ; engine_status . active_revision = engine_status . submitted_revision ; } return start_result ; }
2026-10-28 10:37:01 +01:00
xtc_result xtc_backend_set_loop ( xtc_backend * , uint64_t start , uint64_t end , int enabled )
{ return enabled & & ( end < = start | | end - start < 64 ) ? XTC_ERR_INVALID_ARGUMENT : XTC_OK ; }
2026-10-27 03:37:19 +01:00
xtc_result xtc_backend_set_transition_frames ( xtc_backend * , size_t frames )
{ CHECK ( frames = = 240 ) ; return XTC_OK ; }
xtc_result xtc_backend_request_stop ( xtc_backend * backend )
{ return xtc_backend_stop ( backend ) ; }
2026-10-26 23:40:28 +01:00
xtc_result xtc_backend_stop ( xtc_backend * backend )
{ CHECK ( backend = = & handle ) ; + + stops ; if ( stop_result = = XTC_OK ) handle . running = 0 ; return stop_result ; }
xtc_result xtc_backend_get_status ( const xtc_backend * backend , xtc_backend_status * status )
{
CHECK ( backend = = & handle & & alive = = 1U ) ;
* status = engine_status ;
status - > running = handle . running & & ! status - > server_lost ;
return status_result ;
}
2026-10-27 00:10:45 +01:00
xtc_result xtc_backend_seek ( xtc_backend * , uint64_t sample )
2026-10-27 03:25:04 +01:00
{ if ( handle . running ) return XTC_ERR_INVALID_STATE ; if ( seek_result ! = XTC_OK ) return seek_result ; engine_status . sample_position = sample ; return XTC_OK ; }
2026-10-26 23:55:35 +01:00
xtc_result xtc_backend_submit ( xtc_backend * backend , const xtc_graph * graph )
{
CHECK ( backend = = & handle & & graph ! = nullptr ) ;
+ + submissions ;
if ( submit_result ! = XTC_OK ) return submit_result ;
xtc_plan * plan = nullptr ;
xtc_result result = xtc_graph_compile ( graph , 48000.0 , 32U , & plan ) ;
if ( result ! = XTC_OK ) return result ;
xtc_plan_destroy ( handle . plan ) ; handle . plan = plan ;
+ + engine_status . submitted_revision ;
return XTC_OK ;
}
2026-10-27 00:53:41 +01:00
xtc_result xtc_backend_panic ( xtc_backend * ) { xtc_midi_plan_panic ( handle . midi , engine_status . sample_position ) ; return XTC_OK ; }
2026-10-27 16:15:34 +01:00
xtc_result xtc_backend_read_meters ( xtc_backend * , xtc_meter_snapshot * out )
{
* out = { } ; out - > master_left = engine_status . peak_left ; out - > master_right = engine_status . peak_right ;
return XTC_OK ;
}
2026-10-27 00:31:03 +01:00
xtc_result xtc_backend_prepare_project ( xtc_backend * backend , const xtc_project * project )
{
xtc_audio_plan * plan = nullptr ;
2026-10-27 00:53:41 +01:00
xtc_midi_plan * midi = nullptr ;
2026-10-27 00:31:03 +01:00
xtc_result r = xtc_audio_plan_create ( project , 48000 , & plan ) ;
2026-10-27 00:53:41 +01:00
if ( r = = XTC_OK ) r = xtc_midi_plan_create ( project , 48000 , & midi ) ;
2026-10-27 00:31:03 +01:00
if ( r = = XTC_OK ) r = xtc_backend_submit ( backend , xtc_project_graph ( project ) ) ;
2026-10-27 00:53:41 +01:00
if ( r ! = XTC_OK ) { xtc_audio_plan_destroy ( plan ) ; xtc_midi_plan_destroy ( midi ) ; return r ; }
2026-10-27 00:31:03 +01:00
xtc_audio_plan_destroy ( handle . audio ) ; handle . audio = plan ;
2026-10-27 00:53:41 +01:00
xtc_midi_plan_destroy ( handle . midi ) ; handle . midi = midi ;
2026-10-27 00:31:03 +01:00
engine_status . asset_issues = static_cast < unsigned int > ( xtc_audio_plan_issues ( plan , nullptr ) ) ;
return XTC_OK ;
}
const char * xtc_backend_asset_issue ( const xtc_backend * )
{ const char * path = " " ; xtc_audio_plan_issues ( handle . audio , & path ) ; return path ; }
xtc_result xtc_backend_set_recorder ( xtc_backend * , xtc_recorder * recorder )
{ if ( handle . running ) return XTC_ERR_INVALID_STATE ; handle . recorder = recorder ; return XTC_OK ; }
2026-10-26 23:40:28 +01:00
void xtc_backend_collect ( xtc_backend * backend )
{ CHECK ( backend = = & handle & & alive = = 1U ) ; + + collects ; }
}
int main ( int argc , char * * argv )
{
reset ( ) ;
{
2026-10-28 10:37:01 +01:00
MainWindow window ( false , " " ) ;
2026-10-26 23:40:28 +01:00
window . show ( argc , argv ) ;
auto * play = find ( & window , " Play " ) ;
auto * stop = find ( & window , " Stop " ) ;
auto * reconnect = find ( & window , " Reconnect " ) ;
CHECK ( play ! = nullptr & & stop ! = nullptr & & reconnect ! = nullptr ) ;
if ( play = = nullptr | | stop = = nullptr | | reconnect = = nullptr ) return 1 ;
CHECK ( play - > active ( ) & & ! stop - > active ( ) ) ;
CHECK ( contains ( & window , " ready / stopped " ) ) ;
press ( play ) ;
CHECK ( starts = = 1U & & ! play - > active ( ) & & stop - > active ( ) ) ;
2026-10-26 23:55:35 +01:00
CHECK ( contains ( & window , " running — modular graph " ) ) ;
2026-10-26 23:40:28 +01:00
press ( stop ) ;
CHECK ( stops = = 1U & & play - > active ( ) & & ! stop - > active ( ) ) ;
2026-10-27 03:25:04 +01:00
auto * pause = find ( & window , " Pause " ) ;
auto * home = find ( & window , " To start " ) ;
CHECK ( pause & & home & & ! pause - > active ( ) & & home - > active ( ) ) ;
engine_status . sample_position = 48000 ;
press ( play ) ;
CHECK ( pause - > active ( ) ) ;
CHECK ( window . pause_playback ( ) = = XTC_OK & & engine_status . sample_position = = 48000 ) ;
CHECK ( timeline ( & window ) - > position ( ) = = 1920 & & play - > active ( ) & & ! pause - > active ( ) ) ;
press ( play ) ;
CHECK ( engine_status . sample_position = = 48000 ) ; // Resume does not rewind.
stop_result = XTC_ERR_IO ;
CHECK ( window . return_to_start ( ) = = XTC_ERR_IO & & engine_status . sample_position = = 48000 ) ;
CHECK ( ! play - > active ( ) & & stop - > active ( ) ) ;
stop_result = XTC_OK ;
seek_result = XTC_ERR_IO ;
CHECK ( window . return_to_start ( ) = = XTC_ERR_IO & & engine_status . sample_position = = 48000 ) ;
CHECK ( play - > active ( ) & & ! stop - > active ( ) ) ;
seek_result = XTC_OK ;
press ( home ) ;
CHECK ( engine_status . sample_position = = 0 & & timeline ( & window ) - > position ( ) = = 0 ) ;
2026-10-26 23:40:28 +01:00
start_result = XTC_ERR_IO ;
press ( play ) ;
CHECK ( contains ( & window , " Start engine failed: I/O error " ) ) ;
pump ( ) ; // A refresh must not erase a command error.
CHECK ( contains ( & window , " Start engine failed: I/O error " ) ) ;
start_result = XTC_OK ;
press ( play ) ;
CHECK ( ! contains ( & window , " Start engine failed " ) ) ;
stop_result = XTC_ERR_IO ;
press ( stop ) ;
CHECK ( contains ( & window , " Stop engine failed " ) & & stop - > active ( ) ) ;
stop_result = XTC_OK ;
press ( stop ) ;
Fl_Menu_Bar * menu = nullptr ;
Fl_Tabs * tabs = nullptr ;
for ( int i = 0 ; i < window . children ( ) ; + + i ) {
if ( auto * found = dynamic_cast < Fl_Menu_Bar * > ( window . child ( i ) ) ) menu = found ;
if ( auto * found = dynamic_cast < Fl_Tabs * > ( window . child ( i ) ) ) tabs = found ;
}
CHECK ( menu ! = nullptr & & tabs ! = nullptr ) ;
if ( menu = = nullptr | | tabs = = nullptr ) return 1 ;
for ( const char * view : { " View/Mixer " , " View/Modular Graph " , " View/Sequencer " } ) {
const Fl_Menu_Item * item = menu - > find_item ( view ) ;
CHECK ( item ! = nullptr ) ;
if ( item ! = nullptr ) {
menu - > picked ( item ) ;
CHECK ( std : : strcmp ( tabs - > value ( ) - > label ( ) , std : : strchr ( view , ' / ' ) + 1 ) = = 0 ) ;
}
}
2026-11-06 00:06:56 +01:00
GraphWidget * canvas = graph_canvas ( & window ) ;
2026-10-26 23:55:35 +01:00
CHECK ( canvas ! = nullptr ) ;
if ( canvas = = nullptr ) return 1 ;
menu - > picked ( menu - > find_item ( " View/Modular Graph " ) ) ;
press ( find ( & window , " Add oscillator " ) ) ;
press ( find ( & window , " Add gain " ) ) ;
press ( find ( & window , " Add meter " ) ) ;
press ( find ( & window , " Add output " ) ) ;
size_t nodes , edges ;
CHECK ( xtc_graph_get_counts ( canvas - > graph ( ) , & nodes , & edges ) = = XTC_OK & & nodes = = 4U & & edges = = 0U ) ;
CHECK ( canvas - > connect_ports ( { 0U , 1U , 1U , 0U } ) = = XTC_OK ) ;
CHECK ( canvas - > connect_ports ( { 1U , 1U , 2U , 0U } ) = = XTC_OK ) ;
CHECK ( canvas - > connect_ports ( { 2U , 1U , 3U , 0U } ) = = XTC_OK ) ;
float rendered [ 32 ] ;
CHECK ( xtc_plan_process ( handle . plan , nullptr , rendered , 32U ) = = XTC_OK ) ;
CHECK ( std : : fabs ( rendered [ 1 ] - static_cast < float > ( std : : sin ( 6.283185307179586 * 440.0 / 48000.0 ) * 0.1 ) ) < 0.00001f ) ;
const xtc_graph * before_invalid = canvas - > graph ( ) ;
unsigned int before_submit = submissions ;
CHECK ( canvas - > connect_ports ( { 2U , 1U , 1U , 0U } ) = = XTC_ERR_GRAPH_CYCLE ) ;
CHECK ( canvas - > graph ( ) = = before_invalid & & submissions = = before_submit & & contains ( & window , " graph contains a cycle " ) ) ;
CHECK ( canvas - > connect_ports ( { 1U , 0U , 2U , 0U } ) = = XTC_ERR_INVALID_ARGUMENT ) ;
CHECK ( canvas - > connect_ports ( { 0U , 1U , 1U , 0U } ) = = XTC_ERR_INVALID_STATE ) ;
submit_result = XTC_ERR_CAPACITY ;
xtc_plan * before_plan = handle . plan ;
CHECK ( canvas - > add_node ( XTC_NODE_GAIN ) = = XTC_ERR_CAPACITY ) ;
CHECK ( canvas - > graph ( ) = = before_invalid & & handle . plan = = before_plan & & contains ( & window , " Graph unchanged: capacity " ) ) ;
submit_result = XTC_ERR_IO ;
CHECK ( canvas - > add_node ( XTC_NODE_GAIN ) = = XTC_ERR_IO & & canvas - > graph ( ) = = before_invalid ) ;
submit_result = XTC_OK ;
CHECK ( canvas - > select_node ( 0U ) & & canvas - > move_node ( 0U , 65 , 90 ) ) ;
CHECK ( submissions = = before_submit + 2U ) ; // Moving is GUI state, no compilation.
CHECK ( canvas - > add_node ( XTC_NODE_GAIN ) = = XTC_OK ) ;
int left , top ;
CHECK ( canvas - > node_position ( 0U , left , top ) & & left = = 65 & & top = = 90 ) ;
CHECK ( canvas - > delete_selected ( ) = = XTC_OK ) ; // Newly added node.
CHECK ( canvas - > select_connection ( 1U , 2U ) & & canvas - > delete_selected ( ) = = XTC_OK ) ;
CHECK ( xtc_plan_process ( handle . plan , nullptr , rendered , 32U ) = = XTC_OK & & rendered [ 1 ] = = 0.0f ) ;
CHECK ( canvas - > connect_ports ( { 1U , 1U , 2U , 0U } ) = = XTC_OK ) ;
CHECK ( canvas - > select_node ( 1U ) & & canvas - > delete_selected ( ) = = XTC_OK ) ;
CHECK ( xtc_graph_get_counts ( canvas - > graph ( ) , & nodes , & edges ) = = XTC_OK & & nodes = = 3U & & edges = = 1U ) ;
xtc_node_info surviving { } ;
CHECK ( xtc_graph_get_node ( canvas - > graph ( ) , 1U , & surviving ) = = XTC_OK & & surviving . id = = 2U ) ;
engine_status . active_revision = engine_status . submitted_revision ;
press ( play ) ;
pump ( ) ;
CHECK ( contains ( & window , " Graph: active in engine " ) ) ;
CHECK ( canvas - > add_node ( XTC_NODE_GAIN ) = = XTC_OK ) ;
CHECK ( contains ( & window , " Graph: queued " ) ) ;
engine_status . active_revision = engine_status . submitted_revision ;
pump ( ) ;
CHECK ( contains ( & window , " Graph: active in engine " ) ) ;
press ( stop ) ;
for ( size_t id : { 0U , 2U , 3U , 5U } ) { CHECK ( canvas - > select_node ( id ) ) ; CHECK ( canvas - > delete_selected ( ) = = XTC_OK ) ; }
CHECK ( xtc_graph_get_counts ( canvas - > graph ( ) , & nodes , & edges ) = = XTC_OK & & nodes = = 0U & & edges = = 0U ) ;
CHECK ( xtc_plan_process ( handle . plan , nullptr , rendered , 32U ) = = XTC_OK & & rendered [ 1 ] = = 0.0f ) ;
2026-10-27 00:10:45 +01:00
auto * sequence = timeline ( & window ) ;
CHECK ( sequence ! = nullptr ) ;
char directory [ ] = " /tmp/xtc-gui-XXXXXX " ; CHECK ( mkdtemp ( directory ) ! = nullptr ) ;
std : : string filename = std : : string ( directory ) + " /song.xtc " ;
press ( find ( & window , " Add track " ) ) ; press ( find ( & window , " Add region " ) ) ;
size_t track_count , clip_count ;
CHECK ( xtc_project_get_counts ( sequence - > project ( ) , & track_count , & clip_count ) = = XTC_OK & & track_count = = 1 & & clip_count = = 1 ) ;
xtc_clip_info region { } ;
CHECK ( xtc_project_get_clip ( sequence - > project ( ) , 0 , & region ) = = XTC_OK ) ;
2026-10-27 18:04:39 +01:00
gesture ( sequence , 210 , 50 , 144 , 0 ) ; // Move by three beats.
gesture ( sequence , 325 , 50 , 48 , 0 ) ; // Trim left edge by one beat.
2026-10-27 00:10:45 +01:00
CHECK ( xtc_project_get_clip ( sequence - > project ( ) , 0 , & region ) = = XTC_OK & &
region . start_tick = = 3840 & & region . length_tick = = 2880 & & region . offset_tick = = 960 ) ;
int width = sequence - > w ( ) ;
press ( find ( & window , " Zoom + " ) ) ; CHECK ( sequence - > w ( ) > width ) ;
press ( find ( & window , " Zoom - " ) ) ; CHECK ( sequence - > w ( ) = = width ) ;
CHECK ( window . save_project ( filename . c_str ( ) ) = = XTC_OK ) ;
const xtc_project * retained = sequence - > project ( ) ;
CHECK ( window . open_project ( " /missing/xtc-project " ) = = XTC_ERR_IO & & sequence - > project ( ) = = retained ) ;
2026-10-27 03:25:04 +01:00
press ( play ) ;
2026-10-27 00:10:45 +01:00
submit_result = XTC_ERR_CAPACITY ;
CHECK ( window . open_project ( filename . c_str ( ) ) = = XTC_ERR_CAPACITY & & sequence - > project ( ) = = retained ) ;
2026-10-27 03:25:04 +01:00
CHECK ( play - > active ( ) & & ! stop - > active ( ) ) ; // Failed install refreshed stopped state.
2026-10-27 00:10:45 +01:00
submit_result = XTC_OK ;
press ( find ( & window , " Delete region/track " ) ) ;
CHECK ( window . open_project ( filename . c_str ( ) ) = = XTC_OK ) ;
CHECK ( xtc_project_get_clip ( sequence - > project ( ) , 0 , & region ) = = XTC_OK & & region . start_tick = = 3840 & & region . offset_tick = = 960 ) ;
engine_status . sample_position = 48000 ; pump ( ) ;
CHECK ( sequence - > position ( ) = = 1920 ) ;
CHECK ( canvas - > add_node ( XTC_NODE_GAIN ) = = XTC_OK ) ;
CHECK ( sequence - > position ( ) = = 1920 ) ; // Graph edits preserve the transport.
engine_status . sample_position = 0 ;
2026-10-27 00:31:03 +01:00
std : : string take = std : : string ( directory ) + " /take.wav " ;
2026-10-27 16:49:30 +01:00
size_t before_take_nodes = 0 , before_take_edges = 0 ;
CHECK ( xtc_graph_get_counts ( canvas - > graph ( ) , & before_take_nodes , & before_take_edges ) = = XTC_OK ) ;
2026-10-27 00:31:03 +01:00
CHECK ( window . record_audio ( take . c_str ( ) ) = = XTC_OK & & handle . recorder ! = nullptr ) ;
CHECK ( canvas - > add_node ( XTC_NODE_GAIN ) = = XTC_ERR_INVALID_STATE ) ;
CHECK ( window . save_project ( filename . c_str ( ) ) = = XTC_ERR_INVALID_STATE ) ;
2026-10-27 03:25:04 +01:00
CHECK ( ! pause - > active ( ) & & ! home - > active ( ) ) ;
CHECK ( window . pause_playback ( ) = = XTC_ERR_INVALID_STATE ) ;
CHECK ( window . return_to_start ( ) = = XTC_ERR_INVALID_STATE ) ;
CHECK ( window . new_project ( ) = = XTC_ERR_INVALID_STATE ) ;
2026-10-27 00:31:03 +01:00
window . do_callback ( ) ;
CHECK ( window . shown ( ) & & contains ( & window , " Stop recording before closing " ) ) ;
float recorded [ 64 ] ; for ( float & value : recorded ) value = 0.25f ;
xtc_recorder_push ( handle . recorder , recorded , 64 ) ;
pump ( ) ;
press ( stop ) ;
CHECK ( handle . recorder = = nullptr ) ;
CHECK ( xtc_project_get_counts ( sequence - > project ( ) , & track_count , & clip_count ) = = XTC_OK & & clip_count = = 2 ) ;
2026-10-27 16:49:30 +01:00
CHECK ( window . undo ( false ) = = XTC_OK ) ;
CHECK ( xtc_project_get_counts ( sequence - > project ( ) , & track_count , & clip_count ) = = XTC_OK & & clip_count = = 1 ) ;
CHECK ( access ( take . c_str ( ) , F_OK ) = = 0 ) ; // Undo never deletes the external take.
CHECK ( window . undo ( false ) = = XTC_OK ) ;
size_t after_take_nodes = 0 , after_take_edges = 0 ;
CHECK ( xtc_graph_get_counts ( canvas - > graph ( ) , & after_take_nodes , & after_take_edges ) = = XTC_OK & & after_take_nodes + 1 = = before_take_nodes ) ;
CHECK ( window . undo ( true ) = = XTC_OK & & window . undo ( true ) = = XTC_OK ) ;
CHECK ( xtc_project_get_counts ( sequence - > project ( ) , & track_count , & clip_count ) = = XTC_OK & & clip_count = = 2 ) ;
2026-10-27 15:46:41 +01:00
CHECK ( sequence - > waveform_count ( ) = = 1 ) ;
// Short clips use their four-pixel draw width for hit testing too.
xtc_clip_info audio_clip { } ;
CHECK ( xtc_project_get_clip ( sequence - > project ( ) , 1 , & audio_clip ) = = XTC_OK & & audio_clip . asset_path [ 0 ] ) ;
size_t audio_row = 0 ;
for ( ; audio_row < track_count ; + + audio_row ) {
xtc_track_info t { } ; xtc_project_get_track ( sequence - > project ( ) , audio_row , & t ) ;
if ( t . id = = audio_clip . track_id ) break ;
}
2026-10-27 18:04:39 +01:00
gesture ( sequence , 181 + static_cast < int > ( audio_clip . start_tick * 48 / 960 ) , 50 + static_cast < int > ( audio_row ) * 56 , 0 , 0 ) ;
2026-10-27 15:46:41 +01:00
CHECK ( sequence - > selected_clip ( ) = = audio_clip . id ) ;
CHECK ( sequence - > audio_properties ( 0.5f , 1 , 1 ) = = XTC_OK ) ;
sequence - > position ( audio_clip . start_tick + 1 ) ;
int split_key = Fl : : e_keysym , split_state = Fl : : e_state ;
Fl : : e_keysym = ' s ' ; Fl : : e_state = 0 ;
CHECK ( sequence - > handle ( FL_KEYDOWN ) = = 1 ) ;
Fl : : e_keysym = split_key ; Fl : : e_state = split_state ;
CHECK ( xtc_project_get_counts ( sequence - > project ( ) , & track_count , & clip_count ) = = XTC_OK & & clip_count = = 3 ) ;
CHECK ( sequence - > duplicate_selection ( ) = = XTC_OK & & sequence - > waveform_count ( ) = = 1 ) ;
CHECK ( sequence - > delete_selection ( ) = = XTC_OK ) ;
CHECK ( window . undo ( ) = = XTC_OK ) ;
CHECK ( xtc_project_get_counts ( sequence - > project ( ) , & track_count , & clip_count ) = = XTC_OK & & clip_count = = 4 ) ;
CHECK ( window . undo ( ) = = XTC_OK & & window . undo ( ) = = XTC_OK & & window . undo ( ) = = XTC_OK ) ;
CHECK ( xtc_project_find_clip ( sequence - > project ( ) , audio_clip . id , & audio_clip ) = = XTC_OK & & audio_clip . gain = = 1 & & audio_clip . fade_in_tick = = 0 & & audio_clip . length_tick = = 3 ) ;
CHECK ( xtc_project_get_counts ( sequence - > project ( ) , & track_count , & clip_count ) = = XTC_OK & & clip_count = = 2 ) ;
2026-10-27 00:31:03 +01:00
CHECK ( window . save_project ( filename . c_str ( ) ) = = XTC_OK ) ;
CHECK ( window . open_project ( filename . c_str ( ) ) = = XTC_OK ) ;
float mix [ 64 ] ; CHECK ( xtc_audio_plan_render ( handle . audio , 0 , mix , 64 ) = = XTC_OK & & mix [ 0 ] = = 0.25f ) ;
CHECK ( unlink ( take . c_str ( ) ) = = 0 ) ;
CHECK ( window . open_project ( filename . c_str ( ) ) = = XTC_OK & & contains ( & window , " Missing/unreadable audio " ) ) ;
CHECK ( window . import_audio ( " /missing/xtc-audio.wav " ) = = XTC_ERR_IO ) ;
2026-10-27 00:53:41 +01:00
press ( find ( & window , " Add MIDI clip " ) ) ;
CHECK ( window . open_piano_roll ( ) = = XTC_OK ) ;
auto * roll = window . piano_roll ( ) ; CHECK ( roll ! = nullptr ) ;
int saved_x = Fl : : e_x , saved_y = Fl : : e_y , saved_key = Fl : : e_keysym ;
Fl : : e_x = roll - > x ( ) + 112 ; Fl : : e_y = roll - > y ( ) + 1110 ; Fl : : e_keysym = FL_Button + FL_LEFT_MOUSE ;
CHECK ( roll - > handle ( FL_PUSH ) = = 1 ) ; ( void ) roll - > handle ( FL_RELEASE ) ;
Fl : : e_x = saved_x ; Fl : : e_y = saved_y ; Fl : : e_keysym = saved_key ;
gesture ( roll , 130 , 1110 , 24 , - 16 ) ; // Move one sixteenth and a semitone.
gesture ( roll , 230 , 1094 , 48 , 0 ) ; // Extend right edge by half a beat.
xtc_note_info drawn { } ; size_t note_count ;
CHECK ( xtc_project_note_count ( sequence - > project ( ) , & note_count ) = = XTC_OK & & note_count = = 1 ) ;
CHECK ( xtc_project_get_note ( sequence - > project ( ) , 0 , & drawn ) = = XTC_OK & &
drawn . start_tick = = 720 & & drawn . length_tick = = 1440 & & drawn . pitch = = 61 ) ;
2026-10-27 15:34:54 +01:00
// Piano-roll filtered commands use one undo step and preserve durations.
uint64_t edited_clip = roll - > clip ( ) ;
CHECK ( roll - > set_grid ( 160 ) = = XTC_OK & & roll - > set_grid ( 320 ) = = XTC_OK ) ;
CHECK ( roll - > set_grid ( 7 ) = = XTC_ERR_INVALID_ARGUMENT ) ;
CHECK ( roll - > set_filter ( 0 , 60 , 72 ) = = XTC_OK ) ;
int note_key = Fl : : e_keysym , note_state = Fl : : e_state ;
Fl : : e_keysym = ' a ' ; Fl : : e_state = FL_CTRL ;
CHECK ( roll - > handle ( FL_KEYDOWN ) = = 1 ) ;
Fl : : e_keysym = note_key ; Fl : : e_state = note_state ;
xtc_note_edit properties { XTC_NOTES_EDIT , 0 , 0 , 77 , 2 , 0 , 0 } ;
CHECK ( roll - > apply ( properties ) = = XTC_OK ) ;
CHECK ( xtc_project_get_note ( sequence - > project ( ) , 0 , & drawn ) = = XTC_OK & & drawn . velocity = = 77 & & drawn . channel = = 2 ) ;
CHECK ( roll - > set_filter ( - 1 , 0 , 127 ) = = XTC_OK ) ;
roll - > select_filtered ( ) ;
Fl : : e_keysym = ' q ' ; Fl : : e_state = 0 ;
CHECK ( roll - > handle ( FL_KEYDOWN ) = = 1 ) ;
Fl : : e_keysym = note_key ; Fl : : e_state = note_state ;
CHECK ( xtc_project_get_note ( sequence - > project ( ) , 0 , & drawn ) = = XTC_OK & & drawn . start_tick = = 640 & & drawn . length_tick = = 1440 ) ;
Fl : : e_keysym = FL_Up ; Fl : : e_state = 0 ;
CHECK ( roll - > handle ( FL_KEYDOWN ) = = 1 ) ;
Fl : : e_keysym = note_key ; Fl : : e_state = note_state ;
CHECK ( xtc_project_get_note ( sequence - > project ( ) , 0 , & drawn ) = = XTC_OK & & drawn . pitch = = 62 ) ;
xtc_note_edit repeat { XTC_NOTES_REPEAT , 0 , 0 , - 1 , - 1 , 1 , 960 } ;
CHECK ( roll - > apply ( repeat ) = = XTC_OK ) ;
CHECK ( xtc_project_note_count ( sequence - > project ( ) , & note_count ) = = XTC_OK & & note_count = = 2 ) ;
CHECK ( roll - > delete_selected ( ) = = XTC_OK ) ;
CHECK ( xtc_project_note_count ( sequence - > project ( ) , & note_count ) = = XTC_OK & & note_count = = 0 ) ;
CHECK ( window . undo ( ) = = XTC_OK ) ;
CHECK ( xtc_project_note_count ( sequence - > project ( ) , & note_count ) = = XTC_OK & & note_count = = 2 ) ;
CHECK ( window . undo ( true ) = = XTC_OK ) ;
CHECK ( xtc_project_note_count ( sequence - > project ( ) , & note_count ) = = XTC_OK & & note_count = = 0 ) ;
for ( int step = 0 ; step < 5 ; + + step ) CHECK ( window . undo ( ) = = XTC_OK ) ;
CHECK ( xtc_project_get_note ( sequence - > project ( ) , 0 , & drawn ) = = XTC_OK & & drawn . pitch = = 61 & & drawn . start_tick = = 720 & & drawn . velocity = = 100 & & drawn . channel = = 0 ) ;
CHECK ( xtc_project_note_count ( sequence - > project ( ) , & note_count ) = = XTC_OK & & note_count = = 1 ) ;
roll - > set_project ( sequence - > project ( ) , edited_clip ) ;
CHECK ( roll - > set_grid ( 160 ) = = XTC_OK & & roll - > set_filter ( 5 , 60 , 60 ) = = XTC_OK ) ;
Fl : : e_x = roll - > x ( ) + 104 ; Fl : : e_y = roll - > y ( ) + 1110 ;
Fl : : e_keysym = FL_Button + FL_LEFT_MOUSE ;
CHECK ( roll - > handle ( FL_PUSH ) = = 1 ) ; ( void ) roll - > handle ( FL_RELEASE ) ;
Fl : : e_x = saved_x ; Fl : : e_y = saved_y ; Fl : : e_keysym = saved_key ;
CHECK ( xtc_project_get_note ( sequence - > project ( ) , 1 , & drawn ) = = XTC_OK & & drawn . start_tick = = 320 & & drawn . channel = = 5 & & drawn . pitch = = 60 ) ;
CHECK ( window . undo ( ) = = XTC_OK ) ;
CHECK ( xtc_project_note_count ( sequence - > project ( ) , & note_count ) = = XTC_OK & & note_count = = 1 ) ;
CHECK ( roll - > set_grid ( 240 ) = = XTC_OK & & roll - > set_filter ( - 1 , 0 , 127 ) = = XTC_OK ) ;
roll - > set_project ( sequence - > project ( ) , edited_clip ) ;
2026-10-27 00:53:41 +01:00
CHECK ( window . save_project ( filename . c_str ( ) ) = = XTC_OK ) ;
CHECK ( window . open_project ( filename . c_str ( ) ) = = XTC_OK ) ;
CHECK ( xtc_project_get_note ( sequence - > project ( ) , 0 , & drawn ) = = XTC_OK & & drawn . pitch = = 61 & & drawn . start_tick = = 720 ) ;
float instrument [ 32 ] , heard [ 32 ] ;
CHECK ( xtc_midi_plan_render ( handle . midi , 18000 , nullptr , 0 , instrument , 32 ) = = XTC_OK & & instrument [ 1 ] > 0 ) ;
CHECK ( xtc_plan_process_sources ( handle . plan , nullptr , nullptr , instrument , heard , 32 ) = = XTC_OK & & heard [ 1 ] = = instrument [ 1 ] ) ;
engine_status . sample_position = 18032 ;
press ( find ( & window , " Panic " ) ) ;
CHECK ( xtc_midi_plan_render ( handle . midi , 18032 , nullptr , 0 , instrument , 32 ) = = XTC_OK & & instrument [ 0 ] = = 0 ) ;
engine_status . sample_position = 0 ;
2026-10-27 01:06:57 +01:00
// Mixer edits and automation are undoable; failed backend install retains cursor.
auto * mixer_ui = window . mixer ( ) ; CHECK ( mixer_ui ! = nullptr ) ;
xtc_track_info mixed { } ;
CHECK ( mixer_ui - > mix ( 0.5f , - 1 , 0 , 1 ) = = XTC_OK ) ;
CHECK ( xtc_project_get_track ( sequence - > project ( ) , 0 , & mixed ) = = XTC_OK & & mixed . gain = = 0.5f & & mixed . pan = = - 1 & & mixed . solo ) ;
CHECK ( mixer_ui - > point ( 0 , 0 ) = = XTC_OK & & mixer_ui - > point ( 960 , 1 ) = = XTC_OK ) ;
size_t points = 0 ;
CHECK ( xtc_project_automation_count ( sequence - > project ( ) , & points ) = = XTC_OK & & points = = 2 ) ;
CHECK ( window . undo ( ) = = XTC_OK ) ;
CHECK ( xtc_project_automation_count ( sequence - > project ( ) , & points ) = = XTC_OK & & points = = 1 ) ;
submit_result = XTC_ERR_CAPACITY ;
CHECK ( window . undo ( true ) = = XTC_ERR_CAPACITY ) ;
CHECK ( xtc_project_automation_count ( sequence - > project ( ) , & points ) = = XTC_OK & & points = = 1 ) ;
submit_result = XTC_OK ;
CHECK ( window . undo ( true ) = = XTC_OK ) ;
CHECK ( xtc_project_automation_count ( sequence - > project ( ) , & points ) = = XTC_OK & & points = = 2 ) ;
CHECK ( window . undo ( ) = = XTC_OK & & mixer_ui - > point ( 1920 , 0.25f ) = = XTC_OK & & window . undo ( true ) = = XTC_ERR_NOT_FOUND ) ;
CHECK ( window . save_project ( filename . c_str ( ) ) = = XTC_OK & & window . open_project ( filename . c_str ( ) ) = = XTC_OK ) ;
CHECK ( xtc_project_get_track ( sequence - > project ( ) , 0 , & mixed ) = = XTC_OK & & mixed . gain = = 0.5f & & mixed . pan = = - 1 ) ;
CHECK ( xtc_project_automation_count ( sequence - > project ( ) , & points ) = = XTC_OK & & points = = 2 ) ;
CHECK ( window . undo ( ) = = XTC_ERR_NOT_FOUND ) ; // Open resets history.
auto * lane = mixer_ui - > child ( mixer_ui - > children ( ) - 1 ) ;
saved_x = Fl : : e_x ; saved_y = Fl : : e_y ; saved_key = Fl : : e_keysym ;
Fl : : e_x = lane - > x ( ) + ( lane - > w ( ) - 1 ) / 4 ; Fl : : e_y = lane - > y ( ) + lane - > h ( ) / 2 ;
Fl : : e_keysym = FL_Button + FL_LEFT_MOUSE ;
CHECK ( lane - > handle ( FL_PUSH ) = = 1 ) ;
CHECK ( xtc_project_automation_count ( sequence - > project ( ) , & points ) = = XTC_OK & & points = = 3 ) ;
Fl : : e_keysym = FL_Button + FL_RIGHT_MOUSE ;
CHECK ( lane - > handle ( FL_PUSH ) = = 1 ) ;
CHECK ( xtc_project_automation_count ( sequence - > project ( ) , & points ) = = XTC_OK & & points = = 2 ) ;
Fl : : e_keysym = ' m ' ; CHECK ( mixer_ui - > handle ( FL_KEYDOWN ) = = 1 ) ;
CHECK ( xtc_project_get_track ( sequence - > project ( ) , 0 , & mixed ) = = XTC_OK & & mixed . mute ) ;
CHECK ( window . undo ( ) = = XTC_OK ) ;
CHECK ( xtc_project_get_track ( sequence - > project ( ) , 0 , & mixed ) = = XTC_OK & & ! mixed . mute ) ;
Fl : : e_x = saved_x ; Fl : : e_y = saved_y ; Fl : : e_keysym = saved_key ;
2026-10-27 00:10:45 +01:00
CHECK ( unlink ( filename . c_str ( ) ) = = 0 & & rmdir ( directory ) = = 0 ) ;
2026-10-26 23:40:28 +01:00
engine_status . xruns = 3U ;
engine_status . processed_blocks = 123U ;
pump ( ) ;
CHECK ( contains ( & window , " 123 blocks | 3 xruns " ) ) ;
engine_status . sample_rate_changed = 1 ;
pump ( ) ;
CHECK ( contains ( & window , " sample rate changed " ) & & ! play - > active ( ) ) ;
engine_status . sample_rate_changed = 0 ;
engine_status . server_lost = 1 ;
pump ( ) ;
CHECK ( contains ( & window , " server lost " ) & & ! play - > active ( ) ) ;
engine_status . server_lost = 0 ;
press ( reconnect ) ;
CHECK ( contains ( & window , " ready / stopped " ) & & play - > active ( ) ) ;
status_result = XTC_ERR_IO ;
pump ( ) ;
CHECK ( contains ( & window , " status unavailable " ) & & ! play - > active ( ) ) ;
status_result = XTC_OK ;
press ( reconnect ) ;
close_result = XTC_ERR_IO ;
press ( reconnect ) ;
CHECK ( contains ( & window , " Reconnect could not close JACK " ) & & alive = = 1U ) ;
window . do_callback ( ) ;
CHECK ( window . shown ( ) & & alive = = 1U & & contains ( & window , " window kept open for retry " ) ) ;
close_result = XTC_OK ;
menu - > picked ( menu - > find_item ( " File/Quit " ) ) ;
CHECK ( ! window . shown ( ) & & alive = = 0U ) ;
unsigned int before = collects ;
pump ( ) ;
CHECK ( collects = = before ) ; // Removed timer cannot call a closed backend.
}
for ( xtc_result failure : { XTC_ERR_IO , XTC_ERR_UNSUPPORTED } ) {
reset ( ) ;
open_result = failure ;
2026-10-28 10:37:01 +01:00
MainWindow window ( false , " " ) ;
2026-10-26 23:40:28 +01:00
window . show ( argc , argv ) ;
CHECK ( contains ( & window , " Open JACK failed " ) & & ! find ( & window , " Play " ) - > active ( ) ) ;
open_result = XTC_OK ;
press ( find ( & window , " Reconnect " ) ) ;
CHECK ( find ( & window , " Play " ) - > active ( ) ) ;
press ( find ( & window , " Play " ) ) ;
window . do_callback ( ) ; // Close while running, without a preceding stop.
CHECK ( ! window . shown ( ) & & alive = = 0U ) ;
}
reset ( ) ;
partial_open = true ;
open_result = XTC_ERR_IO ;
{
2026-10-28 10:37:01 +01:00
MainWindow window ( false , " " ) ;
2026-10-26 23:40:28 +01:00
CHECK ( alive = = 1U & & ! find ( & window , " Play " ) - > active ( ) ) ;
window . do_callback ( ) ; // Partially initialized handles are closed only.
CHECK ( alive = = 0U ) ;
}
reset ( ) ;
for ( int i = 0 ; i < 10 ; + + i ) {
2026-10-28 10:37:01 +01:00
MainWindow window ( false , " " ) ;
2026-10-26 23:40:28 +01:00
press ( find ( & window , " Play " ) ) ;
window . do_callback ( ) ;
}
CHECK ( alive = = 0U ) ;
2026-10-27 06:28:25 +01:00
reset ( ) ;
{
2026-10-28 10:37:01 +01:00
MainWindow window ( false , " " ) ; auto * s = timeline ( & window ) ;
2026-10-27 06:28:25 +01:00
CHECK ( s - > add_track ( ) = = XTC_OK & & s - > rename_selection ( " Keys " ) = = XTC_OK ) ;
CHECK ( s - > add_region ( ) = = XTC_OK & & s - > rename_selection ( " Verse " ) = = XTC_OK ) ;
CHECK ( s - > grid ( 120 ) = = XTC_OK & & s - > grid ( ) = = 120 & & s - > grid ( 0 ) = = XTC_ERR_INVALID_ARGUMENT ) ;
CHECK ( s - > handle ( FL_FOCUS ) = = 1 ) ;
int old_key = Fl : : e_keysym , old_state = Fl : : e_state ;
Fl : : e_keysym = ' d ' ; Fl : : e_state = FL_CTRL ;
CHECK ( s - > handle ( FL_KEYDOWN ) = = 1 ) ;
Fl : : e_keysym = old_key ; Fl : : e_state = old_state ;
size_t tracks , clips ; xtc_project_get_counts ( s - > project ( ) , & tracks , & clips ) ;
CHECK ( clips = = 2 ) ;
xtc_clip_info c { } ; CHECK ( xtc_project_get_clip ( s - > project ( ) , 1 , & c ) = = XTC_OK & & c . start_tick = = 3840 & & ! std : : strcmp ( c . name , " Verse " ) ) ;
CHECK ( window . undo ( false ) = = XTC_OK ) ; xtc_project_get_counts ( s - > project ( ) , & tracks , & clips ) ; CHECK ( clips = = 1 ) ;
CHECK ( window . undo ( true ) = = XTC_OK ) ; xtc_project_get_counts ( s - > project ( ) , & tracks , & clips ) ; CHECK ( clips = = 2 ) ;
CHECK ( s - > add_track ( ) = = XTC_OK & & s - > rename_selection ( " Bass " ) = = XTC_OK & & s - > move_track ( - 1 ) = = XTC_OK ) ;
xtc_track_info t { } ; CHECK ( xtc_project_get_track ( s - > project ( ) , 0 , & t ) = = XTC_OK & & ! std : : strcmp ( t . name , " Bass " ) ) ;
CHECK ( window . undo ( false ) = = XTC_OK ) ;
CHECK ( xtc_project_get_track ( s - > project ( ) , 0 , & t ) = = XTC_OK & & ! std : : strcmp ( t . name , " Keys " ) ) ;
window . do_callback ( ) ;
}
2026-10-27 16:00:29 +01:00
{
reset ( ) ;
2026-10-28 10:37:01 +01:00
MainWindow window ( false , " " ) ;
2026-10-27 16:00:29 +01:00
auto * canvas = graph_canvas ( & window ) ;
CHECK ( canvas ! = nullptr ) ;
CHECK ( canvas - > add_node ( XTC_NODE_OSCILLATOR ) = = XTC_OK ) ;
CHECK ( canvas - > add_node ( XTC_NODE_OUTPUT ) = = XTC_OK ) ;
CHECK ( canvas - > connect_ports ( { 0 , 1 , 1 , 0 } ) = = XTC_OK ) ;
CHECK ( canvas - > select_connection ( 0 , 1 ) & & contains ( & window , " Wire: #0 audio out -> #1 sum in " ) ) ;
const xtc_graph * original = canvas - > graph ( ) ;
unsigned int submitted = submissions ;
submit_result = XTC_ERR_CAPACITY ;
CHECK ( canvas - > insert_gain ( ) = = XTC_ERR_CAPACITY & & canvas - > graph ( ) = = original & & submissions = = submitted + 1 ) ;
submit_result = XTC_OK ;
CHECK ( canvas - > insert_gain ( ) = = XTC_OK ) ;
size_t inserted = canvas - > selected_node ( ) , nodes , edges ;
CHECK ( inserted = = 2 ) ;
CHECK ( xtc_graph_get_counts ( canvas - > graph ( ) , & nodes , & edges ) = = XTC_OK & & nodes = = 3 & & edges = = 2 ) ;
xtc_routing_info route { } ;
CHECK ( canvas - > routing ( inserted , route ) & & route . level = = 1 & & route . reaches_output & & route . inputs = = 1 ) ;
submitted = submissions ;
CHECK ( canvas - > auto_layout ( ) & & submissions = = submitted ) ;
int sx = 0 , sy = 0 , gx = 0 , gy = 0 , ox = 0 , oy = 0 ;
CHECK ( canvas - > node_position ( 0 , sx , sy ) & & canvas - > node_position ( inserted , gx , gy ) & & canvas - > node_position ( 1 , ox , oy ) ) ;
CHECK ( sx < gx & & gx < ox ) ;
CHECK ( canvas - > select_node ( inserted ) & & contains ( & window , " 1 incoming, 1 outgoing " ) ) ;
CHECK ( canvas - > select_connection ( 0 , inserted ) & & canvas - > delete_selected ( ) = = XTC_OK ) ;
CHECK ( canvas - > select_node ( inserted ) & & contains ( & window , " unwired input uses external audio " ) ) ;
CHECK ( window . undo ( ) = = XTC_OK ) ; // Disconnect is independently undoable.
CHECK ( window . undo ( ) = = XTC_OK ) ; // Whole insertion is one undo step.
CHECK ( xtc_graph_get_counts ( canvas - > graph ( ) , & nodes , & edges ) = = XTC_OK & & nodes = = 2 & & edges = = 1 ) ;
CHECK ( window . undo ( true ) = = XTC_OK ) ;
CHECK ( xtc_graph_get_counts ( canvas - > graph ( ) , & nodes , & edges ) = = XTC_OK & & nodes = = 3 & & edges = = 2 ) ;
CHECK ( canvas - > select_node ( inserted ) ) ;
int key = Fl : : e_keysym , state = Fl : : e_state ;
Fl : : e_keysym = FL_Right ; Fl : : e_state = 0 ;
CHECK ( canvas - > handle ( FL_KEYDOWN ) = = 1 & & canvas - > selected_node ( ) = = 0 ) ;
Fl : : e_keysym = key ; Fl : : e_state = state ;
// Whole-project binding must give every new node a distinct position.
canvas - > reset_layout ( ) ; canvas - > set_graph ( canvas - > graph ( ) ) ;
CHECK ( canvas - > node_position ( 0 , sx , sy ) & & canvas - > node_position ( inserted , gx , gy ) & & canvas - > node_position ( 1 , ox , oy ) ) ;
CHECK ( ( sx ! = gx | | sy ! = gy ) & & ( sx ! = ox | | sy ! = oy ) & & ( gx ! = ox | | gy ! = oy ) ) ;
window . do_callback ( ) ;
}
2026-10-27 16:15:34 +01:00
{
2026-10-28 10:37:01 +01:00
reset ( ) ; MainWindow window ( false , " " ) ;
2026-10-27 16:15:34 +01:00
auto * sequence = timeline ( & window ) ; auto * mixer = window . mixer ( ) ;
CHECK ( sequence - > add_track ( ) = = XTC_OK ) ;
CHECK ( mixer - > add_bus ( ) = = XTC_OK ) ;
uint64_t bus = mixer - > selected_track ( ) ;
xtc_track_info track { } ;
CHECK ( xtc_project_get_track ( sequence - > project ( ) , 1 , & track ) = = XTC_OK & & track . is_bus ) ;
CHECK ( mixer - > mix ( .5f , 1 , 0 , 1 ) = = XTC_OK ) ;
CHECK ( mixer - > select_track ( 0 ) & & mixer - > route ( bus ) = = XTC_OK ) ;
CHECK ( xtc_project_get_track ( sequence - > project ( ) , 0 , & track ) = = XTC_OK & & track . bus_id = = bus ) ;
CHECK ( window . undo ( ) = = XTC_OK ) ;
CHECK ( xtc_project_get_track ( sequence - > project ( ) , 0 , & track ) = = XTC_OK & & track . bus_id = = UINT64_MAX ) ;
CHECK ( window . undo ( true ) = = XTC_OK ) ;
CHECK ( xtc_project_get_track ( sequence - > project ( ) , 0 , & track ) = = XTC_OK & & track . bus_id = = bus ) ;
mixer - > peaks ( 1.25f , .5f ) ; mixer - > track_peaks ( .5f , .25f ) ;
CHECK ( mixer - > master_clipped ( ) & & contains ( & window , " dBFS CLIP " ) ) ;
mixer - > peaks ( 0 , 0 ) ; CHECK ( mixer - > master_clipped ( ) ) ;
mixer - > reset_meters ( ) ; CHECK ( ! mixer - > master_clipped ( ) ) ;
window . do_callback ( ) ;
}
2026-10-27 16:37:59 +01:00
{
2026-10-28 10:37:01 +01:00
reset ( ) ; MainWindow window ( false , " " ) ;
2026-10-27 16:37:59 +01:00
auto * sequence = timeline ( & window ) ;
unsigned int bindings [ 3 ] = { 1 , 74 , 11 } , actual [ 3 ] ;
CHECK ( window . set_control_map ( 2 , bindings ) = = XTC_OK ) ;
CHECK ( xtc_project_get_control_map ( sequence - > project ( ) , 2 , actual ) = = XTC_OK & & actual [ 0 ] = = 1 ) ;
CHECK ( window . undo ( false ) = = XTC_OK ) ;
CHECK ( xtc_project_get_control_map ( sequence - > project ( ) , 2 , actual ) = = XTC_OK & & actual [ 0 ] = = 7 ) ;
CHECK ( window . undo ( true ) = = XTC_OK ) ;
const xtc_project * before = sequence - > project ( ) ;
bindings [ 1 ] = 1 ;
CHECK ( window . set_control_map ( 2 , bindings ) = = XTC_ERR_INVALID_ARGUMENT & & sequence - > project ( ) = = before ) ;
auto * mixer_ui = window . mixer ( ) ;
CHECK ( sequence - > add_track ( ) = = XTC_OK ) ;
auto * mode = dynamic_cast < Fl_Choice * > ( find ( & window , " Interpolation " ) ) ; CHECK ( mode ) ;
if ( mode ) mode - > value ( 1 ) ;
CHECK ( mixer_ui - > point ( 0 , .25f ) = = XTC_OK ) ;
xtc_automation_point point { } ;
CHECK ( xtc_project_get_automation ( sequence - > project ( ) , 0 , & point ) = = XTC_OK & & point . step = = 1 ) ;
CHECK ( window . save_project ( " /tmp/xtc-gui-controls-test.xtc " ) = = XTC_OK ) ;
CHECK ( window . new_project ( ) = = XTC_OK & & window . open_project ( " /tmp/xtc-gui-controls-test.xtc " ) = = XTC_OK ) ;
CHECK ( xtc_project_get_control_map ( sequence - > project ( ) , 2 , actual ) = = XTC_OK & & actual [ 0 ] = = 1 & & actual [ 1 ] = = 74 ) ;
CHECK ( xtc_project_get_automation ( sequence - > project ( ) , 0 , & point ) = = XTC_OK & & point . step = = 1 ) ;
unlink ( " /tmp/xtc-gui-controls-test.xtc " ) ; window . do_callback ( ) ;
}
2026-10-27 16:49:30 +01:00
{
2026-10-28 10:37:01 +01:00
reset ( ) ; MainWindow window ( false , " " ) ;
2026-10-27 16:49:30 +01:00
auto * sequence = timeline ( & window ) ; auto * canvas = graph_canvas ( & window ) ;
CHECK ( sequence - > add_track ( ) = = XTC_OK & & sequence - > add_midi_clip ( ) = = XTC_OK ) ;
const uint64_t track = sequence - > selected_track ( ) , clip = sequence - > selected_clip ( ) ;
CHECK ( window . open_piano_roll ( ) = = XTC_OK ) ;
CHECK ( window . piano_roll ( ) - > add_note ( 0 , 240 , 60 ) = = XTC_OK ) ;
const uint64_t note = window . piano_roll ( ) - > selected_note ( ) ;
CHECK ( window . piano_roll ( ) - > add_note ( 240 , 240 , 64 ) = = XTC_OK ) ;
CHECK ( window . piano_roll ( ) - > select_note ( note ) ) ;
CHECK ( window . undo ( false ) = = XTC_OK ) ;
CHECK ( sequence - > selected_track ( ) = = track & & sequence - > selected_clip ( ) = = clip ) ;
CHECK ( window . piano_roll ( ) - > clip ( ) = = clip & & window . piano_roll ( ) - > selected_note ( ) = = note ) ;
const xtc_project * before = sequence - > project ( ) ; unsigned int bindings [ 3 ] = { 1 , 74 , 11 } ;
unsigned int old_submissions = submissions ;
reserve_result = XTC_ERR_OUT_OF_MEMORY ;
CHECK ( canvas - > add_node ( XTC_NODE_GAIN ) = = XTC_ERR_OUT_OF_MEMORY & & sequence - > project ( ) = = before ) ;
CHECK ( window . set_control_map ( 0 , bindings ) = = XTC_ERR_OUT_OF_MEMORY & & sequence - > project ( ) = = before ) ;
CHECK ( window . import_audio ( " /absent/not-loaded.wav " ) = = XTC_ERR_OUT_OF_MEMORY & & sequence - > project ( ) = = before ) ;
CHECK ( submissions = = old_submissions ) ;
reserve_result = XTC_OK ;
CHECK ( window . undo ( true ) = = XTC_OK ) ; // Failed commands preserved redo.
size_t count = 0 ;
CHECK ( xtc_project_note_count ( sequence - > project ( ) , & count ) = = XTC_OK & & count = = 2 ) ;
CHECK ( canvas - > add_node ( XTC_NODE_GAIN ) = = XTC_OK ) ;
const size_t node = canvas - > selected_node ( ) ; int left , top ;
CHECK ( canvas - > node_position ( node , left , top ) & & canvas - > select_at ( left + 50 , top + 30 ) & & canvas - > selected_node ( ) = = node ) ;
CHECK ( ! canvas - > select_at ( - 20 , - 20 ) & & canvas - > selected_node ( ) = = SIZE_MAX ) ;
window . do_callback ( ) ;
}
2026-11-05 23:44:48 +01:00
// Internal devices must work without plugin fixture environment variables.
for ( unsigned int instrument = 0 ; instrument < 2 ; + + instrument ) {
reset ( ) ; MainWindow window ( false , " " ) ; window . show ( ) ;
CHECK ( window . create_track ( true ) = = XTC_OK ) ;
auto * sequence = timeline ( & window ) ; auto * canvas = graph_canvas ( & window ) ;
uint64_t track = sequence - > selected_track ( ) ;
auto * selector = sequence - > instrument_choice ( track ) ; CHECK ( selector ) ;
selector - > value ( static_cast < int > ( instrument + 1 ) ) ; selector - > do_callback ( ) ;
xtc_plugin_spec spec { } ;
CHECK ( xtc_project_get_instrument ( sequence - > project ( ) , track , & spec ) = = XTC_OK & & spec . index = = instrument ) ;
CHECK ( window . undo ( ) = = XTC_OK & & window . undo ( true ) = = XTC_OK ) ;
2026-11-05 23:47:54 +01:00
spec . values [ 1 ] = 25 ; spec . point_count = 1 ; spec . points [ 0 ] = { 100 , 0 , 0 , .4f } ;
CHECK ( window . set_instrument_spec ( track , spec ) = = XTC_OK ) ;
CHECK ( window . undo ( ) = = XTC_OK & & window . undo ( true ) = = XTC_OK ) ;
CHECK ( xtc_project_get_instrument ( sequence - > project ( ) , track , & spec ) = = XTC_OK & & spec . values [ 1 ] = = 25 & & spec . point_count = = 1 ) ;
auto * bar = dynamic_cast < Fl_Menu_Bar * > ( window . child ( 0 ) ) ; CHECK ( bar ) ;
Fl : : add_timeout ( .05 , [ ] ( void * ) { press ( find ( Fl : : first_window ( ) , " Apply " ) ) ; } ) ;
bar - > picked ( bar - > find_item ( " Edit/Selected instrument controls " ) ) ;
2026-11-05 23:44:48 +01:00
xtc_connection_info wire { } ;
CHECK ( xtc_graph_get_connection ( xtc_project_graph ( sequence - > project ( ) ) , 0 , & wire ) = = XTC_OK ) ;
unsigned int effect = instrument + 2 ; size_t id = SIZE_MAX ;
CHECK ( window . insert_plugin ( wire , XTC_BUILTIN_PATH , effect , & id ) = = XTC_OK ) ;
CHECK ( canvas - > select_node ( id ) ) ;
CHECK ( window . set_plugin_control ( id , 2 , .75f ) = = XTC_OK ) ;
CHECK ( window . set_plugin_automation ( id , { 960 , 2 , 0 , .4f } , false ) = = XTC_OK ) ;
CHECK ( window . undo ( ) = = XTC_OK & & window . undo ( true ) = = XTC_OK ) ;
CHECK ( xtc_graph_get_plugin ( xtc_project_graph ( sequence - > project ( ) ) , id , & spec ) = = XTC_OK ) ;
CHECK ( spec . index = = effect & & spec . values [ 2 ] = = .75f & & spec . point_count = = 1 ) ;
xtc_plugin_details details { } ; xtc_plugin_spec edited { } ;
CHECK ( xtc_plugin_describe ( & spec , 48000 , & details ) = = XTC_OK ) ;
Fl : : add_timeout ( .05 , [ ] ( void * ) { press ( find ( Fl : : first_window ( ) , " Apply " ) ) ; } ) ;
CHECK ( xtc_edit_plugin ( spec , details , edited ) & & edited . index = = effect ) ;
CHECK ( window . save_project ( " /tmp/xtc-internal-gui.xtc " ) = = XTC_OK ) ;
CHECK ( window . new_project ( ) = = XTC_OK & & window . open_project ( " /tmp/xtc-internal-gui.xtc " ) = = XTC_OK ) ;
CHECK ( xtc_project_get_instrument ( sequence - > project ( ) , track , & spec ) = = XTC_OK & & spec . index = = instrument ) ;
CHECK ( xtc_graph_get_plugin ( xtc_project_graph ( sequence - > project ( ) ) , id , & spec ) = = XTC_OK & & spec . index = = effect & & spec . point_count = = 1 ) ;
2026-11-05 23:50:14 +01:00
char sample [ 1024 ] ; CHECK ( getcwd ( sample , sizeof ( sample ) ) ! = nullptr ) ; std : : strcat ( sample , " /examples/projects/original-pluck.wav " ) ;
CHECK ( window . load_sampler ( track , sample ) = = XTC_OK ) ;
CHECK ( xtc_project_get_instrument ( sequence - > project ( ) , track , & spec ) = = XTC_OK & & spec . index = = 4 & & ! std : : strcmp ( spec . sample_path , sample ) ) ;
CHECK ( window . save_project ( " /tmp/xtc-sampler-gui.xtc " ) = = XTC_OK ) ;
CHECK ( window . new_project ( ) = = XTC_OK & & window . open_project ( " /tmp/xtc-sampler-gui.xtc " ) = = XTC_OK ) ;
CHECK ( window . load_sampler ( track , " /absent/sample.wav " ) = = XTC_OK ) ;
CHECK ( window . load_sampler ( track , sample ) = = XTC_OK ) ; // Relink is one prepared history edit.
CHECK ( window . undo ( ) = = XTC_OK & & window . undo ( true ) = = XTC_OK ) ; unlink ( " /tmp/xtc-sampler-gui.xtc " ) ;
2026-11-05 23:44:48 +01:00
size_t added = SIZE_MAX ; CHECK ( window . add_plugin ( XTC_BUILTIN_PATH , effect , & added ) = = XTC_OK ) ;
CHECK ( added ! = id ) ; CHECK ( window . undo ( ) = = XTC_OK ) ;
2026-11-05 23:53:03 +01:00
unsigned int stereo = instrument + 5 ;
CHECK ( window . add_plugin ( XTC_BUILTIN_PATH , stereo , & added ) = = XTC_OK ) ;
CHECK ( window . set_plugin_control ( added , stereo = = 5 ? 3U : 4U , 1 ) = = XTC_OK ) ;
CHECK ( window . save_project ( " /tmp/xtc-stereo-gui.xtc " ) = = XTC_OK ) ;
CHECK ( window . new_project ( ) = = XTC_OK & & window . open_project ( " /tmp/xtc-stereo-gui.xtc " ) = = XTC_OK ) ;
CHECK ( xtc_graph_get_plugin ( xtc_project_graph ( sequence - > project ( ) ) , added , & spec ) = = XTC_OK & & spec . index = = stereo ) ;
unlink ( " /tmp/xtc-stereo-gui.xtc " ) ;
2026-11-05 23:44:48 +01:00
const xtc_project * before = sequence - > project ( ) ; reserve_result = XTC_ERR_OUT_OF_MEMORY ;
CHECK ( window . add_plugin ( XTC_BUILTIN_PATH , effect ) = = XTC_ERR_OUT_OF_MEMORY & & sequence - > project ( ) = = before ) ;
reserve_result = XTC_OK ; unlink ( " /tmp/xtc-internal-gui.xtc " ) ; window . do_callback ( ) ;
}
2026-10-27 04:00:27 +01:00
if ( const char * module = std : : getenv ( " XTC_TEST_PLUGIN " ) ) {
reset ( ) ;
2026-10-28 10:37:01 +01:00
MainWindow window ( false , " " ) ;
2026-10-27 04:00:27 +01:00
auto * sequence = timeline ( & window ) ;
size_t id = SIZE_MAX ;
CHECK ( window . add_plugin ( module , 0 , & id ) = = XTC_OK ) ;
xtc_plugin_spec spec { } ;
2026-10-27 16:24:21 +01:00
// Exercise the actual modal chooser/editor with copied metadata only.
Fl : : add_timeout ( .05 , [ ] ( void * ) {
auto * dialog = Fl : : first_window ( ) ;
2026-10-27 17:34:46 +01:00
CHECK ( dialog & & dialog - > label ( ) & & std : : strcmp ( dialog - > label ( ) , " Plugin descriptors " ) = = 0 ) ;
2026-10-27 16:24:21 +01:00
if ( ! dialog ) return ;
for ( int i = 0 ; i < dialog - > children ( ) ; + + i )
if ( auto * list = dynamic_cast < Fl_Hold_Browser * > ( dialog - > child ( i ) ) ) {
CHECK ( list - > size ( ) = = 7 ) ; list - > select ( 2 ) ;
}
auto * choose = find ( dialog , " Choose " ) ; CHECK ( choose ) ; if ( choose ) choose - > do_callback ( ) ;
} ) ;
unsigned int chosen = UINT32_MAX ;
CHECK ( xtc_choose_plugin ( module , 48000 , chosen ) & & chosen = = 1 ) ;
xtc_plugin_details details { } ; xtc_plugin_spec edited { } ;
CHECK ( xtc_graph_get_plugin ( xtc_project_graph ( sequence - > project ( ) ) , id , & spec ) = = XTC_OK ) ;
CHECK ( xtc_plugin_describe ( & spec , 48000 , & details ) = = XTC_OK ) ;
spec . values [ 0 ] = .25f ; spec . point_count = 1 ; spec . points [ 0 ] = { 0 , spec . ports [ 0 ] , 0 , .5f } ;
Fl : : add_timeout ( .05 , [ ] ( void * ) {
auto * dialog = Fl : : first_window ( ) ; CHECK ( dialog ) ;
CHECK ( contains ( dialog , " Test gain " ) ) ;
auto * reset = find ( dialog , " Reset defaults " ) ; CHECK ( reset ) ; if ( reset ) reset - > do_callback ( ) ;
auto * apply = find ( dialog , " Apply " ) ; CHECK ( apply ) ; if ( apply ) apply - > do_callback ( ) ;
} ) ;
CHECK ( xtc_edit_plugin ( spec , details , edited ) & & edited . values [ 0 ] = = 1 & & ! edited . point_count ) ;
edited . values [ 0 ] = .75f ;
Fl : : add_timeout ( .05 , [ ] ( void * ) {
auto * dialog = Fl : : first_window ( ) ; auto * cancel = find ( dialog , " Cancel " ) ;
CHECK ( cancel ) ; if ( cancel ) cancel - > do_callback ( ) ;
} ) ;
CHECK ( ! xtc_edit_plugin ( spec , details , edited ) & & edited . values [ 0 ] = = .75f ) ;
2026-10-27 04:00:27 +01:00
CHECK ( xtc_graph_get_plugin ( xtc_project_graph ( sequence - > project ( ) ) , id , & spec ) = = XTC_OK ) ;
CHECK ( window . set_plugin_control ( id , spec . ports [ 0 ] , .5f ) = = XTC_OK ) ;
CHECK ( xtc_graph_get_plugin ( xtc_project_graph ( sequence - > project ( ) ) , id , & spec ) = = XTC_OK & & spec . values [ 0 ] = = .5f ) ;
CHECK ( window . set_plugin_control ( id , spec . ports [ 0 ] , 3 ) = = XTC_ERR_UNSUPPORTED ) ;
CHECK ( xtc_graph_get_plugin ( xtc_project_graph ( sequence - > project ( ) ) , id , & spec ) = = XTC_OK & & spec . values [ 0 ] = = .5f ) ;
CHECK ( window . undo ( false ) = = XTC_OK ) ;
CHECK ( xtc_graph_get_plugin ( xtc_project_graph ( sequence - > project ( ) ) , id , & spec ) = = XTC_OK & & spec . values [ 0 ] = = 1 ) ;
CHECK ( window . undo ( true ) = = XTC_OK ) ;
CHECK ( xtc_graph_get_plugin ( xtc_project_graph ( sequence - > project ( ) ) , id , & spec ) = = XTC_OK & & spec . values [ 0 ] = = .5f ) ;
const xtc_project * before = sequence - > project ( ) ;
CHECK ( window . add_plugin ( " /absent/plugin.so " , 0 ) = = XTC_ERR_NOT_FOUND & & sequence - > project ( ) = = before ) ;
CHECK ( window . add_plugin ( module , 2 ) = = XTC_ERR_UNSUPPORTED & & sequence - > project ( ) = = before ) ;
2026-10-27 06:25:30 +01:00
xtc_plugin_point point { 960 , spec . ports [ 0 ] , 0 , .25f } ;
CHECK ( window . set_plugin_automation ( id , point ) = = XTC_OK ) ;
CHECK ( window . set_plugin_automation ( id , point , true ) = = XTC_OK ) ;
CHECK ( window . undo ( false ) = = XTC_OK ) ;
2026-10-27 04:00:27 +01:00
CHECK ( window . save_project ( " /tmp/xtc-gui-plugin-test.xtc " ) = = XTC_OK ) ;
CHECK ( window . new_project ( ) = = XTC_OK ) ;
CHECK ( window . open_project ( " /tmp/xtc-gui-plugin-test.xtc " ) = = XTC_OK ) ;
CHECK ( xtc_graph_get_plugin ( xtc_project_graph ( sequence - > project ( ) ) , id , & spec ) = = XTC_OK & & spec . values [ 0 ] = = .5f ) ;
2026-10-27 06:25:30 +01:00
CHECK ( spec . point_count = = 1 & & spec . points [ 0 ] . tick = = 960 & & spec . points [ 0 ] . value = = .25f ) ;
2026-10-27 16:24:21 +01:00
CHECK ( window . reset_plugin ( id ) = = XTC_OK ) ;
CHECK ( xtc_graph_get_plugin ( xtc_project_graph ( sequence - > project ( ) ) , id , & spec ) = = XTC_OK & & spec . values [ 0 ] = = 1 & & ! spec . point_count ) ;
CHECK ( window . undo ( false ) = = XTC_OK ) ;
CHECK ( xtc_graph_get_plugin ( xtc_project_graph ( sequence - > project ( ) ) , id , & spec ) = = XTC_OK & & spec . values [ 0 ] = = .5f & & spec . point_count = = 1 ) ;
// Replace an effect while preserving node ID and wires; undo restores state.
auto * canvas = graph_canvas ( & window ) ;
CHECK ( canvas - > add_node ( XTC_NODE_OSCILLATOR ) = = XTC_OK ) ;
const size_t source_id = canvas - > selected_node ( ) ;
CHECK ( canvas - > connect_ports ( { source_id , 1 , id , 0 } ) = = XTC_OK ) ;
CHECK ( window . replace_plugin ( id , module , 4 ) = = XTC_OK ) ;
CHECK ( xtc_graph_get_plugin ( xtc_project_graph ( sequence - > project ( ) ) , id , & spec ) = = XTC_OK & & spec . index = = 4 & & spec . values [ 0 ] = = 2 & & ! spec . point_count ) ;
before = sequence - > project ( ) ;
CHECK ( window . replace_plugin ( id , module , 1 ) = = XTC_ERR_UNSUPPORTED & & sequence - > project ( ) = = before ) ;
CHECK ( window . replace_plugin ( id , module , 3 ) = = XTC_ERR_INVALID_STATE & & sequence - > project ( ) = = before ) ;
CHECK ( window . replace_plugin ( id , " /absent/plugin.so " , 0 ) = = XTC_ERR_NOT_FOUND & & sequence - > project ( ) = = before ) ;
CHECK ( window . undo ( false ) = = XTC_OK ) ;
CHECK ( xtc_graph_get_plugin ( xtc_project_graph ( sequence - > project ( ) ) , id , & spec ) = = XTC_OK & & spec . index = = 0 & & spec . values [ 0 ] = = .5f & & spec . point_count = = 1 ) ;
const xtc_connection_info wire { source_id , 1 , id , 0 } ; size_t inserted = SIZE_MAX ;
CHECK ( window . insert_plugin ( wire , module , 0 , & inserted ) = = XTC_OK & & inserted ! = id ) ;
xtc_routing_info routing { } ;
CHECK ( canvas - > routing ( inserted , routing ) & & routing . inputs = = 1 & & routing . outputs = = 1 ) ;
before = sequence - > project ( ) ;
CHECK ( window . insert_plugin ( wire , module , 0 ) = = XTC_ERR_NOT_FOUND & & sequence - > project ( ) = = before ) ;
CHECK ( canvas - > select_node ( inserted ) & & canvas - > delete_selected ( ) = = XTC_OK ) ;
CHECK ( xtc_graph_get_plugin ( xtc_project_graph ( sequence - > project ( ) ) , inserted , & edited ) = = XTC_ERR_NOT_FOUND ) ;
CHECK ( window . undo ( false ) = = XTC_OK ) ;
CHECK ( xtc_graph_get_plugin ( xtc_project_graph ( sequence - > project ( ) ) , inserted , & edited ) = = XTC_OK ) ;
2026-10-27 04:00:27 +01:00
xtc_project * missing = nullptr ; xtc_graph * graph = nullptr ; size_t absent ;
CHECK ( xtc_project_create ( & missing ) = = XTC_OK ) ;
CHECK ( xtc_graph_create ( 8 , & graph ) = = XTC_OK ) ;
std : : strcpy ( spec . path , " /absent/xtc-test-plugin.so " ) ;
CHECK ( xtc_graph_add_plugin ( graph , & spec , & absent ) = = XTC_OK ) ;
CHECK ( xtc_project_set_graph ( missing , graph ) = = XTC_OK ) ;
CHECK ( xtc_project_save ( missing , " /tmp/xtc-gui-missing-plugin.xtc " ) = = XTC_OK ) ;
before = sequence - > project ( ) ;
CHECK ( window . open_project ( " /tmp/xtc-gui-missing-plugin.xtc " ) = = XTC_ERR_NOT_FOUND & & sequence - > project ( ) = = before ) ;
CHECK ( contains ( & window , " /absent/xtc-test-plugin.so " ) ) ;
CHECK ( window . add_plugin ( module , 3 ) = = XTC_ERR_INVALID_STATE & & sequence - > project ( ) = = before ) ;
xtc_project_destroy ( missing ) ; xtc_graph_destroy ( graph ) ;
unlink ( " /tmp/xtc-gui-missing-plugin.xtc " ) ;
unlink ( " /tmp/xtc-gui-plugin-test.xtc " ) ;
window . do_callback ( ) ;
}
2026-10-27 17:34:46 +01:00
for ( const char * variable : { " XTC_TEST_CLAP " , " XTC_TEST_VST2 " , " XTC_TEST_VST3 " } ) {
if ( const char * module = std : : getenv ( variable ) ) {
2026-10-28 10:37:01 +01:00
reset ( ) ; MainWindow window ( false , " " ) ; auto * sequence = timeline ( & window ) ;
2026-10-27 17:34:46 +01:00
size_t id = SIZE_MAX ; xtc_plugin_spec spec { } ;
CHECK ( window . add_plugin ( module , 0 , & id ) = = XTC_OK ) ;
CHECK ( xtc_graph_get_plugin ( xtc_project_graph ( sequence - > project ( ) ) , id , & spec ) = = XTC_OK ) ;
CHECK ( spec . format ! = XTC_PLUGIN_LADSPA & & spec . inputs = = 1 & & ! spec . control_count ) ;
CHECK ( window . undo ( false ) = = XTC_OK & & window . undo ( true ) = = XTC_OK ) ;
Fl : : add_timeout ( .05 , [ ] ( void * ) {
auto * dialog = Fl : : first_window ( ) ; CHECK ( dialog ) ;
auto * choose = find ( dialog , " Choose " ) ; CHECK ( choose ) ; if ( choose ) choose - > do_callback ( ) ;
} ) ;
unsigned int chosen = UINT32_MAX ;
CHECK ( xtc_choose_plugin ( module , 48000 , chosen ) & & chosen = = 0 ) ;
CHECK ( window . save_project ( " /tmp/xtc-gui-modern-test.xtc " ) = = XTC_OK ) ;
CHECK ( window . new_project ( ) = = XTC_OK ) ;
CHECK ( window . open_project ( " /tmp/xtc-gui-modern-test.xtc " ) = = XTC_OK ) ;
xtc_plugin_spec loaded { } ;
CHECK ( xtc_graph_get_plugin ( xtc_project_graph ( sequence - > project ( ) ) , id , & loaded ) = = XTC_OK & & loaded . format = = spec . format ) ;
const xtc_project * before = sequence - > project ( ) ;
CHECK ( window . replace_plugin ( id , " /absent/modern.so " , 0 ) = = XTC_ERR_NOT_FOUND & & sequence - > project ( ) = = before ) ;
unlink ( " /tmp/xtc-gui-modern-test.xtc " ) ; window . do_callback ( ) ;
}
}
2026-10-27 17:51:48 +01:00
for ( const char * variable : { " XTC_TEST_SYNTH_CLAP " , " XTC_TEST_SYNTH_VST2 " , " XTC_TEST_SYNTH_VST3 " } ) {
if ( const char * module = std : : getenv ( variable ) ) {
2026-10-28 10:37:01 +01:00
reset ( ) ; MainWindow window ( false , " " ) ; auto * sequence = timeline ( & window ) ;
2026-10-27 17:51:48 +01:00
CHECK ( sequence - > add_track ( ) = = XTC_OK & & sequence - > add_midi_clip ( ) = = XTC_OK ) ;
uint64_t track = sequence - > selected_track ( ) ; xtc_plugin_spec spec { } ;
CHECK ( window . assign_instrument ( track , module , 0 ) = = XTC_OK ) ;
CHECK ( xtc_project_get_instrument ( sequence - > project ( ) , track , & spec ) = = XTC_OK & & spec . instrument ) ;
CHECK ( window . undo ( false ) = = XTC_OK ) ;
CHECK ( xtc_project_get_instrument ( sequence - > project ( ) , track , & spec ) = = XTC_ERR_NOT_FOUND ) ;
CHECK ( window . undo ( true ) = = XTC_OK ) ;
CHECK ( window . select_live_instrument ( UINT64_MAX ) = = XTC_OK ) ;
CHECK ( window . select_live_instrument ( track ) = = XTC_OK ) ;
const xtc_project * before = sequence - > project ( ) ;
CHECK ( window . assign_instrument ( track , " /absent/instrument.so " , 0 ) = = XTC_ERR_NOT_FOUND & & sequence - > project ( ) = = before ) ;
CHECK ( window . save_project ( " /tmp/xtc-gui-instrument.xtc " ) = = XTC_OK ) ;
CHECK ( window . new_project ( ) = = XTC_OK & & window . open_project ( " /tmp/xtc-gui-instrument.xtc " ) = = XTC_OK ) ;
CHECK ( xtc_project_get_instrument ( sequence - > project ( ) , track , & spec ) = = XTC_OK & & spec . instrument ) ;
2026-10-28 10:08:13 +01:00
CHECK ( window . create_track ( true ) = = XTC_OK ) ;
uint64_t second = sequence - > selected_track ( ) ;
auto * selector = sequence - > instrument_choice ( second ) ; CHECK ( selector & & selector - > value ( ) = = 0 ) ;
2026-10-28 17:16:01 +01:00
CHECK ( selector - > size ( ) = = 6 ) ; // Sine, two internal presets, project instrument, load, terminator.
2026-10-28 10:08:13 +01:00
selector - > value ( 1 ) ; selector - > do_callback ( ) ;
CHECK ( xtc_project_get_instrument ( sequence - > project ( ) , second , & spec ) = = XTC_OK & & spec . instrument ) ;
CHECK ( sequence - > selected_track ( ) = = second ) ;
CHECK ( sequence - > move_track ( - 1 ) = = XTC_OK ) ;
selector = sequence - > instrument_choice ( second ) ; selector - > value ( 0 ) ; selector - > do_callback ( ) ;
CHECK ( xtc_project_get_instrument ( sequence - > project ( ) , second , & spec ) = = XTC_ERR_NOT_FOUND ) ;
CHECK ( xtc_project_get_instrument ( sequence - > project ( ) , track , & spec ) = = XTC_OK ) ;
CHECK ( window . undo ( false ) = = XTC_OK ) ;
CHECK ( xtc_project_get_instrument ( sequence - > project ( ) , second , & spec ) = = XTC_OK ) ;
2026-10-27 17:51:48 +01:00
CHECK ( window . assign_instrument ( track , nullptr , 0 ) = = XTC_OK ) ;
unlink ( " /tmp/xtc-gui-instrument.xtc " ) ; window . do_callback ( ) ;
}
}
2026-10-27 18:04:39 +01:00
// Flat workspace creation is one history edit, and failed preparation keeps
// the current model. Exercise real sidebar buttons rather than mock actions.
reset ( ) ;
{
2026-10-28 10:37:01 +01:00
MainWindow window ( false , " " ) ; window . show ( ) ;
2026-10-27 18:04:39 +01:00
auto * sequence = timeline ( & window ) ;
CHECK ( sequence ! = nullptr ) ;
auto * midi = find ( & window , " New MIDI track " ) ;
auto * audio = find ( & window , " New audio track " ) ;
CHECK ( midi & & audio & & find ( & window , " Import WAV " ) & & find ( & window , " Edit MIDI notes " ) ) ;
press ( midi ) ;
size_t tracks = 0 , clips = 0 ;
xtc_project_get_counts ( sequence - > project ( ) , & tracks , & clips ) ;
CHECK ( tracks = = 1 & & clips = = 1 ) ;
uint64_t track = sequence - > selected_track ( ) , clip = sequence - > selected_clip ( ) ;
xtc_clip_info info { } ; CHECK ( xtc_project_find_clip ( sequence - > project ( ) , clip , & info ) = = XTC_OK & & info . midi ) ;
CHECK ( window . mixer ( ) - > selected_track ( ) = = track ) ;
CHECK ( window . open_piano_roll ( ) = = XTC_OK ) ;
CHECK ( window . undo ( false ) = = XTC_OK ) ;
xtc_project_get_counts ( sequence - > project ( ) , & tracks , & clips ) ; CHECK ( tracks = = 0 & & clips = = 0 ) ;
CHECK ( window . undo ( true ) = = XTC_OK ) ;
CHECK ( sequence - > select ( track , clip ) ) ; pump ( ) ;
CHECK ( contains ( & window , " MIDI track " ) ) ;
press ( audio ) ;
xtc_project_get_counts ( sequence - > project ( ) , & tracks , & clips ) ; CHECK ( tracks = = 2 & & clips = = 1 ) ;
const xtc_project * before = sequence - > project ( ) ;
reserve_result = XTC_ERR_OUT_OF_MEMORY ;
CHECK ( window . create_track ( true ) = = XTC_ERR_OUT_OF_MEMORY & & sequence - > project ( ) = = before ) ;
reserve_result = XTC_OK ; submit_result = XTC_ERR_INVALID_STATE ;
CHECK ( window . create_track ( false ) = = XTC_ERR_INVALID_STATE & & sequence - > project ( ) = = before ) ;
submit_result = XTC_OK ;
// Exercise the supported minimum and a larger workspace with real layout.
for ( auto size : { std : : pair < int , int > { 1220 , 720 } , { 1600 , 1000 } } ) {
window . resize ( window . x ( ) , window . y ( ) , size . first , size . second ) ; pump ( ) ;
CHECK ( midi - > x ( ) > = 0 & & midi - > x ( ) + midi - > w ( ) < = sequence - > parent ( ) - > x ( ) ) ;
CHECK ( sequence - > parent ( ) - > x ( ) + sequence - > parent ( ) - > w ( ) < = window . w ( ) ) ;
CHECK ( find ( & window , " Play " ) - > visible_r ( ) & & midi - > visible_r ( ) ) ;
}
CHECK ( window . save_project ( " /tmp/xtc-workspace.xtc " ) = = XTC_OK ) ;
CHECK ( window . new_project ( ) = = XTC_OK & & window . open_project ( " /tmp/xtc-workspace.xtc " ) = = XTC_OK ) ;
xtc_project_get_counts ( sequence - > project ( ) , & tracks , & clips ) ; CHECK ( tracks = = 2 & & clips = = 1 ) ;
unlink ( " /tmp/xtc-workspace.xtc " ) ; window . do_callback ( ) ;
}
2026-10-28 10:08:13 +01:00
reset ( ) ;
{
2026-10-28 10:37:01 +01:00
MainWindow window ( false , " " ) ; window . show ( ) ; auto * sequence = timeline ( & window ) ;
2026-10-28 10:08:13 +01:00
CHECK ( window . create_track ( true ) = = XTC_OK ) ;
uint64_t clip = sequence - > selected_clip ( ) , track = sequence - > selected_track ( ) ;
auto * snap = dynamic_cast < Fl_Choice * > ( find ( & window , " Snap " ) ) ; CHECK ( snap & & snap - > value ( ) = = 3 ) ;
CHECK ( ! std : : strcmp ( snap - > text ( 3 ) , " 1/16 " ) & & ! std : : strcmp ( snap - > text ( 8 ) , " 1 bar " ) ) ;
snap - > value ( 5 ) ; snap - > do_callback ( ) ; CHECK ( sequence - > grid ( ) = = 480 ) ;
CHECK ( xtc_project_edit_clip ( sequence - > project ( ) , clip , track , 100 , 3840 , 0 ) = = XTC_OK ) ;
CHECK ( sequence - > rename_selection ( " Off-grid " ) = = XTC_OK ) ;
gesture ( sequence , 205 , 50 , 100 , 0 ) ;
xtc_clip_info info { } ; xtc_project_find_clip ( sequence - > project ( ) , clip , & info ) ;
CHECK ( info . start_tick = = 1920 ) ; // Snap absolute position, including an off-grid source.
snap - > value ( 0 ) ; snap - > do_callback ( ) ; CHECK ( sequence - > grid ( ) = = 1 ) ;
gesture ( sequence , 300 , 50 , 1 , 0 ) ; xtc_project_find_clip ( sequence - > project ( ) , clip , & info ) ;
CHECK ( info . start_tick = = 1940 ) ; // Off retains pointer precision rather than a musical grid.
CHECK ( window . undo ( false ) = = XTC_OK ) ;
CHECK ( sequence - > select ( track , clip ) ) ;
snap - > value ( 5 ) ; snap - > do_callback ( ) ;
gesture ( sequence , 466 , 50 , - 13 , 0 ) ; xtc_project_find_clip ( sequence - > project ( ) , clip , & info ) ;
CHECK ( info . start_tick + info . length_tick = = 5280 ) ;
gesture ( sequence , 277 , 50 , 13 , 0 ) ; xtc_project_find_clip ( sequence - > project ( ) , clip , & info ) ;
CHECK ( info . start_tick = = 2400 & & info . offset_tick = = 480 & & info . length_tick = = 2880 ) ;
int old_x = Fl : : e_x , old_y = Fl : : e_y , old_key = Fl : : e_keysym , old_clicks = Fl : : e_clicks ;
Fl : : e_x = sequence - > x ( ) + 320 ; Fl : : e_y = sequence - > y ( ) + 50 ;
Fl : : e_keysym = FL_Button + FL_LEFT_MOUSE ; Fl : : e_clicks = 1 ;
CHECK ( sequence - > handle ( FL_PUSH ) = = 1 ) ; CHECK ( window . piano_roll ( ) & & window . piano_roll ( ) - > clip ( ) = = clip ) ;
Fl : : e_x = old_x ; Fl : : e_y = old_y ; Fl : : e_keysym = old_key ; Fl : : e_clicks = old_clicks ;
auto * piano = window . piano_roll ( ) ;
CHECK ( piano - > add_note ( 0 , 960 , 60 , 100 , 2 ) = = XTC_OK ) ;
uint64_t note_id = piano - > selected_note ( ) ;
CHECK ( piano - > add_note ( 0 , 960 , 64 , 90 , 2 ) = = XTC_OK ) ; // Coincident notes remain individually editable.
uint64_t chord_id = piano - > selected_note ( ) ;
CHECK ( piano - > select_note ( note_id ) ) ; pump ( ) ;
auto * velocity = dynamic_cast < Fl_Value_Slider * > ( find ( piano - > window ( ) , " Note velocity " ) ) ;
CHECK ( velocity & & velocity - > active ( ) ) ; velocity - > value ( 29 ) ; velocity - > do_callback ( ) ;
xtc_note_info note { } , chord { } ;
for ( size_t i = 0 ; i < 2 ; + + i ) { xtc_note_info n { } ; xtc_project_get_note ( sequence - > project ( ) , i , & n ) ;
if ( n . id = = note_id ) note = n ;
if ( n . id = = chord_id ) chord = n ;
}
CHECK ( note . velocity = = 29 & & note . start_tick = = 0 & & note . length_tick = = 960 & & note . pitch = = 60 & & note . channel = = 2 ) ;
CHECK ( chord . velocity = = 90 ) ;
CHECK ( piano - > set_velocity ( note_id , 0 ) = = XTC_ERR_INVALID_ARGUMENT ) ;
CHECK ( piano - > set_velocity ( note_id , 128 ) = = XTC_ERR_INVALID_ARGUMENT ) ;
CHECK ( piano - > set_velocity ( UINT64_MAX , 40 ) = = XTC_ERR_NOT_FOUND ) ;
auto * lane = window . velocity_lane ( ) ; CHECK ( lane ) ;
gesture ( lane , piano - > note_x ( 0 ) - lane - > x ( ) , 70 , 0 , - 30 ) ;
xtc_project_get_note ( sequence - > project ( ) , 0 , & note ) ; CHECK ( note . id = = note_id & & note . velocity > 29 ) ;
xtc_project_get_note ( sequence - > project ( ) , 1 , & chord ) ; CHECK ( chord . id = = chord_id & & chord . velocity = = 90 ) ;
CHECK ( window . undo ( false ) = = XTC_OK ) ;
xtc_project_get_note ( sequence - > project ( ) , 0 , & note ) ; CHECK ( note . velocity = = 29 ) ;
CHECK ( window . undo ( true ) = = XTC_OK ) ;
CHECK ( window . save_project ( " /tmp/xtc-grid-velocity.xtc " ) = = XTC_OK ) ;
CHECK ( window . new_project ( ) = = XTC_OK & & window . open_project ( " /tmp/xtc-grid-velocity.xtc " ) = = XTC_OK ) ;
xtc_project_get_note ( sequence - > project ( ) , 0 , & note ) ; CHECK ( note . velocity > 29 & & note . pitch = = 60 & & note . channel = = 2 ) ;
unlink ( " /tmp/xtc-grid-velocity.xtc " ) ; window . do_callback ( ) ;
}
2026-10-28 10:37:01 +01:00
{
char dir [ ] = " /tmp/xtc-preferences-XXXXXX " ; CHECK ( mkdtemp ( dir ) ) ;
std : : string path = std : : string ( dir ) + " /.xtc.conf " ;
reset ( ) ;
{
MainWindow window ( false , path . c_str ( ) ) ; window . show ( ) ;
xtc_settings settings = window . settings ( ) ; settings . note_velocity = 73 ; settings . sequencer_snap = 480 ;
CHECK ( window . apply_preferences ( settings ) = = XTC_OK ) ;
CHECK ( timeline ( & window ) - > grid ( ) = = 480 ) ;
int old_state = Fl : : e_state ; Fl : : e_state = FL_SHIFT ;
gesture ( timeline ( & window ) , 228 , 12 , 96 , 0 ) ; Fl : : e_state = old_state ;
CHECK ( window . settings ( ) . loop_start = = 960 & & window . settings ( ) . loop_end = = 2880 ) ;
CHECK ( window . set_loop ( 960 , 3840 , true ) = = XTC_OK ) ;
CHECK ( window . settings ( ) . loop_enabled = = 1 & & window . settings ( ) . loop_start = = 960 ) ;
CHECK ( window . set_loop ( 4000 , 2000 , true ) = = XTC_ERR_INVALID_ARGUMENT ) ;
settings = window . settings ( ) ; CHECK ( window . apply_preferences ( settings ) = = XTC_OK ) ;
2026-11-05 23:55:56 +01:00
settings = window . settings ( ) ; settings . loop_enabled = 0 ; settings . stop_after_tail = 1 ; settings . tail_ms = 0 ;
CHECK ( window . apply_preferences ( settings ) = = XTC_OK ) ;
CHECK ( window . create_track ( true ) = = XTC_OK ) ; press ( find ( & window , " Play " ) ) ;
engine_status . sample_position = 1000000 ; pump ( ) ; CHECK ( ! handle . running ) ;
settings . loop_enabled = 1 ; settings . stop_after_tail = 0 ; CHECK ( window . apply_preferences ( settings ) = = XTC_OK ) ;
2026-10-28 10:37:01 +01:00
settings . note_velocity = 128 ; CHECK ( window . apply_preferences ( settings ) = = XTC_ERR_INVALID_ARGUMENT ) ;
PreferencesDialog dialog ( window . settings ( ) , path , [ & ] ( const xtc_settings & v ) { return window . apply_preferences ( v ) ; } ) ;
auto * input = dynamic_cast < Fl_Input * > ( find ( & dialog , " New note velocity " ) ) ; CHECK ( input ) ;
if ( input ) input - > value ( " 81 " ) ;
press ( find ( & dialog , " Apply " ) ) ; CHECK ( window . settings ( ) . note_velocity = = 81 ) ;
if ( input ) input - > value ( " 82 " ) ;
press ( find ( & dialog , " Close " ) ) ; CHECK ( window . settings ( ) . note_velocity = = 81 ) ;
window . do_callback ( ) ;
}
reset ( ) ;
{
MainWindow window ( false , path . c_str ( ) ) ; CHECK ( window . settings ( ) . note_velocity = = 81 ) ;
CHECK ( window . settings ( ) . loop_start = = 960 & & window . settings ( ) . loop_enabled = = 1 ) ;
xtc_settings paths = window . settings ( ) ;
std : : string binary = std : : string ( dir ) + " /test.clap " ; FILE * f = fopen ( binary . c_str ( ) , " wb " ) ; CHECK ( f ) ; if ( f ) fclose ( f ) ;
std : : snprintf ( paths . clap_paths , sizeof ( paths . clap_paths ) , " %s:%s " , dir , dir ) ;
paths . vst2_paths [ 0 ] = paths . vst3_paths [ 0 ] = paths . ladspa_paths [ 0 ] = 0 ;
auto files = xtc_plugin_files ( paths ) ; CHECK ( files . size ( ) = = 1 & & files [ 0 ] = = binary ) ;
unlink ( binary . c_str ( ) ) ; window . do_callback ( ) ;
}
unlink ( path . c_str ( ) ) ; rmdir ( dir ) ;
}
2026-10-28 11:23:25 +01:00
{
reset ( ) ; MainWindow window ( false , " " ) ; window . show ( ) ; auto * m = window . mixer ( ) ;
CHECK ( m - > strip_count ( ) = = 0 & & m - > strip ( UINT64_MAX ) = = nullptr ) ;
CHECK ( window . create_track ( true ) = = XTC_OK & & window . create_track ( true ) = = XTC_OK ) ;
xtc_track_info a { } , b { } ; xtc_project_get_track ( m - > project ( ) , 0 , & a ) ; xtc_project_get_track ( m - > project ( ) , 1 , & b ) ;
CHECK ( m - > strip_count ( ) = = 2 & & m - > strip ( a . id ) & & m - > strip ( b . id ) ) ;
auto * first = m - > strip ( a . id ) ;
auto * fader = dynamic_cast < Fl_Value_Slider * > ( find ( first , " dB " ) ) ; CHECK ( fader ) ;
CHECK ( fader & & fader - > type ( ) = = FL_VERT_SLIDER & & fader - > h ( ) > fader - > w ( ) ) ;
if ( fader ) { fader - > value ( - 6 ) ; fader - > do_callback ( ) ; }
xtc_project_get_track ( m - > project ( ) , 0 , & a ) ; xtc_project_get_track ( m - > project ( ) , 1 , & b ) ;
CHECK ( std : : fabs ( a . gain - std : : pow ( 10.f , - 6.f / 20 ) ) < .0001f & & b . gain = = 1 ) ;
CHECK ( first = = m - > strip ( a . id ) ) ; // Editing must not delete its callback's widget.
if ( fader ) { gesture ( fader , 20 , 40 , 0 , 30 ) ; xtc_project_get_track ( m - > project ( ) , 0 , & a ) ; CHECK ( a . gain < .5f ) ; CHECK ( window . undo ( ) = = XTC_OK ) ; }
if ( fader ) {
reserve_result = XTC_ERR_OUT_OF_MEMORY ;
Fl : : add_timeout ( .05 , [ ] ( void * ) { auto * dialog = Fl : : first_window ( ) ; CHECK ( dialog & & contains ( dialog , " Mixer edit failed " ) ) ; if ( dialog ) dialog - > hide ( ) ; } ) ;
fader - > value ( - 12 ) ; fader - > do_callback ( ) ; reserve_result = XTC_OK ;
xtc_project_get_track ( m - > project ( ) , 0 , & a ) ; CHECK ( std : : fabs ( a . gain - std : : pow ( 10.f , - 6.f / 20 ) ) < .0001f ) ;
CHECK ( std : : fabs ( fader - > value ( ) + 6 ) < .001 ) ;
}
press ( find ( m - > strip ( b . id ) , b . name ) ) ; CHECK ( m - > selected_track ( ) = = b . id ) ;
CHECK ( window . undo ( ) = = XTC_OK ) ; xtc_project_get_track ( m - > project ( ) , 0 , & a ) ; CHECK ( a . gain = = 1 ) ;
CHECK ( window . undo ( true ) = = XTC_OK ) ; xtc_project_get_track ( m - > project ( ) , 0 , & a ) ; CHECK ( a . gain < .51f ) ;
Fl_Dial * pan = nullptr ; for ( int i = 0 ; i < m - > strip ( b . id ) - > children ( ) ; + + i ) if ( auto * d = dynamic_cast < Fl_Dial * > ( m - > strip ( b . id ) - > child ( i ) ) ) pan = d ;
CHECK ( pan ) ; if ( pan ) { pan - > value ( - .75 ) ; pan - > do_callback ( ) ; }
xtc_project_get_track ( m - > project ( ) , 1 , & b ) ; CHECK ( b . pan = = - .75f & & b . gain = = 1 ) ;
auto * mute = dynamic_cast < Fl_Check_Button * > ( find ( m - > strip ( b . id ) , " Mute " ) ) ; CHECK ( mute ) ;
if ( mute ) { mute - > value ( 1 ) ; mute - > do_callback ( ) ; } xtc_project_get_track ( m - > project ( ) , 1 , & b ) ; CHECK ( b . mute & & b . pan = = - .75f ) ;
if ( pan ) { int old = Fl : : e_clicks ; Fl : : e_clicks = 1 ; gesture ( pan , 20 , 20 , 0 , 0 ) ; Fl : : e_clicks = old ; }
xtc_project_get_track ( m - > project ( ) , 1 , & b ) ; CHECK ( b . pan = = 0 & & b . mute ) ;
CHECK ( m - > add_bus ( ) = = XTC_OK ) ; uint64_t bus = m - > selected_track ( ) ; CHECK ( m - > strip_count ( ) = = 3 & & contains ( m - > strip ( bus ) , " Bus: " ) ) ;
Fl_Choice * route = nullptr ; for ( int i = 0 ; i < m - > strip ( a . id ) - > children ( ) ; + + i ) if ( auto * c = dynamic_cast < Fl_Choice * > ( m - > strip ( a . id ) - > child ( i ) ) ) route = c ;
CHECK ( route & & route - > size ( ) = = 3 ) ; // Master, bus, terminator.
if ( route ) { route - > value ( 1 ) ; route - > do_callback ( ) ; } xtc_project_get_track ( m - > project ( ) , 0 , & a ) ; CHECK ( a . bus_id = = bus ) ;
CHECK ( window . undo ( ) = = XTC_OK ) ; xtc_project_get_track ( m - > project ( ) , 0 , & a ) ; CHECK ( a . bus_id = = UINT64_MAX ) ;
CHECK ( m - > add_bus ( ) = = XTC_OK ) ; CHECK ( m - > strip_count ( ) = = 4 ) ;
auto * routes = dynamic_cast < Fl_Choice * > ( find ( m , " Route " ) ) ; CHECK ( routes & & routes - > size ( ) = = 4 ) ;
CHECK ( window . undo ( ) = = XTC_OK ) ; CHECK ( m - > strip_count ( ) = = 3 ) ;
xtc_meter_snapshot snapshot { } ; snapshot . count = 2 ; snapshot . tracks [ 0 ] = { a . id , .5f , .25f } ; snapshot . tracks [ 1 ] = { b . id , 1.2f , .125f } ;
m - > meters ( snapshot ) ; CHECK ( contains ( m - > strip ( a . id ) , " L -6.0 " ) & & contains ( m - > strip ( b . id ) , " CLIP " ) ) ;
m - > select_track ( a . id ) ; CHECK ( contains ( m - > strip ( b . id ) , " CLIP " ) ) ; m - > meters ( xtc_meter_snapshot { } ) ; CHECK ( contains ( m - > strip ( b . id ) , " CLIP " ) ) ;
m - > reset_meters ( ) ; CHECK ( ! contains ( m - > strip ( b . id ) , " CLIP " ) ) ;
CHECK ( window . save_project ( " /tmp/xtc-mixer-strips.xtc " ) = = XTC_OK & & window . open_project ( " /tmp/xtc-mixer-strips.xtc " ) = = XTC_OK ) ;
CHECK ( m - > strip_count ( ) = = 3 ) ; xtc_project_get_track ( m - > project ( ) , 1 , & b ) ; CHECK ( b . pan = = 0 & & b . mute ) ;
CHECK ( m - > select_track ( b . id ) & & m - > mix ( .00001f , 0 , 0 , 0 ) = = XTC_OK ) ;
mute = dynamic_cast < Fl_Check_Button * > ( find ( m - > strip ( b . id ) , " Mute " ) ) ; CHECK ( mute ) ;
if ( mute ) { mute - > value ( 1 ) ; mute - > do_callback ( ) ; } xtc_project_get_track ( m - > project ( ) , 1 , & b ) ; CHECK ( b . gain = = .00001f & & b . mute ) ;
unlink ( " /tmp/xtc-mixer-strips.xtc " ) ; window . do_callback ( ) ;
}
{
xtc_project * project = nullptr ; CHECK ( xtc_project_create ( & project ) = = XTC_OK ) ;
uint64_t first = UINT64_MAX , last = UINT64_MAX ;
for ( size_t i = 0 ; i < XTC_PROJECT_MAX_TRACKS ; + + i ) { uint64_t id ; char name [ 40 ] ; std : : snprintf ( name , sizeof ( name ) , " Duplicate/name & _track " ) ;
CHECK ( xtc_project_add_track ( project , name , & id ) = = XTC_OK ) ; if ( i = = 0 ) first = id ; last = id ; }
{
Fl_Double_Window window ( 1100 , 600 ) ; window . begin ( ) ; MixerWidget m ( 0 , 0 , 1032 , 522 , project , nullptr , nullptr ) ; window . end ( ) ; window . show ( ) ;
CHECK ( m . strip_count ( ) = = XTC_PROJECT_MAX_TRACKS & & m . select_track ( last ) ) ;
auto * track_choice = dynamic_cast < Fl_Choice * > ( find ( & m , " Track " ) ) ;
CHECK ( track_choice & & track_choice - > size ( ) = = static_cast < int > ( XTC_PROJECT_MAX_TRACKS ) + 1 ) ;
CHECK ( track_choice & & track_choice - > value ( ) = = static_cast < int > ( XTC_PROJECT_MAX_TRACKS ) - 1 ) ;
CHECK ( m . strip ( last ) - > x ( ) > = m . x ( ) & & m . strip ( last ) - > x ( ) + m . strip ( last ) - > w ( ) < m . x ( ) + m . w ( ) - 144 ) ;
CHECK ( xtc_project_move_track ( project , last , 0 ) = = XTC_OK ) ; m . set_project ( project ) ;
CHECK ( m . selected_track ( ) = = last & & m . strip_count ( ) = = XTC_PROJECT_MAX_TRACKS ) ;
CHECK ( xtc_project_remove_track ( project , first ) = = XTC_OK ) ; m . set_project ( project ) ; CHECK ( ! m . strip ( first ) ) ;
m . resize ( 0 , 0 , 972 , 442 ) ; m . select_track ( last ) ; auto * f = dynamic_cast < Fl_Value_Slider * > ( find ( m . strip ( last ) , " dB " ) ) ; CHECK ( f & & f - > h ( ) > 40 ) ;
m . resize ( 0 , 0 , 1500 , 900 ) ; CHECK ( f & & f - > h ( ) > 400 ) ;
window . hide ( ) ;
}
xtc_project_destroy ( project ) ;
}
2026-10-28 17:16:01 +01:00
{
reset ( ) ; MainWindow window ( false , " " ) ; window . show ( ) ; CHECK ( window . create_track ( true ) = = XTC_OK ) ;
auto * seq = timeline ( & window ) ; auto clip = seq - > selected_clip ( ) , track = seq - > selected_track ( ) ;
CHECK ( window . set_tempo ( 135500 ) = = XTC_OK & & xtc_project_tempo ( seq - > project ( ) ) = = 135500 ) ;
CHECK ( seq - > selected_clip ( ) = = clip & & seq - > selected_track ( ) = = track ) ;
CHECK ( window . undo ( ) = = XTC_OK & & xtc_project_tempo ( seq - > project ( ) ) = = 120000 ) ;
CHECK ( window . undo ( true ) = = XTC_OK & & xtc_project_tempo ( seq - > project ( ) ) = = 135500 ) ;
const auto * before = seq - > project ( ) ; reserve_result = XTC_ERR_OUT_OF_MEMORY ;
CHECK ( window . set_tempo ( 100000 ) = = XTC_ERR_OUT_OF_MEMORY & & seq - > project ( ) = = before ) ;
reserve_result = XTC_OK ; CHECK ( window . set_tempo ( 0 ) = = XTC_ERR_INVALID_ARGUMENT ) ;
submit_result = XTC_ERR_CAPACITY ; CHECK ( window . set_tempo ( 100000 ) = = XTC_ERR_CAPACITY & & seq - > project ( ) = = before ) ; submit_result = XTC_OK ;
CHECK ( window . set_tempo ( 135500 ) = = XTC_OK ) ;
auto * bpm = dynamic_cast < Fl_Value_Input * > ( find ( & window , " BPM " ) ) ; CHECK ( bpm ) ;
if ( bpm ) { bpm - > value ( 140 ) ; bpm - > do_callback ( ) ; CHECK ( xtc_project_tempo ( seq - > project ( ) ) = = 140000 ) ;
bpm - > value ( 900 ) ; bpm - > do_callback ( ) ; CHECK ( xtc_project_tempo ( seq - > project ( ) ) = = 140000 & & bpm - > value ( ) = = 140 ) ; }
auto * gain = dynamic_cast < Fl_Value_Input * > ( find ( & window , " Gain " ) ) ; CHECK ( gain ) ; pump ( ) ;
if ( gain ) { gain - > value ( .6 ) ; gain - > do_callback ( ) ; } xtc_track_info info { } ; xtc_project_get_track ( seq - > project ( ) , 0 , & info ) ; CHECK ( info . gain = = .6f ) ;
if ( gain ) { Fl : : focus ( & gain - > input ) ; gain - > input . value ( " 0.123 " ) ; pump ( ) ; CHECK ( std : : strcmp ( gain - > input . value ( ) , " 0.123 " ) = = 0 ) ; Fl : : focus ( nullptr ) ; }
CHECK ( window . undo ( ) = = XTC_OK ) ; xtc_project_get_track ( seq - > project ( ) , 0 , & info ) ; CHECK ( info . gain = = 1 ) ;
CHECK ( seq - > set_zoom ( 384 ) = = XTC_OK ) ; window . fit_song ( ) ; CHECK ( seq - > zoom_level ( ) < 384 ) ;
CHECK ( contains ( & window , " 1 tracks / 1 clips " ) ) ;
Fl : : add_timeout ( .05 , [ ] ( void * ) { auto * dialog = Fl : : first_window ( ) ; auto * search = dynamic_cast < Fl_Input * > ( find ( dialog , " Search commands " ) ) ; CHECK ( search ) ;
if ( search ) { search - > value ( " view/mixer " ) ; search - > do_callback ( ) ; } press ( find ( dialog , " Run command " ) ) ; if ( dialog - > shown ( ) ) { CHECK ( false ) ; dialog - > hide ( ) ; } } ) ;
window . show_commands ( ) ; CHECK ( window . mixer ( ) - > visible_r ( ) ) ;
auto * bar = dynamic_cast < Fl_Menu_Bar * > ( window . child ( 0 ) ) ; CHECK ( bar ) ; if ( bar ) bar - > picked ( bar - > find_item ( " View/Sequencer " ) ) ;
auto * follow = dynamic_cast < Fl_Check_Button * > ( find ( & window , " Follow playhead " ) ) ; CHECK ( follow ) ;
if ( follow ) { follow - > value ( 1 ) ; follow - > do_callback ( ) ; CHECK ( window . settings ( ) . follow_playhead = = 1 ) ; }
press ( find ( & window , " Play " ) ) ; engine_status . sample_position = 960000 ; pump ( ) ;
auto * scroll = dynamic_cast < Fl_Scroll * > ( seq - > parent ( ) ) ; CHECK ( scroll & & scroll - > xposition ( ) > 0 ) ;
press ( find ( & window , " Stop " ) ) ; window . fit_song ( ) ; CHECK ( scroll & & scroll - > xposition ( ) = = 0 ) ;
Fl : : add_timeout ( .05 , [ ] ( void * ) { auto * dialog = Fl : : first_window ( ) ; auto * search = dynamic_cast < Fl_Input * > ( find ( dialog , " Search commands " ) ) ;
CHECK ( search ) ; if ( search ) { search - > value ( " does not exist " ) ; search - > do_callback ( ) ; } press ( find ( dialog , " Run command " ) ) ; CHECK ( dialog - > shown ( ) ) ; press ( find ( dialog , " Cancel " ) ) ; } ) ;
window . show_commands ( ) ;
CHECK ( window . create_track ( true ) = = XTC_OK ) ; uint64_t second = seq - > selected_track ( ) ;
window . mixer ( ) - > select_track ( track ) ; if ( bar ) bar - > picked ( bar - > find_item ( " View/Mixer " ) ) ; pump ( ) ;
if ( gain ) { gain - > value ( .4 ) ; gain - > do_callback ( ) ; } xtc_project_get_track ( seq - > project ( ) , 0 , & info ) ; CHECK ( info . id = = track & & info . gain = = .4f ) ;
xtc_project_get_track ( seq - > project ( ) , 1 , & info ) ; CHECK ( info . id = = second & & info . gain = = 1 ) ;
window . do_callback ( ) ;
}
{
CommandPalette palette ( { " View/Mixer " , " File/Open " } ) ;
Fl : : add_timeout ( .05 , [ ] ( void * context ) { auto * p = static_cast < CommandPalette * > ( context ) ; auto * search = dynamic_cast < Fl_Input * > ( find ( p , " Search commands " ) ) ; CHECK ( search ) ;
if ( search ) { search - > value ( " FILE " ) ; search - > do_callback ( ) ; } int old = Fl : : e_keysym ; Fl : : e_keysym = FL_Enter ; CHECK ( p - > handle ( FL_KEYDOWN ) = = 1 ) ; Fl : : e_keysym = old ; } , & palette ) ;
CHECK ( palette . run ( ) = = " File/Open " ) ;
}
{
CommandPalette palette ( { " View/Mixer " } ) ;
Fl : : add_timeout ( .05 , [ ] ( void * context ) { int old = Fl : : e_keysym ; Fl : : e_keysym = FL_Escape ; static_cast < CommandPalette * > ( context ) - > handle ( FL_KEYDOWN ) ; Fl : : e_keysym = old ; } , & palette ) ;
CHECK ( palette . run ( ) . empty ( ) ) ;
}
2026-10-30 12:39:47 +01:00
reset ( ) ;
{
MainWindow window ( false , " " ) ; window . show ( ) ;
CHECK ( window . create_track ( true ) = = XTC_OK ) ;
auto * sequence = timeline ( & window ) ; auto * mix = window . mixer ( ) ;
auto * play = find ( & window , " Play " ) ;
auto * bpm = dynamic_cast < Fl_Value_Input * > ( find ( & window , " BPM " ) ) ;
auto * loop = dynamic_cast < Fl_Check_Button * > ( find ( & window , " Loop " ) ) ;
2026-11-06 00:06:56 +01:00
auto * tabs = dynamic_cast < Fl_Tabs * > ( sequence - > parent ( ) - > parent ( ) - > parent ( ) - > parent ( ) ) ;
2026-10-30 12:39:47 +01:00
CHECK ( play & & bpm & & loop & & tabs ) ;
int original_y = tabs - > y ( ) ;
for ( int pass = 0 ; pass < 3 ; + + pass ) {
window . detach_transport ( true ) ; window . detach_mixer ( true ) ; pump ( ) ;
CHECK ( window . transport_window ( ) - > shown ( ) & & window . mixer_window ( ) - > shown ( ) ) ;
CHECK ( play - > window ( ) = = window . transport_window ( ) & & mix - > window ( ) = = window . mixer_window ( ) ) ;
CHECK ( bpm - > window ( ) = = window . transport_window ( ) & & loop - > window ( ) = = window . transport_window ( ) ) ;
CHECK ( tabs - > y ( ) = = original_y - 90 ) ;
Fl : : focus ( mix ) ; int old_key = Fl : : e_keysym , old_state = Fl : : e_state ;
Fl : : e_keysym = ' ' ; Fl : : e_state = 0 ; unsigned int old_starts = starts ;
CHECK ( window . mixer_window ( ) - > handle ( FL_SHORTCUT ) = = 1 & & starts = = old_starts + 1 ) ;
CHECK ( window . transport_window ( ) - > handle ( FL_SHORTCUT ) = = 1 ) ;
Fl : : e_keysym = old_key ; Fl : : e_state = old_state ;
bpm - > value ( 132 ) ; bpm - > do_callback ( ) ;
CHECK ( xtc_project_tempo ( sequence - > project ( ) ) = = 132000 ) ;
CHECK ( window . set_loop ( 0 , 3840 , true ) = = XTC_OK ) ; pump ( ) ; CHECK ( loop - > value ( ) = = 1 ) ;
CHECK ( window . set_loop ( 0 , 3840 , false ) = = XTC_OK ) ; pump ( ) ; CHECK ( loop - > value ( ) = = 0 ) ;
window . mixer_window ( ) - > resize ( 10 , 10 , 800 , 500 ) ; pump ( ) ;
CHECK ( mix - > x ( ) + mix - > w ( ) < = window . mixer_window ( ) - > w ( ) ) ;
window . resize ( window . x ( ) , window . y ( ) , pass = = 0 ? 1600 : 1220 , pass = = 0 ? 1000 : 720 ) ;
window . transport_window ( ) - > do_callback ( ) ; window . mixer_window ( ) - > do_callback ( ) ; pump ( ) ;
CHECK ( ! window . transport_window ( ) - > shown ( ) & & ! window . mixer_window ( ) - > shown ( ) ) ;
CHECK ( play - > window ( ) = = & window & & mix - > window ( ) = = & window ) ;
CHECK ( tabs - > y ( ) = = original_y & & mix - > w ( ) > 0 & & mix - > h ( ) > 0 ) ;
}
window . detach_transport ( true ) ; window . detach_mixer ( true ) ;
close_result = XTC_ERR_IO ; window . do_callback ( ) ;
CHECK ( window . shown ( ) & & window . transport_window ( ) - > shown ( ) & & window . mixer_window ( ) - > shown ( ) ) ;
close_result = XTC_OK ; window . do_callback ( ) ;
CHECK ( ! window . shown ( ) & & ! window . transport_window ( ) - > shown ( ) & & ! window . mixer_window ( ) - > shown ( ) ) ;
}
2026-11-05 23:57:56 +01:00
reset ( ) ;
{
std : : string path = " /tmp/xtc-panel-layout- " + std : : to_string ( getpid ( ) ) + " .conf " ;
{
MainWindow window ( false , path . c_str ( ) ) ; window . show ( ) ;
window . detach_transport ( true ) ; window . detach_mixer ( true ) ;
window . transport_window ( ) - > resize ( 40 , 40 , 400 , 400 ) ; window . mixer_window ( ) - > resize ( 80 , 80 , 700 , 460 ) ; pump ( ) ;
auto * group = dynamic_cast < Fl_Group * > ( find ( window . transport_window ( ) , " Play " ) - > parent ( ) ) ; CHECK ( group ) ;
for ( int i = 0 ; i < group - > children ( ) ; + + i ) {
auto * widget = group - > child ( i ) ; CHECK ( widget - > x ( ) > = 0 & & widget - > x ( ) + widget - > w ( ) < = 400 ) ;
CHECK ( widget - > y ( ) + widget - > h ( ) < = window . transport_window ( ) - > h ( ) ) ;
}
Fl : : add_timeout ( .05 , [ ] ( void * ) { auto * d = Fl : : first_window ( ) ; press ( find ( d , " Apply " ) ) ; press ( find ( d , " Close " ) ) ; } ) ;
window . show_preferences ( ) ; xtc_settings saved { } ; CHECK ( xtc_settings_load ( & saved , path . c_str ( ) ) = = XTC_OK ) ;
CHECK ( saved . transport_detached & & saved . mixer_detached & & saved . transport_width = = 400 & & saved . mixer_width = = 700 ) ;
saved . transport_x = 30000 ; saved . transport_y = - 30000 ; CHECK ( xtc_settings_save ( & saved , path . c_str ( ) ) = = XTC_OK ) ;
window . do_callback ( ) ;
}
{
MainWindow window ( false , path . c_str ( ) ) ; window . show ( ) ; pump ( ) ;
CHECK ( window . transport_window ( ) & & window . transport_window ( ) - > shown ( ) ) ;
CHECK ( window . mixer_window ( ) & & window . mixer_window ( ) - > shown ( ) ) ;
int sx , sy , sw , sh ; Fl : : screen_work_area ( sx , sy , sw , sh , 0 ) ;
CHECK ( window . transport_window ( ) - > x ( ) > = sx & & window . transport_window ( ) - > x ( ) + window . transport_window ( ) - > w ( ) < = sx + sw ) ;
CHECK ( window . transport_window ( ) - > y ( ) > = sy & & window . transport_window ( ) - > y ( ) + window . transport_window ( ) - > h ( ) < = sy + sh ) ;
window . detach_transport ( false ) ; window . detach_mixer ( false ) ; window . do_callback ( ) ;
}
unlink ( path . c_str ( ) ) ;
}
2026-11-06 00:06:56 +01:00
reset ( ) ;
{
std : : string path = " /tmp/xtc-editors- " + std : : to_string ( getpid ( ) ) + " .conf " ;
{
MainWindow window ( false , path . c_str ( ) ) ; window . show ( ) ; CHECK ( window . create_track ( true ) = = XTC_OK ) ;
auto * sequence = timeline ( & window ) ; auto * graph = graph_canvas ( & window ) ; const xtc_project * project = sequence - > project ( ) ;
uint64_t track = sequence - > selected_track ( ) , clip = sequence - > selected_clip ( ) ;
auto * scroll = dynamic_cast < Fl_Scroll * > ( sequence - > parent ( ) ) ; CHECK ( scroll ) ; scroll - > scroll_to ( 400 , 0 ) ; pump ( ) ;
int pinned = sequence - > header_left ( ) ; CHECK ( pinned = = scroll - > x ( ) ) ;
sequence - > header_width ( 260 ) ; pump ( ) ; CHECK ( sequence - > header_width ( ) = = 260 & & scroll - > xposition ( ) = = 400 ) ;
CHECK ( sequence - > instrument_choice ( track ) - > x ( ) = = pinned + 10 & & sequence - > instrument_choice ( track ) - > w ( ) = = 238 ) ;
gesture ( sequence , pinned - sequence - > x ( ) + 258 , 16 , 40 , 0 ) ; CHECK ( sequence - > header_width ( ) = = 300 ) ;
sequence - > header_width ( 260 ) ;
CHECK ( window . open_piano_roll ( ) = = XTC_OK ) ; auto * piano = window . piano_roll ( ) ;
CHECK ( piano - > add_note ( 0 , 240 , 60 ) = = XTC_OK ) ; uint64_t note = piano - > selected_note ( ) ;
project = sequence - > project ( ) ;
auto * piano_scroll = dynamic_cast < Fl_Scroll * > ( piano - > parent ( ) ) ; CHECK ( piano_scroll ) ; piano_scroll - > scroll_to ( 10 , 500 ) ;
for ( int pass = 0 ; pass < 3 ; + + pass ) {
window . detach_editor ( 0 , true ) ; window . detach_editor ( 1 , true ) ; window . detach_piano ( false ) ; pump ( ) ;
CHECK ( sequence - > window ( ) = = window . editor_window ( 0 ) & & graph - > window ( ) = = window . editor_window ( 1 ) ) ;
CHECK ( find ( window . editor_window ( 0 ) , " Snap " ) ! = nullptr ) ;
CHECK ( piano - > window ( ) = = & window & & piano - > selected_note ( ) = = note & & sequence - > project ( ) = = project ) ;
CHECK ( sequence - > selected_track ( ) = = track & & sequence - > selected_clip ( ) = = clip ) ;
CHECK ( scroll - > xposition ( ) = = 400 & & piano_scroll - > yposition ( ) = = 500 ) ;
window . editor_window ( 0 ) - > resize ( 20 , 20 , 1200 , 700 ) ; window . editor_window ( 1 ) - > resize ( 30 , 30 , 1000 , 500 ) ; pump ( ) ;
window . detach_piano ( true ) ; CHECK ( piano - > window ( ) ! = & window ) ;
auto * piano_window = piano - > window ( ) ; piano_window - > do_callback ( ) ; CHECK ( piano - > window ( ) = = & window ) ;
window . editor_window ( 0 ) - > do_callback ( ) ; window . editor_window ( 1 ) - > do_callback ( ) ; pump ( ) ;
CHECK ( sequence - > window ( ) = = & window & & graph - > window ( ) = = & window & & scroll - > xposition ( ) = = 400 ) ;
CHECK ( piano - > selected_note ( ) = = note & & sequence - > project ( ) = = project ) ;
}
window . detach_editor ( 0 , true ) ; window . detach_editor ( 1 , true ) ; window . detach_piano ( false ) ;
Fl : : add_timeout ( .05 , [ ] ( void * ) { auto * d = Fl : : first_window ( ) ; press ( find ( d , " Apply " ) ) ; press ( find ( d , " Close " ) ) ; } ) ;
window . show_preferences ( ) ; xtc_settings saved { } ; CHECK ( xtc_settings_load ( & saved , path . c_str ( ) ) = = XTC_OK ) ;
CHECK ( saved . sequencer_detached & & saved . graph_detached & & ! saved . piano_detached & & saved . track_header_width = = 260 ) ;
close_result = XTC_ERR_IO ; window . do_callback ( ) ; CHECK ( window . editor_window ( 0 ) - > shown ( ) & & window . editor_window ( 1 ) - > shown ( ) ) ;
close_result = XTC_OK ; window . do_callback ( ) ; CHECK ( ! window . editor_window ( 0 ) - > shown ( ) & & ! window . editor_window ( 1 ) - > shown ( ) ) ;
}
{
MainWindow window ( false , path . c_str ( ) ) ; window . show ( ) ; pump ( ) ;
CHECK ( window . editor_window ( 0 ) - > shown ( ) & & window . editor_window ( 1 ) - > shown ( ) ) ;
CHECK ( window . create_track ( true ) = = XTC_OK & & window . open_piano_roll ( ) = = XTC_OK & & window . piano_roll ( ) - > window ( ) = = & window ) ;
window . reset_layout ( ) ; pump ( ) ; CHECK ( timeline ( & window ) & & graph_canvas ( & window ) ) ;
CHECK ( ! window . editor_window ( 0 ) - > shown ( ) & & ! window . editor_window ( 1 ) - > shown ( ) ) ;
CHECK ( timeline ( & window ) - > header_width ( ) = = 180 ) ; window . do_callback ( ) ;
}
unlink ( path . c_str ( ) ) ;
}
2026-11-06 00:12:37 +01:00
reset ( ) ;
{
MainWindow window ( false , " " ) ; window . show ( ) ; CHECK ( window . create_track ( true ) = = XTC_OK ) ;
auto * s = timeline ( & window ) ; uint64_t first = s - > selected_clip ( ) ; CHECK ( s - > duplicate_selection ( ) = = XTC_OK ) ;
uint64_t second = s - > selected_clip ( ) ; CHECK ( s - > toggle_clip ( first ) ) ; CHECK ( s - > selected_clips ( ) . size ( ) = = 2 ) ;
CHECK ( s - > clip_selected ( first ) & & s - > clip_selected ( second ) ) ; CHECK ( ! s - > toggle_clip ( UINT64_MAX ) ) ;
window . detach_editor ( 0 , true ) ; CHECK ( s - > selected_clips ( ) . size ( ) = = 2 ) ; window . detach_editor ( 0 , false ) ;
CHECK ( s - > toggle_clip ( first ) & & ! s - > clip_selected ( first ) ) ; s - > select_all_clips ( ) ; CHECK ( s - > selected_clips ( ) . size ( ) = = 2 ) ;
2026-11-06 00:13:48 +01:00
CHECK ( s - > nudge_selection ( 1 ) = = XTC_OK ) ; xtc_clip_info moved { } ;
CHECK ( xtc_project_find_clip ( s - > project ( ) , first , & moved ) = = XTC_OK & & moved . start_tick = = 240 ) ;
CHECK ( s - > duplicate_selection ( ) = = XTC_OK & & s - > selected_clips ( ) . size ( ) = = 2 ) ;
size_t tc = 0 , cc = 0 ; xtc_project_get_counts ( s - > project ( ) , & tc , & cc ) ; CHECK ( cc = = 4 ) ;
CHECK ( s - > delete_selection ( ) = = XTC_OK ) ; xtc_project_get_counts ( s - > project ( ) , & tc , & cc ) ; CHECK ( cc = = 2 ) ;
2026-11-06 00:12:37 +01:00
window . do_callback ( ) ;
}
2026-10-26 23:40:28 +01:00
if ( failures ! = 0 ) return 1 ;
std : : printf ( " GUI controls/errors/views/timers/cleanup passed: %u opens, %u closes. \n " , opens , closes ) ;
return 0 ;
}