commit 6da7bf2da489d140fa906e527263a8450bf4ddef
Author: Andreas Bierfert <andreas.bierfert(a)lowlatency.de>
Date: Thu May 31 19:05:08 2012 +0200
1.5.5-1
- version upgrade (rhbz#817257)
- split out -filesystem and clean up -common/-core requires
- re-add winepulse driver (rhbz#821207, rhbz#783699)
- add font replacements for CJK to wine.inf and add information for cjk users
to fedora readme (rhbz#815125, rhbz#820096)
- add support for and require wine-mono
.gitignore | 2 +
sources | 4 +-
wine-README-Fedora | 6 +
wine-cjk.patch | 21 +
wine-pulse-1.5.4.patch | 3359 ++++++++++++++++++++++++++++++++++++++++++++++++
wine.spec | 142 ++-
6 files changed, 3480 insertions(+), 54 deletions(-)
---
diff --git a/.gitignore b/.gitignore
index 6cedb9d..c30c581 100644
--- a/.gitignore
+++ b/.gitignore
@@ -10,3 +10,5 @@
/wine-1.5.3.tar.bz2.sign
/wine-1.5.4.tar.bz2
/wine-1.5.4.tar.bz2.sign
+/wine-1.5.5.tar.bz2
+/wine-1.5.5.tar.bz2.sign
diff --git a/sources b/sources
index 082ab70..f4f87a4 100644
--- a/sources
+++ b/sources
@@ -1,2 +1,2 @@
-e7eb0f4b1f16359005fef12e00bd12df wine-1.5.4.tar.bz2
-57ca3f782c9a01380a347fdc0e9feef7 wine-1.5.4.tar.bz2.sign
+63639cfe9addb75cd40726cac36bcf8a wine-1.5.5.tar.bz2
+9274664843d2505ea7a19c1e4b6c5c2d wine-1.5.5.tar.bz2.sign
diff --git a/wine-README-Fedora b/wine-README-Fedora
index f0e5205..225a865 100644
--- a/wine-README-Fedora
+++ b/wine-README-Fedora
@@ -20,3 +20,9 @@ If you find a bug, please take the time to move your ${HOME}/.wine
directory
someplace and try with a fresh version (like mv ${HOME}/.wine{,-save}). If the
bug is still there look trough
https://bugzilla.redhat.com and see if the bug
is already reported. In case it is not please report it.
+
+CJK support:
+============
+Fedora carries a modified wine.inf file with support for cjk through the
+wqy-microhei font family. If you want to use cjk fonts install the
+wqy-microhei-fonts.noarch package.
diff --git a/wine-cjk.patch b/wine-cjk.patch
new file mode 100644
index 0000000..fbdd3d7
--- /dev/null
+++ b/wine-cjk.patch
@@ -0,0 +1,21 @@
+diff -up wine-1.5.5/tools/wine.inf.in.orig wine-1.5.5/tools/wine.inf.in
+--- wine-1.5.5/tools/wine.inf.in.orig 2012-05-28 16:31:47.995343241 +0200
++++ wine-1.5.5/tools/wine.inf.in 2012-05-28 16:55:08.075906186 +0200
+@@ -610,6 +610,17 @@ HKLM,%FontSubStr%,"Times New Roman CE,23
+ HKLM,%FontSubStr%,"Times New Roman CYR,204",,"Times New Roman,204"
+ HKLM,%FontSubStr%,"Times New Roman Greek,161",,"Times New
Roman,161"
+ HKLM,%FontSubStr%,"Times New Roman TUR,162",,"Times New Roman,162"
++HKCU,Software\Wine\Fonts\Replacements,"SimSun",,"WenQuanYi Micro
Hei"
++HKCU,Software\Wine\Fonts\Replacements,"PMingLiU",,"WenQuanYi Micro
Hei"
++HKCU,Software\Wine\Fonts\Replacements,"MS Gothic",,"Ume Gothic"
++HKCU,Software\Wine\Fonts\Replacements,"MS PGothic",,"Ume P Gothic"
++HKCU,Software\Wine\Fonts\Replacements,"MS UI Gothic",,"Ume UI
Gothic"
++HKCU,Software\Wine\Fonts\Replacements,"MS Mincho",,"Ume Mincho"
++HKCU,Software\Wine\Fonts\Replacements,"MS PMincho",,"Ume P Mincho"
++HKCU,Software\Wine\Fonts\Replacements,"Batang",,"UnBatang"
++HKCU,Software\Wine\Fonts\Replacements,"Dotum",,"UnDotum"
++HKCU,Software\Wine\Fonts\Replacements,"Gulim",,"UnDotum"
++HKCU,Software\Wine\Fonts\Replacements,"Arial Unicode MS",,"Droid Sans
Fallback"
+ HKLM,System\CurrentControlSet\Hardware
Profiles\Current\Software\Fonts,"LogPixels",0x10003,0x00000060
+
+ [MCI]
diff --git a/wine-pulse-1.5.4.patch b/wine-pulse-1.5.4.patch
new file mode 100644
index 0000000..54e7f0d
--- /dev/null
+++ b/wine-pulse-1.5.4.patch
@@ -0,0 +1,3359 @@
+diff -uNr wine-1.5.4.old/configure wine-1.5.4/configure
+--- wine-1.5.4.old/configure 2012-05-11 21:08:09.000000000 +0300
++++ wine-1.5.4/configure 2012-05-23 12:37:16.166737017 +0300
+@@ -629,6 +629,8 @@
+ ALSALIBS
+ GSTREAMER_INCL
+ GSTREAMER_LIBS
++PULSEINCL
++PULSELIBS
+ LIBGETTEXTPO
+ ZLIB
+ FREETYPEINCL
+@@ -805,6 +807,7 @@
+ with_oss
+ with_png
+ with_pthread
++with_pulse
+ with_sane
+ with_tiff
+ with_v4l
+@@ -1495,6 +1498,7 @@
+ --without-oss do not use the OSS sound support
+ --without-png do not use PNG
+ --without-pthread do not use the pthread library
++ --without-pulse do not use PulseAudio sound support
+ --without-sane do not use SANE (scanner support)
+ --without-tiff do not use TIFF
+ --without-v4l do not use v4l1 (v4l support)
+@@ -2679,6 +2683,12 @@
+ fi
+
+
++# Check whether --with-pulse was given.
++if test "${with_pulse+set}" = set; then :
++ withval=$with_pulse;
++fi
++
++
+ # Check whether --with-sane was given.
+ if test "${with_sane+set}" = set; then :
+ withval=$with_sane;
+@@ -10639,6 +10649,87 @@
+ fi
+ fi
+
++PULSELIBS=""
++
++PULSEINCL=""
++
++if test "x$with_pulse" != "xno";
++then
++ ac_save_CPPFLAGS="$CPPFLAGS"
++ if test "$PKG_CONFIG" != "false";
++ then
++ ac_pulse_libs="`$PKG_CONFIG --libs libpulse 2>/dev/null`"
++ ac_pulse_cflags="`$PKG_CONFIG --cflags-only-I libpulse
2>/dev/null`"
++
++ CPPFLAGS="$CPPFLAGS $ac_pulse_cflags"
++ for ac_header in pulse/pulseaudio.h
++do :
++ ac_fn_c_check_header_mongrel "$LINENO" "pulse/pulseaudio.h"
"ac_cv_header_pulse_pulseaudio_h" "$ac_includes_default"
++if test "x$ac_cv_header_pulse_pulseaudio_h" = xyes; then :
++ cat >>confdefs.h <<_ACEOF
++#define HAVE_PULSE_PULSEAUDIO_H 1
++_ACEOF
++ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for pa_stream_is_corked in
-lpulse" >&5
++$as_echo_n "checking for pa_stream_is_corked in -lpulse... " >&6; }
++if ${ac_cv_lib_pulse_pa_stream_is_corked+:} false; then :
++ $as_echo_n "(cached) " >&6
++else
++ ac_check_lib_save_LIBS=$LIBS
++LIBS="-lpulse $ac_pulse_libs $LIBS"
++cat confdefs.h - <<_ACEOF >conftest.$ac_ext
++/* end confdefs.h. */
++
++/* Override any GCC internal prototype to avoid an error.
++ Use char because int might match the return type of a GCC
++ builtin and then its argument prototype would still apply. */
++#ifdef __cplusplus
++extern "C"
++#endif
++char pa_stream_is_corked ();
++int
++main ()
++{
++return pa_stream_is_corked ();
++ ;
++ return 0;
++}
++_ACEOF
++if ac_fn_c_try_link "$LINENO"; then :
++ ac_cv_lib_pulse_pa_stream_is_corked=yes
++else
++ ac_cv_lib_pulse_pa_stream_is_corked=no
++fi
++rm -f core conftest.err conftest.$ac_objext \
++ conftest$ac_exeext conftest.$ac_ext
++LIBS=$ac_check_lib_save_LIBS
++fi
++{ $as_echo "$as_me:${as_lineno-$LINENO}: result:
$ac_cv_lib_pulse_pa_stream_is_corked" >&5
++$as_echo "$ac_cv_lib_pulse_pa_stream_is_corked" >&6; }
++if test "x$ac_cv_lib_pulse_pa_stream_is_corked" = xyes; then :
++
++$as_echo "#define HAVE_PULSEAUDIO 1" >>confdefs.h
++
++ PULSELIBS="$ac_pulse_libs"
++ PULSEINCL="$ac_pulse_cflags"
++fi
++
++
++fi
++
++done
++
++ fi
++ CPPFLAGS="$ac_save_CPPFLAGS"
++fi
++if test "$ac_cv_lib_pulse_pa_stream_is_corked" != "yes"; then :
++ case "x$with_pulse" in
++ x) as_fn_append wine_warnings "|libpulse ${notice_platform}development files
not found or too old, Pulse won't be supported." ;;
++ xno) ;;
++ *) as_fn_error $? "libpulse ${notice_platform}development files not found or
too old, Pulse won't be supported.
++This is an error since --with-pulse was requested." "$LINENO" 5 ;;
++esac
++fi
++
+ if test "x$with_gstreamer" != "xno"
+ then
+ ac_save_CPPFLAGS="$CPPFLAGS"
+@@ -11898,12 +11989,13 @@
+
+ test -n "$ALSALIBS" || enable_winealsa_drv=${enable_winealsa_drv:-no}
+ test -n "$COREAUDIO" ||
enable_winecoreaudio_drv=${enable_winecoreaudio_drv:-no}
++test -n "$PULSELIBS" || enable_winepulse_drv=${enable_winepulse_drv:-no}
+ test "x$ac_cv_member_oss_sysinfo_numaudioengines" = xyes ||
enable_wineoss_drv=${enable_wineoss_drv:-no}
+ test "$ac_cv_header_linux_joystick_h" = "yes" ||
enable_winejoystick_drv=${enable_winejoystick_drv:-no}
+
+-if test "x$ALSALIBS$COREAUDIO" = "x" -a \
++if test "x$ALSALIBS$COREAUDIO$PULSELIBS" = "x" -a \
+ "x$ac_cv_member_oss_sysinfo_numaudioengines" != xyes -a \
+- "x$with_alsa$with_coreaudio$with_oss" != xnonono
++ "x$with_alsa$with_coreaudio$with_oss$with_pulse" != xnononono
+ then
+ as_fn_append wine_warnings "|No sound system was found. Windows applications
will be silent."
+ fi
+@@ -15430,6 +15522,7 @@
+ wine_fn_config_dll wineoss.drv enable_wineoss_drv
+ wine_fn_config_dll wineps.drv enable_wineps_drv install-lib,po
+ wine_fn_config_dll wineps16.drv16 enable_win16
++wine_fn_config_dll winepulse.drv enable_winepulse_drv
+ wine_fn_config_dll wineqtdecoder enable_wineqtdecoder
+ wine_fn_config_dll winequartz.drv enable_winequartz_drv
+ wine_fn_config_dll winex11.drv enable_winex11_drv
+diff -uNr wine-1.5.4.old/configure.ac wine-1.5.4/configure.ac
+--- wine-1.5.4.old/configure.ac 2012-05-11 21:08:09.000000000 +0300
++++ wine-1.5.4/configure.ac 2012-05-23 12:38:20.969468598 +0300
+@@ -74,6 +74,7 @@
+ [if test "x$withval" = "xno"; then
ac_cv_header_png_h=no; fi])
+ AC_ARG_WITH(pthread, AS_HELP_STRING([--without-pthread],[do not use the pthread
library]),
+ [if test "x$withval" = "xno"; then
ac_cv_header_pthread_h=no; fi])
++AC_ARG_WITH(pulse, AC_HELP_STRING([--without-pulse],[do not use PulseAudio sound
support]))
+ AC_ARG_WITH(sane, AS_HELP_STRING([--without-sane],[do not use SANE (scanner
support)]))
+ AC_ARG_WITH(tiff, AS_HELP_STRING([--without-tiff],[do not use TIFF]),
+ [if test "x$withval" = "xno"; then
ac_cv_header_tiffio_h=no; fi])
+@@ -1482,6 +1483,30 @@
+ [GetText ${notice_platform}development files not found (or too old), po files
can't be rebuilt.])
+ fi
+
++dnl **** Check for PulseAudio ****
++AC_SUBST(PULSELIBS,"")
++AC_SUBST(PULSEINCL,"")
++if test "x$with_pulse" != "xno";
++then
++ ac_save_CPPFLAGS="$CPPFLAGS"
++ if test "$PKG_CONFIG" != "false";
++ then
++ ac_pulse_libs="`$PKG_CONFIG --libs libpulse 2>/dev/null`"
++ ac_pulse_cflags="`$PKG_CONFIG --cflags-only-I libpulse
2>/dev/null`"
++
++ CPPFLAGS="$CPPFLAGS $ac_pulse_cflags"
++ AC_CHECK_HEADERS(pulse/pulseaudio.h,
++ [AC_CHECK_LIB(pulse, pa_stream_is_corked,
++ [AC_DEFINE(HAVE_PULSEAUDIO, 1, [Define if you have pulseaudio])
++ PULSELIBS="$ac_pulse_libs"
++ PULSEINCL="$ac_pulse_cflags"],,$ac_pulse_libs)
++ ])
++ fi
++ CPPFLAGS="$ac_save_CPPFLAGS"
++fi
++WINE_WARNING_WITH(pulse, [test "$ac_cv_lib_pulse_pa_stream_is_corked" !=
"yes"],
++ [libpulse ${notice_platform}development files not found or too old, Pulse
won't be supported.])
++
+ dnl **** Check for gstreamer ****
+ if test "x$with_gstreamer" != "xno"
+ then
+@@ -1688,13 +1713,14 @@
+ dnl **** Disable unsupported winmm drivers ****
+ test -n "$ALSALIBS" || enable_winealsa_drv=${enable_winealsa_drv:-no}
+ test -n "$COREAUDIO" ||
enable_winecoreaudio_drv=${enable_winecoreaudio_drv:-no}
++test -n "$PULSELIBS" || enable_winepulse_drv=${enable_winepulse_drv:-no}
+ test "x$ac_cv_member_oss_sysinfo_numaudioengines" = xyes ||
enable_wineoss_drv=${enable_wineoss_drv:-no}
+ test "$ac_cv_header_linux_joystick_h" = "yes" ||
enable_winejoystick_drv=${enable_winejoystick_drv:-no}
+
+ dnl **** Check for any sound system ****
+-if test "x$ALSALIBS$COREAUDIO" = "x" -a \
++if test "x$ALSALIBS$COREAUDIO$PULSELIBS" = "x" -a \
+ "x$ac_cv_member_oss_sysinfo_numaudioengines" != xyes -a \
+- "x$with_alsa$with_coreaudio$with_oss" != xnonono
++ "x$with_alsa$with_coreaudio$with_oss$with_pulse" != xnononono
+ then
+ WINE_WARNING([No sound system was found. Windows applications will be silent.])
+ fi
+@@ -2918,6 +2944,7 @@
+ WINE_CONFIG_DLL(wineoss.drv)
+ WINE_CONFIG_DLL(wineps.drv,,[install-lib,po])
+ WINE_CONFIG_DLL(wineps16.drv16,enable_win16)
++WINE_CONFIG_DLL(winepulse.drv)
+ WINE_CONFIG_DLL(wineqtdecoder)
+ WINE_CONFIG_DLL(winequartz.drv)
+ WINE_CONFIG_DLL(winex11.drv)
+diff -uNr wine-1.5.4.old/dlls/mmdevapi/main.c wine-1.5.4/dlls/mmdevapi/main.c
+--- wine-1.5.4.old/dlls/mmdevapi/main.c 2012-05-11 21:08:09.000000000 +0300
++++ wine-1.5.4/dlls/mmdevapi/main.c 2012-05-23 12:13:41.597012820 +0300
+@@ -111,7 +111,7 @@
+ {
+ static const WCHAR drv_value[] =
{'A','u','d','i','o',0};
+
+- static WCHAR default_list[] =
{'a','l','s','a',',','o','s','s',',',
++ static WCHAR default_list[] =
{'p','u','l','s','e',',','a','l','s','a',',','o','s','s',',',
+
'c','o','r','e','a','u','d','i','o',0};
+
+ DriverFuncs driver;
+diff -uNr wine-1.5.4.old/dlls/winepulse.drv/Makefile.in
wine-1.5.4/dlls/winepulse.drv/Makefile.in
+--- wine-1.5.4.old/dlls/winepulse.drv/Makefile.in 1970-01-01 02:00:00.000000000 +0200
++++ wine-1.5.4/dlls/winepulse.drv/Makefile.in 2012-05-23 12:03:21.068645000 +0300
+@@ -0,0 +1,9 @@
++MODULE = winepulse.drv
++IMPORTS = dxguid uuid winmm user32 advapi32 ole32
++EXTRALIBS = @PULSELIBS@ @LIBPTHREAD@
++EXTRAINCL = @PULSEINCL@
++
++C_SRCS = \
++ mmdevdrv.c
++
++@MAKE_DLL_RULES@
+diff -uNr wine-1.5.4.old/dlls/winepulse.drv/mmdevdrv.c
wine-1.5.4/dlls/winepulse.drv/mmdevdrv.c
+--- wine-1.5.4.old/dlls/winepulse.drv/mmdevdrv.c 1970-01-01 02:00:00.000000000 +0200
++++ wine-1.5.4/dlls/winepulse.drv/mmdevdrv.c 2012-05-23 12:03:21.069645000 +0300
+@@ -0,0 +1,3101 @@
++/*
++ * Copyright 2011-2012 Maarten Lankhorst
++ * Copyright 2010-2011 Maarten Lankhorst for CodeWeavers
++ * Copyright 2011 Andrew Eikum for CodeWeavers
++ *
++ * This library is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU Lesser General Public
++ * License as published by the Free Software Foundation; either
++ * version 2.1 of the License, or (at your option) any later version.
++ *
++ * This library is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
++ * Lesser General Public License for more details.
++ *
++ * You should have received a copy of the GNU Lesser General Public
++ * License along with this library; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
++ *
++ * Pulseaudio driver support.. hell froze over
++ */
++
++#define NONAMELESSUNION
++#define COBJMACROS
++#include "config.h"
++#include <poll.h>
++#include <pthread.h>
++
++#include <stdarg.h>
++#include <unistd.h>
++#include <math.h>
++#include <stdio.h>
++
++#include <pulse/pulseaudio.h>
++
++#include "windef.h"
++#include "winbase.h"
++#include "winnls.h"
++#include "winreg.h"
++#include "wine/debug.h"
++#include "wine/unicode.h"
++#include "wine/list.h"
++
++#include "ole2.h"
++#include "dshow.h"
++#include "dsound.h"
++#include "propsys.h"
++
++#include "initguid.h"
++#include "ks.h"
++#include "ksmedia.h"
++#include "mmdeviceapi.h"
++#include "audioclient.h"
++#include "endpointvolume.h"
++#include "audiopolicy.h"
++
++#include "wine/list.h"
++
++#define NULL_PTR_ERR MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32,
RPC_X_NULL_REF_POINTER)
++
++WINE_DEFAULT_DEBUG_CHANNEL(pulse);
++WINE_DECLARE_DEBUG_CHANNEL(winediag);
++
++static const REFERENCE_TIME MinimumPeriod = 30000;
++static const REFERENCE_TIME DefaultPeriod = 100000;
++
++static pa_context *pulse_ctx;
++static pa_mainloop *pulse_ml;
++
++static HANDLE pulse_thread;
++static pthread_mutex_t pulse_lock = PTHREAD_MUTEX_INITIALIZER;
++static pthread_cond_t pulse_cond = PTHREAD_COND_INITIALIZER;
++static struct list g_sessions = LIST_INIT(g_sessions);
++
++/* Mixer format + period times */
++static WAVEFORMATEXTENSIBLE pulse_fmt[2];
++static REFERENCE_TIME pulse_min_period[2], pulse_def_period[2];
++
++static DWORD pulse_stream_volume;
++
++const WCHAR pulse_keyW[] =
{'S','o','f','t','w','a','r','e','\\',
++
'W','i','n','e','\\','P','u','l','s','e',0};
++const WCHAR pulse_streamW[] = {
'S','t','r','e','a','m','V','o','l',0
};
++
++static GUID pulse_render_guid =
++{ 0xfd47d9cc, 0x4218, 0x4135, { 0x9c, 0xe2, 0x0c, 0x19, 0x5c, 0x87, 0x40, 0x5b } };
++static GUID pulse_capture_guid =
++{ 0x25da76d0, 0x033c, 0x4235, { 0x90, 0x02, 0x19, 0xf4, 0x88, 0x94, 0xac, 0x6f } };
++
++static HANDLE warn_once;
++
++BOOL WINAPI DllMain(HINSTANCE dll, DWORD reason, void *reserved)
++{
++ if (reason == DLL_PROCESS_ATTACH) {
++ HKEY key;
++ if (RegOpenKeyW(HKEY_CURRENT_USER, pulse_keyW, &key) == ERROR_SUCCESS) {
++ DWORD size = sizeof(pulse_stream_volume);
++ RegQueryValueExW(key, pulse_streamW, 0, NULL,
++ (BYTE*)&pulse_stream_volume, &size);
++ RegCloseKey(key);
++ }
++ DisableThreadLibraryCalls(dll);
++ } else if (reason == DLL_PROCESS_DETACH) {
++ if (pulse_ctx) {
++ pa_context_disconnect(pulse_ctx);
++ pa_context_unref(pulse_ctx);
++ }
++ if (pulse_ml)
++ pa_mainloop_quit(pulse_ml, 0);
++ if (pulse_thread)
++ CloseHandle(pulse_thread);
++ if (warn_once)
++ CloseHandle(warn_once);
++ }
++ return TRUE;
++}
++
++typedef struct ACImpl ACImpl;
++
++typedef struct _AudioSession {
++ GUID guid;
++ struct list clients;
++
++ IMMDevice *device;
++
++ float master_vol;
++ UINT32 channel_count;
++ float *channel_vols;
++ BOOL mute;
++
++ struct list entry;
++} AudioSession;
++
++typedef struct _AudioSessionWrapper {
++ IAudioSessionControl2 IAudioSessionControl2_iface;
++ IChannelAudioVolume IChannelAudioVolume_iface;
++ ISimpleAudioVolume ISimpleAudioVolume_iface;
++
++ LONG ref;
++
++ ACImpl *client;
++ AudioSession *session;
++} AudioSessionWrapper;
++
++typedef struct _ACPacket {
++ struct list entry;
++ UINT64 qpcpos;
++ BYTE *data;
++ UINT32 discont;
++} ACPacket;
++
++struct ACImpl {
++ IAudioClient IAudioClient_iface;
++ IAudioRenderClient IAudioRenderClient_iface;
++ IAudioCaptureClient IAudioCaptureClient_iface;
++ IAudioClock IAudioClock_iface;
++ IAudioClock2 IAudioClock2_iface;
++ IAudioStreamVolume IAudioStreamVolume_iface;
++ IMMDevice *parent;
++ struct list entry;
++ float vol[PA_CHANNELS_MAX];
++
++ LONG ref;
++ EDataFlow dataflow;
++ DWORD flags;
++ AUDCLNT_SHAREMODE share;
++ HANDLE event;
++
++ UINT32 bufsize_frames, bufsize_bytes, locked, capture_period, pad, started,
peek_ofs;
++ void *locked_ptr, *tmp_buffer;
++
++ pa_stream *stream;
++ pa_sample_spec ss;
++ pa_channel_map map;
++
++ INT64 clock_lastpos, clock_written;
++ pa_usec_t clock_pulse;
++
++ AudioSession *session;
++ AudioSessionWrapper *session_wrapper;
++ struct list packet_free_head;
++ struct list packet_filled_head;
++};
++
++static const WCHAR defaultW[] =
{'P','u','l','s','e','a','u','d','i','o',0};
++
++static const IAudioClientVtbl AudioClient_Vtbl;
++static const IAudioRenderClientVtbl AudioRenderClient_Vtbl;
++static const IAudioCaptureClientVtbl AudioCaptureClient_Vtbl;
++static const IAudioSessionControl2Vtbl AudioSessionControl2_Vtbl;
++static const ISimpleAudioVolumeVtbl SimpleAudioVolume_Vtbl;
++static const IChannelAudioVolumeVtbl ChannelAudioVolume_Vtbl;
++static const IAudioClockVtbl AudioClock_Vtbl;
++static const IAudioClock2Vtbl AudioClock2_Vtbl;
++static const IAudioStreamVolumeVtbl AudioStreamVolume_Vtbl;
++
++static AudioSessionWrapper *AudioSessionWrapper_Create(ACImpl *client);
++
++static inline ACImpl *impl_from_IAudioClient(IAudioClient *iface)
++{
++ return CONTAINING_RECORD(iface, ACImpl, IAudioClient_iface);
++}
++
++static inline ACImpl *impl_from_IAudioRenderClient(IAudioRenderClient *iface)
++{
++ return CONTAINING_RECORD(iface, ACImpl, IAudioRenderClient_iface);
++}
++
++static inline ACImpl *impl_from_IAudioCaptureClient(IAudioCaptureClient *iface)
++{
++ return CONTAINING_RECORD(iface, ACImpl, IAudioCaptureClient_iface);
++}
++
++static inline AudioSessionWrapper *impl_from_IAudioSessionControl2(IAudioSessionControl2
*iface)
++{
++ return CONTAINING_RECORD(iface, AudioSessionWrapper, IAudioSessionControl2_iface);
++}
++
++static inline AudioSessionWrapper *impl_from_ISimpleAudioVolume(ISimpleAudioVolume
*iface)
++{
++ return CONTAINING_RECORD(iface, AudioSessionWrapper, ISimpleAudioVolume_iface);
++}
++
++static inline AudioSessionWrapper *impl_from_IChannelAudioVolume(IChannelAudioVolume
*iface)
++{
++ return CONTAINING_RECORD(iface, AudioSessionWrapper, IChannelAudioVolume_iface);
++}
++
++static inline ACImpl *impl_from_IAudioClock(IAudioClock *iface)
++{
++ return CONTAINING_RECORD(iface, ACImpl, IAudioClock_iface);
++}
++
++static inline ACImpl *impl_from_IAudioClock2(IAudioClock2 *iface)
++{
++ return CONTAINING_RECORD(iface, ACImpl, IAudioClock2_iface);
++}
++
++static inline ACImpl *impl_from_IAudioStreamVolume(IAudioStreamVolume *iface)
++{
++ return CONTAINING_RECORD(iface, ACImpl, IAudioStreamVolume_iface);
++}
++
++/* Following pulseaudio design here, mainloop has the lock taken whenever
++ * it is handling something for pulse, and the lock is required whenever
++ * doing any pa_* call that can affect the state in any way
++ *
++ * pa_cond_wait is used when waiting on results, because the mainloop needs
++ * the same lock taken to affect the state
++ *
++ * This is basically the same as the pa_threaded_mainloop implementation,
++ * but that cannot be used because it uses pthread_create directly
++ *
++ * pa_threaded_mainloop_(un)lock -> pthread_mutex_(un)lock
++ * pa_threaded_mainloop_signal -> pthread_cond_signal
++ * pa_threaded_mainloop_wait -> pthread_cond_wait
++ */
++
++static int pulse_poll_func(struct pollfd *ufds, unsigned long nfds, int timeout, void
*userdata) {
++ int r;
++ pthread_mutex_unlock(&pulse_lock);
++ r = poll(ufds, nfds, timeout);
++ pthread_mutex_lock(&pulse_lock);
++ return r;
++}
++
++static DWORD CALLBACK pulse_mainloop_thread(void *tmp) {
++ int ret;
++ pulse_ml = pa_mainloop_new();
++ pa_mainloop_set_poll_func(pulse_ml, pulse_poll_func, NULL);
++ pthread_mutex_lock(&pulse_lock);
++ pthread_cond_signal(&pulse_cond);
++ pa_mainloop_run(pulse_ml, &ret);
++ pthread_mutex_unlock(&pulse_lock);
++ pa_mainloop_free(pulse_ml);
++ CloseHandle(pulse_thread);
++ return ret;
++}
++
++static void pulse_contextcallback(pa_context *c, void *userdata);
++static void pulse_stream_state(pa_stream *s, void *user);
++
++static const enum pa_channel_position pulse_pos_from_wfx[] = {
++ PA_CHANNEL_POSITION_FRONT_LEFT,
++ PA_CHANNEL_POSITION_FRONT_RIGHT,
++ PA_CHANNEL_POSITION_FRONT_CENTER,
++ PA_CHANNEL_POSITION_LFE,
++ PA_CHANNEL_POSITION_REAR_LEFT,
++ PA_CHANNEL_POSITION_REAR_RIGHT,
++ PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER,
++ PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER,
++ PA_CHANNEL_POSITION_REAR_CENTER,
++ PA_CHANNEL_POSITION_SIDE_LEFT,
++ PA_CHANNEL_POSITION_SIDE_RIGHT,
++ PA_CHANNEL_POSITION_TOP_CENTER,
++ PA_CHANNEL_POSITION_TOP_FRONT_LEFT,
++ PA_CHANNEL_POSITION_TOP_FRONT_CENTER,
++ PA_CHANNEL_POSITION_TOP_FRONT_RIGHT,
++ PA_CHANNEL_POSITION_TOP_REAR_LEFT,
++ PA_CHANNEL_POSITION_TOP_REAR_CENTER,
++ PA_CHANNEL_POSITION_TOP_REAR_RIGHT
++};
++
++static void pulse_probe_settings(int render, WAVEFORMATEXTENSIBLE *fmt) {
++ WAVEFORMATEX *wfx = &fmt->Format;
++ pa_stream *stream;
++ pa_channel_map map;
++ pa_sample_spec ss;
++ pa_buffer_attr attr;
++ int ret, i;
++ unsigned int length = 0;
++
++ pa_channel_map_init_auto(&map, 2, PA_CHANNEL_MAP_ALSA);
++ ss.rate = 48000;
++ ss.format = PA_SAMPLE_FLOAT32LE;
++ ss.channels = map.channels;
++
++ attr.maxlength = -1;
++ attr.tlength = -1;
++ attr.minreq = attr.fragsize = pa_frame_size(&ss);
++ attr.prebuf = 0;
++
++ stream = pa_stream_new(pulse_ctx, "format test stream", &ss,
&map);
++ if (stream)
++ pa_stream_set_state_callback(stream, pulse_stream_state, NULL);
++ if (!stream)
++ ret = -1;
++ else if (render)
++ ret = pa_stream_connect_playback(stream, NULL, &attr,
++
PA_STREAM_START_CORKED|PA_STREAM_FIX_RATE|PA_STREAM_FIX_CHANNELS|PA_STREAM_EARLY_REQUESTS,
NULL, NULL);
++ else
++ ret = pa_stream_connect_record(stream, NULL, &attr,
PA_STREAM_START_CORKED|PA_STREAM_FIX_RATE|PA_STREAM_FIX_CHANNELS|PA_STREAM_EARLY_REQUESTS);
++ if (ret >= 0) {
++ while (pa_stream_get_state(stream) == PA_STREAM_CREATING)
++ pthread_cond_wait(&pulse_cond, &pulse_lock);
++ if (pa_stream_get_state(stream) == PA_STREAM_READY) {
++ ss = *pa_stream_get_sample_spec(stream);
++ map = *pa_stream_get_channel_map(stream);
++ if (render)
++ length = pa_stream_get_buffer_attr(stream)->minreq;
++ else
++ length = pa_stream_get_buffer_attr(stream)->fragsize;
++ pa_stream_disconnect(stream);
++ while (pa_stream_get_state(stream) == PA_STREAM_READY)
++ pthread_cond_wait(&pulse_cond, &pulse_lock);
++ }
++ }
++ if (stream)
++ pa_stream_unref(stream);
++ if (length)
++ pulse_def_period[!render] = pulse_min_period[!render] = pa_bytes_to_usec(10 *
length, &ss);
++ else
++ pulse_min_period[!render] = MinimumPeriod;
++ if (pulse_def_period[!render] <= DefaultPeriod)
++ pulse_def_period[!render] = DefaultPeriod;
++
++ wfx->wFormatTag = WAVE_FORMAT_EXTENSIBLE;
++ wfx->cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
++ wfx->nChannels = ss.channels;
++ wfx->wBitsPerSample = 8 * pa_sample_size_of_format(ss.format);
++ wfx->nSamplesPerSec = ss.rate;
++ wfx->nBlockAlign = pa_frame_size(&ss);
++ wfx->nAvgBytesPerSec = wfx->nSamplesPerSec * wfx->nBlockAlign;
++ if (ss.format != PA_SAMPLE_S24_32LE)
++ fmt->Samples.wValidBitsPerSample = wfx->wBitsPerSample;
++ else
++ fmt->Samples.wValidBitsPerSample = 24;
++ if (ss.format == PA_SAMPLE_FLOAT32LE)
++ fmt->SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
++ else
++ fmt->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
++
++ fmt->dwChannelMask = 0;
++ for (i = 0; i < map.channels; ++i)
++ switch (map.map[i]) {
++ default: FIXME("Unhandled channel %s\n",
pa_channel_position_to_string(map.map[i])); break;
++ case PA_CHANNEL_POSITION_FRONT_LEFT: fmt->dwChannelMask |=
SPEAKER_FRONT_LEFT; break;
++ case PA_CHANNEL_POSITION_MONO:
++ case PA_CHANNEL_POSITION_FRONT_CENTER: fmt->dwChannelMask |=
SPEAKER_FRONT_CENTER; break;
++ case PA_CHANNEL_POSITION_FRONT_RIGHT: fmt->dwChannelMask |=
SPEAKER_FRONT_RIGHT; break;
++ case PA_CHANNEL_POSITION_REAR_LEFT: fmt->dwChannelMask |=
SPEAKER_BACK_LEFT; break;
++ case PA_CHANNEL_POSITION_REAR_CENTER: fmt->dwChannelMask |=
SPEAKER_BACK_CENTER; break;
++ case PA_CHANNEL_POSITION_REAR_RIGHT: fmt->dwChannelMask |=
SPEAKER_BACK_RIGHT; break;
++ case PA_CHANNEL_POSITION_LFE: fmt->dwChannelMask |=
SPEAKER_LOW_FREQUENCY; break;
++ case PA_CHANNEL_POSITION_SIDE_LEFT: fmt->dwChannelMask |=
SPEAKER_SIDE_LEFT; break;
++ case PA_CHANNEL_POSITION_SIDE_RIGHT: fmt->dwChannelMask |=
SPEAKER_SIDE_RIGHT; break;
++ case PA_CHANNEL_POSITION_TOP_CENTER: fmt->dwChannelMask |=
SPEAKER_TOP_CENTER; break;
++ case PA_CHANNEL_POSITION_TOP_FRONT_LEFT: fmt->dwChannelMask |=
SPEAKER_TOP_FRONT_LEFT; break;
++ case PA_CHANNEL_POSITION_TOP_FRONT_CENTER: fmt->dwChannelMask |=
SPEAKER_TOP_FRONT_CENTER; break;
++ case PA_CHANNEL_POSITION_TOP_FRONT_RIGHT: fmt->dwChannelMask |=
SPEAKER_TOP_FRONT_RIGHT; break;
++ case PA_CHANNEL_POSITION_TOP_REAR_LEFT: fmt->dwChannelMask |=
SPEAKER_TOP_BACK_LEFT; break;
++ case PA_CHANNEL_POSITION_TOP_REAR_CENTER: fmt->dwChannelMask |=
SPEAKER_TOP_BACK_CENTER; break;
++ case PA_CHANNEL_POSITION_TOP_REAR_RIGHT: fmt->dwChannelMask |=
SPEAKER_TOP_BACK_RIGHT; break;
++ }
++}
++
++static HRESULT pulse_connect(void)
++{
++ int len;
++ WCHAR path[PATH_MAX], *name;
++ char *str;
++
++ if (!pulse_thread)
++ {
++ if (!(pulse_thread = CreateThread(NULL, 0, pulse_mainloop_thread, NULL, 0,
NULL)))
++ {
++ ERR("Failed to create mainloop thread.");
++ return E_FAIL;
++ }
++ SetThreadPriority(pulse_thread, THREAD_PRIORITY_TIME_CRITICAL);
++ pthread_cond_wait(&pulse_cond, &pulse_lock);
++ }
++
++ if (pulse_ctx && PA_CONTEXT_IS_GOOD(pa_context_get_state(pulse_ctx)))
++ return S_OK;
++ if (pulse_ctx)
++ pa_context_unref(pulse_ctx);
++
++ GetModuleFileNameW(NULL, path, sizeof(path)/sizeof(*path));
++ name = strrchrW(path, '\\');
++ if (!name)
++ name = path;
++ else
++ name++;
++ len = WideCharToMultiByte(CP_UNIXCP, 0, name, -1, NULL, 0, NULL, NULL);
++ str = pa_xmalloc(len);
++ WideCharToMultiByte(CP_UNIXCP, 0, name, -1, str, len, NULL, NULL);
++ TRACE("Name: %s\n", str);
++ pulse_ctx = pa_context_new(pa_mainloop_get_api(pulse_ml), str);
++ pa_xfree(str);
++ if (!pulse_ctx) {
++ ERR("Failed to create context\n");
++ return E_FAIL;
++ }
++
++ pa_context_set_state_callback(pulse_ctx, pulse_contextcallback, NULL);
++
++ TRACE("libpulse protocol version: %u. API Version %u\n",
pa_context_get_protocol_version(pulse_ctx), PA_API_VERSION);
++ if (pa_context_connect(pulse_ctx, NULL, 0, NULL) < 0)
++ goto fail;
++
++ /* Wait for connection */
++ while (pthread_cond_wait(&pulse_cond, &pulse_lock)) {
++ pa_context_state_t state = pa_context_get_state(pulse_ctx);
++
++ if (state == PA_CONTEXT_FAILED || state == PA_CONTEXT_TERMINATED)
++ goto fail;
++
++ if (state == PA_CONTEXT_READY)
++ break;
++ }
++
++ TRACE("Connected to server %s with protocol version: %i.\n",
++ pa_context_get_server(pulse_ctx),
++ pa_context_get_server_protocol_version(pulse_ctx));
++ pulse_probe_settings(1, &pulse_fmt[0]);
++ pulse_probe_settings(0, &pulse_fmt[1]);
++ return S_OK;
++
++fail:
++ pa_context_unref(pulse_ctx);
++ pulse_ctx = NULL;
++ return E_FAIL;
++}
++
++static void pulse_contextcallback(pa_context *c, void *userdata) {
++ switch (pa_context_get_state(c)) {
++ default:
++ FIXME("Unhandled state: %i\n", pa_context_get_state(c));
++ case PA_CONTEXT_CONNECTING:
++ case PA_CONTEXT_UNCONNECTED:
++ case PA_CONTEXT_AUTHORIZING:
++ case PA_CONTEXT_SETTING_NAME:
++ case PA_CONTEXT_TERMINATED:
++ TRACE("State change to %i\n", pa_context_get_state(c));
++ return;
++
++ case PA_CONTEXT_READY:
++ TRACE("Ready\n");
++ break;
++
++ case PA_CONTEXT_FAILED:
++ ERR("Context failed: %s\n", pa_strerror(pa_context_errno(c)));
++ break;
++ }
++ pthread_cond_signal(&pulse_cond);
++}
++
++static HRESULT pulse_stream_valid(ACImpl *This) {
++ if (!This->stream)
++ return AUDCLNT_E_NOT_INITIALIZED;
++ if (!This->stream || pa_stream_get_state(This->stream) != PA_STREAM_READY)
++ return AUDCLNT_E_DEVICE_INVALIDATED;
++ return S_OK;
++}
++
++static void dump_attr(const pa_buffer_attr *attr) {
++ TRACE("maxlength: %u\n", attr->maxlength);
++ TRACE("minreq: %u\n", attr->minreq);
++ TRACE("fragsize: %u\n", attr->fragsize);
++ TRACE("tlength: %u\n", attr->tlength);
++ TRACE("prebuf: %u\n", attr->prebuf);
++}
++
++static void pulse_op_cb(pa_stream *s, int success, void *user) {
++ TRACE("Success: %i\n", success);
++ *(int*)user = success;
++ pthread_cond_signal(&pulse_cond);
++}
++
++static void pulse_ctx_op_cb(pa_context *c, int success, void *user) {
++ TRACE("Success: %i\n", success);
++ *(int*)user = success;
++ pthread_cond_signal(&pulse_cond);
++}
++
++static void pulse_attr_update(pa_stream *s, void *user) {
++ const pa_buffer_attr *attr = pa_stream_get_buffer_attr(s);
++ TRACE("New attributes or device moved:\n");
++ dump_attr(attr);
++}
++
++static void pulse_wr_callback(pa_stream *s, size_t bytes, void *userdata)
++{
++ ACImpl *This = userdata;
++ pa_usec_t time;
++ UINT32 oldpad = This->pad;
++
++ if (bytes < This->bufsize_bytes)
++ This->pad = This->bufsize_bytes - bytes;
++ else
++ This->pad = 0;
++
++ assert(oldpad >= This->pad);
++
++ if (0 && This->pad && pa_stream_get_time(This->stream,
&time) >= 0)
++ This->clock_pulse = time;
++ else
++ This->clock_pulse = PA_USEC_INVALID;
++
++ This->clock_written += oldpad - This->pad;
++ TRACE("New pad: %u (-%u)\n", This->pad /
pa_frame_size(&This->ss), (oldpad - This->pad) /
pa_frame_size(&This->ss));
++
++ if (This->event)
++ SetEvent(This->event);
++}
++
++static void pulse_underflow_callback(pa_stream *s, void *userdata)
++{
++ ACImpl *This = userdata;
++ This->clock_pulse = PA_USEC_INVALID;
++ WARN("Underflow\n");
++}
++
++/* Latency is periodically updated even when nothing is played,
++ * because of PA_STREAM_AUTO_TIMING_UPDATE so use it as timer
++ *
++ * Perfect for passing all tests :)
++ */
++static void pulse_latency_callback(pa_stream *s, void *userdata)
++{
++ ACImpl *This = userdata;
++ if (!This->pad && This->event)
++ SetEvent(This->event);
++}
++
++static void pulse_started_callback(pa_stream *s, void *userdata)
++{
++ ACImpl *This = userdata;
++ pa_usec_t time;
++
++ TRACE("(Re)started playing\n");
++ assert(This->clock_pulse == PA_USEC_INVALID);
++ if (0 && pa_stream_get_time(This->stream, &time) >= 0)
++ This->clock_pulse = time;
++ if (This->event)
++ SetEvent(This->event);
++}
++
++static void pulse_rd_loop(ACImpl *This, size_t bytes)
++{
++ while (bytes >= This->capture_period) {
++ ACPacket *p, *next;
++ LARGE_INTEGER stamp, freq;
++ BYTE *dst, *src;
++ UINT32 src_len, copy, rem = This->capture_period;
++ if (!(p = (ACPacket*)list_head(&This->packet_free_head))) {
++ p = (ACPacket*)list_head(&This->packet_filled_head);
++ if (!p->discont) {
++ next = (ACPacket*)p->entry.next;
++ next->discont = 1;
++ } else
++ p = (ACPacket*)list_tail(&This->packet_filled_head);
++ assert(This->pad == This->bufsize_bytes);
++ } else {
++ assert(This->pad < This->bufsize_bytes);
++ This->pad += This->capture_period;
++ assert(This->pad <= This->bufsize_bytes);
++ }
++ QueryPerformanceCounter(&stamp);
++ QueryPerformanceFrequency(&freq);
++ p->qpcpos = (stamp.QuadPart * (INT64)10000000) / freq.QuadPart;
++ p->discont = 0;
++ list_remove(&p->entry);
++ list_add_tail(&This->packet_filled_head, &p->entry);
++
++ dst = p->data;
++ while (rem) {
++ pa_stream_peek(This->stream, (const void**)&src, &src_len);
++ assert(src_len);
++ assert(This->peek_ofs < src_len);
++ src += This->peek_ofs;
++ src_len -= This->peek_ofs;
++ assert(src_len <= bytes);
++
++ copy = rem;
++ if (copy > src_len)
++ copy = src_len;
++ memcpy(dst, src, rem);
++ src += copy;
++ src_len -= copy;
++ dst += copy;
++ rem -= copy;
++
++ if (!src_len) {
++ This->peek_ofs = 0;
++ pa_stream_drop(This->stream);
++ } else
++ This->peek_ofs += copy;
++ }
++ bytes -= This->capture_period;
++ }
++}
++
++static void pulse_rd_drop(ACImpl *This, size_t bytes)
++{
++ while (bytes >= This->capture_period) {
++ UINT32 src_len, copy, rem = This->capture_period;
++ while (rem) {
++ const void *src;
++ pa_stream_peek(This->stream, &src, &src_len);
++ assert(src_len);
++ assert(This->peek_ofs < src_len);
++ src_len -= This->peek_ofs;
++ assert(src_len <= bytes);
++
++ copy = rem;
++ if (copy > src_len)
++ copy = src_len;
++
++ src_len -= copy;
++ rem -= copy;
++
++ if (!src_len) {
++ This->peek_ofs = 0;
++ pa_stream_drop(This->stream);
++ } else
++ This->peek_ofs += copy;
++ bytes -= copy;
++ }
++ }
++}
++
++static void pulse_rd_callback(pa_stream *s, size_t bytes, void *userdata)
++{
++ ACImpl *This = userdata;
++
++ TRACE("Readable total: %u, fragsize: %u\n", bytes,
pa_stream_get_buffer_attr(s)->fragsize);
++ assert(bytes >= This->peek_ofs);
++ bytes -= This->peek_ofs;
++ if (bytes < This->capture_period)
++ return;
++
++ if (This->started)
++ pulse_rd_loop(This, bytes);
++ else
++ pulse_rd_drop(This, bytes);
++
++ if (This->event)
++ SetEvent(This->event);
++}
++
++static void pulse_stream_state(pa_stream *s, void *user)
++{
++ pa_stream_state_t state = pa_stream_get_state(s);
++ TRACE("Stream state changed to %i\n", state);
++ pthread_cond_signal(&pulse_cond);
++}
++
++static HRESULT pulse_stream_connect(ACImpl *This, UINT32 period_bytes) {
++ int ret;
++ char buffer[64];
++ static LONG number;
++ pa_buffer_attr attr;
++ if (This->stream) {
++ pa_stream_disconnect(This->stream);
++ while (pa_stream_get_state(This->stream) == PA_STREAM_READY)
++ pthread_cond_wait(&pulse_cond, &pulse_lock);
++ pa_stream_unref(This->stream);
++ }
++ ret = InterlockedIncrement(&number);
++ sprintf(buffer, "audio stream #%i", ret);
++ This->stream = pa_stream_new(pulse_ctx, buffer, &This->ss,
&This->map);
++ pa_stream_set_state_callback(This->stream, pulse_stream_state, This);
++ pa_stream_set_buffer_attr_callback(This->stream, pulse_attr_update, This);
++ pa_stream_set_moved_callback(This->stream, pulse_attr_update, This);
++
++ /* Pulseaudio will fill in correct values */
++ attr.minreq = attr.fragsize = period_bytes;
++ attr.maxlength = attr.tlength = This->bufsize_bytes;
++ attr.prebuf = pa_frame_size(&This->ss);
++ dump_attr(&attr);
++ if (This->dataflow == eRender)
++ ret = pa_stream_connect_playback(This->stream, NULL, &attr,
++
PA_STREAM_START_CORKED|PA_STREAM_START_UNMUTED|PA_STREAM_AUTO_TIMING_UPDATE|PA_STREAM_INTERPOLATE_TIMING|PA_STREAM_EARLY_REQUESTS,
NULL, NULL);
++ else
++ ret = pa_stream_connect_record(This->stream, NULL, &attr,
++
PA_STREAM_START_CORKED|PA_STREAM_START_UNMUTED|PA_STREAM_AUTO_TIMING_UPDATE|PA_STREAM_INTERPOLATE_TIMING|PA_STREAM_EARLY_REQUESTS);
++ if (ret < 0) {
++ WARN("Returns %i\n", ret);
++ return AUDCLNT_E_ENDPOINT_CREATE_FAILED;
++ }
++ while (pa_stream_get_state(This->stream) == PA_STREAM_CREATING)
++ pthread_cond_wait(&pulse_cond, &pulse_lock);
++ if (pa_stream_get_state(This->stream) != PA_STREAM_READY)
++ return AUDCLNT_E_ENDPOINT_CREATE_FAILED;
++
++ if (This->dataflow == eRender) {
++ pa_stream_set_write_callback(This->stream, pulse_wr_callback, This);
++ pa_stream_set_underflow_callback(This->stream, pulse_underflow_callback,
This);
++ pa_stream_set_started_callback(This->stream, pulse_started_callback, This);
++ } else
++ pa_stream_set_read_callback(This->stream, pulse_rd_callback, This);
++ return S_OK;
++}
++
++HRESULT WINAPI AUDDRV_GetEndpointIDs(EDataFlow flow, const WCHAR ***ids, GUID **keys,
++ UINT *num, UINT *def_index)
++{
++ HRESULT hr = S_OK;
++ TRACE("%d %p %p %p\n", flow, ids, num, def_index);
++
++ pthread_mutex_lock(&pulse_lock);
++ hr = pulse_connect();
++ pthread_mutex_unlock(&pulse_lock);
++ if (FAILED(hr))
++ return hr;
++ *num = 1;
++ *def_index = 0;
++
++ *ids = HeapAlloc(GetProcessHeap(), 0, sizeof(**ids));
++ if (!*ids)
++ return E_OUTOFMEMORY;
++ (*ids)[0] = defaultW;
++
++ *keys = HeapAlloc(GetProcessHeap(), 0, sizeof(**keys));
++ if (!*keys) {
++ HeapFree(GetProcessHeap(), 0, *ids);
++ *ids = NULL;
++ return E_OUTOFMEMORY;
++ }
++ if (flow == eRender)
++ (*keys)[0] = pulse_render_guid;
++ else
++ (*keys)[0] = pulse_capture_guid;
++
++ return S_OK;
++}
++
++int WINAPI AUDDRV_GetPriority(void)
++{
++ HRESULT hr;
++ if (getenv("WINENOPULSE")) {
++ FIXME_(winediag)("winepulse has been temporarily disabled through the
environment\n");
++ return 0;
++ }
++ pthread_mutex_lock(&pulse_lock);
++ hr = pulse_connect();
++ pthread_mutex_unlock(&pulse_lock);
++ return SUCCEEDED(hr) ? 3 : 0;
++}
++
++HRESULT WINAPI AUDDRV_GetAudioEndpoint(GUID *guid, IMMDevice *dev, IAudioClient **out)
++{
++ HRESULT hr;
++ ACImpl *This;
++ int i;
++ EDataFlow dataflow;
++
++ /* Give one visible warning per session
++ * Sadly wine has chosen not to accept the winepulse patch, so support ourselves
++ */
++ if (!warn_once && (warn_once = CreateEventA(0, 0, 0,
"__winepulse_warn_event")) && GetLastError() != ERROR_ALREADY_EXISTS) {
++ FIXME_(winediag)("Winepulse is not officially supported by the wine
project\n");
++ FIXME_(winediag)("For sound related feedback and support, please visit
http://ubuntuforums.org/showthread.php?t=1960599\n");
++ } else {
++ WARN_(winediag)("Winepulse is not officially supported by the wine
project\n");
++ WARN_(winediag)("For sound related feedback and support, please visit
http://ubuntuforums.org/showthread.php?t=1960599\n");
++ }
++
++ TRACE("%s %p %p\n", debugstr_guid(guid), dev, out);
++ if (IsEqualGUID(guid, &pulse_render_guid))
++ dataflow = eRender;
++ else if (IsEqualGUID(guid, &pulse_capture_guid))
++ dataflow = eCapture;
++ else
++ return E_UNEXPECTED;
++
++ *out = NULL;
++ pthread_mutex_lock(&pulse_lock);
++ hr = pulse_connect();
++ pthread_mutex_unlock(&pulse_lock);
++ if (FAILED(hr))
++ return hr;
++
++ This = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*This));
++ if (!This)
++ return E_OUTOFMEMORY;
++
++ This->IAudioClient_iface.lpVtbl = &AudioClient_Vtbl;
++ This->IAudioRenderClient_iface.lpVtbl = &AudioRenderClient_Vtbl;
++ This->IAudioCaptureClient_iface.lpVtbl = &AudioCaptureClient_Vtbl;
++ This->IAudioClock_iface.lpVtbl = &AudioClock_Vtbl;
++ This->IAudioClock2_iface.lpVtbl = &AudioClock2_Vtbl;
++ This->IAudioStreamVolume_iface.lpVtbl = &AudioStreamVolume_Vtbl;
++ This->dataflow = dataflow;
++ This->parent = dev;
++ This->clock_pulse = PA_USEC_INVALID;
++ for (i = 0; i < PA_CHANNELS_MAX; ++i)
++ This->vol[i] = 1.f;
++ IMMDevice_AddRef(This->parent);
++
++ *out = &This->IAudioClient_iface;
++ IAudioClient_AddRef(&This->IAudioClient_iface);
++
++ return S_OK;
++}
++
++static HRESULT WINAPI AudioClient_QueryInterface(IAudioClient *iface,
++ REFIID riid, void **ppv)
++{
++ TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
++
++ if (!ppv)
++ return E_POINTER;
++ *ppv = NULL;
++ if (IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IAudioClient))
++ *ppv = iface;
++ if (*ppv) {
++ IUnknown_AddRef((IUnknown*)*ppv);
++ return S_OK;
++ }
++ WARN("Unknown interface %s\n", debugstr_guid(riid));
++ return E_NOINTERFACE;
++}
++
++static ULONG WINAPI AudioClient_AddRef(IAudioClient *iface)
++{
++ ACImpl *This = impl_from_IAudioClient(iface);
++ ULONG ref;
++ ref = InterlockedIncrement(&This->ref);
++ TRACE("(%p) Refcount now %u\n", This, ref);
++ return ref;
++}
++
++static ULONG WINAPI AudioClient_Release(IAudioClient *iface)
++{
++ ACImpl *This = impl_from_IAudioClient(iface);
++ ULONG ref;
++ ref = InterlockedDecrement(&This->ref);
++ TRACE("(%p) Refcount now %u\n", This, ref);
++ if (!ref) {
++ if (This->stream) {
++ pthread_mutex_lock(&pulse_lock);
++ if (PA_STREAM_IS_GOOD(pa_stream_get_state(This->stream))) {
++ pa_stream_disconnect(This->stream);
++ while (PA_STREAM_IS_GOOD(pa_stream_get_state(This->stream)))
++ pthread_cond_wait(&pulse_cond, &pulse_lock);
++ }
++ pa_stream_unref(This->stream);
++ This->stream = NULL;
++ list_remove(&This->entry);
++ pthread_mutex_unlock(&pulse_lock);
++ }
++ IMMDevice_Release(This->parent);
++ HeapFree(GetProcessHeap(), 0, This->tmp_buffer);
++ HeapFree(GetProcessHeap(), 0, This);
++ }
++ return ref;
++}
++
++static void dump_fmt(const WAVEFORMATEX *fmt)
++{
++ TRACE("wFormatTag: 0x%x (", fmt->wFormatTag);
++ switch(fmt->wFormatTag) {
++ case WAVE_FORMAT_PCM:
++ TRACE("WAVE_FORMAT_PCM");
++ break;
++ case WAVE_FORMAT_IEEE_FLOAT:
++ TRACE("WAVE_FORMAT_IEEE_FLOAT");
++ break;
++ case WAVE_FORMAT_EXTENSIBLE:
++ TRACE("WAVE_FORMAT_EXTENSIBLE");
++ break;
++ default:
++ TRACE("Unknown");
++ break;
++ }
++ TRACE(")\n");
++
++ TRACE("nChannels: %u\n", fmt->nChannels);
++ TRACE("nSamplesPerSec: %u\n", fmt->nSamplesPerSec);
++ TRACE("nAvgBytesPerSec: %u\n", fmt->nAvgBytesPerSec);
++ TRACE("nBlockAlign: %u\n", fmt->nBlockAlign);
++ TRACE("wBitsPerSample: %u\n", fmt->wBitsPerSample);
++ TRACE("cbSize: %u\n", fmt->cbSize);
++
++ if (fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE) {
++ WAVEFORMATEXTENSIBLE *fmtex = (void*)fmt;
++ TRACE("dwChannelMask: %08x\n", fmtex->dwChannelMask);
++ TRACE("Samples: %04x\n", fmtex->Samples.wReserved);
++ TRACE("SubFormat: %s\n", wine_dbgstr_guid(&fmtex->SubFormat));
++ }
++}
++
++static WAVEFORMATEX *clone_format(const WAVEFORMATEX *fmt)
++{
++ WAVEFORMATEX *ret;
++ size_t size;
++
++ if (fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
++ size = sizeof(WAVEFORMATEXTENSIBLE);
++ else
++ size = sizeof(WAVEFORMATEX);
++
++ ret = CoTaskMemAlloc(size);
++ if (!ret)
++ return NULL;
++
++ memcpy(ret, fmt, size);
++
++ ret->cbSize = size - sizeof(WAVEFORMATEX);
++
++ return ret;
++}
++
++static DWORD get_channel_mask(unsigned int channels)
++{
++ switch(channels) {
++ case 0:
++ return 0;
++ case 1:
++ return SPEAKER_FRONT_CENTER;
++ case 2:
++ return SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT;
++ case 3:
++ return SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT |
++ SPEAKER_LOW_FREQUENCY;
++ case 4:
++ return SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_BACK_LEFT |
++ SPEAKER_BACK_RIGHT;
++ case 5:
++ return SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_BACK_LEFT |
++ SPEAKER_BACK_RIGHT | SPEAKER_LOW_FREQUENCY;
++ case 6:
++ return SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_BACK_LEFT |
++ SPEAKER_BACK_RIGHT | SPEAKER_LOW_FREQUENCY | SPEAKER_FRONT_CENTER;
++ case 7:
++ return SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_BACK_LEFT |
++ SPEAKER_BACK_RIGHT | SPEAKER_LOW_FREQUENCY | SPEAKER_FRONT_CENTER |
++ SPEAKER_BACK_CENTER;
++ case 8:
++ return SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_BACK_LEFT |
++ SPEAKER_BACK_RIGHT | SPEAKER_LOW_FREQUENCY | SPEAKER_FRONT_CENTER |
++ SPEAKER_SIDE_LEFT | SPEAKER_SIDE_RIGHT;
++ }
++ FIXME("Unknown speaker configuration: %u\n", channels);
++ return 0;
++}
++
++static void session_init_vols(AudioSession *session, UINT channels)
++{
++ if (session->channel_count < channels) {
++ UINT i;
++
++ if (session->channel_vols)
++ session->channel_vols = HeapReAlloc(GetProcessHeap(), 0,
++ session->channel_vols, sizeof(float) * channels);
++ else
++ session->channel_vols = HeapAlloc(GetProcessHeap(), 0,
++ sizeof(float) * channels);
++ if (!session->channel_vols)
++ return;
++
++ for(i = session->channel_count; i < channels; ++i)
++ session->channel_vols[i] = 1.f;
++
++ session->channel_count = channels;
++ }
++}
++
++static AudioSession *create_session(const GUID *guid, IMMDevice *device,
++ UINT num_channels)
++{
++ AudioSession *ret;
++
++ ret = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(AudioSession));
++ if (!ret)
++ return NULL;
++
++ memcpy(&ret->guid, guid, sizeof(GUID));
++
++ ret->device = device;
++
++ list_init(&ret->clients);
++
++ list_add_head(&g_sessions, &ret->entry);
++
++ session_init_vols(ret, num_channels);
++
++ ret->master_vol = 1.f;
++
++ return ret;
++}
++
++/* if channels == 0, then this will return or create a session with
++ * matching dataflow and GUID. otherwise, channels must also match */
++static HRESULT get_audio_session(const GUID *sessionguid,
++ IMMDevice *device, UINT channels, AudioSession **out)
++{
++ AudioSession *session;
++
++ if (!sessionguid || IsEqualGUID(sessionguid, &GUID_NULL)) {
++ *out = create_session(&GUID_NULL, device, channels);
++ if (!*out)
++ return E_OUTOFMEMORY;
++
++ return S_OK;
++ }
++
++ *out = NULL;
++ LIST_FOR_EACH_ENTRY(session, &g_sessions, AudioSession, entry) {
++ if (session->device == device &&
++ IsEqualGUID(sessionguid, &session->guid)) {
++ session_init_vols(session, channels);
++ *out = session;
++ break;
++ }
++ }
++
++ if (!*out) {
++ *out = create_session(sessionguid, device, channels);
++ if (!*out)
++ return E_OUTOFMEMORY;
++ }
++
++ return S_OK;
++}
++
++static HRESULT pulse_spec_from_waveformat(ACImpl *This, const WAVEFORMATEX *fmt)
++{
++ pa_channel_map_init(&This->map);
++ This->ss.rate = fmt->nSamplesPerSec;
++ This->ss.format = PA_SAMPLE_INVALID;
++ switch(fmt->wFormatTag) {
++ case WAVE_FORMAT_IEEE_FLOAT:
++ if (!fmt->nChannels || fmt->nChannels > 2 || fmt->wBitsPerSample !=
32)
++ break;
++ This->ss.format = PA_SAMPLE_FLOAT32LE;
++ pa_channel_map_init_auto(&This->map, fmt->nChannels,
PA_CHANNEL_MAP_ALSA);
++ break;
++ case WAVE_FORMAT_PCM:
++ if (!fmt->nChannels || fmt->nChannels > 2)
++ break;
++ if (fmt->wBitsPerSample == 8)
++ This->ss.format = PA_SAMPLE_U8;
++ else if (fmt->wBitsPerSample == 16)
++ This->ss.format = PA_SAMPLE_S16LE;
++ else
++ return AUDCLNT_E_UNSUPPORTED_FORMAT;
++ pa_channel_map_init_auto(&This->map, fmt->nChannels,
PA_CHANNEL_MAP_ALSA);
++ break;
++ case WAVE_FORMAT_EXTENSIBLE: {
++ WAVEFORMATEXTENSIBLE *wfe = (WAVEFORMATEXTENSIBLE*)fmt;
++ DWORD mask = wfe->dwChannelMask;
++ DWORD i = 0, j;
++ if (fmt->cbSize != (sizeof(*wfe) - sizeof(*fmt)) && fmt->cbSize !=
sizeof(*wfe))
++ break;
++ if (IsEqualGUID(&wfe->SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT)
&&
++ (!wfe->Samples.wValidBitsPerSample || wfe->Samples.wValidBitsPerSample
== 32) &&
++ fmt->wBitsPerSample == 32)
++ This->ss.format = PA_SAMPLE_FLOAT32LE;
++ else if (IsEqualGUID(&wfe->SubFormat, &KSDATAFORMAT_SUBTYPE_PCM)) {
++ DWORD valid = wfe->Samples.wValidBitsPerSample;
++ if (!valid)
++ valid = fmt->wBitsPerSample;
++ if (!valid || valid > fmt->wBitsPerSample)
++ break;
++ switch (fmt->wBitsPerSample) {
++ case 8:
++ if (valid == 8)
++ This->ss.format = PA_SAMPLE_U8;
++ break;
++ case 16:
++ if (valid == 16)
++ This->ss.format = PA_SAMPLE_S16LE;
++ break;
++ case 24:
++ if (valid == 24)
++ This->ss.format = PA_SAMPLE_S24LE;
++ break;
++ case 32:
++ if (valid == 24)
++ This->ss.format = PA_SAMPLE_S24_32LE;
++ else if (valid == 32)
++ This->ss.format = PA_SAMPLE_S32LE;
++ break;
++ default:
++ return AUDCLNT_E_UNSUPPORTED_FORMAT;
++ }
++ }
++ This->map.channels = fmt->nChannels;
++ if (!mask || mask == SPEAKER_ALL)
++ mask = get_channel_mask(fmt->nChannels);
++ else if (mask == ~0U && fmt->nChannels == 1)
++ mask = SPEAKER_FRONT_CENTER;
++ for (j = 0; j < sizeof(pulse_pos_from_wfx)/sizeof(*pulse_pos_from_wfx)
&& i < fmt->nChannels; ++j) {
++ if (mask & (1 << j))
++ This->map.map[i++] = pulse_pos_from_wfx[j];
++ }
++
++ /* Special case for mono since pulse appears to map it differently */
++ if (mask == SPEAKER_FRONT_CENTER)
++ This->map.map[0] = PA_CHANNEL_POSITION_MONO;
++
++ if (i < fmt->nChannels || (mask & SPEAKER_RESERVED)) {
++ This->map.channels = 0;
++ ERR("Invalid channel mask: %i/%i and %x(%x)\n", i,
fmt->nChannels, mask, wfe->dwChannelMask);
++ break;
++ }
++ break;
++ }
++ case WAVE_FORMAT_ALAW:
++ case WAVE_FORMAT_MULAW:
++ if (fmt->wBitsPerSample != 8) {
++ FIXME("Unsupported bpp %u for LAW\n",
fmt->wBitsPerSample);
++ return AUDCLNT_E_UNSUPPORTED_FORMAT;
++ }
++ if (fmt->nChannels != 1 && fmt->nChannels != 2) {
++ FIXME("Unsupported channels %u for LAW\n",
fmt->nChannels);
++ return AUDCLNT_E_UNSUPPORTED_FORMAT;
++ }
++ This->ss.format = fmt->wFormatTag == WAVE_FORMAT_MULAW ?
PA_SAMPLE_ULAW : PA_SAMPLE_ALAW;
++ pa_channel_map_init_auto(&This->map, fmt->nChannels,
PA_CHANNEL_MAP_ALSA);
++ break;
++ default:
++ WARN("Unhandled tag %x\n", fmt->wFormatTag);
++ return AUDCLNT_E_UNSUPPORTED_FORMAT;
++ }
++ This->ss.channels = This->map.channels;
++ if (!pa_channel_map_valid(&This->map) || This->ss.format ==
PA_SAMPLE_INVALID) {
++ ERR("Invalid format! Channel spec valid: %i, format: %i\n",
pa_channel_map_valid(&This->map), This->ss.format);
++ dump_fmt(fmt);
++ return AUDCLNT_E_UNSUPPORTED_FORMAT;
++ }
++ return S_OK;
++}
++
++static HRESULT WINAPI AudioClient_Initialize(IAudioClient *iface,
++ AUDCLNT_SHAREMODE mode, DWORD flags, REFERENCE_TIME duration,
++ REFERENCE_TIME period, const WAVEFORMATEX *fmt,
++ const GUID *sessionguid)
++{
++ ACImpl *This = impl_from_IAudioClient(iface);
++ HRESULT hr = S_OK;
++ UINT period_bytes;
++
++ TRACE("(%p)->(%x, %x, %s, %s, %p, %s)\n", This, mode, flags,
++ wine_dbgstr_longlong(duration), wine_dbgstr_longlong(period), fmt,
debugstr_guid(sessionguid));
++
++ if (!fmt)
++ return E_POINTER;
++
++ if (mode != AUDCLNT_SHAREMODE_SHARED && mode !=
AUDCLNT_SHAREMODE_EXCLUSIVE)
++ return AUDCLNT_E_NOT_INITIALIZED;
++ if (mode == AUDCLNT_SHAREMODE_EXCLUSIVE)
++ return AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED;
++
++ if (flags & ~(AUDCLNT_STREAMFLAGS_CROSSPROCESS |
++ AUDCLNT_STREAMFLAGS_LOOPBACK |
++ AUDCLNT_STREAMFLAGS_EVENTCALLBACK |
++ AUDCLNT_STREAMFLAGS_NOPERSIST |
++ AUDCLNT_STREAMFLAGS_RATEADJUST |
++ AUDCLNT_SESSIONFLAGS_EXPIREWHENUNOWNED |
++ AUDCLNT_SESSIONFLAGS_DISPLAY_HIDE |
++ AUDCLNT_SESSIONFLAGS_DISPLAY_HIDEWHENEXPIRED)) {
++ TRACE("Unknown flags: %08x\n", flags);
++ return E_INVALIDARG;
++ }
++
++ pthread_mutex_lock(&pulse_lock);
++ if (This->stream) {
++ pthread_mutex_unlock(&pulse_lock);
++ return AUDCLNT_E_ALREADY_INITIALIZED;
++ }
++
++ hr = pulse_spec_from_waveformat(This, fmt);
++ if (FAILED(hr))
++ goto exit;
++
++ if (mode == AUDCLNT_SHAREMODE_SHARED) {
++ period = pulse_def_period[This->dataflow == eCapture];
++ if (duration < 2 * period)
++ duration = 2 * period;
++ }
++ period_bytes = pa_frame_size(&This->ss) * MulDiv(period, This->ss.rate,
10000000);
++
++ if (duration < 20000000)
++ This->bufsize_frames = ceil((duration / 10000000.) *
fmt->nSamplesPerSec);
++ else
++ This->bufsize_frames = 2 * fmt->nSamplesPerSec;
++ This->bufsize_bytes = This->bufsize_frames * pa_frame_size(&This->ss);
++
++ This->share = mode;
++ This->flags = flags;
++ hr = pulse_stream_connect(This, period_bytes);
++ if (SUCCEEDED(hr)) {
++ UINT32 unalign;
++ const pa_buffer_attr *attr = pa_stream_get_buffer_attr(This->stream);
++ /* Update frames according to new size */
++ dump_attr(attr);
++ if (This->dataflow == eRender)
++ This->bufsize_bytes = attr->tlength;
++ else {
++ This->capture_period = period_bytes = attr->fragsize;
++ if ((unalign = This->bufsize_bytes % period_bytes))
++ This->bufsize_bytes += period_bytes - unalign;
++ }
++ This->bufsize_frames = This->bufsize_bytes /
pa_frame_size(&This->ss);
++ }
++ if (SUCCEEDED(hr)) {
++ UINT32 i, capture_packets = This->capture_period ? This->bufsize_bytes /
This->capture_period : 0;
++ This->tmp_buffer = HeapAlloc(GetProcessHeap(), 0, This->bufsize_bytes +
capture_packets * sizeof(ACPacket));
++ if (!This->tmp_buffer)
++ hr = E_OUTOFMEMORY;
++ else {
++ ACPacket *cur_packet = (ACPacket*)((char*)This->tmp_buffer +
This->bufsize_bytes);
++ BYTE *data = This->tmp_buffer;
++ memset(This->tmp_buffer, This->ss.format == PA_SAMPLE_U8 ? 0x80 : 0,
This->bufsize_bytes);
++ list_init(&This->packet_free_head);
++ list_init(&This->packet_filled_head);
++ for (i = 0; i < capture_packets; ++i, ++cur_packet) {
++ list_add_tail(&This->packet_free_head,
&cur_packet->entry);
++ cur_packet->data = data;
++ data += This->capture_period;
++ }
++ assert(!This->capture_period || This->bufsize_bytes ==
This->capture_period * capture_packets);
++ assert(!capture_packets || data - This->bufsize_bytes ==
This->tmp_buffer);
++ }
++ }
++ if (SUCCEEDED(hr))
++ hr = get_audio_session(sessionguid, This->parent, fmt->nChannels,
&This->session);
++ if (SUCCEEDED(hr))
++ list_add_tail(&This->session->clients, &This->entry);
++
++exit:
++ if (FAILED(hr)) {
++ HeapFree(GetProcessHeap(), 0, This->tmp_buffer);
++ This->tmp_buffer = NULL;
++ if (This->stream) {
++ pa_stream_disconnect(This->stream);
++ pa_stream_unref(This->stream);
++ This->stream = NULL;
++ }
++ }
++ pthread_mutex_unlock(&pulse_lock);
++ return hr;
++}
++
++static HRESULT WINAPI AudioClient_GetBufferSize(IAudioClient *iface,
++ UINT32 *out)
++{
++ ACImpl *This = impl_from_IAudioClient(iface);
++ HRESULT hr;
++
++ TRACE("(%p)->(%p)\n", This, out);
++
++ if (!out)
++ return E_POINTER;
++
++ pthread_mutex_lock(&pulse_lock);
++ hr = pulse_stream_valid(This);
++ if (SUCCEEDED(hr))
++ *out = This->bufsize_frames;
++ pthread_mutex_unlock(&pulse_lock);
++
++ return hr;
++}
++
++static HRESULT WINAPI AudioClient_GetStreamLatency(IAudioClient *iface,
++ REFERENCE_TIME *latency)
++{
++ ACImpl *This = impl_from_IAudioClient(iface);
++ const pa_buffer_attr *attr;
++ REFERENCE_TIME lat;
++ HRESULT hr;
++
++ TRACE("(%p)->(%p)\n", This, latency);
++
++ if (!latency)
++ return E_POINTER;
++
++ pthread_mutex_lock(&pulse_lock);
++ hr = pulse_stream_valid(This);
++ if (FAILED(hr)) {
++ pthread_mutex_unlock(&pulse_lock);
++ return hr;
++ }
++ attr = pa_stream_get_buffer_attr(This->stream);
++ if (This->dataflow == eRender)
++ lat = attr->minreq / pa_frame_size(&This->ss);
++ else
++ lat = attr->fragsize / pa_frame_size(&This->ss);
++ *latency = 10000000;
++ *latency *= lat;
++ *latency /= This->ss.rate;
++ pthread_mutex_unlock(&pulse_lock);
++ TRACE("Latency: %u ms\n", (DWORD)(*latency / 10000));
++ return S_OK;
++}
++
++static void ACImpl_GetRenderPad(ACImpl *This, UINT32 *out)
++{
++ *out = This->pad / pa_frame_size(&This->ss);
++}
++
++static void ACImpl_GetCapturePad(ACImpl *This, UINT32 *out)
++{
++ ACPacket *packet = This->locked_ptr;
++ if (!packet && !list_empty(&This->packet_filled_head)) {
++ packet = (ACPacket*)list_head(&This->packet_filled_head);
++ This->locked_ptr = packet;
++ list_remove(&packet->entry);
++ }
++ if (out)
++ *out = This->pad / pa_frame_size(&This->ss);
++}
++
++static HRESULT WINAPI AudioClient_GetCurrentPadding(IAudioClient *iface,
++ UINT32 *out)
++{
++ ACImpl *This = impl_from_IAudioClient(iface);
++ HRESULT hr;
++
++ TRACE("(%p)->(%p)\n", This, out);
++
++ if (!out)
++ return E_POINTER;
++
++ pthread_mutex_lock(&pulse_lock);
++ hr = pulse_stream_valid(This);
++ if (FAILED(hr)) {
++ pthread_mutex_unlock(&pulse_lock);
++ return hr;
++ }
++
++ if (This->dataflow == eRender)
++ ACImpl_GetRenderPad(This, out);
++ else
++ ACImpl_GetCapturePad(This, out);
++ pthread_mutex_unlock(&pulse_lock);
++
++ TRACE("%p Pad: %u ms (%u)\n", This, MulDiv(*out, 1000, This->ss.rate),
*out);
++ return S_OK;
++}
++
++static HRESULT WINAPI AudioClient_IsFormatSupported(IAudioClient *iface,
++ AUDCLNT_SHAREMODE mode, const WAVEFORMATEX *fmt,
++ WAVEFORMATEX **out)
++{
++ ACImpl *This = impl_from_IAudioClient(iface);
++ HRESULT hr = S_OK;
++ WAVEFORMATEX *closest = NULL;
++
++ TRACE("(%p)->(%x, %p, %p)\n", This, mode, fmt, out);
++
++ if (!fmt || (mode == AUDCLNT_SHAREMODE_SHARED && !out))
++ return E_POINTER;
++
++ if (out)
++ *out = NULL;
++ if (mode != AUDCLNT_SHAREMODE_SHARED && mode !=
AUDCLNT_SHAREMODE_EXCLUSIVE)
++ return E_INVALIDARG;
++ if (mode == AUDCLNT_SHAREMODE_EXCLUSIVE)
++ return This->dataflow == eCapture ? AUDCLNT_E_UNSUPPORTED_FORMAT :
AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED;
++ switch (fmt->wFormatTag) {
++ case WAVE_FORMAT_EXTENSIBLE:
++ if (fmt->cbSize < sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX))
++ return E_INVALIDARG;
++ dump_fmt(fmt);
++ break;
++ case WAVE_FORMAT_ALAW:
++ case WAVE_FORMAT_MULAW:
++ case WAVE_FORMAT_IEEE_FLOAT:
++ case WAVE_FORMAT_PCM:
++ dump_fmt(fmt);
++ break;
++ default:
++ dump_fmt(fmt);
++ return AUDCLNT_E_UNSUPPORTED_FORMAT;
++ }
++ if (fmt->nChannels == 0)
++ return AUDCLNT_E_UNSUPPORTED_FORMAT;
++ closest = clone_format(fmt);
++ if (!closest) {
++ if (out)
++ *out = NULL;
++ return E_OUTOFMEMORY;
++ }
++
++ if (fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE) {
++ UINT32 mask = 0, i, channels = 0;
++ WAVEFORMATEXTENSIBLE *ext = (WAVEFORMATEXTENSIBLE*)closest;
++
++ if ((fmt->nChannels > 1 && ext->dwChannelMask == SPEAKER_ALL)
||
++ (fmt->nChannels == 1 && ext->dwChannelMask == ~0U)) {
++ mask = ext->dwChannelMask;
++ channels = fmt->nChannels;
++ } else if (ext->dwChannelMask) {
++ for (i = 1; !(i & SPEAKER_RESERVED); i <<= 1) {
++ if (i & ext->dwChannelMask) {
++ mask |= i;
++ channels++;
++ }
++ }
++ if (channels < fmt->nChannels)
++ mask = get_channel_mask(fmt->nChannels);
++ } else
++ mask = ext->dwChannelMask;
++ if (ext->dwChannelMask != mask) {
++ ext->dwChannelMask = mask;
++ hr = S_FALSE;
++ }
++ }
++
++ if (hr == S_OK || !out) {
++ CoTaskMemFree(closest);
++ if (out)
++ *out = NULL;
++ } else if (closest) {
++ closest->nBlockAlign =
++ closest->nChannels * closest->wBitsPerSample / 8;
++ closest->nAvgBytesPerSec =
++ closest->nBlockAlign * closest->nSamplesPerSec;
++ *out = closest;
++ }
++
++ TRACE("returning: %08x %p\n", hr, out ? *out : NULL);
++ return hr;
++}
++
++static HRESULT WINAPI AudioClient_GetMixFormat(IAudioClient *iface,
++ WAVEFORMATEX **pwfx)
++{
++ ACImpl *This = impl_from_IAudioClient(iface);
++ WAVEFORMATEXTENSIBLE *fmt = &pulse_fmt[This->dataflow == eCapture];
++
++ TRACE("(%p)->(%p)\n", This, pwfx);
++
++ if (!pwfx)
++ return E_POINTER;
++
++ *pwfx = clone_format(&fmt->Format);
++ if (!*pwfx)
++ return E_OUTOFMEMORY;
++ dump_fmt(*pwfx);
++ return S_OK;
++}
++
++static HRESULT WINAPI AudioClient_GetDevicePeriod(IAudioClient *iface,
++ REFERENCE_TIME *defperiod, REFERENCE_TIME *minperiod)
++{
++ ACImpl *This = impl_from_IAudioClient(iface);
++
++ TRACE("(%p)->(%p, %p)\n", This, defperiod, minperiod);
++
++ if (!defperiod && !minperiod)
++ return E_POINTER;
++
++ if (defperiod)
++ *defperiod = pulse_def_period[This->dataflow == eCapture];
++ if (minperiod)
++ *minperiod = pulse_min_period[This->dataflow == eCapture];
++
++ return S_OK;
++}
++
++static HRESULT WINAPI AudioClient_Start(IAudioClient *iface)
++{
++ ACImpl *This = impl_from_IAudioClient(iface);
++ HRESULT hr = S_OK;
++ int success;
++ pa_operation *o;
++
++ TRACE("(%p)\n", This);
++
++ pthread_mutex_lock(&pulse_lock);
++ hr = pulse_stream_valid(This);
++ if (FAILED(hr)) {
++ pthread_mutex_unlock(&pulse_lock);
++ return hr;
++ }
++
++ if ((This->flags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK) &&
!This->event) {
++ pthread_mutex_unlock(&pulse_lock);
++ return AUDCLNT_E_EVENTHANDLE_NOT_SET;
++ }
++
++ if (This->started) {
++ pthread_mutex_unlock(&pulse_lock);
++ return AUDCLNT_E_NOT_STOPPED;
++ }
++ This->clock_pulse = PA_USEC_INVALID;
++
++ if (pa_stream_is_corked(This->stream)) {
++ o = pa_stream_cork(This->stream, 0, pulse_op_cb, &success);
++ if (o) {
++ while(pa_operation_get_state(o) == PA_OPERATION_RUNNING)
++ pthread_cond_wait(&pulse_cond, &pulse_lock);
++ pa_operation_unref(o);
++ } else
++ success = 0;
++ if (!success)
++ hr = E_FAIL;
++ }
++ if (SUCCEEDED(hr)) {
++ This->started = TRUE;
++ if (This->dataflow == eRender && This->event)
++ pa_stream_set_latency_update_callback(This->stream,
pulse_latency_callback, This);
++ }
++ pthread_mutex_unlock(&pulse_lock);
++ return hr;
++}
++
++static HRESULT WINAPI AudioClient_Stop(IAudioClient *iface)
++{
++ ACImpl *This = impl_from_IAudioClient(iface);
++ HRESULT hr = S_OK;
++ pa_operation *o;
++ int success;
++
++ TRACE("(%p)\n", This);
++
++ pthread_mutex_lock(&pulse_lock);
++ hr = pulse_stream_valid(This);
++ if (FAILED(hr)) {
++ pthread_mutex_unlock(&pulse_lock);
++ return hr;
++ }
++
++ if (!This->started) {
++ pthread_mutex_unlock(&pulse_lock);
++ return S_FALSE;
++ }
++
++ if (This->dataflow == eRender) {
++ o = pa_stream_cork(This->stream, 1, pulse_op_cb, &success);
++ if (o) {
++ while(pa_operation_get_state(o) == PA_OPERATION_RUNNING)
++ pthread_cond_wait(&pulse_cond, &pulse_lock);
++ pa_operation_unref(o);
++ } else
++ success = 0;
++ if (!success)
++ hr = E_FAIL;
++ }
++ if (SUCCEEDED(hr)) {
++ This->started = FALSE;
++ This->clock_pulse = PA_USEC_INVALID;
++ }
++ pthread_mutex_unlock(&pulse_lock);
++ return hr;
++}
++
++static HRESULT WINAPI AudioClient_Reset(IAudioClient *iface)
++{
++ ACImpl *This = impl_from_IAudioClient(iface);
++ HRESULT hr = S_OK;
++
++ TRACE("(%p)\n", This);
++
++ pthread_mutex_lock(&pulse_lock);
++ hr = pulse_stream_valid(This);
++ if (FAILED(hr)) {
++ pthread_mutex_unlock(&pulse_lock);
++ return hr;
++ }
++
++ if (This->started) {
++ pthread_mutex_unlock(&pulse_lock);
++ return AUDCLNT_E_NOT_STOPPED;
++ }
++
++ if (This->locked) {
++ pthread_mutex_unlock(&pulse_lock);
++ return AUDCLNT_E_BUFFER_OPERATION_PENDING;
++ }
++
++ if (This->dataflow == eRender) {
++ /* If there is still data in the render buffer it needs to be removed from the
server */
++ int success = 0;
++ if (This->pad) {
++ pa_operation *o = pa_stream_flush(This->stream, pulse_op_cb,
&success);
++ if (o) {
++ while(pa_operation_get_state(o) == PA_OPERATION_RUNNING)
++ pthread_cond_wait(&pulse_cond, &pulse_lock);
++ pa_operation_unref(o);
++ }
++ }
++ if (success || !This->pad)
++ This->clock_lastpos = This->clock_written = This->pad = 0;
++ } else {
++ ACPacket *p;
++ This->clock_written += This->pad;
++ This->pad = 0;
++
++ if ((p = This->locked_ptr)) {
++ This->locked_ptr = NULL;
++ list_add_tail(&This->packet_free_head, &p->entry);
++ }
++ list_move_tail(&This->packet_free_head,
&This->packet_filled_head);
++ }
++ pthread_mutex_unlock(&pulse_lock);
++
++ return hr;
++}
++
++static HRESULT WINAPI AudioClient_SetEventHandle(IAudioClient *iface,
++ HANDLE event)
++{
++ ACImpl *This = impl_from_IAudioClient(iface);
++ HRESULT hr;
++
++ TRACE("(%p)->(%p)\n", This, event);
++
++ if (!event)
++ return E_INVALIDARG;
++
++ pthread_mutex_lock(&pulse_lock);
++ hr = pulse_stream_valid(This);
++ if (FAILED(hr)) {
++ pthread_mutex_unlock(&pulse_lock);
++ return hr;
++ }
++
++ if (!(This->flags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK))
++ hr = AUDCLNT_E_EVENTHANDLE_NOT_EXPECTED;
++ else if (This->event)
++ hr = HRESULT_FROM_WIN32(ERROR_INVALID_NAME);
++ else
++ This->event = event;
++ pthread_mutex_unlock(&pulse_lock);
++ return hr;
++}
++
++static HRESULT WINAPI AudioClient_GetService(IAudioClient *iface, REFIID riid,
++ void **ppv)
++{
++ ACImpl *This = impl_from_IAudioClient(iface);
++ HRESULT hr;
++
++ TRACE("(%p)->(%s, %p)\n", This, debugstr_guid(riid), ppv);
++
++ if (!ppv)
++ return E_POINTER;
++ *ppv = NULL;
++
++ pthread_mutex_lock(&pulse_lock);
++ hr = pulse_stream_valid(This);
++ pthread_mutex_unlock(&pulse_lock);
++ if (FAILED(hr))
++ return hr;
++
++ if (IsEqualIID(riid, &IID_IAudioRenderClient)) {
++ if (This->dataflow != eRender)
++ return AUDCLNT_E_WRONG_ENDPOINT_TYPE;
++ *ppv = &This->IAudioRenderClient_iface;
++ } else if (IsEqualIID(riid, &IID_IAudioCaptureClient)) {
++ if (This->dataflow != eCapture)
++ return AUDCLNT_E_WRONG_ENDPOINT_TYPE;
++ *ppv = &This->IAudioCaptureClient_iface;
++ } else if (IsEqualIID(riid, &IID_IAudioClock)) {
++ *ppv = &This->IAudioClock_iface;
++ } else if (IsEqualIID(riid, &IID_IAudioStreamVolume)) {
++ *ppv = &This->IAudioStreamVolume_iface;
++ } else if (IsEqualIID(riid, &IID_IAudioSessionControl) ||
++ IsEqualIID(riid, &IID_IChannelAudioVolume) ||
++ IsEqualIID(riid, &IID_ISimpleAudioVolume)) {
++ if (!This->session_wrapper) {
++ This->session_wrapper = AudioSessionWrapper_Create(This);
++ if (!This->session_wrapper)
++ return E_OUTOFMEMORY;
++ }
++ if (IsEqualIID(riid, &IID_IAudioSessionControl))
++ *ppv = &This->session_wrapper->IAudioSessionControl2_iface;
++ else if (IsEqualIID(riid, &IID_IChannelAudioVolume))
++ *ppv = &This->session_wrapper->IChannelAudioVolume_iface;
++ else if (IsEqualIID(riid, &IID_ISimpleAudioVolume))
++ *ppv = &This->session_wrapper->ISimpleAudioVolume_iface;
++ }
++
++ if (*ppv) {
++ IUnknown_AddRef((IUnknown*)*ppv);
++ return S_OK;
++ }
++
++ FIXME("stub %s\n", debugstr_guid(riid));
++ return E_NOINTERFACE;
++}
++
++static const IAudioClientVtbl AudioClient_Vtbl =
++{
++ AudioClient_QueryInterface,
++ AudioClient_AddRef,
++ AudioClient_Release,
++ AudioClient_Initialize,
++ AudioClient_GetBufferSize,
++ AudioClient_GetStreamLatency,
++ AudioClient_GetCurrentPadding,
++ AudioClient_IsFormatSupported,
++ AudioClient_GetMixFormat,
++ AudioClient_GetDevicePeriod,
++ AudioClient_Start,
++ AudioClient_Stop,
++ AudioClient_Reset,
++ AudioClient_SetEventHandle,
++ AudioClient_GetService
++};
++
++static HRESULT WINAPI AudioRenderClient_QueryInterface(
++ IAudioRenderClient *iface, REFIID riid, void **ppv)
++{
++ TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
++
++ if (!ppv)
++ return E_POINTER;
++ *ppv = NULL;
++
++ if (IsEqualIID(riid, &IID_IUnknown) ||
++ IsEqualIID(riid, &IID_IAudioRenderClient))
++ *ppv = iface;
++ if (*ppv) {
++ IUnknown_AddRef((IUnknown*)*ppv);
++ return S_OK;
++ }
++
++ WARN("Unknown interface %s\n", debugstr_guid(riid));
++ return E_NOINTERFACE;
++}
++
++static ULONG WINAPI AudioRenderClient_AddRef(IAudioRenderClient *iface)
++{
++ ACImpl *This = impl_from_IAudioRenderClient(iface);
++ return AudioClient_AddRef(&This->IAudioClient_iface);
++}
++
++static ULONG WINAPI AudioRenderClient_Release(IAudioRenderClient *iface)
++{
++ ACImpl *This = impl_from_IAudioRenderClient(iface);
++ return AudioClient_Release(&This->IAudioClient_iface);
++}
++
++static HRESULT WINAPI AudioRenderClient_GetBuffer(IAudioRenderClient *iface,
++ UINT32 frames, BYTE **data)
++{
++ ACImpl *This = impl_from_IAudioRenderClient(iface);
++ UINT32 avail, pad, req, bytes = frames * pa_frame_size(&This->ss);
++ HRESULT hr = S_OK;
++ int ret = -1;
++
++ TRACE("(%p)->(%u, %p)\n", This, frames, data);
++
++ if (!data)
++ return E_POINTER;
++ *data = NULL;
++
++ pthread_mutex_lock(&pulse_lock);
++ hr = pulse_stream_valid(This);
++ if (FAILED(hr) || This->locked) {
++ pthread_mutex_unlock(&pulse_lock);
++ return FAILED(hr) ? hr : AUDCLNT_E_OUT_OF_ORDER;
++ }
++ if (!frames) {
++ pthread_mutex_unlock(&pulse_lock);
++ return S_OK;
++ }
++
++ ACImpl_GetRenderPad(This, &pad);
++ avail = This->bufsize_frames - pad;
++ if (avail < frames || bytes > This->bufsize_bytes) {
++ pthread_mutex_unlock(&pulse_lock);
++ WARN("Wanted to write %u, but only %u available\n", frames, avail);
++ return AUDCLNT_E_BUFFER_TOO_LARGE;
++ }
++
++ This->locked = frames;
++ req = bytes;
++ ret = pa_stream_begin_write(This->stream, &This->locked_ptr, &req);
++ if (ret < 0 || req < bytes) {
++ FIXME("%p Not using pulse locked data: %i %u/%u %u/%u\n", This, ret,
req/pa_frame_size(&This->ss), frames, pad, This->bufsize_frames);
++ if (ret >= 0)
++ pa_stream_cancel_write(This->stream);
++ *data = This->tmp_buffer;
++ This->locked_ptr = NULL;
++ } else
++ *data = This->locked_ptr;
++ pthread_mutex_unlock(&pulse_lock);
++ return hr;
++}
++
++static HRESULT WINAPI AudioRenderClient_ReleaseBuffer(
++ IAudioRenderClient *iface, UINT32 written_frames, DWORD flags)
++{
++ ACImpl *This = impl_from_IAudioRenderClient(iface);
++ UINT32 written_bytes = written_frames * pa_frame_size(&This->ss);
++
++ TRACE("(%p)->(%u, %x)\n", This, written_frames, flags);
++
++ pthread_mutex_lock(&pulse_lock);
++ if (!This->locked || !written_frames) {
++ if (This->locked_ptr)
++ pa_stream_cancel_write(This->stream);
++ This->locked = 0;
++ This->locked_ptr = NULL;
++ pthread_mutex_unlock(&pulse_lock);
++ return written_frames ? AUDCLNT_E_OUT_OF_ORDER : S_OK;
++ }
++
++ if (This->locked < written_frames) {
++ pthread_mutex_unlock(&pulse_lock);
++ return AUDCLNT_E_INVALID_SIZE;
++ }
++
++ if (flags & AUDCLNT_BUFFERFLAGS_SILENT) {
++ if (This->ss.format == PA_SAMPLE_U8)
++ memset(This->tmp_buffer, 128, written_bytes);
++ else
++ memset(This->tmp_buffer, 0, written_bytes);
++ }
++
++ This->locked = 0;
++ if (This->locked_ptr)
++ pa_stream_write(This->stream, This->locked_ptr, written_bytes, NULL, 0,
PA_SEEK_RELATIVE);
++ else
++ pa_stream_write(This->stream, This->tmp_buffer, written_bytes, NULL, 0,
PA_SEEK_RELATIVE);
++ This->pad += written_bytes;
++ This->locked_ptr = NULL;
++ TRACE("Released %u, pad %u\n", written_frames, This->pad /
pa_frame_size(&This->ss));
++ assert(This->pad <= This->bufsize_bytes);
++ pthread_mutex_unlock(&pulse_lock);
++ return S_OK;
++}
++
++static const IAudioRenderClientVtbl AudioRenderClient_Vtbl = {
++ AudioRenderClient_QueryInterface,
++ AudioRenderClient_AddRef,
++ AudioRenderClient_Release,
++ AudioRenderClient_GetBuffer,
++ AudioRenderClient_ReleaseBuffer
++};
++
++static HRESULT WINAPI AudioCaptureClient_QueryInterface(
++ IAudioCaptureClient *iface, REFIID riid, void **ppv)
++{
++ TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
++
++ if (!ppv)
++ return E_POINTER;
++ *ppv = NULL;
++
++ if (IsEqualIID(riid, &IID_IUnknown) ||
++ IsEqualIID(riid, &IID_IAudioCaptureClient))
++ *ppv = iface;
++ if (*ppv) {
++ IUnknown_AddRef((IUnknown*)*ppv);
++ return S_OK;
++ }
++
++ WARN("Unknown interface %s\n", debugstr_guid(riid));
++ return E_NOINTERFACE;
++}
++
++static ULONG WINAPI AudioCaptureClient_AddRef(IAudioCaptureClient *iface)
++{
++ ACImpl *This = impl_from_IAudioCaptureClient(iface);
++ return IAudioClient_AddRef(&This->IAudioClient_iface);
++}
++
++static ULONG WINAPI AudioCaptureClient_Release(IAudioCaptureClient *iface)
++{
++ ACImpl *This = impl_from_IAudioCaptureClient(iface);
++ return IAudioClient_Release(&This->IAudioClient_iface);
++}
++
++static HRESULT WINAPI AudioCaptureClient_GetBuffer(IAudioCaptureClient *iface,
++ BYTE **data, UINT32 *frames, DWORD *flags, UINT64 *devpos,
++ UINT64 *qpcpos)
++{
++ ACImpl *This = impl_from_IAudioCaptureClient(iface);
++ HRESULT hr;
++ ACPacket *packet;
++
++ TRACE("(%p)->(%p, %p, %p, %p, %p)\n", This, data, frames, flags,
++ devpos, qpcpos);
++
++ if (!data || !frames || !flags)
++ return E_POINTER;
++
++ pthread_mutex_lock(&pulse_lock);
++ hr = pulse_stream_valid(This);
++ if (FAILED(hr) || This->locked) {
++ pthread_mutex_unlock(&pulse_lock);
++ return FAILED(hr) ? hr : AUDCLNT_E_OUT_OF_ORDER;
++ }
++
++ ACImpl_GetCapturePad(This, NULL);
++ if ((packet = This->locked_ptr)) {
++ *frames = This->capture_period / pa_frame_size(&This->ss);
++ *flags = 0;
++ if (packet->discont)
++ *flags |= AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY;
++ if (devpos) {
++ if (packet->discont)
++ *devpos = (This->clock_written + This->capture_period) /
pa_frame_size(&This->ss);
++ else
++ *devpos = This->clock_written / pa_frame_size(&This->ss);
++ }
++ if (qpcpos)
++ *qpcpos = packet->qpcpos;
++ *data = packet->data;
++ }
++ else
++ *frames = 0;
++ This->locked = *frames;
++ pthread_mutex_unlock(&pulse_lock);
++ return *frames ? S_OK : AUDCLNT_S_BUFFER_EMPTY;
++}
++
++static HRESULT WINAPI AudioCaptureClient_ReleaseBuffer(
++ IAudioCaptureClient *iface, UINT32 done)
++{
++ ACImpl *This = impl_from_IAudioCaptureClient(iface);
++
++ TRACE("(%p)->(%u)\n", This, done);
++
++ pthread_mutex_lock(&pulse_lock);
++ if (!This->locked && done) {
++ pthread_mutex_unlock(&pulse_lock);
++ return AUDCLNT_E_OUT_OF_ORDER;
++ }
++ if (done && This->locked != done) {
++ pthread_mutex_unlock(&pulse_lock);
++ return AUDCLNT_E_INVALID_SIZE;
++ }
++ if (done) {
++ ACPacket *packet = This->locked_ptr;
++ This->locked_ptr = NULL;
++ This->pad -= This->capture_period;
++ if (packet->discont)
++ This->clock_written += 2 * This->capture_period;
++ else
++ This->clock_written += This->capture_period;
++ list_add_tail(&This->packet_free_head, &packet->entry);
++ }
++ This->locked = 0;
++ pthread_mutex_unlock(&pulse_lock);
++ return S_OK;
++}
++
++static HRESULT WINAPI AudioCaptureClient_GetNextPacketSize(
++ IAudioCaptureClient *iface, UINT32 *frames)
++{
++ ACImpl *This = impl_from_IAudioCaptureClient(iface);
++ ACPacket *p;
++
++ TRACE("(%p)->(%p)\n", This, frames);
++ if (!frames)
++ return E_POINTER;
++
++ pthread_mutex_lock(&pulse_lock);
++ ACImpl_GetCapturePad(This, NULL);
++ p = This->locked_ptr;
++ if (p)
++ *frames = This->capture_period / pa_frame_size(&This->ss);
++ else
++ *frames = 0;
++ pthread_mutex_unlock(&pulse_lock);
++ return S_OK;
++}
++
++static const IAudioCaptureClientVtbl AudioCaptureClient_Vtbl =
++{
++ AudioCaptureClient_QueryInterface,
++ AudioCaptureClient_AddRef,
++ AudioCaptureClient_Release,
++ AudioCaptureClient_GetBuffer,
++ AudioCaptureClient_ReleaseBuffer,
++ AudioCaptureClient_GetNextPacketSize
++};
++
++static HRESULT WINAPI AudioClock_QueryInterface(IAudioClock *iface,
++ REFIID riid, void **ppv)
++{
++ ACImpl *This = impl_from_IAudioClock(iface);
++
++ TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
++
++ if (!ppv)
++ return E_POINTER;
++ *ppv = NULL;
++
++ if (IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IAudioClock))
++ *ppv = iface;
++ else if (IsEqualIID(riid, &IID_IAudioClock2))
++ *ppv = &This->IAudioClock2_iface;
++ if (*ppv) {
++ IUnknown_AddRef((IUnknown*)*ppv);
++ return S_OK;
++ }
++
++ WARN("Unknown interface %s\n", debugstr_guid(riid));
++ return E_NOINTERFACE;
++}
++
++static ULONG WINAPI AudioClock_AddRef(IAudioClock *iface)
++{
++ ACImpl *This = impl_from_IAudioClock(iface);
++ return IAudioClient_AddRef(&This->IAudioClient_iface);
++}
++
++static ULONG WINAPI AudioClock_Release(IAudioClock *iface)
++{
++ ACImpl *This = impl_from_IAudioClock(iface);
++ return IAudioClient_Release(&This->IAudioClient_iface);
++}
++
++static HRESULT WINAPI AudioClock_GetFrequency(IAudioClock *iface, UINT64 *freq)
++{
++ ACImpl *This = impl_from_IAudioClock(iface);
++ HRESULT hr;
++
++ TRACE("(%p)->(%p)\n", This, freq);
++
++ pthread_mutex_lock(&pulse_lock);
++ hr = pulse_stream_valid(This);
++ if (SUCCEEDED(hr))
++ *freq = This->ss.rate * pa_frame_size(&This->ss);
++ pthread_mutex_unlock(&pulse_lock);
++ return hr;
++}
++
++static HRESULT WINAPI AudioClock_GetPosition(IAudioClock *iface, UINT64 *pos,
++ UINT64 *qpctime)
++{
++ ACImpl *This = impl_from_IAudioClock(iface);
++ pa_usec_t time;
++ HRESULT hr;
++
++ TRACE("(%p)->(%p, %p)\n", This, pos, qpctime);
++
++ if (!pos)
++ return E_POINTER;
++
++ pthread_mutex_lock(&pulse_lock);
++ hr = pulse_stream_valid(This);
++ if (FAILED(hr)) {
++ pthread_mutex_unlock(&pulse_lock);
++ return hr;
++ }
++
++ *pos = This->clock_written;
++ if (This->clock_pulse != PA_USEC_INVALID &&
pa_stream_get_time(This->stream, &time) >= 0) {
++ UINT32 delta = pa_usec_to_bytes(time - This->clock_pulse, &This->ss);
++ if (delta < This->pad)
++ *pos += delta;
++ else
++ *pos += This->pad;
++ }
++
++ /* Make time never go backwards */
++ if (*pos < This->clock_lastpos)
++ *pos = This->clock_lastpos;
++ else
++ This->clock_lastpos = *pos;
++ pthread_mutex_unlock(&pulse_lock);
++
++ TRACE("%p Position: %u\n", This, (unsigned)*pos);
++
++ if (qpctime) {
++ LARGE_INTEGER stamp, freq;
++ QueryPerformanceCounter(&stamp);
++ QueryPerformanceFrequency(&freq);
++ *qpctime = (stamp.QuadPart * (INT64)10000000) / freq.QuadPart;
++ }
++
++ return S_OK;
++}
++
++static HRESULT WINAPI AudioClock_GetCharacteristics(IAudioClock *iface,
++ DWORD *chars)
++{
++ ACImpl *This = impl_from_IAudioClock(iface);
++
++ TRACE("(%p)->(%p)\n", This, chars);
++
++ if (!chars)
++ return E_POINTER;
++
++ *chars = AUDIOCLOCK_CHARACTERISTIC_FIXED_FREQ;
++
++ return S_OK;
++}
++
++static const IAudioClockVtbl AudioClock_Vtbl =
++{
++ AudioClock_QueryInterface,
++ AudioClock_AddRef,
++ AudioClock_Release,
++ AudioClock_GetFrequency,
++ AudioClock_GetPosition,
++ AudioClock_GetCharacteristics
++};
++
++static HRESULT WINAPI AudioClock2_QueryInterface(IAudioClock2 *iface,
++ REFIID riid, void **ppv)
++{
++ ACImpl *This = impl_from_IAudioClock2(iface);
++ return IAudioClock_QueryInterface(&This->IAudioClock_iface, riid, ppv);
++}
++
++static ULONG WINAPI AudioClock2_AddRef(IAudioClock2 *iface)
++{
++ ACImpl *This = impl_from_IAudioClock2(iface);
++ return IAudioClient_AddRef(&This->IAudioClient_iface);
++}
++
++static ULONG WINAPI AudioClock2_Release(IAudioClock2 *iface)
++{
++ ACImpl *This = impl_from_IAudioClock2(iface);
++ return IAudioClient_Release(&This->IAudioClient_iface);
++}
++
++static HRESULT WINAPI AudioClock2_GetDevicePosition(IAudioClock2 *iface,
++ UINT64 *pos, UINT64 *qpctime)
++{
++ ACImpl *This = impl_from_IAudioClock2(iface);
++ HRESULT hr = AudioClock_GetPosition(&This->IAudioClock_iface, pos, qpctime);
++ if (SUCCEEDED(hr))
++ *pos /= pa_frame_size(&This->ss);
++ return hr;
++}
++
++static const IAudioClock2Vtbl AudioClock2_Vtbl =
++{
++ AudioClock2_QueryInterface,
++ AudioClock2_AddRef,
++ AudioClock2_Release,
++ AudioClock2_GetDevicePosition
++};
++
++static HRESULT WINAPI AudioStreamVolume_QueryInterface(
++ IAudioStreamVolume *iface, REFIID riid, void **ppv)
++{
++ TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
++
++ if (!ppv)
++ return E_POINTER;
++ *ppv = NULL;
++
++ if (IsEqualIID(riid, &IID_IUnknown) ||
++ IsEqualIID(riid, &IID_IAudioStreamVolume))
++ *ppv = iface;
++ if (*ppv) {
++ IUnknown_AddRef((IUnknown*)*ppv);
++ return S_OK;
++ }
++
++ WARN("Unknown interface %s\n", debugstr_guid(riid));
++ return E_NOINTERFACE;
++}
++
++static ULONG WINAPI AudioStreamVolume_AddRef(IAudioStreamVolume *iface)
++{
++ ACImpl *This = impl_from_IAudioStreamVolume(iface);
++ return IAudioClient_AddRef(&This->IAudioClient_iface);
++}
++
++static ULONG WINAPI AudioStreamVolume_Release(IAudioStreamVolume *iface)
++{
++ ACImpl *This = impl_from_IAudioStreamVolume(iface);
++ return IAudioClient_Release(&This->IAudioClient_iface);
++}
++
++static HRESULT WINAPI AudioStreamVolume_GetChannelCount(
++ IAudioStreamVolume *iface, UINT32 *out)
++{
++ ACImpl *This = impl_from_IAudioStreamVolume(iface);
++
++ TRACE("(%p)->(%p)\n", This, out);
++
++ if (!out)
++ return E_POINTER;
++
++ *out = This->ss.channels;
++
++ return S_OK;
++}
++
++struct pulse_info_cb_data {
++ UINT32 n;
++ float *levels;
++};
++
++static void pulse_sink_input_info_cb(pa_context *c, const pa_sink_input_info *info, int
eol, void *data)
++{
++ struct pulse_info_cb_data *d = data;
++ int i;
++ if (eol)
++ return;
++ for (i = 0; i < d->n; ++i)
++ d->levels[i] = (float)info->volume.values[i] / (float)PA_VOLUME_NORM;
++ pthread_cond_signal(&pulse_cond);
++}
++
++static void pulse_source_info_cb(pa_context *c, const pa_source_info *info, int eol,
void *data)
++{
++ struct pulse_info_cb_data *d = data;
++ int i;
++ if (eol)
++ return;
++ for (i = 0; i < d->n; ++i)
++ d->levels[i] = (float)info->volume.values[i] / (float)PA_VOLUME_NORM;
++ pthread_cond_signal(&pulse_cond);
++}
++
++static HRESULT WINAPI AudioStreamVolume_SetAllVolumes(
++ IAudioStreamVolume *iface, UINT32 count, const float *levels)
++{
++ ACImpl *This = impl_from_IAudioStreamVolume(iface);
++ pa_operation *o;
++ HRESULT hr;
++ int success = 0, i;
++ pa_cvolume cv;
++
++ TRACE("(%p)->(%d, %p)\n", This, count, levels);
++
++ if (!levels)
++ return E_POINTER;
++
++ if (count != This->ss.channels)
++ return E_INVALIDARG;
++
++ pthread_mutex_lock(&pulse_lock);
++ hr = pulse_stream_valid(This);
++ if (FAILED(hr))
++ goto out;
++
++ if (pulse_stream_volume) {
++ cv.channels = count;
++ for (i = 0; i < cv.channels; ++i)
++ cv.values[i] = levels[i] * (float)PA_VOLUME_NORM;
++ if (This->dataflow == eRender)
++ o = pa_context_set_sink_input_volume(pulse_ctx,
pa_stream_get_index(This->stream), &cv, pulse_ctx_op_cb, &success);
++ else
++ o = pa_context_set_source_volume_by_index(pulse_ctx,
pa_stream_get_device_index(This->stream), &cv, pulse_ctx_op_cb, &success);
++ if (o) {
++ while(pa_operation_get_state(o) == PA_OPERATION_RUNNING)
++ pthread_cond_wait(&pulse_cond, &pulse_lock);
++ pa_operation_unref(o);
++ }
++ if (!success)
++ hr = AUDCLNT_E_BUFFER_ERROR;
++ } else {
++ int i;
++ for (i = 0; i < count; ++i)
++ This->vol[i] = levels[i];
++ }
++
++out:
++ pthread_mutex_unlock(&pulse_lock);
++ return hr;
++}
++
++static HRESULT WINAPI AudioStreamVolume_GetAllVolumes(
++ IAudioStreamVolume *iface, UINT32 count, float *levels)
++{
++ ACImpl *This = impl_from_IAudioStreamVolume(iface);
++ pa_operation *o;
++ HRESULT hr;
++ struct pulse_info_cb_data info;
++
++ TRACE("(%p)->(%d, %p)\n", This, count, levels);
++
++ if (!levels)
++ return E_POINTER;
++
++ if (count != This->ss.channels)
++ return E_INVALIDARG;
++
++ pthread_mutex_lock(&pulse_lock);
++ hr = pulse_stream_valid(This);
++ if (FAILED(hr))
++ goto out;
++
++ if (pulse_stream_volume) {
++ info.n = count;
++ info.levels = levels;
++ if (This->dataflow == eRender)
++ o = pa_context_get_sink_input_info(pulse_ctx,
pa_stream_get_index(This->stream), pulse_sink_input_info_cb, &info);
++ else
++ o = pa_context_get_source_info_by_index(pulse_ctx,
pa_stream_get_device_index(This->stream), pulse_source_info_cb, &info);
++ if (o) {
++ while(pa_operation_get_state(o) == PA_OPERATION_RUNNING)
++ pthread_cond_wait(&pulse_cond, &pulse_lock);
++ pa_operation_unref(o);
++ } else
++ hr = AUDCLNT_E_BUFFER_ERROR;
++ } else {
++ int i;
++ for (i = 0; i < count; ++i)
++ levels[i] = This->vol[i];
++ }
++
++out:
++ pthread_mutex_unlock(&pulse_lock);
++ return hr;
++}
++
++static HRESULT WINAPI AudioStreamVolume_SetChannelVolume(
++ IAudioStreamVolume *iface, UINT32 index, float level)
++{
++ ACImpl *This = impl_from_IAudioStreamVolume(iface);
++ HRESULT hr;
++ float volumes[PA_CHANNELS_MAX];
++
++ TRACE("(%p)->(%d, %f)\n", This, index, level);
++
++ if (level < 0.f || level > 1.f)
++ return E_INVALIDARG;
++
++ if (index >= This->ss.channels)
++ return E_INVALIDARG;
++
++ hr = AudioStreamVolume_GetAllVolumes(iface, This->ss.channels, volumes);
++ volumes[index] = level;
++ if (SUCCEEDED(hr))
++ hr = AudioStreamVolume_SetAllVolumes(iface, This->ss.channels, volumes);
++ return hr;
++}
++
++static HRESULT WINAPI AudioStreamVolume_GetChannelVolume(
++ IAudioStreamVolume *iface, UINT32 index, float *level)
++{
++ ACImpl *This = impl_from_IAudioStreamVolume(iface);
++ float volumes[PA_CHANNELS_MAX];
++ HRESULT hr;
++
++ TRACE("(%p)->(%d, %p)\n", This, index, level);
++
++ if (!level)
++ return E_POINTER;
++
++ if (index >= This->ss.channels)
++ return E_INVALIDARG;
++
++ hr = AudioStreamVolume_GetAllVolumes(iface, This->ss.channels, volumes);
++ if (SUCCEEDED(hr))
++ *level = volumes[index];
++ return hr;
++}
++
++static const IAudioStreamVolumeVtbl AudioStreamVolume_Vtbl =
++{
++ AudioStreamVolume_QueryInterface,
++ AudioStreamVolume_AddRef,
++ AudioStreamVolume_Release,
++ AudioStreamVolume_GetChannelCount,
++ AudioStreamVolume_SetChannelVolume,
++ AudioStreamVolume_GetChannelVolume,
++ AudioStreamVolume_SetAllVolumes,
++ AudioStreamVolume_GetAllVolumes
++};
++
++static AudioSessionWrapper *AudioSessionWrapper_Create(ACImpl *client)
++{
++ AudioSessionWrapper *ret;
++
++ ret = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
++ sizeof(AudioSessionWrapper));
++ if (!ret)
++ return NULL;
++
++ ret->IAudioSessionControl2_iface.lpVtbl = &AudioSessionControl2_Vtbl;
++ ret->ISimpleAudioVolume_iface.lpVtbl = &SimpleAudioVolume_Vtbl;
++ ret->IChannelAudioVolume_iface.lpVtbl = &ChannelAudioVolume_Vtbl;
++
++ ret->ref = !client;
++
++ ret->client = client;
++ if (client) {
++ ret->session = client->session;
++ AudioClient_AddRef(&client->IAudioClient_iface);
++ }
++
++ return ret;
++}
++
++static HRESULT WINAPI AudioSessionControl_QueryInterface(
++ IAudioSessionControl2 *iface, REFIID riid, void **ppv)
++{
++ TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
++
++ if (!ppv)
++ return E_POINTER;
++ *ppv = NULL;
++
++ if (IsEqualIID(riid, &IID_IUnknown) ||
++ IsEqualIID(riid, &IID_IAudioSessionControl) ||
++ IsEqualIID(riid, &IID_IAudioSessionControl2))
++ *ppv = iface;
++ if (*ppv) {
++ IUnknown_AddRef((IUnknown*)*ppv);
++ return S_OK;
++ }
++
++ WARN("Unknown interface %s\n", debugstr_guid(riid));
++ return E_NOINTERFACE;
++}
++
++static ULONG WINAPI AudioSessionControl_AddRef(IAudioSessionControl2 *iface)
++{
++ AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
++ ULONG ref;
++ ref = InterlockedIncrement(&This->ref);
++ TRACE("(%p) Refcount now %u\n", This, ref);
++ return ref;
++}
++
++static ULONG WINAPI AudioSessionControl_Release(IAudioSessionControl2 *iface)
++{
++ AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
++ ULONG ref;
++ ref = InterlockedDecrement(&This->ref);
++ TRACE("(%p) Refcount now %u\n", This, ref);
++ if (!ref) {
++ if (This->client) {
++ This->client->session_wrapper = NULL;
++ AudioClient_Release(&This->client->IAudioClient_iface);
++ }
++ HeapFree(GetProcessHeap(), 0, This);
++ }
++ return ref;
++}
++
++static HRESULT WINAPI AudioSessionControl_GetState(IAudioSessionControl2 *iface,
++ AudioSessionState *state)
++{
++ AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
++ ACImpl *client;
++
++ TRACE("(%p)->(%p)\n", This, state);
++
++ if (!state)
++ return NULL_PTR_ERR;
++
++ pthread_mutex_lock(&pulse_lock);
++ if (list_empty(&This->session->clients)) {
++ *state = AudioSessionStateExpired;
++ goto out;
++ }
++ LIST_FOR_EACH_ENTRY(client, &This->session->clients, ACImpl, entry) {
++ if (client->started) {
++ *state = AudioSessionStateActive;
++ goto out;
++ }
++ }
++ *state = AudioSessionStateInactive;
++
++out:
++ pthread_mutex_unlock(&pulse_lock);
++ return S_OK;
++}
++
++static HRESULT WINAPI AudioSessionControl_GetDisplayName(
++ IAudioSessionControl2 *iface, WCHAR **name)
++{
++ AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
++
++ FIXME("(%p)->(%p) - stub\n", This, name);
++
++ return E_NOTIMPL;
++}
++
++static HRESULT WINAPI AudioSessionControl_SetDisplayName(
++ IAudioSessionControl2 *iface, const WCHAR *name, const GUID *session)
++{
++ AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
++
++ FIXME("(%p)->(%p, %s) - stub\n", This, name, debugstr_guid(session));
++
++ return E_NOTIMPL;
++}
++
++static HRESULT WINAPI AudioSessionControl_GetIconPath(
++ IAudioSessionControl2 *iface, WCHAR **path)
++{
++ AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
++
++ FIXME("(%p)->(%p) - stub\n", This, path);
++
++ return E_NOTIMPL;
++}
++
++static HRESULT WINAPI AudioSessionControl_SetIconPath(
++ IAudioSessionControl2 *iface, const WCHAR *path, const GUID *session)
++{
++ AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
++
++ FIXME("(%p)->(%p, %s) - stub\n", This, path, debugstr_guid(session));
++
++ return E_NOTIMPL;
++}
++
++static HRESULT WINAPI AudioSessionControl_GetGroupingParam(
++ IAudioSessionControl2 *iface, GUID *group)
++{
++ AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
++
++ FIXME("(%p)->(%p) - stub\n", This, group);
++
++ return E_NOTIMPL;
++}
++
++static HRESULT WINAPI AudioSessionControl_SetGroupingParam(
++ IAudioSessionControl2 *iface, const GUID *group, const GUID *session)
++{
++ AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
++
++ FIXME("(%p)->(%s, %s) - stub\n", This, debugstr_guid(group),
++ debugstr_guid(session));
++
++ return E_NOTIMPL;
++}
++
++static HRESULT WINAPI AudioSessionControl_RegisterAudioSessionNotification(
++ IAudioSessionControl2 *iface, IAudioSessionEvents *events)
++{
++ AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
++
++ FIXME("(%p)->(%p) - stub\n", This, events);
++
++ return S_OK;
++}
++
++static HRESULT WINAPI AudioSessionControl_UnregisterAudioSessionNotification(
++ IAudioSessionControl2 *iface, IAudioSessionEvents *events)
++{
++ AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
++
++ FIXME("(%p)->(%p) - stub\n", This, events);
++
++ return S_OK;
++}
++
++static HRESULT WINAPI AudioSessionControl_GetSessionIdentifier(
++ IAudioSessionControl2 *iface, WCHAR **id)
++{
++ AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
++
++ FIXME("(%p)->(%p) - stub\n", This, id);
++
++ return E_NOTIMPL;
++}
++
++static HRESULT WINAPI AudioSessionControl_GetSessionInstanceIdentifier(
++ IAudioSessionControl2 *iface, WCHAR **id)
++{
++ AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
++
++ FIXME("(%p)->(%p) - stub\n", This, id);
++
++ return E_NOTIMPL;
++}
++
++static HRESULT WINAPI AudioSessionControl_GetProcessId(
++ IAudioSessionControl2 *iface, DWORD *pid)
++{
++ AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
++
++ TRACE("(%p)->(%p)\n", This, pid);
++
++ if (!pid)
++ return E_POINTER;
++
++ *pid = GetCurrentProcessId();
++
++ return S_OK;
++}
++
++static HRESULT WINAPI AudioSessionControl_IsSystemSoundsSession(
++ IAudioSessionControl2 *iface)
++{
++ AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
++
++ TRACE("(%p)\n", This);
++
++ return S_FALSE;
++}
++
++static HRESULT WINAPI AudioSessionControl_SetDuckingPreference(
++ IAudioSessionControl2 *iface, BOOL optout)
++{
++ AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
++
++ TRACE("(%p)->(%d)\n", This, optout);
++
++ return S_OK;
++}
++
++static const IAudioSessionControl2Vtbl AudioSessionControl2_Vtbl =
++{
++ AudioSessionControl_QueryInterface,
++ AudioSessionControl_AddRef,
++ AudioSessionControl_Release,
++ AudioSessionControl_GetState,
++ AudioSessionControl_GetDisplayName,
++ AudioSessionControl_SetDisplayName,
++ AudioSessionControl_GetIconPath,
++ AudioSessionControl_SetIconPath,
++ AudioSessionControl_GetGroupingParam,
++ AudioSessionControl_SetGroupingParam,
++ AudioSessionControl_RegisterAudioSessionNotification,
++ AudioSessionControl_UnregisterAudioSessionNotification,
++ AudioSessionControl_GetSessionIdentifier,
++ AudioSessionControl_GetSessionInstanceIdentifier,
++ AudioSessionControl_GetProcessId,
++ AudioSessionControl_IsSystemSoundsSession,
++ AudioSessionControl_SetDuckingPreference
++};
++
++typedef struct _SessionMgr {
++ IAudioSessionManager2 IAudioSessionManager2_iface;
++
++ LONG ref;
++
++ IMMDevice *device;
++} SessionMgr;
++
++static HRESULT WINAPI AudioSessionManager_QueryInterface(IAudioSessionManager2 *iface,
++ REFIID riid, void **ppv)
++{
++ TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
++
++ if (!ppv)
++ return E_POINTER;
++ *ppv = NULL;
++
++ if (IsEqualIID(riid, &IID_IUnknown) ||
++ IsEqualIID(riid, &IID_IAudioSessionManager) ||
++ IsEqualIID(riid, &IID_IAudioSessionManager2))
++ *ppv = iface;
++ if (*ppv) {
++ IUnknown_AddRef((IUnknown*)*ppv);
++ return S_OK;
++ }
++
++ WARN("Unknown interface %s\n", debugstr_guid(riid));
++ return E_NOINTERFACE;
++}
++
++static inline SessionMgr *impl_from_IAudioSessionManager2(IAudioSessionManager2 *iface)
++{
++ return CONTAINING_RECORD(iface, SessionMgr, IAudioSessionManager2_iface);
++}
++
++static ULONG WINAPI AudioSessionManager_AddRef(IAudioSessionManager2 *iface)
++{
++ SessionMgr *This = impl_from_IAudioSessionManager2(iface);
++ ULONG ref;
++ ref = InterlockedIncrement(&This->ref);
++ TRACE("(%p) Refcount now %u\n", This, ref);
++ return ref;
++}
++
++static ULONG WINAPI AudioSessionManager_Release(IAudioSessionManager2 *iface)
++{
++ SessionMgr *This = impl_from_IAudioSessionManager2(iface);
++ ULONG ref;
++ ref = InterlockedDecrement(&This->ref);
++ TRACE("(%p) Refcount now %u\n", This, ref);
++ if (!ref)
++ HeapFree(GetProcessHeap(), 0, This);
++ return ref;
++}
++
++static HRESULT WINAPI AudioSessionManager_GetAudioSessionControl(
++ IAudioSessionManager2 *iface, const GUID *session_guid, DWORD flags,
++ IAudioSessionControl **out)
++{
++ SessionMgr *This = impl_from_IAudioSessionManager2(iface);
++ AudioSession *session;
++ AudioSessionWrapper *wrapper;
++ HRESULT hr;
++
++ TRACE("(%p)->(%s, %x, %p)\n", This, debugstr_guid(session_guid),
++ flags, out);
++
++ hr = get_audio_session(session_guid, This->device, 0, &session);
++ if (FAILED(hr))
++ return hr;
++
++ wrapper = AudioSessionWrapper_Create(NULL);
++ if (!wrapper)
++ return E_OUTOFMEMORY;
++
++ wrapper->session = session;
++
++ *out = (IAudioSessionControl*)&wrapper->IAudioSessionControl2_iface;
++
++ return S_OK;
++}
++
++static HRESULT WINAPI AudioSessionManager_GetSimpleAudioVolume(
++ IAudioSessionManager2 *iface, const GUID *session_guid, DWORD flags,
++ ISimpleAudioVolume **out)
++{
++ SessionMgr *This = impl_from_IAudioSessionManager2(iface);
++ AudioSession *session;
++ AudioSessionWrapper *wrapper;
++ HRESULT hr;
++
++ TRACE("(%p)->(%s, %x, %p)\n", This, debugstr_guid(session_guid),
++ flags, out);
++
++ hr = get_audio_session(session_guid, This->device, 0, &session);
++ if (FAILED(hr))
++ return hr;
++
++ wrapper = AudioSessionWrapper_Create(NULL);
++ if (!wrapper)
++ return E_OUTOFMEMORY;
++
++ wrapper->session = session;
++
++ *out = &wrapper->ISimpleAudioVolume_iface;
++
++ return S_OK;
++}
++
++static HRESULT WINAPI AudioSessionManager_GetSessionEnumerator(
++ IAudioSessionManager2 *iface, IAudioSessionEnumerator **out)
++{
++ SessionMgr *This = impl_from_IAudioSessionManager2(iface);
++ FIXME("(%p)->(%p) - stub\n", This, out);
++ return E_NOTIMPL;
++}
++
++static HRESULT WINAPI AudioSessionManager_RegisterSessionNotification(
++ IAudioSessionManager2 *iface, IAudioSessionNotification *notification)
++{
++ SessionMgr *This = impl_from_IAudioSessionManager2(iface);
++ FIXME("(%p)->(%p) - stub\n", This, notification);
++ return E_NOTIMPL;
++}
++
++static HRESULT WINAPI AudioSessionManager_UnregisterSessionNotification(
++ IAudioSessionManager2 *iface, IAudioSessionNotification *notification)
++{
++ SessionMgr *This = impl_from_IAudioSessionManager2(iface);
++ FIXME("(%p)->(%p) - stub\n", This, notification);
++ return E_NOTIMPL;
++}
++
++static HRESULT WINAPI AudioSessionManager_RegisterDuckNotification(
++ IAudioSessionManager2 *iface, const WCHAR *session_id,
++ IAudioVolumeDuckNotification *notification)
++{
++ SessionMgr *This = impl_from_IAudioSessionManager2(iface);
++ FIXME("(%p)->(%p) - stub\n", This, notification);
++ return E_NOTIMPL;
++}
++
++static HRESULT WINAPI AudioSessionManager_UnregisterDuckNotification(
++ IAudioSessionManager2 *iface,
++ IAudioVolumeDuckNotification *notification)
++{
++ SessionMgr *This = impl_from_IAudioSessionManager2(iface);
++ FIXME("(%p)->(%p) - stub\n", This, notification);
++ return E_NOTIMPL;
++}
++
++static const IAudioSessionManager2Vtbl AudioSessionManager2_Vtbl =
++{
++ AudioSessionManager_QueryInterface,
++ AudioSessionManager_AddRef,
++ AudioSessionManager_Release,
++ AudioSessionManager_GetAudioSessionControl,
++ AudioSessionManager_GetSimpleAudioVolume,
++ AudioSessionManager_GetSessionEnumerator,
++ AudioSessionManager_RegisterSessionNotification,
++ AudioSessionManager_UnregisterSessionNotification,
++ AudioSessionManager_RegisterDuckNotification,
++ AudioSessionManager_UnregisterDuckNotification
++};
++
++static HRESULT WINAPI SimpleAudioVolume_QueryInterface(
++ ISimpleAudioVolume *iface, REFIID riid, void **ppv)
++{
++ TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
++
++ if (!ppv)
++ return E_POINTER;
++ *ppv = NULL;
++
++ if (IsEqualIID(riid, &IID_IUnknown) ||
++ IsEqualIID(riid, &IID_ISimpleAudioVolume))
++ *ppv = iface;
++ if (*ppv) {
++ IUnknown_AddRef((IUnknown*)*ppv);
++ return S_OK;
++ }
++
++ WARN("Unknown interface %s\n", debugstr_guid(riid));
++ return E_NOINTERFACE;
++}
++
++static ULONG WINAPI SimpleAudioVolume_AddRef(ISimpleAudioVolume *iface)
++{
++ AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
++ return AudioSessionControl_AddRef(&This->IAudioSessionControl2_iface);
++}
++
++static ULONG WINAPI SimpleAudioVolume_Release(ISimpleAudioVolume *iface)
++{
++ AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
++ return AudioSessionControl_Release(&This->IAudioSessionControl2_iface);
++}
++
++static HRESULT WINAPI SimpleAudioVolume_SetMasterVolume(
++ ISimpleAudioVolume *iface, float level, const GUID *context)
++{
++ AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
++ AudioSession *session = This->session;
++
++ TRACE("(%p)->(%f, %s)\n", session, level, wine_dbgstr_guid(context));
++
++ if (level < 0.f || level > 1.f)
++ return E_INVALIDARG;
++
++ if (context)
++ FIXME("Notifications not supported yet\n");
++
++ TRACE("Pulseaudio does not support session volume control\n");
++
++ pthread_mutex_lock(&pulse_lock);
++ session->master_vol = level;
++ pthread_mutex_unlock(&pulse_lock);
++
++ return S_OK;
++}
++
++static HRESULT WINAPI SimpleAudioVolume_GetMasterVolume(
++ ISimpleAudioVolume *iface, float *level)
++{
++ AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
++ AudioSession *session = This->session;
++
++ TRACE("(%p)->(%p)\n", session, level);
++
++ if (!level)
++ return NULL_PTR_ERR;
++
++ *level = session->master_vol;
++
++ return S_OK;
++}
++
++static HRESULT WINAPI SimpleAudioVolume_SetMute(ISimpleAudioVolume *iface,
++ BOOL mute, const GUID *context)
++{
++ AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
++ AudioSession *session = This->session;
++
++ TRACE("(%p)->(%u, %p)\n", session, mute, context);
++
++ if (context)
++ FIXME("Notifications not supported yet\n");
++
++ session->mute = mute;
++
++ return S_OK;
++}
++
++static HRESULT WINAPI SimpleAudioVolume_GetMute(ISimpleAudioVolume *iface,
++ BOOL *mute)
++{
++ AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
++ AudioSession *session = This->session;
++
++ TRACE("(%p)->(%p)\n", session, mute);
++
++ if (!mute)
++ return NULL_PTR_ERR;
++
++ *mute = session->mute;
++
++ return S_OK;
++}
++
++static const ISimpleAudioVolumeVtbl SimpleAudioVolume_Vtbl =
++{
++ SimpleAudioVolume_QueryInterface,
++ SimpleAudioVolume_AddRef,
++ SimpleAudioVolume_Release,
++ SimpleAudioVolume_SetMasterVolume,
++ SimpleAudioVolume_GetMasterVolume,
++ SimpleAudioVolume_SetMute,
++ SimpleAudioVolume_GetMute
++};
++
++static HRESULT WINAPI ChannelAudioVolume_QueryInterface(
++ IChannelAudioVolume *iface, REFIID riid, void **ppv)
++{
++ TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
++
++ if (!ppv)
++ return E_POINTER;
++ *ppv = NULL;
++
++ if (IsEqualIID(riid, &IID_IUnknown) ||
++ IsEqualIID(riid, &IID_IChannelAudioVolume))
++ *ppv = iface;
++ if (*ppv) {
++ IUnknown_AddRef((IUnknown*)*ppv);
++ return S_OK;
++ }
++
++ WARN("Unknown interface %s\n", debugstr_guid(riid));
++ return E_NOINTERFACE;
++}
++
++static ULONG WINAPI ChannelAudioVolume_AddRef(IChannelAudioVolume *iface)
++{
++ AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
++ return AudioSessionControl_AddRef(&This->IAudioSessionControl2_iface);
++}
++
++static ULONG WINAPI ChannelAudioVolume_Release(IChannelAudioVolume *iface)
++{
++ AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
++ return AudioSessionControl_Release(&This->IAudioSessionControl2_iface);
++}
++
++static HRESULT WINAPI ChannelAudioVolume_GetChannelCount(
++ IChannelAudioVolume *iface, UINT32 *out)
++{
++ AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
++ AudioSession *session = This->session;
++
++ TRACE("(%p)->(%p)\n", session, out);
++
++ if (!out)
++ return NULL_PTR_ERR;
++
++ *out = session->channel_count;
++
++ return S_OK;
++}
++
++static HRESULT WINAPI ChannelAudioVolume_SetChannelVolume(
++ IChannelAudioVolume *iface, UINT32 index, float level,
++ const GUID *context)
++{
++ AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
++ AudioSession *session = This->session;
++
++ TRACE("(%p)->(%d, %f, %s)\n", session, index, level,
++ wine_dbgstr_guid(context));
++
++ if (level < 0.f || level > 1.f)
++ return E_INVALIDARG;
++
++ if (index >= session->channel_count)
++ return E_INVALIDARG;
++
++ if (context)
++ FIXME("Notifications not supported yet\n");
++
++ TRACE("Pulseaudio does not support session volume control\n");
++
++ pthread_mutex_lock(&pulse_lock);
++ session->channel_vols[index] = level;
++ pthread_mutex_unlock(&pulse_lock);
++
++ return S_OK;
++}
++
++static HRESULT WINAPI ChannelAudioVolume_GetChannelVolume(
++ IChannelAudioVolume *iface, UINT32 index, float *level)
++{
++ AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
++ AudioSession *session = This->session;
++
++ TRACE("(%p)->(%d, %p)\n", session, index, level);
++
++ if (!level)
++ return NULL_PTR_ERR;
++
++ if (index >= session->channel_count)
++ return E_INVALIDARG;
++
++ *level = session->channel_vols[index];
++
++ return S_OK;
++}
++
++static HRESULT WINAPI ChannelAudioVolume_SetAllVolumes(
++ IChannelAudioVolume *iface, UINT32 count, const float *levels,
++ const GUID *context)
++{
++ AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
++ AudioSession *session = This->session;
++ int i;
++
++ TRACE("(%p)->(%d, %p, %s)\n", session, count, levels,
++ wine_dbgstr_guid(context));
++
++ if (!levels)
++ return NULL_PTR_ERR;
++
++ if (count != session->channel_count)
++ return E_INVALIDARG;
++
++ if (context)
++ FIXME("Notifications not supported yet\n");
++
++ TRACE("Pulseaudio does not support session volume control\n");
++
++ pthread_mutex_lock(&pulse_lock);
++ for(i = 0; i < count; ++i)
++ session->channel_vols[i] = levels[i];
++ pthread_mutex_unlock(&pulse_lock);
++ return S_OK;
++}
++
++static HRESULT WINAPI ChannelAudioVolume_GetAllVolumes(
++ IChannelAudioVolume *iface, UINT32 count, float *levels)
++{
++ AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
++ AudioSession *session = This->session;
++ int i;
++
++ TRACE("(%p)->(%d, %p)\n", session, count, levels);
++
++ if (!levels)
++ return NULL_PTR_ERR;
++
++ if (count != session->channel_count)
++ return E_INVALIDARG;
++
++ for(i = 0; i < count; ++i)
++ levels[i] = session->channel_vols[i];
++
++ return S_OK;
++}
++
++static const IChannelAudioVolumeVtbl ChannelAudioVolume_Vtbl =
++{
++ ChannelAudioVolume_QueryInterface,
++ ChannelAudioVolume_AddRef,
++ ChannelAudioVolume_Release,
++ ChannelAudioVolume_GetChannelCount,
++ ChannelAudioVolume_SetChannelVolume,
++ ChannelAudioVolume_GetChannelVolume,
++ ChannelAudioVolume_SetAllVolumes,
++ ChannelAudioVolume_GetAllVolumes
++};
++
++HRESULT WINAPI AUDDRV_GetAudioSessionManager(IMMDevice *device,
++ IAudioSessionManager2 **out)
++{
++ SessionMgr *This = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
sizeof(SessionMgr));
++ *out = NULL;
++ if (!This)
++ return E_OUTOFMEMORY;
++ This->IAudioSessionManager2_iface.lpVtbl = &AudioSessionManager2_Vtbl;
++ This->device = device;
++ This->ref = 1;
++ *out = &This->IAudioSessionManager2_iface;
++ return S_OK;
++}
+diff -uNr wine-1.5.4.old/dlls/winepulse.drv/winepulse.drv.spec
wine-1.5.4/dlls/winepulse.drv/winepulse.drv.spec
+--- wine-1.5.4.old/dlls/winepulse.drv/winepulse.drv.spec 1970-01-01 02:00:00.000000000
+0200
++++ wine-1.5.4/dlls/winepulse.drv/winepulse.drv.spec 2012-05-23 12:03:21.069645000 +0300
+@@ -0,0 +1,5 @@
++# MMDevAPI driver functions
++@ stdcall -private GetPriority() AUDDRV_GetPriority
++@ stdcall -private GetEndpointIDs(long ptr ptr ptr ptr) AUDDRV_GetEndpointIDs
++@ stdcall -private GetAudioEndpoint(ptr ptr ptr) AUDDRV_GetAudioEndpoint
++@ stdcall -private GetAudioSessionManager(ptr ptr) AUDDRV_GetAudioSessionManager
diff --git a/wine.spec b/wine.spec
index e1a1f51..b2ddea4 100644
--- a/wine.spec
+++ b/wine.spec
@@ -1,6 +1,8 @@
-%global no64bit 0
+%global no64bit 0
+%global winegecko 1.5.0
+%global winemono 0.0.4
Name: wine
-Version: 1.5.4
+Version: 1.5.5
Release: 1%{?dist}
Summary: A Windows 16/32/64 bit emulator
@@ -44,6 +46,12 @@ Source501: wine-tahoma.conf
# and provide a readme
Source502: wine-README-tahoma
+Patch511: wine-cjk.patch
+
+# winepulse backend
+#
http://repo.or.cz/w/wine/multimedia.git
+Patch1001: wine-pulse-1.5.4.patch
+
Buildroot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
%if !%{?no64bit}
@@ -117,7 +125,7 @@ BuildRequires: openal-soft-devel
BuildRequires: icoutils
%endif
-
+Requires: wine-common = %{version}-%{release}
Requires: wine-desktop = %{version}-%{release}
Requires: wine-fonts = %{version}-%{release}
@@ -133,7 +141,8 @@ Requires: wine-pulseaudio(x86-32) = %{version}-%{release}
Requires: wine-openal(x86-32) = %{version}-%{release}
%endif
%if 0%{?fedora} >= 17
-Requires: mingw32-wine-gecko = 1.5
+Requires: mingw32-wine-gecko = %winegecko
+Requires: mingw-wine-mono = %winemono
%endif
%endif
@@ -155,7 +164,8 @@ Requires: wine-pulseaudio(x86-64) = %{version}-%{release}
Requires: wine-openal(x86-64) = %{version}-%{release}
%endif
%if 0%{?fedora} >= 17
-Requires: mingw64-wine-gecko = 1.5
+Requires: mingw64-wine-gecko = %winegecko
+Requires: mingw-wine-mono = %winemono
%endif
Requires: wine-wow(x86-64) = %{version}-%{release}
Conflicts: wine-wow(x86-32) = %{version}-%{release}
@@ -190,23 +200,9 @@ Summary: Wine core package
Group: Applications/Emulators
Requires(post): /sbin/ldconfig
Requires(postun): /sbin/ldconfig
-Obsoletes: wine-arts < 0.9.34
-Provides: wine-arts = %{version}-%{release}
-Obsoletes: wine-tools <= 1.1.27
-Provides: wine-tools = %{version}-%{release}
-# removed as of 1.3.25 (new sound api)
-Obsoletes: wine-esd <= 1.3.24
-Provides: wine-esd = %{version}-%{release}
-Obsoletes: wine-jack <= 1.3.24
-Provides: wine-jack = %{version}-%{release}
-# removed as of 1.3.19 (we don't support oss4)
-Obsoletes: wine-oss <= 1.3.18
-Provides: wine-oss = %{version}-%{release}
-# removed as of 1.3.16
-Obsoletes: wine-nas <= 1.3.15
-Provides: wine-nas = %{version}-%{release}
-# require -common so we get wine.inf (#528335)
-Requires: wine-common = %{version}-%{release}
+
+# require -filesystem
+Requires: wine-filesystem = %{version}-%{release}
%ifarch %{ix86}
Requires: freetype(x86-32)
@@ -232,6 +228,26 @@ Requires: libXrender
Requires: libXcursor
%endif
+# old removed packages
+Obsoletes: wine-arts < 0.9.34
+Provides: wine-arts = %{version}-%{release}
+Obsoletes: wine-tools <= 1.1.27
+Provides: wine-tools = %{version}-%{release}
+
+# removed as of 1.3.25 (new sound api)
+Obsoletes: wine-esd <= 1.3.24
+Provides: wine-esd = %{version}-%{release}
+Obsoletes: wine-jack <= 1.3.24
+Provides: wine-jack = %{version}-%{release}
+
+# removed as of 1.3.19 (we don't support oss4)
+Obsoletes: wine-oss <= 1.3.18
+Provides: wine-oss = %{version}-%{release}
+
+# removed as of 1.3.16
+Obsoletes: wine-nas <= 1.3.15
+Provides: wine-nas = %{version}-%{release}
+
%description core
Wine core package includes the basic wine stuff needed by all other packages.
@@ -274,6 +290,23 @@ BuildArch: noarch
Register the wine binary handler for windows executables via SysV init files.
%endif
+%package filesystem
+Summary: Filesystem directories for wine
+Group: Applications/Emulators
+BuildArch: noarch
+
+%description filesystem
+Filesystem directories and basic configuration for wine.
+
+%package common
+Summary: Common files
+Group: Applications/Emulators
+Requires: wine-core = %{version}-%{release}
+BuildArch: noarch
+
+%description common
+Common wine files and scripts.
+
%package desktop
Summary: Desktop integration features for wine
Group: Applications/Emulators
@@ -384,15 +417,6 @@ Requires: fontpackages-filesystem
%description symbol-fonts
%{summary}
-%package common
-Summary: Common files
-Group: Applications/Emulators
-Requires: wine-core = %{version}-%{release}
-BuildArch: noarch
-
-%description common
-Common wine files and scripts.
-
%package ldap
Summary: LDAP support for wine
Group: System Environment/Libraries
@@ -438,22 +462,10 @@ with the Wine Windows(TM) emulation libraries.
Summary: Pulseaudio support for wine
Group: System Environment/Libraries
Requires: wine-core = %{version}-%{release}
-%ifarch %{ix86}
-Requires: wine-alsa(x86-32) = %{version}-%{release}
-Requires: alsa-plugins-pulseaudio(x86-32)
-%endif
-%ifarch x86_64
-Requires: wine-alsa(x86-64) = %{version}-%{release}
-Requires: alsa-plugins-pulseaudio(x86-64)
-%endif
-%ifarch %{arm}
-Requires: wine-alsa = %{version}-%{release}
-Requires: alsa-plugins-pulseaudio
-%endif
%description pulseaudio
-This package pulse in the alsa pulseaudio backend to allow for pulse playback
-over alsa.
+This package adds a pulseaudio driver for wine. Please do not report bugs in
+the pulseaudio wine backend at winehq.
%package alsa
Summary: Alsa support for wine
@@ -476,6 +488,12 @@ This package adds an openal driver for wine.
%prep
%setup -q
+%patch511 -p1 -b.cjk
+
+%patch1001 -p1 -b.winepulse
+
+autoreconf
+
%build
# disable fortify as it breaks wine
#
http://bugs.winehq.org/show_bug.cgi?id=24606
@@ -488,6 +506,7 @@ export CFLAGS="`echo $RPM_OPT_FLAGS | sed -e
's/-Wp,-D_FORTIFY_SOURCE=2//'` -Wno
--x-includes=%{_includedir} --x-libraries=%{_libdir} \
--without-hal --with-dbus \
--with-x \
+ --with-pulse \
%ifarch x86_64
--enable-win64 \
%endif
@@ -536,6 +555,9 @@ install -p -m 644 %{SOURCE201} \
# add gecko dir
mkdir -p %{buildroot}%{_datadir}/wine/gecko
+# add mono dir
+mkdir -p %{buildroot}%{_datadir}/wine/mono
+
# extract and install icons
%if 0%{?fedora} > 10
mkdir -p %{buildroot}%{_datadir}/icons/hicolor/scalable/apps
@@ -1265,6 +1287,17 @@ gtk-update-icon-cache %{_datadir}/icons/hicolor &>/dev/null
|| :
%{_libdir}/wine/wow32.dll.so
%endif
+%files filesystem
+%defattr(-,root,root,-)
+%doc COPYING.LIB
+%dir %{_datadir}/wine
+%dir %{_datadir}/wine/gecko
+%dir %{_datadir}/wine/mono
+%dir %{_datadir}/wine/fonts
+%{_datadir}/wine/generic.ppd
+%{_datadir}/wine/wine.inf
+%{_datadir}/wine/l_intl.nls
+
%files common
%defattr(-,root,root,-)
%{_bindir}/notepad
@@ -1279,8 +1312,6 @@ gtk-update-icon-cache %{_datadir}/icons/hicolor &>/dev/null
|| :
%{_bindir}/wineboot
%{_bindir}/wineconsole
%{_bindir}/winecfg
-%dir %{_datadir}/wine
-%dir %{_datadir}/wine/gecko
%{_mandir}/man1/wine.1*
%{_mandir}/man1/wineserver.1*
%{_mandir}/man1/msiexec.1*
@@ -1298,13 +1329,11 @@ gtk-update-icon-cache %{_datadir}/icons/hicolor &>/dev/null
|| :
%lang(de) %{_mandir}/de.UTF-8/man1/wine.1*
%lang(de) %{_mandir}/de.UTF-8/man1/wineserver.1*
%lang(pl) %{_mandir}/pl.UTF-8/man1/wine.1*
-%{_datadir}/wine/generic.ppd
-%{_datadir}/wine/wine.inf
-%{_datadir}/wine/l_intl.nls
%files fonts
%defattr(-,root,root,-)
-%dir %{_datadir}/wine/fonts
+# meta package
+
%files courier-fonts
%defattr(-,root,root,-)
@@ -1426,7 +1455,7 @@ gtk-update-icon-cache %{_datadir}/icons/hicolor &>/dev/null
|| :
%files pulseaudio
%defattr(-,root,root,-)
-# empty meta package for deps
+%{_libdir}/wine/winepulse.drv.so
%files alsa
%defattr(-,root,root,-)
@@ -1439,6 +1468,15 @@ gtk-update-icon-cache %{_datadir}/icons/hicolor &>/dev/null
|| :
%endif
%changelog
+* Mon May 28 2012 Andreas Bierfert <andreas.bierfert[AT]lowlatency.de>
+- 1.5.5-1
+- version upgrade (rhbz#817257)
+- split out -filesystem and clean up -common/-core requires
+- re-add winepulse driver (rhbz#821207, rhbz#783699)
+- add font replacements for CJK to wine.inf and add information for cjk users
+ to fedora readme (rhbz#815125, rhbz#820096)
+- add support for and require wine-mono
+
* Mon May 14 2012 Andreas Bierfert <andreas.bierfert[AT]lowlatency.de>
- 1.5.4-1
- version upgrade