From: Eli Zaretskii Date: Mon, 15 Sep 2014 15:51:57 +0000 (+0300) Subject: Support playing on MS-Windows non-ASCII sound files using Unicode APIs. X-Git-Tag: emacs-25.0.90~2635^2~679^2~285 X-Git-Url: http://git.eshelyaron.com/gitweb/?a=commitdiff_plain;h=a7fc3ab8f1e7061a468eaff0c0b47abd12c99003;p=emacs.git Support playing on MS-Windows non-ASCII sound files using Unicode APIs. src/sound.c [WINDOWSNT]: Include w32common.h and mbstring.h. (SOUND_WARNING) [WINDOWSNT]: Include in do..while and improve the error message format. Use message_with_string to have non-ASCII file names properly displayed. (do_play_sound) [WINDOWSNT]: Use Unicode APIs to play sound files when w32-unicode-filenames is non-nil, but not on Windows 9X, where these APIs are not available even in UNICOWS.DLL. Improve the format of error messages and include the file name in them where appropriate. (Fplay_sound_internal) [WINDOWSNT]: Make the MS-Windows branch call play-sound-functions, per documentation. src/w32.c (w32_get_long_filename, w32_get_short_filename): Constify the input file name arguments. src/w32.h (w32_get_long_filename, w32_get_short_filename): Update prototypes. --- diff --git a/src/ChangeLog b/src/ChangeLog index 185a6c4bd40..2258f58299e 100644 --- a/src/ChangeLog +++ b/src/ChangeLog @@ -1,3 +1,23 @@ +2014-09-15 Eli Zaretskii + + * sound.c [WINDOWSNT]: Include w32common.h and mbstring.h. + (SOUND_WARNING) [WINDOWSNT]: Include in do..while and improve the + error message format. Use message_with_string to have non-ASCII + file names properly displayed. + (do_play_sound) [WINDOWSNT]: Use Unicode APIs to play sound files + when w32-unicode-filenames is non-nil, but not on Windows 9X, + where these APIs are not available even in UNICOWS.DLL. Improve + the format of error messages and include the file name in them + where appropriate. + (Fplay_sound_internal) [WINDOWSNT]: Make the MS-Windows branch + call play-sound-functions, per documentation. + + * w32.c (w32_get_long_filename, w32_get_short_filename): Constify + the input file name arguments. + + * w32.h (w32_get_long_filename, w32_get_short_filename): Update + prototypes. + 2014-09-15 Dmitry Antipov If USE_LOCAL_ALLOCATORS, allocate some Lisp objects on stack. diff --git a/src/sound.c b/src/sound.c index 7ba14b36f33..b49348f1256 100644 --- a/src/sound.c +++ b/src/sound.c @@ -86,10 +86,12 @@ along with GNU Emacs. If not, see . */ /* BEGIN: Windows Specific Includes */ #include #include +#include #include #include #include "coding.h" +#include "w32common.h" #include "w32.h" /* END: Windows Specific Includes */ @@ -1207,38 +1209,83 @@ alsa_init (struct sound_device *sd) /* BEGIN: Windows specific functions */ -#define SOUND_WARNING(fun, error, text) \ - { \ - char buf[1024]; \ - char err_string[MAXERRORLENGTH]; \ - fun (error, err_string, sizeof (err_string)); \ - _snprintf (buf, sizeof (buf), "%s\nError: %s", \ - text, err_string); \ - sound_warning (buf); \ - } +#define SOUND_WARNING(func, error, text) \ + do { \ + char buf[1024]; \ + char err_string[MAXERRORLENGTH]; \ + func (error, err_string, sizeof (err_string)); \ + _snprintf (buf, sizeof (buf), "%s\nMCI Error: %s", \ + text, err_string); \ + message_with_string ("%s", build_string (buf), 1); \ + } while (0) static int do_play_sound (const char *psz_file, unsigned long ui_volume) { int i_result = 0; MCIERROR mci_error = 0; - char sz_cmd_buf[520] = {0}; - char sz_ret_buf[520] = {0}; + char sz_cmd_buf_a[520]; + char sz_ret_buf_a[520]; MMRESULT mm_result = MMSYSERR_NOERROR; unsigned long ui_volume_org = 0; BOOL b_reset_volume = FALSE; + char warn_text[560]; - memset (sz_cmd_buf, 0, sizeof (sz_cmd_buf)); - memset (sz_ret_buf, 0, sizeof (sz_ret_buf)); - sprintf (sz_cmd_buf, - "open \"%s\" alias GNUEmacs_PlaySound_Device wait", - psz_file); - mci_error = mciSendString (sz_cmd_buf, sz_ret_buf, sizeof (sz_ret_buf), NULL); + /* Since UNICOWS.DLL includes only a stub for mciSendStringW, we + need to encode the file in the ANSI codepage on Windows 9X even + if w32_unicode_filenames is non-zero. */ + if (w32_major_version <= 4 || !w32_unicode_filenames) + { + char fname_a[MAX_PATH], shortname[MAX_PATH], *fname_to_use; + + filename_to_ansi (psz_file, fname_a); + fname_to_use = fname_a; + /* If the file name is not encodable in ANSI, try its short 8+3 + alias. This will only work if w32_unicode_filenames is + non-zero. */ + if (_mbspbrk ((const unsigned char *)fname_a, + (const unsigned char *)"?")) + { + if (w32_get_short_filename (psz_file, shortname, MAX_PATH)) + fname_to_use = shortname; + else + mci_error = MCIERR_FILE_NOT_FOUND; + } + + if (!mci_error) + { + memset (sz_cmd_buf_a, 0, sizeof (sz_cmd_buf_a)); + memset (sz_ret_buf_a, 0, sizeof (sz_ret_buf_a)); + sprintf (sz_cmd_buf_a, + "open \"%s\" alias GNUEmacs_PlaySound_Device wait", + fname_to_use); + mci_error = mciSendStringA (sz_cmd_buf_a, + sz_ret_buf_a, sizeof (sz_ret_buf_a), NULL); + } + } + else + { + wchar_t sz_cmd_buf_w[520]; + wchar_t sz_ret_buf_w[520]; + wchar_t fname_w[MAX_PATH]; + + filename_to_utf16 (psz_file, fname_w); + memset (sz_cmd_buf_w, 0, sizeof (sz_cmd_buf_w)); + memset (sz_ret_buf_w, 0, sizeof (sz_ret_buf_w)); + /* _swprintf is not available on Windows 9X, so we construct the + UTF-16 command string by hand. */ + wcscpy (sz_cmd_buf_w, L"open \""); + wcscat (sz_cmd_buf_w, fname_w); + wcscat (sz_cmd_buf_w, L"\" alias GNUEmacs_PlaySound_Device wait"); + mci_error = mciSendStringW (sz_cmd_buf_w, + sz_ret_buf_w, ARRAYELTS (sz_ret_buf_w) , NULL); + } if (mci_error != 0) { - SOUND_WARNING (mciGetErrorString, mci_error, - "The open mciSendString command failed to open " - "the specified sound file."); + strcpy (warn_text, + "mciSendString: 'open' command failed to open sound file "); + strcat (warn_text, psz_file); + SOUND_WARNING (mciGetErrorString, mci_error, warn_text); i_result = (int) mci_error; return i_result; } @@ -1252,44 +1299,47 @@ do_play_sound (const char *psz_file, unsigned long ui_volume) if (mm_result != MMSYSERR_NOERROR) { SOUND_WARNING (waveOutGetErrorText, mm_result, - "waveOutSetVolume failed to set the volume level " - "of the WAVE_MAPPER device.\n" - "As a result, the user selected volume level will " - "not be used."); + "waveOutSetVolume: failed to set the volume level" + " of the WAVE_MAPPER device.\n" + "As a result, the user selected volume level will" + " not be used."); } } else { SOUND_WARNING (waveOutGetErrorText, mm_result, - "waveOutGetVolume failed to obtain the original " - "volume level of the WAVE_MAPPER device.\n" - "As a result, the user selected volume level will " - "not be used."); + "waveOutGetVolume: failed to obtain the original" + " volume level of the WAVE_MAPPER device.\n" + "As a result, the user selected volume level will" + " not be used."); } } - memset (sz_cmd_buf, 0, sizeof (sz_cmd_buf)); - memset (sz_ret_buf, 0, sizeof (sz_ret_buf)); - strcpy (sz_cmd_buf, "play GNUEmacs_PlaySound_Device wait"); - mci_error = mciSendString (sz_cmd_buf, sz_ret_buf, sizeof (sz_ret_buf), NULL); + memset (sz_cmd_buf_a, 0, sizeof (sz_cmd_buf_a)); + memset (sz_ret_buf_a, 0, sizeof (sz_ret_buf_a)); + strcpy (sz_cmd_buf_a, "play GNUEmacs_PlaySound_Device wait"); + mci_error = mciSendStringA (sz_cmd_buf_a, sz_ret_buf_a, sizeof (sz_ret_buf_a), + NULL); if (mci_error != 0) { - SOUND_WARNING (mciGetErrorString, mci_error, - "The play mciSendString command failed to play the " - "opened sound file."); + strcpy (warn_text, + "mciSendString: 'play' command failed to play sound file "); + strcat (warn_text, psz_file); + SOUND_WARNING (mciGetErrorString, mci_error, warn_text); i_result = (int) mci_error; } - memset (sz_cmd_buf, 0, sizeof (sz_cmd_buf)); - memset (sz_ret_buf, 0, sizeof (sz_ret_buf)); - strcpy (sz_cmd_buf, "close GNUEmacs_PlaySound_Device wait"); - mci_error = mciSendString (sz_cmd_buf, sz_ret_buf, sizeof (sz_ret_buf), NULL); + memset (sz_cmd_buf_a, 0, sizeof (sz_cmd_buf_a)); + memset (sz_ret_buf_a, 0, sizeof (sz_ret_buf_a)); + strcpy (sz_cmd_buf_a, "close GNUEmacs_PlaySound_Device wait"); + mci_error = mciSendStringA (sz_cmd_buf_a, sz_ret_buf_a, sizeof (sz_ret_buf_a), + NULL); if (b_reset_volume == TRUE) { mm_result = waveOutSetVolume ((HWAVEOUT) WAVE_MAPPER, ui_volume_org); if (mm_result != MMSYSERR_NOERROR) { SOUND_WARNING (waveOutGetErrorText, mm_result, - "waveOutSetVolume failed to reset the original volume " - "level of the WAVE_MAPPER device."); + "waveOutSetVolume: failed to reset the original" + " volume level of the WAVE_MAPPER device."); } } return i_result; @@ -1307,13 +1357,11 @@ Internal use only, use `play-sound' instead. */) { Lisp_Object attrs[SOUND_ATTR_SENTINEL]; ptrdiff_t count = SPECPDL_INDEX (); - -#ifndef WINDOWSNT Lisp_Object file; - struct gcpro gcpro1, gcpro2; Lisp_Object args[2]; -#else /* WINDOWSNT */ - Lisp_Object lo_file; + struct gcpro gcpro1, gcpro2; + +#ifdef WINDOWSNT unsigned long ui_volume_tmp = UINT_MAX; unsigned long ui_volume = UINT_MAX; #endif /* WINDOWSNT */ @@ -1386,11 +1434,8 @@ Internal use only, use `play-sound' instead. */) #else /* WINDOWSNT */ - lo_file = Fexpand_file_name (attrs[SOUND_FILE], Vdata_directory); - lo_file = ENCODE_FILE (lo_file); - /* Since UNICOWS.DLL includes only a stub for mciSendStringW, we - need to encode the file in the ANSI codepage. */ - lo_file = ansi_encode_filename (lo_file); + file = Fexpand_file_name (attrs[SOUND_FILE], Vdata_directory); + file = ENCODE_FILE (file); if (INTEGERP (attrs[SOUND_VOLUME])) { ui_volume_tmp = XFASTINT (attrs[SOUND_VOLUME]); @@ -1399,6 +1444,13 @@ Internal use only, use `play-sound' instead. */) { ui_volume_tmp = XFLOAT_DATA (attrs[SOUND_VOLUME]) * 100; } + + GCPRO2 (sound, file); + + args[0] = Qplay_sound_functions; + args[1] = sound; + Frun_hook_with_args (2, args); + /* Based on some experiments I have conducted, a value of 100 or less for the sound volume is much too low. You cannot even hear it. @@ -1412,7 +1464,9 @@ Internal use only, use `play-sound' instead. */) { ui_volume = ui_volume_tmp * (UINT_MAX / 100); } - do_play_sound (SDATA (lo_file), ui_volume); + (void)do_play_sound (SSDATA (file), ui_volume); + + UNGCPRO; #endif /* WINDOWSNT */ diff --git a/src/w32.c b/src/w32.c index 95e6d394ee0..f014cd73a76 100644 --- a/src/w32.c +++ b/src/w32.c @@ -2294,7 +2294,7 @@ get_long_basename (char * name, char * buf, int size) /* Get long name for file, if possible (assumed to be absolute). */ BOOL -w32_get_long_filename (char * name, char * buf, int size) +w32_get_long_filename (const char * name, char * buf, int size) { char * o = buf; char * p; @@ -2345,7 +2345,7 @@ w32_get_long_filename (char * name, char * buf, int size) } unsigned int -w32_get_short_filename (char * name, char * buf, int size) +w32_get_short_filename (const char * name, char * buf, int size) { if (w32_unicode_filenames) { diff --git a/src/w32.h b/src/w32.h index 2cc179a0c36..e0aedcbffa2 100644 --- a/src/w32.h +++ b/src/w32.h @@ -144,10 +144,10 @@ extern char * w32_strerror (int error_no); extern int w32_valid_pointer_p (void *, int); /* Get long (aka "true") form of file name, if it exists. */ -extern BOOL w32_get_long_filename (char * name, char * buf, int size); +extern BOOL w32_get_long_filename (const char * name, char * buf, int size); /* Get the short (a.k.a. "8+3") form of a file name. */ -extern unsigned int w32_get_short_filename (char *, char *, int); +extern unsigned int w32_get_short_filename (const char *, char *, int); /* Prepare our standard handles for proper inheritance by child processes. */ extern void prepare_standard_handles (int in, int out,