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