docs: refresh all screenshots and illustrate phase 58–68 workflows

This commit is contained in:
Johannes Findeisen 2026-11-06 00:56:27 +01:00
commit 66e07776a4
55 changed files with 178 additions and 21 deletions

View file

@ -554,3 +554,13 @@ test-next-daw: $(BUILD_DIR)/test_next_daw
test: test-next-daw
test-gui-next-daw: $(BUILD_DIR)/test_gui
XTC_TEST_MODERN_EDITING_ONLY=1 XTC_TEST_NEXT_DAW_ONLY=1 xvfb-run -a -s "-screen 0 1280x800x24" ./$(BUILD_DIR)/test_gui
# Optional documentation capture; requires Xvfb, ImageMagick import and xdotool.
$(BUILD_DIR)/capture_screenshots: $(BUILD_DIR)/tools/capture_screenshots.o $(BUILD_DIR)/src/gui/main_window.o $(BUILD_DIR)/src/gui/graph_widget.o $(BUILD_DIR)/src/gui/sequencer_widget.o $(BUILD_DIR)/src/gui/piano_roll.o $(BUILD_DIR)/src/gui/mixer_widget.o $(BUILD_DIR)/src/gui/plugin_dialog.o $(BUILD_DIR)/libxtc.a
$(CXX) $(LDFLAGS) -o $@ $^ $(FLTK_LIBS) $(JACK_LIBS) -lm -ldl
.PHONY: screenshots
screenshots: $(BUILD_DIR)/capture_screenshots $(BUILD_DIR)/plugin-fixture.so $(BUILD_DIR)/clap-synth-fixture.clap $(BUILD_DIR)/vst2-synth-fixture.so $(BUILD_DIR)/vst3-synth-fixture.so
xvfb-run -a -s "-screen 0 1440x960x24" ./$(BUILD_DIR)/capture_screenshots "$(CURDIR)/screenshots"
-include $(BUILD_DIR)/tools/capture_screenshots.d

View file

@ -1673,6 +1673,12 @@ add no dependencies or callback I/O/allocation. See
behavior. Human listening, physical hardware, arbitrary third-party plugins and
feature parity with commercial DAWs remain unqualified.
All application screenshots were refreshed after Phase 68. The
[screenshot gallery](../screenshots/README.md) includes controls and results for
every phase from 58 through 68, plus refreshed existing views and detached
editors. The optional `make screenshots` target reproduces the captures using
real FLTK widgets and isolated demonstration data.
---
## Recommended Execution Order

View file

@ -1,24 +1,49 @@
# xtc screenshots
Captured from the Phase 38 FLTK application on 2026-10-28, with Phase 39
transport, mixer, arrangement and command captures refreshed on 2026-10-30 using
a virtual X11 screen. Images show actual rendered widgets, without mockups or image edits.
All captures refreshed from the Phase 68 FLTK application on 2026-11-06.
These are actual rendered application windows and menus on a 1440 × 960 virtual
X11 screen, without mockups, compositing or image edits.
![Overview of xtc screenshots](overview.png)
![Arrangement with named sections](overview.png)
Internal instrument dropdowns show Soft Keys and Sub Bass; internal delay and
reverb captures show the actual editable effect controls.
The temporary demonstration project starts from
`examples/projects/modern-session.xtc`, adding Intro/Verse/Chorus/Outro markers,
a routed music bus and gain automation. Editing results use the real control
APIs and GUI history/preparation paths. Original projects, media and user
configuration are not overwritten. Black margins are the virtual desktop;
floating views show the actual detached windows together.
The arrangement uses `examples/projects/modern-session.xtc`. Mixer captures add
a bus, routing, pan/level settings and automation. The playing view shows actual
JACK callback meters; additional captures show the minimum 1220 × 720 layout and
a larger session scrolled to its final channels, with the master monitor pinned.
The audio view adds a temporary generated waveform. Plugin views use the
repository's LADSPA, CLAP, VST2 and VST3 test fixtures; these are demonstration
plugins, not commercial instruments. Preferences show application defaults and
the staged loop. The command palette shows the available application actions.
The workspace now includes direct BPM editing, a live track inspector and
arrangement navigation. No user configuration or example project was overwritten.
## New arrangement and composition workflows
| Phase | Feature | Captures |
| --- | --- | --- |
| 58 | Persistent named arrangement markers | [Ruler](arrangement-markers.png), [marker editor](marker-edit.png) |
| 59 | Section navigation and looping | [Current section loop](section-navigation.png) |
| 60 | Ripple time insertion/removal | [Controls](ripple-time.png), [inserted gap](ripple-result.png) |
| 61 | Complete track duplication | [Independent layered track](track-duplication.png) |
| 62 | Nondestructive MIDI splitting | [Split clip](midi-split.png) |
| 63 | MIDI clip consolidation | [Consolidated clip](midi-consolidation.png) |
| 64 | Seeded MIDI humanization | [Timing, velocity and seed](midi-humanize.png) |
| 65 | Chord construction | [Intervals](midi-chord-builder.png), [generated notes](midi-chords-result.png) |
| 66 | Offline arpeggio generation | [Step, gate and direction](midi-arpeggio.png), [written pattern](midi-arpeggio-result.png) |
| 67 | Audio peak normalization | [Target dBFS](audio-normalization.png), [normalized clip](audio-normalization-result.png) |
| 68 | Portable media collection | [New destination folder](project-collection.png) |
Collection is also executed by the capture tool into its temporary directory;
the screenshot shows its destination prompt, not a file browser or progress UI.
See [COMPOSITION_WORKFLOWS.md](../docs/COMPOSITION_WORKFLOWS.md) for precise
behavior, limits and commands. The screenshots illustrate implemented workflows;
they do not establish feature parity with other DAWs.
## Refreshed application views
All pre-existing images below were recaptured. Mixer playback meters come from
real JACK callbacks. The minimum-size capture uses a 1220 × 720 main window;
the scrolled view adds twelve tracks. Audio views import the repository's
`original-pluck.wav` and apply clip gain/fades. Plugin views use repository test
fixtures, not commercial plugins. Internal instrument menus and delay/reverb
editors show the real available devices and controls. The command palette uses
the current application action list.
- [Sequencer loop](sequencer-loop.png)
- [Mixer automation](mixer-automation.png)
@ -55,5 +80,25 @@ arrangement navigation. No user configuration or example project was overwritten
- [Detached mixer](mixer-detached.png)
- [Workspace with transport and mixer detached](workspace-detached.png)
- [Detached sequencer](sequencer-detached.png)
- [Detached modular graph](graph-detached.png)
Detach panels from the View menu; close a floating window or use its Dock button
to return it to the main window. The main arrangement uses the freed toolbar space.
to return it to the main window.
## Reproduce the gallery
From the repository root, run:
```sh
make BUILD_DIR=build/next-daw screenshots
```
This optional documentation target requires the existing FLTK/JACK build
prerequisites, a running JACK server, Xvfb (`xvfb-run`), ImageMagick (`import`) and
`xdotool`. It builds the repository plugin fixtures, runs
[the capture tool](../tools/capture_screenshots.cpp), and replaces the gallery
PNGs. All demonstration edits and collection outputs use an automatically removed
temporary directory; no preferences are saved. It neither installs dependencies
nor starts/reconfigures the audio server.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.1 KiB

After

Width:  |  Height:  |  Size: 84 KiB

Before After
Before After

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.6 KiB

After

Width:  |  Height:  |  Size: 87 KiB

Before After
Before After

Binary file not shown.

After

Width:  |  Height:  |  Size: 81 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 84 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

After

Width:  |  Height:  |  Size: 73 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 77 KiB

Before After
Before After

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

After

Width:  |  Height:  |  Size: 66 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 83 KiB

After

Width:  |  Height:  |  Size: 80 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

After

Width:  |  Height:  |  Size: 66 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.3 KiB

After

Width:  |  Height:  |  Size: 87 KiB

Before After
Before After

BIN
screenshots/marker-edit.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 85 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 63 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 81 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 63 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 79 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 56 KiB

After

Width:  |  Height:  |  Size: 116 KiB

Before After
Before After

Binary file not shown.

After

Width:  |  Height:  |  Size: 83 KiB

BIN
screenshots/midi-split.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 83 KiB

After

Width:  |  Height:  |  Size: 85 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 38 KiB

After

Width:  |  Height:  |  Size: 71 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 83 KiB

After

Width:  |  Height:  |  Size: 83 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 85 KiB

After

Width:  |  Height:  |  Size: 86 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 82 KiB

After

Width:  |  Height:  |  Size: 86 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 78 KiB

After

Width:  |  Height:  |  Size: 69 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 MiB

After

Width:  |  Height:  |  Size: 78 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 63 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 58 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

After

Width:  |  Height:  |  Size: 79 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 41 KiB

After

Width:  |  Height:  |  Size: 76 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

After

Width:  |  Height:  |  Size: 58 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 36 KiB

After

Width:  |  Height:  |  Size: 63 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 58 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

After

Width:  |  Height:  |  Size: 58 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 53 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

After

Width:  |  Height:  |  Size: 54 KiB

Before After
Before After

Binary file not shown.

After

Width:  |  Height:  |  Size: 86 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

BIN
screenshots/ripple-time.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 84 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 76 KiB

After

Width:  |  Height:  |  Size: 81 KiB

Before After
Before After

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 74 KiB

After

Width:  |  Height:  |  Size: 78 KiB

Before After
Before After

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 62 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 61 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 61 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 60 KiB

After

Width:  |  Height:  |  Size: 71 KiB

Before After
Before After

View file

@ -1207,7 +1207,7 @@ void MainWindow::view_callback(Fl_Widget *widget, void *view)
*/
void MainWindow::about_callback(Fl_Widget *, void *)
{
fl_message("%s %s\nModular audio workstation\nPhase 12: timing, transitions and audio quality.",
fl_message("%s %s\nModular audio workstation\nArrangement markers, MIDI composition, audio editing and modular mixing.",
XTC_PROJECT_NAME, XTC_VERSION_STRING);
}

View file

@ -0,0 +1,96 @@
/* Optional documentation tool: real FLTK widgets and real backend, isolated data. */
#include "../src/gui/main_window.hpp"
#include "../src/gui/sequencer_widget.hpp"
#include "../src/gui/mixer_widget.hpp"
#include "../src/gui/piano_roll.hpp"
#include "../src/gui/plugin_dialog.hpp"
#include "../src/gui/plugin_library.hpp"
#include <FL/Fl.H>
#include <FL/Fl_Tabs.H>
#include <FL/Fl_Menu_Bar.H>
#include <FL/Fl_Button.H>
#include <FL/Fl_Choice.H>
#include <FL/Fl_Scroll.H>
#include <FL/x.H>
#include <chrono>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <filesystem>
#include <stdexcept>
#include <functional>
#include <unistd.h>
namespace {
MainWindow *main_window;
std::string destination;
void check(xtc_result r) {if(r!=XTC_OK)throw std::runtime_error(xtc_result_string(r));}
void pump() {auto end=std::chrono::steady_clock::now()+std::chrono::milliseconds(180);while(std::chrono::steady_clock::now()<end)Fl::wait(.01);}
template<class T> T *first(Fl_Group &g) {for(int i=0;i<g.children();++i){auto *c=g.child(i);if(auto *t=dynamic_cast<T *>(c))return t;if(auto *n=dynamic_cast<Fl_Group *>(c))if(auto *t=first<T>(*n))return t;}return nullptr;}
Fl_Widget *named(Fl_Group &g,const char *label) {for(int i=0;i<g.children();++i){auto *c=g.child(i);if(c->label()&&!strcmp(c->label(),label))return c;if(auto *n=dynamic_cast<Fl_Group *>(c))if(auto *w=named(*n,label))return w;}return nullptr;}
void tab(const char *label) {auto *page=named(*main_window,label);if(!page)throw std::runtime_error(label);auto *tabs=dynamic_cast<Fl_Tabs *>(page->parent());if(!tabs)throw std::runtime_error("Tab parent");tabs->value(page);tabs->redraw();pump();}
void capture(const char *name) {Fl::flush();pump();std::string command="import -window root '"+destination+"/"+name+".png'";if(system(command.c_str()))throw std::runtime_error("ImageMagick capture failed");std::printf("Captured %s\n",name);std::fflush(stdout);}
struct Modal {const char *name;const char *page;};
void modal_capture(void *data) {auto *job=static_cast<Modal *>(data);auto *w=Fl::modal();if(!w||w==main_window)throw std::runtime_error("Expected a modal dialog");if(job->page){auto *tabs=first<Fl_Tabs>(*w);auto *page=named(*w,job->page);if(!tabs||!page)throw std::runtime_error(job->page);tabs->value(page);tabs->redraw();}capture(job->name);auto *cancel=named(*w,"Cancel");if(!cancel)cancel=named(*w,"Close");if(!cancel)cancel=named(*w,"OK");if(cancel)cancel->do_callback();else w->do_callback();}
void dialog(const char *name,std::function<void()> action,const char *page=nullptr) {Modal job{name,page};Fl::add_timeout(.3,modal_capture,&job);action();Fl::remove_timeout(modal_capture,&job);pump();}
void menu(const char *path) {auto *bar=first<Fl_Menu_Bar>(*main_window);const auto *item=bar->find_item(path);if(!item)throw std::runtime_error(path);bar->picked(item);}
void piano_view() {
check(main_window->open_piano_roll());
auto *piano=main_window->piano_roll();
if(auto *scroll=dynamic_cast<Fl_Scroll *>(piano->parent()))scroll->scroll_to(0,50*16);
size_t count=0;check(xtc_project_note_count(piano->project(),&count));
for(size_t i=0;i<count;++i){xtc_note_info note{};check(xtc_project_get_note(piano->project(),i,&note));if(note.clip_id==piano->clip()){piano->select_note(note.id);break;}}
piano->redraw();pump();
}
void button(const char *name) {auto *b=named(*main_window,name);if(!b)throw std::runtime_error(name);b->do_callback();pump();}
}
int main(int argc,char **argv) {
if(argc!=2){std::fprintf(stderr,"Usage: capture_screenshots OUTPUT_DIRECTORY (absolute, no apostrophes)\n");return 1;}
destination=argv[1];if(destination.empty()||destination[0]!='/'||destination.find('\'')!=std::string::npos)return 1;
try {
std::filesystem::create_directories(destination);
char temp[]="/tmp/xtc-screenshots-XXXXXX";if(!mkdtemp(temp))return 1;std::string root=temp;
struct Cleanup {std::string path;~Cleanup(){std::filesystem::remove_all(path);}} cleanup{root};
xtc_project *p=nullptr;check(xtc_project_create(&p));check(xtc_project_load(p,"examples/projects/modern-session.xtc"));
for(auto pair:{std::pair<uint64_t,const char *>{0,"Intro"},{3840,"Verse"},{7680,"Chorus"},{11520,"Outro"}})check(xtc_project_set_marker(p,pair.first,pair.second));
uint64_t bus;check(xtc_project_add_bus(p,"Music bus",&bus));check(xtc_project_route_track(p,0,bus));
xtc_automation_point a{0,0,XTC_AUTOMATION_GAIN,.65f,0};check(xtc_project_set_automation(p,&a));a.tick=3840;a.value=1;check(xtc_project_set_automation(p,&a));a.tick=7680;a.value=.8f;check(xtc_project_set_automation(p,&a));
std::string project=root+"/composition.xtc";check(xtc_project_save(p,project.c_str()));xtc_project_destroy(p);
MainWindow window(false,"");main_window=&window;window.resize(0,0,1400,900);window.show();check(window.open_project(project.c_str()));pump();
auto *s=first<SequencerWidget>(window);if(!s)throw std::runtime_error("Sequencer missing");
auto reset=[&](){window.reset_layout();check(window.open_project(project.c_str()));window.resize(0,20,1400,900);tab("Sequencer");s->select(0,0);s->position(0);check(window.set_loop(3840,7680,true));window.fit_song();pump();};
reset();capture("overview");capture("sequencer-loop");capture("arrangement-markers");
s->position(4800);check(window.loop_section());capture("section-navigation");
dialog("marker-edit",[&]{s->marker_dialog();});
dialog("ripple-time",[&]{s->ripple_dialog(false);});
s->position(7680);check(s->ripple_time(1920,false));capture("ripple-result");reset();
check(s->duplicate_track("Keys layered"));capture("track-duplication");reset();
s->position(1920);check(s->split_selection());capture("midi-split");
s->select_all_clips(); // Consolidation requires one track; reset to just the two halves.
s->select(0,0);size_t tracks=0,clips=0;xtc_project_get_counts(s->project(),&tracks,&clips);
for(size_t i=0;i<clips;++i){xtc_clip_info c{};xtc_project_get_clip(s->project(),i,&c);if(c.track_id==0&&c.start_tick==1920)s->toggle_clip(c.id);}
check(s->merge_midi());capture("midi-consolidation");reset();
dialog("midi-humanize",[&]{s->humanize_dialog();});dialog("midi-chord-builder",[&]{s->chord_dialog();});dialog("midi-arpeggio",[&]{s->arpeggio_dialog();});
check(s->make_chord({4,7}));piano_view();capture("midi-chords-result");check(s->arpeggiate(240,70,false));piano_view();capture("midi-arpeggio-result");reset();
piano_view();capture("piano-roll-velocity");window.detach_piano(false);reset();
dialog("commands",[&]{window.show_commands();});
tab("Mixer");capture("mixer-automation");button("Play");pump();capture("mixer-playing");button("Stop");
window.resize(0,0,1220,720);pump();capture("mixer-minimum-size");window.resize(0,0,1400,900);
for(int i=0;i<12;++i){check(window.create_track(false));}
tab("Mixer");auto *scroll=first<Fl_Scroll>(*window.mixer());if(scroll)scroll->scroll_to(std::max(0,static_cast<int>(window.mixer()->strip_count())*156-scroll->w()),0);pump();capture("mixer-scrolled-session");reset();
tab("Modular Graph");capture("modular-graph");reset();
window.detach_transport(true);pump();capture("transport-detached");window.detach_mixer(true);pump();capture("workspace-detached");capture("mixer-detached");reset();
window.detach_editor(0,true);pump();capture("sequencer-detached");reset();tab("Modular Graph");window.detach_editor(1,true);pump();capture("graph-detached");reset();
char wave[4096];if(!realpath("examples/projects/original-pluck.wav",wave))throw std::runtime_error("Missing WAV");check(window.create_track(false));check(window.import_audio(wave));check(s->set_zoom(192));check(s->audio_properties(.8f,240,480));pump();capture("sequencer-audio-waveform");
dialog("audio-clip-properties",[&]{s->audio_properties_dialog();});dialog("audio-normalization",[&]{s->normalize_dialog();});check(s->normalize_audio(.25f));capture("audio-normalization-result");
dialog("project-collection",[&]{menu("File/Collect project media...");});check(window.collect_project((root+"/portable").c_str()));
dialog("loop-range",[&]{button("Loop range...");});dialog("midi-controller-mapping",[&]{menu("Audio/MIDI controller mapping");});dialog("about",[&]{menu("Help/About");});
for(const char *section:{"audio","plugins","interface","editing","project_defaults","transport"}){std::string file="preferences-"+std::string(section);for(char &c:file)if(c==' ')c='_';dialog(file.c_str(),[&]{window.show_preferences();},section);}
xtc_settings settings=window.settings();std::string fixtures=std::filesystem::canonical(argv[0]).parent_path().string();snprintf(settings.ladspa_paths,sizeof(settings.ladspa_paths),"%s",fixtures.c_str());settings.clap_paths[0]=settings.vst2_paths[0]=settings.vst3_paths[0]=0;
dialog("plugin-library",[&]{std::string path;xtc_choose_plugin_file(settings,path);});
for(auto entry:{std::pair<const char *,const char *>{"plugin-descriptors","plugin-fixture.so"},{"clap-instrument-descriptors","clap-synth-fixture.clap"},{"vst2-instrument-descriptors","vst2-synth-fixture.so"},{"vst3-instrument-descriptors","vst3-synth-fixture.so"}}){unsigned int index=0;dialog(entry.first,[&]{xtc_choose_plugin((fixtures+"/"+entry.second).c_str(),48000,index,strcmp(entry.first,"plugin-descriptors")!=0);});}
for(auto entry:{std::pair<const char *,unsigned int>{"internal-delay",2},{"internal-reverb",3},{"plugin-controls",2}}){xtc_plugin_spec spec{},edited{};xtc_plugin_details details{};if(!strcmp(entry.first,"plugin-controls"))check(xtc_plugin_probe((fixtures+"/plugin-fixture.so").c_str(),0,48000,&spec));else check(xtc_plugin_probe_format(XTC_PLUGIN_BUILTIN,XTC_BUILTIN_PATH,entry.second,48000,&spec));check(xtc_plugin_describe(&spec,48000,&details));dialog(entry.first,[&]{xtc_edit_plugin(spec,details,edited);});}
reset();auto *choice=s->instrument_choice(0);if(!choice)throw std::runtime_error("Instrument menu missing");Fl::add_timeout(.3,[](void*){capture("internal-instruments");if(system("xdotool key Escape"))std::abort();});choice->menu()->pulldown(choice->x(),choice->y(),choice->w(),choice->h(),choice->mvalue(),choice);
window.do_callback();std::puts("All documentation captures completed.");return 0;
} catch(const std::exception &e){std::fprintf(stderr,"Screenshot capture failed: %s\n",e.what());return 1;}
}