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 -- [email protected] -- http://www.Moolenaar.net   \\\
///                                                                      \\\
\\\        sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ ///
 \\\            help me help AIDS victims -- http://ICCF-Holland.org    ///

-- 
-- 
You received this message from the "vim_dev" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php

--- 
You received this message because you are subscribed to the Google Groups 
"vim_dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/vim_dev/20221008125058.458621C0CD2%40moolenaar.net.

Raspunde prin e-mail lui