mirror of https://github.com/bsnes-emu/bsnes.git
Update to v094r02 release.
byuu says: Changelog: - ethos: use nall::programpath() instead of realpath(argv[0]) to get executable path - loki: add presentation window - loki: add terminal window - loki: add interface to emulation core - loki: add ruby - loki: add enough support to run games and save data on exit - load game folders via command-line (or drop folder onto binary), use "r" to start, "p" to pause ... temporary command names I'll probably have to say this several times, but for now, loki is only available on Linux/GTK+, due to the use of the Console widget. Support for other platforms can come later easily enough.
This commit is contained in:
parent
c54be74832
commit
187ba0eec6
|
@ -3,7 +3,7 @@
|
||||||
|
|
||||||
namespace Emulator {
|
namespace Emulator {
|
||||||
static const char Name[] = "higan";
|
static const char Name[] = "higan";
|
||||||
static const char Version[] = "094.01";
|
static const char Version[] = "094.02";
|
||||||
static const char Author[] = "byuu";
|
static const char Author[] = "byuu";
|
||||||
static const char License[] = "GPLv3";
|
static const char License[] = "GPLv3";
|
||||||
static const char Website[] = "http://byuu.org/";
|
static const char Website[] = "http://byuu.org/";
|
||||||
|
|
|
@ -45,7 +45,7 @@ Program::Program(int argc, char** argv) {
|
||||||
pause = false;
|
pause = false;
|
||||||
autopause = false;
|
autopause = false;
|
||||||
|
|
||||||
basepath = dir(realpath(argv[0]));
|
basepath = nall::programpath();
|
||||||
userpath = {nall::configpath(), "higan/"};
|
userpath = {nall::configpath(), "higan/"};
|
||||||
sharedpath = {nall::sharedpath(), "higan/"};
|
sharedpath = {nall::sharedpath(), "higan/"};
|
||||||
directory::create(userpath);
|
directory::create(userpath);
|
||||||
|
|
|
@ -6,10 +6,23 @@ include processor/Makefile
|
||||||
include sfc/Makefile
|
include sfc/Makefile
|
||||||
include gb/Makefile
|
include gb/Makefile
|
||||||
|
|
||||||
ui_objects := ui-loki
|
ui_objects := ui-loki ui-interface ui-presentation ui-terminal
|
||||||
ui_objects += phoenix
|
ui_objects += ruby phoenix
|
||||||
ui_objects += $(if $(call streq,$(platform),windows),resource)
|
ui_objects += $(if $(call streq,$(platform),windows),resource)
|
||||||
|
|
||||||
|
ifeq ($(platform),windows)
|
||||||
|
ruby := video.wgl audio.xaudio2 input.windows
|
||||||
|
else ifeq ($(platform),macosx)
|
||||||
|
ruby := video.cgl audio.openal input.carbon
|
||||||
|
else ifeq ($(platform),linux)
|
||||||
|
ruby := video.glx audio.alsa input.udev
|
||||||
|
else ifeq ($(platform),bsd)
|
||||||
|
ruby := video.glx audio.openal input.x
|
||||||
|
endif
|
||||||
|
|
||||||
|
include ruby/Makefile
|
||||||
|
link += $(rubylink)
|
||||||
|
|
||||||
include phoenix/Makefile
|
include phoenix/Makefile
|
||||||
link += $(phoenixlink)
|
link += $(phoenixlink)
|
||||||
|
|
||||||
|
@ -17,6 +30,12 @@ objects := $(ui_objects) $(objects)
|
||||||
objects := $(patsubst %,obj/%.o,$(objects))
|
objects := $(patsubst %,obj/%.o,$(objects))
|
||||||
|
|
||||||
obj/ui-loki.o: $(ui)/loki.cpp $(call rwildcard,$(ui)/)
|
obj/ui-loki.o: $(ui)/loki.cpp $(call rwildcard,$(ui)/)
|
||||||
|
obj/ui-interface.o: $(ui)/interface/interface.cpp $(call rwildcard,$(ui)/)
|
||||||
|
obj/ui-presentation.o: $(ui)/presentation/presentation.cpp $(call rwildcard,$(ui)/)
|
||||||
|
obj/ui-terminal.o: $(ui)/terminal/terminal.cpp $(call rwildcard,$(ui)/)
|
||||||
|
|
||||||
|
obj/ruby.o: ruby/ruby.cpp $(call rwildcard,ruby/)
|
||||||
|
$(compiler) $(rubyflags) -c $< -o $@
|
||||||
|
|
||||||
obj/phoenix.o: phoenix/phoenix.cpp $(call rwildcard,phoenix/)
|
obj/phoenix.o: phoenix/phoenix.cpp $(call rwildcard,phoenix/)
|
||||||
$(compiler) $(phoenixflags) -c $< -o $@
|
$(compiler) $(phoenixflags) -c $< -o $@
|
||||||
|
@ -44,6 +63,9 @@ else
|
||||||
$(strip $(compiler) -o out/$(name) $(objects) $(link))
|
$(strip $(compiler) -o out/$(name) $(objects) $(link))
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
resource:
|
||||||
|
sourcery $(ui)/resource/resource.bml $(ui)/resource/resource.cpp $(ui)/resource/resource.hpp
|
||||||
|
|
||||||
install:
|
install:
|
||||||
ifeq ($(platform),windows)
|
ifeq ($(platform),windows)
|
||||||
else ifeq ($(platform),macosx)
|
else ifeq ($(platform),macosx)
|
||||||
|
|
|
@ -0,0 +1,150 @@
|
||||||
|
#include "../loki.hpp"
|
||||||
|
Interface* interface = nullptr;
|
||||||
|
SuperFamicom::Interface* emulator = nullptr;
|
||||||
|
|
||||||
|
Interface::Interface() {
|
||||||
|
interface = this;
|
||||||
|
emulator = new SuperFamicom::Interface;
|
||||||
|
emulator->bind = this;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Interface::load(string pathname) {
|
||||||
|
pathname.transform("\\", "/").rtrim("/");
|
||||||
|
if(!directory::exists(pathname)) return false;
|
||||||
|
|
||||||
|
string type = extension(pathname);
|
||||||
|
pathname.append("/");
|
||||||
|
|
||||||
|
for(auto& media : emulator->media) {
|
||||||
|
if(media.bootable == false) continue;
|
||||||
|
if(type != media.type) continue;
|
||||||
|
|
||||||
|
pathnames.reset();
|
||||||
|
pathnames(0) = program->path({media.name, ".sys/"});
|
||||||
|
pathnames(media.id) = pathname;
|
||||||
|
|
||||||
|
emulator->load(media.id);
|
||||||
|
emulator->paletteUpdate(Emulator::Interface::PaletteMode::Standard);
|
||||||
|
emulator->power();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Interface::unload() {
|
||||||
|
emulator->unload();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Interface::inputEvent(HID::Device& device, unsigned group, unsigned input, int16_t oldValue, int16_t newValue) {
|
||||||
|
if(device.isKeyboard() == false) return;
|
||||||
|
|
||||||
|
switch(input) {
|
||||||
|
case 84: gamepad.up = newValue; break;
|
||||||
|
case 85: gamepad.down = newValue; break;
|
||||||
|
case 86: gamepad.left = newValue; break;
|
||||||
|
case 87: gamepad.right = newValue; break;
|
||||||
|
case 60: gamepad.b = newValue; break;
|
||||||
|
case 58: gamepad.a = newValue; break;
|
||||||
|
case 35: gamepad.y = newValue; break;
|
||||||
|
case 53: gamepad.x = newValue; break;
|
||||||
|
case 38: gamepad.l = newValue; break;
|
||||||
|
case 37: gamepad.r = newValue; break;
|
||||||
|
case 65: gamepad.select = newValue; break;
|
||||||
|
case 89: gamepad.start = newValue; break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//bindings
|
||||||
|
|
||||||
|
void Interface::loadRequest(unsigned id, string name, string type) {
|
||||||
|
}
|
||||||
|
|
||||||
|
void Interface::loadRequest(unsigned id, string path) {
|
||||||
|
string pathname = {pathnames(emulator->group(id)), path};
|
||||||
|
if(file::exists(pathname) == false) return;
|
||||||
|
mmapstream stream(pathname);
|
||||||
|
emulator->load(id, stream);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Interface::saveRequest(unsigned id, string path) {
|
||||||
|
string pathname = {pathnames(emulator->group(id)), path};
|
||||||
|
filestream stream(pathname, file::mode::write);
|
||||||
|
emulator->save(id, stream);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t Interface::videoColor(unsigned source, uint16_t alpha, uint16_t red, uint16_t green, uint16_t blue) {
|
||||||
|
return ((alpha >> 8) << 24) | ((red >> 8) << 16) | ((green >> 8) << 8) | ((blue >> 8) << 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Interface::videoRefresh(const uint32_t* palette, const uint32_t* data, unsigned pitch, unsigned width, unsigned height) {
|
||||||
|
uint32_t* output;
|
||||||
|
unsigned outputPitch;
|
||||||
|
|
||||||
|
if(video.lock(output, outputPitch, width, height)) {
|
||||||
|
pitch >>= 2, outputPitch >>= 2;
|
||||||
|
|
||||||
|
for(unsigned y = 0; y < height; y++) {
|
||||||
|
const uint32_t* sp = data + y * pitch;
|
||||||
|
uint32_t* dp = output + y * outputPitch;
|
||||||
|
for(unsigned x = 0; x < width; x++) {
|
||||||
|
*dp++ = palette[*sp++];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
video.unlock();
|
||||||
|
video.refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
input.poll();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Interface::audioSample(int16_t lsample, int16_t rsample) {
|
||||||
|
signed samples[] = {lsample, rsample};
|
||||||
|
dspaudio.sample(samples);
|
||||||
|
while(dspaudio.pending()) {
|
||||||
|
dspaudio.read(samples);
|
||||||
|
audio.sample(samples[0], samples[1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int16_t Interface::inputPoll(unsigned port, unsigned device, unsigned input) {
|
||||||
|
if(presentation->focused() == false) return 0;
|
||||||
|
if(port != 0 || device != 0) return 0;
|
||||||
|
|
||||||
|
switch(input) {
|
||||||
|
case 0: return gamepad.b;
|
||||||
|
case 1: return gamepad.y;
|
||||||
|
case 2: return gamepad.select;
|
||||||
|
case 3: return gamepad.start;
|
||||||
|
case 4: return gamepad.up;
|
||||||
|
case 5: return gamepad.down;
|
||||||
|
case 6: return gamepad.left;
|
||||||
|
case 7: return gamepad.right;
|
||||||
|
case 8: return gamepad.a;
|
||||||
|
case 9: return gamepad.x;
|
||||||
|
case 10: return gamepad.l;
|
||||||
|
case 11: return gamepad.r;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Interface::inputRumble(unsigned port, unsigned device, unsigned input, bool enable) {
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned Interface::dipSettings(const Markup::Node& node) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
string Interface::path(unsigned group) {
|
||||||
|
return pathnames(group);
|
||||||
|
}
|
||||||
|
|
||||||
|
string Interface::server() {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
void Interface::notify(string text) {
|
||||||
|
}
|
|
@ -0,0 +1,39 @@
|
||||||
|
struct Interface : Emulator::Interface::Bind {
|
||||||
|
Interface();
|
||||||
|
bool load(string pathname);
|
||||||
|
void unload();
|
||||||
|
void inputEvent(HID::Device& device, unsigned group, unsigned input, int16_t oldValue, int16_t newValue);
|
||||||
|
|
||||||
|
//bindings
|
||||||
|
void loadRequest(unsigned id, string name, string type);
|
||||||
|
void loadRequest(unsigned id, string path);
|
||||||
|
void saveRequest(unsigned id, string path);
|
||||||
|
uint32_t videoColor(unsigned source, uint16_t alpha, uint16_t red, uint16_t green, uint16_t blue);
|
||||||
|
void videoRefresh(const uint32_t* palette, const uint32_t* data, unsigned pitch, unsigned width, unsigned height);
|
||||||
|
void audioSample(int16_t lsample, int16_t rsample);
|
||||||
|
int16_t inputPoll(unsigned port, unsigned device, unsigned input);
|
||||||
|
void inputRumble(unsigned port, unsigned device, unsigned input, bool enable);
|
||||||
|
unsigned dipSettings(const Markup::Node& node);
|
||||||
|
string path(unsigned group);
|
||||||
|
string server();
|
||||||
|
void notify(string text);
|
||||||
|
|
||||||
|
lstring pathnames;
|
||||||
|
struct Gamepad {
|
||||||
|
bool up = false;
|
||||||
|
bool down = false;
|
||||||
|
bool left = false;
|
||||||
|
bool right = false;
|
||||||
|
bool b = false;
|
||||||
|
bool a = false;
|
||||||
|
bool y = false;
|
||||||
|
bool x = false;
|
||||||
|
bool l = false;
|
||||||
|
bool r = false;
|
||||||
|
bool select = false;
|
||||||
|
bool start = false;
|
||||||
|
} gamepad;
|
||||||
|
};
|
||||||
|
|
||||||
|
extern Interface* interface;
|
||||||
|
extern SuperFamicom::Interface* emulator;
|
|
@ -1,4 +1,5 @@
|
||||||
#include "loki.hpp"
|
#include "loki.hpp"
|
||||||
|
#include "resource/resource.cpp"
|
||||||
|
|
||||||
Program* program = nullptr;
|
Program* program = nullptr;
|
||||||
DSP dspaudio;
|
DSP dspaudio;
|
||||||
|
@ -14,15 +15,61 @@ string Program::path(string name) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Program::main() {
|
void Program::main() {
|
||||||
|
if(pause) {
|
||||||
|
usleep(20 * 1000);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Program::Program(int argc, char** argv) {
|
emulator->run();
|
||||||
|
}
|
||||||
|
|
||||||
|
Program::Program(string pathname) {
|
||||||
program = this;
|
program = this;
|
||||||
|
|
||||||
basepath = nall::programpath();
|
basepath = nall::programpath();
|
||||||
userpath = {nall::configpath(), "loki/"};
|
userpath = {nall::configpath(), "loki/"};
|
||||||
sharedpath = {nall::sharedpath(), "loki/"};
|
sharedpath = {nall::sharedpath(), "loki/"};
|
||||||
directory::create(userpath);
|
directory::create(userpath);
|
||||||
|
|
||||||
|
new Interface;
|
||||||
|
new Presentation;
|
||||||
|
new Terminal;
|
||||||
|
|
||||||
|
presentation->setVisible();
|
||||||
|
terminal->setVisible();
|
||||||
|
Application::processEvents();
|
||||||
|
|
||||||
|
video.driver(video.optimalDriver());
|
||||||
|
video.set(Video::Handle, presentation->viewport.handle());
|
||||||
|
video.set(Video::Synchronize, false);
|
||||||
|
video.set(Video::Filter, Video::FilterNearest);
|
||||||
|
if(video.init() == false) { video.driver("None"); video.init(); }
|
||||||
|
|
||||||
|
audio.driver(audio.optimalDriver());
|
||||||
|
audio.set(Audio::Handle, presentation->viewport.handle());
|
||||||
|
audio.set(Audio::Synchronize, true);
|
||||||
|
audio.set(Audio::Frequency, 48000u);
|
||||||
|
if(audio.init() == false) { audio.driver("None"); audio.init(); }
|
||||||
|
|
||||||
|
input.driver(input.optimalDriver());
|
||||||
|
input.set(Input::Handle, presentation->viewport.handle());
|
||||||
|
if(input.init() == false) { input.driver("None"); input.init(); }
|
||||||
|
input.onChange = {&Interface::inputEvent, interface};
|
||||||
|
|
||||||
|
dspaudio.setPrecision(16);
|
||||||
|
dspaudio.setBalance(0.0);
|
||||||
|
dspaudio.setFrequency(32000);
|
||||||
|
dspaudio.setResampler(DSP::ResampleEngine::Hermite);
|
||||||
|
dspaudio.setResamplerFrequency(48000);
|
||||||
|
|
||||||
|
presentation->showSplash();
|
||||||
|
|
||||||
|
interface->load(pathname);
|
||||||
|
|
||||||
|
Application::main = {&Program::main, this};
|
||||||
|
Application::run();
|
||||||
|
|
||||||
|
interface->unload();
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char** argv) {
|
int main(int argc, char** argv) {
|
||||||
|
@ -30,8 +77,14 @@ int main(int argc, char** argv) {
|
||||||
utf8_args(argc, argv);
|
utf8_args(argc, argv);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
if(argc != 2 || !directory::exists(argv[1])) {
|
||||||
|
print("loki v", Emulator::Version, "\n");
|
||||||
|
print("usage: loki /path/game.sfc/\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
Application::setName("loki");
|
Application::setName("loki");
|
||||||
new Program(argc, argv);
|
new Program(argv[1]);
|
||||||
delete program;
|
delete program;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
#include <emulator/emulator.hpp>
|
#include <emulator/emulator.hpp>
|
||||||
|
#include <sfc/interface/interface.hpp>
|
||||||
|
|
||||||
#include <nall/platform.hpp>
|
#include <nall/platform.hpp>
|
||||||
#include <nall/config.hpp>
|
#include <nall/config.hpp>
|
||||||
|
@ -12,17 +13,27 @@
|
||||||
#include <nall/stream/vector.hpp>
|
#include <nall/stream/vector.hpp>
|
||||||
using namespace nall;
|
using namespace nall;
|
||||||
|
|
||||||
|
#include <ruby/ruby.hpp>
|
||||||
|
using namespace ruby;
|
||||||
|
|
||||||
#include <phoenix/phoenix.hpp>
|
#include <phoenix/phoenix.hpp>
|
||||||
using namespace phoenix;
|
using namespace phoenix;
|
||||||
|
|
||||||
|
#include "interface/interface.hpp"
|
||||||
|
#include "presentation/presentation.hpp"
|
||||||
|
#include "terminal/terminal.hpp"
|
||||||
|
#include "resource/resource.hpp"
|
||||||
|
|
||||||
struct Program {
|
struct Program {
|
||||||
string basepath;
|
string basepath;
|
||||||
string userpath;
|
string userpath;
|
||||||
string sharedpath;
|
string sharedpath;
|
||||||
|
|
||||||
|
bool pause = true;
|
||||||
|
|
||||||
string path(string name);
|
string path(string name);
|
||||||
void main();
|
void main();
|
||||||
Program(int argc, char** argv);
|
Program(string pathname);
|
||||||
};
|
};
|
||||||
|
|
||||||
extern Program* program;
|
extern Program* program;
|
||||||
|
|
|
@ -0,0 +1,36 @@
|
||||||
|
#include "../loki.hpp"
|
||||||
|
Presentation* presentation = nullptr;
|
||||||
|
|
||||||
|
Presentation::Presentation() {
|
||||||
|
presentation = this;
|
||||||
|
|
||||||
|
setTitle({"loki v", Emulator::Version});
|
||||||
|
setWindowGeometry({0, 0, 512, 480});
|
||||||
|
setResizable(false);
|
||||||
|
|
||||||
|
layout.append(viewport, {0, 0, 512, 480});
|
||||||
|
append(layout);
|
||||||
|
|
||||||
|
onClose = &Application::quit;
|
||||||
|
|
||||||
|
splash.allocate(512, 480);
|
||||||
|
splash.verticalGradient(0xff00003f, 0xff000000, 512, 480, 256, 0);
|
||||||
|
nall::image floor;
|
||||||
|
floor.allocate(512, 480);
|
||||||
|
floor.radialGradient(0xffff0000, 0x00000000, 384, 240, 256, 415);
|
||||||
|
splash.impose(image::blend::sourceAlpha, 0, 0, floor, 0, 0, floor.width, floor.height);
|
||||||
|
nall::image loki(resource::loki, sizeof resource::loki);
|
||||||
|
splash.impose(image::blend::sourceAlpha, (512 - loki.width) / 2, (480 - loki.height) / 2, loki, 0, 0, loki.width, loki.height);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Presentation::showSplash() {
|
||||||
|
uint32_t* data;
|
||||||
|
unsigned pitch;
|
||||||
|
if(video.lock(data, pitch, 512, 480)) {
|
||||||
|
for(unsigned y = 0; y < 480; y++) {
|
||||||
|
memcpy((uint8_t*)data + y * pitch, splash.data + y * splash.pitch, 512 * sizeof(uint32_t));
|
||||||
|
}
|
||||||
|
video.unlock();
|
||||||
|
video.refresh();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
struct Presentation : Window {
|
||||||
|
FixedLayout layout;
|
||||||
|
Viewport viewport;
|
||||||
|
nall::image splash;
|
||||||
|
|
||||||
|
Presentation();
|
||||||
|
void showSplash();
|
||||||
|
};
|
||||||
|
|
||||||
|
extern Presentation* presentation;
|
Binary file not shown.
After Width: | Height: | Size: 119 KiB |
|
@ -0,0 +1,2 @@
|
||||||
|
resource name=resource
|
||||||
|
binary id=loki name=loki.png
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,3 @@
|
||||||
|
namespace resource {
|
||||||
|
extern const uint8_t loki[121905];
|
||||||
|
};
|
|
@ -0,0 +1,42 @@
|
||||||
|
#include "../loki.hpp"
|
||||||
|
Terminal* terminal = nullptr;
|
||||||
|
|
||||||
|
Terminal::Terminal() {
|
||||||
|
terminal = this;
|
||||||
|
|
||||||
|
setWindowGeometry({0, 480 + frameMargin().height, 800, 480});
|
||||||
|
|
||||||
|
console.setFont(Font::monospace(8));
|
||||||
|
print("loki v", Emulator::Version, "\n\n");
|
||||||
|
print("$ ");
|
||||||
|
|
||||||
|
layout.append(console, {~0, ~0});
|
||||||
|
append(layout);
|
||||||
|
|
||||||
|
onClose = &Application::quit;
|
||||||
|
console.onActivate = {&Terminal::command, this};
|
||||||
|
}
|
||||||
|
|
||||||
|
void Terminal::command(string s) {
|
||||||
|
if(s.empty()) {
|
||||||
|
} else if(s == "quit" || s == "exit") {
|
||||||
|
Application::quit();
|
||||||
|
} else if(s == "clear" || s == "reset") {
|
||||||
|
reset();
|
||||||
|
} else if(s == "r") {
|
||||||
|
program->pause = false;
|
||||||
|
} else if(s == "p") {
|
||||||
|
program->pause = true;
|
||||||
|
} else {
|
||||||
|
print("unrecognized command\n");
|
||||||
|
}
|
||||||
|
print("$ ");
|
||||||
|
}
|
||||||
|
|
||||||
|
void Terminal::reset() {
|
||||||
|
console.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename... Args> void Terminal::print(Args&&... args) {
|
||||||
|
console.print(std::forward<Args>(args)...);
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
struct Terminal : Window {
|
||||||
|
VerticalLayout layout;
|
||||||
|
Console console;
|
||||||
|
|
||||||
|
Terminal();
|
||||||
|
void command(string s);
|
||||||
|
void reset();
|
||||||
|
template<typename... Args> void print(Args&&... args);
|
||||||
|
};
|
||||||
|
|
||||||
|
extern Terminal* terminal;
|
Loading…
Reference in New Issue