From b8807232b8ff2cd6736536c233a007bf23ab8300 Mon Sep 17 00:00:00 2001
From: Thomas Jentzsch
-
Key
Function
+ Key
-
Control + S
Step
+ Control + S
-
Control + T
Trace
+ Control + T
-
Control + L
Scan +1
+ Control + L
-
Control + F
Frame +1
+ Control + F
-
Alt + Left arrow
Rewind 1
+ Alt + Left arrow
-
Shift-Alt + Left arrow
Rewind 10
+ Shift-Alt + Left arrow
-
Alt + Down arrow
Rewind all
+ Alt + Down arrow
-
Alt + Right arrow
Unwind 1
+ Alt + Right arrow
-
Shift-Alt + Right arrow
Unwind 10
+ Shift-Alt + Right arrow
-
Alt + Up arrow
Unwind all
+ Alt + Up arrow
-
Backquote (`)
Run, exits debugger
+ Backquote (`)
Key | Function |
---|---|
Home | Move cursor to beginning of line |
End | Move cursor to end of line |
Delete | Remove character to right of cursor |
Backspace | Remove character to left of cursor |
Control + A | Same function as 'Home' |
Control + E | Same function as 'End' |
Control + D | Same function as 'Delete' |
Control + K | Remove all characters from cursor to end of line |
Control + U | Remove all characters from cursor to beginning of line |
Control + W | Remove entire word to left of cursor |
Shift + PgUp | Scroll up through previous commands one screen/page |
Shift + PgDown | Scroll down through previous commands one screen/page |
Shift + Up | Scroll up through previous commands one line |
Shift + Down | Scroll down through previous commands one line |
Shift + Home | Scroll to beginning of commands |
Shift + End | Scroll to end of commands |
Function | Key |
Move cursor to beginning of line | Home |
Move cursor to end of line | End |
Remove character to right of cursor | Delete |
Remove character to left of cursor | Backspace |
Same function as 'Home' | Control + A |
Same function as 'End' | Control + E |
Same function as 'Delete' | Control + D |
Remove all characters from cursor to end of line | Control + K |
Remove all characters from cursor to beginning of line | Control + U |
Remove entire word to left of cursor | Control + W |
Copy current line | Control + C |
Cut current line | Control + X |
Paste over current line | Control + V |
Scroll up through previous commands one screen/page | Shift + PgUp |
Scroll down through previous commands one screen/page | Shift + PgDown |
Scroll up through previous commands one line | Shift + Up |
Scroll down through previous commands one line | Shift + Down |
Scroll to beginning of commands | Shift + Home |
Scroll to end of commands | Shift + End |
You can also scroll with the mouse. Copy and paste is not yet supported.
+You can also scroll with the mouse. Copy and paste is currently only supported for a complete line.
To see the available commands, enter "help". For extended help, type "help cmd", where 'cmd' is the command you wish to know about. The available commands are listed diff --git a/src/common/PKeyboardHandler.cxx b/src/common/PKeyboardHandler.cxx index a3bfb5720..281096854 100644 --- a/src/common/PKeyboardHandler.cxx +++ b/src/common/PKeyboardHandler.cxx @@ -65,7 +65,10 @@ PhysicalKeyboardHandler::PhysicalKeyboardHandler(OSystem& system, EventHandler& setDefaultMapping(Event::NoType, EventMode::kMenuMode, updateDefaults); #ifdef GUI_SUPPORT setDefaultMapping(Event::NoType, EventMode::kEditMode, updateDefaults); -#endif // DEBUG +#endif +#ifdef DEBUGGER_SUPPORT + setDefaultMapping(Event::NoType, EventMode::kPromptMode, updateDefaults); +#endif } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -195,6 +198,13 @@ void PhysicalKeyboardHandler::setDefaultMapping(Event::Type event, EventMode mod setDefaultKey(item, event, EventMode::kEditMode); break; #endif + #ifdef DEBUGGER_SUPPORT + case EventMode::kPromptMode: + // Edit mode events are always set because they are not saved + for(const auto& item : FixedPromptMapping) + setDefaultKey(item, event, EventMode::kPromptMode); + break; + #endif default: break; @@ -470,7 +480,9 @@ bool PhysicalKeyboardHandler::addMapping(Event::Type event, EventMode mode, myKeyMap.erase(EventMode::kKeypadMode, key, mod); myKeyMap.erase(EventMode::kCompuMateMode, key, mod); } - else if(evMode != EventMode::kMenuMode && evMode != EventMode::kEditMode) + else if(evMode != EventMode::kMenuMode + && evMode != EventMode::kEditMode + && evMode != EventMode::kPromptMode) { // erase identical mapping for kCommonMode myKeyMap.erase(EventMode::kCommonMode, key, mod); @@ -899,7 +911,28 @@ PhysicalKeyboardHandler::FixedEditMapping = { {Event::EndEdit, KBDK_KP_ENTER}, {Event::AbortEdit, KBDK_ESCAPE}, }; -#endif +#endif // GUI_SUPPORT + +#ifdef DEBUGGER_SUPPORT +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +PhysicalKeyboardHandler::EventMappingArray +PhysicalKeyboardHandler::FixedPromptMapping = { + {Event::UINavNext, KBDK_TAB}, + {Event::UINavPrev, KBDK_TAB, KBDM_SHIFT}, + {Event::UIPgUp, KBDK_PAGEUP}, + {Event::UIPgUp, KBDK_PAGEUP, KBDM_SHIFT}, + {Event::UIPgDown, KBDK_PAGEDOWN}, + {Event::UIPgDown, KBDK_PAGEDOWN, KBDM_SHIFT}, + {Event::UIHome, KBDK_HOME, KBDM_SHIFT}, + {Event::UIEnd, KBDK_END, KBDM_SHIFT}, + {Event::UIUp, KBDK_UP, KBDM_SHIFT}, + {Event::UIDown, KBDK_DOWN, KBDM_SHIFT}, + {Event::UILeft, KBDK_DOWN}, + {Event::UIRight, KBDK_UP}, + +}; +#endif // DEBUGGER_SUPPORT + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - PhysicalKeyboardHandler::EventMappingArray PhysicalKeyboardHandler::DefaultJoystickMapping = { diff --git a/src/common/PKeyboardHandler.hxx b/src/common/PKeyboardHandler.hxx index ed8e4c190..6ad851b48 100644 --- a/src/common/PKeyboardHandler.hxx +++ b/src/common/PKeyboardHandler.hxx @@ -149,6 +149,9 @@ class PhysicalKeyboardHandler static EventMappingArray DefaultMenuMapping; #ifdef GUI_SUPPORT static EventMappingArray FixedEditMapping; + #endif + #ifdef DEBUGGER_SUPPORT + static EventMappingArray FixedPromptMapping; #endif static EventMappingArray DefaultCommonMapping; // Controller specific mappings diff --git a/src/common/jsonDefinitions.hxx b/src/common/jsonDefinitions.hxx index ad7e360ee..e1786a249 100644 --- a/src/common/jsonDefinitions.hxx +++ b/src/common/jsonDefinitions.hxx @@ -48,6 +48,7 @@ NLOHMANN_JSON_SERIALIZE_ENUM(JoyHatDir, { NLOHMANN_JSON_SERIALIZE_ENUM(EventMode, { {EventMode::kEditMode, "kEditMode"}, + {EventMode::kPromptMode, "kPromptMode"}, {EventMode::kMenuMode, "kMenuMode"}, {EventMode::kEmulationMode, "kEmulationMode"}, {EventMode::kJoystickMode, "kJoystickMode"}, diff --git a/src/debugger/gui/PromptWidget.cxx b/src/debugger/gui/PromptWidget.cxx index e765cce66..f75107a84 100644 --- a/src/debugger/gui/PromptWidget.cxx +++ b/src/debugger/gui/PromptWidget.cxx @@ -158,291 +158,158 @@ bool PromptWidget::handleText(char text) bool PromptWidget::handleKeyDown(StellaKey key, StellaMod mod) { bool handled = true; - bool dirty = false; + bool dirty = true; if(key != KBDK_TAB && !StellaModTest::isShift(mod)) _tabCount = -1; - switch(key) + // Uses normal edit events + special prompt events + Event::Type event = instance().eventHandler().eventForKey(EventMode::kEditMode, key, mod); + if(event == Event::NoType) + event = instance().eventHandler().eventForKey(EventMode::kPromptMode, key, mod); + + switch(event) { - case KBDK_RETURN: - case KBDK_KP_ENTER: + case Event::EndEdit: { - nextLine(); - - assert(_promptEndPos >= _promptStartPos); - int len = _promptEndPos - _promptStartPos; - - if (len > 0) - { - // Copy the user input to command - string command; - for (int i = 0; i < len; i++) - command += buffer(_promptStartPos + i) & 0x7f; - - // Add the input to the history - addToHistory(command.c_str()); - - // Pass the command to the debugger, and print the result - string result = instance().debugger().run(command); - - // This is a bit of a hack - // Certain commands remove the debugger dialog from underneath us, - // so we shouldn't print any messages - // Those commands will return '_EXIT_DEBUGGER' as their result - if(result == "_EXIT_DEBUGGER") - { - _exitedEarly = true; - return true; - } - else if(result == "_NO_PROMPT") - return true; - else if(result != "") - print(result + "\n"); - } + if(execute()) + return true; printPrompt(); - dirty = true; break; } - case KBDK_TAB: - { - // Tab completion: we complete either commands or labels, but not - // both at once. - - if(_currentPos <= _promptStartPos) - break; // no input - - scrollToCurrent(); - - int len = _promptEndPos - _promptStartPos; - - if(_tabCount != -1) - len = int(strlen(_inputStr)); - if(len > 255) - len = 255; - - int lastDelimPos = -1; - char delimiter = '\0'; - - for(int i = 0; i < len; i++) - { - // copy the input at first tab press only - if(_tabCount == -1) - _inputStr[i] = buffer(_promptStartPos + i) & 0x7f; - // whitespace characters - if(strchr("{*@<> =[]()+-/&|!^~%", _inputStr[i])) - { - lastDelimPos = i; - delimiter = _inputStr[i]; - } - } - if(_tabCount == -1) - _inputStr[len] = '\0'; - - StringList list; - - if(lastDelimPos == -1) - // no delimiters, do only command completion: - instance().debugger().parser().getCompletions(_inputStr, list); - else - { - size_t strLen = len - lastDelimPos - 1; - // do not show ALL commands/labels without any filter as it makes no sense - if(strLen > 0) - { - // Special case for 'help' command - if(BSPF::startsWithIgnoreCase(_inputStr, "help")) - instance().debugger().parser().getCompletions(_inputStr + lastDelimPos + 1, list); - else - { - // we got a delimiter, so this must be a label or a function - const Debugger& dbg = instance().debugger(); - - dbg.cartDebug().getCompletions(_inputStr + lastDelimPos + 1, list); - dbg.getCompletions(_inputStr + lastDelimPos + 1, list); - } - } - - } - if(list.size() < 1) - break; - sort(list.begin(), list.end()); - - if(StellaModTest::isShift(mod)) - { - if(--_tabCount < 0) - _tabCount = int(list.size()) - 1; - } - else - _tabCount = (++_tabCount) % list.size(); - - nextLine(); - _currentPos = _promptStartPos; - killLine(1); // kill whole line - - // start with-autocompleted, fixed string... - for(int i = 0; i < lastDelimPos; i++) - putcharIntern(_inputStr[i]); - if(lastDelimPos > 0) - putcharIntern(delimiter); - - // ...and add current autocompletion string - print(list[_tabCount]); - putcharIntern(' '); - _promptEndPos = _currentPos; - - dirty = true; + case Event::UINavNext: + dirty = autoComplete(+1); break; - } - case KBDK_BACKSPACE: - if (_currentPos > _promptStartPos) + case Event::UINavPrev: + dirty = autoComplete(-1); + break; + + case Event::UILeft: + historyScroll(-1); + break; + + case Event::UIRight: + historyScroll(+1); + break; + + case Event::Backspace: + if(_currentPos > _promptStartPos) killChar(-1); scrollToCurrent(); - dirty = true; break; - case KBDK_DELETE: - case KBDK_KP_PERIOD: // actually the num delete - if(StellaModTest::isShift(mod)) - textCut(); - else - killChar(+1); - dirty = true; + case Event::Delete: + killChar(+1); break; - case KBDK_PAGEUP: - if (StellaModTest::isShift(mod)) - { - // Don't scroll up when at top of buffer - if(_scrollLine < _linesPerPage) - break; - - _scrollLine -= _linesPerPage - 1; - if (_scrollLine < _firstLineInBuffer + _linesPerPage - 1) - _scrollLine = _firstLineInBuffer + _linesPerPage - 1; - updateScrollBuffer(); - - dirty = true; - } + case Event::MoveHome: + _currentPos = _promptStartPos; break; - case KBDK_PAGEDOWN: - if (StellaModTest::isShift(mod)) - { - // Don't scroll down when at bottom of buffer - if(_scrollLine >= _promptEndPos / _lineWidth) - break; - - _scrollLine += _linesPerPage - 1; - if (_scrollLine > _promptEndPos / _lineWidth) - _scrollLine = _promptEndPos / _lineWidth; - updateScrollBuffer(); - - dirty = true; - } + case Event::MoveEnd: + _currentPos = _promptEndPos; break; - case KBDK_HOME: - if (StellaModTest::isShift(mod)) - { - _scrollLine = _firstLineInBuffer + _linesPerPage - 1; - updateScrollBuffer(); - } - else - _currentPos = _promptStartPos; - - dirty = true; - break; - - case KBDK_END: - if (StellaModTest::isShift(mod)) - { - _scrollLine = _promptEndPos / _lineWidth; - if (_scrollLine < _linesPerPage - 1) - _scrollLine = _linesPerPage - 1; - updateScrollBuffer(); - } - else - _currentPos = _promptEndPos; - - dirty = true; - break; - - case KBDK_UP: - if (StellaModTest::isShift(mod)) - { - if(_scrollLine <= _firstLineInBuffer + _linesPerPage - 1) - break; - - _scrollLine -= 1; - updateScrollBuffer(); - - dirty = true; - } - else - historyScroll(+1); - break; - - case KBDK_DOWN: - if (StellaModTest::isShift(mod)) - { - // Don't scroll down when at bottom of buffer - if(_scrollLine >= _promptEndPos / _lineWidth) - break; - - _scrollLine += 1; - updateScrollBuffer(); - - dirty = true; - } - else - historyScroll(-1); - break; - - case KBDK_RIGHT: - if (_currentPos < _promptEndPos) + case Event::MoveRightChar: + if(_currentPos < _promptEndPos) _currentPos++; - - dirty = true; break; - case KBDK_LEFT: - if (_currentPos > _promptStartPos) + case Event::MoveLeftChar: + if(_currentPos > _promptStartPos) _currentPos--; - - dirty = true; break; - case KBDK_INSERT: - if(StellaModTest::isShift(mod)) - { - textPaste(); - dirty = true; - } - else if(StellaModTest::isControl(mod)) - { - textCopy(); - dirty = true; - } - else - handled = false; + case Event::DeleteRightWord: + killChar(+1); + break; + + case Event::DeleteEnd: + killLine(+1); + break; + + case Event::DeleteHome: + killLine(-1); + break; + + case Event::DeleteLeftWord: + killWord(); + break; + + case Event::UIUp: + if(_scrollLine <= _firstLineInBuffer + _linesPerPage - 1) + break; + + _scrollLine -= 1; + updateScrollBuffer(); + break; + + case Event::UIDown: + // Don't scroll down when at bottom of buffer + if(_scrollLine >= _promptEndPos / _lineWidth) + break; + + _scrollLine += 1; + updateScrollBuffer(); + break; + + case Event::UIPgUp: + // Don't scroll up when at top of buffer + if(_scrollLine < _linesPerPage) + break; + + _scrollLine -= _linesPerPage - 1; + if(_scrollLine < _firstLineInBuffer + _linesPerPage - 1) + _scrollLine = _firstLineInBuffer + _linesPerPage - 1; + updateScrollBuffer(); + break; + + case Event::UIPgDown: + // Don't scroll down when at bottom of buffer + if(_scrollLine >= _promptEndPos / _lineWidth) + break; + + _scrollLine += _linesPerPage - 1; + if(_scrollLine > _promptEndPos / _lineWidth) + _scrollLine = _promptEndPos / _lineWidth; + updateScrollBuffer(); + break; + + case Event::UIHome: + _scrollLine = _firstLineInBuffer + _linesPerPage - 1; + updateScrollBuffer(); + break; + + case Event::UIEnd: + _scrollLine = _promptEndPos / _lineWidth; + if(_scrollLine < _linesPerPage - 1) + _scrollLine = _linesPerPage - 1; + updateScrollBuffer(); + break; + + //case Event::SelectAll: + // textSelectAll(); + // break; + + case Event::Cut: + textCut(); + break; + + case Event::Copy: + textCopy(); + break; + + case Event::Paste: + textPaste(); break; default: - if (StellaModTest::isControl(mod)) - { - specialKeys(key); - } - else if (StellaModTest::isAlt(mod)) - { - // Placeholder only - this will never be reached - } - else - handled = false; + handled = false; + dirty = false; break; } @@ -546,46 +413,6 @@ int PromptWidget::getWidth() const return _w + ScrollBarWidget::scrollBarWidth(_font); } -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void PromptWidget::specialKeys(StellaKey key) -{ - bool handled = true; - - switch(key) - { - case KBDK_D: - killChar(+1); - break; - case KBDK_K: - killLine(+1); - break; - case KBDK_U: - killLine(-1); - break; - case KBDK_W: - killWord(); - break; - case KBDK_A: - textSelectAll(); - break; - case KBDK_X: - textCut(); - break; - case KBDK_C: - textCopy(); - break; - case KBDK_V: - textPaste(); - break; - default: - handled = false; - break; - } - - if(handled) - setDirty(); -} - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void PromptWidget::killChar(int direction) { @@ -807,6 +634,135 @@ void PromptWidget::historyScroll(int direction) setDirty(); } +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +bool PromptWidget::execute() +{ + nextLine(); + + assert(_promptEndPos >= _promptStartPos); + int len = _promptEndPos - _promptStartPos; + + if(len > 0) + { + // Copy the user input to command + string command; + for(int i = 0; i < len; i++) + command += buffer(_promptStartPos + i) & 0x7f; + + // Add the input to the history + addToHistory(command.c_str()); + + // Pass the command to the debugger, and print the result + string result = instance().debugger().run(command); + + // This is a bit of a hack + // Certain commands remove the debugger dialog from underneath us, + // so we shouldn't print any messages + // Those commands will return '_EXIT_DEBUGGER' as their result + if(result == "_EXIT_DEBUGGER") + { + _exitedEarly = true; + return true; + } + else if(result == "_NO_PROMPT") + return true; + else if(result != "") + print(result + "\n"); + } + return false; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +bool PromptWidget::autoComplete(int direction) +{ + // Tab completion: we complete either commands or labels, but not + // both at once. + + if(_currentPos <= _promptStartPos) + return false; // no input + + scrollToCurrent(); + + int len = _promptEndPos - _promptStartPos; + + if(_tabCount != -1) + len = int(strlen(_inputStr)); + if(len > 255) + len = 255; + + int lastDelimPos = -1; + char delimiter = '\0'; + + for(int i = 0; i < len; i++) + { + // copy the input at first tab press only + if(_tabCount == -1) + _inputStr[i] = buffer(_promptStartPos + i) & 0x7f; + // whitespace characters + if(strchr("{*@<> =[]()+-/&|!^~%", _inputStr[i])) + { + lastDelimPos = i; + delimiter = _inputStr[i]; + } +} + if(_tabCount == -1) + _inputStr[len] = '\0'; + + StringList list; + + if(lastDelimPos == -1) + // no delimiters, do only command completion: + instance().debugger().parser().getCompletions(_inputStr, list); + else + { + size_t strLen = len - lastDelimPos - 1; + // do not show ALL commands/labels without any filter as it makes no sense + if(strLen > 0) + { + // Special case for 'help' command + if(BSPF::startsWithIgnoreCase(_inputStr, "help")) + instance().debugger().parser().getCompletions(_inputStr + lastDelimPos + 1, list); + else + { + // we got a delimiter, so this must be a label or a function + const Debugger& dbg = instance().debugger(); + + dbg.cartDebug().getCompletions(_inputStr + lastDelimPos + 1, list); + dbg.getCompletions(_inputStr + lastDelimPos + 1, list); + } + } + + } + if(list.size() < 1) + return false; + sort(list.begin(), list.end()); + + if(direction < 0) + { + if(--_tabCount < 0) + _tabCount = int(list.size()) - 1; + } + else + _tabCount = (++_tabCount) % list.size(); + + nextLine(); + _currentPos = _promptStartPos; + killLine(1); // kill whole line + + // start with-autocompleted, fixed string... + for(int i = 0; i < lastDelimPos; i++) + putcharIntern(_inputStr[i]); + if(lastDelimPos > 0) + putcharIntern(delimiter); + + // ...and add current autocompletion string + print(list[_tabCount]); + putcharIntern(' '); + _promptEndPos = _currentPos; + + return true; +} + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void PromptWidget::nextLine() { @@ -971,29 +927,6 @@ string PromptWidget::saveBuffer(const FilesystemNode& file) return "unable to save session"; } -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -string PromptWidget::getCompletionPrefix(const StringList& completions) -{ - // Find the number of characters matching for each of the completions provided - for(uInt32 len = 1;; ++len) - { - for(uInt32 i = 0; i < completions.size(); ++i) - { - string s1 = completions[i]; - if(s1.length() < len) - { - return s1.substr(0, len - 1); - } - string find = s1.substr(0, len); - - for(uInt32 j = i + 1; j < completions.size(); ++j) - { - if(!BSPF::startsWithIgnoreCase(completions[j], find)) - return s1.substr(0, len - 1); - } - } - } -} // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void PromptWidget::clearScreen() diff --git a/src/debugger/gui/PromptWidget.hxx b/src/debugger/gui/PromptWidget.hxx index 3111298e8..b606be094 100644 --- a/src/debugger/gui/PromptWidget.hxx +++ b/src/debugger/gui/PromptWidget.hxx @@ -64,7 +64,6 @@ class PromptWidget : public Widget, public CommandSender void scrollToCurrent(); // Line editing - void specialKeys(StellaKey key); void nextLine(); void killChar(int direction); void killLine(int direction); @@ -80,6 +79,9 @@ class PromptWidget : public Widget, public CommandSender // History void historyScroll(int direction); + bool execute(); + bool autoComplete(int direction); + void handleMouseDown(int x, int y, MouseButton b, int clickCount) override; void handleMouseWheel(int x, int y, int direction) override; bool handleText(char text) override; @@ -92,10 +94,6 @@ class PromptWidget : public Widget, public CommandSender bool wantsFocus() const override { return true; } void loadConfig() override; - private: - // Get the longest prefix (initially 's') that is in every string in the list - string getCompletionPrefix(const StringList& completions); - private: enum { kBufferSize = 32768, diff --git a/src/emucore/EventHandlerConstants.hxx b/src/emucore/EventHandlerConstants.hxx index 64b8a88d3..8cb964cbb 100644 --- a/src/emucore/EventHandlerConstants.hxx +++ b/src/emucore/EventHandlerConstants.hxx @@ -84,6 +84,7 @@ enum class EventMode { kCompuMateMode, // cannot be remapped kCommonMode, // mapping common between controllers kEditMode, // mapping used in editable widgets + kPromptMode, // extra mappings used in debugger's prompt widget kNumModes };