add a proper CLI

featuring skip order (left/right) and pause (space)!
currently available on macOS and Linux only.
This commit is contained in:
tildearrow 2022-07-25 17:23:56 -05:00
parent 3183400019
commit 83386d082d
5 changed files with 229 additions and 15 deletions

View File

@ -478,6 +478,10 @@ if (WIN32)
list(APPEND ENGINE_SOURCES res/furnace.rc)
endif()
set(CLI_SOURCES
src/cli/cli.cpp
)
set(GUI_SOURCES
extern/imgui_patched/imgui.cpp
extern/imgui_patched/imgui_draw.cpp
@ -589,7 +593,7 @@ if (NOT WIN32)
endif()
endif()
set(USED_SOURCES ${ENGINE_SOURCES} ${AUDIO_SOURCES} src/main.cpp)
set(USED_SOURCES ${ENGINE_SOURCES} ${AUDIO_SOURCES} ${CLI_SOURCES} src/main.cpp)
if (USE_BACKWARD)
list(APPEND USED_SOURCES src/backtrace.cpp)

137
src/cli/cli.cpp Normal file
View File

@ -0,0 +1,137 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 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 "cli.h"
#include "../ta-log.h"
bool cliQuit=false;
static void handleTerm(int) {
cliQuit=true;
}
void FurnaceCLI::bindEngine(DivEngine* eng) {
e=eng;
}
bool FurnaceCLI::loop() {
bool escape=false;
bool escapeSecondStage=false;
while (!cliQuit) {
unsigned char c;
if (read(STDIN_FILENO,&c,1)<=0) continue;
if (escape) {
if (escapeSecondStage) {
switch (c) {
case 'C': // right
e->setOrder(e->getOrder()+1);
escape=false;
escapeSecondStage=false;
break;
case 'D': // left
e->setOrder(e->getOrder()-1);
escape=false;
escapeSecondStage=false;
break;
default:
escape=false;
escapeSecondStage=false;
break;
}
} else {
switch (c) {
case '[': case 'O':
escapeSecondStage=true;
break;
default:
escape=false;
break;
}
}
} else {
switch (c) {
case 0x1b: // <ESC>
escape=true;
break;
case 'h': // left
e->setOrder(e->getOrder()-1);
break;
case 'l': // right
e->setOrder(e->getOrder()+1);
break;
case ' ':
if (e->isHalted()) {
e->resume();
} else {
e->halt();
}
break;
}
}
}
printf("\n");
return true;
}
bool FurnaceCLI::finish() {
if (tcsetattr(0,TCSAFLUSH,&termpropold)!=0) {
logE("could not set console attributes!");
logE("you may have to run `reset` on your terminal.");
return false;
}
return true;
}
// blatantly copied from tildearrow/tfmxplay
bool FurnaceCLI::init() {
#ifdef _WIN32
winin=GetStdHandle(STD_INPUT_HANDLE);
winout=GetStdHandle(STD_OUTPUT_HANDLE);
int termprop=0;
int termpropi=0;
GetConsoleMode(winout,(LPDWORD)&termprop);
GetConsoleMode(winin,(LPDWORD)&termpropi);
termprop|=ENABLE_VIRTUAL_TERMINAL_PROCESSING;
termpropi&=~ENABLE_LINE_INPUT;
SetConsoleMode(winout,termprop);
SetConsoleMode(winin,termpropi);
#else
sigemptyset(&intsa.sa_mask);
intsa.sa_flags=0;
intsa.sa_handler=handleTerm;
sigaction(SIGINT,&intsa,NULL);
if (tcgetattr(0,&termprop)!=0) {
logE("could not get console attributes!");
return false;
}
memcpy(&termpropold,&termprop,sizeof(struct termios));
termprop.c_lflag&=~ECHO;
termprop.c_lflag&=~ICANON;
if (tcsetattr(0,TCSAFLUSH,&termprop)!=0) {
logE("could not set console attributes!");
return false;
}
#endif
return true;
}
FurnaceCLI::FurnaceCLI():
e(NULL) {
}

55
src/cli/cli.h Normal file
View File

@ -0,0 +1,55 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 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.
*/
#ifndef _FUR_CLI_H
#define _FUR_CLI_H
#include <stdio.h>
#ifdef _WIN32
#include <windows.h>
#else
#include <signal.h>
#include <unistd.h>
#include <termios.h>
#include <sys/ioctl.h>
#endif
#include "../engine/engine.h"
class FurnaceCLI {
DivEngine* e;
#ifdef _WIN32
HANDLE winin;
HANDLE winout;
#else
struct sigaction intsa;
struct termios termprop;
struct termios termpropold;
#endif
public:
void bindEngine(DivEngine* eng);
bool loop();
bool finish();
bool init();
FurnaceCLI();
};
#endif

View File

@ -201,7 +201,7 @@ const char* formatNote(unsigned char note, unsigned char octave) {
int DivEngine::dispatchCmd(DivCommand c) {
if (view==DIV_STATUS_COMMANDS) {
printf("%8d | %d: %s(%d, %d)\n",totalTicksR,c.chan,cmdName[c.cmd],c.value,c.value2);
if (!skipping) printf("%8d | %d: %s(%d, %d)\n",totalTicksR,c.chan,cmdName[c.cmd],c.value,c.value2);
}
totalCmds++;
if (cmdStreamEnabled && cmdStream.size()<2000) {
@ -771,7 +771,7 @@ void DivEngine::nextRow() {
static char pb1[4096];
static char pb2[4096];
static char pb3[4096];
if (view==DIV_STATUS_PATTERN) {
if (view==DIV_STATUS_PATTERN && !skipping) {
strcpy(pb1,"");
strcpy(pb3,"");
for (int i=0; i<chans; i++) {
@ -1105,7 +1105,7 @@ bool DivEngine::nextTick(bool noAccum, bool inhibitLowLat) {
}
}
if (consoleMode && subticks<=1) fprintf(stderr,"\x1b[2K> %d:%.2d:%.2d.%.2d %.2x/%.2x:%.3d/%.3d %4dcmd/s\x1b[G",totalSeconds/3600,(totalSeconds/60)%60,totalSeconds%60,totalTicks/10000,curOrder,curSubSong->ordersLen,curRow,curSubSong->patLen,cmdsPerSecond);
if (consoleMode && subticks<=1 && !skipping) fprintf(stderr,"\x1b[2K> %d:%.2d:%.2d.%.2d %.2x/%.2x:%.3d/%.3d %4dcmd/s\x1b[G",totalSeconds/3600,(totalSeconds/60)%60,totalSeconds%60,totalTicks/10000,curOrder,curSubSong->ordersLen,curRow,curSubSong->patLen,cmdsPerSecond);
}
if (haltOn==DIV_HALT_TICK) halted=true;

View File

@ -36,6 +36,8 @@
#include <unistd.h>
#endif
#include "cli/cli.h"
#ifdef HAVE_GUI
#include "gui/gui.h"
#endif
@ -46,6 +48,8 @@ DivEngine e;
FurnaceGUI g;
#endif
FurnaceCLI cli;
String outName;
String vgmOutName;
int loops=1;
@ -465,25 +469,39 @@ int main(int argc, char** argv) {
}
if (consoleMode) {
bool cliSuccess=false;
cli.bindEngine(&e);
if (!cli.init()) {
reportError("error while starting CLI!");
} else {
cliSuccess=true;
}
logI("playing...");
e.play();
if (cliSuccess) {
cli.loop();
cli.finish();
e.quit();
return 0;
} else {
#ifdef HAVE_SDL2
SDL_Event ev;
while (true) {
SDL_WaitEvent(&ev);
if (ev.type==SDL_QUIT) break;
}
e.quit();
return 0;
SDL_Event ev;
while (true) {
SDL_WaitEvent(&ev);
if (ev.type==SDL_QUIT) break;
}
e.quit();
return 0;
#else
while (true) {
while (true) {
#ifdef _WIN32
Sleep(500);
Sleep(500);
#else
usleep(500000);
usleep(500000);
#endif
}
#endif
}
#endif
}
#ifdef HAVE_GUI