diff options
Diffstat (limited to 'widget/windows/nsSound.cpp')
-rw-r--r-- | widget/windows/nsSound.cpp | 305 |
1 files changed, 305 insertions, 0 deletions
diff --git a/widget/windows/nsSound.cpp b/widget/windows/nsSound.cpp new file mode 100644 index 000000000..a7e3f8e7c --- /dev/null +++ b/widget/windows/nsSound.cpp @@ -0,0 +1,305 @@ +/* -*- Mode: C++; tab-width: 2; 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/. */ + +#include "nscore.h" +#include "plstr.h" +#include <stdio.h> +#include "nsString.h" +#include <windows.h> + +// mmsystem.h is needed to build with WIN32_LEAN_AND_MEAN +#include <mmsystem.h> + +#include "nsSound.h" +#include "nsIURL.h" +#include "nsNetUtil.h" +#include "nsIChannel.h" +#include "nsContentUtils.h" +#include "nsCRT.h" + +#include "mozilla/Logging.h" +#include "prtime.h" +#include "prprf.h" +#include "prmem.h" + +#include "nsNativeCharsetUtils.h" +#include "nsThreadUtils.h" + +using mozilla::LogLevel; + +PRLogModuleInfo* gWin32SoundLog = nullptr; + +class nsSoundPlayer: public mozilla::Runnable { +public: + nsSoundPlayer(nsSound *aSound, const wchar_t* aSoundName) : + mSoundName(aSoundName), mSound(aSound) + { + Init(); + } + + nsSoundPlayer(nsSound *aSound, const nsAString& aSoundName) : + mSoundName(aSoundName), mSound(aSound) + { + Init(); + } + + NS_DECL_NSIRUNNABLE + +protected: + nsString mSoundName; + nsSound *mSound; // Strong, but this will be released from SoundReleaser. + nsCOMPtr<nsIThread> mThread; + + void Init() + { + NS_GetCurrentThread(getter_AddRefs(mThread)); + NS_ASSERTION(mThread, "failed to get current thread"); + NS_IF_ADDREF(mSound); + } + + class SoundReleaser: public mozilla::Runnable { + public: + SoundReleaser(nsSound* aSound) : + mSound(aSound) + { + } + + NS_DECL_NSIRUNNABLE + + protected: + nsSound *mSound; + }; +}; + +NS_IMETHODIMP +nsSoundPlayer::Run() +{ + PR_SetCurrentThreadName("Play Sound"); + + NS_PRECONDITION(!mSoundName.IsEmpty(), "Sound name should not be empty"); + ::PlaySoundW(mSoundName.get(), nullptr, + SND_NODEFAULT | SND_ALIAS | SND_ASYNC); + nsCOMPtr<nsIRunnable> releaser = new SoundReleaser(mSound); + // Don't release nsSound from here, because here is not an owning thread of + // the nsSound. nsSound must be released in its owning thread. + mThread->Dispatch(releaser, NS_DISPATCH_NORMAL); + return NS_OK; +} + +NS_IMETHODIMP +nsSoundPlayer::SoundReleaser::Run() +{ + mSound->ShutdownOldPlayerThread(); + NS_IF_RELEASE(mSound); + return NS_OK; +} + + +#ifndef SND_PURGE +// Not available on Windows CE, and according to MSDN +// doesn't do anything on recent windows either. +#define SND_PURGE 0 +#endif + +NS_IMPL_ISUPPORTS(nsSound, nsISound, nsIStreamLoaderObserver) + + +nsSound::nsSound() +{ + if (!gWin32SoundLog) { + gWin32SoundLog = PR_NewLogModule("nsSound"); + } + + mLastSound = nullptr; +} + +nsSound::~nsSound() +{ + NS_ASSERTION(!mPlayerThread, "player thread is not null but should be"); + PurgeLastSound(); +} + +void nsSound::ShutdownOldPlayerThread() +{ + nsCOMPtr<nsIThread> playerThread(mPlayerThread.forget()); + if (playerThread) + playerThread->Shutdown(); +} + +void nsSound::PurgeLastSound() +{ + if (mLastSound) { + // Halt any currently playing sound. + ::PlaySound(nullptr, nullptr, SND_PURGE); + + // Now delete the buffer. + free(mLastSound); + mLastSound = nullptr; + } +} + +NS_IMETHODIMP nsSound::Beep() +{ + ::MessageBeep(0); + + return NS_OK; +} + +NS_IMETHODIMP nsSound::OnStreamComplete(nsIStreamLoader *aLoader, + nsISupports *context, + nsresult aStatus, + uint32_t dataLen, + const uint8_t *data) +{ + // print a load error on bad status + if (NS_FAILED(aStatus)) { +#ifdef DEBUG + if (aLoader) { + nsCOMPtr<nsIRequest> request; + nsCOMPtr<nsIChannel> channel; + aLoader->GetRequest(getter_AddRefs(request)); + if (request) + channel = do_QueryInterface(request); + if (channel) { + nsCOMPtr<nsIURI> uri; + channel->GetURI(getter_AddRefs(uri)); + if (uri) { + nsAutoCString uriSpec; + uri->GetSpec(uriSpec); + MOZ_LOG(gWin32SoundLog, LogLevel::Info, + ("Failed to load %s\n", uriSpec.get())); + } + } + } +#endif + return aStatus; + } + + ShutdownOldPlayerThread(); + PurgeLastSound(); + + if (data && dataLen > 0) { + DWORD flags = SND_MEMORY | SND_NODEFAULT; + // We try to make a copy so we can play it async. + mLastSound = (uint8_t *) malloc(dataLen); + if (mLastSound) { + memcpy(mLastSound, data, dataLen); + data = mLastSound; + flags |= SND_ASYNC; + } + ::PlaySoundW(reinterpret_cast<LPCWSTR>(data), 0, flags); + } + + return NS_OK; +} + +NS_IMETHODIMP nsSound::Play(nsIURL *aURL) +{ + nsresult rv; + +#ifdef DEBUG_SOUND + char *url; + aURL->GetSpec(&url); + MOZ_LOG(gWin32SoundLog, LogLevel::Info, + ("%s\n", url)); +#endif + + nsCOMPtr<nsIStreamLoader> loader; + rv = NS_NewStreamLoader(getter_AddRefs(loader), + aURL, + this, // aObserver + nsContentUtils::GetSystemPrincipal(), + nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL, + nsIContentPolicy::TYPE_OTHER); + return rv; +} + + +NS_IMETHODIMP nsSound::Init() +{ + // This call halts a sound if it was still playing. + // We have to use the sound library for something to make sure + // it is initialized. + // If we wait until the first sound is played, there will + // be a time lag as the library gets loaded. + ::PlaySound(nullptr, nullptr, SND_PURGE); + + return NS_OK; +} + + +NS_IMETHODIMP nsSound::PlaySystemSound(const nsAString &aSoundAlias) +{ + ShutdownOldPlayerThread(); + PurgeLastSound(); + + if (!NS_IsMozAliasSound(aSoundAlias)) { + if (aSoundAlias.IsEmpty()) + return NS_OK; + nsCOMPtr<nsIRunnable> player = new nsSoundPlayer(this, aSoundAlias); + NS_ENSURE_TRUE(player, NS_ERROR_OUT_OF_MEMORY); + nsresult rv = NS_NewThread(getter_AddRefs(mPlayerThread), player); + NS_ENSURE_SUCCESS(rv, rv); + return NS_OK; + } + + NS_WARNING("nsISound::playSystemSound is called with \"_moz_\" events, they are obsolete, use nsISound::playEventSound instead"); + + uint32_t eventId; + if (aSoundAlias.Equals(NS_SYSSOUND_MAIL_BEEP)) + eventId = EVENT_NEW_MAIL_RECEIVED; + else if (aSoundAlias.Equals(NS_SYSSOUND_CONFIRM_DIALOG)) + eventId = EVENT_CONFIRM_DIALOG_OPEN; + else if (aSoundAlias.Equals(NS_SYSSOUND_ALERT_DIALOG)) + eventId = EVENT_ALERT_DIALOG_OPEN; + else if (aSoundAlias.Equals(NS_SYSSOUND_MENU_EXECUTE)) + eventId = EVENT_MENU_EXECUTE; + else if (aSoundAlias.Equals(NS_SYSSOUND_MENU_POPUP)) + eventId = EVENT_MENU_POPUP; + else + return NS_OK; + + return PlayEventSound(eventId); +} + +NS_IMETHODIMP nsSound::PlayEventSound(uint32_t aEventId) +{ + ShutdownOldPlayerThread(); + PurgeLastSound(); + + const wchar_t *sound = nullptr; + switch (aEventId) { + case EVENT_NEW_MAIL_RECEIVED: + sound = L"MailBeep"; + break; + case EVENT_ALERT_DIALOG_OPEN: + sound = L"SystemExclamation"; + break; + case EVENT_CONFIRM_DIALOG_OPEN: + sound = L"SystemQuestion"; + break; + case EVENT_MENU_EXECUTE: + sound = L"MenuCommand"; + break; + case EVENT_MENU_POPUP: + sound = L"MenuPopup"; + break; + case EVENT_EDITOR_MAX_LEN: + sound = L".Default"; + break; + default: + // Win32 plays no sounds at NS_SYSSOUND_PROMPT_DIALOG and + // NS_SYSSOUND_SELECT_DIALOG. + return NS_OK; + } + NS_ASSERTION(sound, "sound is null"); + + nsCOMPtr<nsIRunnable> player = new nsSoundPlayer(this, sound); + NS_ENSURE_TRUE(player, NS_ERROR_OUT_OF_MEMORY); + nsresult rv = NS_NewThread(getter_AddRefs(mPlayerThread), player); + NS_ENSURE_SUCCESS(rv, rv); + return NS_OK; +} |