Patch 9.0.0694

5 views
Skip to first unread message

Bram Moolenaar

unread,
Oct 8, 2022, 8:51:22 AM10/8/22
to vim...@googlegroups.com

Patch 9.0.0694
Problem: No native sound support on Mac OS.
Solution: Add sound support for Mac OS. (Yee Cheng Chin, closes #11274)
Files: runtime/doc/builtin.txt, src/configure.ac, src/feature.h,
src/getchar.c, src/os_macosx.m, src/os_unix.c, src/proto.h,
src/proto/os_macosx.pro, src/proto/sound.pro, src/sound.c,
src/ui.c, src/vim.h, src/testdir/test_sound.vim


*** ../vim-9.0.0693/runtime/doc/builtin.txt 2022-10-07 14:31:04.316852643 +0100
--- runtime/doc/builtin.txt 2022-10-08 13:35:32.135921536 +0100
***************
*** 8612,8617 ****
--- 8631,8639 ----
< 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
*** ../vim-9.0.0693/src/configure.ac 2022-10-04 16:23:39.006042192 +0100
--- src/configure.ac 2022-10-08 13:38:23.911150094 +0100
***************
*** 4553,4559 ****
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.
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])
--- 4553,4559 ----
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 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])
*** ../vim-9.0.0693/src/feature.h 2022-10-04 16:28:08.169719793 +0100
--- src/feature.h 2022-10-08 13:35:32.139921518 +0100
***************
*** 484,490 ****
#endif

/*
! * sound - currently only with libcanberra
*/
#if !defined(FEAT_SOUND) && defined(HAVE_CANBERRA)
# define FEAT_SOUND
--- 484,490 ----
#endif

/*
! * sound
*/
#if !defined(FEAT_SOUND) && defined(HAVE_CANBERRA)
# define FEAT_SOUND
*** ../vim-9.0.0693/src/getchar.c 2022-10-04 16:23:39.014042183 +0100
--- src/getchar.c 2022-10-08 13:35:32.139921518 +0100
***************
*** 2326,2331 ****
--- 2326,2335 ----
# 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();
*** ../vim-9.0.0693/src/os_macosx.m 2022-08-21 17:23:57.131150493 +0100
--- src/os_macosx.m 2022-10-08 13:35:32.139921518 +0100
***************
*** 384,389 ****
--- 384,522 ----

#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
*** ../vim-9.0.0693/src/os_unix.c 2022-10-04 13:17:27.496307898 +0100
--- src/os_unix.c 2022-10-08 13:35:32.139921518 +0100
***************
*** 6125,6130 ****
--- 6125,6134 ----
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())
*** ../vim-9.0.0693/src/proto.h 2022-04-03 17:11:32.000000000 +0100
--- src/proto.h 2022-10-08 13:35:32.139921518 +0100
***************
*** 327,332 ****
--- 327,335 ----
# 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);
*** ../vim-9.0.0693/src/proto/os_macosx.pro 2022-10-08 13:48:16.721528655 +0100
--- src/proto/os_macosx.pro 2022-10-08 13:35:32.139921518 +0100
***************
*** 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 : */
*** ../vim-9.0.0693/src/proto/sound.pro 2022-06-27 23:15:22.000000000 +0100
--- src/proto/sound.pro 2022-10-08 13:35:32.139921518 +0100
***************
*** 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);
*** ../vim-9.0.0693/src/sound.c 2021-12-09 10:14:30.000000000 +0000
--- src/sound.c 2022-10-08 13:35:32.139921518 +0100
***************
*** 65,73 ****
}

/*
* Delete "soundcb" from the list of pending callbacks.
*/
! static void
delete_sound_callback(soundcb_T *soundcb)
{
soundcb_T *p;
--- 65,92 ----
}

/*
+ * 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.
*/
! void
delete_sound_callback(soundcb_T *soundcb)
{
soundcb_T *p;
***************
*** 89,95 ****
#if defined(HAVE_CANBERRA) || defined(PROTO)

/*
! * Sound implementation for Linux/Unix/Mac using libcanberra.
*/
# include <canberra.h>

--- 108,114 ----
#if defined(HAVE_CANBERRA) || defined(PROTO)

/*
! * Sound implementation for Linux/Unix using libcanberra.
*/
# include <canberra.h>

***************
*** 152,174 ****
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);

delete_sound_callback(scb->scb_callback);
vim_free(scb);
--- 171,183 ----
invoke_sound_callback(void)
{
soundcb_queue_T *scb;

while (callback_queue != NULL)
{
scb = callback_queue;
callback_queue = scb->scb_next;

! call_sound_callback(scb->scb_callback, scb->scb_id, scb->scb_result);

delete_sound_callback(scb->scb_callback);
vim_free(scb);
***************
*** 307,330 ****
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
! : wParam == MCI_NOTIFY_ABORTED ? 1 : 2;
! argv[2].v_type = VAR_UNKNOWN;
!
! call_callback(&p->snd_callback, -1, &rettv, 2, argv);
! clear_tv(&rettv);

delete_sound_callback(p);
redraw_after_callback(TRUE, FALSE);
--- 316,330 ----
for (p = first_callback; p != NULL; p = p->snd_next)
if (p->snd_device_id == (MCIDEVICEID) lParam)
{
char buf[32];

vim_snprintf(buf, sizeof(buf), "close sound%06ld",
p->snd_id);
mciSendString(buf, NULL, 0, 0);

! long result = wParam == MCI_NOTIFY_SUCCESSFUL ? 0
! : wParam == MCI_NOTIFY_ABORTED ? 1 : 2;
! call_sound_callback(p, p->snd_id, result);

delete_sound_callback(p);
redraw_after_callback(TRUE, FALSE);
***************
*** 459,464 ****
}
# endif

! #endif // MSWIN

#endif // FEAT_SOUND
--- 459,522 ----
}
# endif

! #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
*** ../vim-9.0.0693/src/ui.c 2022-06-10 14:49:43.000000000 +0100
--- src/ui.c 2022-10-08 13:35:32.139921518 +0100
***************
*** 460,466 ****
}
if (due_time <= 0 || (wtime > 0 && due_time > remaining))
due_time = remaining;
! # if defined(FEAT_JOB_CHANNEL) || defined(FEAT_SOUND_CANBERRA)
if ((due_time < 0 || due_time > 10L) && (
# if defined(FEAT_JOB_CHANNEL)
(
--- 460,466 ----
}
if (due_time <= 0 || (wtime > 0 && due_time > remaining))
due_time = remaining;
! # 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,478 ****
!gui.in_use &&
# endif
(has_pending_job() || channel_any_readahead()))
! # ifdef FEAT_SOUND_CANBERRA
||
# endif
# endif
! # ifdef FEAT_SOUND_CANBERRA
has_any_sound_callback()
# endif
))
--- 468,478 ----
!gui.in_use &&
# endif
(has_pending_job() || channel_any_readahead()))
! # if defined(FEAT_SOUND_CANBERRA) || defined(FEAT_SOUND_MACOSX)
||
# endif
# endif
! # if defined(FEAT_SOUND_CANBERRA) || defined(FEAT_SOUND_MACOSX)
has_any_sound_callback()
# endif
))
*** ../vim-9.0.0693/src/vim.h 2022-10-04 16:50:15.663997565 +0100
--- src/vim.h 2022-10-08 13:35:32.139921518 +0100
***************
*** 163,171 ****
*/
#include "feature.h"

! #if defined(MACOS_X_DARWIN) && defined(FEAT_NORMAL) \
! && !defined(FEAT_CLIPBOARD)
! # define FEAT_CLIPBOARD
#endif

// +x11 is only enabled when it's both available and wanted.
--- 163,178 ----
*/
#include "feature.h"

! #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.
*** ../vim-9.0.0693/src/testdir/test_sound.vim 2021-06-07 19:28:19.000000000 +0100
--- src/testdir/test_sound.vim 2022-10-08 13:35:32.139921518 +0100
***************
*** 17,23 ****
endif
let g:playcallback_count = 0
let g:id = 0
! let id = 'bell'->sound_playevent('PlayCallback')
if id == 0
throw 'Skipped: bell event not available'
endif
--- 17,27 ----
endif
let g:playcallback_count = 0
let g:id = 0
! 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
*** ../vim-9.0.0693/src/version.c 2022-10-08 12:52:04.317689786 +0100
--- src/version.c 2022-10-08 13:37:30.991385168 +0100
***************
*** 701,702 ****
--- 701,704 ----
{ /* Add new patch number below this line */
+ /**/
+ 694,
/**/

--
Not too long ago, a keyboard was something to make music with...

/// Bram Moolenaar -- Br...@Moolenaar.net -- http://www.Moolenaar.net \\\
/// \\\
\\\ sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ ///
\\\ help me help AIDS victims -- http://ICCF-Holland.org ///
Reply all
Reply to author
Forward
0 new messages