openMSX
Reactor.cc
Go to the documentation of this file.
1#include "Reactor.hh"
2
3#include "AfterCommand.hh"
4#include "AviRecorder.hh"
5#include "BooleanSetting.hh"
6#include "Command.hh"
7#include "CommandException.hh"
9#include "DiskChanger.hh"
10#include "DiskFactory.hh"
11#include "DiskManipulator.hh"
12#include "Display.hh"
13#include "EnumSetting.hh"
14#include "Event.hh"
15#include "EventDistributor.hh"
16#include "FileContext.hh"
17#include "FileException.hh"
18#include "FilePool.hh"
19#include "GlobalCliComm.hh"
21#include "GlobalSettings.hh"
22#include "HardwareConfig.hh"
23#include "ImGuiManager.hh"
24#include "InfoTopic.hh"
26#include "MSXMotherBoard.hh"
27#include "MSXPPI.hh"
28#include "MessageCommand.hh"
29#include "Mixer.hh"
30#include "MsxChar2Unicode.hh"
31#include "RTScheduler.hh"
32#include "RomDatabase.hh"
33#include "RomInfo.hh"
35#include "SymbolManager.hh"
37#include "TclObject.hh"
38#include "UserSettings.hh"
39#include "VideoSystem.hh"
40#include "XMLElement.hh"
41#include "XMLException.hh"
42
43#include "FileOperations.hh"
44#include "foreach_file.hh"
45#include "Thread.hh"
46#include "Timer.hh"
47
48#include "narrow.hh"
49#include "ranges.hh"
50#include "serialize.hh"
51#include "stl.hh"
52#include "StringOp.hh"
53#include "unreachable.hh"
54#include "build-info.hh"
55
56#include <array>
57#include <cassert>
58#include <memory>
59
60using std::make_unique;
61using std::string;
62using std::string_view;
63using std::vector;
64
65namespace openmsx {
66
67// global variable to communicate the exit-code from the 'exit' command to main()
68int exitCode = 0;
69
70class ExitCommand final : public Command
71{
72public:
73 ExitCommand(CommandController& commandController, EventDistributor& distributor);
74 void execute(std::span<const TclObject> tokens, TclObject& result) override;
75 [[nodiscard]] string help(std::span<const TclObject> tokens) const override;
76private:
77 EventDistributor& distributor;
78};
79
80class MachineCommand final : public Command
81{
82public:
83 MachineCommand(CommandController& commandController, Reactor& reactor);
84 void execute(std::span<const TclObject> tokens, TclObject& result) override;
85 [[nodiscard]] string help(std::span<const TclObject> tokens) const override;
86 void tabCompletion(vector<string>& tokens) const override;
87private:
88 Reactor& reactor;
89};
90
91class TestMachineCommand final : public Command
92{
93public:
94 TestMachineCommand(CommandController& commandController, Reactor& reactor);
95 void execute(std::span<const TclObject> tokens, TclObject& result) override;
96 [[nodiscard]] string help(std::span<const TclObject> tokens) const override;
97 void tabCompletion(vector<string>& tokens) const override;
98private:
99 Reactor& reactor;
100};
101
102class CreateMachineCommand final : public Command
103{
104public:
105 CreateMachineCommand(CommandController& commandController, Reactor& reactor);
106 void execute(std::span<const TclObject> tokens, TclObject& result) override;
107 [[nodiscard]] string help(std::span<const TclObject> tokens) const override;
108private:
109 Reactor& reactor;
110};
111
112class DeleteMachineCommand final : public Command
113{
114public:
115 DeleteMachineCommand(CommandController& commandController, Reactor& reactor);
116 void execute(std::span<const TclObject> tokens, TclObject& result) override;
117 [[nodiscard]] string help(std::span<const TclObject> tokens) const override;
118 void tabCompletion(vector<string>& tokens) const override;
119private:
120 Reactor& reactor;
121};
122
123class ListMachinesCommand final : public Command
124{
125public:
126 ListMachinesCommand(CommandController& commandController, Reactor& reactor);
127 void execute(std::span<const TclObject> tokens, TclObject& result) override;
128 [[nodiscard]] string help(std::span<const TclObject> tokens) const override;
129private:
130 Reactor& reactor;
131};
132
133class ActivateMachineCommand final : public Command
134{
135public:
136 ActivateMachineCommand(CommandController& commandController, Reactor& reactor);
137 void execute(std::span<const TclObject> tokens, TclObject& result) override;
138 [[nodiscard]] string help(std::span<const TclObject> tokens) const override;
139 void tabCompletion(vector<string>& tokens) const override;
140private:
141 Reactor& reactor;
142};
143
144class StoreMachineCommand final : public Command
145{
146public:
147 StoreMachineCommand(CommandController& commandController, Reactor& reactor);
148 void execute(std::span<const TclObject> tokens, TclObject& result) override;
149 [[nodiscard]] string help(std::span<const TclObject> tokens) const override;
150 void tabCompletion(vector<string>& tokens) const override;
151private:
152 Reactor& reactor;
153};
154
155class RestoreMachineCommand final : public Command
156{
157public:
158 RestoreMachineCommand(CommandController& commandController, Reactor& reactor);
159 void execute(std::span<const TclObject> tokens, TclObject& result) override;
160 [[nodiscard]] string help(std::span<const TclObject> tokens) const override;
161 void tabCompletion(vector<string>& tokens) const override;
162private:
163 Reactor& reactor;
164};
165
166class GetClipboardCommand final : public Command
167{
168public:
169 GetClipboardCommand(CommandController& commandController, Reactor& reactor);
170 void execute(std::span<const TclObject> tokens, TclObject& result) override;
171 [[nodiscard]] string help(std::span<const TclObject> tokens) const override;
172private:
173 Reactor& reactor;
174};
175
176class SetClipboardCommand final : public Command
177{
178public:
179 SetClipboardCommand(CommandController& commandController, Reactor& reactor);
180 void execute(std::span<const TclObject> tokens, TclObject& result) override;
181 [[nodiscard]] string help(std::span<const TclObject> tokens) const override;
182private:
183 Reactor& reactor;
184};
185
186class ConfigInfo final : public InfoTopic
187{
188public:
189 ConfigInfo(InfoCommand& openMSXInfoCommand, const string& configName);
190 void execute(std::span<const TclObject> tokens,
191 TclObject& result) const override;
192 [[nodiscard]] string help(std::span<const TclObject> tokens) const override;
193 void tabCompletion(vector<string>& tokens) const override;
194private:
195 const string configName;
196};
197
198class RealTimeInfo final : public InfoTopic
199{
200public:
201 explicit RealTimeInfo(InfoCommand& openMSXInfoCommand);
202 void execute(std::span<const TclObject> tokens,
203 TclObject& result) const override;
204 [[nodiscard]] string help(std::span<const TclObject> tokens) const override;
205private:
206 const uint64_t reference;
207};
208
209class SoftwareInfoTopic final : public InfoTopic
210{
211public:
212 SoftwareInfoTopic(InfoCommand& openMSXInfoCommand, Reactor& reactor);
213 void execute(std::span<const TclObject> tokens,
214 TclObject& result) const override;
215 [[nodiscard]] std::string help(std::span<const TclObject> tokens) const override;
216private:
217 Reactor& reactor;
218};
219
220
221Reactor::Reactor() = default;
222
224{
225 rtScheduler = make_unique<RTScheduler>();
226 eventDistributor = make_unique<EventDistributor>(*this);
227 globalCliComm = make_unique<GlobalCliComm>();
228 globalCommandController = make_unique<GlobalCommandController>(
229 *eventDistributor, *globalCliComm, *this);
230 globalSettings = make_unique<GlobalSettings>(
231 *globalCommandController);
232 inputEventGenerator = make_unique<InputEventGenerator>(
233 *globalCommandController, *eventDistributor, *globalSettings);
234 symbolManager = make_unique<SymbolManager>(
235 *globalCommandController);
236 imGuiManager = make_unique<ImGuiManager>(*this, globalCommandController->getSettingsConfig());
237 diskFactory = make_unique<DiskFactory>(
238 *this);
239 diskManipulator = make_unique<DiskManipulator>(
240 *globalCommandController, *this);
241 virtualDrive = make_unique<DiskChanger>(
242 *this, "virtual_drive");
243 filePool = make_unique<FilePool>(*globalCommandController, *this);
244 userSettings = make_unique<UserSettings>(
245 *globalCommandController);
246 afterCommand = make_unique<AfterCommand>(
247 *this, *eventDistributor, *globalCommandController);
248 exitCommand = make_unique<ExitCommand>(
249 *globalCommandController, *eventDistributor);
250 messageCommand = make_unique<MessageCommand>(
251 *globalCommandController);
252 machineCommand = make_unique<MachineCommand>(
253 *globalCommandController, *this);
254 testMachineCommand = make_unique<TestMachineCommand>(
255 *globalCommandController, *this);
256 createMachineCommand = make_unique<CreateMachineCommand>(
257 *globalCommandController, *this);
258 deleteMachineCommand = make_unique<DeleteMachineCommand>(
259 *globalCommandController, *this);
260 listMachinesCommand = make_unique<ListMachinesCommand>(
261 *globalCommandController, *this);
262 activateMachineCommand = make_unique<ActivateMachineCommand>(
263 *globalCommandController, *this);
264 storeMachineCommand = make_unique<StoreMachineCommand>(
265 *globalCommandController, *this);
266 restoreMachineCommand = make_unique<RestoreMachineCommand>(
267 *globalCommandController, *this);
268 getClipboardCommand = make_unique<GetClipboardCommand>(
269 *globalCommandController, *this);
270 setClipboardCommand = make_unique<SetClipboardCommand>(
271 *globalCommandController, *this);
272 aviRecordCommand = make_unique<AviRecorder>(*this);
273 extensionInfo = make_unique<ConfigInfo>(
274 getOpenMSXInfoCommand(), "extensions");
275 machineInfo = make_unique<ConfigInfo>(
276 getOpenMSXInfoCommand(), "machines");
277 realTimeInfo = make_unique<RealTimeInfo>(
279 softwareInfoTopic = make_unique<SoftwareInfoTopic>(
280 getOpenMSXInfoCommand(), *this);
281 tclCallbackMessages = make_unique<TclCallbackMessages>(
282 *globalCliComm, *globalCommandController);
283
284 createMachineSetting();
285
287
288 eventDistributor->registerEventListener(EventType::QUIT, *this);
289#if PLATFORM_ANDROID
290 eventDistributor->registerEventListener(EventType::WINDOW, *this);
291#endif
292 isInit = true;
293}
294
296{
297 if (!isInit) return;
298 deleteBoard(activeBoard);
299
300 eventDistributor->unregisterEventListener(EventType::QUIT, *this);
301#if PLATFORM_ANDROID
302 eventDistributor->unregisterEventListener(EventType::WINDOW, *this);
303#endif
304
306}
307
309{
310 if (!mixer) {
311 mixer = make_unique<Mixer>(*this, *globalCommandController);
312 }
313 return *mixer;
314}
315
317{
318 if (!softwareDatabase) {
319 softwareDatabase = make_unique<RomDatabase>(*globalCliComm);
320 }
321 return *softwareDatabase;
322}
323
325{
326 return *globalCliComm;
327}
328
333
335{
336 return *globalCommandController;
337}
338
340{
341 return globalCommandController->getOpenMSXInfoCommand();
342}
343
345{
346 return globalCommandController->getHotKey();
347}
348
349vector<string> Reactor::getHwConfigs(string_view type)
350{
351 vector<string> result;
352 for (const auto& p : systemFileContext().getPaths()) {
353 auto fileAction = [&](const std::string& /*path*/, std::string_view name) {
354 if (name.ends_with(".xml")) {
355 name.remove_suffix(4);
356 result.emplace_back(name);
357 }
358 };
359 auto dirAction = [&](std::string& path, std::string_view name) {
360 auto size = path.size();
361 path += "/hardwareconfig.xml";
363 result.emplace_back(name);
364 }
365 path.resize(size);
366 };
367 foreach_file_and_directory(FileOperations::join(p, type), fileAction, dirAction);
368 }
369 // remove duplicates
370 ranges::sort(result);
371 result.erase(ranges::unique(result), end(result));
372 return result;
373}
374
376{
377 // TODO cleanup this code. It should be easier to get a hold of the
378 // 'MsxChar2Unicode' object. Probably the 'Keyboard' class is not the
379 // right location to store it.
380 try {
381 if (MSXMotherBoard* board = getMotherBoard()) {
382 if (auto* ppi = dynamic_cast<MSXPPI*>(board->findDevice("ppi"))) {
383 return ppi->getKeyboard().getMsxChar2Unicode();
384 }
385 }
386 } catch (MSXException&) {
387 // ignore
388 }
389 static const MsxChar2Unicode defaultMsxChars("MSXVID.TXT");
390 return defaultMsxChars;
391}
392
393
394void Reactor::createMachineSetting()
395{
396 auto names = getHwConfigs("machines");
397 EnumSetting<int>::Map machines; // int's are unique dummy values
398 machines.reserve(names.size() + 1);
399 int count = 1;
400 append(machines, view::transform(names,
401 [&](auto& name) { return EnumSettingBase::MapEntry(std::move(name), count++); }));
402 machines.emplace_back("C-BIOS_MSX2+", 0); // default machine
403
404 machineSetting = make_unique<EnumSetting<int>>(
405 *globalCommandController, "default_machine",
406 "default machine (takes effect next time openMSX is started)",
407 0, std::move(machines));
408}
409
411{
412 assert(Thread::isMainThread());
413 return activeBoard.get();
414}
415
416string_view Reactor::getMachineID() const
417{
418 return activeBoard ? activeBoard->getMachineID() : string_view{};
419}
420
421Reactor::Board Reactor::getMachine(string_view machineID) const
422{
423 if (auto it = ranges::find(boards, machineID, &MSXMotherBoard::getMachineID);
424 it != boards.end()) {
425 return *it;
426 }
427 throw CommandException("No machine with ID: ", machineID);
428}
429
431{
432 return std::make_shared<MSXMotherBoard>(*this);
433}
434
436{
437 assert(Thread::isMainThread());
438
439 // Add new board.
440 boards.push_back(newBoard);
441
442 // Lookup old board (it must be present).
443 auto it = find_unguarded(boards, &oldBoard_,
444 [](auto& b) { return b.get(); });
445
446 // If the old board was the active board, then activate the new board
447 if (*it == activeBoard) {
448 switchBoard(newBoard);
449 }
450
451 // Remove the old board.
452 move_pop_back(boards, it);
453}
454
455void Reactor::switchMachine(const string& machine)
456{
457 if (!display) {
458 display = make_unique<Display>(*this);
459 // TODO: Currently it is not possible to move this call into the
460 // constructor of Display because the call to createVideoSystem()
461 // indirectly calls Reactor.getDisplay().
462 display->createVideoSystem();
463 }
464
465 // create+load new machine
466 // switch to new machine
467 // delete old active machine
468
469 assert(Thread::isMainThread());
470 // Note: loadMachine can throw an exception and in that case the
471 // motherboard must be considered as not created at all.
472 auto newBoard = createEmptyMotherBoard();
473 newBoard->loadMachine(machine);
474 boards.push_back(newBoard);
475
476 auto oldBoard = activeBoard;
477 switchBoard(newBoard);
478 deleteBoard(oldBoard);
479}
480
481void Reactor::switchBoard(Board newBoard)
482{
483 assert(Thread::isMainThread());
484 assert(!newBoard || contains(boards, newBoard));
485 assert(!activeBoard || contains(boards, activeBoard));
486 if (activeBoard) {
487 activeBoard->activate(false);
488 }
489 {
490 // Don't hold the lock for longer than the actual switch.
491 // In the past we had a potential for deadlocks here, because
492 // (indirectly) the code below still acquires other locks.
493 std::scoped_lock lock(mbMutex);
494 activeBoard = newBoard;
495 }
496 eventDistributor->distributeEvent(MachineLoadedEvent());
497 globalCliComm->update(CliComm::HARDWARE, getMachineID(), "select");
498 if (activeBoard) {
499 activeBoard->activate(true);
500 }
501}
502
503void Reactor::deleteBoard(Board board)
504{
505 // Note: pass 'board' by-value to keep the parameter from changing
506 // after the call to switchBoard(). switchBoard() changes the
507 // 'activeBoard' member variable, so the 'board' parameter would change
508 // if it were passed by reference to this method (AFAICS this only
509 // happens in ~Reactor()).
510 assert(Thread::isMainThread());
511 if (!board) return;
512
513 if (board == activeBoard) {
514 // delete active board -> there is no active board anymore
515 switchBoard(nullptr);
516 }
517 auto it = rfind_unguarded(boards, board);
518 move_pop_back(boards, it);
519}
520
522{
523 // Note: this method can get called from different threads
524 if (Thread::isMainThread()) {
525 // Don't take lock in main thread to avoid recursive locking.
526 if (activeBoard) {
527 activeBoard->exitCPULoopSync();
528 }
529 } else {
530 std::scoped_lock lock(mbMutex);
531 if (activeBoard) {
532 activeBoard->exitCPULoopAsync();
533 }
534 }
535}
536
538{
539 auto& commandController = *globalCommandController;
540
541 // execute init.tcl
542 try {
543 commandController.source(
544 preferSystemFileContext().resolve("init.tcl"));
545 } catch (FileException&) {
546 // no init.tcl, ignore
547 }
548
549 // execute startup scripts
550 for (const auto& s : parser.getStartupScripts()) {
551 try {
552 commandController.source(userFileContext().resolve(s));
553 } catch (FileException& e) {
554 throw FatalError("Couldn't execute script: ",
555 e.getMessage());
556 }
557 }
558 for (const auto& cmd : parser.getStartupCommands()) {
559 try {
560 commandController.executeCommand(cmd);
561 } catch (CommandException& e) {
562 throw FatalError("Couldn't execute command: ", cmd,
563 '\n', e.getMessage());
564 }
565 }
566
567 fullyStarted = true;
568
569 // At this point openmsx is fully started, it's OK now to start
570 // accepting external commands
572
573 // ...and re-emit any postponed message callbacks now that the scripts
574 // are loaded
575 tclCallbackMessages->redoPostponedCallbacks();
576
577 // Run
578 if (parser.getParseStatus() == CommandLineParser::RUN) {
579 // don't use Tcl to power up the machine, we cannot pass
580 // exceptions through Tcl and ADVRAM might throw in its
581 // powerUp() method. Solution is to implement dependencies
582 // between devices so ADVRAM can check the error condition
583 // in its constructor
584 //commandController.executeCommand("set power on");
585 if (activeBoard) {
586 activeBoard->powerUp();
587 }
588 }
589
590 while (doOneIteration()) {
591 // nothing
592 }
593}
594
595bool Reactor::doOneIteration()
596{
597 eventDistributor->deliverEvents();
598 bool blocked = (blockedCounter > 0) || !activeBoard;
599 if (!blocked) {
600 // copy shared_ptr to keep Board alive (e.g. in case of Tcl
601 // callbacks)
602 auto copy = activeBoard;
603 blocked = !copy->execute();
604 }
605 if (blocked) {
606 // At first sight a better alternative is to use the
607 // SDL_WaitEvent() function. Though when inspecting
608 // the implementation of that function, it turns out
609 // to also use a sleep/poll loop, with even shorter
610 // sleep periods as we use here. Maybe in future
611 // SDL implementations this will be improved.
612 eventDistributor->sleep(20 * 1000);
613 }
614 return running;
615}
616
617void Reactor::unpause()
618{
619 if (paused) {
620 paused = false;
621 globalCliComm->update(CliComm::STATUS, "paused", "false");
622 unblock();
623 }
624}
625
626void Reactor::pause()
627{
628 if (!paused) {
629 paused = true;
630 globalCliComm->update(CliComm::STATUS, "paused", "true");
631 block();
632 }
633}
634
636{
637 ++blockedCounter;
639 getMixer().mute();
640}
641
643{
644 --blockedCounter;
645 assert(blockedCounter >= 0);
646 getMixer().unmute();
647}
648
649
650// Observer<Setting>
651void Reactor::update(const Setting& setting) noexcept
652{
653 auto& pauseSetting = getGlobalSettings().getPauseSetting();
654 if (&setting == &pauseSetting) {
655 if (pauseSetting.getBoolean()) {
656 pause();
657 } else {
658 unpause();
659 }
660 }
661}
662
663// EventListener
664int Reactor::signalEvent(const Event& event)
665{
666 std::visit(overloaded{
667 [&](const QuitEvent& /*e*/) {
669 running = false;
670 },
671 [&](const WindowEvent& e) {
672 (void)e;
673#if PLATFORM_ANDROID
674 if (e.isMainWindow()) {
675 // Android SDL port sends a (un)focus event when an app is put in background
676 // by the OS for whatever reason (like an incoming phone call) and all screen
677 // resources are taken away from the app.
678 // In such case the app is supposed to behave as a good citizen
679 // and minimize its resource usage and related battery drain.
680 // The SDL Android port already takes care of halting the Java
681 // part of the sound processing. The Display class makes sure that it wont try
682 // to render anything to the (temporary missing) graphics resources but the
683 // main emulation should also be temporary stopped, in order to minimize CPU usage
684 if (e.getSdlWindowEvent().type == SDL_WINDOWEVENT_FOCUS_GAINED) {
685 unblock();
686 } else if (e.getSdlWindowEvent().type == SDL_WINDOWEVENT_FOCUS_LOST) {
687 block();
688 }
689 }
690#endif
691 },
692 [](const EventBase /*e*/) {
693 UNREACHABLE; // we didn't subscribe to this event...
694 }
695 }, event);
696 return 0;
697}
698
699
700// class ExitCommand
701
703 EventDistributor& distributor_)
704 : Command(commandController_, "exit")
705 , distributor(distributor_)
706{
707}
708
709void ExitCommand::execute(std::span<const TclObject> tokens, TclObject& /*result*/)
710{
711 checkNumArgs(tokens, Between{1, 2}, Prefix{1}, "?exitcode?");
712 switch (tokens.size()) {
713 case 1:
714 exitCode = 0;
715 break;
716 case 2:
717 exitCode = tokens[1].getInt(getInterpreter());
718 break;
719 }
720 distributor.distributeEvent(QuitEvent());
721}
722
723string ExitCommand::help(std::span<const TclObject> /*tokens*/) const
724{
725 return "Use this command to stop the emulator.\n"
726 "Optionally you can pass an exit-code.\n";
727}
728
729
730// class MachineCommand
731
733 Reactor& reactor_)
734 : Command(commandController_, "machine")
735 , reactor(reactor_)
736{
737}
738
739void MachineCommand::execute(std::span<const TclObject> tokens, TclObject& result)
740{
741 checkNumArgs(tokens, Between{1, 2}, Prefix{1}, "?machinetype?");
742 switch (tokens.size()) {
743 case 1: // get current machine
744 // nothing
745 break;
746 case 2:
747 try {
748 reactor.switchMachine(string(tokens[1].getString()));
749 } catch (MSXException& e) {
750 throw CommandException("Machine switching failed: ",
751 e.getMessage());
752 }
753 break;
754 }
755 // Always return machineID (of current or of new machine).
756 result = reactor.getMachineID();
757}
758
759string MachineCommand::help(std::span<const TclObject> /*tokens*/) const
760{
761 return "Switch to a different MSX machine.";
762}
763
764void MachineCommand::tabCompletion(vector<string>& tokens) const
765{
766 completeString(tokens, Reactor::getHwConfigs("machines"));
767}
768
769
770// class TestMachineCommand
771
773 Reactor& reactor_)
774 : Command(commandController_, "test_machine")
775 , reactor(reactor_)
776{
777}
778
779void TestMachineCommand::execute(std::span<const TclObject> tokens,
780 TclObject& result)
781{
782 checkNumArgs(tokens, 2, "machinetype");
783 try {
784 MSXMotherBoard mb(reactor);
785 mb.loadMachine(string(tokens[1].getString()));
786 } catch (MSXException& e) {
787 result = e.getMessage(); // error
788 }
789}
790
791string TestMachineCommand::help(std::span<const TclObject> /*tokens*/) const
792{
793 return "Test the configuration for the given machine. "
794 "Returns an error message explaining why the configuration is "
795 "invalid or an empty string in case of success.";
796}
797
798void TestMachineCommand::tabCompletion(vector<string>& tokens) const
799{
800 completeString(tokens, Reactor::getHwConfigs("machines"));
801}
802
803
804// class CreateMachineCommand
805
807 CommandController& commandController_, Reactor& reactor_)
808 : Command(commandController_, "create_machine")
809 , reactor(reactor_)
810{
811}
812
813void CreateMachineCommand::execute(std::span<const TclObject> tokens, TclObject& result)
814{
815 checkNumArgs(tokens, 1, Prefix{1}, nullptr);
816 auto newBoard = reactor.createEmptyMotherBoard();
817 result = newBoard->getMachineID();
818 reactor.boards.push_back(std::move(newBoard));
819}
820
821string CreateMachineCommand::help(std::span<const TclObject> /*tokens*/) const
822{
823 return "Creates a new (empty) MSX machine. Returns the ID for the new "
824 "machine.\n"
825 "Use 'load_machine' to actually load a machine configuration "
826 "into this new machine.\n"
827 "The main reason create_machine and load_machine are two "
828 "separate commands is that sometimes you already want to know "
829 "the ID of the machine before load_machine starts emitting "
830 "events for this machine.";
831}
832
833
834// class DeleteMachineCommand
835
837 CommandController& commandController_, Reactor& reactor_)
838 : Command(commandController_, "delete_machine")
839 , reactor(reactor_)
840{
841}
842
843void DeleteMachineCommand::execute(std::span<const TclObject> tokens,
844 TclObject& /*result*/)
845{
846 checkNumArgs(tokens, 2, "id");
847 reactor.deleteBoard(reactor.getMachine(tokens[1].getString()));
848}
849
850string DeleteMachineCommand::help(std::span<const TclObject> /*tokens*/) const
851{
852 return "Deletes the given MSX machine.";
853}
854
855void DeleteMachineCommand::tabCompletion(vector<string>& tokens) const
856{
857 completeString(tokens, reactor.getMachineIDs());
858}
859
860
861// class ListMachinesCommand
862
864 CommandController& commandController_, Reactor& reactor_)
865 : Command(commandController_, "list_machines")
866 , reactor(reactor_)
867{
868}
869
870void ListMachinesCommand::execute(std::span<const TclObject> /*tokens*/,
871 TclObject& result)
872{
873 result.addListElements(reactor.getMachineIDs());
874}
875
876string ListMachinesCommand::help(std::span<const TclObject> /*tokens*/) const
877{
878 return "Returns a list of all machine IDs.";
879}
880
881
882// class ActivateMachineCommand
883
885 CommandController& commandController_, Reactor& reactor_)
886 : Command(commandController_, "activate_machine")
887 , reactor(reactor_)
888{
889}
890
891void ActivateMachineCommand::execute(std::span<const TclObject> tokens,
892 TclObject& result)
893{
894 checkNumArgs(tokens, Between{1, 2}, Prefix{1}, "id");
895 switch (tokens.size()) {
896 case 1:
897 break;
898 case 2:
899 reactor.switchBoard(reactor.getMachine(tokens[1].getString()));
900 break;
901 }
902 result = reactor.getMachineID();
903}
904
905string ActivateMachineCommand::help(std::span<const TclObject> /*tokens*/) const
906{
907 return "Make another machine the active msx machine.\n"
908 "Or when invoked without arguments, query the ID of the "
909 "active msx machine.";
910}
911
912void ActivateMachineCommand::tabCompletion(vector<string>& tokens) const
913{
914 completeString(tokens, reactor.getMachineIDs());
915}
916
917
918// class StoreMachineCommand
919
921 CommandController& commandController_, Reactor& reactor_)
922 : Command(commandController_, "store_machine")
923 , reactor(reactor_)
924{
925}
926
927void StoreMachineCommand::execute(std::span<const TclObject> tokens, TclObject& result)
928{
929 checkNumArgs(tokens, 3, Prefix{1}, "id filename");
930 const auto& machineID = tokens[1].getString();
931 const auto& filename = tokens[2].getString();
932
933 auto& board = *reactor.getMachine(machineID);
934
935 XmlOutputArchive out(filename);
936 out.serialize("machine", board);
937 out.close();
938 result = filename;
939}
940
941string StoreMachineCommand::help(std::span<const TclObject> /*tokens*/) const
942{
943 return
944 "store_machine machineID <filename> Save state of machine \"machineID\" to indicated file\n"
945 "\n"
946 "This is a low-level command, the 'savestate' script is easier to use.";
947}
948
949void StoreMachineCommand::tabCompletion(vector<string>& tokens) const
950{
951 completeString(tokens, reactor.getMachineIDs());
952}
953
954
955// class RestoreMachineCommand
956
958 CommandController& commandController_, Reactor& reactor_)
959 : Command(commandController_, "restore_machine")
960 , reactor(reactor_)
961{
962}
963
964void RestoreMachineCommand::execute(std::span<const TclObject> tokens,
965 TclObject& result)
966{
967 checkNumArgs(tokens, 2, Prefix{1}, "filename");
968 auto newBoard = reactor.createEmptyMotherBoard();
969
970 const auto filename = FileOperations::expandTilde(string(tokens[1].getString()));
971
972 //std::cerr << "Loading " << filename << '\n';
973 try {
974 XmlInputArchive in(filename);
975 in.serialize("machine", *newBoard);
976 } catch (XMLException& e) {
977 throw CommandException("Cannot load state, bad file format: ",
978 e.getMessage());
979 } catch (MSXException& e) {
980 throw CommandException("Cannot load state: ", e.getMessage());
981 }
982
983 // Savestate also contains stuff like the keyboard state at the moment
984 // the snapshot was created (this is required for reverse/replay). But
985 // now we want the MSX to see the actual host keyboard state.
986 newBoard->getStateChangeDistributor().stopReplay(newBoard->getCurrentTime());
987
988 result = newBoard->getMachineID();
989 reactor.boards.push_back(std::move(newBoard));
990}
991
992string RestoreMachineCommand::help(std::span<const TclObject> /*tokens*/) const
993{
994 return "restore_machine Load state from last saved state in default directory\n"
995 "restore_machine <filename> Load state from indicated file\n"
996 "\n"
997 "This is a low-level command, the 'loadstate' script is easier to use.";
998}
999
1000void RestoreMachineCommand::tabCompletion(vector<string>& tokens) const
1001{
1003}
1004
1005
1006// class GetClipboardCommand
1007
1009 CommandController& commandController_, Reactor& reactor_)
1010 : Command(commandController_, "get_clipboard_text")
1011 , reactor(reactor_)
1012{
1013 // Note: cannot yet call getReactor().getDisplay() (e.g. to cache it)
1014 // display may not yet be initialized.
1015}
1016
1017void GetClipboardCommand::execute(std::span<const TclObject> tokens, TclObject& result)
1018{
1019 checkNumArgs(tokens, 1, Prefix{1}, nullptr);
1020 result = reactor.getDisplay().getVideoSystem().getClipboardText();
1021}
1022
1023string GetClipboardCommand::help(std::span<const TclObject> /*tokens*/) const
1024{
1025 return "Returns the (text) content of the clipboard as a string.";
1026}
1027
1028
1029// class SetClipboardCommand
1030
1032 CommandController& commandController_, Reactor& reactor_)
1033 : Command(commandController_, "set_clipboard_text")
1034 , reactor(reactor_)
1035{
1036}
1037
1038void SetClipboardCommand::execute(std::span<const TclObject> tokens, TclObject& /*result*/)
1039{
1040 checkNumArgs(tokens, 2, "text");
1041 reactor.getDisplay().getVideoSystem().setClipboardText(tokens[1].getString());
1042}
1043
1044string SetClipboardCommand::help(std::span<const TclObject> /*tokens*/) const
1045{
1046 return "Send the given string to the clipboard.";
1047}
1048
1049
1050// class ConfigInfo
1051
1053 const string& configName_)
1054 : InfoTopic(openMSXInfoCommand, configName_)
1055 , configName(configName_)
1056{
1057}
1058
1059void ConfigInfo::execute(std::span<const TclObject> tokens, TclObject& result) const
1060{
1061 // TODO make meta info available through this info topic
1062 switch (tokens.size()) {
1063 case 2: {
1064 result.addListElements(Reactor::getHwConfigs(configName));
1065 break;
1066 }
1067 case 3: {
1068 try {
1069 std::array<char, 8192> allocBuffer; // tweak
1070 XMLDocument doc{allocBuffer.data(), sizeof(allocBuffer)};
1072 doc, configName, tokens[2].getString());
1073 if (const auto* info = doc.getRoot()->findChild("info")) {
1074 for (const auto& c : info->getChildren()) {
1075 result.addDictKeyValue(c.getName(), c.getData());
1076 }
1077 }
1078 } catch (MSXException& e) {
1079 throw CommandException(
1080 "Couldn't get config info: ", e.getMessage());
1081 }
1082 break;
1083 }
1084 default:
1085 throw CommandException("Too many parameters");
1086 }
1087}
1088
1089string ConfigInfo::help(std::span<const TclObject> /*tokens*/) const
1090{
1091 return strCat("Shows a list of available ", configName, ", "
1092 "or get meta information about the selected item.\n");
1093}
1094
1095void ConfigInfo::tabCompletion(vector<string>& tokens) const
1096{
1097 completeString(tokens, Reactor::getHwConfigs(configName));
1098}
1099
1100
1101// class RealTimeInfo
1102
1104 : InfoTopic(openMSXInfoCommand, "realtime")
1105 , reference(Timer::getTime())
1106{
1107}
1108
1109void RealTimeInfo::execute(std::span<const TclObject> /*tokens*/,
1110 TclObject& result) const
1111{
1112 auto delta = Timer::getTime() - reference;
1113 result = narrow_cast<double>(delta) * (1.0 / 1000000.0);
1114}
1115
1116string RealTimeInfo::help(std::span<const TclObject> /*tokens*/) const
1117{
1118 return "Returns the time in seconds since openMSX was started.";
1119}
1120
1121
1122// SoftwareInfoTopic
1123
1125 : InfoTopic(openMSXInfoCommand, "software")
1126 , reactor(reactor_)
1127{
1128}
1129
1131 std::span<const TclObject> tokens, TclObject& result) const
1132{
1133 if (tokens.size() != 3) {
1134 throw CommandException("Wrong number of parameters");
1135 }
1136
1137 Sha1Sum sha1sum(tokens[2].getString());
1138 auto& romDatabase = reactor.getSoftwareDatabase();
1139 const RomInfo* romInfo = romDatabase.fetchRomInfo(sha1sum);
1140 if (!romInfo) {
1141 // no match found
1142 throw CommandException(
1143 "Software with sha1sum ", sha1sum.toString(), " not found");
1144 }
1145
1146 const char* bufStart = romDatabase.getBufferStart();
1147 result.addDictKeyValues("title", romInfo->getTitle(bufStart),
1148 "year", romInfo->getYear(bufStart),
1149 "company", romInfo->getCompany(bufStart),
1150 "country", romInfo->getCountry(bufStart),
1151 "orig_type", romInfo->getOrigType(bufStart),
1152 "remark", romInfo->getRemark(bufStart),
1153 "original", romInfo->getOriginal(),
1154 "mapper_type_name", RomInfo::romTypeToName(romInfo->getRomType()),
1155 "genmsxid", romInfo->getGenMSXid());
1156}
1157
1158string SoftwareInfoTopic::help(std::span<const TclObject> /*tokens*/) const
1159{
1160 return "Returns information about the software "
1161 "given its sha1sum, in a paired list.";
1162}
1163
1164} // namespace openmsx
BaseSetting * setting
void execute(std::span< const TclObject > tokens, TclObject &result) override
Execute this command.
Definition Reactor.cc:891
string help(std::span< const TclObject > tokens) const override
Print help for this command.
Definition Reactor.cc:905
ActivateMachineCommand(CommandController &commandController, Reactor &reactor)
Definition Reactor.cc:884
void tabCompletion(vector< string > &tokens) const override
Attempt tab completion for this command.
Definition Reactor.cc:912
Interpreter & getInterpreter() const final
Definition Command.cc:38
const auto & getStartupScripts() const
ParseStatus getParseStatus() const
const auto & getStartupCommands() const
static void completeFileName(std::vector< std::string > &tokens, const FileContext &context, const RANGE &extra)
Definition Completer.hh:150
static void completeString(std::vector< std::string > &tokens, ITER begin, ITER end, bool caseSensitive=true)
Definition Completer.hh:136
void checkNumArgs(std::span< const TclObject > tokens, unsigned exactly, const char *errMessage) const
Definition Completer.cc:181
void tabCompletion(vector< string > &tokens) const override
Attempt tab completion for this command.
Definition Reactor.cc:1095
string help(std::span< const TclObject > tokens) const override
Print help for this topic.
Definition Reactor.cc:1089
ConfigInfo(InfoCommand &openMSXInfoCommand, const string &configName)
Definition Reactor.cc:1052
void execute(std::span< const TclObject > tokens, TclObject &result) const override
Show info on this topic.
Definition Reactor.cc:1059
string help(std::span< const TclObject > tokens) const override
Print help for this command.
Definition Reactor.cc:821
void execute(std::span< const TclObject > tokens, TclObject &result) override
Execute this command.
Definition Reactor.cc:813
CreateMachineCommand(CommandController &commandController, Reactor &reactor)
Definition Reactor.cc:806
void execute(std::span< const TclObject > tokens, TclObject &result) override
Execute this command.
Definition Reactor.cc:843
void tabCompletion(vector< string > &tokens) const override
Attempt tab completion for this command.
Definition Reactor.cc:855
DeleteMachineCommand(CommandController &commandController, Reactor &reactor)
Definition Reactor.cc:836
string help(std::span< const TclObject > tokens) const override
Print help for this command.
Definition Reactor.cc:850
VideoSystem & getVideoSystem()
Definition Display.cc:95
EnumSettingBase::Map Map
void distributeEvent(Event &&event)
Schedule the given event for delivery.
string help(std::span< const TclObject > tokens) const override
Print help for this command.
Definition Reactor.cc:723
ExitCommand(CommandController &commandController, EventDistributor &distributor)
Definition Reactor.cc:702
void execute(std::span< const TclObject > tokens, TclObject &result) override
Execute this command.
Definition Reactor.cc:709
std::span< const std::string > getPaths() const
std::string resolve(std::string_view filename) const
void execute(std::span< const TclObject > tokens, TclObject &result) override
Execute this command.
Definition Reactor.cc:1017
string help(std::span< const TclObject > tokens) const override
Print help for this command.
Definition Reactor.cc:1023
GetClipboardCommand(CommandController &commandController, Reactor &reactor)
Definition Reactor.cc:1008
BooleanSetting & getPauseSetting()
static void loadConfig(XMLDocument &doc, std::string_view type, std::string_view name)
void execute(std::span< const TclObject > tokens, TclObject &result) override
Execute this command.
Definition Reactor.cc:870
ListMachinesCommand(CommandController &commandController, Reactor &reactor)
Definition Reactor.cc:863
string help(std::span< const TclObject > tokens) const override
Print help for this command.
Definition Reactor.cc:876
std::string loadMachine(const std::string &machine)
std::string_view getMachineID() const
MachineCommand(CommandController &commandController, Reactor &reactor)
Definition Reactor.cc:732
void execute(std::span< const TclObject > tokens, TclObject &result) override
Execute this command.
Definition Reactor.cc:739
void tabCompletion(vector< string > &tokens) const override
Attempt tab completion for this command.
Definition Reactor.cc:764
string help(std::span< const TclObject > tokens) const override
Print help for this command.
Definition Reactor.cc:759
void mute()
This methods (un)mute the sound.
Definition Mixer.cc:115
void unmute()
Definition Mixer.cc:122
Contains the main loop of openMSX.
Definition Reactor.hh:72
GlobalSettings & getGlobalSettings()
Definition Reactor.hh:114
MSXMotherBoard * getMotherBoard() const
Definition Reactor.cc:410
std::shared_ptr< MSXMotherBoard > Board
Definition Reactor.hh:121
CommandController & getCommandController()
Definition Reactor.cc:334
void enterMainLoop()
Definition Reactor.cc:521
GlobalCommandController & getGlobalCommandController()
Definition Reactor.hh:88
InfoCommand & getOpenMSXInfoCommand()
Definition Reactor.cc:339
void switchMachine(const std::string &machine)
Definition Reactor.cc:455
void run(const CommandLineParser &parser)
Main loop.
Definition Reactor.cc:537
Display & getDisplay()
Definition Reactor.hh:90
CliComm & getCliComm()
Definition Reactor.cc:324
GlobalCliComm & getGlobalCliComm()
Definition Reactor.hh:87
Board createEmptyMotherBoard()
Definition Reactor.cc:430
Interpreter & getInterpreter()
Definition Reactor.cc:329
const HotKey & getHotKey() const
Definition Reactor.cc:344
void replaceBoard(MSXMotherBoard &oldBoard, Board newBoard)
Definition Reactor.cc:435
std::string_view getMachineID() const
Definition Reactor.cc:416
Mixer & getMixer()
Definition Reactor.cc:308
const MsxChar2Unicode & getMsxChar2Unicode() const
Definition Reactor.cc:375
static std::vector< std::string > getHwConfigs(std::string_view type)
Definition Reactor.cc:349
RomDatabase & getSoftwareDatabase()
Definition Reactor.cc:316
Board getMachine(std::string_view machineID) const
Definition Reactor.cc:421
auto getMachineIDs() const
Definition Reactor.hh:128
RealTimeInfo(InfoCommand &openMSXInfoCommand)
Definition Reactor.cc:1103
string help(std::span< const TclObject > tokens) const override
Print help for this topic.
Definition Reactor.cc:1116
void execute(std::span< const TclObject > tokens, TclObject &result) const override
Show info on this topic.
Definition Reactor.cc:1109
string help(std::span< const TclObject > tokens) const override
Print help for this command.
Definition Reactor.cc:992
void tabCompletion(vector< string > &tokens) const override
Attempt tab completion for this command.
Definition Reactor.cc:1000
RestoreMachineCommand(CommandController &commandController, Reactor &reactor)
Definition Reactor.cc:957
void execute(std::span< const TclObject > tokens, TclObject &result) override
Execute this command.
Definition Reactor.cc:964
std::string_view getYear(const char *buf) const
Definition RomInfo.hh:46
bool getOriginal() const
Definition RomInfo.hh:62
unsigned getGenMSXid() const
Definition RomInfo.hh:63
std::string_view getTitle(const char *buf) const
Definition RomInfo.hh:43
std::string_view getCompany(const char *buf) const
Definition RomInfo.hh:49
std::string_view getRemark(const char *buf) const
Definition RomInfo.hh:58
static std::string_view romTypeToName(RomType type)
Definition RomInfo.cc:188
std::string_view getCountry(const char *buf) const
Definition RomInfo.hh:52
RomType getRomType() const
Definition RomInfo.hh:61
std::string_view getOrigType(const char *buf) const
Definition RomInfo.hh:55
void execute(std::span< const TclObject > tokens, TclObject &result) override
Execute this command.
Definition Reactor.cc:1038
SetClipboardCommand(CommandController &commandController, Reactor &reactor)
Definition Reactor.cc:1031
string help(std::span< const TclObject > tokens) const override
Print help for this command.
Definition Reactor.cc:1044
This class represents the result of a sha1 calculation (a 160-bit value).
Definition sha1.hh:24
std::string toString() const
std::string help(std::span< const TclObject > tokens) const override
Print help for this topic.
Definition Reactor.cc:1158
void execute(std::span< const TclObject > tokens, TclObject &result) const override
Show info on this topic.
Definition Reactor.cc:1130
SoftwareInfoTopic(InfoCommand &openMSXInfoCommand, Reactor &reactor)
Definition Reactor.cc:1124
void tabCompletion(vector< string > &tokens) const override
Attempt tab completion for this command.
Definition Reactor.cc:949
StoreMachineCommand(CommandController &commandController, Reactor &reactor)
Definition Reactor.cc:920
string help(std::span< const TclObject > tokens) const override
Print help for this command.
Definition Reactor.cc:941
void execute(std::span< const TclObject > tokens, TclObject &result) override
Execute this command.
Definition Reactor.cc:927
void detach(Observer< T > &observer)
Definition Subject.hh:55
void attach(Observer< T > &observer)
Definition Subject.hh:49
void addListElements(ITER first, ITER last)
Definition TclObject.hh:132
void addDictKeyValue(const Key &key, const Value &value)
Definition TclObject.hh:145
void addDictKeyValues(Args &&... args)
Definition TclObject.hh:148
void tabCompletion(vector< string > &tokens) const override
Attempt tab completion for this command.
Definition Reactor.cc:798
TestMachineCommand(CommandController &commandController, Reactor &reactor)
Definition Reactor.cc:772
void execute(std::span< const TclObject > tokens, TclObject &result) override
Execute this command.
Definition Reactor.cc:779
string help(std::span< const TclObject > tokens) const override
Print help for this command.
Definition Reactor.cc:791
virtual void setClipboardText(zstring_view text)=0
virtual std::string getClipboardText()=0
ALWAYS_INLINE void serialize(const char *tag, T &t, Args &&...args)
Definition serialize.hh:970
ALWAYS_INLINE void serialize(const char *tag, const T &t, Args &&...args)
Definition serialize.hh:891
constexpr double e
Definition Math.hh:21
string expandTilde(string path)
Expand the '~' character to the users home directory.
bool isRegularFile(const Stat &st)
string join(string_view part1, string_view part2)
Join two paths.
bool isMainThread()
Returns true when called from the main thread.
Definition Thread.cc:15
uint64_t getTime()
Get current (real) time in us.
Definition Timer.cc:7
This file implemented 3 utility functions:
Definition Autofire.cc:11
const FileContext & systemFileContext()
const FileContext & userFileContext()
std::variant< KeyUpEvent, KeyDownEvent, MouseMotionEvent, MouseButtonUpEvent, MouseButtonDownEvent, MouseWheelEvent, JoystickAxisMotionEvent, JoystickHatEvent, JoystickButtonUpEvent, JoystickButtonDownEvent, OsdControlReleaseEvent, OsdControlPressEvent, WindowEvent, TextEvent, FileDropEvent, QuitEvent, FinishFrameEvent, CliCommandEvent, GroupEvent, BootEvent, FrameDrawnEvent, BreakEvent, SwitchRendererEvent, TakeReverseSnapshotEvent, AfterTimedEvent, MachineLoadedEvent, MachineActivatedEvent, MachineDeactivatedEvent, MidiInReaderEvent, MidiInWindowsEvent, MidiInCoreMidiEvent, MidiInCoreMidiVirtualEvent, MidiInALSAEvent, Rs232TesterEvent, Rs232NetEvent, ImGuiDelayedActionEvent, ImGuiActiveEvent > Event
Definition Event.hh:444
int exitCode
Definition Reactor.cc:68
const FileContext & preferSystemFileContext()
bool foreach_file_and_directory(std::string path, FileAction fileAction, DirAction dirAction)
auto unique(ForwardRange &&range)
Definition ranges.hh:222
auto find(InputRange &&range, const T &value)
Definition ranges.hh:160
constexpr void sort(RandomAccessRange &&range)
Definition ranges.hh:49
constexpr auto transform(Range &&range, UnaryOp op)
Definition view.hh:520
ITER find_unguarded(ITER first, ITER last, const VAL &val, Proj proj={})
Faster alternative to 'find' when it's guaranteed that the value will be found (if not the behavior i...
Definition stl.hh:72
void move_pop_back(VECTOR &v, typename VECTOR::iterator it)
Erase the pointed to element from the given vector.
Definition stl.hh:134
auto rfind_unguarded(RANGE &range, const VAL &val, Proj proj={})
Similar to the find(_if)_unguarded functions above, but searches from the back to front.
Definition stl.hh:109
constexpr bool contains(ITER first, ITER last, const VAL &val)
Check if a range contains a given value, using linear search.
Definition stl.hh:32
std::string strCat()
Definition strCat.hh:703
#define UNREACHABLE
constexpr auto end(const zstring_view &x)