widget/gtk/nsSound.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
     2 /* vim:expandtab:shiftwidth=4:tabstop=4:
     3  */
     4 /* This Source Code Form is subject to the terms of the Mozilla Public
     5  * License, v. 2.0. If a copy of the MPL was not distributed with this
     6  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     8 #include <string.h>
    10 #include "nscore.h"
    11 #include "plstr.h"
    12 #include "prlink.h"
    14 #include "nsSound.h"
    16 #include "nsIURL.h"
    17 #include "nsIFileURL.h"
    18 #include "nsNetUtil.h"
    19 #include "nsCOMPtr.h"
    20 #include "nsAutoPtr.h"
    21 #include "nsString.h"
    22 #include "nsDirectoryService.h"
    23 #include "nsDirectoryServiceDefs.h"
    24 #include "mozilla/FileUtils.h"
    25 #include "mozilla/Services.h"
    26 #include "nsIStringBundle.h"
    27 #include "nsIXULAppInfo.h"
    29 #include <stdio.h>
    30 #include <unistd.h>
    32 #include <gtk/gtk.h>
    33 static PRLibrary *libcanberra = nullptr;
    35 /* used to play sounds with libcanberra. */
    36 typedef struct _ca_context ca_context;
    37 typedef struct _ca_proplist ca_proplist;
    39 typedef void (*ca_finish_callback_t) (ca_context *c,
    40                                       uint32_t id,
    41                                       int error_code,
    42                                       void *userdata);
    44 typedef int (*ca_context_create_fn) (ca_context **);
    45 typedef int (*ca_context_destroy_fn) (ca_context *);
    46 typedef int (*ca_context_play_fn) (ca_context *c,
    47                                    uint32_t id,
    48                                    ...);
    49 typedef int (*ca_context_change_props_fn) (ca_context *c,
    50                                            ...);
    51 typedef int (*ca_proplist_create_fn) (ca_proplist **);
    52 typedef int (*ca_proplist_destroy_fn) (ca_proplist *);
    53 typedef int (*ca_proplist_sets_fn) (ca_proplist *c,
    54                                     const char *key,
    55                                     const char *value);
    56 typedef int (*ca_context_play_full_fn) (ca_context *c,
    57                                         uint32_t id,
    58                                         ca_proplist *p,
    59                                         ca_finish_callback_t cb,
    60                                         void *userdata);
    62 static ca_context_create_fn ca_context_create;
    63 static ca_context_destroy_fn ca_context_destroy;
    64 static ca_context_play_fn ca_context_play;
    65 static ca_context_change_props_fn ca_context_change_props;
    66 static ca_proplist_create_fn ca_proplist_create;
    67 static ca_proplist_destroy_fn ca_proplist_destroy;
    68 static ca_proplist_sets_fn ca_proplist_sets;
    69 static ca_context_play_full_fn ca_context_play_full;
    71 struct ScopedCanberraFile {
    72     ScopedCanberraFile(nsIFile *file): mFile(file) {};
    74     ~ScopedCanberraFile() {
    75         if (mFile) {
    76             mFile->Remove(false);
    77         }
    78     }
    80     void forget() {
    81         mFile.forget();
    82     }
    83     nsIFile* operator->() { return mFile; }
    84     operator nsIFile*() { return mFile; }
    86     nsCOMPtr<nsIFile> mFile;
    87 };
    89 static ca_context*
    90 ca_context_get_default()
    91 {
    92     // This allows us to avoid race conditions with freeing the context by handing that
    93     // responsibility to Glib, and still use one context at a time
    94     static GStaticPrivate ctx_static_private = G_STATIC_PRIVATE_INIT;
    96     ca_context* ctx = (ca_context*) g_static_private_get(&ctx_static_private);
    98     if (ctx) {
    99         return ctx;
   100     }
   102     ca_context_create(&ctx);
   103     if (!ctx) {
   104         return nullptr;
   105     }
   107     g_static_private_set(&ctx_static_private, ctx, (GDestroyNotify) ca_context_destroy);
   109     GtkSettings* settings = gtk_settings_get_default();
   110     if (g_object_class_find_property(G_OBJECT_GET_CLASS(settings),
   111                                      "gtk-sound-theme-name")) {
   112         gchar* sound_theme_name = nullptr;
   113         g_object_get(settings, "gtk-sound-theme-name", &sound_theme_name,
   114                      nullptr);
   116         if (sound_theme_name) {
   117             ca_context_change_props(ctx, "canberra.xdg-theme.name",
   118                                     sound_theme_name, nullptr);
   119             g_free(sound_theme_name);
   120         }
   121     }
   123     nsCOMPtr<nsIStringBundleService> bundleService =
   124         mozilla::services::GetStringBundleService();
   125     if (bundleService) {
   126         nsCOMPtr<nsIStringBundle> brandingBundle;
   127         bundleService->CreateBundle("chrome://branding/locale/brand.properties",
   128                                     getter_AddRefs(brandingBundle));
   129         if (brandingBundle) {
   130             nsAutoString wbrand;
   131             brandingBundle->GetStringFromName(MOZ_UTF16("brandShortName"),
   132                                               getter_Copies(wbrand));
   133             NS_ConvertUTF16toUTF8 brand(wbrand);
   135             ca_context_change_props(ctx, "application.name", brand.get(),
   136                                     nullptr);
   137         }
   138     }
   140     nsCOMPtr<nsIXULAppInfo> appInfo = do_GetService("@mozilla.org/xre/app-info;1");
   141     if (appInfo) {
   142         nsAutoCString version;
   143         appInfo->GetVersion(version);
   145         ca_context_change_props(ctx, "application.version", version.get(),
   146                                 nullptr);
   147     }
   149     ca_context_change_props(ctx, "application.icon_name", MOZ_APP_NAME,
   150                             nullptr);
   152     return ctx;
   153 }
   155 static void
   156 ca_finish_cb(ca_context *c,
   157              uint32_t id,
   158              int error_code,
   159              void *userdata)
   160 {
   161     nsIFile *file = reinterpret_cast<nsIFile *>(userdata);
   162     if (file) {
   163         file->Remove(false);
   164         NS_RELEASE(file);
   165     }
   166 }
   168 NS_IMPL_ISUPPORTS(nsSound, nsISound, nsIStreamLoaderObserver)
   170 ////////////////////////////////////////////////////////////////////////
   171 nsSound::nsSound()
   172 {
   173     mInited = false;
   174 }
   176 nsSound::~nsSound()
   177 {
   178 }
   180 NS_IMETHODIMP
   181 nsSound::Init()
   182 {
   183     // This function is designed so that no library is compulsory, and
   184     // one library missing doesn't cause the other(s) to not be used.
   185     if (mInited) 
   186         return NS_OK;
   188     mInited = true;
   190     if (!libcanberra) {
   191         libcanberra = PR_LoadLibrary("libcanberra.so.0");
   192         if (libcanberra) {
   193             ca_context_create = (ca_context_create_fn) PR_FindFunctionSymbol(libcanberra, "ca_context_create");
   194             if (!ca_context_create) {
   195                 PR_UnloadLibrary(libcanberra);
   196                 libcanberra = nullptr;
   197             } else {
   198                 // at this point we know we have a good libcanberra library
   199                 ca_context_destroy = (ca_context_destroy_fn) PR_FindFunctionSymbol(libcanberra, "ca_context_destroy");
   200                 ca_context_play = (ca_context_play_fn) PR_FindFunctionSymbol(libcanberra, "ca_context_play");
   201                 ca_context_change_props = (ca_context_change_props_fn) PR_FindFunctionSymbol(libcanberra, "ca_context_change_props");
   202                 ca_proplist_create = (ca_proplist_create_fn) PR_FindFunctionSymbol(libcanberra, "ca_proplist_create");
   203                 ca_proplist_destroy = (ca_proplist_destroy_fn) PR_FindFunctionSymbol(libcanberra, "ca_proplist_destroy");
   204                 ca_proplist_sets = (ca_proplist_sets_fn) PR_FindFunctionSymbol(libcanberra, "ca_proplist_sets");
   205                 ca_context_play_full = (ca_context_play_full_fn) PR_FindFunctionSymbol(libcanberra, "ca_context_play_full");
   206             }
   207         }
   208     }
   210     return NS_OK;
   211 }
   213 /* static */ void
   214 nsSound::Shutdown()
   215 {
   216     if (libcanberra) {
   217         PR_UnloadLibrary(libcanberra);
   218         libcanberra = nullptr;
   219     }
   220 }
   222 NS_IMETHODIMP nsSound::OnStreamComplete(nsIStreamLoader *aLoader,
   223                                         nsISupports *context,
   224                                         nsresult aStatus,
   225                                         uint32_t dataLen,
   226                                         const uint8_t *data)
   227 {
   228     // print a load error on bad status, and return
   229     if (NS_FAILED(aStatus)) {
   230 #ifdef DEBUG
   231         if (aLoader) {
   232             nsCOMPtr<nsIRequest> request;
   233             aLoader->GetRequest(getter_AddRefs(request));
   234             if (request) {
   235                 nsCOMPtr<nsIURI> uri;
   236                 nsCOMPtr<nsIChannel> channel = do_QueryInterface(request);
   237                 if (channel) {
   238                       channel->GetURI(getter_AddRefs(uri));
   239                       if (uri) {
   240                             nsAutoCString uriSpec;
   241                             uri->GetSpec(uriSpec);
   242                             printf("Failed to load %s\n", uriSpec.get());
   243                       }
   244                 }
   245             }
   246         }
   247 #endif
   248         return aStatus;
   249     }
   251     nsCOMPtr<nsIFile> tmpFile;
   252     nsDirectoryService::gService->Get(NS_OS_TEMP_DIR, NS_GET_IID(nsIFile),
   253                                       getter_AddRefs(tmpFile));
   255     nsresult rv = tmpFile->AppendNative(nsDependentCString("mozilla_audio_sample"));
   256     if (NS_FAILED(rv)) {
   257         return rv;
   258     }
   260     rv = tmpFile->CreateUnique(nsIFile::NORMAL_FILE_TYPE, PR_IRUSR | PR_IWUSR);
   261     if (NS_FAILED(rv)) {
   262         return rv;
   263     }
   265     ScopedCanberraFile canberraFile(tmpFile);
   267     mozilla::AutoFDClose fd;
   268     rv = canberraFile->OpenNSPRFileDesc(PR_WRONLY, PR_IRUSR | PR_IWUSR,
   269                                         &fd.rwget());
   270     if (NS_FAILED(rv)) {
   271         return rv;
   272     }
   274     // XXX: Should we do this on another thread?
   275     uint32_t length = dataLen;
   276     while (length > 0) {
   277         int32_t amount = PR_Write(fd, data, length);
   278         if (amount < 0) {
   279             return NS_ERROR_FAILURE;
   280         }
   281         length -= amount;
   282         data += amount;
   283     }
   285     ca_context* ctx = ca_context_get_default();
   286     if (!ctx) {
   287         return NS_ERROR_OUT_OF_MEMORY;
   288     }
   290     ca_proplist *p;
   291     ca_proplist_create(&p);
   292     if (!p) {
   293         return NS_ERROR_OUT_OF_MEMORY;
   294     }
   296     nsAutoCString path;
   297     rv = canberraFile->GetNativePath(path);
   298     if (NS_FAILED(rv)) {
   299         return rv;
   300     }
   302     ca_proplist_sets(p, "media.filename", path.get());
   303     if (ca_context_play_full(ctx, 0, p, ca_finish_cb, canberraFile) >= 0) {
   304         // Don't delete the temporary file here if ca_context_play_full succeeds
   305         canberraFile.forget();
   306     }
   307     ca_proplist_destroy(p);
   309     return NS_OK;
   310 }
   312 NS_METHOD nsSound::Beep()
   313 {
   314     ::gdk_beep();
   315     return NS_OK;
   316 }
   318 NS_METHOD nsSound::Play(nsIURL *aURL)
   319 {
   320     if (!mInited)
   321         Init();
   323     if (!libcanberra)
   324         return NS_ERROR_NOT_AVAILABLE;
   326     bool isFile;
   327     nsresult rv = aURL->SchemeIs("file", &isFile);
   328     if (NS_SUCCEEDED(rv) && isFile) {
   329         ca_context* ctx = ca_context_get_default();
   330         if (!ctx) {
   331             return NS_ERROR_OUT_OF_MEMORY;
   332         }
   334         nsAutoCString spec;
   335         rv = aURL->GetSpec(spec);
   336         if (NS_FAILED(rv)) {
   337             return rv;
   338         }
   339         gchar *path = g_filename_from_uri(spec.get(), nullptr, nullptr);
   340         if (!path) {
   341             return NS_ERROR_FILE_UNRECOGNIZED_PATH;
   342         }
   344         ca_context_play(ctx, 0, "media.filename", path, nullptr);
   345         g_free(path);
   346     } else {
   347         nsCOMPtr<nsIStreamLoader> loader;
   348         rv = NS_NewStreamLoader(getter_AddRefs(loader), aURL, this);
   349     }
   351     return rv;
   352 }
   354 NS_IMETHODIMP nsSound::PlayEventSound(uint32_t aEventId)
   355 {
   356     if (!mInited)
   357         Init();
   359     if (!libcanberra)
   360         return NS_OK;
   362     // Do we even want alert sounds?
   363     GtkSettings* settings = gtk_settings_get_default();
   365     if (g_object_class_find_property(G_OBJECT_GET_CLASS(settings),
   366                                      "gtk-enable-event-sounds")) {
   367         gboolean enable_sounds = TRUE;
   368         g_object_get(settings, "gtk-enable-event-sounds", &enable_sounds, nullptr);
   370         if (!enable_sounds) {
   371             return NS_OK;
   372         }
   373     }
   375     ca_context* ctx = ca_context_get_default();
   376     if (!ctx) {
   377         return NS_ERROR_OUT_OF_MEMORY;
   378     }
   380     switch (aEventId) {
   381         case EVENT_ALERT_DIALOG_OPEN:
   382             ca_context_play(ctx, 0, "event.id", "dialog-warning", nullptr);
   383             break;
   384         case EVENT_CONFIRM_DIALOG_OPEN:
   385             ca_context_play(ctx, 0, "event.id", "dialog-question", nullptr);
   386             break;
   387         case EVENT_NEW_MAIL_RECEIVED:
   388             ca_context_play(ctx, 0, "event.id", "message-new-email", nullptr);
   389             break;
   390         case EVENT_MENU_EXECUTE:
   391             ca_context_play(ctx, 0, "event.id", "menu-click", nullptr);
   392             break;
   393         case EVENT_MENU_POPUP:
   394             ca_context_play(ctx, 0, "event.id", "menu-popup", nullptr);
   395             break;
   396     }
   397     return NS_OK;
   398 }
   400 NS_IMETHODIMP nsSound::PlaySystemSound(const nsAString &aSoundAlias)
   401 {
   402     if (NS_IsMozAliasSound(aSoundAlias)) {
   403         NS_WARNING("nsISound::playSystemSound is called with \"_moz_\" events, they are obsolete, use nsISound::playEventSound instead");
   404         uint32_t eventId;
   405         if (aSoundAlias.Equals(NS_SYSSOUND_ALERT_DIALOG))
   406             eventId = EVENT_ALERT_DIALOG_OPEN;
   407         else if (aSoundAlias.Equals(NS_SYSSOUND_CONFIRM_DIALOG))
   408             eventId = EVENT_CONFIRM_DIALOG_OPEN;
   409         else if (aSoundAlias.Equals(NS_SYSSOUND_MAIL_BEEP))
   410             eventId = EVENT_NEW_MAIL_RECEIVED;
   411         else if (aSoundAlias.Equals(NS_SYSSOUND_MENU_EXECUTE))
   412             eventId = EVENT_MENU_EXECUTE;
   413         else if (aSoundAlias.Equals(NS_SYSSOUND_MENU_POPUP))
   414             eventId = EVENT_MENU_POPUP;
   415         else
   416             return NS_OK;
   417         return PlayEventSound(eventId);
   418     }
   420     nsresult rv;
   421     nsCOMPtr <nsIURI> fileURI;
   423     // create a nsIFile and then a nsIFileURL from that
   424     nsCOMPtr <nsIFile> soundFile;
   425     rv = NS_NewLocalFile(aSoundAlias, true, 
   426                          getter_AddRefs(soundFile));
   427     NS_ENSURE_SUCCESS(rv,rv);
   429     rv = NS_NewFileURI(getter_AddRefs(fileURI), soundFile);
   430     NS_ENSURE_SUCCESS(rv,rv);
   432     nsCOMPtr<nsIFileURL> fileURL = do_QueryInterface(fileURI,&rv);
   433     NS_ENSURE_SUCCESS(rv,rv);
   435     rv = Play(fileURL);
   437     return rv;
   438 }

mercurial