openMSX
LedStatus.cc
Go to the documentation of this file.
1 #include "LedStatus.hh"
2 #include "MSXCliComm.hh"
3 #include "ReadOnlySetting.hh"
4 #include "CommandController.hh"
5 #include "Timer.hh"
6 #include <memory>
7 
8 namespace openmsx {
9 
10 static std::string getLedName(LedStatus::Led led)
11 {
12  static const char* const names[LedStatus::NUM_LEDS] = {
13  "power", "caps", "kana", "pause", "turbo", "FDD"
14  };
15  return names[led];
16 }
17 
19  RTScheduler& rtScheduler,
20  CommandController& commandController,
21  MSXCliComm& msxCliComm_)
22  : RTSchedulable(rtScheduler)
23  , msxCliComm(msxCliComm_)
24  , interp(commandController.getInterpreter())
25 {
26  lastTime = Timer::getTime();
27  for (int i = 0; i < NUM_LEDS; ++i) {
28  ledValue[i] = false;
29  std::string name = getLedName(static_cast<Led>(i));
30  ledStatus[i] = std::make_unique<ReadOnlySetting>(
31  commandController, "led_" + name,
32  "Current status for LED: " + name,
33  TclObject("off"));
34  }
35 }
36 
37 LedStatus::~LedStatus() = default;
38 
39 void LedStatus::setLed(Led led, bool status)
40 {
41  if (ledValue[led] == status) return;
42  ledValue[led] = status;
43 
44  // Some MSX programs generate tons of LED events (e.g. New Era uses
45  // the LEDs as a VU meter while playing samples). Without throttling
46  // all these events overload the host CPU. That's why we limit it to
47  // 100 events per second.
48  auto now = Timer::getTime();
49  auto diff = now - lastTime;
50  if (diff > 10000) { // 1/100 s
51  // handle now
52  lastTime = now;
53  handleEvent(led);
54  } else {
55  // schedule to handle it later, if we didn't plan to do so already
56  if (!isPendingRT()) {
57  scheduleRT(10000 - diff);
58  }
59  }
60 }
61 
62 void LedStatus::handleEvent(Led led) noexcept
63 {
64  static const string_view ON = "on";
65  static const string_view OFF = "off";
66  const string_view& str = ledValue[led] ? ON : OFF;
67 
68  ledStatus[led]->setReadOnlyValue(TclObject(str));
69  msxCliComm.update(CliComm::LED, getLedName(led), str);
70 }
71 
72 void LedStatus::executeRT()
73 {
74  for (int i = 0; i < NUM_LEDS; ++i) {
75  if (ledValue[i] != ledStatus[i]->getValue().getBoolean(interp)) {
76  handleEvent(static_cast<Led>(i));
77  }
78  }
79  lastTime = Timer::getTime();
80 }
81 
82 } // namespace openmsx
LedStatus(RTScheduler &rtScheduler, CommandController &commandController, MSXCliComm &msxCliComm)
Definition: LedStatus.cc:18
void update(UpdateType type, string_view name, string_view value) override
Definition: MSXCliComm.cc:18
bool isPendingRT() const
Thanks to enen for testing this on a real cartridge:
Definition: Autofire.cc:5
void scheduleRT(uint64_t delta)
This class implements a (close approximation) of the std::string_view class.
Definition: string_view.hh:16
void setLed(Led led, bool status)
Definition: LedStatus.cc:39
uint64_t getTime()
Get current (real) time in us.
Definition: Timer.cc:8