mirror of
https://github.com/vim/vim
synced 2025-07-16 09:12:00 +00:00
patch 9.0.0694: no native sound support on Mac OS
Problem: No native sound support on Mac OS. Solution: Add sound support for Mac OS. (Yee Cheng Chin, closes #11274)
This commit is contained in:
committed by
Bram Moolenaar
parent
5a049846e4
commit
4314e4f7da
@ -8631,6 +8631,9 @@ sound_playevent({name} [, {callback}])
|
||||
< On MS-Windows, {name} can be SystemAsterisk, SystemDefault,
|
||||
SystemExclamation, SystemExit, SystemHand, SystemQuestion,
|
||||
SystemStart, SystemWelcome, etc.
|
||||
On macOS, {name} refers to files located in
|
||||
/System/Library/Sounds (e.g. "Tink"). It will also work for
|
||||
custom installed sounds in folders like ~/Library/Sounds.
|
||||
|
||||
When {callback} is specified it is invoked when the sound is
|
||||
finished. The first argument is the sound ID, the second
|
||||
|
@ -4553,7 +4553,7 @@ if test "$MACOS_X" = "yes"; then
|
||||
AC_MSG_CHECKING([whether we need macOS frameworks])
|
||||
if test "$MACOS_X_DARWIN" = "yes"; then
|
||||
if test "$features" = "tiny"; then
|
||||
dnl Since no FEAT_CLIPBOARD, no longer need for os_macosx.m.
|
||||
dnl Since no FEAT_CLIPBOARD or FEAT_SOUND, no need for os_macosx.m.
|
||||
OS_EXTRA_SRC=`echo "$OS_EXTRA_SRC" | sed -e 's+os_macosx.m++'`
|
||||
OS_EXTRA_OBJ=`echo "$OS_EXTRA_OBJ" | sed -e 's+objects/os_macosx.o++'`
|
||||
AC_MSG_RESULT([yes, we need CoreServices])
|
||||
|
@ -484,7 +484,7 @@
|
||||
#endif
|
||||
|
||||
/*
|
||||
* sound - currently only with libcanberra
|
||||
* sound
|
||||
*/
|
||||
#if !defined(FEAT_SOUND) && defined(HAVE_CANBERRA)
|
||||
# define FEAT_SOUND
|
||||
|
@ -2326,6 +2326,10 @@ parse_queued_messages(void)
|
||||
# ifdef FEAT_TERMINAL
|
||||
free_unused_terminals();
|
||||
# endif
|
||||
|
||||
# ifdef FEAT_SOUND_MACOSX
|
||||
process_cfrunloop();
|
||||
# endif
|
||||
# ifdef FEAT_SOUND_CANBERRA
|
||||
if (has_sound_callback_in_queue())
|
||||
invoke_sound_callback();
|
||||
|
133
src/os_macosx.m
133
src/os_macosx.m
@ -384,6 +384,139 @@ timer_delete(timer_t timerid)
|
||||
|
||||
#endif /* FEAT_RELTIME */
|
||||
|
||||
#ifdef FEAT_SOUND
|
||||
|
||||
static NSMutableDictionary<NSNumber*, NSSound*> *sounds_list = nil;
|
||||
|
||||
/// A delegate for handling when a sound has stopped playing, in
|
||||
/// order to clean up the sound and to send a callback.
|
||||
@interface SoundDelegate : NSObject<NSSoundDelegate>;
|
||||
|
||||
- (id) init:(long) sound_id callback:(soundcb_T*) callback;
|
||||
- (void) sound:(NSSound *)sound didFinishPlaying:(BOOL)flag;
|
||||
|
||||
@property (readonly) long sound_id;
|
||||
@property (readonly) soundcb_T *callback;
|
||||
|
||||
@end
|
||||
|
||||
@implementation SoundDelegate
|
||||
- (id) init:(long) sound_id callback:(soundcb_T*) callback
|
||||
{
|
||||
if ([super init])
|
||||
{
|
||||
_sound_id = sound_id;
|
||||
_callback = callback;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void) sound:(NSSound *)sound didFinishPlaying:(BOOL)flag
|
||||
{
|
||||
if (sounds_list != nil)
|
||||
{
|
||||
if (_callback)
|
||||
{
|
||||
call_sound_callback(_callback, _sound_id, flag ? 0 : 1);
|
||||
delete_sound_callback(_callback);
|
||||
_callback = NULL;
|
||||
}
|
||||
[sounds_list removeObjectForKey:[NSNumber numberWithLong:_sound_id]];
|
||||
}
|
||||
// Release itself. Do that here instead of earlier because NSSound only
|
||||
// holds weak reference to this object.
|
||||
[self release];
|
||||
}
|
||||
@end
|
||||
|
||||
void
|
||||
process_cfrunloop()
|
||||
{
|
||||
if (sounds_list != nil && [sounds_list count] > 0)
|
||||
{
|
||||
// Continually drain the run loop of events. Currently, this
|
||||
// is only used for processing sound callbacks, because
|
||||
// NSSound relies of this runloop to call back to the
|
||||
// delegate.
|
||||
@autoreleasepool
|
||||
{
|
||||
while (CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0, true)
|
||||
== kCFRunLoopRunHandledSource)
|
||||
; // do nothing
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
sound_mch_play(const char_u* sound_name, long sound_id, soundcb_T *callback, bool playfile)
|
||||
{
|
||||
@autoreleasepool
|
||||
{
|
||||
NSString *sound_name_ns = [[[NSString alloc] initWithUTF8String:(const char*)sound_name] autorelease];
|
||||
NSSound* sound = playfile ?
|
||||
[[[NSSound alloc] initWithContentsOfFile:sound_name_ns byReference:YES] autorelease] :
|
||||
[NSSound soundNamed:sound_name_ns];
|
||||
if (!sound)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (sounds_list == nil)
|
||||
{
|
||||
sounds_list = [[NSMutableDictionary<NSNumber*, NSSound*> alloc] init];
|
||||
}
|
||||
sounds_list[[NSNumber numberWithLong:sound_id]] = sound;
|
||||
|
||||
// Make a delegate to handle when the sound stops. No need to call
|
||||
// autorelease because NSSound only holds a weak reference to it.
|
||||
SoundDelegate *delegate = [[SoundDelegate alloc] init:sound_id callback:callback];
|
||||
|
||||
[sound setDelegate:delegate];
|
||||
[sound play];
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
sound_mch_stop(long sound_id)
|
||||
{
|
||||
@autoreleasepool
|
||||
{
|
||||
NSSound *sound = sounds_list[[NSNumber numberWithLong:sound_id]];
|
||||
if (sound != nil)
|
||||
{
|
||||
// Stop the sound. No need to release it because the delegate will do
|
||||
// it for us.
|
||||
[sound stop];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
sound_mch_clear()
|
||||
{
|
||||
if (sounds_list != nil)
|
||||
{
|
||||
@autoreleasepool
|
||||
{
|
||||
for (NSSound *sound in [sounds_list allValues])
|
||||
{
|
||||
[sound stop];
|
||||
}
|
||||
[sounds_list release];
|
||||
sounds_list = nil;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
sound_mch_free()
|
||||
{
|
||||
sound_mch_clear();
|
||||
}
|
||||
|
||||
#endif // FEAT_SOUND
|
||||
|
||||
/* Lift the compiler warning suppression. */
|
||||
#if defined(__clang__) && defined(__STRICT_ANSI__)
|
||||
# pragma clang diagnostic pop
|
||||
|
@ -6125,6 +6125,10 @@ WaitForCharOrMouse(long msec, int *interrupted, int ignore_input)
|
||||
rest -= msec;
|
||||
}
|
||||
# endif
|
||||
# ifdef FEAT_SOUND_MACOSX
|
||||
// Invoke any pending sound callbacks.
|
||||
process_cfrunloop();
|
||||
# endif
|
||||
# ifdef FEAT_SOUND_CANBERRA
|
||||
// Invoke any pending sound callbacks.
|
||||
if (has_sound_callback_in_queue())
|
||||
|
@ -327,6 +327,9 @@ extern char_u *vimpty_getenv(const char_u *string); // in misc2.c
|
||||
# ifdef MACOS_CONVERT
|
||||
# include "os_mac_conv.pro"
|
||||
# endif
|
||||
# ifdef MACOS_X
|
||||
# include "os_macosx.pro"
|
||||
# endif
|
||||
# if defined(MACOS_X_DARWIN) && defined(FEAT_CLIPBOARD) && !defined(FEAT_GUI)
|
||||
// functions in os_macosx.m
|
||||
void clip_mch_lose_selection(Clipboard_T *cbd);
|
||||
|
7
src/proto/os_macosx.pro
Normal file
7
src/proto/os_macosx.pro
Normal file
@ -0,0 +1,7 @@
|
||||
/* os_macosx.m */
|
||||
void process_cfrunloop();
|
||||
bool sound_mch_play(const char_u* event, long sound_id, soundcb_T *callback, bool playfile);
|
||||
void sound_mch_stop(long sound_id);
|
||||
void sound_mch_clear();
|
||||
void sound_mch_free();
|
||||
/* vim: set ft=c : */
|
@ -1,6 +1,10 @@
|
||||
/* sound.c */
|
||||
typedef struct soundcb_S soundcb_T;
|
||||
|
||||
int has_any_sound_callback(void);
|
||||
int has_sound_callback_in_queue(void);
|
||||
void call_sound_callback(soundcb_T *soundcb, long sound_id, int result);
|
||||
void delete_sound_callback(soundcb_T *soundcb);
|
||||
void invoke_sound_callback(void);
|
||||
void f_sound_playevent(typval_T *argvars, typval_T *rettv);
|
||||
void f_sound_playfile(typval_T *argvars, typval_T *rettv);
|
||||
|
108
src/sound.c
108
src/sound.c
@ -64,10 +64,29 @@ get_sound_callback(typval_T *arg)
|
||||
return soundcb;
|
||||
}
|
||||
|
||||
/*
|
||||
* Call "soundcb" with proper parameters.
|
||||
*/
|
||||
void
|
||||
call_sound_callback(soundcb_T *soundcb, long snd_id, int result)
|
||||
{
|
||||
typval_T argv[3];
|
||||
typval_T rettv;
|
||||
|
||||
argv[0].v_type = VAR_NUMBER;
|
||||
argv[0].vval.v_number = snd_id;
|
||||
argv[1].v_type = VAR_NUMBER;
|
||||
argv[1].vval.v_number = result;
|
||||
argv[2].v_type = VAR_UNKNOWN;
|
||||
|
||||
call_callback(&soundcb->snd_callback, -1, &rettv, 2, argv);
|
||||
clear_tv(&rettv);
|
||||
}
|
||||
|
||||
/*
|
||||
* Delete "soundcb" from the list of pending callbacks.
|
||||
*/
|
||||
static void
|
||||
void
|
||||
delete_sound_callback(soundcb_T *soundcb)
|
||||
{
|
||||
soundcb_T *p;
|
||||
@ -89,7 +108,7 @@ delete_sound_callback(soundcb_T *soundcb)
|
||||
#if defined(HAVE_CANBERRA) || defined(PROTO)
|
||||
|
||||
/*
|
||||
* Sound implementation for Linux/Unix/Mac using libcanberra.
|
||||
* Sound implementation for Linux/Unix using libcanberra.
|
||||
*/
|
||||
# include <canberra.h>
|
||||
|
||||
@ -152,23 +171,13 @@ has_sound_callback_in_queue(void)
|
||||
invoke_sound_callback(void)
|
||||
{
|
||||
soundcb_queue_T *scb;
|
||||
typval_T argv[3];
|
||||
typval_T rettv;
|
||||
|
||||
|
||||
while (callback_queue != NULL)
|
||||
{
|
||||
scb = callback_queue;
|
||||
callback_queue = scb->scb_next;
|
||||
|
||||
argv[0].v_type = VAR_NUMBER;
|
||||
argv[0].vval.v_number = scb->scb_id;
|
||||
argv[1].v_type = VAR_NUMBER;
|
||||
argv[1].vval.v_number = scb->scb_result;
|
||||
argv[2].v_type = VAR_UNKNOWN;
|
||||
|
||||
call_callback(&scb->scb_callback->snd_callback, -1, &rettv, 2, argv);
|
||||
clear_tv(&rettv);
|
||||
call_sound_callback(scb->scb_callback, scb->scb_id, scb->scb_result);
|
||||
|
||||
delete_sound_callback(scb->scb_callback);
|
||||
vim_free(scb);
|
||||
@ -307,24 +316,15 @@ sound_wndproc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
|
||||
for (p = first_callback; p != NULL; p = p->snd_next)
|
||||
if (p->snd_device_id == (MCIDEVICEID) lParam)
|
||||
{
|
||||
typval_T argv[3];
|
||||
typval_T rettv;
|
||||
char buf[32];
|
||||
|
||||
vim_snprintf(buf, sizeof(buf), "close sound%06ld",
|
||||
p->snd_id);
|
||||
mciSendString(buf, NULL, 0, 0);
|
||||
|
||||
argv[0].v_type = VAR_NUMBER;
|
||||
argv[0].vval.v_number = p->snd_id;
|
||||
argv[1].v_type = VAR_NUMBER;
|
||||
argv[1].vval.v_number =
|
||||
wParam == MCI_NOTIFY_SUCCESSFUL ? 0
|
||||
long result = wParam == MCI_NOTIFY_SUCCESSFUL ? 0
|
||||
: wParam == MCI_NOTIFY_ABORTED ? 1 : 2;
|
||||
argv[2].v_type = VAR_UNKNOWN;
|
||||
|
||||
call_callback(&p->snd_callback, -1, &rettv, 2, argv);
|
||||
clear_tv(&rettv);
|
||||
call_sound_callback(p, p->snd_id, result);
|
||||
|
||||
delete_sound_callback(p);
|
||||
redraw_after_callback(TRUE, FALSE);
|
||||
@ -459,6 +459,64 @@ sound_free(void)
|
||||
}
|
||||
# endif
|
||||
|
||||
#endif // MSWIN
|
||||
#elif defined(MACOS_X_DARWIN)
|
||||
|
||||
// Sound implementation for macOS.
|
||||
static void
|
||||
sound_play_common(typval_T *argvars, typval_T *rettv, bool playfile)
|
||||
{
|
||||
if (in_vim9script() && check_for_string_arg(argvars, 0) == FAIL)
|
||||
return;
|
||||
|
||||
char_u *sound_name = tv_get_string(&argvars[0]);
|
||||
soundcb_T *soundcb = get_sound_callback(&argvars[1]);
|
||||
|
||||
++sound_id;
|
||||
|
||||
bool play_success = sound_mch_play(sound_name, sound_id, soundcb, playfile);
|
||||
if (!play_success && soundcb)
|
||||
{
|
||||
delete_sound_callback(soundcb);
|
||||
}
|
||||
rettv->vval.v_number = play_success ? sound_id : 0;
|
||||
}
|
||||
|
||||
void
|
||||
f_sound_playevent(typval_T *argvars, typval_T *rettv)
|
||||
{
|
||||
sound_play_common(argvars, rettv, false);
|
||||
}
|
||||
|
||||
void
|
||||
f_sound_playfile(typval_T *argvars, typval_T *rettv)
|
||||
{
|
||||
sound_play_common(argvars, rettv, true);
|
||||
}
|
||||
|
||||
void
|
||||
f_sound_stop(typval_T *argvars, typval_T *rettv UNUSED)
|
||||
{
|
||||
if (in_vim9script() && check_for_number_arg(argvars, 0) == FAIL)
|
||||
return;
|
||||
sound_mch_stop(tv_get_number(&argvars[0]));
|
||||
}
|
||||
|
||||
void
|
||||
f_sound_clear(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
|
||||
{
|
||||
sound_mch_clear();
|
||||
}
|
||||
|
||||
#if defined(EXITFREE) || defined(PROTO)
|
||||
void
|
||||
sound_free(void)
|
||||
{
|
||||
sound_mch_free();
|
||||
while (first_callback != NULL)
|
||||
delete_sound_callback(first_callback);
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // MACOS_X_DARWIN
|
||||
|
||||
#endif // FEAT_SOUND
|
||||
|
@ -17,7 +17,11 @@ func Test_play_event()
|
||||
endif
|
||||
let g:playcallback_count = 0
|
||||
let g:id = 0
|
||||
let id = 'bell'->sound_playevent('PlayCallback')
|
||||
let event_name = 'bell'
|
||||
if has('osx')
|
||||
let event_name = 'Tink'
|
||||
endif
|
||||
let id = event_name->sound_playevent('PlayCallback')
|
||||
if id == 0
|
||||
throw 'Skipped: bell event not available'
|
||||
endif
|
||||
|
6
src/ui.c
6
src/ui.c
@ -460,7 +460,7 @@ ui_wait_for_chars_or_timer(
|
||||
}
|
||||
if (due_time <= 0 || (wtime > 0 && due_time > remaining))
|
||||
due_time = remaining;
|
||||
# if defined(FEAT_JOB_CHANNEL) || defined(FEAT_SOUND_CANBERRA)
|
||||
# if defined(FEAT_JOB_CHANNEL) || defined(FEAT_SOUND_CANBERRA) || defined(FEAT_SOUND_MACOSX)
|
||||
if ((due_time < 0 || due_time > 10L) && (
|
||||
# if defined(FEAT_JOB_CHANNEL)
|
||||
(
|
||||
@ -468,11 +468,11 @@ ui_wait_for_chars_or_timer(
|
||||
!gui.in_use &&
|
||||
# endif
|
||||
(has_pending_job() || channel_any_readahead()))
|
||||
# ifdef FEAT_SOUND_CANBERRA
|
||||
# if defined(FEAT_SOUND_CANBERRA) || defined(FEAT_SOUND_MACOSX)
|
||||
||
|
||||
# endif
|
||||
# endif
|
||||
# ifdef FEAT_SOUND_CANBERRA
|
||||
# if defined(FEAT_SOUND_CANBERRA) || defined(FEAT_SOUND_MACOSX)
|
||||
has_any_sound_callback()
|
||||
# endif
|
||||
))
|
||||
|
@ -699,6 +699,8 @@ static char *(features[]) =
|
||||
|
||||
static int included_patches[] =
|
||||
{ /* Add new patch number below this line */
|
||||
/**/
|
||||
694,
|
||||
/**/
|
||||
693,
|
||||
/**/
|
||||
|
11
src/vim.h
11
src/vim.h
@ -163,10 +163,17 @@
|
||||
*/
|
||||
#include "feature.h"
|
||||
|
||||
#if defined(MACOS_X_DARWIN) && defined(FEAT_NORMAL) \
|
||||
&& !defined(FEAT_CLIPBOARD)
|
||||
#if defined(MACOS_X_DARWIN)
|
||||
# if defined(FEAT_NORMAL) && !defined(FEAT_CLIPBOARD)
|
||||
# define FEAT_CLIPBOARD
|
||||
# endif
|
||||
# if defined(FEAT_BIG) && !defined(FEAT_SOUND)
|
||||
# define FEAT_SOUND
|
||||
# endif
|
||||
# if defined(FEAT_SOUND)
|
||||
# define FEAT_SOUND_MACOSX
|
||||
# endif
|
||||
#endif
|
||||
|
||||
// +x11 is only enabled when it's both available and wanted.
|
||||
#if defined(HAVE_X11) && defined(WANT_X11)
|
||||
|
Reference in New Issue
Block a user