/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
// A global counter tracking the total images saved in the system and it will be // used to form a unique image file name. static uint32_t gImageNumber = 0;
Maybe<Property> property = GetProperty(aPropertyName); if (property.isNothing()) {
g_set_error(aError, G_DBUS_ERROR, G_DBUS_ERROR_NOT_SUPPORTED, "%s.%s %s is not supported", aObjectPath, aInterfaceName,
aPropertyName); return nullptr;
}
MPRISServiceHandler* handler = static_cast<MPRISServiceHandler*>(aUserData); switch (property.value()) { case Property::eSupportedUriSchemes: case Property::eSupportedMimeTypes: // No plan to implement OpenUri for now return g_variant_new_strv(nullptr, 0); case Property::eGetPlaybackStatus: return handler->GetPlaybackStatus(); case Property::eGetMetadata: return handler->GetMetadataAsGVariant(); case Property::eGetPosition: {
CheckedInt64 position =
CheckedInt64((int64_t)handler->GetPositionSeconds()) * 1000000; return g_variant_new_int64(position.isValid() ? position.value() : 0);
} case Property::eIdentity: return g_variant_new_string(handler->Identity()); case Property::eDesktopEntry: return g_variant_new_string(handler->DesktopEntry()); case Property::eHasTrackList: case Property::eCanQuit: return g_variant_new_boolean(false); // Play/Pause would be blocked if CanControl is false case Property::eCanControl: return g_variant_new_boolean(true); case Property::eCanRaise: case Property::eCanGoNext: case Property::eCanGoPrevious: case Property::eCanPlay: case Property::eCanPause: case Property::eCanSeek:
Maybe<dom::MediaControlKey> key = GetPairedKey(property.value());
MOZ_ASSERT(key.isSome()); return g_variant_new_boolean(handler->IsMediaKeySupported(key.value()));
}
MOZ_ASSERT_UNREACHABLE("Switch statement is incomplete"); return nullptr;
}
if (g_dbus_connection_unregister_object(aConnection, mRootRegistrationId)) {
mRootRegistrationId = 0;
} else { // Note: Most code examples in the internet probably dont't even check the // result here, but // according to the spec it _can_ return false.
LOGMPRIS("Unable to unregister root object from within onNameLost!");
}
if (!mPlayerRegistrationId) { return;
}
if (g_dbus_connection_unregister_object(aConnection, mPlayerRegistrationId)) {
mPlayerRegistrationId = 0;
} else { // Note: Most code examples in the internet probably dont't even check the // result here, but // according to the spec it _can_ return false.
LOGMPRIS("Unable to unregister object from within onNameLost!");
}
}
GDBusConnection* conn = g_bus_get_finish(aRes, getter_Transfers(error)); if (!conn) { if (!IsCancelledGError(error.get())) {
NS_WARNING(nsPrintfCString("Failure at g_bus_get_finish: %s",
error ? error->message : "Unknown Error")
.get());
} return;
}
MPRISServiceHandler* handler = static_cast<MPRISServiceHandler*>(aUserData); if (!handler) {
NS_WARNING(
nsPrintfCString("Failure to get a MPRISServiceHandler*: %p", handler)
.get()); return;
}
InitIdentity();
mOwnerId = g_bus_own_name_on_connection(
aConnection, GetServiceName(), // Enter a waiting queue until this service name is free // (likely another FF instance is running/has been crashed)
G_BUS_NAME_OWNER_FLAGS_NONE, OnNameAcquiredStatic, OnNameLostStatic, this,
nullptr);
/* parse introspection data */
mIntrospectionData = dont_AddRef(
g_dbus_node_info_new_for_xml(introspection_xml, getter_Transfers(error)));
if (!mIntrospectionData) {
LOGMPRIS("Failed at parsing XML Interface definition: %s",
error ? error->message : "Unknown Error"); return;
}
void MPRISServiceHandler::Close() { // Reset playback state and metadata before disconnect from dbus.
SetPlaybackState(dom::MediaSessionPlaybackState::None);
ClearMetadata();
OnNameLost(mConnection, GetServiceName());
if (mDBusGetCancellable) {
g_cancellable_cancel(mDBusGetCancellable);
mDBusGetCancellable = nullptr;
}
if (mOwnerId != 0) {
g_bus_unown_name(mOwnerId);
}
LOGMPRIS("InitIdentity() MPRIS desktop ID %s", mDesktopEntry.get());
}
constchar* MPRISServiceHandler::Identity() const {
NS_WARNING_ASSERTION(mInitialized, "MPRISServiceHandler should have been initialized."); return mIdentity.get();
}
constchar* MPRISServiceHandler::DesktopEntry() const {
NS_WARNING_ASSERTION(mInitialized, "MPRISServiceHandler should have been initialized."); return mDesktopEntry.get();
}
bool MPRISServiceHandler::PressKey( const dom::MediaControlAction& aAction) const {
MOZ_ASSERT(mInitialized); if (!IsMediaKeySupported(aAction.mKey.value())) {
LOGMPRIS("%s is not supported",
dom::GetEnumString(aAction.mKey.value()).get()); returnfalse;
}
LOGMPRIS("Press %s", dom::GetEnumString(aAction.mKey.value()).get());
EmitEvent(aAction); returntrue;
}
LOGMPRIS("Emitting MPRIS property changes for 'PlaybackStatus'");
Unused << EmitPropertiesChangedSignal(parameters);
}
GVariant* MPRISServiceHandler::GetPlaybackStatus() const { switch (GetPlaybackState()) { case dom::MediaSessionPlaybackState::Playing: return g_variant_new_string("Playing"); case dom::MediaSessionPlaybackState::Paused: return g_variant_new_string("Paused"); case dom::MediaSessionPlaybackState::None: return g_variant_new_string("Stopped"); default:
MOZ_ASSERT_UNREACHABLE("Invalid Playback State"); return nullptr;
}
}
void MPRISServiceHandler::SetMediaMetadata( const dom::MediaMetadataBase& aMetadata) { // Reset the index of the next available image to be fetched in the artwork, // before checking the fetching process should be started or not. The image // fetching process could be skipped if the image being fetching currently is // in the artwork. If the current image fetching fails, the next availabe // candidate should be the first image in the latest artwork
mNextImageIndex = 0;
// No need to fetch a MPRIS image if // 1) MPRIS image is being fetched, and the one in fetching is in the artwork // 2) MPRIS image is not being fetched, and the one in use is in the artwork if (!mFetchingUrl.IsEmpty()) { if (dom::IsImageIn(aMetadata.mArtwork, mFetchingUrl)) {
LOGMPRIS( "No need to load MPRIS image. The one being processed is in the " "artwork"); // Set MPRIS without the image first. The image will be loaded to MPRIS // asynchronously once it's fetched and saved into a local file
SetMediaMetadataInternal(aMetadata); return;
}
} elseif (!mCurrentImageUrl.IsEmpty()) { if (dom::IsImageIn(aMetadata.mArtwork, mCurrentImageUrl)) {
LOGMPRIS("No need to load MPRIS image. The one in use is in the artwork");
SetMediaMetadataInternal(aMetadata, false); return;
}
}
// Set MPRIS without the image first then load the image to MPRIS // asynchronously
SetMediaMetadataInternal(aMetadata);
LoadImageAtIndex(mNextImageIndex++);
}
if (aIndex >= mMPRISMetadata.mArtwork.Length()) {
LOGMPRIS("Stop loading image to MPRIS. No available image");
mImageFetchRequest.DisconnectIfExists(); return;
}
uint32_t size = 0; char* data = nullptr; // Only used to hold the image data
nsCOMPtr<nsIInputStream> inputStream;
nsresult rv = dom::GetEncodedImageBuffer(
aImage, mMimeType, getter_AddRefs(inputStream), &size, &data); if (NS_FAILED(rv) || !inputStream || size == 0 || !data) {
LOGMPRIS("Failed to get the image buffer info. Try next image");
LoadImageAtIndex(mNextImageIndex++); return;
}
if (SetImageToDisplay(data, size)) {
mCurrentImageUrl = mFetchingUrl;
LOGMPRIS("The MPRIS image is updated to the image from: %s",
NS_ConvertUTF16toUTF8(mCurrentImageUrl).get());
} else {
LOGMPRIS("Failed to set image to MPRIS");
mCurrentImageUrl.Truncate();
}
mFetchingUrl.Truncate();
},
[this, self](bool) {
LOGMPRIS("Failed to fetch image. Try next image");
mImageFetchRequest.Complete();
mFetchingUrl.Truncate();
LoadImageAtIndex(mNextImageIndex++);
})
->Track(mImageFetchRequest);
}
MOZ_ASSERT(mLocalImageFolder);
MOZ_ASSERT(!mLocalImageFile);
nsresult rv = mLocalImageFolder->Clone(getter_AddRefs(mLocalImageFile)); if (NS_FAILED(rv)) {
LOGMPRIS("Failed to get the image folder"); returnfalse;
}
// Create an unique file name to work around the file caching mechanism in the // Ubuntu. Once the image X specified by the filename Y is used in Ubuntu's // MPRIS, this pair will be cached. As long as the filename is same, even the // file content specified by Y is changed to Z, the image will stay unchanged. // The image shown in the Ubuntu's notification is still X instead of Z. // Changing the filename constantly works around this problem char filename[64];
SprintfLiteral(filename, "%d_%d.%s", getpid(), gImageNumber++,
GetImageFileExtension(mMimeType.get()));
rv = mLocalImageFile->Append(NS_ConvertUTF8toUTF16(filename)); if (NS_FAILED(rv)) {
LOGMPRIS("Failed to create an image filename"); returnfalse;
}
rv = mLocalImageFile->Create(nsIFile::NORMAL_FILE_TYPE, 0600); if (NS_FAILED(rv)) {
LOGMPRIS("Failed to create an image file"); returnfalse;
}
cleanup.release(); returntrue;
}
bool MPRISServiceHandler::InitLocalImageFolder() { if (mLocalImageFolder && LocalImageFolderExists()) { returntrue;
}
nsresult rv = NS_ERROR_FAILURE; if (IsRunningUnderFlatpak()) { // The XDG_DATA_HOME points to the same location in the host and guest // filesystem. if (constauto* xdgDataHome = g_getenv("XDG_DATA_HOME")) {
rv = NS_NewNativeLocalFile(nsDependentCString(xdgDataHome),
getter_AddRefs(mLocalImageFolder));
}
} else {
rv = NS_GetSpecialDirectory(XRE_USER_APP_DATA_DIR,
getter_AddRefs(mLocalImageFolder));
}
if (NS_FAILED(rv) || !mLocalImageFolder) {
LOGMPRIS("Failed to get the image folder"); returnfalse;
}
auto cleanup = MakeScopeExit([&] { mLocalImageFolder = nullptr; });
rv = mLocalImageFolder->Append(u"firefox-mpris"_ns); if (NS_FAILED(rv)) {
LOGMPRIS("Failed to name an image folder"); returnfalse;
}
if (!LocalImageFolderExists()) {
rv = mLocalImageFolder->Create(nsIFile::DIRECTORY_TYPE, 0700); if (NS_FAILED(rv)) {
LOGMPRIS("Failed to create an image folder"); returnfalse;
}
}
cleanup.release(); returntrue;
}
void MPRISServiceHandler::RemoveAllLocalImages() { if (!mLocalImageFolder || !LocalImageFolderExists()) { return;
}
nsresult rv = mLocalImageFolder->Remove(/* aRecursive */ true); if (NS_FAILED(rv)) { // It's ok to fail. The next removal is called when updating the // media-session image, or closing the MPRIS.
LOGMPRIS("Failed to remove images");
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.