Browse Source

fixed circular graph bug, more help, help resizable/slidable, xfade fix

master
nebogeo 22 years ago
parent
commit
6ea8e9f208
23 changed files with 207 additions and 37 deletions
  1. +67
    -22
      GUI/Widgets/Fl_Canvas.C
  2. +4
    -0
      GUI/Widgets/Fl_Canvas.h
  3. +3
    -2
      GUI/Widgets/Fl_DeviceGUI.C
  4. +5
    -1
      GUI/Widgets/Fl_DeviceGUI.h
  5. +21
    -1
      GraphSort.C
  6. +2
    -1
      GraphSort.h
  7. +8
    -0
      SpiralSound/Plugins/FilterPlugin/FilterPluginGUI.C
  8. +3
    -0
      SpiralSound/Plugins/FilterPlugin/FilterPluginGUI.h
  9. +10
    -2
      SpiralSound/Plugins/JackPlugin/JackPlugin.C
  10. +9
    -0
      SpiralSound/Plugins/MoogFilterPlugin/MoogFilterPluginGUI.C
  11. +3
    -0
      SpiralSound/Plugins/MoogFilterPlugin/MoogFilterPluginGUI.h
  12. +16
    -0
      SpiralSound/Plugins/OscillatorPlugin/OscillatorPluginGUI.C
  13. +3
    -0
      SpiralSound/Plugins/OscillatorPlugin/OscillatorPluginGUI.h
  14. +4
    -1
      SpiralSound/Plugins/OutputPlugin/OutputPlugin.C
  15. +14
    -0
      SpiralSound/Plugins/SVFilterPlugin/SVFilterPluginGUI.C
  16. +3
    -0
      SpiralSound/Plugins/SVFilterPlugin/SVFilterPluginGUI.h
  17. +1
    -0
      SpiralSound/Plugins/SpiralPlugin.C
  18. +4
    -0
      SpiralSound/Plugins/SpiralPlugin.h
  19. +8
    -5
      SpiralSound/Plugins/SpiralPluginGUI.C
  20. +14
    -0
      SpiralSound/Plugins/WaveTablePlugin/WaveTablePluginGUI.C
  21. +3
    -0
      SpiralSound/Plugins/WaveTablePlugin/WaveTablePluginGUI.h
  22. +1
    -1
      SpiralSound/Plugins/XFadePlugin/XFadePluginGUI.C
  23. +1
    -1
      SpiralSynthModular.C

+ 67
- 22
GUI/Widgets/Fl_Canvas.C View File

@@ -38,8 +38,10 @@ m_UpdateTimer(0)
{
m_IncompleteWire.OutputChild=-1;
m_IncompleteWire.OutputPort=-1;
m_IncompleteWire.OutputTerminal=false;
m_IncompleteWire.InputChild=-1;
m_IncompleteWire.InputPort=-1;
m_IncompleteWire.InputTerminal=false;
m_BG=NULL;
m_BGData=NULL;
@@ -364,6 +366,7 @@ void Fl_Canvas::PortClicked(Fl_DeviceGUI* Device, int Type, int Port, bool Value
m_IncompleteWire.OutputChild=ChildNum;
m_IncompleteWire.OutputPort=Port;
m_IncompleteWire.OutputID=Device->GetID();
m_IncompleteWire.OutputTerminal=Device->IsTerminal();
}
else
{
@@ -378,6 +381,7 @@ void Fl_Canvas::PortClicked(Fl_DeviceGUI* Device, int Type, int Port, bool Value
m_IncompleteWire.InputChild=ChildNum;
m_IncompleteWire.InputPort=Port;
m_IncompleteWire.InputID=Device->GetID();
m_IncompleteWire.InputTerminal=Device->IsTerminal();
}
else
{
@@ -392,7 +396,8 @@ void Fl_Canvas::PortClicked(Fl_DeviceGUI* Device, int Type, int Port, bool Value
// send the connect callback
cb_Connection(this,(void*)&m_IncompleteWire);
m_Graph.AddConnection(m_IncompleteWire.OutputID,m_IncompleteWire.InputID);
m_Graph.AddConnection(m_IncompleteWire.OutputID,m_IncompleteWire.OutputTerminal,
m_IncompleteWire.InputID,m_IncompleteWire.InputTerminal);
// Turn on both ports
Fl_DeviceGUI* ODGUI = (Fl_DeviceGUI*)(child(m_IncompleteWire.OutputChild));
@@ -557,30 +562,65 @@ istream &operator>>(istream &s, Fl_Canvas &o)
{
int NumWires;
s>>NumWires;
for(int n=0; n<NumWires; n++)
// my bad, didn't version this stream - remove one day...
if (NumWires==-1)
{
CanvasWire NewWire;
int version;
s>>version;
s>>NumWires;
s>>NewWire.OutputID;
s>>NewWire.OutputChild;
s>>NewWire.OutputPort;
s>>NewWire.InputID;
s>>NewWire.InputChild;
s>>NewWire.InputPort;

o.m_WireVec.push_back(NewWire);

// Notify connection by callback
o.cb_Connection(&o,(void*)&NewWire);
o.m_Graph.AddConnection(NewWire.OutputID,NewWire.InputID);
for(int n=0; n<NumWires; n++)
{
CanvasWire NewWire;
// Turn on both ports
((Fl_DeviceGUI*)(o.child(NewWire.OutputChild)))->AddConnection(NewWire.OutputPort+
((Fl_DeviceGUI*)(o.child(NewWire.OutputChild)))->GetInfo()->NumInputs);
((Fl_DeviceGUI*)(o.child(NewWire.InputChild)))->AddConnection(NewWire.InputPort);
}
s>>NewWire.OutputID;
s>>NewWire.OutputChild;
s>>NewWire.OutputPort;
s>>NewWire.OutputTerminal;
s>>NewWire.InputID;
s>>NewWire.InputChild;
s>>NewWire.InputPort;
s>>NewWire.InputTerminal;
o.m_WireVec.push_back(NewWire);

// Notify connection by callback
o.cb_Connection(&o,(void*)&NewWire);
o.m_Graph.AddConnection(NewWire.OutputID,NewWire.OutputTerminal,NewWire.InputID,NewWire.InputTerminal);
// Turn on both ports
((Fl_DeviceGUI*)(o.child(NewWire.OutputChild)))->AddConnection(NewWire.OutputPort+
((Fl_DeviceGUI*)(o.child(NewWire.OutputChild)))->GetInfo()->NumInputs);
((Fl_DeviceGUI*)(o.child(NewWire.InputChild)))->AddConnection(NewWire.InputPort);
}
}
else
{
for(int n=0; n<NumWires; n++)
{
CanvasWire NewWire;
s>>NewWire.OutputID;
s>>NewWire.OutputChild;
s>>NewWire.OutputPort;
s>>NewWire.InputID;
s>>NewWire.InputChild;
s>>NewWire.InputPort;
o.m_WireVec.push_back(NewWire);

// Notify connection by callback
o.cb_Connection(&o,(void*)&NewWire);
o.m_Graph.AddConnection(NewWire.OutputID,false,NewWire.InputID,false);
// Turn on both ports
((Fl_DeviceGUI*)(o.child(NewWire.OutputChild)))->AddConnection(NewWire.OutputPort+
((Fl_DeviceGUI*)(o.child(NewWire.OutputChild)))->GetInfo()->NumInputs);
((Fl_DeviceGUI*)(o.child(NewWire.InputChild)))->AddConnection(NewWire.InputPort);
}
}
return s;
}

@@ -588,6 +628,9 @@ istream &operator>>(istream &s, Fl_Canvas &o)

ostream &operator<<(ostream &s, Fl_Canvas &o)
{
int version=0;
s<<-1<<" "<<version<<" ";
s<<o.m_WireVec.size()<<endl;

for(vector<CanvasWire>::iterator i=o.m_WireVec.begin();
@@ -596,9 +639,11 @@ ostream &operator<<(ostream &s, Fl_Canvas &o)
s<<i->OutputID<<" ";
s<<i->OutputChild<<" ";
s<<i->OutputPort<<" ";
s<<i->OutputTerminal<<" ";
s<<i->InputID<<" ";
s<<i->InputChild<<" ";
s<<i->InputPort<<endl;
s<<i->InputPort<<" ";
s<<i->InputTerminal<<endl;
}

return s;


+ 4
- 0
GUI/Widgets/Fl_Canvas.h View File

@@ -40,18 +40,22 @@ public:
OutputChild=-1;
OutputPort=-1;
OutputID=-1;
OutputTerminal=false;
InputChild=-1;
InputPort=-1;
InputID=-1;
InputTerminal=false;
DelMe=false;
}

int OutputID;
int OutputChild;
int OutputPort;
bool OutputTerminal;
int InputID;
int InputChild;
int InputPort;
bool InputTerminal;
bool DelMe;
};



+ 3
- 2
GUI/Widgets/Fl_DeviceGUI.C View File

@@ -51,13 +51,14 @@ int Fl_PortButton::handle(int event)
return 1;
}

Fl_DeviceGUI::Fl_DeviceGUI(const DeviceGUIInfo& Info, Fl_Group *PW, Fl_Pixmap *Icon) :
Fl_DeviceGUI::Fl_DeviceGUI(const DeviceGUIInfo& Info, Fl_Group *PW, Fl_Pixmap *Icon, bool Terminal) :
Fl_Group(Info.XPos, Info.YPos, Info.Width+(PortGroupWidth*2), Info.Height+TitleBarHeight, ""),
m_PluginWindow(NULL),
m_Icon(NULL),
m_Name(Info.Name),
m_ID(-1),
m_DelMe(false)
m_DelMe(false),
m_IsTerminal(Terminal)
{
for (int n=0; n<512; n++) Numbers[n]=n;


+ 5
- 1
GUI/Widgets/Fl_DeviceGUI.h View File

@@ -79,7 +79,7 @@ struct DeviceGUIInfo
class Fl_DeviceGUI : public Fl_Group
{
public:
Fl_DeviceGUI(const DeviceGUIInfo& Info, Fl_Group *PW, Fl_Pixmap *Icon);
Fl_DeviceGUI(const DeviceGUIInfo& Info, Fl_Group *PW, Fl_Pixmap *Icon, bool Terminal=false);
virtual int handle(int event);
virtual void draw();
@@ -107,6 +107,9 @@ public:
virtual void Clear();
int GetPortType(int n) { return m_Info.PortTypes[n]; }
// do we belong to a plugin that is an output?
bool IsTerminal() { return m_IsTerminal; }

protected:

DeviceGUIInfo m_Info;
@@ -127,6 +130,7 @@ private:
string m_Name;
int m_ID;
bool m_DelMe;
bool m_IsTerminal;
};

#endif

+ 21
- 1
GraphSort.C View File

@@ -48,16 +48,34 @@ void GraphSort::Sort()
// walk back from all the roots
m_Sorted.clear();
list<int> RootNodes;
bool FoundRoot=false;
for (map<int,Node>::iterator i=m_Graph.begin();
i!=m_Graph.end(); i++)
{
// if there are no outputs, this must be a root
if (i->second.Outputs.empty())
{
FoundRoot=true;
RecursiveWalk(i->first);
}
}
// no roots found - try looking for a terminal node and recursing from
// there, this makes circular graphs work.
if (!FoundRoot)
{
for (map<int,Node>::iterator i=m_Graph.begin();
i!=m_Graph.end(); i++)
{
// if there are no outputs, this must be a root
if (i->second.IsTerminal)
{
RecursiveWalk(i->first);
}
}
}
#ifdef GRAPHSORT_TRACE
for(list<int>::iterator i=m_Sorted.begin();
i!=m_Sorted.end(); i++)
@@ -121,12 +139,13 @@ void GraphSort::Dump()
}
}

void GraphSort::AddConnection(int SID, int DID)
void GraphSort::AddConnection(int SID, bool STerminal, int DID, bool DTerminal)
{
map<int,Node>::iterator si=m_Graph.find(SID);
if (si==m_Graph.end())
{
Node newnode;
newnode.IsTerminal = STerminal;
m_Graph[SID]=newnode;
#ifdef GRAPHSORT_TRACE
cerr<<"added "<<SID<<endl;
@@ -137,6 +156,7 @@ void GraphSort::AddConnection(int SID, int DID)
if (di==m_Graph.end())
{
Node newnode;
newnode.IsTerminal = DTerminal;
m_Graph[DID]=newnode;
#ifdef GRAPHSORT_TRACE
cerr<<"added "<<DID<<endl;


+ 2
- 1
GraphSort.h View File

@@ -34,7 +34,7 @@ public:
~GraphSort();
const list<int> &GetSortedList();
void Sort();
void AddConnection(int SID, int DID);
void AddConnection(int SID, bool STerminal, int DID, bool DTerminal);
void RemoveConnection(int SID, int DID);
void Clear();
void Dump();
@@ -43,6 +43,7 @@ public:
{
list<int> Inputs;
list<int> Outputs;
bool IsTerminal;
};
private:


+ 8
- 0
SpiralSound/Plugins/FilterPlugin/FilterPluginGUI.C View File

@@ -81,6 +81,7 @@ inline void FilterPluginGUI::cb_Cutoff_i(Fl_Slider* o, void* v)
float value=100.0f-o->value();
m_GUICH->Set("Cutoff",(float)(value*value)+10.0f);
}

void FilterPluginGUI::cb_Cutoff(Fl_Slider* o, void* v)
{ ((FilterPluginGUI*)(o->parent()))->cb_Cutoff_i(o,v); }

@@ -99,3 +100,10 @@ inline void FilterPluginGUI::cb_RevResonance_i(Fl_Button* o, void* v)
void FilterPluginGUI::cb_RevResonance(Fl_Button* o, void* v)
{ ((FilterPluginGUI*)(o->parent()))->cb_RevResonance_i(o,v); }
const string FilterPluginGUI::GetHelpText(const string &loc){
return string("")
+ "The standard SpiralSynth filter, based on the (zxforms design).\n"
+ "Quite a meaty sound - low pass only, nice for bass modulations.\n"
+ "With variable emphasis/cutoff CV's.\n\n"
+ "It's also pretty fast, and well tested in SpiralSynth.";
}

+ 3
- 0
SpiralSound/Plugins/FilterPlugin/FilterPluginGUI.h View File

@@ -36,6 +36,9 @@ public:
virtual void UpdateValues(SpiralPlugin *o);
protected:
const string GetHelpText(const string &loc);
private:
Fl_Group *GUIFilterGroup;


+ 10
- 2
SpiralSound/Plugins/JackPlugin/JackPlugin.C View File

@@ -154,7 +154,7 @@ int JackClient::Process(jack_nframes_t nframes, void *o)
}
}
}
if(RunCallback&&RunContext)
{
// do the work
@@ -345,6 +345,9 @@ m_Connected(false)
{
m_RefCount++;
// we are an output
m_IsTerminal = true;
m_PluginInfo.Name="Jack";
m_PluginInfo.Width=200;
m_PluginInfo.Height=325;
@@ -390,7 +393,7 @@ PluginInfo &JackPlugin::Initialise(const HostInfo *Host)
PluginInfo& Info= SpiralPlugin::Initialise(Host);
host=Host;
JackClient::Get()->SetCallback(cb_Update,m_Parent);
if (m_RefCount==1) JackClient::Get()->SetCallback(cb_Update,m_Parent);

return Info;
}
@@ -409,6 +412,11 @@ void JackPlugin::Execute()

void JackPlugin::ExecuteCommands()
{
// only do this once per set of plugins
m_NoExecuted++;
if (m_NoExecuted!=m_RefCount) return;
m_NoExecuted=0;
// we want to process this whether we are connected to stuff or not
JackClient* pJack=JackClient::Get();


+ 9
- 0
SpiralSound/Plugins/MoogFilterPlugin/MoogFilterPluginGUI.C View File

@@ -71,3 +71,12 @@ inline void MoogFilterPluginGUI::cb_Resonance_i(Fl_Knob* o, void* v)
{ m_GUICH->Set("Resonance",(float)(o->value())); }
void MoogFilterPluginGUI::cb_Resonance(Fl_Knob* o, void* v)
{ ((MoogFilterPluginGUI*)(o->parent()))->cb_Resonance_i(o,v); }

const string MoogFilterPluginGUI::GetHelpText(const string &loc){
return string("")
+ "Classic moog filter. Very different sound to the other filters,\n"
+ "needless to say, very squelchy. As well as lowpass, band and high\n"
+ "pass are simultaneously calculated too. The emphasis can be pushed\n"
+ "into self oscillation (careful of the speakers). In this way, it\n"
+ "can be used to generate sinewave oscillations.";
}

+ 3
- 0
SpiralSound/Plugins/MoogFilterPlugin/MoogFilterPluginGUI.h View File

@@ -36,6 +36,9 @@ public:
virtual void UpdateValues(SpiralPlugin *o);
protected:
const string GetHelpText(const string &loc);
private:
Fl_Group *GUIFilterGroup;


+ 16
- 0
SpiralSound/Plugins/OscillatorPlugin/OscillatorPluginGUI.C View File

@@ -372,3 +372,19 @@ void OscillatorPluginGUI::cb_pop(Fl_Button* o, void* v) {
((OscillatorPluginGUI*)(o->parent()))->cb_pop_i(o,v);
}
const string OscillatorPluginGUI::GetHelpText(const string &loc){
return string("")
+ "The Oscillator generates raw waveforms from CV controls. Three wave \n"
+ "shapes are included, Square wave, Triangle wave and white noise.\n\n"
+ "In the square and triangle shapes, the Frequency CV controls the pitch \n"
+ "of the signal generated, and the pulsewidth turns the squarewave into \n"
+ "a pulse wave of varying harmonics, and the triangle wave into a sawtooth,\n"
+ "or reverse sawtooth wave.\n\n"
+ "The sample & hold CV changes the time between samples with the white noise.\n"
+ "This is usful for making the Oscillator into a random CV generator.\n\n"
+ "The plugin window allows you to select the wave shape, set the octave and\n"
+ "fine tune the frequency. There are also controls to set the pulsewidth,\n"
+ "sample and hold manually, and control the modulation depth of the input CV's.\n\n"
+ "The frequency can be set extremely low on this oscillator, so you can use\n"
+ "it as an LFO for controlling other plugins.";
}

+ 3
- 0
SpiralSound/Plugins/OscillatorPlugin/OscillatorPluginGUI.h View File

@@ -37,6 +37,9 @@ public:
virtual void UpdateValues(SpiralPlugin *o);
protected:
const string GetHelpText(const string &loc);
private:

Fl_Check_Button *ShapeSquare;


+ 4
- 1
SpiralSound/Plugins/OutputPlugin/OutputPlugin.C View File

@@ -87,7 +87,10 @@ OutputPlugin::OutputPlugin() :
m_Volume(1.0f)
{
m_RefCount++;
// we are an output.
m_IsTerminal=true;
m_PluginInfo.Name="OSS";
m_PluginInfo.Width=100;
m_PluginInfo.Height=100;


+ 14
- 0
SpiralSound/Plugins/SVFilterPlugin/SVFilterPluginGUI.C View File

@@ -80,3 +80,17 @@ inline void SVFilterPluginGUI::cb_Reset_i(Fl_Button* o, void* v)
{ }
void SVFilterPluginGUI::cb_Reset(Fl_Button* o, void* v)
{ ((SVFilterPluginGUI*)(o->parent()))->cb_Reset_i(o,v); }

const string SVFilterPluginGUI::GetHelpText(const string &loc){
return string("")
+ "A State Variable Filter. First thing to say is, it's a bit\n"
+ "broken. Seems to generate glitchy noise when the cutoff is \n"
+ "modulated. Possibly a range bug on the cutoff too.\n"
+ "On the other hand, I like some of the noises it seems to\n"
+ "make, so it's here anyway (I'll fix it some day).\n\n"
+ "Works pretty well at band,high and peaking useful for creating\n"
+ "some different sounds.\n\n"
+ "Note: Comes with a reset button, so if you break it pushing\n"
+ "the emphasis up too high, you can reset the cooeficients\n"
+ "(which fixes it).";
}

+ 3
- 0
SpiralSound/Plugins/SVFilterPlugin/SVFilterPluginGUI.h View File

@@ -36,6 +36,9 @@ public:
virtual void UpdateValues(SpiralPlugin *o);
protected:
const string GetHelpText(const string &loc);
private:
Fl_Group *GUIFilterGroup;


+ 1
- 0
SpiralSound/Plugins/SpiralPlugin.C View File

@@ -30,6 +30,7 @@ SpiralPlugin::SpiralPlugin()
cb_Update=NULL;
m_Parent=NULL;
m_HostID=-1;
m_IsTerminal=false;
m_AudioCH = new ChannelHandler;
}


+ 4
- 0
SpiralSound/Plugins/SpiralPlugin.h View File

@@ -104,6 +104,8 @@ public:
void SetParent(void *s) { m_Parent=s; }

void UpdateChannelHandler();
// is the plugin connected to an external device (oss/alsa/jack)
bool IsTerminal() { return m_IsTerminal; }
ChannelHandler *GetChannelHandler() { return m_AudioCH; }
@@ -153,6 +155,8 @@ protected:
// tell the engine that we are taking control of the
// timing for output.
void (*cb_Blocking)(void*o ,bool m);

bool m_IsTerminal;
private:



+ 8
- 5
SpiralSound/Plugins/SpiralPluginGUI.C View File

@@ -21,6 +21,8 @@
#include <FL/fl_draw.h>
#include <FL/fl_draw.H>
#include <FL/Fl_Multiline_Output.h>
#include <FL/Fl_Text_Display.h>
#include <FL/Fl_Text_Buffer.h>

static const int GUI_COLOUR = 154;
static const int GUIBG_COLOUR = 144;
@@ -83,14 +85,15 @@ inline void SpiralPluginGUI::cb_Help_i(Fl_Button* o, void* v)
{
int w=300,h=200;
m_HelpWin = new Fl_Double_Window(w,h,"Help");
Fl_Multiline_Output* text= new Fl_Multiline_Output(0,0,w,h);
text->value(GetHelpText(SpiralInfo::LOCALE).c_str());

Fl_Text_Display* text = new Fl_Text_Display(0,0,10,10);
text->buffer(new Fl_Text_Buffer);
text->insert(GetHelpText(SpiralInfo::LOCALE).c_str());
text->textsize(10);
text->set_output();
m_HelpWin->add(text);
m_HelpWin->resizable(text);
m_HelpWin->show();
text->size(w,h); // hack to get the text widget to appear???
}
else
{


+ 14
- 0
SpiralSound/Plugins/WaveTablePlugin/WaveTablePluginGUI.C View File

@@ -504,3 +504,17 @@ void WaveTablePluginGUI::cb_pop(Fl_Button* o, void* v) {
((WaveTablePluginGUI*)o->parent())->cb_pop_i(o,v);
}
const string WaveTablePluginGUI::GetHelpText(const string &loc){
return string("")
+ "The WaveTable plugin is a fast multifunction oscillator with a variety \n"
+ "of wave shapes:\n"
+ "Sine, Square, Saw, Reverse Saw, Triangle, Two pulse shapes and an inverse\n"
+ "sinewave.\n\n"
+ "These wave shapes are internally represented as samples, rather than\n"
+ "being continually calculated like the conventional oscillator. This \n"
+ "makes the plugin fast, but restricts the modulations you can do on the\n"
+ "wave forms (no pulsewidth).\n\n"
+ "The oscillator can be pitched very low for use as a LFO CV generator,\n"
+ "using any of the supported wave shapes. User wave shapes are planned,\n"
+ "so you will be able to load your own samples in.";
}

+ 3
- 0
SpiralSound/Plugins/WaveTablePlugin/WaveTablePluginGUI.h View File

@@ -36,6 +36,9 @@ public:
virtual void UpdateValues(SpiralPlugin* o);
protected:
const string GetHelpText(const string &loc);
private:
Fl_Check_Button *ShapeSquare;


+ 1
- 1
SpiralSound/Plugins/XFadePlugin/XFadePluginGUI.C View File

@@ -49,7 +49,7 @@ void XFadePluginGUI::UpdateValues(SpiralPlugin *o)
inline void XFadePluginGUI::cb_Mix_i(Fl_Slider* o, void* v)
{
m_GUICH->Set("Mix",o->value());
m_GUICH->Set("Mix",(float)o->value());
}
void XFadePluginGUI::cb_Mix(Fl_Slider* o, void* v)
{ ((XFadePluginGUI*)(o->parent()))->cb_Mix_i(o,v); }


+ 1
- 1
SpiralSynthModular.C View File

@@ -567,7 +567,7 @@ DeviceWin* SynthModular::NewDeviceWin(int n, int x, int y)
Info.XPos = x; //TOOLBOX_WIDTH+(rand()%400);
Info.YPos = y; //rand()%400;
nlw->m_DeviceGUI = new Fl_DeviceGUI(Info, temp, Pix);
nlw->m_DeviceGUI = new Fl_DeviceGUI(Info, temp, Pix, nlw->m_Device->IsTerminal());
m_Canvas->add(nlw->m_DeviceGUI);
m_Canvas->redraw();



Loading…
Cancel
Save