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 "MediaControlKeyManager.h"
7 #include "MediaControlUtils.h"
8 #include "MediaControlService.h"
9 #include "mozilla/AbstractThread.h"
10 #include "mozilla/Assertions.h"
11 #include "mozilla/Logging.h"
12 #include "mozilla/Preferences.h"
13 #include "mozilla/Services.h"
14 #include "mozilla/StaticPrefs_media.h"
15 #include "mozilla/widget/MediaKeysEventSourceFactory.h"
16 #include "nsContentUtils.h"
17 #include "nsIObserverService.h"
20 #define LOG(msg, ...) \
21 MOZ_LOG(gMediaControlLog, LogLevel::Debug, \
22 ("MediaControlKeyManager=%p, " msg, this, ##__VA_ARGS__))
25 #define LOG_INFO(msg, ...) \
26 MOZ_LOG(gMediaControlLog, LogLevel::Info, \
27 ("MediaControlKeyManager=%p, " msg, this, ##__VA_ARGS__))
29 #define MEDIA_CONTROL_PREF "media.hardwaremediakeys.enabled"
31 namespace mozilla::dom
{
33 bool MediaControlKeyManager::IsOpened() const {
34 return mEventSource
&& mEventSource
->IsOpened();
37 bool MediaControlKeyManager::Open() {
41 return StartMonitoringControlKeys();
44 void MediaControlKeyManager::Close() {
45 // We don't call parent's `Close()` because we want to keep the listener
46 // (MediaControlKeyHandler) all the time. It would be manually removed by
47 // `MediaControlService` when shutdown.
48 StopMonitoringControlKeys();
51 MediaControlKeyManager::MediaControlKeyManager()
52 : mObserver(new Observer(this)) {
53 nsContentUtils::RegisterShutdownObserver(mObserver
);
54 Preferences::AddStrongObserver(mObserver
, MEDIA_CONTROL_PREF
);
57 MediaControlKeyManager::~MediaControlKeyManager() { Shutdown(); }
59 void MediaControlKeyManager::Shutdown() {
60 StopMonitoringControlKeys();
61 mEventSource
= nullptr;
63 nsContentUtils::UnregisterShutdownObserver(mObserver
);
64 Preferences::RemoveObserver(mObserver
, MEDIA_CONTROL_PREF
);
69 bool MediaControlKeyManager::StartMonitoringControlKeys() {
70 if (!StaticPrefs::media_hardwaremediakeys_enabled()) {
75 mEventSource
= widget::CreateMediaControlKeySource();
77 if (mEventSource
&& mEventSource
->Open()) {
78 LOG_INFO("StartMonitoringControlKeys");
79 mEventSource
->SetPlaybackState(mPlaybackState
);
80 mEventSource
->SetMediaMetadata(mMetadata
);
81 mEventSource
->SetSupportedMediaKeys(mSupportedKeys
);
82 mEventSource
->AddListener(this);
85 // Fail to open or create event source (eg. when cross-compiling with MinGW,
86 // we cannot use the related WinAPI)
90 void MediaControlKeyManager::StopMonitoringControlKeys() {
91 if (!mEventSource
|| !mEventSource
->IsOpened()) {
95 LOG_INFO("StopMonitoringControlKeys");
96 mEventSource
->Close();
97 if (StaticPrefs::media_mediacontrol_testingevents_enabled()) {
98 // Close the source would reset the displayed playback state and metadata.
99 if (nsCOMPtr
<nsIObserverService
> obs
= services::GetObserverService()) {
100 obs
->NotifyObservers(nullptr, "media-displayed-playback-changed",
102 obs
->NotifyObservers(nullptr, "media-displayed-metadata-changed",
104 obs
->NotifyObservers(nullptr, "media-position-state-changed", nullptr);
109 void MediaControlKeyManager::OnActionPerformed(
110 const MediaControlAction
& aAction
) {
111 for (auto listener
: mListeners
) {
112 listener
->OnActionPerformed(aAction
);
116 void MediaControlKeyManager::SetPlaybackState(
117 MediaSessionPlaybackState aState
) {
118 if (mEventSource
&& mEventSource
->IsOpened()) {
119 mEventSource
->SetPlaybackState(aState
);
121 mPlaybackState
= aState
;
122 LOG_INFO("playbackState=%s", ToMediaSessionPlaybackStateStr(mPlaybackState
));
123 if (StaticPrefs::media_mediacontrol_testingevents_enabled()) {
124 if (nsCOMPtr
<nsIObserverService
> obs
= services::GetObserverService()) {
125 obs
->NotifyObservers(nullptr, "media-displayed-playback-changed",
131 MediaSessionPlaybackState
MediaControlKeyManager::GetPlaybackState() const {
132 return (mEventSource
&& mEventSource
->IsOpened())
133 ? mEventSource
->GetPlaybackState()
137 void MediaControlKeyManager::SetMediaMetadata(
138 const MediaMetadataBase
& aMetadata
) {
139 if (mEventSource
&& mEventSource
->IsOpened()) {
140 mEventSource
->SetMediaMetadata(aMetadata
);
142 mMetadata
= aMetadata
;
143 LOG_INFO("title=%s, artist=%s album=%s",
144 NS_ConvertUTF16toUTF8(mMetadata
.mTitle
).get(),
145 NS_ConvertUTF16toUTF8(mMetadata
.mArtist
).get(),
146 NS_ConvertUTF16toUTF8(mMetadata
.mAlbum
).get());
147 if (StaticPrefs::media_mediacontrol_testingevents_enabled()) {
148 if (nsCOMPtr
<nsIObserverService
> obs
= services::GetObserverService()) {
149 obs
->NotifyObservers(nullptr, "media-displayed-metadata-changed",
155 void MediaControlKeyManager::SetSupportedMediaKeys(
156 const MediaKeysArray
& aSupportedKeys
) {
157 mSupportedKeys
.Clear();
158 for (const auto& key
: aSupportedKeys
) {
159 LOG_INFO("Supported keys=%s", GetEnumString(key
).get());
160 mSupportedKeys
.AppendElement(key
);
162 if (mEventSource
&& mEventSource
->IsOpened()) {
163 mEventSource
->SetSupportedMediaKeys(mSupportedKeys
);
167 void MediaControlKeyManager::SetEnableFullScreen(bool aIsEnabled
) {
168 LOG_INFO("Set fullscreen %s", aIsEnabled
? "enabled" : "disabled");
169 if (mEventSource
&& mEventSource
->IsOpened()) {
170 mEventSource
->SetEnableFullScreen(aIsEnabled
);
174 void MediaControlKeyManager::SetEnablePictureInPictureMode(bool aIsEnabled
) {
175 LOG_INFO("Set Picture-In-Picture mode %s",
176 aIsEnabled
? "enabled" : "disabled");
177 if (mEventSource
&& mEventSource
->IsOpened()) {
178 mEventSource
->SetEnablePictureInPictureMode(aIsEnabled
);
182 void MediaControlKeyManager::SetPositionState(
183 const Maybe
<PositionState
>& aState
) {
185 LOG_INFO("Set PositionState, duration=%f, playbackRate=%f, position=%f",
186 aState
->mDuration
, aState
->mPlaybackRate
,
187 aState
->mLastReportedPlaybackPosition
);
189 LOG_INFO("Set PositionState, Nothing");
192 if (mEventSource
&& mEventSource
->IsOpened()) {
193 mEventSource
->SetPositionState(aState
);
196 if (StaticPrefs::media_mediacontrol_testingevents_enabled()) {
197 if (nsCOMPtr
<nsIObserverService
> obs
= services::GetObserverService()) {
198 obs
->NotifyObservers(nullptr, "media-position-state-changed", nullptr);
203 void MediaControlKeyManager::OnPreferenceChange() {
204 const bool isPrefEnabled
= StaticPrefs::media_hardwaremediakeys_enabled();
205 // Only start monitoring control keys when the pref is on and having a main
206 // controller that means already having media which need to be controlled.
207 const bool shouldMonitorKeys
=
208 isPrefEnabled
&& MediaControlService::GetService()->GetMainController();
209 LOG_INFO("Preference change : %s media control",
210 isPrefEnabled
? "enable" : "disable");
211 if (shouldMonitorKeys
) {
212 Unused
<< StartMonitoringControlKeys();
214 StopMonitoringControlKeys();
218 NS_IMPL_ISUPPORTS(MediaControlKeyManager::Observer
, nsIObserver
);
220 MediaControlKeyManager::Observer::Observer(MediaControlKeyManager
* aManager
)
221 : mManager(aManager
) {}
224 MediaControlKeyManager::Observer::Observe(nsISupports
* aSubject
,
226 const char16_t
* aData
) {
227 if (!strcmp(aTopic
, NS_XPCOM_SHUTDOWN_OBSERVER_ID
)) {
228 mManager
->Shutdown();
229 } else if (!strcmp(aTopic
, "nsPref:changed")) {
230 mManager
->OnPreferenceChange();
235 } // namespace mozilla::dom