#include #include #include #include #include #include #include "memory.h" #include "cpu.h" #include "audio.h" #include "editor.h" #include "loader.h" #include "timing.h" #include "image.h" #include "graphics.h" #include "menu.h" #define MAX_TERM_COLS 80 #define MAX_TERM_ROWS 47 #define CMD_LINE_ROW 48 #define MAX_HISTORY_CMD 50 #define MAX_COMMAND_CHARS 79 namespace Terminal { const int _screenMaxIndex = (SCREEN_HEIGHT - (FONT_HEIGHT+2)*1)/(FONT_HEIGHT+2); const std::string _eraseLine = std::string(MAX_COMMAND_CHARS+1, 32); enum MenuItem {MenuCopy=0, MenuAll, MenuCut, MenuDel}; bool _terminalModeGiga = false; bool _waitForPromptGiga = false; int _scrollOffset = 0; int _scrollDelta = 0; int _scrollIndex = 0; int _commandHistoryIndex = 0; int _commandCharIndex = 0; std::string _commandLine; std::vector _commandLineHistory; std::vector _selectedText; std::vector _terminalText; void initialise(void) { std::vector items = {"TERM ", "Copy ", "All ", "Cut ", "Delete"}; Menu::createMenu("Terminal", items, 6, 5); } void sendCommandToGiga(const std::string& cmd) { if(_waitForPromptGiga) return; for(int i=0; i= int(_commandLineHistory.size())) _commandHistoryIndex = int(_commandLineHistory.size()) - 1; _commandLine = _commandLineHistory[_commandHistoryIndex]; _commandCharIndex = int(_commandLine.size()); } void prevCommandLineChar(void) { if(--_commandCharIndex < 0) _commandCharIndex = 0; } void nextCommandLineChar(void) { if(++_commandCharIndex > int(_commandLine.size())) _commandCharIndex = int(_commandLine.size()); } void backspaceCommandLineChar(void) { if(_commandLine.size() && _commandCharIndex > 0 && _commandCharIndex <= int(_commandLine.size())) { _commandLine.erase(--_commandCharIndex, 1); } } void deleteCommandLineChar(void) { if(_commandLine.size() && _commandCharIndex >= 0 && _commandCharIndex < int(_commandLine.size())) { _commandLine.erase(_commandCharIndex, 1); if(_commandCharIndex > int(_commandLine.size())) _commandCharIndex = int(_commandLine.size()); } } void copyCommandLineToHistory(void) { if(_commandLineHistory.size() < MAX_HISTORY_CMD) { _commandLineHistory.push_back(_commandLine); _commandHistoryIndex = int(_commandLineHistory.size()); } else { _commandLineHistory.push_back(_commandLine); _commandLineHistory.erase(_commandLineHistory.begin()); _commandHistoryIndex = int(_commandLineHistory.size()) - 1; } clearCommandLine(); } void copyTextToClipboard(void) { int clipboardTextSize = 0; for(int i=0; i= _scrollIndex) _scrollOffset = _scrollIndex; if(_scrollOffset < 0) _scrollOffset = 0; Graphics::clearScreen(0x22222222); Graphics::drawText(_eraseLine, 0, _screenMaxIndex*(FONT_HEIGHT+2)+1, 0x55555555, true, MAX_COMMAND_CHARS+1); // Terminal text for(int i=_scrollOffset; i= _screenMaxIndex) break; bool invert = false; for(int j=0; j= int(commandLine.size())) { _commandCharIndex = int(commandLine.size()); commandLine += char(32); } // Flash cursor static uint8_t toggle = 0; bool invert = ((toggle++) >>4) & 1; Graphics::drawText(commandLine, FONT_WIDTH, _screenMaxIndex*(FONT_HEIGHT+2)+1, 0xFFFFFFFF, invert, 1, _commandCharIndex); } void handleGuiEvents(SDL_Event& event) { switch(event.type) { case SDL_WINDOWEVENT: { switch(event.window.event) { case SDL_WINDOWEVENT_RESIZED: case SDL_WINDOWEVENT_SIZE_CHANGED: { Graphics::setWidthHeight(event.window.data1, event.window.data2); } break; default: break; } } break; case SDL_QUIT: { exitTerminalModeGiga(); Cpu::shutdown(); exit(0); } default: break; } } void handleMouseLeftButtonDown(int mouseX, int mouseY) { // Normalised mouse position float mx = float(mouseX) / float(Graphics::getWidth()); float my = float(mouseY) / float(Graphics::getHeight()); mouseX = int(mx * float(SCREEN_WIDTH)); mouseY = int(my * float(SCREEN_HEIGHT)); //fprintf(stderr, "%d %d %f %f\n", mouseX, mouseY, mx, my); int terminalTextSelected = (mouseY + 2) / (FONT_HEIGHT+2) + _scrollOffset; // Save selected text line as long as it doesn't already exist bool saveText = true; for(int i=0; i selectedMax) selectedMax = _selectedText[i]; if(_selectedText[i] < selectedMin) selectedMin = _selectedText[i]; } for(int i=selectedMin+1; i selectedMax) { _terminalText.erase(_terminalText.begin() + selectedMin, _terminalText.begin() + selectedMax + 1); } } _selectedText.clear(); } break; case MenuDel: { if(_terminalText.size() && _selectedText.size()) { // Sort selected text std::sort(_selectedText.begin(), _selectedText.end()); // Delete selected int selectedMin = _selectedText[0]; int selectedMax = _selectedText.back(); if(int(_terminalText.size()) > selectedMax) { _terminalText.erase(_terminalText.begin() + selectedMin, _terminalText.begin() + selectedMax + 1); } } _selectedText.clear(); } break; default: break; } } void handleMouseWheel(const SDL_Event& event) { if(event.wheel.y > 0) _scrollOffset -= 1; if(event.wheel.y < 0) _scrollOffset += 1; } void handleKey(const SDL_Event& event) { char keyCode = event.text.text[0]; if(_terminalModeGiga) Loader::sendCharGiga(keyCode); if(keyCode >= 32 && keyCode <= 126) { _commandLine.insert(_commandLine.begin() + _commandCharIndex, char(keyCode)); _commandCharIndex++; if(_commandLine.size() >= MAX_COMMAND_CHARS) copyCommandLineToHistory(); } } void handleKeyDown(SDL_Keycode keyCode, Uint16 keyMod) { // Leave terminal mode if(keyCode == Editor::getEmulatorScanCode("Terminal") && keyMod == Editor::getEmulatorKeyMod("Terminal")) { exitTerminalModeGiga(); Editor::setEditorToPrevMode(); } // Quit else if(keyCode == Editor::getEmulatorScanCode("Quit") && keyMod == Editor::getEmulatorKeyMod("Quit")) { exitTerminalModeGiga(); Cpu::shutdown(); exit(0); } // Image editor else if(keyCode == Editor::getEmulatorScanCode("ImageEditor") && keyMod == Editor::getEmulatorKeyMod("ImageEditor")) { Editor::setEditorMode(Editor::Image); } // Audio editor else if(keyCode == Editor::getEmulatorScanCode("AudioEditor") && keyMod == Editor::getEmulatorKeyMod("AudioEditor")) { Editor::setEditorMode(Editor::Audio); } // No modifier keys static bool useTerminalHistory = false; if(keyMod == 0x0000) { // Both modes switch(keyCode) { case SDLK_PAGEUP: _scrollOffset -= _screenMaxIndex; break; case SDLK_PAGEDOWN: _scrollOffset += _screenMaxIndex; break; case SDLK_HOME: _scrollOffset = 0; break; case SDLK_END: _scrollOffset = _scrollIndex; break; default: break; } // Normal mode if(!_terminalModeGiga) { switch(keyCode) { case SDLK_UP: prevCommandLineHistory(); break; case SDLK_DOWN: nextCommandLineHistory(); break; case SDLK_LEFT: prevCommandLineChar(); break; case SDLK_RIGHT: nextCommandLineChar(); break; case SDLK_DELETE: deleteCommandLineChar(); break; case SDLK_BACKSPACE: backspaceCommandLineChar(); break; case '\r': case '\n': { if(!_terminalModeGiga && _commandLine.size() == 1 && (_commandLine[0] == 't' || _commandLine[0] == 'T')) { _terminalModeGiga = true; sendCommandToGiga(_commandLine + "\n"); clearCommandLine(); clearHistoryCommandLine(); } else { sendCommandToGiga(_commandLine + "\n"); copyCommandLineToHistory(); } } break; default: break; } } // Terminal mode else { switch(keyCode) { case SDLK_UP: Loader::sendCharGiga(27); Loader::sendCharGiga('['); Loader::sendCharGiga('A'); break; case SDLK_DOWN: Loader::sendCharGiga(27); Loader::sendCharGiga('['); Loader::sendCharGiga('B'); break; case SDLK_RIGHT: Loader::sendCharGiga(27); Loader::sendCharGiga('['); Loader::sendCharGiga('C'); break; case SDLK_LEFT: Loader::sendCharGiga(27); Loader::sendCharGiga('['); Loader::sendCharGiga('D'); break; case SDLK_TAB: Loader::sendCharGiga(char(keyCode)); break; case SDLK_DELETE: Loader::sendCharGiga(char(keyCode)); backspaceCommandLineChar(); break; case '\r': case '\n': { if(useTerminalHistory) { useTerminalHistory = false; for(int i=0; i= 32 && chr <= 126) line.push_back(chr); if(chr == '\r' || chr == '\n') { if(line.size()) { _terminalText.push_back(line); _scrollDelta = 1; } line.clear(); } } } // Scroll text one line at a time until end is reached, (jump to end - delta if scroll index is not at end) if(_scrollDelta) { _scrollOffset = _scrollIndex - _scrollDelta + 2; _scrollDelta--; } printTerminal(); switch(mouseState._state) { case SDL_BUTTON_LEFT: handleMouseLeftButtonDown(mouseState._x, mouseState._y); break; case SDL_BUTTON_X1: handleMouseRightButtonDown(mouseState._x, mouseState._y); break; default: break; } } void process(void) { handleInput(); Graphics::render(true); } }