]> granicus.if.org Git - vim/commitdiff
patch 8.1.1565: MS-Windows: no sound support v8.1.1565
authorBram Moolenaar <Bram@vim.org>
Mon, 17 Jun 2019 20:19:33 +0000 (22:19 +0200)
committerBram Moolenaar <Bram@vim.org>
Mon, 17 Jun 2019 20:19:33 +0000 (22:19 +0200)
Problem:    MS-Windows: no sound support.
Solution:   Add sound support for MS-Windows. (Yasuhiro Matsumoto, Ken Takata,
            closes #4522)

runtime/doc/eval.txt
src/Make_cyg_ming.mak
src/Make_mvc.mak
src/sound.c
src/testdir/test_sound.vim
src/version.c

index 549d27ba9af85c80bac255ff657dc54706a7f646..80fbfba1db9034c01095e56128bc08f661b1826c 100644 (file)
@@ -1,4 +1,4 @@
-*eval.txt*     For Vim version 8.1.  Last change: 2019 Jun 10
+*eval.txt*     For Vim version 8.1.  Last change: 2019 Jun 17
 
 
                  VIM REFERENCE MANUAL    by Bram Moolenaar
@@ -2183,7 +2183,7 @@ v:val             Value of the current item of a |List| or |Dictionary|.  Only
 
                                        *v:version* *version-variable*
 v:version      Version number of Vim: Major version number times 100 plus
-               minor version number.  Version 5.0 is 500.  Version 5.1 (5.01)
+               minor version number.  Version 5.0 is 500.  Version 5.1
                is 501.  Read-only.  "version" also works, for backwards
                compatibility, unless |scriptversion| is 3 or higher.
                Use |has()| to check if a certain patch was included, e.g.: >
@@ -2193,10 +2193,10 @@ v:version       Version number of Vim: Major version number times 100 plus
                completely different.
 
                                        *v:versionlong* *versionlong-variable*
-v:versionlong  Like v:version, but also including the patchlevel.  Version
-               8.1 with patch 1234 has value 8011234.  This can be used like
-               this: >
-                       if v:versionlong >= 8011234
+v:versionlong  Like v:version, but also including the patchlevel in the last
+               four digits.  Version 8.1 with patch 123 has value 8010123.
+               This can be used like this: >
+                       if v:versionlong >= 8010123
 <              However, if there are gaps in the list of patches included
                this will not work well.  This can happen if a recent patch
                was included into an older version, e.g. for a security fix.
@@ -8123,10 +8123,9 @@ setbufline({expr}, {lnum}, {text})                       *setbufline()*
 
                {lnum} is used like with |setline()|.
                This works like |setline()| for the specified buffer.
-               On success 0 is returned, on failure 1 is returned.
 
-               If {expr} is not a valid buffer or {lnum} is not valid, an
-               error message is given.
+               When {expr} is not a valid buffer or {lnum} is not valid then
+               1 is returned.  On success 0 is returned.
 
 setbufvar({expr}, {varname}, {val})                    *setbufvar()*
                Set option or local variable {varname} in buffer {expr} to
@@ -8884,7 +8883,7 @@ sort({list} [, {func} [, {dict}]])                        *sort()* *E702*
 <
 sound_clear()                                          *sound_clear()*
                Stop playing all sounds.
-               {only available when compiled with the +sound feature}
+               {only available when compiled with the |+sound| feature}
 
                                                        *sound_playevent()*
 sound_playevent({name} [, {callback}])
@@ -8893,8 +8892,11 @@ sound_playevent({name} [, {callback}])
                are used.  On Ubuntu they may be found in
                /usr/share/sounds/freedesktop/stereo.  Example: >
                        call sound_playevent('bell')
+<              On MS-Windows, {name} can be SystemAsterisk, SystemDefault,
+               SystemExclamation, SystemExit, SystemHand, SystemQuestion,
+               SystemStart, SystemWelcome, etc.
 
-<              When {callback} is specified it is invoked when the sound is
+               When {callback} is specified it is invoked when the sound is
                finished.  The first argument is the sound ID, the second
                argument is the status:
                        0       sound was played to the end
@@ -8906,7 +8908,9 @@ sound_playevent({name} [, {callback}])
                   endfunc
                   call sound_playevent('bell', 'Callback')
 
-<              Returns the sound ID, which can be passed to `sound_stop()`.
+<              MS-Windows: {callback} doesn't work for this function.
+
+               Returns the sound ID, which can be passed to `sound_stop()`.
                Returns zero if the sound could not be played.
                {only available when compiled with the |+sound| feature}
 
@@ -8922,6 +8926,10 @@ sound_playfile({path} [, {callback}])
 sound_stop({id})                                       *sound_stop()*
                Stop playing sound {id}.  {id} must be previously returned by
                `sound_playevent()` or `sound_playfile()`.
+
+               On MS-Windows, this does not work for event sound started by
+               `sound_playevent()`. To stop event sounds, use `sound_clear()`.
+
                {only available when compiled with the |+sound| feature}
 
                                                        *soundfold()*
@@ -11592,7 +11600,6 @@ text...
                                #       Number
                                *       Funcref
 
-
 :unl[et][!] {name} ...                         *:unlet* *:unl* *E108* *E795*
                        Remove the internal variable {name}.  Several variable
                        names can be given, they are all removed.  The name
@@ -11637,7 +11644,7 @@ text...
 <                      This is useful if you want to make sure the variable
                        is not modified.
                                                        *E995*
-                       |:const| does not allow to for changing a variable. >
+                       |:const| does not allow to for changing a variable: >
                                :let x = 1
                                :const x = 2  " Error!
 <                                                      *E996*
index f47981216c7367f41584f1c4a6a4ffcd97f0bfc7..3c0c92ba13b6559acd7b6cf40f763cc2d869b9c9 100644 (file)
@@ -106,6 +106,13 @@ else
 TERMINAL=no
 endif
 
+# Set to yes to enable sound support.
+ifneq ($(findstring $(FEATURES),BIG HUGE),)
+SOUND=yes
+else
+SOUND=no
+endif
+
 ifndef CTAGS
 # this assumes ctags is Exuberant ctags
 CTAGS = ctags -I INIT+ --fields=+S
@@ -633,6 +640,10 @@ TERM_DEPS = \
        libvterm/src/vterm_internal.h
 endif
 
+ifeq ($(SOUND),yes)
+DEFINES += -DFEAT_SOUND
+endif
+
 # DirectWrite (DirectX)
 ifeq ($(DIRECTX),yes)
 # Only allow DirectWrite for a GUI build.
@@ -849,6 +860,10 @@ OBJ += $(OUTDIR)/terminal.o \
        $(OUTDIR)/vterm.o
 endif
 
+ifeq ($(SOUND),yes)
+OBJ += $(OUTDIR)/sound.o
+endif
+
 # Include xdiff
 OBJ +=  $(OUTDIR)/xdiffi.o \
        $(OUTDIR)/xemit.o \
@@ -957,6 +972,10 @@ CFLAGS += -I$(ICONV)
 DEFINES+=-DDYNAMIC_ICONV
 endif
 
+ifeq (yes, $(SOUND))
+LIB += -lwinmm
+endif
+
 ifeq (yes, $(USE_STDCPLUS))
 LINK = $(CXX)
  ifeq (yes, $(STATIC_STDCPLUS))
index c6f9b25a009a8a2b9dc543449997f3a6585375b3..54740d7f87ee50868b302863cfa6b2e0273db4c9 100644 (file)
@@ -38,7 +38,9 @@
 #        is yes)
 #      Global IME support: GIME=yes (requires GUI=yes)
 #
-#       Terminal support: TERMINAL=yes (default is yes)
+#      Terminal support: TERMINAL=yes (default is yes)
+#
+#      Sound support: SOUND=yes (default is yes)
 #
 #      DLL support (EXPERIMENTAL): VIMDLL=yes (default is no)
 #        Creates vim{32,64}.dll, and stub gvim.exe and vim.exe.
@@ -381,6 +383,14 @@ TERM_DEPS = \
        libvterm/src/vterm_internal.h
 !endif
 
+!ifndef SOUND
+! if "$(FEATURES)"=="HUGE" || "$(FEATURES)"=="BIG"
+SOUND = yes
+! else
+SOUND = no
+! endif
+!endif
+
 !ifndef NETBEANS
 NETBEANS = $(GUI)
 !endif
@@ -454,6 +464,13 @@ XPM_INC      = -I $(XPM)\include -I $(XPM)\..\include
 ! endif
 !endif # GUI
 
+!if "$(SOUND)" == "yes"
+SOUND_PRO      = proto/sound.pro
+SOUND_OBJ      = $(OBJDIR)/sound.obj
+SOUND_DEFS     = -DFEAT_SOUND
+SOUND_LIB      = winmm.lib
+!endif
+
 !if "$(CHANNEL)" == "yes"
 CHANNEL_PRO    = proto/channel.pro
 CHANNEL_OBJ    = $(OBJDIR)/channel.obj
@@ -494,7 +511,7 @@ WINVER = 0x0501
 #VIMRUNTIMEDIR = somewhere
 
 CFLAGS = -c /W3 /nologo $(CVARS) -I. -Iproto -DHAVE_PATHDEF -DWIN32 \
-               $(CSCOPE_DEFS) $(TERM_DEFS) $(NETBEANS_DEFS) $(CHANNEL_DEFS) \
+               $(CSCOPE_DEFS) $(TERM_DEFS) $(SOUND_DEFS) $(NETBEANS_DEFS) $(CHANNEL_DEFS) \
                $(NBDEBUG_DEFS) $(XPM_DEFS) \
                $(DEFINES) -DWINVER=$(WINVER) -D_WIN32_WINNT=$(WINVER)
 
@@ -1217,7 +1234,7 @@ conflags = $(conflags) /map /mapinfo:lines
 LINKARGS1 = $(linkdebug) $(conflags)
 LINKARGS2 = $(CON_LIB) $(GUI_LIB) $(NODEFAULTLIB) $(LIBC) $(OLE_LIB) user32.lib \
                $(LUA_LIB) $(MZSCHEME_LIB) $(PERL_LIB) $(PYTHON_LIB) $(PYTHON3_LIB) $(RUBY_LIB) \
-               $(TCL_LIB) $(NETBEANS_LIB) $(XPM_LIB) $(LINK_PDB)
+               $(TCL_LIB) $(SOUND_LIB) $(NETBEANS_LIB) $(XPM_LIB) $(LINK_PDB)
 
 # Report link time code generation progress if used. 
 !ifdef NODEBUG
@@ -1253,12 +1270,12 @@ all:    $(MAIN_TARGET) \
 
 $(VIMDLLBASE).dll: $(OUTDIR) $(OBJ) $(XDIFF_OBJ) $(GUI_OBJ) $(CUI_OBJ) $(OLE_OBJ) $(OLE_IDL) $(MZSCHEME_OBJ) \
                $(LUA_OBJ) $(PERL_OBJ) $(PYTHON_OBJ) $(PYTHON3_OBJ) $(RUBY_OBJ) $(TCL_OBJ) \
-               $(CSCOPE_OBJ) $(TERM_OBJ) $(NETBEANS_OBJ) $(CHANNEL_OBJ) $(XPM_OBJ) \
+               $(CSCOPE_OBJ) $(TERM_OBJ) $(SOUND_OBJ) $(NETBEANS_OBJ) $(CHANNEL_OBJ) $(XPM_OBJ) \
                version.c version.h
        $(CC) $(CFLAGS_OUTDIR) version.c
        $(link) $(LINKARGS1) /dll -out:$(VIMDLLBASE).dll $(OBJ) $(XDIFF_OBJ) $(GUI_OBJ) $(CUI_OBJ) $(OLE_OBJ) \
                $(LUA_OBJ) $(MZSCHEME_OBJ) $(PERL_OBJ) $(PYTHON_OBJ) $(PYTHON3_OBJ) $(RUBY_OBJ) \
-               $(TCL_OBJ) $(CSCOPE_OBJ) $(TERM_OBJ) $(NETBEANS_OBJ) $(CHANNEL_OBJ) \
+               $(TCL_OBJ) $(CSCOPE_OBJ) $(TERM_OBJ) $(SOUND_OBJ) $(NETBEANS_OBJ) $(CHANNEL_OBJ) \
                $(XPM_OBJ) $(OUTDIR)\version.obj $(LINKARGS2)
 
 $(GVIM).exe: $(OUTDIR) $(EXEOBJG) $(VIMDLLBASE).dll
@@ -1273,12 +1290,12 @@ $(VIM).exe: $(OUTDIR) $(EXEOBJC) $(VIMDLLBASE).dll
 
 $(VIM).exe: $(OUTDIR) $(OBJ) $(XDIFF_OBJ) $(GUI_OBJ) $(CUI_OBJ) $(OLE_OBJ) $(OLE_IDL) $(MZSCHEME_OBJ) \
                $(LUA_OBJ) $(PERL_OBJ) $(PYTHON_OBJ) $(PYTHON3_OBJ) $(RUBY_OBJ) $(TCL_OBJ) \
-               $(CSCOPE_OBJ) $(TERM_OBJ) $(NETBEANS_OBJ) $(CHANNEL_OBJ) $(XPM_OBJ) \
+               $(CSCOPE_OBJ) $(TERM_OBJ) $(SOUND_OBJ) $(NETBEANS_OBJ) $(CHANNEL_OBJ) $(XPM_OBJ) \
                version.c version.h
        $(CC) $(CFLAGS_OUTDIR) version.c
        $(link) $(LINKARGS1) /subsystem:$(SUBSYSTEM) -out:$(VIM).exe $(OBJ) $(XDIFF_OBJ) $(GUI_OBJ) $(CUI_OBJ) $(OLE_OBJ) \
                $(LUA_OBJ) $(MZSCHEME_OBJ) $(PERL_OBJ) $(PYTHON_OBJ) $(PYTHON3_OBJ) $(RUBY_OBJ) \
-               $(TCL_OBJ) $(CSCOPE_OBJ) $(TERM_OBJ) $(NETBEANS_OBJ) $(CHANNEL_OBJ) \
+               $(TCL_OBJ) $(CSCOPE_OBJ) $(TERM_OBJ) $(SOUND_OBJ) $(NETBEANS_OBJ) $(CHANNEL_OBJ) \
                $(XPM_OBJ) $(OUTDIR)\version.obj $(LINKARGS2)
        if exist $(VIM).exe.manifest mt.exe -nologo -manifest $(VIM).exe.manifest -updateresource:$(VIM).exe;1
 
@@ -1766,6 +1783,7 @@ proto.h: \
        proto/usercmd.pro \
        proto/userfunc.pro \
        proto/window.pro \
+       $(SOUND_PRO) \
        $(NETBEANS_PRO) \
        $(CHANNEL_PRO)
 
index e28ccff90a2272a503e63fa60f778f182850e7f9..70f4425e928c1cf63805155090c2c01e7cc4030d 100644 (file)
 
 #include "vim.h"
 
-#if (defined(FEAT_SOUND) && defined(HAVE_CANBERRA)) || defined(PROTO)
-
-#include <canberra.h>
+#if defined(FEAT_SOUND) || defined(PROTO)
 
 static long        sound_id = 0;
-static ca_context   *context = NULL;
 
 typedef struct soundcb_S soundcb_T;
 
 struct soundcb_S {
     callback_T snd_callback;
+#ifdef MSWIN
+    MCIDEVICEID        snd_device_id;
+    long       snd_id;
+#endif
     soundcb_T  *snd_next;
 };
 
@@ -75,6 +76,15 @@ delete_sound_callback(soundcb_T *soundcb)
        }
 }
 
+#if defined(HAVE_CANBERRA) || defined(PROTO)
+
+/*
+ * Sound implementation for Linux/Unix/Mac using libcanberra.
+ */
+# include <canberra.h>
+
+static ca_context   *context = NULL;
+
     static void
 sound_callback(
        ca_context  *c UNUSED,
@@ -188,7 +198,7 @@ f_sound_clear(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
     }
 }
 
-#if defined(EXITFREE) || defined(PROTO)
+# if defined(EXITFREE) || defined(PROTO)
     void
 sound_free(void)
 {
@@ -197,6 +207,178 @@ sound_free(void)
     while (first_callback != NULL)
        delete_sound_callback(first_callback);
 }
-#endif
+# endif
+
+#elif defined(MSWIN)
+
+/*
+ * Sound implementation for MS-Windows.
+ */
+
+static HWND g_hWndSound = NULL;
+
+    static LRESULT CALLBACK
+sound_wndproc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
+{
+    soundcb_T  *p;
+
+    switch (message)
+    {
+       case MM_MCINOTIFY:
+           for (p = first_callback; p != NULL; p = p->snd_next)
+               if (p->snd_device_id == (MCIDEVICEID) lParam)
+               {
+                   typval_T    argv[3];
+                   typval_T    rettv;
+                   int         dummy;
+                   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, NULL, 0L, 0L, &dummy, TRUE, NULL);
+                   clear_tv(&rettv);
+
+                   delete_sound_callback(p);
+                   redraw_after_callback(TRUE);
+
+               }
+           break;
+    }
+
+    return DefWindowProc(hwnd, message, wParam, lParam);
+}
+
+    static HWND
+sound_window()
+{
+    if (g_hWndSound == NULL)
+    {
+       LPCSTR clazz = "VimSound";
+       WNDCLASS wndclass = {
+           0, sound_wndproc, 0, 0, g_hinst, NULL, 0, 0, NULL, clazz };
+       RegisterClass(&wndclass);
+       g_hWndSound = CreateWindow(clazz, NULL, 0, 0, 0, 0, 0,
+               HWND_MESSAGE, NULL, g_hinst, NULL);
+    }
+
+    return g_hWndSound;
+}
+
+    void
+f_sound_playevent(typval_T *argvars, typval_T *rettv)
+{
+    WCHAR          *wp;
+
+    rettv->v_type = VAR_NUMBER;
+    rettv->vval.v_number = 0;
+
+    wp = enc_to_utf16(tv_get_string(&argvars[0]), NULL);
+    if (wp == NULL)
+       return;
+
+    PlaySoundW(wp, NULL, SND_ASYNC | SND_ALIAS);
+    free(wp);
+
+    rettv->vval.v_number = ++sound_id;
+}
+
+    void
+f_sound_playfile(typval_T *argvars, typval_T *rettv)
+{
+    long       newid = sound_id + 1;
+    size_t     len;
+    char_u     *p, *esc;
+    WCHAR      *wp;
+    soundcb_T  *soundcb;
+    char       buf[32];
+    MCIERROR   err;
+
+    rettv->v_type = VAR_NUMBER;
+    rettv->vval.v_number = 0;
+
+    esc = vim_strsave_shellescape(tv_get_string(&argvars[0]), FALSE, FALSE);
+
+    len = STRLEN(esc) + 5 + 18 + 1;
+    p = alloc(len);
+    if (p == NULL)
+    {
+       free(esc);
+       return;
+    }
+    vim_snprintf((char *)p, len, "open %s alias sound%06ld", esc, newid);
+    free(esc);
+
+    wp = enc_to_utf16((char_u *)p, NULL);
+    free(p);
+    if (wp == NULL)
+       return;
+
+    err = mciSendStringW(wp, NULL, 0, sound_window());
+    free(wp);
+    if (err != 0)
+       return;
+
+    vim_snprintf(buf, sizeof(buf), "play sound%06ld notify", newid);
+    err = mciSendString(buf, NULL, 0, sound_window());
+    if (err != 0)
+       goto failure;
+
+    sound_id = newid;
+    rettv->vval.v_number = sound_id;
+
+    soundcb = get_sound_callback(&argvars[1]);
+    if (soundcb != NULL)
+    {
+       vim_snprintf(buf, sizeof(buf), "sound%06ld", newid);
+       soundcb->snd_id = newid;
+       soundcb->snd_device_id = mciGetDeviceID(buf);
+    }
+    return;
+
+failure:
+    vim_snprintf(buf, sizeof(buf), "close sound%06ld", newid);
+    mciSendString(buf, NULL, 0, NULL);
+}
+
+    void
+f_sound_stop(typval_T *argvars, typval_T *rettv UNUSED)
+{
+    long    id = tv_get_number(&argvars[0]);
+    char    buf[32];
+
+    vim_snprintf(buf, sizeof(buf), "stop sound%06ld", id);
+    mciSendString(buf, NULL, 0, NULL);
+}
+
+    void
+f_sound_clear(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
+{
+    PlaySound(NULL, NULL, 0);
+    mciSendString("close all", NULL, 0, NULL);
+}
+
+# if defined(EXITFREE)
+    void
+sound_free(void)
+{
+    CloseWindow(g_hWndSound);
+
+    while (first_callback != NULL)
+       delete_sound_callback(first_callback);
+}
+# endif
+
+#endif // MSWIN
 
-#endif  // FEAT_SOUND && HAVE_CANBERRA
+#endif  // FEAT_SOUND
index 1e2c455e662ae4aec4fb33361b1665f647f3c398..e74aa132da8542b52f01386234f8d8010281f045 100644 (file)
@@ -10,6 +10,10 @@ func PlayCallback(id, result)
 endfunc
 
 func Test_play_event()
+  if has('win32')
+    throw 'Skipped: Playing event with callback is not supported on Windows'
+  endif
+
   let id = sound_playevent('bell', 'PlayCallback')
   if id == 0
     throw 'Skipped: bell event not available'
index 5831e336d7c57ba04a6c42d53ce814a4b94eb433..ee07b9619518495f32c81247f8da8c2ac12716b6 100644 (file)
@@ -777,6 +777,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    1565,
 /**/
     1564,
 /**/