openMSX
AfterCommand.cc
Go to the documentation of this file.
1 #include "AfterCommand.hh"
2 #include "CommandController.hh"
3 #include "CliComm.hh"
4 #include "Event.hh"
5 #include "Schedulable.hh"
6 #include "EventDistributor.hh"
7 #include "InputEventFactory.hh"
8 #include "Reactor.hh"
9 #include "MSXMotherBoard.hh"
10 #include "ObjectPool.hh"
11 #include "RTSchedulable.hh"
12 #include "EmuTime.hh"
13 #include "CommandException.hh"
14 #include "StringOp.hh"
15 #include "TclObject.hh"
16 #include "ranges.hh"
17 #include "stl.hh"
18 #include "unreachable.hh"
19 #include "view.hh"
20 #include <iterator>
21 #include <memory>
22 #include <sstream>
23 #include <variant>
24 
25 namespace openmsx {
26 
27 class AfterCmd
28 {
29 public:
30  [[nodiscard]] const auto& getCommand() const { return command; }
31  [[nodiscard]] auto getId() const { return id; }
32  [[nodiscard]] auto getIdStr() const { return tmpStrCat("after#", id); }
33  void execute();
34 protected:
38 
41  const unsigned id;
42  static inline unsigned lastAfterId = 0;
43 };
44 
45 class AfterTimedCmd : public AfterCmd, private Schedulable
46 {
47 public:
48  [[nodiscard]] double getTime() const;
49  void reschedule();
50 protected:
51  AfterTimedCmd(Scheduler& scheduler,
53  TclObject command, double time);
54 private:
55  void executeUntil(EmuTime::param time) override;
56  void schedulerDeleted() override;
57 
58  double time; // Zero when expired, otherwise the original duration (to
59  // be able to reschedule for 'after idle').
60 };
61 
62 class AfterTimeCmd final : public AfterTimedCmd
63 {
64 public:
65  AfterTimeCmd(Scheduler& scheduler,
67  TclObject command, double time);
68 };
69 
70 class AfterIdleCmd final : public AfterTimedCmd
71 {
72 public:
73  AfterIdleCmd(Scheduler& scheduler,
75  TclObject command, double time);
76 };
77 
78 class AfterSimpleEventCmd final : public AfterCmd
79 {
80 public:
83  EventType type);
84  [[nodiscard]] std::string_view getType() const;
85  [[nodiscard]] EventType getTypeEnum() const { return type; }
86 private:
87  EventType type;
88 };
89 
90 class AfterInputEventCmd final : public AfterCmd
91 {
92 public:
94  Event event,
96  [[nodiscard]] const Event& getEvent() const { return event; }
97 private:
98  Event event;
99 };
100 
101 class AfterRealTimeCmd final : public AfterCmd, private RTSchedulable
102 {
103 public:
105  TclObject command, double time);
106 
107 private:
108  void executeRT() override;
109 };
110 
111 using AllAfterCmds = std::variant<AfterTimeCmd,
112  AfterIdleCmd,
116 static ObjectPool<AllAfterCmds> afterCmdPool;
117 
118 
120  EventDistributor& eventDistributor_,
121  CommandController& commandController_)
122  : Command(commandController_, "after")
123  , reactor(reactor_)
124  , eventDistributor(eventDistributor_)
125 {
126  // TODO DETACHED <-> EMU types should be cleaned up
127  // (moved to event iso listener?)
128  eventDistributor.registerEventListener(
129  EventType::KEY_UP, *this);
130  eventDistributor.registerEventListener(
131  EventType::KEY_DOWN, *this);
132  eventDistributor.registerEventListener(
133  EventType::MOUSE_MOTION, *this);
134  eventDistributor.registerEventListener(
136  eventDistributor.registerEventListener(
138  eventDistributor.registerEventListener(
139  EventType::MOUSE_WHEEL, *this);
140  eventDistributor.registerEventListener(
142  eventDistributor.registerEventListener(
143  EventType::JOY_HAT, *this);
144  eventDistributor.registerEventListener(
145  EventType::JOY_BUTTON_UP, *this);
146  eventDistributor.registerEventListener(
148  eventDistributor.registerEventListener(
149  EventType::FINISH_FRAME, *this);
150  eventDistributor.registerEventListener(
151  EventType::BREAK, *this);
152  eventDistributor.registerEventListener(
153  EventType::QUIT, *this);
154  eventDistributor.registerEventListener(
155  EventType::BOOT, *this);
156  eventDistributor.registerEventListener(
158  eventDistributor.registerEventListener(
159  EventType::AFTER_TIMED, *this);
160 }
161 
163 {
164  for (auto idx : afterCmds) {
165  afterCmdPool.remove(idx);
166  }
167 
168  eventDistributor.unregisterEventListener(
169  EventType::AFTER_TIMED, *this);
170  eventDistributor.unregisterEventListener(
172  eventDistributor.unregisterEventListener(
173  EventType::BOOT, *this);
174  eventDistributor.unregisterEventListener(
175  EventType::QUIT, *this);
176  eventDistributor.unregisterEventListener(
177  EventType::BREAK, *this);
178  eventDistributor.unregisterEventListener(
179  EventType::FINISH_FRAME, *this);
180  eventDistributor.unregisterEventListener(
182  eventDistributor.unregisterEventListener(
183  EventType::JOY_BUTTON_UP, *this);
184  eventDistributor.unregisterEventListener(
185  EventType::JOY_HAT, *this);
186  eventDistributor.unregisterEventListener(
188  eventDistributor.unregisterEventListener(
189  EventType::MOUSE_WHEEL, *this);
190  eventDistributor.unregisterEventListener(
192  eventDistributor.unregisterEventListener(
194  eventDistributor.unregisterEventListener(
195  EventType::MOUSE_MOTION, *this);
196  eventDistributor.unregisterEventListener(
197  EventType::KEY_DOWN, *this);
198  eventDistributor.unregisterEventListener(
199  EventType::KEY_UP, *this);
200 }
201 
203 {
204  if (tokens.size() < 2) {
205  throw CommandException("Missing argument");
206  }
207  std::string_view subCmd = tokens[1].getString();
208  if (subCmd == "time") {
209  afterTime(tokens, result);
210  } else if (subCmd == "realtime") {
211  afterRealTime(tokens, result);
212  } else if (subCmd == "idle") {
213  afterIdle(tokens, result);
214  } else if (subCmd == "frame") {
215  afterSimpleEvent(tokens, result, EventType::FINISH_FRAME);
216  } else if (subCmd == "break") {
217  afterSimpleEvent(tokens, result, EventType::BREAK);
218  } else if (subCmd == "quit") {
219  afterSimpleEvent(tokens, result, EventType::QUIT);
220  } else if (subCmd == "boot") {
221  afterSimpleEvent(tokens, result, EventType::BOOT);
222  } else if (subCmd == "machine_switch") {
223  afterSimpleEvent(tokens, result, EventType::MACHINE_LOADED);
224  } else if (subCmd == "info") {
225  afterInfo(tokens, result);
226  } else if (subCmd == "cancel") {
227  afterCancel(tokens, result);
228  } else {
229  // A valid integer?
230  if (auto time = tokens[1].getOptionalInt()) {
231  afterTclTime(*time, tokens, result);
232  } else {
233  // A valid event name?
234  try {
235  afterInputEvent(
237  tokens[1], getInterpreter()),
238  tokens, result);
239  } catch (MSXException&) {
240  throw SyntaxError();
241  }
242  }
243  }
244 }
245 
246 static double getTime(Interpreter& interp, const TclObject& obj)
247 {
248  double time = obj.getDouble(interp);
249  if (time < 0) {
250  throw CommandException("Not a valid time specification");
251  }
252  return time;
253 }
254 
255 void AfterCommand::afterTime(span<const TclObject> tokens, TclObject& result)
256 {
257  checkNumArgs(tokens, 4, Prefix{2}, "seconds command");
258  MSXMotherBoard* motherBoard = reactor.getMotherBoard();
259  if (!motherBoard) return;
260  double time = getTime(getInterpreter(), tokens[2]);
261  auto [idx, ptr] = afterCmdPool.emplace(
262  std::in_place_type_t<AfterTimeCmd>{},
263  motherBoard->getScheduler(), *this, tokens[3], time);
264  result = std::get<AfterTimeCmd>(*ptr).getIdStr();
265  afterCmds.push_back(idx);
266 }
267 
268 void AfterCommand::afterRealTime(span<const TclObject> tokens, TclObject& result)
269 {
270  checkNumArgs(tokens, 4, Prefix{2}, "seconds command");
271  double time = getTime(getInterpreter(), tokens[2]);
272  auto [idx, ptr] = afterCmdPool.emplace(
273  std::in_place_type_t<AfterRealTimeCmd>{},
274  reactor.getRTScheduler(), *this, tokens[3], time);
275  result = std::get<AfterRealTimeCmd>(*ptr).getIdStr();
276  afterCmds.push_back(idx);
277 }
278 
279 void AfterCommand::afterTclTime(
280  int ms, span<const TclObject> tokens, TclObject& result)
281 {
282  TclObject command;
283  command.addListElements(view::drop(tokens, 2));
284  auto [idx, ptr] = afterCmdPool.emplace(
285  std::in_place_type_t<AfterRealTimeCmd>{},
286  reactor.getRTScheduler(), *this, command, ms / 1000.0);
287  result = std::get<AfterRealTimeCmd>(*ptr).getIdStr();
288  afterCmds.push_back(idx);
289 }
290 
291 void AfterCommand::afterSimpleEvent(span<const TclObject> tokens, TclObject& result, EventType type)
292 {
293  checkNumArgs(tokens, 3, "command");
294  auto [idx, ptr] = afterCmdPool.emplace(
295  std::in_place_type_t<AfterSimpleEventCmd>{},
296  *this, tokens[2], type);
297  result = std::get<AfterSimpleEventCmd>(*ptr).getIdStr();
298  afterCmds.push_back(idx);
299 }
300 
301 void AfterCommand::afterInputEvent(
302  const Event& event, span<const TclObject> tokens, TclObject& result)
303 {
304  checkNumArgs(tokens, 3, "command");
305  auto [idx, ptr] = afterCmdPool.emplace(
306  std::in_place_type_t<AfterInputEventCmd>{},
307  *this, event, tokens[2]);
308  result = std::get<AfterInputEventCmd>(*ptr).getIdStr();
309  afterCmds.push_back(idx);
310 }
311 
312 void AfterCommand::afterIdle(span<const TclObject> tokens, TclObject& result)
313 {
314  checkNumArgs(tokens, 4, Prefix{2}, "seconds command");
315  MSXMotherBoard* motherBoard = reactor.getMotherBoard();
316  if (!motherBoard) return;
317  double time = getTime(getInterpreter(), tokens[2]);
318  auto [idx, ptr] = afterCmdPool.emplace(
319  std::in_place_type_t<AfterIdleCmd>{},
320  motherBoard->getScheduler(), *this, tokens[3], time);
321  result = std::get<AfterIdleCmd>(*ptr).getIdStr();
322  afterCmds.push_back(idx);
323 }
324 
325 void AfterCommand::afterInfo(span<const TclObject> /*tokens*/, TclObject& result)
326 {
327  auto printTime = [](std::ostream& os, const AfterTimedCmd& cmd) {
328  os.precision(3);
329  os << std::fixed << std::showpoint << cmd.getTime() << ' ';
330  };
331 
332  std::ostringstream str;
333  for (auto idx : afterCmds) {
334  auto& var = afterCmdPool[idx];
335  std::visit([&](AfterCmd& cmd) { str << cmd.getIdStr() << ": "; }, var);
337  [&](AfterTimeCmd& cmd ) { str << "time "; printTime(str, cmd); },
338  [&](AfterIdleCmd& cmd ) { str << "idle "; printTime(str, cmd); },
339  [&](AfterSimpleEventCmd& cmd ) { str << cmd.getType() << ' '; },
340  [&](AfterInputEventCmd& cmd ) { str << toString(cmd.getEvent()) << ' '; },
341  [&](AfterRealTimeCmd& /*cmd*/) { str << "realtime "; }
342  }, var);
343  std::visit([&](AfterCmd& cmd) { str << cmd.getCommand().getString() << '\n'; }, var);
344  }
345  result = str.str();
346 }
347 
348 void AfterCommand::afterCancel(span<const TclObject> tokens, TclObject& /*result*/)
349 {
350  checkNumArgs(tokens, AtLeast{3}, "id|command");
351  if (tokens.size() == 3) {
352  if (auto idStr = tokens[2].getString(); StringOp::startsWith(idStr, "after#")) {
353  if (auto id = StringOp::stringTo<unsigned>(idStr.substr(6))) {
354  auto equalId = [id = *id](Index idx) {
355  return std::visit([&](AfterCmd& cmd) {
356  return cmd.getId() == id;
357  }, afterCmdPool[idx]);
358  };
359  if (auto it = ranges::find_if(afterCmds, equalId);
360  it != end(afterCmds)) {
361  auto idx = *it;
362  afterCmds.erase(it);
363  afterCmdPool.remove(idx);
364  return;
365  }
366  }
367  }
368  }
369  TclObject command;
370  command.addListElements(view::drop(tokens, 2));
371  std::string_view cmdStr = command.getString();
372  auto equalCmd = [&](Index idx) {
373  return std::visit([&](AfterCmd& cmd) {
374  return cmd.getCommand() == cmdStr;
375  }, afterCmdPool[idx]);
376  };
377  if (auto it = ranges::find_if(afterCmds, equalCmd);
378  it != end(afterCmds)) {
379  auto idx = *it;
380  afterCmds.erase(it);
381  afterCmdPool.remove(idx);
382  // Tcl manual is not clear about this, but it seems
383  // there's only occurrences of this command canceled.
384  // It's also not clear which of the (possibly) several
385  // matches is canceled.
386  return;
387  }
388  // It's not an error if no match is found
389 }
390 
391 std::string AfterCommand::help(span<const TclObject> /*tokens*/) const
392 {
393  return "after time <seconds> <command> execute a command after some time (MSX time)\n"
394  "after realtime <seconds> <command> execute a command after some time (realtime)\n"
395  "after idle <seconds> <command> execute a command after some time being idle\n"
396  "after frame <command> execute a command after a new frame is drawn\n"
397  "after break <command> execute a command after a breakpoint is reached\n"
398  "after boot <command> execute a command after a (re)boot\n"
399  "after machine_switch <command> execute a command after a switch to a new machine\n"
400  "after info list all postponed commands\n"
401  "after cancel <id> cancel the postponed command with given id\n";
402 }
403 
404 void AfterCommand::tabCompletion(std::vector<std::string>& tokens) const
405 {
406  if (tokens.size() == 2) {
407  using namespace std::literals;
408  static constexpr std::array cmds = {
409  "time"sv, "realtime"sv, "idle"sv, "frame"sv, "break"sv, "boot"sv,
410  "machine_switch"sv, "info"sv, "cancel"sv,
411  };
412  completeString(tokens, cmds);
413  }
414  // TODO : make more complete
415 }
416 
417 // Execute the cmds for which the predicate returns true, and erase those from afterCmds.
418 template<typename PRED> void AfterCommand::executeMatches(PRED pred)
419 {
420  static std::vector<Index> matches; // static to keep capacity for next call
421  assert(matches.empty());
422 
423  // Usually there are very few matches (typically even 0 or 1), so no
424  // need to reserve() space.
425  auto p = partition_copy_remove(afterCmds, std::back_inserter(matches), pred);
426  afterCmds.erase(p.second, end(afterCmds));
427  for (auto idx : matches) {
428  std::visit([](AfterCmd& cmd) { cmd.execute(); },
429  afterCmdPool[idx]);
430  afterCmdPool.remove(idx);
431  }
432  matches.clear(); // for next call (but keep capacity)
433 }
434 
436  bool operator()(AfterCommand::Index idx) const {
437  return std::visit(overloaded {
438  [&](AfterSimpleEventCmd& cmd) { return cmd.getTypeEnum() == type; },
439  [&](AfterCmd& /*cmd*/) { return false; }
440  }, afterCmdPool[idx]);
441  }
443 };
444 void AfterCommand::executeSimpleEvents(EventType type)
445 {
446  executeMatches(AfterSimpleEventPred{type});
447 }
448 
450  bool operator()(AfterCommand::Index idx) const {
451  return std::visit(overloaded {
452  [&](AfterTimedCmd& cmd) { return cmd.getTime() == 0.0; },
453  [&](AfterCmd& /*cmd*/) { return false; }
454  }, afterCmdPool[idx]);
455  }
456 };
457 
459  explicit AfterInputEventPred(Event event_)
460  : event(std::move(event_)) {}
461  bool operator()(AfterCommand::Index idx) const {
462  return std::visit(overloaded {
463  [&](AfterInputEventCmd& cmd) { return matches(cmd.getEvent(), event); },
464  [&](AfterCmd& /*cmd*/) { return false; }
465  }, afterCmdPool[idx]);
466  }
468 };
469 
470 int AfterCommand::signalEvent(const Event& event) noexcept
471 {
473  [&](const SimpleEvent&) {
474  executeSimpleEvents(getType(event));
475  },
476  [&](const FinishFrameEvent&) {
477  executeSimpleEvents(EventType::FINISH_FRAME);
478  },
479  [&](const QuitEvent&) {
480  executeSimpleEvents(EventType::QUIT);
481  },
482  [&](const AfterTimedEvent&) {
483  executeMatches(AfterEmuTimePred());
484  },
485  [&](const EventBase&) {
486  executeMatches(AfterInputEventPred(event));
487  for (auto idx : afterCmds) {
489  [](AfterIdleCmd& cmd) { cmd.reschedule(); },
490  [](AfterCmd& /*cmd*/) { /*nothing*/ }
491  }, afterCmdPool[idx]);
492  }
493  }
494  }, event);
495  return 0;
496 }
497 
498 
499 // class AfterCmd
500 
501 AfterCmd::AfterCmd(AfterCommand& afterCommand_, TclObject command_)
502  : afterCommand(afterCommand_), command(std::move(command_)), id(++lastAfterId)
503 {
504 }
505 
507 {
508  try {
510  } catch (CommandException& e) {
512  "Error executing delayed command: ", e.getMessage());
513  }
514 }
515 
517 {
518  auto equalThis = [&](AfterCommand::Index idx) {
519  return std::visit([&](AfterCmd& cmd) { return &cmd == this; },
520  afterCmdPool[idx]);
521  };
522  auto it = rfind_if_unguarded(afterCommand.afterCmds, equalThis);
523  auto idx = *it;
524  afterCommand.afterCmds.erase(it); // move_pop_back ?
525  return idx;
526 }
527 
528 
529 // class AfterTimedCmd
530 
532  Scheduler& scheduler_,
533  AfterCommand& afterCommand_,
534  TclObject command_, double time_)
535  : AfterCmd(afterCommand_, std::move(command_))
536  , Schedulable(scheduler_)
537  , time(time_)
538 {
539  reschedule();
540 }
541 
543 {
544  return time;
545 }
546 
548 {
549  removeSyncPoint();
551 }
552 
553 void AfterTimedCmd::executeUntil(EmuTime::param /*time*/)
554 {
555  time = 0.0; // execute on next event
556  afterCommand.eventDistributor.distributeEvent(
557  Event::create<AfterTimedEvent>());
558 }
559 
560 void AfterTimedCmd::schedulerDeleted()
561 {
562  auto idx = removeSelf();
563  afterCmdPool.remove(idx);
564 }
565 
566 
567 // class AfterTimeCmd
568 
570  Scheduler& scheduler_,
571  AfterCommand& afterCommand_,
572  TclObject command_, double time_)
573  : AfterTimedCmd(scheduler_, afterCommand_, std::move(command_), time_)
574 {
575 }
576 
577 
578 // class AfterIdleCmd
579 
581  Scheduler& scheduler_,
582  AfterCommand& afterCommand_,
583  TclObject command_, double time_)
584  : AfterTimedCmd(scheduler_, afterCommand_, std::move(command_), time_)
585 {
586 }
587 
588 
589 // class AfterSimpleEventCmd
590 
592  AfterCommand& afterCommand_, TclObject command_,
593  EventType type_)
594  : AfterCmd(afterCommand_, std::move(command_)), type(type_)
595 {
596 }
597 
598 std::string_view AfterSimpleEventCmd::getType() const
599 {
600  switch (type) {
601  case EventType::FINISH_FRAME: return "frame";
602  case EventType::BREAK: return "break";
603  case EventType::BOOT: return "boot";
604  case EventType::QUIT: return "quit";
605  case EventType::MACHINE_LOADED: return "machine_switch";
606  default: UNREACHABLE; return "";
607  }
608 }
609 
610 
611 // AfterInputEventCmd
612 
614  AfterCommand& afterCommand_,
615  Event event_,
616  TclObject command_)
617  : AfterCmd(afterCommand_, std::move(command_))
618  , event(std::move(event_))
619 {
620 }
621 
622 
623 // class AfterRealTimeCmd
624 
626  RTScheduler& rtScheduler, AfterCommand& afterCommand_,
627  TclObject command_, double time)
628  : AfterCmd(afterCommand_, std::move(command_))
629  , RTSchedulable(rtScheduler)
630 {
631  scheduleRT(uint64_t(time * 1e6)); // micro seconds
632 }
633 
634 void AfterRealTimeCmd::executeRT()
635 {
636  // Remove self before executing, but keep self alive till the end of
637  // this method. Otherwise execute could execute 'after cancel ..' and
638  // removeSelf() asserts that it can't find itself anymore.
639  auto idx = removeSelf();
640  execute();
641  afterCmdPool.remove(idx);
642 }
643 
644 } // namespace openmsx
uintptr_t id
Definition: Interpreter.cc:26
AfterCmd(AfterCommand &afterCommand, TclObject command)
const auto & getCommand() const
Definition: AfterCommand.cc:30
const unsigned id
Definition: AfterCommand.cc:41
auto getIdStr() const
Definition: AfterCommand.cc:32
AfterCommand & afterCommand
Definition: AfterCommand.cc:39
AfterCommand::Index removeSelf()
auto getId() const
Definition: AfterCommand.cc:31
static unsigned lastAfterId
Definition: AfterCommand.cc:42
friend class AfterTimedCmd
Definition: AfterCommand.hh:53
friend class AfterCmd
Definition: AfterCommand.hh:52
std::string help(span< const TclObject > tokens) const override
Print help for this command.
void execute(span< const TclObject > tokens, TclObject &result) override
Execute this command.
AfterCommand(Reactor &reactor, EventDistributor &eventDistributor, CommandController &commandController)
friend class AfterRealTimeCmd
Definition: AfterCommand.hh:54
void tabCompletion(std::vector< std::string > &tokens) const override
Attempt tab completion for this command.
AfterIdleCmd(Scheduler &scheduler, AfterCommand &afterCommand, TclObject command, double time)
AfterInputEventCmd(AfterCommand &afterCommand, Event event, TclObject command)
const Event & getEvent() const
Definition: AfterCommand.cc:96
AfterRealTimeCmd(RTScheduler &rtScheduler, AfterCommand &afterCommand, TclObject command, double time)
EventType getTypeEnum() const
Definition: AfterCommand.cc:85
std::string_view getType() const
AfterSimpleEventCmd(AfterCommand &afterCommand, TclObject command, EventType type)
AfterTimeCmd(Scheduler &scheduler, AfterCommand &afterCommand, TclObject command, double time)
double getTime() const
AfterTimedCmd(Scheduler &scheduler, AfterCommand &afterCommand, TclObject command, double time)
Send when an after-emutime command should be executed.
Definition: Event.hh:440
void printWarning(std::string_view message)
Definition: CliComm.cc:10
CommandController & getCommandController() const
Definition: Command.hh:25
Interpreter & getInterpreter() const final
Definition: Command.cc:38
virtual CliComm & getCliComm()=0
void checkNumArgs(span< const TclObject > tokens, unsigned exactly, const char *errMessage) const
Definition: Completer.cc:176
static void completeString(std::vector< std::string > &tokens, ITER begin, ITER end, bool caseSensitive=true)
Definition: Completer.hh:133
void unregisterEventListener(EventType type, EventListener &listener)
Unregisters a previously registered event listener.
void distributeEvent(Event &&event)
Schedule the given event for delivery.
void registerEventListener(EventType type, EventListener &listener, Priority priority=OTHER)
Registers a given object to receive certain events.
This event is send when a device (v99x8, v9990, video9000, laserdisc) reaches the end of a frame.
Definition: Event.hh:385
const std::string & getMessage() const &
Definition: MSXException.hh:23
void scheduleRT(uint64_t delta)
Contains the main loop of openMSX.
Definition: Reactor.hh:68
MSXMotherBoard * getMotherBoard() const
Definition: Reactor.cc:374
RTScheduler & getRTScheduler()
Definition: Reactor.hh:81
Every class that wants to get scheduled at some point must inherit from this class.
Definition: Schedulable.hh:34
void setSyncPoint(EmuTime::param timestamp)
Definition: Schedulable.cc:23
EmuTime::param getCurrentTime() const
Convenience method: This is the same as getScheduler().getCurrentTime().
Definition: Schedulable.cc:49
TclObject executeCommand(Interpreter &interp, bool compile=false)
Interpret this TclObject as a command and execute it.
Definition: TclObject.cc:181
double getDouble(Interpreter &interp) const
Definition: TclObject.cc:101
Definition: span.hh:126
constexpr index_type size() const noexcept
Definition: span.hh:296
bool startsWith(string_view total, string_view part)
Definition: StringOp.cc:29
Event createInputEvent(const TclObject &str, Interpreter &interp)
This file implemented 3 utility functions:
Definition: Autofire.cc:9
EventType
Definition: Event.hh:507
bool matches(const Event &self, const Event &other)
Does this event 'match' the given event.
Definition: Event.cc:212
auto visit(Visitor &&visitor, const Event &event)
Definition: Event.hh:653
std::variant< AfterTimeCmd, AfterIdleCmd, AfterSimpleEventCmd, AfterInputEventCmd, AfterRealTimeCmd > AllAfterCmds
EventType getType(const Event &event)
Definition: Event.hh:645
std::string toString(const Event &event)
Get a string representation of this event.
Definition: Event.cc:176
auto find_if(InputRange &&range, UnaryPredicate pred)
Definition: ranges.hh:142
constexpr auto drop(Range &&range, size_t n)
Definition: view.hh:373
std::pair< OutputIt, ForwardIt > partition_copy_remove(ForwardIt first, ForwardIt last, OutputIt out_true, UnaryPredicate p)
This is like a combination of partition_copy() and remove().
Definition: stl.hh:168
constexpr auto rfind_if_unguarded(RANGE &range, PRED pred)
Definition: stl.hh:116
TemporaryString tmpStrCat(Ts &&... ts)
Definition: strCat.hh:659
bool operator()(AfterCommand::Index idx) const
bool operator()(AfterCommand::Index idx) const
bool operator()(AfterCommand::Index idx) const
#define UNREACHABLE
Definition: unreachable.hh:38
constexpr auto end(const zstring_view &x)
Definition: zstring_view.hh:84