LCOV - code coverage report
Current view: directory - widget/gtk2 - nsSound.cpp (source / functions) Found Hit Coverage
Test: app.info Lines: 203 3 1.5 %
Date: 2012-06-02 Functions: 20 1 5.0 %

       1                 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
       2                 : /* vim:expandtab:shiftwidth=4:tabstop=4:
       3                 :  */
       4                 : /* ***** BEGIN LICENSE BLOCK *****
       5                 :  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
       6                 :  *
       7                 :  * The contents of this file are subject to the Mozilla Public License Version
       8                 :  * 1.1 (the "License"); you may not use this file except in compliance with
       9                 :  * the License. You may obtain a copy of the License at
      10                 :  * http://www.mozilla.org/MPL/
      11                 :  *
      12                 :  * Software distributed under the License is distributed on an "AS IS" basis,
      13                 :  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
      14                 :  * for the specific language governing rights and limitations under the
      15                 :  * License.
      16                 :  *
      17                 :  * The Original Code is mozilla.org code.
      18                 :  *
      19                 :  * The Initial Developer of the Original Code is
      20                 :  * Netscape Communications Corporation.
      21                 :  * Portions created by the Initial Developer are Copyright (C) 2000
      22                 :  * the Initial Developer. All Rights Reserved.
      23                 :  *
      24                 :  * Contributor(s):
      25                 :  *   Stuart Parmenter <pavlov@netscape.com>
      26                 :  *
      27                 :  * Alternatively, the contents of this file may be used under the terms of
      28                 :  * either the GNU General Public License Version 2 or later (the "GPL"), or
      29                 :  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
      30                 :  * in which case the provisions of the GPL or the LGPL are applicable instead
      31                 :  * of those above. If you wish to allow use of your version of this file only
      32                 :  * under the terms of either the GPL or the LGPL, and not to allow others to
      33                 :  * use your version of this file under the terms of the MPL, indicate your
      34                 :  * decision by deleting the provisions above and replace them with the notice
      35                 :  * and other provisions required by the GPL or the LGPL. If you do not delete
      36                 :  * the provisions above, a recipient may use your version of this file under
      37                 :  * the terms of any one of the MPL, the GPL or the LGPL.
      38                 :  *
      39                 :  * ***** END LICENSE BLOCK ***** */
      40                 : 
      41                 : #include <string.h>
      42                 : 
      43                 : #include "nscore.h"
      44                 : #include "plstr.h"
      45                 : #include "prlink.h"
      46                 : 
      47                 : #include "nsSound.h"
      48                 : 
      49                 : #include "nsIURL.h"
      50                 : #include "nsIFileURL.h"
      51                 : #include "nsNetUtil.h"
      52                 : #include "nsCOMPtr.h"
      53                 : #include "nsAutoPtr.h"
      54                 : #include "nsString.h"
      55                 : #include "nsDirectoryService.h"
      56                 : #include "nsDirectoryServiceDefs.h"
      57                 : #include "mozilla/FileUtils.h"
      58                 : #include "mozilla/Services.h"
      59                 : #include "nsIStringBundle.h"
      60                 : #include "nsIXULAppInfo.h"
      61                 : 
      62                 : #include <stdio.h>
      63                 : #include <unistd.h>
      64                 : 
      65                 : #include <gtk/gtk.h>
      66                 : static PRLibrary *libcanberra = nsnull;
      67                 : 
      68                 : /* used to play sounds with libcanberra. */
      69                 : typedef struct _ca_context ca_context;
      70                 : typedef struct _ca_proplist ca_proplist;
      71                 : 
      72                 : typedef void (*ca_finish_callback_t) (ca_context *c,
      73                 :                                       uint32_t id,
      74                 :                                       int error_code,
      75                 :                                       void *userdata);
      76                 : 
      77                 : typedef int (*ca_context_create_fn) (ca_context **);
      78                 : typedef int (*ca_context_destroy_fn) (ca_context *);
      79                 : typedef int (*ca_context_play_fn) (ca_context *c,
      80                 :                                    uint32_t id,
      81                 :                                    ...);
      82                 : typedef int (*ca_context_change_props_fn) (ca_context *c,
      83                 :                                            ...);
      84                 : typedef int (*ca_proplist_create_fn) (ca_proplist **);
      85                 : typedef int (*ca_proplist_destroy_fn) (ca_proplist *);
      86                 : typedef int (*ca_proplist_sets_fn) (ca_proplist *c,
      87                 :                                     const char *key,
      88                 :                                     const char *value);
      89                 : typedef int (*ca_context_play_full_fn) (ca_context *c,
      90                 :                                         uint32_t id,
      91                 :                                         ca_proplist *p,
      92                 :                                         ca_finish_callback_t cb,
      93                 :                                         void *userdata);
      94                 : 
      95                 : static ca_context_create_fn ca_context_create;
      96                 : static ca_context_destroy_fn ca_context_destroy;
      97                 : static ca_context_play_fn ca_context_play;
      98                 : static ca_context_change_props_fn ca_context_change_props;
      99                 : static ca_proplist_create_fn ca_proplist_create;
     100                 : static ca_proplist_destroy_fn ca_proplist_destroy;
     101                 : static ca_proplist_sets_fn ca_proplist_sets;
     102                 : static ca_context_play_full_fn ca_context_play_full;
     103                 : 
     104                 : struct ScopedCanberraFile {
     105               0 :     ScopedCanberraFile(nsILocalFile *file): mFile(file) {};
     106                 : 
     107               0 :     ~ScopedCanberraFile() {
     108               0 :         if (mFile) {
     109               0 :             mFile->Remove(PR_FALSE);
     110                 :         }
     111               0 :     }
     112                 : 
     113               0 :     void forget() {
     114               0 :         mFile.forget();
     115               0 :     }
     116               0 :     nsILocalFile* operator->() { return mFile; }
     117               0 :     operator nsILocalFile*() { return mFile; }
     118                 : 
     119                 :     nsCOMPtr<nsILocalFile> mFile;
     120                 : };
     121                 : 
     122                 : static ca_context*
     123               0 : ca_context_get_default()
     124                 : {
     125                 :     // This allows us to avoid race conditions with freeing the context by handing that
     126                 :     // responsibility to Glib, and still use one context at a time
     127                 :     static GStaticPrivate ctx_static_private = G_STATIC_PRIVATE_INIT;
     128                 : 
     129               0 :     ca_context* ctx = (ca_context*) g_static_private_get(&ctx_static_private);
     130                 : 
     131               0 :     if (ctx) {
     132               0 :         return ctx;
     133                 :     }
     134                 : 
     135               0 :     ca_context_create(&ctx);
     136               0 :     if (!ctx) {
     137               0 :         return nsnull;
     138                 :     }
     139                 : 
     140               0 :     g_static_private_set(&ctx_static_private, ctx, (GDestroyNotify) ca_context_destroy);
     141                 : 
     142               0 :     GtkSettings* settings = gtk_settings_get_default();
     143               0 :     if (g_object_class_find_property(G_OBJECT_GET_CLASS(settings),
     144               0 :                                      "gtk-sound-theme-name")) {
     145               0 :         gchar* sound_theme_name = nsnull;
     146               0 :         g_object_get(settings, "gtk-sound-theme-name", &sound_theme_name, NULL);
     147                 : 
     148               0 :         if (sound_theme_name) {
     149               0 :             ca_context_change_props(ctx, "canberra.xdg-theme.name", sound_theme_name, NULL);
     150               0 :             g_free(sound_theme_name);
     151                 :         }
     152                 :     }
     153                 : 
     154                 :     nsCOMPtr<nsIStringBundleService> bundleService =
     155               0 :         mozilla::services::GetStringBundleService();
     156               0 :     if (bundleService) {
     157               0 :         nsCOMPtr<nsIStringBundle> brandingBundle;
     158               0 :         bundleService->CreateBundle("chrome://branding/locale/brand.properties",
     159               0 :                                     getter_AddRefs(brandingBundle));
     160               0 :         if (brandingBundle) {
     161               0 :             nsAutoString wbrand;
     162               0 :             brandingBundle->GetStringFromName(NS_LITERAL_STRING("brandShortName").get(),
     163               0 :                                               getter_Copies(wbrand));
     164               0 :             NS_ConvertUTF16toUTF8 brand(wbrand);
     165                 : 
     166               0 :             ca_context_change_props(ctx, "application.name", brand.get(), NULL);
     167                 :         }
     168                 :     }
     169                 : 
     170               0 :     nsCOMPtr<nsIXULAppInfo> appInfo = do_GetService("@mozilla.org/xre/app-info;1");
     171               0 :     if (appInfo) {
     172               0 :         nsCAutoString version;
     173               0 :         appInfo->GetVersion(version);
     174                 : 
     175               0 :         ca_context_change_props(ctx, "application.version", version.get(), NULL);
     176                 :     }
     177                 : 
     178               0 :     ca_context_change_props(ctx, "application.icon_name", MOZ_APP_NAME, NULL);
     179                 : 
     180               0 :     return ctx;
     181                 : }
     182                 : 
     183                 : static void
     184               0 : ca_finish_cb(ca_context *c,
     185                 :              uint32_t id,
     186                 :              int error_code,
     187                 :              void *userdata)
     188                 : {
     189               0 :     nsILocalFile *file = reinterpret_cast<nsILocalFile *>(userdata);
     190               0 :     if (file) {
     191               0 :         file->Remove(PR_FALSE);
     192               0 :         NS_RELEASE(file);
     193                 :     }
     194               0 : }
     195                 : 
     196               0 : NS_IMPL_ISUPPORTS2(nsSound, nsISound, nsIStreamLoaderObserver)
     197                 : 
     198                 : ////////////////////////////////////////////////////////////////////////
     199               0 : nsSound::nsSound()
     200                 : {
     201               0 :     mInited = false;
     202               0 : }
     203                 : 
     204               0 : nsSound::~nsSound()
     205                 : {
     206               0 : }
     207                 : 
     208                 : NS_IMETHODIMP
     209               0 : nsSound::Init()
     210                 : {
     211                 :     // This function is designed so that no library is compulsory, and
     212                 :     // one library missing doesn't cause the other(s) to not be used.
     213               0 :     if (mInited) 
     214               0 :         return NS_OK;
     215                 : 
     216               0 :     mInited = true;
     217                 : 
     218               0 :     if (!libcanberra) {
     219               0 :         libcanberra = PR_LoadLibrary("libcanberra.so.0");
     220               0 :         if (libcanberra) {
     221               0 :             ca_context_create = (ca_context_create_fn) PR_FindFunctionSymbol(libcanberra, "ca_context_create");
     222               0 :             if (!ca_context_create) {
     223               0 :                 PR_UnloadLibrary(libcanberra);
     224               0 :                 libcanberra = nsnull;
     225                 :             } else {
     226                 :                 // at this point we know we have a good libcanberra library
     227               0 :                 ca_context_destroy = (ca_context_destroy_fn) PR_FindFunctionSymbol(libcanberra, "ca_context_destroy");
     228               0 :                 ca_context_play = (ca_context_play_fn) PR_FindFunctionSymbol(libcanberra, "ca_context_play");
     229               0 :                 ca_context_change_props = (ca_context_change_props_fn) PR_FindFunctionSymbol(libcanberra, "ca_context_change_props");
     230               0 :                 ca_proplist_create = (ca_proplist_create_fn) PR_FindFunctionSymbol(libcanberra, "ca_proplist_create");
     231               0 :                 ca_proplist_destroy = (ca_proplist_destroy_fn) PR_FindFunctionSymbol(libcanberra, "ca_proplist_destroy");
     232               0 :                 ca_proplist_sets = (ca_proplist_sets_fn) PR_FindFunctionSymbol(libcanberra, "ca_proplist_sets");
     233               0 :                 ca_context_play_full = (ca_context_play_full_fn) PR_FindFunctionSymbol(libcanberra, "ca_context_play_full");
     234                 :             }
     235                 :         }
     236                 :     }
     237                 : 
     238               0 :     return NS_OK;
     239                 : }
     240                 : 
     241                 : /* static */ void
     242            1387 : nsSound::Shutdown()
     243                 : {
     244            1387 :     if (libcanberra) {
     245               0 :         PR_UnloadLibrary(libcanberra);
     246               0 :         libcanberra = nsnull;
     247                 :     }
     248            1387 : }
     249                 : 
     250               0 : NS_IMETHODIMP nsSound::OnStreamComplete(nsIStreamLoader *aLoader,
     251                 :                                         nsISupports *context,
     252                 :                                         nsresult aStatus,
     253                 :                                         PRUint32 dataLen,
     254                 :                                         const PRUint8 *data)
     255                 : {
     256                 :     // print a load error on bad status, and return
     257               0 :     if (NS_FAILED(aStatus)) {
     258                 : #ifdef DEBUG
     259               0 :         if (aLoader) {
     260               0 :             nsCOMPtr<nsIRequest> request;
     261               0 :             aLoader->GetRequest(getter_AddRefs(request));
     262               0 :             if (request) {
     263               0 :                 nsCOMPtr<nsIURI> uri;
     264               0 :                 nsCOMPtr<nsIChannel> channel = do_QueryInterface(request);
     265               0 :                 if (channel) {
     266               0 :                       channel->GetURI(getter_AddRefs(uri));
     267               0 :                       if (uri) {
     268               0 :                             nsCAutoString uriSpec;
     269               0 :                             uri->GetSpec(uriSpec);
     270               0 :                             printf("Failed to load %s\n", uriSpec.get());
     271                 :                       }
     272                 :                 }
     273                 :             }
     274                 :         }
     275                 : #endif
     276               0 :         return aStatus;
     277                 :     }
     278                 : 
     279               0 :     nsCOMPtr<nsILocalFile> tmpFile;
     280                 :     nsDirectoryService::gService->Get(NS_OS_TEMP_DIR, NS_GET_IID(nsILocalFile),
     281               0 :                                       getter_AddRefs(tmpFile));
     282                 : 
     283               0 :     nsresult rv = tmpFile->AppendNative(nsDependentCString("mozilla_audio_sample"));
     284               0 :     if (NS_FAILED(rv)) {
     285               0 :         return rv;
     286                 :     }
     287                 : 
     288               0 :     rv = tmpFile->CreateUnique(nsIFile::NORMAL_FILE_TYPE, PR_IRUSR | PR_IWUSR);
     289               0 :     if (NS_FAILED(rv)) {
     290               0 :         return rv;
     291                 :     }
     292                 : 
     293               0 :     ScopedCanberraFile canberraFile(tmpFile);
     294                 : 
     295               0 :     mozilla::AutoFDClose fd;
     296               0 :     rv = canberraFile->OpenNSPRFileDesc(PR_WRONLY, PR_IRUSR | PR_IWUSR, &fd);
     297               0 :     if (NS_FAILED(rv)) {
     298               0 :         return rv;
     299                 :     }
     300                 : 
     301                 :     // XXX: Should we do this on another thread?
     302               0 :     PRUint32 length = dataLen;
     303               0 :     while (length > 0) {
     304               0 :         PRInt32 amount = PR_Write(fd, data, length);
     305               0 :         if (amount < 0) {
     306               0 :             return NS_ERROR_FAILURE;
     307                 :         }
     308               0 :         length -= amount;
     309               0 :         data += amount;
     310                 :     }
     311                 : 
     312               0 :     ca_context* ctx = ca_context_get_default();
     313               0 :     if (!ctx) {
     314               0 :         return NS_ERROR_OUT_OF_MEMORY;
     315                 :     }
     316                 : 
     317                 :     ca_proplist *p;
     318               0 :     ca_proplist_create(&p);
     319               0 :     if (!p) {
     320               0 :         return NS_ERROR_OUT_OF_MEMORY;
     321                 :     }
     322                 : 
     323               0 :     nsCAutoString path;
     324               0 :     rv = canberraFile->GetNativePath(path);
     325               0 :     if (NS_FAILED(rv)) {
     326               0 :         return rv;
     327                 :     }
     328                 : 
     329               0 :     ca_proplist_sets(p, "media.filename", path.get());
     330               0 :     if (ca_context_play_full(ctx, 0, p, ca_finish_cb, canberraFile) >= 0) {
     331                 :         // Don't delete the temporary file here if ca_context_play_full succeeds
     332               0 :         canberraFile.forget();
     333                 :     }
     334               0 :     ca_proplist_destroy(p);
     335                 : 
     336               0 :     return NS_OK;
     337                 : }
     338                 : 
     339               0 : NS_METHOD nsSound::Beep()
     340                 : {
     341               0 :     ::gdk_beep();
     342               0 :     return NS_OK;
     343                 : }
     344                 : 
     345               0 : NS_METHOD nsSound::Play(nsIURL *aURL)
     346                 : {
     347               0 :     if (!mInited)
     348               0 :         Init();
     349                 : 
     350               0 :     if (!libcanberra)
     351               0 :         return NS_ERROR_NOT_AVAILABLE;
     352                 : 
     353                 :     bool isFile;
     354               0 :     nsresult rv = aURL->SchemeIs("file", &isFile);
     355               0 :     if (NS_SUCCEEDED(rv) && isFile) {
     356               0 :         ca_context* ctx = ca_context_get_default();
     357               0 :         if (!ctx) {
     358               0 :             return NS_ERROR_OUT_OF_MEMORY;
     359                 :         }
     360                 : 
     361               0 :         nsCAutoString path;
     362               0 :         rv = aURL->GetPath(path);
     363               0 :         if (NS_FAILED(rv)) {
     364               0 :             return rv;
     365                 :         }
     366                 : 
     367               0 :         ca_context_play(ctx, 0, "media.filename", path.get(), NULL);
     368                 :     } else {
     369               0 :         nsCOMPtr<nsIStreamLoader> loader;
     370               0 :         rv = NS_NewStreamLoader(getter_AddRefs(loader), aURL, this);
     371                 :     }
     372                 : 
     373               0 :     return rv;
     374                 : }
     375                 : 
     376               0 : NS_IMETHODIMP nsSound::PlayEventSound(PRUint32 aEventId)
     377                 : {
     378               0 :     if (!mInited)
     379               0 :         Init();
     380                 : 
     381               0 :     if (!libcanberra)
     382               0 :         return NS_OK;
     383                 : 
     384                 :     // Do we even want alert sounds?
     385               0 :     GtkSettings* settings = gtk_settings_get_default();
     386                 : 
     387               0 :     if (g_object_class_find_property(G_OBJECT_GET_CLASS(settings),
     388               0 :                                      "gtk-enable-event-sounds")) {
     389               0 :         gboolean enable_sounds = TRUE;
     390               0 :         g_object_get(settings, "gtk-enable-event-sounds", &enable_sounds, NULL);
     391                 : 
     392               0 :         if (!enable_sounds) {
     393               0 :             return NS_OK;
     394                 :         }
     395                 :     }
     396                 : 
     397               0 :     ca_context* ctx = ca_context_get_default();
     398               0 :     if (!ctx) {
     399               0 :         return NS_ERROR_OUT_OF_MEMORY;
     400                 :     }
     401                 : 
     402               0 :     switch (aEventId) {
     403                 :         case EVENT_ALERT_DIALOG_OPEN:
     404               0 :             ca_context_play(ctx, 0, "event.id", "dialog-warning", NULL);
     405               0 :             break;
     406                 :         case EVENT_CONFIRM_DIALOG_OPEN:
     407               0 :             ca_context_play(ctx, 0, "event.id", "dialog-question", NULL);
     408               0 :             break;
     409                 :         case EVENT_NEW_MAIL_RECEIVED:
     410               0 :             ca_context_play(ctx, 0, "event.id", "message-new-email", NULL);
     411               0 :             break;
     412                 :         case EVENT_MENU_EXECUTE:
     413               0 :             ca_context_play(ctx, 0, "event.id", "menu-click", NULL);
     414               0 :             break;
     415                 :         case EVENT_MENU_POPUP:
     416               0 :             ca_context_play(ctx, 0, "event.id", "menu-popup", NULL);
     417               0 :             break;
     418                 :     }
     419               0 :     return NS_OK;
     420                 : }
     421                 : 
     422               0 : NS_IMETHODIMP nsSound::PlaySystemSound(const nsAString &aSoundAlias)
     423                 : {
     424               0 :     if (NS_IsMozAliasSound(aSoundAlias)) {
     425               0 :         NS_WARNING("nsISound::playSystemSound is called with \"_moz_\" events, they are obsolete, use nsISound::playEventSound instead");
     426                 :         PRUint32 eventId;
     427               0 :         if (aSoundAlias.Equals(NS_SYSSOUND_ALERT_DIALOG))
     428               0 :             eventId = EVENT_ALERT_DIALOG_OPEN;
     429               0 :         else if (aSoundAlias.Equals(NS_SYSSOUND_CONFIRM_DIALOG))
     430               0 :             eventId = EVENT_CONFIRM_DIALOG_OPEN;
     431               0 :         else if (aSoundAlias.Equals(NS_SYSSOUND_MAIL_BEEP))
     432               0 :             eventId = EVENT_NEW_MAIL_RECEIVED;
     433               0 :         else if (aSoundAlias.Equals(NS_SYSSOUND_MENU_EXECUTE))
     434               0 :             eventId = EVENT_MENU_EXECUTE;
     435               0 :         else if (aSoundAlias.Equals(NS_SYSSOUND_MENU_POPUP))
     436               0 :             eventId = EVENT_MENU_POPUP;
     437                 :         else
     438               0 :             return NS_OK;
     439               0 :         return PlayEventSound(eventId);
     440                 :     }
     441                 : 
     442                 :     nsresult rv;
     443               0 :     nsCOMPtr <nsIURI> fileURI;
     444                 : 
     445                 :     // create a nsILocalFile and then a nsIFileURL from that
     446               0 :     nsCOMPtr <nsILocalFile> soundFile;
     447                 :     rv = NS_NewLocalFile(aSoundAlias, true, 
     448               0 :                          getter_AddRefs(soundFile));
     449               0 :     NS_ENSURE_SUCCESS(rv,rv);
     450                 : 
     451               0 :     rv = NS_NewFileURI(getter_AddRefs(fileURI), soundFile);
     452               0 :     NS_ENSURE_SUCCESS(rv,rv);
     453                 : 
     454               0 :     nsCOMPtr<nsIFileURL> fileURL = do_QueryInterface(fileURI,&rv);
     455               0 :     NS_ENSURE_SUCCESS(rv,rv);
     456                 : 
     457               0 :     rv = Play(fileURL);
     458                 : 
     459               0 :     return rv;
     460                 : }

Generated by: LCOV version 1.7