openMSX
CommandConsole.cc
Go to the documentation of this file.
1#include "CommandConsole.hh"
2#include "CommandException.hh"
4#include "Completer.hh"
5#include "Interpreter.hh"
6#include "Keys.hh"
7#include "FileContext.hh"
8#include "FileException.hh"
9#include "FileOperations.hh"
10#include "CliComm.hh"
11#include "Display.hh"
12#include "Event.hh"
13#include "EventDistributor.hh"
14#include "Version.hh"
15#include "VideoSystem.hh"
16#include "StringOp.hh"
17#include "ScopedAssign.hh"
18#include "narrow.hh"
19#include "unreachable.hh"
20#include "utf8_unchecked.hh"
21#include "view.hh"
22#include "xrange.hh"
23#include <algorithm>
24#include <fstream>
25#include <cassert>
26
27using std::string;
28using std::string_view;
29
30namespace openmsx {
31
32// class ConsoleLine
33
34ConsoleLine::ConsoleLine(string line_, uint32_t rgb)
35 : line(std::move(line_))
36 , chunks(1, {rgb, 0})
37{
38}
39
40void ConsoleLine::addChunk(string_view text, uint32_t rgb)
41{
42 chunks.emplace_back(Chunk{rgb, line.size()});
43 line.append(text.data(), text.size());
44}
45
47{
48 return utf8::unchecked::size(line);
49}
50
51uint32_t ConsoleLine::chunkColor(size_t i) const
52{
53 assert(i < chunks.size());
54 return chunks[i].rgb;
55}
56
57string_view ConsoleLine::chunkText(size_t i) const
58{
59 assert(i < chunks.size());
60 auto pos = chunks[i].pos;
61 auto len = ((i + 1) == chunks.size())
62 ? string_view::npos
63 : chunks[i + 1].pos - pos;
64 return string_view(line).substr(pos, len);
65}
66
67// class CommandConsole
68
69static constexpr const char* const PROMPT_NEW = "> ";
70static constexpr const char* const PROMPT_CONT = "| ";
71static constexpr const char* const PROMPT_BUSY = "*busy*";
72
74 GlobalCommandController& commandController_,
75 EventDistributor& eventDistributor_,
76 Display& display_)
77 : commandController(commandController_)
78 , eventDistributor(eventDistributor_)
79 , display(display_)
80 , consoleSetting(
81 commandController, "console",
82 "turns console display on/off", false, Setting::DONT_SAVE)
83 , historySizeSetting(
84 commandController, "console_history_size",
85 "amount of commands kept in console history", 100, 0, 10000)
86 , removeDoublesSetting(
87 commandController, "console_remove_doubles",
88 "don't add the command to history if it's the same as the previous one",
89 true)
90 , prompt(PROMPT_NEW)
91 , history(std::max(1, historySizeSetting.getInt()))
92{
93 resetScrollBack();
94 newLineConsole(prompt);
95 loadHistory();
96 putPrompt();
98
99 const auto& fullVersion = Version::full();
100 print(fullVersion);
101 print(string(fullVersion.size(), '-'));
102 print("\n"
103 "General information about openMSX is available at "
104 "http://openmsx.org.\n"
105 "\n"
106 "Type 'help' to see a list of available commands "
107 "(use <PgUp>/<PgDn> to scroll).\n"
108 "Or read the Console Command Reference in the manual.\n"
109 "\n");
110
111 commandController.getInterpreter().setOutput(this);
112 eventDistributor.registerEventListener(
114 // also listen to KEY_UP events, so that we can consume them
115 eventDistributor.registerEventListener(
117}
118
120{
121 eventDistributor.unregisterEventListener(EventType::KEY_DOWN, *this);
122 eventDistributor.unregisterEventListener(EventType::KEY_UP, *this);
123 commandController.getInterpreter().setOutput(nullptr);
124 Completer::setOutput(nullptr);
125}
126
127void CommandConsole::saveHistory()
128{
129 try {
130 std::ofstream outputFile;
132 userFileContext("console").resolveCreate("history.txt"));
133 if (!outputFile) {
134 throw FileException(
135 "Error while saving the console history.");
136 }
137 for (auto& s : history) {
138 outputFile << s << '\n';
139 }
140 } catch (FileException& e) {
141 commandController.getCliComm().printWarning(e.getMessage());
142 }
143}
144
145void CommandConsole::loadHistory()
146{
147 try {
148 std::ifstream inputFile(
149 userFileContext("console").
150 resolveCreate("history.txt").c_str());
151 string line;
152 while (inputFile) {
153 getline(inputFile, line);
154 putCommandHistory(line);
155 }
156 } catch (FileException&) {
157 // Error while loading the console history, ignore
158 }
159}
160
162{
163 auto xPosition = narrow<int>(cursorPosition % getColumns());
164 auto num = lines[0].numChars() / getColumns();
165 auto yPosition = narrow<int>(num - (cursorPosition / getColumns()));
166 return {xPosition, yPosition};
167}
168
169int CommandConsole::signalEvent(const Event& event)
170{
171 if (!consoleSetting.getBoolean()) return 0;
172
173 // If the console is open then don't pass the event to the MSX
174 // (whatever the (keyboard) event is). If the event has a meaning for
175 // the console, then also don't pass the event to the hotkey system.
176 // For example PgUp, PgDown are keys that have both a meaning in the
177 // console and are used by standard key bindings.
178 return visit(overloaded{
179 [&](const KeyDownEvent& keyEvent) {
180 if (!executingCommand) {
181 if (handleEvent(keyEvent)) {
182 // event was used
183 display.repaintDelayed(40000); // 25fps
184 return EventDistributor::HOTKEY; // block HOTKEY and MSX
185 }
186 } else {
187 // For commands that take a long time to execute (e.g.
188 // a loadstate that needs to create a filePool index),
189 // we also send events during the execution (so that
190 // we can show progress on the OSD). In that case
191 // ignore extra input events.
192 }
194 },
195 [](const KeyUpEvent&) { return EventDistributor::MSX; },
196 [](const EventBase&) { UNREACHABLE; return EventDistributor::MSX; }
197 }, event);
198}
199
200bool CommandConsole::handleEvent(const KeyEvent& keyEvent)
201{
202 auto keyCode = keyEvent.getKeyCode();
203 int key = keyCode & Keys::K_MASK;
204 int mod = keyCode & ~Keys::K_MASK;
205
206 switch (mod) {
207 case Keys::KM_CTRL:
208 switch (key) {
209 case Keys::K_H:
210 backspace();
211 return true;
212 case Keys::K_A:
213 cursorPosition = unsigned(prompt.size());
214 return true;
215 case Keys::K_E:
216 cursorPosition = unsigned(lines[0].numChars());
217 return true;
218 case Keys::K_C:
219 clearCommand();
220 return true;
221 case Keys::K_L:
222 clearHistory();
223 return true;
224#ifndef __APPLE__
225 case Keys::K_V:
226 paste();
227 return true;
228#endif
229 }
230 break;
231 case Keys::KM_SHIFT:
232 switch (key) {
233 case Keys::K_PAGEUP:
234 scroll(std::max(narrow<int>(getRows()) - 1, 1));
235 return true;
236 case Keys::K_PAGEDOWN:
237 scroll(-std::max(narrow<int>(getRows()) - 1, 1));
238 return true;
239 }
240 break;
241 case Keys::KM_META:
242#ifdef __APPLE__
243 switch (key) {
244 case Keys::K_V:
245 paste();
246 return true;
247 case Keys::K_K:
248 clearHistory();
249 return true;
250 case Keys::K_LEFT:
251 cursorPosition = unsigned(prompt.size());
252 return true;
253 case Keys::K_RIGHT:
254 cursorPosition = unsigned(lines[0].numChars());
255 return true;
256 }
257#endif
258 break;
259 case Keys::KM_ALT:
260 switch (key) {
262 deleteToStartOfWord();
263 return true;
264#ifdef __APPLE__
265 case Keys::K_DELETE:
266 deleteToEndOfWord();
267 return true;
268#else
269 case Keys::K_D:
270 deleteToEndOfWord();
271 return true;
272#endif
273 case Keys::K_LEFT:
274 gotoStartOfWord();
275 return true;
276 case Keys::K_RIGHT:
277 gotoEndOfWord();
278 return true;
279 }
280 break;
281 case 0: // no modifier
282 switch (key) {
283 case Keys::K_PAGEUP:
284 scroll(1);
285 return true;
286 case Keys::K_PAGEDOWN:
287 scroll(-1);
288 return true;
289 case Keys::K_UP:
290 prevCommand();
291 return true;
292 case Keys::K_DOWN:
293 nextCommand();
294 return true;
296 backspace();
297 return true;
298 case Keys::K_DELETE:
299 delete_key();
300 return true;
301 case Keys::K_TAB:
302 tabCompletion();
303 return true;
304 case Keys::K_RETURN:
305 case Keys::K_KP_ENTER:
306 commandExecute();
307 cursorPosition = unsigned(prompt.size());
308 return true;
309 case Keys::K_LEFT:
310 if (cursorPosition > prompt.size()) {
311 --cursorPosition;
312 }
313 return true;
314 case Keys::K_RIGHT:
315 if (cursorPosition < lines[0].numChars()) {
316 ++cursorPosition;
317 }
318 return true;
319 case Keys::K_HOME:
320#ifdef __APPLE__
321 scroll(lines.size());
322#else
323 cursorPosition = unsigned(prompt.size());
324#endif
325 return true;
326 case Keys::K_END:
327#ifdef __APPLE__
328 scroll(-lines.size());
329#else
330 cursorPosition = unsigned(lines[0].numChars());
331#endif
332 return true;
333 }
334 break;
335 }
336
337 auto unicode = keyEvent.getUnicode();
338 if (!unicode || (mod & Keys::KM_META)) {
339 // Disallow META modifier for 'normal' key presses because on
340 // MacOSX Cmd+L is used as a hotkey to toggle the console.
341 // Hopefully there are no systems that require META to type
342 // normal keys. However there _are_ systems that require the
343 // following modifiers, some examples:
344 // MODE: to type '1-9' on a N900
345 // ALT: to type | [ ] on a azerty keyboard layout
346 // CTRL+ALT: to type '#' on a spanish keyboard layout (windows)
347 //
348 // Event was not used by the console, allow the other
349 // subsystems to process it. E.g. F10, or Cmd+L to close the
350 // console.
351 return false;
352 }
353
354 // Apparently on macOS keyboard events for keys like F1 have a non-zero
355 // unicode field. This confuses the console code (it thinks it's a
356 // printable character) and it prevents those keys from triggering
357 // hotkey bindings. See this bug report:
358 // https://github.com/openMSX/openMSX/issues/1095
359 // As a workaround we ignore chars in the 'Private Use Area' (PUA).
360 // https://en.wikipedia.org/wiki/Private_Use_Areas
361 if (utf8::is_pua(unicode)) {
362 return false;
363 }
364
365 if (unicode >= 0x20) {
366 normalKey(unicode);
367 } else {
368 // Skip CTRL-<X> combinations, but still return true.
369 }
370 return true;
371}
372
373void CommandConsole::output(string_view text)
374{
375 print(text);
376}
377
378unsigned CommandConsole::getOutputColumns() const
379{
380 return getColumns();
381}
382
383void CommandConsole::print(string_view text, unsigned rgb)
384{
385 while (true) {
386 auto pos = text.find('\n');
387 newLineConsole(ConsoleLine(string(text.substr(0, pos)), rgb));
388 if (pos == string_view::npos) return;
389 text = text.substr(pos + 1); // skip newline
390 if (text.empty()) return;
391 }
392}
393
394void CommandConsole::newLineConsole(string line)
395{
396 newLineConsole(ConsoleLine(std::move(line)));
397}
398
399void CommandConsole::newLineConsole(ConsoleLine line)
400{
401 if (lines.isFull()) {
402 lines.removeBack();
403 }
404 ConsoleLine tmp = std::move(lines[0]);
405 lines[0] = std::move(line);
406 lines.addFront(std::move(tmp));
407}
408
409void CommandConsole::putCommandHistory(const string& command)
410{
411 if (command.empty()) return;
412 if (removeDoublesSetting.getBoolean() && !history.empty() &&
413 (history.back() == command)) {
414 return;
415 }
416 if (history.full()) history.pop_front();
417 history.push_back(command);
418}
419
420void CommandConsole::commandExecute()
421{
422 resetScrollBack();
423 string cmd0 = lines[0].str().substr(prompt.size());
424 putCommandHistory(cmd0);
425 saveHistory(); // save at this point already, so that we don't lose history in case of a crash
426
427 strAppend(commandBuffer, cmd0, '\n');
428 newLineConsole(lines[0]);
429 if (commandController.isComplete(commandBuffer)) {
430 // Normally the busy prompt is NOT shown (not even very briefly
431 // because the screen is not redrawn), though for some commands
432 // that potentially take a long time to execute, we explicitly
433 // send events, see also comment in signalEvent().
434 prompt = PROMPT_BUSY;
435 putPrompt();
436
437 try {
438 ScopedAssign sa(executingCommand, true);
439 auto resultObj = commandController.executeCommand(
440 commandBuffer);
441 auto result = resultObj.getString();
442 if (!result.empty()) {
443 print(result);
444 }
445 } catch (CommandException& e) {
446 print(e.getMessage(), 0xff0000);
447 }
448 commandBuffer.clear();
449 prompt = PROMPT_NEW;
450 } else {
451 prompt = PROMPT_CONT;
452 }
453 putPrompt();
454}
455
456ConsoleLine CommandConsole::highLight(string_view line)
457{
458 ConsoleLine result;
459 result.addChunk(prompt, 0xffffff);
460
461 TclParser parser = commandController.getInterpreter().parse(line);
462 string colors = parser.getColors();
463 assert(colors.size() == line.size());
464
465 unsigned pos = 0;
466 while (pos != colors.size()) {
467 char col = colors[pos];
468 unsigned pos2 = pos++;
469 while ((pos != colors.size()) && (colors[pos] == col)) {
470 ++pos;
471 }
472 // TODO make these color configurable?
473 unsigned rgb = [&] {
474 switch (col) {
475 case 'E': return 0xff0000; // error
476 case 'c': return 0x5c5cff; // comment
477 case 'v': return 0x00ffff; // variable
478 case 'l': return 0xff00ff; // literal
479 case 'p': return 0xcdcd00; // proc
480 case 'o': return 0x00cdcd; // operator
481 default: return 0xffffff; // other
482 }
483 }();
484 result.addChunk(line.substr(pos2, pos - pos2), rgb);
485 }
486 return result;
487}
488
489void CommandConsole::putPrompt()
490{
491 commandScrollBack = unsigned(history.size());
492 currentLine.clear();
493 lines[0] = highLight(currentLine);
494 cursorPosition = unsigned(prompt.size());
495}
496
497void CommandConsole::tabCompletion()
498{
499 resetScrollBack();
500 auto pl = unsigned(prompt.size());
501 string front(utf8::unchecked::substr(lines[0].str(), pl, cursorPosition - pl));
502 string back (utf8::unchecked::substr(lines[0].str(), cursorPosition));
503 string newFront = commandController.tabCompletion(front);
504 cursorPosition = pl + unsigned(utf8::unchecked::size(newFront));
505 currentLine = newFront + back;
506 lines[0] = highLight(currentLine);
507}
508
509void CommandConsole::scroll(int delta)
510{
511 consoleScrollBack = std::max(std::min(consoleScrollBack + delta, int(lines.size()) - int(rows)), 0);
512}
513
514// Returns the position of the start of the word relative to the current cursor position.
515// If the cursor is already located at the start of a word then return the start of the previous word.
516// If there is no previous word, then return the start of the line (position of the prompt).
517//
518// One complication is that the line is utf8 encoded and the positions are
519// given in character-counts instead of byte-counts. To solve this this
520// function returns a tuple:
521// * an iterator to the start of the word (likely the new cursor position).
522// * an iterator to the current cursor position.
523// * the distance (in characters, not bytes) between these two iterators.
524// The first item in this tuple is the actual result. The other two are only
525// returned for efficiency (this function calculates them anyway, and it's
526// likely the caller will need them as well).
527static std::tuple<std::string::const_iterator, std::string::const_iterator, unsigned>
528 getStartOfWord(const std::string& line, unsigned cursorPos, size_t promptSize)
529{
530 auto begin = std::begin(line);
531 auto prompt = begin + narrow<ptrdiff_t>(promptSize); // assumes prompt only contains single-byte utf8 chars
532 auto cursor = begin; utf8::unchecked::advance(cursor, cursorPos);
533 auto pos = cursor;
534
535 // search (backwards) for first non-white-space
536 unsigned distance = 0;
537 while (pos > prompt) {
538 auto pos2 = pos;
540 if (*pos2 != one_of(' ', '\t')) break;
541 pos = pos2;
542 ++distance;
543 }
544 // search (backwards) for first white-space
545 while (pos > prompt) {
546 auto pos2 = pos;
548 if (*pos2 == one_of(' ', '\t')) break;
549 pos = pos2;
550 ++distance;
551 }
552 return {pos, cursor, distance};
553}
554
555// Similar to getStartOfWord() but locates the end of the word instead.
556// The end of the word is the 2nd (instead of the 1st) element in the tuple.
557// This is done so that both getStartOfWord() and getEndOfWord() have the invariant:
558// result = [begin, end, distance]
559// -> begin <= end
560// -> end - begin == distance
561static std::tuple<std::string::const_iterator, std::string::const_iterator, unsigned>
562 getEndOfWord(const std::string& line, unsigned cursorPos)
563{
564 auto begin = std::begin(line);
565 auto end = std::end (line);
566 auto cursor = begin; utf8::unchecked::advance(cursor, cursorPos);
567 auto pos = cursor;
568
569 // search (forwards) for first non-white-space
570 unsigned distance = 0;
571 while ((pos < end) && (*pos == one_of(' ', '\t'))) {
572 ++pos; // single-byte utf8 character
573 ++distance;
574 }
575 // search (forwards) for first white-space
576 while ((pos < end) && (*pos != one_of(' ', '\t'))) {
578 ++distance;
579 }
580 return {cursor, pos, distance};
581}
582
583void CommandConsole::gotoStartOfWord()
584{
585 auto [begin, end, distance] = getStartOfWord(lines[0].str(), cursorPosition, prompt.size());
586 cursorPosition -= distance;
587}
588
589void CommandConsole::deleteToStartOfWord()
590{
591 resetScrollBack();
592 currentLine = lines[0].str();
593 auto [begin, end, distance] = getStartOfWord(currentLine, cursorPosition, prompt.size());
594 currentLine.erase(begin, end);
595 currentLine.erase(0, prompt.size());
596 lines[0] = highLight(currentLine);
597 cursorPosition -= distance;
598}
599
600void CommandConsole::gotoEndOfWord()
601{
602 auto [begin, end, distance] = getEndOfWord(lines[0].str(), cursorPosition);
603 cursorPosition += distance;
604}
605
606void CommandConsole::deleteToEndOfWord()
607{
608 resetScrollBack();
609 currentLine = lines[0].str();
610 auto [begin, end, distance] = getEndOfWord(currentLine, cursorPosition);
611 currentLine.erase(begin, end);
612 currentLine.erase(0, prompt.size());
613 lines[0] = highLight(currentLine);
614}
615
616void CommandConsole::prevCommand()
617{
618 resetScrollBack();
619 if (history.empty()) {
620 return; // no elements
621 }
622 bool match = false;
623 unsigned tmp = commandScrollBack;
624 while ((tmp != 0) && !match) {
625 --tmp;
626 match = history[tmp].starts_with(currentLine);
627 }
628 if (match) {
629 commandScrollBack = tmp;
630 lines[0] = highLight(history[commandScrollBack]);
631 cursorPosition = unsigned(lines[0].numChars());
632 }
633}
634
635void CommandConsole::nextCommand()
636{
637 resetScrollBack();
638 if (commandScrollBack == history.size()) {
639 return; // don't loop !
640 }
641 bool match = false;
642 auto tmp = commandScrollBack;
643 while ((++tmp != history.size()) && !match) {
644 match = history[tmp].starts_with(currentLine);
645 }
646 if (match) {
647 --tmp; // one time to many
648 commandScrollBack = tmp;
649 lines[0] = highLight(history[commandScrollBack]);
650 } else {
651 commandScrollBack = unsigned(history.size());
652 lines[0] = highLight(currentLine);
653 }
654 cursorPosition = unsigned(lines[0].numChars());
655}
656
657void CommandConsole::clearCommand()
658{
659 resetScrollBack();
660 commandBuffer.clear();
661 prompt = PROMPT_NEW;
662 currentLine.clear();
663 lines[0] = highLight(currentLine);
664 cursorPosition = unsigned(prompt.size());
665}
666
667void CommandConsole::clearHistory()
668{
669 resetScrollBack();
670 while (lines.size() > 1) {
671 lines.removeBack();
672 }
673}
674
675void CommandConsole::backspace()
676{
677 resetScrollBack();
678 if (cursorPosition > prompt.size()) {
679 currentLine = lines[0].str();
680 auto b = begin(currentLine);
681 utf8::unchecked::advance(b, cursorPosition - 1);
682 auto e = b;
684 currentLine.erase(b, e);
685 currentLine.erase(0, prompt.size());
686 lines[0] = highLight(currentLine);
687 --cursorPosition;
688 }
689}
690
691void CommandConsole::delete_key()
692{
693 resetScrollBack();
694 if (lines[0].numChars() > cursorPosition) {
695 currentLine = lines[0].str();
696 auto b = begin(currentLine);
697 utf8::unchecked::advance(b, cursorPosition);
698 auto e = b;
700 currentLine.erase(b, e);
701 currentLine.erase(0, prompt.size());
702 lines[0] = highLight(currentLine);
703 }
704}
705
706void CommandConsole::normalKey(uint32_t chr)
707{
708 assert(chr);
709 resetScrollBack();
710 currentLine = lines[0].str();
711 auto pos = begin(currentLine);
712 utf8::unchecked::advance(pos, cursorPosition);
713 utf8::unchecked::append(chr, inserter(currentLine, pos));
714 currentLine.erase(0, prompt.size());
715 lines[0] = highLight(currentLine);
716 ++cursorPosition;
717}
718
719void CommandConsole::resetScrollBack()
720{
721 consoleScrollBack = 0;
722}
723
724static std::vector<std::string_view> splitLines(std::string_view str)
725{
726 // This differs from StringOp::split() in two ways:
727 // - If the input is an empty string, then the resulting vector
728 // contains 1 element which is the empty string (StringOp::split()
729 // would return an empty vector).
730 // - If the input ends on a newline character, then the final item in
731 // the result vector is an empty string (StringOp::split() would not
732 // include that last empty string).
733 // TODO can we come up with a good name for this function and move it
734 // to StringOp?
735 std::vector<std::string_view> result;
736 while (true) {
737 auto pos = str.find_first_of('\n');
738 if (pos == std::string_view::npos) break;
739 result.push_back(str.substr(0, pos));
740 str = str.substr(pos + 1);
741 }
742 result.push_back(str);
743 return result;
744}
745
746void CommandConsole::paste()
747{
748 auto text = display.getVideoSystem().getClipboardText();
749 if (text.empty()) return;
750 auto pastedLines = splitLines(text);
751 assert(!pastedLines.empty());
752
753 // helper function 'append()' to append to the existing text
754 std::string_view prefix = lines[0].str();
755 prefix.remove_prefix(prompt.size());
756 auto append = [&](std::string_view suffix) {
757 if (prefix.empty()) {
758 lines[0] = highLight(suffix);
759 } else {
760 lines[0] = highLight(tmpStrCat(prefix, suffix));
761 prefix = "";
762 }
763 };
764 // execute all but the last line
765 for (const auto& line : view::drop_back(pastedLines, 1)) {
766 append(line);
767 commandExecute();
768 }
769 // only enter (not execute) the last line
770 append(pastedLines.back());
771 cursorPosition = unsigned(lines[0].numChars());
772}
773
774} // namespace openmsx
Assign new value to some variable and restore the original value when this object goes out of scope.
Definition: ScopedAssign.hh:8
std::string getColors() const
Ouput: a string of equal length of the input command where each character indicates the type of the c...
Definition: TclParser.hh:29
void push_back(T2 &&t)
size_t size() const
bool full() const
bool empty() const
Definition: one_of.hh:7
bool getBoolean() const noexcept
void printWarning(std::string_view message)
Definition: CliComm.cc:10
CommandConsole(GlobalCommandController &commandController, EventDistributor &eventDistributor, Display &display)
gl::ivec2 getCursorPosition() const
unsigned getColumns() const
unsigned getRows() const
static void setOutput(InterpreterOutput *output_)
Definition: Completer.hh:68
std::string_view chunkText(size_t i) const
Get the text for the i-th chunk.
ConsoleLine()=default
Construct empty line.
size_t numChars() const
Get the number of UTF8 characters in this line.
uint32_t chunkColor(size_t i) const
Get the color for the i-th chunk.
void addChunk(std::string_view text, uint32_t rgb)
Append a chunk with a (different) color.
Represents the output window/screen of openMSX.
Definition: Display.hh:33
VideoSystem & getVideoSystem()
Definition: Display.cc:107
void repaintDelayed(uint64_t delta)
Definition: Display.cc:371
void unregisterEventListener(EventType type, EventListener &listener)
Unregisters a previously registered event listener.
void registerEventListener(EventType type, EventListener &listener, Priority priority=OTHER)
Registers a given object to receive certain events.
std::string tabCompletion(std::string_view command)
Complete the given command.
TclObject executeCommand(zstring_view command, CliConnection *connection=nullptr) override
Execute the given command.
bool isComplete(zstring_view command)
Returns true iff the command is complete (all braces, quotes etc.
void setOutput(InterpreterOutput *output_)
Definition: Interpreter.hh:27
TclParser parse(std::string_view command)
Definition: Interpreter.cc:456
zstring_view getString() const
Definition: TclObject.cc:120
static std::string full()
Definition: Version.cc:8
virtual std::string getClipboardText()=0
constexpr double e
Definition: Math.hh:20
void append(Result &)
Definition: stl.hh:290
constexpr vecN< N, T > min(const vecN< N, T > &x, const vecN< N, T > &y)
Definition: gl_vec.hh:266
constexpr vecN< N, T > max(const vecN< N, T > &x, const vecN< N, T > &y)
Definition: gl_vec.hh:284
void openOfStream(std::ofstream &stream, zstring_view filename)
Open an ofstream in a platform-independent manner.
@ K_BACKSPACE
Definition: Keys.hh:31
@ K_KP_ENTER
Definition: Keys.hh:120
@ K_RETURN
Definition: Keys.hh:34
@ K_PAGEDOWN
Definition: Keys.hh:132
This file implemented 3 utility functions:
Definition: Autofire.cc:9
auto visit(Visitor &&visitor, const Event &event)
Definition: Event.hh:652
FileContext userFileContext(string_view savePath)
Definition: FileContext.cc:171
STL namespace.
std::string_view substr(std::string_view utf8, std::string_view::size_type first=0, std::string_view::size_type len=std::string_view::npos)
void advance(octet_iterator &it, distance_type n)
size_t size(std::string_view utf8)
uint32_t next(octet_iterator &it)
octet_iterator append(uint32_t cp, octet_iterator result)
uint32_t prior(octet_iterator &it)
auto distance(octet_iterator first, octet_iterator last)
constexpr bool is_pua(uint32_t cp)
Definition: utf8_core.hh:256
constexpr auto drop_back(Range &&range, size_t n)
Definition: view.hh:446
TemporaryString tmpStrCat(Ts &&... ts)
Definition: strCat.hh:610
void strAppend(std::string &result, Ts &&...ts)
Definition: strCat.hh:620
#define UNREACHABLE
Definition: unreachable.hh:38
constexpr auto begin(const zstring_view &x)
constexpr auto end(const zstring_view &x)