Merge branch 'master' of https://github.com/tildearrow/furnace into x1_010_bank
This commit is contained in:
commit
b1e2e33f2d
431 changed files with 25341 additions and 13648 deletions
|
|
@ -78,11 +78,14 @@ const char* aboutLine[]={
|
|||
"Dippy",
|
||||
"djtuBIG-MaliceX",
|
||||
"dumbut",
|
||||
"ElectricKeet",
|
||||
"EpicTyphlosion",
|
||||
"FΛDE",
|
||||
"Forte",
|
||||
"Fragmare",
|
||||
"freq-mod",
|
||||
"gtr3qq",
|
||||
"Hortus",
|
||||
"iyatemu",
|
||||
"JayBOB18",
|
||||
"Jimmy-DS",
|
||||
|
|
@ -100,6 +103,7 @@ const char* aboutLine[]={
|
|||
"MelonadeM",
|
||||
"Miker",
|
||||
"nicco1690",
|
||||
"niffuM",
|
||||
"<nk>",
|
||||
"NyaongI",
|
||||
"potatoTeto",
|
||||
|
|
@ -120,6 +124,7 @@ const char* aboutLine[]={
|
|||
"Ultraprogramer",
|
||||
"UserSniper",
|
||||
"Weeppiko",
|
||||
"Xan",
|
||||
"Yuzu4K",
|
||||
"Zaxolotl",
|
||||
"ZoomTen (Zumi)",
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@
|
|||
#include "../ta-log.h"
|
||||
#include "imgui.h"
|
||||
#include "imgui_internal.h"
|
||||
#include "misc/cpp/imgui_stdlib.h"
|
||||
|
||||
#define FURNACE_FFT_SIZE 4096
|
||||
#define FURNACE_FFT_RATE 80.0
|
||||
|
|
@ -93,7 +94,7 @@ void FurnaceGUI::calcChanOsc() {
|
|||
unsigned short needlePos=buf->needle;
|
||||
needlePos-=displaySize;
|
||||
for (unsigned short i=0; i<512; i++) {
|
||||
float y=(float)buf->data[(unsigned short)(needlePos+(i*displaySize/512))]/65536.0f;
|
||||
float y=(float)buf->data[(unsigned short)(needlePos+(i*displaySize/512))]/32768.0f;
|
||||
if (minLevel>y) minLevel=y;
|
||||
if (maxLevel<y) maxLevel=y;
|
||||
}
|
||||
|
|
@ -116,7 +117,7 @@ void FurnaceGUI::drawChanOsc() {
|
|||
}
|
||||
if (!chanOscOpen) return;
|
||||
ImGui::SetNextWindowSizeConstraints(ImVec2(64.0f*dpiScale,32.0f*dpiScale),ImVec2(canvasW,canvasH));
|
||||
if (ImGui::Begin("Oscilloscope (per-channel)",&chanOscOpen,globalWinFlags|((chanOscOptions)?0:ImGuiWindowFlags_NoTitleBar))) {
|
||||
if (ImGui::Begin("Oscilloscope (per-channel)",&chanOscOpen,globalWinFlags)) {
|
||||
bool centerSettingReset=false;
|
||||
ImDrawList* dl=ImGui::GetWindowDrawList();
|
||||
if (chanOscOptions) {
|
||||
|
|
@ -152,10 +153,10 @@ void FurnaceGUI::drawChanOsc() {
|
|||
|
||||
if (chanOscUseGrad) {
|
||||
if (chanOscGradTex==NULL) {
|
||||
chanOscGradTex=SDL_CreateTexture(sdlRend,SDL_PIXELFORMAT_ABGR8888,SDL_TEXTUREACCESS_STREAMING,chanOscGrad.width,chanOscGrad.height);
|
||||
chanOscGradTex=rend->createTexture(true,chanOscGrad.width,chanOscGrad.height);
|
||||
|
||||
if (chanOscGradTex==NULL) {
|
||||
logE("error while creating gradient texture! %s",SDL_GetError());
|
||||
logE("error while creating gradient texture!");
|
||||
} else {
|
||||
updateChanOscGradTex=true;
|
||||
}
|
||||
|
|
@ -170,16 +171,16 @@ void FurnaceGUI::drawChanOsc() {
|
|||
if (chanOscGradTex!=NULL) {
|
||||
if (updateChanOscGradTex) {
|
||||
chanOscGrad.render();
|
||||
if (SDL_UpdateTexture(chanOscGradTex,NULL,chanOscGrad.grad.get(),chanOscGrad.width*4)==0) {
|
||||
if (rend->updateTexture(chanOscGradTex,chanOscGrad.grad.get(),chanOscGrad.width*4)) {
|
||||
updateChanOscGradTex=false;
|
||||
} else {
|
||||
logE("error while updating gradient texture! %s",SDL_GetError());
|
||||
logE("error while updating gradient texture!");
|
||||
}
|
||||
}
|
||||
|
||||
ImVec2 gradLeft=ImGui::GetCursorPos();
|
||||
ImVec2 gradSize=ImVec2(400.0f*dpiScale,400.0f*dpiScale);
|
||||
ImGui::Image(chanOscGradTex,gradSize);
|
||||
ImGui::Image(rend->getTextureID(chanOscGradTex),gradSize);
|
||||
ImVec2 gradLeftAbs=ImGui::GetItemRectMin();
|
||||
if (ImGui::IsItemClicked(ImGuiMouseButton_Right)) {
|
||||
if (chanOscGrad.points.size()<32) {
|
||||
|
|
@ -284,151 +285,266 @@ void FurnaceGUI::drawChanOsc() {
|
|||
ImGui::ColorPicker4("Color",(float*)&chanOscColor);
|
||||
}
|
||||
|
||||
ImGui::Text("Text format:");
|
||||
ImGui::SameLine();
|
||||
ImGui::InputText("##TextFormat",&chanOscTextFormat);
|
||||
if (ImGui::IsItemHovered()) {
|
||||
if (ImGui::BeginTooltip()) {
|
||||
ImGui::TextUnformatted(
|
||||
"format guide:\n"
|
||||
"- %c: channel name\n"
|
||||
"- %C: channel short name\n"
|
||||
"- %d: channel number (starting from 0)\n"
|
||||
"- %D: channel number (starting from 1)\n"
|
||||
"- %i: instrument name\n"
|
||||
"- %I: instrument number (decimal)\n"
|
||||
"- %x: instrument number (hex)\n"
|
||||
"- %s: chip name\n"
|
||||
"- %S: chip ID\n"
|
||||
"- %v: volume (decimal)\n"
|
||||
"- %V: volume (percentage)\n"
|
||||
"- %b: volume (hex)\n"
|
||||
"- %%: percent sign"
|
||||
);
|
||||
ImGui::EndTooltip();
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::ColorEdit4("Text color",(float*)&chanOscTextColor);
|
||||
|
||||
if (ImGui::Button("OK")) {
|
||||
chanOscOptions=false;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_CellPadding,ImVec2(0.0f,0.0f));
|
||||
float availY=ImGui::GetContentRegionAvail().y;
|
||||
if (ImGui::BeginTable("ChanOsc",chanOscCols,ImGuiTableFlags_Borders)) {
|
||||
std::vector<DivDispatchOscBuffer*> oscBufs;
|
||||
std::vector<ChanOscStatus*> oscFFTs;
|
||||
std::vector<int> oscChans;
|
||||
int chans=e->getTotalChannelCount();
|
||||
ImGuiWindow* window=ImGui::GetCurrentWindow();
|
||||
ImVec2 waveform[512];
|
||||
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_CellPadding,ImVec2(0.0f,0.0f));
|
||||
float availY=ImGui::GetContentRegionAvail().y;
|
||||
if (ImGui::BeginTable("ChanOsc",chanOscCols,ImGuiTableFlags_Borders)) {
|
||||
std::vector<DivDispatchOscBuffer*> oscBufs;
|
||||
std::vector<ChanOscStatus*> oscFFTs;
|
||||
std::vector<int> oscChans;
|
||||
int chans=e->getTotalChannelCount();
|
||||
ImGuiWindow* window=ImGui::GetCurrentWindow();
|
||||
ImVec2 waveform[512];
|
||||
ImGuiStyle& style=ImGui::GetStyle();
|
||||
|
||||
ImGuiStyle& style=ImGui::GetStyle();
|
||||
|
||||
for (int i=0; i<chans; i++) {
|
||||
DivDispatchOscBuffer* buf=e->getOscBuffer(i);
|
||||
if (buf!=NULL && e->curSubSong->chanShow[i]) {
|
||||
oscBufs.push_back(buf);
|
||||
oscFFTs.push_back(&chanOscChan[i]);
|
||||
oscChans.push_back(i);
|
||||
for (int i=0; i<chans; i++) {
|
||||
DivDispatchOscBuffer* buf=e->getOscBuffer(i);
|
||||
if (buf!=NULL && e->curSubSong->chanShow[i]) {
|
||||
oscBufs.push_back(buf);
|
||||
oscFFTs.push_back(&chanOscChan[i]);
|
||||
oscChans.push_back(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
int rows=(oscBufs.size()+(chanOscCols-1))/chanOscCols;
|
||||
int rows=(oscBufs.size()+(chanOscCols-1))/chanOscCols;
|
||||
|
||||
for (size_t i=0; i<oscBufs.size(); i++) {
|
||||
if (i%chanOscCols==0) ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
for (size_t i=0; i<oscBufs.size(); i++) {
|
||||
if (i%chanOscCols==0) ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
|
||||
DivDispatchOscBuffer* buf=oscBufs[i];
|
||||
ChanOscStatus* fft=oscFFTs[i];
|
||||
int ch=oscChans[i];
|
||||
if (buf==NULL) {
|
||||
ImGui::Text("Error!");
|
||||
} else {
|
||||
ImVec2 size=ImGui::GetContentRegionAvail();
|
||||
size.y=availY/rows;
|
||||
DivDispatchOscBuffer* buf=oscBufs[i];
|
||||
ChanOscStatus* fft=oscFFTs[i];
|
||||
int ch=oscChans[i];
|
||||
if (buf==NULL) {
|
||||
ImGui::Text("Error!");
|
||||
} else {
|
||||
ImVec2 size=ImGui::GetContentRegionAvail();
|
||||
size.y=availY/rows;
|
||||
|
||||
if (centerSettingReset) {
|
||||
buf->readNeedle=buf->needle;
|
||||
}
|
||||
if (centerSettingReset) {
|
||||
buf->readNeedle=buf->needle;
|
||||
}
|
||||
|
||||
// check FFT status existence
|
||||
if (fft->plan==NULL) {
|
||||
logD("creating FFT plan for channel %d",ch);
|
||||
fft->inBuf=(double*)fftw_malloc(FURNACE_FFT_SIZE*sizeof(double));
|
||||
fft->outBuf=(fftw_complex*)fftw_malloc(FURNACE_FFT_SIZE*sizeof(fftw_complex));
|
||||
fft->plan=fftw_plan_dft_r2c_1d(FURNACE_FFT_SIZE,fft->inBuf,fft->outBuf,FFTW_ESTIMATE);
|
||||
}
|
||||
// check FFT status existence
|
||||
if (fft->plan==NULL) {
|
||||
logD("creating FFT plan for channel %d",ch);
|
||||
fft->inBuf=(double*)fftw_malloc(FURNACE_FFT_SIZE*sizeof(double));
|
||||
fft->outBuf=(fftw_complex*)fftw_malloc(FURNACE_FFT_SIZE*sizeof(fftw_complex));
|
||||
fft->plan=fftw_plan_dft_r2c_1d(FURNACE_FFT_SIZE,fft->inBuf,fft->outBuf,FFTW_ESTIMATE);
|
||||
}
|
||||
|
||||
int displaySize=(float)(buf->rate)*(chanOscWindowSize/1000.0f);
|
||||
int displaySize=(float)(buf->rate)*(chanOscWindowSize/1000.0f);
|
||||
|
||||
ImVec2 minArea=window->DC.CursorPos;
|
||||
ImVec2 maxArea=ImVec2(
|
||||
minArea.x+size.x,
|
||||
minArea.y+size.y
|
||||
);
|
||||
ImRect rect=ImRect(minArea,maxArea);
|
||||
ImRect inRect=rect;
|
||||
inRect.Min.x+=dpiScale;
|
||||
inRect.Min.y+=dpiScale;
|
||||
inRect.Max.x-=dpiScale;
|
||||
inRect.Max.y-=dpiScale;
|
||||
ImGui::ItemSize(size,style.FramePadding.y);
|
||||
if (ImGui::ItemAdd(rect,ImGui::GetID("chOscDisplay"))) {
|
||||
if (!e->isRunning()) {
|
||||
for (unsigned short i=0; i<512; i++) {
|
||||
float x=(float)i/512.0f;
|
||||
waveform[i]=ImLerp(inRect.Min,inRect.Max,ImVec2(x,0.5f));
|
||||
}
|
||||
} else {
|
||||
float minLevel=1.0f;
|
||||
float maxLevel=-1.0f;
|
||||
float dcOff=0.0f;
|
||||
unsigned short needlePos=buf->needle;
|
||||
for (int i=0; i<FURNACE_FFT_SIZE; i++) {
|
||||
fft->inBuf[i]=(double)buf->data[(unsigned short)(needlePos-displaySize*2+((i*displaySize*2)/FURNACE_FFT_SIZE))]/32768.0;
|
||||
}
|
||||
fftw_execute(fft->plan);
|
||||
|
||||
// find origin frequency
|
||||
int point=1;
|
||||
double candAmp=0.0;
|
||||
for (unsigned short i=1; i<512; i++) {
|
||||
fftw_complex& f=fft->outBuf[i];
|
||||
// AMPLITUDE
|
||||
double amp=sqrt(pow(f[0],2.0)+pow(f[1],2.0))/pow((double)i,0.8);
|
||||
if (amp>candAmp) {
|
||||
point=i;
|
||||
candAmp=amp;
|
||||
ImVec2 minArea=window->DC.CursorPos;
|
||||
ImVec2 maxArea=ImVec2(
|
||||
minArea.x+size.x,
|
||||
minArea.y+size.y
|
||||
);
|
||||
ImRect rect=ImRect(minArea,maxArea);
|
||||
ImRect inRect=rect;
|
||||
inRect.Min.x+=dpiScale;
|
||||
inRect.Min.y+=2.0*dpiScale;
|
||||
inRect.Max.x-=dpiScale;
|
||||
inRect.Max.y-=2.0*dpiScale;
|
||||
|
||||
int precision=inRect.Max.x-inRect.Min.x;
|
||||
if (precision<1) precision=1;
|
||||
if (precision>512) precision=512;
|
||||
|
||||
ImGui::ItemSize(size,style.FramePadding.y);
|
||||
if (ImGui::ItemAdd(rect,ImGui::GetID("chOscDisplay"))) {
|
||||
if (!e->isRunning()) {
|
||||
for (unsigned short i=0; i<precision; i++) {
|
||||
float x=(float)i/(float)precision;
|
||||
waveform[i]=ImLerp(inRect.Min,inRect.Max,ImVec2(x,0.5f));
|
||||
}
|
||||
} else {
|
||||
float minLevel=1.0f;
|
||||
float maxLevel=-1.0f;
|
||||
float dcOff=0.0f;
|
||||
unsigned short needlePos=buf->needle;
|
||||
for (int i=0; i<FURNACE_FFT_SIZE; i++) {
|
||||
fft->inBuf[i]=(double)buf->data[(unsigned short)(needlePos-displaySize*2+((i*displaySize*2)/FURNACE_FFT_SIZE))]/32768.0;
|
||||
}
|
||||
fftw_execute(fft->plan);
|
||||
|
||||
// find origin frequency
|
||||
int point=1;
|
||||
double candAmp=0.0;
|
||||
for (unsigned short i=1; i<512; i++) {
|
||||
fftw_complex& f=fft->outBuf[i];
|
||||
// AMPLITUDE
|
||||
double amp=sqrt(pow(f[0],2.0)+pow(f[1],2.0))/pow((double)i,0.8);
|
||||
if (amp>candAmp) {
|
||||
point=i;
|
||||
candAmp=amp;
|
||||
}
|
||||
}
|
||||
|
||||
// PHASE
|
||||
fftw_complex& candPoint=fft->outBuf[point];
|
||||
double phase=((double)(displaySize*2)/(double)point)*(0.5+(atan2(candPoint[1],candPoint[0])/(M_PI*2)));
|
||||
|
||||
if (chanOscWaveCorr) {
|
||||
needlePos-=phase;
|
||||
}
|
||||
chanOscPitch[ch]=(float)point/32.0f;
|
||||
|
||||
/*
|
||||
String cPhase=fmt::sprintf("%d cphase: %f vol: %f",point,phase,chanOscVol[ch]);
|
||||
dl->AddText(inRect.Min,0xffffffff,cPhase.c_str());
|
||||
*/
|
||||
|
||||
needlePos-=displaySize;
|
||||
for (unsigned short i=0; i<precision; i++) {
|
||||
float y=(float)buf->data[(unsigned short)(needlePos+(i*displaySize/precision))]/32768.0f;
|
||||
if (minLevel>y) minLevel=y;
|
||||
if (maxLevel<y) maxLevel=y;
|
||||
}
|
||||
dcOff=(minLevel+maxLevel)*0.5f;
|
||||
for (unsigned short i=0; i<precision; i++) {
|
||||
float x=(float)i/(float)precision;
|
||||
float y=(float)buf->data[(unsigned short)(needlePos+(i*displaySize/precision))]/32768.0f;
|
||||
y-=dcOff;
|
||||
if (y<-0.5f) y=-0.5f;
|
||||
if (y>0.5f) y=0.5f;
|
||||
y*=chanOscAmplify;
|
||||
waveform[i]=ImLerp(inRect.Min,inRect.Max,ImVec2(x,0.5f-y));
|
||||
}
|
||||
}
|
||||
ImU32 color=ImGui::GetColorU32(chanOscColor);
|
||||
if (chanOscUseGrad) {
|
||||
float xVal=computeGradPos(chanOscColorX,ch);
|
||||
float yVal=computeGradPos(chanOscColorY,ch);
|
||||
|
||||
// PHASE
|
||||
fftw_complex& candPoint=fft->outBuf[point];
|
||||
double phase=((double)(displaySize*2)/(double)point)*(0.5+(atan2(candPoint[1],candPoint[0])/(M_PI*2)));
|
||||
xVal=CLAMP(xVal,0.0f,1.0f);
|
||||
yVal=CLAMP(yVal,0.0f,1.0f);
|
||||
|
||||
if (chanOscWaveCorr) {
|
||||
needlePos-=phase;
|
||||
color=chanOscGrad.get(xVal,1.0f-yVal);
|
||||
}
|
||||
chanOscPitch[ch]=(float)point/32.0f;
|
||||
|
||||
/*
|
||||
String cPhase=fmt::sprintf("%d cphase: %f vol: %f",point,phase,chanOscVol[ch]);
|
||||
dl->AddText(inRect.Min,0xffffffff,cPhase.c_str());
|
||||
*/
|
||||
ImGui::PushClipRect(inRect.Min,inRect.Max,false);
|
||||
|
||||
needlePos-=displaySize;
|
||||
for (unsigned short i=0; i<512; i++) {
|
||||
float y=(float)buf->data[(unsigned short)(needlePos+(i*displaySize/512))]/65536.0f;
|
||||
if (minLevel>y) minLevel=y;
|
||||
if (maxLevel<y) maxLevel=y;
|
||||
}
|
||||
dcOff=(minLevel+maxLevel)*0.5f;
|
||||
for (unsigned short i=0; i<512; i++) {
|
||||
float x=(float)i/512.0f;
|
||||
float y=(float)buf->data[(unsigned short)(needlePos+(i*displaySize/512))]/65536.0f;
|
||||
if (y<-0.5f) y=-0.5f;
|
||||
if (y>0.5f) y=0.5f;
|
||||
waveform[i]=ImLerp(inRect.Min,inRect.Max,ImVec2(x,0.5f-(y-dcOff)));
|
||||
dl->AddPolyline(waveform,precision,color,ImDrawFlags_None,dpiScale);
|
||||
|
||||
if (!chanOscTextFormat.empty()) {
|
||||
String text;
|
||||
bool inFormat=false;
|
||||
|
||||
for (char i: chanOscTextFormat) {
|
||||
if (inFormat) {
|
||||
switch (i) {
|
||||
case 'c':
|
||||
text+=e->getChannelName(ch);
|
||||
break;
|
||||
case 'C':
|
||||
text+=e->getChannelShortName(ch);
|
||||
break;
|
||||
case 'd':
|
||||
text+=fmt::sprintf("%d",ch);
|
||||
break;
|
||||
case 'D':
|
||||
text+=fmt::sprintf("%d",ch+1);
|
||||
break;
|
||||
case 'i': {
|
||||
DivChannelState* chanState=e->getChanState(ch);
|
||||
if (chanState==NULL) break;
|
||||
DivInstrument* ins=e->getIns(chanState->lastIns);
|
||||
text+=ins->name;
|
||||
break;
|
||||
}
|
||||
case 'I': {
|
||||
DivChannelState* chanState=e->getChanState(ch);
|
||||
if (chanState==NULL) break;
|
||||
text+=fmt::sprintf("%d",chanState->lastIns);
|
||||
break;
|
||||
}
|
||||
case 'x': {
|
||||
DivChannelState* chanState=e->getChanState(ch);
|
||||
if (chanState==NULL) break;
|
||||
if (chanState->lastIns<0) {
|
||||
text+="??";
|
||||
} else {
|
||||
text+=fmt::sprintf("%.2X",chanState->lastIns);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 's': {
|
||||
text+=e->getSystemName(e->sysOfChan[ch]);
|
||||
break;
|
||||
}
|
||||
case 'S': {
|
||||
text+=fmt::sprintf("%d",e->dispatchOfChan[ch]);
|
||||
break;
|
||||
}
|
||||
case 'v':
|
||||
break;
|
||||
case 'V':
|
||||
break;
|
||||
case 'b':
|
||||
break;
|
||||
case '%':
|
||||
text+='%';
|
||||
break;
|
||||
default:
|
||||
text+='%';
|
||||
text+=i;
|
||||
break;
|
||||
}
|
||||
inFormat=false;
|
||||
} else {
|
||||
if (i=='%') {
|
||||
inFormat=true;
|
||||
} else {
|
||||
text+=i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dl->AddText(ImLerp(inRect.Min,inRect.Max,ImVec2(0.0f,0.0f)),ImGui::GetColorU32(chanOscTextColor),text.c_str());
|
||||
}
|
||||
|
||||
ImGui::PopClipRect();
|
||||
}
|
||||
ImU32 color=ImGui::GetColorU32(chanOscColor);
|
||||
if (chanOscUseGrad) {
|
||||
float xVal=computeGradPos(chanOscColorX,ch);
|
||||
float yVal=computeGradPos(chanOscColorY,ch);
|
||||
|
||||
xVal=CLAMP(xVal,0.0f,1.0f);
|
||||
yVal=CLAMP(yVal,0.0f,1.0f);
|
||||
|
||||
color=chanOscGrad.get(xVal,1.0f-yVal);
|
||||
}
|
||||
dl->AddPolyline(waveform,512,color,ImDrawFlags_None,dpiScale);
|
||||
}
|
||||
}
|
||||
}
|
||||
ImGui::EndTable();
|
||||
ImGui::EndTable();
|
||||
|
||||
if (ImGui::IsItemClicked(ImGuiMouseButton_Right)) {
|
||||
chanOscOptions=!chanOscOptions;
|
||||
if (ImGui::IsItemClicked(ImGuiMouseButton_Right)) {
|
||||
chanOscOptions=!chanOscOptions;
|
||||
}
|
||||
}
|
||||
ImGui::PopStyleVar();
|
||||
}
|
||||
ImGui::PopStyleVar();
|
||||
}
|
||||
if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_CHAN_OSC;
|
||||
ImGui::End();
|
||||
|
|
|
|||
|
|
@ -180,6 +180,10 @@ void FurnaceGUI::drawCompatFlags() {
|
|||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("behavior changed in 0.6pre4");
|
||||
}
|
||||
ImGui::Checkbox("Broken macros in some FM chips after note off",&e->song.brokenFMOff);
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("behavior changed in 0.6pre5");
|
||||
}
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
if (ImGui::BeginTabItem(".mod import")) {
|
||||
|
|
|
|||
1301
src/gui/dataList.cpp
1301
src/gui/dataList.cpp
File diff suppressed because it is too large
Load diff
|
|
@ -380,7 +380,7 @@ void FurnaceGUI::drawDebug() {
|
|||
}
|
||||
ImGui::TreePop();
|
||||
}
|
||||
if (ImGui::TreeNode("Window Debug")) {
|
||||
if (ImGui::TreeNodeEx("Window Debug",ImGuiTreeNodeFlags_DefaultOpen)) {
|
||||
ImGui::Text("Screen: %dx%d+%d+%d",scrW,scrH,scrX,scrY);
|
||||
ImGui::Text("Screen (Conf): %dx%d+%d+%d",scrConfW,scrConfH,scrConfX,scrConfY);
|
||||
ImGui::Text("Canvas: %dx%d",canvasW,canvasH);
|
||||
|
|
@ -549,6 +549,7 @@ void FurnaceGUI::drawDebug() {
|
|||
|
||||
ImGui::Text("audio: %dµs",lastProcTime);
|
||||
ImGui::Text("render: %.0fµs",(double)renderTimeDelta/perfFreq);
|
||||
ImGui::Text("draw: %.0fµs",(double)drawTimeDelta/perfFreq);
|
||||
ImGui::Text("layout: %.0fµs",(double)layoutTimeDelta/perfFreq);
|
||||
ImGui::Text("event: %.0fµs",(double)eventTimeDelta/perfFreq);
|
||||
ImGui::Separator();
|
||||
|
|
|
|||
|
|
@ -561,17 +561,17 @@ void FurnaceGUI::doAction(int what) {
|
|||
doFlip();
|
||||
break;
|
||||
case GUI_ACTION_PAT_COLLAPSE_ROWS:
|
||||
doCollapse(2,selStart,selEnd);
|
||||
doCollapse(collapseAmount,selStart,selEnd);
|
||||
break;
|
||||
case GUI_ACTION_PAT_EXPAND_ROWS:
|
||||
doExpand(2,selStart,selEnd);
|
||||
doExpand(collapseAmount,selStart,selEnd);
|
||||
break;
|
||||
case GUI_ACTION_PAT_COLLAPSE_PAT: {
|
||||
SelectionPoint selEndPat;
|
||||
selEndPat.xCoarse=e->getTotalChannelCount()-1;
|
||||
selEndPat.xFine=2+e->curPat[selEndPat.xCoarse].effectCols*2;
|
||||
selEndPat.y=e->curSubSong->patLen-1;
|
||||
doCollapse(2,SelectionPoint(0,0,0),selEndPat);
|
||||
doCollapse(collapseAmount,SelectionPoint(0,0,0),selEndPat);
|
||||
break;
|
||||
}
|
||||
case GUI_ACTION_PAT_EXPAND_PAT: {
|
||||
|
|
@ -579,14 +579,14 @@ void FurnaceGUI::doAction(int what) {
|
|||
selEndPat.xCoarse=e->getTotalChannelCount()-1;
|
||||
selEndPat.xFine=2+e->curPat[selEndPat.xCoarse].effectCols*2;
|
||||
selEndPat.y=e->curSubSong->patLen-1;
|
||||
doExpand(2,SelectionPoint(0,0,0),selEndPat);
|
||||
doExpand(collapseAmount,SelectionPoint(0,0,0),selEndPat);
|
||||
break;
|
||||
}
|
||||
case GUI_ACTION_PAT_COLLAPSE_SONG:
|
||||
doCollapseSong(2);
|
||||
doCollapseSong(collapseAmount);
|
||||
break;
|
||||
case GUI_ACTION_PAT_EXPAND_SONG:
|
||||
doExpandSong(2);
|
||||
doExpandSong(collapseAmount);
|
||||
break;
|
||||
case GUI_ACTION_PAT_LATCH: // TODO
|
||||
break;
|
||||
|
|
@ -684,6 +684,9 @@ void FurnaceGUI::doAction(int what) {
|
|||
wavePreviewInit=true;
|
||||
updateFMPreview=true;
|
||||
break;
|
||||
case GUI_ACTION_INS_LIST_DIR_VIEW:
|
||||
insListDir=!insListDir;
|
||||
break;
|
||||
|
||||
case GUI_ACTION_WAVE_LIST_ADD:
|
||||
curWave=e->addWave();
|
||||
|
|
@ -757,6 +760,9 @@ void FurnaceGUI::doAction(int what) {
|
|||
if (++curWave>=(int)e->song.wave.size()) curWave=((int)e->song.wave.size())-1;
|
||||
wantScrollList=true;
|
||||
break;
|
||||
case GUI_ACTION_WAVE_LIST_DIR_VIEW:
|
||||
waveListDir=!waveListDir;
|
||||
break;
|
||||
|
||||
case GUI_ACTION_SAMPLE_LIST_ADD:
|
||||
curSample=e->addSample();
|
||||
|
|
@ -785,6 +791,8 @@ void FurnaceGUI::doAction(int what) {
|
|||
sample->loopEnd=prevSample->loopEnd;
|
||||
sample->loop=prevSample->loop;
|
||||
sample->loopMode=prevSample->loopMode;
|
||||
sample->brrEmphasis=prevSample->brrEmphasis;
|
||||
sample->dither=prevSample->dither;
|
||||
sample->depth=prevSample->depth;
|
||||
if (sample->init(prevSample->samples)) {
|
||||
if (prevSample->getCurBuf()!=NULL) {
|
||||
|
|
@ -815,6 +823,9 @@ void FurnaceGUI::doAction(int what) {
|
|||
case GUI_ACTION_SAMPLE_LIST_SAVE:
|
||||
if (curSample>=0 && curSample<(int)e->song.sample.size()) openFileDialog(GUI_FILE_SAMPLE_SAVE);
|
||||
break;
|
||||
case GUI_ACTION_SAMPLE_LIST_SAVE_RAW:
|
||||
if (curSample>=0 && curSample<(int)e->song.sample.size()) openFileDialog(GUI_FILE_SAMPLE_SAVE_RAW);
|
||||
break;
|
||||
case GUI_ACTION_SAMPLE_LIST_MOVE_UP:
|
||||
if (e->moveSampleUp(curSample)) {
|
||||
curSample--;
|
||||
|
|
@ -835,8 +846,8 @@ void FurnaceGUI::doAction(int what) {
|
|||
MARK_MODIFIED;
|
||||
if (curSample>=(int)e->song.sample.size()) {
|
||||
curSample--;
|
||||
updateSampleTex=true;
|
||||
}
|
||||
updateSampleTex=true;
|
||||
break;
|
||||
case GUI_ACTION_SAMPLE_LIST_EDIT:
|
||||
sampleEditOpen=true;
|
||||
|
|
@ -857,6 +868,9 @@ void FurnaceGUI::doAction(int what) {
|
|||
case GUI_ACTION_SAMPLE_LIST_STOP_PREVIEW:
|
||||
e->stopSamplePreview();
|
||||
break;
|
||||
case GUI_ACTION_SAMPLE_LIST_DIR_VIEW:
|
||||
sampleListDir=!sampleListDir;
|
||||
break;
|
||||
|
||||
case GUI_ACTION_SAMPLE_SELECT:
|
||||
if (curSample<0 || curSample>=(int)e->song.sample.size()) break;
|
||||
|
|
|
|||
|
|
@ -463,181 +463,185 @@ void FurnaceGUI::drawMobileControls() {
|
|||
|
||||
ImGui::Separator();
|
||||
|
||||
if (settings.unifiedDataView) {
|
||||
drawInsList(true);
|
||||
} else {
|
||||
switch (mobScene) {
|
||||
case GUI_SCENE_PATTERN:
|
||||
case GUI_SCENE_ORDERS:
|
||||
case GUI_SCENE_INSTRUMENT:
|
||||
switch (mobScene) {
|
||||
case GUI_SCENE_PATTERN:
|
||||
case GUI_SCENE_ORDERS:
|
||||
case GUI_SCENE_INSTRUMENT:
|
||||
drawInsList(true);
|
||||
break;
|
||||
case GUI_SCENE_WAVETABLE:
|
||||
if (settings.unifiedDataView) {
|
||||
drawInsList(true);
|
||||
break;
|
||||
case GUI_SCENE_WAVETABLE:
|
||||
} else {
|
||||
drawWaveList(true);
|
||||
break;
|
||||
case GUI_SCENE_SAMPLE:
|
||||
}
|
||||
break;
|
||||
case GUI_SCENE_SAMPLE:
|
||||
if (settings.unifiedDataView) {
|
||||
drawInsList(true);
|
||||
} else {
|
||||
drawSampleList(true);
|
||||
break;
|
||||
case GUI_SCENE_SONG: {
|
||||
if (ImGui::Button("New")) {
|
||||
mobileMenuOpen=false;
|
||||
//doAction(GUI_ACTION_NEW);
|
||||
if (modified) {
|
||||
showWarning("Unsaved changes! Save changes before creating a new song?",GUI_WARN_NEW);
|
||||
} else {
|
||||
displayNew=true;
|
||||
}
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Open")) {
|
||||
mobileMenuOpen=false;
|
||||
doAction(GUI_ACTION_OPEN);
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Save")) {
|
||||
mobileMenuOpen=false;
|
||||
doAction(GUI_ACTION_SAVE);
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Save as...")) {
|
||||
mobileMenuOpen=false;
|
||||
doAction(GUI_ACTION_SAVE_AS);
|
||||
}
|
||||
|
||||
ImGui::Button("1.1+ .dmf");
|
||||
ImGui::SameLine();
|
||||
ImGui::Button("Legacy .dmf");
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Export Audio")) {
|
||||
openFileDialog(GUI_FILE_EXPORT_AUDIO_ONE);
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Export VGM")) {
|
||||
openFileDialog(GUI_FILE_EXPORT_VGM);
|
||||
}
|
||||
|
||||
if (ImGui::Button("CmdStream")) {
|
||||
openFileDialog(GUI_FILE_EXPORT_CMDSTREAM_BINARY);
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("CmdStream Text")) {
|
||||
openFileDialog(GUI_FILE_EXPORT_CMDSTREAM);
|
||||
}
|
||||
|
||||
if (ImGui::Button("Restore Backup")) {
|
||||
mobileMenuOpen=false;
|
||||
doAction(GUI_ACTION_OPEN_BACKUP);
|
||||
}
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
if (ImGui::BeginTabBar("MobileSong")) {
|
||||
if (ImGui::BeginTabItem("Song Info")) {
|
||||
drawSongInfo(true);
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
if (ImGui::BeginTabItem("Subsongs")) {
|
||||
drawSubSongs(true);
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
if (ImGui::BeginTabItem("Speed")) {
|
||||
drawSpeed(true);
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
ImGui::EndTabBar();
|
||||
}
|
||||
break;
|
||||
}
|
||||
case GUI_SCENE_CHANNELS:
|
||||
ImGui::Text("Channels here...");
|
||||
break;
|
||||
case GUI_SCENE_CHIPS:
|
||||
ImGui::Text("Chips here...");
|
||||
break;
|
||||
case GUI_SCENE_MIXER:
|
||||
ImGui::Text("What the hell...");
|
||||
break;
|
||||
case GUI_SCENE_OTHER: {
|
||||
if (ImGui::Button("Osc")) {
|
||||
oscOpen=!oscOpen;
|
||||
break;
|
||||
case GUI_SCENE_SONG: {
|
||||
if (ImGui::Button("New")) {
|
||||
mobileMenuOpen=false;
|
||||
//doAction(GUI_ACTION_NEW);
|
||||
if (modified) {
|
||||
showWarning("Unsaved changes! Save changes before creating a new song?",GUI_WARN_NEW);
|
||||
} else {
|
||||
displayNew=true;
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("ChanOsc")) {
|
||||
chanOscOpen=!chanOscOpen;
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("RegView")) {
|
||||
regViewOpen=!regViewOpen;
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Stats")) {
|
||||
statsOpen=!statsOpen;
|
||||
}
|
||||
if (ImGui::Button("Compat Flags")) {
|
||||
compatFlagsOpen=!compatFlagsOpen;
|
||||
}
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
ImGui::Button("Panic");
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Settings")) {
|
||||
mobileMenuOpen=false;
|
||||
settingsOpen=true;
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Log")) {
|
||||
logOpen=!logOpen;
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Debug")) {
|
||||
debugOpen=!debugOpen;
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("About")) {
|
||||
mobileMenuOpen=false;
|
||||
mobileMenuPos=0.0f;
|
||||
aboutOpen=true;
|
||||
}
|
||||
if (ImGui::Button("Switch to Desktop Mode")) {
|
||||
toggleMobileUI(!mobileUI);
|
||||
}
|
||||
|
||||
int numAmiga=0;
|
||||
for (int i=0; i<e->song.systemLen; i++) {
|
||||
if (e->song.system[i]==DIV_SYSTEM_AMIGA) numAmiga++;
|
||||
}
|
||||
|
||||
if (numAmiga) {
|
||||
ImGui::Text(
|
||||
"this is NOT ROM export! only use for making sure the\n"
|
||||
"Furnace Amiga emulator is working properly by\n"
|
||||
"comparing it with real Amiga output."
|
||||
);
|
||||
ImGui::Text("Directory");
|
||||
ImGui::SameLine();
|
||||
ImGui::InputText("##AVDPath",&workingDirROMExport);
|
||||
if (ImGui::Button("Bake Data")) {
|
||||
std::vector<DivROMExportOutput> out=e->buildROM(DIV_ROM_AMIGA_VALIDATION);
|
||||
if (workingDirROMExport.size()>0) {
|
||||
if (workingDirROMExport[workingDirROMExport.size()-1]!=DIR_SEPARATOR) workingDirROMExport+=DIR_SEPARATOR_STR;
|
||||
}
|
||||
for (DivROMExportOutput& i: out) {
|
||||
String path=workingDirROMExport+i.name;
|
||||
FILE* outFile=ps_fopen(path.c_str(),"wb");
|
||||
if (outFile!=NULL) {
|
||||
fwrite(i.data->getFinalBuf(),1,i.data->size(),outFile);
|
||||
fclose(outFile);
|
||||
}
|
||||
i.data->finish();
|
||||
delete i.data;
|
||||
}
|
||||
showError(fmt::sprintf("Done! Baked %d files.",(int)out.size()));
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Open")) {
|
||||
mobileMenuOpen=false;
|
||||
doAction(GUI_ACTION_OPEN);
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Save")) {
|
||||
mobileMenuOpen=false;
|
||||
doAction(GUI_ACTION_SAVE);
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Save as...")) {
|
||||
mobileMenuOpen=false;
|
||||
doAction(GUI_ACTION_SAVE_AS);
|
||||
}
|
||||
|
||||
ImGui::Button("1.1+ .dmf");
|
||||
ImGui::SameLine();
|
||||
ImGui::Button("Legacy .dmf");
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Export Audio")) {
|
||||
openFileDialog(GUI_FILE_EXPORT_AUDIO_ONE);
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Export VGM")) {
|
||||
openFileDialog(GUI_FILE_EXPORT_VGM);
|
||||
}
|
||||
|
||||
if (ImGui::Button("CmdStream")) {
|
||||
openFileDialog(GUI_FILE_EXPORT_CMDSTREAM_BINARY);
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("CmdStream Text")) {
|
||||
openFileDialog(GUI_FILE_EXPORT_CMDSTREAM);
|
||||
}
|
||||
|
||||
if (ImGui::Button("Restore Backup")) {
|
||||
mobileMenuOpen=false;
|
||||
doAction(GUI_ACTION_OPEN_BACKUP);
|
||||
}
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
if (ImGui::BeginTabBar("MobileSong")) {
|
||||
if (ImGui::BeginTabItem("Song Info")) {
|
||||
drawSongInfo(true);
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
if (ImGui::BeginTabItem("Subsongs")) {
|
||||
drawSubSongs(true);
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
if (ImGui::BeginTabItem("Speed")) {
|
||||
drawSpeed(true);
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
ImGui::EndTabBar();
|
||||
}
|
||||
break;
|
||||
}
|
||||
case GUI_SCENE_CHANNELS:
|
||||
ImGui::Text("Channels here...");
|
||||
break;
|
||||
case GUI_SCENE_CHIPS:
|
||||
ImGui::Text("Chips here...");
|
||||
break;
|
||||
case GUI_SCENE_MIXER:
|
||||
ImGui::Text("What the hell...");
|
||||
break;
|
||||
case GUI_SCENE_OTHER: {
|
||||
if (ImGui::Button("Osc")) {
|
||||
oscOpen=!oscOpen;
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("ChanOsc")) {
|
||||
chanOscOpen=!chanOscOpen;
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("RegView")) {
|
||||
regViewOpen=!regViewOpen;
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Stats")) {
|
||||
statsOpen=!statsOpen;
|
||||
}
|
||||
if (ImGui::Button("Compat Flags")) {
|
||||
compatFlagsOpen=!compatFlagsOpen;
|
||||
}
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
ImGui::Button("Panic");
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Settings")) {
|
||||
mobileMenuOpen=false;
|
||||
settingsOpen=true;
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Log")) {
|
||||
logOpen=!logOpen;
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Debug")) {
|
||||
debugOpen=!debugOpen;
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("About")) {
|
||||
mobileMenuOpen=false;
|
||||
mobileMenuPos=0.0f;
|
||||
aboutOpen=true;
|
||||
}
|
||||
if (ImGui::Button("Switch to Desktop Mode")) {
|
||||
toggleMobileUI(!mobileUI);
|
||||
}
|
||||
|
||||
int numAmiga=0;
|
||||
for (int i=0; i<e->song.systemLen; i++) {
|
||||
if (e->song.system[i]==DIV_SYSTEM_AMIGA) numAmiga++;
|
||||
}
|
||||
|
||||
if (numAmiga) {
|
||||
ImGui::Text(
|
||||
"this is NOT ROM export! only use for making sure the\n"
|
||||
"Furnace Amiga emulator is working properly by\n"
|
||||
"comparing it with real Amiga output."
|
||||
);
|
||||
ImGui::Text("Directory");
|
||||
ImGui::SameLine();
|
||||
ImGui::InputText("##AVDPath",&workingDirROMExport);
|
||||
if (ImGui::Button("Bake Data")) {
|
||||
std::vector<DivROMExportOutput> out=e->buildROM(DIV_ROM_AMIGA_VALIDATION);
|
||||
if (workingDirROMExport.size()>0) {
|
||||
if (workingDirROMExport[workingDirROMExport.size()-1]!=DIR_SEPARATOR) workingDirROMExport+=DIR_SEPARATOR_STR;
|
||||
}
|
||||
for (DivROMExportOutput& i: out) {
|
||||
String path=workingDirROMExport+i.name;
|
||||
FILE* outFile=ps_fopen(path.c_str(),"wb");
|
||||
if (outFile!=NULL) {
|
||||
fwrite(i.data->getFinalBuf(),1,i.data->size(),outFile);
|
||||
fclose(outFile);
|
||||
}
|
||||
i.data->finish();
|
||||
delete i.data;
|
||||
}
|
||||
showError(fmt::sprintf("Done! Baked %d files.",(int)out.size()));
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -245,14 +245,22 @@ void FurnaceGUI::doPullDelete() {
|
|||
updateScroll(cursor.y);
|
||||
}
|
||||
|
||||
int iCoarse=selStart.xCoarse;
|
||||
int iFine=selStart.xFine;
|
||||
for (; iCoarse<=selEnd.xCoarse; iCoarse++) {
|
||||
SelectionPoint sStart=selStart;
|
||||
SelectionPoint sEnd=selEnd;
|
||||
|
||||
if (selStart.xCoarse==selEnd.xCoarse && selStart.xFine==selEnd.xFine && selStart.y==selEnd.y && settings.pullDeleteRow) {
|
||||
sStart.xFine=0;
|
||||
sEnd.xFine=2+e->curPat[sEnd.xCoarse].effectCols*2;
|
||||
}
|
||||
|
||||
int iCoarse=sStart.xCoarse;
|
||||
int iFine=sStart.xFine;
|
||||
for (; iCoarse<=sEnd.xCoarse; iCoarse++) {
|
||||
if (!e->curSubSong->chanShow[iCoarse]) continue;
|
||||
DivPattern* pat=e->curPat[iCoarse].getPattern(e->curOrders->ord[iCoarse][curOrder],true);
|
||||
for (; iFine<3+e->curPat[iCoarse].effectCols*2 && (iCoarse<selEnd.xCoarse || iFine<=selEnd.xFine); iFine++) {
|
||||
for (; iFine<3+e->curPat[iCoarse].effectCols*2 && (iCoarse<sEnd.xCoarse || iFine<=sEnd.xFine); iFine++) {
|
||||
maskOut(opMaskPullDelete,iFine);
|
||||
for (int j=selStart.y; j<e->curSubSong->patLen; j++) {
|
||||
for (int j=sStart.y; j<e->curSubSong->patLen; j++) {
|
||||
if (j<e->curSubSong->patLen-1) {
|
||||
if (iFine==0) {
|
||||
pat->data[j][iFine]=pat->data[j+1][iFine];
|
||||
|
|
@ -277,15 +285,23 @@ void FurnaceGUI::doInsert() {
|
|||
prepareUndo(GUI_UNDO_PATTERN_PUSH);
|
||||
curNibble=false;
|
||||
|
||||
int iCoarse=selStart.xCoarse;
|
||||
int iFine=selStart.xFine;
|
||||
for (; iCoarse<=selEnd.xCoarse; iCoarse++) {
|
||||
SelectionPoint sStart=selStart;
|
||||
SelectionPoint sEnd=selEnd;
|
||||
|
||||
if (selStart.xCoarse==selEnd.xCoarse && selStart.xFine==selEnd.xFine && selStart.y==selEnd.y && settings.insertBehavior) {
|
||||
sStart.xFine=0;
|
||||
sEnd.xFine=2+e->curPat[sEnd.xCoarse].effectCols*2;
|
||||
}
|
||||
|
||||
int iCoarse=sStart.xCoarse;
|
||||
int iFine=sStart.xFine;
|
||||
for (; iCoarse<=sEnd.xCoarse; iCoarse++) {
|
||||
if (!e->curSubSong->chanShow[iCoarse]) continue;
|
||||
DivPattern* pat=e->curPat[iCoarse].getPattern(e->curOrders->ord[iCoarse][curOrder],true);
|
||||
for (; iFine<3+e->curPat[iCoarse].effectCols*2 && (iCoarse<selEnd.xCoarse || iFine<=selEnd.xFine); iFine++) {
|
||||
for (; iFine<3+e->curPat[iCoarse].effectCols*2 && (iCoarse<sEnd.xCoarse || iFine<=sEnd.xFine); iFine++) {
|
||||
maskOut(opMaskInsert,iFine);
|
||||
for (int j=e->curSubSong->patLen-1; j>=selStart.y; j--) {
|
||||
if (j==selStart.y) {
|
||||
for (int j=e->curSubSong->patLen-1; j>=sStart.y; j--) {
|
||||
if (j==sStart.y) {
|
||||
if (iFine==0) {
|
||||
pat->data[j][iFine]=0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
#include "fileDialog.h"
|
||||
#include "ImGuiFileDialog.h"
|
||||
#include "util.h"
|
||||
#include "../ta-log.h"
|
||||
|
||||
#ifdef USE_NFD
|
||||
|
|
@ -152,6 +153,7 @@ bool FurnaceGUIFileDialog::openLoad(String header, std::vector<String> filter, c
|
|||
ImGuiFileDialog::Instance()->singleClickSel=mobileUI;
|
||||
ImGuiFileDialog::Instance()->DpiScale=dpiScale;
|
||||
ImGuiFileDialog::Instance()->mobileMode=mobileUI;
|
||||
ImGuiFileDialog::Instance()->homePath=getHomeDir();
|
||||
ImGuiFileDialog::Instance()->OpenModal("FileDialog",header,noSysFilter,path,allowMultiple?999:1,nullptr,0,clickCallback);
|
||||
}
|
||||
opened=true;
|
||||
|
|
@ -235,6 +237,7 @@ bool FurnaceGUIFileDialog::openSave(String header, std::vector<String> filter, c
|
|||
ImGuiFileDialog::Instance()->singleClickSel=false;
|
||||
ImGuiFileDialog::Instance()->DpiScale=dpiScale;
|
||||
ImGuiFileDialog::Instance()->mobileMode=mobileUI;
|
||||
ImGuiFileDialog::Instance()->homePath=getHomeDir();
|
||||
ImGuiFileDialog::Instance()->OpenModal("FileDialog",header,noSysFilter,path,1,nullptr,ImGuiFileDialogFlags_ConfirmOverwrite);
|
||||
}
|
||||
opened=true;
|
||||
|
|
|
|||
428
src/gui/gui.cpp
428
src/gui/gui.cpp
|
|
@ -29,8 +29,6 @@
|
|||
#include "../fileutils.h"
|
||||
#include "imgui.h"
|
||||
#include "imgui_internal.h"
|
||||
#include "imgui_impl_sdl.h"
|
||||
#include "imgui_impl_sdlrenderer.h"
|
||||
#include "ImGuiFileDialog.h"
|
||||
#include "IconsFontAwesome4.h"
|
||||
#include "misc/cpp/imgui_stdlib.h"
|
||||
|
|
@ -1726,6 +1724,16 @@ void FurnaceGUI::openFileDialog(FurnaceGUIFileDialogs type) {
|
|||
dpiScale
|
||||
);
|
||||
break;
|
||||
case GUI_FILE_SAMPLE_SAVE_RAW:
|
||||
if (!dirExists(workingDirSample)) workingDirSample=getHomeDir();
|
||||
hasOpened=fileDialog->openSave(
|
||||
"Load Raw Sample",
|
||||
{"all files", "*"},
|
||||
".*",
|
||||
workingDirSample,
|
||||
dpiScale
|
||||
);
|
||||
break;
|
||||
case GUI_FILE_EXPORT_AUDIO_ONE:
|
||||
if (!dirExists(workingDirAudioExport)) workingDirAudioExport=getHomeDir();
|
||||
hasOpened=fileDialog->openSave(
|
||||
|
|
@ -1958,8 +1966,6 @@ void FurnaceGUI::openFileDialog(FurnaceGUIFileDialogs type) {
|
|||
//ImGui::GetIO().ConfigFlags|=ImGuiConfigFlags_NavEnableKeyboard;
|
||||
}
|
||||
|
||||
#define FURNACE_ZLIB_COMPRESS
|
||||
|
||||
int FurnaceGUI::save(String path, int dmfVersion) {
|
||||
SafeWriter* w;
|
||||
logD("saving file...");
|
||||
|
|
@ -1967,7 +1973,7 @@ int FurnaceGUI::save(String path, int dmfVersion) {
|
|||
if (dmfVersion<24) dmfVersion=24;
|
||||
w=e->saveDMF(dmfVersion);
|
||||
} else {
|
||||
w=e->saveFur();
|
||||
w=e->saveFur(false,settings.newPatternFormat);
|
||||
}
|
||||
if (w==NULL) {
|
||||
lastError=e->getLastError();
|
||||
|
|
@ -1982,35 +1988,56 @@ int FurnaceGUI::save(String path, int dmfVersion) {
|
|||
w->finish();
|
||||
return 1;
|
||||
}
|
||||
#ifdef FURNACE_ZLIB_COMPRESS
|
||||
unsigned char zbuf[131072];
|
||||
int ret;
|
||||
z_stream zl;
|
||||
memset(&zl,0,sizeof(z_stream));
|
||||
ret=deflateInit(&zl,Z_DEFAULT_COMPRESSION);
|
||||
if (ret!=Z_OK) {
|
||||
logE("zlib error!");
|
||||
lastError="compression error";
|
||||
fclose(outFile);
|
||||
w->finish();
|
||||
return 2;
|
||||
}
|
||||
zl.avail_in=w->size();
|
||||
zl.next_in=w->getFinalBuf();
|
||||
while (zl.avail_in>0) {
|
||||
if (settings.compress) {
|
||||
unsigned char zbuf[131072];
|
||||
int ret;
|
||||
z_stream zl;
|
||||
memset(&zl,0,sizeof(z_stream));
|
||||
ret=deflateInit(&zl,Z_DEFAULT_COMPRESSION);
|
||||
if (ret!=Z_OK) {
|
||||
logE("zlib error!");
|
||||
lastError="compression error";
|
||||
fclose(outFile);
|
||||
w->finish();
|
||||
return 2;
|
||||
}
|
||||
zl.avail_in=w->size();
|
||||
zl.next_in=w->getFinalBuf();
|
||||
while (zl.avail_in>0) {
|
||||
zl.avail_out=131072;
|
||||
zl.next_out=zbuf;
|
||||
if ((ret=deflate(&zl,Z_NO_FLUSH))==Z_STREAM_ERROR) {
|
||||
logE("zlib stream error!");
|
||||
lastError="zlib stream error";
|
||||
deflateEnd(&zl);
|
||||
fclose(outFile);
|
||||
w->finish();
|
||||
return 2;
|
||||
}
|
||||
size_t amount=131072-zl.avail_out;
|
||||
if (amount>0) {
|
||||
if (fwrite(zbuf,1,amount,outFile)!=amount) {
|
||||
logE("did not write entirely: %s!",strerror(errno));
|
||||
lastError=strerror(errno);
|
||||
deflateEnd(&zl);
|
||||
fclose(outFile);
|
||||
w->finish();
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
zl.avail_out=131072;
|
||||
zl.next_out=zbuf;
|
||||
if ((ret=deflate(&zl,Z_NO_FLUSH))==Z_STREAM_ERROR) {
|
||||
logE("zlib stream error!");
|
||||
lastError="zlib stream error";
|
||||
if ((ret=deflate(&zl,Z_FINISH))==Z_STREAM_ERROR) {
|
||||
logE("zlib finish stream error!");
|
||||
lastError="zlib finish stream error";
|
||||
deflateEnd(&zl);
|
||||
fclose(outFile);
|
||||
w->finish();
|
||||
return 2;
|
||||
}
|
||||
size_t amount=131072-zl.avail_out;
|
||||
if (amount>0) {
|
||||
if (fwrite(zbuf,1,amount,outFile)!=amount) {
|
||||
if (131072-zl.avail_out>0) {
|
||||
if (fwrite(zbuf,1,131072-zl.avail_out,outFile)!=(131072-zl.avail_out)) {
|
||||
logE("did not write entirely: %s!",strerror(errno));
|
||||
lastError=strerror(errno);
|
||||
deflateEnd(&zl);
|
||||
|
|
@ -2019,37 +2046,16 @@ int FurnaceGUI::save(String path, int dmfVersion) {
|
|||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
zl.avail_out=131072;
|
||||
zl.next_out=zbuf;
|
||||
if ((ret=deflate(&zl,Z_FINISH))==Z_STREAM_ERROR) {
|
||||
logE("zlib finish stream error!");
|
||||
lastError="zlib finish stream error";
|
||||
deflateEnd(&zl);
|
||||
fclose(outFile);
|
||||
w->finish();
|
||||
return 2;
|
||||
}
|
||||
if (131072-zl.avail_out>0) {
|
||||
if (fwrite(zbuf,1,131072-zl.avail_out,outFile)!=(131072-zl.avail_out)) {
|
||||
} else {
|
||||
if (fwrite(w->getFinalBuf(),1,w->size(),outFile)!=w->size()) {
|
||||
logE("did not write entirely: %s!",strerror(errno));
|
||||
lastError=strerror(errno);
|
||||
deflateEnd(&zl);
|
||||
fclose(outFile);
|
||||
w->finish();
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
deflateEnd(&zl);
|
||||
#else
|
||||
if (fwrite(w->getFinalBuf(),1,w->size(),outFile)!=w->size()) {
|
||||
logE("did not write entirely: %s!",strerror(errno));
|
||||
lastError=strerror(errno);
|
||||
fclose(outFile);
|
||||
w->finish();
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
fclose(outFile);
|
||||
w->finish();
|
||||
backupLock.lock();
|
||||
|
|
@ -2262,6 +2268,11 @@ void FurnaceGUI::exportAudio(String path, DivAudioExportModes mode) {
|
|||
displayExporting=true;
|
||||
}
|
||||
|
||||
void FurnaceGUI::editStr(String* which) {
|
||||
editString=which;
|
||||
displayEditString=true;
|
||||
}
|
||||
|
||||
void FurnaceGUI::showWarning(String what, FurnaceGUIWarnings type) {
|
||||
warnString=what;
|
||||
warnAction=type;
|
||||
|
|
@ -2842,8 +2853,14 @@ void FurnaceGUI::editOptions(bool topMenu) {
|
|||
ImGui::Separator();
|
||||
|
||||
if (ImGui::MenuItem("flip selection",BIND_FOR(GUI_ACTION_PAT_FLIP_SELECTION))) doFlip();
|
||||
if (ImGui::MenuItem("collapse",BIND_FOR(GUI_ACTION_PAT_COLLAPSE_ROWS))) doCollapse(2,selStart,selEnd);
|
||||
if (ImGui::MenuItem("expand",BIND_FOR(GUI_ACTION_PAT_EXPAND_ROWS))) doExpand(2,selStart,selEnd);
|
||||
|
||||
ImGui::SetNextItemWidth(120.0f*dpiScale);
|
||||
if (ImGui::InputInt("collapse/expand amount##CollapseAmount",&collapseAmount,1,1)) {
|
||||
if (collapseAmount<2) collapseAmount=2;
|
||||
if (collapseAmount>256) collapseAmount=256;
|
||||
}
|
||||
if (ImGui::MenuItem("collapse",BIND_FOR(GUI_ACTION_PAT_COLLAPSE_ROWS))) doCollapse(collapseAmount,selStart,selEnd);
|
||||
if (ImGui::MenuItem("expand",BIND_FOR(GUI_ACTION_PAT_EXPAND_ROWS))) doExpand(collapseAmount,selStart,selEnd);
|
||||
|
||||
if (topMenu) {
|
||||
ImGui::Separator();
|
||||
|
|
@ -2933,6 +2950,12 @@ int _processEvent(void* instance, SDL_Event* event) {
|
|||
return ((FurnaceGUI*)instance)->processEvent(event);
|
||||
}
|
||||
|
||||
#if SDL_VERSION_ATLEAST(2,0,17)
|
||||
#define VALID_MODS KMOD_NUM|KMOD_CAPS|KMOD_SCROLL
|
||||
#else
|
||||
#define VALID_MODS KMOD_NUM|KMOD_CAPS
|
||||
#endif
|
||||
|
||||
int FurnaceGUI::processEvent(SDL_Event* ev) {
|
||||
if (introPos<11.0 && !shortIntro) return 1;
|
||||
#ifdef IS_MOBILE
|
||||
|
|
@ -2944,7 +2967,7 @@ int FurnaceGUI::processEvent(SDL_Event* ev) {
|
|||
}
|
||||
#endif
|
||||
if (ev->type==SDL_KEYDOWN) {
|
||||
if (!ev->key.repeat && latchTarget==0 && !wantCaptureKeyboard && !sampleMapWaitingInput && (ev->key.keysym.mod&(~(KMOD_NUM|KMOD_CAPS|KMOD_SCROLL)))==0) {
|
||||
if (!ev->key.repeat && latchTarget==0 && !wantCaptureKeyboard && !sampleMapWaitingInput && (ev->key.keysym.mod&(~(VALID_MODS)))==0) {
|
||||
if (settings.notePreviewBehavior==0) return 1;
|
||||
switch (curWindow) {
|
||||
case GUI_WINDOW_SAMPLE_EDIT:
|
||||
|
|
@ -3397,6 +3420,7 @@ bool FurnaceGUI::loop() {
|
|||
logV("portrait: %d (%dx%d)",portrait,scrW,scrH);
|
||||
logD("window resized to %dx%d",scrW,scrH);
|
||||
updateWindow=true;
|
||||
rend->resized(ev);
|
||||
break;
|
||||
case SDL_WINDOWEVENT_MOVED:
|
||||
scrX=ev.window.data1;
|
||||
|
|
@ -3431,6 +3455,7 @@ bool FurnaceGUI::loop() {
|
|||
break;
|
||||
}
|
||||
break;
|
||||
#if SDL_VERSION_ATLEAST(2,0,17)
|
||||
case SDL_DISPLAYEVENT: {
|
||||
switch (ev.display.event) {
|
||||
case SDL_DISPLAYEVENT_CONNECTED:
|
||||
|
|
@ -3448,6 +3473,7 @@ bool FurnaceGUI::loop() {
|
|||
}
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
case SDL_KEYDOWN:
|
||||
if (!ImGui::GetIO().WantCaptureKeyboard) {
|
||||
keyDown(ev);
|
||||
|
|
@ -3464,7 +3490,7 @@ bool FurnaceGUI::loop() {
|
|||
int sampleCountBefore=e->song.sampleLen;
|
||||
std::vector<DivInstrument*> instruments=e->instrumentFromFile(ev.drop.file);
|
||||
DivWavetable* droppedWave=NULL;
|
||||
DivSample* droppedSample=NULL;;
|
||||
DivSample* droppedSample=NULL;
|
||||
if (!instruments.empty()) {
|
||||
if (e->song.sampleLen!=sampleCountBefore) {
|
||||
e->renderSamplesP();
|
||||
|
|
@ -3510,7 +3536,8 @@ bool FurnaceGUI::loop() {
|
|||
// update config x/y/w/h values based on scrMax state
|
||||
if (updateWindow) {
|
||||
logV("updateWindow is true");
|
||||
if (!scrMax) {
|
||||
if (!scrMax && !fullScreen) {
|
||||
logV("updating scrConf");
|
||||
scrConfX=scrX;
|
||||
scrConfY=scrY;
|
||||
scrConfW=scrW;
|
||||
|
|
@ -3518,8 +3545,8 @@ bool FurnaceGUI::loop() {
|
|||
}
|
||||
}
|
||||
// update canvas size as well
|
||||
if (SDL_GetRendererOutputSize(sdlRend,&canvasW,&canvasH)!=0) {
|
||||
logW("loop: error while getting output size! %s",SDL_GetError());
|
||||
if (!rend->getOutputSize(canvasW,canvasH)) {
|
||||
logW("loop: error while getting output size!");
|
||||
} else {
|
||||
//logV("updateWindow: canvas size %dx%d",canvasW,canvasH);
|
||||
// and therefore window size
|
||||
|
|
@ -3740,9 +3767,13 @@ bool FurnaceGUI::loop() {
|
|||
});
|
||||
}
|
||||
|
||||
bool fontsFailed=false;
|
||||
|
||||
layoutTimeBegin=SDL_GetPerformanceCounter();
|
||||
|
||||
ImGui_ImplSDLRenderer_NewFrame();
|
||||
if (!rend->newFrame()) {
|
||||
fontsFailed=true;
|
||||
}
|
||||
ImGui_ImplSDL2_NewFrame(sdlWin);
|
||||
ImGui::NewFrame();
|
||||
|
||||
|
|
@ -3768,6 +3799,7 @@ bool FurnaceGUI::loop() {
|
|||
}
|
||||
}
|
||||
if (ImGui::BeginMenu("open recent")) {
|
||||
exitDisabledTimer=1;
|
||||
for (int i=0; i<(int)recentFile.size(); i++) {
|
||||
String item=recentFile[i];
|
||||
if (ImGui::MenuItem(item.c_str())) {
|
||||
|
|
@ -3814,6 +3846,7 @@ bool FurnaceGUI::loop() {
|
|||
}
|
||||
ImGui::Separator();
|
||||
if (ImGui::BeginMenu("export audio...")) {
|
||||
exitDisabledTimer=1;
|
||||
if (ImGui::MenuItem("one file")) {
|
||||
openFileDialog(GUI_FILE_EXPORT_AUDIO_ONE);
|
||||
}
|
||||
|
|
@ -3832,6 +3865,7 @@ bool FurnaceGUI::loop() {
|
|||
ImGui::EndMenu();
|
||||
}
|
||||
if (ImGui::BeginMenu("export VGM...")) {
|
||||
exitDisabledTimer=1;
|
||||
ImGui::Text("settings:");
|
||||
if (ImGui::BeginCombo("format version",fmt::sprintf("%d.%.2x",vgmExportVersion>>8,vgmExportVersion&0xff).c_str())) {
|
||||
for (int i=0; i<7; i++) {
|
||||
|
|
@ -3920,6 +3954,7 @@ bool FurnaceGUI::loop() {
|
|||
}
|
||||
if (numZSMCompat > 0) {
|
||||
if (ImGui::BeginMenu("export ZSM...")) {
|
||||
exitDisabledTimer=1;
|
||||
ImGui::Text("Commander X16 Zsound Music File");
|
||||
if (ImGui::InputInt("Tick Rate (Hz)",&zsmExportTickRate,1,2)) {
|
||||
if (zsmExportTickRate<1) zsmExportTickRate=1;
|
||||
|
|
@ -3940,6 +3975,7 @@ bool FurnaceGUI::loop() {
|
|||
}
|
||||
if (numAmiga && settings.iCannotWait) {
|
||||
if (ImGui::BeginMenu("export Amiga validation data...")) {
|
||||
exitDisabledTimer=1;
|
||||
ImGui::Text(
|
||||
"this is NOT ROM export! only use for making sure the\n"
|
||||
"Furnace Amiga emulator is working properly by\n"
|
||||
|
|
@ -3970,6 +4006,7 @@ bool FurnaceGUI::loop() {
|
|||
}
|
||||
}
|
||||
if (ImGui::BeginMenu("export command stream...")) {
|
||||
exitDisabledTimer=1;
|
||||
ImGui::Text(
|
||||
"this option exports a text or binary file which\n"
|
||||
"contains a dump of the internal command stream\n"
|
||||
|
|
@ -3987,6 +4024,7 @@ bool FurnaceGUI::loop() {
|
|||
}
|
||||
ImGui::Separator();
|
||||
if (ImGui::BeginMenu("add chip...")) {
|
||||
exitDisabledTimer=1;
|
||||
DivSystem picked=systemPicker();
|
||||
if (picked!=DIV_SYSTEM_NULL) {
|
||||
if (!e->addSystem(picked)) {
|
||||
|
|
@ -4003,6 +4041,7 @@ bool FurnaceGUI::loop() {
|
|||
ImGui::EndMenu();
|
||||
}
|
||||
if (ImGui::BeginMenu("configure chip...")) {
|
||||
exitDisabledTimer=1;
|
||||
for (int i=0; i<e->song.systemLen; i++) {
|
||||
if (ImGui::TreeNode(fmt::sprintf("%d. %s##_SYSP%d",i+1,getSystemName(e->song.system[i]),i).c_str())) {
|
||||
drawSysConf(i,e->song.system[i],e->song.systemFlags[i],true);
|
||||
|
|
@ -4012,6 +4051,7 @@ bool FurnaceGUI::loop() {
|
|||
ImGui::EndMenu();
|
||||
}
|
||||
if (ImGui::BeginMenu("change chip...")) {
|
||||
exitDisabledTimer=1;
|
||||
ImGui::Checkbox("Preserve channel positions",&preserveChanPos);
|
||||
for (int i=0; i<e->song.systemLen; i++) {
|
||||
if (ImGui::BeginMenu(fmt::sprintf("%d. %s##_SYSC%d",i+1,getSystemName(e->song.system[i]),i).c_str())) {
|
||||
|
|
@ -4031,6 +4071,7 @@ bool FurnaceGUI::loop() {
|
|||
ImGui::EndMenu();
|
||||
}
|
||||
if (ImGui::BeginMenu("remove chip...")) {
|
||||
exitDisabledTimer=1;
|
||||
ImGui::Checkbox("Preserve channel positions",&preserveChanPos);
|
||||
for (int i=0; i<e->song.systemLen; i++) {
|
||||
if (ImGui::MenuItem(fmt::sprintf("%d. %s##_SYSR%d",i+1,getSystemName(e->song.system[i]),i).c_str())) {
|
||||
|
|
@ -4047,6 +4088,7 @@ bool FurnaceGUI::loop() {
|
|||
}
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
ImGui::BeginDisabled(exitDisabledTimer);
|
||||
ImGui::Separator();
|
||||
if (ImGui::MenuItem("restore backup",BIND_FOR(GUI_ACTION_OPEN_BACKUP))) {
|
||||
doAction(GUI_ACTION_OPEN_BACKUP);
|
||||
|
|
@ -4059,7 +4101,10 @@ bool FurnaceGUI::loop() {
|
|||
quit=true;
|
||||
}
|
||||
}
|
||||
ImGui::EndDisabled();
|
||||
ImGui::EndMenu();
|
||||
} else {
|
||||
exitDisabledTimer=0;
|
||||
}
|
||||
if (ImGui::BeginMenu("edit")) {
|
||||
ImGui::Text("...");
|
||||
|
|
@ -4310,6 +4355,7 @@ bool FurnaceGUI::loop() {
|
|||
MEASURE(log,drawLog());
|
||||
MEASURE(compatFlags,drawCompatFlags());
|
||||
MEASURE(stats,drawStats());
|
||||
MEASURE(chanOsc,drawChanOsc());
|
||||
} else {
|
||||
globalWinFlags=0;
|
||||
ImGui::DockSpaceOverViewport(NULL,lockLayout?(ImGuiDockNodeFlags_NoWindowMenuButton|ImGuiDockNodeFlags_NoMove|ImGuiDockNodeFlags_NoResize|ImGuiDockNodeFlags_NoCloseButton|ImGuiDockNodeFlags_NoDocking|ImGuiDockNodeFlags_NoDockingSplitMe|ImGuiDockNodeFlags_NoDockingSplitOther):0);
|
||||
|
|
@ -4381,7 +4427,7 @@ bool FurnaceGUI::loop() {
|
|||
portrait=(scrW<scrH);
|
||||
logV("portrait: %d (%dx%d)",portrait,scrW,scrH);
|
||||
|
||||
SDL_GetRendererOutputSize(sdlRend,&canvasW,&canvasH);
|
||||
rend->getOutputSize(canvasW,canvasH);
|
||||
#endif
|
||||
if (patternOpen) nextWindow=GUI_WINDOW_PATTERN;
|
||||
#ifdef __APPLE__
|
||||
|
|
@ -4447,6 +4493,7 @@ bool FurnaceGUI::loop() {
|
|||
case GUI_FILE_SAMPLE_OPEN_REPLACE:
|
||||
case GUI_FILE_SAMPLE_OPEN_REPLACE_RAW:
|
||||
case GUI_FILE_SAMPLE_SAVE:
|
||||
case GUI_FILE_SAMPLE_SAVE_RAW:
|
||||
workingDirSample=fileDialog->getPath()+DIR_SEPARATOR_STR;
|
||||
break;
|
||||
case GUI_FILE_EXPORT_AUDIO_ONE:
|
||||
|
|
@ -4714,7 +4761,16 @@ bool FurnaceGUI::loop() {
|
|||
break;
|
||||
case GUI_FILE_SAMPLE_SAVE:
|
||||
if (curSample>=0 && curSample<(int)e->song.sample.size()) {
|
||||
e->song.sample[curSample]->save(copyOfName.c_str());
|
||||
if (!e->song.sample[curSample]->save(copyOfName.c_str())) {
|
||||
showError("could not save sample! open Log Viewer for more information.");
|
||||
}
|
||||
}
|
||||
break;
|
||||
case GUI_FILE_SAMPLE_SAVE_RAW:
|
||||
if (curSample>=0 && curSample<(int)e->song.sample.size()) {
|
||||
if (!e->song.sample[curSample]->saveRaw(copyOfName.c_str())) {
|
||||
showError("could not save sample! open Log Viewer for more information.");
|
||||
}
|
||||
}
|
||||
break;
|
||||
case GUI_FILE_EXPORT_AUDIO_ONE:
|
||||
|
|
@ -5020,7 +5076,28 @@ bool FurnaceGUI::loop() {
|
|||
newSongQuery="";
|
||||
newSongFirstFrame=true;
|
||||
displayNew=false;
|
||||
ImGui::OpenPopup("New Song");
|
||||
if (settings.newSongBehavior==1) {
|
||||
e->createNewFromDefaults();
|
||||
undoHist.clear();
|
||||
redoHist.clear();
|
||||
curFileName="";
|
||||
modified=false;
|
||||
curNibble=false;
|
||||
orderNibble=false;
|
||||
orderCursor=-1;
|
||||
samplePos=0;
|
||||
updateSampleTex=true;
|
||||
selStart=SelectionPoint();
|
||||
selEnd=SelectionPoint();
|
||||
cursor=SelectionPoint();
|
||||
updateWindowTitle();
|
||||
} else {
|
||||
ImGui::OpenPopup("New Song");
|
||||
}
|
||||
}
|
||||
|
||||
if (displayEditString) {
|
||||
ImGui::OpenPopup("EditString");
|
||||
}
|
||||
|
||||
if (nextWindow==GUI_WINDOW_ABOUT) {
|
||||
|
|
@ -5044,7 +5121,7 @@ bool FurnaceGUI::loop() {
|
|||
ImGui::EndPopup();
|
||||
}
|
||||
|
||||
//drawTutorial();
|
||||
drawTutorial();
|
||||
|
||||
ImVec2 newSongMinSize=mobileUI?ImVec2(canvasW-(portrait?0:(60.0*dpiScale)),canvasH-60.0*dpiScale):ImVec2(400.0f*dpiScale,200.0f*dpiScale);
|
||||
ImVec2 newSongMaxSize=ImVec2(canvasW-((mobileUI && !portrait)?(60.0*dpiScale):0),canvasH-(mobileUI?(60.0*dpiScale):0));
|
||||
|
|
@ -5562,6 +5639,27 @@ bool FurnaceGUI::loop() {
|
|||
ImGui::EndPopup();
|
||||
}
|
||||
|
||||
if (ImGui::BeginPopup("EditString",ImGuiWindowFlags_AlwaysAutoResize|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoSavedSettings)) {
|
||||
if (editString==NULL) {
|
||||
ImGui::Text("Error! No string provided!");
|
||||
} else {
|
||||
if (displayEditString) {
|
||||
ImGui::SetItemDefaultFocus();
|
||||
ImGui::SetKeyboardFocusHere();
|
||||
}
|
||||
ImGui::InputText("##StringVal",editString);
|
||||
}
|
||||
displayEditString=false;
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("OK") || ImGui::IsKeyPressed(ImGuiKey_Enter,false)) {
|
||||
editString=NULL;
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
ImGui::EndPopup();
|
||||
} else {
|
||||
editString=NULL;
|
||||
}
|
||||
|
||||
MEASURE_END(popup);
|
||||
|
||||
if (!tutorial.introPlayed || settings.alwaysPlayIntro!=0) {
|
||||
|
|
@ -5602,7 +5700,7 @@ bool FurnaceGUI::loop() {
|
|||
}
|
||||
}
|
||||
logD("saving backup...");
|
||||
SafeWriter* w=e->saveFur(true);
|
||||
SafeWriter* w=e->saveFur(true,true);
|
||||
logV("writing file...");
|
||||
|
||||
if (w!=NULL) {
|
||||
|
|
@ -5724,33 +5822,35 @@ bool FurnaceGUI::loop() {
|
|||
}
|
||||
}
|
||||
|
||||
SDL_SetRenderDrawColor(sdlRend,uiColors[GUI_COLOR_BACKGROUND].x*255,
|
||||
uiColors[GUI_COLOR_BACKGROUND].y*255,
|
||||
uiColors[GUI_COLOR_BACKGROUND].z*255,
|
||||
uiColors[GUI_COLOR_BACKGROUND].w*255);
|
||||
SDL_RenderClear(sdlRend);
|
||||
if (!settings.renderClearPos) {
|
||||
rend->clear(uiColors[GUI_COLOR_BACKGROUND]);
|
||||
}
|
||||
renderTimeBegin=SDL_GetPerformanceCounter();
|
||||
ImGui::Render();
|
||||
renderTimeEnd=SDL_GetPerformanceCounter();
|
||||
ImGui_ImplSDLRenderer_RenderDrawData(ImGui::GetDrawData());
|
||||
drawTimeBegin=SDL_GetPerformanceCounter();
|
||||
rend->renderGUI();
|
||||
if (mustClear) {
|
||||
SDL_RenderClear(sdlRend);
|
||||
rend->clear(ImVec4(0,0,0,0));
|
||||
mustClear--;
|
||||
} else {
|
||||
if (initialScreenWipe>0.0f && !settings.disableFadeIn) {
|
||||
WAKE_UP;
|
||||
initialScreenWipe-=ImGui::GetIO().DeltaTime*5.0f;
|
||||
if (initialScreenWipe>0.0f) {
|
||||
SDL_SetRenderDrawBlendMode(sdlRend,SDL_BLENDMODE_BLEND);
|
||||
SDL_SetRenderDrawColor(sdlRend,0,0,0,255*pow(initialScreenWipe,2.0f));
|
||||
SDL_RenderFillRect(sdlRend,NULL);
|
||||
rend->wipe(pow(initialScreenWipe,2.0f));
|
||||
}
|
||||
}
|
||||
}
|
||||
SDL_RenderPresent(sdlRend);
|
||||
drawTimeEnd=SDL_GetPerformanceCounter();
|
||||
rend->present();
|
||||
if (settings.renderClearPos) {
|
||||
rend->clear(uiColors[GUI_COLOR_BACKGROUND]);
|
||||
}
|
||||
|
||||
layoutTimeDelta=layoutTimeEnd-layoutTimeBegin;
|
||||
renderTimeDelta=renderTimeEnd-renderTimeBegin;
|
||||
drawTimeDelta=drawTimeEnd-drawTimeBegin;
|
||||
eventTimeDelta=eventTimeEnd-eventTimeBegin;
|
||||
|
||||
soloTimeout-=ImGui::GetIO().DeltaTime;
|
||||
|
|
@ -5760,6 +5860,10 @@ bool FurnaceGUI::loop() {
|
|||
WAKE_UP;
|
||||
}
|
||||
|
||||
if (!ImGui::IsMouseDown(ImGuiMouseButton_Left)) {
|
||||
exitDisabledTimer=0;
|
||||
}
|
||||
|
||||
wheelX=0;
|
||||
wheelY=0;
|
||||
wantScrollList=false;
|
||||
|
|
@ -5772,6 +5876,20 @@ bool FurnaceGUI::loop() {
|
|||
willCommit=false;
|
||||
}
|
||||
|
||||
if (fontsFailed) {
|
||||
showError("it appears I couldn't load these fonts. any setting you can check?");
|
||||
logE("couldn't load fonts");
|
||||
ImGui::GetIO().Fonts->Clear();
|
||||
mainFont=ImGui::GetIO().Fonts->AddFontDefault();
|
||||
patFont=mainFont;
|
||||
if (rend) rend->destroyFontsTexture();
|
||||
if (!ImGui::GetIO().Fonts->Build()) {
|
||||
logE("error again while building font atlas!");
|
||||
} else {
|
||||
rend->createFontsTexture();
|
||||
}
|
||||
}
|
||||
|
||||
if (!editOptsVisible) {
|
||||
latchTarget=0;
|
||||
latchNibble=false;
|
||||
|
|
@ -5846,6 +5964,10 @@ bool FurnaceGUI::init() {
|
|||
basicMode=true;
|
||||
}
|
||||
|
||||
insListDir=e->getConfBool("insListDir",false);
|
||||
waveListDir=e->getConfBool("waveListDir",false);
|
||||
sampleListDir=e->getConfBool("sampleListDir",false);
|
||||
|
||||
tempoView=e->getConfBool("tempoView",true);
|
||||
waveHex=e->getConfBool("waveHex",false);
|
||||
waveSigned=e->getConfBool("waveSigned",false);
|
||||
|
|
@ -5880,6 +6002,7 @@ bool FurnaceGUI::init() {
|
|||
pianoOptions=e->getConfBool("pianoOptions",pianoOptions);
|
||||
pianoSharePosition=e->getConfBool("pianoSharePosition",pianoSharePosition);
|
||||
pianoOptionsSet=e->getConfBool("pianoOptionsSet",pianoOptionsSet);
|
||||
pianoReadonly=e->getConfBool("pianoReadonly",false);
|
||||
pianoOffset=e->getConfInt("pianoOffset",pianoOffset);
|
||||
pianoOffsetEdit=e->getConfInt("pianoOffsetEdit",pianoOffsetEdit);
|
||||
pianoView=e->getConfInt("pianoView",pianoView);
|
||||
|
|
@ -5888,13 +6011,22 @@ bool FurnaceGUI::init() {
|
|||
chanOscCols=e->getConfInt("chanOscCols",3);
|
||||
chanOscColorX=e->getConfInt("chanOscColorX",GUI_OSCREF_CENTER);
|
||||
chanOscColorY=e->getConfInt("chanOscColorY",GUI_OSCREF_CENTER);
|
||||
chanOscTextX=e->getConfFloat("chanOscTextX",0.0f);
|
||||
chanOscTextY=e->getConfFloat("chanOscTextY",0.0f);
|
||||
chanOscAmplify=e->getConfFloat("chanOscAmplify",0.95f);
|
||||
chanOscWindowSize=e->getConfFloat("chanOscWindowSize",20.0f);
|
||||
chanOscWaveCorr=e->getConfBool("chanOscWaveCorr",true);
|
||||
chanOscOptions=e->getConfBool("chanOscOptions",false);
|
||||
chanOscNormalize=e->getConfBool("chanOscNormalize",false);
|
||||
chanOscTextFormat=e->getConfString("chanOscTextFormat","%c");
|
||||
chanOscColor.x=e->getConfFloat("chanOscColorR",1.0f);
|
||||
chanOscColor.y=e->getConfFloat("chanOscColorG",1.0f);
|
||||
chanOscColor.z=e->getConfFloat("chanOscColorB",1.0f);
|
||||
chanOscColor.w=e->getConfFloat("chanOscColorA",1.0f);
|
||||
chanOscTextColor.x=e->getConfFloat("chanOscTextColorR",1.0f);
|
||||
chanOscTextColor.y=e->getConfFloat("chanOscTextColorG",1.0f);
|
||||
chanOscTextColor.z=e->getConfFloat("chanOscTextColorB",1.0f);
|
||||
chanOscTextColor.w=e->getConfFloat("chanOscTextColorA",0.75f);
|
||||
chanOscUseGrad=e->getConfBool("chanOscUseGrad",false);
|
||||
chanOscGrad.fromString(e->getConfString("chanOscGrad",""));
|
||||
chanOscGrad.render();
|
||||
|
|
@ -5920,7 +6052,9 @@ bool FurnaceGUI::init() {
|
|||
e->setAutoNotePoly(noteInputPoly);
|
||||
|
||||
SDL_SetHint(SDL_HINT_VIDEO_ALLOW_SCREENSAVER,"1");
|
||||
#if SDL_VERSION_ATLEAST(2,0,17)
|
||||
SDL_SetHint(SDL_HINT_MOUSE_TOUCH_EVENTS,"0");
|
||||
#endif
|
||||
SDL_SetHint(SDL_HINT_TOUCH_MOUSE_EVENTS,"0");
|
||||
// don't disable compositing on KWin
|
||||
#if SDL_VERSION_ATLEAST(2,0,22)
|
||||
|
|
@ -5929,7 +6063,18 @@ bool FurnaceGUI::init() {
|
|||
#endif
|
||||
|
||||
// initialize SDL
|
||||
SDL_Init(SDL_INIT_VIDEO|SDL_INIT_HAPTIC);
|
||||
logD("initializing video...");
|
||||
if (SDL_Init(SDL_INIT_VIDEO)!=0) {
|
||||
logE("could not initialize video! %s",SDL_GetError());
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef IS_MOBILE
|
||||
logD("initializing haptic...");
|
||||
if (SDL_Init(SDL_INIT_HAPTIC)!=0) {
|
||||
logW("could not initialize haptic! %s",SDL_GetError());
|
||||
}
|
||||
#endif
|
||||
|
||||
const char* videoBackend=SDL_GetCurrentVideoDriver();
|
||||
if (videoBackend!=NULL) {
|
||||
|
|
@ -6030,7 +6175,28 @@ bool FurnaceGUI::init() {
|
|||
|
||||
logV("window size: %dx%d",scrW,scrH);
|
||||
|
||||
sdlWin=SDL_CreateWindow("Furnace",scrX,scrY,scrW,scrH,SDL_WINDOW_RESIZABLE|SDL_WINDOW_ALLOW_HIGHDPI|(scrMax?SDL_WINDOW_MAXIMIZED:0)|(fullScreen?SDL_WINDOW_FULLSCREEN_DESKTOP:0));
|
||||
if (!initRender()) {
|
||||
if (settings.renderBackend!="SDL" && !settings.renderBackend.empty()) {
|
||||
settings.renderBackend="";
|
||||
e->setConf("renderBackend","");
|
||||
e->saveConf();
|
||||
lastError=fmt::sprintf("\r\nthe render backend has been set to a safe value. please restart Furnace.");
|
||||
} else {
|
||||
lastError=fmt::sprintf("could not init renderer! %s",SDL_GetError());
|
||||
if (!settings.renderDriver.empty()) {
|
||||
settings.renderDriver="";
|
||||
e->setConf("renderDriver","");
|
||||
e->saveConf();
|
||||
lastError=fmt::sprintf("\r\nthe render driver has been set to a safe value. please restart Furnace.");
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
rend->preInit();
|
||||
|
||||
logD("creating window...");
|
||||
sdlWin=SDL_CreateWindow("Furnace",scrX,scrY,scrW,scrH,SDL_WINDOW_RESIZABLE|SDL_WINDOW_ALLOW_HIGHDPI|(scrMax?SDL_WINDOW_MAXIMIZED:0)|(fullScreen?SDL_WINDOW_FULLSCREEN_DESKTOP:0)|rend->getWindowFlags());
|
||||
if (sdlWin==NULL) {
|
||||
lastError=fmt::sprintf("could not open window! %s",SDL_GetError());
|
||||
return false;
|
||||
|
|
@ -6094,22 +6260,28 @@ bool FurnaceGUI::init() {
|
|||
SDL_SetHint(SDL_HINT_RENDER_DRIVER,settings.renderDriver.c_str());
|
||||
}
|
||||
|
||||
sdlRend=SDL_CreateRenderer(sdlWin,-1,SDL_RENDERER_ACCELERATED|SDL_RENDERER_PRESENTVSYNC|SDL_RENDERER_TARGETTEXTURE);
|
||||
|
||||
if (sdlRend==NULL) {
|
||||
lastError=fmt::sprintf("could not init renderer! %s",SDL_GetError());
|
||||
if (!settings.renderDriver.empty()) {
|
||||
settings.renderDriver="";
|
||||
e->setConf("renderDriver","");
|
||||
e->saveConf();
|
||||
lastError=fmt::sprintf("\r\nthe render driver has been set to a safe value. please restart Furnace.");
|
||||
logD("starting render backend...");
|
||||
if (!rend->init(sdlWin)) {
|
||||
if (settings.renderBackend!="SDL" && !settings.renderBackend.empty()) {
|
||||
settings.renderBackend="";
|
||||
//e->setConf("renderBackend","");
|
||||
//e->saveConf();
|
||||
//lastError=fmt::sprintf("\r\nthe render backend has been set to a safe value. please restart Furnace.");
|
||||
} else {
|
||||
lastError=fmt::sprintf("could not init renderer! %s",SDL_GetError());
|
||||
if (!settings.renderDriver.empty()) {
|
||||
settings.renderDriver="";
|
||||
e->setConf("renderDriver","");
|
||||
e->saveConf();
|
||||
lastError=fmt::sprintf("\r\nthe render driver has been set to a safe value. please restart Furnace.");
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// try acquiring the canvas size
|
||||
if (SDL_GetRendererOutputSize(sdlRend,&canvasW,&canvasH)!=0) {
|
||||
logW("could not get renderer output size! %s",SDL_GetError());
|
||||
if (!rend->getOutputSize(canvasW,canvasH)) {
|
||||
logW("could not get renderer output size!");
|
||||
} else {
|
||||
logV("canvas size: %dx%d",canvasW,canvasH);
|
||||
}
|
||||
|
|
@ -6117,30 +6289,45 @@ bool FurnaceGUI::init() {
|
|||
// special consideration for Wayland
|
||||
if (settings.dpiScale<0.5f) {
|
||||
if (strcmp(videoBackend,"wayland")==0) {
|
||||
dpiScale=(double)canvasW/(double)scrW;
|
||||
int realW=scrW;
|
||||
int realH=scrH;
|
||||
|
||||
SDL_GetWindowSize(sdlWin,&realW,&realH);
|
||||
|
||||
if (realW<1) {
|
||||
logW("screen width is zero!\n");
|
||||
dpiScale=1.0;
|
||||
} else {
|
||||
dpiScale=(double)canvasW/(double)realW;
|
||||
logV("we're on Wayland... scaling factor: %f",dpiScale);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
IMGUI_CHECKVERSION();
|
||||
ImGui::CreateContext();
|
||||
updateWindowTitle();
|
||||
|
||||
ImGui_ImplSDL2_InitForSDLRenderer(sdlWin,sdlRend);
|
||||
ImGui_ImplSDLRenderer_Init(sdlRend);
|
||||
rend->clear(ImVec4(0.0,0.0,0.0,1.0));
|
||||
rend->present();
|
||||
|
||||
logD("preparing user interface...");
|
||||
rend->initGUI(sdlWin);
|
||||
|
||||
applyUISettings();
|
||||
|
||||
logD("building font...");
|
||||
if (!ImGui::GetIO().Fonts->Build()) {
|
||||
logE("error while building font atlas!");
|
||||
showError("error while loading fonts! please check your settings.");
|
||||
ImGui::GetIO().Fonts->Clear();
|
||||
mainFont=ImGui::GetIO().Fonts->AddFontDefault();
|
||||
patFont=mainFont;
|
||||
ImGui_ImplSDLRenderer_DestroyFontsTexture();
|
||||
if (rend) rend->destroyFontsTexture();
|
||||
if (!ImGui::GetIO().Fonts->Build()) {
|
||||
logE("error again while building font atlas!");
|
||||
}
|
||||
}
|
||||
|
||||
logD("preparing layout...");
|
||||
strncpy(finalLayoutPath,(e->getConfigPath()+String(LAYOUT_INI)).c_str(),4095);
|
||||
backupPath=e->getConfigPath();
|
||||
if (backupPath.size()>0) {
|
||||
|
|
@ -6152,8 +6339,6 @@ bool FurnaceGUI::init() {
|
|||
ImGui::GetIO().ConfigFlags|=ImGuiConfigFlags_DockingEnable;
|
||||
toggleMobileUI(mobileUI,true);
|
||||
|
||||
updateWindowTitle();
|
||||
|
||||
for (int i=0; i<DIV_MAX_CHANS; i++) {
|
||||
oldPat[i]=new DivPattern;
|
||||
}
|
||||
|
|
@ -6200,6 +6385,7 @@ bool FurnaceGUI::init() {
|
|||
return curIns;
|
||||
});
|
||||
|
||||
#ifdef IS_MOBILE
|
||||
vibrator=SDL_HapticOpen(0);
|
||||
if (vibrator==NULL) {
|
||||
logD("could not open vibration device: %s",SDL_GetError());
|
||||
|
|
@ -6210,7 +6396,9 @@ bool FurnaceGUI::init() {
|
|||
logD("vibration not available: %s",SDL_GetError());
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
logI("done!");
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -6273,6 +6461,11 @@ void FurnaceGUI::commitState() {
|
|||
e->setConf("spoilerOpen",spoilerOpen);
|
||||
e->setConf("basicMode",basicMode);
|
||||
|
||||
// commit dir state
|
||||
e->setConf("insListDir",insListDir);
|
||||
e->setConf("waveListDir",waveListDir);
|
||||
e->setConf("sampleListDir",sampleListDir);
|
||||
|
||||
// commit last window size
|
||||
e->setConf("lastWindowWidth",scrConfW);
|
||||
e->setConf("lastWindowHeight",scrConfH);
|
||||
|
|
@ -6310,6 +6503,7 @@ void FurnaceGUI::commitState() {
|
|||
e->setConf("pianoOptions",pianoOptions);
|
||||
e->setConf("pianoSharePosition",pianoSharePosition);
|
||||
e->setConf("pianoOptionsSet",pianoOptionsSet);
|
||||
e->setConf("pianoReadonly",pianoReadonly);
|
||||
e->setConf("pianoOffset",pianoOffset);
|
||||
e->setConf("pianoOffsetEdit",pianoOffsetEdit);
|
||||
e->setConf("pianoView",pianoView);
|
||||
|
|
@ -6319,13 +6513,22 @@ void FurnaceGUI::commitState() {
|
|||
e->setConf("chanOscCols",chanOscCols);
|
||||
e->setConf("chanOscColorX",chanOscColorX);
|
||||
e->setConf("chanOscColorY",chanOscColorY);
|
||||
e->setConf("chanOscTextX",chanOscTextX);
|
||||
e->setConf("chanOscTextY",chanOscTextY);
|
||||
e->setConf("chanOscAmplify",chanOscAmplify);
|
||||
e->setConf("chanOscWindowSize",chanOscWindowSize);
|
||||
e->setConf("chanOscWaveCorr",chanOscWaveCorr);
|
||||
e->setConf("chanOscOptions",chanOscOptions);
|
||||
e->setConf("chanOscNormalize",chanOscNormalize);
|
||||
e->setConf("chanOscTextFormat",chanOscTextFormat);
|
||||
e->setConf("chanOscColorR",chanOscColor.x);
|
||||
e->setConf("chanOscColorG",chanOscColor.y);
|
||||
e->setConf("chanOscColorB",chanOscColor.z);
|
||||
e->setConf("chanOscColorA",chanOscColor.w);
|
||||
e->setConf("chanOscTextColorR",chanOscTextColor.x);
|
||||
e->setConf("chanOscTextColorG",chanOscTextColor.y);
|
||||
e->setConf("chanOscTextColorB",chanOscTextColor.z);
|
||||
e->setConf("chanOscTextColorA",chanOscTextColor.w);
|
||||
e->setConf("chanOscUseGrad",chanOscUseGrad);
|
||||
e->setConf("chanOscGrad",chanOscGrad.toString());
|
||||
|
||||
|
|
@ -6342,10 +6545,10 @@ void FurnaceGUI::commitState() {
|
|||
|
||||
bool FurnaceGUI::finish() {
|
||||
commitState();
|
||||
ImGui_ImplSDLRenderer_Shutdown();
|
||||
rend->quitGUI();
|
||||
ImGui_ImplSDL2_Shutdown();
|
||||
ImGui::DestroyContext();
|
||||
SDL_DestroyRenderer(sdlRend);
|
||||
quitRender();
|
||||
SDL_DestroyWindow(sdlWin);
|
||||
|
||||
if (vibrator) {
|
||||
|
|
@ -6365,8 +6568,9 @@ bool FurnaceGUI::finish() {
|
|||
|
||||
FurnaceGUI::FurnaceGUI():
|
||||
e(NULL),
|
||||
renderBackend(GUI_BACKEND_SDL),
|
||||
rend(NULL),
|
||||
sdlWin(NULL),
|
||||
sdlRend(NULL),
|
||||
vibrator(NULL),
|
||||
vibratorAvailable(false),
|
||||
sampleTex(NULL),
|
||||
|
|
@ -6388,6 +6592,7 @@ FurnaceGUI::FurnaceGUI():
|
|||
portrait(false),
|
||||
injectBackUp(false),
|
||||
mobileMenuOpen(false),
|
||||
warnColorPushed(false),
|
||||
wantCaptureKeyboard(false),
|
||||
oldWantCaptureKeyboard(false),
|
||||
displayMacroMenu(false),
|
||||
|
|
@ -6401,6 +6606,7 @@ FurnaceGUI::FurnaceGUI():
|
|||
displayPendingRawSample(false),
|
||||
snesFilterHex(false),
|
||||
modTableHex(false),
|
||||
displayEditString(false),
|
||||
mobileEdit(false),
|
||||
vgmExportVersion(0x171),
|
||||
vgmExportTrailingTicks(-1),
|
||||
|
|
@ -6420,6 +6626,7 @@ FurnaceGUI::FurnaceGUI():
|
|||
fmPreviewOn(false),
|
||||
fmPreviewPaused(false),
|
||||
fmPreviewOPN(NULL),
|
||||
editString(NULL),
|
||||
pendingRawSampleDepth(8),
|
||||
pendingRawSampleChannels(1),
|
||||
pendingRawSampleUnsigned(false),
|
||||
|
|
@ -6474,7 +6681,6 @@ FurnaceGUI::FurnaceGUI():
|
|||
loopEnd(-1),
|
||||
isClipping(0),
|
||||
extraChannelButtons(0),
|
||||
patNameTarget(-1),
|
||||
newSongCategory(0),
|
||||
latchTarget(0),
|
||||
wheelX(0),
|
||||
|
|
@ -6486,6 +6692,7 @@ FurnaceGUI::FurnaceGUI():
|
|||
oldBeat(-1),
|
||||
oldBar(-1),
|
||||
curGroove(-1),
|
||||
exitDisabledTimer(0),
|
||||
soloTimeout(0.0f),
|
||||
exportFadeOut(5.0),
|
||||
editControlsOpen(true),
|
||||
|
|
@ -6524,6 +6731,10 @@ FurnaceGUI::FurnaceGUI():
|
|||
groovesOpen(false),
|
||||
introMonOpen(false),
|
||||
basicMode(true),
|
||||
shortIntro(false),
|
||||
insListDir(false),
|
||||
waveListDir(false),
|
||||
sampleListDir(false),
|
||||
clockShowReal(true),
|
||||
clockShowRow(true),
|
||||
clockShowBeat(true),
|
||||
|
|
@ -6541,7 +6752,6 @@ FurnaceGUI::FurnaceGUI():
|
|||
collapseWindow(false),
|
||||
demandScrollX(false),
|
||||
fancyPattern(false),
|
||||
wantPatName(false),
|
||||
firstFrame(true),
|
||||
tempoView(true),
|
||||
waveHex(false),
|
||||
|
|
@ -6558,6 +6768,7 @@ FurnaceGUI::FurnaceGUI():
|
|||
dragMobileMenu(false),
|
||||
dragMobileEditButton(false),
|
||||
wantGrooveListFocus(false),
|
||||
lastAssetType(0),
|
||||
curWindow(GUI_WINDOW_NOTHING),
|
||||
nextWindow(GUI_WINDOW_NOTHING),
|
||||
curWindowLast(GUI_WINDOW_NOTHING),
|
||||
|
|
@ -6573,6 +6784,7 @@ FurnaceGUI::FurnaceGUI():
|
|||
wavePreviewLen(32),
|
||||
wavePreviewHeight(255),
|
||||
wavePreviewInit(true),
|
||||
wavePreviewPaused(false),
|
||||
pgSys(0),
|
||||
pgAddr(0),
|
||||
pgVal(0),
|
||||
|
|
@ -6662,6 +6874,9 @@ FurnaceGUI::FurnaceGUI():
|
|||
renderTimeBegin(0),
|
||||
renderTimeEnd(0),
|
||||
renderTimeDelta(0),
|
||||
drawTimeBegin(0),
|
||||
drawTimeEnd(0),
|
||||
drawTimeDelta(0),
|
||||
eventTimeBegin(0),
|
||||
eventTimeEnd(0),
|
||||
eventTimeDelta(0),
|
||||
|
|
@ -6670,11 +6885,14 @@ FurnaceGUI::FurnaceGUI():
|
|||
sysToMove(-1),
|
||||
sysToDelete(-1),
|
||||
opToMove(-1),
|
||||
assetToMove(-1),
|
||||
dirToMove(-1),
|
||||
transposeAmount(0),
|
||||
randomizeMin(0),
|
||||
randomizeMax(255),
|
||||
fadeMin(0),
|
||||
fadeMax(255),
|
||||
collapseAmount(2),
|
||||
scaleMax(100.0f),
|
||||
fadeMode(false),
|
||||
randomMode(false),
|
||||
|
|
@ -6698,6 +6916,7 @@ FurnaceGUI::FurnaceGUI():
|
|||
sampleDragMode(false),
|
||||
sampleDrag16(false),
|
||||
sampleZoomAuto(true),
|
||||
sampleSelTarget(0),
|
||||
sampleDragTarget(NULL),
|
||||
sampleDragStart(0,0),
|
||||
sampleDragAreaSize(0,0),
|
||||
|
|
@ -6732,11 +6951,17 @@ FurnaceGUI::FurnaceGUI():
|
|||
chanOscColorX(GUI_OSCREF_CENTER),
|
||||
chanOscColorY(GUI_OSCREF_CENTER),
|
||||
chanOscWindowSize(20.0f),
|
||||
chanOscTextX(0.0f),
|
||||
chanOscTextY(0.0f),
|
||||
chanOscAmplify(0.95f),
|
||||
chanOscWaveCorr(true),
|
||||
chanOscOptions(false),
|
||||
updateChanOscGradTex(true),
|
||||
chanOscUseGrad(false),
|
||||
chanOscNormalize(false),
|
||||
chanOscTextFormat("%c"),
|
||||
chanOscColor(1.0f,1.0f,1.0f,1.0f),
|
||||
chanOscTextColor(1.0f,1.0f,1.0f,0.75f),
|
||||
chanOscGrad(64,64),
|
||||
chanOscGradTex(NULL),
|
||||
followLog(true),
|
||||
|
|
@ -6746,6 +6971,7 @@ FurnaceGUI::FurnaceGUI():
|
|||
pianoOptions(true),
|
||||
pianoSharePosition(false),
|
||||
pianoOptionsSet(false),
|
||||
pianoReadonly(false),
|
||||
pianoOffset(6),
|
||||
pianoOffsetEdit(9),
|
||||
pianoView(PIANO_LAYOUT_AUTOMATIC),
|
||||
|
|
@ -6755,6 +6981,7 @@ FurnaceGUI::FurnaceGUI():
|
|||
pianoOctavesEdit(4),
|
||||
pianoOptions(false),
|
||||
pianoSharePosition(true),
|
||||
pianoReadonly(false),
|
||||
pianoOffset(6),
|
||||
pianoOffsetEdit(6),
|
||||
pianoView(PIANO_LAYOUT_STANDARD),
|
||||
|
|
@ -6779,6 +7006,7 @@ FurnaceGUI::FurnaceGUI():
|
|||
mustClear(2),
|
||||
initialScreenWipe(1.0f),
|
||||
introSkipDo(false),
|
||||
introStopped(false),
|
||||
curTutorial(-1),
|
||||
curTutorialStep(0) {
|
||||
// value keys
|
||||
|
|
|
|||
168
src/gui/gui.h
168
src/gui/gui.h
|
|
@ -23,8 +23,7 @@
|
|||
#include "../engine/engine.h"
|
||||
#include "../engine/waveSynth.h"
|
||||
#include "imgui.h"
|
||||
#include "imgui_impl_sdl.h"
|
||||
#include "imgui_impl_sdlrenderer.h"
|
||||
#include "imgui_impl_sdl2.h"
|
||||
#include <SDL.h>
|
||||
#include <fftw3.h>
|
||||
#include <deque>
|
||||
|
|
@ -70,23 +69,66 @@
|
|||
|
||||
#define FM_PREVIEW_SIZE 512
|
||||
|
||||
enum FurnaceGUIRenderBackend {
|
||||
GUI_BACKEND_SDL=0,
|
||||
GUI_BACKEND_GL,
|
||||
GUI_BACKEND_DX11
|
||||
};
|
||||
|
||||
#ifdef HAVE_RENDER_SDL
|
||||
#define GUI_BACKEND_DEFAULT GUI_BACKEND_SDL
|
||||
#define GUI_BACKEND_DEFAULT_NAME "SDL"
|
||||
#else
|
||||
#ifdef HAVE_RENDER_DX11
|
||||
#define GUI_BACKEND_DEFAULT GUI_BACKEND_DX11
|
||||
#define GUI_BACKEND_DEFAULT_NAME "DirectX 11"
|
||||
#else
|
||||
#define GUI_BACKEND_DEFAULT GUI_BACKEND_GL
|
||||
#define GUI_BACKEND_DEFAULT_NAME "OpenGL"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// TODO:
|
||||
// - add colors for FM envelope and waveform
|
||||
// - maybe add "alternate" color for FM modulators/carriers (a bit difficult)
|
||||
enum FurnaceGUIColors {
|
||||
GUI_COLOR_BACKGROUND=0,
|
||||
GUI_COLOR_FRAME_BACKGROUND,
|
||||
GUI_COLOR_FRAME_BACKGROUND_CHILD,
|
||||
GUI_COLOR_FRAME_BACKGROUND_POPUP,
|
||||
GUI_COLOR_MODAL_BACKDROP,
|
||||
GUI_COLOR_HEADER,
|
||||
GUI_COLOR_TEXT,
|
||||
GUI_COLOR_ACCENT_PRIMARY,
|
||||
GUI_COLOR_ACCENT_SECONDARY,
|
||||
GUI_COLOR_TITLE_INACTIVE,
|
||||
GUI_COLOR_TITLE_COLLAPSED,
|
||||
GUI_COLOR_MENU_BAR,
|
||||
GUI_COLOR_BORDER,
|
||||
GUI_COLOR_BORDER_SHADOW,
|
||||
GUI_COLOR_SCROLL_BACKGROUND,
|
||||
GUI_COLOR_SCROLL,
|
||||
GUI_COLOR_SCROLL_HOVER,
|
||||
GUI_COLOR_SCROLL_ACTIVE,
|
||||
GUI_COLOR_SEPARATOR,
|
||||
GUI_COLOR_SEPARATOR_HOVER,
|
||||
GUI_COLOR_SEPARATOR_ACTIVE,
|
||||
GUI_COLOR_DOCKING_PREVIEW,
|
||||
GUI_COLOR_DOCKING_EMPTY,
|
||||
GUI_COLOR_TABLE_HEADER,
|
||||
GUI_COLOR_TABLE_BORDER_HARD,
|
||||
GUI_COLOR_TABLE_BORDER_SOFT,
|
||||
GUI_COLOR_DRAG_DROP_TARGET,
|
||||
GUI_COLOR_NAV_HIGHLIGHT,
|
||||
GUI_COLOR_NAV_WIN_HIGHLIGHT,
|
||||
GUI_COLOR_NAV_WIN_BACKDROP,
|
||||
GUI_COLOR_TOGGLE_OFF,
|
||||
GUI_COLOR_TOGGLE_ON,
|
||||
GUI_COLOR_EDITING,
|
||||
GUI_COLOR_SONG_LOOP,
|
||||
GUI_COLOR_DESTRUCTIVE,
|
||||
GUI_COLOR_WARNING,
|
||||
GUI_COLOR_ERROR,
|
||||
|
||||
GUI_COLOR_FILE_DIR,
|
||||
GUI_COLOR_FILE_SONG_NATIVE,
|
||||
|
|
@ -365,6 +407,7 @@ enum FurnaceGUIFileDialogs {
|
|||
GUI_FILE_SAMPLE_OPEN_REPLACE,
|
||||
GUI_FILE_SAMPLE_OPEN_REPLACE_RAW,
|
||||
GUI_FILE_SAMPLE_SAVE,
|
||||
GUI_FILE_SAMPLE_SAVE_RAW,
|
||||
GUI_FILE_EXPORT_AUDIO_ONE,
|
||||
GUI_FILE_EXPORT_AUDIO_PER_SYS,
|
||||
GUI_FILE_EXPORT_AUDIO_PER_CHANNEL,
|
||||
|
|
@ -565,6 +608,7 @@ enum FurnaceGUIActions {
|
|||
GUI_ACTION_INS_LIST_EDIT,
|
||||
GUI_ACTION_INS_LIST_UP,
|
||||
GUI_ACTION_INS_LIST_DOWN,
|
||||
GUI_ACTION_INS_LIST_DIR_VIEW,
|
||||
GUI_ACTION_INS_LIST_MAX,
|
||||
|
||||
GUI_ACTION_WAVE_LIST_MIN,
|
||||
|
|
@ -581,6 +625,7 @@ enum FurnaceGUIActions {
|
|||
GUI_ACTION_WAVE_LIST_EDIT,
|
||||
GUI_ACTION_WAVE_LIST_UP,
|
||||
GUI_ACTION_WAVE_LIST_DOWN,
|
||||
GUI_ACTION_WAVE_LIST_DIR_VIEW,
|
||||
GUI_ACTION_WAVE_LIST_MAX,
|
||||
|
||||
GUI_ACTION_SAMPLE_LIST_MIN,
|
||||
|
|
@ -591,6 +636,7 @@ enum FurnaceGUIActions {
|
|||
GUI_ACTION_SAMPLE_LIST_OPEN_RAW,
|
||||
GUI_ACTION_SAMPLE_LIST_OPEN_REPLACE_RAW,
|
||||
GUI_ACTION_SAMPLE_LIST_SAVE,
|
||||
GUI_ACTION_SAMPLE_LIST_SAVE_RAW,
|
||||
GUI_ACTION_SAMPLE_LIST_MOVE_UP,
|
||||
GUI_ACTION_SAMPLE_LIST_MOVE_DOWN,
|
||||
GUI_ACTION_SAMPLE_LIST_DELETE,
|
||||
|
|
@ -599,6 +645,7 @@ enum FurnaceGUIActions {
|
|||
GUI_ACTION_SAMPLE_LIST_DOWN,
|
||||
GUI_ACTION_SAMPLE_LIST_PREVIEW,
|
||||
GUI_ACTION_SAMPLE_LIST_STOP_PREVIEW,
|
||||
GUI_ACTION_SAMPLE_LIST_DIR_VIEW,
|
||||
GUI_ACTION_SAMPLE_LIST_MAX,
|
||||
|
||||
GUI_ACTION_SAMPLE_MIN,
|
||||
|
|
@ -1165,7 +1212,7 @@ struct FurnaceGUIQueryResult {
|
|||
|
||||
struct FurnaceGUIImage {
|
||||
unsigned char* data;
|
||||
SDL_Texture* tex;
|
||||
void* tex;
|
||||
int width, height, ch;
|
||||
|
||||
FurnaceGUIImage():
|
||||
|
|
@ -1187,15 +1234,52 @@ struct FurnaceGUIPerfMetric {
|
|||
elapsed(0) {}
|
||||
};
|
||||
|
||||
enum FurnaceGUIBlendMode {
|
||||
GUI_BLEND_MODE_NONE=0,
|
||||
GUI_BLEND_MODE_BLEND,
|
||||
GUI_BLEND_MODE_ADD,
|
||||
GUI_BLEND_MODE_MULTIPLY
|
||||
};
|
||||
|
||||
class FurnaceGUIRender {
|
||||
public:
|
||||
virtual ImTextureID getTextureID(void* which);
|
||||
virtual bool lockTexture(void* which, void** data, int* pitch);
|
||||
virtual bool unlockTexture(void* which);
|
||||
virtual bool updateTexture(void* which, void* data, int pitch);
|
||||
virtual void* createTexture(bool dynamic, int width, int height);
|
||||
virtual bool destroyTexture(void* which);
|
||||
virtual void setTextureBlendMode(void* which, FurnaceGUIBlendMode mode);
|
||||
virtual void setBlendMode(FurnaceGUIBlendMode mode);
|
||||
virtual void resized(const SDL_Event& ev);
|
||||
virtual void clear(ImVec4 color);
|
||||
virtual bool newFrame();
|
||||
virtual void createFontsTexture();
|
||||
virtual void destroyFontsTexture();
|
||||
virtual void renderGUI();
|
||||
virtual void wipe(float alpha);
|
||||
virtual void present();
|
||||
virtual bool getOutputSize(int& w, int& h);
|
||||
virtual int getWindowFlags();
|
||||
virtual void preInit();
|
||||
virtual bool init(SDL_Window* win);
|
||||
virtual void initGUI(SDL_Window* win);
|
||||
virtual void quitGUI();
|
||||
virtual bool quit();
|
||||
virtual ~FurnaceGUIRender();
|
||||
};
|
||||
|
||||
class FurnaceGUI {
|
||||
DivEngine* e;
|
||||
|
||||
FurnaceGUIRenderBackend renderBackend;
|
||||
FurnaceGUIRender* rend;
|
||||
|
||||
SDL_Window* sdlWin;
|
||||
SDL_Renderer* sdlRend;
|
||||
SDL_Haptic* vibrator;
|
||||
bool vibratorAvailable;
|
||||
|
||||
SDL_Texture* sampleTex;
|
||||
|
||||
void* sampleTex;
|
||||
int sampleTexW, sampleTexH;
|
||||
bool updateSampleTex;
|
||||
|
||||
|
|
@ -1205,6 +1289,7 @@ class FurnaceGUI {
|
|||
String workingDirLayout, workingDirROM, workingDirTest;
|
||||
String mmlString[32];
|
||||
String mmlStringW, mmlStringSNES, grooveString, grooveListString, mmlStringModTable;
|
||||
String folderString;
|
||||
|
||||
std::vector<DivSystem> sysSearchResults;
|
||||
std::vector<FurnaceGUISysDef> newSongSearchResults;
|
||||
|
|
@ -1214,10 +1299,10 @@ class FurnaceGUI {
|
|||
|
||||
bool quit, warnQuit, willCommit, edit, modified, displayError, displayExporting, vgmExportLoop, zsmExportLoop, vgmExportPatternHints;
|
||||
bool vgmExportDirectStream, displayInsTypeList;
|
||||
bool portrait, injectBackUp, mobileMenuOpen;
|
||||
bool portrait, injectBackUp, mobileMenuOpen, warnColorPushed;
|
||||
bool wantCaptureKeyboard, oldWantCaptureKeyboard, displayMacroMenu;
|
||||
bool displayNew, fullScreen, preserveChanPos, wantScrollList, noteInputPoly;
|
||||
bool displayPendingIns, pendingInsSingle, displayPendingRawSample, snesFilterHex, modTableHex;
|
||||
bool displayPendingIns, pendingInsSingle, displayPendingRawSample, snesFilterHex, modTableHex, displayEditString;
|
||||
bool mobileEdit;
|
||||
bool willExport[DIV_MAX_CHIPS];
|
||||
int vgmExportVersion;
|
||||
|
|
@ -1235,6 +1320,7 @@ class FurnaceGUI {
|
|||
short fmPreview[FM_PREVIEW_SIZE];
|
||||
bool updateFMPreview, fmPreviewOn, fmPreviewPaused;
|
||||
void* fmPreviewOPN;
|
||||
String* editString;
|
||||
|
||||
String pendingRawSample;
|
||||
int pendingRawSampleDepth, pendingRawSampleChannels;
|
||||
|
|
@ -1400,8 +1486,10 @@ class FurnaceGUI {
|
|||
int channelFont;
|
||||
int channelTextCenter;
|
||||
int midiOutClock;
|
||||
int midiOutTime;
|
||||
int midiOutProgramChange;
|
||||
int midiOutMode;
|
||||
int midiOutTimeRate;
|
||||
int maxRecentFile;
|
||||
int centerPattern;
|
||||
int ordersCursor;
|
||||
|
|
@ -1415,6 +1503,12 @@ class FurnaceGUI {
|
|||
int alwaysPlayIntro;
|
||||
int iCannotWait;
|
||||
int orderButtonPos;
|
||||
int compress;
|
||||
int newPatternFormat;
|
||||
int renderClearPos;
|
||||
int insertBehavior;
|
||||
int pullDeleteRow;
|
||||
int newSongBehavior;
|
||||
unsigned int maxUndoSteps;
|
||||
String mainFontPath;
|
||||
String patFontPath;
|
||||
|
|
@ -1422,6 +1516,7 @@ class FurnaceGUI {
|
|||
String midiInDevice;
|
||||
String midiOutDevice;
|
||||
String c163Name;
|
||||
String renderBackend;
|
||||
String renderDriver;
|
||||
String initialSysName;
|
||||
String noteOffLabel;
|
||||
|
|
@ -1543,8 +1638,10 @@ class FurnaceGUI {
|
|||
channelFont(1),
|
||||
channelTextCenter(1),
|
||||
midiOutClock(0),
|
||||
midiOutTime(0),
|
||||
midiOutProgramChange(0),
|
||||
midiOutMode(1),
|
||||
midiOutTimeRate(0),
|
||||
maxRecentFile(10),
|
||||
centerPattern(0),
|
||||
ordersCursor(1),
|
||||
|
|
@ -1558,6 +1655,12 @@ class FurnaceGUI {
|
|||
alwaysPlayIntro(0),
|
||||
iCannotWait(0),
|
||||
orderButtonPos(2),
|
||||
compress(1),
|
||||
newPatternFormat(1),
|
||||
renderClearPos(0),
|
||||
insertBehavior(1),
|
||||
pullDeleteRow(1),
|
||||
newSongBehavior(0),
|
||||
maxUndoSteps(100),
|
||||
mainFontPath(""),
|
||||
patFontPath(""),
|
||||
|
|
@ -1565,6 +1668,7 @@ class FurnaceGUI {
|
|||
midiInDevice(""),
|
||||
midiOutDevice(""),
|
||||
c163Name(""),
|
||||
renderBackend(""),
|
||||
renderDriver(""),
|
||||
initialSysName("Sega Genesis/Mega Drive"),
|
||||
noteOffLabel("OFF"),
|
||||
|
|
@ -1577,12 +1681,12 @@ class FurnaceGUI {
|
|||
struct Tutorial {
|
||||
int userComesFrom;
|
||||
bool introPlayed;
|
||||
bool welcome;
|
||||
bool protoWelcome;
|
||||
bool taken[GUI_TUTORIAL_MAX];
|
||||
Tutorial():
|
||||
userComesFrom(0),
|
||||
introPlayed(false),
|
||||
welcome(false) {
|
||||
protoWelcome(false) {
|
||||
memset(taken,0,GUI_TUTORIAL_MAX*sizeof(bool));
|
||||
}
|
||||
} tutorial;
|
||||
|
|
@ -1592,9 +1696,9 @@ class FurnaceGUI {
|
|||
DivInstrument* prevInsData;
|
||||
|
||||
int curIns, curWave, curSample, curOctave, curOrder, prevIns, oldRow, oldOrder, oldOrder1, editStep, exportLoops, soloChan,orderEditMode, orderCursor;
|
||||
int loopOrder, loopRow, loopEnd, isClipping, extraChannelButtons, patNameTarget, newSongCategory, latchTarget;
|
||||
int loopOrder, loopRow, loopEnd, isClipping, extraChannelButtons, newSongCategory, latchTarget;
|
||||
int wheelX, wheelY, dragSourceX, dragSourceXFine, dragSourceY, dragDestinationX, dragDestinationXFine, dragDestinationY, oldBeat, oldBar;
|
||||
int curGroove;
|
||||
int curGroove, exitDisabledTimer;
|
||||
float soloTimeout;
|
||||
|
||||
double exportFadeOut;
|
||||
|
|
@ -1608,14 +1712,16 @@ class FurnaceGUI {
|
|||
bool groovesOpen, introMonOpen;
|
||||
|
||||
bool basicMode, shortIntro;
|
||||
bool insListDir, waveListDir, sampleListDir;
|
||||
|
||||
bool clockShowReal, clockShowRow, clockShowBeat, clockShowMetro, clockShowTime;
|
||||
float clockMetroTick[16];
|
||||
|
||||
SelectionPoint selStart, selEnd, cursor, cursorDrag, dragStart, dragEnd;
|
||||
bool selecting, selectingFull, dragging, curNibble, orderNibble, followOrders, followPattern, changeAllOrders, mobileUI;
|
||||
bool collapseWindow, demandScrollX, fancyPattern, wantPatName, firstFrame, tempoView, waveHex, waveSigned, waveGenVisible, lockLayout, editOptsVisible, latchNibble, nonLatchNibble;
|
||||
bool collapseWindow, demandScrollX, fancyPattern, firstFrame, tempoView, waveHex, waveSigned, waveGenVisible, lockLayout, editOptsVisible, latchNibble, nonLatchNibble;
|
||||
bool keepLoopAlive, keepGrooveAlive, orderScrollLocked, orderScrollTolerance, dragMobileMenu, dragMobileEditButton, wantGrooveListFocus;
|
||||
unsigned char lastAssetType;
|
||||
FurnaceGUIWindows curWindow, nextWindow, curWindowLast;
|
||||
std::atomic<FurnaceGUIWindows> curWindowThreadSafe;
|
||||
float peak[DIV_MAX_OUTPUTS];
|
||||
|
|
@ -1633,7 +1739,7 @@ class FurnaceGUI {
|
|||
|
||||
DivWaveSynth wavePreview;
|
||||
int wavePreviewLen, wavePreviewHeight;
|
||||
bool wavePreviewInit;
|
||||
bool wavePreviewInit, wavePreviewPaused;
|
||||
|
||||
// bit 31: ctrl
|
||||
// bit 30: reserved for SDL scancode mask
|
||||
|
|
@ -1770,6 +1876,7 @@ class FurnaceGUI {
|
|||
|
||||
int layoutTimeBegin, layoutTimeEnd, layoutTimeDelta;
|
||||
int renderTimeBegin, renderTimeEnd, renderTimeDelta;
|
||||
int drawTimeBegin, drawTimeEnd, drawTimeDelta;
|
||||
int eventTimeBegin, eventTimeEnd, eventTimeDelta;
|
||||
|
||||
FurnaceGUIPerfMetric perfMetrics[64];
|
||||
|
|
@ -1781,6 +1888,7 @@ class FurnaceGUI {
|
|||
std::map<FurnaceGUIImages,FurnaceGUIImage*> images;
|
||||
|
||||
int chanToMove, sysToMove, sysToDelete, opToMove;
|
||||
int assetToMove, dirToMove;
|
||||
|
||||
ImVec2 patWindowPos, patWindowSize;
|
||||
|
||||
|
|
@ -1789,7 +1897,7 @@ class FurnaceGUI {
|
|||
ImVec2 noteCellSize, insCellSize, volCellSize, effectCellSize, effectValCellSize;
|
||||
SelectionPoint sel1, sel2;
|
||||
int dummyRows, demandX;
|
||||
int transposeAmount, randomizeMin, randomizeMax, fadeMin, fadeMax;
|
||||
int transposeAmount, randomizeMin, randomizeMax, fadeMin, fadeMax, collapseAmount;
|
||||
float scaleMax;
|
||||
bool fadeMode, randomMode, haveHitBounds, pendingStepUpdate;
|
||||
|
||||
|
|
@ -1811,6 +1919,9 @@ class FurnaceGUI {
|
|||
int sampleSelStart, sampleSelEnd;
|
||||
bool sampleInfo, sampleCompatRate;
|
||||
bool sampleDragActive, sampleDragMode, sampleDrag16, sampleZoomAuto;
|
||||
// 0: start
|
||||
// 1: end
|
||||
unsigned char sampleSelTarget;
|
||||
void* sampleDragTarget;
|
||||
ImVec2 sampleDragStart;
|
||||
ImVec2 sampleDragAreaSize;
|
||||
|
|
@ -1841,11 +1952,12 @@ class FurnaceGUI {
|
|||
|
||||
// per-channel oscilloscope
|
||||
int chanOscCols, chanOscColorX, chanOscColorY;
|
||||
float chanOscWindowSize;
|
||||
bool chanOscWaveCorr, chanOscOptions, updateChanOscGradTex, chanOscUseGrad;
|
||||
ImVec4 chanOscColor;
|
||||
float chanOscWindowSize, chanOscTextX, chanOscTextY, chanOscAmplify;
|
||||
bool chanOscWaveCorr, chanOscOptions, updateChanOscGradTex, chanOscUseGrad, chanOscNormalize;
|
||||
String chanOscTextFormat;
|
||||
ImVec4 chanOscColor, chanOscTextColor;
|
||||
Gradient2D chanOscGrad;
|
||||
SDL_Texture* chanOscGradTex;
|
||||
void* chanOscGradTex;
|
||||
float chanOscLP0[DIV_MAX_CHANS];
|
||||
float chanOscLP1[DIV_MAX_CHANS];
|
||||
float chanOscVol[DIV_MAX_CHANS];
|
||||
|
|
@ -1897,6 +2009,7 @@ class FurnaceGUI {
|
|||
bool pianoOptions, pianoSharePosition, pianoOptionsSet;
|
||||
float pianoKeyHit[180];
|
||||
bool pianoKeyPressed[180];
|
||||
bool pianoReadonly;
|
||||
int pianoOffset, pianoOffsetEdit;
|
||||
int pianoView, pianoInputPadMode;
|
||||
|
||||
|
|
@ -1931,7 +2044,7 @@ class FurnaceGUI {
|
|||
double monitorPos;
|
||||
int mustClear;
|
||||
float initialScreenWipe;
|
||||
bool introSkipDo;
|
||||
bool introSkipDo, introStopped;
|
||||
ImVec2 introMin, introMax;
|
||||
|
||||
// tutorial
|
||||
|
|
@ -1973,6 +2086,11 @@ class FurnaceGUI {
|
|||
void pushAccentColors(const ImVec4& one, const ImVec4& two, const ImVec4& border, const ImVec4& borderShadow);
|
||||
void popAccentColors();
|
||||
|
||||
void pushDestColor();
|
||||
void popDestColor();
|
||||
void pushWarningColor(bool warnCond, bool errorCond=false);
|
||||
void popWarningColor();
|
||||
|
||||
float calcBPM(const DivGroovePattern& speeds, float hz, int vN, int vD);
|
||||
|
||||
void patternRow(int i, bool isPlaying, float lineHeight, int chans, int ord, const DivPattern** patCache, bool inhibitSel);
|
||||
|
|
@ -1986,6 +2104,10 @@ class FurnaceGUI {
|
|||
void actualWaveList();
|
||||
void actualSampleList();
|
||||
|
||||
void insListItem(int index, int dir, int asset);
|
||||
void waveListItem(int index, float* wavePreview, int dir, int asset);
|
||||
void sampleListItem(int index, int dir, int asset);
|
||||
|
||||
void toggleMobileUI(bool enable, bool force=false);
|
||||
|
||||
void pushToggleColors(bool status);
|
||||
|
|
@ -1994,7 +2116,7 @@ class FurnaceGUI {
|
|||
void highlightWindow(const char* winName);
|
||||
|
||||
FurnaceGUIImage* getImage(FurnaceGUIImages image);
|
||||
SDL_Texture* getTexture(FurnaceGUIImages image, SDL_BlendMode blendMode=SDL_BLENDMODE_BLEND);
|
||||
void* getTexture(FurnaceGUIImages image, FurnaceGUIBlendMode blendMode=GUI_BLEND_MODE_BLEND);
|
||||
void drawImage(ImDrawList* dl, FurnaceGUIImages image, const ImVec2& pos, const ImVec2& scale, double rotate, const ImVec2& uvMin, const ImVec2& uvMax, const ImVec4& imgColor);
|
||||
|
||||
void drawMobileControls();
|
||||
|
|
@ -2143,9 +2265,13 @@ class FurnaceGUI {
|
|||
String encodeKeyMap(std::map<int,int>& map);
|
||||
void decodeKeyMap(std::map<int,int>& map, String source);
|
||||
|
||||
bool initRender();
|
||||
bool quitRender();
|
||||
|
||||
const char* getSystemName(DivSystem which);
|
||||
|
||||
public:
|
||||
void editStr(String* which);
|
||||
void showWarning(String what, FurnaceGUIWarnings type);
|
||||
void showError(String what);
|
||||
String getLastError();
|
||||
|
|
|
|||
|
|
@ -506,7 +506,7 @@ const FurnaceGUIActionDef guiActions[GUI_ACTION_MAX]={
|
|||
D("WINDOW_ABOUT", "About", 0),
|
||||
D("WINDOW_SETTINGS", "Settings", 0),
|
||||
D("WINDOW_MIXER", "Mixer", 0),
|
||||
D("WINDOW_DEBUG", "Debug Menu", 0),
|
||||
D("WINDOW_DEBUG", "Debug Menu", FURKMOD_CMD|FURKMOD_SHIFT|SDLK_d),
|
||||
D("WINDOW_OSCILLOSCOPE", "Oscilloscope (master)", 0),
|
||||
D("WINDOW_VOL_METER", "Volume Meter", 0),
|
||||
D("WINDOW_STATS", "Statistics", 0),
|
||||
|
|
@ -610,6 +610,7 @@ const FurnaceGUIActionDef guiActions[GUI_ACTION_MAX]={
|
|||
D("INS_LIST_EDIT", "Edit", FURKMOD_SHIFT|SDLK_RETURN),
|
||||
D("INS_LIST_UP", "Cursor up", SDLK_UP),
|
||||
D("INS_LIST_DOWN", "Cursor down", SDLK_DOWN),
|
||||
D("INS_LIST_DIR_VIEW", "Toggle folders/standard view", FURKMOD_CMD|SDLK_v),
|
||||
D("INS_LIST_MAX", "", NOT_AN_ACTION),
|
||||
|
||||
D("WAVE_LIST_MIN", "---Wavetable list", NOT_AN_ACTION),
|
||||
|
|
@ -626,6 +627,7 @@ const FurnaceGUIActionDef guiActions[GUI_ACTION_MAX]={
|
|||
D("WAVE_LIST_EDIT", "Edit", FURKMOD_SHIFT|SDLK_RETURN),
|
||||
D("WAVE_LIST_UP", "Cursor up", SDLK_UP),
|
||||
D("WAVE_LIST_DOWN", "Cursor down", SDLK_DOWN),
|
||||
D("WAVE_LIST_DIR_VIEW", "Toggle folders/standard view", FURKMOD_CMD|SDLK_v),
|
||||
D("WAVE_LIST_MAX", "", NOT_AN_ACTION),
|
||||
|
||||
D("SAMPLE_LIST_MIN", "---Sample list", NOT_AN_ACTION),
|
||||
|
|
@ -636,6 +638,7 @@ const FurnaceGUIActionDef guiActions[GUI_ACTION_MAX]={
|
|||
D("SAMPLE_LIST_OPEN_RAW", "Import raw data", 0),
|
||||
D("SAMPLE_LIST_OPEN_REPLACE_RAW", "Import raw data (replace current)", 0),
|
||||
D("SAMPLE_LIST_SAVE", "Save", 0),
|
||||
D("SAMPLE_LIST_SAVE_RAW", "Save (raw)", 0),
|
||||
D("SAMPLE_LIST_MOVE_UP", "Move up", FURKMOD_SHIFT|SDLK_UP),
|
||||
D("SAMPLE_LIST_MOVE_DOWN", "Move down", FURKMOD_SHIFT|SDLK_DOWN),
|
||||
D("SAMPLE_LIST_DELETE", "Delete", 0),
|
||||
|
|
@ -644,6 +647,7 @@ const FurnaceGUIActionDef guiActions[GUI_ACTION_MAX]={
|
|||
D("SAMPLE_LIST_DOWN", "Cursor down", SDLK_DOWN),
|
||||
D("SAMPLE_LIST_PREVIEW", "Preview", 0),
|
||||
D("SAMPLE_LIST_STOP_PREVIEW", "Stop preview", 0),
|
||||
D("SAMPLE_LIST_DIR_VIEW", "Toggle folders/standard view", FURKMOD_CMD|SDLK_v),
|
||||
D("SAMPLE_LIST_MAX", "", NOT_AN_ACTION),
|
||||
|
||||
D("SAMPLE_MIN", "---Sample editor", NOT_AN_ACTION),
|
||||
|
|
@ -706,17 +710,41 @@ const FurnaceGUIActionDef guiActions[GUI_ACTION_MAX]={
|
|||
const FurnaceGUIColorDef guiColors[GUI_COLOR_MAX]={
|
||||
D(GUI_COLOR_BACKGROUND,"Background",ImVec4(0.1f,0.1f,0.1f,1.0f)),
|
||||
D(GUI_COLOR_FRAME_BACKGROUND,"",ImVec4(0.0f,0.0f,0.0f,0.85f)),
|
||||
D(GUI_COLOR_FRAME_BACKGROUND_CHILD,"",ImVec4(0.0f,0.0f,0.0f,0.0f)),
|
||||
D(GUI_COLOR_FRAME_BACKGROUND_POPUP,"",ImVec4(0.08f,0.08f,0.08f,0.94f)),
|
||||
D(GUI_COLOR_MODAL_BACKDROP,"",ImVec4(0.0f,0.0f,0.0f,0.55f)),
|
||||
D(GUI_COLOR_HEADER,"",ImVec4(0.2f,0.2f,0.2f,1.0f)),
|
||||
D(GUI_COLOR_TEXT,"",ImVec4(1.0f,1.0f,1.0f,1.0f)),
|
||||
D(GUI_COLOR_ACCENT_PRIMARY,"",ImVec4(0.06f,0.53f,0.98f,1.0f)),
|
||||
D(GUI_COLOR_ACCENT_SECONDARY,"",ImVec4(0.26f,0.59f,0.98f,1.0f)),
|
||||
D(GUI_COLOR_TITLE_INACTIVE,"",ImVec4(0.04f,0.04f,0.04f,1.0f)),
|
||||
D(GUI_COLOR_TITLE_COLLAPSED,"",ImVec4(0.0f,0.0f,0.0f,0.51f)),
|
||||
D(GUI_COLOR_MENU_BAR,"",ImVec4(0.14f,0.14f,0.14f,1.0f)),
|
||||
D(GUI_COLOR_BORDER,"",ImVec4(0.43f,0.43f,0.5f,0.5f)),
|
||||
D(GUI_COLOR_BORDER_SHADOW,"",ImVec4(0.0f,0.0f,0.0f,0.0f)),
|
||||
D(GUI_COLOR_SCROLL_BACKGROUND,"",ImVec4(0.02f,0.02f,0.02f,0.33f)),
|
||||
D(GUI_COLOR_SCROLL,"",ImVec4(0.31f,0.31f,0.31f,1.0f)),
|
||||
D(GUI_COLOR_SCROLL_HOVER,"",ImVec4(0.41f,0.41f,0.41f,1.0f)),
|
||||
D(GUI_COLOR_SCROLL_ACTIVE,"",ImVec4(0.51f,0.51f,0.51f,1.0f)),
|
||||
D(GUI_COLOR_SEPARATOR,"",ImVec4(0.43f,0.43f,0.5f,0.5f)),
|
||||
D(GUI_COLOR_SEPARATOR_HOVER,"",ImVec4(0.1f,0.4f,0.75f,0.78f)),
|
||||
D(GUI_COLOR_SEPARATOR_ACTIVE,"",ImVec4(0.1f,0.4f,0.75f,1.0f)),
|
||||
D(GUI_COLOR_DOCKING_PREVIEW,"",ImVec4(0.26f,0.59f,0.98f,0.7f)),
|
||||
D(GUI_COLOR_DOCKING_EMPTY,"",ImVec4(0.2f,0.2f,0.2f,1.0f)),
|
||||
D(GUI_COLOR_TABLE_HEADER,"",ImVec4(0.19f,0.19f,0.2f,1.0f)),
|
||||
D(GUI_COLOR_TABLE_BORDER_HARD,"",ImVec4(0.31f,0.31f,0.35f,1.0f)),
|
||||
D(GUI_COLOR_TABLE_BORDER_SOFT,"",ImVec4(0.23f,0.23f,0.25f,1.0f)),
|
||||
D(GUI_COLOR_DRAG_DROP_TARGET,"",ImVec4(1.0f,1.0f,0.0f,0.9f)),
|
||||
D(GUI_COLOR_NAV_HIGHLIGHT,"",ImVec4(0.26f,0.59f,0.98f,1.0f)),
|
||||
D(GUI_COLOR_NAV_WIN_HIGHLIGHT,"",ImVec4(1.0f,1.0f,1.0f,0.7f)),
|
||||
D(GUI_COLOR_NAV_WIN_BACKDROP,"",ImVec4(0.8f,0.8f,0.8f,0.2f)),
|
||||
D(GUI_COLOR_TOGGLE_OFF,"",ImVec4(0.2f,0.2f,0.2f,1.0f)),
|
||||
D(GUI_COLOR_TOGGLE_ON,"",ImVec4(0.2f,0.6f,0.2f,1.0f)),
|
||||
D(GUI_COLOR_EDITING,"",ImVec4(0.2f,0.1f,0.1f,1.0f)),
|
||||
D(GUI_COLOR_SONG_LOOP,"",ImVec4(0.3f,0.5f,0.8f,0.4f)),
|
||||
D(GUI_COLOR_DESTRUCTIVE,"",ImVec4(1.0f,0.2f,0.2f,1.0f)),
|
||||
D(GUI_COLOR_WARNING,"",ImVec4(0.98f,0.98f,0.06f,1.0f)),
|
||||
D(GUI_COLOR_ERROR,"",ImVec4(0.98f,0.06f,0.11f,1.0f)),
|
||||
|
||||
D(GUI_COLOR_FILE_DIR,"",ImVec4(0.0f,1.0f,1.0f,1.0f)),
|
||||
D(GUI_COLOR_FILE_SONG_NATIVE,"",ImVec4(0.5f,1.0f,0.5f,1.0f)),
|
||||
|
|
|
|||
|
|
@ -15,9 +15,6 @@
|
|||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* this license only applies to the code. for the license of each font used,
|
||||
* see `papers/`.
|
||||
*/
|
||||
|
||||
#include "gui.h"
|
||||
|
|
@ -49,7 +46,7 @@ const unsigned int imageLen[GUI_IMAGE_MAX]={
|
|||
image_pat_size
|
||||
};
|
||||
|
||||
SDL_Texture* FurnaceGUI::getTexture(FurnaceGUIImages image, SDL_BlendMode blendMode) {
|
||||
void* FurnaceGUI::getTexture(FurnaceGUIImages image, FurnaceGUIBlendMode blendMode) {
|
||||
FurnaceGUIImage* img=getImage(image);
|
||||
|
||||
if (img==NULL) return NULL;
|
||||
|
|
@ -57,14 +54,14 @@ SDL_Texture* FurnaceGUI::getTexture(FurnaceGUIImages image, SDL_BlendMode blendM
|
|||
if (img->width<=0 || img->height<=0) return NULL;
|
||||
|
||||
if (img->tex==NULL) {
|
||||
img->tex=SDL_CreateTexture(sdlRend,SDL_PIXELFORMAT_ABGR8888,SDL_TEXTUREACCESS_STATIC,img->width,img->height);
|
||||
img->tex=rend->createTexture(false,img->width,img->height);
|
||||
if (img->tex==NULL) {
|
||||
logE("error while creating image %d texture! %s",(int)image,SDL_GetError());
|
||||
return NULL;
|
||||
}
|
||||
SDL_SetTextureBlendMode(img->tex,blendMode);
|
||||
rend->setTextureBlendMode(img->tex,blendMode);
|
||||
|
||||
if (SDL_UpdateTexture(img->tex,NULL,img->data,img->width*4)!=0) {
|
||||
if (!rend->updateTexture(img->tex,img->data,img->width*4)) {
|
||||
logE("error while updating texture of image %d! %s",(int)image,SDL_GetError());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2262,9 +2262,11 @@ void FurnaceGUI::drawInsEdit() {
|
|||
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
|
||||
ImGui::PushID(2+curIns);
|
||||
if (ImGui::InputText("##Name",&ins->name)) {
|
||||
MARK_MODIFIED;
|
||||
}
|
||||
ImGui::PopID();
|
||||
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
|
|
@ -5153,7 +5155,9 @@ void FurnaceGUI::drawInsEdit() {
|
|||
wavePreview2[i]=wave2->data[i];
|
||||
}
|
||||
}
|
||||
if (ins->ws.enabled) wavePreview.tick(true);
|
||||
if (ins->ws.enabled && (!wavePreviewPaused || wavePreviewInit)) {
|
||||
wavePreview.tick(true);
|
||||
}
|
||||
for (int i=0; i<wavePreviewLen; i++) {
|
||||
if (wave2->data[i]>wavePreviewHeight) {
|
||||
wavePreview3[i]=wavePreviewHeight;
|
||||
|
|
@ -5199,9 +5203,43 @@ void FurnaceGUI::drawInsEdit() {
|
|||
}
|
||||
}
|
||||
ImGui::TableNextColumn();
|
||||
if (ImGui::Button(wavePreviewPaused?(ICON_FA_PLAY "##WSPause"):(ICON_FA_PAUSE "##WSPause"))) {
|
||||
wavePreviewPaused=!wavePreviewPaused;
|
||||
}
|
||||
if (ImGui::IsItemHovered()) {
|
||||
if (wavePreviewPaused) {
|
||||
ImGui::SetTooltip("Resume preview");
|
||||
} else {
|
||||
ImGui::SetTooltip("Pause preview");
|
||||
}
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button(ICON_FA_REPEAT "##WSRestart")) {
|
||||
wavePreviewInit=true;
|
||||
}
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("Restart preview");
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button(ICON_FA_UPLOAD "##WSCopy")) {
|
||||
curWave=e->addWave();
|
||||
if (curWave==-1) {
|
||||
showError("too many wavetables!");
|
||||
} else {
|
||||
wantScrollList=true;
|
||||
MARK_MODIFIED;
|
||||
RESET_WAVE_MACRO_ZOOM;
|
||||
nextWindow=GUI_WINDOW_WAVE_EDIT;
|
||||
|
||||
DivWavetable* copyWave=e->song.wave[curWave];
|
||||
copyWave->len=wavePreviewLen;
|
||||
copyWave->max=wavePreviewHeight;
|
||||
memcpy(copyWave->data,wavePreview.output,256*sizeof(int));
|
||||
}
|
||||
}
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("Copy to new wavetable");
|
||||
}
|
||||
ImGui::SameLine();
|
||||
ImGui::Text("(%d×%d)",wavePreviewLen,wavePreviewHeight+1);
|
||||
ImGui::EndTable();
|
||||
|
|
|
|||
|
|
@ -19,12 +19,13 @@
|
|||
|
||||
#define _USE_MATH_DEFINES
|
||||
#include "gui.h"
|
||||
#include "../ta-log.h"
|
||||
#include "imgui_internal.h"
|
||||
#include <fmt/printf.h>
|
||||
|
||||
void FurnaceGUI::drawImage(ImDrawList* dl, FurnaceGUIImages image, const ImVec2& pos, const ImVec2& scale, double rotate, const ImVec2& uvMin, const ImVec2& uvMax, const ImVec4& imgColor) {
|
||||
FurnaceGUIImage* imgI=getImage(image);
|
||||
SDL_Texture* img=getTexture(image);
|
||||
void* img=getTexture(image);
|
||||
|
||||
float squareSize=MAX(introMax.x-introMin.x,introMax.y-introMin.y);
|
||||
float uDiff=uvMax.x-uvMin.x;
|
||||
|
|
@ -69,10 +70,12 @@ void FurnaceGUI::drawImage(ImDrawList* dl, FurnaceGUIImages image, const ImVec2&
|
|||
|
||||
ImU32 colorConverted=ImGui::GetColorU32(imgColor);
|
||||
|
||||
dl->AddImageQuad(img,quad0,quad1,quad2,quad3,uv0,uv1,uv2,uv3,colorConverted);
|
||||
dl->AddImageQuad(rend->getTextureID(img),quad0,quad1,quad2,quad3,uv0,uv1,uv2,uv3,colorConverted);
|
||||
}
|
||||
|
||||
void FurnaceGUI::endIntroTune() {
|
||||
if (introStopped) return;
|
||||
logV("ending intro");
|
||||
stop();
|
||||
if (curFileName.empty()) {
|
||||
e->createNewFromDefaults();
|
||||
|
|
@ -95,6 +98,8 @@ void FurnaceGUI::endIntroTune() {
|
|||
selEnd=SelectionPoint();
|
||||
cursor=SelectionPoint();
|
||||
updateWindowTitle();
|
||||
updateScroll(0);
|
||||
introStopped=true;
|
||||
}
|
||||
|
||||
void FurnaceGUI::drawIntro(double introTime, bool monitor) {
|
||||
|
|
@ -162,7 +167,7 @@ void FurnaceGUI::drawIntro(double introTime, bool monitor) {
|
|||
getTexture(GUI_IMAGE_TALOGO);
|
||||
getTexture(GUI_IMAGE_TACHIP);
|
||||
getTexture(GUI_IMAGE_LOGO);
|
||||
getTexture(GUI_IMAGE_INTROBG,SDL_BLENDMODE_ADD);
|
||||
getTexture(GUI_IMAGE_INTROBG,GUI_BLEND_MODE_ADD);
|
||||
|
||||
if (monitor) {
|
||||
ImVec2 textPos=ImLerp(top,bottom,ImVec2(0.5,0.5));
|
||||
|
|
@ -290,7 +295,7 @@ void FurnaceGUI::drawIntro(double introTime, bool monitor) {
|
|||
if (introSkipDo) {
|
||||
introSkip+=ImGui::GetIO().DeltaTime;
|
||||
if (introSkip>=0.5) {
|
||||
if (e->isPlaying()) endIntroTune();
|
||||
if (!shortIntro) endIntroTune();
|
||||
introPos=0.1;
|
||||
if (introSkip>=0.75) introPos=12.0;
|
||||
}
|
||||
|
|
@ -317,7 +322,7 @@ void FurnaceGUI::drawIntro(double introTime, bool monitor) {
|
|||
e->setRepeatPattern(false);
|
||||
play();
|
||||
}
|
||||
if (e->isPlaying() && introPos>=10.0 && !shortIntro) endIntroTune();
|
||||
if (introPos>=10.0 && !shortIntro) endIntroTune();
|
||||
introPos+=ImGui::GetIO().DeltaTime;
|
||||
if (introPos>=(shortIntro?1.0:11.0)) {
|
||||
introPos=12.0;
|
||||
|
|
@ -325,5 +330,7 @@ void FurnaceGUI::drawIntro(double introTime, bool monitor) {
|
|||
commitTutorial();
|
||||
}
|
||||
}
|
||||
} else if (!shortIntro) {
|
||||
endIntroTune();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -49,11 +49,11 @@ void _popPartBlend(const ImDrawList* drawList, const ImDrawCmd* cmd) {
|
|||
}
|
||||
|
||||
void FurnaceGUI::pushPartBlend() {
|
||||
SDL_SetRenderDrawBlendMode(sdlRend,SDL_BLENDMODE_ADD);
|
||||
rend->setBlendMode(GUI_BLEND_MODE_ADD);
|
||||
}
|
||||
|
||||
void FurnaceGUI::popPartBlend() {
|
||||
SDL_SetRenderDrawBlendMode(sdlRend,SDL_BLENDMODE_BLEND);
|
||||
rend->setBlendMode(GUI_BLEND_MODE_BLEND);
|
||||
}
|
||||
|
||||
// draw a pattern row
|
||||
|
|
@ -287,7 +287,7 @@ inline void FurnaceGUI::patternRow(int i, bool isPlaying, float lineHeight, int
|
|||
if (pat->data[i][index]>0xff) {
|
||||
snprintf(id,63,"??##PE%d_%d_%d",k,i,j);
|
||||
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_PATTERN_EFFECT_INVALID]);
|
||||
} else if (pat->data[i][index]>0x10 || settings.oneDigitEffects==0) {
|
||||
} else if (pat->data[i][index]>=0x10 || settings.oneDigitEffects==0) {
|
||||
const unsigned char data=pat->data[i][index];
|
||||
snprintf(id,63,"%.2X##PE%d_%d_%d",data,k,i,j);
|
||||
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[fxColors[data]]);
|
||||
|
|
@ -834,28 +834,9 @@ void FurnaceGUI::drawPattern() {
|
|||
if (extraChannelButtons==2) {
|
||||
DivPattern* pat=e->curPat[i].getPattern(e->curOrders->ord[i][ord],true);
|
||||
ImGui::PushFont(mainFont);
|
||||
if (patNameTarget==i) {
|
||||
snprintf(chanID,2048,"##PatNameI%d_%d",i,ord);
|
||||
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x-(8.0f*dpiScale));
|
||||
ImGui::SetCursorPosX(ImGui::GetCursorPosX()+4.0f*dpiScale);
|
||||
ImGui::InputText(chanID,&pat->name);
|
||||
if (wantPatName) {
|
||||
wantPatName=false;
|
||||
ImGui::SetItemDefaultFocus();
|
||||
ImGui::SetKeyboardFocusHere(-1);
|
||||
} else {
|
||||
if (!ImGui::IsItemActive()) {
|
||||
patNameTarget=-1;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
snprintf(chanID,2048," %s##PatName%d",pat->name.c_str(),i);
|
||||
if (ImGui::Selectable(chanID,true,ImGuiSelectableFlags_NoPadWithHalfSpacing,ImVec2(0.0f,lineHeight+1.0f*dpiScale))) {
|
||||
patNameTarget=i;
|
||||
wantPatName=true;
|
||||
snprintf(chanID,2048,"##PatNameI%d_%d",i,ord);
|
||||
ImGui::SetActiveID(ImGui::GetID(chanID),ImGui::GetCurrentWindow());
|
||||
}
|
||||
snprintf(chanID,2048," %s##PatName%d",pat->name.c_str(),i);
|
||||
if (ImGui::Selectable(chanID,true,ImGuiSelectableFlags_NoPadWithHalfSpacing,ImVec2(0.0f,lineHeight+1.0f*dpiScale))) {
|
||||
editStr(&pat->name);
|
||||
}
|
||||
ImGui::PopFont();
|
||||
} else if (extraChannelButtons==1) {
|
||||
|
|
|
|||
|
|
@ -123,6 +123,7 @@ void FurnaceGUI::drawPiano() {
|
|||
pianoInputPadMode=PIANO_INPUT_PAD_SPLIT_VISIBLE;
|
||||
}
|
||||
ImGui::Checkbox("Share play/edit offset/range",&pianoSharePosition);
|
||||
ImGui::Checkbox("Read-only (can't input notes)",&pianoReadonly);
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
|
||||
|
|
@ -223,7 +224,7 @@ void FurnaceGUI::drawPiano() {
|
|||
//ImGui::ItemSize(size,ImGui::GetStyle().FramePadding.y);
|
||||
if (ImGui::ItemAdd(rect,ImGui::GetID("pianoDisplay"))) {
|
||||
bool canInput=false;
|
||||
if (ImGui::ItemHoverable(rect,ImGui::GetID("pianoDisplay"))) {
|
||||
if (!pianoReadonly && ImGui::ItemHoverable(rect,ImGui::GetID("pianoDisplay"))) {
|
||||
canInput=true;
|
||||
ImGui::InhibitInertialScroll();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,11 +19,11 @@
|
|||
|
||||
// portions based on imgui_widgets.cpp
|
||||
|
||||
#include "plot_nolerp.h"
|
||||
#include "imgui.h"
|
||||
#ifndef IMGUI_DEFINE_MATH_OPERATORS
|
||||
#define IMGUI_DEFINE_MATH_OPERATORS
|
||||
#endif
|
||||
#include "plot_nolerp.h"
|
||||
#include "imgui.h"
|
||||
#include "imgui_internal.h"
|
||||
|
||||
struct FurnacePlotArrayGetterData
|
||||
|
|
|
|||
|
|
@ -822,10 +822,15 @@ void FurnaceGUI::initSystemPresets() {
|
|||
}
|
||||
);
|
||||
ENTRY(
|
||||
"ZX Spectrum (48K)", {
|
||||
"ZX Spectrum (48K, SFX-like engine)", {
|
||||
CH(DIV_SYSTEM_SFX_BEEPER, 1.0f, 0, "")
|
||||
}
|
||||
);
|
||||
ENTRY(
|
||||
"ZX Spectrum (48K, QuadTone engine)", {
|
||||
CH(DIV_SYSTEM_SFX_BEEPER_QUADTONE, 1.0f, 0, "")
|
||||
}
|
||||
);
|
||||
ENTRY(
|
||||
"ZX Spectrum (128K)", {
|
||||
CH(DIV_SYSTEM_AY8910, 1.0f, 0, "clockSel=1") //beeper was also included
|
||||
|
|
@ -2597,10 +2602,15 @@ void FurnaceGUI::initSystemPresets() {
|
|||
}
|
||||
);
|
||||
ENTRY(
|
||||
"ZX Spectrum (beeper only)", {
|
||||
"ZX Spectrum (beeper only, SFX-like engine)", {
|
||||
CH(DIV_SYSTEM_SFX_BEEPER, 1.0f, 0, "")
|
||||
}
|
||||
);
|
||||
ENTRY(
|
||||
"ZX Spectrum (beeper only, QuadTone engine)", {
|
||||
CH(DIV_SYSTEM_SFX_BEEPER_QUADTONE, 1.0f, 0, "")
|
||||
}
|
||||
);
|
||||
ENTRY(
|
||||
"Sharp SM8521", {
|
||||
CH(DIV_SYSTEM_SM8521, 1.0f, 0, "")
|
||||
|
|
|
|||
79
src/gui/render.cpp
Normal file
79
src/gui/render.cpp
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
/**
|
||||
* Furnace Tracker - multi-system chiptune tracker
|
||||
* Copyright (C) 2021-2023 tildearrow and contributors
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "gui.h"
|
||||
#include "../ta-log.h"
|
||||
#ifdef HAVE_RENDER_SDL
|
||||
#include "render/renderSDL.h"
|
||||
#endif
|
||||
#ifdef HAVE_RENDER_GL
|
||||
#include "render/renderGL.h"
|
||||
#endif
|
||||
#ifdef HAVE_RENDER_DX11
|
||||
#include "render/renderDX11.h"
|
||||
#endif
|
||||
|
||||
bool FurnaceGUI::initRender() {
|
||||
if (rend!=NULL) return false;
|
||||
|
||||
if (settings.renderBackend=="OpenGL") {
|
||||
renderBackend=GUI_BACKEND_GL;
|
||||
} else if (settings.renderBackend=="DirectX 11") {
|
||||
renderBackend=GUI_BACKEND_DX11;
|
||||
} else if (settings.renderBackend=="SDL") {
|
||||
renderBackend=GUI_BACKEND_SDL;
|
||||
} else {
|
||||
renderBackend=GUI_BACKEND_DEFAULT;
|
||||
}
|
||||
|
||||
switch (renderBackend) {
|
||||
#ifdef HAVE_RENDER_GL
|
||||
case GUI_BACKEND_GL:
|
||||
logI("render backend: OpenGL");
|
||||
rend=new FurnaceGUIRenderGL;
|
||||
break;
|
||||
#endif
|
||||
#ifdef HAVE_RENDER_DX11
|
||||
case GUI_BACKEND_DX11:
|
||||
logI("render backend: DirectX 11");
|
||||
rend=new FurnaceGUIRenderDX11;
|
||||
break;
|
||||
#endif
|
||||
#ifdef HAVE_RENDER_SDL
|
||||
case GUI_BACKEND_SDL:
|
||||
logI("render backend: SDL_Renderer");
|
||||
rend=new FurnaceGUIRenderSDL;
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
logE("invalid render backend!");
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FurnaceGUI::quitRender() {
|
||||
if (rend==NULL) return false;
|
||||
bool ret=rend->quit();
|
||||
delete rend;
|
||||
rend=NULL;
|
||||
return ret;
|
||||
}
|
||||
103
src/gui/render/abstract.cpp
Normal file
103
src/gui/render/abstract.cpp
Normal file
|
|
@ -0,0 +1,103 @@
|
|||
/**
|
||||
* Furnace Tracker - multi-system chiptune tracker
|
||||
* Copyright (C) 2021-2023 tildearrow and contributors
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "../gui.h"
|
||||
|
||||
ImTextureID FurnaceGUIRender::getTextureID(void* which) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool FurnaceGUIRender::lockTexture(void* which, void** data, int* pitch) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool FurnaceGUIRender::unlockTexture(void* which) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool FurnaceGUIRender::updateTexture(void* which, void* data, int pitch) {
|
||||
return false;
|
||||
}
|
||||
|
||||
void* FurnaceGUIRender::createTexture(bool dynamic, int width, int height) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool FurnaceGUIRender::destroyTexture(void* which) {
|
||||
return false;
|
||||
}
|
||||
|
||||
void FurnaceGUIRender::setTextureBlendMode(void* which, FurnaceGUIBlendMode mode) {
|
||||
}
|
||||
|
||||
void FurnaceGUIRender::setBlendMode(FurnaceGUIBlendMode mode) {
|
||||
}
|
||||
|
||||
void FurnaceGUIRender::resized(const SDL_Event& ev) {
|
||||
}
|
||||
|
||||
void FurnaceGUIRender::clear(ImVec4 color) {
|
||||
}
|
||||
|
||||
bool FurnaceGUIRender::newFrame() {
|
||||
return true;
|
||||
}
|
||||
|
||||
void FurnaceGUIRender::createFontsTexture() {
|
||||
}
|
||||
|
||||
void FurnaceGUIRender::destroyFontsTexture() {
|
||||
}
|
||||
|
||||
void FurnaceGUIRender::renderGUI() {
|
||||
}
|
||||
|
||||
void FurnaceGUIRender::wipe(float alpha) {
|
||||
}
|
||||
|
||||
void FurnaceGUIRender::present() {
|
||||
}
|
||||
|
||||
bool FurnaceGUIRender::getOutputSize(int& w, int& h) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int FurnaceGUIRender::getWindowFlags() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void FurnaceGUIRender::preInit() {
|
||||
}
|
||||
|
||||
bool FurnaceGUIRender::init(SDL_Window* win) {
|
||||
return false;
|
||||
}
|
||||
|
||||
void FurnaceGUIRender::initGUI(SDL_Window* win) {
|
||||
}
|
||||
|
||||
bool FurnaceGUIRender::quit() {
|
||||
return false;
|
||||
}
|
||||
|
||||
void FurnaceGUIRender::quitGUI() {
|
||||
}
|
||||
|
||||
FurnaceGUIRender::~FurnaceGUIRender() {
|
||||
}
|
||||
571
src/gui/render/renderDX11.cpp
Normal file
571
src/gui/render/renderDX11.cpp
Normal file
|
|
@ -0,0 +1,571 @@
|
|||
/**
|
||||
* Furnace Tracker - multi-system chiptune tracker
|
||||
* Copyright (C) 2021-2023 tildearrow and contributors
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#define INCLUDE_D3D11
|
||||
#include "renderDX11.h"
|
||||
#include <SDL_syswm.h>
|
||||
#include "backends/imgui_impl_dx11.h"
|
||||
#include "../../ta-log.h"
|
||||
|
||||
typedef HRESULT (__stdcall *D3DCompile_t)(LPCVOID,SIZE_T,LPCSTR,D3D_SHADER_MACRO*,ID3DInclude*,LPCSTR,LPCSTR,UINT,UINT,ID3DBlob**,ID3DBlob*);
|
||||
|
||||
const char* shD3D11_wipe_srcV=
|
||||
"cbuffer WipeUniform: register(b0) {\n"
|
||||
" float alpha;\n"
|
||||
" float padding1;\n"
|
||||
" float padding2;\n"
|
||||
" float padding3;\n"
|
||||
" float4 padding4;\n"
|
||||
"};\n"
|
||||
"\n"
|
||||
"struct vsInput {\n"
|
||||
" float4 pos: POSITION;\n"
|
||||
"};\n"
|
||||
"\n"
|
||||
"struct fsInput {\n"
|
||||
" float4 pos: SV_POSITION;\n"
|
||||
" float4 color: COLOR0;\n"
|
||||
"};\n"
|
||||
"\n"
|
||||
"fsInput main(vsInput input) {\n"
|
||||
" fsInput output;\n"
|
||||
" output.pos=input.pos;\n"
|
||||
" output.color=float4(0.0f,0.0f,0.0f,alpha);\n"
|
||||
" return output;\n"
|
||||
"}";
|
||||
|
||||
const char* shD3D11_wipe_srcF=
|
||||
"struct fsInput {\n"
|
||||
" float4 pos: SV_POSITION;\n"
|
||||
" float4 color: COLOR0;\n"
|
||||
"};\n"
|
||||
"\n"
|
||||
"float4 main(fsInput input): SV_Target {\n"
|
||||
" return input.color;\n"
|
||||
"}";
|
||||
|
||||
const D3D11_INPUT_ELEMENT_DESC shD3D11_wipe_inputLayout={
|
||||
"POSITION", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0
|
||||
};
|
||||
|
||||
const D3D_FEATURE_LEVEL possibleFeatureLevels[2]={
|
||||
D3D_FEATURE_LEVEL_11_0,
|
||||
D3D_FEATURE_LEVEL_10_0
|
||||
};
|
||||
|
||||
struct FurnaceDXTexture {
|
||||
ID3D11Texture2D* tex;
|
||||
ID3D11ShaderResourceView* view;
|
||||
int width, height;
|
||||
unsigned char* lockedData;
|
||||
bool dynamic;
|
||||
FurnaceDXTexture():
|
||||
tex(NULL),
|
||||
view(NULL),
|
||||
width(0),
|
||||
height(0),
|
||||
lockedData(NULL),
|
||||
dynamic(false) {}
|
||||
};
|
||||
|
||||
bool FurnaceGUIRenderDX11::destroyRenderTarget() {
|
||||
if (renderTarget!=NULL) {
|
||||
renderTarget->Release();
|
||||
renderTarget=NULL;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool FurnaceGUIRenderDX11::createRenderTarget() {
|
||||
ID3D11Texture2D* screen=NULL;
|
||||
HRESULT result;
|
||||
|
||||
destroyRenderTarget();
|
||||
|
||||
if (swapchain==NULL || device==NULL) {
|
||||
logW("createRenderTarget: swapchain or device are NULL!");
|
||||
return false;
|
||||
}
|
||||
|
||||
DXGI_SWAP_CHAIN_DESC chainDesc;
|
||||
memset(&chainDesc,0,sizeof(chainDesc));
|
||||
if (swapchain->GetDesc(&chainDesc)!=S_OK) {
|
||||
logW("createRenderTarget: could not get swapchain desc!");
|
||||
} else {
|
||||
outW=chainDesc.BufferDesc.Width;
|
||||
outH=chainDesc.BufferDesc.Height;
|
||||
logI("DX11: buffer desc sizes: %d, %d",chainDesc.BufferDesc.Width,chainDesc.BufferDesc.Height);
|
||||
}
|
||||
|
||||
result=swapchain->GetBuffer(0,IID_PPV_ARGS(&screen));
|
||||
if (result!=S_OK) {
|
||||
logW("createRenderTarget: could not get buffer! %.8x",result);
|
||||
return false;
|
||||
}
|
||||
if (screen==NULL) {
|
||||
logW("createRenderTarget: screen is null!");
|
||||
return false;
|
||||
}
|
||||
|
||||
result=device->CreateRenderTargetView(screen,NULL,&renderTarget);
|
||||
if (result!=S_OK) {
|
||||
logW("createRenderTarget: could not create render target view! %.8x",result);
|
||||
screen->Release();
|
||||
return false;
|
||||
}
|
||||
if (renderTarget==NULL) {
|
||||
logW("createRenderTarget: what the hell the render target is null?");
|
||||
screen->Release();
|
||||
return false;
|
||||
}
|
||||
|
||||
screen->Release();
|
||||
return true;
|
||||
}
|
||||
|
||||
ImTextureID FurnaceGUIRenderDX11::getTextureID(void* which) {
|
||||
FurnaceDXTexture* t=(FurnaceDXTexture*)which;
|
||||
return (ImTextureID)t->view;
|
||||
}
|
||||
|
||||
bool FurnaceGUIRenderDX11::lockTexture(void* which, void** data, int* pitch) {
|
||||
FurnaceDXTexture* t=(FurnaceDXTexture*)which;
|
||||
if (t->lockedData!=NULL) return false;
|
||||
|
||||
D3D11_MAPPED_SUBRESOURCE mappedRes;
|
||||
memset(&mappedRes,0,sizeof(mappedRes));
|
||||
|
||||
HRESULT result=context->Map(t->tex,D3D11CalcSubresource(0,0,1),D3D11_MAP_WRITE_DISCARD,0,&mappedRes);
|
||||
if (result!=S_OK) {
|
||||
logW("could not map texture! %.8x",result);
|
||||
return false;
|
||||
}
|
||||
t->lockedData=(unsigned char*)mappedRes.pData;
|
||||
*data=mappedRes.pData;
|
||||
*pitch=mappedRes.RowPitch;
|
||||
|
||||
logV("texture locked... pitch: %d",mappedRes.RowPitch);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FurnaceGUIRenderDX11::unlockTexture(void* which) {
|
||||
FurnaceDXTexture* t=(FurnaceDXTexture*)which;
|
||||
if (t->lockedData==NULL) return false;
|
||||
context->Unmap(t->tex,D3D11CalcSubresource(0,0,1));
|
||||
t->lockedData=NULL;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FurnaceGUIRenderDX11::updateTexture(void* which, void* data, int pitch) {
|
||||
FurnaceDXTexture* t=(FurnaceDXTexture*)which;
|
||||
if (t->dynamic) {
|
||||
unsigned char* d=NULL;
|
||||
int p=0;
|
||||
if (!lockTexture(t,(void**)&d,&p)) return false;
|
||||
if (p==pitch) {
|
||||
memcpy(d,data,p*t->height);
|
||||
} else {
|
||||
unsigned char* ucData=(unsigned char*)data;
|
||||
int srcPos=0;
|
||||
int destPos=0;
|
||||
for (int i=0; i<t->height; i++) {
|
||||
memcpy(&d[destPos],&ucData[srcPos],pitch);
|
||||
srcPos+=pitch;
|
||||
destPos+=p;
|
||||
}
|
||||
}
|
||||
unlockTexture(t);
|
||||
} else {
|
||||
context->UpdateSubresource(t->tex,D3D11CalcSubresource(0,0,1),NULL,data,pitch,pitch*t->height);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void* FurnaceGUIRenderDX11::createTexture(bool dynamic, int width, int height) {
|
||||
D3D11_TEXTURE2D_DESC texDesc;
|
||||
D3D11_SHADER_RESOURCE_VIEW_DESC viewDesc;
|
||||
ID3D11Texture2D* tex=NULL;
|
||||
ID3D11ShaderResourceView* view=NULL;
|
||||
HRESULT result;
|
||||
|
||||
memset(&texDesc,0,sizeof(texDesc));
|
||||
memset(&viewDesc,0,sizeof(viewDesc));
|
||||
|
||||
texDesc.Width=width;
|
||||
texDesc.Height=height;
|
||||
texDesc.MipLevels=1;
|
||||
texDesc.ArraySize=1;
|
||||
texDesc.Format=DXGI_FORMAT_R8G8B8A8_UNORM; // ???
|
||||
texDesc.SampleDesc.Count=1;
|
||||
texDesc.SampleDesc.Quality=0;
|
||||
texDesc.Usage=dynamic?D3D11_USAGE_DYNAMIC:D3D11_USAGE_DEFAULT;
|
||||
texDesc.BindFlags=D3D11_BIND_SHADER_RESOURCE;
|
||||
texDesc.CPUAccessFlags=dynamic?D3D11_CPU_ACCESS_WRITE:0;
|
||||
texDesc.MiscFlags=0;
|
||||
|
||||
result=device->CreateTexture2D(&texDesc,NULL,&tex);
|
||||
if (result!=S_OK) {
|
||||
logW("could not create texture! %.8x",result);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
viewDesc.Format=texDesc.Format=texDesc.Format;
|
||||
viewDesc.ViewDimension=D3D11_SRV_DIMENSION_TEXTURE2D;
|
||||
viewDesc.Texture2D.MostDetailedMip=0;
|
||||
viewDesc.Texture2D.MipLevels=texDesc.MipLevels;
|
||||
|
||||
result=device->CreateShaderResourceView(tex,&viewDesc,&view);
|
||||
if (result!=S_OK) {
|
||||
logW("could not create texture view! %.8x",result);
|
||||
tex->Release();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
FurnaceDXTexture* ret=new FurnaceDXTexture;
|
||||
ret->width=width;
|
||||
ret->height=height;
|
||||
ret->tex=tex;
|
||||
ret->view=view;
|
||||
ret->dynamic=dynamic;
|
||||
textures.push_back(ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool FurnaceGUIRenderDX11::destroyTexture(void* which) {
|
||||
FurnaceDXTexture* t=(FurnaceDXTexture*)which;
|
||||
t->view->Release();
|
||||
t->tex->Release();
|
||||
delete t;
|
||||
|
||||
for (size_t i=0; i<textures.size(); i++) {
|
||||
if (textures[i]==t) {
|
||||
textures.erase(textures.begin()+i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void FurnaceGUIRenderDX11::setTextureBlendMode(void* which, FurnaceGUIBlendMode mode) {
|
||||
}
|
||||
|
||||
void FurnaceGUIRenderDX11::setBlendMode(FurnaceGUIBlendMode mode) {
|
||||
}
|
||||
|
||||
void FurnaceGUIRenderDX11::resized(const SDL_Event& ev) {
|
||||
destroyRenderTarget();
|
||||
logI("DX11: resizing buffers");
|
||||
HRESULT result=swapchain->ResizeBuffers(0,(unsigned int)ev.window.data1,(unsigned int)ev.window.data2,DXGI_FORMAT_UNKNOWN,DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH);
|
||||
if (result!=S_OK) {
|
||||
logW("error while resizing swapchain buffers! %.8x",result);
|
||||
}
|
||||
createRenderTarget();
|
||||
}
|
||||
|
||||
void FurnaceGUIRenderDX11::clear(ImVec4 color) {
|
||||
float floatColor[4]={
|
||||
color.x*color.w,
|
||||
color.y*color.w,
|
||||
color.z*color.w,
|
||||
color.w,
|
||||
};
|
||||
|
||||
context->OMSetRenderTargets(1,&renderTarget,NULL);
|
||||
context->ClearRenderTargetView(renderTarget,floatColor);
|
||||
}
|
||||
|
||||
bool FurnaceGUIRenderDX11::newFrame() {
|
||||
ImGui_ImplDX11_NewFrame();
|
||||
return true;
|
||||
}
|
||||
|
||||
void FurnaceGUIRenderDX11::createFontsTexture() {
|
||||
ImGui_ImplDX11_CreateDeviceObjects();
|
||||
}
|
||||
|
||||
void FurnaceGUIRenderDX11::destroyFontsTexture() {
|
||||
ImGui_ImplDX11_InvalidateDeviceObjects();
|
||||
}
|
||||
|
||||
void FurnaceGUIRenderDX11::renderGUI() {
|
||||
ImGui_ImplDX11_RenderDrawData(ImGui::GetDrawData());
|
||||
}
|
||||
|
||||
const float blendFactor[4]={
|
||||
1.0f, 1.0f, 1.0f, 1.0f
|
||||
};
|
||||
|
||||
void FurnaceGUIRenderDX11::wipe(float alpha) {
|
||||
D3D11_VIEWPORT viewPort;
|
||||
unsigned int strides=4*sizeof(float);
|
||||
unsigned int offsets=0;
|
||||
|
||||
memset(&viewPort,0,sizeof(viewPort));
|
||||
viewPort.TopLeftX=0.0f;
|
||||
viewPort.TopLeftY=0.0f;
|
||||
viewPort.Width=outW;
|
||||
viewPort.Height=outH;
|
||||
viewPort.MinDepth=0.0f;
|
||||
viewPort.MaxDepth=1.0f;
|
||||
|
||||
D3D11_MAPPED_SUBRESOURCE mappedUniform;
|
||||
if (context->Map(sh_wipe_uniform,0,D3D11_MAP_WRITE_DISCARD,0,&mappedUniform)!=S_OK) {
|
||||
logW("could not map constant");
|
||||
}
|
||||
WipeUniform* sh_wipe_uniformState=(WipeUniform*)mappedUniform.pData;
|
||||
sh_wipe_uniformState->alpha=alpha;
|
||||
context->Unmap(sh_wipe_uniform,0);
|
||||
|
||||
context->RSSetViewports(1,&viewPort);
|
||||
context->RSSetState(rsState);
|
||||
|
||||
context->OMSetBlendState(omBlendState,blendFactor,0xffffffff);
|
||||
context->IASetInputLayout(sh_wipe_inputLayout);
|
||||
context->IASetVertexBuffers(0,1,&quadVertex,&strides,&offsets);
|
||||
context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP);
|
||||
context->VSSetShader(sh_wipe_vertex,NULL,0);
|
||||
context->VSSetConstantBuffers(0,1,&sh_wipe_uniform);
|
||||
context->PSSetShader(sh_wipe_fragment,NULL,0);
|
||||
|
||||
context->Draw(4,0);
|
||||
}
|
||||
|
||||
void FurnaceGUIRenderDX11::present() {
|
||||
swapchain->Present(1,0);
|
||||
}
|
||||
|
||||
bool FurnaceGUIRenderDX11::getOutputSize(int& w, int& h) {
|
||||
w=outW;
|
||||
h=outH;
|
||||
return true;
|
||||
}
|
||||
|
||||
int FurnaceGUIRenderDX11::getWindowFlags() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void FurnaceGUIRenderDX11::preInit() {
|
||||
}
|
||||
|
||||
const float wipeVertices[4][4]={
|
||||
-1.0, -1.0, 0.0, 1.0,
|
||||
1.0, -1.0, 0.0, 1.0,
|
||||
-1.0, 1.0, 0.0, 1.0,
|
||||
1.0, 1.0, 0.0, 1.0
|
||||
};
|
||||
|
||||
bool FurnaceGUIRenderDX11::init(SDL_Window* win) {
|
||||
SDL_SysWMinfo sysWindow;
|
||||
D3D_FEATURE_LEVEL featureLevel;
|
||||
|
||||
SDL_VERSION(&sysWindow.version);
|
||||
if (SDL_GetWindowWMInfo(win,&sysWindow)==SDL_FALSE) {
|
||||
logE("could not get window WM info! %s",SDL_GetError());
|
||||
return false;
|
||||
}
|
||||
HWND window=(HWND)sysWindow.info.win.window;
|
||||
|
||||
DXGI_SWAP_CHAIN_DESC chainDesc;
|
||||
memset(&chainDesc,0,sizeof(chainDesc));
|
||||
chainDesc.BufferDesc.Width=0;
|
||||
chainDesc.BufferDesc.Height=0;
|
||||
chainDesc.BufferDesc.Format=DXGI_FORMAT_R8G8B8A8_UNORM;
|
||||
chainDesc.BufferDesc.RefreshRate.Numerator=60;
|
||||
chainDesc.BufferDesc.RefreshRate.Denominator=1;
|
||||
chainDesc.SampleDesc.Count=1;
|
||||
chainDesc.SampleDesc.Quality=0;
|
||||
chainDesc.BufferUsage=DXGI_USAGE_RENDER_TARGET_OUTPUT;
|
||||
chainDesc.BufferCount=2;
|
||||
chainDesc.OutputWindow=window;
|
||||
chainDesc.Windowed=TRUE; // TODO: what if we're in full screen mode?
|
||||
chainDesc.SwapEffect=DXGI_SWAP_EFFECT_DISCARD;
|
||||
chainDesc.Flags=DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH;
|
||||
|
||||
HRESULT result=D3D11CreateDeviceAndSwapChain(NULL,D3D_DRIVER_TYPE_HARDWARE,NULL,0,possibleFeatureLevels,2,D3D11_SDK_VERSION,&chainDesc,&swapchain,&device,&featureLevel,&context);
|
||||
if (result!=S_OK) {
|
||||
logE("could not create device and/or swap chain! %.8x",result);
|
||||
return false;
|
||||
}
|
||||
|
||||
// https://github.com/ocornut/imgui/pull/638
|
||||
D3DCompile_t D3DCompile=NULL;
|
||||
char dllBuffer[20];
|
||||
for (int i=47; (i>30 && !D3DCompile); i--) {
|
||||
snprintf(dllBuffer,20,"d3dcompiler_%d.dll",i);
|
||||
HMODULE hDll=LoadLibraryA(dllBuffer);
|
||||
if (hDll) {
|
||||
D3DCompile=(D3DCompile_t)GetProcAddress(hDll,"D3DCompile");
|
||||
}
|
||||
}
|
||||
if (!D3DCompile) {
|
||||
logE("could not find D3DCompile!");
|
||||
return false;
|
||||
}
|
||||
|
||||
// create wipe shader
|
||||
ID3DBlob* wipeBlobV=NULL;
|
||||
ID3DBlob* wipeBlobF=NULL;
|
||||
D3D11_BUFFER_DESC wipeConstantDesc;
|
||||
|
||||
result=D3DCompile(shD3D11_wipe_srcV,strlen(shD3D11_wipe_srcV),NULL,NULL,NULL,"main","vs_4_0",0,0,&wipeBlobV,NULL);
|
||||
if (result!=S_OK) {
|
||||
logE("could not compile vertex shader! %.8x",result);
|
||||
return false;
|
||||
}
|
||||
result=D3DCompile(shD3D11_wipe_srcF,strlen(shD3D11_wipe_srcF),NULL,NULL,NULL,"main","ps_4_0",0,0,&wipeBlobF,NULL);
|
||||
if (result!=S_OK) {
|
||||
logE("could not compile pixel shader! %.8x",result);
|
||||
return false;
|
||||
}
|
||||
|
||||
result=device->CreateVertexShader(wipeBlobV->GetBufferPointer(),wipeBlobV->GetBufferSize(),NULL,&sh_wipe_vertex);
|
||||
if (result!=S_OK) {
|
||||
logE("could not create vertex shader! %.8x",result);
|
||||
return false;
|
||||
}
|
||||
result=device->CreatePixelShader(wipeBlobF->GetBufferPointer(),wipeBlobF->GetBufferSize(),NULL,&sh_wipe_fragment);
|
||||
if (result!=S_OK) {
|
||||
logE("could not create pixel shader! %.8x",result);
|
||||
return false;
|
||||
}
|
||||
|
||||
result=device->CreateInputLayout(&shD3D11_wipe_inputLayout,1,wipeBlobV->GetBufferPointer(),wipeBlobV->GetBufferSize(),&sh_wipe_inputLayout);
|
||||
if (result!=S_OK) {
|
||||
logE("could not create input layout! %.8x",result);
|
||||
return false;
|
||||
}
|
||||
|
||||
memset(&wipeConstantDesc,0,sizeof(wipeConstantDesc));
|
||||
wipeConstantDesc.ByteWidth=sizeof(WipeUniform);
|
||||
wipeConstantDesc.Usage=D3D11_USAGE_DYNAMIC;
|
||||
wipeConstantDesc.BindFlags=D3D11_BIND_CONSTANT_BUFFER;
|
||||
wipeConstantDesc.CPUAccessFlags=D3D11_CPU_ACCESS_WRITE;
|
||||
wipeConstantDesc.MiscFlags=0;
|
||||
wipeConstantDesc.StructureByteStride=0;
|
||||
|
||||
result=device->CreateBuffer(&wipeConstantDesc,NULL,&sh_wipe_uniform);
|
||||
if (result!=S_OK) {
|
||||
logE("could not create constant buffer! %.8x",result);
|
||||
return false;
|
||||
}
|
||||
|
||||
// create wipe vertices
|
||||
D3D11_BUFFER_DESC vertexDesc;
|
||||
D3D11_SUBRESOURCE_DATA vertexRes;
|
||||
|
||||
memset(&vertexDesc,0,sizeof(vertexDesc));
|
||||
memset(&vertexRes,0,sizeof(vertexRes));
|
||||
|
||||
vertexDesc.ByteWidth=4*4*sizeof(float);
|
||||
vertexDesc.Usage=D3D11_USAGE_DEFAULT;
|
||||
vertexDesc.BindFlags=D3D11_BIND_VERTEX_BUFFER;
|
||||
vertexDesc.CPUAccessFlags=0;
|
||||
vertexDesc.MiscFlags=0;
|
||||
vertexDesc.StructureByteStride=0;
|
||||
|
||||
vertexRes.pSysMem=wipeVertices;
|
||||
vertexRes.SysMemPitch=0;
|
||||
vertexRes.SysMemSlicePitch=0;
|
||||
|
||||
result=device->CreateBuffer(&vertexDesc,&vertexRes,&quadVertex);
|
||||
if (result!=S_OK) {
|
||||
logE("could not create vertex buffer! %.8x",result);
|
||||
return false;
|
||||
}
|
||||
|
||||
// initialize the rest
|
||||
D3D11_RASTERIZER_DESC rasterDesc;
|
||||
D3D11_BLEND_DESC blendDesc;
|
||||
|
||||
memset(&rasterDesc,0,sizeof(rasterDesc));
|
||||
memset(&blendDesc,0,sizeof(blendDesc));
|
||||
|
||||
rasterDesc.FillMode=D3D11_FILL_SOLID;
|
||||
rasterDesc.CullMode=D3D11_CULL_NONE;
|
||||
rasterDesc.FrontCounterClockwise=false;
|
||||
rasterDesc.DepthBias=0;
|
||||
rasterDesc.DepthBiasClamp=0.0f;
|
||||
rasterDesc.SlopeScaledDepthBias=0.0f;
|
||||
rasterDesc.DepthClipEnable=false;
|
||||
rasterDesc.ScissorEnable=false;
|
||||
rasterDesc.MultisampleEnable=false;
|
||||
rasterDesc.AntialiasedLineEnable=false;
|
||||
result=device->CreateRasterizerState(&rasterDesc,&rsState);
|
||||
if (result!=S_OK) {
|
||||
logE("could not create rasterizer state! %.8x",result);
|
||||
return false;
|
||||
}
|
||||
|
||||
blendDesc.AlphaToCoverageEnable=false;
|
||||
blendDesc.IndependentBlendEnable=false;
|
||||
blendDesc.RenderTarget[0].BlendEnable=true;
|
||||
blendDesc.RenderTarget[0].SrcBlend=D3D11_BLEND_SRC_ALPHA;
|
||||
blendDesc.RenderTarget[0].DestBlend=D3D11_BLEND_INV_SRC_ALPHA;
|
||||
blendDesc.RenderTarget[0].BlendOp=D3D11_BLEND_OP_ADD;
|
||||
blendDesc.RenderTarget[0].SrcBlendAlpha=D3D11_BLEND_ONE;
|
||||
blendDesc.RenderTarget[0].DestBlendAlpha=D3D11_BLEND_INV_SRC_ALPHA;
|
||||
blendDesc.RenderTarget[0].BlendOpAlpha=D3D11_BLEND_OP_ADD;
|
||||
blendDesc.RenderTarget[0].RenderTargetWriteMask=D3D11_COLOR_WRITE_ENABLE_ALL;
|
||||
result=device->CreateBlendState(&blendDesc,&omBlendState);
|
||||
if (result!=S_OK) {
|
||||
logE("could not create blend state! %.8x",result);
|
||||
return false;
|
||||
}
|
||||
|
||||
createRenderTarget();
|
||||
return true;
|
||||
}
|
||||
|
||||
void FurnaceGUIRenderDX11::initGUI(SDL_Window* win) {
|
||||
IMGUI_CHECKVERSION();
|
||||
ImGui::CreateContext();
|
||||
|
||||
ImGui_ImplSDL2_InitForD3D(win);
|
||||
ImGui_ImplDX11_Init(device,context);
|
||||
}
|
||||
|
||||
bool FurnaceGUIRenderDX11::quit() {
|
||||
destroyRenderTarget();
|
||||
|
||||
for (FurnaceDXTexture* i: textures) {
|
||||
i->view->Release();
|
||||
i->tex->Release();
|
||||
delete i;
|
||||
}
|
||||
textures.clear();
|
||||
|
||||
if (swapchain!=NULL) {
|
||||
swapchain->Release();
|
||||
swapchain=NULL;
|
||||
}
|
||||
if (context!=NULL) {
|
||||
context->Release();
|
||||
context=NULL;
|
||||
}
|
||||
if (device!=NULL) {
|
||||
device->Release();
|
||||
device=NULL;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void FurnaceGUIRenderDX11::quitGUI() {
|
||||
ImGui_ImplDX11_Shutdown();
|
||||
}
|
||||
103
src/gui/render/renderDX11.h
Normal file
103
src/gui/render/renderDX11.h
Normal file
|
|
@ -0,0 +1,103 @@
|
|||
/**
|
||||
* Furnace Tracker - multi-system chiptune tracker
|
||||
* Copyright (C) 2021-2023 tildearrow and contributors
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "../gui.h"
|
||||
#ifdef INCLUDE_D3D11
|
||||
#include <d3d11.h>
|
||||
#else
|
||||
typedef void ID3D11DeviceContext;
|
||||
typedef void ID3D11RenderTargetView;
|
||||
typedef void ID3D11Buffer;
|
||||
typedef void ID3D11RasterizerState;
|
||||
typedef void ID3D11BlendState;
|
||||
typedef void ID3D11VertexShader;
|
||||
typedef void ID3D11PixelShader;
|
||||
typedef void ID3D11InputLayout;
|
||||
typedef void IDXGISwapChain;
|
||||
#endif
|
||||
|
||||
struct FurnaceDXTexture;
|
||||
|
||||
class FurnaceGUIRenderDX11: public FurnaceGUIRender {
|
||||
ID3D11Device* device;
|
||||
ID3D11DeviceContext* context;
|
||||
ID3D11RenderTargetView* renderTarget;
|
||||
IDXGISwapChain* swapchain;
|
||||
ID3D11RasterizerState* rsState;
|
||||
ID3D11BlendState* omBlendState;
|
||||
|
||||
ID3D11Buffer* quadVertex;
|
||||
int outW, outH;
|
||||
|
||||
// SHADERS //
|
||||
// -> wipe
|
||||
ID3D11VertexShader* sh_wipe_vertex;
|
||||
ID3D11PixelShader* sh_wipe_fragment;
|
||||
ID3D11InputLayout* sh_wipe_inputLayout;
|
||||
ID3D11Buffer* sh_wipe_uniform;
|
||||
struct WipeUniform {
|
||||
float alpha;
|
||||
float padding[7];
|
||||
};
|
||||
|
||||
bool destroyRenderTarget();
|
||||
bool createRenderTarget();
|
||||
|
||||
std::vector<FurnaceDXTexture*> textures;
|
||||
|
||||
public:
|
||||
ImTextureID getTextureID(void* which);
|
||||
bool lockTexture(void* which, void** data, int* pitch);
|
||||
bool unlockTexture(void* which);
|
||||
bool updateTexture(void* which, void* data, int pitch);
|
||||
void* createTexture(bool dynamic, int width, int height);
|
||||
bool destroyTexture(void* which);
|
||||
void setTextureBlendMode(void* which, FurnaceGUIBlendMode mode);
|
||||
void setBlendMode(FurnaceGUIBlendMode mode);
|
||||
void resized(const SDL_Event& ev);
|
||||
void clear(ImVec4 color);
|
||||
bool newFrame();
|
||||
void createFontsTexture();
|
||||
void destroyFontsTexture();
|
||||
void renderGUI();
|
||||
void wipe(float alpha);
|
||||
void present();
|
||||
bool getOutputSize(int& w, int& h);
|
||||
int getWindowFlags();
|
||||
void preInit();
|
||||
bool init(SDL_Window* win);
|
||||
void initGUI(SDL_Window* win);
|
||||
void quitGUI();
|
||||
bool quit();
|
||||
FurnaceGUIRenderDX11():
|
||||
device(NULL),
|
||||
context(NULL),
|
||||
renderTarget(NULL),
|
||||
swapchain(NULL),
|
||||
rsState(NULL),
|
||||
omBlendState(NULL),
|
||||
quadVertex(NULL),
|
||||
outW(0),
|
||||
outH(0),
|
||||
sh_wipe_vertex(NULL),
|
||||
sh_wipe_fragment(NULL),
|
||||
sh_wipe_inputLayout(NULL),
|
||||
sh_wipe_uniform(NULL) {
|
||||
}
|
||||
};
|
||||
387
src/gui/render/renderGL.cpp
Normal file
387
src/gui/render/renderGL.cpp
Normal file
|
|
@ -0,0 +1,387 @@
|
|||
/**
|
||||
* Furnace Tracker - multi-system chiptune tracker
|
||||
* Copyright (C) 2021-2023 tildearrow and contributors
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "renderGL.h"
|
||||
#include "../../ta-log.h"
|
||||
#ifdef USE_GLES
|
||||
#include "SDL_opengles2.h"
|
||||
#define PIXEL_FORMAT GL_UNSIGNED_BYTE
|
||||
#else
|
||||
#include "SDL_opengl.h"
|
||||
#define PIXEL_FORMAT GL_UNSIGNED_INT_8_8_8_8_REV
|
||||
#endif
|
||||
#include "backends/imgui_impl_opengl3.h"
|
||||
|
||||
#define C(x) x; if (glGetError()!=GL_NO_ERROR) logW("OpenGL error in %s:%d: " #x,__FILE__,__LINE__);
|
||||
|
||||
PFNGLGENBUFFERSPROC furGenBuffers=NULL;
|
||||
PFNGLBINDBUFFERPROC furBindBuffer=NULL;
|
||||
PFNGLBUFFERDATAPROC furBufferData=NULL;
|
||||
PFNGLVERTEXATTRIBPOINTERPROC furVertexAttribPointer=NULL;
|
||||
PFNGLENABLEVERTEXATTRIBARRAYPROC furEnableVertexAttribArray=NULL;
|
||||
PFNGLACTIVETEXTUREPROC furActiveTexture=NULL;
|
||||
|
||||
PFNGLCREATESHADERPROC furCreateShader=NULL;
|
||||
PFNGLSHADERSOURCEPROC furShaderSource=NULL;
|
||||
PFNGLCOMPILESHADERPROC furCompileShader=NULL;
|
||||
PFNGLGETSHADERIVPROC furGetShaderiv=NULL;
|
||||
PFNGLATTACHSHADERPROC furAttachShader=NULL;
|
||||
PFNGLBINDATTRIBLOCATIONPROC furBindAttribLocation=NULL;
|
||||
PFNGLCREATEPROGRAMPROC furCreateProgram=NULL;
|
||||
PFNGLLINKPROGRAMPROC furLinkProgram=NULL;
|
||||
PFNGLGETPROGRAMIVPROC furGetProgramiv=NULL;
|
||||
PFNGLUSEPROGRAMPROC furUseProgram=NULL;
|
||||
PFNGLGETUNIFORMLOCATIONPROC furGetUniformLocation=NULL;
|
||||
PFNGLUNIFORM1FPROC furUniform1f=NULL;
|
||||
PFNGLGETSHADERINFOLOGPROC furGetShaderInfoLog=NULL;
|
||||
|
||||
struct FurnaceGLTexture {
|
||||
GLuint id;
|
||||
int width, height;
|
||||
unsigned char* lockedData;
|
||||
FurnaceGLTexture():
|
||||
id(0),
|
||||
width(0),
|
||||
height(0),
|
||||
lockedData(NULL) {}
|
||||
};
|
||||
|
||||
#ifdef USE_GLES
|
||||
const char* sh_wipe_srcV=
|
||||
"attribute vec4 fur_position;\n"
|
||||
"void main() {\n"
|
||||
" gl_Position=fur_position;\n"
|
||||
"}\n";
|
||||
|
||||
const char* sh_wipe_srcF=
|
||||
"uniform float uAlpha;\n"
|
||||
"void main() {\n"
|
||||
" gl_FragColor=vec4(0.0,0.0,0.0,uAlpha);\n"
|
||||
"}\n";
|
||||
#else
|
||||
const char* sh_wipe_srcV=
|
||||
"#version 130\n"
|
||||
"in vec4 fur_position;\n"
|
||||
"void main() {\n"
|
||||
" gl_Position=fur_position;\n"
|
||||
"}\n";
|
||||
|
||||
const char* sh_wipe_srcF=
|
||||
"#version 130\n"
|
||||
"uniform float uAlpha;\n"
|
||||
"out vec4 fur_FragColor;\n"
|
||||
"void main() {\n"
|
||||
" fur_FragColor=vec4(0.0,0.0,0.0,uAlpha);\n"
|
||||
"}\n";
|
||||
#endif
|
||||
|
||||
bool FurnaceGUIRenderGL::createShader(const char* vertexS, const char* fragmentS, int& vertex, int& fragment, int& program) {
|
||||
int status;
|
||||
char infoLog[4096];
|
||||
int infoLogLen;
|
||||
|
||||
if (!furCreateShader || !furShaderSource || !furCompileShader || !furGetShaderiv ||
|
||||
!furGetShaderInfoLog || !furCreateProgram || !furAttachShader || !furLinkProgram ||
|
||||
!furBindAttribLocation || !furGetProgramiv) {
|
||||
logW("I can't compile shaders");
|
||||
return false;
|
||||
}
|
||||
|
||||
vertex=furCreateShader(GL_VERTEX_SHADER);
|
||||
furShaderSource(vertex,1,&vertexS,NULL);
|
||||
furCompileShader(vertex);
|
||||
furGetShaderiv(vertex,GL_COMPILE_STATUS,&status);
|
||||
if (!status) {
|
||||
logW("failed to compile vertex shader");
|
||||
furGetShaderInfoLog(vertex,4095,&infoLogLen,infoLog);
|
||||
infoLog[infoLogLen]=0;
|
||||
logW("%s",infoLog);
|
||||
return false;
|
||||
}
|
||||
|
||||
fragment=furCreateShader(GL_FRAGMENT_SHADER);
|
||||
furShaderSource(fragment,1,&fragmentS,NULL);
|
||||
furCompileShader(fragment);
|
||||
furGetShaderiv(fragment,GL_COMPILE_STATUS,&status);
|
||||
if (!status) {
|
||||
logW("failed to compile fragment shader");
|
||||
return false;
|
||||
}
|
||||
|
||||
program=furCreateProgram();
|
||||
furAttachShader(program,vertex);
|
||||
furAttachShader(program,fragment);
|
||||
furBindAttribLocation(program,0,"fur_position");
|
||||
furLinkProgram(program);
|
||||
furGetProgramiv(program,GL_LINK_STATUS,&status);
|
||||
if (!status) {
|
||||
logW("failed to link shader!");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
ImTextureID FurnaceGUIRenderGL::getTextureID(void* which) {
|
||||
intptr_t ret=((FurnaceGLTexture*)which)->id;
|
||||
return (ImTextureID)ret;
|
||||
}
|
||||
|
||||
bool FurnaceGUIRenderGL::lockTexture(void* which, void** data, int* pitch) {
|
||||
FurnaceGLTexture* t=(FurnaceGLTexture*)which;
|
||||
if (t->lockedData!=NULL) return false;
|
||||
t->lockedData=new unsigned char[t->width*t->height*4];
|
||||
|
||||
*data=t->lockedData;
|
||||
*pitch=t->width*4;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FurnaceGUIRenderGL::unlockTexture(void* which) {
|
||||
FurnaceGLTexture* t=(FurnaceGLTexture*)which;
|
||||
if (t->lockedData==NULL) return false;
|
||||
|
||||
C(glBindTexture(GL_TEXTURE_2D,t->id));
|
||||
C(glTexImage2D(GL_TEXTURE_2D,0,GL_RGBA,t->width,t->height,0,GL_RGBA,PIXEL_FORMAT,t->lockedData));
|
||||
|
||||
C(glFlush());
|
||||
delete[] t->lockedData;
|
||||
t->lockedData=NULL;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FurnaceGUIRenderGL::updateTexture(void* which, void* data, int pitch) {
|
||||
FurnaceGLTexture* t=(FurnaceGLTexture*)which;
|
||||
|
||||
if (t->width*4!=pitch) return false;
|
||||
|
||||
C(glBindTexture(GL_TEXTURE_2D,t->id));
|
||||
C(glTexImage2D(GL_TEXTURE_2D,0,GL_RGBA,t->width,t->height,0,GL_RGBA,PIXEL_FORMAT,data));
|
||||
return true;
|
||||
}
|
||||
|
||||
void* FurnaceGUIRenderGL::createTexture(bool dynamic, int width, int height) {
|
||||
FurnaceGLTexture* t=new FurnaceGLTexture;
|
||||
C(glGenTextures(1,&t->id));
|
||||
C(glBindTexture(GL_TEXTURE_2D,t->id));
|
||||
C(glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR));
|
||||
C(glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR));
|
||||
C(glTexImage2D(GL_TEXTURE_2D,0,GL_RGBA,width,height,0,GL_RGBA,PIXEL_FORMAT,NULL));
|
||||
C(furActiveTexture(GL_TEXTURE0));
|
||||
t->width=width;
|
||||
t->height=height;
|
||||
return t;
|
||||
}
|
||||
|
||||
bool FurnaceGUIRenderGL::destroyTexture(void* which) {
|
||||
FurnaceGLTexture* t=(FurnaceGLTexture*)which;
|
||||
C(glDeleteTextures(1,&t->id));
|
||||
delete t;
|
||||
return true;
|
||||
}
|
||||
|
||||
void FurnaceGUIRenderGL::setTextureBlendMode(void* which, FurnaceGUIBlendMode mode) {
|
||||
}
|
||||
|
||||
void FurnaceGUIRenderGL::setBlendMode(FurnaceGUIBlendMode mode) {
|
||||
switch (mode) {
|
||||
case GUI_BLEND_MODE_NONE:
|
||||
C(glBlendFunc(GL_ONE,GL_ZERO));
|
||||
C(glDisable(GL_BLEND));
|
||||
break;
|
||||
case GUI_BLEND_MODE_BLEND:
|
||||
C(glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA));
|
||||
C(glEnable(GL_BLEND));
|
||||
break;
|
||||
case GUI_BLEND_MODE_ADD:
|
||||
C(glBlendFunc(GL_SRC_ALPHA,GL_ONE));
|
||||
C(glEnable(GL_BLEND));
|
||||
break;
|
||||
case GUI_BLEND_MODE_MULTIPLY:
|
||||
C(glBlendFunc(GL_ZERO,GL_SRC_COLOR));
|
||||
C(glEnable(GL_BLEND));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void FurnaceGUIRenderGL::clear(ImVec4 color) {
|
||||
SDL_GL_MakeCurrent(sdlWin,context);
|
||||
C(glClearColor(color.x,color.y,color.z,color.w));
|
||||
C(glClear(GL_COLOR_BUFFER_BIT));
|
||||
}
|
||||
|
||||
bool FurnaceGUIRenderGL::newFrame() {
|
||||
ImGui_ImplOpenGL3_NewFrame();
|
||||
return true;
|
||||
}
|
||||
|
||||
void FurnaceGUIRenderGL::createFontsTexture() {
|
||||
ImGui_ImplOpenGL3_CreateFontsTexture();
|
||||
}
|
||||
|
||||
void FurnaceGUIRenderGL::destroyFontsTexture() {
|
||||
ImGui_ImplOpenGL3_DestroyFontsTexture();
|
||||
}
|
||||
|
||||
void FurnaceGUIRenderGL::renderGUI() {
|
||||
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
|
||||
}
|
||||
|
||||
void FurnaceGUIRenderGL::wipe(float alpha) {
|
||||
C(glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA));
|
||||
C(glEnable(GL_BLEND));
|
||||
|
||||
quadVertex[0][0]=-1.0f;
|
||||
quadVertex[0][1]=-1.0f;
|
||||
quadVertex[0][2]=0.0f;
|
||||
quadVertex[1][0]=1.0f;
|
||||
quadVertex[1][1]=-1.0f;
|
||||
quadVertex[1][2]=0.0f;
|
||||
quadVertex[2][0]=-1.0f;
|
||||
quadVertex[2][1]=1.0f;
|
||||
quadVertex[2][2]=0.0f;
|
||||
quadVertex[3][0]=1.0f;
|
||||
quadVertex[3][1]=1.0f;
|
||||
quadVertex[3][2]=0.0f;
|
||||
|
||||
C(furBindBuffer(GL_ARRAY_BUFFER,quadBuf));
|
||||
C(furBufferData(GL_ARRAY_BUFFER,sizeof(quadVertex),quadVertex,GL_STATIC_DRAW));
|
||||
C(furVertexAttribPointer(0,3,GL_FLOAT,GL_FALSE,0,NULL));
|
||||
C(furEnableVertexAttribArray(0));
|
||||
C(furActiveTexture(GL_TEXTURE0));
|
||||
C(glBindTexture(GL_TEXTURE_2D,0));
|
||||
if (furUseProgram && furUniform1f) {
|
||||
C(furUseProgram(sh_wipe_program));
|
||||
C(furUniform1f(sh_wipe_uAlpha,alpha));
|
||||
}
|
||||
C(glDrawArrays(GL_TRIANGLE_STRIP,0,4));
|
||||
}
|
||||
|
||||
void FurnaceGUIRenderGL::present() {
|
||||
SDL_GL_SwapWindow(sdlWin);
|
||||
C(glFlush());
|
||||
}
|
||||
|
||||
bool FurnaceGUIRenderGL::getOutputSize(int& w, int& h) {
|
||||
SDL_GL_GetDrawableSize(sdlWin,&w,&h);
|
||||
return true;
|
||||
}
|
||||
|
||||
int FurnaceGUIRenderGL::getWindowFlags() {
|
||||
return SDL_WINDOW_OPENGL;
|
||||
}
|
||||
|
||||
void FurnaceGUIRenderGL::preInit() {
|
||||
#if defined(USE_GLES)
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, 0);
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK,SDL_GL_CONTEXT_PROFILE_ES);
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION,2);
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION,0);
|
||||
#elif defined(__APPLE__)
|
||||
// not recommended...
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_FORWARD_COMPATIBLE_FLAG);
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK,SDL_GL_CONTEXT_PROFILE_CORE);
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION,3);
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION,2);
|
||||
#else
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS,0);
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION,3);
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION,0);
|
||||
#endif
|
||||
|
||||
SDL_GL_SetAttribute(SDL_GL_RED_SIZE,8);
|
||||
SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE,8);
|
||||
SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE,8);
|
||||
SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE,0);
|
||||
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER,1);
|
||||
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE,24);
|
||||
}
|
||||
|
||||
#define LOAD_PROC_MANDATORY(_v,_t,_s) \
|
||||
_v=(_t)SDL_GL_GetProcAddress(_s); \
|
||||
if (!_v) { \
|
||||
logE(_s " not found"); \
|
||||
return false; \
|
||||
}
|
||||
|
||||
#define LOAD_PROC_OPTIONAL(_v,_t,_s) \
|
||||
_v=(_t)SDL_GL_GetProcAddress(_s); \
|
||||
if (!_v) { \
|
||||
logW(_s " not found"); \
|
||||
}
|
||||
|
||||
bool FurnaceGUIRenderGL::init(SDL_Window* win) {
|
||||
sdlWin=win;
|
||||
context=SDL_GL_CreateContext(win);
|
||||
if (context==NULL) {
|
||||
return false;
|
||||
}
|
||||
SDL_GL_MakeCurrent(win,context);
|
||||
SDL_GL_SetSwapInterval(1);
|
||||
|
||||
LOAD_PROC_MANDATORY(furGenBuffers,PFNGLGENBUFFERSPROC,"glGenBuffers");
|
||||
LOAD_PROC_MANDATORY(furBindBuffer,PFNGLBINDBUFFERPROC,"glBindBuffer");
|
||||
LOAD_PROC_MANDATORY(furBufferData,PFNGLBUFFERDATAPROC,"glBufferData");
|
||||
LOAD_PROC_MANDATORY(furVertexAttribPointer,PFNGLVERTEXATTRIBPOINTERPROC,"glVertexAttribPointer");
|
||||
LOAD_PROC_MANDATORY(furEnableVertexAttribArray,PFNGLENABLEVERTEXATTRIBARRAYPROC,"glEnableVertexAttribArray");
|
||||
LOAD_PROC_MANDATORY(furActiveTexture,PFNGLACTIVETEXTUREPROC,"glActiveTexture");
|
||||
|
||||
LOAD_PROC_OPTIONAL(furCreateShader,PFNGLCREATESHADERPROC,"glCreateShader");
|
||||
LOAD_PROC_OPTIONAL(furShaderSource,PFNGLSHADERSOURCEPROC,"glShaderSource");
|
||||
LOAD_PROC_OPTIONAL(furCompileShader,PFNGLCOMPILESHADERPROC,"glCompileShader");
|
||||
LOAD_PROC_OPTIONAL(furGetShaderiv,PFNGLGETSHADERIVPROC,"glGetShaderiv");
|
||||
LOAD_PROC_OPTIONAL(furAttachShader,PFNGLATTACHSHADERPROC,"glAttachShader");
|
||||
LOAD_PROC_OPTIONAL(furBindAttribLocation,PFNGLBINDATTRIBLOCATIONPROC,"glBindAttribLocation");
|
||||
LOAD_PROC_OPTIONAL(furCreateProgram,PFNGLCREATEPROGRAMPROC,"glCreateProgram");
|
||||
LOAD_PROC_OPTIONAL(furLinkProgram,PFNGLLINKPROGRAMPROC,"glLinkProgram");
|
||||
LOAD_PROC_OPTIONAL(furGetProgramiv,PFNGLGETPROGRAMIVPROC,"glGetProgramiv");
|
||||
LOAD_PROC_OPTIONAL(furUseProgram,PFNGLUSEPROGRAMPROC,"glUseProgram");
|
||||
LOAD_PROC_OPTIONAL(furGetUniformLocation,PFNGLGETUNIFORMLOCATIONPROC,"glGetUniformLocation");
|
||||
LOAD_PROC_OPTIONAL(furUniform1f,PFNGLUNIFORM1FPROC,"glUniform1f");
|
||||
LOAD_PROC_OPTIONAL(furGetShaderInfoLog,PFNGLGETSHADERINFOLOGPROC,"glGetShaderInfoLog");
|
||||
|
||||
|
||||
if (createShader(sh_wipe_srcV,sh_wipe_srcF,sh_wipe_vertex,sh_wipe_fragment,sh_wipe_program)) {
|
||||
sh_wipe_uAlpha=furGetUniformLocation(sh_wipe_program,"uAlpha");
|
||||
}
|
||||
|
||||
C(furGenBuffers(1,&quadBuf));
|
||||
return true;
|
||||
}
|
||||
|
||||
void FurnaceGUIRenderGL::initGUI(SDL_Window* win) {
|
||||
IMGUI_CHECKVERSION();
|
||||
ImGui::CreateContext();
|
||||
|
||||
ImGui_ImplSDL2_InitForOpenGL(win,context);
|
||||
ImGui_ImplOpenGL3_Init();
|
||||
}
|
||||
|
||||
bool FurnaceGUIRenderGL::quit() {
|
||||
if (context==NULL) return false;
|
||||
SDL_GL_DeleteContext(context);
|
||||
context=NULL;
|
||||
return true;
|
||||
}
|
||||
|
||||
void FurnaceGUIRenderGL::quitGUI() {
|
||||
ImGui_ImplOpenGL3_Shutdown();
|
||||
}
|
||||
65
src/gui/render/renderGL.h
Normal file
65
src/gui/render/renderGL.h
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
/**
|
||||
* Furnace Tracker - multi-system chiptune tracker
|
||||
* Copyright (C) 2021-2023 tildearrow and contributors
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "../gui.h"
|
||||
|
||||
class FurnaceGUIRenderGL: public FurnaceGUIRender {
|
||||
SDL_GLContext context;
|
||||
SDL_Window* sdlWin;
|
||||
float quadVertex[4][3];
|
||||
unsigned int quadBuf;
|
||||
|
||||
// SHADERS //
|
||||
// -> wipe
|
||||
int sh_wipe_vertex;
|
||||
int sh_wipe_fragment;
|
||||
int sh_wipe_program;
|
||||
int sh_wipe_uAlpha;
|
||||
|
||||
bool createShader(const char* vertexS, const char* fragmentS, int& vertex, int& fragment, int& program);
|
||||
|
||||
public:
|
||||
ImTextureID getTextureID(void* which);
|
||||
bool lockTexture(void* which, void** data, int* pitch);
|
||||
bool unlockTexture(void* which);
|
||||
bool updateTexture(void* which, void* data, int pitch);
|
||||
void* createTexture(bool dynamic, int width, int height);
|
||||
bool destroyTexture(void* which);
|
||||
void setTextureBlendMode(void* which, FurnaceGUIBlendMode mode);
|
||||
void setBlendMode(FurnaceGUIBlendMode mode);
|
||||
void clear(ImVec4 color);
|
||||
bool newFrame();
|
||||
void createFontsTexture();
|
||||
void destroyFontsTexture();
|
||||
void renderGUI();
|
||||
void wipe(float alpha);
|
||||
void present();
|
||||
bool getOutputSize(int& w, int& h);
|
||||
int getWindowFlags();
|
||||
void preInit();
|
||||
bool init(SDL_Window* win);
|
||||
void initGUI(SDL_Window* win);
|
||||
void quitGUI();
|
||||
bool quit();
|
||||
FurnaceGUIRenderGL():
|
||||
context(NULL),
|
||||
sdlWin(NULL) {
|
||||
memset(quadVertex,0,4*3*sizeof(float));
|
||||
}
|
||||
};
|
||||
147
src/gui/render/renderSDL.cpp
Normal file
147
src/gui/render/renderSDL.cpp
Normal file
|
|
@ -0,0 +1,147 @@
|
|||
/**
|
||||
* Furnace Tracker - multi-system chiptune tracker
|
||||
* Copyright (C) 2021-2023 tildearrow and contributors
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "renderSDL.h"
|
||||
#include "backends/imgui_impl_sdlrenderer2.h"
|
||||
|
||||
ImTextureID FurnaceGUIRenderSDL::getTextureID(void* which) {
|
||||
return which;
|
||||
}
|
||||
|
||||
bool FurnaceGUIRenderSDL::lockTexture(void* which, void** data, int* pitch) {
|
||||
return SDL_LockTexture((SDL_Texture*)which,NULL,data,pitch)==0;
|
||||
}
|
||||
|
||||
bool FurnaceGUIRenderSDL::unlockTexture(void* which) {
|
||||
SDL_UnlockTexture((SDL_Texture*)which);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FurnaceGUIRenderSDL::updateTexture(void* which, void* data, int pitch) {
|
||||
return SDL_UpdateTexture((SDL_Texture*)which,NULL,data,pitch)==0;
|
||||
}
|
||||
|
||||
void* FurnaceGUIRenderSDL::createTexture(bool dynamic, int width, int height) {
|
||||
return SDL_CreateTexture(sdlRend,SDL_PIXELFORMAT_ABGR8888,dynamic?SDL_TEXTUREACCESS_STREAMING:SDL_TEXTUREACCESS_STATIC,width,height);
|
||||
}
|
||||
|
||||
bool FurnaceGUIRenderSDL::destroyTexture(void* which) {
|
||||
SDL_DestroyTexture((SDL_Texture*)which);
|
||||
return true;
|
||||
}
|
||||
|
||||
void FurnaceGUIRenderSDL::setTextureBlendMode(void* which, FurnaceGUIBlendMode mode) {
|
||||
switch (mode) {
|
||||
case GUI_BLEND_MODE_NONE:
|
||||
SDL_SetTextureBlendMode((SDL_Texture*)which,SDL_BLENDMODE_NONE);
|
||||
break;
|
||||
case GUI_BLEND_MODE_BLEND:
|
||||
SDL_SetTextureBlendMode((SDL_Texture*)which,SDL_BLENDMODE_BLEND);
|
||||
break;
|
||||
case GUI_BLEND_MODE_ADD:
|
||||
SDL_SetTextureBlendMode((SDL_Texture*)which,SDL_BLENDMODE_ADD);
|
||||
break;
|
||||
case GUI_BLEND_MODE_MULTIPLY:
|
||||
SDL_SetTextureBlendMode((SDL_Texture*)which,SDL_BLENDMODE_MOD);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void FurnaceGUIRenderSDL::setBlendMode(FurnaceGUIBlendMode mode) {
|
||||
switch (mode) {
|
||||
case GUI_BLEND_MODE_NONE:
|
||||
SDL_SetRenderDrawBlendMode(sdlRend,SDL_BLENDMODE_NONE);
|
||||
break;
|
||||
case GUI_BLEND_MODE_BLEND:
|
||||
SDL_SetRenderDrawBlendMode(sdlRend,SDL_BLENDMODE_BLEND);
|
||||
break;
|
||||
case GUI_BLEND_MODE_ADD:
|
||||
SDL_SetRenderDrawBlendMode(sdlRend,SDL_BLENDMODE_ADD);
|
||||
break;
|
||||
case GUI_BLEND_MODE_MULTIPLY:
|
||||
SDL_SetRenderDrawBlendMode(sdlRend,SDL_BLENDMODE_MOD);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void FurnaceGUIRenderSDL::clear(ImVec4 color) {
|
||||
SDL_SetRenderDrawColor(sdlRend,color.x*255,color.y*255,color.z*255,color.w*255);
|
||||
SDL_RenderClear(sdlRend);
|
||||
}
|
||||
|
||||
bool FurnaceGUIRenderSDL::newFrame() {
|
||||
return ImGui_ImplSDLRenderer2_NewFrame();
|
||||
}
|
||||
|
||||
void FurnaceGUIRenderSDL::createFontsTexture() {
|
||||
ImGui_ImplSDLRenderer2_CreateFontsTexture();
|
||||
}
|
||||
|
||||
void FurnaceGUIRenderSDL::destroyFontsTexture() {
|
||||
ImGui_ImplSDLRenderer2_DestroyFontsTexture();
|
||||
}
|
||||
|
||||
void FurnaceGUIRenderSDL::renderGUI() {
|
||||
ImGui_ImplSDLRenderer2_RenderDrawData(ImGui::GetDrawData());
|
||||
}
|
||||
|
||||
void FurnaceGUIRenderSDL::wipe(float alpha) {
|
||||
SDL_SetRenderDrawBlendMode(sdlRend,SDL_BLENDMODE_BLEND);
|
||||
SDL_SetRenderDrawColor(sdlRend,0,0,0,255*alpha);
|
||||
SDL_RenderFillRect(sdlRend,NULL);
|
||||
}
|
||||
|
||||
void FurnaceGUIRenderSDL::present() {
|
||||
SDL_RenderPresent(sdlRend);
|
||||
}
|
||||
|
||||
bool FurnaceGUIRenderSDL::getOutputSize(int& w, int& h) {
|
||||
return SDL_GetRendererOutputSize(sdlRend,&w,&h)==0;
|
||||
}
|
||||
|
||||
int FurnaceGUIRenderSDL::getWindowFlags() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void FurnaceGUIRenderSDL::preInit() {
|
||||
}
|
||||
|
||||
bool FurnaceGUIRenderSDL::init(SDL_Window* win) {
|
||||
sdlRend=SDL_CreateRenderer(win,-1,SDL_RENDERER_ACCELERATED|SDL_RENDERER_PRESENTVSYNC|SDL_RENDERER_TARGETTEXTURE);
|
||||
return (sdlRend!=NULL);
|
||||
}
|
||||
|
||||
void FurnaceGUIRenderSDL::initGUI(SDL_Window* win) {
|
||||
IMGUI_CHECKVERSION();
|
||||
ImGui::CreateContext();
|
||||
|
||||
ImGui_ImplSDL2_InitForSDLRenderer(win,sdlRend);
|
||||
ImGui_ImplSDLRenderer2_Init(sdlRend);
|
||||
}
|
||||
|
||||
void FurnaceGUIRenderSDL::quitGUI() {
|
||||
ImGui_ImplSDLRenderer2_Shutdown();
|
||||
}
|
||||
|
||||
bool FurnaceGUIRenderSDL::quit() {
|
||||
if (sdlRend==NULL) return false;
|
||||
SDL_DestroyRenderer(sdlRend);
|
||||
sdlRend=NULL;
|
||||
return true;
|
||||
}
|
||||
49
src/gui/render/renderSDL.h
Normal file
49
src/gui/render/renderSDL.h
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
/**
|
||||
* Furnace Tracker - multi-system chiptune tracker
|
||||
* Copyright (C) 2021-2023 tildearrow and contributors
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "../gui.h"
|
||||
|
||||
class FurnaceGUIRenderSDL: public FurnaceGUIRender {
|
||||
SDL_Renderer* sdlRend;
|
||||
public:
|
||||
ImTextureID getTextureID(void* which);
|
||||
bool lockTexture(void* which, void** data, int* pitch);
|
||||
bool unlockTexture(void* which);
|
||||
bool updateTexture(void* which, void* data, int pitch);
|
||||
void* createTexture(bool dynamic, int width, int height);
|
||||
bool destroyTexture(void* which);
|
||||
void setTextureBlendMode(void* which, FurnaceGUIBlendMode mode);
|
||||
void setBlendMode(FurnaceGUIBlendMode mode);
|
||||
void clear(ImVec4 color);
|
||||
bool newFrame();
|
||||
void createFontsTexture();
|
||||
void destroyFontsTexture();
|
||||
void renderGUI();
|
||||
void wipe(float alpha);
|
||||
void present();
|
||||
bool getOutputSize(int& w, int& h);
|
||||
int getWindowFlags();
|
||||
void preInit();
|
||||
bool init(SDL_Window* win);
|
||||
void initGUI(SDL_Window* win);
|
||||
void quitGUI();
|
||||
bool quit();
|
||||
FurnaceGUIRenderSDL():
|
||||
sdlRend(NULL) {}
|
||||
};
|
||||
|
|
@ -43,6 +43,12 @@ const double timeMultipliers[13]={
|
|||
#define CENTER_TEXT(text) \
|
||||
ImGui::SetCursorPosX(ImGui::GetCursorPosX()+0.5*(ImGui::GetContentRegionAvail().x-ImGui::CalcTextSize(text).x));
|
||||
|
||||
#define SAMPLE_WARN(_x,_text) \
|
||||
if (_x.find(_text)==String::npos) { \
|
||||
if (!_x.empty()) _x+='\n'; \
|
||||
_x+=_text; \
|
||||
}
|
||||
|
||||
void FurnaceGUI::drawSampleEdit() {
|
||||
if (nextWindow==GUI_WINDOW_SAMPLE_EDIT) {
|
||||
sampleEditOpen=true;
|
||||
|
|
@ -133,6 +139,12 @@ void FurnaceGUI::drawSampleEdit() {
|
|||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("Open");
|
||||
}
|
||||
if (ImGui::BeginPopupContextItem("SampleEOpenOpt")) {
|
||||
if (ImGui::MenuItem("import raw...")) {
|
||||
doAction((curSample>=0 && curSample<(int)e->song.sample.size())?GUI_ACTION_SAMPLE_LIST_OPEN_REPLACE_RAW:GUI_ACTION_SAMPLE_LIST_OPEN_RAW);
|
||||
}
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button(ICON_FA_FLOPPY_O "##SESave")) {
|
||||
doAction(GUI_ACTION_SAMPLE_LIST_SAVE);
|
||||
|
|
@ -140,18 +152,29 @@ void FurnaceGUI::drawSampleEdit() {
|
|||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("Save");
|
||||
}
|
||||
if (ImGui::BeginPopupContextItem("SampleESaveOpt")) {
|
||||
if (ImGui::MenuItem("save raw...")) {
|
||||
doAction(GUI_ACTION_SAMPLE_LIST_SAVE_RAW);
|
||||
}
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
|
||||
ImGui::SameLine();
|
||||
|
||||
ImGui::Text("Name");
|
||||
ImGui::SameLine();
|
||||
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
|
||||
ImGui::PushID(2+curSample);
|
||||
if (ImGui::InputText("##SampleName",&sample->name)) {
|
||||
MARK_MODIFIED;
|
||||
}
|
||||
ImGui::PopID();
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
String warnLoop, warnLoopMode, warnLoopPos;
|
||||
String warnLength;
|
||||
|
||||
bool isChipVisible[DIV_MAX_CHIPS];
|
||||
bool isTypeVisible[DIV_MAX_SAMPLE_TYPE];
|
||||
bool isMemVisible[DIV_MAX_SAMPLE_TYPE][DIV_MAX_CHIPS];
|
||||
|
|
@ -160,7 +183,104 @@ void FurnaceGUI::drawSampleEdit() {
|
|||
memset(isTypeVisible,0,DIV_MAX_SAMPLE_TYPE*sizeof(bool));
|
||||
memset(isMemVisible,0,DIV_MAX_CHIPS*DIV_MAX_SAMPLE_TYPE*sizeof(bool));
|
||||
memset(isMemWarning,0,DIV_MAX_CHIPS*DIV_MAX_SAMPLE_TYPE*sizeof(bool));
|
||||
|
||||
for (int i=0; i<e->song.systemLen; i++) {
|
||||
// warnings
|
||||
switch (e->song.system[i]) {
|
||||
case DIV_SYSTEM_SNES:
|
||||
if (sample->loop) {
|
||||
if (sample->loopStart&15 || sample->loopEnd&15) {
|
||||
SAMPLE_WARN(warnLoopPos,"SNES: loop must be a multiple of 16");
|
||||
}
|
||||
}
|
||||
break;
|
||||
case DIV_SYSTEM_QSOUND:
|
||||
if (sample->loop) {
|
||||
if (sample->loopEnd-sample->loopStart>32767) {
|
||||
SAMPLE_WARN(warnLoopPos,"QSound: loop cannot be longer than 32767 samples");
|
||||
}
|
||||
}
|
||||
if (sample->samples>65535) {
|
||||
SAMPLE_WARN(warnLength,"QSound: maximum sample length is 65535");
|
||||
}
|
||||
break;
|
||||
case DIV_SYSTEM_NES:
|
||||
if (sample->loop) {
|
||||
if (sample->loopStart!=0 || sample->loopEnd!=(int)(sample->samples)) {
|
||||
SAMPLE_WARN(warnLoopPos,"NES: loop point ignored on DPCM (may only loop entire sample)");
|
||||
}
|
||||
}
|
||||
if (sample->samples>32648) {
|
||||
SAMPLE_WARN(warnLength,"NES: maximum DPCM sample length is 32648");
|
||||
}
|
||||
break;
|
||||
case DIV_SYSTEM_X1_010:
|
||||
if (sample->loop) {
|
||||
SAMPLE_WARN(warnLoop,"X1-010: samples can't loop");
|
||||
}
|
||||
if (sample->samples>131072) {
|
||||
SAMPLE_WARN(warnLength,"X1-010: maximum sample length is 131072");
|
||||
}
|
||||
break;
|
||||
case DIV_SYSTEM_GA20:
|
||||
if (sample->loop) {
|
||||
SAMPLE_WARN(warnLoop,"GA20: samples can't loop");
|
||||
}
|
||||
break;
|
||||
case DIV_SYSTEM_YM2608:
|
||||
case DIV_SYSTEM_YM2608_EXT:
|
||||
case DIV_SYSTEM_YM2608_CSM:
|
||||
if (sample->loop) {
|
||||
if (sample->loopStart!=0 || sample->loopEnd!=(int)(sample->samples)) {
|
||||
SAMPLE_WARN(warnLoopPos,"YM2608: loop point ignored on ADPCM-B (may only loop entire sample)");
|
||||
}
|
||||
}
|
||||
break;
|
||||
case DIV_SYSTEM_YM2610_FULL:
|
||||
case DIV_SYSTEM_YM2610_FULL_EXT:
|
||||
case DIV_SYSTEM_YM2610_CSM:
|
||||
case DIV_SYSTEM_YM2610B:
|
||||
case DIV_SYSTEM_YM2610B_EXT:
|
||||
if (sample->loop) {
|
||||
SAMPLE_WARN(warnLoop,"YM2610: ADPCM-A samples can't loop");
|
||||
if (sample->loopStart!=0 || sample->loopEnd!=(int)(sample->samples)) {
|
||||
SAMPLE_WARN(warnLoopPos,"YM2610: loop point ignored on ADPCM-B (may only loop entire sample)");
|
||||
}
|
||||
}
|
||||
if (sample->samples>2097152) {
|
||||
SAMPLE_WARN(warnLength,"YM2610: maximum ADPCM-A sample length is 2097152");
|
||||
}
|
||||
break;
|
||||
case DIV_SYSTEM_AMIGA:
|
||||
if (sample->loop) {
|
||||
if (sample->loopStart&1 || sample->loopEnd&1) {
|
||||
SAMPLE_WARN(warnLoopPos,"Amiga: loop must be a multiple of 2");
|
||||
}
|
||||
}
|
||||
if (sample->samples>131070) {
|
||||
SAMPLE_WARN(warnLength,"Amiga: maximum sample length is 131070");
|
||||
}
|
||||
break;
|
||||
case DIV_SYSTEM_SEGAPCM:
|
||||
case DIV_SYSTEM_SEGAPCM_COMPAT:
|
||||
if (sample->samples>65280) {
|
||||
SAMPLE_WARN(warnLength,"SegaPCM: maximum sample length is 65280");
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (e->song.system[i]!=DIV_SYSTEM_PCM_DAC) {
|
||||
if (e->song.system[i]==DIV_SYSTEM_ES5506) {
|
||||
if (sample->loopMode==DIV_SAMPLE_LOOP_BACKWARD) {
|
||||
SAMPLE_WARN(warnLoopMode,"ES5506: backward loop mode isn't supported");
|
||||
}
|
||||
} else if (sample->loopMode!=DIV_SAMPLE_LOOP_FORWARD) {
|
||||
SAMPLE_WARN(warnLoopMode,"backward/ping-pong only supported in Generic PCM DAC\nping-pong also on ES5506");
|
||||
}
|
||||
}
|
||||
|
||||
// chips grid
|
||||
DivDispatch* dispatch=e->getDispatch(i);
|
||||
if (dispatch==NULL) continue;
|
||||
|
||||
|
|
@ -172,10 +292,13 @@ void FurnaceGUI::drawSampleEdit() {
|
|||
if (!dispatch->isSampleLoaded(j,curSample)) isMemWarning[j][i]=true;
|
||||
}
|
||||
}
|
||||
|
||||
int selColumns=1;
|
||||
for (int i=0; i<DIV_MAX_CHIPS; i++) {
|
||||
if (isChipVisible[i]) selColumns++;
|
||||
}
|
||||
|
||||
int targetRate=sampleCompatRate?sample->rate:sample->centerRate;
|
||||
|
||||
if (ImGui::BeginTable("SampleProps",(selColumns>1)?4:3,ImGuiTableFlags_SizingStretchSame|ImGuiTableFlags_BordersV|ImGuiTableFlags_BordersOuterH)) {
|
||||
ImGui::TableNextRow(ImGuiTableRowFlags_Headers);
|
||||
|
|
@ -201,24 +324,34 @@ void FurnaceGUI::drawSampleEdit() {
|
|||
}
|
||||
popToggleColors();
|
||||
ImGui::TableNextColumn();
|
||||
bool doLoop=(sample->isLoopable());
|
||||
bool doLoop=(sample->loop);
|
||||
pushWarningColor(!warnLoop.empty());
|
||||
if (ImGui::Checkbox("Loop",&doLoop)) { MARK_MODIFIED
|
||||
if (doLoop) {
|
||||
sample->loop=true;
|
||||
sample->loopStart=0;
|
||||
sample->loopEnd=sample->samples;
|
||||
if (sample->loopStart<0) {
|
||||
sample->loopStart=0;
|
||||
}
|
||||
if (sample->loopEnd<0) {
|
||||
sample->loopEnd=sample->samples;
|
||||
}
|
||||
} else {
|
||||
sample->loop=false;
|
||||
/*
|
||||
sample->loopStart=-1;
|
||||
sample->loopEnd=sample->samples;
|
||||
sample->loopEnd=sample->samples;*/
|
||||
}
|
||||
updateSampleTex=true;
|
||||
if (e->getSampleFormatMask()&(1U<<DIV_SAMPLE_DEPTH_BRR)) {
|
||||
e->renderSamplesP();
|
||||
}
|
||||
}
|
||||
if (ImGui::IsItemHovered() && sample->depth==DIV_SAMPLE_DEPTH_BRR) {
|
||||
ImGui::SetTooltip("changing the loop in a BRR sample may result in glitches!");
|
||||
popWarningColor();
|
||||
if (ImGui::IsItemHovered() && (!warnLoop.empty() || sample->depth==DIV_SAMPLE_DEPTH_BRR)) {
|
||||
if (sample->depth==DIV_SAMPLE_DEPTH_BRR) {
|
||||
SAMPLE_WARN(warnLoop,"changing the loop in a BRR sample may result in glitches!");
|
||||
}
|
||||
ImGui::SetTooltip("%s",warnLoop.c_str());
|
||||
}
|
||||
|
||||
if (selColumns>1) {
|
||||
|
|
@ -238,8 +371,7 @@ void FurnaceGUI::drawSampleEdit() {
|
|||
if (ImGui::Selectable(sampleDepths[i])) {
|
||||
sample->prepareUndo(true);
|
||||
e->lockEngine([this,sample,i]() {
|
||||
sample->render();
|
||||
sample->depth=(DivSampleDepth)i;
|
||||
sample->convert((DivSampleDepth)i);
|
||||
e->renderSamples();
|
||||
});
|
||||
updateSampleTex=true;
|
||||
|
|
@ -273,8 +405,20 @@ void FurnaceGUI::drawSampleEdit() {
|
|||
}
|
||||
}
|
||||
}
|
||||
if (sample->depth!=DIV_SAMPLE_DEPTH_8BIT && e->getSampleFormatMask()&(1L<<DIV_SAMPLE_DEPTH_8BIT)) {
|
||||
bool di=sample->dither;
|
||||
if (ImGui::Checkbox("8-bit dither",&di)) {
|
||||
sample->prepareUndo(true);
|
||||
sample->dither=di;
|
||||
e->renderSamplesP();
|
||||
updateSampleTex=true;
|
||||
MARK_MODIFIED;
|
||||
}
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("dither the sample when used on a chip that only supports 8-bit samples.");
|
||||
}
|
||||
}
|
||||
|
||||
int targetRate=sampleCompatRate?sample->rate:sample->centerRate;
|
||||
int sampleNote=round(64.0+(128.0*12.0*log((double)targetRate/8363.0)/log(2.0)));
|
||||
int sampleNoteCoarse=60+(sampleNote>>7);
|
||||
int sampleNoteFine=(sampleNote&127)-64;
|
||||
|
|
@ -385,6 +529,7 @@ void FurnaceGUI::drawSampleEdit() {
|
|||
keepLoopAlive=false;
|
||||
ImGui::Text("Mode");
|
||||
ImGui::SameLine();
|
||||
pushWarningColor(!warnLoopMode.empty());
|
||||
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
|
||||
if (ImGui::BeginCombo("##SampleLoopMode",loopType.c_str())) {
|
||||
for (int i=0; i<DIV_SAMPLE_LOOP_MAX; i++) {
|
||||
|
|
@ -399,7 +544,12 @@ void FurnaceGUI::drawSampleEdit() {
|
|||
}
|
||||
ImGui::EndCombo();
|
||||
}
|
||||
if (ImGui::IsItemHovered() && !warnLoopMode.empty()) {
|
||||
ImGui::SetTooltip("%s",warnLoopMode.c_str());
|
||||
}
|
||||
popWarningColor();
|
||||
|
||||
pushWarningColor(!warnLoopPos.empty());
|
||||
ImGui::Text("Start");
|
||||
ImGui::SameLine();
|
||||
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
|
||||
|
|
@ -418,8 +568,11 @@ void FurnaceGUI::drawSampleEdit() {
|
|||
if (ImGui::IsItemActive()) {
|
||||
keepLoopAlive=true;
|
||||
}
|
||||
if (ImGui::IsItemHovered() && sample->depth==DIV_SAMPLE_DEPTH_BRR) {
|
||||
ImGui::SetTooltip("changing the loop in a BRR sample may result in glitches!");
|
||||
if (ImGui::IsItemHovered() && (!warnLoopPos.empty() || sample->depth==DIV_SAMPLE_DEPTH_BRR)) {
|
||||
if (sample->depth==DIV_SAMPLE_DEPTH_BRR) {
|
||||
SAMPLE_WARN(warnLoopPos,"changing the loop in a BRR sample may result in glitches!");
|
||||
}
|
||||
ImGui::SetTooltip("%s",warnLoopPos.c_str());
|
||||
}
|
||||
|
||||
ImGui::Text("End");
|
||||
|
|
@ -440,9 +593,13 @@ void FurnaceGUI::drawSampleEdit() {
|
|||
if (ImGui::IsItemActive()) {
|
||||
keepLoopAlive=true;
|
||||
}
|
||||
if (ImGui::IsItemHovered() && sample->depth==DIV_SAMPLE_DEPTH_BRR) {
|
||||
ImGui::SetTooltip("changing the loop in a BRR sample may result in glitches!");
|
||||
if (ImGui::IsItemHovered() && (!warnLoopPos.empty() || sample->depth==DIV_SAMPLE_DEPTH_BRR)) {
|
||||
if (sample->depth==DIV_SAMPLE_DEPTH_BRR) {
|
||||
SAMPLE_WARN(warnLoopPos,"changing the loop in a BRR sample may result in glitches!");
|
||||
}
|
||||
ImGui::SetTooltip("%s",warnLoopPos.c_str());
|
||||
}
|
||||
popWarningColor();
|
||||
ImGui::EndDisabled();
|
||||
|
||||
if (selColumns>1) {
|
||||
|
|
@ -608,7 +765,7 @@ void FurnaceGUI::drawSampleEdit() {
|
|||
ImGui::SameLine();
|
||||
ImGui::Button(ICON_FA_EXPAND "##SResample");
|
||||
if (ImGui::IsItemClicked()) {
|
||||
resampleTarget=sample->rate;
|
||||
resampleTarget=targetRate;
|
||||
}
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("Resample");
|
||||
|
|
@ -629,23 +786,23 @@ void FurnaceGUI::drawSampleEdit() {
|
|||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("==")) {
|
||||
resampleTarget=sample->rate;
|
||||
resampleTarget=targetRate;
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("2.0x")) {
|
||||
resampleTarget*=2.0;
|
||||
}
|
||||
double factor=resampleTarget/(double)sample->rate;
|
||||
double factor=resampleTarget/(double)targetRate;
|
||||
if (ImGui::InputDouble("Factor",&factor,0.125,0.5,"%g")) {
|
||||
resampleTarget=(double)sample->rate*factor;
|
||||
resampleTarget=(double)targetRate*factor;
|
||||
if (resampleTarget<0) resampleTarget=0;
|
||||
if (resampleTarget>96000) resampleTarget=96000;
|
||||
}
|
||||
ImGui::Combo("Filter",&resampleStrat,resampleStrats,6);
|
||||
if (ImGui::Button("Resample")) {
|
||||
sample->prepareUndo(true);
|
||||
e->lockEngine([this,sample]() {
|
||||
if (!sample->resample(resampleTarget,resampleStrat)) {
|
||||
e->lockEngine([this,sample,targetRate]() {
|
||||
if (!sample->resample(targetRate,resampleTarget,resampleStrat)) {
|
||||
showError("couldn't resample! make sure your sample is 8 or 16-bit.");
|
||||
}
|
||||
e->renderSamples();
|
||||
|
|
@ -658,7 +815,7 @@ void FurnaceGUI::drawSampleEdit() {
|
|||
}
|
||||
ImGui::EndPopup();
|
||||
} else {
|
||||
resampleTarget=sample->rate;
|
||||
resampleTarget=targetRate;
|
||||
}
|
||||
ImGui::SameLine();
|
||||
ImGui::Dummy(ImVec2(4.0*dpiScale,dpiScale));
|
||||
|
|
@ -1093,12 +1250,12 @@ void FurnaceGUI::drawSampleEdit() {
|
|||
|
||||
if (sampleTex==NULL || sampleTexW!=avail.x || sampleTexH!=avail.y) {
|
||||
if (sampleTex!=NULL) {
|
||||
SDL_DestroyTexture(sampleTex);
|
||||
rend->destroyTexture(sampleTex);
|
||||
sampleTex=NULL;
|
||||
}
|
||||
if (avail.x>=1 && avail.y>=1) {
|
||||
logD("recreating sample texture.");
|
||||
sampleTex=SDL_CreateTexture(sdlRend,SDL_PIXELFORMAT_ABGR8888,SDL_TEXTUREACCESS_STREAMING,avail.x,avail.y);
|
||||
sampleTex=rend->createTexture(true,avail.x,avail.y);
|
||||
sampleTexW=avail.x;
|
||||
sampleTexH=avail.y;
|
||||
if (sampleTex==NULL) {
|
||||
|
|
@ -1114,7 +1271,7 @@ void FurnaceGUI::drawSampleEdit() {
|
|||
unsigned int* dataT=NULL;
|
||||
int pitch=0;
|
||||
logD("updating sample texture.");
|
||||
if (SDL_LockTexture(sampleTex,NULL,(void**)&dataT,&pitch)!=0) {
|
||||
if (!rend->lockTexture(sampleTex,(void**)&dataT,&pitch)) {
|
||||
logE("error while locking sample texture! %s",SDL_GetError());
|
||||
} else {
|
||||
unsigned int* data=new unsigned int[sampleTexW*sampleTexH];
|
||||
|
|
@ -1199,19 +1356,54 @@ void FurnaceGUI::drawSampleEdit() {
|
|||
}
|
||||
}
|
||||
|
||||
memcpy(dataT,data,sampleTexW*sampleTexH*sizeof(unsigned int));
|
||||
SDL_UnlockTexture(sampleTex);
|
||||
if ((pitch>>2)==sampleTexW) {
|
||||
memcpy(dataT,data,sampleTexW*sampleTexH*sizeof(unsigned int));
|
||||
} else {
|
||||
int srcY=0;
|
||||
int destY=0;
|
||||
for (int i=0; i<sampleTexH; i++) {
|
||||
memcpy(&dataT[destY],&data[srcY],sampleTexW*sizeof(unsigned int));
|
||||
srcY+=sampleTexW;
|
||||
destY+=pitch>>2;
|
||||
}
|
||||
}
|
||||
rend->unlockTexture(sampleTex);
|
||||
delete[] data;
|
||||
}
|
||||
updateSampleTex=false;
|
||||
}
|
||||
|
||||
ImGui::ImageButton(sampleTex,avail,ImVec2(0,0),ImVec2(1,1),0);
|
||||
ImGui::ImageButton(rend->getTextureID(sampleTex),avail,ImVec2(0,0),ImVec2(1,1),0);
|
||||
|
||||
ImVec2 rectMin=ImGui::GetItemRectMin();
|
||||
ImVec2 rectMax=ImGui::GetItemRectMax();
|
||||
ImVec2 rectSize=ImGui::GetItemRectSize();
|
||||
|
||||
unsigned char selectTarget=255;
|
||||
|
||||
if (ImGui::IsItemHovered()) {
|
||||
int start=sampleSelStart;
|
||||
int end=sampleSelEnd;
|
||||
if (start>end) {
|
||||
start^=end;
|
||||
end^=start;
|
||||
start^=end;
|
||||
}
|
||||
ImVec2 p1=rectMin;
|
||||
p1.x+=(start-samplePos)/sampleZoom;
|
||||
|
||||
ImVec2 p2=ImVec2(rectMin.x+(end-samplePos)/sampleZoom,rectMax.y);
|
||||
|
||||
ImVec2 mousePos=ImGui::GetMousePos();
|
||||
if (p1.x>=rectMin.x && p1.x<=rectMax.x && fabs(mousePos.x-p1.x)<2.0*dpiScale) {
|
||||
ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeEW);
|
||||
selectTarget=0;
|
||||
} else if (p2.x>=rectMin.x && p2.x<=rectMax.x && fabs(mousePos.x-p2.x)<2.0*dpiScale) {
|
||||
ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeEW);
|
||||
selectTarget=1;
|
||||
}
|
||||
}
|
||||
|
||||
if (ImGui::IsItemClicked()) {
|
||||
nextWindow=GUI_WINDOW_SAMPLE_EDIT;
|
||||
if (ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left)) {
|
||||
|
|
@ -1238,9 +1430,23 @@ void FurnaceGUI::drawSampleEdit() {
|
|||
}
|
||||
sampleDragLen=sample->samples;
|
||||
sampleDragActive=true;
|
||||
sampleSelStart=-1;
|
||||
sampleSelEnd=-1;
|
||||
if (sampleDragMode) sample->prepareUndo(true);
|
||||
if (!sampleDragMode) {
|
||||
switch (selectTarget) {
|
||||
case 0:
|
||||
sampleSelStart^=sampleSelEnd;
|
||||
sampleSelEnd^=sampleSelStart;
|
||||
sampleSelStart^=sampleSelEnd;
|
||||
break;
|
||||
case 1:
|
||||
break;
|
||||
default:
|
||||
sampleSelStart=-1;
|
||||
sampleSelEnd=-1;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
sample->prepareUndo(true);
|
||||
}
|
||||
processDrags(ImGui::GetMousePos().x,ImGui::GetMousePos().y);
|
||||
}
|
||||
}
|
||||
|
|
@ -1529,7 +1735,16 @@ void FurnaceGUI::drawSampleEdit() {
|
|||
ImGui::TableNextColumn();
|
||||
ImGui::TextUnformatted(statusBar2.c_str());
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextUnformatted(statusBar3.c_str());
|
||||
if (!warnLength.empty()) {
|
||||
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_WARNING]);
|
||||
ImGui::TextUnformatted(statusBar3.c_str());
|
||||
ImGui::PopStyleColor();
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("%s",warnLength.c_str());
|
||||
}
|
||||
} else {
|
||||
ImGui::TextUnformatted(statusBar3.c_str());
|
||||
}
|
||||
|
||||
ImGui::EndTable();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -508,6 +508,14 @@ void FurnaceGUI::drawSettings() {
|
|||
settings.alwaysPlayIntro=3;
|
||||
}
|
||||
|
||||
ImGui::Text("When creating new song:");
|
||||
if (ImGui::RadioButton("Display system preset selector##NSB0",settings.newSongBehavior==0)) {
|
||||
settings.newSongBehavior=0;
|
||||
}
|
||||
if (ImGui::RadioButton("Start with initial system##NSB1",settings.newSongBehavior==1)) {
|
||||
settings.newSongBehavior=1;
|
||||
}
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
if (CWSliderFloat("Double-click time (seconds)",&settings.doubleClickTime,0.02,1.0,"%.2f")) {
|
||||
|
|
@ -543,6 +551,16 @@ void FurnaceGUI::drawSettings() {
|
|||
settings.stepOnDelete=stepOnDeleteB;
|
||||
}
|
||||
|
||||
bool insertBehaviorB=settings.insertBehavior;
|
||||
if (ImGui::Checkbox("Insert pushes entire channel row",&insertBehaviorB)) {
|
||||
settings.insertBehavior=insertBehaviorB;
|
||||
}
|
||||
|
||||
bool pullDeleteRowB=settings.pullDeleteRow;
|
||||
if (ImGui::Checkbox("Pull delete affects entire channel row",&pullDeleteRowB)) {
|
||||
settings.pullDeleteRow=pullDeleteRowB;
|
||||
}
|
||||
|
||||
bool absorbInsInputB=settings.absorbInsInput;
|
||||
if (ImGui::Checkbox("Change current instrument when changing instrument column (absorb)",&absorbInsInputB)) {
|
||||
settings.absorbInsInput=absorbInsInputB;
|
||||
|
|
@ -626,6 +644,14 @@ void FurnaceGUI::drawSettings() {
|
|||
ImGui::SetTooltip("saves power by lowering the frame rate to 2fps when idle.\nmay cause issues under Mesa drivers!");
|
||||
}
|
||||
|
||||
bool renderClearPosB=settings.renderClearPos;
|
||||
if (ImGui::Checkbox("Late render clear",&renderClearPosB)) {
|
||||
settings.renderClearPos=renderClearPosB;
|
||||
}
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("calls rend->clear() after rend->present(). might reduce UI latency by one frame in some drivers.");
|
||||
}
|
||||
|
||||
#ifndef IS_MOBILE
|
||||
bool noThreadedInputB=settings.noThreadedInput;
|
||||
if (ImGui::Checkbox("Disable threaded input (restart after changing!)",&noThreadedInputB)) {
|
||||
|
|
@ -654,6 +680,22 @@ void FurnaceGUI::drawSettings() {
|
|||
settings.saveUnusedPatterns=saveUnusedPatternsB;
|
||||
}
|
||||
|
||||
bool compressB=settings.compress;
|
||||
if (ImGui::Checkbox("Compress when saving",&compressB)) {
|
||||
settings.compress=compressB;
|
||||
}
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("use zlib to compress saved songs.");
|
||||
}
|
||||
|
||||
bool newPatternFormatB=settings.newPatternFormat;
|
||||
if (ImGui::Checkbox("Use new pattern format when saving",&newPatternFormatB)) {
|
||||
settings.newPatternFormat=newPatternFormatB;
|
||||
}
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("use a packed format which saves space when saving songs.\ndisable if you need compatibility with older Furnace and/or tools\nwhich do not support this format.");
|
||||
}
|
||||
|
||||
bool cursorFollowsOrderB=settings.cursorFollowsOrder;
|
||||
if (ImGui::Checkbox("Cursor follows current order when moving it",&cursorFollowsOrderB)) {
|
||||
settings.cursorFollowsOrder=cursorFollowsOrderB;
|
||||
|
|
@ -1146,14 +1188,38 @@ void FurnaceGUI::drawSettings() {
|
|||
settings.midiOutMode=2;
|
||||
}*/
|
||||
|
||||
bool midiOutProgramChangeB=settings.midiOutProgramChange;
|
||||
if (ImGui::Checkbox("Send Program Change",&midiOutProgramChangeB)) {
|
||||
settings.midiOutProgramChange=midiOutProgramChangeB;
|
||||
}
|
||||
|
||||
bool midiOutClockB=settings.midiOutClock;
|
||||
if (ImGui::Checkbox("Send MIDI clock",&midiOutClockB)) {
|
||||
settings.midiOutClock=midiOutClockB;
|
||||
}
|
||||
|
||||
bool midiOutProgramChangeB=settings.midiOutProgramChange;
|
||||
if (ImGui::Checkbox("Send Program Change",&midiOutProgramChangeB)) {
|
||||
settings.midiOutProgramChange=midiOutProgramChangeB;
|
||||
bool midiOutTimeB=settings.midiOutTime;
|
||||
if (ImGui::Checkbox("Send MIDI timecode",&midiOutTimeB)) {
|
||||
settings.midiOutTime=midiOutTimeB;
|
||||
}
|
||||
|
||||
if (settings.midiOutTime) {
|
||||
ImGui::Text("Timecode frame rate:");
|
||||
if (ImGui::RadioButton("Closest to Tick Rate",settings.midiOutTimeRate==0)) {
|
||||
settings.midiOutTimeRate=0;
|
||||
}
|
||||
if (ImGui::RadioButton("Film (24fps)",settings.midiOutTimeRate==1)) {
|
||||
settings.midiOutTimeRate=1;
|
||||
}
|
||||
if (ImGui::RadioButton("PAL (25fps)",settings.midiOutTimeRate==2)) {
|
||||
settings.midiOutTimeRate=2;
|
||||
}
|
||||
if (ImGui::RadioButton("NTSC drop (29.97fps)",settings.midiOutTimeRate==3)) {
|
||||
settings.midiOutTimeRate=3;
|
||||
}
|
||||
if (ImGui::RadioButton("NTSC non-drop (30fps)",settings.midiOutTimeRate==4)) {
|
||||
settings.midiOutTimeRate=4;
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::TreePop();
|
||||
|
|
@ -1238,17 +1304,38 @@ void FurnaceGUI::drawSettings() {
|
|||
ImVec2 settingsViewSize=ImGui::GetContentRegionAvail();
|
||||
settingsViewSize.y-=ImGui::GetFrameHeight()+ImGui::GetStyle().WindowPadding.y;
|
||||
if (ImGui::BeginChild("SettingsView",settingsViewSize)) {
|
||||
if (ImGui::BeginCombo("Render driver",settings.renderDriver.empty()?"Automatic":settings.renderDriver.c_str())) {
|
||||
if (ImGui::Selectable("Automatic",settings.renderDriver.empty())) {
|
||||
settings.renderDriver="";
|
||||
String curRenderBackend=settings.renderBackend.empty()?GUI_BACKEND_DEFAULT_NAME:settings.renderBackend;
|
||||
if (ImGui::BeginCombo("Render backend",curRenderBackend.c_str())) {
|
||||
#ifdef HAVE_RENDER_SDL
|
||||
if (ImGui::Selectable("SDL Renderer",curRenderBackend=="SDL")) {
|
||||
settings.renderBackend="SDL";
|
||||
}
|
||||
for (String& i: availRenderDrivers) {
|
||||
if (ImGui::Selectable(i.c_str(),i==settings.renderDriver)) {
|
||||
settings.renderDriver=i;
|
||||
}
|
||||
#endif
|
||||
#ifdef HAVE_RENDER_DX11
|
||||
if (ImGui::Selectable("DirectX 11",curRenderBackend=="DirectX 11")) {
|
||||
settings.renderBackend="DirectX 11";
|
||||
}
|
||||
#endif
|
||||
#ifdef HAVE_RENDER_GL
|
||||
if (ImGui::Selectable("OpenGL",curRenderBackend=="OpenGL")) {
|
||||
settings.renderBackend="OpenGL";
|
||||
}
|
||||
#endif
|
||||
ImGui::EndCombo();
|
||||
}
|
||||
if (curRenderBackend=="SDL") {
|
||||
if (ImGui::BeginCombo("Render driver",settings.renderDriver.empty()?"Automatic":settings.renderDriver.c_str())) {
|
||||
if (ImGui::Selectable("Automatic",settings.renderDriver.empty())) {
|
||||
settings.renderDriver="";
|
||||
}
|
||||
for (String& i: availRenderDrivers) {
|
||||
if (ImGui::Selectable(i.c_str(),i==settings.renderDriver)) {
|
||||
settings.renderDriver=i;
|
||||
}
|
||||
}
|
||||
ImGui::EndCombo();
|
||||
}
|
||||
}
|
||||
|
||||
bool dpiScaleAuto=(settings.dpiScale<0.5f);
|
||||
if (ImGui::Checkbox("Automatic UI scaling factor",&dpiScaleAuto)) {
|
||||
|
|
@ -1811,18 +1898,41 @@ void FurnaceGUI::drawSettings() {
|
|||
}
|
||||
UI_COLOR_CONFIG(GUI_COLOR_BACKGROUND,"Background");
|
||||
UI_COLOR_CONFIG(GUI_COLOR_FRAME_BACKGROUND,"Window background");
|
||||
UI_COLOR_CONFIG(GUI_COLOR_FRAME_BACKGROUND_CHILD,"Sub-window background");
|
||||
UI_COLOR_CONFIG(GUI_COLOR_FRAME_BACKGROUND_POPUP,"Pop-up background");
|
||||
UI_COLOR_CONFIG(GUI_COLOR_MODAL_BACKDROP,"Modal backdrop");
|
||||
UI_COLOR_CONFIG(GUI_COLOR_HEADER,"Header");
|
||||
UI_COLOR_CONFIG(GUI_COLOR_TEXT,"Text");
|
||||
UI_COLOR_CONFIG(GUI_COLOR_ACCENT_PRIMARY,"Primary");
|
||||
UI_COLOR_CONFIG(GUI_COLOR_ACCENT_SECONDARY,"Secondary");
|
||||
UI_COLOR_CONFIG(GUI_COLOR_TITLE_INACTIVE,"Title bar (inactive)");
|
||||
UI_COLOR_CONFIG(GUI_COLOR_TITLE_COLLAPSED,"Title bar (collapsed)");
|
||||
UI_COLOR_CONFIG(GUI_COLOR_MENU_BAR,"Menu bar");
|
||||
UI_COLOR_CONFIG(GUI_COLOR_BORDER,"Border");
|
||||
UI_COLOR_CONFIG(GUI_COLOR_BORDER_SHADOW,"Border shadow");
|
||||
UI_COLOR_CONFIG(GUI_COLOR_SCROLL,"Scroll bar");
|
||||
UI_COLOR_CONFIG(GUI_COLOR_SCROLL_HOVER,"Scroll bar (hovered)");
|
||||
UI_COLOR_CONFIG(GUI_COLOR_SCROLL_ACTIVE,"Scroll bar (clicked)");
|
||||
UI_COLOR_CONFIG(GUI_COLOR_SCROLL_BACKGROUND,"Scroll bar background");
|
||||
UI_COLOR_CONFIG(GUI_COLOR_SEPARATOR,"Separator");
|
||||
UI_COLOR_CONFIG(GUI_COLOR_SEPARATOR_HOVER,"Separator (hover)");
|
||||
UI_COLOR_CONFIG(GUI_COLOR_SEPARATOR_ACTIVE,"Separator (active)");
|
||||
UI_COLOR_CONFIG(GUI_COLOR_DOCKING_PREVIEW,"Docking preview");
|
||||
UI_COLOR_CONFIG(GUI_COLOR_DOCKING_EMPTY,"Docking empty");
|
||||
UI_COLOR_CONFIG(GUI_COLOR_TABLE_HEADER,"Table header");
|
||||
UI_COLOR_CONFIG(GUI_COLOR_TABLE_BORDER_HARD,"Table border (hard)");
|
||||
UI_COLOR_CONFIG(GUI_COLOR_TABLE_BORDER_SOFT,"Table border (soft)");
|
||||
UI_COLOR_CONFIG(GUI_COLOR_DRAG_DROP_TARGET,"Drag and drop target");
|
||||
UI_COLOR_CONFIG(GUI_COLOR_NAV_WIN_HIGHLIGHT,"Window switcher (highlight)");
|
||||
UI_COLOR_CONFIG(GUI_COLOR_NAV_WIN_BACKDROP,"Window switcher backdrop");
|
||||
UI_COLOR_CONFIG(GUI_COLOR_TOGGLE_ON,"Toggle on");
|
||||
UI_COLOR_CONFIG(GUI_COLOR_TOGGLE_OFF,"Toggle off");
|
||||
UI_COLOR_CONFIG(GUI_COLOR_EDITING,"Editing");
|
||||
UI_COLOR_CONFIG(GUI_COLOR_SONG_LOOP,"Song loop");
|
||||
UI_COLOR_CONFIG(GUI_COLOR_PLAYBACK_STAT,"Playback status");
|
||||
UI_COLOR_CONFIG(GUI_COLOR_DESTRUCTIVE,"Destructive hint");
|
||||
UI_COLOR_CONFIG(GUI_COLOR_WARNING,"Warning hint");
|
||||
UI_COLOR_CONFIG(GUI_COLOR_ERROR,"Error hint");
|
||||
ImGui::TreePop();
|
||||
}
|
||||
if (ImGui::TreeNode("File Picker (built-in)")) {
|
||||
|
|
@ -2327,6 +2437,7 @@ void FurnaceGUI::drawSettings() {
|
|||
UI_KEYBIND_CONFIG(GUI_ACTION_INS_LIST_EDIT);
|
||||
UI_KEYBIND_CONFIG(GUI_ACTION_INS_LIST_UP);
|
||||
UI_KEYBIND_CONFIG(GUI_ACTION_INS_LIST_DOWN);
|
||||
UI_KEYBIND_CONFIG(GUI_ACTION_INS_LIST_DIR_VIEW);
|
||||
|
||||
KEYBIND_CONFIG_END;
|
||||
ImGui::TreePop();
|
||||
|
|
@ -2344,6 +2455,7 @@ void FurnaceGUI::drawSettings() {
|
|||
UI_KEYBIND_CONFIG(GUI_ACTION_WAVE_LIST_EDIT);
|
||||
UI_KEYBIND_CONFIG(GUI_ACTION_WAVE_LIST_UP);
|
||||
UI_KEYBIND_CONFIG(GUI_ACTION_WAVE_LIST_DOWN);
|
||||
UI_KEYBIND_CONFIG(GUI_ACTION_WAVE_LIST_DIR_VIEW);
|
||||
|
||||
KEYBIND_CONFIG_END;
|
||||
ImGui::TreePop();
|
||||
|
|
@ -2363,6 +2475,7 @@ void FurnaceGUI::drawSettings() {
|
|||
UI_KEYBIND_CONFIG(GUI_ACTION_SAMPLE_LIST_DOWN);
|
||||
UI_KEYBIND_CONFIG(GUI_ACTION_SAMPLE_LIST_PREVIEW);
|
||||
UI_KEYBIND_CONFIG(GUI_ACTION_SAMPLE_LIST_STOP_PREVIEW);
|
||||
UI_KEYBIND_CONFIG(GUI_ACTION_SAMPLE_LIST_DIR_VIEW);
|
||||
|
||||
KEYBIND_CONFIG_END;
|
||||
ImGui::TreePop();
|
||||
|
|
@ -2629,8 +2742,10 @@ void FurnaceGUI::syncSettings() {
|
|||
settings.channelTextCenter=e->getConfInt("channelTextCenter",1);
|
||||
settings.maxRecentFile=e->getConfInt("maxRecentFile",10);
|
||||
settings.midiOutClock=e->getConfInt("midiOutClock",0);
|
||||
settings.midiOutTime=e->getConfInt("midiOutTime",0);
|
||||
settings.midiOutProgramChange=e->getConfInt("midiOutProgramChange",0);
|
||||
settings.midiOutMode=e->getConfInt("midiOutMode",1);
|
||||
settings.midiOutTimeRate=e->getConfInt("midiOutTimeRate",0);
|
||||
settings.centerPattern=e->getConfInt("centerPattern",0);
|
||||
settings.ordersCursor=e->getConfInt("ordersCursor",1);
|
||||
settings.persistFadeOut=e->getConfInt("persistFadeOut",1);
|
||||
|
|
@ -2644,6 +2759,13 @@ void FurnaceGUI::syncSettings() {
|
|||
settings.cursorFollowsOrder=e->getConfInt("cursorFollowsOrder",1);
|
||||
settings.iCannotWait=e->getConfInt("iCannotWait",0);
|
||||
settings.orderButtonPos=e->getConfInt("orderButtonPos",2);
|
||||
settings.compress=e->getConfInt("compress",1);
|
||||
settings.newPatternFormat=e->getConfInt("newPatternFormat",1);
|
||||
settings.renderBackend=e->getConfString("renderBackend","SDL");
|
||||
settings.renderClearPos=e->getConfInt("renderClearPos",0);
|
||||
settings.insertBehavior=e->getConfInt("insertBehavior",1);
|
||||
settings.pullDeleteRow=e->getConfInt("pullDeleteRow",1);
|
||||
settings.newSongBehavior=e->getConfInt("newSongBehavior",0);
|
||||
|
||||
clampSetting(settings.mainFontSize,2,96);
|
||||
clampSetting(settings.patFontSize,2,96);
|
||||
|
|
@ -2749,8 +2871,10 @@ void FurnaceGUI::syncSettings() {
|
|||
clampSetting(settings.channelTextCenter,0,1);
|
||||
clampSetting(settings.maxRecentFile,0,30);
|
||||
clampSetting(settings.midiOutClock,0,1);
|
||||
clampSetting(settings.midiOutTime,0,1);
|
||||
clampSetting(settings.midiOutProgramChange,0,1);
|
||||
clampSetting(settings.midiOutMode,0,2);
|
||||
clampSetting(settings.midiOutTimeRate,0,4);
|
||||
clampSetting(settings.centerPattern,0,1);
|
||||
clampSetting(settings.ordersCursor,0,1);
|
||||
clampSetting(settings.persistFadeOut,0,1);
|
||||
|
|
@ -2762,6 +2886,12 @@ void FurnaceGUI::syncSettings() {
|
|||
clampSetting(settings.cursorFollowsOrder,0,1);
|
||||
clampSetting(settings.iCannotWait,0,1);
|
||||
clampSetting(settings.orderButtonPos,0,2);
|
||||
clampSetting(settings.compress,0,1);
|
||||
clampSetting(settings.newPatternFormat,0,1);
|
||||
clampSetting(settings.renderClearPos,0,1);
|
||||
clampSetting(settings.insertBehavior,0,1);
|
||||
clampSetting(settings.pullDeleteRow,0,1);
|
||||
clampSetting(settings.newSongBehavior,0,1);
|
||||
|
||||
if (settings.exportLoops<0.0) settings.exportLoops=0.0;
|
||||
if (settings.exportFadeOut<0.0) settings.exportFadeOut=0.0;
|
||||
|
|
@ -2960,8 +3090,10 @@ void FurnaceGUI::commitSettings() {
|
|||
e->setConf("channelTextCenter",settings.channelTextCenter);
|
||||
e->setConf("maxRecentFile",settings.maxRecentFile);
|
||||
e->setConf("midiOutClock",settings.midiOutClock);
|
||||
e->setConf("midiOutTime",settings.midiOutTime);
|
||||
e->setConf("midiOutProgramChange",settings.midiOutProgramChange);
|
||||
e->setConf("midiOutMode",settings.midiOutMode);
|
||||
e->setConf("midiOutTimeRate",settings.midiOutTimeRate);
|
||||
e->setConf("centerPattern",settings.centerPattern);
|
||||
e->setConf("ordersCursor",settings.ordersCursor);
|
||||
e->setConf("persistFadeOut",settings.persistFadeOut);
|
||||
|
|
@ -2975,6 +3107,13 @@ void FurnaceGUI::commitSettings() {
|
|||
e->setConf("cursorFollowsOrder",settings.cursorFollowsOrder);
|
||||
e->setConf("iCannotWait",settings.iCannotWait);
|
||||
e->setConf("orderButtonPos",settings.orderButtonPos);
|
||||
e->setConf("compress",settings.compress);
|
||||
e->setConf("newPatternFormat",settings.newPatternFormat);
|
||||
e->setConf("renderBackend",settings.renderBackend);
|
||||
e->setConf("renderClearPos",settings.renderClearPos);
|
||||
e->setConf("insertBehavior",settings.insertBehavior);
|
||||
e->setConf("pullDeleteRow",settings.pullDeleteRow);
|
||||
e->setConf("newSongBehavior",settings.newSongBehavior);
|
||||
|
||||
// colors
|
||||
for (int i=0; i<GUI_COLOR_MAX; i++) {
|
||||
|
|
@ -3014,17 +3153,21 @@ void FurnaceGUI::commitSettings() {
|
|||
|
||||
applyUISettings();
|
||||
|
||||
ImGui_ImplSDLRenderer_DestroyFontsTexture();
|
||||
if (rend) rend->destroyFontsTexture();
|
||||
if (!ImGui::GetIO().Fonts->Build()) {
|
||||
logE("error while building font atlas!");
|
||||
showError("error while loading fonts! please check your settings.");
|
||||
ImGui::GetIO().Fonts->Clear();
|
||||
mainFont=ImGui::GetIO().Fonts->AddFontDefault();
|
||||
patFont=mainFont;
|
||||
ImGui_ImplSDLRenderer_DestroyFontsTexture();
|
||||
if (rend) rend->destroyFontsTexture();
|
||||
if (!ImGui::GetIO().Fonts->Build()) {
|
||||
logE("error again while building font atlas!");
|
||||
} else {
|
||||
rend->createFontsTexture();
|
||||
}
|
||||
} else {
|
||||
rend->createFontsTexture();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -3363,6 +3506,35 @@ void FurnaceGUI::popAccentColors() {
|
|||
ImGui::PopStyleColor(24);
|
||||
}
|
||||
|
||||
void FurnaceGUI::pushDestColor() {
|
||||
pushAccentColors(uiColors[GUI_COLOR_DESTRUCTIVE],uiColors[GUI_COLOR_DESTRUCTIVE],uiColors[GUI_COLOR_DESTRUCTIVE],ImVec4(0.0f,0.0f,0.0f,0.0f));
|
||||
}
|
||||
|
||||
void FurnaceGUI::popDestColor() {
|
||||
popAccentColors();
|
||||
}
|
||||
|
||||
void FurnaceGUI::pushWarningColor(bool warnCond, bool errorCond) {
|
||||
if (warnColorPushed) {
|
||||
logE("warnColorPushed");
|
||||
abort();
|
||||
}
|
||||
if (errorCond) {
|
||||
pushAccentColors(uiColors[GUI_COLOR_ERROR],uiColors[GUI_COLOR_ERROR],uiColors[GUI_COLOR_ERROR],ImVec4(0.0f,0.0f,0.0f,0.0f));
|
||||
warnColorPushed=true;
|
||||
} else if (warnCond) {
|
||||
pushAccentColors(uiColors[GUI_COLOR_WARNING],uiColors[GUI_COLOR_WARNING],uiColors[GUI_COLOR_WARNING],ImVec4(0.0f,0.0f,0.0f,0.0f));
|
||||
warnColorPushed=true;
|
||||
}
|
||||
}
|
||||
|
||||
void FurnaceGUI::popWarningColor() {
|
||||
if (warnColorPushed) {
|
||||
popAccentColors();
|
||||
warnColorPushed=false;
|
||||
}
|
||||
}
|
||||
|
||||
#define IGFD_FileStyleByExtension IGFD_FileStyleByExtention
|
||||
|
||||
#ifdef _WIN32
|
||||
|
|
@ -3493,7 +3665,28 @@ void FurnaceGUI::applyUISettings(bool updateFonts) {
|
|||
}
|
||||
|
||||
sty.Colors[ImGuiCol_WindowBg]=uiColors[GUI_COLOR_FRAME_BACKGROUND];
|
||||
sty.Colors[ImGuiCol_ChildBg]=uiColors[GUI_COLOR_FRAME_BACKGROUND_CHILD];
|
||||
sty.Colors[ImGuiCol_PopupBg]=uiColors[GUI_COLOR_FRAME_BACKGROUND_POPUP];
|
||||
sty.Colors[ImGuiCol_TitleBg]=uiColors[GUI_COLOR_TITLE_INACTIVE];
|
||||
sty.Colors[ImGuiCol_TitleBgCollapsed]=uiColors[GUI_COLOR_TITLE_COLLAPSED];
|
||||
sty.Colors[ImGuiCol_MenuBarBg]=uiColors[GUI_COLOR_MENU_BAR];
|
||||
sty.Colors[ImGuiCol_ModalWindowDimBg]=uiColors[GUI_COLOR_MODAL_BACKDROP];
|
||||
sty.Colors[ImGuiCol_ScrollbarBg]=uiColors[GUI_COLOR_SCROLL_BACKGROUND];
|
||||
sty.Colors[ImGuiCol_ScrollbarGrab]=uiColors[GUI_COLOR_SCROLL];
|
||||
sty.Colors[ImGuiCol_ScrollbarGrabHovered]=uiColors[GUI_COLOR_SCROLL_HOVER];
|
||||
sty.Colors[ImGuiCol_ScrollbarGrabActive]=uiColors[GUI_COLOR_SCROLL_ACTIVE];
|
||||
sty.Colors[ImGuiCol_Separator]=uiColors[GUI_COLOR_SEPARATOR];
|
||||
sty.Colors[ImGuiCol_SeparatorHovered]=uiColors[GUI_COLOR_SEPARATOR_HOVER];
|
||||
sty.Colors[ImGuiCol_SeparatorActive]=uiColors[GUI_COLOR_SEPARATOR_ACTIVE];
|
||||
sty.Colors[ImGuiCol_DockingPreview]=uiColors[GUI_COLOR_DOCKING_PREVIEW];
|
||||
sty.Colors[ImGuiCol_DockingEmptyBg]=uiColors[GUI_COLOR_DOCKING_EMPTY];
|
||||
sty.Colors[ImGuiCol_TableHeaderBg]=uiColors[GUI_COLOR_TABLE_HEADER];
|
||||
sty.Colors[ImGuiCol_TableBorderStrong]=uiColors[GUI_COLOR_TABLE_BORDER_HARD];
|
||||
sty.Colors[ImGuiCol_TableBorderLight]=uiColors[GUI_COLOR_TABLE_BORDER_SOFT];
|
||||
sty.Colors[ImGuiCol_DragDropTarget]=uiColors[GUI_COLOR_DRAG_DROP_TARGET];
|
||||
sty.Colors[ImGuiCol_NavHighlight]=uiColors[GUI_COLOR_NAV_HIGHLIGHT];
|
||||
sty.Colors[ImGuiCol_NavWindowingHighlight]=uiColors[GUI_COLOR_NAV_WIN_HIGHLIGHT];
|
||||
sty.Colors[ImGuiCol_NavWindowingDimBg]=uiColors[GUI_COLOR_NAV_WIN_BACKDROP];
|
||||
sty.Colors[ImGuiCol_Text]=uiColors[GUI_COLOR_TEXT];
|
||||
|
||||
sty.Colors[ImGuiCol_Button]=primary;
|
||||
|
|
@ -3709,7 +3902,8 @@ void FurnaceGUI::applyUISettings(bool updateFonts) {
|
|||
}
|
||||
|
||||
mainFont->FallbackChar='?';
|
||||
mainFont->DotChar='.';
|
||||
mainFont->EllipsisChar='.';
|
||||
mainFont->EllipsisCharCount=3;
|
||||
}
|
||||
|
||||
// TODO: allow changing these colors.
|
||||
|
|
@ -3731,6 +3925,10 @@ void FurnaceGUI::applyUISettings(bool updateFonts) {
|
|||
ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".ttc",uiColors[GUI_COLOR_FILE_FONT],ICON_FA_FONT);
|
||||
|
||||
ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".mod",uiColors[GUI_COLOR_FILE_SONG_IMPORT],ICON_FA_FILE);
|
||||
ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".fc13",uiColors[GUI_COLOR_FILE_SONG_IMPORT],ICON_FA_FILE);
|
||||
ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".fc14",uiColors[GUI_COLOR_FILE_SONG_IMPORT],ICON_FA_FILE);
|
||||
ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".fc",uiColors[GUI_COLOR_FILE_SONG_IMPORT],ICON_FA_FILE);
|
||||
ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".smod",uiColors[GUI_COLOR_FILE_SONG_IMPORT],ICON_FA_FILE);
|
||||
ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".ftm",uiColors[GUI_COLOR_FILE_SONG_IMPORT],ICON_FA_FILE);
|
||||
|
||||
ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".tfi",uiColors[GUI_COLOR_FILE_INSTR],ICON_FA_FILE);
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@ void FurnaceGUI::drawSpeed(bool asChild) {
|
|||
if (tempoView) setHz/=2.5;
|
||||
if (setHz<1) setHz=1;
|
||||
if (setHz>999) setHz=999;
|
||||
e->setSongRate(setHz,setHz<52);
|
||||
e->setSongRate(setHz);
|
||||
}
|
||||
if (tempoView) {
|
||||
ImGui::SameLine();
|
||||
|
|
|
|||
|
|
@ -92,6 +92,7 @@ void FurnaceGUI::drawSubSongs(bool asChild) {
|
|||
ImGui::SetTooltip("Add");
|
||||
}
|
||||
ImGui::SameLine();
|
||||
pushDestColor();
|
||||
if (ImGui::Button(ICON_FA_MINUS "##SubSongDel")) {
|
||||
if (e->song.subsong.size()<=1) {
|
||||
showError("this is the only subsong!");
|
||||
|
|
@ -99,6 +100,7 @@ void FurnaceGUI::drawSubSongs(bool asChild) {
|
|||
showWarning("are you sure you want to remove this subsong?",GUI_WARN_SUBSONG_DEL);
|
||||
}
|
||||
}
|
||||
popDestColor();
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("Remove");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -310,6 +310,7 @@ bool FurnaceGUI::drawSysConf(int chan, DivSystem type, DivConfig& flags, bool mo
|
|||
case DIV_SYSTEM_GB: {
|
||||
int chipType=flags.getInt("chipType",0);
|
||||
bool noAntiClick=flags.getBool("noAntiClick",false);
|
||||
bool invertWave=flags.getBool("invertWave",true);
|
||||
bool enoughAlready=flags.getBool("enoughAlready",false);
|
||||
|
||||
if (ImGui::Checkbox("Disable anti-click",&noAntiClick)) {
|
||||
|
|
@ -332,6 +333,26 @@ bool FurnaceGUI::drawSysConf(int chan, DivSystem type, DivConfig& flags, bool mo
|
|||
chipType=3;
|
||||
altered=true;
|
||||
}
|
||||
ImGui::Text("Wave channel orientation:");
|
||||
if (chipType==3) {
|
||||
if (ImGui::RadioButton("Normal",!invertWave)) {
|
||||
invertWave=false;
|
||||
altered=true;
|
||||
}
|
||||
if (ImGui::RadioButton("Inverted",invertWave)) {
|
||||
invertWave=true;
|
||||
altered=true;
|
||||
}
|
||||
} else {
|
||||
if (ImGui::RadioButton("Exact data (inverted)",!invertWave)) {
|
||||
invertWave=false;
|
||||
altered=true;
|
||||
}
|
||||
if (ImGui::RadioButton("Exact output (normal)",invertWave)) {
|
||||
invertWave=true;
|
||||
altered=true;
|
||||
}
|
||||
}
|
||||
if (ImGui::Checkbox("Pretty please one more compat flag when I use arpeggio and my sound length",&enoughAlready)) {
|
||||
altered=true;
|
||||
}
|
||||
|
|
@ -340,6 +361,7 @@ bool FurnaceGUI::drawSysConf(int chan, DivSystem type, DivConfig& flags, bool mo
|
|||
e->lockSave([&]() {
|
||||
flags.set("chipType",chipType);
|
||||
flags.set("noAntiClick",noAntiClick);
|
||||
flags.set("invertWave",invertWave);
|
||||
flags.set("enoughAlready",enoughAlready);
|
||||
});
|
||||
}
|
||||
|
|
@ -434,6 +456,9 @@ bool FurnaceGUI::drawSysConf(int chan, DivSystem type, DivConfig& flags, bool mo
|
|||
case DIV_SYSTEM_FDS:
|
||||
case DIV_SYSTEM_MMC5: {
|
||||
int clockSel=flags.getInt("clockSel",0);
|
||||
bool dpcmMode=flags.getBool("dpcmMode",true);
|
||||
|
||||
ImGui::Text("Clock rate:");
|
||||
|
||||
if (ImGui::RadioButton("NTSC (1.79MHz)",clockSel==0)) {
|
||||
clockSel=0;
|
||||
|
|
@ -448,9 +473,21 @@ bool FurnaceGUI::drawSysConf(int chan, DivSystem type, DivConfig& flags, bool mo
|
|||
altered=true;
|
||||
}
|
||||
|
||||
ImGui::Text("DPCM channel mode:");
|
||||
|
||||
if (ImGui::RadioButton("DPCM (muffled samples; low CPU usage)",dpcmMode)) {
|
||||
dpcmMode=true;
|
||||
altered=true;
|
||||
}
|
||||
if (ImGui::RadioButton("PCM (crisp samples; high CPU usage)",!dpcmMode)) {
|
||||
dpcmMode=false;
|
||||
altered=true;
|
||||
}
|
||||
|
||||
if (altered) {
|
||||
e->lockSave([&]() {
|
||||
flags.set("clockSel",clockSel);
|
||||
flags.set("dpcmMode",dpcmMode);
|
||||
});
|
||||
}
|
||||
break;
|
||||
|
|
@ -826,7 +863,7 @@ bool FurnaceGUI::drawSysConf(int chan, DivSystem type, DivConfig& flags, bool mo
|
|||
if (echoBufSize1<0) echoBufSize1=0;
|
||||
if (echoBufSize1>2725) echoBufSize1=2725;
|
||||
echoDelay=2725-echoBufSize1;
|
||||
altered=true;;
|
||||
altered=true;
|
||||
} rightClickable
|
||||
ImGui::Text("Echo feedback:");
|
||||
if (CWSliderInt("##EchoFeedback",&echoFeedback,0,255)) {
|
||||
|
|
|
|||
|
|
@ -223,26 +223,28 @@ void FurnaceGUI::initTutorial() {
|
|||
void FurnaceGUI::syncTutorial() {
|
||||
// tutorial.userComesFrom=e->getConfInt("tutUserComesFrom",0);
|
||||
tutorial.introPlayed=e->getConfBool("tutIntroPlayed",false);
|
||||
// tutorial.welcome=e->getConfBool("tutWelcome",false);
|
||||
tutorial.protoWelcome=e->getConfBool("tutProtoWelcome2",false);
|
||||
}
|
||||
|
||||
void FurnaceGUI::commitTutorial() {
|
||||
// e->setConf("tutUserComesFrom",tutorial.userComesFrom);
|
||||
e->setConf("tutIntroPlayed",tutorial.introPlayed);
|
||||
// e->setConf("tutWelcome",tutorial.welcome);
|
||||
e->setConf("tutProtoWelcome2",tutorial.protoWelcome);
|
||||
}
|
||||
|
||||
void FurnaceGUI::activateTutorial(FurnaceGUITutorials which) {
|
||||
if (tutorial.welcome && !tutorial.taken[which] && !ImGui::IsPopupOpen((const char*)NULL,ImGuiPopupFlags_AnyPopupId|ImGuiPopupFlags_AnyPopupLevel) && curTutorial==-1 && introPos>=10.0) {
|
||||
/*
|
||||
if (tutorial.protoWelcome && !tutorial.taken[which] && !ImGui::IsPopupOpen((const char*)NULL,ImGuiPopupFlags_AnyPopupId|ImGuiPopupFlags_AnyPopupLevel) && curTutorial==-1 && introPos>=10.0) {
|
||||
logV("activating tutorial %d.",which);
|
||||
curTutorial=which;
|
||||
curTutorialStep=0;
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
void FurnaceGUI::drawTutorial() {
|
||||
// welcome
|
||||
if (!tutorial.welcome) {
|
||||
if (!tutorial.protoWelcome) {
|
||||
ImGui::OpenPopup("Welcome");
|
||||
}
|
||||
if (ImGui::BeginPopupModal("Welcome",NULL,ImGuiWindowFlags_AlwaysAutoResize|ImGuiWindowFlags_NoMove|ImGuiWindowFlags_NoTitleBar)) {
|
||||
|
|
@ -253,7 +255,35 @@ void FurnaceGUI::drawTutorial() {
|
|||
|
||||
ImGui::Text("welcome to Furnace, the biggest open-source chiptune tracker!");
|
||||
|
||||
ImGui::TextWrapped("get ready for the tutorial, which will teach you how to use it.");
|
||||
ImGui::TextWrapped(
|
||||
"did I say that 0.6pre5 will have a tutorial? well, it doesn't...\n"
|
||||
"the reason is because 0.6pre5 fixes a critical bug which may cause config loss in some machines.\n"
|
||||
"furthermore, it dramatically improves the backup system. couldn't put this version on hold anymore."
|
||||
);
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
ImGui::TextWrapped("here are some tips to get you started:");
|
||||
|
||||
ImGui::TextWrapped(
|
||||
"- add an instrument by clicking on + in Instruments\n"
|
||||
"- click on the pattern view to focus it\n"
|
||||
"- channel columns have the following, in this order: note, instrument, volume and effects\n"
|
||||
"- hit space bar while on the pattern to toggle Edit Mode\n"
|
||||
"- click on the pattern or use arrow keys to move the cursor\n"
|
||||
"- values (instrument, volume, effects and effect values) are in hexadecimal\n"
|
||||
"- hit enter to play/stop the song\n"
|
||||
"- extend the song by adding more orders in the Orders window\n"
|
||||
"- click on the Orders matrix to change the patterns of a channel (left click increases; right click decreases)"
|
||||
);
|
||||
|
||||
ImGui::TextWrapped(
|
||||
"if you need help, you may:\n"
|
||||
"- read the (incomplete) manual: https://github.com/tildearrow/furnace/blob/master/doc/README.md\n"
|
||||
"- ask for help in Discussions (https://github.com/tildearrow/furnace/discussions) or the Furnace Discord (https://discord.gg/EfrwT2wq7z)"
|
||||
);
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
ImGui::TextWrapped(
|
||||
"there are two interface modes: Basic, and Advanced.\n"
|
||||
|
|
@ -265,13 +295,13 @@ void FurnaceGUI::drawTutorial() {
|
|||
|
||||
if (ImGui::Button("Start in Basic Mode")) {
|
||||
basicMode=true;
|
||||
tutorial.welcome=true;
|
||||
tutorial.protoWelcome=true;
|
||||
commitTutorial();
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
if (ImGui::Button("Start in Advanced Mode")) {
|
||||
basicMode=false;
|
||||
tutorial.welcome=true;
|
||||
tutorial.protoWelcome=true;
|
||||
commitTutorial();
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue