package gui.screens; import com.badlogic.gdx.Gdx; import com.badlogic.gdx.Input; import com.badlogic.gdx.graphics.OrthographicCamera; import com.badlogic.gdx.scenes.scene2d.InputEvent; import com.badlogic.gdx.scenes.scene2d.InputListener; import com.badlogic.gdx.scenes.scene2d.Stage; import com.badlogic.gdx.scenes.scene2d.ui.Table; import com.badlogic.gdx.utils.viewport.FitViewport; import game.Hakd; import game.MenuTerminal; import gui.input.GdxInputDecoder; import jline.*; import joptsimple.OptionParser; import org.python.core.Py; import org.python.core.PyString; import org.python.util.PythonInterpreter; import other.Util; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.Writer; import java.util.Arrays; import java.util.HashSet; import java.util.Queue; import java.util.Set; import java.util.concurrent.ConcurrentLinkedQueue; public class MenuScreen extends HakdScreen { private final Stage stage; private final Table canvas; private ConsoleReader consoleReader; private MenuTerminal terminal; private Queue<Integer> inputQueue; private InputStream in; private MenuOutput out; private float timer = 0; private final SimpleCompletor firstArgumentCompletor = new SimpleCompletor(listFileNames(Gdx.files.internal(Util.ASSETS + "/python/menu/").file().listFiles())); private Completor argumentCompletor = new ArgumentCompletor(firstArgumentCompletor); public MenuScreen(Hakd game) { super(game); cam = new OrthographicCamera(); ((OrthographicCamera) cam).setToOrtho(false, width, height); cam.update(); stage = new Stage(new FitViewport(Gdx.graphics.getWidth(), Gdx.graphics.getHeight(), cam)); canvas = new Table(); stage.addActor(canvas); canvas.setSize(Gdx.graphics.getWidth(), Gdx.graphics.getHeight()); terminal = new MenuTerminal(); inputQueue = new ConcurrentLinkedQueue<Integer>(); in = new MenuInput(); out = new MenuOutput(); try { consoleReader = new ConsoleReader(in, out, null, terminal); terminal.initializeTerminal(); } catch (IOException e) { e.printStackTrace(); } catch (Exception e) { e.printStackTrace(); } terminal.setConsoleReader(consoleReader); terminal.setMenuScreen(this); consoleReader.setDefaultPrompt("[#38FF4C]" + System.getProperty("user.name") + "[] @ [#FFC123]127.0.0.1[] [#4B6ABF]~[] [#E54235]$[] "); consoleReader.addCompletor(argumentCompletor); } @Override public void show() { super.show(); Gdx.input.setInputProcessor(stage); StartTerminal(); canvas.add(terminal.getScroll()).expand().fill(); try { consoleReader.redrawLine(); } catch (IOException e) { e.printStackTrace(); } } private void StartTerminal() { stage.addListener(new InputListener() { @Override public boolean touchDown(InputEvent event, float x, float y, int pointer, int button) { terminal.setAtBottom(false); return true; } @Override public boolean keyDown(InputEvent event, int keycode) { return true; } @Override public boolean keyUp(InputEvent event, int keycode) { return true; } @Override public boolean keyTyped(InputEvent event, char character) { int keycode = event.getKeyCode(); timer = 0; if (terminal.isCommandRunning()) { return true; } int modifiers = 0; // using binary to pass non-mutually exclusive arguments if (Gdx.input.isKeyPressed(Input.Keys.SHIFT_LEFT) || Gdx.input.isKeyPressed(Input.Keys.SHIFT_RIGHT)) { modifiers |= GdxInputDecoder.ModifierKeys.SHIFT.value; } if (Gdx.input.isKeyPressed(Input.Keys.ALT_LEFT) || Gdx.input.isKeyPressed(Input.Keys.ALT_RIGHT)) { modifiers |= GdxInputDecoder.ModifierKeys.ALT.value; } if (Gdx.input.isKeyPressed(Input.Keys.CONTROL_LEFT) || Gdx.input.isKeyPressed(Input.Keys.CONTROL_RIGHT)) { modifiers |= GdxInputDecoder.ModifierKeys.CONTROL.value; } int jlineCode = character >= 32 && !GdxInputDecoder.contains(keycode, modifiers) ? character : GdxInputDecoder.getJlineCode(keycode, modifiers); // various stuff to do before writing to screen or before adding characters to the queue if (keycode == 66) { // enter jlineCode = 10; } else if (jlineCode == '[' || jlineCode == ']') { // the user may not enter brackets jlineCode = ConsoleOperations.UNKNOWN; // due to the blink character messing with gdx color markup } else if (keycode == 61) { consoleReader.removeCompletor(argumentCompletor); argumentCompletor = new ArgumentCompletor(new Completor[]{firstArgumentCompletor, OptionsCompletor()}); consoleReader.addCompletor(argumentCompletor); } inputQueue.add(jlineCode); if (jlineCode == '\n') { terminal.setCommandStarted(true); } String line = null; try { line = consoleReader.readLine(); } catch (IOException e) { e.printStackTrace(); } if (jlineCode == '\n') { terminal.run(line); } try { consoleReader.redrawLine(); } catch (IOException e) { e.printStackTrace(); } return true; } }); // input.addListener(new InputListener() { // @Override // public boolean keyTyped(InputEvent event, char character) { // if (commandRunning) { // line = history.size(); // input.setText("Press Control and C to cancel"); // input.setDisabled(true); // } // return true; // } // // @Override // public boolean keyDown(InputEvent event, int keycode) { // if (keycode == Input.Keys.ENTER && !commandRunning) { // display.setText(display.getText() + "\n\n[#BF5C2B]" + System.getProperty("user.name") + "[] @ 127.0.0.1" + " : ~" + "\n$ " + input.getText()); // history.add(input.getText()); // // Command(input.getText()); // // commandRunning = true; // line = history.size(); // input.setText(""); // } else if (keycode == Input.Keys.C && (Gdx.input.isKeyPressed(Input.Keys.CONTROL_LEFT) || Gdx.input.isKeyPressed(Input.Keys.CONTROL_RIGHT))) { // addText("^C"); // stop(); // } else if (keycode == Input.Keys.DOWN && line < history.size() - 1) { // line++; // input.setText(history.get(line)); // input.setCursorPosition(input.getText().length()); // } else if (keycode == Input.Keys.UP && line > 0) { // line--; // input.setText(history.get(line)); // input.setCursorPosition(input.getText().length()); // } // return true; // } // // @Override // public boolean keyUp(InputEvent event, int keycode) { // if (keycode == Input.Keys.ENTER || (keycode == Input.Keys.C && (Gdx.input.isKeyPressed(Input.Keys.CONTROL_LEFT) || Gdx.input.isKeyPressed(Input.Keys.CONTROL_RIGHT)) && commandRunning)) { // scroll.setScrollY(display.getHeight()); // } // return super.keyUp(event, keycode); // } // }); } @Override public void render(float delta) { super.render(delta); timer += delta; if (timer % MenuTerminal.BLINK_SPEED <= delta && !terminal.isCommandRunning()) { terminal.blink(!terminal.isBlinkCharShown()); try { consoleReader.redrawLine(); } catch (IOException e) { e.printStackTrace(); } } if (terminal.isAtBottom()) { terminal.getScroll().setScrollY(terminal.getDisplay().getHeight()); } stage.act(Gdx.graphics.getDeltaTime()); stage.draw(); } @Override public void dispose() { super.dispose(); if (terminal.isCommandRunning()) { terminal.stop(); } } private String[] listFileNames(File[] files) { Set<String> names = new HashSet<String>(); for (File f : files) { String name = f.getName(); if (name.endsWith(".py")) { names.add(name.substring(0, name.length() - 3)); } } return names.toArray(new String[names.size()]); } /** * Runs the parser code in a command to get the options available. */ private Completor OptionsCompletor() { String name = consoleReader.getCursorBuffer().getBuffer().toString(); if (name.isEmpty()) { return new NullCompletor(); } else if (name.contains(" ")) { name = name.substring(0, name.indexOf(" ")); } File[] files = Gdx.files.internal(Util.ASSETS + "/python/menu/").file().listFiles(); File file = null; if (files == null) { return new NullCompletor(); } for (File f : files) { if (f.getName().equals(name + ".py")) { file = f; } } if (file == null || !file.exists()) { return new NullCompletor(); } PythonInterpreter pi = new PythonInterpreter(); Py.getSystemState().path.append(new PyString(file.getParentFile().getAbsolutePath())); String parserCode = null; try { parserCode = Util.getParserCode(file); } catch (Exception e) { e.printStackTrace(); } pi.exec(parserCode); OptionParser parser = (OptionParser) pi.get("parser").__tojava__(OptionParser.class); Set<String> options = new HashSet<String>(parser.recognizedOptions().keySet()); Set<String> optionSet = new HashSet<String>(); if (options.contains("[arguments]")) { options.remove("[arguments]"); } for (String s : options) { if (s.length() == 1) { optionSet.add("-" + s); } else { optionSet.add("--" + s); } } return new SimpleCompletor(optionSet.toArray(new String[options.size()])); } private class MenuInput extends InputStream { @Override public int read() throws IOException { return (inputQueue.peek() != null) ? inputQueue.poll() : ConsoleOperations.UNKNOWN; // unknown is -99 } } private class MenuOutput extends Writer { @Override public void write(char[] chars, int start, int end) throws IOException { terminal.write(Arrays.copyOfRange(chars, start, end)); } @Override public void flush() throws IOException { } @Override public void close() throws IOException { } } public MenuTerminal getTerminal() { return terminal; } public Queue<Integer> getInputQueue() { return inputQueue; } public MenuOutput getOut() { return out; } }