mirror of
https://github.com/tildearrow/furnace.git
synced 2025-01-03 14:11:11 +00:00
add a proper CLI
featuring skip order (left/right) and pause (space)! currently available on macOS and Linux only.
This commit is contained in:
parent
3183400019
commit
83386d082d
5 changed files with 229 additions and 15 deletions
|
@ -478,6 +478,10 @@ if (WIN32)
|
||||||
list(APPEND ENGINE_SOURCES res/furnace.rc)
|
list(APPEND ENGINE_SOURCES res/furnace.rc)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
set(CLI_SOURCES
|
||||||
|
src/cli/cli.cpp
|
||||||
|
)
|
||||||
|
|
||||||
set(GUI_SOURCES
|
set(GUI_SOURCES
|
||||||
extern/imgui_patched/imgui.cpp
|
extern/imgui_patched/imgui.cpp
|
||||||
extern/imgui_patched/imgui_draw.cpp
|
extern/imgui_patched/imgui_draw.cpp
|
||||||
|
@ -589,7 +593,7 @@ if (NOT WIN32)
|
||||||
endif()
|
endif()
|
||||||
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)
|
if (USE_BACKWARD)
|
||||||
list(APPEND USED_SOURCES src/backtrace.cpp)
|
list(APPEND USED_SOURCES src/backtrace.cpp)
|
||||||
|
|
137
src/cli/cli.cpp
Normal file
137
src/cli/cli.cpp
Normal 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
55
src/cli/cli.h
Normal 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
|
|
@ -201,7 +201,7 @@ const char* formatNote(unsigned char note, unsigned char octave) {
|
||||||
|
|
||||||
int DivEngine::dispatchCmd(DivCommand c) {
|
int DivEngine::dispatchCmd(DivCommand c) {
|
||||||
if (view==DIV_STATUS_COMMANDS) {
|
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++;
|
totalCmds++;
|
||||||
if (cmdStreamEnabled && cmdStream.size()<2000) {
|
if (cmdStreamEnabled && cmdStream.size()<2000) {
|
||||||
|
@ -771,7 +771,7 @@ void DivEngine::nextRow() {
|
||||||
static char pb1[4096];
|
static char pb1[4096];
|
||||||
static char pb2[4096];
|
static char pb2[4096];
|
||||||
static char pb3[4096];
|
static char pb3[4096];
|
||||||
if (view==DIV_STATUS_PATTERN) {
|
if (view==DIV_STATUS_PATTERN && !skipping) {
|
||||||
strcpy(pb1,"");
|
strcpy(pb1,"");
|
||||||
strcpy(pb3,"");
|
strcpy(pb3,"");
|
||||||
for (int i=0; i<chans; i++) {
|
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;
|
if (haltOn==DIV_HALT_TICK) halted=true;
|
||||||
|
|
40
src/main.cpp
40
src/main.cpp
|
@ -36,6 +36,8 @@
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include "cli/cli.h"
|
||||||
|
|
||||||
#ifdef HAVE_GUI
|
#ifdef HAVE_GUI
|
||||||
#include "gui/gui.h"
|
#include "gui/gui.h"
|
||||||
#endif
|
#endif
|
||||||
|
@ -46,6 +48,8 @@ DivEngine e;
|
||||||
FurnaceGUI g;
|
FurnaceGUI g;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
FurnaceCLI cli;
|
||||||
|
|
||||||
String outName;
|
String outName;
|
||||||
String vgmOutName;
|
String vgmOutName;
|
||||||
int loops=1;
|
int loops=1;
|
||||||
|
@ -465,25 +469,39 @@ int main(int argc, char** argv) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (consoleMode) {
|
if (consoleMode) {
|
||||||
|
bool cliSuccess=false;
|
||||||
|
cli.bindEngine(&e);
|
||||||
|
if (!cli.init()) {
|
||||||
|
reportError("error while starting CLI!");
|
||||||
|
} else {
|
||||||
|
cliSuccess=true;
|
||||||
|
}
|
||||||
logI("playing...");
|
logI("playing...");
|
||||||
e.play();
|
e.play();
|
||||||
|
if (cliSuccess) {
|
||||||
|
cli.loop();
|
||||||
|
cli.finish();
|
||||||
|
e.quit();
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
#ifdef HAVE_SDL2
|
#ifdef HAVE_SDL2
|
||||||
SDL_Event ev;
|
SDL_Event ev;
|
||||||
while (true) {
|
while (true) {
|
||||||
SDL_WaitEvent(&ev);
|
SDL_WaitEvent(&ev);
|
||||||
if (ev.type==SDL_QUIT) break;
|
if (ev.type==SDL_QUIT) break;
|
||||||
}
|
}
|
||||||
e.quit();
|
e.quit();
|
||||||
return 0;
|
return 0;
|
||||||
#else
|
#else
|
||||||
while (true) {
|
while (true) {
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
Sleep(500);
|
Sleep(500);
|
||||||
#else
|
#else
|
||||||
usleep(500000);
|
usleep(500000);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef HAVE_GUI
|
#ifdef HAVE_GUI
|
||||||
|
|
Loading…
Reference in a new issue