Bug 1472338: part 2) Change `clipboard.readText()` to read from the clipboard asynchr...
[gecko.git] / dom / midi / midirMIDIPlatformService.cpp
blob8ed24047b99f944fc9f8c2c33a3c8e46a5d5420e
1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
3 * You can obtain one at http://mozilla.org/MPL/2.0/. */
5 #include "midirMIDIPlatformService.h"
6 #include "mozilla/StaticMutex.h"
7 #include "mozilla/TimeStamp.h"
8 #include "mozilla/dom/MIDIPort.h"
9 #include "mozilla/dom/MIDITypes.h"
10 #include "mozilla/dom/MIDIPortInterface.h"
11 #include "mozilla/dom/MIDIPortParent.h"
12 #include "mozilla/dom/MIDIPlatformRunnables.h"
13 #include "mozilla/dom/MIDIUtils.h"
14 #include "mozilla/ipc/BackgroundParent.h"
15 #include "mozilla/Unused.h"
16 #include "nsIThread.h"
17 #include "mozilla/Logging.h"
18 #include "MIDILog.h"
20 using namespace mozilla;
21 using namespace mozilla::dom;
22 using namespace mozilla::ipc;
24 static_assert(sizeof(TimeStamp) == sizeof(GeckoTimeStamp));
26 /**
27 * Runnable used for to send messages asynchronously on the I/O thread.
29 class SendRunnable : public MIDIBackgroundRunnable {
30 public:
31 explicit SendRunnable(const nsAString& aPortID, const MIDIMessage& aMessage)
32 : MIDIBackgroundRunnable("SendRunnable"),
33 mPortID(aPortID),
34 mMessage(aMessage) {}
35 ~SendRunnable() = default;
36 virtual void RunInternal() {
37 AssertIsOnBackgroundThread();
38 if (!MIDIPlatformService::IsRunning()) {
39 // Some send operations might outlive the service, bail out and do nothing
40 return;
42 midirMIDIPlatformService* srv =
43 static_cast<midirMIDIPlatformService*>(MIDIPlatformService::Get());
44 srv->SendMessage(mPortID, mMessage);
47 private:
48 nsString mPortID;
49 MIDIMessage mMessage;
52 // static
53 StaticMutex midirMIDIPlatformService::gBackgroundThreadMutex;
55 // static
56 nsCOMPtr<nsIThread> midirMIDIPlatformService::gBackgroundThread;
58 midirMIDIPlatformService::midirMIDIPlatformService()
59 : mImplementation(nullptr) {
60 StaticMutexAutoLock lock(gBackgroundThreadMutex);
61 gBackgroundThread = NS_GetCurrentThread();
64 midirMIDIPlatformService::~midirMIDIPlatformService() {
65 LOG("midir_impl_shutdown");
66 if (mImplementation) {
67 midir_impl_shutdown(mImplementation);
69 StaticMutexAutoLock lock(gBackgroundThreadMutex);
70 gBackgroundThread = nullptr;
73 // static
74 void midirMIDIPlatformService::AddPort(const nsString* aId,
75 const nsString* aName, bool aInput) {
76 MIDIPortType type = aInput ? MIDIPortType::Input : MIDIPortType::Output;
77 MIDIPortInfo port(*aId, *aName, u""_ns, u""_ns, static_cast<uint32_t>(type));
78 MIDIPlatformService::Get()->AddPortInfo(port);
81 void midirMIDIPlatformService::Init() {
82 if (mImplementation) {
83 return;
86 mImplementation = midir_impl_init(AddPort);
88 if (mImplementation) {
89 MIDIPlatformService::Get()->SendPortList();
90 } else {
91 LOG("midir_impl_init failure");
95 // static
96 void midirMIDIPlatformService::CheckAndReceive(const nsString* aId,
97 const uint8_t* aData,
98 size_t aLength,
99 const GeckoTimeStamp* aTimeStamp,
100 uint64_t aMicros) {
101 nsTArray<uint8_t> data;
102 for (size_t i = 0; i < aLength; i++) {
103 data.AppendElement(aData[i]);
105 const TimeStamp* openTime = reinterpret_cast<const TimeStamp*>(aTimeStamp);
106 TimeStamp timestamp =
107 *openTime + TimeDuration::FromMicroseconds(static_cast<double>(aMicros));
108 MIDIMessage message(data, timestamp);
109 LogMIDIMessage(message, *aId, MIDIPortType::Input);
110 nsTArray<MIDIMessage> messages;
111 messages.AppendElement(message);
113 nsCOMPtr<nsIRunnable> r(new ReceiveRunnable(*aId, messages));
114 StaticMutexAutoLock lock(gBackgroundThreadMutex);
115 if (gBackgroundThread) {
116 gBackgroundThread->Dispatch(r, NS_DISPATCH_NORMAL);
120 void midirMIDIPlatformService::Open(MIDIPortParent* aPort) {
121 MOZ_ASSERT(aPort);
122 nsString id = aPort->MIDIPortInterface::Id();
123 TimeStamp openTimeStamp = TimeStamp::Now();
124 if (midir_impl_open_port(mImplementation, &id,
125 reinterpret_cast<GeckoTimeStamp*>(&openTimeStamp),
126 CheckAndReceive)) {
127 LOG("MIDI port open: %s at t=%lf", NS_ConvertUTF16toUTF8(id).get(),
128 (openTimeStamp - TimeStamp::ProcessCreation()).ToSeconds());
129 nsCOMPtr<nsIRunnable> r(new SetStatusRunnable(
130 aPort, aPort->DeviceState(), MIDIPortConnectionState::Open));
131 NS_DispatchToCurrentThread(r);
132 } else {
133 LOG("MIDI port open failed: %s", NS_ConvertUTF16toUTF8(id).get());
137 void midirMIDIPlatformService::Stop() {
138 // Nothing to do here AFAIK
141 void midirMIDIPlatformService::ScheduleSend(const nsAString& aPortId) {
142 LOG("MIDI port schedule send %s", NS_ConvertUTF16toUTF8(aPortId).get());
143 nsTArray<MIDIMessage> messages;
144 GetMessages(aPortId, messages);
145 TimeStamp now = TimeStamp::Now();
146 for (const auto& message : messages) {
147 if (message.timestamp().IsNull()) {
148 SendMessage(aPortId, message);
149 } else {
150 double delay = (message.timestamp() - now).ToMilliseconds();
151 if (delay < 1.0) {
152 SendMessage(aPortId, message);
153 } else {
154 nsCOMPtr<nsIRunnable> r(new SendRunnable(aPortId, message));
155 NS_DelayedDispatchToCurrentThread(r.forget(),
156 static_cast<uint32_t>(delay));
162 void midirMIDIPlatformService::ScheduleClose(MIDIPortParent* aPort) {
163 MOZ_ASSERT(aPort);
164 nsString id = aPort->MIDIPortInterface::Id();
165 LOG("MIDI port schedule close %s", NS_ConvertUTF16toUTF8(id).get());
166 if (aPort->ConnectionState() == MIDIPortConnectionState::Open) {
167 midir_impl_close_port(mImplementation, &id);
168 nsCOMPtr<nsIRunnable> r(new SetStatusRunnable(
169 aPort, aPort->DeviceState(), MIDIPortConnectionState::Closed));
170 NS_DispatchToCurrentThread(r);
174 void midirMIDIPlatformService::SendMessage(const nsAString& aPortId,
175 const MIDIMessage& aMessage) {
176 LOG("MIDI send message on %s", NS_ConvertUTF16toUTF8(aPortId).get());
177 LogMIDIMessage(aMessage, aPortId, MIDIPortType::Output);
178 midir_impl_send(mImplementation, &aPortId, &aMessage.data());