feat: persist floating panel layouts and wrap the compact transport
This commit is contained in:
parent
836cdedc6b
commit
73e8bf09dc
9 changed files with 149 additions and 13 deletions
|
|
@ -71,3 +71,8 @@ Phase 44 adds `[transport] tail_ms=3000` (0..60000) and
|
|||
`stop_after_tail=0` (0/1). Export always includes the configured tail; optional
|
||||
playback stopping uses the GUI timer and is disabled during looping/recording.
|
||||
See BUILTIN_DEVICES.md for endpoint, cancellation and reset semantics.
|
||||
|
||||
Phase 45 adds `[interface] transport_detached`, `mixer_detached`, and each
|
||||
panel's `_x`, `_y`, `_width`, `_height` rectangle. Use Preferences / Apply after
|
||||
moving panels to save the captured layout. Restoration clamps rectangles to the
|
||||
current display; the floating transport wraps at narrow widths.
|
||||
|
|
|
|||
|
|
@ -1261,14 +1261,18 @@ External-plugin history remains retained. See BUILTIN_DEVICES.md for limits.
|
|||
Retain useful floating layouts across sessions and support narrower displays.
|
||||
|
||||
### Tasks and acceptance criteria
|
||||
- [ ] Persist docked/floating state and panel geometry through the application preferences workflow.
|
||||
- [ ] Restore layouts safely when monitors or display sizes change; keep all panels reachable.
|
||||
- [ ] Provide a compact/responsive floating transport while retaining BPM, loop and playback controls.
|
||||
- [ ] Preserve close-to-dock, Dock all, shortcuts, editing focus protection and safe shutdown.
|
||||
- [ ] Test restart, invalid settings, off-screen geometry, narrow sizes and repeated dock/resize cycles.
|
||||
- [x] Persist docked/floating state and panel geometry through the application preferences workflow.
|
||||
- [x] Restore layouts safely when monitors or display sizes change; keep all panels reachable.
|
||||
- [x] Provide a compact/responsive floating transport while retaining BPM, loop and playback controls.
|
||||
- [x] Preserve close-to-dock, Dock all, shortcuts, editing focus protection and safe shutdown.
|
||||
- [x] Test restart, invalid settings, off-screen geometry, narrow sizes and repeated dock/resize cycles.
|
||||
|
||||
### Implementation status
|
||||
Planned. Phase 39 starts docked each session and its transport minimum is 1220 pixels.
|
||||
Implemented: validated panel preferences, geometry capture on Preferences/Apply,
|
||||
safe display-area restoration and a wrapping floating transport down to 400
|
||||
pixels. Core/settings and GUI tests pass, including restart, invalid geometry,
|
||||
off-screen clamping, narrow control bounds and existing dock/lifecycle checks.
|
||||
The full-window minimum is unchanged. See WORKSPACE.md and PREFERENCES.md.
|
||||
|
||||
---
|
||||
|
||||
|
|
|
|||
|
|
@ -177,3 +177,19 @@ Verified with `make BUILD_DIR=build/phase39 gui test-gui test-gui-instruments
|
|||
test-gui-jack` (one line), plus the GUI suite under AddressSanitizer/UBSan.
|
||||
Tests cover repeated reparenting, close-to-dock, resize, shared BPM/loop state,
|
||||
playback shortcuts, failed shutdown and cleanup.
|
||||
|
||||
## Phase 45 saved panel layouts
|
||||
|
||||
Preferences / Apply now saves transport/mixer dock state and window rectangles.
|
||||
Open Preferences after arranging the windows to capture their current positions.
|
||||
Closing the application alone does not save preferences. Startup restores the
|
||||
saved layout; off-screen rectangles are clamped to an available display's work
|
||||
area. Invalid dimensions are rejected transactionally by the settings reader.
|
||||
|
||||
The floating transport supports widths down to 400 pixels. Controls wrap into
|
||||
rows below 1220 pixels, retaining playback, BPM, loop and navigation actions;
|
||||
its height grows as necessary to keep every control available. Docking restores
|
||||
the wide toolbar and arrangement geometry. Mixer minimum remains 600 × 420.
|
||||
Existing close-to-dock, Dock all and shortcuts retain their shared widget state.
|
||||
Tests cover narrow control bounds, save/restart, off-screen restoration, invalid
|
||||
geometry, repeated docking/resizing and shared transport state.
|
||||
|
|
|
|||
|
|
@ -11,6 +11,9 @@ typedef struct {
|
|||
char client_name[64], clap_paths[2048], vst2_paths[2048], vst3_paths[2048], ladspa_paths[2048];
|
||||
int64_t max_frames, channels, transition_ms, offline_rate, recording_seconds;
|
||||
int64_t width, height, piano_width, piano_height, refresh_ms;
|
||||
int64_t transport_detached,mixer_detached;
|
||||
int64_t transport_x,transport_y,transport_width,transport_height;
|
||||
int64_t mixer_x,mixer_y,mixer_width,mixer_height;
|
||||
int64_t sequencer_snap, piano_snap, sequencer_zoom;
|
||||
int64_t clip_ticks, note_ticks, note_velocity, note_channel, filter_channel, filter_low, filter_high;
|
||||
int64_t tempo, volume_cc, pan_cc, sustain_cc;
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@
|
|||
#include <cstdlib>
|
||||
#include <cmath>
|
||||
#include <cerrno>
|
||||
#include <algorithm>
|
||||
|
||||
namespace {
|
||||
class PanelWindow : public Fl_Double_Window {
|
||||
|
|
@ -39,11 +40,22 @@ class PanelWindow : public Fl_Double_Window {
|
|||
public:
|
||||
PanelWindow(int width, int height, const char *title, MainWindow *owner)
|
||||
: Fl_Double_Window(width,height,title), owner_(owner) {}
|
||||
void resize(int x,int y,int width,int height) override {
|
||||
Fl_Double_Window::resize(x,y,width,height);
|
||||
if(owner_ && owner_->transport_window()==this)owner_->layout_transport();
|
||||
}
|
||||
int handle(int event) override {
|
||||
if (event == FL_SHORTCUT && owner_->panel_shortcut(event)) return 1;
|
||||
return Fl_Double_Window::handle(event);
|
||||
}
|
||||
};
|
||||
void restore_panel(Fl_Window *window,int x,int y,int width,int height,int min_width,int min_height)
|
||||
{
|
||||
int sx,sy,sw,sh;Fl::screen_work_area(sx,sy,sw,sh,Fl::screen_num(x,y,width,height));
|
||||
width=std::min(std::max(width,min_width),sw);height=std::min(std::max(height,min_height),sh);
|
||||
x=std::clamp(x,sx,sx+sw-width);y=std::clamp(y,sy,sy+sh-height);
|
||||
window->resize(x,y,width,height);
|
||||
}
|
||||
bool editing_number(Fl_Value_Input *input)
|
||||
{
|
||||
return Fl::focus() == input || Fl::focus() == &input->input || Fl::pushed() == input ||
|
||||
|
|
@ -254,6 +266,10 @@ MainWindow::MainWindow(bool prompt_unsaved,const char *configuration)
|
|||
if(r!=XTC_OK)w->report_error("Loop unchanged",r);},this);
|
||||
|
||||
transport_group_->end();
|
||||
for(int i=0;i<transport_group_->children();++i) {
|
||||
auto *item=transport_group_->child(i);
|
||||
transport_items_.push_back({item,item->x(),item->y()-28,item->w(),item->h()});
|
||||
}
|
||||
|
||||
tabs_ = new Fl_Tabs(216, 126, 1056, 578);
|
||||
tabs_->begin();
|
||||
|
|
@ -498,20 +514,57 @@ MainWindow::MainWindow(bool prompt_unsaved,const char *configuration)
|
|||
Fl::add_timeout(static_cast<double>(settings_.refresh_ms)/1000,timer_callback,this);
|
||||
}
|
||||
|
||||
void MainWindow::resize(int x,int y,int width,int height)
|
||||
{
|
||||
Fl_Double_Window::resize(x,y,width,height);
|
||||
if(transport_group_)layout_transport();
|
||||
}
|
||||
void MainWindow::layout_transport()
|
||||
{
|
||||
if(layout_transport_busy_ || !transport_group_ || transport_items_.empty())return;
|
||||
layout_transport_busy_=true;
|
||||
int width=transport_detached_ && transport_window_ ? transport_window_->w() : w();
|
||||
int origin=transport_detached_ ? 32 : 28;
|
||||
bool compact=transport_detached_ && width<1220;
|
||||
int left=12,top=12;
|
||||
transport_group_->resizable(nullptr);
|
||||
for(const auto &item:transport_items_) {
|
||||
int x,y;
|
||||
if(compact) {
|
||||
if(left+item.w+(item.widget==tempo_display_ ? 36 : 0)>width-12){left=12;top+=40;}
|
||||
x=left;y=origin+top;left+=item.w+(item.widget==tempo_display_ ? 36 : 0)+8;
|
||||
} else {x=item.x*width/1280;y=origin+item.y;}
|
||||
item.widget->resize(x,y,item.w,item.h);
|
||||
}
|
||||
int height=compact ? top+48 : 90;
|
||||
transport_group_->resize(0,origin,width,height);
|
||||
// Group resize can transform children: restore exact layout after it.
|
||||
left=12;top=12;
|
||||
for(const auto &item:transport_items_) {
|
||||
int x,y;if(compact){if(left+item.w+(item.widget==tempo_display_ ? 36 : 0)>width-12){left=12;top+=40;}x=left;y=origin+top;left+=item.w+(item.widget==tempo_display_ ? 36 : 0)+8;}
|
||||
else{x=item.x*width/1280;y=origin+item.y;}
|
||||
item.widget->resize(x,y,item.w,item.h);
|
||||
}
|
||||
transport_group_->init_sizes();
|
||||
if(transport_detached_ && transport_window_ && transport_window_->h()<origin+height+8)
|
||||
transport_window_->resize(transport_window_->x(),transport_window_->y(),width,origin+height+8);
|
||||
layout_transport_busy_=false;
|
||||
}
|
||||
void MainWindow::detach_transport(bool detached)
|
||||
{
|
||||
if (detached==transport_detached_ || !transport_group_) return;
|
||||
if (detached && !transport_window_) {
|
||||
auto *current=Fl_Group::current(); Fl_Group::current(nullptr);
|
||||
transport_window_.reset(new PanelWindow(w(),130,"xtc — Transport (close to dock)",this));
|
||||
transport_window_.reset(new PanelWindow(static_cast<int>(settings_.transport_width),static_cast<int>(settings_.transport_height),"xtc — Transport (close to dock)",this));
|
||||
auto *dock=new Fl_Button(12,4,140,24,"Dock transport");
|
||||
dock->callback([](Fl_Widget *,void *c){static_cast<MainWindow *>(c)->detach_transport(false);},this);
|
||||
xtc_ui::style(transport_window_.get());
|
||||
transport_window_->end(); transport_window_->size_range(1220,130,0,130);
|
||||
transport_window_->end(); transport_window_->size_range(400,130);
|
||||
transport_window_->callback([](Fl_Widget *,void *c){static_cast<MainWindow *>(c)->detach_transport(false);},this);
|
||||
restore_panel(transport_window_.get(),static_cast<int>(settings_.transport_x),static_cast<int>(settings_.transport_y),static_cast<int>(settings_.transport_width),static_cast<int>(settings_.transport_height),400,130);
|
||||
Fl_Group::current(current);
|
||||
}
|
||||
transport_detached_=detached;
|
||||
transport_detached_=detached;settings_.transport_detached=detached;
|
||||
transport_group_->parent()->remove(transport_group_);
|
||||
if (detached) {
|
||||
transport_window_->add(transport_group_);
|
||||
|
|
@ -528,6 +581,7 @@ void MainWindow::detach_transport(bool detached)
|
|||
init_sizes();
|
||||
auto *item=const_cast<Fl_Menu_Item *>(menu_->find_item("View/Detach transport"));
|
||||
if (detached) item->set(); else item->clear();
|
||||
layout_transport();
|
||||
if (detached) transport_window_->show();
|
||||
redraw();
|
||||
}
|
||||
|
|
@ -543,9 +597,10 @@ void MainWindow::detach_mixer(bool detached)
|
|||
xtc_ui::style(mixer_window_.get());
|
||||
mixer_window_->end(); mixer_window_->size_range(600,420);
|
||||
mixer_window_->callback([](Fl_Widget *,void *c){static_cast<MainWindow *>(c)->detach_mixer(false);},this);
|
||||
restore_panel(mixer_window_.get(),static_cast<int>(settings_.mixer_x),static_cast<int>(settings_.mixer_y),static_cast<int>(settings_.mixer_width),static_cast<int>(settings_.mixer_height),600,420);
|
||||
Fl_Group::current(current);
|
||||
}
|
||||
mixer_detached_=detached;
|
||||
mixer_detached_=detached;settings_.mixer_detached=detached;
|
||||
mixer_->parent()->remove(mixer_);
|
||||
if (detached) {
|
||||
mixer_page_->resizable(nullptr);
|
||||
|
|
@ -1624,7 +1679,10 @@ xtc_result MainWindow::open_piano_roll()
|
|||
xtc_ui::style(piano_window_.get());
|
||||
piano_window_->resizable(scroll);
|
||||
scroll->scroll_to(0, 60 * 16);
|
||||
settings_.width=w();settings_.height=h();
|
||||
settings_.transport_detached=transport_detached_;settings_.mixer_detached=mixer_detached_;
|
||||
if(transport_window_) {settings_.transport_x=transport_window_->x();settings_.transport_y=transport_window_->y();settings_.transport_width=transport_window_->w();settings_.transport_height=transport_window_->h();}
|
||||
if(mixer_window_) {settings_.mixer_x=mixer_window_->x();settings_.mixer_y=mixer_window_->y();settings_.mixer_width=mixer_window_->w();settings_.mixer_height=mixer_window_->h();}
|
||||
settings_.width=w();settings_.height=h();
|
||||
settings_.sequencer_snap=static_cast<int64_t>(sequencer_->grid());settings_.sequencer_zoom=sequencer_->zoom_level();
|
||||
apply_ui_preferences();
|
||||
} else
|
||||
|
|
@ -2256,6 +2314,9 @@ xtc_result MainWindow::set_loop(uint64_t start,uint64_t end,bool enabled)
|
|||
}
|
||||
void MainWindow::collect_preferences()
|
||||
{
|
||||
settings_.transport_detached=transport_detached_;settings_.mixer_detached=mixer_detached_;
|
||||
if(transport_window_) {settings_.transport_x=transport_window_->x();settings_.transport_y=transport_window_->y();settings_.transport_width=transport_window_->w();settings_.transport_height=transport_window_->h();}
|
||||
if(mixer_window_) {settings_.mixer_x=mixer_window_->x();settings_.mixer_y=mixer_window_->y();settings_.mixer_width=mixer_window_->w();settings_.mixer_height=mixer_window_->h();}
|
||||
settings_.width=w();settings_.height=h();settings_.sequencer_snap=static_cast<int64_t>(sequencer_->grid());settings_.sequencer_zoom=sequencer_->zoom_level();
|
||||
if(piano_) {
|
||||
settings_.piano_width=piano_window_->w();settings_.piano_height=piano_window_->h();settings_.piano_snap=static_cast<int64_t>(piano_->grid());
|
||||
|
|
@ -2264,6 +2325,10 @@ void MainWindow::collect_preferences()
|
|||
}
|
||||
void MainWindow::apply_ui_preferences()
|
||||
{
|
||||
bool floating_transport=settings_.transport_detached!=0,floating_mixer=settings_.mixer_detached!=0;
|
||||
detach_transport(floating_transport);detach_mixer(floating_mixer);
|
||||
if(transport_window_)restore_panel(transport_window_.get(),static_cast<int>(settings_.transport_x),static_cast<int>(settings_.transport_y),static_cast<int>(settings_.transport_width),static_cast<int>(settings_.transport_height),400,130);
|
||||
if(mixer_window_)restore_panel(mixer_window_.get(),static_cast<int>(settings_.mixer_x),static_cast<int>(settings_.mixer_y),static_cast<int>(settings_.mixer_width),static_cast<int>(settings_.mixer_height),600,420);
|
||||
follow_->value(static_cast<int>(settings_.follow_playhead));
|
||||
resize(x(),y(),static_cast<int>(settings_.width),static_cast<int>(settings_.height));
|
||||
sequencer_->grid(static_cast<uint64_t>(settings_.sequencer_snap));sequencer_->set_zoom(static_cast<int>(settings_.sequencer_zoom));
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
#include <FL/Fl_Double_Window.H>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include "xtc/xtc_project.h"
|
||||
#include "xtc/xtc_backend.h"
|
||||
|
|
@ -35,6 +36,8 @@ class MainWindow : public Fl_Double_Window {
|
|||
xtc_result set_tempo(unsigned int milli_bpm);
|
||||
void show_commands();
|
||||
/* Reparent the existing controls on the UI thread; closing a panel docks it. */
|
||||
void layout_transport();
|
||||
void resize(int x,int y,int width,int height) override;
|
||||
void detach_transport(bool detached);
|
||||
void detach_mixer(bool detached);
|
||||
Fl_Double_Window *transport_window() const { return transport_window_.get(); }
|
||||
|
|
@ -203,6 +206,9 @@ class MainWindow : public Fl_Double_Window {
|
|||
std::unique_ptr<Fl_Double_Window> transport_window_, mixer_window_;
|
||||
Fl_Group *transport_group_=nullptr, *mixer_page_=nullptr, *sidebar_=nullptr;
|
||||
Fl_Group *mixer_placeholder_=nullptr;
|
||||
struct TransportItem {Fl_Widget *widget;int x,y,w,h;};
|
||||
std::vector<TransportItem> transport_items_;
|
||||
bool layout_transport_busy_=false;
|
||||
bool transport_detached_=false, mixer_detached_=false;
|
||||
xtc_settings settings_{};
|
||||
std::string configuration_;
|
||||
|
|
|
|||
|
|
@ -23,6 +23,12 @@ static const xtc_setting_info schema[] = {
|
|||
STR("plugins",ladspa_paths,"LADSPA search paths (colon-separated)"),
|
||||
NUM("interface",width,"Main window width",1220,3840), NUM("interface",height,"Main window height",720,2160),
|
||||
NUM("interface",piano_width,"Piano window width",800,3840), NUM("interface",piano_height,"Piano window height",520,2160),
|
||||
NUM("interface",transport_detached,"Floating transport: 0 or 1",0,1),
|
||||
NUM("interface",mixer_detached,"Floating mixer: 0 or 1",0,1),
|
||||
NUM("interface",transport_x,"Transport window X",-32768,32768),NUM("interface",transport_y,"Transport window Y",-32768,32768),
|
||||
NUM("interface",transport_width,"Transport width",400,3840),NUM("interface",transport_height,"Transport height",130,2160),
|
||||
NUM("interface",mixer_x,"Mixer window X",-32768,32768),NUM("interface",mixer_y,"Mixer window Y",-32768,32768),
|
||||
NUM("interface",mixer_width,"Mixer window width",600,3840),NUM("interface",mixer_height,"Mixer window height",420,2160),
|
||||
NUM("interface",refresh_ms,"Display refresh interval, ms",20,1000),
|
||||
NUM("editing",sequencer_snap,"Sequencer snap ticks (1 means Off)",1,3840),
|
||||
NUM("editing",piano_snap,"Piano snap ticks (1 means Off)",1,3840),
|
||||
|
|
@ -54,6 +60,8 @@ void xtc_settings_defaults(xtc_settings *s)
|
|||
strcpy(s->ladspa_paths,"~/.ladspa:/usr/local/lib/ladspa:/usr/lib/ladspa");
|
||||
s->max_frames=65536; s->channels=2; s->transition_ms=5; s->offline_rate=48000; s->recording_seconds=2;
|
||||
s->width=1280; s->height=800; s->piano_width=900; s->piano_height=620; s->refresh_ms=100;
|
||||
s->transport_x=s->transport_y=80;s->transport_width=800;s->transport_height=300;
|
||||
s->mixer_x=s->mixer_y=100;s->mixer_width=1056;s->mixer_height=600;
|
||||
s->sequencer_snap=s->piano_snap=240; s->sequencer_zoom=48; s->clip_ticks=3840; s->note_ticks=960;
|
||||
s->note_velocity=100; s->filter_channel=-1; s->filter_high=127; s->tempo=120000;
|
||||
s->volume_cc=7; s->pan_cc=10; s->sustain_cc=64; s->loop_end=3840;s->tail_ms=3000;
|
||||
|
|
|
|||
|
|
@ -1237,6 +1237,35 @@ int main(int argc, char **argv)
|
|||
close_result=XTC_OK; window.do_callback();
|
||||
CHECK(!window.shown() && !window.transport_window()->shown() && !window.mixer_window()->shown());
|
||||
}
|
||||
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());
|
||||
}
|
||||
if (failures != 0) return 1;
|
||||
std::printf("GUI controls/errors/views/timers/cleanup passed: %u opens, %u closes.\n", opens, closes);
|
||||
return 0;
|
||||
|
|
|
|||
|
|
@ -13,13 +13,13 @@ int main(void)
|
|||
CHECK(fd>=0);close(fd);unlink(path);
|
||||
xtc_settings_defaults(&a);CHECK(xtc_settings_validate(&a)==XTC_OK);
|
||||
b=a;CHECK(xtc_settings_load(&b,path)==XTC_ERR_NOT_FOUND);
|
||||
strcpy(a.clap_paths,"~/space folder:/tmp/quoted\"name\\path");a.loop_enabled=1;a.follow_playhead=1;a.note_velocity=87;
|
||||
strcpy(a.clap_paths,"~/space folder:/tmp/quoted\"name\\path");a.loop_enabled=1;a.follow_playhead=1;a.transport_detached=1;a.mixer_detached=1;a.transport_x=-30000;a.transport_width=400;a.note_velocity=87;
|
||||
CHECK(xtc_settings_save(&a,path)==XTC_OK);CHECK(stat(path,&st)==0 && (st.st_mode&0777)==0600);
|
||||
CHECK(xtc_settings_load(&b,path)==XTC_OK);CHECK(memcmp(&a,&b,sizeof(a))==0);
|
||||
xtc_settings_schema(&n);
|
||||
for(i=0;i<n;++i){CHECK(xtc_settings_get(&a,i,value,sizeof(value))==XTC_OK);CHECK(xtc_settings_set(&b,i,value)==XTC_OK);}
|
||||
CHECK(memcmp(&a,&b,sizeof(a))==0);
|
||||
{const char *bad[]={"[unknown]\n","[audio]\nchannels=3\n","[audio]\nchannels=1\nchannels=2\n","[audio]\nchannels=9999999999999999999999\n","[plugins]\nclap_paths=\"unterminated\n","[transport]\nloop_start=4000\nloop_end=2\n"};
|
||||
{const char *bad[]={"[unknown]\n","[interface]\ntransport_width=399\n","[interface]\nmixer_detached=2\n","[audio]\nchannels=3\n","[audio]\nchannels=1\nchannels=2\n","[audio]\nchannels=9999999999999999999999\n","[plugins]\nclap_paths=\"unterminated\n","[transport]\nloop_start=4000\nloop_end=2\n"};
|
||||
for(i=0;i<sizeof(bad)/sizeof(bad[0]);++i){f=fopen(path,"wb");CHECK(f);CHECK(fputs(bad[i],f)>=0);CHECK(fclose(f)==0);before=b;CHECK(xtc_settings_load(&b,path)==XTC_ERR_FORMAT);CHECK(memcmp(&before,&b,sizeof(b))==0);}}
|
||||
f=fopen(path,"wb");CHECK(f);CHECK(fwrite(";x\0hidden",1,9,f)==9);fclose(f);CHECK(xtc_settings_load(&b,path)==XTC_ERR_FORMAT);
|
||||
CHECK(xtc_settings_save(&a,path)==XTC_OK);b=a;b.loop_end=b.loop_start;CHECK(xtc_settings_save(&b,path)==XTC_ERR_INVALID_ARGUMENT);CHECK(xtc_settings_load(&b,path)==XTC_OK && b.loop_end==a.loop_end);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue