#include"mx.hpp" #include"gl.hpp" #include"argz.hpp" #include"ast.hpp" #include"parser.hpp" #include"scanner.hpp" #include"types.hpp" #include"string_buffer.hpp" #include"command.hpp" #ifdef __EMSCRIPTEN__ #include <emscripten/emscripten.h> #include <GLES3/gl3.h> #endif #include<atomic> #include<thread> #include<filesystem> #include<mutex> #include<condition_variable> #include<fstream> #include<iostream> #include"loadpng.hpp" #include"version_info.hpp" #define CHECK_GL_ERROR() \ { GLenum err = glGetError(); \ if (err != GL_NO_ERROR) \ printf("OpenGL Error: %d at %s:%d\n", err, __FILE__, __LINE__); } #ifndef M_PI #define M_PI 3.14159265358979323846 #endif // Real-time console output stream: every write is immediately forwarded // to the GLWindow console instead of buffering in an ostringstream. class ConsoleStreamBuf : public std::streambuf { public: ConsoleStreamBuf(gl::GLWindow *win) : window(win) {} protected: int overflow(int c) override { if (c != EOF) { char ch = static_cast<char>(c); buf += ch; if (ch == '\n') { flush_buf(); } } return c; } std::streamsize xsputn(const char *s, std::streamsize n) override { buf.append(s, static_cast<size_t>(n)); // flush on every newline or when buffer gets large if (buf.find('\n') != std::string::npos || buf.size() > 256) { flush_buf(); } return n; } int sync() override { flush_buf(); return 0; } private: void flush_buf() { if (!buf.empty()) { window->console.thread_safe_print(buf); window->console.process_message_queue(); buf.clear(); } } gl::GLWindow *window; std::string buf; }; struct MultiLineState { bool needsMoreInput; int blockDepth; bool lineContinuation; }; static int countWord(const std::string& input, const std::string& word) { int count = 0; size_t pos = 0; while ((pos = input.find(word, pos)) != std::string::npos) { bool validStart = (pos == 0 || !std::isalnum(input[pos - 1])); bool validEnd = (pos + word.length() >= input.length() || !std::isalnum(input[pos + word.length()])); if (validStart && validEnd) { count++; } pos += word.length(); } return count; } static bool endsWithBackslash(const std::string& input) { if (input.empty()) return false; size_t lastNonSpace = input.find_last_not_of(" \t\n\r"); if (lastNonSpace != std::string::npos && input[lastNonSpace] == '\\') { return true; } return false; } static MultiLineState checkMultiLineState(const std::string& input) { MultiLineState state; state.needsMoreInput = false; state.blockDepth = 0; state.lineContinuation = false; if (input.empty()) { return state; } if (endsWithBackslash(input)) { state.lineContinuation = true; state.needsMoreInput = true; return state; } int forCount = countWord(input, "for"); int whileCount = countWord(input, "while"); int ifCount = countWord(input, "if"); int defineCount = countWord(input, "define"); int doneCount = countWord(input, "done"); int fiCount = countWord(input, "fi"); int endCount = countWord(input, "end"); int loopDepth = (forCount + whileCount) - doneCount; int ifDepth = ifCount - fiCount; int defineDepth = defineCount - endCount; state.blockDepth = loopDepth + ifDepth + defineDepth; if (state.blockDepth > 0) { state.needsMoreInput = true; } return state; } class Game : public gl::GLObject { int start_shader = 10; public: Game(int start_shader_ = -1) : gl::GLObject(), start_shader(start_shader_) { cmd::AstExecutor::getExecutor().setInterrupt(&interrupt_command); } virtual ~Game() override { interrupt_command = true; executor.setUpdateCallback(nullptr); std::lock_guard<std::mutex> lock(worker_mutex); for (auto &worker : workers) { if (worker.joinable()) { worker.join(); } } workers.clear(); executor.setInterrupt(nullptr); } std::atomic<bool> interrupt_command{false}; std::atomic<bool> program_running{false}; std::atomic<bool> command_active{false}; cmd::AstExecutor &executor = cmd::AstExecutor::getExecutor(); bool cmd_echo = true; std::string multiLineBuffer; bool isMultiLineInput = false; int blockDepth = 0; std::string savedPrompt; std::mutex task_mutex; std::vector<std::function<void(gl::GLWindow*)>> main_thread_tasks; std::mutex worker_mutex; std::vector<std::thread> workers; std::thread::id ui_thread_id; bool background_shader_enabled = true; void queueTaskForMainThread(std::function<void(gl::GLWindow*)> task) { std::lock_guard<std::mutex> lock(task_mutex); main_thread_tasks.push_back(task); } void processMainThreadTasks(gl::GLWindow *win) { std::vector<std::function<void(gl::GLWindow*)>> tasks_to_run; { std::lock_guard<std::mutex> lock(task_mutex); if (!main_thread_tasks.empty()) { tasks_to_run.swap(main_thread_tasks); } } for (const auto& task : tasks_to_run) { task(win); } } void useWSL(gl::GLWindow *win, const bool &enable) { #ifdef _WIN32 if(enable) { if(!std::filesystem::exists("C:\\Windows\\System32\\wsl.exe")) { win->console.thread_safe_print("WSL executable not found. Please ensure WSL is installed.\n"); win->console.process_message_queue(); return; } cmd::cmd_type = "wsl.exe"; win->console.thread_safe_print("WSL enabled. Commands will be executed in WSL.\n"); win->console.process_message_queue(); } else { cmd::cmd_type = "cmd.exe /c "; win->console.thread_safe_print("WSL disabled. Commands will be executed in cmd.exe.\n"); win->console.process_message_queue(); } #endif } void load(gl::GLWindow *win) override { ui_thread_id = std::this_thread::get_id(); font.loadFont(win->util.getFilePath("data/font.ttf"), 36); win->console.print("Console Skeleton Example\nLostSideDead Software\nhttps://lostsidedead.biz\n"); win->console.setPrompt("$> "); win->console_visible = true; win->console.show(); shaders = { win->util.getFilePath("data/shaders/default.glsl"), win->util.getFilePath("data/shaders/cyclone.glsl"), win->util.getFilePath("data/shaders/geometric.glsl"), win->util.getFilePath("data/shaders/distort.glsl"), win->util.getFilePath("data/shaders/atan.glsl"), win->util.getFilePath("data/shaders/huri.glsl"), win->util.getFilePath("data/shaders/vhs.glsl"), win->util.getFilePath("data/shaders/fractal.glsl"), win->util.getFilePath("data/shaders/color-f2.glsl"), win->util.getFilePath("data/shaders/color-cycle.glsl"), win->util.getFilePath("data/shaders/color-swirl.glsl"), win->util.getFilePath("data/shaders/color-shift.glsl"), win->util.getFilePath("data/shaders/ripple.glsl") }; executor.setInterrupt(&interrupt_command); win->console.setInputCallback([this](gl::GLWindow *window, const std::string &text) -> int { auto tokenize = [](const std::string &text) { std::vector<std::string> tokens; std::istringstream iss(text); std::string token; while (iss >> token) { tokens.push_back(token); } return tokens; }; try { if(text.empty()) { if(isMultiLineInput) { multiLineBuffer += "\n"; window->console.thread_safe_print("\n"); window->console.process_message_queue(); return 0; } return 0; } bool lineContinuation = false; std::string inputLine = text; if (!inputLine.empty()) { size_t lastNonSpace = inputLine.find_last_not_of(" \t"); if (lastNonSpace != std::string::npos && inputLine[lastNonSpace] == '\\') { lineContinuation = true; inputLine = inputLine.substr(0, lastNonSpace); } } if (isMultiLineInput) { multiLineBuffer += "\n" + inputLine; MultiLineState state = checkMultiLineState(multiLineBuffer); if (lineContinuation || state.needsMoreInput) { blockDepth = state.blockDepth; window->console.thread_safe_print(".. " + text + "\n"); window->console.process_message_queue(); return 0; } else { window->console.thread_safe_print(".. " + text + "\n"); window->console.process_message_queue(); std::string fullCommand = multiLineBuffer; isMultiLineInput = false; multiLineBuffer.clear(); blockDepth = 0; window->console.setPrompt(savedPrompt); const_cast<std::string&>(text) = fullCommand; } } else { MultiLineState state = checkMultiLineState(inputLine); if (lineContinuation || state.needsMoreInput) { isMultiLineInput = true; multiLineBuffer = inputLine; blockDepth = state.blockDepth; savedPrompt = "$> "; window->console.setPrompt(".. "); if(cmd_echo) { window->console.thread_safe_print("$> " + text + "\n"); window->console.process_message_queue(); } return 0; } } if(text == "random_shader") { queueTaskForMainThread([this](gl::GLWindow* main_thread_win_param) { this->setRandomShader(main_thread_win_param, -1); }); window->console.thread_safe_print("Random shader command queued.\n"); window->console.process_message_queue(); return 0; } else if(text == "default_shader") { queueTaskForMainThread([this](gl::GLWindow* main_thread_win_param) { this->setRandomShader(main_thread_win_param, 0); }); window->console.thread_safe_print("Default shader command queued.\n"); window->console.process_message_queue(); return 0; } else if(text.compare(0, 10, "set_shader") == 0) { std::string arg = text.substr(10); size_t start = arg.find_first_not_of(" \t\n\r\f\v"); if (start == std::string::npos) { window->console.thread_safe_print("Error: No shader path provided.\n"); window->console.process_message_queue(); return 0; } arg = arg.substr(start); if (arg.size() >= 2 && arg.front() == '"' && arg.back() == '"') { std::string shader_path = arg.substr(1, arg.size() - 2); if (!std::filesystem::exists(shader_path)) { window->console.thread_safe_print("Error: Shader file does not exist: " + shader_path + "\n"); window->console.process_message_queue(); return 0; } queueTaskForMainThread([this, shader_path](gl::GLWindow* main_thread_win_param) { this->setShader(main_thread_win_param, shader_path); }); window->console.thread_safe_print("Set shader command queued for: " + shader_path + "\n"); window->console.process_message_queue(); } else { window->console.thread_safe_print("Error: Shader path must be enclosed in quotes.\n"); window->console.process_message_queue(); } return 0; } else if(text == "wsl_on") { #ifdef _WIN32 queueTaskForMainThread([this](gl::GLWindow* main_thread_win_param) { this->useWSL(main_thread_win_param, true); }); return 0; #else window->console.thread_safe_print("WSL is not supported on this platform.\n"); window->console.process_message_queue(); return 1; #endif } else if(text == "wsl_off") { #ifdef _WIN32 queueTaskForMainThread([this](gl::GLWindow* main_thread_win_param) { this->useWSL(main_thread_win_param, false); }); return 0; #else window->console.thread_safe_print("WSL is not supported on this platform.\n"); window->console.process_message_queue(); return 1; #endif } else if(text == "clear") { window->console.clearText(); window->console.process_message_queue(); return 0; } else if(text == "about") { window->console.thread_safe_print("Console Skeleton Example v1.0\n"); window->console.thread_safe_print("MX2 version v" + std::to_string(PROJECT_VERSION_MAJOR) + "." + std::to_string(PROJECT_VERSION_MINOR) + "\n"); window->console.thread_safe_print("Written by Jared Bruni\n"); window->console.thread_safe_print("https://lostsidedead.biz\n"); window->console.process_message_queue(); return 0; } else if(text == "exit") { window->console.thread_safe_print("Exiting console...\n"); window->quit(); return 0; } else if(text == "help") { window->console.thread_safe_print("Available Built-in Console Control commands:\n"); window->console.thread_safe_print(" - random_shader: Set a random shader.\n"); window->console.thread_safe_print(" - default_shader: Set the default shader.\n"); window->console.thread_safe_print(" - set_shader \"path/to/shader.glsl\": Set a specific shader.\n"); window->console.thread_safe_print(" - wsl_on: Enable WSL for command execution (Windows only).\n"); window->console.thread_safe_print(" - wsl_off: Disable WSL for command execution (Windows only).\n"); window->console.thread_safe_print(" - clear: Clear the console.\n"); window->console.thread_safe_print(" - about: Show information about this application.\n"); window->console.thread_safe_print(" - exit: Exit the application.\n"); window->console.thread_safe_print("\nMulti-line Input:\n"); window->console.thread_safe_print(" for/while ... done, if ... fi, define ... end\n"); window->console.thread_safe_print(" Use \\ at end of line for line continuation\n"); window->console.thread_safe_print(" CTRL+C cancels multi-line input\n"); window->console.process_message_queue(); return 0; } else if(text == "@echo_on") { cmd_echo = true; window->console.thread_safe_print("Echoing commands on.\n"); window->console.process_message_queue(); return 0; } else if(text == "@echo_off") { cmd_echo = false; window->console.thread_safe_print("Echoing commands off.\n"); window->console.process_message_queue(); return 0; } else if(!text.empty() && text[0] == '@') { std::string command = text.substr(1); auto tokens = tokenize(command); window->console.procDefaultCommands(tokens); return 0; } if(cmd_echo) { window->console.thread_safe_print("$ " + text + "\n"); window->console.process_message_queue(); } bool expected = false; if (!command_active.compare_exchange_strong(expected, true)) { window->console.thread_safe_print("A command is already running. Press CTRL+C to interrupt.\n"); window->console.process_message_queue(); return 0; } auto runCommand = [this, text, window]() { try { executor.setInterrupt(&this->interrupt_command); std::cout << "Executing: " << text << std::endl; bool update_callback_printed = false; struct ExecutionGuard { cmd::AstExecutor &exec; std::atomic<bool> &running; std::atomic<bool> &active; ~ExecutionGuard() { exec.setUpdateCallback(nullptr); running = false; active = false; } } guard{executor, program_running, command_active}; program_running = true; executor.setUpdateCallback( [window,this,&update_callback_printed](const std::string &chunk) { if(!chunk.empty()) { window->console.thread_safe_print(chunk); window->console.process_message_queue(); update_callback_printed = true; } if(interrupt_command) { interrupt_command = false; throw cmd::Exit_Exception(100); } } ); std::stringstream input_stream(text); scan::TString string_buffer(text); scan::Scanner scanner(string_buffer); cmd::Parser parser(scanner); auto ast = parser.parse(); ConsoleStreamBuf consoleBuf(window); std::ostream out_stream(&consoleBuf); executor.execute(input_stream, out_stream, ast); out_stream.flush(); } catch(const scan::ScanExcept &e) { window->console.thread_safe_print("Scanner Exception: " + e.why() + "\n"); window->console.process_message_queue(); } catch(const cmd::Exit_Exception &e) { if(e.getCode() == 100) { window->console.thread_safe_print("Execution interrupted\n"); } else { window->console.thread_safe_print("Execution exited with code " + std::to_string(e.getCode()) + "\n"); window->quit(); return; } window->console.process_message_queue(); interrupt_command = false; } catch(const std::runtime_error &e) { window->console.thread_safe_print("Runtime Exception: " + std::string(e.what()) + "\n"); window->console.process_message_queue(); } catch(const std::exception &e) { window->console.thread_safe_print("Exception: " + std::string(e.what()) + "\n"); window->console.process_message_queue(); } catch (const state::StateException &e) { window->console.thread_safe_print("State Exception: " + std::string(e.what()) + "\n"); window->console.process_message_queue(); } catch(const cmd::AstFailure &e) { window->console.thread_safe_print("Failure: " + std::string(e.what()) + "\n"); window->console.process_message_queue(); } catch(...) { window->console.thread_safe_print("Unknown Error: Command execution failed\n"); window->console.process_message_queue(); command_active = false; } }; auto trimLeft = [](const std::string &value) { size_t first = value.find_first_not_of(" \t\r\n"); if (first == std::string::npos) { return std::string(); } return value.substr(first); }; std::string commandText = trimLeft(text); bool runOnMainThread = false; if (commandText.rfind("cmd ", 0) == 0 || commandText.rfind("cmd\t", 0) == 0) { runOnMainThread = true; } if (runOnMainThread) { if (std::this_thread::get_id() == ui_thread_id) { runCommand(); } else { std::mutex done_mutex; std::condition_variable done_cv; bool done = false; queueTaskForMainThread([runCommand, &done_mutex, &done_cv, &done](gl::GLWindow*) mutable { runCommand(); { std::lock_guard<std::mutex> lock(done_mutex); done = true; } done_cv.notify_one(); }); std::unique_lock<std::mutex> lock(done_mutex); done_cv.wait(lock, [&done]() { return done; }); } } else { try { std::lock_guard<std::mutex> lock(worker_mutex); workers.emplace_back(std::move(runCommand)); } catch (...) { command_active = false; throw; } } return 0; } catch(const std::exception &e) { window->console.thread_safe_print("Error: " + std::string(e.what()) + "\n"); window->console.process_message_queue(); return 1; } }); std::vector<std::string> img = { "data/crystal_red.png", "data/saphire.png", "data/crystal_blue.png", "data/crystal_green.png", "data/crystal_pink.png", "data/diamond.png" }; std::string img_index = img.at(mx::generateRandomInt(0, img.size()-1)); setRandomShader(win, start_shader); logo.initSize(win->w, win->h); logo.loadTexture(program.get(), win->util.getFilePath(img_index), 0.0f, 0.0f, win->w, win->h); CHECK_GL_ERROR(); } const char *vSource = R"(#version 330 core layout (location = 0) in vec3 aPos; layout (location = 1) in vec2 aTexCoord; out vec2 TexCoord; void main() { gl_Position = vec4(aPos, 1.0); TexCoord = aTexCoord; } )"; std::vector<std::string> shaders; void setRandomShader(gl::GLWindow *win, int index = -1) { std::fstream file; std::string filename; if(index == -1) { filename = shaders.at(mx::generateRandomInt(0, shaders.size()-1)); file.open(filename, std::ios::in); } else { if(index < 0 || index >= static_cast<int>(shaders.size())) { std::cerr << "Invalid shader index. Using random shader." << std::endl; filename = shaders.at(mx::generateRandomInt(0, shaders.size()-1)); file.open(filename, std::ios::in); } else { filename = shaders.at(index); file.open(filename, std::ios::in); } } if (!file.is_open()) { std::cerr << "Failed to open shader file: " << filename << std::endl; return; } std::ostringstream shader_source; shader_source << file.rdbuf(); file.close(); program.reset(new gl::ShaderProgram()); if(program->loadProgramFromText(vSource, shader_source.str())) { program->setSilent(true); program->useProgram(); program->setUniform("textTexture", 0); program->setUniform("time_f", 0.0f); program->setUniform("alpha", 1.0f); GLint windowSizeLoc = glGetUniformLocation(program->id(), "iResolution"); glUniform2f(windowSizeLoc, static_cast<float>(win->w), static_cast<float>(win->h)); logo.setShader(program.get()); } else { std::cerr << "Failed to load shader program from file: " << filename << std::endl; } CHECK_GL_ERROR(); } void setShader(gl::GLWindow *win, const std::string &shader_path) { std::fstream file(shader_path, std::ios::in); if (!file.is_open()) { std::cerr << "Failed to open shader file: " << shader_path << std::endl; return; } std::ostringstream shader_source; shader_source << file.rdbuf(); file.close(); program.reset(new gl::ShaderProgram()); if(program->loadProgramFromText(vSource, shader_source.str())) { program->setSilent(true); program->useProgram(); program->setUniform("textTexture", 0); program->setUniform("time_f", 0.0f); program->setUniform("alpha", 1.0f); GLint windowSizeLoc = glGetUniformLocation(program->id(), "iResolution"); glUniform2f(windowSizeLoc, static_cast<float>(win->w), static_cast<float>(win->h)); logo.setShader(program.get()); win->console.thread_safe_print("Shader program loaded successfully from file: " + shader_path + "\n"); win->console.process_message_queue(); } else { win->console.thread_safe_print("Failed to load shader program from file: " + shader_path + "\n"); win->console.thread_safe_print("Using default shader instead.\n"); setRandomShader(win, 0); win->console.process_message_queue(); } } void draw(gl::GLWindow *win) override { processMainThreadTasks(win); win->makeCurrent(); glDisable(GL_DEPTH_TEST); Uint32 currentTime = SDL_GetTicks(); float deltaTime = (currentTime - lastUpdateTime) / 1000.0f; lastUpdateTime = currentTime; if(background_shader_enabled) { logo.draw(); update(deltaTime); } else { glUseProgram(0); } } void event(gl::GLWindow *win, SDL_Event &e) override { if(e.type == SDL_KEYDOWN) { if(e.key.keysym.sym == SDLK_c && (e.key.keysym.mod & KMOD_CTRL)) { if(isMultiLineInput) { win->console.thread_safe_print("\n^C - Multi-line input cancelled\n"); multiLineBuffer.clear(); isMultiLineInput = false; blockDepth = 0; win->console.setPrompt(savedPrompt); win->console.process_message_queue(); return; } if(program_running && interrupt_command == false) { win->console.thread_safe_print("\nCTRL+C Interrupt - Command interrupted\n"); interrupt_command = true; } else { win->console.thread_safe_print("\nCTRL+C Interrupt - No Command Running\n"); } win->console.process_message_queue(); return; } if(e.key.keysym.sym == SDLK_r && (e.key.keysym.mod & KMOD_CTRL)) { win->console.thread_safe_print("\nCTRL+R - Reloading Random shader\n"); win->console.process_message_queue(); setRandomShader(win, -1); return; } if(e.key.keysym.sym == SDLK_F10) { background_shader_enabled = !background_shader_enabled; if(background_shader_enabled) { win->console.thread_safe_print("\nF10 - Shader background enabled\n"); } else { win->console.thread_safe_print("\nF10 - Shader background disabled (black screen mode)\n"); } win->console.process_message_queue(); return; } } } void update(float deltaTime) { float time_f = 0.0f; time_f = fmod(static_cast<float>(SDL_GetTicks()) / 1000.0f, 10000.0f); program->setUniform("time_f", time_f); } private: mx::Font font; gl::GLSprite logo; std::unique_ptr<gl::ShaderProgram> program; Uint32 lastUpdateTime = SDL_GetTicks(); }; class MainWindow : public gl::GLWindow { public: MainWindow(int shader_index, std::string path, int tw, int th) : gl::GLWindow("Console Skeleton", tw, th) { setPath(std::filesystem::current_path().string()+"/"+path); SDL_Surface *icon = png::LoadPNG(util.getFilePath("data/term.icon.png").c_str()); if(icon) { setWindowIcon(icon); SDL_FreeSurface(icon); } else { std::cerr << "Failed to load window icon." << std::endl; } setObject(new Game(shader_index)); activateConsole({25, 25, tw-50, th-50}, util.getFilePath("data/font.ttf"), 16, {255, 255, 255, 255}); showConsole(true); object->load(this); } ~MainWindow() override {} virtual void event(SDL_Event &e) override {} virtual void draw() override { glClearColor(0.0f, 0.0f, 0.0f, 1.0f); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glEnable(GL_DEPTH_TEST); glViewport(0, 0, w, h); object->draw(this); swap(); delay(); } }; MainWindow *main_w = nullptr; void eventProc() { main_w->proc(); } struct CustomArguments : Arguments { int shader_index = -1; }; CustomArguments proc_custom_args(int &argc, char **argv) { CustomArguments args; Argz<std::string> parser(argc, argv); parser.addOptionSingle('h', "Display help message") .addOptionDouble('H', "help", "Display help message") .addOptionSingle('v', "Display version") .addOptionDouble('V', "version", "Display version") .addOptionSingleValue('p', "assets path") .addOptionDoubleValue('P', "path", "assets path") .addOptionSingleValue('r',"Resolution WidthxHeight") .addOptionDoubleValue('R',"resolution", "Resolution WidthxHeight") .addOptionDoubleValue('S', "shader", "Shader index") .addOptionSingle('s', "shader index") #ifdef _WIN32 .addOptionSingle('w', "Use WSL") .addOptionDouble('W', "wsl", "Use WSL") #endif .addOptionSingle('f', "fullscreen") .addOptionDouble('F', "fullscreen", "fullscreen"); Argument<std::string> arg; std::string path; int value = 0; int tw = 1280, th = 720; bool fullscreen = false; int shader_index = -1; try { while((value = parser.proc(arg)) != -1) { switch(value) { #ifdef _WIN32 case 'w': case 'W': if(!std::filesystem::exists("C:\\Windows\\System32\\wsl.exe")) { std::cerr << "mx: WSL not found, please install WSL to use this feature.\n"; std::cerr.flush(); exit(EXIT_FAILURE); } mx::system_out << "mx: WSL enabled\n"; mx::system_out.flush(); cmd::cmd_type = "wsl.exe"; break; #endif case 'h': case 'H': case 'v': case 'V': std::cout << "mx: Console Skeleton Example\n"; std::cout << "mx: Version " << version_string << "\n"; parser.help(std::cout); exit(EXIT_SUCCESS); break; case 'p': case 'P': path = arg.arg_value; break; case 'r': case 'R': { auto pos = arg.arg_value.find("x"); if(pos == std::string::npos) { std::cerr << "Error invalid resolution use WidthxHeight\n"; std::cerr.flush(); exit(EXIT_FAILURE); } std::string left, right; left = arg.arg_value.substr(0, pos); right = arg.arg_value.substr(pos+1); tw = atoi(left.c_str()); th = atoi(right.c_str()); } break; case 'f': case 'F': fullscreen = true; break; case 's': case 'S': shader_index = atoi(arg.arg_value.c_str()); break; } } } catch (const ArgException<std::string>& e) { std::cerr << "mx: Argument Exception" << e.text() << std::endl; args.width = 1280; args.height = 720; args.path = "."; args.fullscreen = false; args.shader_index = -1; return args; } if(path.empty()) { std::cerr << "mx: No path provided trying default current directory.\n"; path = "."; } args.width = tw; args.height = th; args.path = path; args.fullscreen = fullscreen; args.shader_index = shader_index; return args; } int main(int argc, char **argv) { cmd::AstExecutor::getExecutor().setInterrupt(nullptr); #ifdef __EMSCRIPTEN__ try { MainWindow main_window("/", 1920, 1080); main_w = &main_window; emscripten_set_main_loop(eventProc, 0, 1); } catch(const mx::Exception &e) { mx::system_err << "mx: Exception: " << e.text() << "\n"; mx::system_err.flush(); return EXIT_FAILURE; } #else CustomArguments args = proc_custom_args(argc, argv); try { MainWindow main_window(args.shader_index, args.path, args.width, args.height); if(args.fullscreen) main_window.setFullScreen(true); main_window.loop(); } catch(const mx::Exception &e) { mx::system_err << "mx: Exception: " << e.text() << "\n"; mx::system_err.flush(); return EXIT_FAILURE; } #endif return 0; }